Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(325)

Side by Side Diff: webkit/media/crypto/ppapi/cdm_wrapper.cc

Issue 10876014: Hook up CDM calls in CdmWrapper. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Resolve comments and Rebase. Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <cstring> // For std::memcpy. 5 #include <cstring> // For memcpy.
6 #include <vector>
6 7
7 #include "base/compiler_specific.h" // For OVERRIDE. 8 #include "base/compiler_specific.h" // For OVERRIDE.
8 #include "ppapi/c/pp_errors.h" 9 #include "ppapi/c/pp_errors.h"
9 #include "ppapi/c/pp_stdint.h" 10 #include "ppapi/c/pp_stdint.h"
10 #include "ppapi/c/private/pp_content_decryptor.h" 11 #include "ppapi/c/private/pp_content_decryptor.h"
11 #include "ppapi/cpp/completion_callback.h" 12 #include "ppapi/cpp/completion_callback.h"
12 #include "ppapi/cpp/core.h" 13 #include "ppapi/cpp/core.h"
13 #include "ppapi/cpp/instance.h" 14 #include "ppapi/cpp/instance.h"
14 #include "ppapi/cpp/logging.h" 15 #include "ppapi/cpp/logging.h"
15 #include "ppapi/cpp/module.h" 16 #include "ppapi/cpp/module.h"
16 #include "ppapi/cpp/pass_ref.h" 17 #include "ppapi/cpp/pass_ref.h"
17 #include "ppapi/cpp/resource.h" 18 #include "ppapi/cpp/resource.h"
18 #include "ppapi/cpp/var.h" 19 #include "ppapi/cpp/var.h"
19 #include "ppapi/cpp/var_array_buffer.h" 20 #include "ppapi/cpp/var_array_buffer.h"
20 #include "ppapi/cpp/dev/buffer_dev.h" 21 #include "ppapi/cpp/dev/buffer_dev.h"
21 #include "ppapi/cpp/private/content_decryptor_private.h" 22 #include "ppapi/cpp/private/content_decryptor_private.h"
22 #include "ppapi/utility/completion_callback_factory.h" 23 #include "ppapi/utility/completion_callback_factory.h"
24 #include "webkit/media/crypto/ppapi/content_decryption_module.h"
23 25
24 namespace { 26 namespace {
25 27
26 struct DecryptorMessage { 28 // This must be consistent with MediaKeyError defined in the spec:
27 DecryptorMessage() : media_error(0), system_code(0) {} 29 // http://goo.gl/rbdnR
28 std::string key_system; 30 // TODO(xhwang): Add PP_MediaKeyError enum to avoid later static_cast in
29 std::string session_id; 31 // PluginInstance.
30 std::string default_url; 32 enum MediaKeyError {
31 std::string message_data; 33 kUnknownError = 1,
32 int32_t media_error; 34 kClientError,
33 int32_t system_code; 35 kServiceError,
34 }; 36 kOutputError,
35 37 kHardwareChangeError,
36 struct DecryptedBlock { 38 kDomainError
37 DecryptedBlock() {
38 std::memset(reinterpret_cast<void*>(&decrypted_block_info),
39 0,
40 sizeof(decrypted_block_info));
41 }
42 std::string decrypted_data;
43 PP_DecryptedBlockInfo decrypted_block_info;
44 }; 39 };
45 40
46 bool IsMainThread() { 41 bool IsMainThread() {
47 return pp::Module::Get()->core()->IsMainThread(); 42 return pp::Module::Get()->core()->IsMainThread();
48 } 43 }
49 44
50 void CallOnMain(pp::CompletionCallback cb) { 45 void CallOnMain(pp::CompletionCallback cb) {
51 // TODO(tomfinegan): This is only necessary because PPAPI doesn't allow calls 46 // TODO(tomfinegan): This is only necessary because PPAPI doesn't allow calls
52 // off the main thread yet. Remove this once the change lands. 47 // off the main thread yet. Remove this once the change lands.
53 if (IsMainThread()) 48 if (IsMainThread())
54 cb.Run(PP_OK); 49 cb.Run(PP_OK);
55 else 50 else
56 pp::Module::Get()->core()->CallOnMainThread(0, cb, PP_OK); 51 pp::Module::Get()->core()->CallOnMainThread(0, cb, PP_OK);
57 } 52 }
58 53
59 } // namespace 54 } // namespace
60 55
56 namespace webkit_media {
61 57
62 // A wrapper class for abstracting away PPAPI interaction and threading for a 58 // A wrapper class for abstracting away PPAPI interaction and threading for a
63 // Content Decryption Module (CDM). 59 // Content Decryption Module (CDM).
64 class CDMWrapper : public pp::Instance, 60 class CdmWrapper : public pp::Instance,
65 public pp::ContentDecryptor_Private { 61 public pp::ContentDecryptor_Private {
66 public: 62 public:
67 CDMWrapper(PP_Instance instance, pp::Module* module); 63 CdmWrapper(PP_Instance instance, pp::Module* module);
68 virtual ~CDMWrapper() {} 64 virtual ~CdmWrapper();
65
66 virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) {
67 return true;
68 }
69 69
70 // PPP_ContentDecryptor_Private methods 70 // PPP_ContentDecryptor_Private methods
71 // Note: As per comments in PPP_ContentDecryptor_Private, these calls should
72 // return false if the call was not forwarded to the CDM and should return
73 // true otherwise. Once the call reaches the CDM, the call result/status
74 // should be reported through the PPB_ContentDecryptor_Private interface.
71 virtual bool GenerateKeyRequest(const std::string& key_system, 75 virtual bool GenerateKeyRequest(const std::string& key_system,
72 pp::VarArrayBuffer init_data) OVERRIDE; 76 pp::VarArrayBuffer init_data) OVERRIDE;
73 virtual bool AddKey(const std::string& session_id, 77 virtual bool AddKey(const std::string& session_id,
74 pp::VarArrayBuffer key, 78 pp::VarArrayBuffer key,
75 pp::VarArrayBuffer init_data) OVERRIDE; 79 pp::VarArrayBuffer init_data) OVERRIDE;
76 virtual bool CancelKeyRequest(const std::string& session_id) OVERRIDE; 80 virtual bool CancelKeyRequest(const std::string& session_id) OVERRIDE;
77 virtual bool Decrypt( 81 virtual bool Decrypt(
78 pp::Buffer_Dev encrypted_buffer, 82 pp::Buffer_Dev encrypted_buffer,
79 const PP_EncryptedBlockInfo& encrypted_block_info) OVERRIDE; 83 const PP_EncryptedBlockInfo& encrypted_block_info) OVERRIDE;
80
81 virtual bool DecryptAndDecode( 84 virtual bool DecryptAndDecode(
82 pp::Buffer_Dev encrypted_buffer, 85 pp::Buffer_Dev encrypted_buffer,
83 const PP_EncryptedBlockInfo& encrypted_block_info) OVERRIDE { 86 const PP_EncryptedBlockInfo& encrypted_block_info) OVERRIDE;
84 return false;
85 }
86
87 virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) {
88 return true;
89 }
90 87
91 private: 88 private:
92 PP_Resource StringToBufferResource(const std::string& str); 89 // Creates a PP_Resource containing a PPB_Buffer_Impl, copies |data| into the
90 // buffer resource, and returns it. Returns a an invalid PP_Resource with an
91 // ID of 0 on failure. Upon success, the returned Buffer resource has a
92 // reference count of 1.
93 PP_Resource MakeBufferResource(const uint8_t* data, uint32_t data_size);
93 94
94 // <code>PPB_ContentDecryptor_Private</code> dispatchers. These are passed to 95 // <code>PPB_ContentDecryptor_Private</code> dispatchers. These are passed to
95 // <code>callback_factory_</code> to ensure that calls into 96 // <code>callback_factory_</code> to ensure that calls into
96 // <code>PPP_ContentDecryptor_Private</code> are asynchronous. 97 // <code>PPP_ContentDecryptor_Private</code> are asynchronous.
97 void NeedKey(int32_t result, const DecryptorMessage& decryptor_message); 98 void KeyAdded(int32_t result, const std::string& session_id);
98 void KeyAdded(int32_t result, const DecryptorMessage& decryptor_message); 99 void KeyMessage(int32_t result, cdm::KeyMessage& key_message);
99 void KeyMessage(int32_t result, const DecryptorMessage& decryptor_message); 100 void KeyError(int32_t result, const std::string& session_id);
100 void KeyError(int32_t result, const DecryptorMessage& decryptor_message); 101 void DeliverBlock(int32_t result,
101 void DeliverBlock(int32_t result, const DecryptedBlock& decrypted_block); 102 const cdm::Status& status,
102 103 cdm::OutputBuffer& output_buffer,
103 pp::CompletionCallbackFactory<CDMWrapper> callback_factory_; 104 const PP_DecryptTrackingInfo& tracking_info);
105
106 pp::CompletionCallbackFactory<CdmWrapper> callback_factory_;
107 cdm::ContentDecryptionModule* cdm_;
108 std::string key_system_;
104 }; 109 };
105 110
106 CDMWrapper::CDMWrapper(PP_Instance instance, 111 CdmWrapper::CdmWrapper(PP_Instance instance, pp::Module* module)
107 pp::Module* module)
108 : pp::Instance(instance), 112 : pp::Instance(instance),
109 pp::ContentDecryptor_Private(this) { 113 pp::ContentDecryptor_Private(this),
114 cdm_(NULL) {
110 callback_factory_.Initialize(this); 115 callback_factory_.Initialize(this);
111 } 116 }
112 117
113 bool CDMWrapper::GenerateKeyRequest(const std::string& key_system, 118 CdmWrapper::~CdmWrapper() {
119 if (cdm_)
120 DestroyCdmInstance(cdm_);
121 }
122
123 bool CdmWrapper::GenerateKeyRequest(const std::string& key_system,
114 pp::VarArrayBuffer init_data) { 124 pp::VarArrayBuffer init_data) {
115 PP_DCHECK(!key_system.empty() && init_data.ByteLength()); 125 PP_DCHECK(!key_system.empty());
116 126
117 DecryptorMessage decryptor_message; 127 if (!cdm_) {
118 decryptor_message.key_system = key_system; 128 cdm_ = CreateCdmInstance();
119 decryptor_message.session_id = "0"; 129 if (!cdm_)
120 decryptor_message.default_url = "http://www.google.com"; 130 return false;
121 decryptor_message.message_data = "GenerateKeyRequest"; 131 }
122 132
123 CallOnMain(callback_factory_.NewCallback(&CDMWrapper::KeyMessage, 133 cdm::KeyMessage key_request;
124 decryptor_message)); 134 cdm::Status status = cdm_->GenerateKeyRequest(
125 return true; 135 reinterpret_cast<const uint8_t*>(init_data.Map()),
126 } 136 init_data.ByteLength(),
127 137 &key_request);
128 bool CDMWrapper::AddKey(const std::string& session_id, 138
139 if (status != cdm::kSuccess ||
140 !key_request.message ||
141 key_request.message_size == 0) {
142 CallOnMain(callback_factory_.NewCallback(&CdmWrapper::KeyError,
143 std::string()));
144 return true;
145 }
146
147 // TODO(xhwang): Remove unnecessary CallOnMain calls here and below once we
148 // only support out-of-process.
149 // If running out-of-process, PPB calls will always behave asynchronously
150 // since IPC is involved. In that case, if we are already on main thread,
151 // we don't need to use CallOnMain to help us call PPB call on main thread,
152 // or to help call PPB asynchronously.
153 key_system_ = key_system;
154 CallOnMain(callback_factory_.NewCallback(&CdmWrapper::KeyMessage,
155 key_request));
156
157 return true;
158 }
159
160 bool CdmWrapper::AddKey(const std::string& session_id,
129 pp::VarArrayBuffer key, 161 pp::VarArrayBuffer key,
130 pp::VarArrayBuffer init_data) { 162 pp::VarArrayBuffer init_data) {
131 const std::string key_string(reinterpret_cast<char*>(key.Map()), 163 const uint8_t* key_ptr = reinterpret_cast<const uint8_t*>(key.Map());
132 key.ByteLength()); 164 int key_size = key.ByteLength();
133 const std::string init_data_string(reinterpret_cast<char*>(init_data.Map()), 165 const uint8_t* init_data_ptr =
134 init_data.ByteLength()); 166 reinterpret_cast<const uint8_t*>(init_data.Map());
135 167 int init_data_size = init_data.ByteLength();
136 PP_DCHECK(!session_id.empty() && !key_string.empty()); 168
137 169 if (!key_ptr || key_size <= 0 || !init_data_ptr || init_data_size <= 0)
138 DecryptorMessage decryptor_message; 170 return false;
139 decryptor_message.key_system = "AddKey"; 171
140 decryptor_message.session_id = "0"; 172 PP_DCHECK(cdm_);
141 decryptor_message.default_url = "http://www.google.com"; 173 cdm::Status status = cdm_->AddKey(session_id.data(), session_id.size(),
142 decryptor_message.message_data = "AddKey"; 174 key_ptr, key_size,
143 CallOnMain(callback_factory_.NewCallback(&CDMWrapper::KeyAdded, 175 init_data_ptr, init_data_size);
144 decryptor_message)); 176
145 return true; 177 if (status != cdm::kSuccess) {
146 } 178 CallOnMain(callback_factory_.NewCallback(&CdmWrapper::KeyError,
147 179 session_id));
148 bool CDMWrapper::CancelKeyRequest(const std::string& session_id) { 180 return true;
149 // TODO(tomfinegan): cancel pending key request in CDM. 181 }
150 182
151 PP_DCHECK(!session_id.empty()); 183 CallOnMain(callback_factory_.NewCallback(&CdmWrapper::KeyAdded, session_id));
152 184 return true;
153 DecryptorMessage decryptor_message; 185 }
154 decryptor_message.key_system = "CancelKeyRequest"; 186
155 decryptor_message.session_id = "0"; 187 bool CdmWrapper::CancelKeyRequest(const std::string& session_id) {
156 decryptor_message.default_url = "http://www.google.com"; 188 PP_DCHECK(cdm_);
157 decryptor_message.message_data = "CancelKeyRequest"; 189
158 190 cdm::Status status = cdm_->CancelKeyRequest(session_id.data(),
159 CallOnMain(callback_factory_.NewCallback(&CDMWrapper::KeyMessage, 191 session_id.size());
160 decryptor_message)); 192
161 return true; 193 if (status != cdm::kSuccess) {
162 } 194 CallOnMain(callback_factory_.NewCallback(&CdmWrapper::KeyError,
163 195 session_id));
164 bool CDMWrapper::Decrypt(pp::Buffer_Dev encrypted_buffer, 196 return true;
197 }
198
199 return true;
200 }
201
202 bool CdmWrapper::Decrypt(pp::Buffer_Dev encrypted_buffer,
165 const PP_EncryptedBlockInfo& encrypted_block_info) { 203 const PP_EncryptedBlockInfo& encrypted_block_info) {
166 PP_DCHECK(!encrypted_buffer.is_null()); 204 PP_DCHECK(!encrypted_buffer.is_null());
167 205 PP_DCHECK(cdm_);
168 DecryptedBlock decrypted_block; 206
169 decrypted_block.decrypted_data = "Pretend I'm decrypted data!"; 207 // TODO(xhwang): Simplify the following data conversion.
170 decrypted_block.decrypted_block_info.result = PP_DECRYPTRESULT_SUCCESS; 208 cdm::InputBuffer input_buffer;
171 decrypted_block.decrypted_block_info.tracking_info = 209 input_buffer.data = reinterpret_cast<uint8_t*>(encrypted_buffer.data());
172 encrypted_block_info.tracking_info; 210 input_buffer.data_size = encrypted_buffer.size();
173 211 input_buffer.data_offset = encrypted_block_info.data_offset;
174 // TODO(tomfinegan): This would end up copying a lot of data in the real 212 input_buffer.key_id = encrypted_block_info.key_id;
175 // implementation if we continue passing std::strings around. It *might* not 213 input_buffer.key_id_size = encrypted_block_info.key_id_size;
176 // be such a big deal w/a real CDM. We may be able to simply pass a pointer 214 input_buffer.iv = encrypted_block_info.iv;
177 // into the CDM. Otherwise we could look into using std::tr1::shared_ptr 215 input_buffer.iv_size = encrypted_block_info.iv_size;
178 // instead of passing a giant std::string filled with encrypted data. 216 input_buffer.checksum = encrypted_block_info.checksum;
179 CallOnMain(callback_factory_.NewCallback(&CDMWrapper::DeliverBlock, 217 input_buffer.checksum_size = encrypted_block_info.checksum_size;
180 decrypted_block)); 218 input_buffer.num_subsamples = encrypted_block_info.num_subsamples;
181 return true; 219 std::vector<cdm::SubsampleEntry> subsamples;
182 } 220 for (uint32_t i = 0; i < encrypted_block_info.num_subsamples; ++i) {
183 221 subsamples.push_back(cdm::SubsampleEntry(
184 PP_Resource CDMWrapper::StringToBufferResource(const std::string& str) { 222 encrypted_block_info.subsamples[i].clear_bytes,
185 if (str.empty()) 223 encrypted_block_info.subsamples[i].cipher_bytes));
224 }
225 input_buffer.subsamples = &subsamples[0];
226 input_buffer.timestamp = encrypted_block_info.tracking_info.timestamp;
227
228 cdm::OutputBuffer output_buffer;
229 cdm::Status status = cdm_->Decrypt(input_buffer, &output_buffer);
230
231 CallOnMain(callback_factory_.NewCallback(
232 &CdmWrapper::DeliverBlock,
233 status,
234 output_buffer,
235 encrypted_block_info.tracking_info));
236
237 return true;
238 }
239
240 bool CdmWrapper::DecryptAndDecode(
241 pp::Buffer_Dev encrypted_buffer,
242 const PP_EncryptedBlockInfo& encrypted_block_info) {
243 return false;
244 }
245
246 PP_Resource CdmWrapper::MakeBufferResource(const uint8_t* data,
247 uint32_t data_size) {
248 if (!data || !data_size)
186 return 0; 249 return 0;
187 250
188 pp::Buffer_Dev buffer(this, str.size()); 251 pp::Buffer_Dev buffer(this, data_size);
189 if (!buffer.data()) 252 if (!buffer.data())
190 return 0; 253 return 0;
191 254
192 std::memcpy(buffer.data(), str.data(), str.size()); 255 memcpy(buffer.data(), data, data_size);
256
193 return buffer.detach(); 257 return buffer.detach();
194 } 258 }
195 259
196 void CDMWrapper::NeedKey(int32_t result, 260 void CdmWrapper::KeyAdded(int32_t result, const std::string& session_id) {
197 const DecryptorMessage& decryptor_message) { 261 pp::ContentDecryptor_Private::KeyAdded(key_system_, session_id);
198 const std::string& message_data = decryptor_message.message_data; 262 }
199 pp::VarArrayBuffer init_data(message_data.size()); 263
200 std::memcpy(init_data.Map(), message_data.data(), message_data.size()); 264 void CdmWrapper::KeyMessage(int32_t result,
201 pp::ContentDecryptor_Private::NeedKey(decryptor_message.key_system, 265 cdm::KeyMessage& key_message) {
202 decryptor_message.session_id, 266 pp::Buffer_Dev message_buffer(MakeBufferResource(key_message.message,
203 init_data); 267 key_message.message_size));
204 } 268 pp::ContentDecryptor_Private::KeyMessage(
205 269 key_system_,
206 void CDMWrapper::KeyAdded(int32_t result, 270 std::string(key_message.session_id, key_message.session_id_size),
207 const DecryptorMessage& decryptor_message) { 271 message_buffer,
208 pp::ContentDecryptor_Private::KeyAdded(decryptor_message.key_system, 272 std::string(key_message.default_url, key_message.default_url_size));
209 decryptor_message.session_id); 273
210 } 274 // TODO(xhwang): Fix this. This is not always safe as the memory is allocated
211 275 // in another shared object.
212 void CDMWrapper::KeyMessage(int32_t result, 276 delete [] key_message.session_id;
213 const DecryptorMessage& decryptor_message) { 277 key_message.session_id = NULL;
214 pp::Buffer_Dev message_buffer( 278 delete [] key_message.message;
215 StringToBufferResource(decryptor_message.message_data)); 279 key_message.message = NULL;
216 pp::ContentDecryptor_Private::KeyMessage(decryptor_message.key_system, 280 delete [] key_message.default_url;
217 decryptor_message.session_id, 281 key_message.default_url = NULL;
218 message_buffer, 282 }
219 decryptor_message.default_url); 283
220 } 284 // TODO(xhwang): Support MediaKeyError (see spec: http://goo.gl/rbdnR) in CDM
221 285 // interface and in this function.
222 void CDMWrapper::KeyError(int32_t result, 286 void CdmWrapper::KeyError(int32_t result, const std::string& session_id) {
223 const DecryptorMessage& decryptor_message) { 287 pp::ContentDecryptor_Private::KeyError(key_system_,
224 pp::ContentDecryptor_Private::KeyError(decryptor_message.key_system, 288 session_id,
225 decryptor_message.session_id, 289 kUnknownError,
226 decryptor_message.media_error, 290 0);
227 decryptor_message.system_code); 291 }
228 } 292
229 293 void CdmWrapper::DeliverBlock(int32_t result,
230 void CDMWrapper::DeliverBlock(int32_t result, 294 const cdm::Status& status,
231 const DecryptedBlock& decrypted_block) { 295 cdm::OutputBuffer& output_buffer,
232 pp::Buffer_Dev decrypted_buffer( 296 const PP_DecryptTrackingInfo& tracking_info) {
233 StringToBufferResource(decrypted_block.decrypted_data)); 297 pp::Buffer_Dev decrypted_buffer(MakeBufferResource(output_buffer.data,
234 pp::ContentDecryptor_Private::DeliverBlock( 298 output_buffer.data_size));
235 decrypted_buffer, 299
236 decrypted_block.decrypted_block_info); 300 PP_DecryptedBlockInfo decrypted_block_info;
301 decrypted_block_info.tracking_info.request_id = tracking_info.request_id;
302 decrypted_block_info.tracking_info.timestamp = output_buffer.timestamp;
303
304 switch (status) {
305 case cdm::kSuccess:
306 decrypted_block_info.result = PP_DECRYPTRESULT_SUCCESS;
307 break;
308 case cdm::kErrorNoKey:
309 decrypted_block_info.result = PP_DECRYPTRESULT_DECRYPT_NOKEY;
310 break;
311 default:
312 decrypted_block_info.result = PP_DECRYPTRESULT_DECRYPT_ERROR;
313 }
314
315 pp::ContentDecryptor_Private::DeliverBlock(decrypted_buffer,
316 decrypted_block_info);
317
318 // TODO(xhwang): Fix this. This is not always safe as the memory is allocated
319 // in another shared object.
320 delete [] output_buffer.data;
321 output_buffer.data = NULL;
237 } 322 }
238 323
239 // This object is the global object representing this plugin library as long 324 // This object is the global object representing this plugin library as long
240 // as it is loaded. 325 // as it is loaded.
241 class MyModule : public pp::Module { 326 class MyModule : public pp::Module {
242 public: 327 public:
243 MyModule() : pp::Module() {} 328 MyModule() : pp::Module() {}
244 virtual ~MyModule() {} 329 virtual ~MyModule() {}
245 330
246 virtual pp::Instance* CreateInstance(PP_Instance instance) { 331 virtual pp::Instance* CreateInstance(PP_Instance instance) {
247 return new CDMWrapper(instance, this); 332 return new CdmWrapper(instance, this);
248 } 333 }
249 }; 334 };
250 335
336 } // namespace webkit_media
337
251 namespace pp { 338 namespace pp {
252 339
253 // Factory function for your specialization of the Module object. 340 // Factory function for your specialization of the Module object.
254 Module* CreateModule() { 341 Module* CreateModule() {
255 return new MyModule(); 342 return new webkit_media::MyModule();
256 } 343 }
257 344
258 } // namespace pp 345 } // namespace pp
OLDNEW
« no previous file with comments | « webkit/media/crypto/key_systems.cc ('k') | webkit/media/crypto/ppapi/content_decryption_module.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698