Coder Social home page Coder Social logo

[Uncaught DOMException: Failed to execute 'registerElement' on 'Document'] when used in Chrome Extension about document-register-element HOT 16 CLOSED

BryceBeagle avatar BryceBeagle commented on August 18, 2024
[Uncaught DOMException: Failed to execute 'registerElement' on 'Document'] when used in Chrome Extension

from document-register-element.

Comments (16)

WebReflection avatar WebReflection commented on August 18, 2024

duplicating the answer for ungap/custom-elements-builtin#3 (comment)


@BryceBeagle everything is written in here:
https://github.com/WebReflection/document-register-element#how-to-polyfill-custom-elements-v1

If you have window.customElements already you don't need any polyfill to use this one, and this one is about using V1, where available, to extend V1 itself, specially on Safari, the only one left behind with built in extends.

Every browser that won't have customElements will never show a warning about anything.

If for some reason you cannot use that approach, you can var dre = require('document-register-elements/pony'); and then if (!window.customElements) dre(window);

In all cases, you won't have any warning.

from document-register-element.

BryceBeagle avatar BryceBeagle commented on August 18, 2024

I'm not sure I understand what you're saying, sorry. My scope does not natively have customElements defined, as you can see with the final error of my original comment above. My understanding is that this means I need to use a polyfill to get this functionality, right?

My issue was not simply with the deprecation warning that I get, but with the Uncaught DOMException that follows and leaves the custom element undefined. I left the deprecation warning in the output in the event that it was helpful.

I get this error only with this polyfill and not with the webcomponentsjs polyfill, so clearly something is different.

from document-register-element.

MikeVaz avatar MikeVaz commented on August 18, 2024

Hey @BryceBeagle. Your problem could be unrelated to web components and this polyfill.
What is the scope of your test.js file? console.log(this);
Maybe try it first in the browser directly without any chrome extension wrapping. This way you could narrow the scope of possible problems. ✌️

from document-register-element.

WebReflection avatar WebReflection commented on August 18, 2024

Oh gosh ... Is this really yet another jsdom thing ?

from document-register-element.

MikeVaz avatar MikeVaz commented on August 18, 2024

I suspect it something to do with the global object. It might not be window object in the context of extension API Bryce is using https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/executeScript.

Quick fix could be to use window.customElements instead of customElements

from document-register-element.

BryceBeagle avatar BryceBeagle commented on August 18, 2024

Unfortunately, using window.customElements has the same result for me. In my scope, this is defined to be window:

image

Also, customElements work just fine for me in the browser when not in the Extension, without the polyfill.

from document-register-element.

WebReflection avatar WebReflection commented on August 18, 2024

@BryceBeagle this page works for me https://webreflection.github.io/document-register-element/test/ so it's clear you have some third part obtrusive script that is causing you problems.

As reminder, the extension AdBlock Plus, with nearly 100 million users, ships with this polyfill already in both Chrome and Firefox but it's loaded only if needed as previously explained.

if (!window.customElements) require("document-register-element/pony")(window);

from document-register-element.

MikeVaz avatar MikeVaz commented on August 18, 2024

@BryceBeagle, it totally makes sense that some API will not be allowed for extensions. Especially the ones which could manipulate the DOM.

I won't be surprised if customElements is blocked.

I would clarify that first.

You can try moving define from your test.js

E.g. in you background.js

// chrome.tabs.executeScript(tab.id, {file: 'test.js'}); // content script. probably can't define custom elemenst.
// Instead try this below (copy from test.js to background.js)
class TestElement extends HTMLElement {}
customElements.define('test-element', TestElement);
document.body.appendChild(new TestElement);

Edit: modified example

from document-register-element.

WebReflection avatar WebReflection commented on August 18, 2024

true that we use the poly within the extension page we own, we don't try to pollute the global context that hosts our extensions thought. Is that what you are trying to do?

from document-register-element.

MikeVaz avatar MikeVaz commented on August 18, 2024

I need to learn more about extensions. But it looks like we have two different scopes.
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts

content scripts vs background scripts.

Content scripts have a lot of limitations and probably shouldn't be able to manipulate the DOM or define custom elements.

But @BryceBeagle can try defining elements in both scopes. Content script and Background script.

chrome.tabs.executeScript(tab.id, {file: 'test.js'}); test.js is a content script in this case. So it won't work.

