Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / ui / webui / media_router / media_router_ui.cc
blob296124c1f59c59f22c0c1b7be8b843fb45a79392
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_ui.h"
7 #include <string>
9 #include "base/strings/string_util.h"
10 #include "chrome/browser/media/router/create_presentation_session_request.h"
11 #include "chrome/browser/media/router/issue.h"
12 #include "chrome/browser/media/router/issues_observer.h"
13 #include "chrome/browser/media/router/media_route.h"
14 #include "chrome/browser/media/router/media_router.h"
15 #include "chrome/browser/media/router/media_router_factory.h"
16 #include "chrome/browser/media/router/media_router_mojo_impl.h"
17 #include "chrome/browser/media/router/media_routes_observer.h"
18 #include "chrome/browser/media/router/media_sink.h"
19 #include "chrome/browser/media/router/media_sinks_observer.h"
20 #include "chrome/browser/media/router/media_source.h"
21 #include "chrome/browser/media/router/media_source_helper.h"
22 #include "chrome/browser/media/router/presentation_service_delegate_impl.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/browser/sessions/session_tab_helper.h"
25 #include "chrome/browser/ui/webui/media_router/media_router_localized_strings_provider.h"
26 #include "chrome/browser/ui/webui/media_router/media_router_resources_provider.h"
27 #include "chrome/browser/ui/webui/media_router/media_router_webui_message_handler.h"
28 #include "chrome/common/url_constants.h"
29 #include "content/public/browser/web_contents.h"
30 #include "content/public/browser/web_ui.h"
31 #include "content/public/browser/web_ui_data_source.h"
32 #include "ui/web_dialogs/web_dialog_delegate.h"
34 namespace media_router {
36 namespace {
38 std::string GetHostFromURL(const GURL& gurl) {
39 if (gurl.is_empty())
40 return std::string();
41 std::string host = gurl.host();
42 if (base::StartsWith(host, "www.", base::CompareCase::INSENSITIVE_ASCII))
43 host = host.substr(4);
44 return host;
47 } // namespace
49 // This class calls to refresh the UI when the highest priority issue is
50 // updated.
51 class MediaRouterUI::UIIssuesObserver : public IssuesObserver {
52 public:
53 UIIssuesObserver(MediaRouter* router, MediaRouterUI* ui)
54 : IssuesObserver(router), ui_(ui) {
55 DCHECK(ui);
58 ~UIIssuesObserver() override {}
60 // IssuesObserver implementation.
61 void OnIssueUpdated(const Issue* issue) override { ui_->SetIssue(issue); }
63 private:
64 // Reference back to the owning MediaRouterUI instance.
65 MediaRouterUI* ui_;
67 DISALLOW_COPY_AND_ASSIGN(UIIssuesObserver);
70 MediaRouterUI::UIMediaRoutesObserver::UIMediaRoutesObserver(
71 MediaRouter* router,
72 const RoutesUpdatedCallback& callback)
73 : MediaRoutesObserver(router), callback_(callback) {
74 DCHECK(!callback_.is_null());
77 MediaRouterUI::UIMediaRoutesObserver::~UIMediaRoutesObserver() {}
79 void MediaRouterUI::UIMediaRoutesObserver::OnRoutesUpdated(
80 const std::vector<MediaRoute>& routes) {
81 std::vector<MediaRoute> routes_for_display;
82 for (const MediaRoute& route : routes) {
83 if (route.for_display()) {
84 #ifndef NDEBUG
85 for (const MediaRoute& existing_route : routes_for_display) {
86 if (existing_route.media_sink_id() == route.media_sink_id()) {
87 DVLOG(2) << "Received another route for display with the same sink"
88 << " id as an existing route. "
89 << route.media_route_id() << " has the same sink id as "
90 << existing_route.media_sink_id() << ".";
93 #endif
94 routes_for_display.push_back(route);
98 callback_.Run(routes_for_display);
101 MediaRouterUI::MediaRouterUI(content::WebUI* web_ui)
102 : ConstrainedWebDialogUI(web_ui),
103 handler_(new MediaRouterWebUIMessageHandler(this)),
104 ui_initialized_(false),
105 has_pending_route_request_(false),
106 requesting_route_for_default_source_(false),
107 initiator_(nullptr),
108 router_(nullptr),
109 weak_factory_(this) {
110 // Create a WebUIDataSource containing the chrome://media-router page's
111 // content.
112 scoped_ptr<content::WebUIDataSource> html_source(
113 content::WebUIDataSource::Create(chrome::kChromeUIMediaRouterHost));
115 content::WebContents* wc = web_ui->GetWebContents();
116 DCHECK(wc);
118 router_ = static_cast<MediaRouterMojoImpl*>(
119 MediaRouterFactory::GetApiForBrowserContext(wc->GetBrowserContext()));
121 // Allows UI to load extensionview.
122 // TODO(haibinlu): limit object-src to current extension once crbug/514866
123 // is fixed.
124 html_source->OverrideContentSecurityPolicyObjectSrc("object-src *;");
126 AddLocalizedStrings(html_source.get());
127 AddMediaRouterUIResources(html_source.get());
128 // Ownership of |html_source| is transferred to the BrowserContext.
129 content::WebUIDataSource::Add(Profile::FromWebUI(web_ui),
130 html_source.release());
132 // Ownership of |handler_| is transferred to |web_ui|.
133 web_ui->AddMessageHandler(handler_);
136 MediaRouterUI::~MediaRouterUI() {
137 if (query_result_manager_.get())
138 query_result_manager_->RemoveObserver(this);
139 if (presentation_service_delegate_.get())
140 presentation_service_delegate_->RemoveDefaultMediaSourceObserver(this);
141 // If |presentation_request_| still exists, then it means presentation route
142 // request was never attempted.
143 if (presentation_request_) {
144 presentation_request_->InvokeErrorCallback(content::PresentationError(
145 content::PRESENTATION_ERROR_SESSION_REQUEST_CANCELLED,
146 "Dialog closed."));
150 void MediaRouterUI::InitWithDefaultMediaSource(
151 const base::WeakPtr<PresentationServiceDelegateImpl>& delegate) {
152 DCHECK(delegate);
153 DCHECK(!presentation_service_delegate_);
154 DCHECK(!query_result_manager_.get());
156 presentation_service_delegate_ = delegate;
157 presentation_service_delegate_->AddDefaultMediaSourceObserver(this);
158 InitCommon(presentation_service_delegate_->web_contents(),
159 presentation_service_delegate_->default_source(),
160 presentation_service_delegate_->default_frame_url());
163 void MediaRouterUI::InitWithPresentationSessionRequest(
164 content::WebContents* initiator,
165 const base::WeakPtr<PresentationServiceDelegateImpl>& delegate,
166 scoped_ptr<CreatePresentationSessionRequest> presentation_request) {
167 DCHECK(initiator);
168 DCHECK(presentation_request);
169 DCHECK(!presentation_request_);
170 DCHECK(!query_result_manager_);
172 presentation_request_ = presentation_request.Pass();
173 presentation_service_delegate_ = delegate;
174 InitCommon(initiator, presentation_request_->media_source(),
175 presentation_request_->frame_url());
178 void MediaRouterUI::InitCommon(content::WebContents* initiator,
179 const MediaSource& default_source,
180 const GURL& default_frame_url) {
181 DCHECK(initiator);
182 DCHECK(router_);
184 // Register for Issue and MediaRoute updates.
185 issues_observer_.reset(new UIIssuesObserver(router_, this));
186 routes_observer_.reset(new UIMediaRoutesObserver(
187 router_,
188 base::Bind(&MediaRouterUI::OnRoutesUpdated, base::Unretained(this))));
190 query_result_manager_.reset(new QueryResultManager(router_));
191 query_result_manager_->AddObserver(this);
193 // These modes are always available.
194 query_result_manager_->StartSinksQuery(
195 MediaCastMode::DESKTOP_MIRROR, MediaSourceForDesktop());
196 initiator_ = initiator;
197 MediaSource mirroring_source(
198 MediaSourceForTab(SessionTabHelper::IdForTab(initiator)));
199 query_result_manager_->StartSinksQuery(
200 MediaCastMode::TAB_MIRROR, mirroring_source);
202 OnDefaultMediaSourceChanged(default_source, default_frame_url);
205 void MediaRouterUI::OnDefaultMediaSourceChanged(const MediaSource& source,
206 const GURL& frame_url) {
207 if (source.Empty()) {
208 query_result_manager_->StopSinksQuery(MediaCastMode::DEFAULT);
209 } else {
210 query_result_manager_->StartSinksQuery(MediaCastMode::DEFAULT, source);
212 UpdateSourceHostAndCastModes(frame_url);
215 void MediaRouterUI::UpdateSourceHostAndCastModes(const GURL& frame_url) {
216 DCHECK(query_result_manager_);
217 frame_url_ = frame_url;
218 query_result_manager_->GetSupportedCastModes(&cast_modes_);
219 if (ui_initialized_)
220 handler_->UpdateCastModes(cast_modes_, GetHostFromURL(frame_url_));
223 void MediaRouterUI::Close() {
224 ConstrainedWebDialogDelegate* delegate = GetConstrainedDelegate();
225 if (delegate) {
226 delegate->GetWebDialogDelegate()->OnDialogClosed(std::string());
227 delegate->OnDialogCloseFromWebUI();
231 void MediaRouterUI::UIInitialized() {
232 ui_initialized_ = true;
235 bool MediaRouterUI::CreateRoute(const MediaSink::Id& sink_id) {
236 return DoCreateRoute(sink_id, GetPreferredCastMode(cast_modes_));
239 bool MediaRouterUI::CreateRouteWithCastModeOverride(
240 const MediaSink::Id& sink_id,
241 MediaCastMode cast_mode_override) {
242 // NOTE: It's actually not an override if
243 // |cast_mode_override| == |GetPreferredCastMode(cast_modes_)|.
244 return DoCreateRoute(sink_id, cast_mode_override);
247 void MediaRouterUI::CloseRoute(const MediaRoute::Id& route_id) {
248 router_->CloseRoute(route_id);
251 void MediaRouterUI::AddIssue(const Issue& issue) {
252 router_->AddIssue(issue);
255 void MediaRouterUI::ClearIssue(const std::string& issue_id) {
256 router_->ClearIssue(issue_id);
259 std::string MediaRouterUI::GetInitialHeaderText() const {
260 if (cast_modes_.empty())
261 return std::string();
263 return MediaCastModeToDescription(GetPreferredCastMode(cast_modes_),
264 GetHostFromURL(frame_url_));
267 std::string MediaRouterUI::GetInitialHeaderTextTooltip() const {
268 if (cast_modes_.empty())
269 return std::string();
271 return GetHostFromURL(frame_url_);
274 void MediaRouterUI::OnResultsUpdated(
275 const std::vector<MediaSinkWithCastModes>& sinks) {
276 sinks_ = sinks;
277 if (ui_initialized_)
278 handler_->UpdateSinks(sinks_);
281 void MediaRouterUI::SetIssue(const Issue* issue) {
282 if (ui_initialized_)
283 handler_->UpdateIssue(issue);
286 void MediaRouterUI::OnRoutesUpdated(const std::vector<MediaRoute>& routes) {
287 routes_ = routes;
288 if (ui_initialized_)
289 handler_->UpdateRoutes(routes_);
292 void MediaRouterUI::OnRouteResponseReceived(const MediaSink::Id& sink_id,
293 const MediaRoute* route,
294 const std::string& presentation_id,
295 const std::string& error) {
296 DVLOG(1) << "OnRouteResponseReceived";
297 if (!route) {
298 // The provider will handle sending an issue for a failed route request.
299 DVLOG(0) << "MediaRouteResponse returned error: " << error;
302 handler_->OnCreateRouteResponseReceived(sink_id, route);
303 has_pending_route_request_ = false;
304 requesting_route_for_default_source_ = false;
307 bool MediaRouterUI::DoCreateRoute(const MediaSink::Id& sink_id,
308 MediaCastMode cast_mode) {
309 DCHECK(query_result_manager_.get());
310 DCHECK(initiator_);
312 // Note that there is a rarely-encountered bug, where the MediaCastMode to
313 // MediaSource mapping could have been updated, between when the user
314 // clicked on the UI to start a create route request,
315 // and when this function is called.
316 // However, since the user does not have visibility into the MediaSource, and
317 // that it occurs very rarely in practice, we leave it as-is for now.
318 MediaSource source = query_result_manager_->GetSourceForCastMode(cast_mode);
319 if (source.Empty()) {
320 LOG(ERROR) << "No corresponding MediaSource for cast mode " << cast_mode;
321 return false;
324 has_pending_route_request_ = true;
325 requesting_route_for_default_source_ = cast_mode == MediaCastMode::DEFAULT;
326 GURL origin;
327 // TODO(imcheng): What is the origin if not creating route in DEFAULT mode?
328 if (requesting_route_for_default_source_) {
329 origin = frame_url_.GetOrigin();
330 } else {
331 // Requesting route for mirroring. Use a placeholder URL as origin.
332 origin = GURL(chrome::kChromeUIMediaRouterURL);
334 DCHECK(origin.is_valid());
336 DVLOG(1) << "DoCreateRoute: origin: " << origin;
338 // There are 3 cases. In all cases the MediaRouterUI will need to be notified.
339 // (1) Non-presentation route request (e.g., mirroring). No additional
340 // notification necessary.
341 // (2) Presentation route request for a Presentation API startSession call.
342 // The startSession (CreatePresentationSessionRequest) will need to be
343 // answered with the
344 // route response.
345 // (3) Browser-initiated presentation route request. If successful,
346 // PresentationServiceDelegateImpl will have to be notified. Note that we
347 // treat subsequent route requests from a Presentation API-initiated dialogs
348 // as browser-initiated.
349 std::vector<MediaRouteResponseCallback> route_response_callbacks;
350 route_response_callbacks.push_back(
351 base::Bind(&MediaRouterUI::OnRouteResponseReceived,
352 weak_factory_.GetWeakPtr(), sink_id));
353 if (requesting_route_for_default_source_) {
354 if (presentation_request_) {
355 // |presentation_request_| will be nullptr after this call, as the
356 // object will be transferred to the callback.
357 route_response_callbacks.push_back(
358 base::Bind(&CreatePresentationSessionRequest::HandleRouteResponse,
359 base::Passed(&presentation_request_)));
360 } else if (presentation_service_delegate_) {
361 route_response_callbacks.push_back(
362 base::Bind(&PresentationServiceDelegateImpl::OnRouteResponse,
363 presentation_service_delegate_));
367 router_->CreateRoute(source.id(), sink_id, origin,
368 SessionTabHelper::IdForTab(initiator_),
369 route_response_callbacks);
370 return true;
373 std::string MediaRouterUI::GetFrameURLHost() const {
374 return GetHostFromURL(frame_url_);
377 const std::string& MediaRouterUI::GetRouteProviderExtensionId() const {
378 return router_->media_route_provider_extension_id();
381 } // namespace media_router