Skip to content

Commit 8d672f3

Browse files
committedJul 24, 2021
Show Error on Blank Page
If there are recent console errors, show them as well!
1 parent b6f49c9 commit 8d672f3

File tree

3 files changed

+189
-23
lines changed

3 files changed

+189
-23
lines changed
 

‎chrome/content/grabber.js

+184-23
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
var hterr_grabber = {
2-
pageData: '<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n<html xmlns="http://www.w3.org/1999/xhtml" lang="{%HTML_LANG%}">\n <head>\n <title>{%HTML_TITLE%}</title>\n <link rel="stylesheet" href="chrome://global/skin/netError.css" type="text/css" media="all" />\n <link rel="icon" href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+PHRleHQgeD0iMCIgeT0iMTQiPuKaoO+4jzwvdGV4dD48L3N2Zz4=" type="image/svg+xml" />\n <script>\n function goBackThis(buttonEl)\n {\n try\n {\n history.go(-1);\n }\n catch(e) {}\n buttonEl.disabled = true;\n }\n function retryThis(buttonEl)\n {\n try \n {\n location.reload();\n }\n catch(e) {}\n buttonEl.disabled = true;\n }\n </script>\n </head>\n <body dir="{%LOCALE_DIR%}">\n <div id="errorPageContainer">\n <div id="errorTitle"><h1 id="errorTitleText">{%HTTP_ERROR_TITLE%}</h1></div>\n <div id="errorLongContent">\n <div id="errorShortDesc"><p id="errorShortDescText">{%HTTP_ERROR_DESCRIPTION%}</p></div>\n <div id="errorLongDesc">{%HTTP_ERROR_DETAILS%}</div>\n </div>\n <button id="errorTryAgain" autocomplete="off" onclick="retryThis(this);">{%RETRY_LABEL%}</button>\n <button id="errorGoBack" autocomplete="off" onclick="goBackThis(this);">{%BACK_LABEL%}</button>\n <script>\n // Only do autofocus if we\'re the toplevel frame; otherwise we\n // don\'t want to call attention to ourselves! The key part is\n // that autofocus happens on insertion into the tree, so we\n // can remove the button, add @autofocus, and reinsert the\n // button.\n if (2 > history.length)\n {\n var button = document.getElementById("errorGoBack");\n var parent = button.parentNode;\n parent.removeChild(button);\n }\n var noRetry = {%HTTP_ERROR_RETRY_BLOCKED%};\n if (noRetry)\n {\n var button = document.getElementById("errorTryAgain");\n var parent = button.parentNode;\n parent.removeChild(button);\n var button2 = document.getElementById("errorGoBack");\n if (button2 !== null)\n button2.setAttribute("id", "errorTryAgain");\n }\n if (window.top == window)\n {\n var button = document.getElementById("errorTryAgain");\n if (button !== null)\n {\n var nextSibling = button.nextSibling;\n var parent = button.parentNode;\n parent.removeChild(button);\n button.setAttribute("autofocus", "true");\n parent.insertBefore(button, nextSibling);\n }\n }\n </script>\n </div>\n </body>\n</html>',
2+
pageData: '<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n<html xmlns="http://www.w3.org/1999/xhtml" lang="{%HTML_LANG%}">\n <head>\n <title>{%HTML_TITLE%}</title>\n <link rel="stylesheet" href="chrome://global/skin/netError.css" type="text/css" media="all" />\n <link rel="icon" href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+PHRleHQgeD0iMCIgeT0iMTQiPuKaoO+4jzwvdGV4dD48L3N2Zz4=" type="image/svg+xml" />\n <script>\n function goBackThis(buttonEl)\n {\n try\n {\n history.go(-1);\n }\n catch(e) {}\n buttonEl.disabled = true;\n }\n function retryThis(buttonEl)\n {\n try \n {\n {%RETRY_SCOPE%}location.reload();\n }\n catch(e) {}\n buttonEl.disabled = true;\n }\n </script>{%STYLE%}\n </head>\n <body dir="{%LOCALE_DIR%}">\n <div id="errorPageContainer">\n <div id="errorTitle"><h1 id="errorTitleText">{%HTTP_ERROR_TITLE%}</h1></div>\n <div id="errorLongContent">\n <div id="errorShortDesc"><p id="errorShortDescText">{%HTTP_ERROR_DESCRIPTION%}</p></div>\n <div id="errorLongDesc">{%HTTP_ERROR_DETAILS%}</div>\n </div>\n <button id="errorTryAgain" autocomplete="off" onclick="retryThis(this);">{%RETRY_LABEL%}</button>\n <button id="errorGoBack" autocomplete="off" onclick="goBackThis(this);">{%BACK_LABEL%}</button>\n <script>\n // Only do autofocus if we\'re the toplevel frame; otherwise we\n // don\'t want to call attention to ourselves! The key part is\n // that autofocus happens on insertion into the tree, so we\n // can remove the button, add @autofocus, and reinsert the\n // button.\n if (2 > history.length)\n {\n var button = document.getElementById("errorGoBack");\n var parent = button.parentNode;\n parent.removeChild(button);\n }\n var noRetry = {%HTTP_ERROR_RETRY_BLOCKED%};\n if (noRetry)\n {\n var button = document.getElementById("errorTryAgain");\n var parent = button.parentNode;\n parent.removeChild(button);\n var button2 = document.getElementById("errorGoBack");\n if (button2 !== null)\n button2.setAttribute("id", "errorTryAgain");\n }\n if (window.top == window)\n {\n var button = document.getElementById("errorTryAgain");\n if (button !== null)\n {\n var nextSibling = button.nextSibling;\n var parent = button.parentNode;\n parent.removeChild(button);\n button.setAttribute("autofocus", "true");\n parent.insertBefore(button, nextSibling);\n }\n }\n </script>\n </div>\n </body>\n</html>',
33
plainData: '{%HTML_TITLE%}\n\n{%HTTP_ERROR_TITLE%}\n{%HTTP_ERROR_DESCRIPTION%}\n\n{%HTTP_ERROR_DETAILS%}',
44
hideReload: [400, 403, 410, 414, 415, 418, 421, 422, 423, 426, 428, 431, 444, 449, 450, 451, 494, 497, 499, 501, 505, 506, 507, 508, 509, 510, 518, 523, 526, 530],
55
nullData: {},
@@ -66,6 +66,8 @@ var hterr_grabber = {
6666
}
6767
if (hterr_grabber.hideReload.includes(code))
6868
errNoRetry = 'true';
69+
data = data.replace(/{%STYLE%}/g, '');
70+
data = data.replace(/{%RETRY_SCOPE%}/g, '');
6971
data = data.replace(/{%LOCALE_DIR%}/g, localeDir);
7072
data = data.replace(/{%HTML_LANG%}/g, htmlLang);
7173

@@ -88,6 +90,168 @@ var hterr_grabber = {
8890
data = data.replace(/<[^>]+>/gi, '');
8991
return String.fromCodePoint(0xEF, 0xBB, 0xBF) + data;
9092
},
93+
BlankBuilder: function(sConsole)
94+
{
95+
let data = hterr_grabber.pageData;
96+
let localeDir = 'ltr';
97+
let htmlLang = 'en';
98+
99+
let htmlTitle = 'Problem loading page';
100+
let cmdBack = 'Go Back';
101+
let cmdRetry = 'Try Again';
102+
103+
let errTitle = 'Blank Page Detected';
104+
let errDesc = 'There was an error rendering this website.';
105+
let errDetail = '<p>The page you are visiting did not render correctly. The error console may provide more details.</p>';
106+
let errNoRetry = 'false';
107+
let gBundle = Components.classes['@mozilla.org/intl/stringbundle;1'].getService(Components.interfaces.nsIStringBundleService);
108+
let locale = gBundle.createBundle('chrome://hterr/locale/hterr.properties');
109+
try {localeDir = locale.GetStringFromName('locale.dir');}
110+
catch(e) {localeDir = 'ltr';}
111+
try {htmlLang = locale.GetStringFromName('html.lang');}
112+
catch(e) {htmlLang = 'en';}
113+
114+
try {htmlTitle = locale.GetStringFromName('html.title');}
115+
catch(e) {htmlTitle = 'Problem loading page';}
116+
try {cmdBack = locale.GetStringFromName('back.label');}
117+
catch(e) {cmdBack = 'Go Back';}
118+
try {cmdRetry = locale.GetStringFromName('retry.label');}
119+
catch(e) {cmdRetry = 'Try Again';}
120+
121+
try {errTitle = locale.GetStringFromName('error.blank.title');}
122+
catch(e) {errTitle = 'Blank Page Detected';}
123+
try {errDesc = locale.GetStringFromName('error.blank.desc');}
124+
catch(e) {errDesc = 'There was an error rendering this website.';}
125+
try {errDetail = locale.GetStringFromName('error.blank.longDesc');}
126+
catch(e) {errDetail = '<p>The page you are visiting did not render correctly. The error console may provide more details.</p>';}
127+
if (sConsole !== '')
128+
errDetail+= '<textarea id="console" readonly="readonly" wrap="off">' + sConsole + '</textarea>';
129+
130+
let sStyle = '\n <style>' +
131+
'\n #console' +
132+
'\n {' +
133+
'\n background-color: inherit;' +
134+
'\n color: inherit;' +
135+
'\n min-width: 100%;' +
136+
'\n max-width: 100%;' +
137+
'\n min-height: 6em;' +
138+
'\n max-height: 30em;' +
139+
'\n }' +
140+
'\n </style>';
141+
142+
data = data.replace(/{%STYLE%}/g, sStyle);
143+
data = data.replace(/{%RETRY_SCOPE%}/g, 'window.top.');
144+
data = data.replace(/{%LOCALE_DIR%}/g, localeDir);
145+
data = data.replace(/{%HTML_LANG%}/g, htmlLang);
146+
147+
data = data.replace(/{%HTML_TITLE%}/g, htmlTitle);
148+
data = data.replace(/{%BACK_LABEL%}/g, cmdBack);
149+
data = data.replace(/{%RETRY_LABEL%}/g, cmdRetry);
150+
151+
data = data.replace(/{%HTTP_ERROR_TITLE%}/g, errTitle);
152+
data = data.replace(/{%HTTP_ERROR_DESCRIPTION%}/g, errDesc);
153+
data = data.replace(/{%HTTP_ERROR_DETAILS%}/g, errDetail);
154+
data = data.replace(/{%HTTP_ERROR_RETRY_BLOCKED%}/g, errNoRetry);
155+
return data;
156+
},
157+
BlankPageChecker: function(wnd)
158+
{
159+
if (!wnd)
160+
return;
161+
if (wnd.contentWindow.hterrTimer)
162+
wnd.contentWindow.clearTimeout(wnd.hterrTimer);
163+
if (wnd.contentDocument.body.scrollHeight > wnd.contentDocument.documentElement.clientHeight)
164+
return;
165+
if (wnd.contentDocument.body.scrollWidth > wnd.contentDocument.documentElement.clientWidth)
166+
return;
167+
const canvas = wnd.contentDocument.createElementNS('http://www.w3.org/1999/xhtml', 'canvas');
168+
canvas.width = wnd.contentDocument.documentElement.clientWidth;
169+
canvas.height = wnd.contentDocument.documentElement.clientHeight;
170+
const ctx = canvas.getContext('2d');
171+
ctx.drawWindow(wnd.contentWindow, 0, 0, canvas.width, canvas.height, 'rgb(255,255,255)', ctx.DRAWWINDOW_DRAW_VIEW);
172+
const dt = ctx.getImageData(0, 0, canvas.width, canvas.height);
173+
let r = dt.data.filter(b => b !== 255);
174+
if (r.length > 0)
175+
return;
176+
let consoleService = Components.classes["@mozilla.org/consoleservice;1"].getService(Components.interfaces.nsIConsoleService);
177+
let consoleLines = consoleService.getMessageArray();
178+
let sConsole = '';
179+
let waitForBlank = 10;
180+
let prefs = Components.classes['@mozilla.org/preferences-service;1'].getService(Components.interfaces.nsIPrefBranch);
181+
if (prefs.prefHasUserValue('extensions.hterr.blankwait'))
182+
waitForBlank = prefs.getIntPref('extensions.hterr.blankwait');
183+
if (waitForBlank < 1)
184+
waitForBlank = 1;
185+
if (waitForBlank > 60)
186+
waitForBlank = 60;
187+
for (let i = 0; i < consoleLines.length; i++)
188+
{
189+
try
190+
{
191+
let oErr = consoleLines[i].QueryInterface(Components.interfaces.nsIScriptError);
192+
let wndUtil = wnd.contentWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindowUtils);
193+
if (wndUtil.currentInnerWindowID !== oErr.innerWindowID)
194+
continue;
195+
if ((oErr.flags & 2) === 0)
196+
continue;
197+
let sMsg = oErr.errorMessage;
198+
if (oErr.sourceName !== '')
199+
sMsg += '\n ' + oErr.sourceName;
200+
if (oErr.lineNumber > 0)
201+
{
202+
sMsg += '\n line ' + oErr.lineNumber;
203+
if (oErr.columnNumber > 0)
204+
sMsg += ', column ' + oErr.columnNumber;
205+
}
206+
if (oErr.sourceLine !== '')
207+
sMsg += '\n> ...' + oErr.sourceLine + '...';
208+
if (sConsole === '')
209+
sConsole = sMsg;
210+
else
211+
sConsole += '\n\n' + sMsg;
212+
}
213+
catch(ex){continue;}
214+
}
215+
let sHTML = hterr_grabber.BlankBuilder(sConsole);
216+
let doc = wnd.contentDocumentAsCPOW;
217+
let d = doc.createElement('iframe');
218+
d.setAttribute('frameborder', '0');
219+
d.setAttribute('style','position: absolute; top: 0; left: 0; width: 100%; height: 100%;');
220+
d.srcdoc = sHTML;
221+
doc.body.appendChild(d);
222+
},
223+
DocumentFinder: function(request, mustBeReady)
224+
{
225+
let mainDoc = false;
226+
if (gBrowser.browsers === undefined || gBrowser.browsers.length < 1)
227+
return mainDoc;
228+
for (let i = 0; i < gBrowser.browsers.length; i++)
229+
{
230+
if (mustBeReady)
231+
{
232+
if (gBrowser.browsers[i].contentDocument.readyState === 'complete')
233+
continue;
234+
if (gBrowser.browsers[i].contentDocument.readyState === 'uninitialized')
235+
continue;
236+
if (gBrowser.browsers[i].contentDocument.readyState !== 'loading' && gBrowser.browsers[i].contentDocument.readyState !== 'interactive')
237+
console.log('Unknown readyState:', gBrowser.browsers[i].contentDocument.readyState, '(Risking it as something to compare)');
238+
}
239+
if (request.originalURI !== undefined)
240+
{
241+
if (gBrowser.browsers[i].contentDocument.location.href === request.originalURI.spec)
242+
{
243+
mainDoc = gBrowser.browsers[i];
244+
break;
245+
}
246+
}
247+
if (gBrowser.browsers[i].contentDocument.location.href === request.name)
248+
{
249+
mainDoc = gBrowser.browsers[i];
250+
break;
251+
}
252+
}
253+
return mainDoc;
254+
},
91255
ResponseObserver:
92256
{
93257
observe: function(aSubject, aTopic, aData)
@@ -127,6 +291,12 @@ hterr_grabber.TracingListener.prototype = {
127291
},
128292
onStartRequest: function(request, context)
129293
{
294+
let mainDoc = hterr_grabber.DocumentFinder(request, false);
295+
if (mainDoc !== false)
296+
{
297+
if (mainDoc.contentWindow.hterrTimer)
298+
mainDoc.contentWindow.clearTimeout(mainDoc.hterrTimer);
299+
}
130300
request.QueryInterface(Components.interfaces.nsIHttpChannel);
131301
hterr_grabber.nullData[request.channelId] = true;
132302
try
@@ -139,6 +309,19 @@ hterr_grabber.TracingListener.prototype = {
139309
{
140310
try
141311
{
312+
let mainDoc = hterr_grabber.DocumentFinder(request, true);
313+
if (!mainDoc)
314+
return;
315+
if (mainDoc.contentWindow.hterrTimer)
316+
mainDoc.contentWindow.clearTimeout(mainDoc.contentWindow.hterrTimer);
317+
let waitForBlank = 10;
318+
let prefs = Components.classes['@mozilla.org/preferences-service;1'].getService(Components.interfaces.nsIPrefBranch);
319+
if (prefs.prefHasUserValue('extensions.hterr.blankwait'))
320+
waitForBlank = prefs.getIntPref('extensions.hterr.blankwait');
321+
if (waitForBlank > 60)
322+
waitForBlank = 60;
323+
if (waitForBlank > 0)
324+
mainDoc.contentWindow.hterrTimer = mainDoc.contentWindow.setTimeout(hterr_grabber.BlankPageChecker, waitForBlank * 1000, mainDoc);
142325
request.QueryInterface(Components.interfaces.nsIHttpChannel);
143326
if (!hterr_grabber.nullData[request.channelId])
144327
return;
@@ -162,28 +345,6 @@ hterr_grabber.TracingListener.prototype = {
162345
let cEnc = mime.substring(mime.indexOf('/') + 1);
163346
if (!(cType === 'text' || (cType === 'application' && (cEnc.includes('html') || cEnc.includes('xml'))) || mime === 'image/svg+xml'))
164347
return;
165-
let mainDoc = false;
166-
for (let i = 0; i < gBrowser.browsers.length; i++)
167-
{
168-
if (gBrowser.browsers[i].contentDocument.readyState === 'complete')
169-
continue;
170-
if (gBrowser.browsers[i].contentDocument.readyState === 'uninitialized')
171-
continue;
172-
if (gBrowser.browsers[i].contentDocument.readyState !== 'loading' && gBrowser.browsers[i].contentDocument.readyState !== 'interactive')
173-
console.log('Unknown readyState:', gBrowser.browsers[i].contentDocument.readyState, '(Risking it as something to compare)');
174-
if (gBrowser.browsers[i].contentDocument.location.href === request.originalURI.spec)
175-
{
176-
mainDoc = true;
177-
break;
178-
}
179-
if (gBrowser.browsers[i].contentDocument.location.href === request.name)
180-
{
181-
mainDoc = true;
182-
break;
183-
}
184-
}
185-
if (!mainDoc)
186-
return;
187348
let extraParam = null;
188349
try
189350
{

‎chrome/locale/en-US/hterr.properties

+4
Original file line numberDiff line numberDiff line change
@@ -276,3 +276,7 @@ error.530.longDesc=<p>Your request could not be completed because the server has
276276
error.598.title=Proxy Error 598
277277
error.598.desc=The server can not complete your request.
278278
error.598.longDesc=<p>While accessing your destination server, the proxy did not receive a timely response, and was unable to return a response to you.</p><p>Please try again.</p>
279+
280+
error.blank.title=Blank Page Detected
281+
error.blank.desc=There was an error rendering this website.
282+
error.blank.longDesc=<p>The page you are visiting did not render correctly. The error console may provide more details.</p>

‎defaults/preferences/hterr.js

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
pref("extensions.{E4E173E0-B462-54B7-83B8-1EF15E801FED}.description", "chrome://hterr/locale/hterr.properties");
2+
pref("extensions.hterr.blankwait", 10);

0 commit comments

Comments
 (0)
Please sign in to comment.