Closed Bug 983465 Opened 11 years ago Closed 11 years ago

Performance of getBoundingClientRect on ranges containing whitespace nodes with suppressed frames is poor

Categories

(Core :: Layout, defect)

28 Branch
x86
macOS
defect
Not set
normal

Tracking

()

VERIFIED FIXED
mozilla31

People

(Reporter: jjones, Assigned: roc)

Details

Attachments

(3 files)

Attached file boundingrect_text.zip (deleted) —
User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:28.0) Gecko/20100101 Firefox/28.0 (Beta/Release) Build ID: 20140306171728 Steps to reproduce: 1) Unzip attached HTML file 2) Edit file to uncomment the style tag and the correct filter_param code block for the browser you want to test with. 3) Open the browser and make sure that the dev tools console is showing. 4) Load up the file in the browser. 5) 5s after opening a script will run to walk the DOM for a certain set of nodes and get the bounding rects for the nodes. It will report the total time this took when completed. Actual results: Time to complete DOM walk: For FireFox Without Multicolumn: ~200ms With Multicolumn: ~30s For Chrome Without Multicolumn: ~30ms With Multicolumn: ~30ms For IE Without Multicolumn: ~130ms With Multicolumn: ~130ms Expected results: Time for walking the DOM and getting bounding rects should be approximately equivalent. I'd be OK with an order of magnitude but 2+ orders of magnitude is just horrible (might I add embarrassing)?
Severity: normal → major
Based on profile this is all layout/reflow.
Component: DOM: Core & HTML → Layout
(I don't understand why we end up reflowing so many times.)
This is pretty ridiculous. What happens here is that nsRange::GetBoundingClientRect calls CollectClientRects, which calls GetPartialTextRect, which calls GetTextFrameForContent, which calls nsCSSFrameConstructor::EnsureFrameForTextNode. In cases when the textnode had a frame suppressed (i.e. it was whitespace-only) this causes us to create and insert the new frame and then we have to do layout. In the columns case layout is a lot slower than in the non-columns case to do height-balancing (which Chrome doesn't do particularly well, by the way). This issue will only affect people asking for getBoundingClientRect on collapsed textnodes... If I comment out this line of the testcase: bounding_rect = range.getBoundingClientRect(); then I get times like so: For FireFox Without Multicolumn: ~24ms With Multicolumn: ~24ms For Chrome Without Multicolumn: ~21ms With Multicolumn: ~21ms Robert, is there any way we can just avoid creating the frames in this situation?
Severity: major → normal
Status: UNCONFIRMED → NEW
Ever confirmed: true
Flags: needinfo?(roc)
Summary: Performance of getBoundingClientRect in MultiColumn layout is exceedingly slow → Performance of getBoundingClientRect on ranges containing whitespace nodes with suppressed frames is poor
Attached file simple correctness testcase (deleted) —
Chrome deals with this by just being incorrect. Some collapsed-away text nodes (but not all) do not contribute to the list returned by getClientRects (or, presumably, getBoundingClientRect). See this testcase, in which the first box should always be zero-sized. Also, there should be three boxes in each list. Chrome returns only 1 in the second case, which is inexplicable.
Flags: needinfo?(roc)
In fact, even in the Joseph's testcase, Chrome is returning incorrect results for all the collapsed-away text nodes
(It's returning 0,0,0,0 when it should be returning reasonable x/y values.)
Hmm. IE11 also seems to return only 1 rect, if I'm testing right...
The easiest way to fix this case is to have a document-wide flag to disable the collapsed-away text node optimization when we discover we need the text frame for any collapsed-away node.
(In reply to Boris Zbarsky [:bz] from comment #7) > Hmm. IE11 also seems to return only 1 rect, if I'm testing right... In which part of the test? What does IE11 do for the second part of the test?
For the first part of the test, IE has rects.length == 0. For the second part of the test, IE has rects.length == 1, and the rect has x=9, y=271.4, width=0, height=0.
Attached patch fix (deleted) — Splinter Review
Assignee: nobody → roc
Attachment #8391109 - Flags: review?(bzbarsky)
This fix reveals a columns bug with the reporter's testcase. Filed as bug 983581.
Comment on attachment 8391109 [details] [diff] [review] fix > if (frame && frame->GetType() == nsGkAtoms::textFrame) { >+ aContent->OwnerDoc()->FlushPendingNotifications(Flush_Layout); > return static_cast<nsTextFrame*>(frame); frame maybe a dead object here.
Comment on attachment 8391109 [details] [diff] [review] fix Olli is right. We should either flush before doing the GetPrimaryFrame() call in EnsureFrameForTextNode or reget the primary frame after flushing. And we should document the mAlwaysCreateFramesForIgnorableWhitespace member to make it clear that we turn off the optimization globally because if people are poking at whitespace sizes/positions they'll probably keep doing it. Or something like that. r=me with that
Attachment #8391109 - Flags: review?(bzbarsky) → review+
(In reply to Boris Zbarsky [:bz] from comment #14) > Olli is right. We should either flush before doing the GetPrimaryFrame() > call in EnsureFrameForTextNode or reget the primary frame after flushing. Good point. I'll do the latter. > And we should document the mAlwaysCreateFramesForIgnorableWhitespace member > to make it clear that we turn off the optimization globally because if > people are poking at whitespace sizes/positions they'll probably keep doing > it. Or something like that. Sure.
(In reply to Boris Zbarsky [:bz] from comment #3) > This is pretty ridiculous. > > What happens here is that nsRange::GetBoundingClientRect calls > CollectClientRects, which calls GetPartialTextRect, which calls > GetTextFrameForContent, which calls > nsCSSFrameConstructor::EnsureFrameForTextNode. > > In cases when the textnode had a frame suppressed (i.e. it was > whitespace-only) this causes us to create and insert the new frame and then > we have to do layout. In the columns case layout is a lot slower than in > the non-columns case to do height-balancing (which Chrome doesn't do > particularly well, by the way). > > This issue will only affect people asking for getBoundingClientRect on > collapsed textnodes... If I comment out this line of the testcase: > > bounding_rect = range.getBoundingClientRect(); > > then I get times like so: > > For FireFox > Without Multicolumn: ~24ms > With Multicolumn: ~24ms > > For Chrome > Without Multicolumn: ~21ms > With Multicolumn: ~21ms > > Robert, is there any way we can just avoid creating the frames in this > situation? Is there a way to tell if a text nod eis collapsed on the client side?
Not easily. It depends on all sorts of stuff. A prerequisite is it being whitespace-only (and even that doesn't have a nice API to detect it), but various other conditions apply too.
Status: NEW → RESOLVED
Closed: 11 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla31
Verified on Mac OSX 10.8.5 using Firefox 31.0b8 and the following times are achieved: Without Multicolumn: ~88ms With Multicolumn: ~120ms - If I comment out this line of the testcase: bounding_rect = range.getBoundingClientRect(); then I get times like so: Without Multicolumn: ~27ms With Multicolumn: ~27ms
Status: RESOLVED → VERIFIED
Keywords: verifyme
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: