Index: content/browser/loader/temporary_file_manager.cc |
diff --git a/content/browser/loader/temporary_file_manager.cc b/content/browser/loader/temporary_file_manager.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..8979270a48799c84bff4eea8308dad75038fc563 |
--- /dev/null |
+++ b/content/browser/loader/temporary_file_manager.cc |
@@ -0,0 +1,154 @@ |
+// Copyright 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "content/browser/loader/temporary_file_manager.h" |
+ |
+#include "base/files/file_util_proxy.h" |
+#include "content/browser/child_process_security_policy_impl.h" |
+#include "content/public/browser/browser_thread.h" |
+#include "net/base/file_stream.h" |
+#include "webkit/common/blob/shareable_file_reference.h" |
+ |
+using webkit_blob::ShareableFileReference; |
+ |
+namespace content { |
+ |
+namespace { |
+ |
+void RemoveDownloadFileFromChildSecurityPolicy(int child_id, |
+ const base::FilePath& path) { |
+ ChildProcessSecurityPolicyImpl::GetInstance()->RevokeAllPermissionsForFile( |
+ child_id, path); |
+} |
+ |
+} // namespace |
+ |
+struct TemporaryFileManager::PendingTemporary { |
+ int child_id; |
+ int request_id; |
+ TemporaryFileStreamFactory::Callback callback; |
+ bool cancelled; |
mmenke
2013/12/04 20:55:48
nit: Methods go before member variables.
davidben
2013/12/04 22:44:04
Done.
|
+ |
+ PendingTemporary(int child_id, |
+ int request_id, |
+ const TemporaryFileStreamFactory::Callback& callback) |
+ : child_id(child_id), |
+ request_id(request_id), |
+ callback(callback), |
+ cancelled(false) { |
+ } |
+}; |
+ |
+TemporaryFileManager::TemporaryFileManager() : weak_factory_(this) { |
+} |
+ |
+TemporaryFileManager::~TemporaryFileManager() { |
+} |
+ |
+void TemporaryFileManager::UnregisterDownloadedTempFile( |
+ int child_id, int request_id) { |
+ DeletableFilesMap& map = registered_temp_files_[child_id]; |
+ DeletableFilesMap::iterator found = map.find(request_id); |
+ if (found != map.end()) { |
+ map.erase(found); |
+ } |
+ |
+ // Cancel any pending requests. |
+ PendingTemporaryMap::iterator found_pending = |
+ pending_temporary_map_.find(GlobalRequestID(child_id, request_id)); |
+ if (found_pending != pending_temporary_map_.end()) { |
+ found_pending->second->cancelled = true; |
+ } |
+ |
+ // Note that we don't remove the security bits here. This will be done |
+ // when all file refs are deleted (see RegisterDownloadedTempFile). |
+} |
+ |
+void TemporaryFileManager::UnregisterFilesForChild(int child_id) { |
+ registered_temp_files_.erase(child_id); |
+ |
+ // Cancel all pending requests. |
+ PendingTemporaryMap::iterator iter = |
+ pending_temporary_map_.lower_bound(GlobalRequestID(child_id, -1)); |
+ PendingTemporaryMap::iterator stop = |
+ pending_temporary_map_.lower_bound(GlobalRequestID(child_id + 1, -1)); |
+ for (; iter != stop; ++iter) { |
+ DCHECK_EQ(child_id, iter->first.child_id); |
+ iter->second->cancelled = true; |
+ } |
+} |
+ |
+void TemporaryFileManager::CreateTemporary( |
+ int child_id, |
+ int request_id, |
+ const TemporaryFileStreamFactory::Callback& callback) { |
+ PendingTemporary* pending = |
+ new PendingTemporary(child_id, request_id, callback); |
+ pending_temporary_map_[GlobalRequestID(child_id, request_id)] = pending; |
+ |
+ base::FileUtilProxy::CreateTemporary( |
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE).get(), |
+ base::PLATFORM_FILE_ASYNC, |
+ base::Bind(&TemporaryFileManager::DidCreateTemporaryFile, AsWeakPtr(), |
+ base::Owned(pending))); |
+} |
+ |
+void TemporaryFileManager::DidCreateTemporaryFile( |
+ TemporaryFileManager::PendingTemporary* pending, |
+ base::PlatformFileError error_code, |
+ base::PassPlatformFile file_handle, |
+ const base::FilePath& file_path) { |
+ DCHECK_EQ(pending, |
+ pending_temporary_map_[GlobalRequestID(pending->child_id, |
+ pending->request_id)]); |
+ pending_temporary_map_.erase(GlobalRequestID(pending->child_id, |
+ pending->request_id)); |
+ |
+ if (error_code != base::PLATFORM_FILE_OK) { |
+ if (!pending->cancelled) |
+ pending->callback.Run(error_code, scoped_ptr<net::FileStream>(), NULL); |
+ return; |
+ } |
+ |
+ // Cancelled or not, create the deletable_file so the temporary is cleaned up. |
+ scoped_refptr<ShareableFileReference> deletable_file = |
+ ShareableFileReference::GetOrCreate( |
+ file_path, |
+ ShareableFileReference::DELETE_ON_FINAL_RELEASE, |
+ BrowserThread::GetMessageLoopProxyForThread( |
+ BrowserThread::FILE).get()); |
+ |
+ if (pending->cancelled) |
+ return; |
+ |
+ scoped_ptr<net::FileStream> file_stream(new net::FileStream( |
+ file_handle.ReleaseValue(), |
+ base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_ASYNC, |
+ NULL)); |
+ |
+ RegisterDownloadedTempFile(pending->child_id, pending->request_id, |
+ deletable_file.get()); |
+ pending->callback.Run(error_code, file_stream.Pass(), deletable_file); |
+} |
+ |
+void TemporaryFileManager::RegisterDownloadedTempFile( |
+ int child_id, int request_id, ShareableFileReference* reference) { |
+ registered_temp_files_[child_id][request_id] = reference; |
+ ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile( |
+ child_id, reference->path()); |
+ |
+ // When the temp file is deleted, revoke permissions that the renderer has |
+ // to that file. This covers an edge case where the file is deleted and then |
+ // the same name is re-used for some other purpose, we don't want the old |
+ // renderer to still have access to it. |
+ // |
+ // We do this when the file is deleted because the renderer can take a blob |
+ // reference to the temp file that outlives the url loaded that it was |
+ // loaded with to keep the file (and permissions) alive. |
+ reference->AddFinalReleaseCallback( |
+ base::Bind(&RemoveDownloadFileFromChildSecurityPolicy, |
+ child_id)); |
+} |
+ |
+} // namespace content |