VulpesOS/assets/webapps/system.gaiamobile.org/js/net_error.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

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));