Skip to content

Feature: catchall domains - e.g. *.google.com - #2912

Open
mckenfra wants to merge 1 commit into
mozilla:mainfrom
mckenfra:domains
Open

Feature: catchall domains - e.g. *.google.com#2912
mckenfra wants to merge 1 commit into
mozilla:mainfrom
mckenfra:domains

Conversation

@mckenfra

Copy link
Copy Markdown
Contributor

Before submitting your pull request

  • I agree to license my code under the MPL 2.0 license.
  • I rebased my work on top of the main branch.
  • I ran npm test and all tests passed.
  • I added test coverages if relevant.

Description

Adds the ability to assign all subdomains of a domain to a container.

The list of domains is automatically updated as new sites are assigned to a container. This feature is made possible by the new publicSuffix API that is due to be released in Firefox 153.

mac-domains.mov

Type of change

Select all that apply.

  • Bug fix
  • New feature
  • Major change (fix or feature that would cause existing functionality to work differently than in the current version)

Tag issues related to this pull request:

@Ignotum77

Ignotum77 commented Jul 2, 2026

Copy link
Copy Markdown

Does the code have I fallback when publicSuffix is not supported for older FF versions? Because with the current code it will only work on FireFox 153 or above.

Furthermore, thank for this updated version. It solves the problem discussed by @dannycolin in #2352 (comment).

@mckenfra

mckenfra commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

Does the code have I fallback when publicSuffix is not supported for older FF versions? Because with the current code it will only work of FireFox 153 or above.

No the current implementation does not provide a fallback. Instead, the minimum required Firefox version is increased to 153.0.0.

See the change to the min version here: https://github.com/mozilla/multi-account-containers/pull/2912/changes#diff-6bc2c0b5164076a1b57b067398be19a40d2b8efa3428b03504562ea88593866cR37

@Rob--W

Rob--W commented Jul 20, 2026

Copy link
Copy Markdown
Member

Instead of bumping the minimum required version to 153, would it be feasible to hide the UI and feature when the feature is unavailable? That enables users of older Firefox versions to use other features of MAC.

