New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for WebComponents API #1030

Open
jimfb opened this Issue Feb 17, 2015 · 53 comments

Comments

Projects
None yet
@jimfb

jimfb commented Feb 17, 2015

We are trying to use jsdom for our unit tests for React core (http://facebook.github.io/react/).

Unfortunately, the web components spec is not natively supported by jsdom, and the webcomponents.js polyfill does not run on jsdom. This issue requests the addition of WebComponent support (custom elements, shadow dom, html imports, etc).

@Sebmaster Sebmaster added the feature label Feb 17, 2015

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Feb 17, 2015

Member

It'd be interesting to look into which APIs webcomponents.js uses that jsdom doesn't support. If I had to guess, that will be much easier to implement than the full web components spec.

That said, it would be pretty cool to implement web components. Probably not as hard as one might think---the specs are relatively small.

Member

domenic commented Feb 17, 2015

It'd be interesting to look into which APIs webcomponents.js uses that jsdom doesn't support. If I had to guess, that will be much easier to implement than the full web components spec.

That said, it would be pretty cool to implement web components. Probably not as hard as one might think---the specs are relatively small.

@Sebmaster

This comment has been minimized.

Show comment
Hide comment
@Sebmaster

Sebmaster Feb 18, 2015

Member

Just had time to dig into this a bit:

First off, we don't have Window defined in the window scope. I just patched this with this.Window = this.prototype in the Window constructor.
Second, webcomponentsjs expects Window to have another prototype, i.e. the EventTarget prototype, which we don't implement as a seperate entity.

Just a bit of info, because I had a bit of time.

Member

Sebmaster commented Feb 18, 2015

Just had time to dig into this a bit:

First off, we don't have Window defined in the window scope. I just patched this with this.Window = this.prototype in the Window constructor.
Second, webcomponentsjs expects Window to have another prototype, i.e. the EventTarget prototype, which we don't implement as a seperate entity.

Just a bit of info, because I had a bit of time.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Feb 18, 2015

Member

Nice. Should be able to expose Window pretty easily. EventTarget prototype is a bit trickier but seems doable given how we currently implement that stuff; it's been a TODO of mine.

Member

domenic commented Feb 18, 2015

Nice. Should be able to expose Window pretty easily. EventTarget prototype is a bit trickier but seems doable given how we currently implement that stuff; it's been a TODO of mine.

@Sebmaster

This comment has been minimized.

Show comment
Hide comment
@Sebmaster

Sebmaster Feb 18, 2015

Member

Okay, patches so far are rather easy:

  • this.Window = Window; in the Window constructor
  • inherits(dom.EventTarget, Window, dom.EventTarget.prototype); after the definition of Window

The next crash of webcomponents.js happens due to us not implementing HTMLUnknownElement (#1068), after shiming that we need to implement the SVGUseElement. That's what I'm currently blocked on, because webcomponents.js apparently doesn't like the SVGUseElement shimmed by a HTMLDivElement and throws in an assert.

Member

Sebmaster commented Feb 18, 2015

Okay, patches so far are rather easy:

  • this.Window = Window; in the Window constructor
  • inherits(dom.EventTarget, Window, dom.EventTarget.prototype); after the definition of Window

The next crash of webcomponents.js happens due to us not implementing HTMLUnknownElement (#1068), after shiming that we need to implement the SVGUseElement. That's what I'm currently blocked on, because webcomponents.js apparently doesn't like the SVGUseElement shimmed by a HTMLDivElement and throws in an assert.

@Sebmaster

This comment has been minimized.

Show comment
Hide comment
@Sebmaster

Sebmaster Mar 20, 2015

Member

Okay I checked into the Polyfill some more, we need to implement/you need to shim the following:

  • HTMLUnknownElement #1068
  • SVGUseElement
  • window.CanvasRenderingContext2D
  • Range APIs (including: document.getRange(), window.getSelection(), window.Range, window.Selection; #804 might be a start)
  • npm i canvas

(non-exhaustive list for now)

A start is something like the following:

jsdom.env({
  file: __dirname + '/index.htm', // refers to webcomponent.js
  created: function (err, window) {
    jsdom.getVirtualConsole(window).sendTo(console)

    window.document.createRange = function () { }
    window.getSelection = function () { }
    window.Range = function () { }
    window.Selection = function () { }
    window.CanvasRenderingContext2D = function () { } // Object.getPrototypeOf(require("canvas")(0,0).getContext("2d")) might be better
    window.SVGUseElement = window.HTMLUnknownElement
  },
  done: function (err, window) {
    console.log(err[0].data.error);
    console.log(window.CustomElements)
  },
  features: {
    ProcessExternalResources: ['script']
  }
});

That done, there's some bug in our HTMLDocument constructor, which leads to a maximum call stack error. The constructor is at the moment only for internal use, however it's valid that some script on the site makes calls to it so we need to make that constructor available for public consumption.

Member

Sebmaster commented Mar 20, 2015

Okay I checked into the Polyfill some more, we need to implement/you need to shim the following:

  • HTMLUnknownElement #1068
  • SVGUseElement
  • window.CanvasRenderingContext2D
  • Range APIs (including: document.getRange(), window.getSelection(), window.Range, window.Selection; #804 might be a start)
  • npm i canvas

(non-exhaustive list for now)

A start is something like the following:

jsdom.env({
  file: __dirname + '/index.htm', // refers to webcomponent.js
  created: function (err, window) {
    jsdom.getVirtualConsole(window).sendTo(console)

    window.document.createRange = function () { }
    window.getSelection = function () { }
    window.Range = function () { }
    window.Selection = function () { }
    window.CanvasRenderingContext2D = function () { } // Object.getPrototypeOf(require("canvas")(0,0).getContext("2d")) might be better
    window.SVGUseElement = window.HTMLUnknownElement
  },
  done: function (err, window) {
    console.log(err[0].data.error);
    console.log(window.CustomElements)
  },
  features: {
    ProcessExternalResources: ['script']
  }
});

That done, there's some bug in our HTMLDocument constructor, which leads to a maximum call stack error. The constructor is at the moment only for internal use, however it's valid that some script on the site makes calls to it so we need to make that constructor available for public consumption.

@bedeoverend

This comment has been minimized.

Show comment
Hide comment
@bedeoverend

bedeoverend Sep 14, 2015

+1 Would love to see WebComponents on jsdom, particularly as Polymer gains in popularity, would be great to be able to test custom elements on a headless system.

bedeoverend commented Sep 14, 2015

+1 Would love to see WebComponents on jsdom, particularly as Polymer gains in popularity, would be great to be able to test custom elements on a headless system.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Sep 14, 2015

Member

Right now there is no cross-browser definition of web components, so it'd be premature to implement. (We're not just going to copy Chrome.) In the meantime, you can try using Polymer with jsdom.

Member

domenic commented Sep 14, 2015

Right now there is no cross-browser definition of web components, so it'd be premature to implement. (We're not just going to copy Chrome.) In the meantime, you can try using Polymer with jsdom.

@bedeoverend

This comment has been minimized.

Show comment
Hide comment
@bedeoverend

bedeoverend Sep 14, 2015

@domenic fair enough. Well it's more the support for the WebComponents.js polyfill that I'm after, as that's what Polymer depends on - or webcomponents-lite (polyfills all of them barring Shadow DOM) at the moment. Made a few attempts to get Polymer working on jsdom, but no luck so far - I'm assuming @Sebmaster's tasks in the comment above will at least need to be patched first.

bedeoverend commented Sep 14, 2015

@domenic fair enough. Well it's more the support for the WebComponents.js polyfill that I'm after, as that's what Polymer depends on - or webcomponents-lite (polyfills all of them barring Shadow DOM) at the moment. Made a few attempts to get Polymer working on jsdom, but no luck so far - I'm assuming @Sebmaster's tasks in the comment above will at least need to be patched first.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Sep 14, 2015

Member

My understanding is that there are three separate polyfills in question. The one in the OP is separate from Polymer. Then there's the webcomponents.org polyfills, which used to be used in old-Polymer. Then in Polymer 1.0, they have their own polyfills, I think, which aren't really polyfills, but instead alternate libraries that do things kinda web-component-ish. Maybe that is webcomponents-lite though.

Member

domenic commented Sep 14, 2015

My understanding is that there are three separate polyfills in question. The one in the OP is separate from Polymer. Then there's the webcomponents.org polyfills, which used to be used in old-Polymer. Then in Polymer 1.0, they have their own polyfills, I think, which aren't really polyfills, but instead alternate libraries that do things kinda web-component-ish. Maybe that is webcomponents-lite though.

@bedeoverend

This comment has been minimized.

Show comment
Hide comment
@bedeoverend

bedeoverend Sep 14, 2015

On the WebComponentsJS repo, it says that the webcomponentsjs-lite is a variant, providing polyfills for all but Shadow DOM, which Polymer then independently attempts to shim using their Shady DOM system. So from that I'm pretty sure Polymer relies on WebComponents as much as it can, with the WebComponentsJS polyfill doing the grunt work. The lite version is supposed to be significantly less weight (funnily enough..) so I'll see if I can pinpoint what it is that jsdom needs for the lite version. What do you think the chances are of getting the polyfill (lite or full) working in jsdom is?

bedeoverend commented Sep 14, 2015

On the WebComponentsJS repo, it says that the webcomponentsjs-lite is a variant, providing polyfills for all but Shadow DOM, which Polymer then independently attempts to shim using their Shady DOM system. So from that I'm pretty sure Polymer relies on WebComponents as much as it can, with the WebComponentsJS polyfill doing the grunt work. The lite version is supposed to be significantly less weight (funnily enough..) so I'll see if I can pinpoint what it is that jsdom needs for the lite version. What do you think the chances are of getting the polyfill (lite or full) working in jsdom is?

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Sep 14, 2015

Member

It's really hard to say without some investigation... looking forward to what you find out.

Member

domenic commented Sep 14, 2015

It's really hard to say without some investigation... looking forward to what you find out.

@Sebmaster

This comment has been minimized.

Show comment
Hide comment
@Sebmaster

Sebmaster Sep 14, 2015

Member

Yeah, I think my list of todo tasks is still applicable and required to use the shims. Getting #1227 merged in might make us a lot quicker with implementing standards-compliant interfaces so we can fix the missing ones more quickly.

Member

Sebmaster commented Sep 14, 2015

Yeah, I think my list of todo tasks is still applicable and required to use the shims. Getting #1227 merged in might make us a lot quicker with implementing standards-compliant interfaces so we can fix the missing ones more quickly.

@matthewp

This comment has been minimized.

Show comment
Hide comment
@matthewp

matthewp Jun 15, 2016

Contributor

I've (probably naively) started working on adding CustomElementsRegistry as a way to understand how jsdom is structured. I added "custom-elements/custom-elements-registry/define.html" to the web platform tests list and it passes when it shouldn't (i haven't implemented nearly enough yet). I'm pretty sure the test isn't really running as even adding a throw at the top of the test won't prevent it from passing. So I've obviously missed something; aside from adding the test in test/web-platform-tests/index.js is there anything else I need to do?

Contributor

matthewp commented Jun 15, 2016

I've (probably naively) started working on adding CustomElementsRegistry as a way to understand how jsdom is structured. I added "custom-elements/custom-elements-registry/define.html" to the web platform tests list and it passes when it shouldn't (i haven't implemented nearly enough yet). I'm pretty sure the test isn't really running as even adding a throw at the top of the test won't prevent it from passing. So I've obviously missed something; aside from adding the test in test/web-platform-tests/index.js is there anything else I need to do?

@Sebmaster

This comment has been minimized.

Show comment
Hide comment
@Sebmaster

Sebmaster Jun 15, 2016

Member

Seems like that's caused because we fail in the initial const testWindow = iframe.contentDocument.defaultView; line because contentDocument is undefined for some reason. Might be an issue with our loading order vs. script execution, but haven't dug into that. Hope that helps you work around that. We might have to simplify the test for our purposes (for now).

Member

Sebmaster commented Jun 15, 2016

Seems like that's caused because we fail in the initial const testWindow = iframe.contentDocument.defaultView; line because contentDocument is undefined for some reason. Might be an issue with our loading order vs. script execution, but haven't dug into that. Hope that helps you work around that. We might have to simplify the test for our purposes (for now).

@matthewp

This comment has been minimized.

Show comment
Hide comment
@matthewp

matthewp Jun 15, 2016

Contributor

That helps very much, thanks! I'll see if I can figure out what is going on there, and if not I'll create a simplified test as you recommended.

Contributor

matthewp commented Jun 15, 2016

That helps very much, thanks! I'll see if I can figure out what is going on there, and if not I'll create a simplified test as you recommended.

@matthewp

This comment has been minimized.

Show comment
Hide comment
@matthewp

matthewp Jun 22, 2016

Contributor

@Sebmaster Just in case your interested, I did a bit of research into what is going on with that test and the results are surprising to me.

The test is using the named access feature of html. This means you can do stuff like:

<div id="foo"></div>
<script>
  console.log(window.foo === document.getElementById('foo'));
</script>

However, if the element has a nested browsing context, the global should point to that instead (see the linked spec). For iframe's that's the contentWindow. jsdom gets this right, there's even a test. Safari gets it right too.

What's crazy is that Chrome and Firefox get this wrong; the global points to the iframe, not it's contentWindow. Seeing this, I assumed it was a jsdom bug and did some hunting, eventually finding that test, which led me to the spec.

tldr; working on jsdom is very educational and you guys do an amazing job.

Going to file bugs in the respective browsers. Also will send a PR to web-platform-tests, I found some other mistakes in the test as well.

Contributor

matthewp commented Jun 22, 2016

@Sebmaster Just in case your interested, I did a bit of research into what is going on with that test and the results are surprising to me.

The test is using the named access feature of html. This means you can do stuff like:

<div id="foo"></div>
<script>
  console.log(window.foo === document.getElementById('foo'));
</script>

However, if the element has a nested browsing context, the global should point to that instead (see the linked spec). For iframe's that's the contentWindow. jsdom gets this right, there's even a test. Safari gets it right too.

What's crazy is that Chrome and Firefox get this wrong; the global points to the iframe, not it's contentWindow. Seeing this, I assumed it was a jsdom bug and did some hunting, eventually finding that test, which led me to the spec.

tldr; working on jsdom is very educational and you guys do an amazing job.

Going to file bugs in the respective browsers. Also will send a PR to web-platform-tests, I found some other mistakes in the test as well.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Jun 22, 2016

Member

This is even more motivation to upstream tests like https://github.com/tmpvar/jsdom/blob/master/test/living-html/named-properties-window.js to WPT. Thank you for posting! It makes me feel really great about jsdom ^_^

Member

domenic commented Jun 22, 2016

This is even more motivation to upstream tests like https://github.com/tmpvar/jsdom/blob/master/test/living-html/named-properties-window.js to WPT. Thank you for posting! It makes me feel really great about jsdom ^_^

@solkimicreb

This comment has been minimized.

Show comment
Hide comment
@solkimicreb

solkimicreb Jul 26, 2016

Hi!

I managed to make Custom Elements polyfill work with jsdom by combining

Note: the repo uses jsdom 8.5.0. The reason is that I only had success with a MutationObserver polyfill, that uses Mutation Events internally. Mutation Events were removed after 8.5.0 due to bad performance. If native Mutation Observer comes out I will remove the polyfill and update to the latest jsdom.

solkimicreb commented Jul 26, 2016

Hi!

I managed to make Custom Elements polyfill work with jsdom by combining

Note: the repo uses jsdom 8.5.0. The reason is that I only had success with a MutationObserver polyfill, that uses Mutation Events internally. Mutation Events were removed after 8.5.0 due to bad performance. If native Mutation Observer comes out I will remove the polyfill and update to the latest jsdom.

@lastmjs

This comment has been minimized.

Show comment
Hide comment
@lastmjs

lastmjs Nov 19, 2016

I've got the latest jsdom, and https://github.com/WebReflection/document-register-element is working for me! I've been experimenting with the more official polyfills, and I'm having trouble for some reason. My goal is to get at least custom elements and html imports to work...it would be awesome if we could get Polymer to work as well.

lastmjs commented Nov 19, 2016

I've got the latest jsdom, and https://github.com/WebReflection/document-register-element is working for me! I've been experimenting with the more official polyfills, and I'm having trouble for some reason. My goal is to get at least custom elements and html imports to work...it would be awesome if we could get Polymer to work as well.

@lastmjs

This comment has been minimized.

Show comment
Hide comment
@lastmjs

lastmjs Nov 19, 2016

I can get the Polymer scripts to run without error. I can even create a component and pass it to the Polymer constructor. After that it fails silently. I think shadow DOM is the issue.

I've been trying to get the webcomponentsjs HTML imports polyfill to work. I can get the script to run, and I believe my HTML imports execute an xmlhttprequest, but it doesn't seem like the scripts in my imports get run.

lastmjs commented Nov 19, 2016

I can get the Polymer scripts to run without error. I can even create a component and pass it to the Polymer constructor. After that it fails silently. I think shadow DOM is the issue.

I've been trying to get the webcomponentsjs HTML imports polyfill to work. I can get the script to run, and I believe my HTML imports execute an xmlhttprequest, but it doesn't seem like the scripts in my imports get run.

@snuggs

This comment has been minimized.

Show comment
Hide comment
@snuggs

snuggs Nov 21, 2016

Contributor

Care to share an example @lastmjs? I'm currently knee deep in web components myself. If I can be of help i'd gladly contribute with you.

Contributor

snuggs commented Nov 21, 2016

Care to share an example @lastmjs? I'm currently knee deep in web components myself. If I can be of help i'd gladly contribute with you.

@lastmjs

This comment has been minimized.

Show comment
Hide comment
@lastmjs

lastmjs Nov 22, 2016

@snuggs Thanks! Give me a day or two, I'm in the middle of some pressing things at the moment.

lastmjs commented Nov 22, 2016

@snuggs Thanks! Give me a day or two, I'm in the middle of some pressing things at the moment.

@sebs

This comment has been minimized.

Show comment
Hide comment
@sebs

sebs May 15, 2017

Awesome to see there is a way. As much as I like polymer, the test setu is hell and having jsdom as a fallback is nice ;) Thanks for putting the work in

sebs commented May 15, 2017

Awesome to see there is a way. As much as I like polymer, the test setu is hell and having jsdom as a fallback is nice ;) Thanks for putting the work in

@lastmjs

This comment has been minimized.

Show comment
Hide comment
@lastmjs

lastmjs May 29, 2017

Looks like there's a PR moving this forward! #1872

lastmjs commented May 29, 2017

Looks like there's a PR moving this forward! #1872

@snuggs

This comment has been minimized.

Show comment
Hide comment
@snuggs

snuggs Jun 27, 2017

Contributor

Actually, I mock the customElements, using the lib document-register-element @darlanmendonca

Should read this link about attaching jsdom globals to node global. It's an anti-pattern.

Contributor

snuggs commented Jun 27, 2017

Actually, I mock the customElements, using the lib document-register-element @darlanmendonca

Should read this link about attaching jsdom globals to node global. It's an anti-pattern.

@NicoleRauch

This comment has been minimized.

Show comment
Hide comment
@NicoleRauch

NicoleRauch Aug 8, 2017

Hello all,
I'm a bit confused regarding the status of running Polymer inside JSDOM (using Node.js 6.7.0 and JSDOM 11.1.0). I've tried various things, with mixed results. I'd be really grateful if somebody could fill me in here...

What I did so far:

1) I fired up a http server from my root directory

./node_modules/http-server/bin/http-server --cors

2) I loaded one of my Polymer components into JSDOM:

jsdom.JSDOM.fromURL("http://localhost:8080/path/to/my-component.html",
  { runScripts: "dangerously",
    resources: "usable"
  })
.then(function (dom) {
  setTimeout(() => {
    window = dom.window;
    component = window.document.querySelector("my-component");
  }, 10000);
})

(I also tried loading the component file from the file system, with the same results.)

3) This is my component code:

