Bug 1423013 - Expand the layout viewport to the minimum scale size for overflow:hidden pages. r=botond,tnikkel

Depends on D15345

Differential Revision: https://phabricator.services.mozilla.com/D15347

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Hiroyuki Ikezoe 2019-01-15 03:28:10 +00:00
Родитель f3f82aedc4
Коммит 4faf5e4991
16 изменённых файлов: 317 добавлений и 11 удалений

Просмотреть файл

@ -71,6 +71,10 @@ class MobileViewportManager final : public nsIDOMEventListener,
* presShell is initialized. */
void SetInitialViewport();
const mozilla::LayoutDeviceIntSize& DisplaySize() const {
return mDisplaySize;
};
private:
~MobileViewportManager();

Просмотреть файл

@ -15,6 +15,7 @@
#include "nsIContentViewer.h"
#include "nsPresContext.h"
#include "nsView.h"
#include "nsViewportInfo.h"
#include "nsIScrollable.h"
#include "nsContainerFrame.h"
#include "nsGkAtoms.h"
@ -372,9 +373,14 @@ bool nsHTMLScrollFrame::TryLayout(ScrollReflowInput* aState,
std::max(aKidMetrics->Height(), vScrollbarMinHeight);
aState->mInsideBorderSize =
ComputeInsideBorderSize(aState, desiredInsideBorderSize);
nsSize scrollPortSize = nsSize(
std::max(0, aState->mInsideBorderSize.width - vScrollbarDesiredWidth),
std::max(0, aState->mInsideBorderSize.height - hScrollbarDesiredHeight));
nsSize layoutSize = mHelper.mIsUsingMinimumScaleSize
? mHelper.mMinimumScaleSize
: aState->mInsideBorderSize;
nsSize scrollPortSize =
nsSize(std::max(0, layoutSize.width - vScrollbarDesiredWidth),
std::max(0, layoutSize.height - hScrollbarDesiredHeight));
nsSize visualViewportSize = scrollPortSize;
nsIPresShell* presShell = PresShell();
@ -447,8 +453,7 @@ bool nsHTMLScrollFrame::TryLayout(ScrollReflowInput* aState,
nsPoint scrollPortOrigin(aState->mComputedBorder.left,
aState->mComputedBorder.top);
if (!IsScrollbarOnRight()) {
nscoord vScrollbarActualWidth =
aState->mInsideBorderSize.width - scrollPortSize.width;
nscoord vScrollbarActualWidth = layoutSize.width - scrollPortSize.width;
scrollPortOrigin.x += vScrollbarActualWidth;
}
mHelper.mScrollPort = nsRect(scrollPortOrigin, scrollPortSize);
@ -717,6 +722,12 @@ void nsHTMLScrollFrame::ReflowContents(ScrollReflowInput* aState,
}
}
if (IsRootScrollFrameOfDocument()) {
mHelper.UpdateMinimumScaleSize(
aState->mContentsOverflowAreas.ScrollableOverflow(),
kidDesiredSize.PhysicalSize());
}
// Try vertical scrollbar settings that leave the vertical scrollbar
// unchanged. Do this first because changing the vertical scrollbar setting is
// expensive, forcing a reflow always.
@ -1048,10 +1059,11 @@ void nsHTMLScrollFrame::Reflow(nsPresContext* aPresContext,
ReflowContents(&state, aDesiredSize);
aDesiredSize.Width() =
state.mInsideBorderSize.width + state.mComputedBorder.LeftRight();
aDesiredSize.Height() =
state.mInsideBorderSize.height + state.mComputedBorder.TopBottom();
nsSize layoutSize = mHelper.mIsUsingMinimumScaleSize
? mHelper.mMinimumScaleSize
: state.mInsideBorderSize;
aDesiredSize.Width() = layoutSize.width + state.mComputedBorder.LeftRight();
aDesiredSize.Height() = layoutSize.height + state.mComputedBorder.TopBottom();
// Set the size of the frame now since computing the perspective-correct
// overflow (within PlaceScrollArea) can rely on it.
@ -1091,7 +1103,7 @@ void nsHTMLScrollFrame::Reflow(nsPresContext* aPresContext,
// place and reflow scrollbars
nsRect insideBorderArea =
nsRect(nsPoint(state.mComputedBorder.left, state.mComputedBorder.top),
state.mInsideBorderSize);
layoutSize);
mHelper.LayoutScrollbars(state.mBoxState, insideBorderArea,
oldScrollAreaBounds);
} else {
@ -1998,6 +2010,7 @@ ScrollFrameHelper::ScrollFrameHelper(nsContainerFrame* aOuter, bool aIsRoot)
mZoomableByAPZ(false),
mHasOutOfFlowContentInsideFilter(false),
mSuppressScrollbarRepaints(false),
mIsUsingMinimumScaleSize(false),
mVelocityQueue(aOuter->PresContext()) {
if (LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0) {
mScrollbarActivity = new ScrollbarActivity(do_QueryFrame(aOuter));
@ -5445,6 +5458,65 @@ void ScrollFrameHelper::FinishReflowForScrollbar(Element* aElement,
SetCoordAttribute(aElement, nsGkAtoms::increment, aIncrement);
}
void ScrollFrameHelper::UpdateMinimumScaleSize(
const nsRect& aScrollableOverflow, const nsSize& aICBSize) {
MOZ_ASSERT(mIsRoot);
mIsUsingMinimumScaleSize = false;
if (!mOuter->PresShell()->GetIsViewportOverridden()) {
return;
}
nsPresContext* pc = mOuter->PresContext();
MOZ_ASSERT(pc->IsRootContentDocument(),
"The pres context should be for the root content document");
const ScrollStyles& styles = pc->GetViewportScrollStylesOverride();
// FIXME: Bug 1520077 - Drop this check. We should use the minimum-scale size
// even if no overflow:hidden is specified.
if (styles.mHorizontal != StyleOverflow::Hidden) {
return;
}
RefPtr<MobileViewportManager> manager =
mOuter->PresShell()->GetMobileViewportManager();
MOZ_ASSERT(manager);
ScreenIntSize displaySize = ViewAs<ScreenPixel>(
manager->DisplaySize(),
PixelCastJustification::LayoutDeviceIsScreenForBounds);
if (displaySize.width == 0 || displaySize.height == 0) {
return;
}
Document* doc = pc->Document();
MOZ_ASSERT(doc, "The document should be valid");
nsViewportInfo viewportInfo = doc->GetViewportInfo(displaySize);
// FIXME: Bug 1520081 - Drop this check. We should use the minimum-scale size
// even if the minimum-scale size is greater than 1.0.
if (viewportInfo.GetMinZoom() >=
pc->CSSToDevPixelScale() * LayoutDeviceToScreenScale(1.0f)) {
return;
}
nsSize maximumPossibleSize =
CSSSize::ToAppUnits(ScreenSize(displaySize) / viewportInfo.GetMinZoom());
mMinimumScaleSize =
Min(maximumPossibleSize,
nsSize(aScrollableOverflow.XMost(), aScrollableOverflow.YMost()));
mMinimumScaleSize = Max(aICBSize, mMinimumScaleSize);
// Chrome doesn't allow overflow-y:hidden region reachable if there is no
// overflow-x:hidden region.
// TODO: Bug 1508177: We can drop this condition once after we shrink the
// content even if no content area gets visible.
if (mMinimumScaleSize.width != aICBSize.width) {
mIsUsingMinimumScaleSize = true;
}
}
bool ScrollFrameHelper::ReflowFinished() {
mPostedReflowCallback = false;

Просмотреть файл

@ -518,6 +518,11 @@ class ScrollFrameHelper : public nsIReflowCallback {
bool IsRootScrollFrameOfDocument() const { return mIsRoot; }
// Update minimum-scale size. The minimum-scale size will be set/used only
// if there is overflow-x:hidden region.
void UpdateMinimumScaleSize(const nsRect& aScrollableOverflow,
const nsSize& aICBSize);
// owning references to the nsIAnonymousContentCreator-built content
nsCOMPtr<mozilla::dom::Element> mHScrollbarContent;
nsCOMPtr<mozilla::dom::Element> mVScrollbarContent;
@ -543,7 +548,11 @@ class ScrollFrameHelper : public nsIReflowCallback {
nsAtom* mLastSmoothScrollOrigin;
Maybe<nsPoint> mApzSmoothScrollDestination;
uint32_t mScrollGeneration;
// NOTE: On mobile this value might be factoring into overflow:hidden region
// in the case of the top level document.
nsRect mScrollPort;
nsSize mMinimumScaleSize;
// Where we're currently scrolling to, if we're scrolling asynchronously.
// If we're not in the middle of an asynchronous scroll then this is
// just the current scroll position. ScrollBy will choose its
@ -670,6 +679,9 @@ class ScrollFrameHelper : public nsIReflowCallback {
// True if we don't want the scrollbar to repaint itself right now.
bool mSuppressScrollbarRepaints : 1;
// True if we are using the minimum scale size instead of ICB for scroll port.
bool mIsUsingMinimumScaleSize : 1;
mozilla::layout::ScrollVelocityQueue mVelocityQueue;
protected:

Просмотреть файл

@ -6454,12 +6454,16 @@ UniquePtr<ScrollMetadata> nsDisplaySubDocument::ComputeScrollMetadata(
nsRect viewport = mFrame->GetRect() - mFrame->GetPosition() +
mFrame->GetOffsetToCrossDoc(ReferenceFrame());
nsIScrollableFrame* scrollableFrame = rootScrollFrame->GetScrollTargetFrame();
if (isRootContentDocument) {
viewport.SizeTo(scrollableFrame->GetScrollPortRect().Size());
}
UniquePtr<ScrollMetadata> metadata =
MakeUnique<ScrollMetadata>(nsLayoutUtils::ComputeScrollMetadata(
mFrame, rootScrollFrame, rootScrollFrame->GetContent(),
ReferenceFrame(), aLayerManager, mScrollParentId, viewport, Nothing(),
isRootContentDocument, Some(params)));
nsIScrollableFrame* scrollableFrame = rootScrollFrame->GetScrollTargetFrame();
if (scrollableFrame) {
scrollableFrame->NotifyApzTransaction();
}

Просмотреть файл

@ -0,0 +1,15 @@
<!DOCTYPE html>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
html, body {
margin: 0;
width: 100%;
height: 100%;
}
div {
height: 50%;
position: absolute;
}
</style>
<div style="background: green; width: 100%;"></div>
<div style="background: blue; width: 50%;"></div>

Просмотреть файл

@ -0,0 +1,19 @@
<!DOCTYPE html>
<meta name="viewport" content="width=device-width, minimum-scale=0.5">
<style>
html {
overflow-x: hidden;
}
html, body {
margin: 0;
width: 100%;
height: 100%;
}
div {
height: 100%;
position: absolute;
}
</style>
<div style="background: red; width: 300%;"></div>
<div style="background: green; width: 200%;"></div>
<div style="background: blue; width: 100%;"></div>

Просмотреть файл

@ -0,0 +1,22 @@
<!DOCTYPE html>
<meta name="viewport" content="width=device-width, minimum-scale=0.25">
<style>
html {
overflow: hidden;
}
html, body {
margin: 0;
width: 100%;
height: 100%;
}
div {
position: absolute;
}
</style>
<!--
Even if minimum-scale=0.25 is specified, the widest element in this content is
below div element whose *real* width is 200% because of `margin-left: -500%`,
so this content should be scaled to 0.5x.
-->
<div style="background: green; width: 700%; height: 500%; margin-left: -500%;"></div>
<div style="background: blue; width: 100%; height: 100%;"></div>

Просмотреть файл

@ -0,0 +1,18 @@
<!DOCTYPE html>
<meta name="viewport" content="width=device-width, minimum-scale=0.5">
<style>
html {
overflow: hidden;
}
html, body {
margin: 0;
width: 100%;
height: 100%;
}
div {
position: absolute;
}
</style>
<div style="background: red; width: 300%; height: 300%;"></div>
<div style="background: green; width: 200%; height: 200%;"></div>
<div style="background: blue; width: 100%; height: 100%;"></div>

Просмотреть файл

@ -0,0 +1,14 @@
<!DOCTYPE html>
<meta name="viewport" content="width=device-width,initial-scale=1">
<style>
html, body {
margin: 0;
width: 100%;
height: 100%;
}
div {
position: absolute;
}
</style>
<div style="background: green; width: 100%; height: 100%;"></div>
<div style="background: blue; width: 50%; height: 50%;"></div>

Просмотреть файл

@ -0,0 +1,16 @@
<!DOCTYPE html>
<meta name="viewport" content="width=device-width, minimum-scale=0.5">
<style>
html, body {
margin: 0;
width: 100%;
height: 100%;
scrollbar-width: none;
}
div {
position: absolute;
}
</style>
<div style="background: red; width: 300%; height: 300%;"></div>
<div style="background: green; width: 200%; height: 200%;"></div>
<div style="background: blue; width: 100%; height: 100%"></div>

Просмотреть файл

@ -9,3 +9,12 @@ default-preferences pref(dom.meta-viewport.enabled,true) pref(apz.allow_zooming,
== initial-scale-1.html no-zoom-ref.html
== minimum-scale.html no-zoom-ref.html
== clamped-by-default-minimum-scale.html initial-scale-0_25-ref.html
# Skip below tests on Windows (bug 1516322) on Webrender (bug 1520096)
skip-if(winWidget||webrender) == overflow-region.html overflow-region-ref.html
skip-if(winWidget||webrender) == overflow-hidden-region.html overflow-region-ref.html
skip-if(winWidget||webrender) == overflow-hidden-region-with-negative-left-positioned-element.html overflow-region-ref.html
skip-if(winWidget||webrender) fails == horizontal-overflow-hidden-region.html horizontal-overflow-hidden-region-ref.html # bug 1508177
skip-if(winWidget||webrender) == vertical-overflow-hidden-region.html about:blank
skip-if(winWidget||webrender) == scroll-to-unreachable-area.html scroll-to-unreachable-area-ref.html
skip-if(winWidget||webrender) == wrapped-text-at-icb.html wrapped-text-at-icb-ref.html

Просмотреть файл

@ -0,0 +1,14 @@
<!DOCTYPE html>
<meta name="viewport" content="width=device-width,initial-scale=1">
<style>
html, body {
margin: 0;
width: 100%;
height: 100%;
}
div {
position: absolute;
}
</style>
<div style="background: red; width: 100%; height: 100%;"></div>
<div style="background: green; width: 50%; height: 50%;"></div>

Просмотреть файл

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html class="reftest-wait">
<meta name="viewport" content="width=device-width, minimum-scale=0.5">
<style>
html {
overflow: hidden;
}
html, body {
margin: 0;
width: 100%;
height: 100%;
}
div {
position: absolute;
}
</style>
<div style="background: red; width: 300%; height: 300%;"></div>
<div id="green" style="background: green; width: 200%; height: 200%;"></div>
<div style="background: blue; width: 100%; height: 100%;"></div>
<script>
document.addEventListener('MozReftestInvalidate', () => {
const rect = green.getBoundingClientRect();
window.scrollTo(rect.right, rect.bottom);
document.documentElement.classList.remove('reftest-wait');
});
</script>
</html>

Просмотреть файл

@ -0,0 +1,19 @@
<!DOCTYPE html>
<meta name="viewport" content="width=device-width, minimum-scale=0.5">
<style>
html {
overflow-y: hidden;
}
html, body {
margin: 0;
width: 100%;
height: 100%;
}
div {
width: 100%;
position: absolute;
}
</style>
<div style="background: red; height: 300%;"></div>
<div style="background: green; height: 200%;"></div>
<div style="background: white; height: 100%;"></div>

Просмотреть файл

@ -0,0 +1,18 @@
<!DOCTYPE html>
<meta name="viewport" content="width=device-width, initial-scale=0.5">
<style>
html, body {
margin: 0;
width: 100%;
height: 100%;
font-size: 20px;
scrollbar-width: none;
}
p {
line-height: 1rem;
width: 50%;
}
</style>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>

Просмотреть файл

@ -0,0 +1,23 @@
<!DOCTYPE html>
<meta name="viewport" content="width=device-width, minimum-scale=0.5">
<style>
html {
overflow: hidden;
font-size: 20px;
}
html, body {
margin: 0;
width: 100%;
height: 100%;
}
div {
position: absolute;
}
p {
line-height: 1rem;
}
</style>
</head><body><p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
<div style="width: 200%; height: 200%;"></div>