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.
Improving startup performance
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.
Load only what you need, when you need it
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.
Defer everything that you can
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.
General Performance Tips
Avoid Creating Memory Leaks
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.
Avoid Writing Slow CSS
- 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.
Avoid DOM mutation event listeners
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.
Lazily load services
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.
Use asynchronous I/O
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.
- Never use synchronous XMLHttpRequests.
- NetUtils.jsm provides helpers for asynchronous reading and copying of files.
- Never access a SQLite database synchronously. Use the asynchronous API instead.
- Performing sequential, asynchronous operations can often be greatly simplified using Promises.
Avoid mouse movement events
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.
Avoid animated images
Animated images are much more expensive than generally expected, especially when used in XUL
Consider using Chrome Workers
You can use a
ChromeWorker to execute long running tasks or do data processing.