diff options
Diffstat (limited to 'chrome/browser')
-rw-r--r-- | chrome/browser/extensions/extension_omnibox_api.cc | 122 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_omnibox_api.h | 9 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_omnibox_unittest.cc | 144 |
3 files changed, 234 insertions, 41 deletions
diff --git a/chrome/browser/extensions/extension_omnibox_api.cc b/chrome/browser/extensions/extension_omnibox_api.cc index 93b44bd..705771f 100644 --- a/chrome/browser/extensions/extension_omnibox_api.cc +++ b/chrome/browser/extensions/extension_omnibox_api.cc @@ -30,6 +30,7 @@ const char kSuggestionDescription[] = "description"; const char kSuggestionDescriptionStyles[] = "descriptionStyles"; const char kDescriptionStylesType[] = "type"; const char kDescriptionStylesOffset[] = "offset"; +const char kDescriptionStylesLength[] = "length"; }; // namespace // static @@ -103,47 +104,10 @@ bool OmniboxSendSuggestionsFunction::RunImpl() { ListValue* styles; EXTENSION_FUNCTION_VALIDATE( suggestion_value->GetList(kSuggestionDescriptionStyles, &styles)); - + EXTENSION_FUNCTION_VALIDATE(suggestion.ReadStylesFromValue(*styles)); + } else { suggestion.description_styles.clear(); - - int last_offset = -1; - for (size_t j = 0; j < styles->GetSize(); ++j) { - DictionaryValue* style; - std::string type; - int offset; - EXTENSION_FUNCTION_VALIDATE(styles->GetDictionary(j, &style)); - EXTENSION_FUNCTION_VALIDATE( - style->GetString(kDescriptionStylesType, &type)); - EXTENSION_FUNCTION_VALIDATE( - style->GetInteger(kDescriptionStylesOffset, &offset)); - - int type_class = - (type == "none") ? ACMatchClassification::NONE : - (type == "url") ? ACMatchClassification::URL : - (type == "match") ? ACMatchClassification::MATCH : - (type == "dim") ? ACMatchClassification::DIM : -1; - EXTENSION_FUNCTION_VALIDATE(type_class != -1); - - if (offset <= last_offset) { - error_ = kDescriptionStylesOrderError; - return false; - } - if (static_cast<size_t>(offset) >= suggestion.description.length()) { - error_ = kDescriptionStylesLengthError; - return false; - } - - suggestion.description_styles.push_back( - ACMatchClassification(offset, type_class)); - last_offset = offset; - } - } - - // Ensure the styles cover the whole range of text. - if (suggestion.description_styles.empty() || - suggestion.description_styles[0].offset != 0) { - suggestion.description_styles.insert( - suggestion.description_styles.begin(), + suggestion.description_styles.push_back( ACMatchClassification(0, ACMatchClassification::NONE)); } } @@ -160,7 +124,83 @@ ExtensionOmniboxSuggestion::ExtensionOmniboxSuggestion() {} ExtensionOmniboxSuggestion::~ExtensionOmniboxSuggestion() {} +bool ExtensionOmniboxSuggestion::ReadStylesFromValue( + const ListValue& styles_value) { + // Start with a NONE style covering the entire range. As we iterate over the + // styles, bisect or overwrite the running style list with each new style. + description_styles.clear(); + description_styles.push_back( + ACMatchClassification(0, ACMatchClassification::NONE)); + + for (size_t i = 0; i < styles_value.GetSize(); ++i) { + DictionaryValue* style; + std::string type; + int offset; + int length; + if (!styles_value.GetDictionary(i, &style)) + return false; + if (!style->GetString(kDescriptionStylesType, &type)) + return false; + if (!style->GetInteger(kDescriptionStylesOffset, &offset)) + return false; + if (!style->GetInteger(kDescriptionStylesLength, &length)) + return false; + + int type_class = + (type == "url") ? ACMatchClassification::URL : + (type == "match") ? ACMatchClassification::MATCH : + (type == "dim") ? ACMatchClassification::DIM : -1; + if (type_class == -1) + return false; + + InsertNewStyle(type_class, offset, length); + } + + return true; +} + +void ExtensionOmniboxSuggestion::InsertNewStyle(int type, + size_t offset, + size_t length) { + // Find the first and last existing styles that this new style overlaps. Those + // will have to be removed (if they completely overlap), or bisected. + size_t start = 0, end = 0; + while (end < description_styles.size()) { + if (start < description_styles.size() && + description_styles[start].offset < offset) + ++start; + if (description_styles[end].offset <= offset + length) { + ++end; + } else { + break; + } + } + + DCHECK_GT(end, 0u); + DCHECK(start == description_styles.size() || + description_styles[start].offset >= offset); + DCHECK(end == description_styles.size() || + description_styles[end].offset > offset + length); + + // The last style in the overlapping range will come after our new style. + int last_style = description_styles[end - 1].style; + + // Remove all overlapping styles. + if (start < end) { + description_styles.erase(description_styles.begin() + start, + description_styles.begin() + end); + } + + // Insert our new style. + description_styles.insert(description_styles.begin() + start, + ACMatchClassification(offset, type)); + if (offset + length < description.length()) { + description_styles.insert(description_styles.begin() + start + 1, + ACMatchClassification(offset + length, + last_style)); + } +} + ExtensionOmniboxSuggestions::ExtensionOmniboxSuggestions() : request_id(0) {} ExtensionOmniboxSuggestions::~ExtensionOmniboxSuggestions() {} - diff --git a/chrome/browser/extensions/extension_omnibox_api.h b/chrome/browser/extensions/extension_omnibox_api.h index 9d0dc99..98758cb 100644 --- a/chrome/browser/extensions/extension_omnibox_api.h +++ b/chrome/browser/extensions/extension_omnibox_api.h @@ -49,6 +49,10 @@ struct ExtensionOmniboxSuggestion { ExtensionOmniboxSuggestion(); ~ExtensionOmniboxSuggestion(); + // Converts a list of style ranges from the extension into the format expected + // by the autocomplete system. + bool ReadStylesFromValue(const ListValue& value); + // The text that gets put in the edit box. string16 content; @@ -57,6 +61,11 @@ struct ExtensionOmniboxSuggestion { // Contains style ranges for the description. ACMatchClassifications description_styles; + + private: + // Helper function to add the given style to the running list of + // |description_styles|. + void InsertNewStyle(int type, size_t offset, size_t length); }; struct ExtensionOmniboxSuggestions { diff --git a/chrome/browser/extensions/extension_omnibox_unittest.cc b/chrome/browser/extensions/extension_omnibox_unittest.cc new file mode 100644 index 0000000..ca2d1e0 --- /dev/null +++ b/chrome/browser/extensions/extension_omnibox_unittest.cc @@ -0,0 +1,144 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/values.h" +#include "chrome/browser/extensions/extension_omnibox_api.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +namespace { + +void AppendStyle(const std::string& type, + int offset, int length, + ListValue* styles) { + DictionaryValue* style = new DictionaryValue; + style->SetString("type", type); + style->SetInteger("offset", offset); + style->SetInteger("length", length); + styles->Append(style); +} + +void CompareClassification(const ACMatchClassifications& expected, + const ACMatchClassifications& actual) { + EXPECT_EQ(expected.size(), actual.size()); + for (size_t i = 0; i < expected.size() && i < actual.size(); ++i) { + EXPECT_EQ(expected[i].offset, actual[i].offset) << "Index:" << i; + EXPECT_EQ(expected[i].style, actual[i].style) << "Index:" << i; + } +} + +} // namespace + +// Test output key: n = character with no styling, d = dim, m = match, u = url + +// 0123456789 +// mmmm +// + ddd +// = nmmmmndddn +TEST(ExtensionOmniboxTest, DescriptionStylesSimple) { + ListValue styles_value; + AppendStyle("match", 1, 4, &styles_value); + AppendStyle("dim", 6, 3, &styles_value); + + ACMatchClassifications styles_expected; + styles_expected.push_back( + ACMatchClassification(0, ACMatchClassification::NONE)); + styles_expected.push_back( + ACMatchClassification(1, ACMatchClassification::MATCH)); + styles_expected.push_back( + ACMatchClassification(5, ACMatchClassification::NONE)); + styles_expected.push_back( + ACMatchClassification(6, ACMatchClassification::DIM)); + styles_expected.push_back( + ACMatchClassification(9, ACMatchClassification::NONE)); + + ExtensionOmniboxSuggestion suggestions; + suggestions.description.resize(10); + EXPECT_TRUE(suggestions.ReadStylesFromValue(styles_value)); + CompareClassification(styles_expected, suggestions.description_styles); + + // Same input, but swap the order. Ensure it still works. + styles_value.Clear(); + AppendStyle("dim", 6, 3, &styles_value); + AppendStyle("match", 1, 4, &styles_value); + EXPECT_TRUE(suggestions.ReadStylesFromValue(styles_value)); + CompareClassification(styles_expected, suggestions.description_styles); +} + +// 0123456789 +// uuuuuu +// + dd +// + mm +// + mmmm +// + dd +// = mddmunnnnm +TEST(ExtensionOmniboxTest, DescriptionStylesOverlap) { + ListValue styles_value; + AppendStyle("url", 0, 5, &styles_value); + AppendStyle("dim", 9, 2, &styles_value); + AppendStyle("match", 9, 2, &styles_value); + AppendStyle("match", 0, 4, &styles_value); + AppendStyle("dim", 1, 2, &styles_value); + + ACMatchClassifications styles_expected; + styles_expected.push_back( + ACMatchClassification(0, ACMatchClassification::MATCH)); + styles_expected.push_back( + ACMatchClassification(1, ACMatchClassification::DIM)); + styles_expected.push_back( + ACMatchClassification(3, ACMatchClassification::MATCH)); + styles_expected.push_back( + ACMatchClassification(4, ACMatchClassification::URL)); + styles_expected.push_back( + ACMatchClassification(5, ACMatchClassification::NONE)); + styles_expected.push_back( + ACMatchClassification(9, ACMatchClassification::MATCH)); + + ExtensionOmniboxSuggestion suggestions; + suggestions.description.resize(10); + EXPECT_TRUE(suggestions.ReadStylesFromValue(styles_value)); + CompareClassification(styles_expected, suggestions.description_styles); + + // Try moving the "dim/match" style pair at offset 9. Output should be the + // same. + styles_value.Clear(); + AppendStyle("url", 0, 5, &styles_value); + AppendStyle("match", 0, 4, &styles_value); + AppendStyle("dim", 9, 2, &styles_value); + AppendStyle("match", 9, 2, &styles_value); + AppendStyle("dim", 1, 2, &styles_value); + EXPECT_TRUE(suggestions.ReadStylesFromValue(styles_value)); + CompareClassification(styles_expected, suggestions.description_styles); +} + +// 0123456789 +// uuuuu +// + mmmmm +// + mmm +// + ddd +// + ddd +// = dddddnnnnn +TEST(ExtensionOmniboxTest, DescriptionStylesOverlap2) { + ListValue styles_value; + AppendStyle("url", 0, 5, &styles_value); + AppendStyle("match", 0, 5, &styles_value); + AppendStyle("match", 0, 3, &styles_value); + AppendStyle("dim", 2, 3, &styles_value); + AppendStyle("dim", 0, 3, &styles_value); + + // We don't merge adjacent identical styles, but the autocomplete system + // doesn't mind. + ACMatchClassifications styles_expected; + styles_expected.push_back( + ACMatchClassification(0, ACMatchClassification::DIM)); + styles_expected.push_back( + ACMatchClassification(3, ACMatchClassification::DIM)); + styles_expected.push_back( + ACMatchClassification(5, ACMatchClassification::NONE)); + + ExtensionOmniboxSuggestion suggestions; + suggestions.description.resize(10); + EXPECT_TRUE(suggestions.ReadStylesFromValue(styles_value)); + CompareClassification(styles_expected, suggestions.description_styles); +} |