I'm working on a todo app that uses indexedDB. Everything is working fine on my Flame phone. One exception: when I open the app a day later to check my tasks, it doesn't reload the database. Yesterday's items still appear as today's items, etc. I have to hold the home button, quit the app and the start it again. I used the code below to force reloads, but it doesn't seem to work.
<meta http-equiv = "pragma" content = "no-cache">
<meta http-equiv = "cache-control" content = "no-cache">
Use the visibilitychange event to listen when your app becomes active.
function handleVisibilityChange() {
if (!document.hidden) {
// reload
}
}
document.addEventListener("visibilitychange", handleVisibilityChange, false);
If you want to reload, you need to planify a regular reload.
I would use window.setTimeout which reload the page or reload your base.
// Initial load
loadInterface();
function loadInterface() {
// You load your tasks
// You change your interface
// ...
// Next reload : in 60m*60s = 1h
window.setTimeout(loadInterface, 3600000);
}
Related
I'm working on a simple link sharing extension (pinboard, readability, delicious, etc), and have a question about how to properly deal with a context menu item. In my non-persistent background page I call chrome.contextMenus.create and chrome.contextMenus.onClicked.addListener to setup/respond to the context menu.
The context menu entry works as expected. But the background page is showing the following error (right after it starts and before I've used the entry) :
contextMenus.create: Cannot create item with duplicate id id_share_link at chrome-extension://.../share.js:52:30 lastError:29 set
This made me realize that at no point do I remove the item or the listener. Knowing little about javascript and extensions, I'm left wondering if I'm doing everything correctly. I'm assuming this top-level code is going to re-execute every time the background page is invoked. So there are going to be redundant calls to create and addListener (and hence the error I see being logged).
I clearly can't do cleanup in response to suspend, as these calls need to be present to wake up the background script.
Should I be handling things differently?
If you want to use an event page, ie a non-persistent background page, as you call it, you should register a context menu via contextMenus.create in the event handler of runtime.onInstalled, as these context menu registrations ”persist“ anyways.
You have to add the listener-function for the contextMenus.onClicked event every time the event page gets reloaded, though, as the registration of your wish to listen on that event persists, while the handler callback itself does not. So generally don't call contextMenus.onClicked.addListener from runtime.onInstalled, but from top level or other code, that is guaranteed to be executed each time the event page loads.[1]
You can handle it one of two ways:
You can add the context menu and the listeners on install using:
chrome.runtime.onInstalled.addListener(function() {
/* Add context menu and listener */
});
You can remove the context menu and listener, and then re-add it each time the file is called.
[solution may no longer be the case, read comment]
runtime.onInstalled is not triggered if you disable/enable your extension.
My solution is to always add menu items and swallow errors:
'use strict';
{
let seqId = 0;
const createMenuLinkEntry = (title, tab2url) => {
const id = (++seqId).toString();
chrome.contextMenus.create({
id: id,
title: title,
contexts: ['browser_action'],
}, () => {
const err = chrome.runtime.lastError;
if(err) {
console.warn('Context menu error ignored:', err);
}
});
};
createMenuLinkEntry('Go to Google', (tab) => 'https://google.com');
createMenuLinkEntry('Go to GitHub', (tab) => 'https://github.com');
} // namespace
I want to build a extension that behaves like a timer. It should count down the seconds when activated, but should do nothing with inactive.
The chrome.alarms API is interesting, but does not have enough precision nor granularity. It only fires at most once per minute, and it may fire late. If I want something to execute more often than that, I can't use this API.
Then, the next natural solution is to use a background page and use setTimeout or setInterval in there. However, background pages are persistent, and they take up resources (e.g. memory) even when idle. So they are not ideal.
The best solution seems to be an event page to run the timer. However, the documentation says:
Once it has been loaded, the event page will stay running as long as it is active (for example, calling an extension API or issuing a network request).
[…]
Once the event page has been idle a short time (a few seconds), the runtime.onSuspend event is dispatched. The event page has a few more seconds to handle this event before it is forcibly unloaded.
[…]
If your extension uses window.setTimeout() or window.setInterval(), switch to using the alarms API instead. DOM-based timers won't be honored if the event page shuts down.
Unfortunately, having an active setInterval is not enough to consider an event page active. In fact, from my tests, an interval up to 10 seconds is short enough to keep the event page running, but anything greater than 10 or 15 seconds is too far apart and the event page will get unloaded. I've tested this on my crx-reload-tab project.
I believe what I want is a middle ground:
I want a background page that I can load and unload on demand. (Instead of one that keeps loaded all the time.)
I want an event page that stays persistent in memory for as long as I say; but otherwise could be unloaded. (Instead of one that gets unloaded automatically by the browser.)
Is it possible? How can I do it?
Background pages cannot be unloaded on demand, and Chrome decides Event page lifecycle for you (there is nothing you can do in onSuspend to prevent it).
If your concern is timers, you could try my solution from this answer, which basically splits a timer into shorter timers for a "sparse" busy-wait. That's enough to keep the event page loaded and is a viable solution if you don't need to do that frequently.
In general, there are some things that will keep an event page loaded:
If you're using message passing, be sure to close unused message ports. The event page will not shut down until all message ports are closed.
This can be exploited if you have any other context to keep an open Port to, for example a content script. See Long-lived connections docs for more details.
In practice, if you often or constantly need precise, sub-minute timers, an Event page is a bad solution. Your resource gains from using one might not justify it.
As mentioned in Xan's answer we can abuse messaging. There's nothing wrong about it either in case you want to temporarily prevent the event page from unloading. For example while displaying a progress meter using chrome.notifications API or any other activity based on setTimeout/setInterval that may exceed the default unload timeout which is 5-15 seconds.
Demo
It creates an iframe in the background page and the iframe connects to the background page. In addition to manifest.json and a background script you'll need to make two additional files bg-iframe.html and bg-iframe.js with the code specified below.
manifest.json excerpt:
"background": {
"scripts": ["bg.js"],
"persistent": false
}
bg.js:
function preventUnload() {
let iframe = document.querySelector('iframe');
if (!iframe) {
iframe = document.createElement('iframe');
document.body.appendChild(iframe).src = 'bg-iframe.html';
}
}
function allowUnload() {
let iframe = document.querySelector('iframe');
if (iframe) iframe.remove();
}
chrome.runtime.onConnect.addListener(() => {});
bg-iframe.html:
<script src="bg-iframe.js"></script>
bg-iframe.js:
chrome.runtime.connect();
Usage example in bg.js:
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message === 'start') doSomething();
});
function doSomething() {
preventUnload();
// do something asynchronous that's spread over time
// like for example consecutive setTimeout or setInterval calls
let ticks = 20;
const interval = setInterval(tick, 1000);
function tick() {
// do something
// ................
if (--ticks <= 0) done();
}
function done() {
clearInterval(interval);
allowUnload();
}
}
I use this function:
function _doNotSleep() {
if (isActive) {
setTimeout(() => {
fetch(chrome.runtime.getURL('manifest.json'));
_doNotSleep();
}, 2000);
}
}
But the problem with such approach is that Devtools network tab polluted with this http stub.
I got a quick question for you guys, I'm new to making Chrome Extensions and the idea I have for one I'm not sure if I can do this with an extension or not. I've been looking through the API but haven't run across something that might help. So my idea for my extension is that whoever downloads the extension will be able to set a pin code they will click the icon and it basically will lock down the browser so if someone else came to the browser they would only be able to access that one page and what it would lead to, they wouldn't be able to us the url bar or have access to the tabs unless permitted.Then the owner can press a hot key and it will ask them for there pin and will unlock the browser if need be.Or even put it in the presentation mode but not able to get out of it without a password? Is this something a chrome extension could do or am I going at this the wrong way? I noticed there are some options in the Chrome://about settings where you can compact the url bar and also make the tabs on the side bar. Any help or direction for this would be great, thanks!
You can create an options page where the extension settings are saved, and then create an option called eg DisableBrowser.
In file background.js, we monitor the onBeforeRequest event, and then check the value of variable DisableBrowser if it has true value, set the value of cancel parameter onBeforeRequest event, being equal to true when cancel is equal value to true, the request is canceled.
In short, just cancel and set equal to true and everything is rejected, ie, the browser will not open urls while the extension is installed and enabled.
Update:
The sample code below is the content of background.js file, showing how to allow only certain urls that are allowed in a list is executed successfully, and consequently all other urls will be denied and fails when opened.
// callback
var onBeforeRequestCallback = function( details ) {
// List of Urls Allowed
// You can create an array or use localStorage through options.html page,
// to save the urls allowed,
// then check and if an allowed URL, the request is not canceled, or in other words, it is permitted,
// in case of failure it is canceled and is not permitted.
if ( details.url === 'https://www.google.com/' || details.url === 'http://www.bing.com/' ) {
return {
cancel : false
};
} else {
return {
cancel : true
};
}
};
// filter
var onBeforeRequestFilter = {
urls : [
"http://*/*",
"https://*/*"
]
};
// opt_extraInfoSpec
var onBeforeRequestInfo = [
"blocking",
"requestBody"
];
// Monitors onBeforeRequest event
chrome.webRequest.onBeforeRequest.addListener( onBeforeRequestCallback, onBeforeRequestFilter, onBeforeRequestInfo );
Help Links:
options
background
onBeforeRequest
localStorage
I have a chrome extension which injects an iframe into every open tab. I have a chrome.runtime.onInstalled listener in my background.js which manually injects the required scripts as follows (Details of the API here : http://developer.chrome.com/extensions/runtime.html#event-onInstalled ) :
background.js
var injectIframeInAllTabs = function(){
console.log("reinject content scripts into all tabs");
var manifest = chrome.app.getDetails();
chrome.windows.getAll({},function(windows){
for( var win in windows ){
chrome.tabs.getAllInWindow(win.id, function reloadTabs(tabs) {
for (var i in tabs) {
var scripts = manifest.content_scripts[0].js;
console.log("content scripts ", scripts);
var k = 0, s = scripts.length;
for( ; k < s; k++ ) {
chrome.tabs.executeScript(tabs[i].id, {
file: scripts[k]
});
}
}
});
}
});
};
This works fine when I first install the extension. I want to do the same when my extension is updated. If I run the same script on update as well, I do not see a new iframe injected. Not only that, if I try to send a message to my content script AFTER the update, none of the messages go through to the content script. I have seen other people also running into the same issue on SO (Chrome: message content-script on runtime.onInstalled). What is the correct way of removing old content scripts and injecting new ones after chrome extension update?
When the extension is updated Chrome automatically cuts off all the "old" content scripts from talking to the background page and they also throw an exception if the old content script does try to communicate with the runtime. This was the missing piece for me. All I did was, in chrome.runtime.onInstalled in bg.js, I call the same method as posted in the question. That injects another iframe that talks to the correct runtime. At some point in time, the old content scripts tries to talk to the runtime which fails. I catch that exception and just wipeout the old content script. Also note that, each iframe gets injected into its own "isolated world" (Isolated world is explained here: http://www.youtube.com/watch?v=laLudeUmXHM) hence newly injected iframe cannot clear out the old lingering iframe.
Hope this helps someone in future!
There is no way to "remove" old content scripts (Apart from reloading the page in question using window.location.reload, which would be bad)
If you want to be more flexible about what code you execute in your content script, use the "code" parameter in the executeScript function, that lets you pass in a raw string with javascript code. If your content script is just one big function (i.e. content_script_function) which lives in background.js
in background.js:
function content_script_function(relevant_background_script_info) {
// this function will be serialized as a string using .toString()
// and will be called in the context of the content script page
// do your content script stuff here...
}
function execute_script_in_content_page(info) {
chrome.tabs.executeScript(tabid,
{code: "(" + content_script_function.toString() + ")(" +
JSON.stringify(info) + ");"});
}
chrome.tabs.onUpdated.addListener(
execute_script_in_content_page.bind( { reason: 'onUpdated',
otherinfo: chrome.app.getDetails() });
chrome.runtime.onInstalled.addListener(
execute_script_in_content_page.bind( { reason: 'onInstalled',
otherinfo: chrome.app.getDetails() });
)
Where relevant_background_script_info contains information about the background page, i.e. which version it is, whether there was an upgrade event, and why the function is being called. The content script page still maintains all its relevant state. This way you have full control over how to handle an "upgrade" event.
How to tell google syndication not to wait forever to load the ads in case of slow internet connection or otherwise too? Can we fix a time that says okay go and search for 5ms to get ads otherwise don’t delay the load of rest of page.
The YSlow extension for Firebug is great for this sort of thing, it benchmarks your page loading and tells you what's slow, and advises what techniques you can use to improve things.
For example, it gives you advice on where to put your javascript to aid the speed of your site as perceived by the user.
Assuming you mean this is on your site, be sure that your javascript is loaded at the end of the page so your other content can load first
see this blog item "Google Ads Async (asynchronous)" might give you a good starting point for this:
<script type="text/javascript"><!--
// dynamically Load Ads out-of-band
setTimeout((function ()
{
// placeholder for ads
var eleAds = document.createElement("ads");
// dynamic script element
var eleScript = document.createElement("script");
// remember the implementation of document.write function
w = document.write;
// override and replace with our version
document.write = (function(params)
{
// replace our placeholder with real ads
eleAds.innerHTML = params;
// put the old implementation back in place
document.write=w;
});
// setup the ads script element
eleScript.setAttribute("type", "text/javascript");
eleScript.setAttribute("src", "http://pagead2.googlesyndication.com/pagead/show_ads.js");
// add the two elements, causing the ads script to run
document.body.appendChild(eleAds);
document.body.appendChild(eleScript);
}), 1);
//-->
</script>