Chrome extension working on two tabs - javascript

I am trying to create a Chrome extension that retrieves some content from the DOM of the current tab, then opens a new tab, and performs an operation on this new tab using the previously retrieved content.
I am attempting to use callbacks to perform this sequence, but I can't figure out a proper way to do it. Now I am able to execute the first script, and get the content from the current tab DOM: what I still can't do is passing this content to the second script, utilities2.js, to be executed on the new tab.
Here's my current code:
popup.html
<!doctype html>
<html>
<head><title>Extension</title></head>
<body>
<button id="clickactivity">Click here</button>
<script src="popup.js"></script>
</body>
</html>
popup.js
function injectTheScript() {
var tempStorage;
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.executeScript(tabs[0].id, {file: "content_script.js"}, function(results){
tempStorage = results[0];
});
chrome.tabs.create({"url":"http://www.google.com","selected":true}, function(tab) {
});
});
}
document.getElementById('clickactivity').addEventListener('click', injectTheScript);
contentScript.js
function execContentScript() {
var code = document.getElementById('textarea').value;
return code;
}
execContentScript();
manifest.json
{
"manifest_version": 2,
"name": "My extension",
"description": "My extension",
"version": "1.0",
"permissions": ["tabs", "<all_urls>","activeTab"],
"browser_action": {
"default_popup": "popup.html"
}
}

You can store the content in the local storage of Chrome extension and after navigating to next tab, you can get the content from local storage of Chrome.
The following code may help you in this case:
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);
});
If you want to learn more about the local storage of Chrome, please read from here.
Edit:
I think you don't have idea of background scripts in the Chrome extension. Background scripts which have highest privileges to run the Chrome commands.
The complete workflow will be as follows:
popup.js is included as script
When you click button, injectTheScript function will be called from popup.js
Then it will get the element id from page and then send the request to background.js
Then background.js will save the value to Chrome storage
Then background.js will create new tab
After successful creation of tab, it will execute callback function and get the value from storage of Chrome
The following is the complete code of working extension:
manifest.json
{
"manifest_version": 2,
"name": "My extension",
"description": "My extension",
"version": "1.0",
"permissions": ["tabs", "<all_urls>","activeTab","storage"],
"browser_action": {
"default_popup": "popup.html"
},
"background": {
"scripts": ["background.js"],
"persistent": false
}
}
popup.html
<!doctype html>
<html>
<head><
title>Extension</title>
</head>
<body>
<button id="clickactivity">Click here</button>
<script src="popup.js"></script>
</body>
</html>
popup.js
function injectTheScript() {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
var code="abc";
//var code = document.getElementById('textarea').value;
chrome.runtime.sendMessage({type: "navigate",value:code}, function(response) {
console.log('code saved to local storage');
});
});
}
document.getElementById('clickactivity').addEventListener('click', injectTheScript);
background.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.type == "navigate"){
chrome.storage.sync.set({key: request.value}, function() {
console.log('Background got request for saving the code '+request.value+' to local storage');
chrome.tabs.create({"url":"http://www.google.com","selected":true}, function( tab) {
console.log('navigated to new tab');
chrome.storage.sync.get(['key'], function(result) {
console.log('new tab key is '+result.key);
});
});
});
}
});
You don't need utilities2.js and content_script.js.
I've tested the code and it's working according to your requirements.
You can not see the console output if anything is consoled in background page. To see the output on console (for anything in background), open the link chrome://extensions
and click the button Background Page as following
A new window will appear as following

Related

How to edit HTML of popup.html dynamically (Google Chrome Extension)? [duplicate]

