1 // Copyright 2013 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 "chrome/browser/translate/translate_script.h" 6 7 #include "base/bind.h" 8 #include "base/command_line.h" 9 #include "base/logging.h" 10 #include "base/message_loop/message_loop.h" 11 #include "base/strings/string_piece.h" 12 #include "base/strings/string_util.h" 13 #include "base/strings/stringprintf.h" 14 #include "chrome/browser/translate/translate_url_fetcher.h" 15 #include "chrome/browser/translate/translate_url_util.h" 16 #include "chrome/common/chrome_switches.h" 17 #include "components/translate/common/translate_util.h" 18 #include "google_apis/google_api_keys.h" 19 #include "grit/browser_resources.h" 20 #include "net/base/escape.h" 21 #include "net/base/url_util.h" 22 #include "ui/base/resource/resource_bundle.h" 23 24 namespace { 25 26 const int kExpirationDelayDays = 1; 27 28 } // namespace 29 30 const char TranslateScript::kScriptURL[] = 31 "https://translate.google.com/translate_a/element.js"; 32 const char TranslateScript::kRequestHeader[] = 33 "Google-Translate-Element-Mode: library"; 34 const char TranslateScript::kAlwaysUseSslQueryName[] = "aus"; 35 const char TranslateScript::kAlwaysUseSslQueryValue[] = "true"; 36 const char TranslateScript::kCallbackQueryName[] = "cb"; 37 const char TranslateScript::kCallbackQueryValue[] = 38 "cr.googleTranslate.onTranslateElementLoad"; 39 const char TranslateScript::kCssLoaderCallbackQueryName[] = "clc"; 40 const char TranslateScript::kCssLoaderCallbackQueryValue[] = 41 "cr.googleTranslate.onLoadCSS"; 42 const char TranslateScript::kJavascriptLoaderCallbackQueryName[] = "jlc"; 43 const char TranslateScript::kJavascriptLoaderCallbackQueryValue[] = 44 "cr.googleTranslate.onLoadJavascript"; 45 46 TranslateScript::TranslateScript() 47 : expiration_delay_(base::TimeDelta::FromDays(kExpirationDelayDays)), 48 weak_method_factory_(this) { 49 } 50 51 TranslateScript::~TranslateScript() { 52 } 53 54 void TranslateScript::Request(const Callback& callback) { 55 if (fetcher_.get() != NULL) { 56 NOTREACHED(); 57 return; 58 } 59 60 callback_ = callback; 61 62 GURL translate_script_url; 63 // Check if command-line contains an alternative URL for translate service. 64 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 65 if (command_line.HasSwitch(switches::kTranslateScriptURL)) { 66 translate_script_url = GURL( 67 command_line.GetSwitchValueASCII(switches::kTranslateScriptURL)); 68 if (!translate_script_url.is_valid() || 69 !translate_script_url.query().empty()) { 70 LOG(WARNING) << "The following translate URL specified at the " 71 << "command-line is invalid: " 72 << translate_script_url.spec(); 73 translate_script_url = GURL(); 74 } 75 } 76 77 // Use default URL when command-line argument is not specified, or specified 78 // URL is invalid. 79 if (translate_script_url.is_empty()) 80 translate_script_url = GURL(kScriptURL); 81 82 translate_script_url = net::AppendQueryParameter( 83 translate_script_url, 84 kCallbackQueryName, 85 kCallbackQueryValue); 86 translate_script_url = net::AppendQueryParameter( 87 translate_script_url, 88 kAlwaysUseSslQueryName, 89 kAlwaysUseSslQueryValue); 90 #if !defined(OS_IOS) 91 // iOS doesn't need to use specific loaders for the isolated world. 92 translate_script_url = net::AppendQueryParameter( 93 translate_script_url, 94 kCssLoaderCallbackQueryName, 95 kCssLoaderCallbackQueryValue); 96 translate_script_url = net::AppendQueryParameter( 97 translate_script_url, 98 kJavascriptLoaderCallbackQueryName, 99 kJavascriptLoaderCallbackQueryValue); 100 #endif // !defined(OS_IOS) 101 102 translate_script_url = 103 TranslateURLUtil::AddHostLocaleToUrl(translate_script_url); 104 translate_script_url = 105 TranslateURLUtil::AddApiKeyToUrl(translate_script_url); 106 107 fetcher_.reset(new TranslateURLFetcher(kFetcherId)); 108 fetcher_->set_extra_request_header(kRequestHeader); 109 fetcher_->Request( 110 translate_script_url, 111 base::Bind(&TranslateScript::OnScriptFetchComplete, 112 base::Unretained(this))); 113 } 114 115 116 void TranslateScript::OnScriptFetchComplete( 117 int id, bool success, const std::string& data) { 118 DCHECK_EQ(kFetcherId, id); 119 120 scoped_ptr<const TranslateURLFetcher> delete_ptr(fetcher_.release()); 121 122 if (success) { 123 DCHECK(data_.empty()); 124 // Insert variable definitions on API Key and security origin. 125 data_ = base::StringPrintf("var translateApiKey = '%s';\n", 126 google_apis::GetAPIKey().c_str()); 127 128 GURL security_origin = translate::GetTranslateSecurityOrigin(); 129 base::StringAppendF( 130 &data_, "var securityOrigin = '%s';", security_origin.spec().c_str()); 131 132 // Append embedded translate.js and a remote element library. 133 base::StringPiece str = ResourceBundle::GetSharedInstance(). 134 GetRawDataResource(IDR_TRANSLATE_JS); 135 str.AppendToString(&data_); 136 data_ += data; 137 138 // We'll expire the cached script after some time, to make sure long 139 // running browsers still get fixes that might get pushed with newer 140 // scripts. 141 base::MessageLoop::current()->PostDelayedTask( 142 FROM_HERE, 143 base::Bind(&TranslateScript::Clear, 144 weak_method_factory_.GetWeakPtr()), 145 expiration_delay_); 146 } 147 148 callback_.Run(success, data); 149 } 150