diff --git a/extensions/chromium/extension-router.js b/extensions/chromium/extension-router.js index 2e072142d..2e9d28875 100644 --- a/extensions/chromium/extension-router.js +++ b/extensions/chromium/extension-router.js @@ -83,6 +83,7 @@ limitations under the License. // When session restore is used, viewer pages may be loaded before the // webRequest event listener is attached (= page not found). + // Or the extension could have been crashed (OOM), leaving a sad tab behind. // Reload these tabs. chrome.tabs.query({ url: CRX_BASE_URL + '*:*' @@ -92,4 +93,25 @@ limitations under the License. } }); console.log('Set up extension URL router.'); + + Object.keys(localStorage).forEach(function(key) { + // The localStorage item is set upon unload by chromecom.js. + var parsedKey = /^unload-(\d+)-(true|false)-(.+)/.exec(key); + if (parsedKey) { + var timeStart = parseInt(parsedKey[1], 10); + var isHidden = parsedKey[2] === 'true'; + var url = parsedKey[3]; + if (Date.now() - timeStart < 3000) { + // Is it a new item (younger than 3 seconds)? Assume that the extension + // just reloaded, so restore the tab (work-around for crbug.com/511670). + chrome.tabs.create({ + url: chrome.runtime.getURL('restoretab.html') + + '?' + encodeURIComponent(url) + + '#' + encodeURIComponent(localStorage.getItem(key)), + active: !isHidden + }); + } + localStorage.removeItem(key); + } + }); })(); diff --git a/extensions/chromium/restoretab.html b/extensions/chromium/restoretab.html new file mode 100644 index 000000000..6e6fa425d --- /dev/null +++ b/extensions/chromium/restoretab.html @@ -0,0 +1,17 @@ + + + diff --git a/extensions/chromium/restoretab.js b/extensions/chromium/restoretab.js new file mode 100644 index 000000000..a5d84b1b6 --- /dev/null +++ b/extensions/chromium/restoretab.js @@ -0,0 +1,33 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ +/* +Copyright 2015 Mozilla Foundation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/** + * This is part of the work-around for crbug.com/511670. + * - chromecom.js sets the URL and history state upon unload. + * - extension-router.js retrieves the saved state and opens restoretab.html + * - restoretab.html (this script) restores the URL and history state. + */ +'use strict'; + +var url = decodeURIComponent(location.search.slice(1)); +var historyState = decodeURIComponent(location.hash.slice(1)); + +historyState = historyState === 'undefined' ? null : JSON.parse(historyState); + +history.replaceState(historyState, null, url); +location.reload(); diff --git a/web/chromecom.js b/web/chromecom.js index 29790f340..23d59e595 100644 --- a/web/chromecom.js +++ b/web/chromecom.js @@ -215,6 +215,23 @@ var ChromeCom = (function ChromeComClosure() { }); } + if (window === top) { + // Chrome closes all extension tabs (crbug.com/511670) when the extension + // reloads. To counter this, the tab URL and history state is saved to + // localStorage and restored by extension-router.js. + // Unfortunately, the window and tab index are not restored. And if it was + // the only tab in an incognito window, then the tab is not restored either. + addEventListener('unload', function() { + // If the runtime is still available, the unload is most likely a normal + // tab closure. Otherwise it is most likely an extension reload. + if (!isRuntimeAvailable()) { + localStorage.setItem( + 'unload-' + Date.now() + '-' + document.hidden + '-' + location.href, + JSON.stringify(history.state)); + } + }); + } + // This port is used for several purposes: // 1. When disconnected, the background page knows that the frame has unload. // 2. When the referrer was saved in history.state.chromecomState, it is sent