OLD | NEW |
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/history/history_backend.h" | 5 #include "chrome/browser/history/history_backend.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <list> | 8 #include <list> |
9 #include <map> | 9 #include <map> |
10 #include <set> | 10 #include <set> |
(...skipping 394 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
405 // Get the starting visit_time for visit_id. | 405 // Get the starting visit_time for visit_id. |
406 VisitRow visit_row; | 406 VisitRow visit_row; |
407 if (db_->GetRowForVisit(visit_id, &visit_row)) { | 407 if (db_->GetRowForVisit(visit_id, &visit_row)) { |
408 // We should never have a negative duration time even when time is skewed. | 408 // We should never have a negative duration time even when time is skewed. |
409 visit_row.visit_duration = end_ts > visit_row.visit_time ? | 409 visit_row.visit_duration = end_ts > visit_row.visit_time ? |
410 end_ts - visit_row.visit_time : TimeDelta::FromMicroseconds(0); | 410 end_ts - visit_row.visit_time : TimeDelta::FromMicroseconds(0); |
411 db_->UpdateVisitRow(visit_row); | 411 db_->UpdateVisitRow(visit_row); |
412 } | 412 } |
413 } | 413 } |
414 | 414 |
415 void HistoryBackend::AddPage(scoped_refptr<HistoryAddPageArgs> request) { | 415 void HistoryBackend::AddPage(const HistoryAddPageArgs& request) { |
416 if (!db_.get()) | 416 if (!db_.get()) |
417 return; | 417 return; |
418 | 418 |
419 // Will be filled with the URL ID and the visit ID of the last addition. | 419 // Will be filled with the URL ID and the visit ID of the last addition. |
420 std::pair<URLID, VisitID> last_ids(0, tracker_.GetLastVisit( | 420 std::pair<URLID, VisitID> last_ids(0, tracker_.GetLastVisit( |
421 request->id_scope, request->page_id, request->referrer)); | 421 request.id_scope, request.page_id, request.referrer)); |
422 | 422 |
423 VisitID from_visit_id = last_ids.second; | 423 VisitID from_visit_id = last_ids.second; |
424 | 424 |
425 // If a redirect chain is given, we expect the last item in that chain to be | 425 // If a redirect chain is given, we expect the last item in that chain to be |
426 // the final URL. | 426 // the final URL. |
427 DCHECK(request->redirects.empty() || | 427 DCHECK(request.redirects.empty() || |
428 request->redirects.back() == request->url); | 428 request.redirects.back() == request.url); |
429 | 429 |
430 // Avoid duplicating times in the database, at least as long as pages are | 430 // Avoid duplicating times in the database, at least as long as pages are |
431 // added in order. However, we don't want to disallow pages from recording | 431 // added in order. However, we don't want to disallow pages from recording |
432 // times earlier than our last_recorded_time_, because someone might set | 432 // times earlier than our last_recorded_time_, because someone might set |
433 // their machine's clock back. | 433 // their machine's clock back. |
434 if (last_requested_time_ == request->time) { | 434 if (last_requested_time_ == request.time) { |
435 last_recorded_time_ = last_recorded_time_ + TimeDelta::FromMicroseconds(1); | 435 last_recorded_time_ = last_recorded_time_ + TimeDelta::FromMicroseconds(1); |
436 } else { | 436 } else { |
437 last_requested_time_ = request->time; | 437 last_requested_time_ = request.time; |
438 last_recorded_time_ = last_requested_time_; | 438 last_recorded_time_ = last_requested_time_; |
439 } | 439 } |
440 | 440 |
441 // If the user is adding older history, we need to make sure our times | 441 // If the user is adding older history, we need to make sure our times |
442 // are correct. | 442 // are correct. |
443 if (request->time < first_recorded_time_) | 443 if (request.time < first_recorded_time_) |
444 first_recorded_time_ = request->time; | 444 first_recorded_time_ = request.time; |
445 | 445 |
446 content::PageTransition transition = | 446 content::PageTransition request_transition = request.transition; |
447 content::PageTransitionStripQualifier(request->transition); | 447 content::PageTransition stripped_transition = |
| 448 content::PageTransitionStripQualifier(request_transition); |
448 bool is_keyword_generated = | 449 bool is_keyword_generated = |
449 (transition == content::PAGE_TRANSITION_KEYWORD_GENERATED); | 450 (stripped_transition == content::PAGE_TRANSITION_KEYWORD_GENERATED); |
450 | 451 |
451 // If the user is navigating to a not-previously-typed intranet hostname, | 452 // If the user is navigating to a not-previously-typed intranet hostname, |
452 // change the transition to TYPED so that the omnibox will learn that this is | 453 // change the transition to TYPED so that the omnibox will learn that this is |
453 // a known host. | 454 // a known host. |
454 bool has_redirects = request->redirects.size() > 1; | 455 bool has_redirects = request.redirects.size() > 1; |
455 if (content::PageTransitionIsMainFrame(request->transition) && | 456 if (content::PageTransitionIsMainFrame(request_transition) && |
456 (transition != content::PAGE_TRANSITION_TYPED) && !is_keyword_generated) { | 457 (stripped_transition != content::PAGE_TRANSITION_TYPED) && |
| 458 !is_keyword_generated) { |
457 const GURL& origin_url(has_redirects ? | 459 const GURL& origin_url(has_redirects ? |
458 request->redirects[0] : request->url); | 460 request.redirects[0] : request.url); |
459 if (origin_url.SchemeIs(chrome::kHttpScheme) || | 461 if (origin_url.SchemeIs(chrome::kHttpScheme) || |
460 origin_url.SchemeIs(chrome::kHttpsScheme) || | 462 origin_url.SchemeIs(chrome::kHttpsScheme) || |
461 origin_url.SchemeIs(chrome::kFtpScheme)) { | 463 origin_url.SchemeIs(chrome::kFtpScheme)) { |
462 std::string host(origin_url.host()); | 464 std::string host(origin_url.host()); |
463 if ((net::RegistryControlledDomainService::GetRegistryLength( | 465 if ((net::RegistryControlledDomainService::GetRegistryLength( |
464 host, false) == 0) && !db_->IsTypedHost(host)) { | 466 host, false) == 0) && !db_->IsTypedHost(host)) { |
465 transition = content::PAGE_TRANSITION_TYPED; | 467 stripped_transition = content::PAGE_TRANSITION_TYPED; |
466 request->transition = content::PageTransitionFromInt(transition | | 468 request_transition = |
467 content::PageTransitionGetQualifier(request->transition)); | 469 content::PageTransitionFromInt( |
| 470 stripped_transition | |
| 471 content::PageTransitionGetQualifier(request_transition)); |
468 } | 472 } |
469 } | 473 } |
470 } | 474 } |
471 | 475 |
472 if (!has_redirects) { | 476 if (!has_redirects) { |
473 // The single entry is both a chain start and end. | 477 // The single entry is both a chain start and end. |
474 content::PageTransition t = content::PageTransitionFromInt( | 478 content::PageTransition t = content::PageTransitionFromInt( |
475 request->transition | | 479 request_transition | |
476 content::PAGE_TRANSITION_CHAIN_START | | 480 content::PAGE_TRANSITION_CHAIN_START | |
477 content::PAGE_TRANSITION_CHAIN_END); | 481 content::PAGE_TRANSITION_CHAIN_END); |
478 | 482 |
479 // No redirect case (one element means just the page itself). | 483 // No redirect case (one element means just the page itself). |
480 last_ids = AddPageVisit(request->url, last_recorded_time_, | 484 last_ids = AddPageVisit(request.url, last_recorded_time_, |
481 last_ids.second, t, request->visit_source); | 485 last_ids.second, t, request.visit_source); |
482 | 486 |
483 // Update the segment for this visit. KEYWORD_GENERATED visits should not | 487 // Update the segment for this visit. KEYWORD_GENERATED visits should not |
484 // result in changing most visited, so we don't update segments (most | 488 // result in changing most visited, so we don't update segments (most |
485 // visited db). | 489 // visited db). |
486 if (!is_keyword_generated) { | 490 if (!is_keyword_generated) { |
487 UpdateSegments(request->url, from_visit_id, last_ids.second, t, | 491 UpdateSegments(request.url, from_visit_id, last_ids.second, t, |
488 last_recorded_time_); | 492 last_recorded_time_); |
489 | 493 |
490 // Update the referrer's duration. | 494 // Update the referrer's duration. |
491 UpdateVisitDuration(from_visit_id, last_recorded_time_); | 495 UpdateVisitDuration(from_visit_id, last_recorded_time_); |
492 } | 496 } |
493 } else { | 497 } else { |
494 // Redirect case. Add the redirect chain. | 498 // Redirect case. Add the redirect chain. |
495 | 499 |
496 content::PageTransition redirect_info = | 500 content::PageTransition redirect_info = |
497 content::PAGE_TRANSITION_CHAIN_START; | 501 content::PAGE_TRANSITION_CHAIN_START; |
498 | 502 |
499 if (request->redirects[0].SchemeIs(chrome::kAboutScheme)) { | 503 RedirectList redirects = request.redirects; |
| 504 if (redirects[0].SchemeIs(chrome::kAboutScheme)) { |
500 // When the redirect source + referrer is "about" we skip it. This | 505 // When the redirect source + referrer is "about" we skip it. This |
501 // happens when a page opens a new frame/window to about:blank and then | 506 // happens when a page opens a new frame/window to about:blank and then |
502 // script sets the URL to somewhere else (used to hide the referrer). It | 507 // script sets the URL to somewhere else (used to hide the referrer). It |
503 // would be nice to keep all these redirects properly but we don't ever | 508 // would be nice to keep all these redirects properly but we don't ever |
504 // see the initial about:blank load, so we don't know where the | 509 // see the initial about:blank load, so we don't know where the |
505 // subsequent client redirect came from. | 510 // subsequent client redirect came from. |
506 // | 511 // |
507 // In this case, we just don't bother hooking up the source of the | 512 // In this case, we just don't bother hooking up the source of the |
508 // redirects, so we remove it. | 513 // redirects, so we remove it. |
509 request->redirects.erase(request->redirects.begin()); | 514 redirects.erase(redirects.begin()); |
510 } else if (request->transition & content::PAGE_TRANSITION_CLIENT_REDIRECT) { | 515 } else if (request_transition & content::PAGE_TRANSITION_CLIENT_REDIRECT) { |
511 redirect_info = content::PAGE_TRANSITION_CLIENT_REDIRECT; | 516 redirect_info = content::PAGE_TRANSITION_CLIENT_REDIRECT; |
512 // The first entry in the redirect chain initiated a client redirect. | 517 // The first entry in the redirect chain initiated a client redirect. |
513 // We don't add this to the database since the referrer is already | 518 // We don't add this to the database since the referrer is already |
514 // there, so we skip over it but change the transition type of the first | 519 // there, so we skip over it but change the transition type of the first |
515 // transition to client redirect. | 520 // transition to client redirect. |
516 // | 521 // |
517 // The referrer is invalid when restoring a session that features an | 522 // The referrer is invalid when restoring a session that features an |
518 // https tab that redirects to a different host or to http. In this | 523 // https tab that redirects to a different host or to http. In this |
519 // case we don't need to reconnect the new redirect with the existing | 524 // case we don't need to reconnect the new redirect with the existing |
520 // chain. | 525 // chain. |
521 if (request->referrer.is_valid()) { | 526 if (request.referrer.is_valid()) { |
522 DCHECK(request->referrer == request->redirects[0]); | 527 DCHECK(request.referrer == redirects[0]); |
523 request->redirects.erase(request->redirects.begin()); | 528 redirects.erase(redirects.begin()); |
524 | 529 |
525 // If the navigation entry for this visit has replaced that for the | 530 // If the navigation entry for this visit has replaced that for the |
526 // first visit, remove the CHAIN_END marker from the first visit. This | 531 // first visit, remove the CHAIN_END marker from the first visit. This |
527 // can be called a lot, for example, the page cycler, and most of the | 532 // can be called a lot, for example, the page cycler, and most of the |
528 // time we won't have changed anything. | 533 // time we won't have changed anything. |
529 VisitRow visit_row; | 534 VisitRow visit_row; |
530 if (request->did_replace_entry && | 535 if (request.did_replace_entry && |
531 db_->GetRowForVisit(last_ids.second, &visit_row) && | 536 db_->GetRowForVisit(last_ids.second, &visit_row) && |
532 visit_row.transition & content::PAGE_TRANSITION_CHAIN_END) { | 537 visit_row.transition & content::PAGE_TRANSITION_CHAIN_END) { |
533 visit_row.transition = content::PageTransitionFromInt( | 538 visit_row.transition = content::PageTransitionFromInt( |
534 visit_row.transition & ~content::PAGE_TRANSITION_CHAIN_END); | 539 visit_row.transition & ~content::PAGE_TRANSITION_CHAIN_END); |
535 db_->UpdateVisitRow(visit_row); | 540 db_->UpdateVisitRow(visit_row); |
536 } | 541 } |
537 } | 542 } |
538 } | 543 } |
539 | 544 |
540 for (size_t redirect_index = 0; redirect_index < request->redirects.size(); | 545 for (size_t redirect_index = 0; redirect_index < redirects.size(); |
541 redirect_index++) { | 546 redirect_index++) { |
542 content::PageTransition t = | 547 content::PageTransition t = |
543 content::PageTransitionFromInt(transition | redirect_info); | 548 content::PageTransitionFromInt(stripped_transition | redirect_info); |
544 | 549 |
545 // If this is the last transition, add a CHAIN_END marker | 550 // If this is the last transition, add a CHAIN_END marker |
546 if (redirect_index == (request->redirects.size() - 1)) { | 551 if (redirect_index == (redirects.size() - 1)) { |
547 t = content::PageTransitionFromInt( | 552 t = content::PageTransitionFromInt( |
548 t | content::PAGE_TRANSITION_CHAIN_END); | 553 t | content::PAGE_TRANSITION_CHAIN_END); |
549 } | 554 } |
550 | 555 |
551 // Record all redirect visits with the same timestamp. We don't display | 556 // Record all redirect visits with the same timestamp. We don't display |
552 // them anyway, and if we ever decide to, we can reconstruct their order | 557 // them anyway, and if we ever decide to, we can reconstruct their order |
553 // from the redirect chain. | 558 // from the redirect chain. |
554 last_ids = AddPageVisit(request->redirects[redirect_index], | 559 last_ids = AddPageVisit(redirects[redirect_index], |
555 last_recorded_time_, last_ids.second, | 560 last_recorded_time_, last_ids.second, |
556 t, request->visit_source); | 561 t, request.visit_source); |
557 if (t & content::PAGE_TRANSITION_CHAIN_START) { | 562 if (t & content::PAGE_TRANSITION_CHAIN_START) { |
558 // Update the segment for this visit. | 563 // Update the segment for this visit. |
559 UpdateSegments(request->redirects[redirect_index], | 564 UpdateSegments(redirects[redirect_index], |
560 from_visit_id, last_ids.second, t, last_recorded_time_); | 565 from_visit_id, last_ids.second, t, last_recorded_time_); |
561 | 566 |
562 // Update the visit_details for this visit. | 567 // Update the visit_details for this visit. |
563 UpdateVisitDuration(from_visit_id, last_recorded_time_); | 568 UpdateVisitDuration(from_visit_id, last_recorded_time_); |
564 } | 569 } |
565 | 570 |
566 // Subsequent transitions in the redirect list must all be server | 571 // Subsequent transitions in the redirect list must all be server |
567 // redirects. | 572 // redirects. |
568 redirect_info = content::PAGE_TRANSITION_SERVER_REDIRECT; | 573 redirect_info = content::PAGE_TRANSITION_SERVER_REDIRECT; |
569 } | 574 } |
570 | 575 |
571 // Last, save this redirect chain for later so we can set titles & favicons | 576 // Last, save this redirect chain for later so we can set titles & favicons |
572 // on the redirected pages properly. It is indexed by the destination page. | 577 // on the redirected pages properly. It is indexed by the destination page. |
573 recent_redirects_.Put(request->url, request->redirects); | 578 recent_redirects_.Put(request.url, redirects); |
574 } | 579 } |
575 | 580 |
576 // TODO(brettw) bug 1140015: Add an "add page" notification so the history | 581 // TODO(brettw) bug 1140015: Add an "add page" notification so the history |
577 // views can keep in sync. | 582 // views can keep in sync. |
578 | 583 |
579 // Add the last visit to the tracker so we can get outgoing transitions. | 584 // Add the last visit to the tracker so we can get outgoing transitions. |
580 // TODO(evanm): Due to http://b/1194536 we lose the referrers of a subframe | 585 // TODO(evanm): Due to http://b/1194536 we lose the referrers of a subframe |
581 // navigation anyway, so last_visit_id is always zero for them. But adding | 586 // navigation anyway, so last_visit_id is always zero for them. But adding |
582 // them here confuses main frame history, so we skip them for now. | 587 // them here confuses main frame history, so we skip them for now. |
583 if (transition != content::PAGE_TRANSITION_AUTO_SUBFRAME && | 588 if (stripped_transition != content::PAGE_TRANSITION_AUTO_SUBFRAME && |
584 transition != content::PAGE_TRANSITION_MANUAL_SUBFRAME && | 589 stripped_transition != content::PAGE_TRANSITION_MANUAL_SUBFRAME && |
585 !is_keyword_generated) { | 590 !is_keyword_generated) { |
586 tracker_.AddVisit(request->id_scope, request->page_id, request->url, | 591 tracker_.AddVisit(request.id_scope, request.page_id, request.url, |
587 last_ids.second); | 592 last_ids.second); |
588 } | 593 } |
589 | 594 |
590 if (text_database_.get()) { | 595 if (text_database_.get()) { |
591 text_database_->AddPageURL(request->url, last_ids.first, last_ids.second, | 596 text_database_->AddPageURL(request.url, last_ids.first, last_ids.second, |
592 last_recorded_time_); | 597 last_recorded_time_); |
593 } | 598 } |
594 | 599 |
595 ScheduleCommit(); | 600 ScheduleCommit(); |
596 } | 601 } |
597 | 602 |
598 void HistoryBackend::InitImpl(const std::string& languages) { | 603 void HistoryBackend::InitImpl(const std::string& languages) { |
599 DCHECK(!db_.get()) << "Initializing HistoryBackend twice"; | 604 DCHECK(!db_.get()) << "Initializing HistoryBackend twice"; |
600 // In the rare case where the db fails to initialize a dialog may get shown | 605 // In the rare case where the db fails to initialize a dialog may get shown |
601 // the blocks the caller, yet allows other messages through. For this reason | 606 // the blocks the caller, yet allows other messages through. For this reason |
(...skipping 2263 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2865 info.url_id = visit.url_id; | 2870 info.url_id = visit.url_id; |
2866 info.time = visit.visit_time; | 2871 info.time = visit.visit_time; |
2867 info.transition = visit.transition; | 2872 info.transition = visit.transition; |
2868 // If we don't have a delegate yet during setup or shutdown, we will drop | 2873 // If we don't have a delegate yet during setup or shutdown, we will drop |
2869 // these notifications. | 2874 // these notifications. |
2870 if (delegate_.get()) | 2875 if (delegate_.get()) |
2871 delegate_->NotifyVisitDBObserversOnAddVisit(info); | 2876 delegate_->NotifyVisitDBObserversOnAddVisit(info); |
2872 } | 2877 } |
2873 | 2878 |
2874 } // namespace history | 2879 } // namespace history |
OLD | NEW |