OLD | NEW |
---|---|
(Empty) | |
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. | |
4 | |
5 #include "chrome/browser/media/router/presentation_service_delegate_impl.h" | |
6 | |
7 #include <string> | |
8 | |
9 #include "base/containers/scoped_ptr_hash_map.h" | |
10 #include "base/guid.h" | |
11 #include "base/strings/string_util.h" | |
12 #include "base/strings/stringprintf.h" | |
13 #include "chrome/browser/media/router/create_session_request.h" | |
14 #include "chrome/browser/media/router/media_route.h" | |
15 #include "chrome/browser/media/router/media_router.h" | |
16 #include "chrome/browser/media/router/media_sink.h" | |
17 #include "chrome/browser/media/router/media_source_helper.h" | |
18 #include "chrome/browser/media/router/presentation_helper.h" | |
19 #include "chrome/browser/media/router/presentation_media_sinks_observer.h" | |
20 #include "chrome/browser/sessions/session_tab_helper.h" | |
21 #include "content/public/browser/presentation_screen_availability_listener.h" | |
22 #include "content/public/browser/presentation_session.h" | |
23 #include "content/public/browser/render_frame_host.h" | |
24 #include "content/public/browser/render_process_host.h" | |
25 | |
26 DEFINE_WEB_CONTENTS_USER_DATA_KEY( | |
27 media_router::PresentationServiceDelegateImpl); | |
28 | |
29 using content::RenderFrameHost; | |
30 | |
31 namespace media_router { | |
32 | |
33 namespace { | |
34 | |
35 // Helper function to extract ID for a RFH. | |
36 RenderFrameHostId GetRenderFrameHostId(RenderFrameHost* rfh) { | |
37 int render_process_id = rfh->GetProcess()->GetID(); | |
38 int render_frame_id = rfh->GetRoutingID(); | |
39 return RenderFrameHostId(render_process_id, render_frame_id); | |
40 } | |
41 | |
42 // Gets the host name associated with the frame given by |rfh_id|. | |
43 std::string GetSourceHostForFrame(RenderFrameHostId rfh_id) { | |
44 RenderFrameHost* rfh = RenderFrameHost::FromID(rfh_id.first, rfh_id.second); | |
45 DCHECK(rfh); | |
46 std::string host = rfh->GetLastCommittedURL().host(); | |
47 if (StartsWithASCII(host, "www.", false)) | |
48 host = host.substr(4); | |
49 return host; | |
50 } | |
51 | |
52 } // namespace | |
53 | |
54 // Helper class for PresentationServiceDelegateImpl to manage | |
55 // listeners and default presentation info in a render frame. | |
56 class PresentationFrame final { | |
57 public: | |
58 using DelegateObserver = content::PresentationServiceDelegate::Observer; | |
59 | |
60 explicit PresentationFrame(content::WebContents* web_contents, | |
imcheng (use chromium acct)
2015/05/19 20:03:29
rm explicit
haibinlu
2015/05/19 21:14:35
Done.
| |
61 MediaRouter* router); | |
62 ~PresentationFrame(); | |
63 | |
64 // Returns true if listener was added. | |
65 bool AddScreenAvailabilityListener( | |
66 content::PresentationScreenAvailabilityListener* listener); | |
67 | |
68 // Returns true if listener was removed. | |
69 bool RemoveScreenAvailabilityListener( | |
70 content::PresentationScreenAvailabilityListener* listener); | |
71 | |
72 // Returns true if there is a listener for |source_id|. | |
73 bool HasScreenAvailabilityListenerForTest( | |
74 const MediaSourceId& source_id) const; | |
75 | |
76 // Returns the number of screen-availability listeners. | |
77 int NumScreenAvailabilityListeners() const; | |
78 | |
79 // Sets the default presentation URL and ID. | |
80 void SetDefaultPresentationInfo(const std::string& default_presentation_url, | |
81 const std::string& default_presentation_id); | |
82 | |
83 // Returns empty string if no default presentation ID is set. | |
84 std::string GetDefaultPresentationId() const; | |
85 void set_delegate_observer(DelegateObserver* observer) { | |
86 delegate_observer_ = observer; | |
87 } | |
88 DelegateObserver* delegate_observer() const { return delegate_observer_; } | |
89 | |
90 void Reset(); | |
91 | |
92 private: | |
93 MediaSource GetMediaSourceFromListener( | |
94 content::PresentationScreenAvailabilityListener* listener); | |
95 | |
96 scoped_ptr<content::PresentationSessionInfo> default_presentation_info_; | |
97 base::ScopedPtrHashMap<MediaSourceId, scoped_ptr<MediaSinksObserver>> | |
98 sink_observers_; | |
99 | |
100 // Does not own these objects. | |
101 const content::WebContents* web_contents_; | |
102 MediaRouter* router_; | |
103 DelegateObserver* delegate_observer_; | |
104 }; | |
105 | |
106 PresentationFrame::PresentationFrame(content::WebContents* web_contents, | |
107 MediaRouter* router) | |
108 : web_contents_(web_contents), | |
109 router_(router), | |
110 delegate_observer_(nullptr) { | |
111 DCHECK(web_contents_); | |
112 DCHECK(router_); | |
113 } | |
114 | |
115 PresentationFrame::~PresentationFrame() { | |
116 DCHECK(sink_observers_.empty()); | |
117 Reset(); | |
imcheng (use chromium acct)
2015/05/19 20:03:29
Why does Reset() need to be called? The dtor will
haibinlu
2015/05/19 21:14:35
removed
| |
118 if (delegate_observer_) { | |
119 delegate_observer_->OnDelegateDestroyed(); | |
imcheng (use chromium acct)
2015/05/19 20:03:29
OnDelegateDestroyed() should be called in Presesen
haibinlu
2015/05/19 21:14:35
Done.
| |
120 } | |
121 } | |
122 | |
123 bool PresentationFrame::AddScreenAvailabilityListener( | |
124 content::PresentationScreenAvailabilityListener* listener) { | |
125 MediaSource source(GetMediaSourceFromListener(listener)); | |
126 if (sink_observers_.contains(source.id())) | |
127 return false; | |
128 | |
129 return sink_observers_.add(source.id(), | |
130 make_scoped_ptr(new PresentationMediaSinksObserver( | |
131 router_, listener, source))).second; | |
132 } | |
133 | |
134 bool PresentationFrame::RemoveScreenAvailabilityListener( | |
135 content::PresentationScreenAvailabilityListener* listener) { | |
136 MediaSource source(GetMediaSourceFromListener(listener)); | |
137 return sink_observers_.erase(source.id()) == 1; | |
138 } | |
139 | |
140 bool PresentationFrame::HasScreenAvailabilityListenerForTest( | |
141 const MediaSourceId& source_id) const { | |
142 return sink_observers_.contains(source_id); | |
143 } | |
144 | |
145 int PresentationFrame::NumScreenAvailabilityListeners() const { | |
146 return sink_observers_.size(); | |
147 } | |
148 | |
149 void PresentationFrame::Reset() { | |
150 sink_observers_.clear(); | |
151 default_presentation_info_.reset(); | |
152 } | |
153 | |
154 void PresentationFrame::SetDefaultPresentationInfo( | |
155 const std::string& default_presentation_url, | |
156 const std::string& default_presentation_id) { | |
157 if (default_presentation_url.empty() && default_presentation_id.empty()) { | |
158 default_presentation_info_.reset(); | |
159 } else { | |
160 default_presentation_info_.reset(new content::PresentationSessionInfo( | |
161 default_presentation_url, default_presentation_id)); | |
162 } | |
163 } | |
164 | |
165 std::string PresentationFrame::GetDefaultPresentationId() const { | |
166 return default_presentation_info_ | |
167 ? default_presentation_info_->presentation_id | |
168 : ""; | |
169 } | |
170 | |
171 MediaSource PresentationFrame::GetMediaSourceFromListener( | |
172 content::PresentationScreenAvailabilityListener* listener) { | |
173 // If the default presentation URL is empty then fall back to 1-UA mode, | |
174 // i.e. offscreeen tab rendering. | |
175 std::string presentation_url(listener->GetPresentationUrl()); | |
176 return presentation_url.empty() | |
177 ? ForTabMediaSource(SessionTabHelper::IdForTab(web_contents_)) | |
178 : ForPresentationUrl(presentation_url); | |
179 } | |
180 | |
181 // Helper class for PresentationServiceDelegateImpl to manage | |
182 // PresentationFrames. | |
183 class PresentationFrameMap final { | |
184 public: | |
185 using RenderFrameHostId = std::pair<int, int>; | |
imcheng (use chromium acct)
2015/05/19 20:03:29
I don't think you need this. You can just reuse th
haibinlu
2015/05/19 21:14:35
Done.
| |
186 using DelegateObserver = content::PresentationServiceDelegate::Observer; | |
187 | |
188 explicit PresentationFrameMap(content::WebContents* web_contents, | |
imcheng (use chromium acct)
2015/05/19 20:03:29
rm explicit
haibinlu
2015/05/19 21:14:35
Done.
| |
189 MediaRouter* router); | |
190 ~PresentationFrameMap(); | |
191 | |
192 // Returns true if listener was added. | |
193 bool AddScreenAvailabilityListener( | |
194 int render_process_id, | |
imcheng (use chromium acct)
2015/05/19 20:03:29
Use RenderFrameHostId here and below?
haibinlu
2015/05/19 21:14:35
Done.
| |
195 int render_frame_id, | |
196 content::PresentationScreenAvailabilityListener* listener); | |
197 | |
198 // Returns true if listener was removed. | |
199 bool RemoveScreenAvailabilityListener( | |
200 int render_process_id, | |
201 int render_frame_id, | |
202 content::PresentationScreenAvailabilityListener* listener); | |
203 | |
204 // Returns true if there is a listener for |source_id| in the frame. | |
205 bool HasScreenAvailabilityListenerForTest( | |
206 RenderFrameHostId rfh_id, | |
207 const MediaSourceId& source_id) const; | |
208 | |
209 // Returns the number of screen-availability listeners. | |
210 int NumScreenAvailabilityListeners() const; | |
211 | |
212 // Returns the default presentation URL and ID for the frame. | |
213 void SetDefaultPresentationInfo(int render_process_id, | |
214 int render_frame_id, | |
215 const std::string& default_presentation_url, | |
216 const std::string& default_presentation_id); | |
217 | |
218 // Returns empty string if no default presentation ID is set in the frame. | |
219 std::string GetDefaultPresentationId(RenderFrameHostId rfh_id) const; | |
220 | |
221 void RegisterPresenationFrame(int render_process_id, | |
222 int render_frame_id, | |
223 DelegateObserver* observer); | |
224 void UnregisterPresenationFrame(int render_process_id, int render_frame_id); | |
225 | |
226 DelegateObserver* GetDelegateObserver(RenderFrameHostId rfh_id) const; | |
227 | |
228 void Reset(RenderFrameHostId rfh_id); | |
229 | |
230 void SetMediaRouterForTest(MediaRouter* router); | |
231 | |
232 private: | |
233 PresentationFrame* GetOrAddFrame(RenderFrameHostId rfh_id); | |
234 | |
235 // Maps a frame identifier to a PresentationFrame object for frames | |
236 // that are using presentation API. | |
237 base::ScopedPtrHashMap<RenderFrameHostId, scoped_ptr<PresentationFrame>> | |
238 presentation_frames_; | |
239 | |
240 // Does not own these two objects. | |
241 MediaRouter* router_; | |
242 content::WebContents* web_contents_; | |
243 }; | |
244 | |
245 PresentationFrameMap::PresentationFrameMap(content::WebContents* web_contents, | |
246 MediaRouter* router) | |
247 : web_contents_(web_contents), router_(router) { | |
248 DCHECK(web_contents_); | |
249 } | |
250 | |
251 PresentationFrameMap::~PresentationFrameMap() { | |
252 } | |
253 | |
254 bool PresentationFrameMap::AddScreenAvailabilityListener( | |
255 int render_process_id, | |
256 int render_frame_id, | |
257 content::PresentationScreenAvailabilityListener* listener) { | |
258 if (NumScreenAvailabilityListeners() >= | |
259 PresentationServiceDelegateImpl::kMaxNumSources) { | |
260 return false; | |
261 } | |
262 | |
263 RenderFrameHostId rfh_id(render_process_id, render_frame_id); | |
264 auto presentation_frame = GetOrAddFrame(rfh_id); | |
265 return presentation_frame->AddScreenAvailabilityListener(listener); | |
266 } | |
267 | |
268 bool PresentationFrameMap::RemoveScreenAvailabilityListener( | |
269 int render_process_id, | |
270 int render_frame_id, | |
271 content::PresentationScreenAvailabilityListener* listener) { | |
272 DCHECK(listener); | |
273 | |
274 RenderFrameHostId rfh_id(render_process_id, render_frame_id); | |
275 auto presentation_frame = presentation_frames_.get(rfh_id); | |
276 return presentation_frame && | |
277 presentation_frame->RemoveScreenAvailabilityListener(listener); | |
278 } | |
279 | |
280 bool PresentationFrameMap::HasScreenAvailabilityListenerForTest( | |
281 RenderFrameHostId rfh_id, | |
282 const MediaSourceId& source_id) const { | |
283 auto presentation_frame = presentation_frames_.get(rfh_id); | |
284 return presentation_frame && | |
285 presentation_frame->HasScreenAvailabilityListenerForTest(source_id); | |
286 } | |
287 | |
288 int PresentationFrameMap::NumScreenAvailabilityListeners() const { | |
289 int count = 0; | |
290 for (const auto& pf : presentation_frames_) { | |
291 count += pf.second->NumScreenAvailabilityListeners(); | |
292 } | |
293 return count; | |
294 } | |
295 | |
296 void PresentationFrameMap::SetDefaultPresentationInfo( | |
297 int render_process_id, | |
298 int render_frame_id, | |
299 const std::string& default_presentation_url, | |
300 const std::string& default_presentation_id) { | |
301 RenderFrameHostId rfh_id(render_process_id, render_frame_id); | |
302 auto presentation_frame = GetOrAddFrame(rfh_id); | |
303 presentation_frame->SetDefaultPresentationInfo(default_presentation_url, | |
304 default_presentation_id); | |
305 } | |
306 | |
307 std::string PresentationFrameMap::GetDefaultPresentationId( | |
308 RenderFrameHostId rfh_id) const { | |
309 auto presentation_frame = presentation_frames_.get(rfh_id); | |
310 return presentation_frame ? presentation_frame->GetDefaultPresentationId() | |
311 : ""; | |
312 } | |
313 | |
314 void PresentationFrameMap::RegisterPresenationFrame( | |
315 int render_process_id, | |
316 int render_frame_id, | |
317 content::PresentationServiceDelegate::Observer* observer) { | |
318 RenderFrameHostId rfh_id(render_process_id, render_frame_id); | |
319 auto presentation_frame = GetOrAddFrame(rfh_id); | |
320 presentation_frame->set_delegate_observer(observer); | |
321 } | |
322 | |
323 void PresentationFrameMap::UnregisterPresenationFrame(int render_process_id, | |
324 int render_frame_id) { | |
325 RenderFrameHostId rfh_id(render_process_id, render_frame_id); | |
326 auto presentation_frame = presentation_frames_.get(rfh_id); | |
327 if (presentation_frame) { | |
328 presentation_frame->set_delegate_observer(nullptr); | |
imcheng (use chromium acct)
2015/05/19 20:03:29
not needed if you move the OnDelegateDestroyed() t
haibinlu
2015/05/19 21:14:35
Done.
| |
329 presentation_frames_.erase(rfh_id); | |
330 } | |
331 } | |
332 | |
333 PresentationFrameMap::DelegateObserver* | |
334 PresentationFrameMap::GetDelegateObserver(RenderFrameHostId rfh_id) const { | |
335 auto presentation_frame = presentation_frames_.get(rfh_id); | |
336 return presentation_frame ? presentation_frame->delegate_observer() : nullptr; | |
337 } | |
338 | |
339 void PresentationFrameMap::Reset(RenderFrameHostId rfh_id) { | |
340 auto presentation_frame = presentation_frames_.get(rfh_id); | |
341 if (presentation_frame) | |
342 presentation_frame->Reset(); | |
343 } | |
344 | |
345 PresentationFrame* PresentationFrameMap::GetOrAddFrame( | |
346 RenderFrameHostId rfh_id) { | |
347 if (!presentation_frames_.contains(rfh_id)) { | |
348 presentation_frames_.add( | |
349 rfh_id, scoped_ptr<PresentationFrame>( | |
350 new PresentationFrame(web_contents_, router_))); | |
351 } | |
352 return presentation_frames_.get(rfh_id); | |
353 } | |
354 | |
355 void PresentationFrameMap::SetMediaRouterForTest(MediaRouter* router) { | |
356 router_ = router; | |
357 } | |
358 | |
359 // TODO(haibinlu): Get router from MediaRouterMojoImplFactory once it lands. | |
360 PresentationServiceDelegateImpl::PresentationServiceDelegateImpl( | |
361 content::WebContents* web_contents) | |
362 : web_contents_(web_contents), | |
363 router_(NULL), | |
364 frame_map_(new PresentationFrameMap(web_contents, nullptr)), | |
imcheng (use chromium acct)
2015/05/19 20:03:29
you might as well pass in router_ here if you are
haibinlu
2015/05/19 21:14:35
will update this once MediaRouterMojoImplFactory l
| |
365 weak_factory_(this) { | |
366 // DCHECK(router_); | |
367 } | |
368 | |
369 PresentationServiceDelegateImpl::~PresentationServiceDelegateImpl() { | |
370 } | |
371 | |
372 void PresentationServiceDelegateImpl::AddObserver( | |
373 int render_process_id, | |
374 int render_frame_id, | |
375 content::PresentationServiceDelegate::Observer* observer) { | |
376 DCHECK(observer); | |
377 frame_map_->RegisterPresenationFrame(render_process_id, render_frame_id, | |
378 observer); | |
379 } | |
380 | |
381 void PresentationServiceDelegateImpl::RemoveObserver(int render_process_id, | |
382 int render_frame_id) { | |
383 frame_map_->UnregisterPresenationFrame(render_process_id, render_frame_id); | |
384 } | |
385 | |
386 bool PresentationServiceDelegateImpl::AddScreenAvailabilityListener( | |
387 int render_process_id, | |
388 int render_frame_id, | |
389 content::PresentationScreenAvailabilityListener* listener) { | |
390 DCHECK(listener); | |
391 return frame_map_->AddScreenAvailabilityListener(render_process_id, | |
392 render_frame_id, listener); | |
393 } | |
394 | |
395 void PresentationServiceDelegateImpl::RemoveScreenAvailabilityListener( | |
396 int render_process_id, | |
397 int render_frame_id, | |
398 content::PresentationScreenAvailabilityListener* listener) { | |
399 DCHECK(listener); | |
400 frame_map_->RemoveScreenAvailabilityListener(render_process_id, | |
401 render_frame_id, listener); | |
402 } | |
403 | |
404 void PresentationServiceDelegateImpl::Reset(int render_process_id, | |
405 int render_frame_id) { | |
406 RenderFrameHostId rfh_id(render_process_id, render_frame_id); | |
407 frame_map_->Reset(rfh_id); | |
408 if (IsMainFrame(rfh_id)) | |
409 UpdateDefaultMediaSourceAndNotifyObservers(MediaSource(), std::string()); | |
410 } | |
411 | |
412 void PresentationServiceDelegateImpl::SetDefaultPresentationUrl( | |
413 int render_process_id, | |
414 int render_frame_id, | |
415 const std::string& default_presentation_url, | |
416 const std::string& default_presentation_id) { | |
417 frame_map_->SetDefaultPresentationInfo(render_process_id, render_frame_id, | |
418 default_presentation_url, | |
419 default_presentation_id); | |
420 | |
421 RenderFrameHostId rfh_id(render_process_id, render_frame_id); | |
422 if (IsMainFrame(rfh_id)) { | |
423 // This is the main frame, that means tab-level default presentation | |
424 // might have been updated. | |
425 MediaSource new_default_source; | |
426 if (!default_presentation_url.empty()) | |
427 new_default_source = ForPresentationUrl(default_presentation_url); | |
428 std::string new_default_source_host(GetSourceHostForFrame(rfh_id)); | |
429 UpdateDefaultMediaSourceAndNotifyObservers(new_default_source, | |
430 new_default_source_host); | |
431 } | |
432 } | |
433 | |
434 bool PresentationServiceDelegateImpl::IsMainFrame( | |
435 RenderFrameHostId rfh_id) const { | |
436 RenderFrameHost* main_frame = web_contents_->GetMainFrame(); | |
437 return main_frame && GetRenderFrameHostId(main_frame) == rfh_id; | |
438 } | |
439 | |
440 void PresentationServiceDelegateImpl:: | |
441 UpdateDefaultMediaSourceAndNotifyObservers( | |
442 const MediaSource& new_default_source, | |
443 const std::string& new_default_source_host) { | |
444 if (!new_default_source.Equals(default_source_) || | |
445 new_default_source_host != default_source_host_) { | |
446 default_source_ = new_default_source; | |
447 default_source_host_ = new_default_source_host; | |
448 FOR_EACH_OBSERVER( | |
449 DefaultMediaSourceObserver, default_media_source_observers_, | |
450 OnDefaultMediaSourceChanged(default_source_, default_source_host_)); | |
451 } | |
452 } | |
453 | |
454 void PresentationServiceDelegateImpl::StartSession( | |
455 int render_process_id, | |
456 int render_frame_id, | |
457 const std::string& presentation_url, | |
458 const std::string& presentation_id, | |
459 const PresentationSessionSuccessCallback& success_cb, | |
460 const PresentationSessionErrorCallback& error_cb) { | |
461 NOTIMPLEMENTED(); | |
462 } | |
463 | |
464 void PresentationServiceDelegateImpl::JoinSession( | |
465 int render_process_id, | |
466 int render_frame_id, | |
467 const std::string& presentation_url, | |
468 const std::string& presentation_id, | |
469 const PresentationSessionSuccessCallback& success_cb, | |
470 const PresentationSessionErrorCallback& error_cb) { | |
471 NOTIMPLEMENTED(); | |
472 } | |
473 | |
474 void PresentationServiceDelegateImpl::ListenForSessionMessages( | |
475 int render_process_id, | |
476 int render_frame_id, | |
477 const PresentationSessionMessageCallback& message_cb) { | |
478 NOTIMPLEMENTED(); | |
479 } | |
480 | |
481 void PresentationServiceDelegateImpl::SendMessage( | |
482 int render_process_id, | |
483 int render_frame_id, | |
484 scoped_ptr<content::PresentationSessionMessage> message_request, | |
485 const SendMessageCallback& send_message_cb) { | |
486 NOTIMPLEMENTED(); | |
487 } | |
488 | |
489 void PresentationServiceDelegateImpl::OnRouteCreated(const MediaRoute& route) { | |
490 const MediaSource& source = route.media_source(); | |
491 DCHECK(!source.Empty()); | |
492 if (default_source_.Equals(source)) { | |
493 RenderFrameHost* main_frame = web_contents_->GetMainFrame(); | |
494 if (main_frame) { | |
495 RenderFrameHostId rfh_id(GetRenderFrameHostId(main_frame)); | |
496 auto observer = frame_map_->GetDelegateObserver(rfh_id); | |
497 if (observer) { | |
498 // TODO(imcheng): Pass in valid default presentation ID once it is | |
499 // available from MediaRoute URN. | |
500 observer->OnDefaultPresentationStarted(content::PresentationSessionInfo( | |
501 GetPresentationUrl(source), std::string())); | |
502 } | |
503 } | |
504 } | |
505 } | |
506 | |
507 void PresentationServiceDelegateImpl::AddDefaultMediaSourceObserver( | |
508 DefaultMediaSourceObserver* observer) { | |
509 default_media_source_observers_.AddObserver(observer); | |
510 } | |
511 | |
512 void PresentationServiceDelegateImpl::RemoveDefaultMediaSourceObserver( | |
513 DefaultMediaSourceObserver* observer) { | |
514 default_media_source_observers_.RemoveObserver(observer); | |
515 } | |
516 | |
517 void PresentationServiceDelegateImpl::SetMediaRouterForTest( | |
518 MediaRouter* router) { | |
519 router_ = router; | |
520 frame_map_->SetMediaRouterForTest(router); | |
521 } | |
522 | |
523 base::WeakPtr<PresentationServiceDelegateImpl> | |
524 PresentationServiceDelegateImpl::GetWeakPtr() { | |
525 return weak_factory_.GetWeakPtr(); | |
526 } | |
527 | |
528 bool PresentationServiceDelegateImpl::HasScreenAvailabilityListenerForTest( | |
529 RenderFrameHostId rfh_id, | |
530 const MediaSourceId& source_id) const { | |
531 return frame_map_->HasScreenAvailabilityListenerForTest(rfh_id, source_id); | |
532 } | |
533 | |
534 } // namespace media_router | |
OLD | NEW |