# Global Metrics

path: .metrics.nom.closures
old: 0.0
new: 2.0
path: .metrics.nom.functions
old: 20.0
new: 17.0
path: .metrics.nom.total
old: 20.0
new: 19.0
path: .metrics.cognitive.average
old: 0.0
new: 0.5789473684210527
path: .metrics.cognitive.sum
old: 0.0
new: 11.0
path: .metrics.mi.mi_sei
old: -14.269474730390726
new: -8.526717464579328
path: .metrics.mi.mi_visual_studio
old: 5.930942612387386
new: 14.106869054552885
path: .metrics.mi.mi_original
old: 10.14191186718243
new: 24.122746083285435
path: .metrics.nargs.average
old: 1.15
new: 0.42105263157894735
path: .metrics.nargs.sum
old: 23.0
new: 8.0
path: .metrics.cyclomatic.sum
old: 34.0
new: 38.0
path: .metrics.cyclomatic.average
old: 1.0303030303030305
new: 1.4074074074074074
path: .metrics.nexits.average
old: 0.3
new: 0.5789473684210527
path: .metrics.nexits.sum
old: 6.0
new: 11.0
path: .metrics.halstead.purity_ratio
old: 1.8127404273924628
new: 1.6786057373752006
path: .metrics.halstead.n2
old: 280.0
new: 197.0
path: .metrics.halstead.N2
old: 579.0
new: 390.0
path: .metrics.halstead.time
old: 10379.413172541495
new: 11255.96663236292
path: .metrics.halstead.volume
old: 10629.328942316191
new: 7580.941629317933
path: .metrics.halstead.vocabulary
old: 297.0
new: 224.0
path: .metrics.halstead.length
old: 1294.0
new: 971.0
path: .metrics.halstead.N1
old: 715.0
new: 581.0
path: .metrics.halstead.n1
old: 17.0
new: 27.0
path: .metrics.halstead.estimated_program_length
old: 2345.686113045847
new: 1629.92617099132
path: .metrics.halstead.difficulty
old: 17.576785714285716
new: 26.725888324873097
path: .metrics.halstead.effort
old: 186829.43710574697
new: 202607.39938253257
path: .metrics.halstead.level
old: 0.05689322361068779
new: 0.03741690408357075
path: .metrics.halstead.bugs
old: 1.0893705126318372
new: 1.1498705381538254
path: .metrics.loc.ploc
old: 249.0
new: 199.0
path: .metrics.loc.blank
old: 101.0
new: 44.0
path: .metrics.loc.sloc
old: 646.0
new: 287.0
path: .metrics.loc.lloc
old: 20.0
new: 33.0
path: .metrics.loc.cloc
old: 296.0
new: 44.0

# Spaces Data

## Minimal test - lines (21, 285)

