1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 ChromeUtils.defineESModuleGetters(lazy, {
8 PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
11 import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
13 export function nsWebHandlerApp() {}
15 nsWebHandlerApp.prototype = {
16 classDescription: "A web handler for protocols and content",
17 classID: Components.ID("8b1ae382-51a9-4972-b930-56977a57919d"),
18 contractID: "@mozilla.org/uriloader/web-handler-app;1",
19 QueryInterface: ChromeUtils.generateQI(["nsIWebHandlerApp", "nsIHandlerApp"]),
22 _detailedDescription: null,
35 get detailedDescription() {
36 return this._detailedDescription;
39 set detailedDescription(aDesc) {
40 this._detailedDescription = aDesc;
45 throw Components.Exception("", Cr.NS_ERROR_NULL_POINTER);
49 aHandlerApp instanceof Ci.nsIWebHandlerApp &&
50 aHandlerApp.uriTemplate &&
52 aHandlerApp.uriTemplate == this.uriTemplate
59 launchWithURI(aURI, aBrowsingContext) {
60 // XXX need to strip passwd & username from URI to handle, as per the
61 // WhatWG HTML5 draft. nsSimpleURL, which is what we're going to get,
62 // can't do this directly. Ideally, we'd fix nsStandardURL to make it
63 // possible to turn off all of its quirks handling, and use that...
65 let { scheme } = aURI;
66 if (scheme == "ftp" || scheme == "ftps" || scheme == "sftp") {
67 // FTP URLs are parsed by nsStandardURL, so clearing the username and
68 // password does not throw.
69 aURI = aURI.mutate().setUserPass("").finalize();
72 // encode the URI to be handled
73 var escapedUriSpecToHandle = encodeURIComponent(aURI.spec);
75 // insert the encoded URI and create the object version.
76 var uriToSend = Services.io.newURI(
77 this.uriTemplate.replace("%s", escapedUriSpecToHandle)
80 let policy = WebExtensionPolicy.getByURI(uriToSend);
81 let privateAllowed = !policy || policy.privateBrowsingAllowed;
83 if (!scheme.startsWith("web+") && !scheme.startsWith("ext+")) {
84 // If we're in a frame, check if we're a built-in scheme, in which case,
85 // override the target browsingcontext. It's not a good idea to try to
86 // load mail clients or other apps with potential for logged in data into
87 // iframes, and in any case it's unlikely to work due to framing
88 // restrictions employed by the target site.
89 if (aBrowsingContext && aBrowsingContext != aBrowsingContext.top) {
90 aBrowsingContext = null;
93 // Unset the browsing context when in a pinned tab and changing hosts
94 // to force loading the mail handler in a new unpinned tab.
95 if (aBrowsingContext?.top.isAppTab) {
96 let docURI = aBrowsingContext.currentWindowGlobal.documentURI;
97 let docHost, sendHost;
100 docHost = docURI?.host;
101 sendHost = uriToSend?.host;
104 // Special case: ignore "www" prefix if it is part of host string
105 if (docHost?.startsWith("www.")) {
106 docHost = docHost.replace(/^www\./, "");
108 if (sendHost?.startsWith("www.")) {
109 sendHost = sendHost.replace(/^www\./, "");
112 if (docHost != sendHost) {
113 aBrowsingContext = null;
118 // if we have a context, use the URI loader to load there
119 if (aBrowsingContext) {
120 if (aBrowsingContext.usePrivateBrowsing && !privateAllowed) {
121 throw Components.Exception(
122 "Extension not allowed in private windows.",
123 Cr.NS_ERROR_FILE_NOT_FOUND
127 let triggeringPrincipal =
128 Services.scriptSecurityManager.getSystemPrincipal();
129 Services.tm.dispatchToMainThread(() =>
130 aBrowsingContext.loadURI(uriToSend, { triggeringPrincipal })
135 // The window type depends on the app.
137 AppConstants.MOZ_APP_NAME == "thunderbird"
139 : "navigator:browser";
140 let win = Services.wm.getMostRecentWindow(windowType);
142 // If this is an extension handler, check private browsing access.
143 if (!privateAllowed && lazy.PrivateBrowsingUtils.isWindowPrivate(win)) {
144 throw Components.Exception(
145 "Extension not allowed in private windows.",
146 Cr.NS_ERROR_FILE_NOT_FOUND
150 // If we get an exception, there are several possible reasons why:
151 // a) this gecko embedding doesn't provide an nsIBrowserDOMWindow
152 // implementation (i.e. doesn't support browser-style functionality),
153 // so we need to kick the URL out to the OS default browser. This is
154 // the subject of bug 394479.
155 // b) this embedding does provide an nsIBrowserDOMWindow impl, but
156 // there doesn't happen to be a browser window open at the moment; one
157 // should be opened. It's not clear whether this situation will really
158 // ever occur in real life. If it does, the only API that I can find
159 // that seems reasonably likely to work for most embedders is the
160 // command line handler.
161 // c) something else went wrong
163 // It's not clear how one would differentiate between the three cases
164 // above, so for now we don't catch the exception.
167 win.browserDOMWindow.openURI(
169 null, // no window.opener
170 Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW,
171 Ci.nsIBrowserDOMWindow.OPEN_NEW,
172 Services.scriptSecurityManager.getSystemPrincipal()
179 return this._uriTemplate;
182 set uriTemplate(aURITemplate) {
183 this._uriTemplate = aURITemplate;