3

I have a basic content script in my manifest.json file where I have it match a certain URL and also load up the scripts.

"content_scripts": [
    {
        "matches": ["http://www.urlhere.com/videos/*"],
        "js": ["scripts/jquery-2.1.1.min.js", "scripts/csScript.js"]
    }
],

The csScript.js gets an attribute on the page and sends it back to the background.js script through a message.

chrome.runtime.sendMessage({url: getUrl()});

function getUrl() {
  url = $('meta[itemprop=contentURL]').attr('content');
  return url;
}

The background.js page gets the message with the attribute value, manipulates it, and then shows the page_action icon.

chrome.runtime.onMessage.addListener(
  function(request, sender, sendResponse) {
    url = manipulateUrl(request.url);
    chrome.pageAction.show(sender.tab.id);
});

chrome.pageAction.onClicked.addListener(function(tab) {
console.log("url: " + url);
});

This all works fine and dandy on the first page load - the content script gets executed and the message is passed. However, the page has links that goes to other video...when I click on one of those, I notice that the Chrome spinner loads up a new URL in the address bar but the content script is not executed again.

When I click my page_action icon I get the old URL that was passed which indicates that the content script didn't go get the new one for the page.

Any ideas?

joshft91
  • 1,755
  • 7
  • 38
  • 53
  • just on the offchance, but are you sure the new URL when you click a link matches that string too? Possibly it drops `http` or it becomes `https`? – James Aug 19 '14 at 14:47
  • Nearly positive that it's not changing. `https` never shows up for any of the video pages so I think that's not the issue. Something must be staying consistent in the DOM for the content script to not get injected again is my guess, but I'm too new to extensions development to know. I've also tried `programmatic injection` to inject the script whenever the `page_icon` is clicked, but the same issue occurs - I get the old url data from the 'old' DOM. – joshft91 Aug 19 '14 at 14:53
  • Is it a fully new URL or just the hash part (window.location.hash) that changes? – James Aug 19 '14 at 15:02
  • Definitely a new URL. If I go back to the home page, then click on a video, it works fine. But after the video ends, I get a list of suggested videos - if I click one, the spinner spins and loads up a new URL but no content script is injected. Doesn't make much sense to me. – joshft91 Aug 19 '14 at 15:07
  • 1
    possibly similar situation: http://stackoverflow.com/questions/20865581/chrome-extension-content-script-not-loaded-until-page-is-refreshed – James Aug 19 '14 at 15:15
  • Hmm, good find. That definitely helped me get closer...whenever I click on any related videos, the content script is run, but it still gives me the old URL. Possibly getting run too early and still grabbing the old video? Not sure - time for some investigation – joshft91 Aug 19 '14 at 15:29

1 Answers1

2

The URL is probably changed through history manipulation or URL fragment change, but does not actually reload the page.

If it's a URL fragment change, you can use window.onhashchange event.

If the URL changes completely but page does not reload, it probably is achieved with history.pushState or history.replaceState functions. Unfortunately, they do not trigger any event. It is, therefore, tricky to detect.

A hacky solution would be to inject code into the page to replace history.pushState function in the page's context, so that it also does something you can detect from the content script. It can be a DOM change, a custom event, or a window.postMessage call.


Edit: from a linked question, a better solution is to listen to chrome.webNavigation.onHistoryStateUpdated (credit to joshft91)

Community
  • 1
  • 1
Xan
  • 74,770
  • 16
  • 179
  • 206
  • Is there a good way to tell if it's a fragment change? It wouldn't surprise me if it is since everything is the same except for the last fragment which is the title. However, the Chrome spinner was spinning so I assumed it was loading a new page. I'll take a look at your solutions and see if I can get anything work. Thanks. – joshft91 Aug 19 '14 at 15:18
  • See if `window.onhashchange` is triggered when it happens, e.g. set `window.onhashchange = function(e){console.dir(e);}` – Xan Aug 19 '14 at 15:19
  • judgeja linked to this possible solution (http://stackoverflow.com/questions/20865581/chrome-extension-content-script-not-loaded-until-page-is-refreshed) which seems to get me closer. Every time I click one of the related videos, I get an alert that the content script has run, but it still is pulling from the old, original DOM. Interesting. – joshft91 Aug 19 '14 at 15:31
  • It is possible that page's JavaScript updates the history state before updating the DOM. Then you are in a race condition. Try delaying re-injection by some time. – Xan Aug 19 '14 at 15:34
  • Currently my `.onHistoryStateUpdated()` method looks like this for injecting the content script: http://jsfiddle.net/uu4tbnrw/ I'm not a Javascript developer by any means, and after some googling I'm not sure what would be the best way to delay the injection. What is the best practice for doing that? – joshft91 Aug 19 '14 at 15:41
  • 1) You probably don't need to reinject jQuery, 2) `setTimeout(function(){/*...*/},500)` will delay by half a second (asynchronously). This is not a "best practice" solution, only means of debugging what's going on. – Xan Aug 19 '14 at 15:42
  • Changed to: http://jsfiddle.net/uu4tbnrw/1/ The delay is definitely there but for some weird reason it's still grabbing the old URL. No idea how or why. Hmm. – joshft91 Aug 19 '14 at 15:59
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/59593/discussion-between-joshft91-and-xan). – joshft91 Aug 19 '14 at 16:05