How to determine is your Chrome extension installed? - javascript

I have my Chrome extension and website that host button for navigation to my extension.
There are plenty of solutions online, but none of them worked for me as well for some other people.
I've tried an inline installation which is awesome but it will be deprecated next month.
What is the easiest way to find out is my Chrome extension already installed so I can hide the installation button?
Here is some latest thing I have tried, but all the time I'm getting the false value:
Background.js
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
if (request) {
if (request.message) {
if (request.message == "version") {
sendResponse({version: 1.0});
}
}
}
return true;
});
Here is my website component:
window.chrome.runtime.sendMessage('kgpphmdamkaepmgiepihmihoohflolkf', { message: 'version' },
function (reply) {
if (reply) {
if (reply.version) {
if (reply.version >= requiredVersion) {
console.log('true');
}
}
}
else {
console.log('false');
}
});
manifest.json :
"externally_connectable": {
"matches": ["*://localhos:8080/*", "*://search.call.com/*"]
}

How about creating an element on your page and hiding it from your extension ?
In your website:
<div id="prompt-insall-YOUR_CHROME_EXTENSION_ID">
<button>Install</button>
</div>
In your extension:
document.getElementById(`prompt-install-${chrome.runtime.id}`).style.display = 'none';

Related

Creating and accessing global variable in google chrome extension

All of the information I can find on this is pretty old. Like the title says I am trying to make a global variable in one script and access it from another. The purpose of the extension is to search for a class named "page-title" and then return the innerHTML of that HTML element. Once I get the code working I will specify the URL I want the extension to run on so it's not constantly running.
After a couple iterations trying to accomplish this in different ways I followed the method explained in this answer but my needs have different requirements and I am receiving the error "Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist." tied to the popup.html.
I tried the Unchecked runtime error solution found here but it's been awhile (~ 7 years) since I've dived into any coding and I'm not sure I implemented it correctly.
I've also tried to pass the value between JS documents is the HTML injection method, but without overriding security defaults in the manifest that doesn't really work. It also seemed super bootstrappy and I wanted to pass the information in a more conventional way. I tried creating a global variable by simply declaring the variable outside of a function/class/if statement and loading that .js file first, but that was unsuccessful as well.
Manifest
"name": "P.P. to Sharepoint",
"version": "1.0.0",
"description": "Open P.P. client folder in sharepoint",
"manifest_version": 3,
"author": "Zach Morris",
"action":{
"default_popup": "popup.html",
"default_title": "Open Sharepoint Folder"
},
"background": {
"service_worker": "background.js"
},
"permissions": [
"activeTab",
"tabs",
"scripting",
"notifications"
],
"content_scripts": [{
"js": ["contentScript.js"],
"matches": ["<all_urls>"]
}]
}
popup.html
My popup.html is super simple and really just has a button to press. I included all the .js files in the order I thought necessary
<script src="globalVariable.js"></script>
<script src="contentScript.js"></script>
<script src="popup.js"></script>
<script src="script.js"></script>
<script src="background.js"></script>
globalVariable.js
This one is straight forward. I need to pull the client's name out of the HTML of the page then use it in an API call when I click the button in popup.js This initializes the variable and uses it as place holder.
var clientInfo = {
name: 'test name'
};
ContentScript.js
I only want to run this if importScripts is not undefined. So I threw it in the if statement. Then I make sure I pulled a client name from the page. If not I throw an error message saying no client was found.
if( 'function' === typeof importScripts) {
importScripts('globalVariable.js');
addEventListener('message', onMessage);
function onMessage(e) {
if(b[0]) {
clientInfo.name = b[0].innerHTML;
alert(clientInfo.name + ' was assigned!');
} else {
alert('There is no client on this screen ' + 'b[0] is ' + b[0] + " clientInfo = " + clientInfo.name);
};
};
} else {
console.log("Your stupid code didn't work. ");
}
popup.js
This one pulls up the globalVariable.js to use the clientInfo. and makes a call to the button in background.js
if( 'function' === typeof importScripts) {
importScripts('globalVariable.js');
addEventListener('message', onMessage);
function onMessage(e) {
const text = clientInfo.name;
const notify = document.getElementById( 'myButton' );
notify.addEventListener( 'click', () => {
chrome.runtime.sendMessage( '', {
type: 'notification',
message: text });
} );
}
}
background.js
Same thing here. I import the globalVariable script to use the global variable. The notification will eventually be replaced with the API call when the rest of the code is working properly. I probably don't need to import the script here to access the variable because I can mass it with the event listener in popup.js, but I put it in here out of desperation.
if( 'function' === typeof importScripts) {
importScripts('globalVariable.js');
addEventListener('message', onMessage);
function onMessage(e) {
// do some work here
chrome.runtime.onMessage.addListener( data => {
if ( data.type === 'notification' ) {
chrome.notifications.create(
'',
{
type: 'basic',
title: 'Notify!',
message: data.message || 'Notify!',
iconUrl: 'notify.png',
}
);
console.log("sent notification");
};
});
}
}
You can have the popup.js listen for a button click and content.js handle all the logic of finding the correct element.
popup.js
document.querySelector('#btn').addEventListener('click', () => {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) =>
chrome.tabs.sendMessage(tabs[0].id, { command: 'getClientName' })
);
});
content.js
chrome.runtime.onMessage.addListener((msg, sender, response) => {
if (msg.command === 'getClientName')
findClientName(document.querySelectorAll('h3.page-title'));
});
Example of findClientName function:
const findClientName = async (element) => {
let clientName;
if (element.length > 0) {
element.length === 1
? (clientName = setClientName(element[0]))
: handleMultipleElements(element);
} else {
handleNoClientNameFound();
}
clientName ? await makeAPIRequest(clientName) : null;
};
Try this method instead maybe?
{
var x = 2;
}
so:
{
var clientInfo = {
name: 'test name'
};
}
Not very good at this language, so I thought maybe you're missing the brackets?

Chrome extension content script not being injected sometimes

I have a chrome extension for fillling forms on certain websites. This all works well, however sporadically the content script for filling the form doesn't get injected anymore, then I have to reinstall the extension to remediate the problem. This is the code I use for injecting the content script:
chrome.tabs.create({ url: url, active: true }, function (tab) { //create tab
chrome.tabs.onUpdated.addListener(function listener(tabId, info) {
if (info.status === 'complete' && tabId === tab.id) {
chrome.tabs.onUpdated.removeListener(listener);
chrome.tabs.executeScript(tab.id, { file: 'library.js', allFrames: true, runAt: "document_end" }, function () {
chrome.tabs.executeScript(tab.id, { file: 'fillForm.js', allFrames: true, runAt: "document_end" }, function () {
//inject content script
chrome.tabs.sendMessage(tab.id, { formData }); //send message to content script
});
});
}
});
});
I suppose it's some kind of a timing issue or something that changed in the Chrome api? Because the problem only occured recently.

Tab Listener not Getting Activated by Message from Background Script

I am sending a message to the tab were I have a content script (getTradingData.js) from the background.js with the following code:
alert("Automated TradingView Extension is running");
chrome.tabs.query({
url: 'https://www.tradingview.com/*'
}, function(tabs) {
if (tabs.length == 1) {
chrome.tabs.sendMessage(tabs[0].id, {subject: "testConnection"}, function(response) {
alert(response); //THIS RETURNS UNDEFINED
if (response.msg == "getTradingDataScriptHere") {
alert("Script Already Injected. Do not reinject"); //THIS IS NOT RUNNING
} else {
chrome.tabs.executeScript(tabs[0].id, {file: "jquery-2.2.3.min.js"});
chrome.tabs.executeScript(tabs[0].id, {file: "jquery.waituntilexists.min.js"});
chrome.tabs.executeScript(tabs[0].id, {file: "getTradingData.js"});
alert("Injected all Nessessary Scripts for Auto Trading View to work"); //THIS IS NOT RUNNING
}
});
} else {
alert("Please have one and only one tradingview chart page opened.");
}
});
var price = "Waiting For Price"
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.subject == "getPrice") {
sendResponse({
price: price
});
} else if (request.from == "getTradingData" && request.subject == "scriptLoaded") {
//getTradingData.js Script has Fully Loaded onto Website
} else if (request.from == "getTradingData" && request.subject == "updatePrice") {
price = request.price
}
});
However the response return as undefined. So basically I am not getting a response back.
Here is what I have in my getTradingData.js that should respond to the message:
alert("getTradingData.js is Running");
//Send message to let the extension know the script has been injected on site
chrome.runtime.sendMessage({
from: 'getTradingData',
subject: 'scriptLoaded'
});
chrome.runtime.onConnect.addListener(function(port) { //THIS DOESN'T WORK EITHER
console.assert(port.name == "tradingdata");
port.onMessage.addListener(function(request) {
if (request.msg == "Knock knock")
port.postMessage({subject: "price"});
else if (msg.answer == "Madame")
port.postMessage({question: "Madame who?"});
else if (msg.answer == "Madame... Bovary")
port.postMessage({question: "I don't get it."});
});
});
//to check if script already injected
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
alert("got message"); //THIS IS NOT RUNNING
if (request.subject == "testConnection") {
sendResponse({msg: "getTradingDataScriptHere"});
}
});
//wait till item has loaded
$(".dl-header-figures").waitUntilExists(function(){
alert($(".dl-header-figures").text());
updatePrice();
});
function updatePrice(){
alert("updating price");
chrome.runtime.sendMessage({
from: 'getTradingData',
subject: 'updatePrice',
price: $(".dl-header-figures").text()
});
}
//TODO: Use long lived connections for this to work: https://developer.chrome.com/extensions/messaging
// setInterval(updatePrice(), 3000);
However this never gets activated, I never get the alert "got message".
Here is what my manifest.json looks like:
{
"manifest_version": 2,
"name": "Automated TradingView Strategy",
"description": "This extension shows a Google Image search result for the current page",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"background": {
"scripts": ["jquery-2.2.3.min.js", "background.js"]
},
"content_scripts": [
{
"matches": ["https://www.tradingview.com/chart/*", "http://www.tradingview.com/*"],
"js": ["jquery-2.2.3.min.js", "jquery.waituntilexists.min.js", "getTradingData.js"]
}
],
"permissions": [
"activeTab",
"tabs",
"*://*.tradingview.com/*",
"https://ajax.googleapis.com/"
]
}
What am I doing wrong? How can I make it send a response back. Even when I refresh extension which should reload background.js without reloading tabs which already has the content script injected in it I get no response because the Listener is not activated.
What are you trying to do exactly in your background script?
chrome.tabs.query runs only once when you load the extension, also, the scripts you are injecting with chrome.tabs.executeScript should be injected already because of the manifest.
I don't know exactly what you're trying to do, but, you can listen to an event every time a tab is updated (tabs are updated after being created) - chrome.tabs.onUpdated.addListener
Updated background.js:
alert("Automated TradingView Extension is running");
chrome.tabs.query({
url: 'https://www.tradingview.com/*'
}, function(tabs) {
console.log(tabs);
if (tabs.length == 1) {
chrome.tabs.sendMessage(tabs[0].id, {subject: "testConnection"}, function(response) {
if (response) {
alert("Script Already Injected. Do not reinject");
} else {
chrome.tabs.executeScript(tabs[0].id, {file: "jquery-2.2.3.min.js"});
chrome.tabs.executeScript(tabs[0].id, {file: "jquery.waituntilexists.min.js"});
chrome.tabs.executeScript(tabs[0].id, {file: "getTradingData.js"});
alert("Injected all Nessessary Scripts for Auto Trading View to work");
}
});
} else {
alert("Please have one and only one tradingview chart page opened.");
}
});
var price = "Waiting For Price"
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.subject == "getPrice") {
sendResponse({
price: price
});
} else if (request.from == "getTradingData" && request.subject == "scriptLoaded") {
//getTradingData.js Script has Fully Loaded onto Website
} else if (request.from == "getTradingData" && request.subject == "updatePrice") {
price = request.price
}
});

FireFox AddOn SDK close current tab

I am porting a Chrome Extension for FireFox using the Add-On SDK. I am using require("sdk/page-mod") to run a content script at the start of the document.
In the code, I need to close the current tab if some condition is met. In Chrome, I can send a message to the background.js file to have it close the current tab, but I am not able to figure this out for Firefox.
window.close() is very unreliable and I need to figure out a way to call a function in the main.js file from my content script.
Appreciate your help.
EDIT:
Below is my Chrome code, I need to port the same to FF AddOn SDK (FF Extension).
//in the content.js file
function closeCurrTab() {
chrome.runtime.sendMessage({action: "closeTab"}, function() {});
}
//below in the background.js file
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
switch (request.action) {
case 'closeTab':
try {
chrome.tabs.getSelected(function(tab) {removeTab(tab.id);});
} catch (e) {
alert(e);
}
break;
}
}
);
function removeTab(tabId) {
try {
chrome.tabs.remove(tabId, function() {});
} catch (e) {
alert(e);
}
}
in content script:
self.port.emit("close-tab");
in main.js
PageMod({
include: "*",
contentScriptFile: "./content-script.js",
onAttach: function(worker) {
worker.port.on("close-tab", function() {
tabs.activeTab.close();
});
}
});
The following might help if you are developing an extension of firefox:
function onError(error) {
console.log(`Error: ${error}`);
}
function onRemoved() {
console.log(`Removed`);
}
function closeTabs(tabIds) {
removing = browser.tabs.remove(tabIds);
removing.then(onRemoved, onError);
}
var querying = browser.tabs.query({currentWindow: true});
querying.than(closeTabs, onError);
This will close the current tab:
require("sdk/tabs").activeTab.close();
Here's an expanded example that implements a toolbar button that closes the current tab ( silly example, I know ):
var ActionButton = require("sdk/ui/button/action").ActionButton;
var button = ActionButton({
id: "my-button-id",
label: "Close this tab",
icon: {
"16": "chrome://mozapps/skin/extensions/extensionGeneric.png"
},
onClick: function(state) {
require('sdk/tabs').activeTab.close();
}
});
For more info, please see the documentation for the tabs module.

