Merge branch 'serviceworker' into staging

pull/1/head
yflory 7 years ago
commit 4896fe81fe

@ -89,7 +89,7 @@ define([
};
/* Used to accept friend requests within apps other than /contacts/ */
Msg.addDirectMessageHandler = function (cfg) {
Msg.addDirectMessageHandler = function (cfg, href) {
var network = cfg.network;
var proxy = cfg.proxy;
if (!network) { return void console.error('Network not ready'); }
@ -97,13 +97,12 @@ define([
var msg;
if (sender === network.historyKeeper) { return; }
try {
var parsed = Hash.parsePadUrl(window.location.href);
var parsed = Hash.parsePadUrl(href);
var secret = Hash.getSecrets(parsed.type, parsed.hash);
if (!parsed.hashData) { return; }
var chan = Hash.hrefToHexChannelId(window.location.href);
var chan = secret.channel;
// Decrypt
var keyStr = parsed.hashData.key;
var cryptor = Crypto.createEditCryptor(keyStr);
var key = cryptor.cryptKey;
var key = secret.keys ? secret.keys.cryptKey : Hash.decodeBase64(secret.key);
var decryptMsg;
try {
decryptMsg = Crypto.decrypt(message, key);
@ -197,15 +196,14 @@ define([
var network = cfg.network;
var netfluxId = data.netfluxId;
var parsed = Hash.parsePadUrl(data.href);
var secret = Hash.getSecrets(parsed.type, parsed.hash);
if (!parsed.hashData) { return; }
// Message
var chan = Hash.hrefToHexChannelId(data.href);
var chan = secret.channel;
var myData = createData(cfg.proxy);
var msg = ["FRIEND_REQ", chan, myData];
// Encryption
var keyStr = parsed.hashData.key;
var cryptor = Crypto.createEditCryptor(keyStr);
var key = cryptor.cryptKey;
var key = secret.keys ? secret.keys.cryptKey : Hash.decodeBase64(secret.key);
var msgStr = Crypto.encrypt(JSON.stringify(msg), key);
// Send encrypted message
if (pendingRequests.indexOf(netfluxId) === -1) {

@ -7,25 +7,27 @@ define([
'/common/common-constants.js',
'/common/common-feedback.js',
'/common/outer/local-store.js',
'/common/outer/store-rpc.js',
'/common/outer/worker-channel.js',
'/customize/application_config.js',
'/bower_components/nthen/index.js',
], function (Config, Messages, Util, Hash,
Messaging, Constants, Feedback, LocalStore, AStore,
Messaging, Constants, Feedback, LocalStore, Channel,
AppConfig, Nthen) {
/* This file exposes functionality which is specific to Cryptpad, but not to
any particular pad type. This includes functions for committing metadata
about pads to your local storage for future use and improved usability.
Additionally, there is some basic functionality for import/export.
*/
var postMessage = function (cmd, data, cb) {
setTimeout(function () {
var urlArgs = Util.find(Config, ['requireConf', 'urlArgs']) || '';
var postMessage = function (/*cmd, data, cb*/) {
/*setTimeout(function () {
AStore.query(cmd, data, cb);
});
});*/
console.error('NOT_READY');
};
var tryParsing = function (x) {
try { return JSON.parse(x); }
@ -531,6 +533,11 @@ define([
var messaging = common.messaging = {};
messaging.onFriendRequest = Util.mkEvent();
messaging.onFriendComplete = Util.mkEvent();
messaging.addHandlers = function (href) {
postMessage("ADD_DIRECT_MESSAGE_HANDLERS", {
href: href
});
};
// Messenger
var messenger = common.messenger = {};
@ -570,8 +577,8 @@ define([
// Pad RPC
var pad = common.padRpc = {};
pad.joinPad = function (data, cb) {
postMessage("JOIN_PAD", data, cb);
pad.joinPad = function (data) {
postMessage("JOIN_PAD", data);
};
pad.sendPadMsg = function (data, cb) {
postMessage("SEND_PAD_MSG", data, cb);
@ -581,6 +588,7 @@ define([
pad.onJoinEvent = Util.mkEvent();
pad.onLeaveEvent = Util.mkEvent();
pad.onDisconnectEvent = Util.mkEvent();
pad.onConnectEvent = Util.mkEvent();
pad.onErrorEvent = Util.mkEvent();
// Loading events
@ -669,108 +677,57 @@ define([
window.location.href = '/login/';
};
common.startAccountDeletion = function (cb) {
common.startAccountDeletion = function (data, cb) {
// Logout other tabs
LocalStore.logout(null, true);
cb();
};
var onMessage = function (cmd, data, cb) {
cb = cb || function () {};
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': {
require(['/api/config?' + (+new Date())], function (NewConfig) {
var update = updateLocalVersion(NewConfig.requireConf && NewConfig.requireConf.urlArgs);
if (update) {
postMessage('DISCONNECT');
return;
}
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;
}
}
var queries = {
REQUEST_LOGIN: requestLogin,
UPDATE_METADATA: common.changeMetadata,
UPDATE_TOKEN: function (data) {
var localToken = tryParsing(localStorage.getItem(Constants.tokenKey));
if (localToken !== data.token) { requestLogin(); }
},
// Messaging
Q_FRIEND_REQUEST: common.messaging.onFriendRequest.fire,
EV_FIREND_COMPLETE: common.messaging.onFriendComplete.fire,
// Network
NETWORK_DISCONNECT: common.onNetworkDisconnect.fire,
NETWORK_RECONNECT: function (data) {
require(['/api/config?' + (+new Date())], function (NewConfig) {
var update = updateLocalVersion(NewConfig.requireConf && NewConfig.requireConf.urlArgs);
if (update) {
postMessage('DISCONNECT');
return;
}
common.onNetworkReconnect.fire(data);
});
},
// Messenger
CONTACTS_MESSAGE: common.messenger.onMessageEvent.fire,
CONTACTS_JOIN: common.messenger.onJoinEvent.fire,
CONTACTS_LEAVE: common.messenger.onLeaveEvent.fire,
CONTACTS_UPDATE: common.messenger.onUpdateEvent.fire,
CONTACTS_FRIEND: common.messenger.onFriendEvent.fire,
CONTACTS_UNFRIEND: common.messenger.onUnfriendEvent.fire,
// Pad
PAD_READY: common.padRpc.onReadyEvent.fire,
PAD_MESSAGE: common.padRpc.onMessageEvent.fire,
PAD_JOIN: common.padRpc.onJoinEvent.fire,
PAD_LEAVE: common.padRpc.onLeaveEvent.fire,
PAD_DISCONNECT: common.padRpc.onDisconnectEvent.fire,
PAD_CONNECT: common.padRpc.onConnectEvent.fire,
PAD_ERROR: common.padRpc.onErrorEvent.fire,
// Drive
DRIVE_LOG: common.drive.onLog.fire,
DRIVE_CHANGE: common.drive.onChange.fire,
DRIVE_REMOVE: common.drive.onRemove.fire,
// Account deletion
DELETE_ACCOUNT: common.startAccountDeletion,
// Loading
LOADING_DRIVE: common.loading.onDriveEvent.fire
};
common.ready = (function () {
@ -828,43 +785,166 @@ define([
}
}).nThen(function (waitFor) {
var cfg = {
query: onMessage, // TODO temporary, will be replaced by a webworker channel
init: true,
//query: onMessage, // TODO temporary, will be replaced by a webworker channel
userHash: LocalStore.getUserHash(),
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]);
}
var channelIsReady = waitFor();
var msgEv = Util.mkEvent();
var postMsg, worker;
Nthen(function (waitFor2) {
if (typeof(SharedWorker) !== "undefined") {
worker = new SharedWorker('/common/outer/sharedworker.js?' + urlArgs);
worker.onerror = function (e) {
console.error(e);
};
worker.port.onmessage = function (ev) {
if (ev.data === "SW_READY") {
return;
}
msgEv.fire(ev);
};
postMsg = function (data) {
worker.port.postMessage(data);
};
postMsg('INIT');
window.addEventListener('beforeunload', function () {
postMsg('CLOSE');
});
} 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?' + urlArgs, {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);
});
window.addEventListener('beforeunload', function () {
postMsg('CLOSE');
});
} else if (Worker) {
worker = new Worker('/common/outer/webworker.js?' + urlArgs);
worker.onmessage = function (ev) {
msgEv.fire(ev);
};
postMsg = function (data) {
worker.postMessage(data);
};
} else {
require(['/common/outer/noworker.js'], waitFor2(function (NoWorker) {
NoWorker.onMessage(function (data) {
msgEv.fire({data: data});
});
postMsg = function (d) { setTimeout(function () { NoWorker.query(d); }); };
NoWorker.create();
}));
}
}).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) {
cb = cb || function () {};
chan.query(cmd, data, function (err, data) {
if (err) { return void cb ({error: err}); }
cb(data);
});
};
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;
}
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;
channelIsReady();
});
}, false);
});
initFeedback(data.feedback);
initialized = true;
}));
}).nThen(function (waitFor) {
// Load the new pad when the hash has changed
var oldHref = document.location.href;
@ -893,9 +973,12 @@ define([
document.location.reload();
} else if (o && !n) {
LocalStore.logout();
postMessage("DISCONNECT");
}
});
LocalStore.onLogout(function () {
console.log('onLogout: disconnect');
postMessage("DISCONNECT");
});
if (PINNING_ENABLED && LocalStore.isLoggedIn()) {
console.log("logged in. pads will be pinned");

File diff suppressed because it is too large Load Diff

@ -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 () { });
};
@ -259,7 +265,8 @@ define([], function () {
};
return {
start: start
start: start,
removeCp: removeCp
/*function (config) {
config.sframeChan.whenReg('EV_RT_READY', function () {
start(config);

@ -0,0 +1,103 @@
define([
'/common/common-util.js',
'/common/outer/worker-channel.js',
'/common/outer/store-rpc.js',
], function (Util, Channel, SRpc) {
var msgEv = Util.mkEvent();
var sendMsg = Util.mkEvent();
var create = function () {
var Rpc = SRpc();
var postMessage = function (data) {
sendMsg.fire(data);
};
Channel.create(msgEv, postMessage, function (chan) {
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](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) {
// load Store here, with cfg, and pass a "query" (chan.query)
// 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(data2);
});
};
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) {
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);
};
return {
query: function (data) {
msgEv.fire({data: data});
},
onMessage: function (cb) {
sendMsg.reg(function (data) {
setTimeout(function () {
cb(data);
});
});
},
create: create
};
});

@ -0,0 +1,175 @@
/* jshint ignore:start */
importScripts('/bower_components/requirejs/require.js');
window = self;
localStorage = {
setItem: function (k, v) { localStorage[k] = v; },
getItem: function (k) { return localStorage[k]; }
};
self.tabs = {};
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/requireconfig.js'
], function (RequireConfig) {
require.config(RequireConfig());
require([
'/common/common-util.js',
'/common/outer/worker-channel.js',
'/common/outer/store-rpc.js'
], function (Util, Channel, SRpc) {
debug('SW Required ressources loaded');
var msgEv = Util.mkEvent();
if (!self.Rpc) {
self.Rpc = SRpc();
}
var Rpc = self.Rpc;
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);
}
if (q === "DISCONNECT") {
console.log('Deleting existing store!');
delete self.Rpc;
delete self.store;
}
});
});
chan.on('CONNECT', function (cfg, cb) {
debug('SW Connect callback');
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") {
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[client.id].close = function () {
Rpc._removeClient(client.id);
};
});
});
};
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 (e.data === "CLOSE") {
if (tabs[cId] && tabs[cId].close) {
console.log('leave');
tabs[cId].close();
}
} 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!');
});

@ -0,0 +1,174 @@
/* jshint ignore:start */
importScripts('/bower_components/requirejs/require.js');
window = self;
localStorage = {
setItem: function (k, v) { localStorage[k] = v; },
getItem: function (k) { return localStorage[k]; }
};
self.tabs = {};
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/requireconfig.js'
], function (RequireConfig) {
require.config(RequireConfig());
require([
'/common/common-util.js',
'/common/outer/worker-channel.js',
'/common/outer/store-rpc.js'
], function (Util, Channel, SRpc) {
debug('SharedW Required ressources loaded');
var msgEv = Util.mkEvent();
if (!self.Rpc) {
self.Rpc = SRpc();
}
var Rpc = self.Rpc;
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);
}
if (q === "DISCONNECT") {
console.log('Deleting existing store!');
delete self.Rpc;
delete self.store;
}
});
});
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;
client.close = function () {
Rpc._removeClient(client.id);
};
});
});
};
onconnect = function(e) {
debug('New SharedWorker 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 (e.data === "CLOSE") {
if (client && client.close) {
console.log('leave');
client.close();
}
} else if (client && client.msgEv) {
client.msgEv.fire(e);
}
};
};

