0

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 ?

Community
  • 1
  • 1
waaadim
  • 409
  • 5
  • 17
  • 1
    Have you tried forcing your content script to run after the [document loads](http://stackoverflow.com/a/5114084/949328)? – hiattp Nov 05 '13 at 17:05
  • Post your manifest as well. – gkalpak Nov 05 '13 at 20:07
  • @hiattp I've added `"run_at": "document_end"`, but nothing changed. – waaadim Nov 06 '13 at 07:57
  • Why are you using content scripts? The background page alone is sufficient for this purpose. In addition, when using background/event pages, you can see navigations to all URLs, including `chrome://` URLs. Check out the [`chrome.webNavigation`](https://developer.chrome.com/extensions/webNavigation.html) API, in particular the [`onCommitted`](https://developer.chrome.com/extensions/webNavigation.html#event-onCommitted) event. – Rob W Nov 06 '13 at 11:21

1 Answers1

4

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:

  1. You visit http://www.site1.com.
  2. chrome.tabs.query({...}, callback) gets executed (notice the callback has not been executed yet).
  3. sendResponse({data: currentUrl}) gets executed (note that at this point currentUrl is not defined).
  4. 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).
  5. You visit http://www.site2.com.
  6. chrome.tabs.query({...}, callback) gets executed (notice the callback has not been executed yet).
  7. sendResponse({data: currentUrl}) gets executed (note that at this point currentUrl still equals http://www.site1.com from step 4).
  8. 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 ?

gkalpak
  • 47,844
  • 8
  • 105
  • 118
  • I must shamefully admit that I actually needed solution `(C)`. But both (A) and (B) are returning undefined no matter how many times I refresh. PS. I replaced the code from A,B in `background.js` – waaadim Nov 07 '13 at 07:49
  • 1
    It is strange, because I used the exact same code and it worked. Make sure you have replaced your content script's **chrome.extension.sendRequest(...)** with `chrome.runtime.sendMessage(msg, function(response) {...});`. Do you see any errors in the console log ? – gkalpak Nov 07 '13 at 09:19