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_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