I have read the documentation on how to do Send Message From background javascript file(main.js) to Content Script (content.js) but I cannot get the onMessage to open my alert.
Manifest.json
{
"name": "Example",
"version": "1.0.1",
"manifest_version" : 2,
"description": "Example Description",
"background" : {
"scripts" : ["main.js"]
},
"page_action" : {
"default_icon": {
"19": "icons/19.png",
"38": "icons/38.png"
},
"default_title" : "Example Title"
},
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["lib/jquery-1.8.3.min.js","scripts/content.js"],
"run_at": "document_idle",
"all_frames": false
}],
"permissions": [
"tabs",
"geolocation"
],
"icons": {
"16": "icons/16.png",
"48": "icons/48.png",
"128": "icons/48.png"
}
}
Background javascript file (main.js)
chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
chrome.tabs.sendMessage(tabs[0].id, {action: "SendIt"}, function(response) {});
});
Content javascript file (content.js)
chrome.extension.onMessage.addListener(function(msg, sender, sendResponse) {
if (msg.action == 'SendIt') {
alert("Message recieved!");
}
});
Thanks to the insight of #Teepeemm I have included a tab load complettion before sending message to content script.
WAIT FOR TAB TO BE FULLY LOADED
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
if (changeInfo.status == 'complete') {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
chrome.tabs.sendMessage(tabs[0].id, {action: "SendIt"}, function(response) {});
});
}
});
Sidenote: chrome.extension.onMessage is deprecated, you should be using chrome.runtime.onMessage - although I don't believe this will solve your problem.
I remember that I had an issue injecting a minified jquery file using content scripts. Try to use an un-minified version (ie jquery-1.8.3.js). Once you have done that, also add jquery-1.8.3.js to web_accessible_resources in your manifest file. (Read about that here)
If it still doesn't work, my last suggestion would be to add "<all_urls>" to the permissions array in your manifest.
If you have each script announce its presence (I prefer console.log to alert), you see that the background script runs once (on installation or startup), while the content script runs with each new page. This means that you'll want to have some external event trigger the message. Something like
chrome.pageAction.onClicked.addListener(function(tab) {
chrome.tabs.sendMessage(tab.id,{action:"SendIt"});
});
And don't forget to call chrome.pageAction.show(tabId); as appropriate.
Related
Problem
My chrome extension needs to inject the content script into all tabs when switched on and then when any new tabs are loaded etc.
By adding the content script to the manifest file I can satisfy the second requirement of having it loaded in newly loaded tabs.
To make it also inject the content script into tabs as soon as the extension is loaded or refreshed (there is no popup), I am using chrome.scripting.executeScript.
Problem is each time the extension is turned off/on or refreshed, the content script is loaded in again and any DOM manipulation from content script occurs multiple times.
So from reading another post, I liked the idea of sending a message to the content script, if I don't get a response (undefined) then I inject the script into this tab, if I do get a response then do nothing.
In my background logs I get this error Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist. which is what I would expect if the content script did not exist, but even when the content script is clearly being loaded, I still get the same error.
Any help greatly appreciated. And maybe this is not even the best approach for this, I am not sure. Ideally I would also want the extension to immediately stop working when it's turned off, but I haven't got around to this problem yet.
Manifest
{
"name": "Text highlighter",
"description": "Text highlighter",
"version": "1.0.0",
"manifest_version": 3,
"background": {
"service_worker": "background.js"
},
"options_ui": {
"page": "options.html",
"open_in_tab": false
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"],
"css": [ "styles.css" ]
}
],
"permissions": ["storage", "activeTab", "scripting", "tabs"],
"host_permissions": ["<all_urls>"]
}
self.oninstall = () => {
chrome.tabs.query({}, function(tabs) {
tabs.forEach((tab) => {
chrome.tabs.sendMessage(tab.id, true, (response) => {
console.log('response', response);
if(!response) {
chrome.scripting.executeScript({
target: {tabId: tab.id},
files: ['content.js']
});
chrome.scripting.insertCSS({
target: { tabId: tab.id },
files: ['styles.css']
});
}
});
});
});
};
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
console.log('request', request);
sendResponse(true);
return true;
});
I've been trying to work with chrome extensions lately and right now I'm just trying to get familiar with the chrome apis.
Specifically, I'm testing out chrome.tabs.query to create an alert with the URL of the currently active tab, but the event doesn't trigger. Here is the code for my background.js file.
chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
var activeTab = tabs[0];
var activeTabUrl = activeTab.url;
alert(activeTabUrl);
});
And here is my manifest.json. I'm pretty sure all the permissions are correct.
{
"manifest_version": 2,
"name": "Time Tracker",
"description": "Track how much time spent on ...",
"version": "1.0",
"permissions": ["history", "bookmarks", "activeTab", "tabs"],
"background":{
"service_worker": "background.js"
},
"content_scripts": [
{
"matches": ["http://*.www.youtube.com/*"],
"js": ["content.js"]
}
],
"browser_action": {
"default_popup": "popup.html",
"default_icon": "/images/clock.png"
}
}
I've also tried out other chrome apis such as chrome.history.onVisited to try and add an eventListener but nothing seems to be working.
I've added a link to the full repo here.
https://github.com/ismail-ahmed0149/Time-Tracker
Update #1 June 2. 2021 (Response to most recent comment)
Here is my most recent code my background script.
function handleUpdated(tabId, changeInfo, tabInfo) {
console.log("Updated tab: " + tabId);
console.log("Changed attributes: ");
console.log(changeInfo);
console.log("New tab Info: ");
console.log(tabInfo);
}
chrome.tabs.onUpdated.addListener(handleUpdated);
chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
var activeTab = tabs[0];
var activeTabUrl = activeTab.url;
alert(activeTabUrl);
});
You'll find docs for chrome.tabs events here:
LINK
(scroll down till the end)
You have to add a "onUpdated" event in your background script.
Recently, it is reported that the context menu of my app is vanished. If you remove the app and reinstall it, it works. But the vanishment happens again.
I found an error. I'm not sure if the error causes the vanishment of the context menu. But I'd like to fix this matter, because all I found is this.
This app shows texts you select in a page. When you select texts in an ordinaly page and click browser action button, it works without error. But if you try it on Google Docs, you will get error "Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist".
I'm afraid I don't know what to do with this. And I might have two problems. It'll be great help if you could give me some advice.
[manifest.js]
{
"manifest_version": 2,
"name": "Test Chrome Extension",
"short_name": "Test",
"version": "1.0",
"description": "This is a test.",
"icons": {
"128": "128.png"
},
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["googleDocsUtil.js", "content_scripts.js"]
}],
"background": {
"scripts": ["background.js"],
"persistent": true
},
"browser_action": {
"default_icon": {
"48": "48.png"
},
"default_title": "Test Chrome Extension"
},
"permissions": [
"contextMenus",
"tabs",
"background",
"http://*/*",
"https://*/*"
]
}
[background.js]
chrome.contextMenus.create({
type: 'normal',
id: 'testchromeextension',
title: 'Test Chrome Extension',
contexts:['selection']
});
chrome.contextMenus.onClicked.addListener(function(info,tab){
if( info.menuItemId == 'testchromeextension' ){
var selectedText = info.selectionText.replace(/ /g, "\n");
doSomething(selectedText);
}
});
chrome.browserAction.onClicked.addListener( function(tab) {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {method: "getSelection"}, function(response) {
doSomething(response.data);
});
});
});
function doSomething(selectedText) {
console.log(selectedText);
}
[content_scripts.js]
chrome.runtime.onMessage.addListener( function(request, sender, sendResponse) {
if (request.method == "getSelection") {
var post_val = window.getSelection().toString();
if ( !post_val ) {
var googleDocument = googleDocsUtil.getGoogleDocument();
post_val = googleDocument.selectedText;
}
sendResponse({data: post_val});
}
});
I believe this error is caused when you update the local version of an extension and then try to use the extension with its old/not-updated source code.
The fix: after you reload your local extension at chrome://extensions/, make sure you refresh the page you're using the extension on. You should no longer see the error.
Update: I am sorry that I wasn't clear about my case. The content script is not literally content script but a js file embedded into the page via the createElement method.
It's something like:
// this is "content.js"
var scriptElement = document.createElement('script');
scriptElement.src = 'chrome-extension://' + chrome.runtime.id + '/js/page-inject.js';
(document.head || document.documentElement).appendChild(scriptElement);
I want to use it in "page-inject.js", not the "content.js"
I thought it was very simple but failed after hours of retrying and searching.
I have a background.js like this:
chrome.tabs.query({active: true, currentWindow: true},
function(tabs) {
chrome.tabs.executeScript(tabs[0].id, {
code: 'window.my_var = 123'
}, function() {
chrome.tabs.executeScript(tabs[0].id, {file: 'js/content.js'});
});
});
After this, when I type "window.my_var" in the console of that page. It's not working. The wanted "123" is not showing up.
If I replace the code: 'window.my_var = 123' to alert(123), it works. But what I need is just to pass a variable from my background script (background.js) to the web page that my extension injected into.
Is there anything wrong with my try?
As #wOxxOm already pointed out you how to see the variable inject using the chrome.tabs.executeScript in the web page console by changing the Top in the console.
There is another way you can easily pass values from background to content script using messaging.
Se below a simple example:
manifest.json
{
"name": "test",
"version": "0.0.1",
"manifest_version": 2,
"description": "Test ",
"background": {
"scripts": [
"background.js"
]
},
"browser_action": {
"default_title": "Test "
},
"permissions": [
"*://*/*",
"tabs"
], "content_scripts" : [{
"matches" : ["*://*/*"],
"js" : ["content.js"]
}]
}
background.js
chrome.browserAction.onClicked.addListener(function(){
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {data: "some value"});
});
});
content.js
chrome.extension.onMessage.addListener(handleMessage);
function handleMessage(request) {
console.log(request.data);
}
chrome.extension.getBackgroundPage() will return the global window object of the background page. You can access scripts and any data in the background worker.
Example
const backgroundDOMWindow = chrome.extension.getBackgroundPage();
const myGlobalVariable = backgroundDOMWindow.window['myGlobalVariable'];
Adding script to global background object "background.js"
document.write(
'<script src="main.js"></script>'+
'<script src="main2.js"></script>'
);
I've written a google chrome extension. It's okay and works now but I want the extension to be usebale only on two domains because it's written for these two websites only and is useless for others. There is a context menu only. For now it hasn't even popup, or action button in the top right corner (hidden by default). How can achieve this?
My current manifest.json:
{
"manifest_version": 2,
"background": {
"scripts": ["scripts/jquery.min.js", "scripts/background.js"]
},
"name": "Export Entries",
"description": "some descriptions here",
"version": "1.0",
"icons": {
"16": "images/logo.png",
"48": "images/logo.png",
"128": "images/logo.png"
},
"permissions": ["downloads", "tabs", "contextMenus", "http://my-own-domain-accessed-via-ajax-for-creating-some-files-there.com/*"],
"content_scripts": [{
"matches": ["*://allowed-domain1.com/*", "*://allowed-domain2.com/*"],
"css": ["styles/style.css"],
"js": ["scripts/jquery.min.js", "scripts/popup.js", "scripts/background.js"],
"run_at": "document_end"
}],
"web_accessible_resources": [
"images/logo.png"
]
}
As I understand the extension cannot be disabled absolutely, its process will run in background again. But it's not a problem. I just want to not display the context menu item on other websites.
background.js creates the context menu item and handles its click event:
function exportEntries(info, tab) {
if (info['linkUrl'].indexOf('domain1.com/user/') > -1) {
var user = info['linkUrl'].substr('27');
} else {
var user = null; // export all entries from this topic
}
$.ajax({
url: 'http://my-own-domain-which-creates-the-file.eu/exportEntries/create.php',
method: 'POST',
data: {
topic: tab.url,
user: user
}
}).done(function(url) {
forceDownload(url);
});
}
function forceDownload(url) {
var filename = url.replace(/^.*\/|\.[^.]*$/g, '');
chrome.downloads.download({
url: url,
saveAs: true
}, // options array
function(id) {
// callback function
}
);
};
document.addEventListener('DOMContentLoaded', function() {
chrome.contextMenus.create({
'title': 'Export Entries',
'contexts': ['link'],
'onclick': function(info, tab) {
exportEntries(info, tab);
}
});
});
create.php is on my own domain. It just gets the current page's URL and the user's nickname. Then export all entries from the given topic (i.e. page URL) for the given user, creates a file (.txt, .pdf etc.) and sends back url for downloading the file.
popup.html, popup.js, css file and other stuff is not used for now.
Remove all of your content scripts, they're useless, because the chrome.contextMenus API can only be used on the background page.
To limit the context menu entry to certain domains, pecify the documentUrlPatterns key when you create the context menu using the chrome.contextMenus.create:
chrome.contextMenus.create({
'title': 'Export Entries',
'contexts': ['link'],
'onclick': function(info, tab) {
exportEntries(info, tab);
},
'documentUrlPatterns': [
'*://allowed-domain1.com/*',
'*://allowed-domain2.com/*'
]
});
According to the content scripts documentation:
"If you want to inject the code only sometimes, use the permissions field instead, as described in Programmatic injection."
So instead of
"content_scripts": [
{
"matches": [ "http://allowed-domain.com" ]
}
],
use
permissions: [
"tabs", "http://allowed-domain.com"
],