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