# Global Metrics

path: .metrics.nom.total
old: 3.0
new: 29.0
path: .metrics.nom.functions
old: 3.0
new: 29.0
path: .metrics.cognitive.sum
old: 14.0
new: 16.0
path: .metrics.cognitive.average
old: 4.666666666666667
new: 0.5517241379310345
path: .metrics.loc.ploc
old: 159.0
new: 308.0
path: .metrics.loc.cloc
old: 15.0
new: 294.0
path: .metrics.loc.sloc
old: 233.0
new: 685.0
path: .metrics.loc.lloc
old: 90.0
new: 75.0
path: .metrics.loc.blank
old: 59.0
new: 83.0
path: .metrics.nexits.average
old: 1.0
new: 1.206896551724138
path: .metrics.nexits.sum
old: 3.0
new: 35.0
path: .metrics.cyclomatic.sum
old: 13.0
new: 61.0
path: .metrics.cyclomatic.average
old: 3.25
new: 1.648648648648649
path: .metrics.halstead.bugs
old: 1.068337314420316
new: 1.8003971043661116
path: .metrics.halstead.volume
old: 5528.975085778734
new: 11100.136635150266
path: .metrics.halstead.n2
old: 123.0
new: 257.0
path: .metrics.halstead.length
old: 769.0
new: 1357.0
path: .metrics.halstead.time
old: 10080.265552730742
new: 22052.70336042986
path: .metrics.halstead.purity_ratio
old: 1.2457362927776825
new: 1.6388434156828708
path: .metrics.halstead.level
old: 0.030471943515421773
new: 0.027963658125238017
path: .metrics.halstead.N2
old: 351.0
new: 557.0
path: .metrics.halstead.effort
old: 181444.77994915337
new: 396948.66048773745
path: .metrics.halstead.N1
old: 418.0
new: 800.0
path: .metrics.halstead.difficulty
old: 32.81707317073171
new: 35.76070038910506
path: .metrics.halstead.estimated_program_length
old: 957.9712091460376
new: 2223.910515081656
path: .metrics.halstead.n1
old: 23.0
new: 33.0
path: .metrics.halstead.vocabulary
old: 146.0
new: 290.0
path: .metrics.mi.mi_original
old: 34.8908368030459
new: 2.756908797324101
path: .metrics.mi.mi_sei
old: -4.888910339677995
new: -23.040409942502116
path: .metrics.mi.mi_visual_studio
old: 20.4039981304362
new: 1.6122273668561995
path: .metrics.nargs.sum
old: 3.0
new: 8.0
path: .metrics.nargs.average
old: 1.0
new: 0.27586206896551724

# Spaces Data

## Minimal test - lines (25, 683)

path: .spaces[1].metrics.halstead.volume
old: 993.3186894003514
new: 10940.803400697148
path: .spaces[1].metrics.halstead.N1
old: 104.0
new: 799.0
path: .spaces[1].metrics.halstead.difficulty
old: 15.428571428571429
new: 36.32661290322581
path: .spaces[1].metrics.halstead.length
old: 176.0
new: 1345.0
path: .spaces[1].metrics.halstead.bugs
old: 0.20566242870448112
new: 1.8018895173657337
path: .spaces[1].metrics.halstead.n2
old: 35.0
new: 248.0
path: .spaces[1].metrics.halstead.effort
old: 15325.48835074828
new: 397442.32998742175
path: .spaces[1].metrics.halstead.level
old: 0.06481481481481481
new: 0.027528027528027528
path: .spaces[1].metrics.halstead.N2
old: 72.0
new: 546.0
path: .spaces[1].metrics.halstead.purity_ratio
old: 1.3530015029954636
new: 1.5904131530964865
path: .spaces[1].metrics.halstead.estimated_program_length
old: 238.1282645272016
new: 2139.105690914774
path: .spaces[1].metrics.halstead.n1
old: 15.0
new: 33.0
path: .spaces[1].metrics.halstead.vocabulary
old: 50.0
new: 281.0
path: .spaces[1].metrics.halstead.time
old: 851.4160194860156
new: 22080.129443745653
path: .spaces[1].metrics.nexits.average
old: 1.0
new: 1.206896551724138
path: .spaces[1].metrics.nexits.sum
old: 1.0
new: 35.0
path: .spaces[1].metrics.nargs.average
old: 0.0
new: 0.27586206896551724
path: .spaces[1].metrics.nargs.sum
old: 0.0
new: 8.0
path: .spaces[1].metrics.cyclomatic.sum
old: 5.0
new: 59.0
path: .spaces[1].metrics.cyclomatic.average
old: 5.0
new: 1.6857142857142855
path: .spaces[1].metrics.loc.cloc
old: 1.0
new: 286.0
path: .spaces[1].metrics.loc.blank
old: 13.0
new: 79.0
path: .spaces[1].metrics.loc.ploc
old: 40.0
new: 294.0
path: .spaces[1].metrics.loc.sloc
old: 54.0
new: 659.0
path: .spaces[1].metrics.loc.lloc
old: 26.0
new: 75.0
path: .spaces[1].metrics.nom.total
old: 1.0
new: 29.0
path: .spaces[1].metrics.nom.functions
old: 1.0
new: 29.0
path: .spaces[1].metrics.cognitive.sum
old: 9.0
new: 16.0
path: .spaces[1].metrics.cognitive.average
old: 9.0
new: 0.5517241379310345
path: .spaces[1].metrics.mi.mi_original
old: 69.34299039341577
new: 3.918955287644792
path: .spaces[1].metrics.mi.mi_visual_studio
old: 40.551456370418585
new: 2.291786717920931
path: .spaces[1].metrics.mi.mi_sei
old: 35.312053599694444
new: -21.41912039206099

### Code

    namespace mozilla {
    namespace widget {

    /**
     * KeyHandlingState is result of IMContextWrapper::OnKeyEvent().
     */
    enum class KeyHandlingState {
      // The native key event has not been handled by IMContextWrapper.
      eNotHandled,
      // The native key event was handled by IMContextWrapper.
      eHandled,
      // The native key event has not been handled by IMContextWrapper,
      // but eKeyDown or eKeyUp event has been dispatched.
      eNotHandledButEventDispatched,
      // The native key event has not been handled by IMContextWrapper,
      // but eKeyDown or eKeyUp event has been dispatched and consumed.
      eNotHandledButEventConsumed,
    };

    class IMContextWrapper final : public TextEventDispatcherListener {
     public:
      // TextEventDispatcherListener implementation
      NS_DECL_ISUPPORTS

      NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher,
                           const IMENotification& aNotification) override;
      NS_IMETHOD_(IMENotificationRequests) GetIMENotificationRequests() override;
      NS_IMETHOD_(void)
      OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher) override;
      NS_IMETHOD_(void)
      WillDispatchKeyboardEvent(TextEventDispatcher* aTextEventDispatcher,
                                WidgetKeyboardEvent& aKeyboardEvent,
                                uint32_t aIndexOfKeypress, void* aData) override;

     public:
      // aOwnerWindow is a pointer of the owner window.  When aOwnerWindow is
      // destroyed, the related IME contexts are released (i.e., IME cannot be
      // used with the instance after that).
      explicit IMContextWrapper(nsWindow* aOwnerWindow);

      // Called when the process is being shut down.
      static void Shutdown();

      // "Enabled" means the users can use all IMEs.
      // I.e., the focus is in the normal editors.
      bool IsEnabled() const;

      // OnFocusWindow is a notification that aWindow is going to be focused.
      void OnFocusWindow(nsWindow* aWindow);
      // OnBlurWindow is a notification that aWindow is going to be unfocused.
      void OnBlurWindow(nsWindow* aWindow);
      // OnDestroyWindow is a notification that aWindow is going to be destroyed.
      void OnDestroyWindow(nsWindow* aWindow);
      // OnFocusChangeInGecko is a notification that an editor gets focus.
      void OnFocusChangeInGecko(bool aFocus);
      // OnSelectionChange is a notification that selection (caret) is changed
      // in the focused editor.
      void OnSelectionChange(nsWindow* aCaller,
                             const IMENotification& aIMENotification);
      // OnThemeChanged is called when desktop theme is changed.
      static void OnThemeChanged();

      /**
       * OnKeyEvent() is called when aWindow gets a native key press event or a
       * native key release event.  If this returns true, the key event was
       * filtered by IME.  Otherwise, this returns false.
       * NOTE: When the native key press event starts composition, this returns
       *       true but dispatches an eKeyDown event or eKeyUp event before
       *       dispatching composition events or content command event.
       *
       * @param aWindow                       A window on which user operate the
       *                                      key.
       * @param aEvent                        A native key press or release
       *                                      event.
       * @param aKeyboardEventWasDispatched   true if eKeyDown or eKeyUp event
       *                                      for aEvent has already been
       *                                      dispatched.  In this case,
       *                                      this class doesn't dispatch
       *                                      keyboard event anymore.
       */
      KeyHandlingState OnKeyEvent(nsWindow* aWindow, GdkEventKey* aEvent,
                                  bool aKeyboardEventWasDispatched = false);

      // IME related nsIWidget methods.
      nsresult EndIMEComposition(nsWindow* aCaller);
      void SetInputContext(nsWindow* aCaller, const InputContext* aContext,
                           const InputContextAction* aAction);
      InputContext GetInputContext();
      void OnUpdateComposition();
      void OnLayoutChange();

      TextEventDispatcher* GetTextEventDispatcher();

      // TODO: Typically, new IM comes every several years.  And now, our code
      //       becomes really IM behavior dependent.  So, perhaps, we need prefs
      //       to control related flags for IM developers.
      enum class IMContextID : uint8_t {
        Fcitx,  // 4.x or earlier
        Fcitx5,
        IBus,
        IIIMF,
        Scim,
        Uim,
        Wayland,
        Unknown,
      };

      friend std::ostream& operator<<(std::ostream& aStream,
                                      const IMContextID& aIMContextID) {
        switch (aIMContextID) {
          case IMContextID::Fcitx:
            return aStream << "Fcitx";
          case IMContextID::Fcitx5:
            return aStream << "Fcitx5";
          case IMContextID::IBus:
            return aStream << "IBus";
          case IMContextID::IIIMF:
            return aStream << "IIIMF";
          case IMContextID::Scim:
            return aStream << "Scim";
          case IMContextID::Uim:
            return aStream << "Uim";
          case IMContextID::Wayland:
            return aStream << "Wayland";
          case IMContextID::Unknown:
            return aStream << "Unknown";
        }
        MOZ_ASSERT_UNREACHABLE("Add new case for the new IM support");
        return aStream << "Unknown";
      }

      /**
       * GetIMName() returns IM name associated with mContext.  If the context is
       * xim, this look for actual engine from XMODIFIERS environment variable.
       */
      nsDependentCSubstring GetIMName() const;

      /**
       * GetWaitingSynthesizedKeyPressHardwareKeyCode() returns hardware_keycode
       * value of last handled GDK_KEY_PRESS event which is probable handled by
       * IME asynchronously and we have not received synthesized GDK_KEY_PRESS
       * event yet.
       */
      static guint16 GetWaitingSynthesizedKeyPressHardwareKeyCode() {
        return sWaitingSynthesizedKeyPressHardwareKeyCode;
      }

     protected:
      ~IMContextWrapper();

      // Owner of an instance of this class. This should be top level window.
      // The owner window must release the contexts when it's destroyed because
      // the IME contexts need the native window.  If OnDestroyWindow() is called
      // with the owner window, it'll release IME contexts.  Otherwise, it'll
      // just clean up any existing composition if it's related to the destroying
      // child window.
      nsWindow* mOwnerWindow;

      // A last focused window in this class's context.
      nsWindow* mLastFocusedWindow;

      // Actual context. This is used for handling the user's input.
      GtkIMContext* mContext;

      // mSimpleContext is used for the password field and
      // the |ime-mode: disabled;| editors if sUseSimpleContext is true.
      // These editors disable IME.  But dead keys should work.  Fortunately,
      // the simple IM context of GTK2 support only them.
      GtkIMContext* mSimpleContext;

      // mDummyContext is a dummy context and will be used in Focus()
      // when the state of mEnabled means disabled.  This context's IME state is
      // always "closed", so it closes IME forcedly.
      GtkIMContext* mDummyContext;

      // mComposingContext is not nullptr while one of mContext, mSimpleContext
      // and mDummyContext has composition.
      // XXX: We don't assume that two or more context have composition same time.
      GtkIMContext* mComposingContext;

      // IME enabled state and other things defined in InputContext.
      // Use following helper methods if you don't need the detail of the status.
      InputContext mInputContext;

      // mCompositionStart is the start offset of the composition string in the
      // current content.  When  or <input> have focus, it means offset
      // from the first character of them.  When a HTML editor has focus, it
      // means offset from the first character of the root element of the editor.
      uint32_t mCompositionStart;

      // mDispatchedCompositionString is the latest composition string which
      // was dispatched by compositionupdate event.
      nsString mDispatchedCompositionString;

      // mSelectedStringRemovedByComposition is the selected string which was
      // removed by first compositionchange event.
      nsString mSelectedStringRemovedByComposition;

      // OnKeyEvent() temporarily sets mProcessingKeyEvent to the given native
      // event.
      GdkEventKey* mProcessingKeyEvent;

      /**
       * GdkEventKeyQueue stores *copy* of GdkEventKey instances.  However, this
       * must be safe to our usecase since it has |time| and the value should not
       * be same as older event.
       */
      class GdkEventKeyQueue final {
       public:
        ~GdkEventKeyQueue() { Clear(); }

        void Clear() {
          if (!mEvents.IsEmpty()) {
            RemoveEventsAt(0, mEvents.Length());
          }
        }

        /**
         * PutEvent() puts new event into the queue.
         */
        void PutEvent(const GdkEventKey* aEvent) {
          GdkEventKey* newEvent = reinterpret_cast<GdkEventKey*>(
              gdk_event_copy(reinterpret_cast<const GdkEvent*>(aEvent)));
          newEvent->state &= GDK_MODIFIER_MASK;
          mEvents.AppendElement(newEvent);
        }

        /**
         * RemoveEvent() removes oldest same event and its preceding events
         * from the queue.
         */
        void RemoveEvent(const GdkEventKey* aEvent) {
          size_t index = IndexOf(aEvent);
          if (NS_WARN_IF(index == GdkEventKeyQueue::NoIndex())) {
            return;
          }
          RemoveEventsAt(0, index + 1);
        }

        /**
         * FirstEvent() returns oldest event in the queue.
         */
        GdkEventKey* GetFirstEvent() const {
          if (mEvents.IsEmpty()) {
            return nullptr;
          }
          return mEvents[0];
        }

        bool IsEmpty() const { return mEvents.IsEmpty(); }

        static size_t NoIndex() { return nsTArray<GdkEventKey*>::NoIndex; }
        size_t Length() const { return mEvents.Length(); }
        size_t IndexOf(const GdkEventKey* aEvent) const {
          static_assert(!(GDK_MODIFIER_MASK & (1 << 24)),
                        "We assumes 25th bit is used by some IM, but used by GDK");
          static_assert(!(GDK_MODIFIER_MASK & (1 << 25)),
                        "We assumes 26th bit is used by some IM, but used by GDK");
          for (size_t i = 0; i < mEvents.Length(); i++) {
            GdkEventKey* event = mEvents[i];
            // It must be enough to compare only type, time, keyval and
            // part of state.   Note that we cannot compaire two events
            // simply since IME may have changed unused bits of state.
            if (event->time == aEvent->time) {
              if (NS_WARN_IF(event->type != aEvent->type) ||
                  NS_WARN_IF(event->keyval != aEvent->keyval) ||
                  NS_WARN_IF(event->state != (aEvent->state & GDK_MODIFIER_MASK))) {
                continue;
              }
            }
            return i;
          }
          return GdkEventKeyQueue::NoIndex();
        }

       private:
        nsTArray<GdkEventKey*> mEvents;

        void RemoveEventsAt(size_t aStart, size_t aCount) {
          for (size_t i = aStart; i < aStart + aCount; i++) {
            gdk_event_free(reinterpret_cast<GdkEvent*>(mEvents[i]));
          }
          mEvents.RemoveElementsAt(aStart, aCount);
        }
      };
      // OnKeyEvent() append mPostingKeyEvents when it believes that a key event
      // is posted to other IME process.
      GdkEventKeyQueue mPostingKeyEvents;

      static guint16 sWaitingSynthesizedKeyPressHardwareKeyCode;

      struct Range {
        uint32_t mOffset;
        uint32_t mLength;

        Range() : mOffset(UINT32_MAX), mLength(UINT32_MAX) {}

        bool IsValid() const { return mOffset != UINT32_MAX; }
        void Clear() {
          mOffset = UINT32_MAX;
          mLength = UINT32_MAX;
        }
      };

      // current target offset and length of IME composition
      Range mCompositionTargetRange;

      // mCompositionState indicates current status of composition.
      enum eCompositionState : uint8_t {
        eCompositionState_NotComposing,
        eCompositionState_CompositionStartDispatched,
        eCompositionState_CompositionChangeEventDispatched
      };
      eCompositionState mCompositionState;

      bool IsComposing() const {
        return (mCompositionState != eCompositionState_NotComposing);
      }

      bool IsComposingOn(GtkIMContext* aContext) const {
        return IsComposing() && mComposingContext == aContext;
      }

      bool IsComposingOnCurrentContext() const {
        return IsComposingOn(GetCurrentContext());
      }

      bool EditorHasCompositionString() {
        return (mCompositionState ==
                eCompositionState_CompositionChangeEventDispatched);
      }

      /**
       * Checks if aContext is valid context for handling composition.
       *
       * @param aContext          An IM context which is specified by native
       *                          composition events.
       * @return                  true if the context is valid context for
       *                          handling composition.  Otherwise, false.
       */
      bool IsValidContext(GtkIMContext* aContext) const;

      const char* GetCompositionStateName() {
        switch (mCompositionState) {
          case eCompositionState_NotComposing:
            return "NotComposing";
          case eCompositionState_CompositionStartDispatched:
            return "CompositionStartDispatched";
          case eCompositionState_CompositionChangeEventDispatched:
            return "CompositionChangeEventDispatched";
          default:
            return "InvaildState";
        }
      }

      // mIMContextID indicates the ID of mContext.  This is actually indicates
      // IM which user selected.
      IMContextID mIMContextID;

      struct Selection final {
        nsString mString;
        uint32_t mOffset;
        WritingMode mWritingMode;

        Selection() : mOffset(UINT32_MAX) {}

        void Clear() {
          mString.Truncate();
          mOffset = UINT32_MAX;
          mWritingMode = WritingMode();
        }
        void CollapseTo(uint32_t aOffset, const WritingMode& aWritingMode) {
          mWritingMode = aWritingMode;
          mOffset = aOffset;
          mString.Truncate();
        }

        void Assign(const IMENotification& aIMENotification);
        void Assign(const WidgetQueryContentEvent& aSelectedTextEvent);

        bool IsValid() const { return mOffset != UINT32_MAX; }
        bool Collapsed() const { return mString.IsEmpty(); }
        uint32_t Length() const { return mString.Length(); }
        uint32_t EndOffset() const {
          if (NS_WARN_IF(!IsValid())) {
            return UINT32_MAX;
          }
          CheckedInt<uint32_t> endOffset =
              CheckedInt<uint32_t>(mOffset) + mString.Length();
          if (NS_WARN_IF(!endOffset.isValid())) {
            return UINT32_MAX;
          }
          return endOffset.value();
        }
      } mSelection;
      bool EnsureToCacheSelection(nsAString* aSelectedString = nullptr);

      // mIsIMFocused is set to TRUE when we call gtk_im_context_focus_in(). And
      // it's set to FALSE when we call gtk_im_context_focus_out().
      bool mIsIMFocused;
      // mFallbackToKeyEvent is set to false when this class starts to handle
      // a native key event (at that time, mProcessingKeyEvent is set to the
      // native event).  If active IME just commits composition with a character
      // which is produced by the key with current keyboard layout, this is set
      // to true.
      bool mFallbackToKeyEvent;
      // mKeyboardEventWasDispatched is used by OnKeyEvent() and
      // MaybeDispatchKeyEventAsProcessedByIME().
      // MaybeDispatchKeyEventAsProcessedByIME() dispatches an eKeyDown or
      // eKeyUp event event if the composition is caused by a native
      // key press event.  If this is true, a keyboard event has been dispatched
      // for the native event.  If so, MaybeDispatchKeyEventAsProcessedByIME()
      // won't dispatch keyboard event anymore.
      bool mKeyboardEventWasDispatched;
      // Whether the keyboard event which as dispatched at setting
      // mKeyboardEventWasDispatched to true was consumed or not.
      bool mKeyboardEventWasConsumed;
      // mIsDeletingSurrounding is true while OnDeleteSurroundingNative() is
      // trying to delete the surrounding text.
      bool mIsDeletingSurrounding;
      // mLayoutChanged is true after OnLayoutChange() is called.  This is reset
      // when eCompositionChange is being dispatched.
      bool mLayoutChanged;
      // mSetCursorPositionOnKeyEvent true when caret rect or position is updated
      // with no composition.  If true, we update candidate window position
      // before key down
      bool mSetCursorPositionOnKeyEvent;
      // mPendingResettingIMContext becomes true if selection change notification
      // is received during composition but the selection change occurred before
      // starting the composition.  In such case, we cannot notify IME of
      // selection change during composition because we don't want to commit
      // the composition in such case.  However, we should notify IME of the
      // selection change after the composition is committed.
      bool mPendingResettingIMContext;
      // mRetrieveSurroundingSignalReceived is true after "retrieve_surrounding"
      // signal is received until selection is changed in Gecko.
      bool mRetrieveSurroundingSignalReceived;
      // mMaybeInDeadKeySequence is set to true when we detect a dead key press
      // and set to false when we're sure dead key sequence has been finished.
      // Note that we cannot detect which key event causes ending a dead key
      // sequence.  For example, when you press dead key grave with ibus Spanish
      // keyboard layout, it just consumes the key event when we call
      // gtk_im_context_filter_keypress().  Then, pressing "Escape" key cancels
      // the dead key sequence but we don't receive any signal and it's consumed
      // by gtk_im_context_filter_keypress() normally.  On the other hand, when
      // pressing "Shift" key causes exactly same behavior but dead key sequence
      // isn't finished yet.
      bool mMaybeInDeadKeySequence;
      // mIsIMInAsyncKeyHandlingMode is set to true if we know that IM handles
      // key events asynchronously.  I.e., filtered key event may come again
      // later.
      bool mIsIMInAsyncKeyHandlingMode;
      // mIsKeySnooped is set to true if IM uses key snooper to listen key events.
      // In such case, we won't receive key events if IME consumes the event.
      bool mIsKeySnooped;

      // sLastFocusedContext is a pointer to the last focused instance of this
      // class.  When a instance is destroyed and sLastFocusedContext refers it,
      // this is cleared.  So, this refers valid pointer always.
      static IMContextWrapper* sLastFocusedContext;

      // sUseSimpleContext indeicates if password editors and editors with
      // |ime-mode: disabled;| should use GtkIMContextSimple.
      // If true, they use GtkIMContextSimple.  Otherwise, not.
      static bool sUseSimpleContext;

      // Callback methods for native IME events.  These methods should call
      // the related instance methods simply.
      static gboolean OnRetrieveSurroundingCallback(GtkIMContext* aContext,
                                                    IMContextWrapper* aModule);
      static gboolean OnDeleteSurroundingCallback(GtkIMContext* aContext,
                                                  gint aOffset, gint aNChars,
                                                  IMContextWrapper* aModule);
      static void OnCommitCompositionCallback(GtkIMContext* aContext,
                                              const gchar* aString,
                                              IMContextWrapper* aModule);
      static void OnChangeCompositionCallback(GtkIMContext* aContext,
                                              IMContextWrapper* aModule);
      static void OnStartCompositionCallback(GtkIMContext* aContext,
                                             IMContextWrapper* aModule);
      static void OnEndCompositionCallback(GtkIMContext* aContext,
                                           IMContextWrapper* aModule);

      // The instance methods for the native IME events.
      gboolean OnRetrieveSurroundingNative(GtkIMContext* aContext);
      gboolean OnDeleteSurroundingNative(GtkIMContext* aContext, gint aOffset,
                                         gint aNChars);
      void OnCommitCompositionNative(GtkIMContext* aContext, const gchar* aString);
      void OnChangeCompositionNative(GtkIMContext* aContext);
      void OnStartCompositionNative(GtkIMContext* aContext);
      void OnEndCompositionNative(GtkIMContext* aContext);

      /**
       * GetCurrentContext() returns current IM context which is chosen with the
       * enabled state.
       * WARNING:
       *     When this class receives some signals for a composition after focus
       *     is moved in Gecko, the result of this may be different from given
       *     context by the signals.
       */
      GtkIMContext* GetCurrentContext() const;

      /**
       * GetActiveContext() returns a composing context or current context.
       */
      GtkIMContext* GetActiveContext() const {
        return mComposingContext ? mComposingContext : GetCurrentContext();
      }

      // If the owner window and IM context have been destroyed, returns TRUE.
      bool IsDestroyed() { return !mOwnerWindow; }

      // Sets focus to the instance of this class.
      void Focus();

      // Steals focus from the instance of this class.
      void Blur();

      // Initializes the instance.
      void Init();

      /**
       * Reset the active context, i.e., if there is mComposingContext, reset it.
       * Otherwise, reset current context.  Note that all native composition
       * events during calling this will be ignored.
       */
      void ResetIME();

      // Gets the current composition string by the native APIs.
      void GetCompositionString(GtkIMContext* aContext,
                                nsAString& aCompositionString);

      /**
       * Generates our text range array from current composition string.
       *
       * @param aContext              A GtkIMContext which is being handled.
       * @param aCompositionString    The data to be dispatched with
       *                              compositionchange event.
       */
      already_AddRefed<TextRangeArray> CreateTextRangeArray(
          GtkIMContext* aContext, const nsAString& aCompositionString);

      /**
       * SetTextRange() initializes aTextRange with aPangoAttrIter.
       *
       * @param aPangoAttrIter            An iter which represents a clause of the
       *                                  composition string.
       * @param aUTF8CompositionString    The whole composition string (UTF-8).
       * @param aUTF16CaretOffset         The caret offset in the composition
       *                                  string encoded as UTF-16.
       * @param aTextRange                The result.
       * @return                          true if this initializes aTextRange.
       *                                  Otherwise, false.
       */
      bool SetTextRange(PangoAttrIterator* aPangoAttrIter,
                        const gchar* aUTF8CompositionString,
                        uint32_t aUTF16CaretOffset, TextRange& aTextRange) const;

      /**
       * ToNscolor() converts the PangoColor in aPangoAttrColor to nscolor.
       */
      static nscolor ToNscolor(PangoAttrColor* aPangoAttrColor);

      /**
       * Move the candidate window with "fake" cursor position.
       *
       * @param aContext              A GtkIMContext which is being handled.
       */
      void SetCursorPosition(GtkIMContext* aContext);

      // Queries the current selection offset of the window.
      uint32_t GetSelectionOffset(nsWindow* aWindow);

      // Get current paragraph text content and cursor position
      nsresult GetCurrentParagraph(nsAString& aText, uint32_t& aCursorPos);

      /**
       * Delete text portion
       *
       * @param aContext              A GtkIMContext which is being handled.
       * @param aOffset               Start offset of the range to delete.
       * @param aNChars               Count of characters to delete.  It depends
       *                              on |g_utf8_strlen()| what is one character.
       */
      nsresult DeleteText(GtkIMContext* aContext, int32_t aOffset,
                          uint32_t aNChars);

      // Initializes the GUI event.
      void InitEvent(WidgetGUIEvent& aEvent);

      // Called before destroying the context to work around some platform bugs.
      void PrepareToDestroyContext(GtkIMContext* aContext);

      /**
       *  WARNING:
       *    Following methods dispatch gecko events.  Then, the focused widget
       *    can be destroyed, and also it can be stolen focus.  If they returns
       *    FALSE, callers cannot continue the composition.
       *      - MaybeDispatchKeyEventAsProcessedByIME
       *      - DispatchCompositionStart
       *      - DispatchCompositionChangeEvent
       *      - DispatchCompositionCommitEvent
       */

      /**
       * Dispatch an eKeyDown or eKeyUp event whose mKeyCode value is
       * NS_VK_PROCESSKEY and mKeyNameIndex is KEY_NAME_INDEX_Process if
       * we're not in a dead key sequence, mProcessingKeyEvent is nullptr
       * but mPostingKeyEvents is not empty or mProcessingKeyEvent is not
       * nullptr and mKeyboardEventWasDispatched is still false.  If this
       * dispatches a keyboard event, this sets mKeyboardEventWasDispatched
       * to true.
       *
       * @param aFollowingEvent       The following event message.
       * @return                      If the caller can continue to handle
       *                              composition, returns true.  Otherwise,
       *                              false.  For example, if focus is moved
       *                              by dispatched keyboard event, returns
       *                              false.
       */
      bool MaybeDispatchKeyEventAsProcessedByIME(EventMessage aFollowingEvent);

      /**
       * Dispatches a composition start event.
       *
       * @param aContext              A GtkIMContext which is being handled.
       * @return                      true if the focused widget is neither
       *                              destroyed nor changed.  Otherwise, false.
       */
      bool DispatchCompositionStart(GtkIMContext* aContext);

      /**
       * Dispatches a compositionchange event.
       *
       * @param aContext              A GtkIMContext which is being handled.
       * @param aCompositionString    New composition string.
       * @return                      true if the focused widget is neither
       *                              destroyed nor changed.  Otherwise, false.
       */
      bool DispatchCompositionChangeEvent(GtkIMContext* aContext,
                                          const nsAString& aCompositionString);

      /**
       * Dispatches a compositioncommit event or compositioncommitasis event.
       *
       * @param aContext              A GtkIMContext which is being handled.
       * @param aCommitString         If this is nullptr, the composition will
       *                              be committed with last dispatched data.
       *                              Otherwise, the composition will be
       *                              committed with this value.
       * @return                      true if the focused widget is neither
       *                              destroyed nor changed.  Otherwise, false.
       */
      bool DispatchCompositionCommitEvent(GtkIMContext* aContext,
                                          const nsAString* aCommitString = nullptr);
    };

    }  // namespace widget
    }  // namespace mozilla</i></pre>

    <h2>Minimal test - lines (23, 23)</h2>
    <b>path:</b> .spaces[0].metrics.halstead.bugs<br>
    <b>old:</b> 0.004556086527391188<br>
    <b>new:</b> 0.0<br><br>
    <b>path:</b> .spaces[0].metrics.halstead.length<br>
    <b>old:</b> 9.0<br>
    <b>new:</b> 1.0<br><br>
    <b>path:</b> .spaces[0].metrics.halstead.n1<br>
    <b>old:</b> 4.0<br>
    <b>new:</b> 0.0<br><br>
    <b>path:</b> .spaces[0].metrics.halstead.estimated_program_length<br>
    <b>old:</b> 12.754887502163468<br>
    <b>new:</b> null<br><br>
    <b>path:</b> .spaces[0].metrics.halstead.difficulty<br>
    <b>old:</b> 2.0<br>
    <b>new:</b> 0.0<br><br>
    <b>path:</b> .spaces[0].metrics.halstead.N1<br>
    <b>old:</b> 6.0<br>
    <b>new:</b> 0.0<br><br>
    <b>path:</b> .spaces[0].metrics.halstead.level<br>
    <b>old:</b> 0.5<br>
    <b>new:</b> null<br><br>
    <b>path:</b> .spaces[0].metrics.halstead.N2<br>
    <b>old:</b> 3.0<br>
    <b>new:</b> 1.0<br><br>
    <b>path:</b> .spaces[0].metrics.halstead.purity_ratio<br>
    <b>old:</b> 1.4172097224626077<br>
    <b>new:</b> null<br><br>
    <b>path:</b> .spaces[0].metrics.halstead.effort<br>
    <b>old:</b> 50.53238859703688<br>
    <b>new:</b> 0.0<br><br>
    <b>path:</b> .spaces[0].metrics.halstead.time<br>
    <b>old:</b> 2.807354922057604<br>
    <b>new:</b> 0.0<br><br>
    <b>path:</b> .spaces[0].metrics.halstead.n2<br>
    <b>old:</b> 3.0<br>
    <b>new:</b> 1.0<br><br>
    <b>path:</b> .spaces[0].metrics.halstead.vocabulary<br>
    <b>old:</b> 7.0<br>
    <b>new:</b> 1.0<br><br>
    <b>path:</b> .spaces[0].metrics.halstead.volume<br>
    <b>old:</b> 25.26619429851844<br>
    <b>new:</b> 0.0<br><br>
    <b>path:</b> .spaces[0].metrics.mi.mi_original<br>
    <b>old:</b> 127.90387581438756<br>
    <b>new:</b> null<br><br>
    <b>path:</b> .spaces[0].metrics.mi.mi_visual_studio<br>
    <b>old:</b> 74.79758819554827<br>
    <b>new:</b> null<br><br>
    <b>path:</b> .spaces[0].metrics.mi.mi_sei<br>
    <b>old:</b> 108.92725521528646<br>
    <b>new:</b> null<br><br>
    <b>path:</b> .spaces[0].metrics.nargs.sum<br>
    <b>old:</b> 1.0<br>
    <b>new:</b> 0.0<br><br>
    <b>path:</b> .spaces[0].metrics.nargs.average<br>
    <b>old:</b> 1.0<br>
    <b>new:</b> null<br><br>
    <b>path:</b> .spaces[0].metrics.nexits.average<br>
    <b>old:</b> 0.0<br>
    <b>new:</b> null<br><br>
    <b>path:</b> .spaces[0].metrics.nom.total<br>
    <b>old:</b> 1.0<br>
    <b>new:</b> 0.0<br><br>
    <b>path:</b> .spaces[0].metrics.nom.functions<br>
    <b>old:</b> 1.0<br>
    <b>new:</b> 0.0<br><br>
    <b>path:</b> .spaces[0].metrics.cognitive.average<br>
    <b>old:</b> 0.0<br>
    <b>new:</b> null<br><br>
    <b>path:</b> .spaces[0].metrics.loc.sloc<br>
    <b>old:</b> 5.0<br>
    <b>new:</b> 1.0<br><br>
    <b>path:</b> .spaces[0].metrics.loc.lloc<br>
    <b>old:</b> 1.0<br>
    <b>new:</b> 0.0<br><br>
    <b>path:</b> .spaces[0].metrics.loc.ploc<br>
    <b>old:</b> 5.0<br>
    <b>new:</b> 1.0<br><br>
    <h3>Code</h3>
    <pre><i>class nsWindow;</i></pre>

    </body>
    </html>