Backed out changeset 6aa63fcf5c4e (bug 1373853) for leaking preferences windows, e.g. after browser-chrome's browser/base/content/test/general/browser_bug735471.js ran. r=backout
authorSebastian Hengst <archaeopteryx@coole-files.de>
Wed, 20 Sep 2017 21:14:28 +0200
changeset 434020 50ed4d668bf776fc022d8306fe90e2c6e6eca0b8
parent 434019 802042e3b5e061a3e832289021e02e6ac7718377
child 434021 a88a995faeedd735e3d3e737e17de2e2e46ea6ee
push id1567
push userjlorenzo@mozilla.com
push dateThu, 02 Nov 2017 12:36:05 +0000
treeherdermozilla-release@e512c14a0406 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs1373853, 735471
milestone57.0a1
backs out6aa63fcf5c4ec13f9bef8ff21d9fea7a04d5436a
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out changeset 6aa63fcf5c4e (bug 1373853) for leaking preferences windows, e.g. after browser-chrome's browser/base/content/test/general/browser_bug735471.js ran. r=backout
browser/components/preferences/in-content/main.js
browser/components/preferences/in-content/main.xul
browser/components/preferences/in-content/tests/addons/set_newtab.xpi
browser/components/preferences/in-content/tests/browser.ini
browser/components/preferences/in-content/tests/browser_extension_controlled.js
browser/components/preferences/in-content/tests/browser_homepages_filter_aboutpreferences.js
browser/locales/en-US/chrome/browser/preferences/preferences.properties
browser/themes/shared/incontentprefs/preferences.inc.css
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -1,18 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* import-globals-from preferences.js */
 /* import-globals-from ../../../../toolkit/mozapps/preferences/fontbuilder.js */
 /* import-globals-from ../../../base/content/aboutDialog-appUpdater.js */
 
-XPCOMUtils.defineLazyModuleGetter(this, "ExtensionSettingsStore",
-                                  "resource://gre/modules/ExtensionSettingsStore.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "ExtensionPreferencesManager",
+                                  "resource://gre/modules/ExtensionPreferencesManager.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
                                   "resource://gre/modules/AddonManager.jsm");
 
 Components.utils.import("resource://gre/modules/Services.jsm");
 Components.utils.import("resource://gre/modules/Downloads.jsm");
 Components.utils.import("resource://gre/modules/FileUtils.jsm");
 Components.utils.import("resource:///modules/ShellService.jsm");
 Components.utils.import("resource:///modules/TransientPrefs.jsm");
