Firefox Web extension. Accessing popup content in content script - javascript

Directories
----MyExtension
|----popup.html
|----popup.js
|----content.js
|----background.js
|----manifest.json
mainfest.json
{
"manifest_version": 2,
...........
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"]
}
],
"browser_action": {
"default_title": "Practice",
"default_popup": "popup.html"
},
"permissions": [
"<all_urls>",
"tabs",
"storage",
"activeTab"
],
"background": {
"scripts": ["background.js"]
}
}
....
popup.html
<html>
<head>
....
<script src="popup.js"></script>
</head>
<body>
<input id="status" type="chckbox">
</body>
</html>
popup.js
$(document).ready(function(){
$on = $("#status");
//sends the settings to background to save it
$on.on("click",function(){
$obj = {"on":$on.prop("checked")}
browser.runtime.sendMessage($obj);
console.log("sending....");
})
})
What im trying to do is simply send a message to background script if the check box in popup.html is checked.
The problem is I cannot access the browser namespace in popup.js because its not content script or background script. And i cannot access the check box from the content scrip as it not linked to popup.html (if its linked i get reference error browser is not defined. I've tried countless google searches and spent hours reading web extension docs still cannot find an answer how to go around it any help appreciated.

I have good news for you - you can access the browser namespace in your browser action poupus, otherwise they would be pretty useless :)
Which means that something else is broken.
First, if you didn't do it yet, open 'browser toolbox' with Ctrl+Shift+Alt+I to see that you probably have a bit different kind of error there.
Then include this in your background.js :
function handleMessage(request, sender, sendResponse) {
console.log("Message from somewhere: ", request);
}
browser.runtime.onMessage.addListener(handleMessage);
To actually read the message.
See:
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/browserAction
JavaScript running in the popup gets access to all the same WebExtension APIs as your background scripts, but its global context is the popup, not the current page displayed in the browser. To affect web pages you need to communicate with them via messages.
Edit, unrelated:
$on = $("#status");
You do realize that by doing so you refer to/create a global variable '$on', right? Did you mean:
var $on = $("#status");
?

Related

Uncaught TypeError: Cannot read properties of null (reading 'addEventListener') - Not an issue with DOM ordering

