Skip to content

Commit

Permalink
iwa: Add Command to get IWA browsing data usage
Browse files Browse the repository at this point in the history
GetIsolatedWebAppBrowsingDataCommand generates a map of installed IWA
origin to total browsing data used by the app. The data usage is
computed by creating a CookiesTreeModel and BrowsingDataModel for each
StoragePartition owned by the IWA and combining the returned usage.

Bug: 1311065
Change-Id: Ib03f2057bade4db1aa1810fc78abb23ceb67b105
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4327127
Reviewed-by: Zelin Liu <zelin@chromium.org>
Reviewed-by: Dibyajyoti Pal <dibyapal@chromium.org>
Commit-Queue: Robbie McElrath <rmcelrath@chromium.org>
Reviewed-by: Christian Dullweber <dullweber@chromium.org>
Reviewed-by: Daniel Murphy <dmurph@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1116052}
  • Loading branch information
robbiemc authored and Chromium LUCI CQ committed Mar 11, 2023
1 parent 2681bdc commit 3984ce8
Show file tree
Hide file tree
Showing 8 changed files with 446 additions and 16 deletions.
19 changes: 4 additions & 15 deletions chrome/browser/browsing_data/cookies_tree_model.cc
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,10 @@ CookiesTreeModel* CookieTreeNode::GetModel() const {
}

int64_t CookieTreeNode::InclusiveSize() const {
return 0;
return std::accumulate(children().cbegin(), children().cend(), int64_t{0},
[](int64_t total, const auto& child) {
return total + child->InclusiveSize();
});
}

int CookieTreeNode::NumberOfCookies() const {
Expand Down Expand Up @@ -855,13 +858,6 @@ class CookieTreeCollectionNode : public CookieTreeNode {
CookieTreeCollectionNode& operator=(const CookieTreeCollectionNode&) = delete;

~CookieTreeCollectionNode() override = default;

int64_t InclusiveSize() const final {
return std::accumulate(children().cbegin(), children().cend(), int64_t{0},
[](int64_t total, const auto& child) {
return total + child->InclusiveSize();
});
}
};

///////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -1201,13 +1197,6 @@ bool CookieTreeHostNode::CanCreateContentException() const {
return !url_.SchemeIsFile();
}

int64_t CookieTreeHostNode::InclusiveSize() const {
return std::accumulate(children().cbegin(), children().cend(), int64_t{0},
[](int64_t total, const auto& child) {
return total + child->InclusiveSize();
});
}

///////////////////////////////////////////////////////////////////////////////
// ScopedBatchUpdateNotifier
CookiesTreeModel::ScopedBatchUpdateNotifier::ScopedBatchUpdateNotifier(
Expand Down
1 change: 0 additions & 1 deletion chrome/browser/browsing_data/cookies_tree_model.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,6 @@ class CookieTreeHostNode : public CookieTreeNode {

// CookieTreeNode methods:
DetailedInfo GetDetailedInfo() const override;
int64_t InclusiveSize() const override;

// CookieTreeHostNode methods:
CookieTreeCookiesNode* GetOrCreateCookiesNode();
Expand Down
4 changes: 4 additions & 0 deletions chrome/browser/web_applications/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ source_set("web_applications") {
"file_utils_wrapper.h",
"install_bounce_metric.cc",
"install_bounce_metric.h",
"isolated_web_apps/get_isolated_web_app_browsing_data_command.cc",
"isolated_web_apps/get_isolated_web_app_browsing_data_command.h",
"isolated_web_apps/install_isolated_web_app_command.cc",
"isolated_web_apps/install_isolated_web_app_command.h",
"isolated_web_apps/install_isolated_web_app_from_command_line.cc",
Expand Down Expand Up @@ -360,6 +362,7 @@ source_set("web_applications") {
"//chrome/browser/profiles:profile",
"//chrome/common",
"//chrome/common:non_code_constants",
"//components/browsing_data/content",
"//components/content_settings/core/browser",
"//components/custom_handlers",
"//components/device_event_log",
Expand Down Expand Up @@ -780,6 +783,7 @@ source_set("web_applications_browser_tests") {
"commands/update_protocol_handler_approval_command_browsertest.cc",
"externally_installed_web_app_prefs_browsertest.cc",
"externally_managed_app_manager_impl_browsertest.cc",
"isolated_web_apps/get_isolated_web_app_browsing_data_command_browsertest.cc",
"isolated_web_apps/install_isolated_web_app_from_command_line_browsertest.cc",
"isolated_web_apps/isolated_web_app_browsertest.cc",
"isolated_web_apps/isolated_web_app_url_loader_factory_browsertest.cc",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
// 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.

#include "chrome/browser/web_applications/isolated_web_apps/get_isolated_web_app_browsing_data_command.h"

#include <memory>

#include "base/barrier_closure.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/browsing_data/chrome_browsing_data_model_delegate.h"
#include "chrome/browser/browsing_data/cookies_tree_model.h"
#include "chrome/browser/browsing_data/local_data_container.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_observer.h"
#include "chrome/browser/web_applications/locks/full_system_lock.h"
#include "chrome/browser/web_applications/web_app_id.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "components/browsing_data/content/browsing_data_model.h"
#include "content/public/browser/storage_partition_config.h"
#include "ui/base/models/tree_model.h"
#include "url/origin.h"

namespace web_app {
namespace {

const char kDebugOriginKey[] = "iwa_origins";

// Estimates the size in bytes of a non-default StoragePartition by summing the
// size of all browsing data stored within it.
class StoragePartitionSizeEstimator : private CookiesTreeModel::Observer,
private ProfileObserver {
public:
static void EstimateSize(
Profile* profile,
const content::StoragePartitionConfig& storage_partition_config,
base::OnceCallback<void(int64_t)> complete_callback) {
DCHECK(!storage_partition_config.is_default());

// |owning_closure| will own the StoragePartitionSizeEstimator and delete
// it when called or reset.
auto* estimator = new StoragePartitionSizeEstimator(profile);
base::OnceClosure owning_closure =
base::DoNothingWithBoundArgs(base::WrapUnique(estimator));

estimator->Start(
storage_partition_config,
std::move(complete_callback).Then(std::move(owning_closure)));
}

~StoragePartitionSizeEstimator() override { profile_->RemoveObserver(this); }

private:
explicit StoragePartitionSizeEstimator(Profile* profile) : profile_(profile) {
profile_->AddObserver(this);
}

void Start(const content::StoragePartitionConfig& storage_partition_config,
base::OnceCallback<void(int64_t)> complete_callback) {
complete_callback_ = std::move(complete_callback);
// Need to wait for both BrowsingDataModel and CookiesTreeModel to load.
int number_of_models_to_load = 2;
model_loaded_closure_ = base::BarrierClosure(
number_of_models_to_load,
base::BindOnce(&StoragePartitionSizeEstimator::ModelsLoaded,
weak_ptr_factory_.GetWeakPtr()));

content::StoragePartition* storage_partition =
profile_->GetStoragePartition(storage_partition_config);
BrowsingDataModel::BuildFromDisk(
storage_partition,
ChromeBrowsingDataModelDelegate::CreateForProfile(profile_),
base::BindOnce(&StoragePartitionSizeEstimator::BrowsingDataModelLoaded,
weak_ptr_factory_.GetWeakPtr()));

std::unique_ptr<LocalDataContainer> local_data_container =
LocalDataContainer::CreateFromStoragePartition(
storage_partition,
CookiesTreeModel::GetCookieDeletionDisabledCallback(profile_));
cookies_tree_model_ = base::WrapUnique(
new CookiesTreeModel(std::move(local_data_container),
/*special_storage_policy=*/nullptr));
cookies_tree_model_->AddCookiesTreeObserver(this);
}

void ModelsLoaded() {
int64_t size = cookies_tree_model_->GetRoot()->InclusiveSize();
for (const BrowsingDataModel::BrowsingDataEntryView& entry :
*browsing_data_model_) {
size += entry.data_details->storage_size;
}
std::move(complete_callback_).Run(size);
}

void BrowsingDataModelLoaded(
std::unique_ptr<BrowsingDataModel> browsing_data_model) {
browsing_data_model_ = std::move(browsing_data_model);
model_loaded_closure_.Run();
}

// CookiesTreeModel::Observer
void TreeModelEndBatchDeprecated(
CookiesTreeModel* cookies_tree_model) override {
cookies_tree_model_->RemoveCookiesTreeObserver(this);
model_loaded_closure_.Run();
}

void TreeNodesAdded(ui::TreeModel* model,
ui::TreeModelNode* parent,
size_t start,
size_t count) override {}
void TreeNodesRemoved(ui::TreeModel* model,
ui::TreeModelNode* parent,
size_t start,
size_t count) override {}
void TreeNodeChanged(ui::TreeModel* model, ui::TreeModelNode* node) override {
}

// ProfileObserver:
void OnProfileWillBeDestroyed(Profile* profile) override {
// Abort if the Profile is being deleted. |complete_callback_| owns the
// object, so resetting it will delete |this|.
complete_callback_.Reset();
}

Profile* profile_;
base::OnceCallback<void(int64_t)> complete_callback_;
base::RepeatingClosure model_loaded_closure_;
std::unique_ptr<BrowsingDataModel> browsing_data_model_;
std::unique_ptr<CookiesTreeModel> cookies_tree_model_;
base::WeakPtrFactory<StoragePartitionSizeEstimator> weak_ptr_factory_{this};
};

} // namespace

GetIsolatedWebAppBrowsingDataCommand::GetIsolatedWebAppBrowsingDataCommand(
Profile* profile,
BrowsingDataCallback callback)
: WebAppCommandTemplate<FullSystemLock>(
"GetIsolatedWebAppBrowsingDataCommand"),
profile_(profile),
callback_(std::move(callback)),
lock_description_(std::make_unique<FullSystemLockDescription>()),
browsing_data_({}) {
debug_data_.Set("profile", profile_->GetDebugName());
}

GetIsolatedWebAppBrowsingDataCommand::~GetIsolatedWebAppBrowsingDataCommand() =
default;

const LockDescription& GetIsolatedWebAppBrowsingDataCommand::lock_description()
const {
return *lock_description_;
}

base::Value GetIsolatedWebAppBrowsingDataCommand::ToDebugValue() const {
return base::Value(debug_data_.Clone());
}

void GetIsolatedWebAppBrowsingDataCommand::StartWithLock(
std::unique_ptr<FullSystemLock> lock) {
lock_ = std::move(lock);

pending_task_count_++;
const WebAppRegistrar& web_app_registrar = lock_->registrar();
for (const WebApp& web_app : web_app_registrar.GetApps()) {
const AppId& app_id = web_app.app_id();
if (!web_app_registrar.IsIsolated(app_id)) {
continue;
}
url::Origin iwa_origin = url::Origin::Create(web_app.scope());
for (const content::StoragePartitionConfig& storage_partition_config :
web_app_registrar.GetIsolatedWebAppStoragePartitionConfigs(app_id)) {
pending_task_count_++;
debug_data_.EnsureDict(kDebugOriginKey)->Set(iwa_origin.Serialize(), -1);
StoragePartitionSizeEstimator::EstimateSize(
profile_, storage_partition_config,
base::BindOnce(&GetIsolatedWebAppBrowsingDataCommand::
StoragePartitionSizeFetched,
weak_factory_.GetWeakPtr(),
/*data_key=*/iwa_origin));
}
}
pending_task_count_--;

MaybeCompleteCommand();
}

void GetIsolatedWebAppBrowsingDataCommand::StoragePartitionSizeFetched(
const url::Origin& iwa_origin,
int64_t size) {
DCHECK_GT(pending_task_count_, 0);
pending_task_count_--;
browsing_data_[iwa_origin] += size;
// Store the size as a double because Value::Dict doesn't support 64-bit
// integers. This should only lead to data loss when size is >2^54.
debug_data_.EnsureDict(kDebugOriginKey)
->Set(iwa_origin.Serialize(), static_cast<double>(size));

MaybeCompleteCommand();
}

void GetIsolatedWebAppBrowsingDataCommand::MaybeCompleteCommand() {
if (pending_task_count_ == 0) {
SignalCompletionAndSelfDestruct(
CommandResult::kSuccess,
base::BindOnce(std::move(callback_), browsing_data_));
}
}

void GetIsolatedWebAppBrowsingDataCommand::OnShutdown() {
SignalCompletionAndSelfDestruct(
CommandResult::kShutdown,
base::BindOnce(std::move(callback_),
base::flat_map<url::Origin, int64_t>()));
}

} // namespace web_app
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// 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.

#ifndef CHROME_BROWSER_WEB_APPLICATIONS_ISOLATED_WEB_APPS_GET_ISOLATED_WEB_APP_BROWSING_DATA_COMMAND_H_
#define CHROME_BROWSER_WEB_APPLICATIONS_ISOLATED_WEB_APPS_GET_ISOLATED_WEB_APP_BROWSING_DATA_COMMAND_H_

#include <memory>

#include "base/containers/flat_map.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/values.h"
#include "chrome/browser/web_applications/commands/web_app_command.h"
#include "chrome/browser/web_applications/locks/full_system_lock.h"

class Profile;

namespace url {
class Origin;
} // namespace url

namespace web_app {

class FullSystemLock;
class FullSystemLockDescription;
class LockDescription;

// Computes the total browsing data usage in bytes of every installed Isolated
// Web App.
class GetIsolatedWebAppBrowsingDataCommand
: public WebAppCommandTemplate<FullSystemLock> {
public:
using BrowsingDataCallback =
base::OnceCallback<void(base::flat_map<url::Origin, int64_t>)>;

GetIsolatedWebAppBrowsingDataCommand(Profile* profile,
BrowsingDataCallback callback);
~GetIsolatedWebAppBrowsingDataCommand() override;

// WebAppCommandTemplate<FullSystemLock>:
const LockDescription& lock_description() const override;
base::Value ToDebugValue() const override;
void StartWithLock(std::unique_ptr<FullSystemLock> lock) override;
void OnShutdown() override;
void OnSyncSourceRemoved() override {}

private:
void StoragePartitionSizeFetched(const url::Origin& iwa_origin, int64_t size);
void MaybeCompleteCommand();

base::raw_ptr<Profile> profile_;
BrowsingDataCallback callback_;

std::unique_ptr<FullSystemLockDescription> lock_description_;
std::unique_ptr<FullSystemLock> lock_;

int pending_task_count_ = 0;
base::flat_map<url::Origin, int64_t> browsing_data_;

base::Value::Dict debug_data_;

base::WeakPtrFactory<GetIsolatedWebAppBrowsingDataCommand> weak_factory_{
this};
};

} // namespace web_app

#endif // CHROME_BROWSER_WEB_APPLICATIONS_ISOLATED_WEB_APPS_GET_ISOLATED_WEB_APP_BROWSING_DATA_COMMAND_H_

0 comments on commit 3984ce8

Please sign in to comment.