1// Copyright 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/ui/search/instant_loader.h"
6
7#include "chrome/browser/content_settings/tab_specific_content_settings.h"
8#include "chrome/browser/extensions/api/web_navigation/web_navigation_api.h"
9#include "chrome/browser/favicon/favicon_tab_helper.h"
10#include "chrome/browser/safe_browsing/safe_browsing_tab_observer.h"
11#include "chrome/browser/search/search.h"
12#include "chrome/browser/tab_contents/tab_util.h"
13#include "chrome/browser/ui/blocked_content/blocked_content_tab_helper.h"
14#include "chrome/browser/ui/bookmarks/bookmark_tab_helper.h"
15#include "chrome/browser/ui/search/search_tab_helper.h"
16#include "chrome/browser/ui/tab_contents/core_tab_helper.h"
17#include "content/public/browser/navigation_entry.h"
18#include "content/public/browser/notification_source.h"
19#include "content/public/browser/notification_types.h"
20#include "content/public/browser/site_instance.h"
21#include "content/public/browser/web_contents_view.h"
22#include "grit/generated_resources.h"
23#include "ui/base/l10n/l10n_util.h"
24
25#if !defined(OS_ANDROID)
26#include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h"
27#endif
28
29namespace {
30
31// This HTTP header and value are set on loads that originate from Instant.
32const char kInstantHeader[] = "X-Purpose: Instant";
33
34}  // namespace
35
36InstantLoader::Delegate::~Delegate() {
37}
38
39InstantLoader::InstantLoader(Delegate* delegate)
40    : delegate_(delegate), stale_page_timer_(false, false) {}
41
42InstantLoader::~InstantLoader() {
43}
44
45void InstantLoader::Init(const GURL& instant_url,
46                         Profile* profile,
47                         const base::Closure& on_stale_callback) {
48  content::WebContents::CreateParams create_params(profile);
49  create_params.site_instance = content::SiteInstance::CreateForURL(
50      profile, instant_url);
51  SetContents(scoped_ptr<content::WebContents>(
52      content::WebContents::Create(create_params)));
53  instant_url_ = instant_url;
54  on_stale_callback_ = on_stale_callback;
55}
56
57void InstantLoader::Load() {
58  DVLOG(1) << "LoadURL: " << instant_url_;
59  contents_->GetController().LoadURL(
60      instant_url_, content::Referrer(),
61      content::PAGE_TRANSITION_GENERATED, kInstantHeader);
62
63  // Explicitly set the new tab title and virtual URL.
64  //
65  // This ensures that the title is set even before we get a title from the
66  // page, preventing a potential flicker of the URL, and also ensures that
67  // (unless overridden by the page) the new tab title matches the browser UI
68  // locale.
69  content::NavigationEntry* entry = contents_->GetController().GetActiveEntry();
70  if (entry)
71    entry->SetTitle(l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE));
72
73  contents_->WasHidden();
74
75  int staleness_timeout_ms = chrome::GetInstantLoaderStalenessTimeoutSec() *
76      1000;
77  if (staleness_timeout_ms > 0) {
78    stale_page_timer_.Start(
79        FROM_HERE,
80        base::TimeDelta::FromMilliseconds(staleness_timeout_ms),
81        on_stale_callback_);
82  }
83}
84
85void InstantLoader::SetContents(scoped_ptr<content::WebContents> new_contents) {
86  contents_.reset(new_contents.release());
87  contents_->SetDelegate(this);
88
89  // Set up various tab helpers. The rest will get attached when (if) the
90  // contents is added to the tab strip.
91
92  // Tab helpers to control popups.
93  BlockedContentTabHelper::CreateForWebContents(contents());
94  BlockedContentTabHelper::FromWebContents(contents())->
95      SetAllContentsBlocked(true);
96  TabSpecificContentSettings::CreateForWebContents(contents());
97  TabSpecificContentSettings::FromWebContents(contents())->
98      SetPopupsBlocked(true);
99
100  // Bookmarks (Users can bookmark the Instant NTP. This ensures the bookmarked
101  // state is correctly set when the contents are swapped into a tab.)
102  BookmarkTabHelper::CreateForWebContents(contents());
103
104  // A tab helper to catch prerender content swapping shenanigans.
105  CoreTabHelper::CreateForWebContents(contents());
106  CoreTabHelper::FromWebContents(contents())->set_delegate(this);
107
108  SearchTabHelper::CreateForWebContents(contents());
109
110#if !defined(OS_ANDROID)
111  // Observers.
112  extensions::WebNavigationTabObserver::CreateForWebContents(contents());
113#endif  // OS_ANDROID
114
115  // Favicons, required by the Task Manager.
116  FaviconTabHelper::CreateForWebContents(contents());
117
118  // And some flat-out paranoia.
119  safe_browsing::SafeBrowsingTabObserver::CreateForWebContents(contents());
120
121  // When the WebContents finishes loading it should be checked to ensure that
122  // it is in the instant process.
123  registrar_.Add(this, content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
124                 content::Source<content::WebContents>(contents_.get()));
125}
126
127scoped_ptr<content::WebContents> InstantLoader::ReleaseContents() {
128  stale_page_timer_.Stop();
129  contents_->SetDelegate(NULL);
130
131  // Undo tab helper work done in SetContents().
132
133  BlockedContentTabHelper::FromWebContents(contents())->
134      SetAllContentsBlocked(false);
135  TabSpecificContentSettings::FromWebContents(contents())->
136      SetPopupsBlocked(false);
137#if !defined(OS_ANDROID)
138  PopupBlockerTabHelper* popup_helper =
139      PopupBlockerTabHelper::FromWebContents(contents());
140  if (popup_helper) {
141    TabSpecificContentSettings::FromWebContents(contents())
142        ->SetPopupsBlocked(!!popup_helper->GetBlockedPopupsCount());
143  }
144#endif
145
146  CoreTabHelper::FromWebContents(contents())->set_delegate(NULL);
147
148  registrar_.Remove(this, content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
149                    content::Source<content::WebContents>(contents_.get()));
150  return contents_.Pass();
151}
152
153void InstantLoader::Observe(int type,
154                            const content::NotificationSource& source,
155                            const content::NotificationDetails& details) {
156  DCHECK_EQ(type, content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME);
157  const content::WebContents* web_contents =
158      content::Source<content::WebContents>(source).ptr();
159  DCHECK_EQ(contents_.get(), web_contents);
160  delegate_->LoadCompletedMainFrame();
161}
162
163void InstantLoader::SwapTabContents(content::WebContents* old_contents,
164                                    content::WebContents* new_contents) {
165  DCHECK_EQ(old_contents, contents());
166  // We release here without deleting since the caller has the responsibility
167  // for deleting the old WebContents.
168  ignore_result(ReleaseContents().release());
169  SetContents(scoped_ptr<content::WebContents>(new_contents));
170  delegate_->OnSwappedContents();
171}
172
173bool InstantLoader::ShouldSuppressDialogs() {
174  // Messages shown during Instant cancel Instant, so we suppress them.
175  return true;
176}
177
178bool InstantLoader::ShouldFocusPageAfterCrash() {
179  return false;
180}
181
182void InstantLoader::CanDownload(content::RenderViewHost* /* render_view_host */,
183                                int /* request_id */,
184                                const std::string& /* request_method */,
185                                const base::Callback<void(bool)>& callback) {
186  // Downloads are disabled.
187  callback.Run(false);
188}
189
190bool InstantLoader::OnGoToEntryOffset(int /* offset */) {
191  return false;
192}
193
194content::WebContents* InstantLoader::OpenURLFromTab(
195    content::WebContents* source,
196    const content::OpenURLParams& params) {
197  return delegate_->OpenURLFromTab(source, params);
198}
199