Open Bug 1280260 Opened 8 years ago Updated 2 years ago

Pause Frame Creation from JS

Categories

(Core :: DOM: Core & HTML, defect, P3)

defect

Tracking

()

People

(Reporter: zbraniecki, Unassigned)

Details

(Whiteboard: [gecko-l20n])

Attachments

(5 files)

We're implementing new client-side l10n layer for Firefox and we would like to be able to avoid the race condition where we're trying to get l10n resources loaded and DOM translated before the layout happens.

We're not the only ones that need this. It's becoming a common problem where Web Apps want to alter the DOM as part of the bootstrap process before layout/firstPaint.

It has been discussed at https://groups.google.com/d/topic/mozilla.dev.platform/F3Mp6dZonMA/discussion with a thumbs-up from :sicking.

:bz said that he could expose an API for us (for chrome only code now) to enable us to do that.
We could start with chrome-only API and later consider standardizing it and exposing to the Web.

:bz - the way I thought about it would be sth like a:

document.pauseFrameCreation();
(...)
document.resumeFrameCreation();

The pausing could also be a <meta> tag maybe?

How would you envision that?
CC'ing :sicking and :mbest (per :sickin's suggestion) because it would tremendously help WebApps.
Flags: needinfo?(bzbarsky)
I don't think we'd ever expose this sort of API to non-chrome code, fwiw.  Not least because it's a best-effort thing: if someone flushes layout we _will_ construct frames.

I would probably call it document.blockLayoutStart()/document.unblockLayoutStart.  It would twiddle the mPendingSheetCount (needs renaming) in nsContentSink.cpp, for the HTML case.

And for XULDocument case we'd need to do something slightly different, possibly; it uses a different mechanism to determine when to start layout.
Flags: needinfo?(bzbarsky)
I would suggest talking to the layout team.
:bz - would you be able to take this bug?
Flags: needinfo?(bzbarsky)
So I have this partially written up, but it's ending up fairly complicated (especially with XUL involved) and quite fragile.  For example, any <script> following yours that touches a layout property will cause FOUC.

So for purposes of l20n, here's a question: can you just use sync XHR and take advantage of the parser-blocking <script> already does?
Flags: needinfo?(bzbarsky) → needinfo?(gandalf)
Flags: needinfo?(gandalf) → needinfo?(stas)
I'd like to keep our options open and experiment with something like document.blockLayoutStart() to see how it affects our performance.

At the same time, FOUCs haven't come up as a significant performance problem in our work so far.  If they do, I'll be happy to first try sync IO as :bz suggests in comment 5.

Let's keep this bug open.  I'll report in a few weeks with more data on async v. sync IO.
Flags: needinfo?(stas)
Boris said he could try to get us a POC soon :)
Flags: needinfo?(bzbarsky)
Assignee: nobody → bzbarsky
Status: NEW → ASSIGNED
Flags: needinfo?(bzbarsky)
Note that this is not the way I'd structure stuff for real (e.g. the layout blocker count should probably just move to the document), but it's enough to at least try this out.  Again, no XUL document support so far.
Assignee: bzbarsky → nobody
Status: ASSIGNED → NEW
Priority: -- → P3
I was able to apply the patch and use it for my testing.

I can confirm that it does prevent painting until blocker is removed, but it doesn't seem to prevent MozAfterPaint's.

I tried to set a document without any JS except of:

===
   <script type="application/javascript">
   document.addLayoutStartBlocker();

   setTimeout(() => {
     document.removeLayoutStartBlocker();
     window.docReady = 1;
   }, 1000);

  function logFirstPaint() {
    performance.mark('moz-after-paint');
    let measures = performance.getEntriesByName('moz-after-paint');
    let measure = measures[measures.length - 1];
    if (window.docReady) {
      window.removeEventListener('MozAfterPaint', logFirstPaint, true);
      console.log('Paint: ' + measure.startTime + ' (after docReady)');
    } else {
      console.log('Paint: ' + measure.startTime + ' (before docReady)');
    }
  }
  window.addEventListener("MozAfterPaint", logFirstPaint, true);
   </script>

===

and when I launched it in a tab (via chrome://) it returns 20 paint logs, 19 of which happen before the `docReady = 1`.

is that expected?
Probably: painting happens from the root of the document tree, so even documents with nothing to paint yet (like this one) would get painted...
(In reply to Boris Zbarsky [:bz] from comment #15)
> Probably: painting happens from the root of the document tree, so even
> documents with nothing to paint yet (like this one) would get painted...

so, how can I measure when the engine starts layout/painting of the document?

I see MozAfterPaint firing before DOMContentLoaded, then between DOMContentLoaded and window.onload, and then again. How do I know which one of the three is the real painting of HTML?

The one that I have to finish any DOM modifications before if I want to prevent FOUC?
Flags: needinfo?(bzbarsky)
> so, how can I measure when the engine starts layout/painting of the document?

I don't know offhand.

> How do I know which one of the three is the real painting of HTML?

They all could be, depending on what the DOM and layout trees look like...

> The one that I have to finish any DOM modifications before if I want to prevent FOUC?

FOUC will happen if a paint happens before your modifications are done _and_ layout is being done on whatever is already there, no?  The whole point of blocking layout is to prevent that second condition from being true.
Flags: needinfo?(bzbarsky)
> FOUC will happen if a paint happens before your modifications are done _and_ layout is being done on whatever is already there, no?  The whole point of blocking layout is to prevent that second condition from being true.

I thought that blocking layout will prevent paints so I was trying to check if your patch impacts the first paint time to be after l20n is done (so, DOM is ready).

If blocking layout doesn't prevent paints, then how can we test the impact of our changes on the performance?

The ts_paint tests the painting, and if painting is done irrelevant of layout, then we can block layout for a second and the paint won't be affected, am I correct?
Flags: needinfo?(bzbarsky)
> I thought that blocking layout will prevent paints

Well, it prevents there being anything there to paint, which is not quite the same thing.

> then how can we test the impact of our changes on the performance?

I'm not sure, honestly.

> then we can block layout for a second and the paint won't be affected, am I correct?

I don't know enough about what ts_paint is measuring to answer that question, sorry.
Flags: needinfo?(bzbarsky)
Mass change dependency tree for bug 1279002 into a whiteboard keyword.
No longer blocks: gecko-l20n
Whiteboard: [gecko-l20n]
Component: DOM → DOM: Core & HTML
Severity: normal → S3
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: