| Index: ui/base/ime/android/cursor_anchor_info_controller.cc
 | 
| diff --git a/ui/base/ime/android/cursor_anchor_info_controller.cc b/ui/base/ime/android/cursor_anchor_info_controller.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..e2405b0990c18bd9f860ccbc34200d1528cd7c28
 | 
| --- /dev/null
 | 
| +++ b/ui/base/ime/android/cursor_anchor_info_controller.cc
 | 
| @@ -0,0 +1,329 @@
 | 
| +// Copyright (c) 2014 The Chromium Authors. All rights reserved.
 | 
| +// Use of this source code is governed by a BSD-style license that can be
 | 
| +// found in the LICENSE file.
 | 
| +
 | 
| +#include "ui/base/ime/android/cursor_anchor_info_controller.h"
 | 
| +
 | 
| +#include "base/android/build_info.h"
 | 
| +#include "base/strings/string_piece.h"
 | 
| +#include "base/strings/stringprintf.h"
 | 
| +#include "cc/output/compositor_frame_metadata.h"
 | 
| +#include "cc/output/viewport_selection_bound.h"
 | 
| +#include "ui/base/ime/android/cursor_anchor_info_builder.h"
 | 
| +#include "ui/base/ime/android/cursor_anchor_info_sender.h"
 | 
| +#include "ui/base/ui_base_switches_util.h"
 | 
| +
 | 