<!DOCTYPE html>
<html>
<head>
  <script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
</head>

<body>
<link rel="import" href="/bower_components/polymer/polymer.html">
<dom-module id="order-app">
  <template>
    <h1>Hello Polymer</h1>
  </template>

  <script>
    console.log("javascript is being executed");
    addEventListener('WebComponentsReady', function () {
      console.log("web components are ready");
      Polymer({
        is: 'order-app'
      });
    });
  </script>
</dom-module>
</body>
</html>

(I added the HTML head in order to load the webcomponents polyfill.)

What can I observe?

When I run this, I see

  • that the webcomponents polyfill is being loaded from the webserver
  • the message "javascript is being executed" in the console

What I don't see

  • that the polymer.html component is being loaded from the webserver
  • the message "web components are ready" in the console

This leads me to the conclusion that the WebComponentsReady event is not being fired (probably because the HTML import does not work?). Also,
window.WebComponents contains { flags: { log: {} } } -- the ready indicator is missing.

I also tried some mocking and polyfilling:

  window.Object = Object;
  window.Math = Math;
  require('document-register-element/pony')(window);

but that didn't seem to change anything.

Now, I'm wondering :-) Is this supposed to work at all? If so, why does it not work for me? If not, what is missing / required to get it to work?

Thanks for any insights!

