[iOS][WK2] Make the state restore from HistoryItem more precise and reliable
authorbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 23 Jun 2014 22:05:47 +0000 (22:05 +0000)
committerbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 23 Jun 2014 22:05:47 +0000 (22:05 +0000)
https://bugs.webkit.org/show_bug.cgi?id=134150

Patch by Benjamin Poulain <bpoulain@apple.com> on 2014-06-23
Reviewed by Tim Horton.

Source/WebCore:
The two biggest changes for WebCore are:
-Store everything we need to handle changes of ViewportConfiguration on HistoryItem.
-Store the exposedRect with floating point coordinates.

* WebCore.exp.in:
* history/CachedPage.cpp:
(WebCore::CachedPage::restore):
We cannot determine a good scroll position from the WebProcess because the obscured insets can change
arbitrarily in the UIProcess. When we scroll here from the WebProcess, we would force an invalid position
to the UIProcess with the next layer tree update.

To avoid any problem, we prohibit scrolling when restoring the focus appearance.

* history/HistoryItem.cpp:
(WebCore::encodeRect):
(WebCore::encodeSize):
(WebCore::HistoryItem::encodeBackForwardTreeNode):
(WebCore::decodeRect):
(WebCore::decodeSize):
(WebCore::HistoryItem::decodeBackForwardTree):
* history/HistoryItem.h:
(WebCore::HistoryItem::exposedContentRect):
(WebCore::HistoryItem::setExposedContentRect):
(WebCore::HistoryItem::unobscuredContentRect):
(WebCore::HistoryItem::setUnobscuredContentRect):
(WebCore::HistoryItem::minimumLayoutSizeInScrollViewCoordinates):
(WebCore::HistoryItem::setMinimumLayoutSizeInScrollViewCoordinates):
(WebCore::HistoryItem::contentSize):
(WebCore::HistoryItem::setContentSize):
(WebCore::HistoryItem::exposedContentPosition): Deleted.
(WebCore::HistoryItem::setExposedContentPosition): Deleted.
* loader/HistoryController.cpp:
(WebCore::HistoryController::saveScrollPositionAndViewStateToItem):
* page/FrameView.h:
* platform/ScrollView.h:
* platform/ios/ScrollViewIOS.mm:
(WebCore::ScrollView::exposedContentRect):
(WebCore::ScrollView::setExposedContentRect):
* rendering/RenderLayerCompositor.cpp:
(WebCore::RenderLayerCompositor::didChangeVisibleRect):

Source/WebKit2:
This patch make several little improvements to improve how we restore the visible content rect and scale
from the HistoryItem.

The biggest architectural change is that the exposed rect is now restored on the UIProcess instead of the WebProcess,
this ensure we restore the same position regardless of any modification of obscured areas.

* Shared/VisibleContentRectUpdateInfo.cpp:
(WebKit::VisibleContentRectUpdateInfo::encode):
(WebKit::VisibleContentRectUpdateInfo::decode):
* Shared/VisibleContentRectUpdateInfo.h:
(WebKit::VisibleContentRectUpdateInfo::VisibleContentRectUpdateInfo):
(WebKit::VisibleContentRectUpdateInfo::lastLayerTreeTransactionId):
(WebKit::WebPage::updateVisibleContentRects):
* WebProcess/WebPage/mac/RemoteLayerTreeDrawingArea.h:
A race between the UIProcess and the WebProcess could cause the viewport of the next page to be influenced by updates
of the previous page. To avoid that, VisibleContentRectUpdateInfo keeps track of the last transaction seen at the time
of the update.

The WebProcess updates the size and scale of the content through layer tree updates. If an update was generated for a layer tree
update of the old page, none of the information is valid for the current content. Since the UIProcess drives the state in case of conflicts,
the WebProcess was updating the scale of the current page based on incorrect information.

To avoid the problems, we save the layer tree transaction ID when we commit a new page. Only updates after that transaction are useful
for the current page.

* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView _processDidExit]):
(withinEpsilon):
(changeContentOffsetBoundedInValidRange):
(-[WKWebView _didCommitLayerTree:WebKit::]):
(-[WKWebView _restorePageStateToExposedRect:WebCore::scale:]):
(-[WKWebView _restorePageStateToUnobscuredCenter:WebCore::scale:]):
* UIProcess/API/Cocoa/WKWebViewInternal.h:
* UIProcess/PageClient.h:
* UIProcess/WebPageProxy.h:
* UIProcess/WebPageProxy.messages.in:
* UIProcess/ios/PageClientImplIOS.h:
* UIProcess/ios/PageClientImplIOS.mm:
(WebKit::PageClientImpl::restorePageState):
(WebKit::PageClientImpl::restorePageCenterAndScale):
Restoring the state is now done by WKWebView. The state is only updated on the next layer tree commit,
this is done to avoid any jumping if the page has scrolled since we tried to restore its state.

Both update path end up calling _updateVisibleContentRects. This is because the update on the WebProcess
never sets the ScrollPosition (because it does not know the current state of the obscured insets). Pushing
a new VisibleContentRect will nicely udpates the exposed rect, scroll position, fixed elements, etc.

* UIProcess/ios/WKContentView.mm:
(-[WKContentView didUpdateVisibleRect:unobscuredRect:unobscuredRectInScrollViewCoordinates:scale:minimumScale:inStableState:isChangingObscuredInsetsInteractively:]):
* UIProcess/ios/WebPageProxyIOS.mm:
(WebKit::WebPageProxy::restorePageState):
(WebKit::WebPageProxy::restorePageCenterAndScale):
* UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.h:
(WebKit::RemoteLayerTreeDrawingAreaProxy::lastCommittedLayerTreeTransactionID):
* UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.mm:
(WebKit::RemoteLayerTreeDrawingAreaProxy::RemoteLayerTreeDrawingAreaProxy):
* WebProcess/WebCoreSupport/ios/WebFrameLoaderClientIOS.mm:
(WebKit::WebFrameLoaderClient::saveViewStateToItem):
(WebKit::WebFrameLoaderClient::restoreViewState):
* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::WebPage):
(WebKit::WebPage::didCommitLoad):
* WebProcess/WebPage/WebPage.h:
Get rid of m_obscuredTopInset. It was a bad idea. The UIProcess updates the obscured insets a lot during
page load, the value we used to restore the scroll position was frequently stale.

(WebKit::WebPage::userHasChangedPageScaleFactor): Deleted.
* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::savePageState):
(WebKit::scaleAfterViewportWidthChange):
(WebKit::relativeCenterAfterContentSizeChange):
(WebKit::adjustExposedRectForNewScale):
Extract this out of dynamicViewportSizeUpdate(). It is useful to adjust the exposed rect when restoring a HistoryItem
to a ViewportConfiguration that is different from when it was saved.

(WebKit::WebPage::restorePageState):
There are two variations of restorePage:
1) If the viewport configuration is compatible, restore the exact scale and position of the page.
2) Otherwise, restore the scale and position similarily to dynamicViewportSizeUpdate().

(WebKit::WebPage::dynamicViewportSizeUpdate):
(WebKit::WebPage::viewportConfigurationChanged):
(WebKit::adjustExposedRectForBoundedScale):
(WebKit::RemoteLayerTreeDrawingArea::currentTransactionID):
Expose the transactionID for the race issue on VisibleRectUpdate.

* WebProcess/WebPage/mac/RemoteLayerTreeDrawingArea.mm:
(WebKit::RemoteLayerTreeDrawingArea::setExposedContentRect):
Store the exposed rect in floating point coordinates. This makes it possible to restore that exact
position when needed.

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

30 files changed:
Source/WebCore/ChangeLog
Source/WebCore/WebCore.exp.in
Source/WebCore/history/CachedPage.cpp
Source/WebCore/history/HistoryItem.cpp
Source/WebCore/history/HistoryItem.h
Source/WebCore/loader/HistoryController.cpp
Source/WebCore/page/FrameView.h
Source/WebCore/platform/ScrollView.h
Source/WebCore/platform/ios/ScrollViewIOS.mm
Source/WebCore/rendering/RenderLayerCompositor.cpp
Source/WebKit2/ChangeLog
Source/WebKit2/Shared/VisibleContentRectUpdateInfo.cpp
Source/WebKit2/Shared/VisibleContentRectUpdateInfo.h
Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm
Source/WebKit2/UIProcess/API/Cocoa/WKWebViewInternal.h
Source/WebKit2/UIProcess/PageClient.h
Source/WebKit2/UIProcess/WebPageProxy.h
Source/WebKit2/UIProcess/WebPageProxy.messages.in
Source/WebKit2/UIProcess/ios/PageClientImplIOS.h
Source/WebKit2/UIProcess/ios/PageClientImplIOS.mm
Source/WebKit2/UIProcess/ios/WKContentView.mm
Source/WebKit2/UIProcess/ios/WebPageProxyIOS.mm
Source/WebKit2/UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.h
Source/WebKit2/UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.mm
Source/WebKit2/WebProcess/WebCoreSupport/ios/WebFrameLoaderClientIOS.mm
Source/WebKit2/WebProcess/WebPage/WebPage.cpp
Source/WebKit2/WebProcess/WebPage/WebPage.h
Source/WebKit2/WebProcess/WebPage/ios/WebPageIOS.mm
Source/WebKit2/WebProcess/WebPage/mac/RemoteLayerTreeDrawingArea.h
Source/WebKit2/WebProcess/WebPage/mac/RemoteLayerTreeDrawingArea.mm

index 6b74644..4859f3c 100644 (file)
@@ -1,3 +1,51 @@
+2014-06-23  Benjamin Poulain  <bpoulain@apple.com>
+
+        [iOS][WK2] Make the state restore from HistoryItem more precise and reliable
+        https://bugs.webkit.org/show_bug.cgi?id=134150
+
+        Reviewed by Tim Horton.
+
+        The two biggest changes for WebCore are:
+        -Store everything we need to handle changes of ViewportConfiguration on HistoryItem.
+        -Store the exposedRect with floating point coordinates.
+
+        * WebCore.exp.in:
+        * history/CachedPage.cpp:
+        (WebCore::CachedPage::restore):
+        We cannot determine a good scroll position from the WebProcess because the obscured insets can change
+        arbitrarily in the UIProcess. When we scroll here from the WebProcess, we would force an invalid position
+        to the UIProcess with the next layer tree update.
+
+        To avoid any problem, we prohibit scrolling when restoring the focus appearance.
+
+        * history/HistoryItem.cpp:
+        (WebCore::encodeRect):
+        (WebCore::encodeSize):
+        (WebCore::HistoryItem::encodeBackForwardTreeNode):
+        (WebCore::decodeRect):
+        (WebCore::decodeSize):
+        (WebCore::HistoryItem::decodeBackForwardTree):
+        * history/HistoryItem.h:
+        (WebCore::HistoryItem::exposedContentRect):
+        (WebCore::HistoryItem::setExposedContentRect):
+        (WebCore::HistoryItem::unobscuredContentRect):
+        (WebCore::HistoryItem::setUnobscuredContentRect):
+        (WebCore::HistoryItem::minimumLayoutSizeInScrollViewCoordinates):
+        (WebCore::HistoryItem::setMinimumLayoutSizeInScrollViewCoordinates):
+        (WebCore::HistoryItem::contentSize):
+        (WebCore::HistoryItem::setContentSize):
+        (WebCore::HistoryItem::exposedContentPosition): Deleted.
+        (WebCore::HistoryItem::setExposedContentPosition): Deleted.
+        * loader/HistoryController.cpp:
+        (WebCore::HistoryController::saveScrollPositionAndViewStateToItem):
+        * page/FrameView.h:
+        * platform/ScrollView.h:
+        * platform/ios/ScrollViewIOS.mm:
+        (WebCore::ScrollView::exposedContentRect):
+        (WebCore::ScrollView::setExposedContentRect):
+        * rendering/RenderLayerCompositor.cpp:
+        (WebCore::RenderLayerCompositor::didChangeVisibleRect):
+
 2014-06-23  Eric Carlson  <eric.carlson@apple.com>
 
         [Mac] process raw VTT in-band captions
index 47f7311..10c2a9b 100644 (file)
@@ -2502,7 +2502,7 @@ _WebUIApplicationWillResignActiveNotification
 __ZN7WebCore10CredentialC1EP13__SecIdentityPK9__CFArrayNS_21CredentialPersistenceE
 __ZN7WebCore10RenderView35resumePausedImageAnimationsIfNeededEv
 __ZN7WebCore10ScrollView15setScrollOffsetERKNS_8IntPointE
-__ZN7WebCore10ScrollView21setExposedContentRectERKNS_7IntRectE
+__ZN7WebCore10ScrollView21setExposedContentRectERKNS_9FloatRectE
 __ZN7WebCore10ScrollView24setUnobscuredContentSizeERKNS_7IntSizeE
 __ZN7WebCore10XLinkNames4initEv
 __ZN7WebCore10inSameLineERKNS_15VisiblePositionES2_
@@ -2779,6 +2779,7 @@ __ZNK7WebCore9FloatSizecv6CGSizeEv
 __ZNK7WebCore9FrameView17wasScrolledByUserEv
 __ZNK7WebCore9FrameView30viewportConstrainedObjectsRectEv
 __ZNK7WebCore9RenderBox11borderRadiiEv
+__ZNK7WebCore10ScrollView18exposedContentRectEv
 _webThreadShouldYield
 _wkExecutableWasLinkedOnOrAfterIOSVersion
 _wkGetAvailableScreenSize
index 7721e53..af06f58 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006, 2007, 2008, 2014 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -89,9 +89,18 @@ void CachedPage::restore(Page& page)
         // We don't want focused nodes changing scroll position when restoring from the cache
         // as it can cause ugly jumps before we manage to restore the cached position.
         page.mainFrame().selection().suppressScrolling();
+
+        bool hadProhibitsScrolling = false;
+        FrameView* frameView = page.mainFrame().view();
+        if (frameView) {
+            hadProhibitsScrolling = frameView->prohibitsScrolling();
+            frameView->setProhibitsScrolling(true);
+        }
 #endif
         element->updateFocusAppearance(true);
 #if PLATFORM(IOS)
+        if (frameView)
+            frameView->setProhibitsScrolling(hadProhibitsScrolling);
         page.mainFrame().selection().restoreScrolling();
 #endif
     }
index 770daa5..ec134c5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005, 2006, 2008, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2005, 2006, 2008, 2011, 2014 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -592,6 +592,36 @@ void HistoryItem::encodeBackForwardTree(KeyedEncoder& encoder) const
     });
 }
 
+#if PLATFORM(IOS)
+static void encodeRect(Encoder& encoder, const FloatRect& floatRect)
+{
+    encoder.encodeFloat(floatRect.x());
+    encoder.encodeFloat(floatRect.y());
+    encoder.encodeFloat(floatRect.width());
+    encoder.encodeFloat(floatRect.height());
+}
+
+static void encodeRect(Encoder& encoder, const IntRect& intRect)
+{
+    encoder.encodeInt32(intRect.x());
+    encoder.encodeInt32(intRect.y());
+    encoder.encodeInt32(intRect.width());
+    encoder.encodeInt32(intRect.height());
+}
+
+static void encodeSize(Encoder& encoder, const FloatSize& floatSize)
+{
+    encoder.encodeFloat(floatSize.width());
+    encoder.encodeFloat(floatSize.height());
+}
+
+static void encodeSize(Encoder& encoder, const IntSize& intSize)
+{
+    encoder.encodeInt32(intSize.width());
+    encoder.encodeInt32(intSize.height());
+}
+#endif
+
 void HistoryItem::encodeBackForwardTreeNode(Encoder& encoder) const
 {
     size_t size = m_children.size();
@@ -633,6 +663,14 @@ void HistoryItem::encodeBackForwardTreeNode(Encoder& encoder) const
         encoder.encodeBytes(m_stateObject->data().data(), m_stateObject->data().size());
 
     encoder.encodeString(m_target);
+
+#if PLATFORM(IOS)
+    encodeRect(encoder, m_exposedContentRect);
+    encodeRect(encoder, m_unobscuredContentRect);
+    encodeSize(encoder, m_minimumLayoutSizeInScrollViewCoordinates);
+    encodeSize(encoder, m_contentSize);
+    encoder.encodeBool(m_scaleIsInitial);
+#endif
 }
 
 void HistoryItem::encodeBackForwardTreeNode(KeyedEncoder& encoder) const
@@ -687,6 +725,68 @@ struct DecodeRecursionStackElement {
     }
 };
 
