diff options
author | plundblad@chromium.org <plundblad@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-11 06:24:37 +0000 |
---|---|---|
committer | plundblad@chromium.org <plundblad@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-11 06:24:37 +0000 |
commit | 44beb038b6ed734bae5c4d8101847eb56dc498fe (patch) | |
tree | 8f880b1c3bfdeadd0dcbeb59c19b14ddb8de439c | |
parent | 21bc6c095c79723f1c2833678f506f19e9b32598 (diff) | |
download | chromium_src-44beb038b6ed734bae5c4d8101847eb56dc498fe.zip chromium_src-44beb038b6ed734bae5c4d8101847eb56dc498fe.tar.gz chromium_src-44beb038b6ed734bae5c4d8101847eb56dc498fe.tar.bz2 |
Remove ChromeVox manifest file read from UI thread.
Fixes a DCHECK by making ChromeVox extension load asynchronous.
This was broken in r272781 (https://codereview.chromium.org/295123002) when
the manifest was moved from the .pak file to its own file on disk.
BUG=
Review URL: https://codereview.chromium.org/323623002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@276277 0039d316-1c4b-4281-b951-d872f2087c98
4 files changed, 159 insertions, 76 deletions
diff --git a/chrome/browser/chromeos/accessibility/accessibility_manager.cc b/chrome/browser/chromeos/accessibility/accessibility_manager.cc index 91a66ae..66bfb63 100644 --- a/chrome/browser/chromeos/accessibility/accessibility_manager.cc +++ b/chrome/browser/chromeos/accessibility/accessibility_manager.cc @@ -12,6 +12,8 @@ #include "ash/shell.h" #include "ash/sticky_keys/sticky_keys_controller.h" #include "ash/system/tray/system_tray_notifier.h" +#include "base/callback.h" +#include "base/callback_helpers.h" #include "base/memory/scoped_ptr.h" #include "base/memory/singleton.h" #include "base/metrics/histogram.h" @@ -157,50 +159,77 @@ class ContentScriptLoader { std::queue<extensions::ExtensionResource> resources_; }; -void LoadChromeVoxExtension(Profile* profile, - RenderViewHost* render_view_host) { +void InjectChromeVoxContentScript( + ExtensionService* extension_service, + int render_process_id, + int render_view_id, + const base::Closure& done_cb); + +void LoadChromeVoxExtension( + Profile* profile, + RenderViewHost* render_view_host, + base::Closure done_cb) { ExtensionService* extension_service = extensions::ExtensionSystem::Get(profile)->extension_service(); - std::string extension_id = - extension_service->component_loader()->AddChromeVoxExtension(); if (render_view_host) { - ExtensionService* extension_service = - extensions::ExtensionSystem::Get(profile)->extension_service(); - const extensions::Extension* extension = - extension_service->extensions()->GetByID(extension_id); - - // Set a flag to tell ChromeVox that it's just been enabled, - // so that it won't interrupt our speech feedback enabled message. - ExtensionMsg_ExecuteCode_Params params; - params.request_id = 0; - params.extension_id = extension->id(); - params.is_javascript = true; - params.code = "window.INJECTED_AFTER_LOAD = true;"; - params.run_at = extensions::UserScript::DOCUMENT_IDLE; - params.all_frames = true; - params.match_about_blank = false; - params.in_main_world = false; - render_view_host->Send(new ExtensionMsg_ExecuteCode( - render_view_host->GetRoutingID(), params)); - - // Inject ChromeVox' content scripts. - ContentScriptLoader* loader = new ContentScriptLoader( - extension->id(), render_view_host->GetProcess()->GetID(), - render_view_host->GetRoutingID()); - - const extensions::UserScriptList& content_scripts = - extensions::ContentScriptsInfo::GetContentScripts(extension); - for (size_t i = 0; i < content_scripts.size(); i++) { - const extensions::UserScript& script = content_scripts[i]; - for (size_t j = 0; j < script.js_scripts().size(); ++j) { - const extensions::UserScript::File &file = script.js_scripts()[j]; - extensions::ExtensionResource resource = extension->GetResource( - file.relative_path()); - loader->AppendScript(resource); - } + // Wrap the passed in callback to inject the content script. + done_cb = base::Bind( + &InjectChromeVoxContentScript, + extension_service, + render_view_host->GetProcess()->GetID(), + render_view_host->GetRoutingID(), + done_cb); + } + extension_service->component_loader()->AddChromeVoxExtension(done_cb); +} + +void InjectChromeVoxContentScript( + ExtensionService* extension_service, + int render_process_id, + int render_view_id, + const base::Closure& done_cb) { + // Make sure to always run |done_cb|. ChromeVox was loaded even if we end up + // not injecting into this particular render view. + base::ScopedClosureRunner done_runner(done_cb); + RenderViewHost* render_view_host = + RenderViewHost::FromID(render_process_id, render_view_id); + if (!render_view_host) + return; + const extensions::Extension* extension = + extension_service->extensions()->GetByID( + extension_misc::kChromeVoxExtensionId); + + // Set a flag to tell ChromeVox that it's just been enabled, + // so that it won't interrupt our speech feedback enabled message. + ExtensionMsg_ExecuteCode_Params params; + params.request_id = 0; + params.extension_id = extension->id(); + params.is_javascript = true; + params.code = "window.INJECTED_AFTER_LOAD = true;"; + params.run_at = extensions::UserScript::DOCUMENT_IDLE; + params.all_frames = true; + params.match_about_blank = false; + params.in_main_world = false; + render_view_host->Send(new ExtensionMsg_ExecuteCode( + render_view_host->GetRoutingID(), params)); + + // Inject ChromeVox' content scripts. + ContentScriptLoader* loader = new ContentScriptLoader( + extension->id(), render_view_host->GetProcess()->GetID(), + render_view_host->GetRoutingID()); + + const extensions::UserScriptList& content_scripts = + extensions::ContentScriptsInfo::GetContentScripts(extension); + for (size_t i = 0; i < content_scripts.size(); i++) { + const extensions::UserScript& script = content_scripts[i]; + for (size_t j = 0; j < script.js_scripts().size(); ++j) { + const extensions::UserScript::File &file = script.js_scripts()[j]; + extensions::ExtensionResource resource = extension->GetResource( + file.relative_path()); + loader->AppendScript(resource); } - loader->Run(); // It cleans itself up when done. } + loader->Run(); // It cleans itself up when done. } void UnloadChromeVoxExtension(Profile* profile) { @@ -523,19 +552,22 @@ void AccessibilityManager::UpdateSpokenFeedbackFromPref() { } void AccessibilityManager::LoadChromeVox() { + base::Closure done_cb = base::Bind(&AccessibilityManager::PostLoadChromeVox, + weak_ptr_factory_.GetWeakPtr(), + profile_); ScreenLocker* screen_locker = ScreenLocker::default_screen_locker(); if (screen_locker && screen_locker->locked()) { // If on the lock screen, loads ChromeVox only to the lock screen as for // now. On unlock, it will be loaded to the user screen. // (see. AccessibilityManager::Observe()) - LoadChromeVoxToLockScreen(); + LoadChromeVoxToLockScreen(done_cb); } else { - LoadChromeVoxToUserScreen(); + LoadChromeVoxToUserScreen(done_cb); } - PostLoadChromeVox(profile_); } -void AccessibilityManager::LoadChromeVoxToUserScreen() { +void AccessibilityManager::LoadChromeVoxToUserScreen( + const base::Closure& done_cb) { if (chrome_vox_loaded_on_user_screen_) return; @@ -556,12 +588,15 @@ void AccessibilityManager::LoadChromeVoxToUserScreen() { chrome_vox_loaded_on_lock_screen_ = true; } - LoadChromeVoxExtension(profile_, login_web_ui ? - login_web_ui->GetWebContents()->GetRenderViewHost() : NULL); chrome_vox_loaded_on_user_screen_ = true; + LoadChromeVoxExtension( + profile_, login_web_ui ? + login_web_ui->GetWebContents()->GetRenderViewHost() : NULL, + done_cb); } -void AccessibilityManager::LoadChromeVoxToLockScreen() { +void AccessibilityManager::LoadChromeVoxToLockScreen( + const base::Closure& done_cb) { if (chrome_vox_loaded_on_lock_screen_) return; @@ -570,9 +605,11 @@ void AccessibilityManager::LoadChromeVoxToLockScreen() { content::WebUI* lock_web_ui = screen_locker->GetAssociatedWebUI(); if (lock_web_ui) { Profile* profile = Profile::FromWebUI(lock_web_ui); - LoadChromeVoxExtension(profile, - lock_web_ui->GetWebContents()->GetRenderViewHost()); chrome_vox_loaded_on_lock_screen_ = true; + LoadChromeVoxExtension( + profile, + lock_web_ui->GetWebContents()->GetRenderViewHost(), + done_cb); } } } @@ -925,7 +962,7 @@ base::TimeDelta AccessibilityManager::PlayShutdownSound() { } void AccessibilityManager::InjectChromeVox(RenderViewHost* render_view_host) { - LoadChromeVoxExtension(profile_, render_view_host); + LoadChromeVoxExtension(profile_, render_view_host, base::Closure()); } scoped_ptr<AccessibilityStatusSubscription> @@ -1013,16 +1050,13 @@ void AccessibilityManager::Observe( case chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED: { bool is_screen_locked = *content::Details<bool>(details).ptr(); if (spoken_feedback_enabled_) { - if (is_screen_locked) { - LoadChromeVoxToLockScreen(); - - // Status tray gets verbalized by user screen ChromeVox, so we need - // this as well. - LoadChromeVoxToUserScreen(); - } else { - // If spoken feedback was enabled, also enable it on the user screen. - LoadChromeVoxToUserScreen(); - } + if (is_screen_locked) + LoadChromeVoxToLockScreen(base::Closure()); + // If spoken feedback was enabled, make sure it is also enabled on + // the user screen. + // The status tray gets verbalized by user screen ChromeVox, so we need + // to load it on the user screen even if the screen is locked. + LoadChromeVoxToUserScreen(base::Closure()); } break; } diff --git a/chrome/browser/chromeos/accessibility/accessibility_manager.h b/chrome/browser/chromeos/accessibility/accessibility_manager.h index 91a4f4c..fa3f336 100644 --- a/chrome/browser/chromeos/accessibility/accessibility_manager.h +++ b/chrome/browser/chromeos/accessibility/accessibility_manager.h @@ -9,6 +9,7 @@ #include "ash/accessibility_delegate.h" #include "ash/session/session_state_observer.h" +#include "base/callback_forward.h" #include "base/callback_list.h" #include "base/memory/weak_ptr.h" #include "base/prefs/pref_change_registrar.h" @@ -195,8 +196,8 @@ class AccessibilityManager private: void LoadChromeVox(); - void LoadChromeVoxToUserScreen(); - void LoadChromeVoxToLockScreen(); + void LoadChromeVoxToUserScreen(const base::Closure& done_cb); + void LoadChromeVoxToLockScreen(const base::Closure& done_cb); void UnloadChromeVox(); void UnloadChromeVoxFromLockScreen(); void PostLoadChromeVox(Profile* profile); diff --git a/chrome/browser/extensions/component_loader.cc b/chrome/browser/extensions/component_loader.cc index cca90da..debe6cf 100644 --- a/chrome/browser/extensions/component_loader.cc +++ b/chrome/browser/extensions/component_loader.cc @@ -21,6 +21,7 @@ #include "chrome/common/chrome_version_info.h" #include "chrome/common/extensions/extension_constants.h" #include "chrome/common/pref_names.h" +#include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_details.h" #include "content/public/browser/notification_source.h" #include "content/public/browser/plugin_service.h" @@ -57,6 +58,8 @@ #include "grit/chromium_strings.h" #endif +using content::BrowserThread; + namespace extensions { namespace { @@ -97,6 +100,19 @@ std::string GenerateId(const base::DictionaryValue* manifest, return id; } +#if defined(OS_CHROMEOS) +scoped_ptr<base::DictionaryValue> +LoadManifestOnFileThread( + const base::FilePath& chromevox_path, const char* manifest_filename) { + DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); + std::string error; + scoped_ptr<base::DictionaryValue> manifest( + file_util::LoadManifest(chromevox_path, manifest_filename, &error)); + CHECK(manifest) << error; + return manifest.Pass(); +} +#endif // defined(OS_CHROMEOS) + } // namespace ComponentLoader::ComponentExtensionInfo::ComponentExtensionInfo( @@ -117,7 +133,8 @@ ComponentLoader::ComponentLoader(ExtensionServiceInterface* extension_service, : profile_prefs_(profile_prefs), local_state_(local_state), browser_context_(browser_context), - extension_service_(extension_service) {} + extension_service_(extension_service), + weak_factory_(this) {} ComponentLoader::~ComponentLoader() { ClearAllRegistered(); @@ -329,24 +346,38 @@ void ComponentLoader::AddNetworkSpeechSynthesisExtension() { } #if defined(OS_CHROMEOS) -std::string ComponentLoader::AddChromeVoxExtension() { - base::FilePath chromevox_path; - PathService::Get(chrome::DIR_RESOURCES, &chromevox_path); - chromevox_path = - chromevox_path.Append(extension_misc::kChromeVoxExtensionPath); +void ComponentLoader::AddChromeVoxExtension( + const base::Closure& done_cb) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + base::FilePath resources_path; + PathService::Get(chrome::DIR_RESOURCES, &resources_path); + base::FilePath chromevox_path = + resources_path.Append(extension_misc::kChromeVoxExtensionPath); const CommandLine* command_line = CommandLine::ForCurrentProcess(); const char* manifest_filename = command_line->HasSwitch(chromeos::switches::kGuestSession) ? extension_misc::kChromeVoxGuestManifestFilename : extension_misc::kChromeVoxManifestFilename; - - std::string error; - scoped_ptr<base::DictionaryValue> manifest( - file_util::LoadManifest(chromevox_path, manifest_filename, &error)); - CHECK(manifest) << error; - - return Add(manifest.release(), chromevox_path); + BrowserThread::PostTaskAndReplyWithResult( + BrowserThread::FILE, + FROM_HERE, + base::Bind(&LoadManifestOnFileThread, chromevox_path, manifest_filename), + base::Bind(&ComponentLoader::AddChromeVoxExtensionWithManifest, + weak_factory_.GetWeakPtr(), + chromevox_path, + done_cb)); +} + +void ComponentLoader::AddChromeVoxExtensionWithManifest( + const base::FilePath& chromevox_path, + const base::Closure& done_cb, + scoped_ptr<base::DictionaryValue> manifest) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + std::string extension_id = Add(manifest.release(), chromevox_path); + CHECK_EQ(extension_misc::kChromeVoxExtensionId, extension_id); + if (!done_cb.is_null()) + done_cb.Run(); } std::string ComponentLoader::AddChromeOsSpeechSynthesisExtension() { @@ -543,7 +574,7 @@ void ComponentLoader::AddDefaultComponentExtensionsWithBackgroundPages( // Load ChromeVox extension now if spoken feedback is enabled. if (chromeos::AccessibilityManager::Get() && chromeos::AccessibilityManager::Get()->IsSpokenFeedbackEnabled()) { - AddChromeVoxExtension(); + AddChromeVoxExtension(base::Closure()); } #endif // defined(OS_CHROMEOS) diff --git a/chrome/browser/extensions/component_loader.h b/chrome/browser/extensions/component_loader.h index 735184f..8e09727 100644 --- a/chrome/browser/extensions/component_loader.h +++ b/chrome/browser/extensions/component_loader.h @@ -8,8 +8,10 @@ #include <string> #include <vector> +#include "base/callback_forward.h" #include "base/files/file_path.h" #include "base/gtest_prod_util.h" +#include "base/memory/weak_ptr.h" #include "base/values.h" class ExtensionServiceInterface; @@ -97,7 +99,10 @@ class ComponentLoader { void Reload(const std::string& extension_id); #if defined(OS_CHROMEOS) - std::string AddChromeVoxExtension(); + // Calls |done_cb|, if not a null callback, on success. + // NOTE: |done_cb| is not called if the component loader is shut down + // during loading. + void AddChromeVoxExtension(const base::Closure& done_cb); std::string AddChromeOsSpeechSynthesisExtension(); #endif @@ -146,6 +151,16 @@ class ComponentLoader { // Enable HTML5 FileSystem for given component extension in Guest mode. void EnableFileSystemInGuestMode(const std::string& id); +#if defined(OS_CHROMEOS) + // Used as a reply callback when loading the ChromeVox extension. + // Called with a |chromevox_path| and parsed |manifest| and invokes + // |done_cb| after adding the extension. + void AddChromeVoxExtensionWithManifest( + const base::FilePath& chromevox_path, + const base::Closure& done_cb, + scoped_ptr<base::DictionaryValue> manifest); +#endif + PrefService* profile_prefs_; PrefService* local_state_; content::BrowserContext* browser_context_; @@ -156,6 +171,8 @@ class ComponentLoader { typedef std::vector<ComponentExtensionInfo> RegisteredComponentExtensions; RegisteredComponentExtensions component_extensions_; + base::WeakPtrFactory<ComponentLoader> weak_factory_; + FRIEND_TEST_ALL_PREFIXES(TtsApiTest, NetworkSpeechEngine); FRIEND_TEST_ALL_PREFIXES(TtsApiTest, NoNetworkSpeechEngineWhenOffline); |