NicoleRauch commented Aug 8, 2017

Hello all,
I'm a bit confused regarding the status of running Polymer inside JSDOM (using Node.js 6.7.0 and JSDOM 11.1.0). I've tried various things, with mixed results. I'd be really grateful if somebody could fill me in here...

What I did so far:

1) I fired up a http server from my root directory

./node_modules/http-server/bin/http-server --cors

2) I loaded one of my Polymer components into JSDOM:

jsdom.JSDOM.fromURL("http://localhost:8080/path/to/my-component.html",
  { runScripts: "dangerously",
    resources: "usable"
  })
.then(function (dom) {
  setTimeout(() => {
    window = dom.window;
    component = window.document.querySelector("my-component");
  }, 10000);
})

(I also tried loading the component file from the file system, with the same results.)

3) This is my component code:

<!DOCTYPE html>
<html>
<head>
  <script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
</head>

<body>
<link rel="import" href="/bower_components/polymer/polymer.html">
<dom-module id="order-app">
  <template>
    <h1>Hello Polymer</h1>
  </template>

  <script>
    console.log("javascript is being executed");
    addEventListener('WebComponentsReady', function () {
      console.log("web components are ready");
      Polymer({
        is: 'order-app'
      });
    });
  </script>
</dom-module>
</body>
</html>

(I added the HTML head in order to load the webcomponents polyfill.)

What can I observe?

When I run this, I see

  • that the webcomponents polyfill is being loaded from the webserver
  • the message "javascript is being executed" in the console

What I don't see

  • that the polymer.html component is being loaded from the webserver
  • the message "web components are ready" in the console

This leads me to the conclusion that the WebComponentsReady event is not being fired (probably because the HTML import does not work?). Also,
window.WebComponents contains { flags: { log: {} } } -- the ready indicator is missing.

I also tried some mocking and polyfilling:

  window.Object = Object;
  window.Math = Math;
  require('document-register-element/pony')(window);

but that didn't seem to change anything.

Now, I'm wondering :-) Is this supposed to work at all? If so, why does it not work for me? If not, what is missing / required to get it to work?

Thanks for any insights!

@sebs

This comment has been minimized.

Show comment
Hide comment
@sebs

sebs Aug 8, 2017

Moin

I even tried that with even less success and I gave up to wait what will be the result of this discussion here.

https://github.com/sebs/noframework/blob/master/test/configurator.js

not a solution just another failed attempt. Same confusion btw. Same conclusion as well

sebs commented Aug 8, 2017

Moin

I even tried that with even less success and I gave up to wait what will be the result of this discussion here.

https://github.com/sebs/noframework/blob/master/test/configurator.js

not a solution just another failed attempt. Same confusion btw. Same conclusion as well

@caridy

This comment has been minimized.

Show comment
Hide comment
@caridy

caridy Oct 3, 2017

Polyfilling custom elements in jsdom is proven to be very challenging. Can someone list the challenges of getting that implemented in jsdom? Trying to assess the level of effort to get that in.

caridy commented Oct 3, 2017

Polyfilling custom elements in jsdom is proven to be very challenging. Can someone list the challenges of getting that implemented in jsdom? Trying to assess the level of effort to get that in.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Oct 3, 2017

Member

The fundamental obstacle is that jsdom shares constructors and their prototypes.

This makes it basically impossible to implement a per-window custom elements registry, because the HTMLElement constructor is shared between all windows. So when you do the super() call in your custom element constructor, the now-running HTMLElement constructor doesn't know what window to look things up in. This sucks.

I'm not sure if there are any good intermediate solutions. The big gun is to move jsdom to an architecture that allows non-shared constructors/prototypes. We could do this in a few ways, all with different tradeoffs. Perhaps we'd want to open a dedicated issue to discussing it with the team and community, but for now let me list the ones that come to mind off the top of my head:

  • Use [WebIDL2JSFactory] for everything in jsdom, or at least HTMLElement and all its descendants. I'm not sure if [WebIDL2JSFactory] even works well with inheritance yet, but it could be made to work. This alternative causes everyone to pay the cost of extra constructors/prototypes, but maybe that is better than making custom elements an opt-in feature.
  • Have an option where jsdom runs all of the class definition modules inside of the vm sandbox. E.g. have some build step that bundles up all the web APIs in jsdom, then when you create a new window, we do vm.runScript() with that bundle inside the new sandbox global. This would probably allow us to get rid of [WebIDL2JSFactory].

I guess another solution would be to implement custom elements with a giant warning that the custom element registry is global per Node.js process? That seems terrible though.


After that initial hurdle, the rest is relatively straightforward in terms of following the spec. The hardest part will probably be implementing [CEReactions] and updating all our IDL files to have that in the appropriate places, but it's not too difficult.

Member

domenic commented Oct 3, 2017

The fundamental obstacle is that jsdom shares constructors and their prototypes.

This makes it basically impossible to implement a per-window custom elements registry, because the HTMLElement constructor is shared between all windows. So when you do the super() call in your custom element constructor, the now-running HTMLElement constructor doesn't know what window to look things up in. This sucks.

I'm not sure if there are any good intermediate solutions. The big gun is to move jsdom to an architecture that allows non-shared constructors/prototypes. We could do this in a few ways, all with different tradeoffs. Perhaps we'd want to open a dedicated issue to discussing it with the team and community, but for now let me list the ones that come to mind off the top of my head:

  • Use [WebIDL2JSFactory] for everything in jsdom, or at least HTMLElement and all its descendants. I'm not sure if [WebIDL2JSFactory] even works well with inheritance yet, but it could be made to work. This alternative causes everyone to pay the cost of extra constructors/prototypes, but maybe that is better than making custom elements an opt-in feature.
  • Have an option where jsdom runs all of the class definition modules inside of the vm sandbox. E.g. have some build step that bundles up all the web APIs in jsdom, then when you create a new window, we do vm.runScript() with that bundle inside the new sandbox global. This would probably allow us to get rid of [WebIDL2JSFactory].

