Elim cr-checkbox
[chromium-blink-merge.git] / chrome / browser / ui / webui / media_router / media_router_webui_message_handler.cc
blob5a0b66c2f992021e26a5356da2d34a4e9850d8dc
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/webui/media_router/media_router_webui_message_handler.h"
7 #include <string>
9 #include "base/bind.h"
10 #include "base/strings/stringprintf.h"
11 #include "chrome/browser/media/router/issue.h"
12 #include "chrome/browser/ui/webui/media_router/media_router_ui.h"
13 #include "chrome/grit/generated_resources.h"
14 #include "extensions/common/constants.h"
15 #include "ui/base/l10n/l10n_util.h"
17 namespace media_router {
19 namespace {
21 const char kHelpPageUrlPrefix[] =
22 "https://support.google.com/chromecast/answer/%d";
24 // Message names.
25 const char kRequestInitialData[] = "requestInitialData";
26 const char kCreateRoute[] = "requestRoute";
27 const char kActOnIssue[] = "actOnIssue";
28 const char kCloseRoute[] = "closeRoute";
29 const char kCloseDialog[] = "closeDialog";
31 // JS function names.
32 const char kSetInitialData[] = "media_router.ui.setInitialData";
33 const char kOnCreateRouteResponseReceived[] =
34 "media_router.ui.onCreateRouteResponseReceived";
35 const char kSetIssue[] = "media_router.ui.setIssue";
36 const char kSetSinkList[] = "media_router.ui.setSinkList";
37 const char kSetRouteList[] = "media_router.ui.setRouteList";
38 const char kSetCastModeList[] = "media_router.ui.setCastModeList";
39 const char kWindowOpen[] = "window.open";
41 scoped_ptr<base::ListValue> SinksToValue(
42 const std::vector<MediaSinkWithCastModes>& sinks) {
43 scoped_ptr<base::ListValue> value(new base::ListValue);
45 for (const MediaSinkWithCastModes& sink_with_cast_modes : sinks) {
46 scoped_ptr<base::DictionaryValue> sink_val(new base::DictionaryValue);
48 const MediaSink& sink = sink_with_cast_modes.sink;
49 sink_val->SetString("id", sink.id());
50 sink_val->SetString("name", sink.name());
51 sink_val->SetInteger("iconType", sink.icon_type());
52 sink_val->SetBoolean("isLaunching", sink.is_launching());
54 scoped_ptr<base::ListValue> cast_modes_val(new base::ListValue);
55 for (MediaCastMode cast_mode : sink_with_cast_modes.cast_modes)
56 cast_modes_val->AppendInteger(cast_mode);
57 sink_val->Set("castModes", cast_modes_val.Pass());
59 value->Append(sink_val.release());
62 return value.Pass();
65 scoped_ptr<base::DictionaryValue> RouteToValue(
66 const MediaRoute& route, const std::string& extension_id) {
67 scoped_ptr<base::DictionaryValue> dictionary(new base::DictionaryValue);
69 dictionary->SetString("id", route.media_route_id());
70 dictionary->SetString("sinkId", route.media_sink_id());
71 dictionary->SetString("description", route.description());
72 dictionary->SetBoolean("isLocal", route.is_local());
74 const std::string& custom_path = route.custom_controller_path();
76 if (!custom_path.empty()) {
77 std::string full_custom_controller_path = base::StringPrintf("%s://%s/%s",
78 extensions::kExtensionScheme, extension_id.c_str(),
79 custom_path.c_str());
80 DCHECK(GURL(full_custom_controller_path).is_valid());
81 dictionary->SetString("customControllerPath",
82 full_custom_controller_path);
85 return dictionary.Pass();
88 scoped_ptr<base::ListValue> RoutesToValue(
89 const std::vector<MediaRoute>& routes, const std::string& extension_id) {
90 scoped_ptr<base::ListValue> value(new base::ListValue);
92 for (const MediaRoute& route : routes) {
93 scoped_ptr<base::DictionaryValue> route_val(RouteToValue(route,
94 extension_id));
95 value->Append(route_val.release());
98 return value.Pass();
101 scoped_ptr<base::ListValue> CastModesToValue(const CastModeSet& cast_modes,
102 const std::string& source_host) {
103 scoped_ptr<base::ListValue> value(new base::ListValue);
105 for (const MediaCastMode& cast_mode : cast_modes) {
106 scoped_ptr<base::DictionaryValue> cast_mode_val(new base::DictionaryValue);
107 cast_mode_val->SetInteger("type", cast_mode);
108 cast_mode_val->SetString(
109 "description", MediaCastModeToDescription(cast_mode, source_host));
110 cast_mode_val->SetString("host", source_host);
111 value->Append(cast_mode_val.release());
114 return value.Pass();
117 // Returns an Issue dictionary created from |issue| that can be used in WebUI.
118 scoped_ptr<base::DictionaryValue> IssueToValue(const Issue& issue) {
119 scoped_ptr<base::DictionaryValue> dictionary(new base::DictionaryValue);
120 dictionary->SetString("id", issue.id());
121 dictionary->SetString("title", issue.title());
122 dictionary->SetString("message", issue.message());
123 dictionary->SetInteger("defaultActionType", issue.default_action().type());
124 if (!issue.secondary_actions().empty()) {
125 dictionary->SetInteger("secondaryActionType",
126 issue.secondary_actions().begin()->type());
128 if (!issue.route_id().empty())
129 dictionary->SetString("routeId", issue.route_id());
130 dictionary->SetBoolean("isBlocking", issue.is_blocking());
132 return dictionary.Pass();
135 bool IsValidIssueActionTypeNum(int issue_action_type_num) {
136 return issue_action_type_num >= 0 &&
137 issue_action_type_num < IssueAction::TYPE_MAX;
140 // Composes a "learn more" URL. The URL depends on template arguments in |args|.
141 // Returns an empty string if |args| is invalid.
142 std::string GetLearnMoreUrl(const base::DictionaryValue* args) {
143 // TODO(imcheng): The template arguments for determining the learn more URL
144 // should come from the Issue object in the browser, not from WebUI.
145 int help_page_id = -1;
146 if (!args->GetInteger("helpPageId", &help_page_id) || help_page_id < 0) {
147 DVLOG(1) << "Invalid help page id.";
148 return std::string();
151 std::string help_url = base::StringPrintf(kHelpPageUrlPrefix, help_page_id);
152 if (!GURL(help_url).is_valid()) {
153 DVLOG(1) << "Error: URL is invalid and cannot be opened.";
154 return std::string();
156 return help_url;
159 } // namespace
161 MediaRouterWebUIMessageHandler::MediaRouterWebUIMessageHandler(
162 MediaRouterUI* media_router_ui)
163 : dialog_closing_(false),
164 media_router_ui_(media_router_ui) {
165 DCHECK(media_router_ui_);
168 MediaRouterWebUIMessageHandler::~MediaRouterWebUIMessageHandler() {
171 void MediaRouterWebUIMessageHandler::UpdateSinks(
172 const std::vector<MediaSinkWithCastModes>& sinks) {
173 DVLOG(2) << "UpdateSinks";
174 scoped_ptr<base::ListValue> sinks_val(SinksToValue(sinks));
175 web_ui()->CallJavascriptFunction(kSetSinkList, *sinks_val);
178 void MediaRouterWebUIMessageHandler::UpdateRoutes(
179 const std::vector<MediaRoute>& routes) {
180 DVLOG(2) << "UpdateRoutes";
181 scoped_ptr<base::ListValue> routes_val(RoutesToValue(routes,
182 media_router_ui_->GetRouteProviderExtensionId()));
183 web_ui()->CallJavascriptFunction(kSetRouteList, *routes_val);
186 void MediaRouterWebUIMessageHandler::UpdateCastModes(
187 const CastModeSet& cast_modes,
188 const std::string& source_host) {
189 DVLOG(2) << "UpdateCastModes";
190 scoped_ptr<base::ListValue> cast_modes_val(
191 CastModesToValue(cast_modes, source_host));
192 web_ui()->CallJavascriptFunction(kSetCastModeList, *cast_modes_val);
195 void MediaRouterWebUIMessageHandler::OnCreateRouteResponseReceived(
196 const MediaSink::Id& sink_id,
197 const MediaRoute* route) {
198 DVLOG(2) << "OnCreateRouteResponseReceived";
199 if (route) {
200 scoped_ptr<base::DictionaryValue> route_value(RouteToValue(*route,
201 media_router_ui_->GetRouteProviderExtensionId()));
202 web_ui()->CallJavascriptFunction(kOnCreateRouteResponseReceived,
203 base::StringValue(sink_id), *route_value);
204 } else {
205 web_ui()->CallJavascriptFunction(kOnCreateRouteResponseReceived,
206 base::StringValue(sink_id),
207 *base::Value::CreateNullValue());
211 void MediaRouterWebUIMessageHandler::UpdateIssue(const Issue* issue) {
212 DVLOG(2) << "UpdateIssue";
213 if (issue) {
214 scoped_ptr<base::DictionaryValue> issue_val(IssueToValue(*issue));
215 web_ui()->CallJavascriptFunction(kSetIssue, *issue_val);
216 } else {
217 // Clears the issue in the WebUI.
218 web_ui()->CallJavascriptFunction(kSetIssue);
222 void MediaRouterWebUIMessageHandler::RegisterMessages() {
223 web_ui()->RegisterMessageCallback(
224 kRequestInitialData,
225 base::Bind(&MediaRouterWebUIMessageHandler::OnRequestInitialData,
226 base::Unretained(this)));
227 web_ui()->RegisterMessageCallback(
228 kCreateRoute,
229 base::Bind(&MediaRouterWebUIMessageHandler::OnCreateRoute,
230 base::Unretained(this)));
231 web_ui()->RegisterMessageCallback(
232 kActOnIssue,
233 base::Bind(&MediaRouterWebUIMessageHandler::OnActOnIssue,
234 base::Unretained(this)));
235 web_ui()->RegisterMessageCallback(
236 kCloseRoute,
237 base::Bind(&MediaRouterWebUIMessageHandler::OnCloseRoute,
238 base::Unretained(this)));
239 web_ui()->RegisterMessageCallback(
240 kCloseDialog,
241 base::Bind(&MediaRouterWebUIMessageHandler::OnCloseDialog,
242 base::Unretained(this)));
245 void MediaRouterWebUIMessageHandler::OnRequestInitialData(
246 const base::ListValue* args) {
247 DVLOG(1) << "OnRequestInitialData";
248 base::DictionaryValue initial_data;
250 initial_data.SetString("headerText",
251 media_router_ui_->GetInitialHeaderText());
252 initial_data.SetString("headerTextTooltip",
253 media_router_ui_->GetInitialHeaderTextTooltip());
255 // "No Cast devices found?" Chromecast help center page.
256 initial_data.SetString("deviceMissingUrl",
257 base::StringPrintf(kHelpPageUrlPrefix, 3249268));
259 scoped_ptr<base::ListValue> sinks(SinksToValue(media_router_ui_->sinks()));
260 initial_data.Set("sinks", sinks.release());
262 scoped_ptr<base::ListValue> routes(RoutesToValue(media_router_ui_->routes(),
263 media_router_ui_->GetRouteProviderExtensionId()));
264 initial_data.Set("routes", routes.release());
266 scoped_ptr<base::ListValue> cast_modes(CastModesToValue(
267 media_router_ui_->cast_modes(),
268 media_router_ui_->GetFrameURLHost()));
269 initial_data.Set("castModes", cast_modes.release());
271 web_ui()->CallJavascriptFunction(kSetInitialData, initial_data);
272 media_router_ui_->UIInitialized();
275 void MediaRouterWebUIMessageHandler::OnCreateRoute(
276 const base::ListValue* args) {
277 DVLOG(1) << "OnCreateRoute";
278 const base::DictionaryValue* args_dict = nullptr;
279 std::string sink_id;
280 int cast_mode_num = -1;
281 if (!args->GetDictionary(0, &args_dict) ||
282 !args_dict->GetString("sinkId", &sink_id) ||
283 !args_dict->GetInteger("selectedCastMode", &cast_mode_num)) {
284 DVLOG(1) << "Unable to extract args.";
285 return;
288 if (sink_id.empty()) {
289 DVLOG(1) << "Media Route UI did not respond with a "
290 << "valid sink ID. Aborting.";
291 return;
294 MediaRouterUI* media_router_ui =
295 static_cast<MediaRouterUI*>(web_ui()->GetController());
296 if (media_router_ui->has_pending_route_request()) {
297 DVLOG(1) << "UI already has pending route request. Ignoring.";
298 Issue issue(
299 l10n_util::GetStringUTF8(IDS_MEDIA_ROUTER_ISSUE_PENDING_ROUTE),
300 std::string(), IssueAction(IssueAction::TYPE_DISMISS),
301 std::vector<IssueAction>(), std::string(), Issue::NOTIFICATION,
302 false, std::string());
303 media_router_ui_->AddIssue(issue);
304 return;
307 DVLOG(2) << "sink id: " << sink_id << ", cast mode: " << cast_mode_num;
309 // TODO(haibinlu): Pass additional parameters into the CreateRoute request,
310 // e.g. low-fps-mirror, user-override. (crbug.com/490364)
311 bool success = false;
312 if (IsValidCastModeNum(cast_mode_num)) {
313 // User explicitly selected cast mode.
314 DVLOG(2) << "Cast mode override: " << cast_mode_num;
315 success = media_router_ui->CreateRouteWithCastModeOverride(
316 sink_id, static_cast<MediaCastMode>(cast_mode_num));
317 } else {
318 success = media_router_ui->CreateRoute(sink_id);
321 if (!success) {
322 // The provider will handle sending an issue for a failed route request.
323 DVLOG(1) << "Error initiating route request.";
327 void MediaRouterWebUIMessageHandler::OnActOnIssue(
328 const base::ListValue* args) {
329 DVLOG(1) << "OnActOnIssue";
330 const base::DictionaryValue* args_dict = nullptr;
331 Issue::Id issue_id;
332 int action_type_num = -1;
333 if (!args->GetDictionary(0, &args_dict) ||
334 !args_dict->GetString("issueId", &issue_id) ||
335 !args_dict->GetInteger("actionType", &action_type_num)) {
336 DVLOG(1) << "Unable to extract args.";
337 return;
339 if (!IsValidIssueActionTypeNum(action_type_num)) {
340 DVLOG(1) << "Invalid action type: " << action_type_num;
341 return;
343 IssueAction::Type action_type =
344 static_cast<IssueAction::Type>(action_type_num);
345 if (ActOnIssueType(action_type, args_dict))
346 DVLOG(1) << "ActOnIssueType failed for Issue ID " << issue_id;
347 media_router_ui_->ClearIssue(issue_id);
350 void MediaRouterWebUIMessageHandler::OnCloseRoute(
351 const base::ListValue* args) {
352 DVLOG(1) << "OnCloseRoute";
353 const base::DictionaryValue* args_dict = nullptr;
354 std::string route_id;
355 if (!args->GetDictionary(0, &args_dict) ||
356 !args_dict->GetString("routeId", &route_id)) {
357 DVLOG(1) << "Unable to extract args.";
358 return;
360 media_router_ui_->CloseRoute(route_id);
363 void MediaRouterWebUIMessageHandler::OnCloseDialog(
364 const base::ListValue* args) {
365 DVLOG(1) << "OnCloseDialog";
366 if (dialog_closing_)
367 return;
369 dialog_closing_ = true;
370 media_router_ui_->Close();
373 bool MediaRouterWebUIMessageHandler::ActOnIssueType(
374 const IssueAction::Type& action_type,
375 const base::DictionaryValue* args) {
376 if (action_type == IssueAction::TYPE_LEARN_MORE) {
377 std::string learn_more_url = GetLearnMoreUrl(args);
378 if (learn_more_url.empty())
379 return false;
380 scoped_ptr<base::ListValue> open_args(new base::ListValue);
381 open_args->AppendString(learn_more_url);
382 web_ui()->CallJavascriptFunction(kWindowOpen, *open_args);
383 return true;
384 } else {
385 // Do nothing; no other issue action types require any other action.
386 return true;
390 } // namespace media_router