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