Notifying background task when content script wants - javascript

I have one main task (background.js) that runs some stuffs.
I need to create a new trigger event on the background script of my app, so that he will create a new timeout of one day (24 hours). But, i don't want to check every page i enter, on my background, What i want to do is to send some sort of "message" trough one content_script page, to my background task (the same app).
What i need: run a function on my background, from my content_script.
Is it possible? How?

Yes, it is possible with message Passing. You can refer following script as a reference
Demonstration
The following code will execute a function in background page when triggered from content script.
manifest.json
Registered background and content script with extension and added necessary permissions
{
"name": "Run Background page function from content script",
"description": "",
"version": "1",
"manifest_version": 2,
"background": {
"scripts": [
"background.js"
]
},
"content_scripts": [
{
"matches": [
"https://www.google.co.in/*"
],
"js": [
"myscript.js"
]
}
],
"permissions": [
"https://www.google.co.in/*"
]
}
background.js
It contains some trivial function and a listener for triggering call for function
//Some Trivial Function
function runme() {
console.log("I am executed ... ");
}
//Listener fired when message is recieved
chrome.extension.onMessage.addListener(function (content) {
if (content.message == "Hey i am from Google.co.in Page please run runme function") { // Check for correct message
runme(); // Invoke function
}
});
myscript.js
//Trigger background page through message passing
chrome.extension.sendMessage({
message: "Hey i am from Google.co.in Page please run runme function" //Some message
});
Output
You can see background function running when trigger is invoked.
Reference
Message Passing.

Related

chrome extension call specific function

I have a popup.js:
function registerButtonAction(tabId, button, action) {
// clicking button will send a message to
// content script in the same tab as the popup
button.addEventListener('click', () => chrome.tabs.sendMessage(tabId, { [action]: true }));
}
function setupButtons(tabId) {
// add click actions to each 3 buttons
registerButtonAction(tabId, document.getElementById('start-btn'), 'startSearch');
registerButtonAction(tabId, document.getElementById('deals-btn'), 'startDeals');
}
function injectStartSearchScript() {
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
// Injects JavaScript code into a page
// chrome.tabs.executeScript(tabs[0].id, { file: 'main.js' });
// dd click handlers for buttons
setupButtons(tabs[0].id);
});
}
injectStartSearchScript()
// document.getElementById('inject-btn').addEventListener('click', injectStartSearchScript);
Which I use to inject my script into the page with the "start-btn" inside my popup.html.
This is my main.js which includes my functions I would like to call on a page:
function pong() {
// do something
}
function ping() {
// do something else
}
my manifest.json:
{
"manifest_version": 2,
"name": "test app",
"description": "test desc",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": ["tabs", "<all_urls>"],
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["main.js"]
}
]
}
So basically my setup is that I have a popup.html which includes 3 buttons and they should call one of the functions inside my main.js dpending on what button i press.
But I can not get that working. Currently I only can make at least one function call if I directly call pong() inside main.js on load. But I would need to call one of the functions after I click on a button inside my popup.html.
EDIT: I have updated the code as far as I understood. I am very sorry but I don't understand what would be needed to be changed else to fulfill your proposal. I mean how to write it to be more correct.
EDIT 2: I have removed the line chrome.tabs.executeScript(tabs[0].id, { file: 'main.js' }); as well as document.getElementById('inject-btn').addEventListener('click', injectStartSearchScript)and added injectStartSearchScript()into the popup.js file. Is that what you meant?
Updated and complete example with explanation (you are almost there!)
manifest
You manifest looks good, no changes needed there.
This configuration says to load the content script in each tab. So before popup even opens each tab will have "main.js" injected into it (exactly once).
popup.js
Popup script looks good, no changes needed there.
The tab lookup is still necessary, since to send a message to a specific tab, must know its id. The popup looks for the currently active tab in current window (same as the popup is in) and sets up the button click actions to send a message to the tab.
main.js
Will need minor changes here
Make sure to register the onMessage listener, as included in the example below.
note the conditions: message.startSearch and message.startDeals -- these must match the messages sent from the popup, i.e. when popup sends a message with content {startDeals: true}, the if condition is startDeals. It is matching by a key in the sent message and if the key does not match any condition, the message is going to be ignored.
function pong() {
// do something
alert('Start!');
}
function ping() {
// do something else
alert('Deals!');
}
// register listener to receive messages
chrome.runtime.onMessage.addListener(message => {
// what to do on each received message:
if (message.startSearch) pong();
else if (message.startDeals) ping();
});
// sanity check: content has loaded in the tab
console.log('content loaded');
One more note as it relates to debugging extensions (and perhaps a source of some of these debugging issues) when the content script has been configured to be injected in the manifest, Chrome will inject main.js into tabs, in a separate extension context. If, after this injection, the developer reloads the extension (circular arrow in chrome://extensions or other tool), this will invalidate the context of the content script in the tab. The tab has to be reloaded to reactivate the content script. This is not an issue with a real user, but it does happen during debugging, so double check this is not the cause of issues while debugging.

How do you detect changes in url with a chrome extension?

I am learning to make chrome extensions. I ran into a problem where context scripts that I want to run, even just alert("test");, are unable to when onload is not activated. This also occurs when you press the back arrow to visit the last visited page. I notice that the url changed, but nothing activates. How do I detect this? If the answer is with service workers, a detailed explanation would be greatly appreciated.
maifest version 2.0
Try using chrome.tabs.onUpdated.addListener((id, change, tab)=>{}). This should run every time the URL changes! Here is a minimalistic example of some code that injects js to a site when the URL changes.
background.js:
// inject code on change
chrome.tabs.onUpdated.addListener((id, change, tab) => {
// inject js file called 'inject.js'
chrome.tabs.executeScript(id, {
file: 'inject.js'
});
});
mainfest version 3.0
You can do it by using chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {}). However, this will actually trigger multiple times when a page URL is changed So you need to add a check for the URL in the changeInfo variable, so it only triggers once!
manifest.json:
{
"name": "URL change detector",
"description": "detect a URL change in a tab, and inject a script to the page!",
"version": "1.0",
"manifest_version": 3,
"permissions": [
"scripting",
"tabs"
],
"host_permissions": [
"http://*/*",
"https://*/*"
],
"background": {
"service_worker": "background.js"
}
}
background.js:
// function that injects code to a specific tab
function injectScript(tabId) {
chrome.scripting.executeScript(
{
target: {tabId: tabId},
files: ['inject.js'],
}
);
}
// adds a listener to tab change
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
// check for a URL in the changeInfo parameter (url is only added when it is changed)
if (changeInfo.url) {
// calls the inject function
injectScript(tabId);
}
});
inject.js:
// you can write the code here that you want to inject
alert('Hello world!');

How to Listen to XMLHttpRequests - Browser extension

My Script
var origOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function() {
this.addEventListener('load', function() {
//console.log(this.responseURL);
if(this.responseURL == "https://get.example.com/dashboard/summary")
{
// Run the function
funMain();
}
});
origOpen.apply(this, arguments);
};
function funMain() {
// My codes
}
This is my script and it's working as expected.
What this does is, this listen to XMLHttpRequests and when an XMLHttpRequest is made to 'https://get.example.com/dashboard/summary', it calls funMain function. Since this is an event, this is not a one time event. This can be happened multiple times. This XMLHttpRequest is made by https://example.com/dashboard webpage.
Process: If an XMLHttpRequest is made to 'https://get.example.com/dashboard/summary' by https://example.com/dashboard webpage, then call funMain function.
So now, I want to build an extension using this working script.
This is what I have tried so far...
Content Script
function funMain() {
// My codes
}
Note: I know that I have to use browser.runtime.sendMessage() to communicate between background script and content script. But at the moment, I'm trying to get the event function (in the Background Script) working.
Background Script
console.log("Msg 01 - Background script is running.");
var origOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function() {
this.addEventListener('load', function() {
console.log("Msg 02 - " + this.responseURL);
if(this.responseURL == "https://get.example.com/dashboard/summary")
{
// Run the function
// funMain();
console.log("Msg 03 - Hello! I am working as expected.");
}
});
origOpen.apply(this, arguments);
};
Manifest
{
"manifest_version": 2,
"name": "Beastify",
"version": "1.0",
"description": "des",
"icons": {
"48": "icons/beasts-48.png"
},
"permissions": [
"webRequest",
"<all_urls>"
],
"content_scripts": [{
"matches": [
"*://*.example.com/dashboard"
],
"js": [
"content-script.js"
]
}],
"background": {
"scripts": ["background.js"]
}
}
The problem is, the event does not work as expected. Therefore, funMain cannot be called. When I inspect the background page, I can only see Msg 01. I cannot see Msg 02 or Msg 03.
Output: background.js
Msg 01 - Background script is running.
What am I doing wrong and how can I fix this?
It all depends on the actual situation. Here are some ideas that you can work on.
It is better to use standard JavaScript XMLHttpRequest instead of usingXMLHttpRequest.prototype.
Content script and browser have different scope and for security do not interact directly.
You can
Have XHR in the content script and use
webRequest
API in the background script to listen to HTTP requests
Use runtime.sendMessage() from content script to background script to run the XHR in the background script
Then from background script
Use
tabs.sendMessage() to send message to contentscript
Use tabs.executeScript() to directly run scripts in the content scope or initiate functions in the content script

