LCOV - code coverage report
Current view: directory - widget/gtk2 - nsGtkIMModule.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 654 0 0.0 %
Date: 2012-06-02 Functions: 45 0 0.0 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2                 : /* vim:expandtab:shiftwidth=4:tabstop=4:
       3                 :  */
       4                 : /* ***** BEGIN LICENSE BLOCK *****
       5                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       6                 :  *
       7                 :  * The contents of this file are subject to the Mozilla Public License Version
       8                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       9                 :  * the License. You may obtain a copy of the License at
      10                 :  * http://www.mozilla.org/MPL/
      11                 :  *
      12                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      13                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      14                 :  * for the specific language governing rights and limitations under the
      15                 :  * License.
      16                 :  *
      17                 :  * The Original Code is mozilla.org code.
      18                 :  *
      19                 :  * The Initial Developer of the Original Code is Christopher Blizzard
      20                 :  * <blizzard@mozilla.org>.  Portions created by the Initial Developer
      21                 :  * are Copyright (C) 2001 the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   Mats Palmgren <mats.palmgren@bredband.net>
      25                 :  *   Masayuki Nakano <masayuki@d-toybox.com>
      26                 :  *
      27                 :  * Alternatively, the contents of this file may be used under the terms of
      28                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      29                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      30                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      31                 :  * of those above. If you wish to allow use of your version of this file only
      32                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      33                 :  * use your version of this file under the terms of the MPL, indicate your
      34                 :  * decision by deleting the provisions above and replace them with the notice
      35                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      36                 :  * the provisions above, a recipient may use your version of this file under
      37                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      38                 :  *
      39                 :  * ***** END LICENSE BLOCK ***** */
      40                 : 
      41                 : #ifdef MOZ_PLATFORM_MAEMO
      42                 : #define MAEMO_CHANGES
      43                 : #endif
      44                 : 
      45                 : #ifdef MOZ_LOGGING
      46                 : #define FORCE_PR_LOG /* Allow logging in the release build */
      47                 : #endif // MOZ_LOGGING
      48                 : #include "prlog.h"
      49                 : 
      50                 : #include "nsGtkIMModule.h"
      51                 : #include "nsWindow.h"
      52                 : #include "mozilla/Preferences.h"
      53                 : 
      54                 : #ifdef MOZ_PLATFORM_MAEMO
      55                 : #include "nsServiceManagerUtils.h"
      56                 : #include "nsIObserverService.h"
      57                 : #include "mozilla/Services.h"
      58                 : #endif
      59                 : 
      60                 : using namespace mozilla;
      61                 : using namespace mozilla::widget;
      62                 : 
      63                 : #ifdef PR_LOGGING
      64                 : PRLogModuleInfo* gGtkIMLog = nsnull;
      65                 : 
      66                 : static const char*
      67               0 : GetRangeTypeName(PRUint32 aRangeType)
      68                 : {
      69               0 :     switch (aRangeType) {
      70                 :         case NS_TEXTRANGE_RAWINPUT:
      71               0 :             return "NS_TEXTRANGE_RAWINPUT";
      72                 :         case NS_TEXTRANGE_CONVERTEDTEXT:
      73               0 :             return "NS_TEXTRANGE_CONVERTEDTEXT";
      74                 :         case NS_TEXTRANGE_SELECTEDRAWTEXT:
      75               0 :             return "NS_TEXTRANGE_SELECTEDRAWTEXT";
      76                 :         case NS_TEXTRANGE_SELECTEDCONVERTEDTEXT:
      77               0 :             return "NS_TEXTRANGE_SELECTEDCONVERTEDTEXT";
      78                 :         case NS_TEXTRANGE_CARETPOSITION:
      79               0 :             return "NS_TEXTRANGE_CARETPOSITION";
      80                 :         default:
      81               0 :             return "UNKNOWN SELECTION TYPE!!";
      82                 :     }
      83                 : }
      84                 : 
      85                 : static const char*
      86               0 : GetEnabledStateName(PRUint32 aState)
      87                 : {
      88               0 :     switch (aState) {
      89                 :         case IMEState::DISABLED:
      90               0 :             return "DISABLED";
      91                 :         case IMEState::ENABLED:
      92               0 :             return "ENABLED";
      93                 :         case IMEState::PASSWORD:
      94               0 :             return "PASSWORD";
      95                 :         case IMEState::PLUGIN:
      96               0 :             return "PLUG_IN";
      97                 :         default:
      98               0 :             return "UNKNOWN ENABLED STATUS!!";
      99                 :     }
     100                 : }
     101                 : #endif
     102                 : 
     103                 : nsGtkIMModule* nsGtkIMModule::sLastFocusedModule = nsnull;
     104                 : 
     105                 : #ifdef MOZ_PLATFORM_MAEMO
     106                 : static bool gIsVirtualKeyboardOpened = false;
     107                 : #endif
     108                 : 
     109               0 : nsGtkIMModule::nsGtkIMModule(nsWindow* aOwnerWindow) :
     110                 :     mOwnerWindow(aOwnerWindow), mLastFocusedWindow(nsnull),
     111                 :     mContext(nsnull),
     112                 : #ifndef NS_IME_ENABLED_ON_PASSWORD_FIELD
     113                 :     mSimpleContext(nsnull),
     114                 : #endif
     115                 :     mDummyContext(nsnull),
     116                 :     mCompositionStart(PR_UINT32_MAX), mProcessingKeyEvent(nsnull),
     117                 :     mCompositionState(eCompositionState_NotComposing),
     118               0 :     mIsIMFocused(false), mIgnoreNativeCompositionEvent(false)
     119                 : {
     120                 : #ifdef PR_LOGGING
     121               0 :     if (!gGtkIMLog) {
     122               0 :         gGtkIMLog = PR_NewLogModule("nsGtkIMModuleWidgets");
     123                 :     }
     124                 : #endif
     125               0 :     Init();
     126               0 : }
     127                 : 
     128                 : void
     129               0 : nsGtkIMModule::Init()
     130                 : {
     131               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     132                 :         ("GtkIMModule(%p): Init, mOwnerWindow=%p",
     133                 :          this, mOwnerWindow));
     134                 : 
     135               0 :     MozContainer* container = mOwnerWindow->GetMozContainer();
     136               0 :     NS_PRECONDITION(container, "container is null");
     137               0 :     GdkWindow* gdkWindow = GTK_WIDGET(container)->window;
     138                 : 
     139                 :     // NOTE: gtk_im_*_new() abort (kill) the whole process when it fails.
     140                 :     //       So, we don't need to check the result.
     141                 : 
     142                 :     // Normal context.
     143               0 :     mContext = gtk_im_multicontext_new();
     144               0 :     gtk_im_context_set_client_window(mContext, gdkWindow);
     145               0 :     g_signal_connect(mContext, "preedit_changed",
     146                 :                      G_CALLBACK(nsGtkIMModule::OnChangeCompositionCallback),
     147               0 :                      this);
     148               0 :     g_signal_connect(mContext, "retrieve_surrounding",
     149                 :                      G_CALLBACK(nsGtkIMModule::OnRetrieveSurroundingCallback),
     150               0 :                      this);
     151               0 :     g_signal_connect(mContext, "delete_surrounding",
     152                 :                      G_CALLBACK(nsGtkIMModule::OnDeleteSurroundingCallback),
     153               0 :                      this);
     154               0 :     g_signal_connect(mContext, "commit",
     155                 :                      G_CALLBACK(nsGtkIMModule::OnCommitCompositionCallback),
     156               0 :                      this);
     157               0 :     g_signal_connect(mContext, "preedit_start",
     158                 :                      G_CALLBACK(nsGtkIMModule::OnStartCompositionCallback),
     159               0 :                      this);
     160               0 :     g_signal_connect(mContext, "preedit_end",
     161                 :                      G_CALLBACK(nsGtkIMModule::OnEndCompositionCallback),
     162               0 :                      this);
     163                 : 
     164                 : #ifndef NS_IME_ENABLED_ON_PASSWORD_FIELD
     165                 :     // Simple context
     166               0 :     mSimpleContext = gtk_im_context_simple_new();
     167               0 :     gtk_im_context_set_client_window(mSimpleContext, gdkWindow);
     168               0 :     g_signal_connect(mSimpleContext, "preedit_changed",
     169                 :                      G_CALLBACK(&nsGtkIMModule::OnChangeCompositionCallback),
     170               0 :                      this);
     171               0 :     g_signal_connect(mSimpleContext, "retrieve_surrounding",
     172                 :                      G_CALLBACK(&nsGtkIMModule::OnRetrieveSurroundingCallback),
     173               0 :                      this);
     174               0 :     g_signal_connect(mSimpleContext, "delete_surrounding",
     175                 :                      G_CALLBACK(&nsGtkIMModule::OnDeleteSurroundingCallback),
     176               0 :                      this);
     177               0 :     g_signal_connect(mSimpleContext, "commit",
     178                 :                      G_CALLBACK(&nsGtkIMModule::OnCommitCompositionCallback),
     179               0 :                      this);
     180               0 :     g_signal_connect(mSimpleContext, "preedit_start",
     181                 :                      G_CALLBACK(nsGtkIMModule::OnStartCompositionCallback),
     182               0 :                      this);
     183               0 :     g_signal_connect(mSimpleContext, "preedit_end",
     184                 :                      G_CALLBACK(nsGtkIMModule::OnEndCompositionCallback),
     185               0 :                      this);
     186                 : #endif // NS_IME_ENABLED_ON_PASSWORD_FIELD
     187                 : 
     188                 :     // Dummy context
     189               0 :     mDummyContext = gtk_im_multicontext_new();
     190               0 :     gtk_im_context_set_client_window(mDummyContext, gdkWindow);
     191               0 : }
     192                 : 
     193               0 : nsGtkIMModule::~nsGtkIMModule()
     194                 : {
     195               0 :     if (this == sLastFocusedModule) {
     196               0 :         sLastFocusedModule = nsnull;
     197                 :     }
     198               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     199                 :         ("GtkIMModule(%p) was gone", this));
     200               0 : }
     201                 : 
     202                 : void
     203               0 : nsGtkIMModule::OnDestroyWindow(nsWindow* aWindow)
     204                 : {
     205               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     206                 :         ("GtkIMModule(%p): OnDestroyWindow, aWindow=%p, mLastFocusedWindow=%p, mOwnerWindow=%p, mLastFocusedModule=%p",
     207                 :          this, aWindow, mLastFocusedWindow, mOwnerWindow, sLastFocusedModule));
     208                 : 
     209               0 :     NS_PRECONDITION(aWindow, "aWindow must not be null");
     210                 : 
     211               0 :     if (mLastFocusedWindow == aWindow) {
     212               0 :         CancelIMEComposition(aWindow);
     213               0 :         if (mIsIMFocused) {
     214               0 :             Blur();
     215                 :         }
     216               0 :         mLastFocusedWindow = nsnull;
     217                 :     }
     218                 : 
     219               0 :     if (mOwnerWindow != aWindow) {
     220               0 :         return;
     221                 :     }
     222                 : 
     223               0 :     if (sLastFocusedModule == this) {
     224               0 :         sLastFocusedModule = nsnull;
     225                 :     }
     226                 : 
     227                 :     /**
     228                 :      * NOTE:
     229                 :      *   The given window is the owner of this, so, we must release the
     230                 :      *   contexts now.  But that might be referred from other nsWindows
     231                 :      *   (they are children of this.  But we don't know why there are the
     232                 :      *   cases).  So, we need to clear the pointers that refers to contexts
     233                 :      *   and this if the other referrers are still alive. See bug 349727.
     234                 :      */
     235               0 :     if (mContext) {
     236               0 :         PrepareToDestroyContext(mContext);
     237               0 :         gtk_im_context_set_client_window(mContext, nsnull);
     238               0 :         g_object_unref(mContext);
     239               0 :         mContext = nsnull;
     240                 :     }
     241                 : 
     242                 : #ifndef NS_IME_ENABLED_ON_PASSWORD_FIELD
     243               0 :     if (mSimpleContext) {
     244               0 :         gtk_im_context_set_client_window(mSimpleContext, nsnull);
     245               0 :         g_object_unref(mSimpleContext);
     246               0 :         mSimpleContext = nsnull;
     247                 :     }
     248                 : #endif // NS_IME_ENABLED_ON_PASSWORD_FIELD
     249                 : 
     250               0 :     if (mDummyContext) {
     251                 :         // mContext and mDummyContext have the same slaveType and signal_data
     252                 :         // so no need for another workaround_gtk_im_display_closed.
     253               0 :         gtk_im_context_set_client_window(mDummyContext, nsnull);
     254               0 :         g_object_unref(mDummyContext);
     255               0 :         mDummyContext = nsnull;
     256                 :     }
     257                 : 
     258               0 :     mOwnerWindow = nsnull;
     259               0 :     mLastFocusedWindow = nsnull;
     260               0 :     mInputContext.mIMEState.mEnabled = IMEState::DISABLED;
     261                 : 
     262               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     263                 :         ("    SUCCEEDED, Completely destroyed"));
     264                 : }
     265                 : 
     266                 : // Work around gtk bug http://bugzilla.gnome.org/show_bug.cgi?id=483223:
     267                 : // (and the similar issue of GTK+ IIIM)
     268                 : // The GTK+ XIM and IIIM modules register handlers for the "closed" signal
     269                 : // on the display, but:
     270                 : //  * The signal handlers are not disconnected when the module is unloaded.
     271                 : //
     272                 : // The GTK+ XIM module has another problem:
     273                 : //  * When the signal handler is run (with the module loaded) it tries
     274                 : //    XFree (and fails) on a pointer that did not come from Xmalloc.
     275                 : //
     276                 : // To prevent these modules from being unloaded, use static variables to
     277                 : // hold ref of GtkIMContext class.
     278                 : // For GTK+ XIM module, to prevent the signal handler from being run,
     279                 : // find the signal handlers and remove them.
     280                 : //
     281                 : // GtkIMContextXIMs share XOpenIM connections and display closed signal
     282                 : // handlers (where possible).
     283                 : 
     284                 : void
     285               0 : nsGtkIMModule::PrepareToDestroyContext(GtkIMContext *aContext)
     286                 : {
     287               0 :     MozContainer* container = mOwnerWindow->GetMozContainer();
     288               0 :     NS_PRECONDITION(container, "The container of the window is null");
     289                 : 
     290               0 :     GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT(aContext);
     291               0 :     GtkIMContext *slave = multicontext->slave;
     292               0 :     if (!slave) {
     293               0 :         return;
     294                 :     }
     295                 : 
     296               0 :     GType slaveType = G_TYPE_FROM_INSTANCE(slave);
     297               0 :     const gchar *im_type_name = g_type_name(slaveType);
     298               0 :     if (strcmp(im_type_name, "GtkIMContextXIM") == 0) {
     299               0 :         if (gtk_check_version(2, 12, 1) == NULL) {
     300               0 :             return; // gtk bug has been fixed
     301                 :         }
     302                 : 
     303                 :         struct GtkIMContextXIM
     304                 :         {
     305                 :             GtkIMContext parent;
     306                 :             gpointer private_data;
     307                 :             // ... other fields
     308                 :         };
     309                 : 
     310                 :         gpointer signal_data =
     311               0 :             reinterpret_cast<GtkIMContextXIM*>(slave)->private_data;
     312               0 :         if (!signal_data) {
     313               0 :             return;
     314                 :         }
     315                 : 
     316                 :         g_signal_handlers_disconnect_matched(
     317               0 :             gtk_widget_get_display(GTK_WIDGET(container)),
     318               0 :             G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, signal_data);
     319                 : 
     320                 :         // Add a reference to prevent the XIM module from being unloaded
     321                 :         // and reloaded: each time the module is loaded and used, it
     322                 :         // opens (and doesn't close) new XOpenIM connections.
     323                 :         static gpointer gtk_xim_context_class =
     324               0 :             g_type_class_ref(slaveType);
     325                 :         // Mute unused variable warning:
     326               0 :         gtk_xim_context_class = gtk_xim_context_class;
     327               0 :     } else if (strcmp(im_type_name, "GtkIMContextIIIM") == 0) {
     328                 :         // Add a reference to prevent the IIIM module from being unloaded
     329                 :         static gpointer gtk_iiim_context_class =
     330               0 :             g_type_class_ref(slaveType);
     331                 :         // Mute unused variable warning:
     332               0 :         gtk_iiim_context_class = gtk_iiim_context_class;
     333                 :     }
     334                 : }
     335                 : 
     336                 : void
     337               0 : nsGtkIMModule::OnFocusWindow(nsWindow* aWindow)
     338                 : {
     339               0 :     if (NS_UNLIKELY(IsDestroyed())) {
     340               0 :         return;
     341                 :     }
     342                 : 
     343               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     344                 :         ("GtkIMModule(%p): OnFocusWindow, aWindow=%p, mLastFocusedWindow=%p",
     345                 :          this, aWindow, mLastFocusedWindow));
     346               0 :     mLastFocusedWindow = aWindow;
     347               0 :     Focus();
     348                 : }
     349                 : 
     350                 : void
     351               0 : nsGtkIMModule::OnBlurWindow(nsWindow* aWindow)
     352                 : {
     353               0 :     if (NS_UNLIKELY(IsDestroyed())) {
     354               0 :         return;
     355                 :     }
     356                 : 
     357               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     358                 :         ("GtkIMModule(%p): OnBlurWindow, aWindow=%p, mLastFocusedWindow=%p, mIsIMFocused=%s",
     359                 :          this, aWindow, mLastFocusedWindow, mIsIMFocused ? "YES" : "NO"));
     360                 : 
     361               0 :     if (!mIsIMFocused || mLastFocusedWindow != aWindow) {
     362               0 :         return;
     363                 :     }
     364                 : 
     365               0 :     Blur();
     366                 : }
     367                 : 
     368                 : bool
     369               0 : nsGtkIMModule::OnKeyEvent(nsWindow* aCaller, GdkEventKey* aEvent,
     370                 :                           bool aKeyDownEventWasSent /* = false */)
     371                 : {
     372               0 :     NS_PRECONDITION(aEvent, "aEvent must be non-null");
     373                 : 
     374               0 :     if (!IsEditable() || NS_UNLIKELY(IsDestroyed())) {
     375               0 :         return false;
     376                 :     }
     377                 : 
     378               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     379                 :         ("GtkIMModule(%p): OnKeyEvent, aCaller=%p, aKeyDownEventWasSent=%s",
     380                 :          this, aCaller, aKeyDownEventWasSent ? "TRUE" : "FALSE"));
     381               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     382                 :         ("    aEvent: type=%s, keyval=%s, unicode=0x%X",
     383                 :          aEvent->type == GDK_KEY_PRESS ? "GDK_KEY_PRESS" :
     384                 :          aEvent->type == GDK_KEY_RELEASE ? "GDK_KEY_RELEASE" : "Unknown",
     385                 :          gdk_keyval_name(aEvent->keyval),
     386                 :          gdk_keyval_to_unicode(aEvent->keyval)));
     387                 : 
     388               0 :     if (aCaller != mLastFocusedWindow) {
     389               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     390                 :             ("    FAILED, the caller isn't focused window, mLastFocusedWindow=%p",
     391                 :              mLastFocusedWindow));
     392               0 :         return false;
     393                 :     }
     394                 : 
     395               0 :     GtkIMContext* im = GetContext();
     396               0 :     if (NS_UNLIKELY(!im)) {
     397               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     398                 :             ("    FAILED, there are no context"));
     399               0 :         return false;
     400                 :     }
     401                 : 
     402               0 :     mKeyDownEventWasSent = aKeyDownEventWasSent;
     403               0 :     mFilterKeyEvent = true;
     404               0 :     mProcessingKeyEvent = aEvent;
     405               0 :     gboolean isFiltered = gtk_im_context_filter_keypress(im, aEvent);
     406               0 :     mProcessingKeyEvent = nsnull;
     407                 : 
     408                 :     // We filter the key event if the event was not committed (because
     409                 :     // it's probably part of a composition) or if the key event was
     410                 :     // committed _and_ changed.  This way we still let key press
     411                 :     // events go through as simple key press events instead of
     412                 :     // composed characters.
     413               0 :     bool filterThisEvent = isFiltered && mFilterKeyEvent;
     414                 : 
     415               0 :     if (IsComposing() && !isFiltered) {
     416               0 :         if (aEvent->type == GDK_KEY_PRESS) {
     417               0 :             if (!mDispatchedCompositionString.IsEmpty()) {
     418                 :                 // If there is composition string, we shouldn't dispatch
     419                 :                 // any keydown events during composition.
     420               0 :                 filterThisEvent = true;
     421                 :             } else {
     422                 :                 // A Hangul input engine for SCIM doesn't emit preedit_end
     423                 :                 // signal even when composition string becomes empty.  On the
     424                 :                 // other hand, we should allow to make composition with empty
     425                 :                 // string for other languages because there *might* be such
     426                 :                 // IM.  For compromising this issue, we should dispatch
     427                 :                 // compositionend event, however, we don't need to reset IM
     428                 :                 // actually.
     429               0 :                 CommitCompositionBy(EmptyString());
     430               0 :                 filterThisEvent = false;
     431                 :             }
     432                 :         } else {
     433                 :             // Key release event may not be consumed by IM, however, we
     434                 :             // shouldn't dispatch any keyup event during composition.
     435               0 :             filterThisEvent = true;
     436                 :         }
     437                 :     }
     438                 : 
     439               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     440                 :         ("    filterThisEvent=%s (isFiltered=%s, mFilterKeyEvent=%s)",
     441                 :          filterThisEvent ? "TRUE" : "FALSE", isFiltered ? "YES" : "NO",
     442                 :          mFilterKeyEvent ? "YES" : "NO"));
     443                 : 
     444               0 :     return filterThisEvent;
     445                 : }
     446                 : 
     447                 : void
     448               0 : nsGtkIMModule::OnFocusChangeInGecko(bool aFocus)
     449                 : {
     450               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     451                 :         ("GtkIMModule(%p): OnFocusChangeInGecko, aFocus=%s, "
     452                 :          "mCompositionState=%s, mIsIMFocused=%s, "
     453                 :          "mIgnoreNativeCompositionEvent=%s",
     454                 :          this, aFocus ? "YES" : "NO", GetCompositionStateName(),
     455                 :          mIsIMFocused ? "YES" : "NO",
     456                 :          mIgnoreNativeCompositionEvent ? "YES" : "NO"));
     457                 : 
     458                 :     // We shouldn't carry over the removed string to another editor.
     459               0 :     mSelectedString.Truncate();
     460                 : 
     461               0 :     if (aFocus) {
     462                 :         // If we failed to commit forcedely in previous focused editor,
     463                 :         // we should reopen the gate for native signals in new focused editor.
     464               0 :         mIgnoreNativeCompositionEvent = false;
     465                 :     }
     466               0 : }
     467                 : 
     468                 : void
     469               0 : nsGtkIMModule::ResetIME()
     470                 : {
     471               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     472                 :         ("GtkIMModule(%p): ResetIME, mCompositionState=%s, mIsIMFocused=%s",
     473                 :          this, GetCompositionStateName(), mIsIMFocused ? "YES" : "NO"));
     474                 : 
     475               0 :     GtkIMContext *im = GetContext();
     476               0 :     if (NS_UNLIKELY(!im)) {
     477               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     478                 :             ("    FAILED, there are no context"));
     479               0 :         return;
     480                 :     }
     481                 : 
     482               0 :     mIgnoreNativeCompositionEvent = true;
     483               0 :     gtk_im_context_reset(im);
     484                 : }
     485                 : 
     486                 : nsresult
     487               0 : nsGtkIMModule::ResetInputState(nsWindow* aCaller)
     488                 : {
     489               0 :     if (NS_UNLIKELY(IsDestroyed())) {
     490               0 :         return NS_OK;
     491                 :     }
     492                 : 
     493               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     494                 :         ("GtkIMModule(%p): ResetInputState, aCaller=%p, mCompositionState=%s",
     495                 :          this, aCaller, GetCompositionStateName()));
     496                 : 
     497               0 :     if (aCaller != mLastFocusedWindow) {
     498               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     499                 :             ("    WARNING: the caller isn't focused window, mLastFocusedWindow=%p",
     500                 :              mLastFocusedWindow));
     501               0 :         return NS_OK;
     502                 :     }
     503                 : 
     504               0 :     if (!IsComposing()) {
     505               0 :         return NS_OK;
     506                 :     }
     507                 : 
     508                 :     // XXX We should commit composition ourselves temporary...
     509               0 :     ResetIME();
     510               0 :     CommitCompositionBy(mDispatchedCompositionString);
     511                 : 
     512               0 :     return NS_OK;
     513                 : }
     514                 : 
     515                 : nsresult
     516               0 : nsGtkIMModule::CancelIMEComposition(nsWindow* aCaller)
     517                 : {
     518               0 :     if (NS_UNLIKELY(IsDestroyed())) {
     519               0 :         return NS_OK;
     520                 :     }
     521                 : 
     522               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     523                 :         ("GtkIMModule(%p): CancelIMEComposition, aCaller=%p",
     524                 :          this, aCaller));
     525                 : 
     526               0 :     if (aCaller != mLastFocusedWindow) {
     527               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     528                 :             ("    FAILED, the caller isn't focused window, mLastFocusedWindow=%p",
     529                 :              mLastFocusedWindow));
     530               0 :         return NS_OK;
     531                 :     }
     532                 : 
     533               0 :     if (!IsComposing()) {
     534               0 :         return NS_OK;
     535                 :     }
     536                 : 
     537               0 :     GtkIMContext *im = GetContext();
     538               0 :     if (NS_UNLIKELY(!im)) {
     539               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     540                 :             ("    FAILED, there are no context"));
     541               0 :         return NS_OK;
     542                 :     }
     543                 : 
     544               0 :     ResetIME();
     545               0 :     CommitCompositionBy(EmptyString());
     546                 : 
     547               0 :     return NS_OK;
     548                 : }
     549                 : 
     550                 : void
     551               0 : nsGtkIMModule::SetInputContext(nsWindow* aCaller,
     552                 :                                const InputContext* aContext,
     553                 :                                const InputContextAction* aAction)
     554                 : {
     555               0 :     if (NS_UNLIKELY(IsDestroyed())) {
     556               0 :         return;
     557                 :     }
     558                 : 
     559               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     560                 :         ("GtkIMModule(%p): SetInputContext, aCaller=%p, aState=%s mHTMLInputType=%s",
     561                 :          this, aCaller, GetEnabledStateName(aContext->mIMEState.mEnabled),
     562                 :          NS_ConvertUTF16toUTF8(aContext->mHTMLInputType).get()));
     563                 : 
     564               0 :     if (aCaller != mLastFocusedWindow) {
     565               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     566                 :             ("    FAILED, the caller isn't focused window, mLastFocusedWindow=%p",
     567                 :              mLastFocusedWindow));
     568               0 :         return;
     569                 :     }
     570                 : 
     571               0 :     if (!mContext) {
     572               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     573                 :             ("    FAILED, there are no context"));
     574               0 :         return;
     575                 :     }
     576                 : 
     577                 : 
     578               0 :     if (sLastFocusedModule != this) {
     579               0 :         mInputContext = *aContext;
     580               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     581                 :             ("    SUCCEEDED, but we're not active"));
     582               0 :         return;
     583                 :     }
     584                 : 
     585                 :     bool changingEnabledState =
     586               0 :         aContext->mIMEState.mEnabled != mInputContext.mIMEState.mEnabled;
     587                 : 
     588                 :     // Release current IME focus if IME is enabled.
     589               0 :     if (changingEnabledState && IsEditable()) {
     590               0 :         ResetInputState(mLastFocusedWindow);
     591               0 :         Blur();
     592                 :     }
     593                 : 
     594               0 :     mInputContext = *aContext;
     595                 : 
     596                 :     // Even when aState is not enabled state, we need to set IME focus.
     597                 :     // Because some IMs are updating the status bar of them at this time.
     598                 :     // Be aware, don't use aWindow here because this method shouldn't move
     599                 :     // focus actually.
     600               0 :     if (changingEnabledState) {
     601               0 :         Focus();
     602                 :     }
     603                 : 
     604                 : #if (MOZ_PLATFORM_MAEMO == 5)
     605                 :     GtkIMContext *im = GetContext();
     606                 :     if (im) {
     607                 :         if (IsEnabled()) {
     608                 :             // Ensure that opening the virtual keyboard is allowed for this specific
     609                 :             // InputContext depending on the content.ime.strict.policy pref
     610                 :             if (mInputContext.mIMEState.mEnabled != IMEState::DISABLED && 
     611                 :                 mInputContext.mIMEState.mEnabled != IMEState::PLUGIN &&
     612                 :                 Preferences::GetBool("content.ime.strict_policy", false) &&
     613                 :                 !aAction->ContentGotFocusByTrustedCause() &&
     614                 :                 !aAction->UserMightRequestOpenVKB()) {
     615                 :                 return;
     616                 :             }
     617                 : 
     618                 :             // It is not desired that the hildon's autocomplete mechanism displays
     619                 :             // user previous entered passwds, so lets make completions invisible
     620                 :             // in these cases.
     621                 :             int mode;
     622                 :             g_object_get(im, "hildon-input-mode", &mode, NULL);
     623                 : 
     624                 :             if (mInputContext.mIMEState.mEnabled == IMEState::ENABLED ||
     625                 :                 mInputContext.mIMEState.mEnabled == IMEState::PLUGIN) {
     626                 :                 mode &= ~HILDON_GTK_INPUT_MODE_INVISIBLE;
     627                 :             } else if (mInputContext.mIMEState.mEnabled == IMEState::PASSWORD) {
     628                 :                mode |= HILDON_GTK_INPUT_MODE_INVISIBLE;
     629                 :             }
     630                 : 
     631                 :             // Turn off auto-capitalization for editboxes
     632                 :             mode &= ~HILDON_GTK_INPUT_MODE_AUTOCAP;
     633                 : 
     634                 :             // Turn off predictive dictionaries for editboxes
     635                 :             mode &= ~HILDON_GTK_INPUT_MODE_DICTIONARY;
     636                 : 
     637                 :             g_object_set(im, "hildon-input-mode",
     638                 :                          (HildonGtkInputMode)mode, NULL);
     639                 :             gIsVirtualKeyboardOpened = true;
     640                 :             hildon_gtk_im_context_show(im);
     641                 :         } else {
     642                 :             gIsVirtualKeyboardOpened = false;
     643                 :             hildon_gtk_im_context_hide(im);
     644                 :         }
     645                 :     }
     646                 : 
     647                 :     nsCOMPtr<nsIObserverService> observerService =
     648                 :         mozilla::services::GetObserverService();
     649                 :     if (observerService) {
     650                 :         nsAutoString rectBuf;
     651                 :         PRInt32 x, y, w, h;
     652                 :         gdk_window_get_position(aCaller->GetGdkWindow(), &x, &y);
     653                 :         gdk_window_get_size(aCaller->GetGdkWindow(), &w, &h);
     654                 :         rectBuf.Assign(NS_LITERAL_STRING("{\"left\": "));
     655                 :         rectBuf.AppendInt(x);
     656                 :         rectBuf.Append(NS_LITERAL_STRING(", \"top\": "));
     657                 :         rectBuf.AppendInt(y);
     658                 :         rectBuf.Append(NS_LITERAL_STRING(", \"right\": "));
     659                 :         rectBuf.AppendInt(w);
     660                 :         rectBuf.Append(NS_LITERAL_STRING(", \"bottom\": "));
     661                 :         rectBuf.AppendInt(h);
     662                 :         rectBuf.Append(NS_LITERAL_STRING("}"));
     663                 :         observerService->NotifyObservers(nsnull, "softkb-change",
     664                 :                                          rectBuf.get());
     665                 :     }
     666                 : #endif
     667                 : }
     668                 : 
     669                 : InputContext
     670               0 : nsGtkIMModule::GetInputContext()
     671                 : {
     672               0 :     mInputContext.mIMEState.mOpen = IMEState::OPEN_STATE_NOT_SUPPORTED;
     673               0 :     return mInputContext;
     674                 : }
     675                 : 
     676                 : /* static */
     677                 : bool
     678               0 : nsGtkIMModule::IsVirtualKeyboardOpened()
     679                 : {
     680                 : #ifdef MOZ_PLATFORM_MAEMO
     681                 :     return gIsVirtualKeyboardOpened;
     682                 : #else
     683               0 :     return false;
     684                 : #endif
     685                 : }
     686                 : 
     687                 : GtkIMContext*
     688               0 : nsGtkIMModule::GetContext()
     689                 : {
     690               0 :     if (IsEnabled()) {
     691               0 :         return mContext;
     692                 :     }
     693                 : 
     694                 : #ifndef NS_IME_ENABLED_ON_PASSWORD_FIELD
     695               0 :     if (mInputContext.mIMEState.mEnabled == IMEState::PASSWORD) {
     696               0 :         return mSimpleContext;
     697                 :     }
     698                 : #endif // NS_IME_ENABLED_ON_PASSWORD_FIELD
     699                 : 
     700               0 :     return mDummyContext;
     701                 : }
     702                 : 
     703                 : bool
     704               0 : nsGtkIMModule::IsEnabled()
     705                 : {
     706                 :     return mInputContext.mIMEState.mEnabled == IMEState::ENABLED ||
     707                 : #ifdef NS_IME_ENABLED_ON_PASSWORD_FIELD
     708                 :            mInputContext.mIMEState.mEnabled == IMEState::PASSWORD ||
     709                 : #endif // NS_IME_ENABLED_ON_PASSWORD_FIELD
     710               0 :            mInputContext.mIMEState.mEnabled == IMEState::PLUGIN;
     711                 : }
     712                 : 
     713                 : bool
     714               0 : nsGtkIMModule::IsEditable()
     715                 : {
     716                 :     return mInputContext.mIMEState.mEnabled == IMEState::ENABLED ||
     717                 :            mInputContext.mIMEState.mEnabled == IMEState::PLUGIN ||
     718               0 :            mInputContext.mIMEState.mEnabled == IMEState::PASSWORD;
     719                 : }
     720                 : 
     721                 : void
     722               0 : nsGtkIMModule::Focus()
     723                 : {
     724               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     725                 :         ("GtkIMModule(%p): Focus, sLastFocusedModule=%p",
     726                 :          this, sLastFocusedModule));
     727                 : 
     728               0 :     if (mIsIMFocused) {
     729               0 :         NS_ASSERTION(sLastFocusedModule == this,
     730                 :                      "We're not active, but the IM was focused?");
     731               0 :         return;
     732                 :     }
     733                 : 
     734               0 :     GtkIMContext *im = GetContext();
     735               0 :     if (!im) {
     736               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     737                 :             ("    FAILED, there are no context"));
     738               0 :         return;
     739                 :     }
     740                 : 
     741               0 :     if (sLastFocusedModule && sLastFocusedModule != this) {
     742               0 :         sLastFocusedModule->Blur();
     743                 :     }
     744                 : 
     745               0 :     sLastFocusedModule = this;
     746                 : 
     747               0 :     gtk_im_context_focus_in(im);
     748               0 :     mIsIMFocused = true;
     749                 : 
     750               0 :     if (!IsEnabled()) {
     751                 :         // We should release IME focus for uim and scim.
     752                 :         // These IMs are using snooper that is released at losing focus.
     753               0 :         Blur();
     754                 :     }
     755                 : }
     756                 : 
     757                 : void
     758               0 : nsGtkIMModule::Blur()
     759                 : {
     760               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     761                 :         ("GtkIMModule(%p): Blur, mIsIMFocused=%s",
     762                 :          this, mIsIMFocused ? "YES" : "NO"));
     763                 : 
     764               0 :     if (!mIsIMFocused) {
     765               0 :         return;
     766                 :     }
     767                 : 
     768               0 :     GtkIMContext *im = GetContext();
     769               0 :     if (!im) {
     770               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     771                 :             ("    FAILED, there are no context"));
     772               0 :         return;
     773                 :     }
     774                 : 
     775               0 :     gtk_im_context_focus_out(im);
     776               0 :     mIsIMFocused = false;
     777                 : }
     778                 : 
     779                 : /* static */
     780                 : void
     781               0 : nsGtkIMModule::OnStartCompositionCallback(GtkIMContext *aContext,
     782                 :                                           nsGtkIMModule* aModule)
     783                 : {
     784               0 :     aModule->OnStartCompositionNative(aContext);
     785               0 : }
     786                 : 
     787                 : void
     788               0 : nsGtkIMModule::OnStartCompositionNative(GtkIMContext *aContext)
     789                 : {
     790               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     791                 :         ("GtkIMModule(%p): OnStartCompositionNative, aContext=%p",
     792                 :          this, aContext));
     793                 : 
     794                 :     // See bug 472635, we should do nothing if IM context doesn't match.
     795               0 :     if (GetContext() != aContext) {
     796               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     797                 :             ("    FAILED, given context doesn't match, GetContext()=%p",
     798                 :              GetContext()));
     799               0 :         return;
     800                 :     }
     801                 : 
     802               0 :     if (!DispatchCompositionStart()) {
     803               0 :         return;
     804                 :     }
     805               0 :     SetCursorPosition(mCompositionStart);
     806                 : }
     807                 : 
     808                 : /* static */
     809                 : void
     810               0 : nsGtkIMModule::OnEndCompositionCallback(GtkIMContext *aContext,
     811                 :                                         nsGtkIMModule* aModule)
     812                 : {
     813               0 :     aModule->OnEndCompositionNative(aContext);
     814               0 : }
     815                 : 
     816                 : void
     817               0 : nsGtkIMModule::OnEndCompositionNative(GtkIMContext *aContext)
     818                 : {
     819               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     820                 :         ("GtkIMModule(%p): OnEndCompositionNative, aContext=%p",
     821                 :          this, aContext));
     822                 : 
     823                 :     // See bug 472635, we should do nothing if IM context doesn't match.
     824               0 :     if (GetContext() != aContext) {
     825               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     826                 :             ("    FAILED, given context doesn't match, GetContext()=%p",
     827                 :              GetContext()));
     828               0 :         return;
     829                 :     }
     830                 : 
     831               0 :     bool shouldIgnoreThisEvent = ShouldIgnoreNativeCompositionEvent();
     832                 : 
     833                 :     // Finish the cancelling mode here rather than DispatchCompositionEnd()
     834                 :     // because DispatchCompositionEnd() is called ourselves when we need to
     835                 :     // commit the composition string *before* the focus moves completely.
     836                 :     // Note that the native commit can be fired *after* ResetIME().
     837               0 :     mIgnoreNativeCompositionEvent = false;
     838                 : 
     839               0 :     if (!IsComposing() || shouldIgnoreThisEvent) {
     840                 :         // If we already handled the commit event, we should do nothing here.
     841               0 :         return;
     842                 :     }
     843                 : 
     844                 :     // Be aware, widget can be gone
     845               0 :     DispatchCompositionEnd();
     846                 : }
     847                 : 
     848                 : /* static */
     849                 : void
     850               0 : nsGtkIMModule::OnChangeCompositionCallback(GtkIMContext *aContext,
     851                 :                                            nsGtkIMModule* aModule)
     852                 : {
     853               0 :     aModule->OnChangeCompositionNative(aContext);
     854               0 : }
     855                 : 
     856                 : void
     857               0 : nsGtkIMModule::OnChangeCompositionNative(GtkIMContext *aContext)
     858                 : {
     859               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     860                 :         ("GtkIMModule(%p): OnChangeCompositionNative, aContext=%p",
     861                 :          this, aContext));
     862                 : 
     863                 :     // See bug 472635, we should do nothing if IM context doesn't match.
     864               0 :     if (GetContext() != aContext) {
     865               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     866                 :             ("    FAILED, given context doesn't match, GetContext()=%p",
     867                 :              GetContext()));
     868               0 :         return;
     869                 :     }
     870                 : 
     871               0 :     if (ShouldIgnoreNativeCompositionEvent()) {
     872               0 :         return;
     873                 :     }
     874                 : 
     875               0 :     nsAutoString compositionString;
     876               0 :     GetCompositionString(compositionString);
     877               0 :     if (!IsComposing() && compositionString.IsEmpty()) {
     878               0 :         mDispatchedCompositionString.Truncate();
     879                 :         return; // Don't start the composition with empty string.
     880                 :     }
     881                 : 
     882                 :     // Be aware, widget can be gone
     883               0 :     DispatchTextEvent(compositionString, false);
     884                 : }
     885                 : 
     886                 : /* static */
     887                 : gboolean
     888               0 : nsGtkIMModule::OnRetrieveSurroundingCallback(GtkIMContext  *aContext,
     889                 :                                              nsGtkIMModule *aModule)
     890                 : {
     891               0 :     return aModule->OnRetrieveSurroundingNative(aContext);
     892                 : }
     893                 : 
     894                 : gboolean
     895               0 : nsGtkIMModule::OnRetrieveSurroundingNative(GtkIMContext *aContext)
     896                 : {
     897               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     898                 :         ("GtkIMModule(%p): OnRetrieveSurroundingNative, aContext=%p, current context=%p",
     899                 :          this, aContext, GetContext()));
     900                 : 
     901               0 :     if (GetContext() != aContext) {
     902               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     903                 :             ("    FAILED, given context doesn't match, GetContext()=%p",
     904                 :              GetContext()));
     905               0 :         return FALSE;
     906                 :     }
     907                 : 
     908               0 :     nsAutoString uniStr;
     909                 :     PRUint32 cursorPos;
     910               0 :     if (NS_FAILED(GetCurrentParagraph(uniStr, cursorPos))) {
     911               0 :         return FALSE;
     912                 :     }
     913                 : 
     914                 :     glong wbytes;
     915               0 :     gchar *utf8_str = g_utf16_to_utf8((const gunichar2 *)uniStr.get(),
     916               0 :                                       uniStr.Length(), NULL, &wbytes, NULL);
     917               0 :     if (utf8_str == NULL) {
     918               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     919                 :             ("    failed to convert utf16 string to utf8"));
     920               0 :         return FALSE;
     921                 :     }
     922                 :     gtk_im_context_set_surrounding(aContext, utf8_str, wbytes,
     923               0 :         g_utf8_offset_to_pointer(utf8_str, cursorPos) - utf8_str);
     924               0 :     g_free(utf8_str);
     925                 : 
     926               0 :     return TRUE;
     927                 : }
     928                 : 
     929                 : /* static */
     930                 : gboolean
     931               0 : nsGtkIMModule::OnDeleteSurroundingCallback(GtkIMContext  *aContext,
     932                 :                                            gint           aOffset,
     933                 :                                            gint           aNChars,
     934                 :                                            nsGtkIMModule *aModule)
     935                 : {
     936               0 :     return aModule->OnDeleteSurroundingNative(aContext, aOffset, aNChars);
     937                 : }
     938                 : 
     939                 : gboolean
     940               0 : nsGtkIMModule::OnDeleteSurroundingNative(GtkIMContext  *aContext,
     941                 :                                          gint           aOffset,
     942                 :                                          gint           aNChars)
     943                 : {
     944               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     945                 :         ("GtkIMModule(%p): OnDeleteSurroundingNative, aContext=%p, current context=%p",
     946                 :          this, aContext, GetContext()));
     947                 : 
     948               0 :     if (GetContext() != aContext) {
     949               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     950                 :             ("    FAILED, given context doesn't match, GetContext()=%p",
     951                 :              GetContext()));
     952               0 :         return FALSE;
     953                 :     }
     954                 : 
     955               0 :     if (NS_SUCCEEDED(DeleteText(aOffset, (PRUint32)aNChars))) {
     956               0 :         return TRUE;
     957                 :     }
     958                 : 
     959                 :     // failed
     960               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     961                 :         ("    FAILED, cannot delete text"));
     962               0 :     return FALSE;
     963                 : }
     964                 :                          
     965                 : /* static */
     966                 : void
     967               0 : nsGtkIMModule::OnCommitCompositionCallback(GtkIMContext *aContext,
     968                 :                                            const gchar *aString,
     969                 :                                            nsGtkIMModule* aModule)
     970                 : {
     971               0 :     aModule->OnCommitCompositionNative(aContext, aString);
     972               0 : }
     973                 : 
     974                 : void
     975               0 : nsGtkIMModule::OnCommitCompositionNative(GtkIMContext *aContext,
     976                 :                                          const gchar *aUTF8Char)
     977                 : {
     978               0 :     const gchar emptyStr = 0;
     979               0 :     const gchar *commitString = aUTF8Char ? aUTF8Char : &emptyStr;
     980                 : 
     981               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     982                 :         ("GtkIMModule(%p): OnCommitCompositionNative, aContext=%p, current context=%p, commitString=\"%s\"",
     983                 :          this, aContext, GetContext(), commitString));
     984                 : 
     985                 :     // See bug 472635, we should do nothing if IM context doesn't match.
     986               0 :     if (GetContext() != aContext) {
     987               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
     988                 :             ("    FAILED, given context doesn't match, GetContext()=%p",
     989                 :              GetContext()));
     990               0 :         return;
     991                 :     }
     992                 : 
     993                 :     // If we are not in composition and committing with empty string,
     994                 :     // we need to do nothing because if we continued to handle this
     995                 :     // signal, we would dispatch compositionstart, text, compositionend
     996                 :     // events with empty string.  Of course, they are unnecessary events
     997                 :     // for Web applications and our editor.
     998               0 :     if (!IsComposing() && !commitString[0]) {
     999               0 :         return;
    1000                 :     }
    1001                 : 
    1002               0 :     if (ShouldIgnoreNativeCompositionEvent()) {
    1003               0 :         return;
    1004                 :     }
    1005                 : 
    1006                 :     // If IME doesn't change their keyevent that generated this commit,
    1007                 :     // don't send it through XIM - just send it as a normal key press
    1008                 :     // event.
    1009               0 :     if (!IsComposing() && mProcessingKeyEvent) {
    1010                 :         char keyval_utf8[8]; /* should have at least 6 bytes of space */
    1011                 :         gint keyval_utf8_len;
    1012                 :         guint32 keyval_unicode;
    1013                 : 
    1014               0 :         keyval_unicode = gdk_keyval_to_unicode(mProcessingKeyEvent->keyval);
    1015               0 :         keyval_utf8_len = g_unichar_to_utf8(keyval_unicode, keyval_utf8);
    1016               0 :         keyval_utf8[keyval_utf8_len] = '\0';
    1017                 : 
    1018               0 :         if (!strcmp(commitString, keyval_utf8)) {
    1019               0 :             PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1020                 :                 ("GtkIMModule(%p): OnCommitCompositionNative, we'll send normal key event",
    1021                 :                  this));
    1022               0 :             mFilterKeyEvent = false;
    1023               0 :             return;
    1024                 :         }
    1025                 :     }
    1026                 : 
    1027               0 :     NS_ConvertUTF8toUTF16 str(commitString);
    1028               0 :     CommitCompositionBy(str); // Be aware, widget can be gone
    1029                 : }
    1030                 : 
    1031                 : bool
    1032               0 : nsGtkIMModule::CommitCompositionBy(const nsAString& aString)
    1033                 : {
    1034               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1035                 :         ("GtkIMModule(%p): CommitCompositionBy, aString=\"%s\", "
    1036                 :          "mDispatchedCompositionString=\"%s\"",
    1037                 :          this, NS_ConvertUTF16toUTF8(aString).get(),
    1038                 :          NS_ConvertUTF16toUTF8(mDispatchedCompositionString).get()));
    1039                 : 
    1040               0 :     if (!DispatchTextEvent(aString, true)) {
    1041               0 :         return false;
    1042                 :     }
    1043                 :     // We should dispatch the compositionend event here because some IMEs
    1044                 :     // might not fire "preedit_end" native event.
    1045               0 :     return DispatchCompositionEnd(); // Be aware, widget can be gone
    1046                 : }
    1047                 : 
    1048                 : void
    1049               0 : nsGtkIMModule::GetCompositionString(nsAString &aCompositionString)
    1050                 : {
    1051                 :     gchar *preedit_string;
    1052                 :     gint cursor_pos;
    1053                 :     PangoAttrList *feedback_list;
    1054                 :     gtk_im_context_get_preedit_string(GetContext(), &preedit_string,
    1055               0 :                                       &feedback_list, &cursor_pos);
    1056               0 :     if (preedit_string && *preedit_string) {
    1057               0 :         CopyUTF8toUTF16(preedit_string, aCompositionString);
    1058                 :     } else {
    1059               0 :         aCompositionString.Truncate();
    1060                 :     }
    1061                 : 
    1062               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1063                 :         ("GtkIMModule(%p): GetCompositionString, result=\"%s\"",
    1064                 :          this, preedit_string));
    1065                 : 
    1066               0 :     pango_attr_list_unref(feedback_list);
    1067               0 :     g_free(preedit_string);
    1068               0 : }
    1069                 : 
    1070                 : bool
    1071               0 : nsGtkIMModule::DispatchCompositionStart()
    1072                 : {
    1073               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1074                 :         ("GtkIMModule(%p): DispatchCompositionStart", this));
    1075                 : 
    1076               0 :     if (IsComposing()) {
    1077               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1078                 :             ("    WARNING, we're already in composition"));
    1079               0 :         return true;
    1080                 :     }
    1081                 : 
    1082               0 :     if (!mLastFocusedWindow) {
    1083               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1084                 :             ("    FAILED, there are no focused window in this module"));
    1085               0 :         return false;
    1086                 :     }
    1087                 : 
    1088                 :     nsEventStatus status;
    1089                 :     nsQueryContentEvent selection(true, NS_QUERY_SELECTED_TEXT,
    1090               0 :                                   mLastFocusedWindow);
    1091               0 :     InitEvent(selection);
    1092               0 :     mLastFocusedWindow->DispatchEvent(&selection, status);
    1093                 : 
    1094               0 :     if (!selection.mSucceeded || selection.mReply.mOffset == PR_UINT32_MAX) {
    1095               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1096                 :             ("    FAILED, cannot query the selection offset"));
    1097               0 :         return false;
    1098                 :     }
    1099                 : 
    1100                 :     // XXX The composition start point might be changed by composition events
    1101                 :     //     even though we strongly hope it doesn't happen.
    1102                 :     //     Every composition event should have the start offset for the result
    1103                 :     //     because it may high cost if we query the offset every time.
    1104               0 :     mCompositionStart = selection.mReply.mOffset;
    1105               0 :     mDispatchedCompositionString.Truncate();
    1106                 : 
    1107               0 :     if (mProcessingKeyEvent && !mKeyDownEventWasSent &&
    1108                 :         mProcessingKeyEvent->type == GDK_KEY_PRESS) {
    1109                 :         // If this composition is started by a native keydown event, we need to
    1110                 :         // dispatch our keydown event here (before composition start).
    1111               0 :         nsCOMPtr<nsIWidget> kungFuDeathGrip = mLastFocusedWindow;
    1112                 :         bool isCancelled;
    1113                 :         mLastFocusedWindow->DispatchKeyDownEvent(mProcessingKeyEvent,
    1114               0 :                                                  &isCancelled);
    1115               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1116                 :             ("    keydown event is dispatched"));
    1117               0 :         if (static_cast<nsWindow*>(kungFuDeathGrip.get())->IsDestroyed() ||
    1118               0 :             kungFuDeathGrip != mLastFocusedWindow) {
    1119               0 :             PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1120                 :                 ("    NOTE, the focused widget was destroyed/changed by keydown event"));
    1121               0 :             return false;
    1122                 :         }
    1123                 :     }
    1124                 : 
    1125               0 :     if (mIgnoreNativeCompositionEvent) {
    1126               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1127                 :             ("    WARNING, mIgnoreNativeCompositionEvent is already TRUE, but we forcedly reset"));
    1128               0 :         mIgnoreNativeCompositionEvent = false;
    1129                 :     }
    1130                 : 
    1131               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1132                 :         ("    mCompositionStart=%u", mCompositionStart));
    1133               0 :     mCompositionState = eCompositionState_CompositionStartDispatched;
    1134                 :     nsCompositionEvent compEvent(true, NS_COMPOSITION_START,
    1135               0 :                                  mLastFocusedWindow);
    1136               0 :     InitEvent(compEvent);
    1137               0 :     nsCOMPtr<nsIWidget> kungFuDeathGrip = mLastFocusedWindow;
    1138               0 :     mLastFocusedWindow->DispatchEvent(&compEvent, status);
    1139               0 :     if (static_cast<nsWindow*>(kungFuDeathGrip.get())->IsDestroyed() ||
    1140               0 :         kungFuDeathGrip != mLastFocusedWindow) {
    1141               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1142                 :             ("    NOTE, the focused widget was destroyed/changed by compositionstart event"));
    1143               0 :         return false;
    1144                 :     }
    1145                 : 
    1146               0 :     return true;
    1147                 : }
    1148                 : 
    1149                 : bool
    1150               0 : nsGtkIMModule::DispatchCompositionEnd()
    1151                 : {
    1152               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1153                 :         ("GtkIMModule(%p): DispatchCompositionEnd, "
    1154                 :          "mDispatchedCompositionString=\"%s\"",
    1155                 :          this, NS_ConvertUTF16toUTF8(mDispatchedCompositionString).get()));
    1156                 : 
    1157               0 :     if (!IsComposing()) {
    1158               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1159                 :             ("    WARNING, we have alrady finished the composition"));
    1160               0 :         return false;
    1161                 :     }
    1162                 : 
    1163               0 :     if (!mLastFocusedWindow) {
    1164               0 :         mDispatchedCompositionString.Truncate();
    1165               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1166                 :             ("    FAILED, there are no focused window in this module"));
    1167               0 :         return false;
    1168                 :     }
    1169                 : 
    1170                 :     nsCompositionEvent compEvent(true, NS_COMPOSITION_END,
    1171               0 :                                  mLastFocusedWindow);
    1172               0 :     InitEvent(compEvent);
    1173               0 :     compEvent.data = mDispatchedCompositionString;
    1174                 :     nsEventStatus status;
    1175               0 :     nsCOMPtr<nsIWidget> kungFuDeathGrip = mLastFocusedWindow;
    1176               0 :     mLastFocusedWindow->DispatchEvent(&compEvent, status);
    1177               0 :     mCompositionState = eCompositionState_NotComposing;
    1178               0 :     mCompositionStart = PR_UINT32_MAX;
    1179               0 :     mDispatchedCompositionString.Truncate();
    1180               0 :     if (static_cast<nsWindow*>(kungFuDeathGrip.get())->IsDestroyed() ||
    1181               0 :         kungFuDeathGrip != mLastFocusedWindow) {
    1182               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1183                 :             ("    NOTE, the focused widget was destroyed/changed by compositionend event"));
    1184               0 :         return false;
    1185                 :     }
    1186                 : 
    1187               0 :     return true;
    1188                 : }
    1189                 : 
    1190                 : bool
    1191               0 : nsGtkIMModule::DispatchTextEvent(const nsAString &aCompositionString,
    1192                 :                                  bool aIsCommit)
    1193                 : {
    1194               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1195                 :         ("GtkIMModule(%p): DispatchTextEvent, aIsCommit=%s",
    1196                 :          this, aIsCommit ? "TRUE" : "FALSE"));
    1197                 : 
    1198               0 :     if (!mLastFocusedWindow) {
    1199               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1200                 :             ("    FAILED, there are no focused window in this module"));
    1201               0 :         return false;
    1202                 :     }
    1203                 : 
    1204               0 :     if (!IsComposing()) {
    1205               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1206                 :             ("    The composition wasn't started, force starting..."));
    1207               0 :         nsCOMPtr<nsIWidget> kungFuDeathGrip = mLastFocusedWindow;
    1208               0 :         if (!DispatchCompositionStart()) {
    1209               0 :             return false;
    1210                 :         }
    1211                 :     }
    1212                 : 
    1213                 :     nsEventStatus status;
    1214               0 :     nsRefPtr<nsWindow> lastFocusedWindow = mLastFocusedWindow;
    1215                 : 
    1216               0 :     if (aCompositionString != mDispatchedCompositionString) {
    1217                 :       nsCompositionEvent compositionUpdate(true, NS_COMPOSITION_UPDATE,
    1218               0 :                                            mLastFocusedWindow);
    1219               0 :       InitEvent(compositionUpdate);
    1220               0 :       compositionUpdate.data = aCompositionString;
    1221               0 :       mDispatchedCompositionString = aCompositionString;
    1222               0 :       mLastFocusedWindow->DispatchEvent(&compositionUpdate, status);
    1223               0 :       if (lastFocusedWindow->IsDestroyed() ||
    1224               0 :           lastFocusedWindow != mLastFocusedWindow) {
    1225               0 :           PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1226                 :               ("    NOTE, the focused widget was destroyed/changed by compositionupdate"));
    1227               0 :           return false;
    1228                 :       }
    1229                 :     }
    1230                 : 
    1231                 :     // Store the selected string which will be removed by following text event.
    1232               0 :     if (mCompositionState == eCompositionState_CompositionStartDispatched) {
    1233                 :         // XXX We should assume, for now, any web applications don't change
    1234                 :         //     selection at handling this text event.
    1235                 :         nsQueryContentEvent querySelectedTextEvent(true,
    1236                 :                                                    NS_QUERY_SELECTED_TEXT,
    1237               0 :                                                    mLastFocusedWindow);
    1238               0 :         mLastFocusedWindow->DispatchEvent(&querySelectedTextEvent, status);
    1239               0 :         if (querySelectedTextEvent.mSucceeded) {
    1240               0 :             mSelectedString = querySelectedTextEvent.mReply.mString;
    1241               0 :             mCompositionStart = querySelectedTextEvent.mReply.mOffset;
    1242                 :         }
    1243                 :     }
    1244                 : 
    1245               0 :     nsTextEvent textEvent(true, NS_TEXT_TEXT, mLastFocusedWindow);
    1246               0 :     InitEvent(textEvent);
    1247                 : 
    1248               0 :     PRUint32 targetOffset = mCompositionStart;
    1249                 : 
    1250               0 :     nsAutoTArray<nsTextRange, 4> textRanges;
    1251               0 :     if (!aIsCommit) {
    1252                 :         // NOTE: SetTextRangeList() assumes that mDispatchedCompositionString
    1253                 :         //       has been updated already.
    1254               0 :         SetTextRangeList(textRanges);
    1255               0 :         for (PRUint32 i = 0; i < textRanges.Length(); i++) {
    1256               0 :             nsTextRange& range = textRanges[i];
    1257               0 :             if (range.mRangeType == NS_TEXTRANGE_SELECTEDRAWTEXT ||
    1258                 :                 range.mRangeType == NS_TEXTRANGE_SELECTEDCONVERTEDTEXT) {
    1259               0 :                 targetOffset += range.mStartOffset;
    1260               0 :                 break;
    1261                 :             }
    1262                 :         }
    1263                 :     }
    1264                 : 
    1265               0 :     textEvent.rangeCount = textRanges.Length();
    1266               0 :     textEvent.rangeArray = textRanges.Elements();
    1267               0 :     textEvent.theText = mDispatchedCompositionString.get();
    1268                 : 
    1269                 :     mCompositionState = aIsCommit ?
    1270                 :         eCompositionState_CommitTextEventDispatched :
    1271               0 :         eCompositionState_TextEventDispatched;
    1272                 : 
    1273               0 :     mLastFocusedWindow->DispatchEvent(&textEvent, status);
    1274               0 :     if (lastFocusedWindow->IsDestroyed() ||
    1275               0 :         lastFocusedWindow != mLastFocusedWindow) {
    1276               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1277                 :             ("    NOTE, the focused widget was destroyed/changed by text event"));
    1278               0 :         return false;
    1279                 :     }
    1280                 : 
    1281               0 :     SetCursorPosition(targetOffset);
    1282                 : 
    1283               0 :     return true;
    1284                 : }
    1285                 : 
    1286                 : void
    1287               0 : nsGtkIMModule::SetTextRangeList(nsTArray<nsTextRange> &aTextRangeList)
    1288                 : {
    1289               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1290                 :         ("GtkIMModule(%p): SetTextRangeList", this));
    1291                 : 
    1292               0 :     NS_PRECONDITION(aTextRangeList.IsEmpty(), "aTextRangeList must be empty");
    1293                 : 
    1294                 :     gchar *preedit_string;
    1295                 :     gint cursor_pos;
    1296                 :     PangoAttrList *feedback_list;
    1297                 :     gtk_im_context_get_preedit_string(GetContext(), &preedit_string,
    1298               0 :                                       &feedback_list, &cursor_pos);
    1299               0 :     if (!preedit_string || !*preedit_string) {
    1300               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1301                 :             ("    preedit_string is null"));
    1302               0 :         pango_attr_list_unref(feedback_list);
    1303               0 :         g_free(preedit_string);
    1304               0 :         return;
    1305                 :     }
    1306                 : 
    1307                 :     PangoAttrIterator* iter;
    1308               0 :     iter = pango_attr_list_get_iterator(feedback_list);
    1309               0 :     if (!iter) {
    1310               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1311                 :             ("    FAILED, iterator couldn't be allocated"));
    1312               0 :         pango_attr_list_unref(feedback_list);
    1313               0 :         g_free(preedit_string);
    1314               0 :         return;
    1315                 :     }
    1316                 : 
    1317                 :     /*
    1318                 :      * Depend on gtk2's implementation on XIM support.
    1319                 :      * In aFeedback got from gtk2, there are only three types of data:
    1320                 :      * PANGO_ATTR_UNDERLINE, PANGO_ATTR_FOREGROUND, PANGO_ATTR_BACKGROUND.
    1321                 :      * Corresponding to XIMUnderline, XIMReverse.
    1322                 :      * Don't take PANGO_ATTR_BACKGROUND into account, since
    1323                 :      * PANGO_ATTR_BACKGROUND and PANGO_ATTR_FOREGROUND are always
    1324                 :      * a couple.
    1325                 :      */
    1326               0 :     do {
    1327                 :         PangoAttribute* attrUnderline =
    1328               0 :             pango_attr_iterator_get(iter, PANGO_ATTR_UNDERLINE);
    1329                 :         PangoAttribute* attrForeground =
    1330               0 :             pango_attr_iterator_get(iter, PANGO_ATTR_FOREGROUND);
    1331               0 :         if (!attrUnderline && !attrForeground) {
    1332               0 :             continue;
    1333                 :         }
    1334                 : 
    1335                 :         // Get the range of the current attribute(s)
    1336                 :         gint start, end;
    1337               0 :         pango_attr_iterator_range(iter, &start, &end);
    1338                 : 
    1339               0 :         nsTextRange range;
    1340                 :         // XIMReverse | XIMUnderline
    1341               0 :         if (attrUnderline && attrForeground) {
    1342               0 :             range.mRangeType = NS_TEXTRANGE_SELECTEDCONVERTEDTEXT;
    1343                 :         }
    1344                 :         // XIMUnderline
    1345               0 :         else if (attrUnderline) {
    1346               0 :             range.mRangeType = NS_TEXTRANGE_CONVERTEDTEXT;
    1347                 :         }
    1348                 :         // XIMReverse
    1349               0 :         else if (attrForeground) {
    1350               0 :             range.mRangeType = NS_TEXTRANGE_SELECTEDRAWTEXT;
    1351                 :         } else {
    1352               0 :             range.mRangeType = NS_TEXTRANGE_RAWINPUT;
    1353                 :         }
    1354                 : 
    1355               0 :         gunichar2* uniStr = nsnull;
    1356               0 :         if (start == 0) {
    1357               0 :             range.mStartOffset = 0;
    1358                 :         } else {
    1359                 :             glong uniStrLen;
    1360                 :             uniStr = g_utf8_to_utf16(preedit_string, start,
    1361               0 :                                      NULL, &uniStrLen, NULL);
    1362               0 :             if (uniStr) {
    1363               0 :                 range.mStartOffset = uniStrLen;
    1364               0 :                 g_free(uniStr);
    1365               0 :                 uniStr = nsnull;
    1366                 :             }
    1367                 :         }
    1368                 : 
    1369                 :         glong uniStrLen;
    1370                 :         uniStr = g_utf8_to_utf16(preedit_string + start, end - start,
    1371               0 :                                  NULL, &uniStrLen, NULL);
    1372               0 :         if (!uniStr) {
    1373               0 :             range.mEndOffset = range.mStartOffset;
    1374                 :         } else {
    1375               0 :             range.mEndOffset = range.mStartOffset + uniStrLen;
    1376               0 :             g_free(uniStr);
    1377               0 :             uniStr = nsnull;
    1378                 :         }
    1379                 : 
    1380               0 :         aTextRangeList.AppendElement(range);
    1381                 : 
    1382               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1383                 :             ("    mStartOffset=%u, mEndOffset=%u, mRangeType=%s",
    1384                 :              range.mStartOffset, range.mEndOffset,
    1385                 :              GetRangeTypeName(range.mRangeType)));
    1386               0 :     } while (pango_attr_iterator_next(iter));
    1387                 : 
    1388               0 :     nsTextRange range;
    1389               0 :     if (cursor_pos < 0) {
    1390               0 :         range.mStartOffset = 0;
    1391               0 :     } else if (PRUint32(cursor_pos) > mDispatchedCompositionString.Length()) {
    1392               0 :         range.mStartOffset = mDispatchedCompositionString.Length();
    1393                 :     } else {
    1394               0 :         range.mStartOffset = PRUint32(cursor_pos);
    1395                 :     }
    1396               0 :     range.mEndOffset = range.mStartOffset;
    1397               0 :     range.mRangeType = NS_TEXTRANGE_CARETPOSITION;
    1398               0 :     aTextRangeList.AppendElement(range);
    1399                 : 
    1400               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1401                 :         ("    mStartOffset=%u, mEndOffset=%u, mRangeType=%s",
    1402                 :          range.mStartOffset, range.mEndOffset,
    1403                 :          GetRangeTypeName(range.mRangeType)));
    1404                 : 
    1405               0 :     pango_attr_iterator_destroy(iter);
    1406               0 :     pango_attr_list_unref(feedback_list);
    1407               0 :     g_free(preedit_string);
    1408                 : }
    1409                 : 
    1410                 : void
    1411               0 : nsGtkIMModule::SetCursorPosition(PRUint32 aTargetOffset)
    1412                 : {
    1413               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1414                 :         ("GtkIMModule(%p): SetCursorPosition, aTargetOffset=%u",
    1415                 :          this, aTargetOffset));
    1416                 : 
    1417               0 :     if (aTargetOffset == PR_UINT32_MAX) {
    1418               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1419                 :             ("    FAILED, aTargetOffset is wrong offset"));
    1420               0 :         return;
    1421                 :     }
    1422                 : 
    1423               0 :     if (!mLastFocusedWindow) {
    1424               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1425                 :             ("    FAILED, there are no focused window"));
    1426               0 :         return;
    1427                 :     }
    1428                 : 
    1429               0 :     GtkIMContext *im = GetContext();
    1430               0 :     if (!im) {
    1431               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1432                 :             ("    FAILED, there are no context"));
    1433               0 :         return;
    1434                 :     }
    1435                 : 
    1436                 :     nsQueryContentEvent charRect(true, NS_QUERY_TEXT_RECT,
    1437               0 :                                  mLastFocusedWindow);
    1438               0 :     charRect.InitForQueryTextRect(aTargetOffset, 1);
    1439               0 :     InitEvent(charRect);
    1440                 :     nsEventStatus status;
    1441               0 :     mLastFocusedWindow->DispatchEvent(&charRect, status);
    1442               0 :     if (!charRect.mSucceeded) {
    1443               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1444                 :             ("    FAILED, NS_QUERY_TEXT_RECT was failed"));
    1445                 :         return;
    1446                 :     }
    1447                 :     nsWindow* rootWindow =
    1448               0 :         static_cast<nsWindow*>(mLastFocusedWindow->GetTopLevelWidget());
    1449                 : 
    1450                 :     // Get the position of the rootWindow in screen.
    1451                 :     gint rootX, rootY;
    1452               0 :     gdk_window_get_origin(rootWindow->GetGdkWindow(), &rootX, &rootY);
    1453                 : 
    1454                 :     // Get the position of IM context owner window in screen.
    1455                 :     gint ownerX, ownerY;
    1456               0 :     gdk_window_get_origin(mOwnerWindow->GetGdkWindow(), &ownerX, &ownerY);
    1457                 : 
    1458                 :     // Compute the caret position in the IM owner window.
    1459                 :     GdkRectangle area;
    1460               0 :     area.x = charRect.mReply.mRect.x + rootX - ownerX;
    1461               0 :     area.y = charRect.mReply.mRect.y + rootY - ownerY;
    1462               0 :     area.width  = 0;
    1463               0 :     area.height = charRect.mReply.mRect.height;
    1464                 : 
    1465               0 :     gtk_im_context_set_cursor_location(im, &area);
    1466                 : }
    1467                 : 
    1468                 : nsresult
    1469               0 : nsGtkIMModule::GetCurrentParagraph(nsAString& aText, PRUint32& aCursorPos)
    1470                 : {
    1471               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1472                 :         ("GtkIMModule(%p): GetCurrentParagraph, mCompositionState=%s",
    1473                 :          this, GetCompositionStateName()));
    1474                 : 
    1475               0 :     if (!mLastFocusedWindow) {
    1476               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1477                 :             ("    FAILED, there are no focused window in this module"));
    1478               0 :         return NS_ERROR_NULL_POINTER;
    1479                 :     }
    1480                 : 
    1481                 :     nsEventStatus status;
    1482                 : 
    1483               0 :     PRUint32 selOffset = mCompositionStart;
    1484               0 :     PRUint32 selLength = mSelectedString.Length();
    1485                 : 
    1486                 :     // If focused editor doesn't have composition string, we should use
    1487                 :     // current selection.
    1488               0 :     if (!EditorHasCompositionString()) {
    1489                 :         // Query cursor position & selection
    1490                 :         nsQueryContentEvent querySelectedTextEvent(true,
    1491                 :                                                    NS_QUERY_SELECTED_TEXT,
    1492               0 :                                                    mLastFocusedWindow);
    1493               0 :         mLastFocusedWindow->DispatchEvent(&querySelectedTextEvent, status);
    1494               0 :         NS_ENSURE_TRUE(querySelectedTextEvent.mSucceeded, NS_ERROR_FAILURE);
    1495                 : 
    1496               0 :         selOffset = querySelectedTextEvent.mReply.mOffset;
    1497               0 :         selLength = querySelectedTextEvent.mReply.mString.Length();
    1498                 :     }
    1499                 : 
    1500               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1501                 :         ("        selOffset=%u, selLength=%u",
    1502                 :          selOffset, selLength));
    1503                 : 
    1504                 :     // XXX nsString::Find and nsString::RFind take PRInt32 for offset, so,
    1505                 :     //     we cannot support this request when the current offset is larger
    1506                 :     //     than PR_INT32_MAX.
    1507               0 :     if (selOffset > PR_INT32_MAX || selLength > PR_INT32_MAX ||
    1508                 :         selOffset + selLength > PR_INT32_MAX) {
    1509               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1510                 :             ("    FAILED, The selection is out of range"));
    1511               0 :         return NS_ERROR_FAILURE;
    1512                 :     }
    1513                 : 
    1514                 :     // Get all text contents of the focused editor
    1515                 :     nsQueryContentEvent queryTextContentEvent(true,
    1516                 :                                               NS_QUERY_TEXT_CONTENT,
    1517               0 :                                               mLastFocusedWindow);
    1518               0 :     queryTextContentEvent.InitForQueryTextContent(0, PR_UINT32_MAX);
    1519               0 :     mLastFocusedWindow->DispatchEvent(&queryTextContentEvent, status);
    1520               0 :     NS_ENSURE_TRUE(queryTextContentEvent.mSucceeded, NS_ERROR_FAILURE);
    1521                 : 
    1522               0 :     nsAutoString textContent(queryTextContentEvent.mReply.mString);
    1523               0 :     if (selOffset + selLength > textContent.Length()) {
    1524               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1525                 :             ("    FAILED, The selection is invalid, textContent.Length()=%u",
    1526                 :              textContent.Length()));
    1527               0 :         return NS_ERROR_FAILURE;
    1528                 :     }
    1529                 : 
    1530                 :     // Remove composing string and restore the selected string because
    1531                 :     // GtkEntry doesn't remove selected string until committing, however,
    1532                 :     // our editor does it.  We should emulate the behavior for IME.
    1533               0 :     if (EditorHasCompositionString() &&
    1534               0 :         mDispatchedCompositionString != mSelectedString) {
    1535                 :         textContent.Replace(mCompositionStart,
    1536               0 :             mDispatchedCompositionString.Length(), mSelectedString);
    1537                 :     }
    1538                 : 
    1539                 :     // Get only the focused paragraph, by looking for newlines
    1540                 :     PRInt32 parStart = (selOffset == 0) ? 0 :
    1541               0 :         textContent.RFind("\n", false, selOffset - 1, -1) + 1;
    1542               0 :     PRInt32 parEnd = textContent.Find("\n", false, selOffset + selLength, -1);
    1543               0 :     if (parEnd < 0) {
    1544               0 :         parEnd = textContent.Length();
    1545                 :     }
    1546               0 :     aText = nsDependentSubstring(textContent, parStart, parEnd - parStart);
    1547               0 :     aCursorPos = selOffset - PRUint32(parStart);
    1548                 : 
    1549               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1550                 :         ("    aText=%s, aText.Length()=%u, aCursorPos=%u",
    1551                 :          NS_ConvertUTF16toUTF8(aText).get(),
    1552                 :          aText.Length(), aCursorPos));
    1553                 : 
    1554               0 :     return NS_OK;
    1555                 : }
    1556                 : 
    1557                 : nsresult
    1558               0 : nsGtkIMModule::DeleteText(const PRInt32 aOffset, const PRUint32 aNChars)
    1559                 : {
    1560               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1561                 :         ("GtkIMModule(%p): DeleteText, aOffset=%d, aNChars=%d, "
    1562                 :          "mCompositionState=%s",
    1563                 :          this, aOffset, aNChars, GetCompositionStateName()));
    1564                 : 
    1565               0 :     if (!mLastFocusedWindow) {
    1566               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1567                 :             ("    FAILED, there are no focused window in this module"));
    1568               0 :         return NS_ERROR_NULL_POINTER;
    1569                 :     }
    1570                 : 
    1571               0 :     nsRefPtr<nsWindow> lastFocusedWindow(mLastFocusedWindow);
    1572                 :     nsEventStatus status;
    1573                 : 
    1574                 :     // First, we should cancel current composition because editor cannot
    1575                 :     // handle changing selection and deleting text.
    1576                 :     PRUint32 selOffset;
    1577               0 :     bool wasComposing = IsComposing();
    1578               0 :     bool editorHadCompositionString = EditorHasCompositionString();
    1579               0 :     if (wasComposing) {
    1580               0 :         selOffset = mCompositionStart;
    1581               0 :         if (editorHadCompositionString &&
    1582               0 :             !DispatchTextEvent(mSelectedString, false)) {
    1583               0 :             PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1584                 :                 ("    FAILED, quitting from DeletText"));
    1585               0 :             return NS_ERROR_FAILURE;
    1586                 :         }
    1587               0 :         if (!DispatchCompositionEnd()) {
    1588               0 :             PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1589                 :                 ("    FAILED, quitting from DeletText"));
    1590               0 :             return NS_ERROR_FAILURE;
    1591                 :         }
    1592                 :     } else {
    1593                 :         // Query cursor position & selection
    1594                 :         nsQueryContentEvent querySelectedTextEvent(true,
    1595                 :                                                    NS_QUERY_SELECTED_TEXT,
    1596               0 :                                                    mLastFocusedWindow);
    1597               0 :         lastFocusedWindow->DispatchEvent(&querySelectedTextEvent, status);
    1598               0 :         NS_ENSURE_TRUE(querySelectedTextEvent.mSucceeded, NS_ERROR_FAILURE);
    1599                 : 
    1600               0 :         selOffset = querySelectedTextEvent.mReply.mOffset;
    1601                 :     }
    1602                 : 
    1603                 :     // Set selection to delete
    1604                 :     nsSelectionEvent selectionEvent(true, NS_SELECTION_SET,
    1605               0 :                                     mLastFocusedWindow);
    1606               0 :     selectionEvent.mOffset = selOffset + aOffset;
    1607               0 :     selectionEvent.mLength = aNChars;
    1608               0 :     selectionEvent.mReversed = false;
    1609               0 :     selectionEvent.mExpandToClusterBoundary = false;
    1610               0 :     lastFocusedWindow->DispatchEvent(&selectionEvent, status);
    1611                 : 
    1612               0 :     if (!selectionEvent.mSucceeded ||
    1613               0 :         lastFocusedWindow != mLastFocusedWindow ||
    1614               0 :         lastFocusedWindow->Destroyed()) {
    1615               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1616                 :             ("    FAILED, setting selection caused focus change "
    1617                 :              "or window destroyed"));
    1618               0 :         return NS_ERROR_FAILURE;
    1619                 :     }
    1620                 : 
    1621                 :     // Delete the selection
    1622                 :     nsContentCommandEvent contentCommandEvent(true,
    1623                 :                                               NS_CONTENT_COMMAND_DELETE,
    1624               0 :                                               mLastFocusedWindow);
    1625               0 :     mLastFocusedWindow->DispatchEvent(&contentCommandEvent, status);
    1626                 : 
    1627               0 :     if (!contentCommandEvent.mSucceeded ||
    1628               0 :         lastFocusedWindow != mLastFocusedWindow ||
    1629               0 :         lastFocusedWindow->Destroyed()) {
    1630               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1631                 :             ("    FAILED, deleting the selection caused focus change "
    1632                 :              "or window destroyed"));
    1633               0 :         return NS_ERROR_FAILURE;
    1634                 :     }
    1635                 : 
    1636               0 :     if (!wasComposing) {
    1637               0 :         return NS_OK;
    1638                 :     }
    1639                 : 
    1640                 :     // Restore the composition at new caret position.
    1641               0 :     if (!DispatchCompositionStart()) {
    1642               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1643                 :             ("    FAILED, resterting composition start"));
    1644               0 :         return NS_ERROR_FAILURE;
    1645                 :     }
    1646                 : 
    1647               0 :     if (!editorHadCompositionString) {
    1648               0 :         return NS_OK;
    1649                 :     }
    1650                 : 
    1651               0 :     nsAutoString compositionString;
    1652               0 :     GetCompositionString(compositionString);
    1653               0 :     if (!DispatchTextEvent(compositionString, true)) {
    1654               0 :         PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1655                 :             ("    FAILED, restoring composition string"));
    1656               0 :         return NS_ERROR_FAILURE;
    1657                 :     }
    1658                 : 
    1659               0 :     return NS_OK;
    1660                 : }
    1661                 : 
    1662                 : void
    1663               0 : nsGtkIMModule::InitEvent(nsGUIEvent &aEvent)
    1664                 : {
    1665               0 :     aEvent.time = PR_Now() / 1000;
    1666               0 : }
    1667                 : 
    1668                 : bool
    1669               0 : nsGtkIMModule::ShouldIgnoreNativeCompositionEvent()
    1670                 : {
    1671               0 :     PR_LOG(gGtkIMLog, PR_LOG_ALWAYS,
    1672                 :         ("GtkIMModule(%p): ShouldIgnoreNativeCompositionEvent, mLastFocusedWindow=%p, mIgnoreNativeCompositionEvent=%s",
    1673                 :          this, mLastFocusedWindow,
    1674                 :          mIgnoreNativeCompositionEvent ? "YES" : "NO"));
    1675                 : 
    1676               0 :     if (!mLastFocusedWindow) {
    1677               0 :         return true; // cannot continue
    1678                 :     }
    1679                 : 
    1680               0 :     return mIgnoreNativeCompositionEvent;
    1681                 : }

Generated by: LCOV version 1.7