Bug 480958. Update viewport dimensions as per MozScrolledAreaChanged. r=mfinkle

This commit is contained in:
Roy Frostig 2009-09-08 15:02:49 -07:00
Родитель 962da0c7ca
Коммит 9900b8fc86
3 изменённых файлов: 197 добавлений и 49 удалений

Просмотреть файл

@ -171,6 +171,10 @@ BrowserView.Util = {
return browser.__BrowserView__vps;
},
/**
* Calling this is likely to cause a reflow of the browser's document. Use
* wisely.
*/
getBrowserDimensions: function getBrowserDimensions(browser) {
let cdoc = browser.contentDocument;
if (cdoc instanceof SVGDocument) {
@ -235,7 +239,7 @@ BrowserView.prototype = {
this._contentWindow = null;
this._renderMode = 0;
this._offscreenDepth = 0;
let cacheSize = kBrowserViewCacheSize;
try {
cacheSize = gPrefService.getIntPref("tile.cache.size");
@ -262,6 +266,7 @@ BrowserView.prototype = {
this._tileManager = new TileManager(this._appendTile, this._removeTile, this, cacheSize);
this._visibleRectFactory = visibleRectFactory;
this._suppressZoomToPage = false;
this._idleServiceObserver = new BrowserView.IdleServiceObserver(this);
this._idleService = Cc["@mozilla.org/widget/idleservice;1"].getService(Ci.nsIIdleService);
@ -282,9 +287,16 @@ BrowserView.prototype = {
if (!bvs)
return;
if (!causedByZoom)
this._suppressZoomToPage = false;
let oldwidth = bvs.viewportRect.right;
let oldheight = bvs.viewportRect.bottom;
bvs.viewportRect.right = width;
bvs.viewportRect.bottom = height;
let sizeChanged = (oldwidth != width || oldheight != height);
// XXX we might not want the user's page to disappear from under them
// at this point, which could happen if the container gets resized such
// that visible rect becomes entirely outside of viewport rect. might
@ -292,19 +304,33 @@ BrowserView.prototype = {
// then again, we could also argue this is the responsibility of the
// caller who would do such a thing...
this._viewportChanged(true, !!causedByZoom);
this._viewportChanged(sizeChanged, sizeChanged && !!causedByZoom);
},
setZoomLevel: function setZoomLevel(zl) {
/**
* @return [width, height]
*/
getViewportDimensions: function getViewportDimensions() {
let bvs = this._browserViewportState;
if (!bvs)
throw "Cannot get viewport dimensions when no browser is set";
return [bvs.viewportRect.right, bvs.viewportRect.bottom];
},
setZoomLevel: function setZoomLevel(zoomLevel) {
let bvs = this._browserViewportState;
if (!bvs)
return;
let newZL = BrowserView.Util.clampZoomLevel(zl);
if (newZL != bvs.zoomLevel) {
let newZoomLevel = BrowserView.Util.clampZoomLevel(zoomLevel);
if (newZoomLevel != bvs.zoomLevel) {
let browserW = this.viewportToBrowser(bvs.viewportRect.right);
let browserH = this.viewportToBrowser(bvs.viewportRect.bottom);
bvs.zoomLevel = newZL; // side-effect: now scale factor in transformations is newZL
bvs.zoomLevel = newZoomLevel; // side-effect: now scale factor in transformations is newZoomLevel
this.setViewportDimensions(this.browserToViewport(browserW),
this.browserToViewport(browserH),
true);
@ -439,19 +465,17 @@ BrowserView.prototype = {
throw "Cannot set non-null browser with null BrowserViewportState";
}
let browserChanged = (this._browser !== browser);
let oldBrowser = this._browser;
if (this._browser) {
this._browser.removeEventListener("MozAfterPaint", this.handleMozAfterPaint, false);
this._browser.removeEventListener("scroll", this.handlePageScroll, false);
let browserChanged = (oldBrowser !== browser);
// !!! --- RESIZE HACK BEGIN -----
// change to the real event type and perhaps refactor the handler function name
this._browser.removeEventListener("FakeMozAfterSizeChange", this.handleMozAfterSizeChange, false);
// !!! --- RESIZE HACK END -------
if (oldBrowser) {
oldBrowser.removeEventListener("MozAfterPaint", this.handleMozAfterPaint, false);
oldBrowser.removeEventListener("scroll", this.handlePageScroll, false);
oldBrowser.removeEventListener("MozScrolledAreaChanged", this.handleMozScrolledAreaChanged, false);
this._browser.setAttribute("type", "content");
this._browser.docShell.isOffScreenBrowser = false;
oldBrowser.setAttribute("type", "content");
oldBrowser.docShell.isOffScreenBrowser = false;
}
this._browser = browser;
@ -465,11 +489,7 @@ BrowserView.prototype = {
browser.addEventListener("MozAfterPaint", this.handleMozAfterPaint, false);
browser.addEventListener("scroll", this.handlePageScroll, false);
// !!! --- RESIZE HACK BEGIN -----
// change to the real event type and perhaps refactor the handler function name
browser.addEventListener("FakeMozAfterSizeChange", this.handleMozAfterSizeChange, false);
// !!! --- RESIZE HACK END -------
browser.addEventListener("MozScrolledAreaChanged", this.handleMozScrolledAreaChanged, false);
if (doZoom) {
browser.docShell.isOffScreenBrowser = true;
@ -527,28 +547,31 @@ BrowserView.prototype = {
this.onAfterVisibleMove();
},
// !!! --- RESIZE HACK BEGIN -----
simulateMozAfterSizeChange: function simulateMozAfterSizeChange() {
let [w, h] = BrowserView.Util.getBrowserDimensions(this._browser);
let ev = document.createEvent("MouseEvents");
ev.initMouseEvent("FakeMozAfterSizeChange", false, false, window, 0, w, h, 0, 0, false, false, false, false, 0, null);
this._browser.dispatchEvent(ev);
},
// !!! --- RESIZE HACK END -------
handleMozScrolledAreaChanged: function handleMozScrolledAreaChanged(ev) {
if (ev.target != this._browser.contentDocument)
return;
handleMozAfterSizeChange: function handleMozAfterSizeChange(ev) {
// !!! --- RESIZE HACK BEGIN -----
// get the correct properties off of the event, these are wrong because
// we're using a MouseEvent, as it has an X and Y prop of some sort and
// we piggyback on that.
let w = ev.screenX;
let h = ev.screenY;
// !!! --- RESIZE HACK END -------
this.setViewportDimensions(this.browserToViewport(w), this.browserToViewport(h));
let { x: scrollX, y: scrollY } = BrowserView.Util.getContentScrollOffset(this._browser);
let x = ev.x + scrollX;
let y = ev.y + scrollY;
let w = ev.width;
let h = ev.height;
// Adjust width and height from the incoming event properties so that we
// ignore changes to width and height contributed by growth in page
// quadrants other than x > 0 && y > 0.
if (x < 0) w += x;
if (y < 0) h += y;
this.setViewportDimensions(this.browserToViewport(w),
this.browserToViewport(h));
},
zoomToPage: function zoomToPage() {
this.setZoomLevel(this.getZoomForPage());
// See invalidateEntireView() for why we might be suppressing this zoom.
if (!this._suppressZoomToPage)
this.setZoomLevel(this.getZoomForPage());
},
getZoomForPage: function getZoomForPage() {
@ -559,7 +582,9 @@ BrowserView.prototype = {
if (Util.contentIsHandheld(browser))
return 1;
let [w, h] = BrowserView.Util.getBrowserDimensions(browser);
let bvs = this._browserViewportState; // browser exists, so bvs must as well
let w = this.viewportToBrowser(bvs.viewportRect.right);
let h = this.viewportToBrowser(bvs.viewportRect.bottom);
return BrowserView.Util.pageZoomLevel(this.getVisibleRect(), w, h);
},
@ -578,6 +603,55 @@ BrowserView.prototype = {
this.setZoomLevel(bvs.zoomLevel + zoomDelta);
},
//
// MozAfterPaint events do not guarantee to inform us of all
// invalidated paints (See
// https://developer.mozilla.org/en/Gecko-Specific_DOM_Events#Important_notes
// for details on what the event *does* guarantee). This is only an
// issue when the same current <browser> is used to navigate to a
// new page. Unless a zoom was issued during the page transition
// (e.g. a call to zoomToPage() or something of that nature), we
// aren't guaranteed that we've actually invalidated the entire
// page. We don't want to leave bits of the previous page in the
// view of the new one, so this method exists as a way for Browser
// to inform us that the page is changing, and that we really ought
// to invalidate everything. Ideally, we wouldn't have to rely on
// this being called, and we would get proper invalidates for the
// whole page no matter what is or is not visible.
//
// Note that calling this function isn't necessary in almost all
// cases, but should be done for correctness. Most of the time, one
// of the following two conditions is satisfied. Either
//
// (1) Pages have different widths so the Browser calls a
// zoomToPage() which forces a dirtyAll, or
// (2) MozAfterPaint does indeed inform us of dirtyRects covering
// the entire page (everything that could possibly become
// visible).
//
// Since calling this method means "everything is wrong and the
// <browser> is about to start giving you new data via MozAfterPaint
// and MozScrolledAreaChanged", we also supress any zoomToPage()
// that might be called until the next time setViewportDimensions()
// is called (which will probably be caused by an incoming
// MozScrolledAreaChanged event, or via someone very eagerly setting
// it manually so that they can zoom to that manual "page" width).
//
/**
* Invalidates the entire page by throwing away any cached graphical
* portions of the view and refusing to allow a zoomToPage() until
* the next explicit update of the viewport dimensions.
*
* This method should be called when the <browser> last set by
* setBrowser() is about to navigate to a new page.
*/
invalidateEntireView: function invalidateEntireView() {
if (this._browserViewportState) {
this._viewportChanged(false, true, true);
this._suppressZoomToPage = true;
}
},
/**
* Render a rectangle within the browser viewport to the destination canvas
* under the given scale.

Просмотреть файл

@ -151,18 +151,95 @@ TileManager.prototype = {
dirtyAll) {
let tc = this._tileCache;
tc.iBound = Math.ceil(viewportRect.right / kTileWidth);
tc.jBound = Math.ceil(viewportRect.bottom / kTileHeight);
let iBoundOld = tc.iBound;
let jBoundOld = tc.jBound;
let iBound = tc.iBound = Math.ceil(viewportRect.right / kTileWidth) - 1;
let jBound = tc.jBound = Math.ceil(viewportRect.bottom / kTileHeight) - 1;
if (criticalRect.isEmpty() || !criticalRect.equals(this._criticalRect)) {
this.beginCriticalMove(criticalRect);
this.endCriticalMove(criticalRect, !boundsSizeChanged);
this.endCriticalMove(criticalRect, !(dirtyAll || boundsSizeChanged));
}
if (boundsSizeChanged) {
// TODO fastpath if !dirtyAll
if (dirtyAll) {
this.dirtyRects([viewportRect.clone()], true);
} else if (boundsSizeChanged) {
//
// This is a special case. The bounds size changed, but we are
// told that not everything is dirty (so mayhap content grew or
// shrank vertically or horizontally). We might have old tiles
// around in those areas just due to the fact that they haven't
// been yet evicted, so we patrol the new regions in search of
// any such leftover tiles and mark those we find as dirty.
//
// The two dirty rects below mark dirty any renegade tiles in
// the newly annexed grid regions as per the following diagram
// of the "new" viewport.
//
// +------------+------+
// |old | A |
// |viewport | |
// | | |
// | | |
// | | |
// +------------+ |
// | B | |
// | | |
// +------------+------+
//
// The first rectangle covers annexed region A, the second
// rectangle covers annexed region B.
//
// XXXrf If the tiles are large, then we are creating some
// redundant work here by invalidating the entire tile that
// the old viewport boundary crossed (note markDirty() being
// called with no rectangle parameter). The rectangular area
// within the tile lying beyond the old boundary is certainly
// dirty, but not the area before. Moreover, since we mark
// dirty entire tiles that may cross into the old viewport,
// they might, in particular, cross into the critical rect
// (which is anyhwere in the old viewport), so we call a
// criticalRectPaint() for such cleanup. We do all this more
// or less because we don't have much of a notion of "the old
// viewport" here except for in the sense that we know the
// index bounds on the tilecache grid from before (and the new
// index bounds now).
//
let t, l, b, r, rect;
let rects = [];
if (iBoundOld <= iBound) {
l = iBoundOld * kTileWidth;
t = 0;
r = (iBound + 1) * kTileWidth;
b = (jBound + 1) * kTileHeight;
rect = new Rect(l, t, r - l, b - t);
rect.restrictTo(viewportRect);
if (!rect.isEmpty())
rects.push(rect);
}
if (jBoundOld <= jBound) {
l = 0;
t = jBoundOld * kTileHeight;
r = (iBound + 1) * kTileWidth;
b = (jBound + 1) * kTileHeight;
rect = new Rect(l, t, r - l, b - t);
rect.restrictTo(viewportRect);
if (!rect.isEmpty())
rects.push(rect);
}
this.dirtyRects(rects, true);
}
},
dirtyRects: function dirtyRects(rects, doCriticalRender) {

Просмотреть файл

@ -2444,10 +2444,6 @@ Tab.prototype = {
let bv = Browser._browserView;
if (this == Browser.selectedTab) {
// !!! --- RESIZE HACK BEGIN -----
bv.simulateMozAfterSizeChange();
// !!! --- RESIZE HACK END -----
let restoringPage = (this._state != null);
if (!this._browserViewportState.zoomChanged && !restoringPage) {
@ -2485,6 +2481,7 @@ Tab.prototype = {
if (!this._loadingTimeout) {
Browser._browserView.beginBatchOperation();
Browser._browserView.invalidateEntireView();
this._loadingTimeout = setTimeout(Util.bind(this._resizeAndPaint, this), 2000);
}
},