MozAfterPaint

This article needs a technical review. How you can help.

The MozAfterPaint event is triggered when content is presented to the user on the screen and provides information about what has been repainted. It is mainly used to investigate performance optimization.

Note that MozAfterPaint is not fired immediately after painting has occurred, but after both painting and compositing have occurred. This means that when it fires, something has been presented to the user.

Note:
  • This event is available to add-ons but since Firefox 4 it is not available to web pages by default. It can only be made available to web pages by setting the preference dom.send_after_paint_to_content to true. (Since Bug 539356, when this preference is set to true, all the MozAfterPaint events are sent to web pages. See Bug 829330 for more information.)
  • Web pages that want to take an action after a repaint of the page can use requestAnimationFrame with a callback that sets a timeout of zero to then call the code that takes the desired post-repaint action.
  • If the handler for this event does anything that triggers repainting (such as changing the style of an element), an infinite loop will probably be triggered.
  • Repainting of areas scrolled outside the viewport is reported, but repainting of areas scrolled outside overflow:auto elements and the like is not.
  • Repainting in windowed plug-ins (which is most plug-ins on Windows and GTK) is not reported.

General info

Specification
Add-ons specific
Interface
Event
Bubbles
Yes
Cancelable
Yes
Target
window
Default Action
None

Properties

Property Type Description
target Read only EventTarget The event target (the topmost target in the DOM tree).
type Read only DOMString The type of event.
bubbles Read only boolean Does the event normally bubble?
cancelable Read only boolean Is it possible to cancel the event?
boundingClientRect clientRect The equivalent of getBoundingClientRect() for the repainted zone. Read only.
clientRects clientRectList The equivalent of getClientRects()  for the repainted zone. Read only.
transactionId uint64_t The transaction id of the composition that just occurred to present something to the user. Read only.

Example

This example highlights elements that get repainted while hovering the document with a cursor.

(function(){
  var store = [];
 
  // every repaint will be logged in store
  window.addEventListener("MozAfterPaint", log, false);
 
  if ( document.body )
    bind();
  else
    window.addEventListener("load", bind, false);
 
  function log(e){
    store.push( [(new Date).getTime(), e.clientRects] );
  }
 
  function bind(){
    // clicking anywhere on the document will prevent other repaint to be logged
    // as well as display the visual "repaint heatmap"
    document.body.addEventListener("click", function onClick(){
      window.removeEventListener("MozAfterPaint", log, false);
 
      for ( var pos = 0; pos < store.length; pos++ ) {
        var rects = store[pos][1];
 
        for ( var i = 0; i < rects.length; i++ ) {
          // will simply "draw" semi-transparent red divs where
          // repaints where recorded
          var rect = rects[i];
          var div = document.createElement("div");
 
          with (div.style) {
            background = "red";
            opacity = "0.1";
            position = "absolute";
            top = rect.top + "px";
            left = rect.left + "px";
            width = (rect.right - rect.left) + "px";
            height = (rect.bottom - rect.top) + "px";
          }
 
          document.body.appendChild( div );
        }
      }
 
      document.body.removeEventListener("click", onClick, false);
    }, false);
  }
})();

This example is for measuring how long something took to paint to the user.

// Suppose we want to measure how long it takes to paint the
// next frame after a click event is fired on element "target".

let winUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
                     .getInterface(Ci.nsIDOMWindowUtils);

// The last transaction id is the last id that was sent to the
// compositor before our script started to execute.
let lastTransactionId = winUtils.lastTransactionId;
let start = window.performance.now();

// Set up our MozAfterPaint listener, but we only care about
// MozAfterPaint events where the transaction id is GREATER
// than lastTransactionId. This is to account for the possibility
// that a composite is underway at the time this script is running.
addEventListener("MozAfterPaint", function onPaint(event) {
  if (event.transactionId > lastTransactionId) {
    // Since the transaction id is greater than the last transaction
    // id, that means we're safe to assume that whatever effect that
    // clicking on the "target" element was supposed to have, if the
    // change should have been instantaneous, then it has been presented
    // to the user.
    let finish = window.performance.now();
    alert(`Time to present: ${finish - start}ms`);
    removeEventListener("MozAfterPaint", onPaint);
  }
});

document.getElementById("target").click();

 

Document Tags and Contributors

Tags: 
 Last updated by: mconley,