| 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 |