private void icUpdateGecko(boolean force) {

    // Skip if receiving a repeated request, or
    // if suppressing compositions during text selection.
    if ((!force && mIcUpdateSeqno == mLastIcUpdateSeqno) || mSuppressCompositions) {
      if (DEBUG) {
        Log.d(LOGTAG, "icUpdateGecko() skipped");
      }
      return;
    }
    mLastIcUpdateSeqno = mIcUpdateSeqno;
    mActionQueue.syncWithGecko();

    if (DEBUG) {
      Log.d(LOGTAG, "icUpdateGecko()");
    }

    final int selStart = mText.getSpanStart(Selection.SELECTION_START);
    final int selEnd = mText.getSpanEnd(Selection.SELECTION_END);
    int composingStart = mText.length();
    int composingEnd = 0;
    Object[] spans = mText.getSpans(0, composingStart, Object.class);

    for (Object span : spans) {
      if ((mText.getSpanFlags(span) & Spanned.SPAN_COMPOSING) != 0) {
        composingStart = Math.min(composingStart, mText.getSpanStart(span));
        composingEnd = Math.max(composingEnd, mText.getSpanEnd(span));
      }
    }
    if (DEBUG) {
      Log.d(LOGTAG, " range = " + composingStart + "-" + composingEnd);
      Log.d(LOGTAG, " selection = " + selStart + "-" + selEnd);
    }
    if (composingStart >= composingEnd) {
      if (selStart >= 0 && selEnd >= 0) {
        onImeSetSelection(selStart, selEnd);
      } else {
        onImeRemoveComposition();
      }
      return;
    }

    if (selEnd >= composingStart && selEnd <= composingEnd) {
      onImeAddCompositionRange(
          selEnd - composingStart,
          selEnd - composingStart,
          IME_RANGE_CARETPOSITION,
          0,
          0,
          false,
          0,
          0,
          0);
    }
    int rangeStart = composingStart;
    TextPaint tp = new TextPaint();
    TextPaint emptyTp = new TextPaint();
    // set initial foreground color to 0, because we check for tp.getColor() == 0
    // below to decide whether to pass a foreground color to Gecko
    emptyTp.setColor(0);
    do {
      int rangeType, rangeStyles = 0, rangeLineStyle = IME_RANGE_LINE_NONE;
      boolean rangeBoldLine = false;
      int rangeForeColor = 0, rangeBackColor = 0, rangeLineColor = 0;
      int rangeEnd = mText.nextSpanTransition(rangeStart, composingEnd, Object.class);

      if (selStart > rangeStart && selStart < rangeEnd) {
        rangeEnd = selStart;
      } else if (selEnd > rangeStart && selEnd < rangeEnd) {
        rangeEnd = selEnd;
      }
      CharacterStyle[] styleSpans = mText.getSpans(rangeStart, rangeEnd, CharacterStyle.class);

      if (DEBUG) {
        Log.d(LOGTAG, " found " + styleSpans.length + " spans @ " + rangeStart + "-" + rangeEnd);
      }

      if (styleSpans.length == 0) {
        rangeType =
            (selStart == rangeStart && selEnd == rangeEnd)
                ? IME_RANGE_SELECTEDRAWTEXT
                : IME_RANGE_RAWINPUT;
      } else {
        rangeType =
            (selStart == rangeStart && selEnd == rangeEnd)
                ? IME_RANGE_SELECTEDCONVERTEDTEXT
                : IME_RANGE_CONVERTEDTEXT;
        tp.set(emptyTp);
        for (CharacterStyle span : styleSpans) {
          span.updateDrawState(tp);
        }
        int tpUnderlineColor = 0;
        float tpUnderlineThickness = 0.0f;

        // These TextPaint fields only exist on Android ICS+ and are not in the SDK.
        if (Versions.feature14Plus) {
          tpUnderlineColor = (Integer) getField(tp, "underlineColor", 0);
          tpUnderlineThickness = (Float) getField(tp, "underlineThickness", 0.0f);
        }
        if (tpUnderlineColor != 0) {
          rangeStyles |= IME_RANGE_UNDERLINE | IME_RANGE_LINECOLOR;
          rangeLineColor = tpUnderlineColor;
          // Approximately translate underline thickness to what Gecko understands
          if (tpUnderlineThickness <= 0.5f) {
            rangeLineStyle = IME_RANGE_LINE_DOTTED;
          } else {
            rangeLineStyle = IME_RANGE_LINE_SOLID;
            if (tpUnderlineThickness >= 2.0f) {
              rangeBoldLine = true;
            }
          }
        } else if (tp.isUnderlineText()) {
          rangeStyles |= IME_RANGE_UNDERLINE;
          rangeLineStyle = IME_RANGE_LINE_SOLID;
        }
        if (tp.getColor() != 0) {
          rangeStyles |= IME_RANGE_FORECOLOR;
          rangeForeColor = tp.getColor();
        }
        if (tp.bgColor != 0) {
          rangeStyles |= IME_RANGE_BACKCOLOR;
          rangeBackColor = tp.bgColor;
        }
      }
      onImeAddCompositionRange(
          rangeStart - composingStart,
          rangeEnd - composingStart,
          rangeType,
          rangeStyles,
          rangeLineStyle,
          rangeBoldLine,
          rangeForeColor,
          rangeBackColor,
          rangeLineColor);
      rangeStart = rangeEnd;

      if (DEBUG) {
        Log.d(
            LOGTAG,
            " added "
                + rangeType
                + " : "
                + Integer.toHexString(rangeStyles)
                + " : "
                + Integer.toHexString(rangeForeColor)
                + " : "
                + Integer.toHexString(rangeBackColor));
      }
    } while (rangeStart < composingEnd);

    onImeUpdateComposition(composingStart, composingEnd);
  }