Like the the 10,000 examples with the exact same heading, I am having a trouble with the development of a chrome extension, specifically the execution of scripts between my popup window and my content script.
The specific error i'm receiving is "Uncaught TypeError: Cannot read properties of null (reading 'addEventListener')".
Now, I have reviewed several dozen threads with the same issue and the common theme seems to be that the placement of the scripting within the HTML code is causing the script to execute prior to the DOM loading, however I have attempted the following to resolve placement being an issue.
I have reviewed my code and I have ensured that the script it executing directly above the tag (as to be the last item executing within the body section, and that the code also has the 'delay' flag within the script has a redundancy. I have also wrapped the code within the content script in window.onload=function() as a further redundancy, but alas, no love. I have also attempted moving the order of the code around within foreground.js, however as expected that didn't resolve the issue.
foreground.js
function calculatescript() {
console.log("This prints to the console of the page")
};
window.onload=function(){
var runcalculate = document.getElementById("runcalculate");
runcalculate.addEventListener("click", calculatescript);
}
popup.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="popup.css">
<title>Example Extension</title>
</head>
<body>
<div>
<button class="button button1" id="runcalculate">Calculate</button>
</div>
<script src="foreground.js" defer></script>
</body>
</html>
manifest.js
{
"manifest_version": 3,
"name": "Example",
"description": "An example of a chrome extension using manifest v3",
"version": "0.0.1",
"icons": {
"16": "logo/logo-16.png",
"48": "logo/logo-48.png",
"128": "logo/logo-128.png"
},
"options_page": "settings/settings.html",
"action": {
"default_title": "example",
"default_popup": "popup/popup.html"
},
"permissions": [
"tabs", "scripting"
],
"host_permissions": [
"*://*/*"
],
"background": {
"service_worker": "service-worker.js"
},
"content_scripts": [{
"js": ["foreground.js"],
"matches": ["http://*/*", "https://*/*"]
}]
}
A background js scripts exist however are empty.
If I remove the attempt to pass the script and print directly to the console without trying to wrap it in a button, it works without issue.
I've exhausted my knowledge of what to do to get this working, and the ability to execute off a button is I imagine a fairly common practice, so I'm obviously doing something inherently wrong.

"Communicate" the contents js and background js files in extension

I am writing an extension and I encountered a problem: I can not send data from the extension menu to content.js. In the extension menu I have a couple of intuitions, after filling in and clicking on the button, I write down their values and I want to send them to content.js where this data will be used for implementation inhtml But for some reason, the data is not sent.
document.getElementById('btn').onclick = function() {
var first = document.getElementById('first').value;
var second = document.getElementById('second').value;
//send in content
chrome.extension.sendMessage('hello');
}
<head>
<script type="text/javascript" src="content.js"></script>
<script type="text/javascript" src="background.js"></script>
</head>
<input type="text" id="first">
<input type="text" id="second">
<input type="button" id="btn" value="send">
Here is the manifest.json (maybe there's something wrong)
{
"manifest_version": 2,
"version": "1.3",
"description": "name",
"browser_action":{
"default_popup": "content/popup.html"
},
"background": {
"persistent": false,
"scripts": ["content/background.js"]
},
"content_scripts": [
{
"matches": [ "https://google.com/*" ],
"js": ["content/content.js"],
"css": ["content/qq.css"],
"run_at": "document_end"
}
]
}
content.js: get data from background
chrome.extension.onMessage.addListener(function(request){
if(request=='hello'){
console.log('1. Принято: ', request);
}
});
As I can see everything, background.js is the file that is responsible forjs in the extension menu. content.js is the file that is responsible for making changes to the DOM on the sites.
Your files' structure is unclear: what is the content of popup.html? why do you load both content.js and background.js in the same page?
Here is an example that does what I think you try to accomplish.
It works like this:
The popup screen will display the inputs for the user to fill.
When the button is pressed, the value of the inputs is sent to the background script which, in turn, sends them to the content script. The content script then uses those values in the way you want: for instance, to fill an input in the host webpage.
manifest.json
{
"manifest_version": 2,
"version": "1.3",
"description": "name",
"browser_action":{
"default_popup": "content/popup.html"
},
"background": {
"persistent": true,
"scripts": ["content/background.js"]
},
"content_scripts": [
{
"matches": [ "https://google.com/*" ],
"js": ["content/content.js"],
"css": ["content/qq.css"],
"run_at": "document_end"
}
]
}
background.js
var contentTabId;
chrome.runtime.onMessage.addListener(function(msg,sender) {
if (msg.from == "content") { //get content scripts tab id
contentTabId = sender.tab.id;
}
if (msg.from == "popup" && contentTabId) { //got message from popup
chrome.tabs.sendMessage(contentTabId, { //send it to content script
from: "background",
first: msg.first,
second: msg.second
});
}
});
content.js
chrome.runtime.sendMessage({from:"content"}); //first, tell the background page that this is the tab that wants to receive the messages.
chrome.runtime.onMessage.addListener(function(msg) {
if (msg.from == "background") {
var first = msg.first;
var second = msg.second;
//here you use the values as you wish, for example:
//document.getElementById("anInput").value = first;
}
});
popup.html
<html>
<body>
<input type="text" id="first">
<input type="text" id="second">
<button id="send">Send</button>
<script src="popup.js"></script>
</body>
</html>
popup.js (this file must be located in the same directory as popup.html)
document.getElementById("send").onclick = function() {
chrome.runtime.sendMessage({ //send a message to the background script
from: "popup",
first: document.getElementById("first").value,
second: document.getElementById("second").value
});
}
I hope that helps.
I think you are looking for the runtime property of the chrome / browser object.
This would make your send message command chrome.runtime.sendMessage without the use of the extension property.
Likewise the on message event would be chrome.runtime.onMessage.
I'm pulling this info from the following documentation: https://developer.chrome.com/apps/messaging
content.js should not be included into popup.html. content.js is run whenever a site matches the pattern in your manifest.json. Right now, whenever someone visits google.com with your extension installed, the content.js script is run in the background of google.com.
background.js also shouldn't be loaded into the popup. It's a script that's always run in the background of the browser, it's not something that should get loaded by anything. It's where you add stuff like code to change the omnibox behavior.
You should create a new popup.js script that gets included by popup.html, and it should only handle things like onload and onclick events for the actual popup window.
The various files you mention, content.js, background.js and the file you should create popup.js all have different jobs and should not communicate between each other. There's neither a need nor a possibility for it. If you want to e.g. get the value of what's inside some other site put it in content.js, which is run on each site that matches your pattern, and do all the handling in there.
background.js = code that sets up your extension inside the browser, stuff like changing the omnibox behavior.
content.js = code that runs on each website that matches a certain pattern.
popup.js = code that runs when the user opens the popup window of your extension.
So don't have them communicate, they aren't supposed to, they fill entirely different functions.
There's no reason why you should need to communicate between them either, please explain a scenario where you'd need this and I'll explain why you don't need it. :)
To communicate with content.js, you need to use chrome.tabs.sendMessage instead of chrome.extension.sendMessage, because to communicate with the content.js you need to provide the tabId, which is passed as an argument in the former listed API.

Chrome extension Javascript running only on icon click

I want my Chrome Extension to load Javascript once user visits a website. But currently, the Javascript is executed only when user click the extension icon and till the extension popup is open.
I saw the answer in this Chrome extension to load the script without clicking on icon question.
My manifest.json is:
{
"manifest_version": 2,
"name": "Javascript example",
"description": "Some description.",
"version": "1.1",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"activeTab",
"storage"
],
"content_scripts": [
{
"matches": ["*://*/*"],
"js": ["popup.js"]
}
]
}
The popup.js is:
document.addEventListener('DOMContentLoaded', () => {
alert("Working");
});
popup.html is:
<!doctype html>
<html>
<head>
<title>Getting Started Extension's Popup</title>
<script src="popup.js"></script>
</head>
<body>
</body>
</html>
The alert dialog is shown only when I click the extension icon (i.e. when popup.html is run), not when a page loads. I want the popup.js file to execute without user needing to click the extension icon. What am I missing?
Your manifest works, but run_at defaults to document_idle which means that DOMContentLoaded has already fired. This can be fixed by specifying run_at to document_start instead.
{
"manifest_version": 2,
"name": "Javascript example",
"description": "Some description.",
"version": "1.1",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"activeTab",
"storage"
],
"content_scripts": [
{
"matches": ["*://*/*"],
"run_at": "document_start",
"js": ["popup.js"]
}
]
}
Source: https://developer.chrome.com/extensions/content_scripts
You need the content script.
Content scripts are JavaScript files that run in the context of web
pages. By using the standard Document Object Model (DOM), they can
read details of the web pages the browser visits, or make changes to
them.
Here are some examples of what content scripts can do:
Find unlinked URLs in web pages and convert them into hyperlinks
Increase the font size to make text more legible
Find and process microformat data in the DOM
If your content script's code should always be injected, register it in the extension manifest using the content_scripts field, as in the following example.
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["http://www.google.com/*"],
"css": ["mystyles.css"],
"js": ["jquery.js", "myscript.js"]
}
],
...
}
Refer this link for more information & implementation.
You can use content script that runs in the context of the page and this way you'll know when the user visits a specific site: https://developer.chrome.com/extensions/content_scripts

I wanted to build a chrome extension that will update some page but without any popup

I wanted to build a chrome extension that will update some page (from other source/page) without any popup (the question is silly as I know as I am new in extension dev)
For example, I had my manifest.json:
{......
"browser_action": {
"default_icon": "icon.png",
"default_title": "My chrome extension title"
},
"background": {
"scripts": ["background.js"],
"persistent": false
},
"permissions": [
"activeTab",
"https://ajax.googleapis.com/"
]
......
}
background.js:
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.create({ url: "pageloader.html" });
});
pageloader.html:
<!doctype html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script>
$(document).ready(function () {
$("#status").append("hello chrome extension");
})
</script>
<title>Page loader</title>
</head>
<body>
<div id="status">
</div>
<p>hello</p>
</body>
</html>
When I open 'pageloader.html', javascript is just running fine and showing the output in the browser like:
hello chrome extension
hello
But when I run install the extension and clicking on the 'extension' it is opening 'pageloader.html' in the new tab but showing only
hello
I mean, the javascript part is not running. I will appreciate, if someone would show me my mistake.
CSP does not allow inline javascript and loading resources like jquery from external servers. In order to make it work.
Download jquery and save it in local directory under extension folder.
Move the inline javascript in external file and then include this file after jquery.
If you want to relax the default policy : https://developer.chrome.com/extensions/contentSecurityPolicy#relaxing

Scripts defined in content scripts but not accessible in popup (Chrome extension)

I'm new in javascript and chrome extensions (this is first application).
Extension get a QRcode of the open page's URL.
For QRcode generation I use this lib: https://github.com/jeromeetienne/jquery-qrcode
I read some quides and many answers on SO, but extension doesn't work.
All *.js libraries are in the root catalog with manifest.json
manifest.json
{
"manifest_version": 2,
"name": "QRify",
"description": "This extension shows a QR code of the open page",
"version": "1.0",
"content_scripts": [
{
"matches": ["http://www.google.com/*"],
"js": [
"jquery.min.js",
"jquery.qrcode.js",
"jquery.qrcode.min.js",
"qrcode.js"
]
}
],
"browser_action":{
"default_icon": "icon.png",
"default_popup": "popup.html"
}
}
popup.html
<!DOCTYPE html>
<html>
<head>
<title>basic example</title>
</head>
<body>
<script src="popup.js"></script>
</body>
</html>
popup.js
var pathname = window.location.pathname;
jQuery('#URLqrcodeCanvas').qrcode({
text : pathname
});
Most likely I forgot something...
There are few things which are wrong in your code. Let's take them step by step
Inclusion of both jquery.qrcode.js and jquery.qrcode.min.js : In production code, we try to use minified jquery because downloading of minified js files is faster.
No element with selector used in popup.js : You are trying to access URLqrcodeCanvas in your popup.js while no such element is present in popup.html. May be you should add this
You have not included jquery and qrcode in your popup.html : You need to understand the context of content script, popup scripts and background scripts. Read this
SO Answer: Difference between popup script, background and content script
Wrong use of window.location.pathname : May be you wanted to access the path of current active tab instead of popup. Once you understand the difference between popup and content script then you will be easily figure out this point. Read this
SO Answer: How to get url of active tab ?
Thanks to #Abraham for adding points 3 and 4 in this answer. Hope it helps!!

Categories

Resources