michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: const Cu = Components.utils; michael@0: michael@0: Cu.import("resource://services-sync/main.js"); michael@0: Cu.import("resource:///modules/PlacesUIUtils.jsm"); michael@0: Cu.import("resource://gre/modules/PlacesUtils.jsm", this); michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: michael@0: let RemoteTabViewer = { michael@0: _tabsList: null, michael@0: michael@0: init: function () { michael@0: Services.obs.addObserver(this, "weave:service:login:finish", false); michael@0: Services.obs.addObserver(this, "weave:engine:sync:finish", false); michael@0: michael@0: this._tabsList = document.getElementById("tabsList"); michael@0: michael@0: this.buildList(true); michael@0: }, michael@0: michael@0: uninit: function () { michael@0: Services.obs.removeObserver(this, "weave:service:login:finish"); michael@0: Services.obs.removeObserver(this, "weave:engine:sync:finish"); michael@0: }, michael@0: michael@0: buildList: function(force) { michael@0: if (!Weave.Service.isLoggedIn || !this._refetchTabs(force)) michael@0: return; michael@0: //XXXzpao We should say something about not being logged in & not having data michael@0: // or tell the appropriate condition. (bug 583344) michael@0: michael@0: this._generateTabList(); michael@0: }, michael@0: michael@0: createItem: function(attrs) { michael@0: let item = document.createElement("richlistitem"); michael@0: michael@0: // Copy the attributes from the argument into the item michael@0: for (let attr in attrs) michael@0: item.setAttribute(attr, attrs[attr]); michael@0: michael@0: if (attrs["type"] == "tab") michael@0: item.label = attrs.title != "" ? attrs.title : attrs.url; michael@0: michael@0: return item; michael@0: }, michael@0: michael@0: filterTabs: function(event) { michael@0: let val = event.target.value.toLowerCase(); michael@0: let numTabs = this._tabsList.getRowCount(); michael@0: let clientTabs = 0; michael@0: let currentClient = null; michael@0: for (let i = 0;i < numTabs;i++) { michael@0: let item = this._tabsList.getItemAtIndex(i); michael@0: let hide = false; michael@0: if (item.getAttribute("type") == "tab") { michael@0: if (!item.getAttribute("url").toLowerCase().contains(val) && michael@0: !item.getAttribute("title").toLowerCase().contains(val)) michael@0: hide = true; michael@0: else michael@0: clientTabs++; michael@0: } michael@0: else if (item.getAttribute("type") == "client") { michael@0: if (currentClient) { michael@0: if (clientTabs == 0) michael@0: currentClient.hidden = true; michael@0: } michael@0: currentClient = item; michael@0: clientTabs = 0; michael@0: } michael@0: item.hidden = hide; michael@0: } michael@0: if (clientTabs == 0) michael@0: currentClient.hidden = true; michael@0: }, michael@0: michael@0: openSelected: function() { michael@0: let items = this._tabsList.selectedItems; michael@0: let urls = []; michael@0: for (let i = 0;i < items.length;i++) { michael@0: if (items[i].getAttribute("type") == "tab") { michael@0: urls.push(items[i].getAttribute("url")); michael@0: let index = this._tabsList.getIndexOfItem(items[i]); michael@0: this._tabsList.removeItemAt(index); michael@0: } michael@0: } michael@0: if (urls.length) { michael@0: getTopWin().gBrowser.loadTabs(urls); michael@0: this._tabsList.clearSelection(); michael@0: } michael@0: }, michael@0: michael@0: bookmarkSingleTab: function() { michael@0: let item = this._tabsList.selectedItems[0]; michael@0: let uri = Weave.Utils.makeURI(item.getAttribute("url")); michael@0: let title = item.getAttribute("title"); michael@0: PlacesUIUtils.showBookmarkDialog({ action: "add" michael@0: , type: "bookmark" michael@0: , uri: uri michael@0: , title: title michael@0: , hiddenRows: [ "description" michael@0: , "location" michael@0: , "loadInSidebar" michael@0: , "keyword" ] michael@0: }, window.top); michael@0: }, michael@0: michael@0: bookmarkSelectedTabs: function() { michael@0: let items = this._tabsList.selectedItems; michael@0: let URIs = []; michael@0: for (let i = 0;i < items.length;i++) { michael@0: if (items[i].getAttribute("type") == "tab") { michael@0: let uri = Weave.Utils.makeURI(items[i].getAttribute("url")); michael@0: if (!uri) michael@0: continue; michael@0: michael@0: URIs.push(uri); michael@0: } michael@0: } michael@0: if (URIs.length) { michael@0: PlacesUIUtils.showBookmarkDialog({ action: "add" michael@0: , type: "folder" michael@0: , URIList: URIs michael@0: , hiddenRows: [ "description" ] michael@0: }, window.top); michael@0: } michael@0: }, michael@0: michael@0: getIcon: function (iconUri, defaultIcon) { michael@0: try { michael@0: let iconURI = Weave.Utils.makeURI(iconUri); michael@0: return PlacesUtils.favicons.getFaviconLinkForIcon(iconURI).spec; michael@0: } catch(ex) { michael@0: // Do nothing. michael@0: } michael@0: michael@0: // Just give the provided default icon or the system's default. michael@0: return defaultIcon || PlacesUtils.favicons.defaultFavicon.spec; michael@0: }, michael@0: michael@0: _generateTabList: function() { michael@0: let engine = Weave.Service.engineManager.get("tabs"); michael@0: let list = this._tabsList; michael@0: michael@0: // clear out existing richlistitems michael@0: let count = list.getRowCount(); michael@0: if (count > 0) { michael@0: for (let i = count - 1; i >= 0; i--) michael@0: list.removeItemAt(i); michael@0: } michael@0: michael@0: let seenURLs = new Set(); michael@0: let localURLs = engine.getOpenURLs(); michael@0: michael@0: for (let [guid, client] in Iterator(engine.getAllClients())) { michael@0: // Create the client node, but don't add it in-case we don't show any tabs michael@0: let appendClient = true; michael@0: michael@0: client.tabs.forEach(function({title, urlHistory, icon}) { michael@0: let url = urlHistory[0]; michael@0: if (!url || localURLs.has(url) || seenURLs.has(url)) { michael@0: return; michael@0: } michael@0: seenURLs.add(url); michael@0: michael@0: if (appendClient) { michael@0: let attrs = { michael@0: type: "client", michael@0: clientName: client.clientName, michael@0: class: Weave.Service.clientsEngine.isMobile(client.id) ? "mobile" : "desktop" michael@0: }; michael@0: let clientEnt = this.createItem(attrs); michael@0: list.appendChild(clientEnt); michael@0: appendClient = false; michael@0: clientEnt.disabled = true; michael@0: } michael@0: let attrs = { michael@0: type: "tab", michael@0: title: title || url, michael@0: url: url, michael@0: icon: this.getIcon(icon), michael@0: } michael@0: let tab = this.createItem(attrs); michael@0: list.appendChild(tab); michael@0: }, this); michael@0: } michael@0: }, michael@0: michael@0: adjustContextMenu: function(event) { michael@0: let mode = "all"; michael@0: switch (this._tabsList.selectedItems.length) { michael@0: case 0: michael@0: break; michael@0: case 1: michael@0: mode = "single" michael@0: break; michael@0: default: michael@0: mode = "multiple"; michael@0: break; michael@0: } michael@0: let menu = document.getElementById("tabListContext"); michael@0: let el = menu.firstChild; michael@0: while (el) { michael@0: let showFor = el.getAttribute("showFor"); michael@0: if (showFor) michael@0: el.hidden = showFor != mode && showFor != "all"; michael@0: michael@0: el = el.nextSibling; michael@0: } michael@0: }, michael@0: michael@0: _refetchTabs: function(force) { michael@0: if (!force) { michael@0: // Don't bother refetching tabs if we already did so recently michael@0: let lastFetch = 0; michael@0: try { michael@0: lastFetch = Services.prefs.getIntPref("services.sync.lastTabFetch"); michael@0: } michael@0: catch (e) { /* Just use the default value of 0 */ } michael@0: let now = Math.floor(Date.now() / 1000); michael@0: if (now - lastFetch < 30) michael@0: return false; michael@0: } michael@0: michael@0: // if Clients hasn't synced yet this session, need to sync it as well michael@0: if (Weave.Service.clientsEngine.lastSync == 0) michael@0: Weave.Service.clientsEngine.sync(); michael@0: michael@0: // Force a sync only for the tabs engine michael@0: let engine = Weave.Service.engineManager.get("tabs"); michael@0: engine.lastModified = null; michael@0: engine.sync(); michael@0: Services.prefs.setIntPref("services.sync.lastTabFetch", michael@0: Math.floor(Date.now() / 1000)); michael@0: michael@0: return true; michael@0: }, michael@0: michael@0: observe: function(subject, topic, data) { michael@0: switch (topic) { michael@0: case "weave:service:login:finish": michael@0: this.buildList(true); michael@0: break; michael@0: case "weave:engine:sync:finish": michael@0: if (subject == "tabs") michael@0: this._generateTabList(); michael@0: break; michael@0: } michael@0: }, michael@0: michael@0: handleClick: function(event) { michael@0: if (event.target.getAttribute("type") != "tab") michael@0: return; michael@0: michael@0: if (event.button == 1) { michael@0: let url = event.target.getAttribute("url"); michael@0: openUILink(url, event); michael@0: let index = this._tabsList.getIndexOfItem(event.target); michael@0: this._tabsList.removeItemAt(index); michael@0: } michael@0: } michael@0: } michael@0: