Merge mozilla-aurora to b2g37. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 06 Feb 2015 17:17:04 -0500
changeset 232809 0552759956d3fede805f40e34f368d2e3490ee59
parent 232782 418673bfce1fafe6e80d36058ebb26aa07063da7 (current diff)
parent 232808 85477b7fa1d632d71b11370000dfeb04060f9a16 (diff)
child 232810 0336de435c70d74d393e4bf13d532e8044bd6e23
push id115
push userryanvm@gmail.com
push dateFri, 06 Feb 2015 22:17:13 +0000
treeherdermozilla-b2g37_v2_2@0552759956d3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone37.0a2
Merge mozilla-aurora to b2g37. a=merge
--- a/browser/base/content/browser-fxaccounts.js
+++ b/browser/base/content/browser-fxaccounts.js
@@ -5,16 +5,21 @@
 let gFxAccounts = {
 
   PREF_SYNC_START_DOORHANGER: "services.sync.ui.showSyncStartDoorhanger",
   DOORHANGER_ACTIVATE_DELAY_MS: 5000,
   SYNC_MIGRATION_NOTIFICATION_TITLE: "fxa-migration",
 
   _initialized: false,
   _inCustomizationMode: false,
+  // _expectingNotifyClose is a hack that helps us determine if the
+  // migration notification was closed due to being "dismissed" vs closed
+  // due to one of the migration buttons being clicked.  It's ugly and somewhat
+  // fragile, so bug 1119020 exists to help us do this better.
+  _expectingNotifyClose: false,
 
   get weave() {
     delete this.weave;
     return this.weave = Cc["@mozilla.org/weave/service;1"]
                           .getService(Ci.nsISupports)
                           .wrappedJSObject;
   },
 
@@ -23,17 +28,18 @@ let gFxAccounts = {
     delete this.topics;
     return this.topics = [
       "weave:service:ready",
       "weave:service:sync:start",
       "weave:service:login:error",
       "weave:service:setup-complete",
       "fxa-migration:state-changed",
       this.FxAccountsCommon.ONVERIFIED_NOTIFICATION,
-      this.FxAccountsCommon.ONLOGOUT_NOTIFICATION
+      this.FxAccountsCommon.ONLOGOUT_NOTIFICATION,
+      "weave:notification:removed",
     ];
   },
 
   get button() {
     delete this.button;
     return this.button = document.getElementById("PanelUI-fxa-status");
   },
 
@@ -105,16 +111,26 @@ let gFxAccounts = {
         Services.prefs.setBoolPref(this.PREF_SYNC_START_DOORHANGER, true);
         break;
       case "weave:service:sync:start":
         this.onSyncStart();
         break;
       case "fxa-migration:state-changed":
         this.onMigrationStateChanged(data, subject);
         break;
+      case "weave:notification:removed":
+        // this exists just so we can tell the difference between "box was
+        // closed due to button press" vs "was closed due to click on [x]"
+        let notif = subject.wrappedJSObject.object;
+        if (notif.title == this.SYNC_MIGRATION_NOTIFICATION_TITLE &&
+            !this._expectingNotifyClose) {
+          // it's an [x] on our notification, so record telemetry.
+          this.fxaMigrator.recordTelemetry(this.fxaMigrator.TELEMETRY_DECLINED);
+        }
+        break;
       default:
         this.updateUI();
         break;
     }
   },
 
   onSyncStart: function () {
     if (!this.isActiveWindow) {
@@ -258,58 +274,67 @@ let gFxAccounts = {
     }
     this.button.label = label;
     this.button.hidden = false;
     this.button.setAttribute("fxastatus", status);
   }),
 
   updateMigrationNotification: Task.async(function* () {
     if (!this._migrationInfo) {
+      this._expectingNotifyClose = true;
       Weave.Notifications.removeAll(this.SYNC_MIGRATION_NOTIFICATION_TITLE);
+      // because this is called even when there is no such notification, we
+      // set _expectingNotifyClose back to false as we may yet create a new
+      // notification (but in general, once we've created a migration
+      // notification once in a session, we don't create one again)
+      this._expectingNotifyClose = false;
       return;
     }
     let note = null;
     switch (this._migrationInfo.state) {
       case this.fxaMigrator.STATE_USER_FXA: {
         // There are 2 cases here - no email address means it is an offer on
         // the first device (so the user is prompted to create an account).
         // If there is an email address it is the "join the party" flow, so the
         // user is prompted to sign in with the address they previously used.
-        let msg, upgradeLabel, upgradeAccessKey;
+        let msg, upgradeLabel, upgradeAccessKey, learnMoreLink;
         if (this._migrationInfo.email) {
           msg = this.strings.formatStringFromName("signInAfterUpgradeOnOtherDevice.description",
                                                   [this._migrationInfo.email],
                                                   1);
           upgradeLabel = this.strings.GetStringFromName("signInAfterUpgradeOnOtherDevice.label");
           upgradeAccessKey = this.strings.GetStringFromName("signInAfterUpgradeOnOtherDevice.accessKey");
         } else {
           msg = this.strings.GetStringFromName("needUserLong");
           upgradeLabel = this.strings.GetStringFromName("upgradeToFxA.label");
           upgradeAccessKey = this.strings.GetStringFromName("upgradeToFxA.accessKey");
+          learnMoreLink = this.fxaMigrator.learnMoreLink;
         }
         note = new Weave.Notification(
           undefined, msg, undefined, Weave.Notifications.PRIORITY_WARNING, [
             new Weave.NotificationButton(upgradeLabel, upgradeAccessKey, () => {
+              this._expectingNotifyClose = true;
               this.fxaMigrator.createFxAccount(window);
             }),
-          ]
+          ], learnMoreLink
         );
         break;
       }
       case this.fxaMigrator.STATE_USER_FXA_VERIFIED: {
         let msg =
           this.strings.formatStringFromName("needVerifiedUserLong",
                                             [this._migrationInfo.email], 1);
         let resendLabel =
           this.strings.GetStringFromName("resendVerificationEmail.label");
         let resendAccessKey =
           this.strings.GetStringFromName("resendVerificationEmail.accessKey");
         note = new Weave.Notification(
           undefined, msg, undefined, Weave.Notifications.PRIORITY_INFO, [
             new Weave.NotificationButton(resendLabel, resendAccessKey, () => {
+              this._expectingNotifyClose = true;
               this.fxaMigrator.resendVerificationMail();
             }),
           ]
         );
         break;
       }
     }
     note.title = this.SYNC_MIGRATION_NOTIFICATION_TITLE;
--- a/browser/base/content/sync/notification.xml
+++ b/browser/base/content/sync/notification.xml
@@ -68,16 +68,28 @@
         <parameter name="notification"/>
         <body><![CDATA[
           var node = this.appendNotification(notification.description,
                                              notification.title,
                                              notification.iconURL,
                                              notification.priority,
                                              notification.buttons);
           node.notification = notification;
+
+          if (notification.link) {
+            let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+            let link = node.ownerDocument.createElementNS(XULNS, "label");
+            link.className = "text-link";
+            link.setAttribute("value", notification.link.text);
+            link.href = notification.link.href;
+            let desc = node.ownerDocument.getAnonymousElementByAttribute(
+              node, "anonid", "messageText"
+            );
+            desc.appendChild(link);
+          }
         ]]></body>
       </method>
 
     </implementation>
   </binding>
 
   <binding id="notification" extends="chrome://global/content/bindings/notification.xml#notification">
     <content>
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -1041,16 +1041,22 @@
           document.getAnonymousElementByAttribute(this, "anonid", "searchbar-engine-name")
                   .setAttribute("value", headerText);
           document.getAnonymousElementByAttribute(this, "anonid", "searchbar-engine")
                   .engine = currentEngine;
         ]]></body>
       </method>
     </implementation>
     <handlers>
