Commit 8e6121cf authored by Alexey Baskakov's avatar Alexey Baskakov Committed by Commit Bot

WebApp: Implement ReadSmallestCompressedIcon method in WebAppIconManager.

Drive by changes:
- Mark blocking I/O functions with ScopedBlockingCall objects.
- Promote TaskPriority to USER_VISIBLE: icons are visible in UI.
- Promote TaskShutdownBehavior to BLOCK_SHUTDOWN: Web Apps metadata is critical.

This code is disabled by default behind kDesktopPWAsWithoutExtensions
base feature.

Bug: 1029221
Change-Id: Iaec0bb4b6ec97181ec0dad2326bcd249fd6d0ea9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1941449Reviewed-by: default avatarEric Willigers <ericwilligers@chromium.org>
Commit-Queue: Alexey Baskakov <loyso@chromium.org>
Cr-Commit-Position: refs/heads/master@{#719875}
parent d297016e
......@@ -5,6 +5,9 @@
#ifndef CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_APP_ICON_MANAGER_H_
#define CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_APP_ICON_MANAGER_H_
#include <cstdint>
#include <vector>
#include "base/callback_forward.h"
#include "base/macros.h"
#include "chrome/browser/web_applications/components/web_app_helpers.h"
......@@ -33,6 +36,16 @@ class AppIconManager {
int icon_size_in_px,
ReadIconCallback callback) = 0;
// Reads smallest icon, compressed as PNG with size at least
// |icon_size_in_px|. Returns false if there is no such icon. Returns empty
// |data| in |callback| if IO error.
using ReadCompressedIconCallback =
base::OnceCallback<void(std::vector<uint8_t> data)>;
virtual bool ReadSmallestCompressedIcon(
const AppId& app_id,
int icon_size_in_px,
ReadCompressedIconCallback callback) = 0;
private:
DISALLOW_COPY_AND_ASSIGN(AppIconManager);
};
......
......@@ -66,4 +66,12 @@ bool BookmarkAppIconManager::ReadSmallestIcon(const web_app::AppId& app_id,
ExtensionIconSet::MATCH_BIGGER, std::move(callback));
}
bool BookmarkAppIconManager::ReadSmallestCompressedIcon(
const web_app::AppId& app_id,
int icon_size_in_px,
ReadCompressedIconCallback callback) {
NOTIMPLEMENTED();
return false;
}
} // namespace extensions
......@@ -26,6 +26,9 @@ class BookmarkAppIconManager : public web_app::AppIconManager {
bool ReadSmallestIcon(const web_app::AppId& app_id,
int icon_size_in_px,
ReadIconCallback callback) override;
bool ReadSmallestCompressedIcon(const web_app::AppId& app_id,
int icon_size_in_px,
ReadCompressedIconCallback callback) override;
private:
Profile* const profile_;
......
......@@ -13,6 +13,7 @@
#include "base/strings/stringprintf.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#include "base/threading/scoped_blocking_call.h"
#include "chrome/browser/web_applications/components/web_app_utils.h"
#include "chrome/browser/web_applications/file_utils_wrapper.h"
#include "chrome/browser/web_applications/web_app.h"
......@@ -115,6 +116,9 @@ bool WriteDataBlocking(std::unique_ptr<FileUtilsWrapper> utils,
base::FilePath web_apps_directory,
AppId app_id,
std::vector<WebApplicationIconInfo> icon_infos) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
const base::FilePath temp_dir = GetTempDir(utils.get(), web_apps_directory);
if (temp_dir.empty()) {
LOG(ERROR)
......@@ -147,21 +151,33 @@ bool WriteDataBlocking(std::unique_ptr<FileUtilsWrapper> utils,
bool DeleteDataBlocking(std::unique_ptr<FileUtilsWrapper> utils,
base::FilePath web_apps_directory,
AppId app_id) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
const base::FilePath app_dir = GetAppDirectory(web_apps_directory, app_id);
return utils->DeleteFileRecursively(app_dir);
}
base::FilePath GetIconFileName(const base::FilePath& web_apps_directory,
const AppId& app_id,
int icon_size_px) {
const base::FilePath app_dir = GetAppDirectory(web_apps_directory, app_id);
const base::FilePath icons_dir = app_dir.Append(kIconsDirectoryName);
return icons_dir.AppendASCII(base::StringPrintf("%i.png", icon_size_px));
}
// Performs blocking I/O. May be called on another thread.
// Returns empty SkBitmap if any errors occurred.
SkBitmap ReadIconBlocking(std::unique_ptr<FileUtilsWrapper> utils,
base::FilePath web_apps_directory,
AppId app_id,
int icon_size_px) {
const base::FilePath app_dir = GetAppDirectory(web_apps_directory, app_id);
const base::FilePath icons_dir = app_dir.Append(kIconsDirectoryName);
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
base::FilePath icon_file =
icons_dir.AppendASCII(base::StringPrintf("%i.png", icon_size_px));
GetIconFileName(web_apps_directory, app_id, icon_size_px);
auto icon_data = base::MakeRefCounted<base::RefCountedString>();
......@@ -180,9 +196,33 @@ SkBitmap ReadIconBlocking(std::unique_ptr<FileUtilsWrapper> utils,
return bitmap;
}
// Performs blocking I/O. May be called on another thread.
// Returns empty vector if any errors occurred.
std::vector<uint8_t> ReadCompressedIconBlocking(
std::unique_ptr<FileUtilsWrapper> utils,
base::FilePath web_apps_directory,
AppId app_id,
int icon_size_px) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
base::FilePath icon_file =
GetIconFileName(web_apps_directory, app_id, icon_size_px);
std::string icon_data;
if (!utils->ReadFileToString(icon_file, &icon_data)) {
LOG(ERROR) << "Could not read icon file: " << icon_file;
return std::vector<uint8_t>{};
}
// Copy data: we can't std::move std::string into std::vector.
return std::vector<uint8_t>(icon_data.begin(), icon_data.end());
}
constexpr base::TaskTraits kTaskTraits = {
base::ThreadPool(), base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN};
base::ThreadPool(), base::MayBlock(), base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::BLOCK_SHUTDOWN};
} // namespace
......@@ -242,23 +282,49 @@ bool WebAppIconManager::ReadSmallestIcon(const AppId& app_id,
ReadIconCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
int best_size_in_px = 0;
if (!FindBestSizeInPx(app_id, icon_size_in_px, &best_size_in_px))
return false;
ReadIconInternal(app_id, best_size_in_px, std::move(callback));
return true;
}
bool WebAppIconManager::ReadSmallestCompressedIcon(
const AppId& app_id,
int icon_size_in_px,
ReadCompressedIconCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
int best_size_in_px = 0;
if (!FindBestSizeInPx(app_id, icon_size_in_px, &best_size_in_px))
return false;
base::PostTaskAndReplyWithResult(
FROM_HERE, kTaskTraits,
base::BindOnce(ReadCompressedIconBlocking, utils_->Clone(),
web_apps_directory_, app_id, best_size_in_px),
std::move(callback));
return true;
}
bool WebAppIconManager::FindBestSizeInPx(const AppId& app_id,
int icon_size_in_px,
int* best_size_in_px) const {
const WebApp* web_app = registrar_.GetAppById(app_id);
if (!web_app)
return false;
int best_size_in_px = std::numeric_limits<int>::max();
*best_size_in_px = std::numeric_limits<int>::max();
for (const WebApp::IconInfo& icon_info : web_app->icons()) {
if (icon_info.size_in_px >= icon_size_in_px &&
icon_info.size_in_px < best_size_in_px) {
best_size_in_px = icon_info.size_in_px;
icon_info.size_in_px < *best_size_in_px) {
*best_size_in_px = icon_info.size_in_px;
}
}
if (best_size_in_px == std::numeric_limits<int>::max())
return false;
ReadIconInternal(app_id, best_size_in_px, std::move(callback));
return true;
return *best_size_in_px != std::numeric_limits<int>::max();
}
void WebAppIconManager::ReadIconInternal(const AppId& app_id,
......
......@@ -42,8 +42,15 @@ class WebAppIconManager : public AppIconManager {
bool ReadSmallestIcon(const AppId& app_id,
int icon_size_in_px,
ReadIconCallback callback) override;
bool ReadSmallestCompressedIcon(const AppId& app_id,
int icon_size_in_px,
ReadCompressedIconCallback callback) override;
private:
bool FindBestSizeInPx(const AppId& app_id,
int icon_size_in_px,
int* best_size_in_px) const;
void ReadIconInternal(const AppId& app_id,
int icon_size_in_px,
ReadIconCallback callback);
......
......@@ -83,6 +83,23 @@ class WebAppIconManagerTest : public WebAppTest {
return icons;
}
std::vector<uint8_t> ReadSmallestCompressedIcon(const AppId& app_id,
int icon_size_in_px) {
std::vector<uint8_t> result;
base::RunLoop run_loop;
bool icon_requested = icon_manager().ReadSmallestCompressedIcon(
app_id, icon_size_in_px,
base::BindLambdaForTesting([&](std::vector<uint8_t> data) {
result = std::move(data);
run_loop.Quit();
}));
EXPECT_TRUE(icon_requested);
run_loop.Run();
return result;
}
std::unique_ptr<WebApp> CreateWebApp() {
const GURL app_url = GURL("https://example.com/path");
const AppId app_id = GenerateAppIdFromURL(app_url);
......@@ -309,4 +326,41 @@ TEST_F(WebAppIconManagerTest, DeleteData_Failure) {
run_loop.Run();
}
TEST_F(WebAppIconManagerTest, ReadSmallestCompressedIcon_Success) {
auto web_app = CreateWebApp();
const AppId app_id = web_app->app_id();
const std::vector<int> sizes_px{icon_size::k128};
const std::vector<SkColor> colors{SK_ColorGREEN};
WriteIcons(app_id, web_app->launch_url(), sizes_px, colors);
web_app->SetIcons(ListIcons(web_app->launch_url(), sizes_px));
controller().RegisterApp(std::move(web_app));
std::vector<uint8_t> compressed_data =
ReadSmallestCompressedIcon(app_id, sizes_px[0]);
EXPECT_FALSE(compressed_data.empty());
auto* data_ptr = reinterpret_cast<const char*>(compressed_data.data());
// Check that |compressed_data| starts with the 8-byte PNG magic string.
std::string png_magic_string{data_ptr, 8};
EXPECT_EQ(png_magic_string, "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a");
}
TEST_F(WebAppIconManagerTest, ReadSmallestCompressedIcon_Failure) {
auto web_app = CreateWebApp();
const AppId app_id = web_app->app_id();
const std::vector<int> sizes_px{icon_size::k64};
web_app->SetIcons(ListIcons(web_app->launch_url(), sizes_px));
controller().RegisterApp(std::move(web_app));
std::vector<uint8_t> compressed_data =
ReadSmallestCompressedIcon(app_id, sizes_px[0]);
EXPECT_TRUE(compressed_data.empty());
}
} // namespace web_app
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