summaryrefslogtreecommitdiffstats
path: root/components/translate
diff options
context:
space:
mode:
authordroger@chromium.org <droger@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-07 08:18:09 +0000
committerdroger@chromium.org <droger@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-07 08:18:09 +0000
commit8bb49f306e67620b70e8a8114223879a3b6521ea (patch)
tree1ff60b9d87b7c323d0fc201888bc98d4d6b55168 /components/translate
parentd9a7cf7a118a3369c17095277b3f605bd9c36d04 (diff)
downloadchromium_src-8bb49f306e67620b70e8a8114223879a3b6521ea.zip
chromium_src-8bb49f306e67620b70e8a8114223879a3b6521ea.tar.gz
chromium_src-8bb49f306e67620b70e8a8114223879a3b6521ea.tar.bz2
Move the translate script to the Translate component
This CL moves the TranslateScript class and the translate.js script. A new translate_resources target is created. BUG=335074 TBR=jochen Review URL: https://codereview.chromium.org/138933006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@249620 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'components/translate')
-rw-r--r--components/translate/DEPS1
-rw-r--r--components/translate/core/browser/resources/translate.js314
-rw-r--r--components/translate/core/browser/translate_download_manager.cc20
-rw-r--r--components/translate/core/browser/translate_download_manager.h19
-rw-r--r--components/translate/core/browser/translate_script.cc149
-rw-r--r--components/translate/core/browser/translate_script.h94
-rw-r--r--components/translate/core/browser/translate_script_unittest.cc120
-rw-r--r--components/translate/core/common/translate_switches.cc3
-rw-r--r--components/translate/core/common/translate_switches.h1
9 files changed, 718 insertions, 3 deletions
diff --git a/components/translate/DEPS b/components/translate/DEPS
index 64ab5b8..21d3161 100644
--- a/components/translate/DEPS
+++ b/components/translate/DEPS
@@ -1,6 +1,7 @@
include_rules = [
"+components/language_usage_metrics",
"+google_apis",
+ "+grit", # For generated headers.
"+net",
"+ui",
diff --git a/components/translate/core/browser/resources/translate.js b/components/translate/core/browser/resources/translate.js
new file mode 100644
index 0000000..fb59e66
--- /dev/null
+++ b/components/translate/core/browser/resources/translate.js
@@ -0,0 +1,314 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This code is used in conjunction with the Google Translate Element script.
+// It is executed in an isolated world of a page to translate it from one
+// language to another.
+// It should be included in the page before the Translate Element script.
+
+var cr = cr || {};
+
+/**
+ * An object to provide functions to interact with the Translate library.
+ * @type {object}
+ */
+cr.googleTranslate = (function() {
+ /**
+ * The Translate Element library's instance.
+ * @type {object}
+ */
+ var lib;
+
+ /**
+ * A flag representing if the Translate Element library is initialized.
+ * @type {boolean}
+ */
+ var libReady = false;
+
+ /**
+ * Error definitions for |errorCode|. See chrome/common/translate_errors.h
+ * to modify the definition.
+ * @const
+ */
+ var ERROR = {
+ 'NONE': 0,
+ 'INITIALIZATION_ERROR': 2,
+ 'UNSUPPORTED_LANGUAGE': 4,
+ 'TRANSLATION_ERROR': 6,
+ 'TRANSLATION_TIMEOUT': 7,
+ 'UNEXPECTED_SCRIPT_ERROR': 8,
+ 'BAD_ORIGIN': 9,
+ 'SCRIPT_LOAD_ERROR': 10
+ };
+
+ /**
+ * Error code map from te.dom.DomTranslator.Error to |errorCode|.
+ * See also go/dom_translator.js in google3.
+ * @const
+ */
+ var TRANSLATE_ERROR_TO_ERROR_CODE_MAP = {
+ 0: ERROR['NONE'],
+ 1: ERROR['TRANSLATION_ERROR'],
+ 2: ERROR['UNSUPPORTED_LANGUAGE']
+ };
+
+ /**
+ * An error code happened in translate.js and the Translate Element library.
+ */
+ var errorCode = ERROR['NONE'];
+
+ /**
+ * A flag representing if the Translate Element has finished a translation.
+ * @type {boolean}
+ */
+ var finished = false;
+
+ /**
+ * Counts how many times the checkLibReady function is called. The function
+ * is called in every 100 msec and counted up to 6.
+ * @type {number}
+ */
+ var checkReadyCount = 0;
+
+ /**
+ * Time in msec when this script is injected.
+ * @type {number}
+ */
+ var injectedTime = performance.now();
+
+ /**
+ * Time in msec when the Translate Element library is loaded completely.
+ * @type {number}
+ */
+ var loadedTime = 0.0;
+
+ /**
+ * Time in msec when the Translate Element library is initialized and ready
+ * for performing translation.
+ * @type {number}
+ */
+ var readyTime = 0.0;
+
+ /**
+ * Time in msec when the Translate Element library starts a translation.
+ * @type {number}
+ */
+ var startTime = 0.0;
+
+ /**
+ * Time in msec when the Translate Element library ends a translation.
+ * @type {number}
+ */
+ var endTime = 0.0;
+
+ function checkLibReady() {
+ if (lib.isAvailable()) {
+ readyTime = performance.now();
+ libReady = true;
+ return;
+ }
+ if (checkReadyCount++ > 5) {
+ errorCode = ERROR['TRANSLATION_TIMEOUT'];
+ return;
+ }
+ setTimeout(checkLibReady, 100);
+ }
+
+ function onTranslateProgress(progress, opt_finished, opt_error) {
+ finished = opt_finished;
+ // opt_error can be 'undefined'.
+ if (typeof opt_error == 'boolean' && opt_error) {
+ // TODO(toyoshim): Remove boolean case once a server is updated.
+ errorCode = ERROR['TRANSLATION_ERROR'];
+ // We failed to translate, restore so the page is in a consistent state.
+ lib.restore();
+ } else if (typeof opt_error == 'number' && opt_error != 0) {
+ errorCode = TRANSLATE_ERROR_TO_ERROR_CODE_MAP[opt_error];
+ lib.restore();
+ }
+ if (finished)
+ endTime = performance.now();
+ }
+
+ // Public API.
+ return {
+ /**
+ * Whether the library is ready.
+ * The translate function should only be called when |libReady| is true.
+ * @type {boolean}
+ */
+ get libReady() {
+ return libReady;
+ },
+
+ /**
+ * Whether the current translate has finished successfully.
+ * @type {boolean}
+ */
+ get finished() {
+ return finished;
+ },
+
+ /**
+ * Whether an error occured initializing the library of translating the
+ * page.
+ * @type {boolean}
+ */
+ get error() {
+ return errorCode != ERROR['NONE'];
+ },
+
+ /**
+ * Returns a number to represent error type.
+ * @type {number}
+ */
+ get errorCode() {
+ return errorCode;
+ },
+
+ /**
+ * The language the page translated was in. Is valid only after the page
+ * has been successfully translated and the original language specified to
+ * the translate function was 'auto'. Is empty otherwise.
+ * Some versions of Element library don't provide |getDetectedLanguage|
+ * function. In that case, this function returns 'und'.
+ * @type {boolean}
+ */
+ get sourceLang() {
+ if (!libReady || !finished || errorCode != ERROR['NONE'])
+ return '';
+ if (!lib.getDetectedLanguage)
+ return 'und'; // Defined as translate::kUnknownLanguageCode in C++.
+ return lib.getDetectedLanguage();
+ },
+
+ /**
+ * Time in msec from this script being injected to all server side scripts
+ * being loaded.
+ * @type {number}
+ */
+ get loadTime() {
+ if (loadedTime == 0)
+ return 0;
+ return loadedTime - injectedTime;
+ },
+
+ /**
+ * Time in msec from this script being injected to the Translate Element
+ * library being ready.
+ * @type {number}
+ */
+ get readyTime() {
+ if (!libReady)
+ return 0;
+ return readyTime - injectedTime;
+ },
+
+ /**
+ * Time in msec to perform translation.
+ * @type {number}
+ */
+ get translationTime() {
+ if (!finished)
+ return 0;
+ return endTime - startTime;
+ },
+
+ /**
+ * Translate the page contents. Note that the translation is asynchronous.
+ * You need to regularly check the state of |finished| and |errorCode| to
+ * know if the translation finished or if there was an error.
+ * @param {string} originalLang The language the page is in.
+ * @param {string} targetLang The language the page should be translated to.
+ * @return {boolean} False if the translate library was not ready, in which
+ * case the translation is not started. True otherwise.
+ */
+ translate: function(originalLang, targetLang) {
+ finished = false;
+ errorCode = ERROR['NONE'];
+ if (!libReady)
+ return false;
+ startTime = performance.now();
+ try {
+ lib.translatePage(originalLang, targetLang, onTranslateProgress);
+ } catch (err) {
+ console.error('Translate: ' + err);
+ errorCode = ERROR['UNEXPECTED_SCRIPT_ERROR'];
+ return false;
+ }
+ return true;
+ },
+
+ /**
+ * Reverts the page contents to its original value, effectively reverting
+ * any performed translation. Does nothing if the page was not translated.
+ */
+ revert: function() {
+ lib.restore();
+ },
+
+ /**
+ * Entry point called by the Translate Element once it has been injected in
+ * the page.
+ */
+ onTranslateElementLoad: function() {
+ loadedTime = performance.now();
+ try {
+ lib = google.translate.TranslateService({
+ // translateApiKey is predefined by translate_script.cc.
+ 'key': translateApiKey,
+ 'useSecureConnection': true
+ });
+ translateApiKey = undefined;
+ } catch (err) {
+ errorCode = ERROR['INITIALIZATION_ERROR'];
+ translateApiKey = undefined;
+ return;
+ }
+ // The TranslateService is not available immediately as it needs to start
+ // Flash. Let's wait until it is ready.
+ checkLibReady();
+ },
+
+ /**
+ * Entry point called by the Translate Element when it want to load an
+ * external CSS resource into the page.
+ * @param {string} url URL of an external CSS resource to load.
+ */
+ onLoadCSS: function(url) {
+ var element = document.createElement('link');
+ element.type = 'text/css';
+ element.rel = 'stylesheet';
+ element.charset = 'UTF-8';
+ element.href = url;
+ document.head.appendChild(element);
+ },
+
+ /**
+ * Entry point called by the Translate Element when it want to load and run
+ * an external JavaScript on the page.
+ * @param {string} url URL of an external JavaScript to load.
+ */
+ onLoadJavascript: function(url) {
+ // securityOrigin is predefined by translate_script.cc.
+ if (url.indexOf(securityOrigin) != 0) {
+ console.error('Translate: ' + url + ' is not allowed to load.');
+ errorCode = ERROR['BAD_ORIGIN'];
+ return;
+ }
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', url, true);
+ xhr.onreadystatechange = function() {
+ if (this.readyState != this.DONE)
+ return;
+ if (this.status != 200) {
+ errorCode = ERROR['SCRIPT_LOAD_ERROR'];
+ return;
+ }
+ eval(this.responseText);
+ }
+ xhr.send();
+ }
+ };
+})();
diff --git a/components/translate/core/browser/translate_download_manager.cc b/components/translate/core/browser/translate_download_manager.cc
index 4542d20..9d1898c 100644
--- a/components/translate/core/browser/translate_download_manager.cc
+++ b/components/translate/core/browser/translate_download_manager.cc
@@ -16,12 +16,14 @@ TranslateDownloadManager* TranslateDownloadManager::GetInstance() {
}
TranslateDownloadManager::TranslateDownloadManager()
- : language_list_(new TranslateLanguageList) {}
+ : language_list_(new TranslateLanguageList),
+ script_(new TranslateScript) {}
TranslateDownloadManager::~TranslateDownloadManager() {}
void TranslateDownloadManager::Shutdown() {
language_list_.reset();
+ script_.reset();
request_context_ = NULL;
}
@@ -106,3 +108,19 @@ bool TranslateDownloadManager::IsAlphaLanguage(const std::string& language) {
return language_list->IsAlphaLanguage(language);
}
+
+void TranslateDownloadManager::ClearTranslateScriptForTesting() {
+ if (script_.get() == NULL) {
+ NOTREACHED();
+ return;
+ }
+ script_->Clear();
+}
+
+void TranslateDownloadManager::SetTranslateScriptExpirationDelay(int delay_ms) {
+ if (script_.get() == NULL) {
+ NOTREACHED();
+ return;
+ }
+ script_->set_expiration_delay(delay_ms);
+}
diff --git a/components/translate/core/browser/translate_download_manager.h b/components/translate/core/browser/translate_download_manager.h
index 6cffd27..f8dcae5 100644
--- a/components/translate/core/browser/translate_download_manager.h
+++ b/components/translate/core/browser/translate_download_manager.h
@@ -11,6 +11,7 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "components/translate/core/browser/translate_language_list.h"
+#include "components/translate/core/browser/translate_script.h"
#include "net/url_request/url_request_context_getter.h"
template <typename T> struct DefaultSingletonTraits;
@@ -19,8 +20,6 @@ class PrefService;
// Manages the downloaded resources for Translate, such as the translate script
// and the language list.
-// TODO(droger): TranslateDownloadManager should own TranslateScript.
-// See http://crbug.com/335074.
class TranslateDownloadManager {
public:
// Returns the singleton instance.
@@ -46,6 +45,9 @@ class TranslateDownloadManager {
// The language list.
TranslateLanguageList* language_list() { return language_list_.get(); }
+ // The translate script.
+ TranslateScript* script() { return script_.get(); }
+
// Let the caller decide if and when we should fetch the language list from
// the translate server. This is a NOOP if switches::kDisableTranslate is set
// or if prefs::kEnableTranslate is set to false.
@@ -77,12 +79,25 @@ class TranslateDownloadManager {
// Must be called to shut Translate down. Cancels any pending fetches.
void Shutdown();
+ // Clears the translate script, so it will be fetched next time we translate.
+ void ClearTranslateScriptForTesting();
+
+ // Used by unit-tests to override some defaults:
+ // Delay after which the translate script is fetched again from the
+ // translation server.
+ void SetTranslateScriptExpirationDelay(int delay_ms);
+
private:
friend struct DefaultSingletonTraits<TranslateDownloadManager>;
TranslateDownloadManager();
virtual ~TranslateDownloadManager();
scoped_ptr<TranslateLanguageList> language_list_;
+
+ // An instance of TranslateScript which manages JavaScript source for
+ // Translate.
+ scoped_ptr<TranslateScript> script_;
+
std::string application_locale_;
scoped_refptr<net::URLRequestContextGetter> request_context_;
};
diff --git a/components/translate/core/browser/translate_script.cc b/components/translate/core/browser/translate_script.cc
new file mode 100644
index 0000000..a83f499
--- /dev/null
+++ b/components/translate/core/browser/translate_script.cc
@@ -0,0 +1,149 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/translate/core/browser/translate_script.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "components/translate/core/browser/translate_url_fetcher.h"
+#include "components/translate/core/browser/translate_url_util.h"
+#include "components/translate/core/common/translate_switches.h"
+#include "components/translate/core/common/translate_util.h"
+#include "google_apis/google_api_keys.h"
+#include "grit/component_resources.h"
+#include "net/base/escape.h"
+#include "net/base/url_util.h"
+#include "ui/base/resource/resource_bundle.h"
+
+namespace {
+
+const int kExpirationDelayDays = 1;
+
+} // namespace
+
+const char TranslateScript::kScriptURL[] =
+ "https://translate.google.com/translate_a/element.js";
+const char TranslateScript::kRequestHeader[] =
+ "Google-Translate-Element-Mode: library";
+const char TranslateScript::kAlwaysUseSslQueryName[] = "aus";
+const char TranslateScript::kAlwaysUseSslQueryValue[] = "true";
+const char TranslateScript::kCallbackQueryName[] = "cb";
+const char TranslateScript::kCallbackQueryValue[] =
+ "cr.googleTranslate.onTranslateElementLoad";
+const char TranslateScript::kCssLoaderCallbackQueryName[] = "clc";
+const char TranslateScript::kCssLoaderCallbackQueryValue[] =
+ "cr.googleTranslate.onLoadCSS";
+const char TranslateScript::kJavascriptLoaderCallbackQueryName[] = "jlc";
+const char TranslateScript::kJavascriptLoaderCallbackQueryValue[] =
+ "cr.googleTranslate.onLoadJavascript";
+
+TranslateScript::TranslateScript()
+ : expiration_delay_(base::TimeDelta::FromDays(kExpirationDelayDays)),
+ weak_method_factory_(this) {
+}
+
+TranslateScript::~TranslateScript() {
+}
+
+void TranslateScript::Request(const Callback& callback) {
+ if (fetcher_.get() != NULL) {
+ NOTREACHED();
+ return;
+ }
+
+ callback_ = callback;
+
+ GURL translate_script_url;
+ // Check if command-line contains an alternative URL for translate service.
+ const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+ if (command_line.HasSwitch(translate::switches::kTranslateScriptURL)) {
+ translate_script_url = GURL(command_line.GetSwitchValueASCII(
+ translate::switches::kTranslateScriptURL));
+ if (!translate_script_url.is_valid() ||
+ !translate_script_url.query().empty()) {
+ LOG(WARNING) << "The following translate URL specified at the "
+ << "command-line is invalid: "
+ << translate_script_url.spec();
+ translate_script_url = GURL();
+ }
+ }
+
+ // Use default URL when command-line argument is not specified, or specified
+ // URL is invalid.
+ if (translate_script_url.is_empty())
+ translate_script_url = GURL(kScriptURL);
+
+ translate_script_url = net::AppendQueryParameter(
+ translate_script_url,
+ kCallbackQueryName,
+ kCallbackQueryValue);
+ translate_script_url = net::AppendQueryParameter(
+ translate_script_url,
+ kAlwaysUseSslQueryName,
+ kAlwaysUseSslQueryValue);
+#if !defined(OS_IOS)
+ // iOS doesn't need to use specific loaders for the isolated world.
+ translate_script_url = net::AppendQueryParameter(
+ translate_script_url,
+ kCssLoaderCallbackQueryName,
+ kCssLoaderCallbackQueryValue);
+ translate_script_url = net::AppendQueryParameter(
+ translate_script_url,
+ kJavascriptLoaderCallbackQueryName,
+ kJavascriptLoaderCallbackQueryValue);
+#endif // !defined(OS_IOS)
+
+ translate_script_url =
+ TranslateURLUtil::AddHostLocaleToUrl(translate_script_url);
+ translate_script_url =
+ TranslateURLUtil::AddApiKeyToUrl(translate_script_url);
+
+ fetcher_.reset(new TranslateURLFetcher(kFetcherId));
+ fetcher_->set_extra_request_header(kRequestHeader);
+ fetcher_->Request(
+ translate_script_url,
+ base::Bind(&TranslateScript::OnScriptFetchComplete,
+ base::Unretained(this)));
+}
+
+
+void TranslateScript::OnScriptFetchComplete(
+ int id, bool success, const std::string& data) {
+ DCHECK_EQ(kFetcherId, id);
+
+ scoped_ptr<const TranslateURLFetcher> delete_ptr(fetcher_.release());
+
+ if (success) {
+ DCHECK(data_.empty());
+ // Insert variable definitions on API Key and security origin.
+ data_ = base::StringPrintf("var translateApiKey = '%s';\n",
+ google_apis::GetAPIKey().c_str());
+
+ GURL security_origin = translate::GetTranslateSecurityOrigin();
+ base::StringAppendF(
+ &data_, "var securityOrigin = '%s';", security_origin.spec().c_str());
+
+ // Append embedded translate.js and a remote element library.
+ base::StringPiece str = ResourceBundle::GetSharedInstance().
+ GetRawDataResource(IDR_TRANSLATE_JS);
+ str.AppendToString(&data_);
+ data_ += data;
+
+ // We'll expire the cached script after some time, to make sure long
+ // running browsers still get fixes that might get pushed with newer
+ // scripts.
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&TranslateScript::Clear,
+ weak_method_factory_.GetWeakPtr()),
+ expiration_delay_);
+ }
+
+ callback_.Run(success, data);
+}
diff --git a/components/translate/core/browser/translate_script.h b/components/translate/core/browser/translate_script.h
new file mode 100644
index 0000000..552daee
--- /dev/null
+++ b/components/translate/core/browser/translate_script.h
@@ -0,0 +1,94 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_TRANSLATE_CORE_BROWSER_TRANSLATE_SCRIPT_H_
+#define COMPONENTS_TRANSLATE_CORE_BROWSER_TRANSLATE_SCRIPT_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+
+class TranslateScriptTest;
+class TranslateURLFetcher;
+
+class TranslateScript {
+ public:
+ typedef base::Callback<void(bool, const std::string&)> Callback;
+
+ static const int kFetcherId = 0;
+
+ TranslateScript();
+ virtual ~TranslateScript();
+
+ // Returns the feched the translate script.
+ const std::string& data() { return data_; }
+
+ // Used by unit-tests to override some defaults:
+ // Delay after which the translate script is fetched again from the
+ // translation server.
+ void set_expiration_delay(int delay_ms) {
+ expiration_delay_ = base::TimeDelta::FromMilliseconds(delay_ms);
+ }
+
+ // Clears the translate script, so it will be fetched next time we translate.
+ void Clear() { data_.clear(); }
+
+ // Fetches the JS translate script (the script that is injected in the page
+ // to translate it).
+ void Request(const Callback& callback);
+
+ // Returns true if this has a pending request.
+ bool HasPendingRequest() const { return fetcher_.get() != NULL; }
+
+ private:
+ friend class TranslateScriptTest;
+ FRIEND_TEST_ALL_PREFIXES(TranslateScriptTest, CheckScriptParameters);
+ FRIEND_TEST_ALL_PREFIXES(TranslateScriptTest, CheckScriptURL);
+
+ static const char kScriptURL[];
+ static const char kRequestHeader[];
+
+ // Used in kTranslateScriptURL to specify using always ssl to load resources.
+ static const char kAlwaysUseSslQueryName[];
+ static const char kAlwaysUseSslQueryValue[];
+
+ // Used in kTranslateScriptURL to specify a callback function name.
+ static const char kCallbackQueryName[];
+ static const char kCallbackQueryValue[];
+
+ // Used in kTranslateScriptURL to specify a CSS loader callback function name.
+ static const char kCssLoaderCallbackQueryName[];
+ static const char kCssLoaderCallbackQueryValue[];
+
+ // Used in kTranslateScriptURL to specify a JavaScript loader callback
+ // function name.
+ static const char kJavascriptLoaderCallbackQueryName[];
+ static const char kJavascriptLoaderCallbackQueryValue[];
+
+ // The callback when the script is fetched or a server error occured.
+ void OnScriptFetchComplete(int id, bool success, const std::string& data);
+
+ // URL fetcher to fetch the translate script.
+ scoped_ptr<TranslateURLFetcher> fetcher_;
+
+ // The JS injected in the page to do the translation.
+ std::string data_;
+
+ // Delay after which the translate script is fetched again from the translate
+ // server.
+ base::TimeDelta expiration_delay_;
+
+ // The callback called when the server sends a response.
+ Callback callback_;
+
+ base::WeakPtrFactory<TranslateScript> weak_method_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(TranslateScript);
+};
+
+#endif // COMPONENTS_TRANSLATE_CORE_BROWSER_TRANSLATE_SCRIPT_H_
diff --git a/components/translate/core/browser/translate_script_unittest.cc b/components/translate/core/browser/translate_script_unittest.cc
new file mode 100644
index 0000000..c8e3316
--- /dev/null
+++ b/components/translate/core/browser/translate_script_unittest.cc
@@ -0,0 +1,120 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/translate/core/browser/translate_script.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/strings/stringprintf.h"
+#include "components/translate/core/browser/translate_download_manager.h"
+#include "components/translate/core/common/translate_switches.h"
+#include "net/base/load_flags.h"
+#include "net/base/url_util.h"
+#include "net/http/http_request_headers.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+class TranslateScriptTest : public testing::Test {
+ public:
+ TranslateScriptTest() : testing::Test() {}
+
+ protected:
+ virtual void SetUp() {
+ script_.reset(new TranslateScript);
+ DCHECK(script_.get());
+ TranslateDownloadManager::GetInstance()->set_application_locale("en");
+ }
+
+ virtual void TearDown() {
+ script_.reset();
+ }
+
+ void Request() {
+ script_->Request(
+ base::Bind(&TranslateScriptTest::OnComplete, base::Unretained(this)));
+ }
+
+ net::TestURLFetcher* GetTestURLFetcher() {
+ return url_fetcher_factory_.GetFetcherByID(TranslateScript::kFetcherId);
+ }
+
+ private:
+ void OnComplete(bool success, const std::string& script) {
+ }
+
+ scoped_ptr<TranslateScript> script_;
+ net::TestURLFetcherFactory url_fetcher_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(TranslateScriptTest);
+};
+
+TEST_F(TranslateScriptTest, CheckScriptParameters) {
+ Request();
+ net::TestURLFetcher* fetcher = GetTestURLFetcher();
+ ASSERT_TRUE(fetcher);
+
+ GURL expected_url(TranslateScript::kScriptURL);
+ GURL url = fetcher->GetOriginalURL();
+ EXPECT_TRUE(url.is_valid());
+ EXPECT_EQ(expected_url.GetOrigin().spec(), url.GetOrigin().spec());
+ EXPECT_EQ(expected_url.path(), url.path());
+
+ int load_flags = fetcher->GetLoadFlags();
+ EXPECT_EQ(net::LOAD_DO_NOT_SEND_COOKIES,
+ load_flags & net::LOAD_DO_NOT_SEND_COOKIES);
+ EXPECT_EQ(net::LOAD_DO_NOT_SAVE_COOKIES,
+ load_flags & net::LOAD_DO_NOT_SAVE_COOKIES);
+
+ std::string expected_extra_headers =
+ base::StringPrintf("%s\r\n\r\n", TranslateScript::kRequestHeader);
+ net::HttpRequestHeaders extra_headers;
+ fetcher->GetExtraRequestHeaders(&extra_headers);
+ EXPECT_EQ(expected_extra_headers, extra_headers.ToString());
+
+ std::string always_use_ssl;
+ net::GetValueForKeyInQuery(
+ url, TranslateScript::kAlwaysUseSslQueryName, &always_use_ssl);
+ EXPECT_EQ(std::string(TranslateScript::kAlwaysUseSslQueryValue),
+ always_use_ssl);
+
+ std::string callback;
+ net::GetValueForKeyInQuery(
+ url, TranslateScript::kCallbackQueryName, &callback);
+ EXPECT_EQ(std::string(TranslateScript::kCallbackQueryValue), callback);
+
+#if !defined(OS_IOS)
+ // iOS does not have specific loaders for the isolated world.
+ std::string css_loader_callback;
+ net::GetValueForKeyInQuery(
+ url, TranslateScript::kCssLoaderCallbackQueryName, &css_loader_callback);
+ EXPECT_EQ(std::string(TranslateScript::kCssLoaderCallbackQueryValue),
+ css_loader_callback);
+
+ std::string javascript_loader_callback;
+ net::GetValueForKeyInQuery(
+ url,
+ TranslateScript::kJavascriptLoaderCallbackQueryName,
+ &javascript_loader_callback);
+ EXPECT_EQ(std::string(TranslateScript::kJavascriptLoaderCallbackQueryValue),
+ javascript_loader_callback);
+#endif // !defined(OS_IOS)
+}
+
+TEST_F(TranslateScriptTest, CheckScriptURL) {
+ const std::string script_url("http://www.tamurayukari.com/mero-n.js");
+ CommandLine* command_line = CommandLine::ForCurrentProcess();
+ command_line->AppendSwitchASCII(translate::switches::kTranslateScriptURL,
+ script_url);
+
+ Request();
+ net::TestURLFetcher* fetcher = GetTestURLFetcher();
+ ASSERT_TRUE(fetcher);
+
+ GURL expected_url(script_url);
+ GURL url = fetcher->GetOriginalURL();
+ EXPECT_TRUE(url.is_valid());
+ EXPECT_EQ(expected_url.GetOrigin().spec(), url.GetOrigin().spec());
+ EXPECT_EQ(expected_url.path(), url.path());
+}
diff --git a/components/translate/core/common/translate_switches.cc b/components/translate/core/common/translate_switches.cc
index 71fdf12..1ac6f33 100644
--- a/components/translate/core/common/translate_switches.cc
+++ b/components/translate/core/common/translate_switches.cc
@@ -12,6 +12,9 @@ namespace switches {
// disable translate with the preference.
const char kDisableTranslate[] = "disable-translate";
+// Overrides the default server used for Google Translate.
+const char kTranslateScriptURL[] = "translate-script-url";
+
// Overrides security-origin with which Translate runs in an isolated world.
const char kTranslateSecurityOrigin[] = "translate-security-origin";
diff --git a/components/translate/core/common/translate_switches.h b/components/translate/core/common/translate_switches.h
index 4ab971f..ace4f3a 100644
--- a/components/translate/core/common/translate_switches.h
+++ b/components/translate/core/common/translate_switches.h
@@ -9,6 +9,7 @@ namespace translate {
namespace switches {
extern const char kDisableTranslate[];
+extern const char kTranslateScriptURL[];
extern const char kTranslateSecurityOrigin[];
} // namespace switches