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.
4
5#include "content/renderer/media/webcontentdecryptionmodulesession_impl.h"
6
7#include "base/bind.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_result_promise.h"
13#include "content/renderer/media/cdm_session_adapter.h"
14#include "media/base/cdm_promise.h"
15#include "third_party/WebKit/public/platform/WebURL.h"
16
17namespace content {
18
19const char kCreateSessionUMAName[] = "CreateSession";
20
21typedef base::Callback<blink::WebContentDecryptionModuleResult::SessionStatus(
22    const std::string& web_session_id)> SessionInitializedCB;
23
24class NewSessionCdmResultPromise : public CdmResultPromise<std::string> {
25 public:
26  NewSessionCdmResultPromise(blink::WebContentDecryptionModuleResult result,
27                             std::string uma_name,
28                             const SessionInitializedCB& new_session_created_cb)
29      : CdmResultPromise<std::string>(result, uma_name),
30        new_session_created_cb_(new_session_created_cb) {}
31
32 protected:
33  virtual void OnResolve(const std::string& web_session_id) OVERRIDE {
34    blink::WebContentDecryptionModuleResult::SessionStatus status =
35        new_session_created_cb_.Run(web_session_id);
36    web_cdm_result_.completeWithSession(status);
37  }
38
39 private:
40  SessionInitializedCB new_session_created_cb_;
41};
42
43WebContentDecryptionModuleSessionImpl::WebContentDecryptionModuleSessionImpl(
44    const scoped_refptr<CdmSessionAdapter>& adapter)
45    : adapter_(adapter),
46      is_closed_(false),
47      weak_ptr_factory_(this) {
48}
49
50WebContentDecryptionModuleSessionImpl::
51    ~WebContentDecryptionModuleSessionImpl() {
52  if (!web_session_id_.empty())
53    adapter_->UnregisterSession(web_session_id_);
54}
55
56void WebContentDecryptionModuleSessionImpl::setClientInterface(Client* client) {
57  client_ = client;
58}
59
60blink::WebString WebContentDecryptionModuleSessionImpl::sessionId() const {
61  return blink::WebString::fromUTF8(web_session_id_);
62}
63
64void WebContentDecryptionModuleSessionImpl::initializeNewSession(
65    const blink::WebString& init_data_type,
66    const uint8* init_data,
67    size_t init_data_length) {
68  // TODO(jrummell): Remove once blink updated.
69  NOTREACHED();
70}
71
72void WebContentDecryptionModuleSessionImpl::update(const uint8* response,
73                                                   size_t response_length) {
74  // TODO(jrummell): Remove once blink updated.
75  NOTREACHED();
76}
77
78void WebContentDecryptionModuleSessionImpl::release() {
79  // TODO(jrummell): Remove once blink updated.
80  NOTREACHED();
81}
82
83void WebContentDecryptionModuleSessionImpl::initializeNewSession(
84    const blink::WebString& init_data_type,
85    const uint8* init_data,
86    size_t init_data_length,
87    const blink::WebString& session_type,
88    blink::WebContentDecryptionModuleResult result) {
89
90  // TODO(ddorwin): Guard against this in supported types check and remove this.
91  // Chromium only supports ASCII MIME types.
92  if (!base::IsStringASCII(init_data_type)) {
93    NOTREACHED();
94    std::string message = "The initialization data type " +
95                          init_data_type.utf8() +
96                          " is not supported by the key system.";
97    result.completeWithError(
98        blink::WebContentDecryptionModuleExceptionNotSupportedError,
99        0,
100        blink::WebString::fromUTF8(message));
101    return;
102  }
103
104  std::string init_data_type_as_ascii = base::UTF16ToASCII(init_data_type);
105  DLOG_IF(WARNING, init_data_type_as_ascii.find('/') != std::string::npos)
106      << "init_data_type '" << init_data_type_as_ascii
107      << "' may be a MIME type";
108
109  adapter_->InitializeNewSession(
110      init_data_type_as_ascii,
111      init_data,
112      init_data_length,
113      media::MediaKeys::TEMPORARY_SESSION,
114      scoped_ptr<media::NewSessionCdmPromise>(new NewSessionCdmResultPromise(
115          result,
116          adapter_->GetKeySystemUMAPrefix() + kCreateSessionUMAName,
117          base::Bind(
118              &WebContentDecryptionModuleSessionImpl::OnSessionInitialized,
119              base::Unretained(this)))));
120}
121
122void WebContentDecryptionModuleSessionImpl::update(
123    const uint8* response,
124    size_t response_length,
125    blink::WebContentDecryptionModuleResult result) {
126  DCHECK(response);
127  DCHECK(!web_session_id_.empty());
128  adapter_->UpdateSession(
129      web_session_id_,
130      response,
131      response_length,
132      scoped_ptr<media::SimpleCdmPromise>(new SimpleCdmResultPromise(result)));
133}
134
135void WebContentDecryptionModuleSessionImpl::close(
136    blink::WebContentDecryptionModuleResult result) {
137  DCHECK(!web_session_id_.empty());
138  adapter_->CloseSession(
139      web_session_id_,
140      scoped_ptr<media::SimpleCdmPromise>(new SimpleCdmResultPromise(result)));
141}
142
143void WebContentDecryptionModuleSessionImpl::remove(
144    blink::WebContentDecryptionModuleResult result) {
145  DCHECK(!web_session_id_.empty());
146  adapter_->RemoveSession(
147      web_session_id_,
148      scoped_ptr<media::SimpleCdmPromise>(new SimpleCdmResultPromise(result)));
149}
150
151void WebContentDecryptionModuleSessionImpl::getUsableKeyIds(
152    blink::WebContentDecryptionModuleResult result) {
153  DCHECK(!web_session_id_.empty());
154  adapter_->GetUsableKeyIds(
155      web_session_id_,
156      scoped_ptr<media::KeyIdsPromise>(
157          new CdmResultPromise<media::KeyIdsVector>(result)));
158}
159
160void WebContentDecryptionModuleSessionImpl::release(
161    blink::WebContentDecryptionModuleResult result) {
162  close(result);
163}
164
165void WebContentDecryptionModuleSessionImpl::OnSessionMessage(
166    const std::vector<uint8>& message,
167    const GURL& destination_url) {
168  DCHECK(client_) << "Client not set before message event";
169  client_->message(
170      message.empty() ? NULL : &message[0], message.size(), destination_url);
171}
172
173void WebContentDecryptionModuleSessionImpl::OnSessionKeysChange(
174    bool has_additional_usable_key) {
175  // TODO(jrummell): Update this once Blink client supports this.
176}
177
178void WebContentDecryptionModuleSessionImpl::OnSessionExpirationUpdate(
179    const base::Time& new_expiry_time) {
180  // TODO(jrummell): Update this once Blink client supports this.
181  // The EME spec has expiration attribute as the time in milliseconds, so use
182  // InMillisecondsF() to convert.
183}
184
185void WebContentDecryptionModuleSessionImpl::OnSessionReady() {
186  client_->ready();
187}
188
189void WebContentDecryptionModuleSessionImpl::OnSessionClosed() {
190  if (!is_closed_) {
191    is_closed_ = true;
192    client_->close();
193  }
194}
195
196void WebContentDecryptionModuleSessionImpl::OnSessionError(
197    media::MediaKeys::Exception exception_code,
198    uint32 system_code,
199    const std::string& error_message) {
200  // Convert |exception_code| back to MediaKeyErrorCode if possible.
201  // TODO(jrummell): Update this conversion when promises flow
202  // back into blink:: (as blink:: will have its own error definition).
203  switch (exception_code) {
204    case media::MediaKeys::CLIENT_ERROR:
205      client_->error(Client::MediaKeyErrorCodeClient, system_code);
206      break;
207    default:
208      // This will include all other CDM4 errors and any error generated
209      // by CDM5 or later.
210      client_->error(Client::MediaKeyErrorCodeUnknown, system_code);
211      break;
212  }
213}
214
215blink::WebContentDecryptionModuleResult::SessionStatus
216WebContentDecryptionModuleSessionImpl::OnSessionInitialized(
217    const std::string& web_session_id) {
218  // CDM will return NULL if the session to be loaded can't be found.
219  if (web_session_id.empty())
220    return blink::WebContentDecryptionModuleResult::SessionNotFound;
221
222  DCHECK(web_session_id_.empty()) << "Session ID may not be changed once set.";
223  web_session_id_ = web_session_id;
224  return adapter_->RegisterSession(web_session_id_,
225                                   weak_ptr_factory_.GetWeakPtr())
226             ? blink::WebContentDecryptionModuleResult::NewSession
227             : blink::WebContentDecryptionModuleResult::SessionAlreadyExists;
228}
229
230}  // namespace content
231