I'm trying to create a Chrome extenstion that allows the user to get the selected text of a web page after clicking on a button and then log this text in the console.
But my code is working only if the text is selected from the HTML pop-up.
If i select a text from a random webpage and then click the "Save" button, then a blank line is printed in the console.
I guess my content.js file is not able to interact with the web page when the extension popup is displayed but I don't know how to resolve that. I know there are other similar questions but nothing I tried (message passing between different .js files for example) worked.
Here are my files :
manifest.json :
{
"manifest_version": 3,
"version": "1.0",
"name": "test",
"action": {
"default_popup": "index.html"
},
"permissions": [
"tabs",
"notifications"
],
"content_scripts": [
{ "matches": ["<all_urls>"],
"js" : ["content.js"]}
],
"background":
{
"service_worker": "background.js"
}}
index.html :
<html>
<head>
<link rel="stylesheet" href="index.css">
</head>
<body>
<p>Just some text.</p>
<button id="save-btn">SAVE SELECTION</button>
<script src="content.js"></script>
</body>
</html>
content.js :
const saveBtn = document.getElementById("save-btn")
saveBtn.addEventListener("click", function(){
console.log(window.getSelection().toString())
})
Remove content.js from index.html. Content scripts are for web pages, not for extension pages such as the popup.
Create index.js and load it in index.html:
<script src="index.js"></script>
</body>
index.js:
document.getElementById("save-btn").onclick = async () => {
const [tab] = await chrome.tabs.query({active: true, currentWindow: true});
let result;
try {
[{result}] = await chrome.scripting.executeScript({
target: {tabId: tab.id},
function: () => getSelection().toString(),
});
} catch (e) {
return; // ignoring an unsupported page like chrome://extensions
}
document.body.append('Selection: ' + result);
};
edit manifest.json to allow code injection in the active tab on click:
"permissions": ["scripting", "activeTab"]
Note that the popup is a separate window so it has its own separate devtools and console: right-click inside the popup and select "inspect" in the menu.

Getting selected text in a Chrome extension

I'm trying to create a Chrome extenstion that allows the user to get the selected text of a web page after clicking on a button and then log this text in the console.
But my code is working only if the text is selected from the HTML pop-up.
If i select a text from a random webpage and then click the "Save" button, then a blank line is printed in the console.
I guess my content.js file is not able to interact with the web page when the extension popup is displayed but I don't know how to resolve that. I know there are other similar questions but nothing I tried (message passing between different .js files for example) worked.
Here are my files :
manifest.json :
{
"manifest_version": 3,
"version": "1.0",
"name": "test",
"action": {
"default_popup": "index.html"
},
"permissions": [
"tabs",
"notifications"
],
"content_scripts": [
{ "matches": ["<all_urls>"],
"js" : ["content.js"]}
],
"background":
{
"service_worker": "background.js"
}}
index.html :
<html>
<head>
<link rel="stylesheet" href="index.css">
</head>
<body>
<p>Just some text.</p>
<button id="save-btn">SAVE SELECTION</button>
<script src="content.js"></script>
</body>
</html>
content.js :
const saveBtn = document.getElementById("save-btn")
saveBtn.addEventListener("click", function(){
console.log(window.getSelection().toString())
})
Remove content.js from index.html. Content scripts are for web pages, not for extension pages such as the popup.
Create index.js and load it in index.html:
<script src="index.js"></script>
</body>
index.js:
document.getElementById("save-btn").onclick = async () => {
const [tab] = await chrome.tabs.query({active: true, currentWindow: true});
let result;
try {
[{result}] = await chrome.scripting.executeScript({
target: {tabId: tab.id},
function: () => getSelection().toString(),
});
} catch (e) {
return; // ignoring an unsupported page like chrome://extensions
}
document.body.append('Selection: ' + result);
};
edit manifest.json to allow code injection in the active tab on click:
"permissions": ["scripting", "activeTab"]
Note that the popup is a separate window so it has its own separate devtools and console: right-click inside the popup and select "inspect" in the menu.

How to get input field value from Chrome Extension

