From a599ff9500fbdc0286dd006bf68e52b9796768cb Mon Sep 17 00:00:00 2001 From: Josh Vlk Date: Sun, 8 Nov 2026 17:43:02 -0500 Subject: [PATCH 1/6] fix(webapi): add private native record pilot --- src/dom-nodes/DOMTree.res | 22 ++++++++++------------ src/dom-nodes/Node.res | 6 ++++++ src/file/FileTypes.res | 4 ++-- src/file/WritableStream.res | 2 +- src/html/HTMLSlotElement.res | 3 +++ src/messaging/WebSocketsTypes.res | 5 ++--- src/storage/IDBIndex.res | 2 +- src/storage/IDBObjectStore.res | 2 +- src/storage/IndexedDbTypes.res | 8 ++++---- tests/DOMAPI/AddEventListener__test.res | 8 +++++--- tests/DOMAPI/HTMLSlotElement__test.res | 4 ++-- tests/FileAPI/ReadableStream__test.res | 9 ++++----- 12 files changed, 41 insertions(+), 34 deletions(-) diff --git a/src/dom-nodes/DOMTree.res b/src/dom-nodes/DOMTree.res index 9611fd51..460bd4df 100644 --- a/src/dom-nodes/DOMTree.res +++ b/src/dom-nodes/DOMTree.res @@ -8,15 +8,14 @@ type slotAssignmentMode = | @as("manual") Manual | @as("named") Named -type customStateSet = {} +type customStateSet = private {} /** Node is an interface from which a number of WebApiDOM API object types inherit. It allows those types to be treated similarly; for example, inheriting the same set of methods, or being tested in the same way. [See Node on MDN](https://developer.mozilla.org/docs/Web/API/Node) -TODO: mark as private once mutating fields of private records is allowed */ @editor.completeFrom(Node) -type rec node = { +type rec node = private { ...DOM.eventTarget, /** Returns the type of node. @@ -91,9 +90,8 @@ type rec node = { /** Element is the most general base class from which all objects in a Document inherit. It only has methods and properties common to all kinds of elements. More specific classes inherit from Element. [See Element on MDN](https://developer.mozilla.org/docs/Web/API/Element) -TODO: mark as private once mutating fields of private records is allowed */ -@editor.completeFrom(Element) and element = { +@editor.completeFrom(Element) and element = private { // Base properties from Node /** Returns the type of node. @@ -463,7 +461,7 @@ A minimal document object that has no parent. It is used as a lightweight versio [See DocumentFragment on MDN](https://developer.mozilla.org/docs/Web/API/DocumentFragment) TODO: mark as private once mutating fields of private records is allowed */ -@editor.completeFrom(DocumentFragment) and documentFragment = { +@editor.completeFrom(DocumentFragment) and documentFragment = private { // Base properties from Node /** Returns the type of node. @@ -560,7 +558,7 @@ TODO: mark as private once mutating fields of private records is allowed [See ShadowRoot on MDN](https://developer.mozilla.org/docs/Web/API/ShadowRoot) TODO: mark as private once mutating fields of private records is allowed */ -@editor.completeFrom(ShadowRoot) and shadowRoot = { +@editor.completeFrom(ShadowRoot) and shadowRoot = private { // Base properties from DocumentFragment // End base properties from DocumentFragment @@ -703,7 +701,7 @@ Any HTML element. Some elements directly implement this interface, while others [See HTMLElement on MDN](https://developer.mozilla.org/docs/Web/API/HTMLElement) TODO: mark as private once mutating fields of private records is allowed */ -@editor.completeFrom(HTMLElement) and htmlElement = { +@editor.completeFrom(HTMLElement) and htmlElement = private { // Base properties from Element /** Returns the namespace. @@ -996,7 +994,7 @@ A
element in the WebApiDOM; it allows access to and in some cases modific [See HTMLFormElement on MDN](https://developer.mozilla.org/docs/Web/API/HTMLFormElement) TODO: mark as private once mutating fields of private records is allowed */ -@editor.completeFrom(HTMLFormElement) and htmlFormElement = { +@editor.completeFrom(HTMLFormElement) and htmlFormElement = private { // Base properties from HTMLElement /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/HTMLElement/title) @@ -1298,12 +1296,12 @@ TODO: mark as private once mutating fields of private records is allowed } @editor.completeFrom(HTMLFormControlsCollection) -and htmlFormControlsCollection = { +and htmlFormControlsCollection = private { length: int, } @editor.completeFrom(HTMLSlotElement) -and htmlSlotElement = { +and htmlSlotElement = private { // Base properties from Element /** Returns the namespace. @@ -1596,7 +1594,7 @@ and htmlSlotElement = { } @editor.completeFrom(ElementInternals) -and elementInternals = { +and elementInternals = private { shadowRoot: Null.t, form: Null.t, willValidate: bool, diff --git a/src/dom-nodes/Node.res b/src/dom-nodes/Node.res index 6d35787a..59206404 100644 --- a/src/dom-nodes/Node.res +++ b/src/dom-nodes/Node.res @@ -11,6 +11,12 @@ module Impl = ( external asNode: T.t => DOMTree.node = "%identity" + @set + external setNodeValue: (T.t, Null.t) => unit = "nodeValue" + + @set + external setTextContent: (T.t, Null.t) => unit = "textContent" + type getRootNodeOptions = {mutable composed?: bool} /** diff --git a/src/file/FileTypes.res b/src/file/FileTypes.res index d50b7cd1..48165b4e 100644 --- a/src/file/FileTypes.res +++ b/src/file/FileTypes.res @@ -37,7 +37,7 @@ This Streams API interface represents a readable stream of byte data. The WebApi [See ReadableStream on MDN](https://developer.mozilla.org/docs/Web/API/ReadableStream) */ @editor.completeFrom(ReadableStream) -type readableStream<'r> = { +type readableStream<'r> = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/ReadableStream/locked) */ @@ -49,7 +49,7 @@ This Streams API interface provides a standard abstraction for writing streamin [See WritableStream on MDN](https://developer.mozilla.org/docs/Web/API/WritableStream) */ @editor.completeFrom(WritableStream) -type writableStream<'w> = { +type writableStream<'w> = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/WritableStream/locked) */ diff --git a/src/file/WritableStream.res b/src/file/WritableStream.res index d2cabeb4..bc64f321 100644 --- a/src/file/WritableStream.res +++ b/src/file/WritableStream.res @@ -1,5 +1,5 @@ @editor.completeFrom(WritableStream) -type t<'w> = FileTypes.writableStream<'w> = {...FileTypes.writableStream<'w>} +type t<'w> = FileTypes.writableStream<'w> = private {...FileTypes.writableStream<'w>} /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/WritableStream) diff --git a/src/html/HTMLSlotElement.res b/src/html/HTMLSlotElement.res index 7e549fe3..5fcc0a63 100644 --- a/src/html/HTMLSlotElement.res +++ b/src/html/HTMLSlotElement.res @@ -7,6 +7,9 @@ type t = DOMTree.htmlSlotElement = private { include HTMLElement.Impl({type t = t}) +@set +external setName: (t, string) => unit = "name" + /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/HTMLSlotElement/assignedNodes) */ diff --git a/src/messaging/WebSocketsTypes.res b/src/messaging/WebSocketsTypes.res index d17d22bb..ca4061e5 100644 --- a/src/messaging/WebSocketsTypes.res +++ b/src/messaging/WebSocketsTypes.res @@ -9,10 +9,9 @@ type messageEventSource = unknown /** Provides the API for creating and managing a WebSocket connection to a server, as well as for sending and receiving data on the connection. [See WebSocket on MDN](https://developer.mozilla.org/docs/Web/API/WebSocket) -TODO: mark as private once mutating fields of private records is allowed */ @editor.completeFrom(WebSocket) -type webSocket = { +type webSocket = private { ...DOM.eventTarget, /** Returns the WebApiURL that was used to establish the WebSocket connection. @@ -47,7 +46,7 @@ If the WebSocket connection is closed, this attribute's value will only increase Can be set, to change how binary data is returned. The default is "blob". [Read more on MDN](https://developer.mozilla.org/docs/Web/API/WebSocket/binaryType) */ - mutable binaryType: binaryType, + binaryType: binaryType, } /** diff --git a/src/storage/IDBIndex.res b/src/storage/IDBIndex.res index e1e7d823..6195f169 100644 --- a/src/storage/IDBIndex.res +++ b/src/storage/IDBIndex.res @@ -1,4 +1,4 @@ -type t = IndexedDbTypes.idbIndex = {...IndexedDbTypes.idbIndex} +type t = IndexedDbTypes.idbIndex = private {...IndexedDbTypes.idbIndex} /** Retrieves the value of the first record matching the given key or key range in query. diff --git a/src/storage/IDBObjectStore.res b/src/storage/IDBObjectStore.res index cf30c2cc..e72cecca 100644 --- a/src/storage/IDBObjectStore.res +++ b/src/storage/IDBObjectStore.res @@ -1,4 +1,4 @@ -type t = IndexedDbTypes.idbObjectStore = {...IndexedDbTypes.idbObjectStore} +type t = IndexedDbTypes.idbObjectStore = private {...IndexedDbTypes.idbObjectStore} /** Adds or updates a record in store with the given value and key. diff --git a/src/storage/IndexedDbTypes.res b/src/storage/IndexedDbTypes.res index cd7315dd..9a6f2680 100644 --- a/src/storage/IndexedDbTypes.res +++ b/src/storage/IndexedDbTypes.res @@ -129,15 +129,15 @@ type idbOpenDBRequest = { /** This example shows a variety of different uses of object stores, from updating the data structure with IDBObjectStore.createIndex inside an onupgradeneeded function, to adding a new item to our object store with IDBObjectStore.add. For a full working example, see our To-do Notifications app (view example live.) [See IDBObjectStore on MDN](https://developer.mozilla.org/docs/Web/API/IDBObjectStore) -TODO: mark as private once mutating fields of private records is allowed */ @editor.completeFrom(IDBObjectStore) -type idbObjectStore = { +type idbObjectStore = private { /** Returns the name of the store. [Read more on MDN](https://developer.mozilla.org/docs/Web/API/IDBObjectStore/name) */ @throws(JsExn) + @set mutable name: string, /** Returns the key path of the store, or null if none. @@ -164,15 +164,15 @@ type idbObjectStore = { /** IDBIndex interface of the WebApiIndexedDB API provides asynchronous access to an index in a database. An index is a kind of object store for looking up records in another object store, called the referenced object store. You use this interface to retrieve data. [See IDBIndex on MDN](https://developer.mozilla.org/docs/Web/API/IDBIndex) -TODO: mark as private once mutating fields of private records is allowed */ @editor.completeFrom(IDBIndex) -type idbIndex = { +type idbIndex = private { /** Returns the name of the index. [Read more on MDN](https://developer.mozilla.org/docs/Web/API/IDBIndex/name) */ @throws(JsExn) + @set mutable name: string, /** Returns the IDBObjectStore the index belongs to. diff --git a/tests/DOMAPI/AddEventListener__test.res b/tests/DOMAPI/AddEventListener__test.res index 7daa3b0b..12949efe 100644 --- a/tests/DOMAPI/AddEventListener__test.res +++ b/tests/DOMAPI/AddEventListener__test.res @@ -1,16 +1,18 @@ let button = DomGlobal.document->Document.querySelector("button")->Null.toOption let h2 = DomGlobal.document->Document.querySelector("h2")->Null.toOption +module AnyElement = Element.Impl({type t = Element.t}) + switch (button, h2) { | (Some(button), Some(h2)) => button->Element.addEventListener(EventType.Click, (e: UiEventsTypes.mouseEvent) => { Console.log(`Button clicked, ${Int.toString(e.button)}`) switch h2.textContent { - | Null => h2.textContent = Value("1") + | Null => h2->AnyElement.setTextContent(Value("1")) | Value(text) => switch Int.fromString(text) { - | None => h2.textContent = Value("1") - | Some(number) => h2.textContent = Value(Int.toString(number + 1)) + | None => h2->AnyElement.setTextContent(Value("1")) + | Some(number) => h2->AnyElement.setTextContent(Value(Int.toString(number + 1))) } } }) diff --git a/tests/DOMAPI/HTMLSlotElement__test.res b/tests/DOMAPI/HTMLSlotElement__test.res index 6e0acc3b..527fb4ad 100644 --- a/tests/DOMAPI/HTMLSlotElement__test.res +++ b/tests/DOMAPI/HTMLSlotElement__test.res @@ -1,7 +1,7 @@ let assignedNodesForElement = (element: DOMTree.element) => switch element.assignedSlot { | Value(slot) => - slot.name = "content" + slot->HTMLSlotElement.setName("content") slot->HTMLSlotElement.assignedNodes | Null => [] } @@ -9,7 +9,7 @@ let assignedNodesForElement = (element: DOMTree.element) => let assignedNodesForText = (text: Text.t) => switch text.assignedSlot { | Value(slot) => - slot.name = "content" + slot->HTMLSlotElement.setName("content") slot->HTMLSlotElement.assignedNodes | Null => [] } diff --git a/tests/FileAPI/ReadableStream__test.res b/tests/FileAPI/ReadableStream__test.res index 8207d12d..fef5dba0 100644 --- a/tests/FileAPI/ReadableStream__test.res +++ b/tests/FileAPI/ReadableStream__test.res @@ -3,9 +3,8 @@ let strategy: FileTypes.queuingStrategy = Obj.magic() let _make: ReadableStream.t = ReadableStream.make() -let _fromUnderlyingSource = ReadableStream.fromUnderlyingSource(underlyingSource) +let _fromUnderlyingSource: ReadableStream.t = + ReadableStream.fromUnderlyingSource(underlyingSource) -let _fromUnderlyingSourceWithStrategy = ReadableStream.fromUnderlyingSource( - underlyingSource, - ~strategy, -) +let _fromUnderlyingSourceWithStrategy: ReadableStream.t = + ReadableStream.fromUnderlyingSource(underlyingSource, ~strategy) From 4acdab4fd5c02149d1c0e64c445a9ecb796e9920 Mon Sep 17 00:00:00 2001 From: Josh Vlk Date: Sun, 8 Nov 2026 18:02:56 -0500 Subject: [PATCH 2/6] fix(webapi): expand private native record coverage --- CHANGELOG.md | 6 ++ .../docs/contributing/api-modelling.mdx | 34 ++++++ docs/content/docs/project-status.mdx | 10 +- scripts/check-consumer.mjs | 36 ++++++- scripts/consumer-native-setters.res | 53 +++++++++ scripts/consumer-private-coercion-caveat.res | 18 ++++ .../DOMTokenListBackingRouteIsPrivate.res | 1 + .../DOMTokenListPublicViewIsPrivate.res | 1 + .../DOMTreeElementBackingRouteIsPrivate.res | 1 + .../GenericNodeListUpdateIsPrivate.res | 1 + .../HTMLCanvasRecordUpdateIsPrivate.res | 1 + .../negative/NativeRecordUpdateIsPrivate.res | 1 + scripts/negative-fixtures.json | 36 +++++++ src/canvas/HTMLCanvasElement.res | 8 +- src/core/DOM.res | 16 +-- src/cssom/CSSStyleDeclaration.res | 9 +- src/dom-nodes/AbstractRange.res | 2 +- src/dom-nodes/Attr.res | 5 +- src/dom-nodes/CDATASection.res | 2 +- src/dom-nodes/CharacterData.res | 6 +- src/dom-nodes/DOMImplementation.res | 4 +- src/dom-nodes/DOMTree.res | 3 - src/dom-nodes/Element.res | 102 ++++++++++++++++++ src/dom-nodes/ElementInternals.res | 43 ++++++++ src/dom-nodes/ProcessingInstruction.res | 2 +- src/dom-nodes/ShadowRoot.res | 14 +++ src/dom-nodes/TreeWalker.res | 6 +- src/dom-platform/DOMException.res | 2 +- src/dom-platform/DOMStringList.res | 2 +- src/dom-platform/DOMTokenList.res | 5 +- src/html/HTMLAnchorElement.res | 2 +- src/html/HTMLAreaElement.res | 2 +- src/html/HTMLBaseElement.res | 2 +- src/html/HTMLDataElement.res | 2 +- src/html/HTMLDialogElement.res | 2 +- src/html/HTMLElement.res | 39 +++++++ src/html/HTMLEmbedElement.res | 2 +- src/html/HTMLFieldSetElement.res | 2 +- src/html/HTMLFormElement.res | 17 +++ src/html/HTMLIFrameElement.res | 2 +- src/html/HTMLImageElement.res | 2 +- src/html/HTMLInputElement.res | 2 +- src/html/HTMLLabelElement.res | 2 +- src/html/HTMLLinkElement.res | 2 +- src/html/HTMLMapElement.res | 2 +- src/html/HTMLMetaElement.res | 2 +- src/html/HTMLMeterElement.res | 2 +- src/html/HTMLOListElement.res | 2 +- src/html/HTMLObjectElement.res | 2 +- src/html/HTMLOptGroupElement.res | 2 +- src/html/HTMLOptionElement.res | 2 +- src/html/HTMLOptionsCollection.res | 2 +- src/html/HTMLOutputElement.res | 2 +- src/html/HTMLProgressElement.res | 2 +- src/html/HTMLScriptElement.res | 2 +- src/html/HTMLSelectElement.res | 2 +- src/html/HTMLSourceElement.res | 2 +- src/html/HTMLStyleElement.res | 2 +- src/html/HTMLTableCellElement.res | 2 +- src/html/HTMLTemplateElement.res | 2 +- src/html/HTMLTextAreaElement.res | 2 +- src/html/HTMLTimeElement.res | 2 +- src/html/HTMLTitleElement.res | 2 +- src/html/HTMLUnknownElement.res | 2 +- src/messaging/WebSocket.res | 3 + src/messaging/WebSocketsTypes.res | 3 +- src/storage/IDBIndex.res | 3 + src/storage/IDBObjectStore.res | 3 + src/storage/IndexedDbTypes.res | 6 +- src/svg/SVGElement.res | 20 +++- tests/DOMAPI/AddEventListener__test.res | 4 +- tests/FileAPI/ReadableStream__test.res | 10 +- tests/browser/BindingContracts__test.res | 6 +- 73 files changed, 528 insertions(+), 80 deletions(-) create mode 100644 scripts/consumer-native-setters.res create mode 100644 scripts/consumer-private-coercion-caveat.res create mode 100644 scripts/fixtures/negative/DOMTokenListBackingRouteIsPrivate.res create mode 100644 scripts/fixtures/negative/DOMTokenListPublicViewIsPrivate.res create mode 100644 scripts/fixtures/negative/DOMTreeElementBackingRouteIsPrivate.res create mode 100644 scripts/fixtures/negative/GenericNodeListUpdateIsPrivate.res create mode 100644 scripts/fixtures/negative/HTMLCanvasRecordUpdateIsPrivate.res create mode 100644 scripts/fixtures/negative/NativeRecordUpdateIsPrivate.res diff --git a/CHANGELOG.md b/CHANGELOG.md index 0835093c..70264098 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,12 @@ versioning and publication. browser's successful result is `undefined`, not a database. - `WebAPI.Notification.t` no longer contains a static permission field. Read that value through `WebAPI.Notification.permission`. +- Native browser-instance record construction and record-update copies are being restricted with + private record views. Reads remain direct; writes to protected mutable properties migrate to + typed setters such as `canvas->WebAPI.HTMLCanvasElement.setWidth(640)`. Options, initializers, + callback dictionaries, and plain data records remain constructible. This compile-time restriction + does not prevent a deliberate explicit structural coercion (`:>`) from a recreated public record + shape, unsafe casts, or JavaScript fabrication. For the intended installation, compiler range, runtime/browser checks, and known limitations, see the [project status](https://rescript-lang.github.io/experimental-rescript-webapi/project-status/) diff --git a/docs/content/docs/contributing/api-modelling.mdx b/docs/content/docs/contributing/api-modelling.mdx index f5119614..0690fc52 100644 --- a/docs/content/docs/contributing/api-modelling.mdx +++ b/docs/content/docs/contributing/api-modelling.mdx @@ -58,6 +58,40 @@ Keep null and empty states explicit when they are part of the browser contract. for nullable results and variants for meaningful empty values; do not replace native results with invented defaults or unchecked casts. +## Native instance types + +Model browser-owned objects as private records at both the backing declaration and every public +record view. Keep direct field reads when useful, but expose typed native setters for writable +properties instead of asking callers to mutate a private field: + +```ReScript +let canvas = WebAPI.Document.createElement(document, "canvas")->WebAPI.HTMLCanvasElement.classify +switch canvas { +| Some(canvas) => + let width = canvas.width + canvas->WebAPI.HTMLCanvasElement.setWidth(width + 1) +| None => () +} +``` + +Use names such as `setWidth`, preserve the JavaScript property name and existing field type, and +keep throwing behavior on setters whose native operation can throw. Put shared properties on the +existing `Impl` chain so derived interfaces reuse their base setters. Do not make initializer +dictionaries, option records, callback data, or ordinary result records private merely because they +are exported beside a native interface. + +For `CSSStyleDeclaration`, keep direct reads but use its existing `setProperty` operation for +individual declarations, using CSS property names such as `"background-color"` rather than the +camel-cased IDL field name. Both `float` and `cssFloat` address the `"float"` CSS property. +Replacing the complete declaration block uses `CSSStyleDeclaration.setCssText`; do not route that +operation through `setProperty`. + +Private records prevent ordinary literals and record-update copies, but ReScript currently permits +a deliberate explicit structural coercion such as `ordinaryRecord :> Native.t` when a caller +recreates the full public shape. This representation is not an unforgeable native brand; do not +describe it as preventing deliberate coercions, unsafe casts, or JavaScript fabrication. A stronger +guarantee requires an opaque representation and corresponding getter APIs. + ## Verification Add compile-only, Node runtime, or browser runtime fixtures according to the API being modeled. diff --git a/docs/content/docs/project-status.mdx b/docs/content/docs/project-status.mdx index d7528876..e17b3fa6 100644 --- a/docs/content/docs/project-status.mdx +++ b/docs/content/docs/project-status.mdx @@ -35,9 +35,13 @@ implementation modules and internal type-holder modules are not part of that pub Bindings are maintained and corrected by hand as well as generated; the generator is not a reproducible source of truth for every checked-in binding. -Approximately 80 bindings still carry private-record TODOs; a blanket conversion is deferred -because the current compiler reports mutation warning 111. Generator reproducibility is also -follow-up work, not an alpha release gate. +Native-instance privacy is being implemented in the private-record alternative for +[#403](https://github.com/rescript-lang/experimental-rescript-webapi/issues/403). Protected +instances retain direct reads and use typed setters for supported writes; caller-owned option and +initializer records remain constructible. This is not yet complete across the package while the +implementation is under review. The private-record representation also permits a deliberate +explicit structural coercion (`:>`) from a recreated public shape, so it does not provide an +unforgeable native-instance brand. Generator reproducibility remains separate follow-up work. CI uses Node 26 for ReScript compilation and Node runtime fixtures. Its Playwright suite runs compiled Chromium regressions for the confirmed alpha contracts, including typed byte/audio diff --git a/scripts/check-consumer.mjs b/scripts/check-consumer.mjs index 9f1ec437..97c63f4c 100644 --- a/scripts/check-consumer.mjs +++ b/scripts/check-consumer.mjs @@ -67,7 +67,7 @@ const writeProject = (directory, sourceFile, features) => { dependencies: features === null ? ["@rescript/webapi"] : [{ name: "@rescript/webapi", features }], "experimental-features": { LetUnwrap: true }, - warnings: { error: "+8" }, + warnings: { error: "+8+111" }, "package-specs": { module: "esmodule", "in-source": true }, }, null, @@ -80,6 +80,29 @@ const writeProject = (directory, sourceFile, features) => { const compilerCli = (directory) => path.join(directory, "node_modules", "rescript", "cli", "rescript.js"); +const verifyPrivateRecordRelaxation = (directory) => { + const backingFile = path.join( + directory, + "node_modules", + "@rescript", + "webapi", + "src/core/DOM.res", + ); + const source = readFileSync(backingFile, "utf8"); + const privateDeclaration = "type domTokenList = private {"; + if (source.split(privateDeclaration).length !== 2) { + throw new Error("Expected exactly one private DOM.domTokenList backing declaration."); + } + writeFileSync(backingFile, source.replace(privateDeclaration, "type domTokenList = {")); + writeProject(directory, "scripts/fixtures/negative/DOMTokenListBackingRouteIsPrivate.res", [ + "WebAPI.DOM", + ]); + compile(directory); + console.log( + "[ok] DOM backing-route fixture compiles when only its privacy restriction is relaxed", + ); +}; + const compile = (directory, expectedFailure = null) => { const cli = compilerCli(directory); const clean = spawnSync(process.execPath, [cli, "clean"], { cwd: directory }); @@ -193,6 +216,16 @@ const checkConsumers = async () => { fixture: "scripts/consumer-concrete-types.res", features: null, }, + { + name: "native setters and constructible request options", + fixture: "scripts/consumer-native-setters.res", + features: null, + }, + { + name: "documented private-record coercion limitation", + fixture: "scripts/consumer-private-coercion-caveat.res", + features: null, + }, { name: "Fetch-only", fixture: "scripts/consumer-fetch.res", @@ -247,6 +280,7 @@ const checkConsumers = async () => { compile(projectDirectory, negativeCase.expectedDiagnostic); console.log(`[ok] expected rejection: ${negativeCase.name}`); } + verifyPrivateRecordRelaxation(projectDirectory); console.log(`[ok] tarball contents (${packagedFiles.length} files)`); } finally { rmSync(tempDirectory, { recursive: true, force: true }); diff --git a/scripts/consumer-native-setters.res b/scripts/consumer-native-setters.res new file mode 100644 index 00000000..4a2d9b63 --- /dev/null +++ b/scripts/consumer-native-setters.res @@ -0,0 +1,53 @@ +let requestInit: WebAPI.Request.requestInit = {method: "POST"} +let responseInit: WebAPI.Response.responseInit = {status: 201} +let request = WebAPI.Request.fromURL("https://example.invalid", ~init=requestInit) +let response = WebAPI.Response.fromString("ok", ~init=responseInit) +let responseStatus = response.status + +module NodeBindings = WebAPI.Node.Impl({type t = WebAPI.Node.t}) +let setNodeText = (node: WebAPI.Node.t) => { + node->NodeBindings.setTextContent(Null.Value("updated")) + node.textContent +} + +module ElementBindings = WebAPI.Element.Impl({type t = WebAPI.Element.t}) +let setElementScroll = (element: WebAPI.Element.t) => { + element->ElementBindings.setScrollTop(12.) + element.scrollTop +} + +let setCanvasSize = (canvas: WebAPI.HTMLCanvasElement.t) => { + canvas->WebAPI.HTMLCanvasElement.setWidth(640) + canvas->WebAPI.HTMLCanvasElement.setHeight(480) + (canvas.width, canvas.height) +} + +let setTokenListValue = (tokens: WebAPI.DOMTokenList.t) => + { + tokens->WebAPI.DOMTokenList.setValue("active") + tokens.value + } + +let setCssDeclaration = (style: WebAPI.CSSStyleDeclaration.t) => { + style->WebAPI.CSSStyleDeclaration.setProperty(~property="background-color", ~value="red") + let backgroundColor = style.backgroundColor + style->WebAPI.CSSStyleDeclaration.setCssText("background-color: blue") + backgroundColor +} + +let setSocketBinaryType = (socket: WebAPI.WebSocket.t) => + socket->WebAPI.WebSocket.setBinaryType(socket.binaryType) + +let setIndexedDBNames = ( + store: WebAPI.IDBObjectStore.t, + index: WebAPI.IDBIndex.t, +) => { + store->WebAPI.IDBObjectStore.setName("records") + index->WebAPI.IDBIndex.setName("by-name") +} + +let asBaseElement = (canvas: WebAPI.HTMLCanvasElement.t) => + WebAPI.HTMLCanvasElement.asElement(canvas) + +let _ = (request, responseStatus, setNodeText, setElementScroll, setCanvasSize, setTokenListValue, + setCssDeclaration, setSocketBinaryType, setIndexedDBNames, asBaseElement) diff --git a/scripts/consumer-private-coercion-caveat.res b/scripts/consumer-private-coercion-caveat.res new file mode 100644 index 00000000..cb0c33f3 --- /dev/null +++ b/scripts/consumer-private-coercion-caveat.res @@ -0,0 +1,18 @@ +type responseShape = {...WebAPI.Response.t} + +let deliberatelyForgeResponse = (original: WebAPI.Response.t) => { + let ordinaryRecord: responseShape = { + type_: original.type_, + url: "", + redirected: false, + status: 200, + ok: true, + statusText: "OK", + headers: original.headers, + body: Null, + bodyUsed: false, + } + (ordinaryRecord :> WebAPI.Response.t) +} + +let _ = deliberatelyForgeResponse diff --git a/scripts/fixtures/negative/DOMTokenListBackingRouteIsPrivate.res b/scripts/fixtures/negative/DOMTokenListBackingRouteIsPrivate.res new file mode 100644 index 00000000..398410e6 --- /dev/null +++ b/scripts/fixtures/negative/DOMTokenListBackingRouteIsPrivate.res @@ -0,0 +1 @@ +let update = (tokens: WebAPI.DOM.domTokenList) => {...tokens, value: "forged"} diff --git a/scripts/fixtures/negative/DOMTokenListPublicViewIsPrivate.res b/scripts/fixtures/negative/DOMTokenListPublicViewIsPrivate.res new file mode 100644 index 00000000..8f176ecc --- /dev/null +++ b/scripts/fixtures/negative/DOMTokenListPublicViewIsPrivate.res @@ -0,0 +1 @@ +let update = (tokens: WebAPI.DOMTokenList.t) => {...tokens, value: "forged"} diff --git a/scripts/fixtures/negative/DOMTreeElementBackingRouteIsPrivate.res b/scripts/fixtures/negative/DOMTreeElementBackingRouteIsPrivate.res new file mode 100644 index 00000000..9c52afff --- /dev/null +++ b/scripts/fixtures/negative/DOMTreeElementBackingRouteIsPrivate.res @@ -0,0 +1 @@ +let update = (element: WebAPI.DOMTree.element) => {...element, id: "forged"} diff --git a/scripts/fixtures/negative/GenericNodeListUpdateIsPrivate.res b/scripts/fixtures/negative/GenericNodeListUpdateIsPrivate.res new file mode 100644 index 00000000..c9e38d62 --- /dev/null +++ b/scripts/fixtures/negative/GenericNodeListUpdateIsPrivate.res @@ -0,0 +1 @@ +let update = (nodes: WebAPI.NodeList.t) => {...nodes, length: 0} diff --git a/scripts/fixtures/negative/HTMLCanvasRecordUpdateIsPrivate.res b/scripts/fixtures/negative/HTMLCanvasRecordUpdateIsPrivate.res new file mode 100644 index 00000000..1d9c3277 --- /dev/null +++ b/scripts/fixtures/negative/HTMLCanvasRecordUpdateIsPrivate.res @@ -0,0 +1 @@ +let update = (canvas: WebAPI.HTMLCanvasElement.t) => {...canvas, width: 1} diff --git a/scripts/fixtures/negative/NativeRecordUpdateIsPrivate.res b/scripts/fixtures/negative/NativeRecordUpdateIsPrivate.res new file mode 100644 index 00000000..314ac65d --- /dev/null +++ b/scripts/fixtures/negative/NativeRecordUpdateIsPrivate.res @@ -0,0 +1 @@ +let update = (response: WebAPI.Response.t) => {...response, statusText: "forged"} diff --git a/scripts/negative-fixtures.json b/scripts/negative-fixtures.json index 98b868fb..382955e1 100644 --- a/scripts/negative-fixtures.json +++ b/scripts/negative-fixtures.json @@ -59,6 +59,42 @@ "features": ["WebAPI.Canvas"], "expectedDiagnostic": "Cannot create values of the" }, + { + "name": "native Response record updates are private", + "fixture": "scripts/fixtures/negative/NativeRecordUpdateIsPrivate.res", + "features": ["WebAPI.Fetch"], + "expectedDiagnostic": "Cannot create values of the" + }, + { + "name": "DOMTokenList public view rejects record updates", + "fixture": "scripts/fixtures/negative/DOMTokenListPublicViewIsPrivate.res", + "features": ["WebAPI.DOMPlatform"], + "expectedDiagnostic": "Cannot create values of the" + }, + { + "name": "DOM backing route rejects record updates", + "fixture": "scripts/fixtures/negative/DOMTokenListBackingRouteIsPrivate.res", + "features": ["WebAPI.DOM"], + "expectedDiagnostic": "Cannot create values of the" + }, + { + "name": "HTML canvas record updates are private", + "fixture": "scripts/fixtures/negative/HTMLCanvasRecordUpdateIsPrivate.res", + "features": ["WebAPI.Canvas"], + "expectedDiagnostic": "Cannot create values of the" + }, + { + "name": "DOMTree backing route rejects record updates", + "fixture": "scripts/fixtures/negative/DOMTreeElementBackingRouteIsPrivate.res", + "features": ["WebAPI.DOMNodes"], + "expectedDiagnostic": "Cannot create values of the" + }, + { + "name": "generic native NodeList updates are private", + "fixture": "scripts/fixtures/negative/GenericNodeListUpdateIsPrivate.res", + "features": ["WebAPI.DOMNodes"], + "expectedDiagnostic": "Cannot create values of the" + }, { "name": "Web Locks callback accepts nullable Lock", "fixture": "scripts/fixtures/negative/WebLocksRejectsNonNullableCallback.res", diff --git a/src/canvas/HTMLCanvasElement.res b/src/canvas/HTMLCanvasElement.res index a60d303d..e059097b 100644 --- a/src/canvas/HTMLCanvasElement.res +++ b/src/canvas/HTMLCanvasElement.res @@ -3,7 +3,7 @@ Provides properties and methods for manipulating the layout and presentation of [See HTMLCanvasElement on MDN](https://developer.mozilla.org/docs/Web/API/HTMLCanvasElement) TODO: mark as private once mutating fields of private records is allowed */ -type t = { +type t = private { ...DOMTree.htmlElement, /** Gets or sets the width of a canvas element on a document. @@ -19,6 +19,12 @@ type t = { include HTMLElement.Impl({type t = t}) +@set +external setWidth: (t, int) => unit = "width" + +@set +external setHeight: (t, int) => unit = "height" + /** `isInstanceOf(value)` diff --git a/src/core/DOM.res b/src/core/DOM.res index 50a9a5cd..9c8a5e1e 100644 --- a/src/core/DOM.res +++ b/src/core/DOM.res @@ -11,7 +11,7 @@ An abnormal event (called an exception) which occurs as a result of calling a me [See DOMException on MDN](https://developer.mozilla.org/docs/Web/API/DOMException) */ @editor.completeFrom(DOMException) -type domException = { +type domException = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/DOMException/name) */ @@ -27,7 +27,7 @@ A type returned by some APIs which contains a list of DOMString (strings). [See DOMStringList on MDN](https://developer.mozilla.org/docs/Web/API/DOMStringList) */ @editor.completeFrom(DOMStringList) -type domStringList = { +type domStringList = private { /** Returns the number of strings in strings. [Read more on MDN](https://developer.mozilla.org/docs/Web/API/DOMStringList/length) @@ -201,7 +201,7 @@ type shareData = { /** [See UserActivation on MDN](https://developer.mozilla.org/docs/Web/API/UserActivation) */ -type userActivation = { +type userActivation = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/UserActivation/hasBeenActive) */ @@ -221,7 +221,7 @@ type navigator // TODO: mark as private once mutating fields of private records is allowed @editor.completeFrom(DOMTokenList) -type domTokenList = { +type domTokenList = private { /** Returns the number of tokens. [Read more on MDN](https://developer.mozilla.org/docs/Web/API/DOMTokenList/length) @@ -239,7 +239,7 @@ Can be set, to change the associated attribute. /** [See FragmentDirective on MDN](https://developer.mozilla.org/docs/Web/API/FragmentDirective) */ -type fragmentDirective = {} +type fragmentDirective = private {} /** [See CustomElementRegistry on MDN](https://developer.mozilla.org/docs/Web/API/CustomElementRegistry) @@ -250,7 +250,7 @@ type customElementRegistry = private {} /** [See BarProp on MDN](https://developer.mozilla.org/docs/Web/API/BarProp) */ -type barProp = { +type barProp = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/BarProp/visible) */ @@ -401,7 +401,7 @@ type screenOrientation = private { A screen, usually the one on which the current window is being rendered, and is obtained using window.screen. [See Screen on MDN](https://developer.mozilla.org/docs/Web/API/Screen) */ -type screen = { +type screen = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/Screen/availWidth) */ @@ -516,7 +516,7 @@ type domRect = private { The validity states that an element can be in, with respect to constraint validation. Together, they help explain why an element's value fails to validate, if it's not valid. [See ValidityState on MDN](https://developer.mozilla.org/docs/Web/API/ValidityState) */ -type validityState = { +type validityState = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/ValidityState/valueMissing) */ diff --git a/src/cssom/CSSStyleDeclaration.res b/src/cssom/CSSStyleDeclaration.res index 42e7ae6f..50d82cc0 100644 --- a/src/cssom/CSSStyleDeclaration.res +++ b/src/cssom/CSSStyleDeclaration.res @@ -1,5 +1,5 @@ @editor.completeFrom(CSSStyleDeclaration) -type t = { +type t = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/CSSStyleDeclaration/cssText) */ @@ -1702,6 +1702,13 @@ type t = { mutable cssFloat: string, } +/** +Replaces the declaration block's complete serialized contents. +[Read more on MDN](https://developer.mozilla.org/docs/Web/API/CSSStyleDeclaration/cssText) +*/ +@set +external setCssText: (t, string) => unit = "cssText" + /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/CSSStyleDeclaration/item) */ diff --git a/src/dom-nodes/AbstractRange.res b/src/dom-nodes/AbstractRange.res index cdc09c8e..aaf9b210 100644 --- a/src/dom-nodes/AbstractRange.res +++ b/src/dom-nodes/AbstractRange.res @@ -1,7 +1,7 @@ /** [See AbstractRange on MDN](https://developer.mozilla.org/docs/Web/API/AbstractRange) */ -type t = { +type t = private { /** Returns range's start node. [Read more on MDN](https://developer.mozilla.org/docs/Web/API/AbstractRange/startContainer) diff --git a/src/dom-nodes/Attr.res b/src/dom-nodes/Attr.res index a21ac964..4d503956 100644 --- a/src/dom-nodes/Attr.res +++ b/src/dom-nodes/Attr.res @@ -2,7 +2,7 @@ A WebApiDOM element's attribute as an object. In most WebApiDOM methods, you will probably directly retrieve the attribute as a string (e.g., Element.getAttribute(), but certain functions (e.g., Element.getAttributeNode()) or means of iterating give Attr types. [See Attr on MDN](https://developer.mozilla.org/docs/Web/API/Attr) */ -type t = { +type t = private { // Base properties from Node /** Returns the type of node. @@ -99,3 +99,6 @@ type t = { */ ownerElement: Null.t, } + +@set +external setValue: (t, string) => unit = "value" diff --git a/src/dom-nodes/CDATASection.res b/src/dom-nodes/CDATASection.res index 11809c99..4a72fb49 100644 --- a/src/dom-nodes/CDATASection.res +++ b/src/dom-nodes/CDATASection.res @@ -2,6 +2,6 @@ A CDATA section that can be used within XML to include extended portions of unescaped text. The symbols < and & don't need escaping as they normally do when inside a CDATA section. [See CDATASection on MDN](https://developer.mozilla.org/docs/Web/API/CDATASection) */ -type t = { +type t = private { ...Text.t, } diff --git a/src/dom-nodes/CharacterData.res b/src/dom-nodes/CharacterData.res index e9ec7107..c0a3bd36 100644 --- a/src/dom-nodes/CharacterData.res +++ b/src/dom-nodes/CharacterData.res @@ -1,9 +1,8 @@ /** The CharacterData abstract interface represents a Node object that contains characters. This is an abstract interface, meaning there aren't any object of type CharacterData: it is implemented by other interfaces, like Text, Comment, or ProcessingInstruction which aren't abstract. [See CharacterData on MDN](https://developer.mozilla.org/docs/Web/API/CharacterData) -TODO: mark as private once mutating fields of private records is allowed */ -type t = { +type t = private { // Base properties from Node /** Returns the type of node. @@ -104,6 +103,9 @@ module Impl = ( external asCharacterData: T.t => t = "%identity" + @set + external setData: (T.t, string) => unit = "data" + /** Inserts nodes just after node, while replacing strings in nodes with equivalent Text nodes. diff --git a/src/dom-nodes/DOMImplementation.res b/src/dom-nodes/DOMImplementation.res index 40b46a92..9d8a7d2b 100644 --- a/src/dom-nodes/DOMImplementation.res +++ b/src/dom-nodes/DOMImplementation.res @@ -8,7 +8,7 @@ type t = private {} A Node containing a doctype. [See DocumentType on MDN](https://developer.mozilla.org/docs/Web/API/DocumentType) */ -type documentType = { +type documentType = private { // Base properties from Node /** Returns the type of node. @@ -98,7 +98,7 @@ type documentType = { An XML document. It inherits from the generic Document and does not add any specific methods or properties to it: nevertheless, several algorithms behave differently with the two types of documents. [See XMLDocument on MDN](https://developer.mozilla.org/docs/Web/API/XMLDocument) */ -type xmlDocument = { +type xmlDocument = private { ...DOM.document, } diff --git a/src/dom-nodes/DOMTree.res b/src/dom-nodes/DOMTree.res index 460bd4df..d8e40ece 100644 --- a/src/dom-nodes/DOMTree.res +++ b/src/dom-nodes/DOMTree.res @@ -459,7 +459,6 @@ Element is the most general base class from which all objects in a Document inhe /** A minimal document object that has no parent. It is used as a lightweight version of Document that stores a segment of a document structure comprised of nodes just like a standard document. The key difference is that because the document fragment isn't part of the active document tree structure, changes made to the fragment don't affect the document, cause reflow, or incur any performance impact that can occur when changes are made. [See DocumentFragment on MDN](https://developer.mozilla.org/docs/Web/API/DocumentFragment) -TODO: mark as private once mutating fields of private records is allowed */ @editor.completeFrom(DocumentFragment) and documentFragment = private { // Base properties from Node @@ -699,7 +698,6 @@ Similarly, when the focused element is in a different node tree than documentOrS /** Any HTML element. Some elements directly implement this interface, while others implement it via an interface that inherits it. [See HTMLElement on MDN](https://developer.mozilla.org/docs/Web/API/HTMLElement) -TODO: mark as private once mutating fields of private records is allowed */ @editor.completeFrom(HTMLElement) and htmlElement = private { // Base properties from Element @@ -992,7 +990,6 @@ TODO: mark as private once mutating fields of private records is allowed /** A element in the WebApiDOM; it allows access to and in some cases modification of aspects of the form, as well as access to its component elements. [See HTMLFormElement on MDN](https://developer.mozilla.org/docs/Web/API/HTMLFormElement) -TODO: mark as private once mutating fields of private records is allowed */ @editor.completeFrom(HTMLFormElement) and htmlFormElement = private { // Base properties from HTMLElement diff --git a/src/dom-nodes/Element.res b/src/dom-nodes/Element.res index 7ae7d68e..0f2dcaf2 100644 --- a/src/dom-nodes/Element.res +++ b/src/dom-nodes/Element.res @@ -11,6 +11,108 @@ module Impl = ( external asElement: T.t => DOMTree.element = "%identity" + @set + external setId: (T.t, string) => unit = "id" + + @set + external setSlot: (T.t, string) => unit = "slot" + + @set + external setScrollTop: (T.t, float) => unit = "scrollTop" + + @set + external setScrollLeft: (T.t, float) => unit = "scrollLeft" + + @throws(JsExn) @set + external setInnerHTML: (T.t, string) => unit = "innerHTML" + + @throws(JsExn) @set + external setOuterHTML: (T.t, string) => unit = "outerHTML" + + @set + external setAriaAtomic: (T.t, Null.t) => unit = "ariaAtomic" + @set + external setAriaAutoComplete: (T.t, Null.t) => unit = "ariaAutoComplete" + @set + external setAriaBrailleLabel: (T.t, Null.t) => unit = "ariaBrailleLabel" + @set + external setAriaBrailleRoleDescription: (T.t, Null.t) => unit = + "ariaBrailleRoleDescription" + @set + external setAriaBusy: (T.t, Null.t) => unit = "ariaBusy" + @set + external setAriaChecked: (T.t, Null.t) => unit = "ariaChecked" + @set + external setAriaColCount: (T.t, Null.t) => unit = "ariaColCount" + @set + external setAriaColIndex: (T.t, Null.t) => unit = "ariaColIndex" + @set + external setAriaColIndexText: (T.t, Null.t) => unit = "ariaColIndexText" + @set + external setAriaColSpan: (T.t, Null.t) => unit = "ariaColSpan" + @set + external setAriaCurrent: (T.t, Null.t) => unit = "ariaCurrent" + @set + external setAriaDescription: (T.t, Null.t) => unit = "ariaDescription" + @set + external setAriaDisabled: (T.t, Null.t) => unit = "ariaDisabled" + @set + external setAriaExpanded: (T.t, Null.t) => unit = "ariaExpanded" + @set + external setAriaHasPopup: (T.t, Null.t) => unit = "ariaHasPopup" + @set + external setAriaHidden: (T.t, Null.t) => unit = "ariaHidden" + @set + external setAriaKeyShortcuts: (T.t, Null.t) => unit = "ariaKeyShortcuts" + @set + external setAriaLabel: (T.t, Null.t) => unit = "ariaLabel" + @set + external setAriaLevel: (T.t, Null.t) => unit = "ariaLevel" + @set + external setAriaLive: (T.t, Null.t) => unit = "ariaLive" + @set + external setAriaModal: (T.t, Null.t) => unit = "ariaModal" + @set + external setAriaMultiLine: (T.t, Null.t) => unit = "ariaMultiLine" + @set + external setAriaMultiSelectable: (T.t, Null.t) => unit = "ariaMultiSelectable" + @set + external setAriaOrientation: (T.t, Null.t) => unit = "ariaOrientation" + @set + external setAriaPlaceholder: (T.t, Null.t) => unit = "ariaPlaceholder" + @set + external setAriaPosInSet: (T.t, Null.t) => unit = "ariaPosInSet" + @set + external setAriaPressed: (T.t, Null.t) => unit = "ariaPressed" + @set + external setAriaReadOnly: (T.t, Null.t) => unit = "ariaReadOnly" + @set + external setAriaRequired: (T.t, Null.t) => unit = "ariaRequired" + @set + external setAriaRoleDescription: (T.t, Null.t) => unit = "ariaRoleDescription" + @set + external setAriaRowCount: (T.t, Null.t) => unit = "ariaRowCount" + @set + external setAriaRowIndex: (T.t, Null.t) => unit = "ariaRowIndex" + @set + external setAriaRowIndexText: (T.t, Null.t) => unit = "ariaRowIndexText" + @set + external setAriaRowSpan: (T.t, Null.t) => unit = "ariaRowSpan" + @set + external setAriaSelected: (T.t, Null.t) => unit = "ariaSelected" + @set + external setAriaSetSize: (T.t, Null.t) => unit = "ariaSetSize" + @set + external setAriaSort: (T.t, Null.t) => unit = "ariaSort" + @set + external setAriaValueMax: (T.t, Null.t) => unit = "ariaValueMax" + @set + external setAriaValueMin: (T.t, Null.t) => unit = "ariaValueMin" + @set + external setAriaValueNow: (T.t, Null.t) => unit = "ariaValueNow" + @set + external setAriaValueText: (T.t, Null.t) => unit = "ariaValueText" + /** Inserts nodes just after node, while replacing strings in nodes with equivalent Text nodes. diff --git a/src/dom-nodes/ElementInternals.res b/src/dom-nodes/ElementInternals.res index b4d942cc..fd975c59 100644 --- a/src/dom-nodes/ElementInternals.res +++ b/src/dom-nodes/ElementInternals.res @@ -2,6 +2,49 @@ type t = DOMTree.elementInternals = private { ...DOMTree.elementInternals, } +@set external setAriaAtomic: (t, Null.t) => unit = "ariaAtomic" +@set external setAriaAutoComplete: (t, Null.t) => unit = "ariaAutoComplete" +@set external setAriaBrailleLabel: (t, Null.t) => unit = "ariaBrailleLabel" +@set +external setAriaBrailleRoleDescription: (t, Null.t) => unit = "ariaBrailleRoleDescription" +@set external setAriaBusy: (t, Null.t) => unit = "ariaBusy" +@set external setAriaChecked: (t, Null.t) => unit = "ariaChecked" +@set external setAriaColCount: (t, Null.t) => unit = "ariaColCount" +@set external setAriaColIndex: (t, Null.t) => unit = "ariaColIndex" +@set external setAriaColIndexText: (t, Null.t) => unit = "ariaColIndexText" +@set external setAriaColSpan: (t, Null.t) => unit = "ariaColSpan" +@set external setAriaCurrent: (t, Null.t) => unit = "ariaCurrent" +@set external setAriaDescription: (t, Null.t) => unit = "ariaDescription" +@set external setAriaDisabled: (t, Null.t) => unit = "ariaDisabled" +@set external setAriaExpanded: (t, Null.t) => unit = "ariaExpanded" +@set external setAriaHasPopup: (t, Null.t) => unit = "ariaHasPopup" +@set external setAriaHidden: (t, Null.t) => unit = "ariaHidden" +@set external setAriaKeyShortcuts: (t, Null.t) => unit = "ariaKeyShortcuts" +@set external setAriaLabel: (t, Null.t) => unit = "ariaLabel" +@set external setAriaLevel: (t, Null.t) => unit = "ariaLevel" +@set external setAriaLive: (t, Null.t) => unit = "ariaLive" +@set external setAriaModal: (t, Null.t) => unit = "ariaModal" +@set external setAriaMultiLine: (t, Null.t) => unit = "ariaMultiLine" +@set external setAriaMultiSelectable: (t, Null.t) => unit = "ariaMultiSelectable" +@set external setAriaOrientation: (t, Null.t) => unit = "ariaOrientation" +@set external setAriaPlaceholder: (t, Null.t) => unit = "ariaPlaceholder" +@set external setAriaPosInSet: (t, Null.t) => unit = "ariaPosInSet" +@set external setAriaPressed: (t, Null.t) => unit = "ariaPressed" +@set external setAriaReadOnly: (t, Null.t) => unit = "ariaReadOnly" +@set external setAriaRequired: (t, Null.t) => unit = "ariaRequired" +@set external setAriaRoleDescription: (t, Null.t) => unit = "ariaRoleDescription" +@set external setAriaRowCount: (t, Null.t) => unit = "ariaRowCount" +@set external setAriaRowIndex: (t, Null.t) => unit = "ariaRowIndex" +@set external setAriaRowIndexText: (t, Null.t) => unit = "ariaRowIndexText" +@set external setAriaRowSpan: (t, Null.t) => unit = "ariaRowSpan" +@set external setAriaSelected: (t, Null.t) => unit = "ariaSelected" +@set external setAriaSetSize: (t, Null.t) => unit = "ariaSetSize" +@set external setAriaSort: (t, Null.t) => unit = "ariaSort" +@set external setAriaValueMax: (t, Null.t) => unit = "ariaValueMax" +@set external setAriaValueMin: (t, Null.t) => unit = "ariaValueMin" +@set external setAriaValueNow: (t, Null.t) => unit = "ariaValueNow" +@set external setAriaValueText: (t, Null.t) => unit = "ariaValueText" + /** Sets both the state and submission value of internals's target element to value. diff --git a/src/dom-nodes/ProcessingInstruction.res b/src/dom-nodes/ProcessingInstruction.res index 8084d0c5..97c75514 100644 --- a/src/dom-nodes/ProcessingInstruction.res +++ b/src/dom-nodes/ProcessingInstruction.res @@ -2,7 +2,7 @@ A processing instruction embeds application-specific instructions in XML which can be ignored by other applications that don't recognize them. [See ProcessingInstruction on MDN](https://developer.mozilla.org/docs/Web/API/ProcessingInstruction) */ -type t = { +type t = private { ...CharacterData.t, /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/ProcessingInstruction/target) diff --git a/src/dom-nodes/ShadowRoot.res b/src/dom-nodes/ShadowRoot.res index d7ce0cf1..d35c6972 100644 --- a/src/dom-nodes/ShadowRoot.res +++ b/src/dom-nodes/ShadowRoot.res @@ -4,6 +4,20 @@ type t = DOMTree.shadowRoot = private { include DocumentFragment.Impl({type t = t}) +/** +Sets the serialized HTML contents of this shadow root. +[Read more on MDN](https://developer.mozilla.org/docs/Web/API/ShadowRoot/innerHTML) +*/ +@throws(JsExn) @set +external setInnerHTML: (t, string) => unit = "innerHTML" + +/** +Sets the adopted style sheets for this shadow root. +[Read more on MDN](https://developer.mozilla.org/docs/Web/API/Document/adoptedStyleSheets) +*/ +@throws(JsExn) @set +external setAdoptedStyleSheets: (t, array) => unit = "adoptedStyleSheets" + /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/Document/getAnimations) */ diff --git a/src/dom-nodes/TreeWalker.res b/src/dom-nodes/TreeWalker.res index a5f00340..46f1c218 100644 --- a/src/dom-nodes/TreeWalker.res +++ b/src/dom-nodes/TreeWalker.res @@ -1,9 +1,8 @@ /** The nodes of a document subtree and a position within them. [See TreeWalker on MDN](https://developer.mozilla.org/docs/Web/API/TreeWalker) -TODO: mark as private once mutating fields of private records is allowed */ -type t = { +type t = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/TreeWalker/root) */ @@ -22,6 +21,9 @@ type t = { mutable currentNode: DOMTree.node, } +@set +external setCurrentNode: (t, DOMTree.node) => unit = "currentNode" + /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/TreeWalker/parentNode) */ diff --git a/src/dom-platform/DOMException.res b/src/dom-platform/DOMException.res index 216b546d..023b872d 100644 --- a/src/dom-platform/DOMException.res +++ b/src/dom-platform/DOMException.res @@ -1,4 +1,4 @@ -type t = DOM.domException +type t = DOM.domException = private {...DOM.domException} /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/DOMException) diff --git a/src/dom-platform/DOMStringList.res b/src/dom-platform/DOMStringList.res index 42fdaa5e..bcae05a6 100644 --- a/src/dom-platform/DOMStringList.res +++ b/src/dom-platform/DOMStringList.res @@ -1,4 +1,4 @@ -type t = DOM.domStringList +type t = DOM.domStringList = private {...DOM.domStringList} /** Returns the string with index index from strings. diff --git a/src/dom-platform/DOMTokenList.res b/src/dom-platform/DOMTokenList.res index 931e826e..f73d978c 100644 --- a/src/dom-platform/DOMTokenList.res +++ b/src/dom-platform/DOMTokenList.res @@ -1,4 +1,7 @@ -type t = DOM.domTokenList = {...DOM.domTokenList} +type t = DOM.domTokenList = private {...DOM.domTokenList} + +@set +external setValue: (t, string) => unit = "value" /** Returns the token with index index. diff --git a/src/html/HTMLAnchorElement.res b/src/html/HTMLAnchorElement.res index c5a79b50..e0993e90 100644 --- a/src/html/HTMLAnchorElement.res +++ b/src/html/HTMLAnchorElement.res @@ -3,7 +3,7 @@ Hyperlink elements and provides special properties and methods (beyond those of [See HTMLAnchorElement on MDN](https://developer.mozilla.org/docs/Web/API/HTMLAnchorElement) TODO: mark as private once mutating fields of private records is allowed */ -type t = { +type t = private { // Base properties from HTMLElement /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/HTMLElement/title) diff --git a/src/html/HTMLAreaElement.res b/src/html/HTMLAreaElement.res index 7f312888..bc965db6 100644 --- a/src/html/HTMLAreaElement.res +++ b/src/html/HTMLAreaElement.res @@ -3,7 +3,7 @@ Provides special properties and methods (beyond those of the regular object HTML [See HTMLAreaElement on MDN](https://developer.mozilla.org/docs/Web/API/HTMLAreaElement) TODO: mark as private once mutating fields of private records is allowed */ -type t = { +type t = private { // Base properties from HTMLElement /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/HTMLElement/title) diff --git a/src/html/HTMLBaseElement.res b/src/html/HTMLBaseElement.res index be86410d..823ac39b 100644 --- a/src/html/HTMLBaseElement.res +++ b/src/html/HTMLBaseElement.res @@ -3,7 +3,7 @@ Contains the base URI for a document. This object inherits all of the propertie [See HTMLBaseElement on MDN](https://developer.mozilla.org/docs/Web/API/HTMLBaseElement) TODO: mark as private once mutating fields of private records is allowed */ -type t = { +type t = private { ...DOMTree.htmlElement, /** Gets or sets the baseline WebApiURL on which relative links are based. diff --git a/src/html/HTMLDataElement.res b/src/html/HTMLDataElement.res index 22e82729..58953c39 100644 --- a/src/html/HTMLDataElement.res +++ b/src/html/HTMLDataElement.res @@ -3,7 +3,7 @@ Provides special properties (beyond the regular HTMLElement interface it also ha [See HTMLDataElement on MDN](https://developer.mozilla.org/docs/Web/API/HTMLDataElement) TODO: mark as private once mutating fields of private records is allowed */ -type t = { +type t = private { ...DOMTree.htmlElement, /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/HTMLDataElement/value) diff --git a/src/html/HTMLDialogElement.res b/src/html/HTMLDialogElement.res index 5dfd6049..19ace8fd 100644 --- a/src/html/HTMLDialogElement.res +++ b/src/html/HTMLDialogElement.res @@ -2,7 +2,7 @@ [See HTMLDialogElement on MDN](https://developer.mozilla.org/docs/Web/API/HTMLDialogElement) TODO: mark as private once mutating fields of private records is allowed */ -type t = { +type t = private { ...DOMTree.htmlElement, /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/HTMLDialogElement/open) diff --git a/src/html/HTMLElement.res b/src/html/HTMLElement.res index 4f436620..a089be74 100644 --- a/src/html/HTMLElement.res +++ b/src/html/HTMLElement.res @@ -43,6 +43,45 @@ This getter never returns `null`. @get external hidden: T.t => hiddenState = "hidden" + @set + external setTitle: (T.t, string) => unit = "title" + @set + external setLang: (T.t, string) => unit = "lang" + @set + external setTranslate: (T.t, bool) => unit = "translate" + @set + external setDir: (T.t, string) => unit = "dir" + @set + external setInert: (T.t, bool) => unit = "inert" + @set + external setAccessKey: (T.t, string) => unit = "accessKey" + @set + external setDraggable: (T.t, bool) => unit = "draggable" + @set + external setSpellcheck: (T.t, bool) => unit = "spellcheck" + @set + external setAutocapitalize: (T.t, string) => unit = "autocapitalize" + @set + external setInnerText: (T.t, string) => unit = "innerText" + @set + external setOuterText: (T.t, string) => unit = "outerText" + @set + external setPopover: (T.t, Null.t) => unit = "popover" + @set + external setContentEditable: (T.t, string) => unit = "contentEditable" + @set + external setEnterKeyHint: (T.t, string) => unit = "enterKeyHint" + @set + external setInputMode: (T.t, string) => unit = "inputMode" + @set + external setNonce: (T.t, string) => unit = "nonce" + @set + external setAutofocus: (T.t, bool) => unit = "autofocus" + @set + external setTabIndex: (T.t, int) => unit = "tabIndex" + @set + external setClassName: (T.t, string) => unit = "className" + /** Sets the element's semantic hidden state. diff --git a/src/html/HTMLEmbedElement.res b/src/html/HTMLEmbedElement.res index 7e440d20..4af0b9c5 100644 --- a/src/html/HTMLEmbedElement.res +++ b/src/html/HTMLEmbedElement.res @@ -3,7 +3,7 @@ Provides special properties (beyond the regular HTMLElement interface it also ha [See HTMLEmbedElement on MDN](https://developer.mozilla.org/docs/Web/API/HTMLEmbedElement) TODO: mark as private once mutating fields of private records is allowed */ -type t = { +type t = private { // Base properties from HTMLElement /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/HTMLElement/title) diff --git a/src/html/HTMLFieldSetElement.res b/src/html/HTMLFieldSetElement.res index 4a73c1b9..4d6d8184 100644 --- a/src/html/HTMLFieldSetElement.res +++ b/src/html/HTMLFieldSetElement.res @@ -3,7 +3,7 @@ Provides special properties and methods (beyond the regular HTMLElement interfac [See HTMLFieldSetElement on MDN](https://developer.mozilla.org/docs/Web/API/HTMLFieldSetElement) TODO: mark as private once mutating fields of private records is allowed */ -type t = { +type t = private { ...DOMTree.htmlElement, /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/HTMLFieldSetElement/disabled) diff --git a/src/html/HTMLFormElement.res b/src/html/HTMLFormElement.res index 0dc372c5..7b66cfd5 100644 --- a/src/html/HTMLFormElement.res +++ b/src/html/HTMLFormElement.res @@ -4,6 +4,23 @@ type t = DOMTree.htmlFormElement = private { include HTMLElement.Impl({type t = t}) +@set +external setAcceptCharset: (t, string) => unit = "acceptCharset" +@set +external setAction: (t, string) => unit = "action" +@set +external setAutocomplete: (t, DOM.autoFillBase) => unit = "autocomplete" +@set +external setEnctype: (t, string) => unit = "enctype" +@set +external setEncoding: (t, string) => unit = "encoding" +@set +external setMethod: (t, string) => unit = "method" +@set +external setName: (t, string) => unit = "name" +@set +external setTarget: (t, string) => unit = "target" + /** Fires when a FORM is about to be submitted. [Read more on MDN](https://developer.mozilla.org/docs/Web/API/HTMLFormElement/submit) diff --git a/src/html/HTMLIFrameElement.res b/src/html/HTMLIFrameElement.res index 39b47180..6ca4c2ae 100644 --- a/src/html/HTMLIFrameElement.res +++ b/src/html/HTMLIFrameElement.res @@ -3,7 +3,7 @@ Provides special properties and methods (beyond those of the HTMLElement interfa [See HTMLIFrameElement on MDN](https://developer.mozilla.org/docs/Web/API/HTMLIFrameElement) TODO: mark as private once mutating fields of private records is allowed */ -type t = { +type t = private { ...DOMTree.htmlElement, /** Sets or retrieves a WebApiURL to be loaded by the object. diff --git a/src/html/HTMLImageElement.res b/src/html/HTMLImageElement.res index 7e08a6bf..28586f7e 100644 --- a/src/html/HTMLImageElement.res +++ b/src/html/HTMLImageElement.res @@ -3,7 +3,7 @@ Provides special properties and methods for manipulating elements. [See HTMLImageElement on MDN](https://developer.mozilla.org/docs/Web/API/HTMLImageElement) TODO: mark as private once mutating fields of private records is allowed */ -type t = { +type t = private { // Base properties from HTMLElement /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/HTMLElement/title) diff --git a/src/html/HTMLInputElement.res b/src/html/HTMLInputElement.res index bee1478a..1908ab7f 100644 --- a/src/html/HTMLInputElement.res +++ b/src/html/HTMLInputElement.res @@ -3,7 +3,7 @@ Provides special properties and methods for manipulating the options, layout, an [See HTMLInputElement on MDN](https://developer.mozilla.org/docs/Web/API/HTMLInputElement) TODO: mark as private once mutating fields of private records is allowed */ -type t = { +type t = private { ...DOMTree.htmlElement, /** Sets or retrieves a comma-separated list of content types. diff --git a/src/html/HTMLLabelElement.res b/src/html/HTMLLabelElement.res index 60bfbe6f..449a349e 100644 --- a/src/html/HTMLLabelElement.res +++ b/src/html/HTMLLabelElement.res @@ -3,7 +3,7 @@ Gives access to properties specific to