| Index: chrome/browser/translate/translate_manager.cc
|
| ===================================================================
|
| --- chrome/browser/translate/translate_manager.cc (revision 89207)
|
| +++ chrome/browser/translate/translate_manager.cc (working copy)
|
| @@ -6,10 +6,12 @@
|
|
|
| #include "base/command_line.h"
|
| #include "base/compiler_specific.h"
|
| +#include "base/json/json_reader.h"
|
| #include "base/memory/singleton.h"
|
| #include "base/metrics/histogram.h"
|
| #include "base/string_split.h"
|
| #include "base/string_util.h"
|
| +#include "base/values.h"
|
| #include "chrome/browser/autofill/autofill_manager.h"
|
| #include "chrome/browser/browser_process.h"
|
| #include "chrome/browser/prefs/pref_service.h"
|
| @@ -124,11 +126,17 @@
|
| "Google-Translate-Element-Mode: library";
|
| const char* const kReportLanguageDetectionErrorURL =
|
| "http://translate.google.com/translate_error";
|
| -
|
| +const char* const kLanguageListFetchURL =
|
| + "http://translate.googleapis.com/translate_a/l?client=chrome&cb=sl";
|
| +const int kMaxRetryLanguageListFetch = 5;
|
| const int kTranslateScriptExpirationDelayMS = 24 * 60 * 60 * 1000; // 1 day.
|
|
|
| } // namespace
|
|
|
| +// This must be kept in sync with the &cb= value in the kLanguageListFetchURL.
|
| +const char* const TranslateManager::kLanguageListCallbackName = "sl(";
|
| +const char* const TranslateManager::kTargetLanguagesKey = "tl";
|
| +
|
| // static
|
| base::LazyInstance<std::set<std::string> >
|
| TranslateManager::supported_languages_(base::LINKER_INITIALIZED);
|
| @@ -154,11 +162,69 @@
|
| }
|
|
|
| // static
|
| +void TranslateManager::SetSupportedLanguages(const std::string& language_list) {
|
| + // The format is:
|
| + // sl({'sl': {'XX': 'LanguageName', ...}, 'tl': {'XX': 'LanguageName', ...}})
|
| + // Where "sl(" is set in kLanguageListCallbackName
|
| + // and 'tl' is kTargetLanguagesKey
|
| + if (!StartsWithASCII(language_list, kLanguageListCallbackName, false) ||
|
| + !EndsWith(language_list, ")", false)) {
|
| + // We don't have a NOTREACHED here since this can happen in ui_tests, even
|
| + // though the the BrowserMain function won't call us with parameters.ui_task
|
| + // is NULL some tests don't set it, so we must bail here.
|
| + return;
|
| + }
|
| + static const size_t kLanguageListCallbackNameLength =
|
| + strlen(kLanguageListCallbackName);
|
| + std::string languages_json = language_list.substr(
|
| + kLanguageListCallbackNameLength,
|
| + language_list.size() - kLanguageListCallbackNameLength - 1);
|
| + // JSON doesn't support single quotes though this is what is used on the
|
| + // translate server so we must replace them with double quotes.
|
| + ReplaceSubstringsAfterOffset(&languages_json, 0, "'", "\"");
|
| + scoped_ptr<Value> json_value(base::JSONReader::Read(languages_json, true));
|
| + if (json_value == NULL || !json_value->IsType(Value::TYPE_DICTIONARY)) {
|
| + NOTREACHED();
|
| + return;
|
| + }
|
| + // The first level dictionary contains two sub-dict, one for source languages
|
| + // and the other for target languages, we want to use the target languages.
|
| + DictionaryValue* language_dict =
|
| + static_cast<DictionaryValue*>(json_value.get());
|
| + DictionaryValue* target_languages = NULL;
|
| + if (!language_dict->GetDictionary(kTargetLanguagesKey, &target_languages) ||
|
| + target_languages == NULL) {
|
| + NOTREACHED();
|
| + return;
|
| + }
|
| + // Now we can clear our current state...
|
| + std::set<std::string>* supported_languages = supported_languages_.Pointer();
|
| + supported_languages->clear();
|
| + // ... and replace it with the values we just fetched from the server.
|
| + DictionaryValue::key_iterator iter = target_languages->begin_keys();
|
| + for (; iter != target_languages->end_keys(); ++iter)
|
| + supported_languages_.Pointer()->insert(*iter);
|
| +}
|
| +
|
| +// static
|
| +void TranslateManager::InitSupportedLanguages() {
|
| + // If our list of supported languages have not been set yet, we default
|
| + // to our hard coded list of languages in kSupportedLanguages.
|
| + if (supported_languages_.Pointer()->empty()) {
|
| + for (size_t i = 0; i < arraysize(kSupportedLanguages); ++i)
|
| + supported_languages_.Pointer()->insert(kSupportedLanguages[i]);
|
| + }
|
| +}
|
| +
|
| +// static
|
| void TranslateManager::GetSupportedLanguages(
|
| std::vector<std::string>* languages) {
|
| DCHECK(languages && languages->empty());
|
| - for (size_t i = 0; i < arraysize(kSupportedLanguages); ++i)
|
| - languages->push_back(kSupportedLanguages[i]);
|
| + InitSupportedLanguages();
|
| + std::set<std::string>* supported_languages = supported_languages_.Pointer();
|
| + std::set<std::string>::const_iterator iter = supported_languages->begin();
|
| + for (; iter != supported_languages->end(); ++iter)
|
| + languages->push_back(*iter);
|
| }
|
|
|
| // static
|
| @@ -177,12 +243,8 @@
|
|
|
| // static
|
| bool TranslateManager::IsSupportedLanguage(const std::string& page_language) {
|
| - std::set<std::string>* supported_languages = supported_languages_.Pointer();
|
| - if (supported_languages->empty()) {
|
| - for (size_t i = 0; i < arraysize(kSupportedLanguages); ++i)
|
| - supported_languages->insert(kSupportedLanguages[i]);
|
| - }
|
| - return supported_languages->find(page_language) != supported_languages->end();
|
| + InitSupportedLanguages();
|
| + return supported_languages_.Pointer()->count(page_language) != 0;
|
| }
|
|
|
| void TranslateManager::Observe(NotificationType type,
|
| @@ -288,54 +350,74 @@
|
| const net::ResponseCookies& cookies,
|
| const std::string& data) {
|
| scoped_ptr<const URLFetcher> delete_ptr(source);
|
| - DCHECK(translate_script_request_pending_);
|
| - translate_script_request_pending_ = false;
|
| + DCHECK(translate_script_request_pending_ || language_list_request_pending_);
|
| + // We quickly recognize that we are handling a translate script request
|
| + // if we don't have a language_list_request_pending_. Otherwise we do the
|
| + // more expensive check of confirming we got the kTranslateScriptURL in the
|
| + // rare case where we would have both requests pending at the same time.
|
| + bool translate_script_request = !language_list_request_pending_ ||
|
| + url == GURL(kTranslateScriptURL);
|
| + // Here we make sure that if we didn't get the translate_script_request,
|
| + // we actually got a language_list_request.
|
| + DCHECK(translate_script_request || url == GURL(kLanguageListFetchURL));
|
| + if (translate_script_request)
|
| + translate_script_request_pending_ = false;
|
| + else
|
| + language_list_request_pending_ = false;
|
| +
|
| bool error =
|
| (status.status() != net::URLRequestStatus::SUCCESS ||
|
| response_code != 200);
|
|
|
| - if (!error) {
|
| - base::StringPiece str = ResourceBundle::GetSharedInstance().
|
| - GetRawDataResource(IDR_TRANSLATE_JS);
|
| - DCHECK(translate_script_.empty());
|
| - str.CopyToString(&translate_script_);
|
| - translate_script_ += "\n" + 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.
|
| - MessageLoop::current()->PostDelayedTask(FROM_HERE,
|
| - method_factory_.NewRunnableMethod(
|
| - &TranslateManager::ClearTranslateScript),
|
| - translate_script_expiration_delay_);
|
| - }
|
| -
|
| - // Process any pending requests.
|
| - std::vector<PendingRequest>::const_iterator iter;
|
| - for (iter = pending_requests_.begin(); iter != pending_requests_.end();
|
| - ++iter) {
|
| - const PendingRequest& request = *iter;
|
| - TabContents* tab = tab_util::GetTabContentsByID(request.render_process_id,
|
| - request.render_view_id);
|
| - if (!tab) {
|
| - // The tab went away while we were retrieving the script.
|
| - continue;
|
| + if (translate_script_request) {
|
| + if (!error) {
|
| + base::StringPiece str = ResourceBundle::GetSharedInstance().
|
| + GetRawDataResource(IDR_TRANSLATE_JS);
|
| + DCHECK(translate_script_.empty());
|
| + str.CopyToString(&translate_script_);
|
| + translate_script_ += "\n" + 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.
|
| + MessageLoop::current()->PostDelayedTask(FROM_HERE,
|
| + method_factory_.NewRunnableMethod(
|
| + &TranslateManager::ClearTranslateScript),
|
| + translate_script_expiration_delay_);
|
| }
|
| - NavigationEntry* entry = tab->controller().GetActiveEntry();
|
| - if (!entry || entry->page_id() != request.page_id) {
|
| - // We navigated away from the page the translation was triggered on.
|
| - continue;
|
| - }
|
| + // Process any pending requests.
|
| + std::vector<PendingRequest>::const_iterator iter;
|
| + for (iter = pending_requests_.begin(); iter != pending_requests_.end();
|
| + ++iter) {
|
| + const PendingRequest& request = *iter;
|
| + TabContents* tab = tab_util::GetTabContentsByID(request.render_process_id,
|
| + request.render_view_id);
|
| + if (!tab) {
|
| + // The tab went away while we were retrieving the script.
|
| + continue;
|
| + }
|
| + NavigationEntry* entry = tab->controller().GetActiveEntry();
|
| + if (!entry || entry->page_id() != request.page_id) {
|
| + // We navigated away from the page the translation was triggered on.
|
| + continue;
|
| + }
|
|
|
| - if (error) {
|
| - ShowInfoBar(tab, TranslateInfoBarDelegate::CreateErrorDelegate(
|
| - TranslateErrors::NETWORK, tab,
|
| - request.source_lang, request.target_lang));
|
| - } else {
|
| - // Translate the page.
|
| - DoTranslatePage(tab, translate_script_,
|
| - request.source_lang, request.target_lang);
|
| + if (error) {
|
| + ShowInfoBar(tab, TranslateInfoBarDelegate::CreateErrorDelegate(
|
| + TranslateErrors::NETWORK, tab,
|
| + request.source_lang, request.target_lang));
|
| + } else {
|
| + // Translate the page.
|
| + DoTranslatePage(tab, translate_script_,
|
| + request.source_lang, request.target_lang);
|
| + }
|
| }
|
| + pending_requests_.clear();
|
| + } else { // if (translate_script_request)
|
| + if (!error)
|
| + SetSupportedLanguages(data);
|
| + else
|
| + VLOG(1) << "Failed to Fetch languages from: " << kLanguageListFetchURL;
|
| }
|
| - pending_requests_.clear();
|
| }
|
|
|
| // static
|
| @@ -346,7 +428,8 @@
|
| TranslateManager::TranslateManager()
|
| : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
|
| translate_script_expiration_delay_(kTranslateScriptExpirationDelayMS),
|
| - translate_script_request_pending_(false) {
|
| + translate_script_request_pending_(false),
|
| + language_list_request_pending_(false) {
|
| notification_registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED,
|
| NotificationService::AllSources());
|
| notification_registrar_.Add(this, NotificationType::TAB_LANGUAGE_DETERMINED,
|
| @@ -619,6 +702,27 @@
|
| accept_languages_[prefs] = accept_langs_set;
|
| }
|
|
|
| +void TranslateManager::FetchLanguageListFromTranslateServer(
|
| + PrefService* prefs) {
|
| + if (language_list_request_pending_)
|
| + return;
|
| +
|
| + // We don't want to do this when translate is disabled.
|
| + DCHECK(prefs != NULL);
|
| + if (CommandLine::ForCurrentProcess()->HasSwitch(
|
| + switches::kDisableTranslate) ||
|
| + (prefs != NULL && !prefs->GetBoolean(prefs::kEnableTranslate))) {
|
| + return;
|
| + }
|
| +
|
| + language_list_request_pending_ = true;
|
| + URLFetcher* fetcher = URLFetcher::Create(1, GURL(kLanguageListFetchURL),
|
| + URLFetcher::GET, this);
|
| + fetcher->set_request_context(Profile::GetDefaultRequestContext());
|
| + fetcher->set_max_retries(kMaxRetryLanguageListFetch);
|
| + fetcher->Start();
|
| +}
|
| +
|
| void TranslateManager::RequestTranslateScript() {
|
| if (translate_script_request_pending_)
|
| return;
|
|
|