Reputation: 211
I'm having a really weird issue, I've developped a webextension that uses messaging between content script and background script (using chrome.runtime.connect
) and nativemessaging.
The issue i'm facing is that when I install the extension (manually from the store beforehand and then connect to my website, everything works as expected, the chrome.runtime.connect works and returns a valid port to the background script.
But when i do an inline install of the extension from my website to get around the fact to have to navigate to have the content script in the webpage, i manually inject the content script into my page using
function injectContentScript() {
var s = document.createElement("script");
s.setAttribute("src", "chrome-extension://<extensionid>/content.js");
document.head.appendChild(s);
}
and the exact same content script but manually injected doesn't behave the same. chrome.runtime.connect returns a null object and chrome.runtime.lastError gives me
Could not establish connection. Receiving end does not exist.
I'm calling on the sender side (content.js - manually injected content script) chrome.runtime.connect(extensionID)
where extension id is the id of the extension generated by the chrome webstore. And on the receiving side (background.js - extension background script) chrome.runtime.onConnect.addListener(onPortConnected);
I'm not really sure how to debug this issue, maybe it's a timing issue? The background script is well executed even with the inline install (i've added logs and debugged it through the background.html in chrome extension manager)
Any help would be greatly appreciated!
Upvotes: 2
Views: 253
Reputation: 77482
You have two scenarios.
Your content script content.js
is executed as normal upon navigation, as a content script defined in the manifest.
In this case, it executes in a special JS context attached to the page and reserved for your content scripts. See Execution Environment docs section for explanation. It is isolated from the webpage and is considered part of the extension (albeit with lower privileges).
When you connect from a content script, chrome.runtime.connect()
is treated as internal communication between parts of the extension. So while you can provide the extension ID, it is not needed.
More importantly, the event raised in this case is chrome.runtime.onConnect
.
Your supposed "inject content script immediately" code called from the webpage does something completely different.
Instead of creating a new execution context, the code is instead added directly to the page; it is not considered part of the extension and has no access to extension API.
Normally, a call to chrome.runtime.connect()
would simply fail, as this is not a function exposed to webpages; however, you also declared externally_connectable
, so it is exposed specifically to your webpage.
In this case, passing the extension ID is mandatory for the connect
. You were doing this already, so the call was succeeding.
However, and that's what made it fail: the corresponding event is no longer onConnect
, but onConnectExternal
!
What you should be doing is:
Not mixing code that is run in very different contexts.
If you need communication from the webpage to background, always do it from the webpage, not sometimes-from-content-sometimes-from-page.
That way you only have to listen to onConnectExternal
and it cuts out the need for a content script (if it was its only function).
See the docs as well: Sending messages from web pages.
You don't have to source the code from chrome-extension://<extensionid>/
; you can directly add this to your website's code and potentially avoid web_accessible_resources
.
And if you actually want to inject content scripts on first run, see for example this answer.
Related reading: How to properly handle chrome extension updates from content scripts
Upvotes: 3