Commit the Fennec tile testapp from Stuart for testing

This commit is contained in:
Benjamin Smedberg 2009-07-22 14:44:46 -04:00
parent d125f2e7bb
commit ecb4b6a7b9
16 changed files with 4266 additions and 0 deletions

View File

@ -0,0 +1,11 @@
[App]
Vendor=venderr
Name=tile
Version=1.0
BuildID=20060101
Copyright=Copyright (c) 2006 Mark Finkle
ID=xulapp@starkravingfinkle.org
[Gecko]
MinVersion=1.8
MaxVersion=1.9.2.*

View File

@ -0,0 +1 @@
content tile file:content/

View File

@ -0,0 +1,736 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Mobile Browser.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Roy Frostig <rfrostig@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
let Ci = Components.interfaces;
// --- REMOVE ---
let noop = function() {};
let 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) {
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);
return [w, h];
}
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}';
}
};

View File

@ -0,0 +1,359 @@
let 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);
*************************************************************/
};
let ws = {
beginUpdateBatch: noop,
panTo: noop,
endUpdateBatch: noop
};
let Ci = Components.interfaces;
let bv = null;
let 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
.QueryInterface(Components.interfaces.nsIScrollBoxObject);
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() {
let x = {};
let y = {};
this._scrollbox.getPosition(x, y);
return [x.value, y.value];
},
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 = {};
let y = {};
let w = {};
let h = {};
scrollbox.getPosition(x, y);
scrollbox.getScrolledSize(w, h);
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.value + dx, 0);
let newY = Math.max(y.value + dy, 0);
if (newX < w.value || newY < h.value) {
// clip dx and dy to prevent us from going below 0
dx = Math.max(dx, -x.value);
dy = Math.max(dy, -y.value);
bv.onBeforeScroll(x.value, y.value, 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();
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
<?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>

View File

@ -0,0 +1,469 @@
<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 = {};
let h = {};
scrollbox.getScrolledSize(w, h);
let container = document.getElementById("tile_container");
let [x, y] = getScrollboxPosition();
let [w, h] = [w.value, h.value];
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 each (let ijstr in ijstrs) {
let [i, j] = ijstr.split(',').map(function (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 = {};
let y = {};
let w = {};
let h = {};
scrollbox.getPosition(x, y);
scrollbox.getScrolledSize(w, h);
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.value + dx, 0);
let newY = Math.max(y.value + dy, 0);
if (newX < w.value || newY < h.value) {
// clip dx and dy to prevent us from going below 0
dx = Math.max(dx, -x.value);
dy = Math.max(dy, -y.value);
let oldx = x.value;
let oldy = y.value;
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
.QueryInterface(Components.interfaces.nsIScrollBoxObject);
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() {
let x = {};
let y = {};
scrollbox.getPosition(x, y);
return [x.value, y.value];
}
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" style="width: 1024px; height: 614px"/> -->
<browser id="googlenews" src="http://news.google.com/" type="content" style="width: 1024px; height: 614px"/>
</html:div>
</html:div>
</box>
</window>

View File

@ -0,0 +1,7 @@
<?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>

View File

@ -0,0 +1,15 @@
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);

View File

@ -0,0 +1 @@
<!ENTITY tile.label "Your localized menuitem">

View File

@ -0,0 +1,3 @@
helloMessage=Hello World!
helloMessageTitle=Hello
prefMessage=Int Pref Value: %d

View File

@ -0,0 +1,5 @@
/* This is just an example. You shouldn't do this. */
#tile-hello
{
color: red ! important;
}

View File

@ -0,0 +1,4 @@
pref("toolkit.defaultChromeURI", "chrome://tile/content/foo.xul");
pref("javascript.options.jit.chrome", true);
pref("javascript.options.jit.content", false);
pref("browser.dom.window.dump.enabled", true);

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>tile@roy</em:id>
<em:name>tile</em:name>
<em:version>1.0</em:version>
<em:creator>Roy</em:creator>
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <!-- firefox -->
<em:minVersion>1.5</em:minVersion>
<em:maxVersion>3.5.*</em:maxVersion>
</Description>
</em:targetApplication>
</Description>
</RDF>

View File

@ -0,0 +1,104 @@
#!/usr/bin/python
import re, sys
interesting_re = re.compile("(js_Execute|CallHook) ([^ ]+) ([^ ]+ )?([^ ]+ms)")
class Entry:
def __init__(self, kind, depth, file, linenum, func, timetaken):
self.kind = kind
self.depth = depth
self.file = file
self.linenum = linenum
self.func = func
self.timetaken = timetaken
self.calls = 0
self.duration = 0
def __str__(self):
return " ".join(map(str,[self.kind, self.depth, self.file, self.linenum, self.func, self.timetaken]))
def id(self):
if self.kind == "js_Execute":
return self.file
else:
if self.file and self.linenum:
strout = "%s:%d" % (self.file, self.linenum)
if self.func:
strout = "%s %s" % (self.func, strout)
return strout
elif self.func:
return self.func
else:
print("No clue what my id is:"+self)
def call(self, timetaken):
self.calls += 1
self.duration += timetaken
def parse_line(line):
m = interesting_re.search(line)
if not m:
return None
ms_index = line.find("ms")
depth = m.start() - ms_index - 3
kind = m.group(1)
func = None
file = None
linenum = None
if kind == "CallHook":
func = m.group(2)
file = m.group(3)
colpos = file.rfind(":")
(file,linenum) = file[:colpos], file[colpos+1:-1]
if linenum == "0":
linenum = None
else:
linenum = int(linenum)
offset = 1
else:
file = m.group(3)
timetaken = None
try:
timetaken = float(m.group(4)[:-2])
except:
return None
return Entry(kind, depth, file, linenum, func, timetaken)
def compare(x,y):
diff = x[1].calls - y[1].calls
if diff == 0:
return int(x[1].duration - y[1].duration)
elif diff > 0:
return 1
elif diff < 0:
return -1
def frequency(ls):
dict = {}
for item in ls:
id = item.id()
stat = None
if not id in dict:
stat = dict[id] = item
else:
stat = dict[id]
stat.call(item.timetaken)
ls = dict.items()
ls.sort(compare)
ls = filter(lambda (_,item): item.duration > 20, ls)
# ls = filter(lambda (_,item): item.file and item.file.find("browser.js") != -1 and item.linenum <= 1223 and item.linenum >1067, ls)
for key, item in ls:
print(item.calls,key, str(item.duration)+"ms")
def go():
file = sys.argv[1]
ls = filter(lambda x: x != None, map(parse_line, open(file).readlines()))
frequency(ls)
print ls[0]
go()