Bug 1574307 - Part 1. Parse viewport-fit in meta element. r=smaug
authorMakoto Kato <m_kato@ga2.so-net.ne.jp>
Mon, 20 Jan 2020 08:40:05 +0000
changeset 510875 1a20594b4fe249b7b8bbeb62bffc1c647f67d17b
parent 510874 4f49e933eb839b72b69757ba8c633687e474dbae
child 510876 1571a382ea470f5e07645f2037c5702e247dc4b7
push id37041
push usershindli@mozilla.com
push dateTue, 21 Jan 2020 16:14:08 +0000
treeherdermozilla-central@875ae8e3ce6f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1574307
milestone74.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1574307 - Part 1. Parse viewport-fit in meta element. r=smaug For safe area insets (cutout) support, CSS Round Display Level 1 (https://drafts.csswg.org/css-round-display/#viewport-fit-descriptor) has new viewport value as `viewport-fit`. To support safe area insets that is notch on display, CSS Environment Variables Module Level 1 (https://drafts.csswg.org/css-env-1/#safe-area-insets) adds `safearea-insets-*` (left, top, right and bottom). Also, `meta` element has `viewport-fit` enum value. (ex `<meta name="viewport" content="viewport-fit=cover>`) whether web browser window cover notch area. `viewport-fit` has 3 enum value, `auto`, `cover` and `contain`. GeckoView wants to expose this value to browser application such Fenix. Because Android API (https://developer.android.com/guide/topics/display-cutout) uses window root layout (It is set by Application) to cover notch on display. Differential Revision: https://phabricator.services.mozilla.com/D55609
dom/base/Document.cpp
dom/base/Document.h
dom/base/ViewportMetaData.cpp
dom/base/ViewportMetaData.h
dom/base/nsViewportInfo.h
gfx/layers/apz/test/gtest/mvm/TestMobileViewportManager.cpp
xpcom/ds/StaticAtoms.py
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -1349,16 +1349,17 @@ Document::Document(const char* aContentT
       mBlockDOMContentLoaded(0),
       mUseCounters(0),
       mChildDocumentUseCounters(0),
       mUserHasInteracted(false),
       mHasUserInteractionTimerScheduled(false),
       mStackRefCnt(0),
       mUpdateNestLevel(0),
       mViewportType(Unknown),
+      mViewportFit(ViewportFitType::Auto),
       mSubDocuments(nullptr),
       mHeaderData(nullptr),
       mFlashClassification(FlashClassification::Unknown),
       mScrollAnchorAdjustmentLength(0),
       mScrollAnchorAdjustmentCount(0),
       mServoRestyleRootDirtyBits(0),
       mThrowOnDynamicMarkupInsertionCounter(0),
       mIgnoreOpensDuringUnloadCounter(0),
@@ -9614,16 +9615,31 @@ nsViewportInfo Document::GetViewportInfo
           (metaData.mUserScalable.EqualsLiteral("no")) ||
           (metaData.mUserScalable.EqualsLiteral("false"))) {
         mAllowZoom = false;
       }
       if (!metaData.mUserScalable.IsEmpty()) {
         hasValidContents = true;
       }
 
+      // Resolve viewport-fit value.
+      // https://drafts.csswg.org/css-round-display/#viewport-fit-descriptor
+      mViewportFit = ViewportFitType::Auto;
+      if (!metaData.mViewportFit.IsEmpty()) {
+        if (metaData.mViewportFit.EqualsLiteral("contain")) {
+          mViewportFit = ViewportFitType::Contain;
+          hasValidContents = true;
+        } else if (metaData.mViewportFit.EqualsLiteral("cover")) {
+          mViewportFit = ViewportFitType::Cover;
+          hasValidContents = true;
+        } else if (metaData.mViewportFit.EqualsLiteral("auto")) {
+          hasValidContents = true;
+        }
+      }
+
       mWidthStrEmpty = metaData.mWidth.IsEmpty();
 
       mViewportType = hasValidContents ? Specified : NoValidContent;
       [[fallthrough]];
     }
     case Specified:
     case NoValidContent:
     default:
@@ -9824,17 +9840,17 @@ nsViewportInfo Document::GetViewportInfo
         size.width = std::max(size.width, displaySize.width);
         size.height = std::max(size.height, displaySize.height);
       }
 
       return nsViewportInfo(
           scaleFloat, scaleMinFloat, scaleMaxFloat, size, sizeFlag,
           mValidScaleFloat ? nsViewportInfo::AutoScaleFlag::FixedScale
                            : nsViewportInfo::AutoScaleFlag::AutoScale,
-          effectiveZoomFlag);
+          effectiveZoomFlag, mViewportFit);
   }
 }
 
 ViewportMetaData Document::GetViewportMetaData() const {
   // The order of mMetaViewport is first-modified is first. We want the last
   // modified since Chrome uses the last one.
   // See https://webcompat.com/issues/20701#issuecomment-436054739
   return !mMetaViewports.IsEmpty() ? mMetaViewports.LastElement().mData
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -208,16 +208,17 @@ class ServiceWorkerDescriptor;
 class StyleSheetList;
 class SVGDocument;
 class SVGElement;
 class SVGSVGElement;
 class SVGUseElement;
 class Touch;
 class TouchList;
 class TreeWalker;
+enum class ViewportFitType : uint8_t;
 class XPathEvaluator;
 class XPathExpression;
 class XPathNSResolver;
 class XPathResult;
 class BrowsingContext;
 
 template <typename>
 class Sequence;
@@ -5162,16 +5163,20 @@ class Document : public nsINode,
     DisplayWidthHeight,
     Specified,
     Unknown,
     NoValidContent,
   };
 
   ViewportType mViewportType;
 
+  // viewport-fit described by
+  // https://drafts.csswg.org/css-round-display/#viewport-fit-descriptor
+  ViewportFitType mViewportFit;
+
   PLDHashTable* mSubDocuments;
 
   DocHeaderData* mHeaderData;
 
   // For determining if this is a flash document which should be
   // blocked based on its principal.
   FlashClassification mFlashClassification;
 
--- a/dom/base/ViewportMetaData.cpp
+++ b/dom/base/ViewportMetaData.cpp
@@ -50,16 +50,18 @@ static void ProcessViewportToken(Viewpor
   } else if (key_atom == nsGkAtoms::initial_scale) {
     aData.mInitialScale.Assign(value);
   } else if (key_atom == nsGkAtoms::minimum_scale) {
     aData.mMinimumScale.Assign(value);
   } else if (key_atom == nsGkAtoms::maximum_scale) {
     aData.mMaximumScale.Assign(value);
   } else if (key_atom == nsGkAtoms::user_scalable) {
     aData.mUserScalable.Assign(value);
+  } else if (key_atom == nsGkAtoms::viewport_fit) {
+    aData.mViewportFit.Assign(value);
   }
 }
 
 #define IS_SEPARATOR(c)                                             \
   (((c) == '=') || ((c) == ',') || ((c) == ';') || ((c) == '\t') || \
    ((c) == '\n') || ((c) == '\r'))
 
 ViewportMetaData::ViewportMetaData(const nsAString& aViewportInfo) {
--- a/dom/base/ViewportMetaData.h
+++ b/dom/base/ViewportMetaData.h
@@ -15,23 +15,25 @@ namespace dom {
 struct ViewportMetaData {
   // https://drafts.csswg.org/css-device-adapt/#meta-properties
   nsString mWidth;
   nsString mHeight;
   nsString mInitialScale;
   nsString mMinimumScale;
   nsString mMaximumScale;
   nsString mUserScalable;
+  nsString mViewportFit;
 
   bool operator==(const ViewportMetaData& aOther) const {
     return mWidth == aOther.mWidth && mHeight == aOther.mHeight &&
            mInitialScale == aOther.mInitialScale &&
            mMinimumScale == aOther.mMinimumScale &&
            mMaximumScale == aOther.mMaximumScale &&
-           mUserScalable == aOther.mUserScalable;
+           mUserScalable == aOther.mUserScalable &&
+           mViewportFit == aOther.mViewportFit;
   }
   bool operator!=(const ViewportMetaData& aOther) const {
     return !(*this == aOther);
   }
 
   ViewportMetaData() = default;
   /* Process viewport META data. This gives us information for the scale
    * and zoom of a page on mobile devices. We stick the information in
--- a/dom/base/nsViewportInfo.h
+++ b/dom/base/nsViewportInfo.h
@@ -4,16 +4,26 @@
 
 #ifndef nsViewportInfo_h___
 #define nsViewportInfo_h___
 
 #include <stdint.h>
 #include "mozilla/Attributes.h"
 #include "Units.h"
 
+namespace mozilla {
+namespace dom {
+enum class ViewportFitType : uint8_t {
+  Auto,
+  Contain,
+  Cover,
+};
+}
+}  // namespace mozilla
+
 /**
  * Default values for the nsViewportInfo class.
  */
 static const mozilla::LayoutDeviceToScreenScale kViewportMinScale(0.25f);
 static const mozilla::LayoutDeviceToScreenScale kViewportMaxScale(10.0f);
 static const mozilla::CSSIntSize kViewportMinSize(200, 40);
 static const mozilla::CSSIntSize kViewportMaxSize(10000, 10000);
 
@@ -34,51 +44,56 @@ class MOZ_STACK_CLASS nsViewportInfo {
   enum class ZoomFlag {
     AllowZoom,
     DisallowZoom,
   };
   nsViewportInfo(const mozilla::ScreenIntSize& aDisplaySize,
                  const mozilla::CSSToScreenScale& aDefaultZoom,
                  ZoomFlag aZoomFlag)
       : mDefaultZoom(aDefaultZoom),
+        mViewportFit(mozilla::dom::ViewportFitType::Auto),
         mDefaultZoomValid(true),
         mAutoSize(true),
         mAllowZoom(aZoomFlag == ZoomFlag::AllowZoom) {
     mSize = mozilla::ScreenSize(aDisplaySize) / mDefaultZoom;
     mozilla::CSSToLayoutDeviceScale pixelRatio(1.0f);
     mMinZoom = pixelRatio * kViewportMinScale;
     mMaxZoom = pixelRatio * kViewportMaxScale;
     ConstrainViewportValues();
   }
 
   nsViewportInfo(const mozilla::CSSToScreenScale& aDefaultZoom,
                  const mozilla::CSSToScreenScale& aMinZoom,
                  const mozilla::CSSToScreenScale& aMaxZoom,
                  const mozilla::CSSSize& aSize, AutoSizeFlag aAutoSizeFlag,
-                 AutoScaleFlag aAutoScaleFlag, ZoomFlag aZoomFlag)
+                 AutoScaleFlag aAutoScaleFlag, ZoomFlag aZoomFlag,
+                 mozilla::dom::ViewportFitType aViewportFit)
       : mDefaultZoom(aDefaultZoom),
         mMinZoom(aMinZoom),
         mMaxZoom(aMaxZoom),
         mSize(aSize),
+        mViewportFit(aViewportFit),
         mDefaultZoomValid(aAutoScaleFlag != AutoScaleFlag::AutoScale),
         mAutoSize(aAutoSizeFlag == AutoSizeFlag::AutoSize),
         mAllowZoom(aZoomFlag == ZoomFlag::AllowZoom) {
     ConstrainViewportValues();
   }
 
   bool IsDefaultZoomValid() const { return mDefaultZoomValid; }
   mozilla::CSSToScreenScale GetDefaultZoom() const { return mDefaultZoom; }
   mozilla::CSSToScreenScale GetMinZoom() const { return mMinZoom; }
   mozilla::CSSToScreenScale GetMaxZoom() const { return mMaxZoom; }
 
   mozilla::CSSSize GetSize() const { return mSize; }
 
   bool IsAutoSizeEnabled() const { return mAutoSize; }
   bool IsZoomAllowed() const { return mAllowZoom; }
 
+  mozilla::dom::ViewportFitType GetViewportFit() const { return mViewportFit; }
+
   enum {
     Auto = -1,
     ExtendToZoom = -2,
     DeviceSize = -3,  // for device-width or device-height
   };
   // MIN/MAX computations where one of the arguments is auto resolve to the
   // other argument. For instance, MIN(0.25, auto) = 0.25, and
   // MAX(5, auto) = 5.
@@ -102,16 +117,19 @@ class MOZ_STACK_CLASS nsViewportInfo {
   mozilla::CSSToScreenScale mMinZoom;
 
   // The maximum zoom level permitted by the page.
   mozilla::CSSToScreenScale mMaxZoom;
 
   // The size of the viewport, specified by the <meta name="viewport"> tag.
   mozilla::CSSSize mSize;
 
+  // The value of the viewport-fit.
+  mozilla::dom::ViewportFitType mViewportFit;
+
   // If the default zoom was specified and was between the min and max
   // zoom values.
   // FIXME: Bug 1504362 - Unify this and mDefaultZoom into
   // Maybe<CSSToScreenScale>.
   bool mDefaultZoomValid;
 
   // Whether or not we should automatically size the viewport to the device's
   // width. This is true if the document has been optimized for mobile, and
--- a/gfx/layers/apz/test/gtest/mvm/TestMobileViewportManager.cpp
+++ b/gfx/layers/apz/test/gtest/mvm/TestMobileViewportManager.cpp
@@ -54,17 +54,18 @@ class MockMVMContext : public MVMContext
     // this test harness.
     CSSSize viewportSize = mDisplaySize / mDeviceScale;
     if (mAutoSizeFlag == AutoSizeFlag::FixedSize) {
       viewportSize = CSSSize(mFixedViewportWidth,
                              mFixedViewportWidth * (float(mDisplaySize.height) /
                                                     mDisplaySize.width));
     }
     return nsViewportInfo(mDefaultScale, mMinScale, mMaxScale, viewportSize,
-                          mAutoSizeFlag, mAutoScaleFlag, mZoomFlag);
+                          mAutoSizeFlag, mAutoScaleFlag, mZoomFlag,
+                          dom::ViewportFitType::Auto);
   }
   CSSToLayoutDeviceScale CSSToDevPixelScale() const { return mDeviceScale; }
   float GetResolution() const { return mResolution; }
   bool SubjectMatchesDocument(nsISupports* aSubject) const { return true; }
   Maybe<CSSRect> CalculateScrollableRectForRSF() const {
     return Some(CSSRect(CSSPoint(), mContentSize));
   }
   bool IsResolutionUpdatedByApz() const { return false; }
--- a/xpcom/ds/StaticAtoms.py
+++ b/xpcom/ds/StaticAtoms.py
@@ -1240,16 +1240,17 @@ STATIC_ATOMS = [
     Atom("variable", "variable"),
     Atom("vendor", "vendor"),
     Atom("vendorUrl", "vendor-url"),
     Atom("version", "version"),
     Atom("vertical", "vertical"),
     Atom("audio", "audio"),
     Atom("video", "video"),
     Atom("viewport", "viewport"),
+    Atom("viewport_fit", "viewport-fit"),
     Atom("viewport_height", "viewport-height"),
     Atom("viewport_initial_scale", "viewport-initial-scale"),
     Atom("viewport_maximum_scale", "viewport-maximum-scale"),
     Atom("viewport_minimum_scale", "viewport-minimum-scale"),
     Atom("viewport_user_scalable", "viewport-user-scalable"),
     Atom("viewport_width", "viewport-width"),
     Atom("visibility", "visibility"),
     Atom("visuallyselected", "visuallyselected"),