1 // Copyright 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.
5 chrome
.runtime
.onMessageExternal
.addListener(
6 function(message
, sender
, sendResponse
) {
7 function doSendResponse(value
, errorString
) {
11 error
['name'] = 'ComponentExtensonError';
12 error
['message'] = errorString
;
15 var errorMessage
= error
|| chrome
.extension
.lastError
;
16 sendResponse({'value': value
, 'error': errorMessage
});
19 function getHost(url
) {
22 // Use the DOM to parse the URL. Since we don't add the anchor to
23 // the page, this is the only reference to it and it will be
24 // deleted once it's gone out of scope.
25 var a
= document
.createElement('a');
27 var origin
= a
.protocol
+ '//' + a
.hostname
;
29 origin
= origin
+ ':' + a
.port
;
30 origin
= origin
+ '/';
37 requestInfo
['tabId'] = sender
.tab
.id
;
40 if (sender
.guestProcessId
) {
41 requestInfo
['guestProcessId'] = sender
.guestProcessId
;
44 if (sender
.guestRenderFrameRoutingId
) {
45 requestInfo
['guestRenderFrameId'] = sender
.guestRenderFrameRoutingId
;
48 var method
= message
['method'];
49 var origin
= getHost(sender
.url
);
50 if (method
== 'cpu.getInfo') {
51 chrome
.system
.cpu
.getInfo(doSendResponse
);
53 } else if (method
== 'logging.setMetadata') {
54 var metaData
= message
['metaData'];
55 chrome
.webrtcLoggingPrivate
.setMetaData(
56 requestInfo
, origin
, metaData
, doSendResponse
);
58 } else if (method
== 'logging.start') {
59 chrome
.webrtcLoggingPrivate
.start(
60 requestInfo
, origin
, doSendResponse
);
62 } else if (method
== 'logging.uploadOnRenderClose') {
63 chrome
.webrtcLoggingPrivate
.setUploadOnRenderClose(
64 requestInfo
, origin
, true);
67 } else if (method
== 'logging.noUploadOnRenderClose') {
68 chrome
.webrtcLoggingPrivate
.setUploadOnRenderClose(
69 requestInfo
, origin
, false);
72 } else if (method
== 'logging.stop') {
73 chrome
.webrtcLoggingPrivate
.stop(
74 requestInfo
, origin
, doSendResponse
);
76 } else if (method
== 'logging.upload') {
77 chrome
.webrtcLoggingPrivate
.upload(
78 requestInfo
, origin
, doSendResponse
);
80 } else if (method
== 'logging.uploadStored') {
81 var logId
= message
['logId'];
82 chrome
.webrtcLoggingPrivate
.uploadStored(
83 requestInfo
, origin
, logId
, doSendResponse
);
85 } else if (method
== 'logging.stopAndUpload') {
86 stopAllRtpDump(requestInfo
, origin
,
87 function(rtpDumpValue
, rtpDumpErrorString
) {
88 if (chrome
.extension
.lastError
!== undefined) {
89 // Stopping RTP dump failed, try to stop logging but don't try
90 // to upload since that will fail.
91 chrome
.webrtcLoggingPrivate
.stop(
92 requestInfo
, origin
, function(stopValue
, stopErrorString
) {
93 if (chrome
.extension
.lastError
!== undefined) {
94 // Stopping logging also failed, report the error for logging.
95 doSendResponse(stopValue
, stopErrorString
);
97 // Stopping logging succeeded, report the error for RTP dump.
98 doSendResponse(rtpDumpValue
, rtpDumpErrorString
);
102 // Stopping RTP dump succeeded.
103 chrome
.webrtcLoggingPrivate
.stop(
104 requestInfo
, origin
, function(stopValue
, stopErrorString
) {
105 if (chrome
.extension
.lastError
!== undefined) {
106 // Stopping logging failed, report error and don't try to
107 // upload since that will fail.
108 doSendResponse(stopValue
, stopErrorString
);
110 // Stopping logging succeeded.
111 chrome
.webrtcLoggingPrivate
.upload(
112 requestInfo
, origin
, doSendResponse
);
118 } else if (method
== 'logging.store') {
119 var logId
= message
['logId'];
120 chrome
.webrtcLoggingPrivate
.store(
121 requestInfo
, origin
, logId
, doSendResponse
);
123 } else if (method
== 'logging.discard') {
124 chrome
.webrtcLoggingPrivate
.discard(
125 requestInfo
, origin
, doSendResponse
);
127 } else if (method
== 'getSinks') {
128 chrome
.webrtcAudioPrivate
.getSinks(doSendResponse
);
130 } else if (method
== 'getActiveSink') {
131 chrome
.webrtcAudioPrivate
.getActiveSink(
132 requestInfo
, doSendResponse
);
134 } else if (method
== 'setActiveSink') {
135 var sinkId
= message
['sinkId'];
136 chrome
.webrtcAudioPrivate
.setActiveSink(
137 requestInfo
, sinkId
, doSendResponse
);
139 } else if (method
== 'getAssociatedSink') {
140 var sourceId
= message
['sourceId'];
141 chrome
.webrtcAudioPrivate
.getAssociatedSink(
142 origin
, sourceId
, doSendResponse
);
144 } else if (method
== 'isExtensionEnabled') {
145 // This method is necessary because there may be more than one
146 // version of this extension, under different extension IDs. By
147 // first calling this method on the extension ID, the client can
148 // check if it's loaded; if it's not, the extension system will
149 // call the callback with no arguments and set
150 // chrome.runtime.lastError.
153 } else if (method
== 'getNaclArchitecture') {
154 chrome
.runtime
.getPlatformInfo(function(obj
) {
155 doSendResponse(obj
.nacl_arch
);
158 } else if (method
== 'logging.startRtpDump') {
159 var incoming
= message
['incoming'] || false;
160 var outgoing
= message
['outgoing'] || false;
161 chrome
.webrtcLoggingPrivate
.startRtpDump(
162 requestInfo
, origin
, incoming
, outgoing
, doSendResponse
);
164 } else if (method
== 'logging.stopRtpDump') {
165 var incoming
= message
['incoming'] || false;
166 var outgoing
= message
['outgoing'] || false;
167 chrome
.webrtcLoggingPrivate
.stopRtpDump(
168 requestInfo
, origin
, incoming
, outgoing
, doSendResponse
);
171 throw new Error('Unknown method: ' + method
);
173 doSendResponse(null, e
.name
+ ': ' + e
.message
);
178 // If Hangouts connects with a port named 'onSinksChangedListener', we
179 // will register a listener and send it a message {'eventName':
180 // 'onSinksChanged'} whenever the event fires.
181 function onSinksChangedPort(port
) {
182 function clientListener() {
183 port
.postMessage({'eventName': 'onSinksChanged'});
185 chrome
.webrtcAudioPrivate
.onSinksChanged
.addListener(clientListener
);
187 port
.onDisconnect
.addListener(function() {
188 chrome
.webrtcAudioPrivate
.onSinksChanged
.removeListener(
193 // This is a one-time-use port for calling chooseDesktopMedia. The page
194 // sends one message, identifying the requested source types, and the
195 // extension sends a single reply, with the user's selected streamId. A port
196 // is used so that if the page is closed before that message is sent, the
197 // window picker dialog will be closed.
198 function onChooseDesktopMediaPort(port
) {
199 function sendResponse(streamId
) {
200 port
.postMessage({'value': {'streamId': streamId
}});
204 port
.onMessage
.addListener(function(message
) {
205 var method
= message
['method'];
206 if (method
== 'chooseDesktopMedia') {
207 var sources
= message
['sources'];
209 if (port
.sender
.tab
) {
210 cancelId
= chrome
.desktopCapture
.chooseDesktopMedia(
211 sources
, port
.sender
.tab
, sendResponse
);
213 var requestInfo
= {};
214 requestInfo
['guestProcessId'] = port
.sender
.guestProcessId
|| 0;
215 requestInfo
['guestRenderFrameId'] =
216 port
.sender
.guestRenderFrameRoutingId
|| 0;
217 cancelId
= chrome
.webrtcDesktopCapturePrivate
.chooseDesktopMedia(
218 sources
, requestInfo
, sendResponse
);
220 port
.onDisconnect
.addListener(function() {
221 // This method has no effect if called after the user has selected a
222 // desktop media source, so it does not need to be conditional.
223 if (port
.sender
.tab
) {
224 chrome
.desktopCapture
.cancelChooseDesktopMedia(cancelId
);
226 chrome
.webrtcDesktopCapturePrivate
.cancelChooseDesktopMedia(cancelId
);
233 // A port for continuously reporting relevant CPU usage information to the page.
234 function onProcessCpu(port
) {
235 var tabPid
= port
.sender
.guestProcessId
|| undefined;
236 function processListener(processes
) {
237 if (tabPid
== undefined) {
238 // getProcessIdForTab sometimes fails, and does not call the callback.
239 // (Tracked at https://crbug.com/368855.)
240 // This call retries it on each process update until it succeeds.
241 chrome
.processes
.getProcessIdForTab(port
.sender
.tab
.id
, function(x
) {
246 var tabProcess
= processes
[tabPid
];
250 var pluginProcessCpu
= 0, browserProcessCpu
= 0, gpuProcessCpu
= 0;
251 for (var pid
in processes
) {
252 var process
= processes
[pid
];
253 if (process
.type
== 'browser') {
254 browserProcessCpu
= process
.cpu
;
255 } else if (process
.type
== 'gpu') {
256 gpuProcessCpu
= process
.cpu
;
257 } else if ((process
.type
== 'plugin' || process
.type
== 'nacl') &&
258 process
.title
.toLowerCase().indexOf('hangouts') > 0) {
259 pluginProcessCpu
= process
.cpu
;
264 'tabCpuUsage': tabProcess
.cpu
,
265 'browserCpuUsage': browserProcessCpu
,
266 'gpuCpuUsage': gpuProcessCpu
,
267 'pluginCpuUsage': pluginProcessCpu
271 chrome
.processes
.onUpdated
.addListener(processListener
);
272 port
.onDisconnect
.addListener(function() {
273 chrome
.processes
.onUpdated
.removeListener(processListener
);
277 function stopAllRtpDump(requestInfo
, origin
, callback
) {
278 // Stops incoming and outgoing separately, otherwise stopRtpDump will fail if
279 // either type of dump has not been started.
280 chrome
.webrtcLoggingPrivate
.stopRtpDump(
281 requestInfo
, origin
, true, false,
283 chrome
.webrtcLoggingPrivate
.stopRtpDump(
284 requestInfo
, origin
, false, true, callback
);
288 chrome
.runtime
.onConnectExternal
.addListener(function(port
) {
289 if (port
.name
== 'onSinksChangedListener') {
290 onSinksChangedPort(port
);
291 } else if (port
.name
== 'chooseDesktopMedia') {
292 onChooseDesktopMediaPort(port
);
293 } else if (port
.name
== 'processCpu') {
296 // Unknown port type.