Chrome Extension: popup.js and content.js won't talk - javascript

I've been trying for hours to get popup.js to talk with the rest of my chrome extension using chrome api's sendMessage and onMessage. For testing purposes, all I want right now is to log something onto my currently active tab when a button is pressed on popup.js. This seems like it should be simple, but I just can't figure out what the problem is. Would really appreciate any help and explanation!
popup.html
<!DOCTYPE html>
<body>
<p>testing</p>
<input type="submit" id="clickme" value="button">
</body>
popup.js
function popup(){
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
var activeTab = tabs[0];
chrome.tabs.sendMessage(activeTab.id, {"message": "clicked_browser_action"});
}
document.addEventListener("DOMContentLoaded", function() {
document.getElementById("clickme").addEventListener("click",popup)
});
content.js (I expect this to log 'started' onto console of my active tab)
// content.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if( request.message === "clicked_browser_action" ) {
console.log('started')
}
}
);
Just in case it applies, this is the manifest.json
{
"manifest_version":2,
"name": "extension",
"version": "0.1",
"content_scripts":[
{
"matches": [
"<all_urls>"
],
"js":["jquery-3.1.1.min.js","content.js"]
}],
"browser_action":{
"default_icon":"icon.png",
"default_popup":"popup.html"
},
"background":{
"scripts":["background.js"]
}
}

Related

Link to options page from popup in programatically injected Chrome extension

I have made a programatically injected version of a Chrome extension in order to have the extension working on pages from hosts with different top-level domain names (cfr previous post). Now I would like to add a link to an options page to the popup menu. However I keep getting the following error message:
Unchecked runtime.lastError: Cannot access contents of url
"chrome-extension://••••••••••••••••/options.html".
Extension manifest must request permission to access this host.
Can anyone tell me how to request such a permission? In background.js, I have also tried chrome.tabs.create({url: "options.html"}); without success.
Note that the options page is displayed ok, but the error message keeps coming.
manifest.json:
{
"manifest_version": 2,
"name": "My Extension",
"version": "0.5",
"description": "Does what it does",
"icons": {"32": "icon32.png",
"48": "icon48.png",
"128": "icon128.png"
},
"options_page": "options.html",
"background": {
"scripts": ["background.js"],
"persistent": false
},
"browser_action":{
"default_popup": "popup.html"
},
"optional_permissions":["tabs","https://*/*"],
"permissions": ["storage"]
}
popup.html:
<html>
<head>
</head>
<body>
<ul>
<li id="li_1">Enable My Extension</li>
<li id="li_2">Options</li>
</ul>
<script src="jQuery.js"></script>
<script src="popup.js"></script>
</body>
</html>
popup.js:
jQuery(function($){
var tabId = 0;
$(document).ready(function(){
$("#li_1").click(function(){ // this works
window.close();
chrome.permissions.request({
permissions: ['tabs'],
origins: ["https://*/*"]
}, function(granted) {
if (granted) {
chrome.runtime.sendMessage("granted");
} else {
alert("denied");
}
});
});
$("#li_2").click(function(){ // this sends request to background.js which then causes error
window.close();
chrome.runtime.sendMessage("openOptions");
});
});
});
background.js:
chrome.runtime.onMessage.addListener(
function(message) {
switch(message){
case "granted": //this works, please ignore
var tabId = 0;
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
tabId = tabs[0].id;
injectScript(tabId);
chrome.permissions.contains({origins:["https://*/*"]}, function(perm) {
if (perm) {
chrome.tabs.onUpdated.addListener(function(tabId){
injectScript(tabId);
});
}
});
});
break;
case "openOptions": //this does not work – generates error msg
chrome.runtime.openOptionsPage();
break;
}
return true;
});
As suggested by wOxxOm in a comment above, the error was caused by he extension's attempt to inject the content script into the options.html page. One way to circumvent that would be to insert a condition into the injectScript() function (which was not shown in the question) in background.js, ensuring that only web pages are injected. A working version is shown below.
background.js
function injectScript(tab){
chrome.tabs.get(tab,function(thisTab){ // get tab info
if(thisTab.url.startsWith("http")){ // condition: is this a web page?
chrome.tabs.executeScript(tab,{ // if so, inject scripts
file: "jQuery.js"
});
chrome.tabs.executeScript(tab,{
file: "content.js"
});
}
});
}
chrome.runtime.onMessage.addListener(
function(message) {
switch(message){
case "granted":
var tabId = 0;
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
tabId = tabs[0].id;
injectScript(tabId);
chrome.permissions.contains({origins:["https://*/*"]}, function(perm) {
if (perm) {
chrome.tabs.onUpdated.addListener(function(tabId){
injectScript(tabId);
});
}
});
});
break;
case "openOptions":
chrome.runtime.openOptionsPage();
break;
}
return true;
});

Chrome extension message passing from background script to content script error: receiving end doesn't exist

