bookmarks.js 4.8 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 */
  24. import * as config from "./config.js";
  25. import * as business from "./business.js";
  26. const pendingSaves = new Set();
  27. Promise.resolve().then(enable);
  28. export {
  29. onMessage,
  30. enable as saveCreatedBookmarks,
  31. disable,
  32. update
  33. };
  34. async function onMessage(message) {
  35. if (message.method.endsWith(".saveCreatedBookmarks")) {
  36. enable();
  37. return {};
  38. }
  39. if (message.method.endsWith(".disable")) {
  40. disable();
  41. return {};
  42. }
  43. }
  44. async function enable() {
  45. try {
  46. browser.bookmarks.onCreated.removeListener(onCreated);
  47. browser.bookmarks.onMoved.removeListener(onMoved);
  48. } catch (error) {
  49. // ignored
  50. }
  51. let enabled;
  52. const profiles = await config.getProfiles();
  53. Object.keys(profiles).forEach(profileName => {
  54. if (profiles[profileName].saveCreatedBookmarks) {
  55. enabled = true;
  56. }
  57. });
  58. if (enabled) {
  59. browser.bookmarks.onCreated.addListener(onCreated);
  60. browser.bookmarks.onMoved.addListener(onMoved);
  61. }
  62. }
  63. async function disable() {
  64. let disabled;
  65. const profiles = await config.getProfiles();
  66. Object.keys(profiles).forEach(profileName => disabled = disabled || !profiles[profileName].saveCreatedBookmarks);
  67. if (disabled) {
  68. browser.bookmarks.onCreated.removeListener(onCreated);
  69. browser.bookmarks.onMoved.removeListener(onMoved);
  70. }
  71. }
  72. async function update(id, changes) {
  73. try {
  74. await browser.bookmarks.update(id, changes);
  75. } catch (error) {
  76. // ignored
  77. }
  78. }
  79. async function onCreated(bookmarkId, bookmarkInfo) {
  80. pendingSaves.add(bookmarkId);
  81. await saveBookmark(bookmarkId, bookmarkInfo.url, bookmarkInfo);
  82. }
  83. async function onMoved(bookmarkId, bookmarkInfo) {
  84. if (pendingSaves.has(bookmarkId)) {
  85. const bookmarks = await browser.bookmarks.get(bookmarkId);
  86. if (bookmarks[0]) {
  87. await saveBookmark(bookmarkId, bookmarks[0].url, bookmarkInfo);
  88. }
  89. }
  90. }
  91. async function saveBookmark(bookmarkId, url, bookmarkInfo) {
  92. const activeTabs = await browser.tabs.query({ lastFocusedWindow: true, active: true });
  93. const options = await config.getOptions(url);
  94. if (options.saveCreatedBookmarks) {
  95. const bookmarkFolders = await getParentFolders(bookmarkInfo.parentId);
  96. const allowedBookmarkSet = options.allowedBookmarkFolders.toString();
  97. const allowedBookmark = bookmarkFolders.find(folder => options.allowedBookmarkFolders.includes(folder));
  98. const ignoredBookmarkSet = options.ignoredBookmarkFolders.toString();
  99. const ignoredBookmark = bookmarkFolders.find(folder => options.ignoredBookmarkFolders.includes(folder));
  100. if (
  101. ((allowedBookmarkSet && allowedBookmark) || !allowedBookmarkSet) &&
  102. ((ignoredBookmarkSet && !ignoredBookmark) || !ignoredBookmarkSet)
  103. ) {
  104. if (activeTabs.length && activeTabs[0].url == url) {
  105. pendingSaves.delete(bookmarkId);
  106. business.saveTabs(activeTabs, { bookmarkId, bookmarkFolders });
  107. } else {
  108. const tabs = await browser.tabs.query({});
  109. if (tabs.length) {
  110. const tab = tabs.find(tab => tab.url == url);
  111. if (tab) {
  112. pendingSaves.delete(bookmarkId);
  113. business.saveTabs([tab], { bookmarkId, bookmarkFolders });
  114. } else {
  115. if (url) {
  116. if (url == "about:blank") {
  117. browser.bookmarks.onChanged.addListener(onChanged);
  118. } else {
  119. saveUrl(url);
  120. }
  121. }
  122. }
  123. }
  124. }
  125. }
  126. }
  127. async function getParentFolders(id, folderNames = []) {
  128. if (id) {
  129. const bookmarkNode = (await browser.bookmarks.get(id))[0];
  130. if (bookmarkNode && bookmarkNode.title) {
  131. folderNames.unshift(bookmarkNode.title);
  132. await getParentFolders(bookmarkNode.parentId, folderNames);
  133. }
  134. }
  135. return folderNames;
  136. }
  137. function onChanged(id, changeInfo) {
  138. if (id == bookmarkId && changeInfo.url) {
  139. browser.bookmarks.onChanged.removeListener(onChanged);
  140. saveUrl(changeInfo.url);
  141. }
  142. }
  143. function saveUrl(url) {
  144. pendingSaves.delete(bookmarkId);
  145. business.saveUrls([url], { bookmarkId });
  146. }
  147. }