OLD | NEW |
| (Empty) |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "components/web_view/find_controller.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/strings/string_util.h" | |
9 #include "base/trace_event/trace_event.h" | |
10 #include "components/web_view/find_controller_delegate.h" | |
11 #include "components/web_view/frame.h" | |
12 | |
13 namespace web_view { | |
14 | |
15 FindController::FindController(FindControllerDelegate* delegate) | |
16 : delegate_(delegate), | |
17 find_request_id_counter_(0), | |
18 current_find_request_id_(-1), | |
19 frame_with_selection_(0), | |
20 weak_ptr_factory_(this) {} | |
21 | |
22 FindController::~FindController() {} | |
23 | |
24 void FindController::Find(const std::string& in_search_string, | |
25 bool forward_direction) { | |
26 TRACE_EVENT2("web_view", "FindController::Find", | |
27 "search_string", in_search_string, | |
28 "forward_direction", forward_direction); | |
29 std::string search_string = in_search_string; | |
30 // Remove the carriage return character, which generally isn't in web content. | |
31 const char kInvalidChars[] = {'\r', 0}; | |
32 base::RemoveChars(search_string, kInvalidChars, &search_string); | |
33 | |
34 previous_find_text_ = find_text_; | |
35 | |
36 // TODO(erg): Do we need to something like |find_op_aborted_|? | |
37 | |
38 find_frames_in_order_ = delegate_->GetAllFrames(); | |
39 | |
40 uint32_t starting_frame; | |
41 bool continue_last_find = | |
42 find_text_ == search_string || search_string.empty(); | |
43 if (!continue_last_find) { | |
44 current_find_request_id_ = find_request_id_counter_++; | |
45 frame_with_selection_ = 0; | |
46 returned_find_data_.clear(); | |
47 starting_frame = find_frames_in_order_.front()->id(); | |
48 } else { | |
49 starting_frame = frame_with_selection_; | |
50 } | |
51 | |
52 if (!search_string.empty()) | |
53 find_text_ = search_string; | |
54 | |
55 LocalFindOptions options; | |
56 options.forward = forward_direction; | |
57 options.continue_last_find = continue_last_find; | |
58 // Prime the continue loop. | |
59 OnContinueFinding(current_find_request_id_, search_string, options, | |
60 starting_frame, 0u, false); | |
61 } | |
62 | |
63 void FindController::StopFinding() { | |
64 // Don't report any callbacks that we get after this. | |
65 current_find_request_id_ = -1; | |
66 frame_with_selection_ = 0; | |
67 | |
68 for (Frame* f : delegate_->GetAllFrames()) | |
69 f->StopFinding(true); | |
70 } | |
71 | |
72 void FindController::OnFindInFrameCountUpdated(int32_t request_id, | |
73 Frame* frame, | |
74 int32_t count, | |
75 bool final_update) { | |
76 if (request_id != current_find_request_id_) | |
77 return; | |
78 | |
79 auto it = returned_find_data_.find(frame); | |
80 if (it == returned_find_data_.end()) { | |
81 NOTREACHED(); | |
82 return; | |
83 } | |
84 | |
85 it->second.count = count; | |
86 it->second.final_update = final_update; | |
87 | |
88 int merged_count = 0; | |
89 bool merged_final_update = true; | |
90 for (auto const& data : returned_find_data_) { | |
91 merged_count += data.second.count; | |
92 merged_final_update = merged_final_update && data.second.final_update; | |
93 } | |
94 | |
95 // We can now take the individual FindInFrame messages and construct a | |
96 // FindInPage message. | |
97 delegate_->GetWebViewClient()->FindInPageMatchCountUpdated( | |
98 request_id, merged_count, merged_final_update); | |
99 } | |
100 | |
101 void FindController::OnFindInPageSelectionUpdated( | |
102 int32_t request_id, | |
103 Frame* frame, | |
104 int32_t active_match_ordinal) { | |
105 if (request_id != current_find_request_id_) | |
106 return; | |
107 | |
108 // TODO(erg): This is the one that's really hard. To give an accurate count | |
109 // here, we need to have all the results for frames that are before the Frame | |
110 // that contains the selected match so we can add their sums together. | |
111 // | |
112 // Thankfully, we don't have to worry about this now. Since there aren't | |
113 // back/forward controls yet, active_match_ordinal will always be 1. | |
114 delegate_->GetWebViewClient()->FindInPageSelectionUpdated( | |
115 request_id, active_match_ordinal); | |
116 } | |
117 | |
118 void FindController::DidDestroyFrame(Frame* frame) { | |
119 auto it = | |
120 find(find_frames_in_order_.begin(), find_frames_in_order_.end(), frame); | |
121 if (it != find_frames_in_order_.end()) | |
122 find_frames_in_order_.erase(it); | |
123 } | |
124 | |
125 void FindController::OnContinueFinding(int32_t request_id, | |
126 const std::string& search_string, | |
127 LocalFindOptions options, | |
128 uint32_t starting_frame, | |
129 uint32_t current_frame, | |
130 bool found) { | |
131 TRACE_EVENT2("web_view", "FindController::OnContinueFinding", | |
132 "request_id", request_id, | |
133 "search_string", search_string); | |
134 if (!found) { | |
135 // So we need to figure out what the next frame to search is. | |
136 Frame* next_frame = | |
137 GetNextFrameToSearch(starting_frame, current_frame, options.forward); | |
138 | |
139 // If we have one more frame to search: | |
140 if (next_frame) { | |
141 bool wrap_within_frame = find_frames_in_order_.size() == 1; | |
142 next_frame->Find( | |
143 request_id, mojo::String::From(search_string), | |
144 mojom::FindOptions::From(options), wrap_within_frame, | |
145 base::Bind(&FindController::OnContinueFinding, | |
146 weak_ptr_factory_.GetWeakPtr(), request_id, search_string, | |
147 options, starting_frame, next_frame->id())); | |
148 | |
149 // TODO(erg): This doesn't deal with wrapping around the document at the | |
150 // end when there are multiple frames and the last frame doesn't have a | |
151 // result. | |
152 return; | |
153 } | |
154 } | |
155 | |
156 frame_with_selection_ = found ? current_frame : 0; | |
157 | |
158 if (!options.continue_last_find) { | |
159 // If nothing is found, set result to "0 of 0", otherwise, set it to | |
160 // "-1 of 1" to indicate that we found at least one item, but we don't know | |
161 // yet what is active. | |
162 int ordinal = found ? -1 : 0; // -1 here means, we might know more later. | |
163 int match_count = found ? 1 : 0; // 1 here means possibly more coming. | |
164 | |
165 // If we find no matches then this will be our last status update. | |
166 // Otherwise the scoping effort will send more results. | |
167 bool final_status_update = !found; | |
168 | |
169 // Send priming messages. | |
170 delegate_->GetWebViewClient()->FindInPageSelectionUpdated(request_id, | |
171 ordinal); | |
172 delegate_->GetWebViewClient()->FindInPageMatchCountUpdated( | |
173 request_id, match_count, final_status_update); | |
174 | |
175 std::vector<Frame*> frames = delegate_->GetAllFrames(); | |
176 for (Frame* f : frames) { | |
177 f->StopHighlightingFindResults(); | |
178 | |
179 if (found) { | |
180 MatchData& match_data = returned_find_data_[f]; | |
181 match_data.count = 0; | |
182 match_data.final_update = false; | |
183 f->HighlightFindResults(request_id, mojo::String::From(search_string), | |
184 mojom::FindOptions::From(options), true); | |
185 } | |
186 } | |
187 } | |
188 } | |
189 | |
190 Frame* FindController::GetNextFrameToSearch(uint32_t starting_frame, | |
191 uint32_t current_frame, | |
192 bool forward) { | |
193 std::vector<Frame*>::iterator it; | |
194 if (current_frame == 0u) { | |
195 it = GetFrameIteratorById(starting_frame); | |
196 CHECK(it != find_frames_in_order_.end()); | |
197 return *it; | |
198 } | |
199 | |
200 Frame* candidate; | |
201 it = GetFrameIteratorById(current_frame); | |
202 if (it == find_frames_in_order_.end()) { | |
203 // Our current frame has been deleted. There is nothing to be done now; we | |
204 // will abort. | |
205 return nullptr; | |
206 } | |
207 | |
208 if (forward) { | |
209 ++it; | |
210 if (it == find_frames_in_order_.end()) | |
211 it = find_frames_in_order_.begin(); | |
212 candidate = *it; | |
213 } else { | |
214 if (it == find_frames_in_order_.begin()) { | |
215 candidate = find_frames_in_order_.back(); | |
216 } else { | |
217 --it; | |
218 candidate = *it; | |
219 } | |
220 } | |
221 | |
222 // If we've looped around the entire frame tree, then we are done and return | |
223 // null. | |
224 if (candidate->id() == starting_frame) | |
225 return nullptr; | |
226 return candidate; | |
227 } | |
228 | |
229 std::vector<Frame*>::iterator FindController::GetFrameIteratorById( | |
230 uint32_t frame_id) { | |
231 return find_if(find_frames_in_order_.begin(), | |
232 find_frames_in_order_.end(), | |
233 [&frame_id](Frame* f) { | |
234 return f->id() == frame_id; | |
235 }); | |
236 } | |
237 | |
238 } // namespace web_view | |
OLD | NEW |