Apply _RELATIVE relocations ahead of others.
[chromium-blink-merge.git] / content / browser / media / webrtc_internals.cc
blobd8a0314466c3930839e820a09045db574754536c
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/path_service.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "content/browser/media/webrtc_internals_ui_observer.h"
10 #include "content/browser/web_contents/web_contents_view.h"
11 #include "content/public/browser/browser_thread.h"
12 #include "content/public/browser/content_browser_client.h"
13 #include "content/public/browser/notification_service.h"
14 #include "content/public/browser/notification_types.h"
15 #include "content/public/browser/power_save_blocker.h"
16 #include "content/public/browser/render_process_host.h"
17 #include "content/public/browser/web_contents.h"
19 using base::ProcessId;
20 using std::string;
22 namespace content {
24 namespace {
26 static base::LazyInstance<WebRTCInternals>::Leaky g_webrtc_internals =
27 LAZY_INSTANCE_INITIALIZER;
29 // Makes sure that |dict| has a ListValue under path "log".
30 static base::ListValue* EnsureLogList(base::DictionaryValue* dict) {
31 base::ListValue* log = NULL;
32 if (!dict->GetList("log", &log)) {
33 log = new base::ListValue();
34 if (log)
35 dict->Set("log", log);
37 return log;
40 } // namespace
42 WebRTCInternals::WebRTCInternals()
43 : aec_dump_enabled_(false) {
44 registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED,
45 NotificationService::AllBrowserContextsAndSources());
46 // TODO(grunell): Shouldn't all the webrtc_internals* files be excluded from the
47 // build if WebRTC is disabled?
48 #if defined(ENABLE_WEBRTC)
49 aec_dump_file_path_ =
50 GetContentClient()->browser()->GetDefaultDownloadDirectory();
51 if (aec_dump_file_path_.empty()) {
52 // In this case the default path (|aec_dump_file_path_|) will be empty and
53 // the platform default path will be used in the file dialog (with no
54 // default file name). See SelectFileDialog::SelectFile. On Android where
55 // there's no dialog we'll fail to open the file.
56 VLOG(1) << "Could not get the download directory.";
57 } else {
58 aec_dump_file_path_ =
59 aec_dump_file_path_.Append(FILE_PATH_LITERAL("audio.aecdump"));
61 #endif // defined(ENABLE_WEBRTC)
64 WebRTCInternals::~WebRTCInternals() {
67 WebRTCInternals* WebRTCInternals::GetInstance() {
68 return g_webrtc_internals.Pointer();
71 void WebRTCInternals::OnAddPeerConnection(int render_process_id,
72 ProcessId pid,
73 int lid,
74 const string& url,
75 const string& rtc_configuration,
76 const string& constraints) {
77 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
79 base::DictionaryValue* dict = new base::DictionaryValue();
80 if (!dict)
81 return;
83 dict->SetInteger("rid", render_process_id);
84 dict->SetInteger("pid", static_cast<int>(pid));
85 dict->SetInteger("lid", lid);
86 dict->SetString("rtcConfiguration", rtc_configuration);
87 dict->SetString("constraints", constraints);
88 dict->SetString("url", url);
89 peer_connection_data_.Append(dict);
90 CreateOrReleasePowerSaveBlocker();
92 if (observers_.might_have_observers())
93 SendUpdate("addPeerConnection", dict);
96 void WebRTCInternals::OnRemovePeerConnection(ProcessId pid, int lid) {
97 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
98 for (size_t i = 0; i < peer_connection_data_.GetSize(); ++i) {
99 base::DictionaryValue* dict = NULL;
100 peer_connection_data_.GetDictionary(i, &dict);
102 int this_pid = 0;
103 int this_lid = 0;
104 dict->GetInteger("pid", &this_pid);
105 dict->GetInteger("lid", &this_lid);
107 if (this_pid != static_cast<int>(pid) || this_lid != lid)
108 continue;
110 peer_connection_data_.Remove(i, NULL);
111 CreateOrReleasePowerSaveBlocker();
113 if (observers_.might_have_observers()) {
114 base::DictionaryValue id;
115 id.SetInteger("pid", static_cast<int>(pid));
116 id.SetInteger("lid", lid);
117 SendUpdate("removePeerConnection", &id);
119 break;
123 void WebRTCInternals::OnUpdatePeerConnection(
124 ProcessId pid, int lid, const string& type, const string& value) {
125 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
127 for (size_t i = 0; i < peer_connection_data_.GetSize(); ++i) {
128 base::DictionaryValue* record = NULL;
129 peer_connection_data_.GetDictionary(i, &record);
131 int this_pid = 0, this_lid = 0;
132 record->GetInteger("pid", &this_pid);
133 record->GetInteger("lid", &this_lid);
135 if (this_pid != static_cast<int>(pid) || this_lid != lid)
136 continue;
138 // Append the update to the end of the log.
139 base::ListValue* log = EnsureLogList(record);
140 if (!log)
141 return;
143 base::DictionaryValue* log_entry = new base::DictionaryValue();
144 if (!log_entry)
145 return;
147 double epoch_time = base::Time::Now().ToJsTime();
148 string time = base::DoubleToString(epoch_time);
149 log_entry->SetString("time", time);
150 log_entry->SetString("type", type);
151 log_entry->SetString("value", value);
152 log->Append(log_entry);
154 if (observers_.might_have_observers()) {
155 base::DictionaryValue update;
156 update.SetInteger("pid", static_cast<int>(pid));
157 update.SetInteger("lid", lid);
158 update.MergeDictionary(log_entry);
160 SendUpdate("updatePeerConnection", &update);
162 return;
166 void WebRTCInternals::OnAddStats(base::ProcessId pid, int lid,
167 const base::ListValue& value) {
168 if (!observers_.might_have_observers())
169 return;
171 base::DictionaryValue dict;
172 dict.SetInteger("pid", static_cast<int>(pid));
173 dict.SetInteger("lid", lid);
175 base::ListValue* list = value.DeepCopy();
176 if (!list)
177 return;
179 dict.Set("reports", list);
181 SendUpdate("addStats", &dict);
184 void WebRTCInternals::OnGetUserMedia(int rid,
185 base::ProcessId pid,
186 const std::string& origin,
187 bool audio,
188 bool video,
189 const std::string& audio_constraints,
190 const std::string& video_constraints) {
191 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
193 base::DictionaryValue* dict = new base::DictionaryValue();
194 dict->SetInteger("rid", rid);
195 dict->SetInteger("pid", static_cast<int>(pid));
196 dict->SetString("origin", origin);
197 if (audio)
198 dict->SetString("audio", audio_constraints);
199 if (video)
200 dict->SetString("video", video_constraints);
202 get_user_media_requests_.Append(dict);
204 if (observers_.might_have_observers())
205 SendUpdate("addGetUserMedia", dict);
208 void WebRTCInternals::AddObserver(WebRTCInternalsUIObserver *observer) {
209 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
210 observers_.AddObserver(observer);
213 void WebRTCInternals::RemoveObserver(WebRTCInternalsUIObserver *observer) {
214 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
215 observers_.RemoveObserver(observer);
217 // Disables the AEC recording if it is enabled and the last webrtc-internals
218 // page is going away.
219 if (aec_dump_enabled_ && !observers_.might_have_observers())
220 DisableAecDump();
223 void WebRTCInternals::UpdateObserver(WebRTCInternalsUIObserver* observer) {
224 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
225 if (peer_connection_data_.GetSize() > 0)
226 observer->OnUpdate("updateAllPeerConnections", &peer_connection_data_);
228 for (base::ListValue::iterator it = get_user_media_requests_.begin();
229 it != get_user_media_requests_.end();
230 ++it) {
231 observer->OnUpdate("addGetUserMedia", *it);
235 void WebRTCInternals::EnableAecDump(content::WebContents* web_contents) {
236 #if defined(ENABLE_WEBRTC)
237 #if defined(OS_ANDROID)
238 EnableAecDumpOnAllRenderProcessHosts();
239 #else
240 select_file_dialog_ = ui::SelectFileDialog::Create(this, NULL);
241 select_file_dialog_->SelectFile(
242 ui::SelectFileDialog::SELECT_SAVEAS_FILE,
243 base::string16(),
244 aec_dump_file_path_,
245 NULL,
247 FILE_PATH_LITERAL(""),
248 web_contents->GetTopLevelNativeWindow(),
249 NULL);
250 #endif
251 #endif
254 void WebRTCInternals::DisableAecDump() {
255 #if defined(ENABLE_WEBRTC)
256 aec_dump_enabled_ = false;
258 // Tear down the dialog since the user has unchecked the AEC dump box.
259 select_file_dialog_ = NULL;
261 for (RenderProcessHost::iterator i(
262 content::RenderProcessHost::AllHostsIterator());
263 !i.IsAtEnd(); i.Advance()) {
264 i.GetCurrentValue()->DisableAecDump();
266 #endif
269 void WebRTCInternals::ResetForTesting() {
270 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
271 observers_.Clear();
272 peer_connection_data_.Clear();
273 CreateOrReleasePowerSaveBlocker();
274 get_user_media_requests_.Clear();
275 aec_dump_enabled_ = false;
278 void WebRTCInternals::SendUpdate(const string& command, base::Value* value) {
279 DCHECK(observers_.might_have_observers());
281 FOR_EACH_OBSERVER(WebRTCInternalsUIObserver,
282 observers_,
283 OnUpdate(command, value));
286 void WebRTCInternals::Observe(int type,
287 const NotificationSource& source,
288 const NotificationDetails& details) {
289 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
290 DCHECK_EQ(type, NOTIFICATION_RENDERER_PROCESS_TERMINATED);
291 OnRendererExit(Source<RenderProcessHost>(source)->GetID());
294 void WebRTCInternals::FileSelected(const base::FilePath& path,
295 int /* unused_index */,
296 void* /*unused_params */) {
297 #if defined(ENABLE_WEBRTC)
298 aec_dump_file_path_ = path;
299 EnableAecDumpOnAllRenderProcessHosts();
300 #endif
303 void WebRTCInternals::FileSelectionCanceled(void* params) {
304 #if defined(ENABLE_WEBRTC)
305 SendUpdate("aecRecordingFileSelectionCancelled", NULL);
306 #endif
309 void WebRTCInternals::OnRendererExit(int render_process_id) {
310 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
312 // Iterates from the end of the list to remove the PeerConnections created
313 // by the exitting renderer.
314 for (int i = peer_connection_data_.GetSize() - 1; i >= 0; --i) {
315 base::DictionaryValue* record = NULL;
316 peer_connection_data_.GetDictionary(i, &record);
318 int this_rid = 0;
319 record->GetInteger("rid", &this_rid);
321 if (this_rid == render_process_id) {
322 if (observers_.might_have_observers()) {
323 int lid = 0, pid = 0;
324 record->GetInteger("lid", &lid);
325 record->GetInteger("pid", &pid);
327 base::DictionaryValue update;
328 update.SetInteger("lid", lid);
329 update.SetInteger("pid", pid);
330 SendUpdate("removePeerConnection", &update);
332 peer_connection_data_.Remove(i, NULL);
335 CreateOrReleasePowerSaveBlocker();
337 bool found_any = false;
338 // Iterates from the end of the list to remove the getUserMedia requests
339 // created by the exiting renderer.
340 for (int i = get_user_media_requests_.GetSize() - 1; i >= 0; --i) {
341 base::DictionaryValue* record = NULL;
342 get_user_media_requests_.GetDictionary(i, &record);
344 int this_rid = 0;
345 record->GetInteger("rid", &this_rid);
347 if (this_rid == render_process_id) {
348 get_user_media_requests_.Remove(i, NULL);
349 found_any = true;
353 if (found_any && observers_.might_have_observers()) {
354 base::DictionaryValue update;
355 update.SetInteger("rid", render_process_id);
356 SendUpdate("removeGetUserMediaForRenderer", &update);
360 #if defined(ENABLE_WEBRTC)
361 void WebRTCInternals::EnableAecDumpOnAllRenderProcessHosts() {
362 aec_dump_enabled_ = true;
363 for (RenderProcessHost::iterator i(
364 content::RenderProcessHost::AllHostsIterator());
365 !i.IsAtEnd(); i.Advance()) {
366 i.GetCurrentValue()->EnableAecDump(aec_dump_file_path_);
369 #endif
371 void WebRTCInternals::CreateOrReleasePowerSaveBlocker() {
372 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
374 if (peer_connection_data_.empty() && power_save_blocker_) {
375 DVLOG(1) << ("Releasing the block on application suspension since no "
376 "PeerConnections are active anymore.");
377 power_save_blocker_.reset();
378 } else if (!peer_connection_data_.empty() && !power_save_blocker_) {
379 DVLOG(1) << ("Preventing the application from being suspended while one or "
380 "more PeerConnections are active.");
381 power_save_blocker_ = content::PowerSaveBlocker::Create(
382 content::PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension,
383 "WebRTC has active PeerConnections.").Pass();
387 } // namespace content