Add support for history.scrollRestoration
authorsimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 8 Mar 2017 20:01:15 +0000 (20:01 +0000)
committersimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 8 Mar 2017 20:01:15 +0000 (20:01 +0000)
https://bugs.webkit.org/show_bug.cgi?id=147782
rdar://problem/22614568

Reviewed by Sam Weinig.
LayoutTests/imported/w3c:

New passing baselines.

* web-platform-tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-basic-expected.txt:
* web-platform-tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin-expected.txt:
* web-platform-tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-cross-origin-expected.txt:
* web-platform-tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-samedoc-expected.txt:

Source/WebCore:

Add support for history.scrollRestoration, per spec:
<https://html.spec.whatwg.org/multipage/browsers.html#dom-history-scroll-restoration>

This is a new attribute on the History interface. On setting, sets the "shouldRestoreScrollPosition"
state on the current history item, and the getter returns that state. pushState() inherits the
state from the current item.

HistoryController::restoreScrollPositionAndViewState() consults this state, and if set to "manual"
("don't restore) it just uses the current scroll position (we need something to pass to
setPageScaleFactor() so can't just avoid the restoration).

FrameLoader::scrollToFragmentWithParentBoundary() also needs to consult the historyItem
to know if it's OK to scroll to a fragment, on back/forward same-document loads.

Tests: fast/history/history-scroll-restoration-attribute.html
       fast/history/history-scroll-restoration.html

* history/HistoryItem.cpp:
(WebCore::HistoryItem::HistoryItem):
(WebCore::HistoryItem::shouldRestoreScrollPosition):
(WebCore::HistoryItem::setShouldRestoreScrollPosition):
* history/HistoryItem.h:
* loader/FrameLoader.cpp:
(WebCore::FrameLoader::loadInSameDocument):
(WebCore::itemAllowsScrollRestoration):
(WebCore::isSameDocumentReload):
(WebCore::FrameLoader::scrollToFragmentWithParentBoundary):
(WebCore::FrameLoader::continueLoadAfterNavigationPolicy):
* loader/FrameLoader.h:
* loader/HistoryController.cpp:
(WebCore::HistoryController::restoreScrollPositionAndViewState):
(WebCore::HistoryController::goToItem):
(WebCore::HistoryController::pushState):
(WebCore::HistoryController::replaceState):
* page/History.cpp:
(WebCore::History::scrollRestoration):
(WebCore::History::setScrollRestoration):
* page/History.h:
* page/History.idl:

Source/WebKit2:

Need to send shouldRestoreScrollPosition to the UI process in SessionState,
WKWebView now stores _unobscuredCenterToRestore and _scrollOffsetToRestore as
std::optionals, and they will be nullopt if scroll restoration should not happen.

ViewGestureControllerIOS also needs knowledge of whether scroll restoration will
happen, and compares UI-process scroll position vs. the position at snapshot time
to know if the snapshot should be shown (this prevents showing a misleading snapshot
when swiping back on a navigation where scroll restoration is disabled).

* Shared/SessionState.cpp:
(WebKit::FrameState::encode):
(WebKit::FrameState::decode):
* Shared/SessionState.h:
* Shared/WebBackForwardListItem.h:
(WebKit::WebBackForwardListItem::pageState):
* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView _processDidExit]):
(-[WKWebView _didCommitLayerTree:]):
(-[WKWebView _restorePageScrollPosition:scrollOrigin:previousObscuredInset:scale:]):
(-[WKWebView _restorePageStateToUnobscuredCenter:scale:]):
* UIProcess/API/Cocoa/WKWebViewInternal.h:
* UIProcess/API/gtk/PageClientImpl.cpp:
(WebKit::PageClientImpl::viewScrollPosition):
* UIProcess/API/gtk/PageClientImpl.h:
* UIProcess/PageClient.h:
* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::viewScrollPosition):
* UIProcess/WebPageProxy.h:
* UIProcess/WebPageProxy.messages.in:
* UIProcess/ios/PageClientImplIOS.h:
* UIProcess/ios/PageClientImplIOS.mm:
(WebKit::PageClientImpl::viewScrollPosition):
(WebKit::PageClientImpl::restorePageState):
(WebKit::PageClientImpl::restorePageCenterAndScale):
* UIProcess/ios/ViewGestureControllerIOS.mm:
(WebKit::ViewGestureController::beginSwipeGesture):
* UIProcess/ios/WebPageProxyIOS.mm:
(WebKit::WebPageProxy::restorePageState):
(WebKit::WebPageProxy::restorePageCenterAndScale):
* UIProcess/mac/PageClientImpl.h:
* UIProcess/mac/PageClientImpl.mm:
(WebKit::PageClientImpl::viewScrollPosition):
* UIProcess/mac/ViewSnapshotStore.h:
(WebKit::ViewSnapshot::setViewScrollPosition):
(WebKit::ViewSnapshot::viewScrollPosition):
* UIProcess/mac/ViewSnapshotStore.mm:
(WebKit::ViewSnapshotStore::recordSnapshot):
* WebProcess/WebCoreSupport/SessionStateConversion.cpp:
(WebKit::toFrameState):
(WebKit::applyFrameState):
* WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp:
(WebKit::WebFrameLoaderClient::restoreViewState):
* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::restorePageState):

LayoutTests:

