diff --git a/chrome/moz.build b/chrome/moz.build index 1d647eb2517d..936755193b42 100644 --- a/chrome/moz.build +++ b/chrome/moz.build @@ -13,6 +13,10 @@ XPIDL_SOURCES += [ XPIDL_MODULE = 'chrome' +EXPORTS += [ + 'nsChromeProtocolHandler.h', +] + EXPORTS.mozilla.chrome += [ 'RegistryMessageUtils.h', ] diff --git a/chrome/nsChromeProtocolHandler.cpp b/chrome/nsChromeProtocolHandler.cpp index a1d3b353f185..ebd6f7dba50a 100644 --- a/chrome/nsChromeProtocolHandler.cpp +++ b/chrome/nsChromeProtocolHandler.cpp @@ -66,6 +66,13 @@ nsChromeProtocolHandler::GetProtocolFlags(uint32_t *result) { NS_IMETHODIMP nsChromeProtocolHandler::NewURI(const nsACString &aSpec, const char *aCharset, nsIURI *aBaseURI, nsIURI **result) { + return nsChromeProtocolHandler::CreateNewURI(aSpec, aCharset, aBaseURI, + result); +} + +/* static */ nsresult nsChromeProtocolHandler::CreateNewURI( + const nsACString &aSpec, const char *aCharset, nsIURI *aBaseURI, + nsIURI **result) { // Chrome: URLs (currently) have no additional structure beyond that provided // by standard URLs, so there is no "outer" given to CreateInstance nsresult rv; diff --git a/chrome/nsChromeProtocolHandler.h b/chrome/nsChromeProtocolHandler.h index 115925614fae..d43a2e00e1c9 100644 --- a/chrome/nsChromeProtocolHandler.h +++ b/chrome/nsChromeProtocolHandler.h @@ -27,6 +27,8 @@ class nsChromeProtocolHandler final : public nsIProtocolHandler, // nsChromeProtocolHandler methods: nsChromeProtocolHandler() {} + static nsresult CreateNewURI(const nsACString &aSpec, const char *aCharset, + nsIURI *aBaseURI, nsIURI **result); private: ~nsChromeProtocolHandler() {} diff --git a/dom/jsurl/nsJSProtocolHandler.cpp b/dom/jsurl/nsJSProtocolHandler.cpp index 0d6668581370..3bbfa9f8422c 100644 --- a/dom/jsurl/nsJSProtocolHandler.cpp +++ b/dom/jsurl/nsJSProtocolHandler.cpp @@ -42,6 +42,7 @@ #include "nsILoadInfo.h" #include "nsIObjectInputStream.h" #include "nsIObjectOutputStream.h" +#include "nsITextToSubURI.h" #include "nsIWritablePropertyBag2.h" #include "nsIContentSecurityPolicy.h" #include "nsSandboxFlags.h" @@ -1044,20 +1045,19 @@ nsresult nsJSProtocolHandler::Create(nsISupports* aOuter, REFNSIID aIID, return rv; } -nsresult nsJSProtocolHandler::EnsureUTF8Spec(const nsCString& aSpec, - const char* aCharset, - nsACString& aUTF8Spec) { +/* static */ nsresult nsJSProtocolHandler::EnsureUTF8Spec( + const nsCString& aSpec, const char* aCharset, nsACString& aUTF8Spec) { aUTF8Spec.Truncate(); nsresult rv; - if (!mTextToSubURI) { - mTextToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - } + nsCOMPtr txtToSubURI = + do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + nsAutoString uStr; - rv = mTextToSubURI->UnEscapeNonAsciiURI(nsDependentCString(aCharset), aSpec, - uStr); + rv = txtToSubURI->UnEscapeNonAsciiURI(nsDependentCString(aCharset), aSpec, + uStr); NS_ENSURE_SUCCESS(rv, rv); if (!IsASCII(uStr)) { @@ -1092,10 +1092,16 @@ nsJSProtocolHandler::GetProtocolFlags(uint32_t* result) { URI_OPENING_EXECUTES_SCRIPT; return NS_OK; } - NS_IMETHODIMP nsJSProtocolHandler::NewURI(const nsACString& aSpec, const char* aCharset, nsIURI* aBaseURI, nsIURI** result) { + return nsJSProtocolHandler::CreateNewURI(aSpec, aCharset, aBaseURI, result); +} + +/* static */ nsresult nsJSProtocolHandler::CreateNewURI(const nsACString& aSpec, + const char* aCharset, + nsIURI* aBaseURI, + nsIURI** result) { nsresult rv = NS_OK; // javascript: URLs (currently) have no additional structure beyond that diff --git a/dom/jsurl/nsJSProtocolHandler.h b/dom/jsurl/nsJSProtocolHandler.h index 9015a62d21f7..9f4fa69c50d8 100644 --- a/dom/jsurl/nsJSProtocolHandler.h +++ b/dom/jsurl/nsJSProtocolHandler.h @@ -8,7 +8,6 @@ #include "mozilla/Attributes.h" #include "nsIProtocolHandler.h" -#include "nsITextToSubURI.h" #include "nsIURI.h" #include "nsIMutable.h" #include "nsISerializable.h" @@ -54,13 +53,14 @@ class nsJSProtocolHandler : public nsIProtocolHandler { nsresult Init(); + static nsresult CreateNewURI(const nsACString& aSpec, const char* aCharset, + nsIURI* aBaseURI, nsIURI** result); + protected: virtual ~nsJSProtocolHandler(); - nsresult EnsureUTF8Spec(const nsCString& aSpec, const char* aCharset, - nsACString& aUTF8Spec); - - nsCOMPtr mTextToSubURI; + static nsresult EnsureUTF8Spec(const nsCString& aSpec, const char* aCharset, + nsACString& aUTF8Spec); }; class nsJSURI final : public mozilla::net::nsSimpleURI { diff --git a/mfbt/ThreadLocal.h b/mfbt/ThreadLocal.h index a95d24995ea1..3b789f95ac02 100644 --- a/mfbt/ThreadLocal.h +++ b/mfbt/ThreadLocal.h @@ -178,6 +178,8 @@ class ThreadLocal : public Storage { inline T get() const; inline void set(const T aValue); + + using Type = T; }; template class Storage> diff --git a/netwerk/base/nsNetUtil.cpp b/netwerk/base/nsNetUtil.cpp index a2f234101c2e..de668e5aca11 100644 --- a/netwerk/base/nsNetUtil.cpp +++ b/netwerk/base/nsNetUtil.cpp @@ -85,6 +85,10 @@ #include "nsQueryObject.h" #include "mozIThirdPartyUtil.h" #include "../mime/nsMIMEHeaderParamImpl.h" +#include "nsStandardURL.h" +#include "nsChromeProtocolHandler.h" +#include "nsJSProtocolHandler.h" +#include "nsDataHandler.h" #include @@ -1674,6 +1678,128 @@ nsresult NS_NewURI( ioService); } +static nsresult NewStandardURI(const nsACString &aSpec, const char *aCharset, + nsIURI *aBaseURI, int32_t aDefaultPort, + nsIURI **aURI) { + nsCOMPtr base(aBaseURI); + return NS_MutateURI(new nsStandardURL::Mutator()) + .Apply(NS_MutatorMethod(&nsIStandardURLMutator::Init, + nsIStandardURL::URLTYPE_AUTHORITY, aDefaultPort, + nsCString(aSpec), aCharset, base, nullptr)) + .Finalize(aURI); +} + +extern MOZ_THREAD_LOCAL(uint32_t) gTlsURLRecursionCount; + +template +class TlsAutoIncrement { + public: + explicit TlsAutoIncrement(T &var) : mVar(var) { + mValue = mVar.get(); + mVar.set(mValue + 1); + } + ~TlsAutoIncrement() { + typename T::Type value = mVar.get(); + MOZ_ASSERT(value == mValue + 1); + mVar.set(value - 1); + } + + typename T::Type value() { return mValue; } + + private: + typename T::Type mValue; + T &mVar; +}; + +nsresult NS_NewURIOnAnyThread(nsIURI **aURI, const nsACString &aSpec, + const char *aCharset /* = nullptr */, + nsIURI *aBaseURI /* = nullptr */, + nsIIOService *aIOService /* = nullptr */) { + TlsAutoIncrement inc(gTlsURLRecursionCount); + if (inc.value() >= MAX_RECURSION_COUNT) { + return NS_ERROR_MALFORMED_URI; + } + + nsAutoCString scheme; + nsresult rv = net_ExtractURLScheme(aSpec, scheme); + if (NS_FAILED(rv)) { + // then aSpec is relative + if (!aBaseURI) { + return NS_ERROR_MALFORMED_URI; + } + + if (!aSpec.IsEmpty() && aSpec[0] == '#') { + // Looks like a reference instead of a fully-specified URI. + // --> initialize |uri| as a clone of |aBaseURI|, with ref appended. + return NS_GetURIWithNewRef(aBaseURI, aSpec, aURI); + } + + rv = aBaseURI->GetScheme(scheme); + if (NS_FAILED(rv)) return rv; + } + + if (scheme.EqualsLiteral("http") || scheme.EqualsLiteral("ws")) { + return NewStandardURI(aSpec, aCharset, aBaseURI, NS_HTTP_DEFAULT_PORT, + aURI); + } + if (scheme.EqualsLiteral("https") || scheme.EqualsLiteral("wss")) { + return NewStandardURI(aSpec, aCharset, aBaseURI, NS_HTTPS_DEFAULT_PORT, + aURI); + } + if (scheme.EqualsLiteral("ftp")) { + return NewStandardURI(aSpec, aCharset, aBaseURI, 21, aURI); + } + + if (scheme.EqualsLiteral("file")) { + nsAutoCString buf(aSpec); +#if defined(XP_WIN) + buf.Truncate(); + if (!net_NormalizeFileURL(aSpec, buf)) { + buf = aSpec; + } +#endif + + nsCOMPtr base(aBaseURI); + return NS_MutateURI(new nsStandardURL::Mutator()) + .Apply(NS_MutatorMethod(&nsIFileURLMutator::MarkFileURL)) + .Apply(NS_MutatorMethod(&nsIStandardURLMutator::Init, + nsIStandardURL::URLTYPE_NO_AUTHORITY, -1, buf, + aCharset, base, nullptr)) + .Finalize(aURI); + } + + if (scheme.EqualsLiteral("data")) { + return nsDataHandler::CreateNewURI(aSpec, aCharset, aBaseURI, aURI); + } + + if (scheme.EqualsLiteral("moz-safe-about") || + scheme.EqualsLiteral("page-icon") || scheme.EqualsLiteral("moz") || + scheme.EqualsLiteral("moz-anno") || + scheme.EqualsLiteral("moz-page-thumb") || + scheme.EqualsLiteral("moz-fonttable")) { + return NS_MutateURI(new nsSimpleURI::Mutator()) + .SetSpec(aSpec) + .Finalize(aURI); + } + + if (scheme.EqualsLiteral("chrome")) { + return nsChromeProtocolHandler::CreateNewURI(aSpec, aCharset, aBaseURI, + aURI); + } + + if (scheme.EqualsLiteral("javascript")) { + return nsJSProtocolHandler::CreateNewURI(aSpec, aCharset, aBaseURI, aURI); + } + + if (NS_IsMainThread()) { + // XXX (valentin): this fallback should be removed once we get rid of + // nsIProtocolHandler.newURI + return NS_NewURI(aURI, aSpec, aCharset, aBaseURI, aIOService); + } + + return NS_ERROR_UNKNOWN_PROTOCOL; +} + nsresult NS_GetSanitizedURIStringFromURI(nsIURI *aUri, nsAString &aSanitizedSpec) { aSanitizedSpec.Truncate(); diff --git a/netwerk/base/nsNetUtil.h b/netwerk/base/nsNetUtil.h index 5d51d9f17147..8f2267a2f76e 100644 --- a/netwerk/base/nsNetUtil.h +++ b/netwerk/base/nsNetUtil.h @@ -100,6 +100,19 @@ nsresult NS_NewURI(nsIURI **result, const char *spec, nsIURI *baseURI = nullptr, nsIIOService *ioService = nullptr); // pass in nsIIOService to optimize callers +// This function attempts to create an nsIURI on any thread. This implies we +// can't instantiate a protcol handler, since protocol handers may have a JS +// implementation so they can't work off-main-thread. +// When called off the main thread, if the nsIURI can't be created without +// instantiating protocol handlers, the method will return +// NS_ERROR_UNKNOWN_PROTOCOL. The caller may retry on the main thread. +// When called on the main thread, this function will fall back on calling +// nsIProtocolHandler.newURI +nsresult NS_NewURIOnAnyThread(nsIURI **aResult, const nsACString &aSpec, + const char *aCharset = nullptr, + nsIURI *aBaseURI = nullptr, + nsIIOService *aIOService = nullptr); + nsresult NS_NewFileURI( nsIURI **result, nsIFile *spec, nsIIOService *ioService = diff --git a/netwerk/protocol/data/moz.build b/netwerk/protocol/data/moz.build index 0958118fa9e1..4ec585dc905f 100644 --- a/netwerk/protocol/data/moz.build +++ b/netwerk/protocol/data/moz.build @@ -8,6 +8,10 @@ EXPORTS.mozilla.net += [ 'DataChannelParent.h', ] +EXPORTS += [ + 'nsDataHandler.h', +] + UNIFIED_SOURCES += [ 'DataChannelChild.cpp', 'DataChannelParent.cpp', diff --git a/netwerk/protocol/data/nsDataHandler.cpp b/netwerk/protocol/data/nsDataHandler.cpp index 84e05c6c38b4..3adcbe9a10fb 100644 --- a/netwerk/protocol/data/nsDataHandler.cpp +++ b/netwerk/protocol/data/nsDataHandler.cpp @@ -51,6 +51,13 @@ NS_IMETHODIMP nsDataHandler::NewURI(const nsACString& aSpec, const char* aCharset, // ignore charset info nsIURI* aBaseURI, nsIURI** result) { + return nsDataHandler::CreateNewURI(aSpec, aCharset, aBaseURI, result); +} + +/* static */ nsresult nsDataHandler::CreateNewURI(const nsACString& aSpec, + const char* aCharset, + nsIURI* aBaseURI, + nsIURI** result) { nsresult rv; nsCOMPtr uri; diff --git a/netwerk/protocol/data/nsDataHandler.h b/netwerk/protocol/data/nsDataHandler.h index 2821f64e75f3..67ef5c7b6405 100644 --- a/netwerk/protocol/data/nsDataHandler.h +++ b/netwerk/protocol/data/nsDataHandler.h @@ -22,6 +22,9 @@ class nsDataHandler : public nsIProtocolHandler, // nsDataHandler methods: nsDataHandler() = default; + static nsresult CreateNewURI(const nsACString& aSpec, const char* aCharset, + nsIURI* aBaseURI, nsIURI** result); + // Define a Create method to be used with a factory: static MOZ_MUST_USE nsresult Create(nsISupports* aOuter, const nsIID& aIID, void** aResult); diff --git a/netwerk/test/gtest/TestURIMutator.cpp b/netwerk/test/gtest/TestURIMutator.cpp index 91d342690355..0aba70205caa 100644 --- a/netwerk/test/gtest/TestURIMutator.cpp +++ b/netwerk/test/gtest/TestURIMutator.cpp @@ -3,6 +3,8 @@ #include "nsNetCID.h" #include "nsIURL.h" #include "nsIURIMutator.h" +#include "nsThreadPool.h" +#include "nsNetUtil.h" TEST(TestURIMutator, Mutator) { nsAutoCString out; @@ -97,3 +99,39 @@ TEST(TestURIMutator, Mutator) { ASSERT_EQ(rv, NS_ERROR_NOT_AVAILABLE); ASSERT_TRUE(uri3 == nullptr); } + +extern MOZ_THREAD_LOCAL(uint32_t) gTlsURLRecursionCount; + +TEST(TestURIMutator, NS_NewURIOnAnyThread) { + nsCOMPtr pool = new nsThreadPool(); + pool->SetThreadLimit(60); + + pool = new nsThreadPool(); + for (int i = 0; i < 1000; ++i) { + nsCOMPtr task = + NS_NewRunnableFunction("gtest-NS_NewURIOnAnyThread", []() { + nsCOMPtr uri; + nsresult rv = NS_NewURIOnAnyThread( + getter_AddRefs(uri), NS_LITERAL_CSTRING("http://example.com")); + ASSERT_EQ(rv, NS_OK); + nsAutoCString out; + ASSERT_EQ(uri->GetSpec(out), NS_OK); + ASSERT_TRUE(out == NS_LITERAL_CSTRING("http://example.com/")); + }); + EXPECT_TRUE(task); + + pool->Dispatch(task, NS_DISPATCH_NORMAL); + } + + nsCOMPtr uri; + nsresult rv = NS_NewURIOnAnyThread(getter_AddRefs(uri), + NS_LITERAL_CSTRING("http://example.com")); + ASSERT_EQ(rv, NS_OK); + nsAutoCString out; + ASSERT_EQ(uri->GetSpec(out), NS_OK); + ASSERT_TRUE(out == NS_LITERAL_CSTRING("http://example.com/")); + + pool->Shutdown(); + + ASSERT_EQ(gTlsURLRecursionCount.get(), 0u); +} diff --git a/xpcom/threads/ThreadLocalVariables.cpp b/xpcom/threads/ThreadLocalVariables.cpp new file mode 100644 index 000000000000..02056fbe1cd1 --- /dev/null +++ b/xpcom/threads/ThreadLocalVariables.cpp @@ -0,0 +1,14 @@ +/* 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/. */ + +// This variable is used to ensure creating new URI doesn't put us in an +// infinite loop +MOZ_THREAD_LOCAL(uint32_t) gTlsURLRecursionCount; + +void InitThreadLocalVariables() { + if (!gTlsURLRecursionCount.init()) { + MOZ_CRASH("Could not init gTlsURLRecursionCount"); + } + gTlsURLRecursionCount.set(0); +} diff --git a/xpcom/threads/moz.build b/xpcom/threads/moz.build index d5f3f1bfe259..7220d0710dca 100644 --- a/xpcom/threads/moz.build +++ b/xpcom/threads/moz.build @@ -107,6 +107,7 @@ UNIFIED_SOURCES += [ 'TaskQueue.cpp', 'ThreadEventQueue.cpp', 'ThreadEventTarget.cpp', + 'ThreadLocalVariables.cpp', 'ThrottledEventQueue.cpp', 'TimerThread.cpp', ] diff --git a/xpcom/threads/nsThread.cpp b/xpcom/threads/nsThread.cpp index e3cce65fa7b0..faf164800bec 100644 --- a/xpcom/threads/nsThread.cpp +++ b/xpcom/threads/nsThread.cpp @@ -572,6 +572,7 @@ void nsThread::InitCommon() { #endif } + InitThreadLocalVariables(); AddToThreadList(); }