Home | History | Annotate | Download | only in api
      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 #ifndef EXTENSIONS_BROWSER_API_EXECUTE_CODE_FUNCTION_IMPL_H_
      6 #define EXTENSIONS_BROWSER_API_EXECUTE_CODE_FUNCTION_IMPL_H_
      7 
      8 #include "extensions/browser/api/execute_code_function.h"
      9 
     10 #include "extensions/browser/component_extension_resource_manager.h"
     11 #include "extensions/browser/extensions_browser_client.h"
     12 #include "extensions/browser/file_reader.h"
     13 #include "extensions/common/error_utils.h"
     14 #include "extensions/common/extension_messages.h"
     15 #include "extensions/common/file_util.h"
     16 #include "extensions/common/manifest_constants.h"
     17 #include "extensions/common/message_bundle.h"
     18 #include "net/base/filename_util.h"
     19 #include "ui/base/resource/resource_bundle.h"
     20 
     21 namespace {
     22 
     23 // Error messages
     24 const char kNoCodeOrFileToExecuteError[] = "No source code or file specified.";
     25 const char kMoreThanOneValuesError[] =
     26     "Code and file should not be specified "
     27     "at the same time in the second argument.";
     28 const char kLoadFileError[] = "Failed to load file: \"*\". ";
     29 
     30 }
     31 
     32 namespace extensions {
     33 
     34 using core_api::extension_types::InjectDetails;
     35 
     36 ExecuteCodeFunction::ExecuteCodeFunction() {
     37 }
     38 
     39 ExecuteCodeFunction::~ExecuteCodeFunction() {
     40 }
     41 
     42 void ExecuteCodeFunction::DidLoadFile(bool success, const std::string& data) {
     43   if (!success || !details_->file) {
     44     DidLoadAndLocalizeFile(success, data);
     45     return;
     46   }
     47 
     48   ScriptExecutor::ScriptType script_type =
     49       ShouldInsertCSS() ? ScriptExecutor::CSS : ScriptExecutor::JAVASCRIPT;
     50 
     51   std::string extension_id;
     52   base::FilePath extension_path;
     53   std::string extension_default_locale;
     54   if (extension()) {
     55     extension_id = extension()->id();
     56     extension_path = extension()->path();
     57     extension()->manifest()->GetString(manifest_keys::kDefaultLocale,
     58                                        &extension_default_locale);
     59   }
     60 
     61   content::BrowserThread::PostTask(
     62       content::BrowserThread::FILE,
     63       FROM_HERE,
     64       base::Bind(&ExecuteCodeFunction::GetFileURLAndLocalizeCSS,
     65                  this,
     66                  script_type,
     67                  data,
     68                  extension_id,
     69                  extension_path,
     70                  extension_default_locale));
     71 }
     72 
     73 void ExecuteCodeFunction::GetFileURLAndLocalizeCSS(
     74     ScriptExecutor::ScriptType script_type,
     75     const std::string& data,
     76     const std::string& extension_id,
     77     const base::FilePath& extension_path,
     78     const std::string& extension_default_locale) {
     79   std::string localized_data = data;
     80   // Check if the file is CSS and needs localization.
     81   if ((script_type == ScriptExecutor::CSS) && !extension_id.empty() &&
     82       (data.find(MessageBundle::kMessageBegin) != std::string::npos)) {
     83     scoped_ptr<SubstitutionMap> localization_messages(
     84         file_util::LoadMessageBundleSubstitutionMap(
     85             extension_path, extension_id, extension_default_locale));
     86 
     87     // We need to do message replacement on the data, so it has to be mutable.
     88     std::string error;
     89     MessageBundle::ReplaceMessagesWithExternalDictionary(
     90         *localization_messages, &localized_data, &error);
     91   }
     92 
     93   file_url_ = net::FilePathToFileURL(resource_.GetFilePath());
     94 
     95   // Call back DidLoadAndLocalizeFile on the UI thread. The success parameter
     96   // is always true, because if loading had failed, we wouldn't have had
     97   // anything to localize.
     98   content::BrowserThread::PostTask(
     99       content::BrowserThread::UI,
    100       FROM_HERE,
    101       base::Bind(&ExecuteCodeFunction::DidLoadAndLocalizeFile,
    102                  this,
    103                  true,
    104                  localized_data));
    105 }
    106 
    107 void ExecuteCodeFunction::DidLoadAndLocalizeFile(bool success,
    108                                                  const std::string& data) {
    109   if (success) {
    110     if (!Execute(data))
    111       SendResponse(false);
    112   } else {
    113     // TODO(viettrungluu): bug: there's no particular reason the path should be
    114     // UTF-8, in which case this may fail.
    115     error_ = ErrorUtils::FormatErrorMessage(
    116         kLoadFileError, resource_.relative_path().AsUTF8Unsafe());
    117     SendResponse(false);
    118   }
    119 }
    120 
    121 bool ExecuteCodeFunction::Execute(const std::string& code_string) {
    122   ScriptExecutor* executor = GetScriptExecutor();
    123   if (!executor)
    124     return false;
    125 
    126   if (!extension())
    127     return false;
    128 
    129   ScriptExecutor::ScriptType script_type = ScriptExecutor::JAVASCRIPT;
    130   if (ShouldInsertCSS())
    131     script_type = ScriptExecutor::CSS;
    132 
    133   ScriptExecutor::FrameScope frame_scope =
    134       details_->all_frames.get() && *details_->all_frames
    135           ? ScriptExecutor::ALL_FRAMES
    136           : ScriptExecutor::TOP_FRAME;
    137 
    138   ScriptExecutor::MatchAboutBlank match_about_blank =
    139       details_->match_about_blank.get() && *details_->match_about_blank
    140           ? ScriptExecutor::MATCH_ABOUT_BLANK
    141           : ScriptExecutor::DONT_MATCH_ABOUT_BLANK;
    142 
    143   UserScript::RunLocation run_at = UserScript::UNDEFINED;
    144   switch (details_->run_at) {
    145     case InjectDetails::RUN_AT_NONE:
    146     case InjectDetails::RUN_AT_DOCUMENT_IDLE:
    147       run_at = UserScript::DOCUMENT_IDLE;
    148       break;
    149     case InjectDetails::RUN_AT_DOCUMENT_START:
    150       run_at = UserScript::DOCUMENT_START;
    151       break;
    152     case InjectDetails::RUN_AT_DOCUMENT_END:
    153       run_at = UserScript::DOCUMENT_END;
    154       break;
    155   }
    156   CHECK_NE(UserScript::UNDEFINED, run_at);
    157 
    158   executor->ExecuteScript(
    159       extension()->id(),
    160       script_type,
    161       code_string,
    162       frame_scope,
    163       match_about_blank,
    164       run_at,
    165       ScriptExecutor::ISOLATED_WORLD,
    166       IsWebView() ? ScriptExecutor::WEB_VIEW_PROCESS
    167                   : ScriptExecutor::DEFAULT_PROCESS,
    168       GetWebViewSrc(),
    169       file_url_,
    170       user_gesture_,
    171       has_callback() ? ScriptExecutor::JSON_SERIALIZED_RESULT
    172                      : ScriptExecutor::NO_RESULT,
    173       base::Bind(&ExecuteCodeFunction::OnExecuteCodeFinished, this));
    174   return true;
    175 }
    176 
    177 bool ExecuteCodeFunction::HasPermission() {
    178   return true;
    179 }
    180 
    181 bool ExecuteCodeFunction::RunAsync() {
    182   EXTENSION_FUNCTION_VALIDATE(Init());
    183 
    184   if (!details_->code.get() && !details_->file.get()) {
    185     error_ = kNoCodeOrFileToExecuteError;
    186     return false;
    187   }
    188   if (details_->code.get() && details_->file.get()) {
    189     error_ = kMoreThanOneValuesError;
    190     return false;
    191   }
    192 
    193   if (!CanExecuteScriptOnPage())
    194     return false;
    195 
    196   if (details_->code.get())
    197     return Execute(*details_->code);
    198 
    199   if (!details_->file.get())
    200     return false;
    201   resource_ = extension()->GetResource(*details_->file);
    202 
    203   if (resource_.extension_root().empty() || resource_.relative_path().empty()) {
    204     error_ = kNoCodeOrFileToExecuteError;
    205     return false;
    206   }
    207 
    208   int resource_id;
    209   if (ExtensionsBrowserClient::Get()
    210           ->GetComponentExtensionResourceManager()
    211           ->IsComponentExtensionResource(resource_.extension_root(),
    212                                          resource_.relative_path(),
    213                                          &resource_id)) {
    214     const ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    215     DidLoadFile(true, rb.GetRawDataResource(resource_id).as_string());
    216   } else {
    217     scoped_refptr<FileReader> file_reader(new FileReader(
    218         resource_, base::Bind(&ExecuteCodeFunction::DidLoadFile, this)));
    219     file_reader->Start();
    220   }
    221 
    222   return true;
    223 }
    224 
    225 void ExecuteCodeFunction::OnExecuteCodeFinished(const std::string& error,
    226                                                 const GURL& on_url,
    227                                                 const base::ListValue& result) {
    228   if (!error.empty())
    229     SetError(error);
    230 
    231   SendResponse(error.empty());
    232 }
    233 
    234 }  // namespace extensions
    235 
    236 #endif  // EXTENSIONS_BROWSER_API_EXECUTE_CODE_FUNCTION_IMPL_H_
    237