diff --git a/docshell/base/URIFixup.jsm b/docshell/base/URIFixup.jsm index 001a43bf0964d..8196c60e373db 100644 --- a/docshell/base/URIFixup.jsm +++ b/docshell/base/URIFixup.jsm @@ -55,6 +55,13 @@ XPCOMUtils.defineLazyServiceGetter( "nsIDNSService" ); +XPCOMUtils.defineLazyServiceGetter( + lazy, + "handlerService", + "@mozilla.org/uriloader/handler-service;1", + "nsIHandlerService" +); + XPCOMUtils.defineLazyPreferenceGetter( lazy, "fixupSchemeTypos", @@ -259,7 +266,15 @@ XPCOMUtils.defineLazyGetter(lazy, "knownSuffixes", () => { return suffixes; }); -function URIFixup() {} +function URIFixup() { + // There are cases that nsIExternalProtocolService.externalProtocolHandlerExists() does + // not work well and returns always true due to flatpak. In this case, in order to + // fallback to nsIHandlerService.exits(), we test whether can trust + // nsIExternalProtocolService here. + this._trustExternalProtocolService = !lazy.externalProtocolService.externalProtocolHandlerExists( + `__dummy${Date.now()}__` + ); +} URIFixup.prototype = { get FIXUP_FLAG_NONE() { @@ -329,7 +344,7 @@ URIFixup.prototype = { scheme && (isCommonProtocol || Services.io.getProtocolHandler(scheme) != lazy.defaultProtocolHandler || - lazy.externalProtocolService.externalProtocolHandlerExists(scheme)); + this._isKnownExternalProtocol(scheme)); if ( canHandleProtocol || @@ -631,6 +646,14 @@ URIFixup.prototype = { isDomainKnown, + _isKnownExternalProtocol(scheme) { + return this._trustExternalProtocolService + ? lazy.externalProtocolService.externalProtocolHandlerExists(scheme) + : lazy.handlerService.exists( + lazy.externalProtocolService.getProtocolHandlerInfo(scheme) + ); + }, + classID: Components.ID("{c6cf88b7-452e-47eb-bdc9-86e3561648ef}"), QueryInterface: ChromeUtils.generateQI(["nsIURIFixup"]), }; diff --git a/docshell/test/unit/test_URIFixup_external_protocol_fallback.js b/docshell/test/unit/test_URIFixup_external_protocol_fallback.js new file mode 100644 index 0000000000000..61fa921cc8814 --- /dev/null +++ b/docshell/test/unit/test_URIFixup_external_protocol_fallback.js @@ -0,0 +1,100 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// Test whether fallback mechanism is working if don't trust nsIExternalProtocolService. + +const { MockRegistrar } = ChromeUtils.import( + "resource://testing-common/MockRegistrar.jsm" +); + +add_task(async function setup() { + info( + "Prepare mock nsIExternalProtocolService whose externalProtocolHandlerExists returns always true" + ); + const externalProtocolService = Cc[ + "@mozilla.org/uriloader/external-protocol-service;1" + ].getService(Ci.nsIExternalProtocolService); + const mockId = MockRegistrar.register( + "@mozilla.org/uriloader/external-protocol-service;1", + { + getProtocolHandlerInfo: scheme => + externalProtocolService.getProtocolHandlerInfo(scheme), + externalProtocolHandlerExists: () => true, + QueryInterface: ChromeUtils.generateQI(["nsIExternalProtocolService"]), + } + ); + const mockExternalProtocolService = Cc[ + "@mozilla.org/uriloader/external-protocol-service;1" + ].getService(Ci.nsIExternalProtocolService); + Assert.ok( + mockExternalProtocolService.externalProtocolHandlerExists("__invalid__"), + "Mock service is working" + ); + + info("Register new dummy protocol"); + const dummyProtocolHandlerInfo = externalProtocolService.getProtocolHandlerInfo( + "dummy" + ); + const handlerService = Cc[ + "@mozilla.org/uriloader/handler-service;1" + ].getService(Ci.nsIHandlerService); + handlerService.store(dummyProtocolHandlerInfo); + + info("Prepare test search engine"); + await setupSearchService(); + await addTestEngines(); + await Services.search.setDefault( + Services.search.getEngineByName(kSearchEngineID) + ); + + registerCleanupFunction(() => { + handlerService.remove(dummyProtocolHandlerInfo); + MockRegistrar.unregister(mockId); + }); +}); + +add_task(function basic() { + const testData = [ + { + input: "mailto:test@example.com", + expected: isSupportedInHandlerService("mailto") + ? "mailto:test@example.com" + : "http://mailto:test@example.com/", + }, + { + input: "keyword:search", + expected: "https://www.example.org/?search=keyword%3Asearch", + }, + { + input: "dummy:protocol", + expected: "dummy:protocol", + }, + ]; + + for (const { input, expected } of testData) { + assertFixup(input, expected); + } +}); + +function assertFixup(input, expected) { + const { preferredURI } = Services.uriFixup.getFixupURIInfo( + input, + Services.uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS + ); + Assert.equal(preferredURI.spec, expected); +} + +function isSupportedInHandlerService(scheme) { + const externalProtocolService = Cc[ + "@mozilla.org/uriloader/external-protocol-service;1" + ].getService(Ci.nsIExternalProtocolService); + const handlerService = Cc[ + "@mozilla.org/uriloader/handler-service;1" + ].getService(Ci.nsIHandlerService); + return handlerService.exists( + externalProtocolService.getProtocolHandlerInfo(scheme) + ); +} diff --git a/docshell/test/unit/xpcshell.ini b/docshell/test/unit/xpcshell.ini index 21cf5f321ddea..21e63c2228d01 100644 --- a/docshell/test/unit/xpcshell.ini +++ b/docshell/test/unit/xpcshell.ini @@ -10,6 +10,7 @@ support-files = [test_browsing_context_structured_clone.js] [test_URIFixup.js] [test_URIFixup_check_host.js] +[test_URIFixup_external_protocol_fallback.js] [test_URIFixup_forced.js] # Disabled for 1563343 -- URI fixup should be done at the app level in GV. skip-if = os == 'android' @@ -24,6 +25,6 @@ skip-if = skip-if = true [test_privacy_transition.js] [test_subframe_stop_after_parent_error.js] -skip-if = +skip-if = os == 'android' appname == 'thunderbird' # Needs to run without E10s, can't do that.