Performance best practices in extensions
Support for extensions using XUL/XPCOM or the Add-on SDK was removed in Firefox 57, released November 2017. As there is no supported version of Firefox enabling these technologies, this page will be removed by December 2020.
Add-ons using the techniques described in this document are considered a legacy technology in Firefox. Don't use these techniques to develop new add-ons. Use WebExtensions instead. If you maintain an add-on which uses the techniques described here, consider migrating it to use WebExtensions.
Starting from Firefox 53, no new legacy add-ons will be accepted on addons.mozilla.org (AMO) for desktop Firefox and Firefox for Android.
Starting from Firefox 57, only extensions developed using WebExtensions APIs will be supported on Desktop Firefox and Firefox for Android.
Even before Firefox 57, changes coming up in the Firefox platform will break many legacy extensions. These changes include multiprocess Firefox (e10s), sandboxing, and multiple content processes. Legacy extensions that are affected by these changes should migrate to use WebExtensions APIs if they can. See the "Compatibility Milestones" document for more information.
A wiki page containing resources, migration paths, office hours, and more, is available to help developers transition to the new technologies.
One of Firefox's great advantages is its extreme extensibility. Extensions can do almost anything. There is a down side to this: poorly written extensions can have a severe impact on the browsing experience, including on the overall performance of Firefox itself. This article offers some best practices and suggestions that can not only improve the performance and speed of your extension, but also of Firefox itself.
Extensions are loaded and run whenever a new browser window opens. That means every time a window opens, your extension can have an impact on how long it takes the user to see the content they're trying to view. There are several things you can do to reduce the amount of time your extension delays the appearance of the user's desired content.
Don't load things during startup that are only needed if the user clicks a button, or if a given preference is enabled when it's not. If your extension has features that only work when the user has logged into a service, don't load the resources for those features until the user actually logs in.
Most extensions have a load event listener in the main overlay that runs their startup functions. Do as little as possible here. The browser window is blocked while your add-on's load handler runs, so the more it does, the slower Firefox will appear to the user.
If there is anything that can be done even a fraction of a second later, you can use an
nsITimer or the
window.setTimeout() method to schedule that work for later. Even a short delay can have a big impact.
Memory leaks require the garbage collector and the cycle collector to work harder, which can significantly degrade performance.
See Common causes of memory leaks in extensions for ways to avoid zombie compartments and other kinds of leaks.
- Read the "writing efficient CSS" guide.
- Remember that any selector in your rule which might match many different nodes is a source of inefficiency during either selector matching or dynamic update processing. This is especially bad for the latter if the selector can dynamically start or stop matching. Avoid unqualified ":hover" like the plague.
DOM mutation event listeners are extremely expensive and, once added to a document even briefly, significantly harm its performance. As mutation events are officially deprecated, and there are many alternatives, they should be avoided at all costs.
defineLazyGetter()defines a function on a specified object that acts as a getter which will be created the first time it's used. See examples.
defineLazyServiceGetter()defines a function on a specified object which acts as a getter for a service. The service isn't obtained until the first time it's used. Look through the source for examples.
Many common services are already cached for you in Services.jsm.
This cannot be stressed enough: never do synchronous I/O on the main thread.
Any kind of I/O on the main thread, be it disk or network I/O, can cause serious UI responsiveness issues.
Avoid using mouse event listeners, including mouseover, mouseout, mouseenter, mouseexit, and especially mousemove. These events happen with high frequency, so their listeners can trivially create very high CPU overhead.
When these events cannot be avoided, computation during the listeners should be kept to a minimum and real work throttled. The listeners should be added to the most specific element possible, and removed when not immediately necessary.
Animated images are much more expensive than generally expected, especially when used in XUL
You can use a
ChromeWorker to execute long running tasks or do data processing.