| // 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_database.h" |
| |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/containers/contains.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "chrome/browser/web_applications/system_web_apps/system_web_app_manager.h" |
| #include "chrome/browser/web_applications/system_web_apps/system_web_app_types.h" |
| #include "chrome/browser/web_applications/web_app.h" |
| #include "chrome/browser/web_applications/web_app_chromeos_data.h" |
| #include "chrome/browser/web_applications/web_app_database_factory.h" |
| #include "chrome/browser/web_applications/web_app_file_handler_manager.h" |
| #include "chrome/browser/web_applications/web_app_helpers.h" |
| #include "chrome/browser/web_applications/web_app_proto_utils.h" |
| #include "chrome/browser/web_applications/web_app_registry_update.h" |
| #include "chrome/browser/web_applications/web_app_utils.h" |
| #include "chrome/browser/web_applications/web_application_info.h" |
| #include "components/services/app_service/public/cpp/file_handler.h" |
| #include "components/services/app_service/public/cpp/protocol_handler_info.h" |
| #include "components/services/app_service/public/cpp/share_target.h" |
| #include "components/sync/base/model_type.h" |
| #include "components/sync/base/time.h" |
| #include "components/sync/model/metadata_batch.h" |
| #include "components/sync/model/metadata_change_list.h" |
| #include "components/sync/model/model_error.h" |
| #include "third_party/blink/public/common/manifest/manifest.h" |
| #include "third_party/blink/public/mojom/manifest/capture_links.mojom.h" |
| #include "third_party/blink/public/mojom/manifest/manifest.mojom.h" |
| #include "url/gurl.h" |
| #include "url/origin.h" |
| |
| namespace web_app { |
| |
| namespace { |
| |
| ShareTarget_Method MethodToProto(apps::ShareTarget::Method method) { |
| switch (method) { |
| case apps::ShareTarget::Method::kGet: |
| return ShareTarget_Method_GET; |
| case apps::ShareTarget::Method::kPost: |
| return ShareTarget_Method_POST; |
| } |
| } |
| |
| apps::ShareTarget::Method ProtoToMethod(ShareTarget_Method method) { |
| switch (method) { |
| case ShareTarget_Method_GET: |
| return apps::ShareTarget::Method::kGet; |
| case ShareTarget_Method_POST: |
| return apps::ShareTarget::Method::kPost; |
| } |
| } |
| |
| ShareTarget_Enctype EnctypeToProto(apps::ShareTarget::Enctype enctype) { |
| switch (enctype) { |
| case apps::ShareTarget::Enctype::kFormUrlEncoded: |
| return ShareTarget_Enctype_FORM_URL_ENCODED; |
| case apps::ShareTarget::Enctype::kMultipartFormData: |
| return ShareTarget_Enctype_MULTIPART_FORM_DATA; |
| } |
| } |
| |
| apps::ShareTarget::Enctype ProtoToEnctype(ShareTarget_Enctype enctype) { |
| switch (enctype) { |
| case ShareTarget_Enctype_FORM_URL_ENCODED: |
| return apps::ShareTarget::Enctype::kFormUrlEncoded; |
| case ShareTarget_Enctype_MULTIPART_FORM_DATA: |
| return apps::ShareTarget::Enctype::kMultipartFormData; |
| } |
| } |
| |
| blink::mojom::CaptureLinks ProtoToCaptureLinks( |
| WebAppProto::CaptureLinks capture_links) { |
| switch (capture_links) { |
| case WebAppProto_CaptureLinks_NONE: |
| return blink::mojom::CaptureLinks::kNone; |
| case WebAppProto_CaptureLinks_NEW_CLIENT: |
| return blink::mojom::CaptureLinks::kNewClient; |
| case WebAppProto_CaptureLinks_EXISTING_CLIENT_NAVIGATE: |
| return blink::mojom::CaptureLinks::kExistingClientNavigate; |
| } |
| } |
| |
| WebAppProto::CaptureLinks CaptureLinksToProto( |
| blink::mojom::CaptureLinks capture_links) { |
| switch (capture_links) { |
| case blink::mojom::CaptureLinks::kUndefined: |
| NOTREACHED(); |
| FALLTHROUGH; |
| case blink::mojom::CaptureLinks::kNone: |
| return WebAppProto_CaptureLinks_NONE; |
| case blink::mojom::CaptureLinks::kNewClient: |
| return WebAppProto_CaptureLinks_NEW_CLIENT; |
| case blink::mojom::CaptureLinks::kExistingClientNavigate: |
| return WebAppProto_CaptureLinks_EXISTING_CLIENT_NAVIGATE; |
| } |
| } |
| |
| LaunchHandler::RouteTo ProtoToLaunchHandlerRouteTo( |
| const LaunchHandlerProto::RouteTo& route_to) { |
| switch (route_to) { |
| case LaunchHandlerProto_RouteTo_UNSPECIFIED_ROUTE: |
| case LaunchHandlerProto_RouteTo_AUTO: |
| return LaunchHandler::RouteTo::kAuto; |
| case LaunchHandlerProto_RouteTo_NEW_CLIENT: |
| return LaunchHandler::RouteTo::kNewClient; |
| case LaunchHandlerProto_RouteTo_EXISTING_CLIENT: |
| return LaunchHandler::RouteTo::kExistingClient; |
| } |
| } |
| |
| LaunchHandlerProto::RouteTo LaunchHandlerRouteToToProto( |
| const LaunchHandler::RouteTo& route_to) { |
| switch (route_to) { |
| case LaunchHandler::RouteTo::kAuto: |
| return LaunchHandlerProto_RouteTo_AUTO; |
| case LaunchHandler::RouteTo::kNewClient: |
| return LaunchHandlerProto_RouteTo_NEW_CLIENT; |
| case LaunchHandler::RouteTo::kExistingClient: |
| return LaunchHandlerProto_RouteTo_EXISTING_CLIENT; |
| } |
| } |
| |
| LaunchHandler::NavigateExistingClient |
| ProtoToLaunchHandlerNavigateExistingClient( |
| const LaunchHandlerProto::NavigateExistingClient& |
| navigate_existing_client) { |
| switch (navigate_existing_client) { |
| case LaunchHandlerProto_NavigateExistingClient_UNSPECIFIED_NAVIGATE: |
| case LaunchHandlerProto_NavigateExistingClient_ALWAYS: |
| return LaunchHandler::NavigateExistingClient::kAlways; |
| case LaunchHandlerProto_NavigateExistingClient_NEVER: |
| return LaunchHandler::NavigateExistingClient::kNever; |
| } |
| } |
| |
| LaunchHandlerProto::NavigateExistingClient |
| LaunchHandlerNavigateExistingClientToProto( |
| const LaunchHandler::NavigateExistingClient& navigate_existing_client) { |
| switch (navigate_existing_client) { |
| case LaunchHandler::NavigateExistingClient::kAlways: |
| return LaunchHandlerProto_NavigateExistingClient_ALWAYS; |
| case LaunchHandler::NavigateExistingClient::kNever: |
| return LaunchHandlerProto_NavigateExistingClient_NEVER; |
| } |
| } |
| |
| ApiApprovalState ProtoToApiApprovalState( |
| WebAppProto::ApiApprovalState approval_state) { |
| switch (approval_state) { |
| case WebAppProto_ApiApprovalState_REQUIRES_PROMPT: |
| return ApiApprovalState::kRequiresPrompt; |
| case WebAppProto_ApiApprovalState_ALLOWED: |
| return ApiApprovalState::kAllowed; |
| case WebAppProto_ApiApprovalState_DISALLOWED: |
| return ApiApprovalState::kDisallowed; |
| } |
| } |
| |
| WebAppProto::ApiApprovalState ApiApprovalStateToProto( |
| ApiApprovalState approval_state) { |
| switch (approval_state) { |
| case ApiApprovalState::kRequiresPrompt: |
| return WebAppProto_ApiApprovalState_REQUIRES_PROMPT; |
| case ApiApprovalState::kAllowed: |
| return WebAppProto_ApiApprovalState_ALLOWED; |
| case ApiApprovalState::kDisallowed: |
| return WebAppProto_ApiApprovalState_DISALLOWED; |
| } |
| } |
| |
| } // anonymous namespace |
| |
| WebAppDatabase::WebAppDatabase(AbstractWebAppDatabaseFactory* database_factory, |
| ReportErrorCallback error_callback) |
| : database_factory_(database_factory), |
| error_callback_(std::move(error_callback)) { |
| DCHECK(database_factory_); |
| } |
| |
| WebAppDatabase::~WebAppDatabase() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| } |
| |
| void WebAppDatabase::OpenDatabase(RegistryOpenedCallback callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(!store_); |
| |
| syncer::OnceModelTypeStoreFactory store_factory = |
| database_factory_->GetStoreFactory(); |
| |
| std::move(store_factory) |
| .Run(syncer::WEB_APPS, |
| base::BindOnce(&WebAppDatabase::OnDatabaseOpened, |
| weak_ptr_factory_.GetWeakPtr(), std::move(callback))); |
| } |
| |
| void WebAppDatabase::Write( |
| const RegistryUpdateData& update_data, |
| std::unique_ptr<syncer::MetadataChangeList> metadata_change_list, |
| CompletionCallback callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| CHECK(opened_); |
| |
| std::unique_ptr<syncer::ModelTypeStore::WriteBatch> write_batch = |
| store_->CreateWriteBatch(); |
| |
| // |update_data| can be empty here but we should write |metadata_change_list| |
| // anyway. |
| write_batch->TakeMetadataChangesFrom(std::move(metadata_change_list)); |
| |
| for (const std::unique_ptr<WebApp>& web_app : update_data.apps_to_create) { |
| auto proto = CreateWebAppProto(*web_app); |
| write_batch->WriteData(web_app->app_id(), proto->SerializeAsString()); |
| } |
| |
| for (const std::unique_ptr<WebApp>& web_app : update_data.apps_to_update) { |
| auto proto = CreateWebAppProto(*web_app); |
| write_batch->WriteData(web_app->app_id(), proto->SerializeAsString()); |
| } |
| |
| for (const AppId& app_id : update_data.apps_to_delete) |
| write_batch->DeleteData(app_id); |
| |
| store_->CommitWriteBatch( |
| std::move(write_batch), |
| base::BindOnce(&WebAppDatabase::OnDataWritten, |
| weak_ptr_factory_.GetWeakPtr(), std::move(callback))); |
| } |
| |
| // static |
| std::unique_ptr<WebAppProto> WebAppDatabase::CreateWebAppProto( |
| const WebApp& web_app) { |
| auto local_data = std::make_unique<WebAppProto>(); |
| |
| // Required fields: |
| const GURL start_url = web_app.start_url(); |
| DCHECK(!start_url.is_empty() && start_url.is_valid()); |
| |
| DCHECK(!web_app.app_id().empty()); |
| |
| // Set sync data to sync proto. |
| *(local_data->mutable_sync_data()) = WebAppToSyncProto(web_app); |
| |
| local_data->set_name(web_app.name()); |
| |
| DCHECK(web_app.sources_.any()); |
| local_data->mutable_sources()->set_system(web_app.sources_[Source::kSystem]); |
| local_data->mutable_sources()->set_policy(web_app.sources_[Source::kPolicy]); |
| local_data->mutable_sources()->set_web_app_store( |
| web_app.sources_[Source::kWebAppStore]); |
| local_data->mutable_sources()->set_sync(web_app.sources_[Source::kSync]); |
| local_data->mutable_sources()->set_default_( |
| web_app.sources_[Source::kDefault]); |
| local_data->mutable_sources()->set_sub_app(web_app.sources_[Source::kSubApp]); |
| |
| local_data->set_is_locally_installed(web_app.is_locally_installed()); |
| |
| // Optional fields: |
| if (web_app.launch_query_params()) |
| local_data->set_launch_query_params(*web_app.launch_query_params()); |
| |
| if (web_app.display_mode() != DisplayMode::kUndefined) { |
| local_data->set_display_mode( |
| ToWebAppProtoDisplayMode(web_app.display_mode())); |
| } |
| |
| for (const DisplayMode& display_mode : web_app.display_mode_override()) { |
| local_data->add_display_mode_override( |
| ToWebAppProtoDisplayMode(display_mode)); |
| } |
| |
| local_data->set_description(web_app.description()); |
| if (!web_app.scope().is_empty()) |
| local_data->set_scope(web_app.scope().spec()); |
| if (web_app.theme_color().has_value()) |
| local_data->set_theme_color(web_app.theme_color().value()); |
| if (web_app.dark_mode_theme_color().has_value()) |
| local_data->set_dark_mode_theme_color( |
| web_app.dark_mode_theme_color().value()); |
| if (web_app.background_color().has_value()) |
| local_data->set_background_color(web_app.background_color().value()); |
| if (web_app.dark_mode_background_color().has_value()) { |
| local_data->set_dark_mode_background_color( |
| web_app.dark_mode_background_color().value()); |
| } |
| if (!web_app.last_badging_time().is_null()) { |
| local_data->set_last_badging_time( |
| syncer::TimeToProtoTime(web_app.last_badging_time())); |
| } |
| if (!web_app.last_launch_time().is_null()) { |
| local_data->set_last_launch_time( |
| syncer::TimeToProtoTime(web_app.last_launch_time())); |
| } |
| if (!web_app.install_time().is_null()) { |
| local_data->set_install_time( |
| syncer::TimeToProtoTime(web_app.install_time())); |
| } |
| if (!web_app.manifest_update_time().is_null()) { |
| local_data->set_manifest_update_time( |
| syncer::TimeToProtoTime(web_app.manifest_update_time())); |
| } |
| |
| if (web_app.chromeos_data().has_value()) { |
| auto& chromeos_data = web_app.chromeos_data().value(); |
| auto* mutable_chromeos_data = local_data->mutable_chromeos_data(); |
| mutable_chromeos_data->set_show_in_launcher(chromeos_data.show_in_launcher); |
| mutable_chromeos_data->set_show_in_search(chromeos_data.show_in_search); |
| mutable_chromeos_data->set_show_in_management( |
| chromeos_data.show_in_management); |
| mutable_chromeos_data->set_is_disabled(chromeos_data.is_disabled); |
| mutable_chromeos_data->set_oem_installed(chromeos_data.oem_installed); |
| } |
| |
| if (web_app.client_data().system_web_app_data.has_value()) { |
| auto& swa_data = web_app.client_data().system_web_app_data.value(); |
| |
| auto* mutable_swa_data = |
| local_data->mutable_client_data()->mutable_system_web_app_data(); |
| mutable_swa_data->set_system_app_type( |
| static_cast<SystemWebAppDataProto_SystemAppType>( |
| swa_data.system_app_type)); |
| } |
| |
| local_data->set_user_run_on_os_login_mode( |
| ToWebAppProtoRunOnOsLoginMode(web_app.run_on_os_login_mode())); |
| local_data->set_is_from_sync_and_pending_installation( |
| web_app.is_from_sync_and_pending_installation()); |
| |
| for (const apps::IconInfo& icon_info : web_app.manifest_icons()) |
| *(local_data->add_manifest_icons()) = AppIconInfoToSyncProto(icon_info); |
| |
| for (SquareSizePx size : web_app.downloaded_icon_sizes(IconPurpose::ANY)) { |
| local_data->add_downloaded_icon_sizes_purpose_any(size); |
| } |
| for (SquareSizePx size : |
| web_app.downloaded_icon_sizes(IconPurpose::MASKABLE)) { |
| local_data->add_downloaded_icon_sizes_purpose_maskable(size); |
| } |
| for (SquareSizePx size : |
| web_app.downloaded_icon_sizes(IconPurpose::MONOCHROME)) { |
| local_data->add_downloaded_icon_sizes_purpose_monochrome(size); |
| } |
| |
| local_data->set_is_generated_icon(web_app.is_generated_icon()); |
| |
| for (const auto& file_handler : web_app.file_handlers()) { |
| WebAppFileHandlerProto* file_handler_proto = |
| local_data->add_file_handlers(); |
| file_handler_proto->set_action(file_handler.action.spec()); |
| file_handler_proto->set_display_name( |
| base::UTF16ToUTF8(file_handler.display_name)); |
| |
| for (const auto& accept_entry : file_handler.accept) { |
| WebAppFileHandlerAcceptProto* accept_entry_proto = |
| file_handler_proto->add_accept(); |
| accept_entry_proto->set_mimetype(accept_entry.mime_type); |
| |
| for (const auto& file_extension : accept_entry.file_extensions) |
| accept_entry_proto->add_file_extensions(file_extension); |
| } |
| |
| for (const apps::IconInfo& icon_info : file_handler.downloaded_icons) { |
| *(file_handler_proto->add_downloaded_icons()) = |
| AppIconInfoToSyncProto(icon_info); |
| } |
| } |
| |
| if (web_app.share_target()) { |
| const apps::ShareTarget& share_target = *web_app.share_target(); |
| auto* const mutable_share_target = local_data->mutable_share_target(); |
| mutable_share_target->set_action(share_target.action.spec()); |
| mutable_share_target->set_method(MethodToProto(share_target.method)); |
| mutable_share_target->set_enctype(EnctypeToProto(share_target.enctype)); |
| |
| const apps::ShareTarget::Params& params = share_target.params; |
| auto* const mutable_share_target_params = |
| mutable_share_target->mutable_params(); |
| if (!params.title.empty()) |
| mutable_share_target_params->set_title(params.title); |
| if (!params.text.empty()) |
| mutable_share_target_params->set_text(params.text); |
| if (!params.url.empty()) |
| mutable_share_target_params->set_url(params.url); |
| |
| for (const auto& files_entry : params.files) { |
| ShareTargetParamsFile* mutable_share_target_files = |
| mutable_share_target_params->add_files(); |
| mutable_share_target_files->set_name(files_entry.name); |
| |
| for (const auto& file_type : files_entry.accept) |
| mutable_share_target_files->add_accept(file_type); |
| } |
| } |
| |
| for (const WebApplicationShortcutsMenuItemInfo& shortcut_info : |
| web_app.shortcuts_menu_item_infos()) { |
| WebAppShortcutsMenuItemInfoProto* shortcut_info_proto = |
| local_data->add_shortcuts_menu_item_infos(); |
| shortcut_info_proto->set_name(base::UTF16ToUTF8(shortcut_info.name)); |
| shortcut_info_proto->set_url(shortcut_info.url.spec()); |
| for (IconPurpose purpose : kIconPurposes) { |
| for (const WebApplicationShortcutsMenuItemInfo::Icon& icon_info : |
| shortcut_info.GetShortcutIconInfosForPurpose(purpose)) { |
| sync_pb::WebAppIconInfo* shortcut_icon_info_proto; |
| switch (purpose) { |
| case IconPurpose::ANY: |
| shortcut_icon_info_proto = |
| shortcut_info_proto->add_shortcut_manifest_icons(); |
| break; |
| case IconPurpose::MASKABLE: |
| shortcut_icon_info_proto = |
| shortcut_info_proto->add_shortcut_manifest_icons_maskable(); |
| break; |
| case IconPurpose::MONOCHROME: |
| shortcut_icon_info_proto = |
| shortcut_info_proto->add_shortcut_manifest_icons_monochrome(); |
| break; |
| } |
| |
| DCHECK(!icon_info.url.is_empty()); |
| shortcut_icon_info_proto->set_url(icon_info.url.spec()); |
| shortcut_icon_info_proto->set_size_in_px(icon_info.square_size_px); |
| } |
| } |
| } |
| |
| for (const IconSizes& icon_sizes : |
| web_app.downloaded_shortcuts_menu_icons_sizes()) { |
| DownloadedShortcutsMenuIconSizesProto* icon_sizes_proto = |
| local_data->add_downloaded_shortcuts_menu_icons_sizes(); |
| for (const SquareSizePx& icon_size : |
| icon_sizes.GetSizesForPurpose(IconPurpose::ANY)) { |
| icon_sizes_proto->add_icon_sizes(icon_size); |
| } |
| for (const SquareSizePx& icon_size : |
| icon_sizes.GetSizesForPurpose(IconPurpose::MASKABLE)) { |
| icon_sizes_proto->add_icon_sizes_maskable(icon_size); |
| } |
| for (const SquareSizePx& icon_size : |
| icon_sizes.GetSizesForPurpose(IconPurpose::MONOCHROME)) { |
| icon_sizes_proto->add_icon_sizes_monochrome(icon_size); |
| } |
| } |
| |
| for (const auto& additional_search_term : web_app.additional_search_terms()) { |
| // Additional search terms should be sanitized before being added here. |
| DCHECK(!additional_search_term.empty()); |
| local_data->add_additional_search_terms(additional_search_term); |
| } |
| |
| for (const auto& protocol_handler : web_app.protocol_handlers()) { |
| WebAppProtocolHandler* protocol_handler_proto = |
| local_data->add_protocol_handlers(); |
| protocol_handler_proto->set_protocol(protocol_handler.protocol); |
| protocol_handler_proto->set_url(protocol_handler.url.spec()); |
| } |
| |
| for (const auto& allowed_launch_protocols : |
| web_app.allowed_launch_protocols()) { |
| DCHECK(!allowed_launch_protocols.empty()); |
| local_data->add_allowed_launch_protocols(allowed_launch_protocols); |
| } |
| |
| for (const auto& disallowed_launch_protocols : |
| web_app.disallowed_launch_protocols()) { |
| DCHECK(!disallowed_launch_protocols.empty()); |
| local_data->add_disallowed_launch_protocols(disallowed_launch_protocols); |
| } |
| |
| for (const auto& url_handler : web_app.url_handlers()) { |
| WebAppUrlHandlerProto* url_handler_proto = local_data->add_url_handlers(); |
| url_handler_proto->set_origin(url_handler.origin.Serialize()); |
| url_handler_proto->set_has_origin_wildcard(url_handler.has_origin_wildcard); |
| } |
| |
| if (web_app.note_taking_new_note_url().is_valid()) { |
| local_data->set_note_taking_new_note_url( |
| web_app.note_taking_new_note_url().spec()); |
| } |
| |
| if (web_app.capture_links() != blink::mojom::CaptureLinks::kUndefined) |
| local_data->set_capture_links(CaptureLinksToProto(web_app.capture_links())); |
| else |
| local_data->clear_capture_links(); |
| |
| if (!web_app.manifest_url().is_empty()) |
| local_data->set_manifest_url(web_app.manifest_url().spec()); |
| |
| local_data->set_file_handler_permission_blocked( |
| web_app.file_handler_permission_blocked()); |
| |
| local_data->set_file_handler_approval_state( |
| ApiApprovalStateToProto(web_app.file_handler_approval_state())); |
| |
| local_data->set_window_controls_overlay_enabled( |
| web_app.window_controls_overlay_enabled()); |
| |
| local_data->set_is_storage_isolated(web_app.IsStorageIsolated()); |
| |
| if (web_app.launch_handler()) { |
| LaunchHandlerProto& launch_handler_proto = |
| *local_data->mutable_launch_handler(); |
| launch_handler_proto.set_route_to( |
| LaunchHandlerRouteToToProto(web_app.launch_handler()->route_to)); |
| launch_handler_proto.set_navigate_existing_client( |
| LaunchHandlerNavigateExistingClientToProto( |
| web_app.launch_handler()->navigate_existing_client)); |
| } |
| |
| if (web_app.parent_app_id_) { |
| local_data->set_parent_app_id(*web_app.parent_app_id_); |
| } |
| |
| return local_data; |
| } |
| |
| // static |
| std::unique_ptr<WebApp> WebAppDatabase::CreateWebApp( |
| const WebAppProto& local_data) { |
| if (!local_data.has_sync_data()) { |
| DLOG(ERROR) << "WebApp proto parse error: no sync_data field"; |
| return nullptr; |
| } |
| |
| const sync_pb::WebAppSpecifics& sync_data = local_data.sync_data(); |
| |
| // AppId is a hash of start_url. Read start_url first: |
| GURL start_url(sync_data.start_url()); |
| if (start_url.is_empty() || !start_url.is_valid()) { |
| DLOG(ERROR) << "WebApp proto start_url parse error: " |
| << start_url.possibly_invalid_spec(); |
| return nullptr; |
| } |
| |
| absl::optional<std::string> manifest_id = absl::nullopt; |
| if (sync_data.has_manifest_id()) |
| manifest_id = absl::optional<std::string>(sync_data.manifest_id()); |
| |
| const AppId app_id = GenerateAppId(manifest_id, start_url); |
| |
| auto web_app = std::make_unique<WebApp>(app_id); |
| web_app->SetStartUrl(start_url); |
| |
| web_app->SetManifestId(manifest_id); |
| |
| // Required fields: |
| if (!local_data.has_sources()) { |
| DLOG(ERROR) << "WebApp proto parse error: no sources field"; |
| return nullptr; |
| } |
| |
| WebApp::Sources sources; |
| sources[Source::kSystem] = local_data.sources().system(); |
| sources[Source::kPolicy] = local_data.sources().policy(); |
| sources[Source::kWebAppStore] = local_data.sources().web_app_store(); |
| sources[Source::kSync] = local_data.sources().sync(); |
| sources[Source::kDefault] = local_data.sources().default_(); |
| if (local_data.sources().has_sub_app()) { |
| sources[Source::kSubApp] = local_data.sources().sub_app(); |
| } |
| if (!sources.any()) { |
| DLOG(ERROR) << "WebApp proto parse error: no any source in sources field"; |
| return nullptr; |
| } |
| web_app->sources_ = sources; |
| |
| if (!local_data.has_name()) { |
| DLOG(ERROR) << "WebApp proto parse error: no name field"; |
| return nullptr; |
| } |
| web_app->SetName(local_data.name()); |
| |
| if (!sync_data.has_user_display_mode()) { |
| DLOG(ERROR) << "WebApp proto parse error: no user_display_mode field"; |
| return nullptr; |
| } |
| web_app->SetUserDisplayMode( |
| ToMojomDisplayMode(sync_data.user_display_mode())); |
| |
| // Ordinals used for chrome://apps page. |
| syncer::StringOrdinal page_ordinal = |
| syncer::StringOrdinal(sync_data.user_page_ordinal()); |
| if (!page_ordinal.IsValid()) |
| page_ordinal = syncer::StringOrdinal(); |
| syncer::StringOrdinal launch_ordinal = |
| syncer::StringOrdinal(sync_data.user_launch_ordinal()); |
| if (!launch_ordinal.IsValid()) |
| launch_ordinal = syncer::StringOrdinal(); |
| web_app->SetUserPageOrdinal(page_ordinal); |
| web_app->SetUserLaunchOrdinal(launch_ordinal); |
| |
| if (!local_data.has_is_locally_installed()) { |
| DLOG(ERROR) << "WebApp proto parse error: no is_locally_installed field"; |
| return nullptr; |
| } |
| web_app->SetIsLocallyInstalled(local_data.is_locally_installed()); |
| |
| auto& chromeos_data_proto = local_data.chromeos_data(); |
| |
| if (IsChromeOsDataMandatory() && !local_data.has_chromeos_data()) { |
| DLOG(ERROR) << "WebApp proto parse error: no chromeos_data field. The web " |
| << "app might have been installed when running on an OS other " |
| << "than Chrome OS."; |
| return nullptr; |
| } |
| |
| if (!IsChromeOsDataMandatory() && local_data.has_chromeos_data()) { |
| DLOG(ERROR) << "WebApp proto parse error: has chromeos_data field. The web " |
| << "app might have been installed when running on Chrome OS."; |
| return nullptr; |
| } |
| |
| if (local_data.has_chromeos_data()) { |
| auto chromeos_data = absl::make_optional<WebAppChromeOsData>(); |
| chromeos_data->show_in_launcher = chromeos_data_proto.show_in_launcher(); |
| chromeos_data->show_in_search = chromeos_data_proto.show_in_search(); |
| chromeos_data->show_in_management = |
| chromeos_data_proto.show_in_management(); |
| chromeos_data->is_disabled = chromeos_data_proto.is_disabled(); |
| chromeos_data->oem_installed = chromeos_data_proto.oem_installed(); |
| web_app->SetWebAppChromeOsData(std::move(chromeos_data)); |
| } |
| |
| if (local_data.client_data().has_system_web_app_data()) { |
| WebAppSystemWebAppData& swa_data = |
| web_app->client_data()->system_web_app_data.emplace(); |
| |
| swa_data.system_app_type = static_cast<SystemAppType>( |
| local_data.client_data().system_web_app_data().system_app_type()); |
| } |
| |
| // Optional fields: |
| if (local_data.has_launch_query_params()) |
| web_app->SetLaunchQueryParams(local_data.launch_query_params()); |
| |
| if (local_data.has_display_mode()) |
| web_app->SetDisplayMode(ToMojomDisplayMode(local_data.display_mode())); |
| |
| std::vector<DisplayMode> display_mode_override; |
| for (int i = 0; i < local_data.display_mode_override_size(); i++) { |
| WebAppProto::DisplayMode display_mode = local_data.display_mode_override(i); |
| display_mode_override.push_back(ToMojomDisplayMode(display_mode)); |
| } |
| web_app->SetDisplayModeOverride(std::move(display_mode_override)); |
| |
| if (local_data.has_description()) |
| web_app->SetDescription(local_data.description()); |
| |
| if (local_data.has_scope()) { |
| GURL scope(local_data.scope()); |
| if (scope.is_empty() || !scope.is_valid()) { |
| DLOG(ERROR) << "WebApp proto scope parse error: " |
| << scope.possibly_invalid_spec(); |
| return nullptr; |
| } |
| web_app->SetScope(scope); |
| } |
| |
| if (local_data.has_theme_color()) { |
| web_app->SetThemeColor(local_data.theme_color()); |
| } |
| |
| if (local_data.has_dark_mode_theme_color()) { |
| web_app->SetDarkModeThemeColor(local_data.dark_mode_theme_color()); |
| } |
| |
| if (local_data.has_background_color()) { |
| web_app->SetBackgroundColor(local_data.background_color()); |
| } |
| |
| if (local_data.has_dark_mode_background_color()) { |
| web_app->SetDarkModeBackgroundColor( |
| local_data.dark_mode_background_color()); |
| } |
| |
| if (local_data.has_is_from_sync_and_pending_installation()) |
| web_app->SetIsFromSyncAndPendingInstallation( |
| local_data.is_from_sync_and_pending_installation()); |
| |
| if (local_data.has_last_badging_time()) { |
| web_app->SetLastBadgingTime( |
| syncer::ProtoTimeToTime(local_data.last_badging_time())); |
| } |
| if (local_data.has_last_launch_time()) { |
| web_app->SetLastLaunchTime( |
| syncer::ProtoTimeToTime(local_data.last_launch_time())); |
| } |
| if (local_data.has_install_time()) { |
| web_app->SetInstallTime(syncer::ProtoTimeToTime(local_data.install_time())); |
| } |
| if (local_data.has_manifest_update_time()) { |
| web_app->SetManifestUpdateTime( |
| syncer::ProtoTimeToTime(local_data.manifest_update_time())); |
| } |
| |
| absl::optional<WebApp::SyncFallbackData> parsed_sync_fallback_data = |
| ParseSyncFallbackDataStruct(sync_data); |
| if (!parsed_sync_fallback_data.has_value()) { |
| // ParseSyncFallbackDataStruct() reports any errors. |
| return nullptr; |
| } |
| web_app->SetSyncFallbackData(std::move(parsed_sync_fallback_data.value())); |
| |
| absl::optional<std::vector<apps::IconInfo>> parsed_manifest_icons = |
| ParseAppIconInfos("WebApp", local_data.manifest_icons()); |
| if (!parsed_manifest_icons) { |
| // ParseWebAppIconInfos() reports any errors. |
| return nullptr; |
| } |
| web_app->SetManifestIcons(std::move(parsed_manifest_icons.value())); |
| |
| std::vector<SquareSizePx> icon_sizes_any; |
| for (int32_t size : local_data.downloaded_icon_sizes_purpose_any()) |
| icon_sizes_any.push_back(size); |
| web_app->SetDownloadedIconSizes(IconPurpose::ANY, |
| SortedSizesPx(std::move(icon_sizes_any))); |
| |
| std::vector<SquareSizePx> icon_sizes_maskable; |
| for (int32_t size : local_data.downloaded_icon_sizes_purpose_maskable()) |
| icon_sizes_maskable.push_back(size); |
| web_app->SetDownloadedIconSizes( |
| IconPurpose::MASKABLE, SortedSizesPx(std::move(icon_sizes_maskable))); |
| |
| std::vector<SquareSizePx> icon_sizes_monochrome; |
| for (int32_t size : local_data.downloaded_icon_sizes_purpose_monochrome()) |
| icon_sizes_monochrome.push_back(size); |
| web_app->SetDownloadedIconSizes( |
| IconPurpose::MONOCHROME, SortedSizesPx(std::move(icon_sizes_monochrome))); |
| |
| web_app->SetIsGeneratedIcon(local_data.is_generated_icon()); |
| |
| apps::FileHandlers file_handlers; |
| for (const auto& file_handler_proto : local_data.file_handlers()) { |
| apps::FileHandler file_handler; |
| file_handler.action = GURL(file_handler_proto.action()); |
| |
| if (file_handler.action.is_empty() || !file_handler.action.is_valid()) { |
| DLOG(ERROR) << "WebApp FileHandler proto action parse error"; |
| return nullptr; |
| } |
| |
| if (file_handler_proto.has_display_name()) { |
| file_handler.display_name = |
| base::UTF8ToUTF16(file_handler_proto.display_name()); |
| } |
| |
| for (const auto& accept_entry_proto : file_handler_proto.accept()) { |
| apps::FileHandler::AcceptEntry accept_entry; |
| accept_entry.mime_type = accept_entry_proto.mimetype(); |
| for (const auto& file_extension : accept_entry_proto.file_extensions()) { |
| if (base::Contains(accept_entry.file_extensions, file_extension)) { |
| // We intentionally don't return a nullptr here; instead, duplicate |
| // entries are absorbed. |
| DLOG(ERROR) << "apps::FileHandler::AcceptEntry parsing encountered " |
| << "duplicate file extension"; |
| } |
| accept_entry.file_extensions.insert(file_extension); |
| } |
| file_handler.accept.push_back(std::move(accept_entry)); |
| } |
| |
| if (WebAppFileHandlerManager::IconsEnabled()) { |
| absl::optional<std::vector<apps::IconInfo>> file_handler_icon_infos = |
| ParseAppIconInfos("WebApp", file_handler_proto.downloaded_icons()); |
| if (!file_handler_icon_infos) { |
| // ParseAppIconInfos() reports any errors. |
| return nullptr; |
| } |
| file_handler.downloaded_icons = |
| std::move(file_handler_icon_infos.value()); |
| } |
| |
| file_handlers.push_back(std::move(file_handler)); |
| } |
| web_app->SetFileHandlers(std::move(file_handlers)); |
| |
| if (local_data.has_share_target()) { |
| apps::ShareTarget share_target; |
| const ShareTarget& local_share_target = local_data.share_target(); |
| const ShareTargetParams& local_share_target_params = |
| local_share_target.params(); |
| |
| GURL action(local_share_target.action()); |
| if (action.is_empty() || !action.is_valid()) { |
| DLOG(ERROR) << "WebApp proto action parse error: " |
| << action.possibly_invalid_spec(); |
| return nullptr; |
| } |
| |
| share_target.action = action; |
| share_target.method = ProtoToMethod(local_share_target.method()); |
| share_target.enctype = ProtoToEnctype(local_share_target.enctype()); |
| |
| if (local_share_target_params.has_title()) |
| share_target.params.title = local_share_target_params.title(); |
| if (local_share_target_params.has_text()) |
| share_target.params.text = local_share_target_params.text(); |
| if (local_share_target_params.has_url()) |
| share_target.params.url = local_share_target_params.url(); |
| |
| for (const auto& share_target_params_file : |
| local_share_target_params.files()) { |
| apps::ShareTarget::Files files_entry; |
| files_entry.name = share_target_params_file.name(); |
| for (const auto& file_type : share_target_params_file.accept()) { |
| if (base::Contains(files_entry.accept, file_type)) { |
| // We intentionally don't return a nullptr here; instead, duplicate |
| // entries are absorbed. |
| DLOG(ERROR) << "apps::ShareTarget::Files parsing encountered " |
| << "duplicate file type"; |
| } else { |
| files_entry.accept.push_back(file_type); |
| } |
| } |
| share_target.params.files.push_back(std::move(files_entry)); |
| } |
| |
| web_app->SetShareTarget(std::move(share_target)); |
| } |
| |
| std::vector<WebApplicationShortcutsMenuItemInfo> shortcuts_menu_item_infos; |
| for (const auto& shortcut_info_proto : |
| local_data.shortcuts_menu_item_infos()) { |
| WebApplicationShortcutsMenuItemInfo shortcut_info; |
| shortcut_info.name = base::UTF8ToUTF16(shortcut_info_proto.name()); |
| shortcut_info.url = GURL(shortcut_info_proto.url()); |
| for (IconPurpose purpose : kIconPurposes) { |
| // This default init needed to infer the sophisticated protobuf type. |
| const auto* shortcut_manifest_icons = |
| &shortcut_info_proto.shortcut_manifest_icons(); |
| |
| switch (purpose) { |
| case IconPurpose::ANY: |
| shortcut_manifest_icons = |
| &shortcut_info_proto.shortcut_manifest_icons(); |
| break; |
| case IconPurpose::MASKABLE: |
| shortcut_manifest_icons = |
| &shortcut_info_proto.shortcut_manifest_icons_maskable(); |
| break; |
| case IconPurpose::MONOCHROME: |
| shortcut_manifest_icons = |
| &shortcut_info_proto.shortcut_manifest_icons_monochrome(); |
| break; |
| } |
| |
| std::vector<WebApplicationShortcutsMenuItemInfo::Icon> manifest_icons; |
| for (const auto& icon_info_proto : *shortcut_manifest_icons) { |
| WebApplicationShortcutsMenuItemInfo::Icon shortcut_icon_info; |
| shortcut_icon_info.square_size_px = icon_info_proto.size_in_px(); |
| shortcut_icon_info.url = GURL(icon_info_proto.url()); |
| manifest_icons.emplace_back(std::move(shortcut_icon_info)); |
| } |
| shortcut_info.SetShortcutIconInfosForPurpose(purpose, |
| std::move(manifest_icons)); |
| } |
| shortcuts_menu_item_infos.emplace_back(std::move(shortcut_info)); |
| } |
| web_app->SetShortcutsMenuItemInfos(std::move(shortcuts_menu_item_infos)); |
| |
| std::vector<IconSizes> shortcuts_menu_icons_sizes; |
| for (const auto& shortcuts_icon_sizes_proto : |
| local_data.downloaded_shortcuts_menu_icons_sizes()) { |
| IconSizes icon_sizes; |
| icon_sizes.SetSizesForPurpose( |
| IconPurpose::ANY, std::vector<SquareSizePx>( |
| shortcuts_icon_sizes_proto.icon_sizes().begin(), |
| shortcuts_icon_sizes_proto.icon_sizes().end())); |
| icon_sizes.SetSizesForPurpose( |
| IconPurpose::MASKABLE, |
| std::vector<SquareSizePx>( |
| shortcuts_icon_sizes_proto.icon_sizes_maskable().begin(), |
| shortcuts_icon_sizes_proto.icon_sizes_maskable().end())); |
| icon_sizes.SetSizesForPurpose( |
| IconPurpose::MONOCHROME, |
| std::vector<SquareSizePx>( |
| shortcuts_icon_sizes_proto.icon_sizes_monochrome().begin(), |
| shortcuts_icon_sizes_proto.icon_sizes_monochrome().end())); |
| |
| shortcuts_menu_icons_sizes.push_back(std::move(icon_sizes)); |
| } |
| web_app->SetDownloadedShortcutsMenuIconsSizes( |
| std::move(shortcuts_menu_icons_sizes)); |
| |
| std::vector<std::string> additional_search_terms; |
| for (const std::string& additional_search_term : |
| local_data.additional_search_terms()) { |
| if (additional_search_term.empty()) { |
| DLOG(ERROR) << "WebApp AdditionalSearchTerms proto action parse error"; |
| return nullptr; |
| } |
| additional_search_terms.push_back(additional_search_term); |
| } |
| web_app->SetAdditionalSearchTerms(std::move(additional_search_terms)); |
| |
| std::vector<apps::ProtocolHandlerInfo> protocol_handlers; |
| for (const auto& protocol_handler_proto : local_data.protocol_handlers()) { |
| apps::ProtocolHandlerInfo protocol_handler; |
| protocol_handler.protocol = protocol_handler_proto.protocol(); |
| GURL protocol_handler_url(protocol_handler_proto.url()); |
| if (protocol_handler_url.is_empty() || !protocol_handler_url.is_valid()) { |
| DLOG(ERROR) << "WebApp ProtocolHandler proto url parse error: " |
| << protocol_handler_url.possibly_invalid_spec(); |
| return nullptr; |
| } |
| protocol_handler.url = protocol_handler_url; |
| |
| protocol_handlers.push_back(std::move(protocol_handler)); |
| } |
| web_app->SetProtocolHandlers(std::move(protocol_handlers)); |
| |
| std::vector<std::string> allowed_launch_protocols; |
| for (const std::string& allowed_launch_protocol : |
| local_data.allowed_launch_protocols()) { |
| if (allowed_launch_protocol.empty()) { |
| DLOG(ERROR) << "WebApp AllowedLaunchProtocols proto action parse error"; |
| return nullptr; |
| } |
| allowed_launch_protocols.push_back(allowed_launch_protocol); |
| } |
| web_app->SetAllowedLaunchProtocols(std::move(allowed_launch_protocols)); |
| |
| std::vector<std::string> disallowed_launch_protocols; |
| for (const std::string& disallowed_launch_protocol : |
| local_data.disallowed_launch_protocols()) { |
| if (disallowed_launch_protocol.empty()) { |
| DLOG(ERROR) |
| << "WebApp DisallowedLaunchProtocols proto action parse error"; |
| return nullptr; |
| } |
| disallowed_launch_protocols.push_back(disallowed_launch_protocol); |
| } |
| web_app->SetDisallowedLaunchProtocols(std::move(disallowed_launch_protocols)); |
| |
| std::vector<apps::UrlHandlerInfo> url_handlers; |
| for (const auto& url_handler_proto : local_data.url_handlers()) { |
| apps::UrlHandlerInfo url_handler; |
| |
| url::Origin origin = url::Origin::Create(GURL(url_handler_proto.origin())); |
| if (origin.opaque()) { |
| DLOG(ERROR) << "WebApp UrlHandler proto url parse error: " |
| << origin.GetDebugString(); |
| return nullptr; |
| } |
| url_handler.origin = std::move(origin); |
| url_handler.has_origin_wildcard = url_handler_proto.has_origin_wildcard(); |
| url_handlers.push_back(std::move(url_handler)); |
| } |
| web_app->SetUrlHandlers(std::move(url_handlers)); |
| |
| if (local_data.has_note_taking_new_note_url()) { |
| web_app->SetNoteTakingNewNoteUrl( |
| GURL(local_data.note_taking_new_note_url())); |
| } |
| |
| if (local_data.has_user_run_on_os_login_mode()) { |
| web_app->SetRunOnOsLoginMode( |
| ToRunOnOsLoginMode(local_data.user_run_on_os_login_mode())); |
| } |
| |
| if (local_data.has_capture_links()) |
| web_app->SetCaptureLinks(ProtoToCaptureLinks(local_data.capture_links())); |
| else |
| web_app->SetCaptureLinks(blink::mojom::CaptureLinks::kUndefined); |
| |
| if (local_data.has_manifest_url()) { |
| GURL manifest_url(local_data.manifest_url()); |
| if (manifest_url.is_empty() || !manifest_url.is_valid()) { |
| DLOG(ERROR) << "WebApp proto manifest_url parse error: " |
| << manifest_url.possibly_invalid_spec(); |
| return nullptr; |
| } |
| web_app->SetManifestUrl(manifest_url); |
| } |
| if (local_data.has_file_handler_permission_blocked()) { |
| web_app->SetFileHandlerPermissionBlocked( |
| local_data.file_handler_permission_blocked()); |
| } |
| |
| if (local_data.has_file_handler_approval_state()) { |
| web_app->SetFileHandlerApprovalState( |
| ProtoToApiApprovalState(local_data.file_handler_approval_state())); |
| } |
| |
| if (local_data.has_window_controls_overlay_enabled()) { |
| web_app->SetWindowControlsOverlayEnabled( |
| local_data.window_controls_overlay_enabled()); |
| } |
| |
| web_app->SetStorageIsolated(local_data.is_storage_isolated()); |
| |
| if (local_data.has_launch_handler()) { |
| LaunchHandler launch_handler; |
| const LaunchHandlerProto& launch_handler_proto = |
| local_data.launch_handler(); |
| if (launch_handler_proto.has_route_to()) { |
| launch_handler.route_to = |
| ProtoToLaunchHandlerRouteTo(launch_handler_proto.route_to()); |
| } |
| if (launch_handler_proto.has_navigate_existing_client()) { |
| launch_handler.navigate_existing_client = |
| ProtoToLaunchHandlerNavigateExistingClient( |
| launch_handler_proto.navigate_existing_client()); |
| } |
| web_app->SetLaunchHandler(std::move(launch_handler)); |
| } |
| |
| if (local_data.has_parent_app_id()) { |
| web_app->parent_app_id_ = local_data.parent_app_id(); |
| } |
| |
| return web_app; |
| } |
| |
| void WebAppDatabase::OnDatabaseOpened( |
| RegistryOpenedCallback callback, |
| const absl::optional<syncer::ModelError>& error, |
| std::unique_ptr<syncer::ModelTypeStore> store) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (error) { |
| error_callback_.Run(*error); |
| DLOG(ERROR) << "WebApps LevelDB opening error: " << error->ToString(); |
| return; |
| } |
| |
| store_ = std::move(store); |
| // TODO(loyso): Use ReadAllDataAndPreprocess to parse protos in the background |
| // sequence. |
| store_->ReadAllData(base::BindOnce(&WebAppDatabase::OnAllDataRead, |
| weak_ptr_factory_.GetWeakPtr(), |
| std::move(callback))); |
| } |
| |
| void WebAppDatabase::OnAllDataRead( |
| RegistryOpenedCallback callback, |
| const absl::optional<syncer::ModelError>& error, |
| std::unique_ptr<syncer::ModelTypeStore::RecordList> data_records) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (error) { |
| error_callback_.Run(*error); |
| DLOG(ERROR) << "WebApps LevelDB data read error: " << error->ToString(); |
| return; |
| } |
| |
| store_->ReadAllMetadata(base::BindOnce( |
| &WebAppDatabase::OnAllMetadataRead, weak_ptr_factory_.GetWeakPtr(), |
| std::move(data_records), std::move(callback))); |
| } |
| |
| void WebAppDatabase::OnAllMetadataRead( |
| std::unique_ptr<syncer::ModelTypeStore::RecordList> data_records, |
| RegistryOpenedCallback callback, |
| const absl::optional<syncer::ModelError>& error, |
| std::unique_ptr<syncer::MetadataBatch> metadata_batch) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (error) { |
| error_callback_.Run(*error); |
| DLOG(ERROR) << "WebApps LevelDB metadata read error: " << error->ToString(); |
| return; |
| } |
| |
| Registry registry; |
| for (const syncer::ModelTypeStore::Record& record : *data_records) { |
| const AppId app_id = record.id; |
| std::unique_ptr<WebApp> web_app = ParseWebApp(app_id, record.value); |
| if (web_app) |
| registry.emplace(app_id, std::move(web_app)); |
| } |
| |
| opened_ = true; |
| // This should be a tail call: a callback code may indirectly call |this| |
| // methods, like WebAppDatabase::Write() |
| std::move(callback).Run(std::move(registry), std::move(metadata_batch)); |
| } |
| |
| void WebAppDatabase::OnDataWritten( |
| CompletionCallback callback, |
| const absl::optional<syncer::ModelError>& error) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (error) { |
| error_callback_.Run(*error); |
| DLOG(ERROR) << "WebApps LevelDB write error: " << error->ToString(); |
| } |
| |
| std::move(callback).Run(!error); |
| } |
| |
| // static |
| std::unique_ptr<WebApp> WebAppDatabase::ParseWebApp(const AppId& app_id, |
| const std::string& value) { |
| WebAppProto proto; |
| const bool parsed = proto.ParseFromString(value); |
| if (!parsed) { |
| DLOG(ERROR) << "WebApps LevelDB parse error: can't parse proto."; |
| return nullptr; |
| } |
| |
| auto web_app = CreateWebApp(proto); |
| if (!web_app) { |
| // CreateWebApp() already logged what went wrong here. |
| return nullptr; |
| } |
| |
| if (web_app->app_id() != app_id) { |
| DLOG(ERROR) << "WebApps LevelDB error: app_id doesn't match storage key"; |
| return nullptr; |
| } |
| |
| return web_app; |
| } |
| |
| DisplayMode ToMojomDisplayMode(WebAppProto::DisplayMode display_mode) { |
| switch (display_mode) { |
| case WebAppProto::BROWSER: |
| return DisplayMode::kBrowser; |
| case WebAppProto::MINIMAL_UI: |
| return DisplayMode::kMinimalUi; |
| case WebAppProto::STANDALONE: |
| return DisplayMode::kStandalone; |
| case WebAppProto::FULLSCREEN: |
| return DisplayMode::kFullscreen; |
| case WebAppProto::WINDOW_CONTROLS_OVERLAY: |
| return DisplayMode::kWindowControlsOverlay; |
| case WebAppProto::TABBED: |
| return DisplayMode::kTabbed; |
| } |
| } |
| |
| DisplayMode ToMojomDisplayMode( |
| ::sync_pb::WebAppSpecifics::UserDisplayMode user_display_mode) { |
| switch (user_display_mode) { |
| case ::sync_pb::WebAppSpecifics::BROWSER: |
| return DisplayMode::kBrowser; |
| case ::sync_pb::WebAppSpecifics::TABBED: |
| return DisplayMode::kTabbed; |
| // New display modes will most likely be of the window variety than the |
| // browser tab variety so default to windowed if it's an enum value we don't |
| // know about. |
| case ::sync_pb::WebAppSpecifics::UNSPECIFIED: |
| case ::sync_pb::WebAppSpecifics::STANDALONE: |
| return DisplayMode::kStandalone; |
| } |
| } |
| |
| WebAppProto::DisplayMode ToWebAppProtoDisplayMode(DisplayMode display_mode) { |
| switch (display_mode) { |
| case DisplayMode::kBrowser: |
| return WebAppProto::BROWSER; |
| case DisplayMode::kMinimalUi: |
| return WebAppProto::MINIMAL_UI; |
| case DisplayMode::kUndefined: |
| NOTREACHED(); |
| FALLTHROUGH; |
| case DisplayMode::kStandalone: |
| return WebAppProto::STANDALONE; |
| case DisplayMode::kFullscreen: |
| return WebAppProto::FULLSCREEN; |
| case DisplayMode::kWindowControlsOverlay: |
| return WebAppProto::WINDOW_CONTROLS_OVERLAY; |
| case DisplayMode::kTabbed: |
| return WebAppProto::TABBED; |
| } |
| } |
| |
| } // namespace web_app |