@ -1,193 +1,96 @@
define([
'/common/outer/async-store.js'
], function (Store) {
var Rpc = {};
], function (AStore) {
Rpc.query = function (cmd, data, cb) {
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;
}
var create = function () {
var Store = AStore.create();
var Rpc = {};
var queries = Rpc.queries = {
// Ready
CONNECT: Store.init,
DISCONNECT: Store.disconnect,
CREATE_README: Store.createReadme,
MIGRATE_ANON_DRIVE: Store.migrateAnonDrive,
// 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;
}
INIT_RPC: Store.initRpc,
UPDATE_PIN_LIMIT: Store.updatePinLimit,
GET_PIN_LIMIT: Store.getPinLimit,
CLEAR_OWNED_CHANNEL: Store.clearOwnedChannel,
REMOVE_OWNED_CHANNEL: Store.removeOwnedChannel,
UPLOAD_CHUNK: Store.uploadChunk,
UPLOAD_COMPLETE: Store.uploadComplete,
UPLOAD_STATUS: Store.uploadStatus,
UPLOAD_CANCEL: Store.uploadCancel,
PIN_PADS: Store.pinPads,
UNPIN_PADS: Store.unpinPads,
GET_DELETED_PADS: Store.getDeletedPads,
GET_PINNED_USAGE: Store.getPinnedUsage,
// 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;
}
INIT_ANON_RPC: Store.initAnonRpc,
ANON_RPC_MESSAGE: Store.anonRpcMsg,
GET_FILE_SIZE: Store.getFileSize,
GET_MULTIPLE_FILE_SIZE: Store.getMultipleFileSize,
// 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;
}
GET: Store.get,
SET: Store.set,
ADD_PAD: Store.addPad,
SET_PAD_TITLE: Store.setPadTitle,
MOVE_TO_TRASH: Store.moveToTrash,
RESET_DRIVE: Store.resetDrive,
GET_METADATA: Store.getMetadata,
SET_DISPLAY_NAME: Store.setDisplayName,
SET_PAD_ATTRIBUTE: Store.setPadAttribute,
GET_PAD_ATTRIBUTE: Store.getPadAttribute,
SET_ATTRIBUTE: Store.setAttribute,
GET_ATTRIBUTE: Store.getAttribute,
LIST_ALL_TAGS: Store.listAllTags,
GET_TEMPLATES: Store.getTemplates,
GET_SECURE_FILES_LIST: Store.getSecureFilesList,
GET_PAD_DATA: Store.getPadData,
GET_STRONGER_HASH: Store.getStrongerHash,
INCREMENT_TEMPLATE_USE: Store.incrementTemplateUse,
// Messaging
case 'INVITE_FROM_USERLIST': {
Store.inviteFromUserlist(data, cb); break;
}
INVITE_FROM_USERLIST: Store.inviteFromUserlist,
ADD_DIRECT_MESSAGE_HANDLERS: Store.addDirectMessageHandlers,
// 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;
}
CONTACTS_GET_FRIEND_LIST: Store.messenger.getFriendList,
CONTACTS_GET_MY_INFO: Store.messenger.getMyInfo,
CONTACTS_GET_FRIEND_INFO: Store.messenger.getFriendInfo,
CONTACTS_REMOVE_FRIEND: Store.messenger.removeFriend,
CONTACTS_OPEN_FRIEND_CHANNEL: Store.messenger.openFriendChannel,
CONTACTS_GET_FRIEND_STATUS: Store.messenger.getFriendStatus,
CONTACTS_GET_MORE_HISTORY: Store.messenger.getMoreHistory,
CONTACTS_SEND_MESSAGE: Store.messenger.sendMessage,
CONTACTS_SET_CHANNEL_HEAD: Store.messenger.setChannelHead,
// 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;
}
SEND_PAD_MSG: Store.sendPadMsg,
JOIN_PAD: Store.joinPad,
GET_FULL_HISTORY: Store.getFullHistory,
IS_NEW_CHANNEL: Store.isNewChannel,
// 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");
DRIVE_USEROBJECT: Store.userObjectCommand,
// Settings,
DELETE_ACCOUNT: Store.deleteAccount,
};
break;
Rpc.query = function (cmd, data, cb) {
if (queries[cmd]) {
queries[cmd]('0', data, cb);
} else {
console.error('UNHANDLED_STORE_RPC');
}
}
};
// Internal calls
Rpc._removeClient = Store._removeClient;
Rpc._subscribeToDrive = Store._subscribeToDrive;
Rpc._subscribeToMessenger = Store._subscribeToMessenger;
return Rpc;
};
return Rpc;
return create;
});

