OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/translate/content/renderer/translate_helper.h" | 5 #include "components/translate/content/renderer/translate_helper.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/compiler_specific.h" | 8 #include "base/compiler_specific.h" |
9 #include "base/location.h" | 9 #include "base/location.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
11 #include "base/metrics/histogram_macros.h" | 11 #include "base/metrics/histogram_macros.h" |
12 #include "base/single_thread_task_runner.h" | 12 #include "base/single_thread_task_runner.h" |
13 #include "base/strings/string16.h" | 13 #include "base/strings/string16.h" |
14 #include "base/strings/string_util.h" | 14 #include "base/strings/string_util.h" |
15 #include "base/strings/utf_string_conversions.h" | 15 #include "base/strings/utf_string_conversions.h" |
16 #include "base/threading/thread_task_runner_handle.h" | 16 #include "base/threading/thread_task_runner_handle.h" |
17 #include "components/translate/content/common/translate_messages.h" | 17 #include "components/translate/content/common/translate_messages.h" |
18 #include "components/translate/content/renderer/renderer_cld_data_provider.h" | |
19 #include "components/translate/content/renderer/renderer_cld_data_provider_facto
ry.h" | |
20 #include "components/translate/content/renderer/renderer_cld_utils.h" | |
21 #include "components/translate/core/common/translate_constants.h" | 18 #include "components/translate/core/common/translate_constants.h" |
22 #include "components/translate/core/common/translate_metrics.h" | 19 #include "components/translate/core/common/translate_metrics.h" |
23 #include "components/translate/core/common/translate_util.h" | 20 #include "components/translate/core/common/translate_util.h" |
24 #include "components/translate/core/language_detection/language_detection_util.h
" | 21 #include "components/translate/core/language_detection/language_detection_util.h
" |
25 #include "content/public/common/content_constants.h" | 22 #include "content/public/common/content_constants.h" |
26 #include "content/public/common/url_constants.h" | 23 #include "content/public/common/url_constants.h" |
27 #include "content/public/renderer/render_frame.h" | 24 #include "content/public/renderer/render_frame.h" |
28 #include "content/public/renderer/render_thread.h" | 25 #include "content/public/renderer/render_thread.h" |
29 #include "ipc/ipc_platform_file.h" | 26 #include "ipc/ipc_platform_file.h" |
30 #include "third_party/WebKit/public/web/WebDocument.h" | 27 #include "third_party/WebKit/public/web/WebDocument.h" |
(...skipping 27 matching lines...) Expand all Loading... |
58 // The delay we wait in milliseconds before checking whether the translation has | 55 // The delay we wait in milliseconds before checking whether the translation has |
59 // finished. | 56 // finished. |
60 const int kTranslateStatusCheckDelayMs = 400; | 57 const int kTranslateStatusCheckDelayMs = 400; |
61 | 58 |
62 // Language name passed to the Translate element for it to detect the language. | 59 // Language name passed to the Translate element for it to detect the language. |
63 const char kAutoDetectionLanguage[] = "auto"; | 60 const char kAutoDetectionLanguage[] = "auto"; |
64 | 61 |
65 // Isolated world sets following content-security-policy. | 62 // Isolated world sets following content-security-policy. |
66 const char kContentSecurityPolicy[] = "script-src 'self' 'unsafe-eval'"; | 63 const char kContentSecurityPolicy[] = "script-src 'self' 'unsafe-eval'"; |
67 | 64 |
68 // Whether or not we have set the CLD callback yet. | |
69 bool g_cld_callback_set = false; | |
70 | |
71 // Obtain a new CLD data provider. Defined as a standalone method for ease of | |
72 // use in constructor initialization list. | |
73 std::unique_ptr<translate::RendererCldDataProvider> CreateDataProvider( | |
74 content::RenderFrameObserver* render_frame_observer) { | |
75 translate::RendererCldUtils::ConfigureDefaultDataProvider(); | |
76 return translate::RendererCldDataProviderFactory::Get() | |
77 ->CreateRendererCldDataProvider(render_frame_observer); | |
78 } | |
79 | |
80 // Returns whether the page associated with |document| is a candidate for | 65 // Returns whether the page associated with |document| is a candidate for |
81 // translation. Some pages can explictly specify (via a meta-tag) that they | 66 // translation. Some pages can explictly specify (via a meta-tag) that they |
82 // should not be translated. | 67 // should not be translated. |
83 // TODO(dglazkov): This logic should be moved into Blink. | 68 // TODO(dglazkov): This logic should be moved into Blink. |
84 bool HasNoTranslateMeta(WebDocument* document) { | 69 bool HasNoTranslateMeta(WebDocument* document) { |
85 WebElement head = document->head(); | 70 WebElement head = document->head(); |
86 if (head.isNull() || !head.hasChildNodes()) | 71 if (head.isNull() || !head.hasChildNodes()) |
87 return false; | 72 return false; |
88 | 73 |
89 const WebString meta(ASCIIToUTF16("meta")); | 74 const WebString meta(ASCIIToUTF16("meta")); |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
124 //////////////////////////////////////////////////////////////////////////////// | 109 //////////////////////////////////////////////////////////////////////////////// |
125 // TranslateHelper, public: | 110 // TranslateHelper, public: |
126 // | 111 // |
127 TranslateHelper::TranslateHelper(content::RenderFrame* render_frame, | 112 TranslateHelper::TranslateHelper(content::RenderFrame* render_frame, |
128 int world_id, | 113 int world_id, |
129 int extension_group, | 114 int extension_group, |
130 const std::string& extension_scheme) | 115 const std::string& extension_scheme) |
131 : content::RenderFrameObserver(render_frame), | 116 : content::RenderFrameObserver(render_frame), |
132 page_seq_no_(0), | 117 page_seq_no_(0), |
133 translation_pending_(false), | 118 translation_pending_(false), |
134 cld_data_provider_(CreateDataProvider(this)), | |
135 cld_data_polling_started_(false), | |
136 cld_data_polling_canceled_(false), | |
137 deferred_page_capture_(false), | 119 deferred_page_capture_(false), |
138 deferred_page_seq_no_(-1), | 120 deferred_page_seq_no_(-1), |
139 world_id_(world_id), | 121 world_id_(world_id), |
140 extension_group_(extension_group), | 122 extension_group_(extension_group), |
141 extension_scheme_(extension_scheme), | 123 extension_scheme_(extension_scheme), |
142 weak_method_factory_(this) {} | 124 weak_method_factory_(this) {} |
143 | 125 |
144 TranslateHelper::~TranslateHelper() { | 126 TranslateHelper::~TranslateHelper() { |
145 CancelPendingTranslation(); | |
146 CancelCldDataPolling(); | |
147 } | 127 } |
148 | 128 |
149 void TranslateHelper::PrepareForUrl(const GURL& url) { | 129 void TranslateHelper::PrepareForUrl(const GURL& url) { |
150 ++page_seq_no_; | 130 ++page_seq_no_; |
151 Send(new ChromeFrameHostMsg_TranslateAssignedSequenceNumber(routing_id(), | 131 Send(new ChromeFrameHostMsg_TranslateAssignedSequenceNumber(routing_id(), |
152 page_seq_no_)); | 132 page_seq_no_)); |
153 deferred_page_capture_ = false; | 133 deferred_page_capture_ = false; |
154 deferred_page_seq_no_ = -1; | 134 deferred_page_seq_no_ = -1; |
155 deferred_contents_.clear(); | 135 deferred_contents_.clear(); |
156 if (cld_data_polling_started_) | |
157 return; | |
158 | |
159 // TODO(andrewhayden): Refactor translate_manager.cc's IsTranslatableURL to | |
160 // components/translate/core/common/translate_util.cc, and ignore any URL | |
161 // that fails that check. This will require moving unit tests and rewiring | |
162 // other function calls as well, so for now replicate the logic here. | |
163 if (url.is_empty()) | |
164 return; | |
165 if (url.SchemeIs(content::kChromeUIScheme)) | |
166 return; | |
167 if (url.SchemeIs(content::kChromeDevToolsScheme)) | |
168 return; | |
169 if (url.SchemeIs(url::kFtpScheme)) | |
170 return; | |
171 if (url.SchemeIs(extension_scheme_.c_str())) | |
172 return; | |
173 | |
174 // Start polling for CLD data. | |
175 cld_data_polling_started_ = true; | |
176 TranslateHelper::SendCldDataRequest(0, 1000); | |
177 } | 136 } |
178 | 137 |
179 void TranslateHelper::PageCaptured(const base::string16& contents) { | 138 void TranslateHelper::PageCaptured(const base::string16& contents) { |
180 PageCapturedImpl(page_seq_no_, contents); | 139 PageCapturedImpl(page_seq_no_, contents); |
181 } | 140 } |
182 | 141 |
183 void TranslateHelper::PageCapturedImpl(int page_seq_no, | 142 void TranslateHelper::PageCapturedImpl(int page_seq_no, |
184 const base::string16& contents) { | 143 const base::string16& contents) { |
185 // Get the document language as set by WebKit from the http-equiv | 144 // Get the document language as set by WebKit from the http-equiv |
186 // meta tag for "content-language". This may or may not also | 145 // meta tag for "content-language". This may or may not also |
187 // have a value derived from the actual Content-Language HTTP | 146 // have a value derived from the actual Content-Language HTTP |
188 // header. The two actually have different meanings (despite the | 147 // header. The two actually have different meanings (despite the |
189 // original intent of http-equiv to be an equivalent) with the former | 148 // original intent of http-equiv to be an equivalent) with the former |
190 // being the language of the document and the latter being the | 149 // being the language of the document and the latter being the |
191 // language of the intended audience (a distinction really only | 150 // language of the intended audience (a distinction really only |
192 // relevant for things like langauge textbooks). This distinction | 151 // relevant for things like langauge textbooks). This distinction |
193 // shouldn't affect translation. | 152 // shouldn't affect translation. |
194 WebLocalFrame* main_frame = render_frame()->GetWebFrame(); | 153 WebLocalFrame* main_frame = render_frame()->GetWebFrame(); |
195 if (!main_frame || page_seq_no_ != page_seq_no) | 154 if (!main_frame || page_seq_no_ != page_seq_no) |
196 return; | 155 return; |
197 | 156 |
198 if (!cld_data_provider_->IsCldDataAvailable()) { | |
199 // We're in dynamic mode and CLD data isn't loaded. Retry when CLD data | |
200 // is loaded, if ever. | |
201 deferred_page_capture_ = true; | |
202 deferred_page_seq_no_ = page_seq_no; | |
203 deferred_contents_ = contents; | |
204 RecordLanguageDetectionTiming(DEFERRED); | |
205 return; | |
206 } | |
207 | |
208 if (deferred_page_seq_no_ == -1) { | |
209 // CLD data was available before language detection was requested. | |
210 RecordLanguageDetectionTiming(ON_TIME); | |
211 } else { | |
212 // This is a request that was triggered because CLD data is now available | |
213 // and was previously deferred. | |
214 RecordLanguageDetectionTiming(RESUMED); | |
215 } | |
216 | |
217 WebDocument document = main_frame->document(); | 157 WebDocument document = main_frame->document(); |
218 std::string content_language = document.contentLanguage().utf8(); | 158 std::string content_language = document.contentLanguage().utf8(); |
219 WebElement html_element = document.documentElement(); | 159 WebElement html_element = document.documentElement(); |
220 std::string html_lang; | 160 std::string html_lang; |
221 // |html_element| can be null element, e.g. in | 161 // |html_element| can be null element, e.g. in |
222 // BrowserTest.WindowOpenClose. | 162 // BrowserTest.WindowOpenClose. |
223 if (!html_element.isNull()) | 163 if (!html_element.isNull()) |
224 html_lang = html_element.getAttribute("lang").utf8(); | 164 html_lang = html_element.getAttribute("lang").utf8(); |
225 std::string cld_language; | 165 std::string cld_language; |
226 bool is_cld_reliable; | 166 bool is_cld_reliable; |
(...skipping 22 matching lines...) Expand all Loading... |
249 | 189 |
250 Send(new ChromeFrameHostMsg_TranslateLanguageDetermined( | 190 Send(new ChromeFrameHostMsg_TranslateLanguageDetermined( |
251 routing_id(), details, !details.has_notranslate && !language.empty())); | 191 routing_id(), details, !details.has_notranslate && !language.empty())); |
252 } | 192 } |
253 | 193 |
254 void TranslateHelper::CancelPendingTranslation() { | 194 void TranslateHelper::CancelPendingTranslation() { |
255 weak_method_factory_.InvalidateWeakPtrs(); | 195 weak_method_factory_.InvalidateWeakPtrs(); |
256 translation_pending_ = false; | 196 translation_pending_ = false; |
257 source_lang_.clear(); | 197 source_lang_.clear(); |
258 target_lang_.clear(); | 198 target_lang_.clear(); |
259 CancelCldDataPolling(); | |
260 } | 199 } |
261 | 200 |
262 //////////////////////////////////////////////////////////////////////////////// | 201 //////////////////////////////////////////////////////////////////////////////// |
263 // TranslateHelper, protected: | 202 // TranslateHelper, protected: |
264 // | 203 // |
265 bool TranslateHelper::IsTranslateLibAvailable() { | 204 bool TranslateHelper::IsTranslateLibAvailable() { |
266 return ExecuteScriptAndGetBoolResult( | 205 return ExecuteScriptAndGetBoolResult( |
267 "typeof cr != 'undefined' && typeof cr.googleTranslate != 'undefined' && " | 206 "typeof cr != 'undefined' && typeof cr.googleTranslate != 'undefined' && " |
268 "typeof cr.googleTranslate.translate == 'function'", false); | 207 "typeof cr.googleTranslate.translate == 'function'", false); |
269 } | 208 } |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
373 //////////////////////////////////////////////////////////////////////////////// | 312 //////////////////////////////////////////////////////////////////////////////// |
374 // TranslateHelper, private: | 313 // TranslateHelper, private: |
375 // | 314 // |
376 bool TranslateHelper::OnMessageReceived(const IPC::Message& message) { | 315 bool TranslateHelper::OnMessageReceived(const IPC::Message& message) { |
377 bool handled = true; | 316 bool handled = true; |
378 IPC_BEGIN_MESSAGE_MAP(TranslateHelper, message) | 317 IPC_BEGIN_MESSAGE_MAP(TranslateHelper, message) |
379 IPC_MESSAGE_HANDLER(ChromeFrameMsg_TranslatePage, OnTranslatePage) | 318 IPC_MESSAGE_HANDLER(ChromeFrameMsg_TranslatePage, OnTranslatePage) |
380 IPC_MESSAGE_HANDLER(ChromeFrameMsg_RevertTranslation, OnRevertTranslation) | 319 IPC_MESSAGE_HANDLER(ChromeFrameMsg_RevertTranslation, OnRevertTranslation) |
381 IPC_MESSAGE_UNHANDLED(handled = false) | 320 IPC_MESSAGE_UNHANDLED(handled = false) |
382 IPC_END_MESSAGE_MAP() | 321 IPC_END_MESSAGE_MAP() |
383 if (!handled) { | |
384 handled = cld_data_provider_->OnMessageReceived(message); | |
385 } | |
386 return handled; | 322 return handled; |
387 } | 323 } |
388 | 324 |
389 void TranslateHelper::OnTranslatePage(int page_seq_no, | 325 void TranslateHelper::OnTranslatePage(int page_seq_no, |
390 const std::string& translate_script, | 326 const std::string& translate_script, |
391 const std::string& source_lang, | 327 const std::string& source_lang, |
392 const std::string& target_lang) { | 328 const std::string& target_lang) { |
393 WebLocalFrame* main_frame = render_frame()->GetWebFrame(); | 329 WebLocalFrame* main_frame = render_frame()->GetWebFrame(); |
394 if (!main_frame || page_seq_no_ != page_seq_no) | 330 if (!main_frame || page_seq_no_ != page_seq_no) |
395 return; // We navigated away, nothing to do. | 331 return; // We navigated away, nothing to do. |
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
541 } | 477 } |
542 | 478 |
543 void TranslateHelper::NotifyBrowserTranslationFailed( | 479 void TranslateHelper::NotifyBrowserTranslationFailed( |
544 TranslateErrors::Type error) { | 480 TranslateErrors::Type error) { |
545 translation_pending_ = false; | 481 translation_pending_ = false; |
546 // Notify the browser there was an error. | 482 // Notify the browser there was an error. |
547 render_frame()->Send(new ChromeFrameHostMsg_PageTranslated( | 483 render_frame()->Send(new ChromeFrameHostMsg_PageTranslated( |
548 render_frame()->GetRoutingID(), source_lang_, target_lang_, error)); | 484 render_frame()->GetRoutingID(), source_lang_, target_lang_, error)); |
549 } | 485 } |
550 | 486 |
551 void TranslateHelper::CancelCldDataPolling() { | |
552 cld_data_polling_canceled_ = true; | |
553 } | |
554 | |
555 void TranslateHelper::SendCldDataRequest(const int delay_millis, | |
556 const int next_delay_millis) { | |
557 DCHECK_GE(delay_millis, 0); | |
558 DCHECK_GT(next_delay_millis, 0); | |
559 | |
560 // Terminate immediately if told to stop polling. | |
561 if (cld_data_polling_canceled_) { | |
562 DVLOG(1) << "Aborting CLD data request (polling canceled)"; | |
563 return; | |
564 } | |
565 | |
566 // Terminate immediately if data is already loaded. | |
567 if (cld_data_provider_->IsCldDataAvailable()) { | |
568 DVLOG(1) << "Aborting CLD data request (data available)"; | |
569 return; | |
570 } | |
571 | |
572 // Terminate immediately if the decayed delay is sufficiently large. | |
573 if (next_delay_millis > std::numeric_limits<int>::max() / 2) { | |
574 DVLOG(1) << "Aborting CLD data request (exceeded max number of requests)"; | |
575 cld_data_polling_started_ = false; | |
576 return; | |
577 } | |
578 | |
579 if (!g_cld_callback_set) { | |
580 g_cld_callback_set = true; | |
581 cld_data_provider_->SetCldAvailableCallback( | |
582 base::Bind(&TranslateHelper::OnCldDataAvailable, | |
583 weak_method_factory_.GetWeakPtr())); | |
584 } | |
585 | |
586 // Else, make an asynchronous request to get the data we need. | |
587 DVLOG(1) << "Requesting CLD data from data provider"; | |
588 cld_data_provider_->SendCldDataRequest(); | |
589 | |
590 // ... and enqueue another delayed task to call again. This will start a | |
591 // chain of polling that will last until the pointer stops being NULL, | |
592 // which is the right thing to do. | |
593 // NB: In the great majority of cases, the data file will be available and | |
594 // the very first delayed task will be a no-op that terminates the chain. | |
595 // It's only while downloading the file that this will chain for a | |
596 // nontrivial amount of time. | |
597 // Use a weak pointer to avoid keeping this helper object around forever. | |
598 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( | |
599 FROM_HERE, base::Bind(&TranslateHelper::SendCldDataRequest, | |
600 weak_method_factory_.GetWeakPtr(), | |
601 next_delay_millis, next_delay_millis * 2), | |
602 base::TimeDelta::FromMilliseconds(delay_millis)); | |
603 } | |
604 | |
605 void TranslateHelper::OnCldDataAvailable() { | |
606 if (deferred_page_capture_) { | |
607 deferred_page_capture_ = false; // Don't do this a second time. | |
608 PageCapturedImpl(deferred_page_seq_no_, deferred_contents_); | |
609 deferred_page_seq_no_ = -1; // Clean up for sanity | |
610 deferred_contents_.clear(); // Clean up for sanity | |
611 } | |
612 } | |
613 | |
614 void TranslateHelper::RecordLanguageDetectionTiming( | |
615 LanguageDetectionTiming timing) { | |
616 // The following comment is copied from page_load_histograms.cc, and applies | |
617 // just as equally here: | |
618 // | |
619 // Since there are currently no guarantees that renderer histograms will be | |
620 // sent to the browser, we initiate a PostTask here to be sure that we send | |
621 // the histograms we generated. Without this call, pages that don't have an | |
622 // on-close-handler might generate data that is lost when the renderer is | |
623 // shutdown abruptly (perchance because the user closed the tab). | |
624 DVLOG(1) << "Language detection timing: " << timing; | |
625 UMA_HISTOGRAM_ENUMERATION("Translate.LanguageDetectionTiming", timing, | |
626 LANGUAGE_DETECTION_TIMING_MAX_VALUE); | |
627 | |
628 // Note on performance: Under normal circumstances, this should get called | |
629 // once per page load. The code will either manage to do it ON_TIME or will | |
630 // be DEFERRED until CLD is ready. In the latter case, CLD is in dynamic mode | |
631 // and may eventually become available, triggering the RESUMED event; after | |
632 // this, everything should start being ON_TIME. This should never run more | |
633 // than twice in a page load, under any conditions. | |
634 // Also note that language detection is triggered off of a delay AFTER the | |
635 // page load completed event has fired, making this very much off the critical | |
636 // path. | |
637 content::RenderThread::Get()->UpdateHistograms( | |
638 content::kHistogramSynchronizerReservedSequenceNumber); | |
639 } | |
640 | |
641 void TranslateHelper::OnDestruct() { | 487 void TranslateHelper::OnDestruct() { |
642 delete this; | 488 delete this; |
643 } | 489 } |
644 | 490 |
645 } // namespace translate | 491 } // namespace translate |
OLD | NEW |