blob: 7781f6c5bbf51f5aa7db104fdc4f3ee57ac5a7ff [file] [log] [blame]
// Copyright 2022 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/manifest_update_utils.h"
#include <ostream>
#include <string>
#include "base/feature_list.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/web_applications/web_app.h"
#include "chrome/browser/web_applications/web_app_id.h"
#include "chrome/browser/web_applications/web_app_install_utils.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "chrome/common/chrome_features.h"
#include "third_party/blink/public/common/features.h"
namespace web_app {
namespace {
// Some apps, such as pre-installed apps, have been vetted and are therefore
// considered safe and permitted to update their icon. For others, the
// feature flag needs to be on.
bool AllowUnpromptedIconUpdate(const AppId& app_id,
const WebAppRegistrar& registrar) {
const WebApp* web_app = registrar.GetAppById(app_id);
if (!web_app)
return false;
return CanWebAppUpdateIdentity(web_app) ||
base::FeatureList::IsEnabled(features::kWebAppManifestIconUpdating);
}
} // namespace
std::ostream& operator<<(std::ostream& os, ManifestUpdateResult result) {
switch (result) {
case ManifestUpdateResult::kNoAppInScope:
return os << "kNoAppInScope";
case ManifestUpdateResult::kThrottled:
return os << "kThrottled";
case ManifestUpdateResult::kWebContentsDestroyed:
return os << "kWebContentsDestroyed";
case ManifestUpdateResult::kAppUninstalling:
return os << "kAppUninstalling";
case ManifestUpdateResult::kAppIsPlaceholder:
return os << "kAppIsPlaceholder";
case ManifestUpdateResult::kAppUpToDate:
return os << "kAppUpToDate";
case ManifestUpdateResult::kAppNotEligible:
return os << "kAppNotEligible";
case ManifestUpdateResult::kAppUpdateFailed:
return os << "kAppUpdateFailed";
case ManifestUpdateResult::kAppUpdated:
return os << "kAppUpdated";
case ManifestUpdateResult::kAppIsSystemWebApp:
return os << "kAppIsSystemWebApp";
case ManifestUpdateResult::kIconDownloadFailed:
return os << "kIconDownloadFailed";
case ManifestUpdateResult::kIconReadFromDiskFailed:
return os << "kIconReadFromDiskFailed";
case ManifestUpdateResult::kAppIdMismatch:
return os << "kAppIdMismatch";
case ManifestUpdateResult::kAppAssociationsUpdateFailed:
return os << "kAppAssociationsUpdateFailed";
case ManifestUpdateResult::kAppAssociationsUpdated:
return os << "kAppAssociationsUpdated";
}
}
std::ostream& operator<<(std::ostream& os, ManifestUpdateStage stage) {
switch (stage) {
case ManifestUpdateStage::kPendingInstallableData:
return os << "kPendingInstallableData";
case ManifestUpdateStage::kPendingIconDownload:
return os << "kPendingIconDownload";
case ManifestUpdateStage::kPendingIconReadFromDisk:
return os << "kPendingIconReadFromDisk";
case ManifestUpdateStage::kPendingAppIdentityCheck:
return os << "kPendingAppIdentityCheck";
case ManifestUpdateStage::kPendingMaybeReadExistingIcons:
return os << "kPendingMaybeReadExistingIcons";
case ManifestUpdateStage::kPendingAssociationsUpdate:
return os << "kPendingAssociationsUpdate";
case ManifestUpdateStage::kAppWindowsClosed:
return os << "kAppWindowsClosed";
case ManifestUpdateStage::kPendingFinalizerUpdate:
return os << "kPendingFinalizerUpdate";
}
}
bool AllowUnpromptedNameUpdate(const AppId& app_id,
const WebAppRegistrar& registrar) {
const WebApp* web_app = registrar.GetAppById(app_id);
if (!web_app)
return false;
return CanWebAppUpdateIdentity(web_app);
}
bool NeedsAppIdentityUpdateDialog(bool title_changing,
bool icons_changing,
const AppId& app_id,
const WebAppRegistrar& registrar) {
// Shortcut apps can trigger the update check (https://crbug.com/1366600)
// on subsequent runs of the app, if the user changed the title of the app
// when creating the shortcut. But we should never run the App Identity dialog
// for shortcut apps. Also, ideally we should just use IsShortcutApp here
// instead of checking the install source, but as per
// https://crbug.com/1368592 there is a bug with that where it returns the
// wrong thing for Shortcut apps that specify `scope`.
if (registrar.IsShortcutApp(app_id) ||
registrar.GetAppInstallSourceForMetrics(app_id) ==
webapps::WebappInstallSource::MENU_CREATE_SHORTCUT) {
return false;
}
if (title_changing && !AllowUnpromptedNameUpdate(app_id, registrar))
return true;
if (icons_changing && !AllowUnpromptedIconUpdate(app_id, registrar))
return true;
return false;
}
bool IsUpdateNeededForManifest(const AppId& app_id,
const WebAppInstallInfo& install_info,
const WebAppRegistrar& registrar) {
const WebApp* app = registrar.GetAppById(app_id);
DCHECK(app);
// TODO(crbug.com/1259777): Check whether translations have been updated.
bool title_changing =
install_info.title != base::UTF8ToUTF16(app->untranslated_name());
bool icons_changing = install_info.manifest_icons != app->manifest_icons();
if (!NeedsAppIdentityUpdateDialog(title_changing, icons_changing, app_id,
registrar)) {
if (title_changing && AllowUnpromptedNameUpdate(app_id, registrar)) {
return true;
}
if (icons_changing && AllowUnpromptedIconUpdate(app_id, registrar)) {
return true;
}
}
// Allows updating start_url and manifest_id. Both fields are allowed to
// change as long as the app_id generated from them doesn't change.
{
if (install_info.manifest_id != app->manifest_id())
return true;
if (install_info.start_url != app->start_url())
return true;
}
if (install_info.theme_color != app->theme_color())
return true;
if (install_info.scope != app->scope())
return true;
if (install_info.display_mode != app->display_mode())
return true;
if (install_info.display_override != app->display_mode_override())
return true;
if (install_info.shortcuts_menu_item_infos !=
app->shortcuts_menu_item_infos()) {
return true;
}
if (install_info.share_target != app->share_target())
return true;
if (install_info.protocol_handlers != app->protocol_handlers())
return true;
if (install_info.url_handlers != app->url_handlers())
return true;
if (base::FeatureList::IsEnabled(
blink::features::kWebAppManifestLockScreen) &&
install_info.lock_screen_start_url != app->lock_screen_start_url()) {
return true;
}
if (install_info.note_taking_new_note_url !=
app->note_taking_new_note_url()) {
return true;
}
if (install_info.capture_links != app->capture_links())
return true;
if (app->file_handlers() != install_info.file_handlers)
return true;
if (install_info.background_color != app->background_color())
return true;
if (install_info.dark_mode_theme_color != app->dark_mode_theme_color()) {
return true;
}
if (install_info.dark_mode_background_color !=
app->dark_mode_background_color()) {
return true;
}
if (install_info.manifest_url != app->manifest_url())
return true;
if (install_info.launch_handler != app->launch_handler())
return true;
if (install_info.permissions_policy != app->permissions_policy())
return true;
// TODO(crbug.com/897314): Check changes to tab_strip field once icons are
// stored.
// TODO(crbug.com/1212849): Handle changes to is_storage_isolated.
// TODO(crbug.com/926083): Check more manifest fields.
return false;
}
} // namespace web_app