diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 42f030a85591..9344d5c76344 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -237,6 +237,7 @@ #include "mozilla/dom/ServiceWorkerRegistration.h" #include "mozilla/dom/U2F.h" #include "mozilla/dom/WebIDLGlobalNameHash.h" +#include "mozilla/dom/Worklet.h" #ifdef HAVE_SIDEBAR #include "mozilla/dom/ExternalBinding.h" #endif @@ -14848,6 +14849,13 @@ nsGlobalWindow::TemporarilyDisableDialogs::~TemporarilyDisableDialogs() } } +already_AddRefed +nsGlobalWindow::CreateWorklet(ErrorResult& aRv) +{ + RefPtr worklet = new Worklet(this); + return worklet.forget(); +} + template class nsPIDOMWindow; template class nsPIDOMWindow; template class nsPIDOMWindow; diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index 4bcdbbad907b..8bba89cffc91 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -136,6 +136,7 @@ class WakeLock; #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK) class WindowOrientationObserver; #endif +class Worklet; namespace cache { class CacheStorage; } // namespace cache @@ -926,6 +927,10 @@ public: static void ConvertDialogOptions(const nsAString& aOptions, nsAString& aResult); + // Exposed only for testing + already_AddRefed + CreateWorklet(mozilla::ErrorResult& aRv); + protected: bool AlertOrConfirm(bool aAlert, const nsAString& aMessage, nsIPrincipal& aSubjectPrincipal, diff --git a/dom/moz.build b/dom/moz.build index efc7d4c7f2b4..2f873032233a 100644 --- a/dom/moz.build +++ b/dom/moz.build @@ -101,6 +101,7 @@ DIRS += [ 'console', 'performance', 'xhr', + 'worklet', ] if CONFIG['OS_ARCH'] == 'WINNT': diff --git a/dom/webidl/Window.webidl b/dom/webidl/Window.webidl index 8bbbcca6d059..017eb3179d62 100644 --- a/dom/webidl/Window.webidl +++ b/dom/webidl/Window.webidl @@ -486,6 +486,12 @@ partial interface Window { attribute EventHandler onvrdisplaypresentchange; }; +// For testing worklet only +partial interface Window { + [Pref="dom.worklet.testing.enabled", Throws] + Worklet createWorklet(); +}; + Window implements ChromeWindow; Window implements WindowOrWorkerGlobalScope; diff --git a/dom/webidl/Worklet.webidl b/dom/webidl/Worklet.webidl new file mode 100644 index 000000000000..7f003779f7f4 --- /dev/null +++ b/dom/webidl/Worklet.webidl @@ -0,0 +1,14 @@ +/* -*- Mode: IDL; 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/. + * + * The origin of this IDL file is + * https://drafts.css-houdini.org/worklets/#idl-index + */ + +[Pref="dom.worklet.enabled"] +interface Worklet { + [NewObject, Throws] + Promise import(USVString moduleURL); +}; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 1bdac2256d71..636b83c911cf 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -581,6 +581,7 @@ WEBIDL_FILES = [ 'WorkerGlobalScope.webidl', 'WorkerLocation.webidl', 'WorkerNavigator.webidl', + 'Worklet.webidl', 'XMLDocument.webidl', 'XMLHttpRequest.webidl', 'XMLHttpRequestEventTarget.webidl', diff --git a/dom/worklet/Worklet.cpp b/dom/worklet/Worklet.cpp new file mode 100644 index 000000000000..f4a8d0be44d3 --- /dev/null +++ b/dom/worklet/Worklet.cpp @@ -0,0 +1,121 @@ +/* -*- 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 "Worklet.h" +#include "mozilla/dom/WorkletBinding.h" +#include "mozilla/dom/BlobBinding.h" +#include "mozilla/dom/Fetch.h" +#include "mozilla/dom/PromiseNativeHandler.h" +#include "mozilla/dom/Response.h" + +namespace mozilla { +namespace dom { + +namespace { + +class WorkletFetchHandler : public PromiseNativeHandler +{ +public: + NS_DECL_ISUPPORTS + + static already_AddRefed + Fetch(Worklet* aWorklet, const nsAString& aModuleURL, ErrorResult& aRv) + { + RefPtr promise = Promise::Create(aWorklet->GetParentObject(), aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + RequestOrUSVString request; + request.SetAsUSVString().Rebind(aModuleURL.Data(), aModuleURL.Length()); + + RequestInit init; + + RefPtr fetchPromise = + FetchRequest(aWorklet->GetParentObject(), request, init, aRv); + if (NS_WARN_IF(aRv.Failed())) { + promise->MaybeReject(aRv); + return promise.forget(); + } + + RefPtr handler = + new WorkletFetchHandler(aWorklet, promise); + fetchPromise->AppendNativeHandler(handler); + + return promise.forget(); + } + + virtual void + ResolvedCallback(JSContext* aCx, JS::Handle aValue) override + { + if (!aValue.isObject()) { + mPromise->MaybeReject(NS_ERROR_FAILURE); + return; + } + + RefPtr response; + nsresult rv = UNWRAP_OBJECT(Response, &aValue.toObject(), response); + if (NS_WARN_IF(NS_FAILED(rv))) { + mPromise->MaybeReject(rv); + return; + } + + if (!response->Ok()) { + mPromise->MaybeReject(NS_ERROR_DOM_NETWORK_ERR); + return; + } + + // TODO: do something with this response... + + mPromise->MaybeResolveWithUndefined(); + } + + virtual void + RejectedCallback(JSContext* aCx, JS::Handle aValue) override + { + mPromise->MaybeReject(aCx, aValue); + } + +private: + WorkletFetchHandler(Worklet* aWorklet, Promise* aPromise) + : mWorklet(aWorklet) + , mPromise(aPromise) + {} + + ~WorkletFetchHandler() + {} + + RefPtr mWorklet; + RefPtr mPromise; +}; + +NS_IMPL_ISUPPORTS0(WorkletFetchHandler) + +} // anonymous namespace + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Worklet, mGlobal) +NS_IMPL_CYCLE_COLLECTING_ADDREF(Worklet) +NS_IMPL_CYCLE_COLLECTING_RELEASE(Worklet) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Worklet) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +JSObject* +Worklet::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return WorkletBinding::Wrap(aCx, this, aGivenProto); +} + +already_AddRefed +Worklet::Import(const nsAString& aModuleURL, ErrorResult& aRv) +{ + return WorkletFetchHandler::Fetch(this, aModuleURL, aRv); +} + +} // dom namespace +} // mozilla namespace diff --git a/dom/worklet/Worklet.h b/dom/worklet/Worklet.h new file mode 100644 index 000000000000..a73c17222008 --- /dev/null +++ b/dom/worklet/Worklet.h @@ -0,0 +1,54 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_Worklet_h +#define mozilla_dom_Worklet_h + +#include "mozilla/Attributes.h" +#include "mozilla/ErrorResult.h" +#include "nsWrapperCache.h" +#include "nsCOMPtr.h" + +class nsIGlobalObject; + +namespace mozilla { +namespace dom { + +class Promise; + +class Worklet final : public nsISupports + , public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Worklet) + + explicit Worklet(nsIGlobalObject* aGlobal) + : mGlobal(aGlobal) + {} + + nsIGlobalObject* GetParentObject() const + { + return mGlobal; + } + + virtual JSObject* + WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + + already_AddRefed + Import(const nsAString& aModuleURL, ErrorResult& aRv); + +private: + ~Worklet() + {} + + nsCOMPtr mGlobal; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_Worklet_h diff --git a/dom/worklet/moz.build b/dom/worklet/moz.build new file mode 100644 index 000000000000..d874cf414492 --- /dev/null +++ b/dom/worklet/moz.build @@ -0,0 +1,19 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +EXPORTS.mozilla.dom += [ + 'Worklet.h' +] + +UNIFIED_SOURCES += [ + 'Worklet.cpp', +] + +include('/ipc/chromium/chromium-config.mozbuild') + +MOCHITEST_MANIFESTS += ['tests/mochitest.ini'] + +FINAL_LIBRARY = 'xul' diff --git a/dom/worklet/tests/common.js b/dom/worklet/tests/common.js new file mode 100644 index 000000000000..9d70f6aa7e39 --- /dev/null +++ b/dom/worklet/tests/common.js @@ -0,0 +1,14 @@ +function loadTest(file) { + var iframe = document.createElement('iframe'); + iframe.src = file; + + document.body.appendChild(iframe); +} + +function setupTest() { + window.SimpleTest = parent.SimpleTest; + window.is = parent.is; + window.isnot = parent.isnot; + window.ok = parent.ok; + window.info = parent.info; +} diff --git a/dom/worklet/tests/file_basic.html b/dom/worklet/tests/file_basic.html new file mode 100644 index 000000000000..3816c099f280 --- /dev/null +++ b/dom/worklet/tests/file_basic.html @@ -0,0 +1,33 @@ + + + + Test for Worklet + + + + + + + diff --git a/dom/worklet/tests/mochitest.ini b/dom/worklet/tests/mochitest.ini new file mode 100644 index 000000000000..d84f586df3a1 --- /dev/null +++ b/dom/worklet/tests/mochitest.ini @@ -0,0 +1,6 @@ +[DEFAULT] +support-files = + common.js + file_basic.html + +[test_basic.html] diff --git a/dom/worklet/tests/test_basic.html b/dom/worklet/tests/test_basic.html new file mode 100644 index 000000000000..b3010d1a502b --- /dev/null +++ b/dom/worklet/tests/test_basic.html @@ -0,0 +1,21 @@ + + + + Test for Worklet + + + + + + + + +