| // 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. |
| |
| /** |
| * This class provides a "bridge" for communicating between the javascript and |
| * the browser. |
| */ |
| var BrowserBridge = (function() { |
| 'use strict'; |
| |
| /** |
| * Delay in milliseconds between updates of certain browser information. |
| */ |
| var POLL_INTERVAL_MS = 5000; |
| |
| /** |
| * @constructor |
| */ |
| function BrowserBridge() { |
| assertFirstConstructorCall(BrowserBridge); |
| |
| // List of observers for various bits of browser state. |
| this.connectionTestsObservers_ = []; |
| this.hstsObservers_ = []; |
| this.constantsObservers_ = []; |
| this.crosONCFileParseObservers_ = []; |
| this.storeDebugLogsObservers_ = []; |
| this.setNetworkDebugModeObservers_ = []; |
| // Unprocessed data received before the constants. This serves to protect |
| // against passing along data before having information on how to interpret |
| // it. |
| this.earlyReceivedData_ = []; |
| |
| this.pollableDataHelpers_ = {}; |
| this.pollableDataHelpers_.proxySettings = |
| new PollableDataHelper('onProxySettingsChanged', |
| this.sendGetProxySettings.bind(this)); |
| this.pollableDataHelpers_.badProxies = |
| new PollableDataHelper('onBadProxiesChanged', |
| this.sendGetBadProxies.bind(this)); |
| this.pollableDataHelpers_.httpCacheInfo = |
| new PollableDataHelper('onHttpCacheInfoChanged', |
| this.sendGetHttpCacheInfo.bind(this)); |
| this.pollableDataHelpers_.hostResolverInfo = |
| new PollableDataHelper('onHostResolverInfoChanged', |
| this.sendGetHostResolverInfo.bind(this)); |
| this.pollableDataHelpers_.socketPoolInfo = |
| new PollableDataHelper('onSocketPoolInfoChanged', |
| this.sendGetSocketPoolInfo.bind(this)); |
| this.pollableDataHelpers_.sessionNetworkStats = |
| new PollableDataHelper('onSessionNetworkStatsChanged', |
| this.sendGetSessionNetworkStats.bind(this)); |
| this.pollableDataHelpers_.historicNetworkStats = |
| new PollableDataHelper('onHistoricNetworkStatsChanged', |
| this.sendGetHistoricNetworkStats.bind(this)); |
| this.pollableDataHelpers_.quicInfo = |
| new PollableDataHelper('onQuicInfoChanged', |
| this.sendGetQuicInfo.bind(this)); |
| this.pollableDataHelpers_.spdySessionInfo = |
| new PollableDataHelper('onSpdySessionInfoChanged', |
| this.sendGetSpdySessionInfo.bind(this)); |
| this.pollableDataHelpers_.spdyStatus = |
| new PollableDataHelper('onSpdyStatusChanged', |
| this.sendGetSpdyStatus.bind(this)); |
| this.pollableDataHelpers_.spdyAlternateProtocolMappings = |
| new PollableDataHelper('onSpdyAlternateProtocolMappingsChanged', |
| this.sendGetSpdyAlternateProtocolMappings.bind( |
| this)); |
| if (cr.isWindows) { |
| this.pollableDataHelpers_.serviceProviders = |
| new PollableDataHelper('onServiceProvidersChanged', |
| this.sendGetServiceProviders.bind(this)); |
| } |
| this.pollableDataHelpers_.prerenderInfo = |
| new PollableDataHelper('onPrerenderInfoChanged', |
| this.sendGetPrerenderInfo.bind(this)); |
| this.pollableDataHelpers_.httpPipeliningStatus = |
| new PollableDataHelper('onHttpPipeliningStatusChanged', |
| this.sendGetHttpPipeliningStatus.bind(this)); |
| this.pollableDataHelpers_.extensionInfo = |
| new PollableDataHelper('onExtensionInfoChanged', |
| this.sendGetExtensionInfo.bind(this)); |
| if (cr.isChromeOS) { |
| this.pollableDataHelpers_.systemLog = |
| new PollableDataHelper('onSystemLogChanged', |
| this.getSystemLog.bind(this, 'syslog')); |
| } |
| |
| // Setting this to true will cause messages from the browser to be ignored, |
| // and no messages will be sent to the browser, either. Intended for use |
| // when viewing log files. |
| this.disabled_ = false; |
| |
| // Interval id returned by window.setInterval for polling timer. |
| this.pollIntervalId_ = null; |
| } |
| |
| cr.addSingletonGetter(BrowserBridge); |
| |
| BrowserBridge.prototype = { |
| |
| //-------------------------------------------------------------------------- |
| // Messages sent to the browser |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Wraps |chrome.send|. Doesn't send anything when disabled. |
| */ |
| send: function(value1, value2) { |
| if (!this.disabled_) { |
| if (arguments.length == 1) { |
| chrome.send(value1); |
| } else if (arguments.length == 2) { |
| chrome.send(value1, value2); |
| } else { |
| throw 'Unsupported number of arguments.'; |
| } |
| } |
| }, |
| |
| sendReady: function() { |
| this.send('notifyReady'); |
| this.setPollInterval(POLL_INTERVAL_MS); |
| }, |
| |
| /** |
| * Some of the data we are interested is not currently exposed as a |
| * stream. This starts polling those with active observers (visible |
| * views) every |intervalMs|. Subsequent calls override previous calls |
| * to this function. If |intervalMs| is 0, stops polling. |
| */ |
| setPollInterval: function(intervalMs) { |
| if (this.pollIntervalId_ !== null) { |
| window.clearInterval(this.pollIntervalId_); |
| this.pollIntervalId_ = null; |
| } |
| |
| if (intervalMs > 0) { |
| this.pollIntervalId_ = |
| window.setInterval(this.checkForUpdatedInfo.bind(this, false), |
| intervalMs); |
| } |
| }, |
| |
| sendGetProxySettings: function() { |
| // The browser will call receivedProxySettings on completion. |
| this.send('getProxySettings'); |
| }, |
| |
| sendReloadProxySettings: function() { |
| this.send('reloadProxySettings'); |
| }, |
| |
| sendGetBadProxies: function() { |
| // The browser will call receivedBadProxies on completion. |
| this.send('getBadProxies'); |
| }, |
| |
| sendGetHostResolverInfo: function() { |
| // The browser will call receivedHostResolverInfo on completion. |
| this.send('getHostResolverInfo'); |
| }, |
| |
| sendClearBadProxies: function() { |
| this.send('clearBadProxies'); |
| }, |
| |
| sendClearHostResolverCache: function() { |
| this.send('clearHostResolverCache'); |
| }, |
| |
| sendClearBrowserCache: function() { |
| this.send('clearBrowserCache'); |
| }, |
| |
| sendClearAllCache: function() { |
| this.sendClearHostResolverCache(); |
| this.sendClearBrowserCache(); |
| }, |
| |
| sendStartConnectionTests: function(url) { |
| this.send('startConnectionTests', [url]); |
| }, |
| |
| sendHSTSQuery: function(domain) { |
| this.send('hstsQuery', [domain]); |
| }, |
| |
| sendHSTSAdd: function(domain, sts_include_subdomains, |
| pkp_include_subdomains, pins) { |
| this.send('hstsAdd', [domain, sts_include_subdomains, |
| pkp_include_subdomains, pins]); |
| }, |
| |
| sendHSTSDelete: function(domain) { |
| this.send('hstsDelete', [domain]); |
| }, |
| |
| sendGetHttpCacheInfo: function() { |
| this.send('getHttpCacheInfo'); |
| }, |
| |
| sendGetSocketPoolInfo: function() { |
| this.send('getSocketPoolInfo'); |
| }, |
| |
| sendGetSessionNetworkStats: function() { |
| this.send('getSessionNetworkStats'); |
| }, |
| |
| sendGetHistoricNetworkStats: function() { |
| this.send('getHistoricNetworkStats'); |
| }, |
| |
| sendCloseIdleSockets: function() { |
| this.send('closeIdleSockets'); |
| }, |
| |
| sendFlushSocketPools: function() { |
| this.send('flushSocketPools'); |
| }, |
| |
| sendGetQuicInfo: function() { |
| this.send('getQuicInfo'); |
| }, |
| |
| sendGetSpdySessionInfo: function() { |
| this.send('getSpdySessionInfo'); |
| }, |
| |
| sendGetSpdyStatus: function() { |
| this.send('getSpdyStatus'); |
| }, |
| |
| sendGetSpdyAlternateProtocolMappings: function() { |
| this.send('getSpdyAlternateProtocolMappings'); |
| }, |
| |
| sendGetServiceProviders: function() { |
| this.send('getServiceProviders'); |
| }, |
| |
| sendGetPrerenderInfo: function() { |
| this.send('getPrerenderInfo'); |
| }, |
| |
| sendGetHttpPipeliningStatus: function() { |
| this.send('getHttpPipeliningStatus'); |
| }, |
| |
| sendGetExtensionInfo: function() { |
| this.send('getExtensionInfo'); |
| }, |
| |
| enableIPv6: function() { |
| this.send('enableIPv6'); |
| }, |
| |
| setLogLevel: function(logLevel) { |
| this.send('setLogLevel', ['' + logLevel]); |
| }, |
| |
| refreshSystemLogs: function() { |
| this.send('refreshSystemLogs'); |
| }, |
| |
| getSystemLog: function(log_key, cellId) { |
| this.send('getSystemLog', [log_key, cellId]); |
| }, |
| |
| importONCFile: function(fileContent, passcode) { |
| this.send('importONCFile', [fileContent, passcode]); |
| }, |
| |
| storeDebugLogs: function() { |
| this.send('storeDebugLogs'); |
| }, |
| |
| setNetworkDebugMode: function(subsystem) { |
| this.send('setNetworkDebugMode', [subsystem]); |
| }, |
| |
| //-------------------------------------------------------------------------- |
| // Messages received from the browser. |
| //-------------------------------------------------------------------------- |
| |
| receive: function(command, params) { |
| // Does nothing if disabled. |
| if (this.disabled_) |
| return; |
| |
| // If no constants have been received, and params does not contain the |
| // constants, delay handling the data. |
| if (Constants == null && command != 'receivedConstants') { |
| this.earlyReceivedData_.push({ command: command, params: params }); |
| return; |
| } |
| |
| this[command](params); |
| |
| // Handle any data that was received early in the order it was received, |
| // once the constants have been processed. |
| if (this.earlyReceivedData_ != null) { |
| for (var i = 0; i < this.earlyReceivedData_.length; i++) { |
| var command = this.earlyReceivedData_[i]; |
| this[command.command](command.params); |
| } |
| this.earlyReceivedData_ = null; |
| } |
| }, |
| |
| receivedConstants: function(constants) { |
| for (var i = 0; i < this.constantsObservers_.length; i++) |
| this.constantsObservers_[i].onReceivedConstants(constants); |
| }, |
| |
| receivedLogEntries: function(logEntries) { |
| EventsTracker.getInstance().addLogEntries(logEntries); |
| }, |
| |
| receivedProxySettings: function(proxySettings) { |
| this.pollableDataHelpers_.proxySettings.update(proxySettings); |
| }, |
| |
| receivedBadProxies: function(badProxies) { |
| this.pollableDataHelpers_.badProxies.update(badProxies); |
| }, |
| |
| receivedHostResolverInfo: function(hostResolverInfo) { |
| this.pollableDataHelpers_.hostResolverInfo.update(hostResolverInfo); |
| }, |
| |
| receivedSocketPoolInfo: function(socketPoolInfo) { |
| this.pollableDataHelpers_.socketPoolInfo.update(socketPoolInfo); |
| }, |
| |
| receivedSessionNetworkStats: function(sessionNetworkStats) { |
| this.pollableDataHelpers_.sessionNetworkStats.update(sessionNetworkStats); |
| }, |
| |
| receivedHistoricNetworkStats: function(historicNetworkStats) { |
| this.pollableDataHelpers_.historicNetworkStats.update( |
| historicNetworkStats); |
| }, |
| |
| receivedQuicInfo: function(quicInfo) { |
| this.pollableDataHelpers_.quicInfo.update(quicInfo); |
| }, |
| |
| receivedSpdySessionInfo: function(spdySessionInfo) { |
| this.pollableDataHelpers_.spdySessionInfo.update(spdySessionInfo); |
| }, |
| |
| receivedSpdyStatus: function(spdyStatus) { |
| this.pollableDataHelpers_.spdyStatus.update(spdyStatus); |
| }, |
| |
| receivedSpdyAlternateProtocolMappings: |
| function(spdyAlternateProtocolMappings) { |
| this.pollableDataHelpers_.spdyAlternateProtocolMappings.update( |
| spdyAlternateProtocolMappings); |
| }, |
| |
| receivedServiceProviders: function(serviceProviders) { |
| this.pollableDataHelpers_.serviceProviders.update(serviceProviders); |
| }, |
| |
| receivedStartConnectionTestSuite: function() { |
| for (var i = 0; i < this.connectionTestsObservers_.length; i++) |
| this.connectionTestsObservers_[i].onStartedConnectionTestSuite(); |
| }, |
| |
| receivedStartConnectionTestExperiment: function(experiment) { |
| for (var i = 0; i < this.connectionTestsObservers_.length; i++) { |
| this.connectionTestsObservers_[i].onStartedConnectionTestExperiment( |
| experiment); |
| } |
| }, |
| |
| receivedCompletedConnectionTestExperiment: function(info) { |
| for (var i = 0; i < this.connectionTestsObservers_.length; i++) { |
| this.connectionTestsObservers_[i].onCompletedConnectionTestExperiment( |
| info.experiment, info.result); |
| } |
| }, |
| |
| receivedCompletedConnectionTestSuite: function() { |
| for (var i = 0; i < this.connectionTestsObservers_.length; i++) |
| this.connectionTestsObservers_[i].onCompletedConnectionTestSuite(); |
| }, |
| |
| receivedHSTSResult: function(info) { |
| for (var i = 0; i < this.hstsObservers_.length; i++) |
| this.hstsObservers_[i].onHSTSQueryResult(info); |
| }, |
| |
| receivedONCFileParse: function(error) { |
| for (var i = 0; i < this.crosONCFileParseObservers_.length; i++) |
| this.crosONCFileParseObservers_[i].onONCFileParse(error); |
| }, |
| |
| receivedStoreDebugLogs: function(status) { |
| for (var i = 0; i < this.storeDebugLogsObservers_.length; i++) |
| this.storeDebugLogsObservers_[i].onStoreDebugLogs(status); |
| }, |
| |
| receivedSetNetworkDebugMode: function(status) { |
| for (var i = 0; i < this.setNetworkDebugModeObservers_.length; i++) |
| this.setNetworkDebugModeObservers_[i].onSetNetworkDebugMode(status); |
| }, |
| |
| receivedHttpCacheInfo: function(info) { |
| this.pollableDataHelpers_.httpCacheInfo.update(info); |
| }, |
| |
| receivedPrerenderInfo: function(prerenderInfo) { |
| this.pollableDataHelpers_.prerenderInfo.update(prerenderInfo); |
| }, |
| |
| receivedHttpPipeliningStatus: function(httpPipeliningStatus) { |
| this.pollableDataHelpers_.httpPipeliningStatus.update( |
| httpPipeliningStatus); |
| }, |
| |
| receivedExtensionInfo: function(extensionInfo) { |
| this.pollableDataHelpers_.extensionInfo.update(extensionInfo); |
| }, |
| |
| getSystemLogCallback: function(systemLog) { |
| this.pollableDataHelpers_.systemLog.update(systemLog); |
| }, |
| |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Prevents receiving/sending events to/from the browser. |
| */ |
| disable: function() { |
| this.disabled_ = true; |
| this.setPollInterval(0); |
| }, |
| |
| /** |
| * Returns true if the BrowserBridge has been disabled. |
| */ |
| isDisabled: function() { |
| return this.disabled_; |
| }, |
| |
| /** |
| * 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: function(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: function(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: function(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: function(observer, ignoreWhenUnchanged) { |
| this.pollableDataHelpers_.socketPoolInfo.addObserver(observer, |
| ignoreWhenUnchanged); |
| }, |
| |
| /** |
| * Adds a listener of the network session. |observer| will be called back |
| * when data is received, through: |
| * |
| * observer.onSessionNetworkStatsChanged(sessionNetworkStats) |
| */ |
| addSessionNetworkStatsObserver: function(observer, ignoreWhenUnchanged) { |
| this.pollableDataHelpers_.sessionNetworkStats.addObserver( |
| observer, ignoreWhenUnchanged); |
| }, |
| |
| /** |
| * Adds a listener of persistent network session data. |observer| will be |
| * called back when data is received, through: |
| * |
| * observer.onHistoricNetworkStatsChanged(historicNetworkStats) |
| */ |
| addHistoricNetworkStatsObserver: function(observer, ignoreWhenUnchanged) { |
| this.pollableDataHelpers_.historicNetworkStats.addObserver( |
| observer, ignoreWhenUnchanged); |
| }, |
| |
| /** |
| * Adds a listener of the QUIC info. |observer| will be called back |
| * when data is received, through: |
| * |
| * observer.onQuicInfoChanged(quicInfo) |
| */ |
| addQuicInfoObserver: function(observer, ignoreWhenUnchanged) { |
| this.pollableDataHelpers_.quicInfo.addObserver( |
| observer, ignoreWhenUnchanged); |
| }, |
| |
| /** |
| * Adds a listener of the SPDY info. |observer| will be called back |
| * when data is received, through: |
| * |
| * observer.onSpdySessionInfoChanged(spdySessionInfo) |
| */ |
| addSpdySessionInfoObserver: function(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: function(observer, ignoreWhenUnchanged) { |
| this.pollableDataHelpers_.spdyStatus.addObserver(observer, |
| ignoreWhenUnchanged); |
| }, |
| |
| /** |
| * Adds a listener of the AlternateProtocolMappings. |observer| will be |
| * called back when data is received, through: |
| * |
| * observer.onSpdyAlternateProtocolMappingsChanged( |
| * spdyAlternateProtocolMappings) |
| */ |
| addSpdyAlternateProtocolMappingsObserver: function(observer, |
| ignoreWhenUnchanged) { |
| this.pollableDataHelpers_.spdyAlternateProtocolMappings.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: function(observer, ignoreWhenUnchanged) { |
| if (this.pollableDataHelpers_.serviceProviders) { |
| this.pollableDataHelpers_.serviceProviders.addObserver( |
| observer, ignoreWhenUnchanged); |
| } |
| }, |
| |
| /** |
| * Adds a listener for the progress of the connection tests. |
| * The observer will be called back with: |
| * |
| * observer.onStartedConnectionTestSuite(); |
| * observer.onStartedConnectionTestExperiment(experiment); |
| * observer.onCompletedConnectionTestExperiment(experiment, result); |
| * observer.onCompletedConnectionTestSuite(); |
| */ |
| addConnectionTestsObserver: function(observer) { |
| this.connectionTestsObservers_.push(observer); |
| }, |
| |
| /** |
| * Adds a listener for the http cache info results. |
| * The observer will be called back with: |
| * |
| * observer.onHttpCacheInfoChanged(info); |
| */ |
| addHttpCacheInfoObserver: function(observer, ignoreWhenUnchanged) { |
| this.pollableDataHelpers_.httpCacheInfo.addObserver( |
| observer, ignoreWhenUnchanged); |
| }, |
| |
| /** |
| * Adds a listener for the results of HSTS (HTTPS Strict Transport Security) |
| * queries. The observer will be called back with: |
| * |
| * observer.onHSTSQueryResult(result); |
| */ |
| addHSTSObserver: function(observer) { |
| this.hstsObservers_.push(observer); |
| }, |
| |
| /** |
| * Adds a listener for ONC file parse status. The observer will be called |
| * back with: |
| * |
| * observer.onONCFileParse(error); |
| */ |
| addCrosONCFileParseObserver: function(observer) { |
| this.crosONCFileParseObservers_.push(observer); |
| }, |
| |
| /** |
| * Adds a listener for storing log file status. The observer will be called |
| * back with: |
| * |
| * observer.onStoreDebugLogs(status); |
| */ |
| addStoreDebugLogsObserver: function(observer) { |
| this.storeDebugLogsObservers_.push(observer); |
| }, |
| |
| /** |
| * Adds a listener for network debugging mode status. The observer |
| * will be called back with: |
| * |
| * observer.onSetNetworkDebugMode(status); |
| */ |
| addSetNetworkDebugModeObserver: function(observer) { |
| this.setNetworkDebugModeObservers_.push(observer); |
| }, |
| |
| /** |
| * Adds a listener for the received constants event. |observer| will be |
| * called back when the constants are received, through: |
| * |
| * observer.onReceivedConstants(constants); |
| */ |
| addConstantsObserver: function(observer) { |
| this.constantsObservers_.push(observer); |
| }, |
| |
| /** |
| * Adds a listener for updated prerender info events |
| * |observer| will be called back with: |
| * |
| * observer.onPrerenderInfoChanged(prerenderInfo); |
| */ |
| addPrerenderInfoObserver: function(observer, ignoreWhenUnchanged) { |
| this.pollableDataHelpers_.prerenderInfo.addObserver( |
| observer, ignoreWhenUnchanged); |
| }, |
| |
| /** |
| * Adds a listener of HTTP pipelining status. |observer| will be called |
| * back when data is received, through: |
| * |
| * observer.onHttpPipelineStatusChanged(httpPipeliningStatus) |
| */ |
| addHttpPipeliningStatusObserver: function(observer, ignoreWhenUnchanged) { |
| this.pollableDataHelpers_.httpPipeliningStatus.addObserver( |
| observer, ignoreWhenUnchanged); |
| }, |
| |
| /** |
| * Adds a listener of extension information. |observer| will be called |
| * back when data is received, through: |
| * |
| * observer.onExtensionInfoChanged(extensionInfo) |
| */ |
| addExtensionInfoObserver: function(observer, ignoreWhenUnchanged) { |
| this.pollableDataHelpers_.extensionInfo.addObserver( |
| observer, ignoreWhenUnchanged); |
| }, |
| |
| /** |
| * Adds a listener of system log information. |observer| will be called |
| * back when data is received, through: |
| * |
| * observer.onSystemLogChanged(systemLogInfo) |
| */ |
| addSystemLogObserver: function(observer, ignoreWhenUnchanged) { |
| if (this.pollableDataHelpers_.systemLog) { |
| this.pollableDataHelpers_.systemLog.addObserver( |
| observer, ignoreWhenUnchanged); |
| } |
| }, |
| |
| /** |
| * If |force| is true, calls all startUpdate functions. Otherwise, just |
| * runs updates with active observers. |
| */ |
| checkForUpdatedInfo: function(force) { |
| for (var name in this.pollableDataHelpers_) { |
| var helper = this.pollableDataHelpers_[name]; |
| if (force || helper.hasActiveObserver()) |
| helper.startUpdate(); |
| } |
| }, |
| |
| /** |
| * Calls all startUpdate functions and, if |callback| is non-null, |
| * calls it with the results of all updates. |
| */ |
| updateAllInfo: function(callback) { |
| if (callback) |
| new UpdateAllObserver(callback, this.pollableDataHelpers_); |
| this.checkForUpdatedInfo(true); |
| } |
| }; |
| |
| /** |
| * 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, startUpdateFunction) { |
| this.observerMethodName_ = observerMethodName; |
| this.startUpdate = startUpdateFunction; |
| this.observerInfos_ = []; |
| } |
| |
| PollableDataHelper.prototype = { |
| getObserverMethodName: function() { |
| return this.observerMethodName_; |
| }, |
| |
| isObserver: function(object) { |
| for (var 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: function(observer, ignoreWhenUnchanged) { |
| this.observerInfos_.push(new ObserverInfo(observer, ignoreWhenUnchanged)); |
| }, |
| |
| removeObserver: function(observer) { |
| for (var 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: function(data) { |
| var prevData = this.currentData_; |
| var 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 (var i = 0; i < this.observerInfos_.length; i++) { |
| var 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: function() { |
| for (var 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; |
| } |
| |
| /** |
| * This is a helper class used by BrowserBridge to send data to |
| * a callback once data from all polls has been received. |
| * |
| * It works by keeping track of how many polling functions have |
| * yet to receive data, and recording the data as it it received. |
| * |
| * @constructor |
| */ |
| function UpdateAllObserver(callback, pollableDataHelpers) { |
| this.callback_ = callback; |
| this.observingCount_ = 0; |
| this.updatedData_ = {}; |
| |
| for (var name in pollableDataHelpers) { |
| ++this.observingCount_; |
| var helper = pollableDataHelpers[name]; |
| helper.addObserver(this); |
| this[helper.getObserverMethodName()] = |
| this.onDataReceived_.bind(this, helper, name); |
| } |
| } |
| |
| UpdateAllObserver.prototype = { |
| isActive: function() { |
| return true; |
| }, |
| |
| onDataReceived_: function(helper, name, data) { |
| helper.removeObserver(this); |
| --this.observingCount_; |
| this.updatedData_[name] = data; |
| if (this.observingCount_ == 0) |
| this.callback_(this.updatedData_); |
| } |
| }; |
| |
| return BrowserBridge; |
| })(); |