Chromium Code Reviews| Index: chrome/browser/android/contextualsearch/search_action.cc |
| diff --git a/chrome/browser/android/contextualsearch/search_action.cc b/chrome/browser/android/contextualsearch/search_action.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..67db3a7f6f58c5d794e875b36965b9b29bcf5125 |
| --- /dev/null |
| +++ b/chrome/browser/android/contextualsearch/search_action.cc |
| @@ -0,0 +1,205 @@ |
| +// Copyright 2016 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. |
| + |
| +#include "chrome/browser/android/contextualsearch/search_action.h" |
| + |
| +#include <set> |
| + |
| +#include "base/android/jni_string.h" |
| +#include "base/bind.h" |
| +#include "base/memory/weak_ptr.h" |
| +#include "base/strings/utf_string_conversions.h" |
| +#include "chrome/browser/android/contextualsearch/contextual_search_context.h" |
| +#include "chrome/browser/profiles/profile_manager.h" |
| +#include "content/public/browser/render_frame_host.h" |
| +#include "content/public/browser/web_contents.h" |
| +#include "jni/SearchAction_jni.h" |
| +#include "url/gurl.h" |
| + |
| +using base::android::JavaParamRef; |
| + |
| +namespace { |
| + |
| +const int kSurroundingTextSize = 1536; |
| +const int kSurroundingTextSampleSize = 200; |
| +} |
| + |
| +SearchAction::SearchAction(JNIEnv* env, jobject obj) { |
| + java_object_.Reset(env, obj); |
| +} |
| + |
| +SearchAction::~SearchAction() { |
| + LOG(ERROR) << "ctxs ===== native SearchAction::~SearchAction"; |
| + JNIEnv* env = base::android::AttachCurrentThread(); |
| + Java_SearchAction_clearNativePointer(env, java_object_.obj()); |
| +} |
| + |
| +void SearchAction::Destroy(JNIEnv* env, const JavaParamRef<jobject>& obj) { |
| + LOG(ERROR) << "ctxs ===== native SearchAction::Destroy"; |
| + delete this; |
| +} |
| + |
| +void SearchAction::RequestSurroundingText( |
| + JNIEnv* env, |
| + const JavaParamRef<jobject>& obj, |
| + const JavaParamRef<jobject>& j_web_contents) { |
| + content::WebContents* web_contents = |
| + content::WebContents::FromJavaWebContents(j_web_contents.obj()); |
| + DCHECK(web_contents); |
| + |
| + GURL url(web_contents->GetURL()); |
| + std::string encoding(web_contents->GetEncoding()); |
| + search_context_.reset(new ContextualSearchContext(true, url, encoding)); |
| + |
| + content::RenderFrameHost* focused_frame = web_contents->GetFocusedFrame(); |
| + if (!focused_frame) { |
| + OnSurroundingTextResponse(base::string16(), 0, 0); |
| + } else { |
| + focused_frame->RequestTextSurroundingSelection( |
| + base::Bind(&SearchAction::OnSurroundingTextResponse, AsWeakPtr()), |
| + kSurroundingTextSize); |
| + } |
| +} |
| + |
| +void SearchAction::OnSurroundingTextResponse( |
| + const base::string16& surrounding_text, |
| + int focus_start, |
| + int focus_end) { |
| + search_context_->surrounding_text = surrounding_text; |
| + |
| + // Find the focused word. |
| + std::pair<int, int> focus = |
| + FindFocusedWord(surrounding_text, focus_start, focus_end); |
| + std::string selected_text = base::UTF16ToUTF8( |
| + surrounding_text.substr(focus.first, focus.second - focus.first)); |
| + |
| + // Trim surroundings to a sample small enough to be suitable to send to Java. |
| + std::pair<base::string16, int> sample = SampleSurroundings( |
| + surrounding_text, focus_start, focus_end, kSurroundingTextSampleSize); |
| + JNIEnv* env = base::android::AttachCurrentThread(); |
| + |
| + LOG(ERROR) << "ctxs --- surroundings"; |
| + LOG(ERROR) << "ctxs --- '" << sample.first << "'"; |
| + |
| + base::android::ScopedJavaLocalRef<jstring> j_surrounding_text_sample = |
| + base::android::ConvertUTF16ToJavaString(env, sample.first); |
| + |
| + LOG(ERROR) << "ctxs --- WORD [" << selected_text << "] focus " << focus_start |
| + << ", " << focus_end << " focused word [" << focus.first << ", " |
| + << focus.second << ") substring '" << sample.first << "' offset " |
| + << sample.second; |
| + |
| + // Notify Java. |
| + Java_SearchAction_onSurroundingTextResponse( |
| + env, java_object_.obj(), j_surrounding_text_sample.obj(), sample.second, |
| + focus_start, focus_end, focus.first, focus.second); |
| +} |
| + |
| +// static |
| +std::pair<int, int> SearchAction::FindFocusedWord( |
| + const base::string16& surrounding_text, |
| + int focus_start, |
| + int focus_end) { |
| + int surrounding_text_length = surrounding_text.length(); |
| + |
| + // First, we need to find the focused word boundaries. |
| + int focused_word_start = focus_start; |
| + int focused_word_end = focus_end; |
| + |
| + if (surrounding_text_length > 0 && |
| + IsValidCharacter(surrounding_text[focused_word_start])) { |
| + // Find focused word start (inclusive) |
| + for (int i = focus_start; i >= 0; i--) { |
| + focused_word_start = i; |
| + |
| + wchar_t character; |
| + if (i > 0) { |
| + character = surrounding_text[i - 1]; |
| + |
| + if (!IsValidCharacter(character)) |
| + break; |
| + } |
| + } |
| + |
| + // Find focused word end (non inclusive) |
| + for (int i = focus_end; i < surrounding_text_length; i++) { |
| + wchar_t character = surrounding_text[i]; |
| + |
| + if (IsValidCharacter(character)) { |
| + focused_word_end = i + 1; |
| + } else { |
| + focused_word_end = i; |
| + break; |
| + } |
| + } |
| + } |
| + |
| + return std::pair<int, int>(focused_word_start, focused_word_end); |
| +} |
| + |
| +// static |
| +std::pair<base::string16, int> SearchAction::SampleSurroundings( |
| + const base::string16& surrounding_text, |
| + int focus_start, |
| + int focus_end, |
| + int surrounding_text_sample_limit) { |
| + int surrounding_text_length = surrounding_text.length(); |
| + |
| + //--------------------------------------------------------------------------- |
| + // Cut the surrounding size so it can be sent to the Java side. |
| + |
| + // Start equally distributing the size around the focal point. |
| + int focal_point_size = focus_end - focus_start; |
| + int sample_margin = (surrounding_text_sample_limit - focal_point_size) / 2; |
| + int sample_start = focus_start - sample_margin; |
| + int sample_end = focus_end + sample_margin; |
| + |
| + // If the the start is out of bounds, compensate the end side. |
| + if (sample_start < 0) { |
| + sample_end -= sample_start; |
| + sample_start = 0; |
| + } |
| + |
| + // If the the end is out of bounds, compensate the start side. |
| + if (sample_end > surrounding_text_length) { |
| + int diff = sample_end - surrounding_text_length; |
| + sample_end = surrounding_text_length; |
| + sample_start -= diff; |
| + } |
| + |
| + // Trim the start and end to make sure they are within bounds. |
| + sample_start = std::max(0, sample_start); |
| + sample_end = std::min(surrounding_text_length, sample_end); |
| + base::string16 sample_string( |
| + surrounding_text.substr(sample_start, sample_end - sample_start)); |
| + std::pair<base::string16, int> sample(sample_string, sample_start); |
| + return sample; |
| +} |
| + |
| +// static |
| +bool SearchAction::IsValidCharacter(int char_code) { |
| + // See http://www.unicode.org/Public/UCD/latest/ucd/NamesList.txt |
| + // See http://jrgraphix.net/research/unicode_blocks.php |
| + |
| + // 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.
|
| + |
| + 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.
|
| + (char_code >= 192 && char_code <= 8191) || |
| + (char_code >= 97 && char_code <= 122) || // Lowercase letters |
| + (char_code >= 65 && char_code <= 90) || // Uppercase letters |
| + (char_code >= 48 && char_code <= 57)) { // Numbers |
| + return true; |
| + } |
| + |
| + return false; |
| +} |
| + |
| +bool RegisterSearchAction(JNIEnv* env) { |
| + return RegisterNativesImpl(env); |
| +} |
| + |
| +jlong Init(JNIEnv* env, const JavaParamRef<jobject>& obj) { |
| + SearchAction* content = new SearchAction(env, obj); |
| + return reinterpret_cast<intptr_t>(content); |
| +} |