blob: b18415c30b0346686956d436655507ad7f74abed [file] [log] [blame]
[email protected]02798a982012-01-27 00:45:331// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]85d252e2010-04-06 22:21:022// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Doug Arnett2fd6cf22020-01-27 19:18:405#include "components/translate/content/renderer/translate_agent.h"
[email protected]85d252e2010-04-06 22:21:026
Scott Littlef130e8e82020-08-07 16:01:377#include <stddef.h>
Jan Wilken Dörriead587c32021-03-11 14:09:278#include <string>
tzik9a17d7af2017-05-09 14:21:589#include <utility>
10
[email protected]81c9c662011-10-12 04:27:0111#include "base/bind.h"
Hans Wennborgdf87046c2020-04-28 11:06:2412#include "base/check_op.h"
[email protected]85d252e2010-04-06 22:21:0213#include "base/compiler_specific.h"
Jon Nappera7dc1b002017-06-26 01:01:2114#include "base/json/string_escape.h"
skyostilb0daa012015-06-02 19:03:4815#include "base/location.h"
asvitkinea0f05db2015-06-16 21:45:4616#include "base/metrics/histogram_macros.h"
mcrouse49738942021-04-15 21:24:2517#include "base/metrics/histogram_macros_local.h"
Hans Wennborgdf87046c2020-04-28 11:06:2418#include "base/notreached.h"
skyostilb0daa012015-06-02 19:03:4819#include "base/single_thread_task_runner.h"
[email protected]cbde0d92013-06-14 11:37:0420#include "base/strings/string_util.h"
[email protected]abfd1492013-06-07 21:23:2621#include "base/strings/utf_string_conversions.h"
Karandeep Bhatia4a8b4352020-07-16 06:57:1322#include "components/translate/content/renderer/isolated_world_util.h"
[email protected]eba93c92014-01-07 17:34:1723#include "components/translate/core/common/translate_constants.h"
24#include "components/translate/core/common/translate_metrics.h"
25#include "components/translate/core/common/translate_util.h"
Michael Crouse11e2a152020-12-12 20:50:3426#include "components/translate/core/language_detection/language_detection_model.h"
[email protected]bfa4c82a2014-05-13 23:45:0227#include "components/translate/core/language_detection/language_detection_util.h"
[email protected]9ce237042014-07-17 18:09:5628#include "content/public/common/content_constants.h"
drogerf1da1802014-09-17 14:54:0529#include "content/public/common/url_constants.h"
dglazkov24814772015-09-25 22:27:4730#include "content/public/renderer/render_frame.h"
[email protected]9ce237042014-07-17 18:09:5631#include "content/public/renderer/render_thread.h"
Mario Sanchez Prada8cbe4aa2019-12-03 10:00:2032#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
Blink Reformata30d4232018-04-07 15:31:0633#include "third_party/blink/public/web/web_document.h"
34#include "third_party/blink/public/web/web_language_detection_details.h"
35#include "third_party/blink/public/web/web_local_frame.h"
36#include "third_party/blink/public/web/web_script_source.h"
[email protected]afa9cd922013-07-30 06:12:1937#include "url/gurl.h"
mcrouse0d096d32021-02-11 01:58:4638#include "url/url_constants.h"
[email protected]4d51d5bf2010-07-26 18:48:2639#include "v8/include/v8.h"
[email protected]85d252e2010-04-06 22:21:0240
[email protected]a1221aea2013-11-07 01:31:3041using blink::WebDocument;
Doug Arnett2fd6cf22020-01-27 19:18:4042using blink::WebLanguageDetectionDetails;
dglazkov24814772015-09-25 22:27:4743using blink::WebLocalFrame;
[email protected]a1221aea2013-11-07 01:31:3044using blink::WebScriptSource;
[email protected]a1221aea2013-11-07 01:31:3045using blink::WebString;
46using blink::WebVector;
[email protected]85d252e2010-04-06 22:21:0247
[email protected]ff9b1c12013-02-07 11:40:1348namespace {
49
[email protected]02798a982012-01-27 00:45:3350// The delay in milliseconds that we'll wait before checking to see if the
[email protected]85d252e2010-04-06 22:21:0251// translate library injected in the page is ready.
[email protected]ff9b1c12013-02-07 11:40:1352const int kTranslateInitCheckDelayMs = 150;
[email protected]85d252e2010-04-06 22:21:0253
54// The maximum number of times we'll check to see if the translate library
55// injected in the page is ready.
[email protected]ff9b1c12013-02-07 11:40:1356const int kMaxTranslateInitCheckAttempts = 5;
[email protected]85d252e2010-04-06 22:21:0257
58// The delay we wait in milliseconds before checking whether the translation has
59// finished.
[email protected]ff9b1c12013-02-07 11:40:1360const int kTranslateStatusCheckDelayMs = 400;
[email protected]85d252e2010-04-06 22:21:0261
[email protected]d64b07b2010-04-20 22:14:0662// Language name passed to the Translate element for it to detect the language.
[email protected]33281252013-04-15 14:46:4963const char kAutoDetectionLanguage[] = "auto";
Michael Crouse11e2a152020-12-12 20:50:3464
mcrousee29a9f12021-02-17 19:06:2965// The current CLD model version.
66constexpr char kCLDModelVersion[] = "CLD3";
67
Michael Crouse11e2a152020-12-12 20:50:3468// Returns the language detection model that is shared across the RenderFrames
69// in the renderer.
70translate::LanguageDetectionModel& GetLanguageDetectionModel() {
71 static base::NoDestructor<translate::LanguageDetectionModel> instance;
72 return *instance;
73}
74
[email protected]ff9b1c12013-02-07 11:40:1375} // namespace
[email protected]d64b07b2010-04-20 22:14:0676
drogerf1da1802014-09-17 14:54:0577namespace translate {
[email protected]e5a7a2a2014-03-27 16:16:0378
[email protected]85d252e2010-04-06 22:21:0279////////////////////////////////////////////////////////////////////////////////
Doug Arnett2fd6cf22020-01-27 19:18:4080// TranslateAgent, public:
81TranslateAgent::TranslateAgent(content::RenderFrame* render_frame,
82 int world_id,
83 const std::string& extension_scheme)
dglazkov24814772015-09-25 22:27:4784 : content::RenderFrameObserver(render_frame),
drogerf1da1802014-09-17 14:54:0585 world_id_(world_id),
mcrouse49738942021-04-15 21:24:2586 extension_scheme_(extension_scheme),
87 waiting_for_first_foreground_(render_frame->IsHidden()) {
Karolina Soltysc7fbc032018-10-25 10:10:0888 translate_task_runner_ = this->render_frame()->GetTaskRunner(
89 blink::TaskType::kInternalTranslation);
mcrouse0d096d32021-02-11 01:58:4690
mcrouse49738942021-04-15 21:24:2591 if (!translate::IsTFLiteLanguageDetectionEnabled()) {
92 return;
93 }
94
95 translate::LanguageDetectionModel& language_detection_model =
96 GetLanguageDetectionModel();
97
98 // If the language detection model is available, we do not
99 // worry about requesting the model.
100 if (language_detection_model.IsAvailable()) {
101 return;
102 }
103
104 LOCAL_HISTOGRAM_BOOLEAN(
105 "LanguageDetection.TFLiteModel.WasModelRequestDeferred",
106 waiting_for_first_foreground_);
107
108 // Ensure the render frame is visible, otherwise the browser-side
109 // translate driver may not exist yet (https://crbug.com/1199397).
110 if (!waiting_for_first_foreground_) {
111 GetTranslateHandler()->GetLanguageDetectionModel(
112 base::BindOnce(&TranslateAgent::UpdateLanguageDetectionModel,
113 weak_pointer_factory_.GetWeakPtr()));
mcrouse0d096d32021-02-11 01:58:46114 }
Karolina Soltysc7fbc032018-10-25 10:10:08115}
[email protected]85d252e2010-04-06 22:21:02116
Doug Arnett2fd6cf22020-01-27 19:18:40117TranslateAgent::~TranslateAgent() {}
[email protected]601858c02010-09-01 17:08:20118
mcrouse49738942021-04-15 21:24:25119void TranslateAgent::WasShown() {
120 // Check if the the render frame was initially hidden and
121 // the model request was delayed until the frame was in
122 // the foreground.
123 if (!waiting_for_first_foreground_) {
124 return;
125 }
126
127 waiting_for_first_foreground_ = false;
128
129 if (!translate::IsTFLiteLanguageDetectionEnabled()) {
130 return;
131 }
132
133 translate::LanguageDetectionModel& language_detection_model =
134 GetLanguageDetectionModel();
135 if (language_detection_model.IsAvailable()) {
136 return;
137 }
138 // The model request was deferred because the frame was hidden
139 // and now the model is visible and the model is still not available.
140 // The browser-side translate driver should always be available at
141 // this point so we should make the request and race to get the
142 // model loaded for when the page content is available.
143 GetTranslateHandler()->GetLanguageDetectionModel(
144 base::BindOnce(&TranslateAgent::UpdateLanguageDetectionModel,
145 weak_pointer_factory_.GetWeakPtr()));
146}
147
Doug Arnett2fd6cf22020-01-27 19:18:40148void TranslateAgent::PrepareForUrl(const GURL& url) {
leon.hanb07801312016-08-12 11:47:57149 // Navigated to a new url, reset current page translation.
150 ResetPage();
[email protected]e5a7a2a2014-03-27 16:16:03151}
152
Jan Wilken Dörriefa241ba2021-03-11 17:57:01153void TranslateAgent::PageCaptured(const std::u16string& contents) {
[email protected]256897732012-10-16 19:35:05154 // Get the document language as set by WebKit from the http-equiv
155 // meta tag for "content-language". This may or may not also
156 // have a value derived from the actual Content-Language HTTP
157 // header. The two actually have different meanings (despite the
158 // original intent of http-equiv to be an equivalent) with the former
159 // being the language of the document and the latter being the
160 // language of the intended audience (a distinction really only
mcrouse0d096d32021-02-11 01:58:46161 // relevant for things like language textbooks). This distinction
[email protected]256897732012-10-16 19:35:05162 // shouldn't affect translation.
dglazkov24814772015-09-25 22:27:47163 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
leon.han3134fc62016-08-10 07:25:58164 if (!main_frame)
[email protected]effa54c2013-05-28 11:14:23165 return;
[email protected]e5a7a2a2014-03-27 16:16:03166
Blink Reformat1c4d759e2017-04-09 16:34:54167 WebDocument document = main_frame->GetDocument();
mcrouse23992f12021-05-07 22:01:25168 GURL url = GURL(document.Url());
169 // Limit detection to URLs that only detect the language of the content if the
170 // page is potentially a candidate for translation. This should be strictly a
171 // subset of the conditions in TranslateService::IsTranslatableURL, however,
172 // due to layering they cannot be identical. Critically, this list should
173 // never filter anything that is eligible for translation. Under filtering is
174 // ok as the translate service will make the final call and only results in a
175 // slight overhead in running the model when unnecessary.
176 if (url.is_empty() || url.SchemeIs(content::kChromeUIScheme) ||
177 url.SchemeIs(content::kChromeDevToolsScheme) || url.IsAboutBlank() ||
178 url.SchemeIs(url::kFtpScheme)) {
179 return;
180 }
181
Megan Jablonski27785782021-06-23 23:08:23182 page_contents_length_ = contents.size();
183
adithyas5e615282017-01-17 21:27:01184 WebLanguageDetectionDetails web_detection_details =
Blink Reformat1c4d759e2017-04-09 16:34:54185 WebLanguageDetectionDetails::CollectLanguageDetectionDetails(document);
186 std::string content_language = web_detection_details.content_language.Utf8();
187 std::string html_lang = web_detection_details.html_language.Utf8();
Michael Crouse11e2a152020-12-12 20:50:34188 std::string model_detected_language;
Michael Crouse30c287a12020-12-14 23:26:01189 bool is_model_reliable = false;
mcrousee29a9f12021-02-17 19:06:29190 std::string detection_model_version;
191 float model_reliability_score = 0.0;
Michael Crouse11e2a152020-12-12 20:50:34192
193 std::string language;
194 if (translate::IsTFLiteLanguageDetectionEnabled()) {
Michael Crouse11e2a152020-12-12 20:50:34195 translate::LanguageDetectionModel& language_detection_model =
196 GetLanguageDetectionModel();
197 bool is_available = language_detection_model.IsAvailable();
198 language = is_available ? language_detection_model.DeterminePageLanguage(
199 content_language, html_lang, contents,
mcrousee29a9f12021-02-17 19:06:29200 &model_detected_language, &is_model_reliable,
201 model_reliability_score)
Michael Crouse11e2a152020-12-12 20:50:34202 : translate::kUnknownLanguageCode;
mcrouseca5021c2021-02-19 00:34:14203 UMA_HISTOGRAM_BOOLEAN(
Michael Crouse11e2a152020-12-12 20:50:34204 "LanguageDetection.TFLiteModel.WasModelAvailableForDetection",
205 is_available);
mcrousee29a9f12021-02-17 19:06:29206 detection_model_version = language_detection_model.GetModelVersion();
Michael Crouse11e2a152020-12-12 20:50:34207 } else {
mcrousee29a9f12021-02-17 19:06:29208 language = DeterminePageLanguage(
209 content_language, html_lang, contents, &model_detected_language,
210 &is_model_reliable, model_reliability_score);
211 detection_model_version = kCLDModelVersion;
Michael Crouse11e2a152020-12-12 20:50:34212 }
[email protected]27eff892013-05-21 16:40:52213
214 if (language.empty())
215 return;
[email protected]93b9d692011-04-13 00:44:31216
[email protected]0f6afb42013-05-17 03:44:47217 language_determined_time_ = base::TimeTicks::Now();
218
Michael Crouse11e2a152020-12-12 20:50:34219 // TODO(crbug.com/1157983): Update the language detection details struct to be
220 // model agnostic.
drogerf1da1802014-09-17 14:54:05221 LanguageDetectionDetails details;
[email protected]27eff892013-05-21 16:40:52222 details.time = base::Time::Now();
adithyas5e615282017-01-17 21:27:01223 details.url = web_detection_details.url;
[email protected]27eff892013-05-21 16:40:52224 details.content_language = content_language;
Michael Crouse0ebf0d52020-12-14 23:29:31225 details.model_detected_language = model_detected_language;
226 details.is_model_reliable = is_model_reliable;
Blink Reformat1c4d759e2017-04-09 16:34:54227 details.has_notranslate = web_detection_details.has_no_translate_meta;
[email protected]20efbf2f2013-05-31 06:42:37228 details.html_root_language = html_lang;
[email protected]27eff892013-05-21 16:40:52229 details.adopted_language = language;
mcrousee29a9f12021-02-17 19:06:29230 details.model_reliability_score = model_reliability_score;
231 details.detection_model_version = detection_model_version;
[email protected]27eff892013-05-21 16:40:52232
[email protected]d861d972013-05-30 06:02:31233 // TODO(hajimehoshi): If this affects performance, it should be set only if
234 // translate-internals tab exists.
235 details.contents = contents;
236
leon.han3134fc62016-08-10 07:25:58237 // For the same render frame with the same url, each time when its texts are
238 // captured, it should be treated as a new page to do translation.
leon.hanb07801312016-08-12 11:47:57239 ResetPage();
Michael Martis088a00a2018-04-12 09:33:05240 GetTranslateHandler()->RegisterPage(
Julie Jeongeun Kim9b734c52019-09-25 16:19:56241 receiver_.BindNewPipeAndPassRemote(
242 main_frame->GetTaskRunner(blink::TaskType::kInternalTranslation)),
243 details, !details.has_notranslate && !language.empty());
[email protected]93b9d692011-04-13 00:44:31244}
245
Doug Arnett2fd6cf22020-01-27 19:18:40246void TranslateAgent::CancelPendingTranslation() {
[email protected]81c9c662011-10-12 04:27:01247 weak_method_factory_.InvalidateWeakPtrs();
leon.han3134fc62016-08-10 07:25:58248 // Make sure to send the cancelled response back.
249 if (translate_callback_pending_) {
tzik9a17d7af2017-05-09 14:21:58250 std::move(translate_callback_pending_)
251 .Run(true, source_lang_, target_lang_, TranslateErrors::NONE);
leon.han3134fc62016-08-10 07:25:58252 }
[email protected]d64b07b2010-04-20 22:14:06253 source_lang_.clear();
254 target_lang_.clear();
255}
256
[email protected]85d252e2010-04-06 22:21:02257////////////////////////////////////////////////////////////////////////////////
Doug Arnett2fd6cf22020-01-27 19:18:40258// TranslateAgent, protected:
259bool TranslateAgent::IsTranslateLibAvailable() {
[email protected]0dfc0e52013-04-15 14:53:00260 return ExecuteScriptAndGetBoolResult(
[email protected]85d252e2010-04-06 22:21:02261 "typeof cr != 'undefined' && typeof cr.googleTranslate != 'undefined' && "
Doug Arnett2fd6cf22020-01-27 19:18:40262 "typeof cr.googleTranslate.translate == 'function'",
263 false);
[email protected]85d252e2010-04-06 22:21:02264}
265
Doug Arnett2fd6cf22020-01-27 19:18:40266bool TranslateAgent::IsTranslateLibReady() {
[email protected]0dfc0e52013-04-15 14:53:00267 return ExecuteScriptAndGetBoolResult("cr.googleTranslate.libReady", false);
[email protected]85d252e2010-04-06 22:21:02268}
269
Doug Arnett2fd6cf22020-01-27 19:18:40270bool TranslateAgent::HasTranslationFinished() {
[email protected]0dfc0e52013-04-15 14:53:00271 return ExecuteScriptAndGetBoolResult("cr.googleTranslate.finished", true);
[email protected]85d252e2010-04-06 22:21:02272}
273
Doug Arnett2fd6cf22020-01-27 19:18:40274bool TranslateAgent::HasTranslationFailed() {
[email protected]0dfc0e52013-04-15 14:53:00275 return ExecuteScriptAndGetBoolResult("cr.googleTranslate.error", true);
[email protected]85d252e2010-04-06 22:21:02276}
277
Doug Arnett2fd6cf22020-01-27 19:18:40278int64_t TranslateAgent::GetErrorCode() {
gajendra.n5bcb90a2017-07-05 09:02:52279 int64_t error_code =
280 ExecuteScriptAndGetIntegerResult("cr.googleTranslate.errorCode");
281 DCHECK_LT(error_code, static_cast<int>(TranslateErrors::TRANSLATE_ERROR_MAX));
282 return error_code;
283}
284
Doug Arnett2fd6cf22020-01-27 19:18:40285bool TranslateAgent::StartTranslation() {
Jon Nappera7dc1b002017-06-26 01:01:21286 return ExecuteScriptAndGetBoolResult(
287 BuildTranslationScript(source_lang_, target_lang_), false);
[email protected]85d252e2010-04-06 22:21:02288}
289
Evan Stade157a9212021-04-20 00:09:14290std::string TranslateAgent::GetPageSourceLanguage() {
[email protected]0dfc0e52013-04-15 14:53:00291 return ExecuteScriptAndGetStringResult("cr.googleTranslate.sourceLang");
[email protected]d64b07b2010-04-20 22:14:06292}
293
Doug Arnettc1436ac2020-04-08 03:10:05294base::TimeDelta TranslateAgent::AdjustDelay(int delay_in_milliseconds) {
295 // Just converts |delay_in_milliseconds| without any modification in practical
296 // cases. Tests will override this function to return modified value.
297 return base::TimeDelta::FromMilliseconds(delay_in_milliseconds);
[email protected]e4be2dd2010-12-14 00:44:39298}
299
Doug Arnett2fd6cf22020-01-27 19:18:40300void TranslateAgent::ExecuteScript(const std::string& script) {
dglazkov24814772015-09-25 22:27:47301 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
[email protected]afa9cd922013-07-30 06:12:19302 if (!main_frame)
303 return;
304
Blink Reformat1c4d759e2017-04-09 16:34:54305 WebScriptSource source = WebScriptSource(WebString::FromASCII(script));
Dave Tapuska306a72402021-04-09 14:20:02306 main_frame->ExecuteScriptInIsolatedWorld(
307 world_id_, source, blink::BackForwardCacheAware::kAllow);
[email protected]e0be00992013-04-26 13:15:29308}
309
Doug Arnett2fd6cf22020-01-27 19:18:40310bool TranslateAgent::ExecuteScriptAndGetBoolResult(const std::string& script,
311 bool fallback) {
dglazkov24814772015-09-25 22:27:47312 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
[email protected]e0be00992013-04-26 13:15:29313 if (!main_frame)
314 return fallback;
315
[email protected]dc3d06e2013-09-06 12:21:03316 v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
Blink Reformat1c4d759e2017-04-09 16:34:54317 WebScriptSource source = WebScriptSource(WebString::FromASCII(script));
Hiroshige Hayashizakie1352e62018-05-14 23:03:06318 v8::Local<v8::Value> result =
Dave Tapuska306a72402021-04-09 14:20:02319 main_frame->ExecuteScriptInIsolatedWorldAndReturnValue(
320 world_id_, source, blink::BackForwardCacheAware::kAllow);
Hiroshige Hayashizakie1352e62018-05-14 23:03:06321 if (result.IsEmpty() || !result->IsBoolean()) {
[email protected]e0be00992013-04-26 13:15:29322 NOTREACHED();
323 return fallback;
324 }
325
Ross McIlroy1c8eb142018-08-01 15:12:07326 return result.As<v8::Boolean>()->Value();
[email protected]e0be00992013-04-26 13:15:29327}
328
Doug Arnett2fd6cf22020-01-27 19:18:40329std::string TranslateAgent::ExecuteScriptAndGetStringResult(
[email protected]e0be00992013-04-26 13:15:29330 const std::string& script) {
dglazkov24814772015-09-25 22:27:47331 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
[email protected]e0be00992013-04-26 13:15:29332 if (!main_frame)
333 return std::string();
334
Dan Elphickc027e8ae2018-07-27 10:10:10335 v8::Isolate* isolate = v8::Isolate::GetCurrent();
336 v8::HandleScope handle_scope(isolate);
Blink Reformat1c4d759e2017-04-09 16:34:54337 WebScriptSource source = WebScriptSource(WebString::FromASCII(script));
Hiroshige Hayashizakie1352e62018-05-14 23:03:06338 v8::Local<v8::Value> result =
Dave Tapuska306a72402021-04-09 14:20:02339 main_frame->ExecuteScriptInIsolatedWorldAndReturnValue(
340 world_id_, source, blink::BackForwardCacheAware::kAllow);
Hiroshige Hayashizakie1352e62018-05-14 23:03:06341 if (result.IsEmpty() || !result->IsString()) {
[email protected]e0be00992013-04-26 13:15:29342 NOTREACHED();
343 return std::string();
344 }
345
Hiroshige Hayashizakie1352e62018-05-14 23:03:06346 v8::Local<v8::String> v8_str = result.As<v8::String>();
Scott Littlef130e8e82020-08-07 16:01:37347 int length = v8_str->Utf8Length(isolate);
348 if (length <= 0)
349 return std::string();
350
351 std::string str(static_cast<size_t>(length), '\0');
352 v8_str->WriteUtf8(isolate, &str[0], length);
353 return str;
[email protected]e0be00992013-04-26 13:15:29354}
355
Doug Arnett2fd6cf22020-01-27 19:18:40356double TranslateAgent::ExecuteScriptAndGetDoubleResult(
[email protected]e0be00992013-04-26 13:15:29357 const std::string& script) {
dglazkov24814772015-09-25 22:27:47358 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
[email protected]e0be00992013-04-26 13:15:29359 if (!main_frame)
360 return 0.0;
361
[email protected]dc3d06e2013-09-06 12:21:03362 v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
Blink Reformat1c4d759e2017-04-09 16:34:54363 WebScriptSource source = WebScriptSource(WebString::FromASCII(script));
Hiroshige Hayashizakie1352e62018-05-14 23:03:06364 v8::Local<v8::Value> result =
Dave Tapuska306a72402021-04-09 14:20:02365 main_frame->ExecuteScriptInIsolatedWorldAndReturnValue(
366 world_id_, source, blink::BackForwardCacheAware::kAllow);
Hiroshige Hayashizakie1352e62018-05-14 23:03:06367 if (result.IsEmpty() || !result->IsNumber()) {
[email protected]e0be00992013-04-26 13:15:29368 NOTREACHED();
369 return 0.0;
370 }
371
Ross McIlroy1c8eb142018-08-01 15:12:07372 return result.As<v8::Number>()->Value();
[email protected]e0be00992013-04-26 13:15:29373}
374
Doug Arnett2fd6cf22020-01-27 19:18:40375int64_t TranslateAgent::ExecuteScriptAndGetIntegerResult(
gajendra.n5bcb90a2017-07-05 09:02:52376 const std::string& script) {
377 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
378 if (!main_frame)
379 return 0;
380
381 v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
gajendra.n5bcb90a2017-07-05 09:02:52382 WebScriptSource source = WebScriptSource(WebString::FromASCII(script));
Hiroshige Hayashizakie1352e62018-05-14 23:03:06383 v8::Local<v8::Value> result =
Dave Tapuska306a72402021-04-09 14:20:02384 main_frame->ExecuteScriptInIsolatedWorldAndReturnValue(
385 world_id_, source, blink::BackForwardCacheAware::kAllow);
Hiroshige Hayashizakie1352e62018-05-14 23:03:06386 if (result.IsEmpty() || !result->IsNumber()) {
gajendra.n5bcb90a2017-07-05 09:02:52387 NOTREACHED();
388 return 0;
389 }
390
Ross McIlroy1c8eb142018-08-01 15:12:07391 return result.As<v8::Integer>()->Value();
gajendra.n5bcb90a2017-07-05 09:02:52392}
393
Doug Arnett2fd6cf22020-01-27 19:18:40394// mojom::TranslateAgent implementations.
Doug Arnettc1436ac2020-04-08 03:10:05395void TranslateAgent::GetWebLanguageDetectionDetails(
396 GetWebLanguageDetectionDetailsCallback callback) {
397 NOTREACHED() << "This interface supported by PerFrameTranslateAgent";
398}
399
Doug Arnett2fd6cf22020-01-27 19:18:40400void TranslateAgent::TranslateFrame(const std::string& translate_script,
401 const std::string& source_lang,
402 const std::string& target_lang,
403 TranslateFrameCallback callback) {
dglazkov24814772015-09-25 22:27:47404 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
leon.han3134fc62016-08-10 07:25:58405 if (!main_frame) {
406 // Cancelled.
tzik9a17d7af2017-05-09 14:21:58407 std::move(callback).Run(true, source_lang, target_lang,
408 TranslateErrors::NONE);
[email protected]81273622011-02-02 03:56:13409 return; // We navigated away, nothing to do.
leon.han3134fc62016-08-10 07:25:58410 }
[email protected]81273622011-02-02 03:56:13411
[email protected]4a2387e2013-05-31 03:47:56412 // A similar translation is already under way, nothing to do.
leon.han3134fc62016-08-10 07:25:58413 if (translate_callback_pending_ && target_lang_ == target_lang) {
414 // This request is ignored.
tzik9a17d7af2017-05-09 14:21:58415 std::move(callback).Run(true, source_lang, target_lang,
416 TranslateErrors::NONE);
[email protected]81273622011-02-02 03:56:13417 return;
leon.han3134fc62016-08-10 07:25:58418 }
[email protected]81273622011-02-02 03:56:13419
420 // Any pending translation is now irrelevant.
421 CancelPendingTranslation();
422
423 // Set our states.
tzik9a17d7af2017-05-09 14:21:58424 translate_callback_pending_ = std::move(callback);
[email protected]4a2387e2013-05-31 03:47:56425
[email protected]81273622011-02-02 03:56:13426 // If the source language is undetermined, we'll let the translate element
427 // detect it.
drogerf1da1802014-09-17 14:54:05428 source_lang_ = (source_lang != kUnknownLanguageCode) ? source_lang
429 : kAutoDetectionLanguage;
[email protected]81273622011-02-02 03:56:13430 target_lang_ = target_lang;
431
drogerf1da1802014-09-17 14:54:05432 ReportUserActionDuration(language_determined_time_, base::TimeTicks::Now());
[email protected]0f6afb42013-05-17 03:44:47433
Blink Reformat1c4d759e2017-04-09 16:34:54434 GURL url(main_frame->GetDocument().Url());
drogerf1da1802014-09-17 14:54:05435 ReportPageScheme(url.scheme());
[email protected]595ec512013-05-23 16:40:30436
Karandeep Bhatia4a8b4352020-07-16 06:57:13437 // Set up v8 isolated world.
438 EnsureIsolatedWorldInitialized(world_id_);
[email protected]afa9cd922013-07-30 06:12:19439
[email protected]81273622011-02-02 03:56:13440 if (!IsTranslateLibAvailable()) {
441 // Evaluate the script to add the translation related method to the global
442 // context of the page.
443 ExecuteScript(translate_script);
444 DCHECK(IsTranslateLibAvailable());
445 }
446
leon.han3134fc62016-08-10 07:25:58447 TranslatePageImpl(0);
[email protected]81273622011-02-02 03:56:13448}
449
Doug Arnett2fd6cf22020-01-27 19:18:40450void TranslateAgent::RevertTranslation() {
[email protected]81273622011-02-02 03:56:13451 if (!IsTranslateLibAvailable()) {
452 NOTREACHED();
453 return;
454 }
455
[email protected]81273622011-02-02 03:56:13456 CancelPendingTranslation();
457
[email protected]0dfc0e52013-04-15 14:53:00458 ExecuteScript("cr.googleTranslate.revert()");
[email protected]81273622011-02-02 03:56:13459}
460
leon.han3134fc62016-08-10 07:25:58461////////////////////////////////////////////////////////////////////////////////
Doug Arnett2fd6cf22020-01-27 19:18:40462// TranslateAgent, private:
463void TranslateAgent::CheckTranslateStatus() {
[email protected]85d252e2010-04-06 22:21:02464 // First check if there was an error.
465 if (HasTranslationFailed()) {
gajendra.n5bcb90a2017-07-05 09:02:52466 NotifyBrowserTranslationFailed(
467 static_cast<translate::TranslateErrors::Type>(GetErrorCode()));
[email protected]85d252e2010-04-06 22:21:02468 return; // There was an error.
469 }
470
471 if (HasTranslationFinished()) {
[email protected]d64b07b2010-04-20 22:14:06472 std::string actual_source_lang;
473 // Translation was successfull, if it was auto, retrieve the source
474 // language the Translate Element detected.
475 if (source_lang_ == kAutoDetectionLanguage) {
Evan Stade157a9212021-04-20 00:09:14476 actual_source_lang = GetPageSourceLanguage();
[email protected]d64b07b2010-04-20 22:14:06477 if (actual_source_lang.empty()) {
drogerf1da1802014-09-17 14:54:05478 NotifyBrowserTranslationFailed(TranslateErrors::UNKNOWN_LANGUAGE);
[email protected]579317e2010-06-30 20:13:48479 return;
480 } else if (actual_source_lang == target_lang_) {
drogerf1da1802014-09-17 14:54:05481 NotifyBrowserTranslationFailed(TranslateErrors::IDENTICAL_LANGUAGES);
[email protected]d64b07b2010-04-20 22:14:06482 return;
483 }
484 } else {
485 actual_source_lang = source_lang_;
486 }
487
leon.han3134fc62016-08-10 07:25:58488 if (!translate_callback_pending_) {
[email protected]d64b07b2010-04-20 22:14:06489 NOTREACHED();
490 return;
491 }
492
[email protected]e0be00992013-04-26 13:15:29493 // Check JavaScript performance counters for UMA reports.
drogerf1da1802014-09-17 14:54:05494 ReportTimeToTranslate(
[email protected]3a2ea8f2013-05-13 18:40:15495 ExecuteScriptAndGetDoubleResult("cr.googleTranslate.translationTime"));
Megan Jablonski27785782021-06-23 23:08:23496 ReportTranslatedLanguageDetectionContentLength(page_contents_length_);
[email protected]e0be00992013-04-26 13:15:29497
[email protected]d64b07b2010-04-20 22:14:06498 // Notify the browser we are done.
tzik9a17d7af2017-05-09 14:21:58499 std::move(translate_callback_pending_)
500 .Run(false, actual_source_lang, target_lang_, TranslateErrors::NONE);
[email protected]85d252e2010-04-06 22:21:02501 return;
502 }
503
504 // The translation is still pending, check again later.
Karolina Soltysc7fbc032018-10-25 10:10:08505 translate_task_runner_->PostDelayedTask(
tzik2bcf8e42018-07-31 11:22:15506 FROM_HERE,
Doug Arnett2fd6cf22020-01-27 19:18:40507 base::BindOnce(&TranslateAgent::CheckTranslateStatus,
tzik2bcf8e42018-07-31 11:22:15508 weak_method_factory_.GetWeakPtr()),
[email protected]0dfc0e52013-04-15 14:53:00509 AdjustDelay(kTranslateStatusCheckDelayMs));
[email protected]85d252e2010-04-06 22:21:02510}
511
Doug Arnett2fd6cf22020-01-27 19:18:40512void TranslateAgent::TranslatePageImpl(int count) {
[email protected]85d252e2010-04-06 22:21:02513 DCHECK_LT(count, kMaxTranslateInitCheckAttempts);
[email protected]85d252e2010-04-06 22:21:02514 if (!IsTranslateLibReady()) {
gajendra.n5bcb90a2017-07-05 09:02:52515 // There was an error during initialization of library.
516 TranslateErrors::Type error =
517 static_cast<translate::TranslateErrors::Type>(GetErrorCode());
518 if (error != TranslateErrors::NONE) {
519 NotifyBrowserTranslationFailed(error);
520 return;
521 }
522
[email protected]85d252e2010-04-06 22:21:02523 // The library is not ready, try again later, unless we have tried several
thestig259626c2015-11-23 20:18:11524 // times unsuccessfully already.
[email protected]85d252e2010-04-06 22:21:02525 if (++count >= kMaxTranslateInitCheckAttempts) {
gajendra.n5bcb90a2017-07-05 09:02:52526 NotifyBrowserTranslationFailed(TranslateErrors::TRANSLATION_TIMEOUT);
[email protected]85d252e2010-04-06 22:21:02527 return;
528 }
Hajime Hoshi701c6e12019-03-19 15:19:18529 translate_task_runner_->PostDelayedTask(
tzik2bcf8e42018-07-31 11:22:15530 FROM_HERE,
Doug Arnett2fd6cf22020-01-27 19:18:40531 base::BindOnce(&TranslateAgent::TranslatePageImpl,
tzik2bcf8e42018-07-31 11:22:15532 weak_method_factory_.GetWeakPtr(), count),
[email protected]0dfc0e52013-04-15 14:53:00533 AdjustDelay(count * kTranslateInitCheckDelayMs));
[email protected]85d252e2010-04-06 22:21:02534 return;
535 }
536
[email protected]e0be00992013-04-26 13:15:29537 // The library is loaded, and ready for translation now.
538 // Check JavaScript performance counters for UMA reports.
drogerf1da1802014-09-17 14:54:05539 ReportTimeToBeReady(
[email protected]3a2ea8f2013-05-13 18:40:15540 ExecuteScriptAndGetDoubleResult("cr.googleTranslate.readyTime"));
drogerf1da1802014-09-17 14:54:05541 ReportTimeToLoad(
[email protected]3a2ea8f2013-05-13 18:40:15542 ExecuteScriptAndGetDoubleResult("cr.googleTranslate.loadTime"));
[email protected]e0be00992013-04-26 13:15:29543
[email protected]d64b07b2010-04-20 22:14:06544 if (!StartTranslation()) {
gajendra.n5bcb90a2017-07-05 09:02:52545 CheckTranslateStatus();
[email protected]85d252e2010-04-06 22:21:02546 return;
547 }
548 // Check the status of the translation.
Karolina Soltysc7fbc032018-10-25 10:10:08549 translate_task_runner_->PostDelayedTask(
tzik2bcf8e42018-07-31 11:22:15550 FROM_HERE,
Doug Arnett2fd6cf22020-01-27 19:18:40551 base::BindOnce(&TranslateAgent::CheckTranslateStatus,
tzik2bcf8e42018-07-31 11:22:15552 weak_method_factory_.GetWeakPtr()),
[email protected]0dfc0e52013-04-15 14:53:00553 AdjustDelay(kTranslateStatusCheckDelayMs));
[email protected]85d252e2010-04-06 22:21:02554}
555
Doug Arnett2fd6cf22020-01-27 19:18:40556void TranslateAgent::NotifyBrowserTranslationFailed(
drogerf1da1802014-09-17 14:54:05557 TranslateErrors::Type error) {
leon.han3134fc62016-08-10 07:25:58558 DCHECK(translate_callback_pending_);
[email protected]85d252e2010-04-06 22:21:02559 // Notify the browser there was an error.
tzik9a17d7af2017-05-09 14:21:58560 std::move(translate_callback_pending_)
561 .Run(false, source_lang_, target_lang_, error);
leon.han3134fc62016-08-10 07:25:58562}
563
Julie Jeongeun Kimb1afbe52019-09-26 02:39:48564const mojo::Remote<mojom::ContentTranslateDriver>&
Doug Arnett2fd6cf22020-01-27 19:18:40565TranslateAgent::GetTranslateHandler() {
Michael Martis088a00a2018-04-12 09:33:05566 if (!translate_handler_) {
Mario Sanchez Prada8cbe4aa2019-12-03 10:00:20567 render_frame()->GetBrowserInterfaceBroker()->GetInterface(
Julie Jeongeun Kimb1afbe52019-09-26 02:39:48568 translate_handler_.BindNewPipeAndPassReceiver());
leon.han3134fc62016-08-10 07:25:58569 }
570
Michael Martis088a00a2018-04-12 09:33:05571 return translate_handler_;
[email protected]7faa53862010-06-16 00:09:13572}
[email protected]e5a7a2a2014-03-27 16:16:03573
Doug Arnett2fd6cf22020-01-27 19:18:40574void TranslateAgent::ResetPage() {
Julie Jeongeun Kim9b734c52019-09-25 16:19:56575 receiver_.reset();
leon.hanb07801312016-08-12 11:47:57576 translate_callback_pending_.Reset();
577 CancelPendingTranslation();
Megan Jablonski27785782021-06-23 23:08:23578 page_contents_length_ = 0;
leon.hanb07801312016-08-12 11:47:57579}
580
Doug Arnett2fd6cf22020-01-27 19:18:40581void TranslateAgent::OnDestruct() {
xjz694b50a92016-06-07 21:49:37582 delete this;
583}
584
Jon Nappera7dc1b002017-06-26 01:01:21585/* static */
Doug Arnett2fd6cf22020-01-27 19:18:40586std::string TranslateAgent::BuildTranslationScript(
Jon Nappera7dc1b002017-06-26 01:01:21587 const std::string& source_lang,
588 const std::string& target_lang) {
589 return "cr.googleTranslate.translate(" +
590 base::GetQuotedJSONString(source_lang) + "," +
591 base::GetQuotedJSONString(target_lang) + ")";
592}
593
mcrouse0d096d32021-02-11 01:58:46594void TranslateAgent::UpdateLanguageDetectionModel(base::File model_file) {
595 translate::LanguageDetectionModel& language_detection_model =
596 GetLanguageDetectionModel();
597 language_detection_model.UpdateWithFile(std::move(model_file));
598}
599
drogerf1da1802014-09-17 14:54:05600} // namespace translate