Presentation API: implement WebPresentationClient::setDefaultPresentationUrl().
[chromium-blink-merge.git] / content / browser / presentation / presentation_service_impl.cc
bloba73cdc0a7b3ad3c5b745b746197db877e1796a76
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/presentation_session_message.h"
14 #include "content/public/browser/render_frame_host.h"
15 #include "content/public/browser/render_process_host.h"
16 #include "content/public/browser/web_contents.h"
17 #include "content/public/common/content_client.h"
18 #include "content/public/common/frame_navigate_params.h"
19 #include "content/public/common/presentation_constants.h"
21 namespace content {
23 namespace {
25 const int kInvalidRequestSessionId = -1;
27 int GetNextRequestSessionId() {
28 static int next_request_session_id = 0;
29 return ++next_request_session_id;
32 // The return value takes ownership of the contents of |input|.
33 presentation::SessionMessagePtr ToMojoSessionMessage(
34 content::PresentationSessionMessage* input) {
35 presentation::SessionMessagePtr output(presentation::SessionMessage::New());
36 output->presentation_url.Swap(&input->presentation_url);
37 output->presentation_id.Swap(&input->presentation_id);
38 if (input->is_binary()) {
39 // binary data
40 output->type = presentation::PresentationMessageType::
41 PRESENTATION_MESSAGE_TYPE_ARRAY_BUFFER;
42 output->data.Swap(input->data.get());
43 } else {
44 // string message
45 output->type =
46 presentation::PresentationMessageType::PRESENTATION_MESSAGE_TYPE_TEXT;
47 output->message.Swap(input->message.get());
49 return output.Pass();
52 scoped_ptr<content::PresentationSessionMessage> GetPresentationSessionMessage(
53 presentation::SessionMessagePtr input) {
54 DCHECK(!input.is_null());
55 scoped_ptr<content::PresentationSessionMessage> output;
56 switch (input->type) {
57 case presentation::PresentationMessageType::
58 PRESENTATION_MESSAGE_TYPE_TEXT: {
59 DCHECK(!input->message.is_null());
60 DCHECK(input->data.is_null());
61 // Return null PresentationSessionMessage if size exceeds.
62 if (input->message.size() > content::kMaxPresentationSessionMessageSize)
63 return output.Pass();
65 output = content::PresentationSessionMessage::CreateStringMessage(
66 input->presentation_url, input->presentation_id,
67 make_scoped_ptr(new std::string));
68 input->message.Swap(output->message.get());
69 return output.Pass();
71 case presentation::PresentationMessageType::
72 PRESENTATION_MESSAGE_TYPE_ARRAY_BUFFER: {
73 DCHECK(!input->data.is_null());
74 DCHECK(input->message.is_null());
75 if (input->data.size() > content::kMaxPresentationSessionMessageSize)
76 return output.Pass();
78 output = content::PresentationSessionMessage::CreateArrayBufferMessage(
79 input->presentation_url, input->presentation_id,
80 make_scoped_ptr(new std::vector<uint8_t>));
81 input->data.Swap(output->data.get());
82 return output.Pass();
84 case presentation::PresentationMessageType::
85 PRESENTATION_MESSAGE_TYPE_BLOB: {
86 DCHECK(!input->data.is_null());
87 DCHECK(input->message.is_null());
88 if (input->data.size() > content::kMaxPresentationSessionMessageSize)
89 return output.Pass();
91 output = content::PresentationSessionMessage::CreateBlobMessage(
92 input->presentation_url, input->presentation_id,
93 make_scoped_ptr(new std::vector<uint8_t>));
94 input->data.Swap(output->data.get());
95 return output.Pass();
99 NOTREACHED() << "Invalid presentation message type " << input->type;
100 return output.Pass();
103 void InvokeNewSessionMojoCallbackWithError(
104 const NewSessionMojoCallback& callback) {
105 callback.Run(
106 presentation::PresentationSessionInfoPtr(),
107 presentation::PresentationError::From(
108 PresentationError(PRESENTATION_ERROR_UNKNOWN, "Internal error")));
111 } // namespace
113 PresentationServiceImpl::PresentationServiceImpl(
114 RenderFrameHost* render_frame_host,
115 WebContents* web_contents,
116 PresentationServiceDelegate* delegate)
117 : WebContentsObserver(web_contents),
118 delegate_(delegate),
119 start_session_request_id_(kInvalidRequestSessionId),
120 weak_factory_(this) {
121 DCHECK(render_frame_host);
122 DCHECK(web_contents);
124 render_process_id_ = render_frame_host->GetProcess()->GetID();
125 render_frame_id_ = render_frame_host->GetRoutingID();
126 DVLOG(2) << "PresentationServiceImpl: "
127 << render_process_id_ << ", " << render_frame_id_;
128 if (delegate_)
129 delegate_->AddObserver(render_process_id_, render_frame_id_, this);
132 PresentationServiceImpl::~PresentationServiceImpl() {
133 if (delegate_)
134 delegate_->RemoveObserver(render_process_id_, render_frame_id_);
137 // static
138 void PresentationServiceImpl::CreateMojoService(
139 RenderFrameHost* render_frame_host,
140 mojo::InterfaceRequest<presentation::PresentationService> request) {
141 DVLOG(2) << "CreateMojoService";
142 WebContents* web_contents =
143 WebContents::FromRenderFrameHost(render_frame_host);
144 DCHECK(web_contents);
146 // This object will be deleted when the RenderFrameHost is about to be
147 // deleted (RenderFrameDeleted) or if a connection error occurred
148 // (OnConnectionError).
149 PresentationServiceImpl* impl = new PresentationServiceImpl(
150 render_frame_host,
151 web_contents,
152 GetContentClient()->browser()->GetPresentationServiceDelegate(
153 web_contents));
154 impl->Bind(request.Pass());
157 void PresentationServiceImpl::Bind(
158 mojo::InterfaceRequest<presentation::PresentationService> request) {
159 binding_.reset(new mojo::Binding<presentation::PresentationService>(
160 this, request.Pass()));
161 binding_->set_error_handler(this);
164 void PresentationServiceImpl::OnConnectionError() {
165 DVLOG(1) << "OnConnectionError";
166 delete this;
169 void PresentationServiceImpl::SetClient(
170 presentation::PresentationServiceClientPtr client) {
171 DCHECK(!client_.get());
172 // TODO(imcheng): Set ErrorHandler to listen for errors.
173 client_ = client.Pass();
176 void PresentationServiceImpl::ListenForScreenAvailability() {
177 DVLOG(2) << "ListenForScreenAvailability";
178 if (!delegate_)
179 return;
181 if (screen_availability_listener_.get() &&
182 screen_availability_listener_->GetPresentationUrl() ==
183 default_presentation_url_) {
184 return;
187 ResetScreenAvailabilityListener(default_presentation_url_);
190 void PresentationServiceImpl::ResetScreenAvailabilityListener(
191 const std::string& presentation_url) {
192 DCHECK(delegate_);
193 DCHECK(!screen_availability_listener_.get() ||
194 presentation_url != default_presentation_url_);
196 // (1) Unregister old listener with delegate
197 StopListeningForScreenAvailability();
199 // (2) Replace old listener with new listener
200 screen_availability_listener_.reset(new ScreenAvailabilityListenerImpl(
201 presentation_url, this));
203 // (3) Register new listener with delegate
204 if (!delegate_->AddScreenAvailabilityListener(
205 render_process_id_,
206 render_frame_id_,
207 screen_availability_listener_.get())) {
208 DVLOG(1) << "AddScreenAvailabilityListener failed. Ignoring request.";
209 screen_availability_listener_.reset();
213 void PresentationServiceImpl::StopListeningForScreenAvailability() {
214 DVLOG(2) << "StopListeningForScreenAvailability";
215 if (!delegate_)
216 return;
218 if (screen_availability_listener_.get()) {
219 delegate_->RemoveScreenAvailabilityListener(
220 render_process_id_,
221 render_frame_id_,
222 screen_availability_listener_.get());
223 screen_availability_listener_.reset();
227 void PresentationServiceImpl::ListenForDefaultSessionStart(
228 const DefaultSessionMojoCallback& callback) {
229 if (!default_session_start_context_.get())
230 default_session_start_context_.reset(new DefaultSessionStartContext);
231 default_session_start_context_->AddCallback(callback);
234 void PresentationServiceImpl::StartSession(
235 const mojo::String& presentation_url,
236 const NewSessionMojoCallback& callback) {
237 DVLOG(2) << "StartSession";
238 if (!delegate_) {
239 InvokeNewSessionMojoCallbackWithError(callback);
240 return;
243 // There is a StartSession request in progress. To avoid queueing up
244 // requests, the incoming request is rejected.
245 if (start_session_request_id_ != kInvalidRequestSessionId) {
246 InvokeNewSessionMojoCallbackWithError(callback);
247 return;
250 start_session_request_id_ = GetNextRequestSessionId();
251 pending_start_session_cb_.reset(new NewSessionMojoCallbackWrapper(callback));
252 delegate_->StartSession(
253 render_process_id_, render_frame_id_, presentation_url,
254 base::Bind(&PresentationServiceImpl::OnStartSessionSucceeded,
255 weak_factory_.GetWeakPtr(), start_session_request_id_),
256 base::Bind(&PresentationServiceImpl::OnStartSessionError,
257 weak_factory_.GetWeakPtr(), start_session_request_id_));
260 void PresentationServiceImpl::JoinSession(
261 const mojo::String& presentation_url,
262 const mojo::String& presentation_id,
263 const NewSessionMojoCallback& callback) {
264 DVLOG(2) << "JoinSession";
265 if (!delegate_) {
266 InvokeNewSessionMojoCallbackWithError(callback);
267 return;
270 int request_session_id = RegisterJoinSessionCallback(callback);
271 if (request_session_id == kInvalidRequestSessionId) {
272 InvokeNewSessionMojoCallbackWithError(callback);
273 return;
275 delegate_->JoinSession(
276 render_process_id_,
277 render_frame_id_,
278 presentation_url,
279 presentation_id,
280 base::Bind(&PresentationServiceImpl::OnJoinSessionSucceeded,
281 weak_factory_.GetWeakPtr(), request_session_id),
282 base::Bind(&PresentationServiceImpl::OnJoinSessionError,
283 weak_factory_.GetWeakPtr(), request_session_id));
286 int PresentationServiceImpl::RegisterJoinSessionCallback(
287 const NewSessionMojoCallback& callback) {
288 if (pending_join_session_cbs_.size() >= kMaxNumQueuedSessionRequests)
289 return kInvalidRequestSessionId;
291 int request_id = GetNextRequestSessionId();
292 pending_join_session_cbs_[request_id].reset(
293 new NewSessionMojoCallbackWrapper(callback));
294 return request_id;
297 void PresentationServiceImpl::OnStartSessionSucceeded(
298 int request_session_id,
299 const PresentationSessionInfo& session_info) {
300 if (request_session_id == start_session_request_id_) {
301 CHECK(pending_start_session_cb_.get());
302 pending_start_session_cb_->Run(
303 presentation::PresentationSessionInfo::From(session_info),
304 presentation::PresentationErrorPtr());
305 pending_start_session_cb_.reset();
306 start_session_request_id_ = kInvalidRequestSessionId;
310 void PresentationServiceImpl::OnStartSessionError(
311 int request_session_id,
312 const PresentationError& error) {
313 if (request_session_id == start_session_request_id_) {
314 CHECK(pending_start_session_cb_.get());
315 pending_start_session_cb_->Run(
316 presentation::PresentationSessionInfoPtr(),
317 presentation::PresentationError::From(error));
318 pending_start_session_cb_.reset();
319 start_session_request_id_ = kInvalidRequestSessionId;
323 void PresentationServiceImpl::OnJoinSessionSucceeded(
324 int request_session_id,
325 const PresentationSessionInfo& session_info) {
326 RunAndEraseJoinSessionMojoCallback(
327 request_session_id,
328 presentation::PresentationSessionInfo::From(session_info),
329 presentation::PresentationErrorPtr());
332 void PresentationServiceImpl::OnJoinSessionError(
333 int request_session_id,
334 const PresentationError& error) {
335 RunAndEraseJoinSessionMojoCallback(
336 request_session_id,
337 presentation::PresentationSessionInfoPtr(),
338 presentation::PresentationError::From(error));
341 void PresentationServiceImpl::RunAndEraseJoinSessionMojoCallback(
342 int request_session_id,
343 presentation::PresentationSessionInfoPtr session,
344 presentation::PresentationErrorPtr error) {
345 auto it = pending_join_session_cbs_.find(request_session_id);
346 if (it == pending_join_session_cbs_.end())
347 return;
349 DCHECK(it->second.get());
350 it->second->Run(session.Pass(), error.Pass());
351 pending_join_session_cbs_.erase(it);
354 void PresentationServiceImpl::SetDefaultPresentationURL(
355 const mojo::String& default_presentation_url) {
356 DVLOG(2) << "SetDefaultPresentationURL";
357 if (!delegate_)
358 return;
360 const std::string& old_default_url = default_presentation_url_;
361 const std::string& new_default_url = default_presentation_url.get();
363 if (old_default_url == new_default_url)
364 return;
366 // Replace screen availability listeners if any.
367 if (screen_availability_listener_.get())
368 ResetScreenAvailabilityListener(new_default_url);
370 delegate_->SetDefaultPresentationUrl(
371 render_process_id_,
372 render_frame_id_,
373 default_presentation_url);
374 default_presentation_url_ = default_presentation_url;
378 void PresentationServiceImpl::SendSessionMessage(
379 presentation::SessionMessagePtr session_message,
380 const SendMessageMojoCallback& callback) {
381 DVLOG(2) << "SendSessionMessage";
382 DCHECK(!session_message.is_null());
383 // send_message_callback_ should be null by now, otherwise resetting of
384 // send_message_callback_ with new callback will drop the old callback.
385 if (!delegate_ || send_message_callback_) {
386 callback.Run(false);
387 return;
390 send_message_callback_.reset(new SendMessageMojoCallback(callback));
391 delegate_->SendMessage(
392 render_process_id_,
393 render_frame_id_,
394 GetPresentationSessionMessage(session_message.Pass()),
395 base::Bind(&PresentationServiceImpl::OnSendMessageCallback,
396 weak_factory_.GetWeakPtr()));
399 void PresentationServiceImpl::OnSendMessageCallback(bool sent) {
400 // It is possible that Reset() is invoked before receiving this callback.
401 // So, always check send_message_callback_ for non-null.
402 if (send_message_callback_) {
403 send_message_callback_->Run(sent);
404 send_message_callback_.reset();
408 void PresentationServiceImpl::CloseSession(
409 const mojo::String& presentation_url,
410 const mojo::String& presentation_id) {
411 DVLOG(2) << "CloseSession " << presentation_id;
412 if (delegate_)
413 delegate_->CloseSession(render_process_id_, render_frame_id_,
414 presentation_id);
417 void PresentationServiceImpl::ListenForSessionStateChange() {
418 if (!delegate_)
419 return;
421 delegate_->ListenForSessionStateChange(
422 render_process_id_, render_frame_id_,
423 base::Bind(&PresentationServiceImpl::OnSessionStateChanged,
424 weak_factory_.GetWeakPtr()));
427 void PresentationServiceImpl::OnSessionStateChanged(
428 const PresentationSessionInfo& session_info,
429 PresentationSessionState session_state) {
430 DCHECK(client_.get());
431 client_->OnSessionStateChanged(
432 presentation::PresentationSessionInfo::From(session_info),
433 PresentationSessionStateToMojo(session_state));
436 bool PresentationServiceImpl::FrameMatches(
437 content::RenderFrameHost* render_frame_host) const {
438 if (!render_frame_host)
439 return false;
441 return render_frame_host->GetProcess()->GetID() == render_process_id_ &&
442 render_frame_host->GetRoutingID() == render_frame_id_;
445 void PresentationServiceImpl::ListenForSessionMessages(
446 const SessionMessagesCallback& callback) {
447 DVLOG(2) << "ListenForSessionMessages";
448 if (!delegate_) {
449 callback.Run(mojo::Array<presentation::SessionMessagePtr>());
450 return;
453 // Crash early if renderer is misbehaving.
454 CHECK(!on_session_messages_callback_.get());
456 on_session_messages_callback_.reset(new SessionMessagesCallback(callback));
457 delegate_->ListenForSessionMessages(
458 render_process_id_, render_frame_id_,
459 base::Bind(&PresentationServiceImpl::OnSessionMessages,
460 weak_factory_.GetWeakPtr()));
463 void PresentationServiceImpl::OnSessionMessages(
464 scoped_ptr<ScopedVector<PresentationSessionMessage>> messages) {
465 if (!on_session_messages_callback_.get()) {
466 // The Reset method of this class was invoked.
467 return;
470 if (!messages.get() || messages->empty()) {
471 // Error handling. Session is either closed or in error state.
472 on_session_messages_callback_->Run(
473 mojo::Array<presentation::SessionMessagePtr>());
474 } else {
475 mojo::Array<presentation::SessionMessagePtr> mojoMessages(messages->size());
476 for (size_t i = 0; i < messages->size(); ++i) {
477 mojoMessages[i] = ToMojoSessionMessage((*messages)[i]);
479 on_session_messages_callback_->Run(mojoMessages.Pass());
482 on_session_messages_callback_.reset();
485 void PresentationServiceImpl::DidNavigateAnyFrame(
486 content::RenderFrameHost* render_frame_host,
487 const content::LoadCommittedDetails& details,
488 const content::FrameNavigateParams& params) {
489 DVLOG(2) << "PresentationServiceImpl::DidNavigateAnyFrame";
490 if (!FrameMatches(render_frame_host))
491 return;
493 std::string prev_url_host = details.previous_url.host();
494 std::string curr_url_host = params.url.host();
496 // If a frame navigation is in-page (e.g. navigating to a fragment in
497 // same page) then we do not unregister listeners.
498 DVLOG(2) << "DidNavigateAnyFrame: "
499 << "prev host: " << prev_url_host << ", curr host: " << curr_url_host
500 << ", details.is_in_page: " << details.is_in_page;
501 if (details.is_in_page)
502 return;
504 // Reset if the frame actually navigated.
505 Reset();
508 void PresentationServiceImpl::RenderFrameDeleted(
509 content::RenderFrameHost* render_frame_host) {
510 DVLOG(2) << "PresentationServiceImpl::RenderFrameDeleted";
511 if (!FrameMatches(render_frame_host))
512 return;
514 // RenderFrameDeleted means the associated RFH is going to be deleted soon.
515 // This object should also be deleted.
516 Reset();
517 delete this;
520 void PresentationServiceImpl::Reset() {
521 DVLOG(2) << "PresentationServiceImpl::Reset";
522 if (delegate_)
523 delegate_->Reset(render_process_id_, render_frame_id_);
525 default_presentation_url_.clear();
527 screen_availability_listener_.reset();
529 start_session_request_id_ = kInvalidRequestSessionId;
530 pending_start_session_cb_.reset();
532 pending_join_session_cbs_.clear();
534 default_session_start_context_.reset();
536 if (on_session_messages_callback_.get()) {
537 on_session_messages_callback_->Run(
538 mojo::Array<presentation::SessionMessagePtr>());
539 on_session_messages_callback_.reset();
542 if (send_message_callback_) {
543 // Run the callback with false, indicating the renderer to stop sending
544 // the requests and invalidate all pending requests.
545 send_message_callback_->Run(false);
546 send_message_callback_.reset();
550 void PresentationServiceImpl::OnDelegateDestroyed() {
551 DVLOG(2) << "PresentationServiceImpl::OnDelegateDestroyed";
552 delegate_ = nullptr;
553 Reset();
556 void PresentationServiceImpl::OnDefaultPresentationStarted(
557 const PresentationSessionInfo& session) {
558 if (default_session_start_context_.get())
559 default_session_start_context_->set_session(session);
562 PresentationServiceImpl::ScreenAvailabilityListenerImpl
563 ::ScreenAvailabilityListenerImpl(
564 const std::string& presentation_url,
565 PresentationServiceImpl* service)
566 : presentation_url_(presentation_url),
567 service_(service) {
568 DCHECK(service_);
569 DCHECK(service_->client_.get());
572 PresentationServiceImpl::ScreenAvailabilityListenerImpl::
573 ~ScreenAvailabilityListenerImpl() {
576 std::string PresentationServiceImpl::ScreenAvailabilityListenerImpl
577 ::GetPresentationUrl() const {
578 return presentation_url_;
581 void PresentationServiceImpl::ScreenAvailabilityListenerImpl
582 ::OnScreenAvailabilityChanged(bool available) {
583 service_->client_->OnScreenAvailabilityUpdated(available);
586 PresentationServiceImpl::NewSessionMojoCallbackWrapper
587 ::NewSessionMojoCallbackWrapper(const NewSessionMojoCallback& callback)
588 : callback_(callback) {
591 PresentationServiceImpl::NewSessionMojoCallbackWrapper
592 ::~NewSessionMojoCallbackWrapper() {
593 if (!callback_.is_null())
594 InvokeNewSessionMojoCallbackWithError(callback_);
597 void PresentationServiceImpl::NewSessionMojoCallbackWrapper::Run(
598 presentation::PresentationSessionInfoPtr session,
599 presentation::PresentationErrorPtr error) {
600 DCHECK(!callback_.is_null());
601 callback_.Run(session.Pass(), error.Pass());
602 callback_.reset();
605 PresentationServiceImpl::DefaultSessionStartContext
606 ::DefaultSessionStartContext() {
609 PresentationServiceImpl::DefaultSessionStartContext
610 ::~DefaultSessionStartContext() {
611 Reset();
614 void PresentationServiceImpl::DefaultSessionStartContext::AddCallback(
615 const DefaultSessionMojoCallback& callback) {
616 if (session_.get()) {
617 DCHECK(callbacks_.empty());
618 callback.Run(presentation::PresentationSessionInfo::From(*session_));
619 session_.reset();
620 } else {
621 callbacks_.push_back(new DefaultSessionMojoCallback(callback));
625 void PresentationServiceImpl::DefaultSessionStartContext::set_session(
626 const PresentationSessionInfo& session) {
627 if (callbacks_.empty()) {
628 session_.reset(new PresentationSessionInfo(session));
629 } else {
630 DCHECK(!session_.get());
631 ScopedVector<DefaultSessionMojoCallback> callbacks;
632 callbacks.swap(callbacks_);
633 for (const auto& callback : callbacks)
634 callback->Run(presentation::PresentationSessionInfo::From(session));
638 void PresentationServiceImpl::DefaultSessionStartContext::Reset() {
639 ScopedVector<DefaultSessionMojoCallback> callbacks;
640 callbacks.swap(callbacks_);
641 for (const auto& callback : callbacks)
642 callback->Run(presentation::PresentationSessionInfoPtr());
643 session_.reset();
646 } // namespace content