Skip to content
This repository has been archived by the owner on Apr 1, 2019. It is now read-only.

Commit

Permalink
Cache directory tiles in Service Worker (closes #94)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcoscaceres committed Oct 9, 2015
1 parent 59a76bd commit 95d7738
Show file tree
Hide file tree
Showing 6 changed files with 222 additions and 195 deletions.
17 changes: 11 additions & 6 deletions bin/generate-offline-files.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
const path = require('path');
const fs = require("fs");
const exec = require("child_process").exec;
const fileName = "js/mainSiteFiles.js";
const fileName = "js/mainSiteURLs.js";

// Find css and JS files
const findJs = fileFinder(`js`, `-iname "*.js"`);
Expand Down Expand Up @@ -38,11 +38,12 @@ function processResults(results) {
}

function generateTemplate(paths) {
var mainSiteFiles = `/**
var contents = `/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
/*exports mainSiteFiles*/
/*global self*/
/*exports mainSiteURLs*/
/**
* THIS FILE IS AUTOMATICALLY GENERATED!
Expand All @@ -51,12 +52,16 @@ function generateTemplate(paths) {
* See: ${path.relative(__dirname + "/..", __filename)}
*/
"use strict";
const mainSiteFiles = [ // jshint ignore:line
const mainSiteURLs = [ // jshint ignore:line
"./",
"sw.js",
${paths.join("\n")}
];
].map(
// Using URL API is very slow in workers because it does IPC.
path => self.location.origin + "/" + path
);
`;
return Promise.resolve(mainSiteFiles.trim());
return Promise.resolve(contents);
}

function writeFile(template) {
Expand Down
277 changes: 140 additions & 137 deletions js/lib/cachetasks.js
Original file line number Diff line number Diff line change
@@ -1,151 +1,154 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
/*globals async, caches, fetch, Response, Request*/
/*globals async, caches, fetch, Response, Request, self*/
/*exported CacheTasks */

"use strict";

/**
* Collection of common task performed by a Service Worker cache.
*/
const CacheTasks = {
proxyRemoteResponse: async(function*(request, remoteURL, cacheName) {
var options = {};
if (cacheName) {
options.cacheName = cacheName;
}
var response = yield caches.match(request, options);
// We already stored this, so return it.
if (response) {
return response;
}
try {
response = yield fetch(remoteURL);
yield cache.put(request, response);
} catch (err) {
var msg = `failed to store ${request} in ${cacheName}.`;
console.warn(msg, err);
throw err;
}
return response;
}),
/**
* Populates the a cache with a list of requests.
*
* @param {String} cacheName The name of the cache.
* @param {Array} requests The requests (URLs or Requests) to cache.
*/
populateCache: async(function* (cacheName, requests) {
var cache = yield caches.open(cacheName);
var success = true;
try {
yield cache.addAll(requests);
} catch (err) {
var msg = `Error adding resources to cache ${cacheName}.`;
console.warn(msg, err);
success = false;
}
return success;
}),
(function(exports) {
/**
* Saves a binary file into a cache.
*
* @param {String} cacheName The name of the cache to save into.
* @param {ArrayBuffer} arrayBuffer The arrayBuffer holding the file's data.
* @param {String} type MimeType of the data being stored.
* @param {String|URL} requestURL The URL this request maps to.
* Collection of common task performed by a Service Worker cache.
*/
saveBinaryToCache: async(function* (cacheName, arrayBuffer, type, requestURL) {
var cache = yield caches.open(cacheName);
var dataView = new DataView(arrayBuffer);
var blob = new Blob([dataView], {
type
});
var responseInit = {
headers: {
"Content-Type": type,
const openedCaches = new Map();
const memoizedCaches = {
open: async(function*(cacheName) {
if (openedCaches.has(cacheName)) {
return openedCaches.get(cacheName);
}
};
var request = new Request(requestURL);
var response = new Response(blob, responseInit);
try {
yield cache.put(request, response);
} catch (err) {
var msg = `putting blob in cache ${cacheName} for ${requestURL}.`;
console.warn(msg, err);
throw err;
}
}),
/**
* Respond to a request from the SW's caches.
*
* @param {Request|String} request The request
* @param {String} [cacheName] The name of the cache to look in.
* @param {String} [strategy] The strategy to use when the response
* is not found. "throw" causes this to throw
* otherwise, it passes the request to the
* network via fetch.
*/
respondFromCache: async(function* (request, cacheName, strategy = "") {
var options = {};
if (cacheName) {
options.cacheName = cacheName;
}
var response = yield caches.match(request, options);
switch (strategy) {
case "throw":
if (!response) {
var msg = `Not found in ${cacheName} cache: ${request.url || request}`;
var err = new Error(msg);
console.warn(err);
var cache = yield caches.open(cacheName);
openedCaches.set(cacheName, cache);
return cache;
})
};

const CacheTasks = {
/**
* Populates the a cache with a list of requests.
*
* @param {String} cacheName The name of the cache.
* @param {Array} requests The requests (URLs or Requests) to cache.
*/
populateCache: async(function* (cacheName, requests) {
var cache = yield memoizedCaches.open(cacheName);
var success = true;
try {
yield cache.addAll(requests);
} catch (err) {
var msg = `Error adding resources to cache ${cacheName}.`;
console.warn(msg, err);
success = false;
}
return success;
}),
/**
* Saves a binary file into a cache.
*
* @param {String} cacheName The name of the cache to save into.
* @param {ArrayBuffer} arrayBuffer The arrayBuffer holding the file's data.
* @param {String} type MimeType of the data being stored.
* @param {String|URL} requestURL The URL this request maps to.
*/
saveBinaryToCache: async(function* (cacheName, arrayBuffer, type, requestURL) {
var cache = yield memoizedCaches.open(cacheName);
var blob = new Blob([arrayBuffer], {
type
});
var responseInit = {
headers: {
"Content-Type": type,
}
};
var request = new Request(requestURL);
var response = new Response(blob, responseInit);
try {
yield cache.put(request, response);
} catch (err) {
var msg = `putting blob in cache ${cacheName} for ${requestURL}.`;
console.warn(msg, err);
throw err;
}
break;
//Default passes the request to network using fetch()
default:
if (!response) {
var url = request.url || request;
var msg = `Not in cache ${cacheName}. Fetching from network: ${url}`;
}),
/**
* Respond to a request from the SW's caches.
*
* @param {Request|String} request The request
* @param {String} [cacheName] The name of the cache to look in.
* @param {String} [strategy] The strategy to use when the response
* is not found. "throw" causes this to throw
* otherwise, it passes the request to the
* network via fetch.
*/
respondFromCache: async(function* (request, cacheName, strategy = "") {
var cache = yield memoizedCaches.open(cacheName);
var url = request.url || request;
var response = yield cache.match(request);
var msg = "";
if (response) {
return response;
}
switch (strategy) {
case "throw":
msg = `Not found in ${cacheName} cache: ${url}`;
var error = new Error(msg);
console.warn(error);
throw error;
case "store":
try {
response = yield fetch(request);
yield cache.put(request, response.clone());
} catch (err) {
var msg = `failed to store ${url} in ${cacheName}.`;
console.warn(msg, err);
throw err;
}
break;
//Default passes the request to network using fetch()
default:
msg = `Not in cache ${cacheName}. Fetching but not storing: ${url}`;
console.warn(msg);
response = yield fetch(request);
break;
}
break;
}
return response;
}),
/**
* Checks if there is a cache entry for a particular request.
*
* @param {Request|String} request The request to check for.
* @param {String} [cacheName] The cache's name to look in.
* @return {Boolean} True if it has the request, false otherwise.
*/
hasCacheEntry: async(function* (request, cacheName) {
var cache = yield caches.open(cacheName);
var response = yield cache.match(request);
return (response) ? true : false;
}),
/**
* Deletes a cache entry.
*
* @param {Request[]|String[]} request The request.
* @param {String} cacheName The cache name from where to delete.
* @returns {Booleam}
*/
deleteCacheEntry: async(function* (request, cacheName, options={}) {
var cache = yield caches.open(cacheName);
var result = yield cache.delete(request, options);
return result;
}),
/**
* Delete all the SW's caches.
*/
deleteAllCaches: async(function* () {
var keys = yield caches.keys();
for (var name of keys) {
caches.delete(name);
}
return true;
}),
};
return response;
}),
/**
* Checks if there is a cache entry for a particular request.
*
* @param {Request|String} request The request to check for.
* @param {String} [cacheName] The cache's name to look in.
* @return {Boolean} True if it has the request, false otherwise.
*/
hasCacheEntry: async(function* (request, cacheName) {
var cache = yield memoizedCaches.open(cacheName);
var response = yield cache.match(request);
return (response) ? true : false;
}),
/**
* Deletes a cache entry.
*
* @param {Request[]|String[]} request The request.
* @param {String} cacheName The cache name from where to delete.
* @returns {Boolean}
*/
deleteCacheEntry: async(function* (request, cacheName, options={}) {
var cache = yield memoizedCaches.open(cacheName);
var result = yield cache.delete(request, options);
return result;
}),
/**
* Delete all the SW's caches.
*
* @returns {Map<String,Boolean>} A map representing the keys and the result
* of deleting the cache.
*/
deleteAllCaches: async(function* () {
var keys = yield caches.keys();
var promises = keys.map(key => caches.delete(key));
var results = yield Promise.all(promises);
var keyResult = new Map();
keys.forEach((key, index) => keyResult.set(key, results[index]));
return keyResult;
}),
};
exports.CacheTasks = CacheTasks;
}(self));
13 changes: 9 additions & 4 deletions js/mainSiteFiles.js → js/mainSiteURLs.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
/*exports mainSiteFiles*/
/*global self*/
/*exports mainSiteURLs*/

/**
* THIS FILE IS AUTOMATICALLY GENERATED!
Expand All @@ -11,8 +12,9 @@
* See: bin/generate-offline-files.js
*/
"use strict";
const mainSiteFiles = [ // jshint ignore:line
const mainSiteURLs = [ // jshint ignore:line
"./",
"sw.js",
"css/contentSearchUI.css",
"css/images/close.png",
"css/images/controls.svg",
Expand All @@ -38,7 +40,7 @@ const mainSiteFiles = [ // jshint ignore:line
"js/lib/async.js",
"js/lib/cachetasks.js",
"js/lib/swMessage.js",
"js/mainSiteFiles.js",
"js/mainSiteURLs.js",
"js/newTab.js",
"js/page.js",
"js/pinnedLinks.js",
Expand All @@ -50,4 +52,7 @@ const mainSiteFiles = [ // jshint ignore:line
"js/updater.js",
"js/userDatabase.js",
"locale/newTab.js",
];
].map(
// Using URL API is very slow in workers because it does IPC.
path => self.location.origin + "/" + path
);

0 comments on commit 95d7738

Please sign in to comment.