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