Chrome extension, migrating from MV2 to MV3 service worker - google-chrome-extension

So I'm migrating from MV2 to MV3 and have quite an issue.
On MV2 I've used
"background": {
"page": [
"background.html"
],
},
On MV3, we have service worker, which accepts only .js files.
My background.html is being injected with webpack scripts. And was running then in the background obviously.
As for now, I'm unable to inject or load my scripts to background.js file. There are a lot of scripts and moving everything to background.js is just nonsense I think.
I've tried iframing my background.html into index.html (front-end), so it would run together, but then I'm facing Extension manifest must request permission to access this host error. Meaning I am unable to access chrome:// URL's.
Maybe are there any more options to somehow run background.html together with index.html and just keep service worker very simple?

Use in your 1 background script worker file the import to add your other JavaScript files:
import { mySecondFunction} from "./myscript2.js";
import { myThirdFunction} from "./myscript3.js";
import { myFourFunction} from "./myscript4.js";
Migrating from background pages to service workers
https://developer.chrome.com/docs/extensions/mv3/migrating_to_service_workers/

Related

How to import a js file in a Chrome extension service worker?

I am trying to use socket.io library in a service worker of y Chrome extension, but when I try to load it in Chrome I get the error: Uncaught SyntaxError: "Cannot use import statement outside a module".
I got the library from here, then I saved the it in my extension folder, in socketIo.js:
Then in my service_worker.js, I try to import it:
import io from './socketIo.js';
const socket = io("http://localhost:3002");
Since it as service worker, I do not attach it to my extension html file, therefore I can't simply set the worker as a module like this:
<script type="module" src="./service_worker.js"></script>.
Also, since extensions are not Node.js applications, they don't have a package.json where I could set the property "type": "module.
And as you can see I used both the relative path and the extension of the file imported, as suggested here: Chrome Extension Service Worker not Supporting Importing Other JS Files or NPM Packages
Now I've added the ES module version of socket.io and its "map" file.
And updated my service worker in manifest.json to be a ES module too:
But when I tried to load the extension again, I get this warning: "Service worker registration failed. Status code: 3", and the generic error:
Since it as service worker, I do not attach it to my extension html
file, therefore I can't simply set the worker as a module like this: .
Also, since extensions are not Node.js applications, they don't have a
package.json where I could set the property "type": "module.
Register the service worker
You can optionally specify an extra field of "type": "module" to
include the service worker as an ES Module, which allows you to import
further code.
manifest.json
{
"manifest_version": 3,
"name": "socket.io",
"version": "1.0",
"background": {
"service_worker": "background.js",
"type": "module"
}
}
But the socket.io version you're using isn't an ES Module:
Uncaught SyntaxError: The requested module '/socket.io.js' does not provide an export named 'default'
So I downloaded socket.io.esm.min.js and socket.io.esm.min.js.map from https://cdn.socket.io/4.5.3
I don't know if they have an ES Module version of socket.io 1.7.3
Including the .map file in the extension prevents this error in the service worker console:
DevTools failed to load source map: Could not load content for
chrome-extension://cfdinlnmiepoligakoinbffeaphkfmmp/socket.io.esm.min.js.map:
System error: net::ERR_FILE_NOT_FOUND
background.js
import io from "/socket.io.esm.min.js";
const socket = io("http://localhost:3002");
The service worker console still gets flooded with this error:
Event handler of 'beforeunload' event must be added on the initial evaluation
of worker script.
But I think there's a solution for that on Stackoverflow.
Or not:
Add Socket.io to service_worker (chrome extension)
Remote Socket.io Connection not working from a manifest v3 extension
Also, put the libraries socket.io.esm.min.js and socket.io.esm.min.js.map inside the same folder of service worker, in the same level (not in a sub folder).
After you made this changes, just refresh you extension in chrome://extensions/. But if this doesn't work, delete your extension and load it unpacked again.

Extension loading a content script triggers a warning when published to the chrome web store

I'd like to split my Chrome extension's script into several files. For this purpose, as far as I understand, I need to add a "content_scripts" property to the manifest file. When I publish to the Chrome Web Store, I get a warning message that my extension requires extensive permissions and that reviewing the extension will take more time, and that I'd better do it differently.
Originally, I had one single js file including data and functions. I don't like that, so I wanted to have separate js files for data and functions. So I took the data to a new data.js file and the only way I've found to make it work is to have a "content_script" property inside the manifest file.
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["data.js"]
}
],
The "matches" property seems to be required. My understanding is that this <all_url> value causes the Chrome Store to warn me that this is too much of a permission. They advise me to use "permissions": ["activeTab"] instead. But I already have that in my manifest. So I'm a bit confused as to how I could just add a content script without requiring additional permission, since it's just another embedded js file, and I don't see why that would require more permission than the original background javascript file.
Finally, I managed using several separate js files by referencing them from the background script :
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(tab.id, {file:"script1.js"});
chrome.tabs.executeScript(tab.id, {file:"script2.js"});
});
It works fine, and doesn't cause the Chrome Web Store to prompt a permission warning. So it seems a much better way for what I intended to do.

Unable to replace Alert in injected script

