| // Copyright 2014 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 "chrome/browser/file_select_helper.h" |
| |
| #include <Cocoa/Cocoa.h> |
| #include <sys/stat.h> |
| |
| #include "base/files/file.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/mac/foundation_util.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "third_party/zlib/google/zip.h" |
| #include "ui/shell_dialogs/selected_file_info.h" |
| |
| namespace { |
| |
| // Given the |path| of a package, returns the destination that the package |
| // should be zipped to. Returns an empty path on any errors. |
| base::FilePath ZipDestination(const base::FilePath& path) { |
| base::FilePath dest; |
| |
| if (!base::GetTempDir(&dest)) { |
| // Couldn't get the temporary directory. |
| return base::FilePath(); |
| } |
| |
| // TMPDIR/<bundleID>/zip_cache/<guid> |
| |
| NSString* bundleID = [[NSBundle mainBundle] bundleIdentifier]; |
| dest = dest.Append([bundleID fileSystemRepresentation]); |
| |
| dest = dest.Append("zip_cache"); |
| |
| NSString* guid = [[NSProcessInfo processInfo] globallyUniqueString]; |
| dest = dest.Append([guid fileSystemRepresentation]); |
| |
| return dest; |
| } |
| |
| // Returns the path of the package and its components relative to the package's |
| // parent directory. |
| std::vector<base::FilePath> RelativePathsForPackage( |
| const base::FilePath& package) { |
| // Get the base directory. |
| base::FilePath base_dir = package.DirName(); |
| |
| // Add the package as the first relative path. |
| std::vector<base::FilePath> relative_paths; |
| relative_paths.push_back(package.BaseName()); |
| |
| // Add the components of the package as relative paths. |
| base::FileEnumerator file_enumerator( |
| package, |
| true /* recursive */, |
| base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES); |
| for (base::FilePath path = file_enumerator.Next(); !path.empty(); |
| path = file_enumerator.Next()) { |
| base::FilePath relative_path; |
| bool success = base_dir.AppendRelativePath(path, &relative_path); |
| if (success) |
| relative_paths.push_back(relative_path); |
| } |
| |
| return relative_paths; |
| } |
| |
| } // namespace |
| |
| base::FilePath FileSelectHelper::ZipPackage(const base::FilePath& path) { |
| base::FilePath dest(ZipDestination(path)); |
| if (dest.empty()) |
| return dest; |
| |
| if (!base::CreateDirectory(dest.DirName())) |
| return base::FilePath(); |
| |
| base::File file(dest, base::File::FLAG_CREATE | base::File::FLAG_WRITE); |
| if (!file.IsValid()) |
| return base::FilePath(); |
| |
| std::vector<base::FilePath> files_to_zip(RelativePathsForPackage(path)); |
| base::FilePath base_dir = path.DirName(); |
| bool success = zip::ZipFiles(base_dir, files_to_zip, file.GetPlatformFile()); |
| |
| int result = -1; |
| if (success) |
| result = fchmod(file.GetPlatformFile(), S_IRUSR); |
| |
| return result >= 0 ? dest : base::FilePath(); |
| } |
| |
| void FileSelectHelper::ProcessSelectedFilesMac( |
| const std::vector<ui::SelectedFileInfo>& files) { |
| // Make a mutable copy of the input files. |
| std::vector<ui::SelectedFileInfo> files_out(files); |
| std::vector<base::FilePath> temporary_files; |
| |
| for (auto& file_info : files_out) { |
| NSString* filename = base::mac::FilePathToNSString(file_info.local_path); |
| BOOL isPackage = |
| [[NSWorkspace sharedWorkspace] isFilePackageAtPath:filename]; |
| if (isPackage && base::DirectoryExists(file_info.local_path)) { |
| base::FilePath result = ZipPackage(file_info.local_path); |
| |
| if (!result.empty()) { |
| temporary_files.push_back(result); |
| file_info.local_path = result; |
| file_info.file_path = result; |
| file_info.display_name.append(".zip"); |
| } |
| } |
| } |
| |
| content::BrowserThread::PostTask( |
| content::BrowserThread::UI, |
| FROM_HERE, |
| base::Bind(&FileSelectHelper::ProcessSelectedFilesMacOnUIThread, |
| base::Unretained(this), |
| files_out, |
| temporary_files)); |
| } |
| |
| void FileSelectHelper::ProcessSelectedFilesMacOnUIThread( |
| const std::vector<ui::SelectedFileInfo>& files, |
| const std::vector<base::FilePath>& temporary_files) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| if (!temporary_files.empty()) { |
| temporary_files_.insert( |
| temporary_files_.end(), temporary_files.begin(), temporary_files.end()); |
| |
| // Typically, |temporary_files| are deleted after |web_contents_| is |
| // destroyed. If |web_contents_| is already NULL, then the temporary files |
| // need to be deleted now. |
| if (!web_contents_) { |
| DeleteTemporaryFiles(); |
| RunFileChooserEnd(); |
| return; |
| } |
| } |
| |
| NotifyRenderFrameHostAndEnd(files); |
| } |