Chromium Code Reviews| Index: chrome/browser/chromeos/gdata/gdata_file_system.cc |
| diff --git a/chrome/browser/chromeos/gdata/gdata_file_system.cc b/chrome/browser/chromeos/gdata/gdata_file_system.cc |
| index bd79a7fada83c8cb2e48f257cbe1f9bfb8cc4cde..7c96d47fd8d7fe21e6c673104b03707eefd70ce7 100644 |
| --- a/chrome/browser/chromeos/gdata/gdata_file_system.cc |
| +++ b/chrome/browser/chromeos/gdata/gdata_file_system.cc |
| @@ -862,12 +862,16 @@ GDataFileSystem::GetDocumentsParams::GetDocumentsParams( |
| int start_changestamp, |
| int root_feed_changestamp, |
| std::vector<DocumentFeed*>* feed_list, |
| + bool should_fetch_multiple_feeds, |
| const FilePath& search_file_path, |
| + const std::string& search_query, |
| const FindEntryCallback& callback) |
| : start_changestamp(start_changestamp), |
| root_feed_changestamp(root_feed_changestamp), |
| feed_list(feed_list), |
| + should_fetch_multiple_feeds(should_fetch_multiple_feeds), |
| search_file_path(search_file_path), |
| + search_query(search_query), |
| callback(callback) { |
| } |
| @@ -1128,7 +1132,13 @@ void GDataFileSystem::OnGetAccountMetadata( |
| base::PlatformFileError error = GDataToPlatformError(status); |
| if (error != base::PLATFORM_FILE_OK) { |
| // Get changes starting from the next changestamp from what we have locally. |
| - LoadFeedFromServer(local_changestamp + 1, 0, search_file_path, callback); |
| + LoadFeedFromServer(local_changestamp + 1, 0, |
| + true, |
|
satorux1
2012/05/04 01:48:40
could you add /* should_fetch_multiple_feeds */
|
| + search_file_path, |
| + std::string() /* no search query */, |
| + callback, |
| + base::Bind(&GDataFileSystem::OnFeedFromServerLoaded, |
| + ui_weak_ptr_)); |
| return; |
| } |
| @@ -1136,7 +1146,13 @@ void GDataFileSystem::OnGetAccountMetadata( |
| if (feed_data.get()) |
| feed = AccountMetadataFeed::CreateFrom(*feed_data); |
| if (!feed.get()) { |
| - LoadFeedFromServer(local_changestamp + 1, 0, search_file_path, callback); |
| + LoadFeedFromServer(local_changestamp + 1, 0, |
| + true, |
|
satorux1
2012/05/04 01:48:40
ditto.
|
| + search_file_path, |
| + std::string() /* no search query */, |
| + callback, |
| + base::Bind(&GDataFileSystem::OnFeedFromServerLoaded, |
| + ui_weak_ptr_)); |
| return; |
| } |
| @@ -1170,15 +1186,22 @@ void GDataFileSystem::OnGetAccountMetadata( |
| // Load changes from the server. |
| LoadFeedFromServer(local_changestamp > 0 ? local_changestamp + 1 : 0, |
| feed->largest_changestamp(), |
| + true, |
|
satorux1
2012/05/04 01:48:40
ditto.
|
| search_file_path, |
| - callback); |
| + std::string() /* no search query */, |
| + callback, |
| + base::Bind(&GDataFileSystem::OnFeedFromServerLoaded, |
| + ui_weak_ptr_)); |
| } |
| void GDataFileSystem::LoadFeedFromServer( |
| int start_changestamp, |
| int root_feed_changestamp, |
| + bool should_fetch_multiple_feeds, |
| const FilePath& search_file_path, |
| - const FindEntryCallback& callback) { |
| + const std::string& search_query, |
| + const FindEntryCallback& entry_found_callback, |
| + const LoadDocumentFeedCallback& feed_load_callback) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| // ...then also kick off document feed fetching from the server as well. |
| @@ -1191,13 +1214,49 @@ void GDataFileSystem::LoadFeedFromServer( |
| documents_service_->GetDocuments( |
| GURL(), // root feed start. |
| start_changestamp, |
| + search_query, |
| base::Bind(&GDataFileSystem::OnGetDocuments, |
| ui_weak_ptr_, |
| + feed_load_callback, |
| base::Owned(new GetDocumentsParams(start_changestamp, |
| root_feed_changestamp, |
| feed_list.release(), |
| + should_fetch_multiple_feeds, |
| search_file_path, |
| - callback)))); |
| + search_query, |
| + entry_found_callback)))); |
| +} |
| + |
| +void GDataFileSystem::OnFeedFromServerLoaded(GetDocumentsParams* params, |
| + base::PlatformFileError error) { |
| + if (error != base::PLATFORM_FILE_OK) { |
| + params->callback.Run(error, FilePath(), |
| + reinterpret_cast<GDataEntry*>(NULL)); |
| + return; |
| + } |
| + |
| + error = UpdateFromFeed(*params->feed_list, |
| + FROM_SERVER, |
| + params->start_changestamp, |
| + params->root_feed_changestamp); |
| + |
| + if (error != base::PLATFORM_FILE_OK) { |
| + if (!params->callback.is_null()) { |
| + params->callback.Run(error, FilePath(), |
| + reinterpret_cast<GDataEntry*>(NULL)); |
| + } |
| + |
| + return; |
| + } |
| + |
| + // Save file system metadata to disk. |
| + SaveFileSystemAsProto(); |
| + |
| + // If we had someone to report this too, then this retrieval was done in a |
| + // context of search... so continue search. |
| + if (!params->callback.is_null()) { |
| + FindEntryByPathSyncOnUIThread(params->search_file_path, params->callback); |
| + } |
| } |
| void GDataFileSystem::TransferFile(const FilePath& local_file_path, |
| @@ -1358,25 +1417,28 @@ void GDataFileSystem::Copy(const FilePath& src_file_path, |
| CopyOnUIThread(src_file_path, dest_file_path, callback); |
| } |
| -void GDataFileSystem::CopyOnUIThread(const FilePath& src_file_path, |
| - const FilePath& dest_file_path, |
| +void GDataFileSystem::CopyOnUIThread(const FilePath& original_src_file_path, |
| + const FilePath& original_dest_file_path, |
| const FileOperationCallback& callback) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| base::PlatformFileError error = base::PLATFORM_FILE_OK; |
| - FilePath dest_parent_path = dest_file_path.DirName(); |
| + FilePath dest_parent_path = original_dest_file_path.DirName(); |
| + |
| + FilePath src_file_path; |
| + FilePath dest_file_path; |
| std::string src_file_resource_id; |
| bool src_file_is_hosted_document = false; |
| { |
| base::AutoLock lock(lock_); |
| - GDataEntry* src_entry = GetGDataEntryByPath(src_file_path); |
| + GDataEntry* src_entry = GetGDataEntryByPath(original_src_file_path); |
| GDataEntry* dest_parent = GetGDataEntryByPath(dest_parent_path); |
| if (!src_entry || !dest_parent) { |
| error = base::PLATFORM_FILE_ERROR_NOT_FOUND; |
| } else if (!dest_parent->AsGDataDirectory()) { |
| error = base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY; |
| - } else if (!src_entry->AsGDataFile()) { |
| + } else if (!src_entry->AsGDataFile() || dest_parent->is_detached()) { |
| // TODO(benchan): Implement copy for directories. In the interim, |
| // we handle recursive directory copy in the file manager. |
| error = base::PLATFORM_FILE_ERROR_INVALID_OPERATION; |
| @@ -1384,9 +1446,21 @@ void GDataFileSystem::CopyOnUIThread(const FilePath& src_file_path, |
| src_file_resource_id = src_entry->resource_id(); |
| src_file_is_hosted_document = |
| src_entry->AsGDataFile()->is_hosted_document(); |
| + // |original_src_file_path| and |original_dest_file_path| don't have to |
| + // necessary be equal to |src_entry|'s or |dest_entry|'s file path (e.g. |
| + // paths used to display gdata content search results). |
| + // That's why, instead of using |original_src_file_path| and |
| + // |original_dest_file_path|, we will get file paths to use in copy |
| + // operation from the entries. |
| + src_file_path = src_entry->GetFilePath(); |
| + dest_parent_path = dest_parent->GetFilePath(); |
| + dest_file_path = dest_parent_path.Append(dest_file_path.BaseName()); |
| } |
| } |
| + DCHECK(!src_file_path.empty()); |
| + DCHECK(!dest_file_path.empty()); |
| + |
| if (error != base::PLATFORM_FILE_OK) { |
| if (!callback.is_null()) |
| MessageLoop::current()->PostTask(FROM_HERE, base::Bind(callback, error)); |
| @@ -1534,24 +1608,42 @@ void GDataFileSystem::Move(const FilePath& src_file_path, |
| MoveOnUIThread(src_file_path, dest_file_path, callback); |
| } |
| -void GDataFileSystem::MoveOnUIThread(const FilePath& src_file_path, |
| - const FilePath& dest_file_path, |
| +void GDataFileSystem::MoveOnUIThread(const FilePath& original_src_file_path, |
| + const FilePath& original_dest_file_path, |
| const FileOperationCallback& callback) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| base::PlatformFileError error = base::PLATFORM_FILE_OK; |
| - FilePath dest_parent_path = dest_file_path.DirName(); |
| + FilePath dest_parent_path = original_dest_file_path.DirName(); |
| + |
| + FilePath src_file_path; |
| + FilePath dest_file_path; |
| + FilePath dest_name = original_dest_file_path.BaseName(); |
| { |
| // This scoped lock needs to be released before calling Rename() below. |
| base::AutoLock lock(lock_); |
| - GDataEntry* src_entry = GetGDataEntryByPath(src_file_path); |
| + GDataEntry* src_entry = GetGDataEntryByPath(original_src_file_path); |
| GDataEntry* dest_parent = GetGDataEntryByPath(dest_parent_path); |
| if (!src_entry || !dest_parent) { |
| error = base::PLATFORM_FILE_ERROR_NOT_FOUND; |
| - } else { |
| - if (!dest_parent->AsGDataDirectory()) |
| + } else if (!dest_parent->AsGDataDirectory()) { |
| error = base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY; |
| + } else if (dest_parent->is_detached()){ |
| + // We allow moving to a directory without file system root only if it's |
| + // done as part of renaming (i.e. source and destination parent paths are |
| + // the same). |
| + if (original_src_file_path.DirName() != dest_parent_path) { |
| + error = base::PLATFORM_FILE_ERROR_INVALID_OPERATION; |
| + } else { |
| + // If we are indeed renaming, we have to strip resource id from the file |
| + // name. |
| + std::string resource_id; |
| + std::string file_name; |
| + util::ParseSearchFileName(dest_name.value(), &resource_id, &file_name); |
| + if (!file_name.empty()) |
| + dest_name = FilePath(file_name); |
| + } |
| } |
| if (error != base::PLATFORM_FILE_OK) { |
| @@ -1561,16 +1653,29 @@ void GDataFileSystem::MoveOnUIThread(const FilePath& src_file_path, |
| } |
| return; |
| } |
| + // |original_src_file_path| and |original_dest_file_path| don't have to |
| + // necessary be equal to |src_entry|'s or |dest_entry|'s file path (e.g. |
| + // paths used to display gdata content search results). |
| + // That's why, instead of using |original_src_file_path| and |
| + // |original_dest_file_path|, we will get file paths to use in move |
| + // operation from the entries. |
| + src_file_path = src_entry->GetFilePath(); |
| + if (!dest_parent->is_detached()) |
| + dest_parent_path = dest_parent->GetFilePath(); |
| + dest_file_path = dest_parent_path.Append(dest_name); |
| } |
| + DCHECK(!src_file_path.empty()); |
| + DCHECK(!dest_file_path.empty()); |
| + |
| // If the file/directory is moved to the same directory, just rename it. |
| - if (src_file_path.DirName() == dest_parent_path) { |
| + if (original_src_file_path.DirName() == dest_parent_path) { |
| FilePathUpdateCallback final_file_path_update_callback = |
| base::Bind(&GDataFileSystem::OnFilePathUpdated, |
| ui_weak_ptr_, |
| callback); |
| - Rename(src_file_path, dest_file_path.BaseName().value(), |
| + Rename(original_src_file_path, dest_name.value(), |
| final_file_path_update_callback); |
| return; |
| } |
| @@ -1720,7 +1825,7 @@ void GDataFileSystem::RemoveOnUIThread( |
| base::Bind(&GDataFileSystem::OnRemovedDocument, |
| ui_weak_ptr_, |
| callback, |
| - file_path)); |
| + entry->GetFilePath())); |
| } |
| void GDataFileSystem::CreateDirectory( |
| @@ -1756,6 +1861,14 @@ void GDataFileSystem::CreateDirectoryOnUIThread( |
| const FileOperationCallback& callback) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + if (!ShouldCreateDirectory(directory_path)) { |
| + if (!callback.is_null()) { |
| + MessageLoop::current()->PostTask(FROM_HERE, |
| + base::Bind(callback, base::PLATFORM_FILE_ERROR_INVALID_OPERATION)); |
| + } |
| + return; |
| + } |
| + |
| FilePath last_parent_dir_path; |
| FilePath first_missing_path; |
| GURL last_parent_dir_url; |
| @@ -2670,7 +2783,93 @@ void GDataFileSystem::OnCreateDirectoryCompleted( |
| } |
| } |
| -void GDataFileSystem::OnGetDocuments(GetDocumentsParams* params, |
| +void GDataFileSystem::OnSearch(const ReadDirectoryCallback& callback, |
| + GetDocumentsParams* params, |
| + base::PlatformFileError error) { |
| + // The search results will be returned using virtual directory. |
| + // The directory is not really part of the file system, so it has no parent or |
| + // root. |
| + scoped_ptr<GDataDirectory> search_dir(new GDataDirectory(NULL, NULL)); |
| + |
| + base::AutoLock lock(lock_); |
| + |
| + int delta_feed_changestamp = 0; |
| + int num_regular_files = 0; |
| + int num_hosted_documents = 0; |
| + FileResourceIdMap file_map; |
| + if (error == base::PLATFORM_FILE_OK) { |
| + error = FeedToFileResourceMap(*params->feed_list, |
| + &file_map, |
| + &delta_feed_changestamp, |
| + &num_regular_files, |
| + &num_hosted_documents); |
| + } |
| + |
| + if (error == base::PLATFORM_FILE_OK) { |
| + std::set<FilePath> ignored; |
| + |
| + // Go through all entires generated by the feed and add them to the search |
| + // result directory. |
| + for (FileResourceIdMap::const_iterator it = file_map.begin(); |
| + it != file_map.end(); ++it) { |
| + scoped_ptr<GDataEntry> entry(it->second); |
| + DCHECK_EQ(it->first, entry->resource_id()); |
| + DCHECK(!entry->is_deleted()); |
| + |
| + entry->set_title(entry->resource_id() + "." + entry->title()); |
| + |
| + search_dir->AddEntry(entry.release()); |
| + } |
| + } |
| + |
| + scoped_ptr<GDataDirectoryProto> directory_proto(new GDataDirectoryProto); |
| + search_dir->ToProto(directory_proto.get()); |
| + |
| + if (!callback.is_null()) { |
| + callback.Run(error, directory_proto.Pass()); |
| + } |
| +} |
| + |
| +void GDataFileSystem::SearchAsync(const std::string& search_query, |
| + const ReadDirectoryCallback& callback) { |
| + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + const bool posted = BrowserThread::PostTask( |
| + BrowserThread::UI, |
| + FROM_HERE, |
| + base::Bind(&GDataFileSystem::SearchAsyncOnUIThread, |
| + ui_weak_ptr_, |
| + search_query, |
| + base::Bind(&RelayReadDirectoryCallback, |
| + base::MessageLoopProxy::current(), |
| + callback))); |
| + DCHECK(posted); |
| + return; |
| + } |
| + |
| + SearchAsyncOnUIThread(search_query, callback); |
| +} |
| + |
| +void GDataFileSystem::SearchAsyncOnUIThread( |
| + const std::string& search_query, |
| + const ReadDirectoryCallback& callback) { |
| + scoped_ptr<std::vector<DocumentFeed*> > feed_list( |
| + new std::vector<DocumentFeed*>); |
| + |
| + LoadFeedFromServer(0, 0, // We don't use change stamps when fetching search |
| + // data; we always fetch the whole result feed. |
| + false, // Stop fetching search results after first feed |
| + // chunk to avoid displaying huge number of search |
| + // results (especially since we don't cache them). |
| + FilePath(), // Not used. |
| + search_query, |
| + FindEntryCallback(), // Not used. |
| + base::Bind(&GDataFileSystem::OnSearch, |
| + ui_weak_ptr_, callback)); |
| +} |
| + |
| +void GDataFileSystem::OnGetDocuments(const LoadDocumentFeedCallback& callback, |
| + GetDocumentsParams* params, |
| GDataErrorCode status, |
| scoped_ptr<base::Value> data) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| @@ -2678,14 +2877,12 @@ void GDataFileSystem::OnGetDocuments(GetDocumentsParams* params, |
| base::PlatformFileError error = GDataToPlatformError(status); |
| if (error == base::PLATFORM_FILE_OK && |
| (!data.get() || data->GetType() != Value::TYPE_DICTIONARY)) { |
| - LOG(WARNING) << "No feed content!"; |
| error = base::PLATFORM_FILE_ERROR_FAILED; |
| } |
| if (error != base::PLATFORM_FILE_OK) { |
| - if (!params->callback.is_null()) { |
| - params->callback.Run(error, FilePath(), |
| - reinterpret_cast<GDataEntry*>(NULL)); |
| + if (!callback.is_null()) { |
| + callback.Run(params, error); |
| } |
| return; |
| @@ -2696,9 +2893,8 @@ void GDataFileSystem::OnGetDocuments(GetDocumentsParams* params, |
| GURL next_feed_url; |
| scoped_ptr<DocumentFeed> current_feed(DocumentFeed::ExtractAndParse(*data)); |
| if (!current_feed.get()) { |
| - if (!params->callback.is_null()) { |
| - params->callback.Run(base::PLATFORM_FILE_ERROR_FAILED, FilePath(), |
| - reinterpret_cast<GDataEntry*>(NULL)); |
| + if (!callback.is_null()) { |
| + callback.Run(params, base::PLATFORM_FILE_ERROR_FAILED); |
| } |
| return; |
| @@ -2709,44 +2905,30 @@ void GDataFileSystem::OnGetDocuments(GetDocumentsParams* params, |
| params->feed_list->push_back(current_feed.release()); |
| // Check if we need to collect more data to complete the directory list. |
| - if (has_next_feed_url && !next_feed_url.is_empty()) { |
| + if (params->should_fetch_multiple_feeds && has_next_feed_url && |
| + !next_feed_url.is_empty()) { |
| // Kick of the remaining part of the feeds. |
| documents_service_->GetDocuments( |
| next_feed_url, |
| params->start_changestamp, |
| + params->search_query, |
| base::Bind(&GDataFileSystem::OnGetDocuments, |
| ui_weak_ptr_, |
| + callback, |
| base::Owned( |
| - new GetDocumentsParams(params->start_changestamp, |
| - params->root_feed_changestamp, |
| - params->feed_list.release(), |
| - params->search_file_path, |
| - params->callback)))); |
| + new GetDocumentsParams( |
| + params->start_changestamp, |
| + params->root_feed_changestamp, |
| + params->feed_list.release(), |
| + params->should_fetch_multiple_feeds, |
| + params->search_file_path, |
| + params->search_query, |
| + params->callback)))); |
| return; |
| } |
| - error = UpdateFromFeed(*params->feed_list, |
| - FROM_SERVER, |
| - params->start_changestamp, |
| - params->root_feed_changestamp); |
| - |
| - if (error != base::PLATFORM_FILE_OK) { |
| - if (!params->callback.is_null()) { |
| - params->callback.Run(error, FilePath(), |
| - reinterpret_cast<GDataEntry*>(NULL)); |
| - } |
| - |
| - return; |
| - } |
| - |
| - // Save file system metadata to disk. |
| - SaveFileSystemAsProto(); |
| - |
| - // If we had someone to report this too, then this retrieval was done in a |
| - // context of search... so continue search. |
| - if (!params->callback.is_null()) { |
| - FindEntryByPathSyncOnUIThread(params->search_file_path, params->callback); |
| - } |
| + if (!callback.is_null()) |
| + callback.Run(params, error); |
| } |
| void GDataFileSystem::LoadRootFeedFromCache( |
| @@ -3510,7 +3692,7 @@ base::PlatformFileError GDataFileSystem::AddNewDirectory( |
| parent_dir->AddEntry(new_entry); |
| - NotifyDirectoryChanged(directory_path); |
| + NotifyDirectoryChanged(entry->GetFilePath()); |
|
satorux1
2012/05/04 01:48:40
you might also want to document why we should use
|
| return base::PLATFORM_FILE_OK; |
| } |
| @@ -4777,4 +4959,15 @@ void SetFreeDiskSpaceGetterForTesting(FreeDiskSpaceGetterInterface* getter) { |
| global_free_disk_getter_for_testing = getter; |
| } |
| +bool GDataFileSystem::ShouldCreateDirectory(const FilePath& directory_path) { |
| + // We allow directory creation for paths that are on gdata file system |
| + // (GDATA_SEARCH_PATH_INVALID) and paths that reference actual gdata file |
| + // system path (GDATA_SEARCH_PATH_RESULT_CHILD). |
| + util::GDataSearchPathType path_type = |
| + util::GetSearchPathStatus(directory_path); |
| + return path_type == util::GDATA_SEARCH_PATH_INVALID || |
| + path_type == util::GDATA_SEARCH_PATH_RESULT_CHILD; |
| + |
| +} |
|
satorux1
2012/05/04 01:48:40
you can probably move this to anonymous namespace.
|
| + |
| } // namespace gdata |