Commit 765b0996 authored by Alex Keng's avatar Alex Keng Committed by Commit Bot

TSF: propagate HRESULT from TSF initialization

This CL changes the return value of TSFBridge::Initialize from void to
HRESULT and refactors the logics in TSFTextStore::TSFTextStore out to
TSFTextStore::Initialize. The HRESULTs of various COM calls during TSF
initialization are propagated so the caller can handle errors, if any,
accordingly.

Note at the moment upstream doesn't have any use case for the HRESULT,
but downstream will use the value for WebView2 initialization, ex.
handling errors due to creating CLSID_TF_CategoryMgr instance in a
thread initialized with COM_MTA (while all TSF functions expect STA)

Change-Id: I227000a594f203eecc37838ff877d3dcd077df2f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2340543
Commit-Queue: Alex Keng <shihken@microsoft.com>
Reviewed-by: default avatarYohei Yukawa <yukawa@chromium.org>
Cr-Commit-Position: refs/heads/master@{#797396}
parent d24ca9ba
...@@ -31,7 +31,7 @@ class TSFBridgeImpl : public TSFBridge { ...@@ -31,7 +31,7 @@ class TSFBridgeImpl : public TSFBridge {
TSFBridgeImpl(); TSFBridgeImpl();
~TSFBridgeImpl() override; ~TSFBridgeImpl() override;
bool Initialize(); HRESULT Initialize();
// TsfBridge: // TsfBridge:
void OnTextInputTypeChanged(const TextInputClient* client) override; void OnTextInputTypeChanged(const TextInputClient* client) override;
...@@ -48,24 +48,24 @@ class TSFBridgeImpl : public TSFBridge { ...@@ -48,24 +48,24 @@ class TSFBridgeImpl : public TSFBridge {
void SetInputPanelPolicy(bool input_panel_policy_manual) override; void SetInputPanelPolicy(bool input_panel_policy_manual) override;
private: private:
// Returns true if |tsf_document_map_| is successfully initialized. This // Returns S_OK if |tsf_document_map_| is successfully initialized. This
// method should be called from and only from Initialize(). // method should be called from and only from Initialize().
bool InitializeDocumentMapInternal(); HRESULT InitializeDocumentMapInternal();
// Returns true if |context| is successfully updated to be a disabled // Returns S_OK if |context| is successfully updated to be a disabled
// context, where an IME should be deactivated. This is suitable for some // context, where an IME should be deactivated. This is suitable for some
// special input context such as password fields. // special input context such as password fields.
bool InitializeDisabledContext(ITfContext* context); HRESULT InitializeDisabledContext(ITfContext* context);
// Returns true if a TSF document manager and a TSF context is successfully // Returns S_OK if a TSF document manager and a TSF context is successfully
// created with associating with given |text_store|. The returned // created with associating with given |text_store|. The returned
// |source_cookie| indicates the binding between |text_store| and |context|. // |source_cookie| indicates the binding between |text_store| and |context|.
// You can pass nullptr to |text_store| and |source_cookie| when text store is // You can pass nullptr to |text_store| and |source_cookie| when text store is
// not necessary. // not necessary.
bool CreateDocumentManager(TSFTextStore* text_store, HRESULT CreateDocumentManager(TSFTextStore* text_store,
ITfDocumentMgr** document_manager, ITfDocumentMgr** document_manager,
ITfContext** context, ITfContext** context,
DWORD* source_cookie); DWORD* source_cookie);
// Returns true if |document_manager| is the focused document manager. // Returns true if |document_manager| is the focused document manager.
bool IsFocused(ITfDocumentMgr* document_manager); bool IsFocused(ITfDocumentMgr* document_manager);
...@@ -178,61 +178,66 @@ TSFBridgeImpl::~TSFBridgeImpl() { ...@@ -178,61 +178,66 @@ TSFBridgeImpl::~TSFBridgeImpl() {
client_id_ = TF_CLIENTID_NULL; client_id_ = TF_CLIENTID_NULL;
} }
bool TSFBridgeImpl::Initialize() { HRESULT TSFBridgeImpl::Initialize() {
DCHECK(base::CurrentUIThread::IsSet()); DCHECK(base::CurrentUIThread::IsSet());
if (client_id_ != TF_CLIENTID_NULL) { if (client_id_ != TF_CLIENTID_NULL) {
DVLOG(1) << "Already initialized."; DVLOG(1) << "Already initialized.";
return false; return S_FALSE;
} }
if (FAILED(::CoCreateInstance(CLSID_TF_InputProcessorProfiles, nullptr, HRESULT hr =
CLSCTX_ALL, ::CoCreateInstance(CLSID_TF_InputProcessorProfiles, nullptr, CLSCTX_ALL,
IID_PPV_ARGS(&input_processor_profiles_)))) { IID_PPV_ARGS(&input_processor_profiles_));
if (FAILED(hr)) {
DVLOG(1) << "Failed to create InputProcessorProfiles instance."; DVLOG(1) << "Failed to create InputProcessorProfiles instance.";
return false; return hr;
} }
if (FAILED(::CoCreateInstance(CLSID_TF_ThreadMgr, nullptr, CLSCTX_ALL, hr = ::CoCreateInstance(CLSID_TF_ThreadMgr, nullptr, CLSCTX_ALL,
IID_PPV_ARGS(&thread_manager_)))) { IID_PPV_ARGS(&thread_manager_));
if (FAILED(hr)) {
DVLOG(1) << "Failed to create ThreadManager instance."; DVLOG(1) << "Failed to create ThreadManager instance.";
return false; return hr;
} }
if (FAILED(thread_manager_->Activate(&client_id_))) { hr = thread_manager_->Activate(&client_id_);
if (FAILED(hr)) {
DVLOG(1) << "Failed to activate Thread Manager."; DVLOG(1) << "Failed to activate Thread Manager.";
return false; return hr;
} }
if (!InitializeDocumentMapInternal()) hr = InitializeDocumentMapInternal();
return false; if (FAILED(hr))
return hr;
// Japanese IME expects the default value of this compartment is // Japanese IME expects the default value of this compartment is
// TF_SENTENCEMODE_PHRASEPREDICT like IMM32 implementation. This value 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 // managed per thread, so that it is enough to set this value at once. This
// value does not affect other language's IME behaviors. // value does not affect other language's IME behaviors.
Microsoft::WRL::ComPtr<ITfCompartmentMgr> thread_compartment_manager; Microsoft::WRL::ComPtr<ITfCompartmentMgr> thread_compartment_manager;
if (FAILED(thread_manager_.As(&thread_compartment_manager))) { hr = thread_manager_.As(&thread_compartment_manager);
if (FAILED(hr)) {
DVLOG(1) << "Failed to get ITfCompartmentMgr."; DVLOG(1) << "Failed to get ITfCompartmentMgr.";
return false; return hr;
} }
Microsoft::WRL::ComPtr<ITfCompartment> sentence_compartment; Microsoft::WRL::ComPtr<ITfCompartment> sentence_compartment;
if (FAILED(thread_compartment_manager->GetCompartment( hr = thread_compartment_manager->GetCompartment(
GUID_COMPARTMENT_KEYBOARD_INPUTMODE_SENTENCE, GUID_COMPARTMENT_KEYBOARD_INPUTMODE_SENTENCE, &sentence_compartment);
&sentence_compartment))) { if (FAILED(hr)) {
DVLOG(1) << "Failed to get sentence compartment."; DVLOG(1) << "Failed to get sentence compartment.";
return false; return hr;
} }
base::win::ScopedVariant sentence_variant; base::win::ScopedVariant sentence_variant;
sentence_variant.Set(TF_SENTENCEMODE_PHRASEPREDICT); sentence_variant.Set(TF_SENTENCEMODE_PHRASEPREDICT);
if (FAILED( hr = sentence_compartment->SetValue(client_id_, sentence_variant.ptr());
sentence_compartment->SetValue(client_id_, sentence_variant.ptr()))) { if (FAILED(hr)) {
DVLOG(1) << "Failed to change the sentence mode."; DVLOG(1) << "Failed to change the sentence mode.";
return false; return hr;
} }
return true; return S_OK;
} }
void TSFBridgeImpl::OnTextInputTypeChanged(const TextInputClient* client) { void TSFBridgeImpl::OnTextInputTypeChanged(const TextInputClient* client) {
...@@ -386,83 +391,89 @@ Microsoft::WRL::ComPtr<ITfThreadMgr> TSFBridgeImpl::GetThreadManager() { ...@@ -386,83 +391,89 @@ Microsoft::WRL::ComPtr<ITfThreadMgr> TSFBridgeImpl::GetThreadManager() {
return thread_manager_; return thread_manager_;
} }
bool TSFBridgeImpl::CreateDocumentManager(TSFTextStore* text_store, HRESULT TSFBridgeImpl::CreateDocumentManager(TSFTextStore* text_store,
ITfDocumentMgr** document_manager, ITfDocumentMgr** document_manager,
ITfContext** context, ITfContext** context,
DWORD* source_cookie) { DWORD* source_cookie) {
if (FAILED(thread_manager_->CreateDocumentMgr(document_manager))) { HRESULT hr = thread_manager_->CreateDocumentMgr(document_manager);
if (FAILED(hr)) {
DVLOG(1) << "Failed to create Document Manager."; DVLOG(1) << "Failed to create Document Manager.";
return false; return hr;
} }
if (!text_store || !source_cookie) if (!text_store || !source_cookie)
return true; return S_OK;
DWORD edit_cookie = TF_INVALID_EDIT_COOKIE; DWORD edit_cookie = TF_INVALID_EDIT_COOKIE;
if (FAILED((*document_manager) hr = (*document_manager)
->CreateContext(client_id_, 0, ->CreateContext(client_id_, 0,
static_cast<ITextStoreACP*>(text_store), static_cast<ITextStoreACP*>(text_store), context,
context, &edit_cookie))) { &edit_cookie);
if (FAILED(hr)) {
DVLOG(1) << "Failed to create Context."; DVLOG(1) << "Failed to create Context.";
return false; return hr;
} }
if (FAILED((*document_manager)->Push(*context))) { hr = (*document_manager)->Push(*context);
if (FAILED(hr)) {
DVLOG(1) << "Failed to push context."; DVLOG(1) << "Failed to push context.";
return false; return hr;
} }
Microsoft::WRL::ComPtr<ITfSource> source; Microsoft::WRL::ComPtr<ITfSource> source;
if (FAILED((*context)->QueryInterface(IID_PPV_ARGS(&source)))) { hr = (*context)->QueryInterface(IID_PPV_ARGS(&source));
if (FAILED(hr)) {
DVLOG(1) << "Failed to get source."; DVLOG(1) << "Failed to get source.";
return false; return hr;
} }
if (FAILED(source->AdviseSink(IID_ITfTextEditSink, hr = source->AdviseSink(IID_ITfTextEditSink,
static_cast<ITfTextEditSink*>(text_store), static_cast<ITfTextEditSink*>(text_store),
source_cookie))) { source_cookie);
if (FAILED(hr)) {
DVLOG(1) << "AdviseSink failed."; DVLOG(1) << "AdviseSink failed.";
return false; return hr;
} }
Microsoft::WRL::ComPtr<ITfSource> source_ITfThreadMgr; Microsoft::WRL::ComPtr<ITfSource> source_ITfThreadMgr;
if (FAILED(thread_manager_->QueryInterface( hr = thread_manager_->QueryInterface(IID_PPV_ARGS(&source_ITfThreadMgr));
IID_PPV_ARGS(&source_ITfThreadMgr)))) { if (FAILED(hr)) {
DVLOG(1) << "Failed to get source_ITfThreadMgr."; DVLOG(1) << "Failed to get source_ITfThreadMgr.";
return false; return hr;
} }
if (FAILED(source_ITfThreadMgr->AdviseSink( hr = source_ITfThreadMgr->AdviseSink(
IID_ITfKeyTraceEventSink, IID_ITfKeyTraceEventSink, static_cast<ITfKeyTraceEventSink*>(text_store),
static_cast<ITfKeyTraceEventSink*>(text_store), &key_trace_sink_cookie_);
&key_trace_sink_cookie_))) { if (FAILED(hr)) {
DVLOG(1) << "AdviseSink for ITfKeyTraceEventSink failed."; DVLOG(1) << "AdviseSink for ITfKeyTraceEventSink failed.";
return false; return hr;
} }
Microsoft::WRL::ComPtr<ITfSource> language_source; Microsoft::WRL::ComPtr<ITfSource> language_source;
if (FAILED(input_processor_profiles_->QueryInterface( hr =
IID_PPV_ARGS(&language_source)))) { input_processor_profiles_->QueryInterface(IID_PPV_ARGS(&language_source));
if (FAILED(hr)) {
DVLOG(1) << "Failed to get source_ITfInputProcessorProfiles."; DVLOG(1) << "Failed to get source_ITfInputProcessorProfiles.";
return false; return hr;
} }
if (FAILED( hr = language_source->AdviseSink(IID_ITfLanguageProfileNotifySink,
language_source->AdviseSink(IID_ITfLanguageProfileNotifySink, static_cast<ITfTextEditSink*>(text_store),
static_cast<ITfTextEditSink*>(text_store), &language_profile_cookie_);
&language_profile_cookie_))) { if (FAILED(hr)) {
DVLOG(1) << "AdviseSink for language profile notify sink failed."; DVLOG(1) << "AdviseSink for language profile notify sink failed.";
return false; return hr;
} }
if (*source_cookie == TF_INVALID_COOKIE) { if (*source_cookie == TF_INVALID_COOKIE) {
DVLOG(1) << "The result of cookie is invalid."; DVLOG(1) << "The result of cookie is invalid.";
return false; return E_FAIL;
} }
return true; return S_OK;
} }
bool TSFBridgeImpl::InitializeDocumentMapInternal() { HRESULT TSFBridgeImpl::InitializeDocumentMapInternal() {
const TextInputType kTextInputTypes[] = { const TextInputType kTextInputTypes[] = {
TEXT_INPUT_TYPE_NONE, TEXT_INPUT_TYPE_TEXT, TEXT_INPUT_TYPE_NONE, TEXT_INPUT_TYPE_TEXT,
TEXT_INPUT_TYPE_PASSWORD, TEXT_INPUT_TYPE_SEARCH, TEXT_INPUT_TYPE_PASSWORD, TEXT_INPUT_TYPE_SEARCH,
...@@ -478,57 +489,70 @@ bool TSFBridgeImpl::InitializeDocumentMapInternal() { ...@@ -478,57 +489,70 @@ bool TSFBridgeImpl::InitializeDocumentMapInternal() {
DWORD* cookie_ptr = use_null_text_store ? nullptr : &cookie; DWORD* cookie_ptr = use_null_text_store ? nullptr : &cookie;
scoped_refptr<TSFTextStore> text_store = scoped_refptr<TSFTextStore> text_store =
use_null_text_store ? nullptr : new TSFTextStore(); use_null_text_store ? nullptr : new TSFTextStore();
if (!CreateDocumentManager(text_store.get(), &document_manager, &context, HRESULT hr = S_OK;
cookie_ptr)) if (text_store) {
return false; HRESULT hr = text_store->Initialize();
if ((input_type == TEXT_INPUT_TYPE_PASSWORD) && if (FAILED(hr))
!InitializeDisabledContext(context.Get())) return hr;
return false; }
hr = CreateDocumentManager(text_store.get(), &document_manager, &context,
cookie_ptr);
if (FAILED(hr))
return hr;
if (input_type == TEXT_INPUT_TYPE_PASSWORD) {
hr = InitializeDisabledContext(context.Get());
if (FAILED(hr))
return hr;
}
tsf_document_map_[input_type].text_store = text_store; tsf_document_map_[input_type].text_store = text_store;
tsf_document_map_[input_type].document_manager = document_manager; tsf_document_map_[input_type].document_manager = document_manager;
tsf_document_map_[input_type].cookie = cookie; tsf_document_map_[input_type].cookie = cookie;
if (text_store) if (text_store)
text_store->OnContextInitialized(context.Get()); text_store->OnContextInitialized(context.Get());
} }
return true; return S_OK;
} }
bool TSFBridgeImpl::InitializeDisabledContext(ITfContext* context) { HRESULT TSFBridgeImpl::InitializeDisabledContext(ITfContext* context) {
Microsoft::WRL::ComPtr<ITfCompartmentMgr> compartment_mgr; Microsoft::WRL::ComPtr<ITfCompartmentMgr> compartment_mgr;
if (FAILED(context->QueryInterface(IID_PPV_ARGS(&compartment_mgr)))) { HRESULT hr = context->QueryInterface(IID_PPV_ARGS(&compartment_mgr));
if (FAILED(hr)) {
DVLOG(1) << "Failed to get CompartmentMgr."; DVLOG(1) << "Failed to get CompartmentMgr.";
return false; return hr;
} }
Microsoft::WRL::ComPtr<ITfCompartment> disabled_compartment; Microsoft::WRL::ComPtr<ITfCompartment> disabled_compartment;
if (FAILED(compartment_mgr->GetCompartment(GUID_COMPARTMENT_KEYBOARD_DISABLED, hr = compartment_mgr->GetCompartment(GUID_COMPARTMENT_KEYBOARD_DISABLED,
&disabled_compartment))) { &disabled_compartment);
if (FAILED(hr)) {
DVLOG(1) << "Failed to get keyboard disabled compartment."; DVLOG(1) << "Failed to get keyboard disabled compartment.";
return false; return hr;
} }
base::win::ScopedVariant variant; base::win::ScopedVariant variant;
variant.Set(1); variant.Set(1);
if (FAILED(disabled_compartment->SetValue(client_id_, variant.ptr()))) { hr = disabled_compartment->SetValue(client_id_, variant.ptr());
if (FAILED(hr)) {
DVLOG(1) << "Failed to disable the DocumentMgr."; DVLOG(1) << "Failed to disable the DocumentMgr.";
return false; return hr;
} }
Microsoft::WRL::ComPtr<ITfCompartment> empty_context; Microsoft::WRL::ComPtr<ITfCompartment> empty_context;
if (FAILED(compartment_mgr->GetCompartment(GUID_COMPARTMENT_EMPTYCONTEXT, hr = compartment_mgr->GetCompartment(GUID_COMPARTMENT_EMPTYCONTEXT,
&empty_context))) { &empty_context);
if (FAILED(hr)) {
DVLOG(1) << "Failed to get empty context compartment."; DVLOG(1) << "Failed to get empty context compartment.";
return false; return hr;
} }
base::win::ScopedVariant empty_context_variant; base::win::ScopedVariant empty_context_variant;
empty_context_variant.Set(static_cast<int32_t>(1)); empty_context_variant.Set(static_cast<int32_t>(1));
if (FAILED( hr = empty_context->SetValue(client_id_, empty_context_variant.ptr());
empty_context->SetValue(client_id_, empty_context_variant.ptr()))) { if (FAILED(hr)) {
DVLOG(1) << "Failed to set empty context."; DVLOG(1) << "Failed to set empty context.";
return false; return hr;
} }
return true; return S_OK;
} }
bool TSFBridgeImpl::IsFocused(ITfDocumentMgr* document_manager) { bool TSFBridgeImpl::IsFocused(ITfDocumentMgr* document_manager) {
...@@ -617,20 +641,20 @@ TSFBridge::TSFBridge() {} ...@@ -617,20 +641,20 @@ TSFBridge::TSFBridge() {}
TSFBridge::~TSFBridge() {} TSFBridge::~TSFBridge() {}
// static // static
void TSFBridge::Initialize() { HRESULT TSFBridge::Initialize() {
if (!base::CurrentUIThread::IsSet()) { if (!base::CurrentUIThread::IsSet()) {
DVLOG(1) << "Do not use TSFBridge without UI thread."; DVLOG(1) << "Do not use TSFBridge without UI thread.";
return; return E_FAIL;
} }
TSFBridgeImpl* delegate = static_cast<TSFBridgeImpl*>(TSFBridgeTLS().Get()); TSFBridgeImpl* delegate = static_cast<TSFBridgeImpl*>(TSFBridgeTLS().Get());
if (delegate) if (delegate)
return; return S_OK;
// If we aren't supporting TSF early out. // If we aren't supporting TSF early out.
if (!base::FeatureList::IsEnabled(features::kTSFImeSupport)) if (!base::FeatureList::IsEnabled(features::kTSFImeSupport))
return; return E_FAIL;
delegate = new TSFBridgeImpl(); delegate = new TSFBridgeImpl();
TSFBridgeTLS().Set(delegate); TSFBridgeTLS().Set(delegate);
delegate->Initialize(); return delegate->Initialize();
} }
// static // static
......
...@@ -36,7 +36,7 @@ class COMPONENT_EXPORT(UI_BASE_IME_WIN) TSFBridge { ...@@ -36,7 +36,7 @@ class COMPONENT_EXPORT(UI_BASE_IME_WIN) TSFBridge {
// Sets the thread local instance. Must be called before any calls to // Sets the thread local instance. Must be called before any calls to
// GetInstance(). // GetInstance().
static void Initialize(); static HRESULT Initialize();
// Injects an alternative TSFBridge such as MockTSFBridge for testing. The // Injects an alternative TSFBridge such as MockTSFBridge for testing. The
// injected object should be released by the caller. This function returns // injected object should be released by the caller. This function returns
......
...@@ -187,6 +187,7 @@ class TSFInputPanelTest : public testing::Test { ...@@ -187,6 +187,7 @@ class TSFInputPanelTest : public testing::Test {
protected: protected:
void SetUp() override { void SetUp() override {
text_store_ = new TSFTextStore(); text_store_ = new TSFTextStore();
EXPECT_EQ(S_OK, text_store_->Initialize());
sink_ = new MockStoreACPSink(); sink_ = new MockStoreACPSink();
EXPECT_EQ(S_OK, text_store_->AdviseSink(IID_ITextStoreACPSink, sink_.get(), EXPECT_EQ(S_OK, text_store_->AdviseSink(IID_ITextStoreACPSink, sink_.get(),
TS_AS_ALL_SINKS)); TS_AS_ALL_SINKS));
...@@ -216,7 +217,9 @@ class TSFMultipleInputPanelTest : public testing::Test { ...@@ -216,7 +217,9 @@ class TSFMultipleInputPanelTest : public testing::Test {
protected: protected:
void SetUp() override { void SetUp() override {
text_store1_ = new TSFTextStore(); text_store1_ = new TSFTextStore();
EXPECT_EQ(S_OK, text_store1_->Initialize());
text_store2_ = new TSFTextStore(); text_store2_ = new TSFTextStore();
EXPECT_EQ(S_OK, text_store2_->Initialize());
sink1_ = new MockStoreACPSink(); sink1_ = new MockStoreACPSink();
sink2_ = new MockStoreACPSink(); sink2_ = new MockStoreACPSink();
EXPECT_EQ(S_OK, text_store1_->AdviseSink(IID_ITextStoreACPSink, EXPECT_EQ(S_OK, text_store1_->AdviseSink(IID_ITextStoreACPSink,
......
...@@ -47,21 +47,27 @@ bool GetWindowClientRect(HWND window_handle, ...@@ -47,21 +47,27 @@ bool GetWindowClientRect(HWND window_handle,
} // namespace } // namespace
TSFTextStore::TSFTextStore() { TSFTextStore::TSFTextStore() {}
if (FAILED(::CoCreateInstance(CLSID_TF_CategoryMgr, nullptr, CLSCTX_ALL,
IID_PPV_ARGS(&category_manager_)))) { TSFTextStore::~TSFTextStore() {}
LOG(FATAL) << "Failed to initialize CategoryMgr.";
return; HRESULT TSFTextStore::Initialize() {
HRESULT hr = ::CoCreateInstance(CLSID_TF_CategoryMgr, nullptr, CLSCTX_ALL,
IID_PPV_ARGS(&category_manager_));
if (FAILED(hr)) {
DVLOG(1) << "Failed to initialize CategoryMgr.";
return hr;
} }
if (FAILED(::CoCreateInstance(CLSID_TF_DisplayAttributeMgr, nullptr,
CLSCTX_ALL, hr = ::CoCreateInstance(CLSID_TF_DisplayAttributeMgr, nullptr, CLSCTX_ALL,
IID_PPV_ARGS(&display_attribute_manager_)))) { IID_PPV_ARGS(&display_attribute_manager_));
LOG(FATAL) << "Failed to initialize DisplayAttributeMgr."; if (FAILED(hr)) {
return; DVLOG(1) << "Failed to initialize DisplayAttributeMgr.";
return hr;
} }
}
TSFTextStore::~TSFTextStore() {} return S_OK;
}
ULONG STDMETHODCALLTYPE TSFTextStore::AddRef() { ULONG STDMETHODCALLTYPE TSFTextStore::AddRef() {
return InterlockedIncrement(&ref_count_); return InterlockedIncrement(&ref_count_);
......
...@@ -108,6 +108,7 @@ class COMPONENT_EXPORT(UI_BASE_IME_WIN) TSFTextStore ...@@ -108,6 +108,7 @@ class COMPONENT_EXPORT(UI_BASE_IME_WIN) TSFTextStore
public: public:
TSFTextStore(); TSFTextStore();
virtual ~TSFTextStore(); virtual ~TSFTextStore();
HRESULT Initialize();
// ITextStoreACP: // ITextStoreACP:
IFACEMETHODIMP_(ULONG) AddRef() override; IFACEMETHODIMP_(ULONG) AddRef() override;
......
...@@ -140,6 +140,7 @@ class TSFTextStoreTest : public testing::Test { ...@@ -140,6 +140,7 @@ class TSFTextStoreTest : public testing::Test {
protected: protected:
void SetUp() override { void SetUp() override {
text_store_ = new TSFTextStore(); text_store_ = new TSFTextStore();
EXPECT_EQ(S_OK, text_store_->Initialize());
sink_ = new MockStoreACPSink(); sink_ = new MockStoreACPSink();
EXPECT_EQ(S_OK, text_store_->AdviseSink(IID_ITextStoreACPSink, sink_.get(), EXPECT_EQ(S_OK, text_store_->AdviseSink(IID_ITextStoreACPSink, sink_.get(),
TS_AS_ALL_SINKS)); TS_AS_ALL_SINKS));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment