diff options
-rw-r--r-- | PKGBUILD | 16 | ||||
-rw-r--r-- | aw-snap-reloader-background.js | 189 | ||||
-rw-r--r-- | aw-snap-reloader-manifest.json | 32 | ||||
-rw-r--r-- | chromium-fullscreen.install | 1 | ||||
-rw-r--r-- | chromium-fullscreen.xinitrc | 2 |
5 files changed, 234 insertions, 6 deletions
@@ -1,7 +1,7 @@ # Author: Julian Xhokaxhiu <info@julianxhokaxhiu.com> pkgname=chromium-fullscreen -pkgver=1 -pkgrel=7 +pkgver=2 +pkgrel=1 pkgdesc="A script in order to run Chromium in fullscreen mode under the chromium user" url="" arch=('x86_64' 'i686' 'arm' 'armv6h' 'armv7h') @@ -11,11 +11,15 @@ install='chromium-fullscreen.install' source=('chromium-fullscreen.sysuser' 'chromium-fullscreen.xinitrc' 'disable-x-frame-option-background.js' - 'disable-x-frame-option-manifest.json') + 'disable-x-frame-option-manifest.json' + 'aw-snap-reloader-background.js' + 'aw-snap-reloader-manifest.json') md5sums=('9e979fbdd1e4a5a04fd0e91ce6a9a7be' - 'b5fee3c827d9b9bd3a954dd1213c1525' + 'd95b5d1a926e4144984bb6761cae4217' 'c700c36f4ea3e353c6d49df823561fc4' - 'f543ec018edbaf5533e6bddd5068ff54') + 'f543ec018edbaf5533e6bddd5068ff54' + 'a2e49410d550e79ac9c0037121522387' + 'e6bb49bafce141806d28fe03efc862b0') package() { # Copy autorun script @@ -27,4 +31,6 @@ package() { # Copy the extensions files install -Dm644 ${srcdir}/disable-x-frame-option-background.js "$pkgdir/home/chromium/.extensions/disable-x-frame-option/background.js" install -Dm644 ${srcdir}/disable-x-frame-option-manifest.json "$pkgdir/home/chromium/.extensions/disable-x-frame-option/manifest.json" + install -Dm644 ${srcdir}/aw-snap-reloader-background.js "$pkgdir/home/chromium/.extensions/aw-snap-reloader/background.js" + install -Dm644 ${srcdir}/aw-snap-reloader-manifest.json "$pkgdir/home/chromium/.extensions/aw-snap-reloader/manifest.json" } diff --git a/aw-snap-reloader-background.js b/aw-snap-reloader-background.js new file mode 100644 index 000000000000..d7324e7c97bc --- /dev/null +++ b/aw-snap-reloader-background.js @@ -0,0 +1,189 @@ +// Inspiration for this extension: +// http://zerodeveloper.tumblr.com/post/67664299242/chrome-extension-reload-tab-after-crash +// and +// https://github.com/unclespode/ohnoyoudidnt + +// Different ways to test crashes and other problems in Chrome/Chromium: +// In Chrome/Chromium see chrome://about under "For debug", currenlty +// the following options are available: +// chrome://badcastcrash/ +// chrome://crash/ +// chrome://crashdump/ +// chrome://kill/ +// chrome://hang/ +// chrome://shorthang +// chrome://gpuclean/ +// chrome://gpucrash/ +// chrome://gpuhang/ +// chrome://memory-exhaust/ +// chrome://ppapiflashcrash/ +// chrome://ppapiflashhang/ +// chrome://quit/ +// chrome://restart/ +// +// Another option is to open the developer tools on a tab that should be +// crashed and in the JavaScript console type: +// +// var memoryHog = "more"; while(true) {memoryHog = memoryHog + "andMore";} +// +// This code will consume so much memory that the tab will crash + +// About reloading of tabs; see: https://developer.chrome.com/extensions/tabs +// chrome.tabs.reload(integer tabId, object reloadProperties, function callback) +// tabId (integer optional): The ID of the tab to reload; +// defaults to the selected tab of the current window. +// reloadProperties (object optional): bypassCache (boolean optional): Whether +// using any local cache. Default is false. +// callback (function optional): If you specify the callback parameter, +// it should be a function that looks like this: function() {...}; +// +// For the use case of reloading a kiosk app or tabs that the user is using +// the default of calling chrome.tabs.reload() is best + +var tabSuccessCount = {}; // store of succesful probe calls +var tabUnresponsiveCount = {}; // store of probe calls that got stuck in limbo +// Interval between checks, do not make this too short ( +// less than 1 second seems unwise, better no less than +// 2 seconds) since it is also used to determine if a +// tab is unresponsive +var checkInterval = 10000; // in milliseconds +var tabsChecked = {}; // store for arrays of tab-ids that were checked +var checkIndex = 0; // index of current array of checked tab-ids +var nrTabs = 0; // current number of tabs + +function reloadTabIfNeeded(tab) { + return function(result) { + if (tabCrashed()) { + console.log("Crashed tab: title=" + (tab.title || "") + " id=" + tab.id + + " index=" + tab.index.toString() + " windowId=" + tab.windowId.toString() + + " sessionId=" + (tab.sessionId || "").toString() + + " highlighted=" + tab.highlighted.toString() + " active=" + tab.active.toString()); + if (tabShouldBeReloaded(tab)) { + console.log("Reload tab:" + tab.id.toString()); + chrome.tabs.reload(tab.id); + } + } else { + registerSuccessfulNoOp(tab); + } + tabsChecked[checkIndex].push(tab.id); + }; +} + +function tabShouldBeReloaded(tab) { + // Reload if at least one sucessful no-op has occurred. + // This might be too causious but ensures the tab was working + // before it crashed + // this also ensures that we do not reload a tab that takes a long + // time to load (being unresponsive whilst doing so) + return tabSuccessCount[tab.id] > 0; +} + +function registerSuccessfulNoOp(tab) { + if ((tabSuccessCount[tab.id] || null) === null) { + tabSuccessCount[tab.id] = 0; + } + tabSuccessCount[tab.id] += 1; + tabUnresponsiveCount[tab.id] = 0; +} + +function registerUnresponsive(tab) { + if ((tabUnresponsiveCount[tab.id] || null) === null) { + tabUnresponsiveCount[tab.id] = 0; + } + tabUnresponsiveCount[tab.id] += 1; +} + +function tabCrashed() { + // The crux of finding a crashed tab: + // If an operation (even a no-op) is executed on a crashed tab an error + // is reported which is available as the chrome.runtime.lastError + // The error incorrectly reports the tab was closed instead of the fact that the + // tab does not respond. + return chrome.runtime.lastError && chrome.runtime.lastError.message === "The tab was closed."; +} + +function checkTab(thisTab) { + if (relevantTab(thisTab)) { + // Perform a no-op as a probe to find if the tab reponds + chrome.tabs.executeScript(thisTab.id, { + // To find crashed tabs probing with a no-op is enough + // code: "null;" + // To find unresponsive tabs probing with some operation + // that takes CPU-cycles is needed + code: "1 + 1;" + }, reloadTabIfNeeded(thisTab)); + } +} + +function relevantTab(tab){ + // Only check tabs that have finished loading + // and that use the http or https protocol. + // This ignores tabs like chrome://... + // return tab.url.substring(0, 4) == "http" && tab.status == "complete"; + // This makes testing this extension more difficult, to test use + // the line below + return tab.status == "complete"; +} + +function reloadUnresposiveTabs(index, nrTabs, tabs) { + if (nrTabs === tabs.length && nrTabs > tabsChecked[index].length) { + var nrTabsToFind = nrTabs - tabsChecked[index].length; + console.log("Found " + nrTabsToFind.toString() + " unresponsive tabs"); + for (var j = 0; j < nrTabs && nrTabsToFind > 0; j += 1) { + if (tabsChecked[index].indexOf(tabs[j].id) == -1) { + registerUnresponsive(tabs[j]); + if (tabShouldBeReloaded(tabs[j])) { + console.log("Reload unresponsive tab:" + tabs[j].id.toString()); + // Reloading an unresponsive tab does not work + // chrome.tabs.reload(tabs[j].id); + // Therefore a new tab is created with the url of the old tab + // and the unresponsive tab is removed. + // Setting the new tab as active is mainly aimed at kiosk-like applications + chrome.tabs.create({url: tabs[j].url, active: true}, function(tab){ + console.log("Created new tab: id=" + tab.id.toString() + " title=" + (tab.title || "") + " url=" + (tab.url || "")); + }); + chrome.tabs.remove(tabs[j].id, function(){ + console.log("Removed unresponsive tab:" + tabs[j].id.toString()); + }); + } + nrTabsToFind -= 1; + } + } + } +} + +function checkTabs(tabs) { + // check for unresponsive tabs by checking the results of + // the previous round of checkTab calls + // NOTE: it is assumed all tabs have been checked (all callbacks + // initiated in the previous checkTabs call have ended (apart + // form the ones that were done on unresponsive tabs)). The + // checkInterval has to be long enough to make this a "certainty" + if (nrTabs > 0) { + reloadUnresposiveTabs(checkIndex, nrTabs, tabs); + } + + // roll the checkIndex around after 10 iterations + checkIndex = checkIndex > 9 ? 0 : (checkIndex + 1); + nrTabs = tabs.length; + tabsChecked[checkIndex] = []; + for (var i = 0; i < tabs.length; i += 1) { + checkTab(tabs[i]); + } +} + +// Reset the count for tabs that are closed or that change +function tabChanged(tabId, changeInfo, tab) { + console.log("Resetting Stats for tab: id=" + tabId.toString() + " title=" + + (tab !== undefined ? tab.title : "")); + tabSuccessCount[tabId] = 0; + tabUnresponsiveCount[tabId] = 0; +} + +setInterval(function() { + chrome.tabs.query({}, checkTabs); +}, checkInterval); + +// If the tab reloads, reset stats +chrome.tabs.onUpdated.addListener(tabChanged); +chrome.tabs.onRemoved.addListener(tabChanged); diff --git a/aw-snap-reloader-manifest.json b/aw-snap-reloader-manifest.json new file mode 100644 index 000000000000..0d6aa4a6d8f9 --- /dev/null +++ b/aw-snap-reloader-manifest.json @@ -0,0 +1,32 @@ +{ +"update_url": "https://clients2.google.com/service/update2/crx", + + "name": "Snapper - 'Aw snap' tab reloader", + "short_name": "Snapper", + "description": "Reload crashed or unresponsive Chrome tabs (Aw, snap). Specifically intended for digital signage content running in kiosk mode.", + "manifest_version": 2, + "version": "0.9.7.0", + "background": { + "scripts": ["javascript/background.js"], + "persistent": true + }, + "permissions": [ + "tabs" + ], + "browser_action": { + "default_icon": { + "16": "icons/icon-016.png", + "24": "icons/icon-024.png", + "32": "icons/icon-032.png" + }, + "default_title": "Snapper" + }, + "icons": { + "16": "icons/icon-016.png", + "19": "icons/icon-019.png", + "24": "icons/icon-024.png", + "32": "icons/icon-032.png", + "48": "icons/icon-048.png", + "128": "icons/icon-128.png" + } +} diff --git a/chromium-fullscreen.install b/chromium-fullscreen.install index 711f747a1a49..150d01b72969 100644 --- a/chromium-fullscreen.install +++ b/chromium-fullscreen.install @@ -7,6 +7,7 @@ pre_install() { # Create the extensions folders mkdir -p /home/chromium/.extensions/disable-x-frame-option + mkdir -p /home/chromium/.extensions/aw-snap-reloader } post_install() { diff --git a/chromium-fullscreen.xinitrc b/chromium-fullscreen.xinitrc index 8a7994926d8d..c50beee37cdb 100644 --- a/chromium-fullscreen.xinitrc +++ b/chromium-fullscreen.xinitrc @@ -30,7 +30,7 @@ exec /usr/bin/chromium --disable \ --disable-suggestions-service \ --disable-save-password-bubble \ --disable-session-crashed-bubble \ - --load-extension=.extensions/disable-x-frame-option \ + --load-extension=.extensions/disable-x-frame-option,.extensions/aw-snap-reloader \ --window-position="0,0" \ --window-size="1920,1080" \ --kiosk "$URL" |