summaryrefslogtreecommitdiffstats
path: root/dom/url/URL.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/url/URL.cpp')
-rw-r--r--dom/url/URL.cpp1832
1 files changed, 1832 insertions, 0 deletions
diff --git a/dom/url/URL.cpp b/dom/url/URL.cpp
new file mode 100644
index 000000000..1f15e1151
--- /dev/null
+++ b/dom/url/URL.cpp
@@ -0,0 +1,1832 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "DOMMediaStream.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/MediaSource.h"
+#include "mozilla/dom/URLBinding.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/ipc/nsIRemoteBlob.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "nsContentUtils.h"
+#include "nsEscape.h"
+#include "nsHostObjectProtocolHandler.h"
+#include "nsIIOService.h"
+#include "nsIURIWithQuery.h"
+#include "nsIURL.h"
+#include "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "nsServiceManagerUtils.h"
+#include "WorkerPrivate.h"
+#include "WorkerRunnable.h"
+#include "WorkerScope.h"
+
+namespace mozilla {
+namespace dom {
+
+///////////////////////////////////////////////////////////////////////////////
+// URL for main-thread
+///////////////////////////////////////////////////////////////////////////////
+
+namespace {
+
+template<typename T>
+void
+CreateObjectURLInternal(const GlobalObject& aGlobal, T aObject,
+ nsAString& aResult, ErrorResult& aRv)
+{
+ nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
+ if (NS_WARN_IF(!global)) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+
+ nsCOMPtr<nsIPrincipal> principal =
+ nsContentUtils::ObjectPrincipal(aGlobal.Get());
+
+ nsAutoCString url;
+ aRv = nsHostObjectProtocolHandler::AddDataEntry(aObject, principal, url);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ global->RegisterHostObjectURI(url);
+ CopyASCIItoUTF16(url, aResult);
+}
+
+// The URL implementation for the main-thread
+class URLMainThread final : public URL
+{
+public:
+ static already_AddRefed<URLMainThread>
+ Constructor(const GlobalObject& aGlobal, const nsAString& aURL,
+ URL& aBase, ErrorResult& aRv);
+
+ static already_AddRefed<URLMainThread>
+ Constructor(const GlobalObject& aGlobal, const nsAString& aURL,
+ const Optional<nsAString>& aBase, ErrorResult& aRv);
+
+ static already_AddRefed<URLMainThread>
+ Constructor(nsISupports* aParent, const nsAString& aURL,
+ const nsAString& aBase, ErrorResult& aRv);
+
+ static already_AddRefed<URLMainThread>
+ Constructor(nsISupports* aParent, const nsAString& aURL, nsIURI* aBase,
+ ErrorResult& aRv);
+
+ static void
+ CreateObjectURL(const GlobalObject& aGlobal, Blob& aBlob,
+ const objectURLOptions& aOptions, nsAString& aResult,
+ ErrorResult& aRv)
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ CreateObjectURLInternal(aGlobal, aBlob.Impl(), aResult, aRv);
+ }
+
+ static void
+ CreateObjectURL(const GlobalObject& aGlobal, DOMMediaStream& aStream,
+ const objectURLOptions& aOptions, nsAString& aResult,
+ ErrorResult& aRv)
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ CreateObjectURLInternal(aGlobal, &aStream, aResult, aRv);
+ }
+
+ static void
+ CreateObjectURL(const GlobalObject& aGlobal, MediaSource& aSource,
+ const objectURLOptions& aOptions, nsAString& aResult,
+ ErrorResult& aRv);
+
+ static void
+ RevokeObjectURL(const GlobalObject& aGlobal, const nsAString& aURL,
+ ErrorResult& aRv);
+
+ static bool
+ IsValidURL(const GlobalObject& aGlobal, const nsAString& aURL,
+ ErrorResult& aRv);
+
+ URLMainThread(nsISupports* aParent, already_AddRefed<nsIURI> aURI)
+ : URL(aParent)
+ , mURI(aURI)
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ }
+
+ virtual void
+ GetHref(nsAString& aHref, ErrorResult& aRv) const override;
+
+ virtual void
+ SetHref(const nsAString& aHref, ErrorResult& aRv) override;
+
+ virtual void
+ GetOrigin(nsAString& aOrigin, ErrorResult& aRv) const override;
+
+ virtual void
+ GetProtocol(nsAString& aProtocol, ErrorResult& aRv) const override;
+
+ virtual void
+ SetProtocol(const nsAString& aProtocol, ErrorResult& aRv) override;
+
+ virtual void
+ GetUsername(nsAString& aUsername, ErrorResult& aRv) const override;
+
+ virtual void
+ SetUsername(const nsAString& aUsername, ErrorResult& aRv) override;
+
+ virtual void
+ GetPassword(nsAString& aPassword, ErrorResult& aRv) const override;
+
+ virtual void
+ SetPassword(const nsAString& aPassword, ErrorResult& aRv) override;
+
+ virtual void
+ GetHost(nsAString& aHost, ErrorResult& aRv) const override;
+
+ virtual void
+ SetHost(const nsAString& aHost, ErrorResult& aRv) override;
+
+ virtual void
+ GetHostname(nsAString& aHostname, ErrorResult& aRv) const override;
+
+ virtual void
+ SetHostname(const nsAString& aHostname, ErrorResult& aRv) override;
+
+ virtual void
+ GetPort(nsAString& aPort, ErrorResult& aRv) const override;
+
+ virtual void
+ SetPort(const nsAString& aPort, ErrorResult& aRv) override;
+
+ virtual void
+ GetPathname(nsAString& aPathname, ErrorResult& aRv) const override;
+
+ virtual void
+ SetPathname(const nsAString& aPathname, ErrorResult& aRv) override;
+
+ virtual void
+ GetSearch(nsAString& aSearch, ErrorResult& aRv) const override;
+
+ virtual void
+ GetHash(nsAString& aHost, ErrorResult& aRv) const override;
+
+ virtual void
+ SetHash(const nsAString& aHash, ErrorResult& aRv) override;
+
+ virtual void UpdateURLSearchParams() override;
+
+ virtual void
+ SetSearchInternal(const nsAString& aSearch, ErrorResult& aRv) override;
+
+ nsIURI*
+ GetURI() const
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ return mURI;
+ }
+
+private:
+ ~URLMainThread()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ }
+
+ nsCOMPtr<nsIURI> mURI;
+};
+
+/* static */ already_AddRefed<URLMainThread>
+URLMainThread::Constructor(const GlobalObject& aGlobal, const nsAString& aURL,
+ URL& aBase, ErrorResult& aRv)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ URLMainThread& base = static_cast<URLMainThread&>(aBase);
+ return Constructor(aGlobal.GetAsSupports(), aURL, base.GetURI(), aRv);
+}
+
+/* static */ already_AddRefed<URLMainThread>
+URLMainThread::Constructor(const GlobalObject& aGlobal, const nsAString& aURL,
+ const Optional<nsAString>& aBase, ErrorResult& aRv)
+{
+ if (aBase.WasPassed()) {
+ return Constructor(aGlobal.GetAsSupports(), aURL, aBase.Value(), aRv);
+ }
+
+ return Constructor(aGlobal.GetAsSupports(), aURL, nullptr, aRv);
+}
+
+/* static */ already_AddRefed<URLMainThread>
+URLMainThread::Constructor(nsISupports* aParent, const nsAString& aURL,
+ const nsAString& aBase, ErrorResult& aRv)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsCOMPtr<nsIURI> baseUri;
+ nsresult rv = NS_NewURI(getter_AddRefs(baseUri), aBase, nullptr, nullptr,
+ nsContentUtils::GetIOService());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ aRv.ThrowTypeError<MSG_INVALID_URL>(aBase);
+ return nullptr;
+ }
+
+ return Constructor(aParent, aURL, baseUri, aRv);
+}
+
+/* static */ already_AddRefed<URLMainThread>
+URLMainThread::Constructor(nsISupports* aParent, const nsAString& aURL,
+ nsIURI* aBase, ErrorResult& aRv)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, nullptr, aBase,
+ nsContentUtils::GetIOService());
+ if (NS_FAILED(rv)) {
+ // No need to warn in this case. It's common to use the URL constructor
+ // to determine if a URL is valid and an exception will be propagated.
+ aRv.ThrowTypeError<MSG_INVALID_URL>(aURL);
+ return nullptr;
+ }
+
+ RefPtr<URLMainThread> url = new URLMainThread(aParent, uri.forget());
+ return url.forget();
+}
+
+/* static */ void
+URLMainThread::CreateObjectURL(const GlobalObject& aGlobal,
+ MediaSource& aSource,
+ const objectURLOptions& aOptions,
+ nsAString& aResult, ErrorResult& aRv)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsCOMPtr<nsIPrincipal> principal =
+ nsContentUtils::ObjectPrincipal(aGlobal.Get());
+
+ nsAutoCString url;
+ aRv = nsHostObjectProtocolHandler::AddDataEntry(&aSource, principal, url);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ nsCOMPtr<nsIRunnable> revocation = NS_NewRunnableFunction(
+ [url] {
+ nsHostObjectProtocolHandler::RemoveDataEntry(url);
+ });
+
+ nsContentUtils::RunInStableState(revocation.forget());
+
+ CopyASCIItoUTF16(url, aResult);
+}
+
+/* static */ void
+URLMainThread::RevokeObjectURL(const GlobalObject& aGlobal,
+ const nsAString& aURL, ErrorResult& aRv)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
+ if (!global) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+
+ nsIPrincipal* principal = nsContentUtils::ObjectPrincipal(aGlobal.Get());
+
+ NS_LossyConvertUTF16toASCII asciiurl(aURL);
+
+ nsIPrincipal* urlPrincipal =
+ nsHostObjectProtocolHandler::GetDataEntryPrincipal(asciiurl);
+
+ if (urlPrincipal && principal->Subsumes(urlPrincipal)) {
+ global->UnregisterHostObjectURI(asciiurl);
+ nsHostObjectProtocolHandler::RemoveDataEntry(asciiurl);
+ }
+}
+
+/* static */ bool
+URLMainThread::IsValidURL(const GlobalObject& aGlobal, const nsAString& aURL,
+ ErrorResult& aRv)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ NS_LossyConvertUTF16toASCII asciiurl(aURL);
+ return nsHostObjectProtocolHandler::HasDataEntry(asciiurl);
+}
+
+void
+URLMainThread::GetHref(nsAString& aHref, ErrorResult& aRv) const
+{
+ aHref.Truncate();
+
+ nsAutoCString href;
+ nsresult rv = mURI->GetSpec(href);
+ if (NS_SUCCEEDED(rv)) {
+ CopyUTF8toUTF16(href, aHref);
+ }
+}
+
+void
+URLMainThread::SetHref(const nsAString& aHref, ErrorResult& aRv)
+{
+ NS_ConvertUTF16toUTF8 href(aHref);
+
+ nsresult rv;
+ nsCOMPtr<nsIIOService> ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv)) {
+ aRv.Throw(rv);
+ return;
+ }
+
+ nsCOMPtr<nsIURI> uri;
+ rv = ioService->NewURI(href, nullptr, nullptr, getter_AddRefs(uri));
+ if (NS_FAILED(rv)) {
+ aRv.ThrowTypeError<MSG_INVALID_URL>(aHref);
+ return;
+ }
+
+ mURI = uri;
+ UpdateURLSearchParams();
+}
+
+void
+URLMainThread::GetOrigin(nsAString& aOrigin, ErrorResult& aRv) const
+{
+ nsContentUtils::GetUTFOrigin(mURI, aOrigin);
+}
+
+void
+URLMainThread::GetProtocol(nsAString& aProtocol, ErrorResult& aRv) const
+{
+ nsAutoCString protocol;
+ if (NS_SUCCEEDED(mURI->GetScheme(protocol))) {
+ aProtocol.Truncate();
+ }
+
+ CopyASCIItoUTF16(protocol, aProtocol);
+ aProtocol.Append(char16_t(':'));
+}
+
+void
+URLMainThread::SetProtocol(const nsAString& aProtocol, ErrorResult& aRv)
+{
+ nsAString::const_iterator start, end;
+ aProtocol.BeginReading(start);
+ aProtocol.EndReading(end);
+ nsAString::const_iterator iter(start);
+
+ FindCharInReadable(':', iter, end);
+
+ // Changing the protocol of a URL, changes the "nature" of the URI
+ // implementation. In order to do this properly, we have to serialize the
+ // existing URL and reparse it in a new object.
+ nsCOMPtr<nsIURI> clone;
+ nsresult rv = mURI->Clone(getter_AddRefs(clone));
+ if (NS_WARN_IF(NS_FAILED(rv)) || !clone) {
+ return;
+ }
+
+ rv = clone->SetScheme(NS_ConvertUTF16toUTF8(Substring(start, iter)));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ nsAutoCString href;
+ rv = clone->GetSpec(href);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ nsCOMPtr<nsIURI> uri;
+ rv = NS_NewURI(getter_AddRefs(uri), href);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ mURI = uri;
+}
+
+#define URL_GETTER( value, func ) \
+ value.Truncate(); \
+ nsAutoCString tmp; \
+ nsresult rv = mURI->func(tmp); \
+ if (NS_SUCCEEDED(rv)) { \
+ CopyUTF8toUTF16(tmp, value); \
+ }
+
+void
+URLMainThread::GetUsername(nsAString& aUsername, ErrorResult& aRv) const
+{
+ URL_GETTER(aUsername, GetUsername);
+}
+
+void
+URLMainThread::SetUsername(const nsAString& aUsername, ErrorResult& aRv)
+{
+ mURI->SetUsername(NS_ConvertUTF16toUTF8(aUsername));
+}
+
+void
+URLMainThread::GetPassword(nsAString& aPassword, ErrorResult& aRv) const
+{
+ URL_GETTER(aPassword, GetPassword);
+}
+
+void
+URLMainThread::SetPassword(const nsAString& aPassword, ErrorResult& aRv)
+{
+ mURI->SetPassword(NS_ConvertUTF16toUTF8(aPassword));
+}
+
+void
+URLMainThread::GetHost(nsAString& aHost, ErrorResult& aRv) const
+{
+ URL_GETTER(aHost, GetHostPort);
+}
+
+void
+URLMainThread::SetHost(const nsAString& aHost, ErrorResult& aRv)
+{
+ mURI->SetHostPort(NS_ConvertUTF16toUTF8(aHost));
+}
+
+void
+URLMainThread::UpdateURLSearchParams()
+{
+ if (!mSearchParams) {
+ return;
+ }
+
+ nsAutoCString search;
+ nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
+ if (url) {
+ nsresult rv = url->GetQuery(search);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ search.Truncate();
+ }
+ }
+
+ mSearchParams->ParseInput(search);
+}
+
+void
+URLMainThread::GetHostname(nsAString& aHostname, ErrorResult& aRv) const
+{
+ aHostname.Truncate();
+ nsContentUtils::GetHostOrIPv6WithBrackets(mURI, aHostname);
+}
+
+void
+URLMainThread::SetHostname(const nsAString& aHostname, ErrorResult& aRv)
+{
+ // nsStandardURL returns NS_ERROR_UNEXPECTED for an empty hostname
+ // The return code is silently ignored
+ mURI->SetHost(NS_ConvertUTF16toUTF8(aHostname));
+}
+
+void
+URLMainThread::GetPort(nsAString& aPort, ErrorResult& aRv) 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
+URLMainThread::SetPort(const nsAString& aPort, ErrorResult& aRv)
+{
+ 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
+URLMainThread::GetPathname(nsAString& aPathname, ErrorResult& aRv) const
+{
+ aPathname.Truncate();
+
+ // Do not throw! Not having a valid URI or URL should result in an empty
+ // string.
+
+ nsCOMPtr<nsIURIWithQuery> url(do_QueryInterface(mURI));
+ if (url) {
+ nsAutoCString file;
+ nsresult rv = url->GetFilePath(file);
+ if (NS_SUCCEEDED(rv)) {
+ CopyUTF8toUTF16(file, aPathname);
+ }
+
+ return;
+ }
+
+ nsAutoCString path;
+ nsresult rv = mURI->GetPath(path);
+ if (NS_SUCCEEDED(rv)) {
+ CopyUTF8toUTF16(path, aPathname);
+ }
+}
+
+void
+URLMainThread::SetPathname(const nsAString& aPathname, ErrorResult& aRv)
+{
+ // Do not throw!
+
+ nsCOMPtr<nsIURIWithQuery> url(do_QueryInterface(mURI));
+ if (url) {
+ url->SetFilePath(NS_ConvertUTF16toUTF8(aPathname));
+ return;
+ }
+}
+
+void
+URLMainThread::GetSearch(nsAString& aSearch, ErrorResult& aRv) const
+{
+ aSearch.Truncate();
+
+ // Do not throw! Not having a valid URI or URL should result in an empty
+ // string.
+
+ nsAutoCString search;
+ nsresult rv;
+
+ nsCOMPtr<nsIURIWithQuery> url(do_QueryInterface(mURI));
+ if (url) {
+ rv = url->GetQuery(search);
+ if (NS_SUCCEEDED(rv) && !search.IsEmpty()) {
+ CopyUTF8toUTF16(NS_LITERAL_CSTRING("?") + search, aSearch);
+ }
+ return;
+ }
+}
+
+void
+URLMainThread::GetHash(nsAString& aHash, ErrorResult& aRv) const
+{
+ aHash.Truncate();
+
+ nsAutoCString ref;
+ nsresult rv = mURI->GetRef(ref);
+ if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) {
+ aHash.Assign(char16_t('#'));
+ if (nsContentUtils::GettersDecodeURLHash()) {
+ NS_UnescapeURL(ref); // XXX may result in random non-ASCII bytes!
+ }
+ AppendUTF8toUTF16(ref, aHash);
+ }
+}
+
+void
+URLMainThread::SetHash(const nsAString& aHash, ErrorResult& aRv)
+{
+ mURI->SetRef(NS_ConvertUTF16toUTF8(aHash));
+}
+
+void
+URLMainThread::SetSearchInternal(const nsAString& aSearch, ErrorResult& aRv)
+{
+ // Ignore failures to be compatible with NS4.
+
+ nsCOMPtr<nsIURIWithQuery> uriWithQuery(do_QueryInterface(mURI));
+ if (uriWithQuery) {
+ uriWithQuery->SetQuery(NS_ConvertUTF16toUTF8(aSearch));
+ return;
+ }
+}
+
+} // anonymous namespace
+
+///////////////////////////////////////////////////////////////////////////////
+// URL for Workers
+///////////////////////////////////////////////////////////////////////////////
+
+namespace {
+
+using namespace workers;
+
+// Proxy class to forward all the requests to a URLMainThread object.
+class URLProxy final
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(URLProxy)
+
+ explicit URLProxy(already_AddRefed<URLMainThread> aURL)
+ : mURL(aURL)
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ }
+
+ URLMainThread* URL()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ return mURL;
+ }
+
+ nsIURI* URI()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ return mURL->GetURI();
+ }
+
+ void ReleaseURI()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ mURL = nullptr;
+ }
+
+private:
+ // Private destructor, to discourage deletion outside of Release():
+ ~URLProxy()
+ {
+ MOZ_ASSERT(!mURL);
+ }
+
+ RefPtr<URLMainThread> mURL;
+};
+
+// URLWorker implements the URL object in workers.
+class URLWorker final : public URL
+{
+public:
+ static already_AddRefed<URLWorker>
+ Constructor(const GlobalObject& aGlobal, const nsAString& aURL,
+ URL& aBase, ErrorResult& aRv);
+
+ static already_AddRefed<URLWorker>
+ Constructor(const GlobalObject& aGlobal, const nsAString& aURL,
+ const Optional<nsAString>& aBase, ErrorResult& aRv);
+
+ static already_AddRefed<URLWorker>
+ Constructor(const GlobalObject& aGlobal, const nsAString& aURL,
+ const nsAString& aBase, ErrorResult& aRv);
+
+ static void
+ CreateObjectURL(const GlobalObject& aGlobal, Blob& aBlob,
+ const mozilla::dom::objectURLOptions& aOptions,
+ nsAString& aResult, mozilla::ErrorResult& aRv);
+
+ static void
+ RevokeObjectURL(const GlobalObject& aGlobal, const nsAString& aUrl,
+ ErrorResult& aRv);
+
+ static bool
+ IsValidURL(const GlobalObject& aGlobal, const nsAString& aUrl,
+ ErrorResult& aRv);
+
+ URLWorker(WorkerPrivate* aWorkerPrivate, URLProxy* aURLProxy);
+
+ virtual void
+ GetHref(nsAString& aHref, ErrorResult& aRv) const override;
+
+ virtual void
+ SetHref(const nsAString& aHref, ErrorResult& aRv) override;
+
+ virtual void
+ GetOrigin(nsAString& aOrigin, ErrorResult& aRv) const override;
+
+ virtual void
+ GetProtocol(nsAString& aProtocol, ErrorResult& aRv) const override;
+
+ virtual void
+ SetProtocol(const nsAString& aProtocol, ErrorResult& aRv) override;
+
+ virtual void
+ GetUsername(nsAString& aUsername, ErrorResult& aRv) const override;
+
+ virtual void
+ SetUsername(const nsAString& aUsername, ErrorResult& aRv) override;
+
+ virtual void
+ GetPassword(nsAString& aPassword, ErrorResult& aRv) const override;
+
+ virtual void
+ SetPassword(const nsAString& aPassword, ErrorResult& aRv) override;
+
+ virtual void
+ GetHost(nsAString& aHost, ErrorResult& aRv) const override;
+
+ virtual void
+ SetHost(const nsAString& aHost, ErrorResult& aRv) override;
+
+ virtual void
+ GetHostname(nsAString& aHostname, ErrorResult& aRv) const override;
+
+ virtual void
+ SetHostname(const nsAString& aHostname, ErrorResult& aRv) override;
+
+ virtual void
+ GetPort(nsAString& aPort, ErrorResult& aRv) const override;
+
+ virtual void
+ SetPort(const nsAString& aPort, ErrorResult& aRv) override;
+
+ virtual void
+ GetPathname(nsAString& aPathname, ErrorResult& aRv) const override;
+
+ virtual void
+ SetPathname(const nsAString& aPathname, ErrorResult& aRv) override;
+
+ virtual void
+ GetSearch(nsAString& aSearch, ErrorResult& aRv) const override;
+
+ virtual void
+ GetHash(nsAString& aHost, ErrorResult& aRv) const override;
+
+ virtual void
+ SetHash(const nsAString& aHash, ErrorResult& aRv) override;
+
+ virtual void UpdateURLSearchParams() override;
+
+ virtual void
+ SetSearchInternal(const nsAString& aSearch, ErrorResult& aRv) override;
+
+ URLProxy*
+ GetURLProxy() const
+ {
+ mWorkerPrivate->AssertIsOnWorkerThread();
+ return mURLProxy;
+ }
+
+private:
+ ~URLWorker();
+
+ workers::WorkerPrivate* mWorkerPrivate;
+ RefPtr<URLProxy> mURLProxy;
+};
+
+// This class creates an URL from a DOM Blob on the main thread.
+class CreateURLRunnable : public WorkerMainThreadRunnable
+{
+private:
+ BlobImpl* mBlobImpl;
+ nsAString& mURL;
+
+public:
+ CreateURLRunnable(WorkerPrivate* aWorkerPrivate, BlobImpl* aBlobImpl,
+ const objectURLOptions& aOptions,
+ nsAString& aURL)
+ : WorkerMainThreadRunnable(aWorkerPrivate,
+ NS_LITERAL_CSTRING("URL :: CreateURL"))
+ , mBlobImpl(aBlobImpl)
+ , mURL(aURL)
+ {
+ MOZ_ASSERT(aBlobImpl);
+
+ DebugOnly<bool> isMutable;
+ MOZ_ASSERT(NS_SUCCEEDED(aBlobImpl->GetMutable(&isMutable)));
+ MOZ_ASSERT(!isMutable);
+ }
+
+ bool
+ MainThreadRun()
+ {
+ using namespace mozilla::ipc;
+
+ AssertIsOnMainThread();
+
+ RefPtr<BlobImpl> newBlobImplHolder;
+
+ if (nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(mBlobImpl)) {
+ if (BlobChild* blobChild = remoteBlob->GetBlobChild()) {
+ if (PBackgroundChild* blobManager = blobChild->GetBackgroundManager()) {
+ PBackgroundChild* backgroundManager =
+ BackgroundChild::GetForCurrentThread();
+ MOZ_ASSERT(backgroundManager);
+
+ if (blobManager != backgroundManager) {
+ // Always make sure we have a blob from an actor we can use on this
+ // thread.
+ blobChild = BlobChild::GetOrCreate(backgroundManager, mBlobImpl);
+ MOZ_ASSERT(blobChild);
+
+ newBlobImplHolder = blobChild->GetBlobImpl();
+ MOZ_ASSERT(newBlobImplHolder);
+
+ mBlobImpl = newBlobImplHolder;
+ }
+ }
+ }
+ }
+
+ DebugOnly<bool> isMutable;
+ MOZ_ASSERT(NS_SUCCEEDED(mBlobImpl->GetMutable(&isMutable)));
+ MOZ_ASSERT(!isMutable);
+
+ nsCOMPtr<nsIPrincipal> principal = mWorkerPrivate->GetPrincipal();
+
+ nsAutoCString url;
+ nsresult rv =
+ nsHostObjectProtocolHandler::AddDataEntry(mBlobImpl, principal, url);
+
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to add data entry for the blob!");
+ SetDOMStringToNull(mURL);
+ return false;
+ }
+
+ if (!mWorkerPrivate->IsSharedWorker() &&
+ !mWorkerPrivate->IsServiceWorker()) {
+ // Walk up to top worker object.
+ WorkerPrivate* wp = mWorkerPrivate;
+ while (WorkerPrivate* parent = wp->GetParent()) {
+ wp = parent;
+ }
+
+ nsCOMPtr<nsIScriptContext> sc = wp->GetScriptContext();
+ // We could not have a ScriptContext in JSM code. In this case, we leak.
+ if (sc) {
+ nsCOMPtr<nsIGlobalObject> global = sc->GetGlobalObject();
+ MOZ_ASSERT(global);
+
+ global->RegisterHostObjectURI(url);
+ }
+ }
+
+ mURL = NS_ConvertUTF8toUTF16(url);
+ return true;
+ }
+};
+
+// This class revokes an URL on the main thread.
+class RevokeURLRunnable : public WorkerMainThreadRunnable
+{
+private:
+ const nsString mURL;
+
+public:
+ RevokeURLRunnable(WorkerPrivate* aWorkerPrivate,
+ const nsAString& aURL)
+ : WorkerMainThreadRunnable(aWorkerPrivate,
+ NS_LITERAL_CSTRING("URL :: RevokeURL"))
+ , mURL(aURL)
+ {}
+
+ bool
+ MainThreadRun()
+ {
+ AssertIsOnMainThread();
+
+ NS_ConvertUTF16toUTF8 url(mURL);
+
+ nsIPrincipal* urlPrincipal =
+ nsHostObjectProtocolHandler::GetDataEntryPrincipal(url);
+
+ nsCOMPtr<nsIPrincipal> principal = mWorkerPrivate->GetPrincipal();
+
+ bool subsumes;
+ if (urlPrincipal &&
+ NS_SUCCEEDED(principal->Subsumes(urlPrincipal, &subsumes)) &&
+ subsumes) {
+ nsHostObjectProtocolHandler::RemoveDataEntry(url);
+ }
+
+ if (!mWorkerPrivate->IsSharedWorker() &&
+ !mWorkerPrivate->IsServiceWorker()) {
+ // Walk up to top worker object.
+ WorkerPrivate* wp = mWorkerPrivate;
+ while (WorkerPrivate* parent = wp->GetParent()) {
+ wp = parent;
+ }
+
+ nsCOMPtr<nsIScriptContext> sc = wp->GetScriptContext();
+ // We could not have a ScriptContext in JSM code. In this case, we leak.
+ if (sc) {
+ nsCOMPtr<nsIGlobalObject> global = sc->GetGlobalObject();
+ MOZ_ASSERT(global);
+
+ global->UnregisterHostObjectURI(url);
+ }
+ }
+
+ return true;
+ }
+};
+
+// This class checks if an URL is valid on the main thread.
+class IsValidURLRunnable : public WorkerMainThreadRunnable
+{
+private:
+ const nsString mURL;
+ bool mValid;
+
+public:
+ IsValidURLRunnable(WorkerPrivate* aWorkerPrivate,
+ const nsAString& aURL)
+ : WorkerMainThreadRunnable(aWorkerPrivate,
+ NS_LITERAL_CSTRING("URL :: IsValidURL"))
+ , mURL(aURL)
+ , mValid(false)
+ {}
+
+ bool
+ MainThreadRun()
+ {
+ AssertIsOnMainThread();
+
+ NS_ConvertUTF16toUTF8 url(mURL);
+ mValid = nsHostObjectProtocolHandler::HasDataEntry(url);
+
+ return true;
+ }
+
+ bool
+ IsValidURL() const
+ {
+ return mValid;
+ }
+};
+
+// This class creates a URL object on the main thread.
+class ConstructorRunnable : public WorkerMainThreadRunnable
+{
+private:
+ const nsString mURL;
+
+ nsString mBase; // IsVoid() if we have no base URI string.
+ RefPtr<URLProxy> mBaseProxy;
+
+ RefPtr<URLProxy> mRetval;
+
+public:
+ ConstructorRunnable(WorkerPrivate* aWorkerPrivate,
+ const nsAString& aURL, const Optional<nsAString>& aBase)
+ : WorkerMainThreadRunnable(aWorkerPrivate,
+ NS_LITERAL_CSTRING("URL :: Constructor"))
+ , mURL(aURL)
+ {
+ if (aBase.WasPassed()) {
+ mBase = aBase.Value();
+ } else {
+ mBase.SetIsVoid(true);
+ }
+ mWorkerPrivate->AssertIsOnWorkerThread();
+ }
+
+ ConstructorRunnable(WorkerPrivate* aWorkerPrivate,
+ const nsAString& aURL, URLProxy* aBaseProxy)
+ : WorkerMainThreadRunnable(aWorkerPrivate,
+ NS_LITERAL_CSTRING("URL :: Constructor with BaseURL"))
+ , mURL(aURL)
+ , mBaseProxy(aBaseProxy)
+ {
+ mBase.SetIsVoid(true);
+ mWorkerPrivate->AssertIsOnWorkerThread();
+ }
+
+ bool
+ MainThreadRun()
+ {
+ AssertIsOnMainThread();
+
+ ErrorResult rv;
+ RefPtr<URLMainThread> url;
+ if (mBaseProxy) {
+ url = URLMainThread::Constructor(nullptr, mURL, mBaseProxy->URI(), rv);
+ } else if (!mBase.IsVoid()) {
+ url = URLMainThread::Constructor(nullptr, mURL, mBase, rv);
+ } else {
+ url = URLMainThread::Constructor(nullptr, mURL, nullptr, rv);
+ }
+
+ if (rv.Failed()) {
+ rv.SuppressException();
+ return true;
+ }
+
+ mRetval = new URLProxy(url.forget());
+ return true;
+ }
+
+ URLProxy*
+ GetURLProxy(ErrorResult& aRv) const
+ {
+ MOZ_ASSERT(mWorkerPrivate);
+ mWorkerPrivate->AssertIsOnWorkerThread();
+
+ if (!mRetval) {
+ aRv.ThrowTypeError<MSG_INVALID_URL>(mURL);
+ }
+
+ return mRetval;
+ }
+};
+
+class TeardownURLRunnable : public Runnable
+{
+public:
+ explicit TeardownURLRunnable(URLProxy* aURLProxy)
+ : mURLProxy(aURLProxy)
+ {
+ }
+
+ NS_IMETHOD Run()
+ {
+ AssertIsOnMainThread();
+
+ mURLProxy->ReleaseURI();
+ mURLProxy = nullptr;
+
+ return NS_OK;
+ }
+
+private:
+ RefPtr<URLProxy> mURLProxy;
+};
+
+// This class is the generic getter for any URL property.
+class GetterRunnable : public WorkerMainThreadRunnable
+{
+public:
+ enum GetterType {
+ GetterHref,
+ GetterOrigin,
+ GetterProtocol,
+ GetterUsername,
+ GetterPassword,
+ GetterHost,
+ GetterHostname,
+ GetterPort,
+ GetterPathname,
+ GetterSearch,
+ GetterHash,
+ };
+
+ GetterRunnable(WorkerPrivate* aWorkerPrivate,
+ GetterType aType, nsAString& aValue,
+ URLProxy* aURLProxy)
+ : WorkerMainThreadRunnable(aWorkerPrivate,
+ // We can have telemetry keys for each getter when
+ // needed.
+ NS_LITERAL_CSTRING("URL :: getter"))
+ , mValue(aValue)
+ , mType(aType)
+ , mURLProxy(aURLProxy)
+ {
+ mWorkerPrivate->AssertIsOnWorkerThread();
+ }
+
+ bool
+ MainThreadRun()
+ {
+ AssertIsOnMainThread();
+ ErrorResult rv;
+
+ switch (mType) {
+ case GetterHref:
+ mURLProxy->URL()->GetHref(mValue, rv);
+ break;
+
+ case GetterOrigin:
+ mURLProxy->URL()->GetOrigin(mValue, rv);
+ break;
+
+ case GetterProtocol:
+ mURLProxy->URL()->GetProtocol(mValue, rv);
+ break;
+
+ case GetterUsername:
+ mURLProxy->URL()->GetUsername(mValue, rv);
+ break;
+
+ case GetterPassword:
+ mURLProxy->URL()->GetPassword(mValue, rv);
+ break;
+
+ case GetterHost:
+ mURLProxy->URL()->GetHost(mValue, rv);
+ break;
+
+ case GetterHostname:
+ mURLProxy->URL()->GetHostname(mValue, rv);
+ break;
+
+ case GetterPort:
+ mURLProxy->URL()->GetPort(mValue, rv);
+ break;
+
+ case GetterPathname:
+ mURLProxy->URL()->GetPathname(mValue, rv);
+ break;
+
+ case GetterSearch:
+ mURLProxy->URL()->GetSearch(mValue, rv);
+ break;
+
+ case GetterHash:
+ mURLProxy->URL()->GetHash(mValue, rv);
+ break;
+ }
+
+ MOZ_ASSERT(!rv.Failed(), "Main-thread getters do not fail.");
+ return true;
+ }
+
+private:
+ nsAString& mValue;
+ GetterType mType;
+ RefPtr<URLProxy> mURLProxy;
+};
+
+// This class is the generic setter for any URL property.
+class SetterRunnable : public WorkerMainThreadRunnable
+{
+public:
+ enum SetterType {
+ SetterHref,
+ SetterProtocol,
+ SetterUsername,
+ SetterPassword,
+ SetterHost,
+ SetterHostname,
+ SetterPort,
+ SetterPathname,
+ SetterSearch,
+ SetterHash,
+ };
+
+ SetterRunnable(WorkerPrivate* aWorkerPrivate,
+ SetterType aType, const nsAString& aValue,
+ URLProxy* aURLProxy)
+ : WorkerMainThreadRunnable(aWorkerPrivate,
+ // We can have telemetry keys for each setter when
+ // needed.
+ NS_LITERAL_CSTRING("URL :: setter"))
+ , mValue(aValue)
+ , mType(aType)
+ , mURLProxy(aURLProxy)
+ , mFailed(false)
+ {
+ mWorkerPrivate->AssertIsOnWorkerThread();
+ }
+
+ bool
+ MainThreadRun()
+ {
+ AssertIsOnMainThread();
+ ErrorResult rv;
+
+ switch (mType) {
+ case SetterHref: {
+ mURLProxy->URL()->SetHref(mValue, rv);
+ break;
+ }
+
+ case SetterProtocol:
+ mURLProxy->URL()->SetProtocol(mValue, rv);
+ break;
+
+ case SetterUsername:
+ mURLProxy->URL()->SetUsername(mValue, rv);
+ break;
+
+ case SetterPassword:
+ mURLProxy->URL()->SetPassword(mValue, rv);
+ break;
+
+ case SetterHost:
+ mURLProxy->URL()->SetHost(mValue, rv);
+ break;
+
+ case SetterHostname:
+ mURLProxy->URL()->SetHostname(mValue, rv);
+ break;
+
+ case SetterPort:
+ mURLProxy->URL()->SetPort(mValue, rv);
+ break;
+
+ case SetterPathname:
+ mURLProxy->URL()->SetPathname(mValue, rv);
+ break;
+
+ case SetterSearch:
+ mURLProxy->URL()->SetSearch(mValue, rv);
+ break;
+
+ case SetterHash:
+ mURLProxy->URL()->SetHash(mValue, rv);
+ break;
+ }
+
+ if (NS_WARN_IF(rv.Failed())) {
+ rv.SuppressException();
+ mFailed = true;
+ }
+
+ return true;
+ }
+
+ bool Failed() const
+ {
+ return mFailed;
+ }
+
+private:
+ const nsString mValue;
+ SetterType mType;
+ RefPtr<URLProxy> mURLProxy;
+ bool mFailed;
+};
+
+already_AddRefed<URLWorker>
+FinishConstructor(JSContext* aCx, WorkerPrivate* aPrivate,
+ ConstructorRunnable* aRunnable, ErrorResult& aRv)
+{
+ aRunnable->Dispatch(aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ RefPtr<URLProxy> proxy = aRunnable->GetURLProxy(aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ RefPtr<URLWorker> url = new URLWorker(aPrivate, proxy);
+ return url.forget();
+}
+
+/* static */ already_AddRefed<URLWorker>
+URLWorker::Constructor(const GlobalObject& aGlobal, const nsAString& aURL,
+ URL& aBase, ErrorResult& aRv)
+{
+ MOZ_ASSERT(!NS_IsMainThread());
+
+ JSContext* cx = aGlobal.Context();
+ WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
+
+ URLWorker& base = static_cast<URLWorker&>(aBase);
+ RefPtr<ConstructorRunnable> runnable =
+ new ConstructorRunnable(workerPrivate, aURL, base.GetURLProxy());
+
+ return FinishConstructor(cx, workerPrivate, runnable, aRv);
+}
+
+/* static */ already_AddRefed<URLWorker>
+URLWorker::Constructor(const GlobalObject& aGlobal, const nsAString& aURL,
+ const Optional<nsAString>& aBase, ErrorResult& aRv)
+{
+ JSContext* cx = aGlobal.Context();
+ WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
+
+ RefPtr<ConstructorRunnable> runnable =
+ new ConstructorRunnable(workerPrivate, aURL, aBase);
+
+ return FinishConstructor(cx, workerPrivate, runnable, aRv);
+}
+
+/* static */ already_AddRefed<URLWorker>
+URLWorker::Constructor(const GlobalObject& aGlobal, const nsAString& aURL,
+ const nsAString& aBase, ErrorResult& aRv)
+{
+ JSContext* cx = aGlobal.Context();
+ WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
+
+ Optional<nsAString> base;
+ base = &aBase;
+
+ RefPtr<ConstructorRunnable> runnable =
+ new ConstructorRunnable(workerPrivate, aURL, base);
+
+ return FinishConstructor(cx, workerPrivate, runnable, aRv);
+}
+
+/* static */ void
+URLWorker::CreateObjectURL(const GlobalObject& aGlobal, Blob& aBlob,
+ const mozilla::dom::objectURLOptions& aOptions,
+ nsAString& aResult, mozilla::ErrorResult& aRv)
+{
+ JSContext* cx = aGlobal.Context();
+ WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
+
+ RefPtr<BlobImpl> blobImpl = aBlob.Impl();
+ MOZ_ASSERT(blobImpl);
+
+ aRv = blobImpl->SetMutable(false);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ RefPtr<CreateURLRunnable> runnable =
+ new CreateURLRunnable(workerPrivate, blobImpl, aOptions, aResult);
+
+ runnable->Dispatch(aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ if (workerPrivate->IsSharedWorker() || workerPrivate->IsServiceWorker()) {
+ WorkerGlobalScope* scope = workerPrivate->GlobalScope();
+ MOZ_ASSERT(scope);
+
+ scope->RegisterHostObjectURI(NS_ConvertUTF16toUTF8(aResult));
+ }
+}
+
+/* static */ void
+URLWorker::RevokeObjectURL(const GlobalObject& aGlobal, const nsAString& aUrl,
+ ErrorResult& aRv)
+{
+ JSContext* cx = aGlobal.Context();
+ WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
+
+ RefPtr<RevokeURLRunnable> runnable =
+ new RevokeURLRunnable(workerPrivate, aUrl);
+
+ runnable->Dispatch(aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ if (workerPrivate->IsSharedWorker() || workerPrivate->IsServiceWorker()) {
+ WorkerGlobalScope* scope = workerPrivate->GlobalScope();
+ MOZ_ASSERT(scope);
+
+ scope->UnregisterHostObjectURI(NS_ConvertUTF16toUTF8(aUrl));
+ }
+}
+
+/* static */ bool
+URLWorker::IsValidURL(const GlobalObject& aGlobal, const nsAString& aUrl,
+ ErrorResult& aRv)
+{
+ JSContext* cx = aGlobal.Context();
+ WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
+
+ RefPtr<IsValidURLRunnable> runnable =
+ new IsValidURLRunnable(workerPrivate, aUrl);
+
+ runnable->Dispatch(aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return false;
+ }
+
+ return runnable->IsValidURL();
+}
+
+URLWorker::URLWorker(WorkerPrivate* aWorkerPrivate, URLProxy* aURLProxy)
+ : URL(nullptr)
+ , mWorkerPrivate(aWorkerPrivate)
+ , mURLProxy(aURLProxy)
+{}
+
+URLWorker::~URLWorker()
+{
+ if (mURLProxy) {
+ mWorkerPrivate->AssertIsOnWorkerThread();
+
+ RefPtr<TeardownURLRunnable> runnable =
+ new TeardownURLRunnable(mURLProxy);
+ mURLProxy = nullptr;
+
+ if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
+ NS_ERROR("Failed to dispatch teardown runnable!");
+ }
+ }
+}
+
+void
+URLWorker::GetHref(nsAString& aHref, ErrorResult& aRv) const
+{
+ RefPtr<GetterRunnable> runnable =
+ new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterHref, aHref,
+ mURLProxy);
+
+ runnable->Dispatch(aRv);
+}
+
+void
+URLWorker::SetHref(const nsAString& aHref, ErrorResult& aRv)
+{
+ RefPtr<SetterRunnable> runnable =
+ new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterHref, aHref,
+ mURLProxy);
+
+ runnable->Dispatch(aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ if (runnable->Failed()) {
+ aRv.ThrowTypeError<MSG_INVALID_URL>(aHref);
+ return;
+ }
+
+ UpdateURLSearchParams();
+}
+
+void
+URLWorker::GetOrigin(nsAString& aOrigin, ErrorResult& aRv) const
+{
+ RefPtr<GetterRunnable> runnable =
+ new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterOrigin, aOrigin,
+ mURLProxy);
+
+ runnable->Dispatch(aRv);
+}
+
+void
+URLWorker::GetProtocol(nsAString& aProtocol, ErrorResult& aRv) const
+{
+ RefPtr<GetterRunnable> runnable =
+ new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterProtocol, aProtocol,
+ mURLProxy);
+
+ runnable->Dispatch(aRv);
+}
+
+void
+URLWorker::SetProtocol(const nsAString& aProtocol, ErrorResult& aRv)
+{
+ RefPtr<SetterRunnable> runnable =
+ new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterProtocol,
+ aProtocol, mURLProxy);
+
+ runnable->Dispatch(aRv);
+
+ MOZ_ASSERT(!runnable->Failed());
+}
+
+void
+URLWorker::GetUsername(nsAString& aUsername, ErrorResult& aRv) const
+{
+ RefPtr<GetterRunnable> runnable =
+ new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterUsername, aUsername,
+ mURLProxy);
+
+ runnable->Dispatch(aRv);
+}
+
+void
+URLWorker::SetUsername(const nsAString& aUsername, ErrorResult& aRv)
+{
+ RefPtr<SetterRunnable> runnable =
+ new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterUsername,
+ aUsername, mURLProxy);
+
+ runnable->Dispatch(aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ MOZ_ASSERT(!runnable->Failed());
+}
+
+void
+URLWorker::GetPassword(nsAString& aPassword, ErrorResult& aRv) const
+{
+ RefPtr<GetterRunnable> runnable =
+ new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterPassword, aPassword,
+ mURLProxy);
+
+ runnable->Dispatch(aRv);
+}
+
+void
+URLWorker::SetPassword(const nsAString& aPassword, ErrorResult& aRv)
+{
+ RefPtr<SetterRunnable> runnable =
+ new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterPassword,
+ aPassword, mURLProxy);
+
+ runnable->Dispatch(aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ MOZ_ASSERT(!runnable->Failed());
+}
+
+void
+URLWorker::GetHost(nsAString& aHost, ErrorResult& aRv) const
+{
+ RefPtr<GetterRunnable> runnable =
+ new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterHost, aHost,
+ mURLProxy);
+
+ runnable->Dispatch(aRv);
+}
+
+void
+URLWorker::SetHost(const nsAString& aHost, ErrorResult& aRv)
+{
+ RefPtr<SetterRunnable> runnable =
+ new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterHost,
+ aHost, mURLProxy);
+
+ runnable->Dispatch(aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ MOZ_ASSERT(!runnable->Failed());
+}
+
+void
+URLWorker::GetHostname(nsAString& aHostname, ErrorResult& aRv) const
+{
+ RefPtr<GetterRunnable> runnable =
+ new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterHostname, aHostname,
+ mURLProxy);
+
+ runnable->Dispatch(aRv);
+}
+
+void
+URLWorker::SetHostname(const nsAString& aHostname, ErrorResult& aRv)
+{
+ RefPtr<SetterRunnable> runnable =
+ new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterHostname,
+ aHostname, mURLProxy);
+
+ runnable->Dispatch(aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ MOZ_ASSERT(!runnable->Failed());
+}
+
+void
+URLWorker::GetPort(nsAString& aPort, ErrorResult& aRv) const
+{
+ RefPtr<GetterRunnable> runnable =
+ new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterPort, aPort,
+ mURLProxy);
+
+ runnable->Dispatch(aRv);
+}
+
+void
+URLWorker::SetPort(const nsAString& aPort, ErrorResult& aRv)
+{
+ RefPtr<SetterRunnable> runnable =
+ new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterPort,
+ aPort, mURLProxy);
+
+ runnable->Dispatch(aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ MOZ_ASSERT(!runnable->Failed());
+}
+
+void
+URLWorker::GetPathname(nsAString& aPathname, ErrorResult& aRv) const
+{
+ RefPtr<GetterRunnable> runnable =
+ new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterPathname,
+ aPathname, mURLProxy);
+
+ runnable->Dispatch(aRv);
+}
+
+void
+URLWorker::SetPathname(const nsAString& aPathname, ErrorResult& aRv)
+{
+ RefPtr<SetterRunnable> runnable =
+ new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterPathname,
+ aPathname, mURLProxy);
+
+ runnable->Dispatch(aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ MOZ_ASSERT(!runnable->Failed());
+}
+
+void
+URLWorker::GetSearch(nsAString& aSearch, ErrorResult& aRv) const
+{
+ RefPtr<GetterRunnable> runnable =
+ new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterSearch, aSearch,
+ mURLProxy);
+
+ runnable->Dispatch(aRv);
+}
+
+void
+URLWorker::GetHash(nsAString& aHash, ErrorResult& aRv) const
+{
+ RefPtr<GetterRunnable> runnable =
+ new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterHash, aHash,
+ mURLProxy);
+
+ runnable->Dispatch(aRv);
+}
+
+void
+URLWorker::SetHash(const nsAString& aHash, ErrorResult& aRv)
+{
+ RefPtr<SetterRunnable> runnable =
+ new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterHash,
+ aHash, mURLProxy);
+
+ runnable->Dispatch(aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ MOZ_ASSERT(!runnable->Failed());
+}
+
+void
+URLWorker::SetSearchInternal(const nsAString& aSearch, ErrorResult& aRv)
+{
+ RefPtr<SetterRunnable> runnable =
+ new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterSearch,
+ aSearch, mURLProxy);
+
+ runnable->Dispatch(aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ MOZ_ASSERT(!runnable->Failed());
+}
+
+void
+URLWorker::UpdateURLSearchParams()
+{
+ if (mSearchParams) {
+ nsAutoString search;
+
+ ErrorResult rv;
+ GetSearch(search, rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ rv.SuppressException();
+ }
+
+ mSearchParams->ParseInput(NS_ConvertUTF16toUTF8(Substring(search, 1)));
+ }
+}
+
+} // anonymous namespace
+
+///////////////////////////////////////////////////////////////////////////////
+// Base class for URL
+///////////////////////////////////////////////////////////////////////////////
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(URL, mParent, mSearchParams)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(URL)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(URL)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URL)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+JSObject*
+URL::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return URLBinding::Wrap(aCx, this, aGivenProto);
+}
+
+/* static */ already_AddRefed<URL>
+URL::Constructor(const GlobalObject& aGlobal, const nsAString& aURL,
+ URL& aBase, ErrorResult& aRv)
+{
+ if (NS_IsMainThread()) {
+ return URLMainThread::Constructor(aGlobal, aURL, aBase, aRv);
+ }
+
+ return URLWorker::Constructor(aGlobal, aURL, aBase, aRv);
+}
+
+/* static */ already_AddRefed<URL>
+URL::Constructor(const GlobalObject& aGlobal, const nsAString& aURL,
+ const Optional<nsAString>& aBase, ErrorResult& aRv)
+{
+ if (NS_IsMainThread()) {
+ return URLMainThread::Constructor(aGlobal, aURL, aBase, aRv);
+ }
+
+ return URLWorker::Constructor(aGlobal, aURL, aBase, aRv);
+}
+
+/* static */ already_AddRefed<URL>
+URL::WorkerConstructor(const GlobalObject& aGlobal, const nsAString& aURL,
+ const nsAString& aBase, ErrorResult& aRv)
+{
+ return URLWorker::Constructor(aGlobal, aURL, aBase, aRv);
+}
+
+void
+URL::CreateObjectURL(const GlobalObject& aGlobal, Blob& aBlob,
+ const objectURLOptions& aOptions, nsAString& aResult,
+ ErrorResult& aRv)
+{
+ if (NS_IsMainThread()) {
+ URLMainThread::CreateObjectURL(aGlobal, aBlob, aOptions, aResult, aRv);
+ } else {
+ URLWorker::CreateObjectURL(aGlobal, aBlob, aOptions, aResult, aRv);
+ }
+}
+
+void
+URL::CreateObjectURL(const GlobalObject& aGlobal, DOMMediaStream& aStream,
+ const objectURLOptions& aOptions, nsAString& aResult,
+ ErrorResult& aRv)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ URLMainThread::CreateObjectURL(aGlobal, aStream, aOptions, aResult, aRv);
+}
+
+void
+URL::CreateObjectURL(const GlobalObject& aGlobal, MediaSource& aSource,
+ const objectURLOptions& aOptions,
+ nsAString& aResult,
+ ErrorResult& aRv)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ URLMainThread::CreateObjectURL(aGlobal, aSource, aOptions, aResult, aRv);
+}
+
+void
+URL::RevokeObjectURL(const GlobalObject& aGlobal, const nsAString& aURL,
+ ErrorResult& aRv)
+{
+ if (NS_IsMainThread()) {
+ URLMainThread::RevokeObjectURL(aGlobal, aURL, aRv);
+ } else {
+ URLWorker::RevokeObjectURL(aGlobal, aURL, aRv);
+ }
+}
+
+bool
+URL::IsValidURL(const GlobalObject& aGlobal, const nsAString& aURL,
+ ErrorResult& aRv)
+{
+ if (NS_IsMainThread()) {
+ return URLMainThread::IsValidURL(aGlobal, aURL, aRv);
+ }
+ return URLWorker::IsValidURL(aGlobal, aURL, aRv);
+}
+
+URLSearchParams*
+URL::SearchParams()
+{
+ CreateSearchParamsIfNeeded();
+ return mSearchParams;
+}
+
+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(mParent, this);
+ UpdateURLSearchParams();
+ }
+}
+
+void
+URL::SetSearch(const nsAString& aSearch, ErrorResult& aRv)
+{
+ SetSearchInternal(aSearch, aRv);
+ UpdateURLSearchParams();
+}
+
+void
+URL::URLSearchParamsUpdated(URLSearchParams* aSearchParams)
+{
+ MOZ_ASSERT(mSearchParams);
+ MOZ_ASSERT(mSearchParams == aSearchParams);
+
+ nsAutoString search;
+ mSearchParams->Serialize(search);
+
+ ErrorResult rv;
+ SetSearchInternal(search, rv);
+ NS_WARNING_ASSERTION(!rv.Failed(), "SetSearchInternal failed");
+ rv.SuppressException();
+}
+
+} // namespace dom
+} // namespace mozilla