Coder Social home page Coder Social logo

worker-dom's Introduction

WorkerDOM

An in-progress implementation of the DOM API intended to run within a Web Worker.

Purpose: Move complexity of intermediate work related to DOM mutations to a background thread, sending only the necessary manipulations to a foreground thread.

Use Cases:

  1. Embedded content from a third party living side by side with first party code.
  2. Mitigation of expensive rendering for content not requiring synchronous updates to user actions.
  3. Retaining main thread availablity for high priority updates by async updating elsewhere in a document.

For more information, visit our blog post announcing WorkerDOM or checkout the slides from the announcement at JSConf US.

Installation

npm install @ampproject/worker-dom

Usage

WorkerDOM comes in two flavours, a global variant and a module variant. It is possible to include the WorkerDOM main thread code within your document directly or via a bundler. Here's how you might do so directly:

<script src="path/to/workerdom/dist/main.mjs" type="module"></script>
<script src="path/to/workerdom/dist/main.js" nomodule defer></script>

WorkerDOM allows us to upgrade a specific section of the document to be driven by a worker. For example, imagine a div node in the page like so:

<div src="hello-world.js" id="upgrade-me"></div>

To upgrade this node using the module version of the code, we can directly import upgradeElement and use it like this:

<script type="module">
  import {upgradeElement} from './dist/main.mjs';
  upgradeElement(document.getElementById('upgrade-me'), './dist/worker/worker.mjs');
</script>

The nomodule format exposes the global MainThread, and could upgrade the div in the following way:

<script nomodule async=false defer>
  document.addEventListener('DOMContentLoaded', function() {
    MainThread.upgradeElement(document.getElementById('upgrade-me'), './dist/worker/worker.js');
  }, false);
</script>

AMP Distribution for amp-script

WorkerDOM has a special output variant that supplies additional hooks for safety features e.g. HTML sanitization. This variant is distributed under the amp folder for main and worker thread binaries:

amp/main.mjs
amp/worker/worker.mjs

This output assumes the consumer will compile this distributed JavaScript to ensure it works with older user-agents.

Debug Distribution

WorkerDOM also has an output variant that includes additional debugging messages. This variant is distributed in the debug folder.

debug/main.mjs
debug/main.js
debug/worker/worker.mjs
debug/worker/worker.js

Running Demos Locally

After cloning the repository, you can try out the debug demos with the following.

npm run demo

This script will build the current version of WorkerDOM and start up a local webserver.

Which JavaScript APIs can I use?

Currently, most DOM elements and their properties are supported. DOM query APIs like querySelector have partial support. Browser APIs like History are not implemented yet. Please see the API support matrix here.

Which Browsers are supported?

In general we support the latest two versions of major browsers like Chrome, Firefox, Edge, Safari, Opera and UC Browser. We support desktop, phone, tablet and the web view version of these respective browsers.

Beyond that, we aim for very wide browser support and we accept fixes for all browsers with market share greater than 1 percent.

In particular, we try to maintain "it might not be perfect but isn't broken"-support for IE 11, iOS 8, the Android 4.0 system browser and Chrome 41.

Local Development

Local development of WorkerDOM assumes the following:

  1. Familiarity with npm or yarn
  2. Latest LTS release of Node (10 at time of writing) available.
  3. Comfort with TypeScript, the codebase and tests are entirely written in TypeScript.

Release Log

Each release includes a log of changes with the newly released version. You can find the log here: https://github.com/ampproject/worker-dom/releases

Security disclosures

The AMP Project accepts responsible security disclosures through the Google Application Security program.

Code of conduct

The AMP Project strives for a positive and growing project community that provides a safe environment for everyone. All members, committers and volunteers in the community are required to act according to the code of conduct.

License

worker-dom is licensed under the Apache License, Version 2.0.

worker-dom's People

Contributors

