Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / app_list / start_page_service.cc
index 9e689a6..09c66ca 100644 (file)
@@ -6,17 +6,23 @@
 
 #include <string>
 
+#include "base/bind.h"
 #include "base/command_line.h"
 #include "base/memory/singleton.h"
 #include "base/metrics/user_metrics.h"
+#include "base/prefs/pref_service.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/media/media_stream_infobar_delegate.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/app_list/app_list_service.h"
+#include "chrome/browser/search/hotword_service.h"
+#include "chrome/browser/search/hotword_service_factory.h"
 #include "chrome/browser/ui/app_list/recommended_apps.h"
+#include "chrome/browser/ui/app_list/speech_recognizer.h"
 #include "chrome/browser/ui/app_list/start_page_observer.h"
 #include "chrome/browser/ui/app_list/start_page_service_factory.h"
 #include "chrome/common/chrome_switches.h"
+#include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
 #include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_observer.h"
@@ -53,13 +59,13 @@ class StartPageService::ProfileDestroyObserver
                    chrome::NOTIFICATION_PROFILE_DESTROYED,
                    content::Source<Profile>(service_->profile()));
   }
-  virtual ~ProfileDestroyObserver() {}
+  ~ProfileDestroyObserver() override {}
 
  private:
   // content::NotificationObserver
