NewTabMessaging.sys.mjs (4617B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 5 import { 6 actionTypes as at, 7 actionCreators as ac, 8 } from "resource://newtab/common/Actions.mjs"; 9 10 export class NewTabMessaging { 11 constructor() { 12 this.initialized = false; 13 this.ASRouterDispatch = null; 14 this.browserSet = new WeakSet(); 15 } 16 17 init() { 18 if (!this.initialized) { 19 this.initialized = true; 20 Services.obs.addObserver(this, "newtab-message"); 21 Services.obs.addObserver(this, "newtab-message-query"); 22 } 23 } 24 25 uninit() { 26 Services.obs.removeObserver(this, "newtab-message-query"); 27 Services.obs.removeObserver(this, "newtab-message"); 28 } 29 30 observe(subject, topic, _data) { 31 if (topic === "newtab-message") { 32 let { targetBrowser, message, dispatch } = subject.wrappedJSObject; 33 this.ASRouterDispatch = dispatch; 34 this.showMessage(targetBrowser, message); 35 } else if (topic === "newtab-message-query") { 36 let { browser } = subject.wrappedJSObject; 37 if (this.browserSet.has(browser.selectedBrowser)) { 38 subject.wrappedJSObject.activeNewtabMessage = true; 39 } 40 } 41 } 42 43 async showMessage(targetBrowser, message) { 44 if (targetBrowser) { 45 let actor = 46 targetBrowser.browsingContext.currentWindowGlobal.getActor( 47 "AboutNewTab" 48 ); 49 if (actor) { 50 let tabDetails = actor.getTabDetails(); 51 if (tabDetails) { 52 // Only send the message for the tab that triggered the message 53 this.store.dispatch( 54 ac.OnlyToOneContent( 55 { 56 type: at.MESSAGE_SET, 57 // send along portID as well so that the child process can easily just update the single tab 58 data: { 59 message, 60 portID: tabDetails.portID, 61 }, 62 }, 63 tabDetails.portID 64 ) 65 ); 66 } 67 } 68 } else { 69 // if targetBrowser is null, send to the preloaded tab / main process 70 // This should only run if message is triggered from asrouter during dev 71 this.store.dispatch( 72 ac.AlsoToPreloaded({ 73 type: at.MESSAGE_SET, 74 data: { 75 message, 76 }, 77 }) 78 ); 79 // Also force visibility for messages sent from about:asrouter during dev 80 this.store.dispatch( 81 ac.AlsoToPreloaded({ 82 type: at.MESSAGE_TOGGLE_VISIBILITY, 83 data: true, 84 }) 85 ); 86 } 87 } 88 89 /** 90 * 91 * @param {string} id ID of message to be blocked 92 */ 93 blockMessage(id) { 94 if (id) { 95 this.ASRouterDispatch?.({ 96 type: "BLOCK_MESSAGE_BY_ID", 97 data: { 98 id, 99 }, 100 }); 101 } 102 } 103 104 /** 105 * Send impression to ASRouter 106 * 107 * @param {object} message 108 */ 109 handleImpression(message) { 110 this.sendTelemetry("IMPRESSION", message); 111 this.ASRouterDispatch?.({ 112 type: "IMPRESSION", 113 data: message, 114 }); 115 } 116 117 /** 118 * Sends telemetry data through ASRouter to 119 * match pattern with ASRouterTelemetry 120 */ 121 sendTelemetry(event, message, source = "newtab") { 122 const data = { 123 action: "newtab_message_user_event", 124 event, 125 event_context: { source, page: "about:newtab" }, 126 message_id: message.id, 127 }; 128 this.ASRouterDispatch?.({ 129 type: "NEWTAB_MESSAGE_TELEMETRY", 130 data, 131 }); 132 } 133 134 notifyVisiblity(action) { 135 const { browser } = action._target; 136 if (browser) { 137 // isVisible 138 if (action.data) { 139 // we dont want to add the browser if it is already part of browserSet 140 if (!this.browserSet.has(browser)) { 141 this.browserSet.add(browser); 142 } 143 } else if (this.browserSet.has(browser)) { 144 this.browserSet.delete(browser); 145 } 146 } 147 } 148 149 onAction(action) { 150 switch (action.type) { 151 case at.INIT: 152 this.init(); 153 break; 154 case at.UNINIT: 155 this.uninit(); 156 break; 157 case at.MESSAGE_IMPRESSION: 158 this.handleImpression(action.data); 159 break; 160 case at.MESSAGE_DISMISS: 161 this.sendTelemetry("DISMISS", action.data.message); 162 break; 163 case at.MESSAGE_CLICK: 164 this.sendTelemetry("CLICK", action.data.message, action.data.source); 165 break; 166 case at.MESSAGE_BLOCK: 167 this.blockMessage(action.data); 168 break; 169 case at.MESSAGE_NOTIFY_VISIBILITY: 170 this.notifyVisiblity(action); 171 break; 172 } 173 } 174 }