I am working to learn how to build a Google Chrome Extension. I have a contact form on a webpage that I'm using for testing. I'm trying to create an extension that will read the input field values from that form. At this time, I have:
manifest.json
{
"manifest_version": 2,
"name": "Contact Form Friend",
"description": "This extension gets contact form info.",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"activeTab",
"<all_urls>"
]
}
popup.html
<!doctype html>
<html>
<head>
<title>Getting Started Extension's Popup</title>
<style type="text/css">
body {
margin: 10px;
white-space: nowrap;
}
h1 {
font-size: 15px;
}
#container {
align-items: center;
display: flex;
justify-content: space-between;
}
</style>
<script src="popup.js"></script>
</head>
<body>
<h1>Info</h1>
<div id="info">
</div>
</body>
</html>
popup.js
function getHost() {
return document.documentElement;
}
document.addEventListener('DOMContentLoaded', () => {
const infoDisplay = document.getElementById('info');
const scriptToRun = `(${getHost})()`;
// Run the script in the context of the tab
chrome.tabs.executeScript({
code: scriptToRun
}, (result) => {
var values = [];
var inputFields = result.getElementsByTagName('input');
infoDisplay.innerHTML = 'inputs: ' + inputFields.length;
for (var i = 0; i < inputFields.length; i++) {
values.push(inputFields[i].value);
}
infoDisplay.innerHTML += '<br />fields: ' + values.length;
});
});
When I run this, it acts like it can't access the input fields from the web page the user is on (not the extension's web page). What am I doing wrong? Am I missing a permission? I don't believe so. However, it's not working and I'm not sure why.
Thank you for your help.
As reported by Gabriel Bleu you need a content script to interact with web pages inside Chrome tabs.
Here below is an example of Chrome Extension where a content script exposes two "commands" to popup.js.
See code comments for explanation.
manifest.json (similar to yours)
{
"name": "My ext",
"version": "0.1",
"description": "my desc",
"permissions": [
"activeTab",
"<all_urls>"
],
"icons": {
"128": "icon-128.png"
},
"browser_action": {
"default_title": "My ext",
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"manifest_version": 2,
"content_security_policy": "script-src 'self' https://ajax.googleapis.com object-src 'self'"
}
popup.html (similar to yours except for jQuery usage for easy DOM manipulation)
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script type="application/javascript" charset="utf-8" src="popup.js"></script>
</head>
<body class="body-popup">
<button id="btn_check" class="btn btn-warning">check current tab</button>
<hr>
<div id="pg_url"></div>
<hr>
<div id="log"></div>
</body>
</html>
popup.js
$(function() {
$('#btn_check').click(function() { checkCurrentTab(); });
});
function checkCurrentTab() {
chrome.tabs.query({'active': true, 'lastFocusedWindow': true}, function (tabs) {
var url = tabs[0].url;
console.log("checkCurrentTab: "+url);
$(".pg_url").text(url);
// request content_script to retrieve title element innerHTML from current tab
chrome.tabs.sendMessage(tabs[0].id, "getHeadTitle", null, function(obj) {
console.log("getHeadTitle.from content_script:", obj);
log("from content_script:"+obj);
});
});
}
document.addEventListener('DOMContentLoaded', function () {
chrome.windows.getCurrent(function (currentWindow) {
chrome.tabs.query({active: true, windowId: currentWindow.id}, function(activeTabs) {
// inject content_script to current tab
chrome.tabs.executeScript(activeTabs[0].id, {file: 'content_script.js', allFrames: false});
});
});
});
function log(txt) {
var h = $("#log").html();
$("#log").html(h+"<br>"+txt);
}
content_script.js
// you will see this log in console log of current tab in Chrome when the script is injected
console.log("content_script.js");
chrome.runtime.onMessage.addListener(function(cmd, sender, sendResponse) {
console.log("chrome.runtime.onMessage: "+cmd);
switch(cmd) {
case "getHtml":
// retrieve document HTML and send to popup.js
sendResponse({title: document.title, url: window.location.href, html: document.documentElement.innerHTML});
break;
case "getHeadTitle":
// retrieve title HTML and send to popup.js
sendResponse(document.getElementsByTagName("title")[0].innerHTML);
break;
default:
sendResponse(null);
}
});
P.S.: obviously jQuery is not mandatory
Here is the error you receive:
Error in response to tabs.executeScript: TypeError: result.getElementsByTagName is not a function
at Object.chrome.tabs.executeScript [as callback] (chrome-extension://lmaefdnejmkjjmgalgfofdbobhapfmoh/popup.js:15:32)
at HTMLDocument.document.addEventListener (chrome-extension://lmaefdnejmkjjmgalgfofdbobhapfmoh/popup.js:10:17)
Instead of trying to get the DOM of the current tab in popup.js, I would suggest to use a content script to do the task and send the result as a message to the popup.
manifest.json
{
"manifest_version": 2,
"name": "Contact Form Friend",
"description": "This extension gets contact form info.",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"activeTab",
"<all_urls>"
],
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["content.js"]
}
]
}
In the manifest.json file you must add the content script and set the URLs of the sites in which you want to inject the script.
popup.js
document.addEventListener('DOMContentLoaded', () => {
const infoDisplay = document.getElementById('info');
window.addEventListener('DOMContentLoaded', function () {
chrome.tabs.query({active: true, currentWindow: true}, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, {}, function (result) {
infoDisplay.innerHTML = result
});
});
});
});
In popup.js send a request to the content script, to get the number of input fields and insert the response into the div.
content.js
chrome.runtime.onMessage.addListener(function (msg, sender, response) {
var values = [];
var inputFields = document.getElementsByTagName('input');
var result = 'inputs: ' + inputFields.length;
for (var i = 0; i < inputFields.length; i++) {
values.push(inputFields[i].value);
}
result += '<br />fields: ' + values.length;
response(result)
});
Create a new file, named content.js. This file will be injected into the webpage and it listens to messages from the popup.js. When a message arrives it computes the response and sends it back to popup.js.
To learn more about content scripts, check out the documentation.
If your extension needs to interact with web pages, then it needs a content script. A content script is some JavaScript that executes in the context of a page that's been loaded into the browser. Think of a content script as part of that loaded page, not as part of the extension it was packaged with (its parent extension).
https://developer.chrome.com/docs/extensions/mv3/content_scripts/
The main problem of your approach is that you try to send a DOM tree via message/sendResponse from content script to popup/background in order to process it there. You cannot send DOM trees via message/sendResponse.
A better approach would be to handle the DOM in the content script and send back the desired information (in your case, the input values) to the popup/background page.
One possible way to do it would be:
popup.js
document.addEventListener('DOMContentLoaded', () => {
const infoDisplay = document.getElementById('info');
const scriptToRun = `
var values = [];
var inputFields = document.getElementsByTagName('input');
for (var i = 0; i < inputFields.length; i++) {
values.push(inputFields[i].value);
}
values;`; //all this code will be run on the tab page
//and the array "values" will be returned.
chrome.tabs.executeScript({
code: scriptToRun
}, (result) => {
infoDisplay.innerHTML = `There are: ${result[0].length} inputs, with these values: <ol><li>${result[0].join("<li>")}`;
});
});

