Search code examples
javascriptfirefox-addonfirefox-addon-webextensions

Show custom HTML in Firefox add-on popup/dialog


I'm learning how to make an extension (for Firefox).

This add-on reads the youtube main page (https://www.youtube.com/) and get the visible videos on it. From those videos, I'm able to extract the video ID, title, duration of the video, channel name and channel ID.

With this data, I want to show a pop-up dialog with a HTML table that has the obtained info as folows:

Channel No. of videos
NationSquid 1
Numberphile 4
JhonNroses 8
HugoX Chugox 3

[...and so on]


My manifest.json:

{
  "manifest_version": 2,
  "name": "Ejemplo",
  "version": "1.0",
  "description": "Inspeccionar elementos de youtube.com.",
  "homepage_url": "https://github.com/mdn/webextensions-examples/tree/main/beastify",
  "icons": {
    "48": "icons/border-48.png"
  },
  "applications": {
    "gecko": {
      "id": "[email protected]"
    }
  },
  "permissions": [
    "tabs",
    "activeTab",
    "storage",
    "background"
  ],
  "browser_action": {
    "default_icon": "icons/beasts-32.png",
    "default_title": "Obtain YouTube Info",
    "default_popup": "popup/results.html",
    "browser_style": true
  },
  "content_scripts": [
    {
      "matches": [
        "https://*.youtube.com/"
      ],
      "js": [
        "jquery-1.9.1.min.js",
        "Ejemplo.js",
        "/code/functions.js"
      ]
    }
  ],
  "background": {
    "scripts": [
      "/code/popup.js"
    ]
  }
}

My results.html file = which is the pop-up:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
</head>
<body>
    <div id="allVideos"><i>Loading...</i></div>
    <script src="../jquery-1.9.1.min.js"></script>
    <script src="../code/popup.js"></script>
</body>
</html>

My popup.js file = the file that writes the HTML code in the "results.html" file:

(function () {
  console.log("Entró");
  init_code();
})();

/**
 * Function that initializes the page.
 */
function init_code() {
  try {
    // Initial variables: 
    var html_code = "";

    // Get all videos: 
    // Source: https://www.sitepoint.com/create-firefox-add-on/
    browser.storage.sync.get(['all_videos'])
      .then((result) => {
        if (result.hasOwnProperty('all_videos') && result.all_videos) {
          if (result.all_videos && result.all_videos.length) {
            // Build initial HTML table code: 
            html_code += `<table id="myTable"><tr><th>Thumbnail</th><th>Title</th><th>Channel_ID</th></tr>`;

            // Loop over the items and build the HTML with their data: 
            result.all_videos.forEach((item) => {
              html_code += appendItem(item);
            });

            // Finish the HTML table code: 
            html_code += "</table>";

          } else {
            // Show: "no items available": 
            html_code = '<strong>No videos are stored on the localStorage.</strong>';
          }
        } else {
          // Show: "no items available": 
          html_code = '<strong>No videos are stored on the localStorage.</strong>';
        }
      }, onErrorDetails);

    console.log("Ok!");

    // Show the results: 
    document.getElementById("allVideos").innerHTML = html_code;
  } catch (ex) {
    console.log("Error at the popup");
    console.log("Error: " + ex.message);
    console.log("Stack: " + ex.stack);
  }
}

/**
 * Function that builds the HTML to be added on the main HTML div.
 * @param {object} item The item (i.e, the object that contains the video's data).
 * @returns string
 */
function appendItem(item) {
  return `<tr><img src='${item.Thumbnail}' alt='${item.Title}' /></tr><tr><span>${item.Title}</span></tr><tr><span>${item.Channel}</span></tr><tr><span>${item.Channel_ID}</span></tr>`;
}

/**
 * Function that handles the error(s) - if any - when the info is obtained from the localStorage.
 * @param {object} error_dt The object with the error details.
 */
function onErrorDetails(error_dt) {
  console.log("Error!");
  console.log(error_dt);
}

My goal is:

I want the add-on shows a pop-up dialog with the HTML I want to specify - in this case, a HTML <table> as shown in the example above.

My current issue is:

Despite all variations made in the manifest.json file, the popup.js code is not triggered at all.

Current result: Shows the "Loading..." text embed in the HTML file, when it should build the table HTML.

Loading popup-without modifications

I've read about the user interface and popups in the documentation; however, I followed step-by-step the different tutorial(s) I've found, but, I haven't found a reason about why this code is not running - no even errors or logs in the console.

Can anyone point me to the right path to achive this goal?


1 In the documentation - section "specifying a popup", the add-on uses a base HTML with a pre-defined HTML, but, I haven't found if this "base HTML" can be modified with javascript.


Solution

  • Ok, I finally figure it out.

    The key parts are:

    • Use browser.storage.sync.set and browser.storage.sync.get for use the localStorage. I did follow this tutorial.
    • Load correctly the path of the files in both, the manifest.json and popup.html files = the files that uses your resources. In my case, I didn't set the relative path to the .js file correctly = resulting in not being able to use javascript code at all.
    • The .js file that executes your code in your popup.html file (in my case, the popup.js file) must be added all (including functions) inside de $(document).ready(() => { code block (or its equivalent)1.

    I share my manifest.json file:

    {
      "manifest_version": 2,
      "name": "Ejemplo",
      "version": "1.0",
      "description": "Inspeccionar elementos de youtube.com.",
      "homepage_url": "https://github.com/mdn/webextensions-examples/tree/main/beastify",
      "icons": {
        "48": "icons/border-48.png"
      },
      "applications": {
        "gecko": {
          "id": "[email protected]"
        }
      },
      "permissions": [
        "tabs",
        "activeTab",
        "storage"
      ],
      "browser_action": {
        "default_icon": "icons/beasts-32.png",
        "default_title": "Obtain YouTube Info",
        "default_popup": "popup/results.html",
        "browser_style": true
      },
      "content_scripts": [
        {
          "matches": [
            "https://*.youtube.com/"
          ],
          "js": [
            "jquery-1.9.1.min.js",
            "Ejemplo.js",
            "/code/functions.js",
            "/code/popup.js"
          ]
        }
      ]
    }
    

    results.html = my popup:

    <!DOCTYPE html>
    <html>
    
    <head>
        <meta charset="UTF-8">
        <link href="../css/popup.css" rel="stylesheet" />
    </head>
    
    <body>
        <table id="myTable">
            <tr>
                <th>Channel</th>
                <th>No. of videos</th>
            </tr>
        </table>
        <script src="../jquery-1.9.1.min.js"></script>
        <script src="../code/popup.js"></script>
    </body>
    
    </html>
    

    popup.js = the file that creates the HTML and appends it to results.html:

    $(document).ready(() => {
    
      // Source: https://www.sitepoint.com/create-firefox-add-on/
      browser.storage.sync.get(['all_videos']).then((result) => {
        // Loop over the items and build the HTML with their data: 
        result.all_videos.forEach((item) => {
          appendItem(item);
        });
      }, onErrorDetails);
    
      /**
     * Function that builds the HTML to be added.
     * @param {object} item The item (i.e, the object that contains the video's data).
     * @returns string
     */
      function appendItem(item) {
    
        // Append the row(s) to the table: 
        // Source: https://www.enablegeek.com/tutorial/jquery-add-row-to-table/
        $('#myTable tbody').append(`<tr>
                                      <td>
                                      <td><span>${item.channelName}</span></td>
                                      <td><span>${item.qtyVideos}</span></td>
                                    </tr>`);
      }
    
      /**
     * Function that handles the error(s) - if any - when the info is obtained from the localStorage.
     * @param {object} error_dt The object with the error details.
     */
      function onErrorDetails(error_dt) {
        console.log("Error!");
        console.log(error_dt);
      }
    
    });
    

    1 I suspect this is due to the webpage lifecycle; for some reason, I could not use document.getElementById("...") - everytime I got undefined, so, instead, I have to use JQuery for append the HTML in construction - i.e: $('#myTable tbody').append(<my_string_html>); - see here how to append a row to a table using JQuery.