Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any workaround to make use of html5 localstorage on both http and https?

I need to store some data client side and this data is too large to store it in a cookie. LocalStorage seemed like the perfect way of doing this but the thing is that the website that i will be using this has some parts that work on https and others with just http and as local storage can't access data from https that you set with http this doesn't seem like a viable solution anymore.

Any idea if there is any solution to this? Any other alternatives?

like image 955
daniels Avatar asked May 08 '12 16:05

daniels


1 Answers

Store all data on one domain, e.g. https://my.domain.org/.

  • On https protocols, simply use localStorage.setItem('key', 'value') to save the data.
  • On http protocols, embed a https frame, and use postMessage to save the data:

Demo: http://jsfiddle.net/gK7ce/4/ (with the helper page being located at http://jsfiddle.net/gK7ce/3/).

// Script at https://my.domain.org/postMessage
window.addEventListener('message', function(event) {
    // Domain restriction (to not leak variables to any page..)
    if (event.origin == 'http://my.domain.org' ||
        event.origin == 'https://my.domain.org') {
        var data = JSON.parse(event.data);
        if ('setItem' in data) {
            localStorage.setItem(data.setItem, data.value);
        } else if ('getItem' in data) {
            var gotItem = localStorage.getItem(data.getItem);
            // See below
            event.source.postMessage(
                '#localStorage#' + data.identifier + 
                (gotItem === null ? 'null#' : '#' + gotItem),
                event.origin
            );
        } else if ('removeItem' in data) {
            localStorage.removeItem(data.removeItem);
        }
    }
}, false);

On the http(s) page, the frame can be embedded as follows (replace https://my.mydomain.com with the actual URL. Note that you can simply get a reference to the frame, and use the src attribute):

<iframe name="myPostMessage" src="https://my.domain.org/postMessage" style="display:none;"></iframe>
// Example: Set the data
function LSsetItem(key, value) {
    var obj = {
        setItem: key,
        value: value
    };
    frames['myPostMessage'].postMessage(JSON.stringify(obj), 'https://my.domain.com');
}
LSsetItem('key', 'value');

Note that the method is asynchronous, because of postMessage. An implementation of the getItem method has to be implemented differently:

var callbacks = {};
window.addEventListener('message', function(event) {
    if (event.source === frames['myPostMessage']) {
        var data = /^#localStorage#(\d+)(null)?#([\S\s]*)/.exec(event.data);
        if (data) {
            if (callbacks[data[1]]) {
                // null and "null" are distinguished by our pattern
                callbacks[data[1]](data[2] === 'null' ? null : data[3]);
            }
            delete callbacks[data[1]];
        }
    }
}, false);
function LSgetItem(key, callback) {
    var identifier = new Date().getTime();
    var obj = {
        identifier: identifier,
        getItem: key
    };
    callbacks[identifier] = callback;
    frames['myPostMessage'].postMessage(JSON.stringify(obj), 'https://my.domain.com');
}
// Usage:
LSgetItem('key', function(value) {
    console.log('Value: ' + value);
});

Note that each callback is stored in a hash. Each message also contains an identifier, so that the window which receives the message calls the correct corresponding callback.

For completeness, here's the LSremoveItem method:

function LSremoveItem(key) {
    var obj = {
        removeItem: key
    };
    frames['myPostMessage'].postMessage(JSON.stringify(obj), 'https://my.domain.com');
}
like image 65
Rob W Avatar answered Nov 11 '22 21:11

Rob W