Remember state chrome extension

I use a chrome extension to fire two content scripts to inject css. If the user opens the page the contentscript-on.js loads (defined in my manifest.json):
manifest.json
{
"name": "tools",
"version": "1.1",
"description": "tools",
"browser_action": {
"default_icon": "icon-on.png",
"default_title": "tools"
},
"manifest_version": 2,
"content_scripts": [
{
"matches": [ "*://*/*" ],
"include_globs": [ "*://app.example.*/*" ],
"js": ["jquery-1.11.0.min.js", "contentscript-on.js"]
}
],
"background": {
"scripts": ["background.js"],
"persistent": true
},
"permissions": [
"storage",
"https://*.app.example.de/*", "tabs", "webNavigation"
]
}
background.js
function getToggle(callback) { // expects function(value){...}
chrome.storage.local.get('toggle', function(data){
if(data.toggle === undefined) {
callback(true); // default value
} else {
callback(data.toggle);
}
});
}
function setToggle(value, callback){ // expects function(){...}
chrome.storage.local.set({toggle : value}, function(){
if(chrome.runtime.lastError) {
throw Error(chrome.runtime.lastError);
} else {
callback();
}
});
}
chrome.browserAction.onClicked.addListener( function(tab) {
getToggle(function(toggle){
toggle = !toggle;
setToggle(toggle, function(){
if(toggle){
//change the icon after pushed the icon to On
chrome.browserAction.setIcon({path: "icon-on.png", tabId:tab.id});
//start the content script to hide dashboard
chrome.tabs.executeScript({file:"contentscript-on.js"});
}
else{
//change the icon after pushed the icon to Off
chrome.browserAction.setIcon({path: "icon-off.png", tabId:tab.id});
//start the content script to hide dashboard
chrome.tabs.executeScript({file:"contentscript-off.js"});
}
});
});
});
contentscript-on.js
$(document).ready(function() {
chrome.storage.local.get('toggle', function(data) {
if (data.toggle === false) {
return;
} else {
// do some css inject
}
});
});
contentscript-off.js
$(document).ready(function() {
// set css to original
});
Everything works fine, but how can I save the "state" of the icon? If the user close the browser and open it again, the last used contentscript should load.
Thank you very much for your help.
You have two methods (at least), one is "old" and one is "new".
Old: localStorage
Your extension pages share a common localStorage object you can read/write, and it is persistent through browser restarts.
Working with it is synchronous:
var toggle;
if(localStorage.toggle === undefined){
localStorage.toggle = true;
}
toggle = localStorage.toggle;
chrome.browserAction.onClicked.addListener( function(tab) {
var toggle = !toggle;
localStorage.toggle = toggle;
/* The rest of your code; at this point toggle is saved */
});
It's simple to work with, but there are downsides: localStorage context is different for content scripts, so they need to communicate via Messaging to get the values from the background script; also, complications arise if the extension is used in Incognito mode.
New: chrome.storage API
To work with the new method, you need permission "storage" in the manifest (does not generate a warning).
Also, unlike localStorage, working with it is asynchronous, i.e. you will need to use callbacks:
function getToggle(callback) { // expects function(value){...}
chrome.storage.local.get('toggle', function(data){
if(data.toggle === undefined) {
callback(true); // default value
} else {
callback(data.toggle);
}
});
}
function setToggle(value, callback){ // expects function(){...}
chrome.storage.local.set({toggle : value}, function(){
if(chrome.runtime.lastError) {
throw Error(chrome.runtime.lastError);
} else {
callback();
}
});
}
chrome.browserAction.onClicked.addListener( function(tab) {
getToggle(function(toggle){
toggle = !toggle;
setToggle(toggle, function(){
/* The rest of your code; at this point toggle is saved */
});
});
});
Asynchronous code is a bit harder to work with, but you get some advantages. Namely, content scripts can use chrome.storage directly instead of communicating with the parent, you can watch for changes with onChanged, and you can use chrome.storage.sync instead of (or together with) chrome.storage.local to propagate changes to all browsers a user is logged into.
EDIT
I'm including a full solution, since the OP made a mistake of mixing per-tab state and global state.
contentscript.js
$(document).ready(function() {
chrome.storage.local.get('toggle', function(data) {
if (data.toggle === false) {
return;
} else {
/* do some css inject */
}
});
chrome.storage.onChanged.addListener(function(changes, areaName){
if(areaName == "local" && changes.toggle) {
if(changes.toggle.newValue) {
/* do some css inject */
} else {
/* set css to original */
}
}
});
});
background.js:
/* getToggle, setToggle as above */
function setIcon(value){
var path = (value)?"icon-on.png":"icon-off.png";
chrome.browserAction.setIcon({path: path});
}
getToggle(setIcon); // Initial state
chrome.browserAction.onClicked.addListener( function(tab) {
getToggle(function(toggle){
setToggle(!toggle, function(){
setIcon(!toggle);
});
});
});
This way, you only need one content script.

Categories

Resources