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]. Update 1: Refactored two new protected methods to avoid code duplication. Update 2: Bugfix: Be sure to use CreateInheritingURIForHost for the provided domain so as to catch internationalized domains. Update 3: Nit-fix and rebase [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:
@@ -906,6 +906,117 @@ 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)
|
||||
{
|
||||
if (NS_WARN_IF(!aOrigHost)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> newURI = CreateInheritingURIForHost(NS_ConvertUTF16toUTF8(aNewDomain));
|
||||
if (!newURI) {
|
||||
// Error: failed to parse input domain
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 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;
|
||||
nsAutoCString domain;
|
||||
if (NS_FAILED(aOrigHost->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) {
|
||||
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 +1039,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;
|
||||
|
||||
Reference in New Issue
Block a user