зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
f3f82aedc4
Коммит
4faf5e4991
|
@ -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>
|
Загрузка…
Ссылка в новой задаче