@ -0,0 +1,100 @@
/* jshint ignore:start */
importScripts('/bower_components/requirejs/require.js');
window = self;
localStorage = {
setItem: function (k, v) { localStorage[k] = v; },
getItem: function (k) { return localStorage[k]; }
};
require([
'/common/requireconfig.js'
], function (RequireConfig) {
require.config(RequireConfig());
require([
'/common/common-util.js',
'/common/outer/worker-channel.js',
'/common/outer/store-rpc.js'
], function (Util, Channel, SRpc) {
var msgEv = Util.mkEvent();
var Rpc = SRpc();
Channel.create(msgEv, postMessage, function (chan) {
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](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) {
// load Store here, with cfg, and pass a "query" (chan.query)
// 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(data2);
});
};
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) {
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);
onmessage = function (e) {
msgEv.fire(e);
};
});
});

@ -0,0 +1,147 @@
// This file provides the API for the channel for talking to and from the sandbox iframe.
define([
//'/common/sframe-protocol.js',
'/common/common-util.js'
], function (/*SFrameProtocol,*/ Util) {
var mkTxid = function () {
return Math.random().toString(16).replace('0.', '') + Math.random().toString(16).replace('0.', '');
};
var create = function (onMsg, postMsg, cb, isWorker) {
if (!isWorker) {
var chanLoaded = false;
var waitingData = [];
onMsg.reg(function (data) {
if (chanLoaded) { return; }
waitingData.push(data);
});
}
var evReady = Util.mkEvent(true);
var handlers = {};
var queries = {};
// list of handlers which are registered from the other side...
var insideHandlers = [];
var callWhenRegistered = {};
var chan = {};
// Send a query. channel.query('Q_SOMETHING', { args: "whatever" }, function (reply) { ... });
chan.query = function (q, content, cb) {
var txid = mkTxid();
var timeout = setTimeout(function () {
delete queries[txid];
//console.log("Timeout making query " + q);
}, 30000);
queries[txid] = function (data, msg) {
clearTimeout(timeout);
delete queries[txid];
cb(undefined, data.content, msg);
};
evReady.reg(function () {
postMsg(JSON.stringify({
txid: txid,
content: content,
q: q
}));
});
};
// Fire an event. channel.event('EV_SOMETHING', { args: "whatever" });
var event = chan.event = function (e, content) {
evReady.reg(function () {
postMsg(JSON.stringify({ content: content, q: e }));
});
};
// Be notified on query or event. channel.on('EV_SOMETHING', function (args, reply) { ... });
// If the type is a query, your handler will be invoked with a reply function that takes
// one argument (the content to reply with).
chan.on = function (queryType, handler, quiet) {
(handlers[queryType] = handlers[queryType] || []).push(function (data, msg) {
handler(data.content, function (replyContent) {
postMsg(JSON.stringify({
txid: data.txid,
content: replyContent
}));
}, msg);
});
if (!quiet) {
event('EV_REGISTER_HANDLER', queryType);
}
};
// If a particular handler is registered, call the callback immediately, otherwise it will be called
// when that handler is first registered.
// channel.whenReg('Q_SOMETHING', function () { ...query Q_SOMETHING?... });
chan.whenReg = function (queryType, cb, always) {
var reg = always;
if (insideHandlers.indexOf(queryType) > -1) {
cb();
} else {
reg = true;
}
if (reg) {
(callWhenRegistered[queryType] = callWhenRegistered[queryType] || []).push(cb);
}
};
// Same as whenReg except it will invoke every time there is another registration, not just once.
chan.onReg = function (queryType, cb) { chan.whenReg(queryType, cb, true); };
chan.on('EV_REGISTER_HANDLER', function (content) {
if (callWhenRegistered[content]) {
callWhenRegistered[content].forEach(function (f) { f(); });
delete callWhenRegistered[content];
}
insideHandlers.push(content);
});
chan.whenReg('EV_REGISTER_HANDLER', evReady.fire);
// Make sure both iframes are ready
var isReady =false;
chan.onReady = function (h) {
if (isReady) {
return void h();
}
if (typeof(h) !== "function") { return; }
chan.on('EV_RPC_READY', function () { isReady = true; h(); });
};
chan.ready = function () {
chan.whenReg('EV_RPC_READY', function () {
chan.event('EV_RPC_READY');
});
};
onMsg.reg(function (msg) {
var data = JSON.parse(msg.data);
if (typeof(data.q) === 'string' && handlers[data.q]) {
handlers[data.q].forEach(function (f) {
f(data || JSON.parse(msg.data), msg);
data = undefined;
});
} else if (typeof(data.q) === 'undefined' && queries[data.txid]) {
queries[data.txid](data, msg);
} else {
console.log("DROP Unhandled message");
console.log(msg.data, isWorker);
console.log(msg);
}
});
if (isWorker) {
evReady.fire();
} else {
chanLoaded = true;
waitingData.forEach(function (d) {
onMsg.fire(d);
});
waitingData = [];
}
cb(chan);
};
return { create: create };
});

