Commit 22de64a3 authored by erikchen's avatar erikchen Committed by Commit bot

mac: Zip packages when they are selected by the file opener.

When a package is selected in the file opener, it is automatically zipped, and
the zipped file is passed to the render view host.

This CL fixes 2 unrelated bugs in file_select_helper:
- If the render_view_host_ was destroyed before the user finished with the
file_select_helper, then Release() would never be called and the
file_select_helper would leak.

- If the render_view_host_ was destroyed before the user finished with the
file_select_helper, the default directory would be updated if the user was
opening multiple files, but it would not be updated if the user was opening a
single file. Now both update the default directory.

Note: When a large package is being zipped, the UI thread and renderer are
still responsive, but display no indication of the ongoing work.

BUG=33920

Review URL: https://codereview.chromium.org/634833003

Cr-Commit-Position: refs/heads/master@{#299143}
parent e3e806ff
...@@ -70,6 +70,11 @@ std::vector<ui::SelectedFileInfo> FilePathListToSelectedFileInfoList( ...@@ -70,6 +70,11 @@ std::vector<ui::SelectedFileInfo> FilePathListToSelectedFileInfoList(
return selected_files; return selected_files;
} }
void DeleteFiles(const std::vector<base::FilePath>& paths) {
for (auto& file_path : paths)
base::DeleteFile(file_path, false);
}
} // namespace } // namespace
struct FileSelectHelper::ActiveDirectoryEnumeration { struct FileSelectHelper::ActiveDirectoryEnumeration {
...@@ -126,11 +131,13 @@ void FileSelectHelper::FileSelectedWithExtraInfo( ...@@ -126,11 +131,13 @@ void FileSelectHelper::FileSelectedWithExtraInfo(
const ui::SelectedFileInfo& file, const ui::SelectedFileInfo& file,
int index, int index,
void* params) { void* params) {
if (!render_view_host_)
return;
profile_->set_last_selected_directory(file.file_path.DirName()); profile_->set_last_selected_directory(file.file_path.DirName());
if (!render_view_host_) {
RunFileChooserEnd();
return;
}
const base::FilePath& path = file.local_path; const base::FilePath& path = file.local_path;
if (dialog_type_ == ui::SelectFileDialog::SELECT_UPLOAD_FOLDER) { if (dialog_type_ == ui::SelectFileDialog::SELECT_UPLOAD_FOLDER) {
StartNewEnumeration(path, kFileSelectEnumerationId, render_view_host_); StartNewEnumeration(path, kFileSelectEnumerationId, render_view_host_);
...@@ -139,10 +146,15 @@ void FileSelectHelper::FileSelectedWithExtraInfo( ...@@ -139,10 +146,15 @@ void FileSelectHelper::FileSelectedWithExtraInfo(
std::vector<ui::SelectedFileInfo> files; std::vector<ui::SelectedFileInfo> files;
files.push_back(file); files.push_back(file);
NotifyRenderViewHost(render_view_host_, files, dialog_mode_);
// No members should be accessed from here on. #if defined(OS_MACOSX) && !defined(OS_IOS)
RunFileChooserEnd(); content::BrowserThread::PostTask(
content::BrowserThread::FILE_USER_BLOCKING,
FROM_HERE,
base::Bind(&FileSelectHelper::ProcessSelectedFilesMac, this, files));
#else
NotifyRenderViewHostAndEnd(files);
#endif // defined(OS_MACOSX) && !defined(OS_IOS)
} }
void FileSelectHelper::MultiFilesSelected( void FileSelectHelper::MultiFilesSelected(
...@@ -159,27 +171,19 @@ void FileSelectHelper::MultiFilesSelectedWithExtraInfo( ...@@ -159,27 +171,19 @@ void FileSelectHelper::MultiFilesSelectedWithExtraInfo(
void* params) { void* params) {
if (!files.empty()) if (!files.empty())
profile_->set_last_selected_directory(files[0].file_path.DirName()); profile_->set_last_selected_directory(files[0].file_path.DirName());
if (!render_view_host_)
return;
NotifyRenderViewHost(render_view_host_, files, dialog_mode_); #if defined(OS_MACOSX) && !defined(OS_IOS)
content::BrowserThread::PostTask(
// No members should be accessed from here on. content::BrowserThread::FILE_USER_BLOCKING,
RunFileChooserEnd(); FROM_HERE,
base::Bind(&FileSelectHelper::ProcessSelectedFilesMac, this, files));
#else
NotifyRenderViewHostAndEnd(files);
#endif // defined(OS_MACOSX) && !defined(OS_IOS)
} }
void FileSelectHelper::FileSelectionCanceled(void* params) { void FileSelectHelper::FileSelectionCanceled(void* params) {
if (!render_view_host_) NotifyRenderViewHostAndEnd(std::vector<ui::SelectedFileInfo>());
return;
// If the user cancels choosing a file to upload we pass back an
// empty vector.
NotifyRenderViewHost(
render_view_host_, std::vector<ui::SelectedFileInfo>(),
dialog_mode_);
// No members should be accessed from here on.
RunFileChooserEnd();
} }
void FileSelectHelper::StartNewEnumeration(const base::FilePath& path, void FileSelectHelper::StartNewEnumeration(const base::FilePath& path,
...@@ -237,6 +241,22 @@ void FileSelectHelper::OnListDone(int id, int error) { ...@@ -237,6 +241,22 @@ void FileSelectHelper::OnListDone(int id, int error) {
EnumerateDirectoryEnd(); EnumerateDirectoryEnd();
} }
void FileSelectHelper::NotifyRenderViewHostAndEnd(
const std::vector<ui::SelectedFileInfo>& files) {
if (render_view_host_)
NotifyRenderViewHost(render_view_host_, files, dialog_mode_);
// No members should be accessed from here on.
RunFileChooserEnd();
}
void FileSelectHelper::DeleteTemporaryFiles() {
BrowserThread::PostTask(BrowserThread::FILE,
FROM_HERE,
base::Bind(&DeleteFiles, temporary_files_));
temporary_files_.clear();
}
scoped_ptr<ui::SelectFileDialog::FileTypeInfo> scoped_ptr<ui::SelectFileDialog::FileTypeInfo>
FileSelectHelper::GetFileTypesFromAcceptType( FileSelectHelper::GetFileTypesFromAcceptType(
const std::vector<base::string16>& accept_types) { const std::vector<base::string16>& accept_types) {
...@@ -335,8 +355,12 @@ void FileSelectHelper::RunFileChooser(RenderViewHost* render_view_host, ...@@ -335,8 +355,12 @@ void FileSelectHelper::RunFileChooser(RenderViewHost* render_view_host,
render_view_host_ = render_view_host; render_view_host_ = render_view_host;
web_contents_ = web_contents; web_contents_ = web_contents;
notification_registrar_.RemoveAll(); notification_registrar_.RemoveAll();
notification_registrar_.Add(this,
content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
content::Source<WebContents>(web_contents_));
notification_registrar_.Add( notification_registrar_.Add(
this, content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED, this,
content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
content::Source<RenderWidgetHost>(render_view_host_)); content::Source<RenderWidgetHost>(render_view_host_));
notification_registrar_.Add( notification_registrar_.Add(
this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
...@@ -433,6 +457,12 @@ void FileSelectHelper::RunFileChooserOnUIThread( ...@@ -433,6 +457,12 @@ void FileSelectHelper::RunFileChooserOnUIThread(
// chooser dialog. Perform any cleanup and release the reference we added // chooser dialog. Perform any cleanup and release the reference we added
// in RunFileChooser(). // in RunFileChooser().
void FileSelectHelper::RunFileChooserEnd() { void FileSelectHelper::RunFileChooserEnd() {
// If there are temporary files, then this instance needs to stick around
// until web_contents_ is destroyed, so that this instance can delete the
// temporary files.
if (!temporary_files_.empty())
return;
render_view_host_ = NULL; render_view_host_ = NULL;
web_contents_ = NULL; web_contents_ = NULL;
Release(); Release();
...@@ -472,9 +502,20 @@ void FileSelectHelper::Observe(int type, ...@@ -472,9 +502,20 @@ void FileSelectHelper::Observe(int type,
case content::NOTIFICATION_WEB_CONTENTS_DESTROYED: { case content::NOTIFICATION_WEB_CONTENTS_DESTROYED: {
DCHECK(content::Source<WebContents>(source).ptr() == web_contents_); DCHECK(content::Source<WebContents>(source).ptr() == web_contents_);
web_contents_ = NULL; web_contents_ = NULL;
break;
} }
// Intentional fall through.
case content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED:
if (!temporary_files_.empty()) {
DeleteTemporaryFiles();
// Now that the temporary files have been scheduled for deletion, there
// is no longer any reason to keep this instance around.
Release();
}
break;
default: default:
NOTREACHED(); NOTREACHED();
} }
......
...@@ -48,6 +48,7 @@ class FileSelectHelper ...@@ -48,6 +48,7 @@ class FileSelectHelper
private: private:
friend class base::RefCountedThreadSafe<FileSelectHelper>; friend class base::RefCountedThreadSafe<FileSelectHelper>;
FRIEND_TEST_ALL_PREFIXES(FileSelectHelperTest, IsAcceptTypeValid); FRIEND_TEST_ALL_PREFIXES(FileSelectHelperTest, IsAcceptTypeValid);
FRIEND_TEST_ALL_PREFIXES(FileSelectHelperTest, ZipPackage);
explicit FileSelectHelper(Profile* profile); explicit FileSelectHelper(Profile* profile);
virtual ~FileSelectHelper(); virtual ~FileSelectHelper();
...@@ -121,6 +122,33 @@ class FileSelectHelper ...@@ -121,6 +122,33 @@ class FileSelectHelper
// callback is received from the enumeration code. // callback is received from the enumeration code.
void EnumerateDirectoryEnd(); void EnumerateDirectoryEnd();
#if defined(OS_MACOSX) && !defined(OS_IOS)
// Must be called on the FILE_USER_BLOCKING thread. Each selected file that is
// a package will be zipped, and the zip will be passed to the render view
// host in place of the package.
void ProcessSelectedFilesMac(const std::vector<ui::SelectedFileInfo>& files);
// Saves the paths of |zipped_files| for later deletion. Passes |files| to the
// render view host.
void ProcessSelectedFilesMacOnUIThread(
const std::vector<ui::SelectedFileInfo>& files,
const std::vector<base::FilePath>& zipped_files);
// Zips the package at |path| into a temporary destination. Returns the
// temporary destination, if the zip was successful. Otherwise returns an
// empty path.
static base::FilePath ZipPackage(const base::FilePath& path);
#endif // defined(OS_MACOSX) && !defined(OS_IOS)
// Utility method that passes |files| to the render view host, and ends the
// file chooser.
void NotifyRenderViewHostAndEnd(
const std::vector<ui::SelectedFileInfo>& files);
// Schedules the deletion of the files in |temporary_files_| and clears the
// vector.
void DeleteTemporaryFiles();
// Helper method to get allowed extensions for select file dialog from // Helper method to get allowed extensions for select file dialog from
// the specified accept types as defined in the spec: // the specified accept types as defined in the spec:
// http://whatwg.org/html/number-state.html#attr-input-accept // http://whatwg.org/html/number-state.html#attr-input-accept
...@@ -161,6 +189,10 @@ class FileSelectHelper ...@@ -161,6 +189,10 @@ class FileSelectHelper
// Registrar for notifications regarding our RenderViewHost. // Registrar for notifications regarding our RenderViewHost.
content::NotificationRegistrar notification_registrar_; content::NotificationRegistrar notification_registrar_;
// Temporary files only used on OSX. This class is responsible for deleting
// these files when they are no longer needed.
std::vector<base::FilePath> temporary_files_;
DISALLOW_COPY_AND_ASSIGN(FileSelectHelper); DISALLOW_COPY_AND_ASSIGN(FileSelectHelper);
}; };
......
// 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) {
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<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) {
DCHECK_CURRENTLY_ON(content::BrowserThread::FILE_USER_BLOCKING);
// 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;
}
}
NotifyRenderViewHostAndEnd(files);
}
...@@ -2,10 +2,36 @@ ...@@ -2,10 +2,36 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/path_service.h"
#include "base/process/launch.h"
#include "chrome/browser/file_select_helper.h" #include "chrome/browser/file_select_helper.h"
#include "chrome/common/chrome_paths.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
TEST(FileSelectHelperTest, IsAcceptTypeValid) { class FileSelectHelperTest : public testing::Test {
public:
FileSelectHelperTest() {}
protected:
virtual void SetUp() override {
ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_dir_));
data_dir_ = data_dir_.AppendASCII("file_select_helper");
ASSERT_TRUE(base::PathExists(data_dir_));
}
// The path to input data used in tests.
base::FilePath data_dir_;
private:
DISALLOW_COPY_AND_ASSIGN(FileSelectHelperTest);
};
TEST_F(FileSelectHelperTest, IsAcceptTypeValid) {
EXPECT_TRUE(FileSelectHelper::IsAcceptTypeValid("a/b")); EXPECT_TRUE(FileSelectHelper::IsAcceptTypeValid("a/b"));
EXPECT_TRUE(FileSelectHelper::IsAcceptTypeValid("abc/def")); EXPECT_TRUE(FileSelectHelper::IsAcceptTypeValid("abc/def"));
EXPECT_TRUE(FileSelectHelper::IsAcceptTypeValid("abc/*")); EXPECT_TRUE(FileSelectHelper::IsAcceptTypeValid("abc/*"));
...@@ -17,3 +43,38 @@ TEST(FileSelectHelperTest, IsAcceptTypeValid) { ...@@ -17,3 +43,38 @@ TEST(FileSelectHelperTest, IsAcceptTypeValid) {
EXPECT_FALSE(FileSelectHelper::IsAcceptTypeValid("ABC/*")); EXPECT_FALSE(FileSelectHelper::IsAcceptTypeValid("ABC/*"));
EXPECT_FALSE(FileSelectHelper::IsAcceptTypeValid("abc/def ")); EXPECT_FALSE(FileSelectHelper::IsAcceptTypeValid("abc/def "));
} }
#if defined(OS_MACOSX) && !defined(OS_IOS)
TEST_F(FileSelectHelperTest, ZipPackage) {
// Zip the package.
const char app_name[] = "CalculatorFake.app";
base::FilePath src = data_dir_.Append(app_name);
base::FilePath dest = FileSelectHelper::ZipPackage(src);
ASSERT_FALSE(dest.empty());
ASSERT_TRUE(base::PathExists(dest));
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
// Unzip the package into a temporary directory.
CommandLine cl(base::FilePath("/usr/bin/unzip"));
cl.AppendArg(dest.value().c_str());
cl.AppendArg("-d");
cl.AppendArg(temp_dir.path().value().c_str());
std::string output;
EXPECT_TRUE(base::GetAppOutput(cl, &output));
// Verify that several key files haven't changed.
const char* files_to_verify[] = {"Contents/Info.plist",
"Contents/MacOS/Calculator",
"Contents/_CodeSignature/CodeResources"};
size_t file_count = arraysize(files_to_verify);
for (size_t i = 0; i < file_count; i++) {
const char* relative_path = files_to_verify[i];
base::FilePath orig_file = src.Append(relative_path);
base::FilePath final_file =
temp_dir.path().Append(app_name).Append(relative_path);
EXPECT_TRUE(base::ContentsEqual(orig_file, final_file));
}
}
#endif // defined(OS_MACOSX) && !defined(OS_IOS)
...@@ -412,6 +412,7 @@ ...@@ -412,6 +412,7 @@
'browser/favicon/favicon_tab_helper.h', 'browser/favicon/favicon_tab_helper.h',
'browser/file_select_helper.cc', 'browser/file_select_helper.cc',
'browser/file_select_helper.h', 'browser/file_select_helper.h',
'browser/file_select_helper_mac.mm',
'browser/flags_storage.h', 'browser/flags_storage.h',
'browser/fullscreen.h', 'browser/fullscreen.h',
'browser/fullscreen_chromeos.cc', 'browser/fullscreen_chromeos.cc',
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildMachineOSBuild</key>
<string>13A3007</string>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>Calculator</string>
<key>CFBundleGetInfoString</key>
<string>10.8, Copyright © 2001-2013, Apple Inc.</string>
<key>CFBundleHelpBookFolder</key>
<string>Calculator.help</string>
<key>CFBundleHelpBookName</key>
<string>com.apple.Calculator.help</string>
<key>CFBundleIconFile</key>
<string>Calculator.icns</string>
<key>CFBundleIdentifier</key>
<string>com.apple.calculator</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>Calculator</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>10.8</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>123</string>
<key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformBuild</key>
<string>5A11344p</string>
<key>DTPlatformVersion</key>
<string>GM</string>
<key>DTSDKBuild</key>
<string>13A3007</string>
<key>DTSDKName</key>
<string></string>
<key>DTXcode</key>
<string>0500</string>
<key>DTXcodeBuild</key>
<string>5A11344p</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSApplicationSecondaryCategoryType</key>
<string>public.app-category.productivity</string>
<key>LSHasLocalizedDisplayName</key>
<true/>
<key>LSMinimumSystemVersion</key>
<string>10.8.0</string>
<key>NSMainNibFile</key>
<string>Calculator</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSSupportsSuddenTermination</key>
<true/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>files</key>
<dict>
<key>Resources/Calculator.icns</key>
<data>
Gn02zgleVXVUNMx/IaKHRdogAqc=
</data>
<key>Resources/ConversionCategories.plist</key>
<data>
T8VrSycV7UZI+iraTkfRzXgGpCg=
</data>
<key>Resources/ConversionsFromBase.plist</key>
<data>
C+GN4i6J6fHEGyrL76HwoWD0jwY=
</data>
<key>Resources/ConversionsToBase.plist</key>
<data>
BIGpxM7D5Nd65DZUxGj8RTKhMa4=
</data>
<key>Resources/English.lproj/Calculator.nib</key>
<dict>
<key>hash</key>
<data>
BoYRTYGybnwwBjfhJXPnMV/SDEI=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/English.lproj/ConversionSheet.nib</key>
<dict>
<key>hash</key>
<data>
F8hm0ey2/eT6E+COCmhyuMBgVGU=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/English.lproj/InfoPlist.strings</key>
<dict>
<key>hash</key>
<data>
5Pi5eQt8nOjHiAznV6EBZs18uzk=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/English.lproj/Localizable.strings</key>
<dict>
<key>hash</key>
<data>
R/eA28xGdzOwu+MiJas13zJLGKA=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/English.lproj/UnitNames.strings</key>
<dict>
<key>hash</key>
<data>
CG6oiCqvbVg7cSQxhNIqPMIeygg=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/English.lproj/locversion.plist</key>
<dict>
<key>hash</key>
<data>
58hId6E9Y7r4mNPv7xXxf0VWs18=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/FinancialRates.xml</key>
<data>
qWkmKOHKPg/lbHrldBSFqjOaB+A=
</data>
<key>Resources/Functions.xml</key>
<data>
CW8oJW4TDD/rsTbs2Nvusey0MWk=
</data>
<key>Resources/Speakable.plist</key>
<data>
ugbtHEF/8x4Bumr8ru2Vjj9BUDE=
</data>
<key>Resources/Yahoologo.tiff</key>
<data>
2ZEUVAM/PWFrDo96CnG25ZoLDSg=
</data>
<key>Resources/lcd-bottomleft.tiff</key>
<data>
wgVRVAoNvHqdx7yXgkBTbh8a+OI=
</data>
<key>Resources/lcd-bottommid.tiff</key>
<data>
24Ce6wJF4s/8xy7LrZS7EnFIrtQ=
</data>
<key>Resources/lcd-bottomright.tiff</key>
<data>
3jbeGXx/YB9ZVYE74WDf19hhvGo=
</data>
<key>Resources/lcd-leftmid.tiff</key>
<data>
cfAXp+8CHYpky2NJ5bAy8dgXW/Q=
</data>
<key>Resources/lcd-mid.tiff</key>
<data>
sh4nz6P/COdaA3VRC/mUhUMd2ZA=
</data>
<key>Resources/lcd-rightmid.tiff</key>
<data>
qDP+/eBzHF+AZ4FcDa9uusnotzE=
</data>
<key>Resources/lcd-topleft.tiff</key>
<data>
VO2m5Y1RJbmZyvF7O5Ka8pXa3IE=
</data>
<key>Resources/lcd-topmid.tiff</key>
<data>
OXoawx1x1At1w4Mg+PRwY4sY1Mw=
</data>
<key>Resources/lcd-topright.tiff</key>
<data>
IroGNppXkS+YkGnTBOX5bap2Nvs=
</data>
<key>version.plist</key>
<data>
pW4t92cdIGxgxkmnrFMXAEhELDM=
</data>
</dict>
<key>files2</key>
<dict>
<key>Info.plist</key>
<data>
6KZkeVzwpeLSjgkv20DOgFpcSFA=
</data>
<key>PkgInfo</key>
<data>
n57qDP4tZfLD1rCS43W0B4LQjzE=
</data>
<key>Resources/Calculator.icns</key>
<data>
Gn02zgleVXVUNMx/IaKHRdogAqc=
</data>
<key>Resources/ConversionCategories.plist</key>
<data>
T8VrSycV7UZI+iraTkfRzXgGpCg=
</data>
<key>Resources/ConversionsFromBase.plist</key>
<data>
C+GN4i6J6fHEGyrL76HwoWD0jwY=
</data>
<key>Resources/ConversionsToBase.plist</key>
<data>
BIGpxM7D5Nd65DZUxGj8RTKhMa4=
</data>
<key>Resources/English.lproj/Calculator.nib</key>
<dict>
<key>hash</key>
<data>
BoYRTYGybnwwBjfhJXPnMV/SDEI=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/English.lproj/ConversionSheet.nib</key>
<dict>
<key>hash</key>
<data>
F8hm0ey2/eT6E+COCmhyuMBgVGU=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/English.lproj/InfoPlist.strings</key>
<dict>
<key>hash</key>
<data>
5Pi5eQt8nOjHiAznV6EBZs18uzk=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/English.lproj/Localizable.strings</key>
<dict>
<key>hash</key>
<data>
R/eA28xGdzOwu+MiJas13zJLGKA=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/English.lproj/UnitNames.strings</key>
<dict>
<key>hash</key>
<data>
CG6oiCqvbVg7cSQxhNIqPMIeygg=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/English.lproj/locversion.plist</key>
<dict>
<key>hash</key>
<data>
58hId6E9Y7r4mNPv7xXxf0VWs18=
</data>
<key>optional</key>
<true/>
</dict>
<key>Resources/FinancialRates.xml</key>
<data>
qWkmKOHKPg/lbHrldBSFqjOaB+A=
</data>
<key>Resources/Functions.xml</key>
<data>
CW8oJW4TDD/rsTbs2Nvusey0MWk=
</data>
<key>Resources/Speakable.plist</key>
<data>
ugbtHEF/8x4Bumr8ru2Vjj9BUDE=
</data>
<key>Resources/Yahoologo.tiff</key>
<data>
2ZEUVAM/PWFrDo96CnG25ZoLDSg=
</data>
<key>Resources/lcd-bottomleft.tiff</key>
<data>
wgVRVAoNvHqdx7yXgkBTbh8a+OI=
</data>
<key>Resources/lcd-bottommid.tiff</key>
<data>
24Ce6wJF4s/8xy7LrZS7EnFIrtQ=
</data>
<key>Resources/lcd-bottomright.tiff</key>
<data>
3jbeGXx/YB9ZVYE74WDf19hhvGo=
</data>
<key>Resources/lcd-leftmid.tiff</key>
<data>
cfAXp+8CHYpky2NJ5bAy8dgXW/Q=
</data>
<key>Resources/lcd-mid.tiff</key>
<data>
sh4nz6P/COdaA3VRC/mUhUMd2ZA=
</data>
<key>Resources/lcd-rightmid.tiff</key>
<data>
qDP+/eBzHF+AZ4FcDa9uusnotzE=
</data>
<key>Resources/lcd-topleft.tiff</key>
<data>
VO2m5Y1RJbmZyvF7O5Ka8pXa3IE=
</data>
<key>Resources/lcd-topmid.tiff</key>
<data>
OXoawx1x1At1w4Mg+PRwY4sY1Mw=
</data>
<key>Resources/lcd-topright.tiff</key>
<data>
IroGNppXkS+YkGnTBOX5bap2Nvs=
</data>
<key>version.plist</key>
<data>
pW4t92cdIGxgxkmnrFMXAEhELDM=
</data>
</dict>
<key>rules</key>
<dict>
<key>@[0-9]+</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>\.licns$</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>50</real>
</dict>
<key>^Resources/</key>
<true/>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>30</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>30</real>
</dict>
<key>^Resources/.*\.nib/classes\.nib$</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>5</real>
</dict>
<key>^Resources/.*\.nib/data\.dependency$</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>5</real>
</dict>
<key>^Resources/.*\.nib/designable\.nib$</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>5</real>
</dict>
<key>^Resources/.*\.nib/info\.nib$</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>5</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>35</real>
</dict>
<key>^Resources/BridgeSupport/[^/]*\.(bridgesupport|dylib)$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^Resources/Calculator.help/</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>50</real>
</dict>
<key>^Resources/English\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>40</real>
</dict>
<key>^version.plist$</key>
<true/>
</dict>
<key>rules2</key>
<dict>
<key>@[0-9]+</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>\.licns$</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>50</real>
</dict>
<key>^(Frameworks|SharedFrameworks|Plugins|Plug-ins|XPCServices|Helpers|MacOS)/</key>
<dict>
<key>nested</key>
<true/>
<key>weight</key>
<real>0.0</real>
</dict>
<key>^Resources/</key>
<true/>
<key>^Resources/.*\.lproj/</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>30</real>
</dict>
<key>^Resources/.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>30</real>
</dict>
<key>^Resources/.*\.nib/classes\.nib$</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>5</real>
</dict>
<key>^Resources/.*\.nib/data\.dependency$</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>5</real>
</dict>
<key>^Resources/.*\.nib/designable\.nib$</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>5</real>
</dict>
<key>^Resources/.*\.nib/info\.nib$</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>5</real>
</dict>
<key>^Resources/Base\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>35</real>
</dict>
<key>^Resources/BridgeSupport/[^/]*\.(bridgesupport|dylib)$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^Resources/Calculator.help/</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>50</real>
</dict>
<key>^Resources/English\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>40</real>
</dict>
<key>^[^/]+$</key>
<dict>
<key>top</key>
<true/>
<key>weight</key>
<real>0.0</real>
</dict>
<key>^version.plist$</key>
<true/>
</dict>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildVersion</key>
<string>80</string>
<key>CFBundleShortVersionString</key>
<string>10.8</string>
<key>CFBundleVersion</key>
<string>123</string>
<key>ProjectName</key>
<string>Calculator</string>
<key>SourceVersion</key>
<string>128000000000000</string>
</dict>
</plist>
...@@ -75,8 +75,6 @@ class SelectFileDialogImpl : public ui::SelectFileDialog { ...@@ -75,8 +75,6 @@ class SelectFileDialogImpl : public ui::SelectFileDialog {
const std::vector<base::FilePath>& files, const std::vector<base::FilePath>& files,
int index); int index);
bool ShouldEnableFilename(NSSavePanel* dialog, NSString* filename);
protected: protected:
// SelectFileDialog implementation. // SelectFileDialog implementation.
// |params| is user data we pass back via the Listener interface. // |params| is user data we pass back via the Listener interface.
...@@ -159,16 +157,6 @@ void SelectFileDialogImpl::FileWasSelected( ...@@ -159,16 +157,6 @@ void SelectFileDialogImpl::FileWasSelected(
} }
} }
bool SelectFileDialogImpl::ShouldEnableFilename(NSSavePanel* dialog,
NSString* filename) {
// If this is a single/multiple open file dialog, disable selecting packages.
if (type_map_[dialog] != SELECT_OPEN_FILE &&
type_map_[dialog] != SELECT_OPEN_MULTI_FILE)
return true;
return ![[NSWorkspace sharedWorkspace] isFilePackageAtPath:filename];
}
void SelectFileDialogImpl::SelectFileImpl( void SelectFileDialogImpl::SelectFileImpl(
Type type, Type type,
const base::string16& title, const base::string16& title,
...@@ -427,9 +415,7 @@ bool SelectFileDialogImpl::HasMultipleFileTypeChoicesImpl() { ...@@ -427,9 +415,7 @@ bool SelectFileDialogImpl::HasMultipleFileTypeChoicesImpl() {
} }
- (BOOL)panel:(id)sender shouldEnableURL:(NSURL *)url { - (BOOL)panel:(id)sender shouldEnableURL:(NSURL *)url {
if (![url isFileURL]) return [url isFileURL];
return NO;
return selectFileDialogImpl_->ShouldEnableFilename(sender, [url path]);
} }
@end @end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment