зеркало из https://github.com/mozilla/pjs.git
Bug 480958. Update viewport dimensions as per MozScrolledAreaChanged. r=mfinkle
This commit is contained in:
Родитель
962da0c7ca
Коммит
9900b8fc86
|
@ -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);
|
||||
}
|
||||
},
|
||||
|
|
Загрузка…
Ссылка в новой задаче