Proposal: Display Locking


#1

Github repo is here. Explainer is below:

Display Locking

This is a short proposal for Display Locking. For more details, see the detailed explainer.

Problem

Websites frequently rely on dynamic content to present information. This means that DOM is often manipulated using script to present rich and dynamic content. There are cases when this can cause slow rendering phase updates, including script to update views, style updates, layout, and paint. This, in turn, causes jank or noticeable delay when presenting content, because rendering phase is updated synchronously with user interactions and requestAnimationFrame script.

The following are a few of the common patterns that can cause jank:

  • Resizing multi-pane UI with complex layout within each pane (e.g. IDEs)
  • Adding widgets with complex DOM.
  • Dynamically displaying content based on the current value of text input.
  • Measuring layout of otherwise hidden DOM with intent of sizing containers.

Developers are aware of these issues and build systems to avoid adding complex DOM at once in order to prevent jank. As an example, ReactJS is adding the ability to do async operations to help with this problem.

Proposal

This document proposes to augment user-agent APIs to help address the problem of slow rendering phase updates. Specifically, we propose a concept of display locking an element.

If an element is locked for display, it means that any DOM updates to its subtree are not rendered immediately. Instead, when the lock is committed, the user-agent processed rendering updates co-operatively, yielding periodically to allow script or user interactions to happen.

The visual content of an element which is locked for display does not change. One option is that the visual output is fixed to whatever content was present at the time the lock was acquired, and remains this way until the lock is released and all associated promises are resolved. Another possiblity is effectively making the locked subtree visibility: hidden, meaning that we don’t display any content while work is completed.

There are other possible options here, such as displaying developer specified content. The details of this are up for discussion, but the consistent result is that the visual representation of the modified DOM is not necessarily rendered to screen, affording the user-agent the opportunity to break the work up into smaller pieces.

Example

async function updateDom() {
  let element = document.getElementById("container");

  // Get and acquire a lock, causing the element to be locked.
  let lock = element.getDisplayLock();
  await lock.acquire();

  // Modify element's subtree. The DOM is modified immediately, same as without
  // display locking. However, rendering of the element is delayed.
  element.innerHTML = ...;

  // Commit the lock. This returns a promise. The user-agent starts to
  // co-operatively process steps required to render the modified subtree,
  // including style, layout, and painting. When the user-agent is done
  // with these steps, the promise is resolved.
  lock.commit();
}

In cases where DOM manipulations would cause jank, this instead allows the user-agent to split up the work across several frames, yielding for script and user interactions. When the updates are finished processing, the result is displayed without jank.

Further reading

EDIT: Expanded on the visual content of locked element description.


#2

Hi! I haven’t read the detailed explainer yet, but I like to say that I like the idea!


#3

Just some initial reactions:

  • the naming seems very close to orientation lock, which deals with locking the display (to orientations) - that might cause confusion. Similarly with the wake lock API.
  • how does this play with css will-change (if at all)?

Rick Byers and some other folks had proposed a generic mechanism for “web locks”, parts of which look similar to this. Might help to align with that?


#4

We’ve tried to come up with naming that would be unambiguous, hence display lock, but I can certainly see how that is still confusing relative to other locks that lock the display for whatever reason. It might be worth it for us to include something like “raster” or “visual update” terminology to further disambiguate that.

This change should be pretty agnostic to will-change. That is, it’s not necessary to have a will-change property for this lock to be effective, and its presence also doesn’t interfere with it. contain: paint on the other hand is an important property, since it allows us to reason about the location and visual bounds of the subtree. It also implies a layout containment, which should allow us to move the locked element without incurring any cost for the subtree.

I haven’t heard about “web locks”, but I’ll ask around about Rick Byers’ proposal. I think this proposal is a manifestation of some of ideas that were previously discussed, let’s hope we’re not stepping on toes here.


#5

Thanks for looking into alternative names and the additional info!


#6

This is an interesting, exciting proposal.

I think buffering events sounds like a bad idea because the rest of the page should expect to continue to receive events. On the other hand delivering events sounds like a bad idea because I can presumably multiply-instantiate an element by hopping it into successive subtrees and freezing each one, and SVG use event dispatch was a source of security bugs. Maybe closed-mode Shadow DOM is a useful model here where you continue doing event dispatch but are able to elide the frozen parts?

Some naive questions…

What happens if a locked element enters fullscreen? Or becomes a modal dialog? HTML jumps into unusual, specific layout-like stuff at those points. (“Alignment mode.”)

What happens to screen readers when dynamic properties, like active, and frozen things (presumably content because content depends on style and layout for pseudos and display) interact?

The explainer says:

… style of the locked subtree will not affect style of elements outside of the locked element. Similarly to layout containment, this ensures that we can process any number of elements on the locked subtree without visually changing the content on the rest of the page.

However I think those things are linked, for example, elements which are not displayed are not focusable which will affect things with :focus-within rules outside of the subtree. I guess HTML’s idea of “being rendered” is the branching off point here, and tweaking the meanings of these for the frozen world to avoid paradoxes will be tricky.


#7

What happens if a locked element enters fullscreen? Or becomes a modal dialog? HTML jumps into unusual, specific layout-like stuff at those points

I think a force-unlock should probably happen in this case. See https://github.com/chrishtr/display-locking/issues/20

What happens to screen readers when dynamic properties, like active, and frozen things (presumably content because content depends on style and layout for pseudos and display) interact?

I think this sounds similar to the question of find-in-page/tab order: https://github.com/chrishtr/display-locking/issues/18. I think those should not match content under a lock, but also potentially be used as signals for a timeout after which a stale lock is force-evicted.

things with :focus-within rules outside of the subtree

Per issue 18 (linked above) - focus-within will not change due to the locked content, because it is not focusable.

tweaking the meanings of these for the frozen world to avoid paradoxes will be tricky

The cases discussed above seem to be straightforward to handle. Do you think there will be a lot more?