Try background scripts instead.
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Anatomy_of_a_WebExtension#Background_scripts

from document-register-element.

WebReflection avatar WebReflection commented on August 18, 2024

Content scripts have a lot of limitations and probably shouldn't be able to manipulate the DOM or define custom elements.

the content script cannot change the user global context so this poly won't work for that but it also shouldn't, IMO

from document-register-element.

BryceBeagle avatar BryceBeagle commented on August 18, 2024

Thanks again for the responses. I can use customElements freely inside the background.js just fine, even without the polyfill, but that's not very helpful as I need to manipulate the DOM of the users' pages, not just a hidden background page.

I'm trying to inject my elements into a page for a user to use without having to resort to a popup/other page. Unfortunately background scripts are not able to manipulate the DOM properly and that really is the purpose of the content script system.

Content scripts have a lot of limitations and probably shouldn't be able to manipulate the DOM

My understanding is that this is exactly what content scripts are for. Background scripts and popup dialog scripts (I'm guessing this is where ABP uses customElements?) have no way of accessing the page's DOM. Only the content scripts can do that. Here's their definition from the Chrome Developer's page (emphasis mine):

Content scripts are files that run in the context of web pages. By using the standard Document Object Model (DOM), they are able to read details of the web pages the browser visits, make changes to them and pass information to their parent extension.

There should be a way to make this work. Again, webcomponentsjs functions perfectly for creating regular customElements that extend HTMLElement, but not for creating type extension custom elements (extending things like HTMLTableElement), which is why I'm trying to use the polyfill.

from document-register-element.

WebReflection avatar WebReflection commented on August 18, 2024

Your error literally says:

Elements cannot be registered from extensions.

meaning you cannot use V0 there or it throws. Since whenever customElements are not defined, V0 is used as fallback, which is probably something the other polyfill doesn't use.

There should be a way to make this work.

The content script is a facade of what the user sees. You can crawl the tree, you can even use MutationObserver, but you won't have direct access to the user global scope/context.

If content script doesn't expose customElements it's, I believe, because it could conflict with user customElements.

However, if the other polyfill works so well, it should be able to work with https://github.com/ungap/custom-elements-builtin but apparently it doesn't.

So how about you ditch custom elements as a whole, since inappropriate for this job, and give wickedElements a try instead, so you forget completely polyfills issues?

background.js

"use strict";
chrome.commands.onCommand.addListener(function (command) {
    if (command === "do_thing") {
        chrome.tabs.query({currentWindow: true, active: true}, function (tabs) {
            let tab = tabs[0];
            chrome.tabs.executeScript(tab.id, {file: 'node_modules/wicked-elements/min.js'});
            chrome.tabs.executeScript(tab.id, {file: 'test.js'});
        });
        return true;
    }
});

test.js

class TestElement {
  constructor() {
    return document.createElement('test-element');
  }
  onconnected() {
    console.log('here we go', this.el);
  }
}
wickedElements.define('test-element', TestElement);
document.body.appendChild(new TestElement);

That is just one way to go, you can forget classes all together too.

from document-register-element.

WebReflection avatar WebReflection commented on August 18, 2024

FYI I've just tested the following and it worked like a charm

wickedElements.define('body', {
  init(event) {
    this.el = event.currentTarget;
    console.log('wicked!', this.el);
  }
});

That means you can forget about Custom Elements limitations and their polyfill mess.

from document-register-element.

BryceBeagle avatar BryceBeagle commented on August 18, 2024

That first example with a class you provided does not seem to work. I get the following error using it:

min.js:1 Uncaught TypeError: e.init is not a function
    at min.js:1
    at HTMLElement.c (min.js:1)
    at C (min.js:1)
    at D (min.js:1)
    at k (min.js:1)
    at A (min.js:1)
    at MutationObserver.M (min.js:1)
(anonymous) @ min.js:1
c @ min.js:1
C @ min.js:1
D @ min.js:1
k @ min.js:1
A @ min.js:1
M @ min.js:1

from document-register-element.

WebReflection avatar WebReflection commented on August 18, 2024

That first example with a class you provided ...

I haven't provided any class, I've used a literal object.

With a class, you need to pass an init method (instead of a constructor), but that'd be another project matter, nothing to do here.

from document-register-element.

Related Issues (20)

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.