summaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authornona@chromium.org <nona@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-09-05 06:45:43 +0000
committernona@chromium.org <nona@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-09-05 06:45:43 +0000
commit99ef93de56bae720a4a7d188d7fc631088613b05 (patch)
tree61bf034566933b0e691a6e1a44212225d1ed1408 /ui
parentca61722ee9d47c5e103f339fe2ac87d96b8ab29a (diff)
downloadchromium_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.cc341
-rw-r--r--ui/base/win/tsf_bridge.h75
-rw-r--r--ui/ui.gyp2
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_
diff --git a/ui/ui.gyp b/ui/ui.gyp
index 7556035..d46650b 100644
--- a/ui/ui.gyp
+++ b/ui/ui.gyp
@@ -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',