Undefined response from content script in chrome extension - javascript

I can't get a response from my content script to show up in my popup.html. When this code runs and the find button is clicked, "Hello from response!" prints, but the variable response is printed as undefined. The ultimate goal is to get the current tab's DOM into my script file so that I can parse through it. I'm using a single time message to a content script to get the DOM, but it's not being returned and is showing up as undefined. I'm looking for any help possible. Thanks.
popup.html:
<!DOCTYPE html>
<html>
<body>
<head>
<script src="script.js"></script>
</head>
<form >
Find: <input id="find" type="text"> </input>
</form>
<button id="find_button"> Find </button>
</body>
</html>
manifest.json:
{
"name": "Enhanced Find",
"version": "1.0",
"manifest_version": 2,
"description": "Ctrl+F, but better",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"tabs",
"*://*/*"
],
"background":{
"scripts": ["script.js"],
"persistent": true
},
"content_scripts":[
{
"matches": ["http://*/*", "https://*/*"],
"js": ["content_script.js"],
"run_at": "document_end"
}
]
}
script.js:
var bkg = chrome.extension.getBackgroundPage();
function eventHandler(){
var input = document.getElementById("find");
var text = input.value;
chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
var tab = tabs[0];
var url = tab.url;
chrome.tabs.sendMessage(tab.id, {method: "getDocuments"}, function(response){
bkg.console.log("Hello from response!");
bkg.console.log(response);
});
});
}
content_script.js:
var bkg = chrome.extension.getBackgroundPage();
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse){
if(request.method == "getDOM"){
sendResponse({data : bkg.document});
}else{
sendResponse({});
}
});

There are quite a few issues with your code (see my comment above).
Some suggestions/considerations first:
Do not inject your content script into all webpages. Inject programmatically and only when the user wants to search.
It might be a better idea to do the "searching" right in the content script, where you have direct access to the DOM and can manipulate it (e.g. highlight search results etc). You might need to adjust your permissions if you go for this approach, but always try to keep them to a minimum (e.g. don't use tabs where activeTab would suffice, etc).
Keep in mind that, once the popup is closed/hidden (e.g. a tab receives focus), all JS executing in the context of the popup is aborted.
If you want some sort of persistence (even temporary), e.g. remembering the recent results or last search term, you can use something like chrome.storage or localStorage.
Finally, sample code from my demo version of your extension:
Extension files organization:
extension-root-directory/
|
|_____fg/
| |_____content.js
|
|_____popup/
| |_____popup.html
| |_____popup.js
|
|_____manifest.json
manifest.json:
{
"manifest_version": 2,
"name": "Test Extension",
"version": "0.0",
"offline_enabled": true,
"content_scripts": [
{
"matches": [
"http://*/*",
"https://*/*"
],
"js": ["fg/content.js"],
"run_at": "document_end",
}
],
"browser_action": {
"default_title": "Test Extension",
"default_popup": "popup/popup.html"
}
}
content.js:
// Listen for message...
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
// If the request asks for the DOM content...
if (request.method && (request.method === "getDOM")) {
// ...send back the content of the <html> element
// (Note: You can't send back the current '#document',
// because it is recognised as a circular object and
// cannot be converted to a JSON string.)
var html = document.all[0];
sendResponse({ "htmlContent": html.innerHTML });
}
});
popup.html:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="popup.js"></script>
</head>
<body>
Search:
<input type="text" id="search" />
<input type="button" id="searchBtn" value=" Find "
style="width:100%;" />
</body>
</html>
popup.js:
window.addEventListener("DOMContentLoaded", function() {
var inp = document.getElementById("search");
var btn = document.getElementById("searchBtn");
btn.addEventListener("click", function() {
var searchTerm = inp.value;
if (!inp.value) {
alert("Please, enter a term to search for !");
} else {
// Get the active tab
chrome.tabs.query({
active: true,
currentWindow: true
}, function(tabs) {
// If there is an active tab...
if (tabs.length > 0) {
// ...send a message requesting the DOM...
chrome.tabs.sendMessage(tabs[0].id, {
method: "getDOM"
}, function(response) {
if (chrome.runtime.lastError) {
// An error occurred :(
console.log("ERROR: ", chrome.runtime.lastError);
} else {
// Do something useful with the HTML content
console.log([
"<html>",
response.htmlContent,
"</html>"
].join("\n"));
}
});
}
});
}
});
});

Related

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.

How can I run a extension with Google Chrome for Youtube?

Hello there,
I want to remove thumbnail images that appear on YouTube. I am using the following code for this.
while (true) {
$("ytd-thumbnail").remove()
}
When I paste this code into console, all thumbnail images are removed. I want it to work on the backplane by adding an extension. The code for the plug-in I'm preparing is below.
manifest.json;
{
"manifest_version": 2,
"name": "test",
"description": "test extension",
"version": "1.0",
"browser_action": {
"default_popup": "popup.html"
},
"permissions": [
"activeTab"
]
}
popup.html
<!doctype html>
<html>
<head>
<title>TEST</title>
<script src="popup.js"></script>
</head>
<body>
<h1>TEST</h1>
<button id="checkPage">Check !</button>
</body>
</html>
popup.js
document.addEventListener('DOMContentLoaded', function() {
var checkPageButton = document.getElementById('checkPage');
checkPageButton.addEventListener('click', function() {
chrome.tabs.getSelected(null, function(tab) {
d = document;
while (true) {
$("ytd-thumbnail").remove()
}
});
}, false);
}, false);
When I press the checkPage button nothing happens. But this code works when I add a console. What is the problem? Thanks in advance.
There are several issues with the extension :
You are trying to use $ i.e. jquery which is not available in your
popup.js
You are trying to access "ytd-thumbnail" dom elements which belong
to youtube page and not your popup.html. So, even if you replace $
with document.querySelector , you won't find those elements.
I created a working version that looks something like this. I have not included popup.html which is same as yours.
manifest.json
{
"manifest_version": 2,
"name": "Hello Extensions",
"description" : "Base Level Extension",
"version": "1.0",
"browser_action": {
"default_icon": "hello_extensions.png",
"default_popup": "popup.html"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["background.js"],
"all_frames" : true
}
],
"permissions": [
"activeTab",
"tabs"
]
}
2.popup.js
document.addEventListener('DOMContentLoaded', function()
{
var checkPageButton = document.getElementById('checkPage');
checkPageButton.addEventListener('click', function()
{
chrome.tabs.query({"active":true}, function (tabs){
chrome.tabs.sendMessage(tabs[0].id, "removeThumbnails", function (response) {
});
});
});
});
When the button is clicked, retrieve the active tab and send it a "removeThumbnails" message.
3.background.js
chrome.runtime.onMessage.addListener(function(message, callback) {
if (message == "removeThumbnails")
{
var elements = document.querySelectorAll("ytd-thumbnail");
elements.forEach(a => a.parentNode.removeChild(a));
}
});
background.js is content script and runs in youtube page. It can now access all the dom elements in youtube page. When we receive a "removeThumbnails" message , get all ytd-thumbnail elements and remove them from page.

Fill forms with a data from popup.html

