[Text autosizing] Do not nuke the style on dynamicViewportSizeUpdate
authorzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 20 Jul 2019 13:47:11 +0000 (13:47 +0000)
committerzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 20 Jul 2019 13:47:11 +0000 (13:47 +0000)
https://bugs.webkit.org/show_bug.cgi?id=199718
<rdar://problem/53344961>

Reviewed by Simon Fraser.

Source/WebCore:

When the page scale changes (e.g. as the result of shink to fit mode) we need to visit all the text content on the page and check whether they need to be boosted.
Currently we call setNeedsRecalcStyleInAllFrames() to accomplish it. Unfortunatelly setNeedsRecalcStyleInAllFrames destroys all the style information which means that the
subsequent styleResolve() needs to start from scratch.
This patch addresses this issue by directly adjusting the computed style information when text boosting is required and schedules layout accordingly. We also trigger this style adjusting
on a timer so that rapid dynamicViewportSizeUpdate() calls won't trigger redundant layouts.

* css/StyleResolver.cpp:
(WebCore::hasTextChild):
(WebCore::StyleResolver::adjustRenderStyleForTextAutosizing):
(WebCore::hasTextChildren): Deleted.
* css/StyleResolver.h:
* page/FrameView.h:
* page/Page.cpp:
(WebCore::Page::invalidateTextAutoSizeInAllFrames):
* page/Page.h:

Source/WebKit:

* WebProcess/WebPage/WebPage.cpp:
(WebKit::m_textAutoSizingAdjustmentTimer):
(WebKit::WebPage::close):
(WebKit::WebPage::didCommitLoad):
(WebKit::WebPage::textAutoSizingAdjustmentTimerFired):
(WebKit::m_shrinkToFitContentTimer): Deleted.
* WebProcess/WebPage/WebPage.h:
* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::dynamicViewportSizeUpdate):
(WebKit::WebPage::resetIdempotentTextAutosizingIfNeeded):
(WebKit::WebPage::resetTextAutosizing):
(WebKit::WebPage::viewportConfigurationChanged):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@247667 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/css/StyleResolver.cpp
Source/WebCore/css/StyleResolver.h
Source/WebCore/page/FrameView.h
Source/WebCore/page/Page.cpp
Source/WebCore/page/Page.h
Source/WebKit/ChangeLog
Source/WebKit/WebProcess/WebPage/WebPage.cpp
Source/WebKit/WebProcess/WebPage/WebPage.h
Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm

index 1d65850..79069f5 100644 (file)
@@ -1,3 +1,27 @@
+2019-07-20  Zalan Bujtas  <zalan@apple.com>
+
+        [Text autosizing] Do not nuke the style on dynamicViewportSizeUpdate
+        https://bugs.webkit.org/show_bug.cgi?id=199718
+        <rdar://problem/53344961>
+
+        Reviewed by Simon Fraser.
+
+        When the page scale changes (e.g. as the result of shink to fit mode) we need to visit all the text content on the page and check whether they need to be boosted.
+        Currently we call setNeedsRecalcStyleInAllFrames() to accomplish it. Unfortunatelly setNeedsRecalcStyleInAllFrames destroys all the style information which means that the
+        subsequent styleResolve() needs to start from scratch.
+        This patch addresses this issue by directly adjusting the computed style information when text boosting is required and schedules layout accordingly. We also trigger this style adjusting
+        on a timer so that rapid dynamicViewportSizeUpdate() calls won't trigger redundant layouts.
+
+        * css/StyleResolver.cpp:
+        (WebCore::hasTextChild):
+        (WebCore::StyleResolver::adjustRenderStyleForTextAutosizing):
+        (WebCore::hasTextChildren): Deleted.
+        * css/StyleResolver.h:
+        * page/FrameView.h:
+        * page/Page.cpp:
+        (WebCore::Page::invalidateTextAutoSizeInAllFrames):
+        * page/Page.h:
+
 2019-07-20  Saam Barati  <sbarati@apple.com>
 
         [WHLSL] Make enums work
