Message isn't passed between background.html and popup.html - google-chrome-extension

I'm trying to pass data that is saved in sessionStorage from background.html to popup.html
background.html:
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
data = sessionStorage.getItem(request.tabId);
alert(data);
sendResponse({ data: data });
});
and in popup.html:
chrome.tabs.getSelected(null, function(tab) {
chrome.extension.sendRequest({ tabId: tab.id }, function(response) {
alert(response.data);
});
});
The popup is opened by a pageAction button, when I click the button I get an alert box with "null" on the popup and then an alert box with the data that I stored in sessionStorage on the background!
Any ideas how to fix this?

You don't need to use message/request APIs. I think this response may help you.
You also don't need sessionStorage, just store your data in a global variable of the background page. It will persist until the browser is closed or until the extension is restarted.
So, here is how I would rewrite your code:
background.html:
var data = {}; // Object storing data indexed by tab id
and in popup.html:
chrome.tabs.getSelected(null, function(tab) {
alert(chrome.extension.getBackgroundPage().data[tab.id]);
});
Note that chrome.tabs.getSelected is deprecated since Chrome 16, so popup code should be:
chrome.windows.getCurrent(function(win) {
chrome.tabs.query({'windowId': win.id, 'active': true}, function(tabArray) {
alert(chrome.extension.getBackgroundPage().data[tabArray[0].id]);
});
});

Well, I've done something dumb.
I inspected the background page by opening chrome-extension://[extension-id]/background.html in a tab instead of clicking on "inspect active views: background.html" in the extensions management page. This caused the tab to catch the request and call sendResponse, but the popup expected the REAL background page to call sendResponse (and if I understand Google's documentation regarding message passing, the fact that sendResponse was called twice is root of the problem, because the first call clears the request object)

Related

Chrome extension detect Google search refresh

How can my content script detect a refresh of Google's search?
I believe it is an AJAX reload of the page and not a "real" refresh, so my events won't detect the refresh.
Is it possible to detect it somehow in both a Google Chrome extension and a Firefox WebExtensions add-on?
Google search is a dynamically updated page. Several well-known methods exist to detect an update: MutationObserver, timer-based approach (see waitForKeyElements wrapper), and an event used by the site like pjax:end on GitHub.
Luckily, Google Search in Chrome browser uses message event, so here's our content script:
window.addEventListener('message', function onMessage(e) {
// console.log(e);
if (typeof e.data === 'object' && e.data.type === 'sr') {
onSearchUpdated();
}
});
function onSearchUpdated() {
document.getElementById('resultStats').style.backgroundColor = 'yellow';
}
This method relies on an undocumented site feature which doesn't work in Firefox, for example.
A more reliable crossbrowser method available to Chrome extensions and WebExtensions is to monitor page url changes because Google Search results page always updates its URL hash part. We'll need a background/event page, chrome.tabs.onUpdated listener, and messaging:
background.js
var rxGoogleSearch = /^https?:\/\/(www\.)?google\.(com|\w\w(\.\w\w)?)\/.*?[?#&]q=/;
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if (rxGoogleSearch.test(changeInfo.url)) {
chrome.tabs.sendMessage(tabId, 'url-update');
}
});
content.js
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
if (msg === 'url-update') {
onSearchUpdated();
}
});
function onSearchUpdated() {
document.getElementById('resultStats').style.backgroundColor = 'yellow';
}
manifest.json: background/event page and content script declarations, "tabs" permission.

chrome.tabs.onupdated.addlistener doesn't work on cached pages

chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
if (tab.url.indexOf('http') == '0' && changeInfo.status == 'complete') {
alert(JSON.stringify(changeInfo));
}
});
i have such background.js it nearly works always %99 but not %100
whenever i restart my browser and enter an already accessed web page when browser loads all contents from cache it doesn't fire this chrome.tabs event
but the interesting part is when i click same website from dial speed it doesn't load from cache and calls the "tabs." action
According to the docs https://developer.chrome.com/extensions/webNavigation
Not all navigating tabs correspond to actual tabs in Chrome's UI,
e.g., a tab that is being pre-rendered. Such tabs are not accessible
via the tabs API nor can you request information about them via
webNavigation.getFrame or webNavigation.getAllFrames. Once such a tab
is swapped in, an onTabReplaced event is fired and they become
accessible via these APIs.
and
If a navigation was triggered via Chrome Instant or Instant Pages, a
completely loaded page is swapped into the current tab. In that case,
an onTabReplaced event is fired.
Try using the chrome.webNavigation.onTabReplaced event to catch this:
chrome.webNavigation.onTabReplaced.addListener(function (details) {
chrome.tabs.get(details.tabId, function(tab) {
console.log(tab.url);
});
});
Note: You'll need the "webNavigation" permission in the chrome extension manifest:
{
"name": "My extension",
...
"permissions": [
"webNavigation"
],
...
}
I tested this with 25 open tabs using:
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab){
console.log("update: "+JSON.stringify(changeInfo));
});
On restarting my browser I received 25 "loading" events, but only 23 "completes".
On restarting my browser a second time I received 25 "loading" events, but only 22 "completes".
I don't think changeInfo.status == 'complete' will always follow a pageload, especially after restarting.
consider using chrome.tabs.query() right after your extension loads instead.
chrome.tabs.query({}, function(tabs) {
for(var i=0;i<tabs.length;i++){
if (tabs[i].url.indexOf('http') == '0') {
dostuff();
}
}
});

chrome extension message passing between extension-inner page and background script

I use a extension inner page(chrome-extension://) to request permission, and send result to background.
in extension inner page:
btn.addEventListener('click', function(event) {
event.preventDefault();
chrome.permissions.request({
permissions: permissions,
origins: origin
}, function(granted) {
chrome.runtime.sendMessage({route: '/permissionRequest', data: {command: 'Response', result: granted}}, function(){});
});
}, false);
It looks well.But when I click the button, it open a new tab, url like chrome-extension://.../authorizehtml?undefined. The message send.
I don't know why it open a new tab like that.
And the, I found, if I change the key name 'data' to other name, it never open new tab.The official document don't talk about it.
I hava no idea about it.

Content script programatically injected by Chrome extension background page got injected multiple times

I have a chrome extension, say it injects content script a.js into urls matching 'http://example.com/*'. Then when I click some page elements in example.com page, a.js would ask background to create a new tab with defined url, and programatically inject a content script b.js into the new tab, you can check the code down:
chrome.tabs.create({
url: 'http://example.com'
}, function (tab) {
var taskTab = tab.id;
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
if (tabId === taskTab) {
chrome.tabs.executeScript(taskTab, {
file: 'scripts/b.js'
});
}
});
});
The problem is, the content script b.js would get injected into the new tab for a lot times. I also tried the chrome.webNavigation, the result is the same.
So if I want the b.js got injected into the new tab for only one time? how can i make it?
By the way, the b.js is actually a remote file, I load it into page by jQuery, here to simplify the question I make it a local file.
Thanks very much.
update
I had figure out what's going on with my code.
First, it's wrong for me to wrap the onUpdated event listener inside the create callback, every time I create a new tab, close it, create it again, the event listener would got bind one more time. The more, the more.
Second, #Xan was right, I could do it by checking changeInfo.status. So the code would be:
var taskTab;
chrome.tabs.create({
url: 'http://example.com'
}, function (tab) {
taskTab = tab.id;
});
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
if (changeInfo.status === 'complete'){
if (tabId === taskTab) {
chrome.tabs.executeScript(taskTab, {
file: 'scripts/b.js'
});
}
}
});
And that b.js would only be injected for once, no matter it's a local file or a remote file loaded by jQuery.
onUpdated event can fire for a lot of reasons:
object changeInfo:
Lists the changes to the state of the tab that was updated.
string (optional) status: The status of the tab. Can be either loading or complete.
string (optional) url: The tab's URL if it has changed.
boolean (optional) pinned: The tab's new pinned state.
string (optional) favIconUrl: The tab's new favicon URL.
You might want to filter onUpdated events by those properties of changeInfo
Failsafe method: when you inject a second script, set some guard variable that is checked for undefined before you execute the rest of the script. Any other attempts to inject the script will be in the same context and will see the variable set. Kind of like C's #ifndef includes.

message passing happens twice if "run_at" : "document_start" is used in chrome extension

In my extension, I am passing a message from background.js to contentScript.js.
The issue is that when I use
"run_at" : "document_start"
in my manifest.json, the message from background.js to contentScript.js is called twice and sometimes even more than that.
Message passing more than once is a bit expensive in my case as I'm sending a call to server and the processing the same values at the server multiple times is expensive.
background.js
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
chrome.tabs.sendMessage(tab.id, {type: "get-url", url: tab.url});
});
contentScript.js
chrome.runtime.onMessage.addListener(
function(message, sender, sendResponse) {
switch(message.type) {
case "get-url" : someFunction(message.url);
break;
// other cases
}
});
Is there a way to solve this?
chrome.tabs.onUpdated is called multiple times in the tab-update cycle (e.g. when the tab starts loading the new page, when the tab completes loading the new page, when the favicon is fetched etc).
The best option is to checj for when the tab has completed loading:
chrome.tabs.onUpdated.addListener(function (tabId, info, tab) {
if (info.status === 'complete') {
/* Now it is safe to send the message */
chrome.tabs.sendMessage(tabId, {
type: 'get-url',
url: tab.url
});
}
});
BTW, when injecting programmatically (not in manifest.json), the property's name is runAt, not run_at (docs).

Resources