-  virtual void Observe(int type,
-                       const content::NotificationSource& source,
-                       const content::NotificationDetails& details) OVERRIDE {
+  void Observe(int type,
+               const content::NotificationSource& source,
+               const content::NotificationDetails& details) override {
     DCHECK_EQ(chrome::NOTIFICATION_PROFILE_DESTROYED, type);
     DCHECK_EQ(service_->profile(), content::Source<Profile>(source).ptr());
     service_->Shutdown();
@@ -75,16 +81,23 @@ class StartPageService::StartPageWebContentsDelegate
     : public content::WebContentsDelegate {
  public:
   StartPageWebContentsDelegate() {}
-  virtual ~StartPageWebContentsDelegate() {}
+  ~StartPageWebContentsDelegate() override {}
 
-  virtual void RequestMediaAccessPermission(
+  void RequestMediaAccessPermission(
       content::WebContents* web_contents,
       const content::MediaStreamRequest& request,
-      const content::MediaResponseCallback& callback) OVERRIDE {
+      const content::MediaResponseCallback& callback) override {
     if (MediaStreamInfoBarDelegate::Create(web_contents, request, callback))
       NOTREACHED() << "Media stream not allowed for WebUI";
   }
 
+  bool CheckMediaAccessPermission(content::WebContents* web_contents,
+                                  const GURL& security_origin,
+                                  content::MediaStreamType type) override {
+    return MediaCaptureDevicesDispatcher::GetInstance()
+        ->CheckMediaAccessPermission(web_contents, security_origin, type);
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(StartPageWebContentsDelegate);
 };
@@ -100,32 +113,17 @@ StartPageService::StartPageService(Profile* profile)
       recommended_apps_(new RecommendedApps(profile)),
       state_(app_list::SPEECH_RECOGNITION_OFF),
       speech_button_toggled_manually_(false),
-      speech_result_obtained_(false) {
-#if defined(OS_CHROMEOS)
-  // Updates the default state to hotword listening, because this is
-  // the default behavior. This will be updated when the page is loaded and
-  // the nacl module is loaded.
-  if (app_list::switches::IsVoiceSearchEnabled())
-    state_ = app_list::SPEECH_RECOGNITION_HOTWORD_LISTENING;
-#endif
-
-  contents_.reset(content::WebContents::Create(
-      content::WebContents::CreateParams(profile_)));
-  contents_delegate_.reset(new StartPageWebContentsDelegate());
-  contents_->SetDelegate(contents_delegate_.get());
-
-  GURL url(chrome::kChromeUIAppListStartPageURL);
-  CommandLine* command_line = CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(::switches::kAppListStartPageURL)) {
-    url = GURL(
-        command_line->GetSwitchValueASCII(::switches::kAppListStartPageURL));
-  }
-
-  contents_->GetController().LoadURL(
-      url,
-      content::Referrer(),
-      content::PAGE_TRANSITION_AUTO_TOPLEVEL,
-      std::string());
+      speech_result_obtained_(false),
+      webui_finished_loading_(false),
+      weak_factory_(this) {
+  // If experimental hotwording is enabled, then we're always "ready".
+  // Transitioning into the "hotword recognizing" state is handled by the
+  // hotword extension.
+  if (HotwordService::IsExperimentalHotwordingEnabled())
+    state_ = app_list::SPEECH_RECOGNITION_READY;
+
+  if (app_list::switches::IsExperimentalAppListEnabled())
+    LoadContents();
 }
 
 StartPageService::~StartPageService() {}
@@ -138,19 +136,98 @@ void StartPageService::RemoveObserver(StartPageObserver* observer) {
   observers_.RemoveObserver(observer);
 }
 
+void StartPageService::AppListShown() {
+  if (!contents_) {
+    LoadContents();
+  } else if (contents_->GetWebUI() &&
+             !HotwordService::IsExperimentalHotwordingEnabled()) {
+    // If experimental hotwording is enabled, don't call onAppListShown.
+    // onAppListShown() initializes the web speech API, which is not used with
+    // experimental hotwording.
+    contents_->GetWebUI()->CallJavascriptFunction(
+        "appList.startPage.onAppListShown",
+        base::FundamentalValue(HotwordEnabled()));
+  }
+}
+
+void StartPageService::AppListHidden() {
+  if (contents_->GetWebUI()) {
+    contents_->GetWebUI()->CallJavascriptFunction(
+        "appList.startPage.onAppListHidden");
+  }
+  if (!app_list::switches::IsExperimentalAppListEnabled())
+    UnloadContents();
+
+  if (HotwordService::IsExperimentalHotwordingEnabled() &&
+      speech_recognizer_) {
+    speech_recognizer_->Stop();
+  }
+}
+
 void StartPageService::ToggleSpeechRecognition() {
+  DCHECK(contents_);
   speech_button_toggled_manually_ = true;
+  if (!contents_->GetWebUI())
+    return;
+
+  if (!webui_finished_loading_) {
+    pending_webui_callbacks_.push_back(
+        base::Bind(&StartPageService::ToggleSpeechRecognition,
+                   base::Unretained(this)));
+    return;
+  }
+
+  if (HotwordService::IsExperimentalHotwordingEnabled()) {
+    if (!speech_recognizer_) {
+      std::string profile_locale;
+#if defined(OS_CHROMEOS)
+      profile_locale = profile_->GetPrefs()->GetString(
+          prefs::kApplicationLocale);
+#endif
+      if (profile_locale.empty())
+        profile_locale = g_browser_process->GetApplicationLocale();
+
+      speech_recognizer_.reset(
+          new SpeechRecognizer(weak_factory_.GetWeakPtr(),
+                               profile_->GetRequestContext(),
+                               profile_locale));
+    }
+
+    speech_recognizer_->Start();
+    return;
+  }
+
   contents_->GetWebUI()->CallJavascriptFunction(
       "appList.startPage.toggleSpeechRecognition");
 }
 
+bool StartPageService::HotwordEnabled() {
+  if (HotwordService::IsExperimentalHotwordingEnabled()) {
+    auto prefs = profile_->GetPrefs();
+    return HotwordServiceFactory::IsServiceAvailable(profile_) &&
+        (prefs->GetBoolean(prefs::kHotwordSearchEnabled) ||
+         prefs->GetBoolean(prefs::kHotwordAlwaysOnSearchEnabled));
+  }
+#if defined(OS_CHROMEOS)
+  return HotwordServiceFactory::IsServiceAvailable(profile_) &&
+      profile_->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled);
+#else
+  return false;
+#endif
+}
+
 content::WebContents* StartPageService::GetStartPageContents() {
   return app_list::switches::IsExperimentalAppListEnabled() ? contents_.get()
                                                             : NULL;
 }
 
 content::WebContents* StartPageService::GetSpeechRecognitionContents() {
-  return app_list::switches::IsVoiceSearchEnabled() ? contents_.get() : NULL;
+  if (app_list::switches::IsVoiceSearchEnabled()) {
+    if (!contents_)
+      LoadContents();
+    return contents_.get();
+  }
+  return NULL;
 }
 
 void StartPageService::OnSpeechResult(
@@ -164,7 +241,7 @@ void StartPageService::OnSpeechResult(
                     OnSpeechResult(query, is_final));
 }
 
-void StartPageService::OnSpeechSoundLevelChanged(int16 level) {
+void StartPageService::OnSpeechSoundLevelChanged(int16_t level) {
   FOR_EACH_OBSERVER(StartPageObserver,
                     observers_,
                     OnSpeechSoundLevelChanged(level));
@@ -172,35 +249,75 @@ void StartPageService::OnSpeechSoundLevelChanged(int16 level) {
 
 void StartPageService::OnSpeechRecognitionStateChanged(
     SpeechRecognitionState new_state) {
-  SpeechRecognitionState old_state = state_;
-  state_ = new_state;
 
-  if (!InSpeechRecognition(old_state) && InSpeechRecognition(new_state)) {
+  if (HotwordService::IsExperimentalHotwordingEnabled() &&
+      new_state == SPEECH_RECOGNITION_READY &&
+      speech_recognizer_) {
+    speech_recognizer_->Stop();
+  }
+
+  if (!InSpeechRecognition(state_) && InSpeechRecognition(new_state)) {
     if (!speech_button_toggled_manually_ &&
-        old_state == SPEECH_RECOGNITION_HOTWORD_LISTENING) {
+        state_ == SPEECH_RECOGNITION_HOTWORD_LISTENING) {
       RecordAction(UserMetricsAction("AppList_HotwordRecognized"));
-      AppListService* app_list_service =
-          AppListService::Get(chrome::GetActiveDesktop());
-      if (!app_list_service->IsAppListVisible())
-       app_list_service->Show();
     } else {
       RecordAction(UserMetricsAction("AppList_VoiceSearchStartedManually"));
     }
-  } else if (InSpeechRecognition(old_state) &&
-             !InSpeechRecognition(new_state) &&
+  } else if (InSpeechRecognition(state_) && !InSpeechRecognition(new_state) &&
              !speech_result_obtained_) {
     RecordAction(UserMetricsAction("AppList_VoiceSearchCanceled"));
   }
   speech_button_toggled_manually_ = false;
   speech_result_obtained_ = false;
-
+  state_ = new_state;
   FOR_EACH_OBSERVER(StartPageObserver,
                     observers_,
                     OnSpeechRecognitionStateChanged(new_state));
 }
 
+content::WebContents* StartPageService::GetSpeechContents() {
+  return GetSpeechRecognitionContents();
+}
+
 void StartPageService::Shutdown() {
+  UnloadContents();
+}
+
+void StartPageService::WebUILoaded() {
+  // There's a race condition between the WebUI loading, and calling its JS
+  // functions. Specifically, calling LoadContents() doesn't mean that the page
+  // has loaded, but several code paths make this assumption. This function
+  // allows us to defer calling JS functions until after the page has finished
+  // loading.
+  webui_finished_loading_ = true;
+  for (const auto& cb : pending_webui_callbacks_)
+    cb.Run();
+  pending_webui_callbacks_.clear();
+}
+
+void StartPageService::LoadContents() {
+  contents_.reset(content::WebContents::Create(
+      content::WebContents::CreateParams(profile_)));
+  contents_delegate_.reset(new StartPageWebContentsDelegate());
+  contents_->SetDelegate(contents_delegate_.get());
+
+  GURL url(chrome::kChromeUIAppListStartPageURL);
+  CommandLine* command_line = CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(::switches::kAppListStartPageURL)) {
+    url = GURL(
+        command_line->GetSwitchValueASCII(::switches::kAppListStartPageURL));
+  }
+
+  contents_->GetController().LoadURL(
+      url,
+      content::Referrer(),
+      ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
+      std::string());
+}
+
+void StartPageService::UnloadContents() {
   contents_.reset();
+  webui_finished_loading_ = false;
 }
 
 }  // namespace app_list