diff --git a/server/camlistored/ui/Crypto.js b/server/camlistored/ui/Crypto.js deleted file mode 100644 index 8e1031548..000000000 --- a/server/camlistored/ui/Crypto.js +++ /dev/null @@ -1,188 +0,0 @@ -// From http://code.google.com/p/crypto-js/ -// License: http://www.opensource.org/licenses/bsd-license.php -// -// Copyright (c) 2009, Jeff Mott. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// Redistributions of source code must retain the above copyright notice, this -// list of conditions and the following disclaimer. Redistributions in binary -// form must reproduce the above copyright notice, this list of conditions and -// the following disclaimer in the documentation and/or other materials provided -// with the distribution. Neither the name Crypto-JS nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. THIS SOFTWARE IS PROVIDED -// BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -// EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, -// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -if (typeof goog != 'undefined' && typeof goog.provide != 'undefined') { - goog.provide('camlistore.Crypto'); -} - -if (typeof Crypto == "undefined" || ! Crypto.util) -{ -(function(){ - -var base64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -// Global Crypto object -var Crypto = window.Crypto = {}; - -// Crypto utilities -var util = Crypto.util = { - - // Bit-wise rotate left - rotl: function (n, b) { - return (n << b) | (n >>> (32 - b)); - }, - - // Bit-wise rotate right - rotr: function (n, b) { - return (n << (32 - b)) | (n >>> b); - }, - - // Swap big-endian to little-endian and vice versa - endian: function (n) { - - // If number given, swap endian - if (n.constructor == Number) { - return util.rotl(n, 8) & 0x00FF00FF | - util.rotl(n, 24) & 0xFF00FF00; - } - - // Else, assume array and swap all items - for (var i = 0; i < n.length; i++) - n[i] = util.endian(n[i]); - return n; - - }, - - // Generate an array of any length of random bytes - randomBytes: function (n) { - for (var bytes = []; n > 0; n--) - bytes.push(Math.floor(Math.random() * 256)); - return bytes; - }, - - // Convert a byte array to big-endian 32-bit words - bytesToWords: function (bytes) { - for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8) - words[b >>> 5] |= bytes[i] << (24 - b % 32); - return words; - }, - - // Convert big-endian 32-bit words to a byte array - wordsToBytes: function (words) { - for (var bytes = [], b = 0; b < words.length * 32; b += 8) - bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF); - return bytes; - }, - - // Convert a byte array to a hex string - bytesToHex: function (bytes) { - for (var hex = [], i = 0; i < bytes.length; i++) { - hex.push((bytes[i] >>> 4).toString(16)); - hex.push((bytes[i] & 0xF).toString(16)); - } - return hex.join(""); - }, - - // Convert a hex string to a byte array - hexToBytes: function (hex) { - for (var bytes = [], c = 0; c < hex.length; c += 2) - bytes.push(parseInt(hex.substr(c, 2), 16)); - return bytes; - }, - - // Convert a byte array to a base-64 string - bytesToBase64: function (bytes) { - - // Use browser-native function if it exists - if (typeof btoa == "function") return btoa(Binary.bytesToString(bytes)); - - for(var base64 = [], i = 0; i < bytes.length; i += 3) { - var triplet = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]; - for (var j = 0; j < 4; j++) { - if (i * 8 + j * 6 <= bytes.length * 8) - base64.push(base64map.charAt((triplet >>> 6 * (3 - j)) & 0x3F)); - else base64.push("="); - } - } - - return base64.join(""); - - }, - - // Convert a base-64 string to a byte array - base64ToBytes: function (base64) { - - // Use browser-native function if it exists - if (typeof atob == "function") return Binary.stringToBytes(atob(base64)); - - // Remove non-base-64 characters - base64 = base64.replace(/[^A-Z0-9+\/]/ig, ""); - - for (var bytes = [], i = 0, imod4 = 0; i < base64.length; imod4 = ++i % 4) { - if (imod4 == 0) continue; - bytes.push(((base64map.indexOf(base64.charAt(i - 1)) & (Math.pow(2, -2 * imod4 + 8) - 1)) << (imod4 * 2)) | - (base64map.indexOf(base64.charAt(i)) >>> (6 - imod4 * 2))); - } - - return bytes; - - } - -}; - -// Crypto mode namespace -Crypto.mode = {}; - -// Crypto character encodings -var charenc = Crypto.charenc = {}; - -// UTF-8 encoding -var UTF8 = charenc.UTF8 = { - - // Convert a string to a byte array - stringToBytes: function (str) { - return Binary.stringToBytes(unescape(encodeURIComponent(str))); - }, - - // Convert a byte array to a string - bytesToString: function (bytes) { - return decodeURIComponent(escape(Binary.bytesToString(bytes))); - } - -}; - -// Binary encoding -var Binary = charenc.Binary = { - - // Convert a string to a byte array - stringToBytes: function (str) { - for (var bytes = [], i = 0; i < str.length; i++) - bytes.push(str.charCodeAt(i)); - return bytes; - }, - - // Convert a byte array to a string - bytesToString: function (bytes) { - for (var str = [], i = 0; i < bytes.length; i++) - str.push(String.fromCharCode(bytes[i])); - return str.join(""); - } - -}; - -})(); -} diff --git a/server/camlistored/ui/SHA1.js b/server/camlistored/ui/SHA1.js deleted file mode 100644 index c0659be82..000000000 --- a/server/camlistored/ui/SHA1.js +++ /dev/null @@ -1,115 +0,0 @@ -// From http://code.google.com/p/crypto-js/ -// License: http://www.opensource.org/licenses/bsd-license.php -// -// Copyright (c) 2009, Jeff Mott. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// Redistributions of source code must retain the above copyright notice, this -// list of conditions and the following disclaimer. Redistributions in binary -// form must reproduce the above copyright notice, this list of conditions and -// the following disclaimer in the documentation and/or other materials provided -// with the distribution. Neither the name Crypto-JS nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. THIS SOFTWARE IS PROVIDED -// BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -// EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, -// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -if (typeof goog != 'undefined' && typeof goog.provide != 'undefined') { - goog.provide('camlistore.SHA1'); - - goog.require('camlistore.Crypto'); -} - -(function(){ - -// Shortcuts -var C = Crypto, - util = C.util, - charenc = C.charenc, - UTF8 = charenc.UTF8, - Binary = charenc.Binary; - -// Public API -var SHA1 = C.SHA1 = function (message, options) { - var digestbytes = util.wordsToBytes(SHA1._sha1(message)); - return options && options.asBytes ? digestbytes : - options && options.asString ? Binary.bytesToString(digestbytes) : - util.bytesToHex(digestbytes); -}; - -// The core -SHA1._sha1 = function (message) { - - // Convert to byte array - if (message.constructor == String) message = UTF8.stringToBytes(message); - /* else, assume byte array already */ - - var m = util.bytesToWords(message), - l = message.length * 8, - w = [], - H0 = 1732584193, - H1 = -271733879, - H2 = -1732584194, - H3 = 271733878, - H4 = -1009589776; - - // Padding - m[l >> 5] |= 0x80 << (24 - l % 32); - m[((l + 64 >>> 9) << 4) + 15] = l; - - for (var i = 0; i < m.length; i += 16) { - - var a = H0, - b = H1, - c = H2, - d = H3, - e = H4; - - for (var j = 0; j < 80; j++) { - - if (j < 16) w[j] = m[i + j]; - else { - var n = w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16]; - w[j] = (n << 1) | (n >>> 31); - } - - var t = ((H0 << 5) | (H0 >>> 27)) + H4 + (w[j] >>> 0) + ( - j < 20 ? (H1 & H2 | ~H1 & H3) + 1518500249 : - j < 40 ? (H1 ^ H2 ^ H3) + 1859775393 : - j < 60 ? (H1 & H2 | H1 & H3 | H2 & H3) - 1894007588 : - (H1 ^ H2 ^ H3) - 899497514); - - H4 = H3; - H3 = H2; - H2 = (H1 << 30) | (H1 >>> 2); - H1 = H0; - H0 = t; - - } - - H0 += a; - H1 += b; - H2 += c; - H3 += d; - H4 += e; - - } - - return [H0, H1, H2, H3, H4]; - -}; - -// Package private blocksize -SHA1._blocksize = 16; - -})(); diff --git a/server/camlistored/ui/base64.js b/server/camlistored/ui/base64.js deleted file mode 100644 index 8609ea7e8..000000000 --- a/server/camlistored/ui/base64.js +++ /dev/null @@ -1,220 +0,0 @@ -/* -Copyright (c) 2008 Fred Palmer fred.palmer_at_gmail.com - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. -*/ - -if (typeof goog != 'undefined' && typeof goog.provide != 'undefined') { - goog.provide('camlistore.base64'); -} - -/** - * @constructor - */ -function StringBuffer() -{ - this.buffer = []; -} - -StringBuffer.prototype.append = function append(string) -{ - this.buffer.push(string); - return this; -}; - -StringBuffer.prototype.toString = function toString() -{ - return this.buffer.join(""); -}; - -var Base64 = -{ - codex : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", - - encode : function (input) - { - var output = new StringBuffer(); - - var enumerator = new Utf8EncodeEnumerator(input); - while (enumerator.moveNext()) - { - var chr1 = enumerator.current; - - enumerator.moveNext(); - var chr2 = enumerator.current; - - enumerator.moveNext(); - var chr3 = enumerator.current; - - var enc1 = chr1 >> 2; - var enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); - var enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); - var enc4 = chr3 & 63; - - if (isNaN(chr2)) - { - enc3 = enc4 = 64; - } - else if (isNaN(chr3)) - { - enc4 = 64; - } - - output.append(this.codex.charAt(enc1) + this.codex.charAt(enc2) + this.codex.charAt(enc3) + this.codex.charAt(enc4)); - } - - return output.toString(); - }, - - decode : function (input) - { - // TypedArray usage added by brett@haxor.com 11/27/2010 - var size = 0; - var buffer = new ArrayBuffer(input.length); - var output = new Uint8Array(buffer, 0); - - var enumerator = new Base64DecodeEnumerator(input); - while (enumerator.moveNext()) { - output[size++] = enumerator.current; - } - - // There is nothing in the TypedArray spec to copy/subset a buffer, - // so we have to do a copy to ensure that typedarray.buffer is the - // correct length when passed to XmlHttpRequest methods, etc. - var outputBuffer = new ArrayBuffer(size); - var outputArray = new Uint8Array(outputBuffer, 0); - for (var i = 0; i < size; i++) { - outputArray[i] = output[i]; - } - return outputArray; - } -} - - -/** - * @constructor - */ -function Utf8EncodeEnumerator(input) -{ - this._input = input; - this._index = -1; - this._buffer = []; -} - -Utf8EncodeEnumerator.prototype = -{ - current: Number.NaN, - - moveNext: function() - { - if (this._buffer.length > 0) - { - this.current = this._buffer.shift(); - return true; - } - else if (this._index >= (this._input.length - 1)) - { - this.current = Number.NaN; - return false; - } - else - { - var charCode = this._input.charCodeAt(++this._index); - - // "\r\n" -> "\n" - // - if ((charCode == 13) && (this._input.charCodeAt(this._index + 1) == 10)) - { - charCode = 10; - this._index += 2; - } - - if (charCode < 128) - { - this.current = charCode; - } - else if ((charCode > 127) && (charCode < 2048)) - { - this.current = (charCode >> 6) | 192; - this._buffer.push((charCode & 63) | 128); - } - else - { - this.current = (charCode >> 12) | 224; - this._buffer.push(((charCode >> 6) & 63) | 128); - this._buffer.push((charCode & 63) | 128); - } - - return true; - } - } -} - -/** - * @constructor - */ -function Base64DecodeEnumerator(input) -{ - this._input = input; - this._index = -1; - this._buffer = []; -} - -Base64DecodeEnumerator.prototype = -{ - current: 64, - - moveNext: function() - { - if (this._buffer.length > 0) - { - this.current = this._buffer.shift(); - return true; - } - else if (this._index >= (this._input.length - 1)) - { - this.current = 64; - return false; - } - else - { - var enc1 = Base64.codex.indexOf(this._input.charAt(++this._index)); - var enc2 = Base64.codex.indexOf(this._input.charAt(++this._index)); - var enc3 = Base64.codex.indexOf(this._input.charAt(++this._index)); - var enc4 = Base64.codex.indexOf(this._input.charAt(++this._index)); - - var chr1 = (enc1 << 2) | (enc2 >> 4); - var chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); - var chr3 = ((enc3 & 3) << 6) | enc4; - - this.current = chr1; - - if (enc3 != 64) - this._buffer.push(chr2); - - if (enc4 != 64) - this._buffer.push(chr3); - - return true; - } - } -}; diff --git a/server/camlistored/ui/blob.js b/server/camlistored/ui/blob.js new file mode 100644 index 000000000..df19434a8 --- /dev/null +++ b/server/camlistored/ui/blob.js @@ -0,0 +1,66 @@ +/* +Copyright 2014 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +goog.provide('camlistore.blob'); + +goog.require('goog.crypt'); +goog.require('goog.crypt.Sha1'); + +// Returns the Camlistore blobref for hash object. The only supported hash function is currently sha1, but more might be added later. +// @param {!goog.crypt.Hash} hash +// @returns {!string} +camlistore.blob.refFromHash = function(hash) { + if (hash instanceof goog.crypt.Sha1) { + return 'sha1-' + goog.crypt.byteArrayToHex(hash.digest()); + } + throw new Error('Unsupported hash function type'); +}; + +// Returns the Camlistore blobref for a string using the currently recommended hash function. +// @param {!string} str +// @returns {!string} +camlistore.blob.refFromString = function(str) { + var hash = camlistore.blob.createHash(); + hash.update(str); + return camlistore.blob.refFromHash(hash); +}; + +// Returns the Camlistore blobref for a DOM blob (different from Camlistore blob) using the currently recommended hash function. This function currently only works within workers. +// @param {Blob} blob +// @returns {!string} +camlistore.blob.refFromDOMBlob = function(blob) { + if (!goog.global.FileReaderSync) { + // TODO(aa): If necessary, we can also implement this using FileReader for use on the main thread. But beware that should not be done for very large objects without checking the effect on framerate carefully. + throw new Error('FileReaderSync not available. Perhaps we are on the main thread?'); + } + + var fr = new FileReaderSync(); + var hash = camlistore.blob.createHash(); + var chunkSize = 1024 * 1024; + for (var start = 0; start < blob.size; start += chunkSize) { + var end = Math.min(start + chunkSize, blob.size); + var slice = blob.slice(start, end); + hash.update(new Uint8Array(fr.readAsArrayBuffer(slice))); + } + + return camlistore.blob.refFromHash(hash); +}; + +// Creates an instance of the currently recommened hash function. +// @return {!goog.crypt.Hash'} +camlistore.blob.createHash = function() { + return new goog.crypt.Sha1(); +}; diff --git a/server/camlistored/ui/blobinfo.html b/server/camlistored/ui/blobinfo.html index 43a90e4ab..f3b810ee4 100644 --- a/server/camlistored/ui/blobinfo.html +++ b/server/camlistored/ui/blobinfo.html @@ -5,11 +5,6 @@ - - - - - - - - - - diff --git a/server/camlistored/ui/filetree.html b/server/camlistored/ui/filetree.html index 38efaf608..5ed4e84cc 100644 --- a/server/camlistored/ui/filetree.html +++ b/server/camlistored/ui/filetree.html @@ -5,11 +5,6 @@ - - - - - diff --git a/server/camlistored/ui/hash_worker.js b/server/camlistored/ui/hash_worker.js new file mode 100644 index 000000000..0bb9ec792 --- /dev/null +++ b/server/camlistored/ui/hash_worker.js @@ -0,0 +1,30 @@ +/* +Copyright 2014 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// These two lines are required setup to make goog.require() work throughout the codebase. +var CLOSURE_BASE_PATH = 'closure/goog/'; +importScripts('closure/goog/bootstrap/webworkers.js', 'closure/goog/base.js', 'deps.js'); + +goog.require('camlistore.blob'); +goog.require('camlistore.WorkerMessageRouter'); + +// This is a simple webworker that expects to receive a single message containing a file, and sends back that file's sha1 hash. +// We do this in a worker because we observed that doing it on the main thread decreased the framerate significantly, even when chunking, and even when the chunk sizes were as small as 32k. + +var router = new camlistore.WorkerMessageRouter(goog.global); +router.registerHandler('ref', function(msg, sendReply) { + sendReply(camlistore.blob.refFromDOMBlob(msg)); +}); diff --git a/server/camlistored/ui/index.html b/server/camlistored/ui/index.html index 9a0722d23..67f89647f 100644 --- a/server/camlistored/ui/index.html +++ b/server/camlistored/ui/index.html @@ -11,12 +11,6 @@ - - - - - - diff --git a/server/camlistored/ui/permanode.html b/server/camlistored/ui/permanode.html index cd061570e..6701e6c6a 100644 --- a/server/camlistored/ui/permanode.html +++ b/server/camlistored/ui/permanode.html @@ -5,11 +5,6 @@ - - - - - diff --git a/server/camlistored/ui/server_connection.js b/server/camlistored/ui/server_connection.js index c6aae010d..654abd2bd 100644 --- a/server/camlistored/ui/server_connection.js +++ b/server/camlistored/ui/server_connection.js @@ -1,13 +1,14 @@ goog.provide('camlistore.ServerConnection'); -goog.require('camlistore.base64'); -goog.require('camlistore.SHA1'); goog.require('goog.string'); goog.require('goog.net.XhrIo'); goog.require('goog.Uri'); // because goog.net.XhrIo forgot to include it. goog.require('goog.debug.ErrorHandler'); // because goog.net.Xhrio forgot to include it. goog.require('goog.uri.utils'); + +goog.require('camlistore.blob'); goog.require('camlistore.ServerType'); +goog.require('camlistore.WorkerMessageRouter'); // @fileoverview Connection to the blob server and API for the RPCs it provides. All blob index UI code should use this connection to contact the server. // @param {camlistore.ServerType.DiscoveryDocument} config Discovery document for the current server. @@ -16,6 +17,15 @@ goog.require('camlistore.ServerType'); camlistore.ServerConnection = function(config, opt_sendXhr) { this.config_ = config; this.sendXhr_ = opt_sendXhr || goog.net.XhrIo.send; + this.worker_ = null; +}; + +camlistore.ServerConnection.prototype.getWorker_ = function() { + if (!this.worker_) { + var r = new Date().getTime(); // For cachebusting the worker. Sigh. We need content stamping. + this.worker_ = new camlistore.WorkerMessageRouter(new Worker('hash_worker.js?r=' + r)); + } + return this.worker_; }; camlistore.ServerConnection.prototype.getConfig = function() { @@ -286,7 +296,7 @@ camlistore.ServerConnection.prototype.handlePost_ = function(success, opt_fail, // @param {Function} success Success callback. // @param {?Function} opt_fail Optional fail callback. camlistore.ServerConnection.prototype.uploadString_ = function(s, success, opt_fail) { - var blobref = "sha1-" + Crypto.SHA1(s); + var blobref = camlistore.blob.refFromString(s); var parts = [s]; var bb = new Blob(parts); var fd = new FormData(); @@ -443,27 +453,12 @@ camlistore.ServerConnection.prototype.newDelAttributeClaim = function(permanode, // @param {?Function} opt_fail Optional fail callback. // @param {?Function} opt_onContentsRef Optional callback to set contents during upload. camlistore.ServerConnection.prototype.uploadFile = function(file, success, opt_fail, opt_onContentsRef) { - var fr = new FileReader(); - var onload = function() { - var dataurl = fr.result; - var comma = dataurl.indexOf(","); - if (comma != -1) { - var b64 = dataurl.substring(comma + 1); - var arrayBuffer = Base64.decode(b64).buffer; - var hash = Crypto.SHA1(new Uint8Array(arrayBuffer, 0)); - - var contentsRef = "sha1-" + hash; - if (opt_onContentsRef) { - opt_onContentsRef(contentsRef); - } - this.camliUploadFileHelper_(file, contentsRef, success, this.safeFail_(opt_fail)); + this.getWorker_().sendMessage('ref', file, function(ref) { + if (opt_onContentsRef) { + opt_onContentsRef(ref); } - }; - fr.onload = goog.bind(onload, this); - fr.onerror = function() { - console.log("FileReader onerror: " + fr.error + " code=" + fr.error.code); - }; - fr.readAsDataURL(file); + this.camliUploadFileHelper_(file, ref, success, this.safeFail_(opt_fail)); + }.bind(this)); }; // camliUploadFileHelper uploads the provided file with contents blobref contentsBlobRef diff --git a/server/camlistored/ui/worker_message_router.js b/server/camlistored/ui/worker_message_router.js new file mode 100644 index 000000000..b05fcbcc7 --- /dev/null +++ b/server/camlistored/ui/worker_message_router.js @@ -0,0 +1,101 @@ +/* +Copyright 2014 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +goog.provide('camlistore.WorkerMessageRouter'); + +goog.require('goog.string'); + +// Convenience for sending request/response style messages to and from workers. +// @param {!Worker} worker The DOM worker to wrap. +// @constructor +camlistore.WorkerMessageRouter = function(worker) { + this.worker_ = worker; + this.nextMessageId_ = 1; + + // name->handler - See registerHandler() + // @type Object. + this.handlers_ = {}; + + // messageid->callback - See sendMessage() + // @type Object. + this.pendingMessages_ = {}; + + this.worker_.addEventListener('message', this.handleMessage_.bind(this)); +}; + +// Send a message over the worker, optionally expecting a response. +// @param {!string} name The name of the message to send. +// @param {!*} msg The message content +// @param {?function(*)} opt_callback The function to receive the response. +camlistore.WorkerMessageRouter.prototype.sendMessage = function(name, msg, opt_callback) { + var messageId = 0; + if (opt_callback) { + messageId = this.nextMessageId_++; + this.pendingMessages_[messageId] = opt_callback; + } + this.worker_.postMessage({ + messageId: messageId, + name: name, + message: msg + }); +}; + +// Registers a function to handle a particular named message type. +// @param {!string} name The name of the message type to handle. +// @param {!function(*, function(*))} handler The function to call to return the reply to the client. +camlistore.WorkerMessageRouter.prototype.registerHandler = function(name, handler) { + this.handlers_[name] = handler; +}; + +camlistore.WorkerMessageRouter.prototype.handleMessage_ = function(e) { + if (!goog.isObject(e.data) || !goog.isDef(e.data.messageId)) { + return; + } + + if (goog.isDef(e.data.name)) { + this.handleRequest_(e.data); + } else { + this.handleReply_(e.data); + } +}; + +camlistore.WorkerMessageRouter.prototype.handleRequest_ = function(request) { + var handler = this.handlers_[request.name]; + if (!handler) { + throw new Error(goog.string.subs('No registered handler with name: %s', request.name)); + } + + var sendReply = function(reply) { + if (!request.messageId) { + return; + } + this.worker_.postMessage({ + messageId: request.messageId, + message: reply + }); + }.bind(this); + + handler(request.message, sendReply); +}; + +camlistore.WorkerMessageRouter.prototype.handleReply_ = function(reply) { + var callback = this.pendingMessages_[reply.messageId]; + if (!callback) { + throw new Error('Could not find callback for pending message: %s', reply.messageId); + } + delete this.pendingMessages_[reply.messageId]; + callback(reply.message); +};