I recently had a side project where I had to gather information on some websites. It was a repetitive task that I had to do daily and it was quite boring. Since I thought it could be automated, I chose to give it a try by creating my first Chrome extension. 🧩
This post will serve me as a reflection article on some learning that I realized during this project. 🤔
Note : this extension was built using the Manifest V2, which will be replaced in some time by the Manifest V3. Thus, some information in this post may be out of date or needs to be adapted for the next version, which should be released in January 2021.
While building this extension, I experienced something that I think had ceased to exist since the
async/await functions and promises : callback hell. Every external function that I needed to call does not return a promise and takes a callback function... Oh my god, it was really a challenge to work with asynchronous code asynchronously!
Fortunately for us, the Manifest V3 should add promises to its APIs and, eventually, all methods will support promises. If I had known this information earlier, I would have tried to begin directly with the next version! I should have read the Chrome extensions guide before starting to create my extension! 😝
Let's see what new functions I used for my extension.
For my extension, the
listener function was my entry point , where the main logic is. I have not used its
tab parameter, but after looking at it, it looks like it is the information about the current opened tab. I also add an
async tag as my code is asynchronous. 🤘
For an example of the
tab details, take a look at the chrome.tabs.get function below.
As one of the goals of my extension is to navigate to a list of URLs, I quickly use the function to create a new tab. In its simplest form, I only provide the absolute URL I want to visit, with the
I recently added the
windowId parameter to make sure the tabs are created in the same window, instead of the active window. It will alloy me to do other things in a separate window while my script is running. 🧭
document.querySelector the information I was looking for.
Unfortunately, I had no way to know if my script had finished running or not, as it was wrapped into an async IIFE to have asynchronous functionalities. So I found a not very clean solution by renaming the tab title to a known value as the last line of my script.
chrome.tabs.get function gives a lot of information about a tab, but I found the most interesting are the three following properties:
status : the tab's loading status (
- title : the title of the tab
- url : the URL of the main frame of the tab
Once my scripts were injected and executed inside the new tabs, I would manually close them all at first, but it got tedious as I added more and more URLs to check. So with the previous function, I decided to just get rid of the tab once I got all the info I need.
One of the pages I was interested in had information inside an iframe, so my initial script was not working as I did not have access to it. Fortunately, we could specify the
frameId to target a specific frame of the tab. So, I use the
getAllFrames function to find the frame I want with its hostname.
I initially tried to use the
allFrames parameter of the
executeScript function to inject the script into all frames of the selected tab, but I was not working for me. I now believe it was because the frame had not finished loading. Anyway, I still prefer to inject my script only where it is needed, rather than injecting it on every iframes on the page.
While I was looking for a way to know when I could close a tab that had an iframe, I came across the
sendMessage function. It allows us to send a message to our extension. So I end up sending a message with the current URL to my extension to let it know that the script has been executed successfully.
To listen to messages, I just add the function below at the start of my extension and now I receive messages from my injected scripts. As it seems much better to depend on messages rather than updating the tab title, I plan to refactor that part someday.
While writing this post, I also learned that the function has a
sender argument which contains information about the frame AND the tab. I plan to use that information because it seems more reliable than my
document.URL message. 😉
Here's an example of a
sender argument below:
In retrospect, it was really fun learning to code a Chrome extension. I had never had the opportunity to try it before, both at work and in my personal projects. I hope to have another chance to build a more complex extension! 🤓
Note : as my extension is intended entirely for private use and has not been made reusable, so I do not plan on deploying it on the Chrome Web Store or releasing its source code. Sorry! 🔒