OLD | NEW |
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" |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
42 // there are no more reservations. | 42 // there are no more reservations. |
43 // | 43 // |
44 // It is not an error, although undesirable, to have multiple DownloadItem*s | 44 // It is not an error, although undesirable, to have multiple DownloadItem*s |
45 // that are mapped to the same path. This can happen if a reservation is created | 45 // that are mapped to the same path. This can happen if a reservation is created |
46 // that is supposed to overwrite an existing reservation. | 46 // that is supposed to overwrite an existing reservation. |
47 ReservationMap* g_reservation_map = NULL; | 47 ReservationMap* g_reservation_map = NULL; |
48 | 48 |
49 // Observes a DownloadItem for changes to its target path and state. Updates or | 49 // Observes a DownloadItem for changes to its target path and state. Updates or |
50 // revokes associated download path reservations as necessary. Created, invoked | 50 // revokes associated download path reservations as necessary. Created, invoked |
51 // and destroyed on the UI thread. | 51 // and destroyed on the UI thread. |
52 class DownloadItemObserver : public DownloadItem::Observer { | 52 class DownloadItemObserver : public DownloadItem::Observer, |
| 53 public base::SupportsUserData::Data { |
53 public: | 54 public: |
54 explicit DownloadItemObserver(DownloadItem* download_item); | 55 explicit DownloadItemObserver(DownloadItem* download_item); |
55 | 56 |
56 private: | 57 private: |
57 ~DownloadItemObserver() override; | 58 ~DownloadItemObserver() override; |
58 | 59 |
59 // DownloadItem::Observer | 60 // DownloadItem::Observer |
60 void OnDownloadUpdated(DownloadItem* download) override; | 61 void OnDownloadUpdated(DownloadItem* download) override; |
61 void OnDownloadDestroyed(DownloadItem* download) override; | 62 void OnDownloadDestroyed(DownloadItem* download) override; |
62 | 63 |
63 DownloadItem* download_item_; | 64 DownloadItem* download_item_; |
64 | 65 |
65 // Last known target path for the download. | 66 // Last known target path for the download. |
66 base::FilePath last_target_path_; | 67 base::FilePath last_target_path_; |
67 | 68 |
| 69 static const int kUserDataKey; |
| 70 |
68 DISALLOW_COPY_AND_ASSIGN(DownloadItemObserver); | 71 DISALLOW_COPY_AND_ASSIGN(DownloadItemObserver); |
69 }; | 72 }; |
70 | 73 |
71 // Returns true if the given path is in use by a path reservation. | 74 // Returns true if the given path is in use by a path reservation. |
72 bool IsPathReserved(const base::FilePath& path) { | 75 bool IsPathReserved(const base::FilePath& path) { |
73 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 76 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
74 // No reservation map => no reservations. | 77 // No reservation map => no reservations. |
75 if (g_reservation_map == NULL) | 78 if (g_reservation_map == NULL) |
76 return false; | 79 return false; |
77 // Unfortunately path normalization doesn't work reliably for non-existant | 80 // Unfortunately path normalization doesn't work reliably for non-existant |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
155 bool create_directory, | 158 bool create_directory, |
156 DownloadPathReservationTracker::FilenameConflictAction conflict_action, | 159 DownloadPathReservationTracker::FilenameConflictAction conflict_action, |
157 base::FilePath* reserved_path) { | 160 base::FilePath* reserved_path) { |
158 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 161 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
159 DCHECK(suggested_path.IsAbsolute()); | 162 DCHECK(suggested_path.IsAbsolute()); |
160 | 163 |
161 // Create a reservation map if one doesn't exist. It will be automatically | 164 // Create a reservation map if one doesn't exist. It will be automatically |
162 // deleted when all the reservations are revoked. | 165 // deleted when all the reservations are revoked. |
163 if (g_reservation_map == NULL) | 166 if (g_reservation_map == NULL) |
164 g_reservation_map = new ReservationMap; | 167 g_reservation_map = new ReservationMap; |
| 168 ReservationMap& reservations = *g_reservation_map; |
165 | 169 |
166 ReservationMap& reservations = *g_reservation_map; | 170 // Erase the reservation if it already exists. This can happen during |
167 DCHECK(!ContainsKey(reservations, key)); | 171 // automatic resumption where a new target determination request may be issued |
| 172 // for a DownloadItem without an intervening transition to INTERRUPTED. |
| 173 // |
| 174 // Revoking and re-acquiring the reservation forces us to re-verify the claims |
| 175 // we are making about the path. |
| 176 reservations.erase(key); |
168 | 177 |
169 base::FilePath target_path(suggested_path.NormalizePathSeparators()); | 178 base::FilePath target_path(suggested_path.NormalizePathSeparators()); |
170 base::FilePath target_dir = target_path.DirName(); | 179 base::FilePath target_dir = target_path.DirName(); |
171 base::FilePath filename = target_path.BaseName(); | 180 base::FilePath filename = target_path.BaseName(); |
172 bool is_path_writeable = true; | 181 bool is_path_writeable = true; |
173 bool has_conflicts = false; | 182 bool has_conflicts = false; |
174 bool name_too_long = false; | 183 bool name_too_long = false; |
175 | 184 |
176 // Create target_dir if necessary and appropriate. target_dir may be the last | 185 // Create target_dir if necessary and appropriate. target_dir may be the last |
177 // directory that the user selected in a FilePicker; if that directory has | 186 // directory that the user selected in a FilePicker; if that directory has |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
282 bool verified) { | 291 bool verified) { |
283 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 292 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
284 callback.Run(*reserved_path, verified); | 293 callback.Run(*reserved_path, verified); |
285 } | 294 } |
286 | 295 |
287 DownloadItemObserver::DownloadItemObserver(DownloadItem* download_item) | 296 DownloadItemObserver::DownloadItemObserver(DownloadItem* download_item) |
288 : download_item_(download_item), | 297 : download_item_(download_item), |
289 last_target_path_(download_item->GetTargetFilePath()) { | 298 last_target_path_(download_item->GetTargetFilePath()) { |
290 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 299 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
291 download_item_->AddObserver(this); | 300 download_item_->AddObserver(this); |
| 301 download_item_->SetUserData(&kUserDataKey, this); |
292 } | 302 } |
293 | 303 |
294 DownloadItemObserver::~DownloadItemObserver() { | 304 DownloadItemObserver::~DownloadItemObserver() { |
295 download_item_->RemoveObserver(this); | 305 download_item_->RemoveObserver(this); |
| 306 // DownloadItemObserver is owned by DownloadItem. It should only be getting |
| 307 // destroyed because it's being removed from the UserData pool. No need to |
| 308 // call DownloadItem::RemoveUserData(). |
296 } | 309 } |
297 | 310 |
298 void DownloadItemObserver::OnDownloadUpdated(DownloadItem* download) { | 311 void DownloadItemObserver::OnDownloadUpdated(DownloadItem* download) { |
299 switch (download->GetState()) { | 312 switch (download->GetState()) { |
300 case DownloadItem::IN_PROGRESS: { | 313 case DownloadItem::IN_PROGRESS: { |
301 // Update the reservation. | 314 // Update the reservation. |
302 base::FilePath new_target_path = download->GetTargetFilePath(); | 315 base::FilePath new_target_path = download->GetTargetFilePath(); |
303 if (new_target_path != last_target_path_) { | 316 if (new_target_path != last_target_path_) { |
304 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind( | 317 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind( |
305 &UpdateReservation, download, new_target_path)); | 318 &UpdateReservation, download, new_target_path)); |
(...skipping 10 matching lines...) Expand all Loading... |
316 case DownloadItem::CANCELLED: | 329 case DownloadItem::CANCELLED: |
317 // We no longer need the reservation if the download is being removed. | 330 // We no longer need the reservation if the download is being removed. |
318 | 331 |
319 case DownloadItem::INTERRUPTED: | 332 case DownloadItem::INTERRUPTED: |
320 // The download filename will need to be re-generated when the download is | 333 // The download filename will need to be re-generated when the download is |
321 // restarted. Holding on to the reservation now would prevent the name | 334 // restarted. Holding on to the reservation now would prevent the name |
322 // from being used for a subsequent retry attempt. | 335 // from being used for a subsequent retry attempt. |
323 | 336 |
324 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind( | 337 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind( |
325 &RevokeReservation, download)); | 338 &RevokeReservation, download)); |
326 delete this; | 339 download->RemoveUserData(&kUserDataKey); |
327 break; | 340 break; |
328 | 341 |
329 case DownloadItem::MAX_DOWNLOAD_STATE: | 342 case DownloadItem::MAX_DOWNLOAD_STATE: |
330 // Compiler appeasement. | 343 // Compiler appeasement. |
331 NOTREACHED(); | 344 NOTREACHED(); |
332 } | 345 } |
333 } | 346 } |
334 | 347 |
335 void DownloadItemObserver::OnDownloadDestroyed(DownloadItem* download) { | 348 void DownloadItemObserver::OnDownloadDestroyed(DownloadItem* download) { |
336 // Items should be COMPLETE/INTERRUPTED/CANCELLED before being destroyed. | 349 // Items should be COMPLETE/INTERRUPTED/CANCELLED before being destroyed. |
337 NOTREACHED(); | 350 NOTREACHED(); |
338 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind( | 351 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind( |
339 &RevokeReservation, download)); | 352 &RevokeReservation, download)); |
340 delete this; | |
341 } | 353 } |
342 | 354 |
| 355 // static |
| 356 const int DownloadItemObserver::kUserDataKey = 0; |
| 357 |
343 } // namespace | 358 } // namespace |
344 | 359 |
345 // static | 360 // static |
346 void DownloadPathReservationTracker::GetReservedPath( | 361 void DownloadPathReservationTracker::GetReservedPath( |
347 DownloadItem* download_item, | 362 DownloadItem* download_item, |
348 const base::FilePath& target_path, | 363 const base::FilePath& target_path, |
349 const base::FilePath& default_path, | 364 const base::FilePath& default_path, |
350 bool create_directory, | 365 bool create_directory, |
351 FilenameConflictAction conflict_action, | 366 FilenameConflictAction conflict_action, |
352 const ReservedPathCallback& callback) { | 367 const ReservedPathCallback& callback) { |
(...skipping 17 matching lines...) Expand all Loading... |
370 base::Bind(&RunGetReservedPathCallback, | 385 base::Bind(&RunGetReservedPathCallback, |
371 callback, | 386 callback, |
372 base::Owned(reserved_path))); | 387 base::Owned(reserved_path))); |
373 } | 388 } |
374 | 389 |
375 // static | 390 // static |
376 bool DownloadPathReservationTracker::IsPathInUseForTesting( | 391 bool DownloadPathReservationTracker::IsPathInUseForTesting( |
377 const base::FilePath& path) { | 392 const base::FilePath& path) { |
378 return IsPathInUse(path); | 393 return IsPathInUse(path); |
379 } | 394 } |
OLD | NEW |