I have a chrome extension that sets a string value in JavaScript localStorage in a content script. I have tested that it is being set by grabbing it and displaying it. While on the same page after it has loaded I can still access the stored string in the javascript console. If I go to any other tabs js console or when I try to access it in my other extensions background page(After the browser action has been clicked) it is returning null.
extension 1 contentscript.js
embeded.addEventListener
(
"message",
function(message)
{
localStorage.setItem("pdf", message.data);
loadPDF(message.data);
},
false
);
extension 2 background.js
chrome.browserAction.onClicked.addListener(function(tab) {
if (tab.url.substr(tab.url.length - 4) === ".pdf")
{
if (localStorage.getItem("pdf") !== null)
{
alert("boomy");
}
}
});
localStorage is shared among extension pages (background, options, popup), but not content scripts. And most certainly not shared between 2 extensions!
You have two-and-a-half options. With 2 separate extensions, only one option.
Option one: rely on messaging.
When you need to pass some value between background and content script, you can do so with messaging. Works especially well if you only need to pass it in one direction.
contentscript.js:
embeded.addEventListener(
"message",
function(message)
{
chrome.runtime.sendMessage(extensionTwoId, {pdf: message.data});
loadPDF(message.data);
},
false
);
background.js:
chrome.runtime.onMessageExternal.addListener(
function(message, sender, sendResponse) {
if(sender.id !== extenstionOneId) return;
if(message.pdf) localStorage.setItem("pdf", message.pdf);
}
);
/* ... */
Option two: chrome.storage. Not applicable between 2 extensions.
Related
Example: I have created safari extension, which, after the page loads, replaces all words "fish" with the fish emoji. I want to count how many words I replaced and store that number (total number of replaces since installing the extension). When I click on the extension icon in toolbar, I want to see that total number.
content.js handles the replacing
popup.js handles the toolbar icon menu
I only need the storing capability implemented, so I need a function in content.js called SomehowStoreThisValue() and in popup.js function SomehowRetrieveThisValue()
What I've tried: (Using the local storage)
content.js:
browser.runtime.sendMessage({
data: "Hello popup, how are you"
}, function (response) {
console.dir(response);
});
popup.js:
browser.runtime.onMessage.addListener(function (message, sender, sendResponse) {
sendResponse({
data: "I am fine, thank you. How is life in the background?"
});
let my_value = parseInt(localStorage.getItem('stored_value'));
document.getElementById("MY_ID").innerHTML = my_value;
localStorage.setItem('stored_value', my_value + 1);
});
Which does work, but only when the popup is open, when the replacement is happening, which is not ideal.
I have an options page where the user can define certain options and it saves it in localStorage: options.html
Now, I also have a content script that needs to get the options that were defined in the options.html page, but when I try to access localStorage from the content script, it doesn't return the value from the options page.
How do I make my content script get values from localStorage, from the options page or even the background page?
Update 2016:
Google Chrome released the storage API: https://developer.chrome.com/docs/extensions/reference/storage/
It is pretty easy to use like the other Chrome APIs and you can use it from any page context within Chrome.
// Save it using the Chrome extension storage API.
chrome.storage.sync.set({'foo': 'hello', 'bar': 'hi'}, function() {
console.log('Settings saved');
});
// Read it using the storage API
chrome.storage.sync.get(['foo', 'bar'], function(items) {
message('Settings retrieved', items);
});
To use it, make sure you define it in the manifest:
"permissions": [
"storage"
],
There are methods to "remove", "clear", "getBytesInUse", and an event listener to listen for changed storage "onChanged"
Using native localStorage (old reply from 2011)
Content scripts run in the context of webpages, not extension pages. Therefore, if you're accessing localStorage from your contentscript, it will be the storage from that webpage, not the extension page storage.
Now, to let your content script to read your extension storage (where you set them from your options page), you need to use extension message passing.
The first thing you do is tell your content script to send a request to your extension to fetch some data, and that data can be your extension localStorage:
contentscript.js
chrome.runtime.sendMessage({method: "getStatus"}, function(response) {
console.log(response.status);
});
background.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.method == "getStatus")
sendResponse({status: localStorage['status']});
else
sendResponse({}); // snub them.
});
You can do an API around that to get generic localStorage data to your content script, or perhaps, get the whole localStorage array.
I hope that helped solve your problem.
To be fancy and generic ...
contentscript.js
chrome.runtime.sendMessage({method: "getLocalStorage", key: "status"}, function(response) {
console.log(response.data);
});
background.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.method == "getLocalStorage")
sendResponse({data: localStorage[request.key]});
else
sendResponse({}); // snub them.
});
Sometimes it may be better to use chrome.storage API. It's better then localStorage because you can:
store information from your content script without the need for
message passing between content script and extension;
store your data as JavaScript objects without serializing them to JSON (localStorage only stores strings).
Here's a simple code demonstrating the use of chrome.storage. Content script gets the url of visited page and timestamp and stores it, popup.js gets it from storage area.
content_script.js
(function () {
var visited = window.location.href;
var time = +new Date();
chrome.storage.sync.set({'visitedPages':{pageUrl:visited,time:time}}, function () {
console.log("Just visited",visited)
});
})();
popup.js
(function () {
chrome.storage.onChanged.addListener(function (changes,areaName) {
console.log("New item in storage",changes.visitedPages.newValue);
})
})();
"Changes" here is an object that contains old and new value for a given key. "AreaName" argument refers to name of storage area, either 'local', 'sync' or 'managed'.
Remember to declare storage permission in manifest.json.
manifest.json
...
"permissions": [
"storage"
],
...
Another option would be to use the chromestorage API. This allows storage of user data with optional syncing across sessions.
One downside is that it is asynchronous.
https://developer.chrome.com/extensions/storage.html
[For manifest v3]
You can execute a script that returns localstorage items from the webpage. This script can be executed from popup or background service worker.
Add this line in manifest.json:
"permissions": ["scripting"]
let [tab] = await chrome.tabs.query({ active: true, currentWindow: true })
// Execute script in the current tab
const fromPageLocalStore = await chrome.scripting.executeScript({
target: { tabId: tabId },
func: () => {
return JSON.stringify(localStorage)
}
})
const localStorageItems = JSON.parse(fromPageLocalStore[0].result)
I want to be able to communicate from content-script (any tab) to background.
According to the documentation, I should be using chrome.extension chrome.runtime.onMessage with chrome.runtime.sendMessage.
So that's what I did:
manifest.json
"background": {
"scripts": ["background.js"],
"persistent": false
},
"permissions": [
"*://*/*"
],
background.js
console.info('1');
chrome.runtime.onMessage.addListener((request, sender, sendReponse) => {
console.info('2');
sendReponse({ msg: 'foo' });
return true;
});
I'm not very sure the return true; is needed. I tried with and without.
After building and reloading the extension I accessed chrome background page through chrome://extensions
> 1 // background.js file was run
> chrome.runtime.sendMessage({ hello: 'so' }, v => console.info(v))
> undefined
> undefined // This is the callback response I get. Why?
More important, I also get an empty response callback when running sendMessage from other tabs (i.e. stackoverflow.com)
> chrome.runtime.sendMessage('extension id', { hello: 'so' }, v => console.info(v))
> undefined
> undefined
Why do I get an empty response callback?
Am I missing any special permission? Wrong parameters? Maybe wrong API functions to do so?
runtime.sendMessage doesn't send the message to the sender's context since Chrome 49.
If sending to your extension, the runtime.onMessage event will be fired in every frame of your extension (except for the sender's frame),
webpages can send messages to your background/event page in two cases:
externally_connectable key in manifest.json allowed that page
a content script of your extension is loaded for that page, in which case you can switch the context to your extension in the console toolbar to be able to send messages manually from console:
The content script can be declared in manifest.json in which case it's autoinjected by [re]loading the page. Or you can inject it explicitly with tabs.executeScript from the background/event page.
I'm struggling to find the best way to communicate with my web app, which I'm opening with chrome.windows.create in my extension.
I've got the wiring between content script and background script right. I can right click an element and send it's value to the background script, and the background script creates a window containing my webapp. But from there I can't figure out how to access and use that value in my webapp (it needs to load the value into an editor).
I've tried setting fns and vars on the window and tab objects, but somehow they go missing from the window object once the web app is loaded.
With chrome.tabs.executeScript I can fiddle with the dom, but not set global variables or anything on 'window' either.
If there isn't a better way, I guess I'm forced to add to the DOM and pick that up once my web app is loaded, but it seems messy. I was hoping for a cleaner method, like setting an onLoadFromExtension fn which my web app can execute to get the value it needs.
I found a method that works after much trial and error, though it still seems error prone. And it also depends on the extension ID matching the installed one, so if that can't be hard-coded it'll be another message that needs passing through another channel (after reading up, looks like that can be hard-coded since it's a hash of the public key, so problem solved)... Starting to think manipulating the DOM is less messy...
background.js:
var selectedContent = null;
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
console.info("------------------------------- Got request", request);
if (request.getSelectedContent) {
sendResponse(selectedContent);
}
});
web app:
var extensionId = "naonkagfcedpnnhdhjahadkghagenjnc";
chrome.runtime.sendMessage(extensionId, {getSelectedContent: "true"},
response => {
console.info("----------------- Got response", response);
if(response) {
this.text = response;
}
});
manifest.json:
"externally_connectable": {
"ids": ["naonkagfcedpnnhdhjahadkghagenjnc"],
"matches": ["http://localhost:1338/*"]
},
Within the popup, do the following:
const parentWindow = window.opener
parentWindow.postMessage({ action: 'opened' })
window.onmessage = msg => {
alert(JSON.stringify(msg.data)) // Alerts you with {"your":"data"}
}
Within the script that will call chrome.windows.create, do the following:
window.onmessage = msg => {
if (msg.data.action == 'opened') {
msg.source.postMessage({ your: 'data' })
}
}
Set setSelfAsOpener: true when calling chrome.windows.create
How does this work?
Due to limitations of the Chrome extension windows API, the created window needs to post a message to its creator (aka window.opener) or else the creator won't have access to a WindowProxy (useful for posting messages to the created window).
I try to create a communication channel between specific webpage (per example : www.website.dev) and a chrome extension I created.
By using postMessage it's work from webpage to extension but I can't do that from extension to webpage.
I tried Google example but it uses background page
Thanks for your help
EDIT : sorry I don't understand the difference between content_script and background.js
In my manifest I have content script = test.js
What's about "background" ?
Following the documentation you can pass messages to your extension if you know the extension id:
https://developer.chrome.com/extensions/messaging#external-webpage
manifest.json
"externally_connectable": {
"matches": ["*://*.example.com/*"]
}
website:
// The ID of the extension we want to talk to.
var editorExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";
// Make a simple request:
chrome.runtime.sendMessage(editorExtensionId, {openUrlInEditor: url},
function(response) {
if (!response.success)
handleError(url);
});
extension:
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
if (sender.url == blocklistedWebsite)
return; // don't allow this web page access
if (request.openUrlInEditor)
openUrl(request.openUrlInEditor);
});
You will find all the details in the documentation https://developer.chrome.com/extensions/messaging
You would have one piece use sendMessage function while the other piece is listening for the event, which could be either a webpage or content script.
either the webpage's content script should initiate the communication (so you would get the tab id) or you can have the background query for tabs with specific url and then use sendMessage. Notice two separate functions chrome.extension.sendMessage and chrome.tabs.sendMessage used here.
the following code works for me:
content_script.js:
chrome.extension.sendMessage({"msg":"hello"});
background.js:
chrome.extension.onMessage.addListener(function (request, sender, sendResponse) {
if (request.msg == "hello"){
senderTab = sender.tab.id;
chrome.tabs.sendMessage(senderTab, {"msg": "ehlo"});
};
})