tabs.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  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. /* global browser, singlefile, setTimeout */
  24. singlefile.extension.core.bg.tabs = (() => {
  25. const DELAY_MAYBE_INIT = 1500;
  26. const pendingPrompts = new Map();
  27. browser.tabs.onCreated.addListener(tab => onTabCreated(tab));
  28. browser.tabs.onActivated.addListener(activeInfo => onTabActivated(activeInfo));
  29. browser.tabs.onRemoved.addListener(tabId => onTabRemoved(tabId));
  30. browser.tabs.onUpdated.addListener((tabId, changeInfo) => onTabUpdated(tabId, changeInfo));
  31. return {
  32. onMessage,
  33. get: async options => {
  34. const tabs = await browser.tabs.query(options);
  35. return tabs.sort((tab1, tab2) => tab1.index - tab2.index);
  36. },
  37. create: async createProperties => {
  38. const tab = await browser.tabs.create(createProperties);
  39. return new Promise((resolve, reject) => {
  40. browser.tabs.onUpdated.addListener(onTabUpdated);
  41. browser.tabs.onRemoved.addListener(onTabRemoved);
  42. function onTabUpdated(tabId, changeInfo) {
  43. if (tabId == tab.id && changeInfo.status == "complete") {
  44. resolve(tab);
  45. browser.tabs.onUpdated.removeListener(onTabUpdated);
  46. browser.tabs.onRemoved.removeListener(onTabRemoved);
  47. }
  48. }
  49. function onTabRemoved(tabId) {
  50. if (tabId == tab.id) {
  51. reject(tabId);
  52. browser.tabs.onRemoved.removeListener(onTabRemoved);
  53. }
  54. }
  55. });
  56. },
  57. sendMessage: (tabId, message, options) => browser.tabs.sendMessage(tabId, message, options),
  58. remove: tabId => browser.tabs.remove(tabId),
  59. promptValue: async promptMessage => {
  60. const tabs = await browser.tabs.query({ currentWindow: true, active: true });
  61. return new Promise((resolve, reject) => {
  62. const selectedTabId = tabs[0].id;
  63. browser.tabs.onRemoved.addListener(onTabRemoved);
  64. pendingPrompts.set(selectedTabId, { resolve, reject });
  65. browser.tabs.sendMessage(selectedTabId, { method: "common.promptValueRequest", promptMessage });
  66. function onTabRemoved(tabId) {
  67. if (tabId == selectedTabId) {
  68. pendingPrompts.delete(tabId);
  69. browser.tabs.onUpdated.removeListener(onTabRemoved);
  70. reject();
  71. }
  72. }
  73. });
  74. },
  75. extractAuthCode: authURL => {
  76. return new Promise((resolve, reject) => {
  77. let authTabId;
  78. browser.tabs.onUpdated.addListener(onTabUpdated);
  79. browser.tabs.onRemoved.addListener(onTabRemoved);
  80. function onTabUpdated(tabId, changeInfo) {
  81. if (changeInfo && changeInfo.url == authURL) {
  82. authTabId = tabId;
  83. }
  84. if (authTabId == tabId && changeInfo && changeInfo.title && changeInfo.title.startsWith("Success code=")) {
  85. browser.tabs.onUpdated.removeListener(onTabUpdated);
  86. browser.tabs.onUpdated.removeListener(onTabRemoved);
  87. resolve(changeInfo.title.substring(13, changeInfo.title.length - 49));
  88. }
  89. }
  90. function onTabRemoved(tabId) {
  91. if (tabId == authTabId) {
  92. browser.tabs.onUpdated.removeListener(onTabUpdated);
  93. browser.tabs.onUpdated.removeListener(onTabRemoved);
  94. reject();
  95. }
  96. }
  97. });
  98. },
  99. launchWebAuthFlow: async options => {
  100. const tab = await browser.tabs.create({ url: options.url, active: true });
  101. return new Promise((resolve, reject) => {
  102. browser.tabs.onRemoved.addListener(onTabRemoved);
  103. function onTabRemoved(tabId) {
  104. if (tabId == tab.id) {
  105. browser.tabs.onRemoved.removeListener(onTabRemoved);
  106. reject(new Error("code_required"));
  107. }
  108. }
  109. });
  110. }
  111. };
  112. async function onMessage(message, sender) {
  113. if (message.method.endsWith(".init")) {
  114. singlefile.extension.ui.bg.main.onInit(sender.tab);
  115. singlefile.extension.core.bg.business.onInit(sender.tab);
  116. singlefile.extension.core.bg.autosave.onInit(sender.tab);
  117. singlefile.extension.core.bg.editor.onInit(sender.tab);
  118. }
  119. if (message.method.endsWith(".promptValueResponse")) {
  120. const promptPromise = pendingPrompts.get(sender.tab.id);
  121. if (promptPromise) {
  122. promptPromise.resolve(message.value);
  123. pendingPrompts.delete(sender.tab.id);
  124. }
  125. }
  126. if (message.method.endsWith(".getOptions")) {
  127. return singlefile.extension.core.bg.config.getOptions(message.url);
  128. }
  129. if (message.method.endsWith(".activate")) {
  130. await browser.tabs.update(message.tabId, { active: true });
  131. }
  132. }
  133. async function onTabUpdated(tabId, changeInfo) {
  134. if (changeInfo.status == "complete") {
  135. setTimeout(() => browser.tabs.sendMessage(tabId, { method: "content.maybeInit" }), DELAY_MAYBE_INIT);
  136. }
  137. }
  138. function onTabCreated(tab) {
  139. singlefile.extension.ui.bg.main.onTabCreated(tab);
  140. }
  141. async function onTabActivated(activeInfo) {
  142. const tab = await browser.tabs.get(activeInfo.tabId);
  143. singlefile.extension.ui.bg.main.onTabActivated(tab, activeInfo);
  144. }
  145. function onTabRemoved(tabId) {
  146. singlefile.extension.core.bg.tabsData.onTabRemoved(tabId);
  147. singlefile.extension.core.bg.editor.onTabRemoved(tabId);
  148. singlefile.extension.core.bg.business.onTabRemoved(tabId);
  149. }
  150. })();