OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 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 "chrome/browser/android/contextualsearch/search_action.h" | |
6 | |
7 #include <set> | |
8 | |
9 #include "base/android/jni_string.h" | |
10 #include "base/bind.h" | |
11 #include "base/memory/weak_ptr.h" | |
12 #include "base/strings/utf_string_conversions.h" | |
13 #include "chrome/browser/android/contextualsearch/contextual_search_context.h" | |
14 #include "chrome/browser/profiles/profile_manager.h" | |
15 #include "content/public/browser/render_frame_host.h" | |
16 #include "content/public/browser/web_contents.h" | |
17 #include "jni/SearchAction_jni.h" | |
18 #include "url/gurl.h" | |
19 | |
20 using base::android::JavaParamRef; | |
21 | |
22 namespace { | |
23 | |
24 const int kSurroundingTextSize = 1536; | |
25 const int kSurroundingTextSampleSize = 200; | |
26 } | |
27 | |
28 SearchAction::SearchAction(JNIEnv* env, jobject obj) { | |
29 java_object_.Reset(env, obj); | |
30 } | |
31 | |
32 SearchAction::~SearchAction() { | |
33 LOG(ERROR) << "ctxs ===== native SearchAction::~SearchAction"; | |
34 JNIEnv* env = base::android::AttachCurrentThread(); | |
35 Java_SearchAction_clearNativePointer(env, java_object_.obj()); | |
36 } | |
37 | |
38 void SearchAction::Destroy(JNIEnv* env, const JavaParamRef<jobject>& obj) { | |
39 LOG(ERROR) << "ctxs ===== native SearchAction::Destroy"; | |
40 delete this; | |
41 } | |
42 | |
43 void SearchAction::RequestSurroundingText( | |
44 JNIEnv* env, | |
45 const JavaParamRef<jobject>& obj, | |
46 const JavaParamRef<jobject>& j_web_contents) { | |
47 content::WebContents* web_contents = | |
48 content::WebContents::FromJavaWebContents(j_web_contents.obj()); | |
49 DCHECK(web_contents); | |
50 | |
51 GURL url(web_contents->GetURL()); | |
52 std::string encoding(web_contents->GetEncoding()); | |
53 search_context_.reset(new ContextualSearchContext(true, url, encoding)); | |
54 | |
55 content::RenderFrameHost* focused_frame = web_contents->GetFocusedFrame(); | |
56 if (!focused_frame) { | |
57 OnSurroundingTextResponse(base::string16(), 0, 0); | |
58 } else { | |
59 focused_frame->RequestTextSurroundingSelection( | |
60 base::Bind(&SearchAction::OnSurroundingTextResponse, AsWeakPtr()), | |
61 kSurroundingTextSize); | |
62 } | |
63 } | |
64 | |
65 void SearchAction::OnSurroundingTextResponse( | |
66 const base::string16& surrounding_text, | |
67 int focus_start, | |
68 int focus_end) { | |
69 search_context_->surrounding_text = surrounding_text; | |
70 | |
71 // Find the focused word. | |
72 std::pair<int, int> focus = | |
73 FindFocusedWord(surrounding_text, focus_start, focus_end); | |
74 std::string selected_text = base::UTF16ToUTF8( | |
75 surrounding_text.substr(focus.first, focus.second - focus.first)); | |
76 | |
77 // Trim surroundings to a sample small enough to be suitable to send to Java. | |
78 std::pair<base::string16, int> sample = SampleSurroundings( | |
79 surrounding_text, focus_start, focus_end, kSurroundingTextSampleSize); | |
80 JNIEnv* env = base::android::AttachCurrentThread(); | |
81 | |
82 LOG(ERROR) << "ctxs --- surroundings"; | |
83 LOG(ERROR) << "ctxs --- '" << sample.first << "'"; | |
84 | |
85 base::android::ScopedJavaLocalRef<jstring> j_surrounding_text_sample = | |
86 base::android::ConvertUTF16ToJavaString(env, sample.first); | |
87 | |
88 LOG(ERROR) << "ctxs --- WORD [" << selected_text << "] focus " << focus_start | |
89 << ", " << focus_end << " focused word [" << focus.first << ", " | |
90 << focus.second << ") substring '" << sample.first << "' offset " | |
91 << sample.second; | |
92 | |
93 // Notify Java. | |
94 Java_SearchAction_onSurroundingTextResponse( | |
95 env, java_object_.obj(), j_surrounding_text_sample.obj(), sample.second, | |
96 focus_start, focus_end, focus.first, focus.second); | |
97 } | |
98 | |
99 // static | |
100 std::pair<int, int> SearchAction::FindFocusedWord( | |
101 const base::string16& surrounding_text, | |
102 int focus_start, | |
103 int focus_end) { | |
104 int surrounding_text_length = surrounding_text.length(); | |
105 | |
106 // First, we need to find the focused word boundaries. | |
107 int focused_word_start = focus_start; | |
108 int focused_word_end = focus_end; | |
109 | |
110 if (surrounding_text_length > 0 && | |
111 IsValidCharacter(surrounding_text[focused_word_start])) { | |
112 // Find focused word start (inclusive) | |
113 for (int i = focus_start; i >= 0; i--) { | |
114 focused_word_start = i; | |
115 | |
116 wchar_t character; | |
117 if (i > 0) { | |
118 character = surrounding_text[i - 1]; | |
119 | |
120 if (!IsValidCharacter(character)) | |
121 break; | |
122 } | |
123 } | |
124 | |
125 // Find focused word end (non inclusive) | |
126 for (int i = focus_end; i < surrounding_text_length; i++) { | |
127 wchar_t character = surrounding_text[i]; | |
128 | |
129 if (IsValidCharacter(character)) { | |
130 focused_word_end = i + 1; | |
131 } else { | |
132 focused_word_end = i; | |
133 break; | |
134 } | |
135 } | |
136 } | |
137 | |
138 return std::pair<int, int>(focused_word_start, focused_word_end); | |
139 } | |
140 | |
141 // static | |
142 std::pair<base::string16, int> SearchAction::SampleSurroundings( | |
143 const base::string16& surrounding_text, | |
144 int focus_start, | |
145 int focus_end, | |
146 int surrounding_text_sample_limit) { | |
147 int surrounding_text_length = surrounding_text.length(); | |
148 | |
149 //--------------------------------------------------------------------------- | |
150 // Cut the surrounding size so it can be sent to the Java side. | |
151 | |
152 // Start equally distributing the size around the focal point. | |
153 int focal_point_size = focus_end - focus_start; | |
154 int sample_margin = (surrounding_text_sample_limit - focal_point_size) / 2; | |
155 int sample_start = focus_start - sample_margin; | |
156 int sample_end = focus_end + sample_margin; | |
157 | |
158 // If the the start is out of bounds, compensate the end side. | |
159 if (sample_start < 0) { | |
160 sample_end -= sample_start; | |
161 sample_start = 0; | |
162 } | |
163 | |
164 // If the the end is out of bounds, compensate the start side. | |
165 if (sample_end > surrounding_text_length) { | |
166 int diff = sample_end - surrounding_text_length; | |
167 sample_end = surrounding_text_length; | |
168 sample_start -= diff; | |
169 } | |
170 | |
171 // Trim the start and end to make sure they are within bounds. | |
172 sample_start = std::max(0, sample_start); | |
173 sample_end = std::min(surrounding_text_length, sample_end); | |
174 base::string16 sample_string( | |
175 surrounding_text.substr(sample_start, sample_end - sample_start)); | |
176 std::pair<base::string16, int> sample(sample_string, sample_start); | |
177 return sample; | |
178 } | |
179 | |
180 // static | |
181 bool SearchAction::IsValidCharacter(int char_code) { | |
182 // See http://www.unicode.org/Public/UCD/latest/ucd/NamesList.txt | |
183 // See http://jrgraphix.net/research/unicode_blocks.php | |
184 | |
185 // TODO(pedrosimonetti): should not include CJK characters! | |
Theresa
2016/08/16 15:41:49
Let's be sure to get some manual test coverage on
Donn Denman
2016/08/17 04:35:22
Acknowledged.
Donn Denman
2016/08/17 04:35:22
Pedro, why shouldn't CJK characters be included he
pedro (no code reviews)
2016/08/22 20:54:17
If we include all CJK charactes, as we are doing,
Donn Denman
2016/08/23 23:21:46
Acknowledged.
| |
186 | |
187 if ((char_code >= 11264 && char_code <= 55295) || | |
Theresa
2016/08/16 15:41:49
Can we comment on whta these are as well?
Donn Denman
2016/08/17 04:35:22
Done.
| |
188 (char_code >= 192 && char_code <= 8191) || | |
189 (char_code >= 97 && char_code <= 122) || // Lowercase letters | |
190 (char_code >= 65 && char_code <= 90) || // Uppercase letters | |
191 (char_code >= 48 && char_code <= 57)) { // Numbers | |
192 return true; | |
193 } | |
194 | |
195 return false; | |
196 } | |
197 | |
198 bool RegisterSearchAction(JNIEnv* env) { | |
199 return RegisterNativesImpl(env); | |
200 } | |
201 | |
202 jlong Init(JNIEnv* env, const JavaParamRef<jobject>& obj) { | |
203 SearchAction* content = new SearchAction(env, obj); | |
204 return reinterpret_cast<intptr_t>(content); | |
205 } | |
OLD | NEW |