Commit ec44c35d authored by Chris Lu's avatar Chris Lu Committed by Commit Bot

[ios] Implement Find for FindInPageManager

Implements logic for FindInPageSearch option to search and highlight matches.
Searching for non-ascii not currently supported.

Bug: 896867
Change-Id: I362d3cebacfced1537742fbbab9ad323d6edeafd
Reviewed-on: https://chromium-review.googlesource.com/c/1407665Reviewed-by: default avatarChris Lu <thegreenfrog@chromium.org>
Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Reviewed-by: default avatarMike Dougherty <michaeldo@chromium.org>
Commit-Queue: Chris Lu <thegreenfrog@chromium.org>
Cr-Commit-Position: refs/heads/master@{#626108}
parent 0b9c2fe2
......@@ -441,6 +441,7 @@ source_set("ios_web_web_state_js_unittests") {
"//ios/web/test:test_constants",
"//ios/web/test:test_support",
"//ios/web/web_state:context_menu",
"//ios/web/web_state:find_in_page",
"//ios/web/web_state:navigation_context",
"//ios/web/web_state/js",
"//ios/web/web_state/js:script_util",
......
......@@ -15,7 +15,8 @@ namespace web {
// Indicates what action the FindinPageManager should take.
enum class FindInPageOptions {
// Search for a string. Highlight and scroll to the first result if
// string is found.
// string is found. TODO(crbug.com/925149): Does not support strings with
// non-ascii.
FindInPageSearch = 1,
// Highlight and scroll to the next result if there is one. Otherwise, nothing
// will change. Loop back to the first result if currently on last result. If
......@@ -32,7 +33,8 @@ class FindInPageManager : public web::WebStateUserData<FindInPageManager> {
public:
// Searches for string |query|. Executes new search or traverses results based
// on |options|. |query| must not be null if |options| is |FindInPageSearch|.
// |query| is ignored if |options| is not |FindInPageSearch|.
// |query| is ignored if |options| is not |FindInPageSearch|. If new search is
// started before previous search finishes, old request will be discarded.
virtual void Find(NSString* query, FindInPageOptions options) = 0;
// Removes any highlighting. Does nothing if Find() with
......
......@@ -5,6 +5,7 @@
#ifndef IOS_WEB_PUBLIC_WEB_STATE_WEB_FRAME_UTIL_H_
#define IOS_WEB_PUBLIC_WEB_STATE_WEB_FRAME_UTIL_H_
#include <set>
#include <string>
namespace web {
......@@ -26,6 +27,9 @@ WebFrame* GetWebFrameWithId(WebState* web_state, const std::string& frame_id);
// Returns the ID of |frame|. Returns std::string() if |frame| is nullptr.
std::string GetWebFrameId(WebFrame* frame);
// Returns all web frames in |web_state|.
std::set<WebFrame*> GetAllWebFrames(WebState* web_state);
} // namespace web
#endif // IOS_WEB_PUBLIC_WEB_STATE_WEB_FRAME_UTIL_H_
......@@ -174,6 +174,8 @@ source_set("find_in_page") {
]
sources = [
"find_in_page/find_in_page_constants.h",
"find_in_page/find_in_page_constants.mm",
"find_in_page/find_in_page_manager_impl.h",
"find_in_page/find_in_page_manager_impl.mm",
]
......
// Copyright 2019 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.
#ifndef IOS_WEB_WEB_STATE_FIND_IN_PAGE_FIND_IN_PAGE_CONSTANTS_H_
#define IOS_WEB_WEB_STATE_FIND_IN_PAGE_FIND_IN_PAGE_CONSTANTS_H_
namespace web {
// The name of JavaScript function which finds all matches of a string.
extern const char kFindInPageSearch[];
// The name of JavaScript function which continues an unfinished find.
extern const char kFindInPagePump[];
} // namespace web
#endif // IOS_WEB_WEB_STATE_FIND_IN_PAGE_FIND_IN_PAGE_CONSTANTS_H_
// Copyright 2019 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.
#import "ios/web/web_state/find_in_page/find_in_page_constants.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace web {
const char kFindInPageSearch[] = "findInPage.findString";
const char kFindInPagePump[] = "findInPage.pumpSearch";
} // namespace web
......@@ -41,13 +41,21 @@ class FindInPageManagerImpl : public FindInPageManager,
// |pending_frame_calls_count| after it has been reset by the new find.
int unique_id = 0;
// Counter to keep track of pending frame JavaScript calls.
int pending_frame_calls_count = 0;
int pending_frame_call_count = 0;
// Holds number of matches found for each frame keyed by frame_id.
std::map<std::string, int> frame_matches_count;
std::map<std::string, int> frame_match_count;
// List of frame_ids used for sorting matches.
std::list<std::string> frame_order;
};
// Determines whether find is finished. If not, calls pumpSearch to continue.
// If it is, calls UpdateFrameMatchesCount(). If find returned null, then does
// nothing more.
void ProcessFindInPageResult(const std::string& query,
const std::string& frame_id,
const int request_id,
const base::Value* result);
// WebStateObserver overrides
void WebFrameDidBecomeAvailable(WebState* web_state,
WebFrame* web_frame) override;
......
......@@ -4,7 +4,12 @@
#import "ios/web/web_state/find_in_page/find_in_page_manager_impl.h"
#import "base/strings/sys_string_conversions.h"
#include "base/values.h"
#import "ios/web/public/web_state/web_frame.h"
#include "ios/web/public/web_state/web_frame_util.h"
#import "ios/web/public/web_state/web_frames_manager.h"
#import "ios/web/web_state/find_in_page/find_in_page_constants.h"
#import "ios/web/web_state/web_state_impl.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
......@@ -13,14 +18,25 @@
namespace web {
// static
// Timeout for the find within JavaScript in milliseconds.
const double kFindInPageFindTimeout = 100.0;
// Value returned when |kFindInPageSearch| call times out.
const int kFindInPagePending = -1;
FindInPageManagerImpl::FindInPageManagerImpl(web::WebState* web_state)
// The timeout for JavaScript function calls in milliseconds. Important that
// this is longer than |kFindInPageFindTimeout| to allow for incomplete find to
// restart again. If this timeout hits, then something went wrong with the find
// and find in page should not continue.
const double kJavaScriptFunctionCallTimeout = 200.0;
// static
FindInPageManagerImpl::FindInPageManagerImpl(WebState* web_state)
: web_state_(web_state), weak_factory_(this) {
web_state_->AddObserver(this);
}
void FindInPageManagerImpl::CreateForWebState(web::WebState* web_state) {
void FindInPageManagerImpl::CreateForWebState(WebState* web_state) {
DCHECK(web_state);
if (!FromWebState(web_state)) {
web_state->SetUserData(UserDataKey(),
......@@ -42,7 +58,7 @@ FindInPageManagerImpl::FindRequest::~FindRequest() {}
void FindInPageManagerImpl::WebFrameDidBecomeAvailable(WebState* web_state,
WebFrame* web_frame) {
const std::string frame_id = web_frame->GetFrameId();
last_find_request_.frame_matches_count[frame_id] = 0;
last_find_request_.frame_match_count[frame_id] = 0;
if (web_frame->IsMainFrame()) {
// Main frame matches should show up first.
last_find_request_.frame_order.push_front(frame_id);
......@@ -55,7 +71,7 @@ void FindInPageManagerImpl::WebFrameDidBecomeAvailable(WebState* web_state,
void FindInPageManagerImpl::WebFrameWillBecomeUnavailable(WebState* web_state,
WebFrame* web_frame) {
last_find_request_.frame_order.remove(web_frame->GetFrameId());
last_find_request_.frame_matches_count.erase(web_frame->GetFrameId());
last_find_request_.frame_match_count.erase(web_frame->GetFrameId());
}
void FindInPageManagerImpl::WebStateDestroyed(WebState* web_state) {
......@@ -63,8 +79,74 @@ void FindInPageManagerImpl::WebStateDestroyed(WebState* web_state) {
web_state_ = nullptr;
}
void FindInPageManagerImpl::Find(NSString* query, FindInPageOptions options) {}
void FindInPageManagerImpl::Find(NSString* query, FindInPageOptions options) {
switch (options) {
case FindInPageOptions::FindInPageSearch: {
DCHECK(query);
std::set<WebFrame*> all_frames = GetAllWebFrames(web_state_);
last_find_request_.pending_frame_call_count = all_frames.size();
std::vector<base::Value> params;
params.push_back(base::Value(base::SysNSStringToUTF8(query)));
params.push_back(base::Value(kFindInPageFindTimeout));
int unique_id = ++last_find_request_.unique_id;
for (WebFrame* frame : all_frames) {
frame->CallJavaScriptFunction(
kFindInPageSearch, params,
base::BindOnce(&FindInPageManagerImpl::ProcessFindInPageResult,
weak_factory_.GetWeakPtr(),
base::SysNSStringToUTF8(query), frame->GetFrameId(),
unique_id),
base::TimeDelta::FromSeconds(kJavaScriptFunctionCallTimeout));
}
break;
}
case FindInPageOptions::FindInPageNext:
case FindInPageOptions::FindInPagePrevious:
break;
}
}
void FindInPageManagerImpl::StopFinding() {}
void FindInPageManagerImpl::ProcessFindInPageResult(const std::string& query,
const std::string& frame_id,
const int unique_id,
const base::Value* result) {
if (unique_id != last_find_request_.unique_id) {
// New find was started.
return;
}
last_find_request_.pending_frame_call_count--;
WebFrame* frame = GetWebFrameWithId(web_state_, frame_id);
// The frame no longer exists. If frame is removed during find, result will
// be null.
if (!result || !frame) {
return;
}
int matches_count = 0;
if (result->is_double()) {
// Valid match number returned. If not, match count will be 0 in order to
// zero-out count from previous find.
matches_count = static_cast<int>(result->GetDouble());
}
// If response is equal to kFindInPagePending, find did not finish in the
// JavaScript. Call pumpSearch to continue find.
if (matches_count == kFindInPagePending) {
std::vector<base::Value> params;
params.push_back(base::Value(kFindInPageFindTimeout));
frame->CallJavaScriptFunction(
kFindInPagePump, params,
base::BindOnce(&FindInPageManagerImpl::ProcessFindInPageResult,
weak_factory_.GetWeakPtr(), query, frame_id, unique_id),
base::TimeDelta::FromSeconds(kJavaScriptFunctionCallTimeout));
return;
}
last_find_request_.frame_match_count[frame_id] = matches_count;
}
} // namespace web
......@@ -9,6 +9,7 @@
#import "ios/web/public/web_state/web_frame.h"
#import "ios/web/public/web_state/web_frame_util.h"
#import "ios/web/public/web_state/web_frames_manager.h"
#import "ios/web/web_state/find_in_page/find_in_page_constants.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
......@@ -19,9 +20,6 @@ using base::test::ios::WaitUntilConditionOrTimeout;
namespace {
// Name of JavaScript handler invoked.
const char kFindInPageFindString[] = "findInPage.findString";
// Find strings.
const char kFindStringFoo[] = "foo";
const char kFindString12345[] = "12345";
......@@ -60,7 +58,7 @@ TEST_F(FindInPageWebJsTest, FindText) {
params.push_back(base::Value(kFindStringFoo));
params.push_back(base::Value(kPumpSearchTimeout));
main_web_frame()->CallJavaScriptFunction(
kFindInPageFindString, params, base::BindOnce(^(const base::Value* res) {
kFindInPageSearch, params, base::BindOnce(^(const base::Value* res) {
ASSERT_TRUE(res);
ASSERT_TRUE(res->is_double());
int count = static_cast<int>(res->GetDouble());
......@@ -86,7 +84,7 @@ TEST_F(FindInPageWebJsTest, FindTextNoResults) {
params.push_back(base::Value(kFindStringFoo));
params.push_back(base::Value(kPumpSearchTimeout));
main_web_frame()->CallJavaScriptFunction(
kFindInPageFindString, params, base::BindOnce(^(const base::Value* res) {
kFindInPageSearch, params, base::BindOnce(^(const base::Value* res) {
ASSERT_TRUE(res);
ASSERT_TRUE(res->is_double());
int count = static_cast<int>(res->GetDouble());
......@@ -125,8 +123,7 @@ TEST_F(FindInPageWebJsTest, FindIFrameText) {
params.push_back(base::Value(kFindStringFoo));
params.push_back(base::Value(kPumpSearchTimeout));
child_frame->CallJavaScriptFunction(
kFindInPageFindString, params,
base::BindOnce(^(const base::Value* res) {
kFindInPageSearch, params, base::BindOnce(^(const base::Value* res) {
ASSERT_TRUE(res);
ASSERT_TRUE(res->is_double());
int count = static_cast<int>(res->GetDouble());
......@@ -151,7 +148,7 @@ TEST_F(FindInPageWebJsTest, FindWhiteSpace) {
params.push_back(base::Value(" "));
params.push_back(base::Value(kPumpSearchTimeout));
main_web_frame()->CallJavaScriptFunction(
kFindInPageFindString, params, base::BindOnce(^(const base::Value* res) {
kFindInPageSearch, params, base::BindOnce(^(const base::Value* res) {
ASSERT_TRUE(res);
ASSERT_TRUE(res->is_double());
int count = static_cast<int>(res->GetDouble());
......@@ -176,7 +173,7 @@ TEST_F(FindInPageWebJsTest, FindAcrossMultipleNodes) {
params.push_back(base::Value(kFindString12345));
params.push_back(base::Value(kPumpSearchTimeout));
main_web_frame()->CallJavaScriptFunction(
kFindInPageFindString, params, base::BindOnce(^(const base::Value* res) {
kFindInPageSearch, params, base::BindOnce(^(const base::Value* res) {
ASSERT_TRUE(res);
ASSERT_TRUE(res->is_double());
int count = static_cast<int>(res->GetDouble());
......
......@@ -40,4 +40,9 @@ std::string GetWebFrameId(WebFrame* frame) {
return frame ? frame->GetFrameId() : std::string();
}
std::set<WebFrame*> GetAllWebFrames(WebState* web_state) {
WebFramesManager* manager = WebFramesManager::FromWebState(web_state);
return manager->GetAllWebFrames();
}
} // namespace web
......@@ -115,4 +115,25 @@ TEST_F(WebFrameUtilTest, GetWebFrameId) {
EXPECT_EQ("frame", GetWebFrameId(&frame));
}
// Tests the GetAllWebFrames function.
TEST_F(WebFrameUtilTest, GetAllWebFrames) {
TestWebState test_web_state;
test_web_state.CreateWebFramesManager();
EXPECT_EQ(0U, GetAllWebFrames(&test_web_state).size());
auto main_frame =
std::make_unique<FakeWebFrame>("main_frame", true, GURL::EmptyGURL());
FakeWebFrame* main_frame_ptr = main_frame.get();
test_web_state.AddWebFrame(std::move(main_frame));
auto iframe =
std::make_unique<FakeWebFrame>("iframe", false, GURL::EmptyGURL());
FakeWebFrame* iframe_ptr = iframe.get();
test_web_state.AddWebFrame(std::move(iframe));
std::set<WebFrame*> all_frames = GetAllWebFrames(&test_web_state);
// Both frames should be returned
EXPECT_NE(all_frames.end(), all_frames.find(main_frame_ptr));
EXPECT_NE(all_frames.end(), all_frames.find(iframe_ptr));
EXPECT_EQ(2U, all_frames.size());
}
} // namespace web
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment