diff --git a/browser/components/tabview/content.js b/browser/components/tabview/content.js index 18b12e570931..a156fe04c61f 100644 --- a/browser/components/tabview/content.js +++ b/browser/components/tabview/content.js @@ -74,12 +74,21 @@ let WindowEventHandler = { // as quick as possible, switch the selected tab and hide the tabview // before the modal dialog is shown sendSyncMessage("Panorama:DOMWillOpenModalDialog"); + }, + + // ---------- + // Function: onMozAfterPaint + // Sends an asynchronous message when the "onMozAfterPaint" event + // is fired. + onMozAfterPaint: function WEH_onMozAfterPaint(event) { + sendAsyncMessage("Panorama:MozAfterPaint"); } }; // add event listeners addEventListener("DOMContentLoaded", WindowEventHandler.onDOMContentLoaded, false); addEventListener("DOMWillOpenModalDialog", WindowEventHandler.onDOMWillOpenModalDialog, false); +addEventListener("MozAfterPaint", WindowEventHandler.onMozAfterPaint, false); // ---------- // WindowMessageHandler @@ -90,8 +99,8 @@ let WindowMessageHandler = { // Function: isDocumentLoaded // Checks if the currently active document is loaded. isDocumentLoaded: function WMH_isDocumentLoaded(cx) { - let isLoaded = (content.document.readyState == "complete" && - !webProgress.isLoadingDocument); + let readyState = content.document.readyState; + let isLoaded = (readyState != "uninitalized" && !webProgress.isLoadingDocument); sendAsyncMessage(cx.name, {isLoaded: isLoaded}); } @@ -187,3 +196,4 @@ let WebProgressListener = { // add web progress listener webProgress.addProgressListener(WebProgressListener, Ci.nsIWebProgress.NOTIFY_STATE_WINDOW); + diff --git a/browser/components/tabview/tabitems.js b/browser/components/tabview/tabitems.js index e3c523682439..c761e59c8a5c 100644 --- a/browser/components/tabview/tabitems.js +++ b/browser/components/tabview/tabitems.js @@ -794,6 +794,7 @@ let TabItems = { tempCanvas: null, _reconnectingPaused: false, tabItemPadding: {}, + _mozAfterPaintHandler: null, // ---------- // Function: toString @@ -825,6 +826,10 @@ let TabItems = { this.tempCanvas.width = 150; this.tempCanvas.height = 112; + let mm = gWindow.messageManager; + this._mozAfterPaintHandler = this.onMozAfterPaint.bind(this); + mm.addMessageListener("Panorama:MozAfterPaint", this._mozAfterPaintHandler); + // When a tab is opened, create the TabItem this._eventListeners.open = function (event) { let tab = event.target; @@ -874,6 +879,9 @@ let TabItems = { // ---------- // Function: uninit uninit: function TabItems_uninit() { + let mm = gWindow.messageManager; + mm.removeMessageListener("Panorama:MozAfterPaint", this._mozAfterPaintHandler); + for (let name in this._eventListeners) { AllTabs.unregister(name, this._eventListeners[name]); } @@ -912,20 +920,33 @@ let TabItems = { return this._fragment; }, - // ---------- - // Function: isComplete - // Return whether the xul:tab has fully loaded. - isComplete: function TabItems_isComplete(tab) { - // If our readyState is complete, but we're showing about:blank, - // and we're not loading about:blank, it means we haven't really - // started loading. This can happen to the first few tabs in a - // page. + // Function: _isComplete + // Checks whether the xul:tab has fully loaded and calls a callback with a + // boolean indicates whether the tab is loaded or not. + _isComplete: function TabItems__isComplete(tab, callback) { Utils.assertThrow(tab, "tab"); - return ( - tab.linkedBrowser.contentDocument.readyState == 'complete' && - !(tab.linkedBrowser.contentDocument.URL == 'about:blank' && - tab._tabViewTabItem.url != 'about:blank') - ); + + let mm = tab.linkedBrowser.messageManager; + let message = "Panorama:isDocumentLoaded"; + + mm.addMessageListener(message, function onMessage(cx) { + mm.removeMessageListener(cx.name, onMessage); + callback(cx.json.isLoaded); + }); + mm.sendAsyncMessage(message); + }, + + // ---------- + // Function: _onMozAfterPaint + // Called when a web page is painted. + onMozAfterPaint: function TabItems_onMozAfterPaint(cx) { + let index = gBrowser.browsers.indexOf(cx.target); + if (index == -1) + return; + + let tab = gBrowser.tabs[index]; + if (!tab.pinned) + this.update(tab); }, // ---------- @@ -1009,35 +1030,41 @@ let TabItems = { } // ___ Make sure the tab is complete and ready for updating. - if (!this.isComplete(tab) && (!options || !options.force)) { - // If it's incomplete, stick it on the end of the queue - this._tabsWaitingForUpdate.push(tab); - return; - } - - // ___ thumbnail - let $canvas = tabItem.$canvas; - if (!tabItem.canvasSizeForced) { - let w = $canvas.width(); - let h = $canvas.height(); - if (w != tabItem.$canvas[0].width || h != tabItem.$canvas[0].height) { - tabItem.$canvas[0].width = w; - tabItem.$canvas[0].height = h; + let self = this; + let updateCanvas = function TabItems__update_updateCanvas(tabItem) { + // ___ thumbnail + let $canvas = tabItem.$canvas; + if (!tabItem.canvasSizeForced) { + let w = $canvas.width(); + let h = $canvas.height(); + if (w != tabItem.$canvas[0].width || h != tabItem.$canvas[0].height) { + tabItem.$canvas[0].width = w; + tabItem.$canvas[0].height = h; + } } - } - this._lastUpdateTime = Date.now(); - tabItem._lastTabUpdateTime = this._lastUpdateTime; + self._lastUpdateTime = Date.now(); + tabItem._lastTabUpdateTime = self._lastUpdateTime; - tabItem.tabCanvas.paint(); - tabItem.saveThumbnail(); + tabItem.tabCanvas.paint(); + tabItem.saveThumbnail(); - // ___ cache - if (tabItem.isShowingCachedData()) - tabItem.hideCachedData(); + // ___ cache + if (tabItem.isShowingCachedData()) + tabItem.hideCachedData(); - // ___ notify subscribers that a full update has completed. - tabItem._sendToSubscribers("updated"); + // ___ notify subscribers that a full update has completed. + tabItem._sendToSubscribers("updated"); + }; + if (options && options.force) + updateCanvas(tabItem); + else + this._isComplete(tab, function TabItems__update_isComplete(isComplete) { + if (isComplete) + updateCanvas(tabItem); + else + self._tabsWaitingForUpdate.push(tab); + }); } catch(e) { Utils.log(e); } diff --git a/browser/components/tabview/test/Makefile.in b/browser/components/tabview/test/Makefile.in index 65acdf7a7cf7..5b71fdc84c3f 100644 --- a/browser/components/tabview/test/Makefile.in +++ b/browser/components/tabview/test/Makefile.in @@ -151,6 +151,7 @@ _BROWSER_FILES = \ browser_tabview_bug655269.js \ browser_tabview_bug656778.js \ browser_tabview_bug656913.js \ + browser_tabview_bug659594.js \ browser_tabview_bug662266.js \ browser_tabview_bug663421.js \ browser_tabview_bug665502.js \ diff --git a/browser/components/tabview/test/browser_tabview_bug594958.js b/browser/components/tabview/test/browser_tabview_bug594958.js index 0ac228a415bf..3718d372664e 100644 --- a/browser/components/tabview/test/browser_tabview_bug594958.js +++ b/browser/components/tabview/test/browser_tabview_bug594958.js @@ -54,10 +54,17 @@ function test() { let test = tests.shift(); let tab = win.gBrowser.tabs[0]; - tab.linkedBrowser.addEventListener('load', function onLoad() { - tab.linkedBrowser.removeEventListener('load', onLoad, true); - checkUrl(test); - }, true); + let mm = tab.linkedBrowser.messageManager; + + mm.addMessageListener("Panorama:DOMContentLoaded", function onLoad(cx) { + mm.removeMessageListener(cx.name, onLoad); + + let tabItem = tab._tabViewTabItem; + tabItem.addSubscriber("updated", function onUpdated() { + tabItem.removeSubscriber("updated", onUpdated); + checkUrl(test); + }); + }); tab.linkedBrowser.loadURI(test.url); } diff --git a/browser/components/tabview/test/browser_tabview_bug597248.js b/browser/components/tabview/test/browser_tabview_bug597248.js index 98490c1b29f6..ff70b8a6272c 100644 --- a/browser/components/tabview/test/browser_tabview_bug597248.js +++ b/browser/components/tabview/test/browser_tabview_bug597248.js @@ -134,10 +134,13 @@ function updateAndCheck() { let tabItems = contentWindow.TabItems.getItems(); tabItems.forEach(function(tabItem) { - contentWindow.TabItems._update(tabItem.tab); - ok(!tabItem.isShowingCachedData(), - "Tab item is not showing cached data anymore. " + - tabItem.tab.linkedBrowser.currentURI.spec); + tabItem.addSubscriber("updated", function onUpdated() { + tabItem.removeSubscriber("updated", onUpdated); + ok(!tabItem.isShowingCachedData(), + "Tab item is not showing cached data anymore. " + + tabItem.tab.linkedBrowser.currentURI.spec); + }); + contentWindow.TabItems.update(tabItem.tab); }); // clean up and finish diff --git a/browser/components/tabview/test/browser_tabview_bug659594.js b/browser/components/tabview/test/browser_tabview_bug659594.js new file mode 100644 index 000000000000..6352fabafa05 --- /dev/null +++ b/browser/components/tabview/test/browser_tabview_bug659594.js @@ -0,0 +1,39 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function test() { + waitForExplicitFinish(); + + newWindowWithTabView(function(win) { + let numTabsToUpdate = 2; + + showTabView(function() { + let contentWindow = win.TabView.getContentWindow(); + let groupItem = contentWindow.GroupItems.groupItems[0]; + + groupItem.getChildren().forEach(function(tabItem) { + tabItem.addSubscriber("updated", function onUpdated() { + tabItem.removeSubscriber("updated", onUpdated); + + if (--numTabsToUpdate == 0) + finish(); + }); + contentWindow.TabItems.update(tabItem.tab); + }); + }, win); + }, function(win) { + BrowserOffline.toggleOfflineStatus(); + ok(Services.io.offline, "It is now offline"); + + let originalTab = win.gBrowser.tabs[0]; + originalTab.linkedBrowser.loadURI("http://www.example.com/foo"); + win.gBrowser.addTab("http://www.example.com/bar"); + + registerCleanupFunction(function () { + if (Services.io.offline) + BrowserOffline.toggleOfflineStatus(); + win.close(); + }); + }); +} + diff --git a/browser/components/tabview/test/browser_tabview_expander.js b/browser/components/tabview/test/browser_tabview_expander.js index 305f0995710b..88c624b0503e 100644 --- a/browser/components/tabview/test/browser_tabview_expander.js +++ b/browser/components/tabview/test/browser_tabview_expander.js @@ -26,14 +26,13 @@ function onTabViewWindowLoaded(win) { // procreate! contentWindow.UI.setActive(group); for (var i=0; i<7; i++) { - win.gBrowser.loadOneTab('about:blank#' + i, {inBackground: true}); + win.gBrowser.loadOneTab('http://example.com#' + i, {inBackground: true}); } let children = group.getChildren(); // Wait until they all update because, once updated, they will notice that they // don't have favicons and this will change their styling at some unknown time. afterAllTabItemsUpdated(function() { - ok(!group.shouldStack(group._children.length), "The group should not stack."); is(expander[0].style.display, "none", "The expander is hidden."); diff --git a/browser/components/tabview/test/head.js b/browser/components/tabview/test/head.js index 2ec80fc11142..7b6ee74c667f 100644 --- a/browser/components/tabview/test/head.js +++ b/browser/components/tabview/test/head.js @@ -69,13 +69,23 @@ function closeGroupItem(groupItem, callback) { function afterAllTabItemsUpdated(callback, win) { win = win || window; let tabItems = win.document.getElementById("tab-view").contentWindow.TabItems; + let counter = 0; for (let a = 0; a < win.gBrowser.tabs.length; a++) { let tabItem = win.gBrowser.tabs[a]._tabViewTabItem; - if (tabItem) - tabItems._update(win.gBrowser.tabs[a]); + if (tabItem) { + let tab = win.gBrowser.tabs[a]; + counter++; + tabItem.addSubscriber("updated", function onUpdated() { + tabItem.removeSubscriber("updated", onUpdated); + if (--counter == 0) + callback(); + }); + tabItems.update(tab); + } } - callback(); + if (counter == 0) + callback(); } // ---------