I'm writing an extension for this site. In my injected script(injected using chrome.tabs.executeScript) which runs at "document_end" everything seems to be working fine but I want to remove site's "alert" prompts.
To do I've placed this code in the injected script but it does not work:
window.alert = function(msg){
console.log("bypassed");
}
Also tried this from the injected script:
$('<script>window.alert = function (msg){}</' + 'script>').appendTo(document.body);
I've even tried this in the background page but this too doesn't:
chrome.tabs.executeScript({
code: 'window.alert=function(msg){}' //while alert("hello") works
});
What am I missing? I've written another extension for another site and this method does override the alert function.
UPDATE: I'm unable to call native functions in the downloaded web page from the injected script which will happen when called from content script. But my injected script is loaded using chrome.tabs.executeScript
I am guessing that your issue is with your manifest.json file, where you have specified to "run_at": "document_end".
From the documentation:
In the case of "document_start", the files are injected after any files from css, but before any other DOM is constructed or any other script is run.
but,
In the case of "document_end", the files are injected immediately after the DOM is complete, but before subresources like images and frames have loaded.
Referencing this answer, in the case of document_end, the overwrite cannot take place as the functions are already loaded, therefore you should have the javascript be injected at document_start.

Two-way messaging between contentscript.js and another js file in a Chrome extension

Apart from the usual files such as contentscript.js, background.js and so on, the Chrome extension I am building has a completely arbitrary file called app.js where most of the business logic is concentrated. I am organizing files like so to abstract most of the browser-independent logic into that app.js file and to reuse is for building extensions for other browsers.
I've declared that app.js file in manifest.json:
"background": {
"scripts": [
...,
"scripts/lib/app.js"
]
},
"content_scripts": [
{
"matches": [
"http://*/*",
"https://*/*"
],
"js": [
"scripts/lib/app.js",
"scripts/contentscript.js"
],
"run_at": "document_end",
"all_frames": false
}
]
Now, here comes the question. I can initiate communication between contenscript.js and app.js by running chrome.runtime.sendMessage or chrome.runtime.connect from contentscript.js (as described in the docs). But I couldn't figure out a good way for the opposite way of communication, namely how to send messages from app.js to contenscript.js.
Documentation discusses only sending messages from web pages, but that's not what I want to do. I want to send messages from one js file to another within the Chrome extension. chrome.runtime.sendMessage sent from app.js doesn't seem to register by contentscript.js.
UPDATE: Just to be clear, what I am asking about is how to establish communication between two or more js files declared in the "content_script" section of manifest.json. I can send messages from one file to the other (contenscript.js → app.js), but not from the second one to the first (app.js → contentscript.js or app.js → background.js). Which puzzles me, because app.js is declared in exactly the same section of manifest.json as contentscript.js is.
So, you have 2 scripts defined in the same content script "bundle" in the manifest.
This does not at all differ from just mashing the two scripts together into one .js file. They operate in the same context and share everything.
So, you can directly call functions and use variables from contentscript.js in app.js and vice versa within the same frame.
If you need to communicate across tabs (or frames within a tab), you'll need to use the background as a proxy. There is no direct function to send a message to another content script context.

Chrome extension: Could not load javascript file

I have posted another question regarding my Chrome extension here.
But I have one more question about extensions themselves. I only need a content script for the modification of the Tumblr-Dashboard, no background page or something else, right?
Here is the manifest.json file:
{
"name": "Tumblr - Tiled Dashboard",
"version": "0.0.54",
"manifest_version": 2,
"description": "This extension modifies the look of your Tumblr dashboard.",
"icons": {
"16": "images/icon_16.png",
"48": "images/icon_48.png",
"128": "images/icon_128.png"
},
"content_scripts": [
{
"matches": [ "*://*.tumblr.com/dashboard" ],
"css": [ "styles.css" ],
"js": [ "jquery-2.1.3.min.js", "masonry.min.js", "code.js" ]
}
],
"homepage_url": "mypage",
"author": "myname"
}
To start, I ask if this is alright? I have read a lot about the manifest.json file and everything seems to work fine when I try out the extension locally. But when I pack the extension and upload it, there are two problems:
I cannot find the extension when I search for it
When I use the link to find the extension, and I want to install it (tried that on 2 different PCs), I get an error, telling me that the jquery-2.1.3.min.js file could not be loaded. I therefore changed the order of my JavaScript files to test if it was a problem related to the jQuery file, but having masonry.min.js as the first file in the array resulted in the same error.
Why does this happen? Is the manifest.json file ok? Do I need some special permissions?
Edit:
This is a screenshot of when I try to install the extension from the Chrome Web Store (where I also can't find it by search).
I took a look inside your extension's ZIP file before downloading it, and the result was the following:
*Inspected using Chrome extension source viewer by Rob Wu
The problem here, is that you've uploaded a packed CRX file inside of your ZIP file, instead of your extension source code. You should instead upload a ZIP file containing your extension's root. Since that you're including the manifest.json file, the Web Store doesn't notice anything wrong until you try to install the extension, because the manifest is well written, but when Chromes tries to access the files declared, it fails and returns an error, because those files do not exist.
Quoting from the upload page of the Chrome Web Store Developer Dashboard:
Uploading an item:
Upload a ZIP file of your item directory, not a packaged CRX file.
Include a well-designed product icon in your manifest (more info).
Read the documentation about creating and packaging apps.
Need more help? Check out the Chrome Web Store developer documentation.
So, you should create a ZIP file of your extension's root directory, containing all the files of your extension. Your ZIP file should then look like the following:
I had the same "Could not load javascript file" error, then I noticed that my_custom_script.js file was not builded in dist directory (I'm using npm). You can move it manually and try to reload plugin to check is this problem.
Solution to me was adding new entry to webpack.config.js like this:
entry: {
'my_custom_script': './my_custom_script.js',
'background': './background.js',
'popup/popup': './popup/popup.js',
'options/options': './options/options.js',
},

Resources