Store tab_strip scope_patterns field in web_app_database

Bug: 1381374
Change-Id: If803e582bc1b57154fd279a8846005fff41983fe
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4530101
Reviewed-by: Ben Kelly <[email protected]>
Reviewed-by: Alan Cutter <[email protected]>
Commit-Queue: Ben Kelly <[email protected]>
Auto-Submit: Louise Brett <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1144751}
diff --git a/chrome/browser/web_applications/DEPS b/chrome/browser/web_applications/DEPS
index 242c13f..b15f1b0 100644
--- a/chrome/browser/web_applications/DEPS
+++ b/chrome/browser/web_applications/DEPS
@@ -9,5 +9,6 @@
   "+mojo/public/cpp/bindings",
   "+services/network/public/cpp",
   "+third_party/blink/public/common",
+  "+third_party/liburlpattern",
   "+url",
 }
diff --git a/chrome/browser/web_applications/proto/BUILD.gn b/chrome/browser/web_applications/proto/BUILD.gn
index f3d25488..dcb3712 100644
--- a/chrome/browser/web_applications/proto/BUILD.gn
+++ b/chrome/browser/web_applications/proto/BUILD.gn
@@ -13,6 +13,7 @@
     "web_app_share_target.proto",
     "web_app_tab_strip.proto",
     "web_app_translations.proto",
