0

I am trying to make a Chrome extension that:

  1. reloads the URL of the tab where the extension is launched (say, for instance, www.example.com) every 7 seconds and performs a text search for "Text" on it;
  2. if "Text" exists, then extensions stops reloading the page, selects a certain radio button and clicks on a certain button on www.example.com.

My popup.html is designed with a simple UI with two buttons "Run" and "Stop", respectively running and stopping the reload-and-search function above.

The script in my popup page is called "app.js": <script src="app.js" charset="UTF-8"></script>

To make my content script perform its reload-and-search function on the webpage where the extension is launched, www.example.com, app.js programmatically injects a code.js file (with the reload-and-search function) in it when the user clicks the "Run" button from the UI. And since I would like to also be able to stop the reload-and-search function from the UI, app.js inject a clearInterval(function) code when the user clicks the "Stop" button from the UI.

This is how app.js looks:

chrome.tabs.query({active: true, currentWindow: true}, function(tab) {
document.querySelector('.button-run').addEventListener('click', () => {
    chrome.tabs.executeScript(tab.id, { file: 'code.js'}
    );
    });
});


chrome.tabs.query({active: true, currentWindow: true}, function(tab) {
document.querySelector('.button-stop').addEventListener('click', () => {
    chrome.tabs.executeScript(tab.id, { code: 'clearInterval(timer);'}
    );
    });
});

This is code.js:

function reloadAndFind() {

    if (document.body.innerHTML.search("Text") != -1) {

    clearInterval(timer); 
        // this is to stop the page from reloading when "Text" is found

    beep();
       // this is to call a beep() function coded elsewhere in code.js to make a "beep" sound when "Text" is found

    var radioButton = document.querySelectorAll('input[type=radio]'); 
        // this is to store the specific radio button I am interested in

    var button = document.getElementById("continue-button"); 
       // this is to store the specific button I am interested in 

    radioButton.checked = true;
    button.click();

    } 
}

timer = setInterval(function(){location.reload(); reloadAndFind();}, 7000); 
      // this is to concurrently perform a page reload and call the reloadAndFind() function on the page every 7 seconds

I have two problems:

  1. Stopping the function: When I click the "Run" button on my UI, the reloadAndFind() function starts and the page reloads every 7 seconds. After a number of reloads, when www.example.com shows "Text" I know the function found the tring because the beep() functions warns me, the radio button is selected and the button is clicked. However, clearInterval(timer) does not seem to work and the page keeps reloading again and again. It seems, however, to work when clearInterval() is injected by app.js when I click the "Stop" button on my UI. But this is useless, since I want the extension to be able to stop the reload by itself when I am not in the front of my computer. Also, problem No. 2 appears.
  2. Using "Stop" to stop the function works only partially: When I click the "Stop" button on my UI and clearInterval(timer) is injected through stopcode.js, it actually stops the page from reloading again. However, if I open another tab on Chrome and go to www.example.com, then the page keeps reloading as if I never injected clearInterval(timer).

How can I fix both problem 1 and 2? Please consider that I am very new to javascript and to programming in general.

If helpful, this is my manifest.json:

