chromium/chrome/browser/ash/input_method/input_method_engine.h

// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_ASH_INPUT_METHOD_INPUT_METHOD_ENGINE_H_
#define CHROME_BROWSER_ASH_INPUT_METHOD_INPUT_METHOD_ENGINE_H_

#include <stddef.h>
#include <stdint.h>

#include <map>
#include <string>
#include <string_view>
#include <vector>

#include "base/containers/span.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "base/types/expected.h"
#include "base/values.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/ash/input_method/assistive_window_properties.h"
#include "chrome/browser/ash/input_method/input_method_engine_observer.h"
#include "chrome/browser/ash/input_method/screen_projection_change_monitor.h"
#include "chrome/browser/ash/input_method/suggestion_handler_interface.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_observer.h"
#include "components/prefs/pref_change_registrar.h"
#include "extensions/common/extension_id.h"
#include "ui/base/ime/ash/input_method_descriptor.h"
#include "ui/base/ime/ash/input_method_manager.h"
#include "ui/base/ime/ash/text_input_method.h"
#include "ui/base/ime/candidate_window.h"
#include "ui/base/ime/composition_text.h"
#include "ui/events/event.h"

static_assert(BUILDFLAG(IS_CHROMEOS_ASH), "For ChromeOS ash-chrome only");

namespace ui {
struct CompositionText;
class KeyEvent;

namespace ime {
struct AssistiveWindowButton;
struct InputMethodMenuItem;
struct SuggestionDetails;
}  // namespace ime
}  // namespace ui

