diff --git a/src/main/scala/org/scalajs/dom/experimental/Fetch.scala b/src/main/scala/org/scalajs/dom/experimental/Fetch.scala new file mode 100644 index 000000000..416b5a96c --- /dev/null +++ b/src/main/scala/org/scalajs/dom/experimental/Fetch.scala @@ -0,0 +1,498 @@ +package org.scalajs.dom.experimental + +import org.scalajs.dom.Blob +import org.scalajs.dom.raw.{FormData, Promise} + +import scala.scalajs.js +import scala.scalajs.js.annotation.{JSName, ScalaJSDefined} +import scala.scalajs.js.dom.experimental.JSIterable +import scala.scalajs.js.typedarray.{ArrayBuffer, Uint8Array} + +@js.native +object Fetch extends js.GlobalScope { + + /** + * [[https://fetch.spec.whatwg.org/#fetch-method ¶6.0 Fetch method]] defined in WhatWG spec + * + * @param info + * @param init + * @return + */ + def fetch(info: RequestInfo, init: RequestInit = null): Promise[Response] = js.native +} + +/** + * The Request interface of the Fetch API represents a resource request. MDN + * + * see [[https://fetch.spec.whatwg.org/#request-class ¶6.3 Request Class]] + * in whatwg spec + * + * @param input the url of the requested resource or an unused Request object. + * @param init initialisation information + */ +@js.native +class Request(input: RequestInfo, init: RequestInit = null) extends js.Object { + + /** + * Contains the request's method (GET, POST, etc.) + */ + def method: HttpMethod = js.native + + @JSName("type") + def `mediaType`: RequestType = js.native + + /** + * Contains the URL of the request. + */ + def url: String = js.native //should be USVString + + /** + * Contains the associated Headers object of the request. + */ + def headers: Headers = js.native + + def destination: RequestDestination = js.native + + def referrer: String = js.native + + def referrerPolicy: ReferrerPolicy = js.native + + def mode: RequestMode = js.native + + def credentials: RequestCredentials = js.native + + def cache: RequestCache = js.native + + def redirect: RequestRedirect = js.native + + def integrity: String = js.native //should be DOMString +} + +object RequestInit { + + /** + * Create a RequestInit with some minimal typesafety on attributes. + * + * This method creates as light weight a RequestInit literal as possible + * from the provided parameters, so as to allow the browser to work through + * its default setting strategy. + * + * It is actually quite difficult to work out the defaults, which parameters + * work together and which don't. Check the rules here in case of doubt: + * - [[https://fetch.spec.whatwg.org/#requests ¶3.1.5 Requests]] + * - [[https://fetch.spec.whatwg.org/#request-class ¶6.3 Request class]], especially the constructor function + * of the whatwg Fetch spec. + * + * //todo: it would help a lot if there were a way to make this fully type safe + */ + @inline + def apply(method: js.UndefOr[HttpMethod] = js.undefined, + headers: js.UndefOr[HeadersInit] = js.undefined, + body: js.UndefOr[BodyInit] = js.undefined, + referrer: js.UndefOr[String] = js.undefined, //should be USVString + referrerPolicy: js.UndefOr[ReferrerPolicy] = js.undefined, + mode: js.UndefOr[RequestMode] = js.undefined, + credentials: js.UndefOr[RequestCredentials] = js.undefined, + requestCache: js.UndefOr[RequestCache] = js.undefined, + requestRedirect: js.UndefOr[RequestRedirect] = js.undefined, + integrity: js.UndefOr[String] = js.undefined, //see [[https://w3c + // .github.io/webappsec-subresource-integrity/ integrity spec]] + window: js.UndefOr[Null] = js.undefined + ): RequestInit = { + val result = js.Dynamic.literal() + + @inline + def set[T](attribute: String, value: js.UndefOr[T]) = value.foreach { x => + result.updateDynamic(attribute)(x.asInstanceOf[js.Any]) + } + + set("method", method) + set("headers", headers) + set("body", body) + set("referrer", referrer) + set("referrerPolicy", referrerPolicy) + set("mode", mode) + set("credentials", credentials) + set("requestCache", requestCache) + set("requestRedirect", requestRedirect) + set("integrity", integrity) + set("window", window) + + result.asInstanceOf[RequestInit] + } +} + +/** + * See [[https://fetch.spec.whatwg.org/#requestinit RequestInit]] in Fetch API + * The underlying object is a dictionary. This trait is here to help encode + * the types. + */ +@ScalaJSDefined +trait RequestInit extends js.Object { + var method: js.UndefOr[HttpMethod] + + var headers: js.UndefOr[HeadersInit] + + var body: js.UndefOr[BodyInit] + + var referrer: js.UndefOr[String] + + var referrerPolicy: js.UndefOr[ReferrerPolicy] + + var mode: js.UndefOr[RequestMode] + + var credentials: js.UndefOr[RequestCredentials] + + var requestCache: js.UndefOr[RequestCache] + + var requestRedirect: js.UndefOr[RequestRedirect] + + var integrity: js.UndefOr[String] + + /** + * The whatwg spec section on [[https://fetch.spec.whatwg.org/#requestinit RequestInit dictionary]] + * has a comment that states that this value "can only be set to null". + * In the detailed steps section for [[https://fetch.spec.whatwg.org/#dom-request the Request(input,init) constructor]] + * it says even more clearly: + * "If init's window member is present and it is not null, throw a TypeError." + */ + var window: js.UndefOr[Null] +} + +/** + * [[https://fetch.spec.whatwg.org/#response ¶6.4 Response class]] in whatwg spec + * + * @param content optional content + * @param init optional response initialisiton + */ +@js.native +class Response(content: BodyInit = null, init: ResponseInit = null) + extends Body { + + /** Contains the type of the response */ + def `type`: ResponseType = js.native + + /** Contains the URL of the response. */ + def url: String = js.native + + /** + * Contains a boolean stating whether the response was successful (status in + * the range 200-299) or not. + **/ + def ok: Boolean = js.native + + /** Contains the status code of the response (e.g., 200 for a success). */ + val status: Int = js.native //actually returns unsigned short + + /** Contains the status message corresponding to the status code (e.g., OK for 200). */ + def statusText: ByteString = js.native //actually returns ByteString + + /** Contains the Headers object associated with the response. */ + val headers: Headers = js.native + + val body: ReadableStream[Uint8Array] = js.native + + override def clone(): Response = js.native +} + +/** + * static methods associated with a Response object in + * [[https://fetch.spec.whatwg.org/#response ¶6.4 Response class]] of + * whatwg Fetch spec. + */ +@js.native +object Response extends js.Object { + /** + * @return a new Response object associated with a network error. + */ + def error(): Response = js.native + + /** + * @param url + * @param status redirect status + * @return a new Response + */ + def redirect(url: String, status: Int = 302): Response = js.native +} + +/** + * See [[https://fetch.spec.whatwg.org/#response-class ¶6.4 Response class]] + * definition in whatwg Fetch spec. + * + */ +@ScalaJSDefined +trait ResponseInit extends js.Object { + var status: Int + var statusText: ByteString + var headers: HeadersInit +} + +object ResponseInit { + def apply(_status: Int = 200, + _statusText: ByteString = "OK", + _headers: HeadersInit = js.Dictionary[String]()): ResponseInit = new ResponseInit { + var status = _status + var statusText = _statusText + var headers = _headers + } +} + +/** + * See [[https://fetch.spec.whatwg.org/#body body interface]] in whatwg Fetch spec. + * + * see also [[https://developer.mozilla.org/en-US/docs/Web/API/Body Body]] in MDN + */ +@js.native +trait Body extends js.Object { + + /** + * MDN: Contains a Boolean that indicates whether the body has been read. + */ + def bodyUsed: Boolean = js.native + + /** + * MDN: Takes a Response stream and reads it to completion. It returns a + * promise that resolves with an ArrayBuffer. + */ + def arrayBuffer(): Promise[ArrayBuffer] = js.native + + /** + * Takes a Response stream and reads it to completion. It returns a + * promise that resolves with a Blob. + */ + def blob(): Promise[Blob] = js.native + + /** + * Takes a Response stream and reads it to completion. It returns a + * promise that resolves with a FormData object. + */ + def formData(): Promise[FormData] = js.native + + /** + * Takes a Response stream and reads it to completion. It returns a + * promise that resolves with a JSON object. + * //todo: define the JSON type, and return a Promise[JSON] as per spec + */ + def json(): Promise[js.Any] = js.native + + /** + * Takes a Response stream and reads it to completion. It returns a + * promise that resolves with a USVString (text). + */ + def text(): Promise[String] = js.native +} + +/** + * [[https://fetch.spec.whatwg.org/#headers-class ¶6.1 Headers Class]] of whatwg spec + * + * The Headers interface of the Fetch API allows you to perform various actions + * on HTTP request and response headers. These actions include retrieving, setting, + * adding to, and removing. A Headers object has an associated header + * list, which is initially empty and consists of zero or more name and value pairs. + * You can add to this using methods like append() (see Examples.) In all methods of + * this interface, header names are matched by case-insensitive byte sequence. + * + * For security reasons, some headers can only be controller by the user agent. + * These headers include the forbidden header names and forbidden response header + * names. + * + * A Headers object also has an associated guard, which takes a value of immutable, + * request, request-no-cors, response, or none. This affects whether the set(), + * delete(), and append() methods will mutate the header. For more information + * see Guard. + * + * You can retrieve a Headers object via the Request.headers and Response.headers + * properties, and create a new Headers object using the Headers.Headers() constructor. + * + * MDN + */ +@js.native +class Headers(map: HeadersInit = js.Array[js.Array[String]]()) + extends JSIterable[js.Array[ByteString]] { + + /** + * The append() method of the Headers interface appends a new value onto an + * existing header inside a Headers object, or adds the header if it does + * not already exist. + * + * The difference between Headers.set and append() is that if the specified + * header already exists and accepts multiple values, Headers.set will overwrite + * the existing value with the new one, whereas append() will append the new + * value onto the end of the set of values. + */ + def append(name: ByteString, value: ByteString): Unit = js.native + + /** + * The set() method of the Headers interface sets a new value for an existing + * header inside a Headers object, or adds the header if it does not already exist. + * + * The difference between set() and Headers.append is that if the specified + * header already exists and accepts multiple values, set() overwrites the + * existing value with the new one, whereas Headers.append appends the new + * value to the end of the set of values. + * + * Not all headers can be set. See the list of forbidden header names in + * https://fetch.spec.whatwg.org/#terminology-headers + */ + def set(name: ByteString, value: ByteString): Unit = js.native + + /** + * The delete() method of the Headers interface deletes a header from the current Headers object. + */ + def delete(name: ByteString): Unit = js.native + + /** + * The get() method of the Headers interface returns the first value of a given header from within a Headers object + * . If the requested header doesn't exist in the Headers object, the call returns null. + * + * @param name a ByteString, ie. only ASCII chars + */ + def get(name: ByteString): js.UndefOr[ByteString] = js.native + + /** + * The getAll() method of the Headers interface returns an array of all the + * values of a header within a Headers object with a given name. If the + * requested header doesn't exist in the Headers object, it returns an empty array. + * + * @param name a ByteString, ie. only ASCII chars + */ + def getAll(name: ByteString): Sequence[ByteString] = js.native + + /** + * The has() method of the Headers interface returns a boolean stating whether + * a Headers object contains a certain header. + * + * @param name a ByteString, ie. only ASCII chars + */ + def has(name: ByteString): Boolean = js.native +} + + +@js.native +sealed trait ReferrerPolicy extends js.Any + +object ReferrerPolicy { + val empty = "".asInstanceOf[ReferrerPolicy] + val `no-referrer` = "no-referrer".asInstanceOf[ReferrerPolicy] + val `no-referrer-when-downgrade` = "no-referrer-when-downgrade".asInstanceOf[ReferrerPolicy] + val `origin-only` = "origin-only".asInstanceOf[ReferrerPolicy] + val `origin-when-cross-origin` = "origin-when-cross-origin".asInstanceOf[ReferrerPolicy] + val `unsafe-url` = "unsafe-url".asInstanceOf[ReferrerPolicy] +} + +/** + * This is not typed in the Fetch API but it is easy to create the most common + * defaults. + */ +@js.native +trait HttpMethod extends js.Any + +object HttpMethod { + val GET = "GET".asInstanceOf[HttpMethod] + val POST = "POST".asInstanceOf[HttpMethod] + val PUT = "PUT".asInstanceOf[HttpMethod] + val PATCH = "PATCH".asInstanceOf[HttpMethod] + val DELETE = "DELETE".asInstanceOf[HttpMethod] + val QUERY = "QUERY".asInstanceOf[HttpMethod] + val HEAD = "HEAD".asInstanceOf[HttpMethod] + val OPTIONS = "OPTIONS".asInstanceOf[HttpMethod] +} + +/** + * Fetch APIs [[https://fetch.spec.whatwg.org/#requesttype RequestType enum]] + */ +@js.native +sealed trait RequestType extends js.Any + +object RequestType { + val empty = "".asInstanceOf[RequestType] + val audio = "audio".asInstanceOf[RequestType] + val font = "font".asInstanceOf[RequestType] + val image = "image".asInstanceOf[RequestType] + val script = "script".asInstanceOf[RequestType] + val style = "style".asInstanceOf[RequestType] + val track = "track".asInstanceOf[RequestType] + val video = "video".asInstanceOf[RequestType] +} + +/** + * Fetch APIs [[https://fetch.spec.whatwg.org/#requestdestination RequestDestination enum]] + */ +@js.native +sealed trait RequestDestination extends js.Any + +object RequestDestination { + val empty = "".asInstanceOf[RequestDestination] + val document = "document".asInstanceOf[RequestDestination] + val sharedworker = "sharedworker".asInstanceOf[RequestDestination] + val subresource = "subresource".asInstanceOf[RequestDestination] + val unknown = "unknown".asInstanceOf[RequestDestination] + val worker = "worker".asInstanceOf[RequestDestination] +} + +/** + * Fetch API's [[https://fetch.spec.whatwg.org/#requestmode RequestMode enum]] + */ +@js.native +sealed trait RequestMode extends js.Any + +object RequestMode { + val navigate = "navigate".asInstanceOf[RequestMode] + val `same-origin` = "same-origin".asInstanceOf[RequestMode] + val `no-cors` = "no-cors".asInstanceOf[RequestMode] + val cors = "cors".asInstanceOf[RequestMode] +} + +/** + * Fetch APIs [[https://fetch.spec.whatwg.org/#requestcredentials RequestCredentials enum]] + */ +@js.native +sealed trait RequestCredentials extends js.Any + +object RequestCredentials { + val omit = "omit".asInstanceOf[RequestCredentials] + val `same-origin` = "same-origin".asInstanceOf[RequestCredentials] + val include = "include".asInstanceOf[RequestCredentials] +} + +/** + * Fetch APIs [[https://fetch.spec.whatwg.org/#requestcache RequestCache enum]] + */ +@js.native +sealed trait RequestCache extends js.Any + +object RequestCache { + val default = "default".asInstanceOf[RequestCache] + val `no-store` = "no-store".asInstanceOf[RequestCache] + val reload = "reload".asInstanceOf[RequestCache] + val `no-cache` = "no-cache".asInstanceOf[RequestCache] + val `force-cache` = "force-cache".asInstanceOf[RequestCache] + val `only-if-cached` = "only-if-cached".asInstanceOf[RequestCache] +} + +/** + * Fetch API's [[https://fetch.spec.whatwg.org/#requestredirect RequestRedirect enum]] + */ +@js.native +sealed trait RequestRedirect extends js.Any + +object RequestRedirect { + val follow = "follow".asInstanceOf[RequestRedirect] + val error = "error".asInstanceOf[RequestRedirect] + val manual = "manual".asInstanceOf[RequestRedirect] +} + +@js.native +sealed trait ResponseType extends js.Any + +/** + * see [[https://fetch.spec.whatwg.org/#responsetype]] of whatwg Fetch spec + */ +object ResponseType { + val basic = "basic".asInstanceOf[ResponseType] + val cors = "cors".asInstanceOf[ResponseType] + val default = "default".asInstanceOf[ResponseType] + val error = "error".asInstanceOf[ResponseType] + val opaque = "opaque".asInstanceOf[ResponseType] + val opaqueredirect = "opaqueredirect".asInstanceOf[ResponseType] +} diff --git a/src/main/scala/org/scalajs/dom/experimental/JSIterable.scala b/src/main/scala/org/scalajs/dom/experimental/JSIterable.scala new file mode 100644 index 000000000..62f014027 --- /dev/null +++ b/src/main/scala/org/scalajs/dom/experimental/JSIterable.scala @@ -0,0 +1,56 @@ +package scala.scalajs.js.dom.experimental + +import scala.scalajs.js +import scala.scalajs.js.Dynamic.{global => g} +import scala.scalajs.js.annotation.{ScalaJSDefined, JSBracketCall} + +/** + * See [[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols The iterable protocol]] + * on MDN + * + * todo: to be replaced by official version once + * https://github.com/scala-js/scala-js/issues/1141 + * is completed + */ +@ScalaJSDefined +trait JSIterable[A] extends js.Object + +@ScalaJSDefined +trait JSIterator[+A] extends js.Object { + def next(): JSIterator.Entry[A] +} + +object JSIterator { + private val iteratorSymbol = g.Symbol.iterator + + @js.native + private trait IteratorMethodAccess extends js.Object { + @JSBracketCall + def bracketCall[A](method: js.Any)(): A + } + + @ScalaJSDefined + trait Entry[+A] extends js.Object { + def done: Boolean + def value: A + } + + def toIterator[A](it: JSIterator[A]): scala.collection.Iterator[A] = { + new scala.collection.Iterator[A]() { + private var nextEntry: Entry[A] = it.next() + + def hasNext: Boolean = !nextEntry.done + + def next(): A = { + val result = nextEntry.value + nextEntry = it.next() + result + } + } + } + + implicit class IterableW[+A](it: JSIterable[A]) { + def iterator(): Iterator[A] = + toIterator(it.asInstanceOf[IteratorMethodAccess].bracketCall[JSIterator[A]](iteratorSymbol)()) + } +} diff --git a/src/main/scala/org/scalajs/dom/experimental/ServiceWorker.scala b/src/main/scala/org/scalajs/dom/experimental/ServiceWorker.scala new file mode 100644 index 000000000..8bf1495eb --- /dev/null +++ b/src/main/scala/org/scalajs/dom/experimental/ServiceWorker.scala @@ -0,0 +1,228 @@ +package org.scalajs.dom.experimental + +import org.scalajs.dom.{MessagePort, raw} +import org.scalajs.dom.webgl.RenderingContext + +import scala.scalajs.js +import scala.scalajs.js.| + +//todo: move all ServiceWorker code together ( currently some code in raw ) +//todo: some of the ServiceWorker code in raw there could be typed a bit more precisely using some classes defined here + +@js.native +sealed trait FrameType extends js.Any + +/** + * part of ServiceWorker + * [[https://slightlyoff.github.io/ServiceWorker/spec/service_worker_1/#client-frametype ¶4.2.2 frameType]] + * of serviceWorker spec + */ +object FrameType { + /** + * The window client's global object's browsing context is an auxiliary browsing context. + */ + val auxiliary = "auxiliary".asInstanceOf[FrameType] + + /** The window client's global object's browsing context is a top-level browsing context. */ + val `top-level` = "top-level".asInstanceOf[FrameType] + + /** The window client's global object's browsing context is a nested browsing context. */ + val nested = "nested".asInstanceOf[FrameType] + + val none = "none".asInstanceOf[FrameType] +} + +/** + * [[https://slightlyoff.github.io/ServiceWorker/spec/service_worker_1/#client ¶4.2 Client]] + * of Service Workers 1 spec. + */ +@js.native +trait Client extends js.Object { + /** + * The url attribute must return the context object's associated service worker + * client's serialized creation url. + */ + def url: String = js.native + + def frameType: FrameType = js.native + + /** + * The id attribute must return its associated service worker client's id. + */ + def id: String = js.native + + /** + * + * @param message the spec says this is of type any (?!) + * @param transfer https://html.spec.whatwg.org/multipage/infrastructure.html#transferable-objects + * + */ + def postMessage(message: Any, transfer: Sequence[Transferable] = null): Unit = js.native +} + +/** + * see [[https://html.spec.whatwg.org/multipage/scripting.html#canvasproxy ¶4.12.4.1 Proxying canvases to workers]] + * in whatwg html spec. + */ +@js.native +trait CanvasProxy extends js.Any { + def setContext(context: RenderingContext): Unit = js.native +} + +/** + * See [[https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent FetchEvent]] on MDN + * + * [[https://slightlyoff.github.io/ServiceWorker/spec/service_worker_1/#fetch-event-section ¶4.5 FetchEvent]] + * on whatwg ServiceWorker spec. + */ +@js.native +class FetchEvent extends raw.Event { + /** + * @return Boolean that is true if the event was dispatched with the user's + * intention for the page to reload, and false otherwise. Typically, + * pressing the refresh button in a browser is a reload, while clicking a + * link and pressing the back button is not. + */ + def isReload: Boolean = js.native + + /** + * @return the Request that triggered the event handler. + */ + def request: Request = js.native + + /** + * See [[https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent/respondWith respondWith]] + * page on MDN. + * + * The respondWith() method of the FetchEvent interface is intended for + * containing code that generates custom responses to the requests coming + * from the controlled page. This code will resolve by returning a Response + * or network error to Fetch. + * + * Renderer-side security checks about tainting for cross-origin content are + * tied to the transparency (or opacity) of the Response body, not URLs. If + * the request is a top-level navigation and the return value is a Response + * whose type attribute is opaque (i.e. an opaque response body), a network + * error is returned to Fetch. The final URL of all successful (non + * network-error) responses is the requested URL. + */ + def respondWith(promisedResponse: raw.Promise[Response]): Unit = js.native +} + +object ServiceWorker { + + /** + * typed methods to set event handlers on ServiceWorkers. See + * [[https://slightlyoff.github.io/ServiceWorker/spec/service_worker_1/#service-worker-global-scope-activate-event ¶4.7 Events]] + * of whatwg ServiceWorker spec. + */ + implicit class ServiceWorkerOpts(serviceWorker: raw.ServiceWorker) { + + def addInstallListener(fn: ExtendableEvent => Unit) = + serviceWorker.addEventListener("install", fn) + + def addActivateListener(fn: ExtendableEvent => Unit) = + serviceWorker.addEventListener("activate", fn) + + def addFetchListener(fn: FetchEvent => Unit) = + serviceWorker.addEventListener("fetch", fn) + + def addMessageListener(fn: MessageEvent => Unit) = + serviceWorker.addEventListener("message", fn) + + } +} + +/** + * See [[https://slightlyoff.github.io/ServiceWorker/spec/service_worker_1/#extendable-event-interface ¶4.4 ExtendableEvent]] + * of whatwg ServiceWorker spec. + * + * An ExtendableEvent object has an associated extend lifetime promises (an + * array of promises). It is initially set to null. + */ +@js.native +class ExtendableEvent extends raw.Event { + def waitUntil(promise: raw.Promise[Any]): Unit = js.native +} + +/** + * See [[https://slightlyoff.github.io/ServiceWorker/spec/service_worker_1/#extendablemessage-event-interface ¶4.6 ExtendableMessageEvent]] + * of whatwg ServiceWorker spec. + * + * Service workers define the extendable message event that extends the + * message event defined in HTML to allow extending the lifetime of the event. + * + * See also: [[https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerMessageEvent ServiceWorkerMessageEvent]] on MDN + */ +@js.native +class MessageEvent extends ExtendableEvent { + + /** + * Returns the event's data. It can be any data type. + */ + val data: Any = js.native + + /** + * Returns the origin of the service worker's environment settings object. + */ + val origin: String = js.native //should be type DOMString + + /** + * Represents, in server-sent events, the last event ID of the event source. + */ + val lastEventId: String = js.native //should be String + + /** + * @return a reference to the service worker that sent the message. + */ + def source: Client | raw.ServiceWorker | MessagePort = js.native + + /** + * It represents the MessagePort array being sent, if any. + */ + def ports: js.Array[MessagePort] = js.native +} +/** + * See [[https://slightlyoff.github.io/ServiceWorker/spec/service_worker_1/#service-worker-obj ¶3.1 ServiceWorker]] + * of ServiceWorker whatwg spec. + */ +@js.native +sealed trait ServiceWorkerState extends js.Any + +object ServiceWorkerState { + /** + * The service worker in this state is considered an installing worker. + * During this state, event.waitUntil(f) can be called inside the oninstall + * event handler to extend the life of the installing worker until the + * passed promise resolves successfully. This is primarily used to ensure + * that the service worker is not active until all of the core caches are + * populated. + */ + val installing = "installing".asInstanceOf[ServiceWorkerState] + + /** + * The service worker in this state is considered a waiting worker. + */ + val installed = "installed".asInstanceOf[ServiceWorkerState] + + /** + * The service worker in this state is considered an active worker. During + * this state, event.waitUntil(f) can be called inside the onactivate event + * handler to extend the life of the active worker until the passed promise + * resolves successfully. No functional events are dispatched until the + * state becomes activated. + */ + val activating = "activating".asInstanceOf[ServiceWorkerState] + + /** + * The service worker in this state is considered an active worker ready to + * handle functional events. + */ + val activated = "activated".asInstanceOf[ServiceWorkerState] + + /** + * A new service worker is replacing the current service worker, or the + * current service worker is being discarded due to an install failure. + */ + val redundant = "redundant".asInstanceOf[ServiceWorkerState] +} diff --git a/src/main/scala/org/scalajs/dom/experimental/Stream.scala b/src/main/scala/org/scalajs/dom/experimental/Stream.scala new file mode 100644 index 000000000..c4f4f8d59 --- /dev/null +++ b/src/main/scala/org/scalajs/dom/experimental/Stream.scala @@ -0,0 +1,379 @@ +package org.scalajs.dom.experimental + +import org.scalajs.dom.raw.Promise + +import scala.scalajs.js + +// the stream API is defined in https://streams.spec.whatwg.org/ + +/** + * [[https://streams.spec.whatwg.org/#ws-state ¶4.2.4.3. get state]] of + * whatwg streams spec + */ +@js.native +sealed trait WriteableState extends js.Any + +object WriteableState { + + /** + * The stream’s internal queue is full; that is, the stream is exerting + * backpressure. Use .ready to be notified of when the pressure subsides. + */ + val waiting = "waiting".asInstanceOf[WriteableState] + + /** + * The stream’s internal queue is not full; call .write() until + * backpressure is exerted. + */ + val writable = "writable".asInstanceOf[WriteableState] + + /** + * The stream’s .close() method has been called, and a command to close is + * in the queue or being processed by the underlying sink; attempts to + * write will now fail. */ + val closing = "closing".asInstanceOf[WriteableState] + + /** The underlying sink has been closed; writing is no longer possible. */ + val closed = "closed".asInstanceOf[WriteableState] + + /** An error occurred interacting with the underlying sink or the stream + * has been aborted, so the stream is now + * dead. */ + val errored = "errored".asInstanceOf[WriteableState] +} + +/** + * [[https://streams.spec.whatwg.org/#ws-class ¶4.2. Class WritableStream]] of + * whatwg Stream spec + * + * + * todo: the constructor + * @tparam T Type of the Chunks to be written to the Stream + */ +@js.native +trait WriteableStream[-T] extends js.Object { + + /** + * The closed getter returns a promise that will be fulfilled when the + * stream becomes closed, or rejected if it ever errors. + * see [[https://streams.spec.whatwg.org/#ws-closed ¶4.2.4.1. get closed]] + */ + val closed: Promise[WriteableStream[T]] = js.native + + /** + * The ready getter returns a promise that will be fulfilled when the + * stream transitions away from the "waiting" state to any other state. + * Once the stream transitions back to "waiting", the getter will return a + * new promise that stays pending until the next state transition. In + * essence, this promise gives a signal as to when any backpressure has let + * up (or that the stream has been + * closed or errored). + * + * see [[https://streams.spec.whatwg.org/#ws-ready ¶4.2.4.2. get ready]] + * of whatwg streams spec. + */ + val ready: Promise[WriteableStream[T]] = js.native + + /** + * The state getter returns the state of the stream + * see [[https://streams.spec.whatwg.org/#ws-state ¶4.2.4.3. get state]] + * of whatwg streams spec + */ + def state: WriteableState = js.native + + /** + * The abort method signals that the producer can no longer successfully + * write to the stream and it should be immediately moved to an "errored" + * state, with any queued-up writes discarded. This will also execute any + * abort mechanism of the underlying sink. + * see [[https://streams.spec.whatwg.org/#ws-abort ¶4.2.4.4. abort(reason)]] + * + * @param reason spec specifies Any (!?) + */ + def abort(reason: Any): Unit = js.native + + /** + * The close method signals that the producer is done writing chunks to the + * stream and wishes to move the stream to a "closed" state. This queues an + * action to close the stream, such that once any currently queued-up + * writes complete, the close mechanism of the underlying sink will + * execute, releasing any held resources. In the meantime, the stream will + * be in a "closing" state. + * + * [[https://streams.spec.whatwg.org/#ws-close ¶4.2.4.5. close()]] + * + * @return a promise of this stream being closed + */ + def close(): Promise[WriteableStream[T]] = js.native + + /** + * The write method adds a write to the stream’s internal queue, + * instructing the stream to write the given chunk of data to the + * underlying sink once all other pending writes have finished successfully + * . It returns a promise that will be fulfilled or rejected depending on + * the success or failure of writing the chunk to the underlying sink. The + * impact of enqueuing this chunk will be immediately reflected in the + * stream’s state property; in particular, if the internal queue is now + * full according to the stream’s queuing strategy, the stream will exert + * backpressure by changing its state to "waiting". + * + * see [[https://streams.spec.whatwg.org/#ws-write 4.2.4.6. write(chunk)]] + * of whatwg streams spec + * + * @param chunk + * @return bblfish: not sure what the type of the promise returned is + */ + def write(chunk: Chunk[T]): Promise[Any] = js.native +} + +/** + * defined at [[https://streams.spec.whatwg.org/#readable-stream ¶2.1. Readable Streams]] of + * whatwg Streams spec. + * + * @tparam T Type of the Chunks returned by the Stream. Can't make it coveriant, + * due to T + */ +@js.native +trait ReadableStream[+T] extends js.Object { + /** + * The locked getter returns whether or not the readable stream is locked + * to a reader. + * + * throws scala.scalajs.js.TypeError if the stream is not readable + */ + def locked: Boolean = js.native + + /** + * The cancel method cancels the stream, signaling a loss of interest in + * the stream by a consumer. The supplied reason argument will be given to + * the underlying source, which may or may not use it. + * + * @param reason the reason <- actually not what type this is + * @return a Promise, but not quite sure what it can contain + */ + def cancel(reason: String): Promise[Any] = js.native + + /** + * See [[https://streams.spec.whatwg.org/#rs-get-reader ¶3.2.4.3. getReader()]] + * of whatwg streams spec. Also see the example usage there. + * + * The getReader method creates a readable stream reader and locks the + * stream to the new reader. While the stream is locked, no other reader + * can be acquired until this one is released. The returned reader provides + * the ability to directly read individual chunks from the stream via the + * reader’s read method. This functionality is especially useful for + * creating abstractions that desire the ability to consume a stream in its + * entirety. By getting a reader for the stream, you can ensure nobodyA + * else can interleave reads with yours or cancel the stream, which would + * interfere with your abstraction. + * + * Note that if a stream becomes closed or errored, any reader it is locked + * to is automatically released. + * + * throws scala.scalajs.js.TypeError if not a readable stream + * + * @return a new ReadableStreamReader + * + */ + def getReader(): ReadableStreamReader[T] = js.native + + /** + * see [[https://streams.spec.whatwg.org/#rs-pipe-through §3.2.4.4. pipeThrough({ writable, readable }, options)]] + * + * The pipeThrough method provides a convenient, chainable way of piping + * this readable stream through a transform stream (or any other { + * writable, readable } pair). It simply pipes the stream into the writable + * side of the supplied pair, and returns the readable side for further use + * . Piping a stream will generally lock it for the duration of the pipe, + * preventing any other consumer fromA acquiring a + * reader. + * + * This method is intentionally generic; it does not require that its this + * value be a ReadableStream object. It also does not require that its + * writable argument be a WritableStream instance, or that its readable + * argument be a ReadableStream instance. + * + * //todo: determine the type of options + */ + def pipeThrough[U]( + pair: Any, // TODO js.Tuple2[WriteableStream[T], ReadableStream[U]] + options: Any = js.undefined): ReadableStream[U] = js.native + + /** + * See [[https://streams.spec.whatwg.org/#rs-pipe-to ¶3.2.4.5. pipeTo(dest, { preventClose, preventAbort, preventCancel } = {})]] + * of whatwg Streams spec. + * + * The pipeTo method pipes this readable stream to a given writable stream. + * The way in which the piping process behaves under various error + * conditions can be customized with a number of passed options. It returns + * a promise that fulfills when the piping process completes successfully, + * or rejects if any errors were encountered. + * + * Piping a stream will generally lock it for the duration of the pipe, + * preventing any other consumer from acquiring a reader. This method is + * intentionally generic; it does not require that its this value be a + * ReadableStream object. + * + * //todo: determine the type of options + */ + def pipeTo(dest: WriteableStream[T], options: Any = js.undefined): Unit = js.native + + /** + * See [[https://streams.spec.whatwg.org/#rs-tee ¶3.2.4.6. tee()]] of whatwg + * streams spec. + * + * The tee method tees this readable stream, returning a two-element array + * containing the two resulting branches as new ReadableStream instances. + * + * Teeing a stream will lock it, preventing any other consumer from + * acquiring a reader. To cancel the stream, cancel both of the resulting + * branches; a composite cancellation reason will then be propagated to the + * stream’s underlying source. + * + * Note that the chunks seen in each branch will be the same object. If the + * chunks are not immutable, this could allow interference between the two + * branches. (Let us know if you think we should add an option to tee that + * creates structured clones of the chunks for each branch.) + */ + def tee(): js.Array[_ <: ReadableStream[T]] = js.native //TODO js.Tuple2[ReadableStream[T], ReadableStream[T]] +} + +/** + * See [[https://streams.spec.whatwg.org/#reader-class ¶3.4. Class ReadableStreamReader]] + * of whatwg streams spec. + * + * The ReadableStreamReader class represents a readable stream reader designed + * to be vended [sic] by a ReadableStream instance. + * + * The ReadableStreamReader constructor is generally not meant to be used directly; + * instead, a stream’s getReader() method should be used. This allows different + * classes of readable streams to vend different classes of readers without the + * consumer needing to know which goes with which. + * + * @tparam T Type of the Chunks returned by the Stream + */ +@js.native +class ReadableStreamReader[+T](stream: ReadableStream[T]) extends js.Object { + + /** + * See [[https://streams.spec.whatwg.org/#reader-closed §3.4.4.1. get closed]] + * of whatwg Streams spec. + * + * The closed getter returns a promise that will be fulfilled when the stream + * becomes closed or the reader’s lock is released, or rejected if the + * stream ever errors. + */ + def closed: Promise[ReadableStreamReader[T]] = js.native + + /** + * See [[https://streams.spec.whatwg.org/#reader-cancel §3.4.4.2. cancel(reason)]] + * of whatwg Streams spec. + * + * If the reader is active, the cancel method behaves the same as that for + * the associated stream. When done, it automatically releases the lock. + * + * //todo determine type of reason + */ + def cancel(reason: Any): Promise[Any] = js.native //not actually sure what the return type is here + + /** + * See [[https://streams.spec.whatwg.org/#reader-read 3.4.4.3. read()]] of + * whatwg Stream spec. + * + * The read method will return a promise that allows access to the next chunk + * from the stream’s internal queue, if available. If the chunk does become + * available, the promise will be fulfilled with an object of the form { + * value: theChunk, done: false }. If the stream becomes closed, the + * promise will be fulfilled with an object of the form { value: undefined, + * done: true }. If the stream becomes errored, the promise will be + * rejected with the relevant error. If reading a chunk causes the queue to + * become empty, more data will be pulled from the underlying source. + */ + def read(): Promise[Chunk[T]] = js.native + + /** + * The releaseLock method releases the reader’s lock on the corresponding + * stream. After the lock is released, the reader is no longer active. If the + * associated stream is errored when the lock is released, the reader will + * appear errored in the same way from now on; otherwise, the reader will + * appear closed. + * A reader’s lock cannot be released while it still has a pending read + * request, i.e., if a promise returned by the reader’s read() method has + * not yet been settled. Attempting to do so will throw a TypeError and leave + * the reader locked to the stream. + * + * throws scala.scalajs.js.TypeError + */ + def releaseLock(): Unit = js.native +} + +/** + * + * [[https://streams.spec.whatwg.org/#rs-controller-class ¶3.3 Class + * ReadableStreamController]] of whatwg spec + * + * The ReadableStreamController constructor cannot be used directly; it only + * works on a ReadableStream that is in the middle of being constructed. + * + * @param stream can be null + * @tparam T Type of the Chunks to be enqueued to the Stream + + */ +@js.native +class ReadableStreamController[-T](stream: ReadableStream[T] = null) extends js.Object { + + /** + * + * The desiredSize getter returns the desired size to fill the controlled + * stream’s internal queue. It can be negative, if the queue is over-full. + * An underlying source should use this information to determine when and how + * to apply backpressure. + * + * @return the size of the strem - no idea if this actually is an int + */ + def desiredSize: Int = js.native + + /** + * The close method will close the controlled readable stream. Consumers will + * still be able to read any previously-enqueued chunks from the stream, but + * once those are read, the stream will become closed + * throws scala.scalajs.js.TypeError if this is not a readable controller + */ + def close(): Unit = js.native + + /** + * The enqueue method will enqueue a given chunk in the controlled readable + * stream. + * + * @param chunk + * throws scala.scalajs.js.RangeError if size is too big + * @return seems like its an undefOr[Int] of the size + * + */ + def enqueue(chunk: Chunk[T]): js.UndefOr[Int] = js.native + + /** + * The error method will error the readable stream, making all future + * interactions with it fail with the given error e. + * + * @param e : an error - can this be any type? + * throws scala.scalajs.js.TypeError + */ + def error(e: Any): Unit = js.native +} + +/** + * + * See [[https://streams.spec.whatwg.org/#chunk ¶2 Model]] but mostly the + * examples in the whatwg streams spec + * + */ +@js.native +trait Chunk[+T] extends js.Object { + /** + * The value of the chunk. + */ + def value: T = js.native + + def done: Boolean = js.native +} diff --git a/src/main/scala/org/scalajs/dom/experimental/package.scala b/src/main/scala/org/scalajs/dom/experimental/package.scala new file mode 100644 index 000000000..5783fcfe5 --- /dev/null +++ b/src/main/scala/org/scalajs/dom/experimental/package.scala @@ -0,0 +1,55 @@ +package org.scalajs.dom + +import org.scalajs.dom.crypto.BufferSource + +import scala.scalajs.js +import scala.scalajs.js.| + +package object experimental { + + /** + * defined at [[https://fetch.spec.whatwg.org/#request-class ¶6.3 Request class]] + * of whatwg Fetch spec + */ + type RequestInfo = String | Request + + /** + * defined at [[https://fetch.spec.whatwg.org/#headersinit ¶6.1 Header Class]] + * in whatwg Fetch spec + * todo: it should be OpenEndedDictionary[ByteString] + */ + type HeadersInit = Headers | Sequence[Sequence[ByteString]] | OpenEndedDictionary[ByteString] + + /** + * This type should capture strings consisting only of ASCII chars + * todo: is there a way to capture this type? + */ + type ByteString = String + + /** + * defined at [[https://fetch.spec.whatwg.org/#body-mixin ¶6.2 Body mixin]] + * in whatwg Fetch spec + */ + type BodyInit = Blob | BufferSource | FormData | String //todo: add URLSearchParams + + /** + * WebIDL sequence is js.Array[T] | JSIterable[T]. However @mseddon knows + * at least Blink's IDL compiler treats these as simply js.Array[T] for now. + * We keep this type as a reminder to check in more detail + */ + type Sequence[T] = js.Array[T] + + /** + * see [[https://fetch.spec.whatwg.org/#headers-class ¶6.1 Headers class]] in + * whatwg Fetch spec. + * also see: [[https://github.com/whatwg/fetch/issues/164 issue 164]] in Fetch + * API git repo, as this is not clearly defined + */ + type OpenEndedDictionary[T] = js.Dictionary[T] + + /** + * [[https://html.spec.whatwg.org/multipage/infrastructure.html#transferable ¶2.7.4 Transferable Objects]] + * in whatwg html spec. + */ + type Transferable = js.typedarray.ArrayBuffer | CanvasProxy | MessagePort +}