15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/speech/chrome_speech_recognition_manager_delegate.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <set>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/prefs/pref_service.h"
12868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/synchronization/lock.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/threading/thread_restrictions.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/browser_process.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_service.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile_manager.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/tab_contents/tab_util.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/pref_names.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/browser_thread.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_registrar.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_source.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_types.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/render_process_host.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/render_view_host.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/resource_context.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/speech_recognition_manager.h"
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/speech_recognition_session_config.h"
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/speech_recognition_session_context.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/web_contents.h"
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/common/speech_recognition_error.h"
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/common/speech_recognition_result.h"
33c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "extensions/browser/view_type_utils.h"
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_request_context_getter.h"
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN)
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/installer/util/wmi.h"
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::BrowserThread;
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::SpeechRecognitionManager;
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::WebContents;
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace speech {
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Asynchronously fetches the PC and audio hardware/driver info if
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// the user has opted into UMA. This information is sent with speech input
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// requests to the server for identifying and improving quality issues with
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// specific device configurations.
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ChromeSpeechRecognitionManagerDelegate::OptionalRequestInfo
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : public base::RefCountedThreadSafe<OptionalRequestInfo> {
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  OptionalRequestInfo() : can_report_metrics_(false) {
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void Refresh() {
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // UMA opt-in can be checked only from the UI thread, so switch to that.
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::Bind(&OptionalRequestInfo::CheckUMAAndGetHardwareInfo, this));
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void CheckUMAAndGetHardwareInfo() {
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (g_browser_process->local_state()->GetBoolean(
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        prefs::kMetricsReportingEnabled)) {
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Access potentially slow OS calls from the FILE thread.
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          base::Bind(&OptionalRequestInfo::GetHardwareInfo, this));
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void GetHardwareInfo() {
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::AutoLock lock(lock_);
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    can_report_metrics_ = true;
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    string16 device_model =
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SpeechRecognitionManager::GetInstance()->GetAudioInputDeviceModel();
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    value_ = UTF16ToUTF8(
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        installer::WMIComputerSystem::GetModel() + L"|" + device_model);
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else  // defined(OS_WIN)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    value_ = UTF16ToUTF8(device_model);
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif  // defined(OS_WIN)
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string value() {
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::AutoLock lock(lock_);
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return value_;
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool can_report_metrics() {
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::AutoLock lock(lock_);
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return can_report_metrics_;
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  friend class base::RefCountedThreadSafe<OptionalRequestInfo>;
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ~OptionalRequestInfo() {}
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::Lock lock_;
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string value_;
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool can_report_metrics_;
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(OptionalRequestInfo);
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Simple utility to get notified when a WebContent (a tab or an extension's
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// background page) is closed or crashes. Both the callback site and the
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// callback thread are passed by the caller in the constructor.
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// There is no restriction on the constructor, however this class must be
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// destroyed on the UI thread, due to the NotificationRegistrar dependency.
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ChromeSpeechRecognitionManagerDelegate::TabWatcher
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : public base::RefCountedThreadSafe<TabWatcher>,
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      public content::NotificationObserver {
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  typedef base::Callback<void(int render_process_id, int render_view_id)>
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      TabClosedCallback;
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TabWatcher(TabClosedCallback tab_closed_callback,
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             BrowserThread::ID callback_thread)
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      : tab_closed_callback_(tab_closed_callback),
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        callback_thread_(callback_thread) {
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Starts monitoring the WebContents corresponding to the given
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // |render_process_id|, |render_view_id| pair, invoking |tab_closed_callback_|
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // if closed/unloaded.
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void Watch(int render_process_id, int render_view_id) {
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          &TabWatcher::Watch, this, render_process_id, render_view_id));
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    WebContents* web_contents = tab_util::GetWebContentsByID(render_process_id,
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                             render_view_id);
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Sessions initiated by speech input extension APIs will end up in a NULL
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // WebContent here, but they are properly managed by the
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // chrome::SpeechInputExtensionManager. However, sessions initiated within a
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // extension using the (new) speech JS APIs, will be properly handled here.
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(primiano) turn this line into a DCHECK once speech input extension
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // API is deprecated.
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!web_contents)
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Avoid multiple registrations on |registrar_| for the same |web_contents|.
149c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (FindWebContents(web_contents) !=  registered_web_contents_.end()) {
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
152c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    registered_web_contents_.push_back(
153c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        WebContentsInfo(web_contents, render_process_id, render_view_id));
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Lazy initialize the registrar.
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!registrar_.get())
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      registrar_.reset(new content::NotificationRegistrar());
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    registrar_->Add(this,
160c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                    content::NOTIFICATION_WEB_CONTENTS_SWAPPED,
161c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                    content::Source<WebContents>(web_contents));
162c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    registrar_->Add(this,
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    content::Source<WebContents>(web_contents));
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // content::NotificationObserver implementation.
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void Observe(int type,
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       const content::NotificationSource& source,
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       const content::NotificationDetails& details) OVERRIDE {
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
172c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    DCHECK(type == content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED ||
173c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)           type == content::NOTIFICATION_WEB_CONTENTS_SWAPPED);
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    WebContents* web_contents = content::Source<WebContents>(source).ptr();
176c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    std::vector<WebContentsInfo>::iterator iter = FindWebContents(web_contents);
177c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    DCHECK(iter != registered_web_contents_.end());
178c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    int render_process_id = iter->render_process_id;
179c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    int render_view_id = iter->render_view_id;
180c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    registered_web_contents_.erase(iter);
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    registrar_->Remove(this,
183c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                       content::NOTIFICATION_WEB_CONTENTS_SWAPPED,
184c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                       content::Source<WebContents>(web_contents));
185c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    registrar_->Remove(this,
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       content::Source<WebContents>(web_contents));
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BrowserThread::PostTask(callback_thread_, FROM_HERE, base::Bind(
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        tab_closed_callback_, render_process_id, render_view_id));
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
194c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  struct WebContentsInfo {
195c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    WebContentsInfo(content::WebContents* web_contents,
196c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                    int render_process_id,
197c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                    int render_view_id)
198c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        : web_contents(web_contents),
199c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          render_process_id(render_process_id),
200c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          render_view_id(render_view_id) {}
201c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
202c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    ~WebContentsInfo() {}
203c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
204c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    content::WebContents* web_contents;
205c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    int render_process_id;
206c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    int render_view_id;
207c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  };
208c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  friend class base::RefCountedThreadSafe<TabWatcher>;
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual ~TabWatcher() {
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Must be destroyed on the UI thread due to |registrar_| non thread-safety.
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
216c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Helper function to find the iterator in |registered_web_contents_| which
217c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // contains |web_contents|.
218c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::vector<WebContentsInfo>::iterator FindWebContents(
219c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      content::WebContents* web_contents) {
220c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    for (std::vector<WebContentsInfo>::iterator i(
221c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)         registered_web_contents_.begin());
222c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)         i != registered_web_contents_.end(); ++i) {
223c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (i->web_contents == web_contents)
224c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        return i;
225c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
226c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
227c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return registered_web_contents_.end();
228c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
229c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Lazy-initialized and used on the UI thread to handle web contents
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // notifications (tab closing).
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<content::NotificationRegistrar> registrar_;
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Keeps track of which WebContent(s) have been registered, in order to avoid
235c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // double registrations on |registrar_| and to pass the correct render
236c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // process id and render view id to |tab_closed_callback_| after the process
237c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // has gone away.
238c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::vector<WebContentsInfo> registered_web_contents_;
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Callback used to notify, on the thread specified by |callback_thread_| the
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // closure of a registered tab.
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TabClosedCallback tab_closed_callback_;
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  content::BrowserThread::ID callback_thread_;
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(TabWatcher);
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ChromeSpeechRecognitionManagerDelegate
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)::ChromeSpeechRecognitionManagerDelegate() {
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ChromeSpeechRecognitionManagerDelegate
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)::~ChromeSpeechRecognitionManagerDelegate() {
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ChromeSpeechRecognitionManagerDelegate::TabClosedCallback(
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int render_process_id, int render_view_id) {
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SpeechRecognitionManager* manager = SpeechRecognitionManager::GetInstance();
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // |manager| becomes NULL if a browser shutdown happens between the post of
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // this task (from the UI thread) and this call (on the IO thread). In this
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // case we just return.
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!manager)
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  manager->AbortAllSessionsForRenderView(render_process_id, render_view_id);
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ChromeSpeechRecognitionManagerDelegate::OnRecognitionStart(
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int session_id) {
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const content::SpeechRecognitionSessionContext& context =
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Register callback to auto abort session on tab closure.
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // |tab_watcher_| is lazyly istantiated on the first call.
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!tab_watcher_.get()) {
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tab_watcher_ = new TabWatcher(
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::Bind(&ChromeSpeechRecognitionManagerDelegate::TabClosedCallback,
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   base::Unretained(this)),
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        BrowserThread::IO);
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tab_watcher_->Watch(context.render_process_id, context.render_view_id);
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ChromeSpeechRecognitionManagerDelegate::OnAudioStart(int session_id) {
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ChromeSpeechRecognitionManagerDelegate::OnEnvironmentEstimationComplete(
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int session_id) {
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ChromeSpeechRecognitionManagerDelegate::OnSoundStart(int session_id) {
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ChromeSpeechRecognitionManagerDelegate::OnSoundEnd(int session_id) {
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ChromeSpeechRecognitionManagerDelegate::OnAudioEnd(int session_id) {
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ChromeSpeechRecognitionManagerDelegate::OnRecognitionResults(
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int session_id, const content::SpeechRecognitionResults& result) {
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ChromeSpeechRecognitionManagerDelegate::OnRecognitionError(
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int session_id, const content::SpeechRecognitionError& error) {
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ChromeSpeechRecognitionManagerDelegate::OnAudioLevelsChange(
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int session_id, float volume, float noise_volume) {
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ChromeSpeechRecognitionManagerDelegate::OnRecognitionEnd(int session_id) {
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ChromeSpeechRecognitionManagerDelegate::GetDiagnosticInformation(
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bool* can_report_metrics,
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string* hardware_info) {
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!optional_request_info_.get()) {
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    optional_request_info_ = new OptionalRequestInfo();
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Since hardware info is optional with speech input requests, we start an
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // asynchronous fetch here and move on with recording audio. This first
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // speech input request would send an empty string for hardware info and
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // subsequent requests may have the hardware info available if the fetch
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // completed before them. This way we don't end up stalling the user with
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // a long wait and disk seeks when they click on a UI element and start
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // speaking.
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    optional_request_info_->Refresh();
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *can_report_metrics = optional_request_info_->can_report_metrics();
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *hardware_info = optional_request_info_->value();
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ChromeSpeechRecognitionManagerDelegate::CheckRecognitionIsAllowed(
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int session_id,
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::Callback<void(bool ask_user, bool is_allowed)> callback) {
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const content::SpeechRecognitionSessionContext& context =
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Make sure that initiators (extensions/web pages) properly set the
3457dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  // |render_process_id| field, which is needed later to retrieve the profile.
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_NE(context.render_process_id, 0);
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check that the render view type is appropriate, and whether or not we
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // need to request permission from the user.
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          base::Bind(&CheckRenderViewType,
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     callback,
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     context.render_process_id,
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     context.render_view_id,
3557dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                                     !context.requested_by_page_element));
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)content::SpeechRecognitionEventListener*
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ChromeSpeechRecognitionManagerDelegate::GetEventListener() {
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return this;
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3637dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochbool ChromeSpeechRecognitionManagerDelegate::FilterProfanities(
3647dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    int render_process_id) {
3657dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  content::RenderProcessHost* rph =
3667dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      content::RenderProcessHost::FromID(render_process_id);
3677dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if (!rph)  // Guard against race conditions on RPH lifetime.
3687dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    return true;
3697dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
3707dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  return Profile::FromBrowserContext(rph->GetBrowserContext())->GetPrefs()->
3717dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      GetBoolean(prefs::kSpeechRecognitionFilterProfanities);
3727dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch}
3737dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ChromeSpeechRecognitionManagerDelegate::CheckRenderViewType(
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::Callback<void(bool ask_user, bool is_allowed)> callback,
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int render_process_id,
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int render_view_id,
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bool js_api) {
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const content::RenderViewHost* render_view_host =
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::RenderViewHost::FromID(render_process_id, render_view_id);
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool allowed = false;
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool ask_permission = false;
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!render_view_host) {
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!js_api) {
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If there is no render view, we cannot show the speech bubble, so this
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // is not allowed.
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      allowed = false;
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ask_permission = false;
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // This happens for extensions. Manifest should be checked for permission.
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      allowed = true;
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ask_permission = false;
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            base::Bind(callback, ask_permission, allowed));
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  WebContents* web_contents = WebContents::FromRenderViewHost(render_view_host);
403c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  extensions::ViewType view_type = extensions::GetViewType(web_contents);
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(kalman): Also enable speech bubble for extension popups
4062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // (VIEW_TYPE_EXTENSION_POPUP) once popup-like control UI works properly in
4072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // extensions: http://crbug.com/163851.
4082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Right now the extension popup closes and dismisses immediately on user
4092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // click.
410c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (view_type == extensions::VIEW_TYPE_TAB_CONTENTS ||
41190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      view_type == extensions::VIEW_TYPE_APP_SHELL ||
41290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      view_type == extensions::VIEW_TYPE_VIRTUAL_KEYBOARD) {
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If it is a tab, we can show the speech input bubble or ask for
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // permission.
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    allowed = true;
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (js_api)
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ask_permission = true;
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          base::Bind(callback, ask_permission, allowed));
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace speech
426