blob: 5eba13c176c1a78da352bfa6d0b19eb823aec903 [file] [log] [blame]
// 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", info.release());
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() {}