blob: 8a11623666ad997513411ab43a63815e06f748ca [file] [log] [blame]
// 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/find_in_page/find_in_page_manager_impl.h"
#import "base/strings/sys_string_conversions.h"
#include "base/values.h"
#import "ios/web/find_in_page/find_in_page_constants.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/web_state_impl.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace web {
// Timeout for the find within JavaScript in milliseconds.
const double kFindInPageFindTimeout = 100.0;
// Value returned when |kFindInPageSearch| call times out.
const int kFindInPagePending = -1;
// 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(WebState* web_state) {
DCHECK(web_state);
if (!FromWebState(web_state)) {
web_state->SetUserData(UserDataKey(),
std::make_unique<FindInPageManagerImpl>(web_state));
}
}
FindInPageManagerImpl::~FindInPageManagerImpl() {
if (web_state_) {
web_state_->RemoveObserver(this);
web_state_ = nullptr;
}
}
FindInPageManagerImpl::FindRequest::FindRequest() {}
FindInPageManagerImpl::FindRequest::~FindRequest() {}
void FindInPageManagerImpl::WebFrameDidBecomeAvailable(WebState* web_state,
WebFrame* web_frame) {
const std::string frame_id = web_frame->GetFrameId();
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);
} else {
// The order of iframes is not important.
last_find_request_.frame_order.push_back(frame_id);
}
}
void FindInPageManagerImpl::WebFrameWillBecomeUnavailable(WebState* web_state,
WebFrame* web_frame) {
last_find_request_.frame_order.remove(web_frame->GetFrameId());
last_find_request_.frame_match_count.erase(web_frame->GetFrameId());
}
void FindInPageManagerImpl::WebStateDestroyed(WebState* web_state) {
web_state_->RemoveObserver(this);
web_state_ = nullptr;
}
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