Bug 1586144 - Factor dynamic toolbar max height into layout metrics. r=emilio,botond

Now
* nsPresContext::mVisibleArea is excluding the toolbar max height so that
  ICB is now static regardless of the dynamic toolbar transition
* nsPresContext::mSizeForViewportUnits is introduced to resolve viewport units
  which is including the toolbar max height

That means that with the dynamic toolbar max height;

  mVisibleArea < mSizeForViewportUnits

See https://github.com/bokand/URLBarSizing for more detail backgrounds of this
change.

Depends on D50417

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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Hiroyuki Ikezoe 2019-11-14 06:00:05 +00:00
parent 2a51338a5a
commit f5f6f60da5
11 changed files with 215 additions and 11 deletions

View File

@ -1482,6 +1482,24 @@ nsDOMWindowUtils::GetVisualViewportOffset(int32_t* aOffsetX,
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::SetDynamicToolbarMaxHeight(uint32_t aHeightInScreen) {
if (aHeightInScreen > INT32_MAX) {
return NS_ERROR_INVALID_ARG;
}
RefPtr<nsPresContext> presContext = GetPresContext();
if (!presContext) {
return NS_OK;
}
MOZ_ASSERT(presContext->IsRootContentDocumentCrossProcess());
presContext->SetDynamicToolbarMaxHeight(ScreenIntCoord(aHeightInScreen));
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::GetScrollbarSize(bool aFlushLayout, int32_t* aWidth,
int32_t* aHeight) {

View File

@ -867,6 +867,12 @@ interface nsIDOMWindowUtils : nsISupports {
*/
void getVisualViewportOffset(out long aOffsetX, out long aOffsetY);
/**
* Sets the maximum height of the dynamic toolbar in Screen pixel units.
*/
[can_run_script]
void setDynamicToolbarMaxHeight(in uint32_t aHeightInScreen);
const long FLUSH_NONE = -1;
const long FLUSH_STYLE = 0;
const long FLUSH_LAYOUT = 1;

View File

@ -286,6 +286,7 @@ class BrowserChild final : public nsMessageManagerScriptExecutor,
const mozilla::Maybe<mozilla::gfx::Matrix4x4>& aMatrix,
const mozilla::ScreenRect& aRemoteDocumentRect);
MOZ_CAN_RUN_SCRIPT_BOUNDARY
mozilla::ipc::IPCResult RecvDynamicToolbarMaxHeightChanged(
const mozilla::ScreenIntCoord& aHeight);

View File

@ -460,6 +460,9 @@ ScreenIntSize MobileViewportManager::GetCompositionSize(
return ScreenIntSize();
}
// FIXME: Bug 1586986 - To update VisualViewport in response to the dynamic
// toolbar transition we probably need to include the dynamic toolbar
// _current_ height.
ScreenIntSize compositionSize(aDisplaySize);
ScreenMargin scrollbars =
mContext->ScrollbarAreaToExcludeFromCompositionBounds()

View File

@ -8424,6 +8424,19 @@ bool nsLayoutUtils::GetContentViewerSize(nsPresContext* aPresContext,
nsIntRect bounds;
cv->GetBounds(bounds);
if (aPresContext->HasDynamicToolbar() && !bounds.IsEmpty()) {
MOZ_ASSERT(aPresContext->IsRootContentDocumentCrossProcess());
MOZ_ASSERT(bounds.height > aPresContext->GetDynamicToolbarMaxHeight());
bounds.height -= aPresContext->GetDynamicToolbarMaxHeight();
// Collapse the size in the case the dynamic toolbar max height is greater
// than the content bound height so that hopefully embedders of GeckoView
// may notice they set wrong dynamic toolbar max height.
if (bounds.height < 0) {
bounds.height = 0;
}
}
aOutSize = LayoutDeviceIntRect::FromUnknownRect(bounds).Size();
return true;
}

View File

@ -87,6 +87,7 @@
#include "mozilla/dom/Performance.h"
#include "mozilla/dom/PerformanceTiming.h"
#include "mozilla/layers/APZThreadUtils.h"
#include "MobileViewportManager.h"
// Needed for Start/Stop of Image Animation
#include "imgIContainer.h"
@ -422,6 +423,12 @@ void nsPresContext::AppUnitsPerDevPixelChanged() {
mCurAppUnitsPerDevPixel = mDeviceContext->AppUnitsPerDevPixel();
// Recompute the size for vh units since it's changed by the dynamic toolbar
// max height which is stored in screen coord.
if (IsRootContentDocumentCrossProcess()) {
AdjustSizeForViewportUnits();
}
// nsSubDocumentFrame uses a AppUnitsPerDevPixel difference between parent and
// child document to determine if it needs to build a nsDisplayZoom item. So
// if we that changes then we need to invalidate the subdoc frame so that
@ -2466,6 +2473,21 @@ void nsPresContext::FlushFontFeatureValues() {
}
}
void nsPresContext::SetVisibleArea(const nsRect& r) {
if (!r.IsEqualEdges(mVisibleArea)) {
mVisibleArea = r;
mSizeForViewportUnits = mVisibleArea.Size();
if (IsRootContentDocumentCrossProcess()) {
AdjustSizeForViewportUnits();
}
// Visible area does not affect media queries when paginated.
if (!IsPaginated()) {
MediaFeatureValuesChanged(
{mozilla::MediaFeatureChangeReason::ViewportChange});
}
}
}
void nsPresContext::SetDynamicToolbarMaxHeight(ScreenIntCoord aHeight) {
MOZ_ASSERT(IsRootContentDocumentCrossProcess());
@ -2473,6 +2495,44 @@ void nsPresContext::SetDynamicToolbarMaxHeight(ScreenIntCoord aHeight) {
return;
}
mDynamicToolbarMaxHeight = aHeight;
AdjustSizeForViewportUnits();
if (RefPtr<mozilla::PresShell> presShell = mPresShell) {
// Changing the max height of the dynamic toolbar changes the ICB size, we
// need to kick a reflow with the current window dimensions since the max
// height change doesn't change the window dimensions but
// PresShell::ResizeReflow ends up subtracting the new dynamic toolbar
// height from the window dimensions and kick a reflow with the proper ICB
// size.
nscoord currentWidth, currentHeight;
presShell->GetViewManager()->GetWindowDimensions(&currentWidth,
&currentHeight);
presShell->ResizeReflow(currentWidth, currentHeight,
ResizeReflowOptions::NoOption);
}
}
void nsPresContext::AdjustSizeForViewportUnits() {
MOZ_ASSERT(IsRootContentDocumentCrossProcess());
if (mVisibleArea.height == NS_UNCONSTRAINEDSIZE) {
// Ignore `NS_UNCONSTRAINEDSIZE` since it's a temporary state during a
// reflow. We will end up calling this function again with a proper size in
// the same reflow.
return;
}
if (MOZ_UNLIKELY(mVisibleArea.height +
NSIntPixelsToAppUnits(mDynamicToolbarMaxHeight,
mCurAppUnitsPerDevPixel) >
nscoord_MAX)) {
MOZ_ASSERT_UNREACHABLE("The dynamic toolbar max height is probably wrong");
return;
}
mSizeForViewportUnits.height =
mVisibleArea.height +
NSIntPixelsToAppUnits(mDynamicToolbarMaxHeight, mCurAppUnitsPerDevPixel);
}
#ifdef DEBUG

View File

@ -369,22 +369,27 @@ class nsPresContext : public nsISupports,
* Set the currently visible area. The units for r are standard
* nscoord units (as scaled by the device context).
*/
void SetVisibleArea(const nsRect& r) {
if (!r.IsEqualEdges(mVisibleArea)) {
mVisibleArea = r;
// Visible area does not affect media queries when paginated.
if (!IsPaginated()) {
MediaFeatureValuesChanged(
{mozilla::MediaFeatureChangeReason::ViewportChange});
}
}
}
void SetVisibleArea(const nsRect& r);
/**
* Set the maximum height of the dynamic toolbar in nscoord units.
*/
MOZ_CAN_RUN_SCRIPT
void SetDynamicToolbarMaxHeight(mozilla::ScreenIntCoord aHeight);
mozilla::ScreenIntCoord GetDynamicToolbarMaxHeight() const {
MOZ_ASSERT(IsRootContentDocumentCrossProcess());
return mDynamicToolbarMaxHeight;
}
/**
* Returns true if we are using the dynamic toolbar.
*/
bool HasDynamicToolbar() const {
MOZ_ASSERT(IsRootContentDocumentCrossProcess());
return mDynamicToolbarMaxHeight > 0;
}
/**
* Return true if this presentation context is a paginated
* context.
@ -1114,6 +1119,10 @@ class nsPresContext : public nsISupports,
};
TransactionInvalidations* GetInvalidations(TransactionId aTransactionId);
// This should be called only when we update mVisibleArea or
// mDynamicToolbarMaxHeight or `app units per device pixels` changes.
void AdjustSizeForViewportUnits();
// IMPORTANT: The ownership implicit in the following member variables
// has been explicitly checked. If you add any members to this class,
// please make the ownership explicit (pinkerton, scc).
@ -1173,6 +1182,10 @@ class nsPresContext : public nsISupports,
mozilla::UniquePtr<gfxMissingFontRecorder> mMissingFonts;
nsRect mVisibleArea;
// This value is used to resolve viewport units.
// On mobile this size is including the dynamic toolbar maximum height below.
// On desktops this size is pretty much the same as |mVisibleArea|.
nsSize mSizeForViewportUnits;
// The maximum height of the dynamic toolbar on mobile.
mozilla::ScreenIntCoord mDynamicToolbarMaxHeight;
nsSize mPageSize;

View File

@ -0,0 +1,59 @@
<!doctype html>
<meta charset=utf-8>
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
<title>Tests metrics with dynamic toolbar</title>
<script>
const ok = opener.ok.bind(opener);
const is = opener.is.bind(opener);
const original_finish = opener.SimpleTest.finish;
const SimpleTest = opener.SimpleTest;
const add_task = opener.add_task;
SimpleTest.finish = function finish() {
self.close();
original_finish();
}
</script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
<style>
html {
scrollbar-width: none;
}
#percent {
position: absolute;
height: 100%;
}
#vh {
position: absolute;
height: 100vh;
}
</style>
<div id="percent"></div>
<div id="vh"></div>
<script>
'use strict';
SpecialPowers.DOMWindowUtils.setDynamicToolbarMaxHeight(0);
let percentHeight = getComputedStyle(percent).height;
let vhHeight = getComputedStyle(vh).height;
is(percentHeight, vhHeight,
"%-units and vh-units should be the same when the dynamic toolbar max " +
"height is zero");
SpecialPowers.DOMWindowUtils.setDynamicToolbarMaxHeight(50);
percentHeight = getComputedStyle(percent).height;
vhHeight = getComputedStyle(vh).height;
is(parseInt(percentHeight) + 50, parseInt(vhHeight),
"vh units should be 50px greater than %-units");
is(document.documentElement.clientHeight, parseInt(percentHeight),
"documentElement.clientHeight should equal to %-units");
is(window.innerHeight, parseInt(percentHeight),
"window.innerHeight should equal to %-units when the dynamic toolbar is " +
"visible");
ok(matchMedia(`(height: ${percentHeight})`).matches,
"Media Queries' height is not including the dynamic toolbar max height");
SimpleTest.finish();
</script>

View File

@ -139,6 +139,8 @@ support-files = bug1226904.html
support-files = bug1448730.html
[test_bug1515822.html]
[test_bug1550869_video.html]
[test_dynamic_toolbar_max_height.html]
support-files = file_dynamic_toolbar_max_height.html
[test_emulateMedium.html]
[test_emulate_color_scheme.html]
[test_event_target_iframe_oop.html]

View File

@ -0,0 +1,23 @@
<!doctype html>
<meta charset=utf-8>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<div id="log"></div>
<script>
"use strict";
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv(
{
set: [
["dom.meta-viewport.enabled", true],
["apz.allow_zooming", true],
],
},
function() {
// We need to open a new window because the API to set the dynamic toolbar
// max height works only in the top content document.
window.open("file_dynamic_toolbar_max_height.html");
}
);
</script>
</html>

View File

@ -237,7 +237,13 @@ impl Device {
/// used for viewport unit resolution.
pub fn au_viewport_size_for_viewport_unit_resolution(&self) -> Size2D<Au> {
self.used_viewport_size.store(true, Ordering::Relaxed);
self.au_viewport_size()
let pc = match self.pres_context() {
Some(pc) => pc,
None => return Size2D::new(Au(0), Au(0)),
};
let size = &pc.mSizeForViewportUnits;
Size2D::new(Au(size.width), Au(size.height))
}
/// Returns whether we ever looked up the viewport size of the Device.