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 "content/public/browser/notification_service.h" 27 #include "content/public/browser/render_process_host.h" 28 #include "content/public/browser/web_contents.h" 29 #include "net/base/network_change_notifier.h" 30 31 namespace { 32 33 void DeleteNTPSoon(scoped_ptr<InstantNTP> ntp) { 34 if (!ntp) 35 return; 36 37 if (ntp->contents()) { 38 base::MessageLoop::current()->DeleteSoon( 39 FROM_HERE, ntp->ReleaseContents().release()); 40 } 41 base::MessageLoop::current()->DeleteSoon(FROM_HERE, ntp.release()); 42 } 43 44 } // namespace 45 46 47 InstantNTPPrerenderer::InstantNTPPrerenderer(Profile* profile, 48 PrefService* prefs) 49 : profile_(profile) { 50 DCHECK(profile); 51 52 // In unit tests, prefs may be NULL. 53 if (prefs) { 54 profile_pref_registrar_.Init(prefs); 55 profile_pref_registrar_.Add( 56 prefs::kSearchSuggestEnabled, 57 base::Bind(&InstantNTPPrerenderer::ReloadStaleNTP, 58 base::Unretained(this))); 59 profile_pref_registrar_.Add( 60 prefs::kDefaultSearchProviderID, 61 base::Bind(&InstantNTPPrerenderer::OnDefaultSearchProviderChanged, 62 base::Unretained(this))); 63 } 64 net::NetworkChangeNotifier::AddNetworkChangeObserver(this); 65 } 66 67 InstantNTPPrerenderer::~InstantNTPPrerenderer() { 68 net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this); 69 } 70 71 void InstantNTPPrerenderer::PreloadInstantNTP() { 72 DCHECK(!ntp()); 73 ReloadStaleNTP(); 74 } 75 76 scoped_ptr<content::WebContents> InstantNTPPrerenderer::ReleaseNTPContents() { 77 if (!profile_ || profile_->IsOffTheRecord() || 78 !chrome::ShouldShowInstantNTP()) 79 return scoped_ptr<content::WebContents>(); 80 81 if (ShouldSwitchToLocalNTP()) 82 ResetNTP(GetLocalInstantURL()); 83 84 scoped_ptr<content::WebContents> ntp_contents = ntp_->ReleaseContents(); 85 86 // Preload a new InstantNTP. 87 ResetNTP(GetInstantURL()); 88 return ntp_contents.Pass(); 89 } 90 91 content::WebContents* InstantNTPPrerenderer::GetNTPContents() const { 92 return ntp() ? ntp()->contents() : NULL; 93 } 94 95 void InstantNTPPrerenderer::DeleteNTPContents() { 96 if (ntp_) 97 ntp_.reset(); 98 } 99 100 void InstantNTPPrerenderer::RenderProcessGone() { 101 DeleteNTPSoon(ntp_.Pass()); 102 } 103 104 void InstantNTPPrerenderer::LoadCompletedMainFrame() { 105 if (!ntp_ || ntp_->supports_instant()) 106 return; 107 108 content::WebContents* ntp_contents = ntp_->contents(); 109 DCHECK(ntp_contents); 110 111 InstantService* instant_service = 112 InstantServiceFactory::GetForProfile(profile()); 113 if (instant_service && 114 instant_service->IsInstantProcess( 115 ntp_contents->GetRenderProcessHost()->GetID())) { 116 return; 117 } 118 InstantSupportDetermined(ntp_contents, false); 119 } 120 121 std::string InstantNTPPrerenderer::GetLocalInstantURL() const { 122 return chrome::GetLocalInstantURL(profile_).spec(); 123 } 124 125 std::string InstantNTPPrerenderer::GetInstantURL() const { 126 if (net::NetworkChangeNotifier::IsOffline()) 127 return GetLocalInstantURL(); 128 129 // TODO(kmadhusu): Remove start margin param from chrome::GetInstantURL(). 130 const GURL instant_url = chrome::GetInstantURL(profile_, 131 chrome::kDisableStartMargin); 132 if (!instant_url.is_valid()) 133 return GetLocalInstantURL(); 134 135 return instant_url.spec(); 136 } 137 138 bool InstantNTPPrerenderer::IsJavascriptEnabled() const { 139 GURL instant_url(GetInstantURL()); 140 GURL origin(instant_url.GetOrigin()); 141 ContentSetting js_setting = profile_->GetHostContentSettingsMap()-> 142 GetContentSetting(origin, origin, CONTENT_SETTINGS_TYPE_JAVASCRIPT, 143 NO_RESOURCE_IDENTIFIER); 144 // Javascript can be disabled either in content settings or via a WebKit 145 // preference, so check both. Disabling it through the Settings page affects 146 // content settings. I'm not sure how to disable the WebKit preference, but 147 // it's theoretically possible some users have it off. 148 bool js_content_enabled = 149 js_setting == CONTENT_SETTING_DEFAULT || 150 js_setting == CONTENT_SETTING_ALLOW; 151 bool js_webkit_enabled = profile_->GetPrefs()->GetBoolean( 152 prefs::kWebKitJavascriptEnabled); 153 return js_content_enabled && js_webkit_enabled; 154 } 155 156 bool InstantNTPPrerenderer::InStartup() const { 157 #if !defined(OS_ANDROID) 158 // TODO(kmadhusu): This is not completely reliable. Find a better way to 159 // detect startup time. 160 Browser* browser = chrome::FindBrowserWithProfile(profile_, 161 chrome::GetActiveDesktop()); 162 return !browser || !browser->tab_strip_model()->GetActiveWebContents(); 163 #endif 164 return false; 165 } 166 167 InstantNTP* InstantNTPPrerenderer::ntp() const { 168 return ntp_.get(); 169 } 170 171 void InstantNTPPrerenderer::OnNetworkChanged( 172 net::NetworkChangeNotifier::ConnectionType type) { 173 // Not interested in events conveying change to offline. 174 if (type == net::NetworkChangeNotifier::CONNECTION_NONE) 175 return; 176 177 if (!ntp() || ntp()->IsLocal()) 178 ResetNTP(GetInstantURL()); 179 } 180 181 void InstantNTPPrerenderer::InstantSupportDetermined( 182 const content::WebContents* contents, 183 bool supports_instant) { 184 DCHECK(ntp() && ntp()->contents() == contents); 185 186 if (!supports_instant) { 187 bool is_local = ntp()->IsLocal(); 188 DeleteNTPSoon(ntp_.Pass()); 189 if (!is_local) 190 ResetNTP(GetLocalInstantURL()); 191 } 192 193 content::NotificationService::current()->Notify( 194 chrome::NOTIFICATION_INSTANT_NTP_SUPPORT_DETERMINED, 195 content::Source<InstantNTPPrerenderer>(this), 196 content::NotificationService::NoDetails()); 197 } 198 199 void InstantNTPPrerenderer::InstantPageAboutToNavigateMainFrame( 200 const content::WebContents* /* contents */, 201 const GURL& /* url */) { 202 NOTREACHED(); 203 } 204 205 void InstantNTPPrerenderer::FocusOmnibox( 206 const content::WebContents* /* contents */, 207 OmniboxFocusState /* state */) { 208 NOTREACHED(); 209 } 210 211 void InstantNTPPrerenderer::NavigateToURL( 212 const content::WebContents* /* contents */, 213 const GURL& /* url */, 214 content::PageTransition /* transition */, 215 WindowOpenDisposition /* disposition */, 216 bool /* is_search_type */) { 217 NOTREACHED(); 218 } 219 220 void InstantNTPPrerenderer::PasteIntoOmnibox( 221 const content::WebContents* /* contents */, 222 const string16& /* text */) { 223 NOTREACHED(); 224 } 225 226 void InstantNTPPrerenderer::DeleteMostVisitedItem(const GURL& /* url */) { 227 NOTREACHED(); 228 } 229 230 void InstantNTPPrerenderer::UndoMostVisitedDeletion(const GURL& /* url */) { 231 NOTREACHED(); 232 } 233 234 void InstantNTPPrerenderer::UndoAllMostVisitedDeletions() { 235 NOTREACHED(); 236 } 237 238 void InstantNTPPrerenderer::InstantPageLoadFailed( 239 content::WebContents* contents) { 240 DCHECK(ntp() && ntp()->contents() == contents); 241 242 bool is_local = ntp()->IsLocal(); 243 DeleteNTPSoon(ntp_.Pass()); 244 if (!is_local) 245 ResetNTP(GetLocalInstantURL()); 246 } 247 248 void InstantNTPPrerenderer::OnDefaultSearchProviderChanged( 249 const std::string& pref_name) { 250 DCHECK_EQ(pref_name, std::string(prefs::kDefaultSearchProviderID)); 251 if (!ntp()) 252 return; 253 254 ResetNTP(GetInstantURL()); 255 } 256 257 void InstantNTPPrerenderer::ResetNTP(const std::string& instant_url) { 258 // Instant NTP is only used in extended mode so we should always have a 259 // non-empty URL to use. 260 DCHECK(!instant_url.empty()); 261 ntp_.reset(new InstantNTP(this, instant_url, profile_)); 262 ntp_->InitContents(base::Bind(&InstantNTPPrerenderer::ReloadStaleNTP, 263 base::Unretained(this))); 264 } 265 266 void InstantNTPPrerenderer::ReloadStaleNTP() { 267 ResetNTP(GetInstantURL()); 268 } 269 270 bool InstantNTPPrerenderer::PageIsCurrent() const { 271 const std::string& instant_url = GetInstantURL(); 272 if (instant_url.empty() || 273 !chrome::MatchesOriginAndPath(GURL(ntp()->instant_url()), 274 GURL(instant_url))) 275 return false; 276 277 return ntp()->supports_instant(); 278 } 279 280 bool InstantNTPPrerenderer::ShouldSwitchToLocalNTP() const { 281 if (!ntp()) 282 return true; 283 284 // Assume users with Javascript disabled do not want the online experience. 285 if (!IsJavascriptEnabled()) 286 return true; 287 288 // Already a local page. Not calling IsLocal() because we want to distinguish 289 // between the Google-specific and generic local NTP. 290 if (ntp()->instant_url() == GetLocalInstantURL()) 291 return false; 292 293 if (PageIsCurrent()) 294 return false; 295 296 // The preloaded NTP does not support instant yet. If we're not in startup, 297 // always fall back to the local NTP. If we are in startup, use the local NTP 298 // (unless the finch flag to use the remote NTP is set). 299 return !(InStartup() && chrome::ShouldPreferRemoteNTPOnStartup()); 300 } 301