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
3// found in the LICENSE file.
4
5#include "chrome/browser/history/history_tab_helper.h"
6
7#include <utility>
8
9#include "chrome/browser/history/history_service.h"
10#include "chrome/browser/history/history_service_factory.h"
11#include "chrome/browser/prerender/prerender_contents.h"
12#include "chrome/browser/prerender/prerender_manager.h"
13#include "chrome/browser/prerender/prerender_manager_factory.h"
14#include "chrome/browser/profiles/profile.h"
15#include "chrome/common/render_messages.h"
16#include "content/public/browser/navigation_details.h"
17#include "content/public/browser/navigation_entry.h"
18#include "content/public/browser/web_contents.h"
19#include "content/public/browser/web_contents_delegate.h"
20#include "content/public/common/frame_navigate_params.h"
21
22#if !defined(OS_ANDROID)
23#include "chrome/browser/ui/browser.h"
24#include "chrome/browser/ui/browser_finder.h"
25#endif
26
27using content::NavigationEntry;
28using content::WebContents;
29
30DEFINE_WEB_CONTENTS_USER_DATA_KEY(HistoryTabHelper);
31
32HistoryTabHelper::HistoryTabHelper(WebContents* web_contents)
33    : content::WebContentsObserver(web_contents),
34      received_page_title_(false) {
35}
36
37HistoryTabHelper::~HistoryTabHelper() {
38}
39
40void HistoryTabHelper::UpdateHistoryForNavigation(
41    const history::HistoryAddPageArgs& add_page_args) {
42  HistoryService* hs = GetHistoryService();
43  if (hs)
44    GetHistoryService()->AddPage(add_page_args);
45}
46
47void HistoryTabHelper::UpdateHistoryPageTitle(const NavigationEntry& entry) {
48  HistoryService* hs = GetHistoryService();
49  if (hs)
50    hs->SetPageTitle(entry.GetVirtualURL(),
51                     entry.GetTitleForDisplay(std::string()));
52}
53
54history::HistoryAddPageArgs
55HistoryTabHelper::CreateHistoryAddPageArgs(
56    const GURL& virtual_url,
57    base::Time timestamp,
58    bool did_replace_entry,
59    const content::FrameNavigateParams& params) {
60  history::HistoryAddPageArgs add_page_args(
61      params.url, timestamp, web_contents(), params.page_id,
62      params.referrer.url, params.redirects, params.transition,
63      history::SOURCE_BROWSED, did_replace_entry);
64  if (ui::PageTransitionIsMainFrame(params.transition) &&
65      virtual_url != params.url) {
66    // Hack on the "virtual" URL so that it will appear in history. For some
67    // types of URLs, we will display a magic URL that is different from where
68    // the page is actually navigated. We want the user to see in history what
69    // they saw in the URL bar, so we add the virtual URL as a redirect.  This
70    // only applies to the main frame, as the virtual URL doesn't apply to
71    // sub-frames.
72    add_page_args.url = virtual_url;
73    if (!add_page_args.redirects.empty())
74      add_page_args.redirects.back() = virtual_url;
75  }
76  return add_page_args;
77}
78
79void HistoryTabHelper::DidNavigateMainFrame(
80    const content::LoadCommittedDetails& details,
81    const content::FrameNavigateParams& params) {
82  // Allow the new page to set the title again.
83  received_page_title_ = false;
84}
85
86void HistoryTabHelper::DidNavigateAnyFrame(
87    const content::LoadCommittedDetails& details,
88    const content::FrameNavigateParams& params) {
89  // Update history. Note that this needs to happen after the entry is complete,
90  // which WillNavigate[Main,Sub]Frame will do before this function is called.
91  if (!params.should_update_history)
92    return;
93
94  // Most of the time, the displayURL matches the loaded URL, but for about:
95  // URLs, we use a data: URL as the real value.  We actually want to save the
96  // about: URL to the history db and keep the data: URL hidden. This is what
97  // the WebContents' URL getter does.
98  const history::HistoryAddPageArgs& add_page_args =
99      CreateHistoryAddPageArgs(
100          web_contents()->GetURL(), details.entry->GetTimestamp(),
101          details.did_replace_entry, params);
102
103  prerender::PrerenderManager* prerender_manager =
104      prerender::PrerenderManagerFactory::GetForProfile(
105          Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
106  if (prerender_manager) {
107    prerender::PrerenderContents* prerender_contents =
108        prerender_manager->GetPrerenderContents(web_contents());
109    if (prerender_contents) {
110      prerender_contents->DidNavigate(add_page_args);
111      return;
112    }
113  }
114
115#if !defined(OS_ANDROID)
116  // Don't update history if this web contents isn't associatd with a tab.
117  Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
118  if (!browser || browser->is_app())
119    return;
120#endif
121
122  UpdateHistoryForNavigation(add_page_args);
123}
124
125void HistoryTabHelper::TitleWasSet(NavigationEntry* entry, bool explicit_set) {
126  if (received_page_title_)
127    return;
128
129  if (entry) {
130    UpdateHistoryPageTitle(*entry);
131    received_page_title_ = explicit_set;
132  }
133}
134
135HistoryService* HistoryTabHelper::GetHistoryService() {
136  Profile* profile =
137      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
138  if (profile->IsOffTheRecord())
139    return NULL;
140
141  return HistoryServiceFactory::GetForProfile(profile,
142                                              Profile::IMPLICIT_ACCESS);
143}
144
145void HistoryTabHelper::WebContentsDestroyed() {
146  // We update the history for this URL.
147  WebContents* tab = web_contents();
148  Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
149  if (profile->IsOffTheRecord())
150    return;
151
152  HistoryService* hs =
153      HistoryServiceFactory::GetForProfile(profile, Profile::IMPLICIT_ACCESS);
154  if (hs) {
155    NavigationEntry* entry = tab->GetController().GetLastCommittedEntry();
156    if (entry) {
157      hs->UpdateWithPageEndTime(tab, entry->GetPageID(), tab->GetURL(),
158                                base::Time::Now());
159    }
160    hs->ClearCachedDataForContextID(tab);
161  }
162}
163