forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			354 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			354 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| var Cu = Components.utils;
 | |
| 
 | |
| Cu.import("resource://services-common/utils.js");
 | |
| Cu.import("resource://services-sync/main.js");
 | |
| Cu.import("resource:///modules/PlacesUIUtils.jsm");
 | |
| Cu.import("resource://gre/modules/AppConstants.jsm");
 | |
| Cu.import("resource://gre/modules/PlacesUtils.jsm", this);
 | |
| Cu.import("resource://gre/modules/Services.jsm");
 | |
| Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 | |
| 
 | |
| XPCOMUtils.defineLazyModuleGetter(this, "Promise",
 | |
|                                   "resource://gre/modules/Promise.jsm");
 | |
| 
 | |
| if (AppConstants.MOZ_SERVICES_CLOUDSYNC) {
 | |
|   XPCOMUtils.defineLazyModuleGetter(this, "CloudSync",
 | |
|                                     "resource://gre/modules/CloudSync.jsm");
 | |
| }
 | |
| 
 | |
| var RemoteTabViewer = {
 | |
|   _tabsList: null,
 | |
| 
 | |
|   init() {
 | |
|     Services.obs.addObserver(this, "weave:service:login:finish", false);
 | |
|     Services.obs.addObserver(this, "weave:engine:sync:finish", false);
 | |
| 
 | |
|     Services.obs.addObserver(this, "cloudsync:tabs:update", false);
 | |
| 
 | |
|     this._tabsList = document.getElementById("tabsList");
 | |
| 
 | |
|     this.buildList(true);
 | |
|   },
 | |
| 
 | |
|   uninit() {
 | |
|     Services.obs.removeObserver(this, "weave:service:login:finish");
 | |
|     Services.obs.removeObserver(this, "weave:engine:sync:finish");
 | |
| 
 | |
|     Services.obs.removeObserver(this, "cloudsync:tabs:update");
 | |
|   },
 | |
| 
 | |
|   createItem(attrs) {
 | |
|     let item = document.createElement("richlistitem");
 | |
| 
 | |
|     // Copy the attributes from the argument into the item.
 | |
|     for (let attr in attrs) {
 | |
|       item.setAttribute(attr, attrs[attr]);
 | |
|     }
 | |
| 
 | |
|     if (attrs["type"] == "tab") {
 | |
|       item.label = attrs.title != "" ? attrs.title : attrs.url;
 | |
|     }
 | |
| 
 | |
|     return item;
 | |
|   },
 | |
| 
 | |
|   filterTabs(event) {
 | |
|     let val = event.target.value.toLowerCase();
 | |
|     let numTabs = this._tabsList.getRowCount();
 | |
|     let clientTabs = 0;
 | |
|     let currentClient = null;
 | |
| 
 | |
|     for (let i = 0; i < numTabs; i++) {
 | |
|       let item = this._tabsList.getItemAtIndex(i);
 | |
|       let hide = false;
 | |
|       if (item.getAttribute("type") == "tab") {
 | |
|         if (!item.getAttribute("url").toLowerCase().includes(val) &&
 | |
|             !item.getAttribute("title").toLowerCase().includes(val)) {
 | |
|           hide = true;
 | |
|         } else {
 | |
|           clientTabs++;
 | |
|         }
 | |
|       } else if (item.getAttribute("type") == "client") {
 | |
|         if (currentClient) {
 | |
|           if (clientTabs == 0) {
 | |
|             currentClient.hidden = true;
 | |
|           }
 | |
|         }
 | |
|         currentClient = item;
 | |
|         clientTabs = 0;
 | |
|       }
 | |
|       item.hidden = hide;
 | |
|     }
 | |
|     if (clientTabs == 0) {
 | |
|       currentClient.hidden = true;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   openSelected() {
 | |
|     let items = this._tabsList.selectedItems;
 | |
|     let urls = [];
 | |
|     for (let i = 0; i < items.length; i++) {
 | |
|       if (items[i].getAttribute("type") == "tab") {
 | |
|         urls.push(items[i].getAttribute("url"));
 | |
|         let index = this._tabsList.getIndexOfItem(items[i]);
 | |
|         this._tabsList.removeItemAt(index);
 | |
|       }
 | |
|     }
 | |
|     if (urls.length) {
 | |
|       getTopWin().gBrowser.loadTabs(urls);
 | |
|       this._tabsList.clearSelection();
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   bookmarkSingleTab() {
 | |
|     let item = this._tabsList.selectedItems[0];
 | |
|     let uri = Weave.Utils.makeURI(item.getAttribute("url"));
 | |
|     let title = item.getAttribute("title");
 | |
|     PlacesUIUtils.showBookmarkDialog({ action: "add"
 | |
|                                      , type: "bookmark"
 | |
|                                      , uri
 | |
|                                      , title
 | |
|                                      , hiddenRows: [ "description"
 | |
|                                                    , "location"
 | |
|                                                    , "loadInSidebar"
 | |
|                                                    , "keyword" ]
 | |
|                                      }, window.top);
 | |
|   },
 | |
| 
 | |
|   bookmarkSelectedTabs() {
 | |
|     let items = this._tabsList.selectedItems;
 | |
|     let URIs = [];
 | |
|     for (let i = 0; i < items.length; i++) {
 | |
|       if (items[i].getAttribute("type") == "tab") {
 | |
|         let uri = Weave.Utils.makeURI(items[i].getAttribute("url"));
 | |
|         if (!uri) {
 | |
|           continue;
 | |
|         }
 | |
| 
 | |
|         URIs.push(uri);
 | |
|       }
 | |
|     }
 | |
|     if (URIs.length) {
 | |
|       PlacesUIUtils.showBookmarkDialog({ action: "add"
 | |
|                                        , type: "folder"
 | |
|                                        , URIList: URIs
 | |
|                                        , hiddenRows: [ "description" ]
 | |
|                                        }, window.top);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   getIcon(iconUri, defaultIcon) {
 | |
|     try {
 | |
|       let iconURI = Weave.Utils.makeURI(iconUri);
 | |
|       return PlacesUtils.favicons.getFaviconLinkForIcon(iconURI).spec;
 | |
|     } catch (ex) {
 | |
|       // Do nothing.
 | |
|     }
 | |
| 
 | |
|     // Just give the provided default icon or the system's default.
 | |
|     return defaultIcon || PlacesUtils.favicons.defaultFavicon.spec;
 | |
|   },
 | |
| 
 | |
|   _waitingForBuildList: false,
 | |
| 
 | |
|   _buildListRequested: false,
 | |
| 
 | |
|   buildList(forceSync) {
 | |
|     if (this._waitingForBuildList) {
 | |
|       this._buildListRequested = true;
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     this._waitingForBuildList = true;
 | |
|     this._buildListRequested = false;
 | |
| 
 | |
|     this._clearTabList();
 | |
| 
 | |
|     if (Weave.Service.isLoggedIn) {
 | |
|       this._refetchTabs(forceSync);
 | |
|       this._generateWeaveTabList();
 | |
|     } else {
 | |
|       // XXXzpao We should say something about not being logged in & not having data
 | |
|       //        or tell the appropriate condition. (bug 583344)
 | |
|     }
 | |
| 
 | |
|     let complete = () => {
 | |
|       this._waitingForBuildList = false;
 | |
|       if (this._buildListRequested) {
 | |
|         CommonUtils.nextTick(this.buildList, this);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (CloudSync && CloudSync.ready && CloudSync().tabsReady && CloudSync().tabs.hasRemoteTabs()) {
 | |
|       this._generateCloudSyncTabList()
 | |
|           .then(complete, complete);
 | |
|     } else {
 | |
|       complete();
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   _clearTabList() {
 | |
|     let list = this._tabsList;
 | |
| 
 | |
|     // Clear out existing richlistitems.
 | |
|     let count = list.getRowCount();
 | |
|     if (count > 0) {
 | |
|       for (let i = count - 1; i >= 0; i--) {
 | |
|         list.removeItemAt(i);
 | |
|       }
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   _generateWeaveTabList() {
 | |
|     let engine = Weave.Service.engineManager.get("tabs");
 | |
|     let list = this._tabsList;
 | |
| 
 | |
|     let seenURLs = new Set();
 | |
|     let localURLs = engine.getOpenURLs();
 | |
| 
 | |
|     for (let [, client] of Object.entries(engine.getAllClients())) {
 | |
|       // Create the client node, but don't add it in-case we don't show any tabs
 | |
|       let appendClient = true;
 | |
| 
 | |
|       client.tabs.forEach(function({title, urlHistory, icon}) {
 | |
|         let url = urlHistory[0];
 | |
|         if (!url || localURLs.has(url) || seenURLs.has(url)) {
 | |
|           return;
 | |
|         }
 | |
|         seenURLs.add(url);
 | |
| 
 | |
|         if (appendClient) {
 | |
|           let attrs = {
 | |
|             type: "client",
 | |
|             clientName: client.clientName,
 | |
|             class: Weave.Service.clientsEngine.isMobile(client.id) ? "mobile" : "desktop"
 | |
|           };
 | |
|           let clientEnt = this.createItem(attrs);
 | |
|           list.appendChild(clientEnt);
 | |
|           appendClient = false;
 | |
|           clientEnt.disabled = true;
 | |
|         }
 | |
|         let attrs = {
 | |
|           type:  "tab",
 | |
|           title: title || url,
 | |
|           url,
 | |
|           icon:  this.getIcon(icon),
 | |
|         }
 | |
|         let tab = this.createItem(attrs);
 | |
|         list.appendChild(tab);
 | |
|       }, this);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   _generateCloudSyncTabList() {
 | |
|     let updateTabList = function(remoteTabs) {
 | |
|       let list = this._tabsList;
 | |
| 
 | |
|       for (let client of remoteTabs) {
 | |
|         let clientAttrs = {
 | |
|           type: "client",
 | |
|           clientName: client.name,
 | |
|         };
 | |
| 
 | |
|         let clientEnt = this.createItem(clientAttrs);
 | |
|         list.appendChild(clientEnt);
 | |
| 
 | |
|         for (let tab of client.tabs) {
 | |
|           let tabAttrs = {
 | |
|             type: "tab",
 | |
|             title: tab.title,
 | |
|             url: tab.url,
 | |
|             icon: this.getIcon(tab.icon),
 | |
|           };
 | |
|           let tabEnt = this.createItem(tabAttrs);
 | |
|           list.appendChild(tabEnt);
 | |
|         }
 | |
|       }
 | |
|     }.bind(this);
 | |
| 
 | |
|     return CloudSync().tabs.getRemoteTabs()
 | |
|                            .then(updateTabList, Promise.reject.bind(Promise));
 | |
|   },
 | |
| 
 | |
|   adjustContextMenu(event) {
 | |
|     let mode = "all";
 | |
|     switch (this._tabsList.selectedItems.length) {
 | |
|       case 0:
 | |
|         break;
 | |
|       case 1:
 | |
|         mode = "single"
 | |
|         break;
 | |
|       default:
 | |
|         mode = "multiple";
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     let menu = document.getElementById("tabListContext");
 | |
|     let el = menu.firstChild;
 | |
|     while (el) {
 | |
|       let showFor = el.getAttribute("showFor");
 | |
|       if (showFor) {
 | |
|         el.hidden = showFor != mode && showFor != "all";
 | |
|       }
 | |
| 
 | |
|       el = el.nextSibling;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   _refetchTabs(force) {
 | |
|     if (!force) {
 | |
|       // Don't bother refetching tabs if we already did so recently
 | |
|       let lastFetch = Services.prefs.getIntPref("services.sync.lastTabFetch", 0);
 | |
| 
 | |
|       let now = Math.floor(Date.now() / 1000);
 | |
|       if (now - lastFetch < 30) {
 | |
|         return false;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Ask Sync to just do the tabs engine if it can.
 | |
|     Weave.Service.sync(["tabs"]);
 | |
|     Services.prefs.setIntPref("services.sync.lastTabFetch",
 | |
|                               Math.floor(Date.now() / 1000));
 | |
| 
 | |
|     return true;
 | |
|   },
 | |
| 
 | |
|   observe(subject, topic, data) {
 | |
|     switch (topic) {
 | |
|       case "weave:service:login:finish":
 | |
|         // A login has finished, which means that a Sync is about to start and
 | |
|         // we will eventually get to the "tabs" engine - but try and force the
 | |
|         // tab engine to sync first by passing |true| for the forceSync param.
 | |
|         this.buildList(true);
 | |
|         break;
 | |
|       case "weave:engine:sync:finish":
 | |
|         if (data == "tabs") {
 | |
|           // The tabs engine just finished, so re-build the list without
 | |
|           // forcing a new sync of the tabs engine.
 | |
|           this.buildList(false);
 | |
|         }
 | |
|         break;
 | |
|       case "cloudsync:tabs:update":
 | |
|         this.buildList(false);
 | |
|         break;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   handleClick(event) {
 | |
|     if (event.target.getAttribute("type") != "tab") {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     if (event.button == 1) {
 | |
|       let url = event.target.getAttribute("url");
 | |
|       openUILink(url, event);
 | |
|       let index = this._tabsList.getIndexOfItem(event.target);
 | |
|       this._tabsList.removeItemAt(index);
 | |
|     }
 | |
|   }
 | |
| }
 | 
