Commit 295e99a3 authored by Alexey Baskakov's avatar Alexey Baskakov Committed by Commit Bot

WebApp: Move Installability Check into WebAppDataRetriever.

We may reuse WebAppDataRetriever for new PendingAppManager
implementation.

Some subset of WebAppInstallManager will be used instead
of BookmarkAppHelper
(with a last step implemented in a specific for extensions way)

Bug: 901226
Change-Id: I8b803b3c618309efd9452d1ca4f03a476e70575a
Reviewed-on: https://chromium-review.googlesource.com/c/1335074Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Reviewed-by: default avatarGiovanni Ortuño Urquidi <ortuno@chromium.org>
Commit-Queue: Alexey Baskakov <loyso@chromium.org>
Cr-Commit-Position: refs/heads/master@{#608665}
parent 69b63931
......@@ -5109,6 +5109,8 @@ static_library("test_support") {
"download/download_test_file_activity_observer.h",
"history/history_test_utils.cc",
"history/history_test_utils.h",
"installable/fake_installable_manager.cc",
"installable/fake_installable_manager.h",
"media/webrtc/fake_desktop_media_list.cc",
"media/webrtc/fake_desktop_media_list.h",
"media/webrtc/fake_desktop_media_picker_factory.cc",
......
// 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/installable/fake_installable_manager.h"
#include <utility>
#include "base/callback.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/installable/installable_data.h"
FakeInstallableManager::FakeInstallableManager(
content::WebContents* web_contents)
: InstallableManager(web_contents) {}
FakeInstallableManager::~FakeInstallableManager() {}
void FakeInstallableManager::GetData(const InstallableParams& params,
const InstallableCallback& callback) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindRepeating(callback, *data_));
}
// static
FakeInstallableManager* FakeInstallableManager::CreateForWebContents(
content::WebContents* web_contents) {
auto manager = std::make_unique<FakeInstallableManager>(web_contents);
FakeInstallableManager* result = manager.get();
web_contents->SetUserData(UserDataKey(), std::move(manager));
return result;
}
// static
FakeInstallableManager*
FakeInstallableManager::CreateForWebContentsWithManifest(
content::WebContents* web_contents,
InstallableStatusCode installable_code,
const GURL& manifest_url,
std::unique_ptr<blink::Manifest> manifest) {
FakeInstallableManager* installable_manager =
FakeInstallableManager::CreateForWebContents(web_contents);
installable_manager->manifest_ = std::move(manifest);
const bool valid_manifest = true;
const bool has_worker = true;
// Not used:
const GURL icon_url;
const std::unique_ptr<SkBitmap> icon;
auto installable_data = std::make_unique<InstallableData>(
installable_code, manifest_url, installable_manager->manifest_.get(),
icon_url, icon.get(), icon_url, icon.get(), valid_manifest, has_worker);
installable_manager->data_ = std::move(installable_data);
return installable_manager;
}
// 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_INSTALLABLE_FAKE_INSTALLABLE_MANAGER_H_
#define CHROME_BROWSER_INSTALLABLE_FAKE_INSTALLABLE_MANAGER_H_
#include <memory>
#include "chrome/browser/installable/installable_logging.h"
#include "chrome/browser/installable/installable_manager.h"
struct InstallableData;
namespace blink {
struct Manifest;
}
namespace content {
class WebContents;
}
class FakeInstallableManager : public InstallableManager {
public:
explicit FakeInstallableManager(content::WebContents* web_contents);
~FakeInstallableManager() override;
// InstallableManager:
void GetData(const InstallableParams& params,
const InstallableCallback& callback) override;
// Create the manager and attach it to |web_contents|.
static FakeInstallableManager* CreateForWebContents(
content::WebContents* web_contents);
// Create the manager and attach it to |web_contents|. It responds to
// |GetData| with a blink |manifest| provided.
static FakeInstallableManager* CreateForWebContentsWithManifest(
content::WebContents* web_contents,
InstallableStatusCode installable_code,
const GURL& manifest_url,
std::unique_ptr<blink::Manifest> manifest);
private:
std::unique_ptr<blink::Manifest> manifest_;
std::unique_ptr<InstallableData> data_;
};
#endif // CHROME_BROWSER_INSTALLABLE_FAKE_INSTALLABLE_MANAGER_H_
......@@ -23,4 +23,6 @@ InstallableData::InstallableData(InstallableStatusCode error_code,
valid_manifest(valid_manifest),
has_worker(has_worker) {}
InstallableData::InstallableData(const InstallableData&) = default;
InstallableData::~InstallableData() = default;
......@@ -24,6 +24,7 @@ struct InstallableData {
const SkBitmap* badge_icon,
bool valid_manifest,
bool has_worker);
InstallableData(const InstallableData&);
~InstallableData();
// NO_ERROR_DETECTED if there were no issues.
......
......@@ -25,6 +25,8 @@ class InstallManager {
virtual bool CanInstallWebApp(const content::WebContents* web_contents) = 0;
// Starts a web app installation process for a given |web_contents|.
// |force_shortcut_app| forces the creation of a shortcut app instead of a PWA
// even if installation is available.
virtual void InstallWebApp(content::WebContents* web_contents,
bool force_shortcut_app,
OnceInstallCallback callback) = 0;
......
......@@ -12,6 +12,8 @@
#include "base/bind.h"
#include "base/callback.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/installable/installable_data.h"
#include "chrome/browser/installable/installable_manager.h"
#include "chrome/browser/web_applications/components/web_app_icon_generator.h"
#include "chrome/common/chrome_render_frame.mojom.h"
#include "chrome/common/web_application_info.h"
......@@ -63,6 +65,26 @@ void WebAppDataRetriever::GetWebApplicationInfo(
web_contents, entry->GetUniqueID()));
}
void WebAppDataRetriever::CheckInstallabilityAndRetrieveManifest(
content::WebContents* web_contents,
CheckInstallabilityCallback callback) {
InstallableManager* installable_manager =
InstallableManager::FromWebContents(web_contents);
DCHECK(installable_manager);
// TODO(crbug.com/829232) Unify with other calls to GetData.
InstallableParams params;
params.check_eligibility = true;
params.valid_primary_icon = true;
params.valid_manifest = true;
params.has_worker = true;
// Do not wait_for_worker. OnDidPerformInstallableCheck is always invoked.
installable_manager->GetData(
params, base::BindRepeating(
&WebAppDataRetriever::OnDidPerformInstallableCheck,
weak_ptr_factory_.GetWeakPtr(), base::Passed(&callback)));
}
void WebAppDataRetriever::GetIcons(const GURL& app_url,
const std::vector<GURL>& icon_urls,
GetIconsCallback callback) {
......@@ -132,4 +154,14 @@ void WebAppDataRetriever::OnGetWebApplicationInfoFailed() {
std::move(get_web_app_info_callback_).Run(nullptr);
}
void WebAppDataRetriever::OnDidPerformInstallableCheck(
CheckInstallabilityCallback callback,
const InstallableData& data) {
DCHECK(data.manifest_url.is_valid() || data.manifest->IsEmpty());
const bool is_installable = data.error_code == NO_ERROR_DETECTED;
std::move(callback).Run(*data.manifest, is_installable);
}
} // namespace web_app
......@@ -6,13 +6,18 @@
#define CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_WEB_APP_DATA_RETRIEVER_H_
#include <memory>
#include <vector>
#include "base/callback_forward.h"
#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "chrome/common/chrome_render_frame.mojom.h"
#include "chrome/common/web_application_info.h"
struct InstallableData;
struct WebApplicationInfo;
namespace blink {
struct Manifest;
}
namespace content {
class WebContents;
......@@ -24,8 +29,13 @@ namespace web_app {
// information to install an app. Should only be called from the UI thread.
class WebAppDataRetriever {
public:
// Returns nullptr for WebApplicationInfo if error.
using GetWebApplicationInfoCallback =
base::OnceCallback<void(std::unique_ptr<WebApplicationInfo>)>;
// |is_installable| is false if installability check failed.
using CheckInstallabilityCallback =
base::OnceCallback<void(const blink::Manifest&, bool is_installable)>;
// Returns empty vector if error.
using GetIconsCallback =
base::OnceCallback<void(std::vector<WebApplicationInfo::IconInfo>)>;
......@@ -37,6 +47,11 @@ class WebAppDataRetriever {
virtual void GetWebApplicationInfo(content::WebContents* web_contents,
GetWebApplicationInfoCallback callback);
// Performs installability check and invokes |callback| with manifest.
virtual void CheckInstallabilityAndRetrieveManifest(
content::WebContents* web_contents,
CheckInstallabilityCallback callback);
// Downloads icons from |icon_urls|. If icons are missing for certain required
// sizes, generates them based on |app_url|. Runs |callback| with a vector of
// the retrieved and generated icons.
......@@ -52,6 +67,9 @@ class WebAppDataRetriever {
const WebApplicationInfo& web_app_info);
void OnGetWebApplicationInfoFailed();
void OnDidPerformInstallableCheck(CheckInstallabilityCallback callback,
const InstallableData& data);
// Saved callback from GetWebApplicationInfo().
GetWebApplicationInfoCallback get_web_app_info_callback_;
......
......@@ -9,7 +9,12 @@
#include <vector>
#include "base/optional.h"
#include "base/strings/nullable_string16.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind_test_util.h"
#include "chrome/browser/installable/fake_installable_manager.h"
#include "chrome/browser/installable/installable_data.h"
#include "chrome/browser/installable/installable_manager.h"
#include "chrome/browser/web_applications/components/web_app_icon_generator.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_profile.h"
......@@ -20,6 +25,7 @@
#include "mojo/public/cpp/bindings/associated_binding.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/common/manifest/manifest.h"
namespace web_app {
......@@ -93,6 +99,27 @@ class WebAppDataRetrieverTest : public ChromeRenderViewHostTestHarness {
std::move(quit_closure).Run();
}
std::unique_ptr<WebApplicationInfo> CreateWebApplicationInfo(
const GURL& url,
const std::string name,
const std::string description,
const GURL& scope,
base::Optional<SkColor> theme_color) {
auto web_app_info = std::make_unique<WebApplicationInfo>();
web_app_info->app_url = url;
web_app_info->title = base::UTF8ToUTF16(name);
web_app_info->description = base::UTF8ToUTF16(description);
web_app_info->scope = scope;
web_app_info->theme_color = theme_color;
return web_app_info;
}
static base::NullableString16 ToNullableUTF16(const std::string& str) {
return base::NullableString16(base::UTF8ToUTF16(str), false);
}
protected:
content::WebContentsTester* web_contents_tester() {
return content::WebContentsTester::For(web_contents());
......@@ -278,4 +305,75 @@ TEST_F(WebAppDataRetrieverTest, GetIcons_NoIconsProvided) {
}
}
TEST_F(WebAppDataRetrieverTest, CheckInstallabilityAndRetrieveManifest) {
const GURL manifest_start_url = GURL("https://example.com/start");
const std::string manifest_short_name = "Short Name from Manifest";
const std::string manifest_name = "Name from Manifest";
const GURL manifest_scope = GURL("https://example.com/scope");
const base::Optional<SkColor> manifest_theme_color = 0xAABBCCDD;
{
auto manifest = std::make_unique<blink::Manifest>();
manifest->short_name = ToNullableUTF16(manifest_short_name);
manifest->name = ToNullableUTF16(manifest_name);
manifest->start_url = manifest_start_url;
manifest->scope = manifest_scope;
manifest->theme_color = manifest_theme_color;
FakeInstallableManager::CreateForWebContentsWithManifest(
web_contents(), NO_ERROR_DETECTED, GURL("https://example.com/manifest"),
std::move(manifest));
}
base::RunLoop run_loop;
bool callback_called = false;
WebAppDataRetriever retriever;
retriever.CheckInstallabilityAndRetrieveManifest(
web_contents(),
base::BindLambdaForTesting(
[&](const blink::Manifest& result, bool is_installable) {
EXPECT_TRUE(is_installable);
EXPECT_EQ(base::UTF8ToUTF16(manifest_short_name),
result.short_name.string());
EXPECT_EQ(base::UTF8ToUTF16(manifest_name), result.name.string());
EXPECT_EQ(manifest_start_url, result.start_url);
EXPECT_EQ(manifest_scope, result.scope);
EXPECT_EQ(manifest_theme_color, result.theme_color);
callback_called = true;
run_loop.Quit();
}));
run_loop.Run();
EXPECT_TRUE(callback_called);
}
TEST_F(WebAppDataRetrieverTest, CheckInstallabilityFails) {
{
auto manifest = std::make_unique<blink::Manifest>();
FakeInstallableManager::CreateForWebContentsWithManifest(
web_contents(), NO_MANIFEST, GURL(), std::move(manifest));
}
base::RunLoop run_loop;
bool callback_called = false;
WebAppDataRetriever retriever;
retriever.CheckInstallabilityAndRetrieveManifest(
web_contents(),
base::BindLambdaForTesting(
[&](const blink::Manifest& result, bool is_installable) {
EXPECT_FALSE(is_installable);
callback_called = true;
run_loop.Quit();
}));
run_loop.Run();
EXPECT_TRUE(callback_called);
}
} // namespace web_app
......@@ -2,13 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <utility>
#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/installable/installable_data.h"
#include "chrome/browser/installable/installable_manager.h"
#include "chrome/browser/web_applications/components/web_app_constants.h"
#include "chrome/browser/web_applications/components/web_app_data_retriever.h"
#include "chrome/browser/web_applications/components/web_app_helpers.h"
......@@ -42,17 +42,17 @@ void WebAppInstallManager::InstallWebApp(content::WebContents* contents,
OnceInstallCallback install_callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// No parrallel installations support.
// Concurrent calls are not allowed.
DCHECK(!web_contents());
CHECK(!install_callback_);
force_shortcut_app_ = force_shortcut_app;
install_callback_ = std::move(install_callback);
Observe(contents);
install_callback_ = std::move(install_callback);
data_retriever_->GetWebApplicationInfo(
web_contents(),
base::BindOnce(&WebAppInstallManager::OnGetWebApplicationInfo,
weak_ptr_factory_.GetWeakPtr()));
weak_ptr_factory_.GetWeakPtr(), force_shortcut_app));
}
void WebAppInstallManager::WebContentsDestroyed() {
......@@ -64,15 +64,9 @@ void WebAppInstallManager::SetDataRetrieverForTesting(
data_retriever_ = std::move(data_retriever);
}
void WebAppInstallManager::ResetInstallProcessArguments() {
Observe(nullptr);
force_shortcut_app_ = false;
web_app_info_.reset();
}
void WebAppInstallManager::CallInstallCallback(const AppId& app_id,
InstallResultCode code) {
ResetInstallProcessArguments();
Observe(nullptr);
DCHECK(install_callback_);
std::move(install_callback_).Run(app_id, code);
......@@ -93,6 +87,7 @@ bool WebAppInstallManager::InstallInterrupted() const {
}
void WebAppInstallManager::OnGetWebApplicationInfo(
bool force_shortcut_app,
std::unique_ptr<WebApplicationInfo> web_app_info) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// If interrupted, install_callback_ is already invoked or may invoke later.
......@@ -102,54 +97,44 @@ void WebAppInstallManager::OnGetWebApplicationInfo(
if (!web_app_info)
return ReturnError(InstallResultCode::kGetWebApplicationInfoFailed);
web_app_info_ = std::move(web_app_info);
// TODO(loyso): Consider to merge InstallableManager into WebAppDataRetriever.
InstallableManager* installable_manager =
InstallableManager::FromWebContents(web_contents());
DCHECK(installable_manager);
// TODO(crbug.com/829232) Unify with other calls to GetData.
InstallableParams params;
params.check_eligibility = true;
params.valid_primary_icon = true;
params.valid_manifest = true;
params.has_worker = true;
// Do not wait_for_worker. OnDidPerformInstallableCheck is always invoked.
installable_manager->GetData(
params,
base::BindRepeating(&WebAppInstallManager::OnDidPerformInstallableCheck,
weak_ptr_factory_.GetWeakPtr()));
data_retriever_->CheckInstallabilityAndRetrieveManifest(
web_contents(),
base::BindOnce(&WebAppInstallManager::OnDidPerformInstallableCheck,
weak_ptr_factory_.GetWeakPtr(), std::move(web_app_info),
force_shortcut_app));
}
void WebAppInstallManager::OnDidPerformInstallableCheck(
const InstallableData& data) {
DCHECK(data.manifest_url.is_valid() || data.manifest->IsEmpty());
std::unique_ptr<WebApplicationInfo> web_app_info,
bool force_shortcut_app,
const blink::Manifest& manifest,
bool is_installable) {
// If interrupted, install_callback_ is already invoked or may invoke later.
if (InstallInterrupted())
return;
const ForInstallableSite for_installable_site =
data.error_code == NO_ERROR_DETECTED && !force_shortcut_app_
? ForInstallableSite::kYes
: ForInstallableSite::kNo;
DCHECK(web_app_info);
UpdateWebAppInfoFromManifest(*data.manifest, web_app_info_.get(),
for_installable_site);
const ForInstallableSite for_installable_site =
is_installable && !force_shortcut_app ? ForInstallableSite::kYes
: ForInstallableSite::kNo;
// TODO(loyso): Implement installation logic from BookmarkAppHelper:
// - UpdateShareTargetInPrefs.
// - WebAppIconDownloader.
// etc
const AppId app_id = GenerateAppIdFromURL(web_app_info_->app_url);
UpdateWebAppInfoFromManifest(manifest, web_app_info.get(),
for_installable_site);
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);
web_app->SetScope(web_app_info_->scope);
web_app->SetThemeColor(web_app_info_->theme_color);
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);
web_app->SetScope(web_app_info->scope);
web_app->SetThemeColor(web_app_info->theme_color);
registrar_->RegisterApp(std::move(web_app));
......
......@@ -13,9 +13,12 @@
#include "content/public/browser/web_contents_observer.h"
class Profile;
struct InstallableData;
struct WebApplicationInfo;
namespace blink {
struct Manifest;
}
namespace content {
class WebContents;
}
......@@ -44,7 +47,6 @@ class WebAppInstallManager final : public InstallManager,
std::unique_ptr<WebAppDataRetriever> data_retriever);
private:
void ResetInstallProcessArguments();
void CallInstallCallback(const AppId& app_id, InstallResultCode code);
void ReturnError(InstallResultCode code);
......@@ -52,22 +54,23 @@ class WebAppInstallManager final : public InstallManager,
bool InstallInterrupted() const;
void OnGetWebApplicationInfo(
bool force_shortcut_app,
std::unique_ptr<WebApplicationInfo> web_app_info);
void OnDidPerformInstallableCheck(const InstallableData& data);
void OnDidPerformInstallableCheck(
std::unique_ptr<WebApplicationInfo> web_app_info,
bool force_shortcut_app,
const blink::Manifest& manifest,
bool is_installable);
// Forces the creation of a shortcut app instead of a PWA even if installation
// is available.
bool force_shortcut_app_ = false;
// Arguments, valid during installation process:
// Saved callback:
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);
};
......
......@@ -11,6 +11,7 @@
#include "base/strings/nullable_string16.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind_test_util.h"
#include "chrome/browser/installable/fake_installable_manager.h"
#include "chrome/browser/installable/installable_data.h"
#include "chrome/browser/installable/installable_manager.h"
#include "chrome/browser/ssl/security_state_tab_helper.h"
......@@ -23,35 +24,11 @@
#include "chrome/browser/web_applications/web_app_utils.h"
#include "chrome/test/base/testing_profile.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/manifest/manifest.h"
#include "url/gurl.h"
namespace web_app {
class TestInstallableManager : public InstallableManager {
public:
explicit TestInstallableManager(content::WebContents* web_contents)
: InstallableManager(web_contents) {}
static TestInstallableManager* CreateForWebContents(
content::WebContents* web_contents) {
auto manager = std::make_unique<TestInstallableManager>(web_contents);
TestInstallableManager* result = manager.get();
web_contents->SetUserData(UserDataKey(), std::move(manager));
return result;
}
void SetData(std::unique_ptr<InstallableData> data) {
data_ = std::move(data);
}
void GetData(const InstallableParams& params,
const InstallableCallback& callback) override {
callback.Run(*std::move(data_));
}
std::unique_ptr<InstallableData> data_;
};
class WebAppInstallManagerTest : public WebAppTest {
public:
void SetUp() override {
......@@ -93,25 +70,6 @@ class WebAppInstallManagerTest : public WebAppTest {
SecurityStateTabHelper::CreateForWebContents(web_contents());
}
void CreateTestInstallableManager(const GURL& manifest_url,
blink::Manifest* manifest) {
const InstallableStatusCode installable_code = NO_ERROR_DETECTED;
const bool valid_manifest = true;
const bool has_worker = true;
// Not used in WebAppInstallManager:
const GURL icon_url;
const std::unique_ptr<SkBitmap> icon;
auto installable_data = std::make_unique<InstallableData>(
installable_code, manifest_url, manifest, icon_url, icon.get(),
icon_url, icon.get(), valid_manifest, has_worker);
TestInstallableManager* installable_manager =
TestInstallableManager::CreateForWebContents(web_contents());
installable_manager->SetData(std::move(installable_data));
}
static base::NullableString16 ToNullableUTF16(const std::string& str) {
return base::NullableString16(base::UTF8ToUTF16(str), false);
}
......@@ -230,14 +188,16 @@ TEST_F(WebAppInstallManagerTest, InstallableCheck) {
const GURL manifest_scope = GURL("https://example.com/scope");
const base::Optional<SkColor> manifest_theme_color = 0xAABBCCDD;
blink::Manifest manifest;
manifest.short_name = ToNullableUTF16("Short Name from Manifest");
manifest.name = ToNullableUTF16(manifest_name);
manifest.start_url = manifest_start_url;
manifest.scope = manifest_scope;
manifest.theme_color = manifest_theme_color;
auto manifest = std::make_unique<blink::Manifest>();
manifest->short_name = ToNullableUTF16("Short Name from Manifest");
manifest->name = ToNullableUTF16(manifest_name);
manifest->start_url = manifest_start_url;
manifest->scope = manifest_scope;
manifest->theme_color = manifest_theme_color;
CreateTestInstallableManager(GURL("https://example.com/manifest"), &manifest);
FakeInstallableManager::CreateForWebContentsWithManifest(
web_contents(), NO_ERROR_DETECTED, GURL("https://example.com/manifest"),
std::move(manifest));
base::RunLoop run_loop;
bool callback_called = false;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment