forked from mirrors/gecko-dev
		
	 302950a392
			
		
	
	
		302950a392
		
	
	
	
	
		
			
			Before this patch, the about:home/about:newtab Redux store code had some middleware that queued any messages sent from the page before the parent had sent any messages. Presumably this was so that those messages wouldn't be dropped if they were sent while the parent process was still setting up its Feeds. Unfortunately, there's a race here - if the parent process _is_ ready and just chooses not to send any messages right away, the loaded about:home/about:newtab document will just hold on to any actions until the parent process has sent something down to it. The Talos test that was failing here was waiting for the initial about:home page to send a message which would record a Telemetry probe. That message wasn't arriving in time. Presumably, _eventually_ the parent process would have sent a message down to the about:home page which would flush the actions, but the Talos test would time out before that would occur. This patch changes things by having the _parent_ process queue any messages sent from the content in the event that the ActivityStreamMessageChannel is not yet set up. Once it is set up, those messages are dispatched after the simulated NEW_TAB_INIT and NEW_TAB_LOAD for those early tabs are sent to the parent process Redux store. Differential Revision: https://phabricator.services.mozilla.com/D195179
		
			
				
	
	
		
			173 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			173 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* 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 { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
 | |
| 
 | |
| const lazy = {};
 | |
| 
 | |
| ChromeUtils.defineESModuleGetters(lazy, {
 | |
|   AboutNewTab: "resource:///modules/AboutNewTab.sys.mjs",
 | |
| });
 | |
| 
 | |
| XPCOMUtils.defineLazyModuleGetters(lazy, {
 | |
|   ASRouter: "resource://activity-stream/lib/ASRouter.jsm",
 | |
| });
 | |
| 
 | |
| // A mapping of loaded new tab pages, where the mapping is:
 | |
| //   browser -> { actor, browser, browsingContext, portID, url, loaded }
 | |
| let gLoadedTabs = new Map();
 | |
| 
 | |
| export class AboutNewTabParent extends JSWindowActorParent {
 | |
|   static get loadedTabs() {
 | |
|     return gLoadedTabs;
 | |
|   }
 | |
| 
 | |
|   getTabDetails() {
 | |
|     let browser = this.browsingContext.top.embedderElement;
 | |
|     return browser ? gLoadedTabs.get(browser) : null;
 | |
|   }
 | |
| 
 | |
|   handleEvent(event) {
 | |
|     if (event.type == "SwapDocShells") {
 | |
|       let oldBrowser = this.browsingContext.top.embedderElement;
 | |
|       let newBrowser = event.detail;
 | |
| 
 | |
|       let tabDetails = gLoadedTabs.get(oldBrowser);
 | |
|       if (tabDetails) {
 | |
|         tabDetails.browser = newBrowser;
 | |
|         gLoadedTabs.delete(oldBrowser);
 | |
|         gLoadedTabs.set(newBrowser, tabDetails);
 | |
| 
 | |
|         oldBrowser.removeEventListener("SwapDocShells", this);
 | |
|         newBrowser.addEventListener("SwapDocShells", this);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   async receiveMessage(message) {
 | |
|     switch (message.name) {
 | |
|       case "AboutNewTabVisible":
 | |
|         await lazy.ASRouter.waitForInitialized;
 | |
|         lazy.ASRouter.sendTriggerMessage({
 | |
|           browser: this.browsingContext.top.embedderElement,
 | |
|           // triggerId and triggerContext
 | |
|           id: "defaultBrowserCheck",
 | |
|           context: { source: "newtab" },
 | |
|         });
 | |
|         break;
 | |
| 
 | |
|       case "Init": {
 | |
|         let browsingContext = this.browsingContext;
 | |
|         let browser = browsingContext.top.embedderElement;
 | |
|         if (!browser) {
 | |
|           return;
 | |
|         }
 | |
| 
 | |
|         let tabDetails = {
 | |
|           actor: this,
 | |
|           browser,
 | |
|           browsingContext,
 | |
|           portID: message.data.portID,
 | |
|           url: message.data.url,
 | |
|         };
 | |
|         gLoadedTabs.set(browser, tabDetails);
 | |
| 
 | |
|         browser.addEventListener("SwapDocShells", this);
 | |
|         browser.addEventListener("EndSwapDocShells", this);
 | |
| 
 | |
|         this.notifyActivityStreamChannel("onNewTabInit", message, tabDetails);
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       case "Load":
 | |
|         this.notifyActivityStreamChannel("onNewTabLoad", message);
 | |
|         break;
 | |
| 
 | |
|       case "Unload": {
 | |
|         let tabDetails = this.getTabDetails();
 | |
|         if (!tabDetails) {
 | |
|           // When closing a tab, the embedderElement can already be disconnected, so
 | |
|           // as a backup, look up the tab details by browsing context.
 | |
|           tabDetails = this.getByBrowsingContext(this.browsingContext);
 | |
|         }
 | |
| 
 | |
|         if (!tabDetails) {
 | |
|           return;
 | |
|         }
 | |
| 
 | |
|         tabDetails.browser.removeEventListener("EndSwapDocShells", this);
 | |
| 
 | |
|         gLoadedTabs.delete(tabDetails.browser);
 | |
| 
 | |
|         this.notifyActivityStreamChannel("onNewTabUnload", message, tabDetails);
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       case "ActivityStream:ContentToMain":
 | |
|         this.notifyActivityStreamChannel("onMessage", message);
 | |
|         break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   notifyActivityStreamChannel(name, message, tabDetails) {
 | |
|     if (!tabDetails) {
 | |
|       tabDetails = this.getTabDetails();
 | |
|       if (!tabDetails) {
 | |
|         return;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     let channel = this.getChannel();
 | |
|     if (!channel) {
 | |
|       // We're not yet ready to deal with these messages. We'll queue
 | |
|       // them for now, and then dispatch them once the channel has finished
 | |
|       // being set up.
 | |
|       AboutNewTabParent.#queuedMessages.push({
 | |
|         actor: this,
 | |
|         name,
 | |
|         message,
 | |
|         tabDetails,
 | |
|       });
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     let messageToSend = {
 | |
|       target: this,
 | |
|       data: message.data || {},
 | |
|     };
 | |
| 
 | |
|     channel[name](messageToSend, tabDetails);
 | |
|   }
 | |
| 
 | |
|   getByBrowsingContext(expectedBrowsingContext) {
 | |
|     for (let tabDetails of AboutNewTabParent.loadedTabs.values()) {
 | |
|       if (tabDetails.browsingContext === expectedBrowsingContext) {
 | |
|         return tabDetails;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return null;
 | |
|   }
 | |
| 
 | |
|   getChannel() {
 | |
|     return lazy.AboutNewTab.activityStream?.store?.getMessageChannel();
 | |
|   }
 | |
| 
 | |
|   // Queued messages sent from the content process. These are only queued
 | |
|   // if an AboutNewTabParent receives them before the
 | |
|   // ActivityStreamMessageChannel exists.
 | |
|   static #queuedMessages = [];
 | |
| 
 | |
|   /**
 | |
|    * If there were any messages sent from content before the
 | |
|    * ActivityStreamMessageChannel was set up, dispatch them now.
 | |
|    */
 | |
|   static flushQueuedMessagesFromContent() {
 | |
|     for (let messageData of AboutNewTabParent.#queuedMessages) {
 | |
|       let { actor, name, message, tabDetails } = messageData;
 | |
|       actor.notifyActivityStreamChannel(name, message, tabDetails);
 | |
|     }
 | |
|     AboutNewTabParent.#queuedMessages = [];
 | |
|   }
 | |
| }
 |