+#if PLATFORM(IOS)
+static bool decodeRect(Decoder& decoder, FloatRect& floatRect)
+{
+    float x;
+    if (!decoder.decodeFloat(x))
+        return false;
+    float y;
+    if (!decoder.decodeFloat(y))
+        return false;
+    float width;
+    if (!decoder.decodeFloat(width))
+        return false;
+    float height;
+    if (!decoder.decodeFloat(height))
+        return false;
+    floatRect = FloatRect(x, y, width, height);
+    return true;
+}
+
+static bool decodeRect(Decoder& decoder, IntRect& intRect)
+{
+    int x;
+    if (!decoder.decodeInt32(x))
+        return false;
+    int y;
+    if (!decoder.decodeInt32(y))
+        return false;
+    int width;
+    if (!decoder.decodeInt32(width))
+        return false;
+    int height;
+    if (!decoder.decodeInt32(height))
+        return false;
+    intRect = IntRect(x, y, width, height);
+    return true;
+}
+
+static bool decodeSize(Decoder& decoder, FloatSize& floatSize)
+{
+    float width;
+    if (!decoder.decodeFloat(width))
+        return false;
+    float height;
+    if (!decoder.decodeFloat(height))
+        return false;
+    floatSize = FloatSize(width, height);
+    return true;
+}
+
+static bool decodeSize(Decoder& decoder, IntSize& intSize)
+{
+    int width;
+    if (!decoder.decodeInt32(width))
+        return false;
+    int height;
+    if (!decoder.decodeInt32(height))
+        return false;
+    intSize = IntSize(width, height);
+    return true;
+}
+#endif
+
 PassRefPtr<HistoryItem> HistoryItem::decodeBackForwardTree(const String& topURLString, const String& topTitle, const String& topOriginalURLString, Decoder& decoder)
 {
     // Since the data stream is not trusted, the decode has to be non-recursive.
@@ -784,6 +884,19 @@ resume:
     if (!decoder.decodeString(node->m_target))
         return 0;
 
+#if PLATFORM(IOS)
+    if (!decodeRect(decoder, node->m_exposedContentRect))
+        return nullptr;
+    if (!decodeRect(decoder, node->m_unobscuredContentRect))
+        return nullptr;
+    if (!decodeSize(decoder, node->m_minimumLayoutSizeInScrollViewCoordinates))
+        return nullptr;
+    if (!decodeSize(decoder, node->m_contentSize))
+        return nullptr;
+    if (!decoder.decodeBool(node->m_scaleIsInitial))
+        return nullptr;
+#endif
+
     // Simulate recursion with our own stack.
     if (!recursionStack.isEmpty()) {
         DecodeRecursionStackElement& element = recursionStack.last();
index 95b0c0e..49fa478 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006, 2008, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2006, 2008, 2011, 2014 Apple Inc. All rights reserved.
  * Copyright (C) 2012 Research In Motion Limited. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -27,7 +27,9 @@
 #ifndef HistoryItem_h
 #define HistoryItem_h
 
+#include "FloatRect.h"
 #include "IntPoint.h"
+#include "IntRect.h"
 #include "SerializedScriptValue.h"
 #include <memory>
 #include <wtf/RefCounted.h>
@@ -180,8 +182,17 @@ public:
 #endif
 
 #if PLATFORM(IOS)
-    IntPoint exposedContentPosition() const { return m_exposedContentPosition; }
-    void setExposedContentPosition(IntPoint exposedContentPosition) { m_exposedContentPosition = exposedContentPosition; }
+    FloatRect exposedContentRect() const { return m_exposedContentRect; }
+    void setExposedContentRect(FloatRect exposedContentRect) { m_exposedContentRect = exposedContentRect; }
+
+    IntRect unobscuredContentRect() const { return m_unobscuredContentRect; }
+    void setUnobscuredContentRect(IntRect unobscuredContentRect) { m_unobscuredContentRect = unobscuredContentRect; }
+
+    FloatSize minimumLayoutSizeInScrollViewCoordinates() const { return m_minimumLayoutSizeInScrollViewCoordinates; }
+    void setMinimumLayoutSizeInScrollViewCoordinates(FloatSize minimumLayoutSizeInScrollViewCoordinates) { m_minimumLayoutSizeInScrollViewCoordinates = minimumLayoutSizeInScrollViewCoordinates; }
+
+    IntSize contentSize() const { return m_contentSize; }
+    void setContentSize(IntSize contentSize) { m_contentSize = contentSize; }
 
     float scale() const { return m_scale; }
     bool scaleIsInitial() const { return m_scaleIsInitial; }
@@ -259,7 +270,10 @@ private:
     std::unique_ptr<CachedPage> m_cachedPage;
 
 #if PLATFORM(IOS)
-    IntPoint m_exposedContentPosition;
+    FloatRect m_exposedContentRect;
+    IntRect m_unobscuredContentRect;
+    FloatSize m_minimumLayoutSizeInScrollViewCoordinates;
+    IntSize m_contentSize;
     float m_scale;
     bool m_scaleIsInitial;
     ViewportArguments m_viewportArguments;
index 6389a64..b8fc7cc 100644 (file)
@@ -71,15 +71,17 @@ HistoryController::~HistoryController()
 
 void HistoryController::saveScrollPositionAndViewStateToItem(HistoryItem* item)
 {
-    if (!item || !m_frame.view())
+    FrameView* frameView = m_frame.view();
+    if (!item || !frameView)
         return;
 
     if (m_frame.document()->inPageCache())
-        item->setScrollPoint(m_frame.view()->cachedScrollPosition());
+        item->setScrollPoint(frameView->cachedScrollPosition());
     else
-        item->setScrollPoint(m_frame.view()->scrollPosition());
+        item->setScrollPoint(frameView->scrollPosition());
 #if PLATFORM(IOS)
-    item->setExposedContentPosition(m_frame.view()->exposedContentRect().location());
+    item->setExposedContentRect(frameView->exposedContentRect());
+    item->setUnobscuredContentRect(frameView->unobscuredContentRect());
 #endif
 
     Page* page = m_frame.page();
index f6d1db1..6598570 100644 (file)
@@ -129,6 +129,7 @@ public:
     void setCustomFixedPositionLayoutRect(const IntRect&);
     bool updateFixedPositionLayoutRect();
 
+    IntSize customSizeForResizeEvent() const { return m_customSizeForResizeEvent; }
     void setCustomSizeForResizeEvent(IntSize);
 
     void setScrollVelocity(double horizontalVelocity, double verticalVelocity, double scaleChangeRate, double timestamp);
index 553da56..a827510 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2004, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2004, 2006, 2007, 2008, 2013, 2014 Apple Inc. All rights reserved.
  * Copyright (C) 2009 Holger Hans Peter Freyther
  *
  * Redistribution and use in source and binary forms, with or without
@@ -27,6 +27,7 @@
 #ifndef ScrollView_h
 #define ScrollView_h
 
+#include "FloatRect.h"
 #include "IntRect.h"
 #include "Scrollbar.h"
 #include "ScrollableArea.h"
@@ -182,10 +183,10 @@ public:
 
 #if PLATFORM(IOS)
     // This is the area that is partially or fully exposed, and may extend under overlapping UI elements.
-    IntRect exposedContentRect() const;
+    FloatRect exposedContentRect() const;
 
     // The given rects are only used if there is no platform widget.
-    void setExposedContentRect(const IntRect&);
+    void setExposedContentRect(const FloatRect&);
     void setUnobscuredContentSize(const IntSize&);
 
     void setActualScrollPosition(const IntPoint&);
@@ -429,7 +430,7 @@ private:
     // FIXME: exposedContentRect is a very similar concept to fixedVisibleContentRect except it does not differentiate
     // between exposed and unobscured areas. The two attributes should eventually be merged.
 #if PLATFORM(IOS)
-    IntRect m_exposedContentRect;
+    FloatRect m_exposedContentRect;
     IntSize m_unobscuredContentSize;
 #else
     IntRect m_fixedVisibleContentRect;
index a4a3789..c55d0be 100644 (file)
@@ -119,7 +119,7 @@ void ScrollView::setUnobscuredContentSize(const IntSize& size)
     m_unobscuredContentSize = size;
 }
 
-IntRect ScrollView::exposedContentRect() const
+FloatRect ScrollView::exposedContentRect() const
 {
     if (NSScrollView *view = static_cast<NSScrollView *>(platformWidget())) {
         CGRect r = CGRectZero;
@@ -132,20 +132,20 @@ IntRect ScrollView::exposedContentRect() const
         }
 
         END_BLOCK_OBJC_EXCEPTIONS;
-        return enclosingIntRect(r);
+        return r;
     }
 
     const ScrollView* parent = this->parent();
     if (!parent)
         return m_exposedContentRect;
 
-    IntRect parentViewExtentContentRect = parent->exposedContentRect();
+    IntRect parentViewExtentContentRect = enclosingIntRect(parent->exposedContentRect());
     IntRect selfExtentContentRect = rootViewToContents(parentViewExtentContentRect);
     selfExtentContentRect.intersect(boundsRect());
     return selfExtentContentRect;
 }
 
-void ScrollView::setExposedContentRect(const IntRect& rect)
+void ScrollView::setExposedContentRect(const FloatRect& rect)
 {
     ASSERT(!platformWidget());
     m_exposedContentRect = rect;
index a6ea550..2d96796 100644 (file)
@@ -558,7 +558,7 @@ void RenderLayerCompositor::didChangeVisibleRect()
     const FrameView& frameView = m_renderView.frameView();
 
 #if PLATFORM(IOS)
-    IntRect visibleRect = frameView.exposedContentRect();
+    IntRect visibleRect = enclosingIntRect(frameView.exposedContentRect());
 #else
     IntRect visibleRect = m_clipLayer ? IntRect(IntPoint(), frameView.contentsSize()) : frameView.visibleContentRect();
 #endif
index 94f3968..74d5199 100644 (file)
@@ -1,3 +1,101 @@
+2014-06-23  Benjamin Poulain  <bpoulain@apple.com>
+
+        [iOS][WK2] Make the state restore from HistoryItem more precise and reliable
+        https://bugs.webkit.org/show_bug.cgi?id=134150
+
+        Reviewed by Tim Horton.
+
+        This patch make several little improvements to improve how we restore the visible content rect and scale
+        from the HistoryItem.
+
+        The biggest architectural change is that the exposed rect is now restored on the UIProcess instead of the WebProcess,
+        this ensure we restore the same position regardless of any modification of obscured areas.
+
+        * Shared/VisibleContentRectUpdateInfo.cpp:
+        (WebKit::VisibleContentRectUpdateInfo::encode):
+        (WebKit::VisibleContentRectUpdateInfo::decode):
+        * Shared/VisibleContentRectUpdateInfo.h:
+        (WebKit::VisibleContentRectUpdateInfo::VisibleContentRectUpdateInfo):
+        (WebKit::VisibleContentRectUpdateInfo::lastLayerTreeTransactionId):
+        (WebKit::WebPage::updateVisibleContentRects):
+        * WebProcess/WebPage/mac/RemoteLayerTreeDrawingArea.h:
+        A race between the UIProcess and the WebProcess could cause the viewport of the next page to be influenced by updates
+        of the previous page. To avoid that, VisibleContentRectUpdateInfo keeps track of the last transaction seen at the time
+        of the update.
+
+        The WebProcess updates the size and scale of the content through layer tree updates. If an update was generated for a layer tree
+        update of the old page, none of the information is valid for the current content. Since the UIProcess drives the state in case of conflicts,
+        the WebProcess was updating the scale of the current page based on incorrect information.
+
+        To avoid the problems, we save the layer tree transaction ID when we commit a new page. Only updates after that transaction are useful
+        for the current page.
+
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView _processDidExit]):
+        (withinEpsilon):
+        (changeContentOffsetBoundedInValidRange):
+        (-[WKWebView _didCommitLayerTree:WebKit::]):
+        (-[WKWebView _restorePageStateToExposedRect:WebCore::scale:]):
+        (-[WKWebView _restorePageStateToUnobscuredCenter:WebCore::scale:]):
+        * UIProcess/API/Cocoa/WKWebViewInternal.h:
+        * UIProcess/PageClient.h:
+        * UIProcess/WebPageProxy.h:
+        * UIProcess/WebPageProxy.messages.in:
+        * UIProcess/ios/PageClientImplIOS.h:
+        * UIProcess/ios/PageClientImplIOS.mm:
+        (WebKit::PageClientImpl::restorePageState):
+        (WebKit::PageClientImpl::restorePageCenterAndScale):
+        Restoring the state is now done by WKWebView. The state is only updated on the next layer tree commit,
+        this is done to avoid any jumping if the page has scrolled since we tried to restore its state.
+
+        Both update path end up calling _updateVisibleContentRects. This is because the update on the WebProcess
+        never sets the ScrollPosition (because it does not know the current state of the obscured insets). Pushing
+        a new VisibleContentRect will nicely udpates the exposed rect, scroll position, fixed elements, etc.
+
+        * UIProcess/ios/WKContentView.mm:
+        (-[WKContentView didUpdateVisibleRect:unobscuredRect:unobscuredRectInScrollViewCoordinates:scale:minimumScale:inStableState:isChangingObscuredInsetsInteractively:]):
+        * UIProcess/ios/WebPageProxyIOS.mm:
+        (WebKit::WebPageProxy::restorePageState):
+        (WebKit::WebPageProxy::restorePageCenterAndScale):
+        * UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.h:
+        (WebKit::RemoteLayerTreeDrawingAreaProxy::lastCommittedLayerTreeTransactionID):
+        * UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.mm:
+        (WebKit::RemoteLayerTreeDrawingAreaProxy::RemoteLayerTreeDrawingAreaProxy):
+        * WebProcess/WebCoreSupport/ios/WebFrameLoaderClientIOS.mm:
+        (WebKit::WebFrameLoaderClient::saveViewStateToItem):
+        (WebKit::WebFrameLoaderClient::restoreViewState):
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::WebPage):
+        (WebKit::WebPage::didCommitLoad):
+        * WebProcess/WebPage/WebPage.h:
+        Get rid of m_obscuredTopInset. It was a bad idea. The UIProcess updates the obscured insets a lot during
+        page load, the value we used to restore the scroll position was frequently stale.
+
+        (WebKit::WebPage::userHasChangedPageScaleFactor): Deleted.
+        * WebProcess/WebPage/ios/WebPageIOS.mm:
+        (WebKit::WebPage::savePageState):
+        (WebKit::scaleAfterViewportWidthChange):
+        (WebKit::relativeCenterAfterContentSizeChange):
+        (WebKit::adjustExposedRectForNewScale):
+        Extract this out of dynamicViewportSizeUpdate(). It is useful to adjust the exposed rect when restoring a HistoryItem
+        to a ViewportConfiguration that is different from when it was saved.
+
+        (WebKit::WebPage::restorePageState):
+        There are two variations of restorePage:
+        1) If the viewport configuration is compatible, restore the exact scale and position of the page.
+        2) Otherwise, restore the scale and position similarily to dynamicViewportSizeUpdate().
+
+        (WebKit::WebPage::dynamicViewportSizeUpdate):
+        (WebKit::WebPage::viewportConfigurationChanged):
+        (WebKit::adjustExposedRectForBoundedScale):
+        (WebKit::RemoteLayerTreeDrawingArea::currentTransactionID):
+        Expose the transactionID for the race issue on VisibleRectUpdate.
+
+        * WebProcess/WebPage/mac/RemoteLayerTreeDrawingArea.mm:
+        (WebKit::RemoteLayerTreeDrawingArea::setExposedContentRect):
+        Store the exposed rect in floating point coordinates. This makes it possible to restore that exact
+        position when needed.
+
 2014-06-23  Eric Carlson  <eric.carlson@apple.com>
 
         Unreviewed, revert an unintentional change committed with r170323.
index 72362b2..848f371 100644 (file)
@@ -43,6 +43,7 @@ void VisibleContentRectUpdateInfo::encode(IPC::ArgumentEncoder& encoder) const
     encoder << m_horizontalVelocity;
     encoder << m_verticalVelocity;
     encoder << m_scaleChangeRate;
+    encoder << m_lastLayerTreeTransactionID;
 }
 
 bool VisibleContentRectUpdateInfo::decode(IPC::ArgumentDecoder& decoder, VisibleContentRectUpdateInfo& result)
@@ -69,6 +70,8 @@ bool VisibleContentRectUpdateInfo::decode(IPC::ArgumentDecoder& decoder, Visible
         return false;
     if (!decoder.decode(result.m_scaleChangeRate))
         return false;
+    if (!decoder.decode(result.m_lastLayerTreeTransactionID))
+        return false;
 
     return true;
 }
index 74a4f57..bd3a3a7 100644 (file)
@@ -41,10 +41,11 @@ public:
         : m_scale(-1)
         , m_inStableState(false)
         , m_isChangingObscuredInsetsInteractively(false)
+        , m_lastLayerTreeTransactionID(0)
     {
     }
 
-    VisibleContentRectUpdateInfo(const WebCore::FloatRect& exposedRect, const WebCore::FloatRect& unobscuredRect, const WebCore::FloatRect& unobscuredRectInScrollViewCoordinates, const WebCore::FloatRect& customFixedPositionRect, double scale, bool inStableState, bool isChangingObscuredInsetsInteractively, double timestamp, double horizontalVelocity, double verticalVelocity, double scaleChangeRate)
+    VisibleContentRectUpdateInfo(const WebCore::FloatRect& exposedRect, const WebCore::FloatRect& unobscuredRect, const WebCore::FloatRect& unobscuredRectInScrollViewCoordinates, const WebCore::FloatRect& customFixedPositionRect, double scale, bool inStableState, bool isChangingObscuredInsetsInteractively, double timestamp, double horizontalVelocity, double verticalVelocity, double scaleChangeRate, uint64_t lastLayerTreeTranscationId)
         : m_exposedRect(exposedRect)
         , m_unobscuredRect(unobscuredRect)
         , m_unobscuredRectInScrollViewCoordinates(unobscuredRectInScrollViewCoordinates)
@@ -56,6 +57,7 @@ public:
         , m_horizontalVelocity(horizontalVelocity)
         , m_verticalVelocity(verticalVelocity)
         , m_scaleChangeRate(scaleChangeRate)