index 46f4f26..b7db7b2 100644 (file)
@@ -871,7 +871,7 @@ static OptionSet<TouchAction> computeEffectiveTouchActions(const RenderStyle& st
 #endif
 
 #if ENABLE(TEXT_AUTOSIZING)
-static bool hasTextChildren(const Element& element)
+static bool hasTextChild(const Element& element)
 {
     for (auto* child = element.firstChild(); child; child = child->nextSibling()) {
         if (is<Text>(child))
@@ -880,17 +880,14 @@ static bool hasTextChildren(const Element& element)
     return false;
 }
 
-void StyleResolver::adjustRenderStyleForTextAutosizing(RenderStyle& style, const Element& element)
+bool StyleResolver::adjustRenderStyleForTextAutosizing(RenderStyle& style, const Element& element)
 {
     if (!settings().textAutosizingEnabled() || !settings().textAutosizingUsesIdempotentMode())
-        return;
+        return false;
 
     AutosizeStatus::updateStatus(style);
-    if (!hasTextChildren(element))
-        return;
-
     if (style.textSizeAdjust().isNone())
-        return;
+        return false;
 
     float initialScale = document().page() ? document().page()->initialScale() : 1;
     auto adjustLineHeightIfNeeded = [&](auto computedFontSize) {
@@ -912,16 +909,20 @@ void StyleResolver::adjustRenderStyleForTextAutosizing(RenderStyle& style, const
     auto specifiedFontSize = fontDescription.specifiedSize();
     bool isCandidate = style.isIdempotentTextAutosizingCandidate();
     if (!isCandidate && WTF::areEssentiallyEqual(initialComputedFontSize, specifiedFontSize))
-        return;
+        return false;
 
     auto adjustedFontSize = AutosizeStatus::idempotentTextSize(fontDescription.specifiedSize(), initialScale);
     if (isCandidate && WTF::areEssentiallyEqual(initialComputedFontSize, adjustedFontSize))
-        return;
+        return false;
+
+    if (!hasTextChild(element))
+        return false;
 
     fontDescription.setComputedSize(isCandidate ? adjustedFontSize : specifiedFontSize);
     style.setFontDescription(WTFMove(fontDescription));
     style.fontCascade().update(&document().fontSelector());
     adjustLineHeightIfNeeded(adjustedFontSize);
+    return true;
 }
 #endif
 
index 8c88da8..d8ac328 100644 (file)
@@ -469,6 +469,8 @@ public:
 
     RefPtr<CSSValue> resolvedVariableValue(CSSPropertyID, const CSSValue&, ApplyCascadedPropertyState&) const;
 
+    bool adjustRenderStyleForTextAutosizing(RenderStyle&, const Element&);
+
 private:
     void cacheBorderAndBackground();
 
@@ -500,8 +502,6 @@ private:
     // the last reference to a style declaration are garbage collected.
     void sweepMatchedPropertiesCache();
 
-    void adjustRenderStyleForTextAutosizing(RenderStyle&, const Element&);
-
     typedef HashMap<unsigned, MatchedPropertiesCacheItem> MatchedPropertiesCache;
     MatchedPropertiesCache m_matchedPropertiesCache;
 
index 3207a4d..487483e 100644 (file)
@@ -397,6 +397,8 @@ public:
     void updateIsVisuallyNonEmpty();
     void updateSignificantRenderedTextMilestoneIfNeeded();
     bool isVisuallyNonEmpty() const { return m_isVisuallyNonEmpty; }
+    WEBCORE_EXPORT bool qualifiesAsVisuallyNonEmpty() const;
+
     WEBCORE_EXPORT void enableAutoSizeMode(bool enable, const IntSize& minSize);
     WEBCORE_EXPORT void setAutoSizeFixedMinimumHeight(int);
     bool isAutoSizeEnabled() const { return m_shouldAutoSize; }
@@ -786,7 +788,6 @@ private:
 
     void markRootOrBodyRendererDirty() const;
 
-    bool qualifiesAsVisuallyNonEmpty() const;
     bool qualifiesAsSignificantRenderedText() const;
     void updateHasReachedSignificantRenderedTextThreshold();
 
index 66445fc..eda7c5f 100644 (file)
@@ -91,6 +91,7 @@
 #include "PointerCaptureController.h"
 #include "PointerLockController.h"
 #include "ProgressTracker.h"
+#include "RenderDescendantIterator.h"
 #include "RenderLayerCompositor.h"
 #include "RenderTheme.h"
 #include "RenderView.h"
@@ -3009,4 +3010,27 @@ void Page::didFinishLoadingImageForElement(HTMLImageElement& element)
     chrome().client().didFinishLoadingImageForElement(element);
 }
 
+#if ENABLE(TEXT_AUTOSIZING)
+void Page::recomputeTextAutoSizingInAllFrames()
+{
+    ASSERT(settings().textAutosizingEnabled() && settings().textAutosizingUsesIdempotentMode());
+    for (auto* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) {
+        if (!frame->document())
+            continue;
+        auto& document = *frame->document();
+        if (!document.renderView() || !document.styleScope().resolverIfExists())
+            continue;
+
+        auto& styleResolver = document.styleScope().resolver();
+        for (auto& renderer : descendantsOfType<RenderElement>(*document.renderView())) {
+            if (auto* element = renderer.element()) {
+                auto needsLayout = styleResolver.adjustRenderStyleForTextAutosizing(renderer.mutableStyle(), *element);
+                if (needsLayout)
+                    renderer.setNeedsLayout();
+            }
+        }
+    }
+}
+#endif
+
 } // namespace WebCore
index 789333a..c440234 100644 (file)
@@ -388,6 +388,7 @@ public:
 #if ENABLE(TEXT_AUTOSIZING)
     float textAutosizingWidth() const { return m_textAutosizingWidth; }
     void setTextAutosizingWidth(float textAutosizingWidth) { m_textAutosizingWidth = textAutosizingWidth; }
+    WEBCORE_EXPORT void recomputeTextAutoSizingInAllFrames();
 #endif
 
     const FloatBoxExtent& fullscreenInsets() const { return m_fullscreenInsets; }
index fec2492..ada6dac 100644 (file)
@@ -1,3 +1,24 @@
+2019-07-20  Zalan Bujtas  <zalan@apple.com>
+
+        [Text autosizing] Do not nuke the style on dynamicViewportSizeUpdate
+        https://bugs.webkit.org/show_bug.cgi?id=199718
+        <rdar://problem/53344961>
+
+        Reviewed by Simon Fraser.
+
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::m_textAutoSizingAdjustmentTimer):
+        (WebKit::WebPage::close):
+        (WebKit::WebPage::didCommitLoad):
+        (WebKit::WebPage::textAutoSizingAdjustmentTimerFired):
+        (WebKit::m_shrinkToFitContentTimer): Deleted.
+        * WebProcess/WebPage/WebPage.h:
+        * WebProcess/WebPage/ios/WebPageIOS.mm:
+        (WebKit::WebPage::dynamicViewportSizeUpdate):
+        (WebKit::WebPage::resetIdempotentTextAutosizingIfNeeded):
+        (WebKit::WebPage::resetTextAutosizing):
+        (WebKit::WebPage::viewportConfigurationChanged):
+
 2019-07-19  Youenn Fablet  <youenn@apple.com>
 
         Remote WebInspector should enable mock capture devices in UIProcess if doing it in WebProcess
index 10944f7..6adc762 100644 (file)
@@ -431,6 +431,9 @@ WebPage::WebPage(PageIdentifier pageID, WebPageCreationParameters&& parameters)
 #if ENABLE(VIEWPORT_RESIZING)
     , m_shrinkToFitContentTimer(*this, &WebPage::shrinkToFitContentTimerFired, 0_s)
 #endif
+#if ENABLE(TEXT_AUTOSIZING)
+    , m_textAutoSizingAdjustmentTimer(*this, &WebPage::textAutoSizingAdjustmentTimerFired)
+#endif
 {
     ASSERT(m_pageID);
 
@@ -1402,6 +1405,10 @@ void WebPage::close()
     m_shrinkToFitContentTimer.stop();
 #endif
 
+#if ENABLE(TEXT_AUTOSIZING)
+    m_textAutoSizingAdjustmentTimer.stop();
+#endif
+
 #if ENABLE(CONTEXT_MENUS)
     m_contextMenuClient = std::make_unique<API::InjectedBundle::PageContextMenuClient>();
 #endif
@@ -5793,6 +5800,10 @@ void WebPage::didCommitLoad(WebFrame* frame)
     m_shrinkToFitContentTimer.stop();
 #endif
 
+#if ENABLE(TEXT_AUTOSIZING)
+    m_textAutoSizingAdjustmentTimer.stop();
+#endif
+
 #if ENABLE(PRIMARY_SNAPSHOTTED_PLUGIN_HEURISTIC)
     resetPrimarySnapshottedPlugIn();
 #endif
@@ -6796,6 +6807,13 @@ void WebPage::didFinishLoadingImageForElement(WebCore::HTMLImageElement&)
 
 #endif
 
+#if ENABLE(TEXT_AUTOSIZING)
+void WebPage::textAutoSizingAdjustmentTimerFired()
+{
+    m_page->recomputeTextAutoSizingInAllFrames();
+}
+#endif
+
 } // namespace WebKit
 
 #undef RELEASE_LOG_IF_ALLOWED
index b15d6b8..95409e9 100644 (file)
@@ -1251,8 +1251,6 @@ private:
     void handleSyntheticClick(WebCore::Node& nodeRespondingToClick, const WebCore::FloatPoint& location, OptionSet<WebKit::WebEvent::Modifier>, WebCore::PointerID = WebCore::mousePointerID);
     void completeSyntheticClick(WebCore::Node& nodeRespondingToClick, const WebCore::FloatPoint& location, OptionSet<WebKit::WebEvent::Modifier>, WebCore::SyntheticClickType, WebCore::PointerID = WebCore::mousePointerID);
     void sendTapHighlightForNodeIfNecessary(uint64_t requestID, WebCore::Node*);
-    void resetTextAutosizing();
-    void resetIdempotentTextAutosizingIfNeeded(double previousInitialScale);
     WebCore::VisiblePosition visiblePositionInFocusedNodeForPoint(const WebCore::Frame&, const WebCore::IntPoint&, bool isInteractingWithFocusedElement);
     RefPtr<WebCore::Range> rangeForGranularityAtPoint(WebCore::Frame&, const WebCore::IntPoint&, uint32_t granularity, bool isInteractingWithFocusedElement);
     void setFocusedFrameBeforeSelectingTextAtLocation(const WebCore::IntPoint&);
@@ -1265,6 +1263,11 @@ private:
     bool applyAutocorrectionInternal(const String& correction, const String& originalText);
     bool shouldIgnoreMetaViewport() const;
 #endif
+#if ENABLE(TEXT_AUTOSIZING)
+    void textAutoSizingAdjustmentTimerFired();
+    void resetTextAutosizing();
+    void resetIdempotentTextAutosizingIfNeeded(double previousInitialScale);
+#endif
 
 #if ENABLE(VIEWPORT_RESIZING)
     void scheduleShrinkToFitContent();
@@ -1956,6 +1959,9 @@ private:
 #if HAVE(VISIBILITY_PROPAGATION_VIEW)
     std::unique_ptr<LayerHostingContext> m_contextForVisibilityPropagation;
 #endif
+#if ENABLE(TEXT_AUTOSIZING)
+    WebCore::Timer m_textAutoSizingAdjustmentTimer;
+#endif
 };
 
 #if !PLATFORM(IOS_FAMILY)
