diff options
Diffstat (limited to 'toolkit/content/tests/fennec-tile-testapp/chrome')
12 files changed, 0 insertions, 4009 deletions
diff --git a/toolkit/content/tests/fennec-tile-testapp/chrome/chrome.manifest b/toolkit/content/tests/fennec-tile-testapp/chrome/chrome.manifest deleted file mode 100644 index 118354c81..000000000 --- a/toolkit/content/tests/fennec-tile-testapp/chrome/chrome.manifest +++ /dev/null @@ -1 +0,0 @@ -content tile file:content/ diff --git a/toolkit/content/tests/fennec-tile-testapp/chrome/content/BrowserView.js b/toolkit/content/tests/fennec-tile-testapp/chrome/content/BrowserView.js deleted file mode 100644 index c498810df..000000000 --- a/toolkit/content/tests/fennec-tile-testapp/chrome/content/BrowserView.js +++ /dev/null @@ -1,694 +0,0 @@ -// -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -var Ci = Components.interfaces; - -// --- REMOVE --- -var noop = function() {}; -var endl = '\n'; -// -------------- - -function BrowserView(container, visibleRect) { - bindAll(this); - this.init(container, visibleRect); -} - -/** - * A BrowserView maintains state of the viewport (browser, zoom level, - * dimensions) and the visible rectangle into the viewport, for every - * browser it is given (cf setBrowser()). In updates to the viewport state, - * a BrowserView (using its TileManager) renders parts of the page quasi- - * intelligently, with guarantees of having rendered and appended all of the - * visible browser content (aka the "critical rectangle"). - * - * State is characterized in large part by two rectangles (and an implicit third): - * - Viewport: Always rooted at the origin, ie with (left, top) at (0, 0). The - * width and height (right and bottom) of this rectangle are that of the - * current viewport, which corresponds more or less to the transformed - * browser content (scaled by zoom level). - * - Visible: Corresponds to the client's viewing rectangle in viewport - * coordinates. Has (top, left) corresponding to position, and width & height - * corresponding to the clients viewing dimensions. Take note that the top - * and left of the visible rect are per-browser state, but that the width - * and height persist across setBrowser() calls. This is best explained by - * a simple example: user views browser A, pans to position (x0, y0), switches - * to browser B, where she finds herself at position (x1, y1), tilts her - * device so that visible rectangle's width and height change, and switches - * back to browser A. She expects to come back to position (x0, y0), but her - * device remains tilted. - * - Critical (the implicit one): The critical rectangle is the (possibly null) - * intersection of the visible and viewport rectangles. That is, it is that - * region of the viewport which is visible to the user. We care about this - * because it tells us which region must be rendered as soon as it is dirtied. - * The critical rectangle is mostly state that we do not keep in BrowserView - * but that our TileManager maintains. - * - * Example rectangle state configurations: - * - * - * +-------------------------------+ - * |A | - * | | - * | | - * | | - * | +----------------+ | - * | |B,C | | - * | | | | - * | | | | - * | | | | - * | +----------------+ | - * | | - * | | - * | | - * | | - * | | - * +-------------------------------+ - * - * - * A = viewport ; at (0, 0) - * B = visible ; at (x, y) where x > 0, y > 0 - * C = critical ; at (x, y) - * - * - * - * +-------------------------------+ - * |A | - * | | - * | | - * | | - * +----+-----------+ | - * |B .C | | - * | . | | - * | . | | - * | . | | - * +----+-----------+ | - * | | - * | | - * | | - * | | - * | | - * +-------------------------------+ - * - * - * A = viewport ; at (0, 0) - * B = visible ; at (x, y) where x < 0, y > 0 - * C = critical ; at (0, y) - * - * - * Maintaining per-browser state is a little bit of a hack involving attaching - * an object as the obfuscated dynamic JS property of the browser object, that - * hopefully no one but us will touch. See getViewportStateFromBrowser() for - * the property name. - */ -BrowserView.prototype = ( -function() { - - // ----------------------------------------------------------- - // Privates - // - - const kZoomLevelMin = 0.2; - const kZoomLevelMax = 4.0; - const kZoomLevelPrecision = 10000; - - function visibleRectToCriticalRect(visibleRect, browserViewportState) { - return visibleRect.intersect(browserViewportState.viewportRect); - } - - function clampZoomLevel(zl) { - let bounded = Math.min(Math.max(kZoomLevelMin, zl), kZoomLevelMax); - return Math.round(bounded * kZoomLevelPrecision) / kZoomLevelPrecision; - } - - function pageZoomLevel(visibleRect, browserW, browserH) { - return clampZoomLevel(visibleRect.width / browserW); - } - - function seenBrowser(browser) { - return !!(browser.__BrowserView__vps); - } - - function initBrowserState(browser, visibleRect) { - let [browserW, browserH] = getBrowserDimensions(browser); - - let zoomLevel = pageZoomLevel(visibleRect, browserW, browserH); - let viewportRect = (new wsRect(0, 0, browserW, browserH)).scale(zoomLevel, zoomLevel); - - dump('--- initing browser to ---' + endl); - browser.__BrowserView__vps = new BrowserView.BrowserViewportState(viewportRect, - visibleRect.x, - visibleRect.y, - zoomLevel); - dump(browser.__BrowserView__vps.toString() + endl); - dump('--------------------------' + endl); - } - - function getViewportStateFromBrowser(browser) { - return browser.__BrowserView__vps; - } - - function getBrowserDimensions(browser) { - return [browser.scrollWidth, browser.scrollHeight]; - } - - function getContentScrollValues(browser) { - let cwu = getBrowserDOMWindowUtils(browser); - let scrollX = {}; - let scrollY = {}; - cwu.getScrollXY(false, scrollX, scrollY); - - return [scrollX.value, scrollY.value]; - } - - function getBrowserDOMWindowUtils(browser) { - return browser.contentWindow - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - } - - function getNewBatchOperationState() { - return { - viewportSizeChanged: false, - dirtyAll: false - }; - } - - function clampViewportWH(width, height, visibleRect) { - let minW = visibleRect.width; - let minH = visibleRect.height; - return [Math.max(width, minW), Math.max(height, minH)]; - } - - function initContainer(container, visibleRect) { - container.style.width = visibleRect.width + 'px'; - container.style.height = visibleRect.height + 'px'; - container.style.overflow = '-moz-hidden-unscrollable'; - } - - function resizeContainerToViewport(container, viewportRect) { - container.style.width = viewportRect.width + 'px'; - container.style.height = viewportRect.height + 'px'; - } - - // !!! --- RESIZE HACK BEGIN ----- - function simulateMozAfterSizeChange(browser, width, height) { - let ev = document.createElement("MouseEvents"); - ev.initEvent("FakeMozAfterSizeChange", false, false, window, 0, width, height); - browser.dispatchEvent(ev); - } - // !!! --- RESIZE HACK END ------- - - // --- Change of coordinates functions --- // - - - // The following returned object becomes BrowserView.prototype - return { - - // ----------------------------------------------------------- - // Public instance methods - // - - init: function init(container, visibleRect) { - this._batchOps = []; - this._container = container; - this._browserViewportState = null; - this._renderMode = 0; - this._tileManager = new TileManager(this._appendTile, this._removeTile, this); - this.setVisibleRect(visibleRect); - - // !!! --- RESIZE HACK BEGIN ----- - // remove this eventually - this._resizeHack = { - maxSeenW: 0, - maxSeenH: 0 - }; - // !!! --- RESIZE HACK END ------- - }, - - setVisibleRect: function setVisibleRect(r) { - let bvs = this._browserViewportState; - let vr = this._visibleRect; - - if (!vr) - this._visibleRect = vr = r.clone(); - else - vr.copyFrom(r); - - if (bvs) { - bvs.visibleX = vr.left; - bvs.visibleY = vr.top; - - // reclamp minimally to the new visible rect - // this.setViewportDimensions(bvs.viewportRect.right, bvs.viewportRect.bottom); - } else - this._viewportChanged(false, false); - }, - - getVisibleRect: function getVisibleRect() { - return this._visibleRect.clone(); - }, - - getVisibleRectX: function getVisibleRectX() { return this._visibleRect.x; }, - getVisibleRectY: function getVisibleRectY() { return this._visibleRect.y; }, - getVisibleRectWidth: function getVisibleRectWidth() { return this._visibleRect.width; }, - getVisibleRectHeight: function getVisibleRectHeight() { return this._visibleRect.height; }, - - setViewportDimensions: function setViewportDimensions(width, height, causedByZoom) { - let bvs = this._browserViewportState; - let vis = this._visibleRect; - - if (!bvs) - return; - - // [width, height] = clampViewportWH(width, height, vis); - bvs.viewportRect.right = width; - bvs.viewportRect.bottom = 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 - // be wise to define what UX should be in this case, like a move occurs. - // then again, we could also argue this is the responsibility of the - // caller who would do such a thing... - - this._viewportChanged(true, !!causedByZoom); - }, - - setZoomLevel: function setZoomLevel(zl) { - let bvs = this._browserViewportState; - - if (!bvs) - return; - - let newZL = clampZoomLevel(zl); - - if (newZL != 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 - this.setViewportDimensions(this.browserToViewport(browserW), - this.browserToViewport(browserH)); - } - }, - - getZoomLevel: function getZoomLevel() { - let bvs = this._browserViewportState; - if (!bvs) - return undefined; - - return bvs.zoomLevel; - }, - - beginBatchOperation: function beginBatchOperation() { - this._batchOps.push(getNewBatchOperationState()); - this.pauseRendering(); - }, - - commitBatchOperation: function commitBatchOperation() { - let bops = this._batchOps; - - if (bops.length == 0) - return; - - let opState = bops.pop(); - this._viewportChanged(opState.viewportSizeChanged, opState.dirtyAll); - this.resumeRendering(); - }, - - discardBatchOperation: function discardBatchOperation() { - let bops = this._batchOps; - bops.pop(); - this.resumeRendering(); - }, - - discardAllBatchOperations: function discardAllBatchOperations() { - let bops = this._batchOps; - while (bops.length > 0) - this.discardBatchOperation(); - }, - - moveVisibleBy: function moveVisibleBy(dx, dy) { - let vr = this._visibleRect; - let vs = this._browserViewportState; - - this.onBeforeVisibleMove(dx, dy); - this.onAfterVisibleMove(dx, dy); - }, - - moveVisibleTo: function moveVisibleTo(x, y) { - let visibleRect = this._visibleRect; - let dx = x - visibleRect.x; - let dy = y - visibleRect.y; - this.moveBy(dx, dy); - }, - - /** - * Calls to this function need to be one-to-one with calls to - * resumeRendering() - */ - pauseRendering: function pauseRendering() { - this._renderMode++; - }, - - /** - * Calls to this function need to be one-to-one with calls to - * pauseRendering() - */ - resumeRendering: function resumeRendering(renderNow) { - if (this._renderMode > 0) - this._renderMode--; - - if (renderNow || this._renderMode == 0) - this._tileManager.criticalRectPaint(); - }, - - isRendering: function isRendering() { - return (this._renderMode == 0); - }, - - /** - * @param dx Guess delta to destination x coordinate - * @param dy Guess delta to destination y coordinate - */ - onBeforeVisibleMove: function onBeforeVisibleMove(dx, dy) { - let vs = this._browserViewportState; - let vr = this._visibleRect; - - let destCR = visibleRectToCriticalRect(vr.clone().translate(dx, dy), vs); - - this._tileManager.beginCriticalMove(destCR); - }, - - /** - * @param dx Actual delta to destination x coordinate - * @param dy Actual delta to destination y coordinate - */ - onAfterVisibleMove: function onAfterVisibleMove(dx, dy) { - let vs = this._browserViewportState; - let vr = this._visibleRect; - - vr.translate(dx, dy); - vs.visibleX = vr.left; - vs.visibleY = vr.top; - - let cr = visibleRectToCriticalRect(vr, vs); - - this._tileManager.endCriticalMove(cr, this.isRendering()); - }, - - setBrowser: function setBrowser(browser, skipZoom) { - let currentBrowser = this._browser; - - let browserChanged = (currentBrowser !== browser); - - if (currentBrowser) { - currentBrowser.removeEventListener("MozAfterPaint", this.handleMozAfterPaint, false); - - // !!! --- RESIZE HACK BEGIN ----- - // change to the real event type and perhaps refactor the handler function name - currentBrowser.removeEventListener("FakeMozAfterSizeChange", this.handleMozAfterSizeChange, false); - // !!! --- RESIZE HACK END ------- - - this.discardAllBatchOperations(); - - currentBrowser.setAttribute("type", "content"); - currentBrowser.docShell.isOffScreenBrowser = false; - } - - this._restoreBrowser(browser); - - browser.setAttribute("type", "content-primary"); - - this.beginBatchOperation(); - - browser.addEventListener("MozAfterPaint", this.handleMozAfterPaint, 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 ------- - - if (!skipZoom) { - browser.docShell.isOffScreenBrowser = true; - this.zoomToPage(); - } - - this._viewportChanged(browserChanged, browserChanged); - - this.commitBatchOperation(); - }, - - handleMozAfterPaint: function handleMozAfterPaint(ev) { - let browser = this._browser; - let tm = this._tileManager; - let vs = this._browserViewportState; - - let [scrollX, scrollY] = getContentScrollValues(browser); - let clientRects = ev.clientRects; - - // !!! --- RESIZE HACK BEGIN ----- - // remove this, cf explanation in loop below - let hack = this._resizeHack; - let hackSizeChanged = false; - // !!! --- RESIZE HACK END ------- - - let rects = []; - // loop backwards to avoid xpconnect penalty for .length - for (let i = clientRects.length - 1; i >= 0; --i) { - let e = clientRects.item(i); - let r = new wsRect(e.left + scrollX, - e.top + scrollY, - e.width, e.height); - - this.browserToViewportRect(r); - r.round(); - - if (r.right < 0 || r.bottom < 0) - continue; - - // !!! --- RESIZE HACK BEGIN ----- - // remove this. this is where we make 'lazy' calculations - // that hint at a browser size change and fake the size change - // event dispach - if (r.right > hack.maxW) { - hack.maxW = rect.right; - hackSizeChanged = true; - } - if (r.bottom > hack.maxH) { - hack.maxH = rect.bottom; - hackSizeChanged = true; - } - // !!! --- RESIZE HACK END ------- - - r.restrictTo(vs.viewportRect); - rects.push(r); - } - - // !!! --- RESIZE HACK BEGIN ----- - // remove this, cf explanation in loop above - if (hackSizeChanged) - simulateMozAfterSizeChange(browser, hack.maxW, hack.maxH); - // !!! --- RESIZE HACK END ------- - - tm.dirtyRects(rects, this.isRendering()); - }, - - 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 piggyback on that. - let w = ev.screenX; - let h = ev.screenY; - // !!! --- RESIZE HACK END ------- - - this.setViewportDimensions(w, h); - }, - - zoomToPage: function zoomToPage() { - let browser = this._browser; - - if (!browser) - return; - - let [w, h] = getBrowserDimensions(browser); - this.setZoomLevel(pageZoomLevel(this._visibleRect, w, h)); - }, - - zoom: function zoom(aDirection) { - if (aDirection == 0) - return; - - var zoomDelta = 0.05; // 1/20 - if (aDirection >= 0) - zoomDelta *= -1; - - this.zoomLevel = this._zoomLevel + zoomDelta; - }, - - viewportToBrowser: function viewportToBrowser(x) { - let bvs = this._browserViewportState; - - if (!bvs) - throw "No browser is set"; - - return x / bvs.zoomLevel; - }, - - browserToViewport: function browserToViewport(x) { - let bvs = this._browserViewportState; - - if (!bvs) - throw "No browser is set"; - - return x * bvs.zoomLevel; - }, - - viewportToBrowserRect: function viewportToBrowserRect(rect) { - let f = this.viewportToBrowser(1.0); - return rect.scale(f, f); - }, - - browserToViewportRect: function browserToViewportRect(rect) { - let f = this.browserToViewport(1.0); - return rect.scale(f, f); - }, - - browserToViewportCanvasContext: function browserToViewportCanvasContext(ctx) { - let f = this.browserToViewport(1.0); - ctx.scale(f, f); - }, - - - // ----------------------------------------------------------- - // Private instance methods - // - - _restoreBrowser: function _restoreBrowser(browser) { - let vr = this._visibleRect; - - if (!seenBrowser(browser)) - initBrowserState(browser, vr); - - let bvs = getViewportStateFromBrowser(browser); - - this._contentWindow = browser.contentWindow; - this._browser = browser; - this._browserViewportState = bvs; - vr.left = bvs.visibleX; - vr.top = bvs.visibleY; - this._tileManager.setBrowser(browser); - }, - - _viewportChanged: function _viewportChanged(viewportSizeChanged, dirtyAll) { - let bops = this._batchOps; - - if (bops.length > 0) { - let opState = bops[bops.length - 1]; - - if (viewportSizeChanged) - opState.viewportSizeChanged = viewportSizeChanged; - - if (dirtyAll) - opState.dirtyAll = dirtyAll; - - return; - } - - let bvs = this._browserViewportState; - let vis = this._visibleRect; - - // !!! --- RESIZE HACK BEGIN ----- - // We want to uncomment this for perf, but we can't with the hack in place - // because the mozAfterPaint gives us rects that we use to create the - // fake mozAfterResize event, so we can't just clear things. - /* - if (dirtyAll) { - // We're about to mark the entire viewport dirty, so we can clear any - // queued afterPaint events that will cause redundant draws - getBrowserDOMWindowUtils(this._browser).clearMozAfterPaintEvents(); - } - */ - // !!! --- RESIZE HACK END ------- - - if (bvs) { - resizeContainerToViewport(this._container, bvs.viewportRect); - - this._tileManager.viewportChangeHandler(bvs.viewportRect, - visibleRectToCriticalRect(vis, bvs), - viewportSizeChanged, - dirtyAll); - } - }, - - _appendTile: function _appendTile(tile) { - let canvas = tile.getContentImage(); - - /* - canvas.style.position = "absolute"; - canvas.style.left = tile.x + "px"; - canvas.style.top = tile.y + "px"; - */ - - canvas.setAttribute("style", "position: absolute; left: " + tile.boundRect.left + "px; " + "top: " + tile.boundRect.top + "px;"); - - this._container.appendChild(canvas); - - // dump('++ ' + tile.toString(true) + endl); - }, - - _removeTile: function _removeTile(tile) { - let canvas = tile.getContentImage(); - - this._container.removeChild(canvas); - - // dump('-- ' + tile.toString(true) + endl); - } - - }; - -} -)(); - - -// ----------------------------------------------------------- -// Helper structures -// - -BrowserView.BrowserViewportState = function(viewportRect, - visibleX, - visibleY, - zoomLevel) { - - this.init(viewportRect, visibleX, visibleY, zoomLevel); -}; - -BrowserView.BrowserViewportState.prototype = { - - init: function init(viewportRect, visibleX, visibleY, zoomLevel) { - this.viewportRect = viewportRect; - this.visibleX = visibleX; - this.visibleY = visibleY; - this.zoomLevel = zoomLevel; - }, - - clone: function clone() { - return new BrowserView.BrowserViewportState(this.viewportRect, - this.visibleX, - this.visibleY, - this.zoomLevel); - }, - - toString: function toString() { - let props = ['\tviewportRect=' + this.viewportRect.toString(), - '\tvisibleX=' + this.visibleX, - '\tvisibleY=' + this.visibleY, - '\tzoomLevel=' + this.zoomLevel]; - - return '[BrowserViewportState] {\n' + props.join(',\n') + '\n}'; - } - -}; - diff --git a/toolkit/content/tests/fennec-tile-testapp/chrome/content/FooScript.js b/toolkit/content/tests/fennec-tile-testapp/chrome/content/FooScript.js deleted file mode 100644 index 49cbbed66..000000000 --- a/toolkit/content/tests/fennec-tile-testapp/chrome/content/FooScript.js +++ /dev/null @@ -1,352 +0,0 @@ -var noop = function() {}; -Browser = { - updateViewportSize: noop - /** *********************************************************** - function - let browser = document.getElementById("googlenews"); - let cdoc = browser.contentDocument; - - // These might not exist yet depending on page load state - var body = cdoc.body || {}; - var html = cdoc.documentElement || {}; - - var w = Math.max(body.scrollWidth || 0, html.scrollWidth); - var h = Math.max(body.scrollHeight || 0, html.scrollHeight); - - window.tileManager.viewportHandler(new wsRect(0, 0, w, h), - window.innerWidth, - new wsRect(0, 0, window.innerWidth, window.innerHeight), - false); - *************************************************************/ -}; -var ws = { - beginUpdateBatch: noop, - panTo: noop, - endUpdateBatch: noop -}; -var Ci = Components.interfaces; -var bv = null; -var endl = "\n"; - - -function BrowserView() { - this.init(); - bindAll(this); -} - -BrowserView.prototype = { - - // --- PROPERTIES --- - // public: - // init() - // getViewportInnerBoundsRect(dx, dy) - // tileManager - // scrollbox - // - // private: - // _scrollbox - // _leftbar - // _rightbar - // _topbar - // _browser - // _tileManager - // _viewportRect - // _viewportInnerBoundsRect - // - - get tileManager() { return this._tileManager; }, - get scrollbox() { return this._scrollbox; }, - - init: function init() { - let scrollbox = document.getElementById("scrollbox").boxObject; - this._scrollbox = scrollbox; - - let leftbar = document.getElementById("left_sidebar"); - let rightbar = document.getElementById("right_sidebar"); - let topbar = document.getElementById("top_urlbar"); - this._leftbar = leftbar; - this._rightbar = rightbar; - this._topbar = topbar; - - scrollbox.scrollTo(Math.round(leftbar.getBoundingClientRect().right), 0); - - let tileContainer = document.getElementById("tile_container"); - tileContainer.addEventListener("mousedown", onMouseDown, true); - tileContainer.addEventListener("mouseup", onMouseUp, true); - tileContainer.addEventListener("mousemove", onMouseMove, true); - this._tileContainer = tileContainer; - - let tileManager = new TileManager(this.appendTile, this.removeTile, window.innerWidth); - this._tileManager = tileManager; - - let browser = document.getElementById("googlenews"); - this.setCurrentBrowser(browser, false); // sets this._browser - - let cdoc = browser.contentDocument; - - // These might not exist yet depending on page load state - let body = cdoc.body || {}; - let html = cdoc.documentElement || {}; - - let w = Math.max(body.scrollWidth || 0, html.scrollWidth); - let h = Math.max(body.scrollHeight || 0, html.scrollHeight); - - let viewportRect = new wsRect(0, 0, w, h); - this._viewportRect = viewportRect; - - let viewportInnerBoundsRect = this.getViewportInnerBoundsRect(); - this._viewportInnerBoundsRect = viewportInnerBoundsRect; - - tileManager.viewportHandler(viewportRect, - window.innerWidth, - viewportInnerBoundsRect, - true); - }, - - resizeTileContainer: function resizeTileContainer() { - - }, - - scrollboxToViewportRect: function scrollboxToViewportRect(rect, clip) { - let leftbar = this._leftbar.getBoundingClientRect(); - let rightbar = this._rightbar.getBoundingClientRect(); - let topbar = this._topbar.getBoundingClientRect(); - - let xtrans = -leftbar.width; - let ytrans = -topbar.height; - let x = rect.x + xtrans; - let y = rect.y + ytrans; - - // XXX we're cheating --- this is not really a clip, but its the only - // way this function is used - rect.x = (clip) ? Math.max(x, 0) : x; - rect.y = (clip) ? Math.max(y, 0) : y; - - return rect; - }, - - getScrollboxPosition: function getScrollboxPosition() { - return [this._scrollbox.positionX, this._scrollbox.positionY]; - }, - - getViewportInnerBoundsRect: function getViewportInnerBoundsRect(dx, dy) { - if (!dx) dx = 0; - if (!dy) dy = 0; - - let w = window.innerWidth; - let h = window.innerHeight; - - let leftbar = this._leftbar.getBoundingClientRect(); - let rightbar = this._rightbar.getBoundingClientRect(); - let topbar = this._topbar.getBoundingClientRect(); - - let leftinner = Math.max(leftbar.right - dx, 0); - let rightinner = Math.min(rightbar.left - dx, w); - let topinner = Math.max(topbar.bottom - dy, 0); - - let [x, y] = this.getScrollboxPosition(); - - return this.scrollboxToViewportRect(new wsRect(x + dx, y + dy, rightinner - leftinner, h - topinner), - true); - }, - - appendTile: function appendTile(tile) { - let canvas = tile.contentImage; - - canvas.style.position = "absolute"; - canvas.style.left = tile.x + "px"; - canvas.style.top = tile.y + "px"; - - let tileContainer = document.getElementById("tile_container"); - tileContainer.appendChild(canvas); - - dump('++ ' + tile.toString() + endl); - }, - - removeTile: function removeTile(tile) { - let canvas = tile.contentImage; - - let tileContainer = document.getElementById("tile_container"); - tileContainer.removeChild(canvas); - - dump('-- ' + tile.toString() + endl); - }, - - scrollBy: function scrollBy(dx, dy) { - // TODO - this.onBeforeScroll(); - this.onAfterScroll(); - }, - - // x: current x - // y: current y - // dx: delta to get to x from current x - // dy: delta to get to y from current y - onBeforeScroll: function onBeforeScroll(x, y, dx, dy) { - this.tileManager.onBeforeScroll(this.getViewportInnerBoundsRect(dx, dy)); - - // shouldn't update margin if it doesn't need to be changed - let sidebars = document.getElementsByClassName("sidebar"); - for (let i = 0; i < sidebars.length; i++) { - let sidebar = sidebars[i]; - sidebar.style.margin = (y + dy) + "px 0px 0px 0px"; - } - - let urlbar = document.getElementById("top_urlbar"); - urlbar.style.margin = "0px 0px 0px " + (x + dx) + "px"; - }, - - onAfterScroll: function onAfterScroll(x, y, dx, dy) { - this.tileManager.onAfterScroll(this.getViewportInnerBoundsRect()); - }, - - setCurrentBrowser: function setCurrentBrowser(browser, skipZoom) { - let currentBrowser = this._browser; - if (currentBrowser) { - // backup state - currentBrowser.mZoomLevel = this.zoomLevel; - currentBrowser.mPanX = ws._viewingRect.x; - currentBrowser.mPanY = ws._viewingRect.y; - - // stop monitor paint events for this browser - currentBrowser.removeEventListener("MozAfterPaint", this.handleMozAfterPaint, false); - currentBrowser.setAttribute("type", "content"); - currentBrowser.docShell.isOffScreenBrowser = false; - } - - browser.setAttribute("type", "content-primary"); - if (!skipZoom) - browser.docShell.isOffScreenBrowser = true; - - // start monitoring paint events for this browser - browser.addEventListener("MozAfterPaint", this.handleMozAfterPaint, false); - - this._browser = browser; - - // endLoading(and startLoading in most cases) calls zoom anyway - if (!skipZoom) { - this.zoomToPage(); - } - - if ("mZoomLevel" in browser) { - // restore last state - ws.beginUpdateBatch(); - ws.panTo(browser.mPanX, browser.mPanY); - this.zoomLevel = browser.mZoomLevel; - ws.endUpdateBatch(true); - - // drop the cache - delete browser.mZoomLevel; - delete browser.mPanX; - delete browser.mPanY; - } - - this.tileManager.browser = browser; - }, - - handleMozAfterPaint: function handleMozAfterPaint(ev) { - this.tileManager.handleMozAfterPaint(ev); - }, - - zoomToPage: function zoomToPage() { - /** ****************************************************** - let needToPanToTop = this._needToPanToTop; - // Ensure pages are panned at the top before zooming/painting - // combine the initial pan + zoom into a transaction - if (needToPanToTop) { - ws.beginUpdateBatch(); - this._needToPanToTop = false; - ws.panTo(0, -BrowserUI.toolbarH); - } - // Adjust the zoomLevel to fit the page contents in our window width - let [contentW, ] = this._contentAreaDimensions; - let fakeW = this._fakeWidth; - - if (contentW > fakeW) - this.zoomLevel = fakeW / contentW; - - if (needToPanToTop) - ws.endUpdateBatch(); - ********************************************************/ - } - -}; - - -function onResize(e) { - let browser = document.getElementById("googlenews"); - let cdoc = browser.contentDocument; - - // These might not exist yet depending on page load state - var body = cdoc.body || {}; - var html = cdoc.documentElement || {}; - - var w = Math.max(body.scrollWidth || 0, html.scrollWidth); - var h = Math.max(body.scrollHeight || 0, html.scrollHeight); - - if (bv) - bv.tileManager.viewportHandler(new wsRect(0, 0, w, h), - window.innerWidth, - bv.getViewportInnerBoundsRect(), - true); -} - -function onMouseDown(e) { - window._isDragging = true; - window._dragStart = {x: e.clientX, y: e.clientY}; - - bv.tileManager.startPanning(); -} - -function onMouseUp() { - window._isDragging = false; - - bv.tileManager.endPanning(); -} - -function onMouseMove(e) { - if (window._isDragging) { - let scrollbox = bv.scrollbox; - - let x = scrollbox.positionX; - let y = scrollbox.positionY; - let w = scrollbox.scrolledWidth; - let h = scrollbox.scrolledHeight; - - let dx = window._dragStart.x - e.clientX; - let dy = window._dragStart.y - e.clientY; - - // XXX if max(x, 0) > scrollwidth we shouldn't do anything (same for y/height) - let newX = Math.max(x + dx, 0); - let newY = Math.max(y + dy, 0); - - if (newX < w || newY < h) { - // clip dx and dy to prevent us from going below 0 - dx = Math.max(dx, -x); - dy = Math.max(dy, -y); - - bv.onBeforeScroll(x, y, dx, dy); - - /* dump("==========scroll==========" + endl); - dump("delta: " + dx + "," + dy + endl); - let xx = {}; - let yy = {}; - scrollbox.getPosition(xx, yy); - dump(xx.value + "," + yy.value + endl);*/ - - scrollbox.scrollBy(dx, dy); - - /* scrollbox.getPosition(xx, yy); - dump(xx.value + "," + yy.value + endl); - dump("==========================" + endl);*/ - - bv.onAfterScroll(); - } - } - - window._dragStart = {x: e.clientX, y: e.clientY}; -} - -function onLoad() { - bv = new BrowserView(); -} diff --git a/toolkit/content/tests/fennec-tile-testapp/chrome/content/TileManager.js b/toolkit/content/tests/fennec-tile-testapp/chrome/content/TileManager.js deleted file mode 100644 index 52beb6e36..000000000 --- a/toolkit/content/tests/fennec-tile-testapp/chrome/content/TileManager.js +++ /dev/null @@ -1,1018 +0,0 @@ -// -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -const kXHTMLNamespaceURI = "http://www.w3.org/1999/xhtml"; - -// base-2 exponent for width, height of a single tile. -const kTileExponentWidth = 7; -const kTileExponentHeight = 7; -const kTileWidth = Math.pow(2, kTileExponentWidth); // 2^7 = 128 -const kTileHeight = Math.pow(2, kTileExponentHeight); // 2^7 = 128 -const kLazyRoundTimeCap = 500; // millis - - -function bind(f, thisObj) { - return function() { - return f.apply(thisObj, arguments); - }; -} - -function bindSome(instance, methodNames) { - for (let methodName of methodNames) - if (methodName in instance) - instance[methodName] = bind(instance[methodName], instance); -} - -function bindAll(instance) { - for (let key in instance) - if (instance[key] instanceof Function) - instance[key] = bind(instance[key], instance); -} - - -/** - * The Tile Manager! - * - * @param appendTile The function the tile manager should call in order to - * "display" a tile (e.g. append it to the DOM). The argument to this - * function is a TileManager.Tile object. - * @param removeTile The function the tile manager should call in order to - * "undisplay" a tile (e.g. remove it from the DOM). The argument to this - * function is a TileManager.Tile object. - * @param fakeWidth The width of the widest possible visible rectangle, e.g. - * 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 - this._browserView = browserView; - - // 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 - let self = this; - this._tileCache = new TileManager.TileCache(function(tile) { self._removeTileSafe(tile); }, - -1, -1, 110); - - // 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 - this._criticalRect = null; - - // Current <browser> DOM element, holding the content we wish to render. - // This is null when no browser is attached - this._browser = null; - - // if we have an outstanding paint timeout, its value is stored here - // for cancelling when we end page loads - // this._drawTimeout = 0; - this._pageLoadResizerTimeout = 0; - - // 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 - this._crawler = null; - - // the max right coordinate we've seen from paint events - // while we were loading a page. If we see something that's bigger than - // our width, we'll trigger a page zoom. - this._pageLoadMaxRight = 0; - this._pageLoadMaxBottom = 0; - - // Tells us to pan to top before first draw - this._needToPanToTop = false; -} - -TileManager.prototype = { - - setBrowser: function setBrowser(b) { this._browser = b; }, - - // This is the callback fired by our client whenever the viewport - // changed somehow (or didn't change but someone asked it to update). - viewportChangeHandler: function viewportChangeHandler(viewportRect, - criticalRect, - boundsSizeChanged, - dirtyAll) { - // !!! --- DEBUG BEGIN ----- - dump("***vphandler***\n"); - dump(viewportRect.toString() + "\n"); - dump(criticalRect.toString() + "\n"); - dump(boundsSizeChanged + "\n"); - dump(dirtyAll + "\n***************\n"); - // !!! --- DEBUG END ------- - - let tc = this._tileCache; - - tc.iBound = Math.ceil(viewportRect.right / kTileWidth); - tc.jBound = Math.ceil(viewportRect.bottom / kTileHeight); - - if (!criticalRect || !criticalRect.equals(this._criticalRect)) { - this.beginCriticalMove(criticalRect); - this.endCriticalMove(criticalRect, !boundsSizeChanged); - } - - if (boundsSizeChanged) { - // TODO fastpath if !dirtyAll - this.dirtyRects([viewportRect.clone()], true); - } - }, - - dirtyRects: function dirtyRects(rects, doCriticalRender) { - let criticalIsDirty = false; - let criticalRect = this._criticalRect; - - for (let rect of rects) { - this._tileCache.forEachIntersectingRect(rect, false, this._dirtyTile, this); - - if (criticalRect && rect.intersects(criticalRect)) - criticalIsDirty = true; - } - - if (criticalIsDirty && doCriticalRender) - this.criticalRectPaint(); - }, - - criticalRectPaint: function criticalRectPaint() { - let cr = this._criticalRect; - - if (cr) { - let [ctrx, ctry] = cr.centerRounded(); - this.recenterEvictionQueue(ctrx, ctry); - this._renderAppendHoldRect(cr); - } - }, - - beginCriticalMove2: function beginCriticalMove(destCriticalRect) { - let start = Date.now(); - function appendNonDirtyTile(tile) { - if (!tile.isDirty()) - this._appendTileSafe(tile); - } - - if (destCriticalRect) - this._tileCache.forEachIntersectingRect(destCriticalRect, false, appendNonDirtyTile, this); - let end = Date.now(); - dump("start: " + (end-start) + "\n") - }, - - beginCriticalMove: function beginCriticalMove(destCriticalRect) { - /* - function appendNonDirtyTile(tile) { - if (!tile.isDirty()) - this._appendTileSafe(tile); - } - */ - - let start = Date.now(); - - if (destCriticalRect) { - - let rect = destCriticalRect; - - let create = false; - - // this._tileCache.forEachIntersectingRect(destCriticalRect, false, appendNonDirtyTile, this); - let visited = {}; - let evictGuard = null; - if (create) { - evictGuard = function evictGuard(tile) { - return !visited[tile.toString()]; - }; - } - - let starti = rect.left >> kTileExponentWidth; - let endi = rect.right >> kTileExponentWidth; - - let startj = rect.top >> kTileExponentHeight; - let endj = rect.bottom >> kTileExponentHeight; - - let tile = null; - let tc = this._tileCache; - - for (var j = startj; j <= endj; ++j) { - for (var i = starti; i <= endi; ++i) { - - // 'this' for getTile needs to be tc - - // tile = this.getTile(i, j, create, evictGuard); - // if (!tc.inBounds(i, j)) { - if (0 <= i && 0 <= j && i <= tc.iBound && j <= tc.jBound) { - // return null; - break; - } - - tile = null; - - // if (tc._isOccupied(i, j)) { - if (tc._tiles[i] && tc._tiles[i][j]) { - tile = tc._tiles[i][j]; - } else if (create) { - // NOTE: create is false here - tile = tc._createTile(i, j, evictionGuard); - if (tile) tile.markDirty(); - } - - if (tile) { - visited[tile.toString()] = true; - // fn.call(thisObj, tile); - // function appendNonDirtyTile(tile) { - // if (!tile.isDirty()) - if (!tile._dirtyTileCanvas) { - // this._appendTileSafe(tile); - if (!tile._appended) { - let astart = Date.now(); - this._appendTile(tile); - tile._appended = true; - let aend = Date.now(); - dump("append: " + (aend - astart) + "\n"); - } - } - // } - } - } - } - } - - let end = Date.now(); - dump("start: " + (end-start) + "\n") - }, - - endCriticalMove: function endCriticalMove(destCriticalRect, doCriticalPaint) { - let start = Date.now(); - - let tc = this._tileCache; - let cr = this._criticalRect; - - let dcr = destCriticalRect.clone(); - - let f = function releaseOldTile(tile) { - // release old tile - if (!tile.boundRect.intersects(dcr)) - tc.releaseTile(tile); - } - - if (cr) - tc.forEachIntersectingRect(cr, false, f, this); - - this._holdRect(destCriticalRect); - - if (cr) - cr.copyFrom(destCriticalRect); - else - this._criticalRect = cr = destCriticalRect; - - let crpstart = Date.now(); - if (doCriticalPaint) - this.criticalRectPaint(); - dump(" crp: " + (Date.now() - crpstart) + "\n"); - - let end = Date.now(); - dump("end: " + (end - start) + "\n"); - }, - - restartLazyCrawl: function restartLazyCrawl(startRectOrQueue) { - if (!startRectOrQueue || startRectOrQueue instanceof Array) { - this._crawler = new TileManager.CrawlIterator(this._tileCache); - - if (startRectOrQueue) { - let len = startRectOrQueue.length; - for (let k = 0; k < len; ++k) - this._crawler.enqueue(startRectOrQueue[k].i, startRectOrQueue[k].j); - } - } else { - this._crawler = new TileManager.CrawlIterator(this._tileCache, startRectOrQueue); - } - - if (!this._idleTileCrawlerTimeout) - this._idleTileCrawlerTimeout = setTimeout(this._idleTileCrawler, 2000, this); - }, - - stopLazyCrawl: function stopLazyCrawl() { - this._idleTileCrawlerTimeout = 0; - this._crawler = null; - - let cr = this._criticalRect; - if (cr) { - let [ctrx, ctry] = cr.centerRounded(); - this.recenterEvictionQueue(ctrx, ctry); - } - }, - - recenterEvictionQueue: function recenterEvictionQueue(ctrx, ctry) { - let ctri = ctrx >> kTileExponentWidth; - let ctrj = ctry >> kTileExponentHeight; - - function evictFarTiles(a, b) { - let dista = Math.max(Math.abs(a.i - ctri), Math.abs(a.j - ctrj)); - let distb = Math.max(Math.abs(b.i - ctri), Math.abs(b.j - ctrj)); - return dista - distb; - } - - this._tileCache.sortEvictionQueue(evictFarTiles); - }, - - _renderTile: function _renderTile(tile) { - if (tile.isDirty()) - tile.render(this._browser, this._browserView); - }, - - _appendTileSafe: function _appendTileSafe(tile) { - if (!tile._appended) { - this._appendTile(tile); - tile._appended = true; - } - }, - - _removeTileSafe: function _removeTileSafe(tile) { - if (tile._appended) { - this._removeTile(tile); - tile._appended = false; - } - }, - - _dirtyTile: function _dirtyTile(tile) { - if (!this._criticalRect || !tile.boundRect.intersects(this._criticalRect)) - this._removeTileSafe(tile); - - tile.markDirty(); - - if (this._crawler) - this._crawler.enqueue(tile.i, tile.j); - }, - - _holdRect: function _holdRect(rect) { - this._tileCache.holdTilesIntersectingRect(rect); - }, - - _releaseRect: function _releaseRect(rect) { - this._tileCache.releaseTilesIntersectingRect(rect); - }, - - _renderAppendHoldRect: function _renderAppendHoldRect(rect) { - function renderAppendHoldTile(tile) { - if (tile.isDirty()) - this._renderTile(tile); - - this._appendTileSafe(tile); - this._tileCache.holdTile(tile); - } - - this._tileCache.forEachIntersectingRect(rect, true, renderAppendHoldTile, this); - }, - - _idleTileCrawler: function _idleTileCrawler(self) { - if (!self) self = this; - dump('crawl pass.\n'); - let itered = 0, rendered = 0; - - let start = Date.now(); - let comeAgain = true; - - while ((Date.now() - start) <= kLazyRoundTimeCap) { - let tile = self._crawler.next(); - - if (!tile) { - comeAgain = false; - break; - } - - if (tile.isDirty()) { - self._renderTile(tile); - ++rendered; - } - ++itered; - } - - dump('crawl itered:' + itered + ' rendered:' + rendered + '\n'); - - if (comeAgain) { - self._idleTileCrawlerTimeout = setTimeout(self._idleTileCrawler, 2000, self); - } else { - self.stopLazyCrawl(); - dump('crawl end\n'); - } - } - -}; - - -/** - * The tile cache used by the tile manager to hold and index all - * tiles. Also responsible for pooling tiles and maintaining the - * number of tiles under given capacity. - * - * @param onBeforeTileDetach callback set by the TileManager to call before - * we must "detach" a tile from a tileholder due to needing it elsewhere or - * having to discard it on capacity decrease - * @param capacity the initial capacity of the tile cache, i.e. the max number - * of tiles the cache can have allocated - */ -TileManager.TileCache = function TileCache(onBeforeTileDetach, iBound, jBound, capacity) { - if (arguments.length <= 3 || capacity < 0) - capacity = Infinity; - - // We track all pooled tiles in a 2D array (row, column) ordered as - // they "appear on screen". The array is a grid that functions for - // storage of the tiles and as a lookup map. Each array entry is - // a reference to the tile occupying that space ("tileholder"). Entries - // are not unique, so a tile could be referenced by multiple array entries, - // i.e. a tile could "span" many tile placeholders (e.g. if we merge - // neighbouring tiles). - this._tiles = []; - - // holds the same tiles that _tiles holds, but as contiguous array - // elements, for pooling tiles for reuse under finite capacity - this._tilePool = (capacity == Infinity) ? new Array() : new Array(capacity); - - this._capacity = capacity; - this._nTiles = 0; - this._numFree = 0; - - this._onBeforeTileDetach = onBeforeTileDetach; - - this.iBound = iBound; - this.jBound = jBound; -}; - -TileManager.TileCache.prototype = { - - get size() { return this._nTiles; }, - get numFree() { return this._numFree; }, - - // A comparison function that will compare all free tiles as greater - // than all non-free tiles. Useful because, for instance, to shrink - // the tile pool when capacity is lowered, we want to remove all tiles - // at the new cap and beyond, favoring removal of free tiles first. - evictionCmp: function freeTilesLast(a, b) { - if (a.free == b.free) return (a.j == b.j) ? b.i - a.i : b.j - a.j; - return (a.free) ? 1 : -1; - }, - - getCapacity: function getCapacity() { return this._capacity; }, - - setCapacity: function setCapacity(newCap, skipEvictionQueueSort) { - if (newCap < 0) - throw "Cannot set a negative tile cache capacity"; - - if (newCap == Infinity) { - this._capacity = Infinity; - return; - } else if (this._capacity == Infinity) { - // pretend we had a finite capacity all along and proceed normally - this._capacity = this._tilePool.length; - } - - let rem = null; - - if (newCap < this._capacity) { - // This case is obnoxious. We're decreasing our capacity which means - // we may have to get rid of tiles. Depending on our eviction comparator, - // we probably try to get rid free tiles first, but we might have to throw - // out some nonfree ones too. Note that "throwing out" means the cache - // won't keep them, and they'll get GC'ed as soon as all other refholders - // let go of their refs to the tile. - if (!skipEvictionQueueSort) - this.sortEvictionQueue(); - - rem = this._tilePool.splice(newCap, this._tilePool.length); - - } else { - // This case is win. Extend our tile pool array with new empty space. - this._tilePool.push.apply(this._tilePool, new Array(newCap - this._capacity)); - } - - // update state in the case that we threw things out. - let nTilesDeleted = this._nTiles - newCap; - if (nTilesDeleted > 0) { - let nFreeDeleted = 0; - for (let k = 0; k < nTilesDeleted; ++k) { - if (rem[k].free) - nFreeDeleted++; - - this._detachTile(rem[k].i, rem[k].j); - } - - this._nTiles -= nTilesDeleted; - this._numFree -= nFreeDeleted; - } - - this._capacity = newCap; - }, - - _isOccupied: function _isOccupied(i, j) { - return !!(this._tiles[i] && this._tiles[i][j]); - }, - - _detachTile: function _detachTile(i, j) { - let tile = null; - if (this._isOccupied(i, j)) { - tile = this._tiles[i][j]; - - if (this._onBeforeTileDetach) - this._onBeforeTileDetach(tile); - - this.releaseTile(tile); - delete this._tiles[i][j]; - } - return tile; - }, - - _reassignTile: function _reassignTile(tile, i, j) { - this._detachTile(tile.i, tile.j); // detach - tile.init(i, j); // re-init - this._tiles[i][j] = tile; // attach - return tile; - }, - - _evictTile: function _evictTile(evictionGuard) { - let k = this._nTiles - 1; - let pool = this._tilePool; - let victim = null; - - for (; k >= 0; --k) { - if (pool[k].free && - (!evictionGuard || evictionGuard(pool[k]))) - { - victim = pool[k]; - break; - } - } - - return victim; - }, - - _createTile: function _createTile(i, j, evictionGuard) { - if (!this._tiles[i]) - this._tiles[i] = []; - - let tile = null; - - if (this._nTiles < this._capacity) { - // either capacity is infinite, or we still have room to allocate more - tile = new TileManager.Tile(i, j); - this._tiles[i][j] = tile; - this._tilePool[this._nTiles++] = tile; - this._numFree++; - - } else { - // assert: nTiles == capacity - dump("\nevicting\n"); - tile = this._evictTile(evictionGuard); - if (tile) - this._reassignTile(tile, i, j); - } - - return tile; - }, - - inBounds: function inBounds(i, j) { - return 0 <= i && 0 <= j && i <= this.iBound && j <= this.jBound; - }, - - sortEvictionQueue: function sortEvictionQueue(cmp) { - if (!cmp) cmp = this.evictionCmp; - this._tilePool.sort(cmp); - }, - - /** - * Get a tile by its indices - * - * @param i Column - * @param j Row - * @param create Flag true if the tile should be created in case there is no - * tile at (i, j) - * @param reuseCondition Boolean-valued function to restrict conditions under - * which an old tile may be reused for creating this one. This can happen if - * the cache has reached its capacity and must reuse existing tiles in order to - * create this one. The function is given a Tile object as its argument and - * returns true if the tile is OK for reuse. This argument has no effect if the - * create argument is false. - */ - getTile: function getTile(i, j, create, evictionGuard) { - if (!this.inBounds(i, j)) - return null; - - let tile = null; - - if (this._isOccupied(i, j)) { - tile = this._tiles[i][j]; - } else if (create) { - tile = this._createTile(i, j, evictionGuard); - if (tile) tile.markDirty(); - } - - return tile; - }, - - /** - * Look up (possibly creating) a tile from its viewport coordinates. - * - * @param x - * @param y - * @param create Flag true if the tile should be created in case it doesn't - * already exist at the tileholder corresponding to (x, y) - */ - tileFromPoint: function tileFromPoint(x, y, create) { - let i = x >> kTileExponentWidth; - let j = y >> kTileExponentHeight; - - return this.getTile(i, j, create); - }, - - /** - * Hold a tile (i.e. mark it non-free). Returns true if the operation - * actually did something, false elsewise. - */ - holdTile: function holdTile(tile) { - if (tile && tile.free) { - tile._hold(); - this._numFree--; - return true; - } - return false; - }, - - /** - * Release a tile (i.e. mark it free). Returns true if the operation - * actually did something, false elsewise. - */ - releaseTile: function releaseTile(tile) { - if (tile && !tile.free) { - tile._release(); - this._numFree++; - return true; - } - return false; - }, - - // XXX the following two functions will iterate through duplicate tiles - // once we begin to merge tiles. - /** - * Fetch all tiles that share at least one point with this rect. If `create' - * is true then any tileless tileholders will have tiles created for them. - */ - tilesIntersectingRect: function tilesIntersectingRect(rect, create) { - let dx = (rect.right % kTileWidth) - (rect.left % kTileWidth); - let dy = (rect.bottom % kTileHeight) - (rect.top % kTileHeight); - let tiles = []; - - for (let y = rect.top; y <= rect.bottom - dy; y += kTileHeight) { - for (let x = rect.left; x <= rect.right - dx; x += kTileWidth) { - let tile = this.tileFromPoint(x, y, create); - if (tile) - tiles.push(tile); - } - } - - return tiles; - }, - - forEachIntersectingRect: function forEachIntersectingRect(rect, create, fn, thisObj) { - let visited = {}; - let evictGuard = null; - if (create) { - evictGuard = function evictGuard(tile) { - return !visited[tile.toString()]; - }; - } - - let starti = rect.left >> kTileExponentWidth; - let endi = rect.right >> kTileExponentWidth; - - let startj = rect.top >> kTileExponentHeight; - let endj = rect.bottom >> kTileExponentHeight; - - let tile = null; - for (var j = startj; j <= endj; ++j) { - for (var i = starti; i <= endi; ++i) { - tile = this.getTile(i, j, create, evictGuard); - if (tile) { - visited[tile.toString()] = true; - fn.call(thisObj, tile); - } - } - } - }, - - holdTilesIntersectingRect: function holdTilesIntersectingRect(rect) { - this.forEachIntersectingRect(rect, false, this.holdTile, this); - }, - - releaseTilesIntersectingRect: function releaseTilesIntersectingRect(rect) { - this.forEachIntersectingRect(rect, false, this.releaseTile, this); - } - -}; - - - -TileManager.Tile = function Tile(i, j) { - // canvas element is where we keep paint data from browser for this tile - this._canvas = document.createElementNS(kXHTMLNamespaceURI, "canvas"); - this._canvas.setAttribute("width", String(kTileWidth)); - this._canvas.setAttribute("height", String(kTileHeight)); - this._canvas.setAttribute("moz-opaque", "true"); - // this._canvas.style.border = "1px solid red"; - - this.init(i, j); // defines more properties, cf below -}; - -TileManager.Tile.prototype = { - - // essentially, this is part of constructor code, but since we reuse tiles - // in the tile cache, this is here so that we can reinitialize tiles when we - // reuse them - init: function init(i, j) { - if (!this.boundRect) - this.boundRect = new wsRect(i * kTileWidth, j * kTileHeight, kTileWidth, kTileHeight); - else - this.boundRect.setRect(i * kTileWidth, j * kTileHeight, kTileWidth, kTileHeight); - - // indices! - this.i = i; - this.j = j; - - // flags true if we need to repaint our own local canvas - this._dirtyTileCanvas = false; - - // keep a dirty rectangle (i.e. only part of the tile is dirty) - this._dirtyTileCanvasRect = null; - - // flag used by TileManager to avoid re-appending tiles that have already - // been appended - this._appended = false; - - // We keep tile objects around after their use for later reuse, so this - // flags true for an unused pooled tile. We don't actually care about - // this from within the Tile prototype, it is here for the cache to use. - this.free = true; - }, - - // viewport coordinates - get x() { return this.boundRect.left; }, - get y() { return this.boundRect.top; }, - - // the actual canvas that holds the most recently rendered image of this - // canvas - getContentImage: function getContentImage() { return this._canvas; }, - - isDirty: function isDirty() { return this._dirtyTileCanvas; }, - - /** - * Mark this entire tile as dirty (i.e. the whole tile needs to be rendered - * on next render). - */ - markDirty: function markDirty() { this.updateDirtyRegion(); }, - - unmarkDirty: function unmarkDirty() { - this._dirtyTileCanvasRect = null; - this._dirtyTileCanvas = false; - }, - - /** - * This will mark dirty at least everything in dirtyRect (which must be - * specified in canvas coordinates). If dirtyRect is not given then - * the entire tile is marked dirty. - */ - updateDirtyRegion: function updateDirtyRegion(dirtyRect) { - if (!dirtyRect) { - - if (!this._dirtyTileCanvasRect) - this._dirtyTileCanvasRect = this.boundRect.clone(); - else - this._dirtyTileCanvasRect.copyFrom(this.boundRect); - - } else if (!this._dirtyTileCanvasRect) { - this._dirtyTileCanvasRect = dirtyRect.intersect(this.boundRect); - } else if (dirtyRect.intersects(this.boundRect)) { - this._dirtyTileCanvasRect.expandToContain(dirtyRect.intersect(this.boundRect)); - } - - // TODO if after the above, the dirty rectangle is large enough, - // mark the whole tile dirty. - - if (this._dirtyTileCanvasRect) - this._dirtyTileCanvas = true; - }, - - /** - * Actually draw the browser content into the dirty region of this - * tile. This requires us to actually draw with the - * nsIDOMCanvasRenderingContext2D object's drawWindow method, which - * we expect to be a relatively heavy operation. - * - * You likely want to check if the tile isDirty() before asking it - * to render, as this will cause the entire tile to re-render in the - * case that it is not dirty. - */ - render: function render(browser, browserView) { - if (!this.isDirty()) - this.markDirty(); - - let rect = this._dirtyTileCanvasRect; - - let x = rect.left - this.boundRect.left; - let y = rect.top - this.boundRect.top; - - // content process is not being scaled, so don't scale our rect either - // browserView.viewportToBrowserRect(rect); - // rect.round(); // snap outward to get whole "pixel" (in browser coords) - - let ctx = this._canvas.getContext("2d"); - ctx.save(); - - browserView.browserToViewportCanvasContext(ctx); - - ctx.translate(x, y); - - let cw = browserView._contentWindow; - // let cw = browser.contentWindow; - ctx.asyncDrawXULElement(browserView._browser, - rect.left, rect.top, - rect.right - rect.left, rect.bottom - rect.top, - "grey", - (ctx.DRAWWINDOW_DO_NOT_FLUSH | ctx.DRAWWINDOW_DRAW_CARET)); - - ctx.restore(); - - this.unmarkDirty(); - }, - - toString: function toString(more) { - if (more) { - return 'Tile(' + [this.i, - this.j, - "dirty=" + this.isDirty(), - "boundRect=" + this.boundRect].join(', ') - + ')'; - } - - return 'Tile(' + this.i + ', ' + this.j + ')'; - }, - - _hold: function hold() { this.free = false; }, - _release: function release() { this.free = true; } - -}; - - -/** - * A CrawlIterator is in charge of creating and returning subsequent tiles "crawled" - * over as we render tiles lazily. It supports iterator semantics so you can use - * CrawlIterator objects in for..in loops. - * - * Currently the CrawlIterator is built to expand a rectangle iteratively and return - * subsequent tiles that intersect the boundary of the rectangle. Each expansion of - * the rectangle is one unit of tile dimensions in each direction. This is repeated - * until all tiles from elsewhere have been reused (assuming the cache has finite - * capacity) in this crawl, so that we don't start reusing tiles from the beginning - * of our crawl. Afterward, the CrawlIterator enters a state where it operates as a - * FIFO queue, and calls to next() simply dequeue elements, which must be added with - * enqueue(). - * - * @param tileCache The TileCache over whose tiles this CrawlIterator will crawl - * @param startRect [optional] The rectangle that we grow in the first (rectangle - * expansion) iteration state. - */ -TileManager.CrawlIterator = function CrawlIterator(tileCache, startRect) { - this._tileCache = tileCache; - this._stepRect = startRect; - - // used to remember tiles that we've reused during this crawl - this._visited = {}; - - // filters the tiles we've already reused once from being considered victims - // for reuse when we ask the tile cache to create a new tile - let visited = this._visited; - this._notVisited = function(tile) { return !visited[tile]; }; - - // a generator that generates tile indices corresponding to tiles intersecting - // the boundary of an expanding rectangle - this._crawlIndices = !startRect ? null : (function indicesGenerator(rect, tc) { - let outOfBounds = false; - while (!outOfBounds) { - // expand rect - rect.left -= kTileWidth; - rect.right += kTileWidth; - rect.top -= kTileHeight; - rect.bottom += kTileHeight; - - let dx = (rect.right % kTileWidth) - (rect.left % kTileWidth); - let dy = (rect.bottom % kTileHeight) - (rect.top % kTileHeight); - - outOfBounds = true; - - // top, bottom borders - for (let y of [rect.top, rect.bottom]) { - for (let x = rect.left; x <= rect.right - dx; x += kTileWidth) { - let i = x >> kTileExponentWidth; - let j = y >> kTileExponentHeight; - if (tc.inBounds(i, j)) { - outOfBounds = false; - yield [i, j]; - } - } - } - - // left, right borders - for (let x of [rect.left, rect.right]) { - for (let y = rect.top; y <= rect.bottom - dy; y += kTileHeight) { - let i = x >> kTileExponentWidth; - let j = y >> kTileExponentHeight; - if (tc.inBounds(i, j)) { - outOfBounds = false; - yield [i, j]; - } - } - } - } - })(this._stepRect, this._tileCache), // instantiate the generator - - // after we finish the rectangle iteration state, we enter the FIFO queue state - this._queueState = !startRect; - this._queue = []; - - // used to prevent tiles from being enqueued twice --- "patience, we'll get to - // it in a moment" - this._enqueued = {}; -}; - -TileManager.CrawlIterator.prototype = { - __iterator__: function*() { - while (true) { - let tile = this.next(); - if (!tile) break; - yield tile; - } - }, - - becomeQueue: function becomeQueue() { - this._queueState = true; - }, - - unbecomeQueue: function unbecomeQueue() { - this._queueState = false; - }, - - next: function next() { - if (this._queueState) - return this.dequeue(); - - let tile = null; - - if (this._crawlIndices) { - try { - let [i, j] = this._crawlIndices.next(); - tile = this._tileCache.getTile(i, j, true, this._notVisited); - } catch (e) { - if (!(e instanceof StopIteration)) - throw e; - } - } - - if (tile) { - this._visited[tile] = true; - } else { - this.becomeQueue(); - return this.next(); - } - - return tile; - }, - - dequeue: function dequeue() { - let tile = null; - do { - let idx = this._queue.shift(); - if (!idx) - return null; - - delete this._enqueued[idx]; - let [i, j] = this._unstrIndices(idx); - tile = this._tileCache.getTile(i, j, false); - - } while (!tile); - - return tile; - }, - - enqueue: function enqueue(i, j) { - let idx = this._strIndices(i, j); - if (!this._enqueued[idx]) { - this._queue.push(idx); - this._enqueued[idx] = true; - } - }, - - _strIndices: function _strIndices(i, j) { - return i + "," + j; - }, - - _unstrIndices: function _unstrIndices(str) { - return str.split(','); - } - -}; diff --git a/toolkit/content/tests/fennec-tile-testapp/chrome/content/WidgetStack.js b/toolkit/content/tests/fennec-tile-testapp/chrome/content/WidgetStack.js deleted file mode 100644 index 69288e725..000000000 --- a/toolkit/content/tests/fennec-tile-testapp/chrome/content/WidgetStack.js +++ /dev/null @@ -1,1438 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -var gWsDoLog = false; -var gWsLogDiv = null; - -function logbase() { - if (!gWsDoLog) - return; - - if (gWsLogDiv == null && "console" in window) { - console.log.apply(console, arguments); - } else { - var s = ""; - for (var i = 0; i < arguments.length; i++) { - s += arguments[i] + " "; - } - s += "\n"; - if (gWsLogDiv) { - gWsLogDiv.appendChild(document.createElementNS("http://www.w3.org/1999/xhtml", "br")); - gWsLogDiv.appendChild(document.createTextNode(s)); - } - - dump(s); - } -} - -function dumpJSStack(stopAtNamedFunction) { - let caller = Components.stack.caller; - dump("\tStack: " + caller.name); - while ((caller = caller.caller)) { - dump(" <- " + caller.name); - if (stopAtNamedFunction && caller.name != "anonymous") - break; - } - dump("\n"); -} - -function log() { - // logbase.apply(window, arguments); -} - -function log2() { - // logbase.apply(window, arguments); -} - -var reportError = log; - -/* - * wsBorder class - * - * Simple container for top,left,bottom,right "border" values - */ -function wsBorder(t, l, b, r) { - this.setBorder(t, l, b, r); -} - -wsBorder.prototype = { - - setBorder: function (t, l, b, r) { - this.top = t; - this.left = l; - this.bottom = b; - this.right = r; - }, - - toString: function () { - return "[l:" + this.left + ",t:" + this.top + ",r:" + this.right + ",b:" + this.bottom + "]"; - } -}; - -/* - * wsRect class - * - * Rectangle class, with both x/y/w/h and t/l/b/r accessors. - */ -function wsRect(x, y, w, h) { - this.left = x; - this.top = y; - this.right = x+w; - this.bottom = y+h; -} - -wsRect.prototype = { - - get x() { return this.left; }, - get y() { return this.top; }, - get width() { return this.right - this.left; }, - get height() { return this.bottom - this.top; }, - set x(v) { - let diff = this.left - v; - this.left = v; - this.right -= diff; - }, - set y(v) { - let diff = this.top - v; - this.top = v; - this.bottom -= diff; - }, - set width(v) { this.right = this.left + v; }, - set height(v) { this.bottom = this.top + v; }, - - setRect: function(x, y, w, h) { - this.left = x; - this.top = y; - this.right = x+w; - this.bottom = y+h; - - return this; - }, - - setBounds: function(t, l, b, r) { - this.top = t; - this.left = l; - this.bottom = b; - this.right = r; - - return this; - }, - - equals: function equals(r) { - return (r != null && - this.top == r.top && - this.left == r.left && - this.bottom == r.bottom && - this.right == r.right); - }, - - clone: function clone() { - return new wsRect(this.left, this.top, this.right - this.left, this.bottom - this.top); - }, - - center: function center() { - return [this.left + (this.right - this.left) / 2, - this.top + (this.bottom - this.top) / 2]; - }, - - centerRounded: function centerRounded() { - return this.center().map(Math.round); - }, - - copyFrom: function(r) { - this.top = r.top; - this.left = r.left; - this.bottom = r.bottom; - this.right = r.right; - - return this; - }, - - copyFromTLBR: function(r) { - this.left = r.left; - this.top = r.top; - this.right = r.right; - this.bottom = r.bottom; - - return this; - }, - - translate: function(x, y) { - this.left += x; - this.right += x; - this.top += y; - this.bottom += y; - - return this; - }, - - // return a new wsRect that is the union of that one and this one - union: function(rect) { - let l = Math.min(this.left, rect.left); - let r = Math.max(this.right, rect.right); - let t = Math.min(this.top, rect.top); - let b = Math.max(this.bottom, rect.bottom); - - return new wsRect(l, t, r-l, b-t); - }, - - toString: function() { - return "[" + this.x + "," + this.y + "," + this.width + "," + this.height + "]"; - }, - - expandBy: function(b) { - this.left += b.left; - this.right += b.right; - this.top += b.top; - this.bottom += b.bottom; - return this; - }, - - contains: function(other) { - return !!(other.left >= this.left && - other.right <= this.right && - other.top >= this.top && - other.bottom <= this.bottom); - }, - - intersect: function(r2) { - let xmost1 = this.right; - let xmost2 = r2.right; - - let x = Math.max(this.left, r2.left); - - let temp = Math.min(xmost1, xmost2); - if (temp <= x) - return null; - - let width = temp - x; - - let ymost1 = this.bottom; - let ymost2 = r2.bottom; - let y = Math.max(this.top, r2.top); - - temp = Math.min(ymost1, ymost2); - if (temp <= y) - return null; - - let height = temp - y; - - return new wsRect(x, y, width, height); - }, - - intersects: function(other) { - let xok = (other.left > this.left && other.left < this.right) || - (other.right > this.left && other.right < this.right) || - (other.left <= this.left && other.right >= this.right); - let yok = (other.top > this.top && other.top < this.bottom) || - (other.bottom > this.top && other.bottom < this.bottom) || - (other.top <= this.top && other.bottom >= this.bottom); - return xok && yok; - }, - - /** - * Similar to (and most code stolen from) intersect(). A restriction - * is an intersection, but this modifies the receiving object instead - * of returning a new rect. - */ - restrictTo: function restrictTo(r2) { - let xmost1 = this.right; - let xmost2 = r2.right; - - let x = Math.max(this.left, r2.left); - - let temp = Math.min(xmost1, xmost2); - if (temp <= x) - throw "Intersection is empty but rects cannot be empty"; - - let width = temp - x; - - let ymost1 = this.bottom; - let ymost2 = r2.bottom; - let y = Math.max(this.top, r2.top); - - temp = Math.min(ymost1, ymost2); - if (temp <= y) - throw "Intersection is empty but rects cannot be empty"; - - let height = temp - y; - - return this.setRect(x, y, width, height); - }, - - /** - * Similar to (and most code stolen from) union(). An extension is a - * union (in our sense of the term, not the common set-theoretic sense), - * but this modifies the receiving object instead of returning a new rect. - * Effectively, this rectangle is expanded minimally to contain all of the - * other rect. "Expanded minimally" means that the rect may shrink if - * given a strict subset rect as the argument. - */ - expandToContain: function extendTo(rect) { - let l = Math.min(this.left, rect.left); - let r = Math.max(this.right, rect.right); - let t = Math.min(this.top, rect.top); - let b = Math.max(this.bottom, rect.bottom); - - return this.setRect(l, t, r-l, b-t); - }, - - round: function round(scale) { - if (!scale) scale = 1; - - this.left = Math.floor(this.left * scale) / scale; - this.top = Math.floor(this.top * scale) / scale; - this.right = Math.ceil(this.right * scale) / scale; - this.bottom = Math.ceil(this.bottom * scale) / scale; - - return this; - }, - - scale: function scale(xscl, yscl) { - this.left *= xscl; - this.right *= xscl; - this.top *= yscl; - this.bottom *= yscl; - - return this; - } -}; - -/* - * The "Widget Stack" - * - * Manages a <xul:stack>'s children, allowing them to be dragged around - * the stack, subject to specified constraints. Optionally supports - * one widget designated as the viewport, which can be panned over a virtual - * area without needing to draw that area entirely. The viewport widget - * is designated by a 'viewport' attribute on the child element. - * - * Widgets are subject to various constraints, specified in xul via the - * 'constraint' attribute. Current constraints are: - * ignore-x: When panning, ignore any changes to the widget's x position - * ignore-y: When panning, ignore any changes to the widget's y position - * vp-relative: This widget's position should be claculated relative to - * the viewport widget. It will always keep the same offset from that - * widget as initially laid out, regardless of changes to the viewport - * bounds. - * frozen: This widget is in a fixed position and should never pan. - */ -function WidgetStack(el, ew, eh) { - this.init(el, ew, eh); -} - -WidgetStack.prototype = { - // the <stack> element - _el: null, - - // object indexed by widget id, with state struct for each object (see _addNewWidget) - _widgetState: null, - - // any barriers - _barriers: null, - - // If a viewport widget is present, this will point to its state object; - // otherwise null. - _viewport: null, - - // a wsRect; the inner bounds of the viewport content - _viewportBounds: null, - // a wsBorder; the overflow area to the side of the bounds where our - // viewport-relative widgets go - _viewportOverflow: null, - - // a wsRect; the viewportBounds expanded by the viewportOverflow - _pannableBounds: null, - get pannableBounds() { - if (!this._pannableBounds) { - this._pannableBounds = this._viewportBounds.clone() - .expandBy(this._viewportOverflow); - } - return this._pannableBounds.clone(); - }, - - // a wsRect; the currently visible part of pannableBounds. - _viewingRect: null, - - // the amount of current global offset applied to all widgets (whether - // static or not). Set via offsetAll(). Can be used to push things - // out of the way for overlaying some other UI. - globalOffsetX: 0, - globalOffsetY: 0, - - // if true (default), panning is constrained to the pannable bounds. - _constrainToViewport: true, - - _viewportUpdateInterval: -1, - _viewportUpdateTimeout: -1, - - _viewportUpdateHandler: null, - _panHandler: null, - - _dragState: null, - - _skipViewportUpdates: 0, - _forceViewportUpdate: false, - - // - // init: - // el: the <stack> element whose children are to be managed - // - init: function (el, ew, eh) { - this._el = el; - this._widgetState = {}; - this._barriers = []; - - let rect = this._el.getBoundingClientRect(); - let width = rect.width; - let height = rect.height; - - if (ew != undefined && eh != undefined) { - width = ew; - height = eh; - } - - this._viewportOverflow = new wsBorder(0, 0, 0, 0); - - this._viewingRect = new wsRect(0, 0, width, height); - - // listen for DOMNodeInserted/DOMNodeRemoved/DOMAttrModified - let children = this._el.childNodes; - for (let i = 0; i < children.length; i++) { - let c = this._el.childNodes[i]; - if (c.tagName == "spacer") - this._addNewBarrierFromSpacer(c); - else - this._addNewWidget(c); - } - - // this also updates the viewportOverflow and pannableBounds - this._updateWidgets(); - - if (this._viewport) { - this._viewportBounds = new wsRect(0, 0, this._viewport.rect.width, this._viewport.rect.height); - } else { - this._viewportBounds = new wsRect(0, 0, 0, 0); - } - }, - - // moveWidgetBy: move the widget with the given id by x,y. Should - // not be used on vp-relative or otherwise frozen widgets (using it - // on the x coordinate for x-ignore widgets and similarily for y is - // ok, as long as the other coordinate remains 0.) - moveWidgetBy: function (wid, x, y) { - let state = this._getState(wid); - - state.rect.x += x; - state.rect.y += y; - - this._commitState(state); - }, - - // panBy: pan the entire set of widgets by the given x and y amounts. - // This does the same thing as if the user dragged by the given amount. - // If this is called with an outstanding drag, weirdness might happen, - // but it also might work, so not disabling that. - // - // if ignoreBarriers is true, then barriers are ignored for the pan. - panBy: function panBy(dx, dy, ignoreBarriers) { - dx = Math.round(dx); - dy = Math.round(dy); - - if (dx == 0 && dy == 0) - return false; - - let needsDragWrap = !this._dragging; - - if (needsDragWrap) - this.dragStart(0, 0); - - let panned = this._panBy(dx, dy, ignoreBarriers); - - if (needsDragWrap) - this.dragStop(); - - return panned; - }, - - // panTo: pan the entire set of widgets so that the given x,y - // coordinates are in the upper left of the stack. If either is - // null or undefined, only move the other axis - panTo: function panTo(x, y) { - if (x == undefined || x == null) - x = this._viewingRect.x; - if (y == undefined || y == null) - y = this._viewingRect.y; - this.panBy(x - this._viewingRect.x, y - this._viewingRect.y, true); - }, - - // freeze: set a widget as frozen. A frozen widget won't be moved - // in the stack -- its x,y position will still be tracked in the - // state, but the left/top attributes won't be overwritten. Call unfreeze - // to move the widget back to where the ws thinks it should be. - freeze: function (wid) { - let state = this._getState(wid); - - state.frozen = true; - }, - - unfreeze: function (wid) { - let state = this._getState(wid); - if (!state.frozen) - return; - - state.frozen = false; - this._commitState(state); - }, - - // moveFrozenTo: move a frozen widget with id wid to x, y in the stack. - // can only be used on frozen widgets - moveFrozenTo: function (wid, x, y) { - let state = this._getState(wid); - if (!state.frozen) - throw "moveFrozenTo on non-frozen widget " + wid; - - state.widget.setAttribute("left", x); - state.widget.setAttribute("top", y); - }, - - // moveUnfrozenTo: move an unfrozen, pannable widget with id wid to x, y in - // the stack. should only be used on unfrozen widgets when a dynamic change - // in position needs to be made. we basically remove, adjust and re-add - // the widget - moveUnfrozenTo: function (wid, x, y) { - delete this._widgetState[wid]; - let widget = document.getElementById(wid); - if (x) widget.setAttribute("left", x); - if (y) widget.setAttribute("top", y); - this._addNewWidget(widget); - this._updateWidgets(); - }, - - // we're relying on viewportBounds and viewingRect having the same origin - get viewportVisibleRect () { - let rect = this._viewportBounds.intersect(this._viewingRect); - if (!rect) - rect = new wsRect(0, 0, 0, 0); - return rect; - }, - - isWidgetFrozen: function isWidgetFrozen(wid) { - return this._getState(wid).frozen; - }, - - // isWidgetVisible: return true if any portion of widget with id wid is - // visible; otherwise return false. - isWidgetVisible: function (wid) { - let state = this._getState(wid); - let visibleStackRect = new wsRect(0, 0, this._viewingRect.width, this._viewingRect.height); - - return visibleStackRect.intersects(state.rect); - }, - - // getWidgetVisibility: returns the percentage that the widget is visible - getWidgetVisibility: function (wid) { - let state = this._getState(wid); - let visibleStackRect = new wsRect(0, 0, this._viewingRect.width, this._viewingRect.height); - - let visibleRect = visibleStackRect.intersect(state.rect); - if (visibleRect) - return [visibleRect.width / state.rect.width, visibleRect.height / state.rect.height] - - return [0, 0]; - }, - - // offsetAll: add an offset to all widgets - offsetAll: function (x, y) { - this.globalOffsetX += x; - this.globalOffsetY += y; - - for (let wid in this._widgetState) { - let state = this._widgetState[wid]; - state.rect.x += x; - state.rect.y += y; - - this._commitState(state); - } - }, - - // setViewportBounds - // nb: an object containing top, left, bottom, right properties - // OR - // width, height: integer values; origin assumed to be 0,0 - // OR - // top, left, bottom, right: integer values - // - // Set the bounds of the viewport area; that is, set the size of the - // actual content that the viewport widget will be providing a view - // over. For example, in the case of a 100x100 viewport showing a - // view into a 100x500 webpage, the viewport bounds would be - // { top: 0, left: 0, bottom: 500, right: 100 }. - // - // setViewportBounds will move all the viewport-relative widgets into - // place based on the new viewport bounds. - setViewportBounds: function setViewportBounds() { - let oldBounds = this._viewportBounds.clone(); - - if (arguments.length == 1) { - this._viewportBounds.copyFromTLBR(arguments[0]); - } else if (arguments.length == 2) { - this._viewportBounds.setRect(0, 0, arguments[0], arguments[1]); - } else if (arguments.length == 4) { - this._viewportBounds.setBounds(arguments[0], - arguments[1], - arguments[2], - arguments[3]); - } else { - throw "Invalid number of arguments to setViewportBounds"; - } - - let vp = this._viewport; - - let dleft = this._viewportBounds.left - oldBounds.left; - let dright = this._viewportBounds.right - oldBounds.right; - let dtop = this._viewportBounds.top - oldBounds.top; - let dbottom = this._viewportBounds.bottom - oldBounds.bottom; - - // log2("setViewportBounds dltrb", dleft, dtop, dright, dbottom); - - // move all vp-relative widgets to be the right offset from the bounds again - for (let wid in this._widgetState) { - let state = this._widgetState[wid]; - if (state.vpRelative) { - // log2("vpRelative widget", state.id, state.rect.x, dleft, dright); - if (state.vpOffsetXBefore) { - state.rect.x += dleft; - } else { - state.rect.x += dright; - } - - if (state.vpOffsetYBefore) { - state.rect.y += dtop; - } else { - state.rect.y += dbottom; - } - - // log2("vpRelative widget", state.id, state.rect.x, dleft, dright); - this._commitState(state); - } - } - - for (let bid in this._barriers) { - let barrier = this._barriers[bid]; - - // log2("setViewportBounds: looking at barrier", bid, barrier.vpRelative, barrier.type); - - if (barrier.vpRelative) { - if (barrier.type == "vertical") { - let q = "v barrier moving from " + barrier.x + " to "; - if (barrier.vpOffsetXBefore) { - barrier.x += dleft; - } else { - barrier.x += dright; - } - // log2(q += barrier.x); - } else if (barrier.type == "horizontal") { - let q = "h barrier moving from " + barrier.y + " to "; - if (barrier.vpOffsetYBefore) { - barrier.y += dtop; - } else { - barrier.y += dbottom; - } - // log2(q += barrier.y); - } - } - } - - // clear the pannable bounds cache to make sure it gets rebuilt - this._pannableBounds = null; - - // now let's make sure that the viewing rect and inner bounds are still valid - this._adjustViewingRect(); - - this._viewportUpdate(0, 0, true); - }, - - // setViewportHandler - // uh: A function object - // - // The given function object is called at the end of every drag and viewport - // bounds change, passing in the new rect that's to be displayed in the - // viewport. - // - setViewportHandler: function (uh) { - this._viewportUpdateHandler = uh; - }, - - // setPanHandler - // uh: A function object - // - // The given functin object is called whenever elements pan; it provides - // the new area of the pannable bounds that's visible in the stack. - setPanHandler: function (uh) { - this._panHandler = uh; - }, - - // dragStart: start a drag, with the current coordinates being clientX,clientY - dragStart: function dragStart(clientX, clientY) { - log("(dragStart)", clientX, clientY); - - if (this._dragState) { - reportError("dragStart with drag already in progress? what?"); - this._dragState = null; - } - - this._dragState = { }; - - let t = Date.now(); - - this._dragState.barrierState = []; - - this._dragState.startTime = t; - // outer x, that is outer from the viewport coordinates. In stack-relative coords. - this._dragState.outerStartX = clientX; - this._dragState.outerStartY = clientY; - - this._dragCoordsFromClient(clientX, clientY, t); - - this._dragState.outerLastUpdateDX = 0; - this._dragState.outerLastUpdateDY = 0; - - if (this._viewport) { - // create a copy of these so that we can compute - // deltas correctly to update the viewport - this._viewport.dragStartRect = this._viewport.rect.clone(); - } - - this._dragState.dragging = true; - }, - - _viewportDragUpdate: function viewportDragUpdate() { - let vws = this._viewport; - this._viewportUpdate((vws.dragStartRect.x - vws.rect.x), - (vws.dragStartRect.y - vws.rect.y)); - }, - - // dragStop: stop any drag in progress - dragStop: function dragStop() { - log("(dragStop)"); - - if (!this._dragging) - return; - - if (this._viewportUpdateTimeout != -1) - clearTimeout(this._viewportUpdateTimeout); - - this._viewportDragUpdate(); - - this._dragState = null; - }, - - // dragMove: process a mouse move to clientX,clientY for an ongoing drag - dragMove: function dragMove(clientX, clientY) { - if (!this._dragging) - return false; - - this._dragCoordsFromClient(clientX, clientY); - - let panned = this._dragUpdate(); - - if (this._viewportUpdateInterval != -1) { - if (this._viewportUpdateTimeout != -1) - clearTimeout(this._viewportUpdateTimeout); - let self = this; - this._viewportUpdateTimeout = setTimeout(function () { self._viewportDragUpdate(); }, this._viewportUpdateInterval); - } - - return panned; - }, - - // dragBy: process a mouse move by dx,dy for an ongoing drag - dragBy: function dragBy(dx, dy) { - return this.dragMove(this._dragState.outerCurX + dx, this._dragState.outerCurY + dy); - }, - - // updateSize: tell the WidgetStack to update its size, because it - // was either resized or some other event took place. - updateSize: function updateSize(width, height) { - if (width == undefined || height == undefined) { - let rect = this._el.getBoundingClientRect(); - width = rect.width; - height = rect.height; - } - - // update widget rects and viewportOverflow, since the resize might have - // caused them to change (widgets first, since the viewportOverflow depends - // on them). - - // XXX these methods aren't working correctly yet, but they aren't strictly - // necessary in Fennec's default config - // for (let wid in this._widgetState) { - // let s = this._widgetState[wid]; - // this._updateWidgetRect(s); - // } - // this._updateViewportOverflow(); - - this._viewingRect.width = width; - this._viewingRect.height = height; - - // Wrap this call in a batch to ensure that we always call the - // viewportUpdateHandler, even if _adjustViewingRect doesn't trigger a pan. - // If it does, the batch also ensures that we don't call the handler twice. - this.beginUpdateBatch(); - this._adjustViewingRect(); - this.endUpdateBatch(); - }, - - beginUpdateBatch: function startUpdate() { - if (!this._skipViewportUpdates) { - this._startViewportBoundsString = this._viewportBounds.toString(); - this._forceViewportUpdate = false; - } - this._skipViewportUpdates++; - }, - - endUpdateBatch: function endUpdate(aForceRedraw) { - if (!this._skipViewportUpdates) - throw new Error("Unbalanced call to endUpdateBatch"); - - this._forceViewportUpdate = this._forceViewportUpdate || aForceRedraw; - - this._skipViewportUpdates--; - if (this._skipViewportUpdates) - return; - - let boundsSizeChanged = - this._startViewportBoundsString != this._viewportBounds.toString(); - this._callViewportUpdateHandler(boundsSizeChanged || this._forceViewportUpdate); - }, - - // - // Internal code - // - - _updateWidgetRect: function(state) { - // don't need to support updating the viewport rect at the moment - // (we'd need to duplicate the vptarget* code from _addNewWidget if we did) - if (state == this._viewport) - return; - - let w = state.widget; - let x = w.getAttribute("left") || 0; - let y = w.getAttribute("top") || 0; - let rect = w.getBoundingClientRect(); - state.rect = new wsRect(parseInt(x), parseInt(y), - rect.right - rect.left, - rect.bottom - rect.top); - if (w.hasAttribute("widgetwidth") && w.hasAttribute("widgetheight")) { - state.rect.width = parseInt(w.getAttribute("widgetwidth")); - state.rect.height = parseInt(w.getAttribute("widgetheight")); - } - }, - - _dumpRects: function () { - dump("WidgetStack:\n"); - dump("\tthis._viewportBounds: " + this._viewportBounds + "\n"); - dump("\tthis._viewingRect: " + this._viewingRect + "\n"); - dump("\tthis._viewport.viewportInnerBounds: " + this._viewport.viewportInnerBounds + "\n"); - dump("\tthis._viewport.rect: " + this._viewport.rect + "\n"); - dump("\tthis._viewportOverflow: " + this._viewportOverflow + "\n"); - dump("\tthis.pannableBounds: " + this.pannableBounds + "\n"); - }, - - // Ensures that _viewingRect is within _pannableBounds (call this when either - // one is resized) - _adjustViewingRect: function _adjustViewingRect() { - let vr = this._viewingRect; - let pb = this.pannableBounds; - - if (pb.contains(vr)) - return; // nothing to do here - - // don't bother adjusting _viewingRect if it can't fit into - // _pannableBounds - if (vr.height > pb.height || vr.width > pb.width) - return; - - let panX = 0, panY = 0; - if (vr.right > pb.right) - panX = pb.right - vr.right; - else if (vr.left < pb.left) - panX = pb.left - vr.left; - - if (vr.bottom > pb.bottom) - panY = pb.bottom - vr.bottom; - else if (vr.top < pb.top) - panY = pb.top - vr.top; - - this.panBy(panX, panY, true); - }, - - _getState: function (wid) { - let w = this._widgetState[wid]; - if (!w) - throw "Unknown widget id '" + wid + "'; widget not in stack"; - return w; - }, - - get _dragging() { - return this._dragState && this._dragState.dragging; - }, - - _viewportUpdate: function _viewportUpdate(dX, dY, boundsChanged) { - if (!this._viewport) - return; - - this._viewportUpdateTimeout = -1; - - let vws = this._viewport; - let vwib = vws.viewportInnerBounds; - let vpb = this._viewportBounds; - - // recover the amount the inner bounds moved by the amount the viewport - // widget moved, but don't include offsets that we're making up from previous - // drags that didn't affect viewportInnerBounds - let [ignoreX, ignoreY] = this._offsets || [0, 0]; - let rx = dX - ignoreX; - let ry = dY - ignoreY; - - [dX, dY] = this._rectTranslateConstrain(rx, ry, vwib, vpb); - - // record the offsets that correspond to the amount of the drag we're ignoring - // to ensure the viewportInnerBounds remains within the viewportBounds - this._offsets = [dX - rx, dY - ry]; - - // adjust the viewportInnerBounds, and snap the viewport back - vwib.translate(dX, dY); - vws.rect.translate(dX, dY); - this._commitState(vws); - - // update this so that we can call this function again during the same drag - // and get the right values. - vws.dragStartRect = vws.rect.clone(); - - this._callViewportUpdateHandler(boundsChanged); - }, - - _callViewportUpdateHandler: function _callViewportUpdateHandler(boundsChanged) { - if (!this._viewport || !this._viewportUpdateHandler || this._skipViewportUpdates) - return; - - let vwb = this._viewportBounds.clone(); - - let vwib = this._viewport.viewportInnerBounds.clone(); - - let vis = this.viewportVisibleRect; - - vwib.left += this._viewport.offsetLeft; - vwib.top += this._viewport.offsetTop; - vwib.right += this._viewport.offsetRight; - vwib.bottom += this._viewport.offsetBottom; - - this._viewportUpdateHandler.apply(window, [vwb, vwib, vis, boundsChanged]); - }, - - _dragCoordsFromClient: function (cx, cy, t) { - this._dragState.curTime = t ? t : Date.now(); - this._dragState.outerCurX = cx; - this._dragState.outerCurY = cy; - - let dx = this._dragState.outerCurX - this._dragState.outerStartX; - let dy = this._dragState.outerCurY - this._dragState.outerStartY; - this._dragState.outerDX = dx; - this._dragState.outerDY = dy; - }, - - _panHandleBarriers: function (dx, dy) { - // XXX unless the barriers are sorted by position, this will break - // with multiple barriers that are near enough to eachother that a - // drag could cross more than one. - - let vr = this._viewingRect; - - // XXX this just stops at the first horizontal and vertical barrier it finds - - // barrier_[xy] is the barrier that was used to get to the final - // barrier_d[xy] value. if null, no barrier, and dx/dy shouldn't - // be replaced with barrier_d[xy]. - let barrier_y = null, barrier_x = null; - let barrier_dy = 0, barrier_dx = 0; - - for (let i = 0; i < this._barriers.length; i++) { - let b = this._barriers[i]; - - // log2("barrier", i, b.type, b.x, b.y); - - if (dx != 0 && b.type == "vertical") { - if (barrier_x != null) { - delete this._dragState.barrierState[i]; - continue; - } - - let alreadyKnownDistance = this._dragState.barrierState[i] || 0; - - // log2("alreadyKnownDistance", alreadyKnownDistance); - - let dbx = 0; - - // 100 <= 100 && 100-(-5) > 100 - - if ((vr.left <= b.x && vr.left+dx > b.x) || - (vr.left >= b.x && vr.left+dx < b.x)) - { - dbx = b.x - vr.left; - } else if ((vr.right <= b.x && vr.right+dx > b.x) || - (vr.right >= b.x && vr.right+dx < b.x)) - { - dbx = b.x - vr.right; - } else { - delete this._dragState.barrierState[i]; - continue; - } - - let leftoverDistance = dbx - dx; - - // log2("initial dbx", dbx, leftoverDistance); - - let dist = Math.abs(leftoverDistance + alreadyKnownDistance) - b.size; - - if (dist >= 0) { - if (dx < 0) - dbx -= dist; - else - dbx += dist; - delete this._dragState.barrierState[i]; - } else { - dbx = 0; - this._dragState.barrierState[i] = leftoverDistance + alreadyKnownDistance; - } - - // log2("final dbx", dbx, "state", this._dragState.barrierState[i]); - - if (Math.abs(barrier_dx) <= Math.abs(dbx)) { - barrier_x = b; - barrier_dx = dbx; - - // log2("new barrier_dx", barrier_dx); - } - } - - if (dy != 0 && b.type == "horizontal") { - if (barrier_y != null) { - delete this._dragState.barrierState[i]; - continue; - } - - let alreadyKnownDistance = this._dragState.barrierState[i] || 0; - - // log2("alreadyKnownDistance", alreadyKnownDistance); - - let dby = 0; - - // 100 <= 100 && 100-(-5) > 100 - - if ((vr.top <= b.y && vr.top+dy > b.y) || - (vr.top >= b.y && vr.top+dy < b.y)) - { - dby = b.y - vr.top; - } else if ((vr.bottom <= b.y && vr.bottom+dy > b.y) || - (vr.bottom >= b.y && vr.bottom+dy < b.y)) - { - dby = b.y - vr.bottom; - } else { - delete this._dragState.barrierState[i]; - continue; - } - - let leftoverDistance = dby - dy; - - // log2("initial dby", dby, leftoverDistance); - - let dist = Math.abs(leftoverDistance + alreadyKnownDistance) - b.size; - - if (dist >= 0) { - if (dy < 0) - dby -= dist; - else - dby += dist; - delete this._dragState.barrierState[i]; - } else { - dby = 0; - this._dragState.barrierState[i] = leftoverDistance + alreadyKnownDistance; - } - - // log2("final dby", dby, "state", this._dragState.barrierState[i]); - - if (Math.abs(barrier_dy) <= Math.abs(dby)) { - barrier_y = b; - barrier_dy = dby; - - // log2("new barrier_dy", barrier_dy); - } - } - } - - if (barrier_x) { - // log2("did barrier_x", barrier_x, "barrier_dx", barrier_dx); - dx = barrier_dx; - } - - if (barrier_y) { - dy = barrier_dy; - } - - return [dx, dy]; - }, - - _panBy: function _panBy(dx, dy, ignoreBarriers) { - let vr = this._viewingRect; - - // check if any barriers would be crossed by this pan, and take them - // into account. do this first. - if (!ignoreBarriers) - [dx, dy] = this._panHandleBarriers(dx, dy); - - // constrain the full drag of the viewingRect to the pannableBounds. - // note that the viewingRect needs to move in the opposite - // direction of the pan, so we fiddle with the signs here (as you - // pan to the upper left, more of the bottom right becomes visible, - // so the viewing rect moves to the bottom right of the virtual surface). - [dx, dy] = this._rectTranslateConstrain(dx, dy, vr, this.pannableBounds); - - // If the net result is that we don't have any room to move, then - // just return. - if (dx == 0 && dy == 0) - return false; - - // the viewingRect moves opposite of the actual pan direction, see above - vr.x += dx; - vr.y += dy; - - // Go through each widget and move it by dx,dy. Frozen widgets - // will be ignored in commitState. - // The widget rects are in real stack space though, so we need to subtract - // our (now negated) dx, dy from their coordinates. - for (let wid in this._widgetState) { - let state = this._widgetState[wid]; - if (!state.ignoreX) - state.rect.x -= dx; - if (!state.ignoreY) - state.rect.y -= dy; - - this._commitState(state); - } - - /* Do not call panhandler during pans within a transaction. - * Those pans always end-up covering up the checkerboard and - * do not require sliding out the location bar - */ - if (!this._skipViewportUpdates && this._panHandler) - this._panHandler.apply(window, [vr.clone(), dx, dy]); - - return true; - }, - - _dragUpdate: function _dragUpdate() { - let dx = this._dragState.outerLastUpdateDX - this._dragState.outerDX; - let dy = this._dragState.outerLastUpdateDY - this._dragState.outerDY; - - this._dragState.outerLastUpdateDX = this._dragState.outerDX; - this._dragState.outerLastUpdateDY = this._dragState.outerDY; - - return this.panBy(dx, dy); - }, - - // - // widget addition/removal - // - _addNewWidget: function (w) { - let wid = w.getAttribute("id"); - if (!wid) { - reportError("WidgetStack: child widget without id!"); - return; - } - - if (w.getAttribute("hidden") == "true") - return; - - let state = { - widget: w, - id: wid, - - viewport: false, - ignoreX: false, - ignoreY: false, - sticky: false, - frozen: false, - vpRelative: false, - - offsetLeft: 0, - offsetTop: 0, - offsetRight: 0, - offsetBottom: 0 - }; - - this._updateWidgetRect(state); - - if (w.hasAttribute("constraint")) { - let cs = w.getAttribute("constraint").split(","); - for (let s of cs) { - if (s == "ignore-x") - state.ignoreX = true; - else if (s == "ignore-y") - state.ignoreY = true; - else if (s == "sticky") - state.sticky = true; - else if (s == "frozen") { - state.frozen = true; - } else if (s == "vp-relative") - state.vpRelative = true; - } - } - - if (w.hasAttribute("viewport")) { - if (this._viewport) - reportError("WidgetStack: more than one viewport canvas in stack!"); - - this._viewport = state; - state.viewport = true; - - if (w.hasAttribute("vptargetx") && w.hasAttribute("vptargety") && - w.hasAttribute("vptargetw") && w.hasAttribute("vptargeth")) - { - let wx = parseInt(w.getAttribute("vptargetx")); - let wy = parseInt(w.getAttribute("vptargety")); - let ww = parseInt(w.getAttribute("vptargetw")); - let wh = parseInt(w.getAttribute("vptargeth")); - - state.offsetLeft = state.rect.left - wx; - state.offsetTop = state.rect.top - wy; - state.offsetRight = state.rect.right - (wx + ww); - state.offsetBottom = state.rect.bottom - (wy + wh); - - state.rect = new wsRect(wx, wy, ww, wh); - } - - // initialize inner bounds to top-left - state.viewportInnerBounds = new wsRect(0, 0, state.rect.width, state.rect.height); - } - - this._widgetState[wid] = state; - - log ("(New widget: " + wid + (state.viewport ? " [viewport]" : "") + " at: " + state.rect + ")"); - }, - - _removeWidget: function (w) { - let wid = w.getAttribute("id"); - delete this._widgetState[wid]; - this._updateWidgets(); - }, - - // updateWidgets: - // Go through all the widgets and figure out their viewport-relative offsets. - // If the widget goes to the left or above the viewport widget, then - // vpOffsetXBefore or vpOffsetYBefore is set. - // See setViewportBounds for use of vpOffset* state variables, and for how - // the actual x and y coords of each widget are calculated based on their offsets - // and the viewport bounds. - _updateWidgets: function () { - let vp = this._viewport; - - let ofRect = this._viewingRect.clone(); - - for (let wid in this._widgetState) { - let state = this._widgetState[wid]; - if (vp && state.vpRelative) { - // compute the vpOffset from 0,0 assuming that the viewport rect is 0,0 - if (state.rect.left >= vp.rect.right) { - state.vpOffsetXBefore = false; - state.vpOffsetX = state.rect.left - vp.rect.width; - } else { - state.vpOffsetXBefore = true; - state.vpOffsetX = state.rect.left - vp.rect.left; - } - - if (state.rect.top >= vp.rect.bottom) { - state.vpOffsetYBefore = false; - state.vpOffsetY = state.rect.top - vp.rect.height; - } else { - state.vpOffsetYBefore = true; - state.vpOffsetY = state.rect.top - vp.rect.top; - } - - log("widget", state.id, "offset", state.vpOffsetX, state.vpOffsetXBefore ? "b" : "a", state.vpOffsetY, state.vpOffsetYBefore ? "b" : "a", "rect", state.rect); - } - } - - this._updateViewportOverflow(); - }, - - // updates the viewportOverflow/pannableBounds - _updateViewportOverflow: function() { - let vp = this._viewport; - if (!vp) - return; - - let ofRect = new wsRect(0, 0, this._viewingRect.width, this._viewingRect.height); - - for (let wid in this._widgetState) { - let state = this._widgetState[wid]; - if (vp && state.vpRelative) { - ofRect.left = Math.min(ofRect.left, state.rect.left); - ofRect.top = Math.min(ofRect.top, state.rect.top); - ofRect.right = Math.max(ofRect.right, state.rect.right); - ofRect.bottom = Math.max(ofRect.bottom, state.rect.bottom); - } - } - - // prevent the viewportOverflow from having positive top/left or negative - // bottom/right values, which would otherwise happen if there aren't widgets - // beyond each of those edges - this._viewportOverflow = new wsBorder( - /* top*/ Math.round(Math.min(ofRect.top, 0)), - /* left*/ Math.round(Math.min(ofRect.left, 0)), - /* bottom*/ Math.round(Math.max(ofRect.bottom - vp.rect.height, 0)), - /* right*/ Math.round(Math.max(ofRect.right - vp.rect.width, 0)) - ); - - // clear the _pannableBounds cache, since it depends on the - // viewportOverflow - this._pannableBounds = null; - }, - - _widgetBounds: function () { - let r = new wsRect(0, 0, 0, 0); - - for (let wid in this._widgetState) { - let state = this._widgetState[wid]; - r = r.union(state.rect); - } - - return r; - }, - - _commitState: function (state) { - // if the widget is frozen, don't actually update its left/top; - // presumably the caller is managing those directly for now. - if (state.frozen) - return; - let w = state.widget; - let l = state.rect.x + state.offsetLeft; - let t = state.rect.y + state.offsetTop; - - // cache left/top to avoid calling setAttribute unnessesarily - if (state._left != l) { - state._left = l; - w.setAttribute("left", l); - } - - if (state._top != t) { - state._top = t; - w.setAttribute("top", t); - } - }, - - // constrain translate of rect by dx dy to bounds; return dx dy that can - // be used to bring rect up to the edge of bounds if we'd go over. - _rectTranslateConstrain: function (dx, dy, rect, bounds) { - let newX, newY; - - // If the rect is larger than the bounds, allow it to increase its overlap - let woverflow = rect.width > bounds.width; - let hoverflow = rect.height > bounds.height; - if (woverflow || hoverflow) { - let intersection = rect.intersect(bounds); - let newIntersection = rect.clone().translate(dx, dy).intersect(bounds); - if (woverflow) - newX = (newIntersection.width > intersection.width) ? rect.x + dx : rect.x; - if (hoverflow) - newY = (newIntersection.height > intersection.height) ? rect.y + dy : rect.y; - } - - // Common case, rect fits within the bounds - // clamp new X to within [bounds.left, bounds.right - rect.width], - // new Y to within [bounds.top, bounds.bottom - rect.height] - if (isNaN(newX)) - newX = Math.min(Math.max(bounds.left, rect.x + dx), bounds.right - rect.width); - if (isNaN(newY)) - newY = Math.min(Math.max(bounds.top, rect.y + dy), bounds.bottom - rect.height); - - return [newX - rect.x, newY - rect.y]; - }, - - // add a new barrier from a <spacer> - _addNewBarrierFromSpacer: function (el) { - let t = el.getAttribute("barriertype"); - - // XXX implement these at some point - // t != "lr" && t != "rl" && - // t != "tb" && t != "bt" && - - if (t != "horizontal" && - t != "vertical") - { - throw "Invalid barrier type: " + t; - } - - let x, y; - - let barrier = {}; - let vp = this._viewport; - - barrier.type = t; - - if (el.getAttribute("left")) - barrier.x = parseInt(el.getAttribute("left")); - else if (el.getAttribute("top")) - barrier.y = parseInt(el.getAttribute("top")); - else - throw "Barrier without top or left attribute"; - - if (el.getAttribute("size")) - barrier.size = parseInt(el.getAttribute("size")); - else - barrier.size = 10; - - if (el.hasAttribute("constraint")) { - let cs = el.getAttribute("constraint").split(","); - for (let s of cs) { - if (s == "ignore-x") - barrier.ignoreX = true; - else if (s == "ignore-y") - barrier.ignoreY = true; - else if (s == "sticky") - barrier.sticky = true; - else if (s == "frozen") { - barrier.frozen = true; - } else if (s == "vp-relative") - barrier.vpRelative = true; - } - } - - if (barrier.vpRelative) { - if (barrier.type == "vertical") { - if (barrier.x >= vp.rect.right) { - barrier.vpOffsetXBefore = false; - barrier.vpOffsetX = barrier.x - vp.rect.right; - } else { - barrier.vpOffsetXBefore = true; - barrier.vpOffsetX = barrier.x - vp.rect.left; - } - } else if (barrier.type == "horizontal") { - if (barrier.y >= vp.rect.bottom) { - barrier.vpOffsetYBefore = false; - barrier.vpOffsetY = barrier.y - vp.rect.bottom; - } else { - barrier.vpOffsetYBefore = true; - barrier.vpOffsetY = barrier.y - vp.rect.top; - } - - // log2("h barrier relative", barrier.vpOffsetYBefore, barrier.vpOffsetY); - } - } - - this._barriers.push(barrier); - } -}; diff --git a/toolkit/content/tests/fennec-tile-testapp/chrome/content/firefoxOverlay.xul b/toolkit/content/tests/fennec-tile-testapp/chrome/content/firefoxOverlay.xul deleted file mode 100644 index 612f8bb9f..000000000 --- a/toolkit/content/tests/fennec-tile-testapp/chrome/content/firefoxOverlay.xul +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<?xml-stylesheet href="chrome://tile/skin/overlay.css" type="text/css"?> -<!DOCTYPE overlay SYSTEM "chrome://tile/locale/tile.dtd"> -<overlay id="tile-overlay" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - <script src="overlay.js"/> - <stringbundleset id="stringbundleset"> - <stringbundle id="tile-strings" src="chrome://tile/locale/tile.properties"/> - </stringbundleset> - - <menupopup id="menu_ToolsPopup"> - <menuitem id="tile-hello" label="&tile.label;" - oncommand="tile.onMenuItemCommand(event);"/> - </menupopup> -</overlay> diff --git a/toolkit/content/tests/fennec-tile-testapp/chrome/content/foo.xul b/toolkit/content/tests/fennec-tile-testapp/chrome/content/foo.xul deleted file mode 100644 index cdc01658a..000000000 --- a/toolkit/content/tests/fennec-tile-testapp/chrome/content/foo.xul +++ /dev/null @@ -1,460 +0,0 @@ -<window
- xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
- xmlns:html="http://www.w3.org/1999/xhtml"
- onload="onAlmostLoad();"
- style="background-color:white;"
- width="800"
- height="480"
- onresize="onResize();"
- onkeypress="onKeyPress(event);">
-
-<script type="application/javascript" src="WidgetStack.js"/>
-<script type="application/javascript" src="TileManager.js"/>
-<script type="application/javascript" src="BrowserView.js"/>
-<script type="application/javascript">
-<![CDATA[
-
-// We do not endorse the use of globals, but this is just a closed lab
-// environment. What could possibly go wrong? ...
-let bv = null;
-let scrollbox = null;
-let leftbar = null;
-let rightbar = null;
-let topbar = null;
-
-function debug() {
- let w = scrollbox.scrolledWidth;
- let h = scrollbox.scrolledHeight;
- let container = document.getElementById("tile_container");
- let [x, y] = getScrollboxPosition();
- if (bv) {
- dump('----------------------DEBUG!-------------------------\n');
- dump(bv._browserViewportState.toString() + endl);
-
- dump(endl);
-
- let cr = bv._tileManager._criticalRect;
- dump('criticalRect from BV: ' + (cr ? cr.toString() : null) + endl);
- dump('visibleRect from BV : ' + bv._visibleRect.toString() + endl);
- dump('visibleRect from foo: ' + scrollboxToViewportRect(getVisibleRect()) + endl);
-
- dump(endl);
-
- dump('container width,height from BV: ' + bv._container.style.width + ', '
- + bv._container.style.height + endl);
- dump('container width,height via DOM: ' + container.style.width + ', '
- + container.style.height + endl);
-
- dump(endl);
-
- dump('scrollbox position : ' + x + ', ' + y + endl);
- dump('scrollbox scrolledsize: ' + w + ', ' + h + endl);
-
- dump(endl);
-
- dump('tilecache capacity: ' + bv._tileManager._tileCache.getCapacity() + endl);
- dump('tilecache size : ' + bv._tileManager._tileCache.size + endl);
- dump('tilecache numFree : ' + bv._tileManager._tileCache.numFree + endl);
- dump('tilecache iBound : ' + bv._tileManager._tileCache.iBound + endl);
- dump('tilecache jBound : ' + bv._tileManager._tileCache.jBound + endl);
- dump('tilecache _lru : ' + bv._tileManager._tileCache._lru + endl);
-
- dump('-----------------------------------------------------\n');
- }
-}
-
-function debugTile(i, j) {
- let tc = bv._tileManager._tileCache;
- let t = tc.getTile(i, j);
-
- dump('------ DEBUGGING TILE (' + i + ',' + j + ') --------\n');
-
- dump('in bounds: ' + tc.inBounds(i, j) + endl);
- dump('occupied : ' + tc._isOccupied(i, j) + endl);
- if (t)
- {
- dump('toString : ' + t.toString(true) + endl);
- dump('free : ' + t.free + endl);
- dump('dirtyRect: ' + t._dirtyTileCanvasRect + endl);
-
- let len = tc._tilePool.length;
- for (let k = 0; k < len; ++k)
- if (tc._tilePool[k] === t)
- dump('found in tilePool at index ' + k + endl);
- }
-
- dump('------------------------------------\n');
-}
-
-function onKeyPress(e) {
- const a = 97; // debug all critical tiles
- const c = 99; // set tilecache capacity
- const d = 100; // debug dump
- const f = 102; // run noop() through forEachIntersectingRect (for timing)
- const i = 105; // toggle info click mode
- const l = 108; // restart lazy crawl
- const m = 109; // fix mouseout
- const t = 116; // debug given list of tiles separated by space
-
- switch (e.charCode) {
- case d:
- debug();
-
- break;
- case l:
- bv._tileManager.restartLazyCrawl(bv._tileManager._criticalRect);
-
- break;
- case c:
- let cap = parseInt(window.prompt('new capacity'));
- bv._tileManager._tileCache.setCapacity(cap);
-
- break;
- case f:
- let noop = function noop() { for (let i = 0; i < 10; ++i); };
- bv._tileManager._tileCache.forEachIntersectingRect(bv._tileManager._criticalRect,
- false, noop, window);
-
- break;
- case t:
- let ijstrs = window.prompt('row,col plz').split(' ');
- for (let ijstr of ijstrs) {
- let [i, j] = ijstr.split(',').map(x => parseInt(x));
- debugTile(i, j);
- }
-
- break;
- case a:
- let cr = bv._tileManager._criticalRect;
- dump('>>>>>> critical rect is ' + (cr ? cr.toString() : cr) + endl);
- if (cr) {
- let starti = cr.left >> kTileExponentWidth;
- let endi = cr.right >> kTileExponentWidth;
-
- let startj = cr.top >> kTileExponentHeight;
- let endj = cr.bottom >> kTileExponentHeight;
-
- for (var jj = startj; jj <= endj; ++jj)
- for (var ii = starti; ii <= endi; ++ii)
- debugTile(ii, jj);
- }
-
- break;
- case i:
- window.infoMode = !window.infoMode;
- break;
- case m:
- onMouseUp();
- break;
- default:
- break;
- }
-}
-
-function onResize(e) {
- if (bv) {
- bv.beginBatchOperation();
- bv.setVisibleRect(scrollboxToViewportRect(getVisibleRect()));
- bv.zoomToPage();
- bv.commitBatchOperation();
- }
-}
-
-function onMouseDown(e) {
- if (window.infoMode) {
- let [basex, basey] = getScrollboxPosition();
- let [x, y] = scrollboxToViewportXY(basex + e.clientX, basey + e.clientY);
- let i = x >> kTileExponentWidth;
- let j = y >> kTileExponentHeight;
-
- debugTile(i, j);
- }
-
- window._isDragging = true;
- window._dragStart = {x: e.clientX, y: e.clientY};
-
- bv.pauseRendering();
-}
-
-function onMouseUp() {
- window._isDragging = false;
- bv.resumeRendering();
-}
-
-function onMouseMove(e) {
- if (window._isDragging) {
- let x = scrollbox.positionX;
- let y = scrollbox.positionY;
- let w = scrollbox.scrolledWidth;
- let h = scrollbox.scrolledHeight;
-
- let dx = window._dragStart.x - e.clientX;
- let dy = window._dragStart.y - e.clientY;
-
- // XXX if max(x, 0) > scrollwidth we shouldn't do anything (same for y/height)
- let newX = Math.max(x + dx, 0);
- let newY = Math.max(y + dy, 0);
-
- if (newX < w || newY < h) {
- // clip dx and dy to prevent us from going below 0
- dx = Math.max(dx, -x);
- dy = Math.max(dy, -y);
-
- let oldx = x;
- let oldy = y;
-
- bv.onBeforeVisibleMove(dx, dy);
-
- updateBars(oldx, oldy, dx, dy);
- scrollbox.scrollBy(dx, dy);
-
- let [newx, newy] = getScrollboxPosition();
- let realdx = newx - oldx;
- let realdy = newy - oldy;
-
- updateBars(oldx, oldy, realdx, realdy);
- bv.onAfterVisibleMove(realdx, realdy);
- }
- window._dragStart = {x: e.clientX, y: e.clientY};
- }
-}
-
-function onAlmostLoad() {
- window._isDragging = false;
- window.infoMode = false;
- window.setTimeout(onLoad, 1500);
-}
-
-function onLoad() {
- // ----------------------------------------------------
- scrollbox = document.getElementById("scrollbox").boxObject;
- leftbar = document.getElementById("left_sidebar");
- rightbar = document.getElementById("right_sidebar");
- topbar = document.getElementById("top_urlbar");
- // ----------------------------------------------------
-
- let initX = Math.round(leftbar.getBoundingClientRect().right);
- dump('scrolling to ' + initX + endl);
- scrollbox.scrollTo(initX, 0);
- let [x, y] = getScrollboxPosition();
- dump(' scrolled to ' + x + ',' + y + endl);
-
- let container = document.getElementById("tile_container");
- container.addEventListener("mousedown", onMouseDown, true);
- container.addEventListener("mouseup", onMouseUp, true);
- container.addEventListener("mousemove", onMouseMove, true);
-
- bv = new BrowserView(container, scrollboxToViewportRect(getVisibleRect()));
-
- let browser = document.getElementById("googlenews");
- bv.setBrowser(browser, false);
-}
-
-function updateBars(x, y, dx, dy) {
-return;
- // shouldn't update margin if it doesn't need to be changed
- let sidebars = document.getElementsByClassName("sidebar");
- for (let i = 0; i < sidebars.length; i++) {
- let sidebar = sidebars[i];
- sidebar.style.margin = (y + dy) + "px 0px 0px 0px";
- }
-
- let urlbar = document.getElementById("top_urlbar");
- urlbar.style.margin = "0px 0px 0px " + (x + dx) + "px";
-}
-
-function viewportToScrollboxXY(x, y) {
- return scrollboxToViewportXY(x, y, -1);
-}
-
-function scrollboxToViewportXY(x, y) {
- if (!x) x = 0;
- if (!y) y = 0;
-
- // shield your eyes!
- let direction = (arguments.length >= 3) ? arguments[2] : 1;
-
- let leftbarcr = leftbar.getBoundingClientRect();
- let rightbarcr = rightbar.getBoundingClientRect();
- let topbarcr = topbar.getBoundingClientRect();
-
- let xtrans = direction * (-leftbarcr.width);
- let ytrans = direction * (-topbarcr.height);
- x += xtrans;
- y += ytrans;
-
- return [x, y];
-}
-
-function scrollboxToBrowserXY(browserView, x, y) {
- [x, y] = scrollboxToViewportXY(x, y);
- return [browserView.viewportToBrowser(x),
- browserView.viewportToBrowser(y)];
-}
-
-function scrollboxToViewportRect(rect) {
- let leftbarcr = leftbar.getBoundingClientRect();
- let topbarcr = topbar.getBoundingClientRect();
-
- let xtrans = -leftbarcr.width;
- let ytrans = -topbarcr.height;
-
- rect.translate(xtrans, ytrans);
-
- return rect;
-}
-
-function getScrollboxPosition() {
- return [scrollbox.positionX, scrollbox.positionY];
-}
-
-function getContentScrollValues(browser) {
- let cwu = getBrowserDOMWindowUtils(browser);
- let scrollX = {};
- let scrollY = {};
- cwu.getScrollXY(false, scrollX, scrollY);
-
- return [scrollX.value, scrollY.value];
-}
-
-function getBrowserDOMWindowUtils(browser) {
- return browser.contentWindow
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindowUtils);
-}
-
-function getBrowserClientRect(browser, el) {
- let [scrollX, scrollY] = getContentScrollValues(browser);
- let r = el.getBoundingClientRect();
-
- return new wsRect(r.left + scrollX,
- r.top + scrollY,
- r.width, r.height);
-}
-
-function scrollToElement(browser, el) {
- var elRect = getPagePosition(browser, el);
- bv.browserToViewportRect(elRect);
- elRect.round();
- this.scrollTo(elRect.x, elRect.y);
-}
-
-function zoomToElement(aElement) {
- const margin = 15;
-
- let elRect = getBrowserClientRect(browser, aElement);
- let elWidth = elRect.width;
- let vrWidth = bv.visibleRect.width;
- /* Try to set zoom-level such that once zoomed element is as wide
- * as the visible rect */
- let zoomLevel = vrtWidth / (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 xpadding = Math.max(margin, vrWidth - bv.browserToViewport(elWidth));
-
- // XXX TODO these arguments are wrong, we still have to transform the coordinates
- // from viewport to scrollbox before sending them to scrollTo
- this.scrollTo(Math.floor(Math.max(bv.browserToViewport(elRect.x) - xpadding, 0)),
- Math.floor(Math.max(bv.browserToViewport(elRect.y) - margin, 0)));
-
- bv.commitBatchOperation();
-}
-
-function zoomFromElement(browser, aElement) {
- let elRect = getBrowserClientRect(browser, aElement);
-
- bv.beginBatchOperation();
-
- // pan to the element
- // don't bother with x since we're zooming all the way out
- bv.zoomToPage();
-
- // XXX have this center the element on the page
- // XXX TODO these arguments are wrong, we still have to transform the coordinates
- // from viewport to scrollbox before sending them to scrollTo
- this.scrollTo(0, Math.floor(Math.max(0, bv.browserToViewport(elRect.y))));
-
- bv.commitBatchOperation();
-}
-
-/**
- * Retrieve the content element for a given point in client coordinates
- * (relative to the top left corner of the chrome window).
- */
-function elementFromPoint(browser, browserView, x, y) {
- [x, y] = scrollboxToBrowserXY(browserView, x, y);
- let cwu = getBrowserDOMWindowUtils(browser);
- return cwu.elementFromPoint(x, y,
- true, /* ignore root scroll frame*/
- false); /* don't flush layout */
-}
-
-/* ensures that a given content element is visible */
-function ensureElementIsVisible(browser, aElement) {
- let elRect = getBrowserClientRect(browser, aElement);
-
- bv.browserToViewportRect(elRect);
-
- let curRect = bv.visibleRect;
- let newx = curRect.x;
- let newy = curRect.y;
-
- if (elRect.x < curRect.x || elRect.width > curRect.width) {
- newx = elRect.x;
- } else if (elRect.x + elRect.width > curRect.x + curRect.width) {
- newx = elRect.x - curRect.width + elRect.width;
- }
-
- if (elRect.y < curRect.y || elRect.height > curRect.height) {
- newy = elRect.y;
- } else if (elRect.y + elRect.height > curRect.y + curRect.height) {
- newy = elRect.y - curRect.height + elRect.height;
- }
-
- // XXX TODO these arguments are wrong, we still have to transform the coordinates
- // from viewport to scrollbox before sending them to scrollTo
- this.scrollTo(newx, newy);
-}
-
-// this is a mehful way of getting the visible rect in scrollbox coordinates
-// that we use in this here lab environment and hopefully nowhere in real fennec
-function getVisibleRect() {
- let w = window.innerWidth;
- let h = window.innerHeight;
-
- let [x, y] = getScrollboxPosition();
-
- return new wsRect(x, y, w, h);
-}
-
-]]>
-</script>
-
-<scrollbox id="scrollbox" style="-moz-box-orient: vertical; overflow: scroll;" flex="1">
- <hbox id="top_urlbar" style="background-color: pink"><textbox flex="1"/></hbox>
- <hbox style="position: relative">
- <vbox id="left_sidebar" class="sidebar" style="background-color: red"><button label="left sidebar"/></vbox>
- <box>
- <html:div id="tile_container" style="position: relative; width: 800px; height: 480px; overflow: -moz-hidden-unscrollable;"/>
- </box>
- <vbox id="right_sidebar" class="sidebar" style="background-color: blue"><button label="right sidebar"/></vbox>
- </hbox>
-</scrollbox>
-
- <box>
- <html:div style="position: relative; overflow: hidden; max-width: 0px; max-height: 0px; visibility: hidden;">
- <html:div id="browsers" style="position: absolute;">
- <!-- <browser id="googlenews" src="http://www.webhamster.com/" type="content" remote="true" style="width: 1024px; height: 614px"/> -->
- <iframe id="googlenews" src="http://news.google.com/" type="content" remote="true" style="width: 1024px; height: 614px"/>
- </html:div>
- </html:div>
- </box>
-
-</window>
diff --git a/toolkit/content/tests/fennec-tile-testapp/chrome/content/main.xul b/toolkit/content/tests/fennec-tile-testapp/chrome/content/main.xul deleted file mode 100644 index f829b3f4a..000000000 --- a/toolkit/content/tests/fennec-tile-testapp/chrome/content/main.xul +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version="1.0"?> -<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> - -<window id="main" title="My App" width="300" height="300" -xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - <caption label="Hello World"/> -</window> diff --git a/toolkit/content/tests/fennec-tile-testapp/chrome/content/overlay.js b/toolkit/content/tests/fennec-tile-testapp/chrome/content/overlay.js deleted file mode 100644 index 8dd09af00..000000000 --- a/toolkit/content/tests/fennec-tile-testapp/chrome/content/overlay.js +++ /dev/null @@ -1,15 +0,0 @@ -var tile = { - onLoad: function() { - // initialization code - this.initialized = true; - this.strings = document.getElementById("tile-strings"); - }, - onMenuItemCommand: function(e) { - var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] - .getService(Components.interfaces.nsIPromptService); - promptService.alert(window, this.strings.getString("helloMessageTitle"), - this.strings.getString("helloMessage")); - }, - -}; -window.addEventListener("load", function(e) { tile.onLoad(e); }, false); diff --git a/toolkit/content/tests/fennec-tile-testapp/chrome/locale/en-US/tile.dtd b/toolkit/content/tests/fennec-tile-testapp/chrome/locale/en-US/tile.dtd deleted file mode 100644 index 8cffbce35..000000000 --- a/toolkit/content/tests/fennec-tile-testapp/chrome/locale/en-US/tile.dtd +++ /dev/null @@ -1 +0,0 @@ -<!ENTITY tile.label "Your localized menuitem"> diff --git a/toolkit/content/tests/fennec-tile-testapp/chrome/locale/en-US/tile.properties b/toolkit/content/tests/fennec-tile-testapp/chrome/locale/en-US/tile.properties deleted file mode 100644 index 72062a4f0..000000000 --- a/toolkit/content/tests/fennec-tile-testapp/chrome/locale/en-US/tile.properties +++ /dev/null @@ -1,3 +0,0 @@ -helloMessage=Hello World! -helloMessageTitle=Hello -prefMessage=Int Pref Value: %d diff --git a/toolkit/content/tests/fennec-tile-testapp/chrome/skin/overlay.css b/toolkit/content/tests/fennec-tile-testapp/chrome/skin/overlay.css deleted file mode 100644 index 98718057f..000000000 --- a/toolkit/content/tests/fennec-tile-testapp/chrome/skin/overlay.css +++ /dev/null @@ -1,5 +0,0 @@ -/* This is just an example. You shouldn't do this. */ -#tile-hello -{ - color: red ! important; -} |