Sadly history-scroll-restoration.html needs to be cloned for iOS and to use uiController.doAfterPresentationUpdate()
there, since restoration involves a trip to the UI process, and this same test did not work for Mac WK1 (dispatch_async()
doesn't seem to give the right timing in DRT).

* TestExpectations:
* fast/dom/Window/window-appendages-cleared-expected.txt:
* fast/history/history-scroll-restoration-attribute-expected.txt: Added.
* fast/history/history-scroll-restoration-attribute.html: Added.
* fast/history/history-scroll-restoration-expected.txt: Added.
* fast/history/history-scroll-restoration.html: Added.
* platform/ios-simulator-wk2/TestExpectations:
* platform/ios-simulator/TestExpectations:

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

48 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/fast/dom/Window/window-appendages-cleared-expected.txt
LayoutTests/fast/history/history-scroll-restoration-attribute-expected.txt [new file with mode: 0644]
LayoutTests/fast/history/history-scroll-restoration-attribute.html [new file with mode: 0644]
LayoutTests/fast/history/history-scroll-restoration-expected.txt [new file with mode: 0644]
LayoutTests/fast/history/history-scroll-restoration.html [new file with mode: 0644]
LayoutTests/fast/history/ios/history-scroll-restoration-expected.txt [new file with mode: 0644]
LayoutTests/fast/history/ios/history-scroll-restoration.html [new file with mode: 0644]
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-basic-expected.txt
LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin-expected.txt
LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-cross-origin-expected.txt
LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-samedoc-expected.txt
LayoutTests/platform/ios-simulator-wk2/TestExpectations
LayoutTests/platform/ios-simulator/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/history/HistoryItem.cpp
Source/WebCore/history/HistoryItem.h
Source/WebCore/loader/FrameLoader.cpp
Source/WebCore/loader/FrameLoader.h
Source/WebCore/loader/HistoryController.cpp
Source/WebCore/page/History.cpp
Source/WebCore/page/History.h
Source/WebCore/page/History.idl
Source/WebKit2/ChangeLog
Source/WebKit2/Shared/SessionState.cpp
Source/WebKit2/Shared/SessionState.h
Source/WebKit2/Shared/WebBackForwardListItem.h
Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm
Source/WebKit2/UIProcess/API/Cocoa/WKWebViewInternal.h
Source/WebKit2/UIProcess/API/gtk/PageClientImpl.cpp
Source/WebKit2/UIProcess/API/gtk/PageClientImpl.h
Source/WebKit2/UIProcess/PageClient.h
Source/WebKit2/UIProcess/WebPageProxy.cpp
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/ViewGestureControllerIOS.mm
Source/WebKit2/UIProcess/ios/WebPageProxyIOS.mm
Source/WebKit2/UIProcess/mac/PageClientImpl.h
Source/WebKit2/UIProcess/mac/PageClientImpl.mm
Source/WebKit2/UIProcess/mac/ViewSnapshotStore.h
Source/WebKit2/UIProcess/mac/ViewSnapshotStore.mm
Source/WebKit2/WebProcess/WebCoreSupport/SessionStateConversion.cpp
Source/WebKit2/WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp
Source/WebKit2/WebProcess/WebPage/ios/WebPageIOS.mm

index 7252f31..8291ee4 100644 (file)
@@ -1,3 +1,24 @@
+2017-03-08  Simon Fraser  <simon.fraser@apple.com>
+
+        Add support for history.scrollRestoration
+        https://bugs.webkit.org/show_bug.cgi?id=147782
+        rdar://problem/22614568
+
+        Reviewed by Sam Weinig.
+
+        Sadly history-scroll-restoration.html needs to be cloned for iOS and to use uiController.doAfterPresentationUpdate()
+        there, since restoration involves a trip to the UI process, and this same test did not work for Mac WK1 (dispatch_async()
+        doesn't seem to give the right timing in DRT).
+
+        * TestExpectations:
+        * fast/dom/Window/window-appendages-cleared-expected.txt:
+        * fast/history/history-scroll-restoration-attribute-expected.txt: Added.
+        * fast/history/history-scroll-restoration-attribute.html: Added.
+        * fast/history/history-scroll-restoration-expected.txt: Added.
+        * fast/history/history-scroll-restoration.html: Added.
+        * platform/ios-simulator-wk2/TestExpectations:
+        * platform/ios-simulator/TestExpectations:
+
 2017-03-08  Chris Dumez  <cdumez@apple.com>
 
         [iOS] Throttle DOM timers to 30fps in low power mode
index 68bf136..699f475 100644 (file)
@@ -25,6 +25,7 @@ fast/viewport/ios [ Skip ]
 fast/visual-viewport/ios/ [ Skip ]
 fast/events/ios [ Skip ]
 fast/events/touch/ios [ Skip ]
+fast/history/ios [ Skip ]
 fast/scrolling/ios [ Skip ]
 scrollingcoordinator/ios [ Skip ]
 fast/content-observation [ Skip ]
@@ -1058,6 +1059,7 @@ webkit.org/b/168175 imported/w3c/web-platform-tests/html/semantics/embedded-cont
 webkit.org/b/168175 imported/w3c/web-platform-tests/html/browsers/browsing-the-web/history-traversal/same-url.html [ Pass Failure ]
 webkit.org/b/168175 imported/w3c/web-platform-tests/html/browsers/offline/browser-state/navigator_online_online.html [ Pass Failure ]
 webkit.org/b/168175 imported/w3c/web-platform-tests/html/semantics/text-level-semantics/the-a-element/a-download-click.html [ Timeout Pass ]
+webkit.org/b/169264 imported/w3c/web-platform-tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-samedoc.html [ Failure ]
 
 webkit.org/b/168066 performance-api/performance-now-api.html [ Pass Failure ]
 webkit.org/b/168005 performance-api/performance-now-time-origin-in-worker.html [ Pass Failure ]
index 58dbf63..78c8345 100644 (file)
@@ -4,6 +4,7 @@ PASS history.go == "LEFTOVER" is false
 PASS history.length == "LEFTOVER" is false
 PASS history.pushState == "LEFTOVER" is false
 PASS history.replaceState == "LEFTOVER" is false
+PASS history.scrollRestoration == "LEFTOVER" is false
 PASS history.state == "LEFTOVER" is false
 PASS location.ancestorOrigins == "LEFTOVER" is false
 PASS location.assign == "LEFTOVER" is false
diff --git a/LayoutTests/fast/history/history-scroll-restoration-attribute-expected.txt b/LayoutTests/fast/history/history-scroll-restoration-attribute-expected.txt
new file mode 100644 (file)
index 0000000..d5430e9
--- /dev/null
@@ -0,0 +1,14 @@
+Tests the history.scrollRestoration attribute
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS 'scrollRestoration' in history is true
+PASS history.scrollRestoration is "auto"
+PASS history.scrollRestoration is "manual"
+PASS history.scrollRestoration is "manual"
+PASS history.scrollRestoration is "auto"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/history/history-scroll-restoration-attribute.html b/LayoutTests/fast/history/history-scroll-restoration-attribute.html
new file mode 100644 (file)
index 0000000..229f5cc
--- /dev/null
@@ -0,0 +1,29 @@
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script>
+    description("Tests the history.scrollRestoration attribute");
+
+    if (window.testRunner)
+        testRunner.clearBackForwardList();
+
+    shouldBeTrue("'scrollRestoration' in history");
+
+    history.scrollRestoration = 'auto';
+    shouldBeEqualToString('history.scrollRestoration', 'auto');
+
+    history.scrollRestoration = 'manual';
+    shouldBeEqualToString('history.scrollRestoration', 'manual');
+
+    history.scrollRestoration = 'bananas';
+    shouldBeEqualToString('history.scrollRestoration', 'manual');
+
+    history.scrollRestoration = 'auto';
+    shouldBeEqualToString('history.scrollRestoration', 'auto');
+
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/history/history-scroll-restoration-expected.txt b/LayoutTests/fast/history/history-scroll-restoration-expected.txt
new file mode 100644 (file)
index 0000000..4c7025b
--- /dev/null
@@ -0,0 +1,16 @@
+Tests that history.scrollRestoration works for same-document navigations
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS 'scrollRestoration' in history is true
+PASS history.scrollRestoration is "auto"
+PASS window.scrollX is 123
+PASS window.scrollY is 456
+PASS history.scrollRestoration is "manual"
+PASS window.scrollX is 333
+PASS window.scrollY is 555
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/history/history-scroll-restoration.html b/LayoutTests/fast/history/history-scroll-restoration.html
new file mode 100644 (file)
index 0000000..5ae1cdd
--- /dev/null
@@ -0,0 +1,64 @@
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+<style>
+    body {
+        height: 2000px;
+        width: 2000px;
+    }
+</style>
+</head>
+<body>
+<script>
+    var jsTestIsAsync = true;
+
+    description("Tests that history.scrollRestoration works for same-document navigations");
+
+    if (window.testRunner)
+        testRunner.clearBackForwardList();
+
+    shouldBeTrue("'scrollRestoration' in history");
+    
+    // Can't create history entries inside the onload handler.
+    window.setTimeout(testAutoScrollRestoration, 0);
+
+    function testAutoScrollRestoration()
+    {
+        history.scrollRestoration = 'auto';
+        shouldBeEqualToString('history.scrollRestoration', 'auto');
+
+        window.scrollTo(123, 456);
+        history.pushState(null, '', '#1');
+        window.scrollTo(0, 0);
+        history.back();
+        
+        window.setTimeout(function () {
+            shouldBe('window.scrollX', '123');
+            shouldBe('window.scrollY', '456');
+
+            testManualScrollRestoration();
+        }, 0);
+    }
+
+    function testManualScrollRestoration()
+    {
+        history.scrollRestoration = 'manual';
+        shouldBeEqualToString('history.scrollRestoration', 'manual');
+
+        window.scrollTo(234, 567);
+        history.pushState(null, '', '#2');
+        window.scrollTo(333, 555);
+        history.back();
+        
+        window.setTimeout(function() {
+            shouldBe('window.scrollX', '333');
+            shouldBe('window.scrollY', '555');
+            window.scrollTo(0, 0);
+            finishJSTest();
+        }, 0);
+    }
+
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/history/ios/history-scroll-restoration-expected.txt b/LayoutTests/fast/history/ios/history-scroll-restoration-expected.txt
new file mode 100644 (file)
index 0000000..4c7025b
--- /dev/null
@@ -0,0 +1,16 @@
+Tests that history.scrollRestoration works for same-document navigations
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS 'scrollRestoration' in history is true
+PASS history.scrollRestoration is "auto"
+PASS window.scrollX is 123
+PASS window.scrollY is 456
+PASS history.scrollRestoration is "manual"
+PASS window.scrollX is 333
+PASS window.scrollY is 555
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/history/ios/history-scroll-restoration.html b/LayoutTests/fast/history/ios/history-scroll-restoration.html
new file mode 100644 (file)
index 0000000..ed24724
--- /dev/null
@@ -0,0 +1,74 @@
+<html>
+<head>
+<script src="../../../resources/js-test-pre.js"></script>
+<style>
+    body {
+        height: 2000px;
+        width: 2000px;
+    }
+</style>
+</head>
+<body>
+<script>
+    var jsTestIsAsync = true;
+
+    description("Tests that history.scrollRestoration works for same-document navigations");
+
+    if (window.testRunner)
+        testRunner.clearBackForwardList();
+
+    shouldBeTrue("'scrollRestoration' in history");
+    
+    // Can't create history entries inside the onload handler.
+    window.setTimeout(testAutoScrollRestoration, 0);
+
+    function waitForPresentationUpdateUIScript()
+    {
+        return `(function() {
+            uiController.doAfterPresentationUpdate(function() {
+                uiController.uiScriptComplete();
+            });
+        })()`;
+    }
+
+    function testAutoScrollRestoration()
+    {
+        history.scrollRestoration = 'auto';
+        shouldBeEqualToString('history.scrollRestoration', 'auto');
+
+        window.scrollTo(123, 456);
+        history.pushState(null, '', '#1');
+        window.scrollTo(0, 0);
+        history.back();
+        
+        testRunner.runUIScript(waitForPresentationUpdateUIScript(), function () {
+            shouldBe('window.scrollX', '123');
+            shouldBe('window.scrollY', '456');
+            
+            testManualScrollRestoration();
+        });
+    }
+
+    function testManualScrollRestoration()
+    {
+        history.scrollRestoration = 'manual';
+        shouldBeEqualToString('history.scrollRestoration', 'manual');
+
+        window.scrollTo(234, 567);
+        history.pushState(null, '', '#2');
+        window.scrollTo(333, 555);
+        history.back();
+        
+        testRunner.runUIScript(waitForPresentationUpdateUIScript(), function () {
+            shouldBe('window.scrollX', '333');
+            shouldBe('window.scrollY', '555');
+            
+            window.scrollTo(0, 0);
+            finishJSTest();
+        });
+    }
+
+</script>
+<script src="../../../resources/js-test-post.js"></script>
+</body>
+</html>
index 15aacea..892e52b 100644 (file)
@@ -1,3 +1,18 @@
+2017-03-08  Simon Fraser  <simon.fraser@apple.com>
+
+        Add support for history.scrollRestoration
+        https://bugs.webkit.org/show_bug.cgi?id=147782
+        rdar://problem/22614568
+
+        Reviewed by Sam Weinig.
+        
+        New passing baselines.
+
+        * web-platform-tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-basic-expected.txt:
+        * web-platform-tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin-expected.txt:
+        * web-platform-tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-cross-origin-expected.txt:
+        * web-platform-tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-samedoc-expected.txt:
+
 2017-03-07  Jiewen Tan  <jiewen_tan@apple.com>
 
         [WebCrypto] Implement ECDH ImportKey/ExportKey operations
index 3ea17f7..55a3594 100644 (file)
@@ -1,8 +1,5 @@
 Blocked access to external URL http://www.localhost:8800/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/blank1.html
-CONSOLE MESSAGE: line 2451: Error: assert_equals: navigating back should retain scrollRestoration value expected (string) "manual" but got (undefined) undefined
 
 
-Harness Error (TIMEOUT), message = null
-
-TIMEOUT Manual scroll restoration should take precedent over scrolling to fragment in cross origin navigation Test timed out
+PASS Manual scroll restoration should take precedent over scrolling to fragment in cross origin navigation 
 
index 5ab2293..4e77344 100644 (file)
@@ -6,6 +6,7 @@
 # Platform-specific directories. Skipped globally, then re-enabled here.
 #//////////////////////////////////////////////////////////////////////////////////////////
 
+fast/history/ios [ Pass ]
 fast/scrolling/ios [ Pass ]
 fast/viewport/ios [ Pass ]
 fast/visual-viewport/ios/ [ Pass ]
@@ -783,6 +784,7 @@ fast/history/form-submit-in-frame.html
 fast/history/gesture-before-onload-form-submit.html
 fast/history/gesture-before-onload-location-href.html
 fast/history/history_reload.html
+fast/history/history-scroll-restoration.html [ Skip ]
 fast/history/timed-refresh-in-cached-frame.html
 fast/history/window-open.html
 fast/images/animate-list-item-image-assertion.html
index 4c4ed13..e9707ec 100644 (file)
@@ -2439,6 +2439,8 @@ webkit.org/b/152935 fast/scrolling/scroll-position-on-reload-rtl.html [ Failure
 
 webkit.org/b/153371 imported/w3c/web-platform-tests/XMLHttpRequest/getresponseheader-chunked-trailer.htm [ Pass Failure ]
 
+webkit.org/b/169210 imported/w3c/web-platform-tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-samedoc.html [ Failure ]
+
 webkit.org/b/153498 svg/dom/SVGScriptElement/script-change-externalResourcesRequired-while-loading.svg [ Pass Timeout ]
 
 # displaylist tests are flaky on ios-simulator wk2
index 38c1957..2c0c961 100644 (file)
@@ -1,3 +1,51 @@
+2017-03-08  Simon Fraser  <simon.fraser@apple.com>
+
+        Add support for history.scrollRestoration
+        https://bugs.webkit.org/show_bug.cgi?id=147782
+        rdar://problem/22614568
+
+        Reviewed by Sam Weinig.
+
+        Add support for history.scrollRestoration, per spec:
+        <https://html.spec.whatwg.org/multipage/browsers.html#dom-history-scroll-restoration>
+
+        This is a new attribute on the History interface. On setting, sets the "shouldRestoreScrollPosition"
+        state on the current history item, and the getter returns that state. pushState() inherits the
+        state from the current item.
+
+        HistoryController::restoreScrollPositionAndViewState() consults this state, and if set to "manual"
+        ("don't restore) it just uses the current scroll position (we need something to pass to
+        setPageScaleFactor() so can't just avoid the restoration).
+
+        FrameLoader::scrollToFragmentWithParentBoundary() also needs to consult the historyItem
+        to know if it's OK to scroll to a fragment, on back/forward same-document loads.
+
+        Tests: fast/history/history-scroll-restoration-attribute.html
+               fast/history/history-scroll-restoration.html
+
+        * history/HistoryItem.cpp:
+        (WebCore::HistoryItem::HistoryItem):
+        (WebCore::HistoryItem::shouldRestoreScrollPosition):
+        (WebCore::HistoryItem::setShouldRestoreScrollPosition):
+        * history/HistoryItem.h:
+        * loader/FrameLoader.cpp:
+        (WebCore::FrameLoader::loadInSameDocument):
+        (WebCore::itemAllowsScrollRestoration):
+        (WebCore::isSameDocumentReload):
+        (WebCore::FrameLoader::scrollToFragmentWithParentBoundary):
+        (WebCore::FrameLoader::continueLoadAfterNavigationPolicy):
+        * loader/FrameLoader.h:
+        * loader/HistoryController.cpp:
+        (WebCore::HistoryController::restoreScrollPositionAndViewState):
+        (WebCore::HistoryController::goToItem):
+        (WebCore::HistoryController::pushState):
+        (WebCore::HistoryController::replaceState):
+        * page/History.cpp:
+        (WebCore::History::scrollRestoration):
+        (WebCore::History::setScrollRestoration):
+        * page/History.h:
+        * page/History.idl:
+
 2017-03-08  Chris Dumez  <cdumez@apple.com>
 
         [iOS] Throttle DOM timers to 30fps in low power mode
index 7a5fbbb..8a980b3 100644 (file)
@@ -56,10 +56,7 @@ static void defaultNotifyHistoryItemChanged(HistoryItem*)
 WEBCORE_EXPORT void (*notifyHistoryItemChanged)(HistoryItem*) = defaultNotifyHistoryItemChanged;
 
 HistoryItem::HistoryItem()
-    : m_pageScaleFactor(0)
-    , m_lastVisitWasFailure(false)
-    , m_isTargetItem(false)
-    , m_itemSequenceNumber(generateSequenceNumber())
+    : m_itemSequenceNumber(generateSequenceNumber())
     , m_documentSequenceNumber(generateSequenceNumber())
     , m_pruningReason(PruningReason::None)
 {
@@ -69,9 +66,6 @@ HistoryItem::HistoryItem(const String& urlString, const String& title)
     : m_urlString(urlString)
     , m_originalURLString(urlString)
     , m_title(title)
-    , m_pageScaleFactor(0)
-    , m_lastVisitWasFailure(false)
-    , m_isTargetItem(false)
     , m_itemSequenceNumber(generateSequenceNumber())
     , m_documentSequenceNumber(generateSequenceNumber())
     , m_pruningReason(PruningReason::None)
@@ -85,8 +79,6 @@ HistoryItem::HistoryItem(const String& urlString, const String& title, const Str
     , m_title(title)
     , m_displayTitle(alternateTitle)
     , m_pageScaleFactor(0)
-    , m_lastVisitWasFailure(false)
-    , m_isTargetItem(false)
     , m_itemSequenceNumber(generateSequenceNumber())
     , m_documentSequenceNumber(generateSequenceNumber())
     , m_pruningReason(PruningReason::None)
@@ -271,6 +263,16 @@ void HistoryItem::clearScrollPosition()
     m_scrollPosition = IntPoint();
 }
 
+bool HistoryItem::shouldRestoreScrollPosition() const
+{
+    return m_shouldRestoreScrollPosition;
+}
+
+void HistoryItem::setShouldRestoreScrollPosition(bool shouldRestore)
+{
+    m_shouldRestoreScrollPosition = shouldRestore;
+}
+
 float HistoryItem::pageScaleFactor() const
 {
     return m_pageScaleFactor;
index 60b0b8c..75cfc85 100644 (file)
@@ -102,6 +102,9 @@ public:
     WEBCORE_EXPORT const IntPoint& scrollPosition() const;
     WEBCORE_EXPORT void setScrollPosition(const IntPoint&);
     void clearScrollPosition();
+
+    WEBCORE_EXPORT bool shouldRestoreScrollPosition() const;
+    WEBCORE_EXPORT void setShouldRestoreScrollPosition(bool);
     
     WEBCORE_EXPORT float pageScaleFactor() const;
     WEBCORE_EXPORT void setPageScaleFactor(float);
@@ -215,16 +218,17 @@ private:
     String m_displayTitle;
     
     IntPoint m_scrollPosition;
-    float m_pageScaleFactor;
+    float m_pageScaleFactor { 0 }; // 0 indicates "unset".
     Vector<String> m_documentState;
 
     ShouldOpenExternalURLsPolicy m_shouldOpenExternalURLsPolicy { ShouldOpenExternalURLsPolicy::ShouldNotAllow };
     
     Vector<Ref<HistoryItem>> m_children;
     
-    bool m_lastVisitWasFailure;
-    bool m_isTargetItem;
+    bool m_lastVisitWasFailure { false };
+    bool m_isTargetItem { false };
     bool m_wasRestoredFromSession { false };
+    bool m_shouldRestoreScrollPosition { true };
 
     // If two HistoryItems have the same item sequence number, then they are
     // clones of one another.  Traversing history from one such HistoryItem to
index 06b9759..6851b8b 100644 (file)
@@ -1055,7 +1055,7 @@ void FrameLoader::loadInSameDocument(const URL& url, SerializedScriptValue* stat
 
     // We need to scroll to the fragment whether or not a hash change occurred, since
     // the user might have scrolled since the previous navigation.
-    scrollToFragmentWithParentBoundary(url);
+    scrollToFragmentWithParentBoundary(url, isNewNavigation);
     
     m_isComplete = false;
     checkCompleted();
@@ -2868,7 +2868,17 @@ bool FrameLoader::shouldPerformFragmentNavigation(bool isFormSubmission, const S
         && !m_frame.document()->isFrameSet();
 }
 
-void FrameLoader::scrollToFragmentWithParentBoundary(const URL& url)
+static bool itemAllowsScrollRestoration(HistoryItem* historyItem)
+{
+    return historyItem && historyItem->shouldRestoreScrollPosition();
+}
+
+static bool isSameDocumentReload(bool isNewNavigation, FrameLoadType loadType)
+{
+    return !isNewNavigation && !isBackForwardLoadType(loadType);
+}
+
+void FrameLoader::scrollToFragmentWithParentBoundary(const URL& url, bool isNewNavigation)
 {
     FrameView* view = m_frame.view();
     if (!view)
@@ -2880,7 +2890,8 @@ void FrameLoader::scrollToFragmentWithParentBoundary(const URL& url)
     if (boundaryFrame)
         boundaryFrame->view()->setSafeToPropagateScrollToParent(false);
 
-    view->scrollToFragment(url);
+    if (isSameDocumentReload(isNewNavigation, m_loadType) || itemAllowsScrollRestoration(history().currentItem()))
+        view->scrollToFragment(url);
 
     if (boundaryFrame)
         boundaryFrame->view()->setSafeToPropagateScrollToParent(true);
@@ -3082,7 +3093,7 @@ void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest& reque
         setPolicyDocumentLoader(nullptr);
 
         // If the navigation request came from the back/forward menu, and we punt on it, we have the 
-        // problem that we have optimistically moved the b/f cursor already, so move it back.  For sanity, 
+        // problem that we have optimistically moved the b/f cursor already, so move it back. For sanity,
         // we only do this when punting a navigation for the target frame or top-level frame.  
         if ((isTargetItem || m_frame.isMainFrame()) && isBackForwardLoadType(policyChecker().loadType())) {
             if (Page* page = m_frame.page()) {
index d69c829..6ad12c8 100644 (file)
@@ -334,7 +334,7 @@ private:
     void continueFragmentScrollAfterNavigationPolicy(const ResourceRequest&, bool shouldContinue);
 
     bool shouldPerformFragmentNavigation(bool isFormSubmission, const String& httpMethod, FrameLoadType, const URL&);
-    void scrollToFragmentWithParentBoundary(const URL&);
+    void scrollToFragmentWithParentBoundary(const URL&, bool isNewNavigation = true);
 
     void checkLoadCompleteForThisFrame();
 
index 417446e..a1f5a28 100644 (file)
@@ -158,7 +158,7 @@ void HistoryController::restoreScrollPositionAndViewState()
     // Don't restore scroll point on iOS as FrameLoaderClient::restoreViewState() does that.
     if (view && !view->wasScrolledByUser()) {
         Page* page = m_frame.page();
-        auto desiredScrollPosition = m_currentItem->scrollPosition();
+        auto desiredScrollPosition = m_currentItem->shouldRestoreScrollPosition() ? m_currentItem->scrollPosition() : view->scrollPosition();
         LOG(Scrolling, "HistoryController::restoreScrollPositionAndViewState scrolling to %d,%d", desiredScrollPosition.x(), desiredScrollPosition.y());
         if (page && m_frame.isMainFrame() && m_currentItem->pageScaleFactor())
             page->setPageScaleFactor(m_currentItem->pageScaleFactor() * page->viewScaleFactor(), desiredScrollPosition);
@@ -291,6 +291,8 @@ bool HistoryController::shouldStopLoadingForHistoryItem(HistoryItem& targetItem)
 // This includes recursion to handle loading into framesets properly
 void HistoryController::goToItem(HistoryItem& targetItem, FrameLoadType type)
 {
+    LOG(History, "HistoryController %p goToItem %p type=%d", this, &targetItem, type);
+
     ASSERT(!m_frame.tree().parent());
     
     // shouldGoToHistoryItem is a private delegate method. This is needed to fix:
@@ -853,6 +855,8 @@ void HistoryController::pushState(RefPtr<SerializedScriptValue>&& stateObject, c
     Page* page = m_frame.page();
     ASSERT(page);
 
+    bool shouldRestoreScrollPosition = m_currentItem->shouldRestoreScrollPosition();
+    
     // Get a HistoryItem tree for the current frame tree.
     Ref<HistoryItem> topItem = m_frame.mainFrame().loader().history().createItemTree(m_frame, false);
     
@@ -861,8 +865,9 @@ void HistoryController::pushState(RefPtr<SerializedScriptValue>&& stateObject, c
     m_currentItem->setTitle(title);
     m_currentItem->setStateObject(WTFMove(stateObject));
     m_currentItem->setURLString(urlString);
+    m_currentItem->setShouldRestoreScrollPosition(shouldRestoreScrollPosition);
 
-    LOG(History, "HistoryController %p pushState: Adding top item %p, setting url of current item %p to %s", this, topItem.ptr(), m_currentItem.get(), urlString.ascii().data());
+    LOG(History, "HistoryController %p pushState: Adding top item %p, setting url of current item %p to %s, scrollRestoration is %s", this, topItem.ptr(), m_currentItem.get(), urlString.ascii().data(), topItem->shouldRestoreScrollPosition() ? "auto" : "manual");
 
     page->backForward().addItem(WTFMove(topItem));
 
@@ -878,7 +883,7 @@ void HistoryController::replaceState(RefPtr<SerializedScriptValue>&& stateObject
     if (!m_currentItem)
         return;
 
-    LOG(History, "HistoryController %p replaceState: Setting url of current item %p to %s", this, m_currentItem.get(), urlString.ascii().data());
+    LOG(History, "HistoryController %p replaceState: Setting url of current item %p to %s scrollRestoration %s", this, m_currentItem.get(), urlString.ascii().data(), m_currentItem->shouldRestoreScrollPosition() ? "auto" : "manual");
 
     if (!urlString.isEmpty())
         m_currentItem->setURLString(urlString);
index fbc52f3..f7cf85c 100644 (file)
@@ -59,6 +59,30 @@ unsigned History::length() const
     return page->backForward().count();
 }
 
+ExceptionOr<History::ScrollRestoration> History::scrollRestoration() const
+{
+    if (!m_frame)
+        return Exception { SECURITY_ERR };
+
+    auto* historyItem = m_frame->loader().history().currentItem();
+    if (!historyItem)
+        return ScrollRestoration::Auto;
+    
+    return historyItem->shouldRestoreScrollPosition() ? ScrollRestoration::Auto : ScrollRestoration::Manual;
+}
+
+ExceptionOr<void> History::setScrollRestoration(ScrollRestoration scrollRestoration)
+{
+    if (!m_frame)
+        return Exception { SECURITY_ERR };
+
+    auto* historyItem = m_frame->loader().history().currentItem();
+    if (historyItem)
+        historyItem->setShouldRestoreScrollPosition(scrollRestoration == ScrollRestoration::Auto);
+
+    return { };
+}
+
 SerializedScriptValue* History::state()
 {
     m_lastStateObjectRequested = stateInternal();
index e722a08..6dbb2ff 100644 (file)
@@ -41,6 +41,15 @@ public:
     static Ref<History> create(Frame& frame) { return adoptRef(*new History(frame)); }
 
     unsigned length() const;
+    
+    enum class ScrollRestoration {
+        Auto,
+        Manual
+    };
+
+    ExceptionOr<ScrollRestoration> scrollRestoration() const;
+    ExceptionOr<void> setScrollRestoration(ScrollRestoration);
+
     SerializedScriptValue* state();
     void back();
     void forward();
index 8d9a669..13aff5d 100644 (file)
@@ -27,6 +27,7 @@
     GenerateIsReachable=ImplFrame,
 ] interface History {
     readonly attribute unsigned long length;
+    [SetterMayThrowException, GetterMayThrowException] attribute ScrollRestoration scrollRestoration;
     [CachedAttribute, Custom] readonly attribute SerializedScriptValue state;
 
     [CallWith=Document, ForwardDeclareInHeader] void back();
@@ -36,3 +37,5 @@
     [Custom, MayThrowException] void pushState(any data, DOMString title, optional USVString? url = null);
     [Custom, MayThrowException] void replaceState(any data, DOMString title, optional USVString? url = null);
 };
+
+enum ScrollRestoration { "auto", "manual" };
index 4c00a68..c9aba82 100644 (file)
@@ -1,3 +1,66 @@
+2017-03-08  Simon Fraser  <simon.fraser@apple.com>
+
+        Add support for history.scrollRestoration
+        https://bugs.webkit.org/show_bug.cgi?id=147782
+        rdar://problem/22614568
+
+        Reviewed by Sam Weinig.
+        
+        Need to send shouldRestoreScrollPosition to the UI process in SessionState,
+        WKWebView now stores _unobscuredCenterToRestore and _scrollOffsetToRestore as
+        std::optionals, and they will be nullopt if scroll restoration should not happen.
+
+        ViewGestureControllerIOS also needs knowledge of whether scroll restoration will
+        happen, and compares UI-process scroll position vs. the position at snapshot time
+        to know if the snapshot should be shown (this prevents showing a misleading snapshot
+        when swiping back on a navigation where scroll restoration is disabled).
+
+        * Shared/SessionState.cpp:
+        (WebKit::FrameState::encode):
+        (WebKit::FrameState::decode):
+        * Shared/SessionState.h:
+        * Shared/WebBackForwardListItem.h:
+        (WebKit::WebBackForwardListItem::pageState):
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView _processDidExit]):
+        (-[WKWebView _didCommitLayerTree:]):
+        (-[WKWebView _restorePageScrollPosition:scrollOrigin:previousObscuredInset:scale:]):
+        (-[WKWebView _restorePageStateToUnobscuredCenter:scale:]):
+        * UIProcess/API/Cocoa/WKWebViewInternal.h:
+        * UIProcess/API/gtk/PageClientImpl.cpp:
+        (WebKit::PageClientImpl::viewScrollPosition):
+        * UIProcess/API/gtk/PageClientImpl.h:
+        * UIProcess/PageClient.h:
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::viewScrollPosition):
+        * UIProcess/WebPageProxy.h:
+        * UIProcess/WebPageProxy.messages.in:
+        * UIProcess/ios/PageClientImplIOS.h:
+        * UIProcess/ios/PageClientImplIOS.mm:
+        (WebKit::PageClientImpl::viewScrollPosition):
+        (WebKit::PageClientImpl::restorePageState):
+        (WebKit::PageClientImpl::restorePageCenterAndScale):
+        * UIProcess/ios/ViewGestureControllerIOS.mm:
+        (WebKit::ViewGestureController::beginSwipeGesture):
+        * UIProcess/ios/WebPageProxyIOS.mm:
+        (WebKit::WebPageProxy::restorePageState):
+        (WebKit::WebPageProxy::restorePageCenterAndScale):
+        * UIProcess/mac/PageClientImpl.h:
+        * UIProcess/mac/PageClientImpl.mm:
+        (WebKit::PageClientImpl::viewScrollPosition):
+        * UIProcess/mac/ViewSnapshotStore.h:
+        (WebKit::ViewSnapshot::setViewScrollPosition):
+        (WebKit::ViewSnapshot::viewScrollPosition):
+        * UIProcess/mac/ViewSnapshotStore.mm:
+        (WebKit::ViewSnapshotStore::recordSnapshot):
+        * WebProcess/WebCoreSupport/SessionStateConversion.cpp:
+        (WebKit::toFrameState):
+        (WebKit::applyFrameState):
+        * WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp:
+        (WebKit::WebFrameLoaderClient::restoreViewState):
+        * WebProcess/WebPage/ios/WebPageIOS.mm:
+        (WebKit::WebPage::restorePageState):
+
 2017-03-08  Matt Rajca  <mrajca@apple.com>
 
         Add support for updating autoplay policies after a page has been loaded.
index 0f048a4..7fb60ae 100644 (file)
@@ -114,6 +114,7 @@ void FrameState::encode(IPC::Encoder& encoder) const
     encoder << itemSequenceNumber;
 
     encoder << scrollPosition;
+    encoder << shouldRestoreScrollPosition;
     encoder << pageScaleFactor;
 
     encoder << httpBody;
@@ -152,6 +153,8 @@ bool FrameState::decode(IPC::Decoder& decoder, FrameState& result)
 
     if (!decoder.decode(result.scrollPosition))
         return false;
+    if (!decoder.decode(result.shouldRestoreScrollPosition))
+        return false;
     if (!decoder.decode(result.pageScaleFactor))
         return false;
 
index 0ff7bba..a56a8db 100644 (file)
@@ -96,6 +96,7 @@ struct FrameState {
     int64_t itemSequenceNumber;
 
     WebCore::IntPoint scrollPosition;
+    bool shouldRestoreScrollPosition;
     float pageScaleFactor;
 
     std::optional<HTTPBody> httpBody;
index df4573e..1b754f3 100644 (file)
@@ -52,6 +52,7 @@ public:
     uint64_t pageID() const { return m_pageID; }
 
     void setPageState(PageState pageState) { m_itemState.pageState = WTFMove(pageState); }
+    const PageState& pageState() const { return m_itemState.pageState; }
 
     const String& originalURL() const { return m_itemState.pageState.mainFrameState.originalURLString; }
     const String& url() const { return m_itemState.pageState.mainFrameState.urlString; }
index a6986d2..897a2b7 100644 (file)
@@ -251,13 +251,11 @@ WKWebView* fromWebPageProxy(WebKit::WebPageProxy& page)
     std::optional<CGRect> _frozenVisibleContentRect;
     std::optional<CGRect> _frozenUnobscuredContentRect;
 
-    BOOL _needsToRestoreScrollPosition;
     BOOL _commitDidRestoreScrollPosition;
-    WebCore::FloatPoint _scrollOffsetToRestore;
+    std::optional<WebCore::FloatPoint> _scrollOffsetToRestore;
     WebCore::FloatSize _obscuredInsetWhenSaved;
 
-    BOOL _needsToRestoreUnobscuredCenter;
-    WebCore::FloatPoint _unobscuredCenterToRestore;
+    std::optional<WebCore::FloatPoint> _unobscuredCenterToRestore;
     uint64_t _firstTransactionIDAfterPageRestore;
     double _scaleToRestore;
 
@@ -1369,8 +1367,8 @@ static WebCore::Color scrollViewBackgroundColor(WKWebView *webView)
     _needsResetViewStateAfterCommitLoadForMainFrame = NO;
     _dynamicViewportUpdateMode = DynamicViewportUpdateMode::NotResizing;
     [_contentView setHidden:NO];
-    _needsToRestoreScrollPosition = NO;
-    _needsToRestoreUnobscuredCenter = NO;
+    _scrollOffsetToRestore = std::nullopt;
+    _unobscuredCenterToRestore = std::nullopt;
     _scrollViewBackgroundColor = WebCore::Color();
     _delayUpdateVisibleContentRects = NO;
     _hadDelayedUpdateVisibleContentRects = NO;
@@ -1490,43 +1488,40 @@ static inline bool areEssentiallyEqualAsFloat(float a, float b)
         needUpdateVisbleContentRects = YES;
     }
 
-    bool isTransactionAfterPageRestore = layerTreeTransaction.transactionID() >= _firstTransactionIDAfterPageRestore;
+    if (layerTreeTransaction.transactionID() >= _firstTransactionIDAfterPageRestore) {
+        if (_scrollOffsetToRestore) {
+            WebCore::FloatPoint scaledScrollOffset = _scrollOffsetToRestore.value();
+            _scrollOffsetToRestore = std::nullopt;
 
-    if (_needsToRestoreScrollPosition && isTransactionAfterPageRestore) {
-        _needsToRestoreScrollPosition = NO;
+            if (areEssentiallyEqualAsFloat(contentZoomScale(self), _scaleToRestore)) {
+                scaledScrollOffset.scale(_scaleToRestore);
+                WebCore::FloatPoint contentOffsetInScrollViewCoordinates = scaledScrollOffset - _obscuredInsetWhenSaved;
 
-        if (areEssentiallyEqualAsFloat(contentZoomScale(self), _scaleToRestore)) {
-            WebCore::FloatPoint scaledScrollOffset = _scrollOffsetToRestore;
-            scaledScrollOffset.scale(_scaleToRestore);
-            WebCore::FloatPoint contentOffsetInScrollViewCoordinates = scaledScrollOffset - _obscuredInsetWhenSaved;
-
-            changeContentOffsetBoundedInValidRange(_scrollView.get(), contentOffsetInScrollViewCoordinates);
-            _commitDidRestoreScrollPosition = YES;
-
-            if (_gestureController)
-                _gestureController->didRestoreScrollPosition();
+                changeContentOffsetBoundedInValidRange(_scrollView.get(), contentOffsetInScrollViewCoordinates);
+                _commitDidRestoreScrollPosition = YES;
+            }
         }
-        
-        needUpdateVisbleContentRects = YES;
-    }
 
-    if (_needsToRestoreUnobscuredCenter && isTransactionAfterPageRestore) {
-        _needsToRestoreUnobscuredCenter = NO;
+        if (_unobscuredCenterToRestore) {
+            WebCore::FloatPoint unobscuredCenterToRestore = _unobscuredCenterToRestore.value();
+            _unobscuredCenterToRestore = std::nullopt;
 
-        if (areEssentiallyEqualAsFloat(contentZoomScale(self), _scaleToRestore)) {
-            CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, _obscuredInsets);
-            WebCore::FloatSize unobscuredContentSizeAtNewScale(unobscuredRect.size.width / _scaleToRestore, unobscuredRect.size.height / _scaleToRestore);
-            WebCore::FloatPoint topLeftInDocumentCoordinates(_unobscuredCenterToRestore.x() - unobscuredContentSizeAtNewScale.width() / 2, _unobscuredCenterToRestore.y() - unobscuredContentSizeAtNewScale.height() / 2);
+            if (areEssentiallyEqualAsFloat(contentZoomScale(self), _scaleToRestore)) {
+                CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, _obscuredInsets);
+                WebCore::FloatSize unobscuredContentSizeAtNewScale(unobscuredRect.size.width / _scaleToRestore, unobscuredRect.size.height / _scaleToRestore);
+                WebCore::FloatPoint topLeftInDocumentCoordinates(unobscuredCenterToRestore.x() - unobscuredContentSizeAtNewScale.width() / 2, unobscuredCenterToRestore.y() - unobscuredContentSizeAtNewScale.height() / 2);
 
-            topLeftInDocumentCoordinates.scale(_scaleToRestore);
-            topLeftInDocumentCoordinates.moveBy(WebCore::FloatPoint(-_obscuredInsets.left, -_obscuredInsets.top));
+                topLeftInDocumentCoordinates.scale(_scaleToRestore);
+                topLeftInDocumentCoordinates.moveBy(WebCore::FloatPoint(-_obscuredInsets.left, -_obscuredInsets.top));
 
-            changeContentOffsetBoundedInValidRange(_scrollView.get(), topLeftInDocumentCoordinates);
-            if (_gestureController)
-                _gestureController->didRestoreScrollPosition();
+                changeContentOffsetBoundedInValidRange(_scrollView.get(), topLeftInDocumentCoordinates);
+            }
         }
 
         needUpdateVisbleContentRects = YES;
+
+        if (_gestureController)
+            _gestureController->didRestoreScrollPosition();
     }
 
     if (needUpdateVisbleContentRects)
@@ -1568,7 +1563,7 @@ static inline bool areEssentiallyEqualAsFloat(float a, float b)
         _gestureController->didRestoreScrollPosition();
 }
 
-- (void)_restorePageScrollPosition:(WebCore::FloatPoint)scrollPosition scrollOrigin:(WebCore::FloatPoint)scrollOrigin previousObscuredInset:(WebCore::FloatSize)obscuredInset scale:(double)scale
+- (void)_restorePageScrollPosition:(std::optional<WebCore::FloatPoint>)scrollPosition scrollOrigin:(WebCore::FloatPoint)scrollOrigin previousObscuredInset:(WebCore::FloatSize)obscuredInset scale:(double)scale
 {
     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
         return;
@@ -1576,16 +1571,17 @@ static inline bool areEssentiallyEqualAsFloat(float a, float b)
     if (![self usesStandardContentView])
         return;
 
-    _needsToRestoreUnobscuredCenter = NO;
-    _needsToRestoreScrollPosition = YES;
     _firstTransactionIDAfterPageRestore = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).nextLayerTreeTransactionID();
-    
-    _scrollOffsetToRestore = WebCore::ScrollableArea::scrollOffsetFromPosition(WebCore::FloatPoint(scrollPosition), WebCore::toFloatSize(scrollOrigin));
+    if (scrollPosition)
+        _scrollOffsetToRestore = WebCore::ScrollableArea::scrollOffsetFromPosition(WebCore::FloatPoint(scrollPosition.value()), WebCore::toFloatSize(scrollOrigin));
+    else
+        _scrollOffsetToRestore = std::nullopt;
+
     _obscuredInsetWhenSaved = obscuredInset;
     _scaleToRestore = scale;
 }
 
-- (void)_restorePageStateToUnobscuredCenter:(WebCore::FloatPoint)center scale:(double)scale
+- (void)_restorePageStateToUnobscuredCenter:(std::optional<WebCore::FloatPoint>)center scale:(double)scale
 {
     if (_dynamicViewportUpdateMode != DynamicViewportUpdateMode::NotResizing)
         return;
@@ -1593,10 +1589,9 @@ static inline bool areEssentiallyEqualAsFloat(float a, float b)
     if (![self usesStandardContentView])
         return;
 
-    _needsToRestoreScrollPosition = NO;
-    _needsToRestoreUnobscuredCenter = YES;
     _firstTransactionIDAfterPageRestore = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).nextLayerTreeTransactionID();
-    _unobscuredCenterToRestore = center;
+    _unobscuredCenterToRestore = center.value();
+    
     _scaleToRestore = scale;
 }
 
index 70dc378..778d4f6 100644 (file)
@@ -81,8 +81,8 @@ struct PrintInfo;
 
 - (void)_dynamicViewportUpdateChangedTargetToScale:(double)newScale position:(CGPoint)newScrollPosition nextValidLayerTreeTransactionID:(uint64_t)nextValidLayerTreeTransactionID;
 - (void)_couldNotRestorePageState;
-- (void)_restorePageScrollPosition:(WebCore::FloatPoint)scrollPosition scrollOrigin:(WebCore::FloatPoint)scrollOrigin previousObscuredInset:(WebCore::FloatSize)topInset scale:(double)scale;
-- (void)_restorePageStateToUnobscuredCenter:(WebCore::FloatPoint)center scale:(double)scale; // FIXME: needs scroll origin?
+- (void)_restorePageScrollPosition:(std::optional<WebCore::FloatPoint>)scrollPosition scrollOrigin:(WebCore::FloatPoint)scrollOrigin previousObscuredInset:(WebCore::FloatSize)topInset scale:(double)scale;
+- (void)_restorePageStateToUnobscuredCenter:(std::optional<WebCore::FloatPoint>)center scale:(double)scale; // FIXME: needs scroll origin?
 
 - (PassRefPtr<WebKit::ViewSnapshot>)_takeViewSnapshot;
 
index 8158550..fb7c85e 100644 (file)
@@ -75,6 +75,11 @@ void PageClientImpl::requestScroll(const WebCore::FloatPoint&, const WebCore::In
     notImplemented();
 }
 
+WebCore::FloatPoint PageClientImpl::viewScrollPosition()
+{
+    return { };
+}
+
 WebCore::IntSize PageClientImpl::viewSize()
 {
     auto* drawingArea = static_cast<DrawingAreaProxyImpl*>(webkitWebViewBaseGetPage(WEBKIT_WEB_VIEW_BASE(m_viewWidget))->drawingArea());
index 8d4a310..8c5776f 100644 (file)
@@ -57,6 +57,7 @@ private:
     std::unique_ptr<DrawingAreaProxy> createDrawingAreaProxy() override;
     void setViewNeedsDisplay(const WebCore::Region&) override;
     void requestScroll(const WebCore::FloatPoint& scrollPosition, const WebCore::IntPoint& scrollOrigin, bool isProgrammaticScroll) override;
+    WebCore::FloatPoint viewScrollPosition() override;
     WebCore::IntSize viewSize() override;
     bool isViewWindowActive() override;
     bool isViewFocused() override;
index e124cc3..58b2ca9 100644 (file)
@@ -100,6 +100,9 @@ public:
     // Tell the view to scroll to the given position, and whether this was a programmatic scroll.
     virtual void requestScroll(const WebCore::FloatPoint& scrollPosition, const WebCore::IntPoint& scrollOrigin, bool isProgrammaticScroll) = 0;
 
+    // Return the current scroll position (not necessarily the same as the WebCore scroll position, because of scaling, insets etc.)
+    virtual WebCore::FloatPoint viewScrollPosition() = 0;
+
     // Return the size of the view the page is associated with.
     virtual WebCore::IntSize viewSize() = 0;
 
@@ -294,8 +297,8 @@ public:
 
     virtual void dynamicViewportUpdateChangedTarget(double newScale, const WebCore::FloatPoint& newScrollPosition, uint64_t transactionID) = 0;
     virtual void couldNotRestorePageState() = 0;
-    virtual void restorePageState(const WebCore::FloatPoint& scrollPosition, const WebCore::FloatPoint& scrollOrigin, const WebCore::FloatSize& obscuredInsetOnSave, double scale) = 0;
-    virtual void restorePageCenterAndScale(const WebCore::FloatPoint& center, double scale) = 0;
+    virtual void restorePageState(std::optional<WebCore::FloatPoint> scrollPosition, const WebCore::FloatPoint& scrollOrigin, const WebCore::FloatSize& obscuredInsetOnSave, double scale) = 0;
+    virtual void restorePageCenterAndScale(std::optional<WebCore::FloatPoint> center, double scale) = 0;
 
     virtual void startAssistingNode(const AssistedNodeInformation&, bool userIsInteracting, bool blurPreviousNode, API::Object* userData) = 0;
     virtual void stopAssistingNode() = 0;
index 7507171..122f6a1 100644 (file)
@@ -1435,6 +1435,11 @@ void WebPageProxy::requestScroll(const FloatPoint& scrollPosition, const IntPoin
     m_pageClient.requestScroll(scrollPosition, scrollOrigin, isProgrammaticScroll);
 }
 
+WebCore::FloatPoint WebPageProxy::viewScrollPosition() const
+{
+    return m_pageClient.viewScrollPosition();
+}
+
 void WebPageProxy::setSuppressVisibilityUpdates(bool flag)
 {
     if (m_suppressVisibilityUpdates == flag)
index 566fd0e..64d7c61 100644 (file)
@@ -446,6 +446,8 @@ public:
     void setViewNeedsDisplay(const WebCore::Region&);
     void requestScroll(const WebCore::FloatPoint& scrollPosition, const WebCore::IntPoint& scrollOrigin, bool isProgrammaticScroll);
     
+    WebCore::FloatPoint viewScrollPosition() const;
+
     void setDelegatesScrolling(bool delegatesScrolling) { m_delegatesScrolling = delegatesScrolling; }
     bool delegatesScrolling() const { return m_delegatesScrolling; }
 
@@ -1511,8 +1513,8 @@ private:
 
     void dynamicViewportUpdateChangedTarget(double newTargetScale, const WebCore::FloatPoint& newScrollPosition, uint64_t dynamicViewportSizeUpdateID);
     void couldNotRestorePageState();
-    void restorePageState(const WebCore::FloatPoint& scrollPosition, const WebCore::FloatPoint& scrollOrigin, const WebCore::FloatSize& obscuredInsetOnSave, double scale);
-    void restorePageCenterAndScale(const WebCore::FloatPoint&, double scale);
+    void restorePageState(std::optional<WebCore::FloatPoint> scrollPosition, const WebCore::FloatPoint& scrollOrigin, const WebCore::FloatSize& obscuredInsetOnSave, double scale);
+    void restorePageCenterAndScale(std::optional<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);
 
index 11a0da9..23db107 100644 (file)
@@ -376,8 +376,8 @@ messages -> WebPageProxy {
 #if PLATFORM(IOS)
     DynamicViewportUpdateChangedTarget(double newTargetScale, WebCore::FloatPoint newScrollPosition, uint64_t dynamicViewportSizeUpdateID)
     CouldNotRestorePageState()
-    RestorePageState(WebCore::FloatPoint scrollPosition, WebCore::FloatPoint scrollOrigin, WebCore::FloatSize obscuredInsetOnSave, double scale)
-    RestorePageCenterAndScale(WebCore::FloatPoint unobscuredCenter, double scale)
+    RestorePageState(std::optional<WebCore::FloatPoint> scrollPosition, WebCore::FloatPoint scrollOrigin, WebCore::FloatSize obscuredInsetOnSave, double scale)
+    RestorePageCenterAndScale(std::optional<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(struct WebKit::AssistedNodeInformation information, bool userIsInteracting, bool blurPreviousNode, WebKit::UserData userData)
index 6c8574d..dee7b65 100644 (file)
@@ -51,6 +51,7 @@ private:
     std::unique_ptr<DrawingAreaProxy> createDrawingAreaProxy() override;
     void setViewNeedsDisplay(const WebCore::Region&) override;
     void requestScroll(const WebCore::FloatPoint& scrollPosition, const WebCore::IntPoint& scrollOrigin, bool isProgrammaticScroll) override;
+    WebCore::FloatPoint viewScrollPosition() override;
     WebCore::IntSize viewSize() override;
     bool isViewWindowActive() override;
     bool isViewFocused() override;
@@ -121,8 +122,8 @@ private:
 
     void dynamicViewportUpdateChangedTarget(double newScale, const WebCore::FloatPoint& newScrollPosition, uint64_t transactionID) override;
     void couldNotRestorePageState() override;
-    void restorePageState(const WebCore::FloatPoint&, const WebCore::FloatPoint&, const WebCore::FloatSize&, double) override;
-    void restorePageCenterAndScale(const WebCore::FloatPoint&, double) override;
+    void restorePageState(std::optional<WebCore::FloatPoint>, const WebCore::FloatPoint&, const WebCore::FloatSize&, double) override;
+    void restorePageCenterAndScale(std::optional<WebCore::FloatPoint>, double) override;
 
     void startAssistingNode(const AssistedNodeInformation&, bool userIsInteracting, bool blurPreviousNode, API::Object* userData) override;
     void stopAssistingNode() override;
index 6ec78b6..367ea12 100644 (file)
@@ -137,6 +137,14 @@ void PageClientImpl::requestScroll(const FloatPoint& scrollPosition, const IntPo
     [m_webView _scrollToContentScrollPosition:scrollPosition scrollOrigin:scrollOrigin];
 }
 
+WebCore::FloatPoint PageClientImpl::viewScrollPosition()
+{
+    if (UIScrollView *scroller = [m_contentView _scroller])
+        return scroller.contentOffset;
+
+    return { };
+}
+
 IntSize PageClientImpl::viewSize()
 {
     if (UIScrollView *scroller = [m_contentView _scroller])
@@ -526,12 +534,12 @@ void PageClientImpl::couldNotRestorePageState()
     [m_webView _couldNotRestorePageState];
 }
 
-void PageClientImpl::restorePageState(const WebCore::FloatPoint& scrollPosition, const WebCore::FloatPoint& scrollOrigin, const WebCore::FloatSize& obscuredInsetOnSave, double scale)
+void PageClientImpl::restorePageState(std::optional<WebCore::FloatPoint> scrollPosition, const WebCore::FloatPoint& scrollOrigin, const WebCore::FloatSize& obscuredInsetOnSave, double scale)
 {
     [m_webView _restorePageScrollPosition:scrollPosition scrollOrigin:scrollOrigin previousObscuredInset:obscuredInsetOnSave scale:scale];
 }
 
-void PageClientImpl::restorePageCenterAndScale(const WebCore::FloatPoint& center, double scale)
+void PageClientImpl::restorePageCenterAndScale(std::optional<WebCore::FloatPoint> center, double scale)
 {
     [m_webView _restorePageStateToUnobscuredCenter:center scale:scale];
 }
index 831fb83..9350b6c 100644 (file)
@@ -194,7 +194,11 @@ void ViewGestureController::beginSwipeGesture(_UINavigationInteractiveTransition
         float deviceScaleFactor = m_webPageProxy.deviceScaleFactor();
         FloatSize swipeLayerSizeInDeviceCoordinates(liveSwipeViewFrame.size);
         swipeLayerSizeInDeviceCoordinates.scale(deviceScaleFactor);
-        if (snapshot->hasImage() && snapshot->size() == swipeLayerSizeInDeviceCoordinates && deviceScaleFactor == snapshot->deviceScaleFactor())
+        
+        BOOL shouldRestoreScrollPosition = targetItem->pageState().mainFrameState.shouldRestoreScrollPosition;
+        IntPoint currentScrollPosition = roundedIntPoint(m_webPageProxy.viewScrollPosition());
+
+        if (snapshot->hasImage() && snapshot->size() == swipeLayerSizeInDeviceCoordinates && deviceScaleFactor == snapshot->deviceScaleFactor() && (shouldRestoreScrollPosition || (currentScrollPosition == snapshot->viewScrollPosition())))
             [m_snapshotView layer].contents = snapshot->asLayerContents();
         Color coreColor = snapshot->backgroundColor();
         if (coreColor.isValid())
index f767c5f..3a7a6d7 100644 (file)
@@ -906,12 +906,12 @@ void WebPageProxy::couldNotRestorePageState()
     m_pageClient.couldNotRestorePageState();
 }
 
-void WebPageProxy::restorePageState(const WebCore::FloatPoint& scrollPosition, const WebCore::FloatPoint& scrollOrigin, const WebCore::FloatSize& obscuredInsetOnSave, double scale)
+void WebPageProxy::restorePageState(std::optional<WebCore::FloatPoint> scrollPosition, const WebCore::FloatPoint& scrollOrigin, const WebCore::FloatSize& obscuredInsetOnSave, double scale)
 {
     m_pageClient.restorePageState(scrollPosition, scrollOrigin, obscuredInsetOnSave, scale);
 }
 
-void WebPageProxy::restorePageCenterAndScale(const WebCore::FloatPoint& center, double scale)
+void WebPageProxy::restorePageCenterAndScale(std::optional<WebCore::FloatPoint> center, double scale)
 {
     m_pageClient.restorePageCenterAndScale(center, scale);
 }
index 3ea32dc..eda29a7 100644 (file)
@@ -64,6 +64,7 @@ private:
     std::unique_ptr<DrawingAreaProxy> createDrawingAreaProxy() override;
     void setViewNeedsDisplay(const WebCore::Region&) override;
     void requestScroll(const WebCore::FloatPoint& scrollPosition, const WebCore::IntPoint& scrollOrigin, bool isProgrammaticScroll) override;
+    WebCore::FloatPoint viewScrollPosition() override;
 
     WebCore::IntSize viewSize() override;
     bool isViewWindowActive() override;
index de2a277..ec3d025 100644 (file)
@@ -126,6 +126,11 @@ void PageClientImpl::requestScroll(const FloatPoint& scrollPosition, const IntPo
     ASSERT_NOT_REACHED();
 }
 
+WebCore::FloatPoint PageClientImpl::viewScrollPosition()
+{
+    return { };
+}
+
 IntSize PageClientImpl::viewSize()
 {
     return IntSize([m_view bounds].size);
index a2eb6d1..70b5879 100644 (file)
@@ -27,6 +27,7 @@
 #define ViewSnapshotStore_h
 
 #include <WebCore/Color.h>
+#include <WebCore/IntPoint.h>
 #include <WebCore/IntSize.h>
 #include <WebCore/IOSurface.h>
 #include <wtf/ListHashSet.h>
@@ -70,6 +71,9 @@ public:
 
     void setBackgroundColor(WebCore::Color color) { m_backgroundColor = color; }
     WebCore::Color backgroundColor() const { return m_backgroundColor; }
+    
+    void setViewScrollPosition(WebCore::IntPoint scrollPosition) { m_viewScrollPosition = scrollPosition; }
+    WebCore::IntPoint viewScrollPosition() const { return m_viewScrollPosition; }
 
     void setDeviceScaleFactor(float deviceScaleFactor) { m_deviceScaleFactor = deviceScaleFactor; }
     float deviceScaleFactor() const { return m_deviceScaleFactor; }
@@ -104,6 +108,7 @@ private:
     uint64_t m_renderTreeSize;
     float m_deviceScaleFactor;
     WebCore::Color m_backgroundColor;
+    WebCore::IntPoint m_viewScrollPosition; // Scroll position at snapshot time. Integral to make comparison reliable.
 };
 
 class ViewSnapshotStore {
index 09ea9bc..2f5a950 100644 (file)
@@ -120,6 +120,7 @@ void ViewSnapshotStore::recordSnapshot(WebPageProxy& webPageProxy, WebBackForwar
     snapshot->setRenderTreeSize(webPageProxy.renderTreeSize());
     snapshot->setDeviceScaleFactor(webPageProxy.deviceScaleFactor());
     snapshot->setBackgroundColor(webPageProxy.pageExtendedBackgroundColor());
+    snapshot->setViewScrollPosition(WebCore::roundedIntPoint(webPageProxy.viewScrollPosition()));
 
     item.setSnapshot(WTFMove(snapshot));
 }
index 28f757f..f0e0e43 100644 (file)
@@ -85,6 +85,7 @@ static FrameState toFrameState(const HistoryItem& historyItem)
     frameState.itemSequenceNumber = historyItem.itemSequenceNumber();
 
     frameState.scrollPosition = historyItem.scrollPosition();
+    frameState.shouldRestoreScrollPosition = historyItem.shouldRestoreScrollPosition();
     frameState.pageScaleFactor = historyItem.pageScaleFactor();
 
     if (FormData* formData = const_cast<HistoryItem&>(historyItem).formData()) {
@@ -161,6 +162,7 @@ static void applyFrameState(HistoryItem& historyItem, const FrameState& frameSta
     historyItem.setItemSequenceNumber(frameState.itemSequenceNumber);
 
     historyItem.setScrollPosition(frameState.scrollPosition);
+    historyItem.setShouldRestoreScrollPosition(frameState.shouldRestoreScrollPosition);
     historyItem.setPageScaleFactor(frameState.pageScaleFactor);
 
     if (frameState.httpBody) {
index a856ff9..68779d3 100644 (file)
@@ -1226,8 +1226,10 @@ void WebFrameLoaderClient::restoreViewState()
     if (FrameView* view = frame.view()) {
         if (m_frame->isMainFrame())
             m_frame->page()->restorePageState(*currentItem);
-        else if (!view->wasScrolledByUser())
+        else if (!view->wasScrolledByUser()) {
+            WTFLogAlways("WebFrameLoaderClient::restoreViewState restoring scroll position %d,%d", currentItem->scrollPosition().x(), currentItem->scrollPosition().y());
             view->setScrollPosition(currentItem->scrollPosition());
+        }
     }
 #else
     // Inform the UI process of the scale factor.
index 6e7c88f..f2f4155 100644 (file)
@@ -312,9 +312,12 @@ void WebPage::restorePageState(const HistoryItem& historyItem)
         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.scrollPosition(), frameView.scrollOrigin(), historyItem.obscuredInset(), boundedScale));
+        std::optional<FloatPoint> scrollPosition;
+        if (historyItem.shouldRestoreScrollPosition()) {
+            m_drawingArea->setExposedContentRect(historyItem.exposedContentRect());
+            scrollPosition = FloatPoint(historyItem.scrollPosition());
+        }
+        send(Messages::WebPageProxy::RestorePageState(scrollPosition, frameView.scrollOrigin(), historyItem.obscuredInset(), boundedScale));
     } else {
         IntSize oldContentSize = historyItem.contentSize();
         IntSize newContentSize = frameView.contentsSize();
@@ -322,23 +325,15 @@ void WebPage::restorePageState(const HistoryItem& historyItem)
 
         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);
-
-        FloatRect oldExposedRect = frameView.exposedContentRect();
-        FloatRect adjustedExposedRect = adjustExposedRectForNewScale(oldExposedRect, m_page->pageScaleFactor(), newScale);
-
-        FloatPoint oldCenter = adjustedExposedRect.center();
-        adjustedExposedRect.move(newCenter - oldCenter);
+        std::optional<FloatPoint> newCenter;
+        if (historyItem.shouldRestoreScrollPosition()) {
+            if (!oldContentSize.isEmpty() && !newContentSize.isEmpty() && newContentSize != oldContentSize)
+                newCenter = relativeCenterAfterContentSizeChange(historyItem.unobscuredContentRect(), oldContentSize, newContentSize);
+            else
+                newCenter = FloatRect(historyItem.unobscuredContentRect()).center();
+        }
 
         scalePage(newScale, IntPoint());
-
         send(Messages::WebPageProxy::RestorePageCenterAndScale(newCenter, newScale));
     }
 }