3

I am currently writing a simple chrome extension that saves the current webpage to local storage when you click the extension icon in the header.

I have successfully saved the url into local storage using background.js as per the Chrome Documentation here: https://developer.chrome.com/extensions/activeTab.

My issue is that when I click the chrome extension icon the first time my event fires but my popup.js errors out with

Uncaught TypeError: Cannot read property 'getSelected' of undefined

background.js

chrome.browserAction.onClicked.addListener(function(tab) {

  console.log('Turning ' + tab.url + ' red!');
  var tabNew = tab.url
  chrome.storage.sync.set({ ["key" + 1]:  tabNew}, function(){
      //  A data saved callback omg so fancy
  });

  chrome.storage.sync.get(/* String or Array */["key" + 1], function(items){
      //  items = [ { "yourBody": "myBody" } ]
      console.log(items)
  });


  chrome.tabs.executeScript({
    code: 'document.body.style.backgroundColor="red"'
  });

  chrome.browserAction.setPopup({popup: 'popup.html'});


});

popup.js

chrome.tabs.getSelected(null, function(tab) {
    document.getElementById('currentLink').innerHTML = tab.url;
});



document.addEventListener('DOMContentLoaded', function() {
    var link = document.getElementById('download');
    // onClick's logic below:
    link.addEventListener('click', function() {
        chrome.storage.sync.get(null, function(items) { // null implies all items
            // Convert object to a string.
            var result = JSON.stringify(items);

            // Save as file
            chrome.downloads.download({
                url: 'data:application/json;base64,' + btoa(result),
                filename: 'filename_of_exported_file.json'
            });
        });        
    });
});

popup.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head> 

    <link href='http://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'> 
    <link href='style.css' rel='stylesheet' type='text/css'> 

    <title>Discoveroo</title> 

    <!-- <script type="text/javascript" src="https://www.google.com/jsapi"></script>  -->
    <base target="_blank">

</head>

<body>
<p id="currentLink">Loading ...</p>
<hr />
<ul id="savedLinks"></ul>
<script type="text/javascript" src="popup.js"></script>

<button id="download">Download All</button>
</body>
</html>

manifest.json

{
  "name": "APPNAME",
  "version": "0.1",
  "manifest_version": 2,
  "description": "APP DESC",
  "permissions": ["activeTab", "storage", "downloads", "<all_urls>"], 
  "background": {
    "scripts": ["background.js"],
    "persistent": true
  },
  "content_scripts": [{
    "matches": ["http://*/*", "https://*/*"],
    "js": ["popup.js"]
  }],
  "browser_action": {
    "default_title": "Add this page to my list"
  },
  "icons": {
    "128": "icon.png"
  }
}
cleme001
  • 341
  • 5
  • 15

1 Answers1

4
  1. Don't use popup.js as a content script. You don't need the content script. See the architecture overview. The content scripts have limited access to chrome API and run in the web page, not in the browserAction popup which is a totally separate full extension page with its own chrome-extension:// URL and own devtools console etc, not related to the web page.
  2. browserAction.onClicked doesn't work simultaneously with a popup html so you should pick one. In this case since you need to display the popup page, declare it in manifest.json, remove browserAction.onClicked, put its inner code into your popup.js
  3. As a consequence, there'll be no need for the background script
  4. There's probably no need for <all_urls> permission since you already have activeTab which gives you full access to the active tab after the extension icon is clicked.
  5. chrome.tabs.getSelected is deprecated and will be removed eventually so use chrome.tabs.query({active: true, currentWindow: true}, tabs => { ... })
  6. No need to read chrome.storage since you already get the active URL in your popup.js.
  7. Regarding "omg so fancy", callbacks are not an arbitrary weirdness, but a consequence of the basic fact: the API is asynchronous so the callback is invoked at a later point in time, it's like a one-time "onload" event listener. There are many tutorials about asynchronous JavaScript.
  8. Use the safe textContent property instead of the potentially unsafe innerHTML for displaying a plain text string such as the URL.

manifest.json

  • remove "background"
  • remove "content_scripts"
  • add "default_popup"

    "browser_action": {
      "default_title": "Add this page to my list",
      "default_popup": "popup.html"
    },
    

background.js

Delete it.

popup.js

The only things from the old background script you might need, are executeScript and sync.set calls.

chrome.tabs.executeScript({
  code: 'document.body.style.backgroundColor="red"'
});

chrome.tabs.query({active: true, currentWindow: true}, ([tab]) => {
  document.getElementById('currentLink').textContent = tab.url;
  chrome.storage.sync.set({key1: tab.url});
});

You can also load the Mozilla WebExtension polyfill (using <script> tag in your popup.html right before popup.js) and switch to async/await syntax instead of callbacks:

(async () => {
  const [tab] = await browser.tabs.query({active: true, currentWindow: true});
  document.getElementById('currentLink').textContent = tab.url;
  await chrome.storage.sync.set({key1: tab.url});
})();
wOxxOm
  • 65,848
  • 11
  • 132
  • 136
  • I wanna ask sth. What if I use web-ext redux in the extension? How can I sync proxy(popup) store and wrapped(background) store? – Ceren Keklik Feb 03 '22 at 09:40