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