Bug 1429732 - Use a pref to disable registerProtocolHandler in insecure contexts. r?dao,baku
MozReview-Commit-ID: 9NxFv57CyZO
--- a/browser/base/content/test/general/browser_registerProtocolHandler_notification.js
+++ b/browser/base/content/test/general/browser_registerProtocolHandler_notification.js
@@ -1,16 +1,16 @@
/* 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/. */
function test() {
waitForExplicitFinish();
let notificationValue = "Protocol Registration: testprotocol";
- let testURI = "http://example.com/browser/" +
+ let testURI = "https://example.com/browser/" +
"browser/base/content/test/general/browser_registerProtocolHandler_notification.html";
waitForCondition(function() {
// Do not start until the notification is up
let notificationBox = window.gBrowser.getNotificationBox();
let notification = notificationBox.getNotificationWithValue(notificationValue);
return notification;
},
--- a/browser/components/feeds/test/test_registerHandler.html
+++ b/browser/components/feeds/test/test_registerHandler.html
@@ -31,17 +31,20 @@ https://bugzilla.mozilla.org/show_bug.cg
return false;
}
return true;
}
async function tests() {
await SpecialPowers.pushPrefEnv({
- set: [["dom.registerContentHandler.enabled", true]]
+ set: [
+ ["dom.registerContentHandler.enabled", true],
+ ["dom.registerProtocolHandler.insecure.enabled", true],
+ ]
});
ok(navigator.registerContentHandler, "navigator.registerContentHandler should be defined");
// testing a generic case
is(testRegisterHandler(true, "foo", "http://mochi.test:8888/%s", "Foo handler"), true, "registering a foo protocol handler should work");
is(testRegisterHandler(false, "application/rss+xml", "http://mochi.test:8888/%s", "Foo handler"), true, "registering a foo content handler should work");
// testing with wrong uris
--- a/browser/components/feeds/test/test_registerHandler_disabled.html
+++ b/browser/components/feeds/test/test_registerHandler_disabled.html
@@ -15,19 +15,23 @@ https://bugzilla.mozilla.org/show_bug.cg
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
async function tests() {
await SpecialPowers.pushPrefEnv({
- set: [["dom.registerContentHandler.enabled", true]]
+ set: [
+ ["dom.registerContentHandler.enabled", false],
+ ["dom.registerProtocolHandler.insecure.enabled", false],
+ ]
});
- ok(navigator.registerContentHandler, "navigator.registerContentHandler should be defined");
+ ok(!navigator.registerContentHandler, "navigator.registerContentHandler should be undefined");
+ ok(!navigator.registerProtocolHandler, "navigator.registerProtocolHandler should be undefined");
SimpleTest.finish();
}
tests();
</script>
</pre>
</body>
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_protocolhandler.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_protocolhandler.js
@@ -3,17 +3,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* eslint-disable mozilla/no-arbitrary-setTimeout */
// This test makes sure that the web pages can't register protocol handlers
// inside the private browsing mode.
add_task(async function test() {
let notificationValue = "Protocol Registration: testprotocol";
- let testURI = "http://example.com/browser/" +
+ let testURI = "https://example.com/browser/" +
"browser/components/privatebrowsing/test/browser/browser_privatebrowsing_protocolhandler_page.html";
let doTest = async function(aIsPrivateMode, aWindow) {
let tab = aWindow.gBrowser.selectedTab = aWindow.gBrowser.addTab(testURI);
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
let promiseFinished = PromiseUtils.defer();
setTimeout(function() {
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -960,16 +960,20 @@ Navigator::RegisterProtocolHandler(const
const nsAString& aURI,
const nsAString& aTitle,
ErrorResult& aRv)
{
if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell()) {
return;
}
+ if (!mWindow->IsSecureContext() && mWindow->GetDoc()) {
+ mWindow->GetDoc()->WarnOnceAbout(nsIDocument::eRegisterProtocolHandlerInsecure);
+ }
+
nsCOMPtr<nsIWebContentHandlerRegistrar> registrar =
do_GetService(NS_WEBCONTENTHANDLERREGISTRAR_CONTRACTID);
if (!registrar) {
return;
}
aRv = registrar->RegisterProtocolHandler(aProtocol, aURI, aTitle,
mWindow->GetOuterWindow());
--- a/dom/base/nsDeprecatedOperationList.h
+++ b/dom/base/nsDeprecatedOperationList.h
@@ -36,8 +36,9 @@ DEPRECATED_OPERATION(AppCacheInsecure)
DEPRECATED_OPERATION(PrefixedImageSmoothingEnabled)
DEPRECATED_OPERATION(PrefixedFullscreenAPI)
DEPRECATED_OPERATION(LenientSetter)
DEPRECATED_OPERATION(FileLastModifiedDate)
DEPRECATED_OPERATION(ImageBitmapRenderingContext_TransferImageBitmap)
DEPRECATED_OPERATION(URLCreateObjectURL_MediaStream)
DEPRECATED_OPERATION(XMLBaseAttribute)
DEPRECATED_OPERATION(WindowContentUntrusted)
+DEPRECATED_OPERATION(RegisterProtocolHandlerInsecure)
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -2983,16 +2983,23 @@ nsGlobalWindowInner::OfflineCacheAllowed
/* static */ bool
nsGlobalWindowInner::IsRequestIdleCallbackEnabled(JSContext* aCx, JSObject* aObj)
{
// The requestIdleCallback should always be enabled for system code.
return nsContentUtils::RequestIdleCallbackEnabled() ||
nsContentUtils::IsSystemCaller(aCx);
}
+/* static */ bool
+nsGlobalWindowInner::RegisterProtocolHandlerAllowedForContext(JSContext* aCx, JSObject* aObj)
+{
+ return IsSecureContextOrObjectIsFromSecureContext(aCx, aObj) ||
+ Preferences::GetBool("dom.registerProtocolHandler.insecure.enabled");
+}
+
nsIDOMOfflineResourceList*
nsGlobalWindowInner::GetApplicationCache(ErrorResult& aError)
{
if (!mApplicationCache) {
nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(GetDocShell()));
if (!webNav || !mDoc) {
aError.Throw(NS_ERROR_FAILURE);
return nullptr;
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -399,16 +399,18 @@ public:
static bool IsPrivilegedChromeWindow(JSContext* /* unused */, JSObject* aObj);
static bool OfflineCacheAllowedForContext(JSContext* /* unused */, JSObject* aObj);
static bool IsRequestIdleCallbackEnabled(JSContext* aCx, JSObject* /* unused */);
static bool IsWindowPrintEnabled(JSContext* /* unused */, JSObject* /* unused */);
+ static bool RegisterProtocolHandlerAllowedForContext(JSContext* /* unused */, JSObject* aObj);
+
bool DoResolve(JSContext* aCx, JS::Handle<JSObject*> aObj,
JS::Handle<jsid> aId,
JS::MutableHandle<JS::PropertyDescriptor> aDesc);
// The return value is whether DoResolve might end up resolving the given id.
// If in doubt, return true.
static bool MayResolve(jsid aId);
void GetOwnPropertyNames(JSContext* aCx, JS::AutoIdVector& aNames,
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -347,8 +347,10 @@ ScriptSourceMalformed=<script> source URI is malformed: “%S”.
ModuleSourceMalformed=Module source URI is malformed: “%S”.
# LOCALIZATION NOTE: Do not translate "<script>".
ScriptSourceNotAllowed=<script> source URI is not allowed in this document: “%S”.
ModuleSourceNotAllowed=Module source URI is not allowed in this document: “%S”.
# LOCALIZATION NOTE: %1$S is the invalid property value and %2$S is the property name.
InvalidKeyframePropertyValue=Keyframe property value “%1$S” is invalid according to the syntax for “%2$S”.
# LOCALIZATION NOTE: Do not translate "ReadableStream".
ReadableStreamReadingFailed=Failed to read data from the ReadableStream: “%S”.
+# LOCALIZATION NOTE: Do not translate "registerProtocolHandler".
+RegisterProtocolHandlerInsecureWarning=Use of the registerProtocolHandler for insecure connections will be removed in version 62.
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -74,17 +74,17 @@ interface NavigatorLanguage {
[NoInterfaceObject, Exposed=(Window,Worker)]
interface NavigatorOnLine {
readonly attribute boolean onLine;
};
[NoInterfaceObject]
interface NavigatorContentUtils {
// content handler registration
- [Throws]
+ [Throws, Func="nsGlobalWindowInner::RegisterProtocolHandlerAllowedForContext"]
void registerProtocolHandler(DOMString scheme, DOMString url, DOMString title);
[Pref="dom.registerContentHandler.enabled", Throws]
void registerContentHandler(DOMString mimeType, DOMString url, DOMString title);
// NOT IMPLEMENTED
//DOMString isProtocolHandlerRegistered(DOMString scheme, DOMString url);
//DOMString isContentHandlerRegistered(DOMString mimeType, DOMString url);
//void unregisterProtocolHandler(DOMString scheme, DOMString url);
//void unregisterContentHandler(DOMString mimeType, DOMString url);
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -119,16 +119,24 @@ pref("browser.download.forbid_open_with"
// Remove navigator.registerContentHandler
#ifdef EARLY_BETA_OR_EARLIER
pref("dom.registerContentHandler.enabled", false);
#else
pref("dom.registerContentHandler.enabled", true);
#endif
+// Nightly will have insecure registerProtocolHandler disabled by default
+// Beta and Stable will remain enabled until Firefox 62 providing deprecation stats.
+#ifdef NIGHTLY_BUILD
+pref("dom.registerProtocolHandler.insecure.enabled", false);
+#else
+pref("dom.registerProtocolHandler.insecure.enabled", true);
+#endif
+
// Whether or not testing features are enabled.
pref("dom.quotaManager.testing", false);
// Whether or not indexedDB is enabled.
pref("dom.indexedDB.enabled", true);
// Whether or not indexedDB experimental features are enabled.
pref("dom.indexedDB.experimental", false);
// Enable indexedDB logging.
--- a/testing/web-platform/meta/html/dom/interfaces.html.ini
+++ b/testing/web-platform/meta/html/dom/interfaces.html.ini
@@ -1,10 +1,10 @@
[interfaces.html]
- prefs: [dom.forms.inputmode:true, dom.dialog_element.enabled:true, dom.forms.autocomplete.formautofill:true, dom.webcomponents.shadowdom.enabled:true, browser.cache.offline.insecure.enable:true]
+ prefs: [dom.forms.inputmode:true, dom.dialog_element.enabled:true, dom.forms.autocomplete.formautofill:true, dom.webcomponents.shadowdom.enabled:true, browser.cache.offline.insecure.enable:true, dom.registerProtocolHandler.insecure.enabled:true]
[Document interface: attribute domain]
expected: FAIL
[Document interface: attribute cookie]
expected: FAIL
[Document interface: attribute head]
expected: FAIL
--- a/testing/web-platform/meta/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol.html.ini
+++ b/testing/web-platform/meta/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol.html.ini
@@ -1,9 +1,10 @@
[protocol.html]
+ prefs: [dom.registerProtocolHandler.insecure.enabled: true]
[%s instead of domain name should throw SYNTAX_ERR]
expected: FAIL
[%s instead of subdomain name should throw SYNTAX_ERR]
expected: FAIL
[a url argument pointing to a different domain name, without %s should throw SYNTAX_ERR]
expected: FAIL
--- a/uriloader/exthandler/tests/mochitest/browser_web_protocol_handlers.js
+++ b/uriloader/exthandler/tests/mochitest/browser_web_protocol_handlers.js
@@ -1,9 +1,9 @@
-let testURL = "http://example.com/browser/" +
+let testURL = "https://example.com/browser/" +
"uriloader/exthandler/tests/mochitest/protocolHandler.html";
add_task(async function() {
// Load a page registering a protocol handler.
let browser = gBrowser.selectedBrowser;
browser.loadURI(testURL);
await BrowserTestUtils.browserLoaded(browser, false, testURL);
--- a/uriloader/exthandler/tests/mochitest/handlerApps.js
+++ b/uriloader/exthandler/tests/mochitest/handlerApps.js
@@ -9,17 +9,17 @@ const Cc = SpecialPowers.Cc;
function test() {
// set up the web handler object
var webHandler = Cc["@mozilla.org/uriloader/web-handler-app;1"].
createInstance(SpecialPowers.Ci.nsIWebHandlerApp);
webHandler.name = "Test Web Handler App";
webHandler.uriTemplate =
- "http://mochi.test:8888/tests/uriloader/exthandler/tests/mochitest/" +
+ "https://example.com/tests/uriloader/exthandler/tests/mochitest/" +
"handlerApp.xhtml?uri=%s";
// set up the uri to test with
var ioService = Cc["@mozilla.org/network/io-service;1"].
getService(SpecialPowers.Ci.nsIIOService);
var uri = ioService.newURI(testURI);
// create a window, and launch the handler in it
--- a/uriloader/exthandler/tests/mochitest/mochitest.ini
+++ b/uriloader/exthandler/tests/mochitest/mochitest.ini
@@ -2,9 +2,11 @@
support-files =
handlerApp.xhtml
handlerApps.js
unsafeBidi_chromeScript.js
unsafeBidiFileName.sjs
[test_handlerApps.xhtml]
skip-if = (toolkit == 'android' || os == 'mac') || e10s # OS X: bug 786938
+scheme = https
[test_unsafeBidiChars.xhtml]
+[test_web_protocol_handlers.html]
--- a/uriloader/exthandler/tests/mochitest/test_unsafeBidiChars.xhtml
+++ b/uriloader/exthandler/tests/mochitest/test_unsafeBidiChars.xhtml
@@ -33,17 +33,20 @@ var tests = [
function replace(name, x) {
return name.replace(/\{1\}/, x);
}
function sanitize(name) {
return replace(name, '_');
}
+const INSECURE_REGISTER_PREF = "dom.registerProtocolHandler.insecure.enabled";
+
add_task(async function() {
+ SpecialPowers.setBoolPref(INSECURE_REGISTER_PREF, false);
let url = SimpleTest.getTestFileURL("unsafeBidi_chromeScript.js");
let chromeScript = SpecialPowers.loadChromeScript(url);
for (let test of tests) {
for (let char of unsafeBidiChars) {
let promiseName = new Promise(function(resolve) {
chromeScript.addMessageListener("suggestedFileName",
function listener(data) {
@@ -63,15 +66,17 @@ add_task(async function() {
chromeScript.addMessageListener("unregistered", function listener() {
chromeScript.removeMessageListener("unregistered", listener);
resolve();
});
});
chromeScript.sendAsyncMessage("unregister");
await promise;
+ SpecialPowers.clearUserPref(INSECURE_REGISTER_PREF);
+
chromeScript.destroy();
});
]]>
</script>
</body>
</html>
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/tests/mochitest/test_web_protocol_handlers.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test for disabled registerProtocolHandler</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<iframe id="test"></iframe>
+<script type="text/javascript">
+
+add_task(async function() {
+ let result = "registerProtocolHandler" in navigator;
+ ok(!result, "Insecure registerProtocolHandler is undefined");
+});
+</script>
+</body>
+</html>