My team and I have created a Chrome Extension which bundles a PNaCl application to handle multimedia encoding & muxing which is adapted from the Pepper SDK (version 39) examples and the online SDK tutorial. The application's purpose to capture content from the user's desktop, tab, and webcam in order to create multimedia files.
The extension works as expected while it's visible but the PNaCl process is stopped/unloaded when the extension is hidden. I need to know what is the best strategy to persist a PNaCl process when the Chrome Extension is no longer visible.
The PNaCl app is embedded in my primary UI code (in my case this is set to index.html). The extension contains Background Pages which continues to process requests when hidden so I'm confident the manifest.json permissions and process work as expected. Additionally there are no exceptions.
So far I've attempted:
Make a background JavaScript page the interface to the PNaCl application so it's reference is stored in the background page which should persist.
Create a Chrome Window in order to persist PNaCl application and present a live-preview of the captured stream while the extension is hidden.
(Ongoing) Embed the PNaCl container a background HTML page rather primary HTML page which represents the extension UI.
So far none of them have persisted the PNaCl process.
The relevant parts of my manifest.json:
{
"manifest_version": 2,
"minimum_chrome_version": "39.0.0.0",
"offline_enabled": true,
"permissions":[
"desktopCapture",
"tabCapture",
"tabs",
"unlimitedStorage"
],
"browser_action":{
"default_popup": "index.html"
},
"background":{
"scripts": ["helpers.js", "background.js", "capture_state.js"],
"persistent": true
}
}
If I'm able to resolve before I get a response I'll respond with a solution.
We've resolved the issue despite the lack of documentation.
Here are the primary issues:
Issue #1 - Listener ID in front-end extension code.
We had our id="listener" inside of our front-end extension code (which is unloaded when the extension is hidden) and manually specifying a background.html page doesn't allow us to use background JavaScript files (a requirement of our application).
Resolution: We dynamically added the id="listener" and altered the common.js file using document.addElement to the generated background page (which is automatically created when the extension is loaded).
Issue #2 - getUserMedia method invoked in front-end extension code.
Our application requires that the streams returned from getUserMedia persist so that we can capture from the mic, webcam, and desktop in the background even if the extension is hidden.
Since we requested these in the front-end extension code (to create a live-preview) we thought we could simply pass them into the generated background page and they would persist.
What we found is that when the extension is hidden reading from the streams halts immediately without error. We found this out by digging through our code and monitoring the Chrome Task Manager to see that when the extension was hidden it's CPU usage goes to 0 but it's memory stays elevated (much like phenomena you'd expect when a thread deadlocks or is blocking).
Resolution: Moving the getUserMedia invocation to one of the background scripts and using the streams it returns resolves this.
If you create the PNaCl object in the popup window, which is supposed to be a throwaway destroyed whenever it loses focus, you won't be able to persist it.
It's not losing visibility, it's just completely unloaded.
Your "Ongoing" idea of putting the <embed> into the background page is probably correct.
Related
I have a very simple background page for a Chrome extension:
chrome.runtime.onInstalled.addListener((reason) => {
console.log(reason);
});
The background page runs when my extension is loaded:
The extension also has a popup that runs getBackgroundPage(), using:
const serviceWorkerWindow = await chrome.runtime.getBackgroundPage();
This fails with:
Uncaught (in promise) Error: You do not have a background page.
How do I make getBackgroundPage() work?
The getBackgroundPage() method doesn't retrieve the background script, it retrieves the window object associated with the background script. As you know, Manifest V3 doesn't use background scripts, it uses a service worker. Service workers by definition do not have access to window objects.
What is your use case? Maybe I could suggest an alternative approach.
Answering my own question based on #wOxxOm's comment. If wOxxOm writes their own answer I'll mark that as correct.
My expectation based on Google's documentation was that getBackgroundPage():
retrieves the JavaScript 'window' object for the background page running inside the current extension/app.
In modern JS terms, this would be the globalThis for the service worker (the service workers is still referred to as background in manifest v3.
As wOxxOm said.
This API won't work. The documentation is simply outdated because the ManifestV3 team consists of just a few devs who don't have time to do everything.
Essentially the Chrome Extension documentation is not adequately maintained. There is an outstanding W3C issue on the topic of whether getBackgroundPage should work with service workers - thanks #erosman. The best way to communicate between a popup and a background service worker is via extension messaging. Ie:
chrome.runtime.sendMessage()
And:
chrome.runtime.onMessage.addListener()
I'm trying to develop a simple Chrome extension. It just have to access to an object myVar from my web page and print it into a panel as a table. (I'm using Yeoman generator, so basically everything is there)
background.js // to add a runTime.onMessage listener
contentscript.js // to inject custom.js, add a listener to a custom event and fire the runTime.sendMessage
custom.js // to retrieve myVar and dispatch the custom event.
devtools.js // to create the extension Panel
devtools.html // contains just devtools.js
panel.html // basic html structure of my panel, no js.
So, what I was able to do is to inject a custom script into the web page and use event listeners and chrome messaging to pass MyVar.
What I'm missing is how to interact with the panel, I'm kind of lost.
Once it's initialized I don't know how to debug, access its DOM, communicate with background.js or contentscript.js.
Any ideas?
thank you!
Once it's [the panel] initialized I don't know how to debug, access its DOM, communicate with background.js or contentscript.js.
To debug it, you can detach the Dev Tools panel into a window and then invoke Dev Tools for that with Ctrl/Cmd + Shift + I.
It's a frame, so it's a self-contained document; to access its DOM you'll need a script in there.
To communicate, a devtools panel has access to chrome.runtime messaging functions to initiate communication with the background, even though it seems that the other way is impossible. It's usual to open a port with chrome.runtime.connect, with corresponding chrome.runtime.onConnect in the background script, and use it for two-way communication. Bonus, the sender object will contain the tab ID that DevTools is attached to.
Direct communication with a content script is impossible; you need to use the background as a proxy. Here's an old but thorough answer.
I'm writing a devtools Chrome extension with a dev panel. The devtools script can use chrome.devtools.inspectedWindow.reload to reload the page and run a script before other scripts run (in every frame in the inspected window). However, the injected script is no longer run for subsequent page reloads and newly created frames.
How can I inject a script into the inspected window that is run at the start of every subsequent page load in the inspected window?
I know I can use a content script that runs at document_start, but that would run the script at the start of each page load regardless of whether the dev panel is open - and the script is intensive, so I'd like to avoid running the script when it's not needed.
Could the devtools script somehow listen for the beginning of page loads in the inspected window and respond by running a script in the page's context?
One option that you can use to avoid running the script when it's not needed, as you have said, is programmatic injection. As discussed:
Inserting code into a page programmatically is useful when your JavaScript or CSS code shouldn't be injected into every single page that matches the pattern — for example, if you want a script to run only when the user clicks a browser action's icon.
To insert code into a page, you must have the following:
your extension must have cross-origin permissions for the page.
It also must be able to use the chrome.tabs module.
You can get both kinds of permission using the manifest file's permissions field.
Once you have permissions set up, you can inject JavaScript into a page by calling tabs.executeScript.
As also discussed in chrome.devtools.inspectedWindow in executing code in the inspected window, use the tabs.executeScript method unless you need the specific functionality that the eval method provides.
However in Communicating Between Extension Components, please note that:
The DevTools page can't call tabs.executeScript directly. To inject a content script from the DevTools page, you must retrieve the ID of the inspected window's tab using the inspectedWindow.tabId property and send a message to the background page. From the background page, call tabs.executeScript to inject the script.
You may go through the given documentations for more information and examples.
I am working on a Chrome extension that needs to call a native application.
As per Google documentation, we are using the chrome.runtime.connectNative method.
However in our extension, it seems that the chrome.runtime object has no method 'connectNative'. When we display the list of methods of the chrome.runtime object, we get the following list (printed by console.log("" + Object.getOwnPropertyNames(chrome.runtime));
getManifest,getURL,reload,requestUpdateCheck,connect,sendMessage,onConnect,onMessage,id
We are using Chrome 31.0.1650.63 on MacOS X 10.8.5 . We have also tried with Chrome Canary (version 34.0.1767.0 canary), we have the same error, with a slightly different list of methods for chrome.runtime:
getManifest,getURL,setUninstallUrl,reload,requestUpdateCheck,connect,sendMessage,onConnect,onMessage,id
So, in both cases (regular Chrome and Chrome Canary), we don't have the 'connectNative' method.
This does not seem to be a permissions problem, our extension manifest does have "nativeMessaging" in the permissions attribute. When we click on the permissions link in the Chrome extension settings, we can see that the extension can "communicate with cooperating native applications".
(sorry I couldn't post screenshots or the full manifest, StackOverflow won't let me paste things that even remotely look like I'm posting an image since I don't have enough reputation....)
Are we missing something ?
The list of properties of chrome.runtime you are getting indicates that your code is running as a content script. Most chrome.* APIs are not available to content scripts. They can only be used from background or event pages, popups, or other extension views you define. So you can use regular extension messaging from your content script to a background or event page, which in its turn can call your native app.
I am developing a chrome extension that uses jQuery/Zepto in the content script. Now, the extension is meant to run on every website, which means a copy of jQuery/Zepto is loaded on each tab the user opens.
Is there a way to share the jQuery/Zepto object between the various content scripts?
I know content scripts can communicate with the background script. I was hoping to be able to let the background script have access to the jQuery object and return a reference to it, to each of the content scripts. But I realized only JSON messages can be passed between content and background scripts.
Is there any way to achieve what I want?
Content scripts in different tabs do not have access to each other's JavaScript objects either.
Chrome supports communication between content scripts and/or the background page via chrome.runtime.sendMessage + .onMessage. Because all messages are JSON-serialized, JavaScript object cannot be "leaked" to other contexts in this way.
So: No, you cannot share objects such as jQuery with (content scripts in) other tabs.
Execution environment of Content Scripts ensure content scripts can communicate among themselves
Ex:
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["myscript.js","myscript1.js"]
}
]
}
A Individual DOM Environment where content scripts ["myscript.js","myscript1.js"] injected ensures myscript1.js have access to all contents (Functions,Variables) of myscript.js, but this stops from two Individual DOM Environment's being communicating.
Having said that, What Limitation\requirement you see in Content Scripts which calls for requirement where message passing needs background pages to access DOM of injected pages?
Please Elaborate