| OLD | NEW |
| (Empty) |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 77 void InstantNTPPrerenderer::ReloadInstantNTP() { | |
| 78 ResetNTP(GetInstantURL()); | |
| 79 } | |
| 80 | |
| 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 | |
| 96 content::WebContents* InstantNTPPrerenderer::GetNTPContents() const { | |
| 97 return ntp() ? ntp()->contents() : NULL; | |
| 98 } | |
| 99 | |
| 100 void InstantNTPPrerenderer::DeleteNTPContents() { | |
| 101 if (ntp_) | |
| 102 ntp_.reset(); | |
| 103 } | |
| 104 | |
| 105 void InstantNTPPrerenderer::RenderProcessGone() { | |
| 106 DeleteNTPSoon(ntp_.Pass()); | |
| 107 } | |
| 108 | |
| 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 | |
| 126 std::string InstantNTPPrerenderer::GetLocalInstantURL() const { | |
| 127 return chrome::GetLocalInstantURL(profile_).spec(); | |
| 128 } | |
| 129 | |
| 130 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 | |
| 144 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 | |
| 162 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 | |
| 173 InstantNTP* InstantNTPPrerenderer::ntp() const { | |
| 174 return ntp_.get(); | |
| 175 } | |
| 176 | |
| 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 | |
| 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 | |
| 205 void InstantNTPPrerenderer::InstantPageAboutToNavigateMainFrame( | |
| 206 const content::WebContents* /* contents */, | |
| 207 const GURL& /* url */) { | |
| 208 NOTREACHED(); | |
| 209 } | |
| 210 | |
| 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 | |
| 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 | |
| 232 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 | |
| 242 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 | |
| 263 void InstantNTPPrerenderer::DefaultSearchProviderChanged() { | |
| 264 ReloadInstantNTP(); | |
| 265 } | |
| 266 | |
| 267 void InstantNTPPrerenderer::GoogleURLUpdated() { | |
| 268 ReloadInstantNTP(); | |
| 269 } | |
| OLD | NEW |