5

I have tried registering custom elements through native javascript and polymer from within a chrome extension with no luck. The issue is documented here.

Uncaught NotSupportedError: Failed to execute 'registerElement' on 'Document': Registration failed for type 'polymer-element'. Elements cannot be registered from extensions.

I am looking for suggestions for using web components in an extension - has anyone done so? My instinct is to register these elements using standard html tags (div, etc...) on the shadow DOM for the time being until it is supported. Any suggestions?

dipole_moment
  • 5,266
  • 4
  • 39
  • 55

3 Answers3

12

For me it was sufficient to use @webcomponents/custom-elements package in my content.js. Also webcomponents-sd-ce.js weights about 80kb and custom-elements.min.js only 16kb.

Just install it as a dependancy and import it in the content.js and let your package manager do the rest.

npm i @webcomponents/custom-elements
import '@webcomponents/custom-elements'

This is how I use it in my project popup-tab-switcher.


I believe that all these workarounds are not necessary and custom elements must work for extensions without polyfills. Please vote for the issue in Chrome bugs to draw attention to this flaw.

Dmitry Davydov
  • 987
  • 13
  • 21
  • 1
    I was about to write this answer but realized someone has already find this. Alternatively you could just copy/paste `./node_modules/@webcomponents/custom-elements/custom-elements.js` in the root folder of your extension and register it as a content script in your `manifest.json` – vdegenne Oct 01 '19 at 12:58
  • This worked great with my Chrome Extension! It was very easy, just npm install and import the polyfiill at the top of MyCustomElement class then everything with my custom element worked in my content script!! – RcoderNY Oct 12 '20 at 12:10
  • If you're not using npm in your extension, you can also download the polyfill file [from unpkg](https://unpkg.com/@webcomponents/custom-elements), and the source is available [on Github](https://github.com/webcomponents/polyfills/tree/master/packages/custom-elements). – eritbh Dec 17 '20 at 12:55
7

It is indeed possible. But, a little hacky.

Original Answer

Here are the steps:

  • Native web components (no Polymer)
    NOTE: This requires the usage of the webcomponentsjs polyfill
    Chrome will prevent native registerElement from being executed within a chrome extension. To overcome it you need to prevent the usage of the native registerElement in favor of a simulated one (the polyfill).

In webcomponents-lite.js Comment out the following code (and make this more elegant if you'd like:

scope.hasNative = false; //Boolean(document.registerElement);

And voila! the nativeregisterElement has now been replaced by the polyfill - which chrome does not prevent from using within an extension.

  • Polymer Web components
    You will need to perform the code listed in the step above and in addition, you will need to load polymer via the following code

      var link = document.createElement('link');
      link.setAttribute('rel', 'import');
      link.setAttribute('href', "link_to_your_polymer.html");
      link.onload = function() {
         // Execute polymer dependent code here
      }
    

UPDATE #1 - Improved solution

After further investigation, I've learned that Polymer would not work in an extension when loaded dynamically. As a result, the above modifications are necessary. A simpler alternative to this is to load polymer into the head in your content script like this:

  function loadRes(res) {
    return new Promise(
      function(resolve, reject) {
        var link = document.createElement('link');
        link.setAttribute('rel', 'import');
        link.setAttribute('href', res);
        link.onload = function() {
          resolve(res);
        };
        document.head.appendChild(link);
      });
  }

  loadRes( chrome.extension.getURL("path_to_your_webcomponents-lite.js") ) // You may/may not need webcomponents.js here
    .then( loadRes( chrome.extension.getURL("path_to_your_polymer.html") ) )
    .then( loadRes( chrome.extension.getURL("path_to_your_custome_component") ) )
    .then(function(){
      // code that depends on web components 
    });

YOU MUST add the urls loaded by chrome.extension.getURL to the web_accessible_resources portion of your manifest.

The above is necessary since polymer.html must be loaded through an html resource. Read Eric Bidelman's reply here

Please note that you may not need the webcomponents polyfill loaded here if your component already loads them.

dipole_moment
  • 5,266
  • 4
  • 39
  • 55
  • Is the polyfill really needed since you inject everything into the host page? – Ziyu Jun 17 '15 at 05:15
  • It may not be needed. I initially used the polyfill because ``registerElement`` was not allowed to be executed from a chrome extension when I was loading polymer through a content script. Now that I am loading these resources into ```` I believe you can discard the polyfill. – dipole_moment Jun 17 '15 at 13:05
  • I can't get it work with this code. Can you show me how my manifest should look like? My content_scripts is executed (tried it with ) but nothing renders in my popup.html so I think this file is not injected – Cilenco Feb 07 '16 at 14:46
  • 1
    I get the following error: Cannot read property 'appendChild' of null – Cilenco Feb 07 '16 at 15:14
  • You need to make sure each url is listed in web accessible resources. If not then chrome.extension.getUrl will return null which will lead to your error – dipole_moment Feb 07 '16 at 16:24
  • @Cilenco thanks for pointing that out, I edited my answer. Please take a look and try again. – dipole_moment Feb 07 '16 at 17:37
  • Okay that works fine now but I'm not sure what you are doing at `window.chromeApp = new ChromeApp(options);` Is that for your own code? I can not find any documentation about Windows.chromeApp – Cilenco Feb 07 '16 at 19:16
  • Yes. That's my own code. I'll change the answer to reduce confusion – dipole_moment Feb 07 '16 at 19:27
  • Is this still a valid work around? I can't seem to get it to work. `window.customElements` is null. – Jerinaw Mar 16 '17 at 22:24
3

Arrived at this problem in July 2017. The straightforward solution I found is to load the latest webcomponents polyfill for custom elements as an extra content script. No code changes necessary in the polyfill file (perhaps feature detection has been improved).

Extract from the manifest:

"content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["webcomponents-sd-ce.js", "contentscript.js"]
    }
]
Razvan Caliman
  • 4,509
  • 3
  • 21
  • 24
  • 1
    The link seems dead – Treycos Oct 30 '18 at 18:43
  • You can find `webcomponents-sd-ce.js` [here](https://github.com/oslego/chrome-extension-custom-element/blob/master/webcomponents-sd-ce.js). – Volodymyr Balytskyy Jan 06 '19 at 00:00
  • 4
    You actually can build it yourself. Clone [webcomponentsjs](https://github.com/webcomponents/webcomponentsjs) project. From root of the project run `npm install` and then `npm run build`. It will create a folder called `bundles`. In there you will find your file. @Treycos – Volodymyr Balytskyy Jan 06 '19 at 00:11