Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / components / translate / content / renderer / translate_helper.cc
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
3 // found in the LICENSE file.
4
5 #include "components/translate/content/renderer/translate_helper.h"
6
7 #include "base/bind.h"
8 #include "base/compiler_specific.h"
9 #include "base/logging.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/metrics/histogram.h"
12 #include "base/strings/string16.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "components/translate/content/common/translate_messages.h"
16 #include "components/translate/core/common/translate_constants.h"
17 #include "components/translate/core/common/translate_metrics.h"
18 #include "components/translate/core/common/translate_util.h"
19 #include "components/translate/core/language_detection/language_detection_util.h"
20 #include "content/public/common/content_constants.h"
21 #include "content/public/common/url_constants.h"
22 #include "content/public/renderer/render_thread.h"
23 #include "content/public/renderer/render_view.h"
24 #include "ipc/ipc_platform_file.h"
25 #include "third_party/WebKit/public/web/WebDocument.h"
26 #include "third_party/WebKit/public/web/WebElement.h"
27 #include "third_party/WebKit/public/web/WebFrame.h"
28 #include "third_party/WebKit/public/web/WebNode.h"
29 #include "third_party/WebKit/public/web/WebNodeList.h"
30 #include "third_party/WebKit/public/web/WebScriptSource.h"
31 #include "third_party/WebKit/public/web/WebView.h"
32 #include "third_party/WebKit/public/web/WebWidget.h"
33 #include "url/gurl.h"
34 #include "v8/include/v8.h"
35
36 using base::ASCIIToUTF16;
37 using blink::WebDocument;
38 using blink::WebElement;
39 using blink::WebFrame;
40 using blink::WebNode;
41 using blink::WebNodeList;
42 using blink::WebScriptSource;
43 using blink::WebSecurityOrigin;
44 using blink::WebString;
45 using blink::WebVector;
46 using blink::WebView;
47
48 namespace {
49
50 // The delay in milliseconds that we'll wait before checking to see if the
51 // translate library injected in the page is ready.
52 const int kTranslateInitCheckDelayMs = 150;
53
54 // The maximum number of times we'll check to see if the translate library
55 // injected in the page is ready.
56 const int kMaxTranslateInitCheckAttempts = 5;
57
58 // The delay we wait in milliseconds before checking whether the translation has
59 // finished.
60 const int kTranslateStatusCheckDelayMs = 400;
61
62 // Language name passed to the Translate element for it to detect the language.
63 const char kAutoDetectionLanguage[] = "auto";
64
65 // Isolated world sets following content-security-policy.
66 const char kContentSecurityPolicy[] = "script-src 'self' 'unsafe-eval'";
67
68 // Whether or not we have set the CLD callback yet.
69 bool g_cld_callback_set = false;
70
71 }  // namespace
72
73 namespace translate {
74
75 ////////////////////////////////////////////////////////////////////////////////
76 // TranslateHelper, public:
77 //
78 TranslateHelper::TranslateHelper(content::RenderView* render_view,
79                                  int world_id,
80                                  int extension_group,
81                                  const std::string& extension_scheme)
82     : content::RenderViewObserver(render_view),
83       page_seq_no_(0),
84       translation_pending_(false),
85       cld_data_provider_(translate::CreateRendererCldDataProviderFor(this)),
86       cld_data_polling_started_(false),
87       cld_data_polling_canceled_(false),
88       deferred_page_capture_(false),
89       deferred_page_seq_no_(-1),
90       world_id_(world_id),
91       extension_group_(extension_group),
92       extension_scheme_(extension_scheme),
93       weak_method_factory_(this) {
94 }
95
96 TranslateHelper::~TranslateHelper() {
97   CancelPendingTranslation();
98   CancelCldDataPolling();
99 }
100
101 void TranslateHelper::PrepareForUrl(const GURL& url) {
102   ++page_seq_no_;
103   Send(new ChromeViewHostMsg_TranslateAssignedSequenceNumber(
104       routing_id(), page_seq_no_));
105   deferred_page_capture_ = false;
106   deferred_page_seq_no_ = -1;
107   deferred_contents_.clear();
108   if (cld_data_polling_started_)
109     return;
110
111   // TODO(andrewhayden): Refactor translate_manager.cc's IsTranslatableURL to
112   // components/translate/core/common/translate_util.cc, and ignore any URL
113   // that fails that check. This will require moving unit tests and rewiring
114   // other function calls as well, so for now replicate the logic here.
115   if (url.is_empty())
116     return;
117   if (url.SchemeIs(content::kChromeUIScheme))
118     return;
119   if (url.SchemeIs(content::kChromeDevToolsScheme))
120     return;
121   if (url.SchemeIs(url::kFtpScheme))
122     return;
123   if (url.SchemeIs(extension_scheme_.c_str()))
124     return;
125
126   // Start polling for CLD data.
127   cld_data_polling_started_ = true;
128   TranslateHelper::SendCldDataRequest(0, 1000);
129 }
130
131 void TranslateHelper::PageCaptured(const base::string16& contents) {
132   PageCapturedImpl(page_seq_no_, contents);
133 }
134
135 void TranslateHelper::PageCapturedImpl(int page_seq_no,
136                                        const base::string16& contents) {
137   // Get the document language as set by WebKit from the http-equiv
138   // meta tag for "content-language".  This may or may not also
139   // have a value derived from the actual Content-Language HTTP
140   // header.  The two actually have different meanings (despite the
141   // original intent of http-equiv to be an equivalent) with the former
142   // being the language of the document and the latter being the
143   // language of the intended audience (a distinction really only
144   // relevant for things like langauge textbooks).  This distinction
145   // shouldn't affect translation.
146   WebFrame* main_frame = GetMainFrame();
147   if (!main_frame || page_seq_no_ != page_seq_no)
148     return;
149
150   if (!cld_data_provider_->IsCldDataAvailable()) {
151     // We're in dynamic mode and CLD data isn't loaded. Retry when CLD data
152     // is loaded, if ever.
153     deferred_page_capture_ = true;
154     deferred_page_seq_no_ = page_seq_no;
155     deferred_contents_ = contents;
156     RecordLanguageDetectionTiming(DEFERRED);
157     return;
158   }
159
160   if (deferred_page_seq_no_ == -1) {
161     // CLD data was available before language detection was requested.
162     RecordLanguageDetectionTiming(ON_TIME);
163   } else {
164     // This is a request that was triggered because CLD data is now available
165     // and was previously deferred.
166     RecordLanguageDetectionTiming(RESUMED);
167   }
168
169   WebDocument document = main_frame->document();
170   std::string content_language = document.contentLanguage().utf8();
171   WebElement html_element = document.documentElement();
172   std::string html_lang;
173   // |html_element| can be null element, e.g. in
174   // BrowserTest.WindowOpenClose.
175   if (!html_element.isNull())
176     html_lang = html_element.getAttribute("lang").utf8();
177   std::string cld_language;
178   bool is_cld_reliable;
179   std::string language = DeterminePageLanguage(
180       content_language, html_lang, contents, &cld_language, &is_cld_reliable);
181
182   if (language.empty())
183     return;
184
185   language_determined_time_ = base::TimeTicks::Now();
186
187   GURL url(document.url());
188   LanguageDetectionDetails details;
189   details.time = base::Time::Now();
190   details.url = url;
191   details.content_language = content_language;
192   details.cld_language = cld_language;
193   details.is_cld_reliable = is_cld_reliable;
194   details.html_root_language = html_lang;
195   details.adopted_language = language;
196
197   // TODO(hajimehoshi): If this affects performance, it should be set only if
198   // translate-internals tab exists.
199   details.contents = contents;
200
201   Send(new ChromeViewHostMsg_TranslateLanguageDetermined(
202       routing_id(),
203       details,
204       IsTranslationAllowed(&document) && !language.empty()));
205 }
206
207 void TranslateHelper::CancelPendingTranslation() {
208   weak_method_factory_.InvalidateWeakPtrs();
209   translation_pending_ = false;
210   source_lang_.clear();
211   target_lang_.clear();
212   CancelCldDataPolling();
213 }
214
215 ////////////////////////////////////////////////////////////////////////////////
216 // TranslateHelper, protected:
217 //
218 bool TranslateHelper::IsTranslateLibAvailable() {
219   return ExecuteScriptAndGetBoolResult(
220       "typeof cr != 'undefined' && typeof cr.googleTranslate != 'undefined' && "
221       "typeof cr.googleTranslate.translate == 'function'", false);
222 }
223
224 bool TranslateHelper::IsTranslateLibReady() {
225   return ExecuteScriptAndGetBoolResult("cr.googleTranslate.libReady", false);
226 }
227
228 bool TranslateHelper::HasTranslationFinished() {
229   return ExecuteScriptAndGetBoolResult("cr.googleTranslate.finished", true);
230 }
231
232 bool TranslateHelper::HasTranslationFailed() {
233   return ExecuteScriptAndGetBoolResult("cr.googleTranslate.error", true);
234 }
235
236 bool TranslateHelper::StartTranslation() {
237   std::string script = "cr.googleTranslate.translate('" +
238                        source_lang_ +
239                        "','" +
240                        target_lang_ +
241                        "')";
242   return ExecuteScriptAndGetBoolResult(script, false);
243 }
244
245 std::string TranslateHelper::GetOriginalPageLanguage() {
246   return ExecuteScriptAndGetStringResult("cr.googleTranslate.sourceLang");
247 }
248
249 base::TimeDelta TranslateHelper::AdjustDelay(int delayInMs) {
250   // Just converts |delayInMs| without any modification in practical cases.
251   // Tests will override this function to return modified value.
252   return base::TimeDelta::FromMilliseconds(delayInMs);
253 }
254
255 void TranslateHelper::ExecuteScript(const std::string& script) {
256   WebFrame* main_frame = GetMainFrame();
257   if (!main_frame)
258     return;
259
260   WebScriptSource source = WebScriptSource(ASCIIToUTF16(script));
261   main_frame->executeScriptInIsolatedWorld(
262       world_id_, &source, 1, extension_group_);
263 }
264
265 bool TranslateHelper::ExecuteScriptAndGetBoolResult(const std::string& script,
266                                                     bool fallback) {
267   WebFrame* main_frame = GetMainFrame();
268   if (!main_frame)
269     return fallback;
270
271   v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
272   WebVector<v8::Local<v8::Value> > results;
273   WebScriptSource source = WebScriptSource(ASCIIToUTF16(script));
274   main_frame->executeScriptInIsolatedWorld(
275       world_id_, &source, 1, extension_group_, &results);
276   if (results.size() != 1 || results[0].IsEmpty() || !results[0]->IsBoolean()) {
277     NOTREACHED();
278     return fallback;
279   }
280
281   return results[0]->BooleanValue();
282 }
283
284 std::string TranslateHelper::ExecuteScriptAndGetStringResult(
285     const std::string& script) {
286   WebFrame* main_frame = GetMainFrame();
287   if (!main_frame)
288     return std::string();
289
290   v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
291   WebVector<v8::Local<v8::Value> > results;
292   WebScriptSource source = WebScriptSource(ASCIIToUTF16(script));
293   main_frame->executeScriptInIsolatedWorld(
294       world_id_, &source, 1, extension_group_, &results);
295   if (results.size() != 1 || results[0].IsEmpty() || !results[0]->IsString()) {
296     NOTREACHED();
297     return std::string();
298   }
299
300   v8::Local<v8::String> v8_str = results[0]->ToString();
301   int length = v8_str->Utf8Length() + 1;
302   scoped_ptr<char[]> str(new char[length]);
303   v8_str->WriteUtf8(str.get(), length);
304   return std::string(str.get());
305 }
306
307 double TranslateHelper::ExecuteScriptAndGetDoubleResult(
308     const std::string& script) {
309   WebFrame* main_frame = GetMainFrame();
310   if (!main_frame)
311     return 0.0;
312
313   v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
314   WebVector<v8::Local<v8::Value> > results;
315   WebScriptSource source = WebScriptSource(ASCIIToUTF16(script));
316   main_frame->executeScriptInIsolatedWorld(
317       world_id_, &source, 1, extension_group_, &results);
318   if (results.size() != 1 || results[0].IsEmpty() || !results[0]->IsNumber()) {
319     NOTREACHED();
320     return 0.0;
321   }
322
323   return results[0]->NumberValue();
324 }
325
326 ////////////////////////////////////////////////////////////////////////////////
327 // TranslateHelper, private:
328 //
329
330 // static
331 bool TranslateHelper::IsTranslationAllowed(WebDocument* document) {
332   WebElement head = document->head();
333   if (head.isNull() || !head.hasChildNodes())
334     return true;
335
336   const WebString meta(ASCIIToUTF16("meta"));
337   const WebString name(ASCIIToUTF16("name"));
338   const WebString google(ASCIIToUTF16("google"));
339   const WebString value(ASCIIToUTF16("value"));
340   const WebString content(ASCIIToUTF16("content"));
341
342   WebNodeList children = head.childNodes();
343   for (size_t i = 0; i < children.length(); ++i) {
344     WebNode node = children.item(i);
345     if (!node.isElementNode())
346       continue;
347     WebElement element = node.to<WebElement>();
348     // Check if a tag is <meta>.
349     if (!element.hasHTMLTagName(meta))
350       continue;
351     // Check if the tag contains name="google".
352     WebString attribute = element.getAttribute(name);
353     if (attribute.isNull() || attribute != google)
354       continue;
355     // Check if the tag contains value="notranslate", or content="notranslate".
356     attribute = element.getAttribute(value);
357     if (attribute.isNull())
358       attribute = element.getAttribute(content);
359     if (attribute.isNull())
360       continue;
361     if (LowerCaseEqualsASCII(attribute, "notranslate"))
362       return false;
363   }
364   return true;
365 }
366
367 bool TranslateHelper::OnMessageReceived(const IPC::Message& message) {
368   bool handled = true;
369   IPC_BEGIN_MESSAGE_MAP(TranslateHelper, message)
370     IPC_MESSAGE_HANDLER(ChromeViewMsg_TranslatePage, OnTranslatePage)
371     IPC_MESSAGE_HANDLER(ChromeViewMsg_RevertTranslation, OnRevertTranslation)
372     IPC_MESSAGE_UNHANDLED(handled = false)
373   IPC_END_MESSAGE_MAP()
374   if (!handled) {
375     handled = cld_data_provider_->OnMessageReceived(message);
376   }
377   return handled;
378 }
379
380 void TranslateHelper::OnTranslatePage(int page_seq_no,
381                                       const std::string& translate_script,
382                                       const std::string& source_lang,
383                                       const std::string& target_lang) {
384   WebFrame* main_frame = GetMainFrame();
385   if (!main_frame || page_seq_no_ != page_seq_no)
386     return;  // We navigated away, nothing to do.
387
388   // A similar translation is already under way, nothing to do.
389   if (translation_pending_ && target_lang_ == target_lang)
390     return;
391
392   // Any pending translation is now irrelevant.
393   CancelPendingTranslation();
394
395   // Set our states.
396   translation_pending_ = true;
397
398   // If the source language is undetermined, we'll let the translate element
399   // detect it.
400   source_lang_ = (source_lang != kUnknownLanguageCode) ? source_lang
401                                                        : kAutoDetectionLanguage;
402   target_lang_ = target_lang;
403
404   ReportUserActionDuration(language_determined_time_, base::TimeTicks::Now());
405
406   GURL url(main_frame->document().url());
407   ReportPageScheme(url.scheme());
408
409   // Set up v8 isolated world with proper content-security-policy and
410   // security-origin.
411   WebFrame* frame = GetMainFrame();
412   if (frame) {
413     frame->setIsolatedWorldContentSecurityPolicy(
414         world_id_, WebString::fromUTF8(kContentSecurityPolicy));
415
416     GURL security_origin = GetTranslateSecurityOrigin();
417     frame->setIsolatedWorldSecurityOrigin(
418         world_id_, WebSecurityOrigin::create(security_origin));
419   }
420
421   if (!IsTranslateLibAvailable()) {
422     // Evaluate the script to add the translation related method to the global
423     // context of the page.
424     ExecuteScript(translate_script);
425     DCHECK(IsTranslateLibAvailable());
426   }
427
428   TranslatePageImpl(page_seq_no, 0);
429 }
430
431 void TranslateHelper::OnRevertTranslation(int page_seq_no) {
432   if (page_seq_no_ != page_seq_no)
433     return;  // We navigated away, nothing to do.
434
435   if (!IsTranslateLibAvailable()) {
436     NOTREACHED();
437     return;
438   }
439
440   CancelPendingTranslation();
441
442   ExecuteScript("cr.googleTranslate.revert()");
443 }
444
445 void TranslateHelper::CheckTranslateStatus(int page_seq_no) {
446   // If this is not the same page, the translation has been canceled.  If the
447   // view is gone, the page is closing.
448   if (page_seq_no_ != page_seq_no || !render_view()->GetWebView())
449     return;
450
451   // First check if there was an error.
452   if (HasTranslationFailed()) {
453     // TODO(toyoshim): Check |errorCode| of translate.js and notify it here.
454     NotifyBrowserTranslationFailed(TranslateErrors::TRANSLATION_ERROR);
455     return;  // There was an error.
456   }
457
458   if (HasTranslationFinished()) {
459     std::string actual_source_lang;
460     // Translation was successfull, if it was auto, retrieve the source
461     // language the Translate Element detected.
462     if (source_lang_ == kAutoDetectionLanguage) {
463       actual_source_lang = GetOriginalPageLanguage();
464       if (actual_source_lang.empty()) {
465         NotifyBrowserTranslationFailed(TranslateErrors::UNKNOWN_LANGUAGE);
466         return;
467       } else if (actual_source_lang == target_lang_) {
468         NotifyBrowserTranslationFailed(TranslateErrors::IDENTICAL_LANGUAGES);
469         return;
470       }
471     } else {
472       actual_source_lang = source_lang_;
473     }
474
475     if (!translation_pending_) {
476       NOTREACHED();
477       return;
478     }
479
480     translation_pending_ = false;
481
482     // Check JavaScript performance counters for UMA reports.
483     ReportTimeToTranslate(
484         ExecuteScriptAndGetDoubleResult("cr.googleTranslate.translationTime"));
485
486     // Notify the browser we are done.
487     render_view()->Send(
488         new ChromeViewHostMsg_PageTranslated(render_view()->GetRoutingID(),
489                                              actual_source_lang,
490                                              target_lang_,
491                                              TranslateErrors::NONE));
492     return;
493   }
494
495   // The translation is still pending, check again later.
496   base::MessageLoop::current()->PostDelayedTask(
497       FROM_HERE,
498       base::Bind(&TranslateHelper::CheckTranslateStatus,
499                  weak_method_factory_.GetWeakPtr(), page_seq_no),
500       AdjustDelay(kTranslateStatusCheckDelayMs));
501 }
502
503 void TranslateHelper::TranslatePageImpl(int page_seq_no, int count) {
504   DCHECK_LT(count, kMaxTranslateInitCheckAttempts);
505   if (page_seq_no_ != page_seq_no || !render_view()->GetWebView())
506     return;
507
508   if (!IsTranslateLibReady()) {
509     // The library is not ready, try again later, unless we have tried several
510     // times unsucessfully already.
511     if (++count >= kMaxTranslateInitCheckAttempts) {
512       NotifyBrowserTranslationFailed(TranslateErrors::INITIALIZATION_ERROR);
513       return;
514     }
515     base::MessageLoop::current()->PostDelayedTask(
516         FROM_HERE,
517         base::Bind(&TranslateHelper::TranslatePageImpl,
518                    weak_method_factory_.GetWeakPtr(),
519                    page_seq_no, count),
520         AdjustDelay(count * kTranslateInitCheckDelayMs));
521     return;
522   }
523
524   // The library is loaded, and ready for translation now.
525   // Check JavaScript performance counters for UMA reports.
526   ReportTimeToBeReady(
527       ExecuteScriptAndGetDoubleResult("cr.googleTranslate.readyTime"));
528   ReportTimeToLoad(
529       ExecuteScriptAndGetDoubleResult("cr.googleTranslate.loadTime"));
530
531   if (!StartTranslation()) {
532     NotifyBrowserTranslationFailed(TranslateErrors::TRANSLATION_ERROR);
533     return;
534   }
535   // Check the status of the translation.
536   base::MessageLoop::current()->PostDelayedTask(
537       FROM_HERE,
538       base::Bind(&TranslateHelper::CheckTranslateStatus,
539                  weak_method_factory_.GetWeakPtr(), page_seq_no),
540       AdjustDelay(kTranslateStatusCheckDelayMs));
541 }
542
543 void TranslateHelper::NotifyBrowserTranslationFailed(
544     TranslateErrors::Type error) {
545   translation_pending_ = false;
546   // Notify the browser there was an error.
547   render_view()->Send(new ChromeViewHostMsg_PageTranslated(
548       render_view()->GetRoutingID(), source_lang_, target_lang_, error));
549 }
550
551 WebFrame* TranslateHelper::GetMainFrame() {
552   WebView* web_view = render_view()->GetWebView();
553
554   // When the tab is going to be closed, the web_view can be NULL.
555   if (!web_view)
556     return NULL;
557
558   return web_view->mainFrame();
559 }
560
561 void TranslateHelper::CancelCldDataPolling() {
562   cld_data_polling_canceled_ = true;
563 }
564
565 void TranslateHelper::SendCldDataRequest(const int delay_millis,
566                                          const int next_delay_millis) {
567   // Terminate immediately if told to stop polling.
568   if (cld_data_polling_canceled_)
569     return;
570
571   // Terminate immediately if data is already loaded.
572   if (cld_data_provider_->IsCldDataAvailable())
573     return;
574
575   if (!g_cld_callback_set) {
576     g_cld_callback_set = true;
577     cld_data_provider_->SetCldAvailableCallback(
578         base::Bind(&TranslateHelper::OnCldDataAvailable,
579                    weak_method_factory_.GetWeakPtr()));
580   }
581
582   // Else, make an asynchronous request to get the data we need.
583   cld_data_provider_->SendCldDataRequest();
584
585   // ... and enqueue another delayed task to call again. This will start a
586   // chain of polling that will last until the pointer stops being NULL,
587   // which is the right thing to do.
588   // NB: In the great majority of cases, the data file will be available and
589   // the very first delayed task will be a no-op that terminates the chain.
590   // It's only while downloading the file that this will chain for a
591   // nontrivial amount of time.
592   // Use a weak pointer to avoid keeping this helper object around forever.
593   base::MessageLoop::current()->PostDelayedTask(
594       FROM_HERE,
595       base::Bind(&TranslateHelper::SendCldDataRequest,
596                  weak_method_factory_.GetWeakPtr(),
597                  next_delay_millis,
598                  next_delay_millis),
599       base::TimeDelta::FromMilliseconds(delay_millis));
600 }
601
602 void TranslateHelper::OnCldDataAvailable() {
603   if (deferred_page_capture_) {
604     deferred_page_capture_ = false; // Don't do this a second time.
605     PageCapturedImpl(deferred_page_seq_no_, deferred_contents_);
606     deferred_page_seq_no_ = -1; // Clean up for sanity
607     deferred_contents_.clear(); // Clean up for sanity
608   }
609 }
610
611 void TranslateHelper::RecordLanguageDetectionTiming(
612     LanguageDetectionTiming timing) {
613   // The following comment is copied from page_load_histograms.cc, and applies
614   // just as equally here:
615   //
616   // Since there are currently no guarantees that renderer histograms will be
617   // sent to the browser, we initiate a PostTask here to be sure that we send
618   // the histograms we generated.  Without this call, pages that don't have an
619   // on-close-handler might generate data that is lost when the renderer is
620   // shutdown abruptly (perchance because the user closed the tab).
621   DVLOG(1) << "Language detection timing: " << timing;
622   UMA_HISTOGRAM_ENUMERATION("Translate.LanguageDetectionTiming", timing,
623                             LANGUAGE_DETECTION_TIMING_MAX_VALUE);
624
625   // Note on performance: Under normal circumstances, this should get called
626   // once per page load. The code will either manage to do it ON_TIME or will
627   // be DEFERRED until CLD is ready. In the latter case, CLD is in dynamic mode
628   // and may eventually become available, triggering the RESUMED event; after
629   // this, everything should start being ON_TIME. This should never run more
630   // than twice in a page load, under any conditions.
631   // Also note that language detection is triggered off of a delay AFTER the
632   // page load completed event has fired, making this very much off the critical
633   // path.
634   content::RenderThread::Get()->UpdateHistograms(
635       content::kHistogramSynchronizerReservedSequenceNumber);
636 }
637
638 }  // namespace translate