Content script not listening to message event

I am developing my first browser extension for my website.
What this extension should basically do is to have a browser action which opens a pop-up where you can edit specific cookie values for the current page.
However, cookie A can exist on the page / while cookie B can exist on the page /checkout. So I don't want to list every cookie inside the pop-up, only the one which is active on the current page.
So, I searched the documentation and found that in order to communicate between web page and add-on you have to use the message system as described here
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts#Communicating_with_the_web_page
To do so, my website has a JavaScript file which is loaded on every page. In this JavaScript I'm executing the following code
window.postMessage({type: 'FROM_PAGE', data: visibleCookies}, '*');
This piece of code is definitely executed, because I put a console.log before or after that statement, I can see that it's being logged.
Now, in my content script I want to listen to this by executing the following code
// experimentManager.js
console.log('testd');
window.addEventListener('message', (event) => {
console.log(event);
if (event.source !== window) {
return;
}
});
However, the console.log(event); is never executed. The listener is never activated. When I press the browser action so that the popup opens testd is logged into console, but still, the listener doesn't get any events. It's just not getting executed.
I don't know which files are relevant, but this is my manifest.json
// manifest.json
{
"manifest_version": 2,
"name": "My first addon",
"version": "1.0",
"description": "My first addon description",
"icons": {
"48": "icons/icon.png"
},
"browser_action": {
"default_icon": "icons/icon.png",
"default_title": "My first addon",
"default_popup": "popup/manage_experiment.html",
"browser_style": true
},
"permissions": [
"tabs",
"cookies",
"<all_urls>"
],
"content_scripts": [
{
"matches": ["*://*.mydomain/*"],
"js": ["experimentManager.js"]
}
]
}
And inside the pop-up script I'm executing this code among other things
browser.tabs.executeScript({file: "/content_scripts/experimentManager.js"})
.then(manageExperiments)
.catch(handleError);
which is probably the reason why the console.log('testd') gets executed, but nothing else?
What am I missing?

Sending message from content script to browseraction in firefox webextension?

Is it possible to send a message from Content script to Browser action directly without using background page? Following is a simplified version of what my code looks like. The content script seems to be working fine, but I get the following error in the console:
Error: Error: Could not establish connection. Receiving end does not exist.
I am assuming it's because the Browser action is not always active. But I don't want to use a Background page because I don't want the script running constantly hogging memory. I am hoping to send message directly to Browser action and display a popup, sort of like browserAction.onClicked displaying a popup. This is my first extension that I am trying to build, so trying to figure things out. Thanks
[manifest.json]
{
"manifest_version": 2,
"name": "Test",
"version": "0.1",
"icons": {
"48": "icons/test.png"
},
"permissions": [
"activeTab"
],
"browser_action": {
"default_icon":"icons/test.png",
"default_title": "test",
"default_popup": "popup/popup.html",
"browser_style": true
},
"content_scripts": [
{
"matches": ["*://testwebsite"],
"js": ["content_scripts/content-script.js"]
}
]
}
[popup.js]
function handleMessage(request, sender, sendResponse) {
console.log("Message from the content script: " +
request.greeting);
sendResponse({response: "Response from background script"});
}
browser.runtime.onMessage.addListener(handleMessage);
[content-script.js]
function handleResponse(message) {
console.log(`Message from the background script: ${message.response}`);
}
function handleError(error) {
console.log(`Error: ${error}`);
}
function send_2_popup() {
var sending = browser.runtime.sendMessage({
greeting: "Greeting from the content script"
});
sending.then(handleResponse, handleError);
}
var btn = document.getElementById("btn");
btn.addEventListener("click", send_2_popup);
You can rather send a message from the popup to the background and get a response as well as a message from background.. this way the background will know that the popup exists and hence the message from background to popup will be successful.

Categories

Resources