| // Copyright 2016 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/webapps/browser/installable/installable_logging.h" |
| |
| #include <vector> |
| |
| #include "base/no_destructor.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/stringprintf.h" |
| #include "components/webapps/browser/installable/installable_evaluator.h" |
| #include "content/public/browser/installability_error.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "third_party/blink/public/mojom/devtools/console_message.mojom.h" |
| |
| namespace webapps { |
| |
| namespace { |
| |
| // Error message strings corresponding to the InstallableStatusCode enum. |
| static const char kNotFromSecureOriginMessage[] = |
| "Page is not served from a secure origin"; |
| static const char kNoManifestMessage[] = "Page has no manifest <link> URL"; |
| static const char kManifestParsingOrNetworkErrorMessage[] = |
| "The manifest could not be fetched, parsed, or the document is on an " |
| "opaque origin."; |
| static const char kStartUrlNotValidMessage[] = |
| "Manifest start URL is not valid"; |
| static const char kManifestMissingNameOrShortNameMessage[] = |
| "Manifest does not contain a 'name' or 'short_name' field"; |
| static const char kManifestDisplayNotSupportedMessage[] = |
| "Manifest 'display' property must be one of 'standalone', 'fullscreen', or " |
| "'minimal-ui'"; |
| static const char kManifestMissingSuitableIconMessage[] = |
| "Manifest does not contain a suitable icon - PNG, SVG or WebP format of at " |
| "least %dpx is required, the sizes attribute must be set, and the purpose " |
| "attribute, if set, must include \"any\" or \"maskable\"."; |
| static const char kNoAcceptableIconMessage[] = |
| "No supplied icon is at least %dpx square in PNG, SVG or WebP format"; |
| static const char kCannotDownloadIconMessage[] = |
| "Could not download a required icon from the manifest"; |
| static const char kNoIconAvailableMessage[] = |
| "Downloaded icon was empty or corrupted"; |
| static const char kPlatformNotSupportedOnAndroidMessage[] = |
| "The specified application platform is not supported on Android"; |
| static const char kNoIdSpecifiedMessage[] = "No Play store ID provided"; |
| static const char kIdsDoNotMatchMessage[] = |
| "The Play Store app URL and Play Store ID do not match"; |
| static const char kAlreadyInstalledMessage[] = "The app is already installed"; |
| static const char kUrlNotSupportedForWebApkMessage[] = |
| "A URL in the manifest contains a username, password, or port"; |
| static const char kInIncognitoMessage[] = |
| "Page is loaded in an incognito window"; |
| static const char kPreferRelatedApplications[] = |
| "Manifest specifies prefer_related_applications: true"; |
| static const char kPreferRelatedApplicationsSupportedOnlyBetaStable[] = |
| "prefer_related_applications is only supported on Chrome Beta and Stable " |
| "channels on Android."; |
| static const char kManifestLocationChanged[] = |
| "Manifest location changed during fetch"; |
| static const char kManifestDisplayOverrideNotSupportedMessage[] = |
| "Manifest contains 'display_override' field, and the first supported " |
| "display mode must be one of 'standalone', 'fullscreen', or 'minimal-ui'"; |
| static const char kPipelineRestarted[] = |
| "Web app uninstalled so that it stops any running pipeline"; |
| |
| static const char kNotFromSecureOriginId[] = "not-from-secure-origin"; |
| static const char kNoManifestId[] = "no-manifest"; |
| static const char kManifestParsingOrNetworkErrorId[] = |
| "manifest-parsing-or-network-error"; |
| static const char kStartUrlNotValidId[] = "start-url-not-valid"; |
| static const char kManifestMissingNameOrShortNameId[] = |
| "manifest-missing-name-or-short-name"; |
| static const char kManifestDisplayNotSupportedId[] = |
| "manifest-display-not-supported"; |
| static const char kManifestMissingSuitableIconId[] = |
| "manifest-missing-suitable-icon"; |
| static const char kMinimumIconSizeInPixelsId[] = "minimum-icon-size-in-pixels"; |
| static const char kNoAcceptableIconId[] = "no-acceptable-icon"; |
| static const char kCannotDownloadIconId[] = "cannot-download-icon"; |
| static const char kNoIconAvailableId[] = "no-icon-available"; |
| static const char kPlatformNotSupportedOnAndroidId[] = |
| "platform-not-supported-on-android"; |
| static const char kNoIdSpecifiedId[] = "no-id-specified"; |
| static const char kIdsDoNotMatchId[] = "ids-do-not-match"; |
| static const char kAlreadyInstalledId[] = "already-installed"; |
| static const char kUrlNotSupportedForWebApkId[] = |
| "url-not-supported-for-webapk"; |
| static const char kInIncognitoId[] = "in-incognito"; |
| static const char kPreferRelatedApplicationsId[] = |
| "prefer-related-applications"; |
| static const char kPreferRelatedApplicationsSupportedOnlyBetaStableId[] = |
| "prefer-related-applications-only-beta-stable"; |
| static const char kManifestLocationChangedId[] = "manifest-location-changed"; |
| static const char kManifestDisplayOverrideNotSupportedId[] = |
| "manifest-display-override-not-supported"; |
| static const char kPipelineRestartedId[] = "pipeline-restarted"; |
| |
| const std::string& GetMessagePrefix() { |
| static base::NoDestructor<std::string> message_prefix( |
| "Site cannot be installed: "); |
| return *message_prefix; |
| } |
| |
| } // namespace |
| |
| std::string GetErrorMessage(InstallableStatusCode code) { |
| std::string message; |
| switch (code) { |
| case webapps::InstallableStatusCode::NO_ERROR_DETECTED: |
| // These codes are solely used for UMA reporting. |
| case webapps::InstallableStatusCode::RENDERER_EXITING: |
| case webapps::InstallableStatusCode::RENDERER_CANCELLED: |
| case webapps::InstallableStatusCode::USER_NAVIGATED: |
| case webapps::InstallableStatusCode::PACKAGE_NAME_OR_START_URL_EMPTY: |
| case webapps::InstallableStatusCode::PREVIOUSLY_BLOCKED: |
| case webapps::InstallableStatusCode::PREVIOUSLY_IGNORED: |
| case webapps::InstallableStatusCode::SHOWING_NATIVE_APP_BANNER: |
| case webapps::InstallableStatusCode::SHOWING_WEB_APP_BANNER: |
| case webapps::InstallableStatusCode::FAILED_TO_CREATE_BANNER: |
| case webapps::InstallableStatusCode::WAITING_FOR_MANIFEST: |
| case webapps::InstallableStatusCode::WAITING_FOR_INSTALLABLE_CHECK: |
| case webapps::InstallableStatusCode::NO_GESTURE: |
| case webapps::InstallableStatusCode::WAITING_FOR_NATIVE_DATA: |
| case webapps::InstallableStatusCode::SHOWING_APP_INSTALLATION_DIALOG: |
| case webapps::InstallableStatusCode::DATA_TIMED_OUT: |
| case webapps::InstallableStatusCode::WEBAPK_INSTALL_FAILED: |
| break; |
| case webapps::InstallableStatusCode::NOT_FROM_SECURE_ORIGIN: |
| message = kNotFromSecureOriginMessage; |
| break; |
| case webapps::InstallableStatusCode::NO_MANIFEST: |
| message = kNoManifestMessage; |
| break; |
| case webapps::InstallableStatusCode::MANIFEST_PARSING_OR_NETWORK_ERROR: |
| message = kManifestParsingOrNetworkErrorMessage; |
| break; |
| case webapps::InstallableStatusCode::START_URL_NOT_VALID: |
| message = kStartUrlNotValidMessage; |
| break; |
| case webapps::InstallableStatusCode::MANIFEST_MISSING_NAME_OR_SHORT_NAME: |
| message = kManifestMissingNameOrShortNameMessage; |
| break; |
| case webapps::InstallableStatusCode::MANIFEST_DISPLAY_NOT_SUPPORTED: |
| message = kManifestDisplayNotSupportedMessage; |
| break; |
| case webapps::InstallableStatusCode::MANIFEST_MISSING_SUITABLE_ICON: |
| message = |
| base::StringPrintf(kManifestMissingSuitableIconMessage, |
| InstallableEvaluator::GetMinimumIconSizeInPx()); |
| break; |
| case webapps::InstallableStatusCode::NO_ACCEPTABLE_ICON: |
| message = |
| base::StringPrintf(kNoAcceptableIconMessage, |
| InstallableEvaluator::GetMinimumIconSizeInPx()); |
| break; |
| case webapps::InstallableStatusCode::CANNOT_DOWNLOAD_ICON: |
| message = kCannotDownloadIconMessage; |
| break; |
| case webapps::InstallableStatusCode::NO_ICON_AVAILABLE: |
| message = kNoIconAvailableMessage; |
| break; |
| case webapps::InstallableStatusCode::PLATFORM_NOT_SUPPORTED_ON_ANDROID: |
| message = kPlatformNotSupportedOnAndroidMessage; |
| break; |
| case webapps::InstallableStatusCode::NO_ID_SPECIFIED: |
| message = kNoIdSpecifiedMessage; |
| break; |
| case webapps::InstallableStatusCode::IDS_DO_NOT_MATCH: |
| message = kIdsDoNotMatchMessage; |
| break; |
| case webapps::InstallableStatusCode::ALREADY_INSTALLED: |
| message = kAlreadyInstalledMessage; |
| break; |
| case webapps::InstallableStatusCode::URL_NOT_SUPPORTED_FOR_WEBAPK: |
| message = kUrlNotSupportedForWebApkMessage; |
| break; |
| case webapps::InstallableStatusCode::IN_INCOGNITO: |
| message = kInIncognitoMessage; |
| break; |
| case webapps::InstallableStatusCode::PREFER_RELATED_APPLICATIONS: |
| message = kPreferRelatedApplications; |
| break; |
| case webapps::InstallableStatusCode:: |
| PREFER_RELATED_APPLICATIONS_SUPPORTED_ONLY_BETA_STABLE: |
| message = kPreferRelatedApplicationsSupportedOnlyBetaStable; |
| break; |
| case webapps::InstallableStatusCode::MANIFEST_URL_CHANGED: |
| message = kManifestLocationChanged; |
| break; |
| case webapps::InstallableStatusCode:: |
| MANIFEST_DISPLAY_OVERRIDE_NOT_SUPPORTED: |
| message = kManifestDisplayOverrideNotSupportedMessage; |
| break; |
| case webapps::InstallableStatusCode::PIPELINE_RESTARTED: |
| message = kPipelineRestarted; |
| break; |
| } |
| |
| return message; |
| } |
| |
| content::InstallabilityError GetInstallabilityError( |
| InstallableStatusCode code) { |
| content::InstallabilityError error; |
| std::string error_id; |
| std::vector<content::InstallabilityErrorArgument> error_arguments; |
| switch (code) { |
| case webapps::InstallableStatusCode::NO_ERROR_DETECTED: |
| // These codes are solely used for UMA reporting. |
| case webapps::InstallableStatusCode::RENDERER_EXITING: |
| case webapps::InstallableStatusCode::RENDERER_CANCELLED: |
| case webapps::InstallableStatusCode::USER_NAVIGATED: |
| case webapps::InstallableStatusCode::PACKAGE_NAME_OR_START_URL_EMPTY: |
| case webapps::InstallableStatusCode::PREVIOUSLY_BLOCKED: |
| case webapps::InstallableStatusCode::PREVIOUSLY_IGNORED: |
| case webapps::InstallableStatusCode::SHOWING_NATIVE_APP_BANNER: |
| case webapps::InstallableStatusCode::SHOWING_WEB_APP_BANNER: |
| case webapps::InstallableStatusCode::FAILED_TO_CREATE_BANNER: |
| case webapps::InstallableStatusCode::WAITING_FOR_MANIFEST: |
| case webapps::InstallableStatusCode::WAITING_FOR_INSTALLABLE_CHECK: |
| case webapps::InstallableStatusCode::NO_GESTURE: |
| case webapps::InstallableStatusCode::WAITING_FOR_NATIVE_DATA: |
| case webapps::InstallableStatusCode::SHOWING_APP_INSTALLATION_DIALOG: |
| case webapps::InstallableStatusCode::DATA_TIMED_OUT: |
| case webapps::InstallableStatusCode::WEBAPK_INSTALL_FAILED: |
| break; |
| case webapps::InstallableStatusCode::NOT_FROM_SECURE_ORIGIN: |
| error_id = kNotFromSecureOriginId; |
| break; |
| case webapps::InstallableStatusCode::NO_MANIFEST: |
| error_id = kNoManifestId; |
| break; |
| case webapps::InstallableStatusCode::MANIFEST_PARSING_OR_NETWORK_ERROR: |
| error_id = kManifestParsingOrNetworkErrorId; |
| break; |
| case webapps::InstallableStatusCode::START_URL_NOT_VALID: |
| error_id = kStartUrlNotValidId; |
| break; |
| case webapps::InstallableStatusCode::MANIFEST_MISSING_NAME_OR_SHORT_NAME: |
| error_id = kManifestMissingNameOrShortNameId; |
| break; |
| case webapps::InstallableStatusCode::MANIFEST_DISPLAY_NOT_SUPPORTED: |
| error_id = kManifestDisplayNotSupportedId; |
| break; |
| case webapps::InstallableStatusCode::MANIFEST_MISSING_SUITABLE_ICON: |
| error_id = kManifestMissingSuitableIconId; |
| error_arguments.emplace_back( |
| kMinimumIconSizeInPixelsId, |
| base::NumberToString(InstallableEvaluator::GetMinimumIconSizeInPx())); |
| break; |
| case webapps::InstallableStatusCode::NO_ACCEPTABLE_ICON: |
| error_id = kNoAcceptableIconId; |
| error_arguments.emplace_back( |
| kMinimumIconSizeInPixelsId, |
| base::NumberToString(InstallableEvaluator::GetMinimumIconSizeInPx())); |
| break; |
| case webapps::InstallableStatusCode::CANNOT_DOWNLOAD_ICON: |
| error_id = kCannotDownloadIconId; |
| break; |
| case webapps::InstallableStatusCode::NO_ICON_AVAILABLE: |
| error_id = kNoIconAvailableId; |
| break; |
| case webapps::InstallableStatusCode::PLATFORM_NOT_SUPPORTED_ON_ANDROID: |
| error_id = kPlatformNotSupportedOnAndroidId; |
| break; |
| case webapps::InstallableStatusCode::NO_ID_SPECIFIED: |
| error_id = kNoIdSpecifiedId; |
| break; |
| case webapps::InstallableStatusCode::IDS_DO_NOT_MATCH: |
| error_id = kIdsDoNotMatchId; |
| break; |
| case webapps::InstallableStatusCode::ALREADY_INSTALLED: |
| error_id = kAlreadyInstalledId; |
| break; |
| case webapps::InstallableStatusCode::URL_NOT_SUPPORTED_FOR_WEBAPK: |
| error_id = kUrlNotSupportedForWebApkId; |
| break; |
| case webapps::InstallableStatusCode::IN_INCOGNITO: |
| error_id = kInIncognitoId; |
| break; |
| case webapps::InstallableStatusCode::PREFER_RELATED_APPLICATIONS: |
| error_id = kPreferRelatedApplicationsId; |
| break; |
| case webapps::InstallableStatusCode:: |
| PREFER_RELATED_APPLICATIONS_SUPPORTED_ONLY_BETA_STABLE: |
| error_id = kPreferRelatedApplicationsSupportedOnlyBetaStableId; |
| break; |
| case webapps::InstallableStatusCode::MANIFEST_URL_CHANGED: |
| error_id = kManifestLocationChangedId; |
| break; |
| case webapps::InstallableStatusCode:: |
| MANIFEST_DISPLAY_OVERRIDE_NOT_SUPPORTED: |
| error_id = kManifestDisplayOverrideNotSupportedId; |
| break; |
| case webapps::InstallableStatusCode::PIPELINE_RESTARTED: |
| error_id = kPipelineRestartedId; |
| break; |
| } |
| error.error_id = error_id; |
| error.installability_error_arguments = error_arguments; |
| return error; |
| } |
| |
| void LogToConsole(content::WebContents* web_contents, |
| InstallableStatusCode code, |
| blink::mojom::ConsoleMessageLevel level) { |
| if (!web_contents) |
| return; |
| |
| std::string message = GetErrorMessage(code); |
| |
| if (message.empty()) |
| return; |
| |
| web_contents->GetPrimaryMainFrame()->AddMessageToConsole( |
| level, GetMessagePrefix() + message); |
| } |
| |
| } // namespace webapps |