I'm working on a chrome extension currently, and I keep getting this error:
Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist
I'm trying to pass a message from my background script to my content script. The background script fetches the url of the current tab, and I'm trying to pass that URL to my content script. I've found a few different solutions to this error, but none of them are working for me. From what I understand, it has something to do with the content script not being loaded when the background script tries to send the message, though I've tried some workarounds for that and nothing has worked for me. This is my code:
Background script:
//use a query to get the current URL of the tab
chrome.tabs.query({
active: true,
lastFocusedWindow: true
}, function(tabs){
var currentTab = tabs[0];
var currentUrl = currentTab.url;
//send a message from background script to content script
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
const port = chrome.tabs.connect(tabs[0].id);
port.postMessage({url: currentUrl});
port.onMessage.addListener(function(response){
console.log(response);
});
});
}
);
Content Script:
chrome.runtime.onConnect.addListener(function(port){
port.onMessage.addListener(function(msg){
if(msg.url.includes("amazon")){
console.log("You are on amazon.com");
}
port.postMessage("This is my response!");
});
});
Here is my manifest as well:
{
"manifest_version": 2,
"name": "First Browser Extension",
"version": "1.0.0",
"description": "Add a better description later.",
"page_action": {
"default_icon": "icon.png",
"default_title": "BrowserExtension",
"default_popup": "index.html"
},
"background":{
"scripts": ["background.js"],
"persistent": false
},
"content_scripts":[
{
"matches": ["https://www.amazon.com/*"],
"js": ["content.js"]
}
],
"permissions": [
"tabs",
"activeTab",
"https://*/*"
],
"icons": {"48": "icon.png"}
}
I can't figure out what's going wrong. Any insight would be greatly appreciated!
Edit: updated code
Background
chrome.tabs.query({
active: true,
currentWindow: true //changed lastFocusedWindow to currentWindow
}, function(tabs){
var currentTab = tabs[0];
var currentUrl = currentTab.url;
console.log(currentUrl);
//----------NEW CODE---------------------------
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.contentScriptQuery === "getURL") {
sendResponse({url: currentUrl});
return true; // Will respond asynchronously.
}
});
}
);
Content
chrome.runtime.sendMessage(
{contentScriptQuery: "getURL"},
function(urlFromBackground){
console.log(urlFromBackground.url);
}
);

Communication to Content script fails. Google chrome extension application

i am new to Javascript and am trying to implement a google chrome extension. I read the instructions (https://developer.chrome.com/extensions/getstarted), but still I have some troubles with my code.
I have got a popup menu with buttons and I want to get back the DOM content of the current page when a button is clicked.
Therefore i created a contentscript.js and a popup.js. The popup.js asks the contentscript to get the DOM (still needs to be implemented how I do that) and send back the DOM.
I receive the error:
Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.
This is my code:
popup.js
var ads = ["increase", "reduce", "blabla"];
function markAds(text) {
document.getElementById("markAd").innerHTML=ads[0];
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {greeting: "hello"}, function(response) {
console.log(response.farewell);
alert(response.farewell);
});
});
}
document.addEventListener('DOMContentLoaded', function() {
var markAd = document.getElementById('markAd');
// onClick's logic below:
markAd.addEventListener('click', function() {
markAds();
});
});
mainfest.js
{
"name": "Test",
"description": "Test Description",
"version": "0.7",
"permissions": ["contextMenus","activeTab"],
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["contentscript.js"]
}],
"browser_action": {
"default_popup": "popup.html"
},
"manifest_version": 2
}
contentscript.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.greeting == "hello")
sendResponse({farewell: "goodbye"});
});

Chrome extension messages firing multiple times

I'm making my first chrome extension and noticed that messages being sent from my popup.html page were getting duplicated in my content.js message event listener. I've console logged "sending message" before every message send and "message received" before every message, and I don't understand how the messages are being duplicated. I've also checked the chrome dev docs for sendMessage and onMessage and it specifies that the onMessage listener should only be fired once per sendMessage event.
Any help would be appreciated.
popup.html
<!DOCTYPE html>
<html>
<head>
<title>Messaging Practice</title>
</head>
<body>
<h1>Messaging Practice</h1>
<input type="button" id="send-message" value="Button">
<script src="popup.js"></script>
</body>
</html>
content.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log('message received')
console.log(request);
}
);
popup.js
var messageButton = document.querySelector("#send-message");
messageButton.onclick = function() {
chrome.tabs.query({currentWindow: true, active: true},
function(tabs) {
chrome.tabs.executeScript(
tabs[0].id, {file: "content.js"}
)
console.log("sending message")
chrome.tabs.sendMessage(tabs[0].id, "string message")
});
}
background.js
chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
chrome.declarativeContent.onPageChanged.addRules([{
conditions: [new chrome.declarativeContent.PageStateMatcher({
pageUrl: {hostEquals: 'stackoverflow.com'},
})],
actions: [new chrome.declarativeContent.ShowPageAction()]
}]);
});
manifest.json
{
"name": "Send Messages Practice",
"version": "1.0",
"manifest_version": 2,
"description": "Simple messaging practice with the chrome api",
"permissions": ["declarativeContent", "activeTab"],
"background": {
"scripts": ["background.js"],
"persistant": true
},
"page_action": {
"default_popup": "popup.html",
"scripts": ["content.js"],
"matches": ["http://*/*","https://*/*"]
}
}
Adding "return true;" to event handlers will prevent your code from firing again and again:
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
if (request.action == "show_alert") {
alert(request.alert);
return true;
}
});
Get the same problem when I run my extension with multiple frames. #Wayne Smallman solution works for me:
recommending that all_frames be set to false in the manifest file

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