blob: f83e245358da8a5f37a315182b83da29b81dad20 [file] [log] [blame]
Avi Drissman8ba1bad2022-09-13 19:22:361// Copyright 2012 The Chromium Authors
[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>
Fergal Daly74bfae22024-07-02 06:02:078
Jan Wilken Dörriead587c32021-03-11 14:09:279#include <string>
tzik9a17d7af2017-05-09 14:21:5810#include <utility>
11
Hans Wennborgdf87046c2020-04-28 11:06:2412#include "base/check_op.h"
mcrousef0c2fb002021-10-13 00:40:4813#include "base/command_line.h"
[email protected]85d252e2010-04-06 22:21:0214#include "base/compiler_specific.h"
Avi Drissman12be0312023-01-11 09:16:0915#include "base/functional/bind.h"
Jon Nappera7dc1b002017-06-26 01:01:2116#include "base/json/string_escape.h"
skyostilb0daa012015-06-02 19:03:4817#include "base/location.h"
Keita Suzuki54e166c2024-07-17 00:53:3618#include "base/metrics/histogram_functions.h"
asvitkinea0f05db2015-06-16 21:45:4619#include "base/metrics/histogram_macros.h"
mcrouse49738942021-04-15 21:24:2520#include "base/metrics/histogram_macros_local.h"
Avi Drissmanded77172021-07-02 18:23:0021#include "base/no_destructor.h"
Hans Wennborgdf87046c2020-04-28 11:06:2422#include "base/notreached.h"
[email protected]cbde0d92013-06-14 11:37:0423#include "base/strings/string_util.h"
[email protected]abfd1492013-06-07 21:23:2624#include "base/strings/utf_string_conversions.h"
Patrick Monette643cdf62021-10-15 19:13:4225#include "base/task/single_thread_task_runner.h"
Fergal Daly74bfae22024-07-02 06:02:0726#include "base/trace_event/trace_event.h"
Fergal Dalyfbcc4602024-08-28 15:00:3127#include "components/language_detection/content/renderer/language_detection_agent.h"
Fergal Dalyfe7cf252025-01-20 02:28:4228#include "components/language_detection/core/constants.h"
Fergal Dalyab927912024-07-31 17:16:3429#include "components/language_detection/core/language_detection_provider.h"
Karandeep Bhatia4a8b4352020-07-16 06:57:1330#include "components/translate/content/renderer/isolated_world_util.h"
[email protected]eba93c92014-01-07 17:34:1731#include "components/translate/core/common/translate_metrics.h"
32#include "components/translate/core/common/translate_util.h"
Michael Crouse11e2a152020-12-12 20:50:3433#include "components/translate/core/language_detection/language_detection_model.h"
[email protected]bfa4c82a2014-05-13 23:45:0234#include "components/translate/core/language_detection/language_detection_util.h"
[email protected]9ce237042014-07-17 18:09:5635#include "content/public/common/content_constants.h"
mcrousef0c2fb002021-10-13 00:40:4836#include "content/public/common/content_switches.h"
drogerf1da1802014-09-17 14:54:0537#include "content/public/common/url_constants.h"
dglazkov24814772015-09-25 22:27:4738#include "content/public/renderer/render_frame.h"
[email protected]9ce237042014-07-17 18:09:5639#include "content/public/renderer/render_thread.h"
Lukasz Anforowicz9bc03e72024-07-14 20:25:0240#include "third_party/blink/public/platform/browser_interface_broker_proxy.h"
Dave Tapuskab8035cb2022-09-09 15:12:3441#include "third_party/blink/public/platform/scheduler/web_agent_group_scheduler.h"
Blink Reformata30d4232018-04-07 15:31:0642#include "third_party/blink/public/web/web_document.h"
43#include "third_party/blink/public/web/web_language_detection_details.h"
44#include "third_party/blink/public/web/web_local_frame.h"
45#include "third_party/blink/public/web/web_script_source.h"
[email protected]afa9cd922013-07-30 06:12:1946#include "url/gurl.h"
mcrouse0d096d32021-02-11 01:58:4647#include "url/url_constants.h"
[email protected]4d51d5bf2010-07-26 18:48:2648#include "v8/include/v8.h"
[email protected]85d252e2010-04-06 22:21:0249
[email protected]a1221aea2013-11-07 01:31:3050using blink::WebDocument;
Doug Arnett2fd6cf22020-01-27 19:18:4051using blink::WebLanguageDetectionDetails;
dglazkov24814772015-09-25 22:27:4752using blink::WebLocalFrame;
[email protected]a1221aea2013-11-07 01:31:3053using blink::WebScriptSource;
[email protected]a1221aea2013-11-07 01:31:3054using blink::WebString;
[email protected]85d252e2010-04-06 22:21:0255
[email protected]ff9b1c12013-02-07 11:40:1356namespace {
57
[email protected]02798a982012-01-27 00:45:3358// The delay in milliseconds that we'll wait before checking to see if the
[email protected]85d252e2010-04-06 22:21:0259// translate library injected in the page is ready.
[email protected]ff9b1c12013-02-07 11:40:1360const int kTranslateInitCheckDelayMs = 150;
[email protected]85d252e2010-04-06 22:21:0261
62// The maximum number of times we'll check to see if the translate library
63// injected in the page is ready.
[email protected]ff9b1c12013-02-07 11:40:1364const int kMaxTranslateInitCheckAttempts = 5;
[email protected]85d252e2010-04-06 22:21:0265
66// The delay we wait in milliseconds before checking whether the translation has
67// finished.
[email protected]ff9b1c12013-02-07 11:40:1368const int kTranslateStatusCheckDelayMs = 400;
[email protected]85d252e2010-04-06 22:21:0269
[email protected]d64b07b2010-04-20 22:14:0670// Language name passed to the Translate element for it to detect the language.
[email protected]33281252013-04-15 14:46:4971const char kAutoDetectionLanguage[] = "auto";
Michael Crouse11e2a152020-12-12 20:50:3472
mcrousee29a9f12021-02-17 19:06:2973// The current CLD model version.
74constexpr char kCLDModelVersion[] = "CLD3";
75
Michael Crouse11e2a152020-12-12 20:50:3476// Returns the language detection model that is shared across the RenderFrames
77// in the renderer.
78translate::LanguageDetectionModel& GetLanguageDetectionModel() {
Fergal Daly2e810a42024-07-31 14:08:0679 static base::NoDestructor<translate::LanguageDetectionModel> instance(
Sylvain Defresneae892c02024-09-20 14:29:0980 language_detection::GetLanguageDetectionModel());
Michael Crouse11e2a152020-12-12 20:50:3481 return *instance;
82}
83
mcrouse15e59cc2021-10-20 17:38:3084// Returns if the language detection should be overridden so that a default
85// result is returned immediately.
86bool ShouldOverrideLanguageDetectionForTesting() {
87 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
88 if (command_line->HasSwitch(::switches::kOverrideLanguageDetection)) {
89 return true;
90 }
91 return false;
92}
93
[email protected]ff9b1c12013-02-07 11:40:1394} // namespace
[email protected]d64b07b2010-04-20 22:14:0695
drogerf1da1802014-09-17 14:54:0596namespace translate {
[email protected]e5a7a2a2014-03-27 16:16:0397
[email protected]85d252e2010-04-06 22:21:0298////////////////////////////////////////////////////////////////////////////////
Doug Arnett2fd6cf22020-01-27 19:18:4099// TranslateAgent, public:
Devlin Croninc03d88df2022-01-20 21:29:20100TranslateAgent::TranslateAgent(content::RenderFrame* render_frame, int world_id)
dglazkov24814772015-09-25 22:27:47101 : content::RenderFrameObserver(render_frame),
drogerf1da1802014-09-17 14:54:05102 world_id_(world_id),
Fergal Dalycf6c44ac2024-11-22 08:34:45103 translate_language_detection_model_(GetLanguageDetectionModel()),
Fergal Dalyfbcc4602024-08-28 15:00:31104 language_detection_agent_(
105 IsTFLiteLanguageDetectionEnabled()
Fergal Dalycf6c44ac2024-11-22 08:34:45106 ? new language_detection::LanguageDetectionAgent(
107 render_frame,
108 translate_language_detection_model_->tflite_model())
Fergal Dalyfbcc4602024-08-28 15:00:31109 : nullptr) {
Karolina Soltysc7fbc032018-10-25 10:10:08110 translate_task_runner_ = this->render_frame()->GetTaskRunner(
111 blink::TaskType::kInternalTranslation);
112}
[email protected]85d252e2010-04-06 22:21:02113
Sorin Jianu0373bcb92024-10-10 21:37:17114TranslateAgent::~TranslateAgent() = default;
[email protected]601858c02010-09-01 17:08:20115
mcrousef0c2fb002021-10-13 00:40:48116void TranslateAgent::SeedLanguageDetectionModelForTesting(
117 base::File model_file) {
Fergal Daly6ad84d92024-11-22 09:30:55118 translate_language_detection_model_->tflite_model().UpdateWithFile(
Fergal Dalyfbcc4602024-08-28 15:00:31119 std::move(model_file));
mcrousef0c2fb002021-10-13 00:40:48120}
121
Doug Arnett2fd6cf22020-01-27 19:18:40122void TranslateAgent::PrepareForUrl(const GURL& url) {
Anthony Cuie87885152023-11-29 22:16:31123 // Navigated to a new url, reset current page translation.
leon.hanb07801312016-08-12 11:47:57124 ResetPage();
[email protected]e5a7a2a2014-03-27 16:16:03125}
126
Zinovy Nisfc466c152024-05-01 16:51:28127void TranslateAgent::PageCaptured(
128 scoped_refptr<const base::RefCountedString16> contents) {
Fergal Daly74bfae22024-07-02 06:02:07129 TRACE_EVENT("browser", "TranslateAgent::PageCaptured");
[email protected]256897732012-10-16 19:35:05130 // Get the document language as set by WebKit from the http-equiv
131 // meta tag for "content-language". This may or may not also
132 // have a value derived from the actual Content-Language HTTP
133 // header. The two actually have different meanings (despite the
134 // original intent of http-equiv to be an equivalent) with the former
135 // being the language of the document and the latter being the
136 // language of the intended audience (a distinction really only
Zinovy Nisfc466c152024-05-01 16:51:28137 // relevant for things like language textbooks). This distinction
[email protected]256897732012-10-16 19:35:05138 // shouldn't affect translation.
Zinovy Nisfc466c152024-05-01 16:51:28139 if (!contents) {
140 return;
141 }
dglazkov24814772015-09-25 22:27:47142 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
leon.han3134fc62016-08-10 07:25:58143 if (!main_frame)
[email protected]effa54c2013-05-28 11:14:23144 return;
[email protected]e5a7a2a2014-03-27 16:16:03145
Blink Reformat1c4d759e2017-04-09 16:34:54146 WebDocument document = main_frame->GetDocument();
mcrouse23992f12021-05-07 22:01:25147 GURL url = GURL(document.Url());
148 // Limit detection to URLs that only detect the language of the content if the
149 // page is potentially a candidate for translation. This should be strictly a
150 // subset of the conditions in TranslateService::IsTranslatableURL, however,
151 // due to layering they cannot be identical. Critically, this list should
152 // never filter anything that is eligible for translation. Under filtering is
153 // ok as the translate service will make the final call and only results in a
154 // slight overhead in running the model when unnecessary.
155 if (url.is_empty() || url.SchemeIs(content::kChromeUIScheme) ||
Eric Lawrence [MSFT]874a68792021-11-09 00:11:31156 url.SchemeIs(content::kChromeDevToolsScheme) || url.IsAboutBlank()) {
mcrouse23992f12021-05-07 22:01:25157 return;
158 }
159
Zinovy Nisfc466c152024-05-01 16:51:28160 page_contents_length_ = contents->as_string().size();
Megan Jablonski27785782021-06-23 23:08:23161
adithyas5e615282017-01-17 21:27:01162 WebLanguageDetectionDetails web_detection_details =
Blink Reformat1c4d759e2017-04-09 16:34:54163 WebLanguageDetectionDetails::CollectLanguageDetectionDetails(document);
Victor Tan47cea9a2022-04-04 19:55:48164 WebLanguageDetectionDetails::RecordAcceptLanguageAndXmlHtmlLangMetric(
165 document);
166
Blink Reformat1c4d759e2017-04-09 16:34:54167 std::string content_language = web_detection_details.content_language.Utf8();
168 std::string html_lang = web_detection_details.html_language.Utf8();
Michael Crouse11e2a152020-12-12 20:50:34169 std::string model_detected_language;
Michael Crouse30c287a12020-12-14 23:26:01170 bool is_model_reliable = false;
mcrousee29a9f12021-02-17 19:06:29171 std::string detection_model_version;
172 float model_reliability_score = 0.0;
Michael Crouse11e2a152020-12-12 20:50:34173
mcrouse15e59cc2021-10-20 17:38:30174 if (ShouldOverrideLanguageDetectionForTesting()) {
175 std::string language = "fr";
176 LanguageDetectionDetails details;
177 details.adopted_language = language;
Zinovy Nisfc466c152024-05-01 16:51:28178 details.contents = contents->as_string();
Anthony Cui0220ce02023-01-18 21:35:00179 details.has_run_lang_detection = true;
mcrouse15e59cc2021-10-20 17:38:30180 ResetPage();
Vlad Batalov76821bcd2024-03-07 00:35:05181
182 last_details_ = std::move(details);
183 RenewPageRegistration();
mcrouse15e59cc2021-10-20 17:38:30184 return;
185 }
186
Anthony Cuic526ba72023-01-13 19:02:25187 LanguageDetectionDetails details;
Michael Crouse11e2a152020-12-12 20:50:34188 std::string language;
Anthony Cuie87885152023-11-29 22:16:31189 if (page_contents_length_ == 0) {
Tao Xiong6f6842bb22024-08-05 20:56:20190 // If captured content is empty do not run language detection and
191 // only use page-provided languages.
192 language = translate::DeterminePageLanguageNoModel(
193 content_language, html_lang,
Tao Xionge2fb7df2024-08-13 03:49:37194 translate::LanguageVerificationType::kNoPageContent);
Anthony Cuic526ba72023-01-13 19:02:25195 } else if (translate::IsTFLiteLanguageDetectionEnabled()) {
Anthony Cuie87885152023-11-29 22:16:31196 // Use TFLite and page contents to assist with language detection.
Fergal Dalycf6c44ac2024-11-22 08:34:45197 bool is_available = translate_language_detection_model_->IsAvailable();
Tao Xionge2fb7df2024-08-13 03:49:37198 language =
199 is_available
Fergal Dalycf6c44ac2024-11-22 08:34:45200 ? translate_language_detection_model_->DeterminePageLanguage(
Tao Xionge2fb7df2024-08-13 03:49:37201 content_language, html_lang, contents->as_string(),
202 &model_detected_language, &is_model_reliable,
203 model_reliability_score)
204 // If the model is not available do not run language
205 // detection and only use page-provided languages.
206 : translate::DeterminePageLanguageNoModel(
207 content_language, html_lang,
208 translate::LanguageVerificationType::kModelNotAvailable);
mcrouseca5021c2021-02-19 00:34:14209 UMA_HISTOGRAM_BOOLEAN(
Michael Crouse11e2a152020-12-12 20:50:34210 "LanguageDetection.TFLiteModel.WasModelAvailableForDetection",
211 is_available);
mcrousefe713862021-08-17 22:22:44212 UMA_HISTOGRAM_BOOLEAN(
213 "LanguageDetection.TFLiteModel.WasModelUnavailableDueToDeferredLoad",
Fergal Dalyfbcc4602024-08-28 15:00:31214 !is_available &&
215 language_detection_agent_->waiting_for_first_foreground());
Fergal Dalycf6c44ac2024-11-22 08:34:45216 detection_model_version =
217 translate_language_detection_model_->GetModelVersion();
Anthony Cuic526ba72023-01-13 19:02:25218 details.has_run_lang_detection = true;
Michael Crouse11e2a152020-12-12 20:50:34219 } else {
Anthony Cuie87885152023-11-29 22:16:31220 // Use CLD3 and page contents to assist with language detection.
mcrousee29a9f12021-02-17 19:06:29221 language = DeterminePageLanguage(
Zinovy Nisfc466c152024-05-01 16:51:28222 content_language, html_lang, contents->as_string(),
223 &model_detected_language, &is_model_reliable, model_reliability_score);
mcrousee29a9f12021-02-17 19:06:29224 detection_model_version = kCLDModelVersion;
Anthony Cuic526ba72023-01-13 19:02:25225 details.has_run_lang_detection = true;
Michael Crouse11e2a152020-12-12 20:50:34226 }
[email protected]27eff892013-05-21 16:40:52227
228 if (language.empty())
229 return;
[email protected]93b9d692011-04-13 00:44:31230
[email protected]27eff892013-05-21 16:40:52231 details.time = base::Time::Now();
adithyas5e615282017-01-17 21:27:01232 details.url = web_detection_details.url;
[email protected]27eff892013-05-21 16:40:52233 details.content_language = content_language;
Michael Crouse0ebf0d52020-12-14 23:29:31234 details.model_detected_language = model_detected_language;
235 details.is_model_reliable = is_model_reliable;
Blink Reformat1c4d759e2017-04-09 16:34:54236 details.has_notranslate = web_detection_details.has_no_translate_meta;
[email protected]20efbf2f2013-05-31 06:42:37237 details.html_root_language = html_lang;
[email protected]27eff892013-05-21 16:40:52238 details.adopted_language = language;
mcrousee29a9f12021-02-17 19:06:29239 details.model_reliability_score = model_reliability_score;
240 details.detection_model_version = detection_model_version;
[email protected]27eff892013-05-21 16:40:52241
[email protected]d861d972013-05-30 06:02:31242 // TODO(hajimehoshi): If this affects performance, it should be set only if
243 // translate-internals tab exists.
Zinovy Nisfc466c152024-05-01 16:51:28244 details.contents = contents->as_string();
[email protected]d861d972013-05-30 06:02:31245
leon.han3134fc62016-08-10 07:25:58246 // For the same render frame with the same url, each time when its texts are
247 // captured, it should be treated as a new page to do translation.
leon.hanb07801312016-08-12 11:47:57248 ResetPage();
Vlad Batalov76821bcd2024-03-07 00:35:05249
250 last_details_ = std::move(details);
251 RenewPageRegistration();
252}
253
254void TranslateAgent::RenewPageRegistration() {
255 if (!last_details_.has_value()) {
256 return;
257 }
258
259 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
260 if (!main_frame) {
261 return;
262 }
263
264 LanguageDetectionDetails details = std::move(*last_details_);
265
266 ResetPage();
Michael Martis088a00a2018-04-12 09:33:05267 GetTranslateHandler()->RegisterPage(
Julie Jeongeun Kim9b734c52019-09-25 16:19:56268 receiver_.BindNewPipeAndPassRemote(
269 main_frame->GetTaskRunner(blink::TaskType::kInternalTranslation)),
Vlad Batalov76821bcd2024-03-07 00:35:05270 details, !details.has_notranslate && !details.adopted_language.empty());
271
272 last_details_ = std::move(details);
[email protected]93b9d692011-04-13 00:44:31273}
274
Doug Arnett2fd6cf22020-01-27 19:18:40275void TranslateAgent::CancelPendingTranslation() {
[email protected]81c9c662011-10-12 04:27:01276 weak_method_factory_.InvalidateWeakPtrs();
leon.han3134fc62016-08-10 07:25:58277 // Make sure to send the cancelled response back.
278 if (translate_callback_pending_) {
tzik9a17d7af2017-05-09 14:21:58279 std::move(translate_callback_pending_)
280 .Run(true, source_lang_, target_lang_, TranslateErrors::NONE);
leon.han3134fc62016-08-10 07:25:58281 }
[email protected]d64b07b2010-04-20 22:14:06282 source_lang_.clear();
283 target_lang_.clear();
284}
285
[email protected]85d252e2010-04-06 22:21:02286////////////////////////////////////////////////////////////////////////////////
Doug Arnett2fd6cf22020-01-27 19:18:40287// TranslateAgent, protected:
288bool TranslateAgent::IsTranslateLibAvailable() {
[email protected]0dfc0e52013-04-15 14:53:00289 return ExecuteScriptAndGetBoolResult(
[email protected]85d252e2010-04-06 22:21:02290 "typeof cr != 'undefined' && typeof cr.googleTranslate != 'undefined' && "
Doug Arnett2fd6cf22020-01-27 19:18:40291 "typeof cr.googleTranslate.translate == 'function'",
292 false);
[email protected]85d252e2010-04-06 22:21:02293}
294
Doug Arnett2fd6cf22020-01-27 19:18:40295bool TranslateAgent::IsTranslateLibReady() {
[email protected]0dfc0e52013-04-15 14:53:00296 return ExecuteScriptAndGetBoolResult("cr.googleTranslate.libReady", false);
[email protected]85d252e2010-04-06 22:21:02297}
298
Doug Arnett2fd6cf22020-01-27 19:18:40299bool TranslateAgent::HasTranslationFinished() {
[email protected]0dfc0e52013-04-15 14:53:00300 return ExecuteScriptAndGetBoolResult("cr.googleTranslate.finished", true);
[email protected]85d252e2010-04-06 22:21:02301}
302
Doug Arnett2fd6cf22020-01-27 19:18:40303bool TranslateAgent::HasTranslationFailed() {
[email protected]0dfc0e52013-04-15 14:53:00304 return ExecuteScriptAndGetBoolResult("cr.googleTranslate.error", true);
[email protected]85d252e2010-04-06 22:21:02305}
306
Doug Arnett2fd6cf22020-01-27 19:18:40307int64_t TranslateAgent::GetErrorCode() {
gajendra.n5bcb90a2017-07-05 09:02:52308 int64_t error_code =
309 ExecuteScriptAndGetIntegerResult("cr.googleTranslate.errorCode");
310 DCHECK_LT(error_code, static_cast<int>(TranslateErrors::TRANSLATE_ERROR_MAX));
311 return error_code;
312}
313
Doug Arnett2fd6cf22020-01-27 19:18:40314bool TranslateAgent::StartTranslation() {
Jon Nappera7dc1b002017-06-26 01:01:21315 return ExecuteScriptAndGetBoolResult(
316 BuildTranslationScript(source_lang_, target_lang_), false);
[email protected]85d252e2010-04-06 22:21:02317}
318
Evan Stade157a9212021-04-20 00:09:14319std::string TranslateAgent::GetPageSourceLanguage() {
[email protected]0dfc0e52013-04-15 14:53:00320 return ExecuteScriptAndGetStringResult("cr.googleTranslate.sourceLang");
[email protected]d64b07b2010-04-20 22:14:06321}
322
Doug Arnettc1436ac2020-04-08 03:10:05323base::TimeDelta TranslateAgent::AdjustDelay(int delay_in_milliseconds) {
324 // Just converts |delay_in_milliseconds| without any modification in practical
325 // cases. Tests will override this function to return modified value.
Peter Kastinge5a38ed2021-10-02 03:06:35326 return base::Milliseconds(delay_in_milliseconds);
[email protected]e4be2dd2010-12-14 00:44:39327}
328
Doug Arnett2fd6cf22020-01-27 19:18:40329void TranslateAgent::ExecuteScript(const std::string& script) {
dglazkov24814772015-09-25 22:27:47330 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
[email protected]afa9cd922013-07-30 06:12:19331 if (!main_frame)
332 return;
333
Blink Reformat1c4d759e2017-04-09 16:34:54334 WebScriptSource source = WebScriptSource(WebString::FromASCII(script));
Dave Tapuska306a72402021-04-09 14:20:02335 main_frame->ExecuteScriptInIsolatedWorld(
336 world_id_, source, blink::BackForwardCacheAware::kAllow);
[email protected]e0be00992013-04-26 13:15:29337}
338
Doug Arnett2fd6cf22020-01-27 19:18:40339bool TranslateAgent::ExecuteScriptAndGetBoolResult(const std::string& script,
340 bool fallback) {
dglazkov24814772015-09-25 22:27:47341 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
[email protected]e0be00992013-04-26 13:15:29342 if (!main_frame)
343 return fallback;
344
Dave Tapuskab8035cb2022-09-09 15:12:34345 v8::HandleScope handle_scope(main_frame->GetAgentGroupScheduler()->Isolate());
Blink Reformat1c4d759e2017-04-09 16:34:54346 WebScriptSource source = WebScriptSource(WebString::FromASCII(script));
Hiroshige Hayashizakie1352e62018-05-14 23:03:06347 v8::Local<v8::Value> result =
Dave Tapuska306a72402021-04-09 14:20:02348 main_frame->ExecuteScriptInIsolatedWorldAndReturnValue(
349 world_id_, source, blink::BackForwardCacheAware::kAllow);
Hiroshige Hayashizakie1352e62018-05-14 23:03:06350 if (result.IsEmpty() || !result->IsBoolean()) {
[email protected]e0be00992013-04-26 13:15:29351 return fallback;
352 }
353
Ross McIlroy1c8eb142018-08-01 15:12:07354 return result.As<v8::Boolean>()->Value();
[email protected]e0be00992013-04-26 13:15:29355}
356
Doug Arnett2fd6cf22020-01-27 19:18:40357std::string TranslateAgent::ExecuteScriptAndGetStringResult(
[email protected]e0be00992013-04-26 13:15:29358 const std::string& script) {
dglazkov24814772015-09-25 22:27:47359 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
[email protected]e0be00992013-04-26 13:15:29360 if (!main_frame)
361 return std::string();
362
Dave Tapuskab8035cb2022-09-09 15:12:34363 v8::Isolate* isolate = main_frame->GetAgentGroupScheduler()->Isolate();
Dan Elphickc027e8ae2018-07-27 10:10:10364 v8::HandleScope handle_scope(isolate);
Blink Reformat1c4d759e2017-04-09 16:34:54365 WebScriptSource source = WebScriptSource(WebString::FromASCII(script));
Hiroshige Hayashizakie1352e62018-05-14 23:03:06366 v8::Local<v8::Value> result =
Dave Tapuska306a72402021-04-09 14:20:02367 main_frame->ExecuteScriptInIsolatedWorldAndReturnValue(
368 world_id_, source, blink::BackForwardCacheAware::kAllow);
Hiroshige Hayashizakie1352e62018-05-14 23:03:06369 if (result.IsEmpty() || !result->IsString()) {
[email protected]e0be00992013-04-26 13:15:29370 return std::string();
371 }
372
Hiroshige Hayashizakie1352e62018-05-14 23:03:06373 v8::Local<v8::String> v8_str = result.As<v8::String>();
Samuel Groß4fb6b66f2024-11-18 11:06:49374 size_t length = v8_str->Utf8LengthV2(isolate);
375 if (length == 0) {
Scott Littlef130e8e82020-08-07 16:01:37376 return std::string();
Samuel Groß4fb6b66f2024-11-18 11:06:49377 }
Scott Littlef130e8e82020-08-07 16:01:37378
Samuel Groß4fb6b66f2024-11-18 11:06:49379 std::string str(length, '\0');
380 v8_str->WriteUtf8V2(isolate, str.data(), length);
Scott Littlef130e8e82020-08-07 16:01:37381 return str;
[email protected]e0be00992013-04-26 13:15:29382}
383
Doug Arnett2fd6cf22020-01-27 19:18:40384double TranslateAgent::ExecuteScriptAndGetDoubleResult(
[email protected]e0be00992013-04-26 13:15:29385 const std::string& script) {
dglazkov24814772015-09-25 22:27:47386 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
[email protected]e0be00992013-04-26 13:15:29387 if (!main_frame)
388 return 0.0;
389
Dave Tapuskab8035cb2022-09-09 15:12:34390 v8::HandleScope handle_scope(main_frame->GetAgentGroupScheduler()->Isolate());
Blink Reformat1c4d759e2017-04-09 16:34:54391 WebScriptSource source = WebScriptSource(WebString::FromASCII(script));
Hiroshige Hayashizakie1352e62018-05-14 23:03:06392 v8::Local<v8::Value> result =
Dave Tapuska306a72402021-04-09 14:20:02393 main_frame->ExecuteScriptInIsolatedWorldAndReturnValue(
394 world_id_, source, blink::BackForwardCacheAware::kAllow);
Hiroshige Hayashizakie1352e62018-05-14 23:03:06395 if (result.IsEmpty() || !result->IsNumber()) {
[email protected]e0be00992013-04-26 13:15:29396 return 0.0;
397 }
398
Ross McIlroy1c8eb142018-08-01 15:12:07399 return result.As<v8::Number>()->Value();
[email protected]e0be00992013-04-26 13:15:29400}
401
Doug Arnett2fd6cf22020-01-27 19:18:40402int64_t TranslateAgent::ExecuteScriptAndGetIntegerResult(
gajendra.n5bcb90a2017-07-05 09:02:52403 const std::string& script) {
404 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
405 if (!main_frame)
406 return 0;
407
Dave Tapuskab8035cb2022-09-09 15:12:34408 v8::HandleScope handle_scope(main_frame->GetAgentGroupScheduler()->Isolate());
gajendra.n5bcb90a2017-07-05 09:02:52409 WebScriptSource source = WebScriptSource(WebString::FromASCII(script));
Hiroshige Hayashizakie1352e62018-05-14 23:03:06410 v8::Local<v8::Value> result =
Dave Tapuska306a72402021-04-09 14:20:02411 main_frame->ExecuteScriptInIsolatedWorldAndReturnValue(
412 world_id_, source, blink::BackForwardCacheAware::kAllow);
Hiroshige Hayashizakie1352e62018-05-14 23:03:06413 if (result.IsEmpty() || !result->IsNumber()) {
gajendra.n5bcb90a2017-07-05 09:02:52414 return 0;
415 }
416
Ross McIlroy1c8eb142018-08-01 15:12:07417 return result.As<v8::Integer>()->Value();
gajendra.n5bcb90a2017-07-05 09:02:52418}
419
Doug Arnett2fd6cf22020-01-27 19:18:40420// mojom::TranslateAgent implementations.
421void TranslateAgent::TranslateFrame(const std::string& translate_script,
422 const std::string& source_lang,
423 const std::string& target_lang,
424 TranslateFrameCallback callback) {
dglazkov24814772015-09-25 22:27:47425 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
leon.han3134fc62016-08-10 07:25:58426 if (!main_frame) {
427 // Cancelled.
tzik9a17d7af2017-05-09 14:21:58428 std::move(callback).Run(true, source_lang, target_lang,
429 TranslateErrors::NONE);
[email protected]81273622011-02-02 03:56:13430 return; // We navigated away, nothing to do.
leon.han3134fc62016-08-10 07:25:58431 }
[email protected]81273622011-02-02 03:56:13432
[email protected]4a2387e2013-05-31 03:47:56433 // A similar translation is already under way, nothing to do.
leon.han3134fc62016-08-10 07:25:58434 if (translate_callback_pending_ && target_lang_ == target_lang) {
435 // This request is ignored.
tzik9a17d7af2017-05-09 14:21:58436 std::move(callback).Run(true, source_lang, target_lang,
437 TranslateErrors::NONE);
[email protected]81273622011-02-02 03:56:13438 return;
leon.han3134fc62016-08-10 07:25:58439 }
[email protected]81273622011-02-02 03:56:13440
441 // Any pending translation is now irrelevant.
442 CancelPendingTranslation();
443
444 // Set our states.
tzik9a17d7af2017-05-09 14:21:58445 translate_callback_pending_ = std::move(callback);
[email protected]4a2387e2013-05-31 03:47:56446
[email protected]81273622011-02-02 03:56:13447 // If the source language is undetermined, we'll let the translate element
448 // detect it.
Fergal Dalyfe7cf252025-01-20 02:28:42449 source_lang_ = (source_lang != language_detection::kUnknownLanguageCode)
450 ? source_lang
451 : kAutoDetectionLanguage;
[email protected]81273622011-02-02 03:56:13452 target_lang_ = target_lang;
453
Karandeep Bhatia4a8b4352020-07-16 06:57:13454 // Set up v8 isolated world.
455 EnsureIsolatedWorldInitialized(world_id_);
[email protected]afa9cd922013-07-30 06:12:19456
[email protected]81273622011-02-02 03:56:13457 if (!IsTranslateLibAvailable()) {
458 // Evaluate the script to add the translation related method to the global
459 // context of the page.
460 ExecuteScript(translate_script);
461 DCHECK(IsTranslateLibAvailable());
462 }
463
leon.han3134fc62016-08-10 07:25:58464 TranslatePageImpl(0);
[email protected]81273622011-02-02 03:56:13465}
466
Doug Arnett2fd6cf22020-01-27 19:18:40467void TranslateAgent::RevertTranslation() {
[email protected]81273622011-02-02 03:56:13468 if (!IsTranslateLibAvailable()) {
Peter Boströmcb0d53062024-06-04 18:45:31469 DUMP_WILL_BE_NOTREACHED();
[email protected]81273622011-02-02 03:56:13470 return;
471 }
472
[email protected]81273622011-02-02 03:56:13473 CancelPendingTranslation();
474
[email protected]0dfc0e52013-04-15 14:53:00475 ExecuteScript("cr.googleTranslate.revert()");
[email protected]81273622011-02-02 03:56:13476}
477
leon.han3134fc62016-08-10 07:25:58478////////////////////////////////////////////////////////////////////////////////
Doug Arnett2fd6cf22020-01-27 19:18:40479// TranslateAgent, private:
480void TranslateAgent::CheckTranslateStatus() {
[email protected]85d252e2010-04-06 22:21:02481 // First check if there was an error.
482 if (HasTranslationFailed()) {
gajendra.n5bcb90a2017-07-05 09:02:52483 NotifyBrowserTranslationFailed(
Sylvain Defresnede305ac2022-09-01 13:15:37484 static_cast<translate::TranslateErrors>(GetErrorCode()));
[email protected]85d252e2010-04-06 22:21:02485 return; // There was an error.
486 }
487
488 if (HasTranslationFinished()) {
[email protected]d64b07b2010-04-20 22:14:06489 std::string actual_source_lang;
490 // Translation was successfull, if it was auto, retrieve the source
491 // language the Translate Element detected.
492 if (source_lang_ == kAutoDetectionLanguage) {
Evan Stade157a9212021-04-20 00:09:14493 actual_source_lang = GetPageSourceLanguage();
[email protected]d64b07b2010-04-20 22:14:06494 if (actual_source_lang.empty()) {
drogerf1da1802014-09-17 14:54:05495 NotifyBrowserTranslationFailed(TranslateErrors::UNKNOWN_LANGUAGE);
[email protected]579317e2010-06-30 20:13:48496 return;
497 } else if (actual_source_lang == target_lang_) {
drogerf1da1802014-09-17 14:54:05498 NotifyBrowserTranslationFailed(TranslateErrors::IDENTICAL_LANGUAGES);
[email protected]d64b07b2010-04-20 22:14:06499 return;
500 }
501 } else {
502 actual_source_lang = source_lang_;
503 }
504
leon.han3134fc62016-08-10 07:25:58505 if (!translate_callback_pending_) {
Peter Boström77d21352024-11-13 22:26:11506 NOTREACHED();
[email protected]d64b07b2010-04-20 22:14:06507 }
508
[email protected]e0be00992013-04-26 13:15:29509 // Check JavaScript performance counters for UMA reports.
drogerf1da1802014-09-17 14:54:05510 ReportTimeToTranslate(
[email protected]3a2ea8f2013-05-13 18:40:15511 ExecuteScriptAndGetDoubleResult("cr.googleTranslate.translationTime"));
Megan Jablonski27785782021-06-23 23:08:23512 ReportTranslatedLanguageDetectionContentLength(page_contents_length_);
[email protected]e0be00992013-04-26 13:15:29513
[email protected]d64b07b2010-04-20 22:14:06514 // Notify the browser we are done.
tzik9a17d7af2017-05-09 14:21:58515 std::move(translate_callback_pending_)
516 .Run(false, actual_source_lang, target_lang_, TranslateErrors::NONE);
[email protected]85d252e2010-04-06 22:21:02517 return;
518 }
519
520 // The translation is still pending, check again later.
Karolina Soltysc7fbc032018-10-25 10:10:08521 translate_task_runner_->PostDelayedTask(
tzik2bcf8e42018-07-31 11:22:15522 FROM_HERE,
Doug Arnett2fd6cf22020-01-27 19:18:40523 base::BindOnce(&TranslateAgent::CheckTranslateStatus,
tzik2bcf8e42018-07-31 11:22:15524 weak_method_factory_.GetWeakPtr()),
[email protected]0dfc0e52013-04-15 14:53:00525 AdjustDelay(kTranslateStatusCheckDelayMs));
[email protected]85d252e2010-04-06 22:21:02526}
527
Doug Arnett2fd6cf22020-01-27 19:18:40528void TranslateAgent::TranslatePageImpl(int count) {
[email protected]85d252e2010-04-06 22:21:02529 DCHECK_LT(count, kMaxTranslateInitCheckAttempts);
[email protected]85d252e2010-04-06 22:21:02530 if (!IsTranslateLibReady()) {
gajendra.n5bcb90a2017-07-05 09:02:52531 // There was an error during initialization of library.
Sylvain Defresnede305ac2022-09-01 13:15:37532 TranslateErrors error =
533 static_cast<translate::TranslateErrors>(GetErrorCode());
gajendra.n5bcb90a2017-07-05 09:02:52534 if (error != TranslateErrors::NONE) {
535 NotifyBrowserTranslationFailed(error);
536 return;
537 }
538
[email protected]85d252e2010-04-06 22:21:02539 // The library is not ready, try again later, unless we have tried several
thestig259626c2015-11-23 20:18:11540 // times unsuccessfully already.
[email protected]85d252e2010-04-06 22:21:02541 if (++count >= kMaxTranslateInitCheckAttempts) {
gajendra.n5bcb90a2017-07-05 09:02:52542 NotifyBrowserTranslationFailed(TranslateErrors::TRANSLATION_TIMEOUT);
[email protected]85d252e2010-04-06 22:21:02543 return;
544 }
Hajime Hoshi701c6e12019-03-19 15:19:18545 translate_task_runner_->PostDelayedTask(
tzik2bcf8e42018-07-31 11:22:15546 FROM_HERE,
Doug Arnett2fd6cf22020-01-27 19:18:40547 base::BindOnce(&TranslateAgent::TranslatePageImpl,
tzik2bcf8e42018-07-31 11:22:15548 weak_method_factory_.GetWeakPtr(), count),
[email protected]0dfc0e52013-04-15 14:53:00549 AdjustDelay(count * kTranslateInitCheckDelayMs));
[email protected]85d252e2010-04-06 22:21:02550 return;
551 }
552
[email protected]e0be00992013-04-26 13:15:29553 // The library is loaded, and ready for translation now.
554 // Check JavaScript performance counters for UMA reports.
drogerf1da1802014-09-17 14:54:05555 ReportTimeToBeReady(
[email protected]3a2ea8f2013-05-13 18:40:15556 ExecuteScriptAndGetDoubleResult("cr.googleTranslate.readyTime"));
drogerf1da1802014-09-17 14:54:05557 ReportTimeToLoad(
[email protected]3a2ea8f2013-05-13 18:40:15558 ExecuteScriptAndGetDoubleResult("cr.googleTranslate.loadTime"));
[email protected]e0be00992013-04-26 13:15:29559
[email protected]d64b07b2010-04-20 22:14:06560 if (!StartTranslation()) {
gajendra.n5bcb90a2017-07-05 09:02:52561 CheckTranslateStatus();
[email protected]85d252e2010-04-06 22:21:02562 return;
563 }
564 // Check the status of the translation.
Karolina Soltysc7fbc032018-10-25 10:10:08565 translate_task_runner_->PostDelayedTask(
tzik2bcf8e42018-07-31 11:22:15566 FROM_HERE,
Doug Arnett2fd6cf22020-01-27 19:18:40567 base::BindOnce(&TranslateAgent::CheckTranslateStatus,
tzik2bcf8e42018-07-31 11:22:15568 weak_method_factory_.GetWeakPtr()),
[email protected]0dfc0e52013-04-15 14:53:00569 AdjustDelay(kTranslateStatusCheckDelayMs));
[email protected]85d252e2010-04-06 22:21:02570}
571
Sylvain Defresnede305ac2022-09-01 13:15:37572void TranslateAgent::NotifyBrowserTranslationFailed(TranslateErrors error) {
leon.han3134fc62016-08-10 07:25:58573 DCHECK(translate_callback_pending_);
[email protected]85d252e2010-04-06 22:21:02574 // Notify the browser there was an error.
tzik9a17d7af2017-05-09 14:21:58575 std::move(translate_callback_pending_)
576 .Run(false, source_lang_, target_lang_, error);
leon.han3134fc62016-08-10 07:25:58577}
578
Julie Jeongeun Kimb1afbe52019-09-26 02:39:48579const mojo::Remote<mojom::ContentTranslateDriver>&
Doug Arnett2fd6cf22020-01-27 19:18:40580TranslateAgent::GetTranslateHandler() {
Fergal Dalycebc8252024-07-29 18:50:52581 if (translate_handler_) {
582 if (translate_handler_.is_connected()) {
583 return translate_handler_;
584 }
585 // The translate handler can become unbound or disconnected in testing
586 // so this catches that case and reconnects so `this` can connect to
587 // the driver in the browser.
588 translate_handler_.reset();
leon.han3134fc62016-08-10 07:25:58589 }
590
Lukasz Anforowicz21b928482024-06-26 17:02:51591 render_frame()->GetBrowserInterfaceBroker().GetInterface(
mcrousef0c2fb002021-10-13 00:40:48592 translate_handler_.BindNewPipeAndPassReceiver());
Michael Martis088a00a2018-04-12 09:33:05593 return translate_handler_;
[email protected]7faa53862010-06-16 00:09:13594}
[email protected]e5a7a2a2014-03-27 16:16:03595
Doug Arnett2fd6cf22020-01-27 19:18:40596void TranslateAgent::ResetPage() {
Vlad Batalov76821bcd2024-03-07 00:35:05597 last_details_ = {};
Julie Jeongeun Kim9b734c52019-09-25 16:19:56598 receiver_.reset();
leon.hanb07801312016-08-12 11:47:57599 translate_callback_pending_.Reset();
600 CancelPendingTranslation();
601}
602
Doug Arnett2fd6cf22020-01-27 19:18:40603void TranslateAgent::OnDestruct() {
xjz694b50a92016-06-07 21:49:37604 delete this;
605}
606
Jon Nappera7dc1b002017-06-26 01:01:21607/* static */
Doug Arnett2fd6cf22020-01-27 19:18:40608std::string TranslateAgent::BuildTranslationScript(
Jon Nappera7dc1b002017-06-26 01:01:21609 const std::string& source_lang,
610 const std::string& target_lang) {
611 return "cr.googleTranslate.translate(" +
612 base::GetQuotedJSONString(source_lang) + "," +
613 base::GetQuotedJSONString(target_lang) + ")";
614}
615
drogerf1da1802014-09-17 14:54:05616} // namespace translate