we have a JSP page in a servlet and we have developed a google chrome extension in client side. we want to communicate with the extension through the JSP or html. as we have searched, there should be a background.js and a content script. we put the background.js inside the extension folder and we register it in chrome. then as we put the content script alongside the JSP or external web page, these two pages can not interact.
this is the code of background.js:
alert("hey back");
chrome.runtime.onConnectExternal.addListener(function (port) {
port.postMessageExternal({ greeting: "hey" });
port.onMessageExternal.addListener(function (message, sender) {
if (message.greeting == "salam") {
alert("message from content: " + message.greeting);
port.postMessageExternal({ greeting: "H R U?" });
}
else if (message.greeting == "khobam") {
alert("message from content: " + message.greeting);
}
else {
alert("background did not receive salam");
}
});
});
this is the manifest.json:
{
"manifest_version": 2,
"name": "msg-test",
"description": "message test",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png"
},
"background": {
"scripts": ["background.js"],
"persistent": true
},
"permissions": [
"tabs",
"http://*/*"
],
"offline_enabled": true,
"externally_connectable": {
"ids":["*"],
"matches": ["file:///C:/Users/h.aghajani/Desktop/b.html"],
"accept_tls_channel_id":true
}
}
this is the content script:
alert("hey content");
var port = chrome.runtime.connect({ name: "mycontentscript" });
port.onMessage.addListener(function (message, sender) {
if (message.greeting == "hey") {
alert("message from background: " + message.greeting);
port.postMessage({ greeting: "salam" });
}
else if (message.greeting == "H R U?") {
alert("message from background: " + message.greeting);
port.postMessage({ greeting: "khobam" });
}
else {
alert("content did not receive hello");
port.postMessage({ greeting: "no salam" });
}
});
there are some problems:
it doesn't work at all. where is the problem? in fact how can we call a
background script from an external web page?
Besides, if we put the content script in the extension folder, whenever the browser works offline (without internet connection), the background and content script can not talk properly.
Thanks.
First off, never use alert for debugging purposes. Never. Simply forget it.
Use console.log or the actual debugger. In order to debug the background page of an extension enable the [x] developer mode checkbox on the top of chrome://extensions page.
we call the content.js in html, as : <script src="content.js"/>
This is invalid HTML, the script tag isn't a void (self closing) element, so the correct syntax is:
<script src="content.js"></script>
As you can see on chrome://extensions page the extension has an error that file:// scheme is not permitted in "externally_connectable". Setup a local web server and make it serve the test files. Also you don't need "ids":["*"] as it's only used for communicating from other extensions/apps, not web pages.
"externally_connectable": {
"matches": ["http://localhost/test.html"],
"accept_tls_channel_id":true
},
Now at last the page script, misleadingly named as content.js, is loaded and displays an error in the console that chrome.runtime.connect should specify the extension ID as the first parameter as per the documentation so that Chrome would know where to send the message.
var port = chrome.runtime.connect("qwertyuiopasdfghjkl", { name: "mycontentscript" });
replace qwertyuiopasdfghjkl with your extension's ID as seen on chrome://extensions page when the [x] developer mode checkbox is enabled on the top.
Now let's look at the background script:
There's no such thing in Chrome API as postMessageExternal, just use postMessage.
Related
I have simple website created with vanilla JavaScript and I had create a simple chrome extension.
I want to know is there any way to figure out if my visitors' chrome have my extensions.
This is my manifest.json
{
"name": "Covid-19 Stats UK",
"version": "1.0.0",
"description": "latest covid data of UK",
"manifest_version": 3,
"author": "Sampurna Chapagain",
"action":{
"default_popup": "index.html",
"default_title": "Latest Covid Report"
}
}
If you control both the extension and the website, two approaches leap to mind: web accessible resources or site-extension messaging.
In this answer I'll be using abcdefghijklmnopqrstuvwxyzabcdef as a placeholder for your extension's ID.
Web accessible resources
Perhaps the simplest way to check if the user has your extension installed is to use XMLHttpRequest or fetch() to try to load a file from the extension. By default, extensions do not expose their resources to websites, but extension authors can selectively expose resources using the "web_accessible_resources" key in the manifest. The following example is for a Manifest V3 extension.
manifest.json
{
"name": "",
"version": "",
"manifest_version": 3,
"web_accessible_resources": [
{
"matches": ["https://example.com/*"],
"resources": [
"1x1.png", // Expose a single file
"web-accessible-resources/*" // Or a directory tree
]
}
]
}
Then, add the following snippet to your site to check if the extension is installed.
Website JavaScript
const extensionId = "abcdefghijklmnopqrstuvwxyzabcdef";
async function checkExtensionInstalled() {
try {
await fetch(`chrome-extension://${extensionId}/1x1.png`);
return true;
} catch (e) {
if (e instanceof TypeError && e.message == 'Failed to fetch') {
return false;
} else {
throw e;
}
}
}
// Then in your site's buisness logic, do something like this
checkExtensionInstalled.then(installed => {
if (installed) {
console.log("The extension IS installed.");
} else {
console.log("The extension is NOT installed.")
}
});
Messaging
The extension platform allows a website and an extension to communicate directly with each other, but both sides must know about each other in order for this to work.
The following example is adapted from Chrome's official docs.
First, add an "externally_connectable" declaration to your extension's manifest.
manifest.json
"externally_connectable": {
"matches": ["https://*.example.com/*"]
}
Next, update your site's JS to send the extension a message.
Website JavaScript
// The ID of the extension we want to talk to.
const extensionId = "abcdefghijklmnopqrstuvwxyzabcdef";
const messageBody = "Ping";
if (chrome?.runtime?.sendMessage) {
chrome.runtime.sendMessage(extensionId, messageBody, function(response) {
console.log("The extension IS installed.", response);
});
} else {
console.log("The extension is NOT installed.")
}
Finally, in the extension's background context, add a listener for this message.
background.js
function handleExternalMessage(message, _sender, sendResponse) {
if (message === "Ping") {
sendResponse("Pong");
};
}
chrome.runtime.onMessageExternal.addListener(handleExternalMessage);
I am trying to make a chrome extension that blocks users from accessing sites by redirecting them to the extension's custom HTML block page. The user can then choose to click "Unblock" to exclude the current tab from being checked by the filter.
The extension works as expected. For example, if you try to access https://www.youtube.com/ while "youtube.com" is in the blocked list, it will redirect you to "blocked.html".
However, it seems that the extension only works on the CURRENT TAB that you are working with. If you try to shift click a hyperlink (Which opens the link in a new tab) which leads to https://www.youtube.com, it will redirect to "blocked.html", but Chrome would block the redirect and give you this screen:
Even if you now focus on the tab and press refresh, "blocked.html" still does not load.
I believe this may be because I am missing permissions in my manifest file, however, I looked at the docs for the permissions page and I could not find any relevant permissions I could add.
Thanks in advance.
Note: Interestingly, the yellow error message shown above only appears on pages that have been blocked by chrome. The message is this: "crbug/1173575, non-JS module files deprecated."
Also, if you try to refresh the page, the line number that the message appears becomes higher. (I refreshed a few times and right now it is at VM712:7146). Not sure if this message is related to the error.
manifest.json
"manifest_version": 2,
"background": {
"service_worker": "background.js"
},
"options_page": "options.html",
"permissions": [
"storage",
"activeTab",
"tabs",
"webRequest",
"webRequestBlocking",
"<all_urls>"
],
"page_action": {
"default_popup": "popup.html"
}
blocked.js (Shortened)
// Unblock button redirect
let unblockButton = document.getElementById("unblockButton");
updateOriginalUrl();
chrome.runtime.onMessage.addListener(function update(message) {
updateOriginalUrl();
chrome.runtime.onMessage.removeListener(update);
})
function updateOriginalUrl() {
chrome.storage.sync.get("originalUrl", (result) => {
console.log("Unblock button URL set to: " + result.originalUrl);
unblockButton.addEventListener("click", () => {
location.href = result.originalUrl;
chrome.runtime.sendMessage("exclude")
})
});
}
background.js
chrome.webRequest.onBeforeRequest.addListener((details) => {
console.log("New request detected")
console.log("Request URL: " + details.url);
if(enabled && !excludedTabs.includes(details.tabId)) {
for(let blockedUrl of blockedList) {
if(details.url.includes(blockedUrl)) {
console.log("Match detected, redirecting");
chrome.storage.sync.set( {"originalUrl": details.url}, () => {
chrome.runtime.sendMessage("updateOriginalUrl");
});
return {
redirectUrl: chrome.runtime.getURL("blocked.html")
};
}
}
}
}, {
urls: ["<all_urls>"],
types: ["main_frame"]
}, ["blocking"]);
Thanks #wOxxOm:
Either add blocked.html to web_accessible_resources in manifest.json or switch to using declarativeNetRequest API.
This worked.
I am trying to pass data from my webpage to the chrome extension.
manifest.json (I am trying to do this in my local environment first)
"externally_connectable": {
"matches": ["*://localhost/*"]
}
In listen.js (a background script):
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
if (sender.url == blacklistedWebsite)
return; // don't allow this web page access
if (request.openUrlInEditor)
alert('test2');
alert(request.openUrlInEditor);
});
None of the alerts display above.
I got the extension ID of the unpacked chrome extension by viewing the ID when I navigate to chrome://extensions/. In my webpage in localhost environment
// DEVELOPMENT extension ID
var editorExtensionId = "fppgjikaoolnlcmdjalbfkmlcadcckmb";
var url = 'test';
// Make a simple request:
chrome.runtime.sendMessage(editorExtensionId, {openUrlInEditor: url},
function(response) {
if (!response.success)
handleError(url);
});
When I run this. in the browser, I get an error saying:
Error in event handler for (unknown): TypeError: Cannot read property 'success' of undefined
I'm not sure where to begin debugging this.
After making a few changes in your code I am able achieve your goal.
See below the complete code.
manifest.json
{
"manifest_version": 2,
"name": "CS to Bg Communication",
"version": "0.1",
"background": {
"scripts": ["listen.js"]
},
"content_scripts": [
{
"all_frames" : true,
"matches": ["<all_urls>"],
"js": ["contentscript.js"]
}
],
"browser_action": {
"default_popup": "popup.html"
},
"externally_connectable": {
"matches": ["*://localhost/*"]
}
}
listen.js - the background script
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
blacklistedWebsite = 'http : / / yourdomain . com /';
if (sender.url == blacklistedWebsite)
return; // don't allow this web page access
if (request.openUrlInEditor) {
alert('test2 - ' + request.openUrlInEditor);
sendResponse({"success": true, "AckFromBG": "I have received your messgae. Thanks!"}); // sending back the acknowlege to the webpage
}
});
contentscript.js - the content script - actually does nothing
console.log("this is content script");
web-page.html - The local web page
<html>
<body>
This page will will send some message to BG script
<script type="text/javascript">
// DEVELOPMENT extension ID
var editorExtensionId = "fjaedjckfjgifecmgonfmpaoemochghb"; // replace with your extension ID
var url = 'test';
chrome.runtime.sendMessage(editorExtensionId, {openUrlInEditor: url}, function(response) {
console.log(response);
if (!response.success)
handleError(url);
});
function handleError(url) {
console.log(url);
}
</script>
</body>
</html>
Summary of changes:
Defined blacklistedWebsite - this was undefined.
Added sendResponse({"success": true, "AckFromBG": "I have received
your messgae. Thanks!"}); to send back the acknowledgment to the
webpage.
Define function handleError(url) {
console.log(url);
} this was not defined.
That's it. Hope this will solve your issue.
The error you are getting indicates that chrome.runtime.sendMessage is executed. This means that external messaging is set up correctly: otherwise, chrome.runtime.sendMessage wouldn't be exposed to your page's code.
I think the problem is here:
if (sender.url == blacklistedWebsite)
return; // don't allow this web page access
I suspect the problem is blacklistedWebsite being undefined; the code fails with a ReferenceError and no meaningful response is being sent. Since the onMessage listener terminates (even with an error), the calling code gets an undefined response.
Check your background console to confirm if that's the case; if you copied this partial example code but aren't using this blacklisting functionality, delete this branch.
In future, please make sure you understand what every line of code you copy does!
Is it possible to send a message from Content script to Browser action directly without using background page? Following is a simplified version of what my code looks like. The content script seems to be working fine, but I get the following error in the console:
Error: Error: Could not establish connection. Receiving end does not exist.
I am assuming it's because the Browser action is not always active. But I don't want to use a Background page because I don't want the script running constantly hogging memory. I am hoping to send message directly to Browser action and display a popup, sort of like browserAction.onClicked displaying a popup. This is my first extension that I am trying to build, so trying to figure things out. Thanks
[manifest.json]
{
"manifest_version": 2,
"name": "Test",
"version": "0.1",
"icons": {
"48": "icons/test.png"
},
"permissions": [
"activeTab"
],
"browser_action": {
"default_icon":"icons/test.png",
"default_title": "test",
"default_popup": "popup/popup.html",
"browser_style": true
},
"content_scripts": [
{
"matches": ["*://testwebsite"],
"js": ["content_scripts/content-script.js"]
}
]
}
[popup.js]
function handleMessage(request, sender, sendResponse) {
console.log("Message from the content script: " +
request.greeting);
sendResponse({response: "Response from background script"});
}
browser.runtime.onMessage.addListener(handleMessage);
[content-script.js]
function handleResponse(message) {
console.log(`Message from the background script: ${message.response}`);
}
function handleError(error) {
console.log(`Error: ${error}`);
}
function send_2_popup() {
var sending = browser.runtime.sendMessage({
greeting: "Greeting from the content script"
});
sending.then(handleResponse, handleError);
}
var btn = document.getElementById("btn");
btn.addEventListener("click", send_2_popup);
You can rather send a message from the popup to the background and get a response as well as a message from background.. this way the background will know that the popup exists and hence the message from background to popup will be successful.
I'm trying to communicate from a web page to an extension and vice versa.
To do so, I looked at the Mozilla documentation here : https://developer.mozilla.org/fr/Add-ons/WebExtensions/Content_scripts#Communicating_with_the_web_page
And it has a simple example, but I can't make it work. On the web page script, I have this :
// page-script.js
var messenger = document.getElementById("from-page-script");
messenger.addEventListener("click", messageContentScript);
function messageContentScript() {
window.postMessage({
direction: "from-page-script",
message: "Message from the page"
}, "*");
On the content scripts page in the extension :
// content-script.js
window.addEventListener("message", function(event) {
if (event.source == window &&
event.data.direction &&
event.data.direction == "from-page-script") {
alert("Content script received message: \"" + event.data.message + "\"");
}
});
I installed the extension (as a temporary one, I uploaded my xpi file), then I used the "Debugging" method of API WebExtensions, and put a breakpoint into the listener, but whenever I call the PostMessage, the extension never seems to receive the event, the breakpoint is never triggered.
Is it possible to communicate this way between a web page and an extension ? Or is there another one ?
The problem was in the manifest of my extension. I declared my content script as a background script.
So, instead of writing this :
"background": {
"scripts": ["myscript.js"],
"persistent": true
},
You have to declare the script like this :
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["myscript.js"]
}
]
I was experiencing similar issues and the problem for me is I was calling the
window.postMessage
function from within an iframe. After I changed this to
top.window.postMessage
it started working.