Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add full support for Text Services Framework on Windows #16

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 20 additions & 0 deletions content/browser/renderer_host/render_widget_host_view_aura.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1541,6 +1541,26 @@ bool RenderWidgetHostViewAura::ShouldDoLearning() {
return GetTextInputManager() && GetTextInputManager()->should_do_learning();
}

#if defined(OS_WIN)
void RenderWidgetHostViewAura::DispatchKeyEventForIME(ui::KeyEvent* key) {
if (window_ && window_->GetHost()) {
window_->GetHost()->DispatchKeyEventPostIME(key, base::NullCallback());
}
}

void RenderWidgetHostViewAura::SetCompositionFromExistingText(
size_t start,
size_t end,
const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) {
RenderFrameHostImpl* frame = GetFocusedFrame();
if (frame) {
frame->GetFrameInputHandler()->SetCompositionFromExistingText(
start, end, ui_ime_text_spans);
has_composition_text_ = true;
}
}
#endif

////////////////////////////////////////////////////////////////////////////////
// RenderWidgetHostViewAura, display::DisplayObserver implementation:

Expand Down
9 changes: 9 additions & 0 deletions content/browser/renderer_host/render_widget_host_view_aura.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,15 @@ class CONTENT_EXPORT RenderWidgetHostViewAura
ukm::SourceId GetClientSourceForMetrics() const override;
bool ShouldDoLearning() override;

#if defined(OS_WIN)
// Ovrridden for ui::TextInputClient(Windows only):
void DispatchKeyEventForIME(ui::KeyEvent* key) override;
void SetCompositionFromExistingText(
size_t start,
size_t end,
const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) override;
#endif

// Overridden from display::DisplayObserver:
void OnDisplayMetricsChanged(const display::Display& display,
uint32_t metrics) override;
Expand Down
16 changes: 16 additions & 0 deletions ui/base/ime/text_input_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,22 @@ class UI_BASE_IME_EXPORT TextInputClient {
// improve typing suggestions for the user. This should return false for text
// fields that are considered 'private' (e.g. in incognito tabs).
virtual bool ShouldDoLearning() = 0;

#if defined(OS_WIN)
// Dispatch a key event from input service to text input client. This should
// only be used for composition scenario since the IME will consume windows
// native key message and we won't receive any key events. We need to
// synthesize key event and notify text input client to fire corresponding
// javascript key events. This is windows only.
virtual void DispatchKeyEventForIME(ui::KeyEvent* key) {}

// Start a composition range for existing text. This should only be used for
// composition scenario when IME want to start composition on existing text.
virtual void SetCompositionFromExistingText(
size_t start,
size_t end,
const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) {}
#endif
};

} // namespace ui
Expand Down
49 changes: 43 additions & 6 deletions ui/base/ime/win/tsf_bridge.cc
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ class TSFBridgeImpl : public TSFBridge {
// Represents the window that is currently owns text input focus.
HWND attached_window_handle_ = nullptr;

// Handle to ITfKeyTraceEventSink.
DWORD key_trace_sink_cookie_ = 0;

DISALLOW_COPY_AND_ASSIGN(TSFBridgeImpl);
};

Expand All @@ -135,6 +138,14 @@ TSFBridgeImpl::~TSFBridgeImpl() {
DCHECK(base::MessageLoopCurrentForUI::IsSet());
if (!IsInitialized())
return;

if (thread_manager_ != nullptr) {
Microsoft::WRL::ComPtr<ITfSource> source;
if (SUCCEEDED(thread_manager_->QueryInterface(IID_PPV_ARGS(&source)))) {
source->UnadviseSink(key_trace_sink_cookie_);
}
}

for (TSFDocumentMap::iterator it = tsf_document_map_.begin();
it != tsf_document_map_.end(); ++it) {
Microsoft::WRL::ComPtr<ITfContext> context;
Expand Down Expand Up @@ -311,6 +322,9 @@ bool TSFBridgeImpl::CreateDocumentManager(TSFTextStore* text_store,
return false;
}

if (!text_store || !source_cookie)
return true;

DWORD edit_cookie = TF_INVALID_EDIT_COOKIE;
if (FAILED((*document_manager)
->CreateContext(client_id_, 0,
Expand All @@ -325,9 +339,6 @@ bool TSFBridgeImpl::CreateDocumentManager(TSFTextStore* text_store,
return false;
}

if (!text_store || !source_cookie)
return true;

Microsoft::WRL::ComPtr<ITfSource> source;
if (FAILED((*context)->QueryInterface(IID_PPV_ARGS(&source)))) {
DVLOG(1) << "Failed to get source.";
Expand All @@ -341,6 +352,21 @@ bool TSFBridgeImpl::CreateDocumentManager(TSFTextStore* text_store,
return false;
}

Microsoft::WRL::ComPtr<ITfSource> source_ITfThreadMgr;
if (FAILED(thread_manager_->QueryInterface(
IID_PPV_ARGS(&source_ITfThreadMgr)))) {
DVLOG(1) << "Failed to get source_ITfThreadMgr.";
return false;
}

if (FAILED(source_ITfThreadMgr->AdviseSink(
IID_ITfKeyTraceEventSink,
static_cast<ITfKeyTraceEventSink*>(text_store),
&key_trace_sink_cookie_))) {
DVLOG(1) << "AdviseSink for ITfKeyTraceEventSink failed.";
return false;
}

if (*source_cookie == TF_INVALID_COOKIE) {
DVLOG(1) << "The result of cookie is invalid.";
return false;
Expand Down Expand Up @@ -368,9 +394,8 @@ bool TSFBridgeImpl::InitializeDocumentMapInternal() {
document_manager.GetAddressOf(),
context.GetAddressOf(), 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.Get()))
if ((input_type == TEXT_INPUT_TYPE_PASSWORD) &&
!InitializeDisabledContext(context.Get()))
return false;
tsf_document_map_[input_type].text_store = text_store;
tsf_document_map_[input_type].document_manager = document_manager;
Expand Down Expand Up @@ -419,6 +444,10 @@ bool TSFBridgeImpl::InitializeDisabledContext(ITfContext* context) {
}

bool TSFBridgeImpl::IsFocused(ITfDocumentMgr* document_manager) {
if (!IsInitialized()) {
// Hasn't been initialized yet. Return false.
return false;
}
Microsoft::WRL::ComPtr<ITfDocumentMgr> focused_document_manager;
if (FAILED(
thread_manager_->GetFocus(focused_document_manager.GetAddressOf())))
Expand All @@ -431,6 +460,10 @@ bool TSFBridgeImpl::IsInitialized() {
}

void TSFBridgeImpl::UpdateAssociateFocus() {
if (!IsInitialized()) {
// Hasn't been initialized yet. Do nothing.
return;
}
if (attached_window_handle_ == nullptr)
return;
TSFDocument* document = GetAssociatedDocument();
Expand All @@ -450,6 +483,10 @@ void TSFBridgeImpl::UpdateAssociateFocus() {
}

void TSFBridgeImpl::ClearAssociateFocus() {
if (!IsInitialized()) {
// Hasn't been initialized yet. Do nothing.
return;
}
if (attached_window_handle_ == nullptr)
return;
Microsoft::WRL::ComPtr<ITfDocumentMgr> previous_focus;
Expand Down