+        , m_lastLayerTreeTransactionID(lastLayerTreeTranscationId)
     {
     }
 
@@ -72,6 +74,8 @@ public:
     double verticalVelocity() const { return m_verticalVelocity; }
     double scaleChangeRate() const { return m_scaleChangeRate; }
 
+    uint64_t lastLayerTreeTransactionID() const { return m_lastLayerTreeTransactionID; }
+
     void encode(IPC::ArgumentEncoder&) const;
     static bool decode(IPC::ArgumentDecoder&, VisibleContentRectUpdateInfo&);
 
@@ -87,6 +91,7 @@ private:
     double m_horizontalVelocity;
     double m_verticalVelocity;
     double m_scaleChangeRate;
+    uint64_t m_lastLayerTreeTransactionID;
 };
 
 inline bool operator==(const VisibleContentRectUpdateInfo& a, const VisibleContentRectUpdateInfo& b)
index 13a8a4a..d5c45dd 100644 (file)
@@ -169,6 +169,13 @@ WKWebView* fromWebPageProxy(WebKit::WebPageProxy& page)
     RetainPtr<UIView> _resizeAnimationView;
     CGFloat _lastAdjustmentForScroller;
 
+    BOOL _needsToRestoreExposedRect;
+    WebCore::FloatRect _exposedRectToRestore;
+    BOOL _needsToRestoreUnobscuredCenter;
+    WebCore::FloatPoint _unobscuredCenterToRestore;
+    uint64_t _firstTransactionIDAfterPageRestore;
+    double _scaleToRestore;
+
     std::unique_ptr<WebKit::ViewGestureController> _gestureController;
     BOOL _allowsBackForwardNavigationGestures;
 
@@ -700,6 +707,8 @@ static CGFloat contentZoomScale(WKWebView* webView)
 
     _viewportMetaTagWidth = -1;
     _needsResetViewStateAfterCommitLoadForMainFrame = NO;
+    _needsToRestoreExposedRect = NO;
+    _needsToRestoreUnobscuredCenter = NO;
     _scrollViewBackgroundColor = WebCore::Color();
     _delayUpdateVisibleContentRects = NO;
     _hadDelayedUpdateVisibleContentRects = NO;
@@ -713,6 +722,30 @@ static CGFloat contentZoomScale(WKWebView* webView)
     _usesMinimalUI = NO;
 }
 
+static inline bool withinEpsilon(float a, float b)
+{
+    return fabs(a - b) < std::numeric_limits<float>::epsilon();
+}
+
+static void changeContentOffsetBoundedInValidRange(UIScrollView *scrollView, WebCore::FloatPoint contentOffset)
+{
+    UIEdgeInsets contentInsets = scrollView.contentInset;
+    CGSize contentSize = scrollView.contentSize;
+    CGSize scrollViewSize = scrollView.bounds.size;
+
+    float maxHorizontalOffset = contentSize.width + contentInsets.right - scrollViewSize.width;
+    if (contentOffset.x() > maxHorizontalOffset)
+        contentOffset.setX(maxHorizontalOffset);
+    float maxVerticalOffset = contentSize.height + contentInsets.bottom - scrollViewSize.height;
+    if (contentOffset.y() > maxVerticalOffset)
+        contentOffset.setY(maxVerticalOffset);
+    if (contentOffset.x() < -contentInsets.left)
+        contentOffset.setX(-contentInsets.left);
+    if (contentOffset.y() < -contentInsets.top)
+        contentOffset.setY(-contentInsets.top);
+    scrollView.contentOffset = contentOffset;
+}
+
 - (void)_didCommitLayerTree:(const WebKit::RemoteLayerTreeTransaction&)layerTreeTransaction
 {
     if (_customContentView)
@@ -748,6 +781,34 @@ static CGFloat contentZoomScale(WKWebView* webView)
         [_scrollView setContentOffset:CGPointMake(-_obscuredInsets.left, -_obscuredInsets.top)];
         [self _updateVisibleContentRects];
     }
+
+    if (_needsToRestoreExposedRect && layerTreeTransaction.transactionID() >= _firstTransactionIDAfterPageRestore) {
+        _needsToRestoreExposedRect = NO;
+
+        if (withinEpsilon(contentZoomScale(self), _scaleToRestore)) {
+            WebCore::FloatPoint exposedPosition = _exposedRectToRestore.location();
+            exposedPosition.scale(_scaleToRestore, _scaleToRestore);
+
+            changeContentOffsetBoundedInValidRange(_scrollView.get(), exposedPosition);
+        }
+        [self _updateVisibleContentRects];
+    }
+
+    if (_needsToRestoreUnobscuredCenter && layerTreeTransaction.transactionID() >= _firstTransactionIDAfterPageRestore) {
+        _needsToRestoreUnobscuredCenter = NO;
+
+        if (withinEpsilon(contentZoomScale(self), _scaleToRestore)) {
+            CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, _obscuredInsets);
+            WebCore::FloatSize unobscuredContentSizeAtNewScale(unobscuredRect.size.width / _scaleToRestore, unobscuredRect.size.height / _scaleToRestore);
+            WebCore::FloatPoint topLeftInDocumentCoordinate(_unobscuredCenterToRestore.x() - unobscuredContentSizeAtNewScale.width() / 2, _unobscuredCenterToRestore.y() - unobscuredContentSizeAtNewScale.height() / 2);
+
+            topLeftInDocumentCoordinate.scale(_scaleToRestore, _scaleToRestore);
+            topLeftInDocumentCoordinate.moveBy(WebCore::FloatPoint(-_obscuredInsets.left, -_obscuredInsets.top));
+
+            changeContentOffsetBoundedInValidRange(_scrollView.get(), topLeftInDocumentCoordinate);
+        }
+        [self _updateVisibleContentRects];
+    }
 }
 
 - (void)_dynamicViewportUpdateChangedTargetToScale:(double)newScale position:(CGPoint)newScrollPosition
@@ -768,6 +829,36 @@ static CGFloat contentZoomScale(WKWebView* webView)
     }
 }
 
