1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/sync_file_system/local/local_file_sync_status.h" 6 7 #include "base/logging.h" 8 #include "base/stl_util.h" 9 #include "webkit/common/fileapi/file_system_util.h" 10 11 using fileapi::FileSystemURL; 12 using fileapi::FileSystemURLSet; 13 14 namespace sync_file_system { 15 16 namespace { 17 18 typedef LocalFileSyncStatus::OriginAndType OriginAndType; 19 20 OriginAndType GetOriginAndType(const fileapi::FileSystemURL& url) { 21 return std::make_pair(url.origin(), url.type()); 22 } 23 24 base::FilePath NormalizePath(const base::FilePath& path) { 25 // Ensure |path| has single trailing path-separator, so that we can use 26 // prefix-match to find descendants of |path| in an ordered container. 27 return base::FilePath(path.StripTrailingSeparators().value() + 28 fileapi::VirtualPath::kSeparator); 29 } 30 31 struct SetKeyHelper { 32 template <typename Iterator> 33 static const base::FilePath& GetKey(Iterator itr) { 34 return *itr; 35 } 36 }; 37 38 struct MapKeyHelper { 39 template <typename Iterator> 40 static const base::FilePath& GetKey(Iterator itr) { 41 return itr->first; 42 } 43 }; 44 45 template <typename Container, typename GetKeyHelper> 46 bool ContainsChildOrParent(const Container& paths, 47 const base::FilePath& path, 48 const GetKeyHelper& get_key_helper) { 49 base::FilePath normalized_path = NormalizePath(path); 50 51 // Check if |paths| has a child of |normalized_path|. 52 // Note that descendants of |normalized_path| are stored right after 53 // |normalized_path| since |normalized_path| has trailing path separator. 54 typename Container::const_iterator upper = 55 paths.upper_bound(normalized_path); 56 57 if (upper != paths.end() && 58 normalized_path.IsParent(get_key_helper.GetKey(upper))) 59 return true; 60 61 // Check if any ancestor of |normalized_path| is in |writing_|. 62 while (true) { 63 if (ContainsKey(paths, normalized_path)) 64 return true; 65 66 if (fileapi::VirtualPath::IsRootPath(normalized_path)) 67 return false; 68 69 normalized_path = 70 NormalizePath(fileapi::VirtualPath::DirName(normalized_path)); 71 } 72 } 73 74 } // namespace 75 76 LocalFileSyncStatus::LocalFileSyncStatus() {} 77 78 LocalFileSyncStatus::~LocalFileSyncStatus() {} 79 80 void LocalFileSyncStatus::StartWriting(const FileSystemURL& url) { 81 DCHECK(CalledOnValidThread()); 82 DCHECK(!IsChildOrParentSyncing(url)); 83 writing_[GetOriginAndType(url)][NormalizePath(url.path())]++; 84 } 85 86 void LocalFileSyncStatus::EndWriting(const FileSystemURL& url) { 87 DCHECK(CalledOnValidThread()); 88 base::FilePath normalized_path = NormalizePath(url.path()); 89 OriginAndType origin_and_type = GetOriginAndType(url); 90 91 int count = --writing_[origin_and_type][normalized_path]; 92 if (count == 0) { 93 writing_[origin_and_type].erase(normalized_path); 94 if (writing_[origin_and_type].empty()) 95 writing_.erase(origin_and_type); 96 FOR_EACH_OBSERVER(Observer, observer_list_, OnSyncEnabled(url)); 97 } 98 } 99 100 void LocalFileSyncStatus::StartSyncing(const FileSystemURL& url) { 101 DCHECK(CalledOnValidThread()); 102 DCHECK(!IsChildOrParentWriting(url)); 103 DCHECK(!IsChildOrParentSyncing(url)); 104 syncing_[GetOriginAndType(url)].insert(NormalizePath(url.path())); 105 } 106 107 void LocalFileSyncStatus::EndSyncing(const FileSystemURL& url) { 108 DCHECK(CalledOnValidThread()); 109 base::FilePath normalized_path = NormalizePath(url.path()); 110 OriginAndType origin_and_type = GetOriginAndType(url); 111 112 syncing_[origin_and_type].erase(normalized_path); 113 if (syncing_[origin_and_type].empty()) 114 syncing_.erase(origin_and_type); 115 FOR_EACH_OBSERVER(Observer, observer_list_, OnSyncEnabled(url)); 116 FOR_EACH_OBSERVER(Observer, observer_list_, OnWriteEnabled(url)); 117 } 118 119 bool LocalFileSyncStatus::IsWriting(const FileSystemURL& url) const { 120 DCHECK(CalledOnValidThread()); 121 return IsChildOrParentWriting(url); 122 } 123 124 bool LocalFileSyncStatus::IsWritable(const FileSystemURL& url) const { 125 DCHECK(CalledOnValidThread()); 126 return !IsChildOrParentSyncing(url); 127 } 128 129 bool LocalFileSyncStatus::IsSyncable(const FileSystemURL& url) const { 130 DCHECK(CalledOnValidThread()); 131 return !IsChildOrParentSyncing(url) && !IsChildOrParentWriting(url); 132 } 133 134 void LocalFileSyncStatus::AddObserver(Observer* observer) { 135 DCHECK(CalledOnValidThread()); 136 observer_list_.AddObserver(observer); 137 } 138 139 void LocalFileSyncStatus::RemoveObserver(Observer* observer) { 140 DCHECK(CalledOnValidThread()); 141 observer_list_.RemoveObserver(observer); 142 } 143 144 bool LocalFileSyncStatus::IsChildOrParentWriting( 145 const FileSystemURL& url) const { 146 DCHECK(CalledOnValidThread()); 147 148 URLBucket::const_iterator found = writing_.find(GetOriginAndType(url)); 149 if (found == writing_.end()) 150 return false; 151 return ContainsChildOrParent(found->second, url.path(), 152 MapKeyHelper()); 153 } 154 155 bool LocalFileSyncStatus::IsChildOrParentSyncing( 156 const FileSystemURL& url) const { 157 DCHECK(CalledOnValidThread()); 158 URLSet::const_iterator found = syncing_.find(GetOriginAndType(url)); 159 if (found == syncing_.end()) 160 return false; 161 return ContainsChildOrParent(found->second, url.path(), 162 SetKeyHelper()); 163 } 164 165 } // namespace sync_file_system 166