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
cloneNode on image element is not cloning some properties (e.g., width and height) #2360
Comments
I noticed it successfully copies either width or height if I set it explicitly:
It makes sense given the way cloneNode works: it creates an Image object and then copies all attributes that were explicitly set to the new node. I traced this issue to height -> naturalHeight. It is worth noticing the following comment in the height getter:
Which falls back to the naturalHeight getter, which returns this._image.height. However, this._image.height is 0.
I am more inclined to (2). I understand image support is not a traditional usage of jsdom and it might have those edge cases (if I may call it so) waiting to be discovered. |
It does seem to be a problem with setting the source of a new element equals to the one used previously, which would make me believe it is due to some cache (either intended or unintended). Using cloneNode: const { JSDOM } = require("jsdom");
const options = {
resources: 'usable'
};
const dom = new JSDOM('', options);
const impl = require('./node_modules/jsdom/lib/jsdom/living/generated/utils.js').implSymbol;
const img = new dom.window.Image();
img.onload = function() {
const newImg = this.cloneNode(true);
console.log(this[impl]._image);
console.log(newImg[impl]._image);
}
img.src = 'data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7'; Outputs: Using two different image objects with the same src: const { JSDOM } = require("jsdom");
const options = {
resources: 'usable'
};
const dom = new JSDOM('', options);
const impl = require('./node_modules/jsdom/lib/jsdom/living/generated/utils.js').implSymbol;
const img = new dom.window.Image();
const img2 = new dom.window.Image();
img.onload = function() {
console.log(this[impl]._image);
img2.src = 'data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7';
}
img2.onload = function() {
console.log(this[impl]._image);
}
img.src = 'data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7'; Outputs: Note: It happens disregards the Canvas backend used (canvas or canvas-prebuilt). |
Found it: This issue happens because cloneNode is not fully synchronous for images, given their load that is asynchronous. Running the following code proved it: const { JSDOM } = require("jsdom");
const options = {
resources: 'usable'
};
const dom = new JSDOM('', options);
const impl = require('./node_modules/jsdom/lib/jsdom/living/generated/utils.js').implSymbol;
const img = new dom.window.Image();
img.onload = function() {
console.log(this[impl]._image);
const newImg = this.cloneNode(true);
newImg.onload = () => console.log(newImg[impl]._image);
setTimeout(() => console.log(newImg[impl]._image), 100);
}
img.src = 'data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7'; Which outputs: Proving cloneNode works fully synchronous on Chrome is not hard: const img = new window.Image();
// Note it requires canvas support (canvas or canvas-prebuilt)
img.onload = function() {
const newImg = this.cloneNode(true);
console.log(this.width, this.height);
console.log(newImg.width, newImg.height);
while(1);
}
img.src = 'data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7'; Still displays the expected values. Gonna read the specs to check if it is somehow well defined there. |
As discussed in this Chromium issue, cloneNode should be synchronous. However, they cite the W3C specs and I could not find where the documentation defines it as synchronous. |
Basic info:
Minimal reproduction case
Running this code outputs:
How does similar code behave in browsers?
Tested on Google Chrome 67.0.3396.99 and 69.0.3497.81.
Also tested on Firefox Nightly, all presenting the same behaviour.
The text was updated successfully, but these errors were encountered: