Chrome extension - injecting script and running it on inactive tab - javascript

I have Chrome extension that injects a script into a page on load. If it is a certain page, it opens a set of links on that page in new tabs and when those tabs are loaded the injected script submits a form on the tab. The issue is that the injection is not happening on tabs that are not current. It works only on the current tab.
A simplified version of my code:
manifest.json
{
"name": "name",
"version": "0.0.1",
"manifest_version": 2,
"description": "Doing stuff",
"background": {
"scripts": [
"background.js"
],
"persistent": true
},
"browser_action": {
"default_title": "Bot"
},
"permissions": [
"https://*.url.com/*",
"*://*/*",
"tabs"
]
}
background.js :
chrome.tabs.onUpdated.addListener( function (tabId, changeInfo, tab) {
if(changeInfo.status == 'complete') {
try {
chrome.tabs.executeScript(tab.ib, {
file: 'inject.js'
});
} catch(err) {
console.log(" extension cannot run on the chrome:// page ")
}
}
})
inject.js :
(function() {
function page1() {
links = document.querySelectorAll("a.link_class");
for(var link in links) {
link.setAttribute("target", "_blank");
link.click();
}
}
function page2() {
// this does not run in tabs that are inactive
// in order to make it run, i have to click in the inactive tab and reload it manually
document.querySelector("input[name='field_to_update']").text = "setting the field value";
button = document.querySelector("input[name='button']");
button.click();
}
if(document.querySelector("div.page1") != null) {
page1();
} else {
page2();
}
})();

Related

chrome extension to open in new tab instead of popup

I am trying to get some data from current tab to my extension. It's working fine if I change it to popup style but I am looking for the same functionality to open it in a new tab on extension click. Can anybody guide on this how I can achieve this? Thanks in advance
manifest.json.js
{
"manifest_version": 2,
"name": "Hello World",
"description": "A simple page-scraping extension for Chrome",
"version": "1.0",
"author": "#thomasforth",
"background": {
"scripts": ["popup.js"],
"persistent": true
},
"permissions": [
"tabs",
"http://*/",
"*://*/",
"activeTab"
],
"browser_action": {
"default_icon": "logo.png"
}
}
payload.js
// send the page title as a chrome message
chrome.runtime.sendMessage(document.title);
console.log(document.title)
popup.js
// Inject the payload.js script into the current tab after the popout has loaded
window.addEventListener('load', function (evt) {
chrome.extension.getBackgroundPage().chrome.tabs.executeScript(null, {
file: 'payload.js'
});;
});
// Listen to messages from the payload.js script and write to popout.html
chrome.browserAction.onClicked.addListener(function (tab) {
chrome.tabs.create({ url: "popup.html", selected: false })
chrome.runtime.onMessage.addListener(function (message) {
document.getElementById('pagetitle').innerHTML = message;
})
})
This the is error which I am getting
Use the id of the tab where onClicked was triggered and pass it to popup script using its URL:
chrome.browserAction.onClicked.addListener(function (tab) {
chrome.tabs.create({
url: `popup.html?tabId=` + tab.id,
selected: false,
});
});
// popup.js
const tabId = +new URLSearchParams(location.search).get('tabId');
chrome.tabs.executeScript(tabId, {
file: 'payload.js'
});

Chrome extension message listener fires twice

