diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 86672ff0ccca..46379ce73d20 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -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 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) { diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index 8a5adb0e6ff6..e8cd6b31aaea 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -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; diff --git a/dom/ipc/BrowserChild.h b/dom/ipc/BrowserChild.h index d9accb5597d4..c75f124848d0 100644 --- a/dom/ipc/BrowserChild.h +++ b/dom/ipc/BrowserChild.h @@ -286,6 +286,7 @@ class BrowserChild final : public nsMessageManagerScriptExecutor, const mozilla::Maybe& aMatrix, const mozilla::ScreenRect& aRemoteDocumentRect); + MOZ_CAN_RUN_SCRIPT_BOUNDARY mozilla::ipc::IPCResult RecvDynamicToolbarMaxHeightChanged( const mozilla::ScreenIntCoord& aHeight); diff --git a/layout/base/MobileViewportManager.cpp b/layout/base/MobileViewportManager.cpp index f41e19ca1a33..ead5be5fe0d6 100644 --- a/layout/base/MobileViewportManager.cpp +++ b/layout/base/MobileViewportManager.cpp @@ -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() diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 268601326801..3de743542ef1 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -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; } diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp index 570d27ae3c56..42add327123f 100644 --- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -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 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(¤tWidth, + ¤tHeight); + 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 diff --git a/layout/base/nsPresContext.h b/layout/base/nsPresContext.h index e00ede664b42..e4acd0c6b444 100644 --- a/layout/base/nsPresContext.h +++ b/layout/base/nsPresContext.h @@ -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 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; diff --git a/layout/base/tests/file_dynamic_toolbar_max_height.html b/layout/base/tests/file_dynamic_toolbar_max_height.html new file mode 100644 index 000000000000..eb0d37f9b77a --- /dev/null +++ b/layout/base/tests/file_dynamic_toolbar_max_height.html @@ -0,0 +1,59 @@ + + + +Tests metrics with dynamic toolbar + + + +
+
+ diff --git a/layout/base/tests/mochitest.ini b/layout/base/tests/mochitest.ini index a73659f43999..0775ab7ca480 100644 --- a/layout/base/tests/mochitest.ini +++ b/layout/base/tests/mochitest.ini @@ -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] diff --git a/layout/base/tests/test_dynamic_toolbar_max_height.html b/layout/base/tests/test_dynamic_toolbar_max_height.html new file mode 100644 index 000000000000..7ee0f5163c3a --- /dev/null +++ b/layout/base/tests/test_dynamic_toolbar_max_height.html @@ -0,0 +1,23 @@ + + + + +
+ + diff --git a/servo/components/style/gecko/media_queries.rs b/servo/components/style/gecko/media_queries.rs index bb126e16948f..2e68a889df13 100644 --- a/servo/components/style/gecko/media_queries.rs +++ b/servo/components/style/gecko/media_queries.rs @@ -237,7 +237,13 @@ impl Device { /// used for viewport unit resolution. pub fn au_viewport_size_for_viewport_unit_resolution(&self) -> Size2D { 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.