1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ui/base/ime/win/tsf_input_scope.h"
6
7#include <algorithm>
8
9#include "base/compiler_specific.h"
10#include "base/logging.h"
11#include "base/message_loop/message_loop.h"
12#include "base/win/windows_version.h"
13
14namespace ui {
15namespace tsf_inputscope {
16namespace {
17
18void AppendNonTrivialInputScope(std::vector<InputScope>* input_scopes,
19                                InputScope input_scope) {
20  DCHECK(input_scopes);
21
22  if (input_scope == IS_DEFAULT)
23    return;
24
25  if (std::find(input_scopes->begin(), input_scopes->end(), input_scope) !=
26      input_scopes->end())
27    return;
28
29  input_scopes->push_back(input_scope);
30}
31
32class TSFInputScope FINAL : public ITfInputScope {
33 public:
34  explicit TSFInputScope(const std::vector<InputScope>& input_scopes)
35      : input_scopes_(input_scopes),
36        ref_count_(0) {}
37
38  // ITfInputScope:
39  STDMETHOD_(ULONG, AddRef)() OVERRIDE {
40    return InterlockedIncrement(&ref_count_);
41  }
42
43  STDMETHOD_(ULONG, Release)() OVERRIDE {
44    const LONG count = InterlockedDecrement(&ref_count_);
45    if (!count) {
46      delete this;
47      return 0;
48    }
49    return static_cast<ULONG>(count);
50  }
51
52  STDMETHOD(QueryInterface)(REFIID iid, void** result) OVERRIDE {
53    if (!result)
54      return E_INVALIDARG;
55    if (iid == IID_IUnknown || iid == IID_ITfInputScope) {
56      *result = static_cast<ITfInputScope*>(this);
57    } else {
58      *result = NULL;
59      return E_NOINTERFACE;
60    }
61    AddRef();
62    return S_OK;
63  }
64
65  STDMETHOD(GetInputScopes)(InputScope** input_scopes, UINT* count) OVERRIDE {
66    if (!count || !input_scopes)
67      return E_INVALIDARG;
68    *input_scopes = static_cast<InputScope*>(CoTaskMemAlloc(
69        sizeof(InputScope) * input_scopes_.size()));
70    if (!input_scopes) {
71      *count = 0;
72      return E_OUTOFMEMORY;
73    }
74
75    for (size_t i = 0; i < input_scopes_.size(); ++i)
76      (*input_scopes)[i] = input_scopes_[i];
77    *count = input_scopes_.size();
78    return S_OK;
79  }
80
81  STDMETHOD(GetPhrase)(BSTR** phrases, UINT* count) OVERRIDE {
82    return E_NOTIMPL;
83  }
84
85  STDMETHOD(GetRegularExpression)(BSTR* regexp) OVERRIDE {
86    return E_NOTIMPL;
87  }
88
89  STDMETHOD(GetSRGS)(BSTR* srgs) OVERRIDE {
90    return E_NOTIMPL;
91  }
92
93  STDMETHOD(GetXML)(BSTR* xml) OVERRIDE {
94    return E_NOTIMPL;
95  }
96
97 private:
98  // The corresponding text input types.
99  std::vector<InputScope> input_scopes_;
100
101  // The refrence count of this instance.
102  volatile LONG ref_count_;
103
104  DISALLOW_COPY_AND_ASSIGN(TSFInputScope);
105};
106
107typedef HRESULT (WINAPI *SetInputScopesFunc)(HWND window_handle,
108                                             const InputScope* input_scope_list,
109                                             UINT num_input_scopes,
110                                             WCHAR**, /* unused */
111                                             UINT, /* unused */
112                                             WCHAR*, /* unused */
113                                             WCHAR* /* unused */);
114
115SetInputScopesFunc g_set_input_scopes = NULL;
116bool g_get_proc_done = false;
117
118SetInputScopesFunc GetSetInputScopes() {
119  DCHECK(base::MessageLoopForUI::IsCurrent());
120  // Thread safety is not required because this function is under UI thread.
121  if (!g_get_proc_done) {
122    g_get_proc_done = true;
123
124    // For stability reasons, we do not support Windows XP.
125    if (base::win::GetVersion() < base::win::VERSION_VISTA)
126      return NULL;
127
128    HMODULE module = NULL;
129    if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, L"msctf.dll",
130        &module)) {
131      return NULL;
132    }
133    g_set_input_scopes = reinterpret_cast<SetInputScopesFunc>(
134        GetProcAddress(module, "SetInputScopes"));
135  }
136  return g_set_input_scopes;
137}
138
139InputScope ConvertTextInputTypeToInputScope(TextInputType text_input_type) {
140  // Following mapping is based in IE10 on Windows 8.
141  switch (text_input_type) {
142    case TEXT_INPUT_TYPE_PASSWORD:
143      return IS_PASSWORD;
144    case TEXT_INPUT_TYPE_SEARCH:
145      return IS_SEARCH;
146    case TEXT_INPUT_TYPE_EMAIL:
147      return IS_EMAIL_SMTPEMAILADDRESS;
148    case TEXT_INPUT_TYPE_NUMBER:
149      return IS_NUMBER;
150    case TEXT_INPUT_TYPE_TELEPHONE:
151      return IS_TELEPHONE_FULLTELEPHONENUMBER;
152    case TEXT_INPUT_TYPE_URL:
153      return IS_URL;
154    default:
155      return IS_DEFAULT;
156  }
157}
158
159InputScope ConvertTextInputModeToInputScope(TextInputMode text_input_mode) {
160  switch (text_input_mode) {
161    case TEXT_INPUT_MODE_FULL_WIDTH_LATIN:
162      return IS_ALPHANUMERIC_FULLWIDTH;
163    case TEXT_INPUT_MODE_KANA:
164      return IS_HIRAGANA;
165    case TEXT_INPUT_MODE_KATAKANA:
166      return IS_KATAKANA_FULLWIDTH;
167    case TEXT_INPUT_MODE_NUMERIC:
168      return IS_NUMBER;
169    case TEXT_INPUT_MODE_TEL:
170      return IS_TELEPHONE_FULLTELEPHONENUMBER;
171    case TEXT_INPUT_MODE_EMAIL:
172      return IS_EMAIL_SMTPEMAILADDRESS;
173    case TEXT_INPUT_MODE_URL:
174      return IS_URL;
175    default:
176      return IS_DEFAULT;
177  }
178}
179
180}  // namespace
181
182std::vector<InputScope> GetInputScopes(TextInputType text_input_type,
183                                       TextInputMode text_input_mode) {
184  std::vector<InputScope> input_scopes;
185
186  AppendNonTrivialInputScope(&input_scopes,
187                             ConvertTextInputTypeToInputScope(text_input_type));
188  AppendNonTrivialInputScope(&input_scopes,
189                             ConvertTextInputModeToInputScope(text_input_mode));
190
191  if (input_scopes.empty())
192    input_scopes.push_back(IS_DEFAULT);
193
194  return input_scopes;
195}
196
197ITfInputScope* CreateInputScope(TextInputType text_input_type,
198                                TextInputMode text_input_mode) {
199  return new TSFInputScope(GetInputScopes(text_input_type, text_input_mode));
200}
201
202void SetInputScopeForTsfUnawareWindow(
203    HWND window_handle,
204    TextInputType text_input_type,
205    TextInputMode text_input_mode) {
206  SetInputScopesFunc set_input_scopes = GetSetInputScopes();
207  if (!set_input_scopes)
208    return;
209
210  std::vector<InputScope> input_scopes = GetInputScopes(text_input_type,
211                                                        text_input_mode);
212  set_input_scopes(window_handle, &input_scopes[0], input_scopes.size(), NULL,
213                   0, NULL, NULL);
214}
215
216}  // namespace tsf_inputscope
217}  // namespace ui
218