1 // Copyright (c) 2011 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_engines/search_engine_tab_helper.h" 6 7 #include "chrome/browser/profiles/profile.h" 8 #include "chrome/browser/search_engines/template_url.h" 9 #include "chrome/browser/search_engines/template_url_fetcher.h" 10 #include "chrome/browser/search_engines/template_url_model.h" 11 #include "chrome/browser/ui/search_engines/template_url_fetcher_ui_callbacks.h" 12 #include "chrome/common/render_messages.h" 13 #include "content/common/view_messages.h" 14 #include "content/browser/tab_contents/tab_contents.h" 15 16 namespace { 17 18 // Returns true if the entry's transition type is FORM_SUBMIT. 19 bool IsFormSubmit(const NavigationEntry* entry) { 20 return (PageTransition::StripQualifier(entry->transition_type()) == 21 PageTransition::FORM_SUBMIT); 22 } 23 24 } // namespace 25 26 SearchEngineTabHelper::SearchEngineTabHelper(TabContents* tab_contents) 27 : TabContentsObserver(tab_contents) { 28 DCHECK(tab_contents); 29 } 30 31 SearchEngineTabHelper::~SearchEngineTabHelper() { 32 } 33 34 void SearchEngineTabHelper::DidNavigateMainFramePostCommit( 35 const NavigationController::LoadCommittedDetails& /*details*/, 36 const ViewHostMsg_FrameNavigate_Params& params) { 37 GenerateKeywordIfNecessary(params); 38 } 39 40 bool SearchEngineTabHelper::OnMessageReceived(const IPC::Message& message) { 41 bool handled = true; 42 IPC_BEGIN_MESSAGE_MAP(SearchEngineTabHelper, message) 43 IPC_MESSAGE_HANDLER(ViewHostMsg_PageHasOSDD, OnPageHasOSDD) 44 IPC_MESSAGE_UNHANDLED(handled = false) 45 IPC_END_MESSAGE_MAP() 46 47 return handled; 48 } 49 50 void SearchEngineTabHelper::OnPageHasOSDD( 51 int32 page_id, 52 const GURL& doc_url, 53 const search_provider::OSDDType& msg_provider_type) { 54 // Checks to see if we should generate a keyword based on the OSDD, and if 55 // necessary uses TemplateURLFetcher to download the OSDD and create a 56 // keyword. 57 58 // Make sure page_id is the current page and other basic checks. 59 DCHECK(doc_url.is_valid()); 60 if (!tab_contents()->IsActiveEntry(page_id)) 61 return; 62 if (!tab_contents()->profile()->GetTemplateURLFetcher()) 63 return; 64 if (tab_contents()->profile()->IsOffTheRecord()) 65 return; 66 67 TemplateURLFetcher::ProviderType provider_type; 68 switch (msg_provider_type) { 69 case search_provider::AUTODETECTED_PROVIDER: 70 provider_type = TemplateURLFetcher::AUTODETECTED_PROVIDER; 71 break; 72 73 case search_provider::EXPLICIT_DEFAULT_PROVIDER: 74 provider_type = TemplateURLFetcher::EXPLICIT_DEFAULT_PROVIDER; 75 break; 76 77 case search_provider::EXPLICIT_PROVIDER: 78 provider_type = TemplateURLFetcher::EXPLICIT_PROVIDER; 79 break; 80 81 default: 82 NOTREACHED(); 83 return; 84 } 85 86 const NavigationController& controller = tab_contents()->controller(); 87 const NavigationEntry* entry = controller.GetLastCommittedEntry(); 88 DCHECK(entry); 89 90 const NavigationEntry* base_entry = entry; 91 if (IsFormSubmit(base_entry)) { 92 // If the current page is a form submit, find the last page that was not 93 // a form submit and use its url to generate the keyword from. 94 int index = controller.last_committed_entry_index() - 1; 95 while (index >= 0 && IsFormSubmit(controller.GetEntryAtIndex(index))) 96 index--; 97 if (index >= 0) 98 base_entry = controller.GetEntryAtIndex(index); 99 else 100 base_entry = NULL; 101 } 102 103 // We want to use the user typed URL if available since that represents what 104 // the user typed to get here, and fall back on the regular URL if not. 105 if (!base_entry) 106 return; 107 GURL keyword_url = base_entry->user_typed_url().is_valid() ? 108 base_entry->user_typed_url() : base_entry->url(); 109 if (!keyword_url.is_valid()) 110 return; 111 112 string16 keyword = TemplateURLModel::GenerateKeyword( 113 keyword_url, 114 provider_type == TemplateURLFetcher::AUTODETECTED_PROVIDER); 115 116 // Download the OpenSearch description document. If this is successful, a 117 // new keyword will be created when done. 118 tab_contents()->profile()->GetTemplateURLFetcher()->ScheduleDownload( 119 keyword, 120 doc_url, 121 base_entry->favicon().url(), 122 new TemplateURLFetcherUICallbacks(this, tab_contents()), 123 provider_type); 124 } 125 126 void SearchEngineTabHelper::GenerateKeywordIfNecessary( 127 const ViewHostMsg_FrameNavigate_Params& params) { 128 if (!params.searchable_form_url.is_valid()) 129 return; 130 131 if (tab_contents()->profile()->IsOffTheRecord()) 132 return; 133 134 const NavigationController& controller = tab_contents()->controller(); 135 int last_index = controller.last_committed_entry_index(); 136 // When there was no previous page, the last index will be 0. This is 137 // normally due to a form submit that opened in a new tab. 138 // TODO(brettw) bug 916126: we should support keywords when form submits 139 // happen in new tabs. 140 if (last_index <= 0) 141 return; 142 const NavigationEntry* previous_entry = 143 controller.GetEntryAtIndex(last_index - 1); 144 if (IsFormSubmit(previous_entry)) { 145 // Only generate a keyword if the previous page wasn't itself a form 146 // submit. 147 return; 148 } 149 150 GURL keyword_url = previous_entry->user_typed_url().is_valid() ? 151 previous_entry->user_typed_url() : previous_entry->url(); 152 string16 keyword = 153 TemplateURLModel::GenerateKeyword(keyword_url, true); // autodetected 154 if (keyword.empty()) 155 return; 156 157 TemplateURLModel* url_model = 158 tab_contents()->profile()->GetTemplateURLModel(); 159 if (!url_model) 160 return; 161 162 if (!url_model->loaded()) { 163 url_model->Load(); 164 return; 165 } 166 167 const TemplateURL* current_url; 168 GURL url = params.searchable_form_url; 169 if (!url_model->CanReplaceKeyword(keyword, url, ¤t_url)) 170 return; 171 172 if (current_url) { 173 if (current_url->originating_url().is_valid()) { 174 // The existing keyword was generated from an OpenSearch description 175 // document, don't regenerate. 176 return; 177 } 178 url_model->Remove(current_url); 179 } 180 TemplateURL* new_url = new TemplateURL(); 181 new_url->set_keyword(keyword); 182 new_url->set_short_name(keyword); 183 new_url->SetURL(url.spec(), 0, 0); 184 new_url->add_input_encoding(params.searchable_form_encoding); 185 DCHECK(controller.GetLastCommittedEntry()); 186 const GURL& favicon_url = 187 controller.GetLastCommittedEntry()->favicon().url(); 188 if (favicon_url.is_valid()) { 189 new_url->SetFaviconURL(favicon_url); 190 } else { 191 // The favicon url isn't valid. This means there really isn't a favicon, 192 // or the favicon url wasn't obtained before the load started. This assumes 193 // the later. 194 // TODO(sky): Need a way to set the favicon that doesn't involve generating 195 // its url. 196 new_url->SetFaviconURL(TemplateURL::GenerateFaviconURL(params.referrer)); 197 } 198 new_url->set_safe_for_autoreplace(true); 199 url_model->Add(new_url); 200 } 201