Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(144)

Side by Side Diff: chrome/browser/download/download_path_reservation_tracker.cc

Issue 12212010: Truncate the download file name if it exceeds the filesystem limit. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Add Windows (UTF-16) support. Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« base/sys_info.h ('K') | « base/sys_info_win.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/download/download_path_reservation_tracker.h" 5 #include "chrome/browser/download/download_path_reservation_tracker.h"
6 6
7 #include <map> 7 #include <map>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/callback.h" 10 #include "base/callback.h"
11 #include "base/file_util.h" 11 #include "base/file_util.h"
12 #include "base/logging.h" 12 #include "base/logging.h"
13 #include "base/path_service.h" 13 #include "base/path_service.h"
14 #include "base/stl_util.h" 14 #include "base/stl_util.h"
15 #include "base/string_util.h"
16 #include "base/sys_info.h"
17 #include "base/third_party/icu/icu_utf.h"
15 #include "chrome/browser/download/download_util.h" 18 #include "chrome/browser/download/download_util.h"
16 #include "chrome/common/chrome_paths.h" 19 #include "chrome/common/chrome_paths.h"
17 #include "content/public/browser/browser_thread.h" 20 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/download_id.h" 21 #include "content/public/browser/download_id.h"
19 #include "content/public/browser/download_item.h" 22 #include "content/public/browser/download_item.h"
20 23
21 using content::BrowserThread; 24 using content::BrowserThread;
22 using content::DownloadId; 25 using content::DownloadId;
23 using content::DownloadItem; 26 using content::DownloadItem;
24 27
25 namespace { 28 namespace {
26 29
27 typedef std::map<content::DownloadId, base::FilePath> ReservationMap; 30 typedef std::map<content::DownloadId, base::FilePath> ReservationMap;
28 31
32 // The lower bound for file name truncation. If the truncation results in a name
33 // shorter than this limit, we give up automatic truncation and prompt the user.
34 static const int kTruncatedNameLengthLowerbound = 5;
35
29 // Map of download path reservations. Each reserved path is associated with a 36 // Map of download path reservations. Each reserved path is associated with a
30 // DownloadId. This object is destroyed in |Revoke()| when there are no more 37 // DownloadId. This object is destroyed in |Revoke()| when there are no more
31 // reservations. 38 // reservations.
32 // 39 //
33 // It is not an error, although undesirable, to have multiple DownloadIds that 40 // It is not an error, although undesirable, to have multiple DownloadIds that
34 // are mapped to the same path. This can happen if a reservation is created that 41 // are mapped to the same path. This can happen if a reservation is created that
35 // is supposed to overwrite an existing reservation. 42 // is supposed to overwrite an existing reservation.
36 ReservationMap* g_reservation_map = NULL; 43 ReservationMap* g_reservation_map = NULL;
37 44
38 // Observes a DownloadItem for changes to its target path and state. Updates or 45 // Observes a DownloadItem for changes to its target path and state. Updates or
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
83 if (IsPathReserved(path)) 90 if (IsPathReserved(path))
84 return true; 91 return true;
85 92
86 // If the path exists in the file system, then the path is in use. 93 // If the path exists in the file system, then the path is in use.
87 if (file_util::PathExists(path)) 94 if (file_util::PathExists(path))
88 return true; 95 return true;
89 96
90 return false; 97 return false;
91 } 98 }
92 99
100 // Truncate the given name so that its becomes .size() <= limit. Returns an
101 // empty string if it is impossible due to unknown file-name encoding.
102 FilePath::StringType TruncateFileName(const FilePath::StringType& name,
103 size_t limit) {
104 FilePath::StringType truncated;
105 #if defined(OS_CHROMEOS) || defined(OS_MACOSX)
106 // Character encoding is UTF-8.
107 TruncateUTF8ToByteSize(name, limit, &truncated);
108 #elif defined(OS_WIN)
109 // Character encoding is UTF-16.
110 if (name.size() <= limit) {
111 truncated = name;
112 } else {
113 truncated = name.substr(
114 0, limit > 0 && CBU16_IS_TRAIL(name[limit]) ? limit - 1 : limit);
115 }
116 #else
117 // We cannot generally assume that the file name encoding is in UTF-8 (see
118 // the comment for FilePath::AsUTF8Unsafe), hence no safe way to truncate.
119 #endif
120 return truncated;
121 }
122
93 // Called on the FILE thread to reserve a download path. This method: 123 // Called on the FILE thread to reserve a download path. This method:
94 // - Creates directory |default_download_path| if it doesn't exist. 124 // - Creates directory |default_download_path| if it doesn't exist.
95 // - Verifies that the parent directory of |suggested_path| exists and is 125 // - Verifies that the parent directory of |suggested_path| exists and is
96 // writeable. 126 // writeable.
127 // - Truncates the suggested name if it exceeds the filesystem's limit.
97 // - Uniquifies |suggested_path| if |should_uniquify_path| is true. 128 // - Uniquifies |suggested_path| if |should_uniquify_path| is true.
98 // - Schedules |callback| on the UI thread with the reserved path and a flag 129 // - Schedules |callback| on the UI thread with the reserved path and a flag
99 // indicating whether the returned path has been successfully verified. 130 // indicating whether the returned path has been successfully verified.
100 void CreateReservation( 131 void CreateReservation(
101 DownloadId download_id, 132 DownloadId download_id,
102 const base::FilePath& suggested_path, 133 const base::FilePath& suggested_path,
103 const base::FilePath& default_download_path, 134 const base::FilePath& default_download_path,
104 bool should_uniquify, 135 bool should_uniquify,
105 const DownloadPathReservationTracker::ReservedPathCallback& callback) { 136 const DownloadPathReservationTracker::ReservedPathCallback& callback) {
106 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 137 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
(...skipping 25 matching lines...) Expand all
132 // to the user's "My Documents" directory. We'll prompt them in this case. 163 // to the user's "My Documents" directory. We'll prompt them in this case.
133 base::FilePath dir = target_path.DirName(); 164 base::FilePath dir = target_path.DirName();
134 base::FilePath filename = target_path.BaseName(); 165 base::FilePath filename = target_path.BaseName();
135 if (!file_util::PathIsWritable(dir)) { 166 if (!file_util::PathIsWritable(dir)) {
136 DVLOG(1) << "Unable to write to directory \"" << dir.value() << "\""; 167 DVLOG(1) << "Unable to write to directory \"" << dir.value() << "\"";
137 is_path_writeable = false; 168 is_path_writeable = false;
138 PathService::Get(chrome::DIR_USER_DOCUMENTS, &dir); 169 PathService::Get(chrome::DIR_USER_DOCUMENTS, &dir);
139 target_path = dir.Append(filename); 170 target_path = dir.Append(filename);
140 } 171 }
141 172
173 // Check the limit of file name length if it could be obtained. When the
174 // suggested name exceeds the limit, truncate or prompt the user.
175 bool name_too_long = false;
176 if (is_path_writeable) {
177 int max_length = base::SysInfo::GetMaximumPathComponentLength(dir);
asanka 2013/02/12 20:20:12 We should also limit the length of the full path t
kinaba 2013/02/14 04:52:47 Done (inside the query function).
178 if (max_length > 0) {
179 // The extension should not be truncated, so we split it first.
180 FilePath::StringType ext = filename.Extension();
181 FilePath::StringType name = filename.RemoveExtension().value();
182
183 // Reserve several characters we may append later.
184 const int kExtraChars = sizeof(" (100).crdownload") - 1;
asanka 2013/02/12 20:20:12 Feel free to push back on this one: The intermedi
kinaba 2013/02/14 04:52:47 Sounds reasonable. Let me handle it in a separate
185 int limit = max_length - ext.size() - kExtraChars;
186 if (static_cast<int>(name.size()) > limit) {
187 name_too_long = true;
188 if (limit >= kTruncatedNameLengthLowerbound) {
189 // Truncate the name to fit in |limit|.
190 FilePath::StringType truncated = TruncateFileName(name, limit);
191 if (!truncated.empty()) {
192 target_path = dir.Append(truncated + ext);
193 name_too_long = false;
194 }
195 }
196 }
197 }
198 }
199
142 if (is_path_writeable && should_uniquify && IsPathInUse(target_path)) { 200 if (is_path_writeable && should_uniquify && IsPathInUse(target_path)) {
143 has_conflicts = true; 201 has_conflicts = true;
144 for (int uniquifier = 1; 202 for (int uniquifier = 1;
145 uniquifier <= DownloadPathReservationTracker::kMaxUniqueFiles; 203 uniquifier <= DownloadPathReservationTracker::kMaxUniqueFiles;
146 ++uniquifier) { 204 ++uniquifier) {
147 base::FilePath path_to_check(target_path.InsertBeforeExtensionASCII( 205 base::FilePath path_to_check(target_path.InsertBeforeExtensionASCII(
148 StringPrintf(" (%d)", uniquifier))); 206 StringPrintf(" (%d)", uniquifier)));
149 if (!IsPathInUse(path_to_check)) { 207 if (!IsPathInUse(path_to_check)) {
150 target_path = path_to_check; 208 target_path = path_to_check;
151 has_conflicts = false; 209 has_conflicts = false;
152 break; 210 break;
153 } 211 }
154 } 212 }
155 } 213 }
156 reservations[download_id] = target_path; 214 reservations[download_id] = target_path;
157 BrowserThread::PostTask( 215 bool verified = (is_path_writeable && !has_conflicts && !name_too_long);
158 BrowserThread::UI, FROM_HERE, 216 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
159 base::Bind(callback, target_path, (is_path_writeable && !has_conflicts))); 217 base::Bind(callback, target_path, verified));
160 } 218 }
161 219
162 // Called on the FILE thread to update the path of the reservation associated 220 // Called on the FILE thread to update the path of the reservation associated
163 // with |download_id| to |new_path|. 221 // with |download_id| to |new_path|.
164 void UpdateReservation(DownloadId download_id, const base::FilePath& new_path) { 222 void UpdateReservation(DownloadId download_id, const base::FilePath& new_path) {
165 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 223 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
166 DCHECK(g_reservation_map != NULL); 224 DCHECK(g_reservation_map != NULL);
167 ReservationMap::iterator iter = g_reservation_map->find(download_id); 225 ReservationMap::iterator iter = g_reservation_map->find(download_id);
168 if (iter != g_reservation_map->end()) { 226 if (iter != g_reservation_map->end()) {
169 iter->second = new_path; 227 iter->second = new_path;
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after
267 BrowserThread::FILE, FROM_HERE, 325 BrowserThread::FILE, FROM_HERE,
268 base::Bind(&CreateReservation, download_item.GetGlobalId(), 326 base::Bind(&CreateReservation, download_item.GetGlobalId(),
269 target_path, default_path, uniquify_path, callback)); 327 target_path, default_path, uniquify_path, callback));
270 } 328 }
271 329
272 // static 330 // static
273 bool DownloadPathReservationTracker::IsPathInUseForTesting( 331 bool DownloadPathReservationTracker::IsPathInUseForTesting(
274 const base::FilePath& path) { 332 const base::FilePath& path) {
275 return IsPathInUse(path); 333 return IsPathInUse(path);
276 } 334 }
OLDNEW
« base/sys_info.h ('K') | « base/sys_info_win.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698