blob: 659438c6e08f9ba0b0360ccf438b74ef22746cd1 [file] [log] [blame]
// 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