author | Tim Taubert <tim.taubert@gmx.de> |
Thu, 03 Feb 2011 02:53:13 +0100 | |
changeset 61858 | 54da8f75df7f9d2a91d9a16019c850f25994c6a3 |
parent 61857 | 490e39e1d9569f606e3ff5675c9569b4c7b8529d |
child 61859 | fd7962a458688e92cc5edd857d30da928438ad03 |
push id | 18522 |
push user | eakhgari@mozilla.com |
push date | Thu, 03 Feb 2011 17:29:54 +0000 |
treeherder | mozilla-central@3c87074d5f50 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | ian, blocking2.0 |
bugs | 594958 |
milestone | 2.0b12pre |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/browser/base/content/tabview/tabitems.js +++ b/browser/base/content/tabview/tabitems.js @@ -803,17 +803,17 @@ let TabItems = { let $canvas = iQ("<canvas>") .attr('moz-opaque', ''); $canvas.appendTo(iQ("body")); $canvas.hide(); this.tempCanvas = $canvas[0]; // 150 pixels is an empirical size, below which FF's drawWindow() // algorithm breaks down this.tempCanvas.width = 150; - this.tempCanvas.height = 150; + this.tempCanvas.height = 112; this.tabsProgressListener = { onStateChange: function(browser, webProgress, request, stateFlags, status) { if ((stateFlags & Ci.nsIWebProgressListener.STATE_STOP) && (stateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)) { // browser would only has _tabViewTabItemWithCachedData if // it's showing cached data. if (browser._tabViewTabItemWithCachedData) @@ -1276,77 +1276,109 @@ TabCanvas.prototype = { // ---------- // Function: paint paint: function TabCanvas_paint(evt) { var w = this.canvas.width; var h = this.canvas.height; if (!w || !h) return; - let fromWin = this.tab.linkedBrowser.contentWindow; - if (fromWin == null) { - Utils.log('null fromWin in paint'); + if (!this.tab.linkedBrowser.contentWindow) { + Utils.log('no tab.linkedBrowser.contentWindow in TabCanvas.paint()'); return; } + let ctx = this.canvas.getContext("2d"); let tempCanvas = TabItems.tempCanvas; + let bgColor = '#fff'; + if (w < tempCanvas.width) { // Small draw case where nearest-neighbor algorithm breaks down in Windows // First draw to a larger canvas (150px wide), and then draw that image // to the destination canvas. - - var tempCtx = tempCanvas.getContext("2d"); - - let canvW = tempCanvas.width; - let canvH = (h/w) * canvW; - - var scaler = canvW/fromWin.innerWidth; - - tempCtx.save(); - tempCtx.clearRect(0,0,tempCanvas.width,tempCanvas.height); - tempCtx.scale(scaler, scaler); - try{ - tempCtx.drawWindow(fromWin, fromWin.scrollX, fromWin.scrollY, - canvW/scaler, canvH/scaler, "#fff"); - } catch(e) { + let tempCtx = tempCanvas.getContext("2d"); + this._drawWindow(tempCtx, tempCanvas.width, tempCanvas.height, bgColor); + + // Now copy to tabitem canvas. + try { + this._fillCanvasBackground(ctx, w, h, bgColor); + ctx.drawImage(tempCanvas, 0, 0, w, h); + } catch (e) { Utils.error('paint', e); - } - tempCtx.restore(); - - // Now copy to tabitem canvas. No save/restore necessary. - var destCtx = this.canvas.getContext("2d"); - try{ - // the tempcanvas is square, so draw it as a square. - destCtx.drawImage(tempCanvas, 0, 0, w, w); - } catch(e) { - Utils.error('paint', e); - } - + } } else { // General case where nearest neighbor algorithm looks good // Draw directly to the destination canvas - - var ctx = this.canvas.getContext("2d"); - - var scaler = w/fromWin.innerWidth; - - // TODO: Potentially only redraw the dirty rect? (Is it worth it?) - - ctx.save(); - ctx.scale(scaler, scaler); - try{ - ctx.drawWindow(fromWin, fromWin.scrollX, fromWin.scrollY, - w/scaler, h/scaler, "#fff", - Ci.nsIDOMCanvasRenderingContext2D.DRAWWINDOW_DO_NOT_FLUSH); - } catch(e) { - Utils.error('paint', e); - } - - ctx.restore(); + this._drawWindow(ctx, w, h, bgColor); + } + }, + + // ---------- + // Function: _fillCanvasBackground + // Draws a rectangle of <width>x<height> with color <bgColor> to the given + // canvas context. + _fillCanvasBackground: function TabCanvas__fillCanvasBackground(ctx, width, height, bgColor) { + ctx.fillStyle = bgColor; + ctx.fillRect(0, 0, width, height); + }, + + // ---------- + // Function: _drawWindow + // Draws contents of the tabs' browser window to the given canvas context. + _drawWindow: function TabCanvas__drawWindow(ctx, width, height, bgColor) { + this._fillCanvasBackground(ctx, width, height, bgColor); + + let rect = this._calculateClippingRect(width, height); + let scaler = width / rect.width; + + ctx.save(); + ctx.scale(scaler, scaler); + + try { + let win = this.tab.linkedBrowser.contentWindow; + ctx.drawWindow(win, rect.left, rect.top, rect.width, rect.height, + bgColor, ctx.DRAWWINDOW_DO_NOT_FLUSH); + } catch (e) { + Utils.error('paint', e); } + + ctx.restore(); + }, + + // ---------- + // Function: _calculateClippingRect + // Calculate the clipping rect that will be projected to the tab's + // thumbnail canvas. + _calculateClippingRect: function TabCanvas__calculateClippingRect(origWidth, origHeight) { + let win = this.tab.linkedBrowser.contentWindow; + let body = win.document.body; + + let maxWidth = win.innerWidth - 25; + let maxHeight = win.innerHeight; + + if (body) { + maxWidth = Math.max(maxWidth, body.scrollWidth - win.scrollX); + maxHeight = Math.max(maxHeight, body.scrollHeight - win.scrollY); + } + + let height = Math.min(maxHeight, Math.floor(origHeight * maxWidth / origWidth)); + let width = Math.floor(origWidth * height / origHeight); + + // very short pages in combination with a very wide browser window force us + // to extend the clipping rect and add some empty space around the thumb + let factor = 0.7; + if (width < maxWidth * factor) { + width = maxWidth * factor; + height = Math.floor(origHeight * width / origWidth); + } + + let left = win.scrollX + Math.max(0, Math.round((maxWidth - width) / 2)); + let top = win.scrollY; + + return new Rect(left, top, width, height); }, // ---------- // Function: toImageData toImageData: function TabCanvas_toImageData() { return this.canvas.toDataURL("image/png", ""); } };
--- a/browser/base/content/test/tabview/Makefile.in +++ b/browser/base/content/test/tabview/Makefile.in @@ -52,16 +52,17 @@ include $(topsrcdir)/config/rules.mk browser_tabview_bug587231.js \ browser_tabview_bug587351.js \ browser_tabview_bug587503.js \ browser_tabview_bug587990.js \ browser_tabview_bug588265.js \ browser_tabview_bug589324.js \ browser_tabview_bug590606.js \ browser_tabview_bug591706.js \ + browser_tabview_bug594958.js \ browser_tabview_bug595191.js \ browser_tabview_bug595436.js \ browser_tabview_bug595518.js \ browser_tabview_bug595521.js \ browser_tabview_bug595560.js \ browser_tabview_bug595804.js \ browser_tabview_bug595930.js \ browser_tabview_bug595943.js \
new file mode 100644 --- /dev/null +++ b/browser/base/content/test/tabview/browser_tabview_bug594958.js @@ -0,0 +1,149 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function test() { + let win; + let cw; + + let getGroupItem = function (index) { + return cw.GroupItems.groupItems[index]; + } + + let newWindow = function (callback) { + newWindowWithTabView(function (tvwin) { + registerCleanupFunction(function () { + if (!tvwin.closed) + tvwin.close(); + }); + + win = tvwin; + cw = win.TabView.getContentWindow(); + + callback(); + }); + } + + let finishTest = function () { + win.close(); + finish(); + } + + // very long page that produces black bars at the right + let html1 = '<html><body style="background-color: #00f;">' + + '<div style="background: #fff; width: 95%; height: 10000px; ' + + ' margin: 0 auto;"></div></body></html>'; + + // very short page that produces black bars at the bottom + let html2 = '<html><body style="background-color: #00f;"></body></html>'; + + let tests = [{ + url: 'data:text/html,' + html1, + colors: [ + {right: [0, 0, 255], bottom: [255, 255, 255]}, + {right: [0, 0, 255], bottom: [255, 255, 255]}, + {right: [0, 0, 255], bottom: [255, 255, 255]} + ] + }, { + url: 'about:blank', + colors: [ + {right: [255, 255, 255], bottom: [255, 255, 255]}, + {right: [255, 255, 255], bottom: [255, 255, 255]}, + {right: [255, 255, 255], bottom: [255, 255, 255]} + ] + }, { + url: 'data:text/html,' + html2, + colors: [ + {right: [0, 0, 255], bottom: [0, 0, 255]}, + {right: [0, 0, 255], bottom: [255, 255, 255]}, + {right: [0, 0, 255], bottom: [0, 0, 255]} + ] + }]; + + let next = function () { + if (!tests.length) { + finishTest(); + return; + } + + 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); + + tab.linkedBrowser.loadURI(test.url); + } + + let checkUrl = function (test) { + let sizes = [[-400, 0], [800, -200], [-400, 200]]; + + let nextSize = function () { + if (!sizes.length) { + next(); + return; + } + + let size = sizes.shift(); + let colors = test.colors.shift(); + let groupItem = getGroupItem(0); + + let checkWithSmallItemSize = function () { + groupItem.setSize(150, 150, true); + groupItem.setUserSize(); + + afterAllTabItemsUpdated(function () { + checkPixelColors(test.url, colors, nextSize); + }, win); + } + + let checkWithNormalItemSize = function () { + groupItem.setSize(300, 300, true); + groupItem.setUserSize(); + + afterAllTabItemsUpdated(function () { + checkPixelColors(test.url, colors, checkWithSmallItemSize); + }, win); + } + + win.resizeBy.apply(win, size); + checkWithNormalItemSize(); + } + + nextSize(); + } + + let checkPixelColors = function (url, colors, callback) { + let tab = win.gBrowser.tabs[0]; + let $canvas = tab._tabViewTabItem.$canvas; + let width = $canvas.width(); + let height = $canvas.height(); + let ctx = $canvas[0].getContext("2d"); + + afterAllTabItemsUpdated(function () { + checkPixelColor(ctx, url, colors.bottom, Math.floor(width / 4), height - 1, 'bottom'); + checkPixelColor(ctx, url, colors.right, width - 1, Math.floor(height / 4), 'right'); + callback(); + }, win); + } + + let checkPixelColor = function (ctx, url, color, x, y, edge) { + let data = ctx.getImageData(x, y, 1, 1).data; + let check = (data[0] == color[0] && data[1] == color[1] && data[2] == color[2]); + ok(check, url + ': ' + edge + ' edge color matches pixel value'); + } + + waitForExplicitFinish(); + newWindow(next); +} + +// --------- +function newWindowWithTabView(callback) { + let win = window.openDialog(getBrowserURL(), "_blank", + "chrome,all,dialog=no,height=600,width=800"); + win.addEventListener("load", function onLoad() { + win.removeEventListener("load", onLoad, false); + showTabView(function () callback(win), win); + }, false); +}