You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

897 lines
24 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. /*******************************************************************************
  2. µMatrix - a Chromium browser extension to black/white list requests.
  3. Copyright (C) 2014 Raymond Hill
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see {http://www.gnu.org/licenses/}.
  14. Home: https://github.com/gorhill/uMatrix
  15. */
  16. /* global µMatrix, vAPI */
  17. /* jshint boss: true */
  18. /******************************************************************************/
  19. /******************************************************************************/
  20. // Default handler
  21. (function() {
  22. 'use strict';
  23. var µm = µMatrix;
  24. /******************************************************************************/
  25. // Default is for commonly used message.
  26. function onMessage(request, sender, callback) {
  27. // Async
  28. switch ( request.what ) {
  29. case 'getAssetContent':
  30. return µm.assets.getLocal(request.url, callback);
  31. default:
  32. break;
  33. }
  34. // Sync
  35. var response;
  36. switch ( request.what ) {
  37. case 'forceReloadTab':
  38. µm.forceReload(request.tabId);
  39. break;
  40. case 'getUserSettings':
  41. response = µm.userSettings;
  42. break;
  43. case 'gotoExtensionURL':
  44. µm.utils.gotoExtensionURL(request.url);
  45. break;
  46. case 'gotoURL':
  47. µm.utils.gotoURL(request);
  48. break;
  49. case 'reloadHostsFiles':
  50. µm.reloadHostsFiles(request.switches, request.update);
  51. break;
  52. case 'userSettings':
  53. response = µm.changeUserSettings(request.name, request.value);
  54. break;
  55. default:
  56. return vAPI.messaging.UNHANDLED;
  57. }
  58. callback(response);
  59. }
  60. /******************************************************************************/
  61. vAPI.messaging.setup(onMessage);
  62. /******************************************************************************/
  63. })();
  64. /******************************************************************************/
  65. /******************************************************************************/
  66. (function() {
  67. // popup.js
  68. var µm = µMatrix;
  69. /******************************************************************************/
  70. var smartReload = function(tabs) {
  71. var i = tabs.length;
  72. while ( i-- ) {
  73. µm.smartReloadTabs(µm.userSettings.smartAutoReload, tabs[i].id);
  74. }
  75. };
  76. /******************************************************************************/
  77. // Constructor is faster than object literal
  78. var RowSnapshot = function(srcHostname, desHostname, desDomain) {
  79. this.domain = desDomain;
  80. this.temporary = µm.tMatrix.evaluateRowZXY(srcHostname, desHostname);
  81. this.permanent = µm.pMatrix.evaluateRowZXY(srcHostname, desHostname);
  82. this.counts = RowSnapshot.counts.slice();
  83. this.totals = RowSnapshot.counts.slice();
  84. };
  85. RowSnapshot.counts = (function() {
  86. var i = Object.keys(µm.Matrix.getColumnHeaders()).length;
  87. var aa = new Array(i);
  88. while ( i-- ) {
  89. aa[i] = 0;
  90. }
  91. return aa;
  92. })();
  93. /******************************************************************************/
  94. var matrixSnapshot = function(tabId, details) {
  95. var µmuser = µm.userSettings;
  96. var r = {
  97. tabId: tabId,
  98. url: '',
  99. hostname: '',
  100. domain: '',
  101. blockedCount: 0,
  102. scope: '*',
  103. headers: µm.Matrix.getColumnHeaders(),
  104. tSwitches: {},
  105. pSwitches: {},
  106. rows: {},
  107. rowCount: 0,
  108. diff: [],
  109. userSettings: {
  110. colorBlindFriendly: µmuser.colorBlindFriendly,
  111. displayTextSize: µmuser.displayTextSize,
  112. popupCollapseDomains: µmuser.popupCollapseDomains,
  113. popupCollapseSpecificDomains: µmuser.popupCollapseSpecificDomains,
  114. popupHideBlacklisted: µmuser.popupHideBlacklisted,
  115. popupScopeLevel: µmuser.popupScopeLevel
  116. }
  117. };
  118. // Allow examination of behind-the-scene requests
  119. // TODO: Not portable
  120. if ( details.tabURL ) {
  121. if ( details.tabURL.lastIndexOf(vAPI.getURL(''), 0) === 0 ) {
  122. tabId = µm.behindTheSceneTabId;
  123. } else if ( details.tabURL === µm.behindTheSceneURL ) {
  124. tabId = µm.behindTheSceneTabId;
  125. }
  126. }
  127. var pageStore = µm.pageStatsFromTabId(tabId);
  128. if ( !pageStore ) {
  129. return r;
  130. }
  131. var headers = r.headers;
  132. r.url = pageStore.pageUrl;
  133. r.hostname = pageStore.pageHostname;
  134. r.domain = pageStore.pageDomain;
  135. r.blockedCount = pageStore.requestStats.blocked.all;
  136. if ( µmuser.popupScopeLevel === 'site' ) {
  137. r.scope = r.hostname;
  138. } else if ( µmuser.popupScopeLevel === 'domain' ) {
  139. r.scope = r.domain;
  140. }
  141. var switchNames = µm.Matrix.getSwitchNames();
  142. for ( var switchName in switchNames ) {
  143. if ( switchNames.hasOwnProperty(switchName) === false ) {
  144. continue;
  145. }
  146. r.tSwitches[switchName] = µm.tMatrix.evaluateSwitchZ(switchName, r.scope);
  147. r.pSwitches[switchName] = µm.pMatrix.evaluateSwitchZ(switchName, r.scope);
  148. }
  149. // These rows always exist
  150. r.rows['*'] = new RowSnapshot(r.scope, '*', '*');
  151. r.rows['1st-party'] = new RowSnapshot(r.scope, '1st-party', '1st-party');
  152. r.rowCount += 1;
  153. var µmuri = µm.URI;
  154. var reqKey, reqType, reqHostname, reqDomain;
  155. var desHostname;
  156. var row, typeIndex;
  157. var anyIndex = headers['*'];
  158. var pageRequests = pageStore.requests;
  159. var reqKeys = pageRequests.getRequestKeys();
  160. var iReqKey = reqKeys.length;
  161. var pos;
  162. while ( iReqKey-- ) {
  163. reqKey = reqKeys[iReqKey];
  164. reqType = pageRequests.typeFromRequestKey(reqKey);
  165. reqHostname = pageRequests.hostnameFromRequestKey(reqKey);
  166. // rhill 2013-10-23: hostname can be empty if the request is a data url
  167. // https://github.com/gorhill/httpswitchboard/issues/26
  168. if ( reqHostname === '' ) {
  169. reqHostname = pageStore.pageHostname;
  170. }
  171. reqDomain = µmuri.domainFromHostname(reqHostname) || reqHostname;
  172. // We want rows of self and ancestors
  173. desHostname = reqHostname;
  174. for ( ;; ) {
  175. // If row exists, ancestors exist
  176. if ( r.rows.hasOwnProperty(desHostname) !== false ) {
  177. break;
  178. }
  179. r.rows[desHostname] = new RowSnapshot(r.scope, desHostname, reqDomain);
  180. r.rowCount += 1;
  181. if ( desHostname === reqDomain ) {
  182. break;
  183. }
  184. pos = desHostname.indexOf('.');
  185. if ( pos === -1 ) {
  186. break;
  187. }
  188. desHostname = desHostname.slice(pos + 1);
  189. }
  190. typeIndex = headers[reqType];
  191. row = r.rows[reqHostname];
  192. row.counts[typeIndex] += 1;
  193. row.counts[anyIndex] += 1;
  194. row = r.rows[reqDomain];
  195. row.totals[typeIndex] += 1;
  196. row.totals[anyIndex] += 1;
  197. row = r.rows['*'];
  198. row.totals[typeIndex] += 1;
  199. row.totals[anyIndex] += 1;
  200. }
  201. r.diff = µm.tMatrix.diff(µm.pMatrix, r.hostname, Object.keys(r.rows));
  202. return r;
  203. };
  204. /******************************************************************************/
  205. var matrixSnapshotFromTabId = function(details, callback) {
  206. if ( details.targetTabId ) {
  207. callback(matrixSnapshot(details.targetTabId, details));
  208. return;
  209. }
  210. vAPI.tabs.get(null, function(tab) {
  211. callback(matrixSnapshot(tab.id, details));
  212. });
  213. };
  214. /******************************************************************************/
  215. var onMessage = function(request, sender, callback) {
  216. // Async
  217. switch ( request.what ) {
  218. case 'matrixSnapshot':
  219. matrixSnapshotFromTabId(request, callback);
  220. return;
  221. default:
  222. break;
  223. }
  224. // Sync
  225. var response;
  226. switch ( request.what ) {
  227. case 'disconnected':
  228. // https://github.com/gorhill/httpswitchboard/issues/94
  229. if ( µm.userSettings.smartAutoReload ) {
  230. vAPI.tabs.get(null, smartReload);
  231. }
  232. break;
  233. case 'toggleMatrixSwitch':
  234. µm.tMatrix.setSwitchZ(
  235. request.switchName,
  236. request.srcHostname,
  237. µm.tMatrix.evaluateSwitchZ(request.switchName, request.srcHostname) === false
  238. );
  239. break;
  240. case 'blacklistMatrixCell':
  241. µm.tMatrix.blacklistCell(
  242. request.srcHostname,
  243. request.desHostname,
  244. request.type
  245. );
  246. break;
  247. case 'whitelistMatrixCell':
  248. µm.tMatrix.whitelistCell(
  249. request.srcHostname,
  250. request.desHostname,
  251. request.type
  252. );
  253. break;
  254. case 'graylistMatrixCell':
  255. µm.tMatrix.graylistCell(
  256. request.srcHostname,
  257. request.desHostname,
  258. request.type
  259. );
  260. break;
  261. case 'applyDiffToPermanentMatrix': // aka "persist"
  262. if ( µm.pMatrix.applyDiff(request.diff, µm.tMatrix) ) {
  263. µm.saveMatrix();
  264. }
  265. break;
  266. case 'applyDiffToTemporaryMatrix': // aka "revert"
  267. µm.tMatrix.applyDiff(request.diff, µm.pMatrix);
  268. break;
  269. case 'revertTemporaryMatrix':
  270. µm.tMatrix.assign(µm.pMatrix);
  271. break;
  272. default:
  273. return vAPI.messaging.UNHANDLED;
  274. }
  275. callback(response);
  276. };
  277. vAPI.messaging.listen('popup.js', onMessage);
  278. })();
  279. /******************************************************************************/
  280. /******************************************************************************/
  281. // content scripts
  282. (function() {
  283. var µm = µMatrix;
  284. /******************************************************************************/
  285. var contentScriptSummaryHandler = function(tabId, details) {
  286. // TODO: Investigate "Error in response to tabs.executeScript: TypeError:
  287. // Cannot read property 'locationURL' of null" (2013-11-12). When can this
  288. // happens?
  289. if ( !details || !details.locationURL ) {
  290. return;
  291. }
  292. var pageURL = µm.pageUrlFromTabId(tabId);
  293. var pageStats = µm.pageStatsFromPageUrl(pageURL);
  294. var µmuri = µm.URI.set(details.locationURL);
  295. var frameURL = µmuri.normalizedURI();
  296. var frameHostname = µmuri.hostname;
  297. var urls, url, r;
  298. // https://github.com/gorhill/httpswitchboard/issues/333
  299. // Look-up here whether inline scripting is blocked for the frame.
  300. var inlineScriptBlocked = µm.mustBlock(µm.scopeFromURL(pageURL), frameHostname, 'script');
  301. // scripts
  302. // https://github.com/gorhill/httpswitchboard/issues/25
  303. if ( pageStats && inlineScriptBlocked ) {
  304. urls = details.scriptSources;
  305. for ( url in urls ) {
  306. if ( !urls.hasOwnProperty(url) ) {
  307. continue;
  308. }
  309. if ( url === '{inline_script}' ) {
  310. url = frameURL + '{inline_script}';
  311. }
  312. r = µm.filterRequest(pageURL, 'script', url);
  313. pageStats.recordRequest('script', url, r !== false, r);
  314. }
  315. }
  316. // TODO: as of 2014-05-26, not sure this is needed anymore, since µMatrix
  317. // no longer uses chrome.contentSettings API (I think that was the reason
  318. // this code was put in).
  319. // plugins
  320. // https://github.com/gorhill/httpswitchboard/issues/25
  321. if ( pageStats ) {
  322. urls = details.pluginSources;
  323. for ( url in urls ) {
  324. if ( !urls.hasOwnProperty(url) ) {
  325. continue;
  326. }
  327. r = µm.filterRequest(pageURL, 'plugin', url);
  328. pageStats.recordRequest('plugin', url, r !== false, r);
  329. }
  330. }
  331. // https://github.com/gorhill/httpswitchboard/issues/181
  332. µm.onPageLoadCompleted(pageURL);
  333. };
  334. /******************************************************************************/
  335. var contentScriptLocalStorageHandler = function(pageURL) {
  336. var µmuri = µm.URI.set(pageURL);
  337. var response = µm.mustBlock(µm.scopeFromURL(pageURL), µmuri.hostname, 'cookie');
  338. µm.recordFromPageUrl(
  339. pageURL,
  340. 'cookie',
  341. µmuri.rootURL() + '/{localStorage}',
  342. response
  343. );
  344. response = response && µm.userSettings.deleteLocalStorage;
  345. if ( response ) {
  346. µm.localStorageRemovedCounter++;
  347. }
  348. return response;
  349. };
  350. /******************************************************************************/
  351. var onMessage = function(request, sender, callback) {
  352. // Async
  353. switch ( request.what ) {
  354. default:
  355. break;
  356. }
  357. var tabId = sender.tab.id;
  358. // Sync
  359. var response;
  360. switch ( request.what ) {
  361. case 'contentScriptHasLocalStorage':
  362. response = contentScriptLocalStorageHandler(request.url);
  363. µm.updateBadgeAsync(tabId);
  364. break;
  365. case 'contentScriptSummary':
  366. contentScriptSummaryHandler(tabId, request);
  367. µm.updateBadgeAsync(tabId);
  368. break;
  369. case 'checkScriptBlacklisted':
  370. response = {
  371. scriptBlacklisted: µm.mustBlock(
  372. µm.scopeFromURL(request.url),
  373. µm.hostnameFromURL(request.url),
  374. 'script'
  375. )
  376. };
  377. break;
  378. case 'getUserAgentReplaceStr':
  379. response = µm.tMatrix.evaluateSwitchZ('ua-spoof', request.hostname) ?
  380. µm.userAgentReplaceStr :
  381. undefined;
  382. break;
  383. default:
  384. return vAPI.messaging.UNHANDLED;
  385. }
  386. callback(response);
  387. };
  388. vAPI.messaging.listen('contentscript-start.js', onMessage);
  389. vAPI.messaging.listen('contentscript-end.js', onMessage);
  390. /******************************************************************************/
  391. })();
  392. /******************************************************************************/
  393. /******************************************************************************/
  394. // settings.js
  395. (function() {
  396. var onMessage = function(request, sender, callback) {
  397. var µm = µMatrix;
  398. // Async
  399. switch ( request.what ) {
  400. default:
  401. break;
  402. }
  403. // Sync
  404. var response;
  405. switch ( request.what ) {
  406. default:
  407. return vAPI.messaging.UNHANDLED;
  408. }
  409. callback(response);
  410. };
  411. vAPI.messaging.listen('settings.js', onMessage);
  412. })();
  413. /******************************************************************************/
  414. /******************************************************************************/
  415. // privacy.js
  416. (function() {
  417. var onMessage = function(request, sender, callback) {
  418. var µm = µMatrix;
  419. // Async
  420. switch ( request.what ) {
  421. default:
  422. break;
  423. }
  424. // Sync
  425. var response;
  426. switch ( request.what ) {
  427. case 'getPrivacySettings':
  428. response = {
  429. userSettings: µm.userSettings,
  430. matrixSwitches: {
  431. 'https-strict': µm.pMatrix.evaluateSwitch('https-strict', '*') === 1,
  432. 'ua-spoof': µm.pMatrix.evaluateSwitch('ua-spoof', '*') === 1,
  433. 'referrer-spoof': µm.pMatrix.evaluateSwitch('referrer-spoof', '*') === 1
  434. }
  435. };
  436. break;
  437. case 'setMatrixSwitch':
  438. µm.tMatrix.setSwitch(request.switchName, '*', request.state);
  439. if ( µm.pMatrix.setSwitch(request.switchName, '*', request.state) ) {
  440. µm.saveMatrix();
  441. }
  442. break;
  443. default:
  444. return vAPI.messaging.UNHANDLED;
  445. }
  446. callback(response);
  447. };
  448. vAPI.messaging.listen('privacy.js', onMessage);
  449. })();
  450. /******************************************************************************/
  451. /******************************************************************************/
  452. // user-rules.js
  453. (function() {
  454. var µm = µMatrix;
  455. /******************************************************************************/
  456. var onMessage = function(request, sender, callback) {
  457. // Async
  458. switch ( request.what ) {
  459. default:
  460. break;
  461. }
  462. // Sync
  463. var response;
  464. switch ( request.what ) {
  465. case 'getUserRules':
  466. response = {
  467. temporaryRules: µm.tMatrix.toString(),
  468. permanentRules: µm.pMatrix.toString()
  469. };
  470. break;
  471. case 'setUserRules':
  472. if ( typeof request.temporaryRules === 'string' ) {
  473. µm.tMatrix.fromString(request.temporaryRules);
  474. }
  475. if ( typeof request.permanentRules === 'string' ) {
  476. µm.pMatrix.fromString(request.permanentRules);
  477. µm.saveMatrix();
  478. }
  479. response = {
  480. temporaryRules: µm.tMatrix.toString(),
  481. permanentRules: µm.pMatrix.toString()
  482. };
  483. break;
  484. default:
  485. return vAPI.messaging.UNHANDLED;
  486. }
  487. callback(response);
  488. };
  489. vAPI.messaging.listen('user-rules.js', onMessage);
  490. })();
  491. /******************************************************************************/
  492. /******************************************************************************/
  493. // hosts-files.js
  494. (function() {
  495. var µm = µMatrix;
  496. /******************************************************************************/
  497. var getLists = function(callback) {
  498. var r = {
  499. available: null,
  500. cache: null,
  501. current: µm.liveHostsFiles,
  502. blockedHostnameCount: µm.ubiquitousBlacklist.count,
  503. autoUpdate: µm.userSettings.autoUpdate
  504. };
  505. var onMetadataReady = function(entries) {
  506. r.cache = entries;
  507. callback(r);
  508. };
  509. var onAvailableHostsFilesReady = function(lists) {
  510. r.available = lists;
  511. µm.assets.metadata(onMetadataReady);
  512. };
  513. µm.getAvailableHostsFiles(onAvailableHostsFilesReady);
  514. };
  515. /******************************************************************************/
  516. var onMessage = function(request, sender, callback) {
  517. var µm = µMatrix;
  518. // Async
  519. switch ( request.what ) {
  520. case 'getLists':
  521. return getLists(callback);
  522. case 'purgeAllCaches':
  523. return µm.assets.purgeAll(callback);
  524. default:
  525. break;
  526. }
  527. // Sync
  528. var response;
  529. switch ( request.what ) {
  530. case 'purgeCache':
  531. µm.assets.purge(request.path);
  532. break;
  533. default:
  534. return vAPI.messaging.UNHANDLED;
  535. }
  536. callback(response);
  537. };
  538. vAPI.messaging.listen('hosts-files.js', onMessage);
  539. })();
  540. /******************************************************************************/
  541. /******************************************************************************/
  542. // info.js
  543. (function() {
  544. /******************************************************************************/
  545. // map(pageURL) => array of request log entries
  546. var getRequestLog = function(pageURL) {
  547. var requestLogs = {};
  548. var pageStores = µMatrix.pageStats;
  549. var pageURLs = pageURL ? [pageURL] : Object.keys(pageStores);
  550. var pageStore, pageRequestLog, logEntries, j, logEntry;
  551. for ( var i = 0; i < pageURLs.length; i++ ) {
  552. pageURL = pageURLs[i];
  553. pageStore = pageStores[pageURL];
  554. if ( !pageStore ) {
  555. continue;
  556. }
  557. pageRequestLog = [];
  558. logEntries = pageStore.requests.getLoggedRequests();
  559. j = logEntries.length;
  560. while ( j-- ) {
  561. // rhill 2013-12-04: `logEntry` can be null since a ring buffer is
  562. // now used, and it might not have been filled yet.
  563. if ( logEntry = logEntries[j] ) {
  564. pageRequestLog.push(logEntry);
  565. }
  566. }
  567. requestLogs[pageURL] = pageRequestLog;
  568. }
  569. return requestLogs;
  570. };
  571. /******************************************************************************/
  572. var clearRequestLog = function(pageURL) {
  573. var pageStores = µMatrix.pageStats;
  574. var pageURLs = pageURL ? [pageURL] : Object.keys(pageStores);
  575. var pageStore;
  576. for ( var i = 0; i < pageURLs.length; i++ ) {
  577. if ( pageStore = pageStores[pageURLs[i]] ) {
  578. pageStore.requests.clearLogBuffer();
  579. }
  580. }
  581. };
  582. /******************************************************************************/
  583. var onMessage = function(request, sender, callback) {
  584. var µm = µMatrix;
  585. // Async
  586. switch ( request.what ) {
  587. default:
  588. break;
  589. }
  590. // Sync
  591. var response;
  592. switch ( request.what ) {
  593. case 'getPageURLs':
  594. response = {
  595. pageURLs: Object.keys(µm.pageUrlToTabId),
  596. behindTheSceneURL: µm.behindTheSceneURL
  597. };
  598. break;
  599. case 'getStats':
  600. var pageStore = µm.pageStats[request.pageURL];
  601. response = {
  602. globalNetStats: µm.requestStats,
  603. pageNetStats: pageStore ? pageStore.requestStats : null,
  604. cookieHeaderFoiledCounter: µm.cookieHeaderFoiledCounter,
  605. refererHeaderFoiledCounter: µm.refererHeaderFoiledCounter,
  606. hyperlinkAuditingFoiledCounter: µm.hyperlinkAuditingFoiledCounter,
  607. cookieRemovedCounter: µm.cookieRemovedCounter,
  608. localStorageRemovedCounter: µm.localStorageRemovedCounter,
  609. browserCacheClearedCounter: µm.browserCacheClearedCounter
  610. };
  611. break;
  612. case 'getRequestLogs':
  613. response = getRequestLog(request.pageURL);
  614. break;
  615. case 'clearRequestLogs':
  616. clearRequestLog(request.pageURL);
  617. break;
  618. default:
  619. return vAPI.messaging.UNHANDLED;
  620. }
  621. callback(response);
  622. };
  623. vAPI.messaging.listen('info.js', onMessage);
  624. })();
  625. /******************************************************************************/
  626. /******************************************************************************/
  627. // about.js
  628. (function() {
  629. var µm = µMatrix;
  630. /******************************************************************************/
  631. var restoreUserData = function(userData) {
  632. var countdown = 3;
  633. var onCountdown = function() {
  634. countdown -= 1;
  635. if ( countdown === 0 ) {
  636. vAPI.app.restart();
  637. }
  638. };
  639. var onAllRemoved = function() {
  640. // Be sure to adjust `countdown` if adding/removing anything below
  641. µm.XAL.keyvalSetMany(userData.settings, onCountdown);
  642. µm.XAL.keyvalSetOne('userMatrix', userData.rules, onCountdown);
  643. µm.XAL.keyvalSetOne('liveHostsFiles', userData.hostsFiles, onCountdown);
  644. };
  645. // If we are going to restore all, might as well wipe out clean local
  646. // storage
  647. µm.XAL.keyvalRemoveAll(onAllRemoved);
  648. };
  649. /******************************************************************************/
  650. var resetUserData = function() {
  651. var onAllRemoved = function() {
  652. vAPI.app.restart();
  653. };
  654. µm.XAL.keyvalRemoveAll(onAllRemoved);
  655. };
  656. /******************************************************************************/
  657. var onMessage = function(request, sender, callback) {
  658. // Async
  659. switch ( request.what ) {
  660. default:
  661. break;
  662. }
  663. // Sync
  664. var response;
  665. switch ( request.what ) {
  666. case 'getAllUserData':
  667. response = {
  668. app: 'µMatrix',
  669. version: vAPI.app.version,
  670. when: Date.now(),
  671. settings: µm.userSettings,
  672. rules: µm.pMatrix.toString(),
  673. hostsFiles: µm.liveHostsFiles
  674. };
  675. break;
  676. case 'getSomeStats':
  677. response = {
  678. version: vAPI.app.version,
  679. storageUsed: µm.storageUsed
  680. };
  681. break;
  682. case 'restoreAllUserData':
  683. restoreUserData(request.userData);
  684. break;
  685. case 'resetAllUserData':
  686. resetUserData();
  687. break;
  688. default:
  689. return vAPI.messaging.UNHANDLED;
  690. }
  691. callback(response);
  692. };
  693. vAPI.messaging.listen('about.js', onMessage);
  694. /******************************************************************************/
  695. /******************************************************************************/
  696. })();
  697. /******************************************************************************/