My extension has a content script that captures some info from certain pages and saves them into local storage using the following:
localStorage[myVarName] = value;
If I put the following into my content script, I have no problem retrieving the stored values:
myGottenVariable = localStorage[myVarName];
Elsewhere, my extension creates a new window using
chrome.windows.create({url:LocalURL}}
The newly created window is the main UI for my extension. One of the things I want it to do is retrieve the stored value at "myVarName." However, it is undefined.
I assume that this has to do with how the localstorage is organized. I have an intuition that each extension has its own storage space for local storage and that the created window lacks access to the content script's storage space.
This intuition is confirmed by the fact that I have tried storing content locally from the newly created window, and it has no problem retrieving that content.
My question is, what is the simplest method of achieving my desired result? I get tripped up pretty quickly when dealing with a lot of the chrome extension API stuff: so the ideal option would be as clean and simple as possible. That's why I liked the syntax of:
localStorage[myVarName] = value;
Is there some simple way of modifying this to allow easy access to the desired content? Are both pages actually in the same storage space, but at different places in that directory?
localStorage is isolated per domain, which means if the new window has different domain from the domain where you saved data, you won't get what you saved.
To achieve what you want, you could use chrome.storage. The chrome.storage is shared by background page/content scripts. For your case, you could save data in content scripts and retrieve it in the created window (via content scripts).
chrome.storage.local.set({ key: value });
chrome.storage.local.get(key, function (result) {
console.log(result.key);
});
Related
Background
In our company, we install our offline documentation topics (thousands of .htm files in a folder) on our users' computers. Users view our documentation offline through their browser using the file:/// protocol.
I have a banner that appears in the footer of each .htm file that asks users to decide whether we can track user analytics (via Application Insights). Once they make their choice, we don't show the banner.
My Goal and Overall Problem
My goal is to store their choice in the browser's local storage. I can do that just fine, but the problem is this:
These are offline .htm files. There is no website domain. So, the key and value for any local storage is stored only for the .htm file they are on at the time they make their choice. If they come back to a topic they made their choice on, then yes, my script can retrieve their choice. But if they navigate to another topic in our documentation system (another .htm file), the local storage key and value don't persist to those other topics, and my script doesn't know what they chose--so then the banner pops up again.
My Workaround Idea
I doubt this is the best approach, but not having a lot of experience and not knowing what else to try, necessity becomes the mother of invention.
Here's what I'm trying:
Have my local storage requests go through a single .htm file called storage.htm, thereby getting around the above problem by always having a single point of contact (storage.htm) with the local storage.
storage.htm loads via a blank iframe.
The iframe is tacked onto each .htm topic.
When a topic .htm loads, the iframe also loads and any functions inside it become (hopefully) available for use by my main script.
When users click on the banner, I send the choice as query parameters through my main script to the iframe's src.
storage.htm contains a parseQuery() function, and inside that function, it parses any query params and then does the actual localStorage.getValue(key) and localStorage.setValue(key,value) requests.
I then want to somehow force the iframe to refresh with those parameters and then call the parseQuery() function there from my main script.
My Code
From my main script:
Attempt 1:
I've tried the top-voted item from this thread,
How to check if iframe is loaded or it has a content?
but I get stuck inside the checkIfFrameLoaded() function, and it continues to loop through the check to see if the iframe is loaded. It never loads. I think it's because the contentWindow and/or contentDocument don't work with my offline files, so I won't bore you with that code.
Attempt 2:
This is what I'd like to do as it seems cleaner:
function doIframeStorage(type,key,value){
// We get a handle on the iframe's id of 'storage'.
let storage = document.querySelector('#storage');
const src = storage.getAttribute('src');
let query = src;
if (type==='get'){
query = src + `?type=${type}&key=${key}`;
} else if (type==='set'){
query = src + `?type=${type}&key=${key}&value=${value}`;
}
storage.src = query;
storage.addEventListener('load', (e) => parseQuery());
}
But I'm running into a problem where my parseQuery() function (from storage.htm) is always undefined:
Uncaught ReferenceError: parseQuery is not defined
Is it possible to load and access my parseQuery() function from my main script like I'm doing? If so, how? I thought the addEventListener would ensure it was loaded and therefore the parseQuery() function would then be available.
Or Is there a better way to do what I'm attempting?
I have created a JS file that I place in some webpages other than mine.
So mine is domain-1.com and I place this to domain-2.com and domain-3.com
This JS contains jsonp and I save some data from their pages to my database successfully. Also, I create some cookies and I save a value to the localstorage. the problem is that when a visitor goes to domain-2.com and tomorrow to www.domain-2.com they will have a different value because os the www.
I want this value to be the same across www. or not, maybe at the same time, I do not know an applicable idea. It is better for me to pass the value the same time for www. and without www.
How to do this?
I only provide them with a JS external link. It is ok If I place an iframe also.
The best solution would be to set a redirect to either of the domains so you can avoid this problem altogether.
The following code shows the concept of sending values to the non-www domain for storage only. If you need to read those values from the www domain too or want a library to do everything for you, you should use one of the libraries listed at the end. Those libraries use the same concept but will handle most things for you.
You can store the value on one domain only and use cross-origin communication to send the value if you are on the www domain. Create an iframe that loads a script of the non-www domain. In this script you save the value in the local storage of that domain.
Here is the content of the iframe with some minimal html5 markup, in this example saved as storage.html and served from example.com.
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title> </title>
<script>
window.addEventListener("message", storeItem, false);
function storeItem(event) {
if (!(event.origin == "http://example.com" || event.origin == "http://www.example.com")) {
return;
}
localStorage.setItem(event.data.key, event.data.value);
}
</script>
</head></html>
When you want to store data use postMessage to communicate with the iframe. The iframe needs to be loaded before you can send messages.
<iframe id="storageScriptFrame"></iframe>
<script>
var target = "http://example.com";
var storageScriptFrame = document.getElementById("storageScriptFrame");
var storageScriptWindow = storageScriptFrame.contentWindow;
function storeItem(key, value) {
var message = {key: key, value: value};
storageScriptWindow.postMessage(message, target);
}
// you can store values after the iframe has loaded
storageScriptFrame.onload = function() {
storeItem("foo", "bar");
};
// replace this with actual page
storageScriptFrame.src = 'http://example.com/storage.html';
</script>
Make sure to replace the example.com domain with the actual domain. Checking the origin domain is important so other sites can't send you messages.
At some point you will also want to read those stored values. Depending on what you do with the stored values, you have two options.
If you don't need to interact with the main window, you can move the script that reads values into the iframe.
If you do need to get the value on the main window, use postMessage again to send values back.
The second option can get complicated though, because postMessage is asynchronous and only works one way. I would recommend to use an existing library to do this (you don't need the code above then).
Cross Domain Local Storage looks good and easy to use
localStorage-tools is another library for this task
For example if you Cross Domain Local Storage you simply need to follow the setup instructions and in the initCallback function you can call xdLocalStorage.getItem and xdLocalStorage.setItem to get and set items from the localstorage of example.com.
I have created a JS file that I place in some webpages other than mine.
So mine is domain-1.com and I place this to domain-2.com and domain-3.com
This JS contains jsonp and I save some data from their pages to my database successfully. Also, I create some cookies and I save a value to the localstorage. the problem is that when a visitor goes to domain-2.com and tomorrow to www.domain-2.com they will have a different value because os the www.
I want this value to be the same across www. or not, maybe at the same time, I do not know an applicable idea. It is better for me to pass the value the same time for www. and without www.
How to do this?
I only provide them with a JS external link. It is ok If I place an iframe also.
The best solution would be to set a redirect to either of the domains so you can avoid this problem altogether.
The following code shows the concept of sending values to the non-www domain for storage only. If you need to read those values from the www domain too or want a library to do everything for you, you should use one of the libraries listed at the end. Those libraries use the same concept but will handle most things for you.
You can store the value on one domain only and use cross-origin communication to send the value if you are on the www domain. Create an iframe that loads a script of the non-www domain. In this script you save the value in the local storage of that domain.
Here is the content of the iframe with some minimal html5 markup, in this example saved as storage.html and served from example.com.
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title> </title>
<script>
window.addEventListener("message", storeItem, false);
function storeItem(event) {
if (!(event.origin == "http://example.com" || event.origin == "http://www.example.com")) {
return;
}
localStorage.setItem(event.data.key, event.data.value);
}
</script>
</head></html>
When you want to store data use postMessage to communicate with the iframe. The iframe needs to be loaded before you can send messages.
<iframe id="storageScriptFrame"></iframe>
<script>
var target = "http://example.com";
var storageScriptFrame = document.getElementById("storageScriptFrame");
var storageScriptWindow = storageScriptFrame.contentWindow;
function storeItem(key, value) {
var message = {key: key, value: value};
storageScriptWindow.postMessage(message, target);
}
// you can store values after the iframe has loaded
storageScriptFrame.onload = function() {
storeItem("foo", "bar");
};
// replace this with actual page
storageScriptFrame.src = 'http://example.com/storage.html';
</script>
Make sure to replace the example.com domain with the actual domain. Checking the origin domain is important so other sites can't send you messages.
At some point you will also want to read those stored values. Depending on what you do with the stored values, you have two options.
If you don't need to interact with the main window, you can move the script that reads values into the iframe.
If you do need to get the value on the main window, use postMessage again to send values back.
The second option can get complicated though, because postMessage is asynchronous and only works one way. I would recommend to use an existing library to do this (you don't need the code above then).
Cross Domain Local Storage looks good and easy to use
localStorage-tools is another library for this task
For example if you Cross Domain Local Storage you simply need to follow the setup instructions and in the initCallback function you can call xdLocalStorage.getItem and xdLocalStorage.setItem to get and set items from the localstorage of example.com.
I am building a Chrome Extension. I was wondering if there is a way to remember js variables for a tab even if the page changes. For eg. If I am on example1.com and go to example2.com on the same tab, I should retain the variables which were set on example1.com. I don't want to use Chrome Storage.
I can't use localStorage or sessionStorage because chrome has different storages for different domains. What are the ways by which this can be accomplished ?
Apart from using localStorage( through background page), you can directly save those variables in the background page by messaging from content script to the background page. If those variables are in the extension page(other that content or injected scripts) you can easily save them by chrome.extension.getBackgroundPage().window.variableYouWant method and access those variables in the content script itself by messaging.
If those variables are in the content script use messaging to send those variables to background page like this:
chrome.runtime.sendMessage(object);
object could be any Object you might want to send.
on background page have a lisner like this:
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
myVariabeInBackground = message.yourObjectProperties;
});
when you require those variables you can use chrome.extension.getBackgroundPage().window.myVariabeInBackground in your extension page(other than content script) or you can send message from background page to content script by calling chrome.tabs.sendMessage(tabId, messageObject) in the background page itself and receive at the content script by having a listner like this:
chrome.runtime.onMessage.addListener(
function(request, sender) {}); //request is your sent object.
For localStorage also you can do the same thing to get that stored variable into your content script.
And No, you cant directly access the background page from content script.
PS: variables in background page will reset if extension is reloaded. If you want to use those variables even after reloading the extension use local storage. Extension does not reload on browser actions.
For more details read https://developer.chrome.com/extensions/messaging
use localstorage. the extension is completely isolated from the page in function and variable storage. you will need to use localstorage in a background page, so when you change tabs, the variable is never lost.
You should look into chrome.storage API as an alternative to localStorage+Messaging.
It is asynchronous compared to localStorage, but so is Messaging, and Messaging just for variable storage leads to a lot of boilerplate.
See documentation here, and a recent overview (by me) of pros/cons here.
In our current project, we're using HTML 5 localStorage with fall-back to global storage for Firefox and userdata behaviors for IE6/IE7.
The fall-back is provided through a JS script called jStorage.
This worked ok, until we started testing in IE6/IE7, even though it "works", it turns out that there's a restriction in userdata behaviour which locks it down so storage can only be set and read on the same URL or as MSDN puts it "For security reasons, a UserData store is available only in the same directory and with the same protocol used to persist the store".
Hence if I set a value on one page and then navigate to another, although I'm on the same site, it won't work.
Which for us pretty much renders it unusable as a fall-back for local storage, which is scoped per domain.
Has anyone come across this problem before and found a decent solution?
Any ideas or thoughts will be appreciated.
Remy Sharp's polyfill will do that.
https://gist.github.com/remy/350433
if the problem is to get data across two page in different paths, but in the same domain, you could try one of these (note: i didn't try: i'm just trying to be creative)
Use url rewriting (with an .htaccess) so you can access /path1/page1 and /path2/page2 with a single path-rewritten/page1 and path-rewritten/page2
if you are in /path2/page2 you could load an invisible iframe loading a page in /path1 in which you get the data stored in some data structure that you pass in the parent document.
Since page1 and page2 are - per hypothesys - in the same domain you can make the page1 and iframe communicate each other via javascript.
btw good question.
A theoretical solution would be:
dynamically create a hidden "proxy" iframe accessing a static
document retrieved from a location of your convenience, say
http:/domain/proxy.html
proxy access to the DOM element in the iframe to persist/fetch data