1 // Copyright 2012 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_controller.h" 6 7 #include "base/prefs/pref_service.h" 8 #include "base/strings/stringprintf.h" 9 #include "chrome/browser/chrome_notification_types.h" 10 #include "chrome/browser/content_settings/content_settings_provider.h" 11 #include "chrome/browser/content_settings/host_content_settings_map.h" 12 #include "chrome/browser/platform_util.h" 13 #include "chrome/browser/profiles/profile.h" 14 #include "chrome/browser/search/instant_service.h" 15 #include "chrome/browser/search/instant_service_factory.h" 16 #include "chrome/browser/search/search.h" 17 #include "chrome/browser/search_engines/template_url_service.h" 18 #include "chrome/browser/search_engines/template_url_service_factory.h" 19 #include "chrome/browser/ui/browser_instant_controller.h" 20 #include "chrome/browser/ui/search/instant_tab.h" 21 #include "chrome/browser/ui/search/search_tab_helper.h" 22 #include "chrome/common/chrome_switches.h" 23 #include "chrome/common/content_settings_types.h" 24 #include "chrome/common/pref_names.h" 25 #include "chrome/common/search_urls.h" 26 #include "chrome/common/url_constants.h" 27 #include "components/sessions/serialized_navigation_entry.h" 28 #include "content/public/browser/navigation_entry.h" 29 #include "content/public/browser/notification_service.h" 30 #include "content/public/browser/render_process_host.h" 31 #include "content/public/browser/render_widget_host_view.h" 32 #include "content/public/browser/web_contents.h" 33 #include "net/base/escape.h" 34 #include "net/base/network_change_notifier.h" 35 #include "url/gurl.h" 36 37 #if defined(TOOLKIT_VIEWS) 38 #include "ui/views/widget/widget.h" 39 #endif 40 41 namespace { 42 43 bool IsContentsFrom(const InstantPage* page, 44 const content::WebContents* contents) { 45 return page && (page->contents() == contents); 46 } 47 48 // Adds a transient NavigationEntry to the supplied |contents|'s 49 // NavigationController if the page's URL has not already been updated with the 50 // supplied |search_terms|. Sets the |search_terms| on the transient entry for 51 // search terms extraction to work correctly. 52 void EnsureSearchTermsAreSet(content::WebContents* contents, 53 const base::string16& search_terms) { 54 content::NavigationController* controller = &contents->GetController(); 55 56 // If search terms are already correct or there is already a transient entry 57 // (there shouldn't be), bail out early. 58 if (chrome::GetSearchTerms(contents) == search_terms || 59 controller->GetTransientEntry()) 60 return; 61 62 const content::NavigationEntry* entry = controller->GetLastCommittedEntry(); 63 content::NavigationEntry* transient = controller->CreateNavigationEntry( 64 entry->GetURL(), 65 entry->GetReferrer(), 66 entry->GetTransitionType(), 67 false, 68 std::string(), 69 contents->GetBrowserContext()); 70 transient->SetExtraData(sessions::kSearchTermsKey, search_terms); 71 controller->SetTransientEntry(transient); 72 73 SearchTabHelper::FromWebContents(contents)->NavigationEntryUpdated(); 74 } 75 76 } // namespace 77 78 InstantController::InstantController(BrowserInstantController* browser) 79 : browser_(browser) { 80 } 81 82 InstantController::~InstantController() { 83 } 84 85 bool InstantController::SubmitQuery(const base::string16& search_terms) { 86 if (instant_tab_ && instant_tab_->supports_instant() && 87 search_mode_.is_origin_search()) { 88 // Use |instant_tab_| to run the query if we're already on a search results 89 // page. (NOTE: in particular, we do not send the query to NTPs.) 90 SearchTabHelper::FromWebContents(instant_tab_->contents())->Submit( 91 search_terms); 92 instant_tab_->contents()->Focus(); 93 EnsureSearchTermsAreSet(instant_tab_->contents(), search_terms); 94 return true; 95 } 96 return false; 97 } 98 99 void InstantController::SearchModeChanged(const SearchMode& old_mode, 100 const SearchMode& new_mode) { 101 LOG_INSTANT_DEBUG_EVENT(this, base::StringPrintf( 102 "SearchModeChanged: [origin:mode] %d:%d to %d:%d", old_mode.origin, 103 old_mode.mode, new_mode.origin, new_mode.mode)); 104 105 search_mode_ = new_mode; 106 ResetInstantTab(); 107 } 108 109 void InstantController::ActiveTabChanged() { 110 LOG_INSTANT_DEBUG_EVENT(this, "ActiveTabChanged"); 111 ResetInstantTab(); 112 } 113 114 void InstantController::TabDeactivated(content::WebContents* contents) { 115 // If user is deactivating an NTP tab, log the number of mouseovers for this 116 // NTP session. 117 if (chrome::IsInstantNTP(contents)) 118 InstantTab::EmitNtpStatistics(contents); 119 } 120 121 void InstantController::LogDebugEvent(const std::string& info) const { 122 DVLOG(1) << info; 123 124 debug_events_.push_front(std::make_pair( 125 base::Time::Now().ToInternalValue(), info)); 126 static const size_t kMaxDebugEventSize = 2000; 127 if (debug_events_.size() > kMaxDebugEventSize) 128 debug_events_.pop_back(); 129 } 130 131 void InstantController::ClearDebugEvents() { 132 debug_events_.clear(); 133 } 134 135 Profile* InstantController::profile() const { 136 return browser_->profile(); 137 } 138 139 InstantTab* InstantController::instant_tab() const { 140 return instant_tab_.get(); 141 } 142 143 void InstantController::InstantSupportChanged( 144 InstantSupportState instant_support) { 145 // Handle INSTANT_SUPPORT_YES here because InstantPage is not hooked up to the 146 // active tab. Search model changed listener in InstantPage will handle other 147 // cases. 148 if (instant_support != INSTANT_SUPPORT_YES) 149 return; 150 151 ResetInstantTab(); 152 } 153 154 void InstantController::InstantSupportDetermined( 155 const content::WebContents* contents, 156 bool supports_instant) { 157 DCHECK(IsContentsFrom(instant_tab(), contents)); 158 159 if (!supports_instant) 160 base::MessageLoop::current()->DeleteSoon(FROM_HERE, instant_tab_.release()); 161 162 content::NotificationService::current()->Notify( 163 chrome::NOTIFICATION_INSTANT_TAB_SUPPORT_DETERMINED, 164 content::Source<InstantController>(this), 165 content::NotificationService::NoDetails()); 166 } 167 168 void InstantController::InstantPageAboutToNavigateMainFrame( 169 const content::WebContents* contents, 170 const GURL& url) { 171 DCHECK(IsContentsFrom(instant_tab(), contents)); 172 173 // The Instant tab navigated. Send it the data it needs to display 174 // properly. 175 UpdateInfoForInstantTab(); 176 } 177 178 void InstantController::ResetInstantTab() { 179 if (!search_mode_.is_origin_default()) { 180 content::WebContents* active_tab = browser_->GetActiveWebContents(); 181 if (!instant_tab_ || active_tab != instant_tab_->contents()) { 182 instant_tab_.reset(new InstantTab(this, browser_->profile())); 183 instant_tab_->Init(active_tab); 184 UpdateInfoForInstantTab(); 185 } 186 } else { 187 instant_tab_.reset(); 188 } 189 } 190 191 void InstantController::UpdateInfoForInstantTab() { 192 if (instant_tab_) { 193 // Update theme details. 194 InstantService* instant_service = GetInstantService(); 195 if (instant_service) { 196 instant_service->UpdateThemeInfo(); 197 instant_service->UpdateMostVisitedItemsInfo(); 198 } 199 } 200 } 201 202 InstantService* InstantController::GetInstantService() const { 203 return InstantServiceFactory::GetForProfile(profile()); 204 } 205