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.
Callback hell π
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.
browserAction
Execute a function after clicking on the extension icon
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.
tabs
Create a tab
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 url
parameter.
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. π§
Execute a script inside a tab
Once the tab was created and fully loaded (after a minimal sleep), I could inject any JavaScript file into the page and retrieve with a couple of 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.
Get information about a tab
The 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 (
"unloaded"
,"loading"
, or"complete"
) - title : the title of the tab
- url : the URL of the main frame of the tab
Delete a 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.
webNavigation
Get all frames of a tab
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.
runtime
Send a message
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.
Listen for messages
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:
Conclusion
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! π
Top comments (1)
so unfortunate that this is obsolete because Google in its infinite wisdom decided to deprecate manifest V2.