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"
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
{
38 std::string
GetHostFromURL(const GURL
& gurl
) {
41 std::string host
= gurl
.host();
42 if (base::StartsWith(host
, "www.", base::CompareCase::INSENSITIVE_ASCII
))
43 host
= host
.substr(4);
49 // This class calls to refresh the UI when the highest priority issue is
51 class MediaRouterUI::UIIssuesObserver
: public IssuesObserver
{
53 UIIssuesObserver(MediaRouter
* router
, MediaRouterUI
* ui
)
54 : IssuesObserver(router
), ui_(ui
) {
58 ~UIIssuesObserver() override
{}
60 // IssuesObserver implementation.
61 void OnIssueUpdated(const Issue
* issue
) override
{ ui_
->SetIssue(issue
); }
64 // Reference back to the owning MediaRouterUI instance.
67 DISALLOW_COPY_AND_ASSIGN(UIIssuesObserver
);
70 MediaRouterUI::UIMediaRoutesObserver::UIMediaRoutesObserver(
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 routes_for_display
.push_back(route
);
87 callback_
.Run(routes_for_display
);
90 MediaRouterUI::MediaRouterUI(content::WebUI
* web_ui
)
91 : ConstrainedWebDialogUI(web_ui
),
92 handler_(new MediaRouterWebUIMessageHandler(this)),
93 ui_initialized_(false),
94 has_pending_route_request_(false),
95 requesting_route_for_default_source_(false),
99 // Create a WebUIDataSource containing the chrome://media-router page's
101 scoped_ptr
<content::WebUIDataSource
> html_source(
102 content::WebUIDataSource::Create(chrome::kChromeUIMediaRouterHost
));
104 content::WebContents
* wc
= web_ui
->GetWebContents();
107 router_
= static_cast<MediaRouterMojoImpl
*>(
108 MediaRouterFactory::GetApiForBrowserContext(wc
->GetBrowserContext()));
110 // Allows UI to load extensionview.
111 // TODO(haibinlu): limit object-src to current extension once crbug/514866
113 html_source
->OverrideContentSecurityPolicyObjectSrc("object-src *;");
115 AddLocalizedStrings(html_source
.get());
116 AddMediaRouterUIResources(html_source
.get());
117 // Ownership of |html_source| is transferred to the BrowserContext.
118 content::WebUIDataSource::Add(Profile::FromWebUI(web_ui
),
119 html_source
.release());
121 // Ownership of |handler_| is transferred to |web_ui|.
122 web_ui
->AddMessageHandler(handler_
);
125 MediaRouterUI::~MediaRouterUI() {
126 if (query_result_manager_
.get())
127 query_result_manager_
->RemoveObserver(this);
128 if (presentation_service_delegate_
.get())
129 presentation_service_delegate_
->RemoveDefaultMediaSourceObserver(this);
130 // If |presentation_request_| still exists, then it means presentation route
131 // request was never attempted.
132 if (presentation_request_
) {
133 presentation_request_
->InvokeErrorCallback(content::PresentationError(
134 content::PRESENTATION_ERROR_SESSION_REQUEST_CANCELLED
,
139 void MediaRouterUI::InitWithDefaultMediaSource(
140 const base::WeakPtr
<PresentationServiceDelegateImpl
>& delegate
) {
142 DCHECK(!presentation_service_delegate_
);
143 DCHECK(!query_result_manager_
.get());
145 presentation_service_delegate_
= delegate
;
146 presentation_service_delegate_
->AddDefaultMediaSourceObserver(this);
147 InitCommon(presentation_service_delegate_
->web_contents(),
148 presentation_service_delegate_
->default_source(),
149 presentation_service_delegate_
->default_frame_url());
152 void MediaRouterUI::InitWithPresentationSessionRequest(
153 content::WebContents
* initiator
,
154 const base::WeakPtr
<PresentationServiceDelegateImpl
>& delegate
,
155 scoped_ptr
<CreatePresentationSessionRequest
> presentation_request
) {
157 DCHECK(presentation_request
);
158 DCHECK(!presentation_request_
);
159 DCHECK(!query_result_manager_
);
161 presentation_request_
= presentation_request
.Pass();
162 presentation_service_delegate_
= delegate
;
163 InitCommon(initiator
, presentation_request_
->media_source(),
164 presentation_request_
->frame_url());
167 void MediaRouterUI::InitCommon(content::WebContents
* initiator
,
168 const MediaSource
& default_source
,
169 const GURL
& default_frame_url
) {
173 // Register for Issue and MediaRoute updates.
174 issues_observer_
.reset(new UIIssuesObserver(router_
, this));
175 routes_observer_
.reset(new UIMediaRoutesObserver(
177 base::Bind(&MediaRouterUI::OnRoutesUpdated
, base::Unretained(this))));
179 query_result_manager_
.reset(new QueryResultManager(router_
));
180 query_result_manager_
->AddObserver(this);
182 // These modes are always available.
183 query_result_manager_
->StartSinksQuery(
184 MediaCastMode::DESKTOP_MIRROR
, MediaSourceForDesktop());
185 initiator_
= initiator
;
186 MediaSource
mirroring_source(
187 MediaSourceForTab(SessionTabHelper::IdForTab(initiator
)));
188 query_result_manager_
->StartSinksQuery(
189 MediaCastMode::TAB_MIRROR
, mirroring_source
);
191 OnDefaultMediaSourceChanged(default_source
, default_frame_url
);
194 void MediaRouterUI::OnDefaultMediaSourceChanged(const MediaSource
& source
,
195 const GURL
& frame_url
) {
196 if (source
.Empty()) {
197 query_result_manager_
->StopSinksQuery(MediaCastMode::DEFAULT
);
199 query_result_manager_
->StartSinksQuery(MediaCastMode::DEFAULT
, source
);
201 UpdateSourceHostAndCastModes(frame_url
);
204 void MediaRouterUI::UpdateSourceHostAndCastModes(const GURL
& frame_url
) {
205 DCHECK(query_result_manager_
);
206 frame_url_
= frame_url
;
207 query_result_manager_
->GetSupportedCastModes(&cast_modes_
);
209 handler_
->UpdateCastModes(cast_modes_
, GetHostFromURL(frame_url_
));
212 void MediaRouterUI::Close() {
213 ConstrainedWebDialogDelegate
* delegate
= GetConstrainedDelegate();
215 delegate
->GetWebDialogDelegate()->OnDialogClosed(std::string());
216 delegate
->OnDialogCloseFromWebUI();
220 void MediaRouterUI::UIInitialized() {
221 ui_initialized_
= true;
224 bool MediaRouterUI::CreateRoute(const MediaSink::Id
& sink_id
) {
225 return DoCreateRoute(sink_id
, GetPreferredCastMode(cast_modes_
));
228 bool MediaRouterUI::CreateRouteWithCastModeOverride(
229 const MediaSink::Id
& sink_id
,
230 MediaCastMode cast_mode_override
) {
231 // NOTE: It's actually not an override if
232 // |cast_mode_override| == |GetPreferredCastMode(cast_modes_)|.
233 return DoCreateRoute(sink_id
, cast_mode_override
);
236 void MediaRouterUI::CloseRoute(const MediaRoute::Id
& route_id
) {
237 router_
->CloseRoute(route_id
);
240 void MediaRouterUI::ClearIssue(const std::string
& issue_id
) {
241 router_
->ClearIssue(issue_id
);
244 std::string
MediaRouterUI::GetInitialHeaderText() const {
245 if (cast_modes_
.empty())
246 return std::string();
248 return MediaCastModeToDescription(GetPreferredCastMode(cast_modes_
),
249 GetHostFromURL(frame_url_
));
252 std::string
MediaRouterUI::GetInitialHeaderTextTooltip() const {
253 if (cast_modes_
.empty())
254 return std::string();
256 return GetHostFromURL(frame_url_
);
259 void MediaRouterUI::OnResultsUpdated(
260 const std::vector
<MediaSinkWithCastModes
>& sinks
) {
263 handler_
->UpdateSinks(sinks_
);
266 void MediaRouterUI::SetIssue(const Issue
* issue
) {
268 handler_
->UpdateIssue(issue
);
271 void MediaRouterUI::OnRoutesUpdated(const std::vector
<MediaRoute
>& routes
) {
274 handler_
->UpdateRoutes(routes_
);
277 void MediaRouterUI::OnRouteResponseReceived(const MediaSink::Id
& sink_id
,
278 const MediaRoute
* route
,
279 const std::string
& presentation_id
,
280 const std::string
& error
) {
281 DVLOG(1) << "OnRouteResponseReceived";
282 // TODO(imcheng): Display error in UI. (crbug.com/490372)
284 DVLOG(0) << "MediaRouteResponse returned error: " << error
;
286 handler_
->OnCreateRouteResponseReceived(sink_id
, route
);
287 has_pending_route_request_
= false;
288 requesting_route_for_default_source_
= false;
291 bool MediaRouterUI::DoCreateRoute(const MediaSink::Id
& sink_id
,
292 MediaCastMode cast_mode
) {
293 DCHECK(query_result_manager_
.get());
296 // Note that there is a rarely-encountered bug, where the MediaCastMode to
297 // MediaSource mapping could have been updated, between when the user
298 // clicked on the UI to start a create route request,
299 // and when this function is called.
300 // However, since the user does not have visibility into the MediaSource, and
301 // that it occurs very rarely in practice, we leave it as-is for now.
302 MediaSource source
= query_result_manager_
->GetSourceForCastMode(cast_mode
);
303 if (source
.Empty()) {
304 LOG(ERROR
) << "No corresponding MediaSource for cast mode " << cast_mode
;
308 has_pending_route_request_
= true;
309 requesting_route_for_default_source_
= cast_mode
== MediaCastMode::DEFAULT
;
311 // TODO(imcheng): What is the origin if not creating route in DEFAULT mode?
312 if (requesting_route_for_default_source_
) {
313 origin
= frame_url_
.GetOrigin();
315 // Requesting route for mirroring. Use a placeholder URL as origin.
316 origin
= GURL(chrome::kChromeUIMediaRouterURL
);
318 DCHECK(origin
.is_valid());
320 DVLOG(1) << "DoCreateRoute: origin: " << origin
;
322 // There are 3 cases. In all cases the MediaRouterUI will need to be notified.
323 // (1) Non-presentation route request (e.g., mirroring). No additional
324 // notification necessary.
325 // (2) Presentation route request for a Presentation API startSession call.
326 // The startSession (CreatePresentationSessionRequest) will need to be
329 // (3) Browser-initiated presentation route request. If successful,
330 // PresentationServiceDelegateImpl will have to be notified. Note that we
331 // treat subsequent route requests from a Presentation API-initiated dialogs
332 // as browser-initiated.
333 std::vector
<MediaRouteResponseCallback
> route_response_callbacks
;
334 route_response_callbacks
.push_back(
335 base::Bind(&MediaRouterUI::OnRouteResponseReceived
,
336 weak_factory_
.GetWeakPtr(), sink_id
));
337 if (requesting_route_for_default_source_
) {
338 if (presentation_request_
) {
339 // |presentation_request_| will be nullptr after this call, as the
340 // object will be transferred to the callback.
341 route_response_callbacks
.push_back(
342 base::Bind(&CreatePresentationSessionRequest::HandleRouteResponse
,
343 base::Passed(&presentation_request_
)));
344 } else if (presentation_service_delegate_
) {
345 route_response_callbacks
.push_back(
346 base::Bind(&PresentationServiceDelegateImpl::OnRouteResponse
,
347 presentation_service_delegate_
));
351 router_
->CreateRoute(source
.id(), sink_id
, origin
,
352 SessionTabHelper::IdForTab(initiator_
),
353 route_response_callbacks
);
357 std::string
MediaRouterUI::GetFrameURLHost() const {
358 return GetHostFromURL(frame_url_
);
361 const std::string
& MediaRouterUI::GetRouteProviderExtensionId() const {
362 return router_
->media_route_provider_extension_id();
365 } // namespace media_router