[refactor] More post-NSS WebCrypto cleanups (utility functions).
[chromium-blink-merge.git] / content / browser / presentation / presentation_service_impl.cc
blob80e2aeacd74914cb97eee814b26873ef104ee8e4
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>
8 #include <string>
9 #include <vector>
11 #include "base/logging.h"
12 #include "base/stl_util.h"
13 #include "content/browser/presentation/presentation_type_converters.h"
14 #include "content/public/browser/content_browser_client.h"
15 #include "content/public/browser/navigation_details.h"
16 #include "content/public/browser/presentation_session_message.h"
17 #include "content/public/browser/render_frame_host.h"
18 #include "content/public/browser/render_process_host.h"
19 #include "content/public/browser/web_contents.h"
20 #include "content/public/common/content_client.h"
21 #include "content/public/common/frame_navigate_params.h"
22 #include "content/public/common/presentation_constants.h"
24 namespace content {
26 namespace {
28 const int kInvalidRequestSessionId = -1;
30 int GetNextRequestSessionId() {
31 static int next_request_session_id = 0;
32 return ++next_request_session_id;
35 // Converts a PresentationSessionMessage |input| to a SessionMessage.
36 // |input|: The message to convert.
37 // |pass_ownership|: If true, function may reuse strings or buffers from
38 // |input| without copying. |input| can be freely modified.
39 presentation::SessionMessagePtr ToMojoSessionMessage(
40 content::PresentationSessionMessage* input,
41 bool pass_ownership) {
42 DCHECK(input);
43 presentation::SessionMessagePtr output(presentation::SessionMessage::New());
44 if (input->is_binary()) {
45 // binary data
46 DCHECK(input->data);
47 output->type = presentation::PresentationMessageType::
48 PRESENTATION_MESSAGE_TYPE_ARRAY_BUFFER;
49 if (pass_ownership) {
50 output->data.Swap(input->data.get());
51 } else {
52 output->data = mojo::Array<uint8_t>::From(*input->data);
54 } else {
55 // string message
56 output->type =
57 presentation::PresentationMessageType::PRESENTATION_MESSAGE_TYPE_TEXT;
58 if (pass_ownership) {
59 output->message.Swap(&input->message);
60 } else {
61 output->message = input->message;
64 return output.Pass();
67 scoped_ptr<PresentationSessionMessage> GetPresentationSessionMessage(
68 presentation::SessionMessagePtr input) {
69 DCHECK(!input.is_null());
70 scoped_ptr<content::PresentationSessionMessage> output;
71 switch (input->type) {
72 case presentation::PRESENTATION_MESSAGE_TYPE_TEXT: {
73 DCHECK(!input->message.is_null());
74 DCHECK(input->data.is_null());
75 // Return null PresentationSessionMessage if size exceeds.
76 if (input->message.size() > content::kMaxPresentationSessionMessageSize)
77 return output.Pass();
79 output.reset(
80 new PresentationSessionMessage(PresentationMessageType::TEXT));
81 input->message.Swap(&output->message);
82 return output.Pass();
84 case presentation::PRESENTATION_MESSAGE_TYPE_ARRAY_BUFFER: {
85 DCHECK(!input->data.is_null());
86 DCHECK(input->message.is_null());
87 if (input->data.size() > content::kMaxPresentationSessionMessageSize)
88 return output.Pass();
90 output.reset(new PresentationSessionMessage(
91 PresentationMessageType::ARRAY_BUFFER));
92 output->data.reset(new std::vector<uint8_t>);
93 input->data.Swap(output->data.get());
94 return output.Pass();
96 case presentation::PRESENTATION_MESSAGE_TYPE_BLOB: {
97 DCHECK(!input->data.is_null());
98 DCHECK(input->message.is_null());
99 if (input->data.size() > content::kMaxPresentationSessionMessageSize)
100 return output.Pass();
102 output.reset(
103 new PresentationSessionMessage(PresentationMessageType::BLOB));
104 output->data.reset(new std::vector<uint8_t>);
105 input->data.Swap(output->data.get());
106 return output.Pass();
110 NOTREACHED() << "Invalid presentation message type " << input->type;
111 return output.Pass();
114 void InvokeNewSessionMojoCallbackWithError(
115 const NewSessionMojoCallback& callback) {
116 callback.Run(
117 presentation::PresentationSessionInfoPtr(),
118 presentation::PresentationError::From(
119 PresentationError(PRESENTATION_ERROR_UNKNOWN, "Internal error")));
122 } // namespace
124 PresentationServiceImpl::PresentationServiceImpl(
125 RenderFrameHost* render_frame_host,
126 WebContents* web_contents,
127 PresentationServiceDelegate* delegate)
128 : WebContentsObserver(web_contents),
129 delegate_(delegate),
130 start_session_request_id_(kInvalidRequestSessionId),
131 weak_factory_(this) {
132 DCHECK(render_frame_host);
133 DCHECK(web_contents);
135 render_process_id_ = render_frame_host->GetProcess()->GetID();
136 render_frame_id_ = render_frame_host->GetRoutingID();
137 DVLOG(2) << "PresentationServiceImpl: "
138 << render_process_id_ << ", " << render_frame_id_;
139 if (delegate_)
140 delegate_->AddObserver(render_process_id_, render_frame_id_, this);
143 PresentationServiceImpl::~PresentationServiceImpl() {
144 if (delegate_)
145 delegate_->RemoveObserver(render_process_id_, render_frame_id_);
148 // static
149 void PresentationServiceImpl::CreateMojoService(
150 RenderFrameHost* render_frame_host,
151 mojo::InterfaceRequest<presentation::PresentationService> request) {
152 DVLOG(2) << "CreateMojoService";
153 WebContents* web_contents =
154 WebContents::FromRenderFrameHost(render_frame_host);
155 DCHECK(web_contents);
157 // This object will be deleted when the RenderFrameHost is about to be
158 // deleted (RenderFrameDeleted) or if a connection error occurred
159 // (OnConnectionError).
160 PresentationServiceImpl* impl = new PresentationServiceImpl(
161 render_frame_host,
162 web_contents,
163 GetContentClient()->browser()->GetPresentationServiceDelegate(
164 web_contents));
165 impl->Bind(request.Pass());
168 void PresentationServiceImpl::Bind(
169 mojo::InterfaceRequest<presentation::PresentationService> request) {
170 binding_.reset(new mojo::Binding<presentation::PresentationService>(
171 this, request.Pass()));
172 binding_->set_connection_error_handler([this]() {
173 DVLOG(1) << "Connection error";
174 delete this;
178 void PresentationServiceImpl::SetClient(
179 presentation::PresentationServiceClientPtr client) {
180 DCHECK(!client_.get());
181 // TODO(imcheng): Set ErrorHandler to listen for errors.
182 client_ = client.Pass();
185 void PresentationServiceImpl::ListenForScreenAvailability(
186 const mojo::String& url) {
187 DVLOG(2) << "ListenForScreenAvailability " << url;
188 if (!delegate_) {
189 client_->OnScreenAvailabilityUpdated(url, false);
190 return;
193 const std::string& availability_url = url.get();
194 if (screen_availability_listeners_.count(availability_url))
195 return;
197 scoped_ptr<ScreenAvailabilityListenerImpl> listener(
198 new ScreenAvailabilityListenerImpl(availability_url, this));
199 if (delegate_->AddScreenAvailabilityListener(
200 render_process_id_,
201 render_frame_id_,
202 listener.get())) {
203 screen_availability_listeners_.set(availability_url, listener.Pass());
204 } else {
205 DVLOG(1) << "AddScreenAvailabilityListener failed. Ignoring request.";
209 void PresentationServiceImpl::StopListeningForScreenAvailability(
210 const mojo::String& url) {
211 DVLOG(2) << "StopListeningForScreenAvailability " << url;
212 if (!delegate_)
213 return;
215 const std::string& availability_url = url.get();
216 auto listener_it = screen_availability_listeners_.find(availability_url);
217 if (listener_it == screen_availability_listeners_.end())
218 return;
220 delegate_->RemoveScreenAvailabilityListener(
221 render_process_id_,
222 render_frame_id_,
223 listener_it->second);
224 screen_availability_listeners_.erase(listener_it);
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 callback.Run(
240 presentation::PresentationSessionInfoPtr(),
241 presentation::PresentationError::From(
242 PresentationError(PRESENTATION_ERROR_NO_AVAILABLE_SCREENS,
243 "No screens found.")));
244 return;
247 // There is a StartSession request in progress. To avoid queueing up
248 // requests, the incoming request is rejected.
249 if (start_session_request_id_ != kInvalidRequestSessionId) {
250 InvokeNewSessionMojoCallbackWithError(callback);
251 return;
254 start_session_request_id_ = GetNextRequestSessionId();
255 pending_start_session_cb_.reset(new NewSessionMojoCallbackWrapper(callback));
256 delegate_->StartSession(
257 render_process_id_, render_frame_id_, presentation_url,
258 base::Bind(&PresentationServiceImpl::OnStartSessionSucceeded,
259 weak_factory_.GetWeakPtr(), start_session_request_id_),
260 base::Bind(&PresentationServiceImpl::OnStartSessionError,
261 weak_factory_.GetWeakPtr(), start_session_request_id_));
264 void PresentationServiceImpl::JoinSession(
265 const mojo::String& presentation_url,
266 const mojo::String& presentation_id,
267 const NewSessionMojoCallback& callback) {
268 DVLOG(2) << "JoinSession";
269 if (!delegate_) {
270 callback.Run(
271 presentation::PresentationSessionInfoPtr(),
272 presentation::PresentationError::From(
273 PresentationError(PRESENTATION_ERROR_NO_PRESENTATION_FOUND,
274 "Error joining route: No matching route")));
275 return;
278 int request_session_id = RegisterJoinSessionCallback(callback);
279 if (request_session_id == kInvalidRequestSessionId) {
280 InvokeNewSessionMojoCallbackWithError(callback);
281 return;
283 delegate_->JoinSession(
284 render_process_id_,
285 render_frame_id_,
286 presentation_url,
287 presentation_id,
288 base::Bind(&PresentationServiceImpl::OnJoinSessionSucceeded,
289 weak_factory_.GetWeakPtr(), request_session_id),
290 base::Bind(&PresentationServiceImpl::OnJoinSessionError,
291 weak_factory_.GetWeakPtr(), request_session_id));
294 int PresentationServiceImpl::RegisterJoinSessionCallback(
295 const NewSessionMojoCallback& callback) {
296 if (pending_join_session_cbs_.size() >= kMaxNumQueuedSessionRequests)
297 return kInvalidRequestSessionId;
299 int request_id = GetNextRequestSessionId();
300 pending_join_session_cbs_[request_id].reset(
301 new NewSessionMojoCallbackWrapper(callback));
302 return request_id;
305 void PresentationServiceImpl::OnStartSessionSucceeded(
306 int request_session_id,
307 const PresentationSessionInfo& session_info) {
308 if (request_session_id == start_session_request_id_) {
309 CHECK(pending_start_session_cb_.get());
310 pending_start_session_cb_->Run(
311 presentation::PresentationSessionInfo::From(session_info),
312 presentation::PresentationErrorPtr());
313 pending_start_session_cb_.reset();
314 start_session_request_id_ = kInvalidRequestSessionId;
318 void PresentationServiceImpl::OnStartSessionError(
319 int request_session_id,
320 const PresentationError& error) {
321 if (request_session_id == start_session_request_id_) {
322 CHECK(pending_start_session_cb_.get());
323 pending_start_session_cb_->Run(
324 presentation::PresentationSessionInfoPtr(),
325 presentation::PresentationError::From(error));
326 pending_start_session_cb_.reset();
327 start_session_request_id_ = kInvalidRequestSessionId;
331 void PresentationServiceImpl::OnJoinSessionSucceeded(
332 int request_session_id,
333 const PresentationSessionInfo& session_info) {
334 RunAndEraseJoinSessionMojoCallback(
335 request_session_id,
336 presentation::PresentationSessionInfo::From(session_info),
337 presentation::PresentationErrorPtr());
340 void PresentationServiceImpl::OnJoinSessionError(
341 int request_session_id,
342 const PresentationError& error) {
343 RunAndEraseJoinSessionMojoCallback(
344 request_session_id,
345 presentation::PresentationSessionInfoPtr(),
346 presentation::PresentationError::From(error));
349 void PresentationServiceImpl::RunAndEraseJoinSessionMojoCallback(
350 int request_session_id,
351 presentation::PresentationSessionInfoPtr session,
352 presentation::PresentationErrorPtr error) {
353 auto it = pending_join_session_cbs_.find(request_session_id);
354 if (it == pending_join_session_cbs_.end())
355 return;
357 DCHECK(it->second.get());
358 it->second->Run(session.Pass(), error.Pass());
359 pending_join_session_cbs_.erase(it);
362 void PresentationServiceImpl::SetDefaultPresentationURL(
363 const mojo::String& url) {
364 DVLOG(2) << "SetDefaultPresentationURL";
365 if (!delegate_)
366 return;
368 const std::string& new_default_url = url.get();
369 if (default_presentation_url_ == new_default_url)
370 return;
371 delegate_->SetDefaultPresentationUrl(
372 render_process_id_,
373 render_frame_id_,
374 new_default_url);
375 default_presentation_url_ = new_default_url;
378 void PresentationServiceImpl::SendSessionMessage(
379 presentation::PresentationSessionInfoPtr session,
380 presentation::SessionMessagePtr session_message,
381 const SendMessageMojoCallback& callback) {
382 DVLOG(2) << "SendSessionMessage";
383 DCHECK(!session_message.is_null());
384 // send_message_callback_ should be null by now, otherwise resetting of
385 // send_message_callback_ with new callback will drop the old callback.
386 if (!delegate_ || send_message_callback_) {
387 callback.Run(false);
388 return;
391 send_message_callback_.reset(new SendMessageMojoCallback(callback));
392 delegate_->SendMessage(
393 render_process_id_, render_frame_id_,
394 session.To<PresentationSessionInfo>(),
395 GetPresentationSessionMessage(session_message.Pass()),
396 base::Bind(&PresentationServiceImpl::OnSendMessageCallback,
397 weak_factory_.GetWeakPtr()));
400 void PresentationServiceImpl::OnSendMessageCallback(bool sent) {
401 // It is possible that Reset() is invoked before receiving this callback.
402 // So, always check send_message_callback_ for non-null.
403 if (send_message_callback_) {
404 send_message_callback_->Run(sent);
405 send_message_callback_.reset();
409 void PresentationServiceImpl::CloseSession(
410 const mojo::String& presentation_url,
411 const mojo::String& presentation_id) {
412 DVLOG(2) << "CloseSession " << presentation_id;
413 if (delegate_)
414 delegate_->CloseSession(render_process_id_, render_frame_id_,
415 presentation_id);
418 void PresentationServiceImpl::ListenForSessionStateChange() {
419 if (!delegate_)
420 return;
422 delegate_->ListenForSessionStateChange(
423 render_process_id_, render_frame_id_,
424 base::Bind(&PresentationServiceImpl::OnSessionStateChanged,
425 weak_factory_.GetWeakPtr()));
428 void PresentationServiceImpl::OnSessionStateChanged(
429 const PresentationSessionInfo& session_info,
430 PresentationSessionState session_state) {
431 DCHECK(client_.get());
432 client_->OnSessionStateChanged(
433 presentation::PresentationSessionInfo::From(session_info),
434 PresentationSessionStateToMojo(session_state));
437 bool PresentationServiceImpl::FrameMatches(
438 content::RenderFrameHost* render_frame_host) const {
439 if (!render_frame_host)
440 return false;
442 return render_frame_host->GetProcess()->GetID() == render_process_id_ &&
443 render_frame_host->GetRoutingID() == render_frame_id_;
446 void PresentationServiceImpl::ListenForSessionMessages(
447 presentation::PresentationSessionInfoPtr session) {
448 DVLOG(2) << "ListenForSessionMessages";
449 if (!delegate_)
450 return;
452 PresentationSessionInfo session_info(session.To<PresentationSessionInfo>());
453 delegate_->ListenForSessionMessages(
454 render_process_id_, render_frame_id_, session_info,
455 base::Bind(&PresentationServiceImpl::OnSessionMessages,
456 weak_factory_.GetWeakPtr(), session_info));
459 void PresentationServiceImpl::OnSessionMessages(
460 const PresentationSessionInfo& session,
461 const ScopedVector<PresentationSessionMessage>& messages,
462 bool pass_ownership) {
463 DCHECK(client_);
465 DVLOG(2) << "OnSessionMessages";
466 mojo::Array<presentation::SessionMessagePtr> mojoMessages(messages.size());
467 for (size_t i = 0; i < messages.size(); ++i)
468 mojoMessages[i] = ToMojoSessionMessage(messages[i], pass_ownership);
470 client_->OnSessionMessagesReceived(
471 presentation::PresentationSessionInfo::From(session),
472 mojoMessages.Pass());
475 void PresentationServiceImpl::DidNavigateAnyFrame(
476 content::RenderFrameHost* render_frame_host,
477 const content::LoadCommittedDetails& details,
478 const content::FrameNavigateParams& params) {
479 DVLOG(2) << "PresentationServiceImpl::DidNavigateAnyFrame";
480 if (!FrameMatches(render_frame_host))
481 return;
483 std::string prev_url_host = details.previous_url.host();
484 std::string curr_url_host = params.url.host();
486 // If a frame navigation is in-page (e.g. navigating to a fragment in
487 // same page) then we do not unregister listeners.
488 DVLOG(2) << "DidNavigateAnyFrame: "
489 << "prev host: " << prev_url_host << ", curr host: " << curr_url_host
490 << ", details.is_in_page: " << details.is_in_page;
491 if (details.is_in_page)
492 return;
494 // Reset if the frame actually navigated.
495 Reset();
498 void PresentationServiceImpl::RenderFrameDeleted(
499 content::RenderFrameHost* render_frame_host) {
500 DVLOG(2) << "PresentationServiceImpl::RenderFrameDeleted";
501 if (!FrameMatches(render_frame_host))
502 return;
504 // RenderFrameDeleted means the associated RFH is going to be deleted soon.
505 // This object should also be deleted.
506 Reset();
507 delete this;
510 void PresentationServiceImpl::Reset() {
511 DVLOG(2) << "PresentationServiceImpl::Reset";
512 if (delegate_)
513 delegate_->Reset(render_process_id_, render_frame_id_);
515 default_presentation_url_.clear();
517 screen_availability_listeners_.clear();
519 start_session_request_id_ = kInvalidRequestSessionId;
520 pending_start_session_cb_.reset();
522 pending_join_session_cbs_.clear();
524 default_session_start_context_.reset();
526 if (on_session_messages_callback_.get()) {
527 on_session_messages_callback_->Run(
528 mojo::Array<presentation::SessionMessagePtr>());
529 on_session_messages_callback_.reset();
532 if (send_message_callback_) {
533 // Run the callback with false, indicating the renderer to stop sending
534 // the requests and invalidate all pending requests.
535 send_message_callback_->Run(false);
536 send_message_callback_.reset();
540 void PresentationServiceImpl::OnDelegateDestroyed() {
541 DVLOG(2) << "PresentationServiceImpl::OnDelegateDestroyed";
542 delegate_ = nullptr;
543 Reset();
546 void PresentationServiceImpl::OnDefaultPresentationStarted(
547 const PresentationSessionInfo& session) {
548 if (default_session_start_context_.get())
549 default_session_start_context_->set_session(session);
552 PresentationServiceImpl::ScreenAvailabilityListenerImpl
553 ::ScreenAvailabilityListenerImpl(
554 const std::string& availability_url,
555 PresentationServiceImpl* service)
556 : availability_url_(availability_url),
557 service_(service) {
558 DCHECK(service_);
559 DCHECK(service_->client_.get());
562 PresentationServiceImpl::ScreenAvailabilityListenerImpl::
563 ~ScreenAvailabilityListenerImpl() {
566 std::string PresentationServiceImpl::ScreenAvailabilityListenerImpl
567 ::GetAvailabilityUrl() const {
568 return availability_url_;
571 void PresentationServiceImpl::ScreenAvailabilityListenerImpl
572 ::OnScreenAvailabilityChanged(bool available) {
573 service_->client_->OnScreenAvailabilityUpdated(availability_url_, available);
576 PresentationServiceImpl::NewSessionMojoCallbackWrapper
577 ::NewSessionMojoCallbackWrapper(const NewSessionMojoCallback& callback)
578 : callback_(callback) {
581 PresentationServiceImpl::NewSessionMojoCallbackWrapper
582 ::~NewSessionMojoCallbackWrapper() {
583 if (!callback_.is_null())
584 InvokeNewSessionMojoCallbackWithError(callback_);
587 void PresentationServiceImpl::NewSessionMojoCallbackWrapper::Run(
588 presentation::PresentationSessionInfoPtr session,
589 presentation::PresentationErrorPtr error) {
590 DCHECK(!callback_.is_null());
591 callback_.Run(session.Pass(), error.Pass());
592 callback_.reset();
595 PresentationServiceImpl::DefaultSessionStartContext
596 ::DefaultSessionStartContext() {
599 PresentationServiceImpl::DefaultSessionStartContext
600 ::~DefaultSessionStartContext() {
601 Reset();
604 void PresentationServiceImpl::DefaultSessionStartContext::AddCallback(
605 const DefaultSessionMojoCallback& callback) {
606 if (session_.get()) {
607 DCHECK(callbacks_.empty());
608 callback.Run(presentation::PresentationSessionInfo::From(*session_));
609 session_.reset();
610 } else {
611 callbacks_.push_back(new DefaultSessionMojoCallback(callback));
615 void PresentationServiceImpl::DefaultSessionStartContext::set_session(
616 const PresentationSessionInfo& session) {
617 if (callbacks_.empty()) {
618 session_.reset(new PresentationSessionInfo(session));
619 } else {
620 DCHECK(!session_.get());
621 ScopedVector<DefaultSessionMojoCallback> callbacks;
622 callbacks.swap(callbacks_);
623 for (const auto& callback : callbacks)
624 callback->Run(presentation::PresentationSessionInfo::From(session));
628 void PresentationServiceImpl::DefaultSessionStartContext::Reset() {
629 ScopedVector<DefaultSessionMojoCallback> callbacks;
630 callbacks.swap(callbacks_);
631 for (const auto& callback : callbacks)
632 callback->Run(presentation::PresentationSessionInfoPtr());
633 session_.reset();
636 } // namespace content