+    "web_app_url_pattern.proto",
   ]
   link_deps = [
     "//chrome/browser/ash/system_web_apps/types/proto",
diff --git a/chrome/browser/web_applications/proto/web_app_tab_strip.proto b/chrome/browser/web_applications/proto/web_app_tab_strip.proto
index 55d20bf..ee7aef5a 100644
--- a/chrome/browser/web_applications/proto/web_app_tab_strip.proto
+++ b/chrome/browser/web_applications/proto/web_app_tab_strip.proto
@@ -5,6 +5,7 @@
 syntax = "proto2";
 
 import "content/browser/background_fetch/background_fetch.proto";
+import "chrome/browser/web_applications/proto/web_app_url_pattern.proto";
 
 option optimize_for = LITE_RUNTIME;
 
@@ -12,6 +13,7 @@
 
 message HomeTabParams {
   repeated content.proto.ImageResource icons = 1;
+  repeated UrlPattern scope_patterns = 2;
 }
 
 message NewTabButtonParams {
diff --git a/chrome/browser/web_applications/proto/web_app_url_pattern.proto b/chrome/browser/web_applications/proto/web_app_url_pattern.proto
new file mode 100644
index 0000000..0c9c771
--- /dev/null
+++ b/chrome/browser/web_applications/proto/web_app_url_pattern.proto
@@ -0,0 +1,39 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package web_app.proto;
+
+// Proto represtenting a UrlPattern. This should be kept up to date with
+// third_party/blink/public/common/url_pattern.h
+message UrlPattern {
+  repeated UrlPatternPart pathname = 1;
+}
+
+message UrlPatternPart {
+  enum PartType {
+    UNKNOWN_PART_TYPE = 0;
+    FULL_WILDCARD = 1;
+    SEGMENT_WILDCARD = 2;
+    FIXED = 3;
+  }
+  optional PartType part_type = 1;
+
+  optional string name = 2;
+  optional string prefix = 3;
+  optional string value = 4;
+  optional string suffix = 5;
+
+  enum Modifier {
+    UNKNOWN_MODIFIER = 0;
+    ZERO_OR_MORE = 1;
+    OPTIONAL = 2;
+    ONE_OR_MORE = 3;
+    NONE = 4;
+  }
+  optional Modifier modifier = 6;
+}
diff --git a/chrome/browser/web_applications/test/web_app_test_utils.cc b/chrome/browser/web_applications/test/web_app_test_utils.cc
index 3cffcd9..8620373 100644
--- a/chrome/browser/web_applications/test/web_app_test_utils.cc
+++ b/chrome/browser/web_applications/test/web_app_test_utils.cc
@@ -71,7 +71,9 @@
 #include "third_party/blink/public/common/permissions_policy/permissions_policy.h"
 #include "third_party/blink/public/common/permissions_policy/policy_helper_public.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
+#include "third_party/blink/public/common/url_pattern.h"
 #include "third_party/blink/public/mojom/manifest/capture_links.mojom-shared.h"
+#include "third_party/liburlpattern/pattern.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "url/gurl.h"
 #include "url/origin.h"
@@ -383,6 +385,42 @@
   return icons;
 }
 
+std::vector<blink::UrlPattern> CreateRandomScopePatterns(RandomHelper& random) {
+  std::vector<blink::UrlPattern> scope_patterns;
+
+  for (int i = random.next_uint(4) + 1; i >= 0; --i) {
+    blink::UrlPattern url_pattern;
+
+    for (int j = random.next_uint(4) + 1; j >= 0; --j) {
+      liburlpattern::Part part;
+
+      std::vector<liburlpattern::PartType> part_types = {
+          liburlpattern::PartType::kFixed,
+          liburlpattern::PartType::kFullWildcard,
+          liburlpattern::PartType::kSegmentWildcard};
+      std::vector<liburlpattern::Modifier> modifiers = {
+          liburlpattern::Modifier::kZeroOrMore,
+          liburlpattern::Modifier::kOptional,
+          liburlpattern::Modifier::kOneOrMore, liburlpattern::Modifier::kNone};
+      part.type = part_types[random.next_uint(part_types.size())];
+      part.value = "value" + base::NumberToString(j);
+      if (part.type == liburlpattern::PartType::kFullWildcard ||
+          part.type == liburlpattern::PartType::kSegmentWildcard) {
+        part.prefix = "prefix" + base::NumberToString(j);
+        part.name = "name" + base::NumberToString(j);
+        part.suffix = "suffix" + base::NumberToString(j);
+      }
+
+      part.modifier = modifiers[random.next_uint(modifiers.size())];
+
+      url_pattern.pathname.push_back(std::move(part));
+    }
+
+    scope_patterns.push_back(std::move(url_pattern));
+  }
+  return scope_patterns;
+}
+
 proto::WebAppOsIntegrationState GenerateRandomWebAppOsIntegrationState(
     RandomHelper& random,
     WebApp& app) {
@@ -828,6 +866,9 @@
       if (random.next_bool()) {
         home_tab_params.icons = CreateRandomHomeTabIcons(random);
       }
+      if (random.next_bool()) {
+        home_tab_params.scope_patterns = CreateRandomScopePatterns(random);
+      }
       tab_strip.home_tab = std::move(home_tab_params);
     } else {
       tab_strip.home_tab =
diff --git a/chrome/browser/web_applications/web_app.h b/chrome/browser/web_applications/web_app.h
index d7a0a51..fbc1eab 100644
--- a/chrome/browser/web_applications/web_app.h
+++ b/chrome/browser/web_applications/web_app.h
@@ -566,6 +566,8 @@
   // like is_placeholder and install URLs.
   ExternalConfigMap management_to_external_config_map_;
 
+  // TODO(crbug.com/1381374): Add to AsDebugValue() and
+  // GetManifestDataChanges().
   absl::optional<blink::Manifest::TabStrip> tab_strip_;
 
   // Only used on Mac.
diff --git a/chrome/browser/web_applications/web_app_database.cc b/chrome/browser/web_applications/web_app_database.cc
index 803630c..41a79c4 100644
--- a/chrome/browser/web_applications/web_app_database.cc
+++ b/chrome/browser/web_applications/web_app_database.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
 #include "chrome/browser/web_applications/os_integration/web_app_file_handler_manager.h"
 #include "chrome/browser/web_applications/proto/web_app.pb.h"
+#include "chrome/browser/web_applications/proto/web_app_url_pattern.pb.h"
 #include "chrome/browser/web_applications/user_display_mode.h"
 #include "chrome/browser/web_applications/web_app.h"
 #include "chrome/browser/web_applications/web_app_chromeos_data.h"
@@ -44,8 +45,10 @@
 #include "third_party/blink/public/common/manifest/manifest.h"
 #include "third_party/blink/public/common/permissions_policy/origin_with_possible_wildcards.h"
 #include "third_party/blink/public/common/permissions_policy/policy_helper_public.h"
+#include "third_party/blink/public/common/url_pattern.h"
 #include "third_party/blink/public/mojom/manifest/capture_links.mojom.h"
 #include "third_party/blink/public/mojom/manifest/manifest.mojom.h"
+#include "third_party/blink/public/mojom/url_pattern.mojom.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -303,16 +306,6 @@
   }
 }
 
