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

@ -7,25 +7,27 @@ define([
'/common/common-constants.js', '/common/common-constants.js',
'/common/common-feedback.js', '/common/common-feedback.js',
'/common/outer/local-store.js', '/common/outer/local-store.js',
'/common/outer/store-rpc.js', '/common/outer/worker-channel.js',
'/customize/application_config.js', '/customize/application_config.js',
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
], function (Config, Messages, Util, Hash, ], function (Config, Messages, Util, Hash,
Messaging, Constants, Feedback, LocalStore, AStore, Messaging, Constants, Feedback, LocalStore, Channel,
AppConfig, Nthen) { AppConfig, Nthen) {
/* This file exposes functionality which is specific to Cryptpad, but not to /* This file exposes functionality which is specific to Cryptpad, but not to
any particular pad type. This includes functions for committing metadata any particular pad type. This includes functions for committing metadata
about pads to your local storage for future use and improved usability. about pads to your local storage for future use and improved usability.
Additionally, there is some basic functionality for import/export. Additionally, there is some basic functionality for import/export.
*/ */
var postMessage = function (cmd, data, cb) { var urlArgs = Util.find(Config, ['requireConf', 'urlArgs']) || '';
setTimeout(function () {
var postMessage = function (/*cmd, data, cb*/) {
/*setTimeout(function () {
AStore.query(cmd, data, cb); AStore.query(cmd, data, cb);
}); });*/
console.error('NOT_READY');
}; };
var tryParsing = function (x) { var tryParsing = function (x) {
try { return JSON.parse(x); } try { return JSON.parse(x); }
@ -531,6 +533,11 @@ define([
var messaging = common.messaging = {}; var messaging = common.messaging = {};
messaging.onFriendRequest = Util.mkEvent(); messaging.onFriendRequest = Util.mkEvent();
messaging.onFriendComplete = Util.mkEvent(); messaging.onFriendComplete = Util.mkEvent();
messaging.addHandlers = function (href) {
postMessage("ADD_DIRECT_MESSAGE_HANDLERS", {
href: href
});
};
// Messenger // Messenger
var messenger = common.messenger = {}; var messenger = common.messenger = {};
@ -570,8 +577,8 @@ define([
// Pad RPC // Pad RPC
var pad = common.padRpc = {}; var pad = common.padRpc = {};
pad.joinPad = function (data, cb) { pad.joinPad = function (data) {
postMessage("JOIN_PAD", data, cb); postMessage("JOIN_PAD", data);
}; };
pad.sendPadMsg = function (data, cb) { pad.sendPadMsg = function (data, cb) {
postMessage("SEND_PAD_MSG", data, cb); postMessage("SEND_PAD_MSG", data, cb);
@ -581,6 +588,7 @@ define([
pad.onJoinEvent = Util.mkEvent(); pad.onJoinEvent = Util.mkEvent();
pad.onLeaveEvent = Util.mkEvent(); pad.onLeaveEvent = Util.mkEvent();
pad.onDisconnectEvent = Util.mkEvent(); pad.onDisconnectEvent = Util.mkEvent();
pad.onConnectEvent = Util.mkEvent();
pad.onErrorEvent = Util.mkEvent(); pad.onErrorEvent = Util.mkEvent();
// Loading events // Loading events
@ -669,108 +677,57 @@ define([
window.location.href = '/login/'; window.location.href = '/login/';
}; };
common.startAccountDeletion = function (cb) { common.startAccountDeletion = function (data, cb) {
// Logout other tabs // Logout other tabs
LocalStore.logout(null, true); LocalStore.logout(null, true);
cb(); cb();
}; };
var onMessage = function (cmd, data, cb) { var queries = {
cb = cb || function () {}; REQUEST_LOGIN: requestLogin,
switch (cmd) { UPDATE_METADATA: common.changeMetadata,
case 'REQUEST_LOGIN': { UPDATE_TOKEN: function (data) {
requestLogin(); var localToken = tryParsing(localStorage.getItem(Constants.tokenKey));
break; if (localToken !== data.token) { requestLogin(); }
} },
case 'UPDATE_METADATA': { // Messaging
common.changeMetadata(); Q_FRIEND_REQUEST: common.messaging.onFriendRequest.fire,
break; EV_FIREND_COMPLETE: common.messaging.onFriendComplete.fire,
} // Network
case 'UPDATE_TOKEN': { NETWORK_DISCONNECT: common.onNetworkDisconnect.fire,
var localToken = tryParsing(localStorage.getItem(Constants.tokenKey)); NETWORK_RECONNECT: function (data) {
if (localToken !== data.token) { requestLogin(); } require(['/api/config?' + (+new Date())], function (NewConfig) {
break; var update = updateLocalVersion(NewConfig.requireConf && NewConfig.requireConf.urlArgs);
} if (update) {
case 'Q_FRIEND_REQUEST': { postMessage('DISCONNECT');
common.messaging.onFriendRequest.fire(data, cb); return;
break; }
} common.onNetworkReconnect.fire(data);
case 'EV_FRIEND_COMPLETE': { });
common.messaging.onFriendComplete.fire(data); },
break; // Messenger
} CONTACTS_MESSAGE: common.messenger.onMessageEvent.fire,
// Network CONTACTS_JOIN: common.messenger.onJoinEvent.fire,
case 'NETWORK_DISCONNECT': { CONTACTS_LEAVE: common.messenger.onLeaveEvent.fire,
common.onNetworkDisconnect.fire(); break; CONTACTS_UPDATE: common.messenger.onUpdateEvent.fire,
} CONTACTS_FRIEND: common.messenger.onFriendEvent.fire,
case 'NETWORK_RECONNECT': { CONTACTS_UNFRIEND: common.messenger.onUnfriendEvent.fire,
require(['/api/config?' + (+new Date())], function (NewConfig) { // Pad
var update = updateLocalVersion(NewConfig.requireConf && NewConfig.requireConf.urlArgs); PAD_READY: common.padRpc.onReadyEvent.fire,
if (update) { PAD_MESSAGE: common.padRpc.onMessageEvent.fire,
postMessage('DISCONNECT'); PAD_JOIN: common.padRpc.onJoinEvent.fire,
return; PAD_LEAVE: common.padRpc.onLeaveEvent.fire,
} PAD_DISCONNECT: common.padRpc.onDisconnectEvent.fire,
common.onNetworkReconnect.fire(data); PAD_CONNECT: common.padRpc.onConnectEvent.fire,
}); PAD_ERROR: common.padRpc.onErrorEvent.fire,
break; // Drive
} DRIVE_LOG: common.drive.onLog.fire,
// Messenger DRIVE_CHANGE: common.drive.onChange.fire,
case 'CONTACTS_MESSAGE': { DRIVE_REMOVE: common.drive.onRemove.fire,
common.messenger.onMessageEvent.fire(data); break; // Account deletion
} DELETE_ACCOUNT: common.startAccountDeletion,
case 'CONTACTS_JOIN': { // Loading
common.messenger.onJoinEvent.fire(data); break; LOADING_DRIVE: common.loading.onDriveEvent.fire
}
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 () { common.ready = (function () {
@ -828,43 +785,166 @@ define([
} }
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
var cfg = { 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(), userHash: LocalStore.getUserHash(),
anonHash: LocalStore.getFSHash(), anonHash: LocalStore.getFSHash(),
localToken: tryParsing(localStorage.getItem(Constants.tokenKey)), localToken: tryParsing(localStorage.getItem(Constants.tokenKey)),
language: common.getLanguage(), language: common.getLanguage(),
messenger: rdyCfg.messenger, messenger: rdyCfg.messenger, // Boolean
driveEvents: rdyCfg.driveEvents driveEvents: rdyCfg.driveEvents // Boolean
}; };
if (sessionStorage[Constants.newPadPathKey]) { if (sessionStorage[Constants.newPadPathKey]) {
cfg.initialPath = sessionStorage[Constants.newPadPathKey]; cfg.initialPath = sessionStorage[Constants.newPadPathKey];
delete 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) { var channelIsReady = waitFor();
// copy User_hash into sessionStorage because cross-domain iframes
// on safari replaces localStorage with sessionStorage or something var msgEv = Util.mkEvent();
sessionStorage.setItem(Constants.userHashKey, cfg.userHash); var postMsg, worker;
}*/ Nthen(function (waitFor2) {
if (typeof(SharedWorker) !== "undefined") {
if (cfg.userHash) { worker = new SharedWorker('/common/outer/sharedworker.js?' + urlArgs);
var localToken = tryParsing(localStorage.getItem(Constants.tokenKey)); worker.onerror = function (e) {
if (localToken === null) { console.error(e);
// if that number hasn't been set to localStorage, do so. };
localStorage.setItem(Constants.tokenKey, data[Constants.tokenKey]); 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) { }).nThen(function (waitFor) {
// Load the new pad when the hash has changed // Load the new pad when the hash has changed
var oldHref = document.location.href; var oldHref = document.location.href;
@ -893,9 +973,12 @@ define([
document.location.reload(); document.location.reload();
} else if (o && !n) { } else if (o && !n) {
LocalStore.logout(); LocalStore.logout();
postMessage("DISCONNECT");
} }
}); });
LocalStore.onLogout(function () {
console.log('onLogout: disconnect');
postMessage("DISCONNECT");
});
if (PINNING_ENABLED && LocalStore.isLoggedIn()) { if (PINNING_ENABLED && LocalStore.isLoggedIn()) {
console.log("logged in. pads will be pinned"); 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 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 start = function (conf) {
var channel = conf.channel; var channel = conf.channel;
var validateKey = conf.validateKey; var validateKey = conf.validateKey;
@ -72,7 +76,7 @@ define([], function () {
// at the beginning of each message on the server. // at the beginning of each message on the server.
// We have to make sure our regex ignores this nonce using {0,20} (our IDs // We have to make sure our regex ignores this nonce using {0,20} (our IDs
// should only be 8 characters long) // should only be 8 characters long)
return msg.replace(/^cp\|([A-Za-z0-9+\/=]{0,20}\|)?/, ''); return removeCp(msg);
}; };
var msgOut = function (msg) { var msgOut = function (msg) {
@ -124,6 +128,8 @@ define([], function () {
lastKnownHash = msg.slice(0,64); lastKnownHash = msg.slice(0,64);
var isCp = /^cp\|/.test(msg);
var message = msgIn(peer, msg); var message = msgIn(peer, msg);
verbose(message); verbose(message);
@ -134,7 +140,7 @@ define([], function () {
message = unBencode(message);//.slice(message.indexOf(':[') + 1); message = unBencode(message);//.slice(message.indexOf(':[') + 1);
// pass the message into Chainpad // pass the message into Chainpad
onMessage(peer, message, validateKey); onMessage(peer, message, validateKey, isCp);
//sframeChan.query('Q_RT_MESSAGE', message, function () { }); //sframeChan.query('Q_RT_MESSAGE', message, function () { });
}; };
@ -259,7 +265,8 @@ define([], function () {
}; };
return { return {
start: start start: start,
removeCp: removeCp
/*function (config) { /*function (config) {
config.sframeChan.whenReg('EV_RT_READY', function () { config.sframeChan.whenReg('EV_RT_READY', function () {
start(config); 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([ define([
'/common/outer/async-store.js' '/common/outer/async-store.js'
], function (Store) { ], function (AStore) {
var Rpc = {};
Rpc.query = function (cmd, data, cb) { var create = function () {
switch (cmd) { var Store = AStore.create();
// READY
case 'CONNECT': { var Rpc = {};
Store.init(data, cb); break;
} var queries = Rpc.queries = {
case 'DISCONNECT': { // Ready
Store.disconnect(data, cb); break; CONNECT: Store.init,
} DISCONNECT: Store.disconnect,
case 'CREATE_README': { CREATE_README: Store.createReadme,
Store.createReadme(data, cb); break; MIGRATE_ANON_DRIVE: Store.migrateAnonDrive,
}
case 'MIGRATE_ANON_DRIVE': {
Store.migrateAnonDrive(data, cb); break;
}
// RPC // RPC
case 'INIT_RPC': { INIT_RPC: Store.initRpc,
Store.initRpc(data, cb); break; UPDATE_PIN_LIMIT: Store.updatePinLimit,
} GET_PIN_LIMIT: Store.getPinLimit,
case 'UPDATE_PIN_LIMIT': { CLEAR_OWNED_CHANNEL: Store.clearOwnedChannel,
Store.updatePinLimit(data, cb); break; REMOVE_OWNED_CHANNEL: Store.removeOwnedChannel,
} UPLOAD_CHUNK: Store.uploadChunk,
case 'GET_PIN_LIMIT': { UPLOAD_COMPLETE: Store.uploadComplete,
Store.getPinLimit(data, cb); break; UPLOAD_STATUS: Store.uploadStatus,
} UPLOAD_CANCEL: Store.uploadCancel,
case 'CLEAR_OWNED_CHANNEL': { PIN_PADS: Store.pinPads,
Store.clearOwnedChannel(data, cb); break; UNPIN_PADS: Store.unpinPads,
} GET_DELETED_PADS: Store.getDeletedPads,
case 'REMOVE_OWNED_CHANNEL': { GET_PINNED_USAGE: Store.getPinnedUsage,
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 // ANON RPC
case 'INIT_ANON_RPC': { INIT_ANON_RPC: Store.initAnonRpc,
Store.initAnonRpc(data, cb); break; ANON_RPC_MESSAGE: Store.anonRpcMsg,
} GET_FILE_SIZE: Store.getFileSize,
case 'ANON_RPC_MESSAGE': { GET_MULTIPLE_FILE_SIZE: Store.getMultipleFileSize,
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 // Store
case 'GET': { GET: Store.get,
Store.get(data, cb); break; SET: Store.set,
} ADD_PAD: Store.addPad,
case 'SET': { SET_PAD_TITLE: Store.setPadTitle,
Store.set(data, cb); break; MOVE_TO_TRASH: Store.moveToTrash,
} RESET_DRIVE: Store.resetDrive,
case 'ADD_PAD': { GET_METADATA: Store.getMetadata,
Store.addPad(data, cb); break; SET_DISPLAY_NAME: Store.setDisplayName,
} SET_PAD_ATTRIBUTE: Store.setPadAttribute,
case 'SET_PAD_TITLE': { GET_PAD_ATTRIBUTE: Store.getPadAttribute,
Store.setPadTitle(data, cb); break; SET_ATTRIBUTE: Store.setAttribute,
} GET_ATTRIBUTE: Store.getAttribute,
case 'MOVE_TO_TRASH': { LIST_ALL_TAGS: Store.listAllTags,
Store.moveToTrash(data, cb); break; GET_TEMPLATES: Store.getTemplates,
} GET_SECURE_FILES_LIST: Store.getSecureFilesList,
case 'RESET_DRIVE': { GET_PAD_DATA: Store.getPadData,
Store.resetDrive(data, cb); break; GET_STRONGER_HASH: Store.getStrongerHash,
} INCREMENT_TEMPLATE_USE: Store.incrementTemplateUse,
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 // Messaging
case 'INVITE_FROM_USERLIST': { INVITE_FROM_USERLIST: Store.inviteFromUserlist,
Store.inviteFromUserlist(data, cb); break; ADD_DIRECT_MESSAGE_HANDLERS: Store.addDirectMessageHandlers,
}
// Messenger // Messenger
case 'CONTACTS_GET_FRIEND_LIST': { CONTACTS_GET_FRIEND_LIST: Store.messenger.getFriendList,
Store.messenger.getFriendList(data, cb); break; CONTACTS_GET_MY_INFO: Store.messenger.getMyInfo,
} CONTACTS_GET_FRIEND_INFO: Store.messenger.getFriendInfo,
case 'CONTACTS_GET_MY_INFO': { CONTACTS_REMOVE_FRIEND: Store.messenger.removeFriend,
Store.messenger.getMyInfo(data, cb); break; CONTACTS_OPEN_FRIEND_CHANNEL: Store.messenger.openFriendChannel,
} CONTACTS_GET_FRIEND_STATUS: Store.messenger.getFriendStatus,
case 'CONTACTS_GET_FRIEND_INFO': { CONTACTS_GET_MORE_HISTORY: Store.messenger.getMoreHistory,
Store.messenger.getFriendInfo(data, cb); break; CONTACTS_SEND_MESSAGE: Store.messenger.sendMessage,
} CONTACTS_SET_CHANNEL_HEAD: Store.messenger.setChannelHead,
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 // Pad
case 'SEND_PAD_MSG': { SEND_PAD_MSG: Store.sendPadMsg,
Store.sendPadMsg(data, cb); break; JOIN_PAD: Store.joinPad,
} GET_FULL_HISTORY: Store.getFullHistory,
case 'JOIN_PAD': { IS_NEW_CHANNEL: Store.isNewChannel,
Store.joinPad(data, cb); break;
}
case 'GET_FULL_HISTORY': {
Store.getFullHistory(data, cb); break;
}
// Drive // Drive
case 'DRIVE_USEROBJECT': { DRIVE_USEROBJECT: Store.userObjectCommand,
Store.userObjectCommand(data, cb); break; // Settings,
} DELETE_ACCOUNT: Store.deleteAccount,
// 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; 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'); sframeChan.event('EV_RT_DISCONNECT');
}); });
padRpc.onConnectEvent.reg(function (data) {
onOpen(data);
});
padRpc.onErrorEvent.reg(function (err) { padRpc.onErrorEvent.reg(function (err) {
sframeChan.event('EV_RT_ERROR', err); sframeChan.event('EV_RT_ERROR', err);
}); });
@ -128,8 +132,6 @@ define([], function () {
owners: owners, owners: owners,
password: password, password: password,
expire: expire expire: expire
}, function(data) {
onOpen(data);
}); });
}; };

@ -704,6 +704,9 @@ define([
readOnly: readOnly, readOnly: readOnly,
crypto: Crypto.createEncryptor(secret.keys), crypto: Crypto.createEncryptor(secret.keys),
onConnect: function () { 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 !== '#') { if (window.location.hash && window.location.hash !== '#') {
window.location = parsed.getUrl({ window.location = parsed.getUrl({
present: parsed.hashData.present, present: parsed.hashData.present,

@ -51,6 +51,8 @@ define([
if (!window.Worker) { if (!window.Worker) {
return void $container.text("WebWorkers not supported by your browser"); return void $container.text("WebWorkers not supported by your browser");
} }
/*
// Shared worker
console.log('ready'); console.log('ready');
var myWorker = new SharedWorker('/worker/worker.js'); var myWorker = new SharedWorker('/worker/worker.js');
console.log(myWorker); console.log(myWorker);
@ -65,7 +67,73 @@ define([
} }
$container.append('<br>'); $container.append('<br>');
$container.append(e.data); $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'); $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