Changeset 24285 in webkit


Ignore:
Timestamp:
Jul 14, 2007 1:21:24 AM (17 years ago)
Author:
oliver
Message:

Reviewed by Darin and Alexey.

Fix for <rdar://problem/5231528> Inline input of International text (IME)

http://bugs.webkit.org/show_bug.cgi?id=14331

This patch adds IME support to WebKit/win, it currently does not support
reconversion (<rdar://problem/5334818>) and has issues with the chinese
IMEs (<rdar://problem/5334826>)

  • WebEditorClient.cpp: (WebEditorClient::respondToChangedSelection): (WebEditorClient::handleInputMethodKeypress): Prevent the initial keydown for an IME from triggering a keypressed event
  • WebView.cpp: (WebView::WebView): (WebView::keyUp): (WebView::keyDown): (WebViewWndProc): (IMMDict::dict): (IMMDict::IMMDict):

Dynamic loader for IME libraries

(WebView::getIMMContext):
(WebView::releaseIMMContext):
(WebView::prepareCandidateWindow):
(selectionInsideMarkedText):
(setSelectionToEndOfRange):
(WebView::resetIME):
(WebView::updateSelectionForIME):
(WebView::selectionChanged):
(getCompositionString):
(compositionToUnderlines):

Helper functions

(WebView::onIMEStartComposition):
(WebView::onIMEComposition):
(WebView::onIMEEndComposition):
(WebView::onIMEChar):
(WebView::onIMENotify):
(WebView::onIMERequest):
(WebView::onIMESelect):
(WebView::onIMESetContext):

IME event handling, so far most of these are not implemented, but the bulk of functionality
is performed the the composition event handlers

  • WebView.h:
Location:
trunk/WebKit/win
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/WebKit/win/ChangeLog

    r24252 r24285  
     12007-07-13  Oliver Hunt  <oliver@apple.com>
     2
     3        Reviewed by Darin and Alexey.
     4
     5        Fix for <rdar://problem/5231528> Inline input of International text (IME)
     6                http://bugs.webkit.org/show_bug.cgi?id=14331
     7
     8        This patch adds IME support to WebKit/win, it currently does not support
     9        reconversion (<rdar://problem/5334818>) and has issues with the chinese
     10        IMEs (<rdar://problem/5334826>)
     11
     12        * WebEditorClient.cpp:
     13        (WebEditorClient::respondToChangedSelection):
     14        (WebEditorClient::handleInputMethodKeypress):
     15          Prevent the initial keydown for an IME from triggering a keypressed event
     16        * WebView.cpp:
     17        (WebView::WebView):
     18        (WebView::keyUp):
     19        (WebView::keyDown):
     20        (WebViewWndProc):
     21        (IMMDict::dict):
     22        (IMMDict::IMMDict):
     23           Dynamic loader for IME libraries
     24
     25        (WebView::getIMMContext):
     26        (WebView::releaseIMMContext):
     27        (WebView::prepareCandidateWindow):
     28        (selectionInsideMarkedText):
     29        (setSelectionToEndOfRange):
     30        (WebView::resetIME):
     31        (WebView::updateSelectionForIME):
     32        (WebView::selectionChanged):
     33        (getCompositionString):
     34        (compositionToUnderlines):
     35           Helper functions
     36
     37        (WebView::onIMEStartComposition):
     38        (WebView::onIMEComposition):
     39        (WebView::onIMEEndComposition):
     40        (WebView::onIMEChar):
     41        (WebView::onIMENotify):
     42        (WebView::onIMERequest):
     43        (WebView::onIMESelect):
     44        (WebView::onIMESetContext):
     45           IME event handling, so far most of these are not implemented, but the bulk of functionality
     46           is performed the the composition event handlers
     47        * WebView.h:
     48
    1492007-07-12  Alice Liu  <alice.liu@apple.com>
    250
  • trunk/WebKit/win/WebEditorClient.cpp

    r23455 r24285  
    211211void WebEditorClient::respondToChangedSelection()
    212212{
    213     notImplemented();
     213    m_webView->selectionChanged();
    214214}
    215215
     
    598598}
    599599
    600 void WebEditorClient::handleInputMethodKeypress(KeyboardEvent*)
    601 {
     600void WebEditorClient::handleInputMethodKeypress(KeyboardEvent* evt)
     601{
     602    // The key press that triggers an IME triggers a WM_KEYDOWN with VK_PROCESSKEY
     603    // so we intercept and prevent the keypress from occurring
     604    if (evt->keyEvent() && evt->keyEvent()->WindowsKeyCode() == VK_PROCESSKEY)
     605        evt->setDefaultHandled();
    602606}
    603607
  • trunk/WebKit/win/WebView.cpp

    r24166 r24285  
    9191#include <CFNetwork/CFURLProtocolPriv.h>
    9292#include <tchar.h>
     93#include <dimm.h>
    9394#include <windowsx.h>
    9495#include <ShlObj.h>
     
    136137, m_smartInsertDeleteEnabled(false)
    137138, m_didClose(false)
     139, m_inIMEComposition(0)
    138140, m_toolTipHwnd(0)
    139141{
     
    857859    }
    858860
     861    // Don't process keyDown events during IME composition
     862    if (m_inIMEComposition)
     863        return false;
     864
    859865    PlatformKeyboardEvent keyEvent(m_viewWindow, virtualKeyCode, keyData, m_currentCharacterCode);
    860866    Frame* frame = m_page->focusController()->focusedOrMainFrame();
     
    987993        return false;
    988994
     995    // Don't process keyDown events during IME composition
     996    if (m_inIMEComposition)
     997        return false;
     998
    989999    PlatformKeyboardEvent keyEvent(m_viewWindow, virtualKeyCode, keyData, m_currentCharacterCode);
    9901000    Frame* frame = m_page->focusController()->focusedOrMainFrame();
     
    12351245            // and we need to clear out the focused target.
    12361246            FocusController* focusController = webView->page()->focusController();
     1247            webView->resetIME(focusController->focusedOrMainFrame());
    12371248            if (GetAncestor(hWnd, GA_ROOT) != GetFocus()) {
    12381249                if (Frame* frame = focusController->focusedFrame()) {
     
    13061317            break;
    13071318        }
     1319
     1320        case WM_IME_STARTCOMPOSITION:
     1321            handled = webView->onIMEStartComposition();
     1322            break;
     1323        case WM_IME_REQUEST:
     1324            webView->onIMERequest(wParam, lParam, &lResult);
     1325            break;
     1326        case WM_IME_COMPOSITION:
     1327            handled = webView->onIMEComposition(lParam);
     1328            break;
     1329        case WM_IME_ENDCOMPOSITION:
     1330            handled = webView->onIMEEndComposition();
     1331            break;
     1332        case WM_IME_CHAR:
     1333            handled = webView->onIMEChar(wParam, lParam);
     1334            break;
     1335        case WM_IME_NOTIFY:
     1336            handled = webView->onIMENotify(wParam, lParam, &lResult);
     1337            break;
     1338        case WM_IME_SELECT:
     1339            handled = webView->onIMESelect(wParam, lParam);
     1340            break;
     1341        case WM_IME_SETCONTEXT:
     1342            handled = webView->onIMESetContext(wParam, lParam);
     1343            break;
    13081344        case WM_SETCURSOR:
    13091345            if (lastSetCursor) {
     
    35993635}
    36003636
     3637class IMMDict {
     3638    typedef HIMC (CALLBACK *getContextPtr)(HWND);
     3639    typedef BOOL (CALLBACK *releaseContextPtr)(HWND, HIMC);
     3640    typedef LONG (CALLBACK *getCompositionStringPtr)(HIMC, DWORD, LPVOID, DWORD);
     3641    typedef BOOL (CALLBACK *setCandidateWindowPtr)(HIMC, LPCANDIDATEFORM);
     3642    typedef BOOL (CALLBACK *setOpenStatusPtr)(HIMC, BOOL);
     3643    typedef BOOL (CALLBACK *notifyIMEPtr)(HIMC, DWORD, DWORD, DWORD);
     3644
     3645public:
     3646    getContextPtr getContext;
     3647    releaseContextPtr releaseContext;
     3648    getCompositionStringPtr getCompositionString;
     3649    setCandidateWindowPtr setCandidateWindow;
     3650    setOpenStatusPtr setOpenStatus;
     3651    notifyIMEPtr notifyIME;
     3652    static const IMMDict& dict();
     3653private:
     3654    IMMDict();
     3655    HMODULE m_instance;
     3656};
     3657
     3658const IMMDict& IMMDict::dict()
     3659{
     3660    static IMMDict instance;
     3661    return instance;
     3662}
     3663
     3664IMMDict::IMMDict()
     3665{
     3666    m_instance = ::LoadLibrary(TEXT("IMM32.DLL"));
     3667    getContext = reinterpret_cast<getContextPtr>(::GetProcAddress(m_instance, "ImmGetContext"));
     3668    ASSERT(getContext);
     3669    releaseContext = reinterpret_cast<releaseContextPtr>(::GetProcAddress(m_instance, "ImmReleaseContext"));
     3670    ASSERT(releaseContext);
     3671    getCompositionString = reinterpret_cast<getCompositionStringPtr>(::GetProcAddress(m_instance, "ImmGetCompositionStringW"));
     3672    ASSERT(getCompositionString);
     3673    setCandidateWindow = reinterpret_cast<setCandidateWindowPtr>(::GetProcAddress(m_instance, "ImmSetCandidateWindow"));
     3674    ASSERT(setCandidateWindow);
     3675    setOpenStatus = reinterpret_cast<setOpenStatusPtr>(::GetProcAddress(m_instance, "ImmSetOpenStatus"));
     3676    ASSERT(setOpenStatus);
     3677    notifyIME = reinterpret_cast<notifyIMEPtr>(::GetProcAddress(m_instance, "ImmNotifyIME"));
     3678    ASSERT(notifyIME);
     3679}
     3680
     3681HIMC WebView::getIMMContext()
     3682{
     3683    HIMC context = IMMDict::dict().getContext(m_viewWindow);
     3684    ASSERT(context);
     3685    return context;
     3686}
     3687
     3688void WebView::releaseIMMContext(HIMC hIMC)
     3689{
     3690    if (!hIMC)
     3691        return;
     3692    IMMDict::dict().releaseContext(m_viewWindow, hIMC);
     3693}
     3694
     3695void WebView::prepareCandidateWindow(Frame* targetFrame, HIMC hInputContext)
     3696{
     3697    IntRect caret;
     3698    if (RefPtr<Range> range = targetFrame->selectionController()->selection().toRange()) {
     3699        ExceptionCode ec = 0;
     3700        RefPtr<Range> tempRange = range->cloneRange(ec);
     3701        caret = targetFrame->firstRectForRange(tempRange.get());
     3702    }
     3703    caret = targetFrame->view()->contentsToWindow(caret);
     3704    CANDIDATEFORM form;
     3705    form.dwIndex = 0;
     3706    form.dwStyle = CFS_CANDIDATEPOS;
     3707    form.ptCurrentPos.x = caret.x();
     3708    form.ptCurrentPos.y = caret.y() + caret.height();
     3709    form.rcArea.top = 0;
     3710    form.rcArea.bottom = 0;
     3711    form.rcArea.left = 0;
     3712    form.rcArea.right = 0;
     3713    IMMDict::dict().setCandidateWindow(hInputContext, &form);
     3714}
     3715
     3716static bool markedTextContainsSelection(Range* markedTextRange, Range* selection)
     3717{
     3718    ExceptionCode ec = 0;
     3719
     3720    ASSERT(markedTextRange->startContainer(ec) == markedTextRange->endContainer(ec));
     3721
     3722    if (selection->startContainer(ec) != markedTextRange->startContainer(ec))
     3723        return false;
     3724
     3725    if (selection->endContainer(ec) != markedTextRange->endContainer(ec))
     3726        return false;
     3727
     3728    if (selection->startOffset(ec) < markedTextRange->startOffset(ec))
     3729        return false;
     3730
     3731    if (selection->endOffset(ec) > markedTextRange->endOffset(ec))
     3732        return false;
     3733
     3734    return true;
     3735}
     3736
     3737static void setSelectionToEndOfRange(Frame* targetFrame, Range* sourceRange)
     3738{
     3739    ExceptionCode ec = 0;
     3740    Node* caretContainer = sourceRange->endContainer(ec);
     3741    unsigned caretOffset = sourceRange->endOffset(ec);
     3742    RefPtr<Range> range = targetFrame->document()->createRange();
     3743    range->setStart(caretContainer, caretOffset, ec);
     3744    range->setEnd(caretContainer, caretOffset, ec);
     3745    targetFrame->editor()->unmarkText();
     3746    targetFrame->selectionController()->setSelectedRange(range.get(), WebCore::DOWNSTREAM, true, ec);
     3747}
     3748
     3749void WebView::resetIME(Frame* targetFrame)
     3750{
     3751    if (targetFrame)
     3752        targetFrame->editor()->unmarkText();
     3753
     3754    if (HIMC hInputContext = getIMMContext()) {
     3755        IMMDict::dict().notifyIME(hInputContext, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
     3756        releaseIMMContext(hInputContext);
     3757    }
     3758}
     3759
     3760void WebView::updateSelectionForIME()
     3761{
     3762    Frame* targetFrame = m_page->focusController()->focusedOrMainFrame();
     3763    if (!targetFrame || !targetFrame->markedTextRange())
     3764        return;
     3765   
     3766    if (targetFrame->editor()->ignoreMarkedTextSelectionChange())
     3767        return;
     3768
     3769    RefPtr<Range> selectionRange = targetFrame->selectionController()->selection().toRange();
     3770    if (!selectionRange || !markedTextContainsSelection(targetFrame->markedTextRange(), selectionRange.get()))
     3771        resetIME(targetFrame);
     3772}
     3773
     3774void WebView::selectionChanged()
     3775{
     3776    updateSelectionForIME();
     3777}
     3778
     3779bool WebView::onIMEStartComposition()
     3780{
     3781    m_inIMEComposition++;
     3782    Frame* targetFrame = m_page->focusController()->focusedOrMainFrame();
     3783    if (!targetFrame)
     3784        return true;
     3785
     3786    //Tidy up in case the last IME composition was not correctly terminated
     3787    targetFrame->editor()->unmarkText();
     3788
     3789    HIMC hInputContext = getIMMContext();
     3790    prepareCandidateWindow(targetFrame, hInputContext);
     3791    releaseIMMContext(hInputContext);
     3792    return true;
     3793}
     3794
     3795static bool getCompositionString(HIMC hInputContext, DWORD type, String& result)
     3796{
     3797    int compositionLength = IMMDict::dict().getCompositionString(hInputContext, type, 0, 0);
     3798    if (compositionLength <= 0)
     3799        return false;
     3800    Vector<UChar> compositionBuffer(compositionLength / 2);
     3801    compositionLength = IMMDict::dict().getCompositionString(hInputContext, type, (LPVOID)compositionBuffer.data(), compositionLength);
     3802    result = String(compositionBuffer, compositionLength / 2);
     3803    ASSERT(!compositionLength || compositionBuffer[0]);
     3804    ASSERT(!compositionLength || compositionBuffer[compositionLength / 2 - 1]);
     3805    return true;
     3806}
     3807
     3808static void compositionToUnderlines(const Vector<DWORD>& clauses, const Vector<BYTE>& attributes,
     3809                                    unsigned startOffset, Vector<MarkedTextUnderline>& underlines)
     3810{
     3811    if (!clauses.size())
     3812        return;
     3813 
     3814    const size_t numBoundaries = clauses.size() - 1;
     3815    underlines.resize(numBoundaries);
     3816    for (unsigned i = 0; i < numBoundaries; i++) {
     3817        underlines[i].startOffset = startOffset + clauses[i];
     3818        underlines[i].endOffset = startOffset + clauses[i + 1];
     3819        BYTE attribute = attributes[clauses[i]];
     3820        underlines[i].thick = attribute == ATTR_TARGET_CONVERTED || attribute == ATTR_TARGET_NOTCONVERTED;
     3821        underlines[i].color = Color(0,0,0);
     3822    }
     3823}
     3824
     3825bool WebView::onIMEComposition(LPARAM lparam)
     3826{
     3827    HIMC hInputContext = getIMMContext();
     3828    if (!hInputContext)
     3829        return true;
     3830
     3831    Frame* targetFrame = m_page->focusController()->focusedOrMainFrame();
     3832    if (!targetFrame || !targetFrame->editor()->canEdit())
     3833        return true;
     3834
     3835    prepareCandidateWindow(targetFrame, hInputContext);
     3836    targetFrame->editor()->setIgnoreMarkedTextSelectionChange(true);
     3837
     3838    // if we had marked text already, we need to make sure to replace
     3839    // that, instead of the selection/caret
     3840    targetFrame->editor()->selectMarkedText();
     3841
     3842    if (lparam & GCS_RESULTSTR || !lparam) {
     3843        String compositionString;
     3844        if (!getCompositionString(hInputContext, GCS_RESULTSTR, compositionString) && lparam)
     3845            goto cleanup;
     3846       
     3847        targetFrame->editor()->replaceMarkedText(compositionString);
     3848        RefPtr<Range> sourceRange = targetFrame->selectionController()->selection().toRange();
     3849        setSelectionToEndOfRange(targetFrame, sourceRange.get());
     3850    } else if (lparam) {
     3851        String compositionString;
     3852        if (!getCompositionString(hInputContext, GCS_COMPSTR, compositionString))
     3853            goto cleanup;
     3854       
     3855        targetFrame->editor()->replaceMarkedText(compositionString);
     3856
     3857        ExceptionCode ec = 0;
     3858        RefPtr<Range> sourceRange = targetFrame->selectionController()->selection().toRange();
     3859        if (!sourceRange)
     3860            goto cleanup;
     3861
     3862        Node* startContainer = sourceRange->startContainer(ec);
     3863        const String& str = startContainer->textContent();
     3864        for (unsigned i = 0; i < str.length(); i++)
     3865            ASSERT(str[i]);
     3866
     3867        unsigned startOffset = sourceRange->startOffset(ec);
     3868        RefPtr<Range> range = targetFrame->document()->createRange();
     3869        range->setStart(startContainer, startOffset, ec);
     3870        range->setEnd(startContainer, startOffset + compositionString.length(), ec);
     3871
     3872        // Composition string attributes
     3873        int numAttributes = IMMDict::dict().getCompositionString(hInputContext, GCS_COMPATTR, 0, 0);
     3874        Vector<BYTE> attributes(numAttributes);
     3875        IMMDict::dict().getCompositionString(hInputContext, GCS_COMPATTR, attributes.data(), numAttributes);
     3876
     3877        // Get clauses
     3878        int numClauses = IMMDict::dict().getCompositionString(hInputContext, GCS_COMPCLAUSE, 0, 0);
     3879        Vector<DWORD> clauses(numClauses / sizeof(DWORD));
     3880        IMMDict::dict().getCompositionString(hInputContext, GCS_COMPCLAUSE, clauses.data(), numClauses);
     3881        Vector<MarkedTextUnderline> underlines;
     3882        compositionToUnderlines(clauses, attributes, startOffset, underlines);
     3883        targetFrame->setMarkedTextRange(range.get(), underlines);
     3884        if (targetFrame->markedTextRange())
     3885            targetFrame->selectRangeInMarkedText(LOWORD(IMMDict::dict().getCompositionString(hInputContext, GCS_CURSORPOS, 0, 0)), 0);
     3886    }
     3887cleanup:
     3888    targetFrame->editor()->setIgnoreMarkedTextSelectionChange(false);
     3889    return true;
     3890}
     3891
     3892bool WebView::onIMEEndComposition()
     3893{
     3894    if (m_inIMEComposition)
     3895        m_inIMEComposition--;
     3896    Frame* targetFrame = m_page->focusController()->focusedOrMainFrame();
     3897    if (!targetFrame)
     3898        return true;
     3899    targetFrame->editor()->unmarkText();
     3900    return true;
     3901}
     3902
     3903bool WebView::onIMEChar(WPARAM, LPARAM)
     3904{
     3905    return true;
     3906}
     3907
     3908bool WebView::onIMENotify(WPARAM, LPARAM, LRESULT*)
     3909{
     3910    return false;
     3911}
     3912
     3913bool WebView::onIMERequest(WPARAM, LPARAM, LRESULT*)
     3914{
     3915    return false;
     3916}
     3917
     3918bool WebView::onIMESelect(WPARAM, LPARAM)
     3919{
     3920    return false;
     3921}
     3922
     3923bool WebView::onIMESetContext(WPARAM, LPARAM)
     3924{
     3925    return false;
     3926}
     3927
    36013928class EnumTextMatches : public IEnumTextMatches
    36023929{
  • trunk/WebKit/win/WebView.h

    r24022 r24285  
    636636    void setProhibitsMainFrameScrolling(bool = true);
    637637
     638    bool onIMEStartComposition();
     639    bool onIMEComposition(LPARAM);
     640    bool onIMEEndComposition();
     641    bool onIMEChar(WPARAM, LPARAM);
     642    bool onIMENotify(WPARAM, LPARAM, LRESULT*);
     643    bool onIMERequest(WPARAM, LPARAM, LRESULT*);
     644    bool onIMESelect(WPARAM, LPARAM);
     645    bool onIMESetContext(WPARAM, LPARAM);
     646    void selectionChanged();
     647    void resetIME(WebCore::Frame*);
     648
    638649    HRESULT registerDragDrop();
    639650    HRESULT revokeDragDrop();
     
    656667
    657668protected:
    658     static bool allowSiteSpecificHacks() { return s_allowSiteSpecificHacks; }
     669    HIMC getIMMContext();
     670    void releaseIMMContext(HIMC);
     671    static bool allowSiteSpecificHacks() { return s_allowSiteSpecificHacks; }
    659672    void preflightSpellChecker();
    660673    bool continuousCheckingAllowed();
    661674    void initializeCacheSizesIfNecessary();
    662675    void initializeToolTipWindow();
    663 
     676    void prepareCandidateWindow(WebCore::Frame*, HIMC);
     677    void updateSelectionForIME();
    664678    ULONG m_refCount;
    665679    WebCore::String m_groupName;
     
    701715    bool m_smartInsertDeleteEnabled;
    702716    bool m_didClose;
    703 
     717    unsigned m_inIMEComposition;
    704718    HWND m_toolTipHwnd;
    705719    WebCore::String m_toolTip;
Note: See TracChangeset for help on using the changeset viewer.