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