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 34InstantPage::Delegate::~Delegate() { 35} 36 37InstantPage::~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 49bool InstantPage::supports_instant() const { 50 return contents() ? 51 SearchTabHelper::FromWebContents(contents())->SupportsInstant() : false; 52} 53 54const std::string& InstantPage::instant_url() const { 55 return instant_url_; 56} 57 58bool InstantPage::IsLocal() const { 59 return contents() && 60 contents()->GetURL() == GURL(chrome::kChromeSearchLocalNtpUrl); 61} 62 63void 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 78void InstantPage::InitializePromos() { 79 sender()->SetPromoInformation(apps::IsAppLauncherEnabled()); 80} 81 82InstantPage::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 97void 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 113bool InstantPage::ShouldProcessAboutToNavigateMainFrame() { 114 return false; 115} 116 117bool InstantPage::ShouldProcessFocusOmnibox() { 118 return false; 119} 120 121bool InstantPage::ShouldProcessNavigateToURL() { 122 return false; 123} 124 125bool InstantPage::ShouldProcessPasteIntoOmnibox() { 126 return false; 127} 128 129bool InstantPage::ShouldProcessDeleteMostVisitedItem() { 130 return false; 131} 132 133bool InstantPage::ShouldProcessUndoMostVisitedDeletion() { 134 return false; 135} 136 137bool InstantPage::ShouldProcessUndoAllMostVisitedDeletions() { 138 return false; 139} 140 141bool 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 164void 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 174void 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 184void 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 195void InstantPage::ThemeInfoChanged(const ThemeBackgroundInfo& theme_info) { 196 sender()->SendThemeBackgroundInfo(theme_info); 197} 198 199void 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 212void 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 218void 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 226void 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 237void 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 253void 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 264void InstantPage::OnCountMouseover(int page_id) { 265 if (!contents()->IsActiveEntry(page_id)) 266 return; 267 268 InstantTab::CountMouseover(contents()); 269} 270 271void 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 282void 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 293void 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 304void InstantPage::ClearContents() { 305 if (contents()) 306 SearchTabHelper::FromWebContents(contents())->model()->RemoveObserver(this); 307 308 sender()->SetContents(NULL); 309 Observe(NULL); 310} 311 312void 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