{
   "manifest_version": 2,
   "name": "Find Text",
   "version": "0.1",
   "permissions": [ "activeTab", "tabs", "storage", "browsingData", "notifications", "http://*/", "https://*/"],
   "content_scripts": [{
      "matches": ["https://www.example.com/*"],
      "all_frames": true,
      "js": ["app.js", "code.js"]
   }],
   "browser_action": {
      "default_popup": "popup.html",
      "default_title": "Find Text"
   }  
}
Rob
  • 11
  • 3
  • When the page is reloaded all scripts running there are terminated including the content scripts that you injected via executeScript. In other words, the current approach won't work. You can use your [background script](https://developer.chrome.com/extensions/background_pages) and chrome.tabs.reload instead. – wOxxOm Apr 20 '20 at 11:03
  • Thank you, @wOxxOm But if this is the case (I assumed that this was true only if I launched my script in the Chrome console, BTW), then why the page keeps loading and why it is able to find the "Text" string and call beep()? If the page reloading terminates the script injected, shouldn't it stop altogether after the first reload? Also, since I am very new to javascript, could you please provide additional detail/guidance (or point me where I could find additional information) on the solution that you are suggesting? – Rob Apr 20 '20 at 11:25
  • That's because you also declared `content_scripts` which automatically runs the scripts. See the link I have in my first comment for more info. – wOxxOm Apr 20 '20 at 11:26
  • @wOxxOm thanks. I have read it 10 times and googled around, but I am still unable to figure out to use the background script and chrome.tabs.reload to make my extension work. I'd appreciate if you could provide me with more details/directions. I also tried to look at the code of existing Chrome extensions that refresh the active URL but since they usually include several other features the code is a bit confusing to me. Thanks a lot. – Rob Apr 20 '20 at 13:39
  • In short your popup script will [send a message like 'start'](https://developer.chrome.com/extensions/messaging), the background script onMessage listener will start the timer and run executeScript with code.js (it won't have timer-related code of course). Note that the content script runs inside the web page so it can't access elements or variables in your popup script or background script, it can only send messages. This is really basic stuff but it may take some time to get used to multiple contexts in extensions so try inspecting/debugging the demo extensions. – wOxxOm Apr 20 '20 at 13:54
  • Thanks @wOxxOm . Does it mean that the timer must be included in background.js and not in code.js? So background.js will (i) listen when user clicks "Run" and inject code.js every 7 seconds (with code.js performing reload, find, select/click actions on www.example.com), and (ii) listen when user clicks "Stop" and perform a clearInterval of the timer used in the first listening event? And this will also solve problem No. 2 in my question, provided that I remove content_scripts from manifest.json and includes "scripts": ["background.js"], "persistent": false in manifest.json? – Rob Apr 20 '20 at 14:15
  • Almost. Reloading will be performed in background.js using chrome.tabs.reload. Your content script (code.js) will send a message e.g. 'stop' if reloading should stop, the same message that can be sent from the popup script when the stop button in your UI is clicked. – wOxxOm Apr 20 '20 at 14:21
  • Thank you! But in order to have automatic reloading every 7 secs until 'stop' message is sent I should still use setInterval with chrome.tabs.reload, right? Otherwise it would reload only once. – Rob Apr 20 '20 at 16:02
  • Yes, of course. – wOxxOm Apr 20 '20 at 16:03
  • Thanks @wOxxOm, I added an answer with my new attempt, to avoid extending discussions in comments. – Rob Apr 20 '20 at 17:07

1 Answers1

0

UPDATE:

I have modified my extension while trying to follow wOxxOm's valuable information, but it still does not work.

I now have this background.js script:

chrome.runtime.onMessage.addListener(function(message, callback){
   if(message === 'start') {
      var timer;
      timer = setInterval(function(){chrome.tabs.reload; chrome.tabs.executeScript({ file: 'code.js'});}, 7000);
   } else if(message === 'stop') {
      clearInterval(timer);
   }
});

I have modified app.js as follows:

chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
document.querySelector('.button-run').addEventListener('click', () => {
    var activeTab = tabs[0];
    chrome.tabs.sendMessage(activeTab.id, {"message": 'start'});
    });
});

chrome.tabs.query({active: true, currentWindow: true}, function(tab) {
document.querySelector('.button-stop').addEventListener('click', () => {
    var activeTab = tabs[0];
    chrome.tabs.sendMessage(activeTab.id, {"message": 'stop'});
    });
});

The modified and simplified content script, code.js to test the new setup:

function findText() {
    if (document.body.innerHTML.search("Text") != -1) {
    alert("Text found");
    // potentially perform other actions
    } 
}

(I guess that if and when everything will work as intended, I should include chrome.tabs.sendMessage(activeTab.id, {"message": 'stop'}) in code.js when "Text" is found.)

manifest.json:

{
   "manifest_version": 2,
   "name": "Find Text",
   "version": "0.1",
   "externally_connectable": {
      "matches": ["http://www.example.com"]
   },
   "permissions": [ "activeTab", "storage", "browsingData", "notifications", "http://*/", "https://*/"],
   "background": {
      "scripts": ["background.js"],
      "persistent": false
   },
   "browser_action": {
      "default_popup": "popup.html",
      "default_title": "Find Text"
   }  
}

Now when I click the "Run" button on my UI www.example.com reloads every 7 seconds and it stops reloading when I click the "Stop" button on my UI. However, the findText() function in code.js does not seem to work: when "Text" is displayed on the page, nothing happens.

Also, problem No. 2 above still remains: if I click the "Stop" button on my UI the autorefresh stops, but when I open another tab on Chrome and go to www.example.com, then the page keeps reloading as if the autorefresh was never stopped.