I'm working on Chrome extensions. I try to learn messaging between content and background. I develop simple project for this. But I have issue.
Basic idea is
User click button on extension popup
A function (bot.js) find image from content of tab then extension (background.js) will download it.
The issue is port.onMessage.addListener() in background.js fired twice.
When background.js sends message to contentscript.js there are two same messages in console or when I try to download in background.js (the code line "Do Something") it download the file twice.
How can I solve this problem?
popup.html
<!doctype html>
<html>
<head>
<title>Test Plugin</title>
<script src="background.js"></script>
<script src="popup.js"></script>
</head>
<body>
<h1>Test Plugin</h1>
<button id="btnStart">Button</button>
</body>
</html>
popup.js
document.addEventListener('DOMContentLoaded', function() {
var checkPageButton = document.getElementById('btnStart');
checkPageButton.addEventListener('click', function() {
GetImages("Some URL");
}, false);
}, false);
var tab_title = '';
function GetImages(pageURL){
// Tab match for pageURL and return index
chrome.tabs.query({}, function(tabs) {
var tab=null;
for(var i=0;i<tabs.length;i++){
if(tabs[i].url==undefined || tabs[i].url=="" || tabs[i]==null){}
else{
if(tabs[i].url.includes(pageURL)){
tab=tabs[i];
break;
}
}
}
if(tab!=null){
chrome.tabs.executeScript(tab.id, {
file: "bot.js"
}, function(results){
console.log(results);
});
}
});
}
bot.js
var thumbImagesCount = document.querySelectorAll('.classifiedDetailThumbList .thmbImg').length;
var megaImageURL=document.querySelectorAll('.mega-photo-img img')[0].src;
console.log(megaImageURL + " from bot.js");
port.postMessage({key:"download", text: megaImageURL});
background.js
chrome.runtime.onConnect.addListener(function (port) {
console.assert(port.name == "content-script");
port.onMessage.addListener(function(message) {
console.log(message);
if(message.key=="download"){
// Do Something
// Event fires twice
port.postMessage({key:"download", text: "OK"});
}
})
});
contentscript.js
console.log("content script loaded!");
var port = chrome.runtime.connect({name: "content-script"});
port.onMessage.addListener(function(message){
console.log(message);
});
manifest.json
{
"manifest_version": 2,
"name": "Test Extension",
"description": "This extension will download images from gallery",
"version": "1.0",
"icons": {
"16": "bot16.png",
"48": "bot48.png",
"128": "bot128.png" },
"browser_action": {
"default_icon": "bot48.png",
"default_popup": "popup.html"
},
"permissions": [
"activeTab",
"downloads",
"http://*/",
"https://*/"
],
"background": {
"persistent": false,
"scripts": ["background.js"]
},
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["contentscript.js"]
}
]
}
The background script declared in manifest.json already has its own page, a hidden background page where it runs, so you should not load it in the popup as it makes no sense in case there are listeners for API events, the background page is already listening for them. In this case the copy also creates the second listener while the popup is open.
Solution: don't load background.js in popup.
See also Accessing console and devtools of extension's background.js.

Inject content script on extension reload programmatically (chrome extension)

