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/path_service.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/render_process_host.h" 15 #include "content/public/browser/web_contents.h" 16 17 using base::ProcessId; 18 using std::string; 19 20 namespace content { 21 22 namespace { 23 // Makes sure that |dict| has a ListValue under path "log". 24 static base::ListValue* EnsureLogList(base::DictionaryValue* dict) { 25 base::ListValue* log = NULL; 26 if (!dict->GetList("log", &log)) { 27 log = new base::ListValue(); 28 if (log) 29 dict->Set("log", log); 30 } 31 return log; 32 } 33 34 } // namespace 35 36 WebRTCInternals::WebRTCInternals() 37 : aec_dump_enabled_(false) { 38 registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED, 39 NotificationService::AllBrowserContextsAndSources()); 40 // TODO(grunell): Shouldn't all the webrtc_internals* files be excluded from the 41 // build if WebRTC is disabled? 42 #if defined(ENABLE_WEBRTC) 43 aec_dump_file_path_ = 44 GetContentClient()->browser()->GetDefaultDownloadDirectory(); 45 if (aec_dump_file_path_.empty()) { 46 // In this case the default path (|aec_dump_file_path_|) will be empty and 47 // the platform default path will be used in the file dialog (with no 48 // default file name). See SelectFileDialog::SelectFile. On Android where 49 // there's no dialog we'll fail to open the file. 50 VLOG(1) << "Could not get the download directory."; 51 } else { 52 aec_dump_file_path_ = 53 aec_dump_file_path_.Append(FILE_PATH_LITERAL("audio.aecdump")); 54 } 55 #endif // defined(ENABLE_WEBRTC) 56 } 57 58 WebRTCInternals::~WebRTCInternals() { 59 } 60 61 WebRTCInternals* WebRTCInternals::GetInstance() { 62 return Singleton<WebRTCInternals>::get(); 63 } 64 65 void WebRTCInternals::OnAddPeerConnection(int render_process_id, 66 ProcessId pid, 67 int lid, 68 const string& url, 69 const string& servers, 70 const string& constraints) { 71 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 72 73 base::DictionaryValue* dict = new base::DictionaryValue(); 74 if (!dict) 75 return; 76 77 dict->SetInteger("rid", render_process_id); 78 dict->SetInteger("pid", static_cast<int>(pid)); 79 dict->SetInteger("lid", lid); 80 dict->SetString("servers", servers); 81 dict->SetString("constraints", constraints); 82 dict->SetString("url", url); 83 peer_connection_data_.Append(dict); 84 85 if (observers_.might_have_observers()) 86 SendUpdate("addPeerConnection", dict); 87 } 88 89 void WebRTCInternals::OnRemovePeerConnection(ProcessId pid, int lid) { 90 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 91 for (size_t i = 0; i < peer_connection_data_.GetSize(); ++i) { 92 base::DictionaryValue* dict = NULL; 93 peer_connection_data_.GetDictionary(i, &dict); 94 95 int this_pid = 0; 96 int this_lid = 0; 97 dict->GetInteger("pid", &this_pid); 98 dict->GetInteger("lid", &this_lid); 99 100 if (this_pid != static_cast<int>(pid) || this_lid != lid) 101 continue; 102 103 peer_connection_data_.Remove(i, NULL); 104 105 if (observers_.might_have_observers()) { 106 base::DictionaryValue id; 107 id.SetInteger("pid", static_cast<int>(pid)); 108 id.SetInteger("lid", lid); 109 SendUpdate("removePeerConnection", &id); 110 } 111 break; 112 } 113 } 114 115 void WebRTCInternals::OnUpdatePeerConnection( 116 ProcessId pid, int lid, const string& type, const string& value) { 117 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 118 119 for (size_t i = 0; i < peer_connection_data_.GetSize(); ++i) { 120 base::DictionaryValue* record = NULL; 121 peer_connection_data_.GetDictionary(i, &record); 122 123 int this_pid = 0, this_lid = 0; 124 record->GetInteger("pid", &this_pid); 125 record->GetInteger("lid", &this_lid); 126 127 if (this_pid != static_cast<int>(pid) || this_lid != lid) 128 continue; 129 130 // Append the update to the end of the log. 131 base::ListValue* log = EnsureLogList(record); 132 if (!log) 133 return; 134 135 base::DictionaryValue* log_entry = new base::DictionaryValue(); 136 if (!log_entry) 137 return; 138 139 log_entry->SetString("type", type); 140 log_entry->SetString("value", value); 141 log->Append(log_entry); 142 143 if (observers_.might_have_observers()) { 144 base::DictionaryValue update; 145 update.SetInteger("pid", static_cast<int>(pid)); 146 update.SetInteger("lid", lid); 147 update.SetString("type", type); 148 update.SetString("value", value); 149 150 SendUpdate("updatePeerConnection", &update); 151 } 152 return; 153 } 154 } 155 156 void WebRTCInternals::OnAddStats(base::ProcessId pid, int lid, 157 const base::ListValue& value) { 158 if (!observers_.might_have_observers()) 159 return; 160 161 base::DictionaryValue dict; 162 dict.SetInteger("pid", static_cast<int>(pid)); 163 dict.SetInteger("lid", lid); 164 165 base::ListValue* list = value.DeepCopy(); 166 if (!list) 167 return; 168 169 dict.Set("reports", list); 170 171 SendUpdate("addStats", &dict); 172 } 173 174 void WebRTCInternals::OnGetUserMedia(int rid, 175 base::ProcessId pid, 176 const std::string& origin, 177 bool audio, 178 bool video, 179 const std::string& audio_constraints, 180 const std::string& video_constraints) { 181 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 182 183 base::DictionaryValue* dict = new base::DictionaryValue(); 184 dict->SetInteger("rid", rid); 185 dict->SetInteger("pid", static_cast<int>(pid)); 186 dict->SetString("origin", origin); 187 if (audio) 188 dict->SetString("audio", audio_constraints); 189 if (video) 190 dict->SetString("video", video_constraints); 191 192 get_user_media_requests_.Append(dict); 193 194 if (observers_.might_have_observers()) 195 SendUpdate("addGetUserMedia", dict); 196 } 197 198 void WebRTCInternals::AddObserver(WebRTCInternalsUIObserver *observer) { 199 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 200 observers_.AddObserver(observer); 201 } 202 203 void WebRTCInternals::RemoveObserver(WebRTCInternalsUIObserver *observer) { 204 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 205 observers_.RemoveObserver(observer); 206 207 // Disables the AEC recording if it is enabled and the last webrtc-internals 208 // page is going away. 209 if (aec_dump_enabled_ && !observers_.might_have_observers()) 210 DisableAecDump(); 211 } 212 213 void WebRTCInternals::UpdateObserver(WebRTCInternalsUIObserver* observer) { 214 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 215 if (peer_connection_data_.GetSize() > 0) 216 observer->OnUpdate("updateAllPeerConnections", &peer_connection_data_); 217 218 for (base::ListValue::iterator it = get_user_media_requests_.begin(); 219 it != get_user_media_requests_.end(); 220 ++it) { 221 observer->OnUpdate("addGetUserMedia", *it); 222 } 223 } 224 225 void WebRTCInternals::EnableAecDump(content::WebContents* web_contents) { 226 #if defined(ENABLE_WEBRTC) 227 #if defined(OS_ANDROID) 228 EnableAecDumpOnAllRenderProcessHosts(); 229 #else 230 select_file_dialog_ = ui::SelectFileDialog::Create(this, NULL); 231 select_file_dialog_->SelectFile( 232 ui::SelectFileDialog::SELECT_SAVEAS_FILE, 233 base::string16(), 234 aec_dump_file_path_, 235 NULL, 236 0, 237 FILE_PATH_LITERAL(""), 238 web_contents->GetTopLevelNativeWindow(), 239 NULL); 240 #endif 241 #endif 242 } 243 244 void WebRTCInternals::DisableAecDump() { 245 #if defined(ENABLE_WEBRTC) 246 aec_dump_enabled_ = false; 247 248 // Tear down the dialog since the user has unchecked the AEC dump box. 249 select_file_dialog_ = NULL; 250 251 for (RenderProcessHost::iterator i( 252 content::RenderProcessHost::AllHostsIterator()); 253 !i.IsAtEnd(); i.Advance()) { 254 i.GetCurrentValue()->DisableAecDump(); 255 } 256 #endif 257 } 258 259 void WebRTCInternals::ResetForTesting() { 260 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 261 observers_.Clear(); 262 peer_connection_data_.Clear(); 263 get_user_media_requests_.Clear(); 264 aec_dump_enabled_ = false; 265 } 266 267 void WebRTCInternals::SendUpdate(const string& command, base::Value* value) { 268 DCHECK(observers_.might_have_observers()); 269 270 FOR_EACH_OBSERVER(WebRTCInternalsUIObserver, 271 observers_, 272 OnUpdate(command, value)); 273 } 274 275 void WebRTCInternals::Observe(int type, 276 const NotificationSource& source, 277 const NotificationDetails& details) { 278 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 279 DCHECK_EQ(type, NOTIFICATION_RENDERER_PROCESS_TERMINATED); 280 OnRendererExit(Source<RenderProcessHost>(source)->GetID()); 281 } 282 283 void WebRTCInternals::FileSelected(const base::FilePath& path, 284 int /* unused_index */, 285 void* /*unused_params */) { 286 #if defined(ENABLE_WEBRTC) 287 aec_dump_file_path_ = path; 288 EnableAecDumpOnAllRenderProcessHosts(); 289 #endif 290 } 291 292 void WebRTCInternals::FileSelectionCanceled(void* params) { 293 #if defined(ENABLE_WEBRTC) 294 SendUpdate("aecRecordingFileSelectionCancelled", NULL); 295 #endif 296 } 297 298 void WebRTCInternals::OnRendererExit(int render_process_id) { 299 // Iterates from the end of the list to remove the PeerConnections created 300 // by the exitting renderer. 301 for (int i = peer_connection_data_.GetSize() - 1; i >= 0; --i) { 302 base::DictionaryValue* record = NULL; 303 peer_connection_data_.GetDictionary(i, &record); 304 305 int this_rid = 0; 306 record->GetInteger("rid", &this_rid); 307 308 if (this_rid == render_process_id) { 309 if (observers_.might_have_observers()) { 310 int lid = 0, pid = 0; 311 record->GetInteger("lid", &lid); 312 record->GetInteger("pid", &pid); 313 314 base::DictionaryValue update; 315 update.SetInteger("lid", lid); 316 update.SetInteger("pid", pid); 317 SendUpdate("removePeerConnection", &update); 318 } 319 peer_connection_data_.Remove(i, NULL); 320 } 321 } 322 323 bool found_any = false; 324 // Iterates from the end of the list to remove the getUserMedia requests 325 // created by the exiting renderer. 326 for (int i = get_user_media_requests_.GetSize() - 1; i >= 0; --i) { 327 base::DictionaryValue* record = NULL; 328 get_user_media_requests_.GetDictionary(i, &record); 329 330 int this_rid = 0; 331 record->GetInteger("rid", &this_rid); 332 333 if (this_rid == render_process_id) { 334 get_user_media_requests_.Remove(i, NULL); 335 found_any = true; 336 } 337 } 338 339 if (found_any && observers_.might_have_observers()) { 340 base::DictionaryValue update; 341 update.SetInteger("rid", render_process_id); 342 SendUpdate("removeGetUserMediaForRenderer", &update); 343 } 344 } 345 346 #if defined(ENABLE_WEBRTC) 347 void WebRTCInternals::EnableAecDumpOnAllRenderProcessHosts() { 348 aec_dump_enabled_ = true; 349 for (RenderProcessHost::iterator i( 350 content::RenderProcessHost::AllHostsIterator()); 351 !i.IsAtEnd(); i.Advance()) { 352 i.GetCurrentValue()->EnableAecDump(aec_dump_file_path_); 353 } 354 } 355 #endif 356 357 } // namespace content 358