1 // Copyright 2013 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/media/webcontentdecryptionmodulesession_impl.h"
8 #include "base/callback_helpers.h"
9 #include "base/logging.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "content/renderer/media/cdm_session_adapter.h"
13 #include "media/base/cdm_promise.h"
14 #include "third_party/WebKit/public/platform/WebURL.h"
18 const char kCreateSessionUMAName
[] = "CreateSession";
20 // For backwards compatibility with blink not using
21 // WebContentDecryptionModuleResult, reserve an index for |outstanding_results_|
22 // that will not be used when adding a WebContentDecryptionModuleResult.
23 // TODO(jrummell): Remove once blink always uses
24 // WebContentDecryptionModuleResult.
25 const uint32 kReservedIndex
= 0;
27 static blink::WebContentDecryptionModuleException
ConvertException(
28 media::MediaKeys::Exception exception_code
) {
29 switch (exception_code
) {
30 case media::MediaKeys::NOT_SUPPORTED_ERROR
:
31 return blink::WebContentDecryptionModuleExceptionNotSupportedError
;
32 case media::MediaKeys::INVALID_STATE_ERROR
:
33 return blink::WebContentDecryptionModuleExceptionInvalidStateError
;
34 case media::MediaKeys::INVALID_ACCESS_ERROR
:
35 return blink::WebContentDecryptionModuleExceptionInvalidAccessError
;
36 case media::MediaKeys::QUOTA_EXCEEDED_ERROR
:
37 return blink::WebContentDecryptionModuleExceptionQuotaExceededError
;
38 case media::MediaKeys::UNKNOWN_ERROR
:
39 return blink::WebContentDecryptionModuleExceptionUnknownError
;
40 case media::MediaKeys::CLIENT_ERROR
:
41 return blink::WebContentDecryptionModuleExceptionClientError
;
42 case media::MediaKeys::OUTPUT_ERROR
:
43 return blink::WebContentDecryptionModuleExceptionOutputError
;
46 return blink::WebContentDecryptionModuleExceptionUnknownError
;
50 WebContentDecryptionModuleSessionImpl::WebContentDecryptionModuleSessionImpl(
51 const scoped_refptr
<CdmSessionAdapter
>& adapter
)
54 next_available_result_index_(1),
55 weak_ptr_factory_(this) {
58 WebContentDecryptionModuleSessionImpl::
59 ~WebContentDecryptionModuleSessionImpl() {
60 if (!web_session_id_
.empty())
61 adapter_
->RemoveSession(web_session_id_
);
63 // Release any WebContentDecryptionModuleResult objects that are left. Their
64 // index will have been passed down via a CdmPromise, but it uses a WeakPtr.
65 DLOG_IF(WARNING
, outstanding_results_
.size() > 0)
66 << "Clearing " << outstanding_results_
.size() << " results";
67 for (ResultMap::iterator it
= outstanding_results_
.begin();
68 it
!= outstanding_results_
.end();
70 it
->second
.completeWithError(
71 blink::WebContentDecryptionModuleExceptionInvalidStateError
,
73 "Outstanding request being cancelled.");
75 outstanding_results_
.clear();
78 void WebContentDecryptionModuleSessionImpl::setClientInterface(Client
* client
) {
82 blink::WebString
WebContentDecryptionModuleSessionImpl::sessionId() const {
83 return blink::WebString::fromUTF8(web_session_id_
);
86 void WebContentDecryptionModuleSessionImpl::initializeNewSession(
87 const blink::WebString
& init_data_type
,
88 const uint8
* init_data
,
89 size_t init_data_length
) {
90 DCHECK(base::IsStringASCII(init_data_type
));
92 std::string init_data_type_as_ascii
= base::UTF16ToASCII(init_data_type
);
93 DLOG_IF(WARNING
, init_data_type_as_ascii
.find('/') != std::string::npos
)
94 << "init_data_type '" << init_data_type_as_ascii
95 << "' may be a MIME type";
97 // Attempt to translate content types.
98 // TODO(sandersd): Remove once tests stop using content types.
99 // http://crbug.com/385874
100 std::string content_type
= base::StringToLowerASCII(init_data_type_as_ascii
);
101 if (content_type
== "audio/mp4" || content_type
== "video/mp4") {
102 init_data_type_as_ascii
= "cenc";
103 } else if (content_type
== "audio/webm" || content_type
== "video/webm") {
104 init_data_type_as_ascii
= "webm";
107 scoped_ptr
<media::NewSessionCdmPromise
> promise(
108 new media::NewSessionCdmPromise(
109 base::Bind(&WebContentDecryptionModuleSessionImpl::SessionCreated
,
110 weak_ptr_factory_
.GetWeakPtr(),
112 base::Bind(&WebContentDecryptionModuleSessionImpl::OnSessionError
,
113 weak_ptr_factory_
.GetWeakPtr()),
114 adapter_
->GetKeySystemUMAPrefix() + kCreateSessionUMAName
));
115 adapter_
->InitializeNewSession(init_data_type_as_ascii
,
118 media::MediaKeys::TEMPORARY_SESSION
,
122 void WebContentDecryptionModuleSessionImpl::update(const uint8
* response
,
123 size_t response_length
) {
125 scoped_ptr
<media::SimpleCdmPromise
> promise(new media::SimpleCdmPromise(
126 base::Bind(&WebContentDecryptionModuleSessionImpl::OnSessionReady
,
127 weak_ptr_factory_
.GetWeakPtr()),
128 base::Bind(&WebContentDecryptionModuleSessionImpl::OnSessionError
,
129 weak_ptr_factory_
.GetWeakPtr())));
130 adapter_
->UpdateSession(
131 web_session_id_
, response
, response_length
, promise
.Pass());
134 void WebContentDecryptionModuleSessionImpl::release() {
135 scoped_ptr
<media::SimpleCdmPromise
> promise(new media::SimpleCdmPromise(
136 base::Bind(&WebContentDecryptionModuleSessionImpl::OnSessionClosed
,
137 weak_ptr_factory_
.GetWeakPtr()),
138 base::Bind(&WebContentDecryptionModuleSessionImpl::OnSessionError
,
139 weak_ptr_factory_
.GetWeakPtr())));
140 adapter_
->ReleaseSession(web_session_id_
, promise
.Pass());
143 void WebContentDecryptionModuleSessionImpl::initializeNewSession(
144 const blink::WebString
& init_data_type
,
145 const uint8
* init_data
,
146 size_t init_data_length
,
147 const blink::WebString
& session_type
,
148 blink::WebContentDecryptionModuleResult result
) {
149 uint32 result_index
= AddResult(result
);
151 // TODO(ddorwin): Guard against this in supported types check and remove this.
152 // Chromium only supports ASCII MIME types.
153 if (!base::IsStringASCII(init_data_type
)) {
155 SessionError(result_index
,
156 media::MediaKeys::NOT_SUPPORTED_ERROR
,
158 "The initialization data type " + init_data_type
.utf8() +
159 " is not supported by the key system.");
163 std::string init_data_type_as_ascii
= base::UTF16ToASCII(init_data_type
);
164 DLOG_IF(WARNING
, init_data_type_as_ascii
.find('/') != std::string::npos
)
165 << "init_data_type '" << init_data_type_as_ascii
166 << "' may be a MIME type";
168 scoped_ptr
<media::NewSessionCdmPromise
> promise(
169 new media::NewSessionCdmPromise(
170 base::Bind(&WebContentDecryptionModuleSessionImpl::SessionCreated
,
171 weak_ptr_factory_
.GetWeakPtr(),
173 base::Bind(&WebContentDecryptionModuleSessionImpl::SessionError
,
174 weak_ptr_factory_
.GetWeakPtr(),
176 adapter_
->GetKeySystemUMAPrefix() + kCreateSessionUMAName
));
177 adapter_
->InitializeNewSession(init_data_type_as_ascii
,
180 media::MediaKeys::TEMPORARY_SESSION
,
184 void WebContentDecryptionModuleSessionImpl::update(
185 const uint8
* response
,
186 size_t response_length
,
187 blink::WebContentDecryptionModuleResult result
) {
189 uint32 result_index
= AddResult(result
);
190 scoped_ptr
<media::SimpleCdmPromise
> promise(new media::SimpleCdmPromise(
192 &WebContentDecryptionModuleSessionImpl::SessionUpdatedOrReleased
,
193 weak_ptr_factory_
.GetWeakPtr(),
195 base::Bind(&WebContentDecryptionModuleSessionImpl::SessionError
,
196 weak_ptr_factory_
.GetWeakPtr(),
198 adapter_
->UpdateSession(
199 web_session_id_
, response
, response_length
, promise
.Pass());
202 void WebContentDecryptionModuleSessionImpl::release(
203 blink::WebContentDecryptionModuleResult result
) {
204 uint32 result_index
= AddResult(result
);
205 scoped_ptr
<media::SimpleCdmPromise
> promise(new media::SimpleCdmPromise(
207 &WebContentDecryptionModuleSessionImpl::SessionUpdatedOrReleased
,
208 weak_ptr_factory_
.GetWeakPtr(),
210 base::Bind(&WebContentDecryptionModuleSessionImpl::SessionError
,
211 weak_ptr_factory_
.GetWeakPtr(),
213 adapter_
->ReleaseSession(web_session_id_
, promise
.Pass());
216 void WebContentDecryptionModuleSessionImpl::OnSessionMessage(
217 const std::vector
<uint8
>& message
,
218 const GURL
& destination_url
) {
219 DCHECK(client_
) << "Client not set before message event";
221 message
.empty() ? NULL
: &message
[0], message
.size(), destination_url
);
224 void WebContentDecryptionModuleSessionImpl::OnSessionReady() {
228 void WebContentDecryptionModuleSessionImpl::OnSessionClosed() {
235 void WebContentDecryptionModuleSessionImpl::OnSessionError(
236 media::MediaKeys::Exception exception_code
,
238 const std::string
& error_message
) {
239 // Convert |exception_code| back to MediaKeyErrorCode if possible.
240 // TODO(jrummell): Update this conversion when promises flow
241 // back into blink:: (as blink:: will have its own error definition).
242 switch (exception_code
) {
243 case media::MediaKeys::CLIENT_ERROR
:
244 client_
->error(Client::MediaKeyErrorCodeClient
, system_code
);
247 // This will include all other CDM4 errors and any error generated
249 client_
->error(Client::MediaKeyErrorCodeUnknown
, system_code
);
254 void WebContentDecryptionModuleSessionImpl::SessionCreated(
256 const std::string
& web_session_id
) {
257 blink::WebContentDecryptionModuleResult::SessionStatus status
;
259 // CDM will return NULL if the session to be loaded can't be found.
260 if (web_session_id
.empty()) {
261 status
= blink::WebContentDecryptionModuleResult::SessionNotFound
;
263 DCHECK(web_session_id_
.empty())
264 << "Session ID may not be changed once set.";
265 web_session_id_
= web_session_id
;
267 adapter_
->RegisterSession(web_session_id_
,
268 weak_ptr_factory_
.GetWeakPtr())
269 ? blink::WebContentDecryptionModuleResult::NewSession
270 : blink::WebContentDecryptionModuleResult::SessionAlreadyExists
;
273 ResultMap::iterator it
= outstanding_results_
.find(result_index
);
274 if (it
!= outstanding_results_
.end()) {
275 blink::WebContentDecryptionModuleResult
& result
= it
->second
;
276 result
.completeWithSession(status
);
277 outstanding_results_
.erase(result_index
);
281 void WebContentDecryptionModuleSessionImpl::SessionUpdatedOrReleased(
282 uint32 result_index
) {
283 ResultMap::iterator it
= outstanding_results_
.find(result_index
);
284 DCHECK(it
!= outstanding_results_
.end());
285 blink::WebContentDecryptionModuleResult
& result
= it
->second
;
287 outstanding_results_
.erase(it
);
290 void WebContentDecryptionModuleSessionImpl::SessionError(
292 media::MediaKeys::Exception exception_code
,
294 const std::string
& error_message
) {
295 ResultMap::iterator it
= outstanding_results_
.find(result_index
);
296 DCHECK(it
!= outstanding_results_
.end());
297 blink::WebContentDecryptionModuleResult
& result
= it
->second
;
298 result
.completeWithError(ConvertException(exception_code
),
300 blink::WebString::fromUTF8(error_message
));
301 outstanding_results_
.erase(it
);
304 uint32
WebContentDecryptionModuleSessionImpl::AddResult(
305 blink::WebContentDecryptionModuleResult result
) {
306 uint32 result_index
= next_available_result_index_
++;
307 DCHECK(result_index
!= kReservedIndex
);
308 outstanding_results_
.insert(std::make_pair(result_index
, result
));
312 } // namespace content