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