From d1fde5c4ea54e3a5559dea4e0cedce436aab0036 Mon Sep 17 00:00:00 2001 From: roytam1 Date: Sun, 22 Sep 2024 21:17:32 +0800 Subject: [PATCH] [Basilisk] port PM's ghostbuster --- application/basilisk/app/profile/basilisk.js | 3 ++ .../basilisk/base/content/tabbrowser.xml | 14 +++++++- application/basilisk/base/jar.mn | 2 +- .../basilisk/components/nsBrowserGlue.js | 32 +++++++++++++++++++ 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/application/basilisk/app/profile/basilisk.js b/application/basilisk/app/profile/basilisk.js index a3fdf58803..2e709f70d8 100644 --- a/application/basilisk/app/profile/basilisk.js +++ b/application/basilisk/app/profile/basilisk.js @@ -444,6 +444,9 @@ pref("javascript.options.showInConsole", true); pref("general.warnOnAboutConfig", false); #endif +// Enable unlinking of ghost windows so they can be garbage collected. +pref("browser.ghostbuster.enabled", true); + // This is the pref to control the location bar, change this to true to // force this - this makes the origin of popup windows more obvious to avoid // spoofing. We would rather not do it by default because it affects UE for web diff --git a/application/basilisk/base/content/tabbrowser.xml b/application/basilisk/base/content/tabbrowser.xml index 8772676da0..e7ec7f1ec0 100644 --- a/application/basilisk/base/content/tabbrowser.xml +++ b/application/basilisk/base/content/tabbrowser.xml @@ -2645,12 +2645,24 @@ } // We're going to remove the tab and the browser now. + + // Using the "STOP_ALL" flag should halt all animations, fetches, network + // activity, etc. to come to a clean state for removal and unlinking (if enabled). + var browser = this.getBrowserForTab(aTab); + browser.webNavigation.stop(nsIWebNavigation.STOP_ALL); + this._tabFilters.delete(aTab); this._tabListeners.delete(aTab); - var browser = this.getBrowserForTab(aTab); this._outerWindowIDBrowserMap.delete(browser.outerWindowID); + if (Services.prefs.getBoolPref("browser.ghostbuster.enabled", true)) { + Cu.unlinkGhostWindows(); +#ifdef DEBUG + dump("Unlinking ghost windows has run on tab close.\n"); +#endif + } + // Because of the way XBL works (fields just set JS // properties on the element) and the code we have in place // to preserve the JS objects for any elements that have diff --git a/application/basilisk/base/jar.mn b/application/basilisk/base/jar.mn index fd218f1eef..05f124132f 100644 --- a/application/basilisk/base/jar.mn +++ b/application/basilisk/base/jar.mn @@ -128,7 +128,7 @@ browser.jar: content/browser/contentSearchUI.js (content/contentSearchUI.js) content/browser/contentSearchUI.css (content/contentSearchUI.css) content/browser/tabbrowser.css (content/tabbrowser.css) - content/browser/tabbrowser.xml (content/tabbrowser.xml) +* content/browser/tabbrowser.xml (content/tabbrowser.xml) * content/browser/urlbarBindings.xml (content/urlbarBindings.xml) content/browser/utilityOverlay.js (content/utilityOverlay.js) content/browser/usercontext.svg (content/usercontext.svg) diff --git a/application/basilisk/components/nsBrowserGlue.js b/application/basilisk/components/nsBrowserGlue.js index 516cab06d7..0250909bbf 100644 --- a/application/basilisk/components/nsBrowserGlue.js +++ b/application/basilisk/components/nsBrowserGlue.js @@ -81,6 +81,10 @@ const BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS = 1; // days we will try to create a new one more aggressively. const BOOKMARKS_BACKUP_MAX_INTERVAL_DAYS = 3; +// Use users' idle time to unlink ghost windows and clean up memory. +// Trigger this by default every 5 minutes. +const GHOSTBUSTER_INTERVAL = 5 * 60; + // Factory object const BrowserGlueServiceFactory = { _instance: null, @@ -99,6 +103,10 @@ function BrowserGlue() { "@mozilla.org/widget/idleservice;1", "nsIIdleService"); + XPCOMUtils.defineLazyServiceGetter(this, "_ghostBusterService", + "@mozilla.org/widget/idleservice;1", + "nsIIdleService"); + XPCOMUtils.defineLazyGetter(this, "_distributionCustomizer", function() { Cu.import("resource:///modules/distribution.js"); return new DistributionCustomizer(); @@ -127,6 +135,7 @@ BrowserGlue.prototype = { _isPlacesShutdownObserver: false, _isPlacesDatabaseLocked: false, _migrationImportsDefaultBookmarks: false, + _isGhostBusterObserver: false, _setPrefToSaveSession: function(aForce) { if (!this._saveSession && !aForce) @@ -241,6 +250,15 @@ BrowserGlue.prototype = { break; case "idle": this._backupBookmarks(); + if (this._ghostBusterService.idleTime > GHOSTBUSTER_INTERVAL * 1000) { + if (Services.prefs.getBoolPref("browser.ghostbuster.enabled", true)) { + Cu.unlinkGhostWindows(); + Cu.forceGC(); +#ifdef DEBUG + dump("Unlinking ghost windows + GC has run on idle.\n"); +#endif + } + } break; case "distribution-customization-complete": Services.obs.removeObserver(this, "distribution-customization-complete"); @@ -843,6 +861,13 @@ BrowserGlue.prototype = { this._trackSlowStartup(); + // Initialize ghost window idle observer. + if (!this._isGhostBusterObserver) { + this._ghostBusterService.addIdleObserver(this, GHOSTBUSTER_INTERVAL); + // Prevent re-entry. + this._isGhostBusterObserver = true; + } + // Offer to reset a user's profile if it hasn't been used for 60 days. const OFFER_PROFILE_RESET_INTERVAL_MS = 60 * 24 * 60 * 60 * 1000; let lastUse = Services.appinfo.replacedLockTime; @@ -914,6 +939,13 @@ BrowserGlue.prototype = { if (AppConstants.NIGHTLY_BUILD) { AddonWatcher.uninit(); } + // Shut down ghost window idle observer. + if (this._isGhostBusterObserver) { + this._ghostBusterService.removeIdleObserver(this, GHOSTBUSTER_INTERVAL); + this._isGhostBusterObserver = false; + } + // Do one final unlink to combat shutdown issues. + Cu.unlinkGhostWindows(); }, // All initial windows have opened.