Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(387)

Side by Side Diff: content/browser/renderer_host/media/audio_renderer_host.cc

Issue 1856673002: Mojofication of the Chrome Audio Rendering Prototype Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/renderer_host/media/audio_renderer_host.h" 5 #include "content/browser/renderer_host/media/audio_renderer_host.h"
6 6
7 #include <stdint.h> 7 #include <stdint.h>
8 #include <utility> 8 #include <utility>
9 9
10 #include "base/bind.h" 10 #include "base/bind.h"
(...skipping 13 matching lines...) Expand all
24 #include "content/browser/renderer_host/media/audio_sync_reader.h" 24 #include "content/browser/renderer_host/media/audio_sync_reader.h"
25 #include "content/browser/renderer_host/media/media_stream_manager.h" 25 #include "content/browser/renderer_host/media/media_stream_manager.h"
26 #include "content/browser/renderer_host/media/media_stream_ui_proxy.h" 26 #include "content/browser/renderer_host/media/media_stream_ui_proxy.h"
27 #include "content/browser/renderer_host/render_widget_host_impl.h" 27 #include "content/browser/renderer_host/render_widget_host_impl.h"
28 #include "content/common/media/audio_messages.h" 28 #include "content/common/media/audio_messages.h"
29 #include "content/public/browser/content_browser_client.h" 29 #include "content/public/browser/content_browser_client.h"
30 #include "content/public/browser/media_device_id.h" 30 #include "content/public/browser/media_device_id.h"
31 #include "content/public/browser/media_observer.h" 31 #include "content/public/browser/media_observer.h"
32 #include "content/public/browser/render_frame_host.h" 32 #include "content/public/browser/render_frame_host.h"
33 #include "content/public/common/content_switches.h" 33 #include "content/public/common/content_switches.h"
34 #include "content/renderer/media/audio_output_client.h"
34 #include "media/audio/audio_device_name.h" 35 #include "media/audio/audio_device_name.h"
35 #include "media/audio/audio_manager_base.h" 36 #include "media/audio/audio_manager_base.h"
36 #include "media/audio/audio_streams_tracker.h" 37 #include "media/audio/audio_streams_tracker.h"
37 #include "media/base/audio_bus.h" 38 #include "media/base/audio_bus.h"
38 #include "media/base/limits.h" 39 #include "media/base/limits.h"
40 #include "mojo/edk/embedder/embedder.h"
41 #include "mojo/public/cpp/system/handle.h"
39 42
40 using media::AudioBus; 43 using media::AudioBus;
41 using media::AudioManager; 44 using media::AudioManager;
42 45
43 namespace content { 46 namespace content {
44 47
45 namespace { 48 namespace {
46 49
47 // Tracks the maximum number of simultaneous output streams browser-wide. 50 // Tracks the maximum number of simultaneous output streams browser-wide.
48 // Accessed on IO thread. 51 // Accessed on IO thread.
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
132 class AudioRendererHost::AudioEntry 135 class AudioRendererHost::AudioEntry
133 : public media::AudioOutputController::EventHandler { 136 : public media::AudioOutputController::EventHandler {
134 public: 137 public:
135 AudioEntry(AudioRendererHost* host, 138 AudioEntry(AudioRendererHost* host,
136 int stream_id, 139 int stream_id,
137 int render_frame_id, 140 int render_frame_id,
138 const media::AudioParameters& params, 141 const media::AudioParameters& params,
139 const std::string& output_device_id, 142 const std::string& output_device_id,
140 scoped_ptr<base::SharedMemory> shared_memory, 143 scoped_ptr<base::SharedMemory> shared_memory,
141 scoped_ptr<media::AudioOutputController::SyncReader> reader); 144 scoped_ptr<media::AudioOutputController::SyncReader> reader);
145
146 AudioEntry(AudioRendererHost* host,
147 int stream_id,
148 int render_frame_id,
149 const media::AudioParameters& params,
150 const std::string& output_device_id,
151 scoped_ptr<base::SharedMemory> shared_memory,
152 scoped_ptr<media::AudioOutputController::SyncReader> reader,
153 AudioOutputStreamPtr stream,
154 const AudioOutput::CreateStreamCallback& callback);
155
142 ~AudioEntry() override; 156 ~AudioEntry() override;
143 157
144 int stream_id() const { 158 int stream_id() const {
145 return stream_id_; 159 return stream_id_;
146 } 160 }
147 161
148 int render_frame_id() const { return render_frame_id_; } 162 int render_frame_id() const { return render_frame_id_; }
149 163
150 media::AudioOutputController* controller() const { return controller_.get(); } 164 media::AudioOutputController* controller() const { return controller_.get(); }
151 165
152 base::SharedMemory* shared_memory() { 166 base::SharedMemory* shared_memory() {
153 return shared_memory_.get(); 167 return shared_memory_.get();
154 } 168 }
155 169
156 media::AudioOutputController::SyncReader* reader() const { 170 media::AudioOutputController::SyncReader* reader() const {
157 return reader_.get(); 171 return reader_.get();
158 } 172 }
159 173
160 bool playing() const { return playing_; } 174 bool playing() const { return playing_; }
161 void set_playing(bool playing) { playing_ = playing; } 175 void set_playing(bool playing) { playing_ = playing; }
162 176
163 private: 177 private:
164 // media::AudioOutputController::EventHandler implementation. 178 // media::AudioOutputController::EventHandler implementation.
179 void OnCreated(AudioOutputStreamPtr stream,
180 const AudioOutput::CreateStreamCallback& callback) override;
165 void OnCreated() override; 181 void OnCreated() override;
166 void OnPlaying() override; 182 void OnPlaying() override;
167 void OnPaused() override; 183 void OnPaused() override;
168 void OnError() override; 184 void OnError() override;
169 185
170 AudioRendererHost* const host_; 186 AudioRendererHost* const host_;
171 const int stream_id_; 187 const int stream_id_;
172 188
173 // The routing ID of the source RenderFrame. 189 // The routing ID of the source RenderFrame.
174 const int render_frame_id_; 190 const int render_frame_id_;
(...skipping 25 matching lines...) Expand all
200 reader_(std::move(reader)), 216 reader_(std::move(reader)),
201 controller_(media::AudioOutputController::Create(host->audio_manager_, 217 controller_(media::AudioOutputController::Create(host->audio_manager_,
202 this, 218 this,
203 params, 219 params,
204 output_device_id, 220 output_device_id,
205 reader_.get())), 221 reader_.get())),
206 playing_(false) { 222 playing_(false) {
207 DCHECK(controller_.get()); 223 DCHECK(controller_.get());
208 } 224 }
209 225
226 AudioRendererHost::AudioEntry::AudioEntry(
227 AudioRendererHost* host,
228 int stream_id,
229 int render_frame_id,
230 const media::AudioParameters& params,
231 const std::string& output_device_id,
232 scoped_ptr<base::SharedMemory> shared_memory,
233 scoped_ptr<media::AudioOutputController::SyncReader> reader,
234 AudioOutputStreamPtr stream,
235 const AudioOutput::CreateStreamCallback& callback)
236 : host_(host),
237 stream_id_(stream_id),
238 render_frame_id_(render_frame_id),
239 shared_memory_(std::move(shared_memory)),
240 reader_(std::move(reader)),
241 controller_(media::AudioOutputController::Create(host->audio_manager_,
242 this,
243 params,
244 output_device_id,
245 reader_.get(),
246 std::move(stream),
247 callback)),
248 playing_(false) {
249 DCHECK(controller_.get());
250 }
251
210 AudioRendererHost::AudioEntry::~AudioEntry() {} 252 AudioRendererHost::AudioEntry::~AudioEntry() {}
211 253
212 /////////////////////////////////////////////////////////////////////////////// 254 ///////////////////////////////////////////////////////////////////////////////
213 // AudioRendererHost implementations. 255 // AudioRendererHost implementations.
214 256
215 AudioRendererHost::AudioRendererHost( 257 AudioRendererHost::AudioRendererHost(
216 int render_process_id, 258 int render_process_id,
217 media::AudioManager* audio_manager, 259 media::AudioManager* audio_manager,
218 AudioMirroringManager* mirroring_manager, 260 AudioMirroringManager* mirroring_manager,
219 MediaInternals* media_internals, 261 MediaInternals* media_internals,
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
274 BrowserThread::DeleteOnIOThread::Destruct(this); 316 BrowserThread::DeleteOnIOThread::Destruct(this);
275 } 317 }
276 318
277 void AudioRendererHost::AudioEntry::OnCreated() { 319 void AudioRendererHost::AudioEntry::OnCreated() {
278 BrowserThread::PostTask( 320 BrowserThread::PostTask(
279 BrowserThread::IO, 321 BrowserThread::IO,
280 FROM_HERE, 322 FROM_HERE,
281 base::Bind(&AudioRendererHost::DoCompleteCreation, host_, stream_id_)); 323 base::Bind(&AudioRendererHost::DoCompleteCreation, host_, stream_id_));
282 } 324 }
283 325
326 void AudioRendererHost::AudioEntry::OnCreated(
327 AudioOutputStreamPtr stream,
328 const AudioOutput::CreateStreamCallback& callback) {
329 host_->stream_ptr_ = std::move(stream);
330 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
331 base::Bind(&AudioRendererHost::DoCompleteCreationMojo,
332 host_, stream_id_, callback));
333 }
334
284 void AudioRendererHost::AudioEntry::OnPlaying() { 335 void AudioRendererHost::AudioEntry::OnPlaying() {
285 BrowserThread::PostTask( 336 BrowserThread::PostTask(
286 BrowserThread::IO, 337 BrowserThread::IO,
287 FROM_HERE, 338 FROM_HERE,
288 base::Bind(&AudioRendererHost::DoNotifyStreamStateChanged, 339 base::Bind(&AudioRendererHost::DoNotifyStreamStateChanged,
289 host_, 340 host_,
290 stream_id_, 341 stream_id_,
291 true)); 342 true));
292 } 343 }
293 344
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
343 if (!reader->PrepareForeignSocket(PeerHandle(), &socket_descriptor)) { 394 if (!reader->PrepareForeignSocket(PeerHandle(), &socket_descriptor)) {
344 ReportErrorAndClose(entry->stream_id()); 395 ReportErrorAndClose(entry->stream_id());
345 return; 396 return;
346 } 397 }
347 398
348 Send(new AudioMsg_NotifyStreamCreated( 399 Send(new AudioMsg_NotifyStreamCreated(
349 entry->stream_id(), foreign_memory_handle, socket_descriptor, 400 entry->stream_id(), foreign_memory_handle, socket_descriptor,
350 entry->shared_memory()->requested_size())); 401 entry->shared_memory()->requested_size()));
351 } 402 }
352 403
404 void AudioRendererHost::DoCompleteCreationMojo(
405 int stream_id,
406 const AudioOutput::CreateStreamCallback& callback) {
407 DCHECK_CURRENTLY_ON(BrowserThread::IO);
408
409 if (!PeerHandle()) {
410 DLOG(WARNING) << "Renderer process handle is invalid.";
411 ReportErrorAndClose(stream_id);
412 return;
413 }
414
415 AudioEntry* const entry = LookupById(stream_id);
416 if (!entry) {
417 ReportErrorAndClose(stream_id);
418 return;
419 }
420
421 // Once the audio stream is created then complete the creation process by
422 // mapping shared memory and sharing with the renderer process.
423 base::SharedMemoryHandle foreign_memory_handle;
424 if (!entry->shared_memory()->ShareToProcess(PeerHandle(),
425 &foreign_memory_handle)) {
426 // If we failed to map and share the shared memory then close the audio
427 // stream and send an error message.
428 ReportErrorAndClose(entry->stream_id());
429 return;
430 }
431
432 AudioSyncReader* reader = static_cast<AudioSyncReader*>(entry->reader());
433
434 base::SyncSocket::TransitDescriptor socket_descriptor;
435
436 // If we failed to prepare the sync socket for the renderer then we fail
437 // the construction of audio stream.
438 if (!reader->PrepareForeignSocket(PeerHandle(), &socket_descriptor)) {
439 ReportErrorAndClose(entry->stream_id());
440 return;
441 }
442
443 /*Send(new AudioMsg_NotifyStreamCreated(
444 entry->stream_id(), foreign_memory_handle, socket_descriptor,
445 entry->shared_memory()->requested_size()));*/
446 MojoHandle mojo_foreign_memory_handle;
447
448 MojoResult shared_buffer_result = mojo::edk::CreateSharedBufferWrapper(
449 foreign_memory_handle, entry->shared_memory()->requested_size(), false,
450 &mojo_foreign_memory_handle);
451
452 if (shared_buffer_result != MOJO_RESULT_OK) {
453 LOG(WARNING) << "Failed to wrap transit descriptor. Closing: "
454 << shared_buffer_result;
455 return;
456 }
457
458 MojoHandle socket_descriptor_handle;
459
460 #if defined(OS_WIN)
461 MojoResult platform_handle_result = mojo::edk::CreatePlatformHandleWrapper(
462 mojo::edk::ScopedPlatformHandle(
463 mojo::edk::PlatformHandle(socket_descriptor)),
464 &socket_descriptor_handle);
465 #else
466 MojoResult platform_handle_result = mojo::edk::CreatePlatformHandleWrapper(
467 mojo::edk::ScopedPlatformHandle(
468 mojo::edk::PlatformHandle(socket_descriptor.fd)),
469 &socket_descriptor_handle);
470 #endif
471
472 if (platform_handle_result != MOJO_RESULT_OK) {
473 LOG(WARNING) << "Failed to wrap platform handle. Closing: "
474 << platform_handle_result;
475 return;
476 }
477
478 callback.Run(mojo::ScopedSharedBufferHandle(
479 mojo::SharedBufferHandle(mojo_foreign_memory_handle)),
480 mojo::ScopedHandle(mojo::Handle(socket_descriptor_handle)),
481 std::move(stream_ptr_));
482 }
483
353 void AudioRendererHost::DoNotifyStreamStateChanged(int stream_id, 484 void AudioRendererHost::DoNotifyStreamStateChanged(int stream_id,
354 bool is_playing) { 485 bool is_playing) {
355 DCHECK_CURRENTLY_ON(BrowserThread::IO); 486 DCHECK_CURRENTLY_ON(BrowserThread::IO);
356 487
357 AudioEntry* const entry = LookupById(stream_id); 488 AudioEntry* const entry = LookupById(stream_id);
358 if (!entry) 489 if (!entry)
359 return; 490 return;
360 491
361 Send(new AudioMsg_NotifyStreamStateChanged( 492 Send(new AudioMsg_NotifyStreamStateChanged(
362 stream_id, 493 stream_id,
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after
547 if (auth_data == authorizations_.end()) { 678 if (auth_data == authorizations_.end()) {
548 DoCreateStream(stream_id, render_frame_id, params, std::string()); 679 DoCreateStream(stream_id, render_frame_id, params, std::string());
549 return; 680 return;
550 } 681 }
551 682
552 CHECK(auth_data->second.first); 683 CHECK(auth_data->second.first);
553 DoCreateStream(stream_id, render_frame_id, params, auth_data->second.second); 684 DoCreateStream(stream_id, render_frame_id, params, auth_data->second.second);
554 authorizations_.erase(auth_data); 685 authorizations_.erase(auth_data);
555 } 686 }
556 687
688 void AudioRendererHost::OnCreateStreamMojo(
689 int stream_id,
690 int render_frame_id,
691 const media::AudioParameters& params,
692 AudioOutputStreamPtr stream,
693 const AudioOutput::CreateStreamCallback& callback) {
694 DCHECK_CURRENTLY_ON(BrowserThread::IO);
695 DVLOG(1) << "AudioRendererHost@" << this << "::OnCreateStream"
696 << "(stream_id=" << stream_id << ")";
697
698 const auto& auth_data = authorizations_.find(stream_id);
699
700 // If no previous authorization requested, assume default device
701 if (auth_data == authorizations_.end()) {
702 DoCreateStream(stream_id, render_frame_id, params, std::string(),
703 std::move(stream), callback);
704 return;
705 }
706
707 CHECK(auth_data->second.first);
708 DoCreateStream(stream_id, render_frame_id, params, auth_data->second.second,
709 std::move(stream), callback);
710 authorizations_.erase(auth_data);
711 }
712
557 void AudioRendererHost::DoCreateStream(int stream_id, 713 void AudioRendererHost::DoCreateStream(int stream_id,
558 int render_frame_id, 714 int render_frame_id,
559 const media::AudioParameters& params, 715 const media::AudioParameters& params,
560 const std::string& device_unique_id) { 716 const std::string& device_unique_id) {
561 DCHECK_CURRENTLY_ON(BrowserThread::IO); 717 DCHECK_CURRENTLY_ON(BrowserThread::IO);
562 718
563 // media::AudioParameters is validated in the deserializer. 719 // media::AudioParameters is validated in the deserializer.
564 if (LookupById(stream_id) != NULL) { 720 if (LookupById(stream_id) != NULL) {
565 SendErrorMessage(stream_id); 721 SendErrorMessage(stream_id);
566 return; 722 return;
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
598 g_audio_streams_tracker.Get().IncreaseStreamCount(); 754 g_audio_streams_tracker.Get().IncreaseStreamCount();
599 755
600 audio_log_->OnCreated(stream_id, params, device_unique_id); 756 audio_log_->OnCreated(stream_id, params, device_unique_id);
601 MediaInternals::GetInstance()->SetWebContentsTitleForAudioLogEntry( 757 MediaInternals::GetInstance()->SetWebContentsTitleForAudioLogEntry(
602 stream_id, render_process_id_, render_frame_id, audio_log_.get()); 758 stream_id, render_process_id_, render_frame_id, audio_log_.get());
603 759
604 if (audio_entries_.size() > max_simultaneous_streams_) 760 if (audio_entries_.size() > max_simultaneous_streams_)
605 max_simultaneous_streams_ = audio_entries_.size(); 761 max_simultaneous_streams_ = audio_entries_.size();
606 } 762 }
607 763
764 void AudioRendererHost::DoCreateStream(
765 int stream_id,
766 int render_frame_id,
767 const media::AudioParameters& params,
768 const std::string& device_unique_id,
769 AudioOutputStreamPtr stream,
770 const AudioOutput::CreateStreamCallback& callback) {
771 DCHECK_CURRENTLY_ON(BrowserThread::IO);
772
773 // media::AudioParameters is validated in the deserializer.
774 if (LookupById(stream_id) != NULL) {
775 SendErrorMessage(stream_id);
776 return;
777 }
778
779 // Create the shared memory and share with the renderer process.
780 uint32_t shared_memory_size = sizeof(media::AudioOutputBufferParameters) +
781 AudioBus::CalculateMemorySize(params);
782 scoped_ptr<base::SharedMemory> shared_memory(new base::SharedMemory());
783 if (!shared_memory->CreateAndMapAnonymous(shared_memory_size)) {
784 SendErrorMessage(stream_id);
785 return;
786 }
787
788 scoped_ptr<AudioSyncReader> reader(
789 new AudioSyncReader(shared_memory.get(), params));
790 if (!reader->Init()) {
791 SendErrorMessage(stream_id);
792 return;
793 }
794
795 MediaObserver* const media_observer =
796 GetContentClient()->browser()->GetMediaObserver();
797 if (media_observer)
798 media_observer->OnCreatingAudioStream(render_process_id_, render_frame_id);
799
800 scoped_ptr<AudioEntry> entry(
801 new AudioEntry(this, stream_id, render_frame_id, params, device_unique_id,
802 std::move(shared_memory), std::move(reader),
803 std::move(stream), callback));
804 if (mirroring_manager_) {
805 mirroring_manager_->AddDiverter(
806 render_process_id_, entry->render_frame_id(), entry->controller());
807 }
808 audio_entries_.insert(std::make_pair(stream_id, entry.release()));
809 g_audio_streams_tracker.Get().IncreaseStreamCount();
810
811 audio_log_->OnCreated(stream_id, params, device_unique_id);
812 MediaInternals::GetInstance()->SetWebContentsTitleForAudioLogEntry(
813 stream_id, render_process_id_, render_frame_id, audio_log_.get());
814
815 if (audio_entries_.size() > max_simultaneous_streams_)
816 max_simultaneous_streams_ = audio_entries_.size();
817 }
818
608 void AudioRendererHost::OnPlayStream(int stream_id) { 819 void AudioRendererHost::OnPlayStream(int stream_id) {
609 DCHECK_CURRENTLY_ON(BrowserThread::IO); 820 DCHECK_CURRENTLY_ON(BrowserThread::IO);
610 821
611 AudioEntry* entry = LookupById(stream_id); 822 AudioEntry* entry = LookupById(stream_id);
612 if (!entry) { 823 if (!entry) {
613 SendErrorMessage(stream_id); 824 SendErrorMessage(stream_id);
614 return; 825 return;
615 } 826 }
616 827
617 entry->controller()->Play(); 828 entry->controller()->Play();
(...skipping 28 matching lines...) Expand all
646 entry->controller()->SetVolume(volume); 857 entry->controller()->SetVolume(volume);
647 audio_log_->OnSetVolume(stream_id, volume); 858 audio_log_->OnSetVolume(stream_id, volume);
648 } 859 }
649 860
650 void AudioRendererHost::SendErrorMessage(int stream_id) { 861 void AudioRendererHost::SendErrorMessage(int stream_id) {
651 Send(new AudioMsg_NotifyStreamStateChanged( 862 Send(new AudioMsg_NotifyStreamStateChanged(
652 stream_id, media::AUDIO_OUTPUT_IPC_DELEGATE_STATE_ERROR)); 863 stream_id, media::AUDIO_OUTPUT_IPC_DELEGATE_STATE_ERROR));
653 } 864 }
654 865
655 void AudioRendererHost::OnCloseStream(int stream_id) { 866 void AudioRendererHost::OnCloseStream(int stream_id) {
867 DLOG(WARNING) << "OnCloseStream " << stream_id;
656 DCHECK_CURRENTLY_ON(BrowserThread::IO); 868 DCHECK_CURRENTLY_ON(BrowserThread::IO);
657 authorizations_.erase(stream_id); 869 authorizations_.erase(stream_id);
658 870
659 // Prevent oustanding callbacks from attempting to close/delete the same 871 // Prevent oustanding callbacks from attempting to close/delete the same
660 // AudioEntry twice. 872 // AudioEntry twice.
661 AudioEntryMap::iterator i = audio_entries_.find(stream_id); 873 AudioEntryMap::iterator i = audio_entries_.find(stream_id);
662 if (i == audio_entries_.end()) 874 if (i == audio_entries_.end())
663 return; 875 return;
664 scoped_ptr<AudioEntry> entry(i->second); 876 scoped_ptr<AudioEntry> entry(i->second);
665 audio_entries_.erase(i); 877 audio_entries_.erase(i);
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after
822 callback.Run(false, device_info); 1034 callback.Run(false, device_info);
823 } 1035 }
824 1036
825 bool AudioRendererHost::IsAuthorizationStarted(int stream_id) { 1037 bool AudioRendererHost::IsAuthorizationStarted(int stream_id) {
826 DCHECK_CURRENTLY_ON(BrowserThread::IO); 1038 DCHECK_CURRENTLY_ON(BrowserThread::IO);
827 const auto& i = authorizations_.find(stream_id); 1039 const auto& i = authorizations_.find(stream_id);
828 return i != authorizations_.end(); 1040 return i != authorizations_.end();
829 } 1041 }
830 1042
831 } // namespace content 1043 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/renderer_host/media/audio_renderer_host.h ('k') | content/browser/renderer_host/render_process_host_impl.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698