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

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

Issue 10915180: Make DownloadHistory observe manager, items (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: @r165669 Created 8 years, 1 month 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
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 // 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 }
37 const HistoryService::DownloadQueryCallback& callback) { 69
38 HistoryService* hs = HistoryServiceFactory::GetForProfile( 70 virtual ~DownloadHistoryData() {
39 profile_, Profile::EXPLICIT_ACCESS); 71 }
40 if (!hs) 72
41 return; 73 // Whether this item is currently being added to the database.
42 74 bool is_adding() const { return is_adding_; }
43 hs->QueryDownloads(&history_consumer_, callback); 75 void set_is_adding(bool a) { is_adding_ = a; }
44 76
45 // This is the initial load, so do a cleanup of corrupt in-progress entries. 77 // Whether this item is already persisted in the database.
46 hs->CleanUpInProgressEntries(); 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::Observer::Observer() {}
144 DownloadHistory::Observer::~Observer() {}
145
146 DownloadHistory::DownloadHistory(
147 content::DownloadManager* manager,
148 HistoryService* history)
149 : ALLOW_THIS_IN_INITIALIZER_LIST(notifier_(manager, this)),
150 history_(history),
151 loading_db_handle_(history::DownloadDatabase::kUninitializedHandle),
152 history_size_(0),
153 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
154 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
155 content::DownloadManager::DownloadVector items;
156 notifier_.GetManager()->GetAllDownloads(&items);
157 for (content::DownloadManager::DownloadVector::const_iterator
158 it = items.begin(); it != items.end(); ++it) {
159 OnDownloadCreated(notifier_.GetManager(), *it);
160 }
161 history_->QueryDownloads(&history_consumer_, base::Bind(
162 &DownloadHistory::QueryCallback, weak_ptr_factory_.GetWeakPtr()));
163 }
164
165 DownloadHistory::~DownloadHistory() {
166 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
167 }
168
169 void DownloadHistory::AddObserver(DownloadHistory::Observer* observer) {
170 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
171 observers_.AddObserver(observer);
172 }
173
174 void DownloadHistory::RemoveObserver(DownloadHistory::Observer* observer) {
175 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
176 observers_.RemoveObserver(observer);
47 } 177 }
48 178
49 void DownloadHistory::CheckVisitedReferrerBefore( 179 void DownloadHistory::CheckVisitedReferrerBefore(
50 int32 download_id,
51 const GURL& referrer_url, 180 const GURL& referrer_url,
52 const VisitedBeforeDoneCallback& callback) { 181 const VisitedBeforeDoneCallback& callback) {
53 if (referrer_url.is_valid()) { 182 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
54 HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( 183 if (!referrer_url.is_valid()) {
55 profile_, Profile::EXPLICIT_ACCESS); 184 callback.Run(false);
56 if (hs) { 185 return;
57 HistoryService::Handle handle = 186 }
58 hs->GetVisibleVisitCountToHost(referrer_url, &history_consumer_, 187 history_->GetVisibleVisitCountToHost(
59 base::Bind(&DownloadHistory::OnGotVisitCountToHost, 188 referrer_url, &history_consumer_, base::Bind(
60 base::Unretained(this))); 189 &DownloadHistory::OnGotVisitCountToHost,
61 visited_before_requests_[handle] = callback; 190 weak_ptr_factory_.GetWeakPtr(), callback));
62 return; 191 }
63 } 192
64 } 193 void DownloadHistory::OnGotVisitCountToHost(
65 callback.Run(false); 194 const VisitedBeforeDoneCallback& callback,
66 } 195 HistoryService::Handle unused_handle,
67 196 bool found_visits,
68 void DownloadHistory::AddEntry( 197 int count,
69 DownloadItem* download_item, 198 base::Time first_visit) {
70 const HistoryService::DownloadCreateCallback& callback) { 199 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
71 DCHECK(download_item);
72 // Do not store the download in the history database for a few special cases:
73 // - incognito mode (that is the point of this mode)
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 =
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 && 200 callback.Run(found_visits && count &&
153 (first_visit.LocalMidnight() < base::Time::Now().LocalMidnight())); 201 (first_visit.LocalMidnight() < base::Time::Now().LocalMidnight()));
154 } 202 }
203
204 void DownloadHistory::QueryCallback(InfoVector* infos) {
205 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
206 // ManagerGoingDown() may have happened before the history loaded.
207 if (!notifier_.GetManager())
208 return;
209 for (InfoVector::const_iterator it = infos->begin();
210 it != infos->end(); ++it) {
211 // OnDownloadCreated() is called inside DM::CreateDownloadItem(), so set
212 // loading_db_handle_ to match up the created item with its db_handle. All
213 // methods run on the UI thread and CreateDownloadItem() is synchronous.
214 loading_db_handle_ = it->db_handle;
215 content::DownloadItem* download_item =
216 notifier_.GetManager()->CreateDownloadItem(
217 it->path,
218 it->url,
219 it->referrer_url,
220 it->start_time,
221 it->end_time,
222 it->received_bytes,
223 it->total_bytes,
224 it->state,
225 it->opened);
226 DownloadHistoryData* data = DownloadHistoryData::Get(download_item);
227
228 // If this DCHECK fails, then you probably added an Observer that
229 // synchronously creates a DownloadItem in response to
230 // DownloadManager::OnDownloadCreated(), and your observer runs before
231 // DownloadHistory, and DownloadManager creates items synchronously. Just
232 // bounce your DownloadItem creation off the message loop to flush
233 // DownloadHistory::OnDownloadCreated.
234 DCHECK_EQ(it->db_handle, data->db_handle());
235 ++history_size_;
236 }
237 notifier_.GetManager()->CheckForHistoryFilesRemoval();
238 }
239
240 void DownloadHistory::MaybeAddToHistory(content::DownloadItem* item) {
241 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
242
243 int32 download_id = item->GetId();
244 DownloadHistoryData* data = DownloadHistoryData::Get(item);
245 bool removing = (removing_handles_.find(data->db_handle()) !=
246 removing_handles_.end());
247
248 // TODO(benjhayden): Remove IsTemporary().
249 if (download_crx_util::IsExtensionDownload(*item) ||
250 item->IsTemporary() ||
251 data->is_adding() ||
252 data->is_persisted() ||
253 removing)
254 return;
255
256 data->set_is_adding(true);
257 if (data->info() == NULL) {
258 // Keep the info here regardless of whether the item is in progress so that,
259 // when ItemAdded() calls OnDownloadUpdated(), it can decide whether to
260 // Update the db and/or clear the info.
261 data->set_info(GetPersistentStoreInfo(item));
262 }
263
264 history_->CreateDownload(*data->info(), &history_consumer_, base::Bind(
265 &DownloadHistory::ItemAdded, weak_ptr_factory_.GetWeakPtr(),
266 download_id));
267 }
268
269 void DownloadHistory::ItemAdded(int32 download_id, int64 db_handle) {
asanka 2012/11/02 19:56:57 If CreateDownload() failed, then db_handle == 0. D
benjhayden 2012/11/06 20:01:14 Yes and no. I made DownloadDatabase::CreateDownloa
270 if (removed_while_adding_.find(download_id) !=
271 removed_while_adding_.end()) {
272 removed_while_adding_.erase(download_id);
273 ScheduleRemoveDownload(download_id, db_handle);
274 return;
275 }
276
277 if (!notifier_.GetManager())
278 return;
279
280 content::DownloadItem* item = notifier_.GetManager()->GetDownload(
281 download_id);
282 if (!item) {
283 // This item will have called OnDownloadDestroyed(). If the item should
284 // have been removed from history, then it would have also called
285 // OnDownloadRemoved(), which would have put |download_id| in
286 // removed_while_adding_, handled above.
287 return;
288 }
289
290 UMA_HISTOGRAM_CUSTOM_COUNTS("Download.HistorySize2",
291 history_size_,
292 0/*min*/,
293 (1 << 23)/*max*/,
294 (1 << 7)/*num_buckets*/);
295 ++history_size_;
296
297 DownloadHistoryData* data = DownloadHistoryData::Get(item);
298 data->set_is_adding(false);
299 DCHECK_NE(db_handle, history::DownloadDatabase::kUninitializedHandle);
300 data->set_db_handle(db_handle);
301
302 // Send to observers the actual DownloadPersistentStoreInfo that was sent to
303 // the db, plus the db_handle, instead of completely regenerating the
304 // DownloadPersistentStoreInfo, in order to accurately reflect the contents of
305 // the database.
306 data->info()->db_handle = db_handle;
307 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadStored(
308 item, *data->info()));
309
310 // In case the item changed or became temporary while it was being added.
311 // Don't just update all of the item's observers because we're the only
312 // observer that can also see db_handle, which is the only thing that
313 // ItemAdded changed.
314 OnDownloadUpdated(notifier_.GetManager(), item);
315 }
316
317 void DownloadHistory::OnDownloadCreated(
318 content::DownloadManager* manager, content::DownloadItem* item) {
319 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
320
321 // All downloads should pass through OnDownloadCreated exactly once.
322 CHECK(!DownloadHistoryData::Get(item));
323 DownloadHistoryData* data = new DownloadHistoryData(item, loading_db_handle_);
324 loading_db_handle_ = history::DownloadDatabase::kUninitializedHandle;
325 if (item->GetState() == content::DownloadItem::IN_PROGRESS) {
326 data->set_info(GetPersistentStoreInfo(item));
327 }
328 MaybeAddToHistory(item);
329 }
330
331 void DownloadHistory::OnDownloadUpdated(
332 content::DownloadManager* manager, content::DownloadItem* item) {
333 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
334
335 DownloadHistoryData* data = DownloadHistoryData::Get(item);
336 if (!data->is_persisted()) {
337 MaybeAddToHistory(item);
338 return;
339 }
340 if (item->IsTemporary()) {
341 OnDownloadRemoved(notifier_.GetManager(), item);
342 return;
343 }
344
345 // TODO(asanka): Persist GetTargetFilePath() as well.
346 DownloadPersistentStoreInfo current_info(GetPersistentStoreInfo(item));
347 bool should_update = ShouldUpdateHistory(data->info(), current_info);
348 UMA_HISTOGRAM_ENUMERATION("Download.HistoryPropagatedUpdate",
349 should_update, 2);
350 if (should_update) {
351 history_->UpdateDownload(current_info);
352 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadStored(
353 item, current_info));
354 }
355 if (item->GetState() == content::DownloadItem::IN_PROGRESS) {
356 data->set_info(current_info);
357 } else {
358 data->clear_info();
359 }
360 }
361
362 void DownloadHistory::OnDownloadOpened(
363 content::DownloadManager* manager, content::DownloadItem* item) {
364 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
365 DownloadHistoryData* data = DownloadHistoryData::Get(item);
366 if (!data->is_persisted()) {
367 MaybeAddToHistory(item);
368 return;
369 }
370 if (item->IsTemporary()) {
371 OnDownloadRemoved(manager, item);
372 return;
373 }
374 // Downloads may be opened after they are completed, so don't rely on
375 // DownloadHistoryData::info().
376
377 DownloadPersistentStoreInfo current_info(GetPersistentStoreInfo(item));
378 history_->UpdateDownload(current_info);
379 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadStored(item, current_info));
380 }
381
382 void DownloadHistory::OnDownloadRemoved(
383 content::DownloadManager* manager, content::DownloadItem* item) {
384 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
385
386 DownloadHistoryData* data = DownloadHistoryData::Get(item);
387 if (!data->is_persisted()) {
388 if (data->is_adding()) {
389 // ScheduleRemoveDownload will be called when history_ calls ItemAdded().
390 removed_while_adding_.insert(item->GetId());
391 }
392 return;
393 }
394 ScheduleRemoveDownload(item->GetId(), data->db_handle());
395 data->set_db_handle(history::DownloadDatabase::kUninitializedHandle);
396 // ItemAdded increments history_size_ only if the item wasn't
397 // removed_while_adding_, so the next line does not belong in
398 // ScheduleRemoveDownload().
399 --history_size_;
400 }
401
402 void DownloadHistory::ScheduleRemoveDownload(
403 int32 download_id, int64 db_handle) {
404 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
405
406 // For database efficiency, batch removals together if they happen all at
407 // once.
408 if (removing_handles_.empty()) {
409 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
410 base::Bind(&DownloadHistory::RemoveDownloadsBatch,
411 weak_ptr_factory_.GetWeakPtr()));
412 }
413 removing_handles_.insert(db_handle);
414 removing_ids_.insert(download_id);
415 }
416
417 void DownloadHistory::RemoveDownloadsBatch() {
418 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
419 HandleSet remove_handles;
420 IdSet remove_ids;
421 removing_handles_.swap(remove_handles);
422 removing_ids_.swap(remove_ids);
423 history_->RemoveDownloads(remove_handles);
424 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadsRemoved(remove_ids));
425 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698