A Customized Chrome Extension Icon For Your Dev Version
When developping a Chrome Extension, it is a common pattern to have both installed your development version alongside the production version; the one provided by the Chrome Web Store or your Auto Update server.
The problem is they have the same icon. Which makes our life complicated when it is about clicking on the good extension icon.
Here is how I solved the problem.
§The Extension Channel Concept
The main concern we would like to address is to know if we are in a development context or not.
Unfortunately, the Chrome Extensions API does not let us know if the extension is unpacked (development) or not (production). We can only sniff the chrome.runtime.getManifest().key
value; which is populated when the extension is published on the Web Store.
The sniffing solution will not work if you ship both on the Web Store and an Auto Update Server (for beta test or any other reason). The key
value will be populated in both case, thus leading us back again to our initial problem: what is the execution context of the extension?
I like to make software design decision generic, so let’s say we rather prefer to know in which extension channel we are. Which means either production or development in our case. We have room to extend this concept in the future without adding extra complexity in our code.
The implementation will be done in 4 steps:
- Creating a configurable
BackgroundProcess
object - Requesting the Channel configuration
- Update the Extension icon
- Building our extension for various channels
The final result is available as a public Gist and eventually looks like the following image.
§Extension Background Page Boilerplate
For the sake of testability, we want to separate the features from the execution.
So our background page is defined as is in the Manifest file:
1 |
|
With this content in the process
file:
1 |
|
And this background controller file:
1 |
|
I like this pattern because we can manipulate our context more easily through the process
variable in the DevTools. So as it makes our tests damn easy to write. And the background testable even outside a background context (because it became a simple public API).
§Implementing the Extension Channel
Our channel configuration informations will lie in a specific file, alongside the manifest.json
. It will help our API to remain the same by keeping the differences in this file.
1 |
|
We design our public API first:
1 |
|
This file is loaded through an XMLHttpRequest during the BackgroundProcess
initialisation:
1 |
|
OK, now we have something nice. We can figure out the context of execution of our extension. It is about time to figure it out visually.
§Changing The browserAction
Icon
We will update the previous code to eventually update the browser icon to reflect a visual change.
1 |
|
This way, we execute a post-configuration code only if this latest is avaivable. We alter the extension behaviour only if we deviate from the production environment.
§But, What About Production Channel?
As for now, the channel.json
is systematically loaded. Even if we zip the content of the src/
folder to upload it on the Chrome Web Store.
I rely on Grunt to automate my packaging. For this example, we only need grunt-zip.
The workflow is the following:
- selecting recursively the content of the
src/
folder; - during the selection process, excluding the
channel.json
file; - compressing the content of the temporary folder in a ZIP file;
- when in production, the
channel.json
will not be loaded because it won’t be part of the package; - as a consequence, the
load
event will never be fired; - as a result, the
channel
value will remainproduction
.
The Gruntfile
resulting in this way would look like this:
1 |
|
§What’s Next?
Instead of executing a dynamic function (cf. the prior devSetup
) — thus increasing the size of our BackgroundProcess
class and breaking the single responsible principle — we could spread the configuration via an Extension event (cf. chrome.runtime.sendMessage). Several layers of the app can react to a same event. Like you would do in any DOM application.
Another approach would to be to monkey patch the BackgroundProcess
instance by loading an additional file before initialising the process. It may be easier than the previous solution but it could also lead to more hacky code.
We could also use the FileAPI
to browse a local extension configuration folder, matching the Semantic Versioning of our Manifest extension version:
v0.3-beta
->config/beta.json
;v0.2
->config/stable.json
- etc.
It’s up to you to choose the complexity you add in your extension. The only thing we want is that is works!
Feel free to share your remarks, improvements or correct any English mistake I made :-)