RESOLVED FIXED 108205
Text Autosizing: prevent oscillation of font sizes during autosizing
https://bugs.webkit.org/show_bug.cgi?id=108205
Summary Text Autosizing: prevent oscillation of font sizes during autosizing
timvolodine
Reported 2013-01-29 10:46:08 PST
Text Autosizing: prevent oscillation of font sizes during autosizing
Attachments
Patch (12.56 KB, patch)
2013-01-29 10:54 PST, timvolodine
no flags
Patch (14.82 KB, patch)
2013-02-13 10:44 PST, timvolodine
no flags
Patch (20.69 KB, patch)
2013-02-15 10:01 PST, timvolodine
no flags
Patch (21.48 KB, patch)
2013-02-15 12:01 PST, timvolodine
no flags
Patch (20.47 KB, patch)
2013-02-18 09:21 PST, timvolodine
no flags
timvolodine
Comment 1 2013-01-29 10:54:16 PST
timvolodine
Comment 2 2013-01-29 11:03:32 PST
will probably need to add an extra test for orientation-change (i.e. size change)
timvolodine
Comment 3 2013-02-13 10:44:21 PST
timvolodine
Comment 4 2013-02-15 10:01:30 PST
timvolodine
Comment 5 2013-02-15 10:05:09 PST
in the last patch we decided to go with a simpler approach than in the previous two patches. Empirical tests suggest the approach of fixing the autosizing multiplier once it is set works sufficiently well for most webpages and in addition to preventing oscillation also results in faster layout times.
John Mellor
Comment 6 2013-02-15 11:10:42 PST
Comment on attachment 188593 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=188593&action=review Added some comments. > Source/WebCore/ChangeLog:8 > + On some websites font sizes appear to oscillate or grow very large due to You can delete "appear to", since they really do oscillate. And it's not really a case of them growing very large, it's more that on some sites it takes many layouts before the font sizes settle to a steady size. > Source/WebCore/page/Settings.cpp:369 > + // TODO: I wonder if this needs to traverse frames like in WebViewImpl::resize, or whether there is only one document per Settings instance? s/TODO/FIXME/ according to WebKit style. > Source/WebCore/rendering/TextAutosizer.cpp:99 > + setMultiplier(renderer, 1); Please add a check for renderer->style()->textAutosizingMultiplier() != 1, as setting the multiplier unnecessarily is quite expensive. > Source/WebCore/rendering/TextAutosizer.cpp:199 > + if (localMultiplier != descendant->style()->textAutosizingMultiplier() Nit: might be simpler/cheaper to write: localMultiplier != 1 && descendant->style()->textAutosizingMultiplier() == 1 > LayoutTests/ChangeLog:8 > + Changed old tests to layout without the scrollbar initially, because the A little unclear. Perhaps change this sentence to "Added overflow-y:hidden to some existing tests, since previously those tests would start off with incorrect multipliers (because mainFrame->view()->layoutSize() is initially 785 instead of 800 as ScrollView wrongly guesses a scrollbar will be needed), and then the multipliers would get corrected on a subsequent layout. Now that we don't allow the multiplier to change after being set, it needs to be right first time." > LayoutTests/fast/text-autosizing/oscillation-javascript-fontsize-change.html:42 > +if(element.offsetHeight){ Nit: space before ( and { please (ditto below). > LayoutTests/fast/text-autosizing/oscillation-javascript-fontsize-change.html:43 > + // force layout (to computation of offsetHeight triggers reflow) Nit: s/to // > LayoutTests/fast/text-autosizing/oscillation-javascript-fontsize-change.html:45 > +element.className='largersize'; Nit: spaces around = please (ditto below).
timvolodine
Comment 7 2013-02-15 11:46:09 PST
Comment on attachment 188593 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=188593&action=review >> Source/WebCore/ChangeLog:8 >> + On some websites font sizes appear to oscillate or grow very large due to > > You can delete "appear to", since they really do oscillate. > And it's not really a case of them growing very large, it's more that on some sites it takes many layouts before the font sizes settle to a steady size. On some websites autosized font-sizes oscillate due to layouts caused by hovering or incremental page loading (and on other sites font sizes do eventually stabilize, but it takes many layouts before they reach a steady size). To prevent all these cases, we no longer allow the autosizing multiplier to change after it has been set (to a value other than 1). This won't always give exactly the same results, but testing on 2000 top sites shows that this makes little difference in practice, and it prevents these very jarring cases. As a happy side-effect, this speeds up layouts as font sizes change less. >> Source/WebCore/page/Settings.cpp:369 >> + // TODO: I wonder if this needs to traverse frames like in WebViewImpl::resize, or whether there is only one document per Settings instance? > > s/TODO/FIXME/ according to WebKit style. done >> Source/WebCore/rendering/TextAutosizer.cpp:99 >> + setMultiplier(renderer, 1); > > Please add a check for renderer->style()->textAutosizingMultiplier() != 1, as setting the multiplier unnecessarily is quite expensive. done >> Source/WebCore/rendering/TextAutosizer.cpp:199 >> + if (localMultiplier != descendant->style()->textAutosizingMultiplier() > > Nit: might be simpler/cheaper to write: localMultiplier != 1 && descendant->style()->textAutosizingMultiplier() == 1 done >> LayoutTests/ChangeLog:8 >> + Changed old tests to layout without the scrollbar initially, because the > > A little unclear. Perhaps change this sentence to "Added overflow-y:hidden to some existing tests, since previously those tests would start off with incorrect multipliers (because mainFrame->view()->layoutSize() is initially 785 instead of 800 as ScrollView wrongly guesses a scrollbar will be needed), and then the multipliers would get corrected on a subsequent layout. Now that we don't allow the multiplier to change after being set, it needs to be right first time." done >> LayoutTests/fast/text-autosizing/oscillation-javascript-fontsize-change.html:42 >> +if(element.offsetHeight){ > > Nit: space before ( and { please (ditto below). done >> LayoutTests/fast/text-autosizing/oscillation-javascript-fontsize-change.html:43 >> + // force layout (to computation of offsetHeight triggers reflow) > > Nit: s/to // done >> LayoutTests/fast/text-autosizing/oscillation-javascript-fontsize-change.html:45 >> +element.className='largersize'; > > Nit: spaces around = please (ditto below). done
timvolodine
Comment 8 2013-02-15 12:01:41 PST
John Mellor
Comment 9 2013-02-15 12:07:22 PST
Latest patch looks good to me. Kenneth/Julien, WDYT?
Kenneth Rohde Christiansen
Comment 10 2013-02-18 06:36:19 PST
Comment on attachment 188611 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=188611&action=review > Source/WebCore/ChangeLog:12 > + eventually stabilize, but it takes many layouts before they reach a steady > + size). To prevent all these cases, we no longer allow the autosizing > + multiplier to change after it has been set (to a value other than 1). Sounds like an OK idea. > Source/WebCore/ChangeLog:17 > + This won't always give exactly the same results, but testing on 2000 top > + sites shows that this makes little difference in practice, and it prevents > + these very jarring cases. As a happy side-effect, this speeds up layouts > + as font sizes change less. What happens if you load the page in portrait, then rotate. in comparison with loading it in landscape and rotating to portrait? You get the same result?
John Mellor
Comment 11 2013-02-18 07:28:09 PST
Comment on attachment 188611 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=188611&action=review >> Source/WebCore/ChangeLog:17 >> + as font sizes change less. > > What happens if you load the page in portrait, then rotate. in comparison with loading it in landscape and rotating to portrait? You get the same result? Very perceptive - this was in fact a bug with an initial implementation of this :) That's why the patch adds some code to chromium's WebViewImpl::resize which calls recalculateMultipliers if the page changes width, hence reseting the multiplier lock. However it would probably be better if this was done automatically from WebCore. Can you think of a good place to hook this in? Perhaps in FrameView::contentsResized?
Kenneth Rohde Christiansen
Comment 12 2013-02-18 07:30:16 PST
> Very perceptive - this was in fact a bug with an initial implementation of this :) > That's why the patch adds some code to chromium's WebViewImpl::resize which calls recalculateMultipliers if the page changes width, hence reseting the multiplier lock. > However it would probably be better if this was done automatically from WebCore. Can you think of a good place to hook this in? Perhaps in FrameView::contentsResized? Yeah, I suppose FrameView would be the right place. I guess that way is should also be easier to test for all ports.
timvolodine
Comment 13 2013-02-18 09:21:20 PST
Kenneth Rohde Christiansen
Comment 14 2013-02-18 09:31:57 PST
Comment on attachment 188903 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=188903&action=review > Source/WebCore/page/FrameView.cpp:450 > +#if ENABLE(TEXT_AUTOSIZING) > + // Autosized font sizes depend on the width of the viewing area. > + if (newRect.width() != oldRect.width()) { > + Page* page = m_frame ? m_frame->page() : 0; I think we need to do this elsewhere. Some ports don't use the FrameView as the viewport. There much be another place where you can get the content size changes.
John Mellor
Comment 15 2013-02-18 11:21:00 PST
Comment on attachment 188903 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=188903&action=review >> Source/WebCore/page/FrameView.cpp:450 >> + Page* page = m_frame ? m_frame->page() : 0; > > I think we need to do this elsewhere. Some ports don't use the FrameView as the viewport. There much be another place where you can get the content size changes. This isn't looking for content size changes, it's looking for changes in the browser window width (such as when a device is rotated from portrait to landscape). I think we agreed on IRC that specifically the unscaledVisibleContentSize of the main frame's FrameView is the width we want to watch, and indeed that matches how TextAutosizer::processSubtree calculates the windowSize. Now ScrollView::unscaledVisibleContentSize is somewhat complicated, but it seems it primarily derives its value from Widget::width() and Widget::height(), which should always get changed via FrameView::setFrameRect. So perhaps this is a good place to detect width changes after all?
Kenneth Rohde Christiansen
Comment 16 2013-02-18 11:26:56 PST
I am adding Dongsung as he was looking into fixed layout and setViewportSize.
Dongseong Hwang
Comment 17 2013-02-18 22:30:52 PST
(In reply to comment #15) > (From update of attachment 188903 [details]) > View in context: https://bugs.webkit.org/attachment.cgi?id=188903&action=review > > >> Source/WebCore/page/FrameView.cpp:450 > >> + Page* page = m_frame ? m_frame->page() : 0; > > > > I think we need to do this elsewhere. Some ports don't use the FrameView as the viewport. There much be another place where you can get the content size changes. > > This isn't looking for content size changes, it's looking for changes in the browser window width (such as when a device is rotated from portrait to landscape). I think we agreed on IRC that specifically the unscaledVisibleContentSize of the main frame's FrameView is the width we want to watch, and indeed that matches how TextAutosizer::processSubtree calculates the windowSize. > > Now ScrollView::unscaledVisibleContentSize is somewhat complicated, but it seems it primarily derives its value from Widget::width() and Widget::height(), which should always get changed via FrameView::setFrameRect. So perhaps this is a good place to detect width changes after all? Hi! great work! and I have some questions. Do we need to call TextAutosizer::recalculateMultipliers() when viewport is changed? If so, I think we need to check when page scale is changed as well as FrameView::setFrameRect. I think the unscaledVisibleContentSize of the main frame's FrameView is nothing. the visibleContentRect of the main frame view said what is viewport rect in contents unit. the unscaledVisibleContentSize is just helper function for compositor. never mind unscaledVisibleContentSize. There is two factors that affect the visibleContentRect: frameRect and pageScale. Chromium compositor will stretch the visibleContentRect in contents unit to the device viewport in window (may multiply pageScale x deviceScale). EFL and Qt will follow Chromium's way. I'm not familiar to font autosize, so I'm not sure whether we should call TextAutosizer::recalculateMultipliers() when page scale is changed. WDYT? :)
timvolodine
Comment 18 2013-02-19 02:43:50 PST
from the http://trac.webkit.org/wiki/ScalesAndZooms: "Page scale is used to implement zooming gestures. [...] Page scale affects the virtual viewport, not the CSS viewport, so layout is, generally speaking, not impacted by a changing page scale." so if the layout is not impacted then we should not recompute autosizing when page scale changes.
Dongseong Hwang
Comment 19 2013-02-19 03:16:34 PST
(In reply to comment #18) > from the http://trac.webkit.org/wiki/ScalesAndZooms: > "Page scale is used to implement zooming gestures. [...] > Page scale affects the virtual viewport, not the CSS viewport, so layout is, generally speaking, not impacted by a changing page scale." > > so if the layout is not impacted then we should not recompute autosizing when page scale changes. ah, thank you for explanation. Text Autosizing is related to layout rather than virtual viewport. I agree with you that FrameView::setFrameRect is relevant position. Frankly, I don't fully understand what is Text Autosizing and css viewport yet. :)
Kenneth Rohde Christiansen
Comment 20 2013-02-19 06:27:25 PST
> Do we need to call TextAutosizer::recalculateMultipliers() when viewport is changed? It needs to be recalcuted whenever the visible native viewport changes width, disregarding scaling. > ah, thank you for explanation. Text Autosizing is related to layout rather than virtual viewport. I agree with you that FrameView::setFrameRect is relevant position. Frankly, I don't fully understand what is Text Autosizing and css viewport yet. :) Not really, it is related to what we call the initial layout viewport (according to the css device adaptation spec). which means the visible viewport at css scale 1.0. If this changes, like as a result of an orientation change, then it needs to be recalculated.
John Mellor
Comment 21 2013-02-19 06:54:13 PST
(In reply to comment #20) Or to put it another way, Text Autosizing depends on the physical width of the device, measured in density-independent pixels (DIP). For example a portrait iPhone is 320 DIP wide (whether it's high dpi or low dpi), and a landscape iPhone 4 is 480 DIP wide. This is irrespective of the page's meta viewport tag or zoom level. When the device gets rotated from portrait to landscape, and the window width switches from e.g. 320 DIP to 480 DIP, we need to call recalculateMultipliers. TextAutosizer::processSubtree currently accesses this DIP window width using page->mainFrame()->view()->unscaledVisibleContentSize(true). That definitely works on chromium (both android and linux); if it turns out that doesn't work on other ports which do tiling outside of WebKit then we should change that, but it would probably be cleaner to do so as a separate bug/patch?
Kenneth Rohde Christiansen
Comment 22 2013-02-19 10:27:46 PST
Comment on attachment 188903 [details] Patch I am ok with this, but it would be great if we could actually test by modifying the viewport. That can be done from testing already I believe, but we need to make sure it uses fixed layout for EFL.
WebKit Review Bot
Comment 23 2013-02-19 10:59:14 PST
Comment on attachment 188903 [details] Patch Clearing flags on attachment: 188903 Committed r143356: <http://trac.webkit.org/changeset/143356>
WebKit Review Bot
Comment 24 2013-02-19 10:59:20 PST
All reviewed patches have been landed. Closing bug.
Dongseong Hwang
Comment 25 2013-02-19 18:25:54 PST
(In reply to comment #21) > (In reply to comment #20) > TextAutosizer::processSubtree currently accesses this DIP window width using page->mainFrame()->view()->unscaledVisibleContentSize(true). That definitely works on chromium (both android and linux); if it turns out that doesn't work on other ports which do tiling outside of WebKit then we should change that, but it would probably be cleaner to do so as a separate bug/patch? I understand now page->mainFrame()->view()->unscaledVisibleContentSize(true) returns css viewport, that http://trac.webkit.org/wiki/ScalesAndZooms and http://dev.w3.org/csswg/css-device-adapt/ said. kenneth, when we use fixedLayoutSize, is css viewport determined by fixedLayoutSize instead of frameRect? If so, I think we need to call TextAutosizer::recalculateMultipliers() when fixedLayoutSize is changed..
Kenneth Rohde Christiansen
Comment 26 2013-02-24 07:54:11 PST
(In reply to comment #25) > (In reply to comment #21) > > (In reply to comment #20) > > TextAutosizer::processSubtree currently accesses this DIP window width using page->mainFrame()->view()->unscaledVisibleContentSize(true). That definitely works on chromium (both android and linux); if it turns out that doesn't work on other ports which do tiling outside of WebKit then we should change that, but it would probably be cleaner to do so as a separate bug/patch? > > I understand now page->mainFrame()->view()->unscaledVisibleContentSize(true) returns css viewport, that http://trac.webkit.org/wiki/ScalesAndZooms and http://dev.w3.org/csswg/css-device-adapt/ said. CSS viewport at 1.0 scale, yes. > kenneth, when we use fixedLayoutSize, is css viewport determined by fixedLayoutSize instead of frameRect? If so, I think we need to call TextAutosizer::recalculateMultipliers() when fixedLayoutSize is changed.. fixed layout size is not equal to the viewport at 1.0 scale (only when width=device-width). Often the content is laid out so that it does not fit into the viewport, given fallback values (say 980 for desktop pages) or given values from the viewport meta tag.
Dongseong Hwang
Comment 27 2013-02-24 16:24:59 PST
(In reply to comment #26) > fixed layout size is not equal to the viewport at 1.0 scale (only when width=device-width). Often the content is laid out so that it does not fit into the viewport, given fallback values (say 980 for desktop pages) or given values from the viewport meta tag. aha, Thanks for explanation. fixed layout size and css viewport makes me confused :) In detail, I'm trying to understand which code of WebCore use fixed layout size, css viewport (unscaledVisibleContentSize) or just Widget::frameRect.
Note You need to log in before you can comment on or make changes to this bug.