HTML5 offline web apps - code experiments with several browsers

Last week I was discussing HTML5 offline web app features with some coworkers and I realized it was time to play around with a bit of code.
You can always support your points in a geek discussion by pop'n open a big ol can o whup'code right? Until someone brings out the newclear option! Thank you, I'm here all week ;)

Okay - my goal was to get a some first hand experience with the code and then run it in several different browsers. I chose the browsers listed below because they are available on a variety of client device types and operating systems.
UPDATE 09/11/09 7.44pm:Added results below for iPhone Safari browser!

Feel free to make a case to to run the code in some other browser - I'll add the info to the results too.

I'm by no means and expert here. I've done a little reading and a little coding and I'm hoping you readers will share your experiences and advice. I'll update the post with the good stuff.

Heavy opinion section


There is no such thing as "offline web apps". The words "offline web apps" IMHO are misleading because for some folks it brings to mind "stand alone apps" such as native apps, or runtime based apps written for a java vm or adobe air.
In my opinion there are only "web apps". Some of them can work offline.
Web apps must be able to take full advantage of the client device, the cloud and work when a network connection is not available.

Reference materials and background reading


*) Offline app section of HTML5 spec.
*) W3C Web Storage technical docs
*) text/cache-manifest mime type details and file format.
*) Mark Finkle's post about the Firefox 3 implementation with a sample app.
*) John Resig's post about online / offline events

The code


The goal for this code was to experiment with the three main areas of functionality that allow web apps to work offline.
*) Online/Offline state: the ability to detect when the client device is online vs offline
*) Application cache: the ability of the web app to instruct the browser to cache the web app's resources (html, scripts, images, arbitrary data) for offline use.
*) Local storage: the ability to store application runtime data on the client device and retrieve it after browser shutdown/startup.

Note: when I ran this code in the various browsers I was also tailing my apache access log to watch how they were requesting content. This was helpful in observing caching behaviors and checking that when a browser thought it was offline it wasn't connecting to the web server to get content ;)


How to instruct the browser to use an application cache.



< !DOCTYPE HTML >
< HTML manifest="./cache.manifest" >
...the rest of your HTML5 markup.
< /HTML >





Here is my app's cache.manifest file. This must be served as Content-Type: text/cache-manifest



CACHE MANIFEST
# comment with version string to tweak v 6
theapp.html
blank.png
bug.png
heart.png
arb_bin_data.php

NETWORK:
noncached.html

FALLBACK:
/ ./offline.html





The javascript I wrote to experiment with HTML5 offline app features



/* print out some information about offline features */
function print_offline_support(){
debug_out("navigator.onLine defined: " +
(typeof(navigator.onLine) == 'undefined' ? "no" : "yes"));

debug_out("window.localStorage defined: " +
(typeof(window.localStorage) == 'undefined' ? "no" : "yes"));

debug_out("window.applicationCache defined: " +
(typeof(window.applicationCache) == 'undefined' ? "no" : "yes"));

try{
document.body.addEventListener("online", function(){
debug_out("got event: document.body.online");
}, false);

document.body.addEventListener("offline", function(){
debug_out("got event: document.body.offline");
}, false);

debug_out("document.body.online and document.body.offline events: yes");
}
catch(err){
debug_out("document.body.online and document.body.offline events: no");
debug_out(" event reg debug: " + err);
if(typeof(navigator.onLine) != 'undefined')
{
debug_out(" using cheezy_online_events...");
cheezy_online_events();
}
}

if(typeof(navigator.onLine) != 'undefined'){
debug_out("online state:" + (navigator.onLine == true ? "online" : "offline"));
}
else{
debug_out("online state: undefined");
}
}

/* cheezy way to check online status ..and possibly generate
an event in the app */

function cheezy_online_events(){
debug_out("online state:" + (navigator.onLine == true ? "online" : "offline"));
setTimeout(cheezy_online_events, 5000);
}

/* store some string data to local storage
This is using some text input elements on the page
for input.*/

function store_local(domid_prefix){
debug_out("+,store_local()");
var keyinput = document.getElementById(domid_prefix + "key").value;
var valinput = document.getElementById(domid_prefix + "val").value;
try{
if(typeof(window.localStorage) != 'undefined'){
window.localStorage.setItem(keyinput,valinput);
}
else{
throw "window.localStorage, not defined";
}
}
catch(err){
debug_out("store_local,error," + err);
}
debug_out("-,store_local()");
}

/* load some string data from local storage
This is using some text input elements on the page
for the key name input and value output.*/

function load_local(domid_prefix){
debug_out("+,load_local()");
var keyinput = document.getElementById(domid_prefix + "key").value;
var valoutput = document.getElementById(domid_prefix + "val");
try{
if(typeof(window.localStorage) != 'undefined'){
valoutput.value = window.localStorage.getItem(keyinput);
}
else{
throw "window.localStorage, not defined";
}
}
catch(err){
debug_out("store_local,error," + err);
}
debug_out("-,load_local()");
}

/* see http://dev.w3.org/html5/spec/offline.html#dom-appcache-update*/
function update_app_cache(){
debug_out("+,update_app_cache()");
try{
if(typeof(window.applicationCache) != 'undefined') {
window.applicationCache.update();
}
else{
throw "window.applicationCache, not defined";
}
}
catch(err){
debug_out("update failed,error," + err);
}
debug_out("-,update_app_cache()");
}

/* see http://dev.w3.org/html5/spec/offline.html#dom-appcache-swapcache*/
function swap_app_cache(){
debug_out("+,swap_app_cache()");
try{
if(typeof(window.applicationCache) != 'undefined') {
window.applicationCache.swapCache();
}
else{
throw "window.applicationCache, not defined";
}
}
catch(err){
debug_out("swap failed,error," + err);
}
debug_out("-,swap_app_cache()");
}

/* function to print to the text area element with the id="debug_area" */
function debug_out(str){
document.getElementById("debug_area").value += str + "\n";
}




Results



Firefox v 3.5.5, Windows PC


Output of print_offline_support() function from above:


navigator.onLine defined: yes
window.localStorage defined: yes
window.applicationCache defined: yes
document.body.online and document.body.offline events: yes
online state:online


Does the browser request manifest.cache and its entries?


Yes.

If you have multiple versions of firefox installed be prepared for headaches. I
had 3.5.5 and 3.6b4 installed and just about went crazy trying to figure out why they
wouldn't request the cache.manifest and the entries inside. I finally uninstalled both,
reinstalled 3.5.5 and starting using different profiles. Just a guess: they were both sharing the same
profile directory and confusing each other because of that. I'm now using separate "developer"
profile directories for each version and things are working more or less as expected.
See this page about profiles at Mozilla.


Can "offline mode" can be invoked by app developer for testing?


Yes, go to the File menu and select the "work offline" menu item.

I use this feature to to cause the online / offline events to be fired through
document.body.online and document.body.offline handlers. NICE!


Does window.localStorage.getItem() / setItem() work after browser shutdown / restart?


Yes.


Some window.applicationCache.update() observations.


Browser requests cache.manifest and, if modified also requests entries in the
manifest.


Some observations window.applicationCache.swapCache() observations.


An exception is thrown.
This page at Mozilla says swapCache() not yet supported.


Disconnect and disable network adapters. Does app work when going to url in browser?


Yes! But ... navigator.onLine was still == true.

The online / offline events seem a little flakey to me. It seems as thought the browser really wants
to to think it is online. Even when I've explicitly disabled all the network adapters, disconnected
networks, turned off the web server :).




Firefox v 3.6b4, Windows PC


Output of print_offline_support() function from above:


navigator.onLine defined: yes
window.localStorage defined: yes
window.applicationCache defined: yes
document.body.online and document.body.offline events: yes
online state:online


Does the browser request manifest.cache and its entries?


Yes.

Seemed to work the same as FF 3.5.5. FYI - recommend two different profiles if using
two diff versions of the browser. See this page at Mozilla.


Can "offline mode" can be invoked by app developer for testing?


Yes, go to the File menu and select the "work offline" menu item.

Seems to work like FF 3.5.5 and sent events to document.body.online and document.body.offline event listeners.


Does window.localStorage.getItem() / setItem() work after browser shutdown / restart?


Yes.


Some window.applicationCache.update() observations.


Seemed to work the same as FF 3.5.5. (see comment above)


Some observations window.applicationCache.swapCache() observations.


Seemed to work the same as FF 3.5.5. (see comment above)


Disconnect and disable network adapters. Does app work when going to url in browser?


Yes! ...and it seemed to track offline / online state better than FF 3.5.5. Online/offline events
seemed to be consistent with disabling my network adapters whereas they weren't in FF 3.5.5.




Chrome v 4.0.223.16, Windows PC


Note: Chrome does have offline support through Gears and even through the results below aren't as good as Firefox and Safari, I fully expect Chrome to catch up based on this article.


Output of print_offline_support() function from above:


navigator.onLine defined: yes
window.localStorage defined: yes
window.applicationCache defined: no
document.body.online and document.body.offline events: yes
online state:online


Does the browser request manifest.cache and its entries?


No.


Can "offline mode" can be invoked by app developer for testing?


I haven't found one.


Does window.localStorage.getItem() / setItem() work after browser shutdown / restart?


Yes.


Some window.applicationCache.update() observations.


n/a. window.applicationCache not defined.


Some observations window.applicationCache.swapCache() observations.


n/a. no window.applicationCache not defined.


Disconnect and disable network adapters. Does app work when going to url in browser?


n/a. I wouldn't expect this to work becuase the application cache wasn't available.




Safari v 4.0.3 (531.9.1), Windows PC


Output of print_offline_support() function from above:


navigator.onLine defined: yes

window.localStorage defined: yes

window.applicationCache defined: yes

document.body.online and document.body.offline events: yes


Does the browser request manifest.cache and its entries?


Yes.


Can "offline mode" can be invoked by app developer for testing?


I haven't found one.


Does window.localStorage.getItem() / setItem() work after browser shutdown / restart?


Yes.


Some window.applicationCache.update() observations.


Browser requests cache.manifest, and if modified, the entries declared inside.


Some observations window.applicationCache.swapCache() observations.


I'm not really sure how to use swapCache() yet even after looking at the spec section a few
times.

Seems to do something on first call after window.applicationCache.update(). On second call throws
INVALID_STATE_ERR exception.


Disconnect and disable network adapters. Does app work when going to url in browser?


Yes!

Note that Safari, similar to FF 3.5.5, did not seem to track online / offline state as I enabled/disabled
my network adapters. However, when I closed and reopened the browser the app did work and
navigator.onLine was set == false.




Safari v 4.0 (526.16), iPhone OS 3_1_2


Output of print_offline_support() function from above:


navigator.onLine defined: yes
window.localStorage defined: yes
window.applicationCache defined: yes
document.body.online and document.body.offline events: yes
online state:online


Does the browser request manifest.cache and its entries?


Yes.


Can "offline mode" can be invoked by app developer for testing?


Sort of: on the iPhone I just used "airplane mode" to test offline functionality. It worked great!


Does window.localStorage.getItem() / setItem() work after browser shutdown / restart?


Yes.


Some window.applicationCache.update() observations.


Browser requests cache.manifest, and if modified, the entries declared inside. Good!


Some observations window.applicationCache.swapCache() observations.


Same as Windows PC Safari (see comments above)


Disconnect and disable network adapters. Does app work when going to url in browser?


Yes!

On the iPhone I just turned on airplane mode to test this. It totally worked! Kudos to Apple!.





Internet Explorer v 8.0.6001.18702, Windows PC


Output of print_offline_support() function from above:


navigator.onLine defined: yes
window.localStorage defined: yes
window.applicationCache defined: no
document.body.online and document.body.offline events: no
event reg debug: TypeError: Object doesn't support this property or method
using cheezy_online_events...
online state:online


Does the browser request manifest.cache and its entries?


No.


Can "offline mode" can be invoked by app developer for testing?


Yes. Go to the File menu, select the "Work Offline" menu item.

Using this menu did cause navigator.onLine to be set to true / false. This allowed
my cheezy_online_events() function (in code above) to pick this up. This is good!


Does window.localStorage.getItem() / setItem() work after browser shutdown / restart?


Yes.


Some window.applicationCache.update() observations.


n/a. window.applicatoinCache not defined.


Some observations window.applicationCache.swapCache() observations.


n/a. window.applicatoinCache not defined.


Disconnect and disable network adapters. Does app work when going to url in browser?


n/a. I wouldn't expect this to work becuase the application cache wasn't available.

Note: Internet Explorer 8 did track the online / offline state as I enabled / disabled network adapters. (as seen via cheezy_online_events()).





Opera v 10.10 build 1893, Windows PC


Output of print_offline_support() function from above:



navigator.onLine defined: yes
window.localStorage defined: no
window.applicationCache defined: no
document.body.online and document.body.offline events: yes
online state:offline


Does the browser request manifest.cache and its entries?


No.


Can "offline mode" can be invoked by app developer for testing?


Yes. Go to the File menu, select the "Work Offline" menu item.

Note: using this menu did not cause offline / onlines events to be sent to
document.body.offline and document.body.online listeners.


Does window.localStorage.getItem() / setItem() work after browser shutdown / restart?


n/a. window.localStorage not defined.


Some window.applicationCache.update() observations.


n/a. window.applicatoinCache not defined.


Some observations window.applicationCache.swapCache() observations.


n/a. window.applicatoinCache not defined.


Disconnect and disable network adapters. Does app work when going to url in browser?


n/a. I wouldn't expect this to work becuase the application cache wasn't available.

Note: Opera did not seem to track online / offline state as I enabled / disabled network adapters.

Nähere Informationen zur Compiler-Optimierung finden Sie in unserem Optimierungshinweis.