Text Autosizing: prevent oscillation of font sizes during autosizing
https://bugs.webkit.org/show_bug.cgi?id=108205
Patch by Tim Volodine <timvolodine@chromium.org> on 2013-02-19
Reviewed by Kenneth Rohde Christiansen.
Source/WebCore:
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.
Test: fast/text-autosizing/oscillation-javascript-fontsize-change.html
* page/FrameView.cpp:
(WebCore::FrameView::setFrameRect):
* page/Settings.cpp:
(WebCore::Settings::setTextAutosizingFontScaleFactor):
* rendering/TextAutosizer.cpp:
(WebCore::TextAutosizer::recalculateMultipliers):
(WebCore):
(WebCore::TextAutosizer::processContainer):
* rendering/TextAutosizer.h:
(TextAutosizer):
LayoutTests:
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.
Also added specific oscillation test triggered by javascript.
* fast/text-autosizing/constrained-height-body-expected.html:
* fast/text-autosizing/constrained-height-body.html:
* fast/text-autosizing/constrained-then-float-ancestors-expected.html:
* fast/text-autosizing/constrained-then-float-ancestors.html:
* fast/text-autosizing/constrained-then-position-fixed-ancestors-expected.html:
* fast/text-autosizing/constrained-then-position-fixed-ancestors.html:
* fast/text-autosizing/nested-em-line-height-expected.html:
* fast/text-autosizing/nested-em-line-height.html:
* fast/text-autosizing/oscillation-javascript-fontsize-change-expected.html: Added.
* fast/text-autosizing/oscillation-javascript-fontsize-change.html: Added.
* fast/text-autosizing/simple-paragraph-expected.html:
* fast/text-autosizing/simple-paragraph.html:
* fast/text-autosizing/span-child-expected.html:
* fast/text-autosizing/span-child.html:
* fast/text-autosizing/unwrappable-blocks-expected.html:
* fast/text-autosizing/unwrappable-blocks.html:
* fast/text-autosizing/unwrappable-inlines-expected.html:
* fast/text-autosizing/unwrappable-inlines.html:
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@143356
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
Conflicts:
LayoutTests/ChangeLog
Source/WebCore/ChangeLog
<meta name="viewport" content="width=800">
<style>
html { font-size: 16px; }
-body { width: 800px; margin: 0; }
+body { width: 800px; margin: 0; overflow-y: hidden; }
</style>
</head>
<meta name="viewport" content="width=800">
<style>
html { font-size: 16px; }
-body { width: 800px; margin: 0; }
+body { width: 800px; margin: 0; overflow-y: hidden; }
</style>
<script>
<meta name="viewport" content="width=800">
<style>
html { font-size: 16px; }
-body { width: 800px; margin: 0; }
+body { width: 800px; margin: 0; overflow-y: hidden; }
</style>
</head>
<meta name="viewport" content="width=800">
<style>
html { font-size: 16px; }
-body { width: 800px; margin: 0; }
+body { width: 800px; margin: 0; overflow-y: hidden; }
</style>
<script>
<meta name="viewport" content="width=800">
<style>
html { font-size: 16px; }
-body { width: 800px; margin: 0; }
+body { width: 800px; margin: 0; overflow-y: hidden; }
</style>
</head>
<meta name="viewport" content="width=800">
<style>
html { font-size: 16px; }
-body { width: 800px; margin: 0; }
+body { width: 800px; margin: 0; overflow-y: hidden; }
</style>
<script>
<meta name="viewport" content="width=800">
<style>
html { font-size: 16px; }
-body { width: 800px; margin: 0; }
+body { width: 800px; margin: 0; overflow-y: hidden; }
</style>
</head>
<meta name="viewport" content="width=800">
<style>
html { font-size: 16px; }
-body { width: 800px; margin: 0; }
+body { width: 800px; margin: 0; overflow-y: hidden; }
</style>
<script>
--- /dev/null
+<!DOCTYPE html>
+<html style="font-size: 16px">
+<head>
+
+<meta name="viewport" content="width=800">
+<style>
+ body {
+ width: 800px;
+ margin: 0;
+ overflow-y: hidden;
+ }
+</style>
+
+</head>
+<body>
+
+<div style="top:50px;position:absolute;font-size:19.8px">
+ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .<br/>
+ This paragraph should be autosized to 19.8px<br/>
+ because it contains line breaks.<br/>
+ This test is intended to check<br/>
+ that there are no oscillations due to javascript<br/>
+ briefly increasing the font size of a<br/>
+ small paragraph below.
+</div>
+<div id="sizechanging">
+ This text changes size using javascript below.
+</div>
+
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html>
+<html style="font-size: 16px">
+<head>
+
+<meta name="viewport" content="width=800">
+<style>
+ body {
+ width: 800px;
+ margin: 0;
+ overflow-y: hidden;
+ }
+ .largersize{font-size: 1.1em}
+</style>
+
+<script>
+if (window.internals) {
+ window.internals.settings.setTextAutosizingEnabled(true);
+ window.internals.settings.setTextAutosizingWindowSizeOverride(320, 480);
+} else if (window.console && console.warn) {
+ console.warn("This test depends on the Text Autosizing setting being true, so run it in DumpRenderTree, or manually enable Text Autosizing, and either use a mobile device with 320px device-width (like Nexus S or iPhone), or define HACK_FORCE_TEXT_AUTOSIZING_ON_DESKTOP.");
+}
+</script>
+
+</head>
+<body>
+
+<div style="top:50px;position:absolute;">
+ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .<br/>
+ This paragraph should be autosized to 19.8px<br/>
+ because it contains line breaks.<br/>
+ This test is intended to check<br/>
+ that there are no oscillations due to javascript<br/>
+ briefly increasing the font size of a<br/>
+ small paragraph below.
+</div>
+<div id="sizechanging">
+ This text changes size using javascript below.
+</div>
+
+<script>
+element = document.getElementById("sizechanging");
+if (element.offsetHeight) {
+ // force layout (computation of offsetHeight triggers reflow)
+}
+element.className = 'largersize';
+if (element.offsetHeight) {}
+element.className = '';
+if (element.offsetHeight) {}
+</script>
+<noscript>fail (no support for javascript)</noscript>
+
+</body>
+</html>
<meta name="viewport" content="width=800">
<style>
html { font-size: 16px; }
-body { width: 800px; margin: 0; }
+body { width: 800px; margin: 0; overflow-y: hidden; }
</style>
</head>
<meta name="viewport" content="width=800">
<style>
html { font-size: 16px; }
-body { width: 800px; margin: 0; }
+body { width: 800px; margin: 0; overflow-y: hidden; }
</style>
<script>
<meta name="viewport" content="width=800">
<style>
html { font-size: 16px; }
-body { width: 800px; margin: 0; }
+body { width: 800px; margin: 0; overflow-y: hidden; }
</style>
</head>
<meta name="viewport" content="width=800">
<style>
html { font-size: 16px; }
-body { width: 800px; margin: 0; }
+body { width: 800px; margin: 0; overflow-y: hidden; }
</style>
<script>
<meta name="viewport" content="width=800">
<style>
html { font-size: 16px; }
-body { width: 800px; margin: 0; }
+body { width: 800px; margin: 0; overflow-y: hidden; }
pre { margin: 0; }
</style>
<meta name="viewport" content="width=800">
<style>
html { font-size: 16px; }
-body { width: 800px; margin: 0; }
+body { width: 800px; margin: 0; overflow-y: hidden; }
pre { margin: 0; }
</style>
<meta name="viewport" content="width=800">
<style>
html { font-size: 16px; }
-body { width: 800px; margin: 0; }
+body { width: 800px; margin: 0; overflow-y: hidden; }
pre { margin: 0; }
</style>
<meta name="viewport" content="width=800">
<style>
html { font-size: 16px; }
-body { width: 800px; margin: 0; }
+body { width: 800px; margin: 0; overflow-y: hidden; }
pre { margin: 0; }
</style>
if (newRect == oldRect)
return;
+#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;
+ if (page && page->mainFrame() == m_frame && page->settings()->textAutosizingEnabled()) {
+ for (Frame* frame = page->mainFrame(); frame; frame = frame->tree()->traverseNext())
+ m_frame->document()->textAutosizer()->recalculateMultipliers();
+ }
+ }
+#endif
+
ScrollView::setFrameRect(newRect);
updateScrollableAreaSet();
#include "PageCache.h"
#include "ResourceHandle.h"
#include "StorageMap.h"
+#include "TextAutosizer.h"
#include <limits>
using namespace std;
void Settings::setTextAutosizingFontScaleFactor(float fontScaleFactor)
{
m_textAutosizingFontScaleFactor = fontScaleFactor;
+
+ // FIXME: I wonder if this needs to traverse frames like in WebViewImpl::resize, or whether there is only one document per Settings instance?
+ for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree()->traverseNext())
+ frame->document()->textAutosizer()->recalculateMultipliers();
+
m_page->setNeedsRecalcStyleInAllFrames();
}
{
}
+void TextAutosizer::recalculateMultipliers()
+{
+ RenderObject* renderer = m_document->renderer();
+ while (renderer) {
+ if (renderer->style() && renderer->style()->textAutosizingMultiplier() != 1)
+ setMultiplier(renderer, 1);
+ renderer = renderer->nextInPreOrder();
+ }
+}
+
bool TextAutosizer::processSubtree(RenderObject* layoutRoot)
{
// FIXME: Text Autosizing should only be enabled when m_document->page()->mainFrame()->view()->useFixedLayout()
RenderObject* descendant = nextInPreOrderSkippingDescendantsOfContainers(subtreeRoot, subtreeRoot);
while (descendant) {
if (descendant->isText()) {
- if (localMultiplier != descendant->style()->textAutosizingMultiplier()) {
+ if (localMultiplier != 1 && descendant->style()->textAutosizingMultiplier() == 1) {
setMultiplier(descendant, localMultiplier);
setMultiplier(descendant->parent(), localMultiplier); // Parent does line spacing.
}
virtual ~TextAutosizer();
bool processSubtree(RenderObject* layoutRoot);
+ void recalculateMultipliers();
static float computeAutosizedFontSize(float specifiedSize, float multiplier);