diff options
author | nona@chromium.org <nona@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-05 06:45:43 +0000 |
---|---|---|
committer | nona@chromium.org <nona@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-05 06:45:43 +0000 |
commit | 99ef93de56bae720a4a7d188d7fc631088613b05 (patch) | |
tree | 61bf034566933b0e691a6e1a44212225d1ed1408 /ui | |
parent | ca61722ee9d47c5e103f339fe2ac87d96b8ab29a (diff) | |
download | chromium_src-99ef93de56bae720a4a7d188d7fc631088613b05.zip chromium_src-99ef93de56bae720a4a7d188d7fc631088613b05.tar.gz chromium_src-99ef93de56bae720a4a7d188d7fc631088613b05.tar.bz2 |
Introduce TSFBridge class
TSFBridge provides IME related utility functions.
This class is compiled but not used in production at this moment.
BUG=141611
TEST=try bot
Review URL: https://chromiumcodereview.appspot.com/10831367
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@154902 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui')
-rw-r--r-- | ui/base/win/tsf_bridge.cc | 341 | ||||
-rw-r--r-- | ui/base/win/tsf_bridge.h | 75 | ||||
-rw-r--r-- | ui/ui.gyp | 2 |
3 files changed, 418 insertions, 0 deletions
diff --git a/ui/base/win/tsf_bridge.cc b/ui/base/win/tsf_bridge.cc new file mode 100644 index 0000000..bd00d4a --- /dev/null +++ b/ui/base/win/tsf_bridge.cc @@ -0,0 +1,341 @@ +// Copyright (c) 2012 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 <msctf.h> +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/singleton.h" +#include "base/message_loop.h" +#include "base/threading/thread_local_storage.h" +#include "base/win/scoped_comptr.h" +#include "base/win/scoped_variant.h" +#include "ui/base/win/tsf_bridge.h" +#include "ui/base/win/tsf_text_store.h" + +namespace ui { + +namespace { + +// A TLS implementation of TsfBridge. +class TsfBridgeDelegate : public TsfBridge { + public: + TsfBridgeDelegate() + : source_cookie_(TF_INVALID_COOKIE), + client_id_(TF_CLIENTID_NULL), + client_(NULL) { + } + + virtual ~TsfBridgeDelegate() { + } + + // TsfBridge override. + bool Initialize() { + DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type()); + if (client_id_ != TF_CLIENTID_NULL) { + VLOG(1) << "Already initialized."; + return false; + } + + text_store_.reset(new scoped_refptr<TsfTextStore>(new TsfTextStore())); + + if (FAILED(thread_manager_.CreateInstance(CLSID_TF_ThreadMgr))) { + VLOG(1) << "Failed to create ThreadManager instance."; + return false; + } + + if (!InitializeForEnabledDocumentManager()) + return false; + + if (!InitializeForDisabledDocumentManager()) + return false; + + return true; + } + + // TsfBridge override. + virtual void Shutdown() OVERRIDE { + DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type()); + if (!IsInitialized()) + return; + base::win::ScopedComPtr<ITfContext> context; + if (FAILED(document_manager_->GetTop(context.Receive()))) + return; + base::win::ScopedComPtr<ITfSource> source; + if (FAILED(source.QueryFrom(context))) + return; + if (source_cookie_ == TF_INVALID_COOKIE) + return; + if (FAILED(source->UnadviseSink(source_cookie_))) + return; + + DCHECK(text_store_.get()); + text_store_.reset(); + client_id_ = TF_CLIENTID_NULL; + } + + // TsfBridge override. + virtual bool EnableIME() OVERRIDE { + DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type()); + DCHECK(IsInitialized()); + if (SUCCEEDED(thread_manager_->SetFocus(document_manager_))) { + return true; + } + return false; + } + + // TsfBridge override. + virtual bool DisableIME() OVERRIDE { + DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type()); + DCHECK(IsInitialized()); + if (SUCCEEDED(thread_manager_->SetFocus(disabled_document_manager_))) { + return true; + } + return false; + } + + // TsfBridge override. + virtual bool CancelComposition() OVERRIDE { + DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type()); + DCHECK(IsInitialized()); + // If current focused document manager is not |document_manager_|, do + // nothing here. + base::win::ScopedComPtr<ITfDocumentMgr> focused_document_mangaer; + if (FAILED(thread_manager_->GetFocus(focused_document_mangaer.Receive()))) + return false; + if (!focused_document_mangaer.IsSameObject(document_manager_)) + return false; + + base::win::ScopedComPtr<ITfContext> context; + if (FAILED(document_manager_->GetTop(context.Receive()))) { + VLOG(1) << "Failed to get top context."; + return false; + } + + base::win::ScopedComPtr<ITfContextOwnerCompositionServices> owner; + if (FAILED(owner.QueryFrom(context))) { + VLOG(1) << "Failed to get ITfContextOwnerCompositionService."; + return false; + } + // Cancel all composition. + owner->TerminateComposition(NULL); + return true; + } + + // TsfBridge override. + virtual bool AssociateFocus(HWND window) { + DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type()); + DCHECK(IsInitialized()); + base::win::ScopedComPtr<ITfDocumentMgr> previous_manager; + return SUCCEEDED(thread_manager_->AssociateFocus( + window, + document_manager_, + previous_manager.Receive())); + } + + // TsfBridge override. + virtual void SetFocusedClient(HWND focused_window, + TextInputClient* client) OVERRIDE { + DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type()); + DCHECK(client); + DCHECK(IsInitialized()); + client_ = client; + text_store_->get()->SetFocusedTextInputClient(focused_window, client); + } + + // TsfBridge override. + virtual void RemoveFocusedClient(TextInputClient* client) OVERRIDE { + DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type()); + DCHECK(IsInitialized()); + if (client_ == client) + text_store_->get()->SetFocusedTextInputClient(NULL, NULL); + } + + private: + friend struct DefaultSingletonTraits<TsfBridgeDelegate>; + + // Initializes |document_manager_|. + bool InitializeForEnabledDocumentManager() { + if (FAILED(thread_manager_->Activate(&client_id_))) { + VLOG(1) << "Failed to activate Thread Manager."; + return false; + } + + if (FAILED(thread_manager_->CreateDocumentMgr( + document_manager_.Receive()))) { + VLOG(1) << "Failed to create Document Manager."; + return false; + } + + TfEditCookie unused_edit_cookie = TF_INVALID_EDIT_COOKIE; + base::win::ScopedComPtr<ITfContext> context; + if (FAILED(document_manager_->CreateContext(client_id_, + 0, + static_cast<ITextStoreACP*>( + text_store_->get()), + context.Receive(), + &unused_edit_cookie))) { + VLOG(1) << "Failed to create Context."; + return false; + } + + if (FAILED(document_manager_->Push(context))) { + VLOG(1) << "Failed to push context."; + return false; + } + + if (FAILED(thread_manager_->CreateDocumentMgr( + disabled_document_manager_.Receive()))) { + VLOG(1) << "Failed to create Document Manager."; + return false; + } + + base::win::ScopedComPtr<ITfSource> source; + if (FAILED(source.QueryFrom(context))) { + VLOG(1) << "Failed to get source."; + return false; + } + + if (FAILED(source->AdviseSink(IID_ITfTextEditSink, + static_cast<ITfTextEditSink*>( + text_store_->get()), + &source_cookie_))) { + VLOG(1) << "AdviseSink failed."; + return false; + } + + if (source_cookie_ == TF_INVALID_COOKIE) { + VLOG(1) << "The result of cookie is invalid."; + return false; + } + + return true; + } + + // Initializes |disabled_document_manager_|. + bool InitializeForDisabledDocumentManager() { + base::win::ScopedComPtr<ITfContext> disabled_context; + DWORD disabled_edit_cookie; + if (document_manager_->CreateContext(client_id_, + 0, + NULL, + disabled_context.Receive(), + &disabled_edit_cookie)) { + VLOG(1) << "Failed to create disabled Context"; + return false; + } + + base::win::ScopedComPtr<ITfCompartmentMgr> compartment_mgr; + if (FAILED(compartment_mgr.QueryFrom(disabled_context))) { + VLOG(1) << "Failed to get CompartmentMgr."; + return false; + } + + base::win::ScopedComPtr<ITfCompartment> disabled_compartment; + if (FAILED(compartment_mgr->GetCompartment( + GUID_COMPARTMENT_KEYBOARD_DISABLED, + disabled_compartment.Receive()))) { + VLOG(1) << "Failed to get keyboard disabled compartment."; + return false; + } + + base::win::ScopedVariant variant; + variant.Set(static_cast<int32>(1)); + if (FAILED(disabled_compartment->SetValue(client_id_, &variant))) { + VLOG(1) << "Failed to disable the DocumentMgr."; + return false; + } + + base::win::ScopedComPtr<ITfCompartment> empty_context; + if (FAILED(compartment_mgr->GetCompartment(GUID_COMPARTMENT_EMPTYCONTEXT, + empty_context.Receive()))) { + VLOG(1) << "Failed to get empty context compartment."; + return false; + } + base::win::ScopedVariant empty_context_variant; + empty_context_variant.Set(static_cast<int32>(1)); + if (FAILED(empty_context->SetValue(client_id_, &empty_context_variant))) { + VLOG(1) << "Failed to set empty context."; + return false; + } + + if (FAILED(disabled_document_manager_->Push(disabled_context))) { + VLOG(1) << "Failed to push disabled context."; + return false; + } + + return true; + } + + // Returns true if already initialized. + bool IsInitialized() { + return client_id_ != TF_CLIENTID_NULL; + } + + // An ITfContext object to be used in normal text field. + base::win::ScopedComPtr<ITfDocumentMgr> document_manager_; + + // Altough TSF support IME enable/disable switching without context changing, + // some IMEs don't change their state. So we should switch a focus to a + // |disabled_document_manager_| which contains deactivated context for + // disabling IMEs. + base::win::ScopedComPtr<ITfDocumentMgr> disabled_document_manager_; + + // An ITfThreadMgr object to be used in focus and document management. + base::win::ScopedComPtr<ITfThreadMgr> thread_manager_; + + // A TextStore object to be used in communicating with IME. + scoped_ptr<scoped_refptr<TsfTextStore> > text_store_; + + DWORD source_cookie_; + TfClientId client_id_; + + // Current focused text input client. Do not free |client_|. + TextInputClient* client_; + + DISALLOW_COPY_AND_ASSIGN(TsfBridgeDelegate); +}; + +// We use thread local storage for TsfBridge lifespan management. +base::ThreadLocalStorage::StaticSlot tls_tsf_bridge = TLS_INITIALIZER; + +// Used for releasing TLS object. +void FreeTlsTsfBridgeDelegate(void* data) { + TsfBridgeDelegate* delegate = static_cast<TsfBridgeDelegate*>(data); + delete delegate; +} + +} // namespace + +TsfBridge::TsfBridge() { +} + +TsfBridge::~TsfBridge() { +} + +// static +bool TsfBridge::Initialize() { + if (MessageLoop::current()->type() == MessageLoop::TYPE_UI) { + VLOG(1) << "Do not use TsfBridge without UI thread."; + return false; + } + tls_tsf_bridge.Initialize(FreeTlsTsfBridgeDelegate); + TsfBridgeDelegate* delegate = new TsfBridgeDelegate(); + tls_tsf_bridge.Set(delegate); + return delegate->Initialize(); +} + +// static +TsfBridge* TsfBridge::GetInstance() { + if (MessageLoop::current()->type() == MessageLoop::TYPE_UI) { + VLOG(1) << "Do not use TsfBridge without UI thread."; + return NULL; + } + TsfBridgeDelegate* delegate = + static_cast<TsfBridgeDelegate*>(tls_tsf_bridge.Get()); + DCHECK(delegate) << "Do no call GetInstance before TsfBridge::Initialize."; + return delegate; +} + +} // namespace ui diff --git a/ui/base/win/tsf_bridge.h b/ui/base/win/tsf_bridge.h new file mode 100644 index 0000000..23ae9f3 --- /dev/null +++ b/ui/base/win/tsf_bridge.h @@ -0,0 +1,75 @@ +// Copyright (c) 2012 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. + +#ifndef UI_BASE_WIN_TSF_BRIDGE_H_ +#define UI_BASE_WIN_TSF_BRIDGE_H_ + +#include <Windows.h> +#include "base/basictypes.h" +#include "ui/base/ui_export.h" + +namespace ui { +class TextInputClient; + +// TsfBridge provides high level IME related operations on top of Text Services +// Framework (TSF). TsfBridge is managed by TLS because TSF related stuff is +// associated with each thread and not allowed to access across thread boundary. +// To be consistent with IMM32 behavior, TsfBridge is shared in the same thread. +// TsfBridge is used by the web content text inputting field, for example +// DisableIME() should be called if a password field is focused. +// +// TsfBridge also manages connectivity between TsfTextStore which is the backend +// of text inputting and current focused TextInputClient. +// +// All methods in this class must be used in UI thread. +class TsfBridge { + public: + virtual ~TsfBridge(); + + // Returns the thread local TsfBridge instance. Initialize() must be called + // first. Do not cache this pointer and use it after TsfBridge Shutdown(). + static UI_EXPORT TsfBridge* GetInstance(); + + // Sets the thread local instance. Must be called before any calls to + // GetInstance(). + static UI_EXPORT bool Initialize(); + + // Destroys the thread local instance. + virtual void Shutdown() = 0; + + // Enables the IME. + // Returns false if an error occures. + virtual bool EnableIME() = 0; + + // Diables the IME. + // Returns false if an error occures. + virtual bool DisableIME() = 0; + + // Cancels the ongoing composition if exists. + // Returns false if an error occures. + virtual bool CancelComposition() = 0; + + // Associates |window| with document manager. + virtual bool AssociateFocus(HWND window) = 0; + + // Sets currently focused TextInputClient. + // Caller must free |client|. + virtual void SetFocusedClient(HWND focused_window, + TextInputClient* client) = 0; + + // Removes currently focused TextInputClient. + // Caller must free |client|. + virtual void RemoveFocusedClient(TextInputClient* client) = 0; + + protected: + // Uses GetInstance() instead. + TsfBridge(); + + private: + DISALLOW_COPY_AND_ASSIGN(TsfBridge); +}; + +} // namespace ui + +#endif // UI_BASE_WIN_TSF_BRIDGE_H_ @@ -345,6 +345,8 @@ 'base/win/shell.h', 'base/win/singleton_hwnd.cc', 'base/win/singleton_hwnd.h', + 'base/win/tsf_bridge.cc', + 'base/win/tsf_bridge.h', 'base/win/tsf_text_store.cc', 'base/win/tsf_text_store.h', 'base/win/window_impl.cc', |