Home | History | Annotate | Download | only in browser
      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 
     24 namespace translate {
     25 
     26 namespace {
     27 
     28 const int kExpirationDelayDays = 1;
     29 
     30 }  // namespace
     31 
     32 const char TranslateScript::kScriptURL[] =
     33     "https://translate.google.com/translate_a/element.js";
     34 const char TranslateScript::kRequestHeader[] =
     35     "Google-Translate-Element-Mode: library";
     36 const char TranslateScript::kAlwaysUseSslQueryName[] = "aus";
     37 const char TranslateScript::kAlwaysUseSslQueryValue[] = "true";
     38 const char TranslateScript::kCallbackQueryName[] = "cb";
     39 const char TranslateScript::kCallbackQueryValue[] =
     40     "cr.googleTranslate.onTranslateElementLoad";
     41 const char TranslateScript::kCssLoaderCallbackQueryName[] = "clc";
     42 const char TranslateScript::kCssLoaderCallbackQueryValue[] =
     43     "cr.googleTranslate.onLoadCSS";
     44 const char TranslateScript::kJavascriptLoaderCallbackQueryName[] = "jlc";
     45 const char TranslateScript::kJavascriptLoaderCallbackQueryValue[] =
     46     "cr.googleTranslate.onLoadJavascript";
     47 
     48 TranslateScript::TranslateScript()
     49     : expiration_delay_(base::TimeDelta::FromDays(kExpirationDelayDays)),
     50       weak_method_factory_(this) {
     51 }
     52 
     53 TranslateScript::~TranslateScript() {
     54 }
     55 
     56 void 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 
    118 void 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