- Revision
- 244849
- Author
- [email protected]
- Date
- 2019-05-01 14:08:38 -0700 (Wed, 01 May 2019)
Log Message
[iOS] Add a version of viewport shrink-to-fit heuristics that preserves page layout
https://bugs.webkit.org/show_bug.cgi?id=197342
<rdar://problem/50063091>
Reviewed by Tim Horton.
Source/WebCore:
Adds support for a new shrink-to-fit heuristic that attempts to lay out the contents of the page at a larger
width in order to shrink content to fit the viewport. See WebKit ChangeLog for more details.
Tests: fast/viewport/ios/shrink-to-fit-content-constant-width.html
fast/viewport/ios/shrink-to-fit-content-large-width-breakpoint.html
fast/viewport/ios/shrink-to-fit-content-no-viewport.html
fast/viewport/ios/shrink-to-fit-content-responsive-viewport-with-horizontal-overflow.html
fast/viewport/ios/shrink-to-fit-content-temporary-overflow.html
* page/ViewportConfiguration.cpp:
(WebCore::ViewportConfiguration::setMinimumEffectiveDeviceWidth):
(WebCore::ViewportConfiguration::setIsKnownToLayOutWiderThanViewport):
(WebCore::ViewportConfiguration::description const):
* page/ViewportConfiguration.h:
(WebCore::ViewportConfiguration::canIgnoreScalingConstraints const):
(WebCore::ViewportConfiguration::minimumEffectiveDeviceWidth const):
Add several new getters and setters in ViewportConfiguration.
(WebCore::ViewportConfiguration::isKnownToLayOutWiderThanViewport const):
(WebCore::ViewportConfiguration::shouldIgnoreMinimumEffectiveDeviceWidth const):
Importantly, only allow ignoring the minimum effective device width in webpages with responsive viewports, if
they also have *not* laid out wider than the viewport.
(WebCore::ViewportConfiguration::setForceAlwaysUserScalable):
Source/WebKit:
This patch introduces a new shrink-to-fit heuristic that attempts to lay out the contents of the page at a
larger width in order to shrink content to fit the viewport. This is similar to existing shrink-to-fit behaviors
used for viewport sizing in multitasking mode, except that it not only scales the view, but additionally expands
the layout size, such that the overall layout of the page is preserved. In fact, the reason we ended up
reverting the existing flavor of shrink-to-fit in all cases except for multitasking was that page layout was not
preserved, which caused elements that poke out of the viewport to make the rest of the page look out of
proportion — see <rdar://problem/23818102> and related radars.
Covered by 5 new layout tests, and by adjusting a couple of existing layout tests. See comments below for more
details.
* Platform/Logging.h:
Add a new ViewportSizing logging channel. This will only log on pages that overflow the viewport and shrink to
fit as a result.
* Shared/WebPreferences.yaml:
Turn IgnoreViewportScalingConstraints off by default. This preference currently controls whether we allow
shrink-to-fit behaviors, and is only used by Safari when it is in multitasking mode. The value of this
preference is currenly *on* by default, and is turned off almost immediately during every page load after the
first visible content rect update, wherein visibleContentRectUpdateInfo.allowShrinkToFit() is false.
However, this sometimes causes a brief jitter during page load; to fix this, make the default value for
IgnoreViewportScalingConstraints false, and change the logic in WebPage::updateVisibleContentRects to
setCanIgnoreScalingConstraints to true if either the IgnoreViewportScalingConstraints preference (not only
affected by an internal debug switch) is true, or WKWebView SPI is used to enable the behavior.
* WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp:
(WebKit::WebFrameLoaderClient::dispatchDidFinishDocumentLoad):
(WebKit::WebFrameLoaderClient::dispatchDidFinishLoad):
Add a new hook for WebFrameLoaderClient to call into WebPage when document load finishes. Also, tweak
dispatchDidFinishLoad to take a WebFrame& instead of a WebFrame* in a drive-by fix (the frame is assumed to be
non-null anyways).
* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::didCommitLoad):
(WebKit::WebPage::didFinishDocumentLoad):
(WebKit::WebPage::didFinishLoad):
When finishing document load or finishing the overall load, kick off the shrink-to-fit timer; when committing a
load, cancel the timer.
* WebProcess/WebPage/WebPage.h:
* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::setViewportConfigurationViewLayoutSize):
Don't allow the minimum effective device width from the client to stomp over any minimum effective device width
set as a result of the new shrink-to-fit heuristic; on some pages that load quickly, this can result in a race
where the minimum effective device width (i.e. a value that lower-bounds the minimum layout width) is first set
by the shrink-to-fit heuristic, and then set to an incorrect value by the client.
In the near future, web view SPI used to set the minimum effective device width should actually be removed
altogether, since the new shrink-to-fit heuristic supersedes any need for the client to fiddle with the minimum
effective device width.
(WebKit::WebPage::dynamicViewportSizeUpdate):
When performing a dynamic viewport size update, additionally re-run the shrink-to-fit heuristic. This allows
the minimum layout size of the viewport to be updated, if necessary. An example of where this matters is when a
web page is *below* a tablet/desktop layout breakpoint in portrait device orientation, but then exceeds this
layout breakpoint in landscape orientation. In this scenario, rotating the device should swap between these two
page layouts.
(WebKit::WebPage::resetViewportDefaultConfiguration):
(WebKit::WebPage::scheduleShrinkToFitContent):
(WebKit::WebPage::shrinkToFitContentTimerFired):
(WebKit::WebPage::immediatelyShrinkToFitContent):
Leverage the existing capability for a viewport to have a "minimum effective device width" to grant the viewport
a larger layout size than it would normally have, and then scale down to fit within the bounds of the view. One
challenge with this overall approach is that laying out at a larger width may cause the page to lay out even
wider in response, which may actually worsen horizontal scrolling. To mitigate this, we only attempt to lay out
at the current content width once; if laying out at this width reduced the amount of horizontal scrolling by any
amount, then proceed with this layout width; otherwise, revert to the previous layout width.
(WebKit::WebPage::shouldIgnoreMetaViewport const):
Pull some common logic out into a readonly getter.
(WebKit::WebPage::updateVisibleContentRects):
See the comment below WebPreferences.yaml, above.
LayoutTests:
Introduces new layout tests, and adjusts some existing tests. See comments below.
* fast/viewport/ios/shrink-to-fit-content-constant-width-expected.txt: Added.
* fast/viewport/ios/shrink-to-fit-content-constant-width.html: Added.
Add a new layout test to exercise the scenario where a constant width viewport narrower than the view is used.
* fast/viewport/ios/shrink-to-fit-content-large-width-breakpoint-expected.txt: Added.
* fast/viewport/ios/shrink-to-fit-content-large-width-breakpoint.html: Added.
Add a new layout test to exercise the scenario where a responsive website that lays out larger than the view
width ends up with even more horizontal scrolling when laying out at the initial content width. In this
scenario, we shouldn't try to expand the viewport to try and encompass the content width, since that would only
induce even worse horizontal scrolling.
* fast/viewport/ios/shrink-to-fit-content-no-viewport-expected.txt: Added.
* fast/viewport/ios/shrink-to-fit-content-no-viewport.html: Added.
Add a new layout test for the case where there is no viewport, but content lays out wider than the view.
* fast/viewport/ios/shrink-to-fit-content-responsive-viewport-with-horizontal-overflow-expected.txt: Added.
* fast/viewport/ios/shrink-to-fit-content-responsive-viewport-with-horizontal-overflow.html: Added.
Add a new layout test for the case where the page has opted for a responsive viewport (device-width, initial
scale 1), but has laid out wider than the viewport anyways. In this case, we want to shrink the contents down to
fit inside the view.
* fast/viewport/ios/shrink-to-fit-content-temporary-overflow-expected.txt: Added.
* fast/viewport/ios/shrink-to-fit-content-temporary-overflow.html: Added.
Add a new layout test to exercise the case where, during page load, content width temporarily increases, and
then decreases such that it once again fits within the viewport. In this case, we don't want to expand the
viewport to be as wide as the large temporary width of the page.
* fast/viewport/ios/width-is-device-width-overflowing-body-overflow-hidden-expected.txt:
* fast/viewport/ios/width-is-device-width-overflowing-body-overflow-hidden.html:
* fast/viewport/ios/width-is-device-width-overflowing-expected.txt:
* fast/viewport/ios/width-is-device-width-overflowing.html:
Tweak these 2 existing layout tests to include "shrink-to-fit=no", to prevent the new heuristics from shrinking
the page to fit on device classes that use native viewports by default.
* platform/ipad/fast/viewport/ios/width-is-device-width-overflowing-body-overflow-hidden-expected.txt:
* platform/ipad/fast/viewport/ios/width-is-device-width-overflowing-expected.txt:
Modified Paths
Added Paths
Diff
Modified: trunk/LayoutTests/ChangeLog (244848 => 244849)
--- trunk/LayoutTests/ChangeLog 2019-05-01 20:46:20 UTC (rev 244848)
+++ trunk/LayoutTests/ChangeLog 2019-05-01 21:08:38 UTC (rev 244849)
@@ -1,3 +1,56 @@
+2019-05-01 Wenson Hsieh <[email protected]>
+
+ [iOS] Add a version of viewport shrink-to-fit heuristics that preserves page layout
+ https://bugs.webkit.org/show_bug.cgi?id=197342
+ <rdar://problem/50063091>
+
+ Reviewed by Tim Horton.
+
+ Introduces new layout tests, and adjusts some existing tests. See comments below.
+
+ * fast/viewport/ios/shrink-to-fit-content-constant-width-expected.txt: Added.
+ * fast/viewport/ios/shrink-to-fit-content-constant-width.html: Added.
+
+ Add a new layout test to exercise the scenario where a constant width viewport narrower than the view is used.
+
+ * fast/viewport/ios/shrink-to-fit-content-large-width-breakpoint-expected.txt: Added.
+ * fast/viewport/ios/shrink-to-fit-content-large-width-breakpoint.html: Added.
+
+ Add a new layout test to exercise the scenario where a responsive website that lays out larger than the view
+ width ends up with even more horizontal scrolling when laying out at the initial content width. In this
+ scenario, we shouldn't try to expand the viewport to try and encompass the content width, since that would only
+ induce even worse horizontal scrolling.
+
+ * fast/viewport/ios/shrink-to-fit-content-no-viewport-expected.txt: Added.
+ * fast/viewport/ios/shrink-to-fit-content-no-viewport.html: Added.
+
+ Add a new layout test for the case where there is no viewport, but content lays out wider than the view.
+
+ * fast/viewport/ios/shrink-to-fit-content-responsive-viewport-with-horizontal-overflow-expected.txt: Added.
+ * fast/viewport/ios/shrink-to-fit-content-responsive-viewport-with-horizontal-overflow.html: Added.
+
+ Add a new layout test for the case where the page has opted for a responsive viewport (device-width, initial
+ scale 1), but has laid out wider than the viewport anyways. In this case, we want to shrink the contents down to
+ fit inside the view.
+
+ * fast/viewport/ios/shrink-to-fit-content-temporary-overflow-expected.txt: Added.
+ * fast/viewport/ios/shrink-to-fit-content-temporary-overflow.html: Added.
+
+ Add a new layout test to exercise the case where, during page load, content width temporarily increases, and
+ then decreases such that it once again fits within the viewport. In this case, we don't want to expand the
+ viewport to be as wide as the large temporary width of the page.
+
+ * fast/viewport/ios/width-is-device-width-overflowing-body-overflow-hidden-expected.txt:
+ * fast/viewport/ios/width-is-device-width-overflowing-body-overflow-hidden.html:
+ * fast/viewport/ios/width-is-device-width-overflowing-expected.txt:
+ * fast/viewport/ios/width-is-device-width-overflowing.html:
+
+ Tweak these 2 existing layout tests to include "shrink-to-fit=no", to prevent the new heuristics from shrinking
+ the page to fit on device classes that use native viewports by default.
+
+ * platform/ipad/fast/viewport/ios/width-is-device-width-overflowing-body-overflow-hidden-expected.txt:
+ * platform/ipad/fast/viewport/ios/width-is-device-width-overflowing-expected.txt:
+
2019-05-01 Zalan Bujtas <[email protected]>
[iOS] Star rating is covered with a black circle when writing a review on Yelp
Added: trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-constant-width-expected.txt (0 => 244849)
--- trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-constant-width-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-constant-width-expected.txt 2019-05-01 21:08:38 UTC (rev 244849)
@@ -0,0 +1,11 @@
+This test verifies that a page with a constant width viewport smaller than the actual view width is scaled to fit the view. To test manually, load the page and verify that the bar spans the full width of the page.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS minScale is expectedScale
+PASS innerWidth is 300
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-constant-width.html (0 => 244849)
--- trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-constant-width.html (rev 0)
+++ trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-constant-width.html 2019-05-01 21:08:38 UTC (rev 244849)
@@ -0,0 +1,45 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ shouldIgnoreMetaViewport=true ] -->
+<html>
+<head>
+<meta name="viewport" content="width=300">
+<style>
+body, html {
+ margin: 0;
+}
+
+#bar {
+ width: 100%;
+ height: 100px;
+ background: linear-gradient(to right, red 0%, green 50%, blue 100%);
+}
+
+#description {
+ width: 300px;
+ overflow: scroll;
+}
+</style>
+<script src=""
+<script src=""
+<script>
+jsTestIsAsync = true;
+
+description("This test verifies that a page with a constant width viewport smaller than the actual view width is scaled to fit the view. To test manually, load the page and verify that the bar spans the full width of the page.");
+
+addEventListener("load", async () => {
+ if (!window.testRunner)
+ return;
+
+ await UIHelper.ensurePresentationUpdate();
+ minScale = (await UIHelper.minimumZoomScale()).toFixed(2);
+ expectedScale = (screen.width / 300).toFixed(2);
+ shouldBe("minScale", "expectedScale");
+ shouldBe("innerWidth", "300");
+ finishJSTest();
+});
+</script>
+</head>
+<body>
+<div id="bar"></div>
+<div id="description"></div>
+</body>
+</html>
Added: trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-large-width-breakpoint-expected.txt (0 => 244849)
--- trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-large-width-breakpoint-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-large-width-breakpoint-expected.txt 2019-05-01 21:08:38 UTC (rev 244849)
@@ -0,0 +1,11 @@
+This test verifies that the shrink-to-fit-content heuristic doesn't induce more horizontal scrolling than we would otherwise have. To run the test manually, load the page and check that the bar almost entirely fits within the viewport, with no more than 480px of horizontal scrolling (on a 320px-wide device) and 20px of scrolling (on a 768px-wide device).
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS minScale is 1
+PASS 800 is >= document.scrollingElement.scrollWidth
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-large-width-breakpoint.html (0 => 244849)
--- trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-large-width-breakpoint.html (rev 0)
+++ trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-large-width-breakpoint.html 2019-05-01 21:08:38 UTC (rev 244849)
@@ -0,0 +1,52 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ shouldIgnoreMetaViewport=true ] -->
+<html>
+<head>
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<style>
+body, html {
+ margin: 0;
+ width: 100%;
+ height: 100%;
+}
+
+@media screen and (min-width: 780px) {
+ #bar {
+ min-width: 10000px;
+ }
+}
+
+.bar {
+ width: 800px;
+ height: 100px;
+ background: linear-gradient(to right, red 0%, green 50%, blue 100%);
+}
+
+#description {
+ width: 300px;
+ overflow: scroll;
+}
+</style>
+<script src=""
+<script src=""
+<script>
+jsTestIsAsync = true;
+
+description("This test verifies that the shrink-to-fit-content heuristic doesn't induce more horizontal scrolling than we would otherwise have. To run the test manually, load the page and check that the bar almost entirely fits within the viewport, with no more than 480px of horizontal scrolling (on a 320px-wide device) and 20px of scrolling (on a 768px-wide device).");
+
+addEventListener("load", async () => {
+ if (!window.testRunner)
+ return;
+
+ await UIHelper.ensurePresentationUpdate();
+ minScale = await UIHelper.minimumZoomScale();
+ shouldBe("minScale", "1");
+ shouldBeGreaterThanOrEqual("800", "document.scrollingElement.scrollWidth");
+ finishJSTest();
+});
+</script>
+</head>
+<body>
+<div id="description"></div>
+<div id="bar" class="bar"></div>
+</body>
+</html>
Added: trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-no-viewport-expected.txt (0 => 244849)
--- trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-no-viewport-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-no-viewport-expected.txt 2019-05-01 21:08:38 UTC (rev 244849)
@@ -0,0 +1,11 @@
+This test verifies that a page with a no viewport but with content larger than the actual view width is scaled to fit the view. To test manually, load the page and verify that the bar spans the full width of the page.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS minScale is expectedScale
+PASS innerWidth became 1000
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-no-viewport.html (0 => 244849)
--- trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-no-viewport.html (rev 0)
+++ trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-no-viewport.html 2019-05-01 21:08:38 UTC (rev 244849)
@@ -0,0 +1,46 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ shouldIgnoreMetaViewport=true ] -->
+<html>
+<head>
+<meta name="viewport">
+<style>
+body, html {
+ margin: 0;
+ width: 100%;
+ height: 100%;
+}
+
+#bar {
+ width: 1000px;
+ height: 100px;
+ background: linear-gradient(to right, red 0%, green 50%, blue 100%);
+}
+
+#description {
+ width: 300px;
+ overflow: scroll;
+}
+</style>
+<script src=""
+<script src=""
+<script>
+jsTestIsAsync = true;
+
+description("This test verifies that a page with a no viewport but with content larger than the actual view width is scaled to fit the view. To test manually, load the page and verify that the bar spans the full width of the page.");
+
+addEventListener("load", async () => {
+ if (!window.testRunner)
+ return;
+
+ await UIHelper.ensurePresentationUpdate();
+ minScale = (await UIHelper.minimumZoomScale()).toFixed(2);
+ expectedScale = (screen.width / 1000).toFixed(2);
+ shouldBe("minScale", "expectedScale");
+ shouldBecomeEqual("innerWidth", "1000", finishJSTest);
+});
+</script>
+</head>
+<body>
+<div id="bar"></div>
+<div id="description"></div>
+</body>
+</html>
Added: trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-responsive-viewport-with-horizontal-overflow-expected.txt (0 => 244849)
--- trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-responsive-viewport-with-horizontal-overflow-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-responsive-viewport-with-horizontal-overflow-expected.txt 2019-05-01 21:08:38 UTC (rev 244849)
@@ -0,0 +1,11 @@
+This test verifies that the shrink-to-fit-content heuristic prevents horizontal scrolling by shrinking the page, even when a page specifies a responsive viewport. To run the test manually, load the page and check that the bar entirely fits within the viewport, and the page is not horizontally scrollable.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS minScale is expectedScale
+PASS innerWidth became 960
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-responsive-viewport-with-horizontal-overflow.html (0 => 244849)
--- trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-responsive-viewport-with-horizontal-overflow.html (rev 0)
+++ trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-responsive-viewport-with-horizontal-overflow.html 2019-05-01 21:08:38 UTC (rev 244849)
@@ -0,0 +1,46 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ shouldIgnoreMetaViewport=true ] -->
+<html>
+<head>
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<style>
+body, html {
+ margin: 0;
+ width: 100%;
+ height: 100%;
+}
+
+.bar {
+ width: 960px;
+ height: 100px;
+ background: linear-gradient(to right, red 0%, green 50%, blue 100%);
+}
+
+#description {
+ width: 300px;
+ overflow: scroll;
+}
+</style>
+<script src=""
+<script src=""
+<script>
+jsTestIsAsync = true;
+
+description("This test verifies that the shrink-to-fit-content heuristic prevents horizontal scrolling by shrinking the page, even when a page specifies a responsive viewport. To run the test manually, load the page and check that the bar entirely fits within the viewport, and the page is not horizontally scrollable.");
+
+addEventListener("load", async () => {
+ if (!window.testRunner)
+ return;
+
+ await UIHelper.ensurePresentationUpdate();
+ minScale = (await UIHelper.minimumZoomScale()).toFixed(2);
+ expectedScale = (screen.width / 960).toFixed(2);
+ shouldBe("minScale", "expectedScale");
+ shouldBecomeEqual("innerWidth", "960", finishJSTest);
+});
+</script>
+</head>
+<body>
+<div id="description"></div>
+<div class="bar"></div>
+</body>
+</html>
Added: trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-temporary-overflow-expected.txt (0 => 244849)
--- trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-temporary-overflow-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-temporary-overflow-expected.txt 2019-05-01 21:08:38 UTC (rev 244849)
@@ -0,0 +1,10 @@
+This test verifies that a temporary change in content width does not cause the viewport width to permanently expand to try and accomodate the content. To test manually, load the page and check that the box below reads 'PASS'. This test is only intended to run on devices with less than 1200px screen width.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS minScale is 1
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-temporary-overflow.html (0 => 244849)
--- trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-temporary-overflow.html (rev 0)
+++ trunk/LayoutTests/fast/viewport/ios/shrink-to-fit-content-temporary-overflow.html 2019-05-01 21:08:38 UTC (rev 244849)
@@ -0,0 +1,75 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ shouldIgnoreMetaViewport=true ] -->
+<html>
+<head>
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<style>
+body, html {
+ margin: 0;
+ width: 100%;
+ height: 100%;
+}
+
+.square {
+ background-color: green;
+ width: 100px;
+ height: 100px;
+}
+
+.square::before {
+ color: white;
+ content: "PASS";
+}
+
+@media screen and (min-width: 1200px) {
+ .square::before {
+ content: "FAIL";
+ }
+
+ .square {
+ background-color: red;
+ }
+}
+
+.bar {
+ width: 1500px;
+ height: 100px;
+ background: linear-gradient(to right, red 0%, green 50%, blue 100%);
+}
+
+#description {
+ width: 300px;
+ overflow: scroll;
+}
+</style>
+<script src=""
+<script src=""
+<script>
+jsTestIsAsync = true;
+
+description("This test verifies that a temporary change in content width does not cause the viewport width to permanently expand to try and accomodate the content. To test manually, load the page and check that the box below reads 'PASS'. This test is only intended to run on devices with less than 1200px screen width.");
+
+addEventListener("load", async () => {
+ if (!window.testRunner)
+ return;
+
+ await UIHelper.ensurePresentationUpdate();
+ minScale = await UIHelper.minimumZoomScale();
+ shouldBe("minScale", "1");
+ finishJSTest();
+});
+</script>
+</head>
+<body>
+<div id="description"></div>
+<div class="square"></div>
+<script>
+const bar = document.createElement("div");
+bar.classList.add("bar");
+document.body.appendChild(bar);
+document.scrollingElement.scrollTo(0, 1);
+document.scrollingElement.scrollTo(0, 0);
+document.scrollingElement.scrollTop;
+bar.remove();
+</script>
+</body>
+</html>
Modified: trunk/LayoutTests/fast/viewport/ios/width-is-device-width-overflowing-body-overflow-hidden-expected.txt (244848 => 244849)
--- trunk/LayoutTests/fast/viewport/ios/width-is-device-width-overflowing-body-overflow-hidden-expected.txt 2019-05-01 20:46:20 UTC (rev 244848)
+++ trunk/LayoutTests/fast/viewport/ios/width-is-device-width-overflowing-body-overflow-hidden-expected.txt 2019-05-01 21:08:38 UTC (rev 244849)
@@ -1,4 +1,4 @@
-Viewport: width=device-width
+Viewport: width=device-width, shrink-to-fit=no
scale 1.00000
maxScale 5.00000
Modified: trunk/LayoutTests/fast/viewport/ios/width-is-device-width-overflowing-body-overflow-hidden.html (244848 => 244849)
--- trunk/LayoutTests/fast/viewport/ios/width-is-device-width-overflowing-body-overflow-hidden.html 2019-05-01 20:46:20 UTC (rev 244848)
+++ trunk/LayoutTests/fast/viewport/ios/width-is-device-width-overflowing-body-overflow-hidden.html 2019-05-01 21:08:38 UTC (rev 244849)
@@ -1,8 +1,8 @@
-<!DOCTYPE html>
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
<html>
<head>
- <meta name="viewport" content="width=device-width">
+ <meta name="viewport" content="width=device-width, shrink-to-fit=no">
<script src=""
<style>
body {
Modified: trunk/LayoutTests/fast/viewport/ios/width-is-device-width-overflowing-expected.txt (244848 => 244849)
--- trunk/LayoutTests/fast/viewport/ios/width-is-device-width-overflowing-expected.txt 2019-05-01 20:46:20 UTC (rev 244848)
+++ trunk/LayoutTests/fast/viewport/ios/width-is-device-width-overflowing-expected.txt 2019-05-01 21:08:38 UTC (rev 244849)
@@ -1,4 +1,4 @@
-Viewport: width=device-width
+Viewport: width=device-width, shrink-to-fit=no
scale 1.00000
maxScale 5.00000
Modified: trunk/LayoutTests/fast/viewport/ios/width-is-device-width-overflowing.html (244848 => 244849)
--- trunk/LayoutTests/fast/viewport/ios/width-is-device-width-overflowing.html 2019-05-01 20:46:20 UTC (rev 244848)
+++ trunk/LayoutTests/fast/viewport/ios/width-is-device-width-overflowing.html 2019-05-01 21:08:38 UTC (rev 244849)
@@ -1,8 +1,8 @@
-<!DOCTYPE html>
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
<html>
<head>
- <meta name="viewport" content="width=device-width">
+ <meta name="viewport" content="width=device-width, shrink-to-fit=no">
<script src=""
<style>
.wide {
Modified: trunk/LayoutTests/platform/ipad/fast/viewport/ios/width-is-device-width-overflowing-body-overflow-hidden-expected.txt (244848 => 244849)
--- trunk/LayoutTests/platform/ipad/fast/viewport/ios/width-is-device-width-overflowing-body-overflow-hidden-expected.txt 2019-05-01 20:46:20 UTC (rev 244848)
+++ trunk/LayoutTests/platform/ipad/fast/viewport/ios/width-is-device-width-overflowing-body-overflow-hidden-expected.txt 2019-05-01 21:08:38 UTC (rev 244849)
@@ -1,4 +1,4 @@
-Viewport: width=device-width
+Viewport: width=device-width, shrink-to-fit=no
scale 1.00000
maxScale 5.00000
Modified: trunk/LayoutTests/platform/ipad/fast/viewport/ios/width-is-device-width-overflowing-expected.txt (244848 => 244849)
--- trunk/LayoutTests/platform/ipad/fast/viewport/ios/width-is-device-width-overflowing-expected.txt 2019-05-01 20:46:20 UTC (rev 244848)
+++ trunk/LayoutTests/platform/ipad/fast/viewport/ios/width-is-device-width-overflowing-expected.txt 2019-05-01 21:08:38 UTC (rev 244849)
@@ -1,4 +1,4 @@
-Viewport: width=device-width
+Viewport: width=device-width, shrink-to-fit=no
scale 1.00000
maxScale 5.00000
Modified: trunk/Source/WebCore/ChangeLog (244848 => 244849)
--- trunk/Source/WebCore/ChangeLog 2019-05-01 20:46:20 UTC (rev 244848)
+++ trunk/Source/WebCore/ChangeLog 2019-05-01 21:08:38 UTC (rev 244849)
@@ -1,3 +1,38 @@
+2019-05-01 Wenson Hsieh <[email protected]>
+
+ [iOS] Add a version of viewport shrink-to-fit heuristics that preserves page layout
+ https://bugs.webkit.org/show_bug.cgi?id=197342
+ <rdar://problem/50063091>
+
+ Reviewed by Tim Horton.
+
+ Adds support for a new shrink-to-fit heuristic that attempts to lay out the contents of the page at a larger
+ width in order to shrink content to fit the viewport. See WebKit ChangeLog for more details.
+
+ Tests: fast/viewport/ios/shrink-to-fit-content-constant-width.html
+ fast/viewport/ios/shrink-to-fit-content-large-width-breakpoint.html
+ fast/viewport/ios/shrink-to-fit-content-no-viewport.html
+ fast/viewport/ios/shrink-to-fit-content-responsive-viewport-with-horizontal-overflow.html
+ fast/viewport/ios/shrink-to-fit-content-temporary-overflow.html
+
+ * page/ViewportConfiguration.cpp:
+ (WebCore::ViewportConfiguration::setMinimumEffectiveDeviceWidth):
+ (WebCore::ViewportConfiguration::setIsKnownToLayOutWiderThanViewport):
+ (WebCore::ViewportConfiguration::description const):
+ * page/ViewportConfiguration.h:
+ (WebCore::ViewportConfiguration::canIgnoreScalingConstraints const):
+ (WebCore::ViewportConfiguration::minimumEffectiveDeviceWidth const):
+
+ Add several new getters and setters in ViewportConfiguration.
+
+ (WebCore::ViewportConfiguration::isKnownToLayOutWiderThanViewport const):
+ (WebCore::ViewportConfiguration::shouldIgnoreMinimumEffectiveDeviceWidth const):
+
+ Importantly, only allow ignoring the minimum effective device width in webpages with responsive viewports, if
+ they also have *not* laid out wider than the viewport.
+
+ (WebCore::ViewportConfiguration::setForceAlwaysUserScalable):
+
2019-05-01 Zalan Bujtas <[email protected]>
[iOS] Star rating is covered with a black circle when writing a review on Yelp
Modified: trunk/Source/WebCore/page/ViewportConfiguration.cpp (244848 => 244849)
--- trunk/Source/WebCore/page/ViewportConfiguration.cpp 2019-05-01 20:46:20 UTC (rev 244848)
+++ trunk/Source/WebCore/page/ViewportConfiguration.cpp 2019-05-01 21:08:38 UTC (rev 244849)
@@ -586,6 +586,32 @@
return minimumLayoutSize.height();
}
+bool ViewportConfiguration::setMinimumEffectiveDeviceWidth(double width)
+{
+ if (WTF::areEssentiallyEqual(m_minimumEffectiveDeviceWidth, width))
+ return false;
+
+ m_minimumEffectiveDeviceWidth = width;
+
+ if (shouldIgnoreMinimumEffectiveDeviceWidth())
+ return false;
+
+ updateMinimumLayoutSize();
+ updateConfiguration();
+ return true;
+}
+
+bool ViewportConfiguration::setIsKnownToLayOutWiderThanViewport(bool value)
+{
+ if (m_isKnownToLayOutWiderThanViewport == value)
+ return false;
+
+ m_isKnownToLayOutWiderThanViewport = value;
+ updateMinimumLayoutSize();
+ updateConfiguration();
+ return true;
+}
+
#ifndef NDEBUG
TextStream& operator<<(TextStream& ts, const ViewportConfiguration::Parameters& parameters)
@@ -649,6 +675,7 @@
ts.dumpProperty("ignoring vertical scaling constraints", shouldIgnoreVerticalScalingConstraints() ? "true" : "false");
ts.dumpProperty("avoids unsafe area", avoidsUnsafeArea() ? "true" : "false");
ts.dumpProperty("minimum effective device width", m_minimumEffectiveDeviceWidth);
+ ts.dumpProperty("known to lay out wider than viewport", m_isKnownToLayOutWiderThanViewport ? "true" : "false");
ts.endGroup();
Modified: trunk/Source/WebCore/page/ViewportConfiguration.h (244848 => 244849)
--- trunk/Source/WebCore/page/ViewportConfiguration.h 2019-05-01 20:46:20 UTC (rev 244848)
+++ trunk/Source/WebCore/page/ViewportConfiguration.h 2019-05-01 21:08:38 UTC (rev 244849)
@@ -86,11 +86,38 @@
WEBCORE_EXPORT bool setViewportArguments(const ViewportArguments&);
WEBCORE_EXPORT bool setCanIgnoreScalingConstraints(bool);
+ constexpr bool canIgnoreScalingConstraints() const { return m_canIgnoreScalingConstraints; }
+
+ WEBCORE_EXPORT bool setMinimumEffectiveDeviceWidth(double);
+ constexpr double minimumEffectiveDeviceWidth() const
+ {
+ if (shouldIgnoreMinimumEffectiveDeviceWidth())
+ return 0;
+ return m_minimumEffectiveDeviceWidth;
+ }
+
+ constexpr bool isKnownToLayOutWiderThanViewport() const { return m_isKnownToLayOutWiderThanViewport; }
+ WEBCORE_EXPORT bool setIsKnownToLayOutWiderThanViewport(bool value);
+
+ constexpr bool shouldIgnoreMinimumEffectiveDeviceWidth() const
+ {
+ if (m_canIgnoreScalingConstraints)
+ return true;
+
+ if (m_viewportArguments == ViewportArguments())
+ return false;
+
+ if ((m_viewportArguments.zoom == 1. || m_viewportArguments.width == ViewportArguments::ValueDeviceWidth) && !m_isKnownToLayOutWiderThanViewport)
+ return true;
+
+ return false;
+ }
+
void setForceAlwaysUserScalable(bool forceAlwaysUserScalable) { m_forceAlwaysUserScalable = forceAlwaysUserScalable; }
-
double layoutSizeScaleFactor() const { return m_layoutSizeScaleFactor; }
WEBCORE_EXPORT IntSize layoutSize() const;
+ WEBCORE_EXPORT int layoutWidth() const;
WEBCORE_EXPORT double initialScale() const;
WEBCORE_EXPORT double initialScaleIgnoringContentSize() const;
WEBCORE_EXPORT double minimumScale() const;
@@ -98,7 +125,6 @@
double maximumScaleIgnoringAlwaysScalable() const { return m_configuration.maximumScale; }
WEBCORE_EXPORT bool allowsUserScaling() const;
WEBCORE_EXPORT bool allowsUserScalingIgnoringAlwaysScalable() const;
- bool allowsShrinkToFit() const;
bool avoidsUnsafeArea() const { return m_configuration.avoidsUnsafeArea; }
// Matches a width=device-width, initial-scale=1 viewport.
@@ -120,7 +146,6 @@
void updateConfiguration();
double viewportArgumentsLength(double length) const;
double initialScaleFromSize(double width, double height, bool shouldIgnoreScalingConstraints) const;
- int layoutWidth() const;
int layoutHeight() const;
bool shouldOverrideDeviceWidthAndShrinkToFit() const;
@@ -131,27 +156,6 @@
void updateDefaultConfiguration();
bool canOverrideConfigurationParameters() const;
- constexpr bool shouldIgnoreMinimumEffectiveDeviceWidth() const
- {
- if (m_canIgnoreScalingConstraints)
- return true;
-
- if (m_viewportArguments == ViewportArguments())
- return false;
-
- if (m_viewportArguments.width == ViewportArguments::ValueDeviceWidth || m_viewportArguments.zoom == 1.)
- return true;
-
- return false;
- }
-
- constexpr double minimumEffectiveDeviceWidth() const
- {
- if (shouldIgnoreMinimumEffectiveDeviceWidth())
- return 0;
- return m_minimumEffectiveDeviceWidth;
- }
-
constexpr double forceAlwaysUserScalableMaximumScale() const
{
const double forceAlwaysUserScalableMaximumScaleIgnoringLayoutScaleFactor = 5;
@@ -185,6 +189,7 @@
double m_minimumEffectiveDeviceWidth { 0 };
bool m_canIgnoreScalingConstraints;
bool m_forceAlwaysUserScalable;
+ bool m_isKnownToLayOutWiderThanViewport { false };
};
WTF::TextStream& operator<<(WTF::TextStream&, const ViewportConfiguration::Parameters&);
Modified: trunk/Source/WebKit/ChangeLog (244848 => 244849)
--- trunk/Source/WebKit/ChangeLog 2019-05-01 20:46:20 UTC (rev 244848)
+++ trunk/Source/WebKit/ChangeLog 2019-05-01 21:08:38 UTC (rev 244849)
@@ -1,3 +1,96 @@
+2019-05-01 Wenson Hsieh <[email protected]>
+
+ [iOS] Add a version of viewport shrink-to-fit heuristics that preserves page layout
+ https://bugs.webkit.org/show_bug.cgi?id=197342
+ <rdar://problem/50063091>
+
+ Reviewed by Tim Horton.
+
+ This patch introduces a new shrink-to-fit heuristic that attempts to lay out the contents of the page at a
+ larger width in order to shrink content to fit the viewport. This is similar to existing shrink-to-fit behaviors
+ used for viewport sizing in multitasking mode, except that it not only scales the view, but additionally expands
+ the layout size, such that the overall layout of the page is preserved. In fact, the reason we ended up
+ reverting the existing flavor of shrink-to-fit in all cases except for multitasking was that page layout was not
+ preserved, which caused elements that poke out of the viewport to make the rest of the page look out of
+ proportion — see <rdar://problem/23818102> and related radars.
+
+ Covered by 5 new layout tests, and by adjusting a couple of existing layout tests. See comments below for more
+ details.
+
+ * Platform/Logging.h:
+
+ Add a new ViewportSizing logging channel. This will only log on pages that overflow the viewport and shrink to
+ fit as a result.
+
+ * Shared/WebPreferences.yaml:
+
+ Turn IgnoreViewportScalingConstraints off by default. This preference currently controls whether we allow
+ shrink-to-fit behaviors, and is only used by Safari when it is in multitasking mode. The value of this
+ preference is currenly *on* by default, and is turned off almost immediately during every page load after the
+ first visible content rect update, wherein visibleContentRectUpdateInfo.allowShrinkToFit() is false.
+
+ However, this sometimes causes a brief jitter during page load; to fix this, make the default value for
+ IgnoreViewportScalingConstraints false, and change the logic in WebPage::updateVisibleContentRects to
+ setCanIgnoreScalingConstraints to true if either the IgnoreViewportScalingConstraints preference (not only
+ affected by an internal debug switch) is true, or WKWebView SPI is used to enable the behavior.
+
+ * WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp:
+ (WebKit::WebFrameLoaderClient::dispatchDidFinishDocumentLoad):
+ (WebKit::WebFrameLoaderClient::dispatchDidFinishLoad):
+
+ Add a new hook for WebFrameLoaderClient to call into WebPage when document load finishes. Also, tweak
+ dispatchDidFinishLoad to take a WebFrame& instead of a WebFrame* in a drive-by fix (the frame is assumed to be
+ non-null anyways).
+
+ * WebProcess/WebPage/WebPage.cpp:
+ (WebKit::WebPage::didCommitLoad):
+ (WebKit::WebPage::didFinishDocumentLoad):
+ (WebKit::WebPage::didFinishLoad):
+
+ When finishing document load or finishing the overall load, kick off the shrink-to-fit timer; when committing a
+ load, cancel the timer.
+
+ * WebProcess/WebPage/WebPage.h:
+ * WebProcess/WebPage/ios/WebPageIOS.mm:
+ (WebKit::WebPage::setViewportConfigurationViewLayoutSize):
+
+ Don't allow the minimum effective device width from the client to stomp over any minimum effective device width
+ set as a result of the new shrink-to-fit heuristic; on some pages that load quickly, this can result in a race
+ where the minimum effective device width (i.e. a value that lower-bounds the minimum layout width) is first set
+ by the shrink-to-fit heuristic, and then set to an incorrect value by the client.
+
+ In the near future, web view SPI used to set the minimum effective device width should actually be removed
+ altogether, since the new shrink-to-fit heuristic supersedes any need for the client to fiddle with the minimum
+ effective device width.
+
+ (WebKit::WebPage::dynamicViewportSizeUpdate):
+
+ When performing a dynamic viewport size update, additionally re-run the shrink-to-fit heuristic. This allows
+ the minimum layout size of the viewport to be updated, if necessary. An example of where this matters is when a
+ web page is *below* a tablet/desktop layout breakpoint in portrait device orientation, but then exceeds this
+ layout breakpoint in landscape orientation. In this scenario, rotating the device should swap between these two
+ page layouts.
+
+ (WebKit::WebPage::resetViewportDefaultConfiguration):
+ (WebKit::WebPage::scheduleShrinkToFitContent):
+ (WebKit::WebPage::shrinkToFitContentTimerFired):
+ (WebKit::WebPage::immediatelyShrinkToFitContent):
+
+ Leverage the existing capability for a viewport to have a "minimum effective device width" to grant the viewport
+ a larger layout size than it would normally have, and then scale down to fit within the bounds of the view. One
+ challenge with this overall approach is that laying out at a larger width may cause the page to lay out even
+ wider in response, which may actually worsen horizontal scrolling. To mitigate this, we only attempt to lay out
+ at the current content width once; if laying out at this width reduced the amount of horizontal scrolling by any
+ amount, then proceed with this layout width; otherwise, revert to the previous layout width.
+
+ (WebKit::WebPage::shouldIgnoreMetaViewport const):
+
+ Pull some common logic out into a readonly getter.
+
+ (WebKit::WebPage::updateVisibleContentRects):
+
+ See the comment below WebPreferences.yaml, above.
+
2019-05-01 Dean Jackson <[email protected]>
Link Previews that use WKImagePreviewViewController are not always scaled correctly
Modified: trunk/Source/WebKit/Platform/Logging.h (244848 => 244849)
--- trunk/Source/WebKit/Platform/Logging.h 2019-05-01 20:46:20 UTC (rev 244848)
+++ trunk/Source/WebKit/Platform/Logging.h 2019-05-01 21:08:38 UTC (rev 244849)
@@ -85,6 +85,7 @@
M(TextInput) \
M(ViewGestures) \
M(ViewState) \
+ M(ViewportSizing) \
M(VirtualMemory) \
M(VisibleRects) \
M(WebGL) \
Modified: trunk/Source/WebKit/Shared/WebPreferences.yaml (244848 => 244849)
--- trunk/Source/WebKit/Shared/WebPreferences.yaml 2019-05-01 20:46:20 UTC (rev 244848)
+++ trunk/Source/WebKit/Shared/WebPreferences.yaml 2019-05-01 21:08:38 UTC (rev 244849)
@@ -1089,7 +1089,7 @@
IgnoreViewportScalingConstraints:
type: bool
- defaultValue: true
+ defaultValue: false
category: debug
webcoreBinding: none
condition: PLATFORM(IOS_FAMILY)
Modified: trunk/Source/WebKit/WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp (244848 => 244849)
--- trunk/Source/WebKit/WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp 2019-05-01 20:46:20 UTC (rev 244848)
+++ trunk/Source/WebKit/WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp 2019-05-01 21:08:38 UTC (rev 244849)
@@ -609,6 +609,8 @@
// Notify the UIProcess.
webPage->send(Messages::WebPageProxy::DidFinishDocumentLoadForFrame(m_frame->frameID(), navigationID, UserData(WebProcess::singleton().transformObjectsToHandles(userData.get()).get())));
+
+ webPage->didFinishDocumentLoad(*m_frame);
}
void WebFrameLoaderClient::dispatchDidFinishLoad()
@@ -631,7 +633,7 @@
if (WebFrame::LoadListener* loadListener = m_frame->loadListener())
loadListener->didFinishLoad(m_frame);
- webPage->didFinishLoad(m_frame);
+ webPage->didFinishLoad(*m_frame);
}
void WebFrameLoaderClient::forcePageTransitionIfNeeded()
Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp (244848 => 244849)
--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp 2019-05-01 20:46:20 UTC (rev 244848)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp 2019-05-01 21:08:38 UTC (rev 244849)
@@ -424,6 +424,9 @@
#if PLATFORM(WPE)
, m_hostFileDescriptor(WTFMove(parameters.hostFileDescriptor))
#endif
+#if ENABLE(VIEWPORT_RESIZING)
+ , m_shrinkToFitContentTimer(*this, &WebPage::shrinkToFitContentTimerFired, 0_s)
+#endif
{
ASSERT(m_pageID);
@@ -5714,6 +5717,10 @@
viewportConfigurationChanged();
#endif
+#if ENABLE(VIEWPORT_RESIZING)
+ m_shrinkToFitContentTimer.stop();
+#endif
+
#if ENABLE(PRIMARY_SNAPSHOTTED_PLUGIN_HEURISTIC)
resetPrimarySnapshottedPlugIn();
#endif
@@ -5727,13 +5734,23 @@
updateMainFrameScrollOffsetPinning();
}
-void WebPage::didFinishLoad(WebFrame* frame)
+void WebPage::didFinishDocumentLoad(WebFrame& frame)
{
- if (!frame->isMainFrame())
+ if (!frame.isMainFrame())
return;
- WebProcess::singleton().sendPrewarmInformation(frame->url());
+#if ENABLE(VIEWPORT_RESIZING)
+ scheduleShrinkToFitContent();
+#endif
+}
+void WebPage::didFinishLoad(WebFrame& frame)
+{
+ if (!frame.isMainFrame())
+ return;
+
+ WebProcess::singleton().sendPrewarmInformation(frame.url());
+
#if ENABLE(PRIMARY_SNAPSHOTTED_PLUGIN_HEURISTIC)
m_readyToFindPrimarySnapshottedPlugin = true;
LOG(Plugins, "Primary Plug-In Detection: triggering detection from didFinishLoad (marking as ready to detect).");
@@ -5741,6 +5758,10 @@
#else
UNUSED_PARAM(frame);
#endif
+
+#if ENABLE(VIEWPORT_RESIZING)
+ scheduleShrinkToFitContent();
+#endif
}
void WebPage::didInsertMenuElement(HTMLMenuElement& element)
Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.h (244848 => 244849)
--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.h 2019-05-01 20:46:20 UTC (rev 244848)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.h 2019-05-01 21:08:38 UTC (rev 244849)
@@ -131,6 +131,8 @@
OBJC_CLASS WKAccessibilityWebPageObject;
#endif
+#define ENABLE_VIEWPORT_RESIZING PLATFORM(IOS_FAMILY)
+
namespace API {
class Array;
}
@@ -349,7 +351,8 @@
void didCommitLoad(WebFrame*);
void willReplaceMultipartContent(const WebFrame&);
void didReplaceMultipartContent(const WebFrame&);
- void didFinishLoad(WebFrame*);
+ void didFinishDocumentLoad(WebFrame&);
+ void didFinishLoad(WebFrame&);
void show();
String userAgent(const URL&) const;
String platformUserAgent(const URL&) const;
@@ -1228,8 +1231,15 @@
InteractionInformationAtPosition positionInformation(const InteractionInformationRequest&);
WebAutocorrectionContext autocorrectionContext();
bool applyAutocorrectionInternal(const String& correction, const String& originalText);
+ bool shouldIgnoreMetaViewport() const;
#endif
+#if ENABLE(VIEWPORT_RESIZING)
+ void scheduleShrinkToFitContent();
+ void shrinkToFitContentTimerFired();
+ bool immediatelyShrinkToFitContent();
+#endif
+
#if PLATFORM(IOS_FAMILY) && ENABLE(DATA_INTERACTION)
void requestDragStart(const WebCore::IntPoint& clientPosition, const WebCore::IntPoint& globalPosition, uint64_t allowedActions);
void requestAdditionalItemsForDragSession(const WebCore::IntPoint& clientPosition, const WebCore::IntPoint& globalPosition, uint64_t allowedActions);
@@ -1894,6 +1904,9 @@
WeakPtr<RemoteObjectRegistry> m_remoteObjectRegistry;
#endif
WebCore::IntSize m_lastSentIntrinsicContentSize;
+#if ENABLE(VIEWPORT_RESIZING)
+ WebCore::DeferrableOneShotTimer m_shrinkToFitContentTimer;
+#endif
};
} // namespace WebKit
Modified: trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm (244848 => 244849)
--- trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm 2019-05-01 20:46:20 UTC (rev 244848)
+++ trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm 2019-05-01 21:08:38 UTC (rev 244849)
@@ -2791,7 +2791,8 @@
LOG_WITH_STREAM(VisibleRects, stream << "WebPage " << m_pageID << " setViewportConfigurationViewLayoutSize " << size << " scaleFactor " << scaleFactor << " minimumEffectiveDeviceWidth " << minimumEffectiveDeviceWidth);
auto previousLayoutSizeScaleFactor = m_viewportConfiguration.layoutSizeScaleFactor();
- if (!m_viewportConfiguration.setViewLayoutSize(size, scaleFactor, minimumEffectiveDeviceWidth))
+ auto clampedMinimumEffectiveDevice = m_viewportConfiguration.isKnownToLayOutWiderThanViewport() ? WTF::nullopt : Optional<double>(minimumEffectiveDeviceWidth);
+ if (!m_viewportConfiguration.setViewLayoutSize(size, scaleFactor, WTFMove(clampedMinimumEffectiveDevice)))
return;
auto zoomToInitialScale = ZoomToInitialScale::No;
@@ -2873,8 +2874,16 @@
}
LOG_WITH_STREAM(VisibleRects, stream << "WebPage::dynamicViewportSizeUpdate setting view layout size to " << viewLayoutSize);
- if (m_viewportConfiguration.setViewLayoutSize(viewLayoutSize))
+ bool viewportChanged = m_viewportConfiguration.setIsKnownToLayOutWiderThanViewport(false);
+ viewportChanged |= m_viewportConfiguration.setViewLayoutSize(viewLayoutSize);
+ if (viewportChanged)
viewportConfigurationChanged();
+
+#if ENABLE(VIEWPORT_RESIZING)
+ if (immediatelyShrinkToFitContent())
+ viewportConfigurationChanged();
+#endif
+
IntSize newLayoutSize = m_viewportConfiguration.layoutSize();
if (setFixedLayoutSize(newLayoutSize))
@@ -3020,16 +3029,9 @@
}
auto parametersForStandardFrame = [&] {
- bool shouldIgnoreMetaViewport = false;
- if (auto* mainDocument = m_page->mainFrame().document()) {
- auto* loader = mainDocument->loader();
- shouldIgnoreMetaViewport = loader && loader->metaViewportPolicy() == WebCore::MetaViewportPolicy::Ignore;
- }
-
- if (m_page->settings().shouldIgnoreMetaViewport())
- shouldIgnoreMetaViewport = true;
-
- return shouldIgnoreMetaViewport ? m_viewportConfiguration.nativeWebpageParameters() : ViewportConfiguration::webpageParameters();
+ if (shouldIgnoreMetaViewport())
+ return m_viewportConfiguration.nativeWebpageParameters();
+ return ViewportConfiguration::webpageParameters();
};
if (!frame) {
@@ -3049,8 +3051,85 @@
m_viewportConfiguration.setDefaultConfiguration(ViewportConfiguration::textDocumentParameters());
else
m_viewportConfiguration.setDefaultConfiguration(parametersForStandardFrame());
+ m_viewportConfiguration.setIsKnownToLayOutWiderThanViewport(false);
}
+#if ENABLE(VIEWPORT_RESIZING)
+
+void WebPage::scheduleShrinkToFitContent()
+{
+ m_shrinkToFitContentTimer.restart();
+}
+
+void WebPage::shrinkToFitContentTimerFired()
+{
+ if (immediatelyShrinkToFitContent())
+ viewportConfigurationChanged(ZoomToInitialScale::Yes);
+}
+
+bool WebPage::immediatelyShrinkToFitContent()
+{
+ if (!shouldIgnoreMetaViewport())
+ return false;
+
+ if (!m_viewportConfiguration.viewportArguments().shrinkToFit)
+ return false;
+
+ if (m_viewportConfiguration.canIgnoreScalingConstraints())
+ return false;
+
+ auto mainFrame = makeRefPtr(m_mainFrame->coreFrame());
+ if (!mainFrame)
+ return false;
+
+ auto view = makeRefPtr(mainFrame->view());
+ auto mainDocument = makeRefPtr(mainFrame->document());
+ if (!view || !mainDocument)
+ return false;
+
+ mainDocument->updateLayout();
+
+ static const int toleratedHorizontalScrollingDistance = 20;
+ static const int maximumExpandedLayoutWidth = 1280;
+ int originalContentWidth = view->contentsWidth();
+ int originalLayoutWidth = m_viewportConfiguration.layoutWidth();
+ int originalHorizontalOverflowAmount = originalContentWidth - originalLayoutWidth;
+ if (originalHorizontalOverflowAmount <= toleratedHorizontalScrollingDistance || originalLayoutWidth >= maximumExpandedLayoutWidth || originalContentWidth <= m_viewportConfiguration.viewLayoutSize().width())
+ return false;
+
+ auto changeMinimumEffectiveDeviceWidth = [this, mainDocument] (int targetLayoutWidth) -> bool {
+ if (m_viewportConfiguration.setMinimumEffectiveDeviceWidth(targetLayoutWidth)) {
+ viewportConfigurationChanged();
+ mainDocument->updateLayout();
+ return true;
+ }
+ return false;
+ };
+
+ m_viewportConfiguration.setIsKnownToLayOutWiderThanViewport(true);
+ double originalMinimumDeviceWidth = m_viewportConfiguration.minimumEffectiveDeviceWidth();
+ if (changeMinimumEffectiveDeviceWidth(std::min(maximumExpandedLayoutWidth, originalContentWidth)) && view->contentsWidth() - m_viewportConfiguration.layoutWidth() > originalHorizontalOverflowAmount) {
+ changeMinimumEffectiveDeviceWidth(originalMinimumDeviceWidth);
+ m_viewportConfiguration.setIsKnownToLayOutWiderThanViewport(false);
+ }
+
+ // FIXME (197429): Consider additionally logging an error message to the console if a responsive meta viewport tag was used.
+ RELEASE_LOG(ViewportSizing, "Shrink-to-fit: content width %d => %d; layout width %d => %d", originalContentWidth, view->contentsWidth(), originalLayoutWidth, m_viewportConfiguration.layoutWidth());
+ return true;
+}
+
+#endif // ENABLE(VIEWPORT_RESIZING)
+
+bool WebPage::shouldIgnoreMetaViewport() const
+{
+ if (auto* mainDocument = m_page->mainFrame().document()) {
+ auto* loader = mainDocument->loader();
+ if (loader && loader->metaViewportPolicy() == WebCore::MetaViewportPolicy::Ignore)
+ return true;
+ }
+ return m_page->settings().shouldIgnoreMetaViewport();
+}
+
void WebPage::viewportConfigurationChanged(ZoomToInitialScale zoomToInitialScale)
{
double initialScale = m_viewportConfiguration.initialScale();
@@ -3256,7 +3335,7 @@
if (scrollPosition != frameView.scrollPosition())
m_dynamicSizeUpdateHistory.clear();
- if (m_viewportConfiguration.setCanIgnoreScalingConstraints(m_ignoreViewportScalingConstraints && visibleContentRectUpdateInfo.allowShrinkToFit()))
+ if (m_viewportConfiguration.setCanIgnoreScalingConstraints(m_ignoreViewportScalingConstraints || visibleContentRectUpdateInfo.allowShrinkToFit()))
viewportConfigurationChanged();
frameView.setUnobscuredContentSize(visibleContentRectUpdateInfo.unobscuredContentRect().size());