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 41namespace { 42 43bool 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. 52void 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 78InstantController::InstantController(BrowserInstantController* browser) 79 : browser_(browser) { 80} 81 82InstantController::~InstantController() { 83} 84 85bool 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 99void 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 109void InstantController::ActiveTabChanged() { 110 LOG_INSTANT_DEBUG_EVENT(this, "ActiveTabChanged"); 111 ResetInstantTab(); 112} 113 114void 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 121void 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 131void InstantController::ClearDebugEvents() { 132 debug_events_.clear(); 133} 134 135Profile* InstantController::profile() const { 136 return browser_->profile(); 137} 138 139InstantTab* InstantController::instant_tab() const { 140 return instant_tab_.get(); 141} 142 143void 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 154void 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 168void 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 178void 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 191void 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 202InstantService* InstantController::GetInstantService() const { 203 return InstantServiceFactory::GetForProfile(profile()); 204} 205