WebApp: Introduce WebApp, WebAppRegistrar and InstallManager entities.

Introduce DesktopPWAsWithoutExtensions command line switch.

A WebApp represents single web app.
WebApp objects are owned by WebAppRegistrar.

WebAppRegistrar is a root entity which is able to add/remove new apps.
Later WebAppRegistrar will be able:
- to iterate over all the registered apps.
- to iterate over various subsets of all registered apps (shortcut apps)
- to survive the browser (and ChromeOS) relaunch (persistence)

InstallManager is an abstract manager to plumb 3-dot menu user installation.
We will evolve its WebAppInstallManager implementation
into comprehensive install manager later.

--enable-features=DesktopPWAsWithoutExtensions will enable off-extensions
implementation for Desktop PWAs.

Bug: 891172, 871116
Change-Id: Iebcbd93cf08eec0f11c7c22dce57ded81a2a2e94
Reviewed-on: https://chromium-review.googlesource.com/c/1275468
Reviewed-by: Ben Wells <[email protected]>
Reviewed-by: Trent Apted <[email protected]>
Commit-Queue: Alexey Baskakov <[email protected]>
Cr-Commit-Position: refs/heads/master@{#599887}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index e2701d5..ae9a346 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3970,6 +3970,9 @@
 
       "//chrome/browser/extensions",
       "//chrome/browser/web_applications",
+
+      # TODO(loyso): Erase these. crbug.com/877898.
+      "//chrome/browser/web_applications:web_applications_on_extensions",
       "//chrome/browser/web_applications/bookmark_apps",
       "//chrome/browser/web_applications/components",
       "//chrome/browser/web_applications/extensions",
@@ -3978,6 +3981,7 @@
       "//apps",
       "//chrome/browser/sync_file_system/drive_backend:sync_file_system_drive_proto",
       "//chrome/browser/web_applications",
+      "//chrome/browser/web_applications:web_applications_on_extensions",
       "//chrome/browser/web_applications/bookmark_apps",
       "//chrome/browser/web_applications/components",
       "//chrome/browser/web_applications/extensions",
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 873b29ac..cadddbf 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -69,7 +69,7 @@
     "//chrome/browser/extensions",
     "//chrome/browser/resource_coordinator:tab_metrics_event_proto",
     "//chrome/browser/ssl:proto",
-    "//chrome/browser/web_applications",
+    "//chrome/browser/web_applications:web_applications_on_extensions",
     "//chrome/browser/web_applications/bookmark_apps",
     "//chrome/browser/web_applications/components",
     "//chrome/common",
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 67eab0a..cbeab15f 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1258,6 +1258,10 @@
 
     if (enable_extensions) {
       deps += [
+        "//chrome/browser/web_applications",
+
+        # TODO(loyso): Erase these. crbug.com/877898.
+        "//chrome/browser/web_applications:web_applications_on_extensions",
         "//chrome/browser/web_applications/components",
         "//chrome/browser/web_applications/extensions",
       ]
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc
index 1b80948..fe98ecab 100644
--- a/chrome/browser/ui/browser_commands.cc
+++ b/chrome/browser/ui/browser_commands.cc
@@ -99,9 +99,9 @@
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/api/commands/command_service.h"
 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
-#include "chrome/browser/extensions/tab_helper.h"
 #include "chrome/browser/ui/extensions/settings_api_bubble_helpers.h"
 #include "chrome/browser/web_applications/components/web_app_helpers.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/common/extensions/extension_metrics.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "extensions/browser/extension_registry.h"
@@ -1256,15 +1256,13 @@
 void CreateBookmarkAppFromCurrentWebContents(Browser* browser,
                                              bool force_shortcut_app) {
   base::RecordAction(UserMetricsAction("CreateHostedApp"));
-  extensions::TabHelper::FromWebContents(
-      browser->tab_strip_model()->GetActiveWebContents())
-      ->CreateHostedAppFromWebContents(force_shortcut_app);
+  web_app::WebAppProvider::InstallWebApp(
+      browser->tab_strip_model()->GetActiveWebContents(), force_shortcut_app);
 }
 
 bool CanCreateBookmarkApp(const Browser* browser) {
-  return extensions::TabHelper::FromWebContents(
-             browser->tab_strip_model()->GetActiveWebContents())
-      ->CanCreateBookmarkApp();
+  return web_app::WebAppProvider::CanInstallWebApp(
+      browser->tab_strip_model()->GetActiveWebContents());
 }
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn
index 621fc64..509d0e8 100644
--- a/chrome/browser/web_applications/BUILD.gn
+++ b/chrome/browser/web_applications/BUILD.gn
@@ -8,6 +8,71 @@
 
 source_set("web_applications") {
   sources = [
+    "web_app.cc",
+    "web_app.h",
+    "web_app_install_manager.cc",
+    "web_app_install_manager.h",
+    "web_app_registrar.cc",
+    "web_app_registrar.h",
+    "web_app_utils.cc",
+    "web_app_utils.h",
+  ]
+
+  deps = [
+    ":web_app_group",
+    "//chrome/browser/web_applications/components",
+    "//chrome/common",
+    "//content/public/browser",
+    "//skia",
+  ]
+}
+
+source_set("web_applications_test_support") {
+  testonly = true
+
+  sources = [
+    "test/test_data_retriever.cc",
+    "test/test_data_retriever.h",
+    "test/web_app_test.cc",
+    "test/web_app_test.h",
+  ]
+
+  deps = [
+    ":web_app_group",
+    ":web_applications",
+    "//chrome/browser/web_applications/components",
+    "//chrome/test:test_support",
+    "//testing/gtest",
+  ]
+}
+
+source_set("web_applications_unit_tests") {
+  testonly = true
+
+  sources = [
+    "web_app_install_manager_unittest.cc",
+    "web_app_registrar_unittest.cc",
+  ]
+
+  deps = [
+    ":web_app_group",
+    ":web_applications",
+    ":web_applications_test_support",
+    "//base/test:test_support",
+    "//chrome/browser",
+    "//chrome/browser/web_applications/components",
+    "//chrome/common",
+    "//chrome/test:test_support",
+    "//content/public/browser",
+    "//content/test:test_support",
+    "//skia",
+  ]
+}
+
+# TODO(loyso): Erase this and move WebAppProvider into web_applications set.
+# crbug.com/877898
+source_set("web_applications_on_extensions") {
+  sources = [
     "web_app_provider.cc",
     "web_app_provider.h",
     "web_app_provider_factory.cc",
@@ -16,6 +81,7 @@
 
   deps = [
     ":web_app_group",
+    ":web_applications",
     "//chrome/browser/web_applications/bookmark_apps",
     "//chrome/browser/web_applications/components",
     "//chrome/browser/web_applications/extensions",
@@ -30,6 +96,7 @@
 
   deps = [
     ":web_app_group",
+    ":web_applications_unit_tests",
     "//chrome/browser/web_applications/bookmark_apps:unit_tests",
     "//chrome/browser/web_applications/components:unit_tests",
     "//chrome/browser/web_applications/extensions:unit_tests",
diff --git a/chrome/browser/web_applications/bookmark_apps/BUILD.gn b/chrome/browser/web_applications/bookmark_apps/BUILD.gn
index 8cb2b08d..a6fc8cf 100644
--- a/chrome/browser/web_applications/bookmark_apps/BUILD.gn
+++ b/chrome/browser/web_applications/bookmark_apps/BUILD.gn
@@ -8,6 +8,8 @@
 
 source_set("bookmark_apps") {
   sources = [
+    "bookmark_app_install_manager.cc",
+    "bookmark_app_install_manager.h",
     "external_web_apps.cc",
     "external_web_apps.h",
     "policy/web_app_policy_constants.cc",
@@ -44,7 +46,7 @@
     "//base",
     "//chrome/browser",
     "//chrome/browser/web_applications:web_app_group",
-    "//chrome/browser/web_applications:web_applications",
+    "//chrome/browser/web_applications:web_applications_on_extensions",
     "//chrome/browser/web_applications/components",
     "//chrome/browser/web_applications/components:test_support",
     "//chrome/browser/web_applications/extensions",
diff --git a/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.cc b/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.cc
new file mode 100644
index 0000000..db8b379
--- /dev/null
+++ b/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.cc
@@ -0,0 +1,30 @@
+// 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 "chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.h"
+
+#include "chrome/browser/extensions/tab_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/web_contents.h"
+
+namespace extensions {
+
+BookmarkAppInstallManager::BookmarkAppInstallManager() = default;
+
+BookmarkAppInstallManager::~BookmarkAppInstallManager() = default;
+
+bool BookmarkAppInstallManager::CanInstallWebApp(
+    const content::WebContents* web_contents) {
+  return extensions::TabHelper::FromWebContents(web_contents)
+      ->CanCreateBookmarkApp();
+}
+
+void BookmarkAppInstallManager::InstallWebApp(
+    content::WebContents* web_contents,
+    bool force_shortcut_app) {
+  extensions::TabHelper::FromWebContents(web_contents)
+      ->CreateHostedAppFromWebContents(force_shortcut_app);
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.h b/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.h
new file mode 100644
index 0000000..2b033414
--- /dev/null
+++ b/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.h
@@ -0,0 +1,29 @@
+// 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.
+
+#ifndef CHROME_BROWSER_WEB_APPLICATIONS_BOOKMARK_APPS_BOOKMARK_APP_INSTALL_MANAGER_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_BOOKMARK_APPS_BOOKMARK_APP_INSTALL_MANAGER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/web_applications/components/install_manager.h"
+
+namespace extensions {
+
+class BookmarkAppInstallManager final : public web_app::InstallManager {
+ public:
+  BookmarkAppInstallManager();
+  ~BookmarkAppInstallManager() override;
+
+  // InstallManager interface implementation.
+  bool CanInstallWebApp(const content::WebContents* web_contents) override;
+  void InstallWebApp(content::WebContents* web_contents,
+                     bool force_shortcut_app) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BookmarkAppInstallManager);
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_BOOKMARK_APPS_BOOKMARK_APP_INSTALL_MANAGER_H_
diff --git a/chrome/browser/web_applications/components/BUILD.gn b/chrome/browser/web_applications/components/BUILD.gn
index 584f6b5..16d3fd3 100644
--- a/chrome/browser/web_applications/components/BUILD.gn
+++ b/chrome/browser/web_applications/components/BUILD.gn
@@ -4,6 +4,7 @@
 
 source_set("components") {
   sources = [
+    "install_manager.h",
     "install_result_code.h",
     "pending_app_manager.cc",
     "pending_app_manager.h",
diff --git a/chrome/browser/web_applications/components/install_manager.h b/chrome/browser/web_applications/components/install_manager.h
new file mode 100644
index 0000000..7b70fa21
--- /dev/null
+++ b/chrome/browser/web_applications/components/install_manager.h
@@ -0,0 +1,28 @@
+// 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.
+
+#ifndef CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_INSTALL_MANAGER_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_INSTALL_MANAGER_H_
+
+namespace content {
+class WebContents;
+}
+
+namespace web_app {
+
+class InstallManager {
+ public:
+  // Returns true if a web app can be installed for a given |web_contents|.
+  virtual bool CanInstallWebApp(const content::WebContents* web_contents) = 0;
+
+  // Starts a web app installation process for a given |web_contents|.
+  virtual void InstallWebApp(content::WebContents* web_contents,
+                             bool force_shortcut_app) = 0;
+
+  virtual ~InstallManager() = default;
+};
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_INSTALL_MANAGER_H_
diff --git a/chrome/browser/web_applications/components/install_result_code.h b/chrome/browser/web_applications/components/install_result_code.h
index b557fa6..33616d6 100644
--- a/chrome/browser/web_applications/components/install_result_code.h
+++ b/chrome/browser/web_applications/components/install_result_code.h
@@ -19,6 +19,7 @@
   kAlreadyInstalled,
   // Catch-all failure category. More-specific failure categories are below.
   kFailedUnknownReason,
+  kGetWebApplicationInfoFailed,
   kPreviouslyUninstalled,
 };
 
diff --git a/chrome/browser/web_applications/extensions/BUILD.gn b/chrome/browser/web_applications/extensions/BUILD.gn
index 689ffbe..920160c 100644
--- a/chrome/browser/web_applications/extensions/BUILD.gn
+++ b/chrome/browser/web_applications/extensions/BUILD.gn
@@ -42,9 +42,6 @@
   testonly = true
 
   sources = [
-    # TODO(loyso): Extract it into web_applications_test_support set.
-    "../test/test_data_retriever.cc",
-    "../test/test_data_retriever.h",
     "bookmark_app_installation_task_unittest.cc",
     "bookmark_app_installer_unittest.cc",
     "pending_bookmark_app_manager_unittest.cc",
@@ -55,6 +52,7 @@
     ":extensions",
     "//chrome/browser",
     "//chrome/browser/web_applications:web_app_group",
+    "//chrome/browser/web_applications:web_applications_test_support",
     "//chrome/browser/web_applications/components",
     "//chrome/common",
     "//chrome/test:test_support",
@@ -82,8 +80,8 @@
     "//base/test:test_support",
     "//chrome/browser",
     "//chrome/browser/ui",
-    "//chrome/browser/web_applications",
     "//chrome/browser/web_applications:web_app_group",
+    "//chrome/browser/web_applications:web_applications_on_extensions",
     "//chrome/browser/web_applications/components",
     "//chrome/test:test_support_ui",
     "//extensions/browser",
diff --git a/chrome/browser/web_applications/test/web_app_test.cc b/chrome/browser/web_applications/test/web_app_test.cc
new file mode 100644
index 0000000..d1c0140
--- /dev/null
+++ b/chrome/browser/web_applications/test/web_app_test.cc
@@ -0,0 +1,13 @@
+// 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 "chrome/browser/web_applications/test/web_app_test.h"
+
+namespace web_app {
+
+WebAppTest::WebAppTest() = default;
+
+WebAppTest::~WebAppTest() = default;
+
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/test/web_app_test.h b/chrome/browser/web_applications/test/web_app_test.h
new file mode 100644
index 0000000..d96606f
--- /dev/null
+++ b/chrome/browser/web_applications/test/web_app_test.h
@@ -0,0 +1,24 @@
+// Copyright (c) 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.
+
+#ifndef CHROME_BROWSER_WEB_APPLICATIONS_TEST_WEB_APP_TEST_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_TEST_WEB_APP_TEST_H_
+
+#include "base/macros.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+
+namespace web_app {
+
+class WebAppTest : public ChromeRenderViewHostTestHarness {
+ public:
+  WebAppTest();
+  ~WebAppTest() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(WebAppTest);
+};
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_TEST_WEB_APP_TEST_H_
diff --git a/chrome/browser/web_applications/web_app.cc b/chrome/browser/web_applications/web_app.cc
new file mode 100644
index 0000000..dd2e1a20
--- /dev/null
+++ b/chrome/browser/web_applications/web_app.cc
@@ -0,0 +1,25 @@
+// 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 "chrome/browser/web_applications/web_app.h"
+
+namespace web_app {
+
+WebApp::WebApp(const AppId& app_id) : app_id_(app_id) {}
+
+WebApp::~WebApp() = default;
+
+void WebApp::SetName(const std::string& name) {
+  name_ = name;
+}
+
+void WebApp::SetDescription(const std::string& description) {
+  description_ = description;
+}
+
+void WebApp::SetLaunchUrl(const std::string& launch_url) {
+  launch_url_ = launch_url;
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app.h b/chrome/browser/web_applications/web_app.h
new file mode 100644
index 0000000..cc592a2b
--- /dev/null
+++ b/chrome/browser/web_applications/web_app.h
@@ -0,0 +1,42 @@
+// 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.
+
+#ifndef CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "chrome/browser/web_applications/components/web_app_helpers.h"
+
+namespace web_app {
+
+class WebApp {
+ public:
+  explicit WebApp(const AppId& app_id);
+  ~WebApp();
+
+  const AppId& app_id() const { return app_id_; }
+
+  const std::string& name() const { return name_; }
+  const std::string& description() const { return description_; }
+  const std::string& launch_url() const { return launch_url_; }
+
+  void SetName(const std::string& name);
+  void SetDescription(const std::string& description);
+  void SetLaunchUrl(const std::string& launch_url);
+
+ private:
+  const AppId app_id_;
+
+  std::string name_;
+  std::string description_;
+  std::string launch_url_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebApp);
+};
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_H_
diff --git a/chrome/browser/web_applications/web_app_install_manager.cc b/chrome/browser/web_applications/web_app_install_manager.cc
new file mode 100644
index 0000000..ebf7b31
--- /dev/null
+++ b/chrome/browser/web_applications/web_app_install_manager.cc
@@ -0,0 +1,96 @@
+// 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 "chrome/browser/web_applications/web_app_install_manager.h"
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/web_applications/components/install_result_code.h"
+#include "chrome/browser/web_applications/components/web_app_data_retriever.h"
+#include "chrome/browser/web_applications/components/web_app_helpers.h"
+#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/web_app_registrar.h"
+#include "chrome/browser/web_applications/web_app_utils.h"
+#include "chrome/common/web_application_info.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/web_contents.h"
+
+namespace web_app {
+
+WebAppInstallManager::WebAppInstallManager(Profile* profile,
+                                           WebAppRegistrar* registrar)
+    : profile_(profile),
+      registrar_(registrar),
+      data_retriever_(std::make_unique<WebAppDataRetriever>()) {
+  DCHECK(AllowWebAppInstallation(profile_));
+}
+
+WebAppInstallManager::~WebAppInstallManager() = default;
+
+bool WebAppInstallManager::CanInstallWebApp(
+    const content::WebContents* web_contents) {
+  return IsValidWebAppUrl(web_contents->GetURL());
+}
+
+void WebAppInstallManager::InstallWebApp(content::WebContents* web_contents,
+                                         bool force_shortcut_app) {
+  // TODO(loyso): Use force_shortcut_app flag during installation.
+  InstallFromWebContents(web_contents, base::DoNothing());
+}
+
+void WebAppInstallManager::SetDataRetrieverForTesting(
+    std::unique_ptr<WebAppDataRetriever> data_retriever) {
+  data_retriever_ = std::move(data_retriever);
+}
+
+void WebAppInstallManager::InstallWebAppForTesting(
+    content::WebContents* web_contents,
+    OnceInstallCallback callback) {
+  InstallFromWebContents(web_contents, std::move(callback));
+}
+
+void WebAppInstallManager::InstallFromWebContents(
+    content::WebContents* web_contents,
+    OnceInstallCallback install_callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  data_retriever_->GetWebApplicationInfo(
+      web_contents,
+      base::BindOnce(&WebAppInstallManager::OnGetWebApplicationInfo,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     std::move(install_callback)));
+}
+
+void WebAppInstallManager::OnGetWebApplicationInfo(
+    OnceInstallCallback install_callback,
+    std::unique_ptr<WebApplicationInfo> web_app_info) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  if (!web_app_info) {
+    std::move(install_callback)
+        .Run(AppId(), InstallResultCode::kGetWebApplicationInfoFailed);
+    return;
+  }
+
+  // TODO(loyso): Implement installation logic from BookmarkAppHelper:
+  // - InstallableManager to get InstallableData.
+  // - UpdateWebAppInfoFromManifest.
+  // - UpdateShareTargetInPrefs.
+  // - WebAppIconDownloader.
+  // etc
+
+  const AppId app_id = GenerateAppIdFromURL(web_app_info->app_url);
+  auto web_app = std::make_unique<WebApp>(app_id);
+
+  web_app->SetName(base::UTF16ToUTF8(web_app_info->title));
+  web_app->SetDescription(base::UTF16ToUTF8(web_app_info->description));
+  web_app->SetLaunchUrl(web_app_info->app_url.spec());
+
+  registrar_->RegisterApp(std::move(web_app));
+
+  std::move(install_callback).Run(app_id, InstallResultCode::kSuccess);
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_install_manager.h b/chrome/browser/web_applications/web_app_install_manager.h
new file mode 100644
index 0000000..7f8dfd9f
--- /dev/null
+++ b/chrome/browser/web_applications/web_app_install_manager.h
@@ -0,0 +1,60 @@
+// 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.
+
+#ifndef CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_INSTALL_MANAGER_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_INSTALL_MANAGER_H_
+
+#include "base/callback_forward.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/web_applications/components/install_manager.h"
+#include "chrome/browser/web_applications/components/web_app_helpers.h"
+
+class Profile;
+struct WebApplicationInfo;
+
+namespace web_app {
+
+enum class InstallResultCode;
+
+class WebAppDataRetriever;
+class WebAppRegistrar;
+
+class WebAppInstallManager final : public InstallManager {
+ public:
+  WebAppInstallManager(Profile* profile, WebAppRegistrar* registrar);
+  ~WebAppInstallManager() override;
+
+  using OnceInstallCallback =
+      base::OnceCallback<void(const AppId& app_id, InstallResultCode code)>;
+
+  // InstallManager interface implementation.
+  bool CanInstallWebApp(const content::WebContents* web_contents) override;
+  void InstallWebApp(content::WebContents* web_contents,
+                     bool force_shortcut_app) override;
+
+  void SetDataRetrieverForTesting(
+      std::unique_ptr<WebAppDataRetriever> data_retriever);
+
+  void InstallWebAppForTesting(content::WebContents* web_contents,
+                               OnceInstallCallback callback);
+
+ private:
+  void InstallFromWebContents(content::WebContents* web_contents,
+                              OnceInstallCallback callback);
+
+  void OnGetWebApplicationInfo(
+      OnceInstallCallback install_callback,
+      std::unique_ptr<WebApplicationInfo> web_app_info);
+
+  Profile* profile_;
+  WebAppRegistrar* registrar_;
+  std::unique_ptr<WebAppDataRetriever> data_retriever_;
+
+  base::WeakPtrFactory<WebAppInstallManager> weak_ptr_factory_{this};
+  DISALLOW_COPY_AND_ASSIGN(WebAppInstallManager);
+};
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_INSTALL_MANAGER_H_
diff --git a/chrome/browser/web_applications/web_app_install_manager_unittest.cc b/chrome/browser/web_applications/web_app_install_manager_unittest.cc
new file mode 100644
index 0000000..3dcb48b
--- /dev/null
+++ b/chrome/browser/web_applications/web_app_install_manager_unittest.cc
@@ -0,0 +1,98 @@
+// 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 "chrome/browser/web_applications/web_app_install_manager.h"
+
+#include "base/callback.h"
+#include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/bind_test_util.h"
+#include "chrome/browser/web_applications/components/install_result_code.h"
+#include "chrome/browser/web_applications/test/test_data_retriever.h"
+#include "chrome/browser/web_applications/test/web_app_test.h"
+#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/web_app_registrar.h"
+#include "chrome/browser/web_applications/web_app_utils.h"
+#include "chrome/test/base/testing_profile.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace web_app {
+
+class WebAppInstallManagerTest : public WebAppTest {};
+
+TEST_F(WebAppInstallManagerTest, InstallFromWebContents) {
+  EXPECT_EQ(true, AllowWebAppInstallation(profile()));
+
+  auto registrar = std::make_unique<WebAppRegistrar>();
+  auto manager =
+      std::make_unique<WebAppInstallManager>(profile(), registrar.get());
+
+  auto web_app_info = std::make_unique<WebApplicationInfo>();
+
+  const GURL url = GURL("https://example.com/path");
+  const std::string name = "Name";
+  const std::string description = "Description";
+
+  const AppId app_id = GenerateAppIdFromURL(url);
+
+  web_app_info->app_url = url;
+  web_app_info->title = base::UTF8ToUTF16(name);
+  web_app_info->description = base::UTF8ToUTF16(description);
+  manager->SetDataRetrieverForTesting(
+      std::make_unique<TestDataRetriever>(std::move(web_app_info)));
+
+  base::RunLoop run_loop;
+  bool callback_called = false;
+
+  manager->InstallWebAppForTesting(
+      web_contents(),
+      base::BindLambdaForTesting(
+          [&](const AppId& installed_app_id, InstallResultCode code) {
+            EXPECT_EQ(InstallResultCode::kSuccess, code);
+            EXPECT_EQ(app_id, installed_app_id);
+            callback_called = true;
+            base::ThreadTaskRunnerHandle::Get()->PostTask(
+                FROM_HERE, run_loop.QuitClosure());
+          }));
+  run_loop.Run();
+
+  EXPECT_TRUE(callback_called);
+
+  WebApp* web_app = registrar->GetAppById(app_id);
+  EXPECT_NE(nullptr, web_app);
+
+  EXPECT_EQ(app_id, web_app->app_id());
+  EXPECT_EQ(name, web_app->name());
+  EXPECT_EQ(description, web_app->description());
+  EXPECT_EQ(url.spec(), web_app->launch_url());
+}
+
+TEST_F(WebAppInstallManagerTest, GetWebApplicationInfoFailed) {
+  auto registrar = std::make_unique<WebAppRegistrar>();
+  auto manager =
+      std::make_unique<WebAppInstallManager>(profile(), registrar.get());
+
+  manager->SetDataRetrieverForTesting(std::make_unique<TestDataRetriever>(
+      std::unique_ptr<WebApplicationInfo>()));
+
+  base::RunLoop run_loop;
+  bool callback_called = false;
+
+  manager->InstallWebAppForTesting(
+      web_contents(),
+      base::BindLambdaForTesting(
+          [&](const AppId& installed_app_id, InstallResultCode code) {
+            EXPECT_EQ(InstallResultCode::kGetWebApplicationInfoFailed, code);
+            EXPECT_EQ(AppId(), installed_app_id);
+            callback_called = true;
+            base::ThreadTaskRunnerHandle::Get()->PostTask(
+                FROM_HERE, run_loop.QuitClosure());
+          }));
+  run_loop.Run();
+
+  EXPECT_TRUE(callback_called);
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_provider.cc b/chrome/browser/web_applications/web_app_provider.cc
index 025b758..e407472 100644
--- a/chrome/browser/web_applications/web_app_provider.cc
+++ b/chrome/browser/web_applications/web_app_provider.cc
@@ -7,16 +7,25 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/feature_list.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.h"
 #include "chrome/browser/web_applications/bookmark_apps/external_web_apps.h"
 #include "chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager.h"
 #include "chrome/browser/web_applications/bookmark_apps/system_web_app_manager.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
+#include "chrome/browser/web_applications/components/web_app_helpers.h"
 #include "chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h"
 #include "chrome/browser/web_applications/extensions/web_app_extension_ids_map.h"
+#include "chrome/browser/web_applications/web_app_install_manager.h"
 #include "chrome/browser/web_applications/web_app_provider_factory.h"
+#include "chrome/browser/web_applications/web_app_registrar.h"
+#include "chrome/browser/web_applications/web_app_utils.h"
+#include "chrome/common/chrome_features.h"
 #include "content/public/browser/notification_source.h"
+#include "content/public/browser/web_contents.h"
 
 namespace web_app {
 
@@ -25,9 +34,39 @@
   return WebAppProviderFactory::GetForProfile(profile);
 }
 
-WebAppProvider::WebAppProvider(Profile* profile)
-    : pending_app_manager_(
-          std::make_unique<extensions::PendingBookmarkAppManager>(profile)) {
+// static
+WebAppProvider* WebAppProvider::GetForWebContents(
+    const content::WebContents* web_contents) {
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents->GetBrowserContext());
+  DCHECK(profile);
+  return WebAppProvider::Get(profile);
+}
+
+WebAppProvider::WebAppProvider(Profile* profile) {
+  if (base::FeatureList::IsEnabled(features::kDesktopPWAsWithoutExtensions))
+    CreateWebAppsSubsystems(profile);
+  else
+    CreateBookmarkAppsSubsystems(profile);
+}
+
+WebAppProvider::~WebAppProvider() = default;
+
+void WebAppProvider::CreateWebAppsSubsystems(Profile* profile) {
+  if (!AllowWebAppInstallation(profile))
+    return;
+
+  registrar_ = std::make_unique<WebAppRegistrar>();
+  install_manager_ =
+      std::make_unique<WebAppInstallManager>(profile, registrar_.get());
+}
+
+void WebAppProvider::CreateBookmarkAppsSubsystems(Profile* profile) {
+  install_manager_ = std::make_unique<extensions::BookmarkAppInstallManager>();
+
+  pending_app_manager_ =
+      std::make_unique<extensions::PendingBookmarkAppManager>(profile);
+
   if (WebAppPolicyManager::ShouldEnableForProfile(profile)) {
     web_app_policy_manager_ = std::make_unique<WebAppPolicyManager>(
         profile, pending_app_manager_.get());
@@ -44,8 +83,6 @@
                               weak_ptr_factory_.GetWeakPtr()));
 }
 
-WebAppProvider::~WebAppProvider() = default;
-
 // static
 void WebAppProvider::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
@@ -53,12 +90,38 @@
   WebAppPolicyManager::RegisterProfilePrefs(registry);
 }
 
+// static
+bool WebAppProvider::CanInstallWebApp(
+    const content::WebContents* web_contents) {
+  auto* provider = WebAppProvider::GetForWebContents(web_contents);
+  if (!provider || !provider->install_manager_)
+    return false;
+  return provider->install_manager_->CanInstallWebApp(web_contents);
+}
+
+// static
+void WebAppProvider::InstallWebApp(content::WebContents* web_contents,
+                                   bool force_shortcut_app) {
+  auto* provider = WebAppProvider::GetForWebContents(web_contents);
+  if (!provider || !provider->install_manager_)
+    return;
+  provider->install_manager_->InstallWebApp(web_contents, force_shortcut_app);
+}
+
 void WebAppProvider::Reset() {
+  // TODO(loyso): Make it independent to the order of destruction via using two
+  // end-to-end passes:
+  // 1) Do Reset() for each subsystem to nullify pointers (detach subsystems).
+  // 2) Destroy subsystems.
+
   // PendingAppManager is used by WebAppPolicyManager and therefore should be
   // deleted after it.
   web_app_policy_manager_.reset();
   system_web_app_manager_.reset();
   pending_app_manager_.reset();
+
+  install_manager_.reset();
+  registrar_.reset();
 }
 
 void WebAppProvider::Observe(int type,
diff --git a/chrome/browser/web_applications/web_app_provider.h b/chrome/browser/web_applications/web_app_provider.h
index fc10a45..9c5ef7d 100644
--- a/chrome/browser/web_applications/web_app_provider.h
+++ b/chrome/browser/web_applications/web_app_provider.h
@@ -17,13 +17,24 @@
 
 class Profile;
 
+namespace content {
+class WebContents;
+}
+
 namespace user_prefs {
 class PrefRegistrySyncable;
 }
 
 namespace web_app {
 
+// Forward declarations of generalized interfaces.
 class PendingAppManager;
+class InstallManager;
+
+// Forward declarations for new extension-independent subsystems.
+class WebAppRegistrar;
+
+// Forward declarations for legacy extension-based subsystems.
 class WebAppPolicyManager;
 class SystemWebAppManager;
 
@@ -34,6 +45,8 @@
                        public content::NotificationObserver {
  public:
   static WebAppProvider* Get(Profile* profile);
+  static WebAppProvider* GetForWebContents(
+      const content::WebContents* web_contents);
 
   explicit WebAppProvider(Profile* profile);
 
@@ -45,6 +58,13 @@
 
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
 
+  // Returns true if a bookmark can be installed for a given |web_contents|.
+  static bool CanInstallWebApp(const content::WebContents* web_contents);
+
+  // Starts a bookmark installation process for a given |web_contents|.
+  static void InstallWebApp(content::WebContents* web_contents,
+                            bool force_shortcut_app);
+
   void Reset();
 
   // content::NotificationObserver
@@ -53,10 +73,22 @@
                const content::NotificationDetails& details) override;
 
  private:
+  // Create extension-independent subsystems.
+  void CreateWebAppsSubsystems(Profile* profile);
+  // ... or create legacy extension-based subsystems.
+  void CreateBookmarkAppsSubsystems(Profile* profile);
+
   void OnScanForExternalWebApps(
       std::vector<web_app::PendingAppManager::AppInfo>);
 
+  // New extension-independent subsystems:
+  std::unique_ptr<WebAppRegistrar> registrar_;
+
+  // New generalized subsystems:
+  std::unique_ptr<InstallManager> install_manager_;
   std::unique_ptr<PendingAppManager> pending_app_manager_;
+
+  // Legacy extension-based subsystems:
   std::unique_ptr<WebAppPolicyManager> web_app_policy_manager_;
   std::unique_ptr<SystemWebAppManager> system_web_app_manager_;
 
diff --git a/chrome/browser/web_applications/web_app_registrar.cc b/chrome/browser/web_applications/web_app_registrar.cc
new file mode 100644
index 0000000..47631b9
--- /dev/null
+++ b/chrome/browser/web_applications/web_app_registrar.cc
@@ -0,0 +1,40 @@
+// 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 "chrome/browser/web_applications/web_app_registrar.h"
+
+#include "base/logging.h"
+#include "chrome/browser/web_applications/web_app.h"
+
+namespace web_app {
+
+WebAppRegistrar::WebAppRegistrar() {}
+
+WebAppRegistrar::~WebAppRegistrar() = default;
+
+void WebAppRegistrar::RegisterApp(std::unique_ptr<WebApp> web_app) {
+  const auto app_id = web_app->app_id();
+  DCHECK(!app_id.empty());
+  DCHECK(!GetAppById(app_id));
+
+  registry_.emplace(std::make_pair(app_id, std::move(web_app)));
+}
+
+std::unique_ptr<WebApp> WebAppRegistrar::UnregisterApp(const AppId& app_id) {
+  DCHECK(!app_id.empty());
+
+  auto kv = registry_.find(app_id);
+  DCHECK(kv != registry_.end());
+
+  auto web_app = std::move(kv->second);
+  registry_.erase(kv);
+  return web_app;
+}
+
+WebApp* WebAppRegistrar::GetAppById(const AppId& app_id) {
+  auto kv = registry_.find(app_id);
+  return kv == registry_.end() ? nullptr : kv->second.get();
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_registrar.h b/chrome/browser/web_applications/web_app_registrar.h
new file mode 100644
index 0000000..baa57469
--- /dev/null
+++ b/chrome/browser/web_applications/web_app_registrar.h
@@ -0,0 +1,36 @@
+// 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.
+
+#ifndef CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_REGISTRAR_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_REGISTRAR_H_
+
+#include <map>
+#include <memory>
+
+#include "base/macros.h"
+#include "chrome/browser/web_applications/components/web_app_helpers.h"
+
+namespace web_app {
+
+class WebApp;
+
+class WebAppRegistrar {
+ public:
+  WebAppRegistrar();
+  ~WebAppRegistrar();
+
+  void RegisterApp(std::unique_ptr<WebApp> web_app);
+  std::unique_ptr<WebApp> UnregisterApp(const AppId& app_id);
+
+  WebApp* GetAppById(const AppId& app_id);
+
+ private:
+  std::map<AppId, std::unique_ptr<WebApp>> registry_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebAppRegistrar);
+};
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_REGISTRAR_H_
diff --git a/chrome/browser/web_applications/web_app_registrar_unittest.cc b/chrome/browser/web_applications/web_app_registrar_unittest.cc
new file mode 100644
index 0000000..7791528
--- /dev/null
+++ b/chrome/browser/web_applications/web_app_registrar_unittest.cc
@@ -0,0 +1,76 @@
+// 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 "chrome/browser/web_applications/web_app_registrar.h"
+
+#include "chrome/browser/web_applications/components/web_app_helpers.h"
+#include "chrome/browser/web_applications/web_app.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace web_app {
+
+TEST(WebAppRegistrar, CreateRegisterUnregister) {
+  auto registrar = std::make_unique<WebAppRegistrar>();
+  EXPECT_EQ(nullptr, registrar->GetAppById(AppId()));
+
+  const GURL launch_url = GURL("https://example.com/path");
+  const AppId app_id = GenerateAppIdFromURL(launch_url);
+  const std::string name = "Name";
+  const std::string description = "Description";
+
+  const GURL launch_url2 = GURL("https://example.com/path2");
+  const AppId app_id2 = GenerateAppIdFromURL(launch_url2);
+
+  auto web_app = std::make_unique<WebApp>(app_id);
+  auto web_app2 = std::make_unique<WebApp>(app_id2);
+
+  web_app->SetName(name);
+  web_app->SetDescription(description);
+  web_app->SetLaunchUrl(launch_url.spec());
+
+  EXPECT_EQ(nullptr, registrar->GetAppById(app_id));
+  EXPECT_EQ(nullptr, registrar->GetAppById(app_id2));
+
+  registrar->RegisterApp(std::move(web_app));
+  WebApp* app = registrar->GetAppById(app_id);
+
+  EXPECT_EQ(app_id, app->app_id());
+  EXPECT_EQ(name, app->name());
+  EXPECT_EQ(description, app->description());
+  EXPECT_EQ(launch_url.spec(), app->launch_url());
+
+  EXPECT_EQ(nullptr, registrar->GetAppById(app_id2));
+
+  registrar->RegisterApp(std::move(web_app2));
+  WebApp* app2 = registrar->GetAppById(app_id2);
+  EXPECT_EQ(app_id2, app2->app_id());
+
+  registrar->UnregisterApp(app_id);
+  EXPECT_EQ(nullptr, registrar->GetAppById(app_id));
+
+  // Check that app2 is still registered.
+  app2 = registrar->GetAppById(app_id2);
+  EXPECT_EQ(app_id2, app2->app_id());
+
+  registrar->UnregisterApp(app_id2);
+  EXPECT_EQ(nullptr, registrar->GetAppById(app_id2));
+}
+
+TEST(WebAppRegistrar, DestroyRegistrarOwningRegisteredApps) {
+  auto registrar = std::make_unique<WebAppRegistrar>();
+
+  const AppId app_id = GenerateAppIdFromURL(GURL("https://example.com/path"));
+  const AppId app_id2 = GenerateAppIdFromURL(GURL("https://example.com/path2"));
+
+  auto web_app = std::make_unique<WebApp>(app_id);
+  registrar->RegisterApp(std::move(web_app));
+
+  auto web_app2 = std::make_unique<WebApp>(app_id2);
+  registrar->RegisterApp(std::move(web_app2));
+
+  registrar.reset();
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_utils.cc b/chrome/browser/web_applications/web_app_utils.cc
new file mode 100644
index 0000000..6e5bf76
--- /dev/null
+++ b/chrome/browser/web_applications/web_app_utils.cc
@@ -0,0 +1,16 @@
+// 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 "chrome/browser/web_applications/web_app_utils.h"
+
+#include "chrome/browser/profiles/profile.h"
+
+namespace web_app {
+
+bool AllowWebAppInstallation(Profile* profile) {
+  return !profile->IsGuestSession() && !profile->IsOffTheRecord() &&
+         !profile->IsSystemProfile();
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_utils.h b/chrome/browser/web_applications/web_app_utils.h
new file mode 100644
index 0000000..e7f0291
--- /dev/null
+++ b/chrome/browser/web_applications/web_app_utils.h
@@ -0,0 +1,16 @@
+// 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.
+
+#ifndef CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_UTILS_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_UTILS_H_
+
+class Profile;
+
+namespace web_app {
+
+bool AllowWebAppInstallation(Profile* profile);
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_UTILS_H_
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 2e36309..18ec4dc 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -216,6 +216,11 @@
 const base::Feature kDesktopPWAsStayInWindow{"DesktopPWAsStayInWindow",
                                              base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enables or disables new Desktop PWAs implementation that does not use
+// extensions.
+const base::Feature kDesktopPWAsWithoutExtensions{
+    "DesktopPWAsWithoutExtensions", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Disables downloads of unsafe file types over HTTP.
 const base::Feature kDisallowUnsafeHttpDownloads{
     "DisallowUnsafeHttpDownloads", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 3961025d..97e2073b 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -134,6 +134,9 @@
 extern const base::Feature kDesktopPWAsStayInWindow;
 
 COMPONENT_EXPORT(CHROME_FEATURES)
+extern const base::Feature kDesktopPWAsWithoutExtensions;
+
+COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kDisallowUnsafeHttpDownloads;
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const char kDisallowUnsafeHttpDownloadsParamName[];