+- (void)_restorePageStateToExposedRect:(WebCore::FloatRect)exposedRect scale:(double)scale
+{
+    if (_isAnimatingResize)
+        return;
+
+    if (_customContentView)
+        return;
+
+    _needsToRestoreUnobscuredCenter = NO;
+    _needsToRestoreExposedRect = YES;
+    _firstTransactionIDAfterPageRestore = toRemoteLayerTreeDrawingAreaProxy(_page->drawingArea())->nextLayerTreeTransactionID();
+    _exposedRectToRestore = exposedRect;
+    _scaleToRestore = scale;
+}
+
+- (void)_restorePageStateToUnobscuredCenter:(WebCore::FloatPoint)center scale:(double)scale
+{
+    if (_isAnimatingResize)
+        return;
+
+    if (_customContentView)
+        return;
+
+    _needsToRestoreExposedRect = NO;
+    _needsToRestoreUnobscuredCenter = YES;
+    _firstTransactionIDAfterPageRestore = toRemoteLayerTreeDrawingAreaProxy(_page->drawingArea())->nextLayerTreeTransactionID();
+    _unobscuredCenterToRestore = center;
+    _scaleToRestore = scale;
+}
+
 - (WebKit::ViewSnapshot)_takeViewSnapshot
 {
     float deviceScale = WKGetScreenScaleFactor();
index 78ec7e3..a53381d 100644 (file)
@@ -71,6 +71,8 @@ struct ViewSnapshot;
 - (void)_didCommitLayerTree:(const WebKit::RemoteLayerTreeTransaction&)layerTreeTransaction;
 
 - (void)_dynamicViewportUpdateChangedTargetToScale:(double)newScale position:(CGPoint)newScrollPosition;
+- (void)_restorePageStateToExposedRect:(WebCore::FloatRect)exposedRect scale:(double)scale;
+- (void)_restorePageStateToUnobscuredCenter:(WebCore::FloatPoint)center scale:(double)scale;
 
 - (WebKit::ViewSnapshot)_takeViewSnapshot;
 
index 251600e..afb1871 100644 (file)
@@ -250,6 +250,8 @@ public:
 
     virtual void didCommitLayerTree(const RemoteLayerTreeTransaction&) = 0;
     virtual void dynamicViewportUpdateChangedTarget(double newScale, const WebCore::FloatPoint& newScrollPosition) = 0;
+    virtual void restorePageState(const WebCore::FloatRect&, double) = 0;
+    virtual void restorePageCenterAndScale(const WebCore::FloatPoint&, double) = 0;
 
     virtual void startAssistingNode(const AssistedNodeInformation&, bool userIsInteracting, bool blurPreviousNode, API::Object* userData) = 0;
     virtual void stopAssistingNode() = 0;
index f344332..9f1f66d 100644 (file)
@@ -1198,6 +1198,9 @@ private:
     float textAutosizingWidth();
 
     void dynamicViewportUpdateChangedTarget(double newTargetScale, const WebCore::FloatPoint& newScrollPosition);
+    void restorePageState(const WebCore::FloatRect&, double scale);
+    void restorePageCenterAndScale(const WebCore::FloatPoint&, double scale);
+
     void didGetTapHighlightGeometries(uint64_t requestID, const WebCore::Color& color, const Vector<WebCore::FloatQuad>& geometries, const WebCore::IntSize& topLeftRadius, const WebCore::IntSize& topRightRadius, const WebCore::IntSize& bottomLeftRadius, const WebCore::IntSize& bottomRightRadius);
 
     void startAssistingNode(const AssistedNodeInformation&, bool userIsInteracting, bool blurPreviousNode, IPC::MessageDecoder&);
index d30bfbb..7de371a 100644 (file)
@@ -332,6 +332,8 @@ messages -> WebPageProxy {
 
 #if PLATFORM(IOS)
     DynamicViewportUpdateChangedTarget(double newTargetScale, WebCore::FloatPoint newScrollPosition)
+    RestorePageState(WebCore::FloatRect exposedRect, double scale)
+    RestorePageCenterAndScale(WebCore::FloatPoint unobscuredCenter, double scale)
     DidGetTapHighlightGeometries(uint64_t requestID, WebCore::Color color, Vector<WebCore::FloatQuad> geometries, WebCore::IntSize topLeftRadius, WebCore::IntSize topRightRadius, WebCore::IntSize bottomLeftRadius, WebCore::IntSize bottomRightRadius)
 
     StartAssistingNode(WebKit::AssistedNodeInformation information, bool userIsInteracting, bool blurPreviousNode, WebKit::InjectedBundleUserMessageEncoder userData) Variadic
index 78a3877..39d0fba 100644 (file)
@@ -113,6 +113,8 @@ private:
 
     virtual void didCommitLayerTree(const RemoteLayerTreeTransaction&) override;
     virtual void dynamicViewportUpdateChangedTarget(double newScale, const WebCore::FloatPoint& newScrollPosition) override;
+    virtual void restorePageState(const WebCore::FloatRect&, double) override;
+    virtual void restorePageCenterAndScale(const WebCore::FloatPoint&, double) override;
 
     virtual void startAssistingNode(const AssistedNodeInformation&, bool userIsInteracting, bool blurPreviousNode, API::Object* userData) override;
     virtual void stopAssistingNode() override;
index 36829dd..47e5a73 100644 (file)
@@ -433,6 +433,16 @@ void PageClientImpl::dynamicViewportUpdateChangedTarget(double newScale, const W
     [m_webView _dynamicViewportUpdateChangedTargetToScale:newScale position:newScrollPosition];
 }
 
+void PageClientImpl::restorePageState(const WebCore::FloatRect& exposedRect, double scale)
+{
+    [m_webView _restorePageStateToExposedRect:exposedRect scale:scale];
+}
+
+void PageClientImpl::restorePageCenterAndScale(const WebCore::FloatPoint& center, double scale)
+{
+    [m_webView _restorePageStateToUnobscuredCenter:center scale:scale];
+}
+
 void PageClientImpl::startAssistingNode(const AssistedNodeInformation& nodeInformation, bool userIsInteracting, bool blurPreviousNode, API::Object* userData)
 {
     MESSAGE_CHECK(!userData || userData->type() == API::Object::Type::Data);
index c13ae66..cdec45c 100644 (file)
@@ -303,7 +303,7 @@ private:
 
     FloatRect fixedPositionRectForLayout = _page->computeCustomFixedPositionRect(unobscuredRect, zoomScale, WebPageProxy::UnobscuredRectConstraint::ConstrainedToDocumentRect);
     _page->updateVisibleContentRects(VisibleContentRectUpdateInfo(visibleRect, unobscuredRect, unobscuredRectInScrollViewCoordinates, fixedPositionRectForLayout,
-        zoomScale, isStableState, isChangingObscuredInsetsInteractively, timestamp, velocityData.horizontalVelocity, velocityData.verticalVelocity, velocityData.scaleChangeRate));
+        zoomScale, isStableState, isChangingObscuredInsetsInteractively, timestamp, velocityData.horizontalVelocity, velocityData.verticalVelocity, velocityData.scaleChangeRate, toRemoteLayerTreeDrawingAreaProxy(_page->drawingArea())->lastCommittedLayerTreeTransactionID()));
 
     RemoteScrollingCoordinatorProxy* scrollingCoordinator = _page->scrollingCoordinatorProxy();
     scrollingCoordinator->viewportChangedViaDelegatedScrolling(scrollingCoordinator->rootScrollingNodeID(), _page->computeCustomFixedPositionRect(_page->unobscuredContentRect(), zoomScale), zoomScale);
index 20fcd34..191eb31 100644 (file)
@@ -615,6 +615,16 @@ void WebPageProxy::dynamicViewportUpdateChangedTarget(double newScale, const Web
     }
 }
 
