Bug 1532253 - Add NS_NewURIOnAnyThread r=baku!

Differential Revision: https://phabricator.services.mozilla.com/D22137

--HG--
extra : source : d33b895e5ebeef0ade779ad35323d698f710a5a5
extra : intermediate-source : 82c14f26f6de19645d4e0f4a5f4d44eaa99536f3
This commit is contained in:
Valentin Gosu 2019-03-05 14:01:58 +01:00
parent fe0717fb48
commit 815b481df4
15 changed files with 243 additions and 15 deletions

View File

@ -13,6 +13,10 @@ XPIDL_SOURCES += [
XPIDL_MODULE = 'chrome'
EXPORTS += [
'nsChromeProtocolHandler.h',
]
EXPORTS.mozilla.chrome += [
'RegistryMessageUtils.h',
]

View File

@ -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;

View File

@ -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() {}

View File

@ -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<nsITextToSubURI> 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

View File

@ -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<nsITextToSubURI> mTextToSubURI;
static nsresult EnsureUTF8Spec(const nsCString& aSpec, const char* aCharset,
nsACString& aUTF8Spec);
};
class nsJSURI final : public mozilla::net::nsSimpleURI {

View File

@ -178,6 +178,8 @@ class ThreadLocal : public Storage<T> {
inline T get() const;
inline void set(const T aValue);
using Type = T;
};
template <typename T, template <typename U> class Storage>

View File

@ -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 <limits>
@ -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<nsIURI> 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 <typename T>
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<typeof(gTlsURLRecursionCount)> 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<nsIURI> 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();

View File

@ -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 =

View File

@ -8,6 +8,10 @@ EXPORTS.mozilla.net += [
'DataChannelParent.h',
]
EXPORTS += [
'nsDataHandler.h',
]
UNIFIED_SOURCES += [
'DataChannelChild.cpp',
'DataChannelParent.cpp',

View File

@ -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<nsIURI> uri;

View File

@ -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);

View File

@ -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<nsIThreadPool> pool = new nsThreadPool();
pool->SetThreadLimit(60);
pool = new nsThreadPool();
for (int i = 0; i < 1000; ++i) {
nsCOMPtr<nsIRunnable> task =
NS_NewRunnableFunction("gtest-NS_NewURIOnAnyThread", []() {
nsCOMPtr<nsIURI> 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<nsIURI> 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);
}

View File

@ -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);
}

View File

@ -107,6 +107,7 @@ UNIFIED_SOURCES += [
'TaskQueue.cpp',
'ThreadEventQueue.cpp',
'ThreadEventTarget.cpp',
'ThreadLocalVariables.cpp',
'ThrottledEventQueue.cpp',
'TimerThread.cpp',
]

View File

@ -572,6 +572,7 @@ void nsThread::InitCommon() {
#endif
}
InitThreadLocalVariables();
AddToThreadList();
}