Opening tabs and loading URLs

URILoadingHelper.sys.mjs opens tabs and windows for chrome code. It lives in browser/modules/ rather than in the tabbrowser, and utilityOverlay.js exposes most of it as window globals, so a browser window script calls openTrustedLinkIn(url, "tab") without importing anything. Every key of the params object these functions take is in URILoadingHelper API reference at the end of this page.

Reach for it before gBrowser.addTab. Which principal triggers the load, whether the load belongs in a tab at all, whether that tab opens in front or behind, where it is inserted and which container it inherits are all decided here. addTab decides none of them.

Which function to call

Function

Use it when

openTrustedLinkIn(url, where, params)

the URL is authored by chrome: an about: page, a support link, a hard-coded destination

openWebLinkIn(url, where, params)

the URL came from content or from the user

openLinkIn(url, where, params)

you already hold the principal that should trigger the load

openUILink(url, event, params)

you have the originating event and want where derived from its modifiers

switchToTabHavingURI(uri, openNew, params)

the destination should be focused if it is already open

The trusted/web split is a security decision, not a style choice. openTrustedLinkIn() loads with the system principal, which bypasses the URI load checks and lets a javascript: or data: target inherit chrome privileges. openWebLinkIn() supplies a fresh null principal and throws if it is handed a system principal — but nothing guards the other direction, so passing a content-derived URL to openTrustedLinkIn is a privilege escalation that no lint rule will catch for you.

openLinkIn() throws unless params.triggeringPrincipal is set. Supplying it is all the other two do.

// A destination chrome chose.
openTrustedLinkIn("about:preferences#general", "tab");

// A URL that came from a page, or from the urlbar.
openWebLinkIn(url, "tab", { userContextId });

Where the load lands

where decides that, and not all of the answers are tabs:

where

Result

"current"

loads in the selected browser, or in params.targetBrowser

"tab"

a new tab in the topmost suitable window

"tabshifted"

a new tab, with the foreground decision inverted

"window"

a new browser window

"chromeless"

a new window with no navigation UI, sized by params.width and params.height

"save"

saves the target instead of loading it

An unrecognised where does nothing at all, and neither does a falsy url or where — no throw, no warning. "save" is worth knowing about even if you never pass it, because whereToOpenLink returns it for a modified click: it needs initiatingDoc or isContentWindowPrivate in params, and without either it logs an error and gives up.

"current" is not a guarantee. Three cases redirect the load into a new tab: the Firefox View tab, a userContextId that differs from the target browser’s, and a pinned tab being sent to a different host — the last unless the caller passes allowPinnedTabHostChange, which only the urlbar does. The foreground decision is made before the redirect, so a bounced load selects its new tab even when the caller asked for inBackground: true.

"current" also means whatever is selected now. For a caller that arrived through an actor message or after an await, that may no longer be the tab the user acted in. Pass params.targetBrowser, honoured for "current" only, to pin the load to a specific tab.

A new window inherits the source window’s private state. params.private and params.forceNonPrivate override it, which is what separates a context menu’s “Open in New Window” from its “Open in New Private Window”.

Foreground or background

Only "tab" and "tabshifted" have a choice to make, and willLoadInBackground makes it: an explicit params.inBackground wins; otherwise params.forceForeground opens in front, and failing that the browser.tabs.loadInBackground preference decides. "tabshifted" then inverts the answer, which is how a modifier can flip the preference for one click.

openTrustedLinkIn, openWebLinkIn and openUILink all set forceForeground themselves, so a load through them opens in front unless the caller passes inBackground explicitly. Only a direct openLinkIn caller gets the preference by default.

Deriving where from an event

openUILink() unwraps the event — a middle click arrives wrapped in one or two command events — and hands it to whereToOpenLink, which maps the modifiers:

Input

where

accel (Cmd on macOS, Ctrl elsewhere)

"tab"

accel + Shift

"tabshifted"

middle click

"tab", per browser.tabs.opentabfor.middleclick

Shift

"window"

Alt

"save", per browser.altClickSave, off by default

no modifier

"current"

openUILink’s third argument is the params bag, and it also reads ignoreButton and ignoreAlt from there: ignoreButton for middle-click paste, which must not be mistaken for a request to open a window, and ignoreAlt where Alt is unavailable, as in a menu. The older positional form of this argument has no way to carry a triggering principal, which openUILink requires, so pass the object.