In particular, Firefox ESR 115 continues to be supported for a while for older Windows versions (until at least the start of the next year according to https://whattrainisitnow.com/calendar/).

Firefox ESR 140 is also still supported, albeit only for a few months more. Fortunately the latest ESR is Firefox ESR 153, which is not too coincidentally the release that includes publicSuffix support.

@mckenfra

Copy link
Copy Markdown
Contributor Author

Makes sense, I will look at updating this PR to disable the catchall domains functionality on earlier Firefox versions.

@Ignotum77

Copy link
Copy Markdown

In particular, Firefox ESR 115 continues to be supported for a while for older Windows versions (until at least the start of the next year according to https://whattrainisitnow.com/calendar/).

However if we wait until March 2027 a "disable the catchall domains functionality on earlier Firefox versions" isn't needed.
So question is, are we willing to wait for this feature?

mckenfra added a commit to mckenfra/multi-account-containers-l10n that referenced this pull request Jul 28, 2026
@mckenfra

Copy link
Copy Markdown
Contributor Author

I have updated this PR as follows:

  1. Change the minimum required Firefox version to 115.0.0 instead of 153.0.0.
  2. Hide the catchall-domain UI and functionality ifbrowser.publicSuffix is unavailable.

@Rob--W

Rob--W commented Jul 29, 2026

Copy link
Copy Markdown
Member

I have updated this PR as follows:
Change the minimum required Firefox version to 115.0.0 instead of 153.0.0.

Should be 115.0, there is no 115.0.0.

FYI: Although the patch "bumps" the version from 91.1.0 to 115.0, there is effectively no change in supported versions, because the addition of data_collection_permissions in a previous commit already made the extension incompatible with versions before 115 due to https://bugzilla.mozilla.org/show_bug.cgi?id=1996829 . And even if it were not incompatible, Firefox versions before 115 cannot receive extension updates anyway due to https://support.mozilla.org/en-US/kb/root-certificate-expiration (both of these issues actually affect Firefox 127 and earlier, but Firefox ESR 115 has received patched to fix the issues, and there is no way to say "Firefox 128+ and Firefox ESR 115", so setting the min version to 115.0 is the closest alternative).

@Rob--W Rob--W left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I glanced over the code and left some comments. I haven't worked extensively on the code base before, so I'll defer to @bakulf for an in depth review.

try {
const domainName = browser.publicSuffix.getDomain(
hostname,
{ allowUnknownSuffix: true, encoding: "display" },

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"display" should be used for display only, not as a storage key. Otherwise changes to display rules (by browser updates) would change the storage key and cause associations to be lost.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. This will require converting the stored domains from punycode to display when showing the domains in the UI. Since there is currently no API available for doing this type of encoding conversion, we will need to call publicSuffix.getDomain(domain, { encoding: "display" }) again on each stored domain. See a relevant discussion on this topic here: w3c/webextensions#1012 (comment)

const domainMatchKeys = domainManager.getDomainMatchKeys(pageUrl);
const siteStoreKey = this.getSiteStoreKey(pageUrl);
const storageResponse = await this.area.get([...domainMatchKeys, siteStoreKey]);
const domainStoreKey = domainMatchKeys.find(key => key in storageResponse);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't you try to select the longest matching domain instead of an arbitrary one? Especially with the specified lookup order on the previous line (domainMatchKeys before siteStoreKeys), the implementation would end up preferring catchall domains over specific domains.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intentional, so that the user is only prompted "Open this site in your assigned Container?" once for an enabled catchall domain, and not every time a new subdomain is visited. See my comment here #2912 (comment)

this.storageArea.get(pageUrl).then((siteSettings) => {
this.storageArea.match(pageUrl).then(([siteStoreKey, siteSettings]) => {
if (siteSettings) {
siteSettings.neverAsk = true;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if I'm reading the code right, but could it be that the UI is prompting for a specific site, and then ends up saving the decision for the catchall? That would be surprising if it is the case.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intentional, to avoid showing the user the "Open this site in your assigned Container?" screen every time they browse to a new subdomain with an enabled catchall domain. I.e. if the user clicks "Remember my decision for this site", it then applies to all subdomains. We could change the wording in the prompt to "Remember my decision for all sites in this domain" when a catchall domain is in effect.

The alternative is to:

  1. Show the prompt each time a new subdomain of an enabled catchall domain is visited (this may become annoying?)
  2. If the user then clicks "Remember my decision for this site", a new site entry will need to be added in storage for that subdomain, to hold the "never ask" setting

const pageUrl = m.pageUrl;
if (m.neverAsk === true) {
if (m.defaultContainer === true) {
this.storageArea.remove(pageUrl);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to account for catchalls as well?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well spotted, I had overlooked this. Just as in #2912 (comment) , the same consideration applies here: do we re-prompt the user for every subdomain of an enabled catchall domain, or do we change the wording to "Remember my decision for all sites in this domain"?

If we re-prompt the user for every subdomain, then the implementation here becomes more complicated: we will need to store a list of subdomains to be explicitly excluded from an enabled catchall domain.

.reverse();
},

getDomainNameFromStoreKey(domainStoreKey) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd expect the getDomainNameFromStoreKey implementation to be completely neutral if publicSuffix is unavailable, but there is no isEnabled check here. Shouldn't we add an isEnabled check here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still need this, even when catchall-domains are disabled, to distinguish storage entries that are catchall-domains from those that are plain sites.

It is theoretically possible for a user to use different browser versions on different devices: one browser may support catchall-domains, the other may not. Due to syncing, a device that does not support catchall-domains may still have those catchall-domain entries in storage. Those entries need to be detectable so that they can be filtered out, otherwise they will end up being displayed in the UI as if they are plain sites. Your comment here has highlighted a bug in my implementation that this filtering-out is currently not done, and so I will need to update the getDomainsAndAssignments() method here: https://github.com/mozilla/multi-account-containers/pull/2912/changes#diff-1ffae9dff098547503a2394dc0bede4c2b4bcbbbdcea33cbbd9aea05d2a8d70fR66

FYI - the storage key format for catchall-domains and plain sites is intentionally identical, so that syncing of the storage entries is done automatically for both catchall-domains and plain sites. Storage key examples:

Storage Key Type
siteContainerMap@@_www.amazon.com Plain site
siteContainerMap@@_*.amazon.com Catchall domain

// The container we have in the assignment map isn't present any
// more so lets remove it then continue the existing load
if (siteSettings && !container) {
if (siteSettings && !siteSettings.isDomain && !container) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the "clean up when container isn't present anymore" logic conditional on !siteSettings.isDomain? With the proposed logic you'd fall through below, to reloadPageInContainer with an invalid cookieStoreId.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed: as with plain sites, this should abort if a container is missing. I will make the change.

However, in my view this existing code is dangerous: deleting assignments silently while a user is browsing. What if the container is temporarily unavailable due to a syncing issue or some other glitch?

It may be better to keep the assignments to the phantom container, and show the container in the UI with special highlighting, allowing the user to manually delete it, or else reinstate it.

Note that user assignments suddenly going missing is a commonly-reported bug with MAC, e.g.: #2901 and also https://chat.mozilla.org/#/room/#containers:mozilla.org This silent deletion of assignments while browsing may be contributing to that issue.

However, I do note other bug reports about deleted containers reappearing, e.g. #2682 While fixing that is important, my view is that doing so silently by deleting missing containers in onBeforeRequest is not the best solution.

if (pageUrlorUrlKey.includes("siteContainerMap@@_")) return pageUrlorUrlKey;
const url = new window.URL(pageUrlorUrlKey);
const domainName = this.getDomainNameFromHostname(url.hostname);
return domainName ? this.getDomainStoreKeyFromHostname(domainName) : null;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getDomainStoreKeyFromHostname is not defined anywhere. In fact... this whole function getDomainStoreKey does not have any callers, so it is dead code.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I will remove this method.

// "siteContainerMap@@_*.amazonaws.com",
// ]
const labels = domainName.split(".");
let suffix = labels.pop();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic here appears to return keys that are not valid domain names. There are public suffices with more than 3 parts, e.g. act.edu.au and edu.au and au are all public suffixes. Is it OK to return such domains here? (I haven't checked other places, would be good to double check as well).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getDomainMatchKeysFromName() is used when determining if the user has enabled a catchall-domain for a site.

The problem this method addresses is that a site may have more than one possible public suffix, and therefore more than one possible catchall-domain. When determining if the user has enabled a catchall-domain for a site, we need to try all possible public suffixes.

E.g. the user browses to site www.mysite.s3.us-west-1.amazonaws.com:

Public Suffix Catchall Domain Key to Try
s3.us-west-1.amazonaws.com *.mysite.s3.us-west-1.amazonaws.com
com *.amazonaws.com

If we don't check for all possible catchall-domains, a bug would manifest if the user:

  1. Browses to banana.amazonaws.com
  2. Adds it to container Amazon
  3. Uses the UI to enable catchall-domain *.amazonaws.com in the same container
  4. Browses to www.mysite.s3.us-west-1.amazonaws.com in a new tab
  5. The longest catchall-domain for this site is *.mysite.s3.us-west-1.amazonaws.com, therefore a lookup of this key in storage would not find enabled catchall-domain *.amazonaws.com

The publicSuffix API provides a method for obtaining the longest registrable domain for a site, but not all possible registrable domains.

Therefore as a workaround, this method creates an array of all possible catchall-domain-keys by removing leading domain labels one-at-a-time. This array is then used for a single storage lookup, and the first match is used.

Note that the returned catchall-domain-key array may indeed contain redundant candidates such as *.act.edu.au and *.edu.au, however these will never match anything in storage since the UI provides no way for the user to actually add these.

Comment thread src/manifest.json
"gecko": {
"id": "@testpilot-containers",
"strict_min_version": "91.1.0",
"strict_min_version": "115.0.0",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As noted at #2912 (comment) this should be 115.0, not 115.0.0

Suggested change
"strict_min_version": "115.0.0",
"strict_min_version": "115.0",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted, I will change this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants