summaryrefslogtreecommitdiffstats
path: root/browser/components/newtab/actors
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/newtab/actors')
-rw-r--r--browser/components/newtab/actors/ASRouterChild.jsm112
-rw-r--r--browser/components/newtab/actors/ASRouterParent.jsm111
2 files changed, 223 insertions, 0 deletions
diff --git a/browser/components/newtab/actors/ASRouterChild.jsm b/browser/components/newtab/actors/ASRouterChild.jsm
new file mode 100644
index 0000000000..6471e33757
--- /dev/null
+++ b/browser/components/newtab/actors/ASRouterChild.jsm
@@ -0,0 +1,112 @@
+/* vim: set ts=2 sw=2 sts=2 et tw=80: */
+/* 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 EXPORTED_SYMBOLS = ["ASRouterChild"];
+
+const {
+ MESSAGE_TYPE_LIST,
+ MESSAGE_TYPE_HASH: msg,
+} = ChromeUtils.importESModule(
+ "resource://activity-stream/common/ActorConstants.sys.mjs"
+);
+const VALID_TYPES = new Set(MESSAGE_TYPE_LIST);
+
+class ASRouterChild extends JSWindowActorChild {
+ constructor() {
+ super();
+ this.observers = new Set();
+ }
+
+ didDestroy() {
+ this.observers.clear();
+ }
+
+ actorCreated() {
+ const window = this.contentWindow;
+ Cu.exportFunction(this.asRouterMessage.bind(this), window, {
+ defineAs: "ASRouterMessage",
+ });
+ Cu.exportFunction(this.addParentListener.bind(this), window, {
+ defineAs: "ASRouterAddParentListener",
+ });
+ Cu.exportFunction(this.removeParentListener.bind(this), window, {
+ defineAs: "ASRouterRemoveParentListener",
+ });
+ }
+
+ handleEvent(event) {
+ // DOMDocElementCreated is only used to create the actor.
+ }
+
+ addParentListener(listener) {
+ this.observers.add(listener);
+ }
+
+ removeParentListener(listener) {
+ this.observers.delete(listener);
+ }
+
+ receiveMessage({ name, data }) {
+ switch (name) {
+ case "EnterSnippetsPreviewMode":
+ case "UpdateAdminState":
+ case "ClearProviders":
+ case "ClearMessages": {
+ this.observers.forEach(listener => {
+ let result = Cu.cloneInto(
+ {
+ type: name,
+ data,
+ },
+ this.contentWindow
+ );
+ listener(result);
+ });
+ break;
+ }
+ }
+ }
+
+ wrapPromise(promise) {
+ return new this.contentWindow.Promise((resolve, reject) =>
+ promise.then(resolve, reject)
+ );
+ }
+
+ sendQuery(aName, aData = null) {
+ return this.wrapPromise(
+ new Promise(resolve => {
+ super.sendQuery(aName, aData).then(result => {
+ resolve(Cu.cloneInto(result, this.contentWindow));
+ });
+ })
+ );
+ }
+
+ asRouterMessage({ type, data }) {
+ if (VALID_TYPES.has(type)) {
+ switch (type) {
+ case msg.DISABLE_PROVIDER:
+ case msg.ENABLE_PROVIDER:
+ case msg.EXPIRE_QUERY_CACHE:
+ case msg.FORCE_WHATSNEW_PANEL:
+ case msg.CLOSE_WHATSNEW_PANEL:
+ case msg.FORCE_PRIVATE_BROWSING_WINDOW:
+ case msg.IMPRESSION:
+ case msg.RESET_PROVIDER_PREF:
+ case msg.SET_PROVIDER_USER_PREF:
+ case msg.USER_ACTION: {
+ return this.sendAsyncMessage(type, data);
+ }
+ default: {
+ // these messages need a response
+ return this.sendQuery(type, data);
+ }
+ }
+ }
+ throw new Error(`Unexpected type "${type}"`);
+ }
+}
diff --git a/browser/components/newtab/actors/ASRouterParent.jsm b/browser/components/newtab/actors/ASRouterParent.jsm
new file mode 100644
index 0000000000..461d65d65d
--- /dev/null
+++ b/browser/components/newtab/actors/ASRouterParent.jsm
@@ -0,0 +1,111 @@
+/* vim: set ts=2 sw=2 sts=2 et tw=80: */
+/* 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 EXPORTED_SYMBOLS = ["ASRouterParent", "ASRouterTabs"];
+
+const {
+ MESSAGE_TYPE_HASH: { BLOCK_MESSAGE_BY_ID },
+} = ChromeUtils.importESModule(
+ "resource://activity-stream/common/ActorConstants.sys.mjs"
+);
+const { ASRouterNewTabHook } = ChromeUtils.import(
+ "resource://activity-stream/lib/ASRouterNewTabHook.jsm"
+);
+const { ASRouterDefaultConfig } = ChromeUtils.import(
+ "resource://activity-stream/lib/ASRouterDefaultConfig.jsm"
+);
+
+class ASRouterTabs {
+ constructor({ asRouterNewTabHook }) {
+ this.actors = new Set();
+ this.destroy = () => {};
+ asRouterNewTabHook.createInstance(ASRouterDefaultConfig());
+ this.loadingMessageHandler = asRouterNewTabHook
+ .getInstance()
+ .then(initializer => {
+ const parentProcessMessageHandler = initializer.connect({
+ clearChildMessages: ids => this.messageAll("ClearMessages", ids),
+ clearChildProviders: ids => this.messageAll("ClearProviders", ids),
+ updateAdminState: state => this.messageAll("UpdateAdminState", state),
+ });
+ this.destroy = () => {
+ initializer.disconnect();
+ };
+ return parentProcessMessageHandler;
+ });
+ }
+
+ get size() {
+ return this.actors.size;
+ }
+
+ messageAll(message, data) {
+ return Promise.all(
+ [...this.actors].map(a => a.sendAsyncMessage(message, data))
+ );
+ }
+
+ registerActor(actor) {
+ this.actors.add(actor);
+ }
+
+ unregisterActor(actor) {
+ this.actors.delete(actor);
+ }
+}
+
+const defaultTabsFactory = () =>
+ new ASRouterTabs({ asRouterNewTabHook: ASRouterNewTabHook });
+
+class ASRouterParent extends JSWindowActorParent {
+ static tabs = null;
+
+ static nextTabId = 0;
+
+ constructor({ tabsFactory } = { tabsFactory: defaultTabsFactory }) {
+ super();
+ this.tabsFactory = tabsFactory;
+ }
+
+ actorCreated() {
+ ASRouterParent.tabs = ASRouterParent.tabs || this.tabsFactory();
+ this.tabsFactory = null;
+ this.tabId = ++ASRouterParent.nextTabId;
+ ASRouterParent.tabs.registerActor(this);
+ }
+
+ didDestroy() {
+ ASRouterParent.tabs.unregisterActor(this);
+ if (ASRouterParent.tabs.size < 1) {
+ ASRouterParent.tabs.destroy();
+ ASRouterParent.tabs = null;
+ }
+ }
+
+ getTab() {
+ return {
+ id: this.tabId,
+ browser: this.browsingContext.embedderElement,
+ };
+ }
+
+ receiveMessage({ name, data }) {
+ return ASRouterParent.tabs.loadingMessageHandler.then(handler => {
+ if (name === BLOCK_MESSAGE_BY_ID) {
+ return Promise.all([
+ handler.handleMessage(name, data, this.getTab()),
+ // All tabs should clear messages not just preloaded, for example
+ // two different windows can display the same snippet.
+ // ASRouter blocks snippets by campaign not by id so we just tell
+ // other tabs that this specific campaign was blocked.
+ ASRouterParent.tabs.messageAll("ClearMessages", [data.campaign]),
+ ]).then(([handleMessageResult]) => handleMessageResult);
+ }
+ return handler.handleMessage(name, data, this.getTab());
+ });
+ }
+}