I'm getting the last visited url instead of the current one. (when i go to site.com and after that to site2.com, the url I get is 'site.com' and after I refresh site2.com I'm getting the right one.
Based on the answers here:
Google Chrome Extension get page information
Display current URL in a chrome extension
I've come up with this code:
manifest.json
{
"manifest_version": 2,
"browser_action": {
"default_popup": "action.html"
},
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["content.js"],
"run_at": "document_end"
}
],
"background": {
"scripts": ["background.js"]
},
"permissions": [
"tabs",
"unlimitedStorage",
]
}
content.js
chrome.extension.sendRequest({method: "getUrl"}, function(response) {
console.log(response.data);
});
background.js
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
if(request.method == "getUrl") {
chrome.tabs.query({ currentWindow: true, active: true }, function (tabs) {
currentUrl = tabs[0].url;
});
sendResponse({data: currentUrl});
}
else
sendResponse({}); // snub them.
});
I've also tried to put this code below directly in content.js and I'm getting an error and in background.js and the url is set to chrome://extensions/.
chrome.tabs.query({ currentWindow: true, active: true }, function (tabs) {
currentUrl = tabs[0].url;
});
what is the right way to do this ?
First of all:
chrome.extension.sendRequest/onRequest are deprecated. Please, use chrome.runtime.sendMessage/onMessage instead.
Also, whenever possible, prefer event pages over background pages. Most of the time it takes next to nothing to convert a background page to an event page, but can save consideably on resources.
The problem:
chrome.tabs.query is asynchronous. It means that, when called, it initializes the request and then completes its execution so the next part of your script gets executed. Once the request has completed, the registered callback function is executed. So, this is what happens in your case:
You visit http://www.site1.com.
chrome.tabs.query({...}, callback) gets executed (notice the callback has not been executed yet).
sendResponse({data: currentUrl}) gets executed (note that at this point currentUrl is not defined).
chrome.tabs.query's request completes and the callback function is executed, setting currentUrl = "http://www.site1.com" (but only after it is too late).
You visit http://www.site2.com.
chrome.tabs.query({...}, callback) gets executed (notice the callback has not been executed yet).
sendResponse({data: currentUrl}) gets executed (note that at this point currentUrl still equals http://www.site1.com from step 4).
chrome.tabs.query's request completes and the callback function is executed, setting currentUrl = "http://www.site2.com" (again only after it is too late).
The solutions:
(A)
Move the logic inside the callback.
(Note: Whenever the callback (i.e. sendResponse) is going be called asynchronously, it is necessary for the onMessage listener to return true.)
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if(request.method && (request.method == "getURL")) {
if (sendResponse) {
chrome.tabs.query({
currentWindow: true,
active: true
}, function(tabs) {
if (tabs.length > 0) {
sendResponse({ data: tabs[0].url });
}
});
/* Since 'sendResponse' is to going be called
* asynchronously, it is necessary to return 'true' */
return true;
}
} else {
// Do not talk to strangers !!!
}
});
(B)
Even simpler in your case, the sender parameter of the onMessage listener, contains the field tab which contains the URL info. E.g.:
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if(request.method && (request.method == "getURL") && sendResponse) {
sendResponse({ data: sender.tab.url });
}
});
(C)
If you only need the URL in the context of the content script, why don't you use location.href from within the content script ?
Related
In my chrome extension, my background script sends a message to the content_script which loads as part of a page.
The content_script doesn't seem to receive the message and I think it's because it's not loaded when the background script fires off the message. If I put a 5sec delay in the background script before sending the message, then it works. Is there another way to check if the content_script is ready before firing off the message?
manifest
"manfiest_version": 2,
"content_scripts": [
{
"matches" : [ "https://example.com/*" ],
"js": ["content_scripts.js"]
}
],
background.js
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
console.log('sending msg to tab '+tabs[0].id);
chrome.tabs.sendMessage(tabs[0].id, {message: "hello"}, function(response) {});
});
content_script.js
chrome.runtime.onMessage.addListener(
function(msg, sender, sendResponse) {
console.log('content_script received msg');
}
);
I'm writing chrome extension and I need to catch all requests from the beginning of downloading page.
I'm using chrome.webRequest.onBeforeRequest in background.js, and send them to content.js for logging.
But I don't see all requests. It looks like background.js start working with delay (and I missing important requests). How can I avoid it?
Here is my background.js:
chrome.webRequest.onBeforeRequest.addListener(logURL, { urls: ["<all_urls>"] });
function logURL(requestDetails) {
chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
chrome.tabs.sendMessage(
tabs[0].id,
{ message: requestDetails.url },
function(response) {}
);
});
}
and content.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.message == "reload") {
location.reload();
console.log("reload");
} else {
console.log(request.message);
}
});
How can I catch all request or run my background.js previously? Maybe I have made mistakes and can't find them?
Thanks!
If somebody will have the same mistake:
chrome.webRequest catch all requests, but content.js file inject later (after few requests).
So you should collect requests in background.js and send them after content.js injection (for example you can send a message to background.js).
when i change the window.location.href,the executeScript didn't work.why is this?
manifest.json is
{
"name": "Page Redder",
"description": "Make the current page red",
"version": "2.0",
"permissions": [
"activeTab","*://*/*"
],
"browser_action": {
"default_title": "Make this page red"
},
"background": {
"scripts": ["jquery-1.11.1.js","background.js"]
},
"manifest_version": 2
}
background.js is
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(tab.id,{code:'window.location.href="http://www.google.com"'},function(){
chrome.tabs.executeScript(tab.id, {file:"test.js"}, function() {
if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.message);
}
});
});
});
test.js is
alert("hello work")
The problem is that the file in the second executeScript is being injected after the code of the first executeScript has been executed, but before the window.location.href call has finished. See for yourself: add a breakpoint on the line calling the second chrome.tabs.executeScript, click your browser action and then wait for the page to load before resuming - the popup will work.
One way to solve this is to add a tabs.onUpdated listener. Then, when you click your browser action store what the tabId was. Inside the tabs.onUpdated listener you can execute test.js if the updated tabId matches the tabId set in your browser action. Quick example:
var activeTabId;
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(tab.id, {code: 'window.location.href="http://www.google.com"'}, function (){
activeTabId = tab.id;
});
});
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if(tabId === activeTabId) {
activeTabId = null; //To prevent executing the script multiple times
chrome.tabs.executeScript(tabId, {file:"test.js"});
}
});
I am creating a Chrome Extension. When I try to get the tabId and tabIndex, they are both showing as "undefined".
here is the background.js:
chrome.extension.onRequest.addListener(
function (request, sender)
{
if (request.command == "selected-tab")
{
chrome.tabs.getSelected(null,
function()
{
// both show as undefined
alert('sender.tabId: ' + sender.tabId);
alert('sender.tabIndex' + sender.tabIndex);
});
}
}
);
Here is the content-script.js:
chrome.extension.sendRequest({ command: "selected-tab", urltext: urlText });
Here is the manifest.json:
{
"manifest_version": 2,
"name": "test2",
"description": "Test2 desc",
"version": "1.0",
"permissions": [
"tabs", "http://*/*", "https://*/*","contextMenus"
],
"background": {
"scripts": ["jquery-1.11.0.min.js", "background.js"]
},
"content_scripts": [
{
"matches": ["http://*/*"],
"js": ["jquery-1.11.0.min.js", "content-script.js"]
}
]
}
How can I get the tabId and tabIndex of the current tab inside background.js?
Thanks Before Hand
Update #1
Tried this inside background.js and it still did not show the tab id on the alert:
chrome.tabs.getCurrent(function (tab) {
alert(tab.id);
});
The chrome.extension.onRequest event supports the concept of a response back to the sender, but if the receiver doesn't reply (or indicate that it intends to) there is some code that tries to garbage collect the JS context setup inside your event handler. Because you're starting another asynchronous operation via chrome.tabs.getSelected inside the context of your handler, it might be that the garbage collection kicks in before your callback for getSelected fires.
See the documentaiton on the callback parameter to runtime.onMessage:
"Function to call (at most once) when you have a response. The
argument should be any JSON-ifiable object. If you have more than one
onMessage listener in the same document, then only one may send a
response. This function becomes invalid when the event listener
returns, unless you return true from the event listener to indicate
you wish to send a response asynchronously (this will keep the message
channel open to the other end until sendResponse is called)."
One easy fix to try would be to manually call sendResponse inside your getSelected callback:
chrome.extension.onRequest.addListener(
function (request, sender)
{
if (request.command == "selected-tab")
{
chrome.tabs.getSelected(null,
function()
{
// both show as undefined
alert('sender.tabId: ' + sender.tabId);
alert('sender.tabIndex' + sender.tabIndex);
sendResponse(); // context can now be GC'd
});
}
return true; // indicates we plan to call sendResponse
}
);
I want to create a Chrome extension with a browser action onClicked which provides the same functionality as the following bookmark:
javascript:(function(){if(!window.page2rss_bookmark_urlr)window.page2rss_bookmark_urlr=function(ur){if(ur.error)alert(ur.error);if(ur.page&&ur.page.page)location.href=ur.page.page};var r=document.getElementById('urlFormRequest');if(r)r.parentNode.removeChild(r);r=document.createElement('script');r.id='urlFormRequest';r.type='text/javascript';r.src='http://page2rss.com/api/page?url='+encodeURIComponent(location.href)+'&callback=page2rss_bookmark_urlr';document.body.appendChild(r);})();
However, I struggle to correctly translate the javascript code of the bookmark into the logic of a Chrome extension. I thought the best to is to to put the exact code of the bookmark into a separate script create_feed_url.js and execute it in background.js. My background.js:
chrome.browserAction.onClicked.addListener(function(tab) {
// Run the bookmark code
chrome.tabs.executeScript(null, {file: "create_feed_url.js"});
// Open a new tab for a valid url resulting from create_feed_url.js
var feed_url = "http://page2rss.com/page?url=" + tab.url;
chrome.tabs.create({"url": feed_url});
Yet the code in create_feed_url.js somewhat runs not sucessfully. There is no feed URL generated, resulting in a non existing value for feed_url.
My questions:
Could you please help me to find out why I cannot just put the code of the bookmark into create_feed_url.js and run it?
Is this approach of executeScript recommendable in my case or is there a better way translating a bookmark into an extension?
I solved it with a workaround calling the URL that generates the new feed in a new tab before closing it and finally jumping to the tab with the final RSS feed URL. This solution does not require create_feed_url.js but relies completely on background.js:
chrome.browserAction.onClicked.addListener(function(tab) {
// Original bookmark JS code
//(function(){if(!window.page2rss_bookmark_urlr)window.page2rss_bookmark_urlr=function(ur){if(ur.error)alert(ur.error);if(ur.page&&ur.page.page)location.href=ur.page.page};var r=document.getElementById('urlFormRequest');if(r)r.parentNode.removeChild(r);r=document.createElement('script');r.id='urlFormRequest';r.type='text/javascript';r.src='http://page2rss.com/api/page?url='+encodeURIComponent(location.href)+'&callback=page2rss_bookmark_urlr';document.body.appendChild(r);})();
var create_feed_url = "http://page2rss.com/api/page?url=" + encodeURIComponent(tab.url); //+ "&callback=page2rss_bookmark_urlr"
var feed_url = "http://page2rss.com/page?url=" + tab.url;
chrome.tabs.create({"url": create_feed_url, active: false}, function(tab) {
chrome.browserAction.setBadgeText({text: 'wait'});
setTimeout(function() {
chrome.tabs.remove(tab.id, function(tab) {
chrome.browserAction.setBadgeText({text: ''});
});
}, 5000);
});
setTimeout(function() {
chrome.tabs.create({"url": feed_url, active: true}, function(tab) {
chrome.tabs.onUpdated.addListener(function( tabId , info ) {
if ( info.status == "complete" ) {
chrome.browserAction.setBadgeText({text: 'done', tabId: tabId});
}
});
}); }
, 1000);
});
Based on Rob's comment above of using a content script approach I tried to implement it. However, clicking on the browser icon does not trigger the content script create_feed_url.js through content_script.js. I tried to debug the code but neither the Developer Tools nor the inspect element tool show any error.
background.js:
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(null, {file: "content_script.js"});
});
content_script.js:
var s = document.createElement('script');
s.src = chrome.extension.getURL("create_feed_url.js");
s.onload = function() {
this.parentNode.removeChild(this);
};
(document.head||document.documentElement).appendChild(s);
create_feed_url.js:
(function(){if(!window.page2rss_bookmark_urlr)window.page2rss_bookmark_urlr=function(ur){if(ur.error)alert(ur.error);if(ur.page&&ur.page.page)location.href=ur.page.page};var r=document.getElementById('urlFormRequest');if(r)r.parentNode.removeChild(r);r=document.createElement('script');r.id='urlFormRequest';r.type='text/javascript';r.src='//page2rss.com/api/page?url='+encodeURIComponent(location.href)+'&callback=page2rss_bookmark_urlr';document.body.appendChild(r);})();
manifest.json:
{
"permissions": [
"tabs", "http://*/*", "https://*/*"
],
"background" : {
"scripts": ["background.js"],
"persistent": false
},
"web_accessible_resources": ["create_feed_url.js"],
"browser_action" :
{
"default_icon" : "rss-19.png",
"default_title" : "Create RSS feed for this page"
},
"manifest_version": 2
}