Home | History | Annotate | Download | only in search
      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