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