autocomplete_controller.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
127a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow// Copyright (c) 2012 The Chromium Authors. All rights reserved. 227a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow// Use of this source code is governed by a BSD-style license that can be 327a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow// found in the LICENSE file. 427a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow 527a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include "chrome/browser/autocomplete/autocomplete_controller.h" 627a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow 727a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include <set> 827a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include <string> 927a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow 1027a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include "base/command_line.h" 1127a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include "base/format_macros.h" 1227a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include "base/logging.h" 1327a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include "base/metrics/histogram.h" 1427a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include "base/string_number_conversions.h" 1527a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include "base/stringprintf.h" 1627a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include "base/time.h" 1727a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include "chrome/browser/autocomplete/autocomplete_controller_delegate.h" 1827a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include "chrome/browser/autocomplete/bookmark_provider.h" 1927a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include "chrome/browser/autocomplete/builtin_provider.h" 206b7c2aeb004cc8e499f1a2281c356bee0bfc9061Marshall Clow#include "chrome/browser/autocomplete/extension_app_provider.h" 2127a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include "chrome/browser/autocomplete/history_contents_provider.h" 2227a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include "chrome/browser/autocomplete/history_quick_provider.h" 2327a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include "chrome/browser/autocomplete/history_url_provider.h" 2427a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include "chrome/browser/autocomplete/keyword_provider.h" 2527a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include "chrome/browser/autocomplete/search_provider.h" 2627a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include "chrome/browser/autocomplete/shortcuts_provider.h" 276b7c2aeb004cc8e499f1a2281c356bee0bfc9061Marshall Clow#include "chrome/browser/autocomplete/zero_suggest_provider.h" 2827a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include "chrome/browser/profiles/profile.h" 2927a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include "chrome/browser/search_engines/template_url.h" 306b7c2aeb004cc8e499f1a2281c356bee0bfc9061Marshall Clow#include "chrome/common/chrome_notification_types.h" 3127a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include "chrome/common/chrome_switches.h" 3227a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include "content/public/browser/notification_service.h" 3327a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include "grit/generated_resources.h" 3427a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include "grit/theme_resources.h" 3527a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include "ui/base/l10n/l10n_util.h" 3627a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow 376b7c2aeb004cc8e499f1a2281c356bee0bfc9061Marshall Clow#if defined(OS_CHROMEOS) 3827a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include "chrome/browser/autocomplete/contact_provider_chromeos.h" 3927a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#include "chrome/browser/chromeos/contacts/contact_manager.h" 4027a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow#endif 4127a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow 4227a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clownamespace { 4327a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow 4427a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow// Converts the given type to an integer based on the AQS specification. 4527a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow// For more details, See http://goto.google.com/binary-clients-logging . 4627a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clowint AutocompleteMatchToAssistedQueryType(const AutocompleteMatch::Type& type) { 476b7c2aeb004cc8e499f1a2281c356bee0bfc9061Marshall Clow switch (type) { 4827a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow case AutocompleteMatch::SEARCH_SUGGEST: return 0; 4927a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow case AutocompleteMatch::NAVSUGGEST: return 5; 5027a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow case AutocompleteMatch::SEARCH_WHAT_YOU_TYPED: return 57; 5127a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow case AutocompleteMatch::URL_WHAT_YOU_TYPED: return 58; 5227a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow case AutocompleteMatch::SEARCH_HISTORY: return 59; 5327a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow case AutocompleteMatch::HISTORY_URL: return 60; 5427a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow case AutocompleteMatch::HISTORY_TITLE: return 61; 5527a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow case AutocompleteMatch::HISTORY_BODY: return 62; 5627a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow case AutocompleteMatch::HISTORY_KEYWORD: return 63; 5727a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow case AutocompleteMatch::BOOKMARK_TITLE: return 65; 5827a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow // NOTE: Default must remain 64 for server-side compatability. 5927a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow default: return 64; 6027a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow } 6127a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow} 6227a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow 6327a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow// Appends available autocompletion of the given type and number to the existing 6427a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow// available autocompletions string, encoding according to the spec. 6527a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clowvoid AppendAvailableAutocompletion(int type, 6627a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow int count, 6727a1c252e3f2c0d1192e1e275ce3489b9f0025f9Marshall Clow std::string* autocompletions) { 68 if (!autocompletions->empty()) 69 autocompletions->append("j"); 70 base::StringAppendF(autocompletions, "%d", type); 71 if (count > 1) 72 base::StringAppendF(autocompletions, "l%d", count); 73} 74 75// Amount of time (in ms) between when the user stops typing and when we remove 76// any copied entries. We do this from the time the user stopped typing as some 77// providers (such as SearchProvider) wait for the user to stop typing before 78// they initiate a query. 79const int kExpireTimeMS = 500; 80 81} // namespace 82 83const int AutocompleteController::kNoItemSelected = -1; 84 85AutocompleteController::AutocompleteController( 86 Profile* profile, 87 AutocompleteControllerDelegate* delegate, 88 int provider_types) 89 : delegate_(delegate), 90 keyword_provider_(NULL), 91 search_provider_(NULL), 92 zero_suggest_provider_(NULL), 93 done_(true), 94 in_start_(false), 95 in_zero_suggest_(false), 96 profile_(profile) { 97 bool use_hqp = !!(provider_types & AutocompleteProvider::TYPE_HISTORY_QUICK); 98 // TODO(mrossetti): Permanently modify the HistoryURLProvider to not search 99 // titles once HQP is turned on permanently. 100 // History quick provider can be used on all platforms other than Android. 101 // TODO(jcivelli): Enable the History Quick Provider and figure out why it 102 // reports the wrong results for some pages. 103#if defined(OS_ANDROID) 104 use_hqp = false; 105#endif 106 107 if (provider_types & AutocompleteProvider::TYPE_BUILTIN) 108 providers_.push_back(new BuiltinProvider(this, profile)); 109#if defined(OS_CHROMEOS) 110 if (provider_types & AutocompleteProvider::TYPE_CONTACT) 111 providers_.push_back(new ContactProvider(this, profile, 112 contacts::ContactManager::GetInstance()->GetWeakPtr())); 113#endif 114 if (provider_types & AutocompleteProvider::TYPE_EXTENSION_APP) 115 providers_.push_back(new ExtensionAppProvider(this, profile)); 116 if (provider_types & AutocompleteProvider::TYPE_HISTORY_CONTENTS) 117 providers_.push_back(new HistoryContentsProvider(this, profile, use_hqp)); 118 if (use_hqp) 119 providers_.push_back(new HistoryQuickProvider(this, profile)); 120 if (provider_types & AutocompleteProvider::TYPE_HISTORY_URL) 121 providers_.push_back(new HistoryURLProvider(this, profile)); 122 // Search provider/"tab to search" can be used on all platforms other than 123 // Android. 124#if !defined(OS_ANDROID) 125 if (provider_types & AutocompleteProvider::TYPE_KEYWORD) { 126 keyword_provider_ = new KeywordProvider(this, profile); 127 providers_.push_back(keyword_provider_); 128 } 129#endif 130 if (provider_types & AutocompleteProvider::TYPE_SEARCH) { 131 search_provider_ = new SearchProvider(this, profile); 132 providers_.push_back(search_provider_); 133 } 134 if (provider_types & AutocompleteProvider::TYPE_SHORTCUTS) 135 providers_.push_back(new ShortcutsProvider(this, profile)); 136 137 // Create ZeroSuggest if it is enabled. 138 if (provider_types & AutocompleteProvider::TYPE_ZERO_SUGGEST) { 139 zero_suggest_provider_ = ZeroSuggestProvider::Create(this, profile); 140 if (zero_suggest_provider_) 141 providers_.push_back(zero_suggest_provider_); 142 } 143 144 if ((provider_types & AutocompleteProvider::TYPE_BOOKMARK) && 145 !CommandLine::ForCurrentProcess()->HasSwitch( 146 switches::kDisableBookmarkAutocompleteProvider)) 147 providers_.push_back(new BookmarkProvider(this, profile)); 148 149 for (ACProviders::iterator i(providers_.begin()); i != providers_.end(); ++i) 150 (*i)->AddRef(); 151} 152 153AutocompleteController::~AutocompleteController() { 154 // The providers may have tasks outstanding that hold refs to them. We need 155 // to ensure they won't call us back if they outlive us. (Practically, 156 // calling Stop() should also cancel those tasks and make it so that we hold 157 // the only refs.) We also don't want to bother notifying anyone of our 158 // result changes here, because the notification observer is in the midst of 159 // shutdown too, so we don't ask Stop() to clear |result_| (and notify). 160 result_.Reset(); // Not really necessary. 161 Stop(false); 162 163 for (ACProviders::iterator i(providers_.begin()); i != providers_.end(); ++i) 164 (*i)->Release(); 165 166 providers_.clear(); // Not really necessary. 167} 168 169void AutocompleteController::Start( 170 const string16& text, 171 const string16& desired_tld, 172 bool prevent_inline_autocomplete, 173 bool prefer_keyword, 174 bool allow_exact_keyword_match, 175 AutocompleteInput::MatchesRequested matches_requested) { 176 const string16 old_input_text(input_.text()); 177 const AutocompleteInput::MatchesRequested old_matches_requested = 178 input_.matches_requested(); 179 input_ = AutocompleteInput(text, desired_tld, prevent_inline_autocomplete, 180 prefer_keyword, allow_exact_keyword_match, matches_requested); 181 182 // See if we can avoid rerunning autocomplete when the query hasn't changed 183 // much. When the user presses or releases the ctrl key, the desired_tld 184 // changes, and when the user finishes an IME composition, inline autocomplete 185 // may no longer be prevented. In both these cases the text itself hasn't 186 // changed since the last query, and some providers can do much less work (and 187 // get matches back more quickly). Taking advantage of this reduces flicker. 188 // 189 // NOTE: This comes after constructing |input_| above since that construction 190 // can change the text string (e.g. by stripping off a leading '?'). 191 const bool minimal_changes = (input_.text() == old_input_text) && 192 (input_.matches_requested() == old_matches_requested); 193 194 expire_timer_.Stop(); 195 196 // Start the new query. 197 in_zero_suggest_ = false; 198 in_start_ = true; 199 base::TimeTicks start_time = base::TimeTicks::Now(); 200 for (ACProviders::iterator i(providers_.begin()); i != providers_.end(); 201 ++i) { 202 (*i)->Start(input_, minimal_changes); 203 if (matches_requested != AutocompleteInput::ALL_MATCHES) 204 DCHECK((*i)->done()); 205 } 206 if (matches_requested == AutocompleteInput::ALL_MATCHES && 207 (text.length() < 6)) { 208 base::TimeTicks end_time = base::TimeTicks::Now(); 209 std::string name = "Omnibox.QueryTime." + base::IntToString(text.length()); 210 base::Histogram* counter = base::Histogram::FactoryGet( 211 name, 1, 1000, 50, base::Histogram::kUmaTargetedHistogramFlag); 212 counter->Add(static_cast<int>((end_time - start_time).InMilliseconds())); 213 } 214 in_start_ = false; 215 CheckIfDone(); 216 UpdateResult(true); 217 218 if (!done_) 219 StartExpireTimer(); 220} 221 222void AutocompleteController::Stop(bool clear_result) { 223 for (ACProviders::const_iterator i(providers_.begin()); i != providers_.end(); 224 ++i) { 225 (*i)->Stop(clear_result); 226 } 227 228 expire_timer_.Stop(); 229 done_ = true; 230 if (clear_result && !result_.empty()) { 231 result_.Reset(); 232 // NOTE: We pass in false since we're trying to only clear the popup, not 233 // touch the edit... this is all a mess and should be cleaned up :( 234 NotifyChanged(false); 235 } 236} 237 238void AutocompleteController::StartZeroSuggest( 239 const GURL& url, 240 const string16& user_text) { 241 if (zero_suggest_provider_ != NULL) { 242 DCHECK(!in_start_); // We should not be already running a query. 243 in_zero_suggest_ = true; 244 zero_suggest_provider_->StartZeroSuggest(url, user_text); 245 } 246} 247 248void AutocompleteController::StopZeroSuggest() { 249 if (zero_suggest_provider_ != NULL) { 250 DCHECK(!in_start_); // We should not be already running a query. 251 zero_suggest_provider_->Stop(false); 252 } 253} 254 255void AutocompleteController::DeleteMatch(const AutocompleteMatch& match) { 256 DCHECK(match.deletable); 257 match.provider->DeleteMatch(match); // This may synchronously call back to 258 // OnProviderUpdate(). 259 // If DeleteMatch resulted in a callback to OnProviderUpdate and we're 260 // not done, we might attempt to redisplay the deleted match. Make sure 261 // we aren't displaying it by removing any old entries. 262 ExpireCopiedEntries(); 263} 264 265void AutocompleteController::ExpireCopiedEntries() { 266 // Clear out the results. This ensures no results from the previous result set 267 // are copied over. 268 result_.Reset(); 269 // We allow matches from the previous result set to starve out matches from 270 // the new result set. This means in order to expire matches we have to query 271 // the providers again. 272 UpdateResult(false); 273} 274 275void AutocompleteController::OnProviderUpdate(bool updated_matches) { 276 if (in_zero_suggest_) { 277 // We got ZeroSuggest results before Start(). Show only those results, 278 // because results from other providers are stale. 279 result_.Reset(); 280 result_.AppendMatches(zero_suggest_provider_->matches()); 281 result_.SortAndCull(input_, profile_); 282 NotifyChanged(true); 283 } else { 284 CheckIfDone(); 285 // Multiple providers may provide synchronous results, so we only update the 286 // results if we're not in Start(). 287 if (!in_start_ && (updated_matches || done_)) 288 UpdateResult(false); 289 } 290} 291 292void AutocompleteController::AddProvidersInfo( 293 ProvidersInfo* provider_info) const { 294 provider_info->clear(); 295 for (ACProviders::const_iterator i(providers_.begin()); i != providers_.end(); 296 ++i) { 297 // Add per-provider info, if any. 298 (*i)->AddProviderInfo(provider_info); 299 300 // This is also a good place to put code to add info that you want to 301 // add for every provider. 302 } 303} 304 305void AutocompleteController::UpdateResult(bool is_synchronous_pass) { 306 AutocompleteResult last_result; 307 last_result.Swap(&result_); 308 309 for (ACProviders::const_iterator i(providers_.begin()); i != providers_.end(); 310 ++i) 311 result_.AppendMatches((*i)->matches()); 312 313 // Sort the matches and trim to a small number of "best" matches. 314 result_.SortAndCull(input_, profile_); 315 316 // Need to validate before invoking CopyOldMatches as the old matches are not 317 // valid against the current input. 318#ifndef NDEBUG 319 result_.Validate(); 320#endif 321 322 if (!done_) { 323 // This conditional needs to match the conditional in Start that invokes 324 // StartExpireTimer. 325 result_.CopyOldMatches(input_, last_result, profile_); 326 } 327 328 UpdateKeywordDescriptions(&result_); 329 UpdateAssociatedKeywords(&result_); 330 UpdateAssistedQueryStats(&result_); 331 332 bool notify_default_match = is_synchronous_pass; 333 if (!is_synchronous_pass) { 334 const bool last_default_was_valid = 335 last_result.default_match() != last_result.end(); 336 const bool default_is_valid = result_.default_match() != result_.end(); 337 // We've gotten async results. Send notification that the default match 338 // updated if fill_into_edit differs or associated_keyword differ. (The 339 // latter can change if we've just started Chrome and the keyword database 340 // finishes loading while processing this request.) We don't check the URL 341 // as that may change for the default match even though the fill into edit 342 // hasn't changed (see SearchProvider for one case of this). 343 notify_default_match = 344 (last_default_was_valid != default_is_valid) || 345 (default_is_valid && 346 ((result_.default_match()->fill_into_edit != 347 last_result.default_match()->fill_into_edit) || 348 (result_.default_match()->associated_keyword.get() != 349 last_result.default_match()->associated_keyword.get()))); 350 } 351 352 NotifyChanged(notify_default_match); 353} 354 355void AutocompleteController::UpdateAssociatedKeywords( 356 AutocompleteResult* result) { 357 if (!keyword_provider_) 358 return; 359 360 std::set<string16> keywords; 361 for (ACMatches::iterator match(result->begin()); match != result->end(); 362 ++match) { 363 string16 keyword(match->GetSubstitutingExplicitlyInvokedKeyword(profile_)); 364 if (!keyword.empty()) { 365 keywords.insert(keyword); 366 } else { 367 string16 keyword = match->associated_keyword.get() ? 368 match->associated_keyword->keyword : 369 keyword_provider_->GetKeywordForText(match->fill_into_edit); 370 371 // Only add the keyword if the match does not have a duplicate keyword 372 // with a more relevant match. 373 if (!keyword.empty() && !keywords.count(keyword)) { 374 keywords.insert(keyword); 375 376 if (!match->associated_keyword.get()) 377 match->associated_keyword.reset(new AutocompleteMatch( 378 keyword_provider_->CreateAutocompleteMatch(match->fill_into_edit, 379 keyword, input_))); 380 } else { 381 match->associated_keyword.reset(); 382 } 383 } 384 } 385} 386 387void AutocompleteController::UpdateAssistedQueryStats( 388 AutocompleteResult* result) { 389 if (result->empty()) 390 return; 391 392 // Build the impressions string (the AQS part after "."). 393 std::string autocompletions; 394 int count = 0; 395 int last_type = -1; 396 for (ACMatches::iterator match(result->begin()); match != result->end(); 397 ++match) { 398 int type = AutocompleteMatchToAssistedQueryType(match->type); 399 if (last_type != -1 && type != last_type) { 400 AppendAvailableAutocompletion(last_type, count, &autocompletions); 401 count = 1; 402 } else { 403 count++; 404 } 405 last_type = type; 406 } 407 AppendAvailableAutocompletion(last_type, count, &autocompletions); 408 409 // Go over all matches and set AQS if the match supports it. 410 for (size_t index = 0; index < result->size(); ++index) { 411 AutocompleteMatch* match = result->match_at(index); 412 const TemplateURL* template_url = match->GetTemplateURL(profile_, false); 413 if (!template_url || !match->search_terms_args.get()) 414 continue; 415 match->search_terms_args->assisted_query_stats = 416 base::StringPrintf("chrome.%" PRIuS ".%s", 417 index, 418 autocompletions.c_str()); 419 match->destination_url = GURL(template_url->url_ref().ReplaceSearchTerms( 420 *match->search_terms_args)); 421 } 422} 423 424void AutocompleteController::UpdateKeywordDescriptions( 425 AutocompleteResult* result) { 426 string16 last_keyword; 427 for (AutocompleteResult::iterator i(result->begin()); i != result->end(); 428 ++i) { 429 if ((i->provider->type() == AutocompleteProvider::TYPE_KEYWORD && 430 !i->keyword.empty()) || 431 (i->provider->type() == AutocompleteProvider::TYPE_SEARCH && 432 AutocompleteMatch::IsSearchType(i->type))) { 433 i->description.clear(); 434 i->description_class.clear(); 435 DCHECK(!i->keyword.empty()); 436 if (i->keyword != last_keyword) { 437 const TemplateURL* template_url = i->GetTemplateURL(profile_, false); 438 if (template_url) { 439 i->description = l10n_util::GetStringFUTF16( 440 IDS_AUTOCOMPLETE_SEARCH_DESCRIPTION, 441 template_url->AdjustedShortNameForLocaleDirection()); 442 i->description_class.push_back( 443 ACMatchClassification(0, ACMatchClassification::DIM)); 444 } 445 last_keyword = i->keyword; 446 } 447 } else { 448 last_keyword.clear(); 449 } 450 } 451} 452 453void AutocompleteController::NotifyChanged(bool notify_default_match) { 454 if (delegate_) 455 delegate_->OnResultChanged(notify_default_match); 456 if (done_) { 457 content::NotificationService::current()->Notify( 458 chrome::NOTIFICATION_AUTOCOMPLETE_CONTROLLER_RESULT_READY, 459 content::Source<AutocompleteController>(this), 460 content::NotificationService::NoDetails()); 461 } 462} 463 464void AutocompleteController::CheckIfDone() { 465 for (ACProviders::const_iterator i(providers_.begin()); i != providers_.end(); 466 ++i) { 467 if (!(*i)->done()) { 468 done_ = false; 469 return; 470 } 471 } 472 done_ = true; 473} 474 475void AutocompleteController::StartExpireTimer() { 476 if (result_.HasCopiedMatches()) 477 expire_timer_.Start(FROM_HERE, 478 base::TimeDelta::FromMilliseconds(kExpireTimeMS), 479 this, &AutocompleteController::ExpireCopiedEntries); 480} 481