Chrome extension to change DOM with a button in extension popup

i am completely new to chrome extension development.
I am trying to change the DOM (append data to active webpage) when clicked on a button in the extension popup. How is this possible.
the manifest file
{
"manifest_version": 2,
"name": "test 2",
"description": "test ext 2",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"content_scripts": [
{
"matches": ["http://*/*","https://*/*"],
"js": ["jquery.min.js", "main.js"]
}
],
"permissions": [
"activeTab"
]
}
suppose if the popup.html file is
<!doctype html>
<html>
<head>
<title>test extension 2</title>
<script src="popup.js"></script>
</head>
<body>
<a id="button">button</a>
</body>
</html>
and when i click on #button, i want to execute some jquery code in main.js file which will append some data to the active webpage.
Thank you.
Use Programmatic injection. You could register event listener in popup.js and call chrome.tabs.executeScript to inject some js code/file to current active tab. This requires host permissions.
popup.js
document.getElementById('button').addEventListener('click', function() {
chrome.tabs.query({ active: true, currentWindow: true}, function(activeTabs) {
// WAY 1
chrome.tabs.executeScript(activeTabs[0].id, { code: 'YOUR CODE HERE' });
});
});
Use Message Passing. You could call chrome.tabs.sendMessage in popup.js and listen to that via chrome.runtime.onMessage in content.js.
popup.js
// WAY 2 (Replace WAY1 with following line)
chrome.tabs.sendMessage(activeTabs[0].id, { action: 'executeCode' });
content.js
chrome.runtime.onMessage.addListener(function(request) {
if(request.action === 'executeCode') {
// YOUR CODE HERE
}
});
Use Storage. You could call chrome.storage.local.set in popup.js and listen to storage change in content.js via chrome.storage.onChanged.
popup.js
// WAY 3 (Replace WAY1 with following line)
chrome.storage.local.set({ action: 'executeCode' });
content.js
chrome.storage.onChanged.addListener(function(changes) {
var action = changes['action'];
if(action.newValue === 'executeCode') {
// YOUR CODE HERE
}
});

