Add ICU message format support
[chromium-blink-merge.git] / content / browser / media / media_internals.cc
blob4b4d6cd6b3d3cb4e415a97c43fa0bc93e10a97f2
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
3 // found in the LICENSE file.
5 #include "content/browser/media/media_internals.h"
7 #include "base/metrics/histogram.h"
8 #include "base/strings/string16.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/stringprintf.h"
11 #include "content/public/browser/browser_thread.h"
12 #include "content/public/browser/notification_service.h"
13 #include "content/public/browser/notification_types.h"
14 #include "content/public/browser/render_frame_host.h"
15 #include "content/public/browser/render_process_host.h"
16 #include "content/public/browser/web_contents.h"
17 #include "content/public/browser/web_ui.h"
18 #include "media/audio/audio_parameters.h"
19 #include "media/base/media_log_event.h"
20 #include "media/filters/decrypting_video_decoder.h"
21 #include "media/filters/gpu_video_decoder.h"
23 namespace {
25 static base::LazyInstance<content::MediaInternals>::Leaky g_media_internals =
26 LAZY_INSTANCE_INITIALIZER;
28 base::string16 SerializeUpdate(const std::string& function,
29 const base::Value* value) {
30 return content::WebUI::GetJavascriptCall(
31 function, std::vector<const base::Value*>(1, value));
34 std::string EffectsToString(int effects) {
35 if (effects == media::AudioParameters::NO_EFFECTS)
36 return "NO_EFFECTS";
38 struct {
39 int flag;
40 const char* name;
41 } flags[] = {
42 { media::AudioParameters::ECHO_CANCELLER, "ECHO_CANCELLER" },
43 { media::AudioParameters::DUCKING, "DUCKING" },
44 { media::AudioParameters::KEYBOARD_MIC, "KEYBOARD_MIC" },
45 { media::AudioParameters::HOTWORD, "HOTWORD" },
48 std::string ret;
49 for (size_t i = 0; i < arraysize(flags); ++i) {
50 if (effects & flags[i].flag) {
51 if (!ret.empty())
52 ret += " | ";
53 ret += flags[i].name;
54 effects &= ~flags[i].flag;
58 if (effects) {
59 if (!ret.empty())
60 ret += " | ";
61 ret += base::IntToString(effects);
64 return ret;
67 std::string FormatToString(media::AudioParameters::Format format) {
68 switch (format) {
69 case media::AudioParameters::AUDIO_PCM_LINEAR:
70 return "pcm_linear";
71 case media::AudioParameters::AUDIO_PCM_LOW_LATENCY:
72 return "pcm_low_latency";
73 case media::AudioParameters::AUDIO_FAKE:
74 return "fake";
77 NOTREACHED();
78 return "unknown";
81 const char kAudioLogStatusKey[] = "status";
82 const char kAudioLogUpdateFunction[] = "media.updateAudioComponent";
84 } // namespace
86 namespace content {
88 class AudioLogImpl : public media::AudioLog {
89 public:
90 AudioLogImpl(int owner_id,
91 media::AudioLogFactory::AudioComponent component,
92 content::MediaInternals* media_internals);
93 ~AudioLogImpl() override;
95 void OnCreated(int component_id,
96 const media::AudioParameters& params,
97 const std::string& device_id) override;
98 void OnStarted(int component_id) override;
99 void OnStopped(int component_id) override;
100 void OnClosed(int component_id) override;
101 void OnError(int component_id) override;
102 void OnSetVolume(int component_id, double volume) override;
103 void OnSwitchOutputDevice(int component_id,
104 const std::string& device_id) override;
106 // Called by MediaInternals to update the WebContents title for a stream.
107 void SendWebContentsTitle(int component_id,
108 int render_process_id,
109 int render_frame_id);
111 private:
112 void SendSingleStringUpdate(int component_id,
113 const std::string& key,
114 const std::string& value);
115 void StoreComponentMetadata(int component_id, base::DictionaryValue* dict);
116 std::string FormatCacheKey(int component_id);
118 static void SendWebContentsTitleHelper(const std::string& cache_key,
119 scoped_ptr<base::DictionaryValue> dict,
120 int render_process_id,
121 int render_frame_id);
123 const int owner_id_;
124 const media::AudioLogFactory::AudioComponent component_;
125 content::MediaInternals* const media_internals_;
127 DISALLOW_COPY_AND_ASSIGN(AudioLogImpl);
130 AudioLogImpl::AudioLogImpl(int owner_id,
131 media::AudioLogFactory::AudioComponent component,
132 content::MediaInternals* media_internals)
133 : owner_id_(owner_id),
134 component_(component),
135 media_internals_(media_internals) {}
137 AudioLogImpl::~AudioLogImpl() {}
139 void AudioLogImpl::OnCreated(int component_id,
140 const media::AudioParameters& params,
141 const std::string& device_id) {
142 base::DictionaryValue dict;
143 StoreComponentMetadata(component_id, &dict);
145 dict.SetString(kAudioLogStatusKey, "created");
146 dict.SetString("device_id", device_id);
147 dict.SetString("device_type", FormatToString(params.format()));
148 dict.SetInteger("frames_per_buffer", params.frames_per_buffer());
149 dict.SetInteger("sample_rate", params.sample_rate());
150 dict.SetInteger("channels", params.channels());
151 dict.SetString("channel_layout",
152 ChannelLayoutToString(params.channel_layout()));
153 dict.SetString("effects", EffectsToString(params.effects()));
155 media_internals_->UpdateAudioLog(MediaInternals::CREATE,
156 FormatCacheKey(component_id),
157 kAudioLogUpdateFunction, &dict);
160 void AudioLogImpl::OnStarted(int component_id) {
161 SendSingleStringUpdate(component_id, kAudioLogStatusKey, "started");
164 void AudioLogImpl::OnStopped(int component_id) {
165 SendSingleStringUpdate(component_id, kAudioLogStatusKey, "stopped");
168 void AudioLogImpl::OnClosed(int component_id) {
169 base::DictionaryValue dict;
170 StoreComponentMetadata(component_id, &dict);
171 dict.SetString(kAudioLogStatusKey, "closed");
172 media_internals_->UpdateAudioLog(MediaInternals::UPDATE_AND_DELETE,
173 FormatCacheKey(component_id),
174 kAudioLogUpdateFunction, &dict);
177 void AudioLogImpl::OnError(int component_id) {
178 SendSingleStringUpdate(component_id, "error_occurred", "true");
181 void AudioLogImpl::OnSetVolume(int component_id, double volume) {
182 base::DictionaryValue dict;
183 StoreComponentMetadata(component_id, &dict);
184 dict.SetDouble("volume", volume);
185 media_internals_->UpdateAudioLog(MediaInternals::UPDATE_IF_EXISTS,
186 FormatCacheKey(component_id),
187 kAudioLogUpdateFunction, &dict);
190 void AudioLogImpl::OnSwitchOutputDevice(int component_id,
191 const std::string& device_id) {
192 base::DictionaryValue dict;
193 StoreComponentMetadata(component_id, &dict);
194 dict.SetString("device_id", device_id);
195 media_internals_->UpdateAudioLog(MediaInternals::UPDATE_IF_EXISTS,
196 FormatCacheKey(component_id),
197 kAudioLogUpdateFunction, &dict);
200 void AudioLogImpl::SendWebContentsTitle(int component_id,
201 int render_process_id,
202 int render_frame_id) {
203 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
204 StoreComponentMetadata(component_id, dict.get());
205 SendWebContentsTitleHelper(FormatCacheKey(component_id), dict.Pass(),
206 render_process_id, render_frame_id);
209 std::string AudioLogImpl::FormatCacheKey(int component_id) {
210 return base::StringPrintf("%d:%d:%d", owner_id_, component_, component_id);
213 // static
214 void AudioLogImpl::SendWebContentsTitleHelper(
215 const std::string& cache_key,
216 scoped_ptr<base::DictionaryValue> dict,
217 int render_process_id,
218 int render_frame_id) {
219 // Page title information can only be retrieved from the UI thread.
220 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
221 BrowserThread::PostTask(
222 BrowserThread::UI, FROM_HERE,
223 base::Bind(&SendWebContentsTitleHelper, cache_key, base::Passed(&dict),
224 render_process_id, render_frame_id));
225 return;
228 const WebContents* web_contents = WebContents::FromRenderFrameHost(
229 RenderFrameHost::FromID(render_process_id, render_frame_id));
230 if (!web_contents)
231 return;
233 // Note: by this point the given audio log entry could have been destroyed, so
234 // we use UPDATE_IF_EXISTS to discard such instances.
235 dict->SetInteger("render_process_id", render_process_id);
236 dict->SetString("web_contents_title", web_contents->GetTitle());
237 MediaInternals::GetInstance()->UpdateAudioLog(
238 MediaInternals::UPDATE_IF_EXISTS, cache_key, kAudioLogUpdateFunction,
239 dict.get());
242 void AudioLogImpl::SendSingleStringUpdate(int component_id,
243 const std::string& key,
244 const std::string& value) {
245 base::DictionaryValue dict;
246 StoreComponentMetadata(component_id, &dict);
247 dict.SetString(key, value);
248 media_internals_->UpdateAudioLog(MediaInternals::UPDATE_IF_EXISTS,
249 FormatCacheKey(component_id),
250 kAudioLogUpdateFunction, &dict);
253 void AudioLogImpl::StoreComponentMetadata(int component_id,
254 base::DictionaryValue* dict) {
255 dict->SetInteger("owner_id", owner_id_);
256 dict->SetInteger("component_id", component_id);
257 dict->SetInteger("component_type", component_);
260 // This class lives on the browser UI thread.
261 class MediaInternals::MediaInternalsUMAHandler {
262 public:
263 MediaInternalsUMAHandler();
265 // Called when a render process is terminated. Reports the pipeline status to
266 // UMA for every player associated with the renderer process and then deletes
267 // the player state.
268 void OnProcessTerminated(int render_process_id);
270 // Helper function to save the event payload to RendererPlayerMap.
271 void SavePlayerState(int render_process_id,
272 const media::MediaLogEvent& event);
274 private:
275 struct PipelineInfo {
276 media::PipelineStatus last_pipeline_status;
277 bool has_audio;
278 bool has_video;
279 bool video_dds;
280 bool video_decoder_changed;
281 std::string audio_codec_name;
282 std::string video_codec_name;
283 std::string video_decoder;
284 PipelineInfo()
285 : last_pipeline_status(media::PIPELINE_OK),
286 has_audio(false),
287 has_video(false),
288 video_dds(false),
289 video_decoder_changed(false) {}
292 // Helper function to report PipelineStatus associated with a player to UMA.
293 void ReportUMAForPipelineStatus(const PipelineInfo& player_info);
295 // Helper to generate PipelineStatus UMA name for AudioVideo streams.
296 std::string GetUMANameForAVStream(const PipelineInfo& player_info);
298 // Key is player id.
299 typedef std::map<int, PipelineInfo> PlayerInfoMap;
301 // Key is renderer id.
302 typedef std::map<int, PlayerInfoMap> RendererPlayerMap;
304 // Stores player information per renderer.
305 RendererPlayerMap renderer_info_;
307 DISALLOW_COPY_AND_ASSIGN(MediaInternalsUMAHandler);
310 MediaInternals::MediaInternalsUMAHandler::MediaInternalsUMAHandler() {
313 void MediaInternals::MediaInternalsUMAHandler::SavePlayerState(
314 int render_process_id,
315 const media::MediaLogEvent& event) {
316 DCHECK_CURRENTLY_ON(BrowserThread::UI);
317 PlayerInfoMap& player_info = renderer_info_[render_process_id];
318 switch (event.type) {
319 case media::MediaLogEvent::PIPELINE_ERROR: {
320 int status;
321 event.params.GetInteger("pipeline_error", &status);
322 player_info[event.id].last_pipeline_status =
323 static_cast<media::PipelineStatus>(status);
324 break;
326 case media::MediaLogEvent::PROPERTY_CHANGE:
327 if (event.params.HasKey("found_audio_stream")) {
328 event.params.GetBoolean("found_audio_stream",
329 &player_info[event.id].has_audio);
331 if (event.params.HasKey("found_video_stream")) {
332 event.params.GetBoolean("found_video_stream",
333 &player_info[event.id].has_video);
335 if (event.params.HasKey("audio_codec_name")) {
336 event.params.GetString("audio_codec_name",
337 &player_info[event.id].audio_codec_name);
339 if (event.params.HasKey("video_codec_name")) {
340 event.params.GetString("video_codec_name",
341 &player_info[event.id].video_codec_name);
343 if (event.params.HasKey("video_decoder")) {
344 std::string previous_video_decoder(player_info[event.id].video_decoder);
345 event.params.GetString("video_decoder",
346 &player_info[event.id].video_decoder);
347 if (!previous_video_decoder.empty() &&
348 previous_video_decoder != player_info[event.id].video_decoder) {
349 player_info[event.id].video_decoder_changed = true;
352 if (event.params.HasKey("video_dds")) {
353 event.params.GetBoolean("video_dds", &player_info[event.id].video_dds);
355 break;
356 default:
357 break;
359 return;
362 std::string MediaInternals::MediaInternalsUMAHandler::GetUMANameForAVStream(
363 const PipelineInfo& player_info) {
364 DCHECK_CURRENTLY_ON(BrowserThread::UI);
365 static const char kPipelineUmaPrefix[] = "Media.PipelineStatus.AudioVideo.";
366 std::string uma_name = kPipelineUmaPrefix;
367 if (player_info.video_codec_name == "vp8") {
368 uma_name += "VP8.";
369 } else if (player_info.video_codec_name == "vp9") {
370 uma_name += "VP9.";
371 } else if (player_info.video_codec_name == "h264") {
372 uma_name += "H264.";
373 } else {
374 return uma_name + "Other";
377 if (player_info.video_decoder ==
378 media::DecryptingVideoDecoder::kDecoderName) {
379 return uma_name + "DVD";
382 if (player_info.video_dds) {
383 uma_name += "DDS.";
386 if (player_info.video_decoder == media::GpuVideoDecoder::kDecoderName) {
387 uma_name += "HW";
388 } else {
389 uma_name += "SW";
391 return uma_name;
394 void MediaInternals::MediaInternalsUMAHandler::ReportUMAForPipelineStatus(
395 const PipelineInfo& player_info) {
396 DCHECK_CURRENTLY_ON(BrowserThread::UI);
397 if (player_info.has_video && player_info.has_audio) {
398 base::LinearHistogram::FactoryGet(
399 GetUMANameForAVStream(player_info), 1, media::PIPELINE_STATUS_MAX,
400 media::PIPELINE_STATUS_MAX + 1,
401 base::HistogramBase::kUmaTargetedHistogramFlag)
402 ->Add(player_info.last_pipeline_status);
403 } else if (player_info.has_audio) {
404 UMA_HISTOGRAM_ENUMERATION("Media.PipelineStatus.AudioOnly",
405 player_info.last_pipeline_status,
406 media::PIPELINE_STATUS_MAX + 1);
407 } else if (player_info.has_video) {
408 UMA_HISTOGRAM_ENUMERATION("Media.PipelineStatus.VideoOnly",
409 player_info.last_pipeline_status,
410 media::PIPELINE_STATUS_MAX + 1);
411 } else {
412 UMA_HISTOGRAM_ENUMERATION("Media.PipelineStatus.Unsupported",
413 player_info.last_pipeline_status,
414 media::PIPELINE_STATUS_MAX + 1);
416 // Report whether video decoder fallback happened, but only if a video decoder
417 // was reported.
418 if (!player_info.video_decoder.empty()) {
419 UMA_HISTOGRAM_BOOLEAN("Media.VideoDecoderFallback",
420 player_info.video_decoder_changed);
424 void MediaInternals::MediaInternalsUMAHandler::OnProcessTerminated(
425 int render_process_id) {
426 DCHECK_CURRENTLY_ON(BrowserThread::UI);
428 auto players_it = renderer_info_.find(render_process_id);
429 if (players_it == renderer_info_.end())
430 return;
431 auto it = players_it->second.begin();
432 while (it != players_it->second.end()) {
433 ReportUMAForPipelineStatus(it->second);
434 players_it->second.erase(it++);
436 renderer_info_.erase(players_it);
439 MediaInternals* MediaInternals::GetInstance() {
440 return g_media_internals.Pointer();
443 MediaInternals::MediaInternals()
444 : can_update_(false),
445 owner_ids_(),
446 uma_handler_(new MediaInternalsUMAHandler()) {
447 registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED,
448 NotificationService::AllBrowserContextsAndSources());
451 MediaInternals::~MediaInternals() {}
453 void MediaInternals::Observe(int type,
454 const NotificationSource& source,
455 const NotificationDetails& details) {
456 DCHECK_CURRENTLY_ON(BrowserThread::UI);
457 DCHECK_EQ(type, NOTIFICATION_RENDERER_PROCESS_TERMINATED);
458 RenderProcessHost* process = Source<RenderProcessHost>(source).ptr();
460 uma_handler_->OnProcessTerminated(process->GetID());
461 pending_events_map_.erase(process->GetID());
464 // Converts the |event| to a |update|. Returns whether the conversion succeeded.
465 static bool ConvertEventToUpdate(int render_process_id,
466 const media::MediaLogEvent& event,
467 base::string16* update) {
468 DCHECK(update);
470 base::DictionaryValue dict;
471 dict.SetInteger("renderer", render_process_id);
472 dict.SetInteger("player", event.id);
473 dict.SetString("type", media::MediaLog::EventTypeToString(event.type));
475 // TODO(dalecurtis): This is technically not correct. TimeTicks "can't" be
476 // converted to to a human readable time format. See base/time/time.h.
477 const double ticks = event.time.ToInternalValue();
478 const double ticks_millis = ticks / base::Time::kMicrosecondsPerMillisecond;
479 dict.SetDouble("ticksMillis", ticks_millis);
481 // Convert PipelineStatus to human readable string
482 if (event.type == media::MediaLogEvent::PIPELINE_ERROR) {
483 int status;
484 if (!event.params.GetInteger("pipeline_error", &status) ||
485 status < static_cast<int>(media::PIPELINE_OK) ||
486 status > static_cast<int>(media::PIPELINE_STATUS_MAX)) {
487 return false;
489 media::PipelineStatus error = static_cast<media::PipelineStatus>(status);
490 dict.SetString("params.pipeline_error",
491 media::MediaLog::PipelineStatusToString(error));
492 } else {
493 dict.Set("params", event.params.DeepCopy());
496 *update = SerializeUpdate("media.onMediaEvent", &dict);
497 return true;
500 void MediaInternals::OnMediaEvents(
501 int render_process_id, const std::vector<media::MediaLogEvent>& events) {
502 DCHECK_CURRENTLY_ON(BrowserThread::UI);
503 // Notify observers that |event| has occurred.
504 for (const auto& event : events) {
505 if (CanUpdate()) {
506 base::string16 update;
507 if (ConvertEventToUpdate(render_process_id, event, &update))
508 SendUpdate(update);
511 SaveEvent(render_process_id, event);
512 uma_handler_->SavePlayerState(render_process_id, event);
516 void MediaInternals::AddUpdateCallback(const UpdateCallback& callback) {
517 DCHECK_CURRENTLY_ON(BrowserThread::UI);
518 update_callbacks_.push_back(callback);
520 base::AutoLock auto_lock(lock_);
521 can_update_ = true;
524 void MediaInternals::RemoveUpdateCallback(const UpdateCallback& callback) {
525 DCHECK_CURRENTLY_ON(BrowserThread::UI);
526 for (size_t i = 0; i < update_callbacks_.size(); ++i) {
527 if (update_callbacks_[i].Equals(callback)) {
528 update_callbacks_.erase(update_callbacks_.begin() + i);
529 break;
533 base::AutoLock auto_lock(lock_);
534 can_update_ = !update_callbacks_.empty();
537 bool MediaInternals::CanUpdate() {
538 base::AutoLock auto_lock(lock_);
539 return can_update_;
542 void MediaInternals::SendHistoricalMediaEvents() {
543 DCHECK_CURRENTLY_ON(BrowserThread::UI);
544 for (const auto& pending_events : pending_events_map_) {
545 for (const auto& event : pending_events.second) {
546 base::string16 update;
547 if (ConvertEventToUpdate(pending_events.first, event, &update))
548 SendUpdate(update);
551 // Do not clear the map/list here so that refreshing the UI or opening a
552 // second UI still works nicely!
555 void MediaInternals::SendAudioStreamData() {
556 base::string16 audio_stream_update;
558 base::AutoLock auto_lock(lock_);
559 audio_stream_update = SerializeUpdate(
560 "media.onReceiveAudioStreamData", &audio_streams_cached_data_);
562 SendUpdate(audio_stream_update);
565 void MediaInternals::SendVideoCaptureDeviceCapabilities() {
566 DCHECK_CURRENTLY_ON(BrowserThread::IO);
568 if (!CanUpdate())
569 return;
571 SendUpdate(SerializeUpdate("media.onReceiveVideoCaptureCapabilities",
572 &video_capture_capabilities_cached_data_));
575 void MediaInternals::UpdateVideoCaptureDeviceCapabilities(
576 const media::VideoCaptureDeviceInfos& video_capture_device_infos) {
577 DCHECK_CURRENTLY_ON(BrowserThread::IO);
578 video_capture_capabilities_cached_data_.Clear();
580 for (const auto& video_capture_device_info : video_capture_device_infos) {
581 base::ListValue* format_list = new base::ListValue();
582 for (const auto& format : video_capture_device_info.supported_formats)
583 format_list->AppendString(media::VideoCaptureFormat::ToString(format));
585 base::DictionaryValue* device_dict = new base::DictionaryValue();
586 device_dict->SetString("id", video_capture_device_info.name.id());
587 device_dict->SetString(
588 "name", video_capture_device_info.name.GetNameAndModel());
589 device_dict->Set("formats", format_list);
590 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
591 defined(OS_ANDROID)
592 device_dict->SetString(
593 "captureApi", video_capture_device_info.name.GetCaptureApiTypeString());
594 #endif
595 video_capture_capabilities_cached_data_.Append(device_dict);
598 SendVideoCaptureDeviceCapabilities();
601 scoped_ptr<media::AudioLog> MediaInternals::CreateAudioLog(
602 AudioComponent component) {
603 base::AutoLock auto_lock(lock_);
604 return scoped_ptr<media::AudioLog>(new AudioLogImpl(
605 owner_ids_[component]++, component, this));
608 void MediaInternals::SetWebContentsTitleForAudioLogEntry(
609 int component_id,
610 int render_process_id,
611 int render_frame_id,
612 media::AudioLog* audio_log) {
613 static_cast<AudioLogImpl*>(audio_log)
614 ->SendWebContentsTitle(component_id, render_process_id, render_frame_id);
617 void MediaInternals::SendUpdate(const base::string16& update) {
618 // SendUpdate() may be called from any thread, but must run on the UI thread.
619 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
620 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
621 &MediaInternals::SendUpdate, base::Unretained(this), update));
622 return;
625 for (size_t i = 0; i < update_callbacks_.size(); i++)
626 update_callbacks_[i].Run(update);
629 void MediaInternals::SaveEvent(int process_id,
630 const media::MediaLogEvent& event) {
631 DCHECK_CURRENTLY_ON(BrowserThread::UI);
633 // Max number of saved updates allowed for one process.
634 const size_t kMaxNumEvents = 128;
636 // Do not save instantaneous events that happen frequently and have little
637 // value in the future.
638 if (event.type == media::MediaLogEvent::NETWORK_ACTIVITY_SET ||
639 event.type == media::MediaLogEvent::BUFFERED_EXTENTS_CHANGED) {
640 return;
643 auto& pending_events = pending_events_map_[process_id];
644 // TODO(xhwang): Notify user that some old logs could have been truncated.
645 // See http://crbug.com/498520
646 if (pending_events.size() >= kMaxNumEvents)
647 pending_events.pop_front();
648 pending_events.push_back(event);
651 void MediaInternals::UpdateAudioLog(AudioLogUpdateType type,
652 const std::string& cache_key,
653 const std::string& function,
654 const base::DictionaryValue* value) {
656 base::AutoLock auto_lock(lock_);
657 const bool has_entry = audio_streams_cached_data_.HasKey(cache_key);
658 if ((type == UPDATE_IF_EXISTS || type == UPDATE_AND_DELETE) && !has_entry) {
659 return;
660 } else if (!has_entry) {
661 DCHECK_EQ(type, CREATE);
662 audio_streams_cached_data_.Set(cache_key, value->DeepCopy());
663 } else if (type == UPDATE_AND_DELETE) {
664 scoped_ptr<base::Value> out_value;
665 CHECK(audio_streams_cached_data_.Remove(cache_key, &out_value));
666 } else {
667 base::DictionaryValue* existing_dict = NULL;
668 CHECK(
669 audio_streams_cached_data_.GetDictionary(cache_key, &existing_dict));
670 existing_dict->MergeDictionary(value);
674 if (CanUpdate())
675 SendUpdate(SerializeUpdate(function, value));
678 } // namespace content