1 // Copyright 2014 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/ui/webui/voicesearch_ui.h" 6 7 #include "base/command_line.h" 8 #include "base/metrics/field_trial.h" 9 #include "base/path_service.h" 10 #include "base/prefs/pref_service.h" 11 #include "base/strings/string_number_conversions.h" 12 #include "base/strings/utf_string_conversions.h" 13 #include "chrome/browser/browser_process.h" 14 #include "chrome/browser/extensions/extension_service.h" 15 #include "chrome/browser/plugins/plugin_prefs.h" 16 #include "chrome/browser/profiles/profile.h" 17 #include "chrome/browser/search/hotword_service.h" 18 #include "chrome/browser/search/hotword_service_factory.h" 19 #include "chrome/browser/ui/app_list/start_page_service.h" 20 #include "chrome/browser/ui/webui/version_handler.h" 21 #include "chrome/common/chrome_content_client.h" 22 #include "chrome/common/chrome_paths.h" 23 #include "chrome/common/chrome_version_info.h" 24 #include "chrome/common/extensions/extension_constants.h" 25 #include "chrome/common/pref_names.h" 26 #include "chrome/common/url_constants.h" 27 #include "chrome/grit/chromium_strings.h" 28 #include "chrome/grit/generated_resources.h" 29 #include "chrome/grit/google_chrome_strings.h" 30 #include "content/public/browser/plugin_service.h" 31 #include "content/public/browser/url_data_source.h" 32 #include "content/public/browser/web_ui.h" 33 #include "content/public/browser/web_ui_data_source.h" 34 #include "content/public/browser/web_ui_message_handler.h" 35 #include "content/public/common/user_agent.h" 36 #include "extensions/browser/extension_prefs.h" 37 #include "extensions/browser/extension_system.h" 38 #include "extensions/common/extension.h" 39 #include "grit/browser_resources.h" 40 #include "ui/base/l10n/l10n_util.h" 41 #include "v8/include/v8.h" 42 43 #if defined(OS_WIN) 44 #include "base/win/windows_version.h" 45 #endif 46 47 using base::ASCIIToUTF16; 48 using content::WebUIMessageHandler; 49 50 namespace { 51 52 content::WebUIDataSource* CreateVoiceSearchUiHtmlSource() { 53 content::WebUIDataSource* html_source = 54 content::WebUIDataSource::Create(chrome::kChromeUIVoiceSearchHost); 55 56 html_source->AddLocalizedString("loadingMessage", 57 IDS_VOICESEARCH_LOADING_MESSAGE); 58 html_source->AddLocalizedString("voiceSearchLongTitle", 59 IDS_VOICESEARCH_TITLE_MESSAGE); 60 61 html_source->SetUseJsonJSFormatV2(); 62 html_source->SetJsonPath("strings.js"); 63 html_source->AddResourcePath("about_voicesearch.js", 64 IDR_ABOUT_VOICESEARCH_JS); 65 html_source->SetDefaultResource(IDR_ABOUT_VOICESEARCH_HTML); 66 return html_source; 67 } 68 69 // Helper functions for collecting a list of key-value pairs that will 70 // be displayed. 71 void AddPair(base::ListValue* list, 72 const base::string16& key, 73 const base::string16& value) { 74 scoped_ptr<base::DictionaryValue> results(new base::DictionaryValue()); 75 results->SetString("key", key); 76 results->SetString("value", value); 77 list->Append(results.release()); 78 } 79 80 // Generate an empty data-pair which acts as a line break. 81 void AddLineBreak(base::ListValue* list) { 82 AddPair(list, ASCIIToUTF16(base::StringPiece()), 83 ASCIIToUTF16(base::StringPiece())); 84 } 85 86 //////////////////////////////////////////////////////////////////////////////// 87 // 88 // VoiceSearchDomHandler 89 // 90 //////////////////////////////////////////////////////////////////////////////// 91 92 // The handler for Javascript messages for the about:flags page. 93 class VoiceSearchDomHandler : public WebUIMessageHandler { 94 public: 95 explicit VoiceSearchDomHandler(Profile* profile) : profile_(profile) {} 96 97 virtual ~VoiceSearchDomHandler() {} 98 99 // WebUIMessageHandler implementation. 100 virtual void RegisterMessages() OVERRIDE { 101 web_ui()->RegisterMessageCallback( 102 "requestVoiceSearchInfo", 103 base::Bind(&VoiceSearchDomHandler::HandleRequestVoiceSearchInfo, 104 base::Unretained(this))); 105 } 106 107 private: 108 // Callback for the "requestVoiceSearchInfo" message. No arguments. 109 void HandleRequestVoiceSearchInfo(const base::ListValue* args) { 110 base::DictionaryValue voiceSearchInfo; 111 PopulatePageInformation(&voiceSearchInfo); 112 web_ui()->CallJavascriptFunction("returnVoiceSearchInfo", 113 voiceSearchInfo); 114 } 115 116 // Fill in the data to be displayed on the page. 117 void PopulatePageInformation(base::DictionaryValue* voiceSearchInfo) { 118 // Store Key-Value pairs of about-information. 119 scoped_ptr<base::ListValue> list(new base::ListValue()); 120 121 // Populate information. 122 AddOperatingSystemInfo(list.get()); 123 AddAudioInfo(list.get()); 124 AddLanguageInfo(list.get()); 125 AddHotwordInfo(list.get()); 126 AddHotwordExtensionInfo(list.get()); 127 AddAppListInfo(list.get()); 128 129 // voiceSearchInfo will take ownership of list, and clean it up on 130 // destruction. 131 voiceSearchInfo->Set("voiceSearchInfo", list.release()); 132 } 133 134 // Adds information regarding the system and chrome version info to list. 135 void AddOperatingSystemInfo(base::ListValue* list) { 136 // Obtain the Chrome version info. 137 chrome::VersionInfo version_info; 138 AddPair(list, 139 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME), 140 ASCIIToUTF16( 141 version_info.Version() + " (" + 142 chrome::VersionInfo::GetVersionStringModifier() + ")")); 143 144 // OS version information. 145 std::string os_label = version_info.OSType(); 146 #if defined(OS_WIN) 147 base::win::OSInfo* os = base::win::OSInfo::GetInstance(); 148 switch (os->version()) { 149 case base::win::VERSION_XP: 150 os_label += " XP"; 151 break; 152 case base::win::VERSION_SERVER_2003: 153 os_label += " Server 2003 or XP Pro 64 bit"; 154 break; 155 case base::win::VERSION_VISTA: 156 os_label += " Vista or Server 2008"; 157 break; 158 case base::win::VERSION_WIN7: 159 os_label += " 7 or Server 2008 R2"; 160 break; 161 case base::win::VERSION_WIN8: 162 os_label += " 8 or Server 2012"; 163 break; 164 default: 165 os_label += " UNKNOWN"; 166 break; 167 } 168 os_label += " SP" + base::IntToString(os->service_pack().major); 169 170 if (os->service_pack().minor > 0) 171 os_label += "." + base::IntToString(os->service_pack().minor); 172 173 if (os->architecture() == base::win::OSInfo::X64_ARCHITECTURE) 174 os_label += " 64 bit"; 175 #endif 176 AddPair(list, 177 l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_OS), 178 ASCIIToUTF16(os_label)); 179 180 AddLineBreak(list); 181 } 182 183 // Adds information regarding audio to the list. 184 void AddAudioInfo(base::ListValue* list) { 185 // NaCl and its associated functions are not available on most mobile 186 // platforms. ENABLE_EXTENSIONS covers those platforms and hey would not 187 // allow Hotwording anyways since it is an extension. 188 std::string nacl_enabled = "not available"; 189 #if defined(ENABLE_EXTENSIONS) 190 nacl_enabled = "No"; 191 // Determine if NaCl is available. 192 base::FilePath path; 193 if (PathService::Get(chrome::FILE_NACL_PLUGIN, &path)) { 194 content::WebPluginInfo info; 195 PluginPrefs* plugin_prefs = PluginPrefs::GetForProfile(profile_).get(); 196 if (content::PluginService::GetInstance()->GetPluginInfoByPath(path, 197 &info) && 198 plugin_prefs->IsPluginEnabled(info)) { 199 nacl_enabled = "Yes"; 200 } 201 } 202 #endif 203 204 AddPair(list, ASCIIToUTF16("NaCl Enabled"), ASCIIToUTF16(nacl_enabled)); 205 206 AddPair(list, ASCIIToUTF16("Microphone"), 207 ASCIIToUTF16( 208 HotwordServiceFactory::IsMicrophoneAvailable() ? "Yes" : "No")); 209 210 std::string audio_capture = "No"; 211 if (profile_->GetPrefs()->GetBoolean(prefs::kAudioCaptureAllowed)) 212 audio_capture = "Yes"; 213 AddPair(list, ASCIIToUTF16("Audio Capture Allowed"), 214 ASCIIToUTF16(audio_capture)); 215 216 AddLineBreak(list); 217 } 218 219 // Adds information regarding languages to the list. 220 void AddLanguageInfo(base::ListValue* list) { 221 std::string locale = 222 #if defined(OS_CHROMEOS) 223 // On ChromeOS locale is per-profile. 224 profile_->GetPrefs()->GetString(prefs::kApplicationLocale); 225 #else 226 g_browser_process->GetApplicationLocale(); 227 #endif 228 AddPair(list, ASCIIToUTF16("Current Language"), ASCIIToUTF16(locale)); 229 230 AddPair(list, ASCIIToUTF16("Hotword Previous Language"), 231 ASCIIToUTF16(profile_->GetPrefs()->GetString( 232 prefs::kHotwordPreviousLanguage))); 233 234 AddLineBreak(list); 235 } 236 237 // Adds information specific to the hotword configuration to the list. 238 void AddHotwordInfo(base::ListValue* list) { 239 std::string search_enabled = "No"; 240 if (profile_->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled)) 241 search_enabled = "Yes"; 242 AddPair(list, ASCIIToUTF16("Hotword Search Enabled"), 243 ASCIIToUTF16(search_enabled)); 244 245 std::string audio_logging_enabled = "No"; 246 HotwordService* hotword_service = 247 HotwordServiceFactory::GetForProfile(profile_); 248 if (hotword_service && hotword_service->IsOptedIntoAudioLogging()) 249 audio_logging_enabled = "Yes"; 250 AddPair(list, ASCIIToUTF16("Hotword Audio Logging Enabled"), 251 ASCIIToUTF16(audio_logging_enabled)); 252 253 std::string group = base::FieldTrialList::FindFullName( 254 hotword_internal::kHotwordFieldTrialName); 255 AddPair(list, ASCIIToUTF16("Field trial"), ASCIIToUTF16(group)); 256 257 AddLineBreak(list); 258 } 259 260 // Adds information specific to the hotword extension to the list. 261 void AddHotwordExtensionInfo(base::ListValue* list) { 262 std::string version("undefined"); 263 std::string id("undefined"); 264 base::FilePath path; 265 266 extensions::ExtensionSystem* extension_system = 267 extensions::ExtensionSystem::Get(profile_); 268 if (extension_system) { 269 ExtensionService* extension_service = 270 extension_system->extension_service(); 271 const extensions::Extension* extension = 272 extension_service->GetExtensionById( 273 extension_misc::kHotwordExtensionId, true); 274 if (extension) { 275 id = extension->id(); 276 version = extension->VersionString(); 277 path = extension->path(); 278 } 279 } 280 AddPair(list, ASCIIToUTF16("Extension Id"), 281 ASCIIToUTF16(id)); 282 AddPair(list, ASCIIToUTF16("Extension Version"), 283 ASCIIToUTF16(version)); 284 AddPair(list, ASCIIToUTF16("Extension Path"), 285 path.empty() ? ASCIIToUTF16("undefined") : path.LossyDisplayName()); 286 287 extensions::ExtensionPrefs* extension_prefs = 288 extensions::ExtensionPrefs::Get(profile_); 289 int pref_state = -1; 290 extension_prefs->ReadPrefAsInteger(extension_misc::kHotwordExtensionId, 291 "state", &pref_state); 292 std::string state; 293 switch (pref_state) { 294 case extensions::Extension::DISABLED: 295 state = "DISABLED"; 296 break; 297 case extensions::Extension::ENABLED: 298 state = "ENABLED"; 299 break; 300 case extensions::Extension::EXTERNAL_EXTENSION_UNINSTALLED: 301 state = "EXTERNAL_EXTENSION_UNINSTALLED"; 302 break; 303 default: 304 state = "undefined"; 305 } 306 307 AddPair(list, ASCIIToUTF16("Extension State"), ASCIIToUTF16(state)); 308 309 AddLineBreak(list); 310 } 311 312 // Adds information specific to voice search in the app launcher to the list. 313 void AddAppListInfo(base::ListValue* list) { 314 #if defined (ENABLE_APP_LIST) 315 std::string state = "No Start Page Service"; 316 app_list::StartPageService* start_page_service = 317 app_list::StartPageService::Get(profile_); 318 if (start_page_service) { 319 app_list::SpeechRecognitionState speech_state = 320 start_page_service->state(); 321 switch (speech_state) { 322 case app_list::SPEECH_RECOGNITION_OFF: 323 state = "SPEECH_RECOGNITION_OFF"; 324 break; 325 case app_list::SPEECH_RECOGNITION_READY: 326 state = "SPEECH_RECOGNITION_READY"; 327 break; 328 case app_list::SPEECH_RECOGNITION_HOTWORD_LISTENING: 329 state = "SPEECH_RECOGNITION_HOTWORD_LISTENING"; 330 break; 331 case app_list::SPEECH_RECOGNITION_RECOGNIZING: 332 state = "SPEECH_RECOGNITION_RECOGNIZING"; 333 break; 334 case app_list::SPEECH_RECOGNITION_IN_SPEECH: 335 state = "SPEECH_RECOGNITION_IN_SPEECH"; 336 break; 337 case app_list::SPEECH_RECOGNITION_STOPPING: 338 state = "SPEECH_RECOGNITION_STOPPING"; 339 break; 340 case app_list::SPEECH_RECOGNITION_NETWORK_ERROR: 341 state = "SPEECH_RECOGNITION_NETWORK_ERROR"; 342 break; 343 default: 344 state = "undefined"; 345 } 346 } 347 AddPair(list, ASCIIToUTF16("Start Page State"), ASCIIToUTF16(state)); 348 #endif 349 } 350 351 Profile* profile_; 352 353 DISALLOW_COPY_AND_ASSIGN(VoiceSearchDomHandler); 354 }; 355 356 } // namespace 357 358 /////////////////////////////////////////////////////////////////////////////// 359 // 360 // VoiceSearchUI 361 // 362 /////////////////////////////////////////////////////////////////////////////// 363 364 VoiceSearchUI::VoiceSearchUI(content::WebUI* web_ui) 365 : content::WebUIController(web_ui) { 366 Profile* profile = Profile::FromWebUI(web_ui); 367 web_ui->AddMessageHandler(new VoiceSearchDomHandler(profile)); 368 369 // Set up the about:voicesearch source. 370 content::WebUIDataSource::Add(profile, CreateVoiceSearchUiHtmlSource()); 371 } 372 373 VoiceSearchUI::~VoiceSearchUI() {} 374