chrome-api-no-cb.js 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. #!/usr/bin/env node
  2. /*
  3. Generates a list of callbackless chrome.* API methods from chromium source
  4. to be used in polyfill.js
  5. */
  6. 'use strict';
  7. const manifest = require('../manifest.json');
  8. const fetch = require('make-fetch-happen');
  9. (async () => {
  10. manifest.permissions.push('extension', 'i18n', 'runtime');
  11. const FN_NO_CB = /\bstatic (\w+) (\w+)(?![^)]*callback)\(\s*([^)]*)\)/g;
  12. const BASE = 'https://github.com/chromium/chromium/raw/master/';
  13. const PATHS = [
  14. [BASE + 'extensions/common/api/', 'schema.gni'],
  15. [BASE + 'chrome/common/extensions/api/', 'api_sources.gni'],
  16. ];
  17. console.debug('Downloading...');
  18. const schemas = await Promise.all(PATHS.map(([path, name]) => fetchText(path + name)));
  19. const files = {};
  20. schemas.forEach((text, i) => {
  21. const path = PATHS[i][0];
  22. text.match(/\w+\.(idl|json)/g).forEach(name => {
  23. files[name] = path;
  24. });
  25. });
  26. const resList = [];
  27. const resObj = {};
  28. await Promise.all(Object.entries(files).map(processApi));
  29. Object.entries(resObj)
  30. .sort(([a], [b]) => a < b ? -1 : a > b)
  31. .forEach(([key, val]) => {
  32. delete resObj[key];
  33. resObj[key] = val;
  34. val.sort();
  35. });
  36. console.log(resList.sort().join('\n'));
  37. console.log(JSON.stringify(resObj));
  38. async function fetchText(file) {
  39. return (await fetch(file)).text();
  40. }
  41. async function processApi([file, path]) {
  42. const [name, ext] = file.split('.');
  43. const api = manifest.permissions.find(p =>
  44. name === p.replace(/([A-Z])/g, s => '_' + s.toLowerCase()) ||
  45. name === p.replace(/\./g, '_'));
  46. if (!api) return;
  47. const text = await fetchText(path + file);
  48. const noCmt = text.replace(/^\s*\/\/.*$/gm, '');
  49. if (ext === 'idl') {
  50. const fnBlock = (noCmt.split(/\n\s*interface Functions {\s*/)[1] || '')
  51. .split(/\n\s*interface \w+ {/)[0];
  52. for (let m; (m = FN_NO_CB.exec(fnBlock));) {
  53. const [, type, name, params] = m;
  54. resList.push(`chrome.${api}.${name}(${params.replace(/\n\s*/g, ' ')}): ${type}`);
  55. (resObj[api] || (resObj[api] = [])).push(name);
  56. }
  57. } else {
  58. for (const fn of JSON.parse(noCmt)[0].functions || []) {
  59. const last = fn.parameters[fn.parameters.length - 1];
  60. if (!fn.returns_async && (!last || last.type !== 'function')) {
  61. resList.push(`chrome.${api}.${fn.name}(${
  62. fn.parameters.map(p => `${p.optional ? '?' : ''}${p.name}: ${p.type}`).join(', ')
  63. })`);
  64. (resObj[api] || (resObj[api] = [])).push(fn.name);
  65. }
  66. }
  67. }
  68. }
  69. })();