chromium/third_party/blink/renderer/core/editing/finder/text_finder.cc

/*
 * Copyright (C) 2009 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "third_party/blink/renderer/core/editing/finder/text_finder.h"

#include "third_party/blink/public/mojom/input/focus_type.mojom-blink.h"
#include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom-blink.h"
#include "third_party/blink/public/platform/web_vector.h"
#include "third_party/blink/public/web/web_frame_widget.h"
#include "third_party/blink/public/web/web_local_frame_client.h"
#include "third_party/blink/public/web/web_view_client.h"
#include "third_party/blink/renderer/core/accessibility/ax_object_cache_base.h"
#include "third_party/blink/renderer/core/display_lock/display_lock_document_state.h"
#include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h"
#include "third_party/blink/renderer/core/dom/focus_params.h"
#include "third_party/blink/renderer/core/dom/range.h"
#include "third_party/blink/renderer/core/dom/shadow_root.h"
#include "third_party/blink/renderer/core/editing/editing_utilities.h"
#include "third_party/blink/renderer/core/editing/editor.h"
#include "third_party/blink/renderer/core/editing/ephemeral_range.h"
#include "third_party/blink/renderer/core/editing/finder/find_in_page_coordinates.h"
#include "third_party/blink/renderer/core/editing/finder/find_options.h"
#include "third_party/blink/renderer/core/editing/finder/find_task_controller.h"
#include "third_party/blink/renderer/core/editing/frame_selection.h"
#include "third_party/blink/renderer/core/editing/markers/document_marker.h"
#include "third_party/blink/renderer/core/editing/markers/document_marker_controller.h"
#include "third_party/blink/renderer/core/editing/selection_template.h"
#include "third_party/blink/renderer/core/editing/visible_selection.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/exported/web_view_impl.h"
#include "third_party/blink/renderer/core/frame/find_in_page.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/frame/web_frame_widget_impl.h"
#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
#include "third_party/blink/renderer/core/html/forms/html_input_element.h"
#include "third_party/blink/renderer/core/html/forms/html_text_area_element.h"
#include "third_party/blink/renderer/core/html/html_details_element.h"
#include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
#include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/core/layout/layout_shift_tracker.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/layout/text_autosizer.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/scroll/scroll_into_view_util.h"
#include "third_party/blink/renderer/platform/instrumentation/histogram.h"
#include "third_party/blink/renderer/platform/timer.h"

namespace blink {

TextFinder::FindMatch::FindMatch(Range* range, int ordinal)
    :{}

void TextFinder::FindMatch::Trace(Visitor* visitor) const {}

static void AutoExpandSearchableHiddenElementsUpFrameTree(Range* range) {}

static void ScrollToVisible(Range* match) {}

void TextFinder::InitNewSession(const mojom::blink::FindOptions& options) {}

bool TextFinder::Find(int identifier,
                      const WebString& search_text,
                      const mojom::blink::FindOptions& options,
                      bool wrap_within_frame,
                      bool* active_now) {}

bool TextFinder::FindInternal(int identifier,
                              const WebString& search_text,
                              const mojom::blink::FindOptions& options,
                              bool wrap_within_frame,
                              bool* active_now,
                              Range* first_match,
                              bool wrapped_around) {}

void TextFinder::ClearActiveFindMatch() {}

LocalFrame* TextFinder::GetFrame() const {}

void TextFinder::SetFindEndstateFocusAndSelection() {}

void TextFinder::StopFindingAndClearSelection() {}

void TextFinder::ReportFindInPageTerminationToAccessibility() {}

void TextFinder::ReportFindInPageResultToAccessibility(int identifier) {}

void TextFinder::StartScopingStringMatches(
    int identifier,
    const WebString& search_text,
    const mojom::blink::FindOptions& options) {}

void TextFinder::FlushCurrentScopingEffort(int identifier) {}

void TextFinder::DidFindMatch(int identifier,
                              int current_total_matches,
                              Range* result_range) {}

void TextFinder::UpdateMatches(int identifier,
                               int found_match_count,
                               bool finished_whole_request) {}

void TextFinder::FinishCurrentScopingEffort(int identifier) {}

void TextFinder::CancelPendingScopingEffort() {}

void TextFinder::IncreaseMatchCount(int identifier, int count) {}

void TextFinder::ReportFindInPageSelection(const gfx::Rect& selection_rect,
                                           int active_match_ordinal,
                                           int identifier) {}

void TextFinder::ResetMatchCount() {}

void TextFinder::ClearFindMatchesCache() {}

void TextFinder::InvalidateFindMatchRects() {}

void TextFinder::UpdateFindMatchRects() {}

#if BUILDFLAG(IS_ANDROID)
gfx::RectF TextFinder::ActiveFindMatchRect() {
  if (!current_active_match_frame_ || !active_match_)
    return gfx::RectF();

  return FindInPageRectFromRange(EphemeralRange(ActiveMatch()));
}

Vector<gfx::RectF> TextFinder::FindMatchRects() {
  UpdateFindMatchRects();

  Vector<gfx::RectF> match_rects;
  match_rects.reserve(match_rects.size() + find_matches_cache_.size());
  for (const FindMatch& match : find_matches_cache_) {
    DCHECK(!match.rect_.IsEmpty());
    match_rects.push_back(match.rect_);
  }

  return match_rects;
}

int TextFinder::SelectNearestFindMatch(const gfx::PointF& point,
                                       gfx::Rect* selection_rect) {
  int index = NearestFindMatch(point, nullptr);
  if (index != -1)
    return SelectFindMatch(static_cast<unsigned>(index), selection_rect);

  return -1;
}

int TextFinder::NearestFindMatch(const gfx::PointF& point,
                                 float* distance_squared) {
  UpdateFindMatchRects();

  int nearest = -1;
  float nearest_distance_squared = FLT_MAX;
  for (wtf_size_t i = 0; i < find_matches_cache_.size(); ++i) {
    DCHECK(!find_matches_cache_[i].rect_.IsEmpty());
    gfx::Vector2dF offset = point - find_matches_cache_[i].rect_.CenterPoint();
    float current_distance_squared = offset.LengthSquared();
    if (current_distance_squared < nearest_distance_squared) {
      nearest = i;
      nearest_distance_squared = current_distance_squared;
    }
  }

  if (distance_squared)
    *distance_squared = nearest_distance_squared;

  return nearest;
}

int TextFinder::SelectFindMatch(unsigned index, gfx::Rect* selection_rect) {
  SECURITY_DCHECK(index < find_matches_cache_.size());

  Range* range = find_matches_cache_[index].range_;
  if (!range->BoundaryPointsValid() || !range->startContainer()->isConnected())
    return -1;

  // Check if the match is already selected.
  if (!current_active_match_frame_ || !active_match_ ||
      !AreRangesEqual(active_match_.Get(), range)) {
    active_match_index_ = find_matches_cache_[index].ordinal_ - 1;

    // Set this frame as the active frame (the one with the active highlight).
    current_active_match_frame_ = true;
    OwnerFrame().ViewImpl()->SetFocusedFrame(&OwnerFrame());

    if (active_match_)
      SetMarkerActive(active_match_.Get(), false);
    active_match_ = range;
    SetMarkerActive(active_match_.Get(), true);

    // Clear any user selection, to make sure Find Next continues on from the
    // match we just activated.
    OwnerFrame().GetFrame()->Selection().Clear();

    // Make sure no node is focused. See http://crbug.com/38700.
    OwnerFrame().GetFrame()->GetDocument()->ClearFocusedElement();
  }

  gfx::Rect active_match_rect;
  gfx::Rect active_match_bounding_box =
      ComputeTextRect(EphemeralRange(active_match_.Get()));

  if (!active_match_bounding_box.IsEmpty()) {
    if (active_match_->FirstNode() &&
        active_match_->FirstNode()->GetLayoutObject()) {
      scroll_into_view_util::ScrollRectToVisible(
          *active_match_->FirstNode()->GetLayoutObject(),
          PhysicalRect(active_match_bounding_box),
          scroll_into_view_util::CreateScrollIntoViewParams(
              ScrollAlignment::CenterIfNeeded(),
              ScrollAlignment::CenterIfNeeded(),
              mojom::blink::ScrollType::kUser));

      // Absolute coordinates are scroll-variant so the bounding box will change
      // if the page is scrolled by ScrollRectToVisible above. Recompute the
      // bounding box so we have the updated location for the zoom below.
      // TODO(bokan): This should really use the return value from
      // ScrollRectToVisible which returns the updated position of the
      // scrolled rect. However, this was recently added and this is a fix
      // that needs to be merged to a release branch.
      // https://crbug.com/823365.
      active_match_bounding_box =
          ComputeTextRect(EphemeralRange(active_match_.Get()));
    }

    // Zoom to the active match.
    active_match_rect = OwnerFrame().GetFrameView()->ConvertToRootFrame(
        active_match_bounding_box);
    OwnerFrame().LocalRoot()->FrameWidgetImpl()->ZoomToFindInPageRect(
        active_match_rect);
  }

  if (selection_rect)
    *selection_rect = active_match_rect;

  return active_match_index_ + 1;
}
#endif  // BUILDFLAG(IS_ANDROID)

TextFinder::TextFinder(WebLocalFrameImpl& owner_frame)
    :{}

bool TextFinder::SetMarkerActive(Range* range, bool active) {}

void TextFinder::UnmarkAllTextMatches() {}

void TextFinder::InvalidateIfNecessary() {}

void TextFinder::FlushCurrentScoping() {}

void TextFinder::InvalidatePaintForTickmarks() {}

void TextFinder::Trace(Visitor* visitor) const {}

void TextFinder::Scroll(std::unique_ptr<AsyncScrollContext> context) {}

void TextFinder::IncreaseMarkerVersion() {}

}  // namespace blink