示例#1
0
void
TextComposition::SynthesizeCommit(bool aDiscard)
{
  // backup this instance and use it since this instance might be destroyed
  // by nsIMEStateManager if this is managed by it.
  TextComposition composition = *this;
  nsAutoString data(aDiscard ? EmptyString() : composition.mLastData);
  if (composition.mLastData != data) {
    composition.DispatchCompsotionEventRunnable(NS_COMPOSITION_UPDATE, data);
    composition.DispatchCompsotionEventRunnable(NS_TEXT_TEXT, data);
  }
  composition.DispatchCompsotionEventRunnable(NS_COMPOSITION_END, data);
}
示例#2
0
void
nsIMEStateManager::DispatchCompositionEvent(nsINode* aEventTargetNode,
                                            nsPresContext* aPresContext,
                                            nsEvent* aEvent,
                                            nsEventStatus* aStatus,
                                            nsDispatchingCallback* aCallBack)
{
  MOZ_ASSERT(aEvent->eventStructType == NS_COMPOSITION_EVENT ||
             aEvent->eventStructType == NS_TEXT_EVENT);
  if (!NS_IS_TRUSTED_EVENT(aEvent) ||
      (aEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH) != 0) {
    return;
  }

  EnsureTextCompositionArray();

  nsGUIEvent* GUIEvent = static_cast<nsGUIEvent*>(aEvent);

  TextComposition* composition =
    sTextCompositions->GetCompositionFor(GUIEvent->widget);
  if (!composition) {
    MOZ_ASSERT(GUIEvent->message == NS_COMPOSITION_START);
    TextComposition newComposition(aPresContext, aEventTargetNode, GUIEvent);
    composition = sTextCompositions->AppendElement(newComposition);
  }
#ifdef DEBUG
  else {
    MOZ_ASSERT(GUIEvent->message != NS_COMPOSITION_START);
  }
#endif // #ifdef DEBUG

  // Dispatch the event on composing target.
  composition->DispatchEvent(GUIEvent, aStatus, aCallBack);

  // WARNING: the |composition| might have been destroyed already.

  // Remove the ended composition from the array.
  if (aEvent->message == NS_COMPOSITION_END) {
    TextCompositionArray::index_type i =
      sTextCompositions->IndexOf(GUIEvent->widget);
    if (i != TextCompositionArray::NoIndex) {
      sTextCompositions->RemoveElementAt(i);
    }
  }
}
示例#3
0
nsresult
nsIMEStateManager::OnRemoveContent(nsPresContext* aPresContext,
                                   nsIContent* aContent)
{
  NS_ENSURE_ARG_POINTER(aPresContext);

  // First, if there is a composition in the aContent, clean up it.
  if (sTextCompositions) {
    TextComposition* compositionInContent =
      sTextCompositions->GetCompositionInContent(aPresContext, aContent);

    if (compositionInContent) {
      // Store the composition before accessing the native IME.
      TextComposition storedComposition = *compositionInContent;
      // Try resetting the native IME state.  Be aware, typically, this method
      // is called during the content being removed.  Then, the native
      // composition events which are caused by following APIs are ignored due
      // to unsafe to run script (in PresShell::HandleEvent()).
      nsCOMPtr<nsIWidget> widget = aPresContext->GetNearestWidget();
      if (widget) {
        nsresult rv =
          storedComposition.NotifyIME(REQUEST_TO_CANCEL_COMPOSITION);
        if (NS_FAILED(rv)) {
          storedComposition.NotifyIME(REQUEST_TO_COMMIT_COMPOSITION);
        }
        // By calling the APIs, the composition may have been finished normally.
        compositionInContent =
          sTextCompositions->GetCompositionFor(
                               storedComposition.GetPresContext(),
                               storedComposition.GetEventTargetNode());
      }
    }

    // If the compositionInContent is still available, we should finish the
    // composition just on the content forcibly.
    if (compositionInContent) {
      compositionInContent->SynthesizeCommit(true);
    }
  }

  if (!sPresContext || !sContent ||
      !nsContentUtils::ContentIsDescendantOf(sContent, aContent)) {
    return NS_OK;
  }

  // Current IME transaction should commit
  nsCOMPtr<nsIWidget> widget = sPresContext->GetNearestWidget();
  if (widget) {
    IMEState newState = GetNewIMEState(sPresContext, nullptr);
    InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
                              InputContextAction::LOST_FOCUS);
    SetIMEState(newState, nullptr, widget, action);
  }

  NS_IF_RELEASE(sContent);
  sPresContext = nullptr;

  return NS_OK;
}
示例#4
0
nsresult
nsTextEditRules::TruncateInsertionIfNeeded(Selection* aSelection,
                                           const nsAString  *aInString,
                                           nsAString  *aOutString,
                                           int32_t          aMaxLength,
                                           bool *aTruncated)
{
  if (!aSelection || !aInString || !aOutString) {return NS_ERROR_NULL_POINTER;}
  
  nsresult res = NS_OK;
  *aOutString = *aInString;
  if (aTruncated) {
    *aTruncated = false;
  }
  
  NS_ENSURE_STATE(mEditor);
  if ((-1 != aMaxLength) && IsPlaintextEditor() && !mEditor->IsIMEComposing() )
  {
    // Get the current text length.
    // Get the length of inString.
    // Get the length of the selection.
    //   If selection is collapsed, it is length 0.
    //   Subtract the length of the selection from the len(doc) 
    //   since we'll delete the selection on insert.
    //   This is resultingDocLength.
    // Get old length of IME composing string
    //   which will be replaced by new one.
    // If (resultingDocLength) is at or over max, cancel the insert
    // If (resultingDocLength) + (length of input) > max, 
    //    set aOutString to subset of inString so length = max
    int32_t docLength;
    res = mEditor->GetTextLength(&docLength);
    if (NS_FAILED(res)) { return res; }

    int32_t start, end;
    nsContentUtils::GetSelectionInTextControl(aSelection, mEditor->GetRoot(),
                                              start, end);

    TextComposition* composition = mEditor->GetComposition();
    int32_t oldCompStrLength = composition ? composition->String().Length() : 0;

    const int32_t selectionLength = end - start;
    const int32_t resultingDocLength = docLength - selectionLength - oldCompStrLength;
    if (resultingDocLength >= aMaxLength)
    {
      aOutString->Truncate();
      if (aTruncated) {
        *aTruncated = true;
      }
    }
    else
    {
      int32_t inCount = aOutString->Length();
      if (inCount + resultingDocLength > aMaxLength)
      {
        aOutString->Truncate(aMaxLength - resultingDocLength);
        if (aTruncated) {
          *aTruncated = true;
        }
      }
    }
  }
  return res;
}
示例#5
0
nsresult
TextEditRules::TruncateInsertionIfNeeded(Selection* aSelection,
                                         const nsAString* aInString,
                                         nsAString* aOutString,
                                         int32_t aMaxLength,
                                         bool* aTruncated)
{
  if (!aSelection || !aInString || !aOutString) {
    return NS_ERROR_NULL_POINTER;
  }

  if (!aOutString->Assign(*aInString, mozilla::fallible)) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  if (aTruncated) {
    *aTruncated = false;
  }

  NS_ENSURE_STATE(mTextEditor);
  if (-1 != aMaxLength && IsPlaintextEditor() &&
      !mTextEditor->IsIMEComposing()) {
    // Get the current text length.
    // Get the length of inString.
    // Get the length of the selection.
    //   If selection is collapsed, it is length 0.
    //   Subtract the length of the selection from the len(doc)
    //   since we'll delete the selection on insert.
    //   This is resultingDocLength.
    // Get old length of IME composing string
    //   which will be replaced by new one.
    // If (resultingDocLength) is at or over max, cancel the insert
    // If (resultingDocLength) + (length of input) > max,
    //    set aOutString to subset of inString so length = max
    int32_t docLength;
    nsresult rv = mTextEditor->GetTextLength(&docLength);
    if (NS_FAILED(rv)) {
      return rv;
    }

    uint32_t start, end;
    nsContentUtils::GetSelectionInTextControl(aSelection,
                                              mTextEditor->GetRoot(),
                                              start, end);

    TextComposition* composition = mTextEditor->GetComposition();
    uint32_t oldCompStrLength = composition ? composition->String().Length() : 0;

    const uint32_t selectionLength = end - start;
    const int32_t resultingDocLength = docLength - selectionLength - oldCompStrLength;
    if (resultingDocLength >= aMaxLength) {
      // This call is guaranteed to reduce the capacity of the string, so it
      // cannot cause an OOM.
      aOutString->Truncate();
      if (aTruncated) {
        *aTruncated = true;
      }
    } else {
      int32_t oldLength = aOutString->Length();
      if (oldLength + resultingDocLength > aMaxLength) {
        int32_t newLength = aMaxLength - resultingDocLength;
        MOZ_ASSERT(newLength > 0);
        char16_t newLastChar = aOutString->CharAt(newLength - 1);
        char16_t removingFirstChar = aOutString->CharAt(newLength);
        // Don't separate the string between a surrogate pair.
        if (NS_IS_HIGH_SURROGATE(newLastChar) &&
            NS_IS_LOW_SURROGATE(removingFirstChar)) {
          newLength--;
        }
        // XXX What should we do if we're removing IVS and its preceding
        //     character won't be removed?
        // This call is guaranteed to reduce the capacity of the string, so it
        // cannot cause an OOM.
        aOutString->Truncate(newLength);
        if (aTruncated) {
          *aTruncated = true;
        }
      }
    }
  }
  return NS_OK;
}
示例#6
0
// static
nsresult
nsIMEStateManager::NotifyIME(NotificationToIME aNotification,
                             nsIWidget* aWidget)
{
  NS_ENSURE_TRUE(aWidget, NS_ERROR_INVALID_ARG);

  TextComposition* composition = nullptr;
  if (sTextCompositions) {
    composition = sTextCompositions->GetCompositionFor(aWidget);
  }
  if (!composition || !composition->IsSynthesizedForTests()) {
    switch (aNotification) {
      case NOTIFY_IME_OF_CURSOR_POS_CHANGED:
        return aWidget->ResetInputState();
      case REQUEST_TO_COMMIT_COMPOSITION:
        return composition ? aWidget->ResetInputState() : NS_OK;
      case REQUEST_TO_CANCEL_COMPOSITION:
        return composition ? aWidget->CancelIMEComposition() : NS_OK;
      default:
        MOZ_NOT_REACHED("Unsupported notification");
        return NS_ERROR_INVALID_ARG;
    }
    MOZ_NOT_REACHED(
      "Failed to handle the notification for non-synthesized composition");
  }

  // If the composition is synthesized events for automated tests, we should
  // dispatch composition events for emulating the native composition behavior.
  // NOTE: The dispatched events are discarded if it's not safe to run script.
  switch (aNotification) {
    case REQUEST_TO_COMMIT_COMPOSITION: {
      nsCOMPtr<nsIWidget> widget(aWidget);
      TextComposition backup = *composition;

      nsEventStatus status = nsEventStatus_eIgnore;
      if (!backup.GetLastData().IsEmpty()) {
        nsTextEvent textEvent(true, NS_TEXT_TEXT, widget);
        textEvent.theText = backup.GetLastData();
        textEvent.flags |= NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT;
        widget->DispatchEvent(&textEvent, status);
        if (widget->Destroyed()) {
          return NS_OK;
        }
      }

      status = nsEventStatus_eIgnore;
      nsCompositionEvent endEvent(true, NS_COMPOSITION_END, widget);
      endEvent.data = backup.GetLastData();
      endEvent.flags |= NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT;
      widget->DispatchEvent(&endEvent, status);

      return NS_OK;
    }
    case REQUEST_TO_CANCEL_COMPOSITION: {
      nsCOMPtr<nsIWidget> widget(aWidget);
      TextComposition backup = *composition;

      nsEventStatus status = nsEventStatus_eIgnore;
      if (!backup.GetLastData().IsEmpty()) {
        nsCompositionEvent updateEvent(true, NS_COMPOSITION_UPDATE, widget);
        updateEvent.data = backup.GetLastData();
        updateEvent.flags |= NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT;
        widget->DispatchEvent(&updateEvent, status);
        if (widget->Destroyed()) {
          return NS_OK;
        }

        status = nsEventStatus_eIgnore;
        nsTextEvent textEvent(true, NS_TEXT_TEXT, widget);
        textEvent.theText = backup.GetLastData();
        textEvent.flags |= NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT;
        widget->DispatchEvent(&textEvent, status);
        if (widget->Destroyed()) {
          return NS_OK;
        }
      }

      status = nsEventStatus_eIgnore;
      nsCompositionEvent endEvent(true, NS_COMPOSITION_END, widget);
      endEvent.data = backup.GetLastData();
      endEvent.flags |= NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT;
      widget->DispatchEvent(&endEvent, status);

      return NS_OK;
    }
    default:
      return NS_OK;
  }
}