Bug 571898 - port 1.4 Sync UI for Fennec to mobile-browser [r=mbrubeck]

This commit is contained in:
Mark Finkle 2010-06-29 10:49:36 -04:00
parent ab821a6554
commit 78be1ede63
17 changed files with 483 additions and 1 deletions

View File

@ -474,3 +474,8 @@ pref("font.default.x-western", "SwissA");
// See bug 545869 for details on why these are set the way they are
pref("network.buffer.cache.count", 24);
pref("network.buffer.cache.size", 16384);
// sync service
pref("services.sync.client.type", "mobile");
pref("services.sync.registerEngines", "Tab,Bookmarks,Form,History,Password");
pref("services.sync.autoconnectDelay", 5);

View File

@ -0,0 +1,174 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
<!ENTITY % remoteTabsDTD SYSTEM "chrome://browser/locale/aboutTabs.dtd" >
%remoteTabsDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&remoteTabs.title;</title>
<link rel="icon" type="image/png" href="chrome://branding/content/favicon32.png" />
<link rel="stylesheet" href="chrome://browser/skin/aboutTabs.css" type="text/css"/>
</head>
<body onload="RemoteTabViewer.show();">
<div id="tabList">
</div>
<script type="application/javascript;version=1.8"><![CDATA[
// Make sure this is the only instance of the page
Components.utils.import("resource://services-sync/service.js");
Weave.Utils.ensureOneOpen(window);
const Cc = Components.classes;
const Ci = Components.interfaces;
let RemoteTabViewer = {
get chromeWin() {
let chromeWin = window.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIWebNavigation).QueryInterface(Ci.nsIDocShellTreeItem).
rootTreeItem.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIDOMWindow).QueryInterface(Ci.nsIDOMChromeWindow);
delete this.chromeWin;
return this.chromeWin = chromeWin;
},
show: function RemoteTabViewer_show() {
// Don't do anything if the tabs engine isn't ready
if (!Weave.Engines.get("tabs"))
return;
this._maybeNotify();
this._populateTabs();
this._refetchTabs();
},
_maybeNotify: function _maybeNotify() {
// Don't notify if the tab engine has new tabs or the user dismissed it
let prefs = Weave.Svc.Prefs;
if (prefs.get("notifyTabState") == 0)
return;
// No need to reshow the notification if it's still open
let notifyBox = this.chromeWin.getNotificationBox(window);
if (notifyBox.getNotificationWithValue("remote-tabs") != null)
return;
let message = Weave.Str.sync.get("remote.notification.label");
let notification = notifyBox.appendNotification(message, "remote-tabs", "",
notifyBox.PRIORITY_INFO_LOW);
// Wrap the close function to find out if the user clicks the X
let close = notification.close;
notification.close = function() {
// Once the user dismisses the dialog, remember that and don't show again
prefs.set("notifyTabState", 0);
close.apply(notification, arguments);
};
},
_refetchTabs: function _refetchTabs() {
// Don't bother refetching tabs if we already did so recently
let lastFetch = Weave.Svc.Prefs.get("lastTabFetch", 0);
let now = Math.floor(Date.now() / 1000);
if (now - lastFetch < 30)
return;
// Asynchronously fetch the tabs
setTimeout(function() {
let engine = Weave.Engines.get("tabs");
let lastSync = engine.lastSync;
// Force a sync only for the tabs engine
engine.lastModified = null;
engine.sync();
Weave.Svc.Prefs.set("lastTabFetch", now);
// Only reload the page if something synced
if (engine.lastSync != lastSync)
location.reload();
}, 0);
},
_populateTabs: function _populateTabs() {
// Clear out all child elements from holder first, so we don't
// end up adding duplicate rows.
let engine = Weave.Engines.get("tabs");
let holder = document.getElementById("tabList");
if (holder.hasChildNodes()) {
while (holder.childNodes.length >= 1)
holder.removeChild(holder.firstChild);
}
// Generate the list of tabs
let haveTabs = false;
for (let [guid, client] in Iterator(engine.getAllClients())) {
haveTabs = true;
// Create the client node, but don't add it in-case we don't show any tabs
let appendClient = true;
let nameNode = document.createElement("h2");
nameNode.textContent = client.clientName;
client.tabs.forEach(function({title, urlHistory, icon}) {
let pageUrl = urlHistory[0];
// Skip tabs that are already open
if (engine.locallyOpenTabMatchesURL(pageUrl))
return;
if (title == "")
title = pageUrl;
let item = document.createElement("div");
item.addEventListener("click", function() {
item.setAttribute("selected", true);
RemoteTabViewer.chromeWin.BrowserUI.newTab(pageUrl);
}, false)
item.setAttribute("class", "tab");
let img = document.createElement("img");
img.setAttribute("class", "icon");
img.src = Weave.Utils.getIcon(icon, "chrome://browser/skin/images/tab.png");
let tabDiv = document.createElement("div");
tabDiv.setAttribute("class", "info");
let titleNode = document.createElement("div");
titleNode.setAttribute("class", "title");
titleNode.textContent = title;
let urlNode = document.createElement("div");
urlNode.setAttribute("class", "url");
urlNode.textContent = pageUrl;
tabDiv.appendChild(titleNode);
tabDiv.appendChild(urlNode);
item.appendChild(img);
item.appendChild(tabDiv);
// Append the client name if we haven't yet
if (appendClient) {
appendClient = false;
holder.appendChild(nameNode);
}
holder.appendChild(item);
});
}
if (holder.childNodes.length == 0) {
// Assume we're pending, but we might already have tabs or have synced
let text = Weave.Str.sync.get("remote.pending.label");
if (haveTabs)
text = Weave.Str.sync.get("remote.opened.label");
else if (engine.lastSync != 0)
text = Weave.Str.sync.get("remote.missing.label");
let item = document.createElement("h1");
item.textContent = text;
document.getElementsByTagName('body')[0].appendChild(item);
}
}
};
]]></script>
</body>
</html>

