Source/WebCore/ChangeLog

 12017-02-11 Simon Fraser <simon.fraser@apple.com>
 2
 3 Add support for history.scrollRestoration
 4 https://bugs.webkit.org/show_bug.cgi?id=147782
 5 rdar://problem/22614568
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 Add Mac support for history.scrollRestoration, per spec:
 10 <https://html.spec.whatwg.org/multipage/browsers.html#dom-history-scroll-restoration>
 11
 12 This is a new attribute on the History interface. On setting, sets the "shouldRestoreScrollPosition"
 13 state on the current history item, and the getter returns that state. pushState() inherits the
 14 state from the current item.
 15
 16 HistoryController::restoreScrollPositionAndViewState() consults this state, and if set to "manual"
 17 ("don't restore) it just uses the current scroll position (we need something to pass to
 18 setPageScaleFactor() so can't just avoid the restoration).
 19
 20 FrameLoader::scrollToFragmentWithParentBoundary() also needs to consult the historyItem
 21 to know if it's OK to scroll to a fragment, on back/forward same-document loads.
 22
 23 Tests: fast/history/history-scroll-restoration-attribute.html
 24 fast/history/history-scroll-restoration.html
 25
 26 * history/HistoryItem.cpp:
 27 (WebCore::HistoryItem::HistoryItem):
 28 (WebCore::HistoryItem::shouldRestoreScrollPosition):
 29 (WebCore::HistoryItem::setShouldRestoreScrollPosition):
 30 * history/HistoryItem.h:
 31 * loader/FrameLoader.cpp:
 32 (WebCore::FrameLoader::loadInSameDocument):
 33 (WebCore::itemAllowsScrollRestoration):
 34 (WebCore::isNavigationOtherThanBackForward):
 35 (WebCore::FrameLoader::scrollToFragmentWithParentBoundary):
 36 (WebCore::FrameLoader::continueLoadAfterNavigationPolicy):
 37 * loader/FrameLoader.h:
 38 * loader/HistoryController.cpp:
 39 (WebCore::HistoryController::restoreScrollPositionAndViewState):
 40 (WebCore::HistoryController::pushState):
 41 (WebCore::HistoryController::replaceState):
 42 * page/History.cpp:
 43 (WebCore::History::scrollRestoration):
 44 (WebCore::History::setScrollRestoration):
 45 * page/History.h:
 46 * page/History.idl:
 47
1482017-02-11 Olivier Blin <olivier.blin@softathome.com>
249
350 [GStreamer][MSE][EME] Fix decryptor assignment

Source/WebCore/history/HistoryItem.cpp

@@static void defaultNotifyHistoryItemChanged(HistoryItem*)
5656WEBCORE_EXPORT void (*notifyHistoryItemChanged)(HistoryItem*) = defaultNotifyHistoryItemChanged;
5757
5858HistoryItem::HistoryItem()
59  : m_pageScaleFactor(0)
60  , m_lastVisitWasFailure(false)
61  , m_isTargetItem(false)
62  , m_itemSequenceNumber(generateSequenceNumber())
 59 : m_itemSequenceNumber(generateSequenceNumber())
6360 , m_documentSequenceNumber(generateSequenceNumber())
6461 , m_pruningReason(PruningReason::None)
6562{

@@HistoryItem::HistoryItem(const String& urlString, const String& title)
6966 : m_urlString(urlString)
7067 , m_originalURLString(urlString)
7168 , m_title(title)
72  , m_pageScaleFactor(0)
73  , m_lastVisitWasFailure(false)
74  , m_isTargetItem(false)
7569 , m_itemSequenceNumber(generateSequenceNumber())
7670 , m_documentSequenceNumber(generateSequenceNumber())
7771 , m_pruningReason(PruningReason::None)

@@HistoryItem::HistoryItem(const String& urlString, const String& title, const Str
8579 , m_title(title)
8680 , m_displayTitle(alternateTitle)
8781 , m_pageScaleFactor(0)
88  , m_lastVisitWasFailure(false)
89  , m_isTargetItem(false)
9082 , m_itemSequenceNumber(generateSequenceNumber())
9183 , m_documentSequenceNumber(generateSequenceNumber())
9284 , m_pruningReason(PruningReason::None)

@@void HistoryItem::clearScrollPosition()
271263 m_scrollPosition = IntPoint();
272264}
273265
 266bool HistoryItem::shouldRestoreScrollPosition() const
 267{
 268 return m_shouldRestoreScrollPosition;
 269}
 270
 271void HistoryItem::setShouldRestoreScrollPosition(bool shouldRestore)
 272{
 273 m_shouldRestoreScrollPosition = shouldRestore;
 274}
 275
274276float HistoryItem::pageScaleFactor() const
275277{
276278 return m_pageScaleFactor;

Source/WebCore/history/HistoryItem.h

@@public:
102102 WEBCORE_EXPORT const IntPoint& scrollPosition() const;
103103 WEBCORE_EXPORT void setScrollPosition(const IntPoint&);
104104 void clearScrollPosition();
 105
 106 WEBCORE_EXPORT bool shouldRestoreScrollPosition() const;
 107 WEBCORE_EXPORT void setShouldRestoreScrollPosition(bool);
105108
106109 WEBCORE_EXPORT float pageScaleFactor() const;
107110 WEBCORE_EXPORT void setPageScaleFactor(float);

@@private:
215218 String m_displayTitle;
216219
217220 IntPoint m_scrollPosition;
218  float m_pageScaleFactor;
 221 float m_pageScaleFactor { 0 }; // 0 indicates "unset".
219222 Vector<String> m_documentState;
220223
221224 ShouldOpenExternalURLsPolicy m_shouldOpenExternalURLsPolicy { ShouldOpenExternalURLsPolicy::ShouldNotAllow };
222225
223226 Vector<Ref<HistoryItem>> m_children;
224227
225  bool m_lastVisitWasFailure;
226  bool m_isTargetItem;
 228 bool m_lastVisitWasFailure { false };
 229 bool m_isTargetItem { false };
227230 bool m_wasRestoredFromSession { false };
 231 bool m_shouldRestoreScrollPosition { true };
228232
229233 // If two HistoryItems have the same item sequence number, then they are
230234 // clones of one another. Traversing history from one such HistoryItem to

Source/WebCore/loader/FrameLoader.cpp

@@void FrameLoader::loadInSameDocument(const URL& url, SerializedScriptValue* stat
10511051
10521052 // We need to scroll to the fragment whether or not a hash change occurred, since
10531053 // the user might have scrolled since the previous navigation.
1054  scrollToFragmentWithParentBoundary(url);
 1054 scrollToFragmentWithParentBoundary(url, isNewNavigation);
10551055
10561056 m_isComplete = false;
10571057 checkCompleted();

@@bool FrameLoader::shouldPerformFragmentNavigation(bool isFormSubmission, const S
28592859 && !m_frame.document()->isFrameSet();
28602860}
28612861
2862 void FrameLoader::scrollToFragmentWithParentBoundary(const URL& url)
 2862static bool itemAllowsScrollRestoration(HistoryItem *historyItem)
 2863{
 2864 return historyItem && historyItem->shouldRestoreScrollPosition();
 2865}
 2866
 2867static bool isNavigationOtherThanBackForward(bool isNewNavigation, FrameLoadType loadType)
 2868{
 2869 return isNewNavigation || !isBackForwardLoadType(loadType);
 2870}
 2871
 2872void FrameLoader::scrollToFragmentWithParentBoundary(const URL& url, bool isNewNavigation)
28632873{
28642874 FrameView* view = m_frame.view();
28652875 if (!view)

@@void FrameLoader::scrollToFragmentWithParentBoundary(const URL& url)
28712881 if (boundaryFrame)
28722882 boundaryFrame->view()->setSafeToPropagateScrollToParent(false);
28732883
2874  view->scrollToFragment(url);
 2884 if (isNavigationOtherThanBackForward(isNewNavigation, m_loadType) || itemAllowsScrollRestoration(m_frame.mainFrame().loader().history().currentItem()))
 2885 view->scrollToFragment(url);
28752886
28762887 if (boundaryFrame)
28772888 boundaryFrame->view()->setSafeToPropagateScrollToParent(true);

@@void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest& reque
30623073 setPolicyDocumentLoader(nullptr);
30633074
30643075 // If the navigation request came from the back/forward menu, and we punt on it, we have the
3065  // problem that we have optimistically moved the b/f cursor already, so move it back. For sanity,
 3076 // problem that we have optimistically moved the b/f cursor already, so move it back. For sanity,
30663077 // we only do this when punting a navigation for the target frame or top-level frame.
30673078 if ((isTargetItem || m_frame.isMainFrame()) && isBackForwardLoadType(policyChecker().loadType())) {
30683079 if (Page* page = m_frame.page()) {

Source/WebCore/loader/FrameLoader.h

@@private:
334334 void continueFragmentScrollAfterNavigationPolicy(const ResourceRequest&, bool shouldContinue);
335335
336336 bool shouldPerformFragmentNavigation(bool isFormSubmission, const String& httpMethod, FrameLoadType, const URL&);
337  void scrollToFragmentWithParentBoundary(const URL&);
 337 void scrollToFragmentWithParentBoundary(const URL&, bool isNewNavigation = true);
338338
339339 void checkLoadCompleteForThisFrame();
340340

Source/WebCore/loader/HistoryController.cpp

@@void HistoryController::restoreScrollPositionAndViewState()
158158 // Don't restore scroll point on iOS as FrameLoaderClient::restoreViewState() does that.
159159 if (view && !view->wasScrolledByUser()) {
160160 Page* page = m_frame.page();
161  auto desiredScrollPosition = m_currentItem->scrollPosition();
162 
 161 auto desiredScrollPosition = m_currentItem->shouldRestoreScrollPosition() ? m_currentItem->scrollPosition() : view->scrollPosition();
163162 if (page && m_frame.isMainFrame() && m_currentItem->pageScaleFactor())
164163 page->setPageScaleFactor(m_currentItem->pageScaleFactor() * page->viewScaleFactor(), desiredScrollPosition);
165164 else

@@void HistoryController::pushState(RefPtr<SerializedScriptValue>&& stateObject, c
853852 Page* page = m_frame.page();
854853 ASSERT(page);
855854
 855 bool shouldRestoreScrollPosition = m_currentItem->shouldRestoreScrollPosition();
 856
856857 // Get a HistoryItem tree for the current frame tree.
857858 Ref<HistoryItem> topItem = m_frame.mainFrame().loader().history().createItemTree(m_frame, false);
858859

@@void HistoryController::pushState(RefPtr<SerializedScriptValue>&& stateObject, c
861862 m_currentItem->setTitle(title);
862863 m_currentItem->setStateObject(WTFMove(stateObject));
863864 m_currentItem->setURLString(urlString);
 865 m_currentItem->setShouldRestoreScrollPosition(shouldRestoreScrollPosition);
864866
865  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());
 867 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");
866868
867869 page->backForward().addItem(WTFMove(topItem));
868870

@@void HistoryController::replaceState(RefPtr<SerializedScriptValue>&& stateObject
878880 if (!m_currentItem)
879881 return;
880882
881  LOG(History, "HistoryController %p replaceState: Setting url of current item %p to %s", this, m_currentItem.get(), urlString.ascii().data());
 883 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");
882884
883885 if (!urlString.isEmpty())
884886 m_currentItem->setURLString(urlString);

Source/WebCore/page/History.cpp

@@unsigned History::length() const
5858 return page->backForward().count();
5959}
6060
 61ExceptionOr<History::ScrollRestoration> History::scrollRestoration() const
 62{
 63 auto* historyItem = m_frame->loader().history().currentItem();
 64 if (!historyItem)
 65 return ScrollRestoration::Auto;
 66
 67 return historyItem->shouldRestoreScrollPosition() ? ScrollRestoration::Auto : ScrollRestoration::Manual;
 68}
 69
 70ExceptionOr<void> History::setScrollRestoration(ScrollRestoration scrollRestoration)
 71{
 72 auto* historyItem = m_frame->loader().history().currentItem();
 73 if (historyItem)
 74 historyItem->setShouldRestoreScrollPosition(scrollRestoration == ScrollRestoration::Auto);
 75
 76 return { };
 77}
 78
6179SerializedScriptValue* History::state()
6280{
6381 m_lastStateObjectRequested = stateInternal();

Source/WebCore/page/History.h

@@public:
4141 static Ref<History> create(Frame& frame) { return adoptRef(*new History(frame)); }
4242
4343 unsigned length() const;
 44
 45 enum class ScrollRestoration {
 46 Auto,
 47 Manual
 48 };
 49
 50 ExceptionOr<ScrollRestoration> scrollRestoration() const;
 51 ExceptionOr<void> setScrollRestoration(ScrollRestoration);
 52
4453 SerializedScriptValue* state();
4554 void back();
4655 void forward();

Source/WebCore/page/History.idl

2727 GenerateIsReachable=ImplFrame,
2828] interface History {
2929 readonly attribute unsigned long length;
 30 [SetterMayThrowException, GetterMayThrowException] attribute ScrollRestoration scrollRestoration;
3031 [CachedAttribute, Custom] readonly attribute SerializedScriptValue state;
3132
3233 [CallWith=Document, ForwardDeclareInHeader] void back();

3637 [Custom, MayThrowException] void pushState(any data, DOMString title, optional USVString? url = null);
3738 [Custom, MayThrowException] void replaceState(any data, DOMString title, optional USVString? url = null);
3839};
 40
 41enum ScrollRestoration { "auto", "manual" };

LayoutTests/ChangeLog

 12017-02-11 Simon Fraser <simon.fraser@apple.com>
 2
 3 Add support for history.scrollRestoration
 4 https://bugs.webkit.org/show_bug.cgi?id=147782
 5 rdar://problem/22614568
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 * fast/dom/Window/window-appendages-cleared-expected.txt:
 10 * fast/history/history-scroll-restoration-attribute-expected.txt: Added.
 11 * fast/history/history-scroll-restoration-attribute.html: Added.
 12 * fast/history/history-scroll-restoration-expected.txt: Added.
 13 * fast/history/history-scroll-restoration.html: Added.
 14
1152017-02-11 Andreas Kling <akling@apple.com>
216
317 LayoutTest fast/scrolling/page-cache-back-overflow-scroll-restore.html is a flaky failure

LayoutTests/imported/w3c/ChangeLog

 12017-02-11 Simon Fraser <simon.fraser@apple.com>
 2
 3 Add support for history.scrollRestoration
 4 https://bugs.webkit.org/show_bug.cgi?id=147782
 5 rdar://problem/22614568
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 New passing baselines.
 10
 11 * web-platform-tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-basic-expected.txt:
 12 * web-platform-tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin-expected.txt:
 13 * web-platform-tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-cross-origin-expected.txt:
 14 * web-platform-tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-samedoc-expected.txt:
 15
1162017-02-11 Chris Dumez <cdumez@apple.com>
217
318 Implement URL's toJSON()

LayoutTests/fast/dom/Window/window-appendages-cleared-expected.txt

@@PASS history.go == "LEFTOVER" is false
44PASS history.length == "LEFTOVER" is false
55PASS history.pushState == "LEFTOVER" is false
66PASS history.replaceState == "LEFTOVER" is false
 7PASS history.scrollRestoration == "LEFTOVER" is false
78PASS history.state == "LEFTOVER" is false
89PASS location.ancestorOrigins == "LEFTOVER" is false
910PASS location.assign == "LEFTOVER" is false

LayoutTests/fast/history/history-scroll-restoration-attribute-expected.txt

 1Tests the history.scrollRestoration attribute
 2
 3On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 4
 5
 6PASS 'scrollRestoration' in history is true
 7PASS history.scrollRestoration is "auto"
 8PASS history.scrollRestoration is "manual"
 9PASS history.scrollRestoration is "manual"
 10PASS history.scrollRestoration is "auto"
 11PASS successfullyParsed is true
 12
 13TEST COMPLETE
 14

LayoutTests/fast/history/history-scroll-restoration-attribute.html

 1<html>
 2<head>
 3<script src="../../resources/js-test-pre.js"></script>
 4</head>
 5<body>
 6<script>
 7 description("Tests the history.scrollRestoration attribute");
 8
 9 if (window.testRunner)
 10 testRunner.clearBackForwardList();
 11
 12 shouldBeTrue("'scrollRestoration' in history");
 13
 14 history.scrollRestoration = 'auto';
 15 shouldBeEqualToString('history.scrollRestoration', 'auto');
 16
 17 history.scrollRestoration = 'manual';
 18 shouldBeEqualToString('history.scrollRestoration', 'manual');
 19
 20 history.scrollRestoration = 'bananas';
 21 shouldBeEqualToString('history.scrollRestoration', 'manual');
 22
 23 history.scrollRestoration = 'auto';
 24 shouldBeEqualToString('history.scrollRestoration', 'auto');
 25
 26</script>
 27<script src="../../resources/js-test-post.js"></script>
 28</body>
 29</html>

LayoutTests/fast/history/history-scroll-restoration-expected.txt

 1Tests that history.scrollRestoration works for same-document navigations
 2
 3On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 4
 5
 6PASS 'scrollRestoration' in history is true
 7PASS history.scrollRestoration is "auto"
 8PASS window.scrollX is 123
 9PASS window.scrollY is 456
 10PASS history.scrollRestoration is "manual"
 11PASS window.scrollX is 333
 12PASS window.scrollY is 555
 13PASS successfullyParsed is true
 14
 15TEST COMPLETE
 16

LayoutTests/fast/history/history-scroll-restoration.html

 1<html>
 2<head>
 3<script src="../../resources/js-test-pre.js"></script>
 4<style>
 5 body {
 6 height: 2000px;
 7 width: 2000px;
 8 }
 9</style>
 10</head>
 11<body>
 12<script>
 13 var jsTestIsAsync = true;
 14
 15 description("Tests that history.scrollRestoration works for same-document navigations");
 16
 17 if (window.testRunner)
 18 testRunner.clearBackForwardList();
 19
 20 shouldBeTrue("'scrollRestoration' in history");
 21
 22 // Can't create history entries inside the onload handler.
 23 window.setTimeout(testAutoScrollRestoration, 0);
 24
 25 function testAutoScrollRestoration()
 26 {
 27 history.scrollRestoration = 'auto';
 28 shouldBeEqualToString('history.scrollRestoration', 'auto');
 29
 30 window.scrollTo(123, 456);
 31 history.pushState(null, '', '#1');
 32 window.scrollTo(0, 0);
 33 history.back();
 34
 35 window.setTimeout(function() {
 36 shouldBe('window.scrollX', '123');
 37 shouldBe('window.scrollY', '456');
 38
 39 testManualScrollRestoration();
 40 }, 0);
 41 }
 42
 43 function testManualScrollRestoration()
 44 {
 45 history.scrollRestoration = 'manual';
 46 shouldBeEqualToString('history.scrollRestoration', 'manual');
 47
 48 window.scrollTo(123, 456);
 49 history.pushState(null, '', '#2');
 50 window.scrollTo(333, 555);
 51 history.back();
 52
 53 window.setTimeout(function() {
 54 shouldBe('window.scrollX', '333');
 55 shouldBe('window.scrollY', '555');
 56
 57 window.scrollTo(0, 0);
 58 finishJSTest();
 59 }, 0);
 60 }
 61
 62</script>
 63<script src="../../resources/js-test-post.js"></script>
 64</body>
 65</html>

LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-basic-expected.txt

11
2 FAIL Default value is "auto" assert_equals: expected (string) "auto" but got (undefined) undefined
 2PASS Default value is "auto"
33PASS It is writable
4 FAIL Invalid values are ignored assert_equals: setting to invalid value (3.1415) should be ignored expected (string) "auto" but got (number) 3.1415
 4PASS Invalid values are ignored
55

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

11Blocked access to external URL http://www.localhost:8800/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resources/blank1.html
2 CONSOLE MESSAGE: line 2451: Error: assert_equals: navigating back should retain scrollRestoration value expected (string) "manual" but got (undefined) undefined
32
43
5 Harness Error (TIMEOUT), message = null
6 
7 TIMEOUT Manual scroll restoration should take precedent over scrolling to fragment in cross origin navigation Test timed out
 4PASS Manual scroll restoration should take precedent over scrolling to fragment in cross origin navigation
85

LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-cross-origin-expected.txt

11
22
3 FAIL Navigating to new page should reset to "auto" and navigating back should restore and respect scroll restoration mode assert_equals: new page loads should set scrollRestoration to "auto" expected (string) "auto" but got (undefined) undefined
 3PASS Navigating to new page should reset to "auto" and navigating back should restore and respect scroll restoration mode
44

LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-navigation-samedoc-expected.txt

11
2 FAIL history.{push,replace}State retain scroll restoration mode and navigation in the same document respects it assert_equals: scrollX is correct for #4 expected 555 but got 200
 2PASS history.{push,replace}State retain scroll restoration mode and navigation in the same document respects it
33