We have a javascript api.js which is hosted on domain api.abc.com. It manages the local storage.
We included this javascript in our websites at abc.com and login.abc.com as a cross domain js like
<script src="http://api.abc.com/api.js">
I understand that localstoarge is per domain basis. However since api.js is loaded from api.abc.com, I expected that it will have access to local storage of api.abc.com from both the domains. Unfortunately, it doesn't seem to be the case. When api.js stores a value in localstoarge from one domain, it's not accessible to it when loaded from other domain.
Any idea?
How about using cross domain postmessage and iframes?
So on your wrong-domain-page you include an iframe that posts messages with the cookie data back.
Here is a solid example of cross domain postmessages:
http://blog.teamtreehouse.com/cross-domain-messaging-with-postmessage
live example:
http://codepen.io/anon/pen/EVBGyz //forked sender code with a tiiiiiny change :) :
window.onload = function() {
// Get the window displayed in the iframe.
var receiver = document.getElementById('receiver').contentWindow;
// Get a reference to the 'Send Message' button.
var btn = document.getElementById('send');
// A function to handle sending messages.
function sendMessage(e) {
// Prevent any default browser behaviour.
e.preventDefault();
// Send a message with the text 'Hello Treehouse!' to the new window.
receiver.postMessage('cookie data!', 'http://wrong-domain.com');
}
// Add an event listener that will execute the sendMessage() function
// when the send button is clicked.
btn.addEventListener('click', sendMessage);
}
Receiver code:
window.onload=function(){
var messageEle=document.getElementById('message');
function receiveMessage(e){
if(e.origin!=="http://correct-domain.com")
return;
messageEle.innerHTML="Message Received: "+e.data;
}
window.addEventListener('message',receiveMessage);
}
As noticed in your post the localStorage (sessionStorage too) won't be stored on the storage related to the domain api.abc.com. If this was the case, by using CDN version of a library using localStorage you would have to share storage with all the other websites using this library.
One good solution could be to use an iframe with postMessage as explained in the following stack overflow:
use localStorage across subdomains
You might try this cross-storage from Zendesk. Basically,
There are hubs and clients:
hubs: reside on any server, interact directly with LocalStorage API
clients: load the hub using an embedded iframe, and post messages, interact with data
Key things is you can configure the permission (get, set, delete) that each host or domain client could have.
The library is divided into two types of components: hubs and clients.
Care should be made to limit the origins of the bidirectional
communication. As such, when initializing the hub, an array of
permissions objects is passed. Any messages from clients whose origin
does not match the pattern are ignored, as well as those not within
the allowed set of methods. The set of permissions are enforced thanks
to the same-origin policy. However, keep in mind that any user has
full control of their local storage data - it's still client data.
This only restricts access on a per-domain or web app level.
The other answers all ignore the fact that you're not really operating cross-domain, just between subdomains.
You still need a hidden iframe to encapsulate the origin of the localStorage store you want to access (api.abc.com), but by setting document.domain = "abc.com" on both main window and hidden iframe, they can interact directly. (Though note that this is deprecated, may have security implications, and in Chrome at least requires also sending a Origin-Agent-Cluster: ?0 header).
Then you can literally just use hiddenIFrame.contentWindow.localStorage instead of window.localStorage, and forget about the headache of doing anything via postMessage().
I posted a more detailed version of this answer here: https://stackoverflow.com/a/63602446/999120
Use iframe to store data in local storage & postMessage API to communicate between parent domain & iframe
Create Iframe with message event listener to store data in local storage of iframe domain
window.addEventListener("message", handleMessage, false);
function handleMessage(e) {
let {key, value, method} = e.data;
if (method == 'store') {
window.localStorage.setItem(key, value); // Store data in iframe domain local storage
} else if (method == 'retrieve') {
let response = window.localStorage.getItem(key);
e.source.postMessage({
key,
response,
method: 'response'
}, '*'); // Retrieve local storage data
}
}
Pass Message from parent domain to iframe to store data
document.getElementById('myFrameId').contentWindow.postMessage({
key: 'key',
value: data,
method: 'store'
});
Retrieve data from Iframe
document.getElementById('myFrameId').contentWindow.postMessage({
method: 'response',
key: 'key'
});
window.addEventListener("message", handleResponse, false);
function handleResponse(e) {
let {key,value,method} = e.data
if (method == 'response') {
console.log('Response Key', key);
console.log('Response value', value)
}
}
Related
I am trying to get the value from window.location.pathname (or a similar location read only API) inside the context of a ServiceWorker. I think one way to do that is sending that information from the page to the Service Worker via postMessage:
navigator.serviceWorker.ready.then( registration => {
registration.active.postMessage({
type: "pathname",
value: window.location.pathname
});
});
as seen in https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/message_event
However, I need that data in the install step of the SW lifecycle so waiting on the SW to become the active one is not ideal, and I'd rather try first to get that data earlier so I can go thru the install step with that information.
Within the Service Worker, self.location is accessible via WorkerGlobalScope.location. You could listen to requests and process those that match the same origin of your domain.
self.addEventListener('fetch', event => {
const requestUrl = new URL(event.request.url)
if (self.location.origin === requestUrl.origin) {
const requestPathname = requestUrl.pathname
}
})
I am developing a firefox addon. i use localStorage to save some data and retrieve.
function to check if it is available or not
if(!localStorage.getItem('font')) {
populateStorage();
}else{
var aValue = localStorage.getItem('font');
alert(aValue);
if not then create
function populateStorage(){
localStorage.setItem('cname', name);
localStorage.setItem('font', 'Helvetica');
localStorage.setItem('image', 'myCat.png');
}
This is perfectly working localhost but if i visit other host like google.com and try to get i am getting error not found
if(!localStorage.getItem('font')) {
alert('Not found !!!!');
}else{
var aValue = localStorage.getItem('font');
alert(aValue);
}
is there any way to fix this issue ? or am i doing it in wrong way ?
LocalStorage is intended to be accessible only from the same host. This allows different websites to have a different scope for their data, and also ensures that one website cannot access data from another website.
From MDN,
The read-only localStorage property allows you to access a Storage object for the Document's origin; the stored data is saved across browser sessions.
From: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
If you need to share data across different domains, you should use server-side persistence.
From what I've undestood local storage is not cross domain solution, so this behavior is correct.
What you need to do is fallow MDN solution. I've found something like this:
// define 2 objects
var monster = {
name: "Kraken",
tentacles: true,
eyeCount: 10
}
var kitten = {
name: "Moggy",
tentacles: false,
eyeCount: 2
}
// store the objects
browser.storage.local.set({kitten, monster})
.then(setItem, onError);
(code copied from MDN > JavaScript APIs > storage )
In this solution data will be pinned to browser/extension, not to domain. But be aware, that data still will be destroyed when user clear browser cache or something like that.
How can I get the full URL of the page which is being serviced by a service worker's 'fetch' event?
The "self.location" property seems to only refer to the root URL of the site. For example, if page https://example.com/folder/pagename.html is performing a fetch which the service worker is intercepting, the service worker's 'self.location' property returns "https://example.com".
event.currentTarget.location and event.explicitOriginalTarget.location, event.originalTarget, and event.target all return the URL of the service worker .js file.
How can I get the full URL of the page that triggered the fetch event?
You've got two general approaches, depending on how involved you want to get:
Use the 'Referer' header info
If the request is for a subresource and includes a Referer header, then there's a decent chance that the value of that header is the URL of the page that made the request. (There are some caveats; read this background info to delve into that.)
From within a fetch handler, you can read the value of that header with the following:
self.addEventListener('fetch', event => {
const clientUrl = event.request.referrer;
if (clientUrl) {
// Do something...
}
});
Use the clientId value
Another approach is to use the clientId value that (might) be exposed on the FetchEvent, and then use clients.get(id) or loop through the output of clients.matchAll() to find the matching WindowClient. You could then read the url property of that WindowClient.
One caveat with this approach is that the methods which look up the WindowClient are all asynchronous, and return promises, so if you're somehow using the URL of the client window to determine whether or not you want to call event.respondWith(), you're out of luck (that decision needs to be made synchronously, when the FetchEvent handler is first invoked).
There's a combination of different things that need to be supported in order for this approach to work, and I'm not sure which browsers currently support everything I mentioned. I know Chrome 67 does, for instance (because I just tested it there), but you should check in other browsers if this functionality is important to you.
self.addEventListener('fetch', async event => {
const clientId = event.clientId;
if (clientId) {
if ('get' in clients) {
const client = await clients.get(clientId);
const clientUrl = client.url;
// Do something...
} else {
const allClients = await clients.matchAll({type: 'window'});
const filtered = allClients.filter(client => client.id === clientId);
if (filtered.length > 0) {
const clientUrl = filtered[0].url;
// Do something...
}
}
}
});
I have Javascript on a page that sets localStorage data:
localStorage.setItem('blah', 1);
Then, I have code that will redirect to another page on a button click.
On the other page I try to access the item in localStorage, but I get Uncaught TypeError: Cannot read property 'blah' of null 30% of the time. How come localStorage isn't saving each time?
Am I missing something?
On Current Page
localStorage.setItem('blah', 1);
$('.btn').on('click', function() { window.location.href = 'http://example.com/signup?redirect=/chkout'; });
On Redirect Page
localStorage.getItem('blah', 1); ==> null
This might happen because
The tab/window is in incognito / private surfing mode
The browser is configured to track no history at all
The item was set on another port or scheme (of the same domain) (e.g. http://foo.com instead of https://foo.com)
From the W3C specification:
The user agent may throw a SecurityError exception and abort these steps instead of returning a Storage object if the request violates a policy decision (e.g. if the user agent is configured to not allow the page to persist data).
If the Document's origin is not a scheme/host/port tuple, then throw a SecurityError exception and abort these steps.
The localStorage data should persist, by several documentations. There may be a case when your localStorage data gets overwritten by some other operation. You can try to debug this by adding following eventListener:
window.addEventListener("storage", function(data) {
console.debug(data);
}, false);
This should log the data, every time the storage is accessed.
My application receives data from the another server, using API with limited number of requests. Data changing rarely, but may be necessary even after refresh page.
What's the best solution this problem, using cookie or HTML5
WebStorage?
And may be have other way to solve this task?
As much as cross browser compatibility matters, cookie is the only choice rather than web storage.
But the question really depends on what kind of data you are caching?
For what you are trying, cookie and web-storage might not be needed at all.
Cookies are used to store configuration related information, rather than actual data itself.
Web storage supports persistent data storage, similar to cookies but with a greatly enhanced capacity and no information stored in the HTTP request header. [1]
I would rather say, it would be stupid to cache the entire page as cookie or web-storage both. For these purposes, server-side caching options might be the better way.
Update:
Quoting:
data about user activity in some social networks (fb, vk, google+)
Detect the web-storage features, using libraries like mordernizr and if does not exists fall back to cookie method. A simple example
if (Modernizr.localstorage) {
// browser supports local storage
// Use this method
} else {
// browser doesn't support local storage
// Use Cookie Method
}
[1]: http://en.wikipedia.org/wiki/Web_storage
I wrote this lib to solve the same problem:
Cache your data with Javascript using cacheJS
Here are some basic usages
// just add new cache using array as key
cacheJS.set({blogId:1,type:'view'},'<h1>Blog 1</h1>');
cacheJS.set({blogId:1,type:'json'}, jsonData);
// remove cache using key
cacheJS.removeByKey({blogId:1,type:'json'});
// add cache with ttl and contextual key
cacheJS.set({blogId:2,type:'view'},'<h1>Blog 2</h1>', 3600, {author:'hoangnd'});
cacheJS.set({blogId:3,type:'view'},'<h1>Blog 3</h1>', 3600, {author:'hoangnd'});
// remove cache with con textual key
// cache for blog 2 and 3 will be removed
cacheJS.removeByContext({author:'hoangnd'})
Here is an example of caching data from JQuery AJAX. So if you only want to make the call when you don't have the data yet, its really simple. just do this (example). Here we first check if we have the load information (keyed on line, location and shipdate), and only if we dont, we make the AJAX call and put that data into our cache:
var dict = [];
function checkCachedLoadLine(line, location, shipDate, callback) {
var ret = 0;
if(!((line+location+shipDate) in dict)) {
productionLineService.getProductionLoadLine(line, location, shipDate, callback);
}
return dict[line+location+shipDate];
}
...then in the call back write the value to the cache
function callback(data) {
if (!data) {
document.getElementById('htmlid').innerHTML = 'N/A';
} else {
document.getElementById('htmlid').innerHTML = data[0];
dict[data[2]+data[3]+data[4]] = data[0];
}
}