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

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: @r168573 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 history::DownloadRow
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_row.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 history::DownloadRow* info() { return info_.get(); }
90 void set_info(const history::DownloadRow& i) {
91 info_.reset(new history::DownloadRow(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<history::DownloadRow> info_;
103
104 DISALLOW_COPY_AND_ASSIGN(DownloadHistoryData);
105 };
106
107 const char DownloadHistoryData::kKey[] =
108 "DownloadItem DownloadHistoryData";
109
110 history::DownloadRow GetDownloadRow(
111 content::DownloadItem* item) {
112 // TODO(asanka): Persist GetTargetFilePath() as well.
113 DownloadHistoryData* data = DownloadHistoryData::Get(item);
114 return history::DownloadRow(
115 item->GetFullPath(),
116 item->GetURL(),
117 item->GetReferrerUrl(),
118 item->GetStartTime(),
119 item->GetEndTime(),
120 item->GetReceivedBytes(),
121 item->GetTotalBytes(),
122 item->GetState(),
123 ((data != NULL) ? data->db_handle()
124 : history::DownloadDatabase::kUninitializedHandle),
125 item->GetOpened());
126 }
127
128 bool ShouldUpdateHistory(const history::DownloadRow* previous,
129 const history::DownloadRow& current) {
130 // Ignore url, referrer, start_time, db_handle, which don't change.
131 return ((previous == NULL) ||
132 (previous->path != current.path) ||
133 (previous->end_time != current.end_time) ||
134 (previous->received_bytes != current.received_bytes) ||
135 (previous->total_bytes != current.total_bytes) ||
136 (previous->state != current.state) ||
137 (previous->opened != current.opened));
138 }
139
140 typedef std::vector<history::DownloadRow> InfoVector;
141
142 } // anonymous namespace
143
144 DownloadHistory::HistoryAdapter::HistoryAdapter(HistoryService* history)
145 : history_(history) {
146 }
147 DownloadHistory::HistoryAdapter::~HistoryAdapter() {}
148
149 void DownloadHistory::HistoryAdapter::QueryDownloads(
37 const HistoryService::DownloadQueryCallback& callback) { 150 const HistoryService::DownloadQueryCallback& callback) {
38 HistoryService* hs = HistoryServiceFactory::GetForProfile( 151 history_->QueryDownloads(&consumer_, callback);
39 profile_, Profile::EXPLICIT_ACCESS); 152 }
40 if (!hs) 153
41 return; 154 void DownloadHistory::HistoryAdapter::CreateDownload(
42 155 const history::DownloadRow& info,
43 hs->QueryDownloads(&history_consumer_, callback); 156 const HistoryService::DownloadCreateCallback& callback) {
44 157 history_->CreateDownload(info, &consumer_, callback);
45 // This is the initial load, so do a cleanup of corrupt in-progress entries. 158 }
46 hs->CleanUpInProgressEntries(); 159
47 } 160 void DownloadHistory::HistoryAdapter::UpdateDownload(
48 161 const history::DownloadRow& data) {
49 void DownloadHistory::CheckVisitedReferrerBefore( 162 history_->UpdateDownload(data);
50 int32 download_id, 163 }
51 const GURL& referrer_url, 164
52 const VisitedBeforeDoneCallback& callback) { 165 void DownloadHistory::HistoryAdapter::RemoveDownloads(
53 if (referrer_url.is_valid()) { 166 const std::set<int64>& db_handles) {
54 HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( 167 history_->RemoveDownloads(db_handles);
55 profile_, Profile::EXPLICIT_ACCESS); 168 }
56 if (hs) { 169
57 HistoryService::Handle handle = 170
58 hs->GetVisibleVisitCountToHost(referrer_url, &history_consumer_, 171 DownloadHistory::Observer::Observer() {}
59 base::Bind(&DownloadHistory::OnGotVisitCountToHost, 172 DownloadHistory::Observer::~Observer() {}
60 base::Unretained(this))); 173
61 visited_before_requests_[handle] = callback; 174 bool DownloadHistory::IsPersisted(content::DownloadItem* item) {
62 return; 175 DownloadHistoryData* data = DownloadHistoryData::Get(item);
176 return data && data->is_persisted();
177 }
178
179 DownloadHistory::DownloadHistory(
180 content::DownloadManager* manager,
181 scoped_ptr<HistoryAdapter> history)
182 : ALLOW_THIS_IN_INITIALIZER_LIST(notifier_(manager, this)),
183 history_(history.Pass()),
184 loading_db_handle_(history::DownloadDatabase::kUninitializedHandle),
185 history_size_(0),
186 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
187 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
188 content::DownloadManager::DownloadVector items;
189 notifier_.GetManager()->GetAllDownloads(&items);
190 for (content::DownloadManager::DownloadVector::const_iterator
191 it = items.begin(); it != items.end(); ++it) {
192 OnDownloadCreated(notifier_.GetManager(), *it);
193 }
194 history_->QueryDownloads(base::Bind(
195 &DownloadHistory::QueryCallback, weak_ptr_factory_.GetWeakPtr()));
196 }
197
198 DownloadHistory::~DownloadHistory() {
199 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
200 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadHistoryDestroyed());
201 observers_.Clear();
202 }
203
204 void DownloadHistory::AddObserver(DownloadHistory::Observer* observer) {
205 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
206 observers_.AddObserver(observer);
207 }
208
209 void DownloadHistory::RemoveObserver(DownloadHistory::Observer* observer) {
210 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
211 observers_.RemoveObserver(observer);
212 }
213
214 void DownloadHistory::QueryCallback(InfoVector* infos) {
215 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
216 // ManagerGoingDown() may have happened before the history loaded.
217 if (!notifier_.GetManager())
218 return;
219 for (InfoVector::const_iterator it = infos->begin();
220 it != infos->end(); ++it) {
221 // OnDownloadCreated() is called inside DM::CreateDownloadItem(), so set
222 // loading_db_handle_ to match up the created item with its db_handle. All
223 // methods run on the UI thread and CreateDownloadItem() is synchronous.
224 loading_db_handle_ = it->db_handle;
225 content::DownloadItem* download_item =
226 notifier_.GetManager()->CreateDownloadItem(
227 it->path,
228 it->url,
229 it->referrer_url,
230 it->start_time,
231 it->end_time,
232 it->received_bytes,
233 it->total_bytes,
234 it->state,
235 it->opened);
236 DownloadHistoryData* data = DownloadHistoryData::Get(download_item);
237
238 // If this DCHECK fails, then you probably added an Observer that
239 // synchronously creates a DownloadItem in response to
240 // DownloadManager::OnDownloadCreated(), and your observer runs before
241 // DownloadHistory, and DownloadManager creates items synchronously. Just
242 // bounce your DownloadItem creation off the message loop to flush
243 // DownloadHistory::OnDownloadCreated.
244 DCHECK_EQ(it->db_handle, data->db_handle());
245 ++history_size_;
246 }
247 notifier_.GetManager()->CheckForHistoryFilesRemoval();
248 }
249
250 void DownloadHistory::MaybeAddToHistory(content::DownloadItem* item) {
251 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
252
253 int32 download_id = item->GetId();
254 DownloadHistoryData* data = DownloadHistoryData::Get(item);
255 bool removing = (removing_handles_.find(data->db_handle()) !=
256 removing_handles_.end());
257
258 // TODO(benjhayden): Remove IsTemporary().
259 if (download_crx_util::IsExtensionDownload(*item) ||
260 item->IsTemporary() ||
261 data->is_adding() ||
262 data->is_persisted() ||
263 removing)
264 return;
265
266 data->set_is_adding(true);
267 if (data->info() == NULL) {
268 // Keep the info here regardless of whether the item is in progress so that,
269 // when ItemAdded() calls OnDownloadUpdated(), it can decide whether to
270 // Update the db and/or clear the info.
271 data->set_info(GetDownloadRow(item));
272 }
273
274 history_->CreateDownload(*data->info(), base::Bind(
275 &DownloadHistory::ItemAdded, weak_ptr_factory_.GetWeakPtr(),
276 download_id));
277 }
278
279 void DownloadHistory::ItemAdded(int32 download_id, int64 db_handle) {
280 if (removed_while_adding_.find(download_id) !=
281 removed_while_adding_.end()) {
282 removed_while_adding_.erase(download_id);
283 ScheduleRemoveDownload(download_id, db_handle);
284 return;
285 }
286
287 if (!notifier_.GetManager())
288 return;
289
290 content::DownloadItem* item = notifier_.GetManager()->GetDownload(
291 download_id);
292 if (!item) {
293 // This item will have called OnDownloadDestroyed(). If the item should
294 // have been removed from history, then it would have also called
295 // OnDownloadRemoved(), which would have put |download_id| in
296 // removed_while_adding_, handled above.
297 return;
298 }
299
300 DownloadHistoryData* data = DownloadHistoryData::Get(item);
301 data->set_is_adding(false);
302
303 // The sql INSERT statement failed. Avoid an infinite loop: don't
304 // automatically retry. Retry adding the next time the item is updated by
305 // unsetting is_adding.
306 if (db_handle == history::DownloadDatabase::kUninitializedHandle) {
307 DVLOG(20) << __FUNCTION__ << " INSERT failed id=" << download_id;
308 return;
309 }
310
311 data->set_db_handle(db_handle);
312
313 // Send to observers the actual history::DownloadRow that was sent to
314 // the db, plus the db_handle, instead of completely regenerating the
315 // history::DownloadRow, in order to accurately reflect the contents of
316 // the database.
317 data->info()->db_handle = db_handle;
318 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadStored(
319 item, *data->info()));
320
321 UMA_HISTOGRAM_CUSTOM_COUNTS("Download.HistorySize2",
322 history_size_,
323 0/*min*/,
324 (1 << 23)/*max*/,
325 (1 << 7)/*num_buckets*/);
326 ++history_size_;
327
328 // In case the item changed or became temporary while it was being added.
329 // Don't just update all of the item's observers because we're the only
330 // observer that can also see db_handle, which is the only thing that
331 // ItemAdded changed.
332 OnDownloadUpdated(notifier_.GetManager(), item);
333 }
334
335 void DownloadHistory::OnDownloadCreated(
336 content::DownloadManager* manager, content::DownloadItem* item) {
337 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
338
339 // All downloads should pass through OnDownloadCreated exactly once.
340 CHECK(!DownloadHistoryData::Get(item));
341 DownloadHistoryData* data = new DownloadHistoryData(item, loading_db_handle_);
342 loading_db_handle_ = history::DownloadDatabase::kUninitializedHandle;
343 if (item->GetState() == content::DownloadItem::IN_PROGRESS) {
344 data->set_info(GetDownloadRow(item));
345 }
346 MaybeAddToHistory(item);
347 }
348
349 void DownloadHistory::OnDownloadUpdated(
350 content::DownloadManager* manager, content::DownloadItem* item) {
351 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
352
353 DownloadHistoryData* data = DownloadHistoryData::Get(item);
354 if (!data->is_persisted()) {
355 MaybeAddToHistory(item);
356 return;
357 }
358 if (item->IsTemporary()) {
359 OnDownloadRemoved(notifier_.GetManager(), item);
360 return;
361 }
362
363 history::DownloadRow current_info(GetDownloadRow(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 OnDownloadUpdated(manager, item);
382 }
383
384 void DownloadHistory::OnDownloadRemoved(
385 content::DownloadManager* manager, content::DownloadItem* item) {
386 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
387
388 DownloadHistoryData* data = DownloadHistoryData::Get(item);
389 if (!data->is_persisted()) {
390 if (data->is_adding()) {
391 // ScheduleRemoveDownload will be called when history_ calls ItemAdded().
392 removed_while_adding_.insert(item->GetId());
63 } 393 }
64 } 394 return;
65 callback.Run(false); 395 }
66 } 396 ScheduleRemoveDownload(item->GetId(), data->db_handle());
67 397 data->set_db_handle(history::DownloadDatabase::kUninitializedHandle);
68 void DownloadHistory::AddEntry( 398 // ItemAdded increments history_size_ only if the item wasn't
69 DownloadItem* download_item, 399 // removed_while_adding_, so the next line does not belong in
70 const HistoryService::DownloadCreateCallback& callback) { 400 // ScheduleRemoveDownload().
71 DCHECK(download_item); 401 --history_size_;
72 // Do not store the download in the history database for a few special cases: 402 }
73 // - incognito mode (that is the point of this mode) 403
74 // - extensions (users don't think of extension installation as 'downloading') 404 void DownloadHistory::ScheduleRemoveDownload(
75 // - temporary download, like in drag-and-drop 405 int32 download_id, int64 db_handle) {
76 // - history service is not available (e.g. in tests) 406 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
77 // We have to make sure that these handles don't collide with normal db 407 if (db_handle == history::DownloadDatabase::kUninitializedHandle)
78 // handles, so we use a negative value. Eventually, they could overlap, but 408 return;
79 // you'd have to do enough downloading that your ISP would likely stab you in 409
80 // the neck first. YMMV. 410 // For database efficiency, batch removals together if they happen all at
81 HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( 411 // once.
82 profile_, Profile::EXPLICIT_ACCESS); 412 if (removing_handles_.empty()) {
83 if (download_crx_util::IsExtensionDownload(*download_item) || 413 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
84 download_item->IsTemporary() || !hs) { 414 base::Bind(&DownloadHistory::RemoveDownloadsBatch,
85 callback.Run(download_item->GetId(), GetNextFakeDbHandle()); 415 weak_ptr_factory_.GetWeakPtr()));
86 return; 416 }
87 } 417 removing_handles_.insert(db_handle);
88 418 removing_ids_.insert(download_id);
89 int32 id = download_item->GetId(); 419 }
90 DownloadPersistentStoreInfo history_info = 420
91 download_item->GetPersistentStoreInfo(); 421 void DownloadHistory::RemoveDownloadsBatch() {
92 hs->CreateDownload(id, history_info, &history_consumer_, callback); 422 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
93 } 423 HandleSet remove_handles;
94 424 IdSet remove_ids;
95 void DownloadHistory::UpdateEntry(DownloadItem* download_item) { 425 removing_handles_.swap(remove_handles);
96 // Don't store info in the database if the download was initiated while in 426 removing_ids_.swap(remove_ids);
97 // incognito mode or if it hasn't been initialized in our database table. 427 history_->RemoveDownloads(remove_handles);
98 if (download_item->GetDbHandle() <= DownloadItem::kUninitializedHandle) 428 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadsRemoved(remove_ids));
99 return; 429 }
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698