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 "content/browser/presentation/presentation_service_impl.h"
9 #include "base/logging.h"
10 #include "content/browser/presentation/presentation_type_converters.h"
11 #include "content/public/browser/content_browser_client.h"
12 #include "content/public/browser/navigation_details.h"
13 #include "content/public/browser/render_frame_host.h"
14 #include "content/public/browser/render_process_host.h"
15 #include "content/public/browser/web_contents.h"
16 #include "content/public/common/content_client.h"
17 #include "content/public/common/frame_navigate_params.h"
21 PresentationServiceImpl::PresentationServiceImpl(
22 RenderFrameHost
* render_frame_host
,
23 WebContents
* web_contents
,
24 PresentationServiceDelegate
* delegate
)
25 : WebContentsObserver(web_contents
),
26 render_frame_host_(render_frame_host
),
28 next_request_session_id_(0),
30 DCHECK(render_frame_host_
);
32 DVLOG(2) << "PresentationServiceImpl: "
33 << render_frame_host_
->GetProcess()->GetID() << ", "
34 << render_frame_host_
->GetRoutingID();
36 delegate_
->AddObserver(this);
39 PresentationServiceImpl::~PresentationServiceImpl() {
41 delegate_
->RemoveObserver(this);
45 void PresentationServiceImpl::CreateMojoService(
46 RenderFrameHost
* render_frame_host
,
47 mojo::InterfaceRequest
<presentation::PresentationService
> request
) {
48 DVLOG(2) << "CreateMojoService";
49 WebContents
* web_contents
=
50 WebContents::FromRenderFrameHost(render_frame_host
);
53 // This object will be deleted when the RenderFrameHost is about to be
54 // deleted (RenderFrameDeleted) or if a connection error occurred
55 // (OnConnectionError).
56 PresentationServiceImpl
* impl
= new PresentationServiceImpl(
59 GetContentClient()->browser()->GetPresentationServiceDelegate(
61 impl
->Bind(request
.Pass());
64 void PresentationServiceImpl::Bind(
65 mojo::InterfaceRequest
<presentation::PresentationService
> request
) {
66 binding_
.reset(new mojo::Binding
<presentation::PresentationService
>(
67 this, request
.Pass()));
68 binding_
->set_error_handler(this);
71 void PresentationServiceImpl::OnConnectionError() {
72 DVLOG(1) << "OnConnectionError";
76 PresentationServiceImpl::ScreenAvailabilityContext
*
77 PresentationServiceImpl::GetOrCreateAvailabilityContext(
78 const std::string
& presentation_url
) {
79 auto it
= availability_contexts_
.find(presentation_url
);
80 if (it
== availability_contexts_
.end()) {
81 linked_ptr
<ScreenAvailabilityContext
> context(
82 new ScreenAvailabilityContext(presentation_url
));
83 if (!delegate_
->AddScreenAvailabilityListener(
84 render_frame_host_
->GetProcess()->GetID(),
85 render_frame_host_
->GetRoutingID(),
87 DVLOG(1) << "AddScreenAvailabilityListener failed. Ignoring request.";
90 it
= availability_contexts_
.insert(
91 std::make_pair(context
->GetPresentationUrl(), context
)).first
;
93 return it
->second
.get();
96 void PresentationServiceImpl::ListenForScreenAvailability(
97 const mojo::String
& presentation_url
,
98 const ScreenAvailabilityMojoCallback
& callback
) {
99 DVLOG(2) << "ListenForScreenAvailability";
101 callback
.Run(presentation_url
, false);
105 ScreenAvailabilityContext
* context
=
106 GetOrCreateAvailabilityContext(presentation_url
.get());
108 callback
.Run(presentation_url
, false);
111 context
->CallbackReceived(callback
);
114 void PresentationServiceImpl::RemoveScreenAvailabilityListener(
115 const mojo::String
& presentation_url
) {
116 DVLOG(2) << "RemoveScreenAvailabilityListener";
120 const std::string
& presentation_url_str
= presentation_url
.get();
121 auto it
= availability_contexts_
.find(presentation_url_str
);
122 if (it
== availability_contexts_
.end())
125 delegate_
->RemoveScreenAvailabilityListener(
126 render_frame_host_
->GetProcess()->GetID(),
127 render_frame_host_
->GetRoutingID(),
129 // Resolve the context's pending callbacks before removing it.
130 it
->second
->OnScreenAvailabilityChanged(false);
131 availability_contexts_
.erase(it
);
134 void PresentationServiceImpl::ListenForDefaultSessionStart(
135 const DefaultSessionMojoCallback
& callback
) {
139 void PresentationServiceImpl::StartSession(
140 const mojo::String
& presentation_url
,
141 const mojo::String
& presentation_id
,
142 const NewSessionMojoCallback
& callback
) {
143 DVLOG(2) << "StartSession";
145 InvokeNewSessionMojoCallbackWithError(callback
);
149 queued_start_session_requests_
.push_back(make_linked_ptr(
150 new StartSessionRequest(presentation_url
, presentation_id
, callback
)));
151 if (queued_start_session_requests_
.size() == 1)
152 DoStartSession(presentation_url
, presentation_id
, callback
);
155 void PresentationServiceImpl::JoinSession(
156 const mojo::String
& presentation_url
,
157 const mojo::String
& presentation_id
,
158 const NewSessionMojoCallback
& callback
) {
159 DVLOG(2) << "JoinSession";
161 InvokeNewSessionMojoCallbackWithError(callback
);
165 int request_session_id
= RegisterNewSessionCallback(callback
);
166 delegate_
->JoinSession(
167 render_frame_host_
->GetProcess()->GetID(),
168 render_frame_host_
->GetRoutingID(),
171 base::Bind(&PresentationServiceImpl::OnStartOrJoinSessionSucceeded
,
172 weak_factory_
.GetWeakPtr(), false, request_session_id
),
173 base::Bind(&PresentationServiceImpl::OnStartOrJoinSessionError
,
174 weak_factory_
.GetWeakPtr(), false, request_session_id
));
177 void PresentationServiceImpl::HandleQueuedStartSessionRequests() {
178 DCHECK(!queued_start_session_requests_
.empty());
179 queued_start_session_requests_
.pop_front();
180 if (!queued_start_session_requests_
.empty()) {
181 const linked_ptr
<StartSessionRequest
>& request
=
182 queued_start_session_requests_
.front();
183 DoStartSession(request
->presentation_url
,
184 request
->presentation_id
,
189 int PresentationServiceImpl::RegisterNewSessionCallback(
190 const NewSessionMojoCallback
& callback
) {
191 ++next_request_session_id_
;
192 pending_session_cbs_
[next_request_session_id_
].reset(
193 new NewSessionMojoCallback(callback
));
194 return next_request_session_id_
;
197 void PresentationServiceImpl::DoStartSession(
198 const std::string
& presentation_url
,
199 const std::string
& presentation_id
,
200 const NewSessionMojoCallback
& callback
) {
201 int request_session_id
= RegisterNewSessionCallback(callback
);
202 delegate_
->StartSession(
203 render_frame_host_
->GetProcess()->GetID(),
204 render_frame_host_
->GetRoutingID(),
207 base::Bind(&PresentationServiceImpl::OnStartOrJoinSessionSucceeded
,
208 weak_factory_
.GetWeakPtr(), true, request_session_id
),
209 base::Bind(&PresentationServiceImpl::OnStartOrJoinSessionError
,
210 weak_factory_
.GetWeakPtr(), true, request_session_id
));
213 void PresentationServiceImpl::OnStartOrJoinSessionSucceeded(
214 bool is_start_session
,
215 int request_session_id
,
216 const PresentationSessionInfo
& session_info
) {
217 RunAndEraseNewSessionMojoCallback(
219 presentation::PresentationSessionInfo::From(session_info
),
220 presentation::PresentationErrorPtr());
221 if (is_start_session
)
222 HandleQueuedStartSessionRequests();
225 void PresentationServiceImpl::OnStartOrJoinSessionError(
226 bool is_start_session
,
227 int request_session_id
,
228 const PresentationError
& error
) {
229 RunAndEraseNewSessionMojoCallback(
231 presentation::PresentationSessionInfoPtr(),
232 presentation::PresentationError::From(error
));
233 if (is_start_session
)
234 HandleQueuedStartSessionRequests();
237 void PresentationServiceImpl::RunAndEraseNewSessionMojoCallback(
238 int request_session_id
,
239 presentation::PresentationSessionInfoPtr session
,
240 presentation::PresentationErrorPtr error
) {
241 auto it
= pending_session_cbs_
.find(request_session_id
);
242 if (it
== pending_session_cbs_
.end())
245 DCHECK(it
->second
.get());
246 it
->second
->Run(session
.Pass(), error
.Pass());
247 pending_session_cbs_
.erase(it
);
250 void PresentationServiceImpl::DoSetDefaultPresentationUrl(
251 const std::string
& default_presentation_url
,
252 const std::string
& default_presentation_id
) {
254 delegate_
->SetDefaultPresentationUrl(
255 render_frame_host_
->GetProcess()->GetID(),
256 render_frame_host_
->GetRoutingID(),
257 default_presentation_url
,
258 default_presentation_id
);
259 default_presentation_url_
= default_presentation_url
;
260 default_presentation_id_
= default_presentation_id
;
263 void PresentationServiceImpl::SetDefaultPresentationURL(
264 const mojo::String
& default_presentation_url
,
265 const mojo::String
& default_presentation_id
) {
266 DVLOG(2) << "SetDefaultPresentationURL";
270 const std::string
& old_default_url
= default_presentation_url_
;
271 const std::string
& new_default_url
= default_presentation_url
.get();
273 // Don't call delegate if nothing changed.
274 if (old_default_url
== new_default_url
&&
275 default_presentation_id_
== default_presentation_id
) {
279 auto old_it
= availability_contexts_
.find(old_default_url
);
280 // Haven't started listening yet.
281 if (old_it
== availability_contexts_
.end()) {
282 DoSetDefaultPresentationUrl(new_default_url
, default_presentation_id
);
286 // Have already started listening. Create a listener for the new URL and
287 // transfer the callbacks from the old listener, if any.
288 // This is done so that a listener added before default URL is changed
289 // will continue to work.
290 ScreenAvailabilityContext
* context
=
291 GetOrCreateAvailabilityContext(new_default_url
);
292 old_it
->second
->PassPendingCallbacks(context
);
294 // Remove listener for old default presentation URL.
295 delegate_
->RemoveScreenAvailabilityListener(
296 render_frame_host_
->GetProcess()->GetID(),
297 render_frame_host_
->GetRoutingID(),
298 old_it
->second
.get());
299 availability_contexts_
.erase(old_it
);
300 DoSetDefaultPresentationUrl(new_default_url
, default_presentation_id
);
303 void PresentationServiceImpl::CloseSession(
304 const mojo::String
& presentation_url
,
305 const mojo::String
& presentation_id
) {
309 void PresentationServiceImpl::ListenForSessionStateChange(
310 const SessionStateCallback
& callback
) {
314 void PresentationServiceImpl::DidNavigateAnyFrame(
315 content::RenderFrameHost
* render_frame_host
,
316 const content::LoadCommittedDetails
& details
,
317 const content::FrameNavigateParams
& params
) {
318 DVLOG(2) << "PresentationServiceImpl::DidNavigateAnyFrame";
319 if (render_frame_host_
!= render_frame_host
)
322 std::string prev_url_host
= details
.previous_url
.host();
323 std::string curr_url_host
= params
.url
.host();
325 // If a frame navigation is in-page (e.g. navigating to a fragment in
326 // same page) then we do not unregister listeners.
327 bool in_page_navigation
= details
.is_in_page
||
328 details
.type
== content::NAVIGATION_TYPE_IN_PAGE
;
330 DVLOG(2) << "DidNavigateAnyFrame: "
331 << "prev host: " << prev_url_host
<< ", curr host: " << curr_url_host
332 << ", in_page_navigation: " << in_page_navigation
;
334 if (in_page_navigation
)
337 // Reset if the frame actually navigated.
341 void PresentationServiceImpl::RenderFrameDeleted(
342 content::RenderFrameHost
* render_frame_host
) {
343 DVLOG(2) << "PresentationServiceImpl::RenderFrameDeleted";
344 if (render_frame_host_
!= render_frame_host
)
347 // RenderFrameDeleted means |render_frame_host_| is going to be deleted soon.
348 // This object should also be deleted.
350 render_frame_host_
= nullptr;
354 void PresentationServiceImpl::Reset() {
355 DVLOG(2) << "PresentationServiceImpl::Reset";
358 render_frame_host_
->GetProcess()->GetID(),
359 render_frame_host_
->GetRoutingID());
362 default_presentation_url_
.clear();
363 default_presentation_id_
.clear();
364 for (const auto& context_entry
: availability_contexts_
) {
365 context_entry
.second
->OnScreenAvailabilityChanged(false);
367 availability_contexts_
.clear();
368 for (auto& request_ptr
: queued_start_session_requests_
) {
369 InvokeNewSessionMojoCallbackWithError(request_ptr
->callback
);
371 queued_start_session_requests_
.clear();
372 for (auto& pending_entry
: pending_session_cbs_
) {
373 InvokeNewSessionMojoCallbackWithError(*pending_entry
.second
);
375 pending_session_cbs_
.clear();
378 void PresentationServiceImpl::InvokeNewSessionMojoCallbackWithError(
379 const NewSessionMojoCallback
& callback
) {
381 presentation::PresentationSessionInfoPtr(),
382 presentation::PresentationError::From(
383 PresentationError(PRESENTATION_ERROR_UNKNOWN
, "Internal error")));
386 void PresentationServiceImpl::OnDelegateDestroyed() {
387 DVLOG(2) << "PresentationServiceImpl::OnDelegateDestroyed";
392 PresentationServiceImpl::ScreenAvailabilityContext::ScreenAvailabilityContext(
393 const std::string
& presentation_url
)
394 : presentation_url_(presentation_url
) {
397 PresentationServiceImpl::ScreenAvailabilityContext::
398 ~ScreenAvailabilityContext() {
401 void PresentationServiceImpl::ScreenAvailabilityContext::CallbackReceived(
402 const ScreenAvailabilityMojoCallback
& callback
) {
403 // NOTE: This will overwrite previously registered callback if any.
404 if (!available_ptr_
) {
405 // No results yet, store callback for later invocation.
406 callbacks_
.push_back(new ScreenAvailabilityMojoCallback(callback
));
408 // Run callback now, reset result.
409 // There shouldn't be any callbacks stored in this scenario.
410 DCHECK(!HasPendingCallbacks());
411 callback
.Run(presentation_url_
, *available_ptr_
);
412 available_ptr_
.reset();
416 std::string
PresentationServiceImpl::ScreenAvailabilityContext
417 ::GetPresentationUrl() const {
418 return presentation_url_
;
421 void PresentationServiceImpl::ScreenAvailabilityContext
422 ::OnScreenAvailabilityChanged(bool available
) {
423 if (!HasPendingCallbacks()) {
424 // No callback, stash the result for now.
425 available_ptr_
.reset(new bool(available
));
427 // Invoke callbacks and erase them.
428 // There shouldn't be any result stored in this scenario.
429 DCHECK(!available_ptr_
);
430 ScopedVector
<ScreenAvailabilityMojoCallback
> callbacks
;
431 callbacks
.swap(callbacks_
);
432 for (const auto& callback
: callbacks
)
433 callback
->Run(presentation_url_
, available
);
437 void PresentationServiceImpl::ScreenAvailabilityContext
438 ::PassPendingCallbacks(
439 PresentationServiceImpl::ScreenAvailabilityContext
* other
) {
440 std::vector
<ScreenAvailabilityMojoCallback
*> callbacks
;
441 callbacks_
.release(&callbacks
);
442 std::copy(callbacks
.begin(), callbacks
.end(),
443 std::back_inserter(other
->callbacks_
));
446 bool PresentationServiceImpl::ScreenAvailabilityContext
447 ::HasPendingCallbacks() const {
448 return !callbacks_
.empty();
451 PresentationServiceImpl::StartSessionRequest::StartSessionRequest(
452 const std::string
& presentation_url
,
453 const std::string
& presentation_id
,
454 const NewSessionMojoCallback
& callback
)
455 : presentation_url(presentation_url
),
456 presentation_id(presentation_id
),
460 PresentationServiceImpl::StartSessionRequest::~StartSessionRequest() {
463 } // namespace content