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

Side by Side Diff: content/browser/download/download_item_impl.cc

Issue 8503018: Split DownloadItem into an ABC, an Impl, and a Mock. (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: " Created 9 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) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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 "content/browser/download/download_item.h" 5 #include "content/browser/download/download_item_impl.h"
6 6
7 #include <vector> 7 #include <vector>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/basictypes.h" 10 #include "base/basictypes.h"
11 #include "base/file_util.h" 11 #include "base/file_util.h"
12 #include "base/format_macros.h" 12 #include "base/format_macros.h"
13 #include "base/i18n/case_conversion.h" 13 #include "base/i18n/case_conversion.h"
14 #include "base/i18n/string_search.h" 14 #include "base/i18n/string_search.h"
15 #include "base/logging.h" 15 #include "base/logging.h"
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after
109 return dangerous_file ? 109 return dangerous_file ?
110 DownloadItem::DANGEROUS_FILE : DownloadItem::NOT_DANGEROUS; 110 DownloadItem::DANGEROUS_FILE : DownloadItem::NOT_DANGEROUS;
111 } 111 }
112 112
113 } // namespace 113 } // namespace
114 114
115 // Our download table ID starts at 1, so we use 0 to represent a download that 115 // Our download table ID starts at 1, so we use 0 to represent a download that
116 // has started, but has not yet had its data persisted in the table. We use fake 116 // has started, but has not yet had its data persisted in the table. We use fake
117 // database handles in incognito mode starting at -1 and progressively getting 117 // database handles in incognito mode starting at -1 and progressively getting
118 // more negative. 118 // more negative.
119 // static
120 const int DownloadItem::kUninitializedHandle = 0;
121 119
122 // Constructor for reading from the history service. 120 // Constructor for reading from the history service.
123 DownloadItem::DownloadItem(DownloadManager* download_manager, 121 DownloadItemImpl::DownloadItemImpl(DownloadManager* download_manager,
124 const DownloadPersistentStoreInfo& info) 122 const DownloadPersistentStoreInfo& info)
125 : download_id_(download_manager->GetNextId()), 123 : download_id_(download_manager->GetNextId()),
126 full_path_(info.path), 124 full_path_(info.path),
127 url_chain_(1, info.url), 125 url_chain_(1, info.url),
128 referrer_url_(info.referrer_url), 126 referrer_url_(info.referrer_url),
129 total_bytes_(info.total_bytes), 127 total_bytes_(info.total_bytes),
130 received_bytes_(info.received_bytes), 128 received_bytes_(info.received_bytes),
131 start_tick_(base::TimeTicks()), 129 start_tick_(base::TimeTicks()),
132 state_(static_cast<DownloadState>(info.state)), 130 state_(static_cast<DownloadState>(info.state)),
133 start_time_(info.start_time), 131 start_time_(info.start_time),
(...skipping 12 matching lines...) Expand all
146 open_enabled_(true), 144 open_enabled_(true),
147 delegate_delayed_complete_(false) { 145 delegate_delayed_complete_(false) {
148 if (IsInProgress()) 146 if (IsInProgress())
149 state_ = CANCELLED; 147 state_ = CANCELLED;
150 if (IsComplete()) 148 if (IsComplete())
151 all_data_saved_ = true; 149 all_data_saved_ = true;
152 Init(false /* not actively downloading */); 150 Init(false /* not actively downloading */);
153 } 151 }
154 152
155 // Constructing for a regular download: 153 // Constructing for a regular download:
156 DownloadItem::DownloadItem(DownloadManager* download_manager, 154 DownloadItemImpl::DownloadItemImpl(DownloadManager* download_manager,
157 const DownloadCreateInfo& info, 155 const DownloadCreateInfo& info,
158 DownloadRequestHandleInterface* request_handle, 156 DownloadRequestHandleInterface* request_handle,
159 bool is_otr) 157 bool is_otr)
160 : state_info_(info.original_name, info.save_info.file_path, 158 : state_info_(info.original_name, info.save_info.file_path,
161 info.has_user_gesture, info.transition_type, 159 info.has_user_gesture, info.transition_type,
162 info.prompt_user_for_save_location, info.path_uniquifier, 160 info.prompt_user_for_save_location, info.path_uniquifier,
163 false, false), 161 false, false),
164 request_handle_(request_handle), 162 request_handle_(request_handle),
165 download_id_(info.download_id), 163 download_id_(info.download_id),
166 full_path_(info.path), 164 full_path_(info.path),
(...skipping 20 matching lines...) Expand all
187 is_otr_(is_otr), 185 is_otr_(is_otr),
188 is_temporary_(!info.save_info.file_path.empty()), 186 is_temporary_(!info.save_info.file_path.empty()),
189 all_data_saved_(false), 187 all_data_saved_(false),
190 opened_(false), 188 opened_(false),
191 open_enabled_(true), 189 open_enabled_(true),
192 delegate_delayed_complete_(false) { 190 delegate_delayed_complete_(false) {
193 Init(true /* actively downloading */); 191 Init(true /* actively downloading */);
194 } 192 }
195 193
196 // Constructing for the "Save Page As..." feature: 194 // Constructing for the "Save Page As..." feature:
197 DownloadItem::DownloadItem(DownloadManager* download_manager, 195 DownloadItemImpl::DownloadItemImpl(DownloadManager* download_manager,
198 const FilePath& path, 196 const FilePath& path,
199 const GURL& url, 197 const GURL& url,
200 bool is_otr, 198 bool is_otr,
201 DownloadId download_id) 199 DownloadId download_id)
202 : download_id_(download_id), 200 : download_id_(download_id),
203 full_path_(path), 201 full_path_(path),
204 url_chain_(1, url), 202 url_chain_(1, url),
205 referrer_url_(GURL()), 203 referrer_url_(GURL()),
206 total_bytes_(0), 204 total_bytes_(0),
207 received_bytes_(0), 205 received_bytes_(0),
(...skipping 10 matching lines...) Expand all
218 auto_opened_(false), 216 auto_opened_(false),
219 is_otr_(is_otr), 217 is_otr_(is_otr),
220 is_temporary_(false), 218 is_temporary_(false),
221 all_data_saved_(false), 219 all_data_saved_(false),
222 opened_(false), 220 opened_(false),
223 open_enabled_(true), 221 open_enabled_(true),
224 delegate_delayed_complete_(false) { 222 delegate_delayed_complete_(false) {
225 Init(true /* actively downloading */); 223 Init(true /* actively downloading */);
226 } 224 }
227 225
228 DownloadItem::~DownloadItem() { 226 DownloadItemImpl::~DownloadItemImpl() {
229 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. 227 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
230 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 228 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
231 229
232 TransitionTo(REMOVING); 230 TransitionTo(REMOVING);
233 download_manager_->AssertQueueStateConsistent(this); 231 download_manager_->AssertQueueStateConsistent(this);
234 } 232 }
235 233
236 void DownloadItem::AddObserver(Observer* observer) { 234 void DownloadItemImpl::AddObserver(Observer* observer) {
237 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. 235 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
238 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 236 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
239 237
240 observers_.AddObserver(observer); 238 observers_.AddObserver(observer);
241 } 239 }
242 240
243 void DownloadItem::RemoveObserver(Observer* observer) { 241 void DownloadItemImpl::RemoveObserver(Observer* observer) {
244 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. 242 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
245 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 243 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
246 244
247 observers_.RemoveObserver(observer); 245 observers_.RemoveObserver(observer);
248 } 246 }
249 247
250 void DownloadItem::UpdateObservers() { 248 void DownloadItemImpl::UpdateObservers() {
251 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. 249 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
252 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 250 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
253 251
254 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadUpdated(this)); 252 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadUpdated(this));
255 } 253 }
256 254
257 bool DownloadItem::CanShowInFolder() { 255 bool DownloadItemImpl::CanShowInFolder() {
258 return !IsCancelled() && !file_externally_removed_; 256 return !IsCancelled() && !file_externally_removed_;
259 } 257 }
260 258
261 bool DownloadItem::CanOpenDownload() { 259 bool DownloadItemImpl::CanOpenDownload() {
262 return !file_externally_removed_; 260 return !file_externally_removed_;
263 } 261 }
264 262
265 bool DownloadItem::ShouldOpenFileBasedOnExtension() { 263 bool DownloadItemImpl::ShouldOpenFileBasedOnExtension() {
266 return download_manager_->delegate()->ShouldOpenFileBasedOnExtension( 264 return download_manager_->delegate()->ShouldOpenFileBasedOnExtension(
267 GetUserVerifiedFilePath()); 265 GetUserVerifiedFilePath());
268 } 266 }
269 267
270 void DownloadItem::OpenDownload() { 268 void DownloadItemImpl::OpenDownload() {
271 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. 269 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
272 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 270 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
273 271
274 if (IsPartialDownload()) { 272 if (IsPartialDownload()) {
275 open_when_complete_ = !open_when_complete_; 273 open_when_complete_ = !open_when_complete_;
276 return; 274 return;
277 } 275 }
278 276
279 if (!IsComplete() || file_externally_removed_) 277 if (!IsComplete() || file_externally_removed_)
280 return; 278 return;
281 279
282 // Ideally, we want to detect errors in opening and report them, but we 280 // Ideally, we want to detect errors in opening and report them, but we
283 // don't generally have the proper interface for that to the external 281 // don't generally have the proper interface for that to the external
284 // program that opens the file. So instead we spawn a check to update 282 // program that opens the file. So instead we spawn a check to update
285 // the UI if the file has been deleted in parallel with the open. 283 // the UI if the file has been deleted in parallel with the open.
286 download_manager_->CheckForFileRemoval(this); 284 download_manager_->CheckForFileRemoval(this);
287 download_stats::RecordOpen(end_time(), !opened()); 285 download_stats::RecordOpen(end_time(), !opened());
288 opened_ = true; 286 opened_ = true;
289 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadOpened(this)); 287 FOR_EACH_OBSERVER(Observer, observers_, OnDownloadOpened(this));
290 download_manager_->MarkDownloadOpened(this); 288 download_manager_->MarkDownloadOpened(this);
291 289
292 // For testing: If download opening is disabled on this item, 290 // For testing: If download opening is disabled on this item,
293 // make the rest of the routine a no-op. 291 // make the rest of the routine a no-op.
294 if (!open_enabled_) 292 if (!open_enabled_)
295 return; 293 return;
296 294
297 content::GetContentClient()->browser()->OpenItem(full_path()); 295 content::GetContentClient()->browser()->OpenItem(full_path());
298 } 296 }
299 297
300 void DownloadItem::ShowDownloadInShell() { 298 void DownloadItemImpl::ShowDownloadInShell() {
301 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. 299 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
302 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 300 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
303 301
304 content::GetContentClient()->browser()->ShowItemInFolder(full_path()); 302 content::GetContentClient()->browser()->ShowItemInFolder(full_path());
305 } 303 }
306 304
307 void DownloadItem::DangerousDownloadValidated() { 305 void DownloadItemImpl::DangerousDownloadValidated() {
308 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 306 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
309 DCHECK_EQ(DANGEROUS, safety_state()); 307 DCHECK_EQ(DANGEROUS, safety_state());
310 308
311 UMA_HISTOGRAM_ENUMERATION("Download.DangerousDownloadValidated", 309 UMA_HISTOGRAM_ENUMERATION("Download.DangerousDownloadValidated",
312 GetDangerType(), 310 GetDangerType(),
313 DANGEROUS_TYPE_MAX); 311 DANGEROUS_TYPE_MAX);
314 312
315 safety_state_ = DANGEROUS_BUT_VALIDATED; 313 safety_state_ = DANGEROUS_BUT_VALIDATED;
316 UpdateObservers(); 314 UpdateObservers();
317 315
318 download_manager_->MaybeCompleteDownload(this); 316 download_manager_->MaybeCompleteDownload(this);
319 } 317 }
320 318
321 void DownloadItem::UpdateSize(int64 bytes_so_far) { 319 void DownloadItemImpl::UpdateSize(int64 bytes_so_far) {
322 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. 320 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
323 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 321 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
324 322
325 received_bytes_ = bytes_so_far; 323 received_bytes_ = bytes_so_far;
326 324
327 // If we've received more data than we were expecting (bad server info?), 325 // If we've received more data than we were expecting (bad server info?),
328 // revert to 'unknown size mode'. 326 // revert to 'unknown size mode'.
329 if (received_bytes_ > total_bytes_) 327 if (received_bytes_ > total_bytes_)
330 total_bytes_ = 0; 328 total_bytes_ = 0;
331 } 329 }
332 330
333 // Updates from the download thread may have been posted while this download 331 // Updates from the download thread may have been posted while this download
334 // was being cancelled in the UI thread, so we'll accept them unless we're 332 // was being cancelled in the UI thread, so we'll accept them unless we're
335 // complete. 333 // complete.
336 void DownloadItem::Update(int64 bytes_so_far) { 334 void DownloadItemImpl::Update(int64 bytes_so_far) {
337 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. 335 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
338 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 336 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
339 337
340 if (!IsInProgress()) { 338 if (!IsInProgress()) {
341 NOTREACHED(); 339 NOTREACHED();
342 return; 340 return;
343 } 341 }
344 UpdateSize(bytes_so_far); 342 UpdateSize(bytes_so_far);
345 UpdateObservers(); 343 UpdateObservers();
346 } 344 }
347 345
348 // Triggered by a user action. 346 // Triggered by a user action.
349 void DownloadItem::Cancel(bool user_cancel) { 347 void DownloadItemImpl::Cancel(bool user_cancel) {
350 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. 348 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
351 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 349 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
352 350
353 last_reason_ = user_cancel ? 351 last_reason_ = user_cancel ?
354 DOWNLOAD_INTERRUPT_REASON_USER_CANCELED : 352 DOWNLOAD_INTERRUPT_REASON_USER_CANCELED :
355 DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN; 353 DOWNLOAD_INTERRUPT_REASON_USER_SHUTDOWN;
356 354
357 VLOG(20) << __FUNCTION__ << "() download = " << DebugString(true); 355 VLOG(20) << __FUNCTION__ << "() download = " << DebugString(true);
358 if (!IsPartialDownload()) { 356 if (!IsPartialDownload()) {
359 // Small downloads might be complete before this method has 357 // Small downloads might be complete before this method has
360 // a chance to run. 358 // a chance to run.
361 return; 359 return;
362 } 360 }
363 361
364 download_stats::RecordDownloadCount(download_stats::CANCELLED_COUNT); 362 download_stats::RecordDownloadCount(download_stats::CANCELLED_COUNT);
365 363
366 TransitionTo(CANCELLED); 364 TransitionTo(CANCELLED);
367 if (user_cancel) 365 if (user_cancel)
368 download_manager_->DownloadCancelledInternal(this); 366 download_manager_->DownloadCancelledInternal(this);
369 } 367 }
370 368
371 void DownloadItem::MarkAsComplete() { 369 void DownloadItemImpl::MarkAsComplete() {
372 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. 370 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
373 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 371 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
374 372
375 DCHECK(all_data_saved_); 373 DCHECK(all_data_saved_);
376 end_time_ = base::Time::Now(); 374 end_time_ = base::Time::Now();
377 TransitionTo(COMPLETE); 375 TransitionTo(COMPLETE);
378 } 376 }
379 377
380 void DownloadItem::DelayedDownloadOpened() { 378 void DownloadItemImpl::DelayedDownloadOpened() {
381 auto_opened_ = true; 379 auto_opened_ = true;
382 Completed(); 380 Completed();
383 } 381 }
384 382
385 void DownloadItem::OnAllDataSaved(int64 size) { 383 void DownloadItemImpl::OnAllDataSaved(int64 size) {
386 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. 384 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
387 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 385 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
388 386
389 DCHECK(!all_data_saved_); 387 DCHECK(!all_data_saved_);
390 all_data_saved_ = true; 388 all_data_saved_ = true;
391 UpdateSize(size); 389 UpdateSize(size);
392 } 390 }
393 391
394 void DownloadItem::OnDownloadedFileRemoved() { 392 void DownloadItemImpl::OnDownloadedFileRemoved() {
395 file_externally_removed_ = true; 393 file_externally_removed_ = true;
396 UpdateObservers(); 394 UpdateObservers();
397 } 395 }
398 396
399 void DownloadItem::Completed() { 397 void DownloadItemImpl::Completed() {
400 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. 398 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
401 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 399 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
402 400
403 VLOG(20) << __FUNCTION__ << "() " << DebugString(false); 401 VLOG(20) << __FUNCTION__ << "() " << DebugString(false);
404 402
405 DCHECK(all_data_saved_); 403 DCHECK(all_data_saved_);
406 end_time_ = base::Time::Now(); 404 end_time_ = base::Time::Now();
407 TransitionTo(COMPLETE); 405 TransitionTo(COMPLETE);
408 download_manager_->DownloadCompleted(id()); 406 download_manager_->DownloadCompleted(id());
409 download_stats::RecordDownloadCompleted(start_tick_, received_bytes_); 407 download_stats::RecordDownloadCompleted(start_tick_, received_bytes_);
410 408
411 if (auto_opened_) { 409 if (auto_opened_) {
412 // If it was already handled by the delegate, do nothing. 410 // If it was already handled by the delegate, do nothing.
413 } else if (open_when_complete() || 411 } else if (open_when_complete() ||
414 ShouldOpenFileBasedOnExtension() || 412 ShouldOpenFileBasedOnExtension() ||
415 is_temporary()) { 413 is_temporary()) {
416 // If the download is temporary, like in drag-and-drop, do not open it but 414 // If the download is temporary, like in drag-and-drop, do not open it but
417 // we still need to set it auto-opened so that it can be removed from the 415 // we still need to set it auto-opened so that it can be removed from the
418 // download shelf. 416 // download shelf.
419 if (!is_temporary()) 417 if (!is_temporary())
420 OpenDownload(); 418 OpenDownload();
421 419
422 auto_opened_ = true; 420 auto_opened_ = true;
423 UpdateObservers(); 421 UpdateObservers();
424 } 422 }
425 } 423 }
426 424
427 void DownloadItem::TransitionTo(DownloadState new_state) { 425 void DownloadItemImpl::TransitionTo(DownloadState new_state) {
428 if (state_ == new_state) 426 if (state_ == new_state)
429 return; 427 return;
430 428
431 state_ = new_state; 429 state_ = new_state;
432 UpdateObservers(); 430 UpdateObservers();
433 } 431 }
434 432
435 void DownloadItem::UpdateSafetyState() { 433 void DownloadItemImpl::UpdateSafetyState() {
436 SafetyState updated_value( 434 SafetyState updated_value(
437 GetSafetyState(state_info_.is_dangerous_file, 435 GetSafetyState(state_info_.is_dangerous_file,
438 state_info_.is_dangerous_url)); 436 state_info_.is_dangerous_url));
439 if (updated_value != safety_state_) { 437 if (updated_value != safety_state_) {
440 safety_state_ = updated_value; 438 safety_state_ = updated_value;
441 UpdateObservers(); 439 UpdateObservers();
442 } 440 }
443 } 441 }
444 442
445 void DownloadItem::UpdateTarget() { 443 void DownloadItemImpl::UpdateTarget() {
446 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. 444 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
447 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 445 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
448 446
449 if (state_info_.target_name.value().empty()) 447 if (state_info_.target_name.value().empty())
450 state_info_.target_name = full_path_.BaseName(); 448 state_info_.target_name = full_path_.BaseName();
451 } 449 }
452 450
453 void DownloadItem::Interrupted(int64 size, InterruptReason reason) { 451 void DownloadItemImpl::Interrupted(int64 size, InterruptReason reason) {
454 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. 452 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
455 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 453 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
456 454
457 if (!IsInProgress()) 455 if (!IsInProgress())
458 return; 456 return;
459 457
460 last_reason_ = reason; 458 last_reason_ = reason;
461 UpdateSize(size); 459 UpdateSize(size);
462 download_stats::RecordDownloadInterrupted(reason, 460 download_stats::RecordDownloadInterrupted(reason,
463 received_bytes_, 461 received_bytes_,
464 total_bytes_); 462 total_bytes_);
465 TransitionTo(INTERRUPTED); 463 TransitionTo(INTERRUPTED);
466 } 464 }
467 465
468 void DownloadItem::Delete(DeleteReason reason) { 466 void DownloadItemImpl::Delete(DeleteReason reason) {
469 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. 467 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
470 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 468 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
471 469
472 switch (reason) { 470 switch (reason) {
473 case DELETE_DUE_TO_USER_DISCARD: 471 case DELETE_DUE_TO_USER_DISCARD:
474 UMA_HISTOGRAM_ENUMERATION("Download.UserDiscard", GetDangerType(), 472 UMA_HISTOGRAM_ENUMERATION("Download.UserDiscard", GetDangerType(),
475 DANGEROUS_TYPE_MAX); 473 DANGEROUS_TYPE_MAX);
476 break; 474 break;
477 case DELETE_DUE_TO_BROWSER_SHUTDOWN: 475 case DELETE_DUE_TO_BROWSER_SHUTDOWN:
478 UMA_HISTOGRAM_ENUMERATION("Download.Discard", GetDangerType(), 476 UMA_HISTOGRAM_ENUMERATION("Download.Discard", GetDangerType(),
479 DANGEROUS_TYPE_MAX); 477 DANGEROUS_TYPE_MAX);
480 break; 478 break;
481 default: 479 default:
482 NOTREACHED(); 480 NOTREACHED();
483 } 481 }
484 482
485 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 483 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
486 base::Bind(&DeleteDownloadedFile, full_path_)); 484 base::Bind(&DeleteDownloadedFile, full_path_));
487 Remove(); 485 Remove();
488 // We have now been deleted. 486 // We have now been deleted.
489 } 487 }
490 488
491 void DownloadItem::Remove() { 489 void DownloadItemImpl::Remove() {
492 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. 490 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
493 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 491 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
494 492
495 download_manager_->AssertQueueStateConsistent(this); 493 download_manager_->AssertQueueStateConsistent(this);
496 Cancel(true); 494 Cancel(true);
497 download_manager_->AssertQueueStateConsistent(this); 495 download_manager_->AssertQueueStateConsistent(this);
498 496
499 TransitionTo(REMOVING); 497 TransitionTo(REMOVING);
500 download_manager_->RemoveDownload(db_handle_); 498 download_manager_->RemoveDownload(db_handle_);
501 // We have now been deleted. 499 // We have now been deleted.
502 } 500 }
503 501
504 bool DownloadItem::TimeRemaining(base::TimeDelta* remaining) const { 502 bool DownloadItemImpl::TimeRemaining(base::TimeDelta* remaining) const {
505 if (total_bytes_ <= 0) 503 if (total_bytes_ <= 0)
506 return false; // We never received the content_length for this download. 504 return false; // We never received the content_length for this download.
507 505
508 int64 speed = CurrentSpeed(); 506 int64 speed = CurrentSpeed();
509 if (speed == 0) 507 if (speed == 0)
510 return false; 508 return false;
511 509
512 *remaining = base::TimeDelta::FromSeconds( 510 *remaining = base::TimeDelta::FromSeconds(
513 (total_bytes_ - received_bytes_) / speed); 511 (total_bytes_ - received_bytes_) / speed);
514 return true; 512 return true;
515 } 513 }
516 514
517 int64 DownloadItem::CurrentSpeed() const { 515 int64 DownloadItemImpl::CurrentSpeed() const {
518 if (is_paused_) 516 if (is_paused_)
519 return 0; 517 return 0;
520 base::TimeDelta diff = base::TimeTicks::Now() - start_tick_; 518 base::TimeDelta diff = base::TimeTicks::Now() - start_tick_;
521 int64 diff_ms = diff.InMilliseconds(); 519 int64 diff_ms = diff.InMilliseconds();
522 return diff_ms == 0 ? 0 : received_bytes_ * 1000 / diff_ms; 520 return diff_ms == 0 ? 0 : received_bytes_ * 1000 / diff_ms;
523 } 521 }
524 522
525 int DownloadItem::PercentComplete() const { 523 int DownloadItemImpl::PercentComplete() const {
526 // If the delegate is delaying completion of the download, then we have no 524 // If the delegate is delaying completion of the download, then we have no
527 // idea how long it will take. 525 // idea how long it will take.
528 if (delegate_delayed_complete_ || total_bytes_ <= 0) 526 if (delegate_delayed_complete_ || total_bytes_ <= 0)
529 return -1; 527 return -1;
530 528
531 return static_cast<int>(received_bytes_ * 100.0 / total_bytes_); 529 return static_cast<int>(received_bytes_ * 100.0 / total_bytes_);
532 } 530 }
533 531
534 void DownloadItem::OnPathDetermined(const FilePath& path) { 532 void DownloadItemImpl::OnPathDetermined(const FilePath& path) {
535 full_path_ = path; 533 full_path_ = path;
536 UpdateTarget(); 534 UpdateTarget();
537 } 535 }
538 536
539 void DownloadItem::Rename(const FilePath& full_path) { 537 void DownloadItemImpl::Rename(const FilePath& full_path) {
540 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. 538 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
541 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 539 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
542 540
543 VLOG(20) << __FUNCTION__ << "()" 541 VLOG(20) << __FUNCTION__ << "()"
544 << " full_path = \"" << full_path.value() << "\"" 542 << " full_path = \"" << full_path.value() << "\""
545 << " " << DebugString(true); 543 << " " << DebugString(true);
546 DCHECK(!full_path.empty()); 544 DCHECK(!full_path.empty());
547 full_path_ = full_path; 545 full_path_ = full_path;
548 } 546 }
549 547
550 void DownloadItem::TogglePause() { 548 void DownloadItemImpl::TogglePause() {
551 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. 549 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
552 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 550 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
553 551
554 DCHECK(IsInProgress()); 552 DCHECK(IsInProgress());
555 if (is_paused_) 553 if (is_paused_)
556 request_handle_->ResumeRequest(); 554 request_handle_->ResumeRequest();
557 else 555 else
558 request_handle_->PauseRequest(); 556 request_handle_->PauseRequest();
559 is_paused_ = !is_paused_; 557 is_paused_ = !is_paused_;
560 UpdateObservers(); 558 UpdateObservers();
561 } 559 }
562 560
563 void DownloadItem::OnDownloadCompleting(DownloadFileManager* file_manager) { 561 void DownloadItemImpl::OnDownloadCompleting(DownloadFileManager* file_manager) {
564 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. 562 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
565 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 563 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
566 564
567 VLOG(20) << __FUNCTION__ << "()" 565 VLOG(20) << __FUNCTION__ << "()"
568 << " needs rename = " << NeedsRename() 566 << " needs rename = " << NeedsRename()
569 << " " << DebugString(true); 567 << " " << DebugString(true);
570 DCHECK_NE(DANGEROUS, safety_state()); 568 DCHECK_NE(DANGEROUS, safety_state());
571 DCHECK(file_manager); 569 DCHECK(file_manager);
572 570
573 if (NeedsRename()) { 571 if (NeedsRename()) {
574 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 572 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
575 base::Bind(&DownloadFileManager::RenameCompletingDownloadFile, 573 base::Bind(&DownloadFileManager::RenameCompletingDownloadFile,
576 file_manager, global_id(), 574 file_manager, global_id(),
577 GetTargetFilePath(), safety_state() == SAFE)); 575 GetTargetFilePath(), safety_state() == SAFE));
578 return; 576 return;
579 } 577 }
580 578
581 Completed(); 579 Completed();
582 580
583 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 581 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
584 base::Bind(&DownloadFileManager::CompleteDownload, 582 base::Bind(&DownloadFileManager::CompleteDownload,
585 file_manager, global_id())); 583 file_manager, global_id()));
586 } 584 }
587 585
588 void DownloadItem::OnDownloadRenamedToFinalName(const FilePath& full_path) { 586 void DownloadItemImpl::OnDownloadRenamedToFinalName(const FilePath& full_path) {
589 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. 587 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
590 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 588 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
591 589
592 VLOG(20) << __FUNCTION__ << "()" 590 VLOG(20) << __FUNCTION__ << "()"
593 << " full_path = \"" << full_path.value() << "\"" 591 << " full_path = \"" << full_path.value() << "\""
594 << " needed rename = " << NeedsRename() 592 << " needed rename = " << NeedsRename()
595 << " " << DebugString(false); 593 << " " << DebugString(false);
596 DCHECK(NeedsRename()); 594 DCHECK(NeedsRename());
597 595
598 Rename(full_path); 596 Rename(full_path);
599 597
600 if (download_manager_->delegate()->ShouldOpenDownload(this)) { 598 if (download_manager_->delegate()->ShouldOpenDownload(this)) {
601 Completed(); 599 Completed();
602 } else { 600 } else {
603 delegate_delayed_complete_ = true; 601 delegate_delayed_complete_ = true;
604 } 602 }
605 } 603 }
606 604
607 bool DownloadItem::MatchesQuery(const string16& query) const { 605 bool DownloadItemImpl::MatchesQuery(const string16& query) const {
608 if (query.empty()) 606 if (query.empty())
609 return true; 607 return true;
610 608
611 DCHECK_EQ(query, base::i18n::ToLower(query)); 609 DCHECK_EQ(query, base::i18n::ToLower(query));
612 610
613 string16 url_raw(UTF8ToUTF16(GetURL().spec())); 611 string16 url_raw(UTF8ToUTF16(GetURL().spec()));
614 if (base::i18n::StringSearchIgnoringCaseAndAccents(query, url_raw)) 612 if (base::i18n::StringSearchIgnoringCaseAndAccents(query, url_raw))
615 return true; 613 return true;
616 614
617 // TODO(phajdan.jr): write a test case for the following code. 615 // TODO(phajdan.jr): write a test case for the following code.
618 // A good test case would be: 616 // A good test case would be:
619 // "/\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xbd\xa0\xe5\xa5\xbd", 617 // "/\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xbd\xa0\xe5\xa5\xbd",
620 // L"/\x4f60\x597d\x4f60\x597d", 618 // L"/\x4f60\x597d\x4f60\x597d",
621 // "/%E4%BD%A0%E5%A5%BD%E4%BD%A0%E5%A5%BD" 619 // "/%E4%BD%A0%E5%A5%BD%E4%BD%A0%E5%A5%BD"
622 std::string languages; 620 std::string languages;
623 TabContents* tab = (request_handle_.get() ? 621 TabContents* tab = (request_handle_.get() ?
624 request_handle_->GetTabContents() : NULL); 622 request_handle_->GetTabContents() : NULL);
625 if (tab) 623 if (tab)
626 languages = content::GetContentClient()->browser()->GetAcceptLangs(tab); 624 languages = content::GetContentClient()->browser()->GetAcceptLangs(tab);
627 string16 url_formatted(net::FormatUrl(GetURL(), languages)); 625 string16 url_formatted(net::FormatUrl(GetURL(), languages));
628 if (base::i18n::StringSearchIgnoringCaseAndAccents(query, url_formatted)) 626 if (base::i18n::StringSearchIgnoringCaseAndAccents(query, url_formatted))
629 return true; 627 return true;
630 628
631 string16 path(full_path().LossyDisplayName()); 629 string16 path(full_path().LossyDisplayName());
632 return base::i18n::StringSearchIgnoringCaseAndAccents(query, path); 630 return base::i18n::StringSearchIgnoringCaseAndAccents(query, path);
633 } 631 }
634 632
635 void DownloadItem::SetFileCheckResults(const DownloadStateInfo& state) { 633 void DownloadItemImpl::SetFileCheckResults(const DownloadStateInfo& state) {
636 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. 634 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
637 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 635 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
638 636
639 VLOG(20) << " " << __FUNCTION__ << "()" << " this = " << DebugString(true); 637 VLOG(20) << " " << __FUNCTION__ << "()" << " this = " << DebugString(true);
640 state_info_ = state; 638 state_info_ = state;
641 VLOG(20) << " " << __FUNCTION__ << "()" << " this = " << DebugString(true); 639 VLOG(20) << " " << __FUNCTION__ << "()" << " this = " << DebugString(true);
642 640
643 UpdateSafetyState(); 641 UpdateSafetyState();
644 } 642 }
645 643
646 DownloadItem::DangerType DownloadItem::GetDangerType() const { 644 DownloadItem::DangerType DownloadItemImpl::GetDangerType() const {
647 return ::GetDangerType(state_info_.is_dangerous_file, 645 return ::GetDangerType(state_info_.is_dangerous_file,
648 state_info_.is_dangerous_url); 646 state_info_.is_dangerous_url);
649 } 647 }
650 648
651 bool DownloadItem::IsDangerous() const { 649 bool DownloadItemImpl::IsDangerous() const {
652 return GetDangerType() != DownloadItem::NOT_DANGEROUS; 650 return GetDangerType() != DownloadItem::NOT_DANGEROUS;
653 } 651 }
654 652
655 void DownloadItem::MarkFileDangerous() { 653 void DownloadItemImpl::MarkFileDangerous() {
656 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. 654 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
657 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 655 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
658 656
659 state_info_.is_dangerous_file = true; 657 state_info_.is_dangerous_file = true;
660 UpdateSafetyState(); 658 UpdateSafetyState();
661 } 659 }
662 660
663 void DownloadItem::MarkUrlDangerous() { 661 void DownloadItemImpl::MarkUrlDangerous() {
664 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. 662 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
665 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 663 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
666 664
667 state_info_.is_dangerous_url = true; 665 state_info_.is_dangerous_url = true;
668 UpdateSafetyState(); 666 UpdateSafetyState();
669 } 667 }
670 668
671 DownloadPersistentStoreInfo DownloadItem::GetPersistentStoreInfo() const { 669 DownloadPersistentStoreInfo DownloadItemImpl::GetPersistentStoreInfo() const {
672 return DownloadPersistentStoreInfo(full_path(), 670 return DownloadPersistentStoreInfo(full_path(),
673 GetURL(), 671 GetURL(),
674 referrer_url(), 672 referrer_url(),
675 start_time(), 673 start_time(),
676 end_time(), 674 end_time(),
677 received_bytes(), 675 received_bytes(),
678 total_bytes(), 676 total_bytes(),
679 state(), 677 state(),
680 db_handle(), 678 db_handle(),
681 opened()); 679 opened());
682 } 680 }
683 681
684 TabContents* DownloadItem::GetTabContents() const { 682 TabContents* DownloadItemImpl::GetTabContents() const {
685 if (request_handle_.get()) 683 if (request_handle_.get())
686 return request_handle_->GetTabContents(); 684 return request_handle_->GetTabContents();
687 return NULL; 685 return NULL;
688 } 686 }
689 687
690 FilePath DownloadItem::GetTargetFilePath() const { 688 FilePath DownloadItemImpl::GetTargetFilePath() const {
691 return full_path_.DirName().Append(state_info_.target_name); 689 return full_path_.DirName().Append(state_info_.target_name);
692 } 690 }
693 691
694 FilePath DownloadItem::GetFileNameToReportUser() const { 692 FilePath DownloadItemImpl::GetFileNameToReportUser() const {
695 if (state_info_.path_uniquifier > 0) { 693 if (state_info_.path_uniquifier > 0) {
696 FilePath name(state_info_.target_name); 694 FilePath name(state_info_.target_name);
697 DownloadFile::AppendNumberToPath(&name, state_info_.path_uniquifier); 695 DownloadFile::AppendNumberToPath(&name, state_info_.path_uniquifier);
698 return name; 696 return name;
699 } 697 }
700 return state_info_.target_name; 698 return state_info_.target_name;
701 } 699 }
702 700
703 FilePath DownloadItem::GetUserVerifiedFilePath() const { 701 FilePath DownloadItemImpl::GetUserVerifiedFilePath() const {
704 return (safety_state_ == DownloadItem::SAFE) ? 702 return (safety_state_ == DownloadItem::SAFE) ?
705 GetTargetFilePath() : full_path_; 703 GetTargetFilePath() : full_path_;
706 } 704 }
707 705
708 void DownloadItem::OffThreadCancel(DownloadFileManager* file_manager) { 706 void DownloadItemImpl::OffThreadCancel(DownloadFileManager* file_manager) {
709 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 707 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
710 request_handle_->CancelRequest(); 708 request_handle_->CancelRequest();
711 709
712 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 710 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
713 base::Bind(&DownloadFileManager::CancelDownload, 711 base::Bind(&DownloadFileManager::CancelDownload,
714 file_manager, global_id())); 712 file_manager, global_id()));
715 } 713 }
716 714
717 void DownloadItem::Init(bool active) { 715 void DownloadItemImpl::Init(bool active) {
718 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. 716 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved.
719 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 717 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
720 718
721 UpdateTarget(); 719 UpdateTarget();
722 if (active) { 720 if (active) {
723 download_stats::RecordDownloadCount(download_stats::START_COUNT); 721 download_stats::RecordDownloadCount(download_stats::START_COUNT);
724 } 722 }
725 VLOG(20) << __FUNCTION__ << "() " << DebugString(true); 723 VLOG(20) << __FUNCTION__ << "() " << DebugString(true);
726 } 724 }
727 725
728 // TODO(ahendrickson) -- Move |INTERRUPTED| from |IsCancelled()| to 726 // TODO(ahendrickson) -- Move |INTERRUPTED| from |IsCancelled()| to
729 // |IsPartialDownload()|, when resuming interrupted downloads is implemented. 727 // |IsPartialDownload()|, when resuming interrupted downloads is implemented.
730 bool DownloadItem::IsPartialDownload() const { 728 bool DownloadItemImpl::IsPartialDownload() const {
731 return (state_ == IN_PROGRESS); 729 return (state_ == IN_PROGRESS);
732 } 730 }
733 731
734 bool DownloadItem::IsInProgress() const { 732 bool DownloadItemImpl::IsInProgress() const {
735 return (state_ == IN_PROGRESS); 733 return (state_ == IN_PROGRESS);
736 } 734 }
737 735
738 bool DownloadItem::IsCancelled() const { 736 bool DownloadItemImpl::IsCancelled() const {
739 return (state_ == CANCELLED) || 737 return (state_ == CANCELLED) ||
740 (state_ == INTERRUPTED); 738 (state_ == INTERRUPTED);
741 } 739 }
742 740
743 bool DownloadItem::IsInterrupted() const { 741 bool DownloadItemImpl::IsInterrupted() const {
744 return (state_ == INTERRUPTED); 742 return (state_ == INTERRUPTED);
745 } 743 }
746 744
747 bool DownloadItem::IsComplete() const { 745 bool DownloadItemImpl::IsComplete() const {
748 return (state_ == COMPLETE); 746 return (state_ == COMPLETE);
749 } 747 }
750 748
751 const GURL& DownloadItem::GetURL() const { 749 const GURL& DownloadItemImpl::GetURL() const {
752 return url_chain_.empty() ? 750 return url_chain_.empty() ?
753 GURL::EmptyGURL() : url_chain_.back(); 751 GURL::EmptyGURL() : url_chain_.back();
754 } 752 }
755 753
756 std::string DownloadItem::DebugString(bool verbose) const { 754 std::string DownloadItemImpl::DebugString(bool verbose) const {
757 std::string description = 755 std::string description =
758 base::StringPrintf("{ id = %d" 756 base::StringPrintf("{ id = %d"
759 " state = %s", 757 " state = %s",
760 download_id_.local(), 758 download_id_.local(),
761 DebugDownloadStateString(state())); 759 DebugDownloadStateString(state()));
762 760
763 // Construct a string of the URL chain. 761 // Construct a string of the URL chain.
764 std::string url_list("<none>"); 762 std::string url_list("<none>");
765 if (!url_chain_.empty()) { 763 if (!url_chain_.empty()) {
766 std::vector<GURL>::const_iterator iter = url_chain_.begin(); 764 std::vector<GURL>::const_iterator iter = url_chain_.begin();
(...skipping 28 matching lines...) Expand all
795 state_info_.target_name.value().c_str(), 793 state_info_.target_name.value().c_str(),
796 full_path().value().c_str()); 794 full_path().value().c_str());
797 } else { 795 } else {
798 description += base::StringPrintf(" url = \"%s\"", url_list.c_str()); 796 description += base::StringPrintf(" url = \"%s\"", url_list.c_str());
799 } 797 }
800 798
801 description += " }"; 799 description += " }";
802 800
803 return description; 801 return description;
804 } 802 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698