Skip to content

Commit

Permalink
Bug 1342258 - Refactor SetDomain to IsRegistrableDomainSuffixOfOrEqua…
Browse files Browse the repository at this point in the history
…lTo 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] whatwg/html#2365
[3] w3ctag/design-reviews#97 (comment)

MozReview-Commit-ID: 4Dr8yOMdhez

--HG--
extra : rebase_source : 634bd3c7b60c7ad996ee38ab7314071b426e3f6f
  • Loading branch information
jcjones committed Mar 7, 2017
1 parent f8586f5 commit d183667
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 40 deletions.
145 changes: 105 additions & 40 deletions dom/html/nsHTMLDocument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -906,64 +906,64 @@ nsHTMLDocument::GetDomain(nsAString& aDomain)
return NS_OK;
}

NS_IMETHODIMP
nsHTMLDocument::SetDomain(const nsAString& aDomain)
{
ErrorResult rv;
SetDomain(aDomain, rv);
return rv.StealNSResult();
}

void
nsHTMLDocument::SetDomain(const nsAString& aDomain, ErrorResult& rv)
already_AddRefed<nsIURI>
nsHTMLDocument::CreateInheritingURIForHost(const nsACString& aHostString)
{
if (mSandboxFlags & SANDBOXED_DOMAIN) {
// We're sandboxed; disallow setting domain
rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return;
}

if (aDomain.IsEmpty()) {
rv.Throw(NS_ERROR_DOM_BAD_DOCUMENT_DOMAIN);
return;
if (aHostString.IsEmpty()) {
return nullptr;
}

// Create new URI
nsCOMPtr<nsIURI> uri = GetDomainURI();

if (!uri) {
rv.Throw(NS_ERROR_FAILURE);
return;
return nullptr;
}

nsCOMPtr<nsIURI> newURI;
nsresult rv2 = uri->Clone(getter_AddRefs(newURI));
if (NS_FAILED(rv2)) {
rv.Throw(rv2);
return;
nsresult rv = uri->Clone(getter_AddRefs(newURI));
if (NS_FAILED(rv)) {
return nullptr;
}

rv2 = newURI->SetUserPass(EmptyCString());
if (NS_FAILED(rv2)) {
rv.Throw(rv2);
return;
rv = newURI->SetUserPass(EmptyCString());
if (NS_FAILED(rv)) {
return nullptr;
}

// 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;
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, domain;
if (NS_FAILED(uri->GetAsciiHost(current)))
nsAutoCString current;
nsAutoCString domain;
if (NS_FAILED(aOrigHost->GetAsciiHost(current))) {
current.Truncate();
if (NS_FAILED(newURI->GetAsciiHost(domain)))
}
if (NS_FAILED(newURI->GetAsciiHost(domain))) {
domain.Truncate();
}

bool ok = current.Equals(domain);
if (current.Length() > domain.Length() &&
Expand All @@ -974,18 +974,83 @@ nsHTMLDocument::SetDomain(const nsAString& aDomain, ErrorResult& rv)
nsCOMPtr<nsIEffectiveTLDService> tldService =
do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
if (!tldService) {
rv.Throw(NS_ERROR_NOT_AVAILABLE);
return;
return nullptr;
}

nsAutoCString currentBaseDomain;
ok = NS_SUCCEEDED(tldService->GetBaseDomain(uri, 0, 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)
{
ErrorResult rv;
SetDomain(aDomain, rv);
return rv.StealNSResult();
}

void
nsHTMLDocument::SetDomain(const nsAString& aDomain, ErrorResult& rv)
{
if (mSandboxFlags & SANDBOXED_DOMAIN) {
// We're sandboxed; disallow setting domain
rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return;
}

if (aDomain.IsEmpty()) {
rv.Throw(NS_ERROR_DOM_BAD_DOCUMENT_DOMAIN);
return;
}

nsCOMPtr<nsIURI> uri = GetDomainURI();
if (!uri) {
rv.Throw(NS_ERROR_FAILURE);
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.

nsCOMPtr<nsIURI> newURI = RegistrableDomainSuffixOfInternal(aDomain, uri);
if (!newURI) {
// Error: illegal domain
rv.Throw(NS_ERROR_DOM_BAD_DOCUMENT_DOMAIN);
return;
Expand Down
6 changes: 6 additions & 0 deletions dom/html/nsHTMLDocument.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ class nsHTMLDocument : public nsDocument,
virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
override;
void SetDomain(const nsAString& aDomain, mozilla::ErrorResult& rv);
bool IsRegistrableDomainSuffixOfOrEqualTo(const nsAString& aHostSuffixString,
const nsACString& aOrigHost);
void GetCookie(nsAString& aCookie, mozilla::ErrorResult& rv);
void SetCookie(const nsAString& aCookie, mozilla::ErrorResult& rv);
void NamedGetter(JSContext* cx, const nsAString& aName, bool& aFound,
Expand Down Expand Up @@ -274,6 +276,10 @@ class nsHTMLDocument : public nsDocument,
static void DocumentWriteTerminationFunc(nsISupports *aRef);

already_AddRefed<nsIURI> GetDomainURI();
already_AddRefed<nsIURI> CreateInheritingURIForHost(const nsACString& aHostString);
already_AddRefed<nsIURI> RegistrableDomainSuffixOfInternal(const nsAString& aHostSuffixString,
nsIURI* aOrigHost);


nsresult WriteCommon(JSContext *cx, const nsAString& aText,
bool aNewlineTerminate);
Expand Down

0 comments on commit d183667

Please sign in to comment.