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

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: Partially addressed review comments (#3). 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_win.cc ('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"
15 #include "chrome/browser/download/download_util.h" 17 #include "chrome/browser/download/download_util.h"
16 #include "chrome/common/chrome_paths.h" 18 #include "chrome/common/chrome_paths.h"
17 #include "content/public/browser/browser_thread.h" 19 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/download_id.h" 20 #include "content/public/browser/download_id.h"
19 #include "content/public/browser/download_item.h" 21 #include "content/public/browser/download_item.h"
20 22
21 using content::BrowserThread; 23 using content::BrowserThread;
22 using content::DownloadId; 24 using content::DownloadId;
23 using content::DownloadItem; 25 using content::DownloadItem;
24 26
25 namespace { 27 namespace {
26 28
27 typedef std::map<content::DownloadId, FilePath> ReservationMap; 29 typedef std::map<content::DownloadId, FilePath> ReservationMap;
28 30
31 // The lower bound for file name truncation. If the truncation results in a name
32 // shorter than this limit, we give up automatic truncation and prompt the user.
33 static const int kTruncatedNameLengthLowerbound = 5;
34
29 // Map of download path reservations. Each reserved path is associated with a 35 // 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 36 // DownloadId. This object is destroyed in |Revoke()| when there are no more
31 // reservations. 37 // reservations.
32 // 38 //
33 // It is not an error, although undesirable, to have multiple DownloadIds that 39 // 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 40 // are mapped to the same path. This can happen if a reservation is created that
35 // is supposed to overwrite an existing reservation. 41 // is supposed to overwrite an existing reservation.
36 ReservationMap* g_reservation_map = NULL; 42 ReservationMap* g_reservation_map = NULL;
37 43
38 // Observes a DownloadItem for changes to its target path and state. Updates or 44 // 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)) 89 if (IsPathReserved(path))
84 return true; 90 return true;
85 91
86 // If the path exists in the file system, then the path is in use. 92 // If the path exists in the file system, then the path is in use.
87 if (file_util::PathExists(path)) 93 if (file_util::PathExists(path))
88 return true; 94 return true;
89 95
90 return false; 96 return false;
91 } 97 }
92 98
99 // Truncate the given name so that its becomes .size() <= limit. Returns an
100 // empty string if it is impossible due to unknown file-name encoding.
101 FilePath::StringType TruncateFileName(const FilePath::StringType& name,
102 size_t limit) {
103 FilePath::StringType truncated;
104 #if defined(OS_CHROMEOS) || defined(OS_MACOSX)
105 TruncateUTF8ToByteSize(name, limit, &truncated);
106 #elif defined(OS_WIN)
107 // TODO(kinaba): Implement for Windows, where StringType == string16.
108 #else
109 // We cannot generally assume that the file name encoding is in UTF-8 (see
110 // the comment for FilePath::AsUTF8Unsafe), hence no safe way to truncate.
111 #endif
112 return truncated;
113 }
114
93 // Called on the FILE thread to reserve a download path. This method: 115 // Called on the FILE thread to reserve a download path. This method:
94 // - Creates directory |default_download_path| if it doesn't exist. 116 // - Creates directory |default_download_path| if it doesn't exist.
95 // - Verifies that the parent directory of |suggested_path| exists and is 117 // - Verifies that the parent directory of |suggested_path| exists and is
96 // writeable. 118 // writeable.
119 // - Truncates the suggested name if it exceeds the filesystem's limit.
97 // - Uniquifies |suggested_path| if |should_uniquify_path| is true. 120 // - Uniquifies |suggested_path| if |should_uniquify_path| is true.
98 // - Schedules |callback| on the UI thread with the reserved path and a flag 121 // - Schedules |callback| on the UI thread with the reserved path and a flag
99 // indicating whether the returned path has been successfully verified. 122 // indicating whether the returned path has been successfully verified.
100 void CreateReservation( 123 void CreateReservation(
101 DownloadId download_id, 124 DownloadId download_id,
102 const FilePath& suggested_path, 125 const FilePath& suggested_path,
103 const FilePath& default_download_path, 126 const FilePath& default_download_path,
104 bool should_uniquify, 127 bool should_uniquify,
105 const DownloadPathReservationTracker::ReservedPathCallback& callback) { 128 const DownloadPathReservationTracker::ReservedPathCallback& callback) {
106 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 129 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. 155 // to the user's "My Documents" directory. We'll prompt them in this case.
133 FilePath dir = target_path.DirName(); 156 FilePath dir = target_path.DirName();
134 FilePath filename = target_path.BaseName(); 157 FilePath filename = target_path.BaseName();
135 if (!file_util::PathIsWritable(dir)) { 158 if (!file_util::PathIsWritable(dir)) {
136 DVLOG(1) << "Unable to write to directory \"" << dir.value() << "\""; 159 DVLOG(1) << "Unable to write to directory \"" << dir.value() << "\"";
137 is_path_writeable = false; 160 is_path_writeable = false;
138 PathService::Get(chrome::DIR_USER_DOCUMENTS, &dir); 161 PathService::Get(chrome::DIR_USER_DOCUMENTS, &dir);
139 target_path = dir.Append(filename); 162 target_path = dir.Append(filename);
140 } 163 }
141 164
165 // Check the limit of file name length if it could be obtained. When the
166 // suggested name exceeds the limit, truncate or prompt the user.
167 bool name_too_long = false;
168 if (is_path_writeable) {
169 int max_length = base::SysInfo::GetMaximumPathComponentLength(dir);
170 if (max_length > 0) {
171 // The extension should not be truncated, so we split it first.
172 FilePath::StringType ext = filename.Extension();
173 FilePath::StringType name = filename.RemoveExtension().value();
174
175 // Reserve several characters we may append later.
176 const int kExtraChars = sizeof(" (100).crdownload") - 1;
177 int limit = max_length - ext.size() - kExtraChars;
178 if (static_cast<int>(name.size()) > limit) {
179 name_too_long = true;
180 if (limit >= kTruncatedNameLengthLowerbound) {
181 // Truncate the name to fit in |limit|.
182 FilePath::StringType truncated = TruncateFileName(name, limit);
183 if (!truncated.empty()) {
184 target_path = dir.Append(truncated + ext);
185 name_too_long = false;
186 }
187 }
188 }
189 }
190 }
191
142 if (is_path_writeable && should_uniquify && IsPathInUse(target_path)) { 192 if (is_path_writeable && should_uniquify && IsPathInUse(target_path)) {
143 has_conflicts = true; 193 has_conflicts = true;
144 for (int uniquifier = 1; 194 for (int uniquifier = 1;
145 uniquifier <= DownloadPathReservationTracker::kMaxUniqueFiles; 195 uniquifier <= DownloadPathReservationTracker::kMaxUniqueFiles;
146 ++uniquifier) { 196 ++uniquifier) {
147 FilePath path_to_check(target_path.InsertBeforeExtensionASCII( 197 FilePath path_to_check(target_path.InsertBeforeExtensionASCII(
148 StringPrintf(" (%d)", uniquifier))); 198 StringPrintf(" (%d)", uniquifier)));
149 if (!IsPathInUse(path_to_check)) { 199 if (!IsPathInUse(path_to_check)) {
150 target_path = path_to_check; 200 target_path = path_to_check;
151 has_conflicts = false; 201 has_conflicts = false;
152 break; 202 break;
153 } 203 }
154 } 204 }
155 } 205 }
156 reservations[download_id] = target_path; 206 reservations[download_id] = target_path;
157 BrowserThread::PostTask( 207 bool verified = (is_path_writeable && !has_conflicts && !name_too_long);
158 BrowserThread::UI, FROM_HERE, 208 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
159 base::Bind(callback, target_path, (is_path_writeable && !has_conflicts))); 209 base::Bind(callback, target_path, verified));
160 } 210 }
161 211
162 // Called on the FILE thread to update the path of the reservation associated 212 // Called on the FILE thread to update the path of the reservation associated
163 // with |download_id| to |new_path|. 213 // with |download_id| to |new_path|.
164 void UpdateReservation(DownloadId download_id, const FilePath& new_path) { 214 void UpdateReservation(DownloadId download_id, const FilePath& new_path) {
165 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 215 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
166 DCHECK(g_reservation_map != NULL); 216 DCHECK(g_reservation_map != NULL);
167 ReservationMap::iterator iter = g_reservation_map->find(download_id); 217 ReservationMap::iterator iter = g_reservation_map->find(download_id);
168 if (iter != g_reservation_map->end()) { 218 if (iter != g_reservation_map->end()) {
169 iter->second = new_path; 219 iter->second = new_path;
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after
267 BrowserThread::FILE, FROM_HERE, 317 BrowserThread::FILE, FROM_HERE,
268 base::Bind(&CreateReservation, download_item.GetGlobalId(), 318 base::Bind(&CreateReservation, download_item.GetGlobalId(),
269 target_path, default_path, uniquify_path, callback)); 319 target_path, default_path, uniquify_path, callback));
270 } 320 }
271 321
272 // static 322 // static
273 bool DownloadPathReservationTracker::IsPathInUseForTesting( 323 bool DownloadPathReservationTracker::IsPathInUseForTesting(
274 const FilePath& path) { 324 const FilePath& path) {
275 return IsPathInUse(path); 325 return IsPathInUse(path);
276 } 326 }
OLDNEW
« base/sys_info_win.cc ('K') | « base/sys_info_win.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698