summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/chromeos/extensions/file_browser_private_api.cc287
-rw-r--r--chrome/browser/chromeos/extensions/file_browser_private_api.h22
-rw-r--r--chrome/browser/chromeos/extensions/file_handler_util.cc277
-rw-r--r--chrome/browser/chromeos/extensions/file_handler_util.h55
-rw-r--r--chrome/browser/chromeos/gdata/drive_file_system.cc1
-rw-r--r--chrome/browser/resources/file_manager/js/file_manager.js19
-rw-r--r--chrome/browser/resources/file_manager/js/file_tasks.js10
-rw-r--r--chrome/browser/ui/browser_ui_prefs.cc9
8 files changed, 462 insertions, 218 deletions
diff --git a/chrome/browser/chromeos/extensions/file_browser_private_api.cc b/chrome/browser/chromeos/extensions/file_browser_private_api.cc
index cb21952..151e244 100644
--- a/chrome/browser/chromeos/extensions/file_browser_private_api.cc
+++ b/chrome/browser/chromeos/extensions/file_browser_private_api.cc
@@ -91,16 +91,6 @@ const net::UnescapeRule::Type kUnescapeRuleForQueryParameters =
net::UnescapeRule::URL_SPECIAL_CHARS |
net::UnescapeRule::REPLACE_PLUS_WITH_SPACE;
-ListValue* URLPatternSetToStringList(const URLPatternSet& patterns) {
- ListValue* list = new ListValue();
- for (URLPatternSet::const_iterator it = patterns.begin();
- it != patterns.end(); ++it) {
- list->Append(new StringValue(it->GetAsString()));
- }
-
- return list;
-}
-
const DiskMountManager::Disk* GetVolumeAsDisk(const std::string& mount_path) {
DiskMountManager* disk_mount_manager = DiskMountManager::GetInstance();
@@ -226,16 +216,6 @@ void AddGDataMountPoint(
provider->GrantFileAccessToExtension(extension_id, mount_point_virtual);
}
-// Given a file url, find the virtual FilePath associated with it.
-FilePath GetVirtualPathFromURL(const GURL& file_url) {
- fileapi::FileSystemURL url(file_url);
- if (!chromeos::CrosMountPointProvider::CanHandleURL(url)) {
- NOTREACHED();
- return FilePath();
- }
- return url.virtual_path();
-}
-
// Finds an icon in the list of icons. If unable to find an icon of the exact
// size requested, returns one with the next larger size. If all icons are
// smaller than the preferred size, we'll return the largest one available.
@@ -274,6 +254,85 @@ void GetSizeStatsOnFileThread(const std::string& mount_path,
*remaining_size_kb = static_cast<size_t>(remaining_size_in_bytes / 1024);
}
+// Given a |url| and a file system type |desired_type|, return the virtual
+// FilePath associated with it. If the file isn't of the |desired_type| or can't
+// be parsed, then return an empty FilePath.
+FilePath GetVirtualPathFromURL(const GURL& url) {
+ fileapi::FileSystemURL filesystem_url(url);
+ if (!filesystem_url.is_valid() ||
+ (filesystem_url.type() != fileapi::kFileSystemTypeDrive &&
+ filesystem_url.type() != fileapi::kFileSystemTypeNativeMedia &&
+ filesystem_url.type() != fileapi::kFileSystemTypeNativeLocal)) {
+ return FilePath();
+ }
+ return filesystem_url.virtual_path();
+}
+
+// Make a set of unique filename suffixes out of the list of file URLs.
+std::set<std::string> GetUniqueSuffixes(base::ListValue* file_url_list) {
+ std::set<std::string> suffixes;
+ for (size_t i = 0; i < file_url_list->GetSize(); ++i) {
+ std::string url;
+ if (!file_url_list->GetString(i, &url))
+ return std::set<std::string>();
+ FilePath path = GetVirtualPathFromURL(GURL(url));
+ if (path.empty())
+ return std::set<std::string>();
+ // We'll skip empty suffixes.
+ if (!path.Extension().empty())
+ suffixes.insert(path.Extension());
+ }
+ return suffixes;
+}
+
+// Make a set of unique MIME types out of the list of MIME types.
+std::set<std::string> GetUniqueMimeTypes(base::ListValue* mime_type_list) {
+ std::set<std::string> mime_types;
+ for (size_t i = 0; i < mime_type_list->GetSize(); ++i) {
+ std::string mime_type;
+ if (!mime_type_list->GetString(i, &mime_type))
+ return std::set<std::string>();
+ // We'll skip empty MIME types.
+ if (!mime_type.empty())
+ mime_types.insert(mime_type);
+ }
+ return mime_types;
+}
+
+void LogDefaultTask(const std::set<std::string>& mime_types,
+ const std::set<std::string>& suffixes,
+ const std::string& task_id) {
+ if (!mime_types.empty()) {
+ std::string mime_types_str;
+ for (std::set<std::string>::const_iterator iter = mime_types.begin();
+ iter != mime_types.end(); ++iter) {
+ if (iter == mime_types.begin()) {
+ mime_types_str = *iter;
+ } else {
+ mime_types_str += ", " + *iter;
+ }
+ }
+ VLOG(1) << "Associating task " << task_id
+ << " with the following MIME types: ";
+ VLOG(1) << " " << mime_types_str;
+ }
+
+ if (!suffixes.empty()) {
+ std::string suffixes_str;
+ for (std::set<std::string>::const_iterator iter = suffixes.begin();
+ iter != suffixes.end(); ++iter) {
+ if (iter == suffixes.begin()) {
+ suffixes_str = *iter;
+ } else {
+ suffixes_str += ", " + *iter;
+ }
+ }
+ VLOG(1) << "Associating task " << task_id
+ << " with the following suffixes: ";
+ VLOG(1) << " " << suffixes_str;
+ }
+}
+
} // namespace
class RequestLocalFileSystemFunction::LocalFileSystemCallbackDispatcher {
@@ -524,7 +583,7 @@ void GetFileTasksFileBrowserFunction::IntersectAvailableDriveTasks(
gdata::DriveWebAppsRegistry* registry,
const FileInfoList& file_info_list,
WebAppInfoMap* app_info,
- std::set<std::string>* available_apps) {
+ std::set<std::string>* available_tasks) {
for (FileInfoList::const_iterator file_iter = file_info_list.begin();
file_iter != file_info_list.end(); ++file_iter) {
if (file_iter->file_path.empty())
@@ -534,28 +593,49 @@ void GetFileTasksFileBrowserFunction::IntersectAvailableDriveTasks(
file_iter->mime_type, &info);
std::vector<gdata::DriveWebAppInfo*> info_ptrs;
info.release(&info_ptrs); // so they don't go away prematurely.
- std::set<std::string> apps_for_this_file;
+ std::set<std::string> tasks_for_this_file;
for (std::vector<gdata::DriveWebAppInfo*>::iterator
- apps = info_ptrs.begin(); apps != info_ptrs.end(); ++apps) {
+ apps = info_ptrs.begin(); apps != info_ptrs.end(); ++apps) {
std::pair<WebAppInfoMap::iterator, bool> insert_result =
app_info->insert(std::make_pair((*apps)->app_id, *apps));
- apps_for_this_file.insert((*apps)->app_id);
- // If we failed to insert an app_id because there was a duplicate, then we
- // must delete it (since we own it).
- if (!insert_result.second)
- delete *apps;
+ // TODO(gspencer): For now, the action id is always "open-with", but we
+ // could add any actions that the drive app supports.
+ std::string task_id =
+ file_handler_util::MakeDriveTaskID((*apps)->app_id, "open-with");
+ tasks_for_this_file.insert(task_id);
+ // If we failed to insert a task_id because there was a duplicate, then we
+ // must delete it (since we own it).
+ if (!insert_result.second)
+ delete *apps;
}
if (file_iter == file_info_list.begin()) {
- *available_apps = apps_for_this_file;
+ *available_tasks = tasks_for_this_file;
} else {
std::set<std::string> intersection;
- std::set_intersection(available_apps->begin(),
- available_apps->end(),
- apps_for_this_file.begin(),
- apps_for_this_file.end(),
+ std::set_intersection(available_tasks->begin(),
+ available_tasks->end(),
+ tasks_for_this_file.begin(),
+ tasks_for_this_file.end(),
std::inserter(intersection,
intersection.begin()));
- *available_apps = intersection;
+ *available_tasks = intersection;
+ }
+ }
+}
+
+void GetFileTasksFileBrowserFunction::FindDefaultDriveTasks(
+ const FileInfoList& file_info_list,
+ const std::set<std::string>& available_tasks,
+ std::set<std::string>* default_tasks) {
+ std::set<std::string> default_task_ids;
+ for (FileInfoList::const_iterator file_iter = file_info_list.begin();
+ file_iter != file_info_list.end(); ++file_iter) {
+ std::string task_id = file_handler_util::GetDefaultTaskIdFromPrefs(
+ profile_, file_iter->mime_type, file_iter->file_path.Extension());
+ if (available_tasks.find(task_id) != available_tasks.end()) {
+ VLOG(1) << "Found default task for " << file_iter->file_path.value()
+ << ": " << task_id;
+ default_tasks->insert(task_id);
}
}
}
@@ -564,40 +644,43 @@ void GetFileTasksFileBrowserFunction::IntersectAvailableDriveTasks(
void GetFileTasksFileBrowserFunction::CreateDriveTasks(
gdata::DriveWebAppsRegistry* registry,
const WebAppInfoMap& app_info,
- const std::set<std::string>& available_apps,
- ListValue* result_list) {
+ const std::set<std::string>& available_tasks,
+ const std::set<std::string>& default_tasks,
+ ListValue* result_list,
+ bool* default_already_set) {
+ *default_already_set = false;
// OK, now we traverse the intersection of available applications for this
// list of files, adding a task for each one that is found.
- for (std::set<std::string>::iterator app_iter = available_apps.begin();
- app_iter != available_apps.end(); ++app_iter) {
- WebAppInfoMap::const_iterator info_iter = app_info.find(*app_iter);
+ for (std::set<std::string>::const_iterator app_iter = available_tasks.begin();
+ app_iter != available_tasks.end(); ++app_iter) {
+ std::string app_id;
+ bool result = file_handler_util::CrackDriveTaskID(*app_iter, &app_id, NULL);
+ DCHECK(result) << "Unable to parse Drive task id: " << *app_iter;
+ if (!result)
+ continue;
+ WebAppInfoMap::const_iterator info_iter = app_info.find(app_id);
DCHECK(info_iter != app_info.end());
gdata::DriveWebAppInfo* info = info_iter->second;
DictionaryValue* task = new DictionaryValue;
- // TODO(gspencer): For now, the action id is always "open-with", but we
- // could add any actions that the drive app supports.
- std::string task_id =
- file_handler_util::MakeDriveTaskID(*app_iter, "open-with");
- task->SetString("taskId", task_id);
+
+ task->SetString("taskId", *app_iter);
task->SetString("title", info->app_name);
- // Create the list of extensions as patterns registered for this
- // application. (Extensions here refers to filename suffixes (extensions),
- // not Chrome or Drive extensions.)
- ListValue* pattern_list = new ListValue;
- std::set<std::string> extensions =
- registry->GetExtensionsForWebStoreApp(*app_iter);
- for (std::set<std::string>::iterator ext_iter = extensions.begin();
- ext_iter != extensions.end(); ++ext_iter) {
- pattern_list->Append(new StringValue("filesystem:*." + *ext_iter));
- }
- task->Set("patterns", pattern_list);
GURL best_icon = FindPreferredIcon(info->app_icons,
kPreferredIconSize);
if (!best_icon.is_empty()) {
task->SetString("iconUrl", best_icon.spec());
}
task->SetBoolean("driveApp", true);
+
+ // Once we set a default app, we don't want to set any more.
+ if (!(*default_already_set) &&
+ default_tasks.find(*app_iter) != default_tasks.end()) {
+ task->SetBoolean("isDefault", true);
+ *default_already_set = true;
+ } else {
+ task->SetBoolean("isDefault", false);
+ }
result_list->Append(task);
}
}
@@ -608,7 +691,8 @@ void GetFileTasksFileBrowserFunction::CreateDriveTasks(
// begin with kDriveTaskExtensionPrefix.
bool GetFileTasksFileBrowserFunction::FindDriveAppTasks(
const FileInfoList& file_info_list,
- ListValue* result_list) {
+ ListValue* result_list,
+ bool* default_already_set) {
if (file_info_list.empty())
return true;
@@ -629,11 +713,14 @@ bool GetFileTasksFileBrowserFunction::FindDriveAppTasks(
std::map<std::string, gdata::DriveWebAppInfo*> app_info;
// Set of application IDs. This will end up with the intersection of the
// application IDs that apply to the paths in |file_paths|.
- std::set<std::string> available_apps;
+ std::set<std::string> available_tasks;
IntersectAvailableDriveTasks(registry, file_info_list,
- &app_info, &available_apps);
- CreateDriveTasks(registry, app_info, available_apps, result_list);
+ &app_info, &available_tasks);
+ std::set<std::string> default_tasks;
+ FindDefaultDriveTasks(file_info_list, available_tasks, &default_tasks);
+ CreateDriveTasks(registry, app_info, available_tasks, default_tasks,
+ result_list, default_already_set);
// We own the pointers in |app_info|, so we need to delete them.
STLDeleteContainerPairSecondPointers(app_info.begin(), app_info.end());
@@ -682,16 +769,31 @@ bool GetFileTasksFileBrowserFunction::RunImpl() {
ListValue* result_list = new ListValue();
SetResult(result_list);
- file_handler_util::LastUsedHandlerList common_tasks;
+ // Find the Drive apps first, because we want them to take precedence
+ // when setting the default app.
+ bool default_already_set = false;
+ if (!FindDriveAppTasks(info_list, result_list, &default_already_set))
+ return false;
+
+ // Take the union of Drive and extension tasks: Because any Drive tasks we
+ // found must apply to all of the files (intersection), and because the same
+ // is true of the extensions, we simply take the union of two lists by adding
+ // the extension tasks to the Drive task list. We know there aren't duplicates
+ // because they're entirely different kinds of tasks, but there could be both
+ // kinds of tasks for a file type (an image file, for instance).
+ std::set<const FileBrowserHandler*> common_tasks;
+ std::set<const FileBrowserHandler*> default_tasks;
if (!file_handler_util::FindCommonTasks(profile_, file_urls, &common_tasks))
return false;
+ file_handler_util::FindDefaultTasks(profile_, file_urls,
+ common_tasks, &default_tasks);
ExtensionService* service = profile_->GetExtensionService();
- for (file_handler_util::LastUsedHandlerList::const_iterator iter =
+ for (std::set<const FileBrowserHandler*>::const_iterator iter =
common_tasks.begin();
iter != common_tasks.end();
++iter) {
- const FileBrowserHandler* handler = iter->handler;
+ const FileBrowserHandler* handler = *iter;
const std::string extension_id = handler->extension_id();
const Extension* extension = service->GetExtensionById(extension_id, false);
CHECK(extension);
@@ -699,7 +801,6 @@ bool GetFileTasksFileBrowserFunction::RunImpl() {
task->SetString("taskId",
file_handler_util::MakeTaskID(extension_id, handler->id()));
task->SetString("title", handler->title());
- task->Set("patterns", URLPatternSetToStringList(iter->patterns));
// TODO(zelidrag): Figure out how to expose icon URL that task defined in
// manifest instead of the default extension icon.
GURL icon =
@@ -709,18 +810,19 @@ bool GetFileTasksFileBrowserFunction::RunImpl() {
false, NULL); // grayscale
task->SetString("iconUrl", icon.spec());
task->SetBoolean("driveApp", false);
+
+ // Only set the default if there isn't already a default set.
+ if (!default_already_set &&
+ default_tasks.find(*iter) != default_tasks.end()) {
+ task->SetBoolean("isDefault", true);
+ default_already_set = true;
+ } else {
+ task->SetBoolean("isDefault", false);
+ }
+
result_list->Append(task);
}
- // Take the union of Drive and extension tasks: Because any extension tasks we
- // found must apply to all of the files (intersection), and because the same
- // is true of the drive apps, we simply take the union of two lists by adding
- // the drive tasks to the extension task list. We know there aren't duplicates
- // because they're entirely different kinds of tasks, but there could be both
- // kinds of tasks for a file type (an image file, for instance).
- if (!FindDriveAppTasks(info_list, result_list))
- return false;
-
if (VLOG_IS_ON(1)) {
std::string result_json;
base::JSONWriter::WriteWithOptions(
@@ -805,11 +907,39 @@ bool SetDefaultTaskFileBrowserFunction::RunImpl() {
if (!args_->GetString(0, &task_id) || !task_id.size())
return false;
+ base::ListValue* file_url_list;
+ if (!args_->GetList(1, &file_url_list))
+ return false;
+ std::set<std::string> suffixes = GetUniqueSuffixes(file_url_list);
+
+ // MIME types are an optional parameter.
+ base::ListValue* mime_type_list;
+ std::set<std::string> mime_types;
+ if (args_->GetList(2, &mime_type_list) && !mime_type_list->empty()) {
+ if (mime_type_list->GetSize() != file_url_list->GetSize())
+ return false;
+ mime_types = GetUniqueMimeTypes(mime_type_list);
+ }
+
+ if (VLOG_IS_ON(1))
+ LogDefaultTask(mime_types, suffixes, task_id);
+
+ // If there weren't any mime_types, and all the suffixes were blank,
+ // then we "succeed", but don't actually associate with anything.
+ // Otherwise, any time we set the default on a file with no extension
+ // on the local drive, we'd fail.
+ // TODO(gspencer): Fix file manager so that it never tries to set default in
+ // cases where extensionless local files are part of the selection.
+ if (suffixes.empty() && mime_types.empty()) {
+ SetResult(new base::FundamentalValue(true));
+ return true;
+ }
+
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(
- &file_handler_util::UpdateFileHandlerUsageStats,
- profile_, task_id));
+ &file_handler_util::UpdateDefaultTask,
+ profile_, task_id, suffixes, mime_types));
SetResult(new base::FundamentalValue(true));
return true;
@@ -1868,6 +1998,13 @@ void GetGDataFilePropertiesFunction::OnOperationComplete(
system_service->webapps_registry()->GetWebAppsForFile(
file_path, file_specific_info.content_mime_type(), &web_apps);
if (!web_apps.empty()) {
+ std::string default_task_id = file_handler_util::GetDefaultTaskIdFromPrefs(
+ profile_,
+ file_specific_info.content_mime_type(),
+ file_path.Extension());
+ std::string default_app_id;
+ file_handler_util::CrackDriveTaskID(default_task_id, &default_app_id, NULL);
+
ListValue* apps = new ListValue();
property_dict->Set("driveApps", apps);
for (ScopedVector<gdata::DriveWebAppInfo>::const_iterator it =
@@ -1886,7 +2023,7 @@ void GetGDataFilePropertiesFunction::OnOperationComplete(
if (!doc_icon.is_empty())
app->SetString("docIcon", doc_icon.spec());
app->SetString("objectType", webapp_info->object_type);
- app->SetBoolean("isPrimary", webapp_info->is_primary_selector);
+ app->SetBoolean("isPrimary", default_app_id == webapp_info->app_id);
apps->Append(app);
}
}
diff --git a/chrome/browser/chromeos/extensions/file_browser_private_api.h b/chrome/browser/chromeos/extensions/file_browser_private_api.h
index eb953e8..c390048 100644
--- a/chrome/browser/chromeos/extensions/file_browser_private_api.h
+++ b/chrome/browser/chromeos/extensions/file_browser_private_api.h
@@ -148,15 +148,27 @@ class GetFileTasksFileBrowserFunction : public AsyncExtensionFunction {
// Takes a map of app_id to application information in |app_info|, and the set
// of |available_apps| and adds Drive tasks to the |result_list| for each of
- // the |available_apps|.
+ // the |available_apps|. If a default task is set in the result list,
+ // then |default_already_set| is set to true.
static void CreateDriveTasks(gdata::DriveWebAppsRegistry* registry,
const WebAppInfoMap& app_info,
const std::set<std::string>& available_apps,
- ListValue* result_list);
-
- // Find the list of drive apps that can be used with the given file types.
+ const std::set<std::string>& default_apps,
+ ListValue* result_list,
+ bool* default_already_set);
+
+ // Looks in the preferences and finds any of the available apps that are
+ // also listed as default apps for any of the files in the info list.
+ void FindDefaultDriveTasks(const FileInfoList& file_info_list,
+ const std::set<std::string>& available_apps,
+ std::set<std::string>* default_apps);
+
+ // Find the list of drive apps that can be used with the given file types. If
+ // a default task is set in the result list, then |default_already_set| is set
+ // to true.
bool FindDriveAppTasks(const FileInfoList& file_info_list,
- ListValue* result_list);
+ ListValue* result_list,
+ bool* default_already_set);
};
diff --git a/chrome/browser/chromeos/extensions/file_handler_util.cc b/chrome/browser/chromeos/extensions/file_handler_util.cc
index 60f7c6e..482dfe4 100644
--- a/chrome/browser/chromeos/extensions/file_handler_util.cc
+++ b/chrome/browser/chromeos/extensions/file_handler_util.cc
@@ -54,6 +54,7 @@ const size_t FileTaskExecutor::kDriveTaskExtensionPrefixLength =
arraysize(FileTaskExecutor::kDriveTaskExtensionPrefix) - 1;
namespace {
+typedef std::set<const FileBrowserHandler*> FileBrowserHandlerSet;
const int kReadWriteFilePermissions = base::PLATFORM_FILE_OPEN |
base::PLATFORM_FILE_CREATE |
@@ -87,26 +88,6 @@ int ExtractProcessFromExtensionId(const std::string& extension_id,
return process->GetID();
}
-URLPatternSet GetAllMatchingPatterns(const FileBrowserHandler* handler,
- const std::vector<GURL>& files_list) {
- URLPatternSet matching_patterns;
- const URLPatternSet& patterns = handler->file_url_patterns();
- for (URLPatternSet::const_iterator pattern_it = patterns.begin();
- pattern_it != patterns.end(); ++pattern_it) {
- for (std::vector<GURL>::const_iterator file_it = files_list.begin();
- file_it != files_list.end(); ++file_it) {
- if (pattern_it->MatchesURL(*file_it)) {
- matching_patterns.AddPattern(*pattern_it);
- break;
- }
- }
- }
-
- return matching_patterns;
-}
-
-typedef std::set<const FileBrowserHandler*> ActionSet;
-
const FileBrowserHandler* FindFileBrowserHandler(const Extension* extension,
const std::string& action_id) {
for (Extension::FileBrowserHandlerList::const_iterator action_iter =
@@ -145,7 +126,7 @@ std::string EscapedUtf8ToLower(const std::string& str) {
bool GetFileBrowserHandlers(Profile* profile,
const GURL& selected_file_url,
- ActionSet* results) {
+ FileBrowserHandlerSet* results) {
ExtensionService* service = profile->GetExtensionService();
if (!service)
return false; // In unit-tests, we may not have an ExtensionService.
@@ -178,38 +159,69 @@ bool GetFileBrowserHandlers(Profile* profile,
return true;
}
-bool SortByLastUsedTimestampDesc(const LastUsedHandler& a,
- const LastUsedHandler& b) {
- return a.timestamp > b.timestamp;
-}
+} // namespace
-// TODO(zelidrag): Wire this with ICU to make this sort I18N happy.
-bool SortByTaskName(const LastUsedHandler& a, const LastUsedHandler& b) {
- return base::strcasecmp(a.handler->title().c_str(),
- b.handler->title().c_str()) > 0;
-}
+void UpdateDefaultTask(Profile* profile,
+ const std::string& task_id,
+ const std::set<std::string>& suffixes,
+ const std::set<std::string>& mime_types) {
+ if (!profile || !profile->GetPrefs())
+ return;
-void SortLastUsedHandlerList(LastUsedHandlerList *list) {
- // Sort by the last used descending.
- std::sort(list->begin(), list->end(), SortByLastUsedTimestampDesc);
- if (list->size() > 1) {
- // Sort the rest by name.
- std::sort(list->begin() + 1, list->end(), SortByTaskName);
+ if (!mime_types.empty()) {
+ DictionaryPrefUpdate mime_type_pref(profile->GetPrefs(),
+ prefs::kDefaultTasksByMimeType);
+ for (std::set<std::string>::const_iterator iter = mime_types.begin();
+ iter != mime_types.end(); ++iter) {
+ base::StringValue* value = new base::StringValue(task_id);
+ mime_type_pref->SetWithoutPathExpansion(*iter, value);
+ }
+ }
+
+ if (!suffixes.empty()) {
+ DictionaryPrefUpdate mime_type_pref(profile->GetPrefs(),
+ prefs::kDefaultTasksBySuffix);
+ for (std::set<std::string>::const_iterator iter = suffixes.begin();
+ iter != suffixes.end(); ++iter) {
+ base::StringValue* value = new base::StringValue(task_id);
+ // Suffixes are case insensitive.
+ std::string lower_suffix = StringToLowerASCII(*iter);
+ mime_type_pref->SetWithoutPathExpansion(lower_suffix, value);
+ }
}
}
-} // namespace
+std::string GetDefaultTaskIdFromPrefs(Profile* profile,
+ const std::string& mime_type,
+ const std::string& suffix) {
+ VLOG(1) << "Looking for default for MIME type: " << mime_type
+ << " and suffix: " << suffix;
+ std::string task_id;
+ if (!mime_type.empty()) {
+ const DictionaryValue* mime_task_prefs =
+ profile->GetPrefs()->GetDictionary(prefs::kDefaultTasksByMimeType);
+ DCHECK(mime_task_prefs);
+ if (!mime_task_prefs) {
+ LOG(WARNING) << "Unable to open MIME type prefs";
+ return std::string();
+ }
+ if (mime_task_prefs->GetStringWithoutPathExpansion(mime_type, &task_id)) {
+ VLOG(1) << "Found MIME default handler: " << task_id;
+ return task_id;
+ }
+ }
-// Update file handler usage stats.
-void UpdateFileHandlerUsageStats(Profile* profile, const std::string& task_id) {
- if (!profile || !profile->GetPrefs())
- return;
- DictionaryPrefUpdate prefs_usage_update(profile->GetPrefs(),
- prefs::kLastUsedFileBrowserHandlers);
- prefs_usage_update->SetWithoutPathExpansion(task_id,
- new base::FundamentalValue(
- static_cast<int>(base::Time::Now().ToInternalValue()/
- base::Time::kMicrosecondsPerSecond)));
+ const DictionaryValue* suffix_task_prefs =
+ profile->GetPrefs()->GetDictionary(prefs::kDefaultTasksBySuffix);
+ DCHECK(suffix_task_prefs);
+ if (!suffix_task_prefs) {
+ LOG(WARNING) << "Unable to open suffix prefs";
+ return std::string();
+ }
+ std::string lower_suffix = StringToLowerASCII(suffix);
+ suffix_task_prefs->GetStringWithoutPathExpansion(lower_suffix, &task_id);
+ VLOG_IF(1, !task_id.empty()) << "Found suffix default handler: " << task_id;
+ return task_id;
}
int GetReadWritePermissions() {
@@ -231,11 +243,31 @@ std::string MakeDriveTaskID(const std::string& app_id,
action_id);
}
+bool CrackDriveTaskID(const std::string& task_id,
+ std::string* app_id,
+ std::string* action_id) {
+ std::string app_id_tmp;
+ std::string action_id_tmp;
+ if (!CrackTaskID(task_id, &app_id_tmp, &action_id_tmp))
+ return false;
+ if (StartsWithASCII(app_id_tmp,
+ FileTaskExecutor::kDriveTaskExtensionPrefix,
+ false)) {
+ // Strip off the prefix from the extension ID so we convert it to an app id.
+ if (app_id) {
+ *app_id = app_id_tmp.substr(
+ FileTaskExecutor::kDriveTaskExtensionPrefixLength);
+ }
+ if (action_id)
+ *action_id = action_id_tmp;
+ return true;
+ }
+ return false;
+}
// Breaks down task_id that is used between getFileTasks() and executeTask() on
// its building blocks. task_id field the following structure:
// <extension-id>|<task-action-id>
-// Currently, the only supported task-type is of 'context'.
bool CrackTaskID(const std::string& task_id,
std::string* extension_id,
std::string* action_id) {
@@ -243,96 +275,121 @@ bool CrackTaskID(const std::string& task_id,
int count = Tokenize(task_id, std::string("|"), &result);
if (count != 2)
return false;
- *extension_id = result[0];
- *action_id = result[1];
+ if (extension_id)
+ *extension_id = result[0];
+ if (action_id)
+ *action_id = result[1];
return true;
}
// Find a specific handler in the handler list.
-LastUsedHandlerList::iterator FindHandler(
- LastUsedHandlerList* list,
+FileBrowserHandlerSet::iterator FindHandler(
+ FileBrowserHandlerSet* handler_set,
const std::string& extension_id,
const std::string& id) {
- LastUsedHandlerList::iterator iter = list->begin();
- while (iter != list->end() &&
- !(iter->handler->extension_id() == extension_id &&
- iter->handler->id() == id)) {
+ FileBrowserHandlerSet::iterator iter = handler_set->begin();
+ while (iter != handler_set->end() &&
+ !((*iter)->extension_id() == extension_id &&
+ (*iter)->id() == id)) {
iter++;
}
return iter;
}
+void FindDefaultTasks(Profile* profile,
+ const std::vector<GURL>& files_list,
+ const FileBrowserHandlerSet& common_tasks,
+ FileBrowserHandlerSet* default_tasks) {
+ DCHECK(default_tasks);
+ default_tasks->clear();
+
+ std::set<std::string> default_ids;
+ for (std::vector<GURL>::const_iterator it = files_list.begin();
+ it != files_list.end(); ++it) {
+ // Get the default task for this file based only on the extension (since
+ // we don't have MIME types here), and add it to the set of default tasks.
+ fileapi::FileSystemURL filesystem_url(*it);
+ if (filesystem_url.is_valid() &&
+ (filesystem_url.type() == fileapi::kFileSystemTypeDrive ||
+ filesystem_url.type() == fileapi::kFileSystemTypeNativeMedia ||
+ filesystem_url.type() == fileapi::kFileSystemTypeNativeLocal)) {
+ std::string task_id = file_handler_util::GetDefaultTaskIdFromPrefs(
+ profile, "", filesystem_url.virtual_path().Extension());
+ if (!task_id.empty())
+ default_ids.insert(task_id);
+ }
+ }
+
+ // Convert the default task IDs collected above to one of the handler pointers
+ // from common_tasks.
+ for (FileBrowserHandlerSet::const_iterator task_iter = common_tasks.begin();
+ task_iter != common_tasks.end(); ++task_iter) {
+ std::string task_id = MakeTaskID((*task_iter)->extension_id(),
+ (*task_iter)->id());
+ for (std::set<std::string>::iterator default_iter = default_ids.begin();
+ default_iter != default_ids.end(); ++default_iter) {
+ if (task_id == *default_iter) {
+ default_tasks->insert(*task_iter);
+ break;
+ }
+ }
+ }
+}
+
// Given the list of selected files, returns array of context menu tasks
// that are shared
bool FindCommonTasks(Profile* profile,
const std::vector<GURL>& files_list,
- LastUsedHandlerList* named_action_list) {
- named_action_list->clear();
- ActionSet common_tasks;
+ FileBrowserHandlerSet* common_tasks) {
+ DCHECK(common_tasks);
+ common_tasks->clear();
+
+ FileBrowserHandlerSet common_task_set;
+ std::set<std::string> default_task_ids;
for (std::vector<GURL>::const_iterator it = files_list.begin();
it != files_list.end(); ++it) {
- ActionSet file_actions;
+ FileBrowserHandlerSet file_actions;
if (!GetFileBrowserHandlers(profile, *it, &file_actions))
return false;
// If there is nothing to do for one file, the intersection of tasks for all
- // files will be empty at the end.
- if (!file_actions.size())
+ // files will be empty at the end, and so will the default tasks.
+ if (file_actions.empty())
return true;
- // For the very first file, just copy elements.
+ // For the very first file, just copy all the elements.
if (it == files_list.begin()) {
- common_tasks = file_actions;
+ common_task_set = file_actions;
} else {
- if (common_tasks.size()) {
- // For all additional files, find intersection between the accumulated
- // and file specific set.
- ActionSet intersection;
- std::set_intersection(common_tasks.begin(), common_tasks.end(),
- file_actions.begin(), file_actions.end(),
- std::inserter(intersection,
- intersection.begin()));
- common_tasks = intersection;
- }
- }
- }
-
- const DictionaryValue* prefs_tasks =
- profile->GetPrefs()->GetDictionary(prefs::kLastUsedFileBrowserHandlers);
- for (ActionSet::const_iterator iter = common_tasks.begin();
- iter != common_tasks.end(); ++iter) {
- // Get timestamp of when this task was used last time.
- int last_used_timestamp = 0;
-
- if ((*iter)->extension_id() == kFileBrowserDomain) {
- // Give a little bump to the action from File Browser extension
- // to make sure it is the default on a fresh profile.
- last_used_timestamp = 1;
+ // For all additional files, find intersection between the accumulated and
+ // file specific set.
+ FileBrowserHandlerSet intersection;
+ std::set_intersection(common_task_set.begin(), common_task_set.end(),
+ file_actions.begin(), file_actions.end(),
+ std::inserter(intersection,
+ intersection.begin()));
+ common_task_set = intersection;
+ if (common_task_set.empty())
+ return true;
}
- prefs_tasks->GetInteger(MakeTaskID((*iter)->extension_id(), (*iter)->id()),
- &last_used_timestamp);
- URLPatternSet matching_patterns = GetAllMatchingPatterns(*iter, files_list);
- named_action_list->push_back(LastUsedHandler(last_used_timestamp, *iter,
- matching_patterns));
}
- LastUsedHandlerList::iterator watch_iter = FindHandler(
- named_action_list, kFileBrowserDomain, kFileBrowserWatchTaskId);
- LastUsedHandlerList::iterator gallery_iter = FindHandler(
- named_action_list, kFileBrowserDomain, kFileBrowserGalleryTaskId);
- if (watch_iter != named_action_list->end() &&
- gallery_iter != named_action_list->end()) {
- // Both "watch" and "gallery" actions are applicable which means that
- // the selection is all videos. Showing them both is confusing. We only keep
+ FileBrowserHandlerSet::iterator watch_iter = FindHandler(
+ &common_task_set, kFileBrowserDomain, kFileBrowserWatchTaskId);
+ FileBrowserHandlerSet::iterator gallery_iter = FindHandler(
+ &common_task_set, kFileBrowserDomain, kFileBrowserGalleryTaskId);
+ if (watch_iter != common_task_set.end() &&
+ gallery_iter != common_task_set.end()) {
+ // Both "watch" and "gallery" actions are applicable which means that the
+ // selection is all videos. Showing them both is confusing, so we only keep
// the one that makes more sense ("watch" for single selection, "gallery"
// for multiple selection).
-
if (files_list.size() == 1)
- named_action_list->erase(gallery_iter);
+ common_task_set.erase(gallery_iter);
else
- named_action_list->erase(watch_iter);
+ common_task_set.erase(watch_iter);
}
- SortLastUsedHandlerList(named_action_list);
+ common_tasks->swap(common_task_set);
return true;
}
@@ -341,14 +398,18 @@ bool GetDefaultTask(
std::vector<GURL> file_urls;
file_urls.push_back(url);
- LastUsedHandlerList common_tasks;
+ FileBrowserHandlerSet default_tasks;
+ FileBrowserHandlerSet common_tasks;
if (!FindCommonTasks(profile, file_urls, &common_tasks))
return false;
+ FindDefaultTasks(profile, file_urls, common_tasks, &default_tasks);
- if (common_tasks.size() == 0)
+ // If there's none, or more than one, then we don't have a canonical default.
+ if (default_tasks.size() != 1)
return false;
- *handler = common_tasks[0].handler;
+ // Return the one and only default task.
+ *handler = *default_tasks.begin();
return true;
}
diff --git a/chrome/browser/chromeos/extensions/file_handler_util.h b/chrome/browser/chromeos/extensions/file_handler_util.h
index 6fb8d25..0df0412 100644
--- a/chrome/browser/chromeos/extensions/file_handler_util.h
+++ b/chrome/browser/chromeos/extensions/file_handler_util.h
@@ -10,16 +10,29 @@
#include "base/callback.h"
#include "base/platform_file.h"
#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/file_browser_handler.h"
#include "chrome/common/extensions/url_pattern_set.h"
class Browser;
-class FileBrowserHandler;
class GURL;
class Profile;
namespace file_handler_util {
-void UpdateFileHandlerUsageStats(Profile* profile, const std::string& task_id);
+// Update the default file handler for the given sets of suffixes and MIME
+// types.
+void UpdateDefaultTask(Profile* profile,
+ const std::string& task_id,
+ const std::set<std::string>& suffixes,
+ const std::set<std::string>& mime_types);
+
+// Returns the task ID of the default task for the given |mime_type|/|suffix|
+// combination. If it finds a MIME type match, then it prefers that over a
+// suffix match. If it a default can't be found, then it returns the empty
+// string.
+std::string GetDefaultTaskIdFromPrefs(Profile* profile,
+ const std::string& mime_type,
+ const std::string& suffix);
// Gets read-write file access permission flags.
int GetReadWritePermissions();
@@ -34,32 +47,32 @@ std::string MakeTaskID(const std::string& extension_id,
std::string MakeDriveTaskID(const std::string& app_id,
const std::string& action_id);
-// Extracts action and extension id bound to the file task.
+// Returns the |target_id| and |action_id| of a drive app or extension, given
+// the drive |task_id| created by MakeDriveTaskID. If the |task_id| is a drive
+// task_id then it will return true. If not, or if parsing fails, will return
+// false and not set |app_id| or |action_id|.
+bool CrackDriveTaskID(const std::string& task_id,
+ std::string* app_id,
+ std::string* action_id);
+
+// Extracts action and extension id bound to the file task. Either
+// |target_extension_id| or |action_id| are allowed to be NULL if caller isn't
+// interested in those values. Returns false on failure to parse.
bool CrackTaskID(const std::string& task_id,
std::string* target_extension_id,
std::string* action_id);
-// Struct that keeps track of when a handler was `last used.
-// It is used to determine default file action for a file.
-struct LastUsedHandler {
- LastUsedHandler(int t, const FileBrowserHandler* h, URLPatternSet p)
- : timestamp(t),
- handler(h),
- patterns(p) {
- }
-
- int timestamp;
- const FileBrowserHandler* handler;
- URLPatternSet patterns;
-};
-
-typedef std::vector<LastUsedHandler> LastUsedHandlerList;
+// This generates a list of default tasks (tasks set as default by the user in
+// prefs) from the |common_tasks|.
+void FindDefaultTasks(Profile* profile,
+ const std::vector<GURL>& files_list,
+ const std::set<const FileBrowserHandler*>& common_tasks,
+ std::set<const FileBrowserHandler*>* default_tasks);
-// Generates list of tasks common for all files in |file_list|.
-// The resulting list is sorted by last usage time.
+// This generates list of tasks common for all files in |file_list|.
bool FindCommonTasks(Profile* profile,
const std::vector<GURL>& files_list,
- LastUsedHandlerList* named_action_list);
+ std::set<const FileBrowserHandler*>* common_tasks);
// Find the default task for a file whose url is |url|. (The default task is the
// task that is assigned to file browser task button by default).
diff --git a/chrome/browser/chromeos/gdata/drive_file_system.cc b/chrome/browser/chromeos/gdata/drive_file_system.cc
index 046610f..787d292 100644
--- a/chrome/browser/chromeos/gdata/drive_file_system.cc
+++ b/chrome/browser/chromeos/gdata/drive_file_system.cc
@@ -2988,6 +2988,7 @@ void DriveFileSystem::OnGetEntryInfoCompleteForOpenFile(
DriveFileError error,
scoped_ptr<DriveEntryProto> entry_proto) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(entry_proto.get() || error != DRIVE_FILE_OK);
if (entry_proto.get() && !entry_proto->has_file_specific_info())
error = DRIVE_FILE_ERROR_NOT_FOUND;
diff --git a/chrome/browser/resources/file_manager/js/file_manager.js b/chrome/browser/resources/file_manager/js/file_manager.js
index ccae658..5c3ddf0 100644
--- a/chrome/browser/resources/file_manager/js/file_manager.js
+++ b/chrome/browser/resources/file_manager/js/file_manager.js
@@ -1808,11 +1808,21 @@ FileManager.prototype = {
}
if (gdata.driveApps.length > 0) {
- var url = gdata.driveApps[0].docIcon;
var iconDiv = listItem.querySelector('.detail-icon');
- if (url && iconDiv) {
- iconDiv.style.backgroundImage = 'url(' + url + ')';
+ if (!iconDiv)
+ return;
+ // Find the default app for this file. If there is none, then
+ // leave it as the base icon for the file type.
+ var url;
+ for (var i = 0; i < gdata.driveApps.length; ++i) {
+ var app = gdata.driveApps[i];
+ if (app && app.docIcon && app.isPrimary) {
+ url = app.docIcon;
+ break;
+ }
}
+ if (url)
+ iconDiv.style.backgroundImage = 'url(' + url + ')';
}
};
@@ -2460,7 +2470,8 @@ FileManager.prototype = {
* @param {Object} task Task to set as default.
*/
FileManager.prototype.onDefaultTaskDone_ = function(task) {
- chrome.fileBrowserPrivate.setDefaultTask(task.taskId);
+ chrome.fileBrowserPrivate.setDefaultTask(task.taskId,
+ this.selection.urls, this.selection.mimeTypes);
this.selection.tasks = new FileTasks(
this, this.selection.urls, this.selection.mimeTypes).
display(this.taskItems_);
diff --git a/chrome/browser/resources/file_manager/js/file_tasks.js b/chrome/browser/resources/file_manager/js/file_tasks.js
index 6107b5a..952f9ff 100644
--- a/chrome/browser/resources/file_manager/js/file_tasks.js
+++ b/chrome/browser/resources/file_manager/js/file_tasks.js
@@ -113,10 +113,16 @@ FileTasks.prototype.processTasks_ = function(tasks) {
}
this.tasks_.push(task);
- if (this.defaultTask_ == null) {
+ if (this.defaultTask_ == null && task.isDefault) {
this.defaultTask_ = task;
}
}
+ if (!this.defaultTask_ && tasks.length > 0) {
+ // If we haven't picked a default task yet, then just pick the first one.
+ // This is not the preferred way we want to pick this, but better this than
+ // no default at all if the C++ code didn't set one.
+ this.defaultTask_ = tasks[0];
+ }
};
/**
@@ -304,7 +310,7 @@ FileTasks.prototype.mountArchives_ = function(urls) {
fm.resolveSelectResults_(urls, function(urls) {
for (var index = 0; index < urls.length; ++index) {
- // TODO(kaznacheev): Incapsulate URL to path conversion.
+ // TODO(kaznacheev): Encapsulate URL to path conversion.
var path =
/^filesystem:[\w-]*:\/\/[\w]*\/(external|persistent)(\/.*)$/.
exec(urls[index])[2];
diff --git a/chrome/browser/ui/browser_ui_prefs.cc b/chrome/browser/ui/browser_ui_prefs.cc
index 19eb514..da8b152 100644
--- a/chrome/browser/ui/browser_ui_prefs.cc
+++ b/chrome/browser/ui/browser_ui_prefs.cc
@@ -141,9 +141,12 @@ void RegisterBrowserUserPrefs(PrefService* prefs) {
prefs->RegisterBooleanPref(prefs::kImportSavedPasswords,
true,
PrefService::UNSYNCABLE_PREF);
- // The map of timestamps of the last used file browser handlers.
- prefs->RegisterDictionaryPref(prefs::kLastUsedFileBrowserHandlers,
- PrefService::UNSYNCABLE_PREF);
+
+ // Dictionaries to keep track of default tasks in the file browser.
+ prefs->RegisterDictionaryPref(prefs::kDefaultTasksByMimeType,
+ PrefService::SYNCABLE_PREF);
+ prefs->RegisterDictionaryPref(prefs::kDefaultTasksBySuffix,
+ PrefService::SYNCABLE_PREF);
// We need to register the type of these preferences in order to query
// them even though they're only typically controlled via policy.