I am 100% sure that I made more than one mistake here, but I can't spot them.

Rob
  • 11
  • 3
  • 1) `timer` should be a global variable. 2) to send a message to the background script use chrome.runtime.sendMessage('start') just like this without tab id or query(). – wOxxOm Apr 20 '20 at 17:09
  • Thanks! Does it mean that to send a message I can just use: " document.querySelector('.button-run').addEventListener('click', () => { chrome.tabs.sendMessage({"message": 'start'}); }); "? – Rob Apr 20 '20 at 17:16
  • Yes, but chrome.runtime.sendMessage('start'), not `tabs` and no need for `"message"`. – wOxxOm Apr 20 '20 at 17:24
  • Got it. But it doesn't work. It does not even start refreshing the page. Console says "Error in invocation of tabs.sendMessage(integer tabId, any message, optional object options, optional function responseCallback): No matching signature." As per your suggestion, app.js now only includes document.querySelector('.button-run').addEventListener('click', () => {chrome.tabs.sendMessage('start');}); and a corresponding line for '.button-stop'. Also, I moved "var timer;" at the begging of background.js. – Rob Apr 20 '20 at 17:44
  • For the third time, it's chrome.runtime.sendMessage, not tabs. – wOxxOm Apr 20 '20 at 17:46
  • Oh, sorry, I overlooked it. I just paid attention to 'start' between braces. I have updated it accordingly and yet nothing happens. Console does not return any errors, though. The extension loading page returns the following three errors: FIRST: "Unchecked runtime.lastError: Cannot access contents of the page. Extension manifest must request permission to access the respective host" (this error keeps refreshing in the errors page); SECOND: "Unchecked runtime.lastError: There is no tab in browser window"; THIRD: "Unchecked runtime.lastError: Cannot access a chrome:// URL". – Rob Apr 20 '20 at 18:12
  • All three errors include the following details: "Contest: _generated_background_page.html" and "Stack analysis: _generated_background_page.html:0 (anonimous function)". Many thanks for your help. – Rob Apr 20 '20 at 18:12
  • The errors on `chrome://extensions` page are probably old, you need to manually clear them. Use [devtools for the background script](https://stackoverflow.com/a/10258029) to debug it if needed. Also, chrome.tabs.reload does nothing at all, it doesn't even invoke because there's no `()`. – wOxxOm Apr 20 '20 at 18:17
  • Thanks. Right, I have fixed chrome.tabs.reload() and the auto-refresh works but it still does not seem to run the findText() function in code.js on the page. Maybe I am not invoking it correctly in background.js or maybe there is a problem in my manifest (I also included "tabs" under "permissions", if helpful). – Rob Apr 20 '20 at 18:40
  • code.js currently only defines findText() function but nothing calls it. Either don't use a function at all or add a call e.g. findText(); read more about functions in JavaScript if needed. – wOxxOm Apr 20 '20 at 18:48
  • Right, thanks! I didn't notice that in this version I wasn't calling the function in code.js! In the previous one I did. Many thanks for your help! – Rob Apr 20 '20 at 18:52
  • @wOxxOm the extension works thanks to your suggestion, except that it reloads all tabs of the browser (i.e., I can't let the extension run in background in the tab where I launched it and open other urls in new tabs because any active tab will refresh every 7 seconds). I'm guessing this could be avoided by passing the id of the tab where the extension is launched as a variable to chrome.tabs.reload(), so I've included in background.js a global var currentTab and added chrome.tabs.query({active: true, currentWindow: true}, tabs => { let currentTab = tabs[0].id; }); – Rob Apr 21 '20 at 11:37
  • Then I'm passing currentTab variable in chrome.tabs.reload(currentTab) but still doesn't work: the extension will reload any new active tab – Rob Apr 21 '20 at 11:38
  • You need to call query() when 'start' message is received and use the tab id in executeScript as well. See the documentation for details. Post a new question if needed. – wOxxOm Apr 21 '20 at 11:50
  • Thanks a lot! This solved my issue. I really appreciate your help and time. BTW, I've added an unrelated new question for this same extension if you have time to take a look at it: https://stackoverflow.com/questions/61344376/how-to-make-dynamic-changes-to-popup-html-in-chrome-extensions . – Rob Apr 21 '20 at 13:13