+      <handler event="popuphidden"><![CDATA[
+        Services.tm.mainThread.dispatch(function() {
+          document.getElementById("searchbar").textbox.selectedButton = null;
+        }, Ci.nsIThread.DISPATCH_NORMAL);
+      ]]></handler>
+
       <handler event="popupshowing"><![CDATA[
         // First handle deciding if we are showing the reduced version of the
         // popup containing only the preferences button. We do this if the
         // glass icon has been clicked if the text field is empty.
         let searchbar = document.getElementById("searchbar");
         let tree = document.getAnonymousElementByAttribute(this, "anonid",
                                                            "tree")
         if (searchbar.hasAttribute("showonlysettings")) {
--- a/browser/components/places/tests/browser/browser.ini
+++ b/browser/components/places/tests/browser/browser.ini
@@ -43,17 +43,17 @@ skip-if = e10s # Bug ?????? - test fails
 skip-if = true
 
 [browser_library_infoBox.js]
 [browser_markPageAsFollowedLink.js]
 skip-if = e10s # Bug 933103 - mochitest's EventUtils.synthesizeMouse functions not e10s friendly (test does EventUtils.sendMouseEvent...)
 [browser_toolbar_migration.js]
 [browser_library_batch_delete.js]
 [browser_555547.js]
-skip-if = e10s || (os == "win" && !debug) # Bug 1115276
+skip-if = e10s
 [browser_416459_cut.js]
 skip-if = e10s # Bug ?????? - clipboard operations don't seem to work in this test?
 [browser_library_downloads.js]
 [browser_library_left_pane_select_hierarchy.js]
 [browser_435851_copy_query.js]
 skip-if = e10s
 [browser_toolbarbutton_menu_context.js]
 skip-if = e10s
--- a/browser/components/places/tests/browser/browser_475045.js
+++ b/browser/components/places/tests/browser/browser_475045.js
@@ -2,25 +2,25 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 // Instead of loading ChromeUtils.js into the test scope in browser-test.js for all tests,
 // we only need ChromeUtils.js for a few files which is why we are using loadSubScript.
 var ChromeUtils = {};
 this._scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                      getService(Ci.mozIJSSubScriptLoader);
 this._scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", ChromeUtils);
 
-function test() {
+add_task(function* test() {
   // Make sure the bookmarks bar is visible and restore its state on cleanup.
   let toolbar = document.getElementById("PersonalToolbar");
   ok(toolbar, "PersonalToolbar should not be null");
 
   if (toolbar.collapsed) {
-    setToolbarVisibility(toolbar, true);
+    yield promiseSetToolbarVisibility(toolbar, true);
     registerCleanupFunction(function() {
-      setToolbarVisibility(toolbar, false);
+      return promiseSetToolbarVisibility(toolbar, false);
     });
   }
 
   // Setup the node we will use to be dropped. The actual node used does not
   // matter because we will set its data, effect, and mimeType manually.
   let placesItems = document.getElementById("PlacesToolbarItems");
   ok(placesItems, "PlacesToolbarItems should not be null");
   ok(placesItems.localName == "scrollbox", "PlacesToolbarItems should not be null");
@@ -57,9 +57,9 @@ function test() {
   // Simulate a bookmark drop for all of the mime types and effects.
   let mimeTypes = ["text/plain", "text/unicode", "text/x-moz-url"];
   let effects = ["move", "copy", "link"];
   effects.forEach(function (effect) {
     mimeTypes.forEach(function (mimeType) {
       simulateDragDrop(effect, mimeType);
     });
   });
-}
+});
--- a/browser/components/places/tests/browser/browser_555547.js
+++ b/browser/components/places/tests/browser/browser_555547.js
@@ -1,59 +1,64 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-function test() {
-  waitForExplicitFinish();
+"use strict";
 
+add_task(function* test() {
   let sidebarBox = document.getElementById("sidebar-box");
   is(sidebarBox.hidden, true, "The sidebar should be hidden");
 
   // Uncollapse the personal toolbar if needed.
   let toolbar = document.getElementById("PersonalToolbar");
   let wasCollapsed = toolbar.collapsed;
-  if (wasCollapsed)
-    setToolbarVisibility(toolbar, true);
+  if (wasCollapsed) {
+    yield promiseSetToolbarVisibility(toolbar, true);
+  }
 
-  let sidebar = document.getElementById("sidebar");
-  sidebar.addEventListener("load", function() {
-    sidebar.removeEventListener("load", arguments.callee, true);
-    let tree = sidebar.contentDocument.getElementById("bookmarks-view");
+  let sidebar = yield promiseLoadedSidebar("viewBookmarksSidebar");
+  registerCleanupFunction(toggleSidebar);
 
-    // The Places view is build on load, so we enqueue our test.
-    executeSoon(function() {
-      // Focus the tree and check if its controller is returned.
-      tree.focus();
+  // Focus the tree and check if its controller is returned.
+  let tree = sidebar.contentDocument.getElementById("bookmarks-view");
+  tree.focus();
 
-      let controller = doGetPlacesControllerForCommand("placesCmd_copy");
-      let treeController = tree.controllers
-                               .getControllerForCommand("placesCmd_copy");
-      ok(controller == treeController, "tree controller was returned");
+  let controller = doGetPlacesControllerForCommand("placesCmd_copy");
+  let treeController = tree.controllers
+                           .getControllerForCommand("placesCmd_copy");
+  ok(controller == treeController, "tree controller was returned");
 
-      // Open the context menu for a toolbar item, and check if the toolbar's
-      // controller is returned.
-      let toolbarItems = document.getElementById("PlacesToolbarItems");
-      EventUtils.synthesizeMouse(toolbarItems.childNodes[0],
-                                 4, 4, { type: "contextmenu", button: 2 },
-                                 window);
-      controller = doGetPlacesControllerForCommand("placesCmd_copy");
-      let toolbarController = document.getElementById("PlacesToolbar")
-                                      .controllers
-                                      .getControllerForCommand("placesCmd_copy");
-      ok(controller == toolbarController, "the toolbar controller was returned");
+  // Open the context menu for a toolbar item, and check if the toolbar's
+  // controller is returned.
+  let toolbarItems = document.getElementById("PlacesToolbarItems");
+  EventUtils.synthesizeMouse(toolbarItems.childNodes[0],
+                             4, 4, { type: "contextmenu", button: 2 },
+                             window);
+  controller = doGetPlacesControllerForCommand("placesCmd_copy");
+  let toolbarController = document.getElementById("PlacesToolbar")
+                                  .controllers
+                                  .getControllerForCommand("placesCmd_copy");
+  ok(controller == toolbarController, "the toolbar controller was returned");
 
-      document.getElementById("placesContext").hidePopup();
+  document.getElementById("placesContext").hidePopup();
 
-      // Now that the context menu is closed, try to get the tree controller again.
-      tree.focus();
-      controller = doGetPlacesControllerForCommand("placesCmd_copy");
-      ok(controller == treeController, "tree controller was returned");
+  // Now that the context menu is closed, try to get the tree controller again.
+  tree.focus();
+  controller = doGetPlacesControllerForCommand("placesCmd_copy");
+  ok(controller == treeController, "tree controller was returned");
+
+  if (wasCollapsed) {
+    yield promiseSetToolbarVisibility(toolbar, false);
+  }
+});
 
-      toggleSidebar();
-      if (wasCollapsed)
-        setToolbarVisibility(toolbar, false);
+function promiseLoadedSidebar(cmd) {
+  return new Promise(resolve => {
+    let sidebar = document.getElementById("sidebar");
+    sidebar.addEventListener("load", function onLoad() {
+      sidebar.removeEventListener("load", onLoad, true);
+      resolve(sidebar);
+    }, true);
 
-      finish();
-    });
-  }, true);
-  toggleSidebar("viewBookmarksSidebar", true);
+    toggleSidebar(cmd, true);
+  });
 }
--- a/browser/components/places/tests/browser/browser_drag_bookmarks_on_toolbar.js
+++ b/browser/components/places/tests/browser/browser_drag_bookmarks_on_toolbar.js
@@ -133,26 +133,16 @@ function getExpectedDataForPlacesNode(aN
     var wrappedFlavor = aFlavor + ": " +
                         PlacesUtils.wrapNode(aNode, aFlavor);
     wrappedNode.push(wrappedFlavor);
   });
 
   return [wrappedNode];
 }
 
-function afterToolbarTransition(callback) {
-  function listener(event) {
-    if (event.propertyName == "max-height") {
-      toolbar.removeEventListener("transitionend", listener);
-      callback();
-    }
-  }
-  toolbar.addEventListener("transitionend", listener);
-}
-
 var gTests = [
 
 //------------------------------------------------------------------------------
 
   {
     desc: "Drag a folder on toolbar",
     run: function() {
       // Create a test folder to be dragged.
@@ -240,31 +230,29 @@ function nextTest() {
     waitForFocus(function() {
       info("Start of test: " + test.desc);
       test.run();
     });
   }
   else {
     // Collapse the personal toolbar if needed.
     if (wasCollapsed) {
-      setToolbarVisibility(toolbar, false);
-      afterToolbarTransition(finish);
+      promiseSetToolbarVisibility(toolbar, false).then(finish);
     } else {
       finish();
     }
   }
 }
 
 let toolbar = document.getElementById("PersonalToolbar");
 let wasCollapsed = toolbar.collapsed;
 
 function test() {
   waitForExplicitFinish();
 
   // Uncollapse the personal toolbar if needed.
   if (wasCollapsed) {
-    setToolbarVisibility(toolbar, true);
-    afterToolbarTransition(nextTest);
+    promiseSetToolbarVisibility(toolbar, true).then(nextTest);
   } else {
     nextTest();
   }
 }
 
--- a/browser/components/places/tests/browser/browser_views_liveupdate.js
+++ b/browser/components/places/tests/browser/browser_views_liveupdate.js
@@ -5,22 +5,27 @@
 /**
  * Tests Places views (menu, toolbar, tree) for liveupdate.
  */
 
 let toolbar = document.getElementById("PersonalToolbar");
 let wasCollapsed = toolbar.collapsed;
 
 function test() {
-  // Uncollapse the personal toolbar if needed.
-  if (wasCollapsed)
-    setToolbarVisibility(toolbar, true);
-
   waitForExplicitFinish();
 
+  // Uncollapse the personal toolbar if needed.
+  if (wasCollapsed) {
+    promiseSetToolbarVisibility(toolbar, true).then(openBookmarksSidebar);
+  } else {
+    openBookmarksSidebar();
+  }
+}
+
+function openBookmarksSidebar() {
   // Sanity checks.
   ok(PlacesUtils, "PlacesUtils in context");
   ok(PlacesUIUtils, "PlacesUIUtils in context");
 
   // Open bookmarks menu.
   var popup = document.getElementById("bookmarksMenuPopup");
   ok(popup, "Menu popup element exists");
   fakeOpenPopup(popup);
@@ -164,20 +169,21 @@ function startTest() {
 /**
  * Restores browser state and calls finish.
  */
 function finishTest() {
   // Close bookmarks sidebar.
   toggleSidebar("viewBookmarksSidebar", false);
 
   // Collapse the personal toolbar if needed.
-  if (wasCollapsed)
-    setToolbarVisibility(toolbar, false);
-
-  finish();
+  if (wasCollapsed) {
+    promiseSetToolbarVisibility(toolbar, false).then(finish);
+  } else {
+    finish();
+  }
 }
 
 /**
  * The observer is where magic happens, for every change we do it will look for
  * nodes positions in the affected views.
  */
 var bookmarksObserver = {
   QueryInterface: XPCOMUtils.generateQI([
--- a/browser/components/places/tests/browser/head.js
+++ b/browser/components/places/tests/browser/head.js
@@ -369,16 +369,82 @@ function promiseHistoryNotification(noti
     let timeout = setTimeout(() => {
       PlacesUtils.history.removeObserver(proxifiedObserver, false);
       reject(new Error("Timed out while waiting for history notification"));
     }, 2000);
   });
 }
 
 /**
+ * Makes the specified toolbar visible or invisible and returns a Promise object
+ * that is resolved when the toolbar has completed any animations associated
+ * with hiding or showing the toolbar.
+ *
+ * Note that this code assumes that changes to a toolbar's visibility trigger
+ * a transition on the max-height property of the toolbar element.
+ * Changes to this styling could cause the returned Promise object to be
+ * resolved too early or not at all.
+ *
+ * @param aToolbar
+ *        The toolbar to update.
+ * @param aVisible
+ *        True to make the toolbar visible, false to make it hidden.
+ *
+ * @return {Promise}
+ * @resolves Any animation associated with updating the toolbar's visibility has
+ *           finished.
+ * @rejects Never.
+ */
+function promiseSetToolbarVisibility(aToolbar, aVisible, aCallback) {
+  return new Promise((resolve, reject) => {
+    function listener(event) {
+      if (event.propertyName == "max-height") {
+        aToolbar.removeEventListener("transitionend", listener);
+        resolve();
+      }
+    }
+
+    let transitionProperties =
+      window.getComputedStyle(aToolbar).transitionProperty.split(", ");
+    if (isToolbarVisible(aToolbar) != aVisible &&
+        transitionProperties.some(
+          prop => prop == "max-height" || prop == "all"
+        )) {
+      // Just because max-height is a transitionable property doesn't mean
+      // a transition will be triggered, but it's more likely.
+      aToolbar.addEventListener("transitionend", listener);
+      setToolbarVisibility(aToolbar, aVisible);
+      return;
+    }
+
+    // No animation to wait for
+    setToolbarVisibility(aToolbar, aVisible);
+    resolve();
+  });
+}
+
+/**
+ * Helper function to determine if the given toolbar is in the visible
+ * state according to its autohide/collapsed attribute.
+ *
+ * @aToolbar The toolbar to query.
+ *
+ * @returns True if the relevant attribute on |aToolbar| indicates it is
+ *          visible, false otherwise.
+ */
+function isToolbarVisible(aToolbar) {
+  let hidingAttribute = aToolbar.getAttribute("type") == "menubar"
+                        ? "autohide"
+                        : "collapsed";
+  let hidingValue = aToolbar.getAttribute(hidingAttribute).toLowerCase();
+  // Check for both collapsed="true" and collapsed="collapsed"
+  return hidingValue !== "true" && hidingValue !== hidingAttribute;
+}
+
+/**
  * Clears history asynchronously.
  *
  * @return {Promise}
  * @resolves When history has been cleared.
  * @rejects Never.
  */
 function promiseClearHistory() {
   let promise = promiseTopicObserved(PlacesUtils.TOPIC_EXPIRATION_FINISHED);
--- a/browser/components/preferences/in-content/sync.js
+++ b/browser/components/preferences/in-content/sync.js
@@ -308,16 +308,26 @@ let gSyncPane = {
         // user is prompted to sign in with the address they previously used.
         let email = subject ? subject.QueryInterface(Components.interfaces.nsISupportsString).data : null;
         let elt = document.getElementById("sync-migrate-upgrade-description");
         elt.textContent = email ?
                           sb.formatStringFromName("signInAfterUpgradeOnOtherDevice.description",
                                                   [email], 1) :
                           sb.GetStringFromName("needUserLong");
 
+        // The "Learn more" link.
+        if (!email) {
+          let learnMoreLink = document.createElement("label");
+          learnMoreLink.className = "text-link";
+          let { text, href } = fxaMigrator.learnMoreLink;
+          learnMoreLink.setAttribute("value", text);
+          learnMoreLink.href = href;
+          elt.appendChild(learnMoreLink);
+        }
+
         // The "upgrade" button.
         let button = document.getElementById("sync-migrate-upgrade");
         button.setAttribute("label",
                             sb.GetStringFromName(email
                                                  ? "signInAfterUpgradeOnOtherDevice.label"
                                                  : "upgradeToFxA.label"));
         button.setAttribute("accesskey",
                             sb.GetStringFromName(email
@@ -398,16 +408,17 @@ let gSyncPane = {
                                 flags,
                                 sb.GetStringFromName("unlinkVerificationConfirm"),
                                 null, null, null, {});
 
     // If the user selects cancel, just bail
     if (buttonChoice == 1)
       return;
 
+    fxaMigrator.recordTelemetry(fxaMigrator.TELEMETRY_UNLINKED);
     Weave.Service.startOver();
     this.updateWeavePrefs();
   },
 
   updatePass: function () {
     if (Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED)
       gSyncUtils.changePassword();
     else
--- a/browser/components/preferences/sync.js
+++ b/browser/components/preferences/sync.js
@@ -208,16 +208,26 @@ let gSyncPane = {
         // user is prompted to sign in with the address they previously used.
         let email = subject ? subject.QueryInterface(Components.interfaces.nsISupportsString).data : null;
         let elt = document.getElementById("sync-migrate-upgrade-description");
         elt.textContent = email ?
                           sb.formatStringFromName("signInAfterUpgradeOnOtherDevice.description",
                                                   [email], 1) :
                           sb.GetStringFromName("needUserLong");
 
+        // The "Learn more" link.
+        if (!email) {
+          let learnMoreLink = document.createElement("label");
+          learnMoreLink.className = "text-link";
+          let { text, href } = fxaMigrator.learnMoreLink;
+          learnMoreLink.setAttribute("value", text);
+          learnMoreLink.href = href;
+          elt.appendChild(learnMoreLink);
+        }
+
         // The "upgrade" button.
         let button = document.getElementById("sync-migrate-upgrade");
         button.setAttribute("label",
                             sb.GetStringFromName(email
                                                  ? "signInAfterUpgradeOnOtherDevice.label"
                                                  : "upgradeToFxA.label"));
         button.setAttribute("accesskey",
                             sb.GetStringFromName(email
--- a/browser/components/search/test/browser_bing.js
+++ b/browser/components/search/test/browser_bing.js
@@ -8,17 +8,17 @@
 "use strict";
 
 const BROWSER_SEARCH_PREF = "browser.search.";
 
 function test() {
   let engine = Services.search.getEngineByName("Bing");
   ok(engine, "Bing");
 
-  let base = "http://www.bing.com/search?q=foo&pc=MOZI";
+  let base = "https://www.bing.com/search?q=foo&pc=MOZI";
   let url;
 
   // Test search URLs (including purposes).
   url = engine.getSubmission("foo").uri.spec;
   is(url, base, "Check search URL for 'foo'");
   url = engine.getSubmission("foo", null, "contextmenu").uri.spec;
   is(url, base + "&form=MOZCON", "Check context menu search URL for 'foo'");
   url = engine.getSubmission("foo", null, "keyword").uri.spec;
@@ -27,34 +27,34 @@ function test() {
   is(url, base + "&form=MOZSBR", "Check search bar search URL for 'foo'");
   url = engine.getSubmission("foo", null, "homepage").uri.spec;
   is(url, base + "&form=MOZSPG", "Check homepage search URL for 'foo'");
   url = engine.getSubmission("foo", null, "newtab").uri.spec;
   is(url, base + "&form=MOZTSB", "Check newtab search URL for 'foo'");
 
   // Check search suggestion URL.
   url = engine.getSubmission("foo", "application/x-suggestions+json").uri.spec;
-  is(url, "http://api.bing.com/osjson.aspx?query=foo&form=OSDJAS&language=" + getLocale(), "Check search suggestion URL for 'foo'");
+  is(url, "https://www.bing.com/osjson.aspx?query=foo&form=OSDJAS&language=" + getLocale(), "Check search suggestion URL for 'foo'");
 
   // Check all other engine properties.
   const EXPECTED_ENGINE = {
     name: "Bing",
     alias: null,
     description: "Bing. Search by Microsoft.",
-    searchForm: "http://www.bing.com/search?q=&pc=MOZI",
+    searchForm: "https://www.bing.com/search?q=&pc=MOZI",
     type: Ci.nsISearchEngine.TYPE_MOZSEARCH,
     hidden: false,
     wrappedJSObject: {
       queryCharset: "UTF-8",
       "_iconURL": "data:image/x-icon;base64,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",
       _urls : [
         {
           type: "application/x-suggestions+json",
           method: "GET",
-          template: "http://api.bing.com/osjson.aspx",
+          template: "https://www.bing.com/osjson.aspx",
           params: [
             {
               name: "query",
               value: "{searchTerms}",
               purpose: undefined,
             },
             {
               name: "form",
@@ -66,17 +66,17 @@ function test() {
               value: "{moz:locale}",
               purpose: undefined,
             },
           ],
         },
         {
           type: "text/html",
           method: "GET",
-          template: "http://www.bing.com/search",
+          template: "https://www.bing.com/search",
           params: [
             {
               name: "q",
               value: "{searchTerms}",
               purpose: undefined,
             },
             {
               name: "pc",
--- a/browser/components/search/test/browser_bing_behavior.js
+++ b/browser/components/search/test/browser_bing_behavior.js
@@ -13,17 +13,17 @@ const BROWSER_SEARCH_PREF = "browser.sea
 function test() {
   let engine = Services.search.getEngineByName("Bing");
   ok(engine, "Bing is installed");
 
   let previouslySelectedEngine = Services.search.currentEngine;
   Services.search.currentEngine = engine;
   engine.alias = "b";
   
-  let base = "http://www.bing.com/search?q=foo&pc=MOZI";
+  let base = "https://www.bing.com/search?q=foo&pc=MOZI";
   let url;
 
   // Test search URLs (including purposes).
   url = engine.getSubmission("foo").uri.spec;
   is(url, base, "Check search URL for 'foo'");
 
   waitForExplicitFinish();
 
--- a/browser/components/uitour/UITour-lib.js
+++ b/browser/components/uitour/UITour-lib.js
@@ -93,16 +93,25 @@ if (typeof Mozilla == 'undefined') {
   };
 
 	Mozilla.UITour.registerPageID = function(pageID) {
 		_sendEvent('registerPageID', {
 			pageID: pageID
 		});
 	};
 
+	Mozilla.UITour.showHeartbeat = function(message, thankyouMessage, flowId, engagementURL) {
+		_sendEvent('showHeartbeat', {
+			message: message,
+			thankyouMessage: thankyouMessage,
+			flowId: flowId,
+			engagementURL: engagementURL
+		});
+	};
+
 	Mozilla.UITour.showHighlight = function(target, effect) {
 		_sendEvent('showHighlight', {
 			target: target,
 			effect: effect
 		});
 	};
 
 	Mozilla.UITour.hideHighlight = function() {
--- a/browser/components/uitour/UITour.jsm
+++ b/browser/components/uitour/UITour.jsm
@@ -8,16 +8,18 @@ this.EXPORTED_SYMBOLS = ["UITour", "UITo
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 
+Cu.importGlobalProperties(["URL"]);
+
 XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
   "resource://gre/modules/LightweightThemeManager.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ResetProfile",
   "resource://gre/modules/ResetProfile.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
   "resource:///modules/CustomizableUI.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "UITelemetry",
   "resource://gre/modules/UITelemetry.jsm");
@@ -394,16 +396,39 @@ this.UITour = {
 
         this.addSeenPageID(data.pageID);
         this.pageIDSourceBrowsers.set(browser, data.pageID);
         this.setTelemetryBucket(data.pageID);
 
         break;
       }
 
+      case "showHeartbeat": {
+        // Validate the input parameters.
+        if (typeof data.message !== "string" || data.message === "") {
+          log.error("showHeartbeat: Invalid message specified.");
+          break;
+        }
+
+        if (typeof data.thankyouMessage !== "string" || data.thankyouMessage === "") {
+          log.error("showHeartbeat: Invalid thank you message specified.");
+          break;
+        }
+
+        if (typeof data.flowId !== "string" || data.flowId === "") {
+          log.error("showHeartbeat: Invalid flowId specified.");
+          break;
+        }
+
+        // Finally show the Heartbeat UI.
+        this.showHeartbeat(window, messageManager, data.message, data.thankyouMessage, data.flowId,
+                           data.engagementURL);
+        break;
+      }
+
       case "showHighlight": {
         let targetPromise = this.getTarget(window, data.target);
         targetPromise.then(target => {
           if (!target.node) {
             log.error("UITour: Target could not be resolved: " + data.target);
             return;
           }
           let effect = undefined;
@@ -963,16 +988,147 @@ this.UITour = {
       LightweightThemeManager.previewTheme(data);
   },
 
   resetTheme: function() {
     LightweightThemeManager.resetPreview();
   },
 
   /**
+   * Show the Heartbeat UI to request user feedback. This function reports back to the
+   * caller using |notify|. The notification event name reflects the current status the UI
+   * is in (either "Heartbeat:NotificationOffered", "Heartbeat:NotificationClosed" or
+   * "Heartbeat:Voted"). When a "Heartbeat:Voted" event is notified the data payload contains
+   * a |score| field which holds the rating picked by the user.
+   * Please note that input parameters are already validated by the caller.
+   *
+   * @param aChromeWindow
+   *        The chrome window that the heartbeat notification is displayed in.
+   * @param aMessageManager
+   *        The message manager to communicate with the API caller.
+   * @param aMessage
+   *        The message, or question, to display on the notification.
+   * @param aThankyouMessage
+   *        The thank you message to display after user votes.
+   * @param aFlowId
+   *        An identifier for this rating flow. Please note that this is only used to
+   *        identify the notification box.
+   * @param [aEngagementURL]
+   *        The engagement URL to open in a new tab once user has voted. If this is null
+   *        or invalid, no new tab is opened.
+   */
+  showHeartbeat: function(aChromeWindow, aMessageManager, aMessage, aThankyouMessage, aFlowId,
+                          aEngagementURL = null) {
+    let nb = aChromeWindow.document.getElementById("high-priority-global-notificationbox");
+
+    // Create the notification. Prefix its ID to decrease the chances of collisions.
+    let notice = nb.appendNotification(aMessage, "heartbeat-" + aFlowId,
+      "chrome://branding/content/icon64.png", nb.PRIORITY_INFO_HIGH, null, function() {
+        // Let the consumer know the notification bar was closed. This also happens
+        // after voting.
+        this.notify("Heartbeat:NotificationClosed", { flowId: aFlowId, timestamp: Date.now() });
+    }.bind(this));
+
+    // Get the elements we need to style.
+    let messageImage =
+      aChromeWindow.document.getAnonymousElementByAttribute(notice, "anonid", "messageImage");
+    let messageText =
+      aChromeWindow.document.getAnonymousElementByAttribute(notice, "anonid", "messageText");
+
+    // Create the fragment holding the rating UI.
+    let frag = aChromeWindow.document.createDocumentFragment();
+
+    // Build the Heartbeat star rating.
+    const numStars = 5;
+    let ratingContainer = aChromeWindow.document.createElement("hbox");
+    ratingContainer.id = "star-rating-container";
+
+    for (let i = 0; i < numStars; i++) {
+      // Create a star rating element.
+      let ratingElement = aChromeWindow.document.createElement("toolbarbutton");
+
+      // Style it.
+      let starIndex = numStars - i;
+      ratingElement.className = "plain star-x";
+      ratingElement.id = "star" + starIndex;
+      ratingElement.setAttribute("data-score", starIndex);
+
+      // Add the click handler.
+      ratingElement.addEventListener("click", function (evt) {
+        let rating = Number(evt.target.getAttribute("data-score"), 10);
+
+        // Let the consumer know user voted.
+        this.notify("Heartbeat:Voted", { flowId: aFlowId, score: rating, timestamp: Date.now() });
+
+        // Display the Heart and make it pulse twice.
+        notice.image = "chrome://browser/skin/heartbeat-icon.svg";
+        notice.label = aThankyouMessage;
+        messageImage.classList.remove("pulse-onshow");
+        messageImage.classList.add("pulse-twice");
+
+        // Remove all the children of the notice (rating container
+        // and the flex).
+        while (notice.firstChild) {
+          notice.removeChild(notice.firstChild);
+        }
+
+        // Make sure that we have a valid URL. If we haven't, do not open the engagement page.
+        let engagementURL = null;
+        try {
+          engagementURL = new URL(aEngagementURL);
+        } catch (error) {
+          log.error("showHeartbeat: Invalid URL specified.");
+        }
+
+        // Just open the engagement tab if we have a valid engagement URL.
+        if (engagementURL) {
+          // Append the score data to the engagement URL.
+          engagementURL.searchParams.append("type", "stars");
+          engagementURL.searchParams.append("score", rating);
+          engagementURL.searchParams.append("flowid", aFlowId);
+
+          // Open the engagement URL in a new tab.
+          aChromeWindow.gBrowser.selectedTab =
+            aChromeWindow.gBrowser.addTab(engagementURL.toString(), {
+              owner: aChromeWindow.gBrowser.selectedTab,
+              relatedToCurrent: true
+            });
+        }
+
+        // Remove the notification bar after 3 seconds.
+        aChromeWindow.setTimeout(() => {
+          nb.removeNotification(notice);
+        }, 3000);
+      }.bind(this));
+
+      // Add it to the container.
+      ratingContainer.appendChild(ratingElement);
+    }
+
+    frag.appendChild(ratingContainer);
+
+    // Make sure the stars are not pushed to the right by the spacer.
+    let rightSpacer = aChromeWindow.document.createElement("spacer");
+    rightSpacer.flex = 20;
+    frag.appendChild(rightSpacer);
+
+    let leftSpacer = messageText.nextSibling;
+    leftSpacer.flex = 0;
+
+    // Append the fragment and apply the styling.
+    notice.appendChild(frag);
+    notice.classList.add("heartbeat");
+    messageImage.classList.add("heartbeat", "pulse-onshow");
+    messageText.classList.add("heartbeat");
+
+    // Let the consumer know the notification was shown.
+    this.notify("Heartbeat:NotificationOffered", { flowId: aFlowId, timestamp: Date.now() });
+  },
+
+  /**
    * @param aChromeWindow The chrome window that the highlight is in. Necessary since some targets
    *                      are in a sub-frame so the defaultView is not the same as the chrome
    *                      window.
    * @param aTarget    The element to highlight.
    * @param aEffect    (optional) The effect to use from UITour.highlightEffects or "none".
    * @see UITour.highlightEffects
    */
   showHighlight: function(aChromeWindow, aTarget, aEffect = "none") {
--- a/browser/components/uitour/test/browser.ini
+++ b/browser/components/uitour/test/browser.ini
@@ -12,16 +12,18 @@ skip-if = e10s # Bug 941428 - UITour.jsm
 # [browser_UITour3.js] Bug 1113038
 # skip-if = os == "linux" || e10s # Linux: Bug 986760, Bug 989101; e10s: Bug 941428 - UITour.jsm not e10s friendly
 [browser_UITour_availableTargets.js]
 skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
 [browser_UITour_detach_tab.js]
 skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
 [browser_UITour_annotation_size_attributes.js]
 skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
+[browser_UITour_heartbeat.js]
+skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
 [browser_UITour_loop.js]
 skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
 [browser_UITour_modalDialog.js]
 run-if = os == "mac" # modal dialog disabling only working on OS X
 skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
 [browser_UITour_observe.js]
 skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
 [browser_UITour_panel_close_annotation.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/uitour/test/browser_UITour_heartbeat.js
@@ -0,0 +1,231 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+let gTestTab;
+let gContentAPI;
+let gContentWindow;
+let notificationBox = document.getElementById("high-priority-global-notificationbox");
+
+Components.utils.import("resource:///modules/UITour.jsm");
+
+function test() {
+  UITourTest();
+}
+
+/**
+ * Simulate a click on a rating element in the Heartbeat notification.
+ *
+ * @param aId
+ *        The id of the notification box.
+ * @param aScore
+ *        The score related to the rating element we want to click on.
+ */
+function simulateVote(aId, aScore) {
+  // UITour.jsm prefixes the notification box ID with "heartbeat-" to prevent collisions.
+  let notification = notificationBox.getNotificationWithValue("heartbeat-" + aId);
+
+  let ratingContainer = notification.childNodes[0];
+  ok(ratingContainer, "The notification has a valid rating container.");
+
+  let ratingElement = ratingContainer.getElementsByAttribute("data-score", aScore);
+  ok(ratingElement[0], "The rating container contains the requested rating element.");
+
+  ratingElement[0].click();
+}
+
+/**
+ * Remove the notification box.
+ *
+ * @param aId
+ *        The id of the notification box to remove.
+ */
+function cleanUpNotification(aId) {
+  let notification = notificationBox.getNotificationWithValue("heartbeat-" + aId);
+  notificationBox.removeNotification(notification);
+}
+
+let tests = [
+  /**
+   * Check that the "stars" heartbeat UI correctly shows and closes.
+   */
+  function test_heartbeat_stars_show(done) {
+    let flowId = "ui-ratefirefox-" + Math.random();
+    let engagementURL = "http://example.com";
+
+    gContentAPI.observe(function (aEventName, aData) {
+      switch (aEventName) {
+        case "Heartbeat:NotificationOffered": {
+          info("'Heartbeat:Offered' notification received (timestamp " + aData.timestamp.toString() + ").");
+          ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
+          cleanUpNotification(flowId);
+          break;
+        }
+        case "Heartbeat:NotificationClosed": {
+          info("'Heartbeat:NotificationClosed' notification received (timestamp " + aData.timestamp.toString() + ").");
+          ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
+          done();
+          break;
+        }
+        default:
+          // We are not expecting other states for this test.
+          ok(false, "Unexpected notification received: " + aEventName);
+      }
+    });
+
+    gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, engagementURL);
+  },
+
+  /**
+   * Test that the heartbeat UI correctly works with null engagement URL.
+   */
+  function test_heartbeat_null_engagementURL(done) {
+    let flowId = "ui-ratefirefox-" + Math.random();
+    let originalTabCount = gBrowser.tabs.length;
+
+    gContentAPI.observe(function (aEventName, aData) {
+      switch (aEventName) {
+        case "Heartbeat:NotificationOffered": {
+          info("'Heartbeat:Offered' notification received (timestamp " + aData.timestamp.toString() + ").");
+          ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
+          // The UI was just shown. We can simulate a click on a rating element (i.e., "star").
+          simulateVote(flowId, 2);
+          break;
+        }
+        case "Heartbeat:Voted": {
+          info("'Heartbeat:Voted' notification received (timestamp " + aData.timestamp.toString() + ").");
+          ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
+          break;
+        }
+        case "Heartbeat:NotificationClosed": {
+          info("'Heartbeat:NotificationClosed' notification received (timestamp " + aData.timestamp.toString() + ").");
+          ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
+          is(gBrowser.tabs.length, originalTabCount, "No engagement tab should be opened.");
+          done();
+          break;
+        }
+        default:
+          // We are not expecting other states for this test.
+          ok(false, "Unexpected notification received: " + aEventName);
+      }
+    });
+
+    gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, null);
+  },
+
+   /**
+   * Test that the heartbeat UI correctly works with an invalid, but non null, engagement URL.
+   */
+  function test_heartbeat_invalid_engagement_URL(done) {
+    let flowId = "ui-ratefirefox-" + Math.random();
+    let originalTabCount = gBrowser.tabs.length;
+    let invalidEngagementURL = "invalidEngagement";
+
+    gContentAPI.observe(function (aEventName, aData) {
+      switch (aEventName) {
+        case "Heartbeat:NotificationOffered": {
+          info("'Heartbeat:Offered' notification received (timestamp " + aData.timestamp.toString() + ").");
+          ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
+          // The UI was just shown. We can simulate a click on a rating element (i.e., "star").
+          simulateVote(flowId, 2);
+          break;
+        }
+        case "Heartbeat:Voted": {
+          info("'Heartbeat:Voted' notification received (timestamp " + aData.timestamp.toString() + ").");
+          ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
+          break;
+        }
+        case "Heartbeat:NotificationClosed": {
+          info("'Heartbeat:NotificationClosed' notification received (timestamp " + aData.timestamp.toString() + ").");
+          ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
+          is(gBrowser.tabs.length, originalTabCount, "No engagement tab should be opened.");
+          done();
+          break;
+        }
+        default:
+          // We are not expecting other states for this test.
+          ok(false, "Unexpected notification received: " + aEventName);
+      }
+    });
+
+    gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, invalidEngagementURL);
+  },
+
+  /**
+   * Test that the score is correctly reported.
+   */
+  function test_heartbeat_stars_vote(done) {
+    const expectedScore = 4;
+    let flowId = "ui-ratefirefox-" + Math.random();
+
+    gContentAPI.observe(function (aEventName, aData) {
+      switch (aEventName) {
+        case "Heartbeat:NotificationOffered": {
+          info("'Heartbeat:Offered' notification received (timestamp " + aData.timestamp.toString() + ").");
+          ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
+          // The UI was just shown. We can simulate a click on a rating element (i.e., "star").
+          simulateVote(flowId, expectedScore);
+          break;
+        }
+        case "Heartbeat:Voted": {
+          info("'Heartbeat:Voted' notification received (timestamp " + aData.timestamp.toString() + ").");
+          ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
+          is(aData.score, expectedScore, "Should report a score of " + expectedScore);
+          done();
+          break;
+        }
+        case "Heartbeat:NotificationClosed": {
+          info("'Heartbeat:NotificationClosed' notification received (timestamp " + aData.timestamp.toString() + ").");
+          ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
+          break;
+        }
+        default:
+          // We are not expecting other states for this test.
+          ok(false, "Unexpected notification received: " + aEventName);
+      }
+    });
+
+    gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, null);
+  },
+
+  /**
+   * Test that the engagement page is correctly opened when voting.
+   */
+  function test_heartbeat_engagement_tab(done) {
+    let engagementURL = "http://example.com";
+    let flowId = "ui-ratefirefox-" + Math.random();
+    let originalTabCount = gBrowser.tabs.length;
+    const expectedTabCount = originalTabCount + 1;
+
+    gContentAPI.observe(function (aEventName, aData) {
+      switch (aEventName) {
+        case "Heartbeat:NotificationOffered": {
+          info("'Heartbeat:Offered' notification received (timestamp " + aData.timestamp.toString() + ").");
+          ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
+          // The UI was just shown. We can simulate a click on a rating element (i.e., "star").
+          simulateVote(flowId, 1);
+          break;
+        }
+        case "Heartbeat:Voted": {
+          info("'Heartbeat:Voted' notification received (timestamp " + aData.timestamp.toString() + ").");
+          ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
+          break;
+        }
+        case "Heartbeat:NotificationClosed": {
+          info("'Heartbeat:NotificationClosed' notification received (timestamp " + aData.timestamp.toString() + ").");
+          ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
+          is(gBrowser.tabs.length, expectedTabCount, "Engagement URL should open in a new tab.");
+          gBrowser.removeCurrentTab();
+          done();
+          break;
+        }
+        default:
+          // We are not expecting other states for this test.
+          ok(false, "Unexpected notification received: " + aEventName);
+      }
+    });
+
+    gContentAPI.showHeartbeat("How would you rate Firefox?", "Thank you!", flowId, engagementURL);
+  }
+];
--- a/browser/devtools/webconsole/console-output.js
+++ b/browser/devtools/webconsole/console-output.js
@@ -888,23 +888,26 @@ Messages.Simple.prototype = Heritage.ext
    * @private
    * @return Element
    */
   _renderBody: function()
   {
     let body = this.document.createElementNS(XHTML_NS, "span");
     body.className = "message-body-wrapper message-body devtools-monospace";
 
-    let anchor, container = body;
+    let bodyInner = this.document.createElementNS(XHTML_NS, "span");
+    body.appendChild(bodyInner);
+
+    let anchor, container = bodyInner;
     if (this._link || this._linkCallback) {
       container = anchor = this.document.createElementNS(XHTML_NS, "a");
       anchor.href = this._link || "#";
       anchor.draggable = false;
       this._addLinkCallback(anchor, this._linkCallback);
-      body.appendChild(anchor);
+      bodyInner.appendChild(anchor);
     }
 
     if (typeof this._message == "function") {
       container.appendChild(this._message(this));
     } else if (this._message instanceof Ci.nsIDOMNode) {
       container.appendChild(this._message);
     } else {
       container.textContent = this._message;
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_587617_output_copy.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_587617_output_copy.js
@@ -52,17 +52,19 @@ function consoleOpened(aHud) {
     HUD.ui.output.selectMessage(msg);
 
     outputNode.focus();
 
     goUpdateCommand("cmd_copy");
     controller = top.document.commandDispatcher.getControllerForCommand("cmd_copy");
     is(controller.isCommandEnabled("cmd_copy"), true, "cmd_copy is enabled");
 
-    let selection = HUD.iframeWindow.getSelection() + "";
+    // Remove new lines since getSelection() includes one between message and line
+    // number, but the clipboard doesn't (see bug 1119503)
+    let selection = (HUD.iframeWindow.getSelection() + "").replace(/\r?\n|\r/g, " ");
     isnot(selection.indexOf("bug587617"), -1,
           "selection text includes 'bug587617'");
 
     waitForClipboard((str) => { return selection.trim() == str.trim(); },
       () => { goDoCommand("cmd_copy") },
       deferred.resolve, deferred.resolve);
   });
   return deferred.promise;
@@ -75,17 +77,19 @@ function testContextMenuCopy() {
 
   let contextMenuId = outputNode.parentNode.getAttribute("context");
   let contextMenu = HUD.ui.document.getElementById(contextMenuId);
   ok(contextMenu, "the output node has a context menu");
 
   let copyItem = contextMenu.querySelector("*[command='cmd_copy']");
   ok(copyItem, "the context menu on the output node has a \"Copy\" item");
 
-  let selection = HUD.iframeWindow.getSelection() + "";
+  // Remove new lines since getSelection() includes one between message and line
+  // number, but the clipboard doesn't (see bug 1119503)
+  let selection = (HUD.iframeWindow.getSelection() + "").replace(/\r?\n|\r/g, " ");
 
   copyItem.doCommand();
 
   waitForClipboard((str) => { return selection.trim() == str.trim(); },
     () => { goDoCommand("cmd_copy") },
     deferred.resolve, deferred.resolve);
   HUD = outputNode = null;
 
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_613280_jsterm_copy.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_613280_jsterm_copy.js
@@ -64,15 +64,17 @@ function performTest(HUD, [result]) {
   HUD.outputNode.focus();
 
   goUpdateCommand("cmd_copy");
 
   controller = top.document.commandDispatcher.
                getControllerForCommand("cmd_copy");
   is(controller.isCommandEnabled("cmd_copy"), true, "cmd_copy is enabled");
 
-  let selectionText = HUD.iframeWindow.getSelection() + "";
+  // Remove new lines since getSelection() includes one between message and line
+  // number, but the clipboard doesn't (see bug 1119503)
+  let selectionText = (HUD.iframeWindow.getSelection() + "").replace(/\r?\n|\r/g, " ");
   isnot(selectionText.indexOf("foobarBazBug613280"), -1,
         "selection text includes 'foobarBazBug613280'");
 
   waitForClipboard((str) => { return str.trim() == selectionText.trim(); },
                    clipboard_setup, clipboard_copy_done, clipboard_copy_done);
 }
--- a/browser/locales/en-US/searchplugins/bing.xml
+++ b/browser/locales/en-US/searchplugins/bing.xml
@@ -4,22 +4,22 @@
 
 <SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
     <ShortName>Bing</ShortName>
     <Description>Bing. Search by Microsoft.</Description>
     <InputEncoding>UTF-8</InputEncoding>
     <Image width="16" height="16">data:image/x-icon;base64,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</Image>
     <Image width="65" height="26">data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAAEEAAAAaCAYAAADovjFxAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyNpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NDkxMSwgMjAxMy8xMC8yOS0xMTo0NzoxNiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkVBOTQzNENBQzQxNDExRTM5RURBRUU5NUU4QkU3NjA4IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkVBOTQzNENCQzQxNDExRTM5RURBRUU5NUU4QkU3NjA4Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NzU2RTRERkZDMzc1MTFFMzlFREFFRTk1RThCRTc2MDgiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NzU2RTRFMDBDMzc1MTFFMzlFREFFRTk1RThCRTc2MDgiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz76XOiPAAAD1ElEQVR42uyYeUgVQRzH9+nLNE07kMKIiqAkSaUIMgUpyu6eHdBhVBSBZQf1R4XkP0WHFUURWBlRmGGHdkI3nWIXVHZIaNkhFXmWYni+vhPfhWHZfbvvqVjZDz7Mzs6w8+Y3v2ue3eFwPFUU5R64CG6BRqWDiReIBCvBNfAeHAXjOpoSqqV+H7AQbO1oStCTz/+VoCjONlpvDdgPelqc3x8kgMD2UEJbSTRIAkMszhfB+jhIbU8lxIIZrbjeV7b1FudngefgansqoQ5kg3cgDUwBXVthXavutoXZ61x7KuEBOAkGgESap0ijGWAeCPZwXZv0bNf0PZFObuzX7klM2Kjp9wDzQSYVcgWsAqFubD4fjAIloAE0g0sgXDN/Li1xBPt+IAcks3+KViXc6xmYbrDuGBaETVzvFegHloKzVpRQBK4bjHUB48FeUADyQDcX32pmuxPkgkr6vahaJ9P/Q3Vi0mD2fbjRdeAln4VSboIIPkdp1lzA8RjuQ1h2CA9wH4i3mh2SLc4bCQJMYozCDDEbDOVpDwPLOHZGml/KtlpSongOArV0g5lgLFguxRFVxGaP0VrCQByYA7pT+b7ie1aV8ASMBhdM5jWY3D3UgLiLpizLAfCGP3agi2/4s43RvE+jucdKbpfEdjV4reNqVSLQu1Mn3AYOxoTcFgawHIPxbLZhJsHtg0Ga/cJxVdkTdKxLlnxFL1JKvqcn4oQWMai0RAk/DcarpFjjSmpN3E2uOOX6RCuNrpTQWdPvC9ZL5uVqk3YL7mBUJ4SwLTdZx2gNb4P1vA3c1McoRZaBxXwWAWgbM0SShZN+KJ2mnqgm3MtgXE1xL1qpDiowca8IReM/ClNUMH1OpKG3YIML91DlB1jL7FDjYp56Grt1xmbRzfJcmK9VUS0ik+0OnTmbWf3WeEkbPMQSdRqVkGrxtncYDAJ7LMwNlE6mhEXXRLAdnOZYgjTfTxNLbJrvaCVI009nPRDH+0c8mMrALIrAQuFaduZeUUAcAZeliGom92kl7mSKeqbRSSyYMqSxYiqgWHpXw/l1ko+LfoXB98s1dUoTrfMEFREnjQ1nbbHE5nA4vvOCFGlxI2UsntI9MNNARn7V3KNobd/AI535AaSSirAxnjTyd2glmK6g507hDPBCsXf47qN4J5SQyELDihykAir+gT+U/KmQIi9WasKn75qYfjRvkn+bAoINAud5Pqeo+baQ5WYK2KT5EySFwe9vFF+6Win/kxAn35sls3oLzbLrpI0b3PRjsMIk5f3pYmPsSuC1WZVPrH9+hwGb0+lUOrr8EmAAiqTecoRlgJMAAAAASUVORK5CYII=</Image>
     <Image width="130" height="52">data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAAIIAAAA0CAYAAABGkOCVAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyNpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NDkxMSwgMjAxMy8xMC8yOS0xMTo0NzoxNiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjc1NkU0REZEQzM3NTExRTM5RURBRUU5NUU4QkU3NjA4IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjc1NkU0REZFQzM3NTExRTM5RURBRUU5NUU4QkU3NjA4Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NzU2RTRERkJDMzc1MTFFMzlFREFFRTk1RThCRTc2MDgiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NzU2RTRERkNDMzc1MTFFMzlFREFFRTk1RThCRTc2MDgiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz5lzzn9AAAIPUlEQVR42uxdCWwV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</Image>
-    <Url type="application/x-suggestions+json" template="http://api.bing.com/osjson.aspx">
+    <Url type="application/x-suggestions+json" template="https://www.bing.com/osjson.aspx">
         <Param name="query" value="{searchTerms}"/>
         <Param name="form" value="OSDJAS"/>
         <Param name="language" value="{moz:locale}"/>
     </Url>
-    <Url type="text/html" method="GET" template="http://www.bing.com/search" rel="searchform">
+    <Url type="text/html" method="GET" template="https://www.bing.com/search" rel="searchform">
         <Param name="q" value="{searchTerms}"/>
         <Param name="pc" value="MOZI"/>
         <MozParam name="form" condition="purpose" purpose="contextmenu" value="MOZCON"/>
         <MozParam name="form" condition="purpose" purpose="searchbar" value="MOZSBR"/>
         <MozParam name="form" condition="purpose" purpose="homepage" value="MOZSPG"/>
         <MozParam name="form" condition="purpose" purpose="keyword" value="MOZLBR"/>
         <MozParam name="form" condition="purpose" purpose="newtab" value="MOZTSB"/>
     </Url>
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -29,16 +29,19 @@ browser.jar:
   skin/classic/browser/click-to-play-warning-stripes.png
   skin/classic/browser/content-contextmenu.svg
   skin/classic/browser/dots.png                             (../shared/dots.png)
   skin/classic/browser/dots@2x.png                          (../shared/dots@2x.png)
 * skin/classic/browser/engineManager.css
   skin/classic/browser/fullscreen-darknoise.png
   skin/classic/browser/Geolocation-16.png
   skin/classic/browser/Geolocation-64.png
+  skin/classic/browser/heartbeat-icon.svg                   (../shared/heartbeat-icon.svg)
+  skin/classic/browser/heartbeat-star-lit.svg               (../shared/heartbeat-star-lit.svg)
+  skin/classic/browser/heartbeat-star-off.svg               (../shared/heartbeat-star-off.svg)
   skin/classic/browser/identity.png
   skin/classic/browser/identity-icons-generic.png
   skin/classic/browser/identity-icons-https.png
   skin/classic/browser/identity-icons-https-ev.png
   skin/classic/browser/identity-icons-https-mixed-active.png
   skin/classic/browser/identity-icons-https-mixed-display.png
   skin/classic/browser/Info.png
   skin/classic/browser/magnifier.png                        (../shared/magnifier.png)
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -31,16 +31,19 @@ browser.jar:
   skin/classic/browser/dots.png                             (../shared/dots.png)
   skin/classic/browser/dots@2x.png                          (../shared/dots@2x.png)
 * skin/classic/browser/engineManager.css                    (engineManager.css)
   skin/classic/browser/fullscreen-darknoise.png
   skin/classic/browser/Geolocation-16.png
   skin/classic/browser/Geolocation-16@2x.png
   skin/classic/browser/Geolocation-64.png
   skin/classic/browser/Geolocation-64@2x.png
+  skin/classic/browser/heartbeat-icon.svg                   (../shared/heartbeat-icon.svg)
+  skin/classic/browser/heartbeat-star-lit.svg               (../shared/heartbeat-star-lit.svg)
+  skin/classic/browser/heartbeat-star-off.svg               (../shared/heartbeat-star-off.svg)
   skin/classic/browser/identity.png
   skin/classic/browser/identity@2x.png
   skin/classic/browser/identity-icons-generic.png
   skin/classic/browser/identity-icons-generic@2x.png
   skin/classic/browser/identity-icons-https.png
   skin/classic/browser/identity-icons-https@2x.png
   skin/classic/browser/identity-icons-https-ev.png
   skin/classic/browser/identity-icons-https-ev@2x.png
--- a/browser/themes/shared/UITour.inc.css
+++ b/browser/themes/shared/UITour.inc.css
@@ -190,8 +190,115 @@
   .SearchHighlight .dot {
     background-image: -moz-image-rect(url("chrome://browser/skin/dots@2x.png"), 0, 100%, 100%, 18);
   }
 
   .SearchHighlight .dot.filled {
     background-image: -moz-image-rect(url("chrome://browser/skin/dots@2x.png"), 0, 14, 100%, 0);
   }
 }
+
+/* Notification overrides for Heartbeat UI */
+
+notification.heartbeat {
+  background-color: #F1F1F1;
+%ifdef XP_MACOSX
+  background-image: linear-gradient(-179deg, #FBFBFB 0%, #EBEBEB 100%);
+%endif
+  box-shadow: 0px 1px 0px 0px rgba(0,0,0,0.35);
+}
+
+@keyframes pulse-onshow {
+ 0% {
+   opacity: 0;
+   transform: scale(1.0);
+ }
+ 25% {
+   opacity: 1;
+   transform: scale(1.1);
+ }
+ 50% {
+   transform: scale(1.0);
+ }
+ 75% {
+   transform: scale(1.1);
+ }
+ 100% {
+   transform: scale(1.0);
+ }
+}
+
+@keyframes pulse-twice {
+ 0% {
+   transform: scale(1.1);
+ }
+ 50% {
+   transform: scale(0.8);
+ }
+ 100% {
+   transform: scale(1);
+ }
+}
+
+.messageText.heartbeat {
+  color: #333333;
+  font-weight: normal;
+  font-family: "Lucida Grande", Segoe, Ubuntu;
+  font-size: 14px;
+  line-height: 16px;
+  text-shadow: none;
+}
+
+.messageImage.heartbeat {
+  width: 36px;
+  height: 36px;
+  -moz-margin-end: 10px;
+}
+
+.messageImage.heartbeat.pulse-onshow {
+  animation-name: pulse-onshow;
+  animation-duration: 1.5s;
+  animation-iteration-count: 1;
+  animation-timing-function: cubic-bezier(.7,1.8,.9,1.1);
+}
+
+.messageImage.heartbeat.pulse-twice {
+  animation-name: pulse-twice;
+  animation-duration: 1s;
+  animation-iteration-count: 2;
+  animation-timing-function: linear;
+}
+
+/* Heartbeat UI Rating Star Classes */
+.heartbeat > #star-rating-container {
+  display: -moz-box;
+}
+
+.heartbeat > #star-rating-container > #star5 {
+  -moz-box-ordinal-group: 5;
+}
+
+.heartbeat > #star-rating-container > #star4 {
+  -moz-box-ordinal-group: 4;
+}
+
+.heartbeat > #star-rating-container > #star3 {
+  -moz-box-ordinal-group: 3;
+}
+
+.heartbeat > #star-rating-container > #star2 {
+  -moz-box-ordinal-group: 2;
+}
+
+.heartbeat > #star-rating-container > .star-x  {
+  background: url("chrome://browser/skin/heartbeat-star-off.svg");
+  cursor: pointer;
+  width: 24px;
+  height: 24px;
+}
+
+.heartbeat > #star-rating-container > .star-x:hover,
+.heartbeat > #star-rating-container > .star-x:hover ~ .star-x {
+  background: url("chrome://browser/skin/heartbeat-star-lit.svg");
+  cursor: pointer;
+  width: 24px;
+  height: 24px;
+}
--- a/browser/themes/shared/devtools/webconsole.inc.css
+++ b/browser/themes/shared/devtools/webconsole.inc.css
@@ -112,17 +112,17 @@ a {
 .message-location > .line-number {
   flex: none;
 }
 
 .message-flex-body {
   display: flex;
 }
 
-.message-body {
+.message-body > * {
   white-space: pre-wrap;
   word-wrap: break-word;
 }
 
 .message-flex-body > .message-body {
   display: block;
   flex: 1 1 auto;
   vertical-align: middle;
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/heartbeat-icon.svg
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="288px" height="248px" viewBox="0 0 288 248" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+    <!-- Generator: Sketch 3.2.2 (9983) - http://www.bohemiancoding.com/sketch -->
+    <title>  + Line 14</title>
+    <desc>Created with Sketch.</desc>
+    <defs>
+        <path id="path-1" d="M144,246.857143 C141.214272,246.857143 138.857152,245.892867 136.928571,243.964286 L36.6428571,147.214286 C35.5714232,146.357139 34.0982237,144.964295 32.2232143,143.035714 C30.3482049,141.107133 27.3750204,137.59824 23.3035714,132.508929 C19.2321225,127.419617 15.5893018,122.196455 12.375,116.839286 C9.16069821,111.482116 6.29465545,105.000038 3.77678571,97.3928571 C1.25891598,89.7856763 0,82.392893 0,75.2142857 C0,51.6427393 6.80350339,33.2143521 20.4107143,19.9285714 C34.0179252,6.64279071 52.8213086,0 76.8214286,0 C83.4643189,0 90.2410369,1.1517742 97.1517857,3.45535714 C104.062535,5.75894009 110.491042,8.86605187 116.4375,12.7767857 C122.383958,16.6875196 127.499979,20.3571257 131.785714,23.7857143 C136.07145,27.2143029 140.142838,30.8571236 144,34.7142857 C147.857162,30.8571236 151.92855,27.2143029 156.214286,23.7857143 C160.500021,20.3571257 165.616042,16.6875196 171.5625,12.7767857 C177.508958,8.86605187 183.937465,5.75894009 190.848214,3.45535714 C197.758963,1.1517742 204.535681,0 211.178571,0 C235.178691,0 253.982075,6.64279071 267.589286,19.9285714 C281.196497,33.2143521 288,51.6427393 288,75.2142857 C288,98.8929755 275.732266,122.999877 251.196429,147.535714 L151.071429,243.964286 C149.142847,245.892867 146.785728,246.857143 144,246.857143 L144,246.857143 Z"/>
+    </defs>
+    <g id="Prompt---Spec" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+        <g id="--+-Line-14" sketch:type="MSLayerGroup" transform="translate(0.000000, -1.000000)">
+            <path d="M144,248.571429 C141.214272,248.571429 138.857152,247.607152 136.928571,245.678571 L36.6428571,148.928571 C35.5714232,148.071424 34.0982237,146.678581 32.2232143,144.75 C30.3482049,142.821419 27.3750204,139.312525 23.3035714,134.223214 C19.2321225,129.133903 15.5893018,123.910741 12.375,118.553571 C9.16069821,113.196402 6.29465545,106.714324 3.77678571,99.1071429 C1.25891598,91.499962 0,84.1071788 0,76.9285714 C0,53.357025 6.80350339,34.9286379 20.4107143,21.6428571 C34.0179252,8.35707643 52.8213086,1.71428571 76.8214286,1.71428571 C83.4643189,1.71428571 90.2410369,2.86605991 97.1517857,5.16964286 C104.062535,7.4732258 110.491042,10.5803376 116.4375,14.4910714 C122.383958,18.4018053 127.499979,22.0714114 131.785714,25.5 C136.07145,28.9285886 140.142838,32.5714093 144,36.4285714 C147.857162,32.5714093 151.92855,28.9285886 156.214286,25.5 C160.500021,22.0714114 165.616042,18.4018053 171.5625,14.4910714 C177.508958,10.5803376 183.937465,7.4732258 190.848214,5.16964286 C197.758963,2.86605991 204.535681,1.71428571 211.178571,1.71428571 C235.178691,1.71428571 253.982075,8.35707643 267.589286,21.6428571 C281.196497,34.9286379 288,53.357025 288,76.9285714 C288,100.607261 275.732266,124.714163 251.196429,149.25 L151.071429,245.678571 C149.142847,247.607152 146.785728,248.571429 144,248.571429 L144,248.571429 Z" id="-" fill="#D74345" sketch:type="MSShapeGroup"/>
+            <g id="Line-14" transform="translate(0.000000, 0.714286)">
+                <mask id="mask-2" sketch:name="Mask" fill="white">
+                    <use xlink:href="#path-1"/>
+                </mask>
+                <use id="Mask" sketch:type="MSShapeGroup" xlink:href="#path-1"/>
+                <path d="M-166,115.135254 C-166,115.135254 0.595052083,115.135254 2.9765625,115.135254 L91.9101562,115.135254 L97.9638977,100.101562 L105.430695,115.135254 L114.893585,115.135254 L131.129913,189.53125 L148.161163,57 L165.348663,131.027344 L172.272491,115.135254 L250.84967,115.135254 L428.259813,115.135254" stroke="#FFFFFF" stroke-width="6" stroke-linecap="round" stroke-linejoin="round" sketch:type="MSShapeGroup" mask="url(#mask-2)">
+                    <g transform="translate(131.129906, 123.265625) scale(1, -1) translate(-131.129906, -123.265625) "/>
+                </path>
+            </g>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/heartbeat-star-lit.svg
@@ -0,0 +1,428 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.1"
+   id="Toolbar"
+   x="0px"
+   y="0px"
+   viewBox="0 0 16 16"
+   enable-background="new 0 0 16 16"
+   xml:space="preserve"
+   inkscape:version="0.48.5 r10040"
+   width="100%"
+   height="100%"
+   sodipodi:docname="star-lit.svg"><metadata
+   id="metadata4111"><rdf:RDF><cc:Work
+       rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+         rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+   id="defs4109" /><sodipodi:namedview
+   pagecolor="#ffffff"
+   bordercolor="#666666"
+   borderopacity="1"
+   objecttolerance="10"
+   gridtolerance="10"
+   guidetolerance="10"
+   inkscape:pageopacity="0"
+   inkscape:pageshadow="2"
+   inkscape:window-width="1440"
+   inkscape:window-height="838"
+   id="namedview4107"
+   showgrid="false"
+   inkscape:zoom="41.7193"
+   inkscape:cx="6.7712219"
+   inkscape:cy="7.3971752"
+   inkscape:window-x="-8"
+   inkscape:window-y="-8"
+   inkscape:window-maximized="1"
+   inkscape:current-layer="Toolbar" />
+<g
+   id="g3926">
+	<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   d="M-238.2,6h-6.1l2.7-2.7c0.4-0.4,0.5-1.1,0.2-1.4l-1.2-1.2   c-0.3-0.3-1-0.3-1.4,0.2l-6.4,6.4c-0.1,0.1-0.1,0.2-0.2,0.2l0,0c0,0,0,0,0,0c0,0.1-0.1,0.1-0.1,0.1c0,0.1-0.1,0.2-0.1,0.3   c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1c0,0.1,0,0.2,0.1,0.3c0,0.1,0.1,0.1,0.1,0.1c0,0,0,0,0,0l0,0c0.1,0.1,0.1,0.2,0.2,0.2l6.4,6.4   c0.4,0.4,1.1,0.5,1.4,0.2l1.2-1.2c0.3-0.3,0.3-1-0.2-1.4l-2.8-2.8h6.2c0.6,0,1-0.4,1-1V7C-237.2,6.4-237.6,6-238.2,6z"
+   id="path3928" />
+</g>
+<g
+   id="g3930">
+	<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   d="M-212.7,6.3l-1,1c-0.2,0.2-0.2,0.4-0.2,0.7c0,0.3,0,0.5,0.2,0.7   l0.8,0.9l4.5,4.5c0.5,0.5,1.2,0.6,1.6,0.2l0.8-0.9c0.4-0.4,0.3-1.1-0.2-1.6l-1.8-1.8h4.7c0.6,0,1-0.5,1-1V7c0-0.6-0.5-1-1-1h-4.8   l1.9-1.9c0.5-0.5,0.6-1.3,0.2-1.6l-0.8-0.9c-0.4-0.4-1.1-0.3-1.6,0.2L-212.7,6.3z"
+   id="path3932" />
+</g>
+<g
+   id="g3934">
+	<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   d="M-167.3,9.7l1-1c0.2-0.2,0.2-0.4,0.2-0.7c0-0.3,0-0.5-0.2-0.7   l-0.8-0.9l-4.5-4.5c-0.5-0.5-1.2-0.6-1.6-0.2l-0.8,0.9c-0.4,0.4-0.3,1.1,0.2,1.6l1.8,1.8h-4.7c-0.6,0-1,0.5-1,1v2c0,0.6,0.5,1,1,1   h4.8l-1.9,1.9c-0.5,0.5-0.6,1.3-0.2,1.6l0.8,0.9c0.4,0.4,1.1,0.3,1.6-0.2L-167.3,9.7z"
+   id="path3936" />
+</g>
+<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   d="M-129.5,8h-7l2.8-2.8c-0.7-0.5-1.4-0.7-2.3-0.7c-2.2,0-4,1.8-4,4  c0,2.2,1.8,4,4,4c1.4,0,2.7-0.7,3.4-1.9l2.3,1c-1.1,2-3.2,3.4-5.7,3.4c-3.6,0-6.5-2.9-6.5-6.5c0-3.6,2.9-6.5,6.5-6.5  c1.5,0,2.9,0.5,4.1,1.4l2.4-2.4V8z"
+   id="path3938" />
+<g
+   id="g3940">
+	<g
+   id="g3942">
+		<polygon
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   points="-93.6,3.8 -95.8,1.7 -100,5.9 -104.3,1.6 -106.4,3.8    -102.1,8 -106.3,12.2 -104.1,14.3 -99.9,10.2 -95.7,14.4 -93.6,12.2 -97.8,8.1   "
+   id="polygon3944" />
+	</g>
+</g>
+<g
+   id="g3946">
+	<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   d="M-64,1l-8,7h2l6-5l6,5h2L-64,1z M-69,8v7h4v-5h2v5h4V8l-5-4L-69,8   z"
+   id="path3948" />
+</g>
+<path
+   fill="#00A3F2"
+   d="M-28,3.6l1,2l0.5,0.9l1,0.2l2.3,0.4l-1.7,1.8l-0.7,0.7l0.1,1l0.4,2.4l-2-1l-0.9-0.5l-0.9,0.5l-2,1l0.4-2.4  l0.1-1l-0.7-0.7l-1.7-1.8l2.4-0.4l1-0.2l0.5-0.9L-28,3.6 M-28,0c-0.3,0-0.6,0.2-0.8,0.7l-2,4.1l-4.3,0.7c-1,0.2-1.2,0.9-0.5,1.6  l3.1,3.3l-0.7,4.6c-0.1,0.7,0.2,1.1,0.7,1.1c0.2,0,0.4-0.1,0.6-0.2l3.9-2.1l3.9,2.1c0.2,0.1,0.5,0.2,0.6,0.2c0.5,0,0.8-0.4,0.7-1.1  l-0.7-4.6l3.1-3.3c0.7-0.7,0.4-1.4-0.5-1.6l-4.3-0.7l-2-4.1C-27.4,0.2-27.7,0-28,0L-28,0z"
+   id="path3950" />
+<path
+   fill="#00A3F2"
+   d="M8,0C7.7,0,7.4,0.2,7.2,0.7l-2,4.1L0.9,5.5c-1,0.2-1.2,0.9-0.5,1.6l3.1,3.3l-0.7,4.6C2.7,15.6,3,16,3.4,16  c0.2,0,0.4-0.1,0.6-0.2L8,13.7l3.9,2.1c0.2,0.1,0.5,0.2,0.6,0.2c0.5,0,0.8-0.4,0.7-1.1l-0.7-4.6l3.1-3.3c0.7-0.7,0.4-1.4-0.5-1.6  l-4.3-0.7l-2-4.1C8.6,0.2,8.3,0,8,0L8,0z"
+   id="path3952"
+   style="fill:#0095dd;fill-opacity:1" />
+<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   d="M44,15.2c-4,0-7.2-3.2-7.2-7.2c0-4,3.2-7.2,7.2-7.2  c4,0,7.2,3.2,7.2,7.2C51.2,12,48,15.2,44,15.2z M44,3c-2.8,0-5,2.2-5,5c0,2.7,2.2,5,5,5c2.8,0,5-2.2,5-5C49,5.3,46.8,3,44,3z   M43.7,8.9C43.3,8.8,43,8.4,43,8V5c0-0.6,0.4-1,1-1c0.6,0,1,0.4,1,1v2.8c1.1,1.1,2,3.2,2,3.2S44.8,10,43.7,8.9z"
+   id="path3954" />
+<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   d="M86.7,9.1l-5.6,5.5C80.8,14.9,80.4,15,80,15  c-0.4,0-0.8-0.1-1.1-0.4l-5.6-5.5C72.7,8.5,72.9,8,73.8,8H77l0-6c0-0.6,0.4-1,1-1h4c0.6,0,1,0.4,1,1v6h3.2C87.1,8,87.3,8.5,86.7,9.1  z"
+   id="path3956" />
+<path
+   fill="#00A3F2"
+   d="M-241,52c0.5,0,1-0.4,1-1v-4c0,0,0.1-0.8,0.8-0.8c0.6,0,0.6,0.8,1.8,0.8c0.6,0,1.5-0.2,1.5-2  c0-1.8-0.9-2-1.5-2c-1.1,0-1.1,0.8-1.8,0.8c-0.6,0-0.8-0.8-0.8-0.8v-2c0-0.6-0.4-1-1-1h-3c0,0-0.8-0.1-0.8-0.8  c0-0.6,0.8-0.6,0.8-1.8c0-0.6-0.2-1.5-2-1.5c-1.8,0-2,0.9-2,1.5c0,1.1,0.8,1.1,0.8,1.8c0,0.6-0.8,0.8-0.8,0.8h-3c-0.5,0-1,0.4-1,1  l0,2.5c0,0-0.1,1.5,1.1,1.5c0.8,0,0.9-1,1.9-1c0.5,0,1,0.5,1,1.6c0,1-0.5,1.6-1,1.6c-1,0-1.1-1-1.9-1c-1.2,0-1.1,1.5-1.1,1.5l0,3.5  c0,0.6,0.4,1,1,1h3.8c0,0,1.5,0.1,1.5-1.1c0-0.8-1-0.9-1-1.9c0-0.5,0.7-1.2,1.8-1.2c1,0,1.8,0.7,1.8,1.2c0,1-1,1.1-1,1.9  c0,1.2,1.5,1.1,1.5,1.1H-241z"
+   id="path3958" />
+<g
+   id="g3960">
+	<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   d="M-200.4,41.9h-0.3v0h-14.4v0h-0.3c-0.2,0-0.4,0.2-0.4,0.4c0,0,0.3,2.2,0.5,4.4   c0.2,2.5,0.2,4.2,0.2,4.2c0,0.2,0.2,0.4,0.4,0.4h13.7c0.2,0,0.4-0.2,0.4-0.4c0,0,0.1-2.1,0.2-4.2c0.1-2.2,0.5-4.4,0.5-4.4   C-200.1,42.1-200.2,41.9-200.4,41.9z"
+   id="path3962" />
+	<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   d="M-200.8,41.3v-2c0-0.3-0.2-0.5-0.5-0.5l-6.7,0l-0.8-1.1c0,0-0.6-0.9-1.2-0.9h-4.2   c-0.6,0-1,0.5-1,1v1l0,2.5H-200.8z"
+   id="path3964" />
+</g>
+<g
+   id="g3966">
+	<g
+   id="g3968">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   d="M-169,37h-7.5c-0.8,0-1.5,0.7-1.5,1.5v11c0,0.8,0.7,1.5,1.5,1.5    h9c0.8,0,1.5-0.7,1.5-1.5V40L-169,37z M-170,41v-3.3l3.3,3.3H-170z"
+   id="path3970" />
+	</g>
+</g>
+<g
+   id="g3972">
+	<g
+   id="g3974">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   d="M-132.5,38c-0.4,0.2-0.9,0.6-1.3,1.2c-0.3,0.4-0.5,0.9-0.8,1.3    c0.7,0.3,1.3,0.8,1.7,1.4c0.4,0.7,0.7,1.5,0.6,2.3c-0.1,1.3-1,2.4-2.1,3c-0.5-0.5-1.4-2.1-1.4-2.1c0,0,0,1.8-0.6,3    c-0.4,0.8-0.9,1.5-1.6,2.1c1,0.4,2,0.6,3,0.6l0-0.1c0,0,0.1,0.1,0.1,0.1c2.1,0,4.1-0.3,4.1-0.3s-0.9-0.6-1.3-0.9    c1.3-0.7,2.2-2,2.5-3.3c0.2-0.5,0.3-1.1,0.4-1.7C-128.7,41.9-130.2,39.3-132.5,38z M-139.8,44.2c0-1.4,0.8-2.8,2.2-3.5    c0.5,0.5,1.6,2,1.6,2s0-3.6,1.2-5.6c-4.3-0.5-6.8,0.3-6.8,0.3s1.2,0.4,1.6,0.8c-0.1,0.1-0.2,0.1-0.3,0.2c-1,0.7-1.7,1.6-2.1,2.7    c-0.3,0.6-0.5,1.3-0.5,2c-0.3,2.8,1.1,5.4,3.4,6.7c0.4-0.3,0.8-0.6,1.2-1.1c0.4-0.4,0.7-0.9,1-1.4    C-138.7,46.9-139.7,45.7-139.8,44.2z"
+   id="path3976" />
+	</g>
+</g>
+<g
+   id="g3978">
+	<g
+   id="g3980">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   d="M-96.6,41.5c-3.3-4-9.6-4.5-9.6-4.5c-0.4,0-0.8,0.3-0.8,0.7v1.4    c0,0.4,0.3,0.7,0.8,0.7c0,0,4.5,0.1,7.1,3.2c3.5,3.6,3.2,7.2,3.2,7.2c0,0.4,0.3,0.8,0.8,0.8h1.5c0.4,0,0.7-0.3,0.7-0.8    C-93,50.2-93.3,44.5-96.6,41.5z M-106.2,42c-0.4,0-0.8,0.3-0.8,0.7V44c0,0.4,0.3,0.7,0.8,0.7c0,0,2.4,0.1,3.8,1.4    c1.9,1.7,1.8,4.2,1.8,4.2c0,0.4,0.2,0.8,0.6,0.8h1.5c0.4,0,0.5-0.3,0.5-0.8c0,0-0.4-3.9-2.5-5.9C-102.5,42.4-106.2,42-106.2,42z     M-105,47c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2c1.1,0,2-0.9,2-2C-103,47.9-103.9,47-105,47z"
+   id="path3982" />
+	</g>
+</g>
+<path
+   fill="none"
+   d="M-102.6,34.4c0.5,0,1-0.4,1-1v-4c0,0,0.1-0.8,0.8-0.8c0.6,0,0.6,0.8,1.8,0.8c0.6,0,1.5-0.2,1.5-2  c0-1.8-0.9-2-1.5-2c-1.1,0-1.1,0.8-1.8,0.8c-0.6,0-0.8-0.8-0.8-0.8v-2c0-0.6-0.4-1-1-1h-3c0,0-0.8-0.1-0.8-0.8  c0-0.6,0.8-0.6,0.8-1.8c0-0.6-0.2-1.5-2-1.5c-1.8,0-2,0.9-2,1.5c0,1.1,0.8,1.1,0.8,1.8c0,0.6-0.8,0.8-0.8,0.8h-3c-0.5,0-1,0.4-1,1  l0,2.5c0,0-0.1,1.5,1.1,1.5c0.8,0,0.9-1,1.9-1c0.5,0,1,0.5,1,1.6c0,1-0.5,1.6-1,1.6c-1,0-1.1-1-1.9-1c-1.2,0-1.1,1.5-1.1,1.5l0,3.5  c0,0.6,0.4,1,1,1h3.8c0,0,1.5,0.1,1.5-1.1c0-0.8-1-0.9-1-1.9c0-0.5,0.7-1.2,1.8-1.2c1,0,1.8,0.7,1.8,1.2c0,1-1,1.1-1,1.9  c0,1.2,1.5,1.1,1.5,1.1H-102.6z"
+   id="path3984" />
+<g
+   id="g3986">
+	<g
+   id="g3988">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   d="M-63,46.7l-0.3-0.1L-63,52l2.6-3.8L-63,46.7z M-63.3,46l0.3,0    l6,2.9V36l-14,11l4.6-0.6L-63,52l-1.9-5.7l-0.3-0.1l0.3,0L-65,46l7-8L-63.3,46L-63.3,46z"
+   id="path3990" />
+	</g>
+</g>
+<g
+   id="g3992">
+	<g
+   id="g3994">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   d="M-23,37h-10c-1.1,0-2,0.9-2,2v10c0,1.1,0.9,2,2,2h10    c1.1,0,2-0.9,2-2V39C-21,37.9-21.9,37-23,37z M-23,46c0,1.7-1.3,2-3,2h-4c-1.7,0-3-1.3-3-3v-4c0-1.7,1.3-3,3-3h4c1.7,0,3,1.3,3,3    V46z"
+   id="path3996" />
+	</g>
+</g>
+<path
+   fill="#00A3F2"
+   d="M-23.7,42.6c0-0.8-0.2-1.5-0.6-2c-0.4-0.5-1-0.7-1.7-0.7c-0.4,0-0.8,0.1-1.1,0.3s-0.6,0.4-0.8,0.8  c-0.2-0.4-0.4-0.6-0.7-0.8c-0.3-0.2-0.7-0.2-1.2-0.2c-0.4,0-0.7,0-1.1,0.1s-0.7,0.2-1,0.4l0.3,0.7c0.6-0.3,1.2-0.5,1.7-0.5  c0.4,0,0.8,0.1,1,0.3c0.2,0.2,0.3,0.6,0.3,1.1v0.4l-1,0c-0.9,0-1.6,0.2-2.1,0.5c-0.5,0.3-0.7,0.8-0.7,1.4c0,0.6,0.2,1,0.5,1.3  c0.3,0.3,0.8,0.5,1.4,0.5c0.5,0,0.9-0.1,1.3-0.3c0.4-0.2,0.7-0.5,1-1c0.5,0.9,1.2,1.3,2.3,1.3c0.4,0,0.7,0,1-0.1  c0.3-0.1,0.6-0.2,0.9-0.3v-0.8c-0.3,0.1-0.6,0.3-0.9,0.3c-0.3,0.1-0.6,0.1-0.9,0.1c-1.2,0-1.8-0.7-1.8-2.2h3.9V42.6z M-28.5,43.6  c0,0.6-0.2,1-0.5,1.3c-0.3,0.3-0.7,0.5-1.3,0.5c-0.3,0-0.6-0.1-0.8-0.2c-0.2-0.2-0.3-0.4-0.3-0.8c0-0.4,0.2-0.7,0.5-0.9  c0.3-0.2,0.8-0.3,1.5-0.3l0.9,0V43.6z M-27.5,42.4c0-0.6,0.2-1,0.4-1.3c0.3-0.3,0.6-0.5,1.1-0.5c0.4,0,0.8,0.2,1,0.5  c0.2,0.3,0.4,0.8,0.4,1.3H-27.5z"
+   id="path3998" />
+<g
+   id="g4000">
+	<g
+   id="g4002">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   d="M14.5,37h-13C0.7,37,0,37.7,0,38.5v11C0,50.3,0.7,51,1.5,51h13    c0.8,0,1.5-0.7,1.5-1.5v-11C16,37.7,15.3,37,14.5,37z M6.5,38C6.8,38,7,38.2,7,38.5C7,38.8,6.8,39,6.5,39C6.2,39,6,38.8,6,38.5    C6,38.2,6.2,38,6.5,38z M4.4,38c0.3,0,0.5,0.2,0.5,0.5c0,0.3-0.2,0.5-0.5,0.5c-0.3,0-0.5-0.2-0.5-0.5C3.9,38.2,4.2,38,4.4,38z     M2.5,38C2.8,38,3,38.2,3,38.5C3,38.8,2.8,39,2.5,39C2.2,39,2,38.8,2,38.5C2,38.2,2.2,38,2.5,38z M14,48c0,0.6-0.4,1-1,1H3    c-0.6,0-1-0.4-1-1v-6c0-0.6,0.4-1,1-1h10c0.6,0,1,0.4,1,1V48z"
+   id="path4004" />
+	</g>
+</g>
+<path
+   fill="#00A3F2"
+   d="M51.2,45.9L51.2,45.9c-0.4-0.1-0.7-0.3-1-0.6c-0.1-0.1-0.1-0.1-0.2-0.2c-0.1-0.1-0.1-0.1-0.2-0.2  c0-0.1-0.1-0.1-0.1-0.2c0-0.1-0.1-0.1-0.1-0.2c-0.3-0.6-0.4-1.5-0.5-2.4c-0.3-2.2-0.1-3.8-3-3.8h-4.4c-2.9,0-2.6,1.6-2.9,3.8  c-0.1,0.9-0.3,1.8-0.6,2.4c0,0,0,0,0,0c-0.1,0.1-0.1,0.2-0.2,0.3c0,0,0,0-0.1,0.1c-0.1,0.2-0.2,0.3-0.4,0.4  c-0.3,0.2-0.5,0.4-0.9,0.5l0,0C36.4,46,36,46.3,36,46.8v1.9c0,0.5,0.4,0.9,0.9,0.9h14.1c0.5,0,0.9-0.4,0.9-0.9v-1.9  C52,46.3,51.6,46,51.2,45.9z M46.4,43.8c0,0.1-0.1,0.2-0.2,0.2h-1.6v1.6c0,0.1-0.1,0.2-0.2,0.2h-0.5c-0.1,0-0.2-0.1-0.2-0.2V44h-1.6  c-0.1,0-0.2-0.1-0.2-0.2v-0.5c0-0.1,0.1-0.2,0.2-0.2h1.6v-1.6c0-0.1,0.1-0.2,0.2-0.2h0.5c0.1,0,0.2,0.1,0.2,0.2v1.6h1.6  c0.1,0,0.2,0.1,0.2,0.2V43.8z"
+   id="path4006" />
+<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   d="M87.7,42c-0.1,1.1,0.2,2.5-1.3,4.4c-1.6,2.1-3.1,2.1-3.4,2.1  c-1.8-0.1-2-1.5-3-1.5c-0.9,0-1.6,1.4-3,1.5c-0.3,0-1.9,0-3.4-2c-1.5-1.9-1.2-3.3-1.3-4.4S72,39.7,72,39.7s0.7,0.7,1.6,0.8  c0.9,0.1,1.1-0.3,3-0.9c2.1-0.6,3.4,1.9,3.4,1.9s1.4-2.4,3.4-1.9c1.9,0.6,2,0.9,2.9,0.9c0.9-0.1,1.7-0.8,1.7-0.8S87.8,40.9,87.7,42z   M76.9,42.5c-1.1-0.3-1.6,0.2-2.1,0.4C74.4,43,74,43.1,74,43.1s0.1,0.7,1.2,1.2c1.1,0.6,3.5,0.3,3.5,0.3S78.9,42.9,76.9,42.5z   M85.2,42.9c-0.5-0.2-1-0.6-2.1-0.4c-2,0.4-1.8,2.1-1.8,2.1s2.4,0.3,3.5-0.3c1.1-0.6,1.2-1.2,1.2-1.2S85.6,43,85.2,42.9z"
+   id="path4008" />
+<g
+   id="g4010">
+	<g
+   id="g4012">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   d="M-237.5,84.9l-3.3-3.3c0.5-0.9,0.9-1.9,0.9-2.9c0-3-2.4-5.5-5.5-5.5    c-3,0-5.5,2.4-5.5,5.5c0,3,2.4,5.5,5.5,5.5c1.1,0,2.1-0.3,3-0.9l3.3,3.3c0.4,0.4,1,0.4,1.4,0l0.2-0.2    C-237.1,85.9-237.1,85.3-237.5,84.9z M-245.4,82c-1.8,0-3.3-1.5-3.3-3.4c0-1.9,1.5-3.4,3.3-3.4c1.8,0,3.3,1.5,3.3,3.4    C-242,80.5-243.5,82-245.4,82z"
+   id="path4014" />
+	</g>
+</g>
+<g
+   id="g4016">
+	<g
+   id="g4018">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   d="M-214.5,86.3l0.2,0.2c0.4,0.4,1,0.4,1.4,0l3.3-3.3c0.9,0.6,1.9,0.9,3,0.9    c3,0,5.5-2.4,5.5-5.5c0-3-2.4-5.5-5.5-5.5c-3,0-5.5,2.4-5.5,5.5c0,1.1,0.3,2.1,0.9,2.9l-3.3,3.3    C-214.9,85.3-214.9,85.9-214.5,86.3z M-210,78.6c0-1.9,1.5-3.4,3.3-3.4c1.8,0,3.3,1.5,3.3,3.4c0,1.9-1.5,3.4-3.3,3.4    C-208.5,82-210,80.5-210,78.6z"
+   id="path4020" />
+	</g>
+</g>
+<g
+   id="g4022">
+	<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   d="M-165,77.5h-1v-2c0-0.6-0.4-1-1-1v-1c0-0.6-0.4-1-1-1h-8   c-0.6,0-1,0.4-1,1v1c-0.6,0-1,0.4-1,1v2h-1c-0.6,0-1,0.4-1,1v5c0,0.6,0.4,1,1,1h2v-1h0.5l-1.5,3h12l-1.5-3h0.5v1h2c0.6,0,1-0.4,1-1   v-5C-164,77.9-164.4,77.5-165,77.5z M-176.5,80.5h-1c-0.3,0-0.5-0.2-0.5-0.5c0-0.3,0.2-0.5,0.5-0.5h1c0.3,0,0.5,0.2,0.5,0.5   C-176,80.2-176.2,80.5-176.5,80.5z M-176,85.5l0.9-2h6.2l0.9,2H-176z M-168,77.5c0,0.6-0.4,1-1,1h-6c-0.6,0-1-0.4-1-1v-3   c0-0.6,0.4-1,1-1h6c0.6,0,1,0.4,1,1V77.5z"
+   id="path4024" />
+</g>
+<g
+   id="g4026">
+	<path
+   fill="#00A3F2"
+   d="M-135,73l1.9,1.9l-2.6,2.6c-0.5,0.5-0.5,1.4,0.1,2c0.6,0.6,1.5,0.6,2,0.1l2.6-2.6l1.9,1.9v-6H-135z    M-138.5,80.4l-2.6,2.6L-143,81v6h6l-1.9-1.9l2.6-2.6c0.5-0.5,0.5-1.4-0.1-2C-137.1,79.9-138,79.9-138.5,80.4z"
+   id="path4028" />
+</g>
+<g
+   id="g4030">
+	<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   d="M-93.5,73.5c-0.6-0.6-1.5-0.6-2-0.1l-2.6,2.6L-100,74v6h6   l-1.9-1.9l2.6-2.6C-92.9,75-92.9,74.1-93.5,73.5z M-104.1,81.9l-2.6,2.6c-0.5,0.5-0.5,1.4,0.1,2c0.6,0.6,1.5,0.6,2,0.1l2.6-2.6   l1.9,1.9v-6h-6L-104.1,81.9z"
+   id="path4032" />
+</g>
+<g
+   id="g4034">
+	<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   d="M14.6,75.1l-2.3,2.3c-0.5,0.5-1.3,0.5-1.7,0   c-0.5-0.5-0.5-1.2,0-1.7l2.4-2.3c-0.6-0.3-1.2-0.5-1.9-0.5C8.8,72.9,7,74.7,7,77c0,0.5,0.1,1.1,0.3,1.5l-5.9,5.8   c-0.6,0.6-0.6,1.7,0,2.3c0.6,0.6,1.7,0.6,2.3,0l6-5.9c0.4,0.2,0.9,0.3,1.4,0.3c2.2,0,4.1-1.8,4.1-4.1   C15.1,76.3,14.9,75.7,14.6,75.1z M2.5,86.4c-0.5,0-0.9-0.4-0.9-0.9c0-0.5,0.4-0.9,0.9-0.9c0.5,0,0.9,0.4,0.9,0.9   C3.4,86,3,86.4,2.5,86.4z"
+   id="path4036" />
+</g>
+<path
+   fill="#00A3F2"
+   d="M-22.7,83.9l-1.1-1.1c0.2-0.3,0.3-0.6,0.5-0.9c0.1-0.3,0.2-0.6,0.3-0.9l1.6,0c0.3,0,0.5-0.2,0.5-0.5l0-1  c0-0.3-0.2-0.5-0.5-0.5l-1.6,0c-0.1-0.6-0.4-1.2-0.7-1.8l1.1-1.1c0.2-0.2,0.2-0.5,0-0.7l-0.7-0.7c-0.2-0.2-0.5-0.2-0.7,0l-1.1,1.1  c-0.3-0.2-0.6-0.3-0.9-0.5c-0.3-0.1-0.6-0.2-0.9-0.3l0-1.6c0-0.3-0.2-0.5-0.5-0.5l-1,0c-0.3,0-0.5,0.2-0.5,0.5l0,1.6  c-0.6,0.1-1.2,0.4-1.8,0.7l-1.1-1.1c-0.2-0.2-0.5-0.2-0.7,0l-0.7,0.7c-0.2,0.2-0.2,0.5,0,0.7l1.1,1.1c-0.2,0.3-0.3,0.6-0.5,0.9  c-0.1,0.3-0.2,0.6-0.3,0.9l-1.6,0c-0.3,0-0.5,0.2-0.5,0.5l0,1c0,0.3,0.2,0.5,0.5,0.5l1.6,0c0.1,0.6,0.4,1.2,0.7,1.8l-1.1,1.1  c-0.2,0.2-0.2,0.5,0,0.7l0.7,0.7c0.2,0.2,0.5,0.2,0.7,0l1.1-1.1c0.3,0.2,0.6,0.3,0.9,0.5c0.3,0.1,0.6,0.2,0.9,0.3l0,1.6  c0,0.3,0.2,0.5,0.5,0.5l1,0c0.3,0,0.5-0.2,0.5-0.5l0-1.6c0.6-0.1,1.2-0.4,1.8-0.7l1.1,1.1c0.2,0.2,0.5,0.2,0.7,0l0.7-0.7  C-22.5,84.4-22.5,84.1-22.7,83.9z M-28.8,82.1c-1.2-0.5-1.7-1.8-1.3-3c0.5-1.2,1.8-1.7,3-1.3c1.2,0.5,1.7,1.8,1.3,3  C-26.4,82-27.7,82.6-28.8,82.1z"
+   id="path4038" />
+<g
+   id="g4040">
+	<g
+   id="g4042">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   d="M-62,77h-4c-0.6,0-1,0.4-1,1v4c0,0.6,0.4,1,1,1h4    c0.6,0,1-0.4,1-1v-4C-61,77.4-61.4,77-62,77z M-62,81c0,0.6-0.4,1-1,1h-2c-0.6,0-1-0.4-1-1v-1c0-0.6,0.4-1,1-1h2c0.6,0,1,0.4,1,1    V81z M-64,87l3-3h-6L-64,87z M-67,76h6l-3-3L-67,76z M-60,76.9v6l3-3L-60,76.9z M-68,82.9v-6l-3,3L-68,82.9z"
+   id="path4044" />
+	</g>
+</g>
+<g
+   id="g4046">
+	<g
+   id="g4048">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   d="M37.5,76.5h13c0.8,0,1.5-0.7,1.5-1.5c0-0.8-0.7-1.5-1.5-1.5h-13    c-0.8,0-1.5,0.7-1.5,1.5C36,75.8,36.7,76.5,37.5,76.5z M50.5,78.5h-13c-0.8,0-1.5,0.7-1.5,1.5c0,0.8,0.7,1.5,1.5,1.5h13    c0.8,0,1.5-0.7,1.5-1.5C52,79.2,51.3,78.5,50.5,78.5z M50.5,83.5h-13c-0.8,0-1.5,0.7-1.5,1.5c0,0.8,0.7,1.5,1.5,1.5h13    c0.8,0,1.5-0.7,1.5-1.5C52,84.2,51.3,83.5,50.5,83.5z"
+   id="path4050" />
+	</g>
+</g>
+<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   d="M85.6,87c-0.8,0-1.5-0.2-2.3-1.3c-0.8-1.1-1.7-2.5-1.7-2.5  s-0.7-0.9-1.1-1.6c-0.4-0.7-1.1-0.5-1.1-0.5s-2.8-4.6-3.3-5.4c-0.7-1,0.6-2.7,0.6-2.7l4.4,7c0,0,1.3,1.9,1.9,2.3  c0.5,0.4,1.4-0.4,2.8,0.9C87.7,85,87.1,87,85.6,87z M85.4,84.1c-0.9-1-1.7-0.9-1.9-0.6c-0.2,0.3,0,1.2,0.4,1.7  c0.4,0.5,0.8,0.7,1.4,0.7C85.9,86,86.4,85.2,85.4,84.1z M81.6,79.4l-1.2-1.8l2.9-4.6c0,0,1.2,1.7,0.6,2.7  C83.6,76.1,82.5,78,81.6,79.4z M77,82.3c0.3-0.3,1-1.1,1.4-1.7l0.8,1.2c-0.4,0.6-0.9,1.4-0.9,1.4s-0.9,1.4-1.7,2.5  c-0.8,1.1-1.5,1.3-2.3,1.3c-1.4,0-2.1-2-0.1-3.8C75.6,82,76.5,82.7,77,82.3z M74.6,84.1c-0.9,1-0.4,1.8,0.2,1.8c0.6,0,1-0.2,1.4-0.7  c0.4-0.5,0.6-1.5,0.4-1.7C76.3,83.2,75.5,83.1,74.6,84.1z"
+   id="path4052" />
+<g
+   id="g4054">
+	<g
+   id="g4056">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   d="M-243,111c0-0.1-2-2-2-2c-1.7,0-5,0-5,0c-0.6,0-1,0.4-1,1v8    c0,0.6,0.4,1,1,1h4v-6.1c0-0.3,0.2-0.5,0.5-0.5h2.5V111z M-246,112v-2l2,2H-246z M-239,113c-1.7,0-5,0-5,0c-0.6,0-1,0.4-1,1v8    c0,0.6,0.4,1,1,1h6c0.6,0,1-0.4,1-1c0,0,0-4.8,0-7C-237,115-239,113-239,113z M-240,116v-2l2,2H-240z"
+   id="path4058" />
+	</g>
+</g>
+<g
+   id="g4060">
+	<g
+   id="g4062">
+		<path
+   fill="#00A3F2"
+   d="M-204.5,111h-1.3l0,0c0,0-0.2-2-2.2-2c-2,0-2.2,2-2.2,2l0,0h-1.3c-0.8,0-1.5,0.7-1.5,1.5v9    c0,0.8,0.7,1.5,1.5,1.5h7c0.8,0,1.5-0.7,1.5-1.5v-9C-203,111.7-203.7,111-204.5,111z M-210.7,112.1l0.8-0.4l0.4-0.2l0-0.4    c0-0.2,0.2-1.3,1.5-1.3c1.2,0,1.4,1.1,1.5,1.3l0,0.4l0.4,0.2l0.8,0.4l0.3,0.7h-6.1L-210.7,112.1z M-210.1,120.4l-2.8-4.9l3.3-1.9    h4.3l1.8,3.1L-210.1,120.4z"
+   id="path4064" />
+	</g>
+</g>
+<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   d="M-177,115h10v2h-10V115z"
+   id="path4066" />
+<g
+   id="g4068">
+	<g
+   id="g4070">
+		<polygon
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   points="-131,115 -135,115 -135,111 -137,111 -137,115 -141,115    -141,117 -137,117 -137,121 -135,121 -135,117 -131,117   "
+   id="polygon4072" />
+	</g>
+</g>
+<g
+   id="g4074">
+	<path
+   fill="#00A3F2"
+   d="M-94,111.7l-3,2.7v-2c0-0.8-0.6-1.4-1.3-1.4h-7.4c-0.7,0-1.3,0.6-1.3,1.4v7.2c0,0.8,0.6,1.4,1.3,1.4h7.4   c0.7,0,1.3-0.6,1.3-1.4v-2.1l3,2.7c0.3,0.3,0.6,0.4,1,0.3v-9.1C-93.3,111.4-93.7,111.5-94,111.7z"
+   id="path4076" />
+</g>
+<g
+   id="g4078">
+	<g
+   id="g4080">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   d="M-61.9,112h-3l3.8,4l-3.8,4h3.1l3.8-4L-61.9,112z M-66.9,112h-3    l3.8,4l-3.8,4h3.1l3.8-4L-66.9,112z"
+   id="path4082" />
+	</g>
+</g>
+<g
+   id="g4084">
+	<g
+   id="g4086">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   d="M-23,111.5h-2l-3-3l-3,3h-2c-1.1,0-2,0.9-2,2v8c0,1.1,0.9,2,2,2    h10c1.1,0,2-0.9,2-2v-8C-21,112.4-21.9,111.5-23,111.5z M-32,121.5c-0.6,0-1-0.4-1-1s0.4-1,1-1s1,0.4,1,1S-31.4,121.5-32,121.5z     M-32,118.5c-0.6,0-1-0.4-1-1s0.4-1,1-1s1,0.4,1,1S-31.4,118.5-32,118.5z M-32,115.5c-0.6,0-1-0.4-1-1s0.4-1,1-1s1,0.4,1,1    S-31.4,115.5-32,115.5z M-23,121.5h-7v-2h7V121.5z M-23,118.5h-7v-2h7V118.5z M-23,115.5h-7v-2h7V115.5z"
+   id="path4088" />
+	</g>
+</g>
+<g
+   id="g4090">
+	<g
+   id="g4092">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   d="M2,115h7c0.6,0,1-0.4,1-1v-4c0-0.6-0.4-1-1-1H2c-0.6,0-1,0.4-1,1    v4C1,114.6,1.4,115,2,115z M14,109h-2c-0.6,0-1,0.4-1,1v4c0,0.6,0.4,1,1,1h2c0.6,0,1-0.4,1-1v-4C15,109.4,14.6,109,14,109z     M14,116H8c-0.6,0-1,0.4-1,1v5c0,0.6,0.4,1,1,1h6c0.6,0,1-0.4,1-1v-5C15,116.4,14.6,116,14,116z M5,116H2c-0.6,0-1,0.4-1,1v5    c0,0.6,0.4,1,1,1h3c0.6,0,1-0.4,1-1v-5C6,116.4,5.6,116,5,116z"
+   id="path4094" />
+	</g>
+</g>
+<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   d="M45.8,116c0,0-0.6,0.8-1.8,0.8c-1.2,0-1.8-0.8-1.8-0.8l-6-5.1  c0.3-0.5,0.9-0.9,1.6-0.9h12.4c0.7,0,1.3,0.4,1.6,0.9L45.8,116z M42.2,117.7c0,0,0.6,0.8,1.8,0.8c1.2,0,1.8-0.8,1.8-0.8l6.2-5.4v8  c0,0.9-0.8,1.7-1.8,1.7H37.8c-1,0-1.8-0.8-1.8-1.7v-8L42.2,117.7z"
+   id="path4096" />
+<g
+   id="g4098">
+	<g
+   id="g4100">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   d="M-237.5,145h-13c-0.8,0-1.5,0.7-1.5,1.5v11    c0,0.8,0.7,1.5,1.5,1.5h13c0.8,0,1.5-0.7,1.5-1.5v-11C-236,145.7-236.7,145-237.5,145z M-245.5,146c0.3,0,0.5,0.2,0.5,0.5    c0,0.3-0.2,0.5-0.5,0.5c-0.3,0-0.5-0.2-0.5-0.5C-246,146.2-245.8,146-245.5,146z M-247.6,146c0.3,0,0.5,0.2,0.5,0.5    c0,0.3-0.2,0.5-0.5,0.5s-0.5-0.2-0.5-0.5C-248.1,146.2-247.8,146-247.6,146z M-249.5,146c0.3,0,0.5,0.2,0.5,0.5    c0,0.3-0.2,0.5-0.5,0.5s-0.5-0.2-0.5-0.5C-250,146.2-249.8,146-249.5,146z M-250,156v-6c0-0.6,0.4-1,1-1h7v8h-7    C-249.6,157-250,156.6-250,156z M-238,156c0,0.6-0.4,1-1,1h-1v-8h1c0.6,0,1,0.4,1,1V156z"
+   id="path4102" />
+	</g>
+</g>
+<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#00A3F2"
+   d="M-213.2,156c-1.3-1.2-2.2-2.8-2.2-4.6c0-3.6,3.3-6.5,7.4-6.5  c4.1,0,7.4,2.9,7.4,6.5c0,3.6-3.3,6.5-7.4,6.5c-0.8,0-1.6-0.1-2.4-0.3c-1.8,0.7-4.3,1.7-4.5,1.4C-213.9,157.9-213.5,156.8-213.2,156  z"
+   id="path4104" />
+<rect
+   id="_x3C_Slice_x3E_"
+   fill="none"
+   width="16"
+   height="16" />
+</svg>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/heartbeat-star-off.svg
@@ -0,0 +1,428 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.1"
+   id="Toolbar"
+   x="0px"
+   y="0px"
+   viewBox="0 0 16 16"
+   enable-background="new 0 0 16 16"
+   xml:space="preserve"
+   inkscape:version="0.48.5 r10040"
+   width="100%"
+   height="100%"
+   sodipodi:docname="star-off.svg"><metadata
+   id="metadata5255"><rdf:RDF><cc:Work
+       rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+         rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+   id="defs5253" /><sodipodi:namedview
+   pagecolor="#ffffff"
+   bordercolor="#666666"
+   borderopacity="1"
+   objecttolerance="10"
+   gridtolerance="10"
+   guidetolerance="10"
+   inkscape:pageopacity="0"
+   inkscape:pageshadow="2"
+   inkscape:window-width="710"
+   inkscape:window-height="480"
+   id="namedview5251"
+   showgrid="false"
+   inkscape:zoom="14.75"
+   inkscape:cx="7.6064963"
+   inkscape:cy="8"
+   inkscape:window-x="0"
+   inkscape:window-y="0"
+   inkscape:window-maximized="0"
+   inkscape:current-layer="Toolbar" />
+<g
+   id="g5070">
+	<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   d="M-238.2,6h-6.1l2.7-2.7c0.4-0.4,0.5-1.1,0.2-1.4l-1.2-1.2   c-0.3-0.3-1-0.3-1.4,0.2l-6.4,6.4c-0.1,0.1-0.1,0.2-0.2,0.2l0,0c0,0,0,0,0,0c0,0.1-0.1,0.1-0.1,0.1c0,0.1-0.1,0.2-0.1,0.3   c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1c0,0.1,0,0.2,0.1,0.3c0,0.1,0.1,0.1,0.1,0.1c0,0,0,0,0,0l0,0c0.1,0.1,0.1,0.2,0.2,0.2l6.4,6.4   c0.4,0.4,1.1,0.5,1.4,0.2l1.2-1.2c0.3-0.3,0.3-1-0.2-1.4l-2.8-2.8h6.2c0.6,0,1-0.4,1-1V7C-237.2,6.4-237.6,6-238.2,6z"
+   id="path5072" />
+</g>
+<g
+   id="g5074">
+	<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   d="M-212.7,6.3l-1,1c-0.2,0.2-0.2,0.4-0.2,0.7c0,0.3,0,0.5,0.2,0.7   l0.8,0.9l4.5,4.5c0.5,0.5,1.2,0.6,1.6,0.2l0.8-0.9c0.4-0.4,0.3-1.1-0.2-1.6l-1.8-1.8h4.7c0.6,0,1-0.5,1-1V7c0-0.6-0.5-1-1-1h-4.8   l1.9-1.9c0.5-0.5,0.6-1.3,0.2-1.6l-0.8-0.9c-0.4-0.4-1.1-0.3-1.6,0.2L-212.7,6.3z"
+   id="path5076" />
+</g>
+<g
+   id="g5078">
+	<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   d="M-167.3,9.7l1-1c0.2-0.2,0.2-0.4,0.2-0.7c0-0.3,0-0.5-0.2-0.7   l-0.8-0.9l-4.5-4.5c-0.5-0.5-1.2-0.6-1.6-0.2l-0.8,0.9c-0.4,0.4-0.3,1.1,0.2,1.6l1.8,1.8h-4.7c-0.6,0-1,0.5-1,1v2c0,0.6,0.5,1,1,1   h4.8l-1.9,1.9c-0.5,0.5-0.6,1.3-0.2,1.6l0.8,0.9c0.4,0.4,1.1,0.3,1.6-0.2L-167.3,9.7z"
+   id="path5080" />
+</g>
+<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   d="M-129.5,8h-7l2.8-2.8c-0.7-0.5-1.4-0.7-2.3-0.7c-2.2,0-4,1.8-4,4  c0,2.2,1.8,4,4,4c1.4,0,2.7-0.7,3.4-1.9l2.3,1c-1.1,2-3.2,3.4-5.7,3.4c-3.6,0-6.5-2.9-6.5-6.5c0-3.6,2.9-6.5,6.5-6.5  c1.5,0,2.9,0.5,4.1,1.4l2.4-2.4V8z"
+   id="path5082" />
+<g
+   id="g5084">
+	<g
+   id="g5086">
+		<polygon
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   points="-93.6,3.8 -95.8,1.7 -100,5.9 -104.3,1.6 -106.4,3.8     -102.1,8 -106.3,12.2 -104.1,14.3 -99.9,10.2 -95.7,14.4 -93.6,12.2 -97.8,8.1   "
+   id="polygon5088" />
+	</g>
+</g>
+<g
+   id="g5090">
+	<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   d="M-64,1l-8,7h2l6-5l6,5h2L-64,1z M-69,8v7h4v-5h2v5h4V8l-5-4L-69,8   z"
+   id="path5092" />
+</g>
+<path
+   fill="#231F20"
+   d="M-28,3.6l1,2l0.5,0.9l1,0.2l2.3,0.4l-1.7,1.8l-0.7,0.7l0.1,1l0.4,2.4l-2-1l-0.9-0.5l-0.9,0.5l-2,1l0.4-2.4  l0.1-1l-0.7-0.7l-1.7-1.8l2.4-0.4l1-0.2l0.5-0.9L-28,3.6 M-28,0c-0.3,0-0.6,0.2-0.8,0.7l-2,4.1l-4.3,0.7c-1,0.2-1.2,0.9-0.5,1.6  l3.1,3.3l-0.7,4.6c-0.1,0.7,0.2,1.1,0.7,1.1c0.2,0,0.4-0.1,0.6-0.2l3.9-2.1l3.9,2.1c0.2,0.1,0.5,0.2,0.6,0.2c0.5,0,0.8-0.4,0.7-1.1  l-0.7-4.6l3.1-3.3c0.7-0.7,0.4-1.4-0.5-1.6l-4.3-0.7l-2-4.1C-27.4,0.2-27.7,0-28,0L-28,0z"
+   id="path5094" />
+<path
+   fill="#231F20"
+   d="M8,0C7.7,0,7.4,0.2,7.2,0.7l-2,4.1L0.9,5.5c-1,0.2-1.2,0.9-0.5,1.6l3.1,3.3l-0.7,4.6C2.7,15.6,3,16,3.4,16  c0.2,0,0.4-0.1,0.6-0.2L8,13.7l3.9,2.1c0.2,0.1,0.5,0.2,0.6,0.2c0.5,0,0.8-0.4,0.7-1.1l-0.7-4.6l3.1-3.3c0.7-0.7,0.4-1.4-0.5-1.6  l-4.3-0.7l-2-4.1C8.6,0.2,8.3,0,8,0L8,0z"
+   id="path5096"
+   style="fill:#c0c0c0;fill-opacity:1" />
+<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   d="M44,15.2c-4,0-7.2-3.2-7.2-7.2c0-4,3.2-7.2,7.2-7.2  c4,0,7.2,3.2,7.2,7.2C51.2,12,48,15.2,44,15.2z M44,3c-2.8,0-5,2.2-5,5c0,2.7,2.2,5,5,5c2.8,0,5-2.2,5-5C49,5.3,46.8,3,44,3z   M43.7,8.9C43.3,8.8,43,8.4,43,8V5c0-0.6,0.4-1,1-1c0.6,0,1,0.4,1,1v2.8c1.1,1.1,2,3.2,2,3.2S44.8,10,43.7,8.9z"
+   id="path5098" />
+<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   d="M86.7,9.1l-5.6,5.5C80.8,14.9,80.4,15,80,15  c-0.4,0-0.8-0.1-1.1-0.4l-5.6-5.5C72.7,8.5,72.9,8,73.8,8H77l0-6c0-0.6,0.4-1,1-1h4c0.6,0,1,0.4,1,1v6h3.2C87.1,8,87.3,8.5,86.7,9.1  z"
+   id="path5100" />
+<path
+   fill="#231F20"
+   d="M-241,52c0.5,0,1-0.4,1-1v-4c0,0,0.1-0.8,0.8-0.8c0.6,0,0.6,0.8,1.8,0.8c0.6,0,1.5-0.2,1.5-2  c0-1.8-0.9-2-1.5-2c-1.1,0-1.1,0.8-1.8,0.8c-0.6,0-0.8-0.8-0.8-0.8v-2c0-0.6-0.4-1-1-1h-3c0,0-0.8-0.1-0.8-0.8  c0-0.6,0.8-0.6,0.8-1.8c0-0.6-0.2-1.5-2-1.5c-1.8,0-2,0.9-2,1.5c0,1.1,0.8,1.1,0.8,1.8c0,0.6-0.8,0.8-0.8,0.8h-3c-0.5,0-1,0.4-1,1  l0,2.5c0,0-0.1,1.5,1.1,1.5c0.8,0,0.9-1,1.9-1c0.5,0,1,0.5,1,1.6c0,1-0.5,1.6-1,1.6c-1,0-1.1-1-1.9-1c-1.2,0-1.1,1.5-1.1,1.5l0,3.5  c0,0.6,0.4,1,1,1h3.8c0,0,1.5,0.1,1.5-1.1c0-0.8-1-0.9-1-1.9c0-0.5,0.7-1.2,1.8-1.2c1,0,1.8,0.7,1.8,1.2c0,1-1,1.1-1,1.9  c0,1.2,1.5,1.1,1.5,1.1H-241z"
+   id="path5102" />
+<g
+   id="g5104">
+	<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   d="M-200.4,41.9h-0.3v0h-14.4v0h-0.3c-0.2,0-0.4,0.2-0.4,0.4c0,0,0.3,2.2,0.5,4.4   c0.2,2.5,0.2,4.2,0.2,4.2c0,0.2,0.2,0.4,0.4,0.4h13.7c0.2,0,0.4-0.2,0.4-0.4c0,0,0.1-2.1,0.2-4.2c0.1-2.2,0.5-4.4,0.5-4.4   C-200.1,42.1-200.2,41.9-200.4,41.9z"
+   id="path5106" />
+	<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   d="M-200.8,41.3v-2c0-0.3-0.2-0.5-0.5-0.5l-6.7,0l-0.8-1.1c0,0-0.6-0.9-1.2-0.9h-4.2   c-0.6,0-1,0.5-1,1v1l0,2.5H-200.8z"
+   id="path5108" />
+</g>
+<g
+   id="g5110">
+	<g
+   id="g5112">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   d="M-169,37h-7.5c-0.8,0-1.5,0.7-1.5,1.5v11c0,0.8,0.7,1.5,1.5,1.5    h9c0.8,0,1.5-0.7,1.5-1.5V40L-169,37z M-170,41v-3.3l3.3,3.3H-170z"
+   id="path5114" />
+	</g>
+</g>
+<g
+   id="g5116">
+	<g
+   id="g5118">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   d="M-132.5,38c-0.4,0.2-0.9,0.6-1.3,1.2c-0.3,0.4-0.5,0.9-0.8,1.3    c0.7,0.3,1.3,0.8,1.7,1.4c0.4,0.7,0.7,1.5,0.6,2.3c-0.1,1.3-1,2.4-2.1,3c-0.5-0.5-1.4-2.1-1.4-2.1c0,0,0,1.8-0.6,3    c-0.4,0.8-0.9,1.5-1.6,2.1c1,0.4,2,0.6,3,0.6l0-0.1c0,0,0.1,0.1,0.1,0.1c2.1,0,4.1-0.3,4.1-0.3s-0.9-0.6-1.3-0.9    c1.3-0.7,2.2-2,2.5-3.3c0.2-0.5,0.3-1.1,0.4-1.7C-128.7,41.9-130.2,39.3-132.5,38z M-139.8,44.2c0-1.4,0.8-2.8,2.2-3.5    c0.5,0.5,1.6,2,1.6,2s0-3.6,1.2-5.6c-4.3-0.5-6.8,0.3-6.8,0.3s1.2,0.4,1.6,0.8c-0.1,0.1-0.2,0.1-0.3,0.2c-1,0.7-1.7,1.6-2.1,2.7    c-0.3,0.6-0.5,1.3-0.5,2c-0.3,2.8,1.1,5.4,3.4,6.7c0.4-0.3,0.8-0.6,1.2-1.1c0.4-0.4,0.7-0.9,1-1.4    C-138.7,46.9-139.7,45.7-139.8,44.2z"
+   id="path5120" />
+	</g>
+</g>
+<g
+   id="g5122">
+	<g
+   id="g5124">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   d="M-96.6,41.5c-3.3-4-9.6-4.5-9.6-4.5c-0.4,0-0.8,0.3-0.8,0.7v1.4    c0,0.4,0.3,0.7,0.8,0.7c0,0,4.5,0.1,7.1,3.2c3.5,3.6,3.2,7.2,3.2,7.2c0,0.4,0.3,0.8,0.8,0.8h1.5c0.4,0,0.7-0.3,0.7-0.8    C-93,50.2-93.3,44.5-96.6,41.5z M-106.2,42c-0.4,0-0.8,0.3-0.8,0.7V44c0,0.4,0.3,0.7,0.8,0.7c0,0,2.4,0.1,3.8,1.4    c1.9,1.7,1.8,4.2,1.8,4.2c0,0.4,0.2,0.8,0.6,0.8h1.5c0.4,0,0.5-0.3,0.5-0.8c0,0-0.4-3.9-2.5-5.9C-102.5,42.4-106.2,42-106.2,42z     M-105,47c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2c1.1,0,2-0.9,2-2C-103,47.9-103.9,47-105,47z"
+   id="path5126" />
+	</g>
+</g>
+<path
+   fill="none"
+   d="M-102.6,34.4c0.5,0,1-0.4,1-1v-4c0,0,0.1-0.8,0.8-0.8c0.6,0,0.6,0.8,1.8,0.8c0.6,0,1.5-0.2,1.5-2  c0-1.8-0.9-2-1.5-2c-1.1,0-1.1,0.8-1.8,0.8c-0.6,0-0.8-0.8-0.8-0.8v-2c0-0.6-0.4-1-1-1h-3c0,0-0.8-0.1-0.8-0.8  c0-0.6,0.8-0.6,0.8-1.8c0-0.6-0.2-1.5-2-1.5c-1.8,0-2,0.9-2,1.5c0,1.1,0.8,1.1,0.8,1.8c0,0.6-0.8,0.8-0.8,0.8h-3c-0.5,0-1,0.4-1,1  l0,2.5c0,0-0.1,1.5,1.1,1.5c0.8,0,0.9-1,1.9-1c0.5,0,1,0.5,1,1.6c0,1-0.5,1.6-1,1.6c-1,0-1.1-1-1.9-1c-1.2,0-1.1,1.5-1.1,1.5l0,3.5  c0,0.6,0.4,1,1,1h3.8c0,0,1.5,0.1,1.5-1.1c0-0.8-1-0.9-1-1.9c0-0.5,0.7-1.2,1.8-1.2c1,0,1.8,0.7,1.8,1.2c0,1-1,1.1-1,1.9  c0,1.2,1.5,1.1,1.5,1.1H-102.6z"
+   id="path5128" />
+<g
+   id="g5130">
+	<g
+   id="g5132">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   d="M-63,46.7l-0.3-0.1L-63,52l2.6-3.8L-63,46.7z M-63.3,46l0.3,0    l6,2.9V36l-14,11l4.6-0.6L-63,52l-1.9-5.7l-0.3-0.1l0.3,0L-65,46l7-8L-63.3,46L-63.3,46z"
+   id="path5134" />
+	</g>
+</g>
+<g
+   id="g5136">
+	<g
+   id="g5138">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   d="M-23,37h-10c-1.1,0-2,0.9-2,2v10c0,1.1,0.9,2,2,2h10    c1.1,0,2-0.9,2-2V39C-21,37.9-21.9,37-23,37z M-23,46c0,1.7-1.3,2-3,2h-4c-1.7,0-3-1.3-3-3v-4c0-1.7,1.3-3,3-3h4c1.7,0,3,1.3,3,3    V46z"
+   id="path5140" />
+	</g>
+</g>
+<path
+   fill="#231F20"
+   d="M-23.7,42.6c0-0.8-0.2-1.5-0.6-2c-0.4-0.5-1-0.7-1.7-0.7c-0.4,0-0.8,0.1-1.1,0.3s-0.6,0.4-0.8,0.8  c-0.2-0.4-0.4-0.6-0.7-0.8c-0.3-0.2-0.7-0.2-1.2-0.2c-0.4,0-0.7,0-1.1,0.1s-0.7,0.2-1,0.4l0.3,0.7c0.6-0.3,1.2-0.5,1.7-0.5  c0.4,0,0.8,0.1,1,0.3c0.2,0.2,0.3,0.6,0.3,1.1v0.4l-1,0c-0.9,0-1.6,0.2-2.1,0.5c-0.5,0.3-0.7,0.8-0.7,1.4c0,0.6,0.2,1,0.5,1.3  c0.3,0.3,0.8,0.5,1.4,0.5c0.5,0,0.9-0.1,1.3-0.3c0.4-0.2,0.7-0.5,1-1c0.5,0.9,1.2,1.3,2.3,1.3c0.4,0,0.7,0,1-0.1  c0.3-0.1,0.6-0.2,0.9-0.3v-0.8c-0.3,0.1-0.6,0.3-0.9,0.3c-0.3,0.1-0.6,0.1-0.9,0.1c-1.2,0-1.8-0.7-1.8-2.2h3.9V42.6z M-28.5,43.6  c0,0.6-0.2,1-0.5,1.3c-0.3,0.3-0.7,0.5-1.3,0.5c-0.3,0-0.6-0.1-0.8-0.2c-0.2-0.2-0.3-0.4-0.3-0.8c0-0.4,0.2-0.7,0.5-0.9  c0.3-0.2,0.8-0.3,1.5-0.3l0.9,0V43.6z M-27.5,42.4c0-0.6,0.2-1,0.4-1.3c0.3-0.3,0.6-0.5,1.1-0.5c0.4,0,0.8,0.2,1,0.5  c0.2,0.3,0.4,0.8,0.4,1.3H-27.5z"
+   id="path5142" />
+<g
+   id="g5144">
+	<g
+   id="g5146">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   d="M14.5,37h-13C0.7,37,0,37.7,0,38.5v11C0,50.3,0.7,51,1.5,51h13    c0.8,0,1.5-0.7,1.5-1.5v-11C16,37.7,15.3,37,14.5,37z M6.5,38C6.8,38,7,38.2,7,38.5C7,38.8,6.8,39,6.5,39C6.2,39,6,38.8,6,38.5    C6,38.2,6.2,38,6.5,38z M4.4,38c0.3,0,0.5,0.2,0.5,0.5c0,0.3-0.2,0.5-0.5,0.5c-0.3,0-0.5-0.2-0.5-0.5C3.9,38.2,4.2,38,4.4,38z     M2.5,38C2.8,38,3,38.2,3,38.5C3,38.8,2.8,39,2.5,39C2.2,39,2,38.8,2,38.5C2,38.2,2.2,38,2.5,38z M14,48c0,0.6-0.4,1-1,1H3    c-0.6,0-1-0.4-1-1v-6c0-0.6,0.4-1,1-1h10c0.6,0,1,0.4,1,1V48z"
+   id="path5148" />
+	</g>
+</g>
+<path
+   fill="#231F20"
+   d="M51.2,45.9L51.2,45.9c-0.4-0.1-0.7-0.3-1-0.6c-0.1-0.1-0.1-0.1-0.2-0.2c-0.1-0.1-0.1-0.1-0.2-0.2  c0-0.1-0.1-0.1-0.1-0.2c0-0.1-0.1-0.1-0.1-0.2c-0.3-0.6-0.4-1.5-0.5-2.4c-0.3-2.2-0.1-3.8-3-3.8h-4.4c-2.9,0-2.6,1.6-2.9,3.8  c-0.1,0.9-0.3,1.8-0.6,2.4c0,0,0,0,0,0c-0.1,0.1-0.1,0.2-0.2,0.3c0,0,0,0-0.1,0.1c-0.1,0.2-0.2,0.3-0.4,0.4  c-0.3,0.2-0.5,0.4-0.9,0.5l0,0C36.4,46,36,46.3,36,46.8v1.9c0,0.5,0.4,0.9,0.9,0.9h14.1c0.5,0,0.9-0.4,0.9-0.9v-1.9  C52,46.3,51.6,46,51.2,45.9z M46.4,43.8c0,0.1-0.1,0.2-0.2,0.2h-1.6v1.6c0,0.1-0.1,0.2-0.2,0.2h-0.5c-0.1,0-0.2-0.1-0.2-0.2V44h-1.6  c-0.1,0-0.2-0.1-0.2-0.2v-0.5c0-0.1,0.1-0.2,0.2-0.2h1.6v-1.6c0-0.1,0.1-0.2,0.2-0.2h0.5c0.1,0,0.2,0.1,0.2,0.2v1.6h1.6  c0.1,0,0.2,0.1,0.2,0.2V43.8z"
+   id="path5150" />
+<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   d="M87.7,42c-0.1,1.1,0.2,2.5-1.3,4.4c-1.6,2.1-3.1,2.1-3.4,2.1  c-1.8-0.1-2-1.5-3-1.5c-0.9,0-1.6,1.4-3,1.5c-0.3,0-1.9,0-3.4-2c-1.5-1.9-1.2-3.3-1.3-4.4S72,39.7,72,39.7s0.7,0.7,1.6,0.8  c0.9,0.1,1.1-0.3,3-0.9c2.1-0.6,3.4,1.9,3.4,1.9s1.4-2.4,3.4-1.9c1.9,0.6,2,0.9,2.9,0.9c0.9-0.1,1.7-0.8,1.7-0.8S87.8,40.9,87.7,42z   M76.9,42.5c-1.1-0.3-1.6,0.2-2.1,0.4C74.4,43,74,43.1,74,43.1s0.1,0.7,1.2,1.2c1.1,0.6,3.5,0.3,3.5,0.3S78.9,42.9,76.9,42.5z   M85.2,42.9c-0.5-0.2-1-0.6-2.1-0.4c-2,0.4-1.8,2.1-1.8,2.1s2.4,0.3,3.5-0.3c1.1-0.6,1.2-1.2,1.2-1.2S85.6,43,85.2,42.9z"
+   id="path5152" />
+<g
+   id="g5154">
+	<g
+   id="g5156">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   d="M-237.5,84.9l-3.3-3.3c0.5-0.9,0.9-1.9,0.9-2.9c0-3-2.4-5.5-5.5-5.5    c-3,0-5.5,2.4-5.5,5.5c0,3,2.4,5.5,5.5,5.5c1.1,0,2.1-0.3,3-0.9l3.3,3.3c0.4,0.4,1,0.4,1.4,0l0.2-0.2    C-237.1,85.9-237.1,85.3-237.5,84.9z M-245.4,82c-1.8,0-3.3-1.5-3.3-3.4c0-1.9,1.5-3.4,3.3-3.4c1.8,0,3.3,1.5,3.3,3.4    C-242,80.5-243.5,82-245.4,82z"
+   id="path5158" />
+	</g>
+</g>
+<g
+   id="g5160">
+	<g
+   id="g5162">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   d="M-214.5,86.3l0.2,0.2c0.4,0.4,1,0.4,1.4,0l3.3-3.3c0.9,0.6,1.9,0.9,3,0.9    c3,0,5.5-2.4,5.5-5.5c0-3-2.4-5.5-5.5-5.5c-3,0-5.5,2.4-5.5,5.5c0,1.1,0.3,2.1,0.9,2.9l-3.3,3.3    C-214.9,85.3-214.9,85.9-214.5,86.3z M-210,78.6c0-1.9,1.5-3.4,3.3-3.4c1.8,0,3.3,1.5,3.3,3.4c0,1.9-1.5,3.4-3.3,3.4    C-208.5,82-210,80.5-210,78.6z"
+   id="path5164" />
+	</g>
+</g>
+<g
+   id="g5166">
+	<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   d="M-165,77.5h-1v-2c0-0.6-0.4-1-1-1v-1c0-0.6-0.4-1-1-1h-8   c-0.6,0-1,0.4-1,1v1c-0.6,0-1,0.4-1,1v2h-1c-0.6,0-1,0.4-1,1v5c0,0.6,0.4,1,1,1h2v-1h0.5l-1.5,3h12l-1.5-3h0.5v1h2c0.6,0,1-0.4,1-1   v-5C-164,77.9-164.4,77.5-165,77.5z M-176.5,80.5h-1c-0.3,0-0.5-0.2-0.5-0.5c0-0.3,0.2-0.5,0.5-0.5h1c0.3,0,0.5,0.2,0.5,0.5   C-176,80.2-176.2,80.5-176.5,80.5z M-176,85.5l0.9-2h6.2l0.9,2H-176z M-168,77.5c0,0.6-0.4,1-1,1h-6c-0.6,0-1-0.4-1-1v-3   c0-0.6,0.4-1,1-1h6c0.6,0,1,0.4,1,1V77.5z"
+   id="path5168" />
+</g>
+<g
+   id="g5170">
+	<path
+   fill="#231F20"
+   d="M-135,73l1.9,1.9l-2.6,2.6c-0.5,0.5-0.5,1.4,0.1,2c0.6,0.6,1.5,0.6,2,0.1l2.6-2.6l1.9,1.9v-6H-135z    M-138.5,80.4l-2.6,2.6L-143,81v6h6l-1.9-1.9l2.6-2.6c0.5-0.5,0.5-1.4-0.1-2C-137.1,79.9-138,79.9-138.5,80.4z"
+   id="path5172" />
+</g>
+<g
+   id="g5174">
+	<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   d="M-93.5,73.5c-0.6-0.6-1.5-0.6-2-0.1l-2.6,2.6L-100,74v6h6   l-1.9-1.9l2.6-2.6C-92.9,75-92.9,74.1-93.5,73.5z M-104.1,81.9l-2.6,2.6c-0.5,0.5-0.5,1.4,0.1,2c0.6,0.6,1.5,0.6,2,0.1l2.6-2.6   l1.9,1.9v-6h-6L-104.1,81.9z"
+   id="path5176" />
+</g>
+<g
+   id="g5178">
+	<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   d="M14.6,75.1l-2.3,2.3c-0.5,0.5-1.3,0.5-1.7,0   c-0.5-0.5-0.5-1.2,0-1.7l2.4-2.3c-0.6-0.3-1.2-0.5-1.9-0.5C8.8,72.9,7,74.7,7,77c0,0.5,0.1,1.1,0.3,1.5l-5.9,5.8   c-0.6,0.6-0.6,1.7,0,2.3c0.6,0.6,1.7,0.6,2.3,0l6-5.9c0.4,0.2,0.9,0.3,1.4,0.3c2.2,0,4.1-1.8,4.1-4.1   C15.1,76.3,14.9,75.7,14.6,75.1z M2.5,86.4c-0.5,0-0.9-0.4-0.9-0.9c0-0.5,0.4-0.9,0.9-0.9c0.5,0,0.9,0.4,0.9,0.9   C3.4,86,3,86.4,2.5,86.4z"
+   id="path5180" />
+</g>
+<path
+   fill="#231F20"
+   d="M-22.7,83.9l-1.1-1.1c0.2-0.3,0.3-0.6,0.5-0.9c0.1-0.3,0.2-0.6,0.3-0.9l1.6,0c0.3,0,0.5-0.2,0.5-0.5l0-1  c0-0.3-0.2-0.5-0.5-0.5l-1.6,0c-0.1-0.6-0.4-1.2-0.7-1.8l1.1-1.1c0.2-0.2,0.2-0.5,0-0.7l-0.7-0.7c-0.2-0.2-0.5-0.2-0.7,0l-1.1,1.1  c-0.3-0.2-0.6-0.3-0.9-0.5c-0.3-0.1-0.6-0.2-0.9-0.3l0-1.6c0-0.3-0.2-0.5-0.5-0.5l-1,0c-0.3,0-0.5,0.2-0.5,0.5l0,1.6  c-0.6,0.1-1.2,0.4-1.8,0.7l-1.1-1.1c-0.2-0.2-0.5-0.2-0.7,0l-0.7,0.7c-0.2,0.2-0.2,0.5,0,0.7l1.1,1.1c-0.2,0.3-0.3,0.6-0.5,0.9  c-0.1,0.3-0.2,0.6-0.3,0.9l-1.6,0c-0.3,0-0.5,0.2-0.5,0.5l0,1c0,0.3,0.2,0.5,0.5,0.5l1.6,0c0.1,0.6,0.4,1.2,0.7,1.8l-1.1,1.1  c-0.2,0.2-0.2,0.5,0,0.7l0.7,0.7c0.2,0.2,0.5,0.2,0.7,0l1.1-1.1c0.3,0.2,0.6,0.3,0.9,0.5c0.3,0.1,0.6,0.2,0.9,0.3l0,1.6  c0,0.3,0.2,0.5,0.5,0.5l1,0c0.3,0,0.5-0.2,0.5-0.5l0-1.6c0.6-0.1,1.2-0.4,1.8-0.7l1.1,1.1c0.2,0.2,0.5,0.2,0.7,0l0.7-0.7  C-22.5,84.4-22.5,84.1-22.7,83.9z M-28.8,82.1c-1.2-0.5-1.7-1.8-1.3-3c0.5-1.2,1.8-1.7,3-1.3c1.2,0.5,1.7,1.8,1.3,3  C-26.4,82-27.7,82.6-28.8,82.1z"
+   id="path5182" />
+<g
+   id="g5184">
+	<g
+   id="g5186">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   d="M-62,77h-4c-0.6,0-1,0.4-1,1v4c0,0.6,0.4,1,1,1h4    c0.6,0,1-0.4,1-1v-4C-61,77.4-61.4,77-62,77z M-62,81c0,0.6-0.4,1-1,1h-2c-0.6,0-1-0.4-1-1v-1c0-0.6,0.4-1,1-1h2c0.6,0,1,0.4,1,1    V81z M-64,87l3-3h-6L-64,87z M-67,76h6l-3-3L-67,76z M-60,76.9v6l3-3L-60,76.9z M-68,82.9v-6l-3,3L-68,82.9z"
+   id="path5188" />
+	</g>
+</g>
+<g
+   id="g5190">
+	<g
+   id="g5192">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   d="M37.5,76.5h13c0.8,0,1.5-0.7,1.5-1.5c0-0.8-0.7-1.5-1.5-1.5h-13    c-0.8,0-1.5,0.7-1.5,1.5C36,75.8,36.7,76.5,37.5,76.5z M50.5,78.5h-13c-0.8,0-1.5,0.7-1.5,1.5c0,0.8,0.7,1.5,1.5,1.5h13    c0.8,0,1.5-0.7,1.5-1.5C52,79.2,51.3,78.5,50.5,78.5z M50.5,83.5h-13c-0.8,0-1.5,0.7-1.5,1.5c0,0.8,0.7,1.5,1.5,1.5h13    c0.8,0,1.5-0.7,1.5-1.5C52,84.2,51.3,83.5,50.5,83.5z"
+   id="path5194" />
+	</g>
+</g>
+<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   d="M85.6,87c-0.8,0-1.5-0.2-2.3-1.3c-0.8-1.1-1.7-2.5-1.7-2.5  s-0.7-0.9-1.1-1.6c-0.4-0.7-1.1-0.5-1.1-0.5s-2.8-4.6-3.3-5.4c-0.7-1,0.6-2.7,0.6-2.7l4.4,7c0,0,1.3,1.9,1.9,2.3  c0.5,0.4,1.4-0.4,2.8,0.9C87.7,85,87.1,87,85.6,87z M85.4,84.1c-0.9-1-1.7-0.9-1.9-0.6c-0.2,0.3,0,1.2,0.4,1.7  c0.4,0.5,0.8,0.7,1.4,0.7C85.9,86,86.4,85.2,85.4,84.1z M81.6,79.4l-1.2-1.8l2.9-4.6c0,0,1.2,1.7,0.6,2.7  C83.6,76.1,82.5,78,81.6,79.4z M77,82.3c0.3-0.3,1-1.1,1.4-1.7l0.8,1.2c-0.4,0.6-0.9,1.4-0.9,1.4s-0.9,1.4-1.7,2.5  c-0.8,1.1-1.5,1.3-2.3,1.3c-1.4,0-2.1-2-0.1-3.8C75.6,82,76.5,82.7,77,82.3z M74.6,84.1c-0.9,1-0.4,1.8,0.2,1.8c0.6,0,1-0.2,1.4-0.7  c0.4-0.5,0.6-1.5,0.4-1.7C76.3,83.2,75.5,83.1,74.6,84.1z"
+   id="path5196" />
+<g
+   id="g5198">
+	<g
+   id="g5200">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   d="M-243,111c0-0.1-2-2-2-2c-1.7,0-5,0-5,0c-0.6,0-1,0.4-1,1v8    c0,0.6,0.4,1,1,1h4v-6.1c0-0.3,0.2-0.5,0.5-0.5h2.5V111z M-246,112v-2l2,2H-246z M-239,113c-1.7,0-5,0-5,0c-0.6,0-1,0.4-1,1v8    c0,0.6,0.4,1,1,1h6c0.6,0,1-0.4,1-1c0,0,0-4.8,0-7C-237,115-239,113-239,113z M-240,116v-2l2,2H-240z"
+   id="path5202" />
+	</g>
+</g>
+<g
+   id="g5204">
+	<g
+   id="g5206">
+		<path
+   fill="#231F20"
+   d="M-204.5,111h-1.3l0,0c0,0-0.2-2-2.2-2c-2,0-2.2,2-2.2,2l0,0h-1.3c-0.8,0-1.5,0.7-1.5,1.5v9    c0,0.8,0.7,1.5,1.5,1.5h7c0.8,0,1.5-0.7,1.5-1.5v-9C-203,111.7-203.7,111-204.5,111z M-210.7,112.1l0.8-0.4l0.4-0.2l0-0.4    c0-0.2,0.2-1.3,1.5-1.3c1.2,0,1.4,1.1,1.5,1.3l0,0.4l0.4,0.2l0.8,0.4l0.3,0.7h-6.1L-210.7,112.1z M-210.1,120.4l-2.8-4.9l3.3-1.9    h4.3l1.8,3.1L-210.1,120.4z"
+   id="path5208" />
+	</g>
+</g>
+<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   d="M-177,115h10v2h-10V115z"
+   id="path5210" />
+<g
+   id="g5212">
+	<g
+   id="g5214">
+		<polygon
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   points="-131,115 -135,115 -135,111 -137,111 -137,115 -141,115     -141,117 -137,117 -137,121 -135,121 -135,117 -131,117   "
+   id="polygon5216" />
+	</g>
+</g>
+<g
+   id="g5218">
+	<path
+   fill="#231F20"
+   d="M-94,111.7l-3,2.7v-2c0-0.8-0.6-1.4-1.3-1.4h-7.4c-0.7,0-1.3,0.6-1.3,1.4v7.2c0,0.8,0.6,1.4,1.3,1.4h7.4   c0.7,0,1.3-0.6,1.3-1.4v-2.1l3,2.7c0.3,0.3,0.6,0.4,1,0.3v-9.1C-93.3,111.4-93.7,111.5-94,111.7z"
+   id="path5220" />
+</g>
+<g
+   id="g5222">
+	<g
+   id="g5224">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   d="M-61.9,112h-3l3.8,4l-3.8,4h3.1l3.8-4L-61.9,112z M-66.9,112h-3    l3.8,4l-3.8,4h3.1l3.8-4L-66.9,112z"
+   id="path5226" />
+	</g>
+</g>
+<g
+   id="g5228">
+	<g
+   id="g5230">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   d="M-23,111.5h-2l-3-3l-3,3h-2c-1.1,0-2,0.9-2,2v8c0,1.1,0.9,2,2,2    h10c1.1,0,2-0.9,2-2v-8C-21,112.4-21.9,111.5-23,111.5z M-32,121.5c-0.6,0-1-0.4-1-1s0.4-1,1-1s1,0.4,1,1S-31.4,121.5-32,121.5z     M-32,118.5c-0.6,0-1-0.4-1-1s0.4-1,1-1s1,0.4,1,1S-31.4,118.5-32,118.5z M-32,115.5c-0.6,0-1-0.4-1-1s0.4-1,1-1s1,0.4,1,1    S-31.4,115.5-32,115.5z M-23,121.5h-7v-2h7V121.5z M-23,118.5h-7v-2h7V118.5z M-23,115.5h-7v-2h7V115.5z"
+   id="path5232" />
+	</g>
+</g>
+<g
+   id="g5234">
+	<g
+   id="g5236">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   d="M2,115h7c0.6,0,1-0.4,1-1v-4c0-0.6-0.4-1-1-1H2c-0.6,0-1,0.4-1,1    v4C1,114.6,1.4,115,2,115z M14,109h-2c-0.6,0-1,0.4-1,1v4c0,0.6,0.4,1,1,1h2c0.6,0,1-0.4,1-1v-4C15,109.4,14.6,109,14,109z     M14,116H8c-0.6,0-1,0.4-1,1v5c0,0.6,0.4,1,1,1h6c0.6,0,1-0.4,1-1v-5C15,116.4,14.6,116,14,116z M5,116H2c-0.6,0-1,0.4-1,1v5    c0,0.6,0.4,1,1,1h3c0.6,0,1-0.4,1-1v-5C6,116.4,5.6,116,5,116z"
+   id="path5238" />
+	</g>
+</g>
+<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   d="M45.8,116c0,0-0.6,0.8-1.8,0.8c-1.2,0-1.8-0.8-1.8-0.8l-6-5.1  c0.3-0.5,0.9-0.9,1.6-0.9h12.4c0.7,0,1.3,0.4,1.6,0.9L45.8,116z M42.2,117.7c0,0,0.6,0.8,1.8,0.8c1.2,0,1.8-0.8,1.8-0.8l6.2-5.4v8  c0,0.9-0.8,1.7-1.8,1.7H37.8c-1,0-1.8-0.8-1.8-1.7v-8L42.2,117.7z"
+   id="path5240" />
+<g
+   id="g5242">
+	<g
+   id="g5244">
+		<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   d="M-237.5,145h-13c-0.8,0-1.5,0.7-1.5,1.5v11    c0,0.8,0.7,1.5,1.5,1.5h13c0.8,0,1.5-0.7,1.5-1.5v-11C-236,145.7-236.7,145-237.5,145z M-245.5,146c0.3,0,0.5,0.2,0.5,0.5    c0,0.3-0.2,0.5-0.5,0.5c-0.3,0-0.5-0.2-0.5-0.5C-246,146.2-245.8,146-245.5,146z M-247.6,146c0.3,0,0.5,0.2,0.5,0.5    c0,0.3-0.2,0.5-0.5,0.5s-0.5-0.2-0.5-0.5C-248.1,146.2-247.8,146-247.6,146z M-249.5,146c0.3,0,0.5,0.2,0.5,0.5    c0,0.3-0.2,0.5-0.5,0.5s-0.5-0.2-0.5-0.5C-250,146.2-249.8,146-249.5,146z M-250,156v-6c0-0.6,0.4-1,1-1h7v8h-7    C-249.6,157-250,156.6-250,156z M-238,156c0,0.6-0.4,1-1,1h-1v-8h1c0.6,0,1,0.4,1,1V156z"
+   id="path5246" />
+	</g>
+</g>
+<path
+   fill-rule="evenodd"
+   clip-rule="evenodd"
+   fill="#231F20"
+   d="M-213.2,156c-1.3-1.2-2.2-2.8-2.2-4.6c0-3.6,3.3-6.5,7.4-6.5  c4.1,0,7.4,2.9,7.4,6.5c0,3.6-3.3,6.5-7.4,6.5c-0.8,0-1.6-0.1-2.4-0.3c-1.8,0.7-4.3,1.7-4.5,1.4C-213.9,157.9-213.5,156.8-213.2,156  z"
+   id="path5248" />
+<rect
+   id="_x3C_Slice_x3E_"
+   fill="none"
+   width="16"
+   height="16" />
+</svg>
\ No newline at end of file
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -31,16 +31,19 @@ browser.jar:
         skin/classic/browser/click-to-play-warning-stripes.png
         skin/classic/browser/content-contextmenu.svg
         skin/classic/browser/dots.png                                (../shared/dots.png)
         skin/classic/browser/dots@2x.png                             (../shared/dots@2x.png)
 *       skin/classic/browser/engineManager.css
         skin/classic/browser/fullscreen-darknoise.png
         skin/classic/browser/Geolocation-16.png
         skin/classic/browser/Geolocation-64.png
+        skin/classic/browser/heartbeat-icon.svg                      (../shared/heartbeat-icon.svg)
+        skin/classic/browser/heartbeat-star-lit.svg                  (../shared/heartbeat-star-lit.svg)
+        skin/classic/browser/heartbeat-star-off.svg                  (../shared/heartbeat-star-off.svg)
         skin/classic/browser/Info.png
         skin/classic/browser/identity.png
         skin/classic/browser/identity-icons-generic.png
         skin/classic/browser/identity-icons-https.png
         skin/classic/browser/identity-icons-https-ev.png
         skin/classic/browser/identity-icons-https-mixed-active.png
         skin/classic/browser/identity-icons-https-mixed-display.png
         skin/classic/browser/keyhole-forward-mask.svg
@@ -484,16 +487,19 @@ browser.jar:
         skin/classic/aero/browser/click-to-play-warning-stripes.png
 *       skin/classic/aero/browser/content-contextmenu.svg
         skin/classic/aero/browser/dots.png                           (../shared/dots.png)
         skin/classic/aero/browser/dots@2x.png                        (../shared/dots@2x.png)
 *       skin/classic/aero/browser/engineManager.css
         skin/classic/aero/browser/fullscreen-darknoise.png
         skin/classic/aero/browser/Geolocation-16.png
         skin/classic/aero/browser/Geolocation-64.png
+        skin/classic/aero/browser/heartbeat-icon.svg                 (../shared/heartbeat-icon.svg)
+        skin/classic/aero/browser/heartbeat-star-lit.svg             (../shared/heartbeat-star-lit.svg)
+        skin/classic/aero/browser/heartbeat-star-off.svg             (../shared/heartbeat-star-off.svg)
         skin/classic/aero/browser/Info.png                           (Info-aero.png)
         skin/classic/aero/browser/identity.png                       (identity-aero.png)
         skin/classic/aero/browser/identity-icons-generic.png
         skin/classic/aero/browser/identity-icons-https.png
         skin/classic/aero/browser/identity-icons-https-ev.png
         skin/classic/aero/browser/identity-icons-https-mixed-active.png
         skin/classic/aero/browser/identity-icons-https-mixed-display.png
         skin/classic/aero/browser/keyhole-forward-mask.svg
--- a/dom/base/nsCopySupport.cpp
+++ b/dom/base/nsCopySupport.cpp
@@ -92,17 +92,18 @@ SelectionCopyHelper(nsISelection *aSel, 
   // is. if it is a selection into input/textarea element or in a html content
   // with pre-wrap style : text/plain. Otherwise text/html.
   // see nsHTMLCopyEncoder::SetSelection
   nsAutoString mimeType;
   mimeType.AssignLiteral(kUnicodeMime);
 
   // Do the first and potentially trial encoding as preformatted and raw.
   uint32_t flags = aFlags | nsIDocumentEncoder::OutputPreformatted
-                          | nsIDocumentEncoder::OutputRaw;
+                          | nsIDocumentEncoder::OutputRaw
+                          | nsIDocumentEncoder::OutputForPlainTextClipboardCopy;
 
   nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aDoc);
   NS_ASSERTION(domDoc, "Need a document");
 
   rv = docEncoder->Init(domDoc, mimeType, flags);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = docEncoder->SetSelection(aSel);
--- a/dom/base/nsDocumentEncoder.cpp
+++ b/dom/base/nsDocumentEncoder.cpp
@@ -42,19 +42,16 @@
 #include "nsNodeUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsReadableUtils.h"
 #include "nsTArray.h"
 #include "nsIFrame.h"
 #include "nsStringBuffer.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ShadowRoot.h"
-#include "nsIEditor.h"
-#include "nsIHTMLEditor.h"
-#include "nsIDocShell.h"
 #include "mozilla/dom/EncodingUtils.h"
 #include "nsComputedDOMStyle.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 nsresult NS_NewDomSelection(nsISelection **aDomSelection);
 
@@ -319,44 +316,32 @@ bool
 nsDocumentEncoder::IncludeInContext(nsINode *aNode)
 {
   return false;
 }
 
 static
 bool
 IsInvisibleBreak(nsINode *aNode) {
-  // xxxehsan: we should probably figure out a way to determine
-  // if a BR node is visible without using the editor.
-  Element* elt = aNode->AsElement();
-  if (!elt->IsHTML(nsGkAtoms::br) ||
-      !aNode->IsEditable()) {
+  if (!aNode->IsElement() || !aNode->IsEditable()) {
+    return false;
+  }
+  nsIFrame* frame = aNode->AsElement()->GetPrimaryFrame();
+  if (!frame || frame->GetType() != nsGkAtoms::brFrame) {
     return false;
   }
 
-  // Grab the editor associated with the document
-  nsIDocument *doc = aNode->GetComposedDoc();
-  if (doc) {
-    nsPIDOMWindow *window = doc->GetWindow();
-    if (window) {
-      nsIDocShell *docShell = window->GetDocShell();
-      if (docShell) {
-        nsCOMPtr<nsIEditor> editor;
-        docShell->GetEditor(getter_AddRefs(editor));
-        nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(editor);
-        if (htmlEditor) {
-          bool isVisible = false;
-          nsCOMPtr<nsIDOMNode> domNode = do_QueryInterface(aNode);
-          htmlEditor->BreakIsVisible(domNode, &isVisible);
-          return !isVisible;
-        }
-      }
-    }
-  }
-  return false;
+  // If the BRFrame has caused a visible line break, it should have a next
+  // sibling, or otherwise no siblings (or immediately after a br) and a
+  // non-zero height.
+  bool visible = frame->GetNextSibling() ||
+                 ((!frame->GetPrevSibling() ||
+                   frame->GetPrevSibling()->GetType() == nsGkAtoms::brFrame) &&
+                  frame->GetRect().Height() != 0);
+  return !visible;
 }
 
 nsresult
 nsDocumentEncoder::SerializeNodeStart(nsINode* aNode,
                                       int32_t aStartOffset,
                                       int32_t aEndOffset,
                                       nsAString& aStr,
                                       nsINode* aOriginalNode)
--- a/dom/base/nsHTMLContentSerializer.cpp
+++ b/dom/base/nsHTMLContentSerializer.cpp
@@ -187,17 +187,17 @@ nsHTMLContentSerializer::AppendElementSt
     return NS_OK;
   }
 
   nsIAtom *name = content->Tag();
   int32_t ns = content->GetNameSpaceID();
 
   bool lineBreakBeforeOpen = LineBreakBeforeOpen(ns, name);
 
-  if ((mDoFormat || forceFormat) && !mPreLevel && !mDoRaw) {
+  if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel()) {
     if (mColPos && lineBreakBeforeOpen) {
       AppendNewLineToString(aStr);
     }
     else {
       MaybeAddNewlineForRootNode(aStr);
     }
     if (!mColPos) {
       AppendIndentation(aStr);
@@ -220,17 +220,17 @@ nsHTMLContentSerializer::AppendElementSt
   
   AppendToString(kLessThan, aStr);
 
   AppendToString(nsDependentAtomString(name), aStr);
 
   MaybeEnterInPreContent(content);
 
   // for block elements, we increase the indentation
-  if ((mDoFormat || forceFormat) && !mPreLevel && !mDoRaw)
+  if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel())
     IncrIndentation(name);
 
   // Need to keep track of OL and LI elements in order to get ordinal number 
   // for the LI.
   if (mIsCopying && name == nsGkAtoms::ol && ns == kNameSpaceID_XHTML){
     // We are copying and current node is an OL;
     // Store its start attribute value in olState->startVal.
     nsAutoString start;
@@ -275,18 +275,18 @@ nsHTMLContentSerializer::AppendElementSt
   if (ns == kNameSpaceID_XHTML &&
       (name == nsGkAtoms::script ||
        name == nsGkAtoms::style ||
        name == nsGkAtoms::noscript ||
        name == nsGkAtoms::noframes)) {
     ++mDisableEntityEncoding;
   }
 
-  if ((mDoFormat || forceFormat) && !mPreLevel &&
-    !mDoRaw && LineBreakAfterOpen(ns, name)) {
+  if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel() &&
+    LineBreakAfterOpen(ns, name)) {
     AppendNewLineToString(aStr);
   }
 
   AfterElementStart(content, aOriginalElement, aStr);
 
   return NS_OK;
 }
   
@@ -307,28 +307,28 @@ nsHTMLContentSerializer::AppendElementEn
        name == nsGkAtoms::noscript ||
        name == nsGkAtoms::noframes)) {
     --mDisableEntityEncoding;
   }
 
   bool forceFormat = !(mFlags & nsIDocumentEncoder::OutputIgnoreMozDirty) &&
                      content->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty);
 
-  if ((mDoFormat || forceFormat) && !mPreLevel && !mDoRaw) {
+  if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel()) {
     DecrIndentation(name);
   }
 
   if (name == nsGkAtoms::script) {
     nsCOMPtr<nsIScriptElement> script = do_QueryInterface(aElement);
 
-    if (script && script->IsMalformed()) {
+    if (ShouldMaintainPreLevel() && script && script->IsMalformed()) {
       // We're looking at a malformed script tag. This means that the end tag
       // was missing in the source. Imitate that here by not serializing the end
       // tag.
-      --mPreLevel;
+      --PreLevel();
       return NS_OK;
     }
   }
   else if (mIsCopying && name == nsGkAtoms::ol && ns == kNameSpaceID_XHTML) {
     NS_ASSERTION((!mOLStateStack.IsEmpty()), "Cannot have an empty OL Stack");
     /* Though at this point we must always have an state to be deleted as all 
     the OL opening tags are supposed to push an olState object to the stack*/
     if (!mOLStateStack.IsEmpty()) {
@@ -346,17 +346,17 @@ nsHTMLContentSerializer::AppendElementEn
         IsContainer(parserService->HTMLCaseSensitiveAtomTagToId(name),
                     isContainer);
       if (!isContainer) {
         return NS_OK;
       }
     }
   }
 
-  if ((mDoFormat || forceFormat) && !mPreLevel && !mDoRaw) {
+  if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel()) {
 
     bool lineBreakBeforeClose = LineBreakBeforeClose(ns, name);
 
     if (mColPos && lineBreakBeforeClose) {
       AppendNewLineToString(aStr);
     }
     if (!mColPos) {
       AppendIndentation(aStr);
@@ -372,18 +372,18 @@ nsHTMLContentSerializer::AppendElementEn
   }
 
   AppendToString(kEndTag, aStr);
   AppendToString(nsDependentAtomString(name), aStr);
   AppendToString(kGreaterThan, aStr);
 
   MaybeLeaveFromPreContent(content);
 
-  if ((mDoFormat || forceFormat) && !mPreLevel
-      && !mDoRaw && LineBreakAfterClose(ns, name)) {
+  if ((mDoFormat || forceFormat)&& !mDoRaw  && !PreLevel()
+      && LineBreakAfterClose(ns, name)) {
     AppendNewLineToString(aStr);
   }
   else {
     MaybeFlagNewlineForRootNode(aElement);
   }
 
   if (name == nsGkAtoms::body && ns == kNameSpaceID_XHTML) {
     --mInBody;
--- a/dom/base/nsIDocumentEncoder.idl
+++ b/dom/base/nsIDocumentEncoder.idl
@@ -223,16 +223,23 @@ interface nsIDocumentEncoder : nsISuppor
   const unsigned long OutputNonTextContentAsPlaceholder = (1 << 23);
 
   /**
    * Don't Strip ending spaces from a line (only for serializing to plaintext).
    */
   const unsigned long OutputDontRemoveLineEndingSpaces = (1 << 24);
 
   /**
+   * Serialize in a way that is suitable for copying a plaintext version of the
+   * document to the clipboard.  This can for example cause line endings to be
+   * injected at preformatted block element boundaries.
+   */
+  const unsigned long OutputForPlainTextClipboardCopy = (1 << 25);
+
+  /**
    * Initialize with a pointer to the document and the mime type.
    * @param aDocument Document to encode.
    * @param aMimeType MimeType to use. May also be set by SetMimeType.
    * @param aFlags Flags to use while encoding. May also be set by SetFlags.
    */
   void init(in nsIDOMDocument aDocument,
             in AString aMimeType,
             in unsigned long aFlags);
--- a/dom/base/nsPlainTextSerializer.cpp
+++ b/dom/base/nsPlainTextSerializer.cpp
@@ -17,16 +17,17 @@
 #include "nsTextFragment.h"
 #include "nsContentUtils.h"
 #include "nsReadableUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsCRT.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/BinarySearch.h"
+#include "nsComputedDOMStyle.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 #define PREF_STRUCTS "converter.html2txt.structs"
 #define PREF_HEADER_STRATEGY "converter.html2txt.header_strategy"
 
 static const  int32_t kTabSize=4;
@@ -85,16 +86,18 @@ nsPlainTextSerializer::nsPlainTextSerial
   mCurrentLineWidth = 0;
 
   // Flow
   mEmptyLines = 1; // The start of the document is an "empty line" in itself,
   mInWhitespace = false;
   mPreFormatted = false;
   mStartedOutput = false;
 
+  mPreformattedBlockBoundary = false;
+
   // initialize the tag stack to zero:
   // The stack only ever contains pointers to static atoms, so they don't
   // need refcounting.
   mTagStack = new nsIAtom*[TagStackSize];
   mTagStackIndex = 0;
   mIgnoreAboveIndex = (uint32_t)kNotFound;
 
   // initialize the OL stack, where numbers for ordered lists are kept
@@ -161,16 +164,18 @@ nsPlainTextSerializer::Init(uint32_t aFl
   else {
     // Platform/default
     mLineBreak.AssignLiteral(NS_LINEBREAK);
   }
 
   mLineBreakDue = false;
   mFloatingLines = -1;
 
+  mPreformattedBlockBoundary = false;
+
   if (mFlags & nsIDocumentEncoder::OutputFormatted) {
     // Get some prefs that controls how we do formatted output
     mStructs = Preferences::GetBool(PREF_STRUCTS, mStructs);
 
     mHeaderStrategy =
       Preferences::GetInt(PREF_HEADER_STRATEGY, mHeaderStrategy);
 
     // DontWrapAnyQuotes is set according to whether plaintext mail
@@ -351,16 +356,17 @@ nsPlainTextSerializer::AppendElementStar
   nsIAtom* id = GetIdForContent(mElement);
 
   bool isContainer = !nsContentUtils::IsHTMLVoid(id);
 
   mOutputString = &aStr;
 
   if (isContainer) {
     rv = DoOpenContainer(id);
+    mPreformatStack.push(IsElementPreformatted(mElement));
   }
   else {
     rv = DoAddLeaf(id);
   }
 
   mElement = nullptr;
   mOutputString = nullptr;
 
@@ -384,16 +390,17 @@ nsPlainTextSerializer::AppendElementEnd(
 
   bool isContainer = !nsContentUtils::IsHTMLVoid(id);
 
   mOutputString = &aStr;
 
   rv = NS_OK;
   if (isContainer) {
     rv = DoCloseContainer(id);
+    mPreformatStack.pop();
   }
 
   mElement = nullptr;
   mOutputString = nullptr;
 
   if (id == nsGkAtoms::head) {
     NS_ASSERTION(mHeadLevel != 0,
                  "mHeadLevel being decremented below 0");
@@ -429,16 +436,26 @@ nsPlainTextSerializer::DoOpenContainer(n
       // Serialize current node as placeholder character
       Write(NS_LITERAL_STRING("\xFFFC"));
     }
     // Ignore child nodes.
     mIgnoredChildNodeLevel++;
     return NS_OK;
   }
 
+  if (mFlags & nsIDocumentEncoder::OutputForPlainTextClipboardCopy) {
+    if (mPreformattedBlockBoundary && DoOutput()) {
+      // Should always end a line, but get no more whitespace
+      if (mFloatingLines < 0)
+        mFloatingLines = 0;
+      mLineBreakDue = true;
+    }
+    mPreformattedBlockBoundary = false;
+  }
+
   if (mFlags & nsIDocumentEncoder::OutputRaw) {
     // Raw means raw.  Don't even think about doing anything fancy
     // here like indenting, adding line breaks or any other
     // characters such as list item bullets, quote characters
     // around <q>, etc.  I mean it!  Don't make me smack you!
 
     return NS_OK;
   }
@@ -662,17 +679,17 @@ nsPlainTextSerializer::DoOpenContainer(n
     }
   }
   else if (aTag == nsGkAtoms::q) {
     Write(NS_LITERAL_STRING("\""));
   }
 
   // Else make sure we'll separate block level tags,
   // even if we're about to leave, before doing any other formatting.
-  else if (nsContentUtils::IsHTMLBlock(aTag)) {
+  else if (IsElementBlock(mElement)) {
     EnsureVerticalSpace(0);
   }
 
   //////////////////////////////////////////////////////////////
   if (!(mFlags & nsIDocumentEncoder::OutputFormatted)) {
     return NS_OK;
   }
   //////////////////////////////////////////////////////////////
@@ -759,16 +776,24 @@ nsPlainTextSerializer::DoOpenContainer(n
 nsresult
 nsPlainTextSerializer::DoCloseContainer(nsIAtom* aTag)
 {
   if (ShouldReplaceContainerWithPlaceholder(mElement->Tag())) {
     mIgnoredChildNodeLevel--;
     return NS_OK;
   }
 
+  if (mFlags & nsIDocumentEncoder::OutputForPlainTextClipboardCopy) {
+    if (DoOutput() && IsInPre() && IsElementBlock(mElement)) {
+      // If we're closing a preformatted block element, output a line break
+      // when we find a new container.
+      mPreformattedBlockBoundary = true;
+    }
+  }
+
   if (mFlags & nsIDocumentEncoder::OutputRaw) {
     // Raw means raw.  Don't even think about doing anything fancy
     // here like indenting, adding line breaks or any other
     // characters such as list item bullets, quote characters
     // around <q>, etc.  I mean it!  Don't make me smack you!
 
     return NS_OK;
   }
@@ -879,18 +904,17 @@ nsPlainTextSerializer::DoCloseContainer(
       mIndent -= kTabSize;
       mFloatingLines = 1;
     }
     mLineBreakDue = true;
   }
   else if (aTag == nsGkAtoms::q) {
     Write(NS_LITERAL_STRING("\""));
   }
-  else if (nsContentUtils::IsHTMLBlock(aTag)
-           && aTag != nsGkAtoms::script) {
+  else if (IsElementBlock(mElement) && aTag != nsGkAtoms::script) {
     // All other blocks get 1 vertical space after them
     // in formatted mode, otherwise 0.
     // This is hard. Sometimes 0 is a better number, but
     // how to know?
     if (mFlags & nsIDocumentEncoder::OutputFormatted)
       EnsureVerticalSpace(1);
     else {
       if (mFloatingLines < 0)
@@ -1029,16 +1053,18 @@ nsPlainTextSerializer::DoAddText(bool aI
     mURL.Truncate();
   }
   Write(aText);
 }
 
 nsresult
 nsPlainTextSerializer::DoAddLeaf(nsIAtom* aTag)
 {
+  mPreformattedBlockBoundary = false;
+
   // If we don't want any output, just return
   if (!DoOutput()) {
     return NS_OK;
   }
 
   if (mLineBreakDue)
     EnsureVerticalSpace(mFloatingLines);
 
@@ -1532,17 +1558,17 @@ nsPlainTextSerializer::Write(const nsASt
   // Output directly while the other code path goes through AddToLine.
   if ((mPreFormatted && !mWrapColumn) || IsInPre()
       || ((mSpanLevel > 0 || mDontWrapAnyQuotes)
           && mEmptyLines >= 0 && str.First() == char16_t('>'))) {
     // No intelligent wrapping.
 
     // This mustn't be mixed with intelligent wrapping without clearing
     // the mCurrentLine buffer before!!!
-    NS_ASSERTION(mCurrentLine.IsEmpty(),
+    NS_ASSERTION(mCurrentLine.IsEmpty() || IsInPre(),
                  "Mixed wrapping data and nonwrapping data on the same line");
     if (!mCurrentLine.IsEmpty()) {
       FlushLine();
     }
 
     // Put the mail quote "> " chars in, if appropriate.
     // Have to put it in before every line.
     while(bol<totLen) {
@@ -1750,39 +1776,48 @@ nsPlainTextSerializer::GetIdForContent(n
   if (!aContent->IsHTML()) {
     return nullptr;
   }
 
   nsIAtom* localName = aContent->Tag();
   return localName->IsStaticAtom() ? localName : nullptr;
 }
 
-/**
- * Returns true if we currently are inside a <pre>. The check is done
- * by traversing the tag stack looking for <pre> until we hit a block
- * level tag which is assumed to override any <pre>:s below it in
- * the stack. To do this correctly to a 100% would require access
- * to style which we don't support in this converter.
- */  
 bool
 nsPlainTextSerializer::IsInPre()
 {
-  int32_t i = mTagStackIndex;
-  while(i > 0) {
-    if (mTagStack[i - 1] == nsGkAtoms::pre)
-      return true;
-    if (nsContentUtils::IsHTMLBlock(mTagStack[i - 1])) {
-      // We assume that every other block overrides a <pre>
-      return false;
-    }
-    --i;
+  return !mPreformatStack.empty() && mPreformatStack.top();
+}
+
+bool
+nsPlainTextSerializer::IsElementPreformatted(Element* aElement)
+{
+  nsRefPtr<nsStyleContext> styleContext =
+    nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement, nullptr,
+                                                         nullptr);
+  if (styleContext) {
+    const nsStyleText* textStyle = styleContext->StyleText();
+    return textStyle->WhiteSpaceOrNewlineIsSignificant();
   }
+  // Fall back to looking at the tag, in case there is no style information.
+  return GetIdForContent(aElement) == nsGkAtoms::pre;
+}
 
-  // Not a <pre> in the whole stack
-  return false;
+bool
+nsPlainTextSerializer::IsElementBlock(Element* aElement)
+{
+  nsRefPtr<nsStyleContext> styleContext =
+    nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement, nullptr,
+                                                         nullptr);
+  if (styleContext) {
+    const nsStyleDisplay* displayStyle = styleContext->StyleDisplay();
+    return displayStyle->IsBlockOutsideStyle();
+  }
+  // Fall back to looking at the tag, in case there is no style information.
+  return nsContentUtils::IsHTMLBlock(GetIdForContent(aElement));
 }
 
 /**
  * This method is required only to identify LI's inside OL.
  * Returns TRUE if we are inside an OL tag and FALSE otherwise.
  */
 bool
 nsPlainTextSerializer::IsInOL()
--- a/dom/base/nsPlainTextSerializer.h
+++ b/dom/base/nsPlainTextSerializer.h
@@ -17,16 +17,18 @@
 #include "nsCOMPtr.h"
 #include "nsIAtom.h"
 #include "nsIContentSerializer.h"
 #include "nsIDocumentEncoder.h"
 #include "nsILineBreaker.h"
 #include "nsString.h"
 #include "nsTArray.h"
 
+#include <stack>
+
 class nsIContent;
 
 namespace mozilla {
 namespace dom {
 class Element;
 } // namespace dom
 } // namespace mozilla
 
@@ -107,16 +109,20 @@ protected:
   // Stack handling functions
   bool GetLastBool(const nsTArray<bool>& aStack);
   void SetLastBool(nsTArray<bool>& aStack, bool aValue);
   void PushBool(nsTArray<bool>& aStack, bool aValue);
   bool PopBool(nsTArray<bool>& aStack);
 
   bool ShouldReplaceContainerWithPlaceholder(nsIAtom* aTag);
 
+private:
+  bool IsElementPreformatted(mozilla::dom::Element* aElement);
+  bool IsElementBlock(mozilla::dom::Element* aElement);
+
 protected:
   nsString         mCurrentLine;
   uint32_t         mHeadLevel;
   bool             mAtFirstColumn;
 
   // Handling of quoted text (for mail):
   // Quotes need to be wrapped differently from non-quoted text,
   // because quoted text has a few extra characters (e.g. ">> ")
@@ -160,17 +166,19 @@ protected:
 
   bool             mInWhitespace;
   bool             mPreFormatted;
   bool             mStartedOutput; // we've produced at least a character
 
   // While handling a new tag, this variable should remind if any line break
   // is due because of a closing tag. Setting it to "TRUE" while closing the tags.
   // Hence opening tags are guaranteed to start with appropriate line breaks.
-  bool             mLineBreakDue; 
+  bool             mLineBreakDue;
+
+  bool             mPreformattedBlockBoundary;
 
   nsString         mURL;
   int32_t          mHeaderStrategy;    /* Header strategy (pref)
                                           0 = no indention
                                           1 = indention, increased with
                                               header level (default)
                                           2 = numbering and slight indention */
   int32_t          mHeaderCounter[7];  /* For header-numbering:
@@ -191,16 +199,21 @@ protected:
   nsAString*            mOutputString;
 
   // The tag stack: the stack of tags we're operating on, so we can nest.
   // The stack only ever points to static atoms, so they don't need to be
   // refcounted.
   nsIAtom**        mTagStack;
   uint32_t         mTagStackIndex;
 
+  // The stack indicating whether the elements we've been operating on are
+  // CSS preformatted elements, so that we can tell if the text inside them
+  // should be formatted.
+  std::stack<bool> mPreformatStack;
+
   // Content in the stack above this index should be ignored:
   uint32_t          mIgnoreAboveIndex;
 
   // The stack for ordered lists
   int32_t         *mOLStack;
   uint32_t         mOLStackIndex;
 
   uint32_t         mULCount;
--- a/dom/base/nsXHTMLContentSerializer.cpp
+++ b/dom/base/nsXHTMLContentSerializer.cpp
@@ -28,16 +28,18 @@
 #include "nsITextToSubURI.h"
 #include "nsCRT.h"
 #include "nsIParserService.h"
 #include "nsContentUtils.h"
 #include "nsLWBrkCIID.h"
 #include "nsIScriptElement.h"
 #include "nsAttrName.h"
 #include "nsParserConstants.h"
+#include "nsComputedDOMStyle.h"
+#include "mozilla/dom/Element.h"
 
 static const int32_t kLongLineLen = 128;
 
 #define kXMLNS "xmlns"
 
 nsresult NS_NewXHTMLContentSerializer(nsIContentSerializer** aSerializer)
 {
   nsXHTMLContentSerializer* it = new nsXHTMLContentSerializer();
@@ -126,17 +128,17 @@ nsXHTMLContentSerializer::AppendText(nsI
 
   nsAutoString data;
   nsresult rv;
 
   rv = AppendTextData(aText, aStartOffset, aEndOffset, data, true);
   if (NS_FAILED(rv))
     return NS_ERROR_FAILURE;
 
-  if (mPreLevel > 0 || mDoRaw) {
+  if (mDoRaw || PreLevel() > 0) {
     AppendToStringConvertLF(data, aStr);
   }
   else if (mDoFormat) {
     AppendToStringFormatedWrapped(data, aStr);
   }
   else if (mDoWrap) {
     AppendToStringWrapped(data, aStr);
   }
@@ -530,18 +532,19 @@ nsXHTMLContentSerializer::CheckElementSt
   // even if we're not in pretty printing mode
   aForceFormat = !(mFlags & nsIDocumentEncoder::OutputIgnoreMozDirty) &&
                  aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty);
 
   nsIAtom *name = aContent->Tag();
   int32_t namespaceID = aContent->GetNameSpaceID();
 
   if (namespaceID == kNameSpaceID_XHTML) {
-    if (name == nsGkAtoms::br && mPreLevel > 0 && 
-        (mFlags & nsIDocumentEncoder::OutputNoFormattingInPre)) {
+    if (name == nsGkAtoms::br &&
+        (mFlags & nsIDocumentEncoder::OutputNoFormattingInPre) &&
+        PreLevel() > 0) {
       AppendNewLineToString(aStr);
       return false;
     }
 
     if (name == nsGkAtoms::body) {
       ++mInBody;
     }
   }
@@ -838,51 +841,70 @@ nsXHTMLContentSerializer::LineBreakAfter
 
   return false;
 }
 
 
 void
 nsXHTMLContentSerializer::MaybeEnterInPreContent(nsIContent* aNode)
 {
-
-  if (aNode->GetNameSpaceID() != kNameSpaceID_XHTML) {
+  if (!ShouldMaintainPreLevel() ||
+      aNode->GetNameSpaceID() != kNameSpaceID_XHTML) {
     return;
   }
 
   nsIAtom *name = aNode->Tag();
 
-  if (name == nsGkAtoms::pre ||
+  if (IsElementPreformatted(aNode) ||
       name == nsGkAtoms::script ||
       name == nsGkAtoms::style ||
       name == nsGkAtoms::noscript ||
       name == nsGkAtoms::noframes
       ) {
-    mPreLevel++;
+    PreLevel()++;
   }
 }
 
 void
 nsXHTMLContentSerializer::MaybeLeaveFromPreContent(nsIContent* aNode)
 {
-  if (aNode->GetNameSpaceID() != kNameSpaceID_XHTML) {
+  if (!ShouldMaintainPreLevel() ||
+      aNode->GetNameSpaceID() != kNameSpaceID_XHTML) {
     return;
   }
 
   nsIAtom *name = aNode->Tag();
-  if (name == nsGkAtoms::pre ||
+  if (IsElementPreformatted(aNode) ||
       name == nsGkAtoms::script ||
       name == nsGkAtoms::style ||
       name == nsGkAtoms::noscript ||
       name == nsGkAtoms::noframes
     ) {
-    --mPreLevel;
+    --PreLevel();
   }
 }
 
+bool
+nsXHTMLContentSerializer::IsElementPreformatted(nsIContent* aNode)
+{
+  MOZ_ASSERT(ShouldMaintainPreLevel(), "We should not be calling this needlessly");
+
+  if (!aNode->IsElement()) {
+    return false;
+  }
+  nsRefPtr<nsStyleContext> styleContext =
+    nsComputedDOMStyle::GetStyleContextForElementNoFlush(aNode->AsElement(),
+                                                         nullptr, nullptr);
+  if (styleContext) {
+    const nsStyleText* textStyle = styleContext->StyleText();
+    return textStyle->WhiteSpaceOrNewlineIsSignificant();
+  }
+  return false;
+}
+
 void 
 nsXHTMLContentSerializer::SerializeLIValueAttribute(nsIContent* aElement,
                                                     nsAString& aStr)
 {
   // We are copying and we are at the "first" LI node of OL in selected range.
   // It may not be the first LI child of OL but it's first in the selected range.
   // Note that we get into this condition only once per a OL.
   bool found = false;
--- a/dom/base/nsXHTMLContentSerializer.h
+++ b/dom/base/nsXHTMLContentSerializer.h
@@ -88,16 +88,20 @@ class nsXHTMLContentSerializer : public 
   bool IsShorthandAttr(const nsIAtom* aAttrName,
                          const nsIAtom* aElementName);
   virtual void AppendAndTranslateEntities(const nsAString& aStr,
                                           nsAString& aOutputStr) MOZ_OVERRIDE;
   nsresult EscapeURI(nsIContent* aContent,
                      const nsAString& aURI,
                      nsAString& aEscapedURI);
 
+private:
+  bool IsElementPreformatted(nsIContent* aNode);
+
+protected:
   nsCOMPtr<nsIEntityConverter> mEntityConverter;
 
   /*
    * isHTMLParser should be set to true by the HTML parser which inherits from
    * this class. It avoids to redefine methods just for few changes.
    */
   bool          mIsHTMLSerializer;
 
--- a/dom/base/nsXMLContentSerializer.cpp
+++ b/dom/base/nsXMLContentSerializer.cpp
@@ -182,17 +182,17 @@ nsXMLContentSerializer::AppendText(nsICo
 
   nsAutoString data;
   nsresult rv;
 
   rv = AppendTextData(aText, aStartOffset, aEndOffset, data, true);
   if (NS_FAILED(rv))
     return NS_ERROR_FAILURE;
 
-  if (mPreLevel > 0 || mDoRaw) {
+  if (mDoRaw || PreLevel() > 0) {
     AppendToStringConvertLF(data, aStr);
   }
   else if (mDoFormat) {
     AppendToStringFormatedWrapped(data, aStr);
   }
   else if (mDoWrap) {
     AppendToStringWrapped(data, aStr);
   }
@@ -209,17 +209,17 @@ nsXMLContentSerializer::AppendCDATASecti
                                            int32_t aEndOffset,
                                            nsAString& aStr)
 {
   NS_ENSURE_ARG(aCDATASection);
   nsresult rv;
 
   NS_NAMED_LITERAL_STRING(cdata , "<![CDATA[");
 
-  if (mPreLevel > 0 || mDoRaw) {
+  if (mDoRaw || PreLevel() > 0) {
     AppendToString(cdata, aStr);
   }
   else if (mDoFormat) {
     AppendToStringFormatedWrapped(cdata, aStr);
   }
   else if (mDoWrap) {
     AppendToStringWrapped(cdata, aStr);
   }
@@ -255,17 +255,17 @@ nsXMLContentSerializer::AppendProcessing
   if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
 
   rv = pi->GetData(data);
   if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
 
   start.AppendLiteral("<?");
   start.Append(target);
 
-  if (mPreLevel > 0 || mDoRaw) {
+  if (mDoRaw || PreLevel() > 0) {
     AppendToString(start, aStr);
   }
   else if (mDoFormat) {
     if (mAddSpace) {
       AppendNewLineToString(aStr);
     }
     AppendToStringFormatedWrapped(start, aStr);
   }
@@ -313,17 +313,17 @@ nsXMLContentSerializer::AppendComment(ns
     }
     data.Assign(frag);
   }
 
   MaybeAddNewlineForRootNode(aStr);
 
   NS_NAMED_LITERAL_STRING(startComment, "<!--");
 
-  if (mPreLevel > 0 || mDoRaw) {
+  if (mDoRaw || PreLevel() > 0) {
     AppendToString(startComment, aStr);
   }
   else if (mDoFormat) {
     if (mAddSpace) {
       AppendNewLineToString(aStr);
     }
     AppendToStringFormatedWrapped(startComment, aStr);
   }
@@ -688,17 +688,17 @@ nsXMLContentSerializer::SerializeAttr(co
                             NS_LITERAL_STRING("&amp;"));
     if (bIncludesDouble && bIncludesSingle) {
       sValue.ReplaceSubstring(NS_LITERAL_STRING("\""),
                               NS_LITERAL_STRING("&quot;"));
     }
     attrString.Append(sValue);
     attrString.Append(cDelimiter);
   }
-  if (mPreLevel > 0 || mDoRaw) {
+  if (mDoRaw || PreLevel() > 0) {
     AppendToStringConvertLF(attrString, aStr);
   }
   else if (mDoFormat) {
     AppendToStringFormatedWrapped(attrString, aStr);
   }
   else if (mDoWrap) {
     AppendToStringWrapped(attrString, aStr);
   }
@@ -893,17 +893,17 @@ nsXMLContentSerializer::AppendElementSta
   aElement->NodeInfo()->GetNamespaceURI(tagNamespaceURI);
 
   uint32_t skipAttr = ScanNamespaceDeclarations(content,
                           aOriginalElement, tagNamespaceURI);
 
   nsIAtom *name = content->Tag();
   bool lineBreakBeforeOpen = LineBreakBeforeOpen(content->GetNameSpaceID(), name);
 
-  if ((mDoFormat || forceFormat) && !mPreLevel && !mDoRaw) {
+  if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel()) {
     if (mColPos && lineBreakBeforeOpen) {
       AppendNewLineToString(aStr);
     }
     else {
       MaybeAddNewlineForRootNode(aStr);
     }
     if (!mColPos) {
       AppendIndentation(aStr);
@@ -934,28 +934,28 @@ nsXMLContentSerializer::AppendElementSta
   if (!tagPrefix.IsEmpty()) {
     AppendToString(tagPrefix, aStr);
     AppendToString(NS_LITERAL_STRING(":"), aStr);
   }
   AppendToString(tagLocalName, aStr);
 
   MaybeEnterInPreContent(content);
 
-  if ((mDoFormat || forceFormat) && !mPreLevel && !mDoRaw) {
+  if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel()) {
     IncrIndentation(name);
   }
 
   SerializeAttributes(content, aOriginalElement, tagPrefix, tagNamespaceURI,
                       name, aStr, skipAttr, addNSAttr);
 
   AppendEndOfElementStart(aOriginalElement, name, content->GetNameSpaceID(),
                           aStr);
 
-  if ((mDoFormat || forceFormat) && !mPreLevel 
-    && !mDoRaw && LineBreakAfterOpen(content->GetNameSpaceID(), name)) {
+  if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel()
+    && LineBreakAfterOpen(content->GetNameSpaceID(), name)) {
     AppendNewLineToString(aStr);
   }
 
   AfterElementStart(content, aOriginalElement, aStr);
 
   return NS_OK;
 }
 
@@ -982,17 +982,17 @@ nsXMLContentSerializer::AppendElementEnd
 
   nsIContent* content = aElement;
 
   bool forceFormat = false, outputElementEnd;
   outputElementEnd = CheckElementEnd(content, forceFormat, aStr);
 
   nsIAtom *name = content->Tag();
 
-  if ((mDoFormat || forceFormat) && !mPreLevel && !mDoRaw) {
+  if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel()) {
     DecrIndentation(name);
   }
 
   if (!outputElementEnd) {
     PopNameSpaceDeclsFor(aElement);
     MaybeFlagNewlineForRootNode(aElement);
     return NS_OK;
   }
@@ -1004,17 +1004,17 @@ nsXMLContentSerializer::AppendElementEnd
   aElement->NodeInfo()->GetNamespaceURI(tagNamespaceURI);
 
 #ifdef DEBUG
   bool debugNeedToPushNamespace =
 #endif
   ConfirmPrefix(tagPrefix, tagNamespaceURI, aElement, false);
   NS_ASSERTION(!debugNeedToPushNamespace, "Can't push namespaces in closing tag!");
 
-  if ((mDoFormat || forceFormat) && !mPreLevel && !mDoRaw) {
+  if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel()) {
 
     bool lineBreakBeforeClose = LineBreakBeforeClose(content->GetNameSpaceID(), name);
 
     if (mColPos && lineBreakBeforeClose) {
       AppendNewLineToString(aStr);
     }
     if (!mColPos) {
       AppendIndentation(aStr);
@@ -1036,18 +1036,18 @@ nsXMLContentSerializer::AppendElementEnd
   }
   AppendToString(tagLocalName, aStr);
   AppendToString(kGreaterThan, aStr);
 
   PopNameSpaceDeclsFor(aElement);
 
   MaybeLeaveFromPreContent(content);
 
-  if ((mDoFormat || forceFormat) && !mPreLevel
-      && !mDoRaw && LineBreakAfterClose(content->GetNameSpaceID(), name)) {
+  if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel()
+      && LineBreakAfterClose(content->GetNameSpaceID(), name)) {
     AppendNewLineToString(aStr);
   }
   else {
     MaybeFlagNewlineForRootNode(aElement);
   }
 
   AfterElementEnd(content, aStr);
 
@@ -1212,33 +1212,35 @@ nsXMLContentSerializer::MaybeFlagNewline
     mAddNewlineForRootNode = parent->IsNodeOfType(nsINode::eDOCUMENT);
   }
 }
 
 void
 nsXMLContentSerializer::MaybeEnterInPreContent(nsIContent* aNode)
 {
   // support of the xml:space attribute
-  if (aNode->HasAttr(kNameSpaceID_XML, nsGkAtoms::space)) {
+  if (ShouldMaintainPreLevel() &&
+      aNode->HasAttr(kNameSpaceID_XML, nsGkAtoms::space)) {
     nsAutoString space;
     aNode->GetAttr(kNameSpaceID_XML, nsGkAtoms::space, space);
     if (space.EqualsLiteral("preserve"))
-      ++mPreLevel;
+      ++PreLevel();
   }
 }
 
 void
 nsXMLContentSerializer::MaybeLeaveFromPreContent(nsIContent* aNode)
 {
   // support of the xml:space attribute
-  if (aNode->HasAttr(kNameSpaceID_XML, nsGkAtoms::space)) {
+  if (ShouldMaintainPreLevel() &&
+      aNode->HasAttr(kNameSpaceID_XML, nsGkAtoms::space)) {
     nsAutoString space;
     aNode->GetAttr(kNameSpaceID_XML, nsGkAtoms::space, space);
     if (space.EqualsLiteral("preserve"))
-      --mPreLevel;
+      --PreLevel();
   }
 }
 
 void
 nsXMLContentSerializer::AppendNewLineToString(nsAString& aStr)
 {
   AppendToString(mLineBreak, aStr);
   mMayIgnoreLineBreakSequence = true;
@@ -1437,17 +1439,17 @@ nsXMLContentSerializer::AppendWrapped_No
   uint32_t length, colPos;
 
   do {
 
     if (mColPos) {
       colPos = mColPos;
     }
     else {
-      if (mDoFormat && !mPreLevel && !onceAgainBecauseWeAddedBreakInFront) {
+      if (mDoFormat && !mDoRaw && !PreLevel() && !onceAgainBecauseWeAddedBreakInFront) {
         colPos = mIndent.Length();
       }
       else
         colPos = 0;
     }
     foundWhitespaceInLoop = false;
     length = 0;
     // we iterate until the next whitespace character
@@ -1706,8 +1708,15 @@ nsXMLContentSerializer::AppendToStringWr
       AppendWrapped_WhitespaceSequence(pos, end, sequenceStart, aOutputStr);
     }
     else { // any other non-whitespace char
       AppendWrapped_NonWhitespaceSequence(pos, end, sequenceStart,
         mayIgnoreStartOfLineWhitespaceSequence, sequenceStartAfterAWhitespace, aOutputStr);
     }
   }
 }
+
+bool
+nsXMLContentSerializer::ShouldMaintainPreLevel() const
+{
+  // Only attempt to maintain the pre level for consumers who care about it.
+  return !mDoRaw || (mFlags & nsIDocumentEncoder::OutputNoFormattingInPre);
+}
--- a/dom/base/nsXMLContentSerializer.h
+++ b/dom/base/nsXMLContentSerializer.h
@@ -289,16 +289,26 @@ class nsXMLContentSerializer : public ns
   // the root of a document. See mAddNewlineForRootNode
   void MaybeAddNewlineForRootNode(nsAString& aStr);
   void MaybeFlagNewlineForRootNode(nsINode* aNode);
 
   // Functions to check if we enter in or leave from a preformated content
   virtual void MaybeEnterInPreContent(nsIContent* aNode);
   virtual void MaybeLeaveFromPreContent(nsIContent* aNode);
 
+  bool ShouldMaintainPreLevel() const;
+  int32_t PreLevel() const {
+    MOZ_ASSERT(ShouldMaintainPreLevel());
+    return mPreLevel;
+  }
+  int32_t& PreLevel() {
+    MOZ_ASSERT(ShouldMaintainPreLevel());
+    return mPreLevel;
+  }
+
   int32_t mPrefixIndex;
 
   struct NameSpaceDecl {
     nsString mPrefix;
     nsString mURI;
     nsIContent* mOwner;
   };
 
@@ -356,16 +366,17 @@ class nsXMLContentSerializer : public ns
   // says that if the next string to add contains a newline character at the
   // begining, then this newline character should be ignored, because a
   // such character has already been added into the output string
   bool          mMayIgnoreLineBreakSequence;
 
   bool          mBodyOnly;
   int32_t       mInBody;
 
+private:
   // number of nested elements which have preformated content
   int32_t       mPreLevel;
 };
 
 nsresult
 NS_NewXMLContentSerializer(nsIContentSerializer** aSerializer);
 
 #endif 
--- a/dom/base/test/TestPlainTextSerializer.cpp
+++ b/dom/base/test/TestPlainTextSerializer.cpp
@@ -110,16 +110,64 @@ TestPrettyPrintedHtml()
     return NS_ERROR_FAILURE;
   }
 
   passed("prettyprinted HTML to text serialization test");
   return NS_OK;
 }
 
 nsresult
+TestPreElement()
+{
+  nsString test;
+  test.AppendLiteral(
+    "<html>" NS_LINEBREAK
+    "<body>" NS_LINEBREAK
+    "<pre>" NS_LINEBREAK
+    "  first" NS_LINEBREAK
+    "  second" NS_LINEBREAK
+    "</pre>" NS_LINEBREAK
+    "</body>" NS_LINEBREAK "</html>");
+
+  ConvertBufToPlainText(test, 0);
+  if (!test.EqualsLiteral("  first" NS_LINEBREAK "  second" NS_LINEBREAK NS_LINEBREAK)) {
+    fail("Wrong prettyprinted html to text serialization");
+    return NS_ERROR_FAILURE;
+  }
+
+  passed("prettyprinted HTML to text serialization test");
+  return NS_OK;
+}
+
+nsresult
+TestBlockElement()
+{
+  nsString test;
+  test.AppendLiteral(
+    "<html>" NS_LINEBREAK
+    "<body>" NS_LINEBREAK
+    "<div>" NS_LINEBREAK
+    "  first" NS_LINEBREAK
+    "</div>" NS_LINEBREAK
+    "<div>" NS_LINEBREAK
+    "  second" NS_LINEBREAK
+    "</div>" NS_LINEBREAK
+    "</body>" NS_LINEBREAK "</html>");
+
+  ConvertBufToPlainText(test, 0);
+  if (!test.EqualsLiteral("first" NS_LINEBREAK "second" NS_LINEBREAK)) {
+    fail("Wrong prettyprinted html to text serialization");
+    return NS_ERROR_FAILURE;
+  }
+
+  passed("prettyprinted HTML to text serialization test");
+  return NS_OK;
+}
+
+nsresult
 TestPlainTextSerializer()
 {
   nsString test;
   test.AppendLiteral("<html><base>base</base><head><span>span</span></head>"
                      "<body>body</body></html>");
   ConvertBufToPlainText(test, 0);
   if (!test.EqualsLiteral("basespanbody")) {
     fail("Wrong html to text serialization");
@@ -132,16 +180,22 @@ TestPlainTextSerializer()
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = TestCJKWithFlowedDelSp();
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = TestPrettyPrintedHtml();
   NS_ENSURE_SUCCESS(rv, rv);
 
+  rv = TestPreElement();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = TestBlockElement();
+  NS_ENSURE_SUCCESS(rv, rv);
+
   // Add new tests here...
   return NS_OK;
 }
 
 int main(int argc, char** argv)
 {
   ScopedXPCOM xpcom("PlainTextSerializer");
   if (xpcom.failed())
--- a/dom/base/test/copypaste.js
+++ b/dom/base/test/copypaste.js
@@ -155,27 +155,27 @@ function testCopyPaste (isXHTML) {
   }
   else {
     testClipboardValue("text/html", "<div id=\"div4\">\n  T<textarea>t t t</textarea>\n</div>");
     testInnerHTML("div4", "\n  T<textarea>t t t</textarea>\n");
   }
   testPasteText(" Tt t t ");
 
   copyChildrenToClipboard("div5");
-  testSelectionToString(" T ");
-  testClipboardValue("text/unicode", " T ");
+  testSelectionToString(" T     ");
+  testClipboardValue("text/unicode", " T     ");
   if (isXHTML) {
     testClipboardValue("text/html", "<div id=\"div5\">\n  T<textarea xmlns=\"http://www.w3.org/1999/xhtml\">     </textarea>\n</div>");
     testInnerHTML("div5", "\n  T<textarea xmlns=\"http://www.w3.org/1999/xhtml\">     </textarea>\n");
   }
   else {
     testClipboardValue("text/html", "<div id=\"div5\">\n  T<textarea>     </textarea>\n</div>");
     testInnerHTML("div5", "\n  T<textarea>     </textarea>\n");
   }
-  testPasteText(" T ");
+  testPasteText(" T     ");
 
   copyRangeToClipboard($("div6").childNodes[0],0, $("div6").childNodes[1],1,suppressUnicodeCheckIfHidden);
   testSelectionToString("");
 // START Disabled due to bug 564688
 if (false) {
   testClipboardValue("text/unicode", "");
   testClipboardValue("text/html", "");
 }
--- a/dom/base/test/file_htmlserializer_1_bodyonly.html
+++ b/dom/base/test/file_htmlserializer_1_bodyonly.html
@@ -28,16 +28,19 @@ var d = a < b && a > c;
 <ol><li>Fusce
  a ipsum</li><li> non lacus posuere aliquet.</li><li> Sed fermentum posuere nulla</li><li> Donec tempor.</li></ol>
 Donec sollicitudin tortor 
 <!-- test on 
 comments -->
 <pre>lacinia <em>libero</em> ullamcorper laoreet.<br>
  Cras quis<br>
  nisi at odio<br>
- consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at pharetra rutrum, <br>
+ consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non 
+urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci 
+luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at 
+pharetra rutrum, <br>
 lacus risus pulvinar ante.
 </pre>
 ut gravida eros leo ut libero
 <p></p>
 <noscript>
 <p>Curabitur consectetuer urna a sem. Nunc & non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus</p></noscript>
 <p>Nam eu sapien. Sed viverra lacus. Donec quis ipsum. Nunc cursus aliquet lectus. Nunc vitae eros. Class aptent taciti</p></body>
\ No newline at end of file
--- a/dom/base/test/file_htmlserializer_1_format.html
+++ b/dom/base/test/file_htmlserializer_1_format.html
@@ -37,21 +37,24 @@ var d = a < b && a > c;
       <li> non lacus posuere aliquet.</li>
       <li> Sed fermentum posuere nulla</li>
       <li> Donec tempor.</li>
     </ol>
     Donec sollicitudin tortor
     <!-- test on 
 comments -->
     <pre>lacinia <em>libero</em> ullamcorper laoreet.<br>
- Cras quis<br>
- nisi at odio<br>
- consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at pharetra rutrum, <br>
-lacus risus pulvinar ante.
-</pre>
+      Cras quis<br>
+      nisi at odio<br>
+      consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non
+      urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci
+      luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla
+      at pharetra rutrum, <br>
+      lacus risus pulvinar ante.
+    </pre>
     ut gravida eros leo ut libero
     <p></p>
     <noscript>
 <p>Curabitur consectetuer urna a sem. Nunc & non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus</p></noscript>
     <p>Nam eu sapien. Sed viverra lacus. Donec quis ipsum. Nunc cursus
       aliquet lectus. Nunc vitae eros. Class aptent taciti</p>
   </body>
 </html>
--- a/dom/base/test/file_htmlserializer_1_linebreak.html
+++ b/dom/base/test/file_htmlserializer_1_linebreak.html
@@ -32,16 +32,19 @@ var d = a < b && a > c;
 <ol><li>Fusce
  a ipsum</li><li> non lacus posuere aliquet.</li><li> Sed fermentum posuere nulla</li><li> Donec tempor.</li></ol>
 Donec sollicitudin tortor 
 <!-- test on 
 comments -->
 <pre>lacinia <em>libero</em> ullamcorper laoreet.<br>
  Cras quis<br>
  nisi at odio<br>
- consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at pharetra rutrum, <br>
+ consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non 
+urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci 
+luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at 
+pharetra rutrum, <br>
 lacus risus pulvinar ante.
 </pre>
 ut gravida eros leo ut libero
 <p></p>
 <noscript>
 <p>Curabitur consectetuer urna a sem. Nunc & non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus</p></noscript>
 <p>Nam eu sapien. Sed viverra lacus. Donec quis ipsum. Nunc cursus aliquet lectus. Nunc vitae eros. Class aptent taciti</p></body></html>
\ No newline at end of file
--- a/dom/base/test/file_htmlserializer_1_links.html
+++ b/dom/base/test/file_htmlserializer_1_links.html
@@ -32,16 +32,19 @@ var d = a < b && a > c;
 <ol><li>Fusce
  a ipsum</li><li> non lacus posuere aliquet.</li><li> Sed fermentum posuere nulla</li><li> Donec tempor.</li></ol>
 Donec sollicitudin tortor 
 <!-- test on 
 comments -->
 <pre>lacinia <em>libero</em> ullamcorper laoreet.<br>
  Cras quis<br>
  nisi at odio<br>
- consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at pharetra rutrum, <br>
+ consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non 
+urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci 
+luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at 
+pharetra rutrum, <br>
 lacus risus pulvinar ante.
 </pre>
 ut gravida eros leo ut libero
 <p></p>
 <noscript>
 <p>Curabitur consectetuer urna a sem. Nunc & non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus</p></noscript>
 <p>Nam eu sapien. Sed viverra lacus. Donec quis ipsum. Nunc cursus aliquet lectus. Nunc vitae eros. Class aptent taciti</p></body></html>
--- a/dom/base/test/file_htmlserializer_1_nested_body.html
+++ b/dom/base/test/file_htmlserializer_1_nested_body.html
@@ -32,16 +32,19 @@ var d = a < b && a > c;
 <ol><li>Fusce
  a ipsum</li><li> non lacus posuere aliquet.</li><li> Sed fermentum posuere nulla</li><li> Donec tempor.</li></ol>
 Donec sollicitudin tortor 
 <!-- test on 
 comments -->
 <pre>lacinia <em>libero</em> ullamcorper laoreet.<br>
  Cras quis<br>
  nisi at odio<br>
- consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at pharetra rutrum, <br>
+ consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non 
+urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci 
+luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at 
+pharetra rutrum, <br>
 lacus risus pulvinar ante.
 </pre>
 ut gravida eros leo ut libero
 <p></p>
 <noscript>
 <p>Curabitur consectetuer urna a sem. Nunc & non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus</p></noscript>
 <p>Nam eu sapien. Sed viverra lacus. Donec quis ipsum. Nunc cursus aliquet lectus. Nunc vitae eros. Class aptent taciti</p><body><p>this is an other body element</p></body></body></html>
\ No newline at end of file
--- a/dom/base/test/file_htmlserializer_1_noflag.html
+++ b/dom/base/test/file_htmlserializer_1_noflag.html
@@ -32,16 +32,19 @@ var d = a < b && a > c;
 <ol><li>Fusce
  a ipsum</li><li> non lacus posuere aliquet.</li><li> Sed fermentum posuere nulla</li><li> Donec tempor.</li></ol>
 Donec sollicitudin tortor 
 <!-- test on 
 comments -->
 <pre>lacinia <em>libero</em> ullamcorper laoreet.<br>
  Cras quis<br>
  nisi at odio<br>
- consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at pharetra rutrum, <br>
+ consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non 
+urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci 
+luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at 
+pharetra rutrum, <br>
 lacus risus pulvinar ante.
 </pre>
 ut gravida eros leo ut libero
 <p></p>
 <noscript>
 <p>Curabitur consectetuer urna a sem. Nunc & non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus</p></noscript>
 <p>Nam eu sapien. Sed viverra lacus. Donec quis ipsum. Nunc cursus aliquet lectus. Nunc vitae eros. Class aptent taciti</p></body></html>
\ No newline at end of file
--- a/dom/base/test/file_htmlserializer_1_noformatpre.html
+++ b/dom/base/test/file_htmlserializer_1_noformatpre.html
@@ -29,23 +29,22 @@ var a=3, b=4, c=7;
 var d = a < b && a > c;
 </script>
 
 <ol><li>Fusce
  a ipsum</li><li> non lacus posuere aliquet.</li><li> Sed fermentum posuere nulla</li><li> Donec tempor.</li></ol>
 Donec sollicitudin tortor 
 <!-- test on 
 comments -->
-<pre>lacinia <em>libero</em> ullamcorper laoreet.
-
- Cras quis
-
- nisi at odio
-
- consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at pharetra rutrum, 
-
+<pre>lacinia <em>libero</em> ullamcorper laoreet.<br>
+ Cras quis<br>
+ nisi at odio<br>
+ consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non 
+urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci 
+luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at 
+pharetra rutrum, <br>
 lacus risus pulvinar ante.
 </pre>
 ut gravida eros leo ut libero
 <p></p>
 <noscript>
 <p>Curabitur consectetuer urna a sem. Nunc & non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus</p></noscript>
 <p>Nam eu sapien. Sed viverra lacus. Donec quis ipsum. Nunc cursus aliquet lectus. Nunc vitae eros. Class aptent taciti</p></body></html>
\ No newline at end of file
--- a/dom/base/test/file_htmlserializer_1_sibling_body.html
+++ b/dom/base/test/file_htmlserializer_1_sibling_body.html
@@ -32,16 +32,19 @@ var d = a < b && a > c;
 <ol><li>Fusce
  a ipsum</li><li> non lacus posuere aliquet.</li><li> Sed fermentum posuere nulla</li><li> Donec tempor.</li></ol>
 Donec sollicitudin tortor 
 <!-- test on 
 comments -->
 <pre>lacinia <em>libero</em> ullamcorper laoreet.<br>
  Cras quis<br>
  nisi at odio<br>
- consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at pharetra rutrum, <br>
+ consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non 
+urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci 
+luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at 
+pharetra rutrum, <br>
 lacus risus pulvinar ante.
 </pre>
 ut gravida eros leo ut libero
 <p></p>
 <noscript>
 <p>Curabitur consectetuer urna a sem. Nunc & non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus</p></noscript>
 <p>Nam eu sapien. Sed viverra lacus. Donec quis ipsum. Nunc cursus aliquet lectus. Nunc vitae eros. Class aptent taciti</p></body></html>
\ No newline at end of file
--- a/dom/base/test/file_htmlserializer_1_sibling_body_only_body.html
+++ b/dom/base/test/file_htmlserializer_1_sibling_body_only_body.html
@@ -28,16 +28,19 @@ var d = a < b && a > c;
 <ol><li>Fusce
  a ipsum</li><li> non lacus posuere aliquet.</li><li> Sed fermentum posuere nulla</li><li> Donec tempor.</li></ol>
 Donec sollicitudin tortor 
 <!-- test on 
 comments -->
 <pre>lacinia <em>libero</em> ullamcorper laoreet.<br>
  Cras quis<br>
  nisi at odio<br>
- consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at pharetra rutrum, <br>
+ consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non 
+urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci 
+luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at 
+pharetra rutrum, <br>
 lacus risus pulvinar ante.
 </pre>
 ut gravida eros leo ut libero
 <p></p>
 <noscript>
 <p>Curabitur consectetuer urna a sem. Nunc & non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus</p></noscript>
 <p>Nam eu sapien. Sed viverra lacus. Donec quis ipsum. Nunc cursus aliquet lectus. Nunc vitae eros. Class aptent taciti</p></body>
\ No newline at end of file
--- a/dom/base/test/file_htmlserializer_1_wrap.html
+++ b/dom/base/test/file_htmlserializer_1_wrap.html
@@ -36,17 +36,20 @@ var d = a < b && a > c;
  a ipsum</li><li> non lacus posuere aliquet.</li><li> Sed fermentum 
 posuere nulla</li><li> Donec tempor.</li></ol>
 Donec sollicitudin tortor 
 <!-- test on 
 comments -->
 <pre>lacinia <em>libero</em> ullamcorper laoreet.<br>
  Cras quis<br>
  nisi at odio<br>
- consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at pharetra rutrum, <br>
+ consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non 
+urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci 
+luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at 
+pharetra rutrum, <br>
 lacus risus pulvinar ante.
 </pre>
 ut gravida eros leo ut libero
 <p></p>
 <noscript>
 <p>Curabitur consectetuer urna a sem. Nunc & non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus</p></noscript>
 <p>Nam eu sapien. Sed viverra lacus. Donec quis ipsum. Nunc cursus 
 aliquet lectus. Nunc vitae eros. Class aptent taciti</p></body></html>
\ No newline at end of file
--- a/dom/base/test/file_xhtmlserializer_1_bodyonly.xhtml
+++ b/dom/base/test/file_xhtmlserializer_1_bodyonly.xhtml
@@ -30,17 +30,20 @@ var d = a < b && a > c;
 <ol><li>Fusce
  a ipsum</li><li> non lacus posuere aliquet.</li><li> Sed fermentum posuere nulla</li><li> Donec tempor.</li></ol>
 Donec sollicitudin tortor 
 <!-- test on 
 comments -->
 <pre>lacinia <em>libero</em> ullamcorper laoreet.<br />
  Cras quis<br />
  nisi at odio<br />
- consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at pharetra rutrum, <br />
+ consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non 
+urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci 
+luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at 
+pharetra rutrum, <br />
 lacus risus pulvinar ante.
 </pre>
 ut gravida eros <br />leo ut libero
 <!-- empty element: end tag should be generated for backward compatibility with HTML -->
 <p></p>
 <noscript>
 <p>Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus</p></noscript>
 <p>Nam eu sapien. Sed viverra lacus. Donec quis ipsum. Nunc cursus aliquet lectus. Nunc vitae eros. Class aptent taciti</p>
--- a/dom/base/test/file_xhtmlserializer_1_format.xhtml
+++ b/dom/base/test/file_xhtmlserializer_1_format.xhtml
@@ -40,21 +40,24 @@ var d = a < b && a > c;
       <li> non lacus posuere aliquet.</li>
       <li> Sed fermentum posuere nulla</li>
       <li> Donec tempor.</li>
     </ol>
     Donec sollicitudin tortor
     <!-- test on 
 comments -->
     <pre>lacinia <em>libero</em> ullamcorper laoreet.<br />
- Cras quis<br />
- nisi at odio<br />
- consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at pharetra rutrum, <br />
-lacus risus pulvinar ante.
-</pre>
+      Cras quis<br />
+      nisi at odio<br />
+      consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non
+      urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci
+      luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla
+      at pharetra rutrum, <br />
+      lacus risus pulvinar ante.
+    </pre>
     ut gravida eros <br />
     leo ut libero
     <!-- empty element: end tag should be generated for backward compatibility with HTML -->
     <p></p>
     <noscript>
 <p>Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus</p></noscript>
     <p>Nam eu sapien. Sed viverra lacus. Donec quis ipsum. Nunc cursus
       aliquet lectus. Nunc vitae eros. Class aptent taciti</p>
--- a/dom/base/test/file_xhtmlserializer_1_linebreak.xhtml
+++ b/dom/base/test/file_xhtmlserializer_1_linebreak.xhtml
@@ -38,17 +38,20 @@ var d = a < b && a > c;
 <ol><li>Fusce
  a ipsum</li><li> non lacus posuere aliquet.</li><li> Sed fermentum posuere nulla</li><li> Donec tempor.</li></ol>
 Donec sollicitudin tortor 
 <!-- test on 
 comments -->
 <pre>lacinia <em>libero</em> ullamcorper laoreet.<br />
  Cras quis<br />
  nisi at odio<br />
- consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at pharetra rutrum, <br />
+ consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non 
+urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci 
+luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at 
+pharetra rutrum, <br />
 lacus risus pulvinar ante.
 </pre>
 ut gravida eros <br />leo ut libero
 <!-- empty element: end tag should be generated for backward compatibility with HTML -->
 <p></p>
 <noscript>
 <p>Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus</p></noscript>
 <p>Nam eu sapien. Sed viverra lacus. Donec quis ipsum. Nunc cursus aliquet lectus. Nunc vitae eros. Class aptent taciti</p>
--- a/dom/base/test/file_xhtmlserializer_1_links.xhtml
+++ b/dom/base/test/file_xhtmlserializer_1_links.xhtml
@@ -38,17 +38,20 @@ var d = a < b && a > c;
 <ol><li>Fusce
  a ipsum</li><li> non lacus posuere aliquet.</li><li> Sed fermentum posuere nulla</li><li> Donec tempor.</li></ol>
 Donec sollicitudin tortor 
 <!-- test on 
 comments -->
 <pre>lacinia <em>libero</em> ullamcorper laoreet.<br />
  Cras quis<br />
  nisi at odio<br />
- consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at pharetra rutrum, <br />
+ consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non 
+urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci 
+luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at 
+pharetra rutrum, <br />
 lacus risus pulvinar ante.
 </pre>
 ut gravida eros <br />leo ut libero
 <!-- empty element: end tag should be generated for backward compatibility with HTML -->
 <p></p>
 <noscript>
 <p>Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus</p></noscript>
 <p>Nam eu sapien. Sed viverra lacus. Donec quis ipsum. Nunc cursus aliquet lectus. Nunc vitae eros. Class aptent taciti</p>
--- a/dom/base/test/file_xhtmlserializer_1_nested_body.xhtml
+++ b/dom/base/test/file_xhtmlserializer_1_nested_body.xhtml
@@ -38,17 +38,20 @@ var d = a < b && a > c;
 <ol><li>Fusce
  a ipsum</li><li> non lacus posuere aliquet.</li><li> Sed fermentum posuere nulla</li><li> Donec tempor.</li></ol>
 Donec sollicitudin tortor 
 <!-- test on 
 comments -->
 <pre>lacinia <em>libero</em> ullamcorper laoreet.<br />
  Cras quis<br />
  nisi at odio<br />
- consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at pharetra rutrum, <br />
+ consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non 
+urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci 
+luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at 
+pharetra rutrum, <br />
 lacus risus pulvinar ante.
 </pre>
 ut gravida eros <br />leo ut libero
 <!-- empty element: end tag should be generated for backward compatibility with HTML -->
 <p></p>
 <noscript>
 <p>Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus</p></noscript>
 <p>Nam eu sapien. Sed viverra lacus. Donec quis ipsum. Nunc cursus aliquet lectus. Nunc vitae eros. Class aptent taciti</p>
--- a/dom/base/test/file_xhtmlserializer_1_noflag.xhtml
+++ b/dom/base/test/file_xhtmlserializer_1_noflag.xhtml
@@ -38,17 +38,20 @@ var d = a < b && a > c;
 <ol><li>Fusce
  a ipsum</li><li> non lacus posuere aliquet.</li><li> Sed fermentum posuere nulla</li><li> Donec tempor.</li></ol>
 Donec sollicitudin tortor 
 <!-- test on 
 comments -->
 <pre>lacinia <em>libero</em> ullamcorper laoreet.<br />
  Cras quis<br />
  nisi at odio<br />
- consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at pharetra rutrum, <br />
+ consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non 
+urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci 
+luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at 
+pharetra rutrum, <br />
 lacus risus pulvinar ante.
 </pre>
 ut gravida eros <br />leo ut libero
 <!-- empty element: end tag should be generated for backward compatibility with HTML -->
 <p></p>
 <noscript>
 <p>Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus</p></noscript>
 <p>Nam eu sapien. Sed viverra lacus. Donec quis ipsum. Nunc cursus aliquet lectus. Nunc vitae eros. Class aptent taciti</p>
--- a/dom/base/test/file_xhtmlserializer_1_noformatpre.xhtml
+++ b/dom/base/test/file_xhtmlserializer_1_noformatpre.xhtml
@@ -35,24 +35,23 @@ var d = a < b && a > c;
 //]]>
 </script>
 
 <ol><li>Fusce
  a ipsum</li><li> non lacus posuere aliquet.</li><li> Sed fermentum posuere nulla</li><li> Donec tempor.</li></ol>
 Donec sollicitudin tortor 
 <!-- test on 
 comments -->
-<pre>lacinia <em>libero</em> ullamcorper laoreet.
-
- Cras quis
-
- nisi at odio
-
- consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at pharetra rutrum, 
-
+<pre>lacinia <em>libero</em> ullamcorper laoreet.<br />
+ Cras quis<br />
+ nisi at odio<br />
+ consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non 
+urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci 
+luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at 
+pharetra rutrum, <br />
 lacus risus pulvinar ante.
 </pre>
 ut gravida eros <br />leo ut libero
 <!-- empty element: end tag should be generated for backward compatibility with HTML -->
 <p></p>
 <noscript>
 <p>Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus</p></noscript>
 <p>Nam eu sapien. Sed viverra lacus. Donec quis ipsum. Nunc cursus aliquet lectus. Nunc vitae eros. Class aptent taciti</p>
--- a/dom/base/test/file_xhtmlserializer_1_sibling_body.xhtml
+++ b/dom/base/test/file_xhtmlserializer_1_sibling_body.xhtml
@@ -38,17 +38,20 @@ var d = a < b && a > c;
 <ol><li>Fusce
  a ipsum</li><li> non lacus posuere aliquet.</li><li> Sed fermentum posuere nulla</li><li> Donec tempor.</li></ol>
 Donec sollicitudin tortor 
 <!-- test on 
 comments -->
 <pre>lacinia <em>libero</em> ullamcorper laoreet.<br />
  Cras quis<br />
  nisi at odio<br />
- consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at pharetra rutrum, <br />
+ consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non 
+urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci 
+luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at 
+pharetra rutrum, <br />
 lacus risus pulvinar ante.
 </pre>
 ut gravida eros <br />leo ut libero
 <!-- empty element: end tag should be generated for backward compatibility with HTML -->
 <p></p>
 <noscript>
 <p>Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus</p></noscript>
 <p>Nam eu sapien. Sed viverra lacus. Donec quis ipsum. Nunc cursus aliquet lectus. Nunc vitae eros. Class aptent taciti</p>
--- a/dom/base/test/file_xhtmlserializer_1_sibling_body_only_body.xhtml
+++ b/dom/base/test/file_xhtmlserializer_1_sibling_body_only_body.xhtml
@@ -30,17 +30,20 @@ var d = a < b && a > c;
 <ol><li>Fusce
  a ipsum</li><li> non lacus posuere aliquet.</li><li> Sed fermentum posuere nulla</li><li> Donec tempor.</li></ol>
 Donec sollicitudin tortor 
 <!-- test on 
 comments -->
 <pre>lacinia <em>libero</em> ullamcorper laoreet.<br />
  Cras quis<br />
  nisi at odio<br />
- consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at pharetra rutrum, <br />
+ consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non 
+urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci 
+luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at 
+pharetra rutrum, <br />
 lacus risus pulvinar ante.
 </pre>
 ut gravida eros <br />leo ut libero
 <!-- empty element: end tag should be generated for backward compatibility with HTML -->
 <p></p>
 <noscript>
 <p>Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus</p></noscript>
 <p>Nam eu sapien. Sed viverra lacus. Donec quis ipsum. Nunc cursus aliquet lectus. Nunc vitae eros. Class aptent taciti</p>
--- a/dom/base/test/file_xhtmlserializer_1_wrap.xhtml
+++ b/dom/base/test/file_xhtmlserializer_1_wrap.xhtml
@@ -41,17 +41,20 @@ var d = a < b && a > c;
  a ipsum</li><li> non lacus posuere aliquet.</li><li> Sed fermentum 
 posuere nulla</li><li> Donec tempor.</li></ol>
 Donec sollicitudin tortor 
 <!-- test on 
 comments -->
 <pre>lacinia <em>libero</em> ullamcorper laoreet.<br />
  Cras quis<br />
  nisi at odio<br />
- consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at pharetra rutrum, <br />
+ consectetuer molestie. Curabitur consectetuer urna a sem. Nunc non 
+urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci 
+luctus et ultrices posuere cubilia Curae; Sed sollicitudin, nulla at 
+pharetra rutrum, <br />
 lacus risus pulvinar ante.
 </pre>
 ut gravida eros <br />leo ut libero
 <!-- empty element: end tag should be generated for backward compatibility with HTML -->
 <p></p>
 <noscript>
 <p>Curabitur consectetuer urna a sem. Nunc non urna. Cras in massa. Vestibulum ante ipsum primis in faucibus orci luctus</p></noscript>
 <p>Nam eu sapien. Sed viverra lacus. Donec quis ipsum. Nunc cursus 
--- a/dom/base/test/test_bug116083.html
+++ b/dom/base/test/test_bug116083.html
@@ -11,16 +11,28 @@ https://bugzilla.mozilla.org/show_bug.cg
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=116083">Mozilla Bug 116083</a>
 <div id="content">
 <div style="white-space: pre">foo  bar</div>
 <div style="white-space: pre-wrap">foo  bar</div>
 <div style="white-space: pre-line">foo  bar</div>
 <div style="white-space: -moz-pre-space">foo  bar</div>
+<div data-result="bar  baz"><span style="white-space: pre">bar  </span>baz</div>
+<div data-result="bar  baz"><span style="white-space: pre-wrap">bar  </span>baz</div>
+<div data-result="bar  baz"><span style="white-space: pre-line">bar  </span>baz</div>
+<div data-result="bar  baz"><span style="white-space: -moz-pre-space">bar  </span>baz</div>
+<div data-result="foo  &#10;  bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: pre"><div>foo  </div><div>  bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo &#10; bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: pre" contenteditable><div>foo </div><div> bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo  &#10;  bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: pre-wrap"><div>foo  </div><div>  bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo &#10; bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: pre-wrap" contenteditable><div>foo </div><div> bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo  &#10;  bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: pre-line"><div>foo  </div><div>  bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo &#10; bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: pre-line" contenteditable><div>foo </div><div> bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo  &#10;  bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: -moz-pre-space"><div>foo  </div><div>  bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
+<div data-result="foo &#10; bar&#10;&#10;!&#10;&#10;&#10;baz" style="white-space: -moz-pre-space" contenteditable><div>foo </div><div> bar</div><div><br></div><div>!</div><div><br><br></div><div>baz</div></div>
 <div data-result="&#10;foo bar&#10;">foo  bar</div>
 </div>
 <script type="application/javascript">
 
 SimpleTest.waitForExplicitFinish();
 SimpleTest.waitForFocus(function nextTest() {
   var div = document.querySelector("#content>div");
   if (!div) {
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -186,16 +186,22 @@ static const uint64_t ESTIMATED_DURATION
 static TimeDuration UsecsToDuration(int64_t aUsecs) {
   return TimeDuration::FromMicroseconds(aUsecs);
 }
 
 static int64_t DurationToUsecs(TimeDuration aDuration) {
   return static_cast<int64_t>(aDuration.ToSeconds() * USECS_PER_S);
 }
 
+static const uint32_t MIN_VIDEO_QUEUE_SIZE = 3;
+static const uint32_t MAX_VIDEO_QUEUE_SIZE = 10;
+
+static uint32_t sVideoQueueDefaultSize = MAX_VIDEO_QUEUE_SIZE;
+static uint32_t sVideoQueueHWAccelSize = MIN_VIDEO_QUEUE_SIZE;
+
 MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
                                                    MediaDecoderReader* aReader,
                                                    bool aRealTime) :
   mDecoder(aDecoder),
   mScheduler(new MediaDecoderStateMachineScheduler(
       aDecoder->GetReentrantMonitor(),
       &MediaDecoderStateMachine::TimeoutExpired, this, aRealTime)),
   mState(DECODER_STATE_DECODING_NONE),
@@ -211,17 +217,17 @@ MediaDecoderStateMachine::MediaDecoderSt
   mAudioStartTime(-1),
   mAudioEndTime(-1),
   mDecodedAudioEndTime(-1),
   mVideoFrameEndTime(-1),
   mDecodedVideoEndTime(-1),
   mVolume(1.0),
   mPlaybackRate(1.0),
   mPreservesPitch(true),
-  mAmpleVideoFrames(2),
+  mAmpleVideoFrames(MIN_VIDEO_QUEUE_SIZE),
   mLowAudioThresholdUsecs(detail::LOW_AUDIO_USECS),
   mAmpleAudioThresholdUsecs(detail::AMPLE_AUDIO_USECS),
   mQuickBufferingLowDataThresholdUsecs(detail::QUICK_BUFFERING_LOW_DATA_USECS),
   mIsAudioPrerolling(false),
   mIsVideoPrerolling(false),
   mAudioRequestStatus(RequestStatus::Idle),
   mVideoRequestStatus(RequestStatus::Idle),
   mAudioCaptured(false),
@@ -242,18 +248,26 @@ MediaDecoderStateMachine::MediaDecoderSt
   mLastFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED),
   mDecodingFrozenAtStateDecoding(false),
   mSentLoadedMetadataEvent(false),
   mSentFirstFrameLoadedEvent(false)
 {
   MOZ_COUNT_CTOR(MediaDecoderStateMachine);
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
 
-  mAmpleVideoFrames =
-    std::max<uint32_t>(Preferences::GetUint("media.video-queue.default-size", 10), 3);
+  static bool sPrefCacheInit = false;
+  if (!sPrefCacheInit) {
+    sPrefCacheInit = true;
+    Preferences::AddUintVarCache(&sVideoQueueDefaultSize,
+                                 "media.video-queue.default-size",
+                                 MAX_VIDEO_QUEUE_SIZE);
+    Preferences::AddUintVarCache(&sVideoQueueHWAccelSize,
+                                 "media.video-queue.hw-accel-size",
+                                 MIN_VIDEO_QUEUE_SIZE);
+  }
 
   mBufferingWait = IsRealTime() ? 0 : 30;
   mLowDataThresholdUsecs = IsRealTime() ? 0 : detail::LOW_DATA_THRESHOLD_USECS;
 
 #ifdef XP_WIN
   // Ensure high precision timers are enabled on Windows, otherwise the state
   // machine thread isn't woken up at reliable intervals to set the next frame,
   // and we drop frames while painting. Note that multiple calls to this
@@ -2198,26 +2212,37 @@ nsresult MediaDecoderStateMachine::Decod
       mState == DECODER_STATE_DECODING_METADATA &&
       isAwaitingResources) {
     // change state to DECODER_STATE_WAIT_FOR_RESOURCES
     StartWaitForResources();
     // affect values only if ReadMetadata succeeds
     return NS_OK;
   }
 
+  if (NS_FAILED(res) || (!info.HasValidMedia())) {
+    DECODER_WARN("ReadMetadata failed, res=%x HasValidMedia=%d", res, info.HasValidMedia());
+    return NS_ERROR_FAILURE;
+  }
+
   if (NS_SUCCEEDED(res)) {
     mDecoder->SetMediaSeekable(mReader->IsMediaSeekable());
   }
 
   mInfo = info;
 
-  if (NS_FAILED(res) || (!info.HasValidMedia())) {
-    DECODER_WARN("ReadMetadata failed, res=%x HasValidMedia=%d", res, info.HasValidMedia());
-    return NS_ERROR_FAILURE;
+  if (HasVideo()) {
+    mAmpleVideoFrames = (mReader->IsAsync() && mInfo.mVideo.mIsHardwareAccelerated)
+      ? std::max<uint32_t>(sVideoQueueHWAccelSize, MIN_VIDEO_QUEUE_SIZE)
+      : std::max<uint32_t>(sVideoQueueDefaultSize, MIN_VIDEO_QUEUE_SIZE);
+    DECODER_LOG("Video decode isAsync=%d HWAccel=%d videoQueueSize=%d",
+                mReader->IsAsync(),
+                mInfo.mVideo.mIsHardwareAccelerated,
+                mAmpleVideoFrames);
   }
+
   mDecoder->StartProgressUpdates();
   mGotDurationFromMetaData = (GetDuration() != -1) || mDurationSet;
 
   if (mGotDurationFromMetaData) {
     // We have all the information required: duration and size
     // Inform the element that we've loaded the metadata.
     EnqueueLoadedMetadataEvent();
   }
--- a/dom/media/MediaInfo.h
+++ b/dom/media/MediaInfo.h
@@ -36,16 +36,17 @@ struct TrackInfo {
 
 // Stores info relevant to presenting media frames.
 class VideoInfo {
 public:
   VideoInfo()
     : mDisplay(0,0)
     , mStereoMode(StereoMode::MONO)
     , mHasVideo(false)
+    , mIsHardwareAccelerated(false)
   {
     // TODO: TrackInfo should be initialized by its specific codec decoder.
     // This following call should be removed once we have that implemented.
     mTrackInfo.Init(NS_LITERAL_STRING("2"), NS_LITERAL_STRING("main"),
     EmptyString(), EmptyString(), true);
   }
 
   // Size in pixels at which the video is rendered. This is after it has
@@ -54,16 +55,18 @@ public:
 
   // Indicates the frame layout for single track stereo videos.
   StereoMode mStereoMode;
 
   // True if we have an active video bitstream.
   bool mHasVideo;
 
   TrackInfo mTrackInfo;
+
+  bool mIsHardwareAccelerated;
 };
 
 class AudioInfo {
 public:
   AudioInfo()
     : mRate(44100)
     , mChannels(2)
     , mHasAudio(false)
--- a/dom/media/fmp4/MP4Reader.cpp
+++ b/dom/media/fmp4/MP4Reader.cpp
@@ -460,16 +460,17 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo
                                                       mLayersBackendType,
                                                       mDecoder->GetImageContainer(),
                                                       mVideo.mTaskQueue,
                                                       mVideo.mCallback);
     }
     NS_ENSURE_TRUE(mVideo.mDecoder != nullptr, NS_ERROR_FAILURE);
     nsresult rv = mVideo.mDecoder->Init();
     NS_ENSURE_SUCCESS(rv, rv);
+    mInfo.mVideo.mIsHardwareAccelerated = mVideo.mDecoder->IsHardwareAccelerated();
   }
 
   // Get the duration, and report it to the decoder if we have it.
   Microseconds duration;
   {
     MonitorAutoLock lock(mDemuxerMonitor);
     duration = mDemuxer->Duration();
   }
--- a/dom/media/fmp4/PlatformDecoderModule.h
+++ b/dom/media/fmp4/PlatformDecoderModule.h
@@ -240,13 +240,14 @@ public:
     return false;
   };
   virtual bool IsDormantNeeded() {
     return false;
   };
   virtual void AllocateMediaResources() {}
   virtual void ReleaseMediaResources() {}
   virtual void ReleaseDecoder() {}
+  virtual bool IsHardwareAccelerated() const { return false; }
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/fmp4/SharedDecoderManager.cpp
+++ b/dom/media/fmp4/SharedDecoderManager.cpp
@@ -232,9 +232,16 @@ SharedDecoderProxy::ReleaseMediaResource
 
 void
 SharedDecoderProxy::ReleaseDecoder()
 {
   if (mManager->mActiveProxy == this) {
     mManager->mDecoder->ReleaseMediaResources();
   }
 }
+
+bool
+SharedDecoderProxy::IsHardwareAccelerated() const
+{
+  return mManager->mDecoder->IsHardwareAccelerated();
 }
+
+}
--- a/dom/media/fmp4/SharedDecoderManager.h
+++ b/dom/media/fmp4/SharedDecoderManager.h
@@ -65,16 +65,17 @@ public:
   virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE;
   virtual nsresult Flush() MOZ_OVERRIDE;
   virtual nsresult Drain() MOZ_OVERRIDE;
   virtual nsresult Shutdown() MOZ_OVERRIDE;
   virtual bool IsWaitingMediaResources() MOZ_OVERRIDE;
   virtual bool IsDormantNeeded() MOZ_OVERRIDE;
   virtual void ReleaseMediaResources() MOZ_OVERRIDE;
   virtual void ReleaseDecoder() MOZ_OVERRIDE;
+  virtual bool IsHardwareAccelerated() const MOZ_OVERRIDE;
 
   friend class SharedDecoderManager;
 
 private:
   nsRefPtr<SharedDecoderManager> mManager;
   MediaDataDecoderCallback* mCallback;
 };
 }
--- a/dom/media/fmp4/wmf/WMFMediaDataDecoder.cpp
+++ b/dom/media/fmp4/wmf/WMFMediaDataDecoder.cpp
@@ -174,9 +174,14 @@ WMFMediaDataDecoder::ReleaseMediaResourc
 }
 
 void
 WMFMediaDataDecoder::ReleaseDecoder()
 {
   ReleaseMediaResources();
 }
 
+bool
+WMFMediaDataDecoder::IsHardwareAccelerated() const {
+  return mMFTManager && mMFTManager->IsHardwareAccelerated();
+}
+
 } // namespace mozilla
--- a/dom/media/fmp4/wmf/WMFMediaDataDecoder.h
+++ b/dom/media/fmp4/wmf/WMFMediaDataDecoder.h
@@ -41,16 +41,19 @@ public:
   // enough data to produce more output. If this returns a failure code other
   // than MF_E_TRANSFORM_NEED_MORE_INPUT, an error will be reported to the
   // MP4Reader.
   virtual HRESULT Output(int64_t aStreamOffset,
                          nsRefPtr<MediaData>& aOutput) = 0;
 
   // Destroys all resources.
   virtual void Shutdown() = 0;
+
+  virtual bool IsHardwareAccelerated() const { return false; }
+
 };
 
 // Decodes audio and video using Windows Media Foundation. Samples are decoded
 // using the MFTDecoder created by the MFTManager. This class implements
 // the higher-level logic that drives mapping the MFT to the async
 // MediaDataDecoder interface. The specifics of decoding the exact stream
 // type are handled by MFTManager and the MFTDecoder it creates.
 class WMFMediaDataDecoder : public MediaDataDecoder {
@@ -70,16 +73,17 @@ public:
 
   virtual nsresult Shutdown() MOZ_OVERRIDE;
 
   virtual bool IsWaitingMediaResources() { return false; };
   virtual bool IsDormantNeeded() { return true; };
   virtual void AllocateMediaResources() MOZ_OVERRIDE;
   virtual void ReleaseMediaResources() MOZ_OVERRIDE;
   virtual void ReleaseDecoder() MOZ_OVERRIDE;
+  virtual bool IsHardwareAccelerated() const MOZ_OVERRIDE;
 
 private:
 
   // Called on the task queue. Inserts the sample into the decoder, and
   // extracts output if available.
   void ProcessDecode(mp4_demuxer::MP4Sample* aSample);
 
   // Called on the task queue. Extracts output if available, and delivers
--- a/dom/media/fmp4/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/fmp4/wmf/WMFVideoMFTManager.cpp
@@ -490,9 +490,15 @@ WMFVideoMFTManager::Output(int64_t aStre
 
 void
 WMFVideoMFTManager::Shutdown()
 {
   mDecoder = nullptr;
   DeleteOnMainThread(mDXVA2Manager);
 }
 
+bool
+WMFVideoMFTManager::IsHardwareAccelerated() const
+{
+  return mUseHwAccel;
+}
+
 } // namespace mozilla
--- a/dom/media/fmp4/wmf/WMFVideoMFTManager.h
+++ b/dom/media/fmp4/wmf/WMFVideoMFTManager.h
@@ -30,16 +30,18 @@ public:
 
   virtual HRESULT Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE;
 
   virtual HRESULT Output(int64_t aStreamOffset,
                          nsRefPtr<MediaData>& aOutput) MOZ_OVERRIDE;
 
   virtual void Shutdown() MOZ_OVERRIDE;
 
+  virtual bool IsHardwareAccelerated() const MOZ_OVERRIDE;
+
 private:
 
   bool InitializeDXVA();
 
   HRESULT ConfigureVideoFrameGeometry();
 
   HRESULT CreateBasicVideoFrame(IMFSample* aSample,
                                 int64_t aStreamOffset,
--- a/dom/storage/DOMStorageDBThread.cpp
+++ b/dom/storage/DOMStorageDBThread.cpp
@@ -36,17 +36,17 @@ namespace dom {
 
 DOMStorageDBBridge::DOMStorageDBBridge()
 {
 }
 
 
 DOMStorageDBThread::DOMStorageDBThread()
 : mThread(nullptr)
-, mMonitor("DOMStorageThreadMonitor")
+, mThreadObserver(new ThreadObserver())
 , mStopIOThread(false)
 , mWALModeEnabled(false)
 , mDBReady(false)
 , mStatus(NS_OK)
 , mWorkerStatements(mWorkerConnection)
 , mReaderStatements(mReaderConnection)
 , mDirtyEpoch(0)
 , mFlushImmediately(false)
@@ -71,17 +71,17 @@ DOMStorageDBThread::Init()
 
   // Ensure mozIStorageService init on the main thread first.
   nsCOMPtr<mozIStorageService> service =
     do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Need to keep the lock to avoid setting mThread later then
   // the thread body executes.
-  MonitorAutoLock monitor(mMonitor);
+  MonitorAutoLock monitor(mThreadObserver->GetMonitor());
 
   mThread = PR_CreateThread(PR_USER_THREAD, &DOMStorageDBThread::ThreadFunc, this,
                             PR_PRIORITY_LOW, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD,
                             262144);
   if (!mThread) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
@@ -93,17 +93,17 @@ DOMStorageDBThread::Shutdown()
 {
   if (!mThread) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_SHUTDOWN_DATABASE_MS> timer;
 
   {
-    MonitorAutoLock monitor(mMonitor);
+    MonitorAutoLock monitor(mThreadObserver->GetMonitor());
 
     // After we stop, no other operations can be accepted
     mFlushImmediately = true;
     mStopIOThread = true;
     monitor.Notify();
   }
 
   PR_JoinThread(mThread);
@@ -125,17 +125,17 @@ DOMStorageDBThread::SyncPreload(DOMStora
   }
 
   // Bypass sync load when an update is pending in the queue to write, we would
   // get incosistent data in the cache.  Also don't allow sync main-thread preload
   // when DB open and init is still pending on the background thread.
   if (mDBReady && mWALModeEnabled) {
     bool pendingTasks;
     {
-      MonitorAutoLock monitor(mMonitor);
+      MonitorAutoLock monitor(mThreadObserver->GetMonitor());
       pendingTasks = mPendingTasks.IsScopeUpdatePending(aCache->Scope()) ||
                      mPendingTasks.IsScopeClearPending(aCache->Scope());
     }
 
     if (!pendingTasks) {
       // WAL is enabled, thus do the load synchronously on the main thread.
       DBOperation preload(DBOperation::opPreload, aCache);
       preload.PerformAndFinalize(this);
@@ -152,25 +152,25 @@ DOMStorageDBThread::SyncPreload(DOMStora
   if (NS_SUCCEEDED(rv)) {
     aCache->LoadWait();
   }
 }
 
 void
 DOMStorageDBThread::AsyncFlush()
 {
-  MonitorAutoLock monitor(mMonitor);
+  MonitorAutoLock monitor(mThreadObserver->GetMonitor());
   mFlushImmediately = true;
   monitor.Notify();
 }
 
 bool
 DOMStorageDBThread::ShouldPreloadScope(const nsACString& aScope)
 {
-  MonitorAutoLock monitor(mMonitor);
+  MonitorAutoLock monitor(mThreadObserver->GetMonitor());
   return mScopesHavingData.Contains(aScope);
 }
 
 namespace { // anon
 
 PLDHashOperator
 GetScopesHavingDataEnum(nsCStringHashKey* aKey, void* aArg)
 {
@@ -180,36 +180,36 @@ GetScopesHavingDataEnum(nsCStringHashKey
   return PL_DHASH_NEXT;
 }
 
 } // anon
 
 void
 DOMStorageDBThread::GetScopesHavingData(InfallibleTArray<nsCString>* aScopes)
 {
-  MonitorAutoLock monitor(mMonitor);
+  MonitorAutoLock monitor(mThreadObserver->GetMonitor());
   mScopesHavingData.EnumerateEntries(GetScopesHavingDataEnum, aScopes);
 }
 
 nsresult
 DOMStorageDBThread::InsertDBOp(DOMStorageDBThread::DBOperation* aOperation)
 {
-  MonitorAutoLock monitor(mMonitor);
+  MonitorAutoLock monitor(mThreadObserver->GetMonitor());
 
   // Sentinel to don't forget to delete the operation when we exit early.
   nsAutoPtr<DOMStorageDBThread::DBOperation> opScope(aOperation);
 
   if (mStopIOThread) {
     // Thread use after shutdown demanded.
     MOZ_ASSERT(false);
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   if (NS_FAILED(mStatus)) {
-    MonitorAutoUnlock unlock(mMonitor);
+    MonitorAutoUnlock unlock(mThreadObserver->GetMonitor());
     aOperation->Finalize(mStatus);
     return mStatus;
   }
 
   switch (aOperation->Type()) {
   case DBOperation::opPreload:
   case DBOperation::opPreloadUrgent:
     if (mPendingTasks.IsScopeUpdatePending(aOperation->Scope())) {
@@ -220,17 +220,17 @@ DOMStorageDBThread::InsertDBOp(DOMStorag
       // before the pending flush, we would have got an inconsistent cache content.
       mFlushImmediately = true;
     } else if (mPendingTasks.IsScopeClearPending(aOperation->Scope())) {
       // The scope is scheduled to be cleared, so just quickly load as empty.
       // We need to do this to prevent load of the DB data before the scope has
       // actually been cleared from the database.  Preloads are processed
       // immediately before update and clear operations on the database that
       // are flushed periodically in batches.
-      MonitorAutoUnlock unlock(mMonitor);
+      MonitorAutoUnlock unlock(mThreadObserver->GetMonitor());
       aOperation->Finalize(NS_OK);
       return NS_OK;
     }
     // NO BREAK
 
   case DBOperation::opGetUsage:
     if (aOperation->Type() == DBOperation::opPreloadUrgent) {
       SetHigherPriority(); // Dropped back after urgent preload execution
@@ -287,59 +287,111 @@ DOMStorageDBThread::ThreadFunc(void* aAr
   mozilla::IOInterposer::UnregisterCurrentThread();
 }
 
 void
 DOMStorageDBThread::ThreadFunc()
 {
   nsresult rv = InitDatabase();
 
-  MonitorAutoLock lockMonitor(mMonitor);
+  MonitorAutoLock lockMonitor(mThreadObserver->GetMonitor());
 
   if (NS_FAILED(rv)) {
     mStatus = rv;
     mStopIOThread = true;
     return;
   }
 
-  while (MOZ_LIKELY(!mStopIOThread || mPreloads.Length() || mPendingTasks.HasTasks())) {
+  // Create an nsIThread for the current PRThread, so we can observe runnables
+  // dispatched to it.
+  nsCOMPtr<nsIThread> thread = NS_GetCurrentThread();
+  nsCOMPtr<nsIThreadInternal> threadInternal = do_QueryInterface(thread);
+  MOZ_ASSERT(threadInternal); // Should always succeed.
+  threadInternal->SetObserver(mThreadObserver);
+
+  while (MOZ_LIKELY(!mStopIOThread || mPreloads.Length() ||
+                    mPendingTasks.HasTasks() ||
+                    mThreadObserver->HasPendingEvents())) {
+    // Process xpcom events first.
+    while (MOZ_UNLIKELY(mThreadObserver->HasPendingEvents())) {
+      mThreadObserver->ClearPendingEvents();
+      MonitorAutoUnlock unlock(mThreadObserver->GetMonitor());
+      bool processedEvent;
+      do {
+        rv = thread->ProcessNextEvent(false, &processedEvent);
+      } while (NS_SUCCEEDED(rv) && processedEvent);
+    }
+
     if (MOZ_UNLIKELY(TimeUntilFlush() == 0)) {
       // Flush time is up or flush has been forced, do it now.
       UnscheduleFlush();
       if (mPendingTasks.Prepare()) {
         {
-          MonitorAutoUnlock unlockMonitor(mMonitor);
+          MonitorAutoUnlock unlockMonitor(mThreadObserver->GetMonitor());
           rv = mPendingTasks.Execute(this);
         }
 
         if (!mPendingTasks.Finalize(rv)) {
           mStatus = rv;
           NS_WARNING("localStorage DB access broken");
         }
       }
       NotifyFlushCompletion();
     } else if (MOZ_LIKELY(mPreloads.Length())) {
       nsAutoPtr<DBOperation> op(mPreloads[0]);
       mPreloads.RemoveElementAt(0);
       {
-        MonitorAutoUnlock unlockMonitor(mMonitor);
+        MonitorAutoUnlock unlockMonitor(mThreadObserver->GetMonitor());
         op->PerformAndFinalize(this);
       }
 
       if (op->Type() == DBOperation::opPreloadUrgent) {
         SetDefaultPriority(); // urgent preload unscheduled
       }
     } else if (MOZ_UNLIKELY(!mStopIOThread)) {
       lockMonitor.Wait(TimeUntilFlush());
     }
   } // thread loop
 
   mStatus = ShutdownDatabase();
+
+  if (threadInternal) {
+    threadInternal->SetObserver(nullptr);
+  }
 }
 
+
+NS_IMPL_ISUPPORTS(DOMStorageDBThread::ThreadObserver, nsIThreadObserver)
+
+NS_IMETHODIMP
+DOMStorageDBThread::ThreadObserver::OnDispatchedEvent(nsIThreadInternal *thread)
+{
+  MonitorAutoLock lock(mMonitor);
+  mHasPendingEvents = true;
+  lock.Notify();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMStorageDBThread::ThreadObserver::OnProcessNextEvent(nsIThreadInternal *thread,
+                                       bool mayWait,
+                                       uint32_t recursionDepth)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DOMStorageDBThread::ThreadObserver::AfterProcessNextEvent(nsIThreadInternal *thread,
+                                          uint32_t recursionDepth,
+                                          bool eventWasProcessed)
+{
+  return NS_OK;
+}
+
+
 extern void
 ReverseString(const nsCSubstring& aSource, nsCSubstring& aResult);
 
 namespace { // anon
 
 class nsReverseStringSQLFunction MOZ_FINAL : public mozIStorageFunction
 {
   ~nsReverseStringSQLFunction() {}
@@ -503,17 +555,17 @@ DOMStorageDBThread::InitDatabase()
   NS_ENSURE_SUCCESS(rv, rv);
   mozStorageStatementScoper scope(stmt);
 
   while (NS_SUCCEEDED(rv = stmt->ExecuteStep(&exists)) && exists) {
     nsAutoCString foundScope;
     rv = stmt->GetUTF8String(0, foundScope);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    MonitorAutoLock monitor(mMonitor);
+    MonitorAutoLock monitor(mThreadObserver->GetMonitor());
     mScopesHavingData.PutEntry(foundScope);
   }
 
   return NS_OK;
 }
 
 nsresult
 DOMStorageDBThread::SetJournalMode(bool aIsWal)
@@ -643,17 +695,17 @@ DOMStorageDBThread::ScheduleFlush()
 {
   if (mDirtyEpoch) {
     return; // Already scheduled
   }
 
   mDirtyEpoch = PR_IntervalNow() | 1; // Must be non-zero to indicate we are scheduled
 
   // Wake the monitor from indefinite sleep...
-  mMonitor.Notify();
+  (mThreadObserver->GetMonitor()).Notify();
 }
 
 void
 DOMStorageDBThread::UnscheduleFlush()
 {
   // We are just about to do the flush, drop flags
   mFlushImmediately = false;
   mDirtyEpoch = 0;
--- a/dom/storage/DOMStorageDBThread.h
+++ b/dom/storage/DOMStorageDBThread.h
@@ -10,16 +10,17 @@
 #include "prinrval.h"
 #include "nsTArray.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/storage/StatementCache.h"
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "nsClassHashtable.h"
 #include "nsIFile.h"
+#include "nsIThreadInternal.h"
 
 class mozIStorageConnection;
 
 namespace mozilla {
 namespace dom {
 
 class DOMStorageCacheBridge;
 class DOMStorageUsageBridge;
@@ -203,16 +204,44 @@ public:
 
     // Collection of all tasks, valid only between Prepare() and Execute()
     nsTArray<nsAutoPtr<DBOperation> > mExecList;
 
     // Number of failing flush attempts
     uint32_t mFlushFailureCount;
   };
 
+  class ThreadObserver MOZ_FINAL : public nsIThreadObserver
+  {
+    NS_DECL_THREADSAFE_ISUPPORTS
+    NS_DECL_NSITHREADOBSERVER
+
+    ThreadObserver()
+      : mHasPendingEvents(false)
+      , mMonitor("DOMStorageThreadMonitor")
+    {
+    }
+
+    bool HasPendingEvents() {
+      mMonitor.AssertCurrentThreadOwns();
+      return mHasPendingEvents;
+    }
+    void ClearPendingEvents() {
+      mMonitor.AssertCurrentThreadOwns();
+      mHasPendingEvents = false;
+    }
+    Monitor& GetMonitor() { return mMonitor; }
+
+  private:
+    virtual ~ThreadObserver() {}
+    bool mHasPendingEvents;
+    // The monitor we drive the thread with
+    Monitor mMonitor;
+  };
+
 public:
   DOMStorageDBThread();
   virtual ~DOMStorageDBThread() {}
 
   virtual nsresult Init();
   virtual nsresult Shutdown();
 
   virtual void AsyncPreload(DOMStorageCacheBridge* aCache, bool aPriority = false)
@@ -245,20 +274,21 @@ public:
 
   virtual bool ShouldPreloadScope(const nsACString& aScope);
   virtual void GetScopesHavingData(InfallibleTArray<nsCString>* aScopes);
 
 private:
   nsCOMPtr<nsIFile> mDatabaseFile;
   PRThread* mThread;
 
-  // The monitor we drive the thread with
-  Monitor mMonitor;
+  // Used to observe runnables dispatched to our thread and to monitor it.
+  nsRefPtr<ThreadObserver> mThreadObserver;
 
-  // Flag to stop, protected by the monitor
+  // Flag to stop, protected by the monitor returned by
+  // mThreadObserver->GetMonitor().
   bool mStopIOThread;
 
   // Whether WAL is enabled
   bool mWALModeEnabled;
 
   // Whether DB has already been open, avoid races between main thread reads
   // and pending DB init in the background I/O thread
   bool mDBReady;
--- a/editor/libeditor/nsHTMLEditor.cpp
+++ b/editor/libeditor/nsHTMLEditor.cpp
@@ -964,37 +964,25 @@ nsHTMLEditor::IsVisBreak(nsINode* aNode)
                         &visOffset, &visType);
   if (visType & WSType::block) {
     return false;
   }
   
   return true;
 }
 
-
 bool
 nsHTMLEditor::IsVisBreak(nsIDOMNode* aNode)
 {
   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
   NS_ENSURE_TRUE(node, false);
   return IsVisBreak(node);
 }
 
 NS_IMETHODIMP
-nsHTMLEditor::BreakIsVisible(nsIDOMNode *aNode, bool *aIsVisible)
-{
-  NS_ENSURE_ARG_POINTER(aNode && aIsVisible);
-
-  *aIsVisible = IsVisBreak(aNode);
-
-  return NS_OK;
-}
-
-
-NS_IMETHODIMP
 nsHTMLEditor::GetIsDocumentEditable(bool *aIsDocumentEditable)
 {
   NS_ENSURE_ARG_POINTER(aIsDocumentEditable);
 
   nsCOMPtr<nsIDOMDocument> doc = GetDOMDocument();
   *aIsDocumentEditable = doc && IsModifiable();
 
   return NS_OK;
--- a/editor/nsIHTMLEditor.idl
+++ b/editor/nsIHTMLEditor.idl
@@ -17,18 +17,17 @@ namespace mozilla {
 namespace dom {
 class Element;
 }
 }
 %}
 
 [ptr] native Element (mozilla::dom::Element);
 
-[scriptable, uuid(393a364f-e8e2-48a1-a271-a0067b6bac9b)]
-
+[scriptable, uuid(87ee993e-985f-4a43-a974-0d9512da2fb0)]
 interface nsIHTMLEditor : nsISupports
 {
 %{C++
   typedef short EAlignment;
 %}
 
   // used by GetAlignment()
   const short eLeft = 0;
@@ -547,19 +546,14 @@ interface nsIHTMLEditor : nsISupports
    * A boolean indicating if a return key pressed in a paragraph creates
    * another paragraph or just inserts a <br> at the caret
    *
    * @return    true if CR in a paragraph creates a new paragraph
    */
   attribute boolean returnInParagraphCreatesNewParagraph;
 
   /**
-   * Checks whether a BR node is visible to the user.
-   */
-  boolean breakIsVisible(in nsIDOMNode aNode);
-
-  /**
    * Get an active editor's editing host in DOM window.  If this editor isn't
    * active in the DOM window, this returns NULL.
    */
   [noscript, notxpcom] Element GetActiveEditingHost();
 };
 
--- a/gfx/layers/D3D9SurfaceImage.cpp
+++ b/gfx/layers/D3D9SurfaceImage.cpp
@@ -14,17 +14,74 @@ namespace mozilla {
 namespace layers {
 
 
 D3D9SurfaceImage::D3D9SurfaceImage()
   : Image(nullptr, ImageFormat::D3D9_RGB32_TEXTURE)
   , mSize(0, 0)
 {}
 
-D3D9SurfaceImage::~D3D9SurfaceImage() {}
+D3D9SurfaceImage::~D3D9SurfaceImage()
+{
+  if (mTexture) {
+    gfxWindowsPlatform::sD3D9SurfaceImageUsed -= mSize.width * mSize.height * 4;
+  }
+}
+
+static const GUID sD3D9TextureUsage =
+{ 0x631e1338, 0xdc22, 0x497f, { 0xa1, 0xa8, 0xb4, 0xfe, 0x3a, 0xf4, 0x13, 0x4d } };
+
+/* This class get's it's lifetime tied to a D3D texture
+ * and increments memory usage on construction and decrements
+ * on destruction */
+class TextureMemoryMeasurer9 : public IUnknown
+{
+public:
+  TextureMemoryMeasurer9(size_t aMemoryUsed)
+  {
+    mMemoryUsed = aMemoryUsed;
+    gfxWindowsPlatform::sD3D9MemoryUsed += mMemoryUsed;
+    mRefCnt = 0;
+  }
+  ~TextureMemoryMeasurer9()
+  {
+    gfxWindowsPlatform::sD3D9MemoryUsed -= mMemoryUsed;
+  }
+  STDMETHODIMP_(ULONG) AddRef() {
+    mRefCnt++;
+    return mRefCnt;
+  }
+  STDMETHODIMP QueryInterface(REFIID riid,
+                              void **ppvObject)
+  {
+    IUnknown *punk = nullptr;
+    if (riid == IID_IUnknown) {
+      punk = this;
+    }
+    *ppvObject = punk;
+    if (punk) {
+      punk->AddRef();
+      return S_OK;
+    } else {
+      return E_NOINTERFACE;
+    }
+  }
+
+  STDMETHODIMP_(ULONG) Release() {
+    int refCnt = --mRefCnt;
+    if (refCnt == 0) {
+      delete this;
+    }
+    return refCnt;
+  }
+private:
+  int mRefCnt;
+  int mMemoryUsed;
+};
+
 
 HRESULT
 D3D9SurfaceImage::SetData(const Data& aData)
 {
   NS_ENSURE_TRUE(aData.mSurface, E_POINTER);
   HRESULT hr;
   RefPtr<IDirect3DSurface9> surface = aData.mSurface;
 
@@ -57,16 +114,21 @@ D3D9SurfaceImage::SetData(const Data& aD
                              1,
                              D3DUSAGE_RENDERTARGET,
                              D3DFMT_X8R8G8B8,
                              D3DPOOL_DEFAULT,
                              byRef(texture),
                              &shareHandle);
   NS_ENSURE_TRUE(SUCCEEDED(hr) && shareHandle, hr);
 
+  // Track the lifetime of this memory
+  texture->SetPrivateData(sD3D9TextureUsage, new TextureMemoryMeasurer9(region.width * region.height * 4), sizeof(IUnknown *), D3DSPD_IUNKNOWN);
+
+  gfxWindowsPlatform::sD3D9SurfaceImageUsed += region.width * region.height * 4;
+
   // Copy the image onto the texture, preforming YUV -> RGB conversion if necessary.
   RefPtr<IDirect3DSurface9> textureSurface;
   hr = texture->GetSurfaceLevel(0, byRef(textureSurface));
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
   // Stash the surface description for later use.
   textureSurface->GetDesc(&mDesc);
 
--- a/gfx/layers/d3d9/TextureD3D9.cpp
+++ b/gfx/layers/d3d9/TextureD3D9.cpp
@@ -736,16 +736,19 @@ SharedTextureClientD3D9::SharedTextureCl
   MOZ_COUNT_CTOR(SharedTextureClientD3D9);
 }
 
 SharedTextureClientD3D9::~SharedTextureClientD3D9()
 {
   if (mTexture && mActor) {
     KeepUntilFullDeallocation(new TKeepAlive<IDirect3DTexture9>(mTexture));
   }
+  if (mTexture) {
+    gfxWindowsPlatform::sD3D9SharedTextureUsed -= mDesc.Width * mDesc.Height * 4;
+  }
   MOZ_COUNT_DTOR(SharedTextureClientD3D9);
 }
 
 bool
 SharedTextureClientD3D9::Lock(OpenMode)
 {
   MOZ_ASSERT(!mIsLocked);
   if (!IsValid()) {
--- a/gfx/layers/d3d9/TextureD3D9.h
+++ b/gfx/layers/d3d9/TextureD3D9.h
@@ -261,16 +261,17 @@ public:
   virtual bool ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor) MOZ_OVERRIDE;
 
   void InitWith(IDirect3DTexture9* aTexture, HANDLE aSharedHandle, D3DSURFACE_DESC aDesc)
   {
     MOZ_ASSERT(!mTexture);
     mTexture = aTexture;
     mHandle = aSharedHandle;
     mDesc = aDesc;
+    gfxWindowsPlatform::sD3D9SharedTextureUsed += mDesc.Width * mDesc.Height * 4;
   }
 
   virtual gfx::IntSize GetSize() const
   {
     return gfx::IntSize(mDesc.Width, mDesc.Height);
   }
 
   virtual bool HasInternalBuffer() const MOZ_OVERRIDE { return true; }
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -339,16 +339,70 @@ public:
       return MOZ_COLLECT_REPORT("d3d11-shared-textures", KIND_OTHER, UNITS_BYTES,
                                 gfxWindowsPlatform::sD3D11MemoryUsed,
                                 "Memory used for D3D11 shared textures");
   }
 };
 
 NS_IMPL_ISUPPORTS(D3D11TextureReporter, nsIMemoryReporter)
 
+Atomic<size_t> gfxWindowsPlatform::sD3D9MemoryUsed;
+
+class D3D9TextureReporter MOZ_FINAL : public nsIMemoryReporter
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  NS_IMETHOD CollectReports(nsIHandleReportCallback *aHandleReport,
+                            nsISupports* aData, bool aAnonymize) MOZ_OVERRIDE
+  {
+    return MOZ_COLLECT_REPORT("d3d9-shared-textures", KIND_OTHER, UNITS_BYTES,
+                              gfxWindowsPlatform::sD3D9MemoryUsed,
+                              "Memory used for D3D9 shared textures");
+  }
+};
+
+NS_IMPL_ISUPPORTS(D3D9TextureReporter, nsIMemoryReporter)
+
+Atomic<size_t> gfxWindowsPlatform::sD3D9SurfaceImageUsed;
+
+class D3D9SurfaceImageReporter MOZ_FINAL : public nsIMemoryReporter
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  NS_IMETHOD CollectReports(nsIHandleReportCallback *aHandleReport,
+                            nsISupports* aData, bool aAnonymize) MOZ_OVERRIDE
+  {
+    return MOZ_COLLECT_REPORT("d3d9-surface-image", KIND_OTHER, UNITS_BYTES,
+                              gfxWindowsPlatform::sD3D9SurfaceImageUsed,
+                              "Memory used for D3D9 surface images");
+  }
+};
+
+NS_IMPL_ISUPPORTS(D3D9SurfaceImageReporter, nsIMemoryReporter)
+
+Atomic<size_t> gfxWindowsPlatform::sD3D9SharedTextureUsed;
+
+class D3D9SharedTextureReporter MOZ_FINAL : public nsIMemoryReporter
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  NS_IMETHOD CollectReports(nsIHandleReportCallback *aHandleReport,
+                            nsISupports* aData, bool aAnonymize) MOZ_OVERRIDE
+  {
+    return MOZ_COLLECT_REPORT("d3d9-shared-texture", KIND_OTHER, UNITS_BYTES,
+                              gfxWindowsPlatform::sD3D9SharedTextureUsed,
+                              "Memory used for D3D9 shared textures");
+  }
+};
+
+NS_IMPL_ISUPPORTS(D3D9SharedTextureReporter, nsIMemoryReporter)
+
 gfxWindowsPlatform::gfxWindowsPlatform()
   : mD3D11DeviceInitialized(false)
   , mIsWARP(false)
 {
     mUseClearTypeForDownloadableFonts = UNINITIALIZED_VALUE;
     mUseClearTypeAlways = UNINITIALIZED_VALUE;
 
     mUsingGDIFonts = false;
@@ -367,16 +421,19 @@ gfxWindowsPlatform::gfxWindowsPlatform()
     if (gfxPrefs::Direct2DUse1_1()) {
       InitD3D11Devices();
     }
 
     UpdateRenderMode();
 
     RegisterStrongMemoryReporter(new GPUAdapterReporter());
     RegisterStrongMemoryReporter(new D3D11TextureReporter());
+    RegisterStrongMemoryReporter(new D3D9TextureReporter());
+    RegisterStrongMemoryReporter(new D3D9SurfaceImageReporter());
+    RegisterStrongMemoryReporter(new D3D9SharedTextureReporter());
 }
 
 gfxWindowsPlatform::~gfxWindowsPlatform()
 {
     mDeviceManager = nullptr;
 
     // not calling FT_Done_FreeType because cairo may still hold references to
     // these FT_Faces.  See bug 458169.
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -253,16 +253,19 @@ public:
 
     mozilla::layers::ReadbackManagerD3D11* GetReadbackManager();
 
     static bool IsOptimus();
 
     bool IsWARP() { return mIsWARP; }
 
     static mozilla::Atomic<size_t> sD3D11MemoryUsed;
+    static mozilla::Atomic<size_t> sD3D9MemoryUsed;
+    static mozilla::Atomic<size_t> sD3D9SurfaceImageUsed;
+    static mozilla::Atomic<size_t> sD3D9SharedTextureUsed;
 
 protected:
     RenderMode mRenderMode;
 
     int8_t mUseClearTypeForDownloadableFonts;
     int8_t mUseClearTypeAlways;
 
 private:
--- a/mobile/android/base/resources/layout/home_empty_panel.xml
+++ b/mobile/android/base/resources/layout/home_empty_panel.xml
@@ -20,23 +20,22 @@
                android:layout_width="90dp"
                android:layout_height="90dp"
                android:layout_marginBottom="10dp"
                android:gravity="top|center"
                android:scaleType="fitCenter"/>
 
     <TextView android:id="@+id/home_empty_text"
               android:layout_width="match_parent"
-              android:layout_height="0dip"
+              android:layout_height="wrap_content"
               android:gravity="top|center"
-              android:textAppearance="@style/TextAppearance.EmptyMessage"
-              android:layout_weight="1"/>
+              android:textAppearance="@style/TextAppearance.EmptyMessage"/>
 
     <TextView android:id="@+id/home_empty_hint"
-              android:layout_width="match_parent"
+              android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:visibility="gone"
               android:gravity="top|center"
               android:textAppearance="@style/TextAppearance.EmptyHint"
               android:textColorLink="#FFA62F" />
     <!-- Empty spacer view -->
     <View android:layout_width="match_parent"
           android:layout_height="0dip"
--- a/services/healthreport/docs/dataformat.rst
+++ b/services/healthreport/docs/dataformat.rst
@@ -1633,16 +1633,55 @@ the number of devices of that type.
 Common values include:
 
 desktop
    Corresponds to a Firefox desktop client.
 
 mobile
    Corresponds to a Fennec client.
 
+org.mozilla.sync.migration
+--------------------------
+
+This daily measurement contains information about sync migration (that is, the
+semi-automated process of migrating a legacy sync account to an FxA account.)
+
+Measurements will start being recorded after a migration is offered by the
+sync server and stop after migration is complete or the user elects to "unlink"
+their sync account.  In other words, it is expected that users with Sync setup
+for FxA or with sync unconfigured will not collect data, and that for users
+where data is collected, the collection will only be for a relatively short
+period.
+
+Version 1
+^^^^^^^^^
+
+Version 1 was introduced with Firefox 37 and includes the following properties:
+
+state
+   Corresponds to either a STATE_USER_* string or a STATE_INTERNAL_* string in
+   FxaMigration.jsm.  This reflects a state where we are waiting for the user,
+   or waiting for some internal process to complete on the way to completing
+   the migration.
+
+declined
+    Corresponds to the number of times the user closed the migration infobar.
+
+unlinked
+    Set if the user declined to migrate and instead "unlinked" Sync from the
+    browser.
+
+accepted
+    Corresponds to the number of times the user explicitly elected to start or
+    continue the migration - it counts how often the user clicked on any UI
+    created specifically for migration. The "ideal" UX for migration would see
+    this at exactly 1, some known edge-cases (eg, browser restart required to
+    finish) could expect this to be 2, and anything more means we are doing
+    something wrong.
+
 org.mozilla.sysinfo.sysinfo
 ---------------------------
 
 This measurement contains basic information about the system the application
 is running on.
 
 Version 2
 ^^^^^^^^^
--- a/services/sync/modules/FxaMigrator.jsm
+++ b/services/sync/modules/FxaMigrator.jsm
@@ -22,23 +22,31 @@ XPCOMUtils.defineLazyGetter(this, "Weave
 
 XPCOMUtils.defineLazyModuleGetter(this, "Weave",
   "resource://services-sync/main.js");
 
 // FxAccountsCommon.js doesn't use a "namespace", so create one here.
 let fxAccountsCommon = {};
 Cu.import("resource://gre/modules/FxAccountsCommon.js", fxAccountsCommon);
 
-// We send this notification whenever the migration state changes.
+// We send this notification whenever the "user" migration state changes.
 const OBSERVER_STATE_CHANGE_TOPIC = "fxa-migration:state-changed";
 // We also send the state notification when we *receive* this.  This allows
 // consumers to avoid loading this module until it receives a notification
 // from us (which may never happen if there's no migration to do)
 const OBSERVER_STATE_REQUEST_TOPIC = "fxa-migration:state-request";
 
+// We send this notification whenever the migration is paused waiting for
+// something internal to complete.
+const OBSERVER_INTERNAL_STATE_CHANGE_TOPIC = "fxa-migration:internal-state-changed";
+
+// We use this notification so Sync's healthreport module can record telemetry
+// (actually via "health report") for us.
+const OBSERVER_INTERNAL_TELEMETRY_TOPIC = "fxa-migration:internal-telemetry";
+
 const OBSERVER_TOPICS = [
   "xpcom-shutdown",
   "weave:service:sync:start",
   "weave:service:sync:finish",
   "weave:service:sync:error",
   "weave:eol",
   OBSERVER_STATE_REQUEST_TOPIC,
   fxAccountsCommon.ONLOGIN_NOTIFICATION,
@@ -81,16 +89,29 @@ Migrator.prototype = {
   // either. (a) no migration is necessary or (b) that the migrator module is
   // waiting for something outside of the user's control - eg, sync to complete,
   // the migration sentinel to be uploaded, etc.  In most cases the wait will be
   // short, but edge cases (eg, no network, sync bugs that prevent it stopping
   // until shutdown) may require a significantly longer wait.
   STATE_USER_FXA: "waiting for user to be signed in to FxA",
   STATE_USER_FXA_VERIFIED: "waiting for a verified FxA user",
 
+  // What internal state are we at?  This is primarily used for FHR reporting so
+  // we can determine why exactly we might be stalled.
+  STATE_INTERNAL_WAITING_SYNC_COMPLETE: "waiting for sync to complete",
+  STATE_INTERNAL_WAITING_WRITE_SENTINEL: "waiting for sentinel to be written",
+  STATE_INTERNAL_WAITING_START_OVER: "waiting for sync to reset itself",
+  STATE_INTERNAL_COMPLETE: "migration complete",
+
+  // Flags for the telemetry we record.  The UI will call a helper to record
+  // the fact some UI was interacted with.
+  TELEMETRY_ACCEPTED: "accepted",
+  TELEMETRY_DECLINED: "declined",
+  TELEMETRY_UNLINKED: "unlinked",
+
   finalize() {
     for (let topic of OBSERVER_TOPICS) {
       Services.obs.removeObserver(this, topic);
     }
   },
 
   observe(subject, topic, data) {
     this.log.debug("observed " + topic);
@@ -194,20 +215,24 @@ Migrator.prototype = {
     // We need to disable sync from automatically starting,
     // and if we are currently syncing wait for it to complete.
     this._blockSync();
 
     // Are we currently syncing?
     if (Weave.Service._locked) {
       // our observers will kick us further along when complete.
       this.log.info("waiting for sync to complete")
+      Services.obs.notifyObservers(null, OBSERVER_INTERNAL_STATE_CHANGE_TOPIC,
+                                   this.STATE_INTERNAL_WAITING_SYNC_COMPLETE);
       return null;
     }
 
     // Write the migration sentinel if necessary.
+    Services.obs.notifyObservers(null, OBSERVER_INTERNAL_STATE_CHANGE_TOPIC,
+                                 this.STATE_INTERNAL_WAITING_WRITE_SENTINEL);
     yield this._setMigrationSentinelIfNecessary();
 
     // Get the list of enabled engines to we can restore that state.
     let enginePrefs = this._getEngineEnabledPrefs();
 
     // Must be ready to perform the actual migration.
     this.log.info("Performing final sync migration steps");
     // Do the actual migration.  We setup one observer for when the new identity
@@ -244,30 +269,34 @@ Migrator.prototype = {
         this.log.info("observed that startOver is complete");
         Services.obs.removeObserver(observe, "weave:service:start-over:finish");
         resolve();
       }, "weave:service:start-over:finish", false);
     });
 
     Weave.Service.startOver();
     // need to wait for an observer.
+    Services.obs.notifyObservers(null, OBSERVER_INTERNAL_STATE_CHANGE_TOPIC,
+                                 this.STATE_INTERNAL_WAITING_START_OVER);
     yield startOverComplete;
     // observer fired, now kick things off with the FxA user.
     this.log.info("scheduling initial FxA sync.");
     // Note we technically don't need to unblockSync as by now all sync prefs
     // have been reset - but it doesn't hurt.
     this._unblockSync();
     Weave.Service.scheduler.scheduleNextSync(0);
 
     // Tell the front end that migration is now complete -- Sync is now
     // configured with an FxA user.
     forceObserver = true;
     this.log.info("Migration complete");
     update(null);
 
+    Services.obs.notifyObservers(null, OBSERVER_INTERNAL_STATE_CHANGE_TOPIC,
+                                 this.STATE_INTERNAL_COMPLETE);
     return null;
   }),
 
   /* Return an object with the preferences we care about */
   _getSentinelPrefs() {
     let result = {};
     for (let pref of FXA_SENTINEL_PREFS) {
       if (Services.prefs.prefHasUserValue(pref)) {
@@ -420,16 +449,19 @@ Migrator.prototype = {
     let tail = email ? "&email=" + encodeURIComponent(email) : "";
     // A special flag so server-side metrics can tell this is part of migration.
     tail += "&migration=sync11";
     // We want to ask FxA to offer a "Customize Sync" checkbox iff any engines
     // are disabled.
     let customize = !this._allEnginesEnabled();
     tail += "&customizeSync=" + customize;
 
+    // We assume the caller of this is going to actually use it, so record
+    // telemetry now.
+    this.recordTelemetry(this.TELEMETRY_ACCEPTED);
     return {
       url: "about:accounts?action=" + action + tail,
       options: {ignoreFragment: true, replaceQueryString: true}
     };
   }),
 
   // Ask the FxA servers to re-send a verification mail for the currently
   // logged in user. This should only be called while we are in the
@@ -443,16 +475,17 @@ Migrator.prototype = {
     }
     let ok = true;
     try {
       yield fxAccounts.resendVerificationEmail();
     } catch (ex) {
       this.log.error("Failed to resend verification mail: ${}", ex);
       ok = false;
     }
+    this.recordTelemetry(this.TELEMETRY_ACCEPTED);
     let fxauser = yield fxAccounts.getSignedInUser();
     let sb = Services.strings.createBundle("chrome://browser/locale/accounts.properties");
 
     let heading = ok ?
                   sb.formatStringFromName("verificationSentHeading", [fxauser.email], 1) :
                   sb.GetStringFromName("verificationNotSentHeading");
     let title = sb.GetStringFromName(ok ? "verificationSentTitle" : "verificationNotSentTitle");
     let description = sb.GetStringFromName(ok ? "verificationSentDescription"
@@ -474,13 +507,40 @@ Migrator.prototype = {
   forgetFxAccount: Task.async(function * () {
     // warn if we aren't in the expected state - but go ahead anyway!
     if (this._state != this.STATE_USER_FXA_VERIFIED) {
       this.log.warn("forgetFxAccount called in an unexpected state: ${}", this._state);
     }
     return fxAccounts.signOut();
   }),
 
-}
+  recordTelemetry(flag) {
+    // Note the value is the telemetry field name - but this is an
+    // implementation detail which could be changed later.
+    switch (flag) {
+      case this.TELEMETRY_ACCEPTED:
+      case this.TELEMETRY_UNLINKED:
+      case this.TELEMETRY_DECLINED:
+        Services.obs.notifyObservers(null, OBSERVER_INTERNAL_TELEMETRY_TOPIC, flag);
+        break;
+      default:
+        throw new Error("Unexpected telemetry flag: " + flag);
+    }
+  },
+
+  get learnMoreLink() {
+    try {
+      var url = Services.prefs.getCharPref("app.support.baseURL");
+    } catch (err) {
+      return null;
+    }
+    url += "sync-upgrade";
+    let sb = Services.strings.createBundle("chrome://weave/locale/services/sync.properties");
+    return {
+      text: sb.GetStringFromName("sync.eol.learnMore.label"),
+      href: Services.urlFormatter.formatURL(url),
+    };
+  },
+};
 
 // We expose a singleton
 this.EXPORTED_SYMBOLS = ["fxaMigrator"];
 let fxaMigrator = new Migrator();
--- a/services/sync/modules/healthreport.jsm
+++ b/services/sync/modules/healthreport.jsm
@@ -58,33 +58,55 @@ SyncDevicesMeasurement1.prototype = Obje
     return true;
   },
 
   fieldType: function (name) {
     return Metrics.Storage.FIELD_DAILY_COUNTER;
   },
 });
 
+function SyncMigrationMeasurement1() {
+  Metrics.Measurement.call(this);
+}
+
+SyncMigrationMeasurement1.prototype = Object.freeze({
+  __proto__: Metrics.Measurement.prototype,
+
+  name: "migration",
+  version: 1,
+
+  fields: {
+    state: DAILY_LAST_TEXT_FIELD, // last "user" or "internal" state we saw for the day
+    accepted: DAILY_COUNTER_FIELD, // number of times user tried to start migration
+    declined: DAILY_COUNTER_FIELD, // number of times user closed nagging infobar
+    unlinked: DAILY_LAST_NUMERIC_FIELD, // did the user decline and unlink
+  },
+});
+
 this.SyncProvider = function () {
   Metrics.Provider.call(this);
 };
 SyncProvider.prototype = Object.freeze({
   __proto__: Metrics.Provider.prototype,
 
   name: "org.mozilla.sync",
 
   measurementTypes: [
     SyncDevicesMeasurement1,
     SyncMeasurement1,
+    SyncMigrationMeasurement1,
   ],
 
   _OBSERVERS: [
     "weave:service:sync:start",
     "weave:service:sync:finish",
     "weave:service:sync:error",
+    "fxa-migration:state-changed",
+    "fxa-migration:internal-state-changed",
+    "fxa-migration:internal-telemetry",
   ],
 
   postInit: function () {
     for (let o of this._OBSERVERS) {
       Services.obs.addObserver(this, o, false);
     }
 
     return Promise.resolve();
@@ -94,38 +116,98 @@ SyncProvider.prototype = Object.freeze({
     for (let o of this._OBSERVERS) {
       Services.obs.removeObserver(this, o);
     }
 
     return Promise.resolve();
   },
 
   observe: function (subject, topic, data) {
+    switch (topic) {
+      case "weave:service:sync:start":
+      case "weave:service:sync:finish":
+      case "weave:service:sync:error":
+        return this._observeSync(subject, topic, data);
+
+      case "fxa-migration:state-changed":
+      case "fxa-migration:internal-state-changed":
+      case "fxa-migration:internal-telemetry":
+        return this._observeMigration(subject, topic, data);
+    }
+    Cu.reportError("unexpected topic in sync healthreport provider: " + topic);
+  },
+
+  _observeSync: function (subject, topic, data) {
     let field;
     switch (topic) {
       case "weave:service:sync:start":
         field = "syncStart";
         break;
 
       case "weave:service:sync:finish":
         field = "syncSuccess";
         break;
 
       case "weave:service:sync:error":
         field = "syncError";
         break;
+
+      default:
+        Cu.reportError("unexpected sync topic in sync healthreport provider: " + topic);
+        return;
     }
 
     let m = this.getMeasurement(SyncMeasurement1.prototype.name,
                                 SyncMeasurement1.prototype.version);
     return this.enqueueStorageOperation(function recordSyncEvent() {
       return m.incrementDailyCounter(field);
     });
   },
 
+  _observeMigration: function(subject, topic, data) {
+    switch (topic) {
+      case "fxa-migration:state-changed":
+      case "fxa-migration:internal-state-changed": {
+        // We record both "user" and "internal" states in the same field.  This
+        // works for us as user state is always null when there is an internal
+        // state.
+        if (!data) {
+          return; // we don't count the |null| state
+        }
+        let m = this.getMeasurement(SyncMigrationMeasurement1.prototype.name,
+                                    SyncMigrationMeasurement1.prototype.version);
+        return this.enqueueStorageOperation(function() {
+          return m.setDailyLastText("state", data);
+        });
+      }
+
+      case "fxa-migration:internal-telemetry": {
+        // |data| is our field name.
+        let m = this.getMeasurement(SyncMigrationMeasurement1.prototype.name,
+                                    SyncMigrationMeasurement1.prototype.version);
+        return this.enqueueStorageOperation(function() {
+          switch (data) {
+            case "accepted":
+            case "declined":
+              return m.incrementDailyCounter(data);
+            case "unlinked":
+              return m.setDailyLastNumeric(data, 1);
+            default:
+              Cu.reportError("Unexpected migration field in sync healthreport provider: " + data);
+              return Promise.resolve();
+          }
+        });
+      }
+
+      default:
+        Cu.reportError("unexpected migration topic in sync healthreport provider: " + topic);
+        return;
+    }
+  },
+
   collectDailyData: function () {
     return this.storage.enqueueTransaction(this._populateDailyData.bind(this));
   },
 
   _populateDailyData: function* () {
     let m = this.getMeasurement(SyncMeasurement1.prototype.name,
                                 SyncMeasurement1.prototype.version);
 
--- a/services/sync/modules/notifications.js
+++ b/services/sync/modules/notifications.js
@@ -79,28 +79,31 @@ this.Notifications = {
   }
 };
 
 
 /**
  * A basic notification.  Subclass this to create more complex notifications.
  */
 this.Notification =
- function Notification(title, description, iconURL, priority, buttons) {
+function Notification(title, description, iconURL, priority, buttons, link) {
   this.title = title;
   this.description = description;
 
   if (iconURL)
     this.iconURL = iconURL;
 
   if (priority)
     this.priority = priority;
 
   if (buttons)
     this.buttons = buttons;
+
+  if (link)
+    this.link = link;
 }
 
 // We set each prototype property individually instead of redefining
 // the entire prototype to avoid blowing away existing properties
 // of the prototype like the the "constructor" property, which we use
 // to bind notification objects to their XBL representations.
 Notification.prototype.priority = Notifications.PRIORITY_INFO;
 Notification.prototype.iconURL = null;
new file mode 100644
--- /dev/null
+++ b/services/sync/tests/unit/test_healthreport_migration.js
@@ -0,0 +1,165 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+Cu.import("resource://gre/modules/Metrics.jsm", this);
+Cu.import("resource://gre/modules/Preferences.jsm", this);
+Cu.import("resource://gre/modules/Promise.jsm", this);
+Cu.import("resource://services-sync/healthreport.jsm", this);
+Cu.import("resource://services-sync/FxaMigrator.jsm", this);
+Cu.import("resource://testing-common/services/common/logging.js", this);
+Cu.import("resource://testing-common/services/healthreport/utils.jsm", this);
+
+
+function run_test() {
+  initTestLogging();
+
+  run_next_test();
+}
+
+add_task(function* test_no_data() {
+  let storage = yield Metrics.Storage("collect");
+  let provider = new SyncProvider();
+  yield provider.init(storage);
+
+  try {
+    // Initially nothing should be configured.
+    let now = new Date();
+    yield provider.collectDailyData();
+
+    let m = provider.getMeasurement("migration", 1);
+    let values = yield m.getValues();
+    Assert.equal(values.days.size, 0);
+    Assert.ok(!values.days.hasDay(now));
+  } finally {
+    yield provider.shutdown();
+    yield storage.close();
+  }
+});
+
+
+add_task(function* test_state() {
+  let storage = yield Metrics.Storage("collect");
+  let provider = new SyncProvider();
+  yield provider.init(storage);
+
+  try {
+    // Initially nothing should be configured.
+    let now = new Date();
+
+    let m = provider.getMeasurement("migration", 1);
+
+    // We record both a "user" and "internal" state in the same field.
+    // So simulate a "user" state first.
+    Services.obs.notifyObservers(null, "fxa-migration:state-changed",
+                                 fxaMigrator.STATE_USER_FXA_VERIFIED);
+
+    // Wait for storage to complete.
+    yield m.storage.enqueueOperation(() => {
+      return Promise.resolve();
+    });
+
+    let values = yield m.getValues();
+    Assert.equal(values.days.size, 1);
+    Assert.ok(values.days.hasDay(now));
+    let day = values.days.getDay(now);
+
+    Assert.ok(day.has("state"));
+    Assert.equal(day.get("state"), fxaMigrator.STATE_USER_FXA_VERIFIED);
+
+    // And an  internal state.
+    Services.obs.notifyObservers(null, "fxa-migration:internal-state-changed",
+                                 fxaMigrator.STATE_INTERNAL_WAITING_SYNC_COMPLETE);
+
+    // Wait for storage to complete.
+    yield m.storage.enqueueOperation(() => {
+      return Promise.resolve();
+    });
+
+    values = yield m.getValues();
+    Assert.equal(values.days.size, 1);
+    Assert.ok(values.days.hasDay(now));
+    day = values.days.getDay(now);
+    Assert.ok(day.has("state"));
+    Assert.equal(day.get("state"), fxaMigrator.STATE_INTERNAL_WAITING_SYNC_COMPLETE);
+  } finally {
+    yield provider.shutdown();
+    yield storage.close();
+  }
+});
+
+add_task(function* test_flags() {
+  let storage = yield Metrics.Storage("collect");
+  let provider = new SyncProvider();
+  yield provider.init(storage);
+
+  try {
+    // Initially nothing should be configured.
+    let now = new Date();
+
+    let m = provider.getMeasurement("migration", 1);
+
+    let record = function*(what) {
+      Services.obs.notifyObservers(null, "fxa-migration:internal-telemetry", what);
+      // Wait for storage to complete.
+      yield m.storage.enqueueOperation(Promise.resolve);
+      let values = yield m.getValues();
+      Assert.equal(values.days.size, 1);
+      return values.days.getDay(now);
+    }
+
+    let values = yield m.getValues();
+    Assert.equal(values.days.size, 1);
+    let day = values.days.getDay(now);
+    Assert.ok(!day.has(fxaMigrator.TELEMETRY_ACCEPTED));
+    Assert.ok(!day.has(fxaMigrator.TELEMETRY_DECLINED));
+    Assert.ok(!day.has(fxaMigrator.TELEMETRY_UNLINKED));
+
+    // let's send an unknown value to ensure our error mitigation works.
+    day = yield record("unknown");
+    Assert.ok(!day.has(fxaMigrator.TELEMETRY_ACCEPTED));
+    Assert.ok(!day.has(fxaMigrator.TELEMETRY_DECLINED));
+    Assert.ok(!day.has(fxaMigrator.TELEMETRY_UNLINKED));
+
+    // record an fxaMigrator.TELEMETRY_ACCEPTED state.
+    day = yield record(fxaMigrator.TELEMETRY_ACCEPTED);
+    Assert.ok(day.has(fxaMigrator.TELEMETRY_ACCEPTED));
+    Assert.ok(!day.has(fxaMigrator.TELEMETRY_DECLINED));
+    Assert.ok(!day.has(fxaMigrator.TELEMETRY_UNLINKED));
+    Assert.equal(day.get(fxaMigrator.TELEMETRY_ACCEPTED), 1);
+
+    // and again - it should get 2.
+    day = yield record(fxaMigrator.TELEMETRY_ACCEPTED);
+    Assert.equal(day.get(fxaMigrator.TELEMETRY_ACCEPTED), 2);
+
+    // record fxaMigrator.TELEMETRY_DECLINED - also a counter.
+    day = yield record(fxaMigrator.TELEMETRY_DECLINED);
+    Assert.ok(day.has(fxaMigrator.TELEMETRY_ACCEPTED));
+    Assert.ok(day.has(fxaMigrator.TELEMETRY_DECLINED));
+    Assert.ok(!day.has(fxaMigrator.TELEMETRY_UNLINKED));
+    Assert.equal(day.get(fxaMigrator.TELEMETRY_ACCEPTED), 2);
+    Assert.equal(day.get(fxaMigrator.TELEMETRY_DECLINED), 1);
+
+    day = yield record(fxaMigrator.TELEMETRY_DECLINED);
+    Assert.ok(day.has(fxaMigrator.TELEMETRY_ACCEPTED));
+    Assert.ok(day.has(fxaMigrator.TELEMETRY_DECLINED));
+    Assert.ok(!day.has(fxaMigrator.TELEMETRY_UNLINKED));
+    Assert.equal(day.get(fxaMigrator.TELEMETRY_ACCEPTED), 2);
+    Assert.equal(day.get(fxaMigrator.TELEMETRY_DECLINED), 2);
+
+    // and fxaMigrator.TELEMETRY_UNLINKED - this is conceptually a "daily bool".
+    // (ie, it's DAILY_LAST_NUMERIC_FIELD and only ever has |1| written to it)
+    day = yield record(fxaMigrator.TELEMETRY_UNLINKED);
+    Assert.ok(day.has(fxaMigrator.TELEMETRY_ACCEPTED));
+    Assert.ok(day.has(fxaMigrator.TELEMETRY_DECLINED));
+    Assert.ok(day.has(fxaMigrator.TELEMETRY_UNLINKED));
+    Assert.equal(day.get(fxaMigrator.TELEMETRY_UNLINKED), 1);
+    // and doing it again still leaves us with |1|
+    day = yield record(fxaMigrator.TELEMETRY_UNLINKED);
+    Assert.equal(day.get(fxaMigrator.TELEMETRY_UNLINKED), 1);
+  } finally {
+    yield provider.shutdown();
+    yield storage.close();
+  }
+});
--- a/services/sync/tests/unit/xpcshell.ini
+++ b/services/sync/tests/unit/xpcshell.ini
@@ -166,14 +166,17 @@ skip-if = debug
 [test_prefs_tracker.js]
 [test_tab_engine.js]
 [test_tab_store.js]
 [test_tab_tracker.js]
 
 [test_healthreport.js]
 skip-if = ! healthreport
 
+[test_healthreport_migration.js]
+skip-if = ! healthreport
+
 [test_warn_on_truncated_response.js]
 
 # FxA migration
 [test_block_sync.js]
 [test_fxa_migration.js]
 [test_fxa_migration_sentinel.js]
--- a/storage/src/mozStorageConnection.cpp
+++ b/storage/src/mozStorageConnection.cpp
@@ -46,16 +46,29 @@
 
 // Maximum size of the pages cache per connection.
 #define MAX_CACHE_SIZE_KIBIBYTES 2048 // 2 MiB
 
 #ifdef PR_LOGGING
 PRLogModuleInfo* gStorageLog = nullptr;
 #endif
 
+// Checks that the protected code is running on the main-thread only if the
+// connection was also opened on it.
+#ifdef DEBUG
+#define CHECK_MAINTHREAD_ABUSE() \
+  do { \
+    nsCOMPtr<nsIThread> mainThread = do_GetMainThread(); \
+    NS_WARN_IF_FALSE(threadOpenedOn == mainThread || !NS_IsMainThread(), \
+               "Using Storage synchronous API on main-thread, but the connection was opened on another thread."); \
+  } while(0)
+#else
+#define CHECK_MAINTHREAD_ABUSE() do { /* Nothing */ } while(0)
+#endif
+
 namespace mozilla {
 namespace storage {
 
 namespace {
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Variant Specialization Functions (variantToSQLiteT)
 
@@ -1417,16 +1430,17 @@ Connection::CreateAsyncStatement(const n
   statement.forget(&rawPtr);
   *_stmt = rawPtr;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 Connection::ExecuteSimpleSQL(const nsACString &aSQLStatement)
 {
+  CHECK_MAINTHREAD_ABUSE();
   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
 
   int srv = executeSql(mDBConn, PromiseFlatCString(aSQLStatement).get());
   return convertResultCode(srv);
 }
 
 NS_IMETHODIMP
 Connection::ExecuteAsync(mozIStorageBaseStatement **aStatements,
--- a/storage/src/mozStorageService.cpp
+++ b/storage/src/mozStorageService.cpp
@@ -342,32 +342,42 @@ Service::getConnections(/* inout */ nsTA
 void
 Service::minimizeMemory()
 {
   nsTArray<nsRefPtr<Connection> > connections;
   getConnections(connections);
 
   for (uint32_t i = 0; i < connections.Length(); i++) {
     nsRefPtr<Connection> conn = connections[i];
-    if (conn->connectionReady()) {
-      NS_NAMED_LITERAL_CSTRING(shrinkPragma, "PRAGMA shrink_memory");
-      nsCOMPtr<mozIStorageConnection> syncConn = do_QueryInterface(
-        NS_ISUPPORTS_CAST(mozIStorageAsyncConnection*, conn));
-      DebugOnly<nsresult> rv;
+    if (!conn->connectionReady())
+      continue;
+
+    NS_NAMED_LITERAL_CSTRING(shrinkPragma, "PRAGMA shrink_memory");
+    nsCOMPtr<mozIStorageConnection> syncConn = do_QueryInterface(
+      NS_ISUPPORTS_CAST(mozIStorageAsyncConnection*, conn));
+    bool onOpenedThread = false;
 
-      if (!syncConn) {
-        nsCOMPtr<mozIStoragePendingStatement> ps;
-        rv = connections[i]->ExecuteSimpleSQLAsync(shrinkPragma, nullptr,
-          getter_AddRefs(ps));
-      } else {
-        rv = connections[i]->ExecuteSimpleSQL(shrinkPragma);
-      }
-
-      MOZ_ASSERT(NS_SUCCEEDED(rv),
-        "Should have been able to purge sqlite caches");
+    if (!syncConn) {
+      // This is a mozIStorageAsyncConnection, it can only be used on the main
+      // thread, so we can do a straight API call.
+      nsCOMPtr<mozIStoragePendingStatement> ps;
+      DebugOnly<nsresult> rv =
+        conn->ExecuteSimpleSQLAsync(shrinkPragma, nullptr, getter_AddRefs(ps));
+      MOZ_ASSERT(NS_SUCCEEDED(rv), "Should have purged sqlite caches");
+    } else if (NS_SUCCEEDED(conn->threadOpenedOn->IsOnCurrentThread(&onOpenedThread)) &&
+               onOpenedThread) {
+      // We are on the opener thread, so we can just proceed.
+      conn->ExecuteSimpleSQL(shrinkPragma);
+    } else {
+      // We are on the wrong thread, the query should be executed on the
+      // opener thread, so we must dispatch to it.
+      nsCOMPtr<nsIRunnable> event =
+        NS_NewRunnableMethodWithArg<const nsCString>(
+          conn, &Connection::ExecuteSimpleSQL, shrinkPragma);
+      conn->threadOpenedOn->Dispatch(event, NS_DISPATCH_NORMAL);
     }
   }
 }
 
 void
 Service::shutdown()
 {
   NS_IF_RELEASE(sXPConnect);
--- a/testing/web-platform/meta/media-source/mediasource-config-change-mp4-a-bitrate.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-config-change-mp4-a-bitrate.html.ini
@@ -1,4 +1,11 @@
 [mediasource-config-change-mp4-a-bitrate.html]
   type: testharness
+  disabled:
+    if (os == "win") and (version != "5.1.2600"): occasional timeout
   [Tests mp4 audio-only bitrate changes.]
-    disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1106776
+    disabled:
+      if (os == "win") and (version != "5.1.2600"): occasional timeout.
+    expected:
+      if os == "mac": PASS
+      FAIL
+
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul
@@ -562,17 +562,17 @@ End of 5th\n\
       let measureButton = doc.getElementById("measureButton");
       let verbose = doc.getElementById("verbose");
       verbose.checked = aVerbose;
       measureButton.click();
 
       SimpleTest.waitForClipboard(
         function(aActual) {
           mostRecentActual = aActual;
-          return aActual === aExpected;
+          return aActual.trim() === aExpected.trim();
         },
         function() {
           synthesizeKey("A", {accelKey: true});
           synthesizeKey("C", {accelKey: true});
         },
         aNext,
         function() {
           ok(false, "pasted text doesn't match for " + aFrameId);
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory2.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory2.xul
@@ -101,17 +101,17 @@
     }
 
     SimpleTest.executeSoon(function() {
       let mostRecentActual;
       document.getElementById("amFrame").focus();
       SimpleTest.waitForClipboard(
         function(aActual) {
           mostRecentActual = aActual;
-          return aActual === aExpected;
+          return aActual.trim() === aExpected.trim();
         },
         function() {
           synthesizeKey("A", {accelKey: true});
           synthesizeKey("C", {accelKey: true});
         },
         aNext,
         function() {
           ok(false, "pasted text doesn't match");
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory3.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory3.xul
@@ -101,17 +101,17 @@
       function copyPasteAndCheck() {
         // Copy and paste frame contents, and filter out non-deterministic
         // differences.
         synthesizeKey("A", {accelKey: true});
         synthesizeKey("C", {accelKey: true});
         let actual = SpecialPowers.getClipboardData("text/unicode");
         actual = actual.replace(/\(pid \d+\)/g, "(pid NNN)");
 
-        if (actual === aExpected) {
+        if (actual.trim() === aExpected.trim()) {
           SimpleTest.ok(true, "Clipboard has the expected contents");
           aNext();
         } else {
           numFailures++;
           if (numFailures === maxFailures) {
             ok(false, "pasted text doesn't match");
             dump("******EXPECTED******\n");
             dump(aExpected);
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory4.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory4.xul
@@ -49,17 +49,17 @@
     function copyPasteAndCheck() {
       // Copy and paste frame contents, and filter out non-deterministic
       // differences.
       synthesizeKey("A", {accelKey: true});
       synthesizeKey("C", {accelKey: true});
       let actual = SpecialPowers.getClipboardData("text/unicode");
       actual = actual.replace(/\(pid \d+\)/, "(pid NNN)");
 
-      if (actual === aExpected) {
+      if (actual.trim() === aExpected.trim()) {
         SimpleTest.ok(true, "Clipboard has the expected contents");
         aNext();
       } else {
         numFailures++;
         if (numFailures === maxFailures) {
           ok(false, "pasted text doesn't match");
           dump("******EXPECTED******\n");
           dump(aExpected);
--- a/toolkit/themes/linux/global/notification.css
+++ b/toolkit/themes/linux/global/notification.css
@@ -1,28 +1,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/. */
 
 @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
 
+notification,
+.messageText > .text-link {
+  color: InfoText !important;
+}
+
 notification {
   background-color: InfoBackground;
-  color: InfoText;
   text-shadow: none;
 }
 
+notification[type="info"],
+notification[type="info"] .messageText > .text-link {
+  color: -moz-DialogText !important;
+}
+
 notification[type="info"] {
   background-color: -moz-Dialog;
-  color: -moz-DialogText;
+}
+
+notification[type="critical"],
+notification[type="critical"] .messageText > .text-link {
+  color: white !important;
 }
 
 notification[type="critical"] {
   background-image: linear-gradient(rgb(212,0,0), rgb(152,0,0));
-  color: white;
+}
+
+.messageText > .text-link {
+  text-decoration: underline;
 }
 
 .notification-inner {
   padding-top: 1px;
   padding-bottom: 1px;
 }
 
 .messageText {
--- a/toolkit/themes/osx/global/notification.css
+++ b/toolkit/themes/osx/global/notification.css
@@ -5,37 +5,53 @@
 %include shared.inc
 @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
 
 notification {
   padding: 3px 3px 4px;
   text-shadow: none;
 }
 
+notification[type="info"],
+notification[type="info"] .messageText > .text-link {
+  color: rgba(255,255,255,0.95) !important;
+}
+
 notification[type="info"] {
-  color: rgba(255,255,255,0.95);
   background: url("chrome://global/skin/notification/info-bar-background.png") #404040 repeat-x top left;
   border-top: 1px solid #707070;
   border-bottom: 1px solid #2a2a2a;
 }
 
+notification[type="warning"],
+notification[type="warning"] .messageText > .text-link {
+  color: rgba(0,0,0,0.95) !important;
+}
+
 notification[type="warning"] {
-  color: rgba(0,0,0,0.95);
   background: url("chrome://global/skin/notification/warning-bar-background.png") #ffc703 repeat-x top left;
   border-top: 1px solid #ffe970;
   border-bottom: 1px solid #bf8a01;
 }
 
+notification[type="critical"],
+notification[type="critical"] .messageText > .text-link {
+  color: rgba(255,255,255,0.95) !important;
+}
+
 notification[type="critical"] {
-  color: rgba(255,255,255,0.95);
   background: url("chrome://global/skin/notification/critical-bar-background.png") #980000 repeat-x top left;
   border-top: 1px solid #e35959;
   border-bottom: 1px solid #5d0000;
 }
 
+.messageText > .text-link {
+  text-decoration: underline;
+}
+
 .messageImage {
   width: 16px;
   height: 16px;
   margin: 0 4px;
 }
 
 /* Default icons for notifications */
 
--- a/toolkit/themes/windows/global/notification.css
+++ b/toolkit/themes/windows/global/notification.css
@@ -1,28 +1,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/. */
 
 @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
 
+notification,
+.messageText > .text-link {
+  color: InfoText !important;
+}
+
 notification {
   background-color: InfoBackground;
-  color: InfoText;
   text-shadow: none;
 }
 
+notification[type="info"],
+notification[type="info"] .messageText > .text-link {
+  color: -moz-DialogText !important;
+}
+
 notification[type="info"] {
   background-color: -moz-Dialog;
-  color: -moz-DialogText;
+}
+
+notification[type="critical"],
+notification[type="critical"] .messageText > .text-link {
+  color: white !important;
 }
 
 notification[type="critical"] {
   background-image: linear-gradient(rgb(212,0,0), rgb(152,0,0));
-  color: white;
+}
+
+.messageText > .text-link {
+  text-decoration: underline;
 }
 
 .messageImage {
   width: 16px;
   height: 16px;
   -moz-margin-start: 6px;
   -moz-margin-end: 1px;
 }