| // Copyright 2021 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/ui/webui/media_router/cast_feedback_ui.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "base/json/json_string_value_serializer.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "chrome/browser/media/router/media_router_feature.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/signin/identity_manager_factory.h" |
| #include "chrome/browser/ui/webui/feedback/feedback_ui.h" |
| #include "chrome/browser/ui/webui/metrics_handler.h" |
| #include "chrome/browser/ui/webui/webui_util.h" |
| #include "chrome/common/channel_info.h" |
| #include "chrome/common/url_constants.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "chrome/grit/media_router_feedback_resources.h" |
| #include "chrome/grit/media_router_feedback_resources_map.h" |
| #include "components/media_router/browser/logger_impl.h" |
| #include "components/media_router/browser/media_router.h" |
| #include "components/media_router/browser/media_router_factory.h" |
| #include "components/signin/public/identity_manager/identity_manager.h" |
| #include "components/strings/grit/components_strings.h" |
| #include "components/version_info/channel.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/browser/web_contents_delegate.h" |
| #include "content/public/browser/web_ui.h" |
| #include "content/public/browser/web_ui_data_source.h" |
| #include "extensions/common/features/feature.h" |
| #include "extensions/common/features/feature_provider.h" |
| #include "ui/base/l10n/l10n_util.h" |
| |
| namespace media_router { |
| |
| CastFeedbackUI::CastFeedbackUI(content::WebUI* web_ui) |
| : content::WebUIController(web_ui), |
| profile_(Profile::FromWebUI(web_ui)), |
| web_contents_(web_ui->GetWebContents()) { |
| content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd( |
| profile_, chrome::kChromeUICastFeedbackHost); |
| |
| static constexpr webui::LocalizedString kStrings[] = { |
| {"additionalComments", IDS_MEDIA_ROUTER_FEEDBACK_ADDITIONAL_COMMENTS}, |
| {"allowContactByEmail", IDS_MEDIA_ROUTER_FEEDBACK_ALLOW_CONTACT_BY_EMAIL}, |
| {"audioAcceptable", IDS_MEDIA_ROUTER_FEEDBACK_AUDIO_ACCEPTABLE}, |
| {"audioGood", IDS_MEDIA_ROUTER_FEEDBACK_AUDIO_GOOD}, |
| {"audioPerfect", IDS_MEDIA_ROUTER_FEEDBACK_AUDIO_PERFECT}, |
| {"audioPoor", IDS_MEDIA_ROUTER_FEEDBACK_AUDIO_POOR}, |
| {"audioQuality", IDS_MEDIA_ROUTER_FEEDBACK_AUDIO_QUALITY}, |
| {"audioUnintelligible", IDS_MEDIA_ROUTER_FEEDBACK_AUDIO_UNINTELLIGIBLE}, |
| {"cancel", IDS_CANCEL}, |
| {"contentQuestion", IDS_MEDIA_ROUTER_FEEDBACK_CONTENT_QUESTION}, |
| {"didNotTry", IDS_MEDIA_ROUTER_FEEDBACK_DID_NOT_TRY}, |
| {"discardConfirmation", IDS_MEDIA_ROUTER_FEEDBACK_DISCARD_CONFIRMATION}, |
| {"emailField", IDS_MEDIA_ROUTER_FEEDBACK_EMAIL_FIELD}, |
| {"fineLogsWarning", IDS_MEDIA_ROUTER_FEEDBACK_FINE_LOGS_WARNING}, |
| {"header", IDS_MEDIA_ROUTER_FEEDBACK_HEADER}, |
| {"logsHeader", IDS_MEDIA_ROUTER_FEEDBACK_LOGS_HEADER}, |
| {"mirroringQualitySubheading", |
| IDS_MEDIA_ROUTER_FEEDBACK_MIRRORING_QUALITY_SUBHEADING}, |
| {"na", IDS_MEDIA_ROUTER_FEEDBACK_NA}, |
| {"networkDifferentWifi", |
| IDS_MEDIA_ROUTER_FEEDBACK_NETWORK_DIFFERENT_WIFI}, |
| {"networkQuestion", IDS_MEDIA_ROUTER_FEEDBACK_NETWORK_QUESTION}, |
| {"networkSameWifi", IDS_MEDIA_ROUTER_FEEDBACK_NETWORK_SAME_WIFI}, |
| {"networkWiredPc", IDS_MEDIA_ROUTER_FEEDBACK_NETWORK_WIRED_PC}, |
| {"no", IDS_MEDIA_ROUTER_FEEDBACK_NO}, |
| {"ok", IDS_OK}, |
| {"privacyDataUsage", IDS_MEDIA_ROUTER_FEEDBACK_PRIVACY_DATA_USAGE}, |
| {"prompt", IDS_MEDIA_ROUTER_FEEDBACK_PROMPT}, |
| {"required", IDS_MEDIA_ROUTER_FEEDBACK_REQUIRED}, |
| {"resending", IDS_MEDIA_ROUTER_FEEDBACK_RESENDING}, |
| {"sendButton", IDS_MEDIA_ROUTER_FEEDBACK_SEND_BUTTON}, |
| {"sendFail", IDS_MEDIA_ROUTER_FEEDBACK_SEND_FAIL}, |
| {"sendLogs", IDS_MEDIA_ROUTER_FEEDBACK_SEND_LOGS}, |
| {"sendLogsHtml", IDS_MEDIA_ROUTER_FEEDBACK_SEND_LOGS_HTML}, |
| {"sendSuccess", IDS_MEDIA_ROUTER_FEEDBACK_SEND_SUCCESS}, |
| {"sending", IDS_MEDIA_ROUTER_FEEDBACK_SENDING}, |
| {"softwareQuestion", IDS_MEDIA_ROUTER_FEEDBACK_SOFTWARE_QUESTION}, |
| {"title", IDS_MEDIA_ROUTER_FEEDBACK_TITLE}, |
| {"typeBugOrError", IDS_MEDIA_ROUTER_FEEDBACK_TYPE_BUG_OR_ERROR}, |
| {"typeDiscovery", IDS_MEDIA_ROUTER_FEEDBACK_TYPE_DISCOVERY}, |
| {"typeFeatureRequest", IDS_MEDIA_ROUTER_FEEDBACK_TYPE_FEATURE_REQUEST}, |
| {"typeOther", IDS_MEDIA_ROUTER_FEEDBACK_TYPE_OTHER}, |
| {"typeProjectionQuality", |
| IDS_MEDIA_ROUTER_FEEDBACK_TYPE_PROJECTION_QUALITY}, |
| {"typeQuestion", IDS_MEDIA_ROUTER_FEEDBACK_TYPE_QUESTION}, |
| {"unknown", IDS_MEDIA_ROUTER_FEEDBACK_UNKNOWN}, |
| {"videoAcceptable", IDS_MEDIA_ROUTER_FEEDBACK_VIDEO_ACCEPTABLE}, |
| {"videoFreezes", IDS_MEDIA_ROUTER_FEEDBACK_VIDEO_FREEZES}, |
| {"videoGood", IDS_MEDIA_ROUTER_FEEDBACK_VIDEO_GOOD}, |
| {"videoGreat", IDS_MEDIA_ROUTER_FEEDBACK_VIDEO_GREAT}, |
| {"videoJerky", IDS_MEDIA_ROUTER_FEEDBACK_VIDEO_JERKY}, |
| {"videoPerfect", IDS_MEDIA_ROUTER_FEEDBACK_VIDEO_PERFECT}, |
| {"videoPoor", IDS_MEDIA_ROUTER_FEEDBACK_VIDEO_POOR}, |
| {"videoQuality", IDS_MEDIA_ROUTER_FEEDBACK_VIDEO_QUALITY}, |
| {"videoSmooth", IDS_MEDIA_ROUTER_FEEDBACK_VIDEO_SMOOTH}, |
| {"videoSmoothness", IDS_MEDIA_ROUTER_FEEDBACK_VIDEO_SMOOTHNESS}, |
| {"videoStutter", IDS_MEDIA_ROUTER_FEEDBACK_VIDEO_STUTTER}, |
| {"videoUnwatchable", IDS_MEDIA_ROUTER_FEEDBACK_VIDEO_UNWATCHABLE}, |
| {"yes", IDS_MEDIA_ROUTER_FEEDBACK_YES}, |
| {"yourAnswer", IDS_MEDIA_ROUTER_FEEDBACK_YOUR_ANSWER}, |
| {"yourEmailAddress", IDS_MEDIA_ROUTER_FEEDBACK_YOUR_EMAIL_ADDRESS}, |
| }; |
| source->AddLocalizedStrings(kStrings); |
| source->AddString( |
| "formDescription", |
| l10n_util::GetStringFUTF8( |
| IDS_MEDIA_ROUTER_FEEDBACK_FORM_DESCRIPTION, |
| u"https://support.google.com/chromecast?p=troubleshoot_chromecast")); |
| source->AddString( |
| "setupVisibilityQuestion", |
| l10n_util::GetStringFUTF8( |
| IDS_MEDIA_ROUTER_FEEDBACK_SETUP_VISIBILITY_QUESTION, |
| u"https://support.google.com/chromecast?p=set_up_chromecast")); |
| |
| // Supply media router log data to UI. |
| MediaRouter* const router = |
| media_router::MediaRouterFactory::GetApiForBrowserContext( |
| web_contents_->GetBrowserContext()); |
| |
| std::string log_data; |
| |
| // Reserve a few kb of space to hopefully avoid multiple allocations later. |
| log_data.reserve(8192); |
| |
| JSONStringValueSerializer serializer(&log_data); |
| serializer.set_pretty_print(true); |
| if (!serializer.Serialize(router->GetState())) { |
| log_data.clear(); |
| } |
| |
| LoggerImpl* const logger = router->GetLogger(); |
| if (logger) { |
| log_data += logger->GetLogsAsJson(); |
| } |
| |
| MediaRouterDebugger& debugger = router->GetDebugger(); |
| if (debugger.ShouldFetchMirroringStats()) { |
| std::string mirroring_stats_json; |
| JSONStringValueSerializer mirroring_stats_serializer(&mirroring_stats_json); |
| mirroring_stats_serializer.set_pretty_print(true); |
| if (mirroring_stats_serializer.Serialize(debugger.GetMirroringStats())) { |
| log_data += mirroring_stats_json; |
| } |
| } |
| |
| // If there is any log data, add it to the `source`. |
| if (!log_data.empty()) { |
| source->AddString("logData", log_data); |
| } |
| |
| // As the name suggests, this value is used to categorize feedback reports for |
| // easier analysis and triage. |
| source->AddString( |
| "categoryTag", |
| std::string(version_info::GetChannelString(chrome::GetChannel()))); |
| |
| source->AddBoolean("globalMediaControlsCastStartStop", |
| GlobalMediaControlsCastStartStopEnabled(profile_)); |
| |
| webui::SetupWebUIDataSource( |
| source, |
| base::make_span(kMediaRouterFeedbackResources, |
| kMediaRouterFeedbackResourcesSize), |
| IDR_MEDIA_ROUTER_FEEDBACK_FEEDBACK_HTML); |
| |
| web_ui->RegisterMessageCallback( |
| "close", base::BindRepeating(&CastFeedbackUI::OnCloseMessage, |
| base::Unretained(this))); |
| web_ui->AddMessageHandler(std::make_unique<MetricsHandler>()); |
| } |
| |
| WEB_UI_CONTROLLER_TYPE_IMPL(CastFeedbackUI) |
| |
| CastFeedbackUI::~CastFeedbackUI() = default; |
| |
| void CastFeedbackUI::OnCloseMessage(const base::Value::List&) { |
| web_contents_->GetDelegate()->CloseContents(web_contents_); |
| } |
| |
| CastFeedbackUIConfig::CastFeedbackUIConfig() |
| : DefaultWebUIConfig(content::kChromeUIScheme, |
| chrome::kChromeUICastFeedbackHost) {} |
| |
| bool CastFeedbackUIConfig::IsWebUIEnabled( |
| content::BrowserContext* browser_context) { |
| Profile* profile = Profile::FromBrowserContext(browser_context); |
| return MediaRouterEnabled(profile) && FeedbackUI::IsFeedbackEnabled(profile); |
| } |
| |
| } // namespace media_router |