blob: 195bde4962c7cf8a576e50402f08dec81cc8cbb1 [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"
mcrousef0c2fb002021-10-13 00:40:4813#include "base/command_line.h"
[email protected]85d252e2010-04-06 22:21:0214#include "base/compiler_specific.h"
Jon Nappera7dc1b002017-06-26 01:01:2115#include "base/json/string_escape.h"
skyostilb0daa012015-06-02 19:03:4816#include "base/location.h"
asvitkinea0f05db2015-06-16 21:45:4617#include "base/metrics/histogram_macros.h"
mcrouse49738942021-04-15 21:24:2518#include "base/metrics/histogram_macros_local.h"
Avi Drissmanded77172021-07-02 18:23:0019#include "base/no_destructor.h"
Hans Wennborgdf87046c2020-04-28 11:06:2420#include "base/notreached.h"
[email protected]cbde0d92013-06-14 11:37:0421#include "base/strings/string_util.h"
[email protected]abfd1492013-06-07 21:23:2622#include "base/strings/utf_string_conversions.h"
Patrick Monette643cdf62021-10-15 19:13:4223#include "base/task/single_thread_task_runner.h"
Karandeep Bhatia4a8b4352020-07-16 06:57:1324#include "components/translate/content/renderer/isolated_world_util.h"
[email protected]eba93c92014-01-07 17:34:1725#include "components/translate/core/common/translate_constants.h"
26#include "components/translate/core/common/translate_metrics.h"
[email protected]eba93c92014-01-07 17:34:1727#include "components/translate/core/common/translate_util.h"
Michael Crouse11e2a152020-12-12 20:50:3428#include "components/translate/core/language_detection/language_detection_model.h"
[email protected]bfa4c82a2014-05-13 23:45:0229#include "components/translate/core/language_detection/language_detection_util.h"
[email protected]9ce237042014-07-17 18:09:5630#include "content/public/common/content_constants.h"
mcrousef0c2fb002021-10-13 00:40:4831#include "content/public/common/content_switches.h"
drogerf1da1802014-09-17 14:54:0532#include "content/public/common/url_constants.h"
dglazkov24814772015-09-25 22:27:4733#include "content/public/renderer/render_frame.h"
[email protected]9ce237042014-07-17 18:09:5634#include "content/public/renderer/render_thread.h"
Mario Sanchez Prada8cbe4aa2019-12-03 10:00:2035#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
Blink Reformata30d4232018-04-07 15:31:0636#include "third_party/blink/public/web/web_document.h"
37#include "third_party/blink/public/web/web_language_detection_details.h"
38#include "third_party/blink/public/web/web_local_frame.h"
39#include "third_party/blink/public/web/web_script_source.h"
[email protected]afa9cd922013-07-30 06:12:1940#include "url/gurl.h"
mcrouse0d096d32021-02-11 01:58:4641#include "url/url_constants.h"
[email protected]4d51d5bf2010-07-26 18:48:2642#include "v8/include/v8.h"
[email protected]85d252e2010-04-06 22:21:0243
[email protected]a1221aea2013-11-07 01:31:3044using blink::WebDocument;
Doug Arnett2fd6cf22020-01-27 19:18:4045using blink::WebLanguageDetectionDetails;
dglazkov24814772015-09-25 22:27:4746using blink::WebLocalFrame;
[email protected]a1221aea2013-11-07 01:31:3047using blink::WebScriptSource;
[email protected]a1221aea2013-11-07 01:31:3048using blink::WebString;
49using blink::WebVector;
[email protected]85d252e2010-04-06 22:21:0250
[email protected]ff9b1c12013-02-07 11:40:1351namespace {
52
[email protected]02798a982012-01-27 00:45:3353// The delay in milliseconds that we'll wait before checking to see if the
[email protected]85d252e2010-04-06 22:21:0254// translate library injected in the page is ready.
[email protected]ff9b1c12013-02-07 11:40:1355const int kTranslateInitCheckDelayMs = 150;
[email protected]85d252e2010-04-06 22:21:0256
57// The maximum number of times we'll check to see if the translate library
58// injected in the page is ready.
[email protected]ff9b1c12013-02-07 11:40:1359const int kMaxTranslateInitCheckAttempts = 5;
[email protected]85d252e2010-04-06 22:21:0260
61// The delay we wait in milliseconds before checking whether the translation has
62// finished.
[email protected]ff9b1c12013-02-07 11:40:1363const int kTranslateStatusCheckDelayMs = 400;
[email protected]85d252e2010-04-06 22:21:0264
[email protected]d64b07b2010-04-20 22:14:0665// Language name passed to the Translate element for it to detect the language.
[email protected]33281252013-04-15 14:46:4966const char kAutoDetectionLanguage[] = "auto";
Michael Crouse11e2a152020-12-12 20:50:3467
mcrousee29a9f12021-02-17 19:06:2968// The current CLD model version.
69constexpr char kCLDModelVersion[] = "CLD3";
70
Michael Crouse11e2a152020-12-12 20:50:3471// Returns the language detection model that is shared across the RenderFrames
72// in the renderer.
73translate::LanguageDetectionModel& GetLanguageDetectionModel() {
74 static base::NoDestructor<translate::LanguageDetectionModel> instance;
75 return *instance;
76}
77
mcrouse15e59cc2021-10-20 17:38:3078// Returns if the language detection should be overridden so that a default
79// result is returned immediately.
80bool ShouldOverrideLanguageDetectionForTesting() {
81 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
82 if (command_line->HasSwitch(::switches::kOverrideLanguageDetection)) {
83 return true;
84 }
85 return false;
86}
87
[email protected]ff9b1c12013-02-07 11:40:1388} // namespace
[email protected]d64b07b2010-04-20 22:14:0689
drogerf1da1802014-09-17 14:54:0590namespace translate {
[email protected]e5a7a2a2014-03-27 16:16:0391
[email protected]85d252e2010-04-06 22:21:0292////////////////////////////////////////////////////////////////////////////////
Doug Arnett2fd6cf22020-01-27 19:18:4093// TranslateAgent, public:
94TranslateAgent::TranslateAgent(content::RenderFrame* render_frame,
95 int world_id,
96 const std::string& extension_scheme)
dglazkov24814772015-09-25 22:27:4797 : content::RenderFrameObserver(render_frame),
drogerf1da1802014-09-17 14:54:0598 world_id_(world_id),
mcrouse49738942021-04-15 21:24:2599 extension_scheme_(extension_scheme),
100 waiting_for_first_foreground_(render_frame->IsHidden()) {
Karolina Soltysc7fbc032018-10-25 10:10:08101 translate_task_runner_ = this->render_frame()->GetTaskRunner(
102 blink::TaskType::kInternalTranslation);
mcrouse0d096d32021-02-11 01:58:46103
mcrouse49738942021-04-15 21:24:25104 if (!translate::IsTFLiteLanguageDetectionEnabled()) {
105 return;
106 }
107
108 translate::LanguageDetectionModel& language_detection_model =
109 GetLanguageDetectionModel();
110
111 // If the language detection model is available, we do not
112 // worry about requesting the model.
113 if (language_detection_model.IsAvailable()) {
114 return;
115 }
116
mcrousefe713862021-08-17 22:22:44117 UMA_HISTOGRAM_BOOLEAN("LanguageDetection.TFLiteModel.WasModelRequestDeferred",
118 waiting_for_first_foreground_);
mcrouse49738942021-04-15 21:24:25119
120 // Ensure the render frame is visible, otherwise the browser-side
121 // translate driver may not exist yet (https://crbug.com/1199397).
122 if (!waiting_for_first_foreground_) {
123 GetTranslateHandler()->GetLanguageDetectionModel(
124 base::BindOnce(&TranslateAgent::UpdateLanguageDetectionModel,
125 weak_pointer_factory_.GetWeakPtr()));
mcrouse0d096d32021-02-11 01:58:46126 }
Karolina Soltysc7fbc032018-10-25 10:10:08127}
[email protected]85d252e2010-04-06 22:21:02128
Doug Arnett2fd6cf22020-01-27 19:18:40129TranslateAgent::~TranslateAgent() {}
[email protected]601858c02010-09-01 17:08:20130
mcrouse49738942021-04-15 21:24:25131void TranslateAgent::WasShown() {
132 // Check if the the render frame was initially hidden and
133 // the model request was delayed until the frame was in
134 // the foreground.
135 if (!waiting_for_first_foreground_) {
136 return;
137 }
138
139 waiting_for_first_foreground_ = false;
140
141 if (!translate::IsTFLiteLanguageDetectionEnabled()) {
142 return;
143 }
144
145 translate::LanguageDetectionModel& language_detection_model =
146 GetLanguageDetectionModel();
147 if (language_detection_model.IsAvailable()) {
148 return;
149 }
150 // The model request was deferred because the frame was hidden
151 // and now the model is visible and the model is still not available.
152 // The browser-side translate driver should always be available at
153 // this point so we should make the request and race to get the
154 // model loaded for when the page content is available.
155 GetTranslateHandler()->GetLanguageDetectionModel(
156 base::BindOnce(&TranslateAgent::UpdateLanguageDetectionModel,
157 weak_pointer_factory_.GetWeakPtr()));
158}
159
mcrousef0c2fb002021-10-13 00:40:48160void TranslateAgent::SeedLanguageDetectionModelForTesting(
161 base::File model_file) {
162 UpdateLanguageDetectionModel(std::move(model_file));
163}
164
Doug Arnett2fd6cf22020-01-27 19:18:40165void TranslateAgent::PrepareForUrl(const GURL& url) {
leon.hanb07801312016-08-12 11:47:57166 // Navigated to a new url, reset current page translation.
167 ResetPage();
[email protected]e5a7a2a2014-03-27 16:16:03168}
169
Jan Wilken Dörriefa241ba2021-03-11 17:57:01170void TranslateAgent::PageCaptured(const std::u16string& contents) {
[email protected]256897732012-10-16 19:35:05171 // Get the document language as set by WebKit from the http-equiv
172 // meta tag for "content-language". This may or may not also
173 // have a value derived from the actual Content-Language HTTP
174 // header. The two actually have different meanings (despite the
175 // original intent of http-equiv to be an equivalent) with the former
176 // being the language of the document and the latter being the
177 // language of the intended audience (a distinction really only
mcrouse0d096d32021-02-11 01:58:46178 // relevant for things like language textbooks). This distinction
[email protected]256897732012-10-16 19:35:05179 // shouldn't affect translation.
dglazkov24814772015-09-25 22:27:47180 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
leon.han3134fc62016-08-10 07:25:58181 if (!main_frame)
[email protected]effa54c2013-05-28 11:14:23182 return;
[email protected]e5a7a2a2014-03-27 16:16:03183
Blink Reformat1c4d759e2017-04-09 16:34:54184 WebDocument document = main_frame->GetDocument();
mcrouse23992f12021-05-07 22:01:25185 GURL url = GURL(document.Url());
186 // Limit detection to URLs that only detect the language of the content if the
187 // page is potentially a candidate for translation. This should be strictly a
188 // subset of the conditions in TranslateService::IsTranslatableURL, however,
189 // due to layering they cannot be identical. Critically, this list should
190 // never filter anything that is eligible for translation. Under filtering is
191 // ok as the translate service will make the final call and only results in a
192 // slight overhead in running the model when unnecessary.
193 if (url.is_empty() || url.SchemeIs(content::kChromeUIScheme) ||
194 url.SchemeIs(content::kChromeDevToolsScheme) || url.IsAboutBlank() ||
195 url.SchemeIs(url::kFtpScheme)) {
196 return;
197 }
198
Megan Jablonski27785782021-06-23 23:08:23199 page_contents_length_ = contents.size();
200
adithyas5e615282017-01-17 21:27:01201 WebLanguageDetectionDetails web_detection_details =
Blink Reformat1c4d759e2017-04-09 16:34:54202 WebLanguageDetectionDetails::CollectLanguageDetectionDetails(document);
203 std::string content_language = web_detection_details.content_language.Utf8();
204 std::string html_lang = web_detection_details.html_language.Utf8();
Michael Crouse11e2a152020-12-12 20:50:34205 std::string model_detected_language;
Michael Crouse30c287a12020-12-14 23:26:01206 bool is_model_reliable = false;
mcrousee29a9f12021-02-17 19:06:29207 std::string detection_model_version;
208 float model_reliability_score = 0.0;
Michael Crouse11e2a152020-12-12 20:50:34209
mcrouse15e59cc2021-10-20 17:38:30210 if (ShouldOverrideLanguageDetectionForTesting()) {
211 std::string language = "fr";
212 LanguageDetectionDetails details;
213 details.adopted_language = language;
214 details.contents = contents;
215 ResetPage();
216 GetTranslateHandler()->RegisterPage(
217 receiver_.BindNewPipeAndPassRemote(
218 main_frame->GetTaskRunner(blink::TaskType::kInternalTranslation)),
219 details, !details.has_notranslate && !language.empty());
220 return;
221 }
222
Michael Crouse11e2a152020-12-12 20:50:34223 std::string language;
224 if (translate::IsTFLiteLanguageDetectionEnabled()) {
225 translate::LanguageDetectionModel& language_detection_model =
226 GetLanguageDetectionModel();
227 bool is_available = language_detection_model.IsAvailable();
228 language = is_available ? language_detection_model.DeterminePageLanguage(
229 content_language, html_lang, contents,
mcrousee29a9f12021-02-17 19:06:29230 &model_detected_language, &is_model_reliable,
231 model_reliability_score)
Michael Crouse11e2a152020-12-12 20:50:34232 : translate::kUnknownLanguageCode;
mcrouseca5021c2021-02-19 00:34:14233 UMA_HISTOGRAM_BOOLEAN(
Michael Crouse11e2a152020-12-12 20:50:34234 "LanguageDetection.TFLiteModel.WasModelAvailableForDetection",
235 is_available);
mcrousefe713862021-08-17 22:22:44236 UMA_HISTOGRAM_BOOLEAN(
237 "LanguageDetection.TFLiteModel.WasModelUnavailableDueToDeferredLoad",
238 !is_available && waiting_for_first_foreground_);
mcrousee29a9f12021-02-17 19:06:29239 detection_model_version = language_detection_model.GetModelVersion();
Michael Crouse11e2a152020-12-12 20:50:34240 } else {
mcrousee29a9f12021-02-17 19:06:29241 language = DeterminePageLanguage(
242 content_language, html_lang, contents, &model_detected_language,
243 &is_model_reliable, model_reliability_score);
244 detection_model_version = kCLDModelVersion;
Michael Crouse11e2a152020-12-12 20:50:34245 }
[email protected]27eff892013-05-21 16:40:52246
247 if (language.empty())
248 return;
[email protected]93b9d692011-04-13 00:44:31249
[email protected]0f6afb42013-05-17 03:44:47250 language_determined_time_ = base::TimeTicks::Now();
251
Michael Crouse11e2a152020-12-12 20:50:34252 // TODO(crbug.com/1157983): Update the language detection details struct to be
253 // model agnostic.
drogerf1da1802014-09-17 14:54:05254 LanguageDetectionDetails details;
[email protected]27eff892013-05-21 16:40:52255 details.time = base::Time::Now();
adithyas5e615282017-01-17 21:27:01256 details.url = web_detection_details.url;
[email protected]27eff892013-05-21 16:40:52257 details.content_language = content_language;
Michael Crouse0ebf0d52020-12-14 23:29:31258 details.model_detected_language = model_detected_language;
259 details.is_model_reliable = is_model_reliable;
Blink Reformat1c4d759e2017-04-09 16:34:54260 details.has_notranslate = web_detection_details.has_no_translate_meta;
[email protected]20efbf2f2013-05-31 06:42:37261 details.html_root_language = html_lang;
[email protected]27eff892013-05-21 16:40:52262 details.adopted_language = language;
mcrousee29a9f12021-02-17 19:06:29263 details.model_reliability_score = model_reliability_score;
264 details.detection_model_version = detection_model_version;
[email protected]27eff892013-05-21 16:40:52265
[email protected]d861d972013-05-30 06:02:31266 // TODO(hajimehoshi): If this affects performance, it should be set only if
267 // translate-internals tab exists.
268 details.contents = contents;
269
leon.han3134fc62016-08-10 07:25:58270 // For the same render frame with the same url, each time when its texts are
271 // captured, it should be treated as a new page to do translation.
leon.hanb07801312016-08-12 11:47:57272 ResetPage();
Michael Martis088a00a2018-04-12 09:33:05273 GetTranslateHandler()->RegisterPage(
Julie Jeongeun Kim9b734c52019-09-25 16:19:56274 receiver_.BindNewPipeAndPassRemote(
275 main_frame->GetTaskRunner(blink::TaskType::kInternalTranslation)),
276 details, !details.has_notranslate && !language.empty());
[email protected]93b9d692011-04-13 00:44:31277}
278
Doug Arnett2fd6cf22020-01-27 19:18:40279void TranslateAgent::CancelPendingTranslation() {
[email protected]81c9c662011-10-12 04:27:01280 weak_method_factory_.InvalidateWeakPtrs();
leon.han3134fc62016-08-10 07:25:58281 // Make sure to send the cancelled response back.
282 if (translate_callback_pending_) {
tzik9a17d7af2017-05-09 14:21:58283 std::move(translate_callback_pending_)
284 .Run(true, source_lang_, target_lang_, TranslateErrors::NONE);
leon.han3134fc62016-08-10 07:25:58285 }
[email protected]d64b07b2010-04-20 22:14:06286 source_lang_.clear();
287 target_lang_.clear();
288}
289
[email protected]85d252e2010-04-06 22:21:02290////////////////////////////////////////////////////////////////////////////////
Doug Arnett2fd6cf22020-01-27 19:18:40291// TranslateAgent, protected:
292bool TranslateAgent::IsTranslateLibAvailable() {
[email protected]0dfc0e52013-04-15 14:53:00293 return ExecuteScriptAndGetBoolResult(
[email protected]85d252e2010-04-06 22:21:02294 "typeof cr != 'undefined' && typeof cr.googleTranslate != 'undefined' && "
Doug Arnett2fd6cf22020-01-27 19:18:40295 "typeof cr.googleTranslate.translate == 'function'",
296 false);
[email protected]85d252e2010-04-06 22:21:02297}
298
Doug Arnett2fd6cf22020-01-27 19:18:40299bool TranslateAgent::IsTranslateLibReady() {
[email protected]0dfc0e52013-04-15 14:53:00300 return ExecuteScriptAndGetBoolResult("cr.googleTranslate.libReady", false);
[email protected]85d252e2010-04-06 22:21:02301}
302
Doug Arnett2fd6cf22020-01-27 19:18:40303bool TranslateAgent::HasTranslationFinished() {
[email protected]0dfc0e52013-04-15 14:53:00304 return ExecuteScriptAndGetBoolResult("cr.googleTranslate.finished", true);
[email protected]85d252e2010-04-06 22:21:02305}
306
Doug Arnett2fd6cf22020-01-27 19:18:40307bool TranslateAgent::HasTranslationFailed() {
[email protected]0dfc0e52013-04-15 14:53:00308 return ExecuteScriptAndGetBoolResult("cr.googleTranslate.error", true);
[email protected]85d252e2010-04-06 22:21:02309}
310
Doug Arnett2fd6cf22020-01-27 19:18:40311int64_t TranslateAgent::GetErrorCode() {
gajendra.n5bcb90a2017-07-05 09:02:52312 int64_t error_code =
313 ExecuteScriptAndGetIntegerResult("cr.googleTranslate.errorCode");
314 DCHECK_LT(error_code, static_cast<int>(TranslateErrors::TRANSLATE_ERROR_MAX));
315 return error_code;
316}
317
Doug Arnett2fd6cf22020-01-27 19:18:40318bool TranslateAgent::StartTranslation() {
Jon Nappera7dc1b002017-06-26 01:01:21319 return ExecuteScriptAndGetBoolResult(
320 BuildTranslationScript(source_lang_, target_lang_), false);
[email protected]85d252e2010-04-06 22:21:02321}
322
Evan Stade157a9212021-04-20 00:09:14323std::string TranslateAgent::GetPageSourceLanguage() {
[email protected]0dfc0e52013-04-15 14:53:00324 return ExecuteScriptAndGetStringResult("cr.googleTranslate.sourceLang");
[email protected]d64b07b2010-04-20 22:14:06325}
326
Doug Arnettc1436ac2020-04-08 03:10:05327base::TimeDelta TranslateAgent::AdjustDelay(int delay_in_milliseconds) {
328 // Just converts |delay_in_milliseconds| without any modification in practical
329 // cases. Tests will override this function to return modified value.
Peter Kastinge5a38ed2021-10-02 03:06:35330 return base::Milliseconds(delay_in_milliseconds);
[email protected]e4be2dd2010-12-14 00:44:39331}
332
Doug Arnett2fd6cf22020-01-27 19:18:40333void TranslateAgent::ExecuteScript(const std::string& script) {
dglazkov24814772015-09-25 22:27:47334 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
[email protected]afa9cd922013-07-30 06:12:19335 if (!main_frame)
336 return;
337
Blink Reformat1c4d759e2017-04-09 16:34:54338 WebScriptSource source = WebScriptSource(WebString::FromASCII(script));
Dave Tapuska306a72402021-04-09 14:20:02339 main_frame->ExecuteScriptInIsolatedWorld(
340 world_id_, source, blink::BackForwardCacheAware::kAllow);
[email protected]e0be00992013-04-26 13:15:29341}
342
Doug Arnett2fd6cf22020-01-27 19:18:40343bool TranslateAgent::ExecuteScriptAndGetBoolResult(const std::string& script,
344 bool fallback) {
dglazkov24814772015-09-25 22:27:47345 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
[email protected]e0be00992013-04-26 13:15:29346 if (!main_frame)
347 return fallback;
348
[email protected]dc3d06e2013-09-06 12:21:03349 v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
Blink Reformat1c4d759e2017-04-09 16:34:54350 WebScriptSource source = WebScriptSource(WebString::FromASCII(script));
Hiroshige Hayashizakie1352e62018-05-14 23:03:06351 v8::Local<v8::Value> result =
Dave Tapuska306a72402021-04-09 14:20:02352 main_frame->ExecuteScriptInIsolatedWorldAndReturnValue(
353 world_id_, source, blink::BackForwardCacheAware::kAllow);
Hiroshige Hayashizakie1352e62018-05-14 23:03:06354 if (result.IsEmpty() || !result->IsBoolean()) {
[email protected]e0be00992013-04-26 13:15:29355 NOTREACHED();
356 return fallback;
357 }
358
Ross McIlroy1c8eb142018-08-01 15:12:07359 return result.As<v8::Boolean>()->Value();
[email protected]e0be00992013-04-26 13:15:29360}
361
Doug Arnett2fd6cf22020-01-27 19:18:40362std::string TranslateAgent::ExecuteScriptAndGetStringResult(
[email protected]e0be00992013-04-26 13:15:29363 const std::string& script) {
dglazkov24814772015-09-25 22:27:47364 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
[email protected]e0be00992013-04-26 13:15:29365 if (!main_frame)
366 return std::string();
367
Dan Elphickc027e8ae2018-07-27 10:10:10368 v8::Isolate* isolate = v8::Isolate::GetCurrent();
369 v8::HandleScope handle_scope(isolate);
Blink Reformat1c4d759e2017-04-09 16:34:54370 WebScriptSource source = WebScriptSource(WebString::FromASCII(script));
Hiroshige Hayashizakie1352e62018-05-14 23:03:06371 v8::Local<v8::Value> result =
Dave Tapuska306a72402021-04-09 14:20:02372 main_frame->ExecuteScriptInIsolatedWorldAndReturnValue(
373 world_id_, source, blink::BackForwardCacheAware::kAllow);
Hiroshige Hayashizakie1352e62018-05-14 23:03:06374 if (result.IsEmpty() || !result->IsString()) {
[email protected]e0be00992013-04-26 13:15:29375 NOTREACHED();
376 return std::string();
377 }
378
Hiroshige Hayashizakie1352e62018-05-14 23:03:06379 v8::Local<v8::String> v8_str = result.As<v8::String>();
Scott Littlef130e8e82020-08-07 16:01:37380 int length = v8_str->Utf8Length(isolate);
381 if (length <= 0)
382 return std::string();
383
384 std::string str(static_cast<size_t>(length), '\0');
385 v8_str->WriteUtf8(isolate, &str[0], length);
386 return str;
[email protected]e0be00992013-04-26 13:15:29387}
388
Doug Arnett2fd6cf22020-01-27 19:18:40389double TranslateAgent::ExecuteScriptAndGetDoubleResult(
[email protected]e0be00992013-04-26 13:15:29390 const std::string& script) {
dglazkov24814772015-09-25 22:27:47391 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
[email protected]e0be00992013-04-26 13:15:29392 if (!main_frame)
393 return 0.0;
394
[email protected]dc3d06e2013-09-06 12:21:03395 v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
Blink Reformat1c4d759e2017-04-09 16:34:54396 WebScriptSource source = WebScriptSource(WebString::FromASCII(script));
Hiroshige Hayashizakie1352e62018-05-14 23:03:06397 v8::Local<v8::Value> result =
Dave Tapuska306a72402021-04-09 14:20:02398 main_frame->ExecuteScriptInIsolatedWorldAndReturnValue(
399 world_id_, source, blink::BackForwardCacheAware::kAllow);
Hiroshige Hayashizakie1352e62018-05-14 23:03:06400 if (result.IsEmpty() || !result->IsNumber()) {
[email protected]e0be00992013-04-26 13:15:29401 NOTREACHED();
402 return 0.0;
403 }
404
Ross McIlroy1c8eb142018-08-01 15:12:07405 return result.As<v8::Number>()->Value();
[email protected]e0be00992013-04-26 13:15:29406}
407
Doug Arnett2fd6cf22020-01-27 19:18:40408int64_t TranslateAgent::ExecuteScriptAndGetIntegerResult(
gajendra.n5bcb90a2017-07-05 09:02:52409 const std::string& script) {
410 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
411 if (!main_frame)
412 return 0;
413
414 v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
gajendra.n5bcb90a2017-07-05 09:02:52415 WebScriptSource source = WebScriptSource(WebString::FromASCII(script));
Hiroshige Hayashizakie1352e62018-05-14 23:03:06416 v8::Local<v8::Value> result =
Dave Tapuska306a72402021-04-09 14:20:02417 main_frame->ExecuteScriptInIsolatedWorldAndReturnValue(
418 world_id_, source, blink::BackForwardCacheAware::kAllow);
Hiroshige Hayashizakie1352e62018-05-14 23:03:06419 if (result.IsEmpty() || !result->IsNumber()) {
gajendra.n5bcb90a2017-07-05 09:02:52420 NOTREACHED();
421 return 0;
422 }
423
Ross McIlroy1c8eb142018-08-01 15:12:07424 return result.As<v8::Integer>()->Value();
gajendra.n5bcb90a2017-07-05 09:02:52425}
426
Doug Arnett2fd6cf22020-01-27 19:18:40427// mojom::TranslateAgent implementations.
Doug Arnettc1436ac2020-04-08 03:10:05428void TranslateAgent::GetWebLanguageDetectionDetails(
429 GetWebLanguageDetectionDetailsCallback callback) {
430 NOTREACHED() << "This interface supported by PerFrameTranslateAgent";
431}
432
Doug Arnett2fd6cf22020-01-27 19:18:40433void TranslateAgent::TranslateFrame(const std::string& translate_script,
434 const std::string& source_lang,
435 const std::string& target_lang,
436 TranslateFrameCallback callback) {
dglazkov24814772015-09-25 22:27:47437 WebLocalFrame* main_frame = render_frame()->GetWebFrame();
leon.han3134fc62016-08-10 07:25:58438 if (!main_frame) {
439 // Cancelled.
tzik9a17d7af2017-05-09 14:21:58440 std::move(callback).Run(true, source_lang, target_lang,
441 TranslateErrors::NONE);
[email protected]81273622011-02-02 03:56:13442 return; // We navigated away, nothing to do.
leon.han3134fc62016-08-10 07:25:58443 }
[email protected]81273622011-02-02 03:56:13444
[email protected]4a2387e2013-05-31 03:47:56445 // A similar translation is already under way, nothing to do.
leon.han3134fc62016-08-10 07:25:58446 if (translate_callback_pending_ && target_lang_ == target_lang) {
447 // This request is ignored.
tzik9a17d7af2017-05-09 14:21:58448 std::move(callback).Run(true, source_lang, target_lang,
449 TranslateErrors::NONE);
[email protected]81273622011-02-02 03:56:13450 return;
leon.han3134fc62016-08-10 07:25:58451 }
[email protected]81273622011-02-02 03:56:13452
453 // Any pending translation is now irrelevant.
454 CancelPendingTranslation();
455
456 // Set our states.
tzik9a17d7af2017-05-09 14:21:58457 translate_callback_pending_ = std::move(callback);
[email protected]4a2387e2013-05-31 03:47:56458
[email protected]81273622011-02-02 03:56:13459 // If the source language is undetermined, we'll let the translate element
460 // detect it.
drogerf1da1802014-09-17 14:54:05461 source_lang_ = (source_lang != kUnknownLanguageCode) ? source_lang
462 : kAutoDetectionLanguage;
[email protected]81273622011-02-02 03:56:13463 target_lang_ = target_lang;
464
drogerf1da1802014-09-17 14:54:05465 ReportUserActionDuration(language_determined_time_, base::TimeTicks::Now());
[email protected]0f6afb42013-05-17 03:44:47466
Blink Reformat1c4d759e2017-04-09 16:34:54467 GURL url(main_frame->GetDocument().Url());
drogerf1da1802014-09-17 14:54:05468 ReportPageScheme(url.scheme());
[email protected]595ec512013-05-23 16:40:30469
Karandeep Bhatia4a8b4352020-07-16 06:57:13470 // Set up v8 isolated world.
471 EnsureIsolatedWorldInitialized(world_id_);
[email protected]afa9cd922013-07-30 06:12:19472
[email protected]81273622011-02-02 03:56:13473 if (!IsTranslateLibAvailable()) {
474 // Evaluate the script to add the translation related method to the global
475 // context of the page.
476 ExecuteScript(translate_script);
477 DCHECK(IsTranslateLibAvailable());
478 }
479
leon.han3134fc62016-08-10 07:25:58480 TranslatePageImpl(0);
[email protected]81273622011-02-02 03:56:13481}
482
Doug Arnett2fd6cf22020-01-27 19:18:40483void TranslateAgent::RevertTranslation() {
[email protected]81273622011-02-02 03:56:13484 if (!IsTranslateLibAvailable()) {
485 NOTREACHED();
486 return;
487 }
488
[email protected]81273622011-02-02 03:56:13489 CancelPendingTranslation();
490
[email protected]0dfc0e52013-04-15 14:53:00491 ExecuteScript("cr.googleTranslate.revert()");
[email protected]81273622011-02-02 03:56:13492}
493
leon.han3134fc62016-08-10 07:25:58494////////////////////////////////////////////////////////////////////////////////
Doug Arnett2fd6cf22020-01-27 19:18:40495// TranslateAgent, private:
496void TranslateAgent::CheckTranslateStatus() {
[email protected]85d252e2010-04-06 22:21:02497 // First check if there was an error.
498 if (HasTranslationFailed()) {
gajendra.n5bcb90a2017-07-05 09:02:52499 NotifyBrowserTranslationFailed(
500 static_cast<translate::TranslateErrors::Type>(GetErrorCode()));
[email protected]85d252e2010-04-06 22:21:02501 return; // There was an error.
502 }
503
504 if (HasTranslationFinished()) {
[email protected]d64b07b2010-04-20 22:14:06505 std::string actual_source_lang;
506 // Translation was successfull, if it was auto, retrieve the source
507 // language the Translate Element detected.
508 if (source_lang_ == kAutoDetectionLanguage) {
Evan Stade157a9212021-04-20 00:09:14509 actual_source_lang = GetPageSourceLanguage();
[email protected]d64b07b2010-04-20 22:14:06510 if (actual_source_lang.empty()) {
drogerf1da1802014-09-17 14:54:05511 NotifyBrowserTranslationFailed(TranslateErrors::UNKNOWN_LANGUAGE);
[email protected]579317e2010-06-30 20:13:48512 return;
513 } else if (actual_source_lang == target_lang_) {
drogerf1da1802014-09-17 14:54:05514 NotifyBrowserTranslationFailed(TranslateErrors::IDENTICAL_LANGUAGES);
[email protected]d64b07b2010-04-20 22:14:06515 return;
516 }
517 } else {
518 actual_source_lang = source_lang_;
519 }
520
leon.han3134fc62016-08-10 07:25:58521 if (!translate_callback_pending_) {
[email protected]d64b07b2010-04-20 22:14:06522 NOTREACHED();
523 return;
524 }
525
[email protected]e0be00992013-04-26 13:15:29526 // Check JavaScript performance counters for UMA reports.
drogerf1da1802014-09-17 14:54:05527 ReportTimeToTranslate(
[email protected]3a2ea8f2013-05-13 18:40:15528 ExecuteScriptAndGetDoubleResult("cr.googleTranslate.translationTime"));
Megan Jablonski27785782021-06-23 23:08:23529 ReportTranslatedLanguageDetectionContentLength(page_contents_length_);
[email protected]e0be00992013-04-26 13:15:29530
[email protected]d64b07b2010-04-20 22:14:06531 // Notify the browser we are done.
tzik9a17d7af2017-05-09 14:21:58532 std::move(translate_callback_pending_)
533 .Run(false, actual_source_lang, target_lang_, TranslateErrors::NONE);
[email protected]85d252e2010-04-06 22:21:02534 return;
535 }
536
537 // The translation is still pending, check again later.
Karolina Soltysc7fbc032018-10-25 10:10:08538 translate_task_runner_->PostDelayedTask(
tzik2bcf8e42018-07-31 11:22:15539 FROM_HERE,
Doug Arnett2fd6cf22020-01-27 19:18:40540 base::BindOnce(&TranslateAgent::CheckTranslateStatus,
tzik2bcf8e42018-07-31 11:22:15541 weak_method_factory_.GetWeakPtr()),
[email protected]0dfc0e52013-04-15 14:53:00542 AdjustDelay(kTranslateStatusCheckDelayMs));
[email protected]85d252e2010-04-06 22:21:02543}
544
Doug Arnett2fd6cf22020-01-27 19:18:40545void TranslateAgent::TranslatePageImpl(int count) {
[email protected]85d252e2010-04-06 22:21:02546 DCHECK_LT(count, kMaxTranslateInitCheckAttempts);
[email protected]85d252e2010-04-06 22:21:02547 if (!IsTranslateLibReady()) {
gajendra.n5bcb90a2017-07-05 09:02:52548 // There was an error during initialization of library.
549 TranslateErrors::Type error =
550 static_cast<translate::TranslateErrors::Type>(GetErrorCode());
551 if (error != TranslateErrors::NONE) {
552 NotifyBrowserTranslationFailed(error);
553 return;
554 }
555
[email protected]85d252e2010-04-06 22:21:02556 // The library is not ready, try again later, unless we have tried several
thestig259626c2015-11-23 20:18:11557 // times unsuccessfully already.
[email protected]85d252e2010-04-06 22:21:02558 if (++count >= kMaxTranslateInitCheckAttempts) {
gajendra.n5bcb90a2017-07-05 09:02:52559 NotifyBrowserTranslationFailed(TranslateErrors::TRANSLATION_TIMEOUT);
[email protected]85d252e2010-04-06 22:21:02560 return;
561 }
Hajime Hoshi701c6e12019-03-19 15:19:18562 translate_task_runner_->PostDelayedTask(
tzik2bcf8e42018-07-31 11:22:15563 FROM_HERE,
Doug Arnett2fd6cf22020-01-27 19:18:40564 base::BindOnce(&TranslateAgent::TranslatePageImpl,
tzik2bcf8e42018-07-31 11:22:15565 weak_method_factory_.GetWeakPtr(), count),
[email protected]0dfc0e52013-04-15 14:53:00566 AdjustDelay(count * kTranslateInitCheckDelayMs));
[email protected]85d252e2010-04-06 22:21:02567 return;
568 }
569
[email protected]e0be00992013-04-26 13:15:29570 // The library is loaded, and ready for translation now.
571 // Check JavaScript performance counters for UMA reports.
drogerf1da1802014-09-17 14:54:05572 ReportTimeToBeReady(
[email protected]3a2ea8f2013-05-13 18:40:15573 ExecuteScriptAndGetDoubleResult("cr.googleTranslate.readyTime"));
drogerf1da1802014-09-17 14:54:05574 ReportTimeToLoad(
[email protected]3a2ea8f2013-05-13 18:40:15575 ExecuteScriptAndGetDoubleResult("cr.googleTranslate.loadTime"));
[email protected]e0be00992013-04-26 13:15:29576
[email protected]d64b07b2010-04-20 22:14:06577 if (!StartTranslation()) {
gajendra.n5bcb90a2017-07-05 09:02:52578 CheckTranslateStatus();
[email protected]85d252e2010-04-06 22:21:02579 return;
580 }
581 // Check the status of the translation.
Karolina Soltysc7fbc032018-10-25 10:10:08582 translate_task_runner_->PostDelayedTask(
tzik2bcf8e42018-07-31 11:22:15583 FROM_HERE,
Doug Arnett2fd6cf22020-01-27 19:18:40584 base::BindOnce(&TranslateAgent::CheckTranslateStatus,
tzik2bcf8e42018-07-31 11:22:15585 weak_method_factory_.GetWeakPtr()),
[email protected]0dfc0e52013-04-15 14:53:00586 AdjustDelay(kTranslateStatusCheckDelayMs));
[email protected]85d252e2010-04-06 22:21:02587}
588
Doug Arnett2fd6cf22020-01-27 19:18:40589void TranslateAgent::NotifyBrowserTranslationFailed(
drogerf1da1802014-09-17 14:54:05590 TranslateErrors::Type error) {
leon.han3134fc62016-08-10 07:25:58591 DCHECK(translate_callback_pending_);
[email protected]85d252e2010-04-06 22:21:02592 // Notify the browser there was an error.
tzik9a17d7af2017-05-09 14:21:58593 std::move(translate_callback_pending_)
594 .Run(false, source_lang_, target_lang_, error);
leon.han3134fc62016-08-10 07:25:58595}
596
Julie Jeongeun Kimb1afbe52019-09-26 02:39:48597const mojo::Remote<mojom::ContentTranslateDriver>&
Doug Arnett2fd6cf22020-01-27 19:18:40598TranslateAgent::GetTranslateHandler() {
Michael Martis088a00a2018-04-12 09:33:05599 if (!translate_handler_) {
Mario Sanchez Prada8cbe4aa2019-12-03 10:00:20600 render_frame()->GetBrowserInterfaceBroker()->GetInterface(
Julie Jeongeun Kimb1afbe52019-09-26 02:39:48601 translate_handler_.BindNewPipeAndPassReceiver());
mcrousef0c2fb002021-10-13 00:40:48602 return translate_handler_;
leon.han3134fc62016-08-10 07:25:58603 }
604
mcrousef0c2fb002021-10-13 00:40:48605 // The translate handler can become unbound or disconnected in testing
606 // so this catches that case and reconnects so `this` can connect to
607 // the driver in the browser.
608 if (translate_handler_.is_bound() && translate_handler_.is_connected())
609 return translate_handler_;
610
611 translate_handler_.reset();
612 render_frame()->GetBrowserInterfaceBroker()->GetInterface(
613 translate_handler_.BindNewPipeAndPassReceiver());
Michael Martis088a00a2018-04-12 09:33:05614 return translate_handler_;
[email protected]7faa53862010-06-16 00:09:13615}
[email protected]e5a7a2a2014-03-27 16:16:03616
Doug Arnett2fd6cf22020-01-27 19:18:40617void TranslateAgent::ResetPage() {
Julie Jeongeun Kim9b734c52019-09-25 16:19:56618 receiver_.reset();
leon.hanb07801312016-08-12 11:47:57619 translate_callback_pending_.Reset();
620 CancelPendingTranslation();
621}
622
Doug Arnett2fd6cf22020-01-27 19:18:40623void TranslateAgent::OnDestruct() {
xjz694b50a92016-06-07 21:49:37624 delete this;
625}
626
Jon Nappera7dc1b002017-06-26 01:01:21627/* static */
Doug Arnett2fd6cf22020-01-27 19:18:40628std::string TranslateAgent::BuildTranslationScript(
Jon Nappera7dc1b002017-06-26 01:01:21629 const std::string& source_lang,
630 const std::string& target_lang) {
631 return "cr.googleTranslate.translate(" +
632 base::GetQuotedJSONString(source_lang) + "," +
633 base::GetQuotedJSONString(target_lang) + ")";
634}
635
mcrouse0d096d32021-02-11 01:58:46636void TranslateAgent::UpdateLanguageDetectionModel(base::File model_file) {
637 translate::LanguageDetectionModel& language_detection_model =
638 GetLanguageDetectionModel();
639 language_detection_model.UpdateWithFile(std::move(model_file));
640}
641
drogerf1da1802014-09-17 14:54:05642} // namespace translate