I guess another solution would be to implement custom elements with a giant warning that the custom element registry is global per Node.js process? That seems terrible though.


After that initial hurdle, the rest is relatively straightforward in terms of following the spec. The hardest part will probably be implementing [CEReactions] and updating all our IDL files to have that in the appropriate places, but it's not too difficult.

@TimothyGu

This comment has been minimized.

Show comment
Hide comment
@TimothyGu

TimothyGu Oct 3, 2017

Member

I have been thinking about having a separate prototype version as well. Here are some of my thoughts.

I'm not sure if [WebIDL2JSFactory] even works well with inheritance yet, but it could be made to work.

No it does not, and I'm not sure how exactly to make it work. The second solution is much more straight-forward in my opinion.

Have an option where jsdom runs all of the class definition modules inside of the vm sandbox.

This is what I would prefer. The main problem is passing impl classes into the vm sandbox during initialization, though that can be done by putting everything from the outside context into one global property, and delete that global property after it is done. It would also allow properly implementing [NamedConstructor] and a couple of other extended attributes, and maybe even generating a V8 startup snapshot for a jsdom environment if someone is daring enough.

The whole [WebIDL2JSFactory] business was a hack in the first place, and I'd love to get rid of it as soon as possible.

Member

TimothyGu commented Oct 3, 2017

I have been thinking about having a separate prototype version as well. Here are some of my thoughts.

I'm not sure if [WebIDL2JSFactory] even works well with inheritance yet, but it could be made to work.

No it does not, and I'm not sure how exactly to make it work. The second solution is much more straight-forward in my opinion.

Have an option where jsdom runs all of the class definition modules inside of the vm sandbox.

This is what I would prefer. The main problem is passing impl classes into the vm sandbox during initialization, though that can be done by putting everything from the outside context into one global property, and delete that global property after it is done. It would also allow properly implementing [NamedConstructor] and a couple of other extended attributes, and maybe even generating a V8 startup snapshot for a jsdom environment if someone is daring enough.

The whole [WebIDL2JSFactory] business was a hack in the first place, and I'd love to get rid of it as soon as possible.

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Oct 10, 2017

Member

+1 comments are not helpful to developing this feature, so I'm deleting at least one recent one.

