Bug 571897 (Sync UI) - Part 4: about:sync-tabs [r=dolske]

This commit is contained in:
Paul O’Shannessy 2010-08-02 16:37:56 -07:00
Родитель e2207241b5
Коммит f0fa08ac5d
13 изменённых файлов: 532 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,80 @@
<?xml version="1.0"?>
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Weave.
#
# The Initial Developer of the Original Code is the Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2009
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Edward Lee <edilee@mozilla.com>
# Mike Connor <mconnor@mozilla.com>
# Paul OShannessy <paul@oshannessy.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
<bindings id="tabBindings"
xmlns="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:xbl="http://www.mozilla.org/xbl">
<binding id="tab-listing" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
<content>
<xul:hbox flex="1">
<xul:vbox pack="start">
<xul:image class="tabIcon"
xbl:inherits="src=icon"/>
</xul:vbox>
<xul:vbox pack="start" flex="1">
<xul:label xbl:inherits="value=title,selected"
crop="end" flex="1" class="title"/>
<xul:label xbl:inherits="value=url,selected"
crop="end" flex="1" class="url"/>
</xul:vbox>
</xul:hbox>
</content>
<handlers>
<handler event="dblclick" button="0">
<![CDATA[
RemoteTabViewer.openSelected();
]]>
</handler>
</handlers>
</binding>
<binding id="client-listing" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
<content>
<xul:hbox pack="start" align="center" onfocus="event.target.blur()" onselect="return false;">
<xul:image/>
<xul:label xbl:inherits="value=clientName"
class="clientName"
crop="center" flex="1"/>
</xul:hbox>
</content>
</binding>
</bindings>

Просмотреть файл

@ -0,0 +1,7 @@
richlistitem[type="tab"] {
-moz-binding: url(chrome://browser/content/aboutSyncTabs-bindings.xml#tab-listing);
}
richlistitem[type="client"] {
-moz-binding: url(chrome://browser/content/aboutSyncTabs-bindings.xml#client-listing);
}

Просмотреть файл

@ -0,0 +1,272 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Weave.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Edward Lee <edilee@mozilla.com>
* Mike Connor <mconnor@mozilla.com>
* Paul OShannessy <paul@oshannessy.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
const Cu = Components.utils;
Cu.import("resource://services-sync/service.js");
Cu.import("resource:///modules/PlacesUIUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
let RemoteTabViewer = {
_tabsList: null,
init: function () {
Services.obs.addObserver(this, "weave:service:login:finish", false);
Services.obs.addObserver(this, "weave:engine:sync:finish", false);
this._tabsList = document.getElementById("tabsList");
this.buildList(true);
},
uninit: function () {
Services.obs.removeObserver(this, "weave:service:login:finish");
Services.obs.removeObserver(this, "weave:engine:sync:finish");
},
buildList: function(force) {
if (!Weave.Service.isLoggedIn || !this._refetchTabs(force))
return;
//XXXzpao We should say something about not being logged in & not having data
// or tell the appropriate condition. (bug 583344)
this._generateTabList();
},
createItem: function(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: function(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().indexOf(val) == -1 &&
item.getAttribute("title").toLowerCase().indexOf(val) == -1)
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: function() {
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: function() {
let item = this._tabsList.selectedItems[0];
let uri = Weave.Utils.makeURI(item.getAttribute("url"));
let title = item.getAttribute("title");
PlacesUIUtils.showMinimalAddBookmarkUI(uri, title);
},
bookmarkSelectedTabs: function() {
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.showMinimalAddMultiBookmarkUI(URIs);
},
_generateTabList: function() {
let engine = Weave.Engines.get("tabs");
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);
}
for (let [guid, client] in Iterator(engine.getAllClients())) {
// Create the client node, but don't add it in-case we don't show any tabs
let appendClient = true;
let seenURLs = {};
client.tabs.forEach(function({title, urlHistory, icon}) {
let url = urlHistory[0];
if (engine.locallyOpenTabMatchesURL(url) || url in seenURLs)
return;
seenURLs[url] = null;
if (appendClient) {
let attrs = {
type: "client",
clientName: client.clientName,
class: Weave.Clients.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: url,
icon: Weave.Utils.getIcon(icon)
}
let tab = this.createItem(attrs);
list.appendChild(tab);
}, this);
}
},
adjustContextMenu: function(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: function(force) {
if (!force) {
// Don't bother refetching tabs if we already did so recently
let lastFetch = 0;
try {
lastFetch = Services.prefs.getIntPref("services.sync.lastTabFetch");
}
catch (e) { /* Just use the default value of 0 */ }
let now = Math.floor(Date.now() / 1000);
if (now - lastFetch < 30)
return false;
}
// if Clients hasn't synced yet this session, need to sync it as well
if (Weave.Clients.lastSync == 0)
Weave.Clients.sync();
// Force a sync only for the tabs engine
let engine = Weave.Engines.get("tabs");
engine.lastModified = null;
engine.sync();
Services.prefs.setIntPref("services.sync.lastTabFetch",
Math.floor(Date.now() / 1000));
return true;
},
observe: function(subject, topic, data) {
switch (topic) {
case "weave:service:login:finish":
this.buildList(true);
break;
case "weave:engine:sync:finish":
if (subject == "tabs")
this._generateTabList();
break;
}
},
handleClick: function(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);
}
}
}

Просмотреть файл

@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Weave.
#
# The Initial Developer of the Original Code is the Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2009
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Edward Lee <edilee@mozilla.com>
# Mike Connor <mconnor@mozilla.com>
# Paul OShannessy <paul@oshannessy.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/aboutSyncTabs.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/aboutSyncTabs.css" type="text/css"?>
<!DOCTYPE window [
<!ENTITY % aboutSyncTabsDTD SYSTEM "chrome://browser/locale/aboutSyncTabs.dtd">
%aboutSyncTabsDTD;
]>
<window id="tabs-display"
onload="RemoteTabViewer.init()"
onunload="RemoteTabViewer.uninit()"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
title="&tabs.otherComputers.label;">
<script type="application/javascript;version=1.8" src="chrome://browser/content/aboutSyncTabs.js"/>
<script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
<html:head>
<html:link rel="icon" href="chrome://browser/skin/sync-16.png"/>
</html:head>
<popupset id="contextmenus">
<popup id="tabListContext">
<menuitem label="&tabs.context.openTab.label;"
accesskey="&tabs.context.openTab.accesskey;"
oncommand="RemoteTabViewer.openSelected()"
showFor="single"/>
<menuitem label="&tabs.context.bookmarkSingleTab.label;"
accesskey="&tabs.context.bookmarkSingleTab.accesskey;"
oncommand="RemoteTabViewer.bookmarkSingleTab(event)"
showFor="single"/>
<menuitem label="&tabs.context.openMultipleTabs.label;"
accesskey="&tabs.context.openMultipleTabs.accesskey;"
oncommand="RemoteTabViewer.openSelected()"
showFor="multiple"/>
<menuitem label="&tabs.context.bookmarkMultipleTabs.label;"
accesskey="&tabs.context.bookmarkMultipleTabs.accesskey;"
oncommand="RemoteTabViewer.bookmarkSelectedTabs()"
showFor="multiple"/>
<menuseparator/>
<menuitem label="&tabs.context.refreshList.label;"
accesskey="&tabs.context.refreshList.accesskey;"
oncommand="RemoteTabViewer.buildList()"
showFor="all"/>
</popup>
</popupset>
<richlistbox context="tabListContext" id="tabsList" seltype="multiple"
align="center" flex="1"
onclick="RemoteTabViewer.handleClick(event)"
oncontextmenu="RemoteTabViewer.adjustContextMenu(event)">
<hbox id="headers" align="center">
<label id="tabsListHeading"
value="&tabs.otherComputers.label;"/>
<spacer flex="1"/>
<textbox type="search"
emptytext="&tabs.searchText.label;"
oncommand="RemoteTabViewer.filterTabs(event)"/>
</hbox>
</richlistbox>
</window>

Просмотреть файл

@ -501,6 +501,12 @@
<menuseparator id="endHistorySeparator"
class="hide-if-empty-places-result"
builder="end"/>
#ifdef MOZ_SERVICES_SYNC
<menuitem id="sync-tabs-menuitem"
label="&syncTabsMenu.label;"
oncommand="BrowserOpenSyncTabs();"
disabled="true"/>
#endif
<menu id="historyUndoMenu"
label="&historyUndoMenu.label;"
disabled="true">

Просмотреть файл

@ -736,6 +736,28 @@ HistoryMenu.prototype = {
"for (var i = 0; i < " + undoItems.length + "; i++) undoCloseWindow();");
},
toggleTabsFromOtherComputers: function PHM_toggleTabsFromOtherComputers() {
// This is a no-op if MOZ_SERVICES_SYNC isn't defined
#ifdef MOZ_SERVICES_SYNC
// enable/disable the Tabs From Other Computers menu
let menuitem = document.getElementById("sync-tabs-menuitem");
// If Sync isn't configured yet, then don't show the menuitem.
if (Weave.Status.service == Weave.CLIENT_NOT_CONFIGURED ||
Weave.Svc.Prefs.get("firstSync", "") == "notReady") {
menuitem.setAttribute("hidden", true);
return;
}
// The tabs engine might never be inited (if services.sync.registerEngines
// is modified), so make sure we avoid undefined errors.
let enabled = Weave.Service.isLoggedIn && Weave.Engines.get("tabs") &&
Weave.Engines.get("tabs").enabled;
menuitem.setAttribute("disabled", !enabled);
menuitem.setAttribute("hidden", false);
#endif
},
_onPopupShowing: function HM__onPopupShowing(aEvent) {
PlacesMenu.prototype._onPopupShowing.apply(this, arguments);
@ -745,6 +767,7 @@ HistoryMenu.prototype = {
this.toggleRecentlyClosedTabs();
this.toggleRecentlyClosedWindows();
this.toggleTabsFromOtherComputers();
},
_onCommand: function HM__onCommand(aEvent) {

Просмотреть файл

@ -6723,6 +6723,12 @@ function isTabEmpty(aTab) {
!aTab.hasAttribute("busy");
}
#ifdef MOZ_SERVICES_SYNC
function BrowserOpenSyncTabs() {
switchToTabHavingURI("about:sync-tabs", true);
}
#endif
/**
* Format a URL
* eg:

Просмотреть файл

@ -52,6 +52,12 @@ browser.jar:
* content/browser/web-panels.xul (content/web-panels.xul)
* content/browser/baseMenuOverlay.xul (content/baseMenuOverlay.xul)
* content/browser/nsContextMenu.js (content/nsContextMenu.js)
#ifdef MOZ_SERVICES_SYNC
* content/browser/aboutSyncTabs.xul (content/aboutSyncTabs.xul)
content/browser/aboutSyncTabs.js (content/aboutSyncTabs.js)
content/browser/aboutSyncTabs.css (content/aboutSyncTabs.css)
* content/browser/aboutSyncTabs-bindings.xml (content/aboutSyncTabs-bindings.xml)
#endif
# XXX: We should exclude this one as well (bug 71895)
* content/browser/hiddenWindow.xul (content/hiddenWindow.xul)
#ifdef XP_MACOSX

Просмотреть файл

@ -96,6 +96,10 @@ static RedirEntry kRedirMap[] = {
nsIAboutModule::ALLOW_SCRIPT },
{ "sessionrestore", "chrome://browser/content/aboutSessionRestore.xhtml",
nsIAboutModule::ALLOW_SCRIPT },
#ifdef MOZ_SERVICES_SYNC
{ "sync-tabs", "chrome://browser/content/aboutSyncTabs.xul",
nsIAboutModule::ALLOW_SCRIPT },
#endif
};
static const int kRedirTotal = NS_ARRAY_LENGTH(kRedirMap);

Просмотреть файл

@ -202,6 +202,9 @@ static const mozilla::Module::ContractIDEntry kBrowserContracts[] = {
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "rights", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "robots", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "sessionrestore", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
#ifdef MOZ_SERVICES_SYNC
{ NS_ABOUT_MODULE_CONTRACTID_PREFIX "sync-tabs", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
#endif
#ifndef WINCE
{ NS_PROFILEMIGRATOR_CONTRACTID, &kNS_FIREFOX_PROFILEMIGRATOR_CID },
#if defined(XP_WIN) && !defined(__MINGW32__)

Просмотреть файл

@ -0,0 +1,17 @@
<!-- LOCALIZATION NOTE (tabs.otherComputers.label): Keep this in sync with syncTabsMenu.label from browser.dtd -->
<!ENTITY tabs.otherComputers.label "Tabs From Other Computers">
<!ENTITY tabs.searchText.label "Type here to find tabs…">
<!-- LOCALIZATION NOTE (tabs.context.openTab.accesskey, tabs.context.openMultipleTabs.accesskey):
Only one of these will show at a time (based on selection), so reusing accesskey is ok. -->
<!ENTITY tabs.context.openTab.label "Open This Tab">
<!ENTITY tabs.context.openTab.accesskey "O">
<!ENTITY tabs.context.openMultipleTabs.label "Open Selected Tabs">
<!ENTITY tabs.context.openMultipleTabs.accesskey "O">
<!ENTITY tabs.context.bookmarkSingleTab.label "Bookmark This Tab…">
<!ENTITY tabs.context.bookmarkSingleTab.accesskey "B">
<!ENTITY tabs.context.bookmarkMultipleTabs.label "Bookmark Selected Tabs…">
<!ENTITY tabs.context.bookmarkMultipleTabs.accesskey "B">
<!ENTITY tabs.context.refreshList.label "Refresh List">
<!ENTITY tabs.context.refreshList.accesskey "R">

Просмотреть файл

@ -509,3 +509,6 @@ just addresses the organization to follow, e.g. "This site is run by " -->
<!-- Name for the tabs toolbar as spoken by screen readers.
The word "toolbar" is appended automatically and should not be contained below! -->
<!ENTITY tabsToolbar.label "Browser tabs">
<!-- LOCALIZATION NOTE (syncTabsMenu.label): This appears in the history menu -->
<!ENTITY syncTabsMenu.label "Tabs From Other Computers">

Просмотреть файл

@ -7,6 +7,9 @@
locale/browser/aboutPrivateBrowsing.dtd (%chrome/browser/aboutPrivateBrowsing.dtd)
locale/browser/aboutRobots.dtd (%chrome/browser/aboutRobots.dtd)
locale/browser/aboutSessionRestore.dtd (%chrome/browser/aboutSessionRestore.dtd)
#ifdef MOZ_SERVICES_SYNC
locale/browser/aboutSyncTabs.dtd (%chrome/browser/aboutSyncTabs.dtd)
#endif
locale/browser/credits.dtd (%chrome/browser/credits.dtd)
* locale/browser/browser.dtd (%chrome/browser/browser.dtd)
locale/browser/baseMenuOverlay.dtd (%chrome/browser/baseMenuOverlay.dtd)