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.
4
5#include "content/browser/media/media_internals_proxy.h"
6
7#include "base/bind.h"
8#include "base/message_loop/message_loop.h"
9#include "content/browser/media/media_internals_handler.h"
10#include "content/public/browser/content_browser_client.h"
11#include "content/public/browser/notification_service.h"
12#include "content/public/browser/notification_types.h"
13#include "content/public/browser/render_process_host.h"
14#include "content/public/browser/web_ui.h"
15
16namespace content {
17
18static const int kMediaInternalsProxyEventDelayMilliseconds = 100;
19
20static const net::NetLog::EventType kNetEventTypeFilter[] = {
21  net::NetLog::TYPE_DISK_CACHE_ENTRY_IMPL,
22  net::NetLog::TYPE_SPARSE_READ,
23  net::NetLog::TYPE_SPARSE_WRITE,
24  net::NetLog::TYPE_URL_REQUEST_START_JOB,
25  net::NetLog::TYPE_HTTP_TRANSACTION_READ_RESPONSE_HEADERS,
26};
27
28MediaInternalsProxy::MediaInternalsProxy() {
29  registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED,
30                 NotificationService::AllBrowserContextsAndSources());
31}
32
33void MediaInternalsProxy::Observe(int type,
34                                  const NotificationSource& source,
35                                  const NotificationDetails& details) {
36  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
37  DCHECK_EQ(type, NOTIFICATION_RENDERER_PROCESS_TERMINATED);
38  RenderProcessHost* process = Source<RenderProcessHost>(source).ptr();
39  CallJavaScriptFunctionOnUIThread("media.onRendererTerminated",
40      new base::FundamentalValue(process->GetID()));
41}
42
43void MediaInternalsProxy::Attach(MediaInternalsMessageHandler* handler) {
44  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
45  handler_ = handler;
46  BrowserThread::PostTask(
47      BrowserThread::IO, FROM_HERE,
48      base::Bind(&MediaInternalsProxy::ObserveMediaInternalsOnIOThread, this));
49}
50
51void MediaInternalsProxy::Detach() {
52  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
53  handler_ = NULL;
54  BrowserThread::PostTask(
55      BrowserThread::IO, FROM_HERE,
56      base::Bind(
57          &MediaInternalsProxy::StopObservingMediaInternalsOnIOThread, this));
58}
59
60void MediaInternalsProxy::GetEverything() {
61  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
62
63  // Ask MediaInternals for all its data.
64  BrowserThread::PostTask(
65      BrowserThread::IO, FROM_HERE,
66      base::Bind(&MediaInternalsProxy::GetEverythingOnIOThread, this));
67
68  // Send the page names for constants.
69  CallJavaScriptFunctionOnUIThread("media.onReceiveConstants", GetConstants());
70}
71
72void MediaInternalsProxy::OnUpdate(const base::string16& update) {
73  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
74  BrowserThread::PostTask(
75      BrowserThread::UI, FROM_HERE,
76      base::Bind(&MediaInternalsProxy::UpdateUIOnUIThread, this, update));
77}
78
79void MediaInternalsProxy::OnAddEntry(const net::NetLog::Entry& entry) {
80  bool is_event_interesting = false;
81  for (size_t i = 0; i < arraysize(kNetEventTypeFilter); i++) {
82    if (entry.type() == kNetEventTypeFilter[i]) {
83      is_event_interesting = true;
84      break;
85    }
86  }
87
88  if (!is_event_interesting)
89    return;
90
91  BrowserThread::PostTask(
92      BrowserThread::UI, FROM_HERE,
93      base::Bind(&MediaInternalsProxy::AddNetEventOnUIThread, this,
94                 entry.ToValue()));
95}
96
97MediaInternalsProxy::~MediaInternalsProxy() {}
98
99base::Value* MediaInternalsProxy::GetConstants() {
100  base::DictionaryValue* event_phases = new base::DictionaryValue();
101  event_phases->SetInteger(
102      net::NetLog::EventPhaseToString(net::NetLog::PHASE_NONE),
103      net::NetLog::PHASE_NONE);
104  event_phases->SetInteger(
105      net::NetLog::EventPhaseToString(net::NetLog::PHASE_BEGIN),
106      net::NetLog::PHASE_BEGIN);
107  event_phases->SetInteger(
108      net::NetLog::EventPhaseToString(net::NetLog::PHASE_END),
109      net::NetLog::PHASE_END);
110
111  base::DictionaryValue* constants = new base::DictionaryValue();
112  constants->Set("eventTypes", net::NetLog::GetEventTypesAsValue());
113  constants->Set("eventPhases", event_phases);
114
115  return constants;
116}
117
118void MediaInternalsProxy::ObserveMediaInternalsOnIOThread() {
119  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
120  update_callback_ = base::Bind(&MediaInternalsProxy::OnUpdate,
121                                base::Unretained(this));
122  MediaInternals::GetInstance()->AddUpdateCallback(update_callback_);
123  if (GetContentClient()->browser()->GetNetLog()) {
124    net::NetLog* net_log = GetContentClient()->browser()->GetNetLog();
125    net_log->AddThreadSafeObserver(this, net::NetLog::LOG_ALL_BUT_BYTES);
126  }
127}
128
129void MediaInternalsProxy::StopObservingMediaInternalsOnIOThread() {
130  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
131  MediaInternals::GetInstance()->RemoveUpdateCallback(update_callback_);
132  if (GetContentClient()->browser()->GetNetLog()) {
133    net::NetLog* net_log = GetContentClient()->browser()->GetNetLog();
134    net_log->RemoveThreadSafeObserver(this);
135  }
136}
137
138void MediaInternalsProxy::GetEverythingOnIOThread() {
139  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
140  MediaInternals::GetInstance()->SendEverything();
141}
142
143void MediaInternalsProxy::UpdateUIOnUIThread(const base::string16& update) {
144  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
145  // Don't forward updates to a destructed UI.
146  if (handler_)
147    handler_->OnUpdate(update);
148}
149
150void MediaInternalsProxy::AddNetEventOnUIThread(base::Value* entry) {
151  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
152
153  // Send the updates to the page in kMediaInternalsProxyEventDelayMilliseconds
154  // if an update is not already pending.
155  if (!pending_net_updates_) {
156    pending_net_updates_.reset(new base::ListValue());
157    base::MessageLoop::current()->PostDelayedTask(
158        FROM_HERE,
159        base::Bind(&MediaInternalsProxy::SendNetEventsOnUIThread, this),
160        base::TimeDelta::FromMilliseconds(
161            kMediaInternalsProxyEventDelayMilliseconds));
162  }
163  pending_net_updates_->Append(entry);
164}
165
166void MediaInternalsProxy::SendNetEventsOnUIThread() {
167  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
168  CallJavaScriptFunctionOnUIThread("media.onNetUpdate",
169                                   pending_net_updates_.release());
170}
171
172void MediaInternalsProxy::CallJavaScriptFunctionOnUIThread(
173    const std::string& function, base::Value* args) {
174  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
175  scoped_ptr<base::Value> args_value(args);
176  std::vector<const base::Value*> args_vector;
177  args_vector.push_back(args_value.get());
178  base::string16 update = WebUI::GetJavascriptCall(function, args_vector);
179  UpdateUIOnUIThread(update);
180}
181
182}  // namespace content
183