I am working on a google chrome extension project where content.js script sends message to popup.js. Also trying to get some text from content.js to be turned into the same thing but on popup.js. Here is all my code.
This is my manifest file.
manifest.json
{
"name": "hoko's ext",
"description": "my ext",
"version": "1.0",
"manifest_version": 3,
"action": {
"default_popup": "popup.html"
},
"content_scripts": [
{
"matches": ["https://*/*", "http://*/*"],
"js": ["content-script.js"]
}
],
"background": {
"service_worker": "background.js"
},
"permissions": [
"tabs",
"activeTab"
]
}
This is the popup you get when you click on the icon in the toolbar.
popup.html
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>I am the popup</h1>
<p id="extensionpopupcontent"></p>
<script type="text/javascript" src="popup.js"></script>
</body>
</html>
This is my content script called content-script.js
function injectScript(file_path, tag) {
var node = document.getElementsByTagName(tag)[0];
var script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
script.setAttribute('src', file_path);
node.appendChild(script);
}
injectScript(chrome.runtime.getURL('inject.js'), 'body');
chrome.runtime.sendMessage(document.getElementsByTagName('title')[0].innerText);
In background.js, I am receiving the message from content-script.js.
chrome.runtime.onMessage.addListener(function(response, sender, sendResponse) {
function onMessage(request, sender, sendResponse) {
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension");
if (request.greeting == "hello") {
sendResponse({farewell: "goodbye"});
}
}
})
In popup.js, it is receiving from background.js to output the code from content-script.js.
window.addEventListener('DOMContentLoaded', () => {
let bg = chrome.extension.getBackgroundPage();
chrome.tabs.query({active: true, currentWindow: true}, tabs => {
chrome.tabs.sendMessage(tabs[0].id, {greeting: "hello"}, function(response) {
document.getElementById('extensionpopupcontent').innerHTML = response;
});
});
});
I believe I need to inject this script that is inject.js.
function click() {
return document.getElementsByTagName('title')[0].innerText;
}
click();
remove background and content_scripts in manifest.json
remove tabs from permissions in manifest.json
remove background.js, content-script.js, and inject.js
rewrite popup.js as follows:
chrome.tabs.query({ active: true, currentWindow: true }, tabs => {
document.getElementById('extensionpopupcontent').textContent = tabs[0]?.title;
});
Related
I'm building my first Chrome extension and my goal is to fetch the value of a DOM element from the extension popup by the click of a button. I'm basically following the exact documentation on how to send a message but keep getting:
'Error: Could not establish connection. Receiving end does not exist.'
For now, I'm not even trying to return DOM data; just trying to console.log a message triggered by the extension and returned by the content script. Any idea what the issue might be?
Here's my setup:
manifest.jst
{
"name": "Fetch Test",
"description": "Fetch data from DOM",
"version": "1.0",
"manifest_version": 3,
"action": {
"default_popup": "popup.html"
},
"permissions": ["activeTab", "tabs", "scripting"],
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content_script.js"]
}
]
}
popup.html
<html>
<body>
<div class="container">
<button id="fetch-button">Fetch</button>
</div>
</body>
</html>
<script src="popup.js"></script>
popup.js
document.getElementById("fetch-button").addEventListener("click", function () {
(async () => {
const [tab] = await chrome.tabs.query({
active: true,
lastFocusedWindow: true,
});
const response = await chrome.tabs.sendMessage(tab.id, {
greeting: "hello",
});
console.log(response);
})();
});
content_script.js
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
console.log(
sender.tab
? "from a content script:" + sender.tab.url
: "from the extension"
);
if (request.greeting === "hello") sendResponse({ farewell: "goodbye" });
});
This sample uses executeScript to get document.title.
manifest.json
{
"name": "Get document.title",
"version": "1.0",
"manifest_version": 3,
"permissions": [
"scripting"
],
"host_permissions": [
"<all_urls>"
],
"action": {
"default_popup": "popup.html"
}
}
popup.html
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
* {
font-size: x-large;
}
</style>
</head>
<body style="min-width:300px">
<div id="title"></div><br>
<script src="popup.js"></script>
</body>
</html>
popup.js
const getTitle = () => {
console.log("getTitle() = " + document.title);
return document.title;
}
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
console.log("Execute Script");
chrome.scripting.executeScript({
target: { tabId: tabs[0].id },
func: getTitle
}, (result) => {
console.log("Recv result = " + result[0].result);
document.getElementById("title").innerText = result[0].result;
});
});
You need to include the javascript files in your popup.html file like so. Does that help?
<html>
<head>
<script src="popup.js"></script>
<script src="content_script.js"></script>
</head>
<body>
<div class="container">
<button id="fetch-button">Fetch</button>
</div>
</body>
</html>
You can try to do a long-lived message passing.
popup.js
const extensionContentScriptPort = () => {
return (async function () {
const currentTabQuery = {
active: true,
currentWindow: true,
};
const currentTabId = (await chrome?.tabs?.query(currentTabQuery))?.[0].id;
return chrome?.tabs?.connect(currentTabId, {
name: 'EXTENSION_CONTENTSCRIPT',
});
})();
};
// Add Event Listener for receiving messages
extensionContentScriptPort.then(port =>
port.onMessage.addListener((extensionContentScriptMsg) => {
console.log(extensionContentScriptMsg);
});
);
// Post message to content script
document.getElementById("fetch-button").addEventListener("click", function () {
extensionContentScriptPort?.then(port => {
port.postMessage({
greeting: "hello"
})
});
});
content_script.js
chrome?.runtime?.onConnect?.addListener(function (extensionContentScriptPort) {
console.assert(extensionContentScriptPort?.name === 'EXTENSION_CONTENTSCRIPT');
extensionContentScriptPort?.onMessage?.addListener(function (
extensionContentScriptMsg,
) {
if (extensionContentScriptMsg?.greetings === "hello") {
return extensionContentScriptPort?.postMessage({
greetings_reply: "Hey, there!"
})
}
}
}
I would like to implement a hotkey that triggers a function in my content script.
The content script (main.js) is executed on page laod from my popup.js file.
i have added the command into my manifest.json and when I add the onCommand listener to my popup.js I can console log that it gets triggered when I press my hotkey (Ctrl+Shift+K)
However I can not get it passed to my content script.
manifest.json
{
"manifest_version": 2,
"name": "Ccghjj",
"description": "hdjdjdjsjs",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": ["tabs", "*storage", "activeTab"],
"content_scripts": [
{
"matches": ["*://"],
"css": ["style.css"],
"js": ["jquery.js", "main.js"]
}
],
"content_security_policy": "script-src 'self' https://ajax.googleapis.com; object-src 'self'",
"web_accessible_resources": ["toolbar.html", "style.css"],
"commands": {
"show_deals": {
"suggested_key": {
"default": "Ctrl+Shift+K"
},
"description": "Highlight Deals"
}
}
}
popup.js
function registerButtonAction(tabId, button, action) {
// clicking button will send a message to
// content script in the same tab as the popup
button.addEventListener('click', () => chrome.tabs.sendMessage(tabId, { [action]: true }));
}
function setupButtons(tabId) {
// add click actions to each 3 buttons
registerButtonAction(tabId, document.getElementById('start-btn'), 'startSearch');
registerButtonAction(tabId, document.getElementById('deals-btn'), 'startDeals');
registerButtonAction(tabId, document.getElementById('stop-btn'), 'stopSearch');
}
function injectStartSearchScript() {
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
// Injects JavaScript code into a page
// chrome.tabs.executeScript(tabs[0].id, { file: 'main.js' });
// add click handlers for buttons
setupButtons(tabs[0].id);
});
}
injectStartSearchScript();
// hotkey command listener
chrome.commands.onCommand.addListener((show_deals) => {
console.log(`Command "${show_deals}" triggered`);
// how can I get to main.js to call deals()
});
main.js (content script)
async function deals() {
// should be fired when I press my hotkey Ctrl+Shift+K
Use messages like below
popup.js
chrome.commands.onCommand.addListener((show_deals) => {
console.log(`Command "${show_deals}" triggered`);
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
chrome.tabs.sendMessage(tab.id, { msg: "deals"})
})
});
and in your content script
if (!window.firstTimeExecuted) {
window.firstTimeExecuted = true;
chrome.runtime.onMessage.addListener((data, sender, sendResponse) => {
if (data.msg == 'deals') {
deals()
}
});
}
All I am trying to do change the value of popup.html on the basis of Specific URL. So I am sending message to background.js when user click on change ID of H2 from popup.html render the content by using specific JavaScript file
manifest.json
{
"manifest_version": 2,
"name": "Helpfullio",
"version": "0.1",
"content_scripts": [{
"matches": [
"*://google.com/*/*",
],
"js": ["jquery-3.2.1.js"]
}],
"browser_action": {
"default_popup": "popup.html"
},
"background": {
"scripts": ["background.js"]
},
"permissions": ["tabs","http://*/*", "https://*/*"]
}
popup.html
<html>
<head>
</head>
<body>
<h2 id="change">Change ___________</h2>
<script src="popup.js"></script>
</body>
</html>
popup.js
function clickHandler(e) {
chrome.runtime.sendMessage({directive: "popup-click"}, function(response) {
// this.close(); finishes processing request
});
}
document.addEventListener('DOMContentLoaded', function () {
document.getElementById('change').addEventListener('click', clickHandler);
})
background.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
switch (request.directive) {
case "popup-click":
chrome.tabs.query({
'active': true, 'lastFocusedWindow': true
}, function (tabs) {
var url = tabs[0].url;
var result = url.split('/');
var hostname = result[2];
if("bitlock" == hostname){
chrome.tabs.executeScript(null, {
file: "render.js",
// allFrames: true
});
}else{
hrome.tabs.executeScript(null, {
file: "secondrender.js",
// allFrames: true
});
}
sendResponse({});
});
break;
default:
alert("Unmatched request of '" + request + "' from script to background.js from " + sender);
}
}
);;
JavaScript doesn't render the content of popup.html as it is not finding the elementID "change" . SO here how can I give reference of popup.html file in render.js file
render.js
document.getElementById("change").textContent = 'new text';
Im trying to send message to Background, and as well receive back to content script.
This is my content_script.js
init();
function init() {
function notifyBackgroundPage(event) {
chrome.runtime.sendMessage(chromeExtID, {
greeting: "Greeting from the content script"
});
}
window.addEventListener("click", notifyBackgroundPage);
// accept messages from background
// below line addListener is undefined
chrome.runtime.onMessage.addListener(function backgroundListener(request, sender, sendResponse) {
console.log("BgExt.js says: " + request);
});
}
BgExt.js
define([], function GmailExt() {
return {
start: function () {
chrome.runtime.onMessage.addListener(
function (request, sender, sendResponse) {
console.log('inside response');
// to send back your response to the current tab
chrome.tabs.query({active: true, currentWindow: true}, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, {farewell: response}, function (response) {
});
});
return true;
}
);
}
};
});
This GmailExt file is loaded inside all.js (while all.js is inserted in index.html)
require([
"base_host/chrome/Server",
"kernel/Kernel",
"hydra/version",
"base_host/chrome/StartupConfig",
"hydra/apps",
"dojo/_base/Deferred",
"talkto_util/common",
"dojo/i18n!base_host/nls/CommonStrings",
"base_host/chrome/BgExt",
"dojo/domReady!"
], function (Server, Kernel, version, StartupConfig, apps, Deferred, talktoUtil, nls, BgExt) {
document.getElementById("logoMessage").innerText = nls.simple_secure_text;
var host = new Server();
//if app and not extension
chrome.browserAction && chrome.browserAction.onClicked.addListener(function() {
Deferred.when(kernel.onInit(), function () {
host.appManager.showShell();
});
return true;
});
BgExt.start();
});
client_base/host/chrome/index.html
<!DOCTYPE HTML>
<html>
<head>
<!-- Background Page for Extension/app -->
<title>Flock</title>
<meta charset="UTF-8">
<!--BUILD_REPLACE_BLOCK_START-->
<!-- This block will be remove by build.
All the files from libs/common/files.txt would be prepended to dojo.js -->
<script type="text/javascript" src="../../../hydra/src/libs/common/dojoConfig.js"></script>
<script type="text/javascript" src="../../../hydra/src/libs/common/underscore.js"></script>
<script type="text/javascript" src="../../../hydra/src/libs/common/underscore-ext.js"></script>
<!--BUILD_REPLACE_BLOCK_END-->
<script type="text/javascript" src="../../../hydra/src/libs/dojotoolkit/dojo/dojo.js"></script>
<script type="text/javascript" src="all.js"></script>
</head>
<body>
</body>
</html>
Manifest.json
{
"manifest_version": 2,
"content_security_policy": "script-src 'self' 'unsafe-eval' https://j.maxmind.com https://ssl.google-analytics.com https://flock-apps.flock.co https://flock-apps.flock-staging.co https://flock-apps.flock.com https://flock-apps.flock-staging.com; object-src 'self'",
"minimum_chrome_version": "22",
"options_page": "client_base/host/chrome/static/crx_browser_actions/index.html?app=preferences",
"name": "__MSG_extName__",
"description": "__MSG_extDescription__",
"background": {
"page": "client_base/host/chrome/index.html",
"persistent": true
},
"browser_action": {
"default_popup": "/gmail_ext/popup.html"
},
"web_accessible_resources": [
"client_base/host/chrome/static/blank.gif",
"gmail_ext/icons.png",
"gmail_ext/jquery-3.2.1.min.js",
"gmail_ext/gmail.js",
"gmail_ext/content_script.js"
],
"permissions": [
"<all_urls>",
"unlimitedStorage",
"notifications",
"idle",
"background",
"tabs",
"activeTab"
],
"optional_permissions": [
"clipboardWrite"
],
"externally_connectable": {
"matches": [
"https://*.google.com/*",
"http://localhost/*",
]
},
"content_scripts": [
{
"matches": [
"*://mail.google.com/*"
],
"css": [
"/gmail_ext/content_script.css"
],
"js": [
"/gmail_ext/loader.js"
],
"run_at": "document_end"
}
],
"version": "1.0"
}
I fixed it. The Solution was since I was using loader.js to load my content script and jquery via chrome.extension.getURL('/gmail_ext/content_script.js');
And was using "content_script.js" as a web_accessible_resources only loader.js had access to chrome object.
And yes, its a Dojo project, you can see the Working Extension here : https://chrome.google.com/webstore/detail/flock-chat-for-teams-and/enfaahabcinohafeakbliimmoholjeip?hl=en
I'm using page events now as showed here : https://developer.chrome.com/extensions/content_scripts#host-page-communication
At the top of loader.js I added below:
function initPort() {
var contentScriptPort,
chromeExtID = "lddaepjihbpbfpegjhjnkffjmmoigphe";
contentScriptPort = chrome.runtime.connect(chromeExtID);
contentScriptPort.onMessage.addListener(function (message) {
// Send data back to content script received from Background.
window.postMessage({type: "FROM_BACKGROUND", emails: message}, "*");
});
window.addEventListener("message", function _postMessage(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.emails);
contentScriptPort.postMessage(event.data);
}
}, false);
}
initPort();
Hello i'm trying to send message to popedup window, but when i close popup window end call it(from context menu) again.. it shows all last masseges..
How can i send only one message only to new opened window?
Manifest.json
{
"manifest_version": 2,
"name": "1tst",
"description": "sm txt",
"version": "0.1",
"author": "smwn",
"permissions": [
"activeTab",
"tabs",
"contextMenus"
],
"background": {
"scripts": ["sample.js"]
},
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["1.js"]
}]
}
sample.js
function genericOnClick(info, tab,selT) {
var vid;
var nw_tb;
chrome.tabs.create({
url: chrome.extension.getURL('1.html'),
active: false
}, function(tab) {
vid = tab.id;
// After the tab has been created, open a window to inject the tab
chrome.windows.create({
tabId: tab.id,
type: 'popup',
focused: true
// incognito, top, left, ...
},function(chromeWindow) {
//vid = chromeWindow.id;
}
);
});
chrome.windows.onCreated.addListener(function(tabb){
nw_tb = tabb.id;
alert("vid: "+vid+" nw_tb: "+nw_tb);
chrome.runtime.sendMessage(vid, {statn: selT});
return;
});
}
var title = "Test '%s' menu item";
var id = chrome.contextMenus.create({"title": title, "contexts":["selection"],
"onclick": function(info,tab){ genericOnClick(info,tab, info.selectionText);}
});
1.html
<!doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<script src="1.js"></script>
</head>
<body>
</body>
</html>
1.js
window.onload=function(){
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension");
sendResponse({metroSTAT: "OK"});
alert(request.statn);
});
}
The problem
Every time genericOnClick() is called, it registers a new listener for the windows.onCreated event. Once registered, the listeners will trigger every time.
The solution
One of the many possible solutions is to "un-register" the listener once it is triggered for the first time (using removeListener()):
// Replace the following block:
chrome.windows.onCreated.addListener(function(tabb){
...
});
// With this:
chrome.windows.onCreated.addListener(function tmpListener(tabb) {
chrome.windows.onCreated.removeListener(tmpListener);
...
});