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 manages persisting DownloadItems to the history service by | |
6 // observing a single DownloadManager and all its DownloadItems using an | |
7 // AllDownloadItemNotifier. | |
8 // | |
9 // DownloadHistory decides whether and when to add items to, remove items from, | |
10 // and update items in the database. DownloadHistory uses DownloadHistoryData to | |
11 // store per-DownloadItem data such as its db_handle, whether the item is being | |
12 // added and waiting for its db_handle, and the last DownloadPersistentStoreInfo | |
13 // that was passed to the database. When the DownloadManager and its delegate | |
14 // (ChromeDownloadManagerDelegate) are initialized, DownloadHistory is created | |
15 // and queries the HistoryService. When the HistoryService calls back from | |
16 // QueryDownloads() to QueryCallback(), DownloadHistory uses | |
17 // DownloadManager::CreateDownloadItem() to inform DownloadManager of these | |
18 // persisted DownloadItems. CreateDownloadItem() internally calls | |
19 // OnDownloadCreated(), which normally adds items to the database, so | |
20 // QueryCallback() uses |loading_db_handle_| to disable adding these items to | |
21 // the database as it matches them up with their db_handles. If a download is | |
22 // removed via OnDownloadRemoved() while the item is still being added to the | |
23 // database, DownloadHistory uses |removed_while_adding_| to remember to remove | |
24 // the item when its ItemAdded() callback is called. All callbacks are bound | |
25 // with a weak pointer to DownloadHistory to prevent use-after-free bugs. | |
26 // ChromeDownloadManagerDelegate owns DownloadHistory, and deletes it in | |
27 // Shutdown(), which is called by DownloadManagerImpl::Shutdown() after all | |
28 // DownloadItems are destroyed. | |
29 // | |
30 // Strictly speaking, the weak pointers in the callbacks from the history system | |
31 // are redundant with the CancelableRequestConsumer. | |
32 // TODO(benjhayden) Use PostTaskAndReply with the weak pointers instead of | |
33 // CancelableRequestConsumer. This requires modifying the downloads-related | |
34 // portion of the HistoryService interface. | |
35 | |
5 #include "chrome/browser/download/download_history.h" | 36 #include "chrome/browser/download/download_history.h" |
6 | 37 |
7 #include "base/logging.h" | 38 #include "base/metrics/histogram.h" |
8 #include "chrome/browser/download/download_crx_util.h" | 39 #include "chrome/browser/download/download_crx_util.h" |
9 #include "chrome/browser/history/history_marshaling.h" | 40 #include "chrome/browser/history/download_database.h" |
10 #include "chrome/browser/history/history_service_factory.h" | 41 #include "chrome/browser/history/download_persistent_store_info.h" |
11 #include "chrome/browser/profiles/profile.h" | 42 #include "chrome/browser/history/history.h" |
43 #include "content/public/browser/browser_thread.h" | |
12 #include "content/public/browser/download_item.h" | 44 #include "content/public/browser/download_item.h" |
13 #include "content/public/browser/download_persistent_store_info.h" | 45 #include "content/public/browser/download_manager.h" |
14 | 46 |
15 using content::DownloadItem; | 47 namespace { |
16 using content::DownloadPersistentStoreInfo; | 48 |
17 | 49 // Per-DownloadItem data. This information does not belong inside DownloadItem, |
18 DownloadHistory::DownloadHistory(Profile* profile) | 50 // and keeping maps in DownloadHistory from DownloadItem to this information is |
19 : profile_(profile), | 51 // error-prone and complicated. Unfortunately, DownloadHistory::removing_*_ and |
20 next_fake_db_handle_(DownloadItem::kUninitializedHandle - 1) { | 52 // removed_while_adding_ cannot be moved into this class partly because |
21 DCHECK(profile); | 53 // DownloadHistoryData is destroyed when DownloadItems are destroyed, and we |
22 } | 54 // have no control over when DownloadItems are destroyed. |
23 | 55 class DownloadHistoryData : public base::SupportsUserData::Data { |
24 DownloadHistory::~DownloadHistory() {} | 56 public: |
25 | 57 static DownloadHistoryData* Get(content::DownloadItem* item) { |
26 void DownloadHistory::GetNextId( | 58 base::SupportsUserData::Data* data = item->GetUserData(kKey); |
27 const HistoryService::DownloadNextIdCallback& callback) { | 59 return (data == NULL) ? NULL : |
28 HistoryService* hs = HistoryServiceFactory::GetForProfile( | 60 static_cast<DownloadHistoryData*>(data); |
29 profile_, Profile::EXPLICIT_ACCESS); | 61 } |
30 if (!hs) | 62 |
31 return; | 63 DownloadHistoryData(content::DownloadItem* item, int64 handle) |
32 | 64 : is_adding_(false), |
33 hs->GetNextDownloadId(&history_consumer_, callback); | 65 db_handle_(handle), |
34 } | 66 info_(NULL) { |
35 | 67 item->SetUserData(kKey, this); |
36 void DownloadHistory::Load( | 68 } |
69 | |
70 virtual ~DownloadHistoryData() { | |
71 } | |
72 | |
73 // Whether this item is currently being added to the database. | |
74 bool is_adding() const { return is_adding_; } | |
75 void set_is_adding(bool a) { is_adding_ = a; } | |
76 | |
77 // Whether this item is already persisted in the database. | |
78 bool is_persisted() const { | |
79 return db_handle_ != history::DownloadDatabase::kUninitializedHandle; | |
80 } | |
81 | |
82 int64 db_handle() const { return db_handle_; } | |
83 void set_db_handle(int64 h) { db_handle_ = h; } | |
84 | |
85 // This allows DownloadHistory::OnDownloadUpdated() to see what changed in a | |
86 // DownloadItem if anything, in order to prevent writing to the database | |
87 // unnecessarily. It is nullified when the item is no longer in progress in | |
88 // order to save memory. | |
89 DownloadPersistentStoreInfo* info() { return info_.get(); } | |
90 void set_info(const DownloadPersistentStoreInfo& i) { | |
91 info_.reset(new DownloadPersistentStoreInfo(i)); | |
92 } | |
93 void clear_info() { | |
94 info_.reset(); | |
95 } | |
96 | |
97 private: | |
98 static const char kKey[]; | |
99 | |
100 bool is_adding_; | |
101 int64 db_handle_; | |
102 scoped_ptr<DownloadPersistentStoreInfo> info_; | |
103 | |
104 DISALLOW_COPY_AND_ASSIGN(DownloadHistoryData); | |
105 }; | |
106 | |
107 const char DownloadHistoryData::kKey[] = | |
108 "DownloadItem DownloadHistoryData"; | |
109 | |
110 DownloadPersistentStoreInfo GetPersistentStoreInfo( | |
111 content::DownloadItem* item) { | |
112 DownloadHistoryData* data = DownloadHistoryData::Get(item); | |
113 return DownloadPersistentStoreInfo( | |
114 item->GetFullPath(), | |
115 item->GetURL(), | |
116 item->GetReferrerUrl(), | |
117 item->GetStartTime(), | |
118 item->GetEndTime(), | |
119 item->GetReceivedBytes(), | |
120 item->GetTotalBytes(), | |
121 item->GetState(), | |
122 ((data != NULL) ? data->db_handle() | |
123 : history::DownloadDatabase::kUninitializedHandle), | |
124 item->GetOpened()); | |
125 } | |
126 | |
127 bool ShouldUpdateHistory(const DownloadPersistentStoreInfo* previous, | |
128 const DownloadPersistentStoreInfo& current) { | |
129 // Ignore url, referrer, start_time, db_handle, which don't change. | |
130 return ((previous == NULL) || | |
131 (previous->path != current.path) || | |
132 (previous->end_time != current.end_time) || | |
133 (previous->received_bytes != current.received_bytes) || | |
134 (previous->total_bytes != current.total_bytes) || | |
135 (previous->state != current.state) || | |
136 (previous->opened != current.opened)); | |
137 } | |
138 | |
139 typedef std::vector<DownloadPersistentStoreInfo> InfoVector; | |
140 | |
141 } // anonymous namespace | |
142 | |
143 DownloadHistory::HistoryAdapter::HistoryAdapter(HistoryService* history) | |
144 : history_(history) { | |
145 } | |
146 DownloadHistory::HistoryAdapter::~HistoryAdapter() {} | |
147 | |
148 void DownloadHistory::HistoryAdapter::QueryDownloads( | |
37 const HistoryService::DownloadQueryCallback& callback) { | 149 const HistoryService::DownloadQueryCallback& callback) { |
38 HistoryService* hs = HistoryServiceFactory::GetForProfile( | 150 history_->QueryDownloads(&consumer_, callback); |
39 profile_, Profile::EXPLICIT_ACCESS); | 151 } |
40 if (!hs) | 152 |
41 return; | 153 void DownloadHistory::HistoryAdapter::CreateDownload( |
42 | 154 const DownloadPersistentStoreInfo& info, |
43 hs->QueryDownloads(&history_consumer_, callback); | 155 const HistoryService::DownloadCreateCallback& callback) { |
44 | 156 history_->CreateDownload(info, &consumer_, callback); |
45 // This is the initial load, so do a cleanup of corrupt in-progress entries. | 157 } |
46 hs->CleanUpInProgressEntries(); | 158 |
47 } | 159 void DownloadHistory::HistoryAdapter::UpdateDownload( |
48 | 160 const DownloadPersistentStoreInfo& data) { |
49 void DownloadHistory::CheckVisitedReferrerBefore( | 161 history_->UpdateDownload(data); |
50 int32 download_id, | 162 } |
51 const GURL& referrer_url, | 163 |
52 const VisitedBeforeDoneCallback& callback) { | 164 void DownloadHistory::HistoryAdapter::RemoveDownloads( |
53 if (referrer_url.is_valid()) { | 165 const std::set<int64>& db_handles) { |
54 HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( | 166 history_->RemoveDownloads(db_handles); |
55 profile_, Profile::EXPLICIT_ACCESS); | 167 } |
56 if (hs) { | 168 |
57 HistoryService::Handle handle = | 169 |
58 hs->GetVisibleVisitCountToHost(referrer_url, &history_consumer_, | 170 DownloadHistory::Observer::Observer() {} |
59 base::Bind(&DownloadHistory::OnGotVisitCountToHost, | 171 DownloadHistory::Observer::~Observer() {} |
60 base::Unretained(this))); | 172 |
61 visited_before_requests_[handle] = callback; | 173 bool DownloadHistory::IsPersisted(content::DownloadItem* item) { |
62 return; | 174 DownloadHistoryData* data = DownloadHistoryData::Get(item); |
175 return data && data->is_persisted(); | |
176 } | |
177 | |
178 DownloadHistory::DownloadHistory( | |
179 content::DownloadManager* manager, | |
180 scoped_ptr<HistoryAdapter> history) | |
181 : ALLOW_THIS_IN_INITIALIZER_LIST(notifier_(manager, this)), | |
182 history_(history.Pass()), | |
183 loading_db_handle_(history::DownloadDatabase::kUninitializedHandle), | |
184 history_size_(0), | |
185 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { | |
186 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
187 content::DownloadManager::DownloadVector items; | |
188 notifier_.GetManager()->GetAllDownloads(&items); | |
189 for (content::DownloadManager::DownloadVector::const_iterator | |
190 it = items.begin(); it != items.end(); ++it) { | |
191 OnDownloadCreated(notifier_.GetManager(), *it); | |
192 } | |
193 history_->QueryDownloads(base::Bind( | |
194 &DownloadHistory::QueryCallback, weak_ptr_factory_.GetWeakPtr())); | |
195 } | |
196 | |
197 DownloadHistory::~DownloadHistory() { | |
198 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
199 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadHistoryDestroyed()); | |
200 observers_.Clear(); | |
201 } | |
202 | |
203 void DownloadHistory::AddObserver(DownloadHistory::Observer* observer) { | |
204 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
205 observers_.AddObserver(observer); | |
206 } | |
207 | |
208 void DownloadHistory::RemoveObserver(DownloadHistory::Observer* observer) { | |
209 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
210 observers_.RemoveObserver(observer); | |
211 } | |
212 | |
213 void DownloadHistory::QueryCallback(InfoVector* infos) { | |
214 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
215 // ManagerGoingDown() may have happened before the history loaded. | |
216 if (!notifier_.GetManager()) | |
217 return; | |
218 for (InfoVector::const_iterator it = infos->begin(); | |
219 it != infos->end(); ++it) { | |
220 // OnDownloadCreated() is called inside DM::CreateDownloadItem(), so set | |
221 // loading_db_handle_ to match up the created item with its db_handle. All | |
222 // methods run on the UI thread and CreateDownloadItem() is synchronous. | |
223 loading_db_handle_ = it->db_handle; | |
224 content::DownloadItem* download_item = | |
225 notifier_.GetManager()->CreateDownloadItem( | |
226 it->path, | |
227 it->url, | |
228 it->referrer_url, | |
229 it->start_time, | |
230 it->end_time, | |
231 it->received_bytes, | |
232 it->total_bytes, | |
233 it->state, | |
234 it->opened); | |
235 DownloadHistoryData* data = DownloadHistoryData::Get(download_item); | |
236 | |
237 // If this DCHECK fails, then you probably added an Observer that | |
238 // synchronously creates a DownloadItem in response to | |
239 // DownloadManager::OnDownloadCreated(), and your observer runs before | |
240 // DownloadHistory, and DownloadManager creates items synchronously. Just | |
241 // bounce your DownloadItem creation off the message loop to flush | |
242 // DownloadHistory::OnDownloadCreated. | |
243 DCHECK_EQ(it->db_handle, data->db_handle()); | |
244 ++history_size_; | |
245 } | |
246 notifier_.GetManager()->CheckForHistoryFilesRemoval(); | |
247 } | |
248 | |
249 void DownloadHistory::MaybeAddToHistory(content::DownloadItem* item) { | |
250 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
251 | |
252 int32 download_id = item->GetId(); | |
253 DownloadHistoryData* data = DownloadHistoryData::Get(item); | |
254 bool removing = (removing_handles_.find(data->db_handle()) != | |
255 removing_handles_.end()); | |
256 | |
257 // TODO(benjhayden): Remove IsTemporary(). | |
258 if (download_crx_util::IsExtensionDownload(*item) || | |
259 item->IsTemporary() || | |
260 data->is_adding() || | |
261 data->is_persisted() || | |
262 removing) | |
263 return; | |
264 | |
265 data->set_is_adding(true); | |
266 if (data->info() == NULL) { | |
267 // Keep the info here regardless of whether the item is in progress so that, | |
268 // when ItemAdded() calls OnDownloadUpdated(), it can decide whether to | |
269 // Update the db and/or clear the info. | |
270 data->set_info(GetPersistentStoreInfo(item)); | |
271 } | |
272 | |
273 history_->CreateDownload(*data->info(), base::Bind( | |
274 &DownloadHistory::ItemAdded, weak_ptr_factory_.GetWeakPtr(), | |
275 download_id)); | |
276 } | |
277 | |
278 void DownloadHistory::ItemAdded(int32 download_id, int64 db_handle) { | |
279 if (removed_while_adding_.find(download_id) != | |
280 removed_while_adding_.end()) { | |
281 removed_while_adding_.erase(download_id); | |
282 ScheduleRemoveDownload(download_id, db_handle); | |
283 return; | |
284 } | |
285 | |
286 if (!notifier_.GetManager()) | |
287 return; | |
288 | |
289 content::DownloadItem* item = notifier_.GetManager()->GetDownload( | |
290 download_id); | |
291 if (!item) { | |
asanka
2012/11/08 22:45:53
DCHECK instead?
benjhayden
2012/11/12 18:44:16
I don't see any mechanism to prevent items from be
| |
292 // This item will have called OnDownloadDestroyed(). If the item should | |
293 // have been removed from history, then it would have also called | |
294 // OnDownloadRemoved(), which would have put |download_id| in | |
295 // removed_while_adding_, handled above. | |
296 return; | |
297 } | |
298 | |
299 DownloadHistoryData* data = DownloadHistoryData::Get(item); | |
300 data->set_is_adding(false); | |
301 | |
302 // The sql INSERT statement failed. Avoid an infinite loop: don't | |
303 // automatically retry. Retry adding the next time the item is updated by | |
304 // unsetting is_adding. | |
305 if (db_handle == history::DownloadDatabase::kUninitializedHandle) { | |
306 DVLOG(20) << __FUNCTION__ << " INSERT failed id=" << download_id; | |
307 return; | |
308 } | |
309 | |
310 data->set_db_handle(db_handle); | |
311 | |
312 // Send to observers the actual DownloadPersistentStoreInfo that was sent to | |
313 // the db, plus the db_handle, instead of completely regenerating the | |
314 // DownloadPersistentStoreInfo, in order to accurately reflect the contents of | |
315 // the database. | |
316 data->info()->db_handle = db_handle; | |
317 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadStored( | |
318 item, *data->info())); | |
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 // In case the item changed or became temporary while it was being added. | |
328 // Don't just update all of the item's observers because we're the only | |
329 // observer that can also see db_handle, which is the only thing that | |
330 // ItemAdded changed. | |
331 OnDownloadUpdated(notifier_.GetManager(), item); | |
332 } | |
333 | |
334 void DownloadHistory::OnDownloadCreated( | |
335 content::DownloadManager* manager, content::DownloadItem* item) { | |
336 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
337 | |
338 // All downloads should pass through OnDownloadCreated exactly once. | |
339 CHECK(!DownloadHistoryData::Get(item)); | |
340 DownloadHistoryData* data = new DownloadHistoryData(item, loading_db_handle_); | |
341 loading_db_handle_ = history::DownloadDatabase::kUninitializedHandle; | |
342 if (item->GetState() == content::DownloadItem::IN_PROGRESS) { | |
343 data->set_info(GetPersistentStoreInfo(item)); | |
344 } | |
345 MaybeAddToHistory(item); | |
346 } | |
347 | |
348 void DownloadHistory::OnDownloadUpdated( | |
349 content::DownloadManager* manager, content::DownloadItem* item) { | |
350 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
351 | |
352 DownloadHistoryData* data = DownloadHistoryData::Get(item); | |
353 if (!data->is_persisted()) { | |
354 MaybeAddToHistory(item); | |
355 return; | |
356 } | |
357 if (item->IsTemporary()) { | |
358 OnDownloadRemoved(notifier_.GetManager(), item); | |
359 return; | |
360 } | |
361 | |
362 // TODO(asanka): Persist GetTargetFilePath() as well. | |
363 DownloadPersistentStoreInfo current_info(GetPersistentStoreInfo(item)); | |
364 bool should_update = ShouldUpdateHistory(data->info(), current_info); | |
365 UMA_HISTOGRAM_ENUMERATION("Download.HistoryPropagatedUpdate", | |
366 should_update, 2); | |
367 if (should_update) { | |
368 history_->UpdateDownload(current_info); | |
369 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadStored( | |
370 item, current_info)); | |
371 } | |
372 if (item->GetState() == content::DownloadItem::IN_PROGRESS) { | |
373 data->set_info(current_info); | |
374 } else { | |
375 data->clear_info(); | |
376 } | |
377 } | |
378 | |
379 void DownloadHistory::OnDownloadOpened( | |
380 content::DownloadManager* manager, content::DownloadItem* item) { | |
381 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
382 DownloadHistoryData* data = DownloadHistoryData::Get(item); | |
383 if (!data->is_persisted()) { | |
384 MaybeAddToHistory(item); | |
385 return; | |
386 } | |
387 if (item->IsTemporary()) { | |
388 OnDownloadRemoved(manager, item); | |
389 return; | |
390 } | |
391 // Downloads may be opened after they are completed, so don't rely on | |
392 // DownloadHistoryData::info(). | |
asanka
2012/11/08 22:45:53
Given that ShouldUpdateHistory() returns true if i
benjhayden
2012/11/12 18:44:16
Done.
| |
393 | |
394 DownloadPersistentStoreInfo current_info(GetPersistentStoreInfo(item)); | |
395 history_->UpdateDownload(current_info); | |
396 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadStored(item, current_info)); | |
397 } | |
398 | |
399 void DownloadHistory::OnDownloadRemoved( | |
400 content::DownloadManager* manager, content::DownloadItem* item) { | |
401 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
402 | |
403 DownloadHistoryData* data = DownloadHistoryData::Get(item); | |
404 if (!data->is_persisted()) { | |
405 if (data->is_adding()) { | |
406 // ScheduleRemoveDownload will be called when history_ calls ItemAdded(). | |
407 removed_while_adding_.insert(item->GetId()); | |
63 } | 408 } |
64 } | 409 return; |
65 callback.Run(false); | 410 } |
66 } | 411 ScheduleRemoveDownload(item->GetId(), data->db_handle()); |
67 | 412 data->set_db_handle(history::DownloadDatabase::kUninitializedHandle); |
68 void DownloadHistory::AddEntry( | 413 // ItemAdded increments history_size_ only if the item wasn't |
69 DownloadItem* download_item, | 414 // removed_while_adding_, so the next line does not belong in |
70 const HistoryService::DownloadCreateCallback& callback) { | 415 // ScheduleRemoveDownload(). |
71 DCHECK(download_item); | 416 --history_size_; |
72 // Do not store the download in the history database for a few special cases: | 417 } |
73 // - incognito mode (that is the point of this mode) | 418 |
74 // - extensions (users don't think of extension installation as 'downloading') | 419 void DownloadHistory::ScheduleRemoveDownload( |
75 // - temporary download, like in drag-and-drop | 420 int32 download_id, int64 db_handle) { |
76 // - history service is not available (e.g. in tests) | 421 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
77 // We have to make sure that these handles don't collide with normal db | 422 if (db_handle == history::DownloadDatabase::kUninitializedHandle) |
78 // handles, so we use a negative value. Eventually, they could overlap, but | 423 return; |
79 // you'd have to do enough downloading that your ISP would likely stab you in | 424 |
80 // the neck first. YMMV. | 425 // For database efficiency, batch removals together if they happen all at |
81 HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( | 426 // once. |
82 profile_, Profile::EXPLICIT_ACCESS); | 427 if (removing_handles_.empty()) { |
83 if (download_crx_util::IsExtensionDownload(*download_item) || | 428 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, |
84 download_item->IsTemporary() || !hs) { | 429 base::Bind(&DownloadHistory::RemoveDownloadsBatch, |
85 callback.Run(download_item->GetId(), GetNextFakeDbHandle()); | 430 weak_ptr_factory_.GetWeakPtr())); |
86 return; | 431 } |
87 } | 432 removing_handles_.insert(db_handle); |
88 | 433 removing_ids_.insert(download_id); |
89 int32 id = download_item->GetId(); | 434 } |
90 DownloadPersistentStoreInfo history_info = | 435 |
91 download_item->GetPersistentStoreInfo(); | 436 void DownloadHistory::RemoveDownloadsBatch() { |
92 hs->CreateDownload(id, history_info, &history_consumer_, callback); | 437 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
93 } | 438 HandleSet remove_handles; |
94 | 439 IdSet remove_ids; |
95 void DownloadHistory::UpdateEntry(DownloadItem* download_item) { | 440 removing_handles_.swap(remove_handles); |
96 // Don't store info in the database if the download was initiated while in | 441 removing_ids_.swap(remove_ids); |
97 // incognito mode or if it hasn't been initialized in our database table. | 442 history_->RemoveDownloads(remove_handles); |
98 if (download_item->GetDbHandle() <= DownloadItem::kUninitializedHandle) | 443 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadsRemoved(remove_ids)); |
99 return; | 444 } |
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 = | |
148 visited_before_requests_.find(handle); | |
149 DCHECK(request != visited_before_requests_.end()); | |
150 VisitedBeforeDoneCallback callback = request->second; | |
151 visited_before_requests_.erase(request); | |
152 callback.Run(found_visits && count && | |
153 (first_visit.LocalMidnight() < base::Time::Now().LocalMidnight())); | |
154 } | |
OLD | NEW |