Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
507 lines
14 KiB
JavaScript
507 lines
14 KiB
JavaScript
(function(exports) {
|
|
'use strict';
|
|
|
|
/**
|
|
* Return the current application object
|
|
*/
|
|
var _app;
|
|
function getAppObject(cb) {
|
|
if (_app) {
|
|
cb(_app);
|
|
return;
|
|
}
|
|
|
|
var request = navigator.mozApps.getSelf();
|
|
request.onsuccess = function() {
|
|
cb(request.result);
|
|
};
|
|
request.onerror = function() {
|
|
console.warn('about:netError error fetching app: ' + request.error.name);
|
|
cb();
|
|
};
|
|
}
|
|
|
|
function getFrameType(error) {
|
|
return isFramed(error) ? 'framed' : 'no-frame';
|
|
}
|
|
|
|
/**
|
|
* Check if net_error is coming from within an iframe
|
|
*/
|
|
function isFramed(error) {
|
|
var manifestURL = error.m;
|
|
|
|
// frame type values (regular, browser, app)
|
|
var frameType = error.f;
|
|
switch (frameType) {
|
|
|
|
// if we are in a "regular" frame, we are indeed framed
|
|
case 'regular':
|
|
return true;
|
|
|
|
// if we are an "app" frame, we are not framed
|
|
case 'app':
|
|
return false;
|
|
|
|
// If we are in a "browser" frame, we are either in a browser tab/system
|
|
// bookmark, or mozbrowser iframe within in app. Since system
|
|
// bookmarks are considered unframed, we must perform a check here to
|
|
// distinguish between the two cases.
|
|
case 'browser':
|
|
return manifestURL !== window.SYSTEM_MANIFEST;
|
|
|
|
default:
|
|
throw new Error('about:netError: invalid frame type - ' + frameType);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieve the application name, if we are not in the context
|
|
* of an app simply return the URI
|
|
*/
|
|
function getAppName(cb) {
|
|
getAppObject(function(app) {
|
|
if (app && app.manifest.name) {
|
|
cb(app.manifest.name);
|
|
} else {
|
|
cb(location.protocol + '//' + location.host);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Populate element with localized string
|
|
*/
|
|
var localizeElement = navigator.mozL10n.setAttributes;
|
|
|
|
/**
|
|
* Parse the neterror information that's sent to us as part of the documentURI
|
|
* and return an error object.
|
|
*
|
|
* The error object will contain the following attributes:
|
|
* e - Type of error (eg. 'netOffline').
|
|
* u - URL that generated the error.
|
|
* m - Manifest URI of the application that generated the error.
|
|
* c - Character set for default gecko error message (eg. 'UTF-8').
|
|
* d - Default gecko error message.
|
|
* f - The frame type ("regular", "browser", "app")
|
|
*/
|
|
var _error;
|
|
function getErrorFromURI() {
|
|
if (_error) {
|
|
return _error;
|
|
}
|
|
_error = {};
|
|
var uri = document.documentURI;
|
|
|
|
// Quick check to ensure it's the URI format we're expecting.
|
|
if (!uri.startsWith('about:neterror?')) {
|
|
// A blank error will generate the default error message (no network).
|
|
return _error;
|
|
}
|
|
|
|
// Small hack to get the URL object to parse the URI correctly.
|
|
var url = new URL(uri.replace('about:', 'http://'));
|
|
|
|
// Set the error attributes.
|
|
['e', 'm', 'c', 'd', 'f'].forEach(
|
|
function(v) {
|
|
_error[v] = url.searchParams.get(v);
|
|
}
|
|
);
|
|
// URLs must be displayed in LTR
|
|
_error.u = '\u202a' + url.searchParams.get('u') + '\u202c';
|
|
|
|
switch (_error.e) {
|
|
case 'connectionFailure':
|
|
case 'netInterrupt':
|
|
case 'netTimeout':
|
|
case 'netReset':
|
|
_error.e = 'connectionFailed';
|
|
break;
|
|
|
|
case 'unknownSocketType':
|
|
case 'unknownProtocolFound':
|
|
case 'cspFrameAncestorBlocked':
|
|
_error.e = 'invalidConnection';
|
|
break;
|
|
}
|
|
|
|
return _error;
|
|
}
|
|
|
|
/*
|
|
* This method reloads the window if the device is online and in the
|
|
* foreground
|
|
*/
|
|
function reloadIfOnline() {
|
|
if (navigator.onLine && !document.hidden &&
|
|
!document.body.classList.contains('hidden')) {
|
|
document.body.classList.add('hidden');
|
|
NetError.reload(true);
|
|
window.addEventListener('offline', function onOffline() {
|
|
window.removeEventListener('offline', onOffline);
|
|
document.body.classList.remove('hidden');
|
|
});
|
|
}
|
|
}
|
|
|
|
function showSettingsView() {
|
|
var activity = new window.MozActivity({
|
|
name: 'configure',
|
|
data: {
|
|
target: 'device',
|
|
section: 'root',
|
|
filterBy: 'connectivity'
|
|
}
|
|
});
|
|
|
|
activity.onerror = function() {
|
|
console.warn('Configure activity error:', activity.error.name);
|
|
};
|
|
}
|
|
|
|
function hasHistory() {
|
|
return window.history.length > 1;
|
|
}
|
|
|
|
function cancel() {
|
|
hasHistory() ? goBack() : closeWindow();
|
|
}
|
|
|
|
function goBack() {
|
|
window.history.back();
|
|
}
|
|
|
|
function closeWindow() {
|
|
window.close();
|
|
}
|
|
|
|
function addConnectionHandlers() {
|
|
document.addEventListener('visibilitychange', reloadIfOnline);
|
|
window.addEventListener('online', reloadIfOnline);
|
|
}
|
|
|
|
var ErrorView = function(error, title, message) {
|
|
this.error = error;
|
|
this.titleText = title || 'unable-to-connect';
|
|
this.messageText = message || this.error.d;
|
|
this.node = document.getElementById('net-error-confirm-dialog');
|
|
};
|
|
|
|
ErrorView.prototype = {
|
|
applyStyle: function ew_applyStyle() {
|
|
document.body.classList.add(this.error.e);
|
|
},
|
|
|
|
addHandlers: function ew_addHandlers() {
|
|
// Subclasses should add the handlers
|
|
},
|
|
|
|
populateMessages: function ew_populateMessages() {
|
|
localizeElement(this.title, this.titleText);
|
|
localizeElement(this.message, this.messageText);
|
|
},
|
|
|
|
init: function ew_init() {
|
|
window.LazyLoader.load(this.node, (function loaded() {
|
|
this.title = document.getElementById('error-title');
|
|
this.message = document.getElementById('error-message');
|
|
this.applyStyle();
|
|
this.populateMessages();
|
|
this.addHandlers();
|
|
}).bind(this));
|
|
}
|
|
};
|
|
|
|
var FramedErrorView = function(error, title, message) {
|
|
ErrorView.call(this, error, title, message);
|
|
};
|
|
|
|
FramedErrorView.prototype = Object.create(ErrorView.prototype);
|
|
|
|
FramedErrorView.prototype.applyStyle = function few_applyStyle() {
|
|
ErrorView.prototype.applyStyle.call(this);
|
|
document.body.classList.add('framed');
|
|
};
|
|
|
|
FramedErrorView.prototype.addHandlers = function few_addHandlers() {
|
|
document.body.onclick = function bodyClick() {
|
|
document.getElementById('retry-icon').classList.remove('still');
|
|
NetError.reload(true);
|
|
};
|
|
};
|
|
|
|
var AppErrorView = function(error, title, message) {
|
|
ErrorView.call(this, error, title, message);
|
|
};
|
|
|
|
AppErrorView.prototype = Object.create(ErrorView.prototype);
|
|
|
|
AppErrorView.prototype.applyStyle = function few_applyStyle() {
|
|
ErrorView.prototype.applyStyle.call(this);
|
|
document.body.classList.add('no-frame');
|
|
};
|
|
|
|
AppErrorView.prototype.addHandlers = function aew_addHandlers() {
|
|
document.getElementById('retry-btn').onclick = function retryClick() {
|
|
NetError.reload(true);
|
|
};
|
|
document.getElementById('close-btn').onclick = closeWindow;
|
|
};
|
|
|
|
// Dns error view
|
|
var DnsErrorAppErrorView = function(error, title, message) {
|
|
AppErrorView.call(this, error, title, message);
|
|
};
|
|
|
|
DnsErrorAppErrorView.prototype = Object.create(AppErrorView.prototype);
|
|
|
|
DnsErrorAppErrorView.prototype.populateMessages =
|
|
function noew_populateMessages() {
|
|
localizeElement(this.title, this.titleText);
|
|
localizeElement(this.message, this.messageText,
|
|
{ name: '\u2068' + this.error.u + '\u2069' });
|
|
};
|
|
|
|
var DnsErrorFramedErrorView = function(error, title, message) {
|
|
FramedErrorView.call(this, error, title, message);
|
|
};
|
|
|
|
DnsErrorFramedErrorView.prototype = Object.create(FramedErrorView.prototype);
|
|
|
|
DnsErrorFramedErrorView.prototype.populateMessages =
|
|
function noew_populateMessages() {
|
|
localizeElement(this.title, this.titleText);
|
|
localizeElement(this.message, this.messageText,
|
|
{ name: '\u2068' + this.error.u + '\u2069' });
|
|
};
|
|
|
|
// Offline view
|
|
var NetOfflineAppErrorView = function(error) {
|
|
AppErrorView.call(this, error);
|
|
this.node = document.getElementById('net-error-action-menu');
|
|
};
|
|
|
|
NetOfflineAppErrorView.prototype = Object.create(AppErrorView.prototype);
|
|
|
|
NetOfflineAppErrorView.prototype.populateMessages =
|
|
function noew_populateMessages() {
|
|
if (hasHistory()) {
|
|
localizeElement(this.title, 'network-error-in-app');
|
|
} else {
|
|
getAppName((function localizeTitle(appName) {
|
|
localizeElement(this.title, 'network-error-launching', {
|
|
name: appName
|
|
});
|
|
}).bind(this));
|
|
}
|
|
};
|
|
|
|
NetOfflineAppErrorView.prototype.addHandlers = function noew_addHandlers() {
|
|
addConnectionHandlers();
|
|
document.getElementById('settings-btn').onclick = showSettingsView;
|
|
document.getElementById('cancel-btn').onclick = cancel;
|
|
};
|
|
|
|
var NetOfflineFramedErrorView = function(error) {
|
|
FramedErrorView.call(this, error);
|
|
};
|
|
|
|
NetOfflineFramedErrorView.prototype =
|
|
Object.create(FramedErrorView.prototype);
|
|
|
|
NetOfflineFramedErrorView.prototype.populateMessages =
|
|
function noew_populateMessages() {
|
|
localizeElement(this.title, 'unable-to-connect');
|
|
localizeElement(this.message, 'tap-to-check-settings');
|
|
};
|
|
|
|
NetOfflineFramedErrorView.prototype.addHandlers =
|
|
function noew_addHandlers() {
|
|
addConnectionHandlers();
|
|
document.body.onclick = showSettingsView;
|
|
};
|
|
|
|
// Confirm views
|
|
var ConfirmAppErrorView = function(error, title, message) {
|
|
AppErrorView.call(this, error, title, message);
|
|
};
|
|
|
|
ConfirmAppErrorView.prototype = Object.create(AppErrorView.prototype);
|
|
|
|
ConfirmAppErrorView.prototype.applyStyle = function caew_applyStyle() {
|
|
AppErrorView.prototype.applyStyle.call(this);
|
|
document.body.classList.add('dialog');
|
|
};
|
|
|
|
// Alert view
|
|
var AlertAppErrorView = function(error, title, message) {
|
|
AppErrorView.call(this, error, title, message);
|
|
};
|
|
|
|
AlertAppErrorView.prototype = Object.create(AppErrorView.prototype);
|
|
|
|
AlertAppErrorView.prototype.applyStyle = function aaew_applyStyle() {
|
|
AppErrorView.prototype.applyStyle.call(this);
|
|
document.body.classList.add('dialog', 'alert');
|
|
};
|
|
|
|
var views = {
|
|
netOffline: {
|
|
'framed': NetOfflineFramedErrorView,
|
|
'no-frame': NetOfflineAppErrorView
|
|
},
|
|
dnsNotFound: {
|
|
'framed': DnsErrorFramedErrorView,
|
|
'no-frame': DnsErrorAppErrorView,
|
|
'title': 'server-not-found',
|
|
'message': 'server-not-found-error'
|
|
},
|
|
connectionFailed: {
|
|
'framed': FramedErrorView,
|
|
'no-frame': ConfirmAppErrorView,
|
|
'title': 'connection-failed',
|
|
'message': 'connection-failed-error'
|
|
},
|
|
notCached: {
|
|
'framed': FramedErrorView,
|
|
'no-frame': ConfirmAppErrorView,
|
|
'title': 'not-cached',
|
|
'message': 'not-cached-error'
|
|
},
|
|
fileNotFound: {
|
|
'framed': FramedErrorView,
|
|
'no-frame': AlertAppErrorView,
|
|
'title': 'file-not-found',
|
|
'message': 'file-not-found-error'
|
|
},
|
|
invalidConnection: {
|
|
'framed': FramedErrorView,
|
|
'no-frame': AlertAppErrorView,
|
|
'title': 'invalid-connection',
|
|
'message': 'invalid-connection-error'
|
|
},
|
|
malformedURI: {
|
|
'framed': FramedErrorView,
|
|
'no-frame': AlertAppErrorView,
|
|
'title': 'malformed-uri',
|
|
'message': 'malformed-uri-error'
|
|
},
|
|
redirectLoop: {
|
|
'framed': FramedErrorView,
|
|
'no-frame': AlertAppErrorView,
|
|
'title': 'redirect-loop',
|
|
'message': 'redirect-loop-error'
|
|
},
|
|
isprinting: {
|
|
'framed': FramedErrorView,
|
|
'no-frame': AlertAppErrorView,
|
|
'message': 'is-printing-error'
|
|
},
|
|
deniedPortAccess: {
|
|
'framed': FramedErrorView,
|
|
'no-frame': AlertAppErrorView,
|
|
'title': 'denied-port-access',
|
|
'message': 'denied-port-access-error'
|
|
},
|
|
proxyResolveFailure: {
|
|
'framed': FramedErrorView,
|
|
'no-frame': AlertAppErrorView,
|
|
'title': 'proxy-resolve-failure',
|
|
'message': 'proxy-resolve-failure-error'
|
|
},
|
|
proxyConnectFailure: {
|
|
'framed': FramedErrorView,
|
|
'no-frame': AlertAppErrorView,
|
|
'title': 'proxy-connect-failure',
|
|
'message': 'proxy-connect-failure-error'
|
|
},
|
|
contentEncodingError: {
|
|
'framed': FramedErrorView,
|
|
'no-frame': AlertAppErrorView,
|
|
'title': 'content-encoding',
|
|
'message': 'content-encoding-error'
|
|
},
|
|
remoteXUL: {
|
|
'framed': FramedErrorView,
|
|
'no-frame': AlertAppErrorView,
|
|
'title': 'remote-xul',
|
|
'message': 'remote-xul-error'
|
|
},
|
|
unsafeContentType: {
|
|
'framed': FramedErrorView,
|
|
'no-frame': AlertAppErrorView,
|
|
'title': 'unsafe-content-type',
|
|
'message': 'unsafe-content-type-error'
|
|
},
|
|
corruptedContentError: {
|
|
'framed': FramedErrorView,
|
|
'no-frame': AlertAppErrorView,
|
|
'title': 'corrupted-content',
|
|
'message': 'corrupted-content-error'
|
|
},
|
|
phishingBlocked: {
|
|
'framed': FramedErrorView,
|
|
'no-frame': AlertAppErrorView,
|
|
'title': 'phishing-blocked',
|
|
'message': 'phishing-blocked-error'
|
|
},
|
|
malwareBlocked: {
|
|
'framed': FramedErrorView,
|
|
'no-frame': AlertAppErrorView,
|
|
'title': 'malware-blocked',
|
|
'message': 'malware-blocked-error'
|
|
},
|
|
byDefault: {
|
|
'framed': FramedErrorView,
|
|
'no-frame': AppErrorView
|
|
}
|
|
};
|
|
|
|
var ErrorViewFactory = {
|
|
create: function nef_create() {
|
|
var error = getErrorFromURI();
|
|
var view = views[error.e] || views.byDefault;
|
|
return new view[getFrameType(error)](error, view.title,
|
|
view.message);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Initialize the page
|
|
*/
|
|
function initPage() {
|
|
_error = _app = null;
|
|
// Display detailed info about the error.
|
|
console.error('net-error');
|
|
ErrorViewFactory.create().init();
|
|
}
|
|
|
|
navigator.mozL10n.ready(initPage);
|
|
|
|
var NetError = {
|
|
init: initPage,
|
|
|
|
reload: function reload(forcedReload) {
|
|
|
|
// When reloading a page with POSTDATA the user will be prompted to
|
|
// confirm if he wants to resend the data. If the user accepted to resend
|
|
// the data, during the reload function call the onbeforeunload event is
|
|
// fired, otherwise if the event is not triggered then the last url from
|
|
// the history is loaded.
|
|
var isReloading = false;
|
|
window.addEventListener('beforeunload', function onBeforeunload() {
|
|
isReloading = true;
|
|
});
|
|
|
|
window.location.reload(forcedReload);
|
|
|
|
if (!isReloading) {
|
|
history.back();
|
|
}
|
|
}
|
|
};
|
|
|
|
exports.NetError = NetError;
|
|
}(this));
|