whereToOpenLink and willLoadInBackground are BrowserUtils members rather than window globals, and a caller that has an event but wants to open the load itself can use the first directly.

Focusing a tab that may already be open

switchToTabHavingURI() is a browser.js global rather than a utilityOverlay one. It searches the current window and then every other browser window of the same privateness — about:addons is the one exception — and compares the URL exactly. Fragments and query strings therefore have to match unless you opt out:

  • ignoreFragment: "whenComparing" ignores the fragment when matching, and "whenComparingAndReplace" also loads the requested URL into the tab it found.

  • ignoreQueryString ignores the query string; replaceQueryString ignores it and then loads the requested URL.

  • adoptIntoActiveWindow moves a tab found in another window into this one, rather than raising that window.

It returns whether an existing tab was found, so a false means either that openNew opened one or that nothing happened. Everything else in params is forwarded to openTrustedLinkIn.

What you get back

Nothing. openLinkIn and its wrappers return undefined, and none of them waits for the load. For the browser element, pass a callback: resolveOnContentBrowserCreated is called on all three targets, and resolveOnNewTabCreated only on the new-tab path. Both hand you the browser as soon as it exists, which is well before the load finishes — waiting for that is BrowserTestUtils.browserLoaded in a test, and a progress listener (Progress listeners) in product code.

Calling from outside a browser window

The globals exist only in the documents that load utilityOverlay.js. Anywhere else, get a window and call the method on it:

let win =
  lazy.BrowserWindowTracker.getTopWindow() ??
  (await lazy.BrowserWindowTracker.promiseOpenWindow());
win.openTrustedLinkIn(url, "tab");

Importing URILoadingHelper is not a way around needing a window, since every function takes one as its first argument. It saves nothing but the forwarder.

  • A parent actor resolves the window from the browser it is talking to: browser.documentGlobal, or this.browsingContext.topChromeWindow.

  • A content-privileged about: page cannot open a tab; send a message and open it from the parent.

  • about:preferences and other chrome-privileged about: pages do load utilityOverlay.js, so the globals work. From one of its subdialogs, reach the browser window with window.windowRoot.window.

When gBrowser.addTab is right

When you need something openLinkIn cannot express: a lazy browser (createLazyBrowser, lazyTabTitle), a tab with no load (skipLoad), a tab created outside the strip (insertTab), a specific process (preferredRemoteType), or the bulk-restore and tab-group options. That covers session restore and tab duplication, a discarded tab created for an extension, the placeholder tab that adoptTab swaps a browser into, and a test that wants a tab without a load — BrowserTestUtils.addTab, not gBrowser.addTab directly.

addTab throws without a triggeringPrincipal. addTrustedTab and addWebTab supply one on the same terms as their openLinkIn counterparts, so they are what callers reach for.

Pass inBackground rather than selecting the tab afterwards.

// Leaves the tab without an owner.
gBrowser.selectedTab = gBrowser.addTrustedTab(url);

// Selects it and sets the owner.
gBrowser.addTrustedTab(url, { inBackground: false });

A tab’s owner is the tab to return to when it closes, which browser.tabs.selectOwnerOnClose honours by default. addTab opens in the background, and a background tab is given no owner unless it has an opener, so selecting the tab after the call leaves it ownerless and closing it falls through to the adjacent tab instead of back to where the user was.

Two more things addTab leaves to its caller: it reads no preference, so browser.tabs.loadInBackground has no effect on it, and it inherits userContextId only from an opener tab, so a bare addTab in a container tab opens in the default container. Without an opener the new tab is also appended at the end of the strip rather than next to the current one.

URILoadingHelper API reference

Generated from the JSDoc in URILoadingHelper.sys.mjs. The window globals drop the leading window argument these take.

static URILoadingHelper.openTrustedLinkIn(window, url, where, params)

Opens the given URI using the SystemPrincipal as the triggeringPrincipal, unless a more specific principal is provided.

Arguments:
  • window (Window) – The window the load is initiated from.

  • url (string) – The URL to load.

  • where (string) – Where to open the URL, as for openLinkIn.

  • params (object) – Options for the load, as for openLinkIn, except that forceForeground defaults to true.

