diff --git a/mobile/chrome/content/BrowserView.js b/mobile/chrome/content/BrowserView.js index c523cca9b40..cf22f2a530a 100644 --- a/mobile/chrome/content/BrowserView.js +++ b/mobile/chrome/content/BrowserView.js @@ -266,8 +266,7 @@ BrowserView.prototype = { bvs.visibleX = vr.left; bvs.visibleY = vr.top; - // reclamp minimally to the new visible rect - //this.setViewportDimensions(bvs.viewportRect.right, bvs.viewportRect.bottom); + // XXX should we reclamp minimally to the new visible rect? } else this._viewportChanged(false, false); }, @@ -515,7 +514,7 @@ BrowserView.prototype = { handleMozAfterSizeChange: function handleMozAfterPaint(ev) { // !!! --- RESIZE HACK BEGIN ----- // get the correct properties off of the event, these are wrong because - // we're using a MouseEvent since it has an X and Y prop of some sort and + // 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; @@ -582,6 +581,11 @@ BrowserView.prototype = { ctx.scale(f, f); }, + forceContainerResize: function forceContainerResize() { + let bvs = this._browserViewportState; + if (bvs) + BrowserView.Util.resizeContainerToViewport(this._container, bvs.viewportRect); + }, // ----------------------------------------------------------- // Private instance methods diff --git a/mobile/chrome/content/TileManager.js.in b/mobile/chrome/content/TileManager.js.in index 237d7723458..542006a74da 100644 --- a/mobile/chrome/content/TileManager.js.in +++ b/mobile/chrome/content/TileManager.js.in @@ -113,26 +113,27 @@ const kInitialCacheCapacity = 200; * the width of the screen. This is used in setting the zoomLevel. */ function TileManager(appendTile, removeTile, browserView) { - // backref to the BrowserView object that owns us + /* backref to the BrowserView object that owns us */ this._browserView = browserView; - // callbacks to append / remove a tile to / from the parent + /* callbacks to append / remove a tile to / from the parent */ this._appendTile = appendTile; this._removeTile = removeTile; - // tile cache holds tile objects and pools them under a given capacity + /* tile cache holds tile objects and pools them under a given capacity */ let self = this; this._tileCache = new TileManager.TileCache(function(tile) { self._removeTileSafe(tile); }, -1, -1, kInitialCacheCapacity); - // Rectangle within the viewport that is visible to the user. It is "critical" - // in the sense that it must be rendered as soon as it becomes dirty + /* Rectangle within the viewport that is visible to the user. It is "critical" + * in the sense that it must be rendered as soon as it becomes dirty, and tiles + * within this rectangle should not be evicted for use elsewhere. */ this._criticalRect = null; - // timeout of the non-visible-tiles-crawler to cache renders from the browser + /* timeout of the non-visible-tiles-crawler to cache renders from the browser */ this._idleTileCrawlerTimeout = 0; - // object that keeps state on our current lazyload crawl + /* object that keeps state on our current lazyload crawl */ this._crawler = null; } @@ -246,7 +247,7 @@ TileManager.prototype = { dump(" hold: " + (Date.now() - start) + "\n"); - if (cr) + if (cr && destCriticalRect) cr.copyFrom(destCriticalRect); else this._criticalRect = cr = destCriticalRect; @@ -308,7 +309,7 @@ TileManager.prototype = { } }, - _removeTileSafe: function _removeTileSafe(tile) { debugger; + _removeTileSafe: function _removeTileSafe(tile) { if (tile.appended) { this._removeTile(tile); tile.appended = false; @@ -587,7 +588,7 @@ TileManager.TileCache.prototype = { for (; k >= 0; --k) { if (pool[k].free && ( !evictionGuard || evictionGuard(pool[k]) )) { victim = pool[k]; - --k; + --k; break; } } diff --git a/mobile/chrome/content/browser.js b/mobile/chrome/content/browser.js index cf849630a25..9ba40aee18e 100644 --- a/mobile/chrome/content/browser.js +++ b/mobile/chrome/content/browser.js @@ -314,6 +314,8 @@ var Browser = { _selectedTab : null, windowUtils: window.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils), + scrollbox: null, + scrollboxScroller: null, startup: function() { var self = this; @@ -325,7 +327,8 @@ var Browser = { container.customClicker = this._createContentCustomClicker(bv); - let scrollbox = document.getElementById("scrollbox"); + let scrollbox = this.scrollbox = document.getElementById("scrollbox"); + this.scrollboxScroller = scrollbox.boxObject.QueryInterface(Ci.nsIScrollBoxObject); scrollbox.customDragger = { dragStart: function dragStart(scroller) { @@ -858,7 +861,7 @@ var Browser = { } function elementFromPoint(browser, x, y) { - [x, y] = transformClientToBrowser(browserView, x, y); + [x, y] = transformClientToBrowser(x, y); let cwu = BrowserView.Util.getBrowserDOMWindowUtils(browser); return cwu.elementFromPoint(x, y, true, // ignore root scroll frame @@ -872,6 +875,11 @@ var Browser = { } return { + // XXX whether we are zoomed in or out should be reset to false every time + // we call zoomToPage elsewhere. basically, this variable isn't a great way + // to track state of zoomed in- our out-ness. + zoomIn: false, + singleClick: function singleClick(cX, cY) { if (window.infoMode) { [cX, cY] = transformClientToBrowser(cX, cY); @@ -898,16 +906,17 @@ var Browser = { let zoomElement = elementFromPoint(browser, cX2, cY2); if (zoomElement) { - // TODO actually zoom to and from element - //browserView.zoom(this.zoomDir); - //this.zoomDir *= -1; - dump('zooming to/from element: ' + zoomElement + '\n'); - } + dump('zooming to/from element: ' + zoomElement + + ' :: ' + zoomElement.id + ' :: ' + zoomElement.name + '\n'); + dump(' at ' + cX2 + ', ' + cY2 + '\n'); - //let [x, y] = transformClientToBrowser(cX1, cY1); - //dispatchContentClick(browser, x, y); - //[x, y] = transformClientToBrowser(cX2, cY2); - //dispatchContentClick(browser, x, y); + this.zoomIn = !this.zoomIn; + + if (this.zoomIn) + Browser.zoomToElement(zoomElement); + else + Browser.zoomFromElement(zoomElement); + } } } }; @@ -978,6 +987,93 @@ var Browser = { return snappedX; }, + zoomToElement: function zoomToElement(aElement) { + const margin = 15; + + let bv = Browser._browserView; + let scroller = Browser.scrollboxScroller; + + let elRect = Browser.getBoundingContentRect(aElement); + let elWidth = elRect.width; + let vrWidth = bv.getVisibleRectWidth(); + /* Try to set zoom-level such that once zoomed element is as wide + * as the visible rect */ + let zoomLevel = vrWidth / (elWidth + (2 * margin)); + + bv.beginBatchOperation(); + + bv.setZoomLevel(zoomLevel); + + /* If zoomLevel ends up clamped to less than asked for, calculate + * how many more screen pixels will fit horizontally in addition to + * element's width. This ensures that more of the webpage is + * showing instead of the navbar. Bug 480595. */ + let screenW = vrWidth - bv.browserToViewport(elWidth); + let xpadding = Math.max(margin, screenW); + + let x0 = bv.getVisibleRectX(); + let y0 = bv.getVisibleRectY(); + + let x = bv.browserToViewport(elRect.left) - xpadding; + let y = bv.browserToViewport(elRect.top) - margin; + + x = Math.floor(Math.max(x, 0)); + y = Math.floor(Math.max(y, 0)); + + bv.forceContainerResize(); + Browser.forceChromeReflow(); + + Browser.scrollbox.customDragger.dragMove(x - x0, y - y0, scroller); + + bv.commitBatchOperation(); + }, + + zoomFromElement: function zoomFromElement(aElement) { + let bv = Browser._browserView; + let scroller = Browser.scrollboxScroller; + + let elRect = Browser.getBoundingContentRect(aElement); + + bv.beginBatchOperation(); + + bv.zoomToPage(); + + let x0 = bv.getVisibleRectX(); + let y0 = bv.getVisibleRectY(); + + let x = bv.browserToViewport(elRect.left); + let y = bv.browserToViewport(elRect.top); + + x = Math.floor(Math.max(x, 0)); + y = Math.floor(Math.max(y, 0)); + + bv.forceContainerResize(); + Browser.forceChromeReflow(); + + Browser.scrollbox.customDragger.dragMove(x - x0, y - y0, scroller); + + bv.commitBatchOperation(); + }, + + getBoundingContentRect: function getBoundingContentRect(contentElem) { + let browser = Browser._browserView.getBrowser(); + + if (!browser) + return null; + + let scrollX = {}; + let scrollY = {}; + + let cwu = BrowserView.Util.getBrowserDOMWindowUtils(browser); + cwu.getScrollXY(false, scrollX, scrollY); + + let r = contentElem.getBoundingClientRect(); + + return new wsRect(r.left + scrollX.value, + r.top + scrollY.value, + r.width, r.height); + }, + /** * Transform x and y from client coordinates to BrowserView coordinates. */ @@ -986,9 +1082,23 @@ var Browser = { let containerBCR = container.getBoundingClientRect(); let x0 = Math.round(containerBCR.left); - let y0 = Math.round(containerBCR.top); + let y0; + if (arguments.length > 1) + y0 = Math.round(containerBCR.top); - return [x - x0, y - y0]; + return (arguments.length > 1) ? [x - x0, y - y0] : (x - x0); + }, + + browserViewToClient: function browserViewToClient(x, y) { + let container = document.getElementById("tile-container"); + let containerBCR = container.getBoundingClientRect(); + + let x0 = Math.round(-containerBCR.left); + let y0; + if (arguments.length > 1) + y0 = Math.round(-containerBCR.top); + + return (arguments.length > 1) ? [x - x0, y - y0] : (x - x0); }, /** @@ -1019,6 +1129,10 @@ var Browser = { let y = {}; scroller.getPosition(x, y); return [x.value, y.value]; + }, + + forceChromeReflow: function forceChromeReflow() { + let dummy = getComputedStyle(document.documentElement, "").width; } };