Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / content / browser / media / webrtc_internals.cc
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.
4
5 #include "content/browser/media/webrtc_internals.h"
6
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"
17
18 using base::ProcessId;
19 using std::string;
20
21 namespace content {
22
23 namespace {
24
25 static base::LazyInstance<WebRTCInternals>::Leaky g_webrtc_internals =
26     LAZY_INSTANCE_INITIALIZER;
27
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);
35   }
36   return log;
37 }
38
39 }  // namespace
40
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"));
59   }
60 #endif  // defined(ENABLE_WEBRTC)
61 }
62
63 WebRTCInternals::~WebRTCInternals() {
64 }
65
66 WebRTCInternals* WebRTCInternals::GetInstance() {
67   return g_webrtc_internals.Pointer();
68 }
69
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(BrowserThread::CurrentlyOn(BrowserThread::UI));
77
78   base::DictionaryValue* dict = new base::DictionaryValue();
79   if (!dict)
80     return;
81
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();
90
91   if (observers_.might_have_observers())
92     SendUpdate("addPeerConnection", dict);
93 }
94
95 void WebRTCInternals::OnRemovePeerConnection(ProcessId pid, int lid) {
96   DCHECK(BrowserThread::CurrentlyOn(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);
100
101     int this_pid = 0;
102     int this_lid = 0;
103     dict->GetInteger("pid", &this_pid);
104     dict->GetInteger("lid", &this_lid);
105
106     if (this_pid != static_cast<int>(pid) || this_lid != lid)
107       continue;
108
109     peer_connection_data_.Remove(i, NULL);
110     CreateOrReleasePowerSaveBlocker();
111
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);
117     }
118     break;
119   }
120 }
121
122 void WebRTCInternals::OnUpdatePeerConnection(
123     ProcessId pid, int lid, const string& type, const string& value) {
124   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
125
126   for (size_t i = 0; i < peer_connection_data_.GetSize(); ++i) {
127     base::DictionaryValue* record = NULL;
128     peer_connection_data_.GetDictionary(i, &record);
129
130     int this_pid = 0, this_lid = 0;
131     record->GetInteger("pid", &this_pid);
132     record->GetInteger("lid", &this_lid);
133
134     if (this_pid != static_cast<int>(pid) || this_lid != lid)
135       continue;
136
137     // Append the update to the end of the log.
138     base::ListValue* log = EnsureLogList(record);
139     if (!log)
140       return;
141
142     base::DictionaryValue* log_entry = new base::DictionaryValue();
143     if (!log_entry)
144       return;
145
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);
152
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);
158
159       SendUpdate("updatePeerConnection", &update);
160     }
161     return;
162   }
163 }
164
165 void WebRTCInternals::OnAddStats(base::ProcessId pid, int lid,
166                                  const base::ListValue& value) {
167   if (!observers_.might_have_observers())
168     return;
169
170   base::DictionaryValue dict;
171   dict.SetInteger("pid", static_cast<int>(pid));
172   dict.SetInteger("lid", lid);
173
174   base::ListValue* list = value.DeepCopy();
175   if (!list)
176     return;
177
178   dict.Set("reports", list);
179
180   SendUpdate("addStats", &dict);
181 }
182
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(BrowserThread::CurrentlyOn(BrowserThread::UI));
191
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);
200
201   get_user_media_requests_.Append(dict);
202
203   if (observers_.might_have_observers())
204     SendUpdate("addGetUserMedia", dict);
205 }
206
207 void WebRTCInternals::AddObserver(WebRTCInternalsUIObserver *observer) {
208   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
209   observers_.AddObserver(observer);
210 }
211
212 void WebRTCInternals::RemoveObserver(WebRTCInternalsUIObserver *observer) {
213   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
214   observers_.RemoveObserver(observer);
215
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();
220 }
221
222 void WebRTCInternals::UpdateObserver(WebRTCInternalsUIObserver* observer) {
223   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
224   if (peer_connection_data_.GetSize() > 0)
225     observer->OnUpdate("updateAllPeerConnections", &peer_connection_data_);
226
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);
231   }
232 }
233
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,
245       0,
246       FILE_PATH_LITERAL(""),
247       web_contents->GetTopLevelNativeWindow(),
248       NULL);
249 #endif
250 #endif
251 }
252
253 void WebRTCInternals::DisableAecDump() {
254 #if defined(ENABLE_WEBRTC)
255   aec_dump_enabled_ = false;
256
257   // Tear down the dialog since the user has unchecked the AEC dump box.
258   select_file_dialog_ = NULL;
259
260   for (RenderProcessHost::iterator i(
261            content::RenderProcessHost::AllHostsIterator());
262        !i.IsAtEnd(); i.Advance()) {
263     i.GetCurrentValue()->DisableAecDump();
264   }
265 #endif
266 }
267
268 void WebRTCInternals::ResetForTesting() {
269   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
270   observers_.Clear();
271   peer_connection_data_.Clear();
272   CreateOrReleasePowerSaveBlocker();
273   get_user_media_requests_.Clear();
274   aec_dump_enabled_ = false;
275 }
276
277 void WebRTCInternals::SendUpdate(const string& command, base::Value* value) {
278   DCHECK(observers_.might_have_observers());
279
280   FOR_EACH_OBSERVER(WebRTCInternalsUIObserver,
281                     observers_,
282                     OnUpdate(command, value));
283 }
284
285 void WebRTCInternals::Observe(int type,
286                               const NotificationSource& source,
287                               const NotificationDetails& details) {
288   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
289   DCHECK_EQ(type, NOTIFICATION_RENDERER_PROCESS_TERMINATED);
290   OnRendererExit(Source<RenderProcessHost>(source)->GetID());
291 }
292
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
300 }
301
302 void WebRTCInternals::FileSelectionCanceled(void* params) {
303 #if defined(ENABLE_WEBRTC)
304   SendUpdate("aecRecordingFileSelectionCancelled", NULL);
305 #endif
306 }
307
308 void WebRTCInternals::OnRendererExit(int render_process_id) {
309   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
310
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);
316
317     int this_rid = 0;
318     record->GetInteger("rid", &this_rid);
319
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);
325
326         base::DictionaryValue update;
327         update.SetInteger("lid", lid);
328         update.SetInteger("pid", pid);
329         SendUpdate("removePeerConnection", &update);
330       }
331       peer_connection_data_.Remove(i, NULL);
332     }
333   }
334   CreateOrReleasePowerSaveBlocker();
335
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);
342
343     int this_rid = 0;
344     record->GetInteger("rid", &this_rid);
345
346     if (this_rid == render_process_id) {
347       get_user_media_requests_.Remove(i, NULL);
348       found_any = true;
349     }
350   }
351
352   if (found_any && observers_.might_have_observers()) {
353     base::DictionaryValue update;
354     update.SetInteger("rid", render_process_id);
355     SendUpdate("removeGetUserMediaForRenderer", &update);
356   }
357 }
358
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_);
366   }
367 }
368 #endif
369
370 void WebRTCInternals::CreateOrReleasePowerSaveBlocker() {
371   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
372
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_ = content::PowerSaveBlocker::Create(
381         content::PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension,
382         "WebRTC has active PeerConnections.").Pass();
383   }
384 }
385
386 }  // namespace content