I'm currently creating my first Chrome extension, so far so good.
It's just a little test where I run multiple timers.
But obviously all my timers reset when I open and close the extension.
So to keep all my timers running, I would have to same them somehow when I close the extension and make them run in the background page.
When I open the extension again, those timers should be send back to the open page.
How would you handle this?
I already have an array of all my timers, what would be the best option for me>
A background page runs at all times when the extension is enabled. You cannot see it, but it can modify other aspects of the extension, like setting the browser action badge.
For example, the following would set the icon badge to the number of unread items in a hypothetical service:
function getUnreadItems(callback) {
$.ajax(..., function(data) {
process(data);
callback(data);
});
}
function updateBadge() {
getUnreadItems(function(data) {
chrome.browserAction.setBadgeText({text:data.unreadItems});
});
}
Then, you can make a request and schedule it so the data is retrieved and processed regularly, you can also stop the request at any time.
var pollInterval = 1000 * 60; // 1 minute
function startRequest() {
updateBadge();
window.setTimeout(startRequest, pollInterval);
}
function stopRequest() {
window.clearTimeout(timerId);
}
Now just load it...
onload='startRequest()'
Also, HTML5 offline storage is good for storing data and constantly update it...
var data = "blah";
localStorage.myTextData = data;
Related
I am trying to write a Google Apps script which has a client and server side component. The client side component displays a progress bar. The client calls server side functions (which are called asynchronously), whose progress has to be shown in the client side progress-bar. Now, what I want is to be able to update the client side progress bar based on feedback from the server side functions. Is this possible?
The complexity is created due the the fact that JS makes the server-side calls asynchronously and hence I cannot really have a loop on the client side calling the functions and updating the progress bar.
I could of course split up the execution of the server side function in multiple steps, call one by one from the client side, each time updating the status bar. But I'm wondering if there's a better solution. Is there a way to call a client side function from the server side, and have that update the progress bar based on the argument passed? Or is there a way to access the client side progress-bar object from server side and modify it?
The way I've handled this is to have a middleman (giving a shout out now to Romain Vialard for the idea) handle the progress: Firebase
The HTML/client side can connect to your Firebase account (they're free!) and "watch" for changes.
The client side code can update the database as it progresses through the code - those changes are immediately fed back to the HTML page via Firebase. With that, you can update a progress bar.
Romain has a small example/description here
The code I use:
//Connect to firebase
var fb = new Firebase("https://YOUR_DATABASE.firebaseio.com/");
//Grab the 'child' holding the progress info
var ref = fb.child('Progress');
//When the value changes
ref.on("value", function(data) {
if (data.val()) {
var perc = data.val() * 100;
document.getElementById("load").innerHTML = "<div class='determinate' style='width:" + perc + "%\'></div>";
}
});
On the client side, I use the Firebase library to update the progress:
var fb = FirebaseApp.getDatabaseByUrl("https://YOUR_DATABASE..firebaseio.com/");
var data = { "Progress": .25};
fb.updateData("/",data);
Rather than tying the work requests and progress updating together, I recommend you separate those two concerns.
On the server side, functions that are performing work at the request of the client should update a status store; this could be a ScriptProperty, for example. The work functions don't need to respond to the client until they have completed their work. The server should also have a function that can be called by the client to simply report the current progress.
When the client first calls the server to request work, it should also call the progress reporter. (Presumably, the first call will get a result of 0%.) The onSuccess handler for the status call can update whatever visual you're using to express progress, then call the server's progress reporter again, with itself as the success handler. This should be done with a delay, of course.
When progress reaches 100%, or the work is completed, the client's progress checker can be shut down.
Building on Jens' approach, you can use the CacheService as your data proxy, instead of an external service. The way that I've approached this is to have my "server" application generate an interim cache key which it returns to the "client" application's success callback. The client application then polls this cache key at an interval to see if a result has been returned into the cache by the server application.
The server application returns an interim cache key and contains some helper functions to simplify checking this on the client-side:
function someAsynchronousOperation() {
var interimCacheKey = createInterimCacheKey();
doSomethingComplicated(function(result) {
setCacheKey(interimCacheKey, result);
});
return interimCacheKey;
}
function createInterimCacheKey() {
return Utilities.getUuid();
}
function getCacheKey(cacheKey, returnEmpty) {
var cache = CacheService.getUserCache();
var result = cache.get(cacheKey);
if(result !== null || returnEmpty) {
return result;
}
}
function setCacheKey(cacheKey, value) {
var cache = CacheService.getUserCache();
return cache.put(cacheKey, value);
}
Note that by default getCacheKey doesn't return. This is so that google.script.run's successHandler doesn't get invoked until the cache entry returns non-null.
In the client application (in which I'm using Angular), you call off to the asynchronous operation in the server, and wait for its result:
google.script.run.withSuccessHandler(function(interimCacheKey) {
var interimCacheCheck = $interval(function() {
google.script.run.withSuccessHandler(function(result) {
$interval.cancel(interimCacheCheck);
handleSomeAsynchronousOperation(result);
}).getCacheKey(interimCacheKey, false);
}, 1000, 600); // Check result once per second for 10 minutes
}).someAsynchronousOperation();
Using this approach you could also report progress, and only cancel your check after the progress reaches 100%. You'd want to eliminate the interval expiry in that case.
My Webapp is React -> .NET -> SQL
I want to cache all post requests such that all .NET calls are made just once, next time it's fed from cache. For every small UI change in react I want to use the cache and save development time.
As it's Just for development, looking or something in Chrome maybe, is there an extension for such a task or any guide to what I should look into will be helpful.
How about using chrome.storag.local API where you can store, retrieve, and track changes to user data.
You can use it like this based from this SO post:
function fetchLive(callback) {
doSomething(function(data) {
chrome.storage.local.set({cache: data, cacheTime: Date.now()}, function() {
callback(data);
});
});
}
function fetch(callback) {
chrome.storage.local.get(['cache', 'cacheTime'], function(items) {
if (items.cache && items.cacheTime && items.cacheTime) {
if (items.cacheTime > Date.now() - 3600*1000) {
return callback(items.cache); // Serialization is auto, so nested objects are no problem
}
}
fetchLive(callback);
});
}
Just remember that:
Chrome employs two caches — an on-disk cache and a very fast in-memory cache. The lifetime of an in-memory cache is attached to the
lifetime of a render process, which roughly corresponds to a tab.
Requests that are answered from the in-memory cache are invisible to
the web request API.
I'm trying my hand at creating a chrome extension, but am running into a wall.
I want to be able to use the browser-action popup to write/modify values into local storage (extension storage).
Then, I want to use the stored values in a content script.
From what I've read, it looks like I need a background file? but I'm not sure.
Some coded examples would be extremely appreciated!
Thanks for your help!
You can avoid using a background page as a proxy if you use chrome.storage API. It's a storage solution that is available from Content Scripts directly.
Here is a comparison between it and localStorage in the context of Chrome extensions.
An important thing to note is that it's asynchronous, making code slightly more complicated than using localStorage:
/* ... */
chrome.storage.local.get('key', function(value){
// You can use value here
});
// But not here, as it will execute before the callback
/* ... */
But to be fair, if you go with the background being the proxy for data, message passing is still asynchronous.
One can argue that once the data is passed, localStorage works as a synchronous cache.
But that localStorage object is shared with the web page, which is insecure, and nobody stops you from having your own synchronous storage cache, initialized once with chrome.storage.local.get(null, /*...*/) and kept up to date with a chrome.storage.onChanged listener.
Background pages can access the localStorage variables saved by your extension. Your content script only has access to the localStorage of the website open in a specific tab. You will therefore need to send the variables from the background page to the content script. The content script can then access these variables.
The following code saves a localStorage variable in the background script and then sends it to the content script for use.
Since you requested a coded example, I've written you one. This project would have a background page and a content script. Using localStorage in your popup will allow the background page to access these variables for use in the content script.
Something like this:
background.js
// When a tab is updated
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo) {
// When the tab has loaded
if(changeInfo.status == 'complete') {
// Query open tabs
chrome.tabs.query({'active': true, 'lastFocusedWindow': true}, function (tabs) {
// Get URL of current tab
var tabURL = tabs[0].url;
// If localStorage is not empty
if(localStorage.length != 0) {
// Set a local storage variable
localStorage.helloworld = "Hello World";
// Send message to content script
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
// Send request to show the notification
chrome.tabs.sendMessage(tabs[0].id, {greeting: "hello"}, function(response) {
});
});
}
});
}
});
contentscript.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
// Use the local storage variable in some way
if(request.greeting == "hello") {
var hello = localStorage.helloworld;
// do something with the variable here
}
});
Once you have this working, consider switching to chrome.storage
I'm trying to access local storage from content scripts but even though the message passing is working, the output isn't as expected.
CONTENT SCRIPT
var varproxy = localStorage.getItem('proxy'); //gets data from options page saved to local storage
var proxy = "proxystring";
chrome.runtime.sendMessage({message:"hey"},
function(response) {
proxy = response.proxy;
console.log(response.proxy);
}
);
console.log(proxy);
BACKGROUND PAGE (For message passing)
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse)
{
if (request.message == "hey")
{
sendResponse({proxy: varproxy});
console.log('response sent');
}
else
{sendResponse({});}
});
The console logs the proxy as the value of varproxy and also "response sent" but the
console.log(proxy);
logs the proxy as "proxystring"
Why isn't the value of proxy getting changed? How do I change it as required?
Message sending -- among lots of chrome API function -- is an asynchronous function. The interpreter won't wait for the response, but jumps to the next line. So it can easily happen that log(proxy) will be evaluated first, since communicating with the background page takes some time. As soon as the response is received, the value of proxy changes.
Might I recommend you try out another implementation? What about Chrome Storage?
Then you don't need any message passing at all, because you can access chrome storage within content scripts.
Example, this is something I do in my extensions' content script to grab several values from chrome storage:
chrome.storage.sync.get({HFF_toolbar: 'yes',HFF_logging: 'yes',HFF_timer: '1 Minute'},
function (obj) {
toolbar_option = obj.HFF_toolbar;
logging_option = obj.HFF_logging;
timer_option = obj.HFF_timer;
/* the rest of my content script, using those options */
I personally found this approach much easier, for my purposes anyway, than message passing implementations.
Is there a Chrome extension post install hook/API function that will let me perform an action after the plugin is installed or updated?
I would like to perform an action after my extension is installed, and only right after it is installed. This action should only be performed once (post-install or post-update) of the extension.
Update
Some people have proposed setting the version of the extension in localStorage, the problem that I have is that the content script that has access to localStorage is not loaded into the page when the plugin is first installed.
AFAIK after a plugin is installed, and it makes use of a content script injected into the tab/page, the page has to be reloaded.
I don't know how to access localStorage from the background page; localStorage can only be accessed from a content script.
To get the version number from the background page to the content script requires the use of chrome API function to execute scripts:
chrome.tabs.executeScript(null, {code:function_to_execute}, function() { // callback });
However, when you install a plugin, and the page that this plugin needs to inject a content script into is already loaded, it does not inject the content script, you have to reload the page.
update 2
Looking at some of the tips provided in more detail, for the purpose of saving the version number, it is possible to access the localStorage of the background page. However, for what I need to do, which is reload a specific tab at a specific URL (in order to make sure the content script is the newest version) after installing or updating a plugin, it ended up being unnecessary to bother with localStorage.
For the sake of staying on topic, the advice given about writing the version number to localStorage (in the background page) and then checking against the version number in the manifest file is good enough to allow someone to run a script the first time it is installed/or updated.
HowTo
Make manifest file available to the background page (note: this is taken from somewhere else, I don't take credit for it, but I can't remember the source, if you know, let me know and I will add it).
// MAKE MANIFEST FILE AVAILABLE
chrome.manifest = (function() {
var manifestObject = false;
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
manifestObject = JSON.parse(xhr.responseText);
}
};
xhr.open("GET", chrome.extension.getURL('/manifest.json'), false);
try {
xhr.send();
} catch(e) {
console.log('Couldn\'t load manifest.json');
}
return manifestObject;
})();
Now you can access your version number like this: chrome.manifest.version
To write to localStorage just pass it in like so: localStorage['my_plugin_version'] = chrome.manifest.version
You can do this using a background page. When the extension is installed, the background page is opened in the background, and thus executed. To make sure it's not executed every time, simply store a value in localStorage.