I started to learn chrome extensions, I wanna make an extension with a list of phrases to send in twitch chat.
I have no intention of using the twitch api.
The first message is sent as it should, but the next ones nothing happens.
There's a log that shows that messages arrive on the page, I don't know what I'm doing wrong.
This is what I have:
manifest.json
{
"manifest_version": 2,
"name": "Default Messages",
"version": "0.1",
"offline_enabled": true,
"background": {
"persistent": false,
"scripts": ["background.js"]
},
"content_scripts": [{
"matches": ["*://*.twitch.tv/*"],
"js": ["content.js"]
}],
"page_action": {
"default_title": "Default messages",
"default_popup": "popup.html"
}
}
background.js
chrome.runtime.onMessage.addListener((msg, sender) => {
if (msg.from === "twitch" && msg.subject === "chat") {
chrome.pageAction.show(sender.tab.id);
}
});
content.js
chrome.runtime.sendMessage({
from: "twitch",
subject: "chat",
});
chrome.runtime.onMessage.addListener((msg, sender, response) => {
if (msg.from === "defaultMessages" && msg.subject === "newMessage") {
console.log(msg);
let input = document.querySelectorAll("textarea")[0];
let buttons = document.querySelectorAll("button");
input.textContent = msg.content;
input.dispatchEvent(new Event("input", { bubbles: true }));
buttons.forEach((button) => {
if (button?.dataset?.aTarget == "chat-send-button") {
button.dispatchEvent(new Event("click", { bubbles: true }));
}
});
response({
from: "twitch",
subject: "messageSent",
id: msg.id,
});
}
});
popup.js
window.addEventListener("DOMContentLoaded", () => {
let tab_id = undefined;
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
tab_id = tabs[0].id;
});
document.querySelectorAll("button").forEach((button) => {
button.addEventListener("click", function (e) {
if (!e.target.disabled) {
let content = e.target.textContent;
let id = e.target.id;
chrome.tabs.sendMessage(
tab_id,
{
from: "defaultMessages",
subject: "newMessage",
content,
id,
},
(resp) => {
document.getElementById(resp.id).disabled = true;
}
);
}
});
});
});
popup.html
<!DOCTYPE html>
<html lang="en">
<head>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<script type="text/javascript" src="./popup.js"></script>
</head>
<body>
<div class="container">
<div class="card mt-1" style="width: 15rem;">
<div class="card-body">
<div class="btn-group-vertical">
<button type="button" class="btn btn-outline-primary" id="hello">Hello</button>
<button type="button" class="btn btn-outline-primary" id="hi">Hi</button>
<button type="button" class="btn btn-outline-primary" id="how">How are you doing?</button>
</div>
</div>
</div>
</div>
</body>
</html>
Changing from "textContent" to "value" solved the problem, I don't know why.
content.js
chrome.runtime.sendMessage({
from: "twitch",
subject: "chat",
});
chrome.runtime.onMessage.addListener((msg, sender, response) => {
if (msg.from === "defaultMessages" && msg.subject === "newMessage") {
console.log(msg);
let input = document.querySelectorAll("textarea")[0];
let buttons = document.querySelectorAll("button");
input.value = msg.content; // <<<<<<<<<<<<<<<<<<<<<< this
input.dispatchEvent(new Event("input", { bubbles: true }));
buttons.forEach((button) => {
if (button?.dataset?.aTarget == "chat-send-button") {
button.dispatchEvent(new Event("click", { bubbles: true }));
}
});
response({
from: "twitch",
subject: "messageSent",
id: msg.id,
});
}
});
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!"
})
}
}
}
This is the last step in building my extension and I am seriously stuck. I have researched chrome.local.storage, but can't get it to work.
chrome.storage.local needed to save the checkbox.checked value and if the popup is closed and is reopened the checkbox.checked can be unchecked and the CSS removed.
Updated to use HTML checked in place of aria-checked as recommended by wOxxOm
chrome.storage.local
document.addEventListener('DOMContentLoaded', function () {
chrome.storage.local.get(['enabled'], function (result) {
if (result.enabled != null) {
a.checked = result.enabled;
}
});
chrome.storage.local.set({
enabled: a.checked
}, function () {
//is this where I place the checkbox function. I can't get this to work at all
`enter code here`//a.addEventListener("click", async () => {...
});
});
popup.js
let a = document.getElementById("headings");
a.addEventListener("click", async () => {
let [tab] = await chrome.tabs.query({
active: true,
currentWindow: true
});
if (a.checked) {
try {
await chrome.scripting.insertCSS({
target: {
tabId: tab.id,
},
files: ["css/headings.css"],
});
} catch (err) {
console.error(`failed to insert headings CSS: ${err}`);
}
} else {
try {
await chrome.scripting.removeCSS({
target: {
tabId: tab.id,
},
files: ["css/headings.css"],
});
} catch (err) {
console.error(`failed to remove headings CSS: ${err}`);
}
}
});
popup.html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="css/popup.css">
</head>
<body>
<div class="dimensions" id="dimensions">
<fieldset>
<legend>Options</legend>
<ul>
<li><label for="headings" class="switch--label">
<span class="label">Headings</span>
</label>
<input id="headings" type="checkbox" name="chkAll">
</li>
</ul>
</fieldset>
</div>
<script src="popup.js"></script>
</body>
</html>
chrome.storage.local.set({key: value}, function() {
console.log('Value is set to ' + value);
});
chrome.storage.local.get(['key'], function(result) {
console.log('Value currently is ' + result.key);
});
as you see in the code when you use a string key you don't use "" and if use a variable you put it in [] like this ['key']
I hope this is the problem
document.addEventListener('DOMContentLoaded', function () {
chrome.storage.local.get(['enabled'], function (result) {
if (result.enabled != null) {
a.checked = result.enabled;
}
});
chrome.storage.local.set({
enabled: a.getAttribute('aria-checked') === 'true'
}, function () {
});
});
I find this in chrome storage documentation.
You must declare the "storage" permission in the extension manifest to use the storage API. For example:
{
"name": "My extension",
...
"permissions": [
"storage"
],
...
}
Updated code as recommended by wOxxOm which works for any others searching a solution to Chrome storage.
chrome.storage.local - get
document.addEventListener('DOMContentLoaded', function () {
chrome.storage.local.get(['enabled'], function (result) {
if (result.enabled != null) {
a.checked = result.enabled;
}
});
});
popup.js
a.addEventListener("click", async () => {
let [tab] = await chrome.tabs.query({
active: true,
currentWindow: true
});
chrome.storage.local.set({
enabled: a.checked
}, function () {
if (a.checked) {
try {
chrome.scripting.insertCSS({
target: {
tabId: tab.id,
},
files: ["css/headings.css"],
});
} catch (err) {
console.error(`failed to insert headings CSS: ${err}`);
}
} else {
try {
chrome.scripting.removeCSS({
target: {
tabId: tab.id,
},
files: ["css/headings.css"],
});
} catch (err) {
console.error(`failed to remove headings CSS: ${err}`);
}
}
});
});
popup.html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="css/popup.css">
</head>
<body>
<div class="dimensions" id="dimensions">
<fieldset>
<legend>Options</legend>
<ul>
<li><label for="headings" class="switch--label">
<span class="label">Headings</span>
</label>
<input id="headings" type="checkbox" name="chkAll">
</li>
</ul>
</fieldset>
</div>
<script src="popup.js"></script>
</body>
</html>
The Manifest V3 JavaScript Chrome extension only has one button. When the button is clicked the browser should go to multiple pages and perform an action on each page before going to the next one.
What's currently happening, is the browser jumping to the last page and not performing any action.
How to fix my code, so that the extension visits each page one by one (in the same tab), then waits on the page until a button is detected, clicks the button, checks if the button is clicked, and then goes to the next page?
Manifest.json:
{
"name": "Twitch Follower",
"version": "0.1",
"manifest_version": 3,
"description": "Automatically follows Twitch users.",
"action": {
"default_popup": "popup.html"
},
"permissions": [
"activeTab",
"tabs",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"host_permissions": [
"https://www.twitch.tv/*"
]
}
popup.html:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css#2/out/water.css">
</head>
<body>
<button id="start">Start</button>
<script src="popup.js"></script>
</body>
</html>
popup.js:
start.addEventListener("click", async () => {
chrome.runtime.sendMessage({ start: true });
});
background.js:
const list = ["https://www.twitch.tv/hello1", "https://www.twitch.tv/hello2"];
function clickFollow() {
document.querySelector("button[data-a-target='follow-button']").click();
}
function handleFollowButton(followButton) {
setTimeout(clickFollow, 1000);
}
// Check for a button
async function checkButton() {
const findButton = new Promise((resolve, reject) => {
const observerConfig = { attributes: true, attributeOldValue: true };
const observer = new MutationObserver((mutations) => {
const followButton = document.querySelector("button[data-a-target='follow-button']");
const unfollowButton = document.querySelector("button[data-a-target='unfollow-button']");
// If follow button is found, click it
if (followButton) {
handleFollowButton(followButton);
}
// If unfollow button is found, end observing and continue
if (unfollowButton) {
unfollowButtonFound = True;
observer.disconnect();
resolve;
}
});
window.setTimeout(() => {
if (!unfollowButtonFound) {
reject;
}
}, 5000);
observer.observe(document, {
childList: true,
subtree: true,
});
});
await findButton;
return true;
}
// When the start button is clicked
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
if (request.start === true) {
// Loop through every link
for (const link of list) {
console.log(link);
// Open the link and inject the script
chrome.tabs.update({ url: link }, (tab) => {
chrome.scripting.executeScript(
{
target: { tabId: tab.id },
func: checkButton,
},
(result) => {
console.log(results);
}
);
});
}
}
});
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';
Hi I need to know how to access contents of a pop up window in chrome extension.Presently I have set my website as chrome extension.What I need is that when I install the extension a pop up window should be opened asking for username and password.When the user enters his username and password it should gets stored in a local Storage.Currently I am able to open a pop up window.But I don't know how to store the name which the user enters as username.Anyone please help me.I can't figure out how to do it.
Here is manifest.json
{
"name": "Calpine Extension",
"version": "1.0",
"description": "Log on to calpinemate",
"manifest_version": 2,
"browser_action": {
"default_icon": "icon_128.png"
},
"background": {
"persistent": false,
"scripts": ["background.js"]
},
"browser_action": {
"default_title": "Test Extension",
"default_icon": "calpine_not_logged_in.png"
},
"permissions": [
"*://blog.calpinetech.com/test/index.php",
"alarms",
"notifications"
],
"web_accessible_resources": [
"/icon_128.png"]
}
Here is background.js
var myNotificationID = null;
var oldChromeVersion = !chrome.runtime;
function getGmailUrl() {
return "http://calpinemate.com/";
}
function isGmailUrl(url) {
return url.indexOf(getGmailUrl()) == 0;
}
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.query({
url: "http://calpinemate.com/*",
currentWindow: true
},
function(tabs) {
if (tabs.length > 0) {
var tab = tabs[0];
console.log("Found (at least one) Gmail tab: " + tab.url);
console.log("Focusing and refreshing count...");
chrome.tabs.update(tab.id, { active: true });
updateIcon();
}
else {
console.log("Could not find Gmail tab. Creating one...");
chrome.tabs.create({ url: getGmailUrl() });
updateIcon();
}
});
});
function onInit() {
console.log('onInit');
updateIcon();
if (!oldChromeVersion) {
chrome.alarms.create('watchdog', {periodInMinutes:5});
}
}
function onAlarm(alarm) {
console.log('Got alarm', alarm);
if (alarm && alarm.name == 'watchdog') {
onWatchdog();
}
else {
updateIcon();
}
}
function onWatchdog() {
chrome.alarms.get('refresh', function(alarm) {
if (alarm) {
console.log('Refresh alarm exists. Yay.');
}
else {
console.log('Refresh alarm doesn\'t exist!? ' +
'Refreshing now and rescheduling.');
updateIcon();
}
});
}
if (oldChromeVersion) {
updateIcon();
onInit();
}
else {
chrome.runtime.onInstalled.addListener(onInit);
chrome.alarms.onAlarm.addListener(onAlarm);
}
function updateIcon(){
var req = new XMLHttpRequest();
req.addEventListener("readystatechange", function() {
if (req.readyState == 4) {
if (req.status == 200) {
var item=req.responseText;
if(item==1){
chrome.browserAction.setIcon({path:"calpine_logged_in.png"});
chrome.browserAction.setBadgeBackgroundColor({color:[190, 190, 190, 230]});
chrome.browserAction.setBadgeText({text:""});
}
else{
chrome.browserAction.setIcon({path:"calpine_not_logged_in.png"});
chrome.browserAction.setBadgeBackgroundColor({color:[190, 190, 190, 230]});
chrome.browserAction.setBadgeText({text:""});
chrome.notifications.create(
'id1',{
type: 'basic',
iconUrl: '/icon_128.png',
title: 'Calpinemate',
message: 'Hello calpiner',
buttons: [{ title: 'Mark',
iconUrl: '/tick.jpg'
},{ title: 'Ignore',
iconUrl: '/cross.jpg'}],
priority: 0},
function(id) { myNotificationID = id;}
);
chrome.notifications.onButtonClicked.addListener(function(notifId, btnIdx) {
if (notifId === myNotificationID) {
if (btnIdx === 0) {
window.open("http://www.calpinemate.com/");
} else if (btnIdx === 1) {
notification.close();
}
}
});
chrome.notifications.onClosed.addListener(function() {
notification.close();
});
}
}
else {
// Handle the error
alert("ERROR: status code " + req.status);
}
}
});
req.open("GET", "http://blog.calpinetech.com/test/index.php", true);
req.send(null);
}
function login() {
/* First retrieve the credentials */
chrome.storage.local.get(['username', 'password'], function(items) {
var user = items.username;
var pass = items.password;
if (!user || !pass) {
/* Missing credentials. Prompt the user. */
chrome.windows.create({ url : "test.html" });
return;
}
/* Log-in */
// ...code that logs the user in
});
}
Here is test.html
<html>
<head>
<script type="text/javascript">
function log(){
var uname=document.getElementById('name');
document.getElementById('pp').innerHTML = uname;
}
</script>
</head>
<body>
<form name="userinfo" id="userinfo">
username :
<input id="name" type="text" name="username"/><br><br>
password :
<input type="password" name="password"/><br><br>
<input type="button" value="Log In" onclick="log()"/>
<p id="pp"></p>
</form>
</body>
</html>
test.js
window.addEventListener('DOMContentLoaded', function() {
var user = document.querySelector('input#user');
var pwd = document.querySelector('input#pass');
var form = document.querySelector('form#userinfo');
form.addEventListener('submit', function(evt) {
evt.preventDefault();
var userStr = user.value;
var pwdStr = pwd.value;
if ((userStr.length === 0) || (passStr.length === 0)) {
alert('Please, specify both Username and Password !');
return;
}
chrome.runtime.getBackgroundPage(function(bgPage) {
bgPage.login(userStr,pwdStr); });
window.close();
});
});
Due to the Content Security Policy (CSP), inline scripts won't be executed. You should move the code and the event bindings to an external JS file and use the chrome.storage API to store the username and password.
In test.html:
<html>
<head>
<script type="text/javascript" src="test.js"></script>
</head>
<body>
<form id="userinfo">
<label for="user">Username:</label>
<input type="text" id="user" />
<br />
<label for="pass">Password:</label>
<input type="password" id="pass" />
<br />
<br />
<input type="button" id="login" value="Log In" />
</form>
</body>
</html>
In test.js:
window.addEventListener('DOMContentLoaded', function() {
var user = document.querySelector('input#user');
var pass = document.querySelector('input#pass');
var login = document.querySelector('input#login');
login.addEventListener('click', function() {
var userStr = user.value;
var passStr = pass.value;
/* Validate input */
if ((userStr.length === 0) || (passStr.length === 0)) {
alert('Please, specify both Username and Password !');
return;
}
/* Store the data */
chrome.storage.local.set({
username: userStr,
password: passStr,
}, function() {
if (chrome.runtime.lastError) {
/* An error occurred. Unable to proceed. */
// ...handle the error, e.g. inform the user
return;
}
/* Do whatever youneed to, e.g. log-in the user */
chrome.runtime.getBackgroundPage(function(bgPage) {
bgPage.login();
});
});
});
});
In background.js:
...
function login() {
/* First retrieve the credentials */
chrome.storage.local.get(['username', 'password'], function(items) {
var user = items.username;
var pass = items.password;
if (!user || !pass) {
/* Missing credentials. Prompt the user. */
chrome.windows.create({ url : "test.html" });
return;
}
/* Log-in */
// ...code that logs the user in
});
}
(Using chrome.storage.sync (instead of .local) will sync the data across the user's devices if (s)he enables that option from the account settings.)
Please, note:
Confidential user information should not be stored! The storage area isn't encrypted.