Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / presentation / presentation_service_impl.cc
blob9e2fd22e9c8c59971ce8f898b92864e220704a2e
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"
7 #include <algorithm>
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"
19 namespace content {
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),
27 delegate_(delegate),
28 next_request_session_id_(0),
29 weak_factory_(this) {
30 DCHECK(render_frame_host_);
31 DCHECK(web_contents);
32 DVLOG(2) << "PresentationServiceImpl: "
33 << render_frame_host_->GetProcess()->GetID() << ", "
34 << render_frame_host_->GetRoutingID();
35 if (delegate_)
36 delegate_->AddObserver(this);
39 PresentationServiceImpl::~PresentationServiceImpl() {
40 if (delegate_)
41 delegate_->RemoveObserver(this);
44 // static
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);
51 DCHECK(web_contents);
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(
57 render_frame_host,
58 web_contents,
59 GetContentClient()->browser()->GetPresentationServiceDelegate(
60 web_contents));
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";
73 delete this;
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(),
86 context.get())) {
87 DVLOG(1) << "AddScreenAvailabilityListener failed. Ignoring request.";
88 return nullptr;
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";
100 if (!delegate_) {
101 callback.Run(presentation_url, false);
102 return;
105 ScreenAvailabilityContext* context =
106 GetOrCreateAvailabilityContext(presentation_url.get());
107 if (!context) {
108 callback.Run(presentation_url, false);
109 return;
111 context->CallbackReceived(callback);
114 void PresentationServiceImpl::RemoveScreenAvailabilityListener(
115 const mojo::String& presentation_url) {
116 DVLOG(2) << "RemoveScreenAvailabilityListener";
117 if (!delegate_)
118 return;
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())
123 return;
125 delegate_->RemoveScreenAvailabilityListener(
126 render_frame_host_->GetProcess()->GetID(),
127 render_frame_host_->GetRoutingID(),
128 it->second.get());
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) {
136 NOTIMPLEMENTED();
139 void PresentationServiceImpl::StartSession(
140 const mojo::String& presentation_url,
141 const mojo::String& presentation_id,
142 const NewSessionMojoCallback& callback) {
143 DVLOG(2) << "StartSession";
144 if (!delegate_) {
145 InvokeNewSessionMojoCallbackWithError(callback);
146 return;
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";
160 if (!delegate_) {
161 InvokeNewSessionMojoCallbackWithError(callback);
162 return;
165 int request_session_id = RegisterNewSessionCallback(callback);
166 delegate_->JoinSession(
167 render_frame_host_->GetProcess()->GetID(),
168 render_frame_host_->GetRoutingID(),
169 presentation_url,
170 presentation_id,
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,
185 request->callback);
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(),
205 presentation_url,
206 presentation_id,
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(
218 request_session_id,
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(
230 request_session_id,
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())
243 return;
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) {
253 DCHECK(delegate_);
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";
267 if (!delegate_)
268 return;
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) {
276 return;
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);
283 return;
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) {
306 NOTIMPLEMENTED();
309 void PresentationServiceImpl::ListenForSessionStateChange(
310 const SessionStateCallback& callback) {
311 NOTIMPLEMENTED();
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)
320 return;
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)
335 return;
337 // Reset if the frame actually navigated.
338 Reset();
341 void PresentationServiceImpl::RenderFrameDeleted(
342 content::RenderFrameHost* render_frame_host) {
343 DVLOG(2) << "PresentationServiceImpl::RenderFrameDeleted";
344 if (render_frame_host_ != render_frame_host)
345 return;
347 // RenderFrameDeleted means |render_frame_host_| is going to be deleted soon.
348 // This object should also be deleted.
349 Reset();
350 render_frame_host_ = nullptr;
351 delete this;
354 void PresentationServiceImpl::Reset() {
355 DVLOG(2) << "PresentationServiceImpl::Reset";
356 if (delegate_) {
357 delegate_->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) {
380 callback.Run(
381 presentation::PresentationSessionInfoPtr(),
382 presentation::PresentationError::From(
383 PresentationError(PRESENTATION_ERROR_UNKNOWN, "Internal error")));
386 void PresentationServiceImpl::OnDelegateDestroyed() {
387 DVLOG(2) << "PresentationServiceImpl::OnDelegateDestroyed";
388 delegate_ = nullptr;
389 Reset();
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));
407 } else {
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));
426 } else {
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),
457 callback(callback) {
460 PresentationServiceImpl::StartSessionRequest::~StartSessionRequest() {
463 } // namespace content