path: .spaces[1].metrics.nom.functions
old: 0.0
new: 17.0
path: .spaces[1].metrics.nom.closures
old: 0.0
new: 2.0
path: .spaces[1].metrics.nom.total
old: 0.0
new: 19.0
path: .spaces[1].metrics.halstead.N1
old: 0.0
new: 580.0
path: .spaces[1].metrics.halstead.purity_ratio
old: null
new: 1.6148536670216989
path: .spaces[1].metrics.halstead.n1
old: 0.0
new: 27.0
path: .spaces[1].metrics.halstead.N2
old: 1.0
new: 379.0
path: .spaces[1].metrics.halstead.effort
old: 0.0
new: 202224.6805192158
path: .spaces[1].metrics.halstead.estimated_program_length
old: null
new: 1548.6446666738093
path: .spaces[1].metrics.halstead.n2
old: 1.0
new: 188.0
path: .spaces[1].metrics.halstead.bugs
old: 0.0
new: 1.1484220362343256
path: .spaces[1].metrics.halstead.difficulty
old: 0.0
new: 27.215425531914892
path: .spaces[1].metrics.halstead.volume
old: 0.0
new: 7430.516942756292
path: .spaces[1].metrics.halstead.time
old: 0.0
new: 11234.704473289768
path: .spaces[1].metrics.halstead.length
old: 1.0
new: 959.0
path: .spaces[1].metrics.halstead.vocabulary
old: 1.0
new: 215.0
path: .spaces[1].metrics.halstead.level
old: null
new: 0.03674386787843252
path: .spaces[1].metrics.nexits.average
old: null
new: 0.5789473684210527
path: .spaces[1].metrics.nexits.sum
old: 0.0
new: 11.0
path: .spaces[1].metrics.cyclomatic.average
old: 1.0
new: 1.44
path: .spaces[1].metrics.cyclomatic.sum
old: 1.0
new: 36.0
path: .spaces[1].metrics.loc.lloc
old: 0.0
new: 33.0
path: .spaces[1].metrics.loc.sloc
old: 1.0
new: 265.0
path: .spaces[1].metrics.loc.ploc
old: 1.0
new: 187.0
path: .spaces[1].metrics.loc.blank
old: 0.0
new: 40.0
path: .spaces[1].metrics.loc.cloc
old: 0.0
new: 38.0
path: .spaces[1].metrics.nargs.sum
old: 0.0
new: 8.0
path: .spaces[1].metrics.nargs.average
old: null
new: 0.42105263157894735
path: .spaces[1].metrics.cognitive.average
old: null
new: 0.5789473684210527
path: .spaces[1].metrics.cognitive.sum
old: 0.0
new: 11.0
path: .spaces[1].metrics.mi.mi_sei
old: null
new: -6.877175798631306
path: .spaces[1].metrics.mi.mi_original
old: null
new: 25.9789531251527
path: .spaces[1].metrics.mi.mi_visual_studio
old: null
new: 15.19237024862731

### Code

    namespace mozilla {

    class TextComposition;

    namespace dom {
    class BrowserChild;
    }

    namespace widget {

    class GeckoEditableSupport final
        : public TextEventDispatcherListener,
          public java::GeckoEditableChild::Natives {
      /*
          Rules for managing IME between Gecko and Java:

          * Gecko controls the text content, and Java shadows the Gecko text
             through text updates
          * Gecko and Java maintain separate selections, and synchronize when
             needed through selection updates and set-selection events
          * Java controls the composition, and Gecko shadows the Java
             composition through update composition events
      */

      using EditableBase = java::GeckoEditableChild::Natives;
      using EditableClient = java::SessionTextInput::EditableClient;
      using EditableListener = java::SessionTextInput::EditableListener;

      struct IMETextChange final {
        int32_t mStart, mOldEnd, mNewEnd;

        IMETextChange() : mStart(-1), mOldEnd(-1), mNewEnd(-1) {}

        explicit IMETextChange(const IMENotification& aIMENotification)
            : mStart(aIMENotification.mTextChangeData.mStartOffset),
              mOldEnd(aIMENotification.mTextChangeData.mRemovedEndOffset),
              mNewEnd(aIMENotification.mTextChangeData.mAddedEndOffset) {
          MOZ_ASSERT(aIMENotification.mMessage == NOTIFY_IME_OF_TEXT_CHANGE,
                     "IMETextChange initialized with wrong notification");
          MOZ_ASSERT(aIMENotification.mTextChangeData.IsValid(),
                     "The text change notification isn't initialized");
          MOZ_ASSERT(aIMENotification.mTextChangeData.IsInInt32Range(),
                     "The text change notification is out of range");
        }

        bool IsEmpty() const { return mStart < 0; }
      };

      enum FlushChangesFlag {
        // Not retrying.
        FLUSH_FLAG_NONE,
        // Retrying due to IME text changes during flush.
        FLUSH_FLAG_RETRY,
        // Retrying due to IME sync exceptions during flush.
        FLUSH_FLAG_RECOVER
      };

      enum RemoveCompositionFlag { CANCEL_IME_COMPOSITION, COMMIT_IME_COMPOSITION };

      const bool mIsRemote;
      jni::NativeWeakPtr mWindow;  // Parent only
      RefPtr mDispatcher;
      java::GeckoEditableChild::GlobalRef mEditable;
      bool mEditableAttached;
      InputContext mInputContext;
      AutoTArray, 4> mIMEKeyEvents;
      AutoTArray mIMETextChanges;
      RefPtr mIMERanges;
      RefPtr mDisposeRunnable;
      int32_t mIMEMaskEventsCount;         // Mask events when > 0.
      int32_t mIMEFocusCount;              // We are focused when > 0.
      bool mIMEDelaySynchronizeReply;      // We reply asynchronously when true.
      int32_t mIMEActiveSynchronizeCount;  // The number of replies being delayed.
      int32_t mIMEActiveCompositionCount;  // The number of compositions expected.
      uint32_t mDisposeBlockCount;
      bool mIMESelectionChanged;
      bool mIMETextChangedDuringFlush;
      bool mIMEMonitorCursor;

      nsIWidget* GetWidget() const;
      nsWindow* GetNsWindow() const;

      nsresult BeginInputTransaction(TextEventDispatcher* aDispatcher) {
        if (mIsRemote) {
          return aDispatcher->BeginInputTransaction(this);
        } else {
          return aDispatcher->BeginNativeInputTransaction();
        }
      }

      virtual ~GeckoEditableSupport() {}

      RefPtr GetComposition() const;
      bool RemoveComposition(RemoveCompositionFlag aFlag = COMMIT_IME_COMPOSITION);
      void SendIMEDummyKeyEvent(nsIWidget* aWidget, EventMessage msg);
      void AddIMETextChange(const IMETextChange& aChange);
      void PostFlushIMEChanges();
      void FlushIMEChanges(FlushChangesFlag aFlags = FLUSH_FLAG_NONE);
      void FlushIMEText(FlushChangesFlag aFlags = FLUSH_FLAG_NONE);
      void AsyncNotifyIME(int32_t aNotification);
      void UpdateCompositionRects();
      bool DoReplaceText(int32_t aStart, int32_t aEnd, jni::String::Param aText);
      bool DoUpdateComposition(int32_t aStart, int32_t aEnd, int32_t aFlags);
      void OnNotifyIMEOfCompositionEventHandled();
      void NotifyIMEContext(const InputContext& aContext,
                            const InputContextAction& aAction);

     public:
      template
      static void OnNativeCall(Functor&& aCall) {
        struct IMEEvent : nsAppShell::LambdaEvent {
          explicit IMEEvent(Functor&& l)
              : nsAppShell::LambdaEvent(std::move(l)) {}

          bool IsUIEvent() const override {
            using GES = GeckoEditableSupport;
            if (this->lambda.IsTarget(&GES::OnKeyEvent) ||
                this->lambda.IsTarget(&GES::OnImeReplaceText) ||
                this->lambda.IsTarget(&GES::OnImeUpdateComposition)) {
              return true;
            }
            return false;
          }

          void Run() override {
            if (NS_WARN_IF(!this->lambda.GetNativeObject())) {
              // Ignore stale calls after disposal.
              jni::GetGeckoThreadEnv()->ExceptionClear();
              return;
            }
            nsAppShell::LambdaEvent::Run();
          }
        };
        nsAppShell::PostEvent(mozilla::MakeUnique(std::move(aCall)));
      }

      static void SetOnBrowserChild(dom::BrowserChild* aBrowserChild);

      // Constructor for main process GeckoEditableChild.
      GeckoEditableSupport(jni::NativeWeakPtr aWindow,
                           java::GeckoEditableChild::Param aEditableChild)
          : mIsRemote(!aWindow.IsAttached()),
            mWindow(aWindow),
            mEditable(aEditableChild),
            mEditableAttached(!mIsRemote),
            mIMERanges(new TextRangeArray()),
            mIMEMaskEventsCount(1),  // Mask IME events since there's no focus yet
            mIMEFocusCount(0),
            mIMEDelaySynchronizeReply(false),
            mIMEActiveSynchronizeCount(0),
            mDisposeBlockCount(0),
            mIMESelectionChanged(false),
            mIMETextChangedDuringFlush(false),
            mIMEMonitorCursor(false) {}

      // Constructor for content process GeckoEditableChild.
      explicit GeckoEditableSupport(java::GeckoEditableChild::Param aEditableChild)
          : GeckoEditableSupport(nullptr, aEditableChild) {}

      NS_DECL_ISUPPORTS

      // TextEventDispatcherListener methods
      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;

      void SetInputContext(const InputContext& aContext,
                           const InputContextAction& aAction);

      InputContext GetInputContext();

      bool HasIMEFocus() const { return mIMEFocusCount != 0; }

      void AddBlocker() { mDisposeBlockCount++; }

      void ReleaseBlocker() {
        mDisposeBlockCount--;

        if (!mDisposeBlockCount && mDisposeRunnable) {
          if (HasIMEFocus()) {
            // If we have IME focus, GeckoEditableChild is already attached again.
            // So disposer is unnecessary.
            mDisposeRunnable = nullptr;
            return;
          }

          RefPtr self(this);
          RefPtr disposer = std::move(mDisposeRunnable);

          nsAppShell::PostEvent(
              [self = std::move(self), disposer = std::move(disposer)] {
                self->mEditableAttached = false;
                disposer->Run();
              });
        }
      }

      bool IsGeckoEditableUsed() const { return mDisposeBlockCount != 0; }

      // GeckoEditableChild methods
      using EditableBase::AttachNative;
      using EditableBase::DisposeNative;

      const java::GeckoEditableChild::Ref& GetJavaEditable() { return mEditable; }

      void OnWeakNonIntrusiveDetach(already_AddRefed aDisposer) {
        RefPtr self(this);
        nsAppShell::PostEvent(
            [self = std::move(self), disposer = RefPtr(aDisposer)] {
              if (self->IsGeckoEditableUsed()) {
                // Current calling stack uses GeckoEditableChild, so we should
                // not dispose it now.
                self->mDisposeRunnable = disposer;
                return;
              }
              self->mEditableAttached = false;
              disposer->Run();
            });
      }

      // Transfer to a new parent.
      void TransferParent(jni::Object::Param aEditableParent);

      // Handle an Android KeyEvent.
      void OnKeyEvent(int32_t aAction, int32_t aKeyCode, int32_t aScanCode,
                      int32_t aMetaState, int32_t aKeyPressMetaState, int64_t aTime,
                      int32_t aDomPrintableKeyValue, int32_t aRepeatCount,
                      int32_t aFlags, bool aIsSynthesizedImeKey,
                      jni::Object::Param originalEvent);

      // Synchronize Gecko thread with the InputConnection thread.
      void OnImeSynchronize();

      // Replace a range of text with new text.
      void OnImeReplaceText(int32_t aStart, int32_t aEnd, jni::String::Param aText);

      // Add styling for a range within the active composition.
      void OnImeAddCompositionRange(int32_t aStart, int32_t aEnd,
                                    int32_t aRangeType, int32_t aRangeStyle,
                                    int32_t aRangeLineStyle, bool aRangeBoldLine,
                                    int32_t aRangeForeColor,
                                    int32_t aRangeBackColor,
                                    int32_t aRangeLineColor);

      // Update styling for the active composition using previous-added ranges.
      void OnImeUpdateComposition(int32_t aStart, int32_t aEnd, int32_t aFlags);

      // Set cursor mode whether IME requests
      void OnImeRequestCursorUpdates(int aRequestMode);

      // Commit current composition to sync Gecko text state with Java.
      void OnImeRequestCommit();
    };

    }  // namespace widget
    }  // namespace mozilla