start_page_service.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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/command_line.h"
10#include "base/memory/singleton.h"
11#include "base/metrics/user_metrics.h"
12#include "chrome/browser/chrome_notification_types.h"
13#include "chrome/browser/media/media_stream_infobar_delegate.h"
14#include "chrome/browser/profiles/profile.h"
15#include "chrome/browser/ui/app_list/recommended_apps.h"
16#include "chrome/browser/ui/app_list/start_page_observer.h"
17#include "chrome/browser/ui/app_list/start_page_service_factory.h"
18#include "chrome/common/chrome_switches.h"
19#include "chrome/common/url_constants.h"
20#include "content/public/browser/notification_details.h"
21#include "content/public/browser/notification_observer.h"
22#include "content/public/browser/notification_registrar.h"
23#include "content/public/browser/notification_service.h"
24#include "content/public/browser/notification_source.h"
25#include "content/public/browser/web_contents.h"
26#include "content/public/browser/web_contents_delegate.h"
27#include "extensions/browser/extension_system_provider.h"
28#include "extensions/browser/extensions_browser_client.h"
29#include "extensions/common/extension.h"
30#include "ui/app_list/app_list_switches.h"
31
32using base::RecordAction;
33using base::UserMetricsAction;
34
35namespace app_list {
36
37namespace {
38
39bool InSpeechRecognition(SpeechRecognitionState state) {
40  return state == SPEECH_RECOGNITION_RECOGNIZING ||
41      state == SPEECH_RECOGNITION_IN_SPEECH;
42}
43
44}
45
46class StartPageService::ProfileDestroyObserver
47    : public content::NotificationObserver {
48 public:
49  explicit ProfileDestroyObserver(StartPageService* service)
50      : service_(service) {
51    registrar_.Add(this,
52                   chrome::NOTIFICATION_PROFILE_DESTROYED,
53                   content::Source<Profile>(service_->profile()));
54  }
55  virtual ~ProfileDestroyObserver() {}
56
57 private:
58  // content::NotificationObserver
59  virtual void Observe(int type,
60                       const content::NotificationSource& source,
61                       const content::NotificationDetails& details) OVERRIDE {
62    DCHECK_EQ(chrome::NOTIFICATION_PROFILE_DESTROYED, type);
63    DCHECK_EQ(service_->profile(), content::Source<Profile>(source).ptr());
64    service_->Shutdown();
65  }
66
67  StartPageService* service_;  // Owner of this class.
68  content::NotificationRegistrar registrar_;
69
70  DISALLOW_COPY_AND_ASSIGN(ProfileDestroyObserver);
71};
72
73class StartPageService::StartPageWebContentsDelegate
74    : public content::WebContentsDelegate {
75 public:
76  StartPageWebContentsDelegate() {}
77  virtual ~StartPageWebContentsDelegate() {}
78
79  virtual void RequestMediaAccessPermission(
80      content::WebContents* web_contents,
81      const content::MediaStreamRequest& request,
82      const content::MediaResponseCallback& callback) OVERRIDE {
83    if (MediaStreamInfoBarDelegate::Create(web_contents, request, callback))
84      NOTREACHED() << "Media stream not allowed for WebUI";
85  }
86
87 private:
88  DISALLOW_COPY_AND_ASSIGN(StartPageWebContentsDelegate);
89};
90
91// static
92StartPageService* StartPageService::Get(Profile* profile) {
93  return StartPageServiceFactory::GetForProfile(profile);
94}
95
96StartPageService::StartPageService(Profile* profile)
97    : profile_(profile),
98      profile_destroy_observer_(new ProfileDestroyObserver(this)),
99      recommended_apps_(new RecommendedApps(profile)),
100      state_(app_list::SPEECH_RECOGNITION_OFF),
101      speech_button_toggled_manually_(false),
102      speech_result_obtained_(false) {
103#if defined(OS_CHROMEOS)
104  // Updates the default state to hotword listening, because this is
105  // the default behavior. This will be updated when the page is loaded and
106  // the nacl module is loaded.
107  if (app_list::switches::IsVoiceSearchEnabled())
108    state_ = app_list::SPEECH_RECOGNITION_HOTWORD_LISTENING;
109#endif
110
111  contents_.reset(content::WebContents::Create(
112      content::WebContents::CreateParams(profile_)));
113  contents_delegate_.reset(new StartPageWebContentsDelegate());
114  contents_->SetDelegate(contents_delegate_.get());
115
116  GURL url(chrome::kChromeUIAppListStartPageURL);
117  CommandLine* command_line = CommandLine::ForCurrentProcess();
118  if (command_line->HasSwitch(::switches::kAppListStartPageURL)) {
119    url = GURL(
120        command_line->GetSwitchValueASCII(::switches::kAppListStartPageURL));
121  }
122
123  contents_->GetController().LoadURL(
124      url,
125      content::Referrer(),
126      content::PAGE_TRANSITION_AUTO_TOPLEVEL,
127      std::string());
128}
129
130StartPageService::~StartPageService() {}
131
132void StartPageService::AddObserver(StartPageObserver* observer) {
133  observers_.AddObserver(observer);
134}
135
136void StartPageService::RemoveObserver(StartPageObserver* observer) {
137  observers_.RemoveObserver(observer);
138}
139
140void StartPageService::ToggleSpeechRecognition() {
141  speech_button_toggled_manually_ = true;
142  contents_->GetWebUI()->CallJavascriptFunction(
143      "appList.startPage.toggleSpeechRecognition");
144}
145
146content::WebContents* StartPageService::GetStartPageContents() {
147  return CommandLine::ForCurrentProcess()->HasSwitch(
148      ::switches::kShowAppListStartPage) ? contents_.get() : NULL;
149}
150
151content::WebContents* StartPageService::GetSpeechRecognitionContents() {
152  return app_list::switches::IsVoiceSearchEnabled() ? contents_.get() : NULL;
153}
154
155void StartPageService::OnSpeechResult(
156    const base::string16& query, bool is_final) {
157  if (is_final) {
158    speech_result_obtained_ = true;
159    RecordAction(UserMetricsAction("AppList_SearchedBySpeech"));
160  }
161  FOR_EACH_OBSERVER(StartPageObserver,
162                    observers_,
163                    OnSpeechResult(query, is_final));
164}
165
166void StartPageService::OnSpeechSoundLevelChanged(int16 level) {
167  FOR_EACH_OBSERVER(StartPageObserver,
168                    observers_,
169                    OnSpeechSoundLevelChanged(level));
170}
171
172void StartPageService::OnSpeechRecognitionStateChanged(
173    SpeechRecognitionState new_state) {
174  if (!InSpeechRecognition(state_) && InSpeechRecognition(new_state)) {
175    if (!speech_button_toggled_manually_ &&
176        state_ == SPEECH_RECOGNITION_HOTWORD_LISTENING) {
177      RecordAction(UserMetricsAction("AppList_HotwordRecognized"));
178    } else {
179      RecordAction(UserMetricsAction("AppList_VoiceSearchStartedManually"));
180    }
181  } else if (InSpeechRecognition(state_) && !InSpeechRecognition(new_state) &&
182             !speech_result_obtained_) {
183    RecordAction(UserMetricsAction("AppList_VoiceSearchCanceled"));
184  }
185  speech_button_toggled_manually_ = false;
186  speech_result_obtained_ = false;
187  state_ = new_state;
188  FOR_EACH_OBSERVER(StartPageObserver,
189                    observers_,
190                    OnSpeechRecognitionStateChanged(new_state));
191}
192
193void StartPageService::Shutdown() {
194  contents_.reset();
195}
196
197}  // namespace app_list
198