| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/search/instant_ntp_prerenderer.h" |
| |
| #include "base/basictypes.h" |
| #include "base/bind.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/prefs/pref_service.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/chrome_notification_types.h" |
| #include "chrome/browser/content_settings/content_settings_provider.h" |
| #include "chrome/browser/content_settings/host_content_settings_map.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/search/instant_service.h" |
| #include "chrome/browser/search/instant_service_factory.h" |
| #include "chrome/browser/search/search.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_finder.h" |
| #include "chrome/browser/ui/host_desktop.h" |
| #include "chrome/browser/ui/search/instant_ntp.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/common/content_settings.h" |
| #include "chrome/common/pref_names.h" |
| #include "content/public/browser/notification_service.h" |
| #include "net/base/network_change_notifier.h" |
| |
| namespace { |
| |
| void DeleteNTPSoon(scoped_ptr<InstantNTP> ntp) { |
| if (!ntp) |
| return; |
| |
| if (ntp->contents()) { |
| base::MessageLoop::current()->DeleteSoon( |
| FROM_HERE, ntp->ReleaseContents().release()); |
| } |
| base::MessageLoop::current()->DeleteSoon(FROM_HERE, ntp.release()); |
| } |
| |
| } // namespace |
| |
| |
| InstantNTPPrerenderer::InstantNTPPrerenderer(Profile* profile, |
| PrefService* prefs) |
| : profile_(profile), |
| extended_enabled_(chrome::IsInstantExtendedAPIEnabled()) { |
| DCHECK(profile); |
| |
| // In unit tests, prefs may be NULL. |
| if (prefs) { |
| profile_pref_registrar_.Init(prefs); |
| profile_pref_registrar_.Add( |
| prefs::kSearchSuggestEnabled, |
| base::Bind(&InstantNTPPrerenderer::ReloadStaleNTP, |
| base::Unretained(this))); |
| profile_pref_registrar_.Add( |
| prefs::kDefaultSearchProviderID, |
| base::Bind(&InstantNTPPrerenderer::OnDefaultSearchProviderChanged, |
| base::Unretained(this))); |
| } |
| net::NetworkChangeNotifier::AddNetworkChangeObserver(this); |
| } |
| |
| InstantNTPPrerenderer::~InstantNTPPrerenderer() { |
| net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this); |
| } |
| |
| void InstantNTPPrerenderer::PreloadInstantNTP() { |
| DCHECK(!ntp()); |
| ReloadStaleNTP(); |
| } |
| |
| scoped_ptr<content::WebContents> InstantNTPPrerenderer::ReleaseNTPContents() { |
| if (!extended_enabled() || !profile_ || profile_->IsOffTheRecord() || |
| !chrome::ShouldShowInstantNTP()) |
| return scoped_ptr<content::WebContents>(); |
| |
| if (ShouldSwitchToLocalNTP()) |
| ResetNTP(GetLocalInstantURL()); |
| |
| scoped_ptr<content::WebContents> ntp_contents = ntp_->ReleaseContents(); |
| |
| // Preload a new InstantNTP. |
| ResetNTP(GetInstantURL()); |
| return ntp_contents.Pass(); |
| } |
| |
| content::WebContents* InstantNTPPrerenderer::GetNTPContents() const { |
| return ntp() ? ntp()->contents() : NULL; |
| } |
| |
| void InstantNTPPrerenderer::DeleteNTPContents() { |
| if (ntp_) |
| ntp_.reset(); |
| } |
| |
| void InstantNTPPrerenderer::RenderProcessGone() { |
| DeleteNTPSoon(ntp_.Pass()); |
| } |
| |
| std::string InstantNTPPrerenderer::GetLocalInstantURL() const { |
| return chrome::GetLocalInstantURL(profile_).spec(); |
| } |
| |
| std::string InstantNTPPrerenderer::GetInstantURL() const { |
| if (net::NetworkChangeNotifier::IsOffline()) |
| return GetLocalInstantURL(); |
| |
| // TODO(kmadhusu): Remove start margin param from chrome::GetInstantURL(). |
| const GURL instant_url = chrome::GetInstantURL(profile_, |
| chrome::kDisableStartMargin); |
| if (!instant_url.is_valid()) |
| return GetLocalInstantURL(); |
| |
| return instant_url.spec(); |
| } |
| |
| bool InstantNTPPrerenderer::IsJavascriptEnabled() const { |
| GURL instant_url(GetInstantURL()); |
| GURL origin(instant_url.GetOrigin()); |
| ContentSetting js_setting = profile_->GetHostContentSettingsMap()-> |
| GetContentSetting(origin, origin, CONTENT_SETTINGS_TYPE_JAVASCRIPT, |
| NO_RESOURCE_IDENTIFIER); |
| // Javascript can be disabled either in content settings or via a WebKit |
| // preference, so check both. Disabling it through the Settings page affects |
| // content settings. I'm not sure how to disable the WebKit preference, but |
| // it's theoretically possible some users have it off. |
| bool js_content_enabled = |
| js_setting == CONTENT_SETTING_DEFAULT || |
| js_setting == CONTENT_SETTING_ALLOW; |
| bool js_webkit_enabled = profile_->GetPrefs()->GetBoolean( |
| prefs::kWebKitJavascriptEnabled); |
| return js_content_enabled && js_webkit_enabled; |
| } |
| |
| bool InstantNTPPrerenderer::InStartup() const { |
| #if !defined(OS_ANDROID) |
| // TODO(kmadhusu): This is not completely reliable. Find a better way to |
| // detect startup time. |
| Browser* browser = chrome::FindBrowserWithProfile(profile_, |
| chrome::GetActiveDesktop()); |
| return !browser || !browser->tab_strip_model()->GetActiveWebContents(); |
| #endif |
| return false; |
| } |
| |
| InstantNTP* InstantNTPPrerenderer::ntp() const { |
| return ntp_.get(); |
| } |
| |
| bool InstantNTPPrerenderer::extended_enabled() const { |
| return extended_enabled_; |
| } |
| |
| void InstantNTPPrerenderer::OnNetworkChanged( |
| net::NetworkChangeNotifier::ConnectionType type) { |
| // Not interested in events conveying change to offline. |
| if (type == net::NetworkChangeNotifier::CONNECTION_NONE) |
| return; |
| |
| if (!extended_enabled()) |
| return; |
| |
| if (!ntp() || ntp()->IsLocal()) |
| ResetNTP(GetInstantURL()); |
| } |
| |
| void InstantNTPPrerenderer::InstantSupportDetermined( |
| const content::WebContents* contents, |
| bool supports_instant) { |
| DCHECK(ntp() && ntp()->contents() == contents); |
| |
| if (!supports_instant) { |
| bool is_local = ntp()->IsLocal(); |
| DeleteNTPSoon(ntp_.Pass()); |
| if (!is_local) |
| ResetNTP(GetLocalInstantURL()); |
| } |
| |
| content::NotificationService::current()->Notify( |
| chrome::NOTIFICATION_INSTANT_NTP_SUPPORT_DETERMINED, |
| content::Source<InstantNTPPrerenderer>(this), |
| content::NotificationService::NoDetails()); |
| } |
| |
| void InstantNTPPrerenderer::InstantPageAboutToNavigateMainFrame( |
| const content::WebContents* contents, |
| const GURL& url) { |
| NOTREACHED(); |
| } |
| |
| void InstantNTPPrerenderer::FocusOmnibox(const content::WebContents* contents, |
| OmniboxFocusState state) { |
| NOTREACHED(); |
| } |
| |
| void InstantNTPPrerenderer::NavigateToURL(const content::WebContents* contents, |
| const GURL& url, |
| content::PageTransition transition, |
| WindowOpenDisposition disposition, |
| bool is_search_type) { |
| NOTREACHED(); |
| } |
| |
| void InstantNTPPrerenderer::DeleteMostVisitedItem(const GURL& url) { |
| NOTREACHED(); |
| } |
| |
| void InstantNTPPrerenderer::UndoMostVisitedDeletion(const GURL& url) { |
| NOTREACHED(); |
| } |
| |
| void InstantNTPPrerenderer::UndoAllMostVisitedDeletions() { |
| NOTREACHED(); |
| } |
| |
| void InstantNTPPrerenderer::InstantPageLoadFailed( |
| content::WebContents* contents) { |
| DCHECK(ntp() && ntp()->contents() == contents); |
| |
| bool is_local = ntp()->IsLocal(); |
| DeleteNTPSoon(ntp_.Pass()); |
| if (!is_local) |
| ResetNTP(GetLocalInstantURL()); |
| } |
| |
| void InstantNTPPrerenderer::OnDefaultSearchProviderChanged( |
| const std::string& pref_name) { |
| DCHECK_EQ(pref_name, std::string(prefs::kDefaultSearchProviderID)); |
| if (!extended_enabled() || !ntp()) |
| return; |
| |
| ResetNTP(GetInstantURL()); |
| } |
| |
| void InstantNTPPrerenderer::ResetNTP(const std::string& instant_url) { |
| // Instant NTP is only used in extended mode so we should always have a |
| // non-empty URL to use. |
| DCHECK(!instant_url.empty()); |
| ntp_.reset(new InstantNTP(this, instant_url, profile_)); |
| ntp_->InitContents(base::Bind(&InstantNTPPrerenderer::ReloadStaleNTP, |
| base::Unretained(this))); |
| } |
| |
| void InstantNTPPrerenderer::ReloadStaleNTP() { |
| if (!extended_enabled()) |
| return; |
| |
| ResetNTP(GetInstantURL()); |
| } |
| |
| bool InstantNTPPrerenderer::PageIsCurrent() const { |
| const std::string& instant_url = GetInstantURL(); |
| if (instant_url.empty() || |
| !chrome::MatchesOriginAndPath(GURL(ntp()->instant_url()), |
| GURL(instant_url))) |
| return false; |
| |
| return ntp()->supports_instant(); |
| } |
| |
| bool InstantNTPPrerenderer::ShouldSwitchToLocalNTP() const { |
| if (!ntp()) |
| return true; |
| |
| // Assume users with Javascript disabled do not want the online experience. |
| if (!IsJavascriptEnabled()) |
| return true; |
| |
| // Already a local page. Not calling IsLocal() because we want to distinguish |
| // between the Google-specific and generic local NTP. |
| if (extended_enabled() && ntp()->instant_url() == GetLocalInstantURL()) |
| return false; |
| |
| if (PageIsCurrent()) |
| return false; |
| |
| // The preloaded NTP does not support instant yet. If we're not in startup, |
| // always fall back to the local NTP. If we are in startup, use the local NTP |
| // (unless the finch flag to use the remote NTP is set). |
| return !(InStartup() && chrome::ShouldPreferRemoteNTPOnStartup()); |
| } |