Use multiline attribute to check for IA2_STATE_MULTILINE.
[chromium-blink-merge.git] / content / browser / media / webrtc_internals.cc
blob952633c2d36431017b480f5d73c472019115ccdf
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/notification_service.h"
13 #include "content/public/browser/notification_types.h"
14 #include "content/public/browser/power_save_blocker.h"
15 #include "content/public/browser/render_process_host.h"
16 #include "content/public/browser/web_contents.h"
18 using base::ProcessId;
19 using std::string;
21 namespace content {
23 namespace {
25 static base::LazyInstance<WebRTCInternals>::Leaky g_webrtc_internals =
26 LAZY_INSTANCE_INITIALIZER;
28 // Makes sure that |dict| has a ListValue under path "log".
29 static base::ListValue* EnsureLogList(base::DictionaryValue* dict) {
30 base::ListValue* log = NULL;
31 if (!dict->GetList("log", &log)) {
32 log = new base::ListValue();
33 if (log)
34 dict->Set("log", log);
36 return log;
39 } // namespace
41 WebRTCInternals::WebRTCInternals()
42 : aec_dump_enabled_(false) {
43 registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED,
44 NotificationService::AllBrowserContextsAndSources());
45 // TODO(grunell): Shouldn't all the webrtc_internals* files be excluded from the
46 // build if WebRTC is disabled?
47 #if defined(ENABLE_WEBRTC)
48 aec_dump_file_path_ =
49 GetContentClient()->browser()->GetDefaultDownloadDirectory();
50 if (aec_dump_file_path_.empty()) {
51 // In this case the default path (|aec_dump_file_path_|) will be empty and
52 // the platform default path will be used in the file dialog (with no
53 // default file name). See SelectFileDialog::SelectFile. On Android where
54 // there's no dialog we'll fail to open the file.
55 VLOG(1) << "Could not get the download directory.";
56 } else {
57 aec_dump_file_path_ =
58 aec_dump_file_path_.Append(FILE_PATH_LITERAL("audio.aecdump"));
60 #endif // defined(ENABLE_WEBRTC)
63 WebRTCInternals::~WebRTCInternals() {
66 WebRTCInternals* WebRTCInternals::GetInstance() {
67 return g_webrtc_internals.Pointer();
70 void WebRTCInternals::OnAddPeerConnection(int render_process_id,
71 ProcessId pid,
72 int lid,
73 const string& url,
74 const string& rtc_configuration,
75 const string& constraints) {
76 DCHECK_CURRENTLY_ON(BrowserThread::UI);
78 base::DictionaryValue* dict = new base::DictionaryValue();
79 if (!dict)
80 return;
82 dict->SetInteger("rid", render_process_id);
83 dict->SetInteger("pid", static_cast<int>(pid));
84 dict->SetInteger("lid", lid);
85 dict->SetString("rtcConfiguration", rtc_configuration);
86 dict->SetString("constraints", constraints);
87 dict->SetString("url", url);
88 peer_connection_data_.Append(dict);
89 CreateOrReleasePowerSaveBlocker();
91 if (observers_.might_have_observers())
92 SendUpdate("addPeerConnection", dict);
95 void WebRTCInternals::OnRemovePeerConnection(ProcessId pid, int lid) {
96 DCHECK_CURRENTLY_ON(BrowserThread::UI);
97 for (size_t i = 0; i < peer_connection_data_.GetSize(); ++i) {
98 base::DictionaryValue* dict = NULL;
99 peer_connection_data_.GetDictionary(i, &dict);
101 int this_pid = 0;
102 int this_lid = 0;
103 dict->GetInteger("pid", &this_pid);
104 dict->GetInteger("lid", &this_lid);
106 if (this_pid != static_cast<int>(pid) || this_lid != lid)
107 continue;
109 peer_connection_data_.Remove(i, NULL);
110 CreateOrReleasePowerSaveBlocker();
112 if (observers_.might_have_observers()) {
113 base::DictionaryValue id;
114 id.SetInteger("pid", static_cast<int>(pid));
115 id.SetInteger("lid", lid);
116 SendUpdate("removePeerConnection", &id);
118 break;
122 void WebRTCInternals::OnUpdatePeerConnection(
123 ProcessId pid, int lid, const string& type, const string& value) {
124 DCHECK_CURRENTLY_ON(BrowserThread::UI);
126 for (size_t i = 0; i < peer_connection_data_.GetSize(); ++i) {
127 base::DictionaryValue* record = NULL;
128 peer_connection_data_.GetDictionary(i, &record);
130 int this_pid = 0, this_lid = 0;
131 record->GetInteger("pid", &this_pid);
132 record->GetInteger("lid", &this_lid);
134 if (this_pid != static_cast<int>(pid) || this_lid != lid)
135 continue;
137 // Append the update to the end of the log.
138 base::ListValue* log = EnsureLogList(record);
139 if (!log)
140 return;
142 base::DictionaryValue* log_entry = new base::DictionaryValue();
143 if (!log_entry)
144 return;
146 double epoch_time = base::Time::Now().ToJsTime();
147 string time = base::DoubleToString(epoch_time);
148 log_entry->SetString("time", time);
149 log_entry->SetString("type", type);
150 log_entry->SetString("value", value);
151 log->Append(log_entry);
153 if (observers_.might_have_observers()) {
154 base::DictionaryValue update;
155 update.SetInteger("pid", static_cast<int>(pid));
156 update.SetInteger("lid", lid);
157 update.MergeDictionary(log_entry);
159 SendUpdate("updatePeerConnection", &update);
161 return;
165 void WebRTCInternals::OnAddStats(base::ProcessId pid, int lid,
166 const base::ListValue& value) {
167 if (!observers_.might_have_observers())
168 return;
170 base::DictionaryValue dict;
171 dict.SetInteger("pid", static_cast<int>(pid));
172 dict.SetInteger("lid", lid);
174 base::ListValue* list = value.DeepCopy();
175 if (!list)
176 return;
178 dict.Set("reports", list);
180 SendUpdate("addStats", &dict);
183 void WebRTCInternals::OnGetUserMedia(int rid,
184 base::ProcessId pid,
185 const std::string& origin,
186 bool audio,
187 bool video,
188 const std::string& audio_constraints,
189 const std::string& video_constraints) {
190 DCHECK_CURRENTLY_ON(BrowserThread::UI);
192 base::DictionaryValue* dict = new base::DictionaryValue();
193 dict->SetInteger("rid", rid);
194 dict->SetInteger("pid", static_cast<int>(pid));
195 dict->SetString("origin", origin);
196 if (audio)
197 dict->SetString("audio", audio_constraints);
198 if (video)
199 dict->SetString("video", video_constraints);
201 get_user_media_requests_.Append(dict);
203 if (observers_.might_have_observers())
204 SendUpdate("addGetUserMedia", dict);
207 void WebRTCInternals::AddObserver(WebRTCInternalsUIObserver *observer) {
208 DCHECK_CURRENTLY_ON(BrowserThread::UI);
209 observers_.AddObserver(observer);
212 void WebRTCInternals::RemoveObserver(WebRTCInternalsUIObserver *observer) {
213 DCHECK_CURRENTLY_ON(BrowserThread::UI);
214 observers_.RemoveObserver(observer);
216 // Disables the AEC recording if it is enabled and the last webrtc-internals
217 // page is going away.
218 if (aec_dump_enabled_ && !observers_.might_have_observers())
219 DisableAecDump();
222 void WebRTCInternals::UpdateObserver(WebRTCInternalsUIObserver* observer) {
223 DCHECK_CURRENTLY_ON(BrowserThread::UI);
224 if (peer_connection_data_.GetSize() > 0)
225 observer->OnUpdate("updateAllPeerConnections", &peer_connection_data_);
227 for (base::ListValue::iterator it = get_user_media_requests_.begin();
228 it != get_user_media_requests_.end();
229 ++it) {
230 observer->OnUpdate("addGetUserMedia", *it);
234 void WebRTCInternals::EnableAecDump(content::WebContents* web_contents) {
235 #if defined(ENABLE_WEBRTC)
236 #if defined(OS_ANDROID)
237 EnableAecDumpOnAllRenderProcessHosts();
238 #else
239 select_file_dialog_ = ui::SelectFileDialog::Create(this, NULL);
240 select_file_dialog_->SelectFile(
241 ui::SelectFileDialog::SELECT_SAVEAS_FILE,
242 base::string16(),
243 aec_dump_file_path_,
244 NULL,
246 FILE_PATH_LITERAL(""),
247 web_contents->GetTopLevelNativeWindow(),
248 NULL);
249 #endif
250 #endif
253 void WebRTCInternals::DisableAecDump() {
254 #if defined(ENABLE_WEBRTC)
255 aec_dump_enabled_ = false;
257 // Tear down the dialog since the user has unchecked the AEC dump box.
258 select_file_dialog_ = NULL;
260 for (RenderProcessHost::iterator i(
261 content::RenderProcessHost::AllHostsIterator());
262 !i.IsAtEnd(); i.Advance()) {
263 i.GetCurrentValue()->DisableAecDump();
265 #endif
268 void WebRTCInternals::ResetForTesting() {
269 DCHECK_CURRENTLY_ON(BrowserThread::UI);
270 observers_.Clear();
271 peer_connection_data_.Clear();
272 CreateOrReleasePowerSaveBlocker();
273 get_user_media_requests_.Clear();
274 aec_dump_enabled_ = false;
277 void WebRTCInternals::SendUpdate(const string& command, base::Value* value) {
278 DCHECK(observers_.might_have_observers());
280 FOR_EACH_OBSERVER(WebRTCInternalsUIObserver,
281 observers_,
282 OnUpdate(command, value));
285 void WebRTCInternals::Observe(int type,
286 const NotificationSource& source,
287 const NotificationDetails& details) {
288 DCHECK_CURRENTLY_ON(BrowserThread::UI);
289 DCHECK_EQ(type, NOTIFICATION_RENDERER_PROCESS_TERMINATED);
290 OnRendererExit(Source<RenderProcessHost>(source)->GetID());
293 void WebRTCInternals::FileSelected(const base::FilePath& path,
294 int /* unused_index */,
295 void* /*unused_params */) {
296 #if defined(ENABLE_WEBRTC)
297 aec_dump_file_path_ = path;
298 EnableAecDumpOnAllRenderProcessHosts();
299 #endif
302 void WebRTCInternals::FileSelectionCanceled(void* params) {
303 #if defined(ENABLE_WEBRTC)
304 SendUpdate("aecRecordingFileSelectionCancelled", NULL);
305 #endif
308 void WebRTCInternals::OnRendererExit(int render_process_id) {
309 DCHECK_CURRENTLY_ON(BrowserThread::UI);
311 // Iterates from the end of the list to remove the PeerConnections created
312 // by the exitting renderer.
313 for (int i = peer_connection_data_.GetSize() - 1; i >= 0; --i) {
314 base::DictionaryValue* record = NULL;
315 peer_connection_data_.GetDictionary(i, &record);
317 int this_rid = 0;
318 record->GetInteger("rid", &this_rid);
320 if (this_rid == render_process_id) {
321 if (observers_.might_have_observers()) {
322 int lid = 0, pid = 0;
323 record->GetInteger("lid", &lid);
324 record->GetInteger("pid", &pid);
326 base::DictionaryValue update;
327 update.SetInteger("lid", lid);
328 update.SetInteger("pid", pid);
329 SendUpdate("removePeerConnection", &update);
331 peer_connection_data_.Remove(i, NULL);
334 CreateOrReleasePowerSaveBlocker();
336 bool found_any = false;
337 // Iterates from the end of the list to remove the getUserMedia requests
338 // created by the exiting renderer.
339 for (int i = get_user_media_requests_.GetSize() - 1; i >= 0; --i) {
340 base::DictionaryValue* record = NULL;
341 get_user_media_requests_.GetDictionary(i, &record);
343 int this_rid = 0;
344 record->GetInteger("rid", &this_rid);
346 if (this_rid == render_process_id) {
347 get_user_media_requests_.Remove(i, NULL);
348 found_any = true;
352 if (found_any && observers_.might_have_observers()) {
353 base::DictionaryValue update;
354 update.SetInteger("rid", render_process_id);
355 SendUpdate("removeGetUserMediaForRenderer", &update);
359 #if defined(ENABLE_WEBRTC)
360 void WebRTCInternals::EnableAecDumpOnAllRenderProcessHosts() {
361 aec_dump_enabled_ = true;
362 for (RenderProcessHost::iterator i(
363 content::RenderProcessHost::AllHostsIterator());
364 !i.IsAtEnd(); i.Advance()) {
365 i.GetCurrentValue()->EnableAecDump(aec_dump_file_path_);
368 #endif
370 void WebRTCInternals::CreateOrReleasePowerSaveBlocker() {
371 DCHECK_CURRENTLY_ON(BrowserThread::UI);
373 if (peer_connection_data_.empty() && power_save_blocker_) {
374 DVLOG(1) << ("Releasing the block on application suspension since no "
375 "PeerConnections are active anymore.");
376 power_save_blocker_.reset();
377 } else if (!peer_connection_data_.empty() && !power_save_blocker_) {
378 DVLOG(1) << ("Preventing the application from being suspended while one or "
379 "more PeerConnections are active.");
380 power_save_blocker_ =
381 content::PowerSaveBlocker::Create(
382 PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension,
383 PowerSaveBlocker::kReasonOther,
384 "WebRTC has active PeerConnections").Pass();
388 } // namespace content