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