static URILoadingHelper.openWebLinkIn(window, url, where, params)

Opens the given URI using a NullPrincipal as the triggeringPrincipal, unless a more specific principal is provided. Throws if handed the system principal.

Arguments:
  • window (Window) – The window the load is initiated from.

  • url (string) – The URL to load.

  • where (string) – Where to open the URL, as for openLinkIn.

  • params (object) – Options for the load, as for openLinkIn, except that forceForeground defaults to true.

static URILoadingHelper.openLinkIn(window, url, where, params)

Opens a URL in the place given by where, which can be:

  • "current": the current tab, or a new window if there are no browser windows

  • "tab": a new tab, or a new window if there are no browser windows

  • "tabshifted": as "tab", but with the foreground decision inverted

  • "window": a new window

  • "chromeless": a new minimal window, with no browser navigation UI

  • "save": save to disk, with no filename hint

The keys of params fall into five groups, in the order they appear below:

  1. which tab or window to use, and how to open it

  2. the load itself

  3. security, whether the load is allowed, and which cookie container to use

  4. tracking the load elsewhere

  5. where="save" only

Arguments:
  • window (Window) – The window the load is initiated from.

  • url (string) – The URL to load.

  • where (string) – Where to open the URL, from the list above.

  • params (object) – Options for the load, in the five groups above.

  • params.private (boolean) – Load the URL in a private window.

  • params.forceNonPrivate (boolean) – Force the load to happen in non-private windows.

  • params.relatedToCurrent (boolean) – Whether new tabs should go immediately next to the current tab.

  • params.targetBrowser (Element) – The browser to use for the load. Only used if where == “current”.

  • params.inBackground (boolean) – If explicitly true or false, whether to switch to the tab immediately. If null, will switch to the tab if forceForeground was true. If neither is passed, will defer to the user preference browser.tabs.loadInBackground.

  • params.forceForeground (boolean) – Ignore the user preference and load in the foreground.

  • params.allowPinnedTabHostChange (boolean) – Allow even a pinned tab to change hosts.

  • params.allowPopups (boolean) – whether the link is allowed to open in a popup window (ie one with no browser chrome)

  • params.skipTabAnimation (boolean) – Skip the tab opening animation.

  • params.openerBrowser (Element) – The browser that started the load.

  • params.avoidBrowserFocus (boolean) – Don’t focus the browser element immediately after starting the load. Used by the URL bar to avoid leaking user input into web content, see bug 1641287.

  • params.allowThirdPartyFixup (boolean) – Allow transforming the ‘url’ into a search query.

  • params.postData (nsIInputStream) – Data to post as part of the request.

  • params.referrerInfo (nsIReferrerInfo) – Referrer info for the request.

  • params.indicateErrorPageLoad (boolean) – Whether docshell should throw an exception (i.e. return non-NS_OK) if the load fails.

  • params.charset (string) – Character set to use for the load. Only honoured for tabs. Legacy argument - do not use.

  • params.schemelessInput (SchemelessInputType) – Whether the search/URL term was without an explicit scheme.

  • params.forceAllowDataURI (boolean) – Force allow a data URI to load as a toplevel load.

  • params.userContextId (number) – The userContextId (container identifier) to use for the load. If where is “current” and the specified userContextId differs, a new tab is opened instead.

  • params.allowInheritPrincipal (boolean) – Allow the load to inherit the triggering principal.

  • params.forceAboutBlankViewerInCurrent (boolean) – Force load an about:blank page first. Only used if allowInheritPrincipal is passed or no URL was provided.

  • params.triggeringPrincipal (nsIPrincipal) – Triggering principal to pass to docshell for the load.

  • params.originPrincipal (nsIPrincipal) – Origin principal to pass to docshell for the load.

  • params.originStoragePrincipal (nsIPrincipal) – Storage principal to pass to docshell for the load.

  • params.triggeringRemoteType (string) – The remoteType triggering this load.

  • params.policyContainer (nsIPolicyContainer) – The policyContainer that should apply to the load.

  • params.hasValidUserGestureActivation (boolean) – Indicates if a valid user gesture caused this load. This informs e.g. popup blocker decisions.

  • params.fromExternal (boolean) – Indicates the load was started outside of the browser, e.g. passed on the commandline or through OS mechanisms.

  • params.aswebauth (boolean) – Marks a new window as an ASWebAuthenticationSession auth window so that it is not tracked by session restore. Only used when where == “window” or “chromeless”.

  • params.resolveOnNewTabCreated (function) – This callback will be called when a new tab is created.

  • params.resolveOnContentBrowserCreated (function) – This callback will be called with the content browser once it’s created.

  • params.globalHistoryOptions (object) – Used by places to keep track of search related metadata for loads.

  • params.frameID (number) – Used by webextensions for their loads.

  • params.isContentWindowPrivate (boolean) – Save content as coming from a private window.

  • params.initiatingDoc (Document) – Used to determine where to prompt for a filename.