@ -116,6 +116,10 @@ define([], function () {
sframeChan.event('EV_RT_DISCONNECT');
});
padRpc.onConnectEvent.reg(function (data) {
onOpen(data);
});
padRpc.onErrorEvent.reg(function (err) {
sframeChan.event('EV_RT_ERROR', err);
});
@ -128,8 +132,6 @@ define([], function () {
owners: owners,
password: password,
expire: expire
}, function(data) {
onOpen(data);
});
};

@ -704,6 +704,9 @@ define([
readOnly: readOnly,
crypto: Crypto.createEncryptor(secret.keys),
onConnect: function () {
var href = parsed.getUrl();
// Add friends requests handlers when we have the final href
Cryptpad.messaging.addHandlers(href);
if (window.location.hash && window.location.hash !== '#') {
window.location = parsed.getUrl({
present: parsed.hashData.present,

@ -51,6 +51,8 @@ define([
if (!window.Worker) {
return void $container.text("WebWorkers not supported by your browser");
}
/*
// Shared worker
console.log('ready');
var myWorker = new SharedWorker('/worker/worker.js');
console.log(myWorker);
@ -65,7 +67,73 @@ define([
}
$container.append('<br>');
$container.append(e.data);
};
};*/
// Service worker
if ('serviceWorker' in navigator) {
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);
console.log('Registration succeeded. Scope is ' + reg.scope);
$container.append('<br>');
$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) {
if (yes) { common.gotoURL(); }
});
};
// Here we add the event listener for receiving messages
navigator.serviceWorker.addEventListener('message', function (e) {
var data = e.data;
if (data && data.state === "READY") {
initializing = false;
$container.append('<hr>sw.js ready');
postMessage(["Hello worker"]);
return;
}
$container.append('<br>');
$container.append(e.data);
});
if (reg.active) {
worker = reg.active;
postMessage("INIT");
}
}).catch(function(error) {
console.log('Registration failed with ' + error);
$container.append('Registration error: ' + error);
});
} else {
console.log('NO SERVICE WORKER');
}
$container.append('<hr>inner.js ready');
});
});

