diff options
author | gorhill <rhill@raymondhill.net> | 2015-08-11 15:29:14 -0400 |
---|---|---|
committer | gorhill <rhill@raymondhill.net> | 2015-08-11 15:29:14 -0400 |
commit | 690421aead701cb3045c1d8913cb85d432e494c3 (patch) | |
tree | 3c3fb062385ce409240d224d19c84a378ce3dca8 /platform/chromium | |
parent | a7712116a78890924971b219ff49e75ef194cf00 (diff) | |
download | uBlock-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.js | 200 |
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 + }; +})(); + +/******************************************************************************/ +/******************************************************************************/ })(); |