I am creating a chrome extension that I want to be able to enable/disable. I have successfully made a popup that does just that. The trouble is, if I reload the extension (or if the user downloads it initially) my content scripts default to being off. I could just inject the content script in the manifest.json but that results in the content script being injected for any new tab--which I do not want. The behavior should be that if you download/reload the extension, it is on by default, but then you can enable it/disable it and that applies to every new tab. I have tried to put an initialization in background.js but that does not get called at startup apparently.
manifest.json
{
"manifest_version": 2,
"name": "Rotten Tomatoes Search",
"description": "This extension searches rotten tomatoes with highlighted text",
"version": "1.0",
"browser_action": {
"default_icon": "./icons/icon_on.png",
"default_popup": "popup.html"
},
"permissions": [
"activeTab",
"<all_urls>",
"background"
],
"background": {
"scripts": ["background.js"],
"persistent": true
},
"content_scripts": [{
"js": ["jquery-1.12.3.min.js"],
"matches": ["<all_urls>"]
}]
}
background.js
var isExtensionOn = true;
chrome.tabs.executeScript({code: "console.log('backgournd hit...')"});
turnItOn();
chrome.extension.onMessage.addListener(
function (request, sender, sendResponse) {
if (request.cmd == "setOnOffState") {
isExtensionOn = request.data.value;
}
if (request.cmd == "getOnOffState") {
sendResponse(isExtensionOn);
}
});
function turnItOn() {
chrome.browserAction.setIcon({path: "./icons/icon_on.png"});
chrome.tabs.executeScript({file:"openTooltipMenu.js"});
//$('#toggle').text('disable');
}
popup.js
document.addEventListener('DOMContentLoaded', function() {
// show different text depending on on/off state (for icon, handled by having default icon)
chrome.extension.sendMessage({ cmd: "getOnOffState" }, function(currentState){
if (currentState) $('#toggle').text('disable');
else $('#toggle').text('enable');
});
// allow user to toggle state of extension
var toggle = document.getElementById('toggle')
toggle.addEventListener('click', function() {
//chrome.tabs.executeScript({code: "console.log('toggled...')"});
chrome.extension.sendMessage({ cmd: "getOnOffState" }, function(currentState){
var newState = !currentState;
// toggle to the new state in background
chrome.extension.sendMessage({ cmd: "setOnOffState", data: { value: newState } }, function(){
// after toggling, do stuff based on new state
if (newState) turnOn();
else turnOff();
});
});
})
});
function turnOn() {
chrome.browserAction.setIcon({path: "./icons/icon_on.png"});
chrome.tabs.executeScript({file:"openTooltipMenu.js"});
$('#toggle').text('disable');
}
function turnOff() {
chrome.browserAction.setIcon({path: "./icons/icon_off.png"});
chrome.tabs.executeScript({code: "$('body').off();"});
$('#toggle').text('enable');
}
popup.html
<some code>
<script src="./jquery-1.12.3.min.js"></script>
<script src="./popup.js"></script><style type="text/css"></style>
</head>
<body>
<div class="popupMenu" style="list-style-type:none">
<div class="header">Rotten Tomatoes Search</div>
<hr>
<div class="menuEntry" id="toggle"></div>
</div>
</body>
</html>
I have figured out the issue. My architectural approach was wrong. One should inject the content_script globally but check with the script whether or not something should be done. To be clearer, in the script get the status from the background page and do something based on that. Previously, I was only injecting the script once the popup was loaded or once initially when the background was initialized. Additionally, one must loop through all tabs in all windows to update the state in all tabs (if that's what one wants).

Chrome extension: Execute only on current domain name once browser action is clicked

Here is my scenario: By clicking the browser icon, I create a sidebar (html and css) next to the whole page, thus creating two columns (one is my sidebar, the other one is the actual page).
What I to achieve is having the sidebar stay when I reload the page or navigate to another page WITHIN the same domain. What I have right now is just the creation of the sidebar, but I have to click the browser action every time I navigate or reload the web page.
Manifest:
{
"name": "apdrop",
"version": "0.1",
"manifest_version": 2,
"description": "first prototype for apdrop extension",
"icons": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
},"background": {
"scripts": ["background.js"],
"persistent": false
},
"browser_action": {
"default_icon": "icons/icon19.png",
"default_title": "apdrop"
},
"permissions": [
"background",
"tabs",
"http://*/*/",
"https://*/*/"
]
}
Background.js
function injectedScript(tab, method){
chrome.tabs.insertCSS(tab.id, {file:"style.css"});
//chrome.tabs.insertCSS(tab.id, {file:"bootstrap.css"});
chrome.tabs.executeScript(tab.id, { file: 'jquery-2.1.1.min.js'});
//chrome.tabs.executeScript(tab.id, { file: 'bootstrap.min.js'});
chrome.tabs.executeScript(tab.id, { file: 'inject.js'});
}
function click(tab){
console.log("browser action clicked");
injectedScript(tab, 'click');
//alert("action button was clicked");
}
chrome.browserAction.onClicked.addListener(click);
Inject.js
var ev = $("body > *");
if (!document.getElementById('contentxf343487d32'))
{
ev.wrapAll("<div id='insidecontent65675f526567'>");
$("#insidecontent65675f526567").wrapAll("<div id='contentxf343487d32'>");
$("<div id='sidebar343gf87897fh'><div id='insidesidebar87678bbbb'><p>this is my name</p></div></div>").insertBefore("#contentxf343487d32");
}
else
{
$("#sidebar343gf87897fh").remove();
$("#insidecontent65675f526567").unwrap();
$("#insidecontent65675f526567 > div").unwrap();
}
Hope this helps clarify a bit more.
The simplest strategy would be to save state in domain's sessionStorage and have a "detector" script that re-injects your UI.
Add setting the state in your content script:
// inject.js
if (!document.getElementById('contentxf343487d32'))
{
// ...
sessionStorage["contentxf343487d32"] = true;
}
else
{
// ...
sessionStorage["contentxf343487d32"] = false;
}
Add a "detector" script:
// detect.js
if(sessionStorage["contentxf343487d32"])
{
chrome.runtime.sendMessage({injectSidebar: true});
}
Always inject the script on page load, via the manifest (and change to a better permission):
"content_scripts" : [
{
"matches": ["<all_urls>"],
"js": ["detect.js"]
}
],
"permissions": [
"background",
"tabs",
"<all_urls>"
]
In the background, inject the script upon message:
// background.js
chrome.runtime.onMessage.addListener( function (message, sender, sendResponse){
if(message.injectSidebar)
{
click(sender.tab);
}
});
If you need more persistence than sessionStorage provides, use localStorage. If you need a different logic, you can still use this skeleton of a detector signalling the background.

