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

1

14

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?

Troudeloup answered 8/5, 2012 at 16:22 Comment(1)
R
23

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');
}
Recession answered 8/5, 2012 at 20:21 Comment(6)
I tried this but when the https iframe tries to postMessage back to http parent window i get an error "Unsafe JavaScript attempt to access frame with URL... Domains, protocols and ports must match." Any idea?Troudeloup
I'm using this: github.com/mozilla/jschannel and this sample github.com/mozilla/jschannel/tree/master/exampleTroudeloup
Yea, did some more tests, and it seems that it does work. My issue was caused by another script that got injected. Thanks for the help.Troudeloup
This is a clever solution, but it's stopped working on iOS 7/Safari. Anyone else having issues with this now? The local storage seems to be using the parent window's protocol instead of the iframe. Anyone have a new answer to contribute?Oaxaca
Yes having same problem with Safari... #20402251Kaolack
@Kaolack The issue with Safari is caused by the "block cookies and other website data" preference. When it it set to "From third parties and advertisers", localStorage is not shared any more. It must be set to "Never", as seen in the following picture: i.sstatic.net/AP4ed.png.Recession

© 2022 - 2024 — McMap. All rights reserved.