Member

domenic commented Oct 10, 2017

+1 comments are not helpful to developing this feature, so I'm deleting at least one recent one.

@mraerino

This comment has been minimized.

Show comment
Hide comment
@mraerino

mraerino Oct 26, 2017

Hi, I didn't realize @TimothyGu was working on this.
I actually have custom element registration & creation working at
https://github.com/mraerino/jsdom/tree/custom-elements-spec

I'm trying to be as minimally invasive as possible and as well to stay as close to the spec as possible.
Custom Element Registry web platform tests are passing.

While hacking on this last night I found a solution that works without modifying webIdl2JS.
See here: mraerino@592ad12

@TimothyGu would you be willing to combine forces on this one?

mraerino commented Oct 26, 2017

Hi, I didn't realize @TimothyGu was working on this.
I actually have custom element registration & creation working at
https://github.com/mraerino/jsdom/tree/custom-elements-spec

I'm trying to be as minimally invasive as possible and as well to stay as close to the spec as possible.
Custom Element Registry web platform tests are passing.

While hacking on this last night I found a solution that works without modifying webIdl2JS.
See here: mraerino@592ad12

@TimothyGu would you be willing to combine forces on this one?

@mraerino

This comment has been minimized.

Show comment
Hide comment
@mraerino

mraerino Oct 28, 2017

Just some updates here:
I am pretty confident about my implementation of the spec, but am currently stuck because of the [HTMLConstructor] extended IDL attribute. That's why I opened jsdom/webidl2js#87

In the meantime, I will implement the [HTMLConstructor] algorithm using a [Constructor] attribute to be able to easily switch later. (I initially implemented it by inserting a mock HTMLElement class into window, but this didn't seem right.)

mraerino commented Oct 28, 2017

Just some updates here:
I am pretty confident about my implementation of the spec, but am currently stuck because of the [HTMLConstructor] extended IDL attribute. That's why I opened jsdom/webidl2js#87

In the meantime, I will implement the [HTMLConstructor] algorithm using a [Constructor] attribute to be able to easily switch later. (I initially implemented it by inserting a mock HTMLElement class into window, but this didn't seem right.)

@domenic

This comment has been minimized.

Show comment
Hide comment
@domenic

domenic Oct 29, 2017

Member

Yeah, as noted in #1030 (comment), implementing HTMLConstructor correctly will require fundamental changes to jsdom's architecture.

Do you have any information on how many of the web platform tests your version is passing?

Member

domenic commented Oct 29, 2017

Yeah, as noted in #1030 (comment), implementing HTMLConstructor correctly will require fundamental changes to jsdom's architecture.

Do you have any information on how many of the web platform tests your version is passing?

@mraerino

This comment has been minimized.

Show comment
Hide comment
@mraerino

mraerino Oct 29, 2017

Just the customElementRegistry ones for now, and I could be totally wrong about my progress.

Edit: Ok, after re-reading your comment, I got what you mean. I'll try it with my implementation, but @TimothyGu also seems to be working on the separation.

mraerino commented Oct 29, 2017

Just the customElementRegistry ones for now, and I could be totally wrong about my progress.

Edit: Ok, after re-reading your comment, I got what you mean. I'll try it with my implementation, but @TimothyGu also seems to be working on the separation.

@dman777

This comment has been minimized.

Show comment
Hide comment
@dman777

dman777 Nov 11, 2017

I use Polymer so I am 👍 on this request feature

dman777 commented Nov 11, 2017

I use Polymer so I am 👍 on this request feature

@eavichay

This comment has been minimized.

Show comment
Hide comment
@eavichay

eavichay Mar 8, 2018

@dman777 @mraerino Same for slim.js developers. Slim uses native web components API and cannot inherit HTMLElement without hacks on jsdom.

eavichay commented Mar 8, 2018

@dman777 @mraerino Same for slim.js developers. Slim uses native web components API and cannot inherit HTMLElement without hacks on jsdom.

@PashaTurok

This comment has been minimized.

Show comment
Hide comment
@PashaTurok

PashaTurok Oct 17, 2018

Three years have passed since this issue was opened. Can anyone say when approximately jsdom will support custom elements?

PashaTurok commented Oct 17, 2018

Three years have passed since this issue was opened. Can anyone say when approximately jsdom will support custom elements?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment