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