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

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!!

Related

Firefox Web extension. Accessing popup content in content script

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");
?

"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.

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

Chrome Apps, FileSystem API: chooseEntry method isn't working

Edit: Found the error, but can't solve it, see below.
manifest.json
{
...
"offline_enabled": true,
"app": {
"background": {
"persistent": true,
"scripts": [
"/js/background.js"
]
}
},
"permissions": [
"notifications",
"storage",
{"fileSystem": ["directory"]}
]
}
background.js
chrome.app.runtime.onLaunched.addListener(function() {
window.open('/index.html');
});
index.html
<!DOCTYPE html>
<html>
<head>
<title>Achshar Player</title>
<script src="/js/index.js"></script>
</head>
<body>
<input id="input" type="button">
</body>
</html>
index.js
window.addEventListener('load', function() {
document.getElementById('input').addEventListener('click', function() {
chrome.fileSystem.chooseEntry({type: 'openFile'}, function(readOnlyEntry) {
console.log(readOnlyEntry);
});
});
});
The method is called and so is the callback, but the file choosing dialogue never comes and the readOnlyEntry is undefined when the callback is executed. No errors on the dev tools, I am on 35.0.1916.153 m.
I have tried different variations of manifest declarations for fileSystem but since the function is not undefined in the script execution, the manifest is unlikely to be the issue.
When I use the official example extension of fileSystem API the app works, so the chrome setup isn't the problem either. The problem seem to be my code, but I am lost here.
Edit: I added each file's content
Edit 2: Found the error, now how to solve it?
I tried it in canary and realize the errors are shown via chrome.runtime.lastError and not the normal console. And this is the error I get.
Invalid calling page. This function can't be called from a background page.
But this is not in background.js, this is in index.js which is called from index.html.
I just tried this in Chrome, and there doesn't seem to be anything wrong with the code that you've posted. I suspect that there is a problem with the way that you are loading the javascript, or possibly the context that it is running in (foreground page vs. background page)
For instance, if your JavaScript code snippet is actually in main.js, then that will run in the background page, and its window and document elements won't be the ones from your main page.
My test app looks very similar to yours, except that I have left out the main.js file from the manifest, and I have constructed a small index.html file, which loads a foreground.js script instead. This is the complete app:
manifest.json
{
"manifest_version": 2,
"name": "Stack overflow question test app",
"version": "1",
"offline_enabled": true,
"app": {
"background": {
"persistent": true,
"scripts": [
"/js/background.js"
]
}
},
"permissions": [
"notifications",
"storage",
{"fileSystem": ["directory"]}
]
}
js/background.js
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create("index.html");
});
index.html
<!DOCTYPE html>
<html>
<head>
<title>TEST</title>
<script src="js/foreground.js"></script>
</head>
<body>
<input id="input" />
</body>
</html>
js/foreground.js
window.addEventListener('load', function() {
document.getElementById('input').addEventListener('click', function() {
chrome.fileSystem.chooseEntry({type: 'openFile'}, function(readOnlyEntry) {
console.log(readOnlyEntry);
});
});
});
When I run this, I can click on the input element, and I see a file picker. Choosing an entry returns a FileEntry object, which is logged to the console (of the foreground page, not the background page. Right-click in the app window and select "Inspect Element", rather than "Inspect Background Page", to see the foreground console.):
FileEntry {filesystem: DOMFileSystem, fullPath: "/TestFile.rtf", name: "TestFile.rtf", isDirectory: false, isFile: true…} foreground.js:4
Note:
From your original code, it appeared that you were using a framework like jQuery to search for DOM elements within your page. Chrome Apps work just fine with jQuery, but you have to be aware of when you are using a raw DOM Node object, and when you have a wrapped jQuery object.
Specifically, the line
$('input').addEventListener('click', function() {
would have caused you problems.
Replacing it with
document.querySelector('input').addEventListener('click'), function() {
would correctly find the element on the page, and attached the click handler to it.

chrome extension insert content script on browser action

I am trying to make basically an element highlighter chrome extension.
Workflow:
- click on browser icon
- click on the page
- hightlight the element clicked
I am having troubles in running content scripts upon browser action using manifest_version:2
When I inspect the popup that appears it says:
Refused to execute inline script because it violates the following
Content Security Policy directive: "script-src 'self'
chrome-extension-resource:" (popup.html:5).
Which is where the inline script in popup.html is and the script does not work
I have:
manifest.json:
{
"browser_action": {
"default_icon": "images/icon.gif",
"default_popup": "popup.html"
},
"manifest_version": 2,
"description": "MEH!",
"name": "My First Extension",
"permissions": [
"tabs", "http://*/*", "https://*/*"
],
"version": "0.1"
}
popup.html:
<html>
<head>
</head>
<body>
<script>
chrome.tabs.executeScript(null,{
code:"document.body.style.backgroundColor='red'"
});
</script>
<div id='msg' style="width:300px">...</div>
</body>
</html>
Any help would be very much appreciated
Turns out I could not read the error properly until I saw it in here
Apparently manifest v2 does not allow you to have inline scripts, so you just need to
src="path_to_the_file.js"
In extension to #tak3r's answer and #Doug's comment:
Inline scripts need to be changed to external scripts.
Move:
<script>
chrome.tabs.executeScript(null,{
code:"document.body.style.backgroundColor='red'"
});
</script>
To a new file called main.js and remove the <script></script> tags
Include the following in the <head></head> of your HTML
<script type="text/javascript" src="main.js"></script>

Categories

Resources