1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_LIVE_CAPTION_CAPTION_CONTROLLER_BASE_H_
#define COMPONENTS_LIVE_CAPTION_CAPTION_CONTROLLER_BASE_H_
#include <list>
#include <memory>
#include <optional>
#include <string>
#include "base/memory/raw_ptr.h"
#include "media/mojo/mojom/speech_recognition.mojom.h"
#include "ui/native_theme/caption_style.h"
#include "ui/native_theme/native_theme_observer.h"
class PrefChangeRegistrar;
class PrefService;
namespace content {
class WebContents;
} // namespace content
namespace captions {
class CaptionBubbleContext;
class CaptionBubbleController;
class CaptionBubbleSettings;
class TranslationViewWrapperBase;
class CaptionControllerBase : public ui::NativeThemeObserver {
public:
class Delegate {
public:
Delegate(const Delegate&) = delete;
Delegate& operator=(const Delegate&) = delete;
virtual ~Delegate() = default;
virtual std::unique_ptr<CaptionBubbleController>
CreateCaptionBubbleController(
CaptionBubbleSettings* caption_bubble_settings,
const std::string& application_locale,
std::unique_ptr<TranslationViewWrapperBase>
translation_view_wrapper) = 0;
virtual void AddCaptionStyleObserver(ui::NativeThemeObserver* observer) = 0;
virtual void RemoveCaptionStyleObserver(
ui::NativeThemeObserver* observer) = 0;
protected:
Delegate() = default;
};
// Listener for transcription-related events. Listeners are owned by the
// `CaptionControllerBase` for simplicity.
class Listener {
public:
virtual ~Listener() = default;
// Called when a transcription is received from the service for audio that
// originated in `web_contents`. `web_contents` may be null if the audio
// was not associated with any particulat WebContents.
//
// Transcriptions will halt if this returns false.
virtual bool OnTranscription(
content::WebContents*,
CaptionBubbleContext*,
const media::SpeechRecognitionResult& result) = 0;
// Called when the audio stream has ended for audio from `web_contents`,
// which may be null.
virtual void OnAudioStreamEnd(
content::WebContents*,
CaptionBubbleContext* caption_bubble_context) = 0;
// Called when the language is identified for audio from `web_contents`,
// which may be null.
virtual void OnLanguageIdentificationEvent(
content::WebContents*,
CaptionBubbleContext* caption_bubble_context,
const media::mojom::LanguageIdentificationEventPtr& event) = 0;
private:
raw_ptr<CaptionControllerBase> caption_controller_ = nullptr;
};
CaptionControllerBase(const CaptionControllerBase&) = delete;
CaptionControllerBase& operator=(const CaptionControllerBase&) = delete;
~CaptionControllerBase() override;
// Add or remove a `Listener`. We maintain ownership, and destroy the
// listener when it is removed.
void AddListener(std::unique_ptr<Listener>);
// Routes a transcription to all listeners. Returns whether the transcription
// was routed successfully, which currently means that at least one listener
// considered it to be successful. It is unclear if we should continue to
// route transcriptions to a listener once it returns false, but for now
// there's at most one listener anyway.
//
// Transcriptions will halt if this returns false.
bool DispatchTranscription(content::WebContents* web_contents,
CaptionBubbleContext* caption_bubble_context,
const media::SpeechRecognitionResult& result);
// Alerts all listeners that the audio stream has ended.
void OnAudioStreamEnd(content::WebContents* web_contents,
CaptionBubbleContext* caption_bubble_context);
// Notifies all listeners about a language identification event.
void OnLanguageIdentificationEvent(
content::WebContents* web_contents,
CaptionBubbleContext* caption_bubble_context,
const media::mojom::LanguageIdentificationEventPtr& event);
void create_ui_for_testing() { CreateUI(); }
void destroy_ui_for_testing() { DestroyUI(); }
CaptionBubbleController* caption_bubble_controller_for_testing() const {
return caption_bubble_controller();
}
protected:
CaptionControllerBase(PrefService* profile_prefs,
const std::string& application_locale,
std::unique_ptr<Delegate> delegate = nullptr);
void CreateUI();
void DestroyUI();
PrefService* profile_prefs() const;
const std::string& application_locale() const;
PrefChangeRegistrar* pref_change_registrar() const;
CaptionBubbleController* caption_bubble_controller() const;
virtual std::unique_ptr<TranslationViewWrapperBase>
CreateTranslationViewWrapper();
private:
virtual CaptionBubbleSettings* caption_bubble_settings() = 0;
// ui::NativeThemeObserver:
void OnCaptionStyleUpdated() override;
// The listener will be destroyed when this returns. Private, so that we
// don't have to worry about listeners removing themselves during list
// iteration. If Listeners ever want to do that, then we need to get smarter
// about it.
void RemoveListener(Listener*);
const raw_ptr<PrefService> profile_prefs_ = nullptr;
const std::string application_locale_;
const std::unique_ptr<Delegate> delegate_;
// The controller is also a `Listener`, and is owned by `listeners_` when we
// create it. While it exists, `caption_bubble_controller_` aliases it. This
// alias is cleared when it's destroyed.
raw_ptr<CaptionBubbleController> caption_bubble_controller_ = nullptr;
const std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_;
// Whether the UI has been created. The UI is created asynchronously from the
// feature being enabled--some implementations may wait for SODA to download
// first. This flag ensures that the UI is not constructed or deconstructed
// twice.
bool is_ui_constructed_ = false;
// All the listeners we own. One of them will be `caption_bubble_controller_`
// if we have one.
std::list<std::unique_ptr<Listener>> listeners_;
};
} // namespace captions
#endif // COMPONENTS_LIVE_CAPTION_CAPTION_CONTROLLER_BASE_H_
|