2009-07-18 03:17:57 +04:00
|
|
|
// -*- 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.
|
2009-07-18 04:14:49 +04:00
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2009
|
2009-07-18 03:17:57 +04:00
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* Roy Frostig <rfrostig@mozilla.com>
|
2009-07-18 04:14:49 +04:00
|
|
|
* Stuart Parmenter <stuart@mozilla.com>
|
2009-07-18 03:17:57 +04:00
|
|
|
*
|
|
|
|
* 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;
|
|
|
|
|
2009-07-18 07:05:07 +04:00
|
|
|
const kBrowserViewZoomLevelMin = 0.2;
|
|
|
|
const kBrowserViewZoomLevelMax = 4.0;
|
2009-10-24 08:04:51 +04:00
|
|
|
const kBrowserFormZoomLevelMin = 1.0;
|
|
|
|
const kBrowserFormZoomLevelMax = 2.0;
|
2009-07-18 07:05:07 +04:00
|
|
|
const kBrowserViewZoomLevelPrecision = 10000;
|
2010-03-26 18:32:43 +03:00
|
|
|
const kBrowserViewZoomLevelIncrement = 0.1;
|
|
|
|
const kBrowserViewZoomLevelPageFitAdjust = 0.2;
|
2009-10-10 07:03:14 +04:00
|
|
|
const kBrowserViewPrefetchBeginIdleWait = 1; // seconds
|
2009-12-10 21:17:39 +03:00
|
|
|
const kBrowserViewPrefetchBeginIdleWaitLoading = 10; // seconds
|
2010-01-15 00:08:40 +03:00
|
|
|
const kBrowserViewCacheSize = 6;
|
2009-07-18 03:17:57 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
2009-07-18 04:14:49 +04:00
|
|
|
* hopefully no one but us will touch. See BrowserView.Util.getViewportStateFromBrowser()
|
|
|
|
* for the property name.
|
2009-07-18 03:17:57 +04:00
|
|
|
*/
|
2009-08-11 05:03:42 +04:00
|
|
|
function BrowserView(container, visibleRectFactory) {
|
2009-07-18 07:05:07 +04:00
|
|
|
Util.bindAll(this);
|
2009-08-11 05:03:42 +04:00
|
|
|
this.init(container, visibleRectFactory);
|
2009-07-18 07:05:07 +04:00
|
|
|
}
|
2009-07-18 03:17:57 +04:00
|
|
|
|
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
// -----------------------------------------------------------
|
|
|
|
// Util/convenience functions.
|
|
|
|
//
|
|
|
|
// These are mostly for use by BrowserView itself, but if you find them handy anywhere
|
|
|
|
// else, feel free.
|
|
|
|
//
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
BrowserView.Util = {
|
|
|
|
visibleRectToCriticalRect: function visibleRectToCriticalRect(visibleRect, browserViewportState) {
|
2009-07-18 03:17:57 +04:00
|
|
|
return visibleRect.intersect(browserViewportState.viewportRect);
|
2009-07-18 04:14:49 +04:00
|
|
|
},
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
clampZoomLevel: function clampZoomLevel(zl) {
|
|
|
|
let bounded = Math.min(Math.max(kBrowserViewZoomLevelMin, zl), kBrowserViewZoomLevelMax);
|
2009-07-30 02:44:41 +04:00
|
|
|
let rounded = Math.round(bounded * kBrowserViewZoomLevelPrecision) / kBrowserViewZoomLevelPrecision;
|
|
|
|
return (rounded) ? rounded : 1.0;
|
2009-07-18 04:14:49 +04:00
|
|
|
},
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2010-03-26 07:09:23 +03:00
|
|
|
/** Force zoom levels "near" 1 to exactly 1. */
|
2010-03-26 18:32:43 +03:00
|
|
|
adjustZoomLevel: function adjustZoomLevel(zl, threshold) {
|
|
|
|
return (Math.abs(1.0 - zl) < threshold) ? 1.0 : zl;
|
2010-03-26 07:09:23 +03:00
|
|
|
},
|
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
pageZoomLevel: function pageZoomLevel(visibleRect, browserW, browserH) {
|
2010-03-26 07:09:23 +03:00
|
|
|
let zl = BrowserView.Util.clampZoomLevel(visibleRect.width / browserW);
|
2010-03-26 18:32:43 +03:00
|
|
|
return BrowserView.Util.adjustZoomLevel(zl, kBrowserViewZoomLevelPageFitAdjust);
|
2009-07-18 04:14:49 +04:00
|
|
|
},
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-09-05 09:14:59 +04:00
|
|
|
createBrowserViewportState: function createBrowserViewportState() {
|
2009-10-15 23:50:32 +04:00
|
|
|
return new BrowserView.BrowserViewportState(new Rect(0, 0, 1, 1), 0, 0, 1);
|
2009-07-18 04:14:49 +04:00
|
|
|
},
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
getViewportStateFromBrowser: function getViewportStateFromBrowser(browser) {
|
2009-07-18 03:17:57 +04:00
|
|
|
return browser.__BrowserView__vps;
|
2009-07-18 04:14:49 +04:00
|
|
|
},
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-09-09 02:02:49 +04:00
|
|
|
/**
|
|
|
|
* Calling this is likely to cause a reflow of the browser's document. Use
|
|
|
|
* wisely.
|
|
|
|
*/
|
2009-07-18 04:14:49 +04:00
|
|
|
getBrowserDimensions: function getBrowserDimensions(browser) {
|
2009-07-18 03:17:57 +04:00
|
|
|
let cdoc = browser.contentDocument;
|
2009-08-19 09:15:12 +04:00
|
|
|
if (cdoc instanceof SVGDocument) {
|
|
|
|
let rect = cdoc.rootElement.getBoundingClientRect();
|
|
|
|
return [Math.ceil(rect.width), Math.ceil(rect.height)];
|
|
|
|
}
|
2009-07-18 03:17:57 +04:00
|
|
|
|
|
|
|
// These might not exist yet depending on page load state
|
|
|
|
let body = cdoc.body || {};
|
|
|
|
let html = cdoc.documentElement || {};
|
2009-09-04 05:12:02 +04:00
|
|
|
let w = Math.max(body.scrollWidth || 1, html.scrollWidth);
|
|
|
|
let h = Math.max(body.scrollHeight || 1, html.scrollHeight);
|
2009-07-18 03:17:57 +04:00
|
|
|
|
|
|
|
return [w, h];
|
2009-07-18 04:14:49 +04:00
|
|
|
},
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-10-15 23:50:32 +04:00
|
|
|
getContentScrollOffset: function getContentScrollOffset(browser) {
|
2009-07-18 04:14:49 +04:00
|
|
|
let cwu = BrowserView.Util.getBrowserDOMWindowUtils(browser);
|
2009-07-18 03:17:57 +04:00
|
|
|
let scrollX = {};
|
|
|
|
let scrollY = {};
|
|
|
|
cwu.getScrollXY(false, scrollX, scrollY);
|
|
|
|
|
2009-10-15 23:50:32 +04:00
|
|
|
return new Point(scrollX.value, scrollY.value);
|
2009-07-18 04:14:49 +04:00
|
|
|
},
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
getBrowserDOMWindowUtils: function getBrowserDOMWindowUtils(browser) {
|
2009-07-18 03:17:57 +04:00
|
|
|
return browser.contentWindow
|
|
|
|
.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
|
|
.getInterface(Ci.nsIDOMWindowUtils);
|
2009-07-18 04:14:49 +04:00
|
|
|
},
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
getNewBatchOperationState: function getNewBatchOperationState() {
|
2009-07-18 03:17:57 +04:00
|
|
|
return {
|
|
|
|
viewportSizeChanged: false,
|
|
|
|
dirtyAll: false
|
|
|
|
};
|
2009-07-18 04:14:49 +04:00
|
|
|
},
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
initContainer: function initContainer(container, visibleRect) {
|
2009-07-18 03:17:57 +04:00
|
|
|
container.style.width = visibleRect.width + 'px';
|
|
|
|
container.style.height = visibleRect.height + 'px';
|
|
|
|
container.style.overflow = '-moz-hidden-unscrollable';
|
2009-07-18 04:14:49 +04:00
|
|
|
},
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
resizeContainerToViewport: function resizeContainerToViewport(container, viewportRect) {
|
2009-07-18 03:17:57 +04:00
|
|
|
container.style.width = viewportRect.width + 'px';
|
|
|
|
container.style.height = viewportRect.height + 'px';
|
|
|
|
}
|
2009-07-18 04:14:49 +04:00
|
|
|
};
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
BrowserView.prototype = {
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
// -----------------------------------------------------------
|
|
|
|
// Public instance methods
|
|
|
|
//
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-08-11 05:03:42 +04:00
|
|
|
init: function init(container, visibleRectFactory) {
|
2009-07-18 04:14:49 +04:00
|
|
|
this._batchOps = [];
|
|
|
|
this._container = container;
|
|
|
|
this._browser = null;
|
|
|
|
this._browserViewportState = null;
|
|
|
|
this._contentWindow = null;
|
|
|
|
this._renderMode = 0;
|
2009-10-22 03:11:38 +04:00
|
|
|
this._offscreenDepth = 0;
|
2009-09-09 02:02:49 +04:00
|
|
|
|
2009-10-20 00:22:17 +04:00
|
|
|
let cacheSize = kBrowserViewCacheSize;
|
|
|
|
try {
|
|
|
|
cacheSize = gPrefService.getIntPref("tile.cache.size");
|
|
|
|
} catch(e) {}
|
2010-01-15 00:08:40 +03:00
|
|
|
|
|
|
|
if (cacheSize == -1) {
|
|
|
|
let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2);
|
|
|
|
let device = sysInfo.get("device");
|
|
|
|
switch (device) {
|
2010-02-10 04:00:40 +03:00
|
|
|
#ifdef MOZ_PLATFORM_MAEMO
|
2010-01-15 00:08:40 +03:00
|
|
|
case "Nokia N900":
|
|
|
|
cacheSize = 26;
|
|
|
|
break;
|
|
|
|
case "Nokia N8xx":
|
|
|
|
// N8xx has half the memory of N900 and crashes with higher numbers
|
|
|
|
cacheSize = 10;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
// Use a minimum number of tiles sice we don't know the device
|
|
|
|
cacheSize = 6;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-20 00:22:17 +04:00
|
|
|
this._tileManager = new TileManager(this._appendTile, this._removeTile, this, cacheSize);
|
2009-08-11 05:03:42 +04:00
|
|
|
this._visibleRectFactory = visibleRectFactory;
|
2009-10-10 07:03:14 +04:00
|
|
|
|
|
|
|
this._idleServiceObserver = new BrowserView.IdleServiceObserver(this);
|
|
|
|
this._idleService = Cc["@mozilla.org/widget/idleservice;1"].getService(Ci.nsIIdleService);
|
|
|
|
this._idleService.addIdleObserver(this._idleServiceObserver, kBrowserViewPrefetchBeginIdleWait);
|
2009-12-10 21:17:39 +03:00
|
|
|
this._idleServiceWait = kBrowserViewPrefetchBeginIdleWait;
|
2009-11-25 00:59:35 +03:00
|
|
|
|
|
|
|
let browsers = document.getElementById("browsers");
|
|
|
|
browsers.addEventListener("MozScrolledAreaChanged", this.handleMozScrolledAreaChanged, false);
|
2009-07-18 04:14:49 +04:00
|
|
|
},
|
2010-01-15 00:08:40 +03:00
|
|
|
|
2009-10-14 23:04:33 +04:00
|
|
|
uninit: function uninit() {
|
2009-11-25 22:07:00 +03:00
|
|
|
this.setBrowser(null, null);
|
2009-12-10 21:17:39 +03:00
|
|
|
this._idleService.removeIdleObserver(this._idleServiceObserver, this._idleServiceWait);
|
|
|
|
},
|
|
|
|
|
|
|
|
/** When aggressive, spend more time rendering tiles. */
|
|
|
|
setAggressive: function setAggressive(aggro) {
|
|
|
|
let wait = aggro ? kBrowserViewPrefetchBeginIdleWait : kBrowserViewPrefetchBeginIdleWaitLoading;
|
|
|
|
this._idleService.removeIdleObserver(this._idleServiceObserver, this._idleServiceWait);
|
|
|
|
this._idleService.addIdleObserver(this._idleServiceObserver, wait);
|
2009-12-14 08:51:11 +03:00
|
|
|
this._idleServiceWait = wait;
|
2009-10-14 23:04:33 +04:00
|
|
|
},
|
2009-07-18 04:14:49 +04:00
|
|
|
|
|
|
|
getVisibleRect: function getVisibleRect() {
|
2009-08-11 05:03:42 +04:00
|
|
|
return this._visibleRectFactory();
|
2009-07-18 04:14:49 +04:00
|
|
|
},
|
|
|
|
|
2009-09-09 02:02:49 +04:00
|
|
|
/**
|
|
|
|
* @return [width, height]
|
|
|
|
*/
|
|
|
|
getViewportDimensions: function getViewportDimensions() {
|
2009-07-18 04:14:49 +04:00
|
|
|
let bvs = this._browserViewportState;
|
2009-09-09 02:02:49 +04:00
|
|
|
if (!bvs)
|
|
|
|
throw "Cannot get viewport dimensions when no browser is set";
|
|
|
|
|
|
|
|
return [bvs.viewportRect.right, bvs.viewportRect.bottom];
|
|
|
|
},
|
|
|
|
|
|
|
|
setZoomLevel: function setZoomLevel(zoomLevel) {
|
|
|
|
let bvs = this._browserViewportState;
|
2009-07-18 04:14:49 +04:00
|
|
|
if (!bvs)
|
|
|
|
return;
|
|
|
|
|
2009-09-09 02:02:49 +04:00
|
|
|
let newZoomLevel = BrowserView.Util.clampZoomLevel(zoomLevel);
|
|
|
|
if (newZoomLevel != bvs.zoomLevel) {
|
2009-07-18 04:14:49 +04:00
|
|
|
let browserW = this.viewportToBrowser(bvs.viewportRect.right);
|
|
|
|
let browserH = this.viewportToBrowser(bvs.viewportRect.bottom);
|
2009-09-09 02:02:49 +04:00
|
|
|
bvs.zoomLevel = newZoomLevel; // side-effect: now scale factor in transformations is newZoomLevel
|
2009-11-25 00:59:35 +03:00
|
|
|
bvs.viewportRect.right = this.browserToViewport(browserW);
|
|
|
|
bvs.viewportRect.bottom = this.browserToViewport(browserH);
|
|
|
|
this._viewportChanged(true, true);
|
2009-11-24 10:46:01 +03:00
|
|
|
|
|
|
|
if (this._browser) {
|
|
|
|
let event = document.createEvent("Events");
|
|
|
|
event.initEvent("ZoomChanged", true, false);
|
|
|
|
this._browser.dispatchEvent(event);
|
|
|
|
}
|
2009-07-18 04:14:49 +04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
getZoomLevel: function getZoomLevel() {
|
|
|
|
let bvs = this._browserViewportState;
|
|
|
|
if (!bvs)
|
|
|
|
return undefined;
|
|
|
|
|
|
|
|
return bvs.zoomLevel;
|
|
|
|
},
|
|
|
|
|
2010-01-11 23:43:47 +03:00
|
|
|
beginOffscreenOperation: function beginOffscreenOperation(rect) {
|
2009-10-22 03:11:38 +04:00
|
|
|
if (this._offscreenDepth == 0) {
|
|
|
|
let vis = this.getVisibleRect();
|
2010-01-11 23:43:47 +03:00
|
|
|
rect = rect || vis;
|
|
|
|
let zoomRatio = vis.width / rect.width;
|
|
|
|
let viewBuffer = Elements.viewBuffer;
|
|
|
|
viewBuffer.width = vis.width;
|
|
|
|
viewBuffer.height = vis.height;
|
|
|
|
|
|
|
|
this._tileManager.renderRectToCanvas(rect, viewBuffer, zoomRatio, zoomRatio, false);
|
|
|
|
viewBuffer.style.display = "block";
|
|
|
|
window.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
|
|
.getInterface(Ci.nsIDOMWindowUtils).processUpdates();
|
2009-10-22 03:11:38 +04:00
|
|
|
this.pauseRendering();
|
|
|
|
}
|
|
|
|
this._offscreenDepth++;
|
|
|
|
},
|
|
|
|
|
|
|
|
commitOffscreenOperation: function commitOffscreenOperation() {
|
|
|
|
this._offscreenDepth--;
|
|
|
|
if (this._offscreenDepth == 0) {
|
|
|
|
this.resumeRendering();
|
2010-01-11 23:43:47 +03:00
|
|
|
Elements.viewBuffer.style.display = "none";
|
2009-10-22 03:11:38 +04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
beginBatchOperation: function beginBatchOperation() {
|
|
|
|
this._batchOps.push(BrowserView.Util.getNewBatchOperationState());
|
|
|
|
this.pauseRendering();
|
|
|
|
},
|
|
|
|
|
|
|
|
commitBatchOperation: function commitBatchOperation() {
|
|
|
|
let bops = this._batchOps;
|
|
|
|
if (bops.length == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let opState = bops.pop();
|
2009-10-30 01:55:56 +03:00
|
|
|
|
|
|
|
// XXX If stack is not empty, this just assigns opState variables to the next one
|
|
|
|
// on top. Why then have a stack of these booleans?
|
2009-07-18 04:14:49 +04:00
|
|
|
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();
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Calls to this function need to be one-to-one with calls to
|
|
|
|
* resumeRendering()
|
|
|
|
*/
|
|
|
|
pauseRendering: function pauseRendering() {
|
|
|
|
this._renderMode++;
|
2009-11-24 10:46:01 +03:00
|
|
|
if (this._renderMode == 1 && this._browser) {
|
|
|
|
let event = document.createEvent("Events");
|
|
|
|
event.initEvent("RenderStateChanged", true, false);
|
|
|
|
event.isRendering = false;
|
|
|
|
this._browser.dispatchEvent(event);
|
|
|
|
}
|
2009-07-18 04:14:49 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
2009-08-08 05:08:06 +04:00
|
|
|
this.renderNow();
|
2009-10-10 07:03:14 +04:00
|
|
|
|
2009-12-10 21:17:39 +03:00
|
|
|
if (this._renderMode == 0 && this._browser) {
|
|
|
|
let event = document.createEvent("Events");
|
|
|
|
event.initEvent("RenderStateChanged", true, false);
|
|
|
|
event.isRendering = true;
|
|
|
|
this._browser.dispatchEvent(event);
|
2009-11-24 10:46:01 +03:00
|
|
|
}
|
2009-08-08 05:08:06 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Called while rendering is paused to allow update of critical area
|
|
|
|
*/
|
|
|
|
renderNow: function renderNow() {
|
|
|
|
this._tileManager.criticalRectPaint();
|
2009-07-18 04:14:49 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
isRendering: function isRendering() {
|
|
|
|
return (this._renderMode == 0);
|
|
|
|
},
|
|
|
|
|
2009-08-11 05:03:42 +04:00
|
|
|
onAfterVisibleMove: function onAfterVisibleMove() {
|
2009-07-18 04:14:49 +04:00
|
|
|
let vs = this._browserViewportState;
|
2009-08-11 05:03:42 +04:00
|
|
|
let vr = this.getVisibleRect();
|
2009-07-18 04:14:49 +04:00
|
|
|
|
|
|
|
vs.visibleX = vr.left;
|
|
|
|
vs.visibleY = vr.top;
|
|
|
|
|
|
|
|
let cr = BrowserView.Util.visibleRectToCriticalRect(vr, vs);
|
|
|
|
|
2010-01-15 00:35:08 +03:00
|
|
|
this._tileManager.criticalMove(cr, this.isRendering());
|
2009-07-18 04:14:49 +04:00
|
|
|
},
|
|
|
|
|
2009-08-12 00:51:13 +04:00
|
|
|
/**
|
|
|
|
* Swap out the current browser and browser viewport state with a new pair.
|
|
|
|
*/
|
2009-11-25 22:07:00 +03:00
|
|
|
setBrowser: function setBrowser(browser, browserViewportState) {
|
2009-08-12 00:51:13 +04:00
|
|
|
if (browser && !browserViewportState) {
|
|
|
|
throw "Cannot set non-null browser with null BrowserViewportState";
|
|
|
|
}
|
|
|
|
|
2009-09-09 02:02:49 +04:00
|
|
|
let oldBrowser = this._browser;
|
|
|
|
let browserChanged = (oldBrowser !== browser);
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-09-09 02:02:49 +04:00
|
|
|
if (oldBrowser) {
|
|
|
|
oldBrowser.removeEventListener("MozAfterPaint", this.handleMozAfterPaint, false);
|
|
|
|
oldBrowser.removeEventListener("scroll", this.handlePageScroll, false);
|
|
|
|
oldBrowser.setAttribute("type", "content");
|
|
|
|
oldBrowser.docShell.isOffScreenBrowser = false;
|
2009-07-18 04:14:49 +04:00
|
|
|
}
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-08-12 00:51:13 +04:00
|
|
|
this._browser = browser;
|
|
|
|
this._contentWindow = (browser) ? browser.contentWindow : null;
|
|
|
|
this._browserViewportState = browserViewportState;
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
if (browser) {
|
2009-07-18 03:17:57 +04:00
|
|
|
browser.setAttribute("type", "content-primary");
|
|
|
|
|
|
|
|
this.beginBatchOperation();
|
|
|
|
|
|
|
|
browser.addEventListener("MozAfterPaint", this.handleMozAfterPaint, false);
|
2009-08-30 10:37:06 +04:00
|
|
|
browser.addEventListener("scroll", this.handlePageScroll, false);
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-11-12 08:44:27 +03:00
|
|
|
browser.docShell.isOffScreenBrowser = true;
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-11-25 00:59:35 +03:00
|
|
|
if (browserChanged)
|
|
|
|
this._viewportChanged(true, true);
|
2009-07-18 03:17:57 +04:00
|
|
|
|
|
|
|
this.commitBatchOperation();
|
2009-07-18 04:14:49 +04:00
|
|
|
}
|
|
|
|
},
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-25 02:51:16 +04:00
|
|
|
getBrowser: function getBrowser() {
|
|
|
|
return this._browser;
|
|
|
|
},
|
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
handleMozAfterPaint: function handleMozAfterPaint(ev) {
|
|
|
|
let browser = this._browser;
|
|
|
|
let tm = this._tileManager;
|
|
|
|
let vs = this._browserViewportState;
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-10-15 23:50:32 +04:00
|
|
|
let { x: scrollX, y: scrollY } = BrowserView.Util.getContentScrollOffset(browser);
|
2009-07-18 04:14:49 +04:00
|
|
|
let clientRects = ev.clientRects;
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
let rects = [];
|
|
|
|
// loop backwards to avoid xpconnect penalty for .length
|
|
|
|
for (let i = clientRects.length - 1; i >= 0; --i) {
|
|
|
|
let e = clientRects.item(i);
|
2009-10-15 23:50:32 +04:00
|
|
|
let r = new Rect(e.left + scrollX,
|
|
|
|
e.top + scrollY,
|
|
|
|
e.width, e.height);
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-10-15 23:50:32 +04:00
|
|
|
r = this.browserToViewportRect(r);
|
|
|
|
r.expandToIntegers();
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
if (r.right < 0 || r.bottom < 0)
|
|
|
|
continue;
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-10-15 23:50:32 +04:00
|
|
|
r.restrictTo(vs.viewportRect);
|
|
|
|
if (!r.isEmpty())
|
2009-07-18 06:30:57 +04:00
|
|
|
rects.push(r);
|
2009-07-18 04:14:49 +04:00
|
|
|
}
|
|
|
|
|
2010-02-19 04:58:24 +03:00
|
|
|
tm.dirtyRects(rects, this.isRendering(), true);
|
2009-07-18 04:14:49 +04:00
|
|
|
},
|
|
|
|
|
2009-09-22 17:33:30 +04:00
|
|
|
/** If browser scrolls, pan content to new scroll area. */
|
2009-08-30 10:37:06 +04:00
|
|
|
handlePageScroll: function handlePageScroll(aEvent) {
|
2009-12-11 22:27:23 +03:00
|
|
|
if (aEvent.target != this._browser.contentDocument || this._ignorePageScroll)
|
2009-08-30 10:37:06 +04:00
|
|
|
return;
|
2009-12-10 20:55:27 +03:00
|
|
|
// XXX shouldn't really make calls to Browser
|
|
|
|
Browser.scrollContentToBrowser();
|
2009-08-30 10:37:06 +04:00
|
|
|
},
|
|
|
|
|
2009-12-11 22:27:23 +03:00
|
|
|
_ignorePageScroll: false,
|
|
|
|
ignorePageScroll: function ignorePageScroll(aIgnoreScroll) {
|
|
|
|
this._ignorePageScroll = aIgnoreScroll;
|
|
|
|
},
|
|
|
|
|
2009-09-09 02:02:49 +04:00
|
|
|
handleMozScrolledAreaChanged: function handleMozScrolledAreaChanged(ev) {
|
2010-01-15 00:08:40 +03:00
|
|
|
let tab = Browser.getTabForDocument(ev.originalTarget) ||
|
2009-12-15 22:59:01 +03:00
|
|
|
(ev.target.contentDocument && Browser.getTabForDocument(ev.target.contentDocument));
|
2009-11-25 00:59:35 +03:00
|
|
|
if (!tab)
|
2009-09-09 02:02:49 +04:00
|
|
|
return;
|
|
|
|
|
2009-11-25 00:59:35 +03:00
|
|
|
let browser = tab.browser;
|
|
|
|
let bvs = tab.browserViewportState;
|
|
|
|
let { x: scrollX, y: scrollY } = BrowserView.Util.getContentScrollOffset(browser);
|
2009-09-09 02:02:49 +04:00
|
|
|
let x = ev.x + scrollX;
|
|
|
|
let y = ev.y + scrollY;
|
|
|
|
let w = ev.width;
|
|
|
|
let h = ev.height;
|
2009-10-21 22:05:55 +04:00
|
|
|
|
2009-09-09 02:02:49 +04:00
|
|
|
// Adjust width and height from the incoming event properties so that we
|
|
|
|
// ignore changes to width and height contributed by growth in page
|
|
|
|
// quadrants other than x > 0 && y > 0.
|
|
|
|
if (x < 0) w += x;
|
|
|
|
if (y < 0) h += y;
|
|
|
|
|
2009-12-10 20:55:27 +03:00
|
|
|
let vis = this.getVisibleRect();
|
2009-11-25 00:59:35 +03:00
|
|
|
let viewport = bvs.viewportRect;
|
|
|
|
let oldRight = viewport.right;
|
|
|
|
let oldBottom = viewport.bottom;
|
|
|
|
viewport.right = bvs.zoomLevel * w;
|
|
|
|
viewport.bottom = bvs.zoomLevel * h;
|
|
|
|
|
|
|
|
if (browser == this._browser) {
|
|
|
|
// Page has now loaded enough to allow zooming.
|
|
|
|
let sizeChanged = oldRight != viewport.right || oldBottom != viewport.bottom;
|
|
|
|
this._viewportChanged(sizeChanged, false);
|
2009-11-25 22:07:00 +03:00
|
|
|
this.updateDefaultZoom();
|
2009-12-10 20:55:27 +03:00
|
|
|
if (vis.right > viewport.right || vis.bottom > viewport.bottom) {
|
|
|
|
// Content has shrunk outside of the visible rectangle.
|
|
|
|
// XXX for some reason scroller doesn't know it is outside its bounds
|
|
|
|
Browser.contentScrollboxScroller.scrollBy(0, 0);
|
|
|
|
this.onAfterVisibleMove();
|
|
|
|
}
|
2009-11-25 00:59:35 +03:00
|
|
|
}
|
2009-07-18 04:14:49 +04:00
|
|
|
},
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-11-25 22:07:00 +03:00
|
|
|
/** Call when default zoomToPage value may change. */
|
|
|
|
updateDefaultZoom: function updateDefaultZoom() {
|
|
|
|
let bvs = this._browserViewportState;
|
|
|
|
if (!bvs)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
let isDefault = (bvs.zoomLevel == bvs.defaultZoomLevel);
|
|
|
|
bvs.defaultZoomLevel = this.getZoomForPage();
|
|
|
|
if (isDefault)
|
|
|
|
this.setZoomLevel(bvs.defaultZoomLevel);
|
|
|
|
return isDefault;
|
|
|
|
},
|
|
|
|
|
|
|
|
isDefaultZoom: function isDefaultZoom() {
|
|
|
|
let bvs = this._browserViewportState;
|
|
|
|
if (!bvs)
|
|
|
|
return true;
|
|
|
|
return bvs.zoomLevel == bvs.defaultZoomLevel;
|
|
|
|
},
|
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
zoomToPage: function zoomToPage() {
|
2009-11-25 22:07:00 +03:00
|
|
|
let bvs = this._browserViewportState;
|
|
|
|
if (bvs) {
|
2009-09-09 02:02:49 +04:00
|
|
|
this.setZoomLevel(this.getZoomForPage());
|
2009-11-25 22:07:00 +03:00
|
|
|
bvs.defaultZoomLevel = bvs.zoomLevel; // make sure default zl is up to date
|
2009-11-24 22:44:05 +03:00
|
|
|
}
|
2009-10-22 03:11:38 +04:00
|
|
|
},
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-10-22 03:11:38 +04:00
|
|
|
getZoomForPage: function getZoomForPage() {
|
|
|
|
let browser = this._browser;
|
2009-07-18 04:14:49 +04:00
|
|
|
if (!browser)
|
2009-10-22 03:11:38 +04:00
|
|
|
return 0;
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-11-04 00:20:03 +03:00
|
|
|
let metaData = Util.contentIsHandheld(browser);
|
|
|
|
if (metaData.reason == "handheld" || metaData.reason == "doctype")
|
2009-10-22 03:11:38 +04:00
|
|
|
return 1;
|
2009-11-04 00:20:03 +03:00
|
|
|
else if (metaData.reason == "viewport" && metaData.scale > 0)
|
|
|
|
return metaData.scale;
|
2009-10-29 07:37:58 +03:00
|
|
|
|
2009-09-09 02:02:49 +04:00
|
|
|
let bvs = this._browserViewportState; // browser exists, so bvs must as well
|
|
|
|
let w = this.viewportToBrowser(bvs.viewportRect.right);
|
|
|
|
let h = this.viewportToBrowser(bvs.viewportRect.bottom);
|
2009-10-29 07:37:58 +03:00
|
|
|
return BrowserView.Util.pageZoomLevel(this.getVisibleRect(), w, h);
|
2009-07-18 04:14:49 +04:00
|
|
|
},
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
zoom: function zoom(aDirection) {
|
2009-07-25 02:51:16 +04:00
|
|
|
let bvs = this._browserViewportState;
|
|
|
|
if (!bvs)
|
|
|
|
throw "No browser is set";
|
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
if (aDirection == 0)
|
|
|
|
return;
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
var zoomDelta = 0.05; // 1/20
|
|
|
|
if (aDirection >= 0)
|
|
|
|
zoomDelta *= -1;
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-25 02:51:16 +04:00
|
|
|
this.setZoomLevel(bvs.zoomLevel + zoomDelta);
|
2009-07-18 04:14:49 +04:00
|
|
|
},
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-09-09 02:02:49 +04:00
|
|
|
//
|
|
|
|
// MozAfterPaint events do not guarantee to inform us of all
|
|
|
|
// invalidated paints (See
|
|
|
|
// https://developer.mozilla.org/en/Gecko-Specific_DOM_Events#Important_notes
|
|
|
|
// for details on what the event *does* guarantee). This is only an
|
|
|
|
// issue when the same current <browser> is used to navigate to a
|
|
|
|
// new page. Unless a zoom was issued during the page transition
|
|
|
|
// (e.g. a call to zoomToPage() or something of that nature), we
|
|
|
|
// aren't guaranteed that we've actually invalidated the entire
|
|
|
|
// page. We don't want to leave bits of the previous page in the
|
|
|
|
// view of the new one, so this method exists as a way for Browser
|
|
|
|
// to inform us that the page is changing, and that we really ought
|
|
|
|
// to invalidate everything. Ideally, we wouldn't have to rely on
|
|
|
|
// this being called, and we would get proper invalidates for the
|
|
|
|
// whole page no matter what is or is not visible.
|
|
|
|
//
|
|
|
|
// Note that calling this function isn't necessary in almost all
|
|
|
|
// cases, but should be done for correctness. Most of the time, one
|
|
|
|
// of the following two conditions is satisfied. Either
|
|
|
|
//
|
|
|
|
// (1) Pages have different widths so the Browser calls a
|
|
|
|
// zoomToPage() which forces a dirtyAll, or
|
|
|
|
// (2) MozAfterPaint does indeed inform us of dirtyRects covering
|
|
|
|
// the entire page (everything that could possibly become
|
|
|
|
// visible).
|
|
|
|
/**
|
|
|
|
* Invalidates the entire page by throwing away any cached graphical
|
|
|
|
* portions of the view and refusing to allow a zoomToPage() until
|
|
|
|
* the next explicit update of the viewport dimensions.
|
|
|
|
*
|
|
|
|
* This method should be called when the <browser> last set by
|
|
|
|
* setBrowser() is about to navigate to a new page.
|
|
|
|
*/
|
|
|
|
invalidateEntireView: function invalidateEntireView() {
|
|
|
|
if (this._browserViewportState) {
|
2009-11-25 00:59:35 +03:00
|
|
|
this._viewportChanged(false, true);
|
2009-09-09 02:02:49 +04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2009-08-22 22:06:51 +04:00
|
|
|
/**
|
|
|
|
* Render a rectangle within the browser viewport to the destination canvas
|
|
|
|
* under the given scale.
|
|
|
|
*
|
|
|
|
* @param destCanvas The destination canvas into which the image is rendered.
|
|
|
|
* @param destWidth Destination width
|
|
|
|
* @param destHeight Destination height
|
|
|
|
* @param srcRect [optional] The source rectangle in BrowserView coordinates.
|
|
|
|
* This defaults to the visible rect rooted at the x,y of the critical rect.
|
|
|
|
*/
|
|
|
|
renderToCanvas: function renderToCanvas(destCanvas, destWidth, destHeight, srcRect) {
|
|
|
|
let bvs = this._browserViewportState;
|
|
|
|
if (!bvs) {
|
|
|
|
throw "Browser viewport state null in call to renderToCanvas (probably no browser set on BrowserView).";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!srcRect) {
|
|
|
|
let vr = this.getVisibleRect();
|
2009-11-30 22:32:25 +03:00
|
|
|
vr.x = bvs.viewportRect.left;
|
|
|
|
vr.y = bvs.viewportRect.top;
|
2009-08-22 22:06:51 +04:00
|
|
|
srcRect = vr;
|
|
|
|
}
|
|
|
|
|
|
|
|
let scalex = (destWidth / srcRect.width) || 1;
|
|
|
|
let scaley = (destHeight / srcRect.height) || 1;
|
|
|
|
|
|
|
|
srcRect.restrictTo(bvs.viewportRect);
|
|
|
|
this._tileManager.renderRectToCanvas(srcRect, destCanvas, scalex, scaley);
|
|
|
|
},
|
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
viewportToBrowser: function viewportToBrowser(x) {
|
|
|
|
let bvs = this._browserViewportState;
|
|
|
|
if (!bvs)
|
|
|
|
throw "No browser is set";
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
return x / bvs.zoomLevel;
|
|
|
|
},
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
browserToViewport: function browserToViewport(x) {
|
|
|
|
let bvs = this._browserViewportState;
|
|
|
|
if (!bvs)
|
|
|
|
throw "No browser is set";
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
return x * bvs.zoomLevel;
|
|
|
|
},
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
viewportToBrowserRect: function viewportToBrowserRect(rect) {
|
|
|
|
let f = this.viewportToBrowser(1.0);
|
|
|
|
return rect.scale(f, f);
|
|
|
|
},
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
browserToViewportRect: function browserToViewportRect(rect) {
|
|
|
|
let f = this.browserToViewport(1.0);
|
|
|
|
return rect.scale(f, f);
|
|
|
|
},
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
browserToViewportCanvasContext: function browserToViewportCanvasContext(ctx) {
|
|
|
|
let f = this.browserToViewport(1.0);
|
|
|
|
ctx.scale(f, f);
|
|
|
|
},
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-08-01 02:32:48 +04:00
|
|
|
forceContainerResize: function forceContainerResize() {
|
|
|
|
let bvs = this._browserViewportState;
|
|
|
|
if (bvs)
|
|
|
|
BrowserView.Util.resizeContainerToViewport(this._container, bvs.viewportRect);
|
|
|
|
},
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-10-30 01:55:56 +03:00
|
|
|
/**
|
|
|
|
* Force any pending viewport changes to occur. Batch operations will still be on the
|
|
|
|
* stack so commitBatchOperation is still necessary afterwards.
|
|
|
|
*/
|
|
|
|
forceViewportChange: function forceViewportChange() {
|
|
|
|
let bops = this._batchOps;
|
|
|
|
if (bops.length > 0) {
|
|
|
|
let opState = bops[bops.length - 1];
|
|
|
|
this._applyViewportChanges(opState.viewportSizeChanged, opState.dirtyAll);
|
|
|
|
opState.viewportSizeChanged = false;
|
|
|
|
opState.dirtyAll = false;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
// -----------------------------------------------------------
|
|
|
|
// Private instance methods
|
|
|
|
//
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
_viewportChanged: function _viewportChanged(viewportSizeChanged, dirtyAll) {
|
|
|
|
let bops = this._batchOps;
|
|
|
|
if (bops.length > 0) {
|
|
|
|
let opState = bops[bops.length - 1];
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
if (viewportSizeChanged)
|
|
|
|
opState.viewportSizeChanged = viewportSizeChanged;
|
|
|
|
if (dirtyAll)
|
|
|
|
opState.dirtyAll = dirtyAll;
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
return;
|
|
|
|
}
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-10-30 01:55:56 +03:00
|
|
|
this._applyViewportChanges(viewportSizeChanged, dirtyAll);
|
|
|
|
},
|
|
|
|
|
|
|
|
_applyViewportChanges: function _applyViewportChanges(viewportSizeChanged, dirtyAll) {
|
2009-07-18 04:14:49 +04:00
|
|
|
let bvs = this._browserViewportState;
|
|
|
|
if (bvs) {
|
|
|
|
BrowserView.Util.resizeContainerToViewport(this._container, bvs.viewportRect);
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 07:05:07 +04:00
|
|
|
if (dirtyAll) {
|
|
|
|
// We're about to mark the entire viewport dirty, so we can clear any
|
|
|
|
// queued afterPaint events that will cause redundant draws
|
|
|
|
BrowserView.Util.getBrowserDOMWindowUtils(this._browser).clearMozAfterPaintEvents();
|
|
|
|
}
|
|
|
|
|
2009-12-08 20:44:57 +03:00
|
|
|
let vr = this.getVisibleRect();
|
2009-07-18 04:14:49 +04:00
|
|
|
this._tileManager.viewportChangeHandler(bvs.viewportRect,
|
2009-12-08 20:44:57 +03:00
|
|
|
BrowserView.Util.visibleRectToCriticalRect(vr, bvs),
|
2009-07-18 04:14:49 +04:00
|
|
|
viewportSizeChanged,
|
|
|
|
dirtyAll);
|
2009-12-08 20:44:57 +03:00
|
|
|
|
|
|
|
let rects = vr.subtract(bvs.viewportRect);
|
|
|
|
this._tileManager.clearRects(rects);
|
2009-07-18 04:14:49 +04:00
|
|
|
}
|
|
|
|
},
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
_appendTile: function _appendTile(tile) {
|
|
|
|
let canvas = tile.getContentImage();
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 12:16:04 +04:00
|
|
|
//canvas.style.position = "absolute";
|
|
|
|
//canvas.style.left = tile.x + "px";
|
|
|
|
//canvas.style.top = tile.y + "px";
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
// XXX The above causes a trace abort, and this function is called back in the tight
|
|
|
|
// render-heavy loop in TileManager, so even though what we do below isn't so proper
|
|
|
|
// and takes longer on the Platform/C++ emd, it's better than causing a trace abort
|
2009-07-18 07:05:07 +04:00
|
|
|
// in our tight loop.
|
|
|
|
//
|
|
|
|
// But this also overwrites some style already set on the canvas in Tile constructor.
|
|
|
|
// Hack fail...
|
|
|
|
//
|
2009-07-18 12:16:04 +04:00
|
|
|
canvas.setAttribute("style", "position: absolute; left: " + tile.boundRect.left + "px; " + "top: " + tile.boundRect.top + "px;");
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
this._container.appendChild(canvas);
|
|
|
|
},
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
_removeTile: function _removeTile(tile) {
|
|
|
|
let canvas = tile.getContentImage();
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
this._container.removeChild(canvas);
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
2009-07-18 03:17:57 +04:00
|
|
|
|
|
|
|
|
|
|
|
// -----------------------------------------------------------
|
|
|
|
// Helper structures
|
|
|
|
//
|
|
|
|
|
2009-07-18 04:14:49 +04:00
|
|
|
/**
|
|
|
|
* A BrowserViewportState maintains viewport state information that is unique to each
|
|
|
|
* browser. It does not hold *all* viewport state maintained by BrowserView. For
|
|
|
|
* instance, it does not maintain width and height of the visible rectangle (but it
|
|
|
|
* does keep the top and left coordinates (cf visibleX, visibleY)), since those are not
|
|
|
|
* characteristic of the current browser in view.
|
|
|
|
*/
|
2009-11-24 22:44:05 +03:00
|
|
|
BrowserView.BrowserViewportState = function(viewportRect, visibleX, visibleY, zoomLevel) {
|
2009-07-18 03:17:57 +04:00
|
|
|
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;
|
2009-11-24 22:44:05 +03:00
|
|
|
this.defaultZoomLevel = 1;
|
2009-07-18 03:17:57 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
toString: function toString() {
|
2009-11-24 22:44:05 +03:00
|
|
|
let props = ["\tviewportRect=" + this.viewportRect.toString(),
|
|
|
|
"\tvisibleX=" + this.visibleX,
|
|
|
|
"\tvisibleY=" + this.visibleY,
|
|
|
|
"\tzoomLevel=" + this.zoomLevel];
|
2009-07-18 03:17:57 +04:00
|
|
|
|
2009-11-24 22:44:05 +03:00
|
|
|
return "[BrowserViewportState] {\n" + props.join(",\n") + "\n}";
|
2009-07-18 03:17:57 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2009-10-10 07:03:14 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* nsIObserver that implements a callback for the nsIIdleService, which starts
|
|
|
|
* and stops the BrowserView's TileManager's prefetch crawl according to user
|
|
|
|
* idleness.
|
|
|
|
*/
|
|
|
|
BrowserView.IdleServiceObserver = function IdleServiceObserver(browserView) {
|
|
|
|
this._browserView = browserView;
|
2010-01-12 21:10:17 +03:00
|
|
|
this._idle = false;
|
|
|
|
this._paused = false;
|
2009-10-10 07:03:14 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
BrowserView.IdleServiceObserver.prototype = {
|
2010-01-12 21:10:17 +03:00
|
|
|
/** No matter what idle is, make sure prefetching is not active. */
|
|
|
|
pause: function pause() {
|
|
|
|
this._paused = true;
|
|
|
|
this._updateTileManager();
|
2009-10-10 07:03:14 +04:00
|
|
|
},
|
|
|
|
|
2010-01-12 21:10:17 +03:00
|
|
|
/** Prefetch tiles in idle mode. */
|
|
|
|
resume: function resume() {
|
|
|
|
this._paused = false;
|
|
|
|
this._updateTileManager();
|
2009-10-10 07:03:14 +04:00
|
|
|
},
|
|
|
|
|
2009-12-10 21:17:39 +03:00
|
|
|
/** Idle event handler. */
|
|
|
|
observe: function observe(aSubject, aTopic, aUserIdleTime) {
|
2010-01-12 21:10:17 +03:00
|
|
|
this._idle = (aTopic == "idle") ? true : false;
|
|
|
|
this._updateTileManager();
|
2009-12-10 21:17:39 +03:00
|
|
|
},
|
2010-01-12 21:10:17 +03:00
|
|
|
|
|
|
|
_updateTileManager: function _updateTileManager() {
|
|
|
|
let bv = this._browserView;
|
|
|
|
bv._tileManager.setPrefetch(this._idle && !this._paused);
|
|
|
|
}
|
2009-10-10 07:03:14 +04:00
|
|
|
};
|