PageRenderTime 79ms CodeModel.GetById 4ms RepoModel.GetById 0ms app.codeStats 0ms

/resources/net_internals/browser_bridge.js

https://github.com/MichalBu/chromium-src-chrome-browser
JavaScript | 877 lines | 499 code | 120 blank | 258 comment | 49 complexity | b361534f64c12bf41f9e20838344973b MD5 | raw file
  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.
  4. /**
  5. * This class provides a "bridge" for communicating between the javascript and
  6. * the browser.
  7. */
  8. var BrowserBridge = (function() {
  9. 'use strict';
  10. /**
  11. * Delay in milliseconds between updates of certain browser information.
  12. */
  13. var POLL_INTERVAL_MS = 5000;
  14. /**
  15. * @constructor
  16. */
  17. function BrowserBridge() {
  18. assertFirstConstructorCall(BrowserBridge);
  19. // List of observers for various bits of browser state.
  20. this.connectionTestsObservers_ = [];
  21. this.hstsObservers_ = [];
  22. this.constantsObservers_ = [];
  23. this.crosONCFileParseObservers_ = [];
  24. this.storeDebugLogsObservers_ = [];
  25. this.setNetworkDebugModeObservers_ = [];
  26. // Unprocessed data received before the constants. This serves to protect
  27. // against passing along data before having information on how to interpret
  28. // it.
  29. this.earlyReceivedData_ = [];
  30. this.pollableDataHelpers_ = {};
  31. this.pollableDataHelpers_.proxySettings =
  32. new PollableDataHelper('onProxySettingsChanged',
  33. this.sendGetProxySettings.bind(this));
  34. this.pollableDataHelpers_.badProxies =
  35. new PollableDataHelper('onBadProxiesChanged',
  36. this.sendGetBadProxies.bind(this));
  37. this.pollableDataHelpers_.httpCacheInfo =
  38. new PollableDataHelper('onHttpCacheInfoChanged',
  39. this.sendGetHttpCacheInfo.bind(this));
  40. this.pollableDataHelpers_.hostResolverInfo =
  41. new PollableDataHelper('onHostResolverInfoChanged',
  42. this.sendGetHostResolverInfo.bind(this));
  43. this.pollableDataHelpers_.socketPoolInfo =
  44. new PollableDataHelper('onSocketPoolInfoChanged',
  45. this.sendGetSocketPoolInfo.bind(this));
  46. this.pollableDataHelpers_.sessionNetworkStats =
  47. new PollableDataHelper('onSessionNetworkStatsChanged',
  48. this.sendGetSessionNetworkStats.bind(this));
  49. this.pollableDataHelpers_.historicNetworkStats =
  50. new PollableDataHelper('onHistoricNetworkStatsChanged',
  51. this.sendGetHistoricNetworkStats.bind(this));
  52. this.pollableDataHelpers_.quicInfo =
  53. new PollableDataHelper('onQuicInfoChanged',
  54. this.sendGetQuicInfo.bind(this));
  55. this.pollableDataHelpers_.spdySessionInfo =
  56. new PollableDataHelper('onSpdySessionInfoChanged',
  57. this.sendGetSpdySessionInfo.bind(this));
  58. this.pollableDataHelpers_.spdyStatus =
  59. new PollableDataHelper('onSpdyStatusChanged',
  60. this.sendGetSpdyStatus.bind(this));
  61. this.pollableDataHelpers_.spdyAlternateProtocolMappings =
  62. new PollableDataHelper('onSpdyAlternateProtocolMappingsChanged',
  63. this.sendGetSpdyAlternateProtocolMappings.bind(
  64. this));
  65. if (cr.isWindows) {
  66. this.pollableDataHelpers_.serviceProviders =
  67. new PollableDataHelper('onServiceProvidersChanged',
  68. this.sendGetServiceProviders.bind(this));
  69. }
  70. this.pollableDataHelpers_.prerenderInfo =
  71. new PollableDataHelper('onPrerenderInfoChanged',
  72. this.sendGetPrerenderInfo.bind(this));
  73. this.pollableDataHelpers_.httpPipeliningStatus =
  74. new PollableDataHelper('onHttpPipeliningStatusChanged',
  75. this.sendGetHttpPipeliningStatus.bind(this));
  76. this.pollableDataHelpers_.extensionInfo =
  77. new PollableDataHelper('onExtensionInfoChanged',
  78. this.sendGetExtensionInfo.bind(this));
  79. if (cr.isChromeOS) {
  80. this.pollableDataHelpers_.systemLog =
  81. new PollableDataHelper('onSystemLogChanged',
  82. this.getSystemLog.bind(this, 'syslog'));
  83. }
  84. // Setting this to true will cause messages from the browser to be ignored,
  85. // and no messages will be sent to the browser, either. Intended for use
  86. // when viewing log files.
  87. this.disabled_ = false;
  88. // Interval id returned by window.setInterval for polling timer.
  89. this.pollIntervalId_ = null;
  90. }
  91. cr.addSingletonGetter(BrowserBridge);
  92. BrowserBridge.prototype = {
  93. //--------------------------------------------------------------------------
  94. // Messages sent to the browser
  95. //--------------------------------------------------------------------------
  96. /**
  97. * Wraps |chrome.send|. Doesn't send anything when disabled.
  98. */
  99. send: function(value1, value2) {
  100. if (!this.disabled_) {
  101. if (arguments.length == 1) {
  102. chrome.send(value1);
  103. } else if (arguments.length == 2) {
  104. chrome.send(value1, value2);
  105. } else {
  106. throw 'Unsupported number of arguments.';
  107. }
  108. }
  109. },
  110. sendReady: function() {
  111. this.send('notifyReady');
  112. this.setPollInterval(POLL_INTERVAL_MS);
  113. },
  114. /**
  115. * Some of the data we are interested is not currently exposed as a
  116. * stream. This starts polling those with active observers (visible
  117. * views) every |intervalMs|. Subsequent calls override previous calls
  118. * to this function. If |intervalMs| is 0, stops polling.
  119. */
  120. setPollInterval: function(intervalMs) {
  121. if (this.pollIntervalId_ !== null) {
  122. window.clearInterval(this.pollIntervalId_);
  123. this.pollIntervalId_ = null;
  124. }
  125. if (intervalMs > 0) {
  126. this.pollIntervalId_ =
  127. window.setInterval(this.checkForUpdatedInfo.bind(this, false),
  128. intervalMs);
  129. }
  130. },
  131. sendGetProxySettings: function() {
  132. // The browser will call receivedProxySettings on completion.
  133. this.send('getProxySettings');
  134. },
  135. sendReloadProxySettings: function() {
  136. this.send('reloadProxySettings');
  137. },
  138. sendGetBadProxies: function() {
  139. // The browser will call receivedBadProxies on completion.
  140. this.send('getBadProxies');
  141. },
  142. sendGetHostResolverInfo: function() {
  143. // The browser will call receivedHostResolverInfo on completion.
  144. this.send('getHostResolverInfo');
  145. },
  146. sendClearBadProxies: function() {
  147. this.send('clearBadProxies');
  148. },
  149. sendClearHostResolverCache: function() {
  150. this.send('clearHostResolverCache');
  151. },
  152. sendClearBrowserCache: function() {
  153. this.send('clearBrowserCache');
  154. },
  155. sendClearAllCache: function() {
  156. this.sendClearHostResolverCache();
  157. this.sendClearBrowserCache();
  158. },
  159. sendStartConnectionTests: function(url) {
  160. this.send('startConnectionTests', [url]);
  161. },
  162. sendHSTSQuery: function(domain) {
  163. this.send('hstsQuery', [domain]);
  164. },
  165. sendHSTSAdd: function(domain, sts_include_subdomains,
  166. pkp_include_subdomains, pins) {
  167. this.send('hstsAdd', [domain, sts_include_subdomains,
  168. pkp_include_subdomains, pins]);
  169. },
  170. sendHSTSDelete: function(domain) {
  171. this.send('hstsDelete', [domain]);
  172. },
  173. sendGetHttpCacheInfo: function() {
  174. this.send('getHttpCacheInfo');
  175. },
  176. sendGetSocketPoolInfo: function() {
  177. this.send('getSocketPoolInfo');
  178. },
  179. sendGetSessionNetworkStats: function() {
  180. this.send('getSessionNetworkStats');
  181. },
  182. sendGetHistoricNetworkStats: function() {
  183. this.send('getHistoricNetworkStats');
  184. },
  185. sendCloseIdleSockets: function() {
  186. this.send('closeIdleSockets');
  187. },
  188. sendFlushSocketPools: function() {
  189. this.send('flushSocketPools');
  190. },
  191. sendGetQuicInfo: function() {
  192. this.send('getQuicInfo');
  193. },
  194. sendGetSpdySessionInfo: function() {
  195. this.send('getSpdySessionInfo');
  196. },
  197. sendGetSpdyStatus: function() {
  198. this.send('getSpdyStatus');
  199. },
  200. sendGetSpdyAlternateProtocolMappings: function() {
  201. this.send('getSpdyAlternateProtocolMappings');
  202. },
  203. sendGetServiceProviders: function() {
  204. this.send('getServiceProviders');
  205. },
  206. sendGetPrerenderInfo: function() {
  207. this.send('getPrerenderInfo');
  208. },
  209. sendGetHttpPipeliningStatus: function() {
  210. this.send('getHttpPipeliningStatus');
  211. },
  212. sendGetExtensionInfo: function() {
  213. this.send('getExtensionInfo');
  214. },
  215. enableIPv6: function() {
  216. this.send('enableIPv6');
  217. },
  218. setLogLevel: function(logLevel) {
  219. this.send('setLogLevel', ['' + logLevel]);
  220. },
  221. refreshSystemLogs: function() {
  222. this.send('refreshSystemLogs');
  223. },
  224. getSystemLog: function(log_key, cellId) {
  225. this.send('getSystemLog', [log_key, cellId]);
  226. },
  227. importONCFile: function(fileContent, passcode) {
  228. this.send('importONCFile', [fileContent, passcode]);
  229. },
  230. storeDebugLogs: function() {
  231. this.send('storeDebugLogs');
  232. },
  233. setNetworkDebugMode: function(subsystem) {
  234. this.send('setNetworkDebugMode', [subsystem]);
  235. },
  236. //--------------------------------------------------------------------------
  237. // Messages received from the browser.
  238. //--------------------------------------------------------------------------
  239. receive: function(command, params) {
  240. // Does nothing if disabled.
  241. if (this.disabled_)
  242. return;
  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 });
  247. return;
  248. }
  249. this[command](params);
  250. // Handle any data that was received early in the order it was received,
  251. // once the constants have been processed.
  252. if (this.earlyReceivedData_ != null) {
  253. for (var i = 0; i < this.earlyReceivedData_.length; i++) {
  254. var command = this.earlyReceivedData_[i];
  255. this[command.command](command.params);
  256. }
  257. this.earlyReceivedData_ = null;
  258. }
  259. },
  260. receivedConstants: function(constants) {
  261. for (var i = 0; i < this.constantsObservers_.length; i++)
  262. this.constantsObservers_[i].onReceivedConstants(constants);
  263. },
  264. receivedLogEntries: function(logEntries) {
  265. EventsTracker.getInstance().addLogEntries(logEntries);
  266. },
  267. receivedProxySettings: function(proxySettings) {
  268. this.pollableDataHelpers_.proxySettings.update(proxySettings);
  269. },
  270. receivedBadProxies: function(badProxies) {
  271. this.pollableDataHelpers_.badProxies.update(badProxies);
  272. },
  273. receivedHostResolverInfo: function(hostResolverInfo) {
  274. this.pollableDataHelpers_.hostResolverInfo.update(hostResolverInfo);
  275. },
  276. receivedSocketPoolInfo: function(socketPoolInfo) {
  277. this.pollableDataHelpers_.socketPoolInfo.update(socketPoolInfo);
  278. },
  279. receivedSessionNetworkStats: function(sessionNetworkStats) {
  280. this.pollableDataHelpers_.sessionNetworkStats.update(sessionNetworkStats);
  281. },
  282. receivedHistoricNetworkStats: function(historicNetworkStats) {
  283. this.pollableDataHelpers_.historicNetworkStats.update(
  284. historicNetworkStats);
  285. },
  286. receivedQuicInfo: function(quicInfo) {
  287. this.pollableDataHelpers_.quicInfo.update(quicInfo);
  288. },
  289. receivedSpdySessionInfo: function(spdySessionInfo) {
  290. this.pollableDataHelpers_.spdySessionInfo.update(spdySessionInfo);
  291. },
  292. receivedSpdyStatus: function(spdyStatus) {
  293. this.pollableDataHelpers_.spdyStatus.update(spdyStatus);
  294. },
  295. receivedSpdyAlternateProtocolMappings:
  296. function(spdyAlternateProtocolMappings) {
  297. this.pollableDataHelpers_.spdyAlternateProtocolMappings.update(
  298. spdyAlternateProtocolMappings);
  299. },
  300. receivedServiceProviders: function(serviceProviders) {
  301. this.pollableDataHelpers_.serviceProviders.update(serviceProviders);
  302. },
  303. receivedStartConnectionTestSuite: function() {
  304. for (var i = 0; i < this.connectionTestsObservers_.length; i++)
  305. this.connectionTestsObservers_[i].onStartedConnectionTestSuite();
  306. },
  307. receivedStartConnectionTestExperiment: function(experiment) {
  308. for (var i = 0; i < this.connectionTestsObservers_.length; i++) {
  309. this.connectionTestsObservers_[i].onStartedConnectionTestExperiment(
  310. experiment);
  311. }
  312. },
  313. receivedCompletedConnectionTestExperiment: function(info) {
  314. for (var i = 0; i < this.connectionTestsObservers_.length; i++) {
  315. this.connectionTestsObservers_[i].onCompletedConnectionTestExperiment(
  316. info.experiment, info.result);
  317. }
  318. },
  319. receivedCompletedConnectionTestSuite: function() {
  320. for (var i = 0; i < this.connectionTestsObservers_.length; i++)
  321. this.connectionTestsObservers_[i].onCompletedConnectionTestSuite();
  322. },
  323. receivedHSTSResult: function(info) {
  324. for (var i = 0; i < this.hstsObservers_.length; i++)
  325. this.hstsObservers_[i].onHSTSQueryResult(info);
  326. },
  327. receivedONCFileParse: function(error) {
  328. for (var i = 0; i < this.crosONCFileParseObservers_.length; i++)
  329. this.crosONCFileParseObservers_[i].onONCFileParse(error);
  330. },
  331. receivedStoreDebugLogs: function(status) {
  332. for (var i = 0; i < this.storeDebugLogsObservers_.length; i++)
  333. this.storeDebugLogsObservers_[i].onStoreDebugLogs(status);
  334. },
  335. receivedSetNetworkDebugMode: function(status) {
  336. for (var i = 0; i < this.setNetworkDebugModeObservers_.length; i++)
  337. this.setNetworkDebugModeObservers_[i].onSetNetworkDebugMode(status);
  338. },
  339. receivedHttpCacheInfo: function(info) {
  340. this.pollableDataHelpers_.httpCacheInfo.update(info);
  341. },
  342. receivedPrerenderInfo: function(prerenderInfo) {
  343. this.pollableDataHelpers_.prerenderInfo.update(prerenderInfo);
  344. },
  345. receivedHttpPipeliningStatus: function(httpPipeliningStatus) {
  346. this.pollableDataHelpers_.httpPipeliningStatus.update(
  347. httpPipeliningStatus);
  348. },
  349. receivedExtensionInfo: function(extensionInfo) {
  350. this.pollableDataHelpers_.extensionInfo.update(extensionInfo);
  351. },
  352. getSystemLogCallback: function(systemLog) {
  353. this.pollableDataHelpers_.systemLog.update(systemLog);
  354. },
  355. //--------------------------------------------------------------------------
  356. /**
  357. * Prevents receiving/sending events to/from the browser.
  358. */
  359. disable: function() {
  360. this.disabled_ = true;
  361. this.setPollInterval(0);
  362. },
  363. /**
  364. * Returns true if the BrowserBridge has been disabled.
  365. */
  366. isDisabled: function() {
  367. return this.disabled_;
  368. },
  369. /**
  370. * Adds a listener of the proxy settings. |observer| will be called back
  371. * when data is received, through:
  372. *
  373. * observer.onProxySettingsChanged(proxySettings)
  374. *
  375. * |proxySettings| is a dictionary with (up to) two properties:
  376. *
  377. * "original" -- The settings that chrome was configured to use
  378. * (i.e. system settings.)
  379. * "effective" -- The "effective" proxy settings that chrome is using.
  380. * (decides between the manual/automatic modes of the
  381. * fetched settings).
  382. *
  383. * Each of these two configurations is formatted as a string, and may be
  384. * omitted if not yet initialized.
  385. *
  386. * If |ignoreWhenUnchanged| is true, data is only sent when it changes.
  387. * If it's false, data is sent whenever it's received from the browser.
  388. */
  389. addProxySettingsObserver: function(observer, ignoreWhenUnchanged) {
  390. this.pollableDataHelpers_.proxySettings.addObserver(observer,
  391. ignoreWhenUnchanged);
  392. },
  393. /**
  394. * Adds a listener of the proxy settings. |observer| will be called back
  395. * when data is received, through:
  396. *
  397. * observer.onBadProxiesChanged(badProxies)
  398. *
  399. * |badProxies| is an array, where each entry has the property:
  400. * badProxies[i].proxy_uri: String identify the proxy.
  401. * badProxies[i].bad_until: The time when the proxy stops being considered
  402. * bad. Note the time is in time ticks.
  403. */
  404. addBadProxiesObserver: function(observer, ignoreWhenUnchanged) {
  405. this.pollableDataHelpers_.badProxies.addObserver(observer,
  406. ignoreWhenUnchanged);
  407. },
  408. /**
  409. * Adds a listener of the host resolver info. |observer| will be called back
  410. * when data is received, through:
  411. *
  412. * observer.onHostResolverInfoChanged(hostResolverInfo)
  413. */
  414. addHostResolverInfoObserver: function(observer, ignoreWhenUnchanged) {
  415. this.pollableDataHelpers_.hostResolverInfo.addObserver(
  416. observer, ignoreWhenUnchanged);
  417. },
  418. /**
  419. * Adds a listener of the socket pool. |observer| will be called back
  420. * when data is received, through:
  421. *
  422. * observer.onSocketPoolInfoChanged(socketPoolInfo)
  423. */
  424. addSocketPoolInfoObserver: function(observer, ignoreWhenUnchanged) {
  425. this.pollableDataHelpers_.socketPoolInfo.addObserver(observer,
  426. ignoreWhenUnchanged);
  427. },
  428. /**
  429. * Adds a listener of the network session. |observer| will be called back
  430. * when data is received, through:
  431. *
  432. * observer.onSessionNetworkStatsChanged(sessionNetworkStats)
  433. */
  434. addSessionNetworkStatsObserver: function(observer, ignoreWhenUnchanged) {
  435. this.pollableDataHelpers_.sessionNetworkStats.addObserver(
  436. observer, ignoreWhenUnchanged);
  437. },
  438. /**
  439. * Adds a listener of persistent network session data. |observer| will be
  440. * called back when data is received, through:
  441. *
  442. * observer.onHistoricNetworkStatsChanged(historicNetworkStats)
  443. */
  444. addHistoricNetworkStatsObserver: function(observer, ignoreWhenUnchanged) {
  445. this.pollableDataHelpers_.historicNetworkStats.addObserver(
  446. observer, ignoreWhenUnchanged);
  447. },
  448. /**
  449. * Adds a listener of the QUIC info. |observer| will be called back
  450. * when data is received, through:
  451. *
  452. * observer.onQuicInfoChanged(quicInfo)
  453. */
  454. addQuicInfoObserver: function(observer, ignoreWhenUnchanged) {
  455. this.pollableDataHelpers_.quicInfo.addObserver(
  456. observer, ignoreWhenUnchanged);
  457. },
  458. /**
  459. * Adds a listener of the SPDY info. |observer| will be called back
  460. * when data is received, through:
  461. *
  462. * observer.onSpdySessionInfoChanged(spdySessionInfo)
  463. */
  464. addSpdySessionInfoObserver: function(observer, ignoreWhenUnchanged) {
  465. this.pollableDataHelpers_.spdySessionInfo.addObserver(
  466. observer, ignoreWhenUnchanged);
  467. },
  468. /**
  469. * Adds a listener of the SPDY status. |observer| will be called back
  470. * when data is received, through:
  471. *
  472. * observer.onSpdyStatusChanged(spdyStatus)
  473. */
  474. addSpdyStatusObserver: function(observer, ignoreWhenUnchanged) {
  475. this.pollableDataHelpers_.spdyStatus.addObserver(observer,
  476. ignoreWhenUnchanged);
  477. },
  478. /**
  479. * Adds a listener of the AlternateProtocolMappings. |observer| will be
  480. * called back when data is received, through:
  481. *
  482. * observer.onSpdyAlternateProtocolMappingsChanged(
  483. * spdyAlternateProtocolMappings)
  484. */
  485. addSpdyAlternateProtocolMappingsObserver: function(observer,
  486. ignoreWhenUnchanged) {
  487. this.pollableDataHelpers_.spdyAlternateProtocolMappings.addObserver(
  488. observer, ignoreWhenUnchanged);
  489. },
  490. /**
  491. * Adds a listener of the service providers info. |observer| will be called
  492. * back when data is received, through:
  493. *
  494. * observer.onServiceProvidersChanged(serviceProviders)
  495. *
  496. * Will do nothing if on a platform other than Windows, as service providers
  497. * are only present on Windows.
  498. */
  499. addServiceProvidersObserver: function(observer, ignoreWhenUnchanged) {
  500. if (this.pollableDataHelpers_.serviceProviders) {
  501. this.pollableDataHelpers_.serviceProviders.addObserver(
  502. observer, ignoreWhenUnchanged);
  503. }
  504. },
  505. /**
  506. * Adds a listener for the progress of the connection tests.
  507. * The observer will be called back with:
  508. *
  509. * observer.onStartedConnectionTestSuite();
  510. * observer.onStartedConnectionTestExperiment(experiment);
  511. * observer.onCompletedConnectionTestExperiment(experiment, result);
  512. * observer.onCompletedConnectionTestSuite();
  513. */
  514. addConnectionTestsObserver: function(observer) {
  515. this.connectionTestsObservers_.push(observer);
  516. },
  517. /**
  518. * Adds a listener for the http cache info results.
  519. * The observer will be called back with:
  520. *
  521. * observer.onHttpCacheInfoChanged(info);
  522. */
  523. addHttpCacheInfoObserver: function(observer, ignoreWhenUnchanged) {
  524. this.pollableDataHelpers_.httpCacheInfo.addObserver(
  525. observer, ignoreWhenUnchanged);
  526. },
  527. /**
  528. * Adds a listener for the results of HSTS (HTTPS Strict Transport Security)
  529. * queries. The observer will be called back with:
  530. *
  531. * observer.onHSTSQueryResult(result);
  532. */
  533. addHSTSObserver: function(observer) {
  534. this.hstsObservers_.push(observer);
  535. },
  536. /**
  537. * Adds a listener for ONC file parse status. The observer will be called
  538. * back with:
  539. *
  540. * observer.onONCFileParse(error);
  541. */
  542. addCrosONCFileParseObserver: function(observer) {
  543. this.crosONCFileParseObservers_.push(observer);
  544. },
  545. /**
  546. * Adds a listener for storing log file status. The observer will be called
  547. * back with:
  548. *
  549. * observer.onStoreDebugLogs(status);
  550. */
  551. addStoreDebugLogsObserver: function(observer) {
  552. this.storeDebugLogsObservers_.push(observer);
  553. },
  554. /**
  555. * Adds a listener for network debugging mode status. The observer
  556. * will be called back with:
  557. *
  558. * observer.onSetNetworkDebugMode(status);
  559. */
  560. addSetNetworkDebugModeObserver: function(observer) {
  561. this.setNetworkDebugModeObservers_.push(observer);
  562. },
  563. /**
  564. * Adds a listener for the received constants event. |observer| will be
  565. * called back when the constants are received, through:
  566. *
  567. * observer.onReceivedConstants(constants);
  568. */
  569. addConstantsObserver: function(observer) {
  570. this.constantsObservers_.push(observer);
  571. },
  572. /**
  573. * Adds a listener for updated prerender info events
  574. * |observer| will be called back with:
  575. *
  576. * observer.onPrerenderInfoChanged(prerenderInfo);
  577. */
  578. addPrerenderInfoObserver: function(observer, ignoreWhenUnchanged) {
  579. this.pollableDataHelpers_.prerenderInfo.addObserver(
  580. observer, ignoreWhenUnchanged);
  581. },
  582. /**
  583. * Adds a listener of HTTP pipelining status. |observer| will be called
  584. * back when data is received, through:
  585. *
  586. * observer.onHttpPipelineStatusChanged(httpPipeliningStatus)
  587. */
  588. addHttpPipeliningStatusObserver: function(observer, ignoreWhenUnchanged) {
  589. this.pollableDataHelpers_.httpPipeliningStatus.addObserver(
  590. observer, ignoreWhenUnchanged);
  591. },
  592. /**
  593. * Adds a listener of extension information. |observer| will be called
  594. * back when data is received, through:
  595. *
  596. * observer.onExtensionInfoChanged(extensionInfo)
  597. */
  598. addExtensionInfoObserver: function(observer, ignoreWhenUnchanged) {
  599. this.pollableDataHelpers_.extensionInfo.addObserver(
  600. observer, ignoreWhenUnchanged);
  601. },
  602. /**
  603. * Adds a listener of system log information. |observer| will be called
  604. * back when data is received, through:
  605. *
  606. * observer.onSystemLogChanged(systemLogInfo)
  607. */
  608. addSystemLogObserver: function(observer, ignoreWhenUnchanged) {
  609. if (this.pollableDataHelpers_.systemLog) {
  610. this.pollableDataHelpers_.systemLog.addObserver(
  611. observer, ignoreWhenUnchanged);
  612. }
  613. },
  614. /**
  615. * If |force| is true, calls all startUpdate functions. Otherwise, just
  616. * runs updates with active observers.
  617. */
  618. checkForUpdatedInfo: function(force) {
  619. for (var name in this.pollableDataHelpers_) {
  620. var helper = this.pollableDataHelpers_[name];
  621. if (force || helper.hasActiveObserver())
  622. helper.startUpdate();
  623. }
  624. },
  625. /**
  626. * Calls all startUpdate functions and, if |callback| is non-null,
  627. * calls it with the results of all updates.
  628. */
  629. updateAllInfo: function(callback) {
  630. if (callback)
  631. new UpdateAllObserver(callback, this.pollableDataHelpers_);
  632. this.checkForUpdatedInfo(true);
  633. }
  634. };
  635. /**
  636. * This is a helper class used by BrowserBridge, to keep track of:
  637. * - the list of observers interested in some piece of data.
  638. * - the last known value of that piece of data.
  639. * - the name of the callback method to invoke on observers.
  640. * - the update function.
  641. * @constructor
  642. */
  643. function PollableDataHelper(observerMethodName, startUpdateFunction) {
  644. this.observerMethodName_ = observerMethodName;
  645. this.startUpdate = startUpdateFunction;
  646. this.observerInfos_ = [];
  647. }
  648. PollableDataHelper.prototype = {
  649. getObserverMethodName: function() {
  650. return this.observerMethodName_;
  651. },
  652. isObserver: function(object) {
  653. for (var i = 0; i < this.observerInfos_.length; i++) {
  654. if (this.observerInfos_[i].observer === object)
  655. return true;
  656. }
  657. return false;
  658. },
  659. /**
  660. * If |ignoreWhenUnchanged| is true, we won't send data again until it
  661. * changes.
  662. */
  663. addObserver: function(observer, ignoreWhenUnchanged) {
  664. this.observerInfos_.push(new ObserverInfo(observer, ignoreWhenUnchanged));
  665. },
  666. removeObserver: function(observer) {
  667. for (var i = 0; i < this.observerInfos_.length; i++) {
  668. if (this.observerInfos_[i].observer === observer) {
  669. this.observerInfos_.splice(i, 1);
  670. return;
  671. }
  672. }
  673. },
  674. /**
  675. * Helper function to handle calling all the observers, but ONLY if the data
  676. * has actually changed since last time or the observer has yet to receive
  677. * any data. This is used for data we received from browser on an update
  678. * loop.
  679. */
  680. update: function(data) {
  681. var prevData = this.currentData_;
  682. var changed = false;
  683. // If the data hasn't changed since last time, will only need to notify
  684. // observers that have not yet received any data.
  685. if (!prevData || JSON.stringify(prevData) != JSON.stringify(data)) {
  686. changed = true;
  687. this.currentData_ = data;
  688. }
  689. // Notify the observers of the change, as needed.
  690. for (var i = 0; i < this.observerInfos_.length; i++) {
  691. var observerInfo = this.observerInfos_[i];
  692. if (changed || !observerInfo.hasReceivedData ||
  693. !observerInfo.ignoreWhenUnchanged) {
  694. observerInfo.observer[this.observerMethodName_](this.currentData_);
  695. observerInfo.hasReceivedData = true;
  696. }
  697. }
  698. },
  699. /**
  700. * Returns true if one of the observers actively wants the data
  701. * (i.e. is visible).
  702. */
  703. hasActiveObserver: function() {
  704. for (var i = 0; i < this.observerInfos_.length; i++) {
  705. if (this.observerInfos_[i].observer.isActive())
  706. return true;
  707. }
  708. return false;
  709. }
  710. };
  711. /**
  712. * This is a helper class used by PollableDataHelper, to keep track of
  713. * each observer and whether or not it has received any data. The
  714. * latter is used to make sure that new observers get sent data on the
  715. * update following their creation.
  716. * @constructor
  717. */
  718. function ObserverInfo(observer, ignoreWhenUnchanged) {
  719. this.observer = observer;
  720. this.hasReceivedData = false;
  721. this.ignoreWhenUnchanged = ignoreWhenUnchanged;
  722. }
  723. /**
  724. * This is a helper class used by BrowserBridge to send data to
  725. * a callback once data from all polls has been received.
  726. *
  727. * It works by keeping track of how many polling functions have
  728. * yet to receive data, and recording the data as it it received.
  729. *
  730. * @constructor
  731. */
  732. function UpdateAllObserver(callback, pollableDataHelpers) {
  733. this.callback_ = callback;
  734. this.observingCount_ = 0;
  735. this.updatedData_ = {};
  736. for (var name in pollableDataHelpers) {
  737. ++this.observingCount_;
  738. var helper = pollableDataHelpers[name];
  739. helper.addObserver(this);
  740. this[helper.getObserverMethodName()] =
  741. this.onDataReceived_.bind(this, helper, name);
  742. }
  743. }
  744. UpdateAllObserver.prototype = {
  745. isActive: function() {
  746. return true;
  747. },
  748. onDataReceived_: function(helper, name, data) {
  749. helper.removeObserver(this);
  750. --this.observingCount_;
  751. this.updatedData_[name] = data;
  752. if (this.observingCount_ == 0)
  753. this.callback_(this.updatedData_);
  754. }
  755. };
  756. return BrowserBridge;
  757. })();