| +namespace ui {
 | 
| +namespace {
 | 
| +
 | 
| +class CursorAnchorInfoControllerImpl final
 | 
| +    : public CursorAnchorInfoController {
 | 
| + public:
 | 
| +  CursorAnchorInfoControllerImpl(
 | 
| +      CursorAnchorInfoSender* sender,
 | 
| +      scoped_ptr<CursorAnchorInfoBuilder> builder);
 | 
| +
 | 
| +  ~CursorAnchorInfoControllerImpl() override;
 | 
| +
 | 
| +  void OnFocusedNodeChanged(bool is_editable_node) override;
 | 
| +
 | 
| +  void OnTextInputStateChanged(
 | 
| +      const base::string16& text,
 | 
| +      const gfx::Range& selection_range,
 | 
| +      const gfx::Range& composition_range) override;
 | 
| +
 | 
| +  void OnCompositionRangeChanged(
 | 
| +      const gfx::Range& range,
 | 
| +      const std::vector<gfx::Rect>& character_bounds) override;
 | 
| +
 | 
| +  void OnFrameMetadataUpdated(
 | 
| +      const cc::CompositorFrameMetadata& frame_metadata,
 | 
| +      const gfx::Vector2d& view_offset) override;
 | 
| +
 | 
| +  bool OnRequestCursorUpdates(uint32 cursor_update_mode) override;
 | 
| +
 | 
| + private:
 | 
| +  void UpdateBuilder();
 | 
| +  void ScheduleUpdate();
 | 
| +  bool HasPendingUpdate() const;
 | 
| +  void ClearPendingUpdate();
 | 
| +  void SetMonitorMode(bool enabled);
 | 
| +
 | 
| +  // Doesn't own.
 | 
| +  CursorAnchorInfoSender* cursor_anchor_info_sender_;
 | 
| +  scoped_ptr<CursorAnchorInfoBuilder> cursor_anchor_info_builder_;
 | 
| +
 | 
| +  bool editable_;
 | 
| +
 | 
| +  base::string16 text_;
 | 
| +  gfx::Range selection_range_;
 | 
| +  gfx::Range composition_range_;
 | 
| +  std::vector<gfx::Rect> character_bounds_;
 | 
| +  gfx::Range character_bounds_range_;
 | 
| +  bool has_insertion_marker_;
 | 
| +  bool is_insertion_marker_visible_;
 | 
| +  gfx::RectF insertion_marker_rect_;
 | 
| +  bool has_coordinate_info_;
 | 
| +  float scale_;
 | 
| +  gfx::Vector2dF translation_;
 | 
| +
 | 
| +  bool has_pending_request_;
 | 
| +  bool monitor_mode_enabled_;
 | 
| +
 | 
| +  DISALLOW_COPY_AND_ASSIGN(CursorAnchorInfoControllerImpl);
 | 
| +};
 | 
| +
 | 
| +CursorAnchorInfoControllerImpl::CursorAnchorInfoControllerImpl(
 | 
| +    CursorAnchorInfoSender* sender,
 | 
| +    scoped_ptr<CursorAnchorInfoBuilder> builder)
 | 
| +    : cursor_anchor_info_sender_(sender),
 | 
| +      cursor_anchor_info_builder_(builder.Pass()),
 | 
| +      editable_(false),
 | 
| +      selection_range_(gfx::Range::InvalidRange()),
 | 
| +      composition_range_(gfx::Range::InvalidRange()),
 | 
| +      has_insertion_marker_(false),
 | 
| +      is_insertion_marker_visible_(false),
 | 
| +      has_coordinate_info_(false),
 | 
| +      scale_(1.0f),
 | 
| +      has_pending_request_(false),
 | 
| +      monitor_mode_enabled_(false) {
 | 
| +}
 | 
| +
 | 
| +CursorAnchorInfoControllerImpl::~CursorAnchorInfoControllerImpl() {
 | 
| +}
 | 
| +
 | 
| +void CursorAnchorInfoControllerImpl::OnFocusedNodeChanged(
 | 
| +    bool is_editable_node) {
 | 
| +  editable_ = is_editable_node;
 | 
| +  text_.clear();
 | 
| +  selection_range_ = gfx::Range::InvalidRange();
 | 
| +  composition_range_ = gfx::Range::InvalidRange();
 | 
| +  character_bounds_.clear();
 | 
| +  character_bounds_range_ = gfx::Range::InvalidRange();
 | 
| +  has_insertion_marker_ = false;
 | 
| +  is_insertion_marker_visible_= false;
 | 
| +  insertion_marker_rect_.SetRect(0.0f, 0.0f, 0.0f, 0.0f);
 | 
| +  has_coordinate_info_ = false;
 | 
| +  scale_ = 1.0f;
 | 
| +  translation_ = gfx::Vector2d();
 | 
| +}
 | 
| +
 | 
| +void CursorAnchorInfoControllerImpl::OnTextInputStateChanged(
 | 
| +    const base::string16& text,
 | 
| +    const gfx::Range& selection_range,
 | 
| +    const gfx::Range& composition_range) {
 | 
| +  if (!editable_)
 | 
| +    return;
 | 
| +
 | 
| +  if (monitor_mode_enabled_) {
 | 
| +    const bool changed =
 | 
| +        text_ != text
 | 
| +        || selection_range_ != selection_range
 | 
| +        || composition_range_ != composition_range;
 | 
| +    if (changed)
 | 
| +      has_pending_request_ = true;
 | 
| +  }
 | 
| +
 | 
| +  text_ = text;
 | 
| +  selection_range_ = selection_range;
 | 
| +  composition_range_ = composition_range;
 | 
| +}
 | 
| +
 | 
| +void CursorAnchorInfoControllerImpl::OnCompositionRangeChanged(
 | 
| +    const gfx::Range& range,
 | 
| +    const std::vector<gfx::Rect>& character_bounds) {
 | 
| +  if (!editable_)
 | 
| +    return;
 | 
| +
 | 
| +  if (monitor_mode_enabled_) {
 | 
| +    const bool changed =
 | 
| +        character_bounds_ != character_bounds
 | 
| +        || character_bounds_range_ != range;
 | 
| +    if (changed)
 | 
| +      has_pending_request_ = true;
 | 
| +  }
 | 
| +
 | 
| +  character_bounds_ = character_bounds;
 | 
| +  character_bounds_range_ = range;
 | 
| +}
 | 
| +
 | 
| +void CursorAnchorInfoControllerImpl::OnFrameMetadataUpdated(
 | 
| +    const cc::CompositorFrameMetadata& frame_metadata,
 | 
| +    const gfx::Vector2d& view_offset) {
 | 
| +  if (!editable_)
 | 
| +    return;
 | 
| +
 | 
| +  const auto& selection_start = frame_metadata.selection_start;
 | 
| +
 | 
| +  const float scale =
 | 
| +      frame_metadata.device_scale_factor * frame_metadata.page_scale_factor;
 | 
| +  const auto translation =
 | 
| +      gfx::ScaleVector2d(frame_metadata.root_scroll_offset, -scale)
 | 
| +      + gfx::Vector2dF(view_offset.x(), view_offset.y())
 | 
| +      + frame_metadata.location_bar_offset
 | 
| +      + frame_metadata.location_bar_content_translation;
 | 
| +
 | 
| +  const bool has_insertion_marker =
 | 
| +      selection_start.type == cc::SELECTION_BOUND_CENTER;
 | 
| +  const bool is_insertion_marker_visible = selection_start.visible;
 | 
| +  const gfx::PointF insertion_marker_origin = has_insertion_marker
 | 
| +      ? selection_start.edge_top
 | 
| +      : gfx::PointF(0.0f, 0.0f);
 | 
| +  const float insertion_marker_height = has_insertion_marker
 | 
| +      ? selection_start.edge_bottom.y() - selection_start.edge_top.y()
 | 
| +      : 0.0f;
 | 
| +  const gfx::SizeF insertion_marker_size(0.0f, insertion_marker_height);
 | 
| +  const gfx::RectF insertion_marker_rect(insertion_marker_origin,
 | 
| +                                         insertion_marker_size);
 | 
| +  if (monitor_mode_enabled_) {
 | 
| +    const bool changed =
 | 
| +        !has_coordinate_info_
 | 
| +        || scale_ != scale
 | 
| +        || translation_ != translation
 | 
| +        || has_insertion_marker_ != has_insertion_marker
 | 
| +        || is_insertion_marker_visible_ != is_insertion_marker_visible
 | 
| +        || insertion_marker_rect_ != insertion_marker_rect;
 | 
| +    if (changed)
 | 
| +      has_pending_request_ = true;
 | 
| +  }
 | 
| +
 | 
| +  scale_ = scale;
 | 
| +  translation_ = translation;
 | 
| +  has_insertion_marker_ = has_insertion_marker;
 | 
| +  is_insertion_marker_visible_ = is_insertion_marker_visible;
 | 
| +  insertion_marker_rect_ = insertion_marker_rect;
 | 
| +  has_coordinate_info_ = true;
 | 
| +
 | 
| +  if (cursor_anchor_info_sender_
 | 
| +      && cursor_anchor_info_builder_
 | 
| +      && HasPendingUpdate()) {
 | 
| +    UpdateBuilder();
 | 
| +    cursor_anchor_info_sender_->SendCursorAnchorInfo(
 | 
| +        cursor_anchor_info_builder_.get());
 | 
| +    ClearPendingUpdate();
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +bool CursorAnchorInfoControllerImpl::OnRequestCursorUpdates(
 | 
| +    uint32 cursor_update_mode) {
 | 
| +  if (!editable_)
 | 
| +    return false;
 | 
| +
 | 
| +  // Should return false if at least one unknown bit is set, because we don't
 | 
| +  // know what is requested by such a bit.
 | 
| +  const static uint32 kKnownFlags =
 | 
| +      kCursorUpdateModeImmediate | kCursorUpdateModeMonitor;
 | 
| +  if ((cursor_update_mode & ~kKnownFlags) != 0)
 | 
| +    return false;
 | 
| +
 | 
| +  const bool update_immediate = cursor_update_mode & kCursorUpdateModeImmediate;
 | 
| +  const bool update_monitor = cursor_update_mode & kCursorUpdateModeMonitor;
 | 
| +
 | 
| +  SetMonitorMode(update_monitor);
 | 
| +  if (update_immediate)
 | 
| +    ScheduleUpdate();
 | 
| +
 | 
| +  if (cursor_anchor_info_sender_
 | 
| +      && cursor_anchor_info_builder_
 | 
| +      && HasPendingUpdate()) {
 | 
| +    UpdateBuilder();
 | 
| +    cursor_anchor_info_sender_->SendCursorAnchorInfo(
 | 
| +        cursor_anchor_info_builder_.get());
 | 
| +    ClearPendingUpdate();
 | 
| +  }
 | 
| +
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +void CursorAnchorInfoControllerImpl::ScheduleUpdate() {
 | 
| +  has_pending_request_ = true;
 | 
| +}
 | 
| +
 | 
| +bool CursorAnchorInfoControllerImpl::HasPendingUpdate() const {
 | 
| +  return has_pending_request_ && has_coordinate_info_;
 | 
| +}
 | 
| +
 | 
| +void CursorAnchorInfoControllerImpl::ClearPendingUpdate() {
 | 
| +  has_pending_request_ = false;
 | 
| +}
 | 
| +
 | 
| +void CursorAnchorInfoControllerImpl::SetMonitorMode(bool enabled) {
 | 
| +  monitor_mode_enabled_ = enabled;
 | 
| +}
 | 
| +
 | 
| +void CursorAnchorInfoControllerImpl::UpdateBuilder() {
 | 
| +  DCHECK(HasPendingUpdate());
 | 
| +
 | 
| +  cursor_anchor_info_builder_->Reset();
 | 
| +
 | 
| +  if (selection_range_.IsValid())
 | 
| +    cursor_anchor_info_builder_->SetSelectionRange(selection_range_);
 | 
| +
 | 
| +  if (composition_range_.IsValid()) {
 | 
| +    const auto composition_text = base::StringPiece16(text_).substr(
 | 
| +        composition_range_.start(),
 | 
| +        composition_range_.length());
 | 
| +    cursor_anchor_info_builder_->SetComposingText(
 | 
| +        composition_range_.start(),
 | 
| +        composition_text);
 | 
| +  }
 | 
| +  if (character_bounds_range_.IsValid()) {
 | 
| +    const int index_offset = character_bounds_range_.start();
 | 
| +    for (size_t i = 0; i < character_bounds_.size(); ++i) {
 | 
| +      cursor_anchor_info_builder_->AddCharacterBounds(
 | 
| +          index_offset + i,
 | 
| +          character_bounds_[i],
 | 
| +          CursorAnchorInfoBuilder::kFlagHasVisibleRegion);
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  if (has_insertion_marker_) {
 | 
| +    const uint32 flags = is_insertion_marker_visible_
 | 
| +        ? CursorAnchorInfoBuilder::kFlagHasVisibleRegion
 | 
| +        : CursorAnchorInfoBuilder::kFlagHasInvisibleRegion;
 | 
| +    cursor_anchor_info_builder_->SetInsertionMarkerLocation(
 | 
| +        insertion_marker_rect_.x(),
 | 
| +        insertion_marker_rect_.y(),
 | 
| +        insertion_marker_rect_.bottom(),
 | 
| +        insertion_marker_rect_.bottom(),
 | 
| +        flags);
 | 
| +  }
 | 
| +  cursor_anchor_info_builder_->SetScaleAndTranslate(scale_,
 | 
| +                                                    translation_);
 | 
| +}
 | 
| +
 | 
| +}  // namespace
 | 
| +
 | 
| +CursorAnchorInfoController::CursorAnchorInfoController() {
 | 
| +}
 | 
| +
 | 
| +CursorAnchorInfoController::~CursorAnchorInfoController() {
 | 
| +}
 | 
| +
 | 
| +// static
 | 
| +scoped_ptr<CursorAnchorInfoController>
 | 
| +CursorAnchorInfoController::Create(CursorAnchorInfoSender* sender) {
 | 
| +  if (!switches::IsCursorAnchorInfoEnabled())
 | 
| +    return scoped_ptr<ui::CursorAnchorInfoController>();
 | 
| +
 | 
| +  static bool use_cursor_anchor_info =
 | 
| +      base::android::BuildInfo::GetInstance()->sdk_int() >= 21;
 | 
| +  if (!use_cursor_anchor_info)
 | 
| +    return scoped_ptr<ui::CursorAnchorInfoController>();
 | 
| +
 | 
| +  return scoped_ptr<ui::CursorAnchorInfoController>(
 | 
| +      new ui::CursorAnchorInfoControllerImpl(
 | 
| +          sender,
 | 
| +          CursorAnchorInfoBuilder::Create()));
 | 
| +}
 | 
| +
 | 
| +// static
 | 
| +scoped_ptr<CursorAnchorInfoController>
 | 
| +CursorAnchorInfoController::CreateForTest(
 | 
| +    CursorAnchorInfoSender* sender,
 | 
| +    scoped_ptr<ui::CursorAnchorInfoBuilder> builder) {
 | 
| +  return scoped_ptr<CursorAnchorInfoController>(
 | 
| +      new ui::CursorAnchorInfoControllerImpl(sender, builder.Pass()));
 | 
| +}
 | 
| +
 | 
| +}  // namespace ui
 | 
| 
 |