Add ICU message format support
[chromium-blink-merge.git] / content / browser / media / webrtc_internals.cc
blob2ab4c4314f9d8d22e220bc0313f2ec9dcbff8f4f
1 // Copyright (c) 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.
5 #include "content/browser/media/webrtc_internals.h"
7 #include "base/strings/string_number_conversions.h"
8 #include "content/browser/media/webrtc_internals_ui_observer.h"
9 #include "content/browser/web_contents/web_contents_view.h"
10 #include "content/public/browser/browser_thread.h"
11 #include "content/public/browser/content_browser_client.h"
12 #include "content/public/browser/power_save_blocker.h"
13 #include "content/public/browser/render_process_host.h"
14 #include "content/public/browser/web_contents.h"
16 using base::ProcessId;
17 using std::string;
19 namespace content {
21 namespace {
23 static base::LazyInstance<WebRTCInternals>::Leaky g_webrtc_internals =
24 LAZY_INSTANCE_INITIALIZER;
26 // Makes sure that |dict| has a ListValue under path "log".
27 static base::ListValue* EnsureLogList(base::DictionaryValue* dict) {
28 base::ListValue* log = NULL;
29 if (!dict->GetList("log", &log)) {
30 log = new base::ListValue();
31 if (log)
32 dict->Set("log", log);
34 return log;
37 } // namespace
39 WebRTCInternals::WebRTCInternals()
40 : aec_dump_enabled_(false) {
41 // TODO(grunell): Shouldn't all the webrtc_internals* files be excluded from the
42 // build if WebRTC is disabled?
43 #if defined(ENABLE_WEBRTC)
44 aec_dump_file_path_ =
45 GetContentClient()->browser()->GetDefaultDownloadDirectory();
46 if (aec_dump_file_path_.empty()) {
47 // In this case the default path (|aec_dump_file_path_|) will be empty and
48 // the platform default path will be used in the file dialog (with no
49 // default file name). See SelectFileDialog::SelectFile. On Android where
50 // there's no dialog we'll fail to open the file.
51 VLOG(1) << "Could not get the download directory.";
52 } else {
53 aec_dump_file_path_ =
54 aec_dump_file_path_.Append(FILE_PATH_LITERAL("audio.aecdump"));
56 #endif // defined(ENABLE_WEBRTC)
59 WebRTCInternals::~WebRTCInternals() {
62 WebRTCInternals* WebRTCInternals::GetInstance() {
63 return g_webrtc_internals.Pointer();
66 void WebRTCInternals::OnAddPeerConnection(int render_process_id,
67 ProcessId pid,
68 int lid,
69 const string& url,
70 const string& rtc_configuration,
71 const string& constraints) {
72 DCHECK_CURRENTLY_ON(BrowserThread::UI);
74 base::DictionaryValue* dict = new base::DictionaryValue();
75 if (!dict)
76 return;
78 dict->SetInteger("rid", render_process_id);
79 dict->SetInteger("pid", static_cast<int>(pid));
80 dict->SetInteger("lid", lid);
81 dict->SetString("rtcConfiguration", rtc_configuration);
82 dict->SetString("constraints", constraints);
83 dict->SetString("url", url);
84 peer_connection_data_.Append(dict);
85 CreateOrReleasePowerSaveBlocker();
87 if (observers_.might_have_observers())
88 SendUpdate("addPeerConnection", dict);
90 if (render_process_id_set_.insert(render_process_id).second) {
91 RenderProcessHost* host = RenderProcessHost::FromID(render_process_id);
92 if (host)
93 host->AddObserver(this);
97 void WebRTCInternals::OnRemovePeerConnection(ProcessId pid, int lid) {
98 DCHECK_CURRENTLY_ON(BrowserThread::UI);
99 for (size_t i = 0; i < peer_connection_data_.GetSize(); ++i) {
100 base::DictionaryValue* dict = NULL;
101 peer_connection_data_.GetDictionary(i, &dict);
103 int this_pid = 0;
104 int this_lid = 0;
105 dict->GetInteger("pid", &this_pid);
106 dict->GetInteger("lid", &this_lid);
108 if (this_pid != static_cast<int>(pid) || this_lid != lid)
109 continue;
111 peer_connection_data_.Remove(i, NULL);
112 CreateOrReleasePowerSaveBlocker();
114 if (observers_.might_have_observers()) {
115 base::DictionaryValue id;
116 id.SetInteger("pid", static_cast<int>(pid));
117 id.SetInteger("lid", lid);
118 SendUpdate("removePeerConnection", &id);
120 break;
124 void WebRTCInternals::OnUpdatePeerConnection(
125 ProcessId pid, int lid, const string& type, const string& value) {
126 DCHECK_CURRENTLY_ON(BrowserThread::UI);
128 for (size_t i = 0; i < peer_connection_data_.GetSize(); ++i) {
129 base::DictionaryValue* record = NULL;
130 peer_connection_data_.GetDictionary(i, &record);
132 int this_pid = 0, this_lid = 0;
133 record->GetInteger("pid", &this_pid);
134 record->GetInteger("lid", &this_lid);
136 if (this_pid != static_cast<int>(pid) || this_lid != lid)
137 continue;
139 // Append the update to the end of the log.
140 base::ListValue* log = EnsureLogList(record);
141 if (!log)
142 return;
144 base::DictionaryValue* log_entry = new base::DictionaryValue();
145 if (!log_entry)
146 return;
148 double epoch_time = base::Time::Now().ToJsTime();
149 string time = base::DoubleToString(epoch_time);
150 log_entry->SetString("time", time);
151 log_entry->SetString("type", type);
152 log_entry->SetString("value", value);
153 log->Append(log_entry);
155 if (observers_.might_have_observers()) {
156 base::DictionaryValue update;
157 update.SetInteger("pid", static_cast<int>(pid));
158 update.SetInteger("lid", lid);
159 update.MergeDictionary(log_entry);
161 SendUpdate("updatePeerConnection", &update);
163 return;
167 void WebRTCInternals::OnAddStats(base::ProcessId pid, int lid,
168 const base::ListValue& value) {
169 if (!observers_.might_have_observers())
170 return;
172 base::DictionaryValue dict;
173 dict.SetInteger("pid", static_cast<int>(pid));
174 dict.SetInteger("lid", lid);
176 base::ListValue* list = value.DeepCopy();
177 if (!list)
178 return;
180 dict.Set("reports", list);
182 SendUpdate("addStats", &dict);
185 void WebRTCInternals::OnGetUserMedia(int rid,
186 base::ProcessId pid,
187 const std::string& origin,
188 bool audio,
189 bool video,
190 const std::string& audio_constraints,
191 const std::string& video_constraints) {
192 DCHECK_CURRENTLY_ON(BrowserThread::UI);
194 base::DictionaryValue* dict = new base::DictionaryValue();
195 dict->SetInteger("rid", rid);
196 dict->SetInteger("pid", static_cast<int>(pid));
197 dict->SetString("origin", origin);
198 if (audio)
199 dict->SetString("audio", audio_constraints);
200 if (video)
201 dict->SetString("video", video_constraints);
203 get_user_media_requests_.Append(dict);
205 if (observers_.might_have_observers())
206 SendUpdate("addGetUserMedia", dict);
208 if (render_process_id_set_.insert(rid).second) {
209 RenderProcessHost* host = RenderProcessHost::FromID(rid);
210 if (host)
211 host->AddObserver(this);
215 void WebRTCInternals::AddObserver(WebRTCInternalsUIObserver *observer) {
216 DCHECK_CURRENTLY_ON(BrowserThread::UI);
217 observers_.AddObserver(observer);
220 void WebRTCInternals::RemoveObserver(WebRTCInternalsUIObserver *observer) {
221 DCHECK_CURRENTLY_ON(BrowserThread::UI);
222 observers_.RemoveObserver(observer);
224 // Disables the AEC recording if it is enabled and the last webrtc-internals
225 // page is going away.
226 if (aec_dump_enabled_ && !observers_.might_have_observers())
227 DisableAecDump();
230 void WebRTCInternals::UpdateObserver(WebRTCInternalsUIObserver* observer) {
231 DCHECK_CURRENTLY_ON(BrowserThread::UI);
232 if (peer_connection_data_.GetSize() > 0)
233 observer->OnUpdate("updateAllPeerConnections", &peer_connection_data_);
235 for (base::ListValue::iterator it = get_user_media_requests_.begin();
236 it != get_user_media_requests_.end();
237 ++it) {
238 observer->OnUpdate("addGetUserMedia", *it);
242 void WebRTCInternals::EnableAecDump(content::WebContents* web_contents) {
243 #if defined(ENABLE_WEBRTC)
244 #if defined(OS_ANDROID)
245 EnableAecDumpOnAllRenderProcessHosts();
246 #else
247 select_file_dialog_ = ui::SelectFileDialog::Create(this, NULL);
248 select_file_dialog_->SelectFile(
249 ui::SelectFileDialog::SELECT_SAVEAS_FILE,
250 base::string16(),
251 aec_dump_file_path_,
252 NULL,
254 FILE_PATH_LITERAL(""),
255 web_contents->GetTopLevelNativeWindow(),
256 NULL);
257 #endif
258 #endif
261 void WebRTCInternals::DisableAecDump() {
262 #if defined(ENABLE_WEBRTC)
263 aec_dump_enabled_ = false;
265 // Tear down the dialog since the user has unchecked the AEC dump box.
266 select_file_dialog_ = NULL;
268 for (RenderProcessHost::iterator i(
269 content::RenderProcessHost::AllHostsIterator());
270 !i.IsAtEnd(); i.Advance()) {
271 i.GetCurrentValue()->DisableAecDump();
273 #endif
276 void WebRTCInternals::ResetForTesting() {
277 DCHECK_CURRENTLY_ON(BrowserThread::UI);
278 observers_.Clear();
279 peer_connection_data_.Clear();
280 CreateOrReleasePowerSaveBlocker();
281 get_user_media_requests_.Clear();
282 aec_dump_enabled_ = false;
285 void WebRTCInternals::SendUpdate(const string& command, base::Value* value) {
286 DCHECK(observers_.might_have_observers());
288 FOR_EACH_OBSERVER(WebRTCInternalsUIObserver,
289 observers_,
290 OnUpdate(command, value));
293 void WebRTCInternals::RenderProcessHostDestroyed(RenderProcessHost* host) {
294 DCHECK_CURRENTLY_ON(BrowserThread::UI);
295 OnRendererExit(host->GetID());
297 render_process_id_set_.erase(host->GetID());
298 host->RemoveObserver(this);
301 void WebRTCInternals::FileSelected(const base::FilePath& path,
302 int /* unused_index */,
303 void* /*unused_params */) {
304 #if defined(ENABLE_WEBRTC)
305 aec_dump_file_path_ = path;
306 EnableAecDumpOnAllRenderProcessHosts();
307 #endif
310 void WebRTCInternals::FileSelectionCanceled(void* params) {
311 #if defined(ENABLE_WEBRTC)
312 SendUpdate("aecRecordingFileSelectionCancelled", NULL);
313 #endif
316 void WebRTCInternals::OnRendererExit(int render_process_id) {
317 DCHECK_CURRENTLY_ON(BrowserThread::UI);
319 // Iterates from the end of the list to remove the PeerConnections created
320 // by the exitting renderer.
321 for (int i = peer_connection_data_.GetSize() - 1; i >= 0; --i) {
322 base::DictionaryValue* record = NULL;
323 peer_connection_data_.GetDictionary(i, &record);
325 int this_rid = 0;
326 record->GetInteger("rid", &this_rid);
328 if (this_rid == render_process_id) {
329 if (observers_.might_have_observers()) {
330 int lid = 0, pid = 0;
331 record->GetInteger("lid", &lid);
332 record->GetInteger("pid", &pid);
334 base::DictionaryValue update;
335 update.SetInteger("lid", lid);
336 update.SetInteger("pid", pid);
337 SendUpdate("removePeerConnection", &update);
339 peer_connection_data_.Remove(i, NULL);
342 CreateOrReleasePowerSaveBlocker();
344 bool found_any = false;
345 // Iterates from the end of the list to remove the getUserMedia requests
346 // created by the exiting renderer.
347 for (int i = get_user_media_requests_.GetSize() - 1; i >= 0; --i) {
348 base::DictionaryValue* record = NULL;
349 get_user_media_requests_.GetDictionary(i, &record);
351 int this_rid = 0;
352 record->GetInteger("rid", &this_rid);
354 if (this_rid == render_process_id) {
355 get_user_media_requests_.Remove(i, NULL);
356 found_any = true;
360 if (found_any && observers_.might_have_observers()) {
361 base::DictionaryValue update;
362 update.SetInteger("rid", render_process_id);
363 SendUpdate("removeGetUserMediaForRenderer", &update);
367 #if defined(ENABLE_WEBRTC)
368 void WebRTCInternals::EnableAecDumpOnAllRenderProcessHosts() {
369 aec_dump_enabled_ = true;
370 for (RenderProcessHost::iterator i(
371 content::RenderProcessHost::AllHostsIterator());
372 !i.IsAtEnd(); i.Advance()) {
373 i.GetCurrentValue()->EnableAecDump(aec_dump_file_path_);
376 #endif
378 void WebRTCInternals::CreateOrReleasePowerSaveBlocker() {
379 DCHECK_CURRENTLY_ON(BrowserThread::UI);
381 if (peer_connection_data_.empty() && power_save_blocker_) {
382 DVLOG(1) << ("Releasing the block on application suspension since no "
383 "PeerConnections are active anymore.");
384 power_save_blocker_.reset();
385 } else if (!peer_connection_data_.empty() && !power_save_blocker_) {
386 DVLOG(1) << ("Preventing the application from being suspended while one or "
387 "more PeerConnections are active.");
388 power_save_blocker_ =
389 content::PowerSaveBlocker::Create(
390 PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension,
391 PowerSaveBlocker::kReasonOther,
392 "WebRTC has active PeerConnections").Pass();
396 } // namespace content