Disable view source for Developer Tools.
[chromium-blink-merge.git] / media / cdm / ppapi / cdm_file_io_impl.cc
blob726388949ec483ef1ad05191e95a212b0677d29f
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"
7 #include <algorithm>
8 #include <sstream>
10 #include "media/cdm/ppapi/cdm_logging.h"
11 #include "ppapi/c/pp_errors.h"
12 #include "ppapi/cpp/dev/url_util_dev.h"
14 namespace media {
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) \
21 do { \
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); \
27 return; \
28 } \
29 } while (0)
31 #if !defined(NDEBUG)
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),
60 client_(client),
61 pp_instance_handle_(pp_instance),
62 callback_factory_(this),
63 io_offset_(0) {
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.";
79 OnError(OPEN_ERROR);
80 return;
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.";
88 OnError(OPEN_ERROR);
89 return;
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);
98 return;
101 state_ = OPENING_FILE_SYSTEM;
102 OpenFileSystem();
105 // Call sequence:
106 // finished
107 // Read() -> ReadFile() -> OnFileRead() ----------> Done.
108 // ^ |
109 // | not finished |
110 // |--------------|
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);
118 return;
121 if (state_ != FILE_OPENED) {
122 CDM_DLOG() << "Read() called in an invalid state.";
123 OnError(READ_ERROR);
124 return;
127 PP_DCHECK(io_buffer_.empty());
128 PP_DCHECK(cumulative_read_buffer_.empty());
130 io_buffer_.resize(kReadSize);
131 io_offset_ = 0;
133 state_ = READING_FILE;
134 ReadFile();
137 // Call sequence:
138 // finished
139 // Write() -> WriteFile() -> OnFileWritten() ----------> Done.
140 // ^ |
141 // | | not finished
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);
150 return;
153 if (state_ != FILE_OPENED) {
154 CDM_DLOG() << "Write() called in an invalid state.";
155 OnError(WRITE_ERROR);
156 return;
159 PP_DCHECK(io_offset_ == 0);
160 PP_DCHECK(io_buffer_.empty());
161 if (data_size > 0)
162 io_buffer_.assign(data, data + data_size);
163 else
164 PP_DCHECK(!data);
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);
176 CloseFile();
177 ReleaseFileLock();
178 // All pending callbacks are canceled since |callback_factory_| is destroyed.
179 delete this;
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;
189 pp::Var url_var =
190 url_util_dev->GetDocumentURL(pp_instance_handle_, &components);
191 if (!url_var.is_string())
192 return false;
193 std::string url = url_var.AsString();
195 file_id_.append(url, components.scheme.begin, components.scheme.len);
196 file_id_ += ':';
197 file_id_.append(url, components.host.begin, components.host.len);
198 file_id_ += ':';
199 file_id_.append(url, components.port.begin, components.port.len);
200 file_id_ += file_name_;
202 return true;
205 bool CdmFileIOImpl::AcquireFileLock() {
206 PP_DCHECK(IsMainThread());
208 if (file_id_.empty() && !SetFileID())
209 return false;
211 if (!file_lock_map_) {
212 file_lock_map_ = new FileLockMap();
213 } else {
214 FileLockMap::iterator found = file_lock_map_->find(file_id_);
215 if (found != file_lock_map_->end() && found->second)
216 return false;
219 (*file_lock_map_)[file_id_] = true;
220 return true;
223 void CdmFileIOImpl::ReleaseFileLock() {
224 PP_DCHECK(IsMainThread());
226 if (!file_lock_map_)
227 return;
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.";
253 ReleaseFileLock();
254 OnError(OPEN_ERROR);
255 return;
258 file_system_ = file_system;
259 state_ = OPENING_FILE;
260 OpenFile();
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),
274 OPEN_ERROR);
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.";
283 ReleaseFileLock();
284 OnError(OPEN_ERROR);
285 return;
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),
300 READ_ERROR);
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.";
311 OnError(READ_ERROR);
312 return;
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(),
318 io_buffer_.begin(),
319 io_buffer_.begin() + bytes_read);
320 io_offset_ += bytes_read;
322 // Not received end-of-file yet.
323 if (bytes_read > 0) {
324 ReadFile();
325 return;
328 // We hit end-of-file. Return read data to the client.
329 io_buffer_.clear();
330 io_offset_ = 0;
331 // Clear |cumulative_read_buffer_| in case OnReadComplete() calls Read() or
332 // Write().
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);
359 return;
362 if (io_buffer_.empty()) {
363 state_ = FILE_OPENED;
364 client_->OnWriteComplete(cdm::FileIOClient::kSuccess);
365 return;
368 WriteFile();
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_,
380 cb),
381 WRITE_ERROR);
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.";
391 OnError(READ_ERROR);
392 return;
395 io_offset_ += bytes_written;
396 PP_DCHECK(io_offset_ <= io_buffer_.size());
398 if (io_offset_ < io_buffer_.size()) {
399 WriteFile();
400 return;
403 io_buffer_.clear();
404 io_offset_ = 0;
405 state_ = FILE_OPENED;
406 client_->OnWriteComplete(cdm::FileIOClient::kSuccess);
409 void CdmFileIOImpl::CloseFile() {
410 PP_DCHECK(IsMainThread());
412 state_ = FILE_CLOSED;
414 file_io_.Close();
415 io_buffer_.clear();
416 io_offset_ = 0;
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) {
424 io_buffer_.clear();
425 io_offset_ = 0;
426 cumulative_read_buffer_.clear();
428 PostOnMain(callback_factory_.NewCallback(&CdmFileIOImpl::NotifyClientOfError,
429 error_type));
432 void CdmFileIOImpl::NotifyClientOfError(int32_t result,
433 ErrorType error_type) {
434 PP_DCHECK(result == PP_OK);
435 switch (error_type) {
436 case OPEN_ERROR:
437 client_->OnOpenComplete(cdm::FileIOClient::kError);
438 break;
439 case READ_ERROR:
440 client_->OnReadComplete(cdm::FileIOClient::kError, NULL, 0);
441 break;
442 case WRITE_ERROR:
443 client_->OnWriteComplete(cdm::FileIOClient::kError);
444 break;
445 case OPEN_WHILE_IN_USE:
446 client_->OnOpenComplete(cdm::FileIOClient::kInUse);
447 break;
448 case READ_WHILE_IN_USE:
449 client_->OnReadComplete(cdm::FileIOClient::kInUse, NULL, 0);
450 break;
451 case WRITE_WHILE_IN_USE:
452 client_->OnWriteComplete(cdm::FileIOClient::kInUse);
453 break;
457 } // namespace media