Source code

Revision control

Copy as Markdown

Other Tools

/* 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 {Store} from "resource://newtab/lib/Store.sys.mjs" */
import {
actionTypes as at,
actionCreators as ac,
import { WIDGET_REGISTRY } from "resource://newtab/common/WidgetsRegistry.mjs";
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
WidgetManager: "moz-src:///toolkit/components/widgets/WidgetManager.sys.mjs",
});
/**
* @backward-compat { version 160 }
* WidgetManager is a host module added in 160, absent when New Tab train-hops
* onto an older Firefox. Remove this guard once 160 reaches Release.
*/
function isWidgetManagerSupported() {
return Services.vc.compare(AppConstants.MOZ_APP_VERSION, "160.0a1") >= 0;
}
/**
* The information New Tab uses from a widget's extension.
*
* @typedef {object} ExtensionWidget
* @property {string} src - URL of the widget's document
* @property {string} name - display name of the widget
* @property {string} telemetryName - name sent in Glean events, the widget's id
* @property {string[]} validSizes - sizes the widget supports
*/
/**
* Publishes the ExtensionWidget of each extension that a WIDGET_REGISTRY
* entry names and that has a widget in WidgetManager, keyed by extension id,
* as the ExtensionWidgets state.
*/
export class ExtensionWidgetsFeed {
/** @type {Store} */
store = null;
constructor() {
this.onChange = this.onChange.bind(this);
}
/**
* Returns the ExtensionWidget of each extension that a WIDGET_REGISTRY entry
* names and that has a widget in WidgetManager.
*
* @returns {{[extensionID: string]: ExtensionWidget}}
*/
#getExtensionWidgets() {
/** @type {{[extensionID: string]: ExtensionWidget}} */
const extensionWidgets = {};
if (!isWidgetManagerSupported()) {
return extensionWidgets;
}
for (const { extensionID } of WIDGET_REGISTRY) {
const widget = lazy.WidgetManager.getWidgetByID(extensionID);
if (widget) {
extensionWidgets[extensionID] = {
src: widget.src,
name: widget.name,
telemetryName: widget.id,
validSizes: widget.sizes,
};
}
}
return extensionWidgets;
}
/**
* Broadcasts the ExtensionWidgets.
*
* @param {{[extensionID: string]: ExtensionWidget}} extensionWidgets - the
* ExtensionWidgets to broadcast
* @param {object} [meta] - meta of the broadcast action
*/
#broadcast(extensionWidgets, meta) {
this.store.dispatch(
ac.BroadcastToContent({
type: at.WIDGETS_EXTENSIONS_UPDATE,
data: extensionWidgets,
meta,
})
);
}
/**
* WidgetManager change listener. Broadcasts when a WIDGET_REGISTRY entry
* names the extension of the widget that changed.
*
* @param {string} event - "change"
* @param {string} id - id of the widget that changed
*/
onChange(event, id) {
if (WIDGET_REGISTRY.some(({ extensionID }) => extensionID === id)) {
this.#broadcast(this.#getExtensionWidgets());
}
}
onAction(action) {
switch (action.type) {
case at.INIT:
if (isWidgetManagerSupported()) {
lazy.WidgetManager.on("change", this.onChange);
}
this.#broadcast(this.#getExtensionWidgets(), { isStartup: true });
break;
case at.UNINIT:
if (isWidgetManagerSupported()) {
lazy.WidgetManager.off("change", this.onChange);
}
break;
}
}
}