Run Content Script From Toolbar

I want to have a Chrome Extension that will replace text on a page. I've got all the code working on the Javascript side of things, and it runs perfectly when a page loads, the problem is I only want it to replace the text on the page when you click a button on the toolbar.
I setup a button on the toolbar but the replacement Javascript still just runs when the page loads, rather than when you click the button. Additionally at the moment when you click the toolbar button, despite it not doing anything, it still shows a flash of a popup window. All I want it to do is run the text replacement code when you click the toolbar button, without showing a popup.html box.
The code currently is as follows,
Manifest.json
{
"name": "Browser Action",
"version": "0.0.1",
"manifest_version": 2,
"description": "Show how options page works",
// Needed to retrieve options from content script
"background": "background.html",
// This is how you load Browser Action. Nearly equal to Page one.
"browser_action": {
"default_icon": "icon.png",
"popup": "popup.html"
},
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js" : ["popup.js"]
}
]
}
popup.js
function htmlreplace(a, b, element) {
if (!element) element = document.body;
var nodes = element.childNodes;
for (var n=0; n<nodes.length; n++) {
if (nodes[n].nodeType == Node.TEXT_NODE) {
var r = new RegExp(a, 'gi');
nodes[n].textContent = nodes[n].textContent.replace(r, b);
} else {
htmlreplace(a, b, nodes[n]);
}
}
}
htmlreplace('a', 'IT WORKS!!!');
popup.html - Blank
background.html
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(null, {file: "popup.js"});
});
There are a few changes you must make (most of them mentioned by rsanchez - but not all) and a couple more changes that could/should be made.
So, instead of listing things that could/should/must be changed, I will demonstrate a sample extension that does what you want.
First things first - More info on a few key concepts, related to your question/problem:
Manifest File Format
Permissions
Background pages, Event pages
Content scripts
Browser actions
Extension directory structure:
extension-root-directory/
|_____manifest.json
|_____background.js
|_____content.js
manifest.json:
{
"manifest_version": 2,
"name": "Test Extension",
"version": "0.0",
"offline_enabled": true,
"background": {
"persistent": false,
"scripts": ["./bg/background.js"]
},
"browser_action": {
"default_title": "Test Extension"
//"default_icon": {
// "19": "img/icon19.png",
// "38": "img/icon38.png"
//},
},
"permissions": [
"activeTab"
]
}
background.js:
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(tab.id, { file: "content.js" });
});
content.js:
function htmlReplace(a, b, element) {
if (!element) {
element = document.body;
}
var r = new RegExp(a, "gi");
var nodes = element.childNodes;
for (var n = 0; n < nodes.length; n++) {
if (nodes[n].nodeType == Node.TEXT_NODE) {
nodes[n].textContent = nodes[n].textContent.replace(r, b);
} else {
htmlReplace(a, b, nodes[n]);
}
}
}
htmlReplace("a", "IT WORKS !!!");
You just need to do the following changes to your manifest:
Remove the content_scripts section.
Remove the browser_action.popup entry.
Add a section: "permissions": ["activeTab"]
Change your background section to read: "background": { "scripts": ["background.js"] } and rename your file background.html to background.js

Categories