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, std::unique_ptr<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), | |
|
David Tseng
2016/09/08 20:39:31
nit: indent
dmazzoni
2016/09/08 21:00:55
git cl format is happy with this...where did you e
David Tseng
2016/09/08 21:18:42
+4 since it's a continuation of the previous line.
dmazzoni
2016/09/13 23:03:19
The colon to begin initializers for a constructor
| |
| 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; | |
|
David Tseng
2016/09/08 20:39:31
This block has similarity index with Accessibility
dmazzoni
2016/09/08 21:00:55
Good question. There are a few scenarios.
1. If C
David Tseng
2016/09/08 21:18:42
because we knew that UpdateSpokenFeedbackFromPref
dmazzoni
2016/09/13 23:03:19
Makes sense, I added a call to SetProfile.
| |
| 132 | |
| 133 if (!loaded_on_user_screen_ && !loaded_on_lock_screen_) | |
| 134 return; | |
| 135 | |
| 136 // If the extension was already enabled, but not for this profile, | |
| 137 // add it to this profile. | |
|
David Tseng
2016/09/08 20:39:31
nit: more room above
dmazzoni
2016/09/08 21:00:55
done
| |
| 138 auto* extension_service = | |
| 139 extensions::ExtensionSystem::Get(profile_)->extension_service(); | |
| 140 auto* component_loader = extension_service->component_loader(); | |
| 141 if (!component_loader->Exists(extension_id_)) | |
| 142 LoadExtension(profile_, nullptr, base::Closure()); | |
| 143 } | |
| 144 | |
| 145 void AccessibilityExtensionLoader::Load(Profile* profile, | |
| 146 const std::string& init_script_str, | |
| 147 const base::Closure& done_cb) { | |
| 148 profile_ = profile; | |
| 149 init_script_str_ = init_script_str; | |
| 150 ScreenLocker* screen_locker = ScreenLocker::default_screen_locker(); | |
| 151 if (screen_locker && screen_locker->locked()) { | |
| 152 // If on the lock screen, loads only to the lock screen as for | |
| 153 // now. On unlock, it will be loaded to the user screen. | |
| 154 // (see. AccessibilityExtensionLoader::Observe()) | |
| 155 LoadToLockScreen(done_cb); | |
| 156 } else { | |
| 157 LoadToUserScreen(done_cb); | |
| 158 } | |
| 159 } | |
| 160 | |
| 161 void AccessibilityExtensionLoader::Unload() { | |
| 162 if (loaded_on_lock_screen_) | |
| 163 UnloadFromLockScreen(); | |
| 164 | |
| 165 if (loaded_on_user_screen_) { | |
| 166 UnloadExtensionFromProfile(profile_); | |
| 167 loaded_on_user_screen_ = false; | |
| 168 } | |
| 169 | |
| 170 profile_ = nullptr; | |
| 171 } | |
| 172 | |
| 173 void AccessibilityExtensionLoader::LoadToUserScreen( | |
| 174 const base::Closure& done_cb) { | |
| 175 if (loaded_on_user_screen_) | |
| 176 return; | |
| 177 | |
| 178 // Determine whether an OOBE screen is currently being shown. If so, | |
| 179 // the extension will be injected directly into that screen. | |
| 180 content::WebUI* login_web_ui = NULL; | |
| 181 | |
| 182 if (ProfileHelper::IsSigninProfile(profile_)) { | |
| 183 LoginDisplayHost* login_display_host = LoginDisplayHost::default_host(); | |
| 184 if (login_display_host) { | |
| 185 WebUILoginView* web_ui_login_view = | |
| 186 login_display_host->GetWebUILoginView(); | |
| 187 if (web_ui_login_view) | |
| 188 login_web_ui = web_ui_login_view->GetWebUI(); | |
| 189 } | |
| 190 | |
| 191 // Lock screen uses the signin progile. | |
| 192 loaded_on_lock_screen_ = true; | |
| 193 } | |
| 194 | |
| 195 loaded_on_user_screen_ = true; | |
| 196 LoadExtension( | |
| 197 profile_, | |
| 198 login_web_ui ? login_web_ui->GetWebContents()->GetRenderViewHost() : NULL, | |
| 199 done_cb); | |
| 200 } | |
| 201 | |
| 202 void AccessibilityExtensionLoader::LoadToLockScreen( | |
| 203 const base::Closure& done_cb) { | |
| 204 if (loaded_on_lock_screen_) | |
| 205 return; | |
| 206 | |
| 207 ScreenLocker* screen_locker = ScreenLocker::default_screen_locker(); | |
| 208 if (screen_locker && screen_locker->locked()) { | |
| 209 content::WebUI* lock_web_ui = screen_locker->GetAssociatedWebUI(); | |
| 210 if (lock_web_ui) { | |
| 211 Profile* profile = Profile::FromWebUI(lock_web_ui); | |
| 212 loaded_on_lock_screen_ = true; | |
| 213 LoadExtension(profile, lock_web_ui->GetWebContents()->GetRenderViewHost(), | |
| 214 done_cb); | |
| 215 } | |
| 216 } | |
| 217 } | |
| 218 | |
| 219 void AccessibilityExtensionLoader::InjectContentScript( | |
| 220 content::RenderViewHost* render_view_host) { | |
| 221 ExtensionService* extension_service = | |
| 222 extensions::ExtensionSystem::Get(profile_)->extension_service(); | |
| 223 InjectContentScriptAndCallback( | |
| 224 extension_service, render_view_host->GetProcess()->GetID(), | |
| 225 render_view_host->GetRoutingID(), base::Closure()); | |
| 226 } | |
| 227 | |
| 228 // | |
| 229 // private | |
| 230 // | |
| 231 | |
| 232 void AccessibilityExtensionLoader::UnloadExtensionFromProfile( | |
| 233 Profile* profile) { | |
| 234 ExtensionService* extension_service = | |
| 235 extensions::ExtensionSystem::Get(profile)->extension_service(); | |
| 236 extension_service->component_loader()->Remove(extension_path_); | |
| 237 } | |
| 238 | |
| 239 void AccessibilityExtensionLoader::UnloadFromLockScreen() { | |
| 240 // Lock screen uses the signin progile. | |
| 241 Profile* signin_profile = ProfileHelper::GetSigninProfile(); | |
| 242 UnloadExtensionFromProfile(signin_profile); | |
| 243 loaded_on_lock_screen_ = false; | |
| 244 } | |
| 245 | |
| 246 void AccessibilityExtensionLoader::InjectContentScriptAndCallback( | |
| 247 ExtensionService* extension_service, | |
| 248 int render_process_id, | |
| 249 int render_view_id, | |
| 250 const base::Closure& done_cb) { | |
| 251 // Make sure to always run |done_cb|. The extension was loaded even | |
| 252 // if we end up not injecting into this particular render view. | |
| 253 base::ScopedClosureRunner done_runner(done_cb); | |
| 254 content::RenderViewHost* render_view_host = | |
| 255 content::RenderViewHost::FromID(render_process_id, render_view_id); | |
| 256 if (!render_view_host) | |
| 257 return; | |
| 258 const content::WebContents* web_contents = | |
| 259 content::WebContents::FromRenderViewHost(render_view_host); | |
| 260 GURL content_url; | |
| 261 if (web_contents) | |
| 262 content_url = web_contents->GetLastCommittedURL(); | |
| 263 const extensions::Extension* extension = | |
| 264 extensions::ExtensionRegistry::Get(extension_service->profile()) | |
| 265 ->enabled_extensions() | |
| 266 .GetByID(extension_id_); | |
| 267 | |
| 268 if (!init_script_str_.empty()) { | |
| 269 ExecuteScriptHelper(render_view_host, init_script_str_, extension->id()); | |
| 270 } | |
| 271 | |
| 272 // Inject the content scripts. | |
| 273 ContentScriptLoader* loader = new ContentScriptLoader( | |
| 274 extension->id(), render_view_host->GetProcess()->GetID(), | |
| 275 render_view_host->GetRoutingID()); | |
| 276 | |
| 277 const extensions::UserScriptList& content_scripts = | |
| 278 extensions::ContentScriptsInfo::GetContentScripts(extension); | |
| 279 for (const std::unique_ptr<extensions::UserScript>& script : | |
| 280 content_scripts) { | |
| 281 if (web_contents && !script->MatchesURL(content_url)) | |
| 282 continue; | |
| 283 for (const std::unique_ptr<extensions::UserScript::File>& file : | |
| 284 script->js_scripts()) { | |
| 285 extensions::ExtensionResource resource = | |
| 286 extension->GetResource(file->relative_path()); | |
| 287 loader->AppendScript(resource); | |
| 288 } | |
| 289 } | |
| 290 loader->Run(); // It cleans itself up when done. | |
| 291 } | |
| 292 | |
| 293 void AccessibilityExtensionLoader::LoadExtension( | |
| 294 Profile* profile, | |
| 295 content::RenderViewHost* render_view_host, | |
| 296 base::Closure done_cb) { | |
| 297 ExtensionService* extension_service = | |
| 298 extensions::ExtensionSystem::Get(profile)->extension_service(); | |
| 299 if (render_view_host) { | |
| 300 // Wrap the passed in callback to inject the content script. | |
| 301 done_cb = base::Bind( | |
| 302 &AccessibilityExtensionLoader::InjectContentScriptAndCallback, | |
| 303 weak_ptr_factory_.GetWeakPtr(), extension_service, | |
| 304 render_view_host->GetProcess()->GetID(), | |
| 305 render_view_host->GetRoutingID(), done_cb); | |
| 306 } | |
| 307 | |
| 308 extension_service->component_loader()->AddComponentFromDir( | |
| 309 extension_path_, extension_id_.c_str(), done_cb); | |
| 310 } | |
| 311 | |
| 312 } // namespace chromeos | |
| OLD | NEW |