Bug 1626362 - Move docshell uri fixup code into a static helper. r=nika

Differential Revision: https://phabricator.services.mozilla.com/D80104
This commit is contained in:
Matt Woodrow 2020-06-25 04:32:01 +00:00
Родитель b70d4baf2a
Коммит d545a61edb
2 изменённых файлов: 247 добавлений и 209 удалений

Просмотреть файл

@ -5612,6 +5612,202 @@ already_AddRefed<nsIURIFixupInfo> nsDocShell::KeywordToURI(
return info.forget();
}
/* static */
already_AddRefed<nsIURI> nsDocShell::AttemptURIFixup(
nsIChannel* aChannel, nsresult aStatus,
const mozilla::Maybe<nsCString>& aOriginalURIString, uint32_t aLoadType,
bool aIsTopFrame, bool aAllowKeywordFixup, bool aUsePrivateBrowsing,
bool aNotifyKeywordSearchLoading, nsIInputStream** aNewPostData) {
if (aStatus != NS_ERROR_UNKNOWN_HOST && aStatus != NS_ERROR_NET_RESET &&
aStatus != NS_ERROR_CONNECTION_REFUSED) {
return nullptr;
}
if (!(aLoadType == LOAD_NORMAL && aIsTopFrame) && !aAllowKeywordFixup) {
return nullptr;
}
nsCOMPtr<nsIURI> url;
nsresult rv = aChannel->GetURI(getter_AddRefs(url));
if (NS_FAILED(rv)) {
return nullptr;
}
//
// Try and make an alternative URI from the old one
//
nsCOMPtr<nsIURI> newURI;
nsCOMPtr<nsIInputStream> newPostData;
nsAutoCString oldSpec;
url->GetSpec(oldSpec);
//
// First try keyword fixup
//
nsAutoString keywordProviderName, keywordAsSent;
if (aStatus == NS_ERROR_UNKNOWN_HOST && aAllowKeywordFixup) {
// we should only perform a keyword search under the following
// conditions:
// (0) Pref keyword.enabled is true
// (1) the url scheme is http (or https)
// (2) the url does not have a protocol scheme
// If we don't enforce such a policy, then we end up doing
// keyword searchs on urls we don't intend like imap, file,
// mailbox, etc. This could lead to a security problem where we
// send data to the keyword server that we shouldn't be.
// Someone needs to clean up keywords in general so we can
// determine on a per url basis if we want keywords
// enabled...this is just a bandaid...
nsAutoCString scheme;
Unused << url->GetScheme(scheme);
if (Preferences::GetBool("keyword.enabled", false) &&
StringBeginsWith(scheme, NS_LITERAL_CSTRING("http"))) {
bool attemptFixup = false;
nsAutoCString host;
Unused << url->GetHost(host);
if (host.FindChar('.') == kNotFound) {
attemptFixup = true;
} else {
// For domains with dots, we check the public suffix validity.
nsCOMPtr<nsIEffectiveTLDService> tldService =
do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
if (tldService) {
nsAutoCString suffix;
attemptFixup =
NS_SUCCEEDED(tldService->GetKnownPublicSuffix(url, suffix)) &&
suffix.IsEmpty();
}
}
if (attemptFixup) {
nsCOMPtr<nsIURIFixupInfo> info;
// only send non-qualified hosts to the keyword server
if (aOriginalURIString && !aOriginalURIString->IsEmpty()) {
info = KeywordToURI(*aOriginalURIString, aUsePrivateBrowsing,
getter_AddRefs(newPostData));
} else {
//
// If this string was passed through nsStandardURL by
// chance, then it may have been converted from UTF-8 to
// ACE, which would result in a completely bogus keyword
// query. Here we try to recover the original Unicode
// value, but this is not 100% correct since the value may
// have been normalized per the IDN normalization rules.
//
// Since we don't have access to the exact original string
// that was entered by the user, this will just have to do.
bool isACE;
nsAutoCString utf8Host;
nsCOMPtr<nsIIDNService> idnSrv =
do_GetService(NS_IDNSERVICE_CONTRACTID);
if (idnSrv && NS_SUCCEEDED(idnSrv->IsACE(host, &isACE)) && isACE &&
NS_SUCCEEDED(idnSrv->ConvertACEtoUTF8(host, utf8Host))) {
info = KeywordToURI(utf8Host, aUsePrivateBrowsing,
getter_AddRefs(newPostData));
} else {
info = KeywordToURI(host, aUsePrivateBrowsing,
getter_AddRefs(newPostData));
}
}
if (info) {
info->GetPreferredURI(getter_AddRefs(newURI));
if (newURI) {
info->GetKeywordAsSent(keywordAsSent);
info->GetKeywordProviderName(keywordProviderName);
}
}
}
}
}
//
// Now try change the address, e.g. turn http://foo into
// http://www.foo.com, and if that doesn't work try https with
// https://foo and https://www.foo.com.
//
if (aStatus == NS_ERROR_UNKNOWN_HOST || aStatus == NS_ERROR_NET_RESET) {
bool doCreateAlternate = true;
// Skip fixup for anything except a normal document load
// operation on the topframe.
if (aLoadType != LOAD_NORMAL || !aIsTopFrame) {
doCreateAlternate = false;
} else {
// Test if keyword lookup produced a new URI or not
if (newURI) {
bool sameURI = false;
url->Equals(newURI, &sameURI);
if (!sameURI) {
// Keyword lookup made a new URI so no need to try
// an alternate one.
doCreateAlternate = false;
}
}
if (doCreateAlternate) {
nsCOMPtr<nsILoadInfo> info = aChannel->LoadInfo();
// Skip doing this if our channel was redirected, because we
// shouldn't be guessing things about the post-redirect URI.
if (!info->RedirectChain().IsEmpty()) {
doCreateAlternate = false;
}
}
}
if (doCreateAlternate) {
newURI = nullptr;
newPostData = nullptr;
keywordProviderName.Truncate();
keywordAsSent.Truncate();
nsCOMPtr<nsIURIFixup> uriFixup = components::URIFixup::Service();
if (uriFixup) {
uriFixup->CreateFixupURI(
oldSpec, nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI,
getter_AddRefs(newPostData), getter_AddRefs(newURI));
}
}
} else if (aStatus == NS_ERROR_CONNECTION_REFUSED &&
Preferences::GetBool("browser.fixup.fallback-to-https", false)) {
// Try HTTPS, since http didn't work
if (SchemeIsHTTP(url)) {
int32_t port = 0;
url->GetPort(&port);
// Fall back to HTTPS only if port is default
if (port == -1) {
newURI = nullptr;
newPostData = nullptr;
Unused << NS_MutateURI(url)
.SetScheme(NS_LITERAL_CSTRING("https"))
.Finalize(getter_AddRefs(newURI));
}
}
}
// Did we make a new URI that is different to the old one? If so
// load it.
//
if (newURI) {
// Make sure the new URI is different from the old one,
// otherwise there's little point trying to load it again.
bool sameURI = false;
url->Equals(newURI, &sameURI);
if (!sameURI) {
if (aNewPostData) {
newPostData.forget(aNewPostData);
}
if (aNotifyKeywordSearchLoading) {
// This notification is meant for Firefox Health Report so it
// can increment counts from the search engine
MaybeNotifyKeywordSearchLoading(keywordProviderName, keywordAsSent);
}
return newURI.forget();
}
}
return nullptr;
}
nsresult nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
nsIChannel* aChannel, nsresult aStatus) {
MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
@ -5811,220 +6007,55 @@ nsresult nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
return NS_OK;
}
if ((aStatus == NS_ERROR_UNKNOWN_HOST || aStatus == NS_ERROR_NET_RESET ||
aStatus == NS_ERROR_CONNECTION_REFUSED) &&
((mLoadType == LOAD_NORMAL && isTopFrame) || mAllowKeywordFixup)) {
//
// Try and make an alternative URI from the old one
//
nsCOMPtr<nsIURI> newURI;
nsCOMPtr<nsIInputStream> newPostData;
nsCOMPtr<nsIInputStream> newPostData;
nsCOMPtr<nsIURI> newURI =
AttemptURIFixup(aChannel, aStatus, Some(mOriginalUriString), mLoadType,
isTopFrame, mAllowKeywordFixup, UsePrivateBrowsing(),
true, getter_AddRefs(newPostData));
if (newURI) {
nsAutoCString newSpec;
newURI->GetSpec(newSpec);
NS_ConvertUTF8toUTF16 newSpecW(newSpec);
nsAutoCString oldSpec;
url->GetSpec(oldSpec);
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
MOZ_ASSERT(loadInfo, "loadInfo is required on all channels");
nsCOMPtr<nsIPrincipal> triggeringPrincipal =
loadInfo->TriggeringPrincipal();
//
// First try keyword fixup
//
nsAutoString keywordProviderName, keywordAsSent;
if (aStatus == NS_ERROR_UNKNOWN_HOST && mAllowKeywordFixup) {
// we should only perform a keyword search under the following
// conditions:
// (0) Pref keyword.enabled is true
// (1) the url scheme is http (or https)
// (2) the url does not have a protocol scheme
// If we don't enforce such a policy, then we end up doing
// keyword searchs on urls we don't intend like imap, file,
// mailbox, etc. This could lead to a security problem where we
// send data to the keyword server that we shouldn't be.
// Someone needs to clean up keywords in general so we can
// determine on a per url basis if we want keywords
// enabled...this is just a bandaid...
nsAutoCString scheme;
Unused << url->GetScheme(scheme);
if (Preferences::GetBool("keyword.enabled", false) &&
StringBeginsWith(scheme, NS_LITERAL_CSTRING("http"))) {
bool attemptFixup = false;
nsAutoCString host;
Unused << url->GetHost(host);
if (host.FindChar('.') == kNotFound) {
attemptFixup = true;
} else {
// For domains with dots, we check the public suffix validity.
nsCOMPtr<nsIEffectiveTLDService> tldService =
do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
if (tldService) {
nsAutoCString suffix;
attemptFixup =
NS_SUCCEEDED(tldService->GetKnownPublicSuffix(url, suffix)) &&
suffix.IsEmpty();
}
}
if (attemptFixup) {
nsCOMPtr<nsIURIFixupInfo> info;
// only send non-qualified hosts to the keyword server
if (!mOriginalUriString.IsEmpty()) {
info = KeywordToURI(mOriginalUriString, UsePrivateBrowsing(),
getter_AddRefs(newPostData));
} else {
//
// If this string was passed through nsStandardURL by
// chance, then it may have been converted from UTF-8 to
// ACE, which would result in a completely bogus keyword
// query. Here we try to recover the original Unicode
// value, but this is not 100% correct since the value may
// have been normalized per the IDN normalization rules.
//
// Since we don't have access to the exact original string
// that was entered by the user, this will just have to do.
bool isACE;
nsAutoCString utf8Host;
nsCOMPtr<nsIIDNService> idnSrv =
do_GetService(NS_IDNSERVICE_CONTRACTID);
if (idnSrv && NS_SUCCEEDED(idnSrv->IsACE(host, &isACE)) &&
isACE &&
NS_SUCCEEDED(idnSrv->ConvertACEtoUTF8(host, utf8Host))) {
info = KeywordToURI(utf8Host, UsePrivateBrowsing(),
getter_AddRefs(newPostData));
} else {
info = KeywordToURI(host, UsePrivateBrowsing(),
getter_AddRefs(newPostData));
}
}
if (info) {
info->GetPreferredURI(getter_AddRefs(newURI));
if (newURI) {
info->GetKeywordAsSent(keywordAsSent);
info->GetKeywordProviderName(keywordProviderName);
// If the new URI is HTTP, it may not work and we may want to fall
// back to HTTPS, so kick off a speculative connect to get that
// started. Even if we don't adjust to HTTPS here, there could be a
// redirect to HTTPS coming so this could speed things up.
if (SchemeIsHTTP(url)) {
int32_t port = 0;
rv = url->GetPort(&port);
// only do this if the port is default.
if (NS_SUCCEEDED(rv) && port == -1) {
nsCOMPtr<nsIURI> httpsURI;
rv = NS_MutateURI(url)
.SetScheme(NS_LITERAL_CSTRING("https"))
.Finalize(getter_AddRefs(httpsURI));
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIIOService> ios = do_GetIOService();
if (ios) {
nsCOMPtr<nsISpeculativeConnect> speculativeService =
do_QueryInterface(ios);
if (speculativeService) {
speculativeService->SpeculativeConnect(
httpsURI, triggeringPrincipal, nullptr);
}
}
}
}
}
//
// Now try change the address, e.g. turn http://foo into
// http://www.foo.com, and if that doesn't work try https with
// https://foo and https://www.foo.com.
//
if (aStatus == NS_ERROR_UNKNOWN_HOST || aStatus == NS_ERROR_NET_RESET) {
bool doCreateAlternate = true;
// Skip fixup for anything except a normal document load
// operation on the topframe.
if (mLoadType != LOAD_NORMAL || !isTopFrame) {
doCreateAlternate = false;
} else {
// Test if keyword lookup produced a new URI or not
if (newURI) {
bool sameURI = false;
url->Equals(newURI, &sameURI);
if (!sameURI) {
// Keyword lookup made a new URI so no need to try
// an alternate one.
doCreateAlternate = false;
}
}
if (doCreateAlternate) {
nsCOMPtr<nsILoadInfo> info = aChannel->LoadInfo();
// Skip doing this if our channel was redirected, because we
// shouldn't be guessing things about the post-redirect URI.
if (!info->RedirectChain().IsEmpty()) {
doCreateAlternate = false;
}
}
}
if (doCreateAlternate) {
newURI = nullptr;
newPostData = nullptr;
keywordProviderName.Truncate();
keywordAsSent.Truncate();
nsCOMPtr<nsIURIFixup> uriFixup = components::URIFixup::Service();
if (uriFixup) {
uriFixup->CreateFixupURI(
oldSpec, nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI,
getter_AddRefs(newPostData), getter_AddRefs(newURI));
}
}
} else if (aStatus == NS_ERROR_CONNECTION_REFUSED &&
Preferences::GetBool("browser.fixup.fallback-to-https",
false)) {
// Try HTTPS, since http didn't work
if (SchemeIsHTTP(url)) {
int32_t port = 0;
url->GetPort(&port);
// Fall back to HTTPS only if port is default
if (port == -1) {
newURI = nullptr;
newPostData = nullptr;
Unused << NS_MutateURI(url)
.SetScheme(NS_LITERAL_CSTRING("https"))
.Finalize(getter_AddRefs(newURI));
}
}
}
// Did we make a new URI that is different to the old one? If so
// load it.
//
if (newURI) {
// Make sure the new URI is different from the old one,
// otherwise there's little point trying to load it again.
bool sameURI = false;
url->Equals(newURI, &sameURI);
if (!sameURI) {
nsAutoCString newSpec;
newURI->GetSpec(newSpec);
NS_ConvertUTF8toUTF16 newSpecW(newSpec);
// This notification is meant for Firefox Health Report so it
// can increment counts from the search engine
MaybeNotifyKeywordSearchLoading(keywordProviderName, keywordAsSent);
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
MOZ_ASSERT(loadInfo, "loadInfo is required on all channels");
nsCOMPtr<nsIPrincipal> triggeringPrincipal =
loadInfo->TriggeringPrincipal();
// If the new URI is HTTP, it may not work and we may want to fall
// back to HTTPS, so kick off a speculative connect to get that
// started. Even if we don't adjust to HTTPS here, there could be a
// redirect to HTTPS coming so this could speed things up.
if (SchemeIsHTTP(url)) {
int32_t port = 0;
rv = url->GetPort(&port);
// only do this if the port is default.
if (NS_SUCCEEDED(rv) && port == -1) {
nsCOMPtr<nsIURI> httpsURI;
rv = NS_MutateURI(url)
.SetScheme(NS_LITERAL_CSTRING("https"))
.Finalize(getter_AddRefs(httpsURI));
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIIOService> ios = do_GetIOService();
if (ios) {
nsCOMPtr<nsISpeculativeConnect> speculativeService =
do_QueryInterface(ios);
if (speculativeService) {
speculativeService->SpeculativeConnect(
httpsURI, triggeringPrincipal, nullptr);
}
}
}
}
}
LoadURIOptions loadURIOptions;
loadURIOptions.mTriggeringPrincipal = triggeringPrincipal;
loadURIOptions.mCsp = loadInfo->GetCspToInherit();
loadURIOptions.mPostData = newPostData;
return LoadURI(newSpecW, loadURIOptions);
}
}
LoadURIOptions loadURIOptions;
loadURIOptions.mTriggeringPrincipal = triggeringPrincipal;
loadURIOptions.mCsp = loadInfo->GetCspToInherit();
loadURIOptions.mPostData = newPostData;
return LoadURI(newSpecW, loadURIOptions);
}
// Well, fixup didn't work :-(

Просмотреть файл

@ -435,6 +435,13 @@ class nsDocShell final : public nsDocLoader,
nsLoadFlags aLoadFlags, uint32_t aCacheKey, nsresult& rv,
nsIChannel** aChannel);
static already_AddRefed<nsIURI> AttemptURIFixup(
nsIChannel* aChannel, nsresult aStatus,
const mozilla::Maybe<nsCString>& aOriginalURIString, uint32_t aLoadType,
bool aIsTopFrame, bool aAllowKeywordFixup, bool aUsePrivateBrowsing,
bool aNotifyKeywordSearchLoading = false,
nsIInputStream** aNewPostData = nullptr);
// Notify consumers of a search being loaded through the observer service:
static void MaybeNotifyKeywordSearchLoading(const nsString& aProvider,
const nsString& aKeyword);
@ -742,9 +749,9 @@ class nsDocShell final : public nsDocLoader,
uint32_t aResponseStatus, mozilla::dom::BrowsingContext* aBrowsingContext,
nsIWidget* aWidget, uint32_t aLoadType);
already_AddRefed<nsIURIFixupInfo> KeywordToURI(const nsACString& aKeyword,
bool aIsPrivateContext,
nsIInputStream** aPostData);
static already_AddRefed<nsIURIFixupInfo> KeywordToURI(
const nsACString& aKeyword, bool aIsPrivateContext,
nsIInputStream** aPostData);
// Sets the current document's current state object to the given SHEntry's
// state object. The current state object is eventually given to the page