[iOS WK2] Implement support for visual viewports
authorsimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 15 Nov 2016 21:06:06 +0000 (21:06 +0000)
committersimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 15 Nov 2016 21:06:06 +0000 (21:06 +0000)
https://bugs.webkit.org/show_bug.cgi?id=164765

Reviewed by Tim Horton.

Adopt the visual viewport scrolling model in iOS WK2.
Source/WebCore:

This is more complex than the Mac implementation for two primary reasons. First,
WKWebView needs to to able to control the rectangle used for fixed position layout
to get the correct behavior when zooming all the way out, and because iOS displays
pages scaled down, exposing document overflow such that the layout viewport rectangle
has to get larger than the initial containing block size (which does not happen on Mac).

This is achieved by pushing a "layoutViewportOverrideRect" down onto FrameView, in
a similar way to the customFixedPositionRect that's used now. We share that name
for now in code that is agnostic to its use (e.g. VisibleContentRectUpdateInfo).

I tried so hard to write tests, but ran into various problems (webkit.org/b/164762,
webkit.org/b/164764). Will add tests via webkit.org/b/164764.

* page/FrameView.cpp:
(WebCore::FrameView::fixedScrollableAreaBoundsInflatedForScrolling): layoutViewportOrigin()
was removed.
(WebCore::FrameView::setBaseLayoutViewportOrigin): Rename with "base" to make it clearer that
it can be overridden.
(WebCore::FrameView::setLayoutViewportOverrideRect):
(WebCore::FrameView::baseLayoutViewportSize): Renamed.
(WebCore::FrameView::updateLayoutViewport): Logging.
(WebCore::FrameView::layoutViewportRect):
(WebCore::FrameView::scrollPositionForFixedPosition):
(WebCore::FrameView::unscaledMaximumScrollPosition): During page transitions on iOS, it
was possible for unscaledDocumentRect to be empty, but visibleSize() to be non-empty, leading
to odd negative max scroll offsets, so clamp to 0,0.
(WebCore::FrameView::setLayoutViewportOrigin): Deleted.
* page/FrameView.h:
* page/scrolling/AsyncScrollingCoordinator.cpp:
(WebCore::AsyncScrollingCoordinator::reconcileScrollingState): scrollPositionForFixedPosition() already does the
visualViewportEnabled() check.
* page/scrolling/mac/ScrollingTreeFixedNode.mm:
(WebCore::ScrollingTreeFixedNode::updateLayersAfterAncestorChange):
* platform/graphics/FloatSize.cpp:
(WebCore::FloatSize::constrainedBetween): Added for consistency with the other geometry types.
* platform/graphics/FloatSize.h:
* platform/graphics/LayoutSize.cpp:
(WebCore::LayoutSize::constrainedBetween): Ditto.
* platform/graphics/LayoutSize.h:
* rendering/RenderView.cpp:
(WebCore::RenderView::clientLogicalWidthForFixedPosition): If we have an override layout viewport, its size might be different
from the RenderView's size (the initial containing block), so we need to use the layoutViewportRect here.
(WebCore::RenderView::clientLogicalHeightForFixedPosition):

Source/WebKit2:

Pass the parameters used for computing the layout viewport up to WK2 via RemoteLayerTreeTransaction.
These are stored on WebPageProxy. When they change, _didCommitLayerTree triggers a -_updateVisibleContentRects.

WebPageProxy::computeCustomFixedPositionRect() is the function that computes the "override" layout viewport.
It starts with the baseLayoutViewportSize from the web process (which is based on the initial containing block
size), then ensures that it's no smaller than the unobscured content rect, since it makes no sense for the
layout viewport to be smaller than the visual viewport. The static FrameView::computeLayoutViewportOrigin()
is then use to "push" the layout viewport around as the visual viewport changes.

* Shared/VisibleContentRectUpdateInfo.h:
* Shared/WebCoreArgumentCoders.cpp: Encode LayoutSize and LayoutPoint.
(IPC::ArgumentCoder<LayoutSize>::encode):
(IPC::ArgumentCoder<LayoutSize>::decode):
(IPC::ArgumentCoder<LayoutPoint>::encode):
(IPC::ArgumentCoder<LayoutPoint>::decode):
* Shared/WebCoreArgumentCoders.h:
* Shared/mac/RemoteLayerTreeTransaction.h:
(WebKit::RemoteLayerTreeTransaction::baseLayoutViewportSize):
(WebKit::RemoteLayerTreeTransaction::setBaseLayoutViewportSize):
(WebKit::RemoteLayerTreeTransaction::minStableLayoutViewportOrigin):
(WebKit::RemoteLayerTreeTransaction::setMinStableLayoutViewportOrigin):
(WebKit::RemoteLayerTreeTransaction::maxStableLayoutViewportOrigin):
(WebKit::RemoteLayerTreeTransaction::setMaxStableLayoutViewportOrigin):
* Shared/mac/RemoteLayerTreeTransaction.mm:
(WebKit::RemoteLayerTreeTransaction::encode):
(WebKit::RemoteLayerTreeTransaction::decode):
(WebKit::RemoteLayerTreeTransaction::description):
* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView _didCommitLayerTree:]):
* UIProcess/Scrolling/RemoteScrollingCoordinatorProxy.h:
(WebKit::RemoteScrollingCoordinatorProxy::visualViewportEnabled): Accessor.
* UIProcess/WebPageProxy.h:
(WebKit::WebPageProxy::customFixedPositionRect):
* UIProcess/ios/RemoteScrollingCoordinatorProxyIOS.mm:
(WebKit::RemoteScrollingCoordinatorProxy::customFixedPositionRect):
* UIProcess/ios/WKContentView.mm:
(-[WKContentView didUpdateVisibleRect:unobscuredRect:unobscuredRectInScrollViewCoordinates:obscuredInset:scale:minimumScale:inStableState:isChangingObscuredInsetsInteractively:enclosedInScrollableAncestorView:]):
(-[WKContentView _didCommitLayerTree:]):
* UIProcess/ios/WebPageProxyIOS.mm:
(WebKit::WebPageProxy::computeCustomFixedPositionRect): When visual viewports are enabled, compute
the layout viewport rect, taking the baseLayoutViewportSize and the current unobscured rect into account.
(WebKit::WebPageProxy::updateLayoutViewportParameters):
* UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.mm:
(WebKit::RemoteLayerTreeDrawingAreaProxy::commitLayerTree):
* WebProcess/WebPage/WebPage.cpp: Encode in the transaction the layout viewport parameters (with minor refactor).
(WebKit::WebPage::willCommitLayerTree):
* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::updateVisibleContentRects): This is where the web process receives the new override layout viewport
from the web process (with some logging).

LayoutTests:

These tests don't correctly test iOS WK2's async scrolling behavior (webkit.org/b/164779)
so rebaseline.

* platform/ios-simulator-wk2/fast/visual-viewport/nonzoomed-rects-expected.txt: Added.
* platform/ios-simulator-wk2/fast/visual-viewport/rtl-nonzoomed-rects-expected.txt: Added.
* platform/ios-simulator-wk2/fast/visual-viewport/rtl-zoomed-rects-expected.txt: Added.
* platform/ios-simulator-wk2/fast/visual-viewport/zoomed-fixed-expected.txt: Added.
* platform/ios-simulator-wk2/fast/visual-viewport/zoomed-fixed-scroll-down-then-up-expected.txt: Added.
* platform/ios-simulator-wk2/fast/visual-viewport/zoomed-rects-expected.txt: Added.

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

31 files changed:
LayoutTests/ChangeLog
LayoutTests/platform/ios-simulator-wk2/fast/visual-viewport/nonzoomed-rects-expected.txt [new file with mode: 0644]
LayoutTests/platform/ios-simulator-wk2/fast/visual-viewport/rtl-nonzoomed-rects-expected.txt [new file with mode: 0644]
LayoutTests/platform/ios-simulator-wk2/fast/visual-viewport/rtl-zoomed-rects-expected.txt [new file with mode: 0644]
LayoutTests/platform/ios-simulator-wk2/fast/visual-viewport/zoomed-fixed-expected.txt [new file with mode: 0644]
LayoutTests/platform/ios-simulator-wk2/fast/visual-viewport/zoomed-fixed-scroll-down-then-up-expected.txt [new file with mode: 0644]
LayoutTests/platform/ios-simulator-wk2/fast/visual-viewport/zoomed-rects-expected.txt [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/page/FrameView.cpp
Source/WebCore/page/FrameView.h
Source/WebCore/page/scrolling/AsyncScrollingCoordinator.cpp
Source/WebCore/platform/graphics/FloatSize.cpp
Source/WebCore/platform/graphics/FloatSize.h
Source/WebCore/platform/graphics/LayoutSize.cpp
Source/WebCore/platform/graphics/LayoutSize.h
Source/WebCore/rendering/RenderView.cpp
Source/WebKit2/ChangeLog
Source/WebKit2/Shared/VisibleContentRectUpdateInfo.h
Source/WebKit2/Shared/WebCoreArgumentCoders.cpp
Source/WebKit2/Shared/WebCoreArgumentCoders.h
Source/WebKit2/Shared/mac/RemoteLayerTreeTransaction.h
Source/WebKit2/Shared/mac/RemoteLayerTreeTransaction.mm
Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm
Source/WebKit2/UIProcess/Scrolling/RemoteScrollingCoordinatorProxy.h
Source/WebKit2/UIProcess/WebPageProxy.h
Source/WebKit2/UIProcess/ios/RemoteScrollingCoordinatorProxyIOS.mm
Source/WebKit2/UIProcess/ios/WKContentView.mm
Source/WebKit2/UIProcess/ios/WebPageProxyIOS.mm
Source/WebKit2/UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.mm
Source/WebKit2/WebProcess/WebPage/WebPage.cpp
Source/WebKit2/WebProcess/WebPage/ios/WebPageIOS.mm

index 6822264..fdca01c 100644 (file)
@@ -1,3 +1,22 @@
+2016-11-15  Simon Fraser  <simon.fraser@apple.com>
+
+        [iOS WK2] Implement support for visual viewports
+        https://bugs.webkit.org/show_bug.cgi?id=164765
+
+        Reviewed by Tim Horton.
+
+        Adopt the visual viewport scrolling model in iOS WK2.
+        
+        These tests don't correctly test iOS WK2's async scrolling behavior (webkit.org/b/164779)
+        so rebaseline.
+
+        * platform/ios-simulator-wk2/fast/visual-viewport/nonzoomed-rects-expected.txt: Added.
+        * platform/ios-simulator-wk2/fast/visual-viewport/rtl-nonzoomed-rects-expected.txt: Added.
+        * platform/ios-simulator-wk2/fast/visual-viewport/rtl-zoomed-rects-expected.txt: Added.
+        * platform/ios-simulator-wk2/fast/visual-viewport/zoomed-fixed-expected.txt: Added.
+        * platform/ios-simulator-wk2/fast/visual-viewport/zoomed-fixed-scroll-down-then-up-expected.txt: Added.
+        * platform/ios-simulator-wk2/fast/visual-viewport/zoomed-rects-expected.txt: Added.
+
 2016-11-14  Brent Fulgham  <bfulgham@apple.com>
 
         Correct handling of changing input type
diff --git a/LayoutTests/platform/ios-simulator-wk2/fast/visual-viewport/nonzoomed-rects-expected.txt b/LayoutTests/platform/ios-simulator-wk2/fast/visual-viewport/nonzoomed-rects-expected.txt
new file mode 100644 (file)
index 0000000..a59519b
--- /dev/null
@@ -0,0 +1,23 @@
+This test scrolls the page and checks that the layout and visual viewports respond as expected.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+JSON.stringify(internals.layoutViewportRect()) is {"top":0,"right":800,"bottom":600,"left":0,"width":800,"height":600}
+JSON.stringify(internals.visualViewportRect()) is {"top":0,"right":800,"bottom":600,"left":0,"width":800,"height":600}
+
+Scrolled to 475, 525
+JSON.stringify(internals.layoutViewportRect()) is {"top":525,"right":1275,"bottom":1125,"left":475,"width":800,"height":600}
+JSON.stringify(internals.visualViewportRect()) is {"top":525,"right":1275,"bottom":1125,"left":475,"width":800,"height":600}
+
+Scrolled to 100, 776
+JSON.stringify(internals.layoutViewportRect()) is {"top":776,"right":900,"bottom":1376,"left":100,"width":800,"height":600}
+JSON.stringify(internals.visualViewportRect()) is {"top":776,"right":900,"bottom":1376,"left":100,"width":800,"height":600}
+
+Scrolled to 50, 300
+JSON.stringify(internals.layoutViewportRect()) is {"top":300,"right":850,"bottom":900,"left":50,"width":800,"height":600}
+JSON.stringify(internals.visualViewportRect()) is {"top":300,"right":850,"bottom":900,"left":50,"width":800,"height":600}
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/platform/ios-simulator-wk2/fast/visual-viewport/rtl-nonzoomed-rects-expected.txt b/LayoutTests/platform/ios-simulator-wk2/fast/visual-viewport/rtl-nonzoomed-rects-expected.txt
new file mode 100644 (file)
index 0000000..458dbea
--- /dev/null
@@ -0,0 +1,23 @@
+This test scrolls the page and checks that the layout and visual viewports respond as expected.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+JSON.stringify(internals.layoutViewportRect()) is {"top":0,"right":800,"bottom":600,"left":0,"width":800,"height":600}
+JSON.stringify(internals.visualViewportRect()) is {"top":0,"right":800,"bottom":600,"left":0,"width":800,"height":600}
+
+Scrolled to -475, 525
+JSON.stringify(internals.layoutViewportRect()) is {"top":525,"right":325,"bottom":1125,"left":-475,"width":800,"height":600}
+JSON.stringify(internals.visualViewportRect()) is {"top":525,"right":325,"bottom":1125,"left":-475,"width":800,"height":600}
+
+Scrolled to -100, 776
+JSON.stringify(internals.layoutViewportRect()) is {"top":776,"right":700,"bottom":1376,"left":-100,"width":800,"height":600}
+JSON.stringify(internals.visualViewportRect()) is {"top":776,"right":700,"bottom":1376,"left":-100,"width":800,"height":600}
+
+Scrolled to -50, 300
+JSON.stringify(internals.layoutViewportRect()) is {"top":300,"right":750,"bottom":900,"left":-50,"width":800,"height":600}
+JSON.stringify(internals.visualViewportRect()) is {"top":300,"right":750,"bottom":900,"left":-50,"width":800,"height":600}
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/platform/ios-simulator-wk2/fast/visual-viewport/rtl-zoomed-rects-expected.txt b/LayoutTests/platform/ios-simulator-wk2/fast/visual-viewport/rtl-zoomed-rects-expected.txt
new file mode 100644 (file)
index 0000000..700a9df
--- /dev/null
@@ -0,0 +1,23 @@
+This test zooms and scrolls the page and checks that the layout and visual viewports respond as expected.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+JSON.stringify(internals.layoutViewportRect()) is {"top":0,"right":800,"bottom":600,"left":0,"width":800,"height":600}
+JSON.stringify(internals.visualViewportRect()) is {"top":0,"right":400,"bottom":300,"left":0,"width":400,"height":300}
+
+Scrolled to -475, 525
+JSON.stringify(internals.layoutViewportRect()) is {"top":0,"right":800,"bottom":600,"left":0,"width":800,"height":600}
+JSON.stringify(internals.visualViewportRect()) is {"top":525,"right":-75,"bottom":825,"left":-475,"width":400,"height":300}
+
+Scrolled to -100, 776
+JSON.stringify(internals.layoutViewportRect()) is {"top":0,"right":800,"bottom":600,"left":0,"width":800,"height":600}
+JSON.stringify(internals.visualViewportRect()) is {"top":776,"right":300,"bottom":1076,"left":-100,"width":400,"height":300}
+
+Scrolled to -50, 300
+JSON.stringify(internals.layoutViewportRect()) is {"top":0,"right":800,"bottom":600,"left":0,"width":800,"height":600}
+JSON.stringify(internals.visualViewportRect()) is {"top":300,"right":350,"bottom":600,"left":-50,"width":400,"height":300}
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/platform/ios-simulator-wk2/fast/visual-viewport/zoomed-fixed-expected.txt b/LayoutTests/platform/ios-simulator-wk2/fast/visual-viewport/zoomed-fixed-expected.txt
new file mode 100644 (file)
index 0000000..4fa535d
--- /dev/null
@@ -0,0 +1,55 @@
+This test zooms and scrolls the page and checks the positions of fixed-position objects.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+JSON.stringify(internals.layoutViewportRect()) is {"top":0,"right":800,"bottom":600,"left":0,"width":800,"height":600}
+JSON.stringify(internals.visualViewportRect()) is {"top":0,"right":400,"bottom":300,"left":0,"width":400,"height":300}
+client rect of top:
+JSON.stringify(fixedElement.getBoundingClientRect()) is {"top":0,"right":800,"bottom":100,"left":0,"width":800,"height":100}
+client rect of bottom:
+JSON.stringify(fixedElement.getBoundingClientRect()) is {"top":500,"right":800,"bottom":600,"left":0,"width":800,"height":100}
+client rect of left:
+JSON.stringify(fixedElement.getBoundingClientRect()) is {"top":0,"right":100,"bottom":600,"left":0,"width":100,"height":600}
+client rect of right:
+JSON.stringify(fixedElement.getBoundingClientRect()) is {"top":0,"right":800,"bottom":600,"left":700,"width":100,"height":600}
+
+Scrolled to 475, 525
+JSON.stringify(internals.layoutViewportRect()) is {"top":0,"right":800,"bottom":600,"left":0,"width":800,"height":600}
+JSON.stringify(internals.visualViewportRect()) is {"top":525,"right":875,"bottom":825,"left":475,"width":400,"height":300}
+client rect of top:
+JSON.stringify(fixedElement.getBoundingClientRect()) is {"top":-525,"right":325,"bottom":-425,"left":-475,"width":800,"height":100}
+client rect of bottom:
+JSON.stringify(fixedElement.getBoundingClientRect()) is {"top":-25,"right":325,"bottom":75,"left":-475,"width":800,"height":100}
+client rect of left:
+JSON.stringify(fixedElement.getBoundingClientRect()) is {"top":-525,"right":-375,"bottom":75,"left":-475,"width":100,"height":600}
+client rect of right:
+JSON.stringify(fixedElement.getBoundingClientRect()) is {"top":-525,"right":325,"bottom":75,"left":225,"width":100,"height":600}
+
+Scrolled to 100, 776
+JSON.stringify(internals.layoutViewportRect()) is {"top":0,"right":800,"bottom":600,"left":0,"width":800,"height":600}
+JSON.stringify(internals.visualViewportRect()) is {"top":776,"right":500,"bottom":1076,"left":100,"width":400,"height":300}
+client rect of top:
+JSON.stringify(fixedElement.getBoundingClientRect()) is {"top":-776,"right":700,"bottom":-676,"left":-100,"width":800,"height":100}
+client rect of bottom:
+JSON.stringify(fixedElement.getBoundingClientRect()) is {"top":-276,"right":700,"bottom":-176,"left":-100,"width":800,"height":100}
+client rect of left:
+JSON.stringify(fixedElement.getBoundingClientRect()) is {"top":-776,"right":0,"bottom":-176,"left":-100,"width":100,"height":600}
+client rect of right:
+JSON.stringify(fixedElement.getBoundingClientRect()) is {"top":-776,"right":700,"bottom":-176,"left":600,"width":100,"height":600}
+
+Scrolled to 50, 300
+JSON.stringify(internals.layoutViewportRect()) is {"top":0,"right":800,"bottom":600,"left":0,"width":800,"height":600}
+JSON.stringify(internals.visualViewportRect()) is {"top":300,"right":450,"bottom":600,"left":50,"width":400,"height":300}
+client rect of top:
+JSON.stringify(fixedElement.getBoundingClientRect()) is {"top":-300,"right":750,"bottom":-200,"left":-50,"width":800,"height":100}
+client rect of bottom:
+JSON.stringify(fixedElement.getBoundingClientRect()) is {"top":200,"right":750,"bottom":300,"left":-50,"width":800,"height":100}
+client rect of left:
+JSON.stringify(fixedElement.getBoundingClientRect()) is {"top":-300,"right":50,"bottom":300,"left":-50,"width":100,"height":600}
+client rect of right:
+JSON.stringify(fixedElement.getBoundingClientRect()) is {"top":-300,"right":750,"bottom":300,"left":650,"width":100,"height":600}
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/platform/ios-simulator-wk2/fast/visual-viewport/zoomed-fixed-scroll-down-then-up-expected.txt b/LayoutTests/platform/ios-simulator-wk2/fast/visual-viewport/zoomed-fixed-scroll-down-then-up-expected.txt
new file mode 100644 (file)
index 0000000..2dbe930
--- /dev/null
@@ -0,0 +1,14 @@
+layer at (0,0) size 2008x2016
+  RenderView at (0,0) size 800x600
+layer at (0,0) size 800x2016
+  RenderBlock {HTML} at (0,0) size 800x2016
+    RenderBody {BODY} at (8,8) size 2000x2000
+layer at (200,0) size 400x100
+  RenderBlock (positioned) {DIV} at (200,0) size 400x100 [bgcolor=#808080]
+layer at (200,500) size 400x100
+  RenderBlock (positioned) {DIV} at (200,500) size 400x100 [bgcolor=#808080]
+layer at (0,150) size 100x300
+  RenderBlock (positioned) {DIV} at (0,150) size 100x300 [bgcolor=#808080]
+layer at (700,150) size 100x300
+  RenderBlock (positioned) {DIV} at (700,150) size 100x300 [bgcolor=#808080]
+scrolled to 275,10
diff --git a/LayoutTests/platform/ios-simulator-wk2/fast/visual-viewport/zoomed-rects-expected.txt b/LayoutTests/platform/ios-simulator-wk2/fast/visual-viewport/zoomed-rects-expected.txt
new file mode 100644 (file)
index 0000000..e7acb55
--- /dev/null
@@ -0,0 +1,23 @@
+This test zooms and scrolls the page and checks that the layout and visual viewports respond as expected.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+JSON.stringify(internals.layoutViewportRect()) is {"top":0,"right":800,"bottom":600,"left":0,"width":800,"height":600}
+JSON.stringify(internals.visualViewportRect()) is {"top":0,"right":400,"bottom":300,"left":0,"width":400,"height":300}
+
+Scrolled to 475, 525
+JSON.stringify(internals.layoutViewportRect()) is {"top":0,"right":800,"bottom":600,"left":0,"width":800,"height":600}
+JSON.stringify(internals.visualViewportRect()) is {"top":525,"right":875,"bottom":825,"left":475,"width":400,"height":300}
+
+Scrolled to 100, 776
+JSON.stringify(internals.layoutViewportRect()) is {"top":0,"right":800,"bottom":600,"left":0,"width":800,"height":600}
+JSON.stringify(internals.visualViewportRect()) is {"top":776,"right":500,"bottom":1076,"left":100,"width":400,"height":300}
+
+Scrolled to 50, 300
+JSON.stringify(internals.layoutViewportRect()) is {"top":0,"right":800,"bottom":600,"left":0,"width":800,"height":600}
+JSON.stringify(internals.visualViewportRect()) is {"top":300,"right":450,"bottom":600,"left":50,"width":400,"height":300}
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
index 83c8dc5..e511318 100644 (file)
@@ -1,3 +1,56 @@
+2016-11-14  Simon Fraser  <simon.fraser@apple.com>
+
+        [iOS WK2] Implement support for visual viewports
+        https://bugs.webkit.org/show_bug.cgi?id=164765
+
+        Reviewed by Tim Horton.
+
+        Adopt the visual viewport scrolling model in iOS WK2.
+
+        This is more complex than the Mac implementation for two primary reasons. First,
+        WKWebView needs to to able to control the rectangle used for fixed position layout
+        to get the correct behavior when zooming all the way out, and because iOS displays
+        pages scaled down, exposing document overflow such that the layout viewport rectangle
+        has to get larger than the initial containing block size (which does not happen on Mac).
+
+        This is achieved by pushing a "layoutViewportOverrideRect" down onto FrameView, in
+        a similar way to the customFixedPositionRect that's used now. We share that name
+        for now in code that is agnostic to its use (e.g. VisibleContentRectUpdateInfo).
+
+        I tried so hard to write tests, but ran into various problems (webkit.org/b/164762,
+        webkit.org/b/164764). Will add tests via webkit.org/b/164764.
+
+        * page/FrameView.cpp:
+        (WebCore::FrameView::fixedScrollableAreaBoundsInflatedForScrolling): layoutViewportOrigin()
+        was removed.
+        (WebCore::FrameView::setBaseLayoutViewportOrigin): Rename with "base" to make it clearer that
+        it can be overridden.
+        (WebCore::FrameView::setLayoutViewportOverrideRect):
+        (WebCore::FrameView::baseLayoutViewportSize): Renamed.
+        (WebCore::FrameView::updateLayoutViewport): Logging.
+        (WebCore::FrameView::layoutViewportRect):
+        (WebCore::FrameView::scrollPositionForFixedPosition):
+        (WebCore::FrameView::unscaledMaximumScrollPosition): During page transitions on iOS, it
+        was possible for unscaledDocumentRect to be empty, but visibleSize() to be non-empty, leading
+        to odd negative max scroll offsets, so clamp to 0,0.
+        (WebCore::FrameView::setLayoutViewportOrigin): Deleted.
+        * page/FrameView.h:
+        * page/scrolling/AsyncScrollingCoordinator.cpp:
+        (WebCore::AsyncScrollingCoordinator::reconcileScrollingState): scrollPositionForFixedPosition() already does the
+        visualViewportEnabled() check.
+        * page/scrolling/mac/ScrollingTreeFixedNode.mm:
+        (WebCore::ScrollingTreeFixedNode::updateLayersAfterAncestorChange):
+        * platform/graphics/FloatSize.cpp:
+        (WebCore::FloatSize::constrainedBetween): Added for consistency with the other geometry types.
+        * platform/graphics/FloatSize.h:
+        * platform/graphics/LayoutSize.cpp:
+        (WebCore::LayoutSize::constrainedBetween): Ditto.
+        * platform/graphics/LayoutSize.h:
+        * rendering/RenderView.cpp:
+        (WebCore::RenderView::clientLogicalWidthForFixedPosition): If we have an override layout viewport, its size might be different
+        from the RenderView's size (the initial containing block), so we need to use the layoutViewportRect here.
+        (WebCore::RenderView::clientLogicalHeightForFixedPosition):
+
 2016-11-15  Myles C. Maxfield  <mmaxfield@apple.com>
 
         [WebGL] Remove unused Chromium-specific OpenGL extensions
index 017a46c..2ef5838 100644 (file)
@@ -1097,7 +1097,7 @@ LayoutRect FrameView::fixedScrollableAreaBoundsInflatedForScrolling(const Layout
 
     if (frame().settings().visualViewportEnabled()) {
         // FIXME: this is wrong under zooming; uninflatedBounds is scaled but the scroll positions are not.
-        scrollPosition = layoutViewportOrigin();
+        scrollPosition = layoutViewportRect().location();
         topLeftExpansion = scrollPosition - unscaledMinimumScrollPosition();
         bottomRightExpansion = unscaledMaximumScrollPosition() - scrollPosition;
     } else {
@@ -1813,7 +1813,7 @@ LayoutPoint FrameView::computeLayoutViewportOrigin(const LayoutRect& visualViewp
     return layoutViewportOrigin;
 }
 
-void FrameView::setLayoutViewportOrigin(LayoutPoint origin, TriggerLayoutOrNot layoutTriggering)
+void FrameView::setBaseLayoutViewportOrigin(LayoutPoint origin, TriggerLayoutOrNot layoutTriggering)
 {
     ASSERT(frame().settings().visualViewportEnabled());
 
@@ -1831,10 +1831,35 @@ void FrameView::setLayoutViewportOrigin(LayoutPoint origin, TriggerLayoutOrNot l
     }
 }
 
+void FrameView::setLayoutViewportOverrideRect(Optional<LayoutRect> rect)
+{
+    if (rect == m_layoutViewportOverrideRect)
+        return;
+
+    LayoutRect oldRect = layoutViewportRect();
+    m_layoutViewportOverrideRect = rect;
+
+    LOG_WITH_STREAM(Scrolling, stream << "\nFrameView " << this << " setLayoutViewportOverrideRect() - changing layout viewport from " << oldRect << " to " << m_layoutViewportOverrideRect.value());
+
+    // FIXME: do we need to also do this if the origin changes?
+    if (oldRect.size() != layoutViewportRect().size())
+        setViewportConstrainedObjectsNeedLayout();
+}
+
+LayoutSize FrameView::baseLayoutViewportSize() const
+{
+    return renderView() ? renderView()->size() : size();
+}
+
 void FrameView::updateLayoutViewport()
 {
     if (!frame().settings().visualViewportEnabled())
         return;
+    
+    if (m_layoutViewportOverrideRect) {
+        LOG_WITH_STREAM(Scrolling, stream << "\nFrameView " << this << " updateLayoutViewport() - has layoutViewportOverrideRect" << m_layoutViewportOverrideRect.value());
+        return;
+    }
 
     LayoutRect layoutViewport = layoutViewportRect();
 
@@ -1844,8 +1869,8 @@ void FrameView::updateLayoutViewport()
     LOG_WITH_STREAM(Scrolling, stream << "scroll positions: min: " << unscaledMinimumScrollPosition() << " max: "<< unscaledMaximumScrollPosition());
 
     LayoutPoint newLayoutViewportOrigin = computeLayoutViewportOrigin(visualViewportRect(), minStableLayoutViewportOrigin(), maxStableLayoutViewportOrigin(), layoutViewport);
-    if (newLayoutViewportOrigin != layoutViewportOrigin()) {
-        setLayoutViewportOrigin(newLayoutViewportOrigin);
+    if (newLayoutViewportOrigin != m_layoutViewportOrigin) {
+        setBaseLayoutViewportOrigin(newLayoutViewportOrigin);
         LOG_WITH_STREAM(Scrolling, stream << "layoutViewport changed to " << layoutViewportRect());
     }
 }
@@ -1868,9 +1893,12 @@ IntPoint FrameView::unscaledScrollOrigin() const
     return { };
 }
 
-// Size of initial containing block, anchored at scroll position, in document coordinates (unchanged by scale factor).
 LayoutRect FrameView::layoutViewportRect() const
 {
+    if (m_layoutViewportOverrideRect)
+        return m_layoutViewportOverrideRect.value();
+
+    // Size of initial containing block, anchored at scroll position, in document coordinates (unchanged by scale factor).
     return LayoutRect(m_layoutViewportOrigin, renderView() ? renderView()->size() : size());
 }
 
@@ -1912,7 +1940,7 @@ float FrameView::frameScaleFactor() const
 LayoutPoint FrameView::scrollPositionForFixedPosition() const
 {
     if (frame().settings().visualViewportEnabled())
-        return layoutViewportOrigin();
+        return layoutViewportRect().location();
 
     return scrollPositionForFixedPosition(visibleContentRect(), totalContentsSize(), scrollPosition(), scrollOrigin(), frameScaleFactor(), fixedElementsLayoutRelativeToFrame(), scrollBehaviorForFixedElements(), headerHeight(), footerHeight());
 }
@@ -2068,7 +2096,7 @@ ScrollPosition FrameView::unscaledMaximumScrollPosition() const
     if (RenderView* renderView = this->renderView()) {
         IntRect unscaledDocumentRect = renderView->unscaledDocumentRect();
         unscaledDocumentRect.expand(0, headerHeight() + footerHeight());
-        ScrollPosition maximumPosition = unscaledDocumentRect.maxXMaxYCorner() - visibleSize();
+        ScrollPosition maximumPosition = ScrollPosition(unscaledDocumentRect.maxXMaxYCorner() - visibleSize()).expandedTo({ 0, 0 });
 
         if (frame().isMainFrame() && m_scrollPinningBehavior == PinToTop)
             maximumPosition.setY(unscaledMinimumScrollPosition().y());
index 237bacb..05bb315 100644 (file)
@@ -252,15 +252,21 @@ public:
 
     IntPoint unscaledScrollOrigin() const;
 
+    WEBCORE_EXPORT LayoutPoint minStableLayoutViewportOrigin() const;
+    WEBCORE_EXPORT LayoutPoint maxStableLayoutViewportOrigin() const;
+
     enum class TriggerLayoutOrNot {
         No,
         Yes
     };
-    void setLayoutViewportOrigin(LayoutPoint, TriggerLayoutOrNot = TriggerLayoutOrNot::Yes);
-    LayoutPoint layoutViewportOrigin() const { return m_layoutViewportOrigin; }
+    // This origin can be overridden by setLayoutViewportOverrideRect.
+    void setBaseLayoutViewportOrigin(LayoutPoint, TriggerLayoutOrNot = TriggerLayoutOrNot::Yes);
+    // This size can be overridden by setLayoutViewportOverrideRect.
+    WEBCORE_EXPORT LayoutSize baseLayoutViewportSize() const;
     
-    LayoutPoint minStableLayoutViewportOrigin() const;
-    LayoutPoint maxStableLayoutViewportOrigin() const;
+    // If set, overrides the default "m_layoutViewportOrigin, size of initial containing block" rect.
+    // Used with delegated scrolling (i.e. iOS).
+    WEBCORE_EXPORT void setLayoutViewportOverrideRect(Optional<LayoutRect>);
 
     // These are in document coordinates, unaffected by zooming.
     WEBCORE_EXPORT LayoutRect layoutViewportRect() const;
@@ -304,7 +310,7 @@ public:
     // Static function can be called from another thread.
     static LayoutPoint scrollPositionForFixedPosition(const LayoutRect& visibleContentRect, const LayoutSize& totalContentsSize, const LayoutPoint& scrollPosition, const LayoutPoint& scrollOrigin, float frameScaleFactor, bool fixedElementsLayoutRelativeToFrame, ScrollBehaviorForFixedElements, int headerHeight, int footerHeight);
 
-    static LayoutPoint computeLayoutViewportOrigin(const LayoutRect& visualViewport, const LayoutPoint& stableLayoutViewportOriginMin, const LayoutPoint& stableLayoutViewportOriginMax, const LayoutRect& layoutViewport);
+    WEBCORE_EXPORT static LayoutPoint computeLayoutViewportOrigin(const LayoutRect& visualViewport, const LayoutPoint& stableLayoutViewportOriginMin, const LayoutPoint& stableLayoutViewportOriginMax, const LayoutRect& layoutViewport);
 
     // These layers are positioned differently when there is a topContentInset, a header, or a footer. These value need to be computed
     // on both the main thread and the scrolling thread.
@@ -786,6 +792,7 @@ private:
     Optional<FloatRect> m_viewExposedRect;
     
     LayoutPoint m_layoutViewportOrigin;
+    Optional<LayoutRect> m_layoutViewportOverrideRect;
 
     unsigned m_deferSetNeedsLayoutCount;
     bool m_setNeedsLayoutWasDeferred;
index 4457692..4c286a1 100644 (file)
@@ -352,7 +352,7 @@ void AsyncScrollingCoordinator::reconcileScrollingState(FrameView& frameView, co
     WTF::switchOn(layoutViewportOriginOrOverrideRect,
         [&frameView](Optional<FloatPoint> origin) {
             if (origin)
-                frameView.setLayoutViewportOrigin(LayoutPoint(origin.value()), FrameView::TriggerLayoutOrNot::No);
+                frameView.setBaseLayoutViewportOrigin(LayoutPoint(origin.value()), FrameView::TriggerLayoutOrNot::No);
         }, [&frameView](Optional<FloatRect> overrideRect) {
 #if PLATFORM(IOS)
             if (overrideRect)
@@ -383,7 +383,7 @@ void AsyncScrollingCoordinator::reconcileScrollingState(FrameView& frameView, co
     GraphicsLayer* footerLayer = footerLayerForFrameView(frameView);
 
     ASSERT(frameView.scrollPosition() == roundedIntPoint(scrollPosition));
-    LayoutPoint scrollPositionForFixed = visualViewportEnabled() ? frameView.layoutViewportOrigin() : frameView.scrollPositionForFixedPosition();
+    LayoutPoint scrollPositionForFixed = frameView.scrollPositionForFixedPosition();
     float topContentInset = frameView.topContentInset();
 
     FloatPoint positionForInsetClipLayer;
index 5d66908..73eed54 100644 (file)
@@ -41,6 +41,14 @@ FloatSize::FloatSize(const IntSize& size)
 {
 }
 
+FloatSize FloatSize::constrainedBetween(const FloatSize& min, const FloatSize& max) const
+{
+    return {
+        std::max(min.width(), std::min(max.width(), m_width)),
+        std::max(min.height(), std::min(max.height(), m_height))
+    };
+}
+
 float FloatSize::diagonalLength() const
 {
     return sqrtf(diagonalLengthSquared());
index c7c4de7..e48d080 100644 (file)
@@ -95,6 +95,8 @@ public:
         m_height *= scaleY;
     }
 
+    WEBCORE_EXPORT FloatSize constrainedBetween(const FloatSize& min, const FloatSize& max) const;
+
     FloatSize expandedTo(const FloatSize& other) const
     {
         return FloatSize(m_width > other.m_width ? m_width : other.m_width,
index ca7cc5a..1f5e01d 100644 (file)
 
 namespace WebCore {
 
+LayoutSize LayoutSize::constrainedBetween(const LayoutSize& min, const LayoutSize& max) const
+{
+    return {
+        std::max(min.width(), std::min(max.width(), m_width)),
+        std::max(min.height(), std::min(max.height(), m_height))
+    };
+}
+
 TextStream& operator<<(TextStream& ts, const LayoutSize& size)
 {
     return ts << "width=" << size.width().toFloat() << " height=" << size.height().toFloat();
index 5eb1db7..c11df04 100644 (file)
@@ -87,6 +87,8 @@ public:
         m_width *= widthScale;
         m_height *= heightScale;
     }
+
+    LayoutSize constrainedBetween(const LayoutSize& min, const LayoutSize& max) const;
     
     LayoutSize expandedTo(const LayoutSize& other) const
     {
index 98aacef..dd10fb3 100644 (file)
@@ -407,6 +407,9 @@ LayoutUnit RenderView::clientLogicalWidthForFixedPosition() const
         return isHorizontalWritingMode() ? frameView().customFixedPositionLayoutRect().width() : frameView().customFixedPositionLayoutRect().height();
 #endif
 
+    if (frameView().frame().settings().visualViewportEnabled())
+        return isHorizontalWritingMode() ? frameView().layoutViewportRect().width() : frameView().layoutViewportRect().height();
+
     return clientLogicalWidth();
 }
 
@@ -421,6 +424,9 @@ LayoutUnit RenderView::clientLogicalHeightForFixedPosition() const
         return isHorizontalWritingMode() ? frameView().customFixedPositionLayoutRect().height() : frameView().customFixedPositionLayoutRect().width();
 #endif
 
+    if (frameView().frame().settings().visualViewportEnabled())
+        return isHorizontalWritingMode() ? frameView().layoutViewportRect().height() : frameView().layoutViewportRect().width();
+
     return clientLogicalHeight();
 }
 
index fb1180d..a43d2ac 100644 (file)
@@ -1,3 +1,62 @@
+2016-11-14  Simon Fraser  <simon.fraser@apple.com>
+
+        [iOS WK2] Implement support for visual viewports
+        https://bugs.webkit.org/show_bug.cgi?id=164765
+
+        Reviewed by Tim Horton.
+
+        Adopt the visual viewport scrolling model in iOS WK2.
+
+        Pass the parameters used for computing the layout viewport up to WK2 via RemoteLayerTreeTransaction.
+        These are stored on WebPageProxy. When they change, _didCommitLayerTree triggers a -_updateVisibleContentRects.
+        
+        WebPageProxy::computeCustomFixedPositionRect() is the function that computes the "override" layout viewport.
+        It starts with the baseLayoutViewportSize from the web process (which is based on the initial containing block
+        size), then ensures that it's no smaller than the unobscured content rect, since it makes no sense for the
+        layout viewport to be smaller than the visual viewport. The static FrameView::computeLayoutViewportOrigin()
+        is then use to "push" the layout viewport around as the visual viewport changes.
+
+        * Shared/VisibleContentRectUpdateInfo.h:
+        * Shared/WebCoreArgumentCoders.cpp: Encode LayoutSize and LayoutPoint.
+        (IPC::ArgumentCoder<LayoutSize>::encode):
+        (IPC::ArgumentCoder<LayoutSize>::decode):
+        (IPC::ArgumentCoder<LayoutPoint>::encode):
+        (IPC::ArgumentCoder<LayoutPoint>::decode):
+        * Shared/WebCoreArgumentCoders.h:
+        * Shared/mac/RemoteLayerTreeTransaction.h:
+        (WebKit::RemoteLayerTreeTransaction::baseLayoutViewportSize):
+        (WebKit::RemoteLayerTreeTransaction::setBaseLayoutViewportSize):
+        (WebKit::RemoteLayerTreeTransaction::minStableLayoutViewportOrigin):
+        (WebKit::RemoteLayerTreeTransaction::setMinStableLayoutViewportOrigin):
+        (WebKit::RemoteLayerTreeTransaction::maxStableLayoutViewportOrigin):
+        (WebKit::RemoteLayerTreeTransaction::setMaxStableLayoutViewportOrigin):
+        * Shared/mac/RemoteLayerTreeTransaction.mm:
+        (WebKit::RemoteLayerTreeTransaction::encode):
+        (WebKit::RemoteLayerTreeTransaction::decode):
+        (WebKit::RemoteLayerTreeTransaction::description):
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView _didCommitLayerTree:]):
+        * UIProcess/Scrolling/RemoteScrollingCoordinatorProxy.h:
+        (WebKit::RemoteScrollingCoordinatorProxy::visualViewportEnabled): Accessor.
+        * UIProcess/WebPageProxy.h:
+        (WebKit::WebPageProxy::customFixedPositionRect):
+        * UIProcess/ios/RemoteScrollingCoordinatorProxyIOS.mm:
+        (WebKit::RemoteScrollingCoordinatorProxy::customFixedPositionRect):
+        * UIProcess/ios/WKContentView.mm:
+        (-[WKContentView didUpdateVisibleRect:unobscuredRect:unobscuredRectInScrollViewCoordinates:obscuredInset:scale:minimumScale:inStableState:isChangingObscuredInsetsInteractively:enclosedInScrollableAncestorView:]):
+        (-[WKContentView _didCommitLayerTree:]):
+        * UIProcess/ios/WebPageProxyIOS.mm:
+        (WebKit::WebPageProxy::computeCustomFixedPositionRect): When visual viewports are enabled, compute
+        the layout viewport rect, taking the baseLayoutViewportSize and the current unobscured rect into account.
+        (WebKit::WebPageProxy::updateLayoutViewportParameters):
+        * UIProcess/mac/RemoteLayerTreeDrawingAreaProxy.mm:
+        (WebKit::RemoteLayerTreeDrawingAreaProxy::commitLayerTree):
+        * WebProcess/WebPage/WebPage.cpp: Encode in the transaction the layout viewport parameters (with minor refactor).
+        (WebKit::WebPage::willCommitLayerTree):
+        * WebProcess/WebPage/ios/WebPageIOS.mm:
+        (WebKit::WebPage::updateVisibleContentRects): This is where the web process receives the new override layout viewport
+        from the web process (with some logging).
+
 2016-11-15  Jon Lee  <jonlee@apple.com>
 
         Remove HasMediaCaptureDevice
index 6ba1d68..b6b255a 100644 (file)
@@ -87,7 +87,7 @@ private:
     WebCore::FloatRect m_exposedContentRect;
     WebCore::FloatRect m_unobscuredContentRect;
     WebCore::FloatRect m_unobscuredRectInScrollViewCoordinates;
-    WebCore::FloatRect m_customFixedPositionRect;
+    WebCore::FloatRect m_customFixedPositionRect; // When visual viewports are enabled, this is the layout viewport.
     WebCore::FloatSize m_obscuredInset;
     uint64_t m_lastLayerTreeTransactionID { 0 };
     double m_scale { -1 };
index c0e78e2..39ee406 100644 (file)
@@ -433,6 +433,29 @@ bool ArgumentCoder<IntSize>::decode(Decoder& decoder, IntSize& intSize)
     return SimpleArgumentCoder<IntSize>::decode(decoder, intSize);
 }
 
+
+void ArgumentCoder<LayoutSize>::encode(Encoder& encoder, const LayoutSize& layoutSize)
+{
+    SimpleArgumentCoder<LayoutSize>::encode(encoder, layoutSize);
+}
+
+bool ArgumentCoder<LayoutSize>::decode(Decoder& decoder, LayoutSize& layoutSize)
+{
+    return SimpleArgumentCoder<LayoutSize>::decode(decoder, layoutSize);
+}
+
+
+void ArgumentCoder<LayoutPoint>::encode(Encoder& encoder, const LayoutPoint& layoutPoint)
+{
+    SimpleArgumentCoder<LayoutPoint>::encode(encoder, layoutPoint);
+}
+
+bool ArgumentCoder<LayoutPoint>::decode(Decoder& decoder, LayoutPoint& layoutPoint)
+{
+    return SimpleArgumentCoder<LayoutPoint>::decode(decoder, layoutPoint);
+}
+
+
 static void pathEncodeApplierFunction(Encoder& encoder, const PathElement& element)
 {
     encoder.encodeEnum(element.type);
index e86a79f..d6ef3dd 100644 (file)
@@ -54,6 +54,8 @@ class IntPoint;
 class IntRect;
 class IntSize;
 class KeyframeValueList;
+class LayoutSize;
+class LayoutPoint;
 class LinearTimingFunction;
 class Notification;
 class Path;
@@ -236,6 +238,16 @@ template<> struct ArgumentCoder<WebCore::IntSize> {
     static bool decode(Decoder&, WebCore::IntSize&);
 };
 
+template<> struct ArgumentCoder<WebCore::LayoutSize> {
+    static void encode(Encoder&, const WebCore::LayoutSize&);
+    static bool decode(Decoder&, WebCore::LayoutSize&);
+};
+
+template<> struct ArgumentCoder<WebCore::LayoutPoint> {
+    static void encode(Encoder&, const WebCore::LayoutPoint&);
+    static bool decode(Decoder&, WebCore::LayoutPoint&);
+};
+
 template<> struct ArgumentCoder<WebCore::Path> {
     static void encode(Encoder&, const WebCore::Path&);
     static bool decode(Decoder&, WebCore::Path&);
index 95ca502..b5e9055 100644 (file)
@@ -204,6 +204,15 @@ public:
 
     WebCore::IntPoint scrollOrigin() const { return m_scrollOrigin; }
     void setScrollOrigin(const WebCore::IntPoint& origin) { m_scrollOrigin = origin; };
+
+    WebCore::LayoutSize baseLayoutViewportSize() const { return m_baseLayoutViewportSize; }
+    void setBaseLayoutViewportSize(const WebCore::LayoutSize& size) { m_baseLayoutViewportSize = size; };
+    
+    WebCore::LayoutPoint minStableLayoutViewportOrigin() const { return m_minStableLayoutViewportOrigin; }
+    void setMinStableLayoutViewportOrigin(const WebCore::LayoutPoint& point) { m_minStableLayoutViewportOrigin = point; };
+    
+    WebCore::LayoutPoint maxStableLayoutViewportOrigin() const { return m_maxStableLayoutViewportOrigin; }
+    void setMaxStableLayoutViewportOrigin(const WebCore::LayoutPoint& point) { m_maxStableLayoutViewportOrigin = point; };
     
     WebCore::Color pageExtendedBackgroundColor() const { return m_pageExtendedBackgroundColor; }
     void setPageExtendedBackgroundColor(WebCore::Color color) { m_pageExtendedBackgroundColor = color; }
@@ -267,6 +276,9 @@ private:
 
     WebCore::IntSize m_contentsSize;
     WebCore::IntPoint m_scrollOrigin;
+    WebCore::LayoutSize m_baseLayoutViewportSize;
+    WebCore::LayoutPoint m_minStableLayoutViewportOrigin;
+    WebCore::LayoutPoint m_maxStableLayoutViewportOrigin;
 #if PLATFORM(MAC)
     WebCore::IntPoint m_scrollPosition;
 #endif
index a716850..09f9488 100644 (file)
@@ -527,6 +527,11 @@ void RemoteLayerTreeTransaction::encode(IPC::Encoder& encoder) const
 
     encoder << m_contentsSize;
     encoder << m_scrollOrigin;
+
+    encoder << m_baseLayoutViewportSize;
+    encoder << m_minStableLayoutViewportOrigin;
+    encoder << m_maxStableLayoutViewportOrigin;
+
 #if PLATFORM(MAC)
     encoder << m_scrollPosition;
 #endif
@@ -602,6 +607,15 @@ bool RemoteLayerTreeTransaction::decode(IPC::Decoder& decoder, RemoteLayerTreeTr
     if (!decoder.decode(result.m_scrollOrigin))
         return false;
 
+    if (!decoder.decode(result.m_baseLayoutViewportSize))
+        return false;
+
+    if (!decoder.decode(result.m_minStableLayoutViewportOrigin))
+        return false;
+
+    if (!decoder.decode(result.m_maxStableLayoutViewportOrigin))
+        return false;
+    
 #if PLATFORM(MAC)
     if (!decoder.decode(result.m_scrollPosition))
         return false;
@@ -844,6 +858,12 @@ CString RemoteLayerTreeTransaction::description() const
     if (m_scrollOrigin != IntPoint::zero())
         ts.dumpProperty("scrollOrigin", m_scrollOrigin);
 
+    ts.dumpProperty("baseLayoutViewportSize", FloatSize(m_baseLayoutViewportSize));
+
+    if (m_minStableLayoutViewportOrigin != LayoutPoint::zero())
+        ts.dumpProperty("minStableLayoutViewportOrigin", FloatPoint(m_minStableLayoutViewportOrigin));
+    ts.dumpProperty("maxStableLayoutViewportOrigin", FloatPoint(m_maxStableLayoutViewportOrigin));
+
     if (m_pageScaleFactor != 1)
         ts.dumpProperty("pageScaleFactor", m_pageScaleFactor);
 
index b669b6e..03327eb 100644 (file)
@@ -1273,6 +1273,9 @@ static inline bool areEssentiallyEqualAsFloat(float a, float b)
     _viewportMetaTagWidthWasExplicit = layerTreeTransaction.viewportMetaTagWidthWasExplicit();
     _viewportMetaTagCameFromImageDocument = layerTreeTransaction.viewportMetaTagCameFromImageDocument();
     _initialScaleFactor = layerTreeTransaction.initialScaleFactor();
+
+    BOOL needUpdateVisbleContentRects = _page->updateLayoutViewportParameters(layerTreeTransaction);
+
     if (![_contentView _mayDisableDoubleTapGesturesDuringSingleTap])
         [_contentView _setDoubleTapGesturesEnabled:self._allowsDoubleTapGestures];
 
@@ -1284,9 +1287,10 @@ static inline bool areEssentiallyEqualAsFloat(float a, float b)
     if (_needsResetViewStateAfterCommitLoadForMainFrame && layerTreeTransaction.transactionID() >= _firstPaintAfterCommitLoadTransactionID) {
         _needsResetViewStateAfterCommitLoadForMainFrame = NO;
         [_scrollView setContentOffset:[self _adjustedContentOffset:CGPointZero]];
-        [self _updateVisibleContentRects];
         if (_observedRenderingProgressEvents & _WKRenderingProgressEventFirstPaint)
             _navigationState->didFirstPaint();
+
+        needUpdateVisbleContentRects = YES;
     }
 
     bool isTransactionAfterPageRestore = layerTreeTransaction.transactionID() >= _firstTransactionIDAfterPageRestore;
@@ -1305,7 +1309,8 @@ static inline bool areEssentiallyEqualAsFloat(float a, float b)
             if (_gestureController)
                 _gestureController->didRestoreScrollPosition();
         }
-        [self _updateVisibleContentRects];
+        
+        needUpdateVisbleContentRects = YES;
     }
 
     if (_needsToRestoreUnobscuredCenter && isTransactionAfterPageRestore) {
@@ -1323,9 +1328,13 @@ static inline bool areEssentiallyEqualAsFloat(float a, float b)
             if (_gestureController)
                 _gestureController->didRestoreScrollPosition();
         }
-        [self _updateVisibleContentRects];
+
+        needUpdateVisbleContentRects = YES;
     }
-    
+
+    if (needUpdateVisbleContentRects)
+        [self _updateVisibleContentRects];
+
     if (WebKit::RemoteLayerTreeScrollingPerformanceData* scrollPerfData = _page->scrollingPerformanceData())
         scrollPerfData->didCommitLayerTree([self visibleRectInViewCoordinates]);
 }
index 164a57e..c40a6c7 100644 (file)
@@ -51,7 +51,9 @@ class RemoteScrollingCoordinatorProxy {
 public:
     explicit RemoteScrollingCoordinatorProxy(WebPageProxy&);
     virtual ~RemoteScrollingCoordinatorProxy();
-    
+
+    bool visualViewportEnabled() const { return m_scrollingTree && m_scrollingTree->visualViewportEnabled(); }
+
     // Inform the web process that the scroll position changed (called from the scrolling tree)
     void scrollingTreeNodeDidScroll(WebCore::ScrollingNodeID, const WebCore::FloatPoint& newScrollPosition, const Optional<WebCore::FloatPoint>& layoutViewportOrigin, WebCore::SetOrSyncScrollingLayerPosition);
     void scrollingTreeNodeRequestsScroll(WebCore::ScrollingNodeID, const WebCore::FloatPoint& scrollPosition, bool representsProgrammaticScroll);
index 1f99aaf..d5ffa65 100644 (file)
@@ -461,12 +461,15 @@ public:
     double displayedContentScale() const { return m_lastVisibleContentRectUpdate.scale(); }
     const WebCore::FloatRect& exposedContentRect() const { return m_lastVisibleContentRectUpdate.exposedContentRect(); }
     const WebCore::FloatRect& unobscuredContentRect() const { return m_lastVisibleContentRectUpdate.unobscuredContentRect(); }
+    // When visual viewports are enabled, this is the layout viewport rect.
+    const WebCore::FloatRect& customFixedPositionRect() const { return m_lastVisibleContentRectUpdate.customFixedPositionRect(); }
 
     void updateVisibleContentRects(const VisibleContentRectUpdateInfo&);
     void resendLastVisibleContentRects();
 
     enum class UnobscuredRectConstraint { ConstrainedToDocumentRect, Unconstrained };
-    WebCore::FloatRect computeCustomFixedPositionRect(const WebCore::FloatRect& unobscuredContentRect, double displayedContentScale, UnobscuredRectConstraint = UnobscuredRectConstraint::Unconstrained) const;
+    WebCore::FloatRect computeCustomFixedPositionRect(const WebCore::FloatRect& unobscuredContentRect, const WebCore::FloatRect& currentCustomFixedPositionRect, double displayedContentScale, UnobscuredRectConstraint = UnobscuredRectConstraint::Unconstrained, bool visualViewportEnabled = false) const;
+
     void overflowScrollViewWillStartPanGesture();
     void overflowScrollViewDidScroll();
     void overflowScrollWillStartScroll();
@@ -536,6 +539,8 @@ public:
     void didCommitLayerTree(const WebKit::RemoteLayerTreeTransaction&);
     void layerTreeCommitComplete();
 
+    bool updateLayoutViewportParameters(const WebKit::RemoteLayerTreeTransaction&);
+
 #if USE(COORDINATED_GRAPHICS_MULTIPROCESS)
     void didRenderFrame(const WebCore::IntSize& contentsSize, const WebCore::IntRect& coveredRect);
     void commitPageTransitionViewport();
@@ -1875,6 +1880,11 @@ private:
     bool m_autoSizingShouldExpandToViewHeight;
     WebCore::IntSize m_minimumLayoutSize;
 
+    // Visual viewports
+    WebCore::LayoutSize m_baseLayoutViewportSize;
+    WebCore::LayoutPoint m_minStableLayoutViewportOrigin;
+    WebCore::LayoutPoint m_maxStableLayoutViewportOrigin;
+
     float m_mediaVolume;
     WebCore::MediaProducer::MutedStateFlags m_mutedState { WebCore::MediaProducer::NoneMuted };
     bool m_mayStartMediaWhenInWindow;
index f14cf89..10c8df7 100644 (file)
@@ -94,7 +94,7 @@ void RemoteScrollingCoordinatorProxy::connectStateNodeLayers(ScrollingStateTree&
 
 FloatRect RemoteScrollingCoordinatorProxy::customFixedPositionRect() const
 {
-    return m_webPageProxy.computeCustomFixedPositionRect(m_webPageProxy.unobscuredContentRect(), m_webPageProxy.displayedContentScale());
+    return m_webPageProxy.computeCustomFixedPositionRect(m_webPageProxy.unobscuredContentRect(), m_webPageProxy.customFixedPositionRect(), m_webPageProxy.displayedContentScale());
 }
 
 void RemoteScrollingCoordinatorProxy::scrollingTreeNodeWillStartPanGesture()
index 19c4401..b61aecd 100644 (file)
@@ -377,9 +377,10 @@ private:
     else
         _historicalKinematicData.clear();
 
-    FloatRect fixedPositionRectForLayout = _page->computeCustomFixedPositionRect(unobscuredRect, zoomScale, WebPageProxy::UnobscuredRectConstraint::ConstrainedToDocumentRect);
+    RemoteScrollingCoordinatorProxy* scrollingCoordinator = _page->scrollingCoordinatorProxy();
+    FloatRect fixedPositionRectForLayout = _page->computeCustomFixedPositionRect(unobscuredRect, _page->customFixedPositionRect(), zoomScale, WebPageProxy::UnobscuredRectConstraint::ConstrainedToDocumentRect, scrollingCoordinator->visualViewportEnabled());
 
-    LOG_WITH_STREAM(VisibleRects, stream << "didUpdateVisibleRect: visibleRect:" << visibleRect << " unobscuredRect:" << unobscuredRect << " fixedPositionRectForLayout:" << fixedPositionRectForLayout);
+    LOG_WITH_STREAM(VisibleRects, stream << "didUpdateVisibleRect: visibleRect:" << visibleRect << " unobscuredRect:" << unobscuredRect << " fixedPositionRectForLayout:" << fixedPositionRectForLayout << " stable: " << isStableState);
 
     VisibleContentRectUpdateInfo visibleContentRectUpdateInfo(
         visibleRect,
@@ -400,8 +401,7 @@ private:
 
     _page->updateVisibleContentRects(visibleContentRectUpdateInfo);
 
-    RemoteScrollingCoordinatorProxy* scrollingCoordinator = _page->scrollingCoordinatorProxy();
-    FloatRect fixedPositionRect = _page->computeCustomFixedPositionRect(_page->unobscuredContentRect(), zoomScale);
+    FloatRect fixedPositionRect = _page->computeCustomFixedPositionRect(_page->unobscuredContentRect(), _page->customFixedPositionRect(), zoomScale, WebPageProxy::UnobscuredRectConstraint::Unconstrained, scrollingCoordinator->visualViewportEnabled());
     scrollingCoordinator->viewportChangedViaDelegatedScrolling(scrollingCoordinator->rootScrollingNodeID(), fixedPositionRect, zoomScale);
 
     drawingArea->updateDebugIndicator();
@@ -536,7 +536,8 @@ static void storeAccessibilityRemoteConnectionInformation(id element, pid_t pid,
     }
     
     if (boundsChanged) {
-        FloatRect fixedPositionRect = _page->computeCustomFixedPositionRect(_page->unobscuredContentRect(), [[_webView scrollView] zoomScale]);
+        // FIXME: factor computeCustomFixedPositionRect() into something that gives us this rect.
+        FloatRect fixedPositionRect = _page->computeCustomFixedPositionRect(_page->unobscuredContentRect(), _page->customFixedPositionRect(), [[_webView scrollView] zoomScale]);
         [self updateFixedClippingView:fixedPositionRect];
 
         // We need to push the new content bounds to the webview to update fixed position rects.
index 3805188..73c9d99 100644 (file)
@@ -32,6 +32,7 @@
 #import "DataReference.h"
 #import "EditingRange.h"
 #import "InteractionInformationAtPosition.h"
+#import "Logging.h"
 #import "NativeWebKeyboardEvent.h"
 #import "PageClient.h"
 #import "PrintInfo.h"
@@ -49,6 +50,7 @@
 #import <WebCore/NotImplemented.h>
 #import <WebCore/PlatformScreen.h>
 #import <WebCore/SharedBuffer.h>
+#import <WebCore/TextStream.h>
 #import <WebCore/UserAgent.h>
 #import <WebCore/ValidationBubble.h>
 
@@ -217,7 +219,8 @@ static inline float adjustedUnexposedMaxEdge(float documentEdge, float exposedRe
     return exposedRectEdge;
 }
 
-WebCore::FloatRect WebPageProxy::computeCustomFixedPositionRect(const FloatRect& unobscuredContentRect, double displayedContentScale, UnobscuredRectConstraint constraint) const
+// FIXME: rename this when visual viewports are the default.
+WebCore::FloatRect WebPageProxy::computeCustomFixedPositionRect(const FloatRect& unobscuredContentRect, const FloatRect& currentCustomFixedPositionRect, double displayedContentScale, UnobscuredRectConstraint constraint, bool visualViewportEnabled) const
 {
     FloatRect constrainedUnobscuredRect = unobscuredContentRect;
     FloatRect documentRect = m_pageClient.documentRect();
@@ -240,6 +243,21 @@ WebCore::FloatRect WebPageProxy::computeCustomFixedPositionRect(const FloatRect&
         constrainedUnobscuredRect.setHeight(adjustedUnexposedMaxEdge(documentRect.maxY(), constrainedUnobscuredRect.maxY(), factor) - constrainedUnobscuredRect.y());
     }
     
+    if (visualViewportEnabled) {
+        FloatRect layoutViewportRect = currentCustomFixedPositionRect;
+        
+        // The layout viewport is never smaller than m_baseLayoutViewportSize, and never be smaller than the constrainedUnobscuredRect.
+        FloatSize constrainedSize = m_baseLayoutViewportSize;
+        layoutViewportRect.setSize(constrainedSize.expandedTo(constrainedUnobscuredRect.size()));
+
+        LayoutPoint layoutViewportOrigin = FrameView::computeLayoutViewportOrigin(enclosingLayoutRect(constrainedUnobscuredRect), m_minStableLayoutViewportOrigin, m_maxStableLayoutViewportOrigin, enclosingLayoutRect(layoutViewportRect));
+        layoutViewportRect.setLocation(layoutViewportOrigin);
+
+        if (layoutViewportRect != currentCustomFixedPositionRect)
+            LOG_WITH_STREAM(VisibleRects, stream << "WebPageProxy::computeCustomFixedPositionRect: new layout viewport  " << layoutViewportRect);
+        return layoutViewportRect;
+    }
+    
     return FrameView::rectForViewportConstrainedObjects(enclosingLayoutRect(constrainedUnobscuredRect), LayoutSize(documentRect.size()), displayedContentScale, false, StickToViewportBounds);
 }
 
@@ -377,6 +395,19 @@ void WebPageProxy::didCommitLayerTree(const WebKit::RemoteLayerTreeTransaction&
     }
 }
 
+bool WebPageProxy::updateLayoutViewportParameters(const WebKit::RemoteLayerTreeTransaction& layerTreeTransaction)
+{
+    bool changed = m_baseLayoutViewportSize != layerTreeTransaction.baseLayoutViewportSize()
+        || m_minStableLayoutViewportOrigin != layerTreeTransaction.minStableLayoutViewportOrigin()
+        || m_maxStableLayoutViewportOrigin != layerTreeTransaction.maxStableLayoutViewportOrigin();
+
+    m_baseLayoutViewportSize = layerTreeTransaction.baseLayoutViewportSize();
+    m_minStableLayoutViewportOrigin = layerTreeTransaction.minStableLayoutViewportOrigin();
+    m_maxStableLayoutViewportOrigin = layerTreeTransaction.maxStableLayoutViewportOrigin();
+    
+    return changed;
+}
+
 void WebPageProxy::layerTreeCommitComplete()
 {
     m_pageClient.layerTreeCommitComplete();
index 007748e..ef5c338 100644 (file)
@@ -202,7 +202,7 @@ void RemoteLayerTreeDrawingAreaProxy::commitLayerTree(const RemoteLayerTreeTrans
 #if PLATFORM(IOS)
     if (m_webPageProxy.scrollingCoordinatorProxy()->hasFixedOrSticky()) {
         // If we got a new layer for a fixed or sticky node, its position from the WebProcess is probably stale. We need to re-run the "viewport" changed logic to udpate it with our UI-side state.
-        FloatRect customFixedPositionRect = m_webPageProxy.computeCustomFixedPositionRect(m_webPageProxy.unobscuredContentRect(), m_webPageProxy.displayedContentScale());
+        FloatRect customFixedPositionRect = m_webPageProxy.computeCustomFixedPositionRect(m_webPageProxy.unobscuredContentRect(), m_webPageProxy.customFixedPositionRect(), m_webPageProxy.displayedContentScale(), WebPageProxy::UnobscuredRectConstraint::Unconstrained, m_webPageProxy.scrollingCoordinatorProxy()->visualViewportEnabled());
         m_webPageProxy.scrollingCoordinatorProxy()->viewportChangedViaDelegatedScrolling(m_webPageProxy.scrollingCoordinatorProxy()->rootScrollingNodeID(), customFixedPositionRect, m_webPageProxy.displayedContentScale());
     }
 #endif
index 6f03a28..6fd0c79 100644 (file)
@@ -3256,11 +3256,20 @@ void WebPage::setDataDetectionResults(NSArray *detectionResults)
 #if PLATFORM(COCOA)
 void WebPage::willCommitLayerTree(RemoteLayerTreeTransaction& layerTransaction)
 {
-    layerTransaction.setContentsSize(corePage()->mainFrame().view()->contentsSize());
-    layerTransaction.setScrollOrigin(corePage()->mainFrame().view()->scrollOrigin());
+    FrameView* frameView = corePage()->mainFrame().view();
+    if (!frameView)
+        return;
+
+    layerTransaction.setContentsSize(frameView->contentsSize());
+    layerTransaction.setScrollOrigin(frameView->scrollOrigin());
     layerTransaction.setPageScaleFactor(corePage()->pageScaleFactor());
     layerTransaction.setRenderTreeSize(corePage()->renderTreeSize());
     layerTransaction.setPageExtendedBackgroundColor(corePage()->pageExtendedBackgroundColor());
+
+    layerTransaction.setBaseLayoutViewportSize(frameView->baseLayoutViewportSize());
+    layerTransaction.setMinStableLayoutViewportOrigin(frameView->minStableLayoutViewportOrigin());
+    layerTransaction.setMaxStableLayoutViewportOrigin(frameView->maxStableLayoutViewportOrigin());
+
 #if PLATFORM(IOS)
     layerTransaction.setScaleWasSetByUIProcess(scaleWasSetByUIProcess());
     layerTransaction.setMinimumScaleFactor(m_viewportConfiguration.minimumScale());
@@ -3271,8 +3280,9 @@ void WebPage::willCommitLayerTree(RemoteLayerTreeTransaction& layerTransaction)
     layerTransaction.setViewportMetaTagCameFromImageDocument(m_viewportConfiguration.viewportArguments().type == ViewportArguments::ImageDocument);
     layerTransaction.setAllowsUserScaling(allowsUserScaling());
 #endif
+
 #if PLATFORM(MAC)
-    layerTransaction.setScrollPosition(corePage()->mainFrame().view()->scrollPosition());
+    layerTransaction.setScrollPosition(frameView->scrollPosition());
 #endif
 }
 
index 0563c35..a01fa04 100644 (file)
@@ -36,6 +36,7 @@
 #import "EditorState.h"
 #import "GestureTypes.h"
 #import "InteractionInformationAtPosition.h"
+#import "Logging.h"
 #import "PluginView.h"
 #import "RemoteLayerTreeDrawingArea.h"
 #import "UserData.h"
@@ -97,6 +98,7 @@
 #import <WebCore/StyleProperties.h>
 #import <WebCore/TextIndicator.h>
 #import <WebCore/TextIterator.h>
+#import <WebCore/TextStream.h>
 #import <WebCore/VisibleUnits.h>
 #import <WebCore/WKContentObservation.h>
 #import <WebCore/WebEvent.h>
@@ -3054,8 +3056,13 @@ void WebPage::updateVisibleContentRects(const VisibleContentRectUpdateInfo& visi
     frameView.setViewportIsStable(m_isInStableState);
     frameView.setScrollVelocity(horizontalVelocity, verticalVelocity, scaleChangeRate, visibleContentRectUpdateInfo.timestamp());
 
-    if (m_isInStableState)
-        frameView.setCustomFixedPositionLayoutRect(enclosingIntRect(visibleContentRectUpdateInfo.customFixedPositionRect()));
+    LOG_WITH_STREAM(VisibleRects, stream << "WebPage::updateVisibleContentRects setting layoutViewportOverrideRect " << visibleContentRectUpdateInfo.customFixedPositionRect() << " stable " << m_isInStableState);
+    if (m_isInStableState) {
+        if (frameView.frame().settings().visualViewportEnabled())
+            frameView.setLayoutViewportOverrideRect(LayoutRect(visibleContentRectUpdateInfo.customFixedPositionRect()));
+        else
+            frameView.setCustomFixedPositionLayoutRect(enclosingIntRect(visibleContentRectUpdateInfo.customFixedPositionRect()));
+    }
 
     if (!visibleContentRectUpdateInfo.isChangingObscuredInsetsInteractively())
         frameView.setCustomSizeForResizeEvent(expandedIntSize(visibleContentRectUpdateInfo.unobscuredRectInScrollViewCoordinates().size()));