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

Add service worker for offline access to editor #1984

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 104 additions & 1 deletion Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ module.exports = function( grunt ) {
gitadd: 'grunt-git'
});

var swPrecache = require('sw-precache');
var Path = require('path');
var fs = require('fs');

grunt.initConfig({
pkg: grunt.file.readJSON( "package.json" ),

Expand Down Expand Up @@ -131,10 +135,109 @@ module.exports = function( grunt ) {
]
}
}
},
swPrecache: {
dist: {
rootDir: 'dist'
}
}
});

grunt.registerMultiTask('swPrecache', function() {
var done = this.async();
var rootDir = this.data.rootDir;

// We need the full list of locales so we can create runtime caching rules for each
fs.readdir('locales', function(err, locales) {
if(err) {
grunt.fail.warn(err);
return done();
}

locales = locales.map(function(locale) {
// en_US to en-US
return locale.replace('_', '-');
});

// /\/(en-US|pt-BR|es|...)\//
var localesPattern = new RegExp('\/(' + locales.join('|') + ')\/');

writeServiceWorker(getConfig(localesPattern));
});

function getConfig(localesPattern) {
return {
cacheId: 'thimble',
logger: grunt.log.writeln,
staticFileGlobs: [
/* TODO: we need to not localize these asset dirs so we can statically cache
'dist/editor/stylesheets/*.css',
'dist/resources/stylesheets/*.css',
'dist/homepage/stylesheets/*.css'
*/
],
runtimeCaching: [
// TODO: we should be bundling all this vs. loading separate
{
urlPattern: /\/node_modules\//,
handler: 'fastest'
},
{
urlPattern: /\/scripts\/vendor\//,
handler: 'fastest'
},

// TODO: move these to staticFileGlobs--need to figure out runtime path vs. build path issue
{
urlPattern: /\/img\//,
handler: 'fastest'
},
{
urlPattern: /https:\/\/thimble.mozilla.org\/img\//,
handler: 'fastest'
},

// Localization requires runtime caching of rewritten, locale-prefixed URLs
{
urlPattern: localesPattern,
handler: 'fastest'
},

// Various external deps we need
{
urlPattern: /^https:\/\/fonts\.googleapis\.com\/css/,
handler: 'fastest'
},
{
urlPattern: /^https:\/\/fonts\.gstatic\.com\//,
handler: 'fastest'
},
{
urlPattern: /^https:\/\/mozilla.github.io\/thimble-homepage-gallery\/activities.json/,
handler: 'fastest'
},
{
urlPattern: /^https:\/\/pontoon.mozilla.org\/pontoon.js/,
handler: 'fastest'
}
],

ignoreUrlParametersMatching: [/./]
};
}

function writeServiceWorker(config) {
swPrecache.write(Path.join(rootDir, 'thimble-sw.js'), config, function(err) {
if(err) {
grunt.fail.warn(err);
}
done();
});
}

});

grunt.registerTask("test", [ "jshint:server", "jshint:frontend", "lesslint" ]);
grunt.registerTask("build", [ "test", "requirejs:dist" ]);
grunt.registerTask("build", [ "test", "requirejs:dist", "swPrecache" ]);
grunt.registerTask("default", [ "test" ]);
};
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"q-io": "1.13.2",
"request": "2.80.0",
"serve-favicon": "^2.4.1",
"sw-precache": "^5.1.0",
"tar-stream": "^1.5.2",
"throng": "^4.0.0",
"time-grunt": "^1.4.0",
Expand Down
75 changes: 75 additions & 0 deletions public/editor/scripts/editor/js/fc/offline.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* Consolidate all online/offline and Service Worker events from both Thimble and Bramble.
* The events we trigger include:
*
* - `updatesAvailable`: one of Thimble and/or Brackets has updates in the cache, user should reload
* - `online`: the browser has re-established a network connection
* - `offline`: the browser has lost its network connection
*
* You can also use `Offline.isOnline()` to check the online status.
*/
define(function(require) {

var EventEmitter = require("EventEmitter");
var Offline = new EventEmitter();

/**
* Service Worker offline cache registration. The thimble-sw.js file
* is generated by Grunt as part of a dist/ build, and will not do anything
* in dev builds.
*/
function initServiceWorker() {
if (!('serviceWorker' in window.navigator)) {
return;
}

window.navigator.serviceWorker.register('/thimble-sw.js').then(function(reg) {
reg.onupdatefound = function() {
var installingWorker = reg.installing;

installingWorker.onstatechange = function() {
switch (installingWorker.state) {
case 'installed':
if (window.navigator.serviceWorker.controller) {
// Cache has been updated
Offline.trigger("updatesAvailable");
}
break;
case 'redundant':
console.error('[Thimble] The installing service worker became redundant.');
break;
}
};
};
}).catch(function(e) {
"use strict";
console.error('[Bramble] Error during service worker registration:', e);
});
}

Offline.init = function(Bramble) {
initServiceWorker();

// Listen for sw events from Bramble, and consolidate with our own.
Bramble.on("updatesAvailable", function() {
Offline.trigger("updatesAvailable");
});
Bramble.on("offlineReady", function() {
Offline.trigger("offlineReady");
});

// Listen for online/offline events from the browser
window.addEventListener("offline", function() {
Offline.trigger("offline");
}, false);
window.addEventListener("online", function() {
Offline.trigger("online");
});
};

Offline.isOnline = function() {
return navigator.onLine;
};

return Offline;
});
5 changes: 4 additions & 1 deletion public/editor/scripts/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ require.config({
}
});

require(["jquery", "bowser"], function($, bowser) {
require(["jquery", "bowser", "fc/offline"], function($, bowser, Offline) {
// Warn users of unsupported browsers that they can try something newer,
// specifically anything before IE 11 or Safari 8.
if((bowser.msie && bowser.version < 11) || (bowser.safari && bowser.version < 8)) {
Expand All @@ -52,6 +52,9 @@ require(["jquery", "bowser"], function($, bowser) {

Bramble.once("error", onError);

// Initialize offline/online handling
Offline.init(Bramble);

function init(BrambleEditor, Project, SSOOverride, ProjectRenameUtility) {
var thimbleScript = document.getElementById("thimble-script");
var appUrl = thimbleScript.getAttribute("data-app-url");
Expand Down
4 changes: 3 additions & 1 deletion server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ if(!!env.get("FORCE_SSL")) {
*/
Utils.getFileList(path.join(root, "public"), "!(*.js)")
.forEach(file => server.use(express.static(file, maxCacheAge)));
// Don't cache sw script
server.use("/thimble-sw.js", express.static(path.join(root, "public/thimble-sw.js"), { maxAge: 0 }));
server.use(express.static(cssAssets, maxCacheAge));
server.use(express.static(path.join(root, "public/resources"), maxCacheAge));
server.use("/node_modules", express.static(path.join(root, server.locals.node_path), maxCacheAge));
Expand All @@ -111,7 +113,7 @@ server.use("/resources/remix", express.static(path.join(root, "public/resources/
* L10N
*/
localize(server, Object.assign(env.get("L10N"), {
excludeLocaleInUrl: [ "/projects/remix-bar" ]
excludeLocaleInUrl: [ "/projects/remix-bar", "/thimble-sw.js" ]
}));


Expand Down
2 changes: 2 additions & 0 deletions server/security.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ let defaultCSPDirectives = {
defaultSrc: [ "'self'" ],
connectSrc: [
"'self'",
"https://fonts.googleapis.com",
"https://fonts.gstatic.com",
"https://pontoon.mozilla.org",
"https://mozilla.github.io/thimble-homepage-gallery/activities.json"
],
Expand Down