My POC shows that this is indeed true, probably by design (I guess it makes sense, sort of).
Lightning-console apps consider every LOADED tab in scope of an application event. To further complicate this behavior, if more than one record tab is LOADED, then you refresh the browser, only the current record tab is back in scope of an application event. I think this is because the second tab hasn't bootstrapped into the one.app namespace yet.
FYI, I'm using my own service component framework (https://github.com/tsalb/sfdc-lightning-service-components) to fire these events, but the underlying application event firing mechanism is the same, I just have method wrappers to help fire generic ones.
Place both on same Lightning Record Page in Lightning-console app
AppEventFire.cmp
<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId">
<c:EventService aura:id="eventService"/>
<lightning:button label="Fire App Event" onclick="{! c.handleFireAppEventClick }"/>
</aura:component>
AppEventFireController.js
({
handleFireAppEventClick : function(component, event, helper) {
var eventService = component.find("eventService");
eventService.fireAppEvent("TEST_RECORD_EVENT_FIRE");
}
})
AppEventListener.cmp
<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId">
<aura:handler event="c:ServiceAppEvent" action="{! c.handleApplicationEvent }"/>
<aura:attribute name="record" type="Object"/>
<aura:attribute name="simpleRecord" type="Object"/>
<aura:attribute name="recordError" type="String"/>
<force:recordData aura:id="recordHandler"
recordId="{!v.recordId}"
layoutType="FULL"
targetRecord="{!v.record}"
targetFields="{!v.simpleRecord}"
targetError="{!v.recordError}"
mode="EDIT"
/>
{! v.simpleRecord.AppEventCount__c }
</aura:component>
AppEventListenerController.js
({
handleApplicationEvent : function(component, event, helper) {
var params = event.getParams();
switch(params.appEventKey) {
case "TEST_RECORD_EVENT_FIRE":
var currentCount = component.get("v.simpleRecord.AppEventCount__c");
currentCount++;
component.set("v.simpleRecord.AppEventCount__c", currentCount);
component.find("recordHandler").saveRecord($A.getCallback(function(saveResult) {
if (saveResult.state === "SUCCESS" || saveResult.state === "DRAFT") {
component.find("recordHandler").reloadRecord();
}
}));
break;
}
},
})
Then open up two records within the same Lightning-console app, fully load both (so that the fire.cmp and listener.cmp exist, are rendered). You'll see that if you fully load two Lightning-console app tabs, then click the button from either one, both will receive it. I'm writing to server to verify that these application events persist and it's not just a trick of browser / component memory.
P.S. I've added what I'm calling a Record Event to my framework which deals with this very specific issue, very specifically inside lightning-console apps. I'll update my framework repo when I get some time.
Best Answer
This is an expected behaviour since postMessage with targetOrigin '*' sends the message with no target preference and your tabs are in the same lightning container.
What you can do is you can send the id of the data you are handling for each tab as part of the message and use it to decide if you want to do event handling or not.
There is an explanation about one-to-many communication here in One-to-One vs One-to-Many Messaging Scheme.
In receiving end:
Also it is a good idea to specify targetOrigin as using '*' is not safe depending on the data you are sending.
Documentation on postMessage