View File

@ -86,6 +86,7 @@
<script type="application/javascript" src="chrome://browser/content/TileManager.js"/>
<script type="application/javascript" src="chrome://browser/content/BrowserView.js"/>
<script type="application/javascript" src="chrome://browser/content/AnimatedZoom.js"/>
<script type="application/javascript" src="chrome://browser/content/sync.js"/>
<stringbundleset id="stringbundleset">
<stringbundle id="bundle_browser" src="chrome://browser/locale/browser.properties"/>
@ -113,6 +114,7 @@
<!-- tabs -->
<command id="cmd_newTab" label="&newtab.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_closeTab" label="&closetab.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
<command id="cmd_remoteTabs" oncommand="WeaveGlue.openRemoteTabs();"/>
<!-- bookmarking -->
<command id="cmd_star" label="&star.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
@ -202,6 +204,7 @@
<vbox id="tabs" onselect="BrowserUI.selectTab(this);" onclosetab="BrowserUI.closeTab(this)" flex="1"/>
<hbox id="tabs-controls">
<toolbarbutton id="newtab-button" class="button-image" command="cmd_newTab"/>
<toolbarbutton id="remotetabs-button" class="button-image" disabled="true" command="cmd_remoteTabs"/>
</hbox>
</vbox>
</vbox>
@ -431,6 +434,21 @@
<button id="prefs-clear-data" label="&clearPrivateData.button;" command="cmd_sanitize"/>
</setting>
</settings>
<settings id="prefs-sync" label="&sync.title;">
<setting id="sync-user" type="string" title="&sync.username;" />
<setting id="sync-pass" type="string" inputtype="password" title="&sync.password;" />
<setting id="sync-secret" type="string" inputtype="password" title="&sync.secretPhrase;" />
<setting id="sync-device" type="string" title="&sync.deviceName;" onchange="WeaveGlue.changeName(this)"/>
<setting id="sync-connect" type="control">
<button label="&sync.connect;" oncommand="WeaveGlue.connect();" />
</setting>
<setting id="sync-disconnect" type="control">
<button label="&sync.disconnect;" oncommand="WeaveGlue.disconnect();" />
</setting>
<setting id="sync-sync" type="control">
<button id="sync-syncButton" label="&sync.syncNow;" oncommand="WeaveGlue.sync();" />
</setting>
</settings>
</richlistbox>
</notificationbox>
</vbox>

View File

@ -0,0 +1,192 @@
/* ***** 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 Bookmarks sync code.
*
* The Initial Developer of the Original Code is Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Jono DiCarlo <jdicarlo@mozilla.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 ***** */
let WeaveGlue = {
init: function init() {
Components.utils.import("resource://services-sync/service.js");
this._addListeners();
// Generating keypairs is expensive on mobile, so disable it
Weave.Service.keyGenEnabled = false;
},
openRemoteTabs: function openRemoteTabs() {
this._openTab("about:sync-tabs");
},
connect: function connect() {
Weave.Service.login(this._settings.user.value, this._settings.pass.value,
this._settings.secret.value);
Weave.Service.persistLogin();
},
disconnect: function disconnect() {
Weave.Service.logout();
},
sync: function sync() {
Weave.Service.sync();
},
_addListeners: function _addListeners() {
let topics = ["weave:service:sync:start", "weave:service:sync:finish",
"weave:service:sync:error", "weave:service:login:start",
"weave:service:login:finish", "weave:service:login:error",
"weave:service:logout:finish"];
// For each topic, add or remove _updateOptions as the observer
let addRem = function(add) topics.forEach(function(topic) Weave.Svc.
Obs[add ? "add" : "remove"](topic, WeaveGlue._updateOptions, WeaveGlue));
// Add the listeners now, and remove them on unload
addRem(true);
addEventListener("unload", function() addRem(false), false);
},
_openTab: function _openTab(url) {
setTimeout(function() BrowserUI.newTab(url), 0);
},
get _settings() {
// Do a quick test to see if the options exist yet
let syncButton = document.getElementById("sync-syncButton");
if (syncButton == null)
return;
// Get all the setting nodes from the add-ons display
let settings = {};
let ids = ["user", "pass", "secret", "device", "connect", "disconnect", "sync"];
ids.forEach(function(id) {
settings[id] = document.getElementById("sync-" + id);
});
// Replace the getter with the collection of settings
delete this._settings;
return this._settings = settings;
},
_updateOptions: function _updateOptions() {
let loggedIn = Weave.Service.isLoggedIn;
document.getElementById("remotetabs-button").disabled = !loggedIn;
// Make sure we're online when connecting/syncing
Util.forceOnline();
// Can't do anything before settings are loaded
if (this._settings == null)
return;
// Make some aliases
let user = this._settings.user;
let pass = this._settings.pass;
let secret = this._settings.secret;
let connect = this._settings.connect;
let device = this._settings.device;
let disconnect = this._settings.disconnect;
let sync = this._settings.sync;
let syncStr = Weave.Str.sync;
// Make sure the options are in the right state
user.collapsed = loggedIn;
pass.collapsed = loggedIn;
secret.collapsed = loggedIn;
connect.collapsed = loggedIn;
device.collapsed = !loggedIn;
disconnect.collapsed = !loggedIn;
sync.collapsed = !loggedIn;
// Check the lock on a timeout because it's set just after notifying
setTimeout(Weave.Utils.bind2(this, function() {
// Prevent certain actions when the service is locked
if (Weave.Service.locked) {
connect.firstChild.disabled = true;
sync.firstChild.disabled = true;
connect.setAttribute("title", syncStr.get("connecting.label"));
sync.setAttribute("title", syncStr.get("lastSyncInProgress.label"));
}
else {
connect.firstChild.disabled = false;
sync.firstChild.disabled = false;
connect.setAttribute("title", syncStr.get("disconnected.label"));
}
}), 0);
// Move the disconnect and sync settings out to make connect the last item
let parent = connect.parentNode;
if (!loggedIn)
parent = parent.parentNode;
parent.appendChild(disconnect);
parent.appendChild(sync);
// Dynamically generate some strings
let connectedStr = syncStr.get("connected.label", [Weave.Service.username]);
disconnect.setAttribute("title", connectedStr);
// Show the day-of-week and time (HH:MM) of last sync
let lastSync = Weave.Svc.Prefs.get("lastSync");
if (lastSync != null) {
let syncDate = new Date(lastSync).toLocaleFormat("%a %R");
let dateStr = syncStr.get("lastSync.label", [syncDate]);
sync.setAttribute("title", dateStr);
}
// Show what went wrong with login if necessary
let login = Weave.Status.login;
if (login == Weave.LOGIN_SUCCEEDED)
connect.removeAttribute("desc");
else if (login != null)
connect.setAttribute("desc", Weave.Str.errors.get(login));
// Load the values for the string inputs
user.value = Weave.Service.username || "";
pass.value = Weave.Service.password || "";
secret.value = Weave.Service.passphrase || "";
device.value = Weave.Clients.localName;
},
changeName: function changeName(input) {
// Make sure to update to a modified name, e.g., empty-string -> default
Weave.Clients.localName = input.value;
input.value = Weave.Clients.localName;
}
};
// Wait a little bit before loading a bunch of Weave files from service
addEventListener("UIReady", function ready() setTimeout(function() {
removeEventListener("UIReady", ready, false);
setTimeout(function() WeaveGlue.init(), 5000);
}, 0), false);

View File

@ -16,6 +16,7 @@ chrome.jar:
content/aboutCertError.xhtml (content/aboutCertError.xhtml)
content/aboutCertError.css (content/aboutCertError.css)
content/aboutHome.xhtml (content/aboutHome.xhtml)
content/aboutTabs.xhtml (content/aboutTabs.xhtml)
content/languages.properties (content/languages.properties)
* content/browser.xul (content/browser.xul)
* content/browser.js (content/browser.js)
@ -58,5 +59,6 @@ chrome.jar:
content/prompt/select.xul (content/prompt/select.xul)
content/prompt/prompt.js (content/prompt/prompt.js)
content/AnimatedZoom.js (content/AnimatedZoom.js)
content/sync.js (content/sync.js)
% override chrome://global/content/config.xul chrome://browser/content/config.xul

View File

@ -67,6 +67,10 @@ let modules = {
home: {
uri: "chrome://browser/content/aboutHome.xhtml",
privileged: true
},
"sync-tabs": {
uri: "chrome://browser/content/aboutTabs.xhtml",
privileged: true
}
}
@ -153,7 +157,16 @@ AboutHome.prototype = {
classID: Components.ID("{b071364f-ab68-4669-a9db-33fca168271a}")
}
function AboutSyncTabs() {}
AboutSyncTabs.prototype = {
__proto__: AboutGeneric.prototype,
classDescription: "About Sync Tabs",
contractID: "@mozilla.org/network/protocol/about;1?what=sync-tabs",
classID: Components.ID("{d503134a-f6f3-4824-bc3c-09c123177944}")
}
function NSGetModule(compMgr, fileSpec)
XPCOMUtils.generateModule([AboutFirstrun, AboutFennec, AboutRights,
AboutCertError, AboutFirefox, AboutHome]);
AboutCertError, AboutFirefox, AboutHome,
AboutSyncTabs]);

View File

@ -52,6 +52,7 @@ esac
MOZ_XUL_APP=1
MOZ_ENABLE_LIBXUL=1
MOZ_SERVICES_SYNC=1
MOZ_NO_XPCOM_OBSOLETE=1
if test "$LIBXUL_SDK"; then
MOZ_XULRUNNER=1

View File

@ -0,0 +1 @@
<!ENTITY remoteTabs.title "Tabs from my other computers">

View File

@ -18,3 +18,11 @@
<!ENTITY homepage.none "Blank Page">
<!ENTITY homepage.default "&brandShortName; Start">
<!ENTITY homepage.currentpage "Use Current Page">
<!ENTITY sync.title "Sync">
<!ENTITY sync.username "Username">
<!ENTITY sync.password "Password">
<!ENTITY sync.secretPhrase "Secret Phrase">
<!ENTITY sync.deviceName "Device Name">
<!ENTITY sync.connect "Connect">
<!ENTITY sync.disconnect "Disconnect">
<!ENTITY sync.syncNow "Sync Now">

View File

@ -5,6 +5,7 @@
locale/@AB_CD@/browser/about.dtd (%chrome/about.dtd)
locale/@AB_CD@/browser/aboutCertError.dtd (%chrome/aboutCertError.dtd)
locale/@AB_CD@/browser/aboutHome.dtd (%chrome/aboutHome.dtd)
locale/@AB_CD@/browser/aboutTabs.dtd (%chrome/aboutTabs.dtd)
locale/@AB_CD@/browser/browser.dtd (%chrome/browser.dtd)
locale/@AB_CD@/browser/browser.properties (%chrome/browser.properties)
locale/@AB_CD@/browser/config.dtd (%chrome/config.dtd)

View File

@ -0,0 +1,50 @@
body {
color: black;
font-family: "Nokia Sans", Tahoma, Arial, Helvetica, sans-serif;
margin: 0;
padding: 0;
}
h1 {
font-size: 24px;
text-align: center;
}
h2 {
background-color: lightgray;
font-size: 24px;
font-weight: bold;
margin: 0;
padding: 8px;
}
.tab {
border-bottom: solid 1px rgb(207, 207, 207);
min-height: 70px;
padding-right: 32px;
position: relative;
}
.tab[selected] {
background-color: #8db8d8;
}
.icon {
height: 32px;
left: 16px;
position: absolute;
top: 4px;
width: 32px;
}
.info {
margin-left: 32px;
overflow: hidden;
padding-left: 32px;
white-space: nowrap;
}
.title {
font-size: 24px;
margin-top: 4px;
}
.url {
color: blue;
font-size: 18px;
margin-top: 4px;
}

View File

@ -799,6 +799,18 @@ box[type="documenttab"]:only-child .documenttab-close {
list-style-image: url("images/newtab-active-64.png");
}
#remotetabs-button {
list-style-image: url("images/remotetabs-default-64.png");
}
#remotetabs-button:not([disabled]):hover:active {
list-style-image: url("images/remotetabs-active-64.png");
}
#remotetabs-button[disabled] {
list-style-image: url("images/remotetabs-disabled-64.png");
}
/* bookmark editor ------------------------------------------------------- */
#bookmark-container {
padding: 8px; /* core spacing */

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 B

View File

@ -6,6 +6,7 @@ chrome.jar:
aboutPage.css (aboutPage.css)
about.css (about.css)
aboutHome.css (aboutHome.css)
aboutTabs.css (aboutTabs.css)
config.css (config.css)
firstRun.css (firstRun.css)
header.css (header.css)
@ -67,7 +68,11 @@ chrome.jar:
images/console-active-64.png (images/console-active-64.png)
images/newtab-default-64.png (images/newtab-default-64.png)
images/newtab-active-64.png (images/newtab-active-64.png)
images/remotetabs-default-64.png (images/remotetabs-default-64.png)
images/remotetabs-active-64.png (images/remotetabs-active-64.png)
images/remotetabs-disabled-64.png (images/remotetabs-disabled-64.png)
images/remotetabs-32.png (images/remotetabs-32.png)
images/tab-32.png (images/tab-32.png)
images/mozilla-32.png (images/mozilla-32.png)
images/leftcap-default-64.png (images/leftcap-default-64.png)
images/leftcap-active-64.png (images/leftcap-active-64.png)