aboutsummaryrefslogtreecommitdiffstats
path: root/platform/chromium
diff options
context:
space:
mode:
authorgorhill <rhill@raymondhill.net>2015-08-11 15:29:14 -0400
committergorhill <rhill@raymondhill.net>2015-08-11 15:29:14 -0400
commit690421aead701cb3045c1d8913cb85d432e494c3 (patch)
tree3c3fb062385ce409240d224d19c84a378ce3dca8 /platform/chromium
parenta7712116a78890924971b219ff49e75ef194cf00 (diff)
downloaduBlock-690421aead701cb3045c1d8913cb85d432e494c3.zip
uBlock-690421aead701cb3045c1d8913cb85d432e494c3.tar.gz
uBlock-690421aead701cb3045c1d8913cb85d432e494c3.tar.bz2
sync feature (#80): draft
Diffstat (limited to 'platform/chromium')
-rw-r--r--platform/chromium/vapi-background.js200
1 files changed, 200 insertions, 0 deletions
diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js
index 6237124..f3c7ff3 100644
--- a/platform/chromium/vapi-background.js
+++ b/platform/chromium/vapi-background.js
@@ -54,12 +54,14 @@ vAPI.app.restart = function() {
};
/******************************************************************************/
+/******************************************************************************/
// chrome.storage.local.get(null, function(bin){ console.debug('%o', bin); });
vAPI.storage = chrome.storage.local;
/******************************************************************************/
+/******************************************************************************/
// https://github.com/gorhill/uMatrix/issues/234
// https://developer.chrome.com/extensions/privacy#property-network
@@ -102,6 +104,7 @@ vAPI.browserSettings = {
};
/******************************************************************************/
+/******************************************************************************/
vAPI.tabs = {};
@@ -500,6 +503,7 @@ vAPI.tabs.injectScript = function(tabId, details, callback) {
};
/******************************************************************************/
+/******************************************************************************/
// Must read: https://code.google.com/p/chromium/issues/detail?id=410868#c8
@@ -535,6 +539,7 @@ vAPI.setIcon = function(tabId, iconStatus, badge) {
};
/******************************************************************************/
+/******************************************************************************/
vAPI.messaging = {
ports: {},
@@ -752,6 +757,7 @@ vAPI.messaging.broadcast = function(message) {
};
/******************************************************************************/
+/******************************************************************************/
vAPI.net = {};
@@ -876,6 +882,7 @@ vAPI.net.registerListeners = function() {
};
/******************************************************************************/
+/******************************************************************************/
vAPI.contextMenu = {
create: function(details, callback) {
@@ -891,12 +898,14 @@ vAPI.contextMenu = {
};
/******************************************************************************/
+/******************************************************************************/
vAPI.lastError = function() {
return chrome.runtime.lastError;
};
/******************************************************************************/
+/******************************************************************************/
// This is called only once, when everything has been loaded in memory after
// the extension was launched. It can be used to inject content scripts
@@ -948,6 +957,7 @@ vAPI.onLoadAllCompleted = function() {
};
/******************************************************************************/
+/******************************************************************************/
vAPI.punycodeHostname = function(hostname) {
return hostname;
@@ -958,6 +968,196 @@ vAPI.punycodeURL = function(url) {
};
/******************************************************************************/
+/******************************************************************************/
+
+vAPI.cloud = (function() {
+ var chunkCountPerFetch = 16; // Must be a power of 2
+
+ // Mind chrome.storage.sync.MAX_ITEMS (512 at time of writing)
+ var maxChunkCountPerItem = Math.floor(512 * 0.75) & ~(chunkCountPerFetch - 1);
+
+ // Mind chrome.storage.sync.QUOTA_BYTES_PER_ITEM (8192 at time of writing)
+ var maxChunkSize = Math.floor(chrome.storage.sync.QUOTA_BYTES_PER_ITEM * 0.75);
+
+ // Mind chrome.storage.sync.QUOTA_BYTES_PER_ITEM (8192 at time of writing)
+ var maxStorageSize = chrome.storage.sync.QUOTA_BYTES;
+
+ var options = {
+ deviceName: ''
+ };
+
+ var getDeviceName = function() {
+ // Assign a permanent user-friendly id to this uBlock instance if one does
+ // not exist. This will allow to have some sort of identifier for a user
+ // to possibly identify the source of cloud data.
+ var name = window.localStorage.getItem('deviceName') || '';
+ if ( name !== '' ) {
+ return name;
+ }
+
+ return window.navigator.platform;
+ };
+
+ // This is used to find out a rough count of how many chunks exists:
+ // We "poll" at specific index in order to get a rough idea of how
+ // large is the stored string.
+ // This allows reading a single item with only 2 sync operations -- a
+ // good thing given chrome.storage.syncMAX_WRITE_OPERATIONS_PER_MINUTE
+ // and chrome.storage.syncMAX_WRITE_OPERATIONS_PER_HOUR.
+ var getCoarseChunkCount = function(dataKey, callback) {
+ var bin = {};
+ for ( var i = 0; i < maxChunkCountPerItem; i += 16 ) {
+ bin[dataKey + i.toString()] = '';
+ }
+
+ chrome.storage.sync.get(bin, function(bin) {
+ if ( chrome.runtime.lastError ) {
+ callback(0, chrome.runtime.lastError.message);
+ return;
+ }
+
+ // Could loop backward... let's assume for now
+ // maxChunkCountPerItem could be something else than a
+ // multiple of 16.
+ var chunkCount = 0;
+ for ( var i = 0; i < maxChunkCountPerItem; i += 16 ) {
+ if ( bin[dataKey + i.toString()] === '' ) {
+ break;
+ }
+ chunkCount = i + 16;
+ }
+
+ callback(chunkCount);
+ });
+ };
+
+ var deleteChunks = function(dataKey, start) {
+ var keys = [];
+
+ // No point in deleting more than:
+ // - The max number of chunks per item
+ // - The max number of chunks per storage limit
+ var n = Math.min(
+ maxChunkCountPerItem,
+ Math.ceil(maxStorageSize / maxChunkSize)
+ );
+ for ( var i = start; i < n; i++ ) {
+ keys.push(dataKey + i.toString());
+ }
+ chrome.storage.sync.remove(keys);
+ };
+
+ var start = function(/* dataKeys */) {
+ };
+
+ var push = function(dataKey, data, callback) {
+ var item = JSON.stringify({
+ 'source': getDeviceName(),
+ 'tstamp': Date.now(),
+ 'data': data
+ });
+
+ // Chunkify taking into account QUOTA_BYTES_PER_ITEM:
+ // https://developer.chrome.com/extensions/storage#property-sync
+ // "The maximum size (in bytes) of each individual item in sync
+ // "storage, as measured by the JSON stringification of its value
+ // "plus its key length."
+ var bin = {};
+ var chunkCount = Math.ceil(item.length / maxChunkSize);
+ for ( var i = 0; i < chunkCount; i++ ) {
+ bin[dataKey + i.toString()] = item.substr(i * maxChunkSize, maxChunkSize);
+ }
+ bin[dataKey + i.toString()] = ''; // Sentinel
+
+ chrome.storage.sync.set(bin, function() {
+ var errorStr;
+ if ( chrome.runtime.lastError ) {
+ errorStr = chrome.runtime.lastError.message;
+ }
+ callback(errorStr);
+
+ // Remove potentially unused trailing chunks
+ deleteChunks(dataKey, chunkCount);
+ });
+ };
+
+ var pull = function(dataKey, callback) {
+ var assembleChunks = function(bin) {
+ if ( chrome.runtime.lastError ) {
+ callback(null, chrome.runtime.lastError.message);
+ return;
+ }
+
+ // Assemble chunks into a single string.
+ var json = [], jsonSlice;
+ var i = 0;
+ for (;;) {
+ jsonSlice = bin[dataKey + i.toString()];
+ if ( jsonSlice === '' ) {
+ break;
+ }
+ json.push(jsonSlice);
+ i += 1;
+ }
+
+ var entry = null;
+ try {
+ entry = JSON.parse(json.join(''));
+ } catch(ex) {
+ }
+ callback(entry);
+ };
+
+ var fetchChunks = function(coarseCount, errorStr) {
+ if ( coarseCount === 0 || typeof errorStr === 'string' ) {
+ callback(null, errorStr);
+ return;
+ }
+
+ var bin = {};
+ for ( var i = 0; i < coarseCount; i++ ) {
+ bin[dataKey + i.toString()] = '';
+ }
+
+ chrome.storage.sync.get(bin, assembleChunks);
+ };
+
+ getCoarseChunkCount(dataKey, fetchChunks);
+ };
+
+ var getOptions = function(callback) {
+ if ( typeof callback !== 'function' ) {
+ return;
+ }
+
+ callback(options);
+ };
+
+ var setOptions = function(details, callback) {
+ if ( typeof details !== 'object' || details === null ) {
+ return;
+ }
+
+ if ( typeof details.deviceName === 'string' ) {
+ window.localStorage.setItem('deviceName', details.deviceName);
+ }
+
+ if ( typeof callback === 'function' ) {
+ callback(options);
+ }
+ };
+
+ return {
+ start: start,
+ push: push,
+ pull: pull,
+ getOptions: getOptions,
+ setOptions: setOptions
+ };
+})();
+
+/******************************************************************************/
+/******************************************************************************/
})();