aboutSyncTabs.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. var Cu = Components.utils;
  5. Cu.import("resource://services-common/utils.js");
  6. Cu.import("resource://services-sync/main.js");
  7. Cu.import("resource:///modules/PlacesUIUtils.jsm");
  8. Cu.import("resource://gre/modules/PlacesUtils.jsm", this);
  9. Cu.import("resource://gre/modules/Services.jsm");
  10. Cu.import("resource://gre/modules/XPCOMUtils.jsm");
  11. var RemoteTabViewer = {
  12. _tabsList: null,
  13. init: function () {
  14. Services.obs.addObserver(this, "weave:service:login:finish", false);
  15. Services.obs.addObserver(this, "weave:engine:sync:finish", false);
  16. this._tabsList = document.getElementById("tabsList");
  17. this.buildList(true);
  18. },
  19. uninit: function () {
  20. Services.obs.removeObserver(this, "weave:service:login:finish");
  21. Services.obs.removeObserver(this, "weave:engine:sync:finish");
  22. },
  23. createItem: function(attrs) {
  24. let item = document.createElement("richlistitem");
  25. // Copy the attributes from the argument into the item
  26. for (let attr in attrs) {
  27. item.setAttribute(attr, attrs[attr]);
  28. }
  29. if (attrs["type"] == "tab") {
  30. item.label = attrs.title != "" ? attrs.title : attrs.url;
  31. }
  32. return item;
  33. },
  34. filterTabs: function(event) {
  35. let val = event.target.value.toLowerCase();
  36. let numTabs = this._tabsList.getRowCount();
  37. let clientTabs = 0;
  38. let currentClient = null;
  39. for (let i = 0; i < numTabs; i++) {
  40. let item = this._tabsList.getItemAtIndex(i);
  41. let hide = false;
  42. if (item.getAttribute("type") == "tab") {
  43. if (!item.getAttribute("url").toLowerCase().includes(val) &&
  44. !item.getAttribute("title").toLowerCase().includes(val)) {
  45. hide = true;
  46. } else {
  47. clientTabs++;
  48. }
  49. }
  50. else if (item.getAttribute("type") == "client") {
  51. if (currentClient) {
  52. if (clientTabs == 0) {
  53. currentClient.hidden = true;
  54. }
  55. }
  56. currentClient = item;
  57. clientTabs = 0;
  58. }
  59. item.hidden = hide;
  60. }
  61. if (clientTabs == 0) {
  62. currentClient.hidden = true;
  63. }
  64. },
  65. openSelected: function() {
  66. let items = this._tabsList.selectedItems;
  67. let urls = [];
  68. for (let i = 0;i < items.length;i++) {
  69. if (items[i].getAttribute("type") == "tab") {
  70. urls.push(items[i].getAttribute("url"));
  71. let index = this._tabsList.getIndexOfItem(items[i]);
  72. this._tabsList.removeItemAt(index);
  73. }
  74. }
  75. if (urls.length) {
  76. getTopWin().gBrowser.loadTabs(urls);
  77. this._tabsList.clearSelection();
  78. }
  79. },
  80. bookmarkSingleTab: function() {
  81. let item = this._tabsList.selectedItems[0];
  82. let uri = Weave.Utils.makeURI(item.getAttribute("url"));
  83. let title = item.getAttribute("title");
  84. PlacesUIUtils.showBookmarkDialog({ action: "add"
  85. , type: "bookmark"
  86. , uri: uri
  87. , title: title
  88. , hiddenRows: [ "description"
  89. , "location"
  90. , "loadInSidebar"
  91. , "keyword" ]
  92. }, window.top);
  93. },
  94. bookmarkSelectedTabs: function() {
  95. let items = this._tabsList.selectedItems;
  96. let URIs = [];
  97. for (let i = 0;i < items.length;i++) {
  98. if (items[i].getAttribute("type") == "tab") {
  99. let uri = Weave.Utils.makeURI(items[i].getAttribute("url"));
  100. if (!uri) {
  101. continue;
  102. }
  103. URIs.push(uri);
  104. }
  105. }
  106. if (URIs.length) {
  107. PlacesUIUtils.showBookmarkDialog({ action: "add"
  108. , type: "folder"
  109. , URIList: URIs
  110. , hiddenRows: [ "description" ]
  111. }, window.top);
  112. }
  113. },
  114. getIcon: function (iconUri, defaultIcon) {
  115. try {
  116. let iconURI = Weave.Utils.makeURI(iconUri);
  117. return PlacesUtils.favicons.getFaviconLinkForIcon(iconURI).spec;
  118. } catch (ex) {
  119. // Do nothing.
  120. }
  121. // Just give the provided default icon or the system's default.
  122. return defaultIcon || PlacesUtils.favicons.defaultFavicon.spec;
  123. },
  124. _waitingForBuildList: false,
  125. _buildListRequested: false,
  126. buildList: function (force) {
  127. if (this._waitingForBuildList) {
  128. this._buildListRequested = true;
  129. return;
  130. }
  131. this._waitingForBuildList = true;
  132. this._buildListRequested = false;
  133. this._clearTabList();
  134. if (Weave.Service.isLoggedIn && this._refetchTabs(force)) {
  135. this._generateWeaveTabList();
  136. } else {
  137. //XXXzpao We should say something about not being logged in & not having data
  138. // or tell the appropriate condition. (bug 583344)
  139. }
  140. function complete() {
  141. this._waitingForBuildList = false;
  142. if (this._buildListRequested) {
  143. CommonUtils.nextTick(this.buildList, this);
  144. }
  145. }
  146. complete();
  147. },
  148. _clearTabList: function () {
  149. let list = this._tabsList;
  150. // Clear out existing richlistitems
  151. let count = list.getRowCount();
  152. if (count > 0) {
  153. for (let i = count - 1; i >= 0; i--) {
  154. list.removeItemAt(i);
  155. }
  156. }
  157. },
  158. _generateWeaveTabList: function () {
  159. let engine = Weave.Service.engineManager.get("tabs");
  160. let list = this._tabsList;
  161. let seenURLs = new Set();
  162. let localURLs = engine.getOpenURLs();
  163. for (let [guid, client] in Iterator(engine.getAllClients())) {
  164. // Create the client node, but don't add it in-case we don't show any tabs
  165. let appendClient = true;
  166. client.tabs.forEach(function({title, urlHistory, icon}) {
  167. let url = urlHistory[0];
  168. if (!url || localURLs.has(url) || seenURLs.has(url)) {
  169. return;
  170. }
  171. seenURLs.add(url);
  172. if (appendClient) {
  173. let attrs = {
  174. type: "client",
  175. clientName: client.clientName,
  176. class: Weave.Service.clientsEngine.isMobile(client.id) ? "mobile" : "desktop"
  177. };
  178. let clientEnt = this.createItem(attrs);
  179. list.appendChild(clientEnt);
  180. appendClient = false;
  181. clientEnt.disabled = true;
  182. }
  183. let attrs = {
  184. type: "tab",
  185. title: title || url,
  186. url: url,
  187. icon: this.getIcon(icon),
  188. }
  189. let tab = this.createItem(attrs);
  190. list.appendChild(tab);
  191. }, this);
  192. }
  193. },
  194. adjustContextMenu: function(event) {
  195. let mode = "all";
  196. switch (this._tabsList.selectedItems.length) {
  197. case 0:
  198. break;
  199. case 1:
  200. mode = "single"
  201. break;
  202. default:
  203. mode = "multiple";
  204. break;
  205. }
  206. let menu = document.getElementById("tabListContext");
  207. let el = menu.firstChild;
  208. while (el) {
  209. let showFor = el.getAttribute("showFor");
  210. if (showFor) {
  211. el.hidden = showFor != mode && showFor != "all";
  212. }
  213. el = el.nextSibling;
  214. }
  215. },
  216. _refetchTabs: function(force) {
  217. if (!force) {
  218. // Don't bother refetching tabs if we already did so recently
  219. let lastFetch = 0;
  220. try {
  221. lastFetch = Services.prefs.getIntPref("services.sync.lastTabFetch");
  222. }
  223. catch (e) {
  224. /* Just use the default value of 0 */
  225. }
  226. let now = Math.floor(Date.now() / 1000);
  227. if (now - lastFetch < 30) {
  228. return false;
  229. }
  230. }
  231. // if Clients hasn't synced yet this session, we need to sync it as well.
  232. if (Weave.Service.clientsEngine.lastSync == 0) {
  233. Weave.Service.clientsEngine.sync();
  234. }
  235. // Force a sync only for the tabs engine
  236. let engine = Weave.Service.engineManager.get("tabs");
  237. engine.lastModified = null;
  238. engine.sync();
  239. Services.prefs.setIntPref("services.sync.lastTabFetch",
  240. Math.floor(Date.now() / 1000));
  241. return true;
  242. },
  243. observe: function(subject, topic, data) {
  244. switch (topic) {
  245. case "weave:service:login:finish":
  246. this.buildList(true);
  247. break;
  248. case "weave:engine:sync:finish":
  249. if (subject == "tabs") {
  250. this.buildList(false);
  251. }
  252. break;
  253. }
  254. },
  255. handleClick: function(event) {
  256. if (event.target.getAttribute("type") != "tab") {
  257. return;
  258. }
  259. if (event.button == 1) {
  260. let url = event.target.getAttribute("url");
  261. openUILink(url, event);
  262. let index = this._tabsList.getIndexOfItem(event.target);
  263. this._tabsList.removeItemAt(index);
  264. }
  265. }
  266. }