0

I created a custom Polymer element, and tried to use it in my Chrome extension. However, I ran into the following problem.

I can't access my element's API from the content script. Here is what I mean.

my-element's template

<link rel="import" href="../polymer/polymer.html">
<dom-module id="my-element">
<template>
... some html...
</template>
<script src="js/my-element.js"></script>
</dom-module>

in my-element.js

Polymer({
  is: 'my-element',
  init: function () {
    console.log('init');
  }
});

in content.js

var link = document.createElement('link');
link.setAttribute('rel', 'import');
link.setAttribute('href', chrome.extension.getURL('my-element.html'));
link.onload = function() {
  var elem = document.createElement('my-element');
  document.body.appendChild(elem);
  elem.init(); // Error: undefined is not a function
};
document.body.appendChild(link);

What is interesting it's that I can see that my-element is rendered properly on page, with all it's shadow dom and things. And If I run document.querySelector('my-element').init() in the host webpage's console, it is executed without errors. However, executing the same code inside the content script yields an error.

I tried to include Polymer itself as a content script. I ran into registerElement problem, but I solved it thanks to this question. In this case, I have window.Polymer defined in my content.js. My-element's API becomes fully accessible, but now it's shadow dom is not rendered when I append it to the host webpage's DOM.

Community
  • 1
  • 1
optimistiks
  • 491
  • 1
  • 5
  • 16
  • I don't think the browser follows `chrome-extension://` links on a normal http/https page due to sandboxing, at least such links don't work here as is. Have you exposed `my-element.html` via [web_accessible_resources](https://developer.chrome.com/extensions/manifest/web_accessible_resources) in manifest? – wOxxOm Jul 22 '15 at 11:39
  • Yes, I exposed `my-element.html` and all related css files in my manifest. The element is actually rendered and it's methods and properties declared with `Polymer({...})` are accessible with `document.querySelector()`, but only from the host webpage and not from content script. I think I should somehow imperatively declare `my-element` from within the content script, but I have no idea how. – optimistiks Jul 22 '15 at 13:12

1 Answers1

3

You're hitting the sandbox of your content script:

Content scripts execute in a special environment called an isolated world. They have access to the DOM of the page they are injected into, but not to any JavaScript variables or functions created by the page. It looks to each content script as if there is no other JavaScript executing on the page it is running on. The same is true in reverse: JavaScript running on the page cannot call any functions or access any variables defined by content scripts.

from https://developer.chrome.com/extensions/content_scripts#execution-environment

The script of your Polymer component runs in the host page, so it's invisible to your content script.

You can inject a new script tag into the host page, and source it from a .js file in your extension, e.g.

var script = document.createElement('<script>');
script.src = chrome.extension.getURL('something.js');
document.appendChild(script);

This will run something.js in the context of the host page, granting it access to all other scripts (including your Polymer component) in the host page, but also making it invisible to your content script. Please pay special attention to the security implications of injecting your script in the host page.

If you're not able/willing to push all your extension logic into that injected script, e.g. because you need access to chrome.* APIs or some other kind of communication between host page and content script, take a look at message posting.

Philipp Reichart
  • 20,771
  • 6
  • 58
  • 65
  • 1
    I was thinking about that. I finally decided to move to x-tag library. It has more clean imperative way of declaring elements, and one can easily plug in the library to the content script's scope, because it is shipped as a .js file. – optimistiks Jul 26 '15 at 19:55
  • This way I'm spared of moving all the logic from the content script to the injected host script, and of thinking about possible library conflict (like, what if Polymer is already included on the host page). – optimistiks Jul 26 '15 at 20:01