Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / ui / webui / media_router / media_router_ui.cc
blob496683299f0c5832dcf479e680fae47f742ba0bf
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 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),
96 initiator_(nullptr),
97 router_(nullptr),
98 weak_factory_(this) {
99 // Create a WebUIDataSource containing the chrome://media-router page's
100 // content.
101 scoped_ptr<content::WebUIDataSource> html_source(
102 content::WebUIDataSource::Create(chrome::kChromeUIMediaRouterHost));
104 content::WebContents* wc = web_ui->GetWebContents();
105 DCHECK(wc);
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
112 // is fixed.
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,
135 "Dialog closed."));
139 void MediaRouterUI::InitWithDefaultMediaSource(
140 const base::WeakPtr<PresentationServiceDelegateImpl>& delegate) {
141 DCHECK(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) {
156 DCHECK(initiator);
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) {
170 DCHECK(initiator);
171 DCHECK(router_);
173 // Register for Issue and MediaRoute updates.
174 issues_observer_.reset(new UIIssuesObserver(router_, this));
175 routes_observer_.reset(new UIMediaRoutesObserver(
176 router_,
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);
198 } else {
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_);
208 if (ui_initialized_)
209 handler_->UpdateCastModes(cast_modes_, GetHostFromURL(frame_url_));
212 void MediaRouterUI::Close() {
213 ConstrainedWebDialogDelegate* delegate = GetConstrainedDelegate();
214 if (delegate) {
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) {
261 sinks_ = sinks;
262 if (ui_initialized_)
263 handler_->UpdateSinks(sinks_);
266 void MediaRouterUI::SetIssue(const Issue* issue) {
267 if (ui_initialized_)
268 handler_->UpdateIssue(issue);
271 void MediaRouterUI::OnRoutesUpdated(const std::vector<MediaRoute>& routes) {
272 routes_ = routes;
273 if (ui_initialized_)
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)
283 if (!route)
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());
294 DCHECK(initiator_);
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;
305 return false;
308 has_pending_route_request_ = true;
309 requesting_route_for_default_source_ = cast_mode == MediaCastMode::DEFAULT;
310 GURL origin;
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();
314 } else {
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
327 // answered with the
328 // route response.
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);
354 return true;
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