Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / app_list / start_page_service.cc
1 // Copyright 2013 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/app_list/start_page_service.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/memory/singleton.h"
12 #include "base/metrics/user_metrics.h"
13 #include "base/prefs/pref_service.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/media/media_stream_infobar_delegate.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/search/hotword_service.h"
19 #include "chrome/browser/search/hotword_service_factory.h"
20 #include "chrome/browser/ui/app_list/recommended_apps.h"
21 #include "chrome/browser/ui/app_list/speech_recognizer.h"
22 #include "chrome/browser/ui/app_list/start_page_observer.h"
23 #include "chrome/browser/ui/app_list/start_page_service_factory.h"
24 #include "chrome/common/chrome_switches.h"
25 #include "chrome/common/pref_names.h"
26 #include "chrome/common/url_constants.h"
27 #include "content/public/browser/notification_details.h"
28 #include "content/public/browser/notification_observer.h"
29 #include "content/public/browser/notification_registrar.h"
30 #include "content/public/browser/notification_service.h"
31 #include "content/public/browser/notification_source.h"
32 #include "content/public/browser/web_contents.h"
33 #include "content/public/browser/web_contents_delegate.h"
34 #include "extensions/browser/extension_system_provider.h"
35 #include "extensions/browser/extensions_browser_client.h"
36 #include "extensions/common/extension.h"
37 #include "ui/app_list/app_list_switches.h"
38
39 using base::RecordAction;
40 using base::UserMetricsAction;
41
42 namespace app_list {
43
44 namespace {
45
46 bool InSpeechRecognition(SpeechRecognitionState state) {
47   return state == SPEECH_RECOGNITION_RECOGNIZING ||
48       state == SPEECH_RECOGNITION_IN_SPEECH;
49 }
50
51 }
52
53 class StartPageService::ProfileDestroyObserver
54     : public content::NotificationObserver {
55  public:
56   explicit ProfileDestroyObserver(StartPageService* service)
57       : service_(service) {
58     registrar_.Add(this,
59                    chrome::NOTIFICATION_PROFILE_DESTROYED,
60                    content::Source<Profile>(service_->profile()));
61   }
62   ~ProfileDestroyObserver() override {}
63
64  private:
65   // content::NotificationObserver
66   void Observe(int type,
67                const content::NotificationSource& source,
68                const content::NotificationDetails& details) override {
69     DCHECK_EQ(chrome::NOTIFICATION_PROFILE_DESTROYED, type);
70     DCHECK_EQ(service_->profile(), content::Source<Profile>(source).ptr());
71     service_->Shutdown();
72   }
73
74   StartPageService* service_;  // Owner of this class.
75   content::NotificationRegistrar registrar_;
76
77   DISALLOW_COPY_AND_ASSIGN(ProfileDestroyObserver);
78 };
79
80 class StartPageService::StartPageWebContentsDelegate
81     : public content::WebContentsDelegate {
82  public:
83   StartPageWebContentsDelegate() {}
84   ~StartPageWebContentsDelegate() override {}
85
86   void RequestMediaAccessPermission(
87       content::WebContents* web_contents,
88       const content::MediaStreamRequest& request,
89       const content::MediaResponseCallback& callback) override {
90     if (MediaStreamInfoBarDelegate::Create(web_contents, request, callback))
91       NOTREACHED() << "Media stream not allowed for WebUI";
92   }
93
94   bool CheckMediaAccessPermission(content::WebContents* web_contents,
95                                   const GURL& security_origin,
96                                   content::MediaStreamType type) override {
97     return MediaCaptureDevicesDispatcher::GetInstance()
98         ->CheckMediaAccessPermission(web_contents, security_origin, type);
99   }
100
101  private:
102   DISALLOW_COPY_AND_ASSIGN(StartPageWebContentsDelegate);
103 };
104
105 // static
106 StartPageService* StartPageService::Get(Profile* profile) {
107   return StartPageServiceFactory::GetForProfile(profile);
108 }
109
110 StartPageService::StartPageService(Profile* profile)
111     : profile_(profile),
112       profile_destroy_observer_(new ProfileDestroyObserver(this)),
113       recommended_apps_(new RecommendedApps(profile)),
114       state_(app_list::SPEECH_RECOGNITION_OFF),
115       speech_button_toggled_manually_(false),
116       speech_result_obtained_(false),
117       webui_finished_loading_(false),
118       weak_factory_(this) {
119   // If experimental hotwording is enabled, then we're always "ready".
120   // Transitioning into the "hotword recognizing" state is handled by the
121   // hotword extension.
122   if (HotwordService::IsExperimentalHotwordingEnabled())
123     state_ = app_list::SPEECH_RECOGNITION_READY;
124
125   if (app_list::switches::IsExperimentalAppListEnabled())
126     LoadContents();
127 }
128
129 StartPageService::~StartPageService() {}
130
131 void StartPageService::AddObserver(StartPageObserver* observer) {
132   observers_.AddObserver(observer);
133 }
134
135 void StartPageService::RemoveObserver(StartPageObserver* observer) {
136   observers_.RemoveObserver(observer);
137 }
138
139 void StartPageService::AppListShown() {
140   if (!contents_) {
141     LoadContents();
142   } else if (contents_->GetWebUI() &&
143              !HotwordService::IsExperimentalHotwordingEnabled()) {
144     // If experimental hotwording is enabled, don't call onAppListShown.
145     // onAppListShown() initializes the web speech API, which is not used with
146     // experimental hotwording.
147     contents_->GetWebUI()->CallJavascriptFunction(
148         "appList.startPage.onAppListShown",
149         base::FundamentalValue(HotwordEnabled()));
150   }
151 }
152
153 void StartPageService::AppListHidden() {
154   if (contents_->GetWebUI()) {
155     contents_->GetWebUI()->CallJavascriptFunction(
156         "appList.startPage.onAppListHidden");
157   }
158   if (!app_list::switches::IsExperimentalAppListEnabled())
159     UnloadContents();
160
161   if (HotwordService::IsExperimentalHotwordingEnabled() &&
162       speech_recognizer_) {
163     speech_recognizer_->Stop();
164   }
165 }
166
167 void StartPageService::ToggleSpeechRecognition() {
168   DCHECK(contents_);
169   speech_button_toggled_manually_ = true;
170   if (!contents_->GetWebUI())
171     return;
172
173   if (!webui_finished_loading_) {
174     pending_webui_callbacks_.push_back(
175         base::Bind(&StartPageService::ToggleSpeechRecognition,
176                    base::Unretained(this)));
177     return;
178   }
179
180   if (HotwordService::IsExperimentalHotwordingEnabled()) {
181     if (!speech_recognizer_) {
182       std::string profile_locale;
183 #if defined(OS_CHROMEOS)
184       profile_locale = profile_->GetPrefs()->GetString(
185           prefs::kApplicationLocale);
186 #endif
187       if (profile_locale.empty())
188         profile_locale = g_browser_process->GetApplicationLocale();
189
190       speech_recognizer_.reset(
191           new SpeechRecognizer(weak_factory_.GetWeakPtr(),
192                                profile_->GetRequestContext(),
193                                profile_locale));
194     }
195
196     speech_recognizer_->Start();
197     return;
198   }
199
200   contents_->GetWebUI()->CallJavascriptFunction(
201       "appList.startPage.toggleSpeechRecognition");
202 }
203
204 bool StartPageService::HotwordEnabled() {
205   if (HotwordService::IsExperimentalHotwordingEnabled()) {
206     auto prefs = profile_->GetPrefs();
207     return HotwordServiceFactory::IsServiceAvailable(profile_) &&
208         (prefs->GetBoolean(prefs::kHotwordSearchEnabled) ||
209          prefs->GetBoolean(prefs::kHotwordAlwaysOnSearchEnabled));
210   }
211 #if defined(OS_CHROMEOS)
212   return HotwordServiceFactory::IsServiceAvailable(profile_) &&
213       profile_->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled);
214 #else
215   return false;
216 #endif
217 }
218
219 content::WebContents* StartPageService::GetStartPageContents() {
220   return app_list::switches::IsExperimentalAppListEnabled() ? contents_.get()
221                                                             : NULL;
222 }
223
224 content::WebContents* StartPageService::GetSpeechRecognitionContents() {
225   if (app_list::switches::IsVoiceSearchEnabled()) {
226     if (!contents_)
227       LoadContents();
228     return contents_.get();
229   }
230   return NULL;
231 }
232
233 void StartPageService::OnSpeechResult(
234     const base::string16& query, bool is_final) {
235   if (is_final) {
236     speech_result_obtained_ = true;
237     RecordAction(UserMetricsAction("AppList_SearchedBySpeech"));
238   }
239   FOR_EACH_OBSERVER(StartPageObserver,
240                     observers_,
241                     OnSpeechResult(query, is_final));
242 }
243
244 void StartPageService::OnSpeechSoundLevelChanged(int16_t level) {
245   FOR_EACH_OBSERVER(StartPageObserver,
246                     observers_,
247                     OnSpeechSoundLevelChanged(level));
248 }
249
250 void StartPageService::OnSpeechRecognitionStateChanged(
251     SpeechRecognitionState new_state) {
252
253   if (HotwordService::IsExperimentalHotwordingEnabled() &&
254       new_state == SPEECH_RECOGNITION_READY &&
255       speech_recognizer_) {
256     speech_recognizer_->Stop();
257   }
258
259   if (!InSpeechRecognition(state_) && InSpeechRecognition(new_state)) {
260     if (!speech_button_toggled_manually_ &&
261         state_ == SPEECH_RECOGNITION_HOTWORD_LISTENING) {
262       RecordAction(UserMetricsAction("AppList_HotwordRecognized"));
263     } else {
264       RecordAction(UserMetricsAction("AppList_VoiceSearchStartedManually"));
265     }
266   } else if (InSpeechRecognition(state_) && !InSpeechRecognition(new_state) &&
267              !speech_result_obtained_) {
268     RecordAction(UserMetricsAction("AppList_VoiceSearchCanceled"));
269   }
270   speech_button_toggled_manually_ = false;
271   speech_result_obtained_ = false;
272   state_ = new_state;
273   FOR_EACH_OBSERVER(StartPageObserver,
274                     observers_,
275                     OnSpeechRecognitionStateChanged(new_state));
276 }
277
278 content::WebContents* StartPageService::GetSpeechContents() {
279   return GetSpeechRecognitionContents();
280 }
281
282 void StartPageService::Shutdown() {
283   UnloadContents();
284 }
285
286 void StartPageService::WebUILoaded() {
287   // There's a race condition between the WebUI loading, and calling its JS
288   // functions. Specifically, calling LoadContents() doesn't mean that the page
289   // has loaded, but several code paths make this assumption. This function
290   // allows us to defer calling JS functions until after the page has finished
291   // loading.
292   webui_finished_loading_ = true;
293   for (const auto& cb : pending_webui_callbacks_)
294     cb.Run();
295   pending_webui_callbacks_.clear();
296 }
297
298 void StartPageService::LoadContents() {
299   contents_.reset(content::WebContents::Create(
300       content::WebContents::CreateParams(profile_)));
301   contents_delegate_.reset(new StartPageWebContentsDelegate());
302   contents_->SetDelegate(contents_delegate_.get());
303
304   GURL url(chrome::kChromeUIAppListStartPageURL);
305   CommandLine* command_line = CommandLine::ForCurrentProcess();
306   if (command_line->HasSwitch(::switches::kAppListStartPageURL)) {
307     url = GURL(
308         command_line->GetSwitchValueASCII(::switches::kAppListStartPageURL));
309   }
310
311   contents_->GetController().LoadURL(
312       url,
313       content::Referrer(),
314       ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
315       std::string());
316 }
317
318 void StartPageService::UnloadContents() {
319   contents_.reset();
320   webui_finished_loading_ = false;
321 }
322
323 }  // namespace app_list