15f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
25f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// found in the LICENSE file.
45f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
55f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "content/shell/renderer/test_runner/mock_spell_check.h"
65f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
75f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "base/logging.h"
85f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "content/shell/renderer/test_runner/test_common.h"
95f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "third_party/WebKit/public/platform/WebCString.h"
105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)namespace content {
125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)namespace {
145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)void Append(blink::WebVector<blink::WebString>* data,
165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            const blink::WebString& item) {
175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  blink::WebVector<blink::WebString> result(data->size() + 1);
185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  for (size_t i = 0; i < data->size(); ++i)
195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    result[i] = (*data)[i];
205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  result[data->size()] = item;
215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  data->swap(result);
225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}  // namespace
255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)MockSpellCheck::MockSpellCheck() : initialized_(false) {
275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)MockSpellCheck::~MockSpellCheck() {
305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)bool MockSpellCheck::SpellCheckWord(const blink::WebString& text,
335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                    int* misspelled_offset,
345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                    int* misspelled_length) {
355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  DCHECK(misspelled_offset);
365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  DCHECK(misspelled_length);
375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Initialize this spellchecker.
395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  InitializeIfNeeded();
405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Reset the result values as our spellchecker does.
425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  *misspelled_offset = 0;
435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  *misspelled_length = 0;
445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Convert to a base::string16 because we store base::string16 instances in
465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // misspelled_words_ and blink::WebString has no find().
475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  base::string16 string_text = text;
485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  int skipped_length = 0;
495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  while (!string_text.empty()) {
515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // Extract the first possible English word from the given string.
525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // The given string may include non-ASCII characters or numbers. So, we
535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // should filter out such characters before start looking up our
545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // misspelled-word table.
555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // (This is a simple version of our SpellCheckWordIterator class.)
565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // If the given string doesn't include any ASCII characters, we can treat
575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // the string as valid one.
585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    base::string16::iterator first_char =
595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        std::find_if(string_text.begin(), string_text.end(), IsASCIIAlpha);
605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if (first_char == string_text.end())
615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return true;
625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    int word_offset = std::distance(string_text.begin(), first_char);
635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    int max_word_length = static_cast<int>(string_text.length()) - word_offset;
645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    int word_length;
655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    base::string16 word;
665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // Look up our misspelled-word table to check if the extracted word is a
685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // known misspelled word, and return the offset and the length of the
695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // extracted word if this word is a known misspelled word.
705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // (See the comment in MockSpellCheck::InitializeIfNeeded() why we use a
715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // misspelled-word table.)
725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    for (size_t i = 0; i < misspelled_words_.size(); ++i) {
735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      word_length =
745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          static_cast<int>(misspelled_words_.at(i).length()) > max_word_length
755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              ? max_word_length
765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              : static_cast<int>(misspelled_words_.at(i).length());
775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      word = string_text.substr(word_offset, word_length);
785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      if (word == misspelled_words_.at(i) &&
795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          (static_cast<int>(string_text.length()) ==
805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)               word_offset + word_length ||
815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)           IsNotASCIIAlpha(string_text[word_offset + word_length]))) {
825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        *misspelled_offset = word_offset + skipped_length;
835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        *misspelled_length = word_length;
845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        break;
855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      }
865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    }
875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if (*misspelled_length > 0)
895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      break;
905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    base::string16::iterator last_char = std::find_if(
925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        string_text.begin() + word_offset, string_text.end(), IsNotASCIIAlpha);
935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if (last_char == string_text.end())
945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      word_length = static_cast<int>(string_text.length()) - word_offset;
955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    else
965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      word_length = std::distance(first_char, last_char);
975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    DCHECK_LT(0, word_offset + word_length);
995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    string_text = string_text.substr(word_offset + word_length);
1005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    skipped_length += word_offset + word_length;
1015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  }
1025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  return false;
1045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
1055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)bool MockSpellCheck::HasInCache(const blink::WebString& word) {
1075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  return word == blink::WebString::fromUTF8("Spell wellcome. Is it broken?") ||
1085f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)         word == blink::WebString::fromUTF8("Spell wellcome.\x007F");
1095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
1105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)bool MockSpellCheck::IsMultiWordMisspelling(
1125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    const blink::WebString& text,
1135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    std::vector<blink::WebTextCheckingResult>* results) {
1145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (text == blink::WebString::fromUTF8("Helllo wordl.")) {
1155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    results->push_back(blink::WebTextCheckingResult(
1165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        blink::WebTextDecorationTypeSpelling, 0, 6, blink::WebString("Hello")));
1175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    results->push_back(blink::WebTextCheckingResult(
1185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        blink::WebTextDecorationTypeSpelling, 7, 5, blink::WebString("world")));
1195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return true;
1205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  }
1215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  return false;
1225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
1235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)void MockSpellCheck::FillSuggestionList(
1255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    const blink::WebString& word,
1265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    blink::WebVector<blink::WebString>* suggestions) {
1275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (word == blink::WebString::fromUTF8("wellcome"))
1285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    Append(suggestions, blink::WebString::fromUTF8("welcome"));
1295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  else if (word == blink::WebString::fromUTF8("upper case"))
1305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    Append(suggestions, blink::WebString::fromUTF8("uppercase"));
1315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  else if (word == blink::WebString::fromUTF8("Helllo"))
1325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    Append(suggestions, blink::WebString::fromUTF8("Hello"));
1335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  else if (word == blink::WebString::fromUTF8("wordl"))
1345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    Append(suggestions, blink::WebString::fromUTF8("world"));
1355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
1365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)bool MockSpellCheck::InitializeIfNeeded() {
1385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Exit if we have already initialized this object.
1395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (initialized_)
1405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return false;
1415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Create a table that consists of misspelled words used in WebKit layout
1435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // tests.
1445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Since WebKit layout tests don't have so many misspelled words as
1455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // well-spelled words, it is easier to compare the given word with misspelled
1465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // ones than to compare with well-spelled ones.
1475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  static const char* misspelled_words[] = {
1485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      // These words are known misspelled words in webkit tests.
1495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      // If there are other misspelled words in webkit tests, please add them in
1505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      // this array.
1515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      "foo",    "Foo",           "baz",             "fo",         "LibertyF",
1525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      "chello", "xxxtestxxx",    "XXxxx",           "Textx",      "blockquoted",
1535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      "asd",    "Lorem",         "Nunc",            "Curabitur",  "eu",
1545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      "adlj",   "adaasj",        "sdklj",           "jlkds",      "jsaada",
1555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      "jlda",   "zz",            "contentEditable",
1565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      // The following words are used by unit tests.
1575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      "ifmmp",  "qwertyuiopasd", "qwertyuiopasdf",  "upper case", "wellcome"};
1585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  misspelled_words_.clear();
1605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  for (size_t i = 0; i < arraysize(misspelled_words); ++i)
1615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    misspelled_words_.push_back(
1625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        base::string16(misspelled_words[i],
1635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                       misspelled_words[i] + strlen(misspelled_words[i])));
1645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Mark as initialized to prevent this object from being initialized twice
1665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // or more.
1675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  initialized_ = true;
1685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Since this MockSpellCheck class doesn't download dictionaries, this
1705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // function always returns false.
1715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  return false;
1725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
1735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}  // namespace content
175