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

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

Issue 10665049: Make DownloadHistory observe manager, items (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 8 years, 4 months 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
Randy Smith (Not in Mondays) 2012/08/15 20:47:44 Explanatory comment at top of file about basic arc
benjhayden 2012/08/17 18:20:45 I had hoped that the code could speak for itself,
Randy Smith (Not in Mondays) 2012/08/20 18:57:04 L G T M; thanks.
5 #include "chrome/browser/download/download_history.h" 5 #include "chrome/browser/download/download_history.h"
6 6
7 #include "base/logging.h" 7 #include "base/metrics/histogram.h"
8 #include "chrome/browser/cancelable_request.h"
8 #include "chrome/browser/download/download_crx_util.h" 9 #include "chrome/browser/download/download_crx_util.h"
9 #include "chrome/browser/history/history_marshaling.h" 10 #include "chrome/browser/download/download_persistent_store_info.h"
10 #include "chrome/browser/history/history_service_factory.h" 11 #include "content/public/browser/browser_thread.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "content/public/browser/download_item.h" 12 #include "content/public/browser/download_item.h"
13 #include "content/public/browser/download_persistent_store_info.h" 13 #include "content/public/browser/download_manager.h"
14 14
15 using content::BrowserThread;
15 using content::DownloadItem; 16 using content::DownloadItem;
16 using content::DownloadPersistentStoreInfo; 17 using content::DownloadManager;
17 18
18 DownloadHistory::DownloadHistory(Profile* profile) 19 namespace {
19 : profile_(profile), 20
20 next_fake_db_handle_(DownloadItem::kUninitializedHandle - 1) { 21 class DownloadHistoryData : public base::SupportsUserData::Data {
Randy Smith (Not in Mondays) 2012/08/15 20:47:44 Comment documenting purpose and use?
benjhayden 2012/08/17 18:20:45 Done.
21 DCHECK(profile); 22 public:
22 } 23 static const int64 kUninitializedHandle = -1;
Randy Smith (Not in Mondays) 2012/08/15 20:47:44 Why here? I would think this would be a DownloadH
benjhayden 2012/08/17 18:20:45 Moved it out into the anonymous namespace.
Randy Smith (Not in Mondays) 2012/08/20 18:57:04 I think I was just confused in my earlier comment.
23 24
24 DownloadHistory::~DownloadHistory() {} 25 static DownloadHistoryData* Get(DownloadItem* item) {
25 26 base::SupportsUserData::Data* data = item->GetUserData(kKey);
26 void DownloadHistory::GetNextId( 27 return (data == NULL) ? NULL :
27 const HistoryService::DownloadNextIdCallback& callback) { 28 static_cast<DownloadHistoryData*>(data);
28 HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( 29 }
29 profile_, Profile::EXPLICIT_ACCESS); 30
30 if (!hs) 31 explicit DownloadHistoryData(DownloadItem* item)
31 return; 32 : is_adding_(false),
32 33 is_persisted_(false),
33 hs->GetNextDownloadId(&history_consumer_, callback); 34 db_handle_(kUninitializedHandle),
34 } 35 info_(NULL) {
35 36 item->SetUserData(kKey, this);
36 void DownloadHistory::Load( 37 }
38
39 virtual ~DownloadHistoryData() {
40 }
41
42 // Whether this item is currently being added to the database.
43 bool is_adding() const { return is_adding_; }
44 void set_is_adding(bool a) { is_adding_ = a; }
45
46 // Whether this item is already persisted in the database.
47 bool is_persisted() const { return is_persisted_; }
48 void set_is_persisted(bool p) { is_persisted_ = p; }
49
50 int64 db_handle() const { return db_handle_; }
51 void set_db_handle(int64 h) { db_handle_ = h; }
52
53 // This allows OnDownloadUpdated() to see what changed in a DownloadItem if
54 // anything, in order to prevent writing to the database unnecessarily. It is
55 // nullified when the item is no longer in progress in order to save memory.
56 DownloadPersistentStoreInfo* info() { return info_.get(); }
57 void set_info(const DownloadPersistentStoreInfo& i) {
58 info_.reset(new DownloadPersistentStoreInfo(i));
59 }
60 void clear_info() {
61 info_.reset();
62 }
63
64 private:
65 static const char kKey[];
66
67 bool is_adding_;
68 bool is_persisted_;
69 int64 db_handle_;
70 scoped_ptr<DownloadPersistentStoreInfo> info_;
71
72 DISALLOW_COPY_AND_ASSIGN(DownloadHistoryData);
73 };
74
75 const char DownloadHistoryData::kKey[] =
76 "DownloadItem DownloadHistoryData";
77
78 DownloadPersistentStoreInfo GetPersistentStoreInfo(DownloadItem* item) {
79 DownloadHistoryData* data = DownloadHistoryData::Get(item);
80 int64 db_handle = ((data != NULL) ?
81 data->db_handle() :
82 DownloadHistoryData::kUninitializedHandle);
83 return DownloadPersistentStoreInfo(
84 item->GetFullPath(),
85 item->GetURL(),
86 item->GetReferrerUrl(),
87 item->GetStartTime(),
88 item->GetEndTime(),
89 item->GetReceivedBytes(),
90 item->GetTotalBytes(),
91 item->GetState(),
92 db_handle,
93 item->GetOpened());
94 }
95
96 } // anonymous namespace
97
98 HistoryServiceDownloadAdapter::HistoryServiceDownloadAdapter(
99 HistoryService* history_service)
100 : history_service_(history_service) {
101 }
102
103 HistoryServiceDownloadAdapter::~HistoryServiceDownloadAdapter() {}
104
105 void HistoryServiceDownloadAdapter::QueryDownloads(
37 const HistoryService::DownloadQueryCallback& callback) { 106 const HistoryService::DownloadQueryCallback& callback) {
38 HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( 107 history_service_->QueryDownloads(&history_consumer_, callback);
39 profile_, Profile::EXPLICIT_ACCESS); 108 history_service_->CleanUpInProgressEntries();
40 if (!hs) 109 }
41 return; 110
42 111 HistoryService::Handle
43 hs->QueryDownloads(&history_consumer_, callback); 112 HistoryServiceDownloadAdapter::GetVisibleVisitCountToHost(
44 113 const GURL& referrer_url,
45 // This is the initial load, so do a cleanup of corrupt in-progress entries. 114 const HistoryService::GetVisibleVisitCountToHostCallback&
46 hs->CleanUpInProgressEntries(); 115 callback) {
116 return history_service_->GetVisibleVisitCountToHost(
117 referrer_url, &history_consumer_, callback);
118 }
119
120 void HistoryServiceDownloadAdapter::CreateDownload(
121 int32 id,
122 const DownloadPersistentStoreInfo& info,
123 const HistoryService::DownloadCreateCallback& callback) {
124 history_service_->CreateDownload(id, info, &history_consumer_, callback);
125 }
126
127 void HistoryServiceDownloadAdapter::UpdateDownload(
128 const DownloadPersistentStoreInfo& info) {
129 history_service_->UpdateDownload(info);
130 }
131
132 void HistoryServiceDownloadAdapter::RemoveDownloads(
133 const std::set<int64>& handles) {
134 history_service_->RemoveDownloads(handles);
135 }
136
137 void HistoryServiceDownloadAdapter::OnDownloadHistoryDestroyed() {
138 delete this;
139 }
140
141 DownloadHistory::DownloadHistory(
142 DownloadManager* manager,
143 HistoryServiceDownloadAdapter* history)
144 : manager_(manager),
145 history_(history),
146 loading_(false),
147 history_size_(0) {
148 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
149 DCHECK(manager_);
150 manager_->AddObserver(this);
151 history_->QueryDownloads(base::Bind(
152 &DownloadHistory::QueryCallback, AsWeakPtr()));
153 }
154
155 DownloadHistory::~DownloadHistory() {
156 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
157 history_->OnDownloadHistoryDestroyed();
158 if (manager_)
159 manager_->RemoveObserver(this);
Randy Smith (Not in Mondays) 2012/08/15 20:47:44 DCHECK that the manager has no items on it?
benjhayden 2012/08/17 18:20:45 Did the observing_items_ set trick instead. I'll s
160 }
161
162 void DownloadHistory::QueryCallback(
163 DownloadHistory::InfoVector* infos) {
Randy Smith (Not in Mondays) 2012/08/15 20:47:44 Who owns the storage in the vector pointed to by I
benjhayden 2012/08/17 18:20:45 HistoryService::QueryDownloads() creates a history
164 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
165 // We may have caught ManagerGoingDown() before the history loaded.
166 if (manager_ == NULL)
167 return;
168 // OnDownloadCreated() is called inside DownloadManager::CreateDownloadItem(),
169 // before we have a chance to attach a DownloadHistoryData, so temporarily
170 // disable adding new unpersisted items to the history. All methods run on
171 // the UI thread and CreateDownloadItem() is synchronous, so it is impossible
172 // for an OnDownloadCreated() to come in for another DownloadItem while we are
173 // processing the database.
174 loading_ = true;
175 for (InfoVector::const_iterator it = infos->begin();
176 it != infos->end(); ++it) {
177 DownloadItem* download_item = manager_->CreateDownloadItem(
178 it->path,
179 it->url,
180 it->referrer_url,
181 it->start_time,
182 it->end_time,
183 it->received_bytes,
184 it->total_bytes,
185 it->state,
186 it->opened);
187 DownloadHistoryData* data = DownloadHistoryData::Get(download_item);
188 data->set_is_persisted(true);
189 data->set_db_handle(it->db_handle);
190 ++history_size_;
191 }
192 loading_ = false;
193 }
194
195 void DownloadHistory::OnDownloadCreated(
196 DownloadManager* manager, DownloadItem* item) {
197 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
198
199 // Observe even temporary downloads in case they are marked not temporary.
200 item->AddObserver(this);
201 // All downloads should pass through OnDownloadCreated exactly once.
202 CHECK(!DownloadHistoryData::Get(item));
203 DownloadHistoryData* data = new DownloadHistoryData(item);
204 if (item->GetState() == DownloadItem::IN_PROGRESS) {
205 data->set_info(GetPersistentStoreInfo(item));
206 }
207 MaybeAddToHistory(item);
208 }
209
210 void DownloadHistory::MaybeAddToHistory(DownloadItem* item) {
211 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
212 int32 download_id = item->GetId();
213 DownloadHistoryData* data = DownloadHistoryData::Get(item);
214 bool removing = (removing_.find(data->db_handle()) != removing_.end());
Randy Smith (Not in Mondays) 2012/08/15 20:47:44 Should this be a DCHECK?
benjhayden 2012/08/17 18:20:45 I could imagine DownloadItem sending one or two fi
215 if (!loading_ &&
216 !download_crx_util::IsExtensionDownload(*item) &&
217 !data->is_persisted() &&
218 !item->IsTemporary() &&
219 !removing &&
220 !data->is_adding()) {
221 data->set_is_adding(true);
222 if (data->info() == NULL) {
223 // Keep the info here regardless of whether the item is in progress so
224 // that, when ItemAdded() calls OnDownloadUpdated(), it can choose more
225 // intelligently whether to Update the db and/or discard the info.
226 data->set_info(GetPersistentStoreInfo(item));
227 }
228 history_->CreateDownload(
229 download_id,
230 *data->info(),
231 base::Bind(&DownloadHistory::ItemAdded, AsWeakPtr()));
232 }
233 }
234
235 void DownloadHistory::ItemAdded(int32 download_id, int64 db_handle) {
236 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
237
238 if (!manager_)
239 return;
240
241 DownloadItem* item = manager_->GetDownload(download_id);
242 if (!item) {
243 // We should have caught OnDownloadDestroyed(). If the item should have
Randy Smith (Not in Mondays) 2012/08/15 20:47:44 Does this mean we could have a DCHECK() here?
benjhayden 2012/08/17 18:20:45 Nope. There's no mechanism that cancels this parti
Randy Smith (Not in Mondays) 2012/08/20 18:57:04 Hmmm. Then I think the comment's a little confusi
244 // been removed from history, then OnDownloadRemoved() put |download_id| in
245 // removed_while_adding_.
246 if (removed_while_adding_.find(download_id) !=
247 removed_while_adding_.end()) {
248 removed_while_adding_.erase(download_id);
249 if (removing_.empty()) {
250 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
251 base::Bind(&DownloadHistory::RemoveDownloadsBatch, AsWeakPtr()));
252 }
253 removing_.insert(db_handle);
254 }
255 return;
256 }
257
258 UMA_HISTOGRAM_CUSTOM_COUNTS("Download.HistorySize2",
259 history_size_,
260 0/*min*/,
261 (1 << 23)/*max*/,
262 (1 << 7)/*num_buckets*/);
263 ++history_size_;
264
265 DownloadHistoryData* data = DownloadHistoryData::Get(item);
266 data->set_is_adding(false);
267 data->set_is_persisted(true);
268 data->set_db_handle(db_handle);
269
270 // In case the item changed or became temporary while it was being added.
271 // Don't just UpdateObservers() because we're the only observer that can also
272 // see is_persisted/db_handle, which is the only thing that we changed.
273 OnDownloadUpdated(item);
274 }
275
276 void DownloadHistory::OnDownloadUpdated(DownloadItem* item) {
277 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
278
279 DownloadHistoryData* data = DownloadHistoryData::Get(item);
280 if (!data->is_persisted()) {
281 MaybeAddToHistory(item);
282 return;
283 }
284 if (item->IsTemporary()) {
285 OnDownloadRemoved(item);
286 return;
287 }
288
289 // TODO(asanka): Persist GetTargetFilePath() as well.
290 DownloadPersistentStoreInfo current_info(GetPersistentStoreInfo(item));
291 DownloadPersistentStoreInfo* previous_info = data->info();
292 bool do_update = (
293 (previous_info == NULL) ||
294 (previous_info->path != current_info.path) ||
295 (previous_info->end_time != current_info.end_time) ||
296 (previous_info->received_bytes != current_info.received_bytes) ||
297 (previous_info->total_bytes != current_info.total_bytes) ||
298 (previous_info->state != current_info.state) ||
299 (previous_info->opened != current_info.opened));
300 UMA_HISTOGRAM_ENUMERATION("Download.HistoryPropagatedUpdate", do_update, 2);
301 if (do_update) {
302 history_->UpdateDownload(current_info);
303 }
304 if (item->GetState() == DownloadItem::IN_PROGRESS) {
305 data->set_info(current_info);
306 } else {
307 data->clear_info();
308 }
309 }
310
311 // Downloads may be opened after they are completed.
312 void DownloadHistory::OnDownloadOpened(DownloadItem* item) {
313 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
314 DownloadHistoryData* data = DownloadHistoryData::Get(item);
315 if (!data->is_persisted()) {
316 MaybeAddToHistory(item);
317 return;
318 }
319 if (item->IsTemporary()) {
320 OnDownloadRemoved(item);
321 return;
322 }
323
324 DownloadPersistentStoreInfo current_info(GetPersistentStoreInfo(item));
325 history_->UpdateDownload(current_info);
326 if (item->GetState() == DownloadItem::IN_PROGRESS) {
Randy Smith (Not in Mondays) 2012/08/15 20:47:44 Can they be opened before they're completed? I.e.
benjhayden 2012/08/17 18:20:45 Wasn't there some chatter about streaming download
Randy Smith (Not in Mondays) 2012/08/20 18:57:04 Fair enough. We're well short of streaming data t
327 data->set_info(current_info);
328 }
329 }
330
331 void DownloadHistory::OnDownloadRemoved(DownloadItem* item) {
332 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
333
334 DownloadHistoryData* data = DownloadHistoryData::Get(item);
335 if ((data == NULL) ||
336 !data->is_persisted() ||
337 (data->db_handle() <= DownloadHistoryData::kUninitializedHandle)) {
338 if (data->is_adding()) {
339 removed_while_adding_.insert(item->GetId());
340 }
341 return;
342 }
343
344 // For database efficiency, batch removals together if they happen all at
345 // once.
346 if (removing_.empty()) {
347 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
348 base::Bind(&DownloadHistory::RemoveDownloadsBatch, AsWeakPtr()));
349 }
350 removing_.insert(data->db_handle());
351 data->set_db_handle(DownloadHistoryData::kUninitializedHandle);
352 data->set_is_persisted(false);
353 --history_size_;
354 }
355
356 void DownloadHistory::RemoveDownloadsBatch() {
357 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
358 HandleSet remove_handles;
359 removing_.swap(remove_handles);
360 history_->RemoveDownloads(remove_handles);
361 }
362
363 void DownloadHistory::ManagerGoingDown(DownloadManager* manager) {
364 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
365 DCHECK_EQ(manager_, manager);
366 manager_->RemoveObserver(this);
367 manager_ = NULL;
368 }
369
370
371 void DownloadHistory::OnDownloadDestroyed(DownloadItem* item) {
372 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
373 item->RemoveObserver(this);
47 } 374 }
48 375
49 void DownloadHistory::CheckVisitedReferrerBefore( 376 void DownloadHistory::CheckVisitedReferrerBefore(
50 int32 download_id, 377 int32 download_id,
51 const GURL& referrer_url, 378 const GURL& referrer_url,
52 const VisitedBeforeDoneCallback& callback) { 379 const VisitedBeforeDoneCallback& callback) {
380 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
53 if (referrer_url.is_valid()) { 381 if (referrer_url.is_valid()) {
54 HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( 382 HistoryService::Handle handle = history_->GetVisibleVisitCountToHost(
55 profile_, Profile::EXPLICIT_ACCESS); 383 referrer_url,
56 if (hs) { 384 base::Bind(&DownloadHistory::OnGotVisitCountToHost,
57 HistoryService::Handle handle = 385 AsWeakPtr()));
58 hs->GetVisibleVisitCountToHost(referrer_url, &history_consumer_, 386 visited_before_requests_[handle] = callback;
59 base::Bind(&DownloadHistory::OnGotVisitCountToHost, 387 return;
60 base::Unretained(this)));
61 visited_before_requests_[handle] = callback;
62 return;
63 }
64 } 388 }
65 callback.Run(false); 389 callback.Run(false);
66 } 390 }
67 391
68 void DownloadHistory::AddEntry( 392 void DownloadHistory::OnGotVisitCountToHost(
69 DownloadItem* download_item, 393 HistoryService::Handle handle,
70 const HistoryService::DownloadCreateCallback& callback) { 394 bool found_visits,
71 DCHECK(download_item); 395 int count,
72 // Do not store the download in the history database for a few special cases: 396 base::Time first_visit) {
73 // - incognito mode (that is the point of this mode) 397 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 = 398 VisitedBeforeRequestsMap::iterator request =
148 visited_before_requests_.find(handle); 399 visited_before_requests_.find(handle);
149 DCHECK(request != visited_before_requests_.end()); 400 DCHECK(request != visited_before_requests_.end());
150 VisitedBeforeDoneCallback callback = request->second; 401 VisitedBeforeDoneCallback callback = request->second;
151 visited_before_requests_.erase(request); 402 visited_before_requests_.erase(request);
152 callback.Run(found_visits && count && 403 callback.Run(found_visits && count &&
153 (first_visit.LocalMidnight() < base::Time::Now().LocalMidnight())); 404 (first_visit.LocalMidnight() < base::Time::Now().LocalMidnight()));
154 } 405 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698