1/* vim: set ts=2 sw=2 sts=2 et tw=80: */
2/* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5"use strict";
6
7const EXPORTED_SYMBOLS = ["ASRouterNewTabHook"];
8
9class ASRouterNewTabHookInstance {
10  constructor() {
11    this._newTabMessageHandler = null;
12    this._parentProcessMessageHandler = null;
13    this._router = null;
14    this._clearChildMessages = (...params) =>
15      this._newTabMessageHandler === null
16        ? Promise.resolve()
17        : this._newTabMessageHandler.clearChildMessages(...params);
18    this._clearChildProviders = (...params) =>
19      this._newTabMessageHandler === null
20        ? Promise.resolve()
21        : this._newTabMessageHandler.clearChildProviders(...params);
22    this._updateAdminState = (...params) =>
23      this._newTabMessageHandler === null
24        ? Promise.resolve()
25        : this._newTabMessageHandler.updateAdminState(...params);
26  }
27
28  /**
29   * Params:
30   *    object - {
31   *      messageHandler: message handler for parent process messages
32   *      {
33   *        handleCFRAction: Responds to CFR action and returns a Promise
34   *        handleTelemetry: Logs telemetry events and returns nothing
35   *      },
36   *      router: ASRouter instance
37   *      createStorage: function to create DB storage for ASRouter
38   *   }
39   */
40  async initialize({ messageHandler, router, createStorage }) {
41    this._parentProcessMessageHandler = messageHandler;
42    this._router = router;
43    if (!this._router.initialized) {
44      const storage = await createStorage();
45      await this._router.init({
46        storage,
47        sendTelemetry: this._parentProcessMessageHandler.handleTelemetry,
48        dispatchCFRAction: this._parentProcessMessageHandler.handleCFRAction,
49        clearChildMessages: this._clearChildMessages,
50        clearChildProviders: this._clearChildProviders,
51        updateAdminState: this._updateAdminState,
52      });
53    }
54  }
55
56  destroy() {
57    if (this._router?.initialized) {
58      this.disconnect();
59      this._router.uninit();
60    }
61  }
62
63  /**
64   * Connects new tab message handler to hook.
65   * Note: Should only ever be called on an initialized instance
66   * Params:
67   *    newTabMessageHandler - {
68   *      clearChildMessages: clears child messages and returns Promise
69   *      clearChildProviders: clears child providers and returns Promise.
70   *      updateAdminState: updates admin state and returns Promise
71   *   }
72   * Returns: parentProcessMessageHandler
73   */
74  connect(newTabMessageHandler) {
75    this._newTabMessageHandler = newTabMessageHandler;
76    return this._parentProcessMessageHandler;
77  }
78
79  /**
80   * Disconnects new tab message handler from hook.
81   */
82  disconnect() {
83    this._newTabMessageHandler = null;
84  }
85}
86
87class AwaitSingleton {
88  constructor() {
89    this.instance = null;
90    const initialized = new Promise(resolve => {
91      this.setInstance = instance => {
92        this.setInstance = () => {};
93        this.instance = instance;
94        resolve(instance);
95      };
96    });
97    this.getInstance = () => initialized;
98  }
99}
100
101const ASRouterNewTabHook = (() => {
102  const singleton = new AwaitSingleton();
103  const instance = new ASRouterNewTabHookInstance();
104  return {
105    getInstance: singleton.getInstance,
106
107    /**
108     * Param:
109     *    params - see ASRouterNewTabHookInstance.init
110     */
111    createInstance: async params => {
112      await instance.initialize(params);
113      singleton.setInstance(instance);
114    },
115
116    destroy: () => {
117      instance.destroy();
118    },
119  };
120})();
121