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 "content/public/browser/render_frame_host.h" | |
15 #include "content/public/browser/web_contents.h" | |
16 #include "jni/SearchAction_jni.h" | |
17 #include "url/gurl.h" | |
18 | |
19 using base::android::ConvertUTF8ToJavaString; | |
20 using base::android::JavaParamRef; | |
21 using base::android::ScopedJavaGlobalRef; | |
22 using content::WebContents; | |
23 | |
24 namespace { | |
25 // Context length is set to 1.5K characters for historical reasons: | |
26 // This provides the largest context that would fit in a GET request along with | |
Theresa
2016/09/02 17:04:07
nit: s/would/will
Donn Denman
2016/09/02 20:48:35
Done slightly differently -- now phrased fully in
| |
27 // our other required parameters." | |
28 const int kSurroundingTextSize = 1536; | |
29 } | |
30 | |
31 SearchAction::SearchAction(JNIEnv* env, jobject obj) { | |
32 java_object_.Reset(env, obj); | |
33 } | |
34 | |
35 SearchAction::~SearchAction() { | |
36 // The Java object can be null in tests only, by calling the default | |
37 // constructor. | |
38 if (!java_object_.is_null()) { | |
39 JNIEnv* env = base::android::AttachCurrentThread(); | |
40 Java_SearchAction_clearNativePointer(env, java_object_.obj()); | |
41 } | |
42 } | |
43 | |
44 void SearchAction::Destroy(JNIEnv* env, const JavaParamRef<jobject>& obj) { | |
45 delete this; | |
46 } | |
47 | |
48 void SearchAction::RequestSurroundingText( | |
49 JNIEnv* env, | |
50 const JavaParamRef<jobject>& obj, | |
51 const JavaParamRef<jobject>& j_web_contents) { | |
52 WebContents* web_contents = | |
53 WebContents::FromJavaWebContents(j_web_contents.obj()); | |
54 DCHECK(web_contents); | |
55 | |
56 GURL url(web_contents->GetURL()); | |
57 std::string encoding(web_contents->GetEncoding()); | |
58 search_context_.reset(new ContextualSearchContext(true, url, encoding)); | |
59 | |
60 content::RenderFrameHost* focused_frame = web_contents->GetFocusedFrame(); | |
61 if (!focused_frame) { | |
62 OnSurroundingTextResponse(base::string16(), 0, 0); | |
63 } else { | |
64 focused_frame->RequestTextSurroundingSelection( | |
65 base::Bind(&SearchAction::OnSurroundingTextResponse, AsWeakPtr()), | |
66 kSurroundingTextSize); | |
67 } | |
68 } | |
69 | |
70 void SearchAction::OnSurroundingTextResponse( | |
71 const base::string16& surrounding_text, | |
72 int focus_start, | |
73 int focus_end) { | |
74 // Record the context, which can be accessed later on demand. | |
75 search_context_->surrounding_text = surrounding_text; | |
76 search_context_->start_offset = focus_start; | |
77 search_context_->end_offset = focus_end; | |
78 | |
79 // Notify Java. | |
80 JNIEnv* env = base::android::AttachCurrentThread(); | |
81 Java_SearchAction_onSurroundingTextReady(env, java_object_.obj()); | |
82 } | |
83 | |
84 std::string SearchAction::FindFocusedWord() { | |
85 DCHECK(search_context_); | |
86 | |
87 const base::string16& surrounding_text = search_context_->surrounding_text; | |
88 int focus_start = search_context_->start_offset; | |
89 int focus_end = search_context_->end_offset; | |
90 int surrounding_text_length = surrounding_text.length(); | |
91 | |
92 // First, we need to find the focused word boundaries. | |
93 int focused_word_start = focus_start; | |
94 int focused_word_end = focus_end; | |
95 | |
96 if (surrounding_text_length > 0 && | |
97 IsValidCharacter(surrounding_text[focused_word_start])) { | |
98 // Find focused word start (inclusive) | |
99 for (int i = focus_start; i >= 0; i--) { | |
100 focused_word_start = i; | |
101 | |
102 wchar_t character; | |
103 if (i > 0) { | |
104 character = surrounding_text[i - 1]; | |
105 | |
106 if (!IsValidCharacter(character)) | |
107 break; | |
108 } | |
109 } | |
110 | |
111 // Find focused word end (non inclusive) | |
112 for (int i = focus_end; i < surrounding_text_length; i++) { | |
113 wchar_t character = surrounding_text[i]; | |
114 | |
115 if (IsValidCharacter(character)) { | |
116 focused_word_end = i + 1; | |
117 } else { | |
118 focused_word_end = i; | |
119 break; | |
120 } | |
121 } | |
122 } | |
123 | |
124 return base::UTF16ToUTF8(surrounding_text.substr( | |
125 focused_word_start, focused_word_end - focused_word_start)); | |
126 } | |
127 | |
128 std::string SearchAction::GetSampleText(int sample_length) { | |
129 DCHECK(search_context_); | |
130 | |
131 const base::string16& surrounding_text = search_context_->surrounding_text; | |
132 int focus_start = search_context_->start_offset; | |
133 int focus_end = search_context_->end_offset; | |
134 int surrounding_text_length = surrounding_text.length(); | |
135 | |
136 //--------------------------------------------------------------------------- | |
137 // Cut the surrounding size so it can be sent to the Java side. | |
138 | |
139 // Start equally distributing the size around the focal point. | |
140 int focal_point_size = focus_end - focus_start; | |
141 int sample_margin = (sample_length - focal_point_size) / 2; | |
142 int sample_start = focus_start - sample_margin; | |
143 int sample_end = focus_end + sample_margin; | |
144 | |
145 // If the the start is out of bounds, compensate the end side. | |
146 if (sample_start < 0) { | |
147 sample_end -= sample_start; | |
148 sample_start = 0; | |
149 } | |
150 | |
151 // If the the end is out of bounds, compensate the start side. | |
152 if (sample_end > surrounding_text_length) { | |
153 int diff = sample_end - surrounding_text_length; | |
154 sample_end = surrounding_text_length; | |
155 sample_start -= diff; | |
156 } | |
157 | |
158 // Trim the start and end to make sure they are within bounds. | |
159 sample_start = std::max(0, sample_start); | |
160 sample_end = std::min(surrounding_text_length, sample_end); | |
161 return base::UTF16ToUTF8( | |
162 surrounding_text.substr(sample_start, sample_end - sample_start)); | |
163 } | |
164 | |
165 bool SearchAction::IsValidCharacter(int char_code) { | |
166 // See http://www.unicode.org/Public/UCD/latest/ucd/NamesList.txt | |
167 // See http://jrgraphix.net/research/unicode_blocks.php | |
168 | |
169 // TODO(donnd): should not include CJK characters! | |
170 // TODO(donnd): consider using regular expressions. | |
171 | |
172 if ((char_code >= 11264 && char_code <= 55295) || // Asian language symbols | |
173 (char_code >= 192 && char_code <= 8191) || // Letters in many languages | |
174 (char_code >= 97 && char_code <= 122) || // Lowercase letters | |
175 (char_code >= 65 && char_code <= 90) || // Uppercase letters | |
176 (char_code >= 48 && char_code <= 57)) { // Numbers | |
177 return true; | |
178 } | |
179 | |
180 return false; | |
181 } | |
182 | |
183 // Testing | |
184 | |
185 SearchAction::SearchAction() {} | |
186 | |
187 void SearchAction::SetContext(std::string surrounding_text, | |
188 int focus_start, | |
189 int focus_end) { | |
190 search_context_.reset(new ContextualSearchContext(false, GURL(), "")); | |
191 search_context_->surrounding_text = base::UTF8ToUTF16(surrounding_text); | |
192 search_context_->start_offset = focus_start; | |
193 search_context_->end_offset = focus_end; | |
194 } | |
195 | |
196 bool RegisterSearchAction(JNIEnv* env) { | |
197 return RegisterNativesImpl(env); | |
198 } | |
199 | |
200 jlong Init(JNIEnv* env, const JavaParamRef<jobject>& obj) { | |
201 SearchAction* content = new SearchAction(env, obj); | |
202 return reinterpret_cast<intptr_t>(content); | |
203 } | |
OLD | NEW |