summaryrefslogtreecommitdiff
path: root/Source/WebCore/rendering/TextAutoSizing.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/rendering/TextAutoSizing.cpp')
-rw-r--r--Source/WebCore/rendering/TextAutoSizing.cpp205
1 files changed, 205 insertions, 0 deletions
diff --git a/Source/WebCore/rendering/TextAutoSizing.cpp b/Source/WebCore/rendering/TextAutoSizing.cpp
new file mode 100644
index 000000000..aaf71cb79
--- /dev/null
+++ b/Source/WebCore/rendering/TextAutoSizing.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "TextAutoSizing.h"
+
+#if ENABLE(TEXT_AUTOSIZING)
+
+#include "CSSFontSelector.h"
+#include "Document.h"
+#include "Logging.h"
+#include "RenderListMarker.h"
+#include "RenderText.h"
+#include "StyleResolver.h"
+
+namespace WebCore {
+
+static RenderStyle cloneRenderStyleWithState(const RenderStyle& currentStyle)
+{
+ auto newStyle = RenderStyle::clone(currentStyle);
+ if (currentStyle.lastChildState())
+ newStyle.setLastChildState();
+ if (currentStyle.firstChildState())
+ newStyle.setFirstChildState();
+ return newStyle;
+}
+
+TextAutoSizingKey::TextAutoSizingKey(DeletedTag)
+{
+ HashTraits<std::unique_ptr<RenderStyle>>::constructDeletedValue(m_style);
+}
+
+TextAutoSizingKey::TextAutoSizingKey(const RenderStyle& style, unsigned hash)
+ : m_style(RenderStyle::clonePtr(style)) // FIXME: This seems very inefficient.
+ , m_hash(hash)
+{
+}
+
+void TextAutoSizingValue::addTextNode(Text& node, float size)
+{
+ node.renderer()->setCandidateComputedTextSize(size);
+ m_autoSizedNodes.add(&node);
+}
+
+static const float maxScaleIncrease = 1.7f;
+
+auto TextAutoSizingValue::adjustTextNodeSizes() -> StillHasNodes
+{
+ // Remove stale nodes. Nodes may have had their renderers detached. We'll also need to remove the style from the documents m_textAutoSizedNodes
+ // collection. Return true indicates we need to do that removal.
+ Vector<Text*> nodesForRemoval;
+ for (auto& textNode : m_autoSizedNodes) {
+ auto* renderer = textNode->renderer();
+ if (!renderer || !renderer->style().textSizeAdjust().isAuto() || !renderer->candidateComputedTextSize())
+ nodesForRemoval.append(textNode.get());
+ }
+
+ for (auto& node : nodesForRemoval)
+ m_autoSizedNodes.remove(node);
+
+ StillHasNodes stillHasNodes = m_autoSizedNodes.isEmpty() ? StillHasNodes::No : StillHasNodes::Yes;
+
+ // If we only have one piece of text with the style on the page don't adjust it's size.
+ if (m_autoSizedNodes.size() <= 1)
+ return stillHasNodes;
+
+ // Compute average size.
+ float cumulativeSize = 0;
+ for (auto& node : m_autoSizedNodes)
+ cumulativeSize += node->renderer()->candidateComputedTextSize();
+
+ float averageSize = std::round(cumulativeSize / m_autoSizedNodes.size());
+
+ // Adjust sizes.
+ bool firstPass = true;
+ for (auto& node : m_autoSizedNodes) {
+ auto& renderer = *node->renderer();
+ if (renderer.style().fontDescription().computedSize() == averageSize)
+ continue;
+
+ float specifiedSize = renderer.style().fontDescription().specifiedSize();
+ float scaleChange = averageSize / specifiedSize;
+ if (scaleChange > maxScaleIncrease && firstPass) {
+ firstPass = false;
+ averageSize = std::round(specifiedSize * maxScaleIncrease);
+ scaleChange = averageSize / specifiedSize;
+ }
+
+ LOG(TextAutosizing, " adjust node size %p firstPass=%d averageSize=%f scaleChange=%f", node.get(), firstPass, averageSize, scaleChange);
+
+ auto* parentRenderer = renderer.parent();
+
+ auto style = cloneRenderStyleWithState(renderer.style());
+ auto fontDescription = style.fontDescription();
+ fontDescription.setComputedSize(averageSize);
+ style.setFontDescription(fontDescription);
+ style.fontCascade().update(&node->document().fontSelector());
+ parentRenderer->setStyle(WTFMove(style));
+
+ if (parentRenderer->isAnonymousBlock())
+ parentRenderer = parentRenderer->parent();
+
+ // If we have a list we should resize ListMarkers separately.
+ if (is<RenderListMarker>(*parentRenderer->firstChild())) {
+ auto& listMarkerRenderer = downcast<RenderListMarker>(*parentRenderer->firstChild());
+ auto style = cloneRenderStyleWithState(listMarkerRenderer.style());
+ style.setFontDescription(fontDescription);
+ style.fontCascade().update(&node->document().fontSelector());
+ listMarkerRenderer.setStyle(WTFMove(style));
+ }
+
+ // Resize the line height of the parent.
+ auto& parentStyle = parentRenderer->style();
+ auto& lineHeightLength = parentStyle.specifiedLineHeight();
+
+ int specifiedLineHeight;
+ if (lineHeightLength.isPercent())
+ specifiedLineHeight = minimumValueForLength(lineHeightLength, fontDescription.specifiedSize());
+ else
+ specifiedLineHeight = lineHeightLength.value();
+
+ int lineHeight = specifiedLineHeight * scaleChange;
+ if (lineHeightLength.isFixed() && lineHeightLength.value() == lineHeight)
+ continue;
+
+ auto newParentStyle = cloneRenderStyleWithState(parentStyle);
+ newParentStyle.setLineHeight(Length(lineHeight, Fixed));
+ newParentStyle.setSpecifiedLineHeight(Length { lineHeightLength });
+ newParentStyle.setFontDescription(fontDescription);
+ newParentStyle.fontCascade().update(&node->document().fontSelector());
+ parentRenderer->setStyle(WTFMove(newParentStyle));
+ }
+
+ return stillHasNodes;
+}
+
+TextAutoSizingValue::~TextAutoSizingValue()
+{
+ reset();
+}
+
+void TextAutoSizingValue::reset()
+{
+ for (auto& node : m_autoSizedNodes) {
+ auto* renderer = node->renderer();
+ if (!renderer)
+ continue;
+
+ auto* parentRenderer = renderer->parent();
+ if (!parentRenderer)
+ continue;
+
+ // Reset the font size back to the original specified size
+ auto fontDescription = renderer->style().fontDescription();
+ float originalSize = fontDescription.specifiedSize();
+ if (fontDescription.computedSize() != originalSize) {
+ fontDescription.setComputedSize(originalSize);
+ auto style = cloneRenderStyleWithState(renderer->style());
+ style.setFontDescription(fontDescription);
+ style.fontCascade().update(&node->document().fontSelector());
+ parentRenderer->setStyle(WTFMove(style));
+ }
+
+ // Reset the line height of the parent.
+ if (parentRenderer->isAnonymousBlock())
+ parentRenderer = parentRenderer->parent();
+
+ auto& parentStyle = parentRenderer->style();
+ auto& originalLineHeight = parentStyle.specifiedLineHeight();
+ if (originalLineHeight == parentStyle.lineHeight())
+ continue;
+
+ auto newParentStyle = cloneRenderStyleWithState(parentStyle);
+ newParentStyle.setLineHeight(Length { originalLineHeight });
+ newParentStyle.setFontDescription(fontDescription);
+ newParentStyle.fontCascade().update(&node->document().fontSelector());
+ parentRenderer->setStyle(WTFMove(newParentStyle));
+ }
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(TEXT_AUTOSIZING)