namespace ash {

namespace ime {
struct AssistiveWindow;
}  // namespace ime

namespace input_method {

struct AssistiveWindowProperties;

class InputMethodEngine : virtual public TextInputMethod,
                          public ProfileObserver,
                          public SuggestionHandlerInterface {
 public:
  enum {
    MENU_ITEM_MODIFIED_LABEL = 0x0001,
    MENU_ITEM_MODIFIED_CHECKED = 0x0010,
  };

  enum SegmentStyle {
    SEGMENT_STYLE_UNDERLINE,
    SEGMENT_STYLE_DOUBLE_UNDERLINE,
    SEGMENT_STYLE_NO_UNDERLINE,
  };

  struct SegmentInfo {
    int start;
    int end;
    SegmentStyle style;
  };

  struct UsageEntry {
    std::string title;
    std::string body;
  };

  struct Candidate {
    Candidate();
    Candidate(const Candidate& other);
    virtual ~Candidate();

    std::string value;
    int id;
    std::string label;
    std::string annotation;
    UsageEntry usage;
    std::vector<Candidate> candidates;
  };

  struct CandidateWindowProperty {
    CandidateWindowProperty();
    virtual ~CandidateWindowProperty();
    CandidateWindowProperty(const CandidateWindowProperty& other);
    int page_size;
    bool is_cursor_visible;
    bool is_vertical;
    bool show_window_at_composition;

    // Auxiliary text is typically displayed in the footer of the candidate
    // window.
    std::string auxiliary_text;
    bool is_auxiliary_text_visible;

    // The index of the current chosen candidate out of total candidates.
    // value is -1 if there is no current chosen candidate.
    int current_candidate_index = -1;
    int total_candidates = 0;
  };

  enum class Error {
    kInputMethodNotActive,
    kIncorrectContextId,
  };

  InputMethodEngine();
  InputMethodEngine(const InputMethodEngine&) = delete;
  InputMethodEngine& operator=(const InputMethodEngine&) = delete;
  ~InputMethodEngine() override;

  virtual void Initialize(std::unique_ptr<InputMethodEngineObserver> observer,
                          const char* extension_id,
                          Profile* profile);

  // Returns true if this IME is active, false if not.
  bool IsActive() const;

  // Returns the current active input_component id.
  const std::string& GetActiveComponentId() const;

  // Clear the current composition.
  bool ClearComposition(int context_id, std::string* error);

  // Commit the specified text to the specified context.  Fails if the context
  // is not focused.
  bool CommitText(int context_id,
                  const std::u16string& text,
                  std::string* error);

  // Notifies InputContextHandler to commit any composition text.
  // Set |reset_engine| to false if the event was from the extension.
  void ConfirmComposition(bool reset_engine);

  // Deletes |number_of_chars| unicode characters as the basis of |offset| from
  // the surrounding text. The |offset| is relative position based on current
  // caret.
  // NOTE: Currently we are falling back to backspace forwarding workaround,
  // because delete_surrounding_text is not supported in Chrome. So this
  // function is restricted for only preceding text.
  // TODO(nona): Support full spec delete surrounding text.
  bool DeleteSurroundingText(int context_id,
                             int offset,
                             size_t number_of_chars,
                             std::string* error);

  // Deletes any active composition, and the current selection plus the
  // specified number of char16 values before and after the selection, and
  // replaces it with |replacement_string|.
  // Places the cursor at the end of |replacement_string|.
  base::expected<void, Error> ReplaceSurroundingText(
      int context_id,
      int length_before_selection,
      int length_after_selection,
      std::u16string_view replacement_text);

  // Commit the text currently being composed to the composition.
  // Fails if the context is not focused.
  bool FinishComposingText(int context_id, std::string* error);

  // Send the sequence of key events.
  bool SendKeyEvents(int context_id,
                     const std::vector<ui::KeyEvent>& events,
                     std::string* error);

  // Set the current composition and associated properties.
  // Note: The cursor is used to index into a UTF16 version
  // of the input text. Ideally, we should check the
  // UTF16 version of the input text and make sure the
  // selection start and end falls within that range.
  bool SetComposition(int context_id,
                      const char* text,
                      int selection_start,
                      int selection_end,
                      int cursor,
                      const std::vector<SegmentInfo>& segments,
                      std::string* error);

  // Set the current composition range around the current cursor.
  // This function is deprecated. Use |SetComposingRange| instead.
  bool SetCompositionRange(int context_id,
                           int selection_before,
                           int selection_after,
                           const std::vector<SegmentInfo>& segments,
                           std::string* error);

  // Set the current composition range.
  bool SetComposingRange(int context_id,
                         int start,
                         int end,
                         const std::vector<SegmentInfo>& segments,
                         std::string* error);

  gfx::Rect GetTextFieldBounds(int context_id, std::string* error);

  bool SetAutocorrectRange(int context_id,
                           const gfx::Range& range,
                           std::string* error);

  // Called when a key event is handled.
  void KeyEventHandled(const std::string& extension_id,
                       const std::string& request_id,
                       bool handled);

  // Returns the request ID for this key event.
  std::string AddPendingKeyEvent(
      const std::string& component_id,
      TextInputMethod::KeyEventDoneCallback callback);

  // Resolves all the pending key event callbacks as not handled.
  void CancelPendingKeyEvents();

  int GetContextIdForTesting() const { return context_id_; }

  PrefChangeRegistrar* GetPrefChangeRegistrarForTesting() const {
    return pref_change_registrar_.get();
  }

  // TextInputMethod overrides.
  void Focus(const TextInputMethod::InputContext& input_context) override;
  void Blur() override;
  void Enable(const std::string& component_id) override;
  void Disable() override;
  void Reset() override;
  void ProcessKeyEvent(const ui::KeyEvent& key_event,
                       KeyEventDoneCallback callback) override;
  void SetSurroundingText(const std::u16string& text,
                          gfx::Range selection_range,
                          uint32_t offset_pos) override;
  void SetCaretBounds(const gfx::Rect& caret_bounds) override;
  void PropertyActivate(const std::string& property_name) override;
  void CandidateClicked(uint32_t index) override;
  void AssistiveWindowButtonClicked(
      const ui::ime::AssistiveWindowButton& button) override;
  void AssistiveWindowChanged(const ash::ime::AssistiveWindow& window) override;
  ui::VirtualKeyboardController* GetVirtualKeyboardController() const override;
  bool IsReadyForTesting() override;

  // SuggestionHandlerInterface overrides.
  bool DismissSuggestion(int context_id, std::string* error) override;
  bool SetSuggestion(int context_id,
                     const ui::ime::SuggestionDetails& details,
                     std::string* error) override;
  bool AcceptSuggestion(int context_id, std::string* error) override;
  void OnSuggestionsChanged(
      const std::vector<std::string>& suggestions) override;
  bool SetButtonHighlighted(int context_id,
                            const ui::ime::AssistiveWindowButton& button,
                            bool highlighted,
                            std::string* error) override;
  void ClickButton(const ui::ime::AssistiveWindowButton& button) override;
  bool AcceptSuggestionCandidate(int context_id,
                                 const std::u16string& candidate,
                                 size_t delete_previous_utf16_len,
                                 bool use_replace_surrounding_text,
                                 std::string* error) override;
  bool SetAssistiveWindowProperties(
      int context_id,
      const AssistiveWindowProperties& assistive_window,
      std::string* error) override;
  void Announce(const std::u16string& message) override;

  // ProfileObserver overrides.
  void OnProfileWillBeDestroyed(Profile* profile) override;

  // This function returns the current property of the candidate window of the
  // corresponding engine_id. If the CandidateWindowProperty is not set for the
  // engine_id, a default value is set. The caller can use the returned value as
  // the default property and modify some of specified items.
  const CandidateWindowProperty& GetCandidateWindowProperty(
      const std::string& engine_id);

  // Changes the property of the candidate window of the given engine_id and
  // repaints the candidate window widget.
  void SetCandidateWindowProperty(const std::string& engine_id,
                                  const CandidateWindowProperty& property);

  // Show or hide the candidate window.
  bool SetCandidateWindowVisible(bool visible, std::string* error);

  // Set the list of entries displayed in the candidate window.
  bool SetCandidates(int context_id,
                     const std::vector<Candidate>& candidates,
                     std::string* error);

  // Set the position of the cursor in the candidate window.
  bool SetCursorPosition(int context_id, int candidate_id, std::string* error);

  // Update the state of the menu items.
  virtual bool UpdateMenuItems(
      const std::vector<InputMethodManager::MenuItem>& items,
      std::string* error);

  // Hides the input view window (from API call).
  void HideInputView();

  void NotifyInputMethodExtensionReadyForTesting();

 protected:
  virtual void OnInputMethodOptionsChanged();

  // The observer object receiving events for this IME.
  std::unique_ptr<InputMethodEngineObserver> observer_;

 private:
  struct PendingKeyEvent {
    PendingKeyEvent(const std::string& component_id,
                    TextInputMethod::KeyEventDoneCallback callback);
    PendingKeyEvent(PendingKeyEvent&& other);

    PendingKeyEvent(const PendingKeyEvent&) = delete;
    PendingKeyEvent& operator=(const PendingKeyEvent&) = delete;

    ~PendingKeyEvent();

    std::string component_id;
    TextInputMethod::KeyEventDoneCallback callback;
  };

  // Called when Diacritics setting changed for metrics.
  void DiacriticsSettingsChanged();

  // Notifies InputContextHandler that the composition is changed.
  void UpdateComposition(const ui::CompositionText& composition_text,
                         uint32_t cursor_pos,
                         bool is_visible);

  // Notifies InputContextHandler to commit |text|.
  void CommitTextToInputContext(int context_id, const std::u16string& text);

  // Converts MenuItem to InputMethodMenuItem.
  void MenuItemToProperty(const InputMethodManager::MenuItem& item,
                          ui::ime::InputMethodMenuItem* property);

  void OnScreenProjectionChanged(bool is_projected);

  // Infers if the user is choosing from a candidate from the window.
  // TODO(b/300576550): get this information from IME.
  bool InferIsUserSelecting(base::span<const Candidate> candidates);

  // The current candidate window.
  ui::CandidateWindow candidate_window_;

  // The candidate window property of the current engine_id.
  std::pair<std::string, CandidateWindowProperty> candidate_window_property_;

  // Indicates whether the candidate window is visible.
  bool window_visible_ = false;

  // Mapping of candidate index to candidate id.
  std::vector<int> candidate_ids_;

  // Mapping of candidate id to index.
  std::map<int, int> candidate_indexes_;

  ui::TextInputType current_input_type_;

  // ID that is used for the current input context.  False if there is no focus.
  int context_id_;

  // Next id that will be assigned to a context.
  int next_context_id_;

  // The input_component ID in IME extension's manifest.
  std::string active_component_id_;

  // The IME extension ID.
  extensions::ExtensionId extension_id_;

  raw_ptr<Profile> profile_;

  unsigned int next_request_id_ = 1;

  std::map<std::string, PendingKeyEvent> pending_key_events_;

  // The composition text to be set from calling input.ime.setComposition API.
  ui::CompositionText composition_;

  bool composition_changed_;

  // The text to be committed from calling input.ime.commitText API.
  std::u16string text_;

  bool commit_text_changed_;

  std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_;

  base::Value::Dict input_method_settings_snapshot_;

  ScreenProjectionChangeMonitor screen_projection_change_monitor_;

  bool is_ready_for_testing_ = false;

  base::ScopedObservation<Profile, ProfileObserver> profile_observation_{this};
};

}  // namespace input_method
}  // namespace ash

#endif  // CHROME_BROWSER_ASH_INPUT_METHOD_INPUT_METHOD_ENGINE_H_