gecko-dev/browser/modules/AboutNewTab.jsm

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

305 строки
8.4 KiB
JavaScript
Исходник Обычный вид История

/* 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/. */
"use strict";
const { AppConstants } = ChromeUtils.importESModule(
"resource://gre/modules/AppConstants.sys.mjs"
);
const { XPCOMUtils } = ChromeUtils.importESModule(
"resource://gre/modules/XPCOMUtils.sys.mjs"
Bug 1514594: Part 3 - Change ChromeUtils.import API. *** Bug 1514594: Part 3a - Change ChromeUtils.import to return an exports object; not pollute global. r=mccr8 This changes the behavior of ChromeUtils.import() to return an exports object, rather than a module global, in all cases except when `null` is passed as a second argument, and changes the default behavior not to pollute the global scope with the module's exports. Thus, the following code written for the old model: ChromeUtils.import("resource://gre/modules/Services.jsm"); is approximately the same as the following, in the new model: var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); Since the two behaviors are mutually incompatible, this patch will land with a scripted rewrite to update all existing callers to use the new model rather than the old. *** Bug 1514594: Part 3b - Mass rewrite all JS code to use the new ChromeUtils.import API. rs=Gijs This was done using the followng script: https://bitbucket.org/kmaglione/m-c-rewrites/src/tip/processors/cu-import-exports.jsm *** Bug 1514594: Part 3c - Update ESLint plugin for ChromeUtils.import API changes. r=Standard8 Differential Revision: https://phabricator.services.mozilla.com/D16747 *** Bug 1514594: Part 3d - Remove/fix hundreds of duplicate imports from sync tests. r=Gijs Differential Revision: https://phabricator.services.mozilla.com/D16748 *** Bug 1514594: Part 3e - Remove no-op ChromeUtils.import() calls. r=Gijs Differential Revision: https://phabricator.services.mozilla.com/D16749 *** Bug 1514594: Part 3f.1 - Cleanup various test corner cases after mass rewrite. r=Gijs *** Bug 1514594: Part 3f.2 - Cleanup various non-test corner cases after mass rewrite. r=Gijs Differential Revision: https://phabricator.services.mozilla.com/D16750 --HG-- extra : rebase_source : 359574ee3064c90f33bf36c2ebe3159a24cc8895 extra : histedit_source : b93c8f42808b1599f9122d7842d2c0b3e656a594%2C64a3a4e3359dc889e2ab2b49461bab9e27fc10a7
2019-01-17 21:18:31 +03:00
);
const lazy = {};
XPCOMUtils.defineLazyModuleGetters(lazy, {
ActivityStream: "resource://activity-stream/lib/ActivityStream.jsm",
Bug 1747973 - Cache the top-sites query context like we cache other contexts. r=harry This caches the top-sites context like we cache other contexts. The cache is cleared when the top sites change or are enabled/disabled. Unlike other contexts, which are evicted from the cache once too many newer contexts are cached, the top-sites context is never evicted due to space reasons because showing the top sites is a frequent action. I wanted to keep the logic around when/whether the top sites have changed or been enabled/disabled isolated to the top-sites provider, since it's the class concerned with top sites in the first place, instead of spreading it out to `QueryContextCache`. However, by the same token I didn't want to cache contexts directly in the provider in order to keep all context caching in `QueryContextCache`. (Contexts are also per window/urlbar, not global, so if we were to cache contexts in the provider, we'd need to store them in map from windows to contexts or something similar.) So I added a more general listener system to the provider. Listeners are called when the top sites change or they are enabled/disabled. `QueryContextCache` adds a listener function so it can evict its cached top-sites context. The provider keeps weak references to the listener functions because currently `UrlbarView` doesn't have a way to tell when it's destroyed, so it doesn't know when it should remove listeners. Finally, there doesn't seem to be a way to observe the `TopSitesFeed` from the outside, and I don't think it's the role of `TopSitesFeed` to broadcast an observer message to the entire browser or something like that, so I modified `AboutNewTab` to subscribe to changes in its activity stream object and then compare the new top sites to the last seen top sites, and if they're different, then broadcast a new "newtab-top-sites-changed" notification. Differential Revision: https://phabricator.services.mozilla.com/D134863
2022-01-03 22:18:54 +03:00
ObjectUtils: "resource://gre/modules/ObjectUtils.jsm",
RemotePages:
"resource://gre/modules/remotepagemanager/RemotePageManagerParent.jsm",
});
const ABOUT_URL = "about:newtab";
const PREF_ACTIVITY_STREAM_DEBUG = "browser.newtabpage.activity-stream.debug";
const TOPIC_APP_QUIT = "quit-application-granted";
const BROWSER_READY_NOTIFICATION = "sessionstore-windows-restored";
const AboutNewTab = {
QueryInterface: ChromeUtils.generateQI([
"nsIObserver",
"nsISupportsWeakReference",
]),
// AboutNewTab
initialized: false,
pageListener: null,
isPageListenerOverridden: false,
willNotifyUser: false,
_activityStreamEnabled: false,
activityStream: null,
activityStreamDebug: false,
Bug 1747973 - Cache the top-sites query context like we cache other contexts. r=harry This caches the top-sites context like we cache other contexts. The cache is cleared when the top sites change or are enabled/disabled. Unlike other contexts, which are evicted from the cache once too many newer contexts are cached, the top-sites context is never evicted due to space reasons because showing the top sites is a frequent action. I wanted to keep the logic around when/whether the top sites have changed or been enabled/disabled isolated to the top-sites provider, since it's the class concerned with top sites in the first place, instead of spreading it out to `QueryContextCache`. However, by the same token I didn't want to cache contexts directly in the provider in order to keep all context caching in `QueryContextCache`. (Contexts are also per window/urlbar, not global, so if we were to cache contexts in the provider, we'd need to store them in map from windows to contexts or something similar.) So I added a more general listener system to the provider. Listeners are called when the top sites change or they are enabled/disabled. `QueryContextCache` adds a listener function so it can evict its cached top-sites context. The provider keeps weak references to the listener functions because currently `UrlbarView` doesn't have a way to tell when it's destroyed, so it doesn't know when it should remove listeners. Finally, there doesn't seem to be a way to observe the `TopSitesFeed` from the outside, and I don't think it's the role of `TopSitesFeed` to broadcast an observer message to the entire browser or something like that, so I modified `AboutNewTab` to subscribe to changes in its activity stream object and then compare the new top sites to the last seen top sites, and if they're different, then broadcast a new "newtab-top-sites-changed" notification. Differential Revision: https://phabricator.services.mozilla.com/D134863
2022-01-03 22:18:54 +03:00
_cachedTopSites: null,
_newTabURL: ABOUT_URL,
_newTabURLOverridden: false,
/**
* init - Initializes an instance of Activity Stream if one doesn't exist already
* and creates the instance of Remote Page Manager which Activity Stream
* uses for message passing.
*
* @param {obj} pageListener - Optional argument. An existing instance of RemotePages
* which Activity Stream has previously made, and we
* would like to re-use.
*/
init(pageListener) {
Services.obs.addObserver(this, TOPIC_APP_QUIT);
if (!AppConstants.RELEASE_OR_BETA) {
XPCOMUtils.defineLazyPreferenceGetter(
this,
"activityStreamDebug",
PREF_ACTIVITY_STREAM_DEBUG,
false,
() => {
this.notifyChange();
}
);
}
XPCOMUtils.defineLazyPreferenceGetter(
this,
"privilegedAboutProcessEnabled",
"browser.tabs.remote.separatePrivilegedContentProcess",
false,
() => {
this.notifyChange();
}
);
// More initialization happens here
this.toggleActivityStream(true);
this.initialized = true;
if (this.isPageListenerOverridden) {
return;
}
// Since `init` can be called via `reset` at a later time with an existing
// pageListener, we want to only add the observer if we are initializing
// without this pageListener argument. This means it was the first call to `init`
if (!pageListener) {
Services.obs.addObserver(this, BROWSER_READY_NOTIFICATION);
}
this.pageListener =
pageListener ||
new lazy.RemotePages(["about:home", "about:newtab", "about:welcome"]);
},
/**
* React to changes to the activity stream being enabled or not.
*
* This will only act if there is a change of state and if not overridden.
*
* @returns {Boolean} Returns if there has been a state change
*
* @param {Boolean} stateEnabled activity stream enabled state to set to
* @param {Boolean} forceState force state change
*/
toggleActivityStream(stateEnabled, forceState = false) {
if (
!forceState &&
(this._newTabURLOverridden ||
stateEnabled === this._activityStreamEnabled)
) {
// exit there is no change of state
return false;
}
if (stateEnabled) {
this._activityStreamEnabled = true;
} else {
this._activityStreamEnabled = false;
}
this._newTabURL = ABOUT_URL;
return true;
},
get newTabURL() {
return this._newTabURL;
},
set newTabURL(aNewTabURL) {
let newTabURL = aNewTabURL.trim();
if (newTabURL === ABOUT_URL) {
// avoid infinite redirects in case one sets the URL to about:newtab
this.resetNewTabURL();
return;
} else if (newTabURL === "") {
newTabURL = "about:blank";
}
this.toggleActivityStream(false);
this._newTabURL = newTabURL;
this._newTabURLOverridden = true;
this.notifyChange();
},
get newTabURLOverridden() {
return this._newTabURLOverridden;
},
get activityStreamEnabled() {
return this._activityStreamEnabled;
},
resetNewTabURL() {
this._newTabURLOverridden = false;
this._newTabURL = ABOUT_URL;
this.toggleActivityStream(true, true);
this.notifyChange();
},
notifyChange() {
Services.obs.notifyObservers(null, "newtab-url-changed", this._newTabURL);
},
/**
* onBrowserReady - Continues the initialization of Activity Stream after browser is ready.
*/
onBrowserReady() {
if (this.activityStream && this.activityStream.initialized) {
return;
}
this.activityStream = new lazy.ActivityStream();
try {
this.activityStream.init();
Bug 1747973 - Cache the top-sites query context like we cache other contexts. r=harry This caches the top-sites context like we cache other contexts. The cache is cleared when the top sites change or are enabled/disabled. Unlike other contexts, which are evicted from the cache once too many newer contexts are cached, the top-sites context is never evicted due to space reasons because showing the top sites is a frequent action. I wanted to keep the logic around when/whether the top sites have changed or been enabled/disabled isolated to the top-sites provider, since it's the class concerned with top sites in the first place, instead of spreading it out to `QueryContextCache`. However, by the same token I didn't want to cache contexts directly in the provider in order to keep all context caching in `QueryContextCache`. (Contexts are also per window/urlbar, not global, so if we were to cache contexts in the provider, we'd need to store them in map from windows to contexts or something similar.) So I added a more general listener system to the provider. Listeners are called when the top sites change or they are enabled/disabled. `QueryContextCache` adds a listener function so it can evict its cached top-sites context. The provider keeps weak references to the listener functions because currently `UrlbarView` doesn't have a way to tell when it's destroyed, so it doesn't know when it should remove listeners. Finally, there doesn't seem to be a way to observe the `TopSitesFeed` from the outside, and I don't think it's the role of `TopSitesFeed` to broadcast an observer message to the entire browser or something like that, so I modified `AboutNewTab` to subscribe to changes in its activity stream object and then compare the new top sites to the last seen top sites, and if they're different, then broadcast a new "newtab-top-sites-changed" notification. Differential Revision: https://phabricator.services.mozilla.com/D134863
2022-01-03 22:18:54 +03:00
this._subscribeToActivityStream();
} catch (e) {
Cu.reportError(e);
}
},
Bug 1747973 - Cache the top-sites query context like we cache other contexts. r=harry This caches the top-sites context like we cache other contexts. The cache is cleared when the top sites change or are enabled/disabled. Unlike other contexts, which are evicted from the cache once too many newer contexts are cached, the top-sites context is never evicted due to space reasons because showing the top sites is a frequent action. I wanted to keep the logic around when/whether the top sites have changed or been enabled/disabled isolated to the top-sites provider, since it's the class concerned with top sites in the first place, instead of spreading it out to `QueryContextCache`. However, by the same token I didn't want to cache contexts directly in the provider in order to keep all context caching in `QueryContextCache`. (Contexts are also per window/urlbar, not global, so if we were to cache contexts in the provider, we'd need to store them in map from windows to contexts or something similar.) So I added a more general listener system to the provider. Listeners are called when the top sites change or they are enabled/disabled. `QueryContextCache` adds a listener function so it can evict its cached top-sites context. The provider keeps weak references to the listener functions because currently `UrlbarView` doesn't have a way to tell when it's destroyed, so it doesn't know when it should remove listeners. Finally, there doesn't seem to be a way to observe the `TopSitesFeed` from the outside, and I don't think it's the role of `TopSitesFeed` to broadcast an observer message to the entire browser or something like that, so I modified `AboutNewTab` to subscribe to changes in its activity stream object and then compare the new top sites to the last seen top sites, and if they're different, then broadcast a new "newtab-top-sites-changed" notification. Differential Revision: https://phabricator.services.mozilla.com/D134863
2022-01-03 22:18:54 +03:00
_subscribeToActivityStream() {
let unsubscribe = this.activityStream.store.subscribe(() => {
// If the top sites changed, broadcast "newtab-top-sites-changed". We
// ignore changes to the `screenshot` property in each site because
// screenshots are generated at times that are hard to predict and it ends
// up interfering with tests that rely on "newtab-top-sites-changed".
// Observers likely don't care about screenshots anyway.
let topSites = this.activityStream.store
.getState()
.TopSites.rows.map(site => {
site = { ...site };
delete site.screenshot;
return site;
});
if (!lazy.ObjectUtils.deepEqual(topSites, this._cachedTopSites)) {
Bug 1747973 - Cache the top-sites query context like we cache other contexts. r=harry This caches the top-sites context like we cache other contexts. The cache is cleared when the top sites change or are enabled/disabled. Unlike other contexts, which are evicted from the cache once too many newer contexts are cached, the top-sites context is never evicted due to space reasons because showing the top sites is a frequent action. I wanted to keep the logic around when/whether the top sites have changed or been enabled/disabled isolated to the top-sites provider, since it's the class concerned with top sites in the first place, instead of spreading it out to `QueryContextCache`. However, by the same token I didn't want to cache contexts directly in the provider in order to keep all context caching in `QueryContextCache`. (Contexts are also per window/urlbar, not global, so if we were to cache contexts in the provider, we'd need to store them in map from windows to contexts or something similar.) So I added a more general listener system to the provider. Listeners are called when the top sites change or they are enabled/disabled. `QueryContextCache` adds a listener function so it can evict its cached top-sites context. The provider keeps weak references to the listener functions because currently `UrlbarView` doesn't have a way to tell when it's destroyed, so it doesn't know when it should remove listeners. Finally, there doesn't seem to be a way to observe the `TopSitesFeed` from the outside, and I don't think it's the role of `TopSitesFeed` to broadcast an observer message to the entire browser or something like that, so I modified `AboutNewTab` to subscribe to changes in its activity stream object and then compare the new top sites to the last seen top sites, and if they're different, then broadcast a new "newtab-top-sites-changed" notification. Differential Revision: https://phabricator.services.mozilla.com/D134863
2022-01-03 22:18:54 +03:00
this._cachedTopSites = topSites;
Services.obs.notifyObservers(null, "newtab-top-sites-changed");
}
});
this._unsubscribeFromActivityStream = () => {
try {
unsubscribe();
} catch (e) {
Cu.reportError(e);
}
};
},
/**
* uninit - Uninitializes Activity Stream if it exists, and destroys the pageListener
* if it exists.
*/
uninit() {
if (this.activityStream) {
Bug 1747973 - Cache the top-sites query context like we cache other contexts. r=harry This caches the top-sites context like we cache other contexts. The cache is cleared when the top sites change or are enabled/disabled. Unlike other contexts, which are evicted from the cache once too many newer contexts are cached, the top-sites context is never evicted due to space reasons because showing the top sites is a frequent action. I wanted to keep the logic around when/whether the top sites have changed or been enabled/disabled isolated to the top-sites provider, since it's the class concerned with top sites in the first place, instead of spreading it out to `QueryContextCache`. However, by the same token I didn't want to cache contexts directly in the provider in order to keep all context caching in `QueryContextCache`. (Contexts are also per window/urlbar, not global, so if we were to cache contexts in the provider, we'd need to store them in map from windows to contexts or something similar.) So I added a more general listener system to the provider. Listeners are called when the top sites change or they are enabled/disabled. `QueryContextCache` adds a listener function so it can evict its cached top-sites context. The provider keeps weak references to the listener functions because currently `UrlbarView` doesn't have a way to tell when it's destroyed, so it doesn't know when it should remove listeners. Finally, there doesn't seem to be a way to observe the `TopSitesFeed` from the outside, and I don't think it's the role of `TopSitesFeed` to broadcast an observer message to the entire browser or something like that, so I modified `AboutNewTab` to subscribe to changes in its activity stream object and then compare the new top sites to the last seen top sites, and if they're different, then broadcast a new "newtab-top-sites-changed" notification. Differential Revision: https://phabricator.services.mozilla.com/D134863
2022-01-03 22:18:54 +03:00
this._unsubscribeFromActivityStream?.();
this.activityStream.uninit();
this.activityStream = null;
}
if (this.pageListener) {
this.pageListener.destroy();
this.pageListener = null;
}
this.initialized = false;
},
overridePageListener(shouldPassPageListener) {
this.isPageListenerOverridden = true;
const pageListener = this.pageListener;
if (!pageListener) {
return null;
}
if (shouldPassPageListener) {
this.pageListener = null;
return pageListener;
}
this.uninit();
return null;
},
reset(pageListener) {
this.isPageListenerOverridden = false;
this.init(pageListener);
},
getTopSites() {
return this.activityStream
? this.activityStream.store.getState().TopSites.rows
: [];
},
_alreadyRecordedTopsitesPainted: false,
_nonDefaultStartup: false,
noteNonDefaultStartup() {
this._nonDefaultStartup = true;
},
maybeRecordTopsitesPainted(timestamp) {
if (this._alreadyRecordedTopsitesPainted || this._nonDefaultStartup) {
return;
}
const SCALAR_KEY = "timestamps.about_home_topsites_first_paint";
let startupInfo = Services.startup.getStartupInfo();
let processStartTs = startupInfo.process.getTime();
let delta = Math.round(timestamp - processStartTs);
Services.telemetry.scalarSet(SCALAR_KEY, delta);
ChromeUtils.addProfilerMarker("aboutHomeTopsitesFirstPaint");
this._alreadyRecordedTopsitesPainted = true;
},
// nsIObserver implementation
observe(subject, topic, data) {
switch (topic) {
case TOPIC_APP_QUIT: {
// We defer to this to the next tick of the event loop since the
// AboutHomeStartupCache might want to read from the ActivityStream
// store during TOPIC_APP_QUIT.
Services.tm.dispatchToMainThread(() => this.uninit());
break;
}
case BROWSER_READY_NOTIFICATION: {
Services.obs.removeObserver(this, BROWSER_READY_NOTIFICATION);
// Avoid running synchronously during this event that's used for timing
Services.tm.dispatchToMainThread(() => this.onBrowserReady());
break;
}
}
},
};
var EXPORTED_SYMBOLS = ["AboutNewTab"];