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.
5 #include "chrome/browser/ui/webui/voice_search_ui.h"
9 #include "base/command_line.h"
10 #include "base/files/file_enumerator.h"
11 #include "base/memory/weak_ptr.h"
12 #include "base/metrics/field_trial.h"
13 #include "base/path_service.h"
14 #include "base/prefs/pref_service.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/extensions/extension_service.h"
19 #include "chrome/browser/plugins/plugin_prefs.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/search/hotword_service.h"
22 #include "chrome/browser/search/hotword_service_factory.h"
23 #include "chrome/browser/ui/app_list/start_page_service.h"
24 #include "chrome/browser/ui/webui/version_handler.h"
25 #include "chrome/common/chrome_content_client.h"
26 #include "chrome/common/chrome_paths.h"
27 #include "chrome/common/chrome_version_info.h"
28 #include "chrome/common/extensions/extension_constants.h"
29 #include "chrome/common/pref_names.h"
30 #include "chrome/common/url_constants.h"
31 #include "chrome/grit/chromium_strings.h"
32 #include "chrome/grit/generated_resources.h"
33 #include "chrome/grit/google_chrome_strings.h"
34 #include "content/public/browser/browser_thread.h"
35 #include "content/public/browser/plugin_service.h"
36 #include "content/public/browser/url_data_source.h"
37 #include "content/public/browser/web_ui.h"
38 #include "content/public/browser/web_ui_data_source.h"
39 #include "content/public/browser/web_ui_message_handler.h"
40 #include "content/public/common/user_agent.h"
41 #include "extensions/browser/extension_prefs.h"
42 #include "extensions/browser/extension_system.h"
43 #include "extensions/common/extension.h"
44 #include "grit/browser_resources.h"
45 #include "ui/base/l10n/l10n_util.h"
46 #include "v8/include/v8.h"
49 #include "base/win/windows_version.h"
52 using base::ASCIIToUTF16
;
53 using content::WebUIMessageHandler
;
57 content::WebUIDataSource
* CreateVoiceSearchUiHtmlSource() {
58 content::WebUIDataSource
* html_source
=
59 content::WebUIDataSource::Create(chrome::kChromeUIVoiceSearchHost
);
61 html_source
->AddLocalizedString("loadingMessage",
62 IDS_VOICESEARCH_LOADING_MESSAGE
);
63 html_source
->AddLocalizedString("voiceSearchLongTitle",
64 IDS_VOICESEARCH_TITLE_MESSAGE
);
66 html_source
->SetJsonPath("strings.js");
67 html_source
->AddResourcePath("about_voicesearch.js",
68 IDR_ABOUT_VOICESEARCH_JS
);
69 html_source
->SetDefaultResource(IDR_ABOUT_VOICESEARCH_HTML
);
73 // Helper functions for collecting a list of key-value pairs that will
75 void AddPair16(base::ListValue
* list
,
76 const base::string16
& key
,
77 const base::string16
& value
) {
78 scoped_ptr
<base::DictionaryValue
> results(new base::DictionaryValue());
79 results
->SetString("key", key
);
80 results
->SetString("value", value
);
81 list
->Append(results
.release());
84 void AddPair(base::ListValue
* list
,
85 const base::StringPiece
& key
,
86 const base::StringPiece
& value
) {
87 AddPair16(list
, UTF8ToUTF16(key
), UTF8ToUTF16(value
));
90 // Generate an empty data-pair which acts as a line break.
91 void AddLineBreak(base::ListValue
* list
) {
92 AddPair(list
, "", "");
95 void AddSharedModulePlatformsOnFileThread(base::ListValue
* list
,
96 const base::FilePath
& path
,
97 base::Closure callback
) {
98 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
101 // Display available platforms for shared module.
102 base::FilePath platforms_path
= path
.AppendASCII("_platform_specific");
103 base::FileEnumerator
enumerator(
104 platforms_path
, false, base::FileEnumerator::DIRECTORIES
);
105 base::string16 files
;
106 for (base::FilePath name
= enumerator
.Next();
108 name
= enumerator
.Next()) {
109 files
+= name
.BaseName().LossyDisplayName() + ASCIIToUTF16(" ");
112 ASCIIToUTF16("Shared Module Platforms"),
113 files
.empty() ? ASCIIToUTF16("undefined") : files
);
117 content::BrowserThread::PostTask(content::BrowserThread::UI
,
122 ////////////////////////////////////////////////////////////////////////////////
124 // VoiceSearchDomHandler
126 ////////////////////////////////////////////////////////////////////////////////
128 // The handler for Javascript messages for the about:flags page.
129 class VoiceSearchDomHandler
: public WebUIMessageHandler
{
131 explicit VoiceSearchDomHandler(Profile
* profile
)
133 weak_factory_(this) {}
135 ~VoiceSearchDomHandler() override
{}
137 // WebUIMessageHandler implementation.
138 void RegisterMessages() override
{
139 web_ui()->RegisterMessageCallback(
140 "requestVoiceSearchInfo",
141 base::Bind(&VoiceSearchDomHandler::HandleRequestVoiceSearchInfo
,
142 base::Unretained(this)));
146 // Callback for the "requestVoiceSearchInfo" message. No arguments.
147 void HandleRequestVoiceSearchInfo(const base::ListValue
* args
) {
148 PopulatePageInformation();
151 void ReturnVoiceSearchInfo(scoped_ptr
<base::ListValue
> info
) {
152 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
154 base::DictionaryValue voiceSearchInfo
;
155 voiceSearchInfo
.Set("voiceSearchInfo", info
.release());
156 web_ui()->CallJavascriptFunction("returnVoiceSearchInfo", voiceSearchInfo
);
159 // Fill in the data to be displayed on the page.
160 void PopulatePageInformation() {
161 // Store Key-Value pairs of about-information.
162 scoped_ptr
<base::ListValue
> list(new base::ListValue());
164 // Populate information.
165 AddOperatingSystemInfo(list
.get());
166 AddAudioInfo(list
.get());
167 AddLanguageInfo(list
.get());
168 AddHotwordInfo(list
.get());
169 AddAppListInfo(list
.get());
171 std::string extension_id
= extension_misc::kHotwordExtensionId
;
172 HotwordService
* hotword_service
=
173 HotwordServiceFactory::GetForProfile(profile_
);
174 if (hotword_service
&& hotword_service
->IsExperimentalHotwordingEnabled())
175 extension_id
= extension_misc::kHotwordNewExtensionId
;
176 AddExtensionInfo(extension_id
, "Extension", list
.get());
178 AddExtensionInfo(extension_misc::kHotwordSharedModuleId
,
183 extensions::ExtensionSystem
* extension_system
=
184 extensions::ExtensionSystem::Get(profile_
);
185 if (extension_system
) {
186 ExtensionService
* extension_service
=
187 extension_system
->extension_service();
188 const extensions::Extension
* extension
=
189 extension_service
->GetExtensionById(
190 extension_misc::kHotwordSharedModuleId
, true);
192 path
= extension
->path();
194 base::ListValue
* raw_list
= list
.get();
195 content::BrowserThread::PostTask(
196 content::BrowserThread::FILE,
199 &AddSharedModulePlatformsOnFileThread
,
202 base::Bind(&VoiceSearchDomHandler::ReturnVoiceSearchInfo
,
203 weak_factory_
.GetWeakPtr(),
204 base::Passed(list
.Pass()))));
207 // Adds information regarding the system and chrome version info to list.
208 void AddOperatingSystemInfo(base::ListValue
* list
) {
209 // Obtain the Chrome version info.
210 chrome::VersionInfo version_info
;
212 l10n_util::GetStringUTF8(IDS_PRODUCT_NAME
),
213 version_info
.Version() + " (" +
214 chrome::VersionInfo::GetVersionStringModifier() + ")");
216 // OS version information.
217 std::string os_label
= version_info
.OSType();
219 base::win::OSInfo
* os
= base::win::OSInfo::GetInstance();
220 switch (os
->version()) {
221 case base::win::VERSION_XP
:
224 case base::win::VERSION_SERVER_2003
:
225 os_label
+= " Server 2003 or XP Pro 64 bit";
227 case base::win::VERSION_VISTA
:
228 os_label
+= " Vista or Server 2008";
230 case base::win::VERSION_WIN7
:
231 os_label
+= " 7 or Server 2008 R2";
233 case base::win::VERSION_WIN8
:
234 os_label
+= " 8 or Server 2012";
237 os_label
+= " UNKNOWN";
240 os_label
+= " SP" + base::IntToString(os
->service_pack().major
);
242 if (os
->service_pack().minor
> 0)
243 os_label
+= "." + base::IntToString(os
->service_pack().minor
);
245 if (os
->architecture() == base::win::OSInfo::X64_ARCHITECTURE
)
246 os_label
+= " 64 bit";
248 AddPair(list
, l10n_util::GetStringUTF8(IDS_ABOUT_VERSION_OS
), os_label
);
253 // Adds information regarding audio to the list.
254 void AddAudioInfo(base::ListValue
* list
) {
255 // NaCl and its associated functions are not available on most mobile
256 // platforms. ENABLE_EXTENSIONS covers those platforms and hey would not
257 // allow Hotwording anyways since it is an extension.
258 std::string nacl_enabled
= "not available";
259 #if defined(ENABLE_EXTENSIONS)
261 // Determine if NaCl is available.
263 if (PathService::Get(chrome::FILE_NACL_PLUGIN
, &path
)) {
264 content::WebPluginInfo info
;
265 PluginPrefs
* plugin_prefs
= PluginPrefs::GetForProfile(profile_
).get();
266 if (content::PluginService::GetInstance()->GetPluginInfoByPath(path
,
268 plugin_prefs
->IsPluginEnabled(info
)) {
269 nacl_enabled
= "Yes";
274 AddPair(list
, "NaCl Enabled", nacl_enabled
);
278 HotwordServiceFactory::IsMicrophoneAvailable() ? "Yes" : "No");
280 std::string audio_capture
= "No";
281 if (profile_
->GetPrefs()->GetBoolean(prefs::kAudioCaptureAllowed
))
282 audio_capture
= "Yes";
283 AddPair(list
, "Audio Capture Allowed", audio_capture
);
288 // Adds information regarding languages to the list.
289 void AddLanguageInfo(base::ListValue
* list
) {
291 #if defined(OS_CHROMEOS)
292 // On ChromeOS locale is per-profile.
293 profile_
->GetPrefs()->GetString(prefs::kApplicationLocale
);
295 g_browser_process
->GetApplicationLocale();
297 AddPair(list
, "Current Language", locale
);
300 "Hotword Previous Language",
301 profile_
->GetPrefs()->GetString(prefs::kHotwordPreviousLanguage
));
306 // Adds information specific to the hotword configuration to the list.
307 void AddHotwordInfo(base::ListValue
* list
) {
308 std::string search_enabled
= "No";
309 if (profile_
->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled
))
310 search_enabled
= "Yes";
311 AddPair(list
, "Hotword Search Enabled", search_enabled
);
313 std::string always_on_search_enabled
= "No";
314 if (profile_
->GetPrefs()->GetBoolean(prefs::kHotwordAlwaysOnSearchEnabled
))
315 always_on_search_enabled
= "Yes";
316 AddPair(list
, "Always-on Hotword Search Enabled", always_on_search_enabled
);
318 std::string audio_logging_enabled
= "No";
319 HotwordService
* hotword_service
=
320 HotwordServiceFactory::GetForProfile(profile_
);
321 if (hotword_service
&& hotword_service
->IsOptedIntoAudioLogging())
322 audio_logging_enabled
= "Yes";
323 AddPair(list
, "Hotword Audio Logging Enabled", audio_logging_enabled
);
325 std::string group
= base::FieldTrialList::FindFullName(
326 hotword_internal::kHotwordFieldTrialName
);
327 AddPair(list
, "Field trial", group
);
329 std::string new_hotwording_enabled
= "No";
330 if (hotword_service
&& hotword_service
->IsExperimentalHotwordingEnabled())
331 new_hotwording_enabled
= "Yes";
332 AddPair(list
, "New Hotwording Enabled", new_hotwording_enabled
);
337 // Adds information specific to an extension to the list.
338 void AddExtensionInfo(const std::string
& extension_id
,
339 const std::string
& name_prefix
,
340 base::ListValue
* list
) {
341 DCHECK(!name_prefix
.empty());
342 std::string
version("undefined");
343 std::string
id("undefined");
346 extensions::ExtensionSystem
* extension_system
=
347 extensions::ExtensionSystem::Get(profile_
);
348 if (extension_system
) {
349 ExtensionService
* extension_service
=
350 extension_system
->extension_service();
351 const extensions::Extension
* extension
=
352 extension_service
->GetExtensionById(extension_id
, true);
354 id
= extension
->id();
355 version
= extension
->VersionString();
356 path
= extension
->path();
359 AddPair(list
, name_prefix
+ " Id", id
);
360 AddPair(list
, name_prefix
+ " Version", version
);
362 ASCIIToUTF16(name_prefix
+ " Path"),
364 ASCIIToUTF16("undefined") : path
.LossyDisplayName());
366 extensions::ExtensionPrefs
* extension_prefs
=
367 extensions::ExtensionPrefs::Get(profile_
);
369 extension_prefs
->ReadPrefAsInteger(extension_id
, "state", &pref_state
);
371 switch (pref_state
) {
372 case extensions::Extension::DISABLED
:
375 case extensions::Extension::ENABLED
:
378 case extensions::Extension::EXTERNAL_EXTENSION_UNINSTALLED
:
379 state
= "EXTERNAL_EXTENSION_UNINSTALLED";
385 AddPair(list
, name_prefix
+ " State", state
);
390 // Adds information specific to voice search in the app launcher to the list.
391 void AddAppListInfo(base::ListValue
* list
) {
392 #if defined (ENABLE_APP_LIST)
393 std::string state
= "No Start Page Service";
394 app_list::StartPageService
* start_page_service
=
395 app_list::StartPageService::Get(profile_
);
396 if (start_page_service
) {
397 app_list::SpeechRecognitionState speech_state
=
398 start_page_service
->state();
399 switch (speech_state
) {
400 case app_list::SPEECH_RECOGNITION_OFF
:
401 state
= "SPEECH_RECOGNITION_OFF";
403 case app_list::SPEECH_RECOGNITION_READY
:
404 state
= "SPEECH_RECOGNITION_READY";
406 case app_list::SPEECH_RECOGNITION_HOTWORD_LISTENING
:
407 state
= "SPEECH_RECOGNITION_HOTWORD_LISTENING";
409 case app_list::SPEECH_RECOGNITION_RECOGNIZING
:
410 state
= "SPEECH_RECOGNITION_RECOGNIZING";
412 case app_list::SPEECH_RECOGNITION_IN_SPEECH
:
413 state
= "SPEECH_RECOGNITION_IN_SPEECH";
415 case app_list::SPEECH_RECOGNITION_STOPPING
:
416 state
= "SPEECH_RECOGNITION_STOPPING";
418 case app_list::SPEECH_RECOGNITION_NETWORK_ERROR
:
419 state
= "SPEECH_RECOGNITION_NETWORK_ERROR";
425 AddPair(list
, "Start Page State", state
);
431 base::WeakPtrFactory
<VoiceSearchDomHandler
> weak_factory_
;
433 DISALLOW_COPY_AND_ASSIGN(VoiceSearchDomHandler
);
438 ///////////////////////////////////////////////////////////////////////////////
442 ///////////////////////////////////////////////////////////////////////////////
444 VoiceSearchUI::VoiceSearchUI(content::WebUI
* web_ui
)
445 : content::WebUIController(web_ui
) {
446 Profile
* profile
= Profile::FromWebUI(web_ui
);
447 web_ui
->AddMessageHandler(new VoiceSearchDomHandler(profile
));
449 // Set up the about:voicesearch source.
450 content::WebUIDataSource::Add(profile
, CreateVoiceSearchUiHtmlSource());
453 VoiceSearchUI::~VoiceSearchUI() {}