diff -r 000000000000 -r 6474c204b198 dom/base/URL.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dom/base/URL.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,518 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#include "URL.h" + +#include "nsGlobalWindow.h" +#include "nsIDOMFile.h" +#include "DOMMediaStream.h" +#include "mozilla/dom/MediaSource.h" +#include "mozilla/dom/URLBinding.h" +#include "nsHostObjectProtocolHandler.h" +#include "nsServiceManagerUtils.h" +#include "nsIIOService.h" +#include "nsEscape.h" +#include "nsNetCID.h" +#include "nsIURL.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_CLASS(URL) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(URL) + if (tmp->mSearchParams) { + tmp->mSearchParams->RemoveObserver(tmp); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mSearchParams) + } +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(URL) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSearchParams) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(URL) +NS_IMPL_CYCLE_COLLECTING_RELEASE(URL) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URL) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +URL::URL(nsIURI* aURI) + : mURI(aURI) +{ +} + +JSObject* +URL::WrapObject(JSContext* aCx) +{ + return URLBinding::Wrap(aCx, this); +} + +/* static */ already_AddRefed +URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl, + URL& aBase, ErrorResult& aRv) +{ + nsresult rv; + nsCOMPtr ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv)); + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return nullptr; + } + + nsCOMPtr uri; + rv = ioService->NewURI(NS_ConvertUTF16toUTF8(aUrl), nullptr, aBase.GetURI(), + getter_AddRefs(uri)); + if (NS_FAILED(rv)) { + nsAutoString label(aUrl); + aRv.ThrowTypeError(MSG_INVALID_URL, &label); + return nullptr; + } + + nsRefPtr url = new URL(uri); + return url.forget(); +} + +/* static */ already_AddRefed +URL::Constructor(const GlobalObject& aGlobal, const nsAString& aUrl, + const nsAString& aBase, ErrorResult& aRv) +{ + nsresult rv; + nsCOMPtr ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv)); + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return nullptr; + } + + nsCOMPtr baseUri; + rv = ioService->NewURI(NS_ConvertUTF16toUTF8(aBase), nullptr, nullptr, + getter_AddRefs(baseUri)); + if (NS_FAILED(rv)) { + nsAutoString label(aBase); + aRv.ThrowTypeError(MSG_INVALID_URL, &label); + return nullptr; + } + + nsCOMPtr uri; + rv = ioService->NewURI(NS_ConvertUTF16toUTF8(aUrl), nullptr, baseUri, + getter_AddRefs(uri)); + if (NS_FAILED(rv)) { + nsAutoString label(aUrl); + aRv.ThrowTypeError(MSG_INVALID_URL, &label); + return nullptr; + } + + nsRefPtr url = new URL(uri); + return url.forget(); +} + +void +URL::CreateObjectURL(const GlobalObject& aGlobal, + nsIDOMBlob* aBlob, + const objectURLOptions& aOptions, + nsString& aResult, + ErrorResult& aError) +{ + CreateObjectURLInternal(aGlobal, aBlob, + NS_LITERAL_CSTRING(BLOBURI_SCHEME), aOptions, aResult, + aError); +} + +void +URL::CreateObjectURL(const GlobalObject& aGlobal, DOMMediaStream& aStream, + const mozilla::dom::objectURLOptions& aOptions, + nsString& aResult, + ErrorResult& aError) +{ + CreateObjectURLInternal(aGlobal, &aStream, + NS_LITERAL_CSTRING(MEDIASTREAMURI_SCHEME), aOptions, + aResult, aError); +} + +void +URL::CreateObjectURL(const GlobalObject& aGlobal, MediaSource& aSource, + const objectURLOptions& aOptions, + nsString& aResult, + ErrorResult& aError) +{ + CreateObjectURLInternal(aGlobal, &aSource, + NS_LITERAL_CSTRING(MEDIASOURCEURI_SCHEME), aOptions, + aResult, aError); +} + +void +URL::CreateObjectURLInternal(const GlobalObject& aGlobal, nsISupports* aObject, + const nsACString& aScheme, + const objectURLOptions& aOptions, + nsString& aResult, ErrorResult& aError) +{ + nsCOMPtr principal = nsContentUtils::GetObjectPrincipal(aGlobal.Get()); + + nsCString url; + nsresult rv = nsHostObjectProtocolHandler::AddDataEntry(aScheme, aObject, + principal, url); + if (NS_FAILED(rv)) { + aError.Throw(rv); + return; + } + + nsCOMPtr w = do_QueryInterface(aGlobal.GetAsSupports()); + nsGlobalWindow* window = static_cast(w.get()); + + if (window) { + NS_PRECONDITION(window->IsInnerWindow(), "Should be inner window"); + + if (!window->GetExtantDoc()) { + aError.Throw(NS_ERROR_INVALID_POINTER); + return; + } + + nsIDocument* doc = window->GetExtantDoc(); + if (doc) { + doc->RegisterHostObjectUri(url); + } + } + + CopyASCIItoUTF16(url, aResult); +} + +void +URL::RevokeObjectURL(const GlobalObject& aGlobal, const nsAString& aURL) +{ + nsIPrincipal* principal = nsContentUtils::GetObjectPrincipal(aGlobal.Get()); + + NS_LossyConvertUTF16toASCII asciiurl(aURL); + + nsIPrincipal* urlPrincipal = + nsHostObjectProtocolHandler::GetDataEntryPrincipal(asciiurl); + + if (urlPrincipal && principal->Subsumes(urlPrincipal)) { + nsCOMPtr w = do_QueryInterface(aGlobal.GetAsSupports()); + nsGlobalWindow* window = static_cast(w.get()); + + if (window && window->GetExtantDoc()) { + window->GetExtantDoc()->UnregisterHostObjectUri(asciiurl); + } + nsHostObjectProtocolHandler::RemoveDataEntry(asciiurl); + } +} + +void +URL::GetHref(nsString& aHref) const +{ + aHref.Truncate(); + + nsAutoCString href; + nsresult rv = mURI->GetSpec(href); + if (NS_SUCCEEDED(rv)) { + CopyUTF8toUTF16(href, aHref); + } +} + +void +URL::SetHref(const nsAString& aHref, ErrorResult& aRv) +{ + nsCString href = NS_ConvertUTF16toUTF8(aHref); + + nsresult rv; + nsCOMPtr ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv)); + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return; + } + + nsCOMPtr uri; + rv = ioService->NewURI(href, nullptr, nullptr, getter_AddRefs(uri)); + if (NS_FAILED(rv)) { + nsAutoString label(aHref); + aRv.ThrowTypeError(MSG_INVALID_URL, &label); + return; + } + + mURI = uri; + UpdateURLSearchParams(); +} + +void +URL::GetOrigin(nsString& aOrigin) const +{ + nsContentUtils::GetUTFNonNullOrigin(mURI, aOrigin); +} + +void +URL::GetProtocol(nsString& aProtocol) const +{ + nsCString protocol; + if (NS_SUCCEEDED(mURI->GetScheme(protocol))) { + aProtocol.Truncate(); + } + + CopyASCIItoUTF16(protocol, aProtocol); + aProtocol.Append(char16_t(':')); +} + +void +URL::SetProtocol(const nsAString& aProtocol) +{ + nsAString::const_iterator start, end; + aProtocol.BeginReading(start); + aProtocol.EndReading(end); + nsAString::const_iterator iter(start); + + FindCharInReadable(':', iter, end); + mURI->SetScheme(NS_ConvertUTF16toUTF8(Substring(start, iter))); +} + +#define URL_GETTER( value, func ) \ + value.Truncate(); \ + nsAutoCString tmp; \ + nsresult rv = mURI->func(tmp); \ + if (NS_SUCCEEDED(rv)) { \ + CopyUTF8toUTF16(tmp, value); \ + } + +void +URL::GetUsername(nsString& aUsername) const +{ + URL_GETTER(aUsername, GetUsername); +} + +void +URL::SetUsername(const nsAString& aUsername) +{ + mURI->SetUsername(NS_ConvertUTF16toUTF8(aUsername)); +} + +void +URL::GetPassword(nsString& aPassword) const +{ + URL_GETTER(aPassword, GetPassword); +} + +void +URL::SetPassword(const nsAString& aPassword) +{ + mURI->SetPassword(NS_ConvertUTF16toUTF8(aPassword)); +} + +void +URL::GetHost(nsString& aHost) const +{ + URL_GETTER(aHost, GetHostPort); +} + +void +URL::SetHost(const nsAString& aHost) +{ + mURI->SetHostPort(NS_ConvertUTF16toUTF8(aHost)); +} + +void +URL::URLSearchParamsUpdated() +{ + MOZ_ASSERT(mSearchParams); + + nsAutoString search; + mSearchParams->Serialize(search); + SetSearchInternal(search); +} + +void +URL::UpdateURLSearchParams() +{ + if (!mSearchParams) { + return; + } + + nsAutoCString search; + nsCOMPtr url(do_QueryInterface(mURI)); + if (url) { + nsresult rv = url->GetQuery(search); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to get the query from a nsIURL."); + } + } + + mSearchParams->ParseInput(search, this); +} + +void +URL::GetHostname(nsString& aHostname) const +{ + URL_GETTER(aHostname, GetHost); +} + +void +URL::SetHostname(const nsAString& aHostname) +{ + // nsStandardURL returns NS_ERROR_UNEXPECTED for an empty hostname + // The return code is silently ignored + mURI->SetHost(NS_ConvertUTF16toUTF8(aHostname)); +} + +void +URL::GetPort(nsString& aPort) const +{ + aPort.Truncate(); + + int32_t port; + nsresult rv = mURI->GetPort(&port); + if (NS_SUCCEEDED(rv) && port != -1) { + nsAutoString portStr; + portStr.AppendInt(port, 10); + aPort.Assign(portStr); + } +} + +void +URL::SetPort(const nsAString& aPort) +{ + nsresult rv; + nsAutoString portStr(aPort); + int32_t port = -1; + + // nsIURI uses -1 as default value. + if (!portStr.IsEmpty()) { + port = portStr.ToInteger(&rv); + if (NS_FAILED(rv)) { + return; + } + } + + mURI->SetPort(port); +} + +void +URL::GetPathname(nsString& aPathname) const +{ + aPathname.Truncate(); + + nsCOMPtr url(do_QueryInterface(mURI)); + if (!url) { + // Do not throw! Not having a valid URI or URL should result in an empty + // string. + return; + } + + nsAutoCString file; + nsresult rv = url->GetFilePath(file); + if (NS_SUCCEEDED(rv)) { + CopyUTF8toUTF16(file, aPathname); + } +} + +void +URL::SetPathname(const nsAString& aPathname) +{ + nsCOMPtr url(do_QueryInterface(mURI)); + if (!url) { + // Ignore failures to be compatible with NS4. + return; + } + + url->SetFilePath(NS_ConvertUTF16toUTF8(aPathname)); +} + +void +URL::GetSearch(nsString& aSearch) const +{ + aSearch.Truncate(); + + nsCOMPtr url(do_QueryInterface(mURI)); + if (!url) { + // Do not throw! Not having a valid URI or URL should result in an empty + // string. + return; + } + + nsAutoCString search; + nsresult rv = url->GetQuery(search); + if (NS_SUCCEEDED(rv) && !search.IsEmpty()) { + CopyUTF8toUTF16(NS_LITERAL_CSTRING("?") + search, aSearch); + } +} + +void +URL::SetSearch(const nsAString& aSearch) +{ + SetSearchInternal(aSearch); + UpdateURLSearchParams(); +} + +void +URL::SetSearchInternal(const nsAString& aSearch) +{ + nsCOMPtr url(do_QueryInterface(mURI)); + if (!url) { + // Ignore failures to be compatible with NS4. + return; + } + + url->SetQuery(NS_ConvertUTF16toUTF8(aSearch)); +} + +URLSearchParams* +URL::SearchParams() +{ + CreateSearchParamsIfNeeded(); + return mSearchParams; +} + +void +URL::SetSearchParams(URLSearchParams& aSearchParams) +{ + if (mSearchParams) { + mSearchParams->RemoveObserver(this); + } + + // the observer will be cleared using the cycle collector. + mSearchParams = &aSearchParams; + mSearchParams->AddObserver(this); + + nsAutoString search; + mSearchParams->Serialize(search); + SetSearchInternal(search); +} + +void +URL::GetHash(nsString& aHash) const +{ + aHash.Truncate(); + + nsAutoCString ref; + nsresult rv = mURI->GetRef(ref); + if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) { + NS_UnescapeURL(ref); // XXX may result in random non-ASCII bytes! + aHash.Assign(char16_t('#')); + AppendUTF8toUTF16(ref, aHash); + } +} + +void +URL::SetHash(const nsAString& aHash) +{ + mURI->SetRef(NS_ConvertUTF16toUTF8(aHash)); +} + +bool IsChromeURI(nsIURI* aURI) +{ + bool isChrome = false; + if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome))) + return isChrome; + return false; +} + +void +URL::CreateSearchParamsIfNeeded() +{ + if (!mSearchParams) { + mSearchParams = new URLSearchParams(); + mSearchParams->AddObserver(this); + UpdateURLSearchParams(); + } +} + +} +}