blob: 6d59dadbca0292ddf8408fe6c5a9adb61f6a0380 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
'use strict';
// Populated by constants from the browser. Used only by this file.
let NetInfoSources = null;
/**
* This class provides a "bridge" for communicating between the javascript and
* the browser.
*/
const BrowserBridge = (function() {
/**
* Delay in milliseconds between updates of certain browser information.
*/
const POLL_INTERVAL_MS = 5000;
/**
* @constructor
*/
function BrowserBridge() {
assertFirstConstructorCall(BrowserBridge);
// List of observers for various bits of browser state.
this.constantsObservers_ = [];
this.pollableDataHelpers_ = {};
// Add PollableDataHelpers for NetInfoSources, which retrieve information
// directly from the network stack.
this.addNetInfoPollableDataHelper(
'proxySettings', 'onProxySettingsChanged');
this.addNetInfoPollableDataHelper('badProxies', 'onBadProxiesChanged');
this.addNetInfoPollableDataHelper(
'hostResolverInfo', 'onHostResolverInfoChanged');
this.addNetInfoPollableDataHelper(
'socketPoolInfo', 'onSocketPoolInfoChanged');
this.addNetInfoPollableDataHelper(
'spdySessionInfo', 'onSpdySessionInfoChanged');
this.addNetInfoPollableDataHelper('spdyStatus', 'onSpdyStatusChanged');
this.addNetInfoPollableDataHelper(
'altSvcMappings', 'onAltSvcMappingsChanged');
this.addNetInfoPollableDataHelper('quicInfo', 'onQuicInfoChanged');
this.addNetInfoPollableDataHelper(
'reportingInfo', 'onReportingInfoChanged');
this.addNetInfoPollableDataHelper(
'httpCacheInfo', 'onHttpCacheInfoChanged');
// Add other PollableDataHelpers.
this.pollableDataHelpers_.serviceProviders = new PollableDataHelper(
'onServiceProvidersChanged');
this.pollableDataHelpers_.prerenderInfo = new PollableDataHelper(
'onPrerenderInfoChanged');
this.pollableDataHelpers_.extensionInfo = new PollableDataHelper(
'onExtensionInfoChanged');
}
cr.addSingletonGetter(BrowserBridge);
BrowserBridge.prototype = {
// -------------------------------------------------------------------------
// Messages received from the browser.
// -------------------------------------------------------------------------
receivedConstants(constants) {
NetInfoSources = constants.netInfoSources;
for (let i = 0; i < this.constantsObservers_.length; i++) {
this.constantsObservers_[i].onReceivedConstants(constants);
}
},
receivedLogEntries(logEntries) {
EventsTracker.getInstance().addLogEntries(logEntries);
},
receivedNetInfo(netInfo) {
// Dispatch |netInfo| to the various PollableDataHelpers listening to
// each field it contains.
//
// Currently information is only received from one source at a time, but
// the API does allow for data from more that one to be requested at once.
for (const source in netInfo) {
this.pollableDataHelpers_[source].update(netInfo[source]);
}
},
receivedServiceProviders(serviceProviders) {
this.pollableDataHelpers_.serviceProviders.update(serviceProviders);
},
receivedPrerenderInfo(prerenderInfo) {
this.pollableDataHelpers_.prerenderInfo.update(prerenderInfo);
},
receivedExtensionInfo(extensionInfo) {
this.pollableDataHelpers_.extensionInfo.update(extensionInfo);
},
receivedDataReductionProxyInfo(dataReductionProxyInfo) {
this.pollableDataHelpers_.dataReductionProxyInfo.update(
dataReductionProxyInfo);
},
// -------------------------------------------------------------------------
/**
* Adds a listener of the proxy settings. |observer| will be called back
* when data is received, through:
*
* observer.onProxySettingsChanged(proxySettings)
*
* |proxySettings| is a dictionary with (up to) two properties:
*
* "original" -- The settings that chrome was configured to use
* (i.e. system settings.)
* "effective" -- The "effective" proxy settings that chrome is using.
* (decides between the manual/automatic modes of the
* fetched settings).
*
* Each of these two configurations is formatted as a string, and may be
* omitted if not yet initialized.
*
* If |ignoreWhenUnchanged| is true, data is only sent when it changes.
* If it's false, data is sent whenever it's received from the browser.
*/
addProxySettingsObserver(observer, ignoreWhenUnchanged) {
this.pollableDataHelpers_.proxySettings.addObserver(
observer, ignoreWhenUnchanged);
},
/**
* Adds a listener of the proxy settings. |observer| will be called back
* when data is received, through:
*
* observer.onBadProxiesChanged(badProxies)
*
* |badProxies| is an array, where each entry has the property:
* badProxies[i].proxy_uri: String identify the proxy.
* badProxies[i].bad_until: The time when the proxy stops being considered
* bad. Note the time is in time ticks.
*/
addBadProxiesObserver(observer, ignoreWhenUnchanged) {
this.pollableDataHelpers_.badProxies.addObserver(
observer, ignoreWhenUnchanged);
},
/**
* Adds a listener of the host resolver info. |observer| will be called back
* when data is received, through:
*
* observer.onHostResolverInfoChanged(hostResolverInfo)
*/
addHostResolverInfoObserver(observer, ignoreWhenUnchanged) {
this.pollableDataHelpers_.hostResolverInfo.addObserver(
observer, ignoreWhenUnchanged);
},
/**
* Adds a listener of the socket pool. |observer| will be called back
* when data is received, through:
*
* observer.onSocketPoolInfoChanged(socketPoolInfo)
*/
addSocketPoolInfoObserver(observer, ignoreWhenUnchanged) {
this.pollableDataHelpers_.socketPoolInfo.addObserver(
observer, ignoreWhenUnchanged);
},
/**
* Adds a listener of the QUIC info. |observer| will be called back
* when data is received, through:
*
* observer.onQuicInfoChanged(quicInfo)
*/
addQuicInfoObserver(observer, ignoreWhenUnchanged) {
this.pollableDataHelpers_.quicInfo.addObserver(
observer, ignoreWhenUnchanged);
},
/**
* Adds a listener of the Reporting info. |observer| will be called back
* when data is received, through:
*
* observer.onReportingInfoChanged(reportingInfo)
*/
addReportingInfoObserver(observer, ignoreWhenUnchanged) {
this.pollableDataHelpers_.reportingInfo.addObserver(
observer, ignoreWhenUnchanged);
},
/**
* Adds a listener of the SPDY info. |observer| will be called back
* when data is received, through:
*
* observer.onSpdySessionInfoChanged(spdySessionInfo)
*/
addSpdySessionInfoObserver(observer, ignoreWhenUnchanged) {
this.pollableDataHelpers_.spdySessionInfo.addObserver(
observer, ignoreWhenUnchanged);
},
/**
* Adds a listener of the SPDY status. |observer| will be called back
* when data is received, through:
*
* observer.onSpdyStatusChanged(spdyStatus)
*/
addSpdyStatusObserver(observer, ignoreWhenUnchanged) {
this.pollableDataHelpers_.spdyStatus.addObserver(
observer, ignoreWhenUnchanged);
},
/**
* Adds a listener of the altSvcMappings. |observer| will be
* called back when data is received, through:
*
* observer.onAltSvcMappingsChanged(altSvcMappings)
*/
addAltSvcMappingsObserver(observer, ignoreWhenUnchanged) {
this.pollableDataHelpers_.altSvcMappings.addObserver(
observer, ignoreWhenUnchanged);
},
/**
* Adds a listener of the service providers info. |observer| will be called
* back when data is received, through:
*
* observer.onServiceProvidersChanged(serviceProviders)
*
* Will do nothing if on a platform other than Windows, as service providers
* are only present on Windows.
*/
addServiceProvidersObserver(observer, ignoreWhenUnchanged) {
if (this.pollableDataHelpers_.serviceProviders) {
this.pollableDataHelpers_.serviceProviders.addObserver(
observer, ignoreWhenUnchanged);
}
},
/**
* Adds a listener for the http cache info results.
* The observer will be called back with:
*
* observer.onHttpCacheInfoChanged(info);
*/
addHttpCacheInfoObserver(observer, ignoreWhenUnchanged) {
this.pollableDataHelpers_.httpCacheInfo.addObserver(
observer, ignoreWhenUnchanged);
},
/**
* Adds a listener for the received constants event. |observer| will be
* called back when the constants are received, through:
*
* observer.onReceivedConstants(constants);
*/
addConstantsObserver(observer) {
this.constantsObservers_.push(observer);
},
/**
* Adds a listener for updated prerender info events
* |observer| will be called back with:
*
* observer.onPrerenderInfoChanged(prerenderInfo);
*/
addPrerenderInfoObserver(observer, ignoreWhenUnchanged) {
this.pollableDataHelpers_.prerenderInfo.addObserver(
observer, ignoreWhenUnchanged);
},
/**
* Adds a listener of extension information. |observer| will be called
* back when data is received, through:
*
* observer.onExtensionInfoChanged(extensionInfo)
*/
addExtensionInfoObserver(observer, ignoreWhenUnchanged) {
this.pollableDataHelpers_.extensionInfo.addObserver(
observer, ignoreWhenUnchanged);
},
/**
* Adds a PollableDataHelper that listens to the specified NetInfoSource.
*/
addNetInfoPollableDataHelper(sourceName, observerMethodName) {
this.pollableDataHelpers_[sourceName] = new PollableDataHelper(
observerMethodName);
},
};
/**
* This is a helper class used by BrowserBridge, to keep track of:
* - the list of observers interested in some piece of data.
* - the last known value of that piece of data.
* - the name of the callback method to invoke on observers.
* - the update function.
* @constructor
*/
function PollableDataHelper(observerMethodName) {
this.observerMethodName_ = observerMethodName;
this.observerInfos_ = [];
}
PollableDataHelper.prototype = {
getObserverMethodName() {
return this.observerMethodName_;
},
isObserver(object) {
for (let i = 0; i < this.observerInfos_.length; i++) {
if (this.observerInfos_[i].observer === object) {
return true;
}
}
return false;
},
/**
* If |ignoreWhenUnchanged| is true, we won't send data again until it
* changes.
*/
addObserver(observer, ignoreWhenUnchanged) {
this.observerInfos_.push(new ObserverInfo(observer, ignoreWhenUnchanged));
},
removeObserver(observer) {
for (let i = 0; i < this.observerInfos_.length; i++) {
if (this.observerInfos_[i].observer === observer) {
this.observerInfos_.splice(i, 1);
return;
}
}
},
/**
* Helper function to handle calling all the observers, but ONLY if the data
* has actually changed since last time or the observer has yet to receive
* any data. This is used for data we received from browser on an update
* loop.
*/
update(data) {
const prevData = this.currentData_;
let changed = false;
// If the data hasn't changed since last time, will only need to notify
// observers that have not yet received any data.
if (!prevData || JSON.stringify(prevData) !== JSON.stringify(data)) {
changed = true;
this.currentData_ = data;
}
// Notify the observers of the change, as needed.
for (let i = 0; i < this.observerInfos_.length; i++) {
const observerInfo = this.observerInfos_[i];
if (changed || !observerInfo.hasReceivedData ||
!observerInfo.ignoreWhenUnchanged) {
observerInfo.observer[this.observerMethodName_](this.currentData_);
observerInfo.hasReceivedData = true;
}
}
},
/**
* Returns true if one of the observers actively wants the data
* (i.e. is visible).
*/
hasActiveObserver() {
for (let i = 0; i < this.observerInfos_.length; i++) {
if (this.observerInfos_[i].observer.isActive()) {
return true;
}
}
return false;
}
};
/**
* This is a helper class used by PollableDataHelper, to keep track of
* each observer and whether or not it has received any data. The
* latter is used to make sure that new observers get sent data on the
* update following their creation.
* @constructor
*/
function ObserverInfo(observer, ignoreWhenUnchanged) {
this.observer = observer;
this.hasReceivedData = false;
this.ignoreWhenUnchanged = ignoreWhenUnchanged;
}
return BrowserBridge;
})();