Getting DOM in Chrome extension popup?

I'm trying to create a Chrome extension that displays the current page's DOM in a popup.
As a warmup, I tried putting a string in getBackgroundPage().dummy, and this is visible to the popup.js script. But, when I try saving the DOM in getBackgroundPage().domContent, popup.js just sees this as undefined.
Any idea what might be going wrong here?
I looked at this related post, but I couldn't quite figure out how to use the lessons from that post for my code.
Code:
background.js
chrome.extension.getBackgroundPage().dummy = "yo dummy";
function doStuffWithDOM(domContent) {
//console.log("I received the following DOM content:\n" + domContent);
alert("I received the following DOM content:\n" + domContent);
//theDomContent = domContent;
chrome.extension.getBackgroundPage().domContent = domContent;
}
chrome.tabs.onUpdated.addListener(function (tab) {
//communicate with content.js, get the DOM back...
chrome.tabs.sendMessage(tab.id, { text: "report_back" }, doStuffWithDOM); //FIXME (doesnt seem to get into doStuffWithDOM)
});
content.js
/* Listen for messages */
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
/* If the received message has the expected format... */
if (msg.text && (msg.text == "report_back")) {
/* Call the specified callback, passing
the web-pages DOM content as argument */
//alert("hi from content script"); //DOESN'T WORK ... do we ever get in here?
sendResponse(document.all[0].outerHTML);
}
});
popup.js
document.write(chrome.extension.getBackgroundPage().dummy); //WORKS.
document.write(chrome.extension.getBackgroundPage().domContent); //FIXME (shows "undefined")
popup.html
<!doctype html>
<html>
<head>
<title>My popup that should display the DOM</title>
<script src="popup.js"></script>
</head>
</html>
manifest.json
{
"manifest_version": 2,
"name": "Get HTML example w/ popup",
"version": "0.0",
"background": {
"persistent": false,
"scripts": ["background.js"]
},
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["content.js"]
}],
"browser_action": {
"default_title": "Get HTML example",
"default_popup": "popup.html"
},
"permissions": ["tabs"]
}
You got the syntax of chrome.tabs.onUpdated wrong.
In background.js
chrome.tabs.onUpdated.addListener(function(id,changeInfo,tab){
if(changeInfo.status=='complete'){ //To send message after the webpage has loaded
chrome.tabs.sendMessage(tab.id, { text: "report_back" },function(response){
doStuffWithDOM(response);
});
}
})

Categories

Resources