I am developing chrome extension with manifest v3. I tried the below code to register service worker as in many resources, I found that
[Service Worker Registration] Registered extension scheme to allow service workers
Service Workers require a secure origin, such as HTTPS.
chrome-extension:// pages are not HTTP/HTTPS, but are secure so this change
becomes a necessary step to allow extensions to register a Service Worker.
"chrome-extension" is added as a scheme allowing service workers.
but after using the below code also, service worker is not working, and background.js file is not working due to this.
Scope URL:
chrome-extension://ifcfmjiflkcjbbddpbeccmkjhhimbphj/trigger/trigger.html
serviceWorker.js
self.addEventListener('load', function () {
navigator.serviceWorker.register('./background.js').then(function (registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}, function (err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
});
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open('v1').then((cache) => {
return cache.addAll([
]);
})
);
});
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((resp) => {
return resp || fetch(event.request).then((response) => {
return caches.open('v1').then((cache) => {
// cache.put(event.request, response.clone());
return response;
});
});
})
);
});
self.addEventListener('activate', (event) => {
var cacheKeeplist = ['v2'];
event.waitUntil(
caches.keys().then((keyList) => {
return Promise.all(keyList.map((key) => {
if (cacheKeeplist.indexOf(key) === -1) {
return caches.delete(key);
}
}));
})
);
});
manifest.json
{
"manifest_version": 3,
"name": "TestExtension",
"description": "This extension is for testing purpose only",
"version": "0.0.1",
"background":{
"service_worker":"./serviceWorker.js"
},
"declarative_net_request": {
"rule_resources": [{
"id": "ruleset_1",
"enabled": true,
"path": "rules.json"
}]
},
"permissions":["storage", "tabs", "activeTab", "scripting", "declarativeNetRequest", "declarativeNetRequestFeedback","browsingData","contextMenus"],
"host_permissions":["<all_urls>"],
"web_accessible_resources": [
{
"resources": [ "css/notification.css" ],
"matches": [ "<all_urls>" ]
}
],
"content_scripts":[
{
"js":["content-scripts/content.js"],
"matches":["<all_urls>"],
"all_frames":true,
"exclude_matches":["*://extensions/*"],
"run_at":"document_end"
}
],
"icons":{
"16":"assets/baby-logo-black-icon.png",
"48":"assets/baby-logo-black-icon.png",
"128":"assets/baby-logo-black-icon.png"
},
"action":{
"default_title":"TestExtension",
"default_popup":"trigger/trigger.html"
}
}
anyone please help me and guide me on this whether I have missed anything in serviceWorker.js or manifest.json? or made any mistake on this? Kindly help me to make serviceWorker.js and background.js to work.
Note: With the above code, I am not getting any errors on serviceWorker.js, and its not working also.
There's no need to register anything.
The browser does it automatically when you install the extension.
Remove the registration code and serviceWorker.js.
Use "service_worker": "background.js" in manifest.json
Related
In short, I want that when you press my extension button from the context menu, the content script will be added to the web page temporarily. I tried to use sendMessage() but it just didn't work.
Any help will be appreciated:)
//This is the service worker (eventPage)
chrome.runtime.onInstalled.addListener(() => {
chrome.contextMenus.create({
id: 'select',
title: 'select',
type: 'normal',
contexts: ['all']
});
})
chrome.contextMenus.onClicked.addListener(function(clickDate) {
if (clickDate.menuItemId == 'select') {
//send message to make content-script start operate
chrome.runtime.sendMessage('start');
}
});
//let's say that this is the content-script
chrome.runtime.onMessage.addListener(function(response, sender, sendResponse) {
if (response == 'start') {
// js code that is inserted into the site
}
});
{
"manifest_version": 3,
"name": "SendFast",
"description": "This extension makes it easier to send information",
"version": "1.0",
"icons": {
"128": "16-nahum.png",
"48": "64-nahum.png",
"16": "16-nahum.png"
},
"action": {
"default_icon": "16-nahum.png",
"default_popup": "popup.html"
},
"permissions":["contextMenus","activeTab"],
"background":{
"service_worker": "eventPage.js"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content-script.js"]
}
]
}
so I tried to use chrome.scripting but failed. that's what I came up with:
//eventPage.js(after changes)
chrome.runtime.onInstalled.addListener(() => {
chrome.contextMenus.create({
id: 'select',
title: 'select',
type: 'normal',
contexts: ['all']
});
})
async function addJsScript() {
const [tab] = await chrome.tabs.query({active: true, currentWindow: true});
await chrome.scripting.executeScript({
target: {tabId: tab.id},
script:["content-script"],
});
}
chrome.contextMenus.onClicked.addListener(function(clickDate) {
if (clickDate.menuItemId == 'select') {
addJsScript()
}
});
and i added this to the manifest:
"permissions":["contextMenus","activeTab","scripting"],
"host_permissions":["<all_urls>"],
There are a few problems that I have been having that is related to this. Here's what you need to know.
First, to access the tabs, you need to add "host_permissions": ["<all_urls>"] to your manifest (v3) to assure that you can access the web pages.
Second, instead of script:["content-script.js"] as seen in the addJsScript() function, use files:["content-script.js"].
Lastly, make sure you have the scripting permission added in your manifest file.
That is what I had to do to fix it for me.
Also, as stated above, remove the content script from your manifest and add .js to the end of the script name in files
I'm trying to make an installable WPA (Web Progressive App)
I was checking here and there... found out I needed to add a manifest => done
<meta name="theme-color" media="(prefers-color-scheme: light)" content="white">
<link rel="manifest" href="/pathtomanifest.webmanifest" />
my .manifest looks like this
{
"theme_color": "#4a90e2",
"background_color": "#ffffff",
"display": "standalone",
"scope": "/", // -->> also tried with full https://example.com
"start_url": "/", // -->> also tried with full https://example.com
"name": "name",
"short_name": "shortname",
"description": "Rss feeds",
"icons": [
{
"src": "/manifests/icon-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
}
// up to 512x512
]
}
after, I need to register a worker like this
async function workers_load () {
if ('serviceWorker' in navigator) {
try { await navigator.serviceWorker.register('/workers.js'); console.log(`stubworker loaded`); }
catch (e) { console.log(`SW registration failed`); }
}
}
and load it
window.addEventListener('load', function () { workers_load(); }, false);
my workers.js
const CACHE_NAME = 'offline';
const OFFLINE_URL = '/templates/account_js/workers/stuboffline.html';
self.addEventListener('install', function(event) {
console.log("stubworker installed", event);
event.waitUntil((async () => { const cache = await caches.open(CACHE_NAME); await cache.add(new Request(OFFLINE_URL, {cache: 'reload'})); })());
self.skipWaiting();
})
self.addEventListener('activate', function(event){
console.log("stubworker activated", event);
event.waitUntil((async () => { if ('navigationPreload' in self.registration) { await self.registration.navigationPreload.enable(); } })());
self.clients.claim();
})
self.addEventListener('fetch', function (event) {
console.log("stubworker fetch", event);
});
When I load the page I get in the console I get this
worker installed InstallEvent {isTrusted: true, type: "install", target: ServiceWorkerGlobalScope, currentTarget: ServiceWorkerGlobalScope, eventPhase: 2, …}
worker loaded
workers.js?v=4:2 stubworker activated ExtendableEvent {isTrusted: true, type: "activate", target: ServiceWorkerGlobalScope, currentTarget: ServiceWorkerGlobalScope, eventPhase: 2, …}
When I go to google Lighthouse for testing
It tells me 2 errors
Web app manifest or service worker do not meet the installability requirements
This origin has one or more service workers, however the page (...) is not in scope.
it tells me also this
Failure reason
No matching service worker detected. You may need to reload the page, or check that the scope of the service worker for the current page encloses the scope and start URL from the manifest.
it seems related to the manifest, but what I'm I missing in my manifest
What code I'm I missing to make it "installable" ?
Turns out I have to make few modif to make it work
1- register the worker
window.addEventListener('load', function () {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/ws.js?v=31',{scope: '/' });
console.log("worker registered"); }
}, false);
2- *** ABSOLUT MUST ==>> ws.js needs to be in root html folder
3- Manifest
"theme_color": "#4a90e2",
"background_color": "#ffffff",
"display": "standalone",
"scope": "/",
"start_url": "https://www.example.com/",
"name": "name",
"short_name": "name",
"description": "Rss feeds from around the world",
"categories": ["news","feeds","rss"],
"dir": "auto",
"orientation": "any",
4- ws.js starting point
self.addEventListener('install', function(event) { });
self.addEventListener('activate', function(event){ });
self.addEventListener('fetch', function (event) { });
I need to send some data to the newly created tab. I found some answers here to implement the listener first and then send a message. My event listener isn't working and can't catch the message.
manifest:
{
"manifest_version": 2,
"name": "My Cool Extension",
"version": "0.1",
"permissions": ["tabs",
"http://*/*",
"https://*/*",
"activeTab"
],
"browser_action": {
"default_icon": "icon.png"
},
"background": {
"scripts": ["background.js"]
}
}
background:
chrome.browserAction.onClicked.addListener((tab)=>{
chrome.tabs.query({active: true, currentWindow: true}, tabs => {
if(tabs.length === 1 ){
chrome.tabs.create({url:"https://www.youtube.com/", active: true}, (tab)=>{
chrome.tabs.executeScript(tab.id, {file:"content.js"},tab=>{
chrome.tabs.sendMessage(tab.id, {"Active Objects": "elo"})
})
})
}
else{
alert("wrong page")
}
});
});
content:
chrome.runtime.onUpdate.addListener(
(request, sender, sendResponse)=>{
alert("elo")
}
);
I've finally got it to work. I didn't actually tested it the first time.
In addition to replacing onUpdate with onMessage in the content script you
might want to add setTimeout functions in the background script to delay the executions of
chrome.tabs.executeScript and chrome.tabs.sendMessage. Otherwise, you might
get (as I have) a runtime.lastError: The tab was closed.
And also you should avoid having multiple function callback arguments named
tab. Otherwise, they get overriden.
I simplified the background script to get it to work. You don't actually need
to query the current tab to create a tab and execute a script inside it. So,
I've simply kept chrome.tabs.create.
background.js:
chrome.browserAction.onClicked.addListener(function (_) {
chrome.tabs.create({url: "https://www.youtube.com/", active: true},
function (yt_tab) {
setTimeout(function () {
chrome.tabs.executeScript(yt_tab.id, {file: "content.js"});
setTimeout(function () {
chrome.tabs.sendMessage(yt_tab.id, {"Active Objects":"elo"});
}, 1000);
}, 1000);
});
});
content.js:
chrome.runtime.onMessage.addListener(
function (request, sender, sendResponse) {
console.log(request);
}
);
Forgive me for any glaring mistakes as I am new to chrome extensions, but this error with Chrome's message passing API has been discussed here, here, and here in the past and the common response is along the lines of 'disable existing Chrome extensions, one of them is causing the error'. Is this the best that can be accomplished? Are we supposed to just roll over and accept the fact that our extensions will conflict with others? Returning true or returning a Promise for the listener callback function and using sendResponse does not solve the problem for me.
Currently, I can only get the new value stored in chrome.storage.local (no errors) by disabling all other chrome extensions, removing the extension and loading back up the unpacked extension. The code interestingly only seems to work on developer.chrome.com, it doesn't work at all on the other "matches" URLs in manifest.json.
I think that there is some significance in the await and async operators in solving this issue but I am unsure how to properly implement it.
manifest.json:
{
"manifest_version": 2,
"name": "my extension",
"version": "1.0",
"description": "its my extension",
"permissions": [
"declarativeContent",
"storage",
"activeTab"
],
"content_scripts": [
{
"matches": [
"*://developer.chrome.com/*",
"*://bbc.co.uk/*",
"*://theguardian.com/*",
"*://dailymail.co.uk/*"
],
"js": ["content.js"]
}
],
"background": {
"scripts": ["background.js"],
"persistent": false
},
"content_security_policy": "script-src 'self' https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js; object-src 'self'",
"page_action": {
"default_popup": "popup.html"
},
"icons": {
"16": "images/icon16.png",
"32": "images/icon32.png",
"48": "images/icon48.png",
"128": "images/icon128.png"
}
}
popup.html:
<!DOCTYPE html>
<html>
<head>
<title>my extension</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="popup.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<h1>my extension</h1>
<h2>Article: <span id="article-headline"></span></h2>
<button id="detect-article">Detect Article</button>
</body>
</html>
popup.js:
$(document).ready(function() {
$("#detect-article").click(function() {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
chrome.tabs.sendMessage(tabs[0].id, {request: "Requesting headline"}, function(response) {
console.log("Requesting headline")
});
});
});
})
function getHeadline(changes) {
let changedValues = Object.keys(changes);
//console.log(changedValues);
for (var item of changedValues) {
console.log("new value: " + changes[item].newValue);
$("#article-headline").text(changes[item].newValue)
}
}
chrome.storage.onChanged.addListener(getHeadline);
content.js:
function handleRequest(message, sender, sendResponse) {
console.log("Request recieved");
let headlineList = document.getElementsByTagName("h1");
chrome.storage.local.set({headline: headlineList[0].innerText}, function() {
console.log("'" + headlineList[0].innerText + "' stored in local storage");
});
return true;
}
chrome.runtime.onMessage.addListener(handleRequest);
background.js:
chrome.runtime.onInstalled.addListener(function() {
chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
chrome.declarativeContent.onPageChanged.addRules([{
conditions: [
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { hostContains: 'developer.chrome.com' },
}),
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { hostContains: 'bbc.co.uk' },
}),
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { hostContains: 'theguardian.com' },
}),
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { hostContains: 'dailymail.co.uk' },
}),
],
actions: [new chrome.declarativeContent.ShowPageAction()]
}]);
});
});
Many thanks for taking the time to look/re-look at this issue, solutions pertaining to the aforementioned 'disable existing extensions' are not what I am looking for.
When you specify a callback for sendMessage you're telling the API that you NEED a response so when your content script doesn't respond using sendResponse the API thinks something terrible happened and reports it as such!
Reminder: when editing content scripts make sure to reload both the extension on chrome://extensions page and the tabs that should have this content script.
If you need a response from asynchronously running code such as chrome API callback:
Keep return true
Call sendResponse(someImportantData) inside the callback
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
chrome.storage.local.set({foo: 'bar'}, () => {
sendResponse('whatever');
});
return true;
});
Same for Promise, but don't use async for the onMessage listener, more info.
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
fetch(message.url).then(r => r.text())
.then(t => sendResponse({ok: t}))
.catch(e => sendResponse({err: e.message}));
return true;
});
If you need a response and it can be sent immediately:
Replace return true with sendResponse
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
sendResponse('whatever');
});
If you don't need any response:
Remove the callback in sendMessage
chrome.tabs.sendMessage(tabs[0].id, {request: "Requesting headline"});
Remove return true - all it does currently is telling the API to keep the messaging port open indefinitely, which will never be used by you, so it's just a memory leak source.
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
// do something
// don't return true
// ManifestV2: don't call sendResponse
// ManifestV3 bug: uncomment the next line
// sendResponse();
});
For ManifestV3 in Chrome 99, 100, 101 you need a dummy sendResponse() call.
I need to implement messaging in the expansion. From content_script`a in background.js looked for a way to exchange documents using window.postMessage () and window.addEventListener (), but the messages are not sent. Through content_script I loaded js code in head.
Here's the code to background.js:
window.addEventListener ("message", function (event) {
// We only accept messages from ourselves
if (event.source! = window)
return;
if (event.data.type && (event.data.type == "FROM_PAGE")) {
console.log ("Content script received:" + event.data.text);
port.postMessage (event.data.text);
}
}, False);
This in content_script.js:
var s = document.createElement ('script');
s.src = chrome.extension.getURL ('inject.js');
(Document.head || document.documentElement) .appendChild (s);
s.onload = function () {
s.parentNode.removeChild (s);
};
This in inject.js:
window.postMessage ({type: "FROM_PAGE", text: "Hello from the webpage!"}, "*");
What am i doing wrong?
you should use:
chrome.runtime.onMessage.addListener
and
chrome.runtime.sendMessage
to pass messages between different js components. this answer helped me a lot: Chrome Extension how to send data from content script to popup.html
The basic flow of communication between a webpage and a content script or background script is:
Use content scripts to share DOM with the Web, use window.postMessage to send messages, and content.js uses extended API to forward the message to background.js after receiving the message.
manifest.json
{
"name": "you name",
"description": "you description",
"version": "1.1.0",
"background": {
"scripts": ["background.js"],
"persistent": false
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"],
"all_frames": true
}
],
"manifest_version": 2
}
web.html
<body>
<button id="me">Button</button>
</body>
content.js
window.addEventListener('message', event => {
chrome.runtime.sendMessage(`${event.data} forwarded to the background.js`);
});
document.querySelector('#me').addEventListener('click', () => {
window.postMessage('Message from DOM', '*');
});
background.js
chrome.runtime.onMessage.addListener(msg => console.log(msg));
Long-lived connections
content.js
const port = chrome.runtime.connect();
window.addEventListener('message', event => {
port.postMessage(event.data);
});
document.querySelector('#me').addEventListener('click', () => {
window.postMessage('Message from DOM', '*');
});
background.js
chrome.runtime.onConnect.addListener(port => {
port.onMessage.addListener(msg => {
console.log('msg: ', msg);
});
});
Another way to communicate directly with background.js
manifest.json (add externally_connectable)
{
"name": "you name",
"description": "you description",
"version": "1.1.0",
"background": {
"scripts": ["background.js"],
"persistent": false
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"],
"all_frames": true
}
],
"manifest_version": 2,
"externally_connectable": {
"matches": ["http://localhost/*"]
}
}
web.html
<body>
<button id="me">Button</button>
<script>
// Your extension ID, (pabahpeigkdpglhnjponplchdffchkdj).
document.querySelector('#me').addEventListener('click', () => {
chrome.runtime.sendMessage('pabahpeigkdpglhnjponplchdffchkdj', 'Web Message');
});
</script>
</body>
background.js
chrome.runtime.onMessageExternal.addListener(request => {
console.log('request: ', request);
});