andrei0x309 avatar cometkim avatar cramforce avatar ddenisyuk avatar delima02 avatar erwinmombay avatar flawyte avatar fstanis avatar honeybadgerdontcare avatar igorminar avatar jameslmilner avatar jridgewell avatar jtangelder avatar kristoferbaxter avatar morsssss avatar mrjoro avatar nainar avatar niutech avatar patrickkettner avatar powerivq avatar rcebulko avatar renovate-bot avatar renovate[bot] avatar rich-harris avatar samouri avatar torch2424 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

worker-dom's Issues

WebSocket support

Any concerns with adding support for the WebSocket API? If I understand correctly, all major browsers should support it inside workers and it allows for some interesting live use-cases.

Infinite recursion at CSSStyleDeclaration.getPropertyValue

Only reproducible at current HEAD (I think starting at #185). Not reproducible on 0.2.8.

A:

get(): string {
return this.getPropertyValue(hyphenatedKey);
},

B:

public getPropertyValue(key: string): string {
return this[TransferrableKeys.properties][key] || '';
}

A -> B -> A etc.

Results in error:

Uncaught RangeError: Maximum call stack size exceeded
    at CSSStyleDeclaration.getPropertyValue (...)
    at CSSStyleDeclaration.get (...)
    at CSSStyleDeclaration.getPropertyValue (...)
    at CSSStyleDeclaration.get (...)
    ...

Reproduced from author JS:

document.getElementById('hello').addEventListener('click', ()=>{
  const el = document.createElement('h1'); // Infinite recursion starts here on click.
  el.textContent = 'Hello World!';
  document.body.appendChild(el);
});

Stack trace:

getPropertyValue (/examples/amp-script/hello-world.js:formatted:1600)
get (/examples/amp-script/hello-world.js:formatted:1557)
setProperty (/examples/amp-script/hello-world.js:formatted:1627)
set (/examples/amp-script/hello-world.js:formatted:1560)
CSSStyleDeclaration (/examples/amp-script/hello-world.js:formatted:1571)
Element (/examples/amp-script/hello-world.js:formatted:1821)
HTMLElement (/examples/amp-script/hello-world.js:formatted:2226)
createElementNS (/examples/amp-script/hello-world.js:formatted:4610)
createElement (/examples/amp-script/hello-world.js:formatted:4605)
document.getElementById.addEventListener (/examples/amp-script/hello-world.js:formatted:4892)
dispatchEvent (/examples/amp-script/hello-world.js:formatted:815)
(anonymous) (/examples/amp-script/hello-world.js:formatted:4438)

Leveraging a detached Node created during Hydration fails

If you create a node before hydration, but it remains disconnected when hydration occurs, then the node is never available for use during mutation.

Example: replacer and its children are not known to the main thread when the click event requests their insertion. As a result, the DOM operation in the worker thread and main thread mismatch.

const comment = document.createComment('Test');
const el = document.createElement('div');
const replacer = document.createElement('p');

replacer.appendChild(document.createTextNode('replacer'));
el.appendChild(document.createTextNode('bar'));
el.classList.add('foo');
document.body.appendChild(comment);
document.body.appendChild(el);

let replaced = false;
el.addEventListener('click', function() {
  if (!replaced) {
    document.body.replaceChild(replacer, comment);
    replaced = true;
  }
});

APIs needed by lit-html

We would love to get lit-html working in worker-dom.

At a glance it appears we need these APIs that aren't implemented yet:

  • CustomElementRegistry#upgrade
  • Document#adoptNode
  • Document#createComment
  • Document#createTreeWalker
  • Document#customElements
  • Document#importNode
  • Element#innerHTML
  • HTMLTemplateElement#content

innerHTML is going to be difficult, since we use the parsed tree immediately after setting it.

Line breaks in initial DOM hoses hydration

No pun intended. :)

To repro, add a line break somewhere in the initial DOM of hello-world/index.html, e.g.

Works:

<div src="hello-world.js" id="upgrade-me"><div class="root"><button>Insert Hello World!</button></div></div>

Doesn't work:

  <div src="hello-world.js" id="upgrade-me">
    <div class="root"><button>Insert Hello World!</button></div>
  </div>

Breaks on this line:

