Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 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/chromeos/accessibility/accessibility_extension_loader.h " | |
| 6 | |
| 7 #include "base/callback.h" | |
| 8 #include "base/callback_helpers.h" | |
| 9 #include "base/path_service.h" | |
| 10 #include "chrome/browser/chromeos/login/lock/screen_locker.h" | |
| 11 #include "chrome/browser/chromeos/login/ui/login_display_host.h" | |
| 12 #include "chrome/browser/chromeos/login/ui/webui_login_view.h" | |
| 13 #include "chrome/browser/chromeos/profiles/profile_helper.h" | |
| 14 #include "chrome/browser/extensions/component_loader.h" | |
| 15 #include "chrome/browser/extensions/extension_service.h" | |
| 16 #include "chrome/browser/extensions/tab_helper.h" | |
| 17 #include "chrome/browser/profiles/profile.h" | |
| 18 #include "chrome/common/extensions/extension_constants.h" | |
| 19 #include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h" | |
| 20 #include "content/public/browser/render_process_host.h" | |
| 21 #include "content/public/browser/render_view_host.h" | |
| 22 #include "content/public/browser/web_contents.h" | |
| 23 #include "extensions/browser/extension_api_frame_id_map.h" | |
| 24 #include "extensions/browser/extension_registry.h" | |
| 25 #include "extensions/browser/extension_system.h" | |
| 26 #include "extensions/browser/file_reader.h" | |
| 27 #include "extensions/browser/script_executor.h" | |
| 28 #include "extensions/common/extension.h" | |
| 29 #include "extensions/common/extension_messages.h" | |
| 30 #include "extensions/common/extension_resource.h" | |
| 31 | |
| 32 namespace chromeos { | |
| 33 | |
| 34 namespace { | |
| 35 | |
| 36 // Uses the ScriptExecutor associated with the given |render_view_host| to | |
| 37 // execute the given |code|. | |
| 38 void ExecuteScriptHelper(content::RenderViewHost* render_view_host, | |
| 39 const std::string& code, | |
| 40 const std::string& extension_id) { | |
| 41 content::WebContents* web_contents = | |
| 42 content::WebContents::FromRenderViewHost(render_view_host); | |
| 43 if (!web_contents) | |
| 44 return; | |
| 45 if (!extensions::TabHelper::FromWebContents(web_contents)) | |
| 46 extensions::TabHelper::CreateForWebContents(web_contents); | |
| 47 extensions::TabHelper::FromWebContents(web_contents) | |
| 48 ->script_executor() | |
| 49 ->ExecuteScript(HostID(HostID::EXTENSIONS, extension_id), | |
| 50 extensions::ScriptExecutor::JAVASCRIPT, code, | |
| 51 extensions::ScriptExecutor::INCLUDE_SUB_FRAMES, | |
| 52 extensions::ExtensionApiFrameIdMap::kTopFrameId, | |
| 53 extensions::ScriptExecutor::DONT_MATCH_ABOUT_BLANK, | |
| 54 extensions::UserScript::DOCUMENT_IDLE, | |
| 55 extensions::ScriptExecutor::ISOLATED_WORLD, | |
| 56 extensions::ScriptExecutor::DEFAULT_PROCESS, | |
| 57 GURL(), // No webview src. | |
| 58 GURL(), // No file url. | |
| 59 false, // Not user gesture. | |
| 60 extensions::ScriptExecutor::NO_RESULT, | |
| 61 extensions::ScriptExecutor::ExecuteScriptCallback()); | |
| 62 } | |
| 63 | |
| 64 // Helper class that directly loads an extension's content scripts into | |
| 65 // all of the frames corresponding to a given RenderViewHost. | |
| 66 class ContentScriptLoader { | |
| 67 public: | |
| 68 // Initialize the ContentScriptLoader with the ID of the extension | |
| 69 // and the RenderViewHost where the scripts should be loaded. | |
| 70 ContentScriptLoader(const std::string& extension_id, | |
| 71 int render_process_id, | |
| 72 int render_view_id) | |
| 73 : extension_id_(extension_id), | |
| 74 render_process_id_(render_process_id), | |
| 75 render_view_id_(render_view_id) {} | |
| 76 | |
| 77 // Call this once with the ExtensionResource corresponding to each | |
| 78 // content script to be loaded. | |
| 79 void AppendScript(extensions::ExtensionResource resource) { | |
| 80 resources_.push(resource); | |
| 81 } | |
| 82 | |
| 83 // Finally, call this method once to fetch all of the resources and | |
| 84 // load them. This method will delete this object when done. | |
| 85 void Run() { | |
| 86 if (resources_.empty()) { | |
| 87 delete this; | |
| 88 return; | |
| 89 } | |
| 90 | |
| 91 extensions::ExtensionResource resource = resources_.front(); | |
| 92 resources_.pop(); | |
| 93 scoped_refptr<FileReader> reader( | |
| 94 new FileReader(resource, base::Bind(&ContentScriptLoader::OnFileLoaded, | |
| 95 base::Unretained(this)))); | |
| 96 reader->Start(); | |
| 97 } | |
| 98 | |
| 99 private: | |
| 100 void OnFileLoaded(bool success, const std::string& data) { | |
| 101 if (success) { | |
| 102 content::RenderViewHost* render_view_host = | |
| 103 content::RenderViewHost::FromID(render_process_id_, render_view_id_); | |
| 104 if (render_view_host) | |
| 105 ExecuteScriptHelper(render_view_host, data, extension_id_); | |
| 106 } | |
| 107 Run(); | |
| 108 } | |
| 109 | |
| 110 std::string extension_id_; | |
| 111 int render_process_id_; | |
| 112 int render_view_id_; | |
| 113 std::queue<extensions::ExtensionResource> resources_; | |
| 114 }; | |
| 115 | |
| 116 } // namespace | |
| 117 | |
| 118 AccessibilityExtensionLoader::AccessibilityExtensionLoader( | |
| 119 const std::string& extension_id, | |
| 120 const base::FilePath& extension_path) | |
| 121 : profile_(nullptr), | |
| 122 extension_id_(extension_id), | |
| 123 extension_path_(extension_path), | |
| 124 loaded_on_lock_screen_(false), | |
| 125 loaded_on_user_screen_(false), | |
| 126 weak_ptr_factory_(this) {} | |
| 127 | |
| 128 AccessibilityExtensionLoader::~AccessibilityExtensionLoader() {} | |
| 129 | |
| 130 void AccessibilityExtensionLoader::SetProfile(Profile* profile) { | |
| 131 profile_ = profile; | |
| 132 } | |
| 133 | |
| 134 void AccessibilityExtensionLoader::Load(Profile* profile, | |
| 135 const std::string& init_script_str, | |
| 136 const base::Closure& done_cb) { | |
| 137 profile_ = profile; | |
| 138 init_script_str_ = init_script_str; | |
| 139 ScreenLocker* screen_locker = ScreenLocker::default_screen_locker(); | |
| 140 if (screen_locker && screen_locker->locked()) { | |
| 141 // If on the lock screen, loads only to the lock screen as for | |
| 142 // now. On unlock, it will be loaded to the user screen. | |
| 143 // (see. AccessibilityExtensionLoader::Observe()) | |
| 144 LoadToLockScreen(done_cb); | |
| 145 } else { | |
| 146 LoadToUserScreen(done_cb); | |
| 147 } | |
| 148 } | |
| 149 | |
| 150 void AccessibilityExtensionLoader::Unload() { | |
| 151 if (loaded_on_lock_screen_) | |
| 152 UnloadFromLockScreen(); | |
| 153 | |
| 154 if (loaded_on_user_screen_) { | |
| 155 UnloadExtensionFromProfile(profile_); | |
| 156 loaded_on_user_screen_ = false; | |
| 157 } | |
| 158 | |
| 159 profile_ = nullptr; | |
| 160 } | |
| 161 | |
| 162 void AccessibilityExtensionLoader::LoadToUserScreen( | |
| 163 const base::Closure& done_cb) { | |
| 164 if (loaded_on_user_screen_) | |
| 165 return; | |
| 166 | |
| 167 // Determine whether an OOBE screen is currently being shown. If so, | |
| 168 // the extension will be injected directly into that screen. | |
| 169 content::WebUI* login_web_ui = NULL; | |
| 170 | |
| 171 if (ProfileHelper::IsSigninProfile(profile_)) { | |
| 172 LoginDisplayHost* login_display_host = LoginDisplayHost::default_host(); | |
| 173 if (login_display_host) { | |
| 174 WebUILoginView* web_ui_login_view = | |
| 175 login_display_host->GetWebUILoginView(); | |
| 176 if (web_ui_login_view) | |
| 177 login_web_ui = web_ui_login_view->GetWebUI(); | |
| 178 } | |
| 179 | |
| 180 // Lock screen uses the signin progile. | |
| 181 loaded_on_lock_screen_ = true; | |
| 182 } | |
| 183 | |
| 184 loaded_on_user_screen_ = true; | |
| 185 LoadExtension( | |
| 186 profile_, | |
| 187 login_web_ui ? login_web_ui->GetWebContents()->GetRenderViewHost() : NULL, | |
| 188 done_cb); | |
| 189 } | |
| 190 | |
| 191 void AccessibilityExtensionLoader::LoadToLockScreen( | |
| 192 const base::Closure& done_cb) { | |
| 193 if (loaded_on_lock_screen_) | |
| 194 return; | |
| 195 | |
| 196 ScreenLocker* screen_locker = ScreenLocker::default_screen_locker(); | |
| 197 if (screen_locker && screen_locker->locked()) { | |
| 198 content::WebUI* lock_web_ui = screen_locker->GetAssociatedWebUI(); | |
| 199 if (lock_web_ui) { | |
| 200 Profile* profile = Profile::FromWebUI(lock_web_ui); | |
| 201 loaded_on_lock_screen_ = true; | |
| 202 LoadExtension(profile, lock_web_ui->GetWebContents()->GetRenderViewHost(), | |
| 203 done_cb); | |
| 204 } | |
| 205 } | |
| 206 } | |
| 207 | |
| 208 void AccessibilityExtensionLoader::InjectContentScript( | |
| 209 content::RenderViewHost* render_view_host) { | |
| 210 ExtensionService* extension_service = | |
| 211 extensions::ExtensionSystem::Get(profile_)->extension_service(); | |
| 212 InjectContentScriptAndCallback( | |
| 213 extension_service, render_view_host->GetProcess()->GetID(), | |
| 214 render_view_host->GetRoutingID(), base::Closure()); | |
| 215 } | |
| 216 | |
| 217 // | |
| 218 // private | |
| 219 // | |
| 220 | |
| 221 void AccessibilityExtensionLoader::UnloadExtensionFromProfile( | |
| 222 Profile* profile) { | |
| 223 ExtensionService* extension_service = | |
| 224 extensions::ExtensionSystem::Get(profile)->extension_service(); | |
| 225 extension_service->component_loader()->Remove(extension_path_); | |
| 226 } | |
| 227 | |
| 228 void AccessibilityExtensionLoader::UnloadFromLockScreen() { | |
| 229 // Lock screen uses the signin progile. | |
| 230 Profile* signin_profile = ProfileHelper::GetSigninProfile(); | |
| 231 UnloadExtensionFromProfile(signin_profile); | |
| 232 loaded_on_lock_screen_ = false; | |
| 233 } | |
| 234 | |
| 235 void AccessibilityExtensionLoader::InjectContentScriptAndCallback( | |
| 236 ExtensionService* extension_service, | |
| 237 int render_process_id, | |
| 238 int render_view_id, | |
| 239 const base::Closure& done_cb) { | |
| 240 // Make sure to always run |done_cb|. The extension was loaded even | |
| 241 // if we end up not injecting into this particular render view. | |
| 242 base::ScopedClosureRunner done_runner(done_cb); | |
| 243 content::RenderViewHost* render_view_host = | |
| 244 content::RenderViewHost::FromID(render_process_id, render_view_id); | |
| 245 if (!render_view_host) | |
| 246 return; | |
| 247 const content::WebContents* web_contents = | |
| 248 content::WebContents::FromRenderViewHost(render_view_host); | |
| 249 GURL content_url; | |
| 250 if (web_contents) | |
| 251 content_url = web_contents->GetLastCommittedURL(); | |
| 252 const extensions::Extension* extension = | |
| 253 extensions::ExtensionRegistry::Get(extension_service->profile()) | |
| 254 ->enabled_extensions() | |
| 255 .GetByID(extension_id_); | |
| 256 | |
| 257 if (!init_script_str_.empty()) { | |
| 258 ExecuteScriptHelper(render_view_host, init_script_str_, extension->id()); | |
| 259 } | |
| 260 | |
| 261 // Inject the content scripts. | |
| 262 ContentScriptLoader* loader = new ContentScriptLoader( | |
| 263 extension->id(), render_view_host->GetProcess()->GetID(), | |
| 264 render_view_host->GetRoutingID()); | |
| 265 | |
| 266 const extensions::UserScriptList& content_scripts = | |
| 267 extensions::ContentScriptsInfo::GetContentScripts(extension); | |
| 268 for (size_t i = 0; i < content_scripts.size(); i++) { | |
| 269 const extensions::UserScript& script = content_scripts[i]; | |
| 270 if (web_contents && !script.MatchesURL(content_url)) | |
| 271 continue; | |
| 272 for (size_t j = 0; j < script.js_scripts().size(); ++j) { | |
| 273 const extensions::UserScript::File& file = script.js_scripts()[j]; | |
| 274 extensions::ExtensionResource resource = | |
| 275 extension->GetResource(file.relative_path()); | |
| 276 loader->AppendScript(resource); | |
| 277 } | |
| 278 } | |
| 279 loader->Run(); // It cleans itself up when done. | |
| 280 } | |
| 281 | |
| 282 void AccessibilityExtensionLoader::LoadExtension( | |
| 283 Profile* profile, | |
| 284 content::RenderViewHost* render_view_host, | |
| 285 base::Closure done_cb) { | |
| 286 ExtensionService* extension_service = | |
| 287 extensions::ExtensionSystem::Get(profile)->extension_service(); | |
| 288 if (render_view_host) { | |
| 289 // Wrap the passed in callback to inject the content script. | |
| 290 done_cb = base::Bind( | |
| 291 &AccessibilityExtensionLoader::InjectContentScriptAndCallback, | |
| 292 weak_ptr_factory_.GetWeakPtr(), extension_service, | |
| 293 render_view_host->GetProcess()->GetID(), | |
| 294 render_view_host->GetRoutingID(), done_cb); | |
| 295 } | |
| 296 | |
| 297 extension_service->component_loader()->AddComponentWithGuestManifest( | |
|
David Tseng
2016/08/11 22:22:13
Are you going to move this out of component loader
David Tseng
2016/08/11 22:45:45
Caused a little bit of confusion because I can't s
dmazzoni
2016/09/08 16:43:47
This is the place where ChromeVox or another exten
| |
| 298 extension_path_, extension_id_.c_str(), done_cb); | |
| 299 } | |
| 300 | |
| 301 } // namespace chromeos | |
| OLD | NEW |