Bug 1120967 - Broken middle/right click on links via about:preferences pages; r?jaws draft
authorFischer.json <fischer.json@gmail.com>
Fri, 19 Aug 2016 17:32:31 +0800
changeset 411000 784e338a285db48255fcdfcecd22d023361a836f
parent 408409 b7f7ae14590aced450bb0b0469dfb38edd2c0ace
child 530652 804ae35c843e89c1709e10c5257c4ae4c61648ec
push id28813
push userbmo:fliu@mozilla.com
push dateWed, 07 Sep 2016 11:21:01 +0000
reviewersjaws
bugs1120967
milestone51.0a1
Bug 1120967 - Broken middle/right click on links via about:preferences pages; r?jaws MozReview-Commit-ID: FejMTP3invj
browser/base/content/browser.js
browser/base/content/nsContextMenu.js
browser/base/content/sync/utils.js
browser/base/content/test/general/browser.ini
browser/base/content/test/general/browser_contextmenu.js
browser/base/content/test/general/subtst_contextmenu_xul.xul
browser/base/content/utilityOverlay.js
browser/components/preferences/in-content/advanced.xul
browser/components/preferences/in-content/applications.xul
browser/components/preferences/in-content/content.xul
browser/components/preferences/in-content/main.xul
browser/components/preferences/in-content/preferences.js
browser/components/preferences/in-content/privacy.xul
browser/components/preferences/in-content/search.js
browser/components/preferences/in-content/search.xul
browser/components/preferences/in-content/security.xul
browser/components/preferences/in-content/sync.js
browser/components/preferences/in-content/sync.xul
browser/themes/shared/incontentprefs/preferences.inc.css
toolkit/mozapps/update/content/history.xul
toolkit/themes/shared/in-content/common.inc.css
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3723,21 +3723,24 @@ const BrowserSearch = {
 
   /**
    * Returns the search bar element if it is present in the toolbar, null otherwise.
    */
   get searchBar() {
     return document.getElementById("searchbar");
   },
 
+  get searchEnginesURL() {
+    return formatURL("browser.search.searchEnginesURL", true);
+  },
+
   loadAddEngines: function BrowserSearch_loadAddEngines() {
     var newWindowPref = gPrefService.getIntPref("browser.link.open_newwindow");
     var where = newWindowPref == 3 ? "tab" : "window";
-    var searchEnginesURL = formatURL("browser.search.searchEnginesURL", true);
-    openUILinkIn(searchEnginesURL, where);
+    openUILinkIn(this.searchEnginesURL, where);
   },
 
   get _isExtendedTelemetryEnabled() {
     return Services.prefs.getBoolPref("toolkit.telemetry.enabled");
   },
 
   _getSearchEngineId: function (engine) {
     if (engine && engine.identifier) {
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -598,18 +598,19 @@ nsContextMenu.prototype = {
     if (this.isRemote) {
       aNode = gContextMenuContentData.event.target;
       aRangeParent = gContextMenuContentData.event.rangeParent;
       aRangeOffset = gContextMenuContentData.event.rangeOffset;
       editFlags = gContextMenuContentData.editFlags;
     }
 
     const xulNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-    if (aNode.namespaceURI == xulNS ||
-        aNode.nodeType == Node.DOCUMENT_NODE) {
+    if (aNode.nodeType == Node.DOCUMENT_NODE ||
+        // Not display on XUL element but relax for <label class="text-link">
+        (aNode.namespaceURI == xulNS && !isXULTextLinkLabel(aNode))) {
       this.shouldDisplay = false;
       return;
     }
 
     // Initialize contextual info.
     this.onImage           = false;
     this.onLoadedImage     = false;
     this.onCompletedImage  = false;
@@ -790,17 +791,18 @@ nsContextMenu.prototype = {
     const XMLNS = "http://www.w3.org/XML/1998/namespace";
     var elem = this.target;
     while (elem) {
       if (elem.nodeType == Node.ELEMENT_NODE) {
         // Link?
         if (!this.onLink &&
             // Be consistent with what hrefAndLinkNodeForClickEvent
             // does in browser.js
-             ((elem instanceof HTMLAnchorElement && elem.href) ||
+             (isXULTextLinkLabel(elem) ||
+              (elem instanceof HTMLAnchorElement && elem.href) ||
               (elem instanceof HTMLAreaElement && elem.href) ||
               elem instanceof HTMLLinkElement ||
               elem.getAttributeNS("http://www.w3.org/1999/xlink", "type") == "simple")) {
 
           // Target is a link or a descendant of a link.
           this.onLink = true;
 
           // Remember corresponding element.
@@ -890,16 +892,23 @@ nsContextMenu.prototype = {
           InlineSpellCheckerUI.init(editingSession.getEditorForWindow(targetWin));
           InlineSpellCheckerUI.initFromEvent(aRangeParent, aRangeOffset);
         }
         var canSpell = InlineSpellCheckerUI.canSpellCheck && this.canSpellCheck;
         this.showItem("spell-check-enabled", canSpell);
         this.showItem("spell-separator", canSpell);
       }
     }
+
+    function isXULTextLinkLabel(node) {
+      return node.namespaceURI == xulNS &&
+             node.tagName == "label" &&
+             node.classList.contains('text-link') &&
+             node.href;
+    }
   },
 
   // Returns the computed style attribute for the given element.
   getComputedStyle: function(aElem, aProp) {
     return aElem.ownerDocument
                 .defaultView
                 .getComputedStyle(aElem, "").getPropertyValue(aProp);
   },
--- a/browser/base/content/sync/utils.js
+++ b/browser/base/content/sync/utils.js
@@ -72,24 +72,32 @@ var gSyncUtils = {
     if (Weave.Utils.ensureMPUnlocked())
       this.openChange("UpdatePassphrase");
   },
 
   resetPassword: function () {
     this._openLink(Weave.Service.pwResetURL);
   },
 
-  openToS: function () {
+  get tosURL() {
     let root = this.fxAccountsEnabled ? "fxa." : "";
-    this._openLink(Weave.Svc.Prefs.get(root + "termsURL"));
+    return  Weave.Svc.Prefs.get(root + "termsURL");
+  },
+
+  openToS: function () {
+    this._openLink(this.tosURL);
+  },
+
+  get privacyPolicyURL() {
+    let root = this.fxAccountsEnabled ? "fxa." : "";
+    return  Weave.Svc.Prefs.get(root + "privacyURL");
   },
 
   openPrivacyPolicy: function () {
-    let root = this.fxAccountsEnabled ? "fxa." : "";
-    this._openLink(Weave.Svc.Prefs.get(root + "privacyURL"));
+    this._openLink(this.privacyPolicyURL);
   },
 
   /**
    * Prepare an invisible iframe with the passphrase backup document.
    * Used by both the print and saving methods.
    *
    * @param elid : ID of the form element containing the passphrase.
    * @param callback : Function called once the iframe has loaded.
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -82,16 +82,17 @@ support-files =
   ssl_error_reports.sjs
   popup_blocker.html
   print_postdata.sjs
   searchSuggestionEngine.sjs
   searchSuggestionEngine.xml
   searchSuggestionEngine2.xml
   subtst_contextmenu.html
   subtst_contextmenu_input.html
+  subtst_contextmenu_xul.xul
   test-mixedcontent-securityerrors.html
   test_bug435035.html
   test_bug462673.html
   test_bug628179.html
   test_bug839103.html
   test_bug959531.html
   test_process_flags_chrome.html
   title_test.svg
--- a/browser/base/content/test/general/browser_contextmenu.js
+++ b/browser/base/content/test/general/browser_contextmenu.js
@@ -5,28 +5,58 @@ let LOGIN_FILL_ITEMS = [
   "---", null,
   "fill-login", null,
     [
       "fill-login-no-logins", false,
       "---", null,
       "fill-login-saved-passwords", true
     ], null,
 ];
-
 let hasPocket = Services.prefs.getBoolPref("extensions.pocket.enabled");
 let hasContainers = Services.prefs.getBoolPref("privacy.userContext.enabled");
 
-add_task(function* test_setup() {
-  const example_base = "http://example.com/browser/browser/base/content/test/general/";
-  const url = example_base + "subtst_contextmenu.html";
+const example_base = "http://example.com/browser/browser/base/content/test/general/";
+const chrome_base = "chrome://mochitests/content/browser/browser/base/content/test/general/";
+
+Services.scriptloader.loadSubScript(chrome_base + "contextmenu_common.js", this);
+
+// Below are test cases for XUL element
+add_task(function* test_xul_text_link_label() {
+  let  url = chrome_base + "subtst_contextmenu_xul.xul";
+
   yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
 
-  const chrome_base = "chrome://mochitests/content/browser/browser/base/content/test/general/";
-  const contextmenu_common = chrome_base + "contextmenu_common.js";
-  Services.scriptloader.loadSubScript(contextmenu_common, this);
+  yield test_contextmenu("#test-xul-text-link-label",
+    ["context-openlinkintab", true,
+     ...(hasContainers ? ["context-openlinkinusercontext-menu", true] : []),
+     // We need a blank entry here because the containers submenu is
+     // dynamically generated with no ids.
+     ...(hasContainers ? ["", null] : []),
+     "context-openlink",      true,
+     "context-openlinkprivate", true,
+     "---",                   null,
+     "context-bookmarklink",  true,
+     "context-savelink",      true,
+     ...(hasPocket ? ["context-savelinktopocket", true] : []),
+     "context-copylink",      true,
+     "context-searchselect",  true
+    ]
+  );
+
+  // Clean up so won't affect HTML element test cases
+  lastElementSelector = null;
+  gBrowser.removeCurrentTab();
+});
+
+// Below are test cases for HTML element
+
+add_task(function* test_setup_html() {
+  let url = example_base + "subtst_contextmenu.html";
+
+  yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
 
   yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
     let doc = content.document;
     let videoIframe = doc.querySelector("#test-video-in-iframe");
     let video = videoIframe.contentDocument.querySelector("video");
     let awaitPause = ContentTaskUtils.waitForEvent(video, "pause");
     video.pause();
     yield awaitPause;
@@ -921,17 +951,17 @@ add_task(function* test_link_sendlinktod
       *onContextMenuShown() {
         yield openMenuItemSubmenu("context-sendlinktodevice");
       }
     });
 
   restoreRemoteClients(oldGetter);
 });
 
-add_task(function* test_cleanup() {
+add_task(function* test_cleanup_html() {
   gBrowser.removeCurrentTab();
 });
 
 /**
  * Selects the text of the element that matches the provided `selector`
  *
  * @param {String} selector
  *        A selector passed to querySelector to find
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/subtst_contextmenu_xul.xul
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<!-- 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/. -->
+
+<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+      xmlns:html="http://www.w3.org/1999/xhtml">
+  <label id="test-xul-text-link-label" class="text-link" value="XUL text-link label" href="https://www.mozilla.com"/>
+</page>
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -819,23 +819,26 @@ function openNewWindowWith(aURL, aDocume
              { charset: originCharset,
                postData: aPostData,
                allowThirdPartyFixup: aAllowThirdPartyFixup,
                referrerURI: aReferrer,
                referrerPolicy: aReferrerPolicy,
              });
 }
 
-// aCalledFromModal is optional
-function openHelpLink(aHelpTopic, aCalledFromModal, aWhere) {
+function getHelpLinkURL(aHelpTopic) {
   var url = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
                       .getService(Components.interfaces.nsIURLFormatter)
                       .formatURLPref("app.support.baseURL");
-  url += aHelpTopic;
+  return url + aHelpTopic;
+}
 
+// aCalledFromModal is optional
+function openHelpLink(aHelpTopic, aCalledFromModal, aWhere) {
+  var url = getHelpLinkURL(aHelpTopic);
   var where = aWhere;
   if (!aWhere)
     where = aCalledFromModal ? "window" : "tab";
 
   openUILinkIn(url, where);
 }
 
 function openPrefsHelp() {
--- a/browser/components/preferences/in-content/advanced.xul
+++ b/browser/components/preferences/in-content/advanced.xul
@@ -115,18 +115,17 @@
 #endif
   <stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/>
 
 <hbox id="header-advanced"
       class="header"
       hidden="true"
       data-category="paneAdvanced">
   <label class="header-name" flex="1">&paneAdvanced.title;</label>
-  <button class="help-button"
-          aria-label="&helpButton.label;"/>
+  <html:a class="help-button" target="_blank" aria-label="&helpButton.label;"></html:a>
 </hbox>
 
 <tabbox id="advancedPrefs"
         handleCtrlTab="false"
         handleCtrlPageUpDown="false"
         flex="1"
         data-category="paneAdvanced"
         hidden="true">
--- a/browser/components/preferences/in-content/applications.xul
+++ b/browser/components/preferences/in-content/applications.xul
@@ -53,23 +53,22 @@
 </preferences>
 
 <keyset>
   <!-- These <key>s have oncommand attributes because of bug 371900 -->
   <key key="&focusSearch1.key;" modifiers="accel" id="focusSearch1" oncommand=";"/>
   <key key="&focusSearch2.key;" modifiers="accel" id="focusSearch2" oncommand=";"/>
 </keyset>
 
-<hbox id="header-application"
+<hbox id="header-applications"
       class="header"
       hidden="true"
       data-category="paneApplications">
   <label class="header-name" flex="1">&paneApplications.title;</label>
-  <button class="help-button"
-          aria-label="&helpButton.label;"/>
+  <html:a class="help-button" target="_blank" aria-label="&helpButton.label;"></html:a>
 </hbox>
 
 <vbox id="applicationsContent"
       data-category="paneApplications"
       hidden="true"
       flex="1">
   <hbox>
     <textbox id="filter" flex="1"
--- a/browser/components/preferences/in-content/content.xul
+++ b/browser/components/preferences/in-content/content.xul
@@ -32,18 +32,17 @@
 <script type="application/javascript"
         src="chrome://browser/content/preferences/in-content/content.js"/>
 
 <hbox id="header-content"
       class="header"
       hidden="true"
       data-category="paneContent">
   <label class="header-name" flex="1">&paneContent.title;</label>
-  <button class="help-button"
-          aria-label="&helpButton.label;"/>
+  <html:a class="help-button" target="_blank" aria-label="&helpButton.label;"></html:a>
 </hbox>
 
 <groupbox id="drmGroup" data-category="paneContent" hidden="true">
   <caption><label>&drmContent.label;</label></caption>
   <grid id="contentGrid2">
     <columns>
       <column flex="1"/>
       <column/>
--- a/browser/components/preferences/in-content/main.xul
+++ b/browser/components/preferences/in-content/main.xul
@@ -110,18 +110,17 @@
                 type="bool"/>
 </preferences>
 
 <hbox id="header-general"
       class="header"
       hidden="true"
       data-category="paneGeneral">
   <label class="header-name" flex="1">&paneGeneral.title;</label>
-  <button class="help-button"
-          aria-label="&helpButton.label;"/>
+  <html:a class="help-button" target="_blank" aria-label="&helpButton.label;"></html:a>
 </hbox>
 
 <!-- Startup -->
 <groupbox id="startupGroup"
           data-category="paneGeneral"
           hidden="true">
   <caption><label>&startup.label;</label></caption>
 
--- a/browser/components/preferences/in-content/preferences.js
+++ b/browser/components/preferences/in-content/preferences.js
@@ -73,19 +73,23 @@ function init_all() {
   init_dynamic_padding();
 
   var initFinished = new CustomEvent("Initialized", {
     'bubbles': true,
     'cancelable': true
   });
   document.dispatchEvent(initFinished);
 
-  let helpCmds = document.querySelectorAll(".help-button");
-  for (let helpCmd of helpCmds)
-    helpCmd.addEventListener("command", helpButtonCommand);
+  categories = categories.querySelectorAll("richlistitem.category");
+  for (let category of categories) {
+    let name = internalPrefCategoryNameToFriendlyName(category.value);
+    let helpSelector = `#header-${name} > .help-button`;
+    let helpButton = document.querySelector(helpSelector);
+    helpButton.setAttribute("href", getHelpLinkURL(category.getAttribute("helpTopic")));
+  }
 
   // Wait until initialization of all preferences are complete before
   // notifying observers that the UI is now ready.
   Services.obs.notifyObservers(window, "advanced-pane-loaded", null);
 }
 
 // Make the space above the categories list shrink on low window heights
 function init_dynamic_padding() {
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -75,18 +75,17 @@
               type="bool"/>
 </preferences>
 
 <hbox id="header-privacy"
       class="header"
       hidden="true"
       data-category="panePrivacy">
   <label class="header-name" flex="1">&panePrivacy.title;</label>
-  <button class="help-button"
-          aria-label="&helpButton.label;"/>
+  <html:a class="help-button" target="_blank" aria-label="&helpButton.label;"></html:a>
 </hbox>
 
 <!-- Tracking -->
 <groupbox id="trackingGroup" data-category="panePrivacy" hidden="true">
   <vbox id="trackingprotectionbox" hidden="true">
     <hbox align="start">
       <vbox>
         <caption><label>&trackingProtectionHeader.label;
--- a/browser/components/preferences/in-content/search.js
+++ b/browser/components/preferences/in-content/search.js
@@ -31,16 +31,21 @@ var gSearchPane = {
   },
 
   init: function ()
   {
     gEngineView = new EngineView(new EngineStore());
     document.getElementById("engineList").view = gEngineView;
     this.buildDefaultEngineDropDown();
 
+    let addEnginesLink = document.getElementById("addEngines");
+    let searchEnginesURL = Services.wm.getMostRecentWindow('navigator:browser')
+                                      .BrowserSearch.searchEnginesURL;
+    addEnginesLink.setAttribute("href", searchEnginesURL);
+
     window.addEventListener("click", this, false);
     window.addEventListener("command", this, false);
     window.addEventListener("dragstart", this, false);
     window.addEventListener("keypress", this, false);
     window.addEventListener("select", this, false);
     window.addEventListener("blur", this, true);
 
     Services.obs.addObserver(this, "browser-search-engine-modified", false);
@@ -118,20 +123,16 @@ var gSearchPane = {
           if (engineList.inputField.hidden && engineList.view) {
             let selection = engineList.view.selection;
             if (selection.count > 0) {
               selection.toggleSelect(selection.currentIndex);
             }
             engineList.blur();
           }
         }
-        if (aEvent.target.id == "addEngines" && aEvent.button == 0) {
-          Services.wm.getMostRecentWindow('navigator:browser')
-                     .BrowserSearch.loadAddEngines();
-        }
         break;
       case "command":
         switch (aEvent.target.id) {
           case "":
             if (aEvent.target.parentNode &&
                 aEvent.target.parentNode.parentNode &&
                 aEvent.target.parentNode.parentNode.id == "defaultEngine") {
               gSearchPane.setDefaultEngine();
--- a/browser/components/preferences/in-content/search.xul
+++ b/browser/components/preferences/in-content/search.xul
@@ -23,18 +23,17 @@
 
     <stringbundle id="engineManagerBundle" src="chrome://browser/locale/engineManager.properties"/>
 
     <hbox id="header-search"
           class="header"
           hidden="true"
           data-category="paneSearch">
       <label class="header-name" flex="1">&paneSearch.title;</label>
-      <button class="help-button"
-              aria-label="&helpButton.label;"/>
+      <html:a class="help-button" target="_blank" aria-label="&helpButton.label;"></html:a>
     </hbox>
 
     <!-- Default Search Engine -->
     <groupbox id="defaultEngineGroup" align="start" data-category="paneSearch">
       <caption label="&defaultSearchEngine.label;"/>
       <label>&chooseYourDefaultSearchEngine.label;</label>
       <menulist id="defaultEngine">
         <menupopup/>
--- a/browser/components/preferences/in-content/security.xul
+++ b/browser/components/preferences/in-content/security.xul
@@ -48,18 +48,17 @@
 
 </preferences>
 
 <hbox id="header-security"
       class="header"
       hidden="true"
       data-category="paneSecurity">
   <label class="header-name" flex="1">&paneSecurity.title;</label>
-  <button class="help-button"
-          aria-label="&helpButton.label;"/>
+  <html:a class="help-button" target="_blank" aria-label="&helpButton.label;"></html:a>
 </hbox>
 
 <!-- addons, forgery (phishing) UI -->
 <groupbox id="addonsPhishingGroup" data-category="paneSecurity" hidden="true">
   <caption><label>&general.label;</label></caption>
 
   <hbox id="addonInstallBox">
     <checkbox id="warnAddonInstall"
--- a/browser/components/preferences/in-content/sync.js
+++ b/browser/components/preferences/in-content/sync.js
@@ -137,18 +137,29 @@ var gSyncPane = {
     });
 
     XPCOMUtils.defineLazyGetter(this, '_accountsStringBundle', () => {
       return Services.strings.createBundle("chrome://browser/locale/accounts.properties");
     });
 
     let url = Services.prefs.getCharPref("identity.mobilepromo.android") + "sync-preferences";
     document.getElementById("fxaMobilePromo-android").setAttribute("href", url);
+    document.getElementById("fxaMobilePromo-android-hasFxaAccount").setAttribute("href", url);
     url = Services.prefs.getCharPref("identity.mobilepromo.ios") + "sync-preferences";
     document.getElementById("fxaMobilePromo-ios").setAttribute("href", url);
+    document.getElementById("fxaMobilePromo-ios-hasFxaAccount").setAttribute("href", url);
+
+    document.getElementById("tosPP-small-ToS").setAttribute("href", gSyncUtils.tosURL);
+    document.getElementById("tosPP-normal-ToS").setAttribute("href", gSyncUtils.tosURL);
+    document.getElementById("tosPP-small-PP").setAttribute("href", gSyncUtils.privacyPolicyURL);
+    document.getElementById("tosPP-normal-PP").setAttribute("href", gSyncUtils.privacyPolicyURL);
+
+    fxAccounts.promiseAccountsManageURI(this._getEntryPoint()).then(url => {
+      document.getElementById("verifiedManage").setAttribute("href", url);
+    });
 
     this.updateWeavePrefs();
 
     this._initProfileImageUI();
   },
 
   _toggleComputerNameControls: function(editMode) {
     let textbox = document.getElementById("fxaSyncComputerName");
@@ -235,18 +246,16 @@ var gSyncPane = {
       this._toggleComputerNameControls(false);
       this._updateComputerNameValue(true);
       this._focusAfterComputerNameTextbox();
     });
     setEventListener("unlinkDevice", "click", function () {
       gSyncPane.startOver(true);
       return false;
     });
-    setEventListener("tosPP-normal-ToS", "click", gSyncPane.openToS);
-    setEventListener("tosPP-normal-PP", "click", gSyncPane.openPrivacyPolicy);
     setEventListener("loginErrorUpdatePass", "click", function () {
       gSyncPane.updatePass();
       return false;
     });
     setEventListener("loginErrorResetPass", "click", function () {
       gSyncPane.resetPass();
       return false;
     });
@@ -271,18 +280,16 @@ var gSyncPane = {
       /* no warning as account can't have previously synced */
       gSyncPane.unlinkFirefoxAccount(false);
     });
     setEventListener("rejectReSignIn", "command",
       gSyncPane.reSignIn);
     setEventListener("rejectUnlinkFxaAccount", "command", function () {
       gSyncPane.unlinkFirefoxAccount(true);
     });
-    setEventListener("tosPP-small-ToS", "click", gSyncPane.openToS);
-    setEventListener("tosPP-small-PP", "click", gSyncPane.openPrivacyPolicy);
     setEventListener("fxaSyncComputerName", "keypress", function (e) {
       if (e.keyCode == KeyEvent.DOM_VK_RETURN) {
         document.getElementById("fxaSaveChangeDeviceName").click();
       } else if (e.keyCode == KeyEvent.DOM_VK_ESCAPE) {
         document.getElementById("fxaCancelChangeDeviceName").click();
       }
     });
   },
@@ -518,26 +525,16 @@ var gSyncPane = {
     let browser = window.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIWebNavigation)
                         .QueryInterface(Ci.nsIDocShell)
                         .chromeEventHandler;
     // And tell it to load our URL.
     browser.loadURI(url);
   },
 
-  openPrivacyPolicy: function(aEvent) {
-    aEvent.stopPropagation();
-    gSyncUtils.openPrivacyPolicy();
-  },
-
-  openToS: function(aEvent) {
-    aEvent.stopPropagation();
-    gSyncUtils.openToS();
-  },
-
   signUp: function() {
     this._openAboutAccounts("signup");
   },
 
   signIn: function() {
     this._openAboutAccounts("signin");
   },
 
--- a/browser/components/preferences/in-content/sync.xul
+++ b/browser/components/preferences/in-content/sync.xul
@@ -30,18 +30,17 @@
 <script type="application/javascript"
         src="chrome://browser/content/sync/utils.js"/>
 
 <hbox id="header-sync"
       class="header"
       hidden="true"
       data-category="paneSync">
   <label class="header-name" flex="1">&paneSync.title;</label>
-  <button class="help-button"
-          aria-label="&helpButton.label;"/>
+  <html:a class="help-button text-link" target="_blank" aria-label="&helpButton.label;"></html:a>
 </hbox>
 
 <deck id="weavePrefsDeck" data-category="paneSync" hidden="true">
   <!-- These panels are for the "legacy" sync provider -->
   <vbox id="noAccount" align="center">
     <spacer flex="1"/>
     <description id="syncDesc">
       &weaveDesc.label;
@@ -229,17 +228,16 @@
                     tooltiptext="&profilePicture.tooltip;"/>
               </vbox>
               <vbox flex="1" pack="center">
                 <label id="fxaDisplayName" hidden="true"/>
                 <label id="fxaEmailAddress1"/>
                 <hbox class="fxaAccountBoxButtons" align="center">
                   <button id="fxaUnlinkButton" label="&disconnect.label;"/>
                   <label id="verifiedManage" class="text-link"
-                         onclick="gSyncPane.openManageFirefoxAccount(event);"
                          onkeypress="gSyncPane.openManageFirefoxAccount(event);"><!--
                   -->&verifiedManage.label;</label>
                 </hbox>
               </vbox>
             </hbox>
 
             <!-- logged in to an unverified account -->
             <hbox id="fxaLoginUnverified" class="fxaAccountBox">
@@ -335,22 +333,20 @@
           <button id="fxaSaveChangeDeviceName"
                   label="&saveChangeSyncDeviceName.label;"
                   hidden="true"/>
         </hbox>
       </hbox>
     </groupbox>
     <label class="fxaMobilePromo">
         &mobilePromo3.start;<!-- We put these comments to avoid inserting white spaces
-        --><label class="androidLink text-link"
-                  href="https://www.mozilla.org/firefox/android/?utm_source=firefox-browser&amp;utm_medium=firefox-browser&amp;utm_campaign=sync-preferences"><!--
+        --><label class="androidLink text-link" id="fxaMobilePromo-android-hasFxaAccount"><!--
         -->&mobilePromo3.androidLink;</label><!--
         -->&mobilePromo3.iOSBefore;<!--
-        --><label class="iOSLink text-link"
-                  href="https://www.mozilla.org/firefox/ios/?utm_source=firefox-browser&amp;utm_medium=firefox-browser&amp;utm_campaign=sync-preferences"><!--
+        --><label class="iOSLink text-link" id="fxaMobilePromo-ios-hasFxaAccount"><!--
         -->&mobilePromo3.iOSLink;</label><!--
         -->&mobilePromo3.end;
     </label>
     <vbox id="tosPP-small" align="start">
       <label id="tosPP-small-ToS" class="text-link">
         &prefs.tosLink.label;
       </label>
       <label id="tosPP-small-PP" class="text-link">
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -97,16 +97,25 @@ treecol {
 
 @media (max-width: 800px) {
   .category-name {
     display: none;
   }
 }
 
 /* header */
+.header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+
+.header[hidden=true] {
+  display: none;
+}
 
 #header-advanced {
   border-bottom: none;
   padding-bottom: 0;
 }
 
 /* General Pane */
 
--- a/toolkit/mozapps/update/content/history.xul
+++ b/toolkit/mozapps/update/content/history.xul
@@ -19,22 +19,21 @@
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         style="width: 35em;"
         buttons="cancel"
         defaultButton="cancel"
         buttonlabelcancel="&closebutton.label;"
         title="&history.title;"
         onload="gUpdateHistory.onLoad();">
 
-  <script type="application/javascript" 
+  <script type="application/javascript"
           src="chrome://mozapps/content/update/history.js"/>
-          
-  <stringbundle id="updateBundle" 
+
+  <stringbundle id="updateBundle"
                 src="chrome://mozapps/locale/update/updates.properties"/>
-  
+
   <label>&history.intro;</label>
   <separator class="thin"/>
   <richlistbox id="historyItems" flex="1">
     <label>&noupdates.label;</label>
   </richlistbox>
   <separator class="thin"/>
 </dialog>
-
--- a/toolkit/themes/shared/in-content/common.inc.css
+++ b/toolkit/themes/shared/in-content/common.inc.css
@@ -292,16 +292,36 @@ xul|*.help-button > xul|*.button-box > x
   width: 16px;
   height: 16px;
 }
 
 xul|*.help-button > xul|*.button-box > xul|*.button-text {
   display: none;
 }
 
+html|*.help-button {
+  width: 16px;
+  height: 16px;
+  border: 0;
+  padding: 0;
+  display: inline-block;
+  background-image: url("chrome://global/skin/in-content/help-glyph.svg#help");
+  background-repeat: no-repeat;
+  background-position: center center;
+  background-size: contain;
+}
+
+html|*.help-button:hover {
+  background-image: url("chrome://global/skin/in-content/help-glyph.svg#help-hover");
+}
+
+html|*.help-button:hover:active {
+  background-image: url("chrome://global/skin/in-content/help-glyph.svg#help-pressed");
+}
+
 xul|*.spinbuttons-button {
   min-height: initial;
   margin-inline-start: 10px !important;
   margin-inline-end: 2px !important;
 }
 
 xul|*.spinbuttons-up {
   margin-top: 2px !important;