blob: 7de2ec1a3580bdb565cf2bb5817dc68216e1b314 [file] [log] [blame]
[email protected]eac44992012-02-14 21:39:351// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]9ac40092010-10-27 23:05:262// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]371dab12012-06-01 03:23:555#include "chrome/browser/autocomplete/autocomplete_match.h"
6
[email protected]5281d422012-07-28 21:37:107#include "base/i18n/time_formatting.h"
[email protected]9ac40092010-10-27 23:05:268#include "base/logging.h"
[email protected]5281d422012-07-28 21:37:109#include "base/string16.h"
[email protected]3ea1b182013-02-08 22:38:4110#include "base/string_util.h"
[email protected]5281d422012-07-28 21:37:1011#include "base/stringprintf.h"
[email protected]3ea1b182013-02-08 22:38:4112#include "base/strings/string_number_conversions.h"
[email protected]5281d422012-07-28 21:37:1013#include "base/time.h"
14#include "base/utf_string_conversions.h"
[email protected]30f5bc92012-06-26 04:14:5515#include "chrome/browser/autocomplete/autocomplete_provider.h"
[email protected]033f3422012-03-13 21:24:1816#include "chrome/browser/search_engines/template_url.h"
[email protected]85b8d6f2012-05-08 20:53:4717#include "chrome/browser/search_engines/template_url_service.h"
18#include "chrome/browser/search_engines/template_url_service_factory.h"
[email protected]345e8fda2012-09-06 01:07:4719#include "content/public/common/url_constants.h"
[email protected]9ac40092010-10-27 23:05:2620#include "grit/theme_resources.h"
21
[email protected]3b81314d2012-09-11 02:48:4122namespace {
23
24bool IsTrivialClassification(const ACMatchClassifications& classifications) {
25 return classifications.empty() ||
26 ((classifications.size() == 1) &&
27 (classifications.back().style == ACMatchClassification::NONE));
28}
29
30} // namespace
31
[email protected]9ac40092010-10-27 23:05:2632// AutocompleteMatch ----------------------------------------------------------
33
[email protected]531e0342011-11-10 15:08:4134// static
35const char16 AutocompleteMatch::kInvalidChars[] = {
36 '\n', '\r', '\t',
37 0x2028, // Line separator
38 0x2029, // Paragraph separator
39 0
40};
41
[email protected]9ac40092010-10-27 23:05:2642AutocompleteMatch::AutocompleteMatch()
43 : provider(NULL),
44 relevance(0),
[email protected]cf6256f2012-06-12 23:36:0145 typed_count(-1),
[email protected]9ac40092010-10-27 23:05:2646 deletable(false),
[email protected]a2fedb1e2011-01-25 15:23:3647 inline_autocomplete_offset(string16::npos),
[email protected]2905f742011-10-13 03:51:5848 transition(content::PAGE_TRANSITION_GENERATED),
[email protected]9ac40092010-10-27 23:05:2649 is_history_what_you_typed_match(false),
50 type(SEARCH_WHAT_YOU_TYPED),
[email protected]7abfb3462011-01-31 16:43:0651 starred(false),
52 from_previous(false) {
[email protected]9ac40092010-10-27 23:05:2653}
54
55AutocompleteMatch::AutocompleteMatch(AutocompleteProvider* provider,
56 int relevance,
57 bool deletable,
58 Type type)
59 : provider(provider),
60 relevance(relevance),
[email protected]cf6256f2012-06-12 23:36:0161 typed_count(-1),
[email protected]9ac40092010-10-27 23:05:2662 deletable(deletable),
[email protected]a2fedb1e2011-01-25 15:23:3663 inline_autocomplete_offset(string16::npos),
[email protected]2905f742011-10-13 03:51:5864 transition(content::PAGE_TRANSITION_TYPED),
[email protected]9ac40092010-10-27 23:05:2665 is_history_what_you_typed_match(false),
66 type(type),
[email protected]7abfb3462011-01-31 16:43:0667 starred(false),
68 from_previous(false) {
[email protected]9ac40092010-10-27 23:05:2669}
70
[email protected]3cb0f8d92012-02-29 05:43:3471AutocompleteMatch::AutocompleteMatch(const AutocompleteMatch& match)
72 : provider(match.provider),
73 relevance(match.relevance),
[email protected]cf6256f2012-06-12 23:36:0174 typed_count(match.typed_count),
[email protected]3cb0f8d92012-02-29 05:43:3475 deletable(match.deletable),
76 fill_into_edit(match.fill_into_edit),
77 inline_autocomplete_offset(match.inline_autocomplete_offset),
78 destination_url(match.destination_url),
79 stripped_destination_url(match.stripped_destination_url),
80 contents(match.contents),
81 contents_class(match.contents_class),
82 description(match.description),
83 description_class(match.description_class),
84 transition(match.transition),
85 is_history_what_you_typed_match(match.is_history_what_you_typed_match),
86 type(match.type),
[email protected]bca359b2012-06-24 07:53:0487 associated_keyword(match.associated_keyword.get() ?
88 new AutocompleteMatch(*match.associated_keyword) : NULL),
[email protected]3cb0f8d92012-02-29 05:43:3489 keyword(match.keyword),
[email protected]3cb0f8d92012-02-29 05:43:3490 starred(match.starred),
[email protected]bca359b2012-06-24 07:53:0491 from_previous(match.from_previous),
92 search_terms_args(match.search_terms_args.get() ?
93 new TemplateURLRef::SearchTermsArgs(*match.search_terms_args) :
[email protected]5281d422012-07-28 21:37:1094 NULL),
95 additional_info(match.additional_info) {
[email protected]3cb0f8d92012-02-29 05:43:3496}
97
[email protected]9ac40092010-10-27 23:05:2698AutocompleteMatch::~AutocompleteMatch() {
99}
100
[email protected]3cb0f8d92012-02-29 05:43:34101AutocompleteMatch& AutocompleteMatch::operator=(
102 const AutocompleteMatch& match) {
103 if (this == &match)
104 return *this;
105
106 provider = match.provider;
107 relevance = match.relevance;
[email protected]cf6256f2012-06-12 23:36:01108 typed_count = match.typed_count;
[email protected]3cb0f8d92012-02-29 05:43:34109 deletable = match.deletable;
110 fill_into_edit = match.fill_into_edit;
111 inline_autocomplete_offset = match.inline_autocomplete_offset;
112 destination_url = match.destination_url;
113 stripped_destination_url = match.stripped_destination_url;
114 contents = match.contents;
115 contents_class = match.contents_class;
116 description = match.description;
117 description_class = match.description_class;
118 transition = match.transition;
119 is_history_what_you_typed_match = match.is_history_what_you_typed_match;
120 type = match.type;
121 associated_keyword.reset(match.associated_keyword.get() ?
122 new AutocompleteMatch(*match.associated_keyword) : NULL);
123 keyword = match.keyword;
[email protected]3cb0f8d92012-02-29 05:43:34124 starred = match.starred;
125 from_previous = match.from_previous;
[email protected]bca359b2012-06-24 07:53:04126 search_terms_args.reset(match.search_terms_args.get() ?
127 new TemplateURLRef::SearchTermsArgs(*match.search_terms_args) : NULL);
[email protected]5281d422012-07-28 21:37:10128 additional_info = match.additional_info;
[email protected]3cb0f8d92012-02-29 05:43:34129 return *this;
130}
131
[email protected]9ac40092010-10-27 23:05:26132// static
133std::string AutocompleteMatch::TypeToString(Type type) {
[email protected]fc65f272011-06-28 22:21:30134 const char* strings[] = {
[email protected]9ac40092010-10-27 23:05:26135 "url-what-you-typed",
136 "history-url",
137 "history-title",
138 "history-body",
139 "history-keyword",
140 "navsuggest",
141 "search-what-you-typed",
142 "search-history",
143 "search-suggest",
144 "search-other-engine",
[email protected]8f7405482011-04-13 11:08:52145 "extension-app",
[email protected]dbacefb2012-09-12 03:32:06146 "contact",
[email protected]25320602012-10-18 22:05:56147 "bookmark-title",
[email protected]9ac40092010-10-27 23:05:26148 };
[email protected]fc65f272011-06-28 22:21:30149 COMPILE_ASSERT(arraysize(strings) == NUM_TYPES,
150 strings_array_must_match_type_enum);
[email protected]9ac40092010-10-27 23:05:26151 return strings[type];
152}
153
154// static
155int AutocompleteMatch::TypeToIcon(Type type) {
[email protected]fc65f272011-06-28 22:21:30156 int icons[] = {
[email protected]9ac40092010-10-27 23:05:26157 IDR_OMNIBOX_HTTP,
158 IDR_OMNIBOX_HTTP,
[email protected]c37ad9e2012-06-12 04:57:13159 IDR_OMNIBOX_HTTP,
160 IDR_OMNIBOX_HTTP,
161 IDR_OMNIBOX_HTTP,
[email protected]9ac40092010-10-27 23:05:26162 IDR_OMNIBOX_HTTP,
163 IDR_OMNIBOX_SEARCH,
164 IDR_OMNIBOX_SEARCH,
165 IDR_OMNIBOX_SEARCH,
166 IDR_OMNIBOX_SEARCH,
[email protected]8f7405482011-04-13 11:08:52167 IDR_OMNIBOX_EXTENSION_APP,
[email protected]dbacefb2012-09-12 03:32:06168 // ContactProvider isn't used by the omnibox, so this icon is never
169 // displayed.
170 IDR_OMNIBOX_SEARCH,
[email protected]25320602012-10-18 22:05:56171 IDR_OMNIBOX_HTTP,
[email protected]9ac40092010-10-27 23:05:26172 };
[email protected]fc65f272011-06-28 22:21:30173 COMPILE_ASSERT(arraysize(icons) == NUM_TYPES,
174 icons_array_must_match_type_enum);
[email protected]9ac40092010-10-27 23:05:26175 return icons[type];
176}
177
178// static
[email protected]dd2f9e32012-09-19 14:23:40179int AutocompleteMatch::TypeToLocationBarIcon(Type type) {
180 int id = TypeToIcon(type);
181 if (id == IDR_OMNIBOX_HTTP)
182 return IDR_LOCATION_BAR_HTTP;
183 return id;
184}
185
186// static
[email protected]9ac40092010-10-27 23:05:26187bool AutocompleteMatch::MoreRelevant(const AutocompleteMatch& elem1,
188 const AutocompleteMatch& elem2) {
189 // For equal-relevance matches, we sort alphabetically, so that providers
190 // who return multiple elements at the same priority get a "stable" sort
191 // across multiple updates.
[email protected]033f3422012-03-13 21:24:18192 return (elem1.relevance == elem2.relevance) ?
193 (elem1.contents < elem2.contents) : (elem1.relevance > elem2.relevance);
[email protected]9ac40092010-10-27 23:05:26194}
195
196// static
197bool AutocompleteMatch::DestinationSortFunc(const AutocompleteMatch& elem1,
198 const AutocompleteMatch& elem2) {
199 // Sort identical destination_urls together. Place the most relevant matches
200 // first, so that when we call std::unique(), these are the ones that get
201 // preserved.
[email protected]00193bf2012-09-15 14:52:50202 if (DestinationsEqual(elem1, elem2) ||
203 (elem1.stripped_destination_url.is_empty() &&
204 elem2.stripped_destination_url.is_empty()))
205 return MoreRelevant(elem1, elem2);
206 return elem1.stripped_destination_url < elem2.stripped_destination_url;
[email protected]9ac40092010-10-27 23:05:26207}
208
209// static
210bool AutocompleteMatch::DestinationsEqual(const AutocompleteMatch& elem1,
211 const AutocompleteMatch& elem2) {
[email protected]00193bf2012-09-15 14:52:50212 if (elem1.stripped_destination_url.is_empty() &&
213 elem2.stripped_destination_url.is_empty())
214 return false;
[email protected]3cb0f8d92012-02-29 05:43:34215 return elem1.stripped_destination_url == elem2.stripped_destination_url;
[email protected]9ac40092010-10-27 23:05:26216}
217
218// static
219void AutocompleteMatch::ClassifyMatchInString(
[email protected]a2fedb1e2011-01-25 15:23:36220 const string16& find_text,
221 const string16& text,
[email protected]9ac40092010-10-27 23:05:26222 int style,
223 ACMatchClassifications* classification) {
224 ClassifyLocationInString(text.find(find_text), find_text.length(),
225 text.length(), style, classification);
226}
227
[email protected]24d692aa2011-05-25 23:07:58228// static
[email protected]9ac40092010-10-27 23:05:26229void AutocompleteMatch::ClassifyLocationInString(
230 size_t match_location,
231 size_t match_length,
232 size_t overall_length,
233 int style,
234 ACMatchClassifications* classification) {
235 classification->clear();
236
237 // Don't classify anything about an empty string
238 // (AutocompleteMatch::Validate() checks this).
239 if (overall_length == 0)
240 return;
241
242 // Mark pre-match portion of string (if any).
243 if (match_location != 0) {
244 classification->push_back(ACMatchClassification(0, style));
245 }
246
247 // Mark matching portion of string.
[email protected]a2fedb1e2011-01-25 15:23:36248 if (match_location == string16::npos) {
[email protected]9ac40092010-10-27 23:05:26249 // No match, above classification will suffice for whole string.
250 return;
251 }
252 // Classifying an empty match makes no sense and will lead to validation
253 // errors later.
[email protected]eac44992012-02-14 21:39:35254 DCHECK_GT(match_length, 0U);
[email protected]9ac40092010-10-27 23:05:26255 classification->push_back(ACMatchClassification(match_location,
256 (style | ACMatchClassification::MATCH) & ~ACMatchClassification::DIM));
257
258 // Mark post-match portion of string (if any).
259 const size_t after_match(match_location + match_length);
260 if (after_match < overall_length) {
261 classification->push_back(ACMatchClassification(after_match, style));
262 }
263}
264
[email protected]5595f40d2011-10-28 17:29:18265// static
[email protected]3b81314d2012-09-11 02:48:41266AutocompleteMatch::ACMatchClassifications
267 AutocompleteMatch::MergeClassifications(
268 const ACMatchClassifications& classifications1,
269 const ACMatchClassifications& classifications2) {
270 // We must return the empty vector only if both inputs are truly empty.
271 // The result of merging an empty vector with a single (0, NONE)
272 // classification is the latter one-entry vector.
273 if (IsTrivialClassification(classifications1))
274 return classifications2.empty() ? classifications1 : classifications2;
275 if (IsTrivialClassification(classifications2))
276 return classifications1;
277
278 ACMatchClassifications output;
279 for (ACMatchClassifications::const_iterator i = classifications1.begin(),
280 j = classifications2.begin(); i != classifications1.end();) {
281 AutocompleteMatch::AddLastClassificationIfNecessary(&output,
282 std::max(i->offset, j->offset), i->style | j->style);
283 const size_t next_i_offset = (i + 1) == classifications1.end() ?
284 static_cast<size_t>(-1) : (i + 1)->offset;
285 const size_t next_j_offset = (j + 1) == classifications2.end() ?
286 static_cast<size_t>(-1) : (j + 1)->offset;
287 if (next_i_offset >= next_j_offset)
288 ++j;
289 if (next_j_offset >= next_i_offset)
290 ++i;
291 }
292
293 return output;
294}
295
296// static
[email protected]9d2b5f3b2012-03-14 21:34:32297std::string AutocompleteMatch::ClassificationsToString(
298 const ACMatchClassifications& classifications) {
299 std::string serialized_classifications;
300 for (size_t i = 0; i < classifications.size(); ++i) {
301 if (i)
302 serialized_classifications += ',';
303 serialized_classifications += base::IntToString(classifications[i].offset) +
304 ',' + base::IntToString(classifications[i].style);
305 }
306 return serialized_classifications;
307}
308
309// static
310ACMatchClassifications AutocompleteMatch::ClassificationsFromString(
311 const std::string& serialized_classifications) {
312 ACMatchClassifications classifications;
313 std::vector<std::string> tokens;
314 Tokenize(serialized_classifications, ",", &tokens);
315 DCHECK(!(tokens.size() & 1)); // The number of tokens should be even.
316 for (size_t i = 0; i < tokens.size(); i += 2) {
317 int classification_offset = 0;
318 int classification_style = ACMatchClassification::NONE;
319 if (!base::StringToInt(tokens[i], &classification_offset) ||
320 !base::StringToInt(tokens[i + 1], &classification_style)) {
321 NOTREACHED();
322 return classifications;
323 }
324 classifications.push_back(ACMatchClassification(classification_offset,
325 classification_style));
326 }
327 return classifications;
328}
329
330// static
331void AutocompleteMatch::AddLastClassificationIfNecessary(
332 ACMatchClassifications* classifications,
333 size_t offset,
334 int style) {
335 DCHECK(classifications);
336 if (classifications->empty() || classifications->back().style != style) {
337 DCHECK(classifications->empty() ||
338 (offset > classifications->back().offset));
339 classifications->push_back(ACMatchClassification(offset, style));
340 }
341}
342
343// static
[email protected]5595f40d2011-10-28 17:29:18344string16 AutocompleteMatch::SanitizeString(const string16& text) {
345 // NOTE: This logic is mirrored by |sanitizeString()| in
[email protected]83820d42011-11-12 22:03:11346 // schema_generated_bindings.js.
[email protected]5595f40d2011-10-28 17:29:18347 string16 result;
348 TrimWhitespace(text, TRIM_LEADING, &result);
[email protected]531e0342011-11-10 15:08:41349 RemoveChars(result, kInvalidChars, &result);
[email protected]5595f40d2011-10-28 17:29:18350 return result;
351}
352
[email protected]749e7ae02012-09-05 18:47:46353// static
354bool AutocompleteMatch::IsSearchType(Type type) {
355 return type == SEARCH_WHAT_YOU_TYPED ||
356 type == SEARCH_HISTORY ||
357 type == SEARCH_SUGGEST ||
358 type == SEARCH_OTHER_ENGINE;
359}
360
[email protected]dbff446582012-10-30 00:20:26361void AutocompleteMatch::ComputeStrippedDestinationURL(Profile* profile) {
[email protected]345e8fda2012-09-06 01:07:47362 stripped_destination_url = destination_url;
363 if (!stripped_destination_url.is_valid())
364 return;
365
[email protected]dbff446582012-10-30 00:20:26366 // If the destination URL looks like it was generated from a TemplateURL,
367 // remove all substitutions other than the search terms. This allows us
368 // to eliminate cases like past search URLs from history that differ only
369 // by some obscure query param from each other or from the search/keyword
370 // provider matches.
371 TemplateURL* template_url = GetTemplateURL(profile, true);
372 if (template_url != NULL && template_url->SupportsReplacement()) {
373 string16 search_terms;
374 if (template_url->ExtractSearchTermsFromURL(stripped_destination_url,
375 &search_terms)) {
376 stripped_destination_url =
377 GURL(template_url->url_ref().ReplaceSearchTerms(
378 TemplateURLRef::SearchTermsArgs(search_terms)));
379 }
380 }
381
[email protected]345e8fda2012-09-06 01:07:47382 // |replacements| keeps all the substitions we're going to make to
383 // from {destination_url} to {stripped_destination_url}. |need_replacement|
384 // is a helper variable that helps us keep track of whether we need
385 // to apply the replacement.
386 bool needs_replacement = false;
387 GURL::Replacements replacements;
388
389 // Remove the www. prefix from the host.
[email protected]3cb0f8d92012-02-29 05:43:34390 static const char prefix[] = "www.";
391 static const size_t prefix_len = arraysize(prefix) - 1;
[email protected]dbff446582012-10-30 00:20:26392 std::string host = stripped_destination_url.host();
[email protected]345e8fda2012-09-06 01:07:47393 if (host.compare(0, prefix_len, prefix) == 0) {
[email protected]3cb0f8d92012-02-29 05:43:34394 host = host.substr(prefix_len);
[email protected]345e8fda2012-09-06 01:07:47395 replacements.SetHostStr(host);
396 needs_replacement = true;
[email protected]3cb0f8d92012-02-29 05:43:34397 }
[email protected]345e8fda2012-09-06 01:07:47398
399 // Replace https protocol with http protocol.
400 if (stripped_destination_url.SchemeIs(chrome::kHttpsScheme)) {
401 replacements.SetScheme(
402 chrome::kHttpScheme,
403 url_parse::Component(0, strlen(chrome::kHttpScheme)));
404 needs_replacement = true;
405 }
406
407 if (needs_replacement)
[email protected]dbff446582012-10-30 00:20:26408 stripped_destination_url = stripped_destination_url.ReplaceComponents(
409 replacements);
[email protected]3cb0f8d92012-02-29 05:43:34410}
411
[email protected]85b8d6f2012-05-08 20:53:47412void AutocompleteMatch::GetKeywordUIState(Profile* profile,
413 string16* keyword,
[email protected]033f3422012-03-13 21:24:18414 bool* is_keyword_hint) const {
415 *is_keyword_hint = associated_keyword.get() != NULL;
416 keyword->assign(*is_keyword_hint ? associated_keyword->keyword :
[email protected]85b8d6f2012-05-08 20:53:47417 GetSubstitutingExplicitlyInvokedKeyword(profile));
[email protected]033f3422012-03-13 21:24:18418}
419
[email protected]85b8d6f2012-05-08 20:53:47420string16 AutocompleteMatch::GetSubstitutingExplicitlyInvokedKeyword(
421 Profile* profile) const {
[email protected]9b74ab52012-03-30 16:08:07422 if (transition != content::PAGE_TRANSITION_KEYWORD)
423 return string16();
[email protected]dbff446582012-10-30 00:20:26424 const TemplateURL* t_url = GetTemplateURL(profile, false);
[email protected]9b74ab52012-03-30 16:08:07425 return (t_url && t_url->SupportsReplacement()) ? keyword : string16();
[email protected]033f3422012-03-13 21:24:18426}
427
[email protected]dbff446582012-10-30 00:20:26428TemplateURL* AutocompleteMatch::GetTemplateURL(
429 Profile* profile, bool allow_fallback_to_destination_host) const {
[email protected]85b8d6f2012-05-08 20:53:47430 DCHECK(profile);
[email protected]dbff446582012-10-30 00:20:26431 TemplateURLService* template_url_service =
432 TemplateURLServiceFactory::GetForProfile(profile);
433 if (template_url_service == NULL)
434 return NULL;
435 TemplateURL* template_url = keyword.empty() ? NULL :
436 template_url_service->GetTemplateURLForKeyword(keyword);
437 if (template_url == NULL && allow_fallback_to_destination_host) {
438 template_url = template_url_service->GetTemplateURLForHost(
439 destination_url.host());
440 }
441 return template_url;
[email protected]3cb0f8d92012-02-29 05:43:34442}
443
[email protected]5281d422012-07-28 21:37:10444void AutocompleteMatch::RecordAdditionalInfo(const std::string& property,
445 const std::string& value) {
446 DCHECK(property.size());
447 DCHECK(value.size());
448 additional_info[property] = value;
449}
450
451void AutocompleteMatch::RecordAdditionalInfo(const std::string& property,
452 int value) {
453 RecordAdditionalInfo(property, StringPrintf("%d", value));
454}
455
456void AutocompleteMatch::RecordAdditionalInfo(const std::string& property,
457 const base::Time& value) {
458 RecordAdditionalInfo(property,
459 UTF16ToUTF8(base::TimeFormatShortDateAndTime(value)));
460}
461
[email protected]9ac40092010-10-27 23:05:26462#ifndef NDEBUG
463void AutocompleteMatch::Validate() const {
464 ValidateClassifications(contents, contents_class);
465 ValidateClassifications(description, description_class);
466}
467
468void AutocompleteMatch::ValidateClassifications(
[email protected]a2fedb1e2011-01-25 15:23:36469 const string16& text,
[email protected]9ac40092010-10-27 23:05:26470 const ACMatchClassifications& classifications) const {
471 if (text.empty()) {
[email protected]37b95732011-05-26 23:11:09472 DCHECK(classifications.empty());
[email protected]9ac40092010-10-27 23:05:26473 return;
474 }
475
476 // The classifications should always cover the whole string.
[email protected]eac44992012-02-14 21:39:35477 DCHECK(!classifications.empty()) << "No classification for \"" << text << '"';
478 DCHECK_EQ(0U, classifications[0].offset)
479 << "Classification misses beginning for \"" << text << '"';
[email protected]9ac40092010-10-27 23:05:26480 if (classifications.size() == 1)
481 return;
482
483 // The classifications should always be sorted.
484 size_t last_offset = classifications[0].offset;
485 for (ACMatchClassifications::const_iterator i(classifications.begin() + 1);
486 i != classifications.end(); ++i) {
[email protected]35f1f4f02012-09-11 13:17:00487 const char* provider_name = provider ? provider->GetName() : "None";
[email protected]eac44992012-02-14 21:39:35488 DCHECK_GT(i->offset, last_offset)
[email protected]6d478b12012-06-06 12:19:34489 << " Classification for \"" << text << "\" with offset of " << i->offset
490 << " is unsorted in relation to last offset of " << last_offset
[email protected]35f1f4f02012-09-11 13:17:00491 << ". Provider: " << provider_name << ".";
[email protected]eac44992012-02-14 21:39:35492 DCHECK_LT(i->offset, text.length())
[email protected]6d478b12012-06-06 12:19:34493 << " Classification of [" << i->offset << "," << text.length()
494 << "] is out of bounds for \"" << text << "\". Provider: "
[email protected]35f1f4f02012-09-11 13:17:00495 << provider_name << ".";
[email protected]9ac40092010-10-27 23:05:26496 last_offset = i->offset;
497 }
498}
499#endif