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 |
|---|---|
|
the URL is authored by chrome: an |
|
the URL came from content or from the user |
|
you already hold the principal that should trigger the load |
|
you have the originating event and want |
|
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:
|
Result |
|---|---|
|
loads in the selected browser, or in |
|
a new tab in the topmost suitable window |
|
a new tab, with the foreground decision inverted |
|
a new browser window |
|
a new window with no navigation UI, sized by |
|
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 |
|
|---|---|
accel ( |
|
accel + |
|
middle click |
|
|
|
|
|
no modifier |
|
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.ignoreQueryStringignores the query string;replaceQueryStringignores it and then loads the requested URL.adoptIntoActiveWindowmoves 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, orthis.browsingContext.topChromeWindow.A content-privileged
about:page cannot open a tab; send a message and open it from the parent.about:preferencesand other chrome-privilegedabout:pages do loadutilityOverlay.js, so the globals work. From one of its subdialogs, reach the browser window withwindow.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
forceForegrounddefaults 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
forceForegrounddefaults 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
paramsfall into five groups, in the order they appear below:which tab or window to use, and how to open it
the load itself
security, whether the load is allowed, and which cookie container to use
tracking the load elsewhere
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
forceForegroundwas 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.
- static URILoadingHelper.openUILink(window, url, event, aIgnoreButton, aIgnoreAlt, aAllowThirdPartyFixup, aPostData, aReferrerInfo)
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
ignoreButtonto ignore which mouse button was used andignoreAltto 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
ignoreAltabove.aAllowThirdPartyFixup (boolean) – As
params.allowThirdPartyFixupfor openLinkIn.aPostData (object) – As
params.postDatafor openLinkIn.aReferrerInfo (object) – As
params.referrerInfofor 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
paramsargument. 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.