• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 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_ntp_prerenderer.h"
6 
7 #include "base/basictypes.h"
8 #include "base/bind.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/prefs/pref_service.h"
11 #include "build/build_config.h"
12 #include "chrome/browser/chrome_notification_types.h"
13 #include "chrome/browser/content_settings/content_settings_provider.h"
14 #include "chrome/browser/content_settings/host_content_settings_map.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/search/instant_service.h"
17 #include "chrome/browser/search/instant_service_factory.h"
18 #include "chrome/browser/search/search.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/browser/ui/browser_finder.h"
21 #include "chrome/browser/ui/host_desktop.h"
22 #include "chrome/browser/ui/search/instant_ntp.h"
23 #include "chrome/browser/ui/tabs/tab_strip_model.h"
24 #include "chrome/common/content_settings.h"
25 #include "chrome/common/pref_names.h"
26 #include "chrome/common/search_urls.h"
27 #include "content/public/browser/notification_service.h"
28 #include "content/public/browser/render_process_host.h"
29 #include "content/public/browser/web_contents.h"
30 #include "net/base/network_change_notifier.h"
31 
32 namespace {
33 
DeleteNTPSoon(scoped_ptr<InstantNTP> ntp)34 void DeleteNTPSoon(scoped_ptr<InstantNTP> ntp) {
35   if (!ntp)
36     return;
37 
38   if (ntp->contents()) {
39     base::MessageLoop::current()->DeleteSoon(
40         FROM_HERE, ntp->ReleaseContents().release());
41   }
42   base::MessageLoop::current()->DeleteSoon(FROM_HERE, ntp.release());
43 }
44 
45 }  // namespace
46 
47 
InstantNTPPrerenderer(Profile * profile,InstantService * instant_service,PrefService * prefs)48 InstantNTPPrerenderer::InstantNTPPrerenderer(Profile* profile,
49                                              InstantService* instant_service,
50                                              PrefService* prefs)
51     : profile_(profile) {
52   DCHECK(profile);
53 
54   // In unit tests, prefs may be NULL.
55   if (prefs) {
56     profile_pref_registrar_.Init(prefs);
57     profile_pref_registrar_.Add(
58         prefs::kSearchSuggestEnabled,
59         base::Bind(&InstantNTPPrerenderer::ReloadInstantNTP,
60                    base::Unretained(this)));
61   }
62   net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
63 
64   // Allow instant_service to be null for unit tets.
65   if (instant_service)
66     instant_service->AddObserver(this);
67 }
68 
~InstantNTPPrerenderer()69 InstantNTPPrerenderer::~InstantNTPPrerenderer() {
70   InstantService* instant_service =
71       InstantServiceFactory::GetForProfile(profile_);
72   if (instant_service)
73     instant_service->RemoveObserver(this);
74   net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
75 }
76 
ReloadInstantNTP()77 void InstantNTPPrerenderer::ReloadInstantNTP() {
78   ResetNTP(GetInstantURL());
79 }
80 
ReleaseNTPContents()81 scoped_ptr<content::WebContents> InstantNTPPrerenderer::ReleaseNTPContents() {
82   if (!profile_ || profile_->IsOffTheRecord() ||
83       !chrome::ShouldShowInstantNTP())
84     return scoped_ptr<content::WebContents>();
85 
86   if (ShouldSwitchToLocalNTP())
87     ResetNTP(GetLocalInstantURL());
88 
89   scoped_ptr<content::WebContents> ntp_contents = ntp_->ReleaseContents();
90 
91   // Preload a new InstantNTP.
92   ReloadInstantNTP();
93   return ntp_contents.Pass();
94 }
95 
GetNTPContents() const96 content::WebContents* InstantNTPPrerenderer::GetNTPContents() const {
97   return ntp() ? ntp()->contents() : NULL;
98 }
99 
DeleteNTPContents()100 void InstantNTPPrerenderer::DeleteNTPContents() {
101   if (ntp_)
102     ntp_.reset();
103 }
104 
RenderProcessGone()105 void InstantNTPPrerenderer::RenderProcessGone() {
106   DeleteNTPSoon(ntp_.Pass());
107 }
108 
LoadCompletedMainFrame()109 void InstantNTPPrerenderer::LoadCompletedMainFrame() {
110   if (!ntp_ || ntp_->supports_instant())
111     return;
112 
113   content::WebContents* ntp_contents = ntp_->contents();
114   DCHECK(ntp_contents);
115 
116   InstantService* instant_service =
117       InstantServiceFactory::GetForProfile(profile());
118   if (instant_service &&
119       instant_service->IsInstantProcess(
120           ntp_contents->GetRenderProcessHost()->GetID())) {
121     return;
122   }
123   InstantSupportDetermined(ntp_contents, false);
124 }
125 
GetLocalInstantURL() const126 std::string InstantNTPPrerenderer::GetLocalInstantURL() const {
127   return chrome::GetLocalInstantURL(profile_).spec();
128 }
129 
GetInstantURL() const130 std::string InstantNTPPrerenderer::GetInstantURL() const {
131   if (net::NetworkChangeNotifier::IsOffline())
132     return GetLocalInstantURL();
133 
134   // TODO(kmadhusu): Remove start margin param from chrome::GetInstantURL().
135   const GURL instant_url = chrome::GetInstantURL(profile_,
136                                                  chrome::kDisableStartMargin,
137                                                  false);
138   if (!instant_url.is_valid())
139     return GetLocalInstantURL();
140 
141   return instant_url.spec();
142 }
143 
IsJavascriptEnabled() const144 bool InstantNTPPrerenderer::IsJavascriptEnabled() const {
145   GURL instant_url(GetInstantURL());
146   GURL origin(instant_url.GetOrigin());
147   ContentSetting js_setting = profile_->GetHostContentSettingsMap()->
148       GetContentSetting(origin, origin, CONTENT_SETTINGS_TYPE_JAVASCRIPT,
149                         NO_RESOURCE_IDENTIFIER);
150   // Javascript can be disabled either in content settings or via a WebKit
151   // preference, so check both. Disabling it through the Settings page affects
152   // content settings. I'm not sure how to disable the WebKit preference, but
153   // it's theoretically possible some users have it off.
154   bool js_content_enabled =
155       js_setting == CONTENT_SETTING_DEFAULT ||
156       js_setting == CONTENT_SETTING_ALLOW;
157   bool js_webkit_enabled = profile_->GetPrefs()->GetBoolean(
158       prefs::kWebKitJavascriptEnabled);
159   return js_content_enabled && js_webkit_enabled;
160 }
161 
InStartup() const162 bool InstantNTPPrerenderer::InStartup() const {
163 #if !defined(OS_ANDROID)
164   // TODO(kmadhusu): This is not completely reliable. Find a better way to
165   // detect startup time.
166   Browser* browser = chrome::FindBrowserWithProfile(profile_,
167                                                     chrome::GetActiveDesktop());
168   return !browser || !browser->tab_strip_model()->GetActiveWebContents();
169 #endif
170   return false;
171 }
172 
ntp() const173 InstantNTP* InstantNTPPrerenderer::ntp() const {
174   return ntp_.get();
175 }
176 
OnNetworkChanged(net::NetworkChangeNotifier::ConnectionType type)177 void InstantNTPPrerenderer::OnNetworkChanged(
178     net::NetworkChangeNotifier::ConnectionType type) {
179   // Not interested in events conveying change to offline.
180   if (type == net::NetworkChangeNotifier::CONNECTION_NONE)
181     return;
182 
183   if (!ntp() || ntp()->IsLocal())
184     ReloadInstantNTP();
185 }
186 
InstantSupportDetermined(const content::WebContents * contents,bool supports_instant)187 void InstantNTPPrerenderer::InstantSupportDetermined(
188     const content::WebContents* contents,
189     bool supports_instant) {
190   DCHECK(ntp() && ntp()->contents() == contents);
191 
192   if (!supports_instant) {
193     bool is_local = ntp()->IsLocal();
194     DeleteNTPSoon(ntp_.Pass());
195     if (!is_local)
196       ResetNTP(GetLocalInstantURL());
197   }
198 
199   content::NotificationService::current()->Notify(
200       chrome::NOTIFICATION_INSTANT_NTP_SUPPORT_DETERMINED,
201       content::Source<InstantNTPPrerenderer>(this),
202       content::NotificationService::NoDetails());
203 }
204 
InstantPageAboutToNavigateMainFrame(const content::WebContents *,const GURL &)205 void InstantNTPPrerenderer::InstantPageAboutToNavigateMainFrame(
206     const content::WebContents* /* contents */,
207     const GURL& /* url */) {
208   NOTREACHED();
209 }
210 
InstantPageLoadFailed(content::WebContents * contents)211 void InstantNTPPrerenderer::InstantPageLoadFailed(
212     content::WebContents* contents) {
213   DCHECK(ntp() && ntp()->contents() == contents);
214 
215   bool is_local = ntp()->IsLocal();
216   DeleteNTPSoon(ntp_.Pass());
217   if (!is_local)
218     ResetNTP(GetLocalInstantURL());
219 }
220 
ResetNTP(const std::string & instant_url)221 void InstantNTPPrerenderer::ResetNTP(const std::string& instant_url) {
222   // Instant NTP is only used in extended mode so we should always have a
223   // non-empty URL to use.
224   DCHECK(!instant_url.empty());
225   if (!chrome::ShouldUseCacheableNTP()) {
226     ntp_.reset(new InstantNTP(this, instant_url, profile_));
227     ntp_->InitContents(base::Bind(&InstantNTPPrerenderer::ReloadInstantNTP,
228                                   base::Unretained(this)));
229   }
230 }
231 
PageIsCurrent() const232 bool InstantNTPPrerenderer::PageIsCurrent() const {
233   const std::string& instant_url = GetInstantURL();
234   if (instant_url.empty() ||
235       !search::MatchesOriginAndPath(GURL(ntp()->instant_url()),
236                                     GURL(instant_url)))
237     return false;
238 
239   return ntp()->supports_instant();
240 }
241 
ShouldSwitchToLocalNTP() const242 bool InstantNTPPrerenderer::ShouldSwitchToLocalNTP() const {
243   if (!ntp())
244     return true;
245 
246   // Assume users with Javascript disabled do not want the online experience.
247   if (!IsJavascriptEnabled())
248     return true;
249 
250   // Already a local page. Not calling IsLocal() because we want to distinguish
251   // between the Google-specific and generic local NTP.
252   if (ntp()->instant_url() == GetLocalInstantURL())
253     return false;
254 
255   if (PageIsCurrent())
256     return false;
257 
258   // The preloaded NTP does not support instant yet. If we're not in startup,
259   // always fall back to the local NTP. If we are in startup, use the local NTP.
260   return !InStartup();
261 }
262 
DefaultSearchProviderChanged()263 void InstantNTPPrerenderer::DefaultSearchProviderChanged() {
264   ReloadInstantNTP();
265 }
266 
GoogleURLUpdated()267 void InstantNTPPrerenderer::GoogleURLUpdated() {
268   ReloadInstantNTP();
269 }
270