diff --git a/service-worker.js b/service-worker.js index 450e4c5e..1171954e 100644 --- a/service-worker.js +++ b/service-worker.js @@ -25,6 +25,32 @@ // TODO : remove requirejs if it's really useless here importScripts('./www/js/lib/require.js'); +/** + * From https://stackoverflow.com/questions/16245767/creating-a-blob-from-a-base64-string-in-javascript + */ +function b64toBlob(b64Data, contentType, sliceSize) { + contentType = contentType || ''; + sliceSize = sliceSize || 512; + + var byteCharacters = atob(b64Data); + var byteArrays = []; + + for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) { + var slice = byteCharacters.slice(offset, offset + sliceSize); + + var byteNumbers = new Array(slice.length); + for (var i = 0; i < slice.length; i++) { + byteNumbers[i] = slice.charCodeAt(i); + } + + var byteArray = new Uint8Array(byteNumbers); + + byteArrays.push(byteArray); + } + + var blob = new Blob(byteArrays, {type: contentType}); + return blob; +} self.addEventListener('install', function(event) { event.waitUntil(self.skipWaiting()); @@ -55,15 +81,6 @@ function(util, utf8) { console.log('Init message received', event.data); outgoingMessagePort = event.ports[0]; console.log('outgoingMessagePort initialized', outgoingMessagePort); - self.addEventListener('fetch', fetchEventListener); - console.log('fetchEventListener enabled'); - } - if (event.data.action === 'disable') { - console.log('Disable message received'); - outgoingMessagePort = null; - console.log('outgoingMessagePort deleted'); - self.removeEventListener('fetch', fetchEventListener); - console.log('fetchEventListener removed'); } }); @@ -76,13 +93,13 @@ function(util, utf8) { var regexpContentUrl = new RegExp(/\/(.)\/(.*[^\/]+)$/); var regexpDummyArticle = new RegExp(/dummyArticle\.html$/); - - function fetchEventListener(event) { - console.log('ServiceWorker handling fetch event for : ' + event.request.url); + self.addEventListener('fetch', function(event) { + console.log('ServiceWorker handling fetch event for : ' + event.request.url); + // TODO handle the dummy article more properly if (regexpContentUrl.test(event.request.url) && !regexpDummyArticle.test(event.request.url)) { - + console.log('Asking app.js for a content', event.request.url); event.respondWith(new Promise(function(resolve, reject) { var regexpResult = regexpContentUrl.exec(event.request.url); @@ -113,19 +130,6 @@ function(util, utf8) { else if (regexpCSS.test(titleName)) { contentType = 'image/css'; } - var responseInit = { - status: 200, - statusText: 'OK', - headers: { - 'Content-Type': contentType - } - }; - - var httpResponse = new Response(';', responseInit); - - // TODO : temporary before the backend actually sends a proper content - resolve(httpResponse); - return; } // Let's instanciate a new messageChannel, to allow app.s to give us the content @@ -158,5 +162,6 @@ function(util, utf8) { } // If event.respondWith() isn't called because this wasn't a request that we want to handle, // then the default request/response behavior will automatically be used. - } + }); + }); diff --git a/tests/tests.js b/tests/tests.js index dbfa2d90..9b8b1b35 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -532,5 +532,19 @@ define(['jquery', 'title', 'archive', 'zimArchive', 'zimDirEntry', 'util', 'geom }); }); }); + asyncTest("Image 's/style.css' can be loaded", function() { + expect(4); + localZimArchive.getTitleByName("-/s/style.css").then(function(title) { + ok(title !== null, "Title found"); + equal(title.url, "-/s/style.css", "URL is correct."); + localZimArchive.readBinaryFile(title, function(title, data) { + equal(data.length, 104495, "Data length is correct."); + data = utf8.parse(data); + var beginning = "\n/* start http://en.wikipedia.org/w/load.php?debug=false&lang=en&modules=site&only=styles&skin=vector"; + equal(data.slice(0, beginning.length), beginning, "Content starts correctly."); + start(); + }); + }); + }); }; }); diff --git a/www/js/app.js b/www/js/app.js index eb6ed495..a2b8ed7f 100644 --- a/www/js/app.js +++ b/www/js/app.js @@ -864,11 +864,11 @@ define(['jquery', 'abstractBackend', 'util', 'cookies','geometry','osabstraction console.log("Reading binary file..."); selectedArchive.readBinaryFile(title, function(readableTitleName, content) { messagePort.postMessage({'action': 'giveContent', 'titleName' : titleName, 'content': content}); - console.log("content sent to ServiceWorker)"); + console.log("content sent to ServiceWorker"); }); } } - console.log("Fetching tile " + titleName); + console.log("Fetching title " + titleName); selectedArchive.getTitleByName(titleName).then(readFile).fail(function() { console.log("could not find title:" + arguments); messagePort.postMessage({'action': 'giveContent', 'titleName' : titleName, 'content': new UInt8Array()}); diff --git a/www/js/lib/archive.js b/www/js/lib/archive.js index 2d2ba606..3dd103a6 100644 --- a/www/js/lib/archive.js +++ b/www/js/lib/archive.js @@ -338,6 +338,7 @@ define(['normalize_string', 'geometry', 'title', 'util', 'titleIterators', 'q'], * Look for a title by its name, and call the callbackFunction with this Title * If the title is not found, the callbackFunction is called with parameter null * @param {String} titleName + * @return {Promise} resolving to the title object or null if not found. */ LocalArchive.prototype.getTitleByName = function(titleName) { var that = this; diff --git a/www/js/lib/xzdec_wrapper.js b/www/js/lib/xzdec_wrapper.js index 148ad582..59d4179d 100644 --- a/www/js/lib/xzdec_wrapper.js +++ b/www/js/lib/xzdec_wrapper.js @@ -43,10 +43,6 @@ define(['q'], function(q) { function Decompressor(reader, chunkSize) { this._chunkSize = chunkSize || 1024 * 5; this._reader = reader; - this._decHandle = xzdec._init_decompression(this._chunkSize); - this._inStreamPos = 0; - this._outStreamPos = 0; - this._outBuffer = null; }; /** * Read length bytes, offset into the decompressed stream. Consecutive calls may only @@ -55,15 +51,16 @@ define(['q'], function(q) { * @param {Integer} length */ Decompressor.prototype.readSlice = function(offset, length) { + var that = this; + this._inStreamPos = 0; + this._outStreamPos = 0; + this._decHandle = xzdec._init_decompression(this._chunkSize); this._outBuffer = new Int8Array(new ArrayBuffer(length)); this._outBufferPos = 0; - return this._readLoop(offset, length); - }; - /** - * Finish the decompressing and release resources. - */ - Decompressor.prototype.end = function() { - xzdec._release(this._decHandle); + return this._readLoop(offset, length).then(function(data) { + xzdec._release(that._decHandle); + return data; + }); }; /** @@ -88,23 +85,19 @@ define(['q'], function(q) { } var outPos = xzdec._get_out_pos(that._decHandle); - if (finished || outPos === that._chunkSize || that._outStreamPos + outPos >= offset + length) { - if (that._outStreamPos + outPos >= offset) - { - var outBuffer = xzdec._get_out_buffer(that._decHandle); - var copyStart = offset - that._outStreamPos; - if (copyStart < 0) - copyStart = 0; - for (var i = copyStart; i < outPos && that._outBufferPos < that._outBuffer.length; i++) - that._outBuffer[that._outBufferPos++] = xzdec.HEAP8[outBuffer + i]; - } - if (outPos === that._chunkSize) - { - that._outStreamPos += outPos; - xzdec._out_buffer_cleared(that._decHandle); - } + if (outPos > 0 && that._outStreamPos + outPos >= offset) + { + var outBuffer = xzdec._get_out_buffer(that._decHandle); + var copyStart = offset - that._outStreamPos; + if (copyStart < 0) + copyStart = 0; + for (var i = copyStart; i < outPos && that._outBufferPos < that._outBuffer.length; i++) + that._outBuffer[that._outBufferPos++] = xzdec.HEAP8[outBuffer + i]; } - if (finished || that._outStreamPos + outPos >= offset + length) + that._outStreamPos += outPos; + if (outPos > 0) + xzdec._out_buffer_cleared(that._decHandle); + if (finished || that._outStreamPos >= offset + length) return that._outBuffer; else return that._readLoop(offset, length); diff --git a/www/js/lib/zimArchive.js b/www/js/lib/zimArchive.js index 07114f03..46e0e5a4 100644 --- a/www/js/lib/zimArchive.js +++ b/www/js/lib/zimArchive.js @@ -198,8 +198,9 @@ define(['zimfile', 'zimDirEntry', 'util', 'utf8'], }; /** - * + * Searches a title (article / page) by name. * @param {String} titleName + * @return {Promise} resolving to the title object or null if not found. */ ZIMArchive.prototype.getTitleByName = function(titleName) { var that = this; diff --git a/www/js/lib/zimfile.js b/www/js/lib/zimfile.js index b0ff2f55..c7f5d196 100644 --- a/www/js/lib/zimfile.js +++ b/www/js/lib/zimfile.js @@ -154,7 +154,6 @@ define(['xzdec_wrapper', 'util', 'utf8'], function(xz, util, utf8) { ZIMFile.prototype.blob = function(cluster, blob) { var that = this; - //@todo decompress in a streaming way, otherwise we have to "guess" the sizes return this._readSlice(this.clusterPtrPos + cluster * 8, 16).then(function(clusterOffsets) { var clusterOffset = readInt(clusterOffsets, 0, 8); @@ -166,7 +165,7 @@ define(['xzdec_wrapper', 'util', 'utf8'], function(xz, util, utf8) { }; if (compressionType[0] === 0 || compressionType[0] === 1) { // uncompressed - decompressor = { readSlice: plainBlobReader, end: function() {} }; + decompressor = { readSlice: plainBlobReader }; } else if (compressionType[0] === 4) { decompressor = new xz.Decompressor(plainBlobReader); } else { @@ -175,10 +174,7 @@ define(['xzdec_wrapper', 'util', 'utf8'], function(xz, util, utf8) { return decompressor.readSlice(blob * 4, 8).then(function(data) { var blobOffset = readInt(data, 0, 4); var nextBlobOffset = readInt(data, 4, 4); - return decompressor.readSlice(blobOffset, nextBlobOffset - blobOffset).then(function(data) { - decompressor.end(); - return data; - }); + return decompressor.readSlice(blobOffset, nextBlobOffset - blobOffset); }); }); });