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