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/check_op.h"
8 #include "base/stl_util.h"
9 #include "content/public/browser/browser_thread.h"
10 #include "storage/common/file_system/file_system_util.h"
11 
12 using storage::FileSystemURL;
13 using storage::FileSystemURLSet;
14 
15 namespace sync_file_system {
16 
17 namespace {
18 
19 using OriginAndType = LocalFileSyncStatus::OriginAndType;
20 
GetOriginAndType(const storage::FileSystemURL & url)21 OriginAndType GetOriginAndType(const storage::FileSystemURL& url) {
22   return std::make_pair(url.origin().GetURL(), url.type());
23 }
24 
NormalizePath(const base::FilePath & path)25 base::FilePath NormalizePath(const base::FilePath& path) {
26   // Ensure |path| has single trailing path-separator, so that we can use
27   // prefix-match to find descendants of |path| in an ordered container.
28   return base::FilePath(path.StripTrailingSeparators().value() +
29                         storage::VirtualPath::kSeparator);
30 }
31 
32 struct SetKeyHelper {
33   template <typename Iterator>
GetKeysync_file_system::__anon2aafca0b0111::SetKeyHelper34   static const base::FilePath& GetKey(Iterator itr) {
35     return *itr;
36   }
37 };
38 
39 struct MapKeyHelper {
40   template <typename Iterator>
GetKeysync_file_system::__anon2aafca0b0111::MapKeyHelper41   static const base::FilePath& GetKey(Iterator itr) {
42     return itr->first;
43   }
44 };
45 
46 template <typename Container, typename GetKeyHelper>
ContainsChildOrParent(const Container & paths,const base::FilePath & path,const GetKeyHelper & get_key_helper)47 bool ContainsChildOrParent(const Container& paths,
48                            const base::FilePath& path,
49                            const GetKeyHelper& get_key_helper) {
50   base::FilePath normalized_path = NormalizePath(path);
51 
52   // Check if |paths| has a child of |normalized_path|.
53   // Note that descendants of |normalized_path| are stored right after
54   // |normalized_path| since |normalized_path| has trailing path separator.
55   auto upper = 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 (base::Contains(paths, normalized_path))
64       return true;
65 
66     if (storage::VirtualPath::IsRootPath(normalized_path))
67       return false;
68 
69     normalized_path =
70         NormalizePath(storage::VirtualPath::DirName(normalized_path));
71   }
72 }
73 
74 }  // namespace
75 
LocalFileSyncStatus()76 LocalFileSyncStatus::LocalFileSyncStatus() {}
77 
~LocalFileSyncStatus()78 LocalFileSyncStatus::~LocalFileSyncStatus() {}
79 
StartWriting(const FileSystemURL & url)80 void LocalFileSyncStatus::StartWriting(const FileSystemURL& url) {
81   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
82   DCHECK(!IsChildOrParentSyncing(url));
83   writing_[GetOriginAndType(url)][NormalizePath(url.path())]++;
84 }
85 
EndWriting(const FileSystemURL & url)86 void LocalFileSyncStatus::EndWriting(const FileSystemURL& url) {
87   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
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 (auto& observer : observer_list_)
97       observer.OnSyncEnabled(url);
98   }
99 }
100 
StartSyncing(const FileSystemURL & url)101 void LocalFileSyncStatus::StartSyncing(const FileSystemURL& url) {
102   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
103   DCHECK(!IsChildOrParentWriting(url));
104   DCHECK(!IsChildOrParentSyncing(url));
105   syncing_[GetOriginAndType(url)].insert(NormalizePath(url.path()));
106 }
107 
EndSyncing(const FileSystemURL & url)108 void LocalFileSyncStatus::EndSyncing(const FileSystemURL& url) {
109   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
110   base::FilePath normalized_path = NormalizePath(url.path());
111   OriginAndType origin_and_type = GetOriginAndType(url);
112 
113   syncing_[origin_and_type].erase(normalized_path);
114   if (syncing_[origin_and_type].empty())
115     syncing_.erase(origin_and_type);
116   for (auto& observer : observer_list_)
117     observer.OnSyncEnabled(url);
118   for (auto& observer : observer_list_)
119     observer.OnWriteEnabled(url);
120 }
121 
IsWriting(const FileSystemURL & url) const122 bool LocalFileSyncStatus::IsWriting(const FileSystemURL& url) const {
123   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
124   return IsChildOrParentWriting(url);
125 }
126 
IsWritable(const FileSystemURL & url) const127 bool LocalFileSyncStatus::IsWritable(const FileSystemURL& url) const {
128   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
129   return !IsChildOrParentSyncing(url);
130 }
131 
IsSyncable(const FileSystemURL & url) const132 bool LocalFileSyncStatus::IsSyncable(const FileSystemURL& url) const {
133   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
134   return !IsChildOrParentSyncing(url) && !IsChildOrParentWriting(url);
135 }
136 
AddObserver(Observer * observer)137 void LocalFileSyncStatus::AddObserver(Observer* observer) {
138   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
139   observer_list_.AddObserver(observer);
140 }
141 
RemoveObserver(Observer * observer)142 void LocalFileSyncStatus::RemoveObserver(Observer* observer) {
143   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
144   observer_list_.RemoveObserver(observer);
145 }
146 
IsChildOrParentWriting(const FileSystemURL & url) const147 bool LocalFileSyncStatus::IsChildOrParentWriting(
148     const FileSystemURL& url) const {
149   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
150 
151   auto found = writing_.find(GetOriginAndType(url));
152   if (found == writing_.end())
153     return false;
154   return ContainsChildOrParent(found->second, url.path(),
155                                MapKeyHelper());
156 }
157 
IsChildOrParentSyncing(const FileSystemURL & url) const158 bool LocalFileSyncStatus::IsChildOrParentSyncing(
159     const FileSystemURL& url) const {
160   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
161   auto found = syncing_.find(GetOriginAndType(url));
162   if (found == syncing_.end())
163     return false;
164   return ContainsChildOrParent(found->second, url.path(),
165                                SetKeyHelper());
166 }
167 
168 }  // namespace sync_file_system
169