// Syntax error due to line break in a single-quote string.
this.consumeInitialDOM(document, ['DIV','#text','undefined','
    ','BUTTON','Insert Hello World!','http://www.w3.org/1999/xhtml','null','class','root','
  ','src','hello-world.js','id','upgrade-me']

We could use backticks instead of single quote to support multiline strings, but probably just should just strip whitespace.

Question: Is DOM Batching implemented in worker-dom

Hello -

FastDom - another DOM project with origins inside Google, states the following regarding it's value proposition:

FastDom works as a regulatory layer between your app/library and the DOM. By batching DOM access we avoid unnecessary document reflows and dramatically speed up layout performance. Each measure/mutate job is added to a corresponding measure/mutate queue. The queues are emptied (reads, then writes) at the turn of the next frame using window.requestAnimationFrame.

Does worker-dom implement similar batching for actual DOM operations in the main thread (proxied from the worker). If not, would you please consider adding this, possibly even using FastDom.

Adding batching should theoretically not affect any API that worker-dom currently offers. Also, for the most part, batching would eliminate the need to include a virtual-dom implementation, and further benefit, performance wise, from decades of optimizations that browsers have implemented in handling batching.

Thanks

script elements

We need to design/decide what adding a script elements via worker-dom does. This may also need to be configurable.

Currently it would just do it and execute the script on the main thread. Which would be right for some use cases. But for other cases, such as when using code splitting, one might want to run the script inside the worker.

In that case, we may "neuter" the script element and instead use importScript to load the script. I believe that importScript is sync, so we may need to instead fetch the script and then call importScript with a blob (is that supported?)

Open questions

Hi guys, work you've done is awesome ๐Ÿ‘

I have couple questions, aslo about using it inside of big projects

  • do you have any plans to work on spec to have it in platform (of course not in near future)?
  • as far as I understood src in html el <div src="map-preact/dist/main.js" class="amp-script">... has to be aka path to all bundle (in case we bundling). How to include multiple chunks if we have code splitting?
  • any signals bundlers might include sources automatically?

How is memory management handled?

Forgive me using this to ask a question rather than report an issue.

I'm curious if there is anything in WorkerDOM that addresses the issue of leaking of DOM objects outlined in a similar project, via.js, here:

The main reason you wouldn't want to use Via.js is because currently every single DOM object you use from the worker is permanently leaked! This is because Via.js must maintain maps of object ID to the real object, so when it receives a command involving object ID 1 it knows how to find it. It's impossible to know how long the worker will hold on to these references because garbage collection is not observable in JavaScript. So the library has no way of ever removing objects from its ID map. Any time an object would normally be garbage collected, it will have one last reference in the ID map which will prevent it being collected.

The JavaScript WeakRefs proposal could solve this, since then it could map an ID to a weak reference, ensuring the value can be collected if it is the last reference. The WeakRef executor can then also remove the entry from the map.

Notes From Implementing WasmBoy in amp-script

Hello!

I was building a fairly hack-y / complex application using amp-script, Worker DOM, and WasmBoy. Here is a temporary demo site, before the demo and source code gets moved into the main WasmBoy repo.

screen shot 2018-11-30 at 12 57 46 pm

By @kristoferbaxter Here are some of my notes / issues I found during my implementation:

Notes / Issues from implementing:

  • Empty amp-script element isn't picked up by runtime. Requires a child element of at least 1x1 pixel size.
  • amp-script sanitizes raw <img> elements. Should provide a warning that <img> should be replaced with <amp-img>

"in developer mode, we should provide more amp specific information, or point to a URL or something."

  • No element.classList

  • Can't create <amp-image> with data uri. See ampproject/amphtml#19543

  • Errors when trying to modify elements already int the DOM

With the following html example

<amp-script>
  <div id="root">
    <amp-image>
  </div>
</amp-script>

For example, I think you should get c.replace is not a function when you do the following:

const someImage = document.getElementById('some-image');
someImage.src = 'https://avatars2.githubusercontent.com/u/1448289?s=88&v=4';

EDIT: There is no .src I can't code ๐Ÿ™ƒ

And I think you should get another error doing the following:

const newChild = document.createElement('div');
const someImage = document.getElementById('some-image');
someImage.appendChild(newChild);

EDIT: Tested this and it worked.

  • Attributes set using setAttribute('width', '10'), must be strings. Will not strinigify for you. For example

  • Query selector on elements is not working?

document.getElementById('some-div-with-img-child').querySelector('img');
  • Couldn't add event listener to button found with getElementById. Had to create button, add event listeners, and then append to body.

"This is a bug. Create an AMP Script Glitch. Seems to be caused by AMP Script adding a child image, and skewing the WorkerDOM Index.

Take the WorkerDOM Hello world, and try to reproduce with an amp-img"

  • Can't add events to the document.

  • Can't add keydown event to most elements. Only input. Though sometimes it would work on a div?

"This is a bug. See the previous amp-img discovery above"

  • No KrisCompiler Support.

Thanks! ๐Ÿ˜„

Release notes

Let's start publishing release notes in GitHub so users can keep track of breaking API changes like hydration inversion. We should keep it in sync with NPM, which doesn't have much info either.

Support CJS or ES6 export

Hit this while trying to integrate it into amphtml. E.g. in @ampproject/worker-dom/dist/index.safe.js:

Actual:

var MainThread = (function (exports) {
...

Expected:

module.exports = (function (exports) {
...

Or:

export const MainThread = (function (exports) {
...

Exporting can prevent name collisions with "MainThread" in caller code.

Running demo on Windows

Hello,
I have problem with running demo on Windows 10:

`> @ampproject/[email protected] demo F:\wd\worker-dom-master

node -r esm demo/server.mjs

file:///F:/wd/worker-dom-master/demo/server.mjs:1
TypeError: Path must be a string. Received undefined
at assertPath (path.js:28:11)
at Proxy.resolve (path.js:221:7)
at file:///F:/wd/worker-dom-master/demo/server.mjs:24:25`

What does safe mode do?

https://github.com/ampproject/worker-dom#safe-mode

"Safe" mode
WorkerDOM has a special output variant that includes safety features e.g. HTML sanitization and a web worker sandbox.

I believe HTML sanitization part is DOMPurify, but what is web worker sandbox? Is it WHITELISTED_GLOBALS?

And what does those intended to protect? And if possible, I'd like to also know what's not protected.

Thanks!

Bug: strings.getString() sometimes returns non-strings

I noticed this surprising type error when testing todomvc in amp-script. After tapping a checkbox, the worker sends the following mutation:

{
  nodeName: "INPUT",
  attribute: "checked",
  value: true,
}

Then, the value will be true (actual) instead of "true" (expected):

const value = mutation[TransferrableKeys.value] !== undefined ? getString(mutation[TransferrableKeys.value] as number) : null;
if (propertyName && value) {
if (!sanitizer || sanitizer.validProperty(target.nodeName, propertyName, value)) {
target[propertyName] = value;
} else {
// TODO(choumx): Inform worker?
}
}

Then, DOMPurify assumes attempts to call String.replace on a boolean and an exception is thrown.

Better DOM property support

A few issues I've noticed:

  • Boolean and enumerated attributes are not handled correctly (setting attribute value to String(property) is incorrect). Also need main-thread support for attribute removal (important for boolean attributes).
  • Some important properties are not implemented e.g. HTMLInputElement.value. Forwarding attribute mutations are insufficient for some of these properties.

One repro case:

const input = document.createElement('input');
input.setAttribute('type', 'text');
document.body.appendChild(input);

const btn = document.createElement('button');
btn.textContent = 'Clear input and toggle disabled'
btn.addEventListener('click', () => {
  input.value = '';
  input.disabled = !input.disabled;
});
document.body.appendChild(btn);
<input type="text"><button>prompt</button>

Canvas support

Canvas support would allow for many interesting use-cases, such as 3D previews for e-commerce and the various charting libraries.

It seems this could be done either via OffscreenCanvas or perhaps by treating method calls on CanvasRenderingContext2D as separate mutations (this would probably cause some minor delays, but it wouldn't be too big of an issue for use-cases like rendering a chart).

Testing: Verify mutation triggers in worker-thread

Suggestion: Element tests in src/test/ should verify that mutator.ts is invoked with the correct params. Refactoring mutator.ts from module-level vars into a class would help mockability in unit tests.

For example:

test('HTMLInputElement.value', t => {
  const {element, mutator} = t.context;
  element.value = 123;
  expect(mutator.mutate).calledWithMatch({type: PROPERTIES, value: 123});
});

Releasing on npm

I believe worker-dom 0.2.9 incorrectly ships with dompurify 1.0.9. Possibly caused by stale local node_modules/.

Possible solutions:

  • Script for npm releasing that runs yarn check as a prereq.
  • Use build artifacts from CI instead of building locally.

Docs: what APIs are not supported?

README should mention what is and is not supported.

Doing a quick poke around, things like document.querySelector and .innerHTML are not, but it would be nice to know the API surface that's covered.

Multiple upgraded elements causes breaks

Not sure if this currently allowed or not, but it seems calling upgradeElement on multiple elements can cause issues, presumably due to the state that is being shared between the upgraded components.

One example of this is the strings map here: https://github.com/ampproject/worker-dom/blob/master/src/main-thread/strings.ts#L18 which causes there to be an index-mismatch here https://github.com/ampproject/worker-dom/blob/master/src/main-thread/nodes.ts#L68

Repro (in the React map demo here: https://github.com/ampproject/worker-dom/blob/master/demo/react-map/index.html )
Modify the body to be

<body>
  <div src="dist/main.js" id="upgrade-me"></div>
  <div src="dist/main.js" id="upgrade-me-2"></div>
  <script type="module">
    import {upgradeElement} from '/dist/index.mjs';
    upgradeElement(document.getElementById('upgrade-me'), '/dist/worker.mjs');
    upgradeElement(document.getElementById('upgrade-me-2'), '/dist/worker.mjs');
  </script>
  <script nomodule async=false defer>
    document.addEventListener('DOMContentLoaded', function() {
      MainThread.upgradeElement(document.getElementById('upgrade-me'), '/dist/worker.js');
      MainThread.upgradeElement(document.getElementById('upgrade-me-2'), '/dist/worker.js');
    }, false);
  </script>
</body>

APIs needed by Aurelia (vNext)

First of all I would like to say excellent work on what has been accomplished so far. This looks really promising!

We would love to get Aurelia vNext working in worker-dom. The initial attempt to do so can be found here.

So far it seems the cloneNode API is missing. Is there any plan or work-in-progress to add this API?

Enable Windows Support with Travis or Appveyor

We'd like to enable testing of the build scripts on Windows machines via Travis. This will help ensure that changes made do not break developing on the library from Windows.

@rsimha has kindly agreed to take a look. Thank you Raghu!

Edit: Also investigating adding Appveyor testing for Windows environments.

window.location.search is undefined

Since bundled script (via webpack 4) is loaded in worker, I suppose, window.location.search is undefined

Here is webpack added stuff and last line crashes

'use strict';

/* global __resourceQuery WorkerGlobalScope self */
/* eslint prefer-destructuring: off */

var url = require('url');
var stripAnsi = require('strip-ansi');
var log = require('loglevel').getLogger('webpack-dev-server');
var socket = require('./socket');
var overlay = require('./overlay');

function getCurrentScriptSource() {
  // `document.currentScript` is the most accurate way to find the current script,
  // but is not supported in all browsers.
  if (document.currentScript) {
    return document.currentScript.getAttribute('src');
  }
  // Fall back to getting all scripts in the document.
  var scriptElements = document.scripts || [];
  var currentScript = scriptElements[scriptElements.length - 1];
  if (currentScript) {
    return currentScript.getAttribute('src');
  }
  // Fail as there was no script to use.
  throw new Error('[WDS] Failed to get current script source.');
}

var urlParts = void 0;
var hotReload = true;
if (typeof window !== 'undefined') {
  var qs = window.location.search.toLowerCase();

APIs needed by Vue

Not a comprehensive list. As of writing, just the ones I encountered when trying to get "hello world" example to work on Vue 2.6.5 (dev).

window.navigator.userAgent

Uncaught TypeError: Cannot read property 'userAgent' of undefined
var UA = inBrowser && window.navigator.userAgent.toLowerCase();

document.createEvent()

Uncaught TypeError: document.createEvent is not a function
// Determine what event timestamp the browser is using. Annoyingly, the
// timestamp can either be hi-res (relative to page load) or low-res
// (relative to UNIX epoch), so in order to compare time we have to use the
// same timestamp type when saving the flush timestamp.
if (inBrowser && getNow() > document.createEvent('Event').timeStamp) {

Set innerHTML

Uncaught TypeError: Cannot set property innerHTML of #<Element> which has only a getter
function getShouldDecode(href) {
    div = div || document.createElement('div');
    div.innerHTML = href ? "<a href=\"\n\"/>" : "<div a=\"\n\"/>";
    return div.innerHTML.indexOf('&#10;') > 0
}
var he = {
  decode: function decode (html) {
    decoder = decoder || document.createElement('div');
    decoder.innerHTML = html;
    return decoder.textContent
  }
};

new Vue()

Uncaught ReferenceError: Vue is not defined

Not really an API. Vue's global wrapper sets a property on workerDOM's custom this scope. Similar issue to our need to set var document = this.document; etc.

One workaround is to insert const Vue = this.Vue; between framework code and author code.

Debug: Warn on unsupported properties with Proxy

For debug mode only, it might be nice to add a JS proxy that warns when setting unsupported properties. E.g. with a short-link to new issue template for worker-dom.

Thinking about the easiest way to integrate this. If we go with the separate debugging module fetched at runtime, we'd need one for the worker and one for the main-thread. Maybe another output variant is justified? ๐Ÿค”

Library paths for demos are not pointing in the right place

Firstly thanks for this great library, top work! I just wanted to point out, the demos provided in the repository point at ./dist/index.mjs and ./dist/index.js respectively. The dist folder is actually the folder above, so to get any of the demos to work you have to change the URL to be ./../dist/index.js or the module equivalent.

Would you like me to raise a PR to fix this?

npm run debug fails on windows

Using powershell in windows 10.

PS C:\Users\Patrick\Desktop\worker-dom> npm run debug

> @ampproject/[email protected] predebug C:\Users\Patrick\Desktop\worker-dom
> DEBUG_BUNDLE=true npm run ~rollup

'DEBUG_BUNDLE' is not recognized as an internal or external command,
operable program or batch file.
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! @ampproject/[email protected] predebug: `DEBUG_BUNDLE=true npm run ~rollup`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the @ampproject/[email protected] predebug script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\Patrick\AppData\Roaming\npm-cache\_logs\2018-08-22T05_51_13_496Z-debug.log

I think npm run ~rollup --environment=DEBUG_BUNDLE would do the same thing in a cross-platform way w/o the need for any other tools.

Looks like the build task is gonna have the same problem. ๐Ÿ˜ž

Third parties loading

Just a question to early understand if this project can help with third parties loading. If I want to download a tag manager container, for instance, could the web worker completely assume control of loading all tags or I simply can't avoid it being done on the main thread?
Could I isolate third party loading on the web worker?

Getting error "Cannot read property 'setAttribute' of undefined"

This issue can be quite overwhelming, happened to me when trying to use worker-dom with a pretty large library (https://github.com/handsontable/handsontable).

Error stack:

Uncaught TypeError: Cannot read property 'setAttribute' of undefined
    at Object.0 (mutator.js:69)
    at MUTATION_QUEUE.forEach.mutation (mutator.js:134)
    at Array.forEach (<anonymous>)
    at syncFlush (mutator.js:133)
    at requestAnimationFrame (mutator.js:122)

Code in mutator.js (Line 69 highlighted with a comment):

[1 /* CHARACTER_DATA */](mutation, target) {
        const value = mutation[18 /* value */];
        if (value) {
            // Sanitization not necessary for textContent.
            target.textContent = getString(value);
        } // **Line 69**
    },

Seems that target is undefined. I added some debugging code to storeNodes and it never stores the node with id that is being requested. id requested is 28 & storeNodes only gets called starting with 68.

Global variables are not connected to the self and window objects

When running code with WorkerDOM, it appears that global variables don't show up on the self and window objects, and variables set on self or window can't be referenced as globals. This causes errors for code using Google Closure or ZoneJS libraries (and Angular, given its dependency on Zone).

For a small repro, try loading the following code via the main thread, then via a WorkerDOM-powered web worker:

console.log('Object is defined on self', !!self.Object);
console.log('Object is defined on window', !!window.Object);

window.Cat = 'Meow';
console.log('Set global variable Cat on the window', Cat);

self.Dog = 'Woof';
console.log('Set global variable Foo on the window', Dog);

Outside of a Web Worker, the script prints out the following console output:

Object is defined on self true
Object is defined on window true
Set global variable Cat on the window Meow
Set global variable Foo on the window Woof

When loaded with WorkerDOM, the code outputs:

Object is defined on self false
Object is defined on window false
Uncaught ReferenceError: Cat is not defined

Experiment with Transferring Node Position instead of Node Index

Right now the library maintains a Map<number, Element> on both sides of the bridge, additionally Elements are given a property index that corresponds to their number in the mapping. This allows both sides to easily reference Elements as they are created or modified.

However this creates a hard reference to the Element and means that these objects cannot be fully removed by GC.

If we move away from a mapping, and instead leverage the Elements position in the tree as its identifier, we are no longer holding a direct reference. This could mean GC is able to more easily remove these Element objects when they are no longer necessary.

However, this likely introduces other problems to solve like race conditions between the placement of nodes during events. We'll be exploring this option since it's the easiest to test.

Node ID mismatch between main-thread and worker-thread

Noticed this on 0.2.8 while testing amp-script using the minified worker binary.

Results in Uncaught TypeError: Cannot read property 'onchange' of undefined due to undefined target on this line:

let changeEventSubscribed: boolean = target.onchange !== null;

Root cause is consumeInitialDOM() doesn't use node IDs from the main-thread's skeleton:

export function create(document: Document, strings: Array<string>, skeleton: HydrateableNode): RenderableElement {
switch (skeleton[TransferrableKeys.nodeType]) {
case NodeType.TEXT_NODE:
const text = document.createTextNode(strings[skeleton[TransferrableKeys.textContent] as number]);
storeNode(text);
return text;
case NodeType.COMMENT_NODE:
const comment = document.createComment(strings[skeleton[TransferrableKeys.textContent] as number]);
storeNode(comment);
return comment;
default:
const namespace: string | undefined =
skeleton[TransferrableKeys.namespaceURI] !== undefined ? strings[skeleton[TransferrableKeys.namespaceURI] as number] : undefined;
const nodeName = strings[skeleton[TransferrableKeys.nodeName]];
const node: HTMLElement | SVGElement = namespace
? (document.createElementNS(namespace, nodeName) as SVGElement)
: document.createElement(nodeName);
(skeleton[TransferrableKeys.attributes] || []).forEach(attribute => {
const namespaceURI = strings[attribute[0]];
if (namespaceURI !== 'null') {
node.setAttributeNS(namespaceURI, strings[attribute[1]], strings[attribute[2]]);
} else {
node.setAttribute(strings[attribute[1]], strings[attribute[2]]);
}
});
storeNode(node);
(skeleton[TransferrableKeys.childNodes] || []).forEach(child => node.appendChild(create(document, strings, child)));
return node;
}
}

Potential fix: Pass skeleton[TransferrableKeys.index] into the storeNode() invocations in create() to ensure that IDs of hydrated nodes are consistent between main-thread and worker-thread.

#185 (unreleased) appears to fix my repro case but I believe the implicit ordering risk remains.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.