diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js
index 77707860c..fe104d5ec 100644
--- a/www/common/cryptpad-common.js
+++ b/www/common/cryptpad-common.js
@@ -691,103 +691,6 @@ define([
LOADING_DRIVE: common.loading.onDriveEvent.fire
};
- /*
- var onMessage = function (cmd, data, cb) {
- cb = cb || function () {};
- if (queries[cmd]) {
- return void queries[cmd](data, cb);
- } else {
- console.error("Unhandled command " + cmd);
- }
- /*
- switch (cmd) {
- case 'REQUEST_LOGIN': {
- requestLogin();
- break;
- }
- case 'UPDATE_METADATA': {
- common.changeMetadata();
- break;
- }
- case 'UPDATE_TOKEN': {
- var localToken = tryParsing(localStorage.getItem(Constants.tokenKey));
- if (localToken !== data.token) { requestLogin(); }
- break;
- }
- case 'Q_FRIEND_REQUEST': {
- common.messaging.onFriendRequest.fire(data, cb);
- break;
- }
- case 'EV_FRIEND_COMPLETE': {
- common.messaging.onFriendComplete.fire(data);
- break;
- }
- // Network
- case 'NETWORK_DISCONNECT': {
- common.onNetworkDisconnect.fire(); break;
- }
- case 'NETWORK_RECONNECT': {
- common.onNetworkReconnect.fire(data); break;
- }
- // Messenger
- case 'CONTACTS_MESSAGE': {
- common.messenger.onMessageEvent.fire(data); break;
- }
- case 'CONTACTS_JOIN': {
- common.messenger.onJoinEvent.fire(data); break;
- }
- case 'CONTACTS_LEAVE': {
- common.messenger.onLeaveEvent.fire(data); break;
- }
- case 'CONTACTS_UPDATE': {
- common.messenger.onUpdateEvent.fire(data); break;
- }
- case 'CONTACTS_FRIEND': {
- common.messenger.onFriendEvent.fire(data); break;
- }
- case 'CONTACTS_UNFRIEND': {
- common.messenger.onUnfriendEvent.fire(data); break;
- }
- // Pad
- case 'PAD_READY': {
- common.padRpc.onReadyEvent.fire(); break;
- }
- case 'PAD_MESSAGE': {
- common.padRpc.onMessageEvent.fire(data); break;
- }
- case 'PAD_JOIN': {
- common.padRpc.onJoinEvent.fire(data); break;
- }
- case 'PAD_LEAVE': {
- common.padRpc.onLeaveEvent.fire(data); break;
- }
- case 'PAD_DISCONNECT': {
- common.padRpc.onDisconnectEvent.fire(data); break;
- }
- case 'PAD_ERROR': {
- common.padRpc.onErrorEvent.fire(data); break;
- }
- // Drive
- case 'DRIVE_LOG': {
- common.drive.onLog.fire(data); break;
- }
- case 'DRIVE_CHANGE': {
- common.drive.onChange.fire(data); break;
- }
- case 'DRIVE_REMOVE': {
- common.drive.onRemove.fire(data); break;
- }
- // Account deletion
- case 'DELETE_ACCOUNT': {
- common.startAccountDeletion(cb); break;
- }
- // Loading
- case 'LOADING_DRIVE': {
- common.loading.onDriveEvent.fire(data); break;
- }
- }
- };*/
-
common.ready = (function () {
var env = {};
var initialized = false;
@@ -849,98 +752,143 @@ define([
anonHash: LocalStore.getFSHash(),
localToken: tryParsing(localStorage.getItem(Constants.tokenKey)),
language: common.getLanguage(),
- messenger: rdyCfg.messenger,
- driveEvents: rdyCfg.driveEvents
+ messenger: rdyCfg.messenger, // Boolean
+ driveEvents: rdyCfg.driveEvents // Boolean
};
if (sessionStorage[Constants.newPadPathKey]) {
cfg.initialPath = sessionStorage[Constants.newPadPathKey];
delete sessionStorage[Constants.newPadPathKey];
}
- /*AStore.query("CONNECT", cfg, waitFor(function (data) {
- if (data.error) { throw new Error(data.error); }
- if (data.state === 'ALREADY_INIT') {
- data = data.returned;
- }
- if (data.anonHash && !cfg.userHash) { LocalStore.setFSHash(data.anonHash); }
-
- / *if (cfg.userHash && sessionStorage) {
- // copy User_hash into sessionStorage because cross-domain iframes
- // on safari replaces localStorage with sessionStorage or something
- sessionStorage.setItem(Constants.userHashKey, cfg.userHash);
- }* /
-
- if (cfg.userHash) {
- var localToken = tryParsing(localStorage.getItem(Constants.tokenKey));
- if (localToken === null) {
- // if that number hasn't been set to localStorage, do so.
- localStorage.setItem(Constants.tokenKey, data[Constants.tokenKey]);
- }
- }
-
- initFeedback(data.feedback);
- initialized = true;
- }));*/
+ var channelIsReady = waitFor();
var msgEv = Util.mkEvent();
- var worker = new Worker('/common/outer/webworker.js');
- worker.onmessage = function (ev) {
- msgEv.fire(ev);
- };
- var postMsg = function (data) {
- worker.postMessage(data);
- };
- Channel.create(msgEv, postMsg, waitFor(function (chan) {
- console.log('outer ready');
- Object.keys(queries).forEach(function (q) {
- chan.on(q, function (data, cb) {
- try {
- queries[q](data, cb);
- } catch (e) {
- console.error("Error in outer when executing query " + q);
- console.error(e);
- console.log(data);
+ var postMsg, worker;
+ Nthen(function (waitFor2) {
+ if (SharedWorker) {
+ worker = new SharedWorker('/common/outer/sharedworker.js');
+ console.log(worker);
+ worker.port.onmessage = function (ev) {
+ if (ev.data === "SW_READY") {
+ return;
}
+ msgEv.fire(ev);
+ };
+ postMsg = function (data) {
+ worker.port.postMessage(data);
+ };
+ postMsg('INIT');
+ } else if (false && 'serviceWorker' in navigator) {
+ var initializing = true;
+ var stopWaiting = waitFor2(); // Call this function when we're ready
+
+ postMsg = function (data) {
+ if (worker) { return void worker.postMessage(data); }
+ };
+
+ navigator.serviceWorker.register('/common/outer/serviceworker.js', {scope: '/'})
+ .then(function(reg) {
+ // Add handler for receiving messages from the service worker
+ navigator.serviceWorker.addEventListener('message', function (ev) {
+ if (initializing && ev.data === "SW_READY") {
+ initializing = false;
+ } else {
+ msgEv.fire(ev);
+ }
+ });
+
+ // Initialize the worker
+ // If it is active (probably running in another tab), just post INIT
+ if (reg.active) {
+ worker = reg.active;
+ postMsg("INIT");
+ }
+ // If it was not active, wait for the "activated" state and post INIT
+ reg.onupdatefound = function () {
+ if (initializing) {
+ var w = reg.installing;
+ var onStateChange = function () {
+ if (w.state === "activated") {
+ worker = w;
+ postMsg("INIT");
+ w.removeEventListener("statechange", onStateChange);
+ }
+ };
+ w.addEventListener('statechange', onStateChange);
+ return;
+ }
+ // XXX
+ // New version detected (from another tab): kill?
+ console.error('New version detected: ABORT?');
+ };
+ return void stopWaiting();
+ }).catch(function(error) {
+ /**/console.log('Registration failed with ' + error);
+ });
+ } else if (Worker) {
+ worker = new Worker('/common/outer/webworker.js');
+ worker.onmessage = function (ev) {
+ msgEv.fire(ev);
+ };
+ postMsg = function (data) {
+ worker.postMessage(data);
+ };
+ } else {
+ // TODO fallback no webworker?
+ console.error('NO SW OR WW');
+ }
+ }).nThen(function () {
+ Channel.create(msgEv, postMsg, function (chan) {
+ console.log('Outer ready');
+ Object.keys(queries).forEach(function (q) {
+ chan.on(q, function (data, cb) {
+ try {
+ queries[q](data, cb);
+ } catch (e) {
+ console.error("Error in outer when executing query " + q);
+ console.error(e);
+ console.log(data);
+ }
+ });
});
- });
- postMessage = function (cmd, data, cb) {
- /*setTimeout(function () {
- AStore.query(cmd, data, cb);
- });*/
- chan.query(cmd, data, function (err, data) {
- if (err) { return void cb ({error: err}); }
- cb(data);
- });
- };
+ postMessage = function (cmd, data, cb) {
+ chan.query(cmd, data, function (err, data) {
+ if (err) { return void cb ({error: err}); }
+ cb(data);
+ });
+ };
- postMessage('CONNECT', cfg, waitFor(function (data) {
- if (data.error) { throw new Error(data.error); }
- if (data.state === 'ALREADY_INIT') {
- data = data.returned;
- }
-
- if (data.anonHash && !cfg.userHash) { LocalStore.setFSHash(data.anonHash); }
-
- /*if (cfg.userHash && sessionStorage) {
- // copy User_hash into sessionStorage because cross-domain iframes
- // on safari replaces localStorage with sessionStorage or something
- sessionStorage.setItem(Constants.userHashKey, cfg.userHash);
- }*/
-
- if (cfg.userHash) {
- var localToken = tryParsing(localStorage.getItem(Constants.tokenKey));
- if (localToken === null) {
- // if that number hasn't been set to localStorage, do so.
- localStorage.setItem(Constants.tokenKey, data[Constants.tokenKey]);
+ console.log('Posting CONNECT');
+ postMessage('CONNECT', cfg, function (data) {
+ if (data.error) { throw new Error(data.error); }
+ if (data.state === 'ALREADY_INIT') {
+ data = data.returned;
}
- }
- initFeedback(data.feedback);
- initialized = true;
- }));
+ if (data.anonHash && !cfg.userHash) { LocalStore.setFSHash(data.anonHash); }
- }), false);
+ /*if (cfg.userHash && sessionStorage) {
+ // copy User_hash into sessionStorage because cross-domain iframes
+ // on safari replaces localStorage with sessionStorage or something
+ sessionStorage.setItem(Constants.userHashKey, cfg.userHash);
+ }*/
+
+ if (cfg.userHash) {
+ var localToken = tryParsing(localStorage.getItem(Constants.tokenKey));
+ if (localToken === null) {
+ // if that number hasn't been set to localStorage, do so.
+ localStorage.setItem(Constants.tokenKey, data[Constants.tokenKey]);
+ }
+ }
+
+ initFeedback(data.feedback);
+ initialized = true;
+ channelIsReady();
+ });
+
+ }, false);
+ });
}).nThen(function (waitFor) {
// Load the new pad when the hash has changed
diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js
index 664ad56af..f26eb50b4 100644
--- a/www/common/outer/async-store.js
+++ b/www/common/outer/async-store.js
@@ -24,6 +24,8 @@ define([
var Store = {};
var postMessage = function () {};
+ var broadcast = function () {};
+ var sendDriveEvent = function () {};
var storeHash;
@@ -35,10 +37,10 @@ define([
};
- Store.get = function (key, cb) {
+ Store.get = function (clientId, key, cb) {
cb(Util.find(store.proxy, key));
};
- Store.set = function (data, cb) {
+ Store.set = function (clientId, data, cb) {
var path = data.key.slice();
var key = path.pop();
var obj = Util.find(store.proxy, path);
@@ -48,6 +50,8 @@ define([
} else {
obj[key] = data.value;
}
+ console.log('broadcasting displayName');
+ broadcast([clientId], "UPDATE_METADATA");
onSync(cb);
};
@@ -131,7 +135,7 @@ define([
/////////////////////// RPC //////////////////////////////////////
//////////////////////////////////////////////////////////////////
- Store.pinPads = function (data, cb) {
+ Store.pinPads = function (clientId, data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
if (typeof(cb) !== 'function') {
console.error('expected a callback');
@@ -143,7 +147,7 @@ define([
});
};
- Store.unpinPads = function (data, cb) {
+ Store.unpinPads = function (clientId, data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
store.rpc.unpin(data, function (e, hash) {
@@ -154,7 +158,7 @@ define([
var account = {};
- Store.getPinnedUsage = function (data, cb) {
+ Store.getPinnedUsage = function (clientId, data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
store.rpc.getFileListSize(function (err, bytes) {
@@ -166,7 +170,7 @@ define([
};
// Update for all users from accounts and return current user limits
- Store.updatePinLimit = function (data, cb) {
+ Store.updatePinLimit = function (clientId, data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
store.rpc.updatePinLimits(function (e, limit, plan, note) {
if (e) { return void cb({error: e}); }
@@ -177,7 +181,7 @@ define([
});
};
// Get current user limits
- Store.getPinLimit = function (data, cb) {
+ Store.getPinLimit = function (clientId, data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
var ALWAYS_REVALIDATE = true;
@@ -195,14 +199,14 @@ define([
cb(account);
};
- Store.clearOwnedChannel = function (data, cb) {
+ Store.clearOwnedChannel = function (clientId, data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
store.rpc.clearOwnedChannel(data, function (err) {
cb({error:err});
});
};
- Store.removeOwnedChannel = function (data, cb) {
+ Store.removeOwnedChannel = function (clientId, data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
store.rpc.removeOwnedChannel(data, function (err) {
cb({error:err});
@@ -230,7 +234,7 @@ define([
});
};
- Store.uploadComplete = function (data, cb) {
+ Store.uploadComplete = function (clientId, data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
if (data.owned) {
// Owned file
@@ -247,7 +251,7 @@ define([
});
};
- Store.uploadStatus = function (data, cb) {
+ Store.uploadStatus = function (clientId, data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
store.rpc.uploadStatus(data.size, function (err, res) {
if (err) { return void cb({error:err}); }
@@ -255,7 +259,7 @@ define([
});
};
- Store.uploadCancel = function (data, cb) {
+ Store.uploadCancel = function (clientId, data, cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
store.rpc.uploadCancel(data.size, function (err, res) {
if (err) { return void cb({error:err}); }
@@ -263,7 +267,7 @@ define([
});
};
- Store.uploadChunk = function (data, cb) {
+ Store.uploadChunk = function (clientId, data, cb) {
store.rpc.send.unauthenticated('UPLOAD', data.chunk, function (e, msg) {
cb({
error: e,
@@ -272,14 +276,15 @@ define([
});
};
- Store.initRpc = function (data, cb) {
+ Store.initRpc = function (clientId, data, cb) {
+ if (store.rpc) { return void cb(account); }
require(['/common/pinpad.js'], function (Pinpad) {
Pinpad.create(store.network, store.proxy, function (e, call) {
if (e) { return void cb({error: e}); }
store.rpc = call;
- Store.getPinLimit(null, function (obj) {
+ Store.getPinLimit(null, null, function (obj) {
if (obj.error) { console.error(obj.error); }
account.limit = obj.limit;
account.plan = obj.plan;
@@ -302,7 +307,7 @@ define([
//////////////////////////////////////////////////////////////////
////////////////// ANON RPC //////////////////////////////////////
//////////////////////////////////////////////////////////////////
- Store.anonRpcMsg = function (data, cb) {
+ Store.anonRpcMsg = function (clientId, data, cb) {
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
store.anon_rpc.send(data.msg, data.data, function (err, res) {
if (err) { return void cb({error: err}); }
@@ -310,7 +315,7 @@ define([
});
};
- Store.getFileSize = function (data, cb) {
+ Store.getFileSize = function (clientId, data, cb) {
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
var channelId = Hash.hrefToHexChannelId(data.href, data.password);
@@ -324,7 +329,7 @@ define([
});
};
- Store.isNewChannel = function (data, cb) {
+ Store.isNewChannel = function (clientId, data, cb) {
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
var channelId = Hash.hrefToHexChannelId(data.href, data.password);
store.anon_rpc.send("IS_NEW_CHANNEL", channelId, function (e, response) {
@@ -339,7 +344,7 @@ define([
});
};
- Store.getMultipleFileSize = function (data, cb) {
+ Store.getMultipleFileSize = function (clientId, data, cb) {
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
if (!Array.isArray(data.files)) {
return void cb({error: 'INVALID_FILE_LIST'});
@@ -355,7 +360,7 @@ define([
});
};
- Store.getDeletedPads = function (data, cb) {
+ Store.getDeletedPads = function (clientId, data, cb) {
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
var list = getCanonicalChannelList(true);
if (!Array.isArray(list)) {
@@ -372,7 +377,8 @@ define([
});
};
- Store.initAnonRpc = function (data, cb) {
+ Store.initAnonRpc = function (clientId, data, cb) {
+ if (store.anon_rpc) { return void cb(); }
require([
'/common/rpc.js',
], function (Rpc) {
@@ -389,7 +395,7 @@ define([
//////////////////////////////////////////////////////////////////
// Get the metadata for sframe-common-outer
- Store.getMetadata = function (data, cb) {
+ Store.getMetadata = function (clientId, data, cb) {
var disableThumbnails = Util.find(store.proxy, ['settings', 'general', 'disableThumbnails']);
var metadata = {
// "user" is shared with everybody via the userlist
@@ -421,7 +427,7 @@ define([
};
};
- Store.addPad = function (data, cb) {
+ Store.addPad = function (clientId, data, cb) {
if (!data.href) { return void cb({error:'NO_HREF'}); }
var pad = makePad(data.href, data.title);
if (data.owners) { pad.owners = data.owners; }
@@ -432,6 +438,9 @@ define([
if (e) { return void cb({error: "Error while adding a template:"+ e}); }
var path = data.path || ['root'];
store.userObject.add(id, path);
+ sendDriveEvent('DRIVE_CHANGE', {
+ path: ['drive', UserObject.FILES_DATA]
+ }, clientId);
onSync(cb);
});
};
@@ -465,7 +474,7 @@ define([
ownedPads.forEach(function (c) {
var w = waitFor();
sem.take(function (give) {
- Store.removeOwnedChannel(c, give(function (obj) {
+ Store.removeOwnedChannel(null, c, give(function (obj) {
if (obj && obj.error) { console.error(obj.error); }
w();
}));
@@ -473,11 +482,11 @@ define([
});
};
- Store.deleteAccount = function (data, cb) {
+ Store.deleteAccount = function (clientId, data, cb) {
var edPublic = store.proxy.edPublic;
// No password for drive
var secret = Hash.getSecrets('drive', storeHash);
- Store.anonRpcMsg({
+ Store.anonRpcMsg(clientId, {
msg: 'GET_METADATA',
data: secret.channel
}, function (data) {
@@ -488,7 +497,7 @@ define([
nThen(function (waitFor) {
var token = Math.floor(Math.random()*Number.MAX_SAFE_INTEGER);
store.proxy[Constants.tokenKey] = token;
- postMessage("DELETE_ACCOUNT", token, waitFor());
+ postMessage(clientId, "DELETE_ACCOUNT", token, waitFor());
}).nThen(function (waitFor) {
removeOwnedPads(waitFor);
}).nThen(function (waitFor) {
@@ -498,7 +507,7 @@ define([
}));
}).nThen(function (waitFor) {
// Delete Drive
- Store.removeOwnedChannel(secret.channel, waitFor());
+ Store.removeOwnedChannel(clientId, secret.channel, waitFor());
}).nThen(function () {
store.network.disconnect();
cb({
@@ -537,7 +546,7 @@ define([
* - driveReadme
* - driveReadmeTitle
*/
- Store.createReadme = function (data, cb) {
+ Store.createReadme = function (clientId, data, cb) {
require(['/common/cryptget.js'], function (Crypt) {
var hash = Hash.createRandomHash('pad');
Crypt.put(hash, data.driveReadme, function (e) {
@@ -551,7 +560,7 @@ define([
channel: channel,
title: data.driveReadmeTitle,
};
- Store.addPad(fileData, cb);
+ Store.addPad(clientId, fileData, cb);
});
});
};
@@ -562,7 +571,7 @@ define([
* data
* - anonHash
*/
- Store.migrateAnonDrive = function (data, cb) {
+ Store.migrateAnonDrive = function (clientId, data, cb) {
require(['/common/mergeDrive.js'], function (Merge) {
var hash = data.anonHash;
Merge.anonDriveIntoUser(store, hash, cb);
@@ -573,6 +582,7 @@ define([
if (typeof attr === "string") {
console.error('DEPRECATED: use setAttribute with an array, not a string');
return {
+ path: ['settings'],
obj: store.proxy.settings,
key: attr
};
@@ -589,23 +599,28 @@ define([
obj = obj[el];
});
return {
+ path: ['settings'].concat(attr),
obj: obj,
key: attr[attr.length-1]
};
};
// Set the display name (username) in the proxy
- Store.setDisplayName = function (value, cb) {
+ Store.setDisplayName = function (clientId, value, cb) {
store.proxy[Constants.displayNameKey] = value;
+ broadcast([clientId], "UPDATE_METADATA");
onSync(cb);
};
// Reset the drive part of the userObject (from settings)
- Store.resetDrive = function (data, cb) {
+ Store.resetDrive = function (clientId, data, cb) {
nThen(function (waitFor) {
removeOwnedPads(waitFor);
}).nThen(function () {
store.proxy.drive = store.fo.getStructure();
+ sendDriveEvent('DRIVE_CHANGE', {
+ path: ['drive', 'filesData']
+ }, clientId);
onSync(cb);
});
};
@@ -617,25 +632,28 @@ define([
* - attr (Array)
* - value (String)
*/
- Store.setPadAttribute = function (data, cb) {
+ Store.setPadAttribute = function (clientId, data, cb) {
store.userObject.setPadAttribute(data.href, data.attr, data.value, function () {
+ sendDriveEvent('DRIVE_CHANGE', {
+ path: ['drive', UserObject.FILES_DATA]
+ }, clientId);
onSync(cb);
});
};
- Store.getPadAttribute = function (data, cb) {
+ Store.getPadAttribute = function (clientId, data, cb) {
store.userObject.getPadAttribute(data.href, data.attr, function (err, val) {
if (err) { return void cb({error: err}); }
cb(val);
});
};
- Store.setAttribute = function (data, cb) {
+ Store.setAttribute = function (clientId, data, cb) {
try {
var object = getAttributeObject(data.attr);
object.obj[object.key] = data.value;
} catch (e) { return void cb({error: e}); }
onSync(cb);
};
- Store.getAttribute = function (data, cb) {
+ Store.getAttribute = function (clientId, data, cb) {
var object;
try {
object = getAttributeObject(data.attr);
@@ -644,12 +662,12 @@ define([
};
// Tags
- Store.listAllTags = function (data, cb) {
+ Store.listAllTags = function (clientId, data, cb) {
cb(store.userObject.getTagsList());
};
// Templates
- Store.getTemplates = function (data, cb) {
+ Store.getTemplates = function (clientId, data, cb) {
var templateFiles = store.userObject.getFiles(['template']);
var res = [];
templateFiles.forEach(function (f) {
@@ -658,7 +676,7 @@ define([
});
cb(res);
};
- Store.incrementTemplateUse = function (href) {
+ Store.incrementTemplateUse = function (clientId, href) {
store.userObject.getPadAttribute(href, 'used', function (err, data) {
// This is a not critical function, abort in case of error to make sure we won't
// create any issue with the user object or the async store
@@ -669,12 +687,15 @@ define([
};
// Pads
- Store.moveToTrash = function (data, cb) {
+ Store.moveToTrash = function (clientId, data, cb) {
var href = Hash.getRelativeHref(data.href);
store.userObject.forget(href);
+ sendDriveEvent('DRIVE_CHANGE', {
+ path: ['drive', UserObject.FILES_DATA]
+ }, clientId);
onSync(cb);
};
- Store.setPadTitle = function (data, cb) {
+ Store.setPadTitle = function (clientId, data, cb) {
var title = data.title;
var href = data.href;
var channel = data.channel;
@@ -683,14 +704,16 @@ define([
if (AppConfig.disableAnonymousStore && !store.loggedIn) { return void cb(); }
+ var channelData = Store.channels && Store.channels[channel];
+
var owners;
- if (Store.channel && Store.channel.wc && channel === Store.channel.wc.id) {
- owners = Store.channel.data.owners || undefined;
+ if (channelData && channelData.wc && channel === channelData.wc.id) {
+ owners = channelData.data.owners || undefined;
}
var expire;
- if (Store.channel && Store.channel.wc && channel === Store.channel.wc.id) {
- expire = +Store.channel.data.expire || undefined;
+ if (channelData && channelData.wc && channel === channelData.wc.id) {
+ expire = +channelData.data.expire || undefined;
}
var allPads = Util.find(store.proxy, ['drive', 'filesData']) || {};
@@ -757,7 +780,7 @@ define([
// Add the pad if it does not exist in our drive
if (!contains) {
- Store.addPad({
+ Store.addPad(clientId, {
href: href,
channel: channel,
title: title,
@@ -767,12 +790,16 @@ define([
path: data.path
}, cb);
return;
+ } else {
+ sendDriveEvent('DRIVE_CHANGE', {
+ path: ['drive', UserObject.FILES_DATA]
+ }, clientId);
}
onSync(cb);
};
// Filepicker app
- Store.getSecureFilesList = function (query, cb) {
+ Store.getSecureFilesList = function (clientId, query, cb) {
var list = {};
var hashes = [];
var types = query.types;
@@ -801,42 +828,42 @@ define([
});
cb(list);
};
- Store.getPadData = function (id, cb) {
+ Store.getPadData = function (clientId, id, cb) {
cb(store.userObject.getFileData(id));
};
// Messaging (manage friends from the userlist)
- var getMessagingCfg = function () {
+ var getMessagingCfg = function (clientId) {
return {
proxy: store.proxy,
realtime: store.realtime,
network: store.network,
updateMetadata: function () {
- postMessage("UPDATE_METADATA");
+ postMessage(clientId, "UPDATE_METADATA");
},
- pinPads: Store.pinPads,
+ pinPads: function (data, cb) { Store.pinPads(null, data, cb); },
friendComplete: function (data) {
- postMessage("EV_FRIEND_COMPLETE", data);
+ postMessage(clientId, "EV_FRIEND_COMPLETE", data);
},
friendRequest: function (data, cb) {
- postMessage("Q_FRIEND_REQUEST", data, cb);
+ postMessage(clientId, "Q_FRIEND_REQUEST", data, cb);
},
};
};
- Store.inviteFromUserlist = function (data, cb) {
- var messagingCfg = getMessagingCfg();
+ Store.inviteFromUserlist = function (clientId, data, cb) {
+ var messagingCfg = getMessagingCfg(clientId);
Messaging.inviteFromUserlist(messagingCfg, data, cb);
};
- Store.addDirectMessageHandlers = function (data) {
- var messagingCfg = getMessagingCfg();
+ Store.addDirectMessageHandlers = function (clientId, data, cb) {
+ var messagingCfg = getMessagingCfg(clientId);
Messaging.addDirectMessageHandler(messagingCfg, data.href);
};
// Messenger
// Get hashes for the share button
- Store.getStrongerHash = function (data, cb) {
+ Store.getStrongerHash = function (clientId, data, cb) {
var allPads = Util.find(store.proxy, ['drive', 'filesData']) || {};
// If we have a stronger version in drive, add it and add a redirect button
@@ -922,49 +949,106 @@ define([
/////////////////////// PAD //////////////////////////////////////
//////////////////////////////////////////////////////////////////
- // TODO with sharedworker
- // channel will be an object storing the webchannel associated to each browser tab
- var channel = Store.channel = {
- queue: [],
- data: {}
- };
- Store.joinPad = function (data, cb) {
+ var channels = Store.channels = {};
+
+ Store.joinPad = function (clientId, data, cb) {
+ var isNew = typeof channels[data.channel] === "undefined";
+ var channel = channels[data.channel] = channels[data.channel] || {
+ queue: [],
+ data: {},
+ clients: [],
+ bcast: function (cmd, data, notMe) {
+ channel.clients.forEach(function (cId) {
+ if (cId === notMe) { return; }
+ postMessage(cId, cmd, data);
+ });
+ },
+ history: [],
+ pushHistory: function (msg, isCp) {
+ if (isCp) {
+ channel.history.push('cp|' + msg);
+ var i, cpCount = 1;
+ for (i = channel.history.length - 2; i > 0; i--) {
+ if (/^cp\|/.test(channel.history[i])) { break; }
+ }
+ channel.history = channel.history.slice(i);
+ return;
+ }
+ channel.history.push(msg);
+ }
+ };
+ if (channel.clients.indexOf(clientId) === -1) {
+ channel.clients.push(clientId);
+ }
+
+ if (!isNew && channel.wc) {
+ channel.wc.members.forEach(function (m) {
+ postMessage(clientId, "PAD_JOIN", m);
+ });
+ channel.history.forEach(function (msg) {
+ postMessage(clientId, "PAD_MESSAGE", {
+ msg: CpNfWorker.removeCp(msg),
+ user: channel.wc.myID,
+ validateKey: channel.data.validateKey
+ });
+ });
+ postMessage(clientId, "PAD_READY");
+ cb({
+ myID: channel.wc.myID,
+ id: channel.wc.id,
+ members: channel.wc.members
+ });
+
+ return;
+ }
var conf = {
onReady: function (padData) {
channel.data = padData || {};
- postMessage("PAD_READY");
- }, // post EV_PAD_READY
- onMessage: function (user, m, validateKey) {
- postMessage("PAD_MESSAGE", {
+ postMessage(clientId, "PAD_READY");
+ },
+ onMessage: function (user, m, validateKey, isCp) {
+ channel.pushHistory(m, isCp);
+ channel.bcast("PAD_MESSAGE", {
user: user,
msg: m,
validateKey: validateKey
});
- }, // post EV_PAD_MESSAGE
+ },
onJoin: function (m) {
- postMessage("PAD_JOIN", m);
- }, // post EV_PAD_JOIN
+ channel.bcast("PAD_JOIN", m);
+ },
onLeave: function (m) {
- postMessage("PAD_LEAVE", m);
- }, // post EV_PAD_LEAVE
+ channel.bcast("PAD_LEAVE", m);
+ },
onDisconnect: function () {
- postMessage("PAD_DISCONNECT");
- }, // post EV_PAD_DISCONNECT
+ channel.bcast("PAD_DISCONNECT");
+ },
onError: function (err) {
- postMessage("PAD_ERROR", err);
- }, // post EV_PAD_ERROR
+ channel.bcast("PAD_ERROR", err);
+ delete channels[data.channel]; // TODO test?
+ },
channel: data.channel,
validateKey: data.validateKey,
owners: data.owners,
password: data.password,
expire: data.expire,
network: store.network,
- readOnly: data.readOnly,
+ //readOnly: data.readOnly,
onConnect: function (wc, sendMessage) {
- channel.sendMessage = sendMessage;
+ channel.sendMessage = function (msg, cId, cb) {
+ // Send to server
+ sendMessage(msg, cb);
+ // Broadcast to other tabs
+ channel.pushHistory(CpNfWorker.removeCp(msg), /^cp\|/.test(msg));
+ channel.bcast("PAD_MESSAGE", {
+ user: wc.myID,
+ msg: CpNfWorker.removeCp(msg),
+ validateKey: channel.data.validateKey
+ }, cId);
+ };
channel.wc = wc;
channel.queue.forEach(function (data) {
- sendMessage(data.message);
+ channel.sendMessage(data.message, clientId);
});
cb({
myID: wc.myID,
@@ -975,13 +1059,23 @@ define([
};
CpNfWorker.start(conf);
};
- Store.sendPadMsg = function (data, cb) {
- if (!channel.wc) { channel.queue.push(data); }
- channel.sendMessage(data, cb);
+ Store.sendPadMsg = function (clientId, data, cb) {
+ console.log('sendPadMsg ' + data.channel);
+ var msg = data.msg;
+ var channel = channels[data.channel];
+ if (!channel) {
+ console.log('no channel...');
+ return; }
+ if (!channel.wc) {
+ console.log('no WC, push to queue');
+ channel.queue.push(msg);
+ return void cb();
+ }
+ channel.sendMessage(msg, clientId, cb);
};
// GET_FULL_HISTORY from sframe-common-outer
- Store.getFullHistory = function (data, cb) {
+ Store.getFullHistory = function (clientId, data, cb) {
var network = store.network;
var hkn = network.historyKeeper;
//var crypto = Crypto.createEncryptor(data.keys);
@@ -1016,67 +1110,178 @@ define([
network.sendto(hkn, JSON.stringify(['GET_FULL_HISTORY', data.channel, data.validateKey]));
};
- // TODO with sharedworker
- // when the tab is closed, leave the pad
-
// Drive
- Store.userObjectCommand = function (cmdData, cb) {
+ Store.userObjectCommand = function (clientId, cmdData, cb) {
if (!cmdData || !cmdData.cmd) { return; }
var data = cmdData.data;
+ var cb2 = function (data2) {
+ var paths = data.paths || [data.path] || [];
+ paths = paths.concat(data.newPath || []);
+ paths.forEach(function (p) {
+ sendDriveEvent('DRIVE_CHANGE', {
+ //path: ['drive', UserObject.FILES_DATA]
+ path: ['drive'].concat(p)
+ }, clientId);
+ });
+ cb(data2);
+ };
switch (cmdData.cmd) {
case 'move':
- store.userObject.move(data.paths, data.newPath, cb); break;
+ store.userObject.move(data.paths, data.newPath, cb2); break;
case 'restore':
- store.userObject.restore(data.path, cb); break;
+ store.userObject.restore(data.path, cb2); break;
case 'addFolder':
- store.userObject.addFolder(data.path, data.name, cb); break;
+ store.userObject.addFolder(data.path, data.name, cb2); break;
case 'delete':
- store.userObject.delete(data.paths, cb, data.nocheck, data.isOwnPadRemoved); break;
+ store.userObject.delete(data.paths, cb2, data.nocheck, data.isOwnPadRemoved); break;
case 'emptyTrash':
- store.userObject.emptyTrash(cb); break;
+ store.userObject.emptyTrash(cb2); break;
case 'rename':
- store.userObject.rename(data.path, data.newName, cb); break;
+ store.userObject.rename(data.path, data.newName, cb2); break;
default:
cb();
}
};
+ // Clients management
+ var driveEventClients = [];
+ var messengerEventClients = [];
+
+ Store._removeClient = function (clientId) {
+ var driveIdx = driveEventClients.indexOf(clientId);
+ if (driveIdx !== -1) {
+ driveEventClients.splice(driveIdx, 1);
+ }
+ var messengerIdx = messengerEventClients.indexOf(clientId);
+ if (messengerIdx !== -1) {
+ messengerEventClients.splice(messengerIdx, 1);
+ }
+ Object.keys(Store.channels).forEach(function (chanId) {
+ var chanIdx = Store.channels[chanId].clients.indexOf(clientId);
+ if (chanIdx !== -1) {
+ Store.channels[chanId].clients.splice(chanIdx, 1);
+ }
+ });
+ };
+
+ // Special events
+
+ var driveEventInit = false;
+ sendDriveEvent = function (q, data, sender) {
+ console.log('driveevent '+q);
+ console.log(JSON.stringify(driveEventClients));
+ driveEventClients.forEach(function (cId) {
+ if (cId === sender) { return; }
+ postMessage(cId, q, data);
+ });
+ };
+ Store._subscribeToDrive = function (clientId) {
+ if (driveEventClients.indexOf(clientId) === -1) {
+ driveEventClients.push(clientId);
+ }
+ if (!driveEventInit) {
+ store.proxy.on('change', [], function (o, n, p) {
+ sendDriveEvent('DRIVE_CHANGE', {
+ old: o,
+ new: n,
+ path: p
+ });
+ });
+ store.proxy.on('remove', [], function (o, p) {
+ sendDriveEvent(clientId, 'DRIVE_REMOVE', {
+ old: o,
+ path: p
+ });
+ });
+ driveEventInit = true;
+ }
+ };
+
+ var messengerEventInit = false;
+ var sendMessengerEvent = function (q, data) {
+ messengerEventClients.forEach(function (cId) {
+ postMessage(cId, q, data);
+ });
+ };
+ Store._subscribeToMessenger = function (clientId) {
+ if (messengerEventClients.indexOf(clientId) === -1) {
+ messengerEventClients.push(clientId);
+ }
+ if (!messengerEventInit) {
+ var messenger = store.messenger = Messenger.messenger(store);
+ messenger.on('message', function (message) {
+ sendMessengerEvent('CONTACTS_MESSAGE', message);
+ });
+ messenger.on('join', function (curvePublic, channel) {
+ sendMessengerEvent('CONTACTS_JOIN', {
+ curvePublic: curvePublic,
+ channel: channel,
+ });
+ });
+ messenger.on('leave', function (curvePublic, channel) {
+ sendMessengerEvent('CONTACTS_LEAVE', {
+ curvePublic: curvePublic,
+ channel: channel,
+ });
+ });
+ messenger.on('update', function (info, curvePublic) {
+ sendMessengerEvent('CONTACTS_UPDATE', {
+ curvePublic: curvePublic,
+ info: info,
+ });
+ });
+ messenger.on('friend', function (curvePublic) {
+ sendMessengerEvent('CONTACTS_FRIEND', {
+ curvePublic: curvePublic,
+ });
+ });
+ messenger.on('unfriend', function (curvePublic) {
+ sendMessengerEvent('CONTACTS_UNFRIEND', {
+ curvePublic: curvePublic,
+ });
+ });
+ messengerEventInit = true;
+ }
+ };
+
+
//////////////////////////////////////////////////////////////////
/////////////////////// Init /////////////////////////////////////
//////////////////////////////////////////////////////////////////
- var onReady = function (returned, cb) {
+ var onReady = function (clientId, returned, cb) {
var proxy = store.proxy;
var userObject = store.userObject = UserObject.init(proxy.drive, {
- pinPads: Store.pinPads,
- unpinPads: Store.unpinPads,
- removeOwnedChannel: Store.removeOwnedChannel,
+ pinPads: function (data, cb) { Store.pinPads(null, data, cb); },
+ unpinPads: function (data, cb) { Store.unpinPads(null, data, cb); },
+ removeOwnedChannel: function (data, cb) { Store.removeOwnedChannel(null, data, cb); },
edPublic: store.proxy.edPublic,
loggedIn: store.loggedIn,
log: function (msg) {
- postMessage("DRIVE_LOG", msg);
+ // broadcast to all drive apps
+ sendDriveEvent("DRIVE_LOG", msg);
}
});
nThen(function (waitFor) {
- postMessage('LOADING_DRIVE', {
+ postMessage(clientId, 'LOADING_DRIVE', {
state: 2
});
userObject.migrate(waitFor());
}).nThen(function (waitFor) {
Migrate(proxy, waitFor(), function (version, progress) {
- postMessage('LOADING_DRIVE', {
+ postMessage(clientId, 'LOADING_DRIVE', {
state: 2,
progress: progress
});
});
}).nThen(function () {
- postMessage('LOADING_DRIVE', {
+ postMessage(clientId, 'LOADING_DRIVE', {
state: 3
});
userObject.fixFiles();
var requestLogin = function () {
- postMessage("REQUEST_LOGIN");
+ broadcast([], "REQUEST_LOGIN");
};
if (store.loggedIn) {
@@ -1121,26 +1326,26 @@ define([
proxy.on('change', [Constants.displayNameKey], function (o, n) {
if (typeof(n) !== "string") { return; }
- postMessage("UPDATE_METADATA");
+ broadcast([], "UPDATE_METADATA");
});
proxy.on('change', ['profile'], function () {
// Trigger userlist update when the avatar has changed
- postMessage("UPDATE_METADATA");
+ broadcast([], "UPDATE_METADATA");
});
proxy.on('change', ['friends'], function () {
// Trigger userlist update when the friendlist has changed
- postMessage("UPDATE_METADATA");
+ broadcast([], "UPDATE_METADATA");
});
proxy.on('change', ['settings'], function () {
- postMessage("UPDATE_METADATA");
+ broadcast([], "UPDATE_METADATA");
});
proxy.on('change', [Constants.tokenKey], function () {
- postMessage("UPDATE_TOKEN", { token: proxy[Constants.tokenKey] });
+ broadcast([], "UPDATE_TOKEN", { token: proxy[Constants.tokenKey] });
});
});
};
- var connect = function (data, cb) {
+ var connect = function (clientId, data, cb) {
var hash = data.userHash || data.anonHash || Hash.createRandomHash('drive');
storeHash = hash;
if (!hash) {
@@ -1180,9 +1385,9 @@ define([
&& !drive['filesData']) {
drive[Constants.oldStorageKey] = [];
}
- postMessage('LOADING_DRIVE', { state: 1 });
+ postMessage(clientId, 'LOADING_DRIVE', { state: 1 });
// Drive already exist: return the existing drive, don't load data from legacy store
- onReady(returned, cb);
+ onReady(clientId, returned, cb);
})
.on('change', ['drive', 'migrate'], function () {
var path = arguments[2];
@@ -1190,15 +1395,15 @@ define([
if (path[0] === 'drive' && path[1] === "migrate" && value === 1) {
rt.network.disconnect();
rt.realtime.abort();
- postMessage('NETWORK_DISCONNECT');
+ broadcast([], 'NETWORK_DISCONNECT');
}
});
rt.proxy.on('disconnect', function () {
- postMessage('NETWORK_DISCONNECT');
+ broadcast([], 'NETWORK_DISCONNECT');
});
rt.proxy.on('reconnect', function (info) {
- postMessage('NETWORK_RECONNECT', {myId: info.myId});
+ broadcast([], 'NETWORK_RECONNECT', {myId: info.myId});
});
};
@@ -1213,7 +1418,7 @@ define([
* - requestLogin
*/
var initialized = false;
- Store.init = function (data, callback) {
+ Store.init = function (clientId, data, callback) {
if (initialized) {
return void callback({
state: 'ALREADY_INIT',
@@ -1221,72 +1426,21 @@ define([
});
}
initialized = true;
- postMessage = function (cmd, d, cb) {
- setTimeout(function () {
- data.query(cmd, d, cb); // TODO temporary, will be replaced by webworker channel
- });
+ postMessage = function (clientId, cmd, d, cb) {
+ data.query(clientId, cmd, d, cb);
+ };
+ broadcast = function (excludes, cmd, d, cb) {
+ data.broadcast(excludes, cmd, d, cb);
};
store.data = data;
- connect(data, function (ret) {
+ connect(clientId, data, function (ret) {
if (Object.keys(store.proxy).length === 1) {
Feedback.send("FIRST_APP_USE", true);
}
store.returned = ret;
callback(ret);
-
- // Send events whenever there is a change or a removal in the drive
- if (data.driveEvents) {
- store.proxy.on('change', [], function (o, n, p) {
- postMessage('DRIVE_CHANGE', {
- old: o,
- new: n,
- path: p
- });
- });
- store.proxy.on('remove', [], function (o, p) {
- postMessage('DRIVE_REMOVE', {
- old: o,
- path: p
- });
- });
- }
-
- if (data.messenger) {
- var messenger = store.messenger = Messenger.messenger(store);
- messenger.on('message', function (message) {
- postMessage('CONTACTS_MESSAGE', message);
- });
- messenger.on('join', function (curvePublic, channel) {
- postMessage('CONTACTS_JOIN', {
- curvePublic: curvePublic,
- channel: channel,
- });
- });
- messenger.on('leave', function (curvePublic, channel) {
- postMessage('CONTACTS_LEAVE', {
- curvePublic: curvePublic,
- channel: channel,
- });
- });
- messenger.on('update', function (info, curvePublic) {
- postMessage('CONTACTS_UPDATE', {
- curvePublic: curvePublic,
- info: info,
- });
- });
- messenger.on('friend', function (curvePublic) {
- postMessage('CONTACTS_FRIEND', {
- curvePublic: curvePublic,
- });
- });
- messenger.on('unfriend', function (curvePublic) {
- postMessage('CONTACTS_UNFRIEND', {
- curvePublic: curvePublic,
- });
- });
- }
});
};
diff --git a/www/common/outer/chainpad-netflux-worker.js b/www/common/outer/chainpad-netflux-worker.js
index be0030ceb..d34fa4a3b 100644
--- a/www/common/outer/chainpad-netflux-worker.js
+++ b/www/common/outer/chainpad-netflux-worker.js
@@ -22,6 +22,10 @@ define([], function () {
var unBencode = function (str) { return str.replace(/^\d+:/, ''); };
+ var removeCp = function (str) {
+ return str.replace(/^cp\|([A-Za-z0-9+\/=]{0,20}\|)?/, '');
+ };
+
var start = function (conf) {
var channel = conf.channel;
var validateKey = conf.validateKey;
@@ -72,7 +76,7 @@ define([], function () {
// at the beginning of each message on the server.
// We have to make sure our regex ignores this nonce using {0,20} (our IDs
// should only be 8 characters long)
- return msg.replace(/^cp\|([A-Za-z0-9+\/=]{0,20}\|)?/, '');
+ return removeCp(msg);
};
var msgOut = function (msg) {
@@ -124,6 +128,8 @@ define([], function () {
lastKnownHash = msg.slice(0,64);
+
+ var isCp = /^cp\|/.test(msg);
var message = msgIn(peer, msg);
verbose(message);
@@ -134,7 +140,7 @@ define([], function () {
message = unBencode(message);//.slice(message.indexOf(':[') + 1);
// pass the message into Chainpad
- onMessage(peer, message, validateKey);
+ onMessage(peer, message, validateKey, isCp);
//sframeChan.query('Q_RT_MESSAGE', message, function () { });
};
@@ -260,7 +266,8 @@ define([], function () {
};
return {
- start: start
+ start: start,
+ removeCp: removeCp
/*function (config) {
config.sframeChan.whenReg('EV_RT_READY', function () {
start(config);
diff --git a/www/common/outer/serviceworker.js b/www/common/outer/serviceworker.js
new file mode 100644
index 000000000..d7660c311
--- /dev/null
+++ b/www/common/outer/serviceworker.js
@@ -0,0 +1,167 @@
+/* jshint ignore:start */
+importScripts('/bower_components/requirejs/require.js');
+require.config({
+ // fix up locations so that relative urls work.
+ baseUrl: '/',
+ paths: {
+ // jquery declares itself as literally "jquery" so it cannot be pulled by path :(
+ "jquery": "/bower_components/jquery/dist/jquery.min",
+ // json.sortify same
+ "json.sortify": "/bower_components/json.sortify/dist/JSON.sortify",
+ cm: '/bower_components/codemirror'
+ },
+ map: {
+ '*': {
+ 'css': '/bower_components/require-css/css.js',
+ 'less': '/common/RequireLess.js',
+ }
+ }
+});
+
+window = self;
+localStorage = {
+ setItem: function (k, v) { localStorage[k] = v; },
+ getItem: function (k) { return localStorage[k]; }
+};
+
+var postMsg = function (client, data) {
+ client.postMessage(data);
+};
+
+
+var debug = function (msg) { console.log(msg); };
+// debug = function () {};
+
+var init = function (client, cb) {
+ debug('SW INIT');
+
+ require([
+ '/common/common-util.js',
+ '/common/outer/worker-channel.js',
+ '/common/outer/store-rpc.js'
+ ], function (Util, Channel, Rpc) {
+ debug('SW Required ressources loaded');
+ var msgEv = Util.mkEvent();
+
+ var postToClient = function (data) {
+ postMsg(client, data);
+ };
+ Channel.create(msgEv, postToClient, function (chan) {
+ debug('SW Channel created');
+
+ var clientId = client.id;
+ self.tabs[clientId].chan = chan;
+ Object.keys(Rpc.queries).forEach(function (q) {
+ if (q === 'CONNECT') { return; }
+ if (q === 'JOIN_PAD') { return; }
+ if (q === 'SEND_PAD_MSG') { return; }
+ chan.on(q, function (data, cb) {
+ try {
+ Rpc.queries[q](clientId, data, cb);
+ } catch (e) {
+ console.error('Error in webworker when executing query ' + q);
+ console.error(e);
+ console.log(data);
+ }
+ });
+ });
+ chan.on('CONNECT', function (cfg, cb) {
+ debug('SW Connect callback');
+ if (self.store) {
+ if (cfg.driveEvents) {
+ Rpc._subscribeToDrive(clientId);
+ }
+ if (cfg.messenger) {
+ Rpc._subscribeToMessenger(clientId);
+ }
+ return void cb(self.store);
+ }
+
+ debug('Loading new async store');
+ // One-time initialization (init async-store)
+ cfg.query = function (cId, cmd, data, cb) {
+ cb = cb || function () {};
+ self.tabs[cId].chan.query(cmd, data, function (err, data2) {
+ if (err) { return void cb({error: err}); }
+ cb(data2);
+ });
+ };
+ cfg.broadcast = function (excludes, cmd, data, cb) {
+ cb = cb || function () {};
+ Object.keys(self.tabs).forEach(function (cId) {
+ if (excludes.indexOf(cId) !== -1) { return; }
+ self.tabs[cId].chan.query(cmd, data, function (err, data2) {
+ if (err) { return void cb({error: err}); }
+ cb(data2);
+ });
+ });
+ };
+ Rpc.queries['CONNECT'](clientId, cfg, function (data) {
+ if (cfg.driveEvents) {
+ Rpc._subscribeToDrive(clientId);
+ }
+ if (cfg.messenger) {
+ Rpc._subscribeToMessenger(clientId);
+ }
+ if (data && data.state === "ALREADY_INIT") {
+ return void cb(data.returned);
+ }
+ self.store = data;
+ cb(data);
+ });
+ });
+ chan.on('JOIN_PAD', function (data, cb) {
+ self.tabs[clientId].channelId = data.channel;
+ try {
+ Rpc.queries['JOIN_PAD'](clientId, data, cb);
+ } catch (e) {
+ console.error('Error in webworker when executing query JOIN_PAD');
+ console.error(e);
+ console.log(data);
+ }
+ });
+ chan.on('SEND_PAD_MSG', function (msg, cb) {
+ var data = {
+ msg: msg,
+ channel: self.tabs[clientId].channelId
+ };
+ try {
+ Rpc.queries['SEND_PAD_MSG'](clientId, data, cb);
+ } catch (e) {
+ console.error('Error in webworker when executing query SEND_PAD_MSG');
+ console.error(e);
+ console.log(data);
+ }
+ });
+ cb();
+ }, true);
+
+ self.tabs[client.id].msgEv = msgEv;
+ });
+};
+
+self.tabs = {};
+self.addEventListener('message', function (e) {
+ var cId = e.source.id;
+ if (e.data === "INIT") {
+ if (tabs[cId]) { return; }
+ tabs[cId] = {
+ client: e.source
+ };
+ init(e.source, function () {
+ postMsg(e.source, 'SW_READY');
+ });
+ } else if (self.tabs[cId] && self.tabs[cId].msgEv) {
+ self.tabs[cId].msgEv.fire(e);
+ }
+});
+self.addEventListener('install', function (e) {
+ debug('V1 installing…');
+ self.skipWaiting();
+});
+
+self.addEventListener('activate', function (e) {
+ debug('V1 now ready to handle fetches!');
+});
+
+
diff --git a/www/common/outer/sharedworker.js b/www/common/outer/sharedworker.js
new file mode 100644
index 000000000..9d5aa1187
--- /dev/null
+++ b/www/common/outer/sharedworker.js
@@ -0,0 +1,204 @@
+console.log('SW!');
+/* jshint ignore:start */
+importScripts('/bower_components/requirejs/require.js');
+require.config({
+ // fix up locations so that relative urls work.
+ baseUrl: '/',
+ paths: {
+ // jquery declares itself as literally "jquery" so it cannot be pulled by path :(
+ "jquery": "/bower_components/jquery/dist/jquery.min",
+ // json.sortify same
+ "json.sortify": "/bower_components/json.sortify/dist/JSON.sortify",
+ cm: '/bower_components/codemirror'
+ },
+ map: {
+ '*': {
+ 'css': '/bower_components/require-css/css.js',
+ 'less': '/common/RequireLess.js',
+ }
+ }
+});
+
+window = self;
+localStorage = {
+ setItem: function (k, v) { localStorage[k] = v; },
+ getItem: function (k) { return localStorage[k]; }
+};
+
+self.tabs = {};
+var findTab = function (port) {
+ var tab;
+ Object.keys(self.tabs).some(function (id) {
+ if (self.tabs[id].port === port) {
+ tab = port;
+ return true;
+ }
+ });
+ return tab;
+};
+
+var postMsg = function (client, data) {
+ client.port.postMessage(data);
+};
+
+var debug = function (msg) { console.log(msg); };
+// debug = function () {};
+
+var init = function (client, cb) {
+ debug('SharedW INIT');
+
+ require([
+ '/common/common-util.js',
+ '/common/outer/worker-channel.js',
+ '/common/outer/store-rpc.js'
+ ], function (Util, Channel, Rpc) {
+ debug('SharedW Required ressources loaded');
+ var msgEv = Util.mkEvent();
+
+ var postToClient = function (data) {
+ postMsg(client, data);
+ };
+ Channel.create(msgEv, postToClient, function (chan) {
+ debug('SharedW Channel created');
+
+ var clientId = client.id;
+ client.chan = chan;
+ Object.keys(Rpc.queries).forEach(function (q) {
+ if (q === 'CONNECT') { return; }
+ if (q === 'JOIN_PAD') { return; }
+ if (q === 'SEND_PAD_MSG') { return; }
+ chan.on(q, function (data, cb) {
+ try {
+ Rpc.queries[q](clientId, data, cb);
+ } catch (e) {
+ console.error('Error in webworker when executing query ' + q);
+ console.error(e);
+ console.log(data);
+ }
+ });
+ });
+ chan.on('CONNECT', function (cfg, cb) {
+ debug('SharedW connecting to store...');
+ if (self.store) {
+ debug('Store already exists!');
+ if (cfg.driveEvents) {
+ Rpc._subscribeToDrive(clientId);
+ }
+ if (cfg.messenger) {
+ Rpc._subscribeToMessenger(clientId);
+ }
+ return void cb(self.store);
+ }
+
+ debug('Loading new async store');
+ // One-time initialization (init async-store)
+ cfg.query = function (cId, cmd, data, cb) {
+ cb = cb || function () {};
+ self.tabs[cId].chan.query(cmd, data, function (err, data2) {
+ if (err) { return void cb({error: err}); }
+ cb(data2);
+ });
+ };
+ cfg.broadcast = function (excludes, cmd, data, cb) {
+ cb = cb || function () {};
+ Object.keys(self.tabs).forEach(function (cId) {
+ if (excludes.indexOf(cId) !== -1) { return; }
+ self.tabs[cId].chan.query(cmd, data, function (err, data2) {
+ if (err) { return void cb({error: err}); }
+ cb(data2);
+ });
+ });
+ };
+ Rpc.queries['CONNECT'](clientId, cfg, function (data) {
+ if (cfg.driveEvents) {
+ Rpc._subscribeToDrive(clientId);
+ }
+ if (cfg.messenger) {
+ Rpc._subscribeToMessenger(clientId);
+ }
+ if (data && data.state === "ALREADY_INIT") {
+ self.store = data.returned;
+ return void cb(data.returned);
+ }
+ self.store = data;
+ cb(data);
+ });
+ });
+ chan.on('JOIN_PAD', function (data, cb) {
+ client.channelId = data.channel;
+ try {
+ Rpc.queries['JOIN_PAD'](clientId, data, cb);
+ } catch (e) {
+ console.error('Error in webworker when executing query JOIN_PAD');
+ console.error(e);
+ console.log(data);
+ }
+ });
+ chan.on('SEND_PAD_MSG', function (msg, cb) {
+ var data = {
+ msg: msg,
+ channel: client.channelId
+ };
+ try {
+ Rpc.queries['SEND_PAD_MSG'](clientId, data, cb);
+ } catch (e) {
+ console.error('Error in webworker when executing query SEND_PAD_MSG');
+ console.error(e);
+ console.log(data);
+ }
+ });
+ cb();
+ }, true);
+
+ client.msgEv = msgEv;
+ });
+};
+
+onconnect = function(e) {
+ debug('New ShardWorker client');
+ var port = e.ports[0];
+ var cId = Number(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER))
+ var client = self.tabs[cId] = {
+ id: cId,
+ port: port
+ };
+
+ port.onmessage = function (e) {
+ if (e.data === "INIT") {
+ if (client.init) { return; }
+ client.init = true;
+ init(client, function () {
+ postMsg(client, 'SW_READY');
+ });
+ } else if (client && client.msgEv) {
+ client.msgEv.fire(e);
+ }
+ };
+};
+
+self.tabs = {};
+self.addEventListener('message', function (e) {
+ var cId = e.source.id;
+ if (e.data === "INIT") {
+ if (tabs[cId]) { return; }
+ tabs[cId] = {
+ client: e.source
+ };
+ init(e.source, function () {
+ postMsg(e.source, 'SW_READY');
+ });
+ } else if (self.tabs[cId] && self.tabs[cId].msgEv) {
+ self.tabs[cId].msgEv.fire(e);
+ }
+});
+self.addEventListener('install', function (e) {
+ debug('V1 installing…');
+ self.skipWaiting();
+});
+
+self.addEventListener('activate', function (e) {
+ debug('V1 now ready to handle fetches!');
+});
+
+
+
diff --git a/www/common/outer/store-rpc.js b/www/common/outer/store-rpc.js
index 94a6eee85..f44f8d1e2 100644
--- a/www/common/outer/store-rpc.js
+++ b/www/common/outer/store-rpc.js
@@ -73,195 +73,17 @@ define([
Rpc.query = function (cmd, data, cb) {
if (queries[cmd]) {
- queries[cmd](data, cb);
+ queries[cmd]('0', data, cb);
} else {
console.error('UNHANDLED_STORE_RPC');
}
- /*
- switch (cmd) {
- // READY
- case 'CONNECT': {
- Store.init(data, cb); break;
- }
- case 'DISCONNECT': {
- Store.disconnect(data, cb); break;
- }
- case 'CREATE_README': {
- Store.createReadme(data, cb); break;
- }
- case 'MIGRATE_ANON_DRIVE': {
- Store.migrateAnonDrive(data, cb); break;
- }
- // RPC
- case 'INIT_RPC': {
- Store.initRpc(data, cb); break;
- }
- case 'UPDATE_PIN_LIMIT': {
- Store.updatePinLimit(data, cb); break;
- }
- case 'GET_PIN_LIMIT': {
- Store.getPinLimit(data, cb); break;
- }
- case 'CLEAR_OWNED_CHANNEL': {
- Store.clearOwnedChannel(data, cb); break;
- }
- case 'REMOVE_OWNED_CHANNEL': {
- Store.removeOwnedChannel(data, cb); break;
- }
- case 'UPLOAD_CHUNK': {
- Store.uploadChunk(data, cb); break;
- }
- case 'UPLOAD_COMPLETE': {
- Store.uploadComplete(data, cb); break;
- }
- case 'UPLOAD_STATUS': {
- Store.uploadStatus(data, cb); break;
- }
- case 'UPLOAD_CANCEL': {
- Store.uploadCancel(data, cb); break;
- }
- case 'PIN_PADS': {
- Store.pinPads(data, cb); break;
- }
- case 'UNPIN_PADS': {
- Store.unpinPads(data, cb); break;
- }
- case 'GET_DELETED_PADS': {
- Store.getDeletedPads(data, cb); break;
- }
- case 'GET_PINNED_USAGE': {
- Store.getPinnedUsage(data, cb); break;
- }
- // ANON RPC
- case 'INIT_ANON_RPC': {
- Store.initAnonRpc(data, cb); break;
- }
- case 'ANON_RPC_MESSAGE': {
- Store.anonRpcMsg(data, cb); break;
- }
- case 'GET_FILE_SIZE': {
- Store.getFileSize(data, cb); break;
- }
- case 'GET_MULTIPLE_FILE_SIZE': {
- Store.getMultipleFileSize(data, cb); break;
- }
- // Store
- case 'GET': {
- Store.get(data, cb); break;
- }
- case 'SET': {
- Store.set(data, cb); break;
- }
- case 'ADD_PAD': {
- Store.addPad(data, cb); break;
- }
- case 'SET_PAD_TITLE': {
- Store.setPadTitle(data, cb); break;
- }
- case 'MOVE_TO_TRASH': {
- Store.moveToTrash(data, cb); break;
- }
- case 'RESET_DRIVE': {
- Store.resetDrive(data, cb); break;
- }
- case 'GET_METADATA': {
- Store.getMetadata(data, cb); break;
- }
- case 'SET_DISPLAY_NAME': {
- Store.setDisplayName(data, cb); break;
- }
- case 'SET_PAD_ATTRIBUTE': {
- Store.setPadAttribute(data, cb); break;
- }
- case 'GET_PAD_ATTRIBUTE': {
- Store.getPadAttribute(data, cb); break;
- }
- case 'SET_ATTRIBUTE': {
- Store.setAttribute(data, cb); break;
- }
- case 'GET_ATTRIBUTE': {
- Store.getAttribute(data, cb); break;
- }
- case 'LIST_ALL_TAGS': {
- Store.listAllTags(data, cb); break;
- }
- case 'GET_TEMPLATES': {
- Store.getTemplates(data, cb); break;
- }
- case 'GET_SECURE_FILES_LIST': {
- Store.getSecureFilesList(data, cb); break;
- }
- case 'GET_PAD_DATA': {
- Store.getPadData(data, cb); break;
- }
- case 'GET_STRONGER_HASH': {
- Store.getStrongerHash(data, cb); break;
- }
- case 'INCREMENT_TEMPLATE_USE': {
- Store.incrementTemplateUse(data); break;
- }
- // Messaging
- case 'INVITE_FROM_USERLIST': {
- Store.inviteFromUserlist(data, cb); break;
- }
- // Messenger
- case 'CONTACTS_GET_FRIEND_LIST': {
- Store.messenger.getFriendList(data, cb); break;
- }
- case 'CONTACTS_GET_MY_INFO': {
- Store.messenger.getMyInfo(data, cb); break;
- }
- case 'CONTACTS_GET_FRIEND_INFO': {
- Store.messenger.getFriendInfo(data, cb); break;
- }
- case 'CONTACTS_REMOVE_FRIEND': {
- Store.messenger.removeFriend(data, cb); break;
- }
- case 'CONTACTS_OPEN_FRIEND_CHANNEL': {
- Store.messenger.openFriendChannel(data, cb); break;
- }
- case 'CONTACTS_GET_FRIEND_STATUS': {
- Store.messenger.getFriendStatus(data, cb); break;
- }
- case 'CONTACTS_GET_MORE_HISTORY': {
- Store.messenger.getMoreHistory(data, cb); break;
- }
- case 'CONTACTS_SEND_MESSAGE': {
- Store.messenger.sendMessage(data, cb); break;
- }
- case 'CONTACTS_SET_CHANNEL_HEAD': {
- Store.messenger.setChannelHead(data, cb); break;
- }
- // Pad
- case 'SEND_PAD_MSG': {
- Store.sendPadMsg(data, cb); break;
- }
- case 'JOIN_PAD': {
- Store.joinPad(data, cb); break;
- }
- case 'GET_FULL_HISTORY': {
- Store.getFullHistory(data, cb); break;
- }
- // Drive
- case 'DRIVE_USEROBJECT': {
- Store.userObjectCommand(data, cb); break;
- }
- // Settings
- case 'DELETE_ACCOUNT': {
- Store.deleteAccount(data, cb); break;
- }
- case 'IS_NEW_CHANNEL': {
- Store.isNewChannel(data, cb); break;
- }
- default: {
- console.error("UNHANDLED_STORE_RPC");
-
- break;
- }
- }*/
-
};
+ // Internal calls
+ Rpc._removeClient = Store._removeClient;
+ Rpc._subscribeToDrive = Store._subscribeToDrive;
+ Rpc._subscribeToMessenger = Store._subscribeToMessenger;
+
return Rpc;
});
diff --git a/www/common/outer/webworker.js b/www/common/outer/webworker.js
index 81cd2fa9f..d8ed326c3 100644
--- a/www/common/outer/webworker.js
+++ b/www/common/outer/webworker.js
@@ -32,11 +32,14 @@ require([
Channel.create(msgEv, postMessage, function (chan) {
console.log('ww ready');
+ var clientId = '1';
Object.keys(Rpc.queries).forEach(function (q) {
if (q === 'CONNECT') { return; }
+ if (q === 'JOIN_PAD') { return; }
+ if (q === 'SEND_PAD_MSG') { return; }
chan.on(q, function (data, cb) {
try {
- Rpc.queries[q](data, cb);
+ Rpc.queries[q](clientId, data, cb);
} catch (e) {
console.error('Error in webworker when executing query ' + q);
console.error(e);
@@ -47,13 +50,59 @@ require([
chan.on('CONNECT', function (cfg, cb) {
console.log('onConnect');
// load Store here, with cfg, and pass a "query" (chan.query)
- cfg.query = function (cmd, data, cb) {
- chan.query(cmd, data, function (err, data) {
+ // cId is a clientId used in ServiceWorker or SharedWorker
+ cfg.query = function (cId, cmd, data, cb) {
+ cb = cb || function () {};
+ chan.query(cmd, data, function (err, data2) {
if (err) { return void cb({error: err}); }
- cb(data);
+ cb(data2);
});
};
- Rpc.queries['CONNECT'](cfg, cb);
+ cfg.broadcast = function (excludes, cmd, data, cb) {
+ cb = cb || function () {};
+ if (excludes.indexOf(clientId) !== -1) { return; }
+ chan.query(cmd, data, function (err, data2) {
+ if (err) { return void cb({error: err}); }
+ cb(data2);
+ });
+ };
+ Rpc.queries['CONNECT'](clientId, cfg, function (data) {
+ console.log(data);
+ if (data && data.state === "ALREADY_INIT") {
+ return void cb(data);
+ }
+ if (cfg.driveEvents) {
+ Rpc._subscribeToDrive(clientId);
+ }
+ if (cfg.messenger) {
+ Rpc._subscribeToMessenger(clientId);
+ }
+ cb(data);
+ });
+ });
+ var chanId;
+ chan.on('JOIN_PAD', function (data, cb) {
+ chanId = data.channel;
+ try {
+ Rpc.queries['JOIN_PAD'](clientId, data, cb);
+ } catch (e) {
+ console.error('Error in webworker when executing query JOIN_PAD');
+ console.error(e);
+ console.log(data);
+ }
+ });
+ chan.on('SEND_PAD_MSG', function (msg, cb) {
+ var data = {
+ msg: msg,
+ channel: chanId
+ };
+ try {
+ Rpc.queries['SEND_PAD_MSG'](clientId, data, cb);
+ } catch (e) {
+ console.error('Error in webworker when executing query SEND_PAD_MSG');
+ console.error(e);
+ console.log(data);
+ }
});
}, true);
diff --git a/www/common/outer/worker-channel.js b/www/common/outer/worker-channel.js
index c93fc6308..a2129bd69 100644
--- a/www/common/outer/worker-channel.js
+++ b/www/common/outer/worker-channel.js
@@ -24,7 +24,7 @@ define([
var txid = mkTxid();
var timeout = setTimeout(function () {
delete queries[txid];
- console.log("Timeout making query " + q);
+ //console.log("Timeout making query " + q);
}, 30000);
queries[txid] = function (data, msg) {
clearTimeout(timeout);
diff --git a/www/worker/inner.js b/www/worker/inner.js
index 6625960d5..20992b023 100644
--- a/www/worker/inner.js
+++ b/www/worker/inner.js
@@ -71,12 +71,19 @@ define([
// Service worker
if ('serviceWorker' in navigator) {
- var postMessage = function (data) {
- if (navigator.serviceWorker && navigator.serviceWorker.controller) {
- navigator.serviceWorker.controller.postMessage(data);
- }
- };
console.log('here');
+ var initializing = true;
+ var worker;
+ var postMessage = function (data) {
+ console.log(data, navigator.serviceWorker);
+ if (worker) {
+ return void worker.postMessage(data);
+ }
+ console.log('NOT READY');
+ /*if (navigator.serviceWorker && navigator.serviceWorker.controller) {
+ navigator.serviceWorker.controller.postMessage(data);
+ }*/
+ };
navigator.serviceWorker.register('/worker/sw.js', {scope: '/'})
.then(function(reg) {
console.log(reg);
@@ -84,6 +91,19 @@ define([
$container.append('
');
$container.append('Registered! (scope: ' + reg.scope +')');
reg.onupdatefound = function () {
+ if (initializing) {
+ var w = reg.installing;
+ var onStateChange = function () {
+ if (w.state === "activated") {
+ console.log(w);
+ worker = w;
+ postMessage("INIT");
+ w.removeEventListener("statechange", onStateChange);
+ }
+ };
+ w.addEventListener('statechange', onStateChange);
+ return;
+ }
console.log('new SW version found!');
// KILL EVERYTHING
UI.confirm("New version detected, you have to reload", function (yes) {
@@ -94,6 +114,7 @@ define([
navigator.serviceWorker.addEventListener('message', function (e) {
var data = e.data;
if (data && data.state === "READY") {
+ initializing = false;
$container.append('