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/webcontentdecryptionmodule_impl.h"
6
7#include <map>
8#include <vector>
9
10#include "base/basictypes.h"
11#include "base/bind.h"
12#include "base/callback_helpers.h"
13#include "base/logging.h"
14#include "base/memory/weak_ptr.h"
15#include "base/strings/string_util.h"
16#include "content/renderer/media/crypto/content_decryption_module_factory.h"
17#include "content/renderer/media/webcontentdecryptionmodulesession_impl.h"
18#include "media/base/media_keys.h"
19
20namespace content {
21
22// Forwards the session ID-based callbacks of the MediaKeys interface to the
23// appropriate session object.
24class SessionIdAdapter {
25 public:
26  SessionIdAdapter();
27  ~SessionIdAdapter();
28
29  // On success, creates a MediaKeys, returns it in |media_keys|, returns true.
30  bool Initialize(const std::string& key_system,
31                  scoped_ptr<media::MediaKeys>* media_keys);
32
33  // Adds a session to the internal map. Does not take ownership of the session.
34  void AddSession(const std::string& session_id,
35                  WebContentDecryptionModuleSessionImpl* session);
36
37  // Removes a session from the internal map.
38  void RemoveSession(const std::string& session_id);
39
40 private:
41  typedef std::map<std::string, WebContentDecryptionModuleSessionImpl*>
42      SessionMap;
43
44  // Callbacks for firing key events.
45  void KeyAdded(const std::string& session_id);
46  void KeyError(const std::string& session_id,
47                media::MediaKeys::KeyError error_code,
48                int system_code);
49  void KeyMessage(const std::string& session_id,
50                  const std::vector<uint8>& message,
51                  const std::string& destination_url);
52
53  // Helper function of the callbacks.
54  WebContentDecryptionModuleSessionImpl* GetSession(
55      const std::string& session_id);
56
57  base::WeakPtrFactory<SessionIdAdapter> weak_ptr_factory_;
58
59  SessionMap sessions_;
60
61  DISALLOW_COPY_AND_ASSIGN(SessionIdAdapter);
62};
63
64SessionIdAdapter::SessionIdAdapter()
65    : weak_ptr_factory_(this) {
66}
67
68SessionIdAdapter::~SessionIdAdapter() {
69}
70
71bool SessionIdAdapter::Initialize(const std::string& key_system,
72                                  scoped_ptr<media::MediaKeys>* media_keys) {
73  DCHECK(media_keys);
74  DCHECK(!*media_keys);
75
76  base::WeakPtr<SessionIdAdapter> weak_this = weak_ptr_factory_.GetWeakPtr();
77  scoped_ptr<media::MediaKeys> created_media_keys =
78      ContentDecryptionModuleFactory::Create(
79          // TODO(ddorwin): Address lower in the stack: http://crbug.com/252065
80          "webkit-" + key_system,
81#if defined(ENABLE_PEPPER_CDMS)
82          // TODO(ddorwin): Support Pepper-based CDMs: http://crbug.com/250049
83          NULL,
84          NULL,
85          base::Closure(),
86#elif defined(OS_ANDROID)
87          // TODO(xhwang): Support Android.
88          NULL,
89          0,
90#endif  // defined(ENABLE_PEPPER_CDMS)
91          base::Bind(&SessionIdAdapter::KeyAdded, weak_this),
92          base::Bind(&SessionIdAdapter::KeyError, weak_this),
93          base::Bind(&SessionIdAdapter::KeyMessage, weak_this));
94  if (!created_media_keys)
95    return false;
96
97  *media_keys = created_media_keys.Pass();
98  return true;
99}
100
101void SessionIdAdapter::AddSession(
102    const std::string& session_id,
103    WebContentDecryptionModuleSessionImpl* session) {
104  DCHECK(sessions_.find(session_id) == sessions_.end());
105  sessions_[session_id] = session;
106}
107
108void SessionIdAdapter::RemoveSession(const std::string& session_id) {
109  DCHECK(sessions_.find(session_id) != sessions_.end());
110  sessions_.erase(session_id);
111}
112
113void SessionIdAdapter::KeyAdded(const std::string& session_id) {
114  GetSession(session_id)->KeyAdded();
115}
116
117void SessionIdAdapter::KeyError(const std::string& session_id,
118                                media::MediaKeys::KeyError error_code,
119                                int system_code) {
120  GetSession(session_id)->KeyError(error_code, system_code);
121}
122
123void SessionIdAdapter::KeyMessage(const std::string& session_id,
124                                  const std::vector<uint8>& message,
125                                  const std::string& destination_url) {
126  GetSession(session_id)->KeyMessage(message, destination_url);
127}
128
129WebContentDecryptionModuleSessionImpl* SessionIdAdapter::GetSession(
130    const std::string& session_id) {
131  // TODO(ddorwin): Map session IDs correctly. For now, we only support one.
132  std::string session_object_id = "";
133  WebContentDecryptionModuleSessionImpl* session = sessions_[session_object_id];
134  DCHECK(session); // It must have been present.
135  return session;
136}
137
138//------------------------------------------------------------------------------
139
140WebContentDecryptionModuleImpl*
141WebContentDecryptionModuleImpl::Create(const string16& key_system) {
142  // TODO(ddorwin): Guard against this in supported types check and remove this.
143  // Chromium only supports ASCII key systems.
144  if (!IsStringASCII(key_system)) {
145    NOTREACHED();
146    return NULL;
147  }
148
149  // SessionIdAdapter creates the MediaKeys so it can provide its callbacks to
150  // during creation of the MediaKeys.
151  scoped_ptr<media::MediaKeys> media_keys;
152  scoped_ptr<SessionIdAdapter> adapter(new SessionIdAdapter());
153  if (!adapter->Initialize(UTF16ToASCII(key_system), &media_keys))
154    return NULL;
155
156  return new WebContentDecryptionModuleImpl(media_keys.Pass(), adapter.Pass());
157}
158
159WebContentDecryptionModuleImpl::WebContentDecryptionModuleImpl(
160    scoped_ptr<media::MediaKeys> media_keys,
161    scoped_ptr<SessionIdAdapter> adapter)
162    : media_keys_(media_keys.Pass()),
163      adapter_(adapter.Pass()) {
164}
165
166WebContentDecryptionModuleImpl::~WebContentDecryptionModuleImpl() {
167}
168
169// The caller owns the created session.
170WebKit::WebContentDecryptionModuleSession*
171WebContentDecryptionModuleImpl::createSession(
172    WebKit::WebContentDecryptionModuleSession::Client* client) {
173  DCHECK(media_keys_);
174  WebContentDecryptionModuleSessionImpl* session =
175      new WebContentDecryptionModuleSessionImpl(
176          media_keys_.get(),
177          client,
178          base::Bind(&WebContentDecryptionModuleImpl::OnSessionClosed,
179                     base::Unretained(this)));
180
181  // TODO(ddorwin): session_id is not populated yet!
182  adapter_->AddSession(session->session_id(), session);
183  return session;
184}
185
186void WebContentDecryptionModuleImpl::OnSessionClosed(
187    const std::string& session_id) {
188  adapter_->RemoveSession(session_id);
189}
190
191}  // namespace content
192