%findBarDTD; ]> null [] null null null Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService); null null 0 0 0 1 1 binding. this.removeEventListener("pageshow", this.onPageShow, true); this.removeEventListener("pagehide", this.onPageHide, true); this.removeEventListener("DOMPopupBlocked", this.onPopupBlocked, true); ]]> false null true ({}) = this.kDieTime) this._die(); else // This doesn't need to be exact, just be sure to clean up at some point. this._timeout = setTimeout(this._dieIfOld.bind(this), this.kDieTime); }, /** Cleanup after ourselves. */ _die: function() { let timeout = this._timeout; if (timeout) { clearTimeout(timeout); this._timeout = null; } if (this._contentView && Math.abs(this._pixelsPannedSinceRefresh) > 0) this._updateCacheViewport(); // We expect contentViews to contain our ID. If not, something bad // happened. delete this.self._contentViews[this._id]; }, /** * Given the cache size and the viewport size, this determines where the cache * should start relative to the scroll position. This adjusts the position based * on which direction the user is panning, so that we use our cache as * effectively as possible. * * @param aDirection Negative means user is panning to the left or above * Zero means user did not pan * Positive means user is panning to the right or below * @param aViewportSize The width or height of the viewport * @param aCacheSize The width or height of the displayport */ _getRelativeCacheStart: function(aDirection, aViewportSize, aCacheSize) { // Remember that this is relative to the viewport scroll position. // Let's assume we are thinking about the y-axis. // The extreme cases: // |0| would mean that there is no content available above // |aViewportSize - aCacheSize| would mean no content available below // // Taking the average of the extremes puts equal amounts of cache on the // top and bottom of the viewport. If we think of this like a weighted // average, .5 is the sweet spot where equals amounts of content are // above and below the visible area. // // This weight is therefore how much of the cache is above (or to the // left) the visible area. let cachedAbove = .5; // If panning down, leave only 25% of the non-visible cache above. if (aDirection > 0) cachedAbove = .25; // If panning up, Leave 75% of the non-visible cache above. if (aDirection < 0) cachedAbove = .75; return (aViewportSize - aCacheSize) * cachedAbove; }, /** Determine size of the pixel cache. */ _getCacheSize: function(viewportSize) { let self = this.self; let contentView = this._contentView; let cacheWidth = self._cacheRatioWidth * viewportSize.width; let cacheHeight = self._cacheRatioHeight * viewportSize.height; let contentSize = this._getContentSize(); let contentWidth = contentSize.width; let contentHeight = contentSize.height; // There are common cases, such as long skinny pages, where our cache size is // bigger than our content size. In those cases, we take that sliver of leftover // space and apply it to the other dimension. if (contentWidth < cacheWidth) { cacheHeight += (cacheWidth - contentWidth) * cacheHeight / cacheWidth; cacheWidth = contentWidth; } else if (contentHeight < cacheHeight) { cacheWidth += (cacheHeight - contentHeight) * cacheWidth / cacheHeight; cacheHeight = contentHeight; } return { width: cacheWidth, height: cacheHeight }; }, _sendDisplayportUpdate: function(scrollX, scrollY) { let self = this.self; if (!self.active) return; let contentView = this._contentView; let viewportSize = this._getViewportSize(); let cacheSize = this._getCacheSize(viewportSize); let cacheX = this._getRelativeCacheStart(this._pixelsPannedSinceRefresh.x, viewportSize.width, cacheSize.width) + contentView.scrollX; let cacheY = this._getRelativeCacheStart(this._pixelsPannedSinceRefresh.y, viewportSize.height, cacheSize.height) + contentView.scrollY; let contentSize = this._getContentSize(); // Use our pixels efficiently and don't try to cache things outside of content // boundaries (The left bound can be negative because of RTL). let rootScale = self.scale; let leftBound = self._contentDocumentLeft * rootScale; let bounds = new Rect(leftBound, 0, contentSize.width, contentSize.height); let displayport = new Rect(cacheX, cacheY, cacheSize.width, cacheSize.height); displayport.translateInside(bounds); self.messageManager.sendAsyncMessage("Content:SetCacheViewport", { scrollX: Math.round(scrollX) / rootScale, scrollY: Math.round(scrollY) / rootScale, x: Math.round(displayport.x) / rootScale, y: Math.round(displayport.y) / rootScale, w: Math.round(displayport.width) / rootScale, h: Math.round(displayport.height) / rootScale, scale: rootScale, id: contentView.id }); this._pixelsPannedSinceRefresh.x = 0; this._pixelsPannedSinceRefresh.y = 0; }, _updateCSSViewport: function() { let contentView = this._contentView; this._sendDisplayportUpdate(contentView.scrollX, contentView.scrollY); }, /** * The cache viewport is what parts of content is cached in the parent process for * fast scrolling. This syncs that up with the current projection viewport. */ _updateCacheViewport: function() { // Do not update scroll values for content. if (this.isRoot()) this._sendDisplayportUpdate(-1, -1); else { let contentView = this._contentView; this._sendDisplayportUpdate(contentView.scrollX, contentView.scrollY); } }, _getContentSize: function() { let self = this.self; return { width: this._contentView.contentWidth, height: this._contentView.contentHeight }; }, _getViewportSize: function() { let self = this.self; if (this.isRoot()) { let bcr = self.getBoundingClientRect(); return { width: bcr.width, height: bcr.height }; } else { return { width: this._contentView.viewportWidth, height: this._contentView.viewportHeight }; } }, init: function(contentView) { let self = this.self; this._contentView = contentView; this._id = contentView.id; this._scale = 1; self._contentViews[this._id] = this; if (!this.isRoot()) { // Non-root content views are short lived. this._timeout = setTimeout(this._dieIfOld.bind(this), this.kDieTime); // This iframe may not have a display port yet, so build up a cache // immediately. this._updateCacheViewport(); } }, isRoot: function() { return this.self._contentViewManager.rootContentView == this._contentView; }, scrollBy: function(x, y) { let self = this.self; // Bounding content rectangle is in device pixels let contentView = this._contentView; let viewportSize = this._getViewportSize(); let contentSize = this._getContentSize(); // Calculate document dimensions in device pixels let scrollRangeX = contentSize.width - viewportSize.width; let scrollRangeY = contentSize.height - viewportSize.height; let leftOffset = self._contentDocumentLeft * this._scale; x = Math.floor(Math.max(leftOffset, Math.min(scrollRangeX + leftOffset, contentView.scrollX + x))) - contentView.scrollX; y = Math.floor(Math.max(0, Math.min(scrollRangeY, contentView.scrollY + y))) - contentView.scrollY; if (x == 0 && y == 0) return; contentView.scrollBy(x, y); this._lastPanTime = Date.now(); this._pixelsPannedSinceRefresh.x += x; this._pixelsPannedSinceRefresh.y += y; if (Math.abs(this._pixelsPannedSinceRefresh.x) > 20 || Math.abs(this._pixelsPannedSinceRefresh.y) > 20) this._updateCacheViewport(); }, scrollTo: function(x, y) { let contentView = this._contentView; this.scrollBy(x - contentView.scrollX, y - contentView.scrollY); }, _setScale: function _setScale(scale) { this._scale = scale; this._contentView.setScale(scale, scale); }, getPosition: function() { let contentView = this._contentView; return { x: contentView.scrollX, y: contentView.scrollY }; } }) ]]>