-TabStrip::Visibility ProtoToTabStripVisibility(
-    proto::TabStrip::Visibility visibility) {
-  switch (visibility) {
-    case proto::TabStrip_Visibility_AUTO:
-      return TabStrip::Visibility::kAuto;
-    case proto::TabStrip_Visibility_ABSENT:
-      return TabStrip::Visibility::kAbsent;
-  }
-}
-
 std::string FilePathToProto(const base::FilePath& path) {
   base::Pickle pickle;
   path.WriteToPickle(&pickle);
@@ -772,12 +765,21 @@
     } else {
       auto* mutable_home_tab_params =
           mutable_tab_strip->mutable_home_tab_params();
-      absl::optional<std::vector<blink::Manifest::ImageResource>> icons =
+
+      const absl::optional<std::vector<blink::Manifest::ImageResource>>& icons =
           absl::get<blink::Manifest::HomeTabParams>(tab_strip.home_tab).icons;
       for (const auto& image_resource : *icons) {
         *(mutable_home_tab_params->add_icons()) =
             AppImageResourceToProto(image_resource);
       }
+
+      const std::vector<blink::UrlPattern>& scope_patterns =
+          absl::get<blink::Manifest::HomeTabParams>(tab_strip.home_tab)
+              .scope_patterns;
+      for (const auto& pattern : scope_patterns) {
+        *(mutable_home_tab_params->add_scope_patterns()) =
+            ToUrlPatternProto(pattern);
+      }
     }
 
     if (absl::holds_alternative<TabStrip::Visibility>(
@@ -1478,33 +1480,7 @@
   web_app->SetWebAppManagementExternalConfigMap(management_to_external_config);
 
   if (local_data.has_tab_strip()) {
-    TabStrip tab_strip;
-    if (local_data.tab_strip().has_home_tab_visibility()) {
-      tab_strip.home_tab = ProtoToTabStripVisibility(
-          local_data.tab_strip().home_tab_visibility());
-    } else {
-      absl::optional<std::vector<blink::Manifest::ImageResource>> icons =
-          ParseAppImageResource(
-              "WebApp", local_data.tab_strip().home_tab_params().icons());
-      blink::Manifest::HomeTabParams home_tab_params;
-      if (!icons->empty()) {
-        home_tab_params.icons = std::move(*icons);
-      }
-      tab_strip.home_tab = std::move(home_tab_params);
-    }
-
-    if (local_data.tab_strip().has_new_tab_button_visibility()) {
-      tab_strip.new_tab_button = ProtoToTabStripVisibility(
-          local_data.tab_strip().new_tab_button_visibility());
-    } else {
-      blink::Manifest::NewTabButtonParams new_tab_button_params;
-      if (local_data.tab_strip().new_tab_button_params().has_url()) {
-        new_tab_button_params.url =
-            GURL(local_data.tab_strip().new_tab_button_params().url());
-      }
-      tab_strip.new_tab_button = new_tab_button_params;
-    }
-    web_app->SetTabStrip(std::move(tab_strip));
+    web_app->SetTabStrip(ProtoToTabStrip(local_data.tab_strip()));
   }
 
   if (local_data.has_current_os_integration_states()) {
diff --git a/chrome/browser/web_applications/web_app_proto_utils.cc b/chrome/browser/web_applications/web_app_proto_utils.cc
index 4cb6695..7cd03d57 100644
--- a/chrome/browser/web_applications/web_app_proto_utils.cc
+++ b/chrome/browser/web_applications/web_app_proto_utils.cc
@@ -5,10 +5,12 @@
 #include "chrome/browser/web_applications/web_app_proto_utils.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
+#include "chrome/browser/web_applications/proto/web_app_url_pattern.pb.h"
 #include "chrome/browser/web_applications/user_display_mode.h"
 #include "components/services/app_service/public/cpp/icon_info.h"
 #include "third_party/blink/public/common/manifest/manifest.h"
 #include "third_party/blink/public/mojom/manifest/manifest.mojom.h"
+#include "third_party/liburlpattern/pattern.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace web_app {
@@ -56,6 +58,75 @@
   }
 }
 
+proto::UrlPatternPart::Modifier UrlPatternModifierToProto(
+    liburlpattern::Modifier modifier) {
+  switch (modifier) {
+    case liburlpattern::Modifier::kZeroOrMore:
+      return proto::UrlPatternPart_Modifier_ZERO_OR_MORE;
+    case liburlpattern::Modifier::kOptional:
+      return proto::UrlPatternPart_Modifier_OPTIONAL;
+    case liburlpattern::Modifier::kOneOrMore:
+      return proto::UrlPatternPart_Modifier_ONE_OR_MORE;
+    case liburlpattern::Modifier::kNone:
+      return proto::UrlPatternPart_Modifier_NONE;
+  }
+}
+
+absl::optional<liburlpattern::Modifier> ProtoToUrlPatternModifier(
+    proto::UrlPatternPart::Modifier modifier) {
+  switch (modifier) {
+    case proto::UrlPatternPart_Modifier_UNKNOWN_MODIFIER:
+      return absl::nullopt;
+    case proto::UrlPatternPart_Modifier_ZERO_OR_MORE:
+      return liburlpattern::Modifier::kZeroOrMore;
+    case proto::UrlPatternPart_Modifier_OPTIONAL:
+      return liburlpattern::Modifier::kOptional;
+    case proto::UrlPatternPart_Modifier_ONE_OR_MORE:
+      return liburlpattern::Modifier::kOneOrMore;
+    case proto::UrlPatternPart_Modifier_NONE:
+      return liburlpattern::Modifier::kNone;
+  }
+}
+
+proto::UrlPatternPart::PartType UrlPatternPartTypeToProto(
+    liburlpattern::PartType part_type) {
+  switch (part_type) {
+    case liburlpattern::PartType::kRegex:
+      NOTREACHED();
+      [[fallthrough]];
+    case liburlpattern::PartType::kFullWildcard:
+      return proto::UrlPatternPart_PartType_FULL_WILDCARD;
+    case liburlpattern::PartType::kSegmentWildcard:
+      return proto::UrlPatternPart_PartType_SEGMENT_WILDCARD;
+    case liburlpattern::PartType::kFixed:
+      return proto::UrlPatternPart_PartType_FIXED;
+  }
+}
+
+absl::optional<liburlpattern::PartType> ProtoToUrlPatternPartType(
+    proto::UrlPatternPart::PartType part_type) {
+  switch (part_type) {
+    case proto::UrlPatternPart_PartType_UNKNOWN_PART_TYPE:
+      return absl::nullopt;
+    case proto::UrlPatternPart_PartType_FULL_WILDCARD:
+      return liburlpattern::PartType::kFullWildcard;
+    case proto::UrlPatternPart_PartType_SEGMENT_WILDCARD:
+      return liburlpattern::PartType::kSegmentWildcard;
+    case proto::UrlPatternPart_PartType_FIXED:
+      return liburlpattern::PartType::kFixed;
+  }
+}
+
+TabStrip::Visibility ProtoToTabStripVisibility(
+    proto::TabStrip::Visibility visibility) {
+  switch (visibility) {
+    case proto::TabStrip_Visibility_AUTO:
+      return TabStrip::Visibility::kAuto;
+    case proto::TabStrip_Visibility_ABSENT:
+      return TabStrip::Visibility::kAbsent;
+  }
+}
+
 }  // namespace
 
 absl::optional<std::vector<apps::IconInfo>> ParseAppIconInfos(
@@ -297,4 +368,118 @@
   }
 }
 
