WorkerDOM

repository·main·Indexed 25 days ago

https://github.com/ampproject/worker-dom

A facsimile of a modern DOM implementation designed to run within a Web Worker. WorkerDOM allows developers to move complex DOM mutation logic to a background thread, sending only necessary manipulations to the main thread to improve performance and main-thread availability. It includes a custom HTML parser, support for retrieving bounding client rects, and a Babel linter plugin (@ampproject/worker-dom-linter) to validate DOM API usage.

Tokens
33.4K
Snippets
19
Records
66
Agent score
84%

What's inside @ampproject/worker-dom

  1. Overview of the internal HTML parser

    main

    The project includes a custom HTML parser written in TypeScript, based on node-html-parser. This version is specifically modified to work with the WorkerDOM model (/src/worker-thread/dom).

    Key modifications from the original node-html-parser include:

    • A replaced node element model to match the WorkerDOM implementation.
    • Removal of all previous node class declarations.
    • Removal of the CSS Matcher class.
    • Support for parsing HTML Comment nodes (which were previously ignored).
    • Decoding of numeric entities in text nodes.
  2. Upgrade a DOM element to be driven by a worker

    main

    WorkerDOM allows you to upgrade a specific section of the document (e.g., a div) to be controlled by a Web Worker. You can do this using either the module variant or the global variant.

    Using the Module Variant

    Import upgradeElement from the WorkerDOM distribution and pass the target element and the path to your worker script.

    Using the Global Variant

    If using the nomodule format, the MainThread object is exposed globally. You can call MainThread.upgradeElement(element, workerPath) once the DOM is loaded.

    <!-- Module Variant Example -->
    <div src="hello-world.js" id="upgrade-me"></div>
    
    <script type="module">
      import {upgradeElement} from './dist/main.mjs';
      upgradeElement(document.getElementById('upgrade-me'), './dist/worker/worker.mjs');
    </script>
    
    <!-- Global Variant Example -->
    <script nomodule async=false defer>
      document.addEventListener('DOMContentLoaded', function() {
        MainThread.upgradeElement(document.getElementById('upgrade-me'), './dist/worker/worker.js');
      }, false);
    </script>
  3. Compile WebAssembly using AssemblyScript

    main
    The WebAssembly (Wasm) source code for this example is written in AssemblyScript and located in index.ts. To generate the webassembly.wasm file required for the demo, you must compile the AssemblyScript source code using the AssemblyScript compiler. Refer to the official AssemblyScript documentation for specific compilation commands and output configurations.
  4. Handle false positives in @ampproject/worker-dom-linter

    main

    Because the linter lacks complete typing information, it may report false positives (e.g., flagging an offsetWidth property on a non-HTMLElement object). You can suppress these warnings by inserting a /*OK*/ comment into the expression.

    x./*OK*/offsetWidth
  5. Identify unsupported DOM APIs in WorkerDOM

    main

    When developing for WorkerDOM, be aware that certain Web APIs are not implemented (marked with ✖️ in the compatibility table). This includes many features related to Selection, SVGElement, Text, TreeWalker, URL methods, and various Window properties/methods. Using these unsupported APIs will cause errors in the WorkerDOM environment.

    Key categories of unsupported APIs include:

    • Selection API: Most methods like Selection.modify(), Selection.getRangeAt(), and Selection.selectAllChildren().
    • SVG Properties: Properties like SVGElement.viewportElement or SVGElement.xmlbase.
    • TreeWalker: Most traversal methods like TreeWalker.nextNode() or TreeWalker.parentNode().
    • Window/Global Scope: Methods like Window.alert(), Window.confirm(), Window.getComputedStyle(), and properties like Window.document or Window.history.
    • URL Management: Certain methods like URL.createObjectURL() and URL.revokeObjectURL().
    • Text/Encoding: Specific TextDecoder and TextEncoder properties/methods.
    | Selection.empty()                                   | ✖️     |                                                  |
    | Selection.extend()                                  | ✖️     |                                                  |
    | Selection.focusNode                                 | ✖️     |                                                  |
    | Selection.focusOffset                               | ✖️     |                                                  |
    | Selection.getRangeAt()                              | ✖️     |                                                  |
    | Selection.isCollapsed                               | ✖️     |                                                  |
    | Selection.modify()                                 | ✖️     |                                                  |
    | Selection.rangeCount                               | ✖️     |                                                  |
    | Selection.removeAllRanges()                         | ✖️     |                                                  |
    | Selection.removeRange()                            | ✖️     |                                                  |
    | Selection.selectAllChildren()                       | ✖️     |                                                  |
    | Selection.setBaseAndExtent()                        | ✖️     |                                                  |
    | Selection.setPosition()                             | ✖️     |                                                  |
    | Selection.toString()                                | ✖️     |                                                  |
    | Selection.type                                      | ✖️     |                                                  |
    | SVGElement.dataset                                  | ✖️     |                                                  |
    | SVGElement.id                                       | ✖️     |                                                  |
    | SVGElement.ownerSVGElement                          | ✖️     |                                                  |
    | SVGElement.viewportElement                          | ✖️     |                                                  |
    | SVGElement.xmlbase                                  | ✖️     |                                                  |
    | Text.assignedSlot                                   | ✖️     |                                                  |
    | Text.splitText                                      | ✖️     |                                                  |
    | Text.wholeText                                      | ✖️     |                                                  |
    | TextDecoder.decode()                                | ✖️     |                                                  |
    | TextDecoder.encoding                                | ✖️     |                                                  |
    | TextDecoder.fatal                                   | ✖️     |                                                  |
    | TextDecoder.ignoreBOM                               | ✖️     |                                                  |
    | TextEncoder.encode()                                | ✖️     |                                                  |
    | TextEncoder.encoding                                | ✖️     |                                                  |
    | TimeRanges.end()                                    | ✖️     |                                                  |
    | TimeRanges.length                                   | ✖️     |                                                  |
    | TimeRanges.start()                                  | ✖️     |                                                  |
    | TreeWalker.currentNode                              | ✖️     |                                                  |
    | TreeWalker.expandEntityReferences                   | ✖️     |                                                  |
    | TreeWalker.filter                                   | ✖️     |                                                  |
    | TreeWalker.firstChild()                             | ✖️     |                                                  |
    | TreeWalker.lastChild()                              | ✖️     |                                                  |
    | TreeWalker.nextNode()                               | ✖️     |                                                  |
    | TreeWalker.nextSibling()                            | ✖️     |                                                  |
    | TreeWalker.parentNode()                             | ✖️     |                                                  |
    | TreeWalker.previousNode()                           | ✖️     |                                                  |
    | TreeWalker.previousSibling()                        | ✖️     |                                                  |
    | TreeWalker.root                                     | ✖️     |                                                  |
    | TreeWalker.whatToShow                               | ✖️     |                                                  |
    | URL.createObjectURL()                               | ✖️     |                                                  |
    | URL.hash                                            | ✖️     |                                                  |
    | URL.host                                            | ✖️     |                                                  |
    | URL.hostname                                        | ✖️     |                                                  |
    | URL.href                                            | ✖️     |                                                  |
    | URL.origin                                          | ✖️     |                                                  |
    | URL.password                                        | ✖️     |                                                  |
    | URL.pathname                                        | ✖️     |                                                  |
    | URL.port                                            | ✖️     |                                                  |
    | URL.protocol                                        | ✖️     |                                                  |
    | URL.revokeObjectURL()                               | ✖️     |                                                  |
    | URL.search                                          | ✖️     |                                                  |
    | URL.searchParams                                    | ✖️     |                                                  |
    | URL.toJSON()                                        | ✖️     |                                                  |
    | URL.toString()                                      | ✖️     |                                                  |
    | URL.username                                        | ✖️     |                                                  |
    | Window.alert()                                      | ✖️     |                                                  |
    | Window.back()                                       | ✖️     |                                                  |
    | Window.blur()                                       | ✖️     |                                                  |
    | Window.cancelAnimationFrame()                       | ✖️     |                                                  |
    | Window.cancelIdleCallback()                         | ✖️     |                                                  |
    | Window.captureEvents()                              | ✖️     |                                                  |
    | Window.clearImmediate()                             | ✖️     |                                                  |
    | Window.close()                                      | ✖️     |                                                  |
    | Window.closed                                       | ✖️     |                                                  |
    | Window.confirm()                                    | ✖️     |                                                  |
    | Window.content and Window._content                  | ✖️     |                                                  |
    | Window.controllers                                  | ✖️     |                                                  |
    | Window.crypto                                       | ✖️     |                                                  |
    | Window.customElements                              | ✖️     |                                                  |
    | Window.devicePixelRatio                             | ✖️     |                                                  |
    | Window.directories                                  | ✖️     |                                                  |
    | Window.dispatchEvent()                              | ✖️     |                                                  |
    | Window.document                                     | ✖️     |                                                  |
    | Window.DOMMatrix                                    | ✖️     |                                                  |
    | Window.DOMMatrixReadOnly                            | ✖️     |                                                  |
    | Window.DOMPoint                                     | ✖️     |                                                  |
    | Window.DOMPointReadOnly                             | ✖️     |                                                  |
    | Window.DOMQuad                                      | ✖️     |                                                  |
    | Window.DOMRect                                      | ✖️     |                                                  |
    | Window.DOMRectReadOnly                              | ✖️     |                                                  |
    | Window.event                                        | ✖️     |                                                  |
    | Window.find()                                       | ✖️     |                                                  |
    | Window.focus()                                      | ✖️     |                                                  |
    | Window.forward()                                    | ✖️     |                                                  |
    | Window.frameElement                                 | ✖️     |                                                  |
    | Window.frames                                       | ✖️     |                                                  |
    | Window.fullScreen                                   | ✖️     |                                                  |
    | Window.getAttention()                               | ✖️     |                                                  |
    | Window.getAttentionWithCycleCount()                 | ✖️     |                                                  |
    | Window.getComputedStyle()                           | ✖️     |                                                  |
    | Window.getDefaultComputedStyle()                    | ✖️     |                                                  |
    | Window.getSelection()                               | ✖️     |                                                  |
    | Window.history                                      | ✖️     |                                                  |
    | Window.home()                                       | ✖️     |                                                  |
  6. Use the Debug Distribution

    main

    If you need additional debugging messages, use the variant located in the debug/ directory. This variant includes extra logging to assist in development.

    Files available:

    • debug/main.mjs / debug/main.js (Main thread)
    • debug/worker/worker.mjs / debug/worker/worker.js (Worker thread)
  7. Check WorkerDOM DOM API compatibility

    main

    When developing with WorkerDOM, be aware that not all standard Web APIs are implemented. The following table lists the compatibility status for specific properties and methods of HTMLImageElement, HTMLInputElement, HTMLLabelElement, HTMLLinkElement, HTMLMapElement, and HTMLMeterElement.

    ✔️ indicates the API is supported. ✖️ indicates the API is NOT supported.

    | HTMLImageElement.complete                           | ✖️     |                                                  |
    | HTMLImageElement.crossOrigin                        | ✔️     |                                                  |
    | HTMLImageElement.currentSrc                         | ✖️     |                                                  |
    | HTMLImageElement.decode()                            | ✖️     |                                                  |
    | HTMLImageElement.decoding                           | ✖️     |                                                  |
    | HTMLImageElement.height                             | ✔️     |                                                  |
    | HTMLImageElement.isMap                              | ✔️     |                                                  |
    | HTMLImageElement.naturalHeight                      | ✖️     |                                                  |
    | HTMLImageElement.naturalWidth                       | ✖️     |                                                  |
    | HTMLImageElement.referrerPolicy                     | ✖️     |                                                  |
    | HTMLImageElement.sizes                              | ✔️     |                                                  |
    | HTMLImageElement.src                                | ✔️     |                                                  |
    | HTMLImageElement.srcset                             | ✔️     |                                                  |
    | HTMLImageElement.useMap                             | ✔️     |                                                  |
    | HTMLImageElement.width                               | ✔️     |                                                  |
    | HTMLImageElement.x                                  | ✖️     |                                                  |
    | HTMLImageElement.y                                  | ✖️     |                                                  |
    | HTMLInputElement.accept                             | ✔️     |                                                  |
    | HTMLInputElement.accessKey                          | ✔️     |                                                  |
    | HTMLInputElement.allowdirs                          | ✖️     |                                                  |
    | HTMLInputElement.alt                                | ✔️     |                                                  |
    | HTMLInputElement.autocapitalize                     | ✔️     |                                                  |
    | HTMLInputElement.autocomplete                       | ✔️     |                                                  |
    | HTMLInputElement.autofocus                          | ✔️     |                                                  |
    | HTMLInputElement.blur()                             | ✔️     |                                                  |
    | HTMLInputElement.checked                            | ✖️     |                                                  |
    | HTMLInputElement.checkValidity()                    | ✔️     |                                                  |
    | HTMLInputElement.defaultChecked                      | ✔️     |                                                  |
    | HTMLInputElement.defaultValue                       | ✔️     |                                                  |
    | HTMLInputElement.dirName                           | ✔️     |                                                  |
    | HTMLInputElement.disabled                            | ✔️     |                                                  |
    | HTMLInputElement.files                               | ✖️     |                                                  |
    | HTMLInputElement.focus()                            | ✔️     |                                                  |
    | HTMLInputElement.form                               | ✔️     |                                                  |
    | HTMLInputElement.formAction                         | ✔️     |                                                  |
    | HTMLInputElement.formEncType                        | ✔️     |                                                  |
    | HTMLInputElement.formMethod                          | ✔️     |                                                  |
    | HTMLInputElement.formNoValidate                     | ✖️     |                                                  |
    | HTMLInputElement.formTarget                         | ✔️     |                                                  |
    | HTMLInputElement.height                             | ✔️     |                                                  |
    | HTMLInputElement.indeterminate                       | ✖️     |                                                  |
    | HTMLInputElement.labels                             | ✔️     |                                                  |
    | HTMLInputElement.list                               | ✔️     |                                                  |
    | HTMLInputElement.max                                | ✔️     |                                                  |
    | HTMLInputElement.maxLength                          | ✔️     |                                                  |
    | HTMLInputElement.min                                | ✔️     |                                                  |
    | HTMLInputElement.minLength                          | ✔️     |                                                  |
    | HTMLInputElement.multiple                           | ✔️     |                                                  |
    | HTMLInputElement.name                                | ✔️     |                                                  |
    | HTMLInputElement.pattern                            | ✔️     |                                                  |
    | HTMLInputElement.placeholder                        | ✔️     |                                                  |
    | HTMLInputElement.readOnly                           | ✖️     |                                                  |
    | HTMLInputElement.reportValidity()                   | ✔️     |                                                  |
    | HTMLInputElement.required                           | ✔️     |                                                  |
    | HTMLInputElement.select()                            | ✔️     |                                                  |
    | HTMLInputElement.selectionDirection                 | ✖️     |                                                  |
    | HTMLInputElement.selectionEnd                       | ✖️     |                                                  |
    | HTMLInputElement.selectionStart                     | ✖️     |                                                  |
    | HTMLInputElement.setCustomValidity()                | ✖️     |                                                  |
    | HTMLInputElement.setRangeText()                     | ✖️     |                                                  |
    | HTMLInputElement.setSelectionRange()                | ✖️     |                                                  |
    | HTMLInputElement.size                                | ✔️     |                                                  |
    | HTMLInputElement.src                                | ✔️     |                                                  |
    | HTMLInputElement.step                               | ✔️     |                                                  |
    | HTMLInputElement.stepDown()                         | ✖️     |                                                  |
    | HTMLInputElement.stepUp()                           | ✖️     |                                                  |
    | HTMLInputElement.type                                | ✔️     |                                                  |
    | HTMLInputElement.validationMessage                   | ✖️     |                                                  |
    | HTMLInputElement.validity                           | ✔️     |                                                  |
    | HTMLInputElement.value                              | ✔️     |                                                  |
    | HTMLInputElement.valueAsDate                        | ✔️     |                                                  |
    | HTMLInputElement.valueAsNumber                      | ✔️     |                                                  |
    | HTMLInputElement.webkitdirectory                    | ✖️     |                                                  |
    | HTMLInputElement.webkitEntries                      | ✖️     |                                                  |
    | HTMLInputElement.width                               | ✔️     |                                                  |
    | HTMLInputElement.willValidate                       | ✖️     |                                                  |
    | HTMLLabelElement.control                            | ✔️     |                                                  |
    | HTMLLabelElement.form                               | ✔️     |                                                  |
    | HTMLLabelElement.htmlFor                             | ✔️     |                                                  |
    | HTMLLinkElement.as                                  | ✔️     |                                                  |
    | HTMLLinkElement.crossOrigin                         | ✔️     |                                                  |
    | HTMLLinkElement.disabled                            | ✔️     |                                                  |
    | HTMLLinkElement.href                                | ✔️     |                                                  |
    | HTMLLinkElement.hreflang                            | ✔️     |                                                  |
    | HTMLLinkElement.media                               | ✔️     |                                                  |
    | HTMLLinkElement.referrerPolicy                      | ✔️     |                                                  |
    | HTMLLinkElement.rel                                 | ✖️     |                                                  |
    | HTMLLinkElement.relList                             | ✖️     |                                                  |
    | HTMLLinkElement.sizes                              | ✔️     |                                                  |
    | HTMLLinkElement.type                                | ✔️     |                                                  |
    | HTMLMapElement.areas                                | ✔️     |                                                  |
    | HTMLMapElement.name                                 | ✔️     |                                                  |
    | HTMLMeterElement.high                               | ✔️     |                                                  |
    | HTMLMeterElement.labels                             | ✔️     |                                                  |
    | HTMLMeterElement.low                                | ✔️     |                                                  |
    | HTMLMeterElement.max                                | ✔️     |                                                  |
    | HTMLMeterElement.min                                | ✔️     |                                                  |
    | HTMLMeterElement.optimum                           | ✔️     |                                                  |
    | HTMLMeterElement.values                             | ✔️     |                                                  |
  8. Use the AMP Distribution for `amp-script`

    main

    For use with amp-script, WorkerDOM provides a special output variant that includes additional hooks for safety features like HTML sanitization. These files are located in the amp/ directory:

    • amp/main.mjs (Main thread)
    • amp/worker/worker.mjs (Worker thread)

    Note: Consumers are expected to compile this distributed JavaScript to ensure compatibility with older user agents.