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_page.h" 6 7 #include "apps/app_launcher.h" 8 #include "base/strings/utf_string_conversions.h" 9 #include "chrome/browser/chrome_notification_types.h" 10 #include "chrome/browser/history/most_visited_tiles_experiment.h" 11 #include "chrome/browser/history/top_sites.h" 12 #include "chrome/browser/profiles/profile.h" 13 #include "chrome/browser/search/instant_service.h" 14 #include "chrome/browser/search/instant_service_factory.h" 15 #include "chrome/browser/search/search.h" 16 #include "chrome/browser/ui/browser_finder.h" 17 #include "chrome/browser/ui/search/instant_tab.h" 18 #include "chrome/browser/ui/search/search_model.h" 19 #include "chrome/browser/ui/search/search_tab_helper.h" 20 #include "chrome/browser/ui/tabs/tab_strip_model.h" 21 #include "chrome/browser/ui/tabs/tab_strip_model_utils.h" 22 #include "chrome/common/render_messages.h" 23 #include "chrome/common/url_constants.h" 24 #include "content/public/browser/navigation_controller.h" 25 #include "content/public/browser/navigation_details.h" 26 #include "content/public/browser/navigation_entry.h" 27 #include "content/public/browser/notification_service.h" 28 #include "content/public/browser/notification_source.h" 29 #include "content/public/browser/web_contents.h" 30 #include "content/public/common/frame_navigate_params.h" 31 #include "ui/base/resource/resource_bundle.h" 32 #include "ui/gfx/font.h" 33 34 InstantPage::Delegate::~Delegate() { 35 } 36 37 InstantPage::~InstantPage() { 38 if (contents()) 39 SearchTabHelper::FromWebContents(contents())->model()->RemoveObserver(this); 40 41 // |profile_| may be NULL during unit tests. 42 if (profile_) { 43 InstantService* instant_service = 44 InstantServiceFactory::GetForProfile(profile_); 45 instant_service->RemoveObserver(this); 46 } 47 } 48 49 bool InstantPage::supports_instant() const { 50 return contents() ? 51 SearchTabHelper::FromWebContents(contents())->SupportsInstant() : false; 52 } 53 54 const std::string& InstantPage::instant_url() const { 55 return instant_url_; 56 } 57 58 bool InstantPage::IsLocal() const { 59 return contents() && 60 contents()->GetURL() == GURL(chrome::kChromeSearchLocalNtpUrl); 61 } 62 63 void InstantPage::InitializeFonts() { 64 #if defined(OS_MACOSX) 65 // This value should be kept in sync with OmniboxViewMac::GetFieldFont. 66 const gfx::Font& omnibox_font = 67 ui::ResourceBundle::GetSharedInstance().GetFont( 68 ui::ResourceBundle::MediumFont).DeriveFont(1); 69 #else 70 const gfx::Font& omnibox_font = 71 ui::ResourceBundle::GetSharedInstance().GetFont( 72 ui::ResourceBundle::MediumFont); 73 #endif 74 sender()->SetFontInformation(UTF8ToUTF16(omnibox_font.GetFontName()), 75 omnibox_font.GetFontSize()); 76 } 77 78 void InstantPage::InitializePromos() { 79 sender()->SetPromoInformation(apps::IsAppLauncherEnabled()); 80 } 81 82 InstantPage::InstantPage(Delegate* delegate, const std::string& instant_url, 83 Profile* profile, bool is_incognito) 84 : profile_(profile), 85 delegate_(delegate), 86 ipc_sender_(InstantIPCSender::Create(is_incognito)), 87 instant_url_(instant_url), 88 is_incognito_(is_incognito) { 89 // |profile_| may be NULL during unit tests. 90 if (profile_) { 91 InstantService* instant_service = 92 InstantServiceFactory::GetForProfile(profile_); 93 instant_service->AddObserver(this); 94 } 95 } 96 97 void InstantPage::SetContents(content::WebContents* web_contents) { 98 ClearContents(); 99 100 if (!web_contents) 101 return; 102 103 sender()->SetContents(web_contents); 104 Observe(web_contents); 105 SearchModel* model = SearchTabHelper::FromWebContents(contents())->model(); 106 model->AddObserver(this); 107 108 // Already know whether the page supports instant. 109 if (model->instant_support() != INSTANT_SUPPORT_UNKNOWN) 110 InstantSupportDetermined(model->instant_support() == INSTANT_SUPPORT_YES); 111 } 112 113 bool InstantPage::ShouldProcessAboutToNavigateMainFrame() { 114 return false; 115 } 116 117 bool InstantPage::ShouldProcessFocusOmnibox() { 118 return false; 119 } 120 121 bool InstantPage::ShouldProcessNavigateToURL() { 122 return false; 123 } 124 125 bool InstantPage::ShouldProcessPasteIntoOmnibox() { 126 return false; 127 } 128 129 bool InstantPage::ShouldProcessDeleteMostVisitedItem() { 130 return false; 131 } 132 133 bool InstantPage::ShouldProcessUndoMostVisitedDeletion() { 134 return false; 135 } 136 137 bool InstantPage::ShouldProcessUndoAllMostVisitedDeletions() { 138 return false; 139 } 140 141 bool InstantPage::OnMessageReceived(const IPC::Message& message) { 142 if (is_incognito_) 143 return false; 144 145 bool handled = true; 146 IPC_BEGIN_MESSAGE_MAP(InstantPage, message) 147 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_FocusOmnibox, OnFocusOmnibox) 148 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_SearchBoxNavigate, 149 OnSearchBoxNavigate); 150 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_PasteAndOpenDropdown, 151 OnSearchBoxPaste); 152 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_CountMouseover, OnCountMouseover); 153 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_SearchBoxDeleteMostVisitedItem, 154 OnDeleteMostVisitedItem); 155 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_SearchBoxUndoMostVisitedDeletion, 156 OnUndoMostVisitedDeletion); 157 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_SearchBoxUndoAllMostVisitedDeletions, 158 OnUndoAllMostVisitedDeletions); 159 IPC_MESSAGE_UNHANDLED(handled = false) 160 IPC_END_MESSAGE_MAP() 161 return handled; 162 } 163 164 void InstantPage::DidCommitProvisionalLoadForFrame( 165 int64 /* frame_id */, 166 bool is_main_frame, 167 const GURL& url, 168 content::PageTransition /* transition_type */, 169 content::RenderViewHost* /* render_view_host */) { 170 if (is_main_frame && ShouldProcessAboutToNavigateMainFrame()) 171 delegate_->InstantPageAboutToNavigateMainFrame(contents(), url); 172 } 173 174 void InstantPage::DidNavigateMainFrame( 175 const content::LoadCommittedDetails& details, 176 const content::FrameNavigateParams& /* params */) { 177 // A 204 can be sent by the search provider as a lightweight signal 178 // to fall back to the local page, and we obviously want to fall back 179 // if we get any response code that indicates an error. 180 if (details.http_status_code == 204 || details.http_status_code >= 400) 181 delegate_->InstantPageLoadFailed(contents()); 182 } 183 184 void InstantPage::DidFailProvisionalLoad( 185 int64 /* frame_id */, 186 bool is_main_frame, 187 const GURL& /* validated_url */, 188 int /* error_code */, 189 const string16& /* error_description */, 190 content::RenderViewHost* /* render_view_host */) { 191 if (is_main_frame) 192 delegate_->InstantPageLoadFailed(contents()); 193 } 194 195 void InstantPage::ThemeInfoChanged(const ThemeBackgroundInfo& theme_info) { 196 sender()->SendThemeBackgroundInfo(theme_info); 197 } 198 199 void InstantPage::MostVisitedItemsChanged( 200 const std::vector<InstantMostVisitedItem>& items) { 201 std::vector<InstantMostVisitedItem> items_copy(items); 202 MaybeRemoveMostVisitedItems(&items_copy); 203 204 sender()->SendMostVisitedItems(items_copy); 205 206 content::NotificationService::current()->Notify( 207 chrome::NOTIFICATION_INSTANT_SENT_MOST_VISITED_ITEMS, 208 content::Source<InstantPage>(this), 209 content::NotificationService::NoDetails()); 210 } 211 212 void InstantPage::ModelChanged(const SearchModel::State& old_state, 213 const SearchModel::State& new_state) { 214 if (old_state.instant_support != new_state.instant_support) 215 InstantSupportDetermined(new_state.instant_support == INSTANT_SUPPORT_YES); 216 } 217 218 void InstantPage::InstantSupportDetermined(bool supports_instant) { 219 delegate_->InstantSupportDetermined(contents(), supports_instant); 220 221 // If the page doesn't support Instant, stop listening to it. 222 if (!supports_instant) 223 ClearContents(); 224 } 225 226 void InstantPage::OnFocusOmnibox(int page_id, OmniboxFocusState state) { 227 if (!contents()->IsActiveEntry(page_id)) 228 return; 229 230 SearchTabHelper::FromWebContents(contents())->InstantSupportChanged(true); 231 if (!ShouldProcessFocusOmnibox()) 232 return; 233 234 delegate_->FocusOmnibox(contents(), state); 235 } 236 237 void InstantPage::OnSearchBoxNavigate(int page_id, 238 const GURL& url, 239 content::PageTransition transition, 240 WindowOpenDisposition disposition, 241 bool is_search_type) { 242 if (!contents()->IsActiveEntry(page_id)) 243 return; 244 245 SearchTabHelper::FromWebContents(contents())->InstantSupportChanged(true); 246 if (!ShouldProcessNavigateToURL()) 247 return; 248 249 delegate_->NavigateToURL( 250 contents(), url, transition, disposition, is_search_type); 251 } 252 253 void InstantPage::OnSearchBoxPaste(int page_id, const string16& text) { 254 if (!contents()->IsActiveEntry(page_id)) 255 return; 256 257 SearchTabHelper::FromWebContents(contents())->InstantSupportChanged(true); 258 if (!ShouldProcessPasteIntoOmnibox()) 259 return; 260 261 delegate_->PasteIntoOmnibox(contents(), text); 262 } 263 264 void InstantPage::OnCountMouseover(int page_id) { 265 if (!contents()->IsActiveEntry(page_id)) 266 return; 267 268 InstantTab::CountMouseover(contents()); 269 } 270 271 void InstantPage::OnDeleteMostVisitedItem(int page_id, const GURL& url) { 272 if (!contents()->IsActiveEntry(page_id)) 273 return; 274 275 SearchTabHelper::FromWebContents(contents())->InstantSupportChanged(true); 276 if (!ShouldProcessDeleteMostVisitedItem()) 277 return; 278 279 delegate_->DeleteMostVisitedItem(url); 280 } 281 282 void InstantPage::OnUndoMostVisitedDeletion(int page_id, const GURL& url) { 283 if (!contents()->IsActiveEntry(page_id)) 284 return; 285 286 SearchTabHelper::FromWebContents(contents())->InstantSupportChanged(true); 287 if (!ShouldProcessUndoMostVisitedDeletion()) 288 return; 289 290 delegate_->UndoMostVisitedDeletion(url); 291 } 292 293 void InstantPage::OnUndoAllMostVisitedDeletions(int page_id) { 294 if (!contents()->IsActiveEntry(page_id)) 295 return; 296 297 SearchTabHelper::FromWebContents(contents())->InstantSupportChanged(true); 298 if (!ShouldProcessUndoAllMostVisitedDeletions()) 299 return; 300 301 delegate_->UndoAllMostVisitedDeletions(); 302 } 303 304 void InstantPage::ClearContents() { 305 if (contents()) 306 SearchTabHelper::FromWebContents(contents())->model()->RemoveObserver(this); 307 308 sender()->SetContents(NULL); 309 Observe(NULL); 310 } 311 312 void InstantPage::MaybeRemoveMostVisitedItems( 313 std::vector<InstantMostVisitedItem>* items) { 314 // The code below uses APIs not available on Android and the experiment should 315 // not run there. 316 #if !defined(OS_ANDROID) 317 if (!history::MostVisitedTilesExperiment::IsDontShowOpenURLsEnabled()) 318 return; 319 320 Browser* browser = chrome::FindBrowserWithProfile(profile_, 321 chrome::GetActiveDesktop()); 322 if (!browser) 323 return; 324 325 TabStripModel* tab_strip_model = browser->tab_strip_model(); 326 history::TopSites* top_sites = profile_->GetTopSites(); 327 if (!tab_strip_model || !top_sites) { 328 NOTREACHED(); 329 return; 330 } 331 332 std::set<std::string> open_urls; 333 chrome::GetOpenUrls(*tab_strip_model, *top_sites, &open_urls); 334 history::MostVisitedTilesExperiment::RemoveItemsMatchingOpenTabs( 335 open_urls, items); 336 337 #endif 338 } 339