blob: 5416645dded23d5197284b0e5a9600ccc94d3568 [file] [log] [blame]
// Copyright 2015 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/ui/webui/media_router/media_router_webui_message_handler.h"
#include <string>
#include "base/bind.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/sparse_histogram.h"
#include "base/metrics/user_metrics.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "chrome/browser/media/router/issue.h"
#include "chrome/browser/media/router/media_router_metrics.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/media_router/media_router_ui.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/generated_resources.h"
#include "components/prefs/pref_service.h"
#include "extensions/common/constants.h"
#include "ui/base/l10n/l10n_util.h"
#if defined(GOOGLE_CHROME_BUILD)
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "components/browser_sync/browser/profile_sync_service.h"
#include "components/signin/core/browser/signin_manager.h"
#endif // defined(GOOGLE_CHROME_BUILD)
namespace media_router {
namespace {
const char kCastLearnMorePageUrl[] =
"https://www.google.com/chrome/devices/chromecast/learn.html";
const char kHelpPageUrlPrefix[] =
"https://support.google.com/chromecast/answer/%d";
// Message names.
const char kRequestInitialData[] = "requestInitialData";
const char kCreateRoute[] = "requestRoute";
const char kAcknowledgeFirstRunFlow[] = "acknowledgeFirstRunFlow";
const char kActOnIssue[] = "actOnIssue";
const char kCloseRoute[] = "closeRoute";
const char kJoinRoute[] = "joinRoute";
const char kCloseDialog[] = "closeDialog";
const char kReportClickedSinkIndex[] = "reportClickedSinkIndex";
const char kReportInitialAction[] = "reportInitialAction";
const char kReportInitialState[] = "reportInitialState";
const char kReportNavigateToView[] = "reportNavigateToView";
const char kReportRouteCreation[] = "reportRouteCreation";
const char kReportSelectedCastMode[] = "reportSelectedCastMode";
const char kReportSinkCount[] = "reportSinkCount";
const char kReportTimeToClickSink[] = "reportTimeToClickSink";
const char kReportTimeToInitialActionClose[] = "reportTimeToInitialActionClose";
const char kOnInitialDataReceived[] = "onInitialDataReceived";
// JS function names.
const char kSetInitialData[] = "media_router.ui.setInitialData";
const char kNotifyRouteCreationTimeout[] =
"media_router.ui.onNotifyRouteCreationTimeout";
const char kOnCreateRouteResponseReceived[] =
"media_router.ui.onCreateRouteResponseReceived";
const char kSetIssue[] = "media_router.ui.setIssue";
const char kSetSinkList[] = "media_router.ui.setSinkList";
const char kSetRouteList[] = "media_router.ui.setRouteList";
const char kSetCastModeList[] = "media_router.ui.setCastModeList";
const char kUpdateMaxHeight[] = "media_router.ui.updateMaxHeight";
const char kWindowOpen[] = "window.open";
scoped_ptr<base::ListValue> SinksToValue(
const std::vector<MediaSinkWithCastModes>& sinks) {
scoped_ptr<base::ListValue> value(new base::ListValue);
for (const MediaSinkWithCastModes& sink_with_cast_modes : sinks) {
scoped_ptr<base::DictionaryValue> sink_val(new base::DictionaryValue);
const MediaSink& sink = sink_with_cast_modes.sink;
sink_val->SetString("id", sink.id());
sink_val->SetString("name", sink.name());
sink_val->SetInteger("iconType", sink.icon_type());
if (!sink.description().empty())
sink_val->SetString("description", sink.description());
if (!sink.domain().empty())
sink_val->SetString("domain", sink.domain());
int cast_mode_bits = 0;
for (MediaCastMode cast_mode : sink_with_cast_modes.cast_modes)
cast_mode_bits |= cast_mode;
sink_val->SetInteger("castModes", cast_mode_bits);
value->Append(sink_val.release());
}
return value;
}
scoped_ptr<base::DictionaryValue> RouteToValue(
const MediaRoute& route, bool canJoin, const std::string& extension_id) {
scoped_ptr<base::DictionaryValue> dictionary(new base::DictionaryValue);
dictionary->SetString("id", route.media_route_id());
dictionary->SetString("sinkId", route.media_sink_id());
dictionary->SetString("description", route.description());
dictionary->SetBoolean("isLocal", route.is_local());
dictionary->SetBoolean("canJoin", canJoin);
const std::string& custom_path = route.custom_controller_path();
if (!custom_path.empty()) {
std::string full_custom_controller_path = base::StringPrintf("%s://%s/%s",
extensions::kExtensionScheme, extension_id.c_str(),
custom_path.c_str());
DCHECK(GURL(full_custom_controller_path).is_valid());
dictionary->SetString("customControllerPath",
full_custom_controller_path);
}
return dictionary;
}
scoped_ptr<base::ListValue> RoutesToValue(
const std::vector<MediaRoute>& routes,
const std::vector<MediaRoute::Id>& joinable_route_ids,
const std::string& extension_id) {
scoped_ptr<base::ListValue> value(new base::ListValue);
for (const MediaRoute& route : routes) {
bool canJoin = ContainsValue(joinable_route_ids, route.media_route_id());
scoped_ptr<base::DictionaryValue> route_val(RouteToValue(route,
canJoin, extension_id));
value->Append(route_val.release());
}
return value;
}
scoped_ptr<base::ListValue> CastModesToValue(const CastModeSet& cast_modes,
const std::string& source_host) {
scoped_ptr<base::ListValue> value(new base::ListValue);
for (const MediaCastMode& cast_mode : cast_modes) {
scoped_ptr<base::DictionaryValue> cast_mode_val(new base::DictionaryValue);
cast_mode_val->SetInteger("type", cast_mode);
cast_mode_val->SetString(
"description", MediaCastModeToDescription(cast_mode, source_host));
cast_mode_val->SetString("host", source_host);
value->Append(cast_mode_val.release());
}
return value;
}
// Returns an Issue dictionary created from |issue| that can be used in WebUI.
scoped_ptr<base::DictionaryValue> IssueToValue(const Issue& issue) {
scoped_ptr<base::DictionaryValue> dictionary(new base::DictionaryValue);
dictionary->SetString("id", issue.id());
dictionary->SetString("title", issue.title());
dictionary->SetString("message", issue.message());
dictionary->SetInteger("defaultActionType", issue.default_action().type());
if (!issue.secondary_actions().empty()) {
dictionary->SetInteger("secondaryActionType",
issue.secondary_actions().begin()->type());
}
if (!issue.route_id().empty())
dictionary->SetString("routeId", issue.route_id());
dictionary->SetBoolean("isBlocking", issue.is_blocking());
return dictionary;
}
bool IsValidIssueActionTypeNum(int issue_action_type_num) {
return issue_action_type_num >= 0 &&
issue_action_type_num < IssueAction::TYPE_MAX;
}
// Composes a "learn more" URL. The URL depends on template arguments in |args|.
// Returns an empty string if |args| is invalid.
std::string GetLearnMoreUrl(const base::DictionaryValue* args) {
// TODO(imcheng): The template arguments for determining the learn more URL
// should come from the Issue object in the browser, not from WebUI.
int help_page_id = -1;
if (!args->GetInteger("helpPageId", &help_page_id) || help_page_id < 0) {
DVLOG(1) << "Invalid help page id.";
return std::string();
}
std::string help_url = base::StringPrintf(kHelpPageUrlPrefix, help_page_id);
if (!GURL(help_url).is_valid()) {
DVLOG(1) << "Error: URL is invalid and cannot be opened.";
return std::string();
}
return help_url;
}
} // namespace
MediaRouterWebUIMessageHandler::MediaRouterWebUIMessageHandler(
MediaRouterUI* media_router_ui)
: dialog_closing_(false),
media_router_ui_(media_router_ui) {
DCHECK(media_router_ui_);
}
MediaRouterWebUIMessageHandler::~MediaRouterWebUIMessageHandler() {
}
void MediaRouterWebUIMessageHandler::UpdateSinks(
const std::vector<MediaSinkWithCastModes>& sinks) {
DVLOG(2) << "UpdateSinks";
scoped_ptr<base::ListValue> sinks_val(SinksToValue(sinks));
web_ui()->CallJavascriptFunction(kSetSinkList, *sinks_val);
}
void MediaRouterWebUIMessageHandler::UpdateRoutes(
const std::vector<MediaRoute>& routes,
const std::vector<MediaRoute::Id>& joinable_route_ids) {
scoped_ptr<base::ListValue> routes_val(RoutesToValue(routes,
joinable_route_ids,
media_router_ui_->GetRouteProviderExtensionId()));
web_ui()->CallJavascriptFunction(kSetRouteList, *routes_val);
}
void MediaRouterWebUIMessageHandler::UpdateCastModes(
const CastModeSet& cast_modes,
const std::string& source_host) {
DVLOG(2) << "UpdateCastModes";
scoped_ptr<base::ListValue> cast_modes_val(
CastModesToValue(cast_modes, source_host));
web_ui()->CallJavascriptFunction(kSetCastModeList, *cast_modes_val);
}
void MediaRouterWebUIMessageHandler::OnCreateRouteResponseReceived(
const MediaSink::Id& sink_id,
const MediaRoute::Id& route_id) {
DVLOG(2) << "OnCreateRouteResponseReceived";
web_ui()->CallJavascriptFunction(kOnCreateRouteResponseReceived,
base::StringValue(sink_id),
base::StringValue(route_id));
}
void MediaRouterWebUIMessageHandler::UpdateIssue(const Issue* issue) {
DVLOG(2) << "UpdateIssue";
web_ui()->CallJavascriptFunction(kSetIssue,
issue ? *IssueToValue(*issue) : *base::Value::CreateNullValue());
}
void MediaRouterWebUIMessageHandler::UpdateMaxDialogHeight(int height) {
DVLOG(2) << "UpdateMaxDialogHeight";
web_ui()->CallJavascriptFunction(kUpdateMaxHeight,
base::FundamentalValue(height));
}
void MediaRouterWebUIMessageHandler::NotifyRouteCreationTimeout() {
DVLOG(2) << "NotifyRouteCreationTimeout";
web_ui()->CallJavascriptFunction(kNotifyRouteCreationTimeout);
}
void MediaRouterWebUIMessageHandler::RegisterMessages() {
web_ui()->RegisterMessageCallback(
kRequestInitialData,
base::Bind(&MediaRouterWebUIMessageHandler::OnRequestInitialData,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
kCreateRoute,
base::Bind(&MediaRouterWebUIMessageHandler::OnCreateRoute,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
kAcknowledgeFirstRunFlow,
base::Bind(&MediaRouterWebUIMessageHandler::OnAcknowledgeFirstRunFlow,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
kActOnIssue,
base::Bind(&MediaRouterWebUIMessageHandler::OnActOnIssue,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
kCloseRoute,
base::Bind(&MediaRouterWebUIMessageHandler::OnCloseRoute,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
kJoinRoute,
base::Bind(&MediaRouterWebUIMessageHandler::OnJoinRoute,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
kCloseDialog,
base::Bind(&MediaRouterWebUIMessageHandler::OnCloseDialog,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
kReportClickedSinkIndex,
base::Bind(&MediaRouterWebUIMessageHandler::OnReportClickedSinkIndex,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
kReportInitialState,
base::Bind(&MediaRouterWebUIMessageHandler::OnReportInitialState,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
kReportInitialAction,
base::Bind(&MediaRouterWebUIMessageHandler::OnReportInitialAction,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
kReportRouteCreation,
base::Bind(&MediaRouterWebUIMessageHandler::OnReportRouteCreation,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
kReportSelectedCastMode,
base::Bind(&MediaRouterWebUIMessageHandler::OnReportSelectedCastMode,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
kReportNavigateToView,
base::Bind(&MediaRouterWebUIMessageHandler::OnReportNavigateToView,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
kReportSinkCount,
base::Bind(&MediaRouterWebUIMessageHandler::OnReportSinkCount,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
kReportTimeToClickSink,
base::Bind(&MediaRouterWebUIMessageHandler::OnReportTimeToClickSink,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
kReportTimeToInitialActionClose,
base::Bind(
&MediaRouterWebUIMessageHandler::OnReportTimeToInitialActionClose,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
kOnInitialDataReceived,
base::Bind(&MediaRouterWebUIMessageHandler::OnInitialDataReceived,
base::Unretained(this)));
}
void MediaRouterWebUIMessageHandler::OnRequestInitialData(
const base::ListValue* args) {
DVLOG(1) << "OnRequestInitialData";
media_router_ui_->OnUIInitiallyLoaded();
base::DictionaryValue initial_data;
#if defined(GOOGLE_CHROME_BUILD)
// "Casting to a Hangout from Chrome" Chromecast help center page.
initial_data.SetString("firstRunFlowCloudPrefLearnMoreUrl",
base::StringPrintf(kHelpPageUrlPrefix, 6320939));
#endif // defined(GOOGLE_CHROME_BUILD)
// General Chromecast learn more page.
initial_data.SetString("firstRunFlowLearnMoreUrl", kCastLearnMorePageUrl);
// "No Cast devices found?" Chromecast help center page.
initial_data.SetString("deviceMissingUrl",
base::StringPrintf(kHelpPageUrlPrefix, 3249268));
scoped_ptr<base::ListValue> sinks(SinksToValue(media_router_ui_->sinks()));
initial_data.Set("sinks", sinks.release());
scoped_ptr<base::ListValue> routes(RoutesToValue(media_router_ui_->routes(),
media_router_ui_->joinable_route_ids(),
media_router_ui_->GetRouteProviderExtensionId()));
initial_data.Set("routes", routes.release());
const std::set<MediaCastMode> cast_modes = media_router_ui_->cast_modes();
scoped_ptr<base::ListValue> cast_modes_list(
CastModesToValue(cast_modes,
media_router_ui_->GetPresentationRequestSourceName()));
initial_data.Set("castModes", cast_modes_list.release());
Profile* profile = Profile::FromWebUI(web_ui());
bool first_run_flow_acknowledged =
profile->GetPrefs()->GetBoolean(
prefs::kMediaRouterFirstRunFlowAcknowledged);
initial_data.SetBoolean("wasFirstRunFlowAcknowledged",
first_run_flow_acknowledged);
bool show_cloud_pref = false;
#if defined(GOOGLE_CHROME_BUILD)
// Cloud services preference is shown if user is logged in and has sync
// enabled. If the user enables sync after acknowledging the first run flow,
// this is treated as the user opting into Google services, including cloud
// services, if the browser is a Chrome branded build.
if (!profile->GetPrefs()->GetBoolean(
prefs::kMediaRouterCloudServicesPrefSet) &&
profile->IsSyncAllowed()) {
SigninManagerBase* signin_manager =
SigninManagerFactory::GetForProfile(profile);
show_cloud_pref = signin_manager && signin_manager->IsAuthenticated() &&
ProfileSyncServiceFactory::GetForProfile(profile)->IsSyncActive();
}
#endif // defined(GOOGLE_CHROME_BUILD)
initial_data.SetBoolean("showFirstRunFlowCloudPref", show_cloud_pref);
web_ui()->CallJavascriptFunction(kSetInitialData, initial_data);
media_router_ui_->UIInitialized();
}
void MediaRouterWebUIMessageHandler::OnCreateRoute(
const base::ListValue* args) {
DVLOG(1) << "OnCreateRoute";
const base::DictionaryValue* args_dict = nullptr;
std::string sink_id;
int cast_mode_num = -1;
if (!args->GetDictionary(0, &args_dict) ||
!args_dict->GetString("sinkId", &sink_id) ||
!args_dict->GetInteger("selectedCastMode", &cast_mode_num)) {
DVLOG(1) << "Unable to extract args.";
return;
}
if (sink_id.empty()) {
DVLOG(1) << "Media Route UI did not respond with a "
<< "valid sink ID. Aborting.";
return;
}
if (!IsValidCastModeNum(cast_mode_num)) {
// TODO(imcheng): Record error condition with UMA.
DVLOG(1) << "Invalid cast mode: " << cast_mode_num << ". Aborting.";
return;
}
MediaRouterUI* media_router_ui =
static_cast<MediaRouterUI*>(web_ui()->GetController());
if (media_router_ui->HasPendingRouteRequest()) {
DVLOG(1) << "UI already has pending route request. Ignoring.";
Issue issue(
l10n_util::GetStringUTF8(IDS_MEDIA_ROUTER_ISSUE_PENDING_ROUTE),
std::string(), IssueAction(IssueAction::TYPE_DISMISS),
std::vector<IssueAction>(), std::string(), Issue::NOTIFICATION,
false, std::string());
media_router_ui_->AddIssue(issue);
return;
}
DVLOG(2) << __FUNCTION__ << ": sink id: " << sink_id
<< ", cast mode: " << cast_mode_num;
// TODO(haibinlu): Pass additional parameters into the CreateRoute request,
// e.g. low-fps-mirror, user-override. (crbug.com/490364)
if (!media_router_ui->CreateRoute(
sink_id, static_cast<MediaCastMode>(cast_mode_num))) {
// TODO(imcheng): Need to add an issue if failed to initiate a CreateRoute
// request.
DVLOG(1) << "Error initiating route request.";
}
}
void MediaRouterWebUIMessageHandler::OnAcknowledgeFirstRunFlow(
const base::ListValue* args) {
DVLOG(1) << "OnAcknowledgeFirstRunFlow";
Profile::FromWebUI(web_ui())->GetPrefs()->SetBoolean(
prefs::kMediaRouterFirstRunFlowAcknowledged, true);
#if defined(GOOGLE_CHROME_BUILD)
bool enabled_cloud_services = false;
if (!args->GetBoolean(0, &enabled_cloud_services)) {
DVLOG(1) << "Unable to extract args.";
return;
}
Profile::FromWebUI(web_ui())->GetPrefs()->SetBoolean(
prefs::kMediaRouterEnableCloudServices, enabled_cloud_services);
Profile::FromWebUI(web_ui())->GetPrefs()->SetBoolean(
prefs::kMediaRouterCloudServicesPrefSet, true);
#endif // defined(GOOGLE_CHROME_BUILD)
}
void MediaRouterWebUIMessageHandler::OnActOnIssue(
const base::ListValue* args) {
DVLOG(1) << "OnActOnIssue";
const base::DictionaryValue* args_dict = nullptr;
Issue::Id issue_id;
int action_type_num = -1;
if (!args->GetDictionary(0, &args_dict) ||
!args_dict->GetString("issueId", &issue_id) ||
!args_dict->GetInteger("actionType", &action_type_num)) {
DVLOG(1) << "Unable to extract args.";
return;
}
if (!IsValidIssueActionTypeNum(action_type_num)) {
DVLOG(1) << "Invalid action type: " << action_type_num;
return;
}
IssueAction::Type action_type =
static_cast<IssueAction::Type>(action_type_num);
if (ActOnIssueType(action_type, args_dict))
DVLOG(1) << "ActOnIssueType failed for Issue ID " << issue_id;
media_router_ui_->ClearIssue(issue_id);
}
void MediaRouterWebUIMessageHandler::OnJoinRoute(const base::ListValue* args) {
DVLOG(1) << "OnJoinRoute";
const base::DictionaryValue* args_dict = nullptr;
std::string route_id;
std::string sink_id;
if (!args->GetDictionary(0, &args_dict) ||
!args_dict->GetString("sinkId", &sink_id) ||
!args_dict->GetString("routeId", &route_id)) {
DVLOG(1) << "Unable to extract args.";
return;
}
if (sink_id.empty()) {
DVLOG(1) << "Media Route UI did not respond with a "
<< "valid sink ID. Aborting.";
return;
}
if (route_id.empty()) {
DVLOG(1) << "Media Route UI did not respond with a "
<< "valid route ID. Aborting.";
return;
}
MediaRouterUI* media_router_ui =
static_cast<MediaRouterUI*>(web_ui()->GetController());
if (media_router_ui->HasPendingRouteRequest()) {
DVLOG(1) << "UI already has pending route request. Ignoring.";
Issue issue(
l10n_util::GetStringUTF8(IDS_MEDIA_ROUTER_ISSUE_PENDING_ROUTE),
std::string(), IssueAction(IssueAction::TYPE_DISMISS),
std::vector<IssueAction>(), std::string(), Issue::NOTIFICATION,
false, std::string());
media_router_ui_->AddIssue(issue);
return;
}
if (!media_router_ui_->ConnectRoute(sink_id, route_id)) {
// TODO(boetger): Need to add an issue if failed to initiate a JoinRoute
// request.
DVLOG(1) << "Error initiating route join request.";
}
}
void MediaRouterWebUIMessageHandler::OnCloseRoute(const base::ListValue* args) {
DVLOG(1) << "OnCloseRoute";
const base::DictionaryValue* args_dict = nullptr;
std::string route_id;
bool is_local = false;
if (!args->GetDictionary(0, &args_dict) ||
!args_dict->GetString("routeId", &route_id) ||
!args_dict->GetBoolean("isLocal", &is_local)) {
DVLOG(1) << "Unable to extract args.";
return;
}
media_router_ui_->CloseRoute(route_id);
UMA_HISTOGRAM_BOOLEAN("MediaRouter.Ui.Action.StopRoute", !is_local);
}
void MediaRouterWebUIMessageHandler::OnCloseDialog(
const base::ListValue* args) {
DVLOG(1) << "OnCloseDialog";
if (dialog_closing_)
return;
dialog_closing_ = true;
media_router_ui_->Close();
}
void MediaRouterWebUIMessageHandler::OnReportClickedSinkIndex(
const base::ListValue* args) {
DVLOG(1) << "OnReportClickedSinkIndex";
int index;
if (!args->GetInteger(0, &index)) {
DVLOG(1) << "Unable to extract args.";
return;
}
UMA_HISTOGRAM_SPARSE_SLOWLY("MediaRouter.Ui.Action.StartLocalPosition",
std::min(index, 100));
}
void MediaRouterWebUIMessageHandler::OnReportInitialAction(
const base::ListValue* args) {
DVLOG(1) << "OnReportInitialAction";
int action;
if (!args->GetInteger(0, &action)) {
DVLOG(1) << "Unable to extract args.";
return;
}
media_router::MediaRouterMetrics::RecordMediaRouterInitialUserAction(
static_cast<MediaRouterUserAction>(action));
}
void MediaRouterWebUIMessageHandler::OnReportInitialState(
const base::ListValue* args) {
DVLOG(1) << "OnReportInitialState";
std::string initial_view;
if (!args->GetString(0, &initial_view)) {
DVLOG(1) << "Unable to extract args.";
return;
}
bool sink_list_state = initial_view == "sink-list";
DCHECK(sink_list_state || (initial_view == "route-details"));
UMA_HISTOGRAM_BOOLEAN("MediaRouter.Ui.InitialState", sink_list_state);
}
void MediaRouterWebUIMessageHandler::OnReportNavigateToView(
const base::ListValue* args) {
DVLOG(1) << "OnReportNavigateToView";
std::string view;
if (!args->GetString(0, &view)) {
DVLOG(1) << "Unable to extract args.";
return;
}
if (view == "cast-mode-list") {
base::RecordAction(base::UserMetricsAction(
"MediaRouter_Ui_Navigate_SinkListToSource"));
} else if (view == "route-details") {
base::RecordAction(base::UserMetricsAction(
"MediaRouter_Ui_Navigate_SinkListToRouteDetails"));
} else if (view == "sink-list") {
base::RecordAction(base::UserMetricsAction(
"MediaRouter_Ui_Navigate_RouteDetailsToSinkList"));
}
}
void MediaRouterWebUIMessageHandler::OnReportRouteCreation(
const base::ListValue* args) {
DVLOG(1) << "OnReportRouteCreation";
bool route_created_successfully;
if (!args->GetBoolean(0, &route_created_successfully)) {
DVLOG(1) << "Unable to extract args.";
return;
}
UMA_HISTOGRAM_BOOLEAN("MediaRouter.Ui.Action.StartLocalSessionSuccessful",
route_created_successfully);
}
void MediaRouterWebUIMessageHandler::OnReportSelectedCastMode(
const base::ListValue* args) {
DVLOG(1) << "OnReportSelectedCastMode";
int cast_mode_type;
if (!args->GetInteger(0, &cast_mode_type)) {
DVLOG(1) << "Unable to extract args.";
return;
}
UMA_HISTOGRAM_SPARSE_SLOWLY("MediaRouter.Ui.Navigate.SourceSelection",
cast_mode_type);
}
void MediaRouterWebUIMessageHandler::OnReportSinkCount(
const base::ListValue* args) {
DVLOG(1) << "OnReportSinkCount";
int sink_count;
if (!args->GetInteger(0, &sink_count)) {
DVLOG(1) << "Unable to extract args.";
return;
}
UMA_HISTOGRAM_COUNTS_100("MediaRouter.Ui.Device.Count", sink_count);
}
void MediaRouterWebUIMessageHandler::OnReportTimeToClickSink(
const base::ListValue* args) {
DVLOG(1) << "OnReportTimeToClickSink";
double time_to_click;
if (!args->GetDouble(0, &time_to_click)) {
DVLOG(1) << "Unable to extract args.";
return;
}
UMA_HISTOGRAM_TIMES("MediaRouter.Ui.Action.StartLocal.Latency",
base::TimeDelta::FromMillisecondsD(time_to_click));
}
void MediaRouterWebUIMessageHandler::OnReportTimeToInitialActionClose(
const base::ListValue* args) {
DVLOG(1) << "OnReportTimeToInitialActionClose";
double time_to_close;
if (!args->GetDouble(0, &time_to_close)) {
VLOG(0) << "Unable to extract args.";
return;
}
UMA_HISTOGRAM_TIMES("MediaRouter.Ui.Action.CloseLatency",
base::TimeDelta::FromMillisecondsD(time_to_close));
}
void MediaRouterWebUIMessageHandler::OnInitialDataReceived(
const base::ListValue* args) {
DVLOG(1) << "OnInitialDataReceived";
media_router_ui_->OnUIInitialDataReceived();
}
bool MediaRouterWebUIMessageHandler::ActOnIssueType(
const IssueAction::Type& action_type,
const base::DictionaryValue* args) {
if (action_type == IssueAction::TYPE_LEARN_MORE) {
std::string learn_more_url = GetLearnMoreUrl(args);
if (learn_more_url.empty())
return false;
scoped_ptr<base::ListValue> open_args(new base::ListValue);
open_args->AppendString(learn_more_url);
web_ui()->CallJavascriptFunction(kWindowOpen, *open_args);
return true;
} else {
// Do nothing; no other issue action types require any other action.
return true;
}
}
} // namespace media_router