Bug 1342258 - Refactor SetDomain to IsRegistrableDomainSuffixOfOrEqualTo r=smaug

This commit refactors the SetDomain method in a Document to call a new function
IsRegistrableDomainSuffixOfOrEqualTo(), defined in HTML [1]. This commit tries
not to rename anything except input variables, so as to remain as clear as
possible. It likely should have various variables renamed, but given the
author's unfamiliarity with this module, review seems a good time to do that.
It's also duplicating comments a little bit; let me know which one(s) you'd like
to keep!

Note: Commentary on the HTML change is available in the PR [2], and the
rationale for this behavior in Web Auentication, where this algorithm will be
used, is also recorded [3].

Updates: Refactored two new protected methods to avoid code duplication.

[1] https://html.spec.whatwg.org/multipage/browsers.html#is-a-registrable-domain-suffix-of-or-is-equal-to
[2] https://github.com/whatwg/html/pull/2365
[3] https://github.com/w3ctag/spec-reviews/issues/97#issuecomment-175766580

MozReview-Commit-ID: 4Dr8yOMdhez
This commit is contained in:
J.C. Jones
2017-03-07 09:37:34 -07:00
parent bc6bd03962
commit c65e9038e3
2 changed files with 106 additions and 48 deletions

View File

@@ -906,6 +906,104 @@ nsHTMLDocument::GetDomain(nsAString& aDomain)
return NS_OK;
}
already_AddRefed<nsIURI>
nsHTMLDocument::CreateInheritingURIForHost(const nsACString& aHostString)
{
if (aHostString.IsEmpty()) {
return nullptr;
}
// Create new URI
nsCOMPtr<nsIURI> uri = GetDomainURI();
if (!uri) {
return nullptr;
}
nsCOMPtr<nsIURI> newURI;
nsresult rv = uri->Clone(getter_AddRefs(newURI));
if (NS_FAILED(rv)) {
return nullptr;
}
rv = newURI->SetUserPass(EmptyCString());
if (NS_FAILED(rv)) {
return nullptr;
}
// We use SetHostAndPort because we want to reset the port number if needed.
rv = newURI->SetHostAndPort(aHostString);
if (NS_FAILED(rv)) {
return nullptr;
}
return newURI.forget();
}
already_AddRefed<nsIURI>
nsHTMLDocument::RegistrableDomainSuffixOfInternal(const nsAString& aNewDomain,
nsIURI* aOrigHost)
{
// Check new domain - must be a superdomain of the current host
// For example, a page from foo.bar.com may set domain to bar.com,
// but not to ar.com, baz.com, or fi.foo.bar.com.
nsAutoCString domain = NS_ConvertUTF16toUTF8(aNewDomain);
nsAutoCString current;
if (NS_FAILED(aOrigHost->GetAsciiHost(current))) {
current.Truncate();
}
bool ok = current.Equals(domain);
if (current.Length() > domain.Length() &&
StringEndsWith(current, domain) &&
current.CharAt(current.Length() - domain.Length() - 1) == '.') {
// We're golden if the new domain is the current page's base domain or a
// subdomain of it.
nsCOMPtr<nsIEffectiveTLDService> tldService =
do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
if (!tldService) {
return nullptr;
}
nsAutoCString currentBaseDomain;
ok = NS_SUCCEEDED(tldService->GetBaseDomain(aOrigHost, 0, currentBaseDomain));
NS_ASSERTION(StringEndsWith(domain, currentBaseDomain) ==
(domain.Length() >= currentBaseDomain.Length()),
"uh-oh! slight optimization wasn't valid somehow!");
ok = ok && domain.Length() >= currentBaseDomain.Length();
}
if (!ok) {
// Error: illegal domain
return nullptr;
}
return CreateInheritingURIForHost(domain);
}
bool
nsHTMLDocument::IsRegistrableDomainSuffixOfOrEqualTo(const nsAString& aHostSuffixString,
const nsACString& aOrigHost)
{
// https://html.spec.whatwg.org/multipage/browsers.html#is-a-registrable-domain-suffix-of-or-is-equal-to
if (aHostSuffixString.IsEmpty()) {
return false;
}
nsCOMPtr<nsIURI> origURI = CreateInheritingURIForHost(aOrigHost);
if (!origURI) {
// Error: failed to parse input domain
return false;
}
nsCOMPtr<nsIURI> newURI = RegistrableDomainSuffixOfInternal(aHostSuffixString, origURI);
if (!newURI) {
// Error: illegal domain
return false;
}
return true;
}
NS_IMETHODIMP
nsHTMLDocument::SetDomain(const nsAString& aDomain)
{
@@ -928,64 +1026,18 @@ nsHTMLDocument::SetDomain(const nsAString& aDomain, ErrorResult& rv)
return;
}
// Create new URI
nsCOMPtr<nsIURI> uri = GetDomainURI();
if (!uri) {
rv.Throw(NS_ERROR_FAILURE);
return;
}
nsCOMPtr<nsIURI> newURI;
nsresult rv2 = uri->Clone(getter_AddRefs(newURI));
if (NS_FAILED(rv2)) {
rv.Throw(rv2);
return;
}
rv2 = newURI->SetUserPass(EmptyCString());
if (NS_FAILED(rv2)) {
rv.Throw(rv2);
return;
}
// We use SetHostAndPort because we want to reset the port number if needed.
rv2 = newURI->SetHostAndPort(NS_ConvertUTF16toUTF8(aDomain));
if (NS_FAILED(rv2)) {
rv.Throw(rv2);
return;
}
// Check new domain - must be a superdomain of the current host
// For example, a page from foo.bar.com may set domain to bar.com,
// but not to ar.com, baz.com, or fi.foo.bar.com.
nsAutoCString current, domain;
if (NS_FAILED(uri->GetAsciiHost(current)))
current.Truncate();
if (NS_FAILED(newURI->GetAsciiHost(domain)))
domain.Truncate();
bool ok = current.Equals(domain);
if (current.Length() > domain.Length() &&
StringEndsWith(current, domain) &&
current.CharAt(current.Length() - domain.Length() - 1) == '.') {
// We're golden if the new domain is the current page's base domain or a
// subdomain of it.
nsCOMPtr<nsIEffectiveTLDService> tldService =
do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
if (!tldService) {
rv.Throw(NS_ERROR_NOT_AVAILABLE);
return;
}
nsAutoCString currentBaseDomain;
ok = NS_SUCCEEDED(tldService->GetBaseDomain(uri, 0, currentBaseDomain));
NS_ASSERTION(StringEndsWith(domain, currentBaseDomain) ==
(domain.Length() >= currentBaseDomain.Length()),
"uh-oh! slight optimization wasn't valid somehow!");
ok = ok && domain.Length() >= currentBaseDomain.Length();
}
if (!ok) {
nsCOMPtr<nsIURI> newURI = RegistrableDomainSuffixOfInternal(aDomain, uri);
if (!newURI) {
// Error: illegal domain
rv.Throw(NS_ERROR_DOM_BAD_DOCUMENT_DOMAIN);
return;