chrome-browser-polyfill.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. /*
  2. * Copyright 2010-2020 Gildas Lormeau
  3. * contact : gildas.lormeau <at> gmail.com
  4. *
  5. * This file is part of SingleFile.
  6. *
  7. * The code in this file is free software: you can redistribute it and/or
  8. * modify it under the terms of the GNU Affero General Public License
  9. * (GNU AGPL) as published by the Free Software Foundation, either version 3
  10. * of the License, or (at your option) any later version.
  11. *
  12. * The code in this file is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
  15. * General Public License for more details.
  16. *
  17. * As additional permission under GNU AGPL version 3 section 7, you may
  18. * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
  19. * AGPL normally required by section 4, provided you include this license
  20. * notice and a URL through which recipients can access the Corresponding
  21. * Source.
  22. */
  23. (() => {
  24. const FEATURE_TESTS = {};
  25. const NON_COMPLIANT_IMPLEMENTATION = this.origin.startsWith("safari-web-extension://");
  26. if ((!this.browser || NON_COMPLIANT_IMPLEMENTATION) && this.chrome) {
  27. const nativeAPI = this.chrome;
  28. this.__defineGetter__("browser", () => ({
  29. browserAction: {
  30. onClicked: {
  31. addListener: listener => nativeAPI.browserAction.onClicked.addListener(listener)
  32. },
  33. setBadgeText: options => new Promise((resolve, reject) => {
  34. if (!FEATURE_TESTS["browserAction.setBadgeText"] || !FEATURE_TESTS["browserAction.setBadgeText"].callbackNotSupported) {
  35. try {
  36. nativeAPI.browserAction.setBadgeText(options, () => {
  37. if (nativeAPI.runtime.lastError) {
  38. reject(nativeAPI.runtime.lastError);
  39. } else {
  40. resolve();
  41. }
  42. });
  43. } catch (error) {
  44. FEATURE_TESTS["browserAction.setBadgeText"] = { callbackNotSupported: true };
  45. }
  46. }
  47. if (FEATURE_TESTS["browserAction.setBadgeText"] && FEATURE_TESTS["browserAction.setBadgeText"].callbackNotSupported) {
  48. nativeAPI.browserAction.setBadgeText(options);
  49. if (nativeAPI.runtime.lastError) {
  50. reject(nativeAPI.runtime.lastError);
  51. } else {
  52. resolve();
  53. }
  54. }
  55. }),
  56. setBadgeBackgroundColor: options => new Promise((resolve, reject) => {
  57. if (!FEATURE_TESTS["browserAction.setBadgeBackgroundColor"] || !FEATURE_TESTS["browserAction.setBadgeBackgroundColor"].callbackNotSupported) {
  58. try {
  59. nativeAPI.browserAction.setBadgeBackgroundColor(options, () => {
  60. if (nativeAPI.runtime.lastError) {
  61. reject(nativeAPI.runtime.lastError);
  62. } else {
  63. resolve();
  64. }
  65. });
  66. } catch (error) {
  67. FEATURE_TESTS["browserAction.setBadgeBackgroundColor"] = { callbackNotSupported: true };
  68. }
  69. }
  70. if (FEATURE_TESTS["browserAction.setBadgeBackgroundColor"] && FEATURE_TESTS["browserAction.setBadgeBackgroundColor"].callbackNotSupported) {
  71. nativeAPI.browserAction.setBadgeBackgroundColor(options);
  72. if (nativeAPI.runtime.lastError) {
  73. reject(nativeAPI.runtime.lastError);
  74. } else {
  75. resolve();
  76. }
  77. }
  78. }),
  79. setTitle: options => new Promise((resolve, reject) => {
  80. if (!FEATURE_TESTS["browserAction.setTitle"] || !FEATURE_TESTS["browserAction.setTitle"].callbackNotSupported) {
  81. try {
  82. nativeAPI.browserAction.setTitle(options, () => {
  83. if (nativeAPI.runtime.lastError) {
  84. reject(nativeAPI.runtime.lastError);
  85. } else {
  86. resolve();
  87. }
  88. });
  89. } catch (error) {
  90. FEATURE_TESTS["browserAction.setTitle"] = { callbackNotSupported: true };
  91. }
  92. }
  93. if (FEATURE_TESTS["browserAction.setTitle"] && FEATURE_TESTS["browserAction.setTitle"].callbackNotSupported) {
  94. nativeAPI.browserAction.setTitle(options);
  95. if (nativeAPI.runtime.lastError) {
  96. reject(nativeAPI.runtime.lastError);
  97. } else {
  98. resolve();
  99. }
  100. }
  101. }),
  102. setIcon: options => new Promise((resolve, reject) => {
  103. if (!FEATURE_TESTS["browserAction.setIcon"] || !FEATURE_TESTS["browserAction.setIcon"].callbackNotSupported) {
  104. try {
  105. nativeAPI.browserAction.setIcon(options, () => {
  106. if (nativeAPI.runtime.lastError) {
  107. reject(nativeAPI.runtime.lastError);
  108. } else {
  109. resolve();
  110. }
  111. });
  112. } catch (error) {
  113. FEATURE_TESTS["browserAction.setIcon"] = { callbackNotSupported: true };
  114. }
  115. }
  116. if (FEATURE_TESTS["browserAction.setIcon"] && FEATURE_TESTS["browserAction.setIcon"].callbackNotSupported) {
  117. nativeAPI.browserAction.setIcon(options);
  118. if (nativeAPI.runtime.lastError) {
  119. reject(nativeAPI.runtime.lastError);
  120. } else {
  121. resolve();
  122. }
  123. }
  124. })
  125. },
  126. bookmarks: {
  127. get: id => new Promise((resolve, reject) => {
  128. nativeAPI.bookmarks.get(id, result => {
  129. if (nativeAPI.runtime.lastError) {
  130. reject(nativeAPI.runtime.lastError);
  131. } else {
  132. resolve(result);
  133. }
  134. });
  135. }),
  136. onCreated: {
  137. addListener: listener => nativeAPI.bookmarks.onCreated.addListener(listener),
  138. removeListener: listener => nativeAPI.bookmarks.onCreated.removeListener(listener)
  139. },
  140. onChanged: {
  141. addListener: listener => nativeAPI.bookmarks.onChanged.addListener(listener),
  142. removeListener: listener => nativeAPI.bookmarks.onChanged.removeListener(listener)
  143. },
  144. update: (id, changes) => new Promise((resolve, reject) => {
  145. nativeAPI.bookmarks.update(id, changes, node => {
  146. if (nativeAPI.runtime.lastError) {
  147. reject(nativeAPI.runtime.lastError);
  148. } else {
  149. resolve(node);
  150. }
  151. });
  152. })
  153. },
  154. commands: {
  155. onCommand: {
  156. addListener: listener => nativeAPI.commands.onCommand.addListener(listener)
  157. }
  158. },
  159. downloads: {
  160. download: options => new Promise((resolve, reject) => {
  161. nativeAPI.downloads.download(options, downloadId => {
  162. if (nativeAPI.runtime.lastError) {
  163. reject(nativeAPI.runtime.lastError);
  164. } else {
  165. resolve(downloadId);
  166. }
  167. });
  168. }),
  169. onChanged: {
  170. addListener: listener => nativeAPI.downloads.onChanged.addListener(listener),
  171. removeListener: listener => nativeAPI.downloads.onChanged.removeListener(listener)
  172. },
  173. search: query => new Promise((resolve, reject) => {
  174. nativeAPI.downloads.search(query, downloadItems => {
  175. if (nativeAPI.runtime.lastError) {
  176. reject(nativeAPI.runtime.lastError);
  177. } else {
  178. resolve(downloadItems);
  179. }
  180. });
  181. })
  182. },
  183. i18n: {
  184. getMessage: (messageName, substitutions) => nativeAPI.i18n.getMessage(messageName, substitutions)
  185. },
  186. identity: {
  187. get getAuthToken() {
  188. return nativeAPI.identity && nativeAPI.identity.getAuthToken && (details => new Promise((resolve, reject) =>
  189. nativeAPI.identity.getAuthToken(details, token => {
  190. if (nativeAPI.runtime.lastError) {
  191. reject(nativeAPI.runtime.lastError);
  192. } else {
  193. resolve(token);
  194. }
  195. })
  196. ));
  197. },
  198. get launchWebAuthFlow() {
  199. return nativeAPI.identity && nativeAPI.identity.launchWebAuthFlow && (options => new Promise((resolve, reject) => {
  200. nativeAPI.identity.launchWebAuthFlow(options, responseUrl => {
  201. if (nativeAPI.runtime.lastError) {
  202. reject(nativeAPI.runtime.lastError);
  203. } else {
  204. resolve(responseUrl);
  205. }
  206. });
  207. }));
  208. },
  209. get removeCachedAuthToken() {
  210. return nativeAPI.identity && nativeAPI.identity.removeCachedAuthToken && (details => new Promise((resolve, reject) =>
  211. nativeAPI.identity.removeCachedAuthToken(details, () => {
  212. if (nativeAPI.runtime.lastError) {
  213. reject(nativeAPI.runtime.lastError);
  214. } else {
  215. resolve();
  216. }
  217. })
  218. ));
  219. }
  220. },
  221. menus: {
  222. onClicked: {
  223. addListener: listener => nativeAPI.contextMenus.onClicked.addListener(listener)
  224. },
  225. create: options => nativeAPI.contextMenus.create(options),
  226. update: (menuItemId, options) => new Promise((resolve, reject) => {
  227. nativeAPI.contextMenus.update(menuItemId, options, () => {
  228. if (nativeAPI.runtime.lastError) {
  229. reject(nativeAPI.runtime.lastError);
  230. } else {
  231. resolve();
  232. }
  233. });
  234. }),
  235. removeAll: () => new Promise((resolve, reject) => {
  236. nativeAPI.contextMenus.removeAll(() => {
  237. if (nativeAPI.runtime.lastError) {
  238. reject(nativeAPI.runtime.lastError);
  239. } else {
  240. resolve();
  241. }
  242. });
  243. })
  244. },
  245. permissions: {
  246. request: permissions => new Promise((resolve, reject) => {
  247. nativeAPI.permissions.request(permissions, result => {
  248. if (nativeAPI.runtime.lastError) {
  249. reject(nativeAPI.runtime.lastError);
  250. } else {
  251. resolve(result);
  252. }
  253. });
  254. }),
  255. remove: permissions => new Promise((resolve, reject) => {
  256. nativeAPI.permissions.remove(permissions, result => {
  257. if (nativeAPI.runtime.lastError) {
  258. reject(nativeAPI.runtime.lastError);
  259. } else {
  260. resolve(result);
  261. }
  262. });
  263. })
  264. },
  265. runtime: {
  266. connectNative: application => nativeAPI.runtime.connectNative(application),
  267. getManifest: () => nativeAPI.runtime.getManifest(),
  268. onMessage: {
  269. addListener: listener => nativeAPI.runtime.onMessage.addListener((message, sender, sendResponse) => {
  270. const response = listener(message, sender);
  271. if (response && typeof response.then == "function") {
  272. response
  273. .then(response => {
  274. if (response !== undefined) {
  275. try {
  276. sendResponse(response);
  277. } catch (error) {
  278. // ignored
  279. }
  280. }
  281. });
  282. return true;
  283. }
  284. }),
  285. removeListener: listener => nativeAPI.runtime.onMessage.removeListener(listener)
  286. },
  287. onMessageExternal: {
  288. addListener: listener => nativeAPI.runtime.onMessageExternal.addListener((message, sender, sendResponse) => {
  289. const response = listener(message, sender);
  290. if (response && typeof response.then == "function") {
  291. response
  292. .then(response => {
  293. if (response !== undefined) {
  294. try {
  295. sendResponse(response);
  296. } catch (error) {
  297. // ignored
  298. }
  299. }
  300. });
  301. return true;
  302. }
  303. })
  304. },
  305. sendMessage: message => new Promise((resolve, reject) => {
  306. nativeAPI.runtime.sendMessage(message, response => {
  307. if (nativeAPI.runtime.lastError) {
  308. reject(nativeAPI.runtime.lastError);
  309. } else {
  310. resolve(response);
  311. }
  312. });
  313. if (nativeAPI.runtime.lastError) {
  314. reject(nativeAPI.runtime.lastError);
  315. }
  316. }),
  317. getURL: (path) => nativeAPI.runtime.getURL(path),
  318. get lastError() {
  319. return nativeAPI.runtime.lastError;
  320. }
  321. },
  322. storage: {
  323. local: {
  324. set: value => new Promise((resolve, reject) => {
  325. nativeAPI.storage.local.set(value, () => {
  326. if (nativeAPI.runtime.lastError) {
  327. reject(nativeAPI.runtime.lastError);
  328. } else {
  329. resolve();
  330. }
  331. });
  332. }),
  333. get: () => new Promise((resolve, reject) => {
  334. nativeAPI.storage.local.get(null, value => {
  335. if (nativeAPI.runtime.lastError) {
  336. reject(nativeAPI.runtime.lastError);
  337. } else {
  338. resolve(value);
  339. }
  340. });
  341. }),
  342. clear: () => new Promise((resolve, reject) => {
  343. nativeAPI.storage.local.clear(() => {
  344. if (nativeAPI.runtime.lastError) {
  345. reject(nativeAPI.runtime.lastError);
  346. } else {
  347. resolve();
  348. }
  349. });
  350. }),
  351. remove: keys => new Promise((resolve, reject) => {
  352. nativeAPI.storage.local.remove(keys, () => {
  353. if (nativeAPI.runtime.lastError) {
  354. reject(nativeAPI.runtime.lastError);
  355. } else {
  356. resolve();
  357. }
  358. });
  359. })
  360. },
  361. sync: {
  362. set: value => new Promise((resolve, reject) => {
  363. nativeAPI.storage.sync.set(value, () => {
  364. if (nativeAPI.runtime.lastError) {
  365. reject(nativeAPI.runtime.lastError);
  366. } else {
  367. resolve();
  368. }
  369. });
  370. }),
  371. get: () => new Promise((resolve, reject) => {
  372. nativeAPI.storage.sync.get(null, value => {
  373. if (nativeAPI.runtime.lastError) {
  374. reject(nativeAPI.runtime.lastError);
  375. } else {
  376. resolve(value);
  377. }
  378. });
  379. }),
  380. clear: () => new Promise((resolve, reject) => {
  381. nativeAPI.storage.sync.clear(() => {
  382. if (nativeAPI.runtime.lastError) {
  383. reject(nativeAPI.runtime.lastError);
  384. } else {
  385. resolve();
  386. }
  387. });
  388. }),
  389. remove: keys => new Promise((resolve, reject) => {
  390. nativeAPI.storage.sync.remove(keys, () => {
  391. if (nativeAPI.runtime.lastError) {
  392. reject(nativeAPI.runtime.lastError);
  393. } else {
  394. resolve();
  395. }
  396. });
  397. })
  398. }
  399. },
  400. tabs: {
  401. onCreated: {
  402. addListener: listener => nativeAPI.tabs.onCreated.addListener(listener)
  403. },
  404. onActivated: {
  405. addListener: listener => nativeAPI.tabs.onActivated.addListener(listener)
  406. },
  407. onUpdated: {
  408. addListener: listener => nativeAPI.tabs.onUpdated.addListener(listener),
  409. removeListener: listener => nativeAPI.tabs.onUpdated.removeListener(listener)
  410. },
  411. onRemoved: {
  412. addListener: listener => nativeAPI.tabs.onRemoved.addListener(listener),
  413. removeListener: listener => nativeAPI.tabs.onRemoved.removeListener(listener)
  414. },
  415. executeScript: (tabId, details) => new Promise((resolve, reject) => {
  416. nativeAPI.tabs.executeScript(tabId, details, () => {
  417. if (nativeAPI.runtime.lastError) {
  418. reject(nativeAPI.runtime.lastError);
  419. } else {
  420. resolve();
  421. }
  422. });
  423. }),
  424. sendMessage: (tabId, message, options = {}) => new Promise((resolve, reject) => {
  425. nativeAPI.tabs.sendMessage(tabId, message, options, response => {
  426. if (nativeAPI.runtime.lastError) {
  427. reject(nativeAPI.runtime.lastError);
  428. } else {
  429. resolve(response);
  430. }
  431. });
  432. if (nativeAPI.runtime.lastError) {
  433. reject(nativeAPI.runtime.lastError);
  434. }
  435. }),
  436. query: options => new Promise((resolve, reject) => {
  437. nativeAPI.tabs.query(options, tabs => {
  438. if (nativeAPI.runtime.lastError) {
  439. reject(nativeAPI.runtime.lastError);
  440. } else {
  441. resolve(tabs);
  442. }
  443. });
  444. }),
  445. create: createProperties => new Promise((resolve, reject) => {
  446. nativeAPI.tabs.create(createProperties, tab => {
  447. if (nativeAPI.runtime.lastError) {
  448. reject(nativeAPI.runtime.lastError);
  449. } else {
  450. resolve(tab);
  451. }
  452. });
  453. }),
  454. get: options => new Promise((resolve, reject) => {
  455. nativeAPI.tabs.get(options, tab => {
  456. if (nativeAPI.runtime.lastError) {
  457. reject(nativeAPI.runtime.lastError);
  458. } else {
  459. resolve(tab);
  460. }
  461. });
  462. }),
  463. remove: tabId => new Promise((resolve, reject) => {
  464. nativeAPI.tabs.remove(tabId, () => {
  465. if (nativeAPI.runtime.lastError) {
  466. reject(nativeAPI.runtime.lastError);
  467. } else {
  468. resolve();
  469. }
  470. });
  471. }),
  472. update: (tabId, updateProperties) => new Promise((resolve, reject) => {
  473. nativeAPI.tabs.update(tabId, updateProperties, tab => {
  474. if (nativeAPI.runtime.lastError) {
  475. reject(nativeAPI.runtime.lastError);
  476. } else {
  477. resolve(tab);
  478. }
  479. });
  480. })
  481. },
  482. devtools: nativeAPI.devtools && {
  483. inspectedWindow: nativeAPI.devtools.inspectedWindow && {
  484. onResourceContentCommitted: nativeAPI.devtools.inspectedWindow.onResourceContentCommitted && {
  485. addListener: listener => nativeAPI.devtools.inspectedWindow.onResourceContentCommitted.addListener(listener)
  486. },
  487. get tabId() {
  488. return nativeAPI.devtools.inspectedWindow.tabId;
  489. }
  490. }
  491. },
  492. webRequest: {
  493. onBeforeSendHeaders: {
  494. addListener: (listener, filters, extraInfoSpec) => nativeAPI.webRequest.onBeforeSendHeaders.addListener(listener, filters, extraInfoSpec),
  495. removeListener: listener => nativeAPI.webRequest.onBeforeSendHeaders.removeListener(listener)
  496. }
  497. }
  498. }));
  499. }
  500. })();