history_tab_helper.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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/browser/ui/search/instant_overlay.h"
16#include "chrome/common/render_messages.h"
17#include "content/public/browser/navigation_details.h"
18#include "content/public/browser/navigation_entry.h"
19#include "content/public/browser/notification_details.h"
20#include "content/public/browser/notification_source.h"
21#include "content/public/browser/notification_types.h"
22#include "content/public/browser/web_contents.h"
23#include "content/public/browser/web_contents_delegate.h"
24#include "content/public/common/frame_navigate_params.h"
25
26#if !defined(OS_ANDROID)
27#include "chrome/browser/ui/browser.h"
28#include "chrome/browser/ui/browser_finder.h"
29#endif
30
31using content::NavigationEntry;
32using content::WebContents;
33
34DEFINE_WEB_CONTENTS_USER_DATA_KEY(HistoryTabHelper);
35
36HistoryTabHelper::HistoryTabHelper(WebContents* web_contents)
37    : content::WebContentsObserver(web_contents),
38      received_page_title_(false) {
39  registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED,
40                 content::Source<WebContents>(web_contents));
41}
42
43HistoryTabHelper::~HistoryTabHelper() {
44}
45
46void HistoryTabHelper::UpdateHistoryForNavigation(
47    const history::HistoryAddPageArgs& add_page_args) {
48  HistoryService* hs = GetHistoryService();
49  if (hs)
50    GetHistoryService()->AddPage(add_page_args);
51}
52
53void HistoryTabHelper::UpdateHistoryPageTitle(const NavigationEntry& entry) {
54  HistoryService* hs = GetHistoryService();
55  if (hs)
56    hs->SetPageTitle(entry.GetVirtualURL(),
57                     entry.GetTitleForDisplay(std::string()));
58}
59
60history::HistoryAddPageArgs
61HistoryTabHelper::CreateHistoryAddPageArgs(
62    const GURL& virtual_url,
63    base::Time timestamp,
64    bool did_replace_entry,
65    const content::FrameNavigateParams& params) {
66  history::HistoryAddPageArgs add_page_args(
67      params.url, timestamp, web_contents(), params.page_id,
68      params.referrer.url, params.redirects, params.transition,
69      history::SOURCE_BROWSED, did_replace_entry);
70  if (content::PageTransitionIsMainFrame(params.transition) &&
71      virtual_url != params.url) {
72    // Hack on the "virtual" URL so that it will appear in history. For some
73    // types of URLs, we will display a magic URL that is different from where
74    // the page is actually navigated. We want the user to see in history what
75    // they saw in the URL bar, so we add the virtual URL as a redirect.  This
76    // only applies to the main frame, as the virtual URL doesn't apply to
77    // sub-frames.
78    add_page_args.url = virtual_url;
79    if (!add_page_args.redirects.empty())
80      add_page_args.redirects.back() = virtual_url;
81  }
82  return add_page_args;
83}
84
85bool HistoryTabHelper::OnMessageReceived(const IPC::Message& message) {
86  bool handled = true;
87  IPC_BEGIN_MESSAGE_MAP(HistoryTabHelper, message)
88    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_PageContents, OnPageContents)
89    IPC_MESSAGE_UNHANDLED(handled = false)
90  IPC_END_MESSAGE_MAP()
91
92  return handled;
93}
94
95void HistoryTabHelper::DidNavigateMainFrame(
96    const content::LoadCommittedDetails& details,
97    const content::FrameNavigateParams& params) {
98  // Allow the new page to set the title again.
99  received_page_title_ = false;
100}
101
102void HistoryTabHelper::DidNavigateAnyFrame(
103    const content::LoadCommittedDetails& details,
104    const content::FrameNavigateParams& params) {
105  // Update history. Note that this needs to happen after the entry is complete,
106  // which WillNavigate[Main,Sub]Frame will do before this function is called.
107  if (!params.should_update_history)
108    return;
109
110  // Most of the time, the displayURL matches the loaded URL, but for about:
111  // URLs, we use a data: URL as the real value.  We actually want to save the
112  // about: URL to the history db and keep the data: URL hidden. This is what
113  // the WebContents' URL getter does.
114  const history::HistoryAddPageArgs& add_page_args =
115      CreateHistoryAddPageArgs(
116          web_contents()->GetURL(), details.entry->GetTimestamp(),
117          details.did_replace_entry, params);
118
119  prerender::PrerenderManager* prerender_manager =
120      prerender::PrerenderManagerFactory::GetForProfile(
121          Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
122  if (prerender_manager) {
123    prerender::PrerenderContents* prerender_contents =
124        prerender_manager->GetPrerenderContents(web_contents());
125    if (prerender_contents) {
126      prerender_contents->DidNavigate(add_page_args);
127      return;
128    }
129  }
130
131  InstantOverlay* instant_overlay =
132      InstantOverlay::FromWebContents(web_contents());
133  if (instant_overlay) {
134    instant_overlay->DidNavigate(add_page_args);
135    return;
136  }
137
138#if !defined(OS_ANDROID)
139  // Don't update history if this web contents isn't associatd with a tab.
140  Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
141  if (!browser || browser->is_app())
142    return;
143#endif
144
145  UpdateHistoryForNavigation(add_page_args);
146}
147
148void HistoryTabHelper::Observe(int type,
149                               const content::NotificationSource& source,
150                               const content::NotificationDetails& details) {
151  DCHECK(type == content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED);
152  std::pair<content::NavigationEntry*, bool>* title =
153      content::Details<std::pair<content::NavigationEntry*, bool> >(
154          details).ptr();
155
156  if (received_page_title_)
157    return;
158
159  if (title->first) {
160    UpdateHistoryPageTitle(*title->first);
161    received_page_title_ = title->second;
162  }
163}
164
165void HistoryTabHelper::OnPageContents(const GURL& url,
166                                      int32 page_id,
167                                      const string16& contents) {
168  // Don't index any https pages. People generally don't want their bank
169  // accounts, etc. indexed on their computer, especially since some of these
170  // things are not marked cachable.
171  // TODO(brettw) we may want to consider more elaborate heuristics such as
172  // the cachability of the page. We may also want to consider subframes (this
173  // test will still index subframes if the subframe is SSL).
174  // TODO(zelidrag) bug chromium-os:2808 - figure out if we want to reenable
175  // content indexing for chromeos in some future releases.
176#if !defined(OS_CHROMEOS)
177  if (!url.SchemeIsSecure()) {
178    HistoryService* hs = GetHistoryService();
179    if (hs)
180      hs->SetPageContents(url, contents);
181  }
182#endif
183}
184
185HistoryService* HistoryTabHelper::GetHistoryService() {
186  Profile* profile =
187      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
188  if (profile->IsOffTheRecord())
189    return NULL;
190
191  return HistoryServiceFactory::GetForProfile(profile,
192                                              Profile::IMPLICIT_ACCESS);
193}
194
195void HistoryTabHelper::WebContentsDestroyed(WebContents* tab) {
196  // TODO(sky): nuke this since no one is using visit_duration (and this is all
197  // wrong).
198
199  // We update the history for this URL.
200  // The content returned from web_contents() has been destroyed by now.
201  // We need to use tab value directly.
202  Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
203  if (profile->IsOffTheRecord())
204    return;
205
206  HistoryService* hs =
207      HistoryServiceFactory::GetForProfile(profile, Profile::IMPLICIT_ACCESS);
208  if (hs) {
209    NavigationEntry* entry = tab->GetController().GetLastCommittedEntry();
210    if (entry) {
211      hs->UpdateWithPageEndTime(tab, entry->GetPageID(), tab->GetURL(),
212                                base::Time::Now());
213    }
214  }
215}
216