openUILink handles clicks on UI elements that cause URLs to load.

Arguments:
  • window (Window) – The window the load is initiated from.

  • url (string) – The URL to load.

  • event (Event|object) – The event that asked for the load, or a JSON object representing one. Its modifiers decide where the URL opens.

  • aIgnoreButton (boolean|object) – Options for the load, as for openLinkIn, plus ignoreButton to ignore which mouse button was used and ignoreAlt to ignore the Alt modifier. The positional arguments below are the legacy form of this argument, and cannot carry the required triggering principal.

  • aIgnoreAlt (boolean) – As ignoreAlt above.

  • aAllowThirdPartyFixup (boolean) – As params.allowThirdPartyFixup for openLinkIn.

  • aPostData (object) – As params.postData for openLinkIn.

  • aReferrerInfo (object) – As params.referrerInfo for openLinkIn.

static URILoadingHelper.switchToTabHavingURI(window, aURI, aOpenNew, aOpenParams, aUserContextId, aSplitView)

Switch to a tab that has a given URI, and focuses its browser window. If a matching tab is in this window, it will be switched to. Otherwise, other windows will be searched.

Arguments:
  • window (Window) – The current window

  • aURI (nsIURI|string) – URI to search for

  • aOpenNew (boolean) – True to open a new tab and switch to it, if no existing tab is found. If no suitable window is found, a new one will be opened.

  • aOpenParams (object) – If switching to this URI results in us opening a tab, aOpenParams will be the parameter object that gets passed to openTrustedLinkIn. Please see the documentation for openTrustedLinkIn to see what parameters can be passed via this object. The four properties below are consumed here rather than forwarded.

  • aOpenParams.ignoreFragment (string) – "whenComparing" to exclude the fragment when comparing URIs, "whenComparingAndReplace" to also load the requested URI into the tab that was found.

  • aOpenParams.ignoreQueryString (boolean) – Exclude the query string when comparing URIs.

  • aOpenParams.replaceQueryString (boolean) – Exclude the query string when comparing URIs, and load the requested URI into the tab that was found.

  • aOpenParams.adoptIntoActiveWindow (boolean) – Adopt a tab found in another window into the current one.

  • aUserContextId (number) – If not null, will switch to the first found tab having the provided userContextId.

  • aSplitView (object) – If not null, will move the tab to the active split view instead of switching to tab

Returns:

boolean – True if an existing tab was found, false otherwise

static URILoadingHelper.getTargetWindow(window, params)

Finds a browser window suitable for opening a link matching the requirements given in the params argument. If the current window matches the requirements then it is returned otherwise the top-most window that matches will be returned.

Arguments:
  • window (Window) – The current window.

  • params (object) – Parameters for selecting the window.

  • params.skipPopups (boolean) – Require a non-popup window.

  • params.skipTaskbarTabs (boolean) – Require a non-taskbartab window.

  • params.forceNonPrivate (boolean) – Require a non-private window.

Returns:

Window|null – A matching browser window or null if none matched.

static URILoadingHelper.guessUserContextId(aURI)

Given a URI, guess which container to use to open it. This is used for external openers as a quality of life improvement (e.g. to open a document into the container where you are logged in to the service that hosts it). For now this can only use currently-open tabs, until history is tagged with the container id (https://bugzilla.mozilla.org/show_bug.cgi?id=1283320).

Arguments:
  • aURI (nsIURI) – The URI being opened.

Returns:

number|null – The guessed userContextId, or null if none.