Display Cutout: Parse the 'viewport-fit' key

Parse the 'viewport-fit' key in the <meta> element and
store it on ViewportDescription.

BUG=838400

Change-Id: I0a037175791d4cfaadaa12dffa694eb92a60077d
Reviewed-on: https://chromium-review.googlesource.com/1036532
Reviewed-by: Stefan Zager <szager@chromium.org>
Commit-Queue: Becca Hughes <beccahughes@chromium.org>
Cr-Commit-Position: refs/heads/master@{#555562}
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index 6aa98f9..3860c06 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -1835,6 +1835,7 @@
     "html/html_image_element_test.cc",
     "html/html_link_element_sizes_attribute_test.cc",
     "html/html_link_element_test.cc",
+    "html/html_meta_element_test.cc",
     "html/html_slot_element_test.cc",
     "html/html_table_row_element_test.cc",
     "html/image_document_test.cc",
diff --git a/third_party/blink/renderer/core/dom/viewport_description.h b/third_party/blink/renderer/core/dom/viewport_description.h
index f5ca6fc..44b0281 100644
--- a/third_party/blink/renderer/core/dom/viewport_description.h
+++ b/third_party/blink/renderer/core/dom/viewport_description.h
@@ -66,6 +66,20 @@
     kTypeCount = 7
   };
 
+  // Stores the different possible |viewport-fit| configurations.
+  enum class ViewportFit {
+    // No effect - the whole web page is viewable (default).
+    kAuto = 0,
+
+    // The initial layout viewport and the visual viewport are set to the
+    // largest rectangle which is inscribed in the display of the device.
+    kContain,
+
+    // The initial layout viewport and the visual viewport are set to the
+    // circumscribed rectangle of the physical screen of the device.
+    kCover,
+  };
+
   enum {
     kValueAuto = -1,
     kValueDeviceWidth = -2,
@@ -118,6 +132,8 @@
   bool max_zoom_is_explicit;
   bool user_zoom_is_explicit;
 
+  ViewportFit viewport_fit = ViewportFit::kAuto;
+
   bool operator==(const ViewportDescription& other) const {
     // Used for figuring out whether to reset the viewport or not,
     // thus we are not taking type into account.
@@ -131,7 +147,8 @@
            zoom_is_explicit == other.zoom_is_explicit &&
            min_zoom_is_explicit == other.min_zoom_is_explicit &&
            max_zoom_is_explicit == other.max_zoom_is_explicit &&
-           user_zoom_is_explicit == other.user_zoom_is_explicit;
+           user_zoom_is_explicit == other.user_zoom_is_explicit &&
+           viewport_fit == other.viewport_fit;
   }
 
   bool operator!=(const ViewportDescription& other) const {
diff --git a/third_party/blink/renderer/core/html/html_meta_element.cc b/third_party/blink/renderer/core/html/html_meta_element.cc
index 4f4bf2e..6dfa9b9a 100644
--- a/third_party/blink/renderer/core/html/html_meta_element.cc
+++ b/third_party/blink/renderer/core/html/html_meta_element.cc
@@ -304,6 +304,20 @@
   return value;
 }
 
+ViewportDescription::ViewportFit HTMLMetaElement::ParseViewportFitValueAsEnum(
+    bool& unknown_value,
+    const String& value_string) {
+  if (DeprecatedEqualIgnoringCase(value_string, "auto"))
+    return ViewportDescription::ViewportFit::kAuto;
+  if (DeprecatedEqualIgnoringCase(value_string, "contain"))
+    return ViewportDescription::ViewportFit::kContain;
+  if (DeprecatedEqualIgnoringCase(value_string, "cover"))
+    return ViewportDescription::ViewportFit::kCover;
+
+  unknown_value = true;
+  return ViewportDescription::ViewportFit::kAuto;
+}
+
 void HTMLMetaElement::ProcessViewportKeyValuePair(
     Document* document,
     bool report_warnings,
@@ -352,7 +366,17 @@
   } else if (key_string == "minimal-ui") {
     // Ignore vendor-specific argument.
   } else if (key_string == "viewport-fit") {
-    // Ignore vendor-specific argument.
+    if (RuntimeEnabledFeatures::DisplayCutoutViewportFitEnabled()) {
+      bool unknown_value = false;
+      description->viewport_fit =
+          ParseViewportFitValueAsEnum(unknown_value, value_string);
+
+      // If we got an unknown value then report a warning.
+      if (unknown_value) {
+        ReportViewportWarning(document, kViewportFitUnsupported, value_string,
+                              String());
+      }
+    }
   } else if (key_string == "shrink-to-fit") {
     // Ignore vendor-specific argument.
   } else if (report_warnings) {
@@ -371,6 +395,7 @@
       "The value for key \"maximum-scale\" is out of bounds and the value has "
       "been clamped.",
       "The key \"target-densitydpi\" is not supported.",
+      "The value \"%replacement1\" for key \"viewport-fit\" is not supported.",
   };
 
   return kErrors[error_code];
@@ -383,6 +408,7 @@
     case kUnrecognizedViewportArgumentKeyError:
     case kUnrecognizedViewportArgumentValueError:
     case kMaximumScaleTooLargeError:
+    case kViewportFitUnsupported:
       return kWarningMessageLevel;
   }
 
diff --git a/third_party/blink/renderer/core/html/html_meta_element.h b/third_party/blink/renderer/core/html/html_meta_element.h
index be4132e..9303f6e 100644
--- a/third_party/blink/renderer/core/html/html_meta_element.h
+++ b/third_party/blink/renderer/core/html/html_meta_element.h
@@ -35,7 +35,8 @@
   kUnrecognizedViewportArgumentValueError,
   kTruncatedViewportArgumentValueError,
   kMaximumScaleTooLargeError,
-  kTargetDensityDpiUnsupported
+  kTargetDensityDpiUnsupported,
+  kViewportFitUnsupported
 };
 
 class CORE_EXPORT HTMLMetaElement final : public HTMLElement {
@@ -104,6 +105,10 @@
                                        const String& key,
                                        const String& value);
 
+  static ViewportDescription::ViewportFit ParseViewportFitValueAsEnum(
+      bool& unknown_value,
+      const String& value);
+
   static void ReportViewportWarning(Document*,
                                     ViewportErrorCode,
                                     const String& replacement1,
diff --git a/third_party/blink/renderer/core/html/html_meta_element_test.cc b/third_party/blink/renderer/core/html/html_meta_element_test.cc
new file mode 100644
index 0000000..7f14a0a
--- /dev/null
+++ b/third_party/blink/renderer/core/html/html_meta_element_test.cc
@@ -0,0 +1,61 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/html/html_meta_element.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/frame/settings.h"
+#include "third_party/blink/renderer/core/testing/page_test_base.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+
+namespace blink {
+
+class HTMLMetaElementTest : public PageTestBase {
+ public:
+  void SetUp() override {
+    PageTestBase::SetUp();
+
+    RuntimeEnabledFeatures::SetDisplayCutoutViewportFitEnabled(true);
+    GetDocument().GetSettings()->SetViewportMetaEnabled(true);
+  }
+
+  ViewportDescription::ViewportFit LoadTestPageAndReturnViewportFit(
+      const String& value) {
+    LoadTestPageWithViewportFitValue(value);
+    return GetDocument().GetViewportDescription().viewport_fit;
+  }
+
+ private:
+  void LoadTestPageWithViewportFitValue(const String& value) {
+    GetDocument().documentElement()->SetInnerHTMLFromString(
+        "<head>"
+        "<meta name='viewport' content='viewport-fit=" +
+        value +
+        "'>"
+        "</head>");
+  }
+};
+
+TEST_F(HTMLMetaElementTest, ViewportFit_Auto) {
+  EXPECT_EQ(ViewportDescription::ViewportFit::kAuto,
+            LoadTestPageAndReturnViewportFit("auto"));
+}
+
+TEST_F(HTMLMetaElementTest, ViewportFit_Contain) {
+  EXPECT_EQ(ViewportDescription::ViewportFit::kContain,
+            LoadTestPageAndReturnViewportFit("contain"));
+}
+
+TEST_F(HTMLMetaElementTest, ViewportFit_Cover) {
+  EXPECT_EQ(ViewportDescription::ViewportFit::kCover,
+            LoadTestPageAndReturnViewportFit("cover"));
+}
+
+TEST_F(HTMLMetaElementTest, ViewportFit_Invalid) {
+  EXPECT_EQ(ViewportDescription::ViewportFit::kAuto,
+            LoadTestPageAndReturnViewportFit("invalid"));
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 19def019..ab2c7b9 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -395,6 +395,10 @@
       name: "DisableRasterInvalidation",
     },
     {
+      name: "DisplayCutoutViewportFit",
+      settable_from_internals: true,
+    },
+    {
       name: "DisplayNoneIFrameCreatesNoLayoutObject",
       status: "stable",
     },