I have been trying to create an extension that fills a form with data from a popup, I'm a bit confused regarding the use of "background" and "content" files, I don't think I need one. Here is my code:
Manifest:
{
"name": "Browser action for Form",
"description": "Fill a form with data from the popup",
"version": "1.0",
"permissions": [
"tabs", "http://*/*", "https://*/*"
],
"browser_action": {
"default_title": "Form Test",
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"content_scripts": [
{
"matches": ["https://the-site-with-a-form.com/*"],
"js": ["jquery-3.1.1.min.js", "content.js"]
}
],
"manifest_version": 2
}
popup.html
<!doctype html>
<html>
<head>
<title>Form</title>
<script src="popup.js"></script>
</head>
<body>
<form>
<textarea id="txtArea"></textarea>
<input type="button" id="btn1" value="Run">
</form>
</body>
</html>
popup.js
function click(){
var text = document.getElementById("txtArea")
chrome.tabs.sendMessage(
tabs[0].id,
{from: 'popup', subject: 'DOMInfo',data1: text});
}
content.js
chrome.runtime.onMessage.addListener(function (msg, sender, response) {
if ((msg.from === 'popup') && (msg.subject === 'DOMInfo')) {
//Fill the form with the data of the popup
document.getElementById("The textbox from the form").value = msg.data1;
}
});
what is wrong in the code?
Thanks!
Please learn to debug extension popups. If you did, you would see an informative error message.
With that in mind, tabs in your popup code doesn't come from anywhere - so your code there stops with an error. Clearly, that part is ripped out of context (of a tabs.query call, most likely). Note that if your intent is to message the currently active tab, you can just skip the first argument of sendMessage entirely.
You defintiely do need a content script, since it's the only part of an extension that can interact with a webpage's form. Recommended reading: How do all types of Chrome extension scripts work?
Here is the popup.js with the fixed "tabs" argument
function click(e) {
chrome.tabs.query({currentWindow: true, active: true}, function (tabs){
var activeTab = tabs[0];
var text = document.getElementById("txtArea").value;
chrome.tabs.sendMessage(activeTab.id, {from: 'popup', subject: 'DOMInfo',data1: text});
});
window.close();
}
document.addEventListener('DOMContentLoaded', function () {
document.getElementById('submitBtn').addEventListener('click', click);
});

Extension manifest must request permission to access this host

I am trying to append a div to page of current active tab. However I am getting this error:
Error during tabs.executeScript: Cannot access contents of url ....
Extension manifest must request permission to access this host.
My Code: (show_loader.js)
var dv = document.createElement('div');
dv.id = 'myid';
dv.innerHTML = 'test';
document.body.appendChild(dv);
However when I put this code:
document.body.style.backgroundColor = 'green';
It works as expected and background color of current tab is changed with no other change except for the code in show_loader.js file which is run from popup.js like this:
chrome.tabs.executeScript(null, {file: "show_loader.js"});
My Manifest file also does have:
"permissions":
[
"tabs",
"notifications",
"http://*/",
"https://*/"
],
So I wonder why it gives above error when I try to do anything else other than setting background color. Even simple alert or console.log alone on that page gives same above mentioned error.
How to fix that ?
Update: Complete Relevant Code
Manifest:
{
... name and description ...
"icons":
{
"16" : "img/icon.png",
"48" : "img/48.png",
"128" : "img/128.png"
},
"permissions":
[
"tabs",
"notifications",
"http://*/*",
"https://*/*"
],
"browser_action":
{
"default_icon": "img/icon.png",
"default_title": "Page title",
"default_popup": "popup.html"
}
}
popup.js
// send data to server for saving
$('#btn').click(function(){
chrome.tabs.executeScript(null, {file: "show_loader.js"});
$loader.show();
var data = $(this).closest('form').serialize();
$.ajax({.....});
});
window.onload = function(){
var $loader = $('#loader');
$loader.show();
chrome.tabs.getSelected(null, function(tab) {
//console.log(tab);
$('#url').val(tab.url);
$('#title').val(tab.title);
$loader.hide();
});
};
popup.html
<html>
<head>
<link rel="stylesheet" href="css/style.css" type="text/css" />
</head>
<body>
<form action="" method="post" name="frm" id="frm">
<table border="0" cellpadding="3" cellspecing="0" width="370">
......
</table>
</form>
<script src='js/jquery.js'></script>
<script src='popup.js?v=014423432423'></script>
</body>
</html>
show_loader.js
console.log($); // doesn't work
// document.body.style.backgroundColor = 'green'; // WORKS
Code which worked
manifest.json
{
"name": "Manifest Permissions",
"description": "http://stackoverflow.com/questions/14361061/extension-manifest-must-request-permission-to-access-this-host",
"version": "1",
"manifest_version": 2,
"browser_action": {
"default_popup": "popup.html"
},
"permissions": [
"tabs",
"notifications",
"http://*/",
"https://*/"
]
}
popup.html
<html>
<head>
<script src="back.js"></script>
</head>
<body>
<button id="tmp-clipboard">Click Me</button>
</body>
</html>
back.js
document.addEventListener("DOMContentLoaded", function () {
document.getElementById('tmp-clipboard').onclick = function () {
chrome.tabs.executeScript(null, {
file: "script.js"
});
}
});
script.js
var dv = document.createElement('div');
dv.id = 'myid';
dv.innerHTML = 'test';
document.body.appendChild(dv);
Try Eliminating deprecated chrome.tabs.getSelected from your code and use chrome.tabs.query instead.
Sample Usage
chrome.tabs.query({
"currentWindow": true,
"status": true,
"active": true //Add any parameters you want
}, function (tabs) {//It returns an array
for (tab in tabs) {
//Do your stuff here
}
});
Edit 1
If you intention is to capture active browsing tab in current window where he clicked browser action use this code
chrome.tabs.query({
"currentWindow": true,//Filters tabs in current window
"status": "complete", //The Page is completely loaded
"active": true // The tab or web page is browsed at this state,
"windowType": "normal" // Filters normal web pages, eliminates g-talk notifications etc
}, function (tabs) {//It returns an array
for (tab in tabs) {
$('#url').val(tabs[tab].url);
$('#title').val(tabs[tab].title);
$loader.hide();
}
});
The manifest v3 uses a different permission schema. This is what worked for me:
"host_permissions": [
"https://music.youtube.com/*"
],

Accessing Current Tab DOM Object from a Chrome Extension

I have been searching around on how to accomplish this. I have found some articles most notably
Accessing Current Tab DOM Object from "popup.html"?
However I am very new to JavaScript and making chrome extensions and I have hit a dead end.
My guess is that the response isn't being received which explains why document.write("Hellp")
isn't working. Any help to fix this up would be appreciated.
I have three main files
manifest.json
{
"name": "My First Extension",
"version": "1.0",
"description": "The first extension that I made.",
"browser_action":
{
"default_icon": "icon.png",
"popup": "popup.html"
},
"permissions":
[
"tabs"
],
"content_scripts":
[{
"matches": ["<all_urls>"],
"js": ["dom.js"]
}]
}
popup.html
<html>
<body>
</body>
<script>
chrome.tabs.getSelected(null, function(tab)
{
// Send a request to the content script.
chrome.tabs.sendRequest(tab.id, {action: "getDOM"}, function(response)
{
document.write("Hello");
document.write(response.title)
});
});
</script>
</html>
dom.js
chrome.extension.onRequest.addListener(function(request, sender, sendResponse)
{
if (request.action == "getDOM")
sendResponse({dom: document.getElementsByTagName("body")[0]});
else
sendResponse({}); // Send nothing..
});
I see this is an older question, but it's unanswered and I ran into the same issue. Maybe it's a security feature, but you don't seem to be able to return a DOM object. You can, however, return text. So for dom.js:
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
if (request.action == "getDOM")
sendResponse({dom: document.body.innerHTML});
else
sendResponse({}); // Send nothing..
});
I'm workin on an extension that transfers the html of the element as a text and then rebuilding the element back using innerHTML.
Hope that clarifies how to get the DOM elements from the current page**
This is the way I've got the DOM:
Manifest.json
{
"manifest_version": 2,
"version" : "2.0",
"name": "Prettify search",
"description": "This extension shows a search result from the current page",
"icons":{
"128": "./img/icon128.png",
"48": "./img/icon48.png",
"16": "./img/icon16.png"
},
"page_action": {
"default_icon": "./img/icon16.png",
"default_popup": "popup.html",
"default_title": "Prettify Results!"
},
// If the context is the Mach url = sends a message to eventPage.js: active icon
"content_scripts": [
{
"matches": ["http://www.whatever.cat/*"],
"js": ["./js/content.js", "./js/jquery-3.1.1.min.js"]
}
],
"permissions": [
"tabs",
"http://www.whatever.cat/*",
"http://loripsum.net/*" //If you need to call and API here it goes
],
"background":{
"scripts": ["./js/eventPage.js"],
"persistent": false
}
}
Popup.js
$(function() {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {action: "getPage"}, function(response) {
var importedCode = response.searchResults;
var fakeHtml = document.createElement( 'html' );
fakeHtml.innerHTML = importedCode; // recieved String becomes html
});
});
Eventpage.js
>Able/disable the extension button
chrome.runtime.onMessage.addListener(function(req, sender, resp) {
if(req.todo === 'showPageAction'){
chrome.tabs.query({active:true, currentWindow:true}, function(tabs) {
chrome.pageAction.show(tabs[0].id);
});
}
});
content.js
Content_Scripts can not use the Chrome API to enable or disable the >extension icon. We pass a message to Event_Page, js, he can indeed
use the Api
chrome.runtime.sendMessage({ todo: "showPageAction"});
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");
if (request.action == "getPage"){
sendResponse({searchResults: document.body.innerHTML});
}
});
};
popup.html
Just link popup.js

Categories