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 // Populated by constants from the browser. Used only by this file.
6 var NetInfoSources = null;
9 * This class provides a "bridge" for communicating between the javascript and
12 var BrowserBridge = (function() {
16 * Delay in milliseconds between updates of certain browser information.
18 var POLL_INTERVAL_MS = 5000;
23 function BrowserBridge() {
24 assertFirstConstructorCall(BrowserBridge);
26 // List of observers for various bits of browser state.
27 this.connectionTestsObservers_ = [];
28 this.hstsObservers_ = [];
29 this.constantsObservers_ = [];
30 this.crosONCFileParseObservers_ = [];
31 this.storeDebugLogsObservers_ = [];
32 this.setNetworkDebugModeObservers_ = [];
33 // Unprocessed data received before the constants. This serves to protect
34 // against passing along data before having information on how to interpret
36 this.earlyReceivedData_ = [];
38 this.pollableDataHelpers_ = {};
40 // Add PollableDataHelpers for NetInfoSources, which retrieve information
41 // directly from the network stack.
42 this.addNetInfoPollableDataHelper('proxySettings',
43 'onProxySettingsChanged');
44 this.addNetInfoPollableDataHelper('badProxies', 'onBadProxiesChanged');
45 this.addNetInfoPollableDataHelper('hostResolverInfo',
46 'onHostResolverInfoChanged');
47 this.addNetInfoPollableDataHelper('socketPoolInfo',
48 'onSocketPoolInfoChanged');
49 this.addNetInfoPollableDataHelper('spdySessionInfo',
50 'onSpdySessionInfoChanged');
51 this.addNetInfoPollableDataHelper('spdyStatus', 'onSpdyStatusChanged');
52 this.addNetInfoPollableDataHelper('spdyAlternateProtocolMappings',
53 'onSpdyAlternateProtocolMappingsChanged');
54 this.addNetInfoPollableDataHelper('quicInfo', 'onQuicInfoChanged');
55 this.addNetInfoPollableDataHelper('sdchInfo', 'onSdchInfoChanged');
56 this.addNetInfoPollableDataHelper('httpCacheInfo',
57 'onHttpCacheInfoChanged');
59 // Add other PollableDataHelpers.
60 this.pollableDataHelpers_.sessionNetworkStats =
61 new PollableDataHelper('onSessionNetworkStatsChanged',
62 this.sendGetSessionNetworkStats.bind(this));
63 this.pollableDataHelpers_.historicNetworkStats =
64 new PollableDataHelper('onHistoricNetworkStatsChanged',
65 this.sendGetHistoricNetworkStats.bind(this));
67 this.pollableDataHelpers_.serviceProviders =
68 new PollableDataHelper('onServiceProvidersChanged',
69 this.sendGetServiceProviders.bind(this));
71 this.pollableDataHelpers_.prerenderInfo =
72 new PollableDataHelper('onPrerenderInfoChanged',
73 this.sendGetPrerenderInfo.bind(this));
74 this.pollableDataHelpers_.extensionInfo =
75 new PollableDataHelper('onExtensionInfoChanged',
76 this.sendGetExtensionInfo.bind(this));
77 this.pollableDataHelpers_.dataReductionProxyInfo =
78 new PollableDataHelper('onDataReductionProxyInfoChanged',
79 this.sendGetDataReductionProxyInfo.bind(this));
81 // Setting this to true will cause messages from the browser to be ignored,
82 // and no messages will be sent to the browser, either. Intended for use
83 // when viewing log files.
84 this.disabled_ = false;
86 // Interval id returned by window.setInterval for polling timer.
87 this.pollIntervalId_ = null;
90 cr.addSingletonGetter(BrowserBridge);
92 BrowserBridge.prototype = {
94 //--------------------------------------------------------------------------
95 // Messages sent to the browser
96 //--------------------------------------------------------------------------
99 * Wraps |chrome.send|. Doesn't send anything when disabled.
101 send: function(value1, value2) {
102 if (!this.disabled_) {
103 if (arguments.length == 1) {
105 } else if (arguments.length == 2) {
106 chrome.send(value1, value2);
108 throw 'Unsupported number of arguments.';
113 sendReady: function() {
114 this.send('notifyReady');
115 this.setPollInterval(POLL_INTERVAL_MS);
119 * Some of the data we are interested is not currently exposed as a
120 * stream. This starts polling those with active observers (visible
121 * views) every |intervalMs|. Subsequent calls override previous calls
122 * to this function. If |intervalMs| is 0, stops polling.
124 setPollInterval: function(intervalMs) {
125 if (this.pollIntervalId_ !== null) {
126 window.clearInterval(this.pollIntervalId_);
127 this.pollIntervalId_ = null;
130 if (intervalMs > 0) {
131 this.pollIntervalId_ =
132 window.setInterval(this.checkForUpdatedInfo.bind(this, false),
137 sendGetNetInfo: function(netInfoSource) {
138 // If don't have constants yet, don't do anything yet.
140 this.send('getNetInfo', [NetInfoSources[netInfoSource]]);
143 sendReloadProxySettings: function() {
144 this.send('reloadProxySettings');
147 sendClearBadProxies: function() {
148 this.send('clearBadProxies');
151 sendClearHostResolverCache: function() {
152 this.send('clearHostResolverCache');
155 sendClearBrowserCache: function() {
156 this.send('clearBrowserCache');
159 sendClearAllCache: function() {
160 this.sendClearHostResolverCache();
161 this.sendClearBrowserCache();
164 sendStartConnectionTests: function(url) {
165 this.send('startConnectionTests', [url]);
168 sendHSTSQuery: function(domain) {
169 this.send('hstsQuery', [domain]);
172 sendHSTSAdd: function(domain, sts_include_subdomains,
173 pkp_include_subdomains, pins) {
174 this.send('hstsAdd', [domain, sts_include_subdomains,
175 pkp_include_subdomains, pins]);
178 sendHSTSDelete: function(domain) {
179 this.send('hstsDelete', [domain]);
182 sendGetSessionNetworkStats: function() {
183 this.send('getSessionNetworkStats');
186 sendGetHistoricNetworkStats: function() {
187 this.send('getHistoricNetworkStats');
190 sendCloseIdleSockets: function() {
191 this.send('closeIdleSockets');
194 sendFlushSocketPools: function() {
195 this.send('flushSocketPools');
198 sendGetServiceProviders: function() {
199 this.send('getServiceProviders');
202 sendGetPrerenderInfo: function() {
203 this.send('getPrerenderInfo');
206 sendGetExtensionInfo: function() {
207 this.send('getExtensionInfo');
210 sendGetDataReductionProxyInfo: function() {
211 this.send('getDataReductionProxyInfo');
214 enableIPv6: function() {
215 this.send('enableIPv6');
218 setLogLevel: function(logLevel) {
219 this.send('setLogLevel', ['' + logLevel]);
222 importONCFile: function(fileContent, passcode) {
223 this.send('importONCFile', [fileContent, passcode]);
226 storeDebugLogs: function() {
227 this.send('storeDebugLogs');
230 setNetworkDebugMode: function(subsystem) {
231 this.send('setNetworkDebugMode', [subsystem]);
234 //--------------------------------------------------------------------------
235 // Messages received from the browser.
236 //--------------------------------------------------------------------------
238 receive: function(command, params) {
239 // Does nothing if disabled.
243 // If no constants have been received, and params does not contain the
244 // constants, delay handling the data.
245 if (Constants == null && command != 'receivedConstants') {
246 this.earlyReceivedData_.push({ command: command, params: params });
250 this[command](params);
252 // Handle any data that was received early in the order it was received,
253 // once the constants have been processed.
254 if (this.earlyReceivedData_ != null) {
255 for (var i = 0; i < this.earlyReceivedData_.length; i++) {
256 var command = this.earlyReceivedData_[i];
257 this[command.command](command.params);
259 this.earlyReceivedData_ = null;
263 receivedConstants: function(constants) {
264 NetInfoSources = constants.netInfoSources;
265 for (var i = 0; i < this.constantsObservers_.length; i++)
266 this.constantsObservers_[i].onReceivedConstants(constants);
267 // May have been waiting for the constants to be received before getting
268 // information for the currently displayed tab.
269 this.checkForUpdatedInfo();
272 receivedLogEntries: function(logEntries) {
273 EventsTracker.getInstance().addLogEntries(logEntries);
276 receivedNetInfo: function(netInfo) {
277 // Dispatch |netInfo| to the various PollableDataHelpers listening to
278 // each field it contains.
280 // Currently information is only received from one source at a time, but
281 // the API does allow for data from more that one to be requested at once.
282 for (var source in netInfo)
283 this.pollableDataHelpers_[source].update(netInfo[source]);
286 receivedSessionNetworkStats: function(sessionNetworkStats) {
287 this.pollableDataHelpers_.sessionNetworkStats.update(sessionNetworkStats);
290 receivedHistoricNetworkStats: function(historicNetworkStats) {
291 this.pollableDataHelpers_.historicNetworkStats.update(
292 historicNetworkStats);
295 receivedServiceProviders: function(serviceProviders) {
296 this.pollableDataHelpers_.serviceProviders.update(serviceProviders);
299 receivedStartConnectionTestSuite: function() {
300 for (var i = 0; i < this.connectionTestsObservers_.length; i++)
301 this.connectionTestsObservers_[i].onStartedConnectionTestSuite();
304 receivedStartConnectionTestExperiment: function(experiment) {
305 for (var i = 0; i < this.connectionTestsObservers_.length; i++) {
306 this.connectionTestsObservers_[i].onStartedConnectionTestExperiment(
311 receivedCompletedConnectionTestExperiment: function(info) {
312 for (var i = 0; i < this.connectionTestsObservers_.length; i++) {
313 this.connectionTestsObservers_[i].onCompletedConnectionTestExperiment(
314 info.experiment, info.result);
318 receivedCompletedConnectionTestSuite: function() {
319 for (var i = 0; i < this.connectionTestsObservers_.length; i++)
320 this.connectionTestsObservers_[i].onCompletedConnectionTestSuite();
323 receivedHSTSResult: function(info) {
324 for (var i = 0; i < this.hstsObservers_.length; i++)
325 this.hstsObservers_[i].onHSTSQueryResult(info);
328 receivedONCFileParse: function(error) {
329 for (var i = 0; i < this.crosONCFileParseObservers_.length; i++)
330 this.crosONCFileParseObservers_[i].onONCFileParse(error);
333 receivedStoreDebugLogs: function(status) {
334 for (var i = 0; i < this.storeDebugLogsObservers_.length; i++)
335 this.storeDebugLogsObservers_[i].onStoreDebugLogs(status);
338 receivedSetNetworkDebugMode: function(status) {
339 for (var i = 0; i < this.setNetworkDebugModeObservers_.length; i++)
340 this.setNetworkDebugModeObservers_[i].onSetNetworkDebugMode(status);
343 receivedPrerenderInfo: function(prerenderInfo) {
344 this.pollableDataHelpers_.prerenderInfo.update(prerenderInfo);
347 receivedExtensionInfo: function(extensionInfo) {
348 this.pollableDataHelpers_.extensionInfo.update(extensionInfo);
351 receivedDataReductionProxyInfo: function(dataReductionProxyInfo) {
352 this.pollableDataHelpers_.dataReductionProxyInfo.update(
353 dataReductionProxyInfo);
356 //--------------------------------------------------------------------------
359 * Prevents receiving/sending events to/from the browser.
361 disable: function() {
362 this.disabled_ = true;
363 this.setPollInterval(0);
367 * Returns true if the BrowserBridge has been disabled.
369 isDisabled: function() {
370 return this.disabled_;
374 * Adds a listener of the proxy settings. |observer| will be called back
375 * when data is received, through:
377 * observer.onProxySettingsChanged(proxySettings)
379 * |proxySettings| is a dictionary with (up to) two properties:
381 * "original" -- The settings that chrome was configured to use
382 * (i.e. system settings.)
383 * "effective" -- The "effective" proxy settings that chrome is using.
384 * (decides between the manual/automatic modes of the
387 * Each of these two configurations is formatted as a string, and may be
388 * omitted if not yet initialized.
390 * If |ignoreWhenUnchanged| is true, data is only sent when it changes.
391 * If it's false, data is sent whenever it's received from the browser.
393 addProxySettingsObserver: function(observer, ignoreWhenUnchanged) {
394 this.pollableDataHelpers_.proxySettings.addObserver(observer,
395 ignoreWhenUnchanged);
399 * Adds a listener of the proxy settings. |observer| will be called back
400 * when data is received, through:
402 * observer.onBadProxiesChanged(badProxies)
404 * |badProxies| is an array, where each entry has the property:
405 * badProxies[i].proxy_uri: String identify the proxy.
406 * badProxies[i].bad_until: The time when the proxy stops being considered
407 * bad. Note the time is in time ticks.
409 addBadProxiesObserver: function(observer, ignoreWhenUnchanged) {
410 this.pollableDataHelpers_.badProxies.addObserver(observer,
411 ignoreWhenUnchanged);
415 * Adds a listener of the host resolver info. |observer| will be called back
416 * when data is received, through:
418 * observer.onHostResolverInfoChanged(hostResolverInfo)
420 addHostResolverInfoObserver: function(observer, ignoreWhenUnchanged) {
421 this.pollableDataHelpers_.hostResolverInfo.addObserver(
422 observer, ignoreWhenUnchanged);
426 * Adds a listener of the socket pool. |observer| will be called back
427 * when data is received, through:
429 * observer.onSocketPoolInfoChanged(socketPoolInfo)
431 addSocketPoolInfoObserver: function(observer, ignoreWhenUnchanged) {
432 this.pollableDataHelpers_.socketPoolInfo.addObserver(observer,
433 ignoreWhenUnchanged);
437 * Adds a listener of the network session. |observer| will be called back
438 * when data is received, through:
440 * observer.onSessionNetworkStatsChanged(sessionNetworkStats)
442 addSessionNetworkStatsObserver: function(observer, ignoreWhenUnchanged) {
443 this.pollableDataHelpers_.sessionNetworkStats.addObserver(
444 observer, ignoreWhenUnchanged);
448 * Adds a listener of persistent network session data. |observer| will be
449 * called back when data is received, through:
451 * observer.onHistoricNetworkStatsChanged(historicNetworkStats)
453 addHistoricNetworkStatsObserver: function(observer, ignoreWhenUnchanged) {
454 this.pollableDataHelpers_.historicNetworkStats.addObserver(
455 observer, ignoreWhenUnchanged);
459 * Adds a listener of the QUIC info. |observer| will be called back
460 * when data is received, through:
462 * observer.onQuicInfoChanged(quicInfo)
464 addQuicInfoObserver: function(observer, ignoreWhenUnchanged) {
465 this.pollableDataHelpers_.quicInfo.addObserver(
466 observer, ignoreWhenUnchanged);
470 * Adds a listener of the SPDY info. |observer| will be called back
471 * when data is received, through:
473 * observer.onSpdySessionInfoChanged(spdySessionInfo)
475 addSpdySessionInfoObserver: function(observer, ignoreWhenUnchanged) {
476 this.pollableDataHelpers_.spdySessionInfo.addObserver(
477 observer, ignoreWhenUnchanged);
481 * Adds a listener of the SPDY status. |observer| will be called back
482 * when data is received, through:
484 * observer.onSpdyStatusChanged(spdyStatus)
486 addSpdyStatusObserver: function(observer, ignoreWhenUnchanged) {
487 this.pollableDataHelpers_.spdyStatus.addObserver(observer,
488 ignoreWhenUnchanged);
492 * Adds a listener of the AlternateProtocolMappings. |observer| will be
493 * called back when data is received, through:
495 * observer.onSpdyAlternateProtocolMappingsChanged(
496 * spdyAlternateProtocolMappings)
498 addSpdyAlternateProtocolMappingsObserver: function(observer,
499 ignoreWhenUnchanged) {
500 this.pollableDataHelpers_.spdyAlternateProtocolMappings.addObserver(
501 observer, ignoreWhenUnchanged);
505 * Adds a listener of the service providers info. |observer| will be called
506 * back when data is received, through:
508 * observer.onServiceProvidersChanged(serviceProviders)
510 * Will do nothing if on a platform other than Windows, as service providers
511 * are only present on Windows.
513 addServiceProvidersObserver: function(observer, ignoreWhenUnchanged) {
514 if (this.pollableDataHelpers_.serviceProviders) {
515 this.pollableDataHelpers_.serviceProviders.addObserver(
516 observer, ignoreWhenUnchanged);
521 * Adds a listener for the progress of the connection tests.
522 * The observer will be called back with:
524 * observer.onStartedConnectionTestSuite();
525 * observer.onStartedConnectionTestExperiment(experiment);
526 * observer.onCompletedConnectionTestExperiment(experiment, result);
527 * observer.onCompletedConnectionTestSuite();
529 addConnectionTestsObserver: function(observer) {
530 this.connectionTestsObservers_.push(observer);
534 * Adds a listener for the http cache info results.
535 * The observer will be called back with:
537 * observer.onHttpCacheInfoChanged(info);
539 addHttpCacheInfoObserver: function(observer, ignoreWhenUnchanged) {
540 this.pollableDataHelpers_.httpCacheInfo.addObserver(
541 observer, ignoreWhenUnchanged);
545 * Adds a listener for the results of HSTS (HTTPS Strict Transport Security)
546 * queries. The observer will be called back with:
548 * observer.onHSTSQueryResult(result);
550 addHSTSObserver: function(observer) {
551 this.hstsObservers_.push(observer);
555 * Adds a listener for ONC file parse status. The observer will be called
558 * observer.onONCFileParse(error);
560 addCrosONCFileParseObserver: function(observer) {
561 this.crosONCFileParseObservers_.push(observer);
565 * Adds a listener for storing log file status. The observer will be called
568 * observer.onStoreDebugLogs(status);
570 addStoreDebugLogsObserver: function(observer) {
571 this.storeDebugLogsObservers_.push(observer);
575 * Adds a listener for network debugging mode status. The observer
576 * will be called back with:
578 * observer.onSetNetworkDebugMode(status);
580 addSetNetworkDebugModeObserver: function(observer) {
581 this.setNetworkDebugModeObservers_.push(observer);
585 * Adds a listener for the received constants event. |observer| will be
586 * called back when the constants are received, through:
588 * observer.onReceivedConstants(constants);
590 addConstantsObserver: function(observer) {
591 this.constantsObservers_.push(observer);
595 * Adds a listener for updated prerender info events
596 * |observer| will be called back with:
598 * observer.onPrerenderInfoChanged(prerenderInfo);
600 addPrerenderInfoObserver: function(observer, ignoreWhenUnchanged) {
601 this.pollableDataHelpers_.prerenderInfo.addObserver(
602 observer, ignoreWhenUnchanged);
606 * Adds a listener of extension information. |observer| will be called
607 * back when data is received, through:
609 * observer.onExtensionInfoChanged(extensionInfo)
611 addExtensionInfoObserver: function(observer, ignoreWhenUnchanged) {
612 this.pollableDataHelpers_.extensionInfo.addObserver(
613 observer, ignoreWhenUnchanged);
617 * Adds a listener of the data reduction proxy info. |observer| will be
618 * called back when data is received, through:
620 * observer.onDataReductionProxyInfoChanged(dataReductionProxyInfo)
622 addDataReductionProxyInfoObserver: function(observer, ignoreWhenUnchanged) {
623 this.pollableDataHelpers_.dataReductionProxyInfo.addObserver(
624 observer, ignoreWhenUnchanged);
628 * Adds a listener of SDCH information. |observer| will be called
629 * back when data is received, through:
631 * observer.onSdchInfoChanged(sdchInfo)
633 addSdchInfoObserver: function(observer, ignoreWhenUnchanged) {
634 this.pollableDataHelpers_.sdchInfo.addObserver(
635 observer, ignoreWhenUnchanged);
639 * If |force| is true, calls all startUpdate functions. Otherwise, just
640 * runs updates with active observers.
642 checkForUpdatedInfo: function(force) {
643 for (var name in this.pollableDataHelpers_) {
644 var helper = this.pollableDataHelpers_[name];
645 if (force || helper.hasActiveObserver())
646 helper.startUpdate();
651 * Calls all startUpdate functions and, if |callback| is non-null,
652 * calls it with the results of all updates.
654 updateAllInfo: function(callback) {
656 new UpdateAllObserver(callback, this.pollableDataHelpers_);
657 this.checkForUpdatedInfo(true);
661 * Adds a PollableDataHelper that listens to the specified NetInfoSource.
663 addNetInfoPollableDataHelper: function(sourceName, observerMethodName) {
664 this.pollableDataHelpers_[sourceName] = new PollableDataHelper(
665 observerMethodName, this.sendGetNetInfo.bind(this, sourceName));
670 * This is a helper class used by BrowserBridge, to keep track of:
671 * - the list of observers interested in some piece of data.
672 * - the last known value of that piece of data.
673 * - the name of the callback method to invoke on observers.
674 * - the update function.
677 function PollableDataHelper(observerMethodName, startUpdateFunction) {
678 this.observerMethodName_ = observerMethodName;
679 this.startUpdate = startUpdateFunction;
680 this.observerInfos_ = [];
683 PollableDataHelper.prototype = {
684 getObserverMethodName: function() {
685 return this.observerMethodName_;
688 isObserver: function(object) {
689 for (var i = 0; i < this.observerInfos_.length; i++) {
690 if (this.observerInfos_[i].observer === object)
697 * If |ignoreWhenUnchanged| is true, we won't send data again until it
700 addObserver: function(observer, ignoreWhenUnchanged) {
701 this.observerInfos_.push(new ObserverInfo(observer, ignoreWhenUnchanged));
704 removeObserver: function(observer) {
705 for (var i = 0; i < this.observerInfos_.length; i++) {
706 if (this.observerInfos_[i].observer === observer) {
707 this.observerInfos_.splice(i, 1);
714 * Helper function to handle calling all the observers, but ONLY if the data
715 * has actually changed since last time or the observer has yet to receive
716 * any data. This is used for data we received from browser on an update
719 update: function(data) {
720 var prevData = this.currentData_;
723 // If the data hasn't changed since last time, will only need to notify
724 // observers that have not yet received any data.
725 if (!prevData || JSON.stringify(prevData) != JSON.stringify(data)) {
727 this.currentData_ = data;
730 // Notify the observers of the change, as needed.
731 for (var i = 0; i < this.observerInfos_.length; i++) {
732 var observerInfo = this.observerInfos_[i];
733 if (changed || !observerInfo.hasReceivedData ||
734 !observerInfo.ignoreWhenUnchanged) {
735 observerInfo.observer[this.observerMethodName_](this.currentData_);
736 observerInfo.hasReceivedData = true;
742 * Returns true if one of the observers actively wants the data
745 hasActiveObserver: function() {
746 for (var i = 0; i < this.observerInfos_.length; i++) {
747 if (this.observerInfos_[i].observer.isActive())
755 * This is a helper class used by PollableDataHelper, to keep track of
756 * each observer and whether or not it has received any data. The
757 * latter is used to make sure that new observers get sent data on the
758 * update following their creation.
761 function ObserverInfo(observer, ignoreWhenUnchanged) {
762 this.observer = observer;
763 this.hasReceivedData = false;
764 this.ignoreWhenUnchanged = ignoreWhenUnchanged;
768 * This is a helper class used by BrowserBridge to send data to
769 * a callback once data from all polls has been received.
771 * It works by keeping track of how many polling functions have
772 * yet to receive data, and recording the data as it it received.
776 function UpdateAllObserver(callback, pollableDataHelpers) {
777 this.callback_ = callback;
778 this.observingCount_ = 0;
779 this.updatedData_ = {};
781 for (var name in pollableDataHelpers) {
782 ++this.observingCount_;
783 var helper = pollableDataHelpers[name];
784 helper.addObserver(this);
785 this[helper.getObserverMethodName()] =
786 this.onDataReceived_.bind(this, helper, name);
790 UpdateAllObserver.prototype = {
791 isActive: function() {
795 onDataReceived_: function(helper, name, data) {
796 helper.removeObserver(this);
797 --this.observingCount_;
798 this.updatedData_[name] = data;
799 if (this.observingCount_ == 0)
800 this.callback_(this.updatedData_);
804 return BrowserBridge;