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 // DownloadHistory observes a single DownloadManager and all its DownloadItems. | |
Randy Smith (Not in Mondays)
2012/08/20 18:57:04
Very good; thanks. Minor tweak: Can you start wit
benjhayden
2012/08/24 15:32:15
Done.
| |
6 // DownloadHistory decides whether and when to add items to, remove items from, | |
7 // and update items in the database. DownloadHistory uses DownloadHistoryData to | |
8 // store per-DownloadItem data such as its db_handle, whether the item is being | |
9 // added and waiting for its db_handle, and the last DownloadPersistentStoreInfo | |
10 // that was passed to the database. When the DownloadManager and its delegate | |
11 // (ChromeDownloadManagerDelegate) are initialized, DownloadHistory is created | |
12 // and queries the HistoryService. When the HistoryService calls back from | |
13 // QueryDownloads() to QueryCallback(), DownloadHistory uses | |
14 // DownloadManager::CreateDownloadItem() to inform DownloadManager of these | |
15 // persisted DownloadItems. CreateDownloadItem() internally calls | |
16 // OnDownloadCreated(), which normally adds items to the database, so | |
17 // QueryCallback() uses |loading_| to disable adding items to the database. | |
18 // If a download is removed via OnDownloadRemoved() while the item is still | |
19 // being added to the database, DownloadHistory uses |removed_while_adding_| to | |
20 // remember to remove the item when its ItemAdded() callback is called. | |
Randy Smith (Not in Mondays)
2012/08/20 18:57:04
Destruction context so that we can reason about re
benjhayden
2012/08/24 15:32:15
Done.
| |
21 // All callbacks are bound with a weak pointer to DownloadHistory to prevent | |
22 // use-after-free bugs, even though CancelableRequestConsumer should guarantee | |
23 // the same thing. | |
Randy Smith (Not in Mondays)
2012/08/20 18:57:04
This probably should be done in another CL, but if
benjhayden
2012/08/24 15:32:15
It required changing our HistoryService/HistoryBac
Randy Smith (Not in Mondays)
2012/08/27 19:28:19
I didn't follow what you meant here when I first r
| |
24 | |
5 #include "chrome/browser/download/download_history.h" | 25 #include "chrome/browser/download/download_history.h" |
6 | 26 |
7 #include "base/logging.h" | 27 #include "base/metrics/histogram.h" |
28 #include "chrome/browser/cancelable_request.h" | |
8 #include "chrome/browser/download/download_crx_util.h" | 29 #include "chrome/browser/download/download_crx_util.h" |
9 #include "chrome/browser/history/history_marshaling.h" | 30 #include "chrome/browser/download/download_persistent_store_info.h" |
10 #include "chrome/browser/history/history_service_factory.h" | 31 #include "content/public/browser/browser_thread.h" |
11 #include "chrome/browser/profiles/profile.h" | |
12 #include "content/public/browser/download_item.h" | 32 #include "content/public/browser/download_item.h" |
13 #include "content/public/browser/download_persistent_store_info.h" | 33 #include "content/public/browser/download_manager.h" |
14 | 34 |
35 using content::BrowserThread; | |
15 using content::DownloadItem; | 36 using content::DownloadItem; |
16 using content::DownloadPersistentStoreInfo; | 37 using content::DownloadManager; |
17 | 38 |
18 DownloadHistory::DownloadHistory(Profile* profile) | 39 namespace { |
19 : profile_(profile), | 40 |
20 next_fake_db_handle_(DownloadItem::kUninitializedHandle - 1) { | 41 // The value of |db_handle| indicating that the associated DownloadItem is not |
21 DCHECK(profile); | 42 // yet persisted. |
22 } | 43 static const int64 kUninitializedHandle = -1; |
23 | 44 |
24 DownloadHistory::~DownloadHistory() {} | 45 // Per-DownloadItem data. This information does not belong inside DownloadItem, |
25 | 46 // and keeping maps in DownloadHistory from DownloadItem to this information is |
26 void DownloadHistory::GetNextId( | 47 // error-prone and complicated. Unfortunately, DownloadHistory::removing_ and |
27 const HistoryService::DownloadNextIdCallback& callback) { | 48 // removed_while_adding_ cannot be moved into this class partly because |
28 HistoryService* hs = HistoryServiceFactory::GetForProfile( | 49 // DownloadHistoryData is destroyed when DownloadItems are destroyed, and we |
29 profile_, Profile::EXPLICIT_ACCESS); | 50 // have no control over when DownloadItems are destroyed. |
30 if (!hs) | 51 class DownloadHistoryData : public base::SupportsUserData::Data { |
31 return; | 52 public: |
32 | 53 |
33 hs->GetNextDownloadId(&history_consumer_, callback); | 54 static DownloadHistoryData* Get(DownloadItem* item) { |
34 } | 55 base::SupportsUserData::Data* data = item->GetUserData(kKey); |
35 | 56 return (data == NULL) ? NULL : |
36 void DownloadHistory::Load( | 57 static_cast<DownloadHistoryData*>(data); |
58 } | |
59 | |
60 DownloadHistoryData( | |
61 DownloadItem* item, | |
62 const base::WeakPtr<DownloadHistory>& history) | |
63 : is_adding_(false), | |
64 is_disabled_(false), | |
65 history_(history), | |
66 db_handle_(kUninitializedHandle), | |
67 info_(NULL) { | |
68 item->SetUserData(kKey, this); | |
69 } | |
70 | |
71 virtual ~DownloadHistoryData() { | |
72 } | |
73 | |
74 // Whether this item is currently being added to the database. | |
75 bool is_adding() const { return is_adding_; } | |
76 void set_is_adding(bool a) { is_adding_ = a; } | |
77 | |
78 bool is_disabled() const { return is_disabled_; } | |
79 void set_is_disabled(bool d, DownloadItem* item) { | |
80 if (is_disabled_ == d) | |
81 return; | |
82 is_disabled_ = d; | |
83 // May either add item to db or remove it. | |
84 // Could also call item->NotifyObservers(), but that interface should not be | |
85 // public, and DH is the only observer that knows and cares about | |
86 // is_disabled(). | |
87 if (history_.get()) | |
Randy Smith (Not in Mondays)
2012/08/20 18:57:04
Can you think of any contexts in which a DownloadI
benjhayden
2012/08/24 15:32:15
This was part of DownloadHistory::Disable, which w
| |
88 history_->OnDownloadUpdated(item); | |
89 } | |
90 | |
91 // Whether this item is already persisted in the database. | |
92 bool is_persisted() const { return db_handle_ > kUninitializedHandle; } | |
93 | |
94 int64 db_handle() const { return db_handle_; } | |
95 void set_db_handle(int64 h) { db_handle_ = h; } | |
96 | |
97 // This allows OnDownloadUpdated() to see what changed in a DownloadItem if | |
98 // anything, in order to prevent writing to the database unnecessarily. It is | |
99 // nullified when the item is no longer in progress in order to save memory. | |
100 DownloadPersistentStoreInfo* info() { return info_.get(); } | |
101 void set_info(const DownloadPersistentStoreInfo& i) { | |
102 info_.reset(new DownloadPersistentStoreInfo(i)); | |
103 } | |
104 void clear_info() { | |
105 info_.reset(); | |
106 } | |
107 | |
108 private: | |
109 static const char kKey[]; | |
110 | |
111 bool is_adding_; | |
112 bool is_disabled_; | |
113 base::WeakPtr<DownloadHistory> history_; | |
114 int64 db_handle_; | |
115 scoped_ptr<DownloadPersistentStoreInfo> info_; | |
116 | |
117 DISALLOW_COPY_AND_ASSIGN(DownloadHistoryData); | |
118 }; | |
119 | |
120 const char DownloadHistoryData::kKey[] = | |
121 "DownloadItem DownloadHistoryData"; | |
122 | |
123 DownloadPersistentStoreInfo GetPersistentStoreInfo(DownloadItem* item) { | |
124 DownloadHistoryData* dhd = DownloadHistoryData::Get(item); | |
125 return DownloadPersistentStoreInfo( | |
126 item->GetFullPath(), | |
127 item->GetURL(), | |
128 item->GetReferrerUrl(), | |
129 item->GetStartTime(), | |
130 item->GetEndTime(), | |
131 item->GetReceivedBytes(), | |
132 item->GetTotalBytes(), | |
133 item->GetState(), | |
134 ((dhd != NULL) ? dhd->db_handle() : kUninitializedHandle), | |
135 item->GetOpened()); | |
136 } | |
137 | |
138 } // anonymous namespace | |
139 | |
140 HistoryServiceDownloadAdapter::HistoryServiceDownloadAdapter( | |
141 HistoryService* history_service) | |
142 : history_service_(history_service) { | |
143 } | |
144 | |
145 HistoryServiceDownloadAdapter::~HistoryServiceDownloadAdapter() {} | |
146 | |
147 void HistoryServiceDownloadAdapter::QueryDownloads( | |
37 const HistoryService::DownloadQueryCallback& callback) { | 148 const HistoryService::DownloadQueryCallback& callback) { |
38 HistoryService* hs = HistoryServiceFactory::GetForProfile( | 149 history_service_->QueryDownloads(&history_consumer_, callback); |
39 profile_, Profile::EXPLICIT_ACCESS); | 150 history_service_->CleanUpInProgressEntries(); |
40 if (!hs) | 151 } |
41 return; | 152 |
42 | 153 HistoryService::Handle |
43 hs->QueryDownloads(&history_consumer_, callback); | 154 HistoryServiceDownloadAdapter::GetVisibleVisitCountToHost( |
44 | 155 const GURL& referrer_url, |
45 // This is the initial load, so do a cleanup of corrupt in-progress entries. | 156 const HistoryService::GetVisibleVisitCountToHostCallback& |
46 hs->CleanUpInProgressEntries(); | 157 callback) { |
158 return history_service_->GetVisibleVisitCountToHost( | |
159 referrer_url, &history_consumer_, callback); | |
160 } | |
161 | |
162 void HistoryServiceDownloadAdapter::CreateDownload( | |
163 int32 id, | |
164 const DownloadPersistentStoreInfo& info, | |
165 const HistoryService::DownloadCreateCallback& callback) { | |
166 history_service_->CreateDownload(id, info, &history_consumer_, callback); | |
167 } | |
168 | |
169 void HistoryServiceDownloadAdapter::UpdateDownload( | |
170 const DownloadPersistentStoreInfo& info) { | |
171 history_service_->UpdateDownload(info); | |
Randy Smith (Not in Mondays)
2012/08/27 19:28:19
So the fact that this does not take a db handle in
| |
172 } | |
173 | |
174 void HistoryServiceDownloadAdapter::RemoveDownloads( | |
175 const std::set<int64>& handles) { | |
176 history_service_->RemoveDownloads(handles); | |
177 } | |
178 | |
179 void HistoryServiceDownloadAdapter::OnDownloadHistoryDestroyed() { | |
180 delete this; | |
181 } | |
182 | |
183 // static | |
184 void DownloadHistory::Disable(content::DownloadItem* download_item) { | |
185 DownloadHistoryData* dhd = DownloadHistoryData::Get(download_item); | |
186 // If the item has been created, then it has a DHD. | |
187 dhd->set_is_disabled(true, download_item); | |
188 } | |
189 | |
190 DownloadHistory::DownloadHistory( | |
191 DownloadManager* manager, | |
192 HistoryServiceDownloadAdapter* history) | |
193 : manager_(manager), | |
194 history_(history), | |
195 loading_(false), | |
196 history_size_(0), | |
197 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { | |
198 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
199 DCHECK(manager_); | |
200 manager_->AddObserver(this); | |
201 history_->QueryDownloads(base::Bind( | |
202 &DownloadHistory::QueryCallback, weak_ptr_factory_.GetWeakPtr())); | |
203 } | |
204 | |
205 DownloadHistory::~DownloadHistory() { | |
206 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
207 history_->OnDownloadHistoryDestroyed(); | |
Randy Smith (Not in Mondays)
2012/08/20 18:57:04
Why isn't this just a scoped_ptr<>?
benjhayden
2012/08/24 15:32:15
Done.
| |
208 for (ItemSet::const_iterator iter = observing_items_.begin(); | |
Randy Smith (Not in Mondays)
2012/08/20 18:57:04
Shouldn't this list always be null at this point,
benjhayden
2012/08/24 15:32:15
Yes, DownloadManager::Shutdown() deletes the items
Randy Smith (Not in Mondays)
2012/08/27 18:25:11
Hmmm. Ok. What I hear you saying here is that Do
| |
209 iter != observing_items_.end(); ++iter) { | |
210 (*iter)->RemoveObserver(this); | |
211 } | |
212 observing_items_.clear(); | |
213 if (manager_) | |
214 manager_->RemoveObserver(this); | |
215 } | |
216 | |
217 void DownloadHistory::QueryCallback( | |
218 DownloadHistory::InfoVector* infos) { | |
219 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
220 // We may have caught ManagerGoingDown() before the history loaded. | |
221 if (!manager_) | |
222 return; | |
223 // OnDownloadCreated() is called inside DownloadManager::CreateDownloadItem(), | |
224 // before we have a chance to attach a DownloadHistoryData, so temporarily | |
225 // disable adding new unpersisted items to the history. All methods run on | |
226 // the UI thread and CreateDownloadItem() is synchronous, so it is impossible | |
227 // for an OnDownloadCreated() to come in for another DownloadItem while we are | |
228 // processing the database. | |
229 loading_ = true; | |
230 for (InfoVector::const_iterator it = infos->begin(); | |
231 it != infos->end(); ++it) { | |
232 DownloadItem* download_item = manager_->CreateDownloadItem( | |
233 it->path, | |
234 it->url, | |
235 it->referrer_url, | |
236 it->start_time, | |
237 it->end_time, | |
238 it->received_bytes, | |
239 it->total_bytes, | |
240 it->state, | |
241 it->opened); | |
242 DownloadHistoryData* dhd = DownloadHistoryData::Get(download_item); | |
243 DCHECK(it->db_handle > kUninitializedHandle); | |
244 dhd->set_db_handle(it->db_handle); | |
245 ++history_size_; | |
246 } | |
247 manager_->CheckForHistoryFilesRemoval(); | |
248 loading_ = false; | |
249 } | |
250 | |
251 void DownloadHistory::OnDownloadCreated( | |
252 DownloadManager* manager, DownloadItem* item) { | |
253 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
254 | |
255 // Observe even temporary downloads in case they are marked not temporary. | |
256 item->AddObserver(this); | |
257 observing_items_.insert(item); | |
258 // All downloads should pass through OnDownloadCreated exactly once. | |
259 CHECK(!DownloadHistoryData::Get(item)); | |
260 DownloadHistoryData* dhd = new DownloadHistoryData( | |
261 item, weak_ptr_factory_.GetWeakPtr()); | |
262 if (item->GetState() == DownloadItem::IN_PROGRESS) { | |
263 dhd->set_info(GetPersistentStoreInfo(item)); | |
264 } | |
265 MaybeAddToHistory(item); | |
266 } | |
267 | |
268 void DownloadHistory::MaybeAddToHistory(DownloadItem* item) { | |
269 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
270 int32 download_id = item->GetId(); | |
271 DownloadHistoryData* dhd = DownloadHistoryData::Get(item); | |
272 bool removing = (removing_.find(dhd->db_handle()) != removing_.end()); | |
273 // TODO(benjhayden): Remove IsTemporary(). | |
274 if (!loading_ && | |
Randy Smith (Not in Mondays)
2012/08/20 18:57:04
Suggestion: Reverse the sense of the tests and ret
benjhayden
2012/08/24 15:32:15
Done.
| |
275 !download_crx_util::IsExtensionDownload(*item) && | |
276 !dhd->is_persisted() && | |
277 !dhd->is_disabled() && | |
278 !item->IsTemporary() && | |
279 !removing && | |
280 !dhd->is_adding()) { | |
281 dhd->set_is_adding(true); | |
282 if (dhd->info() == NULL) { | |
283 // Keep the info here regardless of whether the item is in progress so | |
284 // that, when ItemAdded() calls OnDownloadUpdated(), it can choose more | |
285 // intelligently whether to Update the db and/or discard the info. | |
286 dhd->set_info(GetPersistentStoreInfo(item)); | |
287 } | |
288 HistoryService::DownloadCreateCallback callback = base::Bind( | |
289 &DownloadHistory::ItemAdded, weak_ptr_factory_.GetWeakPtr()); | |
290 history_->CreateDownload(download_id, *dhd->info(), callback); | |
291 } | |
292 } | |
293 | |
294 void DownloadHistory::ItemAdded(int32 download_id, int64 db_handle) { | |
295 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
296 | |
297 if (!manager_) | |
298 return; | |
299 | |
300 if (removed_while_adding_.find(download_id) != | |
301 removed_while_adding_.end()) { | |
302 removed_while_adding_.erase(download_id); | |
303 if (removing_.empty()) { | |
304 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
305 base::Bind(&DownloadHistory::RemoveDownloadsBatch, | |
306 weak_ptr_factory_.GetWeakPtr())); | |
307 } | |
308 removing_.insert(db_handle); | |
309 return; | |
310 } | |
311 | |
312 DownloadItem* item = manager_->GetDownload(download_id); | |
313 if (!item) { | |
314 // We should have caught OnDownloadDestroyed(). If the item should have | |
315 // been removed from history, then OnDownloadRemoved() put |download_id| in | |
316 // removed_while_adding_. | |
317 return; | |
318 } | |
319 | |
320 UMA_HISTOGRAM_CUSTOM_COUNTS("Download.HistorySize2", | |
321 history_size_, | |
322 0/*min*/, | |
323 (1 << 23)/*max*/, | |
324 (1 << 7)/*num_buckets*/); | |
325 ++history_size_; | |
326 | |
327 DownloadHistoryData* dhd = DownloadHistoryData::Get(item); | |
328 dhd->set_is_adding(false); | |
329 DCHECK(db_handle > kUninitializedHandle); | |
330 dhd->set_db_handle(db_handle); | |
331 | |
332 // In case the item changed or became temporary while it was being added. | |
333 // Don't just UpdateObservers() because we're the only observer that can also | |
334 // see db_handle, which is the only thing that we changed. | |
335 OnDownloadUpdated(item); | |
336 } | |
337 | |
338 void DownloadHistory::OnDownloadUpdated(DownloadItem* item) { | |
339 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
340 | |
341 DownloadHistoryData* dhd = DownloadHistoryData::Get(item); | |
342 if (!dhd->is_persisted()) { | |
343 MaybeAddToHistory(item); | |
Randy Smith (Not in Mondays)
2012/08/20 18:57:04
Under what circumstances would we actually want to
benjhayden
2012/08/24 15:32:15
Transition from Temporary to not Temporary. (In th
Randy Smith (Not in Mondays)
2012/08/27 18:25:11
Yuck; I had adding state transitions for transitio
Randy Smith (Not in Mondays)
2012/08/27 19:28:19
Also: You have the same call to MaybeAddToHistory(
| |
344 return; | |
345 } | |
346 if (item->IsTemporary() || | |
347 dhd->is_disabled()) { | |
348 OnDownloadRemoved(item); | |
349 return; | |
350 } | |
351 | |
352 // TODO(asanka): Persist GetTargetFilePath() as well. | |
353 DownloadPersistentStoreInfo current_info(GetPersistentStoreInfo(item)); | |
354 DownloadPersistentStoreInfo* previous_info = dhd->info(); | |
355 bool do_update = ( | |
356 (previous_info == NULL) || | |
357 (previous_info->path != current_info.path) || | |
358 (previous_info->end_time != current_info.end_time) || | |
359 (previous_info->received_bytes != current_info.received_bytes) || | |
360 (previous_info->total_bytes != current_info.total_bytes) || | |
361 (previous_info->state != current_info.state) || | |
362 (previous_info->opened != current_info.opened)); | |
363 UMA_HISTOGRAM_ENUMERATION("Download.HistoryPropagatedUpdate", do_update, 2); | |
364 if (do_update) { | |
365 history_->UpdateDownload(current_info); | |
366 } | |
367 if (item->GetState() == DownloadItem::IN_PROGRESS) { | |
368 dhd->set_info(current_info); | |
369 } else { | |
370 dhd->clear_info(); | |
371 } | |
372 } | |
373 | |
374 // Downloads may be opened after they are completed. | |
375 void DownloadHistory::OnDownloadOpened(DownloadItem* item) { | |
376 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
377 DownloadHistoryData* dhd = DownloadHistoryData::Get(item); | |
378 if (!dhd->is_persisted()) { | |
379 MaybeAddToHistory(item); | |
380 return; | |
381 } | |
382 if (item->IsTemporary() || | |
383 dhd->is_disabled()) { | |
384 OnDownloadRemoved(item); | |
385 return; | |
386 } | |
387 | |
388 DownloadPersistentStoreInfo current_info(GetPersistentStoreInfo(item)); | |
389 history_->UpdateDownload(current_info); | |
390 if (item->GetState() == DownloadItem::IN_PROGRESS) { | |
391 dhd->set_info(current_info); | |
392 } | |
393 } | |
394 | |
395 void DownloadHistory::OnDownloadRemoved(DownloadItem* item) { | |
396 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
397 | |
398 DownloadHistoryData* dhd = DownloadHistoryData::Get(item); | |
399 if (dhd == NULL) | |
Randy Smith (Not in Mondays)
2012/08/20 18:57:04
How could this happen?
benjhayden
2012/08/24 15:32:15
Done.
| |
400 return; | |
401 if (!dhd->is_persisted()) { | |
402 if (dhd->is_adding()) { | |
403 removed_while_adding_.insert(item->GetId()); | |
404 } | |
405 return; | |
406 } | |
407 | |
408 // For database efficiency, batch removals together if they happen all at | |
409 // once. | |
410 if (removing_.empty()) { | |
411 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
412 base::Bind(&DownloadHistory::RemoveDownloadsBatch, | |
413 weak_ptr_factory_.GetWeakPtr())); | |
414 } | |
415 removing_.insert(dhd->db_handle()); | |
416 dhd->set_db_handle(kUninitializedHandle); | |
417 --history_size_; | |
418 } | |
419 | |
420 void DownloadHistory::RemoveDownloadsBatch() { | |
421 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
422 HandleSet remove_handles; | |
423 removing_.swap(remove_handles); | |
424 history_->RemoveDownloads(remove_handles); | |
425 } | |
426 | |
427 void DownloadHistory::ManagerGoingDown(DownloadManager* manager) { | |
428 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
429 DCHECK_EQ(manager_, manager); | |
430 manager_->RemoveObserver(this); | |
431 manager_ = NULL; | |
432 } | |
433 | |
434 void DownloadHistory::OnDownloadDestroyed(DownloadItem* item) { | |
435 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
436 item->RemoveObserver(this); | |
437 observing_items_.erase(item); | |
47 } | 438 } |
48 | 439 |
49 void DownloadHistory::CheckVisitedReferrerBefore( | 440 void DownloadHistory::CheckVisitedReferrerBefore( |
50 int32 download_id, | 441 int32 download_id, |
51 const GURL& referrer_url, | 442 const GURL& referrer_url, |
52 const VisitedBeforeDoneCallback& callback) { | 443 const VisitedBeforeDoneCallback& callback) { |
444 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
53 if (referrer_url.is_valid()) { | 445 if (referrer_url.is_valid()) { |
54 HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( | 446 HistoryService::Handle handle = history_->GetVisibleVisitCountToHost( |
55 profile_, Profile::EXPLICIT_ACCESS); | 447 referrer_url, |
56 if (hs) { | 448 base::Bind(&DownloadHistory::OnGotVisitCountToHost, |
57 HistoryService::Handle handle = | 449 weak_ptr_factory_.GetWeakPtr())); |
58 hs->GetVisibleVisitCountToHost(referrer_url, &history_consumer_, | 450 visited_before_requests_[handle] = callback; |
59 base::Bind(&DownloadHistory::OnGotVisitCountToHost, | 451 return; |
60 base::Unretained(this))); | |
61 visited_before_requests_[handle] = callback; | |
62 return; | |
63 } | |
64 } | 452 } |
65 callback.Run(false); | 453 callback.Run(false); |
66 } | 454 } |
67 | 455 |
68 void DownloadHistory::AddEntry( | 456 void DownloadHistory::OnGotVisitCountToHost( |
69 DownloadItem* download_item, | 457 HistoryService::Handle handle, |
70 const HistoryService::DownloadCreateCallback& callback) { | 458 bool found_visits, |
71 DCHECK(download_item); | 459 int count, |
72 // Do not store the download in the history database for a few special cases: | 460 base::Time first_visit) { |
73 // - incognito mode (that is the point of this mode) | 461 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
74 // - extensions (users don't think of extension installation as 'downloading') | |
75 // - temporary download, like in drag-and-drop | |
76 // - history service is not available (e.g. in tests) | |
77 // We have to make sure that these handles don't collide with normal db | |
78 // handles, so we use a negative value. Eventually, they could overlap, but | |
79 // you'd have to do enough downloading that your ISP would likely stab you in | |
80 // the neck first. YMMV. | |
81 HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( | |
82 profile_, Profile::EXPLICIT_ACCESS); | |
83 if (download_crx_util::IsExtensionDownload(*download_item) || | |
84 download_item->IsTemporary() || !hs) { | |
85 callback.Run(download_item->GetId(), GetNextFakeDbHandle()); | |
86 return; | |
87 } | |
88 | |
89 int32 id = download_item->GetId(); | |
90 DownloadPersistentStoreInfo history_info = | |
91 download_item->GetPersistentStoreInfo(); | |
92 hs->CreateDownload(id, history_info, &history_consumer_, callback); | |
93 } | |
94 | |
95 void DownloadHistory::UpdateEntry(DownloadItem* download_item) { | |
96 // Don't store info in the database if the download was initiated while in | |
97 // incognito mode or if it hasn't been initialized in our database table. | |
98 if (download_item->GetDbHandle() <= DownloadItem::kUninitializedHandle) | |
99 return; | |
100 | |
101 HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( | |
102 profile_, Profile::EXPLICIT_ACCESS); | |
103 if (!hs) | |
104 return; | |
105 hs->UpdateDownload(download_item->GetPersistentStoreInfo()); | |
106 } | |
107 | |
108 void DownloadHistory::UpdateDownloadPath(DownloadItem* download_item, | |
109 const FilePath& new_path) { | |
110 // No update necessary if the download was initiated while in incognito mode. | |
111 if (download_item->GetDbHandle() <= DownloadItem::kUninitializedHandle) | |
112 return; | |
113 | |
114 HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( | |
115 profile_, Profile::EXPLICIT_ACCESS); | |
116 if (hs) | |
117 hs->UpdateDownloadPath(new_path, download_item->GetDbHandle()); | |
118 } | |
119 | |
120 void DownloadHistory::RemoveEntry(DownloadItem* download_item) { | |
121 // No update necessary if the download was initiated while in incognito mode. | |
122 if (download_item->GetDbHandle() <= DownloadItem::kUninitializedHandle) | |
123 return; | |
124 | |
125 HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( | |
126 profile_, Profile::EXPLICIT_ACCESS); | |
127 if (hs) | |
128 hs->RemoveDownload(download_item->GetDbHandle()); | |
129 } | |
130 | |
131 void DownloadHistory::RemoveEntriesBetween(const base::Time remove_begin, | |
132 const base::Time remove_end) { | |
133 HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( | |
134 profile_, Profile::EXPLICIT_ACCESS); | |
135 if (hs) | |
136 hs->RemoveDownloadsBetween(remove_begin, remove_end); | |
137 } | |
138 | |
139 int64 DownloadHistory::GetNextFakeDbHandle() { | |
140 return next_fake_db_handle_--; | |
141 } | |
142 | |
143 void DownloadHistory::OnGotVisitCountToHost(HistoryService::Handle handle, | |
144 bool found_visits, | |
145 int count, | |
146 base::Time first_visit) { | |
147 VisitedBeforeRequestsMap::iterator request = | 462 VisitedBeforeRequestsMap::iterator request = |
148 visited_before_requests_.find(handle); | 463 visited_before_requests_.find(handle); |
149 DCHECK(request != visited_before_requests_.end()); | 464 DCHECK(request != visited_before_requests_.end()); |
150 VisitedBeforeDoneCallback callback = request->second; | 465 VisitedBeforeDoneCallback callback = request->second; |
151 visited_before_requests_.erase(request); | 466 visited_before_requests_.erase(request); |
152 callback.Run(found_visits && count && | 467 callback.Run(found_visits && count && |
153 (first_visit.LocalMidnight() < base::Time::Now().LocalMidnight())); | 468 (first_visit.LocalMidnight() < base::Time::Now().LocalMidnight())); |
154 } | 469 } |
OLD | NEW |