How to inject content_script only if the devtools panel is open? - google-chrome-extension

I am trying to create a Chrome extension that adds a panel to the DevTools console.
The extension will have permission to interact with all urls, so to respect my users, I want to ensure that nothing is running when the devtools panel is closed.
The only thing the DevTool panel needs from the attached page (and all of its iframes) is to listen to a custom event dispatched on the respective window (could also be a postMessage)
window.addEventListener('my-custom-event', e => console.log(e.detail))
I understand that you cannot attach listeners directly from the DevTools panel onto the page/frames and you need a content_script to shuttle the messages forward (as described here).
The issue is, the content_script is always appended to the page regardless of whether the panel is open or not. It sets a listener and basically doesn't run unless the listener transfers a port to communicate on.
I would feel irresponsible knowing that my content script runs on every page, potentially impacting the performance of that page so I'd like them to only be attached to the page when the devtools panel is open and the page reloaded.
Ideally I would like to implement a "recording" button which, only when enabled, the content_script is added to the page. This is to avoid noise when doing performance profiling.
So my question is:
Can you inject content_script only when the DevTools panel is open and
Are you able to programmatically toggle content_script injection?

Related

Why am I not able to view event listeners added by a Chrome extension in DevTools' Event Listeners tab?

I am using Chrome 101 in Windows 10 and have a Chrome extension installed which does an addEventListener("mouseover"..) on every element on the page. I am unable to view these events in the Event Listeners tab in DevTools. This seems to be a bug according to this question. The page shows a workaround. However, the workaround doesn't show the event listener for me.
I switched the console to the extension's context. For several selected different elements and doing a getEventListeners($0), I always get an empty object as shown. I am not sure if this option got broken or what.
Are there any other ways to display these types of event listeners? The mouseover does work on the page and if I put a breakpoint on this event, the debugger stops in the extension's code (along with code from the page).

Chrome extension: How to do not lose state of my extension when user changes/reloads the page

I'm currently developing an extension that comes in different parts:
- background script (Chrome background.js
- content script (Chrome content-script.js)
- popup script (popup.js and loaded from the iFrame)
- iframe.html (iframe to be loaded when user activate the extension)
It works great except that I lose all the settings/context (used in the popup script to display info in the iframe) when the user change of page or reload the page in the tab.
Technically I want to isolate the extension per tab (each tab can have its own config) but when a user navigate in a same tab the context should be kept.
Question is: what is the best way to achieve that?
I'm thinking about persisting all the context in the content script and reload it when the event chrome.tabs.onUpdated is fired. Is it the right approach?
I recommend you to read about chrome.storage. You can store data there, both background and content script have access to it.
And probably you will need chrome.runtime.sendMessage and chrome.runtime.onMessage to have conversation between background and content. Each time content script runs in a tab it can request background whether this tab have some existing data or not.

Can Chrome Extension’s new options_ui trigger a page action?

I’ve been placing a page action on the options page of my Chrome extension. options.js calls chrome.runtime.connect({"name":"someName"}), and background.js has
chrome.runtime.onConnect.addListener(function(port) {
chrome.pageAction.show(port.sender.tab.id);
});
Unfortunately, in the new options_ui with the recommended default (and someday mandatory) "open_in_tab":false, the Sender's tab won't be set. Is there a way to get the tab id in order to show the page action?
I could use tabs.query to get the chrome://extensions/ tab, but that requires the tabs permission, which I currently don’t need. Active tab seems like it would work, but it doesn’t provide the tab id and isn’t enabled by opening an option dialog (source).
(Why do I want the page action on my options page? The extension works with a website that is only available ~7-10 weeks per year. I’d like my users to be able to interact with the extension the rest of the time, so that they can get used to the process. But I don’t want to adjust the displayed extension permissions just to do so. I can accomplish this by having the options page pretend to be the website in question.)

What can you do in background scripts that you can't do in other js files?

*By other js files, I mean the files you include in popup.html.
The following code works, so why should I use a background script?
Content script
chrome.extension.onMessage.addListener(
function(request, sender, sendResponse) {
//Some code
}
);
Script included in popup.html
chrome.tabs.query({active:true,windowId: chrome.windows.WINDOW_ID_CURRENT},
function(tab) {
chrome.tabs.sendMessage(tab[0].id, {method: "someMethod"},
function(response){
//Some code
});
});
This
Background pages live as long as the Chrome browser is active (and even longer if the "background" permission is set). Popup pages are only active when the badge's popup is open. The popup can only be opened by the user.
The background page's document is never visible, whilst the popup page becomes visible on click of the badge's button.
Besides that, there's no difference between background pages and popup pages. They run in the same extension's process, and have access to the same set of APIs.
If your extension only needs to be active while the popup is active, you don't need a background page. To save the state of the popup, just use the synchronous localStorage or the asynchronous chrome.storage API. When the variables you use are too complex to be stored using either API, a background page may be useful.
One example where the use of a background page is beneficial:
Imagine an extension that downloads a huge text file from a server. The creation of the resource is very resource-intensive for the server. Technically, everything can be done from within the popup. However, offloading the task to the background page allows the user to do other tasks while the file is downloading (if you use a popup only, the download will stop when the user closes the popup).
Though you didn't ask, I'd like to make you aware of event pages. They are similar to background pages, with one difference: Event pages are automatically closed when the extension is idle. In other words, event pages are only active when needed! By doing this, your extension will profit from the advantages of background pages without needlessly wasting the user's memory.
My last example is also a perfect example of when an event page has to be used. Besides doing the http request on behalf of the popup, the background page does nothing. If you use an event page instead of a background page, you get the best of both worlds: the popup page can be closed without interrupting the download, and the extension will not waste memory.
Documentation
Learn more about Background pages and Event pages
"Popup" in this answer refers to the optional panel of the chrome.browserAction or chrome.pageAction API, set by declaring the "default_popup" key in the manifest file, or programatically using the setPopup method.

Howto send a message from content-script to inactive popup, chrome extension

I want to log text on any webpage (using content-script to handle selection) into a database that is popup's resource in order to collect text in one place.
what i am trying
I create a database in popup page and try to manage it from content-script though popup is not active (not opened) by using chrome messaging but cannot make the popup receives any message from content-script.
I'm not sure about using messaging to solve this problem.
Is there any better solution?
A content script cannot send a message to an invisible popup, because the popup's context is inactive (closed) when it's hidden.
There are several solutions to your problem.
Option 1: No message passing, use storage events
If your "database" is in fact a simple key-value store, switch to the chrome.storage API. This API is available to the Content script and the popup, and comes with an event to notify you of value changes.
Example:
// Get notified of changes (in the popup?)
chrome.storage.onChanged.addListener(function(changes, areaName) {
// Do whatever you want with the changes.
});
// Initialization of the popup (print initial information?)
chrome.storage.local.get({keyName: 'defaultValue'}, function(items) {
// Do something with items.keyName
});
// Content script, storage (remember document title?)
chrome.storage.local.set({keyName: document.title});
Option 2: Pass messages to the background/event page
The popup and the background / event page share the same process. Any database tied to the popup is also available to the background page, and vice versa. A high-level overview of this method:
Content script sends a message to the background page.
The background page stores the value in the database
If the popup is open, update the popup's view.
If the popup is opened (so it was closed before), it should read the database (either directly, or by reading data from the background page using chrome.runtime.getBackgroundPage) and handle the results.
I've provided the code corresponding to this flow in this answer.

Resources