@ -0,0 +1,69 @@
/* jshint ignore:start */
var id;
//= Math.floor(Math.random()*100000);
var postMsg = function (client, data) {
client.postMessage(data);
};
var broadcast = function (data, excludes) {
// Loop over all available clients
clients.matchAll().then(function (clients) {
clients.forEach(function (client) {
if (excludes.indexOf(client.id) === -1) {
postMsg(client, data);
}
})
})
};
var sendTo = function (data, clientId){
clients.matchAll().then(function (clients) {
clients.some(function (client) {
if (client.id === clientId) {
postMsg(client, data)
}
})
})
};
var getClients = function () {
clients.matchAll().then(function (clients) {
var cl = clients.map(function (c) {
console.log(JSON.stringify(c));
return c.id;
});
console.log(cl);
});
};
self.addEventListener('message', function (e) {
console.log(clients);
console.log('worker received');
console.log(e.data);
console.log(e.source);
var cId = e.source.id;
if (e.data === "INIT") {
if (!id) {
id = Math.floor(Math.random()*100000);
}
broadcast(cId + ' has joined!', [cId]);
postMsg(e.source, {state: 'READY'});
postMsg(e.source, "Welcome to SW " + id + "!");
postMsg(e.source, "You are identified as " + cId);
} else {
console.log(e.data);
postMsg(e.source, 'Yo (Re: '+e.data+')');
}
});
self.addEventListener('install', function (e) {
console.log(e);
console.log('V1 installing…');
self.skipWaiting();
});
self.addEventListener('activate', function (e) {
console.log(e);
console.log('V1 now ready to handle fetches!');
});
Loading…
Cancel
Save