Index: ui/base/ime/win/tsf_bridge.cc |
diff --git a/ui/base/ime/win/tsf_bridge.cc b/ui/base/ime/win/tsf_bridge.cc |
deleted file mode 100644 |
index efbc285d2c461a044b1a2e4b1847918d4056e08e..0000000000000000000000000000000000000000 |
--- a/ui/base/ime/win/tsf_bridge.cc |
+++ /dev/null |
@@ -1,558 +0,0 @@ |
-// 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 <map> |
- |
-#include "base/logging.h" |
-#include "base/memory/ref_counted.h" |
-#include "base/memory/scoped_ptr.h" |
-#include "base/message_loop/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/ime/text_input_client.h" |
-#include "ui/base/ime/win/tsf_bridge.h" |
-#include "ui/base/ime/win/tsf_text_store.h" |
- |
-namespace ui { |
- |
-namespace { |
- |
-// We use thread local storage for TSFBridge lifespan management. |
-base::ThreadLocalStorage::StaticSlot tls_tsf_bridge = TLS_INITIALIZER; |
- |
- |
-// TsfBridgeDelegate ----------------------------------------------------------- |
- |
-// A TLS implementation of TSFBridge. |
-class TSFBridgeDelegate : public TSFBridge { |
- public: |
- TSFBridgeDelegate(); |
- virtual ~TSFBridgeDelegate(); |
- |
- bool Initialize(); |
- |
- // TsfBridge: |
- virtual void OnTextInputTypeChanged(const TextInputClient* client) OVERRIDE; |
- virtual void OnTextLayoutChanged() OVERRIDE; |
- virtual bool CancelComposition() OVERRIDE; |
- virtual bool ConfirmComposition() OVERRIDE; |
- virtual void SetFocusedClient(HWND focused_window, |
- TextInputClient* client) OVERRIDE; |
- virtual void RemoveFocusedClient(TextInputClient* client) OVERRIDE; |
- virtual base::win::ScopedComPtr<ITfThreadMgr> GetThreadManager() OVERRIDE; |
- virtual TextInputClient* GetFocusedTextInputClient() const OVERRIDE; |
- |
- private: |
- // Returns true if |tsf_document_map_| is successfully initialized. This |
- // method should be called from and only from Initialize(). |
- bool InitializeDocumentMapInternal(); |
- |
- // Returns true if |context| is successfully updated to be a disabled |
- // context, where an IME should be deactivated. This is suitable for some |
- // special input context such as password fields. |
- bool InitializeDisabledContext(ITfContext* context); |
- |
- // Returns true if a TSF document manager and a TSF context is successfully |
- // created with associating with given |text_store|. The returned |
- // |source_cookie| indicates the binding between |text_store| and |context|. |
- // You can pass NULL to |text_store| and |source_cookie| when text store is |
- // not necessary. |
- bool CreateDocumentManager(TSFTextStore* text_store, |
- ITfDocumentMgr** document_manager, |
- ITfContext** context, |
- DWORD* source_cookie); |
- |
- // Returns true if |document_manager| is the focused document manager. |
- bool IsFocused(ITfDocumentMgr* document_manager); |
- |
- // Returns true if already initialized. |
- bool IsInitialized(); |
- |
- // Updates or clears the association maintained in the TSF runtime between |
- // |attached_window_handle_| and the current document manager. Keeping this |
- // association updated solves some tricky event ordering issues between |
- // logical text input focus managed by Chrome and native text input focus |
- // managed by the OS. |
- // Background: |
- // TSF runtime monitors some Win32 messages such as WM_ACTIVATE to |
- // change the focused document manager. This is problematic when |
- // TSFBridge::SetFocusedClient is called first then the target window |
- // receives WM_ACTIVATE. This actually occurs in Aura environment where |
- // WM_NCACTIVATE is used as a trigger to restore text input focus. |
- // Caveats: |
- // TSF runtime does not increment the reference count of the attached |
- // document manager. See the comment inside the method body for |
- // details. |
- void UpdateAssociateFocus(); |
- void ClearAssociateFocus(); |
- |
- // A triple of document manager, text store and binding cookie between |
- // a context owned by the document manager and the text store. This is a |
- // minimum working set of an editable document in TSF. |
- struct TSFDocument { |
- public: |
- TSFDocument() : cookie(TF_INVALID_COOKIE) {} |
- TSFDocument(const TSFDocument& src) |
- : document_manager(src.document_manager), |
- cookie(src.cookie) {} |
- base::win::ScopedComPtr<ITfDocumentMgr> document_manager; |
- scoped_refptr<TSFTextStore> text_store; |
- DWORD cookie; |
- }; |
- |
- // Returns a pointer to TSFDocument that is associated with the current |
- // TextInputType of |client_|. |
- TSFDocument* GetAssociatedDocument(); |
- |
- // An ITfThreadMgr object to be used in focus and document management. |
- base::win::ScopedComPtr<ITfThreadMgr> thread_manager_; |
- |
- // A map from TextInputType to an editable document for TSF. We use multiple |
- // TSF documents that have different InputScopes and TSF attributes based on |
- // the TextInputType associated with the target document. For a TextInputType |
- // that is not coverted by this map, a default document, e.g. the document |
- // for TEXT_INPUT_TYPE_TEXT, should be used. |
- // Note that some IMEs don't change their state unless the document focus is |
- // changed. This is why we use multiple documents instead of changing TSF |
- // metadata of a single document on the fly. |
- typedef std::map<TextInputType, TSFDocument> TSFDocumentMap; |
- TSFDocumentMap tsf_document_map_; |
- |
- // An identifier of TSF client. |
- TfClientId client_id_; |
- |
- // Current focused text input client. Do not free |client_|. |
- TextInputClient* client_; |
- |
- // Represents the window that is currently owns text input focus. |
- HWND attached_window_handle_; |
- |
- DISALLOW_COPY_AND_ASSIGN(TSFBridgeDelegate); |
-}; |
- |
-TSFBridgeDelegate::TSFBridgeDelegate() |
- : client_id_(TF_CLIENTID_NULL), |
- client_(NULL), |
- attached_window_handle_(NULL) { |
-} |
- |
-TSFBridgeDelegate::~TSFBridgeDelegate() { |
- DCHECK(base::MessageLoopForUI::IsCurrent()); |
- if (!IsInitialized()) |
- return; |
- for (TSFDocumentMap::iterator it = tsf_document_map_.begin(); |
- it != tsf_document_map_.end(); ++it) { |
- base::win::ScopedComPtr<ITfContext> context; |
- base::win::ScopedComPtr<ITfSource> source; |
- if (it->second.cookie != TF_INVALID_COOKIE && |
- SUCCEEDED(it->second.document_manager->GetBase(context.Receive())) && |
- SUCCEEDED(source.QueryFrom(context))) { |
- source->UnadviseSink(it->second.cookie); |
- } |
- } |
- tsf_document_map_.clear(); |
- |
- client_id_ = TF_CLIENTID_NULL; |
-} |
- |
-bool TSFBridgeDelegate::Initialize() { |
- DCHECK(base::MessageLoopForUI::IsCurrent()); |
- if (client_id_ != TF_CLIENTID_NULL) { |
- DVLOG(1) << "Already initialized."; |
- return false; |
- } |
- |
- if (FAILED(thread_manager_.CreateInstance(CLSID_TF_ThreadMgr))) { |
- DVLOG(1) << "Failed to create ThreadManager instance."; |
- return false; |
- } |
- |
- if (FAILED(thread_manager_->Activate(&client_id_))) { |
- DVLOG(1) << "Failed to activate Thread Manager."; |
- return false; |
- } |
- |
- if (!InitializeDocumentMapInternal()) |
- return false; |
- |
- // Japanese IME expects the default value of this compartment is |
- // TF_SENTENCEMODE_PHRASEPREDICT like IMM32 implementation. This value is |
- // managed per thread, so that it is enough to set this value at once. This |
- // value does not affect other language's IME behaviors. |
- base::win::ScopedComPtr<ITfCompartmentMgr> thread_compartment_manager; |
- if (FAILED(thread_compartment_manager.QueryFrom(thread_manager_))) { |
- DVLOG(1) << "Failed to get ITfCompartmentMgr."; |
- return false; |
- } |
- |
- base::win::ScopedComPtr<ITfCompartment> sentence_compartment; |
- if (FAILED(thread_compartment_manager->GetCompartment( |
- GUID_COMPARTMENT_KEYBOARD_INPUTMODE_SENTENCE, |
- sentence_compartment.Receive()))) { |
- DVLOG(1) << "Failed to get sentence compartment."; |
- return false; |
- } |
- |
- base::win::ScopedVariant sentence_variant; |
- sentence_variant.Set(TF_SENTENCEMODE_PHRASEPREDICT); |
- if (FAILED(sentence_compartment->SetValue(client_id_, &sentence_variant))) { |
- DVLOG(1) << "Failed to change the sentence mode."; |
- return false; |
- } |
- |
- return true; |
-} |
- |
-void TSFBridgeDelegate::OnTextInputTypeChanged(const TextInputClient* client) { |
- DCHECK(base::MessageLoopForUI::IsCurrent()); |
- DCHECK(IsInitialized()); |
- |
- if (client != client_) { |
- // Called from not focusing client. Do nothing. |
- return; |
- } |
- |
- UpdateAssociateFocus(); |
- |
- TSFDocument* document = GetAssociatedDocument(); |
- if (!document) |
- return; |
- thread_manager_->SetFocus(document->document_manager.get()); |
- OnTextLayoutChanged(); |
-} |
- |
-void TSFBridgeDelegate::OnTextLayoutChanged() { |
- TSFDocument* document = GetAssociatedDocument(); |
- if (!document) |
- return; |
- if (!document->text_store) |
- return; |
- document->text_store->SendOnLayoutChange(); |
-} |
- |
-bool TSFBridgeDelegate::CancelComposition() { |
- DCHECK(base::MessageLoopForUI::IsCurrent()); |
- DCHECK(IsInitialized()); |
- |
- TSFDocument* document = GetAssociatedDocument(); |
- if (!document) |
- return false; |
- if (!document->text_store) |
- return false; |
- |
- return document->text_store->CancelComposition(); |
-} |
- |
-bool TSFBridgeDelegate::ConfirmComposition() { |
- DCHECK(base::MessageLoopForUI::IsCurrent()); |
- DCHECK(IsInitialized()); |
- |
- TSFDocument* document = GetAssociatedDocument(); |
- if (!document) |
- return false; |
- if (!document->text_store) |
- return false; |
- |
- return document->text_store->ConfirmComposition(); |
-} |
- |
-void TSFBridgeDelegate::SetFocusedClient(HWND focused_window, |
- TextInputClient* client) { |
- DCHECK(base::MessageLoopForUI::IsCurrent()); |
- DCHECK(client); |
- DCHECK(IsInitialized()); |
- if (attached_window_handle_ != focused_window) |
- ClearAssociateFocus(); |
- client_ = client; |
- attached_window_handle_ = focused_window; |
- |
- for (TSFDocumentMap::iterator it = tsf_document_map_.begin(); |
- it != tsf_document_map_.end(); ++it) { |
- if (it->second.text_store.get() == NULL) |
- continue; |
- it->second.text_store->SetFocusedTextInputClient(focused_window, |
- client); |
- } |
- |
- // Synchronize text input type state. |
- OnTextInputTypeChanged(client); |
-} |
- |
-void TSFBridgeDelegate::RemoveFocusedClient(TextInputClient* client) { |
- DCHECK(base::MessageLoopForUI::IsCurrent()); |
- DCHECK(IsInitialized()); |
- if (client_ != client) |
- return; |
- ClearAssociateFocus(); |
- client_ = NULL; |
- attached_window_handle_ = NULL; |
- for (TSFDocumentMap::iterator it = tsf_document_map_.begin(); |
- it != tsf_document_map_.end(); ++it) { |
- if (it->second.text_store.get() == NULL) |
- continue; |
- it->second.text_store->SetFocusedTextInputClient(NULL, NULL); |
- } |
-} |
- |
-TextInputClient* TSFBridgeDelegate::GetFocusedTextInputClient() const { |
- return client_; |
-} |
- |
-base::win::ScopedComPtr<ITfThreadMgr> TSFBridgeDelegate::GetThreadManager() { |
- DCHECK(base::MessageLoopForUI::IsCurrent()); |
- DCHECK(IsInitialized()); |
- return thread_manager_; |
-} |
- |
-bool TSFBridgeDelegate::CreateDocumentManager(TSFTextStore* text_store, |
- ITfDocumentMgr** document_manager, |
- ITfContext** context, |
- DWORD* source_cookie) { |
- if (FAILED(thread_manager_->CreateDocumentMgr(document_manager))) { |
- DVLOG(1) << "Failed to create Document Manager."; |
- return false; |
- } |
- |
- DWORD edit_cookie = TF_INVALID_EDIT_COOKIE; |
- if (FAILED((*document_manager)->CreateContext( |
- client_id_, |
- 0, |
- static_cast<ITextStoreACP*>(text_store), |
- context, |
- &edit_cookie))) { |
- DVLOG(1) << "Failed to create Context."; |
- return false; |
- } |
- |
- if (FAILED((*document_manager)->Push(*context))) { |
- DVLOG(1) << "Failed to push context."; |
- return false; |
- } |
- |
- if (!text_store || !source_cookie) |
- return true; |
- |
- base::win::ScopedComPtr<ITfSource> source; |
- if (FAILED(source.QueryFrom(*context))) { |
- DVLOG(1) << "Failed to get source."; |
- return false; |
- } |
- |
- if (FAILED(source->AdviseSink(IID_ITfTextEditSink, |
- static_cast<ITfTextEditSink*>(text_store), |
- source_cookie))) { |
- DVLOG(1) << "AdviseSink failed."; |
- return false; |
- } |
- |
- if (*source_cookie == TF_INVALID_COOKIE) { |
- DVLOG(1) << "The result of cookie is invalid."; |
- return false; |
- } |
- return true; |
-} |
- |
-bool TSFBridgeDelegate::InitializeDocumentMapInternal() { |
- const TextInputType kTextInputTypes[] = { |
- TEXT_INPUT_TYPE_NONE, |
- TEXT_INPUT_TYPE_TEXT, |
- TEXT_INPUT_TYPE_PASSWORD, |
- TEXT_INPUT_TYPE_SEARCH, |
- TEXT_INPUT_TYPE_EMAIL, |
- TEXT_INPUT_TYPE_NUMBER, |
- TEXT_INPUT_TYPE_TELEPHONE, |
- TEXT_INPUT_TYPE_URL, |
- }; |
- for (size_t i = 0; i < arraysize(kTextInputTypes); ++i) { |
- const TextInputType input_type = kTextInputTypes[i]; |
- base::win::ScopedComPtr<ITfContext> context; |
- base::win::ScopedComPtr<ITfDocumentMgr> document_manager; |
- DWORD cookie = TF_INVALID_COOKIE; |
- const bool use_null_text_store = (input_type == TEXT_INPUT_TYPE_NONE); |
- DWORD* cookie_ptr = use_null_text_store ? NULL : &cookie; |
- scoped_refptr<TSFTextStore> text_store = |
- use_null_text_store ? NULL : new TSFTextStore(); |
- if (!CreateDocumentManager(text_store, |
- document_manager.Receive(), |
- context.Receive(), |
- cookie_ptr)) |
- return false; |
- const bool use_disabled_context = |
- (input_type == TEXT_INPUT_TYPE_PASSWORD || |
- input_type == TEXT_INPUT_TYPE_NONE); |
- if (use_disabled_context && !InitializeDisabledContext(context)) |
- return false; |
- tsf_document_map_[input_type].text_store = text_store; |
- tsf_document_map_[input_type].document_manager = document_manager; |
- tsf_document_map_[input_type].cookie = cookie; |
- } |
- return true; |
-} |
- |
-bool TSFBridgeDelegate::InitializeDisabledContext(ITfContext* context) { |
- base::win::ScopedComPtr<ITfCompartmentMgr> compartment_mgr; |
- if (FAILED(compartment_mgr.QueryFrom(context))) { |
- DVLOG(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()))) { |
- DVLOG(1) << "Failed to get keyboard disabled compartment."; |
- return false; |
- } |
- |
- base::win::ScopedVariant variant; |
- variant.Set(1); |
- if (FAILED(disabled_compartment->SetValue(client_id_, &variant))) { |
- DVLOG(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()))) { |
- DVLOG(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))) { |
- DVLOG(1) << "Failed to set empty context."; |
- return false; |
- } |
- |
- return true; |
-} |
- |
-bool TSFBridgeDelegate::IsFocused(ITfDocumentMgr* document_manager) { |
- base::win::ScopedComPtr<ITfDocumentMgr> focused_document_manager; |
- if (FAILED(thread_manager_->GetFocus(focused_document_manager.Receive()))) |
- return false; |
- return focused_document_manager.IsSameObject(document_manager); |
-} |
- |
-bool TSFBridgeDelegate::IsInitialized() { |
- return client_id_ != TF_CLIENTID_NULL; |
-} |
- |
-void TSFBridgeDelegate::UpdateAssociateFocus() { |
- if (attached_window_handle_ == NULL) |
- return; |
- TSFDocument* document = GetAssociatedDocument(); |
- if (document == NULL) { |
- ClearAssociateFocus(); |
- return; |
- } |
- // NOTE: ITfThreadMgr::AssociateFocus does not increment the ref count of |
- // the document manager to be attached. It is our responsibility to make sure |
- // the attached document manager will not be destroyed while it is attached. |
- // This should be true as long as TSFBridge::Shutdown() is called late phase |
- // of UI thread shutdown. |
- base::win::ScopedComPtr<ITfDocumentMgr> previous_focus; |
- thread_manager_->AssociateFocus( |
- attached_window_handle_, document->document_manager.get(), |
- previous_focus.Receive()); |
-} |
- |
-void TSFBridgeDelegate::ClearAssociateFocus() { |
- if (attached_window_handle_ == NULL) |
- return; |
- base::win::ScopedComPtr<ITfDocumentMgr> previous_focus; |
- thread_manager_->AssociateFocus( |
- attached_window_handle_, NULL, previous_focus.Receive()); |
-} |
- |
-TSFBridgeDelegate::TSFDocument* TSFBridgeDelegate::GetAssociatedDocument() { |
- if (!client_) |
- return NULL; |
- TSFDocumentMap::iterator it = |
- tsf_document_map_.find(client_->GetTextInputType()); |
- if (it == tsf_document_map_.end()) { |
- it = tsf_document_map_.find(TEXT_INPUT_TYPE_TEXT); |
- // This check is necessary because it's possible that we failed to |
- // initialize |tsf_document_map_| and it has no TEXT_INPUT_TYPE_TEXT. |
- if (it == tsf_document_map_.end()) |
- return NULL; |
- } |
- return &it->second; |
-} |
- |
-} // namespace |
- |
- |
-// TsfBridge ----------------------------------------------------------------- |
- |
-TSFBridge::TSFBridge() { |
-} |
- |
-TSFBridge::~TSFBridge() { |
-} |
- |
-// static |
-bool TSFBridge::Initialize() { |
- if (!base::MessageLoopForUI::IsCurrent()) { |
- DVLOG(1) << "Do not use TSFBridge without UI thread."; |
- return false; |
- } |
- if (!tls_tsf_bridge.initialized()) { |
- tls_tsf_bridge.Initialize(TSFBridge::Finalize); |
- } |
- TSFBridgeDelegate* delegate = |
- static_cast<TSFBridgeDelegate*>(tls_tsf_bridge.Get()); |
- if (delegate) |
- return true; |
- delegate = new TSFBridgeDelegate(); |
- tls_tsf_bridge.Set(delegate); |
- return delegate->Initialize(); |
-} |
- |
-// static |
-TSFBridge* TSFBridge::ReplaceForTesting(TSFBridge* bridge) { |
- if (!base::MessageLoopForUI::IsCurrent()) { |
- DVLOG(1) << "Do not use TSFBridge without UI thread."; |
- return NULL; |
- } |
- TSFBridge* old_bridge = TSFBridge::GetInstance(); |
- tls_tsf_bridge.Set(bridge); |
- return old_bridge; |
-} |
- |
-// static |
-void TSFBridge::Shutdown() { |
- if (!base::MessageLoopForUI::IsCurrent()) { |
- DVLOG(1) << "Do not use TSFBridge without UI thread."; |
- } |
- if (tls_tsf_bridge.initialized()) { |
- TSFBridgeDelegate* delegate = |
- static_cast<TSFBridgeDelegate*>(tls_tsf_bridge.Get()); |
- tls_tsf_bridge.Set(NULL); |
- delete delegate; |
- } |
-} |
- |
-// static |
-TSFBridge* TSFBridge::GetInstance() { |
- if (!base::MessageLoopForUI::IsCurrent()) { |
- DVLOG(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; |
-} |
- |
-// static |
-void TSFBridge::Finalize(void* data) { |
- TSFBridgeDelegate* delegate = static_cast<TSFBridgeDelegate*>(data); |
- delete delegate; |
-} |
- |
-} // namespace ui |