File: spellchecker_session_bridge_android.cc

package info (click to toggle)
chromium 90.0.4430.212-1~deb10u1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 3,450,632 kB
  • sloc: cpp: 19,832,434; javascript: 2,948,838; ansic: 2,312,399; python: 1,464,622; xml: 584,121; java: 514,189; asm: 470,557; objc: 83,463; perl: 77,861; sh: 77,030; cs: 70,789; fortran: 24,137; tcl: 18,916; php: 18,872; makefile: 16,848; ruby: 16,721; pascal: 13,150; sql: 10,199; yacc: 7,507; lex: 1,313; lisp: 840; awk: 329; jsp: 39; sed: 19
file content (152 lines) | stat: -rw-r--r-- 5,562 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// Copyright 2015 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 "components/spellcheck/browser/spellchecker_session_bridge_android.h"

#include <stddef.h>
#include <utility>

#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/metrics/histogram_macros.h"
#include "components/spellcheck/browser/android/jni_headers/SpellCheckerSessionBridge_jni.h"
#include "components/spellcheck/common/spellcheck_result.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"

using base::android::JavaParamRef;

namespace {

void RecordAvailabilityUMA(bool spellcheck_available) {
  UMA_HISTOGRAM_BOOLEAN("Spellcheck.Android.Available", spellcheck_available);
}

}  // namespace

SpellCheckerSessionBridge::SpellCheckerSessionBridge()
    : java_object_initialization_failed_(false), active_session_(false) {}

SpellCheckerSessionBridge::~SpellCheckerSessionBridge() {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  // Clean-up java side to avoid any stale JNI callbacks.
  DisconnectSession();
}

void SpellCheckerSessionBridge::RequestTextCheck(
    const base::string16& text,
    RequestTextCheckCallback callback) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  // This allows us to discard |callback| safely in case it's not run due to
  // failures in initialization of |java_object_|.
  std::unique_ptr<SpellingRequest> incoming_request =
      std::make_unique<SpellingRequest>(text, std::move(callback));

  // SpellCheckerSessionBridge#create() will return null if spell checker
  // service is unavailable.
  if (java_object_initialization_failed_) {
    if (!active_session_) {
      RecordAvailabilityUMA(false);
      active_session_ = true;
    }
    return;
  }

  // RequestTextCheck API call arrives at the SpellCheckHost before
  // DisconnectSessionBridge when the user focuses an input field that already
  // contains completed text.  We need to initialize the spellchecker here
  // rather than in response to DisconnectSessionBridge so that the existing
  // text will be spellchecked immediately.
  if (java_object_.is_null()) {
    java_object_.Reset(Java_SpellCheckerSessionBridge_create(
        base::android::AttachCurrentThread(),
        reinterpret_cast<intptr_t>(this)));
    if (!active_session_) {
      RecordAvailabilityUMA(!java_object_.is_null());
      active_session_ = true;
    }
    if (java_object_.is_null()) {
      java_object_initialization_failed_ = true;
      return;
    }
  }

  // Save incoming requests to run at the end of the currently active request.
  // If multiple requests arrive during one active request, only the most
  // recent request will run (the others get overwritten).
  if (active_request_) {
    pending_request_ = std::move(incoming_request);
    return;
  }

  active_request_ = std::move(incoming_request);

  JNIEnv* env = base::android::AttachCurrentThread();
  Java_SpellCheckerSessionBridge_requestTextCheck(
      env, java_object_, base::android::ConvertUTF16ToJavaString(env, text));
}

void SpellCheckerSessionBridge::ProcessSpellCheckResults(
    JNIEnv* env,
    const JavaParamRef<jobject>& jobj,
    const JavaParamRef<jintArray>& offset_array,
    const JavaParamRef<jintArray>& length_array,
    const JavaParamRef<jobjectArray>& suggestions_array) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  std::vector<int> offsets;
  std::vector<int> lengths;

  base::android::JavaIntArrayToIntVector(env, offset_array, &offsets);
  base::android::JavaIntArrayToIntVector(env, length_array, &lengths);

  std::vector<SpellCheckResult> results;
  for (size_t i = 0; i < offsets.size(); i++) {
    base::android::ScopedJavaLocalRef<jobjectArray> suggestions_for_word_array(
        env, static_cast<jobjectArray>(
                 env->GetObjectArrayElement(suggestions_array, i)));
    std::vector<base::string16> suggestions_for_word;
    base::android::AppendJavaStringArrayToStringVector(
        env, suggestions_for_word_array, &suggestions_for_word);
    results.push_back(SpellCheckResult(SpellCheckResult::SPELLING, offsets[i],
                                       lengths[i], suggestions_for_word));
  }

  std::move(active_request_->callback_).Run(results);

  active_request_ = std::move(pending_request_);
  if (active_request_) {
    JNIEnv* env = base::android::AttachCurrentThread();
    Java_SpellCheckerSessionBridge_requestTextCheck(
        env, java_object_,
        base::android::ConvertUTF16ToJavaString(env, active_request_->text_));
  }
}

void SpellCheckerSessionBridge::DisconnectSession() {
  // Needs to be executed on the same thread as the RequestTextCheck and
  // ProcessSpellCheckResults methods, which is the UI thread.
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  active_request_.reset();
  pending_request_.reset();
  active_session_ = false;

  if (!java_object_.is_null()) {
    Java_SpellCheckerSessionBridge_disconnect(
        base::android::AttachCurrentThread(), java_object_);
    java_object_.Reset();
  }
}

SpellCheckerSessionBridge::SpellingRequest::SpellingRequest(
    const base::string16& text,
    RequestTextCheckCallback callback)
    : text_(text), callback_(std::move(callback)) {}

SpellCheckerSessionBridge::SpellingRequest::~SpellingRequest() {
  // Ensure that we don't clear an uncalled RequestTextCheckCallback
  if (callback_)
    std::move(callback_).Run(std::vector<SpellCheckResult>());
}