• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4
5chrome.runtime.onMessageExternal.addListener(
6    function(message, sender, sendResponse) {
7      function doSendResponse(value, error) {
8        var errorMessage = error || chrome.extension.lastError;
9        sendResponse({'value': value, 'error': errorMessage});
10      }
11
12      function getHost(url) {
13        if (!url)
14          return '';
15        // Use the DOM to parse the URL. Since we don't add the anchor to
16        // the page, this is the only reference to it and it will be
17        // deleted once it's gone out of scope.
18        var a = document.createElement('a');
19        a.href = url;
20        var origin = a.protocol + '//' + a.hostname;
21        if (a.port != '')
22          origin = origin + ':' + a.port;
23        origin = origin + '/';
24        return origin;
25      }
26
27      try {
28        var method = message['method'];
29        var origin = getHost(sender.url);
30        if (method == 'chooseDesktopMedia') {
31          // TODO(bemasc): Remove this method once the caller has transitioned
32          // to using the port.
33          var cancelId;
34          function sendResponseWithCancelId(streamId) {
35            var value = {'cancelId': cancelId, 'streamId': streamId};
36            doSendResponse(value);
37          }
38          cancelId = chrome.desktopCapture.chooseDesktopMedia(
39              ['screen', 'window'], sender.tab, sendResponseWithCancelId);
40          return true;
41        } else if (method == 'cancelChooseDesktopMedia') {
42          // TODO(bemasc): Remove this method (see above).
43          var cancelId = message['cancelId'];
44          chrome.desktopCapture.cancelChooseDesktopMedia(cancelId);
45          doSendResponse();
46          return false;
47        } else if (method == 'cpu.getInfo') {
48          chrome.system.cpu.getInfo(doSendResponse);
49          return true;
50        } else if (method == 'logging.setMetadata') {
51          var metaData = message['metaData'];
52          chrome.webrtcLoggingPrivate.setMetaData(
53              sender.tab.id, origin, metaData, doSendResponse);
54          return true;
55        } else if (method == 'logging.start') {
56          chrome.webrtcLoggingPrivate.start(
57              sender.tab.id, origin, doSendResponse);
58          return true;
59        } else if (method == 'logging.uploadOnRenderClose') {
60          chrome.webrtcLoggingPrivate.setUploadOnRenderClose(
61              sender.tab.id, origin, true);
62          doSendResponse();
63          return false;
64        } else if (method == 'logging.noUploadOnRenderClose') {
65          chrome.webrtcLoggingPrivate.setUploadOnRenderClose(
66              sender.tab.id, origin, false);
67          doSendResponse();
68          return false;
69        } else if (method == 'logging.stop') {
70          chrome.webrtcLoggingPrivate.stop(
71              sender.tab.id, origin, doSendResponse);
72          return true;
73        } else if (method == 'logging.upload') {
74          chrome.webrtcLoggingPrivate.upload(
75              sender.tab.id, origin, doSendResponse);
76          return true;
77        } else if (method == 'logging.stopAndUpload') {
78          stopAllRtpDump(sender.tab.id, origin, function() {
79            chrome.webrtcLoggingPrivate.stop(sender.tab.id, origin, function() {
80              chrome.webrtcLoggingPrivate.upload(
81                  sender.tab.id, origin, doSendResponse);
82            });
83          });
84          return true;
85        } else if (method == 'logging.discard') {
86          chrome.webrtcLoggingPrivate.discard(
87              sender.tab.id, origin, doSendResponse);
88          return true;
89        } else if (method == 'getSinks') {
90          chrome.webrtcAudioPrivate.getSinks(doSendResponse);
91          return true;
92        } else if (method == 'getActiveSink') {
93          chrome.webrtcAudioPrivate.getActiveSink(
94              sender.tab.id, doSendResponse);
95          return true;
96        } else if (method == 'setActiveSink') {
97          var sinkId = message['sinkId'];
98          chrome.webrtcAudioPrivate.setActiveSink(
99              sender.tab.id, sinkId, doSendResponse);
100          return true;
101        } else if (method == 'getAssociatedSink') {
102          var sourceId = message['sourceId'];
103          chrome.webrtcAudioPrivate.getAssociatedSink(
104              origin, sourceId, doSendResponse);
105          return true;
106        } else if (method == 'isExtensionEnabled') {
107          // This method is necessary because there may be more than one
108          // version of this extension, under different extension IDs. By
109          // first calling this method on the extension ID, the client can
110          // check if it's loaded; if it's not, the extension system will
111          // call the callback with no arguments and set
112          // chrome.runtime.lastError.
113          doSendResponse();
114          return false;
115        } else if (method == 'getNaclArchitecture') {
116          chrome.runtime.getPlatformInfo(function(obj) {
117            doSendResponse(obj.nacl_arch);
118          });
119          return true;
120        } else if (method == 'logging.startRtpDump') {
121          var incoming = message['incoming'] || false;
122          var outgoing = message['outgoing'] || false;
123          chrome.webrtcLoggingPrivate.startRtpDump(
124              sender.tab.id, origin, incoming, outgoing, doSendResponse);
125          return true;
126        } else if (method == 'logging.stopRtpDump') {
127          var incoming = message['incoming'] || false;
128          var outgoing = message['outgoing'] || false;
129          chrome.webrtcLoggingPrivate.stopRtpDump(
130              sender.tab.id, origin, incoming, outgoing, doSendResponse);
131          return true;
132        }
133        throw new Error('Unknown method: ' + method);
134      } catch (e) {
135        doSendResponse(null, e.name + ': ' + e.message);
136      }
137    }
138);
139
140// If Hangouts connects with a port named 'onSinksChangedListener', we
141// will register a listener and send it a message {'eventName':
142// 'onSinksChanged'} whenever the event fires.
143function onSinksChangedPort(port) {
144  function clientListener() {
145    port.postMessage({'eventName': 'onSinksChanged'});
146  }
147  chrome.webrtcAudioPrivate.onSinksChanged.addListener(clientListener);
148
149  port.onDisconnect.addListener(function() {
150    chrome.webrtcAudioPrivate.onSinksChanged.removeListener(
151        clientListener);
152  });
153}
154
155// This is a one-time-use port for calling chooseDesktopMedia.  The page
156// sends one message, identifying the requested source types, and the
157// extension sends a single reply, with the user's selected streamId.  A port
158// is used so that if the page is closed before that message is sent, the
159// window picker dialog will be closed.
160function onChooseDesktopMediaPort(port) {
161  function sendResponse(streamId) {
162    port.postMessage({'value': {'streamId': streamId}});
163    port.disconnect();
164  }
165
166  port.onMessage.addListener(function(message) {
167    var method = message['method'];
168    if (method == 'chooseDesktopMedia') {
169      var sources = message['sources'];
170      var cancelId = chrome.desktopCapture.chooseDesktopMedia(
171          sources, port.sender.tab, sendResponse);
172      port.onDisconnect.addListener(function() {
173        // This method has no effect if called after the user has selected a
174        // desktop media source, so it does not need to be conditional.
175        chrome.desktopCapture.cancelChooseDesktopMedia(cancelId);
176      });
177    }
178  });
179}
180
181// A port for continuously reporting relevant CPU usage information to the page.
182function onProcessCpu(port) {
183  var tabPid;
184  function processListener(processes) {
185    if (tabPid == undefined) {
186      // getProcessIdForTab sometimes fails, and does not call the callback.
187      // (Tracked at https://crbug.com/368855.)
188      // This call retries it on each process update until it succeeds.
189      chrome.processes.getProcessIdForTab(port.sender.tab.id, function(x) {
190        tabPid = x;
191      });
192      return;
193    }
194    var tabProcess = processes[tabPid];
195    if (!tabProcess) {
196      return;
197    }
198    var pluginProcessCpu = 0, browserProcessCpu = 0, gpuProcessCpu = 0;
199    for (var pid in processes) {
200      var process = processes[pid];
201      if (process.type == 'browser') {
202        browserProcessCpu = process.cpu;
203      } else if (process.type == 'gpu') {
204        gpuProcessCpu = process.cpu;
205      } else if ((process.type == 'plugin' || process.type == 'nacl') &&
206                 process.title.toLowerCase().indexOf('hangouts') > 0) {
207        pluginProcessCpu = process.cpu;
208      }
209    }
210
211    port.postMessage({
212      'tabCpuUsage': tabProcess.cpu,
213      'browserCpuUsage': browserProcessCpu,
214      'gpuCpuUsage': gpuProcessCpu,
215      'pluginCpuUsage': pluginProcessCpu
216    });
217  }
218
219  chrome.processes.onUpdated.addListener(processListener);
220  port.onDisconnect.addListener(function() {
221    chrome.processes.onUpdated.removeListener(processListener);
222  });
223}
224
225function stopAllRtpDump(tabId, origin, callback) {
226  // Stops incoming and outgoing separately, otherwise stopRtpDump will fail if
227  // either type of dump has not been started.
228  chrome.webrtcLoggingPrivate.stopRtpDump(
229      tabId, origin, true, false,
230      function() {
231        chrome.webrtcLoggingPrivate.stopRtpDump(
232            tabId, origin, false, true, callback);
233      });
234}
235
236chrome.runtime.onConnectExternal.addListener(function(port) {
237  if (port.name == 'onSinksChangedListener') {
238    onSinksChangedPort(port);
239  } else if (port.name == 'chooseDesktopMedia') {
240    onChooseDesktopMediaPort(port);
241  } else if (port.name == 'processCpu') {
242    onProcessCpu(port);
243  } else {
244    // Unknown port type.
245    port.disconnect();
246  }
247});
248