Add ICU message format support
[chromium-blink-merge.git] / content / browser / presentation / presentation_service_impl.cc
blobdfba7ee532a5ec30d2ea10846acfdeaac8f274ea
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 presentation::SessionMessagePtr ToMojoSessionMessage(
33 const content::PresentationSessionMessage& input) {
34 presentation::SessionMessagePtr output(presentation::SessionMessage::New());
35 if (input.is_binary()) {
36 // binary data
37 output->type = presentation::PresentationMessageType::
38 PRESENTATION_MESSAGE_TYPE_ARRAY_BUFFER;
39 output->data = mojo::Array<uint8_t>::From(*input.data);
40 } else {
41 // string message
42 output->type =
43 presentation::PresentationMessageType::PRESENTATION_MESSAGE_TYPE_TEXT;
44 output->message = input.message;
46 return output.Pass();
49 scoped_ptr<PresentationSessionMessage> GetPresentationSessionMessage(
50 presentation::SessionMessagePtr input) {
51 DCHECK(!input.is_null());
52 scoped_ptr<content::PresentationSessionMessage> output;
53 switch (input->type) {
54 case presentation::PRESENTATION_MESSAGE_TYPE_TEXT: {
55 DCHECK(!input->message.is_null());
56 DCHECK(input->data.is_null());
57 // Return null PresentationSessionMessage if size exceeds.
58 if (input->message.size() > content::kMaxPresentationSessionMessageSize)
59 return output.Pass();
61 output.reset(
62 new PresentationSessionMessage(PresentationMessageType::TEXT));
63 input->message.Swap(&output->message);
64 return output.Pass();
66 case presentation::PRESENTATION_MESSAGE_TYPE_ARRAY_BUFFER: {
67 DCHECK(!input->data.is_null());
68 DCHECK(input->message.is_null());
69 if (input->data.size() > content::kMaxPresentationSessionMessageSize)
70 return output.Pass();
72 output.reset(new PresentationSessionMessage(
73 PresentationMessageType::ARRAY_BUFFER));
74 output->data.reset(new std::vector<uint8_t>);
75 input->data.Swap(output->data.get());
76 return output.Pass();
78 case presentation::PRESENTATION_MESSAGE_TYPE_BLOB: {
79 DCHECK(!input->data.is_null());
80 DCHECK(input->message.is_null());
81 if (input->data.size() > content::kMaxPresentationSessionMessageSize)
82 return output.Pass();
84 output.reset(
85 new PresentationSessionMessage(PresentationMessageType::BLOB));
86 output->data.reset(new std::vector<uint8_t>);
87 input->data.Swap(output->data.get());
88 return output.Pass();
92 NOTREACHED() << "Invalid presentation message type " << input->type;
93 return output.Pass();
96 void InvokeNewSessionMojoCallbackWithError(
97 const NewSessionMojoCallback& callback) {
98 callback.Run(
99 presentation::PresentationSessionInfoPtr(),
100 presentation::PresentationError::From(
101 PresentationError(PRESENTATION_ERROR_UNKNOWN, "Internal error")));
104 } // namespace
106 PresentationServiceImpl::PresentationServiceImpl(
107 RenderFrameHost* render_frame_host,
108 WebContents* web_contents,
109 PresentationServiceDelegate* delegate)
110 : WebContentsObserver(web_contents),
111 delegate_(delegate),
112 start_session_request_id_(kInvalidRequestSessionId),
113 weak_factory_(this) {
114 DCHECK(render_frame_host);
115 DCHECK(web_contents);
117 render_process_id_ = render_frame_host->GetProcess()->GetID();
118 render_frame_id_ = render_frame_host->GetRoutingID();
119 DVLOG(2) << "PresentationServiceImpl: "
120 << render_process_id_ << ", " << render_frame_id_;
121 if (delegate_)
122 delegate_->AddObserver(render_process_id_, render_frame_id_, this);
125 PresentationServiceImpl::~PresentationServiceImpl() {
126 if (delegate_)
127 delegate_->RemoveObserver(render_process_id_, render_frame_id_);
130 // static
131 void PresentationServiceImpl::CreateMojoService(
132 RenderFrameHost* render_frame_host,
133 mojo::InterfaceRequest<presentation::PresentationService> request) {
134 DVLOG(2) << "CreateMojoService";
135 WebContents* web_contents =
136 WebContents::FromRenderFrameHost(render_frame_host);
137 DCHECK(web_contents);
139 // This object will be deleted when the RenderFrameHost is about to be
140 // deleted (RenderFrameDeleted) or if a connection error occurred
141 // (OnConnectionError).
142 PresentationServiceImpl* impl = new PresentationServiceImpl(
143 render_frame_host,
144 web_contents,
145 GetContentClient()->browser()->GetPresentationServiceDelegate(
146 web_contents));
147 impl->Bind(request.Pass());
150 void PresentationServiceImpl::Bind(
151 mojo::InterfaceRequest<presentation::PresentationService> request) {
152 binding_.reset(new mojo::Binding<presentation::PresentationService>(
153 this, request.Pass()));
154 binding_->set_error_handler(this);
157 void PresentationServiceImpl::OnConnectionError() {
158 DVLOG(1) << "OnConnectionError";
159 delete this;
162 void PresentationServiceImpl::SetClient(
163 presentation::PresentationServiceClientPtr client) {
164 DCHECK(!client_.get());
165 // TODO(imcheng): Set ErrorHandler to listen for errors.
166 client_ = client.Pass();
169 void PresentationServiceImpl::ListenForScreenAvailability() {
170 DVLOG(2) << "ListenForScreenAvailability";
171 if (!delegate_)
172 return;
174 if (screen_availability_listener_.get() &&
175 screen_availability_listener_->GetPresentationUrl() ==
176 default_presentation_url_) {
177 return;
180 ResetScreenAvailabilityListener(default_presentation_url_);
183 void PresentationServiceImpl::ResetScreenAvailabilityListener(
184 const std::string& presentation_url) {
185 DCHECK(delegate_);
186 DCHECK(!screen_availability_listener_.get() ||
187 presentation_url != default_presentation_url_);
189 // (1) Unregister old listener with delegate
190 StopListeningForScreenAvailability();
192 // (2) Replace old listener with new listener
193 screen_availability_listener_.reset(new ScreenAvailabilityListenerImpl(
194 presentation_url, this));
196 // (3) Register new listener with delegate
197 if (!delegate_->AddScreenAvailabilityListener(
198 render_process_id_,
199 render_frame_id_,
200 screen_availability_listener_.get())) {
201 DVLOG(1) << "AddScreenAvailabilityListener failed. Ignoring request.";
202 screen_availability_listener_.reset();
206 void PresentationServiceImpl::StopListeningForScreenAvailability() {
207 DVLOG(2) << "StopListeningForScreenAvailability";
208 if (!delegate_)
209 return;
211 if (screen_availability_listener_.get()) {
212 delegate_->RemoveScreenAvailabilityListener(
213 render_process_id_,
214 render_frame_id_,
215 screen_availability_listener_.get());
216 screen_availability_listener_.reset();
220 void PresentationServiceImpl::ListenForDefaultSessionStart(
221 const DefaultSessionMojoCallback& callback) {
222 if (!default_session_start_context_.get())
223 default_session_start_context_.reset(new DefaultSessionStartContext);
224 default_session_start_context_->AddCallback(callback);
227 void PresentationServiceImpl::StartSession(
228 const mojo::String& presentation_url,
229 const NewSessionMojoCallback& callback) {
230 DVLOG(2) << "StartSession";
231 if (!delegate_) {
232 InvokeNewSessionMojoCallbackWithError(callback);
233 return;
236 // There is a StartSession request in progress. To avoid queueing up
237 // requests, the incoming request is rejected.
238 if (start_session_request_id_ != kInvalidRequestSessionId) {
239 InvokeNewSessionMojoCallbackWithError(callback);
240 return;
243 start_session_request_id_ = GetNextRequestSessionId();
244 pending_start_session_cb_.reset(new NewSessionMojoCallbackWrapper(callback));
245 delegate_->StartSession(
246 render_process_id_, render_frame_id_, presentation_url,
247 base::Bind(&PresentationServiceImpl::OnStartSessionSucceeded,
248 weak_factory_.GetWeakPtr(), start_session_request_id_),
249 base::Bind(&PresentationServiceImpl::OnStartSessionError,
250 weak_factory_.GetWeakPtr(), start_session_request_id_));
253 void PresentationServiceImpl::JoinSession(
254 const mojo::String& presentation_url,
255 const mojo::String& presentation_id,
256 const NewSessionMojoCallback& callback) {
257 DVLOG(2) << "JoinSession";
258 if (!delegate_) {
259 InvokeNewSessionMojoCallbackWithError(callback);
260 return;
263 int request_session_id = RegisterJoinSessionCallback(callback);
264 if (request_session_id == kInvalidRequestSessionId) {
265 InvokeNewSessionMojoCallbackWithError(callback);
266 return;
268 delegate_->JoinSession(
269 render_process_id_,
270 render_frame_id_,
271 presentation_url,
272 presentation_id,
273 base::Bind(&PresentationServiceImpl::OnJoinSessionSucceeded,
274 weak_factory_.GetWeakPtr(), request_session_id),
275 base::Bind(&PresentationServiceImpl::OnJoinSessionError,
276 weak_factory_.GetWeakPtr(), request_session_id));
279 int PresentationServiceImpl::RegisterJoinSessionCallback(
280 const NewSessionMojoCallback& callback) {
281 if (pending_join_session_cbs_.size() >= kMaxNumQueuedSessionRequests)
282 return kInvalidRequestSessionId;
284 int request_id = GetNextRequestSessionId();
285 pending_join_session_cbs_[request_id].reset(
286 new NewSessionMojoCallbackWrapper(callback));
287 return request_id;
290 void PresentationServiceImpl::OnStartSessionSucceeded(
291 int request_session_id,
292 const PresentationSessionInfo& session_info) {
293 if (request_session_id == start_session_request_id_) {
294 CHECK(pending_start_session_cb_.get());
295 pending_start_session_cb_->Run(
296 presentation::PresentationSessionInfo::From(session_info),
297 presentation::PresentationErrorPtr());
298 pending_start_session_cb_.reset();
299 start_session_request_id_ = kInvalidRequestSessionId;
303 void PresentationServiceImpl::OnStartSessionError(
304 int request_session_id,
305 const PresentationError& error) {
306 if (request_session_id == start_session_request_id_) {
307 CHECK(pending_start_session_cb_.get());
308 pending_start_session_cb_->Run(
309 presentation::PresentationSessionInfoPtr(),
310 presentation::PresentationError::From(error));
311 pending_start_session_cb_.reset();
312 start_session_request_id_ = kInvalidRequestSessionId;
316 void PresentationServiceImpl::OnJoinSessionSucceeded(
317 int request_session_id,
318 const PresentationSessionInfo& session_info) {
319 RunAndEraseJoinSessionMojoCallback(
320 request_session_id,
321 presentation::PresentationSessionInfo::From(session_info),
322 presentation::PresentationErrorPtr());
325 void PresentationServiceImpl::OnJoinSessionError(
326 int request_session_id,
327 const PresentationError& error) {
328 RunAndEraseJoinSessionMojoCallback(
329 request_session_id,
330 presentation::PresentationSessionInfoPtr(),
331 presentation::PresentationError::From(error));
334 void PresentationServiceImpl::RunAndEraseJoinSessionMojoCallback(
335 int request_session_id,
336 presentation::PresentationSessionInfoPtr session,
337 presentation::PresentationErrorPtr error) {
338 auto it = pending_join_session_cbs_.find(request_session_id);
339 if (it == pending_join_session_cbs_.end())
340 return;
342 DCHECK(it->second.get());
343 it->second->Run(session.Pass(), error.Pass());
344 pending_join_session_cbs_.erase(it);
347 void PresentationServiceImpl::SetDefaultPresentationURL(
348 const mojo::String& default_presentation_url) {
349 DVLOG(2) << "SetDefaultPresentationURL";
350 if (!delegate_)
351 return;
353 const std::string& old_default_url = default_presentation_url_;
354 const std::string& new_default_url = default_presentation_url.get();
356 if (old_default_url == new_default_url)
357 return;
359 // Replace screen availability listeners if any.
360 if (screen_availability_listener_.get())
361 ResetScreenAvailabilityListener(new_default_url);
363 delegate_->SetDefaultPresentationUrl(
364 render_process_id_,
365 render_frame_id_,
366 default_presentation_url);
367 default_presentation_url_ = default_presentation_url;
370 void PresentationServiceImpl::SendSessionMessage(
371 presentation::PresentationSessionInfoPtr session,
372 presentation::SessionMessagePtr session_message,
373 const SendMessageMojoCallback& callback) {
374 DVLOG(2) << "SendSessionMessage";
375 DCHECK(!session_message.is_null());
376 // send_message_callback_ should be null by now, otherwise resetting of
377 // send_message_callback_ with new callback will drop the old callback.
378 if (!delegate_ || send_message_callback_) {
379 callback.Run(false);
380 return;
383 send_message_callback_.reset(new SendMessageMojoCallback(callback));
384 delegate_->SendMessage(
385 render_process_id_, render_frame_id_,
386 session.To<PresentationSessionInfo>(),
387 GetPresentationSessionMessage(session_message.Pass()),
388 base::Bind(&PresentationServiceImpl::OnSendMessageCallback,
389 weak_factory_.GetWeakPtr()));
392 void PresentationServiceImpl::OnSendMessageCallback(bool sent) {
393 // It is possible that Reset() is invoked before receiving this callback.
394 // So, always check send_message_callback_ for non-null.
395 if (send_message_callback_) {
396 send_message_callback_->Run(sent);
397 send_message_callback_.reset();
401 void PresentationServiceImpl::CloseSession(
402 const mojo::String& presentation_url,
403 const mojo::String& presentation_id) {
404 DVLOG(2) << "CloseSession " << presentation_id;
405 if (delegate_)
406 delegate_->CloseSession(render_process_id_, render_frame_id_,
407 presentation_id);
410 void PresentationServiceImpl::ListenForSessionStateChange() {
411 if (!delegate_)
412 return;
414 delegate_->ListenForSessionStateChange(
415 render_process_id_, render_frame_id_,
416 base::Bind(&PresentationServiceImpl::OnSessionStateChanged,
417 weak_factory_.GetWeakPtr()));
420 void PresentationServiceImpl::OnSessionStateChanged(
421 const PresentationSessionInfo& session_info,
422 PresentationSessionState session_state) {
423 DCHECK(client_.get());
424 client_->OnSessionStateChanged(
425 presentation::PresentationSessionInfo::From(session_info),
426 PresentationSessionStateToMojo(session_state));
429 bool PresentationServiceImpl::FrameMatches(
430 content::RenderFrameHost* render_frame_host) const {
431 if (!render_frame_host)
432 return false;
434 return render_frame_host->GetProcess()->GetID() == render_process_id_ &&
435 render_frame_host->GetRoutingID() == render_frame_id_;
438 void PresentationServiceImpl::ListenForSessionMessages(
439 presentation::PresentationSessionInfoPtr session) {
440 DVLOG(2) << "ListenForSessionMessages";
441 if (!delegate_)
442 return;
444 PresentationSessionInfo session_info(session.To<PresentationSessionInfo>());
445 delegate_->ListenForSessionMessages(
446 render_process_id_, render_frame_id_, session_info,
447 base::Bind(&PresentationServiceImpl::OnSessionMessages,
448 weak_factory_.GetWeakPtr(), session_info));
451 void PresentationServiceImpl::OnSessionMessages(
452 const PresentationSessionInfo& session,
453 const ScopedVector<PresentationSessionMessage>& messages) {
454 DCHECK(client_);
456 DVLOG(2) << "OnSessionMessages";
457 mojo::Array<presentation::SessionMessagePtr> mojoMessages(messages.size());
458 for (size_t i = 0; i < messages.size(); ++i)
459 mojoMessages[i] = ToMojoSessionMessage(*messages[i]);
461 client_->OnSessionMessagesReceived(
462 presentation::PresentationSessionInfo::From(session),
463 mojoMessages.Pass());
466 void PresentationServiceImpl::DidNavigateAnyFrame(
467 content::RenderFrameHost* render_frame_host,
468 const content::LoadCommittedDetails& details,
469 const content::FrameNavigateParams& params) {
470 DVLOG(2) << "PresentationServiceImpl::DidNavigateAnyFrame";
471 if (!FrameMatches(render_frame_host))
472 return;
474 std::string prev_url_host = details.previous_url.host();
475 std::string curr_url_host = params.url.host();
477 // If a frame navigation is in-page (e.g. navigating to a fragment in
478 // same page) then we do not unregister listeners.
479 DVLOG(2) << "DidNavigateAnyFrame: "
480 << "prev host: " << prev_url_host << ", curr host: " << curr_url_host
481 << ", details.is_in_page: " << details.is_in_page;
482 if (details.is_in_page)
483 return;
485 // Reset if the frame actually navigated.
486 Reset();
489 void PresentationServiceImpl::RenderFrameDeleted(
490 content::RenderFrameHost* render_frame_host) {
491 DVLOG(2) << "PresentationServiceImpl::RenderFrameDeleted";
492 if (!FrameMatches(render_frame_host))
493 return;
495 // RenderFrameDeleted means the associated RFH is going to be deleted soon.
496 // This object should also be deleted.
497 Reset();
498 delete this;
501 void PresentationServiceImpl::Reset() {
502 DVLOG(2) << "PresentationServiceImpl::Reset";
503 if (delegate_)
504 delegate_->Reset(render_process_id_, render_frame_id_);
506 default_presentation_url_.clear();
508 screen_availability_listener_.reset();
510 start_session_request_id_ = kInvalidRequestSessionId;
511 pending_start_session_cb_.reset();
513 pending_join_session_cbs_.clear();
515 default_session_start_context_.reset();
517 if (on_session_messages_callback_.get()) {
518 on_session_messages_callback_->Run(
519 mojo::Array<presentation::SessionMessagePtr>());
520 on_session_messages_callback_.reset();
523 if (send_message_callback_) {
524 // Run the callback with false, indicating the renderer to stop sending
525 // the requests and invalidate all pending requests.
526 send_message_callback_->Run(false);
527 send_message_callback_.reset();
531 void PresentationServiceImpl::OnDelegateDestroyed() {
532 DVLOG(2) << "PresentationServiceImpl::OnDelegateDestroyed";
533 delegate_ = nullptr;
534 Reset();
537 void PresentationServiceImpl::OnDefaultPresentationStarted(
538 const PresentationSessionInfo& session) {
539 if (default_session_start_context_.get())
540 default_session_start_context_->set_session(session);
543 PresentationServiceImpl::ScreenAvailabilityListenerImpl
544 ::ScreenAvailabilityListenerImpl(
545 const std::string& presentation_url,
546 PresentationServiceImpl* service)
547 : presentation_url_(presentation_url),
548 service_(service) {
549 DCHECK(service_);
550 DCHECK(service_->client_.get());
553 PresentationServiceImpl::ScreenAvailabilityListenerImpl::
554 ~ScreenAvailabilityListenerImpl() {
557 std::string PresentationServiceImpl::ScreenAvailabilityListenerImpl
558 ::GetPresentationUrl() const {
559 return presentation_url_;
562 void PresentationServiceImpl::ScreenAvailabilityListenerImpl
563 ::OnScreenAvailabilityChanged(bool available) {
564 service_->client_->OnScreenAvailabilityUpdated(available);
567 PresentationServiceImpl::NewSessionMojoCallbackWrapper
568 ::NewSessionMojoCallbackWrapper(const NewSessionMojoCallback& callback)
569 : callback_(callback) {
572 PresentationServiceImpl::NewSessionMojoCallbackWrapper
573 ::~NewSessionMojoCallbackWrapper() {
574 if (!callback_.is_null())
575 InvokeNewSessionMojoCallbackWithError(callback_);
578 void PresentationServiceImpl::NewSessionMojoCallbackWrapper::Run(
579 presentation::PresentationSessionInfoPtr session,
580 presentation::PresentationErrorPtr error) {
581 DCHECK(!callback_.is_null());
582 callback_.Run(session.Pass(), error.Pass());
583 callback_.reset();
586 PresentationServiceImpl::DefaultSessionStartContext
587 ::DefaultSessionStartContext() {
590 PresentationServiceImpl::DefaultSessionStartContext
591 ::~DefaultSessionStartContext() {
592 Reset();
595 void PresentationServiceImpl::DefaultSessionStartContext::AddCallback(
596 const DefaultSessionMojoCallback& callback) {
597 if (session_.get()) {
598 DCHECK(callbacks_.empty());
599 callback.Run(presentation::PresentationSessionInfo::From(*session_));
600 session_.reset();
601 } else {
602 callbacks_.push_back(new DefaultSessionMojoCallback(callback));
606 void PresentationServiceImpl::DefaultSessionStartContext::set_session(
607 const PresentationSessionInfo& session) {
608 if (callbacks_.empty()) {
609 session_.reset(new PresentationSessionInfo(session));
610 } else {
611 DCHECK(!session_.get());
612 ScopedVector<DefaultSessionMojoCallback> callbacks;
613 callbacks.swap(callbacks_);
614 for (const auto& callback : callbacks)
615 callback->Run(presentation::PresentationSessionInfo::From(session));
619 void PresentationServiceImpl::DefaultSessionStartContext::Reset() {
620 ScopedVector<DefaultSessionMojoCallback> callbacks;
621 callbacks.swap(callbacks_);
622 for (const auto& callback : callbacks)
623 callback->Run(presentation::PresentationSessionInfoPtr());
624 session_.reset();
627 } // namespace content