index c566649..cae56ba 100644 (file)
@@ -3108,16 +3108,6 @@ void WebPage::setOverrideViewportArguments(const Optional<WebCore::ViewportArgum
     m_page->setOverrideViewportArguments(arguments);
 }
 
-void WebPage::resetTextAutosizing()
-{
-    for (Frame* frame = &m_page->mainFrame(); frame; frame = frame->tree().traverseNext()) {
-        Document* document = frame->document();
-        if (!document || !document->renderView())
-            continue;
-        document->renderView()->resetTextAutosizing();
-    }
-}
-
 void WebPage::dynamicViewportSizeUpdate(const FloatSize& viewLayoutSize, const WebCore::FloatSize& maximumUnobscuredSize, const FloatRect& targetExposedContentRect, const FloatRect& targetUnobscuredRect, const WebCore::FloatRect& targetUnobscuredRectInScrollViewCoordinates, const WebCore::FloatBoxExtent& targetUnobscuredSafeAreaInsets, double targetScale, int32_t deviceOrientation, DynamicViewportSizeUpdateID dynamicViewportSizeUpdateID)
 {
     SetForScope<bool> dynamicSizeUpdateGuard(m_inDynamicSizeUpdate, true);
@@ -3163,9 +3153,10 @@ void WebPage::dynamicViewportSizeUpdate(const FloatSize& viewLayoutSize, const W
 
     IntSize newLayoutSize = m_viewportConfiguration.layoutSize();
 
+#if ENABLE(TEXT_AUTOSIZING)
     if (setFixedLayoutSize(newLayoutSize))
         resetTextAutosizing();
-
+#endif
     setMaximumUnobscuredSize(maximumUnobscuredSize);
     m_page->setUnobscuredSafeAreaInsets(targetUnobscuredSafeAreaInsets);
 
@@ -3336,6 +3327,55 @@ void WebPage::resetViewportDefaultConfiguration(WebFrame* frame, bool hasMobileD
         m_viewportConfiguration.setDefaultConfiguration(parametersForStandardFrame());
 }
 
+#if ENABLE(TEXT_AUTOSIZING)
+void WebPage::resetIdempotentTextAutosizingIfNeeded(double previousInitialScale)
+{
+    if (!m_page->settings().textAutosizingUsesIdempotentMode())
+        return;
+
+    const float minimumScaleChangeBeforeRecomputingTextAutosizing = 0.01;
+    if (std::abs(previousInitialScale - m_page->initialScale()) < minimumScaleChangeBeforeRecomputingTextAutosizing)
+        return;
+
+    if (m_page->initialScale() >= 1 && previousInitialScale >= 1)
+        return;
+
+    if (!m_page->mainFrame().view())
+        return;
+
+    auto textAutoSizingDelay = [&] {
+        auto& frameView = *m_page->mainFrame().view();
+        auto isVisaullyNonEmpty = frameView.isVisuallyNonEmpty();
+        auto willBeVisuallyNonEmptySoon = !isVisaullyNonEmpty && frameView.qualifiesAsVisuallyNonEmpty();
+        if (willBeVisuallyNonEmptySoon) {
+            // Be a bit more agressive on the first display.
+            const Seconds shortTextAutoSizingDelayOnViewportChange = 20_ms;
+            return shortTextAutoSizingDelayOnViewportChange;
+        } 
+        if (!isVisaullyNonEmpty) {
+            // We don't anticipate any paining after the next upcoming layout.
+            const Seconds longTextAutoSizingDelayOnViewportChange = 100_ms;
+            return longTextAutoSizingDelayOnViewportChange;
+        }
+        const Seconds defaultTextAutoSizingDelayOnViewportChange = 80_ms;
+        return defaultTextAutoSizingDelayOnViewportChange;
+    };
+
+    // We don't need to update text sizing eagerly. There might be multiple incoming dynamic viewport changes.
+    m_textAutoSizingAdjustmentTimer.startOneShot(textAutoSizingDelay());
+}
+
+void WebPage::resetTextAutosizing()
+{
+    for (Frame* frame = &m_page->mainFrame(); frame; frame = frame->tree().traverseNext()) {
+        Document* document = frame->document();
+        if (!document || !document->renderView())
+            continue;
+        document->renderView()->resetTextAutosizing();
+    }
+}
+#endif
+
 #if ENABLE(VIEWPORT_RESIZING)
 
 void WebPage::scheduleShrinkToFitContent()
@@ -3430,31 +3470,17 @@ bool WebPage::shouldIgnoreMetaViewport() const
     return m_page->settings().shouldIgnoreMetaViewport();
 }
 
-void WebPage::resetIdempotentTextAutosizingIfNeeded(double previousInitialScale)
-{
-    if (!m_page->settings().textAutosizingUsesIdempotentMode())
-        return;
-
-    const float minimumScaleChangeBeforeRecomputingTextAutosizing = 0.01;
-    if (std::abs(previousInitialScale - m_page->initialScale()) < minimumScaleChangeBeforeRecomputingTextAutosizing)
-        return;
-
-    if (m_page->initialScale() >= 1 && previousInitialScale >= 1)
-        return;
-
-    m_page->setNeedsRecalcStyleInAllFrames();
-}
-
 void WebPage::viewportConfigurationChanged(ZoomToInitialScale zoomToInitialScale)
 {
-    double previousInitialScale = m_page->initialScale();
     double initialScale = m_viewportConfiguration.initialScale();
+#if ENABLE(TEXT_AUTOSIZING)
+    double previousInitialScale = m_page->initialScale();
     m_page->setInitialScale(initialScale);
     resetIdempotentTextAutosizingIfNeeded(previousInitialScale);
 
     if (setFixedLayoutSize(m_viewportConfiguration.layoutSize()))
         resetTextAutosizing();
-
+#endif
     double scale;
     if (m_userHasChangedPageScaleFactor && zoomToInitialScale == ZoomToInitialScale::No)
         scale = std::max(std::min(pageScaleFactor(), m_viewportConfiguration.maximumScale()), m_viewportConfiguration.minimumScale());