+absl::optional<blink::UrlPattern> ToUrlPattern(
+    const proto::UrlPattern& proto_url_pattern) {
+  blink::UrlPattern url_pattern;
+
+  for (const proto::UrlPatternPart& proto_part : proto_url_pattern.pathname()) {
+    liburlpattern::Part part;
+
+    if (!proto_part.has_part_type()) {
+      DLOG(ERROR) << "WebApp UrlPattern Part has missing type";
+      continue;
+    }
+    absl::optional<liburlpattern::PartType> opt_part_type =
+        ProtoToUrlPatternPartType(proto_part.part_type());
+    if (!opt_part_type.has_value()) {
+      return absl::nullopt;
+    }
+    part.type = opt_part_type.value();
+
+    if (!proto_part.has_value()) {
+      DLOG(ERROR) << "WebApp UrlPattern Part has missing value";
+      continue;
+    }
+    part.value = proto_part.value();
+
+    if (!proto_part.has_modifier()) {
+      DLOG(ERROR) << "WebApp UrlPattern Part has missing type";
+      continue;
+    }
+
+    absl::optional<liburlpattern::Modifier> opt_modifier =
+        ProtoToUrlPatternModifier(proto_part.modifier());
+    if (!opt_modifier.has_value()) {
+      return absl::nullopt;
+    }
+    part.modifier = opt_modifier.value();
+
+    if (proto_part.has_name()) {
+      part.name = proto_part.name();
+    }
+
+    if (proto_part.has_prefix()) {
+      part.prefix = proto_part.prefix();
+    }
+
+    if (proto_part.has_suffix()) {
+      part.suffix = proto_part.suffix();
+    }
+
+    url_pattern.pathname.push_back(std::move(part));
+  }
+  return url_pattern;
+}
+
+proto::UrlPattern ToUrlPatternProto(const blink::UrlPattern& url_pattern) {
+  proto::UrlPattern url_pattern_proto;
+  for (const auto& part : url_pattern.pathname) {
+    proto::UrlPatternPart* url_pattern_part_proto =
+        url_pattern_proto.add_pathname();
+
+    url_pattern_part_proto->set_name(part.name);
+    url_pattern_part_proto->set_prefix(part.prefix);
+    url_pattern_part_proto->set_value(part.value);
+    url_pattern_part_proto->set_suffix(part.suffix);
+
+    url_pattern_part_proto->set_part_type(UrlPatternPartTypeToProto(part.type));
+    url_pattern_part_proto->set_modifier(
+        UrlPatternModifierToProto(part.modifier));
+  }
+  return url_pattern_proto;
+}
+
+absl::optional<TabStrip> ProtoToTabStrip(proto::TabStrip tab_strip_proto) {
+  TabStrip tab_strip;
+  if (tab_strip_proto.has_home_tab_visibility()) {
+    tab_strip.home_tab =
+        ProtoToTabStripVisibility(tab_strip_proto.home_tab_visibility());
+  } else {
+    absl::optional<std::vector<blink::Manifest::ImageResource>> icons =
+        ParseAppImageResource("WebApp",
+                              tab_strip_proto.home_tab_params().icons());
+    blink::Manifest::HomeTabParams home_tab_params;
+    if (!icons->empty()) {
+      home_tab_params.icons = std::move(*icons);
+    }
+
+    std::vector<blink::UrlPattern> scope_patterns;
+    for (const proto::UrlPattern& proto_url_pattern :
+         tab_strip_proto.home_tab_params().scope_patterns()) {
+      absl::optional<blink::UrlPattern> url_pattern =
+          ToUrlPattern(proto_url_pattern);
+      if (!url_pattern) {
+        return absl::nullopt;
+      }
+      scope_patterns.push_back(url_pattern.value());
+    }
+    home_tab_params.scope_patterns = std::move(scope_patterns);
+
+    tab_strip.home_tab = std::move(home_tab_params);
+  }
+
+  if (tab_strip_proto.has_new_tab_button_visibility()) {
+    tab_strip.new_tab_button =
+        ProtoToTabStripVisibility(tab_strip_proto.new_tab_button_visibility());
+  } else {
+    blink::Manifest::NewTabButtonParams new_tab_button_params;
+    if (tab_strip_proto.new_tab_button_params().has_url()) {
+      new_tab_button_params.url =
+          GURL(tab_strip_proto.new_tab_button_params().url());
+    }
+    tab_strip.new_tab_button = new_tab_button_params;
+  }
+  return tab_strip;
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_proto_utils.h b/chrome/browser/web_applications/web_app_proto_utils.h
index a07227e..70f01aa 100644
--- a/chrome/browser/web_applications/web_app_proto_utils.h
+++ b/chrome/browser/web_applications/web_app_proto_utils.h
@@ -8,10 +8,13 @@
 #include <vector>
 
 #include "chrome/browser/web_applications/proto/web_app.pb.h"
+#include "chrome/browser/web_applications/proto/web_app_tab_strip.pb.h"
+#include "chrome/browser/web_applications/proto/web_app_url_pattern.pb.h"
 #include "chrome/browser/web_applications/web_app.h"
 #include "components/sync/protocol/web_app_specifics.pb.h"
 #include "content/browser/background_fetch/background_fetch.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/url_pattern.h"
 
 namespace apps {
 struct IconInfo;
@@ -56,6 +59,13 @@
 WebAppProto::RunOnOsLoginMode ToWebAppProtoRunOnOsLoginMode(
     RunOnOsLoginMode mode);
 
+absl::optional<blink::UrlPattern> ToUrlPattern(
+    const proto::UrlPattern& proto_url_pattern);
+
+proto::UrlPattern ToUrlPatternProto(const blink::UrlPattern& url_pattern);
+
+absl::optional<TabStrip> ProtoToTabStrip(proto::TabStrip tab_strip_proto);
+
 }  // namespace web_app
 
 #endif  // CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_PROTO_UTILS_H_