blob: e92b7098c26ed3640dffb1a6da261a7a30c5e384 [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
adithyas5e615282017-01-17 21:27:01182 WebLanguageDetectionDetails web_detection_details =
Blink Reformat1c4d759e2017-04-09 16:34:54183 WebLanguageDetectionDetails::CollectLanguageDetectionDetails(document);
184 std::string content_language = web_detection_details.content_language.Utf8();
185 std::string html_lang = web_detection_details.html_language.Utf8();
Michael Crouse11e2a152020-12-12 20:50:34186 std::string model_detected_language;
Michael Crouse30c287a12020-12-14 23:26:01187 bool is_model_reliable = false;
mcrousee29a9f12021-02-17 19:06:29188 std::string detection_model_version;
189 float model_reliability_score = 0.0;
Michael Crouse11e2a152020-12-12 20:50:34190
191 std::string language;
192 if (translate::IsTFLiteLanguageDetectionEnabled()) {
Michael Crouse11e2a152020-12-12 20:50:34193 translate::LanguageDetectionModel& language_detection_model =
194 GetLanguageDetectionModel();
195 bool is_available = language_detection_model.IsAvailable();
196 language = is_available ? language_detection_model.DeterminePageLanguage(
197 content_language, html_lang, contents,
mcrousee29a9f12021-02-17 19:06:29198 &model_detected_language, &is_model_reliable,
199 model_reliability_score)
Michael Crouse11e2a152020-12-12 20:50:34200 : translate::kUnknownLanguageCode;
mcrouseca5021c2021-02-19 00:34:14201 UMA_HISTOGRAM_BOOLEAN(
Michael Crouse11e2a152020-12-12 20:50:34202 "LanguageDetection.TFLiteModel.WasModelAvailableForDetection",
203 is_available);
mcrousee29a9f12021-02-17 19:06:29204 detection_model_version = language_detection_model.GetModelVersion();
Michael Crouse11e2a152020-12-12 20:50:34205 } else {
mcrousee29a9f12021-02-17 19:06:29206 language = DeterminePageLanguage(
207 content_language, html_lang, contents, &model_detected_language,
208 &is_model_reliable, model_reliability_score);
209 detection_model_version = kCLDModelVersion;
Michael Crouse11e2a152020-12-12 20:50:34210 }
[email protected]27eff892013-05-21 16:40:52211
212 if (language.empty())
213 return;
[email protected]93b9d692011-04-13 00:44:31214
[email protected]0f6afb42013-05-17 03:44:47215 language_determined_time_ = base::TimeTicks::Now();
216
Michael Crouse11e2a152020-12-12 20:50:34217 // TODO(crbug.com/1157983): Update the language detection details struct to be
218 // model agnostic.
drogerf1da1802014-09-17 14:54:05219 LanguageDetectionDetails details;
[email protected]27eff892013-05-21 16:40:52220 details.time = base::Time::Now();
adithyas5e615282017-01-17 21:27:01221 details.url = web_detection_details.url;
[email protected]27eff892013-05-21 16:40:52222 details.content_language = content_language;
Michael Crouse0ebf0d52020-12-14 23:29:31223 details.model_detected_language = model_detected_language;
224 details.is_model_reliable = is_model_reliable;
Blink Reformat1c4d759e2017-04-09 16:34:54225 details.has_notranslate = web_detection_details.has_no_translate_meta;
[email protected]20efbf2f2013-05-31 06:42:37226 details.html_root_language = html_lang;
[email protected]27eff892013-05-21 16:40:52227 details.adopted_language = language;
mcrousee29a9f12021-02-17 19:06:29228 details.model_reliability_score = model_reliability_score;
229 details.detection_model_version = detection_model_version;
[email protected]27eff892013-05-21 16:40:52230
[email protected]d861d972013-05-30 06:02:31231 // TODO(hajimehoshi): If this affects performance, it should be set only if
232 // translate-internals tab exists.
233 details.contents = contents;
234
leon.han3134fc62016-08-10 07:25:58235 // For the same render frame with the same url, each time when its texts are
236 // captured, it should be treated as a new page to do translation.
leon.hanb07801312016-08-12 11:47:57237 ResetPage();
Michael Martis088a00a2018-04-12 09:33:05238 GetTranslateHandler()->RegisterPage(
Julie Jeongeun Kim9b734c52019-09-25 16:19:56239 receiver_.BindNewPipeAndPassRemote(
240 main_frame->GetTaskRunner(blink::TaskType::kInternalTranslation)),
241 details, !details.has_notranslate && !language.empty());
[email protected]93b9d692011-04-13 00:44:31242}
243
Doug Arnett2fd6cf22020-01-27 19:18:40244void TranslateAgent::CancelPendingTranslation() {
[email protected]81c9c662011-10-12 04:27:01245 weak_method_factory_.InvalidateWeakPtrs();
leon.han3134fc62016-08-10 07:25:58246 // Make sure to send the cancelled response back.
247 if (translate_callback_pending_) {
tzik9a17d7af2017-05-09 14:21:58248 std::move(translate_callback_pending_)
249 .Run(true, source_lang_, target_lang_, TranslateErrors::NONE);
leon.han3134fc62016-08-10 07:25:58250 }
[email protected]d64b07b2010-04-20 22:14:06251 source_lang_.clear();
252 target_lang_.clear();
253}
254
[email protected]85d252e2010-04-06 22:21:02255////////////////////////////////////////////////////////////////////////////////
Doug Arnett2fd6cf22020-01-27 19:18:40256// TranslateAgent, protected:
257bool TranslateAgent::IsTranslateLibAvailable() {
[email protected]0dfc0e52013-04-15 14:53:00258 return ExecuteScriptAndGetBoolResult(
[email protected]85d252e2010-04-06 22:21:02259 "typeof cr != 'undefined' && typeof cr.googleTranslate != 'undefined' && "
Doug Arnett2fd6cf22020-01-27 19:18:40260 "typeof cr.googleTranslate.translate == 'function'",
261 false);
[email protected]85d252e2010-04-06 22:21:02262}
263
Doug Arnett2fd6cf22020-01-27 19:18:40264bool TranslateAgent::IsTranslateLibReady() {
[email protected]0dfc0e52013-04-15 14:53:00265 return ExecuteScriptAndGetBoolResult("cr.googleTranslate.libReady", false);
[email protected]85d252e2010-04-06 22:21:02266}
267
Doug Arnett2fd6cf22020-01-27 19:18:40268bool TranslateAgent::HasTranslationFinished() {
[email protected]0dfc0e52013-04-15 14:53:00269 return ExecuteScriptAndGetBoolResult("cr.googleTranslate.finished", true);
[email protected]85d252e2010-04-06 22:21:02270}
271
Doug Arnett2fd6cf22020-01-27 19:18:40272bool TranslateAgent::HasTranslationFailed() {
[email protected]0dfc0e52013-04-15 14:53:00273 return ExecuteScriptAndGetBoolResult("cr.googleTranslate.error", true);
[email protected]85d252e2010-04-06 22:21:02274}
275
Doug Arnett2fd6cf22020-01-27 19:18:40276int64_t TranslateAgent::GetErrorCode() {
gajendra.n5bcb90a2017-07-05 09:02:52277 int64_t error_code =
278 ExecuteScriptAndGetIntegerResult("cr.googleTranslate.errorCode");
279 DCHECK_LT(error_code, static_cast<int>(TranslateErrors::TRANSLATE_ERROR_MAX));
280 return error_code;
281}
282
Doug Arnett2fd6cf22020-01-27 19:18:40283bool TranslateAgent::StartTranslation() {
Jon Nappera7dc1b002017-06-26 01:01:21284 return ExecuteScriptAndGetBoolResult(
285 BuildTranslationScript(source_lang_, target_lang_), false);
[email protected]85d252e2010-04-06 22:21:02286}
287
Evan Stade157a9212021-04-20 00:09:14288std::string TranslateAgent::GetPageSourceLanguage() {
[email protected]0dfc0e52013-04-15 14:53:00289 return ExecuteScriptAndGetStringResult("cr.googleTranslate.sourceLang");
[email protected]d64b07b2010-04-20 22:14:06290}
291
Doug Arnettc1436ac2020-04-08 03:10:05292base::TimeDelta TranslateAgent::AdjustDelay(int delay_in_milliseconds) {
293 // Just converts |delay_in_milliseconds| without any modification in practical
294 // cases. Tests will override this function to return modified value.
295 return base::TimeDelta::FromMilliseconds(delay_in_milliseconds);
[email protected]e4be2dd2010-12-14 00:44:39296}
297
Doug Arnett2fd6cf22020-01-27 19:18:40298void TranslateAgent::ExecuteScript(const std::string& script) {
dglazkov24814772015-09-25 22:27:47299 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
[email protected]afa9cd922013-07-30 06:12:19300 if (!main_frame)
301 return;
302
Blink Reformat1c4d759e2017-04-09 16:34:54303 WebScriptSource source = WebScriptSource(WebString::FromASCII(script));
Dave Tapuska306a72402021-04-09 14:20:02304 main_frame->ExecuteScriptInIsolatedWorld(
305 world_id_, source, blink::BackForwardCacheAware::kAllow);
[email protected]e0be00992013-04-26 13:15:29306}
307
Doug Arnett2fd6cf22020-01-27 19:18:40308bool TranslateAgent::ExecuteScriptAndGetBoolResult(const std::string& script,
309 bool fallback) {
dglazkov24814772015-09-25 22:27:47310 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
[email protected]e0be00992013-04-26 13:15:29311 if (!main_frame)
312 return fallback;
313
[email protected]dc3d06e2013-09-06 12:21:03314 v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
Blink Reformat1c4d759e2017-04-09 16:34:54315 WebScriptSource source = WebScriptSource(WebString::FromASCII(script));
Hiroshige Hayashizakie1352e62018-05-14 23:03:06316 v8::Local<v8::Value> result =
Dave Tapuska306a72402021-04-09 14:20:02317 main_frame->ExecuteScriptInIsolatedWorldAndReturnValue(
318 world_id_, source, blink::BackForwardCacheAware::kAllow);
Hiroshige Hayashizakie1352e62018-05-14 23:03:06319 if (result.IsEmpty() || !result->IsBoolean()) {
[email protected]e0be00992013-04-26 13:15:29320 NOTREACHED();
321 return fallback;
322 }
323
Ross McIlroy1c8eb142018-08-01 15:12:07324 return result.As<v8::Boolean>()->Value();
[email protected]e0be00992013-04-26 13:15:29325}
326
Doug Arnett2fd6cf22020-01-27 19:18:40327std::string TranslateAgent::ExecuteScriptAndGetStringResult(
[email protected]e0be00992013-04-26 13:15:29328 const std::string& script) {
dglazkov24814772015-09-25 22:27:47329 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
[email protected]e0be00992013-04-26 13:15:29330 if (!main_frame)
331 return std::string();
332
Dan Elphickc027e8ae2018-07-27 10:10:10333 v8::Isolate* isolate = v8::Isolate::GetCurrent();
334 v8::HandleScope handle_scope(isolate);
Blink Reformat1c4d759e2017-04-09 16:34:54335 WebScriptSource source = WebScriptSource(WebString::FromASCII(script));
Hiroshige Hayashizakie1352e62018-05-14 23:03:06336 v8::Local<v8::Value> result =
Dave Tapuska306a72402021-04-09 14:20:02337 main_frame->ExecuteScriptInIsolatedWorldAndReturnValue(
338 world_id_, source, blink::BackForwardCacheAware::kAllow);
Hiroshige Hayashizakie1352e62018-05-14 23:03:06339 if (result.IsEmpty() || !result->IsString()) {
[email protected]e0be00992013-04-26 13:15:29340 NOTREACHED();
341 return std::string();
342 }
343
Hiroshige Hayashizakie1352e62018-05-14 23:03:06344 v8::Local<v8::String> v8_str = result.As<v8::String>();
Scott Littlef130e8e82020-08-07 16:01:37345 int length = v8_str->Utf8Length(isolate);
346 if (length <= 0)
347 return std::string();
348
349 std::string str(static_cast<size_t>(length), '\0');
350 v8_str->WriteUtf8(isolate, &str[0], length);
351 return str;
[email protected]e0be00992013-04-26 13:15:29352}
353
Doug Arnett2fd6cf22020-01-27 19:18:40354double TranslateAgent::ExecuteScriptAndGetDoubleResult(
[email protected]e0be00992013-04-26 13:15:29355 const std::string& script) {
dglazkov24814772015-09-25 22:27:47356 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
[email protected]e0be00992013-04-26 13:15:29357 if (!main_frame)
358 return 0.0;
359
[email protected]dc3d06e2013-09-06 12:21:03360 v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
Blink Reformat1c4d759e2017-04-09 16:34:54361 WebScriptSource source = WebScriptSource(WebString::FromASCII(script));
Hiroshige Hayashizakie1352e62018-05-14 23:03:06362 v8::Local<v8::Value> result =
Dave Tapuska306a72402021-04-09 14:20:02363 main_frame->ExecuteScriptInIsolatedWorldAndReturnValue(
364 world_id_, source, blink::BackForwardCacheAware::kAllow);
Hiroshige Hayashizakie1352e62018-05-14 23:03:06365 if (result.IsEmpty() || !result->IsNumber()) {
[email protected]e0be00992013-04-26 13:15:29366 NOTREACHED();
367 return 0.0;
368 }
369
Ross McIlroy1c8eb142018-08-01 15:12:07370 return result.As<v8::Number>()->Value();
[email protected]e0be00992013-04-26 13:15:29371}
372
Doug Arnett2fd6cf22020-01-27 19:18:40373int64_t TranslateAgent::ExecuteScriptAndGetIntegerResult(
gajendra.n5bcb90a2017-07-05 09:02:52374 const std::string& script) {
375 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
376 if (!main_frame)
377 return 0;
378
379 v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
gajendra.n5bcb90a2017-07-05 09:02:52380 WebScriptSource source = WebScriptSource(WebString::FromASCII(script));
Hiroshige Hayashizakie1352e62018-05-14 23:03:06381 v8::Local<v8::Value> result =
Dave Tapuska306a72402021-04-09 14:20:02382 main_frame->ExecuteScriptInIsolatedWorldAndReturnValue(
383 world_id_, source, blink::BackForwardCacheAware::kAllow);
Hiroshige Hayashizakie1352e62018-05-14 23:03:06384 if (result.IsEmpty() || !result->IsNumber()) {
gajendra.n5bcb90a2017-07-05 09:02:52385 NOTREACHED();
386 return 0;
387 }
388
Ross McIlroy1c8eb142018-08-01 15:12:07389 return result.As<v8::Integer>()->Value();
gajendra.n5bcb90a2017-07-05 09:02:52390}
391
Doug Arnett2fd6cf22020-01-27 19:18:40392// mojom::TranslateAgent implementations.
Doug Arnettc1436ac2020-04-08 03:10:05393void TranslateAgent::GetWebLanguageDetectionDetails(
394 GetWebLanguageDetectionDetailsCallback callback) {
395 NOTREACHED() << "This interface supported by PerFrameTranslateAgent";
396}
397
Doug Arnett2fd6cf22020-01-27 19:18:40398void TranslateAgent::TranslateFrame(const std::string& translate_script,
399 const std::string& source_lang,
400 const std::string& target_lang,
401 TranslateFrameCallback callback) {
dglazkov24814772015-09-25 22:27:47402 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
leon.han3134fc62016-08-10 07:25:58403 if (!main_frame) {
404 // Cancelled.
tzik9a17d7af2017-05-09 14:21:58405 std::move(callback).Run(true, source_lang, target_lang,
406 TranslateErrors::NONE);
[email protected]81273622011-02-02 03:56:13407 return; // We navigated away, nothing to do.
leon.han3134fc62016-08-10 07:25:58408 }
[email protected]81273622011-02-02 03:56:13409
[email protected]4a2387e2013-05-31 03:47:56410 // A similar translation is already under way, nothing to do.
leon.han3134fc62016-08-10 07:25:58411 if (translate_callback_pending_ && target_lang_ == target_lang) {
412 // This request is ignored.
tzik9a17d7af2017-05-09 14:21:58413 std::move(callback).Run(true, source_lang, target_lang,
414 TranslateErrors::NONE);
[email protected]81273622011-02-02 03:56:13415 return;
leon.han3134fc62016-08-10 07:25:58416 }
[email protected]81273622011-02-02 03:56:13417
418 // Any pending translation is now irrelevant.
419 CancelPendingTranslation();
420
421 // Set our states.
tzik9a17d7af2017-05-09 14:21:58422 translate_callback_pending_ = std::move(callback);
[email protected]4a2387e2013-05-31 03:47:56423
[email protected]81273622011-02-02 03:56:13424 // If the source language is undetermined, we'll let the translate element
425 // detect it.
drogerf1da1802014-09-17 14:54:05426 source_lang_ = (source_lang != kUnknownLanguageCode) ? source_lang
427 : kAutoDetectionLanguage;
[email protected]81273622011-02-02 03:56:13428 target_lang_ = target_lang;
429
drogerf1da1802014-09-17 14:54:05430 ReportUserActionDuration(language_determined_time_, base::TimeTicks::Now());
[email protected]0f6afb42013-05-17 03:44:47431
Blink Reformat1c4d759e2017-04-09 16:34:54432 GURL url(main_frame->GetDocument().Url());
drogerf1da1802014-09-17 14:54:05433 ReportPageScheme(url.scheme());
[email protected]595ec512013-05-23 16:40:30434
Karandeep Bhatia4a8b4352020-07-16 06:57:13435 // Set up v8 isolated world.
436 EnsureIsolatedWorldInitialized(world_id_);
[email protected]afa9cd922013-07-30 06:12:19437
[email protected]81273622011-02-02 03:56:13438 if (!IsTranslateLibAvailable()) {
439 // Evaluate the script to add the translation related method to the global
440 // context of the page.
441 ExecuteScript(translate_script);
442 DCHECK(IsTranslateLibAvailable());
443 }
444
leon.han3134fc62016-08-10 07:25:58445 TranslatePageImpl(0);
[email protected]81273622011-02-02 03:56:13446}
447
Doug Arnett2fd6cf22020-01-27 19:18:40448void TranslateAgent::RevertTranslation() {
[email protected]81273622011-02-02 03:56:13449 if (!IsTranslateLibAvailable()) {
450 NOTREACHED();
451 return;
452 }
453
[email protected]81273622011-02-02 03:56:13454 CancelPendingTranslation();
455
[email protected]0dfc0e52013-04-15 14:53:00456 ExecuteScript("cr.googleTranslate.revert()");
[email protected]81273622011-02-02 03:56:13457}
458
leon.han3134fc62016-08-10 07:25:58459////////////////////////////////////////////////////////////////////////////////
Doug Arnett2fd6cf22020-01-27 19:18:40460// TranslateAgent, private:
461void TranslateAgent::CheckTranslateStatus() {
[email protected]85d252e2010-04-06 22:21:02462 // First check if there was an error.
463 if (HasTranslationFailed()) {
gajendra.n5bcb90a2017-07-05 09:02:52464 NotifyBrowserTranslationFailed(
465 static_cast<translate::TranslateErrors::Type>(GetErrorCode()));
[email protected]85d252e2010-04-06 22:21:02466 return; // There was an error.
467 }
468
469 if (HasTranslationFinished()) {
[email protected]d64b07b2010-04-20 22:14:06470 std::string actual_source_lang;
471 // Translation was successfull, if it was auto, retrieve the source
472 // language the Translate Element detected.
473 if (source_lang_ == kAutoDetectionLanguage) {
Evan Stade157a9212021-04-20 00:09:14474 actual_source_lang = GetPageSourceLanguage();
[email protected]d64b07b2010-04-20 22:14:06475 if (actual_source_lang.empty()) {
drogerf1da1802014-09-17 14:54:05476 NotifyBrowserTranslationFailed(TranslateErrors::UNKNOWN_LANGUAGE);
[email protected]579317e2010-06-30 20:13:48477 return;
478 } else if (actual_source_lang == target_lang_) {
drogerf1da1802014-09-17 14:54:05479 NotifyBrowserTranslationFailed(TranslateErrors::IDENTICAL_LANGUAGES);
[email protected]d64b07b2010-04-20 22:14:06480 return;
481 }
482 } else {
483 actual_source_lang = source_lang_;
484 }
485
leon.han3134fc62016-08-10 07:25:58486 if (!translate_callback_pending_) {
[email protected]d64b07b2010-04-20 22:14:06487 NOTREACHED();
488 return;
489 }
490
[email protected]e0be00992013-04-26 13:15:29491 // Check JavaScript performance counters for UMA reports.
drogerf1da1802014-09-17 14:54:05492 ReportTimeToTranslate(
[email protected]3a2ea8f2013-05-13 18:40:15493 ExecuteScriptAndGetDoubleResult("cr.googleTranslate.translationTime"));
[email protected]e0be00992013-04-26 13:15:29494
[email protected]d64b07b2010-04-20 22:14:06495 // Notify the browser we are done.
tzik9a17d7af2017-05-09 14:21:58496 std::move(translate_callback_pending_)
497 .Run(false, actual_source_lang, target_lang_, TranslateErrors::NONE);
[email protected]85d252e2010-04-06 22:21:02498 return;
499 }
500
501 // The translation is still pending, check again later.
Karolina Soltysc7fbc032018-10-25 10:10:08502 translate_task_runner_->PostDelayedTask(
tzik2bcf8e42018-07-31 11:22:15503 FROM_HERE,
Doug Arnett2fd6cf22020-01-27 19:18:40504 base::BindOnce(&TranslateAgent::CheckTranslateStatus,
tzik2bcf8e42018-07-31 11:22:15505 weak_method_factory_.GetWeakPtr()),
[email protected]0dfc0e52013-04-15 14:53:00506 AdjustDelay(kTranslateStatusCheckDelayMs));
[email protected]85d252e2010-04-06 22:21:02507}
508
Doug Arnett2fd6cf22020-01-27 19:18:40509void TranslateAgent::TranslatePageImpl(int count) {
[email protected]85d252e2010-04-06 22:21:02510 DCHECK_LT(count, kMaxTranslateInitCheckAttempts);
[email protected]85d252e2010-04-06 22:21:02511 if (!IsTranslateLibReady()) {
gajendra.n5bcb90a2017-07-05 09:02:52512 // There was an error during initialization of library.
513 TranslateErrors::Type error =
514 static_cast<translate::TranslateErrors::Type>(GetErrorCode());
515 if (error != TranslateErrors::NONE) {
516 NotifyBrowserTranslationFailed(error);
517 return;
518 }
519
[email protected]85d252e2010-04-06 22:21:02520 // The library is not ready, try again later, unless we have tried several
thestig259626c2015-11-23 20:18:11521 // times unsuccessfully already.
[email protected]85d252e2010-04-06 22:21:02522 if (++count >= kMaxTranslateInitCheckAttempts) {
gajendra.n5bcb90a2017-07-05 09:02:52523 NotifyBrowserTranslationFailed(TranslateErrors::TRANSLATION_TIMEOUT);
[email protected]85d252e2010-04-06 22:21:02524 return;
525 }
Hajime Hoshi701c6e12019-03-19 15:19:18526 translate_task_runner_->PostDelayedTask(
tzik2bcf8e42018-07-31 11:22:15527 FROM_HERE,
Doug Arnett2fd6cf22020-01-27 19:18:40528 base::BindOnce(&TranslateAgent::TranslatePageImpl,
tzik2bcf8e42018-07-31 11:22:15529 weak_method_factory_.GetWeakPtr(), count),
[email protected]0dfc0e52013-04-15 14:53:00530 AdjustDelay(count * kTranslateInitCheckDelayMs));
[email protected]85d252e2010-04-06 22:21:02531 return;
532 }
533
[email protected]e0be00992013-04-26 13:15:29534 // The library is loaded, and ready for translation now.
535 // Check JavaScript performance counters for UMA reports.
drogerf1da1802014-09-17 14:54:05536 ReportTimeToBeReady(
[email protected]3a2ea8f2013-05-13 18:40:15537 ExecuteScriptAndGetDoubleResult("cr.googleTranslate.readyTime"));
drogerf1da1802014-09-17 14:54:05538 ReportTimeToLoad(
[email protected]3a2ea8f2013-05-13 18:40:15539 ExecuteScriptAndGetDoubleResult("cr.googleTranslate.loadTime"));
[email protected]e0be00992013-04-26 13:15:29540
[email protected]d64b07b2010-04-20 22:14:06541 if (!StartTranslation()) {
gajendra.n5bcb90a2017-07-05 09:02:52542 CheckTranslateStatus();
[email protected]85d252e2010-04-06 22:21:02543 return;
544 }
545 // Check the status of the translation.
Karolina Soltysc7fbc032018-10-25 10:10:08546 translate_task_runner_->PostDelayedTask(
tzik2bcf8e42018-07-31 11:22:15547 FROM_HERE,
Doug Arnett2fd6cf22020-01-27 19:18:40548 base::BindOnce(&TranslateAgent::CheckTranslateStatus,
tzik2bcf8e42018-07-31 11:22:15549 weak_method_factory_.GetWeakPtr()),
[email protected]0dfc0e52013-04-15 14:53:00550 AdjustDelay(kTranslateStatusCheckDelayMs));
[email protected]85d252e2010-04-06 22:21:02551}
552
Doug Arnett2fd6cf22020-01-27 19:18:40553void TranslateAgent::NotifyBrowserTranslationFailed(
drogerf1da1802014-09-17 14:54:05554 TranslateErrors::Type error) {
leon.han3134fc62016-08-10 07:25:58555 DCHECK(translate_callback_pending_);
[email protected]85d252e2010-04-06 22:21:02556 // Notify the browser there was an error.
tzik9a17d7af2017-05-09 14:21:58557 std::move(translate_callback_pending_)
558 .Run(false, source_lang_, target_lang_, error);
leon.han3134fc62016-08-10 07:25:58559}
560
Julie Jeongeun Kimb1afbe52019-09-26 02:39:48561const mojo::Remote<mojom::ContentTranslateDriver>&
Doug Arnett2fd6cf22020-01-27 19:18:40562TranslateAgent::GetTranslateHandler() {
Michael Martis088a00a2018-04-12 09:33:05563 if (!translate_handler_) {
Mario Sanchez Prada8cbe4aa2019-12-03 10:00:20564 render_frame()->GetBrowserInterfaceBroker()->GetInterface(
Julie Jeongeun Kimb1afbe52019-09-26 02:39:48565 translate_handler_.BindNewPipeAndPassReceiver());
leon.han3134fc62016-08-10 07:25:58566 }
567
Michael Martis088a00a2018-04-12 09:33:05568 return translate_handler_;
[email protected]7faa53862010-06-16 00:09:13569}
[email protected]e5a7a2a2014-03-27 16:16:03570
Doug Arnett2fd6cf22020-01-27 19:18:40571void TranslateAgent::ResetPage() {
Julie Jeongeun Kim9b734c52019-09-25 16:19:56572 receiver_.reset();
leon.hanb07801312016-08-12 11:47:57573 translate_callback_pending_.Reset();
574 CancelPendingTranslation();
575}
576
Doug Arnett2fd6cf22020-01-27 19:18:40577void TranslateAgent::OnDestruct() {
xjz694b50a92016-06-07 21:49:37578 delete this;
579}
580
Jon Nappera7dc1b002017-06-26 01:01:21581/* static */
Doug Arnett2fd6cf22020-01-27 19:18:40582std::string TranslateAgent::BuildTranslationScript(
Jon Nappera7dc1b002017-06-26 01:01:21583 const std::string& source_lang,
584 const std::string& target_lang) {
585 return "cr.googleTranslate.translate(" +
586 base::GetQuotedJSONString(source_lang) + "," +
587 base::GetQuotedJSONString(target_lang) + ")";
588}
589
mcrouse0d096d32021-02-11 01:58:46590void TranslateAgent::UpdateLanguageDetectionModel(base::File model_file) {
591 translate::LanguageDetectionModel& language_detection_model =
592 GetLanguageDetectionModel();
593 language_detection_model.UpdateWithFile(std::move(model_file));
594}
595
drogerf1da1802014-09-17 14:54:05596} // namespace translate