| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/webui/voice_search_ui.h" |
| |
| #include <string> |
| #include <utility> |
| |
| #include "base/command_line.h" |
| #include "base/files/file_enumerator.h" |
| #include "base/macros.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/metrics/field_trial.h" |
| #include "base/path_service.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/plugins/plugin_prefs.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/search/hotword_service.h" |
| #include "chrome/browser/search/hotword_service_factory.h" |
| #include "chrome/browser/ui/app_list/start_page_service.h" |
| #include "chrome/browser/ui/webui/version_handler.h" |
| #include "chrome/common/channel_info.h" |
| #include "chrome/common/chrome_content_client.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "chrome/common/extensions/extension_constants.h" |
| #include "chrome/common/features.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/url_constants.h" |
| #include "chrome/grit/browser_resources.h" |
| #include "chrome/grit/chromium_strings.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/strings/grit/components_strings.h" |
| #include "components/version_info/version_info.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/plugin_service.h" |
| #include "content/public/browser/url_data_source.h" |
| #include "content/public/browser/web_ui.h" |
| #include "content/public/browser/web_ui_data_source.h" |
| #include "content/public/browser/web_ui_message_handler.h" |
| #include "content/public/common/user_agent.h" |
| #include "extensions/browser/extension_prefs.h" |
| #include "extensions/browser/extension_system.h" |
| #include "extensions/common/extension.h" |
| #include "extensions/features/features.h" |
| #include "ui/base/l10n/l10n_util.h" |
| |
| #if defined(OS_WIN) |
| #include "base/win/windows_version.h" |
| #endif |
| |
| using base::ASCIIToUTF16; |
| using content::WebUIMessageHandler; |
| |
| namespace { |
| |
| content::WebUIDataSource* CreateVoiceSearchUiHtmlSource() { |
| content::WebUIDataSource* html_source = |
| content::WebUIDataSource::Create(chrome::kChromeUIVoiceSearchHost); |
| |
| html_source->AddLocalizedString("loadingMessage", |
| IDS_VOICESEARCH_LOADING_MESSAGE); |
| html_source->AddLocalizedString("voiceSearchLongTitle", |
| IDS_VOICESEARCH_TITLE_MESSAGE); |
| |
| html_source->SetJsonPath("strings.js"); |
| html_source->AddResourcePath("about_voicesearch.js", |
| IDR_ABOUT_VOICESEARCH_JS); |
| html_source->SetDefaultResource(IDR_ABOUT_VOICESEARCH_HTML); |
| return html_source; |
| } |
| |
| // Helper functions for collecting a list of key-value pairs that will |
| // be displayed. |
| void AddPair16(base::ListValue* list, |
| const base::string16& key, |
| const base::string16& value) { |
| std::unique_ptr<base::DictionaryValue> results(new base::DictionaryValue()); |
| results->SetString("key", key); |
| results->SetString("value", value); |
| list->Append(std::move(results)); |
| } |
| |
| void AddPair(base::ListValue* list, |
| const base::StringPiece& key, |
| const base::StringPiece& value) { |
| AddPair16(list, UTF8ToUTF16(key), UTF8ToUTF16(value)); |
| } |
| |
| void AddPairBool(base::ListValue* list, |
| const base::StringPiece& key, |
| bool value) { |
| AddPair(list, key, value ? "Yes" : "No"); |
| } |
| |
| // Generate an empty data-pair which acts as a line break. |
| void AddLineBreak(base::ListValue* list) { |
| AddPair(list, "", ""); |
| } |
| |
| void AddSharedModulePlatformsOnFileThread(base::ListValue* list, |
| const base::FilePath& path, |
| base::Closure callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); |
| |
| if (!path.empty()) { |
| // Display available platforms for shared module. |
| base::FilePath platforms_path = path.AppendASCII("_platform_specific"); |
| base::FileEnumerator enumerator( |
| platforms_path, false, base::FileEnumerator::DIRECTORIES); |
| base::string16 files; |
| for (base::FilePath name = enumerator.Next(); |
| !name.empty(); |
| name = enumerator.Next()) { |
| files += name.BaseName().LossyDisplayName() + ASCIIToUTF16(" "); |
| } |
| AddPair16(list, |
| ASCIIToUTF16("Shared Module Platforms"), |
| files.empty() ? ASCIIToUTF16("undefined") : files); |
| AddLineBreak(list); |
| } |
| |
| content::BrowserThread::PostTask(content::BrowserThread::UI, |
| FROM_HERE, |
| callback); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // VoiceSearchDomHandler |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| // The handler for Javascript messages for the about:flags page. |
| class VoiceSearchDomHandler : public WebUIMessageHandler { |
| public: |
| explicit VoiceSearchDomHandler(Profile* profile) |
| : profile_(profile), |
| weak_factory_(this) {} |
| |
| ~VoiceSearchDomHandler() override {} |
| |
| // WebUIMessageHandler implementation. |
| void RegisterMessages() override { |
| web_ui()->RegisterMessageCallback( |
| "requestVoiceSearchInfo", |
| base::Bind(&VoiceSearchDomHandler::HandleRequestVoiceSearchInfo, |
| base::Unretained(this))); |
| } |
| |
| private: |
| // Callback for the "requestVoiceSearchInfo" message. No arguments. |
| void HandleRequestVoiceSearchInfo(const base::ListValue* args) { |
| PopulatePageInformation(); |
| } |
| |
| void ReturnVoiceSearchInfo(std::unique_ptr<base::ListValue> info) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| DCHECK(info); |
| base::DictionaryValue voiceSearchInfo; |
| voiceSearchInfo.Set("voiceSearchInfo", std::move(info)); |
| web_ui()->CallJavascriptFunctionUnsafe("returnVoiceSearchInfo", |
| voiceSearchInfo); |
| } |
| |
| // Fill in the data to be displayed on the page. |
| void PopulatePageInformation() { |
| // Store Key-Value pairs of about-information. |
| std::unique_ptr<base::ListValue> list(new base::ListValue()); |
| |
| // Populate information. |
| AddOperatingSystemInfo(list.get()); |
| AddAudioInfo(list.get()); |
| AddLanguageInfo(list.get()); |
| AddHotwordInfo(list.get()); |
| AddAppListInfo(list.get()); |
| |
| AddExtensionInfo(extension_misc::kHotwordNewExtensionId, |
| "Extension", |
| list.get()); |
| |
| AddExtensionInfo(extension_misc::kHotwordSharedModuleId, |
| "Shared Module", |
| list.get()); |
| |
| base::FilePath path; |
| extensions::ExtensionSystem* extension_system = |
| extensions::ExtensionSystem::Get(profile_); |
| if (extension_system) { |
| ExtensionService* extension_service = |
| extension_system->extension_service(); |
| const extensions::Extension* extension = |
| extension_service->GetExtensionById( |
| extension_misc::kHotwordSharedModuleId, true); |
| if (extension) |
| path = extension->path(); |
| } |
| base::ListValue* raw_list = list.get(); |
| content::BrowserThread::PostTask( |
| content::BrowserThread::FILE, FROM_HERE, |
| base::Bind(&AddSharedModulePlatformsOnFileThread, raw_list, path, |
| base::Bind(&VoiceSearchDomHandler::ReturnVoiceSearchInfo, |
| weak_factory_.GetWeakPtr(), |
| base::Passed(std::move(list))))); |
| } |
| |
| // Adds information regarding the system and chrome version info to list. |
| void AddOperatingSystemInfo(base::ListValue* list) { |
| // Obtain the Chrome version info. |
| AddPair(list, |
| l10n_util::GetStringUTF8(IDS_PRODUCT_NAME), |
| version_info::GetVersionNumber() + " (" + |
| chrome::GetChannelString() + ")"); |
| |
| // OS version information. |
| std::string os_label = version_info::GetOSType(); |
| #if defined(OS_WIN) |
| base::win::OSInfo* os = base::win::OSInfo::GetInstance(); |
| switch (os->version()) { |
| case base::win::VERSION_XP: |
| os_label += " XP"; |
| break; |
| case base::win::VERSION_SERVER_2003: |
| os_label += " Server 2003 or XP Pro 64 bit"; |
| break; |
| case base::win::VERSION_VISTA: |
| os_label += " Vista or Server 2008"; |
| break; |
| case base::win::VERSION_WIN7: |
| os_label += " 7 or Server 2008 R2"; |
| break; |
| case base::win::VERSION_WIN8: |
| os_label += " 8 or Server 2012"; |
| break; |
| default: |
| os_label += " UNKNOWN"; |
| break; |
| } |
| os_label += " SP" + base::IntToString(os->service_pack().major); |
| |
| if (os->service_pack().minor > 0) |
| os_label += "." + base::IntToString(os->service_pack().minor); |
| |
| if (os->architecture() == base::win::OSInfo::X64_ARCHITECTURE) |
| os_label += " 64 bit"; |
| #endif |
| AddPair(list, l10n_util::GetStringUTF8(IDS_VERSION_UI_OS), os_label); |
| |
| AddLineBreak(list); |
| } |
| |
| // Adds information regarding audio to the list. |
| void AddAudioInfo(base::ListValue* list) { |
| // NaCl and its associated functions are not available on most mobile |
| // platforms. ENABLE_EXTENSIONS covers those platforms and hey would not |
| // allow Hotwording anyways since it is an extension. |
| std::string nacl_enabled = "not available"; |
| #if BUILDFLAG(ENABLE_EXTENSIONS) |
| nacl_enabled = "No"; |
| // Determine if NaCl is available. |
| base::FilePath path; |
| if (PathService::Get(chrome::FILE_NACL_PLUGIN, &path)) { |
| content::WebPluginInfo info; |
| PluginPrefs* plugin_prefs = PluginPrefs::GetForProfile(profile_).get(); |
| if (content::PluginService::GetInstance()->GetPluginInfoByPath(path, |
| &info) && |
| plugin_prefs->IsPluginEnabled(info)) { |
| nacl_enabled = "Yes"; |
| } |
| } |
| #endif |
| |
| AddPair(list, "NaCl Enabled", nacl_enabled); |
| |
| HotwordService* hotword_service = |
| HotwordServiceFactory::GetForProfile(profile_); |
| AddPairBool(list, "Microphone Present", |
| hotword_service && hotword_service->microphone_available()); |
| |
| AddPairBool(list, "Audio Capture Allowed", |
| profile_->GetPrefs()->GetBoolean(prefs::kAudioCaptureAllowed)); |
| |
| AddLineBreak(list); |
| } |
| |
| // Adds information regarding languages to the list. |
| void AddLanguageInfo(base::ListValue* list) { |
| std::string locale = |
| #if defined(OS_CHROMEOS) |
| // On ChromeOS locale is per-profile. |
| profile_->GetPrefs()->GetString(prefs::kApplicationLocale); |
| #else |
| g_browser_process->GetApplicationLocale(); |
| #endif |
| AddPair(list, "Current Language", locale); |
| |
| AddPair(list, |
| "Hotword Previous Language", |
| profile_->GetPrefs()->GetString(prefs::kHotwordPreviousLanguage)); |
| |
| AddLineBreak(list); |
| } |
| |
| // Adds information specific to the hotword configuration to the list. |
| void AddHotwordInfo(base::ListValue* list) { |
| HotwordService* hotword_service = |
| HotwordServiceFactory::GetForProfile(profile_); |
| AddPairBool(list, "Hotword Module Installable", |
| hotword_service && hotword_service->IsHotwordAllowed()); |
| |
| AddPairBool(list, "Hotword Search Enabled", |
| profile_->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled)); |
| |
| AddPairBool( |
| list, "Always-on Hotword Search Enabled", |
| profile_->GetPrefs()->GetBoolean(prefs::kHotwordAlwaysOnSearchEnabled)); |
| |
| AddPairBool(list, "Hotword Audio Logging Enabled", |
| hotword_service && hotword_service->IsOptedIntoAudioLogging()); |
| |
| AddLineBreak(list); |
| } |
| |
| // Adds information specific to an extension to the list. |
| void AddExtensionInfo(const std::string& extension_id, |
| const std::string& name_prefix, |
| base::ListValue* list) { |
| DCHECK(!name_prefix.empty()); |
| std::string version("undefined"); |
| std::string id("undefined"); |
| base::FilePath path; |
| |
| extensions::ExtensionSystem* extension_system = |
| extensions::ExtensionSystem::Get(profile_); |
| if (extension_system) { |
| ExtensionService* extension_service = |
| extension_system->extension_service(); |
| const extensions::Extension* extension = |
| extension_service->GetExtensionById(extension_id, true); |
| if (extension) { |
| id = extension->id(); |
| version = extension->VersionString(); |
| path = extension->path(); |
| } |
| } |
| AddPair(list, name_prefix + " Id", id); |
| AddPair(list, name_prefix + " Version", version); |
| AddPair16(list, |
| ASCIIToUTF16(name_prefix + " Path"), |
| path.empty() ? |
| ASCIIToUTF16("undefined") : path.LossyDisplayName()); |
| |
| extensions::ExtensionPrefs* extension_prefs = |
| extensions::ExtensionPrefs::Get(profile_); |
| int pref_state = -1; |
| extension_prefs->ReadPrefAsInteger(extension_id, "state", &pref_state); |
| std::string state; |
| switch (pref_state) { |
| case extensions::Extension::DISABLED: |
| state = "DISABLED"; |
| break; |
| case extensions::Extension::ENABLED: |
| state = "ENABLED"; |
| break; |
| case extensions::Extension::EXTERNAL_EXTENSION_UNINSTALLED: |
| state = "EXTERNAL_EXTENSION_UNINSTALLED"; |
| break; |
| default: |
| state = "undefined"; |
| } |
| |
| AddPair(list, name_prefix + " State", state); |
| |
| AddLineBreak(list); |
| } |
| |
| // Adds information specific to voice search in the app launcher to the list. |
| void AddAppListInfo(base::ListValue* list) { |
| #if BUILDFLAG(ENABLE_APP_LIST) |
| std::string state = "No Start Page Service"; |
| app_list::StartPageService* start_page_service = |
| app_list::StartPageService::Get(profile_); |
| if (start_page_service) { |
| app_list::SpeechRecognitionState speech_state = |
| start_page_service->state(); |
| switch (speech_state) { |
| case app_list::SPEECH_RECOGNITION_OFF: |
| state = "SPEECH_RECOGNITION_OFF"; |
| break; |
| case app_list::SPEECH_RECOGNITION_READY: |
| state = "SPEECH_RECOGNITION_READY"; |
| break; |
| case app_list::SPEECH_RECOGNITION_HOTWORD_LISTENING: |
| state = "SPEECH_RECOGNITION_HOTWORD_LISTENING"; |
| break; |
| case app_list::SPEECH_RECOGNITION_RECOGNIZING: |
| state = "SPEECH_RECOGNITION_RECOGNIZING"; |
| break; |
| case app_list::SPEECH_RECOGNITION_IN_SPEECH: |
| state = "SPEECH_RECOGNITION_IN_SPEECH"; |
| break; |
| case app_list::SPEECH_RECOGNITION_STOPPING: |
| state = "SPEECH_RECOGNITION_STOPPING"; |
| break; |
| case app_list::SPEECH_RECOGNITION_NETWORK_ERROR: |
| state = "SPEECH_RECOGNITION_NETWORK_ERROR"; |
| break; |
| default: |
| state = "undefined"; |
| } |
| } |
| AddPair(list, "Start Page State", state); |
| AddLineBreak(list); |
| #endif |
| } |
| |
| Profile* profile_; |
| base::WeakPtrFactory<VoiceSearchDomHandler> weak_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(VoiceSearchDomHandler); |
| }; |
| |
| } // namespace |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // |
| // VoiceSearchUI |
| // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| VoiceSearchUI::VoiceSearchUI(content::WebUI* web_ui) |
| : content::WebUIController(web_ui) { |
| Profile* profile = Profile::FromWebUI(web_ui); |
| web_ui->AddMessageHandler(base::MakeUnique<VoiceSearchDomHandler>(profile)); |
| |
| // Set up the about:voicesearch source. |
| content::WebUIDataSource::Add(profile, CreateVoiceSearchUiHtmlSource()); |
| } |
| |
| VoiceSearchUI::~VoiceSearchUI() {} |