@@ -211,23 +211,16 @@ var gMainPane = {
     this.updatePerformanceSettingsBox({ duringChangeEvent: false });
 
     // set up the "use current page" label-changing listener
     this._updateUseCurrentButton();
     window.addEventListener("focus", this._updateUseCurrentButton.bind(this));
 
     this.updateBrowserStartupLastSession();
 
-    handleControllingExtension("url_overrides", "newTabURL");
-    Services.obs.addObserver({
-      observe(subject, topic, data) {
-          handleControllingExtension("url_overrides", "newTabURL");
-      },
-    }, "newtab-url-changed");
-
     if (AppConstants.platform == "win") {
       // Functionality for "Show tabs in taskbar" on Windows 7 and up.
       try {
         let sysInfo = Cc["@mozilla.org/system-info;1"].
           getService(Ci.nsIPropertyBag2);
         let ver = parseFloat(sysInfo.getProperty("version"));
         let showTabsInTaskbar = document.getElementById("showTabsInTaskbar");
         showTabsInTaskbar.hidden = ver < 6.1;
@@ -251,21 +244,19 @@ var gMainPane = {
     }
     setEventListener("useCurrent", "command",
       gMainPane.setHomePageToCurrent);
     setEventListener("useBookmark", "command",
       gMainPane.setHomePageToBookmark);
     setEventListener("restoreDefaultHomePage", "command",
       gMainPane.restoreDefaultHomePage);
     setEventListener("disableHomePageExtension", "command",
-                     gMainPane.makeDisableControllingExtension("prefs", "homepage_override"));
+                     gMainPane.makeDisableControllingExtension("homepage_override"));
     setEventListener("disableContainersExtension", "command",
-                     gMainPane.makeDisableControllingExtension("prefs", "privacy.containers"));
-setEventListener("disableNewTabExtension", "command",
-                 gMainPane.makeDisableControllingExtension("url_overrides", "newTabURL"));
+                     gMainPane.makeDisableControllingExtension("privacy.containers"));
     setEventListener("chooseLanguage", "command",
       gMainPane.showLanguages);
     setEventListener("translationAttributionImage", "click",
       gMainPane.openTranslationProviderAttribution);
     setEventListener("translateButton", "command",
       gMainPane.showTranslationExceptions);
     setEventListener("font.language.group", "change",
       gMainPane._rebuildFonts);
@@ -499,17 +490,17 @@ setEventListener("disableNewTabExtension
   readBrowserContainersCheckbox() {
     const pref = document.getElementById("privacy.userContext.enabled");
     const settings = document.getElementById("browserContainersSettings");
 
     settings.disabled = !pref.value;
     const containersEnabled = Services.prefs.getBoolPref("privacy.userContext.enabled");
     const containersCheckbox = document.getElementById("browserContainersCheckbox");
     containersCheckbox.checked = containersEnabled;
-    handleControllingExtension("prefs", "privacy.containers")
+    handleControllingExtension("privacy.containers")
       .then((isControlled) => {
         containersCheckbox.disabled = isControlled;
       });
   },
 
   /**
    * Show the Containers UI depending on the privacy.userContext.ui.enabled pref.
    */
@@ -671,17 +662,17 @@ setEventListener("disableNewTabExtension
 
   syncFromHomePref() {
     let homePref = document.getElementById("browser.startup.homepage");
 
     // Set the "Use Current Page(s)" button's text and enabled state.
     this._updateUseCurrentButton();
 
     // This is an async task.
-    handleControllingExtension("prefs", "homepage_override")
+    handleControllingExtension("homepage_override")
       .then((isControlled) => {
         // Disable or enable the inputs based on if this is controlled by an extension.
         document.querySelectorAll("#browserHomePage, .homepage-button")
           .forEach((button) => {
             button.disabled = isControlled;
           });
       });
 
@@ -765,17 +756,17 @@ setEventListener("disableNewTabExtension
     let tabs = this._getTabsForHomePage();
 
     if (tabs.length > 1)
       useCurrent.label = useCurrent.getAttribute("label2");
     else
       useCurrent.label = useCurrent.getAttribute("label1");
 
     // If the homepage is controlled by an extension then you can't use this.
-    if (await getControllingExtensionId("prefs", "homepage_override")) {
+    if (await getControllingExtensionId("homepage_override")) {
       useCurrent.disabled = true;
       return;
     }
 
     // In this case, the button's disabled state is set by preferences.xml.
     let prefName = "pref.browser.homepage.disable_button.current_page";
     if (document.getElementById(prefName).locked)
       return;
@@ -813,19 +804,19 @@ setEventListener("disableNewTabExtension
   /**
    * Restores the default home page as the user's home page.
    */
   restoreDefaultHomePage() {
     var homePage = document.getElementById("browser.startup.homepage");
     homePage.value = homePage.defaultValue;
   },
 
-  makeDisableControllingExtension(type, settingName) {
+  makeDisableControllingExtension(pref) {
     return async function disableExtension() {
-      let id = await getControllingExtensionId(type, settingName);
+      let id = await getControllingExtensionId(pref);
       let addon = await AddonManager.getAddonByID(id);
       addon.userDisabled = true;
     };
   },
 
   /**
    * Utility function to enable/disable the button specified by aButtonID based
    * on the value of the Boolean preference specified by aPreferenceID.
@@ -2657,38 +2648,36 @@ function getLocalHandlerApp(aFile) {
   localHandlerApp.executable = aFile;
 
   return localHandlerApp;
 }
 
 let extensionControlledContentIds = {
   "privacy.containers": "browserContainersExtensionContent",
   "homepage_override": "browserHomePageExtensionContent",
-  "newTabURL": "browserNewTabExtensionContent",
 };
 
 /**
   * Check if a pref is being managed by an extension.
   */
-async function getControllingExtensionId(type, settingName) {
-  await ExtensionSettingsStore.initialize();
-  return ExtensionSettingsStore.getTopExtensionId(type, settingName);
+function getControllingExtensionId(settingName) {
+  return ExtensionPreferencesManager.getControllingExtensionId(settingName);
 }
 
 function getControllingExtensionEl(settingName) {
   return document.getElementById(extensionControlledContentIds[settingName]);
 }
 
-async function handleControllingExtension(type, settingName) {
-  let controllingExtensionId = await getControllingExtensionId(type, settingName);
+async function handleControllingExtension(prefName) {
+  let controllingExtensionId = await getControllingExtensionId(prefName);
 
   if (controllingExtensionId) {
-    showControllingExtension(settingName, controllingExtensionId);
+    showControllingExtension(prefName, controllingExtensionId);
   } else {
-    hideControllingExtension(settingName);
+    hideControllingExtension(prefName);
   }
 
   return !!controllingExtensionId;
 }
 
 async function showControllingExtension(settingName, extensionId) {
   let extensionControlledContent = getControllingExtensionEl(settingName);
   // Tell the user what extension is controlling the setting.
--- a/browser/components/preferences/in-content/main.xul
+++ b/browser/components/preferences/in-content/main.xul
@@ -334,32 +334,26 @@
              id="browserStartupHomePage"/>
       <radio label="&startupBlankPage.label;"
              value="0"
              id="browserStartupBlank"/>
       <radio label="&startupPrevSession.label;"
              value="3"
              id="browserStartupLastSession"/>
     </radiogroup>
-    <hbox id="browserNewTabExtensionContent" align="center" hidden="true">
-      <description control="disableNewTabExtension" flex="1" />
-      <button id="disableNewTabExtension"
-              class="extension-controlled-button accessory-button"
-              label="&disableExtension.label;" />
-    </hbox>
   </vbox>
 </groupbox>
 
 <!-- Home Page -->
 <groupbox id="homepageGroup"
           data-category="paneGeneral"
           hidden="true">
   <caption><label>&homepage2.label;</label></caption>
 
-  <hbox id="browserHomePageExtensionContent" align="center" hidden="true">
+  <hbox id="browserHomePageExtensionContent" align="center">
     <description control="disableHomePageExtension" flex="1" />
     <button id="disableHomePageExtension"
             class="extension-controlled-button accessory-button"
             label="&disableExtension.label;" />
   </hbox>
 
   <vbox>
     <textbox id="browserHomePage"
deleted file mode 100644
index f11db0b6a8625b2587eb33bd272f35180690a771..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/components/preferences/in-content/tests/browser.ini
+++ b/browser/components/preferences/in-content/tests/browser.ini
@@ -1,15 +1,14 @@
 [DEFAULT]
 support-files =
   head.js
   privacypane_tests_perwindow.js
   site_data_test.html
   addons/set_homepage.xpi
-  addons/set_newtab.xpi
   offline/offline.html
   offline/manifest.appcache
 
 [browser_applications_selection.js]
 skip-if = os == 'linux' # bug 1382057
 [browser_advanced_update.js]
 skip-if = !updater
 [browser_basic_rebuild_fonts_test.js]
@@ -41,17 +40,16 @@ skip-if = os != "win" || (os == "win" &&
 [browser_checkspelling.js]
 [browser_connection.js]
 [browser_connection_bug388287.js]
 [browser_cookies_exceptions.js]
 [browser_defaultbrowser_alwayscheck.js]
 [browser_healthreport.js]
 skip-if = true || !healthreport # Bug 1185403 for the "true"
 [browser_homepages_filter_aboutpreferences.js]
-[browser_extension_controlled.js]
 [browser_layersacceleration.js]
 [browser_masterpassword.js]
 [browser_notifications_do_not_disturb.js]
 [browser_password_management.js]
 [browser_performance.js]
 skip-if = !e10s
 [browser_performance_e10srollout.js]
 skip-if = !e10s
deleted file mode 100644
--- a/browser/components/preferences/in-content/tests/browser_extension_controlled.js
+++ /dev/null
@@ -1,131 +0,0 @@
-XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
-                                   "@mozilla.org/browser/aboutnewtab-service;1",
-                                   "nsIAboutNewTabService");
-
-const TEST_DIR = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
-const CHROME_URL_ROOT = TEST_DIR + "/";
-
-function getSupportsFile(path) {
-  let cr = Cc["@mozilla.org/chrome/chrome-registry;1"]
-    .getService(Ci.nsIChromeRegistry);
-  let uri = Services.io.newURI(CHROME_URL_ROOT + path);
-  let fileurl = cr.convertChromeURL(uri);
-  return fileurl.QueryInterface(Ci.nsIFileURL);
-}
-
-function installAddon(xpiName) {
-  let filePath = getSupportsFile(`addons/${xpiName}`).file;
-  return new Promise((resolve, reject) => {
-    AddonManager.getInstallForFile(filePath, install => {
-      if (!install) {
-        throw new Error(`An install was not created for ${filePath}`);
-      }
-      install.addListener({
-        onDownloadFailed: reject,
-        onDownloadCancelled: reject,
-        onInstallFailed: reject,
-        onInstallCancelled: reject,
-        onInstallEnded: resolve
-      });
-      install.install();
-    });
-  });
-}
-
-function waitForMessageChange(messageId, cb) {
-  return new Promise((resolve) => {
-    let target = gBrowser.contentDocument.getElementById(messageId);
-    let observer = new MutationObserver(() => {
-      if (cb(target)) {
-        observer.disconnect();
-        resolve();
-      }
-    });
-    observer.observe(target, { attributes: true, attributeFilter: ["hidden"] });
-  });
-}
-
-function waitForMessageHidden(messageId) {
-  return waitForMessageChange(messageId, target => target.hidden);
-}
-
-function waitForMessageShown(messageId) {
-  return waitForMessageChange(messageId, target => !target.hidden);
-}
-
-add_task(async function testExtensionControlledHomepage() {
-  await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
-  let doc = gBrowser.contentDocument;
-  is(gBrowser.currentURI.spec, "about:preferences#general",
-     "#general should be in the URI for about:preferences");
-  let homepagePref = () => Services.prefs.getCharPref("browser.startup.homepage");
-  let originalHomepagePref = homepagePref();
-  let extensionHomepage = "https://developer.mozilla.org/";
-  let controlledContent = doc.getElementById("browserHomePageExtensionContent");
-
-  // The homepage is set to the default and editable.
-  ok(originalHomepagePref != extensionHomepage, "homepage is empty by default");
-  is(doc.getElementById("browserHomePage").disabled, false, "The homepage input is enabled");
-  is(controlledContent.hidden, true, "The extension controlled row is hidden");
-
-  // Install an extension that will set the homepage.
-  await installAddon("set_homepage.xpi");
-  await waitForMessageShown("browserHomePageExtensionContent");
-
-  // The homepage has been set by the extension, the user is notified and it isn't editable.
-  let controlledLabel = controlledContent.querySelector("description");
-  is(homepagePref(), extensionHomepage, "homepage is set by extension");
-  // There are two spaces before "set_homepage" because it's " <image /> set_homepage".
-  is(controlledLabel.textContent, "An extension,  set_homepage, controls your home page.",
-     "The user is notified that an extension is controlling the homepage");
-  is(controlledContent.hidden, false, "The extension controlled row is hidden");
-  is(doc.getElementById("browserHomePage").disabled, true, "The homepage input is disabled");
-
-  // Disable the extension.
-  doc.getElementById("disableHomePageExtension").click();
-
-  await waitForMessageHidden("browserHomePageExtensionContent");
-
-  is(homepagePref(), originalHomepagePref, "homepage is set back to default");
-  is(doc.getElementById("browserHomePage").disabled, false, "The homepage input is enabled");
-  is(controlledContent.hidden, true, "The extension controlled row is hidden");
-
-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
-});
-
-add_task(async function testExtensionControlledNewTab() {
-  await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
-  let doc = gBrowser.contentDocument;
-  is(gBrowser.currentURI.spec, "about:preferences#general",
-     "#general should be in the URI for about:preferences");
-
-  let controlledContent = doc.getElementById("browserNewTabExtensionContent");
-
-  // The new tab is set to the default and message is hidden.
-  ok(!aboutNewTabService.newTabURL.startsWith("moz-extension:"), "new tab is not set");
-  is(controlledContent.hidden, true, "The extension controlled row is hidden");
-
-  // Install an extension that will set the new tab page.
-  await installAddon("set_newtab.xpi");
-
-  await waitForMessageShown("browserNewTabExtensionContent");
-
-  // The new tab page has been set by the extension and the user is notified.
-  let controlledLabel = controlledContent.querySelector("description");
-  ok(aboutNewTabService.newTabURL.startsWith("moz-extension:"), "new tab url is set by extension");
-  // There are two spaces before "set_newtab" because it's " <image /> set_newtab".
-  is(controlledLabel.textContent, "An extension,  set_newtab, controls your new tab page.",
-     "The user is notified that an extension is controlling the new tab page");
-  is(controlledContent.hidden, false, "The extension controlled row is hidden");
-
-  // Disable the extension.
-  doc.getElementById("disableNewTabExtension").click();
-
-  await waitForMessageHidden("browserNewTabExtensionContent");
-
-  ok(!aboutNewTabService.newTabURL.startsWith("moz-extension:"), "new tab page is set back to default");
-  is(controlledContent.hidden, true, "The extension controlled row is hidden");
-
-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
-});
-
--- a/browser/components/preferences/in-content/tests/browser_homepages_filter_aboutpreferences.js
+++ b/browser/components/preferences/in-content/tests/browser_homepages_filter_aboutpreferences.js
@@ -13,8 +13,99 @@ add_task(async function testSetHomepageU
   is(gBrowser.tabs.length, 3, "Three tabs should be open");
   is(Services.prefs.getCharPref("browser.startup.homepage"), "about:blank|about:home",
      "about:blank and about:home should be the only homepages set");
 
   Services.prefs.setCharPref("browser.startup.homepage", oldHomepagePref);
   await BrowserTestUtils.removeTab(gBrowser.selectedTab);
   await BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
+
+const TEST_DIR = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
+const CHROME_URL_ROOT = TEST_DIR + "/";
+
+function getSupportsFile(path) {
+  let cr = Cc["@mozilla.org/chrome/chrome-registry;1"]
+    .getService(Ci.nsIChromeRegistry);
+  let uri = Services.io.newURI(CHROME_URL_ROOT + path);
+  let fileurl = cr.convertChromeURL(uri);
+  return fileurl.QueryInterface(Ci.nsIFileURL);
+}
+
+function installAddon() {
+  let filePath = getSupportsFile("addons/set_homepage.xpi").file;
+  return new Promise((resolve, reject) => {
+    AddonManager.getInstallForFile(filePath, install => {
+      if (!install) {
+        throw new Error(`An install was not created for ${filePath}`);
+      }
+      install.addListener({
+        onDownloadFailed: reject,
+        onDownloadCancelled: reject,
+        onInstallFailed: reject,
+        onInstallCancelled: reject,
+        onInstallEnded: resolve
+      });
+      install.install();
+    });
+  });
+}
+
+function waitForMessageChange(cb) {
+  return new Promise((resolve) => {
+    let target = gBrowser.contentDocument.getElementById("browserHomePageExtensionContent");
+    let observer = new MutationObserver(() => {
+      if (cb(target)) {
+        observer.disconnect();
+        resolve();
+      }
+    });
+    observer.observe(target, { attributes: true, attributeFilter: ["hidden"] });
+  });
+}
+
+function waitForMessageHidden() {
+  return waitForMessageChange(target => target.hidden);
+}
+
+function waitForMessageShown() {
+  return waitForMessageChange(target => !target.hidden);
+}
+
+add_task(async function testExtensionControlledHomepage() {
+  await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+  let doc = gBrowser.contentDocument;
+  is(gBrowser.currentURI.spec, "about:preferences#general",
+     "#general should be in the URI for about:preferences");
+  let homepagePref = () => Services.prefs.getCharPref("browser.startup.homepage");
+  let originalHomepagePref = homepagePref();
+  let extensionHomepage = "https://developer.mozilla.org/";
+  let controlledContent = doc.getElementById("browserHomePageExtensionContent");
+
+  // The homepage is set to the default and editable.
+  ok(originalHomepagePref != extensionHomepage, "homepage is empty by default");
+  is(doc.getElementById("browserHomePage").disabled, false, "The homepage input is enabled");
+  is(controlledContent.hidden, true, "The extension controlled row is hidden");
+
+  // Install an extension that will set the homepage.
+  await installAddon();
+  await waitForMessageShown();
+
+  // The homepage has been set by the extension, the user is notified and it isn't editable.
+  let controlledLabel = controlledContent.querySelector("description");
+  is(homepagePref(), extensionHomepage, "homepage is set by extension");
+  // There are two spaces before "set_homepage" because it's " <image /> set_homepage".
+  is(controlledLabel.textContent, "An extension,  set_homepage, controls your home page.",
+     "The user is notified that an extension is controlling the homepage");
+  is(controlledContent.hidden, false, "The extension controlled row is hidden");
+  is(doc.getElementById("browserHomePage").disabled, true, "The homepage input is disabled");
+
+  // Disable the extension.
+  doc.getElementById("disableHomePageExtension").click();
+
+  await waitForMessageHidden();
+
+  is(homepagePref(), originalHomepagePref, "homepage is set back to default");
+  is(doc.getElementById("browserHomePage").disabled, false, "The homepage input is enabled");
+  is(controlledContent.hidden, true, "The extension controlled row is hidden");
+
+  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
--- a/browser/locales/en-US/chrome/browser/preferences/preferences.properties
+++ b/browser/locales/en-US/chrome/browser/preferences/preferences.properties
@@ -277,16 +277,12 @@ searchResults.needHelp2=Need help? Visit
 
 # LOCALIZATION NOTE %S is the default value of the `dom.ipc.processCount` pref.
 defaultContentProcessCount=%S (default)
 
 # LOCALIZATION NOTE (extensionControlled.homepage_override):
 # This string is shown to notify the user that their home page is being controlled by an extension.
 extensionControlled.homepage_override = An extension, %S, controls your home page.
 
-# LOCALIZATION NOTE (extensionControlled.newTabURL):
-# This string is shown to notify the user that their new tab page is being controlled by an extension.
-extensionControlled.newTabURL = An extension, %S, controls your new tab page.
-
 # LOCALIZATION NOTE (extensionControlled.privacy.containers):
 # This string is shown to notify the user that Container Tabs are being enabled by an extension
 # %S is the container addon controlling it
 extensionControlled.privacy.containers = An extension, %S, requires Container Tabs.
\ No newline at end of file
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -144,21 +144,16 @@ button > hbox > label {
 #isDefaultLabel {
   font-weight: 600;
 }
 
 #startupPageBox {
   padding-top: 32px;
 }
 
-#browserNewTabExtensionContent {
-  /* Indent the the extension info to match the radio buttons. */
-  margin-inline-start: 34px;
-}
-
 #browserHomePage {
   margin-inline-start: 0;
   margin-inline-end: 0;
   margin-bottom: 4px;
 }
 
 .homepage-button:first-of-type {
   margin-inline-start: 0;