Actions.mjs (15597B)
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 http://mozilla.org/MPL/2.0/. */ 4 5 // This file is accessed from both content and system scopes. 6 7 export const MAIN_MESSAGE_TYPE = "ActivityStream:Main"; 8 export const CONTENT_MESSAGE_TYPE = "ActivityStream:Content"; 9 export const PRELOAD_MESSAGE_TYPE = "ActivityStream:PreloadedBrowser"; 10 export const UI_CODE = 1; 11 export const BACKGROUND_PROCESS = 2; 12 13 /** 14 * globalImportContext - Are we in UI code (i.e. react, a dom) or some kind of background process? 15 * Use this in action creators if you need different logic 16 * for ui/background processes. 17 */ 18 export const globalImportContext = 19 typeof Window === "undefined" ? BACKGROUND_PROCESS : UI_CODE; 20 21 // Create an object that avoids accidental differing key/value pairs: 22 // { 23 // INIT: "INIT", 24 // UNINIT: "UNINIT" 25 // } 26 export const actionTypes = {}; 27 28 for (const type of [ 29 "ABOUT_SPONSORED_TOP_SITES", 30 "ADDONS_INFO_REQUEST", 31 "ADDONS_INFO_RESPONSE", 32 "ADS_FEED_UPDATE", 33 "ADS_INIT", 34 "ADS_RESET", 35 "ADS_UPDATE_SPOCS", 36 "ADS_UPDATE_TILES", 37 "BLOCK_SECTION", 38 "BLOCK_URL", 39 "BOOKMARK_URL", 40 "CARD_SECTION_IMPRESSION", 41 "CLEAR_PREF", 42 "COPY_DOWNLOAD_LINK", 43 "DELETE_BOOKMARK_BY_ID", 44 "DELETE_HISTORY_URL", 45 "DIALOG_CANCEL", 46 "DIALOG_CLOSE", 47 "DIALOG_OPEN", 48 "DISABLE_SEARCH", 49 "DISCOVERY_STREAM_CONFIG_CHANGE", 50 "DISCOVERY_STREAM_CONFIG_RESET", 51 "DISCOVERY_STREAM_CONFIG_RESET_DEFAULTS", 52 "DISCOVERY_STREAM_CONFIG_SETUP", 53 "DISCOVERY_STREAM_CONFIG_SET_VALUE", 54 "DISCOVERY_STREAM_DEV_BLOCKS", 55 "DISCOVERY_STREAM_DEV_BLOCKS_RESET", 56 "DISCOVERY_STREAM_DEV_EXPIRE_CACHE", 57 "DISCOVERY_STREAM_DEV_IDLE_DAILY", 58 "DISCOVERY_STREAM_DEV_IMPRESSIONS", 59 "DISCOVERY_STREAM_DEV_SHOW_PLACEHOLDER", 60 "DISCOVERY_STREAM_DEV_SYNC_RS", 61 "DISCOVERY_STREAM_DEV_SYSTEM_TICK", 62 "DISCOVERY_STREAM_EXPERIMENT_DATA", 63 "DISCOVERY_STREAM_FEEDS_UPDATE", 64 "DISCOVERY_STREAM_FEED_UPDATE", 65 "DISCOVERY_STREAM_IMPRESSION_STATS", 66 "DISCOVERY_STREAM_LAYOUT_RESET", 67 "DISCOVERY_STREAM_LAYOUT_UPDATE", 68 "DISCOVERY_STREAM_LINK_BLOCKED", 69 "DISCOVERY_STREAM_LOADED_CONTENT", 70 "DISCOVERY_STREAM_PERSONALIZATION_INIT", 71 "DISCOVERY_STREAM_PERSONALIZATION_LAST_UPDATED", 72 "DISCOVERY_STREAM_PERSONALIZATION_OVERRIDE", 73 "DISCOVERY_STREAM_PERSONALIZATION_RESET", 74 "DISCOVERY_STREAM_PERSONALIZATION_TOGGLE", 75 "DISCOVERY_STREAM_PERSONALIZATION_UPDATED", 76 "DISCOVERY_STREAM_PREFS_SETUP", 77 "DISCOVERY_STREAM_RETRY_FEED", 78 "DISCOVERY_STREAM_SPOCS_CAPS", 79 "DISCOVERY_STREAM_SPOCS_ENDPOINT", 80 "DISCOVERY_STREAM_SPOCS_ONDEMAND_LOAD", 81 "DISCOVERY_STREAM_SPOCS_ONDEMAND_RESET", 82 "DISCOVERY_STREAM_SPOCS_ONDEMAND_UPDATE", 83 "DISCOVERY_STREAM_SPOCS_PLACEMENTS", 84 "DISCOVERY_STREAM_SPOCS_UPDATE", 85 "DISCOVERY_STREAM_SPOC_BLOCKED", 86 "DISCOVERY_STREAM_SPOC_IMPRESSION", 87 "DISCOVERY_STREAM_SPOC_PLACEHOLDER_DURATION", 88 "DISCOVERY_STREAM_TOPICS_LOADING", 89 "DISCOVERY_STREAM_USER_EVENT", 90 "DOWNLOAD_CHANGED", 91 "FAKE_FOCUS_SEARCH", 92 "FILL_SEARCH_TERM", 93 "FOLLOW_SECTION", 94 "HANDOFF_SEARCH_TO_AWESOMEBAR", 95 "HIDE_PERSONALIZE", 96 "HIDE_TOAST_MESSAGE", 97 "INFERRED_PERSONALIZATION_MODEL_UPDATE", 98 "INFERRED_PERSONALIZATION_REFRESH", 99 "INFERRED_PERSONALIZATION_RESET", 100 "INFERRED_PERSONALIZATION_UPDATE", 101 "INIT", 102 "INLINE_SELECTION_CLICK", 103 "INLINE_SELECTION_IMPRESSION", 104 "MESSAGE_BLOCK", 105 "MESSAGE_CLICK", 106 "MESSAGE_DISMISS", 107 "MESSAGE_IMPRESSION", 108 "MESSAGE_NOTIFY_VISIBILITY", 109 "MESSAGE_SET", 110 "MESSAGE_TOGGLE_VISIBILITY", 111 "NEW_TAB_INIT", 112 "NEW_TAB_INITIAL_STATE", 113 "NEW_TAB_LOAD", 114 "NEW_TAB_REHYDRATED", 115 "NEW_TAB_STATE_REQUEST", 116 "NEW_TAB_STATE_REQUEST_STARTUPCACHE", 117 "NEW_TAB_STATE_REQUEST_WITHOUT_STARTUPCACHE", 118 "NEW_TAB_UNLOAD", 119 "OPEN_DOWNLOAD_FILE", 120 "OPEN_LINK", 121 "OPEN_NEW_WINDOW", 122 "OPEN_PRIVATE_WINDOW", 123 "OPEN_WEBEXT_SETTINGS", 124 "PARTNER_LINK_ATTRIBUTION", 125 "PLACES_BOOKMARKS_REMOVED", 126 "PLACES_BOOKMARK_ADDED", 127 "PLACES_HISTORY_CLEARED", 128 "PLACES_LINKS_CHANGED", 129 "PLACES_LINKS_DELETED", 130 "PLACES_LINK_BLOCKED", 131 "POCKET_CTA", 132 "POCKET_WAITING_FOR_SPOC", 133 "PREFS_INITIAL_VALUES", 134 "PREF_CHANGED", 135 "PREVIEW_REQUEST", 136 "PREVIEW_REQUEST_CANCEL", 137 "PREVIEW_RESPONSE", 138 "PROMO_CARD_CLICK", 139 "PROMO_CARD_DISMISS", 140 "PROMO_CARD_IMPRESSION", 141 "REFRESH_EXTERNAL_COMPONENTS", 142 "REMOVE_DOWNLOAD_FILE", 143 "REPORT_AD_OPEN", 144 "REPORT_AD_SUBMIT", 145 "REPORT_CLOSE", 146 "REPORT_CONTENT_OPEN", 147 "REPORT_CONTENT_SUBMIT", 148 "RICH_ICON_MISSING", 149 "SAVE_SESSION_PERF_DATA", 150 "SCREENSHOT_UPDATED", 151 "SECTION_DEREGISTER", 152 "SECTION_DISABLE", 153 "SECTION_ENABLE", 154 "SECTION_OPTIONS_CHANGED", 155 "SECTION_PERSONALIZATION_SET", 156 "SECTION_PERSONALIZATION_UPDATE", 157 "SECTION_REGISTER", 158 "SECTION_UPDATE", 159 "SECTION_UPDATE_CARD", 160 "SETTINGS_CLOSE", 161 "SETTINGS_OPEN", 162 "SET_PREF", 163 "SHOW_DOWNLOAD_FILE", 164 "SHOW_FIREFOX_ACCOUNTS", 165 "SHOW_PERSONALIZE", 166 "SHOW_PRIVACY_INFO", 167 "SHOW_SEARCH", 168 "SHOW_TOAST_MESSAGE", 169 "SKIPPED_SIGNIN", 170 "SOV_UPDATED", 171 "SUBMIT_EMAIL", 172 "SUBMIT_SIGNIN", 173 "SYSTEM_TICK", 174 "TELEMETRY_IMPRESSION_STATS", 175 "TELEMETRY_USER_EVENT", 176 "TOPIC_SELECTION_IMPRESSION", 177 "TOPIC_SELECTION_MAYBE_LATER", 178 "TOPIC_SELECTION_SPOTLIGHT_CLOSE", 179 "TOPIC_SELECTION_SPOTLIGHT_OPEN", 180 "TOPIC_SELECTION_USER_DISMISS", 181 "TOPIC_SELECTION_USER_OPEN", 182 "TOPIC_SELECTION_USER_SAVE", 183 "TOP_SITES_ADD", 184 "TOP_SITES_CANCEL_EDIT", 185 "TOP_SITES_CLOSE_SEARCH_SHORTCUTS_MODAL", 186 "TOP_SITES_EDIT", 187 "TOP_SITES_INSERT", 188 "TOP_SITES_OPEN_SEARCH_SHORTCUTS_MODAL", 189 "TOP_SITES_ORGANIC_IMPRESSION_STATS", 190 "TOP_SITES_PIN", 191 "TOP_SITES_PREFS_UPDATED", 192 "TOP_SITES_SPONSORED_IMPRESSION_STATS", 193 "TOP_SITES_UNPIN", 194 "TOP_SITES_UPDATED", 195 "TOTAL_BOOKMARKS_REQUEST", 196 "TOTAL_BOOKMARKS_RESPONSE", 197 "UNBLOCK_SECTION", 198 "UNFOLLOW_SECTION", 199 "UNINIT", 200 "UPDATE_PINNED_SEARCH_SHORTCUTS", 201 "UPDATE_SEARCH_SHORTCUTS", 202 "WALLPAPERS_CATEGORY_SET", 203 "WALLPAPERS_CUSTOM_SET", 204 "WALLPAPERS_FEATURE_HIGHLIGHT_COUNTER_INCREMENT", 205 "WALLPAPERS_FEATURE_HIGHLIGHT_CTA_CLICKED", 206 "WALLPAPERS_FEATURE_HIGHLIGHT_DISMISSED", 207 "WALLPAPERS_FEATURE_HIGHLIGHT_SEEN", 208 "WALLPAPERS_SET", 209 "WALLPAPER_CATEGORY_CLICK", 210 "WALLPAPER_CLICK", 211 "WALLPAPER_REMOVE_UPLOAD", 212 "WALLPAPER_UPLOAD", 213 "WEATHER_DETECT_LOCATION", 214 "WEATHER_IMPRESSION", 215 "WEATHER_LOAD_ERROR", 216 "WEATHER_LOCATION_DATA_UPDATE", 217 "WEATHER_LOCATION_SEARCH_UPDATE", 218 "WEATHER_LOCATION_SUGGESTIONS_UPDATE", 219 "WEATHER_OPEN_PROVIDER_URL", 220 "WEATHER_OPT_IN_PROMPT_SELECTION", 221 "WEATHER_QUERY_UPDATE", 222 "WEATHER_SEARCH_ACTIVE", 223 "WEATHER_UPDATE", 224 "WEATHER_USER_OPT_IN_LOCATION", 225 "WEBEXT_CLICK", 226 "WEBEXT_DISMISS", 227 "WIDGETS_LISTS_CHANGE_SELECTED", 228 "WIDGETS_LISTS_SET", 229 "WIDGETS_LISTS_SET_SELECTED", 230 "WIDGETS_LISTS_UPDATE", 231 "WIDGETS_LISTS_USER_EVENT", 232 "WIDGETS_LISTS_USER_IMPRESSION", 233 "WIDGETS_TIMER_END", 234 "WIDGETS_TIMER_PAUSE", 235 "WIDGETS_TIMER_PLAY", 236 "WIDGETS_TIMER_RESET", 237 "WIDGETS_TIMER_SET", 238 "WIDGETS_TIMER_SET_DURATION", 239 "WIDGETS_TIMER_SET_TYPE", 240 "WIDGETS_TIMER_USER_EVENT", 241 "WIDGETS_TIMER_USER_IMPRESSION", 242 ]) { 243 actionTypes[type] = type; 244 } 245 246 // Helper function for creating routed actions between content and main 247 // Not intended to be used by consumers 248 function _RouteMessage(action, options) { 249 const meta = action.meta ? { ...action.meta } : {}; 250 if (!options || !options.from || !options.to) { 251 throw new Error( 252 "Routed Messages must have options as the second parameter, and must at least include a .from and .to property." 253 ); 254 } 255 // For each of these fields, if they are passed as an option, 256 // add them to the action. If they are not defined, remove them. 257 ["from", "to", "toTarget", "fromTarget", "skipMain", "skipLocal"].forEach( 258 o => { 259 if (typeof options[o] !== "undefined") { 260 meta[o] = options[o]; 261 } else if (meta[o]) { 262 delete meta[o]; 263 } 264 } 265 ); 266 return { ...action, meta }; 267 } 268 269 /** 270 * AlsoToMain - Creates a message that will be dispatched locally and also sent to the Main process. 271 * 272 * @param {object} action Any redux action (required) 273 * @param {object} options 274 * @param {bool} skipLocal Used by OnlyToMain to skip the main reducer 275 * @param {string} fromTarget The id of the content port from which the action originated. (optional) 276 * @return {object} An action with added .meta properties 277 */ 278 function AlsoToMain(action, fromTarget, skipLocal) { 279 return _RouteMessage(action, { 280 from: CONTENT_MESSAGE_TYPE, 281 to: MAIN_MESSAGE_TYPE, 282 fromTarget, 283 skipLocal, 284 }); 285 } 286 287 /** 288 * OnlyToMain - Creates a message that will be sent to the Main process and skip the local reducer. 289 * 290 * @param {object} action Any redux action (required) 291 * @param {object} options 292 * @param {string} fromTarget The id of the content port from which the action originated. (optional) 293 * @return {object} An action with added .meta properties 294 */ 295 function OnlyToMain(action, fromTarget) { 296 return AlsoToMain(action, fromTarget, true); 297 } 298 299 /** 300 * BroadcastToContent - Creates a message that will be dispatched to main and sent to ALL content processes. 301 * 302 * @param {object} action Any redux action (required) 303 * @param {object} options (optional) 304 * @return {object} An action with added .meta properties 305 */ 306 function BroadcastToContent(action, options) { 307 return _RouteMessage(action, { 308 from: MAIN_MESSAGE_TYPE, 309 to: CONTENT_MESSAGE_TYPE, 310 ...options, 311 }); 312 } 313 314 /** 315 * AlsoToOneContent - Creates a message that will be will be dispatched to the main store 316 * and also sent to a particular Content process. 317 * 318 * @param {object} action Any redux action (required) 319 * @param {string} target The id of a content port 320 * @param {bool} skipMain Used by OnlyToOneContent to skip the main process 321 * @return {object} An action with added .meta properties 322 */ 323 function AlsoToOneContent(action, target, skipMain) { 324 if (!target) { 325 throw new Error( 326 "You must provide a target ID as the second parameter of AlsoToOneContent. If you want to send to all content processes, use BroadcastToContent" 327 ); 328 } 329 return _RouteMessage(action, { 330 from: MAIN_MESSAGE_TYPE, 331 to: CONTENT_MESSAGE_TYPE, 332 toTarget: target, 333 skipMain, 334 }); 335 } 336 337 /** 338 * OnlyToOneContent - Creates a message that will be sent to a particular Content process 339 * and skip the main reducer. 340 * 341 * @param {object} action Any redux action (required) 342 * @param {string} target The id of a content port 343 * @return {object} An action with added .meta properties 344 */ 345 function OnlyToOneContent(action, target) { 346 return AlsoToOneContent(action, target, true); 347 } 348 349 /** 350 * AlsoToPreloaded - Creates a message that dispatched to the main reducer and also sent to the preloaded tab. 351 * 352 * @param {object} action Any redux action (required) 353 * @return {object} An action with added .meta properties 354 */ 355 function AlsoToPreloaded(action) { 356 return _RouteMessage(action, { 357 from: MAIN_MESSAGE_TYPE, 358 to: PRELOAD_MESSAGE_TYPE, 359 }); 360 } 361 362 /** 363 * UserEvent - A telemetry ping indicating a user action. This should only 364 * be sent from the UI during a user session. 365 * 366 * @param {object} data Fields to include in the ping (source, etc.) 367 * @return {object} An AlsoToMain action 368 */ 369 function UserEvent(data) { 370 return AlsoToMain({ 371 type: actionTypes.TELEMETRY_USER_EVENT, 372 data, 373 }); 374 } 375 376 /** 377 * DiscoveryStreamUserEvent - A telemetry ping indicating a user action from Discovery Stream. This should only 378 * be sent from the UI during a user session. 379 * 380 * @param {object} data Fields to include in the ping (source, etc.) 381 * @return {object} An AlsoToMain action 382 */ 383 function DiscoveryStreamUserEvent(data) { 384 return AlsoToMain({ 385 type: actionTypes.DISCOVERY_STREAM_USER_EVENT, 386 data, 387 }); 388 } 389 390 /** 391 * ImpressionStats - A telemetry ping indicating an impression stats. 392 * 393 * @param {object} data Fields to include in the ping 394 * @param {int} importContext (For testing) Override the import context for testing. 395 * #return {object} An action. For UI code, a AlsoToMain action. 396 */ 397 function ImpressionStats(data, importContext = globalImportContext) { 398 const action = { 399 type: actionTypes.TELEMETRY_IMPRESSION_STATS, 400 data, 401 }; 402 return importContext === UI_CODE ? AlsoToMain(action) : action; 403 } 404 405 /** 406 * DiscoveryStreamImpressionStats - A telemetry ping indicating an impression stats in Discovery Stream. 407 * 408 * @param {object} data Fields to include in the ping 409 * @param {int} importContext (For testing) Override the import context for testing. 410 * #return {object} An action. For UI code, a AlsoToMain action. 411 */ 412 function DiscoveryStreamImpressionStats( 413 data, 414 importContext = globalImportContext 415 ) { 416 const action = { 417 type: actionTypes.DISCOVERY_STREAM_IMPRESSION_STATS, 418 data, 419 }; 420 return importContext === UI_CODE ? AlsoToMain(action) : action; 421 } 422 423 /** 424 * DiscoveryStreamLoadedContent - A telemetry ping indicating a content gets loaded in Discovery Stream. 425 * 426 * @param {object} data Fields to include in the ping 427 * @param {int} importContext (For testing) Override the import context for testing. 428 * #return {object} An action. For UI code, a AlsoToMain action. 429 */ 430 function DiscoveryStreamLoadedContent( 431 data, 432 importContext = globalImportContext 433 ) { 434 const action = { 435 type: actionTypes.DISCOVERY_STREAM_LOADED_CONTENT, 436 data, 437 }; 438 return importContext === UI_CODE ? AlsoToMain(action) : action; 439 } 440 441 function SetPref(prefName, value, importContext = globalImportContext) { 442 const action = { 443 type: actionTypes.SET_PREF, 444 data: { name: prefName, value }, 445 }; 446 return importContext === UI_CODE ? AlsoToMain(action) : action; 447 } 448 449 function WebExtEvent(type, data, importContext = globalImportContext) { 450 if (!data || !data.source) { 451 throw new Error( 452 'WebExtEvent actions should include a property "source", the id of the webextension that should receive the event.' 453 ); 454 } 455 const action = { type, data }; 456 return importContext === UI_CODE ? AlsoToMain(action) : action; 457 } 458 459 export const actionCreators = { 460 BroadcastToContent, 461 UserEvent, 462 DiscoveryStreamUserEvent, 463 ImpressionStats, 464 AlsoToOneContent, 465 OnlyToOneContent, 466 AlsoToMain, 467 OnlyToMain, 468 AlsoToPreloaded, 469 SetPref, 470 WebExtEvent, 471 DiscoveryStreamImpressionStats, 472 DiscoveryStreamLoadedContent, 473 }; 474 475 // These are helpers to test for certain kinds of actions 476 export const actionUtils = { 477 isSendToMain(action) { 478 if (!action.meta) { 479 return false; 480 } 481 return ( 482 action.meta.to === MAIN_MESSAGE_TYPE && 483 action.meta.from === CONTENT_MESSAGE_TYPE 484 ); 485 }, 486 isBroadcastToContent(action) { 487 if (!action.meta) { 488 return false; 489 } 490 if (action.meta.to === CONTENT_MESSAGE_TYPE && !action.meta.toTarget) { 491 return true; 492 } 493 return false; 494 }, 495 isSendToOneContent(action) { 496 if (!action.meta) { 497 return false; 498 } 499 if (action.meta.to === CONTENT_MESSAGE_TYPE && action.meta.toTarget) { 500 return true; 501 } 502 return false; 503 }, 504 isSendToPreloaded(action) { 505 if (!action.meta) { 506 return false; 507 } 508 return ( 509 action.meta.to === PRELOAD_MESSAGE_TYPE && 510 action.meta.from === MAIN_MESSAGE_TYPE 511 ); 512 }, 513 isFromMain(action) { 514 if (!action.meta) { 515 return false; 516 } 517 return ( 518 action.meta.from === MAIN_MESSAGE_TYPE && 519 action.meta.to === CONTENT_MESSAGE_TYPE 520 ); 521 }, 522 getPortIdOfSender(action) { 523 return (action.meta && action.meta.fromTarget) || null; 524 }, 525 _RouteMessage, 526 };