Use "= delete" for DISALLOW_COPY and DISALLOW_ASSIGN.
[chromium-blink-merge.git] / content / renderer / presentation / presentation_dispatcher.cc
blob29f5047f0b57ec0639ff4d9765a2c645beb807f9
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/renderer/presentation/presentation_dispatcher.h"
7 #include "base/logging.h"
8 #include "content/common/presentation/presentation_service.mojom.h"
9 #include "content/public/common/presentation_constants.h"
10 #include "content/public/common/service_registry.h"
11 #include "content/public/renderer/render_frame.h"
12 #include "content/renderer/presentation/presentation_session_client.h"
13 #include "third_party/WebKit/public/platform/WebString.h"
14 #include "third_party/WebKit/public/platform/modules/presentation/WebPresentationController.h"
15 #include "third_party/WebKit/public/platform/modules/presentation/WebPresentationError.h"
16 #include "third_party/WebKit/public/web/WebDocument.h"
17 #include "third_party/WebKit/public/web/WebLocalFrame.h"
18 #include "url/gurl.h"
20 namespace {
22 blink::WebPresentationError::ErrorType GetWebPresentationErrorTypeFromMojo(
23 presentation::PresentationErrorType mojoErrorType) {
24 switch (mojoErrorType) {
25 case presentation::PRESENTATION_ERROR_TYPE_NO_AVAILABLE_SCREENS:
26 return blink::WebPresentationError::ErrorTypeNoAvailableScreens;
27 case presentation::PRESENTATION_ERROR_TYPE_SESSION_REQUEST_CANCELLED:
28 return blink::WebPresentationError::ErrorTypeSessionRequestCancelled;
29 case presentation::PRESENTATION_ERROR_TYPE_NO_PRESENTATION_FOUND:
30 return blink::WebPresentationError::ErrorTypeNoPresentationFound;
31 case presentation::PRESENTATION_ERROR_TYPE_UNKNOWN:
32 default:
33 return blink::WebPresentationError::ErrorTypeUnknown;
37 blink::WebPresentationSessionState GetWebPresentationSessionStateFromMojo(
38 presentation::PresentationSessionState mojoSessionState) {
39 switch (mojoSessionState) {
40 case presentation::PRESENTATION_SESSION_STATE_CONNECTED:
41 return blink::WebPresentationSessionState::Connected;
42 case presentation::PRESENTATION_SESSION_STATE_DISCONNECTED:
43 return blink::WebPresentationSessionState::Disconnected;
46 NOTREACHED();
47 return blink::WebPresentationSessionState::Disconnected;
50 GURL GetPresentationURLFromFrame(content::RenderFrame* frame) {
51 DCHECK(frame);
53 GURL url(frame->GetWebFrame()->document().defaultPresentationURL());
54 return url.is_valid() ? url : GURL();
57 } // namespace
59 namespace content {
61 PresentationDispatcher::PresentationDispatcher(RenderFrame* render_frame)
62 : RenderFrameObserver(render_frame),
63 controller_(nullptr),
64 binding_(this) {
67 PresentationDispatcher::~PresentationDispatcher() {
68 // Controller should be destroyed before the dispatcher when frame is
69 // destroyed.
70 DCHECK(!controller_);
73 void PresentationDispatcher::setController(
74 blink::WebPresentationController* controller) {
75 // There shouldn't be any swapping from one non-null controller to another.
76 DCHECK(controller != controller_ && (!controller || !controller_));
77 controller_ = controller;
78 // The controller is set to null when the frame is about to be detached.
79 // Nothing is listening for screen availability anymore but the Mojo service
80 // will know about the frame being detached anyway.
83 void PresentationDispatcher::updateAvailableChangeWatched(bool watched) {
84 ConnectToPresentationServiceIfNeeded();
85 if (watched)
86 presentation_service_->ListenForScreenAvailability();
87 else
88 presentation_service_->StopListeningForScreenAvailability();
91 void PresentationDispatcher::startSession(
92 const blink::WebString& presentationUrl,
93 const blink::WebString& presentationId,
94 blink::WebPresentationSessionClientCallbacks* callback) {
95 DCHECK(callback);
96 ConnectToPresentationServiceIfNeeded();
98 // The dispatcher owns the service so |this| will be valid when
99 // OnSessionCreated() is called. |callback| needs to be alive and also needs
100 // to be destroyed so we transfer its ownership to the mojo callback.
101 presentation_service_->StartSession(
102 presentationUrl.utf8(),
103 presentationId.utf8(),
104 base::Bind(&PresentationDispatcher::OnSessionCreated,
105 base::Unretained(this),
106 base::Owned(callback)));
109 void PresentationDispatcher::joinSession(
110 const blink::WebString& presentationUrl,
111 const blink::WebString& presentationId,
112 blink::WebPresentationSessionClientCallbacks* callback) {
113 DCHECK(callback);
114 ConnectToPresentationServiceIfNeeded();
116 // The dispatcher owns the service so |this| will be valid when
117 // OnSessionCreated() is called. |callback| needs to be alive and also needs
118 // to be destroyed so we transfer its ownership to the mojo callback.
119 presentation_service_->JoinSession(
120 presentationUrl.utf8(),
121 presentationId.utf8(),
122 base::Bind(&PresentationDispatcher::OnSessionCreated,
123 base::Unretained(this),
124 base::Owned(callback)));
127 void PresentationDispatcher::sendString(
128 const blink::WebString& presentationUrl,
129 const blink::WebString& presentationId,
130 const blink::WebString& message) {
131 if (message.utf8().size() > kMaxPresentationSessionMessageSize) {
132 // TODO(crbug.com/459008): Limit the size of individual messages to 64k
133 // for now. Consider throwing DOMException or splitting bigger messages
134 // into smaller chunks later.
135 LOG(WARNING) << "message size exceeded limit!";
136 return;
139 presentation::SessionMessage* session_message =
140 new presentation::SessionMessage();
141 session_message->presentation_url = presentationUrl.utf8();
142 session_message->presentation_id = presentationId.utf8();
143 session_message->type = presentation::PresentationMessageType::
144 PRESENTATION_MESSAGE_TYPE_TEXT;
145 session_message->message = message.utf8();
147 message_request_queue_.push(make_linked_ptr(session_message));
148 // Start processing request if only one in the queue.
149 if (message_request_queue_.size() == 1) {
150 const linked_ptr<presentation::SessionMessage>& request =
151 message_request_queue_.front();
152 DoSendMessage(*request);
156 void PresentationDispatcher::sendArrayBuffer(
157 const blink::WebString& presentationUrl,
158 const blink::WebString& presentationId,
159 const uint8* data,
160 size_t length) {
161 DCHECK(data);
162 if (length > kMaxPresentationSessionMessageSize) {
163 // TODO(crbug.com/459008): Same as in sendString().
164 LOG(WARNING) << "data size exceeded limit!";
165 return;
168 const std::vector<uint8> vector(data, data + length);
169 presentation::SessionMessage* session_message =
170 new presentation::SessionMessage();
171 session_message->presentation_url = presentationUrl.utf8();
172 session_message->presentation_id = presentationId.utf8();
173 session_message->type = presentation::PresentationMessageType::
174 PRESENTATION_MESSAGE_TYPE_ARRAY_BUFFER;
175 session_message->data = mojo::Array<uint8>::From(vector);
177 message_request_queue_.push(make_linked_ptr(session_message));
178 // Start processing request if only one in the queue.
179 if (message_request_queue_.size() == 1) {
180 const linked_ptr<presentation::SessionMessage>& request =
181 message_request_queue_.front();
182 DoSendMessage(*request);
186 void PresentationDispatcher::DoSendMessage(
187 const presentation::SessionMessage& session_message) {
188 ConnectToPresentationServiceIfNeeded();
190 presentation::SessionMessagePtr message_request(
191 presentation::SessionMessage::New());
192 message_request->presentation_url = session_message.presentation_url;
193 message_request->presentation_id = session_message.presentation_id;
194 message_request->type = session_message.type;
195 if (session_message.type == presentation::PresentationMessageType::
196 PRESENTATION_MESSAGE_TYPE_TEXT) {
197 message_request->message = session_message.message;
198 } else if (session_message.type == presentation::PresentationMessageType::
199 PRESENTATION_MESSAGE_TYPE_ARRAY_BUFFER) {
200 message_request->data = mojo::Array<uint8>::From(
201 session_message.data.storage());
204 presentation_service_->SendSessionMessage(
205 message_request.Pass(),
206 base::Bind(&PresentationDispatcher::HandleSendMessageRequests,
207 base::Unretained(this)));
210 void PresentationDispatcher::HandleSendMessageRequests(bool success) {
211 // In normal cases, message_request_queue_ should not be empty at this point
212 // of time, but when DidCommitProvisionalLoad() is invoked before receiving
213 // the callback for previous send mojo call, queue would have been emptied.
214 if (message_request_queue_.empty())
215 return;
217 if (!success) {
218 // PresentationServiceImpl is informing that Frame has been detached or
219 // navigated away. Invalidate all pending requests.
220 MessageRequestQueue empty;
221 std::swap(message_request_queue_, empty);
222 return;
225 message_request_queue_.pop();
226 if (!message_request_queue_.empty()) {
227 const linked_ptr<presentation::SessionMessage>& request =
228 message_request_queue_.front();
229 DoSendMessage(*request);
233 void PresentationDispatcher::closeSession(
234 const blink::WebString& presentationUrl,
235 const blink::WebString& presentationId) {
236 ConnectToPresentationServiceIfNeeded();
238 presentation_service_->CloseSession(
239 presentationUrl.utf8(),
240 presentationId.utf8());
243 void PresentationDispatcher::DidChangeDefaultPresentation() {
244 GURL presentation_url(GetPresentationURLFromFrame(render_frame()));
246 ConnectToPresentationServiceIfNeeded();
247 presentation_service_->SetDefaultPresentationURL(
248 presentation_url.spec(), mojo::String());
251 void PresentationDispatcher::DidCommitProvisionalLoad(
252 bool is_new_navigation,
253 bool is_same_page_navigation) {
254 blink::WebFrame* frame = render_frame()->GetWebFrame();
255 // If not top-level navigation.
256 if (frame->parent() || is_same_page_navigation)
257 return;
259 // Remove all pending send message requests.
260 MessageRequestQueue empty;
261 std::swap(message_request_queue_, empty);
264 void PresentationDispatcher::OnScreenAvailabilityUpdated(bool available) {
265 if (controller_)
266 controller_->didChangeAvailability(available);
269 void PresentationDispatcher::OnDefaultSessionStarted(
270 presentation::PresentationSessionInfoPtr session_info) {
271 if (!controller_)
272 return;
274 // Reset the callback to get the next event.
275 presentation_service_->ListenForDefaultSessionStart(base::Bind(
276 &PresentationDispatcher::OnDefaultSessionStarted,
277 base::Unretained(this)));
279 if (!session_info.is_null()) {
280 controller_->didStartDefaultSession(
281 new PresentationSessionClient(session_info.Pass()));
285 void PresentationDispatcher::OnSessionCreated(
286 blink::WebPresentationSessionClientCallbacks* callback,
287 presentation::PresentationSessionInfoPtr session_info,
288 presentation::PresentationErrorPtr error) {
289 DCHECK(callback);
290 if (!error.is_null()) {
291 DCHECK(session_info.is_null());
292 callback->onError(new blink::WebPresentationError(
293 GetWebPresentationErrorTypeFromMojo(error->error_type),
294 blink::WebString::fromUTF8(error->message)));
295 return;
298 DCHECK(!session_info.is_null());
299 callback->onSuccess(new PresentationSessionClient(session_info.Pass()));
302 void PresentationDispatcher::OnSessionStateChange(
303 presentation::PresentationSessionInfoPtr session_info,
304 presentation::PresentationSessionState session_state) {
305 if (!controller_)
306 return;
308 presentation_service_->ListenForSessionStateChange(base::Bind(
309 &PresentationDispatcher::OnSessionStateChange,
310 base::Unretained(this)));
312 DCHECK(!session_info.is_null());
313 controller_->didChangeSessionState(
314 new PresentationSessionClient(session_info.Pass()),
315 GetWebPresentationSessionStateFromMojo(session_state));
318 void PresentationDispatcher::OnSessionMessagesReceived(
319 mojo::Array<presentation::SessionMessagePtr> messages) {
320 // When messages is null, there is an error at presentation service side.
321 if (!controller_ || messages.is_null())
322 return;
324 for (size_t i = 0; i < messages.size(); ++i) {
325 if (messages[i]->type ==
326 presentation::PresentationMessageType::PRESENTATION_MESSAGE_TYPE_TEXT) {
327 controller_->didReceiveSessionTextMessage(
328 new PresentationSessionClient(messages[i]->presentation_url,
329 messages[i]->presentation_id),
330 blink::WebString::fromUTF8(messages[i]->message));
331 } else {
332 // TODO(haibinlu): handle binary message
336 presentation_service_->ListenForSessionMessages(
337 base::Bind(&PresentationDispatcher::OnSessionMessagesReceived,
338 base::Unretained(this)));
341 void PresentationDispatcher::ConnectToPresentationServiceIfNeeded() {
342 if (presentation_service_.get())
343 return;
345 render_frame()->GetServiceRegistry()->ConnectToRemoteService(
346 &presentation_service_);
347 presentation::PresentationServiceClientPtr client_ptr;
348 binding_.Bind(GetProxy(&client_ptr));
349 presentation_service_->SetClient(client_ptr.Pass());
351 // TODO(imcheng): Uncomment these once they are implemented on the browser
352 // side. (crbug.com/459006)
354 presentation_service_->ListenForDefaultSessionStart(base::Bind(
355 &PresentationDispatcher::OnDefaultSessionStarted,
356 base::Unretained(this)));
357 presentation_service_->ListenForSessionStateChange(base::Bind(
358 &PresentationDispatcher::OnSessionStateChange,
359 base::Unretained(this)));
360 presentation_service_->ListenForSessionMessages(
361 base::Bind(&PresentationDispatcher::OnSessionMessagesReceived,
362 base::Unretained(this)));
366 } // namespace content