Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2158)

Unified Diff: win8/metro_driver/ime/text_service.cc

Issue 1815463002: Remove win8/metro_driver (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: . Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « win8/metro_driver/ime/text_service.h ('k') | win8/metro_driver/ime/text_service_delegate.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: win8/metro_driver/ime/text_service.cc
diff --git a/win8/metro_driver/ime/text_service.cc b/win8/metro_driver/ime/text_service.cc
deleted file mode 100644
index 1ee845e024ac57e24f2585d358ce5010729af913..0000000000000000000000000000000000000000
--- a/win8/metro_driver/ime/text_service.cc
+++ /dev/null
@@ -1,490 +0,0 @@
-// Copyright 2013 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 "win8/metro_driver/ime/text_service.h"
-
-#include <msctf.h>
-#include <stddef.h>
-#include <stdint.h>
-
-#include <utility>
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/win/scoped_variant.h"
-#include "ui/metro_viewer/ime_types.h"
-#include "win8/metro_driver/ime/text_service_delegate.h"
-#include "win8/metro_driver/ime/text_store.h"
-#include "win8/metro_driver/ime/text_store_delegate.h"
-
-// Architecture overview of input method support on Ash mode:
-//
-// Overview:
-// On Ash mode, the system keyboard focus is owned by the metro_driver process
-// while most of event handling are still implemented in the browser process.
-// Thus the metro_driver basically works as a proxy that simply forwards
-// keyevents to the metro_driver process. IME support must be involved somewhere
-// in this flow.
-//
-// In short, we need to interact with an IME in the metro_driver process since
-// TSF (Text Services Framework) runtime wants to processes keyevents while
-// (and only while) the attached UI thread owns keyboard focus.
-//
-// Due to this limitation, we need to split IME handling into two parts, one
-// is in the metro_driver process and the other is in the browser process.
-// The metro_driver process is responsible for implementing the primary data
-// store for the composition text and wiring it up with an IME via TSF APIs.
-// On the other hand, the browser process is responsible for calculating
-// character position in the composition text whenever the composition text
-// is updated.
-//
-// IPC overview:
-// Fortunately, we don't need so many IPC messages to support IMEs. In fact,
-// only 4 messages are required to enable basic IME functionality.
-//
-// metro_driver process -> browser process
-// Message Type:
-// - MetroViewerHostMsg_ImeCompositionChanged
-// - MetroViewerHostMsg_ImeTextCommitted
-// Message Routing:
-// TextServiceImpl
-// -> ChromeAppViewAsh
-// -- (process boundary) --
-// -> RemoteWindowTreeHostWin
-// -> RemoteInputMethodWin
-//
-// browser process -> metro_driver process
-// Message Type:
-// - MetroViewerHostMsg_ImeCancelComposition
-// - MetroViewerHostMsg_ImeTextInputClientUpdated
-// Message Routing:
-// RemoteInputMethodWin
-// -> RemoteWindowTreeHostWin
-// -- (process boundary) --
-// -> ChromeAppViewAsh
-// -> TextServiceImpl
-//
-// Note that a keyevent may be forwarded through a different path. When a
-// keyevent is not handled by an IME, such keyevent and subsequent character
-// events will be sent from the metro_driver process to the browser process as
-// following IPC messages.
-// - MetroViewerHostMsg_KeyDown
-// - MetroViewerHostMsg_KeyUp
-// - MetroViewerHostMsg_Character
-//
-// How TextServiceImpl works:
-// Here is the list of the major tasks that are handled in TextServiceImpl.
-// - Manages a session object obtained from TSF runtime. We need them to call
-// most of TSF APIs.
-// - Handles OnDocumentChanged event. Whenever the document type is changed,
-// TextServiceImpl destroyes the current document and initializes new one
-// according to the given |input_scopes|.
-// - Stores the |composition_character_bounds_| passed from OnDocumentChanged
-// event so that an IME or on-screen keyboard can query the character
-// position synchronously.
-// The most complicated part is the OnDocumentChanged handler. Since some IMEs
-// such as Japanese IMEs drastically change their behavior depending on
-// properties exposed from the virtual document, we need to set up a lot
-// properties carefully and correctly. See DocumentBinding class in this file
-// about what will be involved in this multi-phase construction. See also
-// text_store.cc and input_scope.cc for more underlying details.
-
-namespace metro_driver {
-namespace {
-
-// Japanese IME expects the default value of this compartment is
-// TF_SENTENCEMODE_PHRASEPREDICT to emulate IMM32 behavior. This value is
-// managed per thread, thus setting this value at once is sufficient. This
-// value never affects non-Japanese IMEs.
-bool InitializeSentenceMode(ITfThreadMgr* thread_manager,
- TfClientId client_id) {
- base::win::ScopedComPtr<ITfCompartmentMgr> thread_compartment_manager;
- HRESULT hr = thread_compartment_manager.QueryFrom(thread_manager);
- if (FAILED(hr)) {
- LOG(ERROR) << "QueryFrom failed. hr = " << hr;
- return false;
- }
- base::win::ScopedComPtr<ITfCompartment> sentence_compartment;
- hr = thread_compartment_manager->GetCompartment(
- GUID_COMPARTMENT_KEYBOARD_INPUTMODE_SENTENCE,
- sentence_compartment.Receive());
- if (FAILED(hr)) {
- LOG(ERROR) << "ITfCompartment::GetCompartment failed. hr = " << hr;
- return false;
- }
-
- base::win::ScopedVariant sentence_variant;
- sentence_variant.Set(TF_SENTENCEMODE_PHRASEPREDICT);
- hr = sentence_compartment->SetValue(client_id, sentence_variant.ptr());
- if (FAILED(hr)) {
- LOG(ERROR) << "ITfCompartment::SetValue failed. hr = " << hr;
- return false;
- }
- return true;
-}
-
-// Initializes |context| as disabled context where IMEs will be disabled.
-bool InitializeDisabledContext(ITfContext* context, TfClientId client_id) {
- base::win::ScopedComPtr<ITfCompartmentMgr> compartment_mgr;
- HRESULT hr = compartment_mgr.QueryFrom(context);
- if (FAILED(hr)) {
- LOG(ERROR) << "QueryFrom failed. hr = " << hr;
- return false;
- }
-
- base::win::ScopedComPtr<ITfCompartment> disabled_compartment;
- hr = compartment_mgr->GetCompartment(GUID_COMPARTMENT_KEYBOARD_DISABLED,
- disabled_compartment.Receive());
- if (FAILED(hr)) {
- LOG(ERROR) << "ITfCompartment::GetCompartment failed. hr = " << hr;
- return false;
- }
-
- base::win::ScopedVariant variant;
- variant.Set(1);
- hr = disabled_compartment->SetValue(client_id, variant.ptr());
- if (FAILED(hr)) {
- LOG(ERROR) << "ITfCompartment::SetValue failed. hr = " << hr;
- return false;
- }
-
- base::win::ScopedComPtr<ITfCompartment> empty_context;
- hr = compartment_mgr->GetCompartment(GUID_COMPARTMENT_EMPTYCONTEXT,
- empty_context.Receive());
- if (FAILED(hr)) {
- LOG(ERROR) << "ITfCompartment::GetCompartment failed. hr = " << hr;
- return false;
- }
-
- base::win::ScopedVariant empty_context_variant;
- empty_context_variant.Set(static_cast<int32_t>(1));
- hr = empty_context->SetValue(client_id, empty_context_variant.ptr());
- if (FAILED(hr)) {
- LOG(ERROR) << "ITfCompartment::SetValue failed. hr = " << hr;
- return false;
- }
-
- return true;
-}
-
-bool IsPasswordField(const std::vector<InputScope>& input_scopes) {
- return std::find(input_scopes.begin(), input_scopes.end(), IS_PASSWORD) !=
- input_scopes.end();
-}
-
-// A class that manages the lifetime of the event callback registration. When
-// this object is destroyed, corresponding event callback will be unregistered.
-class EventSink {
- public:
- EventSink(DWORD cookie, base::win::ScopedComPtr<ITfSource> source)
- : cookie_(cookie),
- source_(source) {}
- ~EventSink() {
- if (!source_.get() || cookie_ != TF_INVALID_COOKIE)
- return;
- source_->UnadviseSink(cookie_);
- cookie_ = TF_INVALID_COOKIE;
- source_.Release();
- }
-
- private:
- DWORD cookie_;
- base::win::ScopedComPtr<ITfSource> source_;
- DISALLOW_COPY_AND_ASSIGN(EventSink);
-};
-
-scoped_ptr<EventSink> CreateTextEditSink(ITfContext* context,
- ITfTextEditSink* text_store) {
- DCHECK(text_store);
- base::win::ScopedComPtr<ITfSource> source;
- DWORD cookie = TF_INVALID_EDIT_COOKIE;
- HRESULT hr = source.QueryFrom(context);
- if (FAILED(hr)) {
- LOG(ERROR) << "QueryFrom failed, hr = " << hr;
- return scoped_ptr<EventSink>();
- }
- hr = source->AdviseSink(IID_ITfTextEditSink, text_store, &cookie);
- if (FAILED(hr)) {
- LOG(ERROR) << "AdviseSink failed, hr = " << hr;
- return scoped_ptr<EventSink>();
- }
- return scoped_ptr<EventSink>(new EventSink(cookie, source));
-}
-
-// A set of objects that should have the same lifetime. Following things
-// are maintained.
-// - TextStore: a COM object that abstracts text buffer. This object is
-// actually implemented by us in text_store.cc
-// - ITfDocumentMgr: a focusable unit in TSF. This object is implemented by
-// TSF runtime and works as a container of TextStore.
-// - EventSink: an object that ensures that the event callback between
-// TSF runtime and TextStore is unregistered when this object is destroyed.
-class DocumentBinding {
- public:
- ~DocumentBinding() {
- if (!document_manager_.get())
- return;
- document_manager_->Pop(TF_POPF_ALL);
- }
-
- static scoped_ptr<DocumentBinding> Create(
- ITfThreadMgr* thread_manager,
- TfClientId client_id,
- const std::vector<InputScope>& input_scopes,
- HWND window_handle,
- TextStoreDelegate* delegate) {
- base::win::ScopedComPtr<ITfDocumentMgr> document_manager;
- HRESULT hr = thread_manager->CreateDocumentMgr(document_manager.Receive());
- if (FAILED(hr)) {
- LOG(ERROR) << "ITfThreadMgr::CreateDocumentMgr failed. hr = " << hr;
- return scoped_ptr<DocumentBinding>();
- }
-
- // Note: In our IPC protocol, an empty |input_scopes| is used to indicate
- // that an IME must be disabled in this context. In such case, we need not
- // instantiate TextStore.
- const bool use_null_text_store = input_scopes.empty();
-
- scoped_refptr<TextStore> text_store;
- if (!use_null_text_store) {
- text_store = TextStore::Create(window_handle, input_scopes, delegate);
- if (!text_store.get()) {
- LOG(ERROR) << "Failed to create TextStore.";
- return scoped_ptr<DocumentBinding>();
- }
- }
-
- base::win::ScopedComPtr<ITfContext> context;
- DWORD edit_cookie = TF_INVALID_EDIT_COOKIE;
- hr = document_manager->CreateContext(
- client_id,
- 0,
- static_cast<ITextStoreACP*>(text_store.get()),
- context.Receive(),
- &edit_cookie);
- if (FAILED(hr)) {
- LOG(ERROR) << "ITfDocumentMgr::CreateContext failed. hr = " << hr;
- return scoped_ptr<DocumentBinding>();
- }
-
- // If null-TextStore is used or |input_scopes| looks like a password field,
- // set special properties to tell IMEs to be disabled.
- if ((use_null_text_store || IsPasswordField(input_scopes)) &&
- !InitializeDisabledContext(context.get(), client_id)) {
- LOG(ERROR) << "InitializeDisabledContext failed.";
- return scoped_ptr<DocumentBinding>();
- }
-
- scoped_ptr<EventSink> text_edit_sink;
- if (!use_null_text_store) {
- text_edit_sink = CreateTextEditSink(context.get(), text_store.get());
- if (!text_edit_sink) {
- LOG(ERROR) << "CreateTextEditSink failed.";
- return scoped_ptr<DocumentBinding>();
- }
- }
- hr = document_manager->Push(context.get());
- if (FAILED(hr)) {
- LOG(ERROR) << "ITfDocumentMgr::Push failed. hr = " << hr;
- return scoped_ptr<DocumentBinding>();
- }
- return scoped_ptr<DocumentBinding>(new DocumentBinding(
- text_store, document_manager, std::move(text_edit_sink)));
- }
-
- ITfDocumentMgr* document_manager() const { return document_manager_.get(); }
-
- scoped_refptr<TextStore> text_store() const {
- return text_store_;
- }
-
- private:
- DocumentBinding(scoped_refptr<TextStore> text_store,
- base::win::ScopedComPtr<ITfDocumentMgr> document_manager,
- scoped_ptr<EventSink> text_edit_sink)
- : text_store_(text_store),
- document_manager_(document_manager),
- text_edit_sink_(std::move(text_edit_sink)) {}
-
- scoped_refptr<TextStore> text_store_;
- base::win::ScopedComPtr<ITfDocumentMgr> document_manager_;
- scoped_ptr<EventSink> text_edit_sink_;
-
- DISALLOW_COPY_AND_ASSIGN(DocumentBinding);
-};
-
-class TextServiceImpl : public TextService,
- public TextStoreDelegate {
- public:
- TextServiceImpl(ITfThreadMgr* thread_manager,
- TfClientId client_id,
- HWND window_handle,
- TextServiceDelegate* delegate)
- : client_id_(client_id),
- window_handle_(window_handle),
- delegate_(delegate),
- thread_manager_(thread_manager) {
- DCHECK_NE(TF_CLIENTID_NULL, client_id);
- DCHECK(window_handle != NULL);
- DCHECK(thread_manager_.get());
- }
- ~TextServiceImpl() override {
- thread_manager_->Deactivate();
- }
-
- private:
- // TextService overrides:
- void CancelComposition() override {
- if (!current_document_) {
- VLOG(0) << "|current_document_| is NULL due to the previous error.";
- return;
- }
- scoped_refptr<TextStore> text_store = current_document_->text_store();
- if (!text_store.get())
- return;
- text_store->CancelComposition();
- }
-
- void OnDocumentChanged(const std::vector<int32_t>& input_scopes,
- const std::vector<metro_viewer::CharacterBounds>&
- character_bounds) override {
- bool document_type_changed = input_scopes_ != input_scopes;
- input_scopes_ = input_scopes;
- composition_character_bounds_ = character_bounds;
- if (document_type_changed)
- OnDocumentTypeChanged(input_scopes);
- }
-
- void OnWindowActivated() override {
- if (!current_document_) {
- VLOG(0) << "|current_document_| is NULL due to the previous error.";
- return;
- }
- ITfDocumentMgr* document_manager = current_document_->document_manager();
- if (!document_manager) {
- VLOG(0) << "|document_manager| is NULL due to the previous error.";
- return;
- }
- HRESULT hr = thread_manager_->SetFocus(document_manager);
- if (FAILED(hr)) {
- LOG(ERROR) << "ITfThreadMgr::SetFocus failed. hr = " << hr;
- return;
- }
- }
-
- void OnCompositionChanged(
- const base::string16& text,
- int32_t selection_start,
- int32_t selection_end,
- const std::vector<metro_viewer::UnderlineInfo>& underlines) override {
- if (!delegate_)
- return;
- delegate_->OnCompositionChanged(text,
- selection_start,
- selection_end,
- underlines);
- }
-
- void OnTextCommitted(const base::string16& text) override {
- if (!delegate_)
- return;
- delegate_->OnTextCommitted(text);
- }
-
- RECT GetCaretBounds() override {
- if (composition_character_bounds_.empty()) {
- const RECT rect = {};
- return rect;
- }
- const metro_viewer::CharacterBounds& bounds =
- composition_character_bounds_[0];
- POINT left_top = { bounds.left, bounds.top };
- POINT right_bottom = { bounds.right, bounds.bottom };
- ClientToScreen(window_handle_, &left_top);
- ClientToScreen(window_handle_, &right_bottom);
- const RECT rect = {
- left_top.x,
- left_top.y,
- right_bottom.x,
- right_bottom.y,
- };
- return rect;
- }
-
- bool GetCompositionCharacterBounds(uint32_t index, RECT* rect) override {
- if (index >= composition_character_bounds_.size()) {
- return false;
- }
- const metro_viewer::CharacterBounds& bounds =
- composition_character_bounds_[index];
- POINT left_top = { bounds.left, bounds.top };
- POINT right_bottom = { bounds.right, bounds.bottom };
- ClientToScreen(window_handle_, &left_top);
- ClientToScreen(window_handle_, &right_bottom);
- SetRect(rect, left_top.x, left_top.y, right_bottom.x, right_bottom.y);
- return true;
- }
-
- void OnDocumentTypeChanged(const std::vector<int32_t>& input_scopes) {
- std::vector<InputScope> native_input_scopes(input_scopes.size());
- for (size_t i = 0; i < input_scopes.size(); ++i)
- native_input_scopes[i] = static_cast<InputScope>(input_scopes[i]);
- scoped_ptr<DocumentBinding> new_document =
- DocumentBinding::Create(thread_manager_.get(),
- client_id_,
- native_input_scopes,
- window_handle_,
- this);
- LOG_IF(ERROR, !new_document) << "Failed to create a new document.";
- current_document_.swap(new_document);
- OnWindowActivated();
- }
-
- TfClientId client_id_;
- HWND window_handle_;
- TextServiceDelegate* delegate_;
- scoped_ptr<DocumentBinding> current_document_;
- base::win::ScopedComPtr<ITfThreadMgr> thread_manager_;
-
- // A vector of InputScope enumeration, which represents the document type of
- // the focused text field. Note that in our IPC message protocol, an empty
- // |input_scopes_| has special meaning that IMEs must be disabled on this
- // document.
- std::vector<int32_t> input_scopes_;
- // Character bounds of the composition. When there is no composition but this
- // vector is not empty, the first element contains the caret bounds.
- std::vector<metro_viewer::CharacterBounds> composition_character_bounds_;
-
- DISALLOW_COPY_AND_ASSIGN(TextServiceImpl);
-};
-
-} // namespace
-
-scoped_ptr<TextService>
-CreateTextService(TextServiceDelegate* delegate, HWND window_handle) {
- if (!delegate)
- return scoped_ptr<TextService>();
- base::win::ScopedComPtr<ITfThreadMgr> thread_manager;
- HRESULT hr = thread_manager.CreateInstance(CLSID_TF_ThreadMgr);
- if (FAILED(hr)) {
- LOG(ERROR) << "Failed to create instance of CLSID_TF_ThreadMgr. hr = "
- << hr;
- return scoped_ptr<TextService>();
- }
- TfClientId client_id = TF_CLIENTID_NULL;
- hr = thread_manager->Activate(&client_id);
- if (FAILED(hr)) {
- LOG(ERROR) << "ITfThreadMgr::Activate failed. hr = " << hr;
- return scoped_ptr<TextService>();
- }
- if (!InitializeSentenceMode(thread_manager.get(), client_id)) {
- LOG(ERROR) << "InitializeSentenceMode failed.";
- thread_manager->Deactivate();
- return scoped_ptr<TextService>();
- }
- return scoped_ptr<TextService>(new TextServiceImpl(
- thread_manager.get(), client_id, window_handle, delegate));
-}
-
-} // namespace metro_driver
« no previous file with comments | « win8/metro_driver/ime/text_service.h ('k') | win8/metro_driver/ime/text_service_delegate.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698