// 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 #include #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) { NSMutableString* dest = [NSMutableString stringWithString:NSTemporaryDirectory()]; // Couldn't get the temporary directory. if (!dest) return base::FilePath(); [dest appendFormat:@"%@/zip_cache/%@", [[NSBundle mainBundle] bundleIdentifier], [[NSProcessInfo processInfo] globallyUniqueString]]; return base::mac::NSStringToFilePath(dest); } // Returns the path of the package and its components relative to the package's // parent directory. std::vector 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 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 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& files) { DCHECK_CURRENTLY_ON(content::BrowserThread::FILE_USER_BLOCKING); // Make a mutable copy of the input files. std::vector files_out(files); std::vector 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& files, const std::vector& 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; } } NotifyRenderViewHostAndEnd(files); }