+void WebPageProxy::restorePageState(const WebCore::FloatRect& exposedRect, double scale)
+{
+    m_pageClient.restorePageState(exposedRect, scale);
+}
+
+void WebPageProxy::restorePageCenterAndScale(const WebCore::FloatPoint& center, double scale)
+{
+    m_pageClient.restorePageCenterAndScale(center, scale);
+}
+
 void WebPageProxy::didGetTapHighlightGeometries(uint64_t requestID, const WebCore::Color& color, const Vector<WebCore::FloatQuad>& highlightedQuads, const WebCore::IntSize& topLeftRadius, const WebCore::IntSize& topRightRadius, const WebCore::IntSize& bottomLeftRadius, const WebCore::IntSize& bottomRightRadius)
 {
     m_pageClient.didGetTapHighlightGeometries(requestID, color, highlightedQuads, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
index 3787728..f280ec0 100644 (file)
@@ -50,6 +50,7 @@ public:
     void coreAnimationDidCommitLayers();
 
     uint64_t nextLayerTreeTransactionID() const { return m_pendingLayerTreeTransactionID + 1; }
+    uint64_t lastCommittedLayerTreeTransactionID() const { return m_transactionIDForPendingCACommit; }
 
 private:
     virtual void sizeDidChange() override;
index 1cef7ce..0249fba 100644 (file)
@@ -50,6 +50,7 @@ RemoteLayerTreeDrawingAreaProxy::RemoteLayerTreeDrawingAreaProxy(WebPageProxy* w
     , m_isWaitingForDidUpdateGeometry(false)
     , m_pendingLayerTreeTransactionID(0)
     , m_lastVisibleTransactionID(0)
+    , m_transactionIDForPendingCACommit(0)
 {
 #if USE(IOSURFACE)
     // We don't want to pool surfaces in the UI process.
index ff3d915..b7379c8 100644 (file)
@@ -82,7 +82,7 @@ void WebFrameLoaderClient::didCreateQuickLookHandle(WebCore::QuickLookHandle& ha
 void WebFrameLoaderClient::saveViewStateToItem(HistoryItem* historyItem)
 {
     if (m_frame->isMainFrame())
-        historyItem->setScaleIsInitial(m_frame->page()->userHasChangedPageScaleFactor());
+        m_frame->page()->savePageState(*historyItem);
 }
 
 void WebFrameLoaderClient::restoreViewState()
@@ -90,8 +90,8 @@ void WebFrameLoaderClient::restoreViewState()
     Frame& frame = *m_frame->coreFrame();
     HistoryItem* currentItem = frame.loader().history().currentItem();
     if (FrameView* view = frame.view()) {
-        if (m_frame->isMainFrame() && currentItem->pageScaleFactor())
-            m_frame->page()->restorePageState(currentItem->pageScaleFactor(), currentItem->scaleIsInitial(), currentItem->exposedContentPosition());
+        if (m_frame->isMainFrame())
+            m_frame->page()->restorePageState(*currentItem);
         else if (!view->wasScrolledByUser())
             view->setScrollPosition(currentItem->scrollPoint());
     }
index 37dadad..61fa321 100644 (file)
 #endif
 
 #if PLATFORM(IOS)
+#include "RemoteLayerTreeDrawingArea.h"
 #include "WebVideoFullscreenManager.h"
 #include <CoreGraphics/CoreGraphics.h>
 #include <WebCore/Icon.h>
@@ -291,7 +292,7 @@ WebPage::WebPage(uint64_t pageID, const WebPageCreationParameters& parameters)
     , m_isShowingContextMenu(false)
 #endif
 #if PLATFORM(IOS)
-    , m_obscuredTopInset(0)
+    , m_lastLayerTreeTransactionIDBeforeDidCommitLoad(0)
     , m_hasReceivedVisibleContentRectsAfterDidCommitLoad(false)
     , m_scaleWasSetByUIProcess(false)
     , m_userHasChangedPageScaleFactor(false)
@@ -4344,6 +4345,8 @@ void WebPage::didCommitLoad(WebFrame* frame)
     }
 #if PLATFORM(IOS)
     m_hasReceivedVisibleContentRectsAfterDidCommitLoad = false;
+    m_scaleWasSetByUIProcess = false;
+    m_lastLayerTreeTransactionIDBeforeDidCommitLoad = toRemoteLayerTreeDrawingArea(*m_drawingArea).currentTransactionID();
     m_userHasChangedPageScaleFactor = false;
 
     WebProcess::shared().eventDispatcher().clearQueuedTouchEventsForPage(*this);
index dd64f1a..1418645 100644 (file)
@@ -457,7 +457,8 @@ public:
     int32_t deviceOrientation() const { return m_deviceOrientation; }
     void viewportPropertiesDidChange(const WebCore::ViewportArguments&);
     void didReceiveMobileDocType(bool);
-    void restorePageState(double scale, bool userHasChangedPageScaleFactor, const WebCore::IntPoint& exposedOrigin);
+    void savePageState(WebCore::HistoryItem&);
+    void restorePageState(const WebCore::HistoryItem&);
 
     void setUseTestingViewportConfiguration(bool useTestingViewport) { m_useTestingViewportConfiguration = useTestingViewport; }
     bool isUsingTestingViewportConfiguration() const { return m_useTestingViewportConfiguration; }
@@ -465,7 +466,6 @@ public:
     double minimumPageScaleFactor() const;
     double maximumPageScaleFactor() const;
     bool allowsUserScaling() const;
-    bool userHasChangedPageScaleFactor() const { return m_userHasChangedPageScaleFactor; }
 
     void handleTap(const WebCore::IntPoint&);
     void potentialTapAtPosition(uint64_t requestID, const WebCore::FloatPoint&);
@@ -1215,7 +1215,7 @@ private:
     WebCore::FloatPoint m_potentialTapLocation;
 
     WebCore::ViewportConfiguration m_viewportConfiguration;
-    float m_obscuredTopInset;
+    uint64_t m_lastLayerTreeTransactionIDBeforeDidCommitLoad;
     bool m_hasReceivedVisibleContentRectsAfterDidCommitLoad;
     bool m_scaleWasSetByUIProcess;
     bool m_userHasChangedPageScaleFactor;
index 4a22a1d..6f74b98 100644 (file)
@@ -64,6 +64,7 @@
 #import <WebCore/HTMLParserIdioms.h>
 #import <WebCore/HTMLSelectElement.h>
 #import <WebCore/HTMLTextAreaElement.h>
+#import <WebCore/HistoryItem.h>
 #import <WebCore/HitTestResult.h>
 #import <WebCore/KeyboardEvent.h>
 #import <WebCore/MainFrame.h>
@@ -151,17 +152,99 @@ void WebPage::didReceiveMobileDocType(bool isMobileDoctype)
         resetViewportDefaultConfiguration(m_mainFrame.get());
 }
 
-void WebPage::restorePageState(double scale, bool userHasChangedPageScaleFactor, const IntPoint& exposedOrigin)
+void WebPage::savePageState(HistoryItem& historyItem)
 {
-    // FIXME: ideally, we should sync this with the UIProcess. We should not try to change the position if the user is interacting
-    // with the page, and we should send the new scroll position as soon as possible to the UIProcess.
+    historyItem.setScaleIsInitial(!m_userHasChangedPageScaleFactor);
+    historyItem.setMinimumLayoutSizeInScrollViewCoordinates(m_viewportConfiguration.activeMinimumLayoutSizeInScrollViewCoordinates());
+    historyItem.setContentSize(m_viewportConfiguration.contentsSize());
+}
+
+static double scaleAfterViewportWidthChange(double currentScale, bool userHasChangedPageScaleFactor, const ViewportConfiguration& viewportConfiguration, float unobscuredWidthInScrollViewCoordinates, const IntSize& newContentSize, const IntSize& oldContentSize, float visibleHorizontalFraction)
+{
+    double scale;
+    if (!userHasChangedPageScaleFactor)
+        scale = viewportConfiguration.initialScale();
+    else
+        scale = std::max(std::min(currentScale, viewportConfiguration.maximumScale()), viewportConfiguration.minimumScale());
+
+    if (userHasChangedPageScaleFactor) {
+        // When the content size changes, we keep the same relative horizontal content width in view, otherwise we would
+        // end up zoomed too far in landscape->portrait, and too close in portrait->landscape.
+        float widthToKeepInView = visibleHorizontalFraction * newContentSize.width();
+        double newScale = unobscuredWidthInScrollViewCoordinates / widthToKeepInView;
+        scale = std::max(std::min(newScale, viewportConfiguration.maximumScale()), viewportConfiguration.minimumScale());
+    }
+    return scale;
+}
+
+static FloatPoint relativeCenterAfterContentSizeChange(const FloatRect& originalContentRect, IntSize oldContentSize, IntSize newContentSize)
+{
+    // If the content size has changed, keep the same relative position.
+    FloatPoint oldContentCenter = originalContentRect.center();
+    float relativeHorizontalPosition = oldContentCenter.x() / oldContentSize.width();
+    float relativeVerticalPosition =  oldContentCenter.y() / oldContentSize.height();
+    return FloatPoint(relativeHorizontalPosition * newContentSize.width(), relativeVerticalPosition * newContentSize.height());
+}
+
+static inline FloatRect adjustExposedRectForNewScale(const FloatRect& exposedRect, double exposedRectScale, double newScale)
+{
+    double overscaledWidth = exposedRect.width();
+    double missingHorizonalMargin = exposedRect.width() * exposedRectScale / newScale - overscaledWidth;
+
+    double overscaledHeight = exposedRect.height();
+    double missingVerticalMargin = exposedRect.height() * exposedRectScale / newScale - overscaledHeight;
+
+    return FloatRect(exposedRect.x() - missingHorizonalMargin / 2, exposedRect.y() - missingVerticalMargin / 2, exposedRect.width() + missingHorizonalMargin, exposedRect.height() + missingVerticalMargin);
+}
+
+void WebPage::restorePageState(const HistoryItem& historyItem)
+{
+    // When a HistoryItem is cleared, its scale factor and scroll point are set to zero. We should not try to restore the other
+    // parameters in those conditions.
+    if (!historyItem.pageScaleFactor())
+        return;
+
+    // We can restore the exposed rect and scale, but we cannot touch the scroll position since the obscured insets
+    // may be changing in the UIProcess. The UIProcess can update the position from the information we send and will then
+    // scroll to the correct position through a regular VisibleContentRectUpdate.
+
+    m_userHasChangedPageScaleFactor = !historyItem.scaleIsInitial();
+
+    FloatSize currentMinimumLayoutSizeInScrollViewCoordinates = m_viewportConfiguration.activeMinimumLayoutSizeInScrollViewCoordinates();
+    if (historyItem.minimumLayoutSizeInScrollViewCoordinates() == currentMinimumLayoutSizeInScrollViewCoordinates) {
+        float boundedScale = std::min<float>(m_viewportConfiguration.maximumScale(), std::max<float>(m_viewportConfiguration.minimumScale(), historyItem.pageScaleFactor()));
+        scalePage(boundedScale, IntPoint());
+
+        m_drawingArea->setExposedContentRect(historyItem.exposedContentRect());
+
+        send(Messages::WebPageProxy::RestorePageState(historyItem.exposedContentRect(), boundedScale));
+    } else {
+        IntSize oldContentSize = historyItem.contentSize();
+        FrameView& frameView = *m_page->mainFrame().view();
+        IntSize newContentSize = frameView.contentsSize();
+        double visibleHorizontalFraction = static_cast<float>(historyItem.unobscuredContentRect().width()) / oldContentSize.width();
+
+        double newScale = scaleAfterViewportWidthChange(historyItem.pageScaleFactor(), !historyItem.scaleIsInitial(), m_viewportConfiguration, currentMinimumLayoutSizeInScrollViewCoordinates.width(), newContentSize, oldContentSize, visibleHorizontalFraction);
+
+        FloatPoint newCenter;
+        if (!oldContentSize.isEmpty() && !newContentSize.isEmpty() && newContentSize != oldContentSize)
+            newCenter = relativeCenterAfterContentSizeChange(historyItem.unobscuredContentRect(), oldContentSize, newContentSize);
+        else
+            newCenter = FloatRect(historyItem.unobscuredContentRect()).center();
+
+        FloatSize unobscuredRectAtNewScale = frameView.customSizeForResizeEvent();
+        unobscuredRectAtNewScale.scale(1 / newScale, 1 / newScale);
+
+        FloatRect oldExposedRect = frameView.exposedContentRect();
+        FloatRect adjustedExposedRect = adjustExposedRectForNewScale(oldExposedRect, m_page->pageScaleFactor(), newScale);
+
+        FloatPoint oldCenter = adjustedExposedRect.center();
+        adjustedExposedRect.move(newCenter - oldCenter);
 
-    float boundedScale = std::min<float>(m_viewportConfiguration.maximumScale(), std::max<float>(m_viewportConfiguration.minimumScale(), scale));
-    float topInsetInPageCoordinates = m_obscuredTopInset / boundedScale;
-    IntPoint scrollPosition(exposedOrigin.x(), exposedOrigin.y() + topInsetInPageCoordinates);
-    scalePage(boundedScale, scrollPosition);
-    m_page->mainFrame().view()->setScrollPosition(scrollPosition);
-    m_userHasChangedPageScaleFactor = userHasChangedPageScaleFactor;
+        scalePage(newScale, IntPoint());
+
+        send(Messages::WebPageProxy::RestorePageCenterAndScale(newCenter, newScale));
+    }
 }
 
 double WebPage::minimumPageScaleFactor() const
@@ -2046,20 +2129,7 @@ void WebPage::dynamicViewportSizeUpdate(const FloatSize& minimumLayoutSize, cons
 
     IntSize newContentSize = frameView.contentsSize();
 
-    double scale;
-    if (!m_userHasChangedPageScaleFactor)
-        scale = m_viewportConfiguration.initialScale();
-    else
-        scale = std::max(std::min(targetScale, m_viewportConfiguration.maximumScale()), m_viewportConfiguration.minimumScale());
-
-    if (m_userHasChangedPageScaleFactor && newContentSize.width() != oldContentSize.width()) {
-        // When the content size change, we keep the same relative horizontal content width in view, otherwise we would
-        // end up zoom to far in landscape->portrait, and too close in portrait->landscape.
-        float widthToKeepInView = visibleHorizontalFraction * newContentSize.width();
-        double newScale = targetUnobscuredRectInScrollViewCoordinates.width() / widthToKeepInView;
-        scale = std::max(std::min(newScale, m_viewportConfiguration.maximumScale()), m_viewportConfiguration.minimumScale());
-    }
-
+    double scale = scaleAfterViewportWidthChange(targetScale, m_userHasChangedPageScaleFactor, m_viewportConfiguration, targetUnobscuredRectInScrollViewCoordinates.width(), newContentSize, oldContentSize, visibleHorizontalFraction);
     FloatRect newUnobscuredContentRect = targetUnobscuredRect;
     FloatRect newExposedContentRect = targetExposedContentRect;
 
@@ -2106,13 +2176,8 @@ void WebPage::dynamicViewportSizeUpdate(const FloatSize& minimumLayoutSize, cons
                 FrameView& containingView = *oldNodeAtCenter->document().frame()->view();
                 FloatRect newBoundingBox = containingView.contentsToRootView(renderer->absoluteBoundingBoxRect(true));
                 newRelativeContentCenter = FloatPoint(newBoundingBox.x() + relativeHorizontalPositionInNodeAtCenter * newBoundingBox.width(), newBoundingBox.y() + relativeVerticalPositionInNodeAtCenter * newBoundingBox.height());
-            } else {
-                // If the content size has changed, keep the same relative position.
-                FloatPoint oldContentCenter = targetUnobscuredRect.center();
-                float relativeHorizontalPosition = oldContentCenter.x() / oldContentSize.width();
-                float relativeVerticalPosition =  oldContentCenter.y() / oldContentSize.height();
-                newRelativeContentCenter = FloatPoint(relativeHorizontalPosition * newContentSize.width(), relativeVerticalPosition * newContentSize.height());
-            }
+            } else
+                newRelativeContentCenter = relativeCenterAfterContentSizeChange(targetUnobscuredRect, oldContentSize, newContentSize);
 
             FloatPoint newUnobscuredContentRectCenter = newUnobscuredContentRect.center();
             FloatPoint positionDelta(newRelativeContentCenter.x() - newUnobscuredContentRectCenter.x(), newRelativeContentCenter.y() - newUnobscuredContentRectCenter.y());
@@ -2234,7 +2299,7 @@ void WebPage::viewportConfigurationChanged()
 
         // FIXME: We could send down the obscured margins to find a better exposed rect and unobscured rect.
         // It is not a big deal at the moment because the tile coverage will always extend past the obscured bottom inset.
-        m_drawingArea->setExposedContentRect(IntRect(scrollPosition, minimumLayoutSizeInDocumentCoordinates));
+        m_drawingArea->setExposedContentRect(FloatRect(scrollPosition, minimumLayoutSizeInDocumentCoordinates));
     }
     scalePage(scale, scrollPosition);
     
@@ -2272,20 +2337,6 @@ void WebPage::applicationDidBecomeActive()
     [[NSNotificationCenter defaultCenter] postNotificationName:WebUIApplicationDidBecomeActiveNotification object:nil];
 }
 
-static inline FloatRect adjustExposedRectForBoundedScale(const FloatRect& exposedRect, double exposedRectScale, double boundedScale)
-{
-    if (exposedRectScale < boundedScale)
-        return exposedRect;
-
-    double overscaledWidth = exposedRect.width();
-    double missingHorizonalMargin = exposedRect.width() * exposedRectScale / boundedScale - overscaledWidth;
-
-    double overscaledHeight = exposedRect.height();
-    double missingVerticalMargin = exposedRect.height() * exposedRectScale / boundedScale - overscaledHeight;
-
-    return FloatRect(exposedRect.x() - missingHorizonalMargin / 2, exposedRect.y() - missingVerticalMargin / 2, exposedRect.width() + missingHorizonalMargin, exposedRect.height() + missingVerticalMargin);
-}
-
 static inline void adjustVelocityDataForBoundedScale(double& horizontalVelocity, double& verticalVelocity, double& scaleChangeRate, double exposedRectScale, double boundedScale)
 {
     if (scaleChangeRate) {
@@ -2297,10 +2348,21 @@ static inline void adjustVelocityDataForBoundedScale(double& horizontalVelocity,
         scaleChangeRate = 0;
 }
 
+static inline FloatRect adjustExposedRectForBoundedScale(const FloatRect& exposedRect, double exposedRectScale, double newScale)
+{
+    if (exposedRectScale < newScale)
+        return exposedRect;
+
+    return adjustExposedRectForNewScale(exposedRect, exposedRectScale, newScale);
+}
+
 void WebPage::updateVisibleContentRects(const VisibleContentRectUpdateInfo& visibleContentRectUpdateInfo)
 {
+    // Skip any VisibleContentRectUpdate that have been queued before DidCommitLoad suppresses the updates in the UIProcess.
+    if (visibleContentRectUpdateInfo.lastLayerTreeTransactionID() <= m_lastLayerTreeTransactionIDBeforeDidCommitLoad)
+        return;
+
     m_hasReceivedVisibleContentRectsAfterDidCommitLoad = true;
-    m_obscuredTopInset = (visibleContentRectUpdateInfo.unobscuredRect().y() - visibleContentRectUpdateInfo.exposedRect().y()) * visibleContentRectUpdateInfo.scale();
 
     double scaleNoiseThreshold = 0.005;
     double filteredScale = visibleContentRectUpdateInfo.scale();
@@ -2315,7 +2377,7 @@ void WebPage::updateVisibleContentRects(const VisibleContentRectUpdateInfo& visi
 
     FloatRect exposedRect = visibleContentRectUpdateInfo.exposedRect();
     FloatRect adjustedExposedRect = adjustExposedRectForBoundedScale(exposedRect, visibleContentRectUpdateInfo.scale(), boundedScale);
-    m_drawingArea->setExposedContentRect(enclosingIntRect(adjustedExposedRect));
+    m_drawingArea->setExposedContentRect(adjustedExposedRect);
 
     IntRect roundedUnobscuredRect = roundedIntRect(visibleContentRectUpdateInfo.unobscuredRect());
     IntPoint scrollPosition = roundedUnobscuredRect.location();
index 1158f78..c68c298 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2014 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -53,6 +53,8 @@ public:
     RemoteLayerTreeDrawingArea(WebPage&, const WebPageCreationParameters&);
     virtual ~RemoteLayerTreeDrawingArea();
 
+    uint64_t currentTransactionID() const { return m_currentTransactionID; }
+
 private:
     // DrawingArea
     virtual void setNeedsDisplay() override;
index 254954d..a18bb62 100644 (file)
@@ -203,7 +203,7 @@ void RemoteLayerTreeDrawingArea::setExposedContentRect(const FloatRect& exposedC
     if (!frameView)
         return;
 
-    frameView->setExposedContentRect(enclosingIntRect(exposedContentRect));
+    frameView->setExposedContentRect(exposedContentRect);
     scheduleCompositingLayerFlush();
 }
 #endif