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

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
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/download/download_crx_util.h" 8 #include "chrome/browser/cancelable_request.h"
9 #include "chrome/browser/history/history_marshaling.h" 9 #include "content/public/browser/browser_thread.h"
10 #include "chrome/browser/history/history_service_factory.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "content/public/browser/download_item.h" 10 #include "content/public/browser/download_item.h"
11 #include "content/public/browser/download_manager.h"
13 #include "content/public/browser/download_persistent_store_info.h" 12 #include "content/public/browser/download_persistent_store_info.h"
14 13
14 using content::BrowserThread;
15 using content::DownloadItem; 15 using content::DownloadItem;
16 using content::DownloadManager;
16 using content::DownloadPersistentStoreInfo; 17 using content::DownloadPersistentStoreInfo;
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 {
21 DCHECK(profile); 22 public:
22 } 23 static const int64 kUninitializedHandle = -1;
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_persisted_(false),
32 33 db_handle_(kUninitializedHandle) {
33 hs->GetNextDownloadId(&history_consumer_, callback); 34 item->SetUserData(kKey, this);
35 }
36
37 virtual ~DownloadHistoryData() {
38 }
39
40 bool is_persisted() const { return is_persisted_; }
41 void set_is_persisted(bool p) { is_persisted_ = p; }
42
43 // TODO(benjhayden) Merge db_handle with DownloadItem::GetId(), then this
44 // class can turn into a simple bool.
45 int64 db_handle() const { return db_handle_; }
46 void set_db_handle(int64 h) { db_handle_ = h; }
47
48 private:
49 static const char kKey[];
50
51 bool is_persisted_;
52 int64 db_handle_;
53
54 DISALLOW_COPY_AND_ASSIGN(DownloadHistoryData);
55 };
56
57 const char DownloadHistoryData::kKey[] =
58 "DownloadItem DownloadHistoryData";
59
60 DownloadPersistentStoreInfo GetPersistentStoreInfo(DownloadItem* item) {
61 DownloadHistoryData* data = DownloadHistoryData::Get(item);
62 int64 db_handle = ((data != NULL) ?
63 data->db_handle() :
64 DownloadHistoryData::kUninitializedHandle);
65 return DownloadPersistentStoreInfo(
66 item->GetFullPath(),
67 item->GetURL(),
68 item->GetReferrerUrl(),
69 item->GetStartTime(),
70 item->GetEndTime(),
71 item->GetReceivedBytes(),
72 item->GetTotalBytes(),
73 item->GetState(),
74 db_handle,
75 item->GetOpened());
76 }
77
78 } // anonymous namespace
79
80 DownloadHistory::DownloadHistory(
81 DownloadManager* manager,
82 HistoryServiceDownloadInterface* history)
83 : manager_(manager),
84 history_(history),
85 history_size_(0) {
86 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
87 DCHECK(manager_);
88 DCHECK(history_);
89 manager_->AddObserver(this);
90 }
91
92 DownloadHistory::~DownloadHistory() {
93 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
94 history_->OnDownloadHistoryDestroyed();
95 if (manager_)
96 manager_->RemoveObserver(this);
34 } 97 }
35 98
36 void DownloadHistory::Load( 99 void DownloadHistory::Load(
37 const HistoryService::DownloadQueryCallback& callback) { 100 const HistoryService::DownloadQueryCallback& callback) {
38 HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( 101 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
39 profile_, Profile::EXPLICIT_ACCESS); 102 history_->QueryDownloads(base::Bind(
40 if (!hs) 103 &DownloadHistory::LoadCallback, AsWeakPtr(), callback));
41 return; 104 }
42 105
43 hs->QueryDownloads(&history_consumer_, callback); 106 void DownloadHistory::LoadCallback(
44 107 const HistoryService::DownloadQueryCallback& callback,
45 // This is the initial load, so do a cleanup of corrupt in-progress entries. 108 DownloadHistory::InfoVector* infos) {
46 hs->CleanUpInProgressEntries(); 109 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
110 loaded_infos_.clear();
111 loaded_infos_.resize(infos->size());
112 std::copy(infos->begin(), infos->end(), loaded_infos_.begin());
113 callback.Run(infos);
47 } 114 }
48 115
49 void DownloadHistory::CheckVisitedReferrerBefore( 116 void DownloadHistory::CheckVisitedReferrerBefore(
50 int32 download_id, 117 int32 download_id,
51 const GURL& referrer_url, 118 const GURL& referrer_url,
52 const VisitedBeforeDoneCallback& callback) { 119 const VisitedBeforeDoneCallback& callback) {
120 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
53 if (referrer_url.is_valid()) { 121 if (referrer_url.is_valid()) {
54 HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists( 122 HistoryService::Handle handle = history_->GetVisibleVisitCountToHost(
55 profile_, Profile::EXPLICIT_ACCESS); 123 referrer_url,
56 if (hs) { 124 base::Bind(&DownloadHistory::OnGotVisitCountToHost,
57 HistoryService::Handle handle = 125 AsWeakPtr()));
58 hs->GetVisibleVisitCountToHost(referrer_url, &history_consumer_, 126 visited_before_requests_[handle] = callback;
59 base::Bind(&DownloadHistory::OnGotVisitCountToHost, 127 return;
60 base::Unretained(this)));
61 visited_before_requests_[handle] = callback;
62 return;
63 }
64 } 128 }
65 callback.Run(false); 129 callback.Run(false);
66 } 130 }
67 131
68 void DownloadHistory::AddEntry( 132 void DownloadHistory::OnGotVisitCountToHost(
69 DownloadItem* download_item, 133 HistoryService::Handle handle,
70 const HistoryService::DownloadCreateCallback& callback) { 134 bool found_visits,
71 DCHECK(download_item); 135 int count,
72 // Do not store the download in the history database for a few special cases: 136 base::Time first_visit) {
73 // - incognito mode (that is the point of this mode) 137 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_item->IsOtr() ||
84 download_crx_util::IsExtensionDownload(*download_item) ||
85 download_item->IsTemporary() || !hs) {
86 callback.Run(download_item->GetId(), GetNextFakeDbHandle());
87 return;
88 }
89
90 int32 id = download_item->GetId();
91 DownloadPersistentStoreInfo history_info =
92 download_item->GetPersistentStoreInfo();
93 hs->CreateDownload(id, history_info, &history_consumer_, callback);
94 }
95
96 void DownloadHistory::UpdateEntry(DownloadItem* download_item) {
97 // Don't store info in the database if the download was initiated while in
98 // incognito mode or if it hasn't been initialized in our database table.
99 if (download_item->GetDbHandle() <= DownloadItem::kUninitializedHandle)
100 return;
101
102 HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists(
103 profile_, Profile::EXPLICIT_ACCESS);
104 if (!hs)
105 return;
106 hs->UpdateDownload(download_item->GetPersistentStoreInfo());
107 }
108
109 void DownloadHistory::UpdateDownloadPath(DownloadItem* download_item,
110 const FilePath& new_path) {
111 // No update necessary if the download was initiated while in incognito mode.
112 if (download_item->GetDbHandle() <= DownloadItem::kUninitializedHandle)
113 return;
114
115 HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists(
116 profile_, Profile::EXPLICIT_ACCESS);
117 if (hs)
118 hs->UpdateDownloadPath(new_path, download_item->GetDbHandle());
119 }
120
121 void DownloadHistory::RemoveEntry(DownloadItem* download_item) {
122 // No update necessary if the download was initiated while in incognito mode.
123 if (download_item->GetDbHandle() <= DownloadItem::kUninitializedHandle)
124 return;
125
126 HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists(
127 profile_, Profile::EXPLICIT_ACCESS);
128 if (hs)
129 hs->RemoveDownload(download_item->GetDbHandle());
130 }
131
132 void DownloadHistory::RemoveEntriesBetween(const base::Time remove_begin,
133 const base::Time remove_end) {
134 HistoryService* hs = HistoryServiceFactory::GetForProfileIfExists(
135 profile_, Profile::EXPLICIT_ACCESS);
136 if (hs)
137 hs->RemoveDownloadsBetween(remove_begin, remove_end);
138 }
139
140 int64 DownloadHistory::GetNextFakeDbHandle() {
141 return next_fake_db_handle_--;
142 }
143
144 void DownloadHistory::OnGotVisitCountToHost(HistoryService::Handle handle,
145 bool found_visits,
146 int count,
147 base::Time first_visit) {
148 VisitedBeforeRequestsMap::iterator request = 138 VisitedBeforeRequestsMap::iterator request =
149 visited_before_requests_.find(handle); 139 visited_before_requests_.find(handle);
150 DCHECK(request != visited_before_requests_.end()); 140 DCHECK(request != visited_before_requests_.end());
151 VisitedBeforeDoneCallback callback = request->second; 141 VisitedBeforeDoneCallback callback = request->second;
152 visited_before_requests_.erase(request); 142 visited_before_requests_.erase(request);
153 callback.Run(found_visits && count && 143 callback.Run(found_visits && count &&
154 (first_visit.LocalMidnight() < base::Time::Now().LocalMidnight())); 144 (first_visit.LocalMidnight() < base::Time::Now().LocalMidnight()));
155 } 145 }
146
147 void DownloadHistory::OnDownloadCreated(
148 DownloadManager* manager, DownloadItem* item) {
149 DCHECK(manager);
150 DCHECK(manager_);
151 DCHECK_EQ(manager_, manager);
152 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
153
154 // Observe even temporary downloads in case they are marked not temporary.
155 item->AddObserver(this);
156 DownloadPersistentStoreInfo info = GetPersistentStoreInfo(item);
157 infos_[item->GetId()] = info;
158 // All downloads should pass through OnDownloadCreated exactly once.
159 DCHECK(!DownloadHistoryData::Get(item));
160 DownloadHistoryData* data = new DownloadHistoryData(item);
161
162 // Try to match up |item| with |loaded_infos_| by all fields except
163 // |db_handle|, because DownloadItem doesn't know its |db_handle|.
Randy Smith (Not in Mondays) 2012/08/02 22:47:56 Refactor load path into DM so that the DH creates
benjhayden 2012/08/14 21:31:47 Done.
164 // Theoretically, DownloadManager will run through infos in order, so this
165 // loop should either match the first info or else loaded_infos_ should be
166 // empty, but we shouldn't rely on that. This matching mechanism relies on
167 // DownloadItem reflecting its info accurately, without "fixing" any fields
168 // except |state|, which is changed from IN_PROGRESS to CANCELLED.
169 for (InfoVector::iterator it = loaded_infos_.begin();
170 it != loaded_infos_.end(); ++it) {
171 LOG(INFO) << "occam " << __FUNCTION__
172 << " " << data->is_persisted() << " " << data->db_handle()
173 << " " << info.db_handle << " " << it->db_handle
174 << " " << info.path.value() << " " << it->path.value()
175 << " " << info.url.spec() << " " << it->url.spec()
176 << " " << info.referrer_url.spec() << " " << it->referrer_url.spec ()
177 << " " << info.start_time.ToTimeT() << " " << it->start_time.ToTim eT()
178 << " " << info.end_time.ToTimeT() << " " << it->end_time.ToTimeT()
179 << " " << info.received_bytes << " " << it->received_bytes
180 << " " << info.total_bytes << " " << it->total_bytes
181 << " " << info.state << " " << it->state
182 << " " << info.opened << " " << it->opened;
183 if ((info.path == it->path) &&
184 (info.url == it->url) &&
185 (info.referrer_url == it->referrer_url) &&
186 (info.start_time == it->start_time) &&
187 (info.end_time == it->end_time) &&
188 (info.received_bytes == it->received_bytes) &&
189 (info.total_bytes == it->total_bytes) &&
190 ((it->state == DownloadItem::IN_PROGRESS) ||
191 (info.state == it->state)) &&
192 (info.opened == it->opened)) {
193 loaded_infos_.erase(it);
194 data->set_is_persisted(true);
195 data->set_db_handle(it->db_handle);
196 break;
197 }
198 }
199
200 MaybeAddToHistory(item);
201 }
202
203 void DownloadHistory::ManagerGoingDown(DownloadManager* manager) {
204 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
205 DCHECK_EQ(manager_, manager);
206 manager_->RemoveObserver(this);
207 manager_ = NULL;
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());
215 bool already_adding = (adding_.find(download_id) != adding_.end());
216 LOG(INFO) << "occam " << __FUNCTION__ << " " << data->is_persisted() << " " << item->IsTemporary() << " " << removing << " " << already_adding;
217 if (!data->is_persisted() &&
218 !item->IsTemporary() &&
219 !removing &&
220 !already_adding) {
221 adding_.insert(download_id);
222 history_->CreateDownload(
223 download_id,
224 infos_[download_id],
225 base::Bind(&DownloadHistory::ItemAdded, AsWeakPtr()));
226 }
227 }
228
229 void DownloadHistory::ItemAdded(int32 download_id, int64 db_handle) {
230 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
231
232 adding_.erase(download_id);
233
234 if (!manager_)
235 return;
236
237 DownloadItem* item = manager_->GetDownload(download_id);
238 LOG(INFO) << "occam " << __FUNCTION__ << " " << item << " " << db_handle;
239 if (!item) {
240 // We should have caught OnDownloadDestroyed(). If the item should have
241 // been removed from history, then OnDownloadRemoved() would have been
242 // called, so don't manually call it here.
243 DCHECK(infos_.find(download_id) == infos_.end());
244 return;
245 }
246
247 UMA_HISTOGRAM_CUSTOM_COUNTS("Download.HistorySize2",
248 history_size_,
249 0/*min*/,
250 (1 << 23)/*max*/,
251 (1 << 7)/*num_buckets*/);
252 ++history_size_;
253
254 DownloadHistoryData* data = DownloadHistoryData::Get(item);
255 data->set_is_persisted(true);
256 data->set_db_handle(db_handle);
Randy Smith (Not in Mondays) 2012/08/02 22:47:56 Maybe also store the last DownloadPersistentStateI
benjhayden 2012/08/14 21:31:47 Done.
257
258 // In case the item changed or became temporary while it was being added.
259 // Don't just UpdateObservers() because we're the only observer that can also
260 // see is_persisted/db_handle, which is the only thing that we changed.
261 OnDownloadUpdated(item);
262 }
263
264 void DownloadHistory::OnDownloadUpdated(DownloadItem* item) {
265 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
266 if (!manager_)
267 return;
268
269 DownloadHistoryData* data = DownloadHistoryData::Get(item);
270 if ((data == NULL) ||
271 !data->is_persisted()) {
272 MaybeAddToHistory(item);
273 return;
274 }
275
276 if (item->IsTemporary()) {
277 OnDownloadRemoved(item);
278 return;
279 }
280
281 // TODO(asanka): Persist GetTargetFilePath() as well.
282 DownloadPersistentStoreInfo current_info = GetPersistentStoreInfo(item);
283 InfoMap::const_iterator previous_info = infos_.find(item->GetId());
284 if ((previous_info == infos_.end()) ||
285 (previous_info->second.end_time != current_info.end_time) ||
286 (previous_info->second.received_bytes != current_info.received_bytes) ||
287 (previous_info->second.state != current_info.state) ||
288 (previous_info->second.opened != current_info.opened)) {
289 history_->UpdateDownload(current_info);
290 }
291 infos_[item->GetId()] = current_info;
292 }
293
294 void DownloadHistory::OnDownloadRemoved(DownloadItem* item) {
295 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
296
297 DownloadHistoryData* data = DownloadHistoryData::Get(item);
298 if ((data == NULL) ||
299 !data->is_persisted() ||
300 (data->db_handle() <= DownloadHistoryData::kUninitializedHandle))
301 return;
302
303 // For database efficiency, batch removals together if they happen all at
304 // once.
305 if (removing_.empty()) {
306 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
307 base::Bind(&DownloadHistory::RemoveDownloadsBatch, AsWeakPtr()));
308 }
309 removing_.insert(data->db_handle());
310 data->set_db_handle(DownloadHistoryData::kUninitializedHandle);
311 data->set_is_persisted(false);
312 }
313
314 void DownloadHistory::RemoveDownloadsBatch() {
315 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
316 HandleSet remove_handles;
317 removing_.swap(remove_handles);
318 history_->RemoveDownloads(remove_handles);
319 --history_size_;
320 }
321
322 void DownloadHistory::OnDownloadDestroyed(DownloadItem* item) {
323 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
324 infos_.erase(item->GetId());
325 item->RemoveObserver(this);
326 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698