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 "media/cdm/ppapi/cdm_file_io_impl.h"
10 #include "media/cdm/ppapi/cdm_logging.h"
11 #include "ppapi/c/pp_errors.h"
12 #include "ppapi/cpp/dev/url_util_dev.h"
16 const int kReadSize
= 4 * 1024; // Arbitrary choice.
18 // Call func_call and check the result. If the result is not
19 // PP_OK_COMPLETIONPENDING, print out logs, call OnError() and return.
20 #define CHECK_PP_OK_COMPLETIONPENDING(func_call, error_type) \
22 int32_t result = func_call; \
23 PP_DCHECK(result != PP_OK); \
24 if (result != PP_OK_COMPLETIONPENDING) { \
25 CDM_DLOG() << #func_call << " failed with result: " << result; \
26 OnError(error_type); \
32 // PPAPI calls should only be made on the main thread. In this file, main thread
33 // checking is only performed in public APIs and the completion callbacks. This
34 // ensures all functions are running on the main thread since internal methods
35 // are called either by the public APIs or by the completion callbacks.
36 static bool IsMainThread() {
37 return pp::Module::Get()->core()->IsMainThread();
39 #endif // !defined(NDEBUG)
41 // Posts a task to run |cb| on the main thread. The task is posted even if the
42 // current thread is the main thread.
43 static void PostOnMain(pp::CompletionCallback cb
) {
44 pp::Module::Get()->core()->CallOnMainThread(0, cb
, PP_OK
);
47 CdmFileIOImpl::FileLockMap
* CdmFileIOImpl::file_lock_map_
= NULL
;
49 CdmFileIOImpl::ResourceTracker::ResourceTracker() {
50 // Do nothing here since we lazy-initialize CdmFileIOImpl::file_lock_map_
51 // in CdmFileIOImpl::AcquireFileLock().
54 CdmFileIOImpl::ResourceTracker::~ResourceTracker() {
55 delete CdmFileIOImpl::file_lock_map_
;
58 CdmFileIOImpl::CdmFileIOImpl(cdm::FileIOClient
* client
, PP_Instance pp_instance
)
59 : state_(FILE_UNOPENED
),
61 pp_instance_handle_(pp_instance
),
62 callback_factory_(this),
64 PP_DCHECK(IsMainThread());
65 PP_DCHECK(pp_instance
); // 0 indicates a "NULL handle".
68 CdmFileIOImpl::~CdmFileIOImpl() {
69 PP_DCHECK(state_
== FILE_CLOSED
);
72 // Call sequence: Open() -> OpenFileSystem() -> OpenFile() -> FILE_OPENED.
73 void CdmFileIOImpl::Open(const char* file_name
, uint32_t file_name_size
) {
74 CDM_DLOG() << __FUNCTION__
;
75 PP_DCHECK(IsMainThread());
77 if (state_
!= FILE_UNOPENED
) {
78 CDM_DLOG() << "Open() called in an invalid state.";
83 // File name should not contain any path separators.
84 std::string
file_name_str(file_name
, file_name_size
);
85 if (file_name_str
.find('/') != std::string::npos
||
86 file_name_str
.find('\\') != std::string::npos
) {
87 CDM_DLOG() << "Invalid file name.";
92 // pp::FileRef only accepts path that begins with a '/' character.
93 file_name_
= '/' + file_name_str
;
95 if (!AcquireFileLock()) {
96 CDM_DLOG() << "File is in use by other cdm::FileIO objects.";
97 OnError(OPEN_WHILE_IN_USE
);
101 state_
= OPENING_FILE_SYSTEM
;
107 // Read() -> ReadFile() -> OnFileRead() ----------> Done.
111 void CdmFileIOImpl::Read() {
112 CDM_DLOG() << __FUNCTION__
;
113 PP_DCHECK(IsMainThread());
115 if (state_
== READING_FILE
|| state_
== WRITING_FILE
) {
116 CDM_DLOG() << "Read() called during pending read/write.";
117 OnError(READ_WHILE_IN_USE
);
121 if (state_
!= FILE_OPENED
) {
122 CDM_DLOG() << "Read() called in an invalid state.";
127 PP_DCHECK(io_buffer_
.empty());
128 PP_DCHECK(cumulative_read_buffer_
.empty());
130 io_buffer_
.resize(kReadSize
);
133 state_
= READING_FILE
;
139 // Write() -> WriteFile() -> OnFileWritten() ----------> Done.
142 // |------------------|
143 void CdmFileIOImpl::Write(const uint8_t* data
, uint32_t data_size
) {
144 CDM_DLOG() << __FUNCTION__
;
145 PP_DCHECK(IsMainThread());
147 if (state_
== READING_FILE
|| state_
== WRITING_FILE
) {
148 CDM_DLOG() << "Write() called during pending read/write.";
149 OnError(WRITE_WHILE_IN_USE
);
153 if (state_
!= FILE_OPENED
) {
154 CDM_DLOG() << "Write() called in an invalid state.";
155 OnError(WRITE_ERROR
);
159 PP_DCHECK(io_offset_
== 0);
160 PP_DCHECK(io_buffer_
.empty());
162 io_buffer_
.assign(data
, data
+ data_size
);
166 state_
= WRITING_FILE
;
168 // Always SetLength() in case |data_size| is less than the file size.
169 SetLength(data_size
);
172 void CdmFileIOImpl::Close() {
173 CDM_DLOG() << __FUNCTION__
;
174 PP_DCHECK(IsMainThread());
175 PP_DCHECK(state_
!= FILE_CLOSED
);
178 // All pending callbacks are canceled since |callback_factory_| is destroyed.
182 bool CdmFileIOImpl::SetFileID() {
183 PP_DCHECK(file_id_
.empty());
184 PP_DCHECK(!file_name_
.empty() && file_name_
[0] == '/');
186 // Not taking ownership of |url_util_dev| (which is a singleton).
187 const pp::URLUtil_Dev
* url_util_dev
= pp::URLUtil_Dev::Get();
188 PP_URLComponents_Dev components
;
190 url_util_dev
->GetDocumentURL(pp_instance_handle_
, &components
);
191 if (!url_var
.is_string())
193 std::string url
= url_var
.AsString();
195 file_id_
.append(url
, components
.scheme
.begin
, components
.scheme
.len
);
197 file_id_
.append(url
, components
.host
.begin
, components
.host
.len
);
199 file_id_
.append(url
, components
.port
.begin
, components
.port
.len
);
200 file_id_
+= file_name_
;
205 bool CdmFileIOImpl::AcquireFileLock() {
206 PP_DCHECK(IsMainThread());
208 if (file_id_
.empty() && !SetFileID())
211 if (!file_lock_map_
) {
212 file_lock_map_
= new FileLockMap();
214 FileLockMap::iterator found
= file_lock_map_
->find(file_id_
);
215 if (found
!= file_lock_map_
->end() && found
->second
)
219 (*file_lock_map_
)[file_id_
] = true;
223 void CdmFileIOImpl::ReleaseFileLock() {
224 PP_DCHECK(IsMainThread());
229 FileLockMap::iterator found
= file_lock_map_
->find(file_id_
);
230 if (found
!= file_lock_map_
->end() && found
->second
)
231 found
->second
= false;
234 void CdmFileIOImpl::OpenFileSystem() {
235 PP_DCHECK(state_
== OPENING_FILE_SYSTEM
);
237 pp::CompletionCallbackWithOutput
<pp::FileSystem
> cb
=
238 callback_factory_
.NewCallbackWithOutput(
239 &CdmFileIOImpl::OnFileSystemOpened
);
240 isolated_file_system_
= pp::IsolatedFileSystemPrivate(
241 pp_instance_handle_
, PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_PLUGINPRIVATE
);
243 CHECK_PP_OK_COMPLETIONPENDING(isolated_file_system_
.Open(cb
), OPEN_ERROR
);
246 void CdmFileIOImpl::OnFileSystemOpened(int32_t result
,
247 pp::FileSystem file_system
) {
248 PP_DCHECK(IsMainThread());
249 PP_DCHECK(state_
== OPENING_FILE_SYSTEM
);
251 if (result
!= PP_OK
) {
252 CDM_DLOG() << "File system open failed asynchronously.";
258 file_system_
= file_system
;
259 state_
= OPENING_FILE
;
263 void CdmFileIOImpl::OpenFile() {
264 PP_DCHECK(state_
== OPENING_FILE
);
266 file_io_
= pp::FileIO(pp_instance_handle_
);
267 file_ref_
= pp::FileRef(file_system_
, file_name_
.c_str());
268 int32_t file_open_flag
= PP_FILEOPENFLAG_READ
|
269 PP_FILEOPENFLAG_WRITE
|
270 PP_FILEOPENFLAG_CREATE
;
271 pp::CompletionCallback cb
=
272 callback_factory_
.NewCallback(&CdmFileIOImpl::OnFileOpened
);
273 CHECK_PP_OK_COMPLETIONPENDING(file_io_
.Open(file_ref_
, file_open_flag
, cb
),
277 void CdmFileIOImpl::OnFileOpened(int32_t result
) {
278 PP_DCHECK(IsMainThread());
279 PP_DCHECK(state_
== OPENING_FILE
);
281 if (result
!= PP_OK
) {
282 CDM_DLOG() << "File open failed.";
288 state_
= FILE_OPENED
;
289 client_
->OnOpenComplete(cdm::FileIOClient::kSuccess
);
292 void CdmFileIOImpl::ReadFile() {
293 PP_DCHECK(state_
== READING_FILE
);
294 PP_DCHECK(!io_buffer_
.empty());
296 pp::CompletionCallback cb
=
297 callback_factory_
.NewCallback(&CdmFileIOImpl::OnFileRead
);
298 CHECK_PP_OK_COMPLETIONPENDING(
299 file_io_
.Read(io_offset_
, &io_buffer_
[0], io_buffer_
.size(), cb
),
303 void CdmFileIOImpl::OnFileRead(int32_t bytes_read
) {
304 CDM_DLOG() << __FUNCTION__
<< ": " << bytes_read
;
305 PP_DCHECK(IsMainThread());
306 PP_DCHECK(state_
== READING_FILE
);
308 // 0 |bytes_read| indicates end-of-file reached.
309 if (bytes_read
< PP_OK
) {
310 CDM_DLOG() << "Read file failed.";
315 PP_DCHECK(static_cast<size_t>(bytes_read
) <= io_buffer_
.size());
316 // Append |bytes_read| in |io_buffer_| to |cumulative_read_buffer_|.
317 cumulative_read_buffer_
.insert(cumulative_read_buffer_
.end(),
319 io_buffer_
.begin() + bytes_read
);
320 io_offset_
+= bytes_read
;
322 // Not received end-of-file yet.
323 if (bytes_read
> 0) {
328 // We hit end-of-file. Return read data to the client.
331 // Clear |cumulative_read_buffer_| in case OnReadComplete() calls Read() or
333 std::vector
<char> local_buffer
;
334 std::swap(cumulative_read_buffer_
, local_buffer
);
336 state_
= FILE_OPENED
;
337 const uint8_t* data
= local_buffer
.empty() ?
338 NULL
: reinterpret_cast<const uint8_t*>(&local_buffer
[0]);
339 client_
->OnReadComplete(
340 cdm::FileIOClient::kSuccess
, data
, local_buffer
.size());
343 void CdmFileIOImpl::SetLength(uint32_t length
) {
344 PP_DCHECK(state_
== WRITING_FILE
);
346 pp::CompletionCallback cb
=
347 callback_factory_
.NewCallback(&CdmFileIOImpl::OnLengthSet
);
348 CHECK_PP_OK_COMPLETIONPENDING(file_io_
.SetLength(length
, cb
), WRITE_ERROR
);
351 void CdmFileIOImpl::OnLengthSet(int32_t result
) {
352 CDM_DLOG() << __FUNCTION__
<< ": " << result
;
353 PP_DCHECK(IsMainThread());
354 PP_DCHECK(state_
== WRITING_FILE
);
356 if (result
!= PP_OK
) {
357 CDM_DLOG() << "File SetLength failed.";
358 OnError(WRITE_ERROR
);
362 if (io_buffer_
.empty()) {
363 state_
= FILE_OPENED
;
364 client_
->OnWriteComplete(cdm::FileIOClient::kSuccess
);
371 void CdmFileIOImpl::WriteFile() {
372 PP_DCHECK(state_
== WRITING_FILE
);
373 PP_DCHECK(io_offset_
< io_buffer_
.size());
375 pp::CompletionCallback cb
=
376 callback_factory_
.NewCallback(&CdmFileIOImpl::OnFileWritten
);
377 CHECK_PP_OK_COMPLETIONPENDING(file_io_
.Write(io_offset_
,
378 &io_buffer_
[io_offset_
],
379 io_buffer_
.size() - io_offset_
,
384 void CdmFileIOImpl::OnFileWritten(int32_t bytes_written
) {
385 CDM_DLOG() << __FUNCTION__
<< ": " << bytes_written
;
386 PP_DCHECK(IsMainThread());
387 PP_DCHECK(state_
== WRITING_FILE
);
389 if (bytes_written
<= PP_OK
) {
390 CDM_DLOG() << "Write file failed.";
395 io_offset_
+= bytes_written
;
396 PP_DCHECK(io_offset_
<= io_buffer_
.size());
398 if (io_offset_
< io_buffer_
.size()) {
405 state_
= FILE_OPENED
;
406 client_
->OnWriteComplete(cdm::FileIOClient::kSuccess
);
409 void CdmFileIOImpl::CloseFile() {
410 PP_DCHECK(IsMainThread());
412 state_
= FILE_CLOSED
;
417 cumulative_read_buffer_
.clear();
420 void CdmFileIOImpl::OnError(ErrorType error_type
) {
421 // For *_WHILE_IN_USE errors, do not reset these values. Otherwise, the
422 // existing read/write operation will fail.
423 if (error_type
== READ_ERROR
|| error_type
== WRITE_ERROR
) {
426 cumulative_read_buffer_
.clear();
428 PostOnMain(callback_factory_
.NewCallback(&CdmFileIOImpl::NotifyClientOfError
,
432 void CdmFileIOImpl::NotifyClientOfError(int32_t result
,
433 ErrorType error_type
) {
434 PP_DCHECK(result
== PP_OK
);
435 switch (error_type
) {
437 client_
->OnOpenComplete(cdm::FileIOClient::kError
);
440 client_
->OnReadComplete(cdm::FileIOClient::kError
, NULL
, 0);
443 client_
->OnWriteComplete(cdm::FileIOClient::kError
);
445 case OPEN_WHILE_IN_USE
:
446 client_
->OnOpenComplete(cdm::FileIOClient::kInUse
);
448 case READ_WHILE_IN_USE
:
449 client_
->OnReadComplete(cdm::FileIOClient::kInUse
, NULL
, 0);
451 case WRITE_WHILE_IN_USE
:
452 client_
->OnWriteComplete(cdm::FileIOClient::kInUse
);