michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: "use strict"; michael@0: michael@0: var ZoomHelper = { michael@0: zoomInAndSnapToRange: function(aRange) { michael@0: // aRange is always non-null here, since a check happened previously. michael@0: let viewport = BrowserApp.selectedTab.getViewport(); michael@0: let fudge = 15; // Add a bit of fudge. michael@0: let boundingElement = aRange.offsetNode; michael@0: while (!boundingElement.getBoundingClientRect && boundingElement.parentNode) { michael@0: boundingElement = boundingElement.parentNode; michael@0: } michael@0: michael@0: let rect = ElementTouchHelper.getBoundingContentRect(boundingElement); michael@0: let drRect = aRange.getClientRect(); michael@0: let scrollTop = michael@0: BrowserApp.selectedBrowser.contentDocument.documentElement.scrollTop || michael@0: BrowserApp.selectedBrowser.contentDocument.body.scrollTop; michael@0: michael@0: // We subtract half the height of the viewport so that we can (ideally) michael@0: // center the area of interest on the screen. michael@0: let topPos = scrollTop + drRect.top - (viewport.cssHeight / 2.0); michael@0: michael@0: // Factor in the border and padding michael@0: let boundingStyle = window.getComputedStyle(boundingElement); michael@0: let leftAdjustment = parseInt(boundingStyle.paddingLeft) + michael@0: parseInt(boundingStyle.borderLeftWidth); michael@0: michael@0: BrowserApp.selectedTab._mReflozPositioned = true; michael@0: michael@0: rect.type = "Browser:ZoomToRect"; michael@0: rect.x = Math.max(viewport.cssPageLeft, rect.x - fudge + leftAdjustment); michael@0: rect.y = Math.max(topPos, viewport.cssPageTop); michael@0: rect.w = viewport.cssWidth; michael@0: rect.h = viewport.cssHeight; michael@0: rect.animate = false; michael@0: michael@0: sendMessageToJava(rect); michael@0: BrowserApp.selectedTab._mReflozPoint = null; michael@0: }, michael@0: michael@0: zoomOut: function() { michael@0: BrowserEventHandler.resetMaxLineBoxWidth(); michael@0: sendMessageToJava({ type: "Browser:ZoomToPageWidth" }); michael@0: }, michael@0: michael@0: isRectZoomedIn: function(aRect, aViewport) { michael@0: // This function checks to see if the area of the rect visible in the michael@0: // viewport (i.e. the "overlapArea" variable below) is approximately michael@0: // the max area of the rect we can show. It also checks that the rect michael@0: // is actually on-screen by testing the left and right edges of the rect. michael@0: // In effect, this tells us whether or not zooming in to this rect michael@0: // will significantly change what the user is seeing. michael@0: const minDifference = -20; michael@0: const maxDifference = 20; michael@0: const maxZoomAllowed = 4; // keep this in sync with mobile/android/base/ui/PanZoomController.MAX_ZOOM michael@0: michael@0: let vRect = new Rect(aViewport.cssX, aViewport.cssY, aViewport.cssWidth, aViewport.cssHeight); michael@0: let overlap = vRect.intersect(aRect); michael@0: let overlapArea = overlap.width * overlap.height; michael@0: let availHeight = Math.min(aRect.width * vRect.height / vRect.width, aRect.height); michael@0: let showing = overlapArea / (aRect.width * availHeight); michael@0: let dw = (aRect.width - vRect.width); michael@0: let dx = (aRect.x - vRect.x); michael@0: michael@0: if (fuzzyEquals(aViewport.zoom, maxZoomAllowed) && overlap.width / aRect.width > 0.9) { michael@0: // we're already at the max zoom and the block is not spilling off the side of the screen so that even michael@0: // if the block isn't taking up most of the viewport we can't pan/zoom in any more. return true so that we zoom out michael@0: return true; michael@0: } michael@0: michael@0: return (showing > 0.9 && michael@0: dx > minDifference && dx < maxDifference && michael@0: dw > minDifference && dw < maxDifference); michael@0: }, michael@0: michael@0: /* Zoom to an element, optionally keeping a particular part of it michael@0: * in view if it is really tall. michael@0: */ michael@0: zoomToElement: function(aElement, aClickY = -1, aCanZoomOut = true, aCanScrollHorizontally = true) { michael@0: let rect = ElementTouchHelper.getBoundingContentRect(aElement); michael@0: ZoomHelper.zoomToRect(rect, aClickY, aCanZoomOut, aCanScrollHorizontally, aElement); michael@0: }, michael@0: michael@0: zoomToRect: function(aRect, aClickY = -1, aCanZoomOut = true, aCanScrollHorizontally = true, aElement) { michael@0: const margin = 15; michael@0: michael@0: if(!aRect.h || !aRect.w) { michael@0: aRect.h = aRect.height; michael@0: aRect.w = aRect.width; michael@0: } michael@0: michael@0: let viewport = BrowserApp.selectedTab.getViewport(); michael@0: let bRect = new Rect(aCanScrollHorizontally ? Math.max(viewport.cssPageLeft, aRect.x - margin) : viewport.cssX, michael@0: aRect.y, michael@0: aCanScrollHorizontally ? aRect.w + 2 * margin : viewport.cssWidth, michael@0: aRect.h); michael@0: // constrict the rect to the screen's right edge michael@0: bRect.width = Math.min(bRect.width, viewport.cssPageRight - bRect.x); michael@0: michael@0: // if the rect is already taking up most of the visible area and is stretching the michael@0: // width of the page, then we want to zoom out instead. michael@0: if (aElement) { michael@0: if (BrowserEventHandler.mReflozPref) { michael@0: let zoomFactor = BrowserApp.selectedTab.getZoomToMinFontSize(aElement); michael@0: michael@0: bRect.width = zoomFactor <= 1.0 ? bRect.width : gScreenWidth / zoomFactor; michael@0: bRect.height = zoomFactor <= 1.0 ? bRect.height : bRect.height / zoomFactor; michael@0: if (zoomFactor == 1.0 || ZoomHelper.isRectZoomedIn(bRect, viewport)) { michael@0: if (aCanZoomOut) { michael@0: ZoomHelper.zoomOut(); michael@0: } michael@0: return; michael@0: } michael@0: } else if (ZoomHelper.isRectZoomedIn(bRect, viewport)) { michael@0: if (aCanZoomOut) { michael@0: ZoomHelper.zoomOut(); michael@0: } michael@0: return; michael@0: } michael@0: } michael@0: michael@0: let rect = {}; michael@0: michael@0: rect.type = "Browser:ZoomToRect"; michael@0: rect.x = bRect.x; michael@0: rect.y = bRect.y; michael@0: rect.w = bRect.width; michael@0: rect.h = Math.min(bRect.width * viewport.cssHeight / viewport.cssWidth, bRect.height); michael@0: michael@0: if (aClickY >= 0) { michael@0: // if the block we're zooming to is really tall, and we want to keep a particular michael@0: // part of it in view, then adjust the y-coordinate of the target rect accordingly. michael@0: // the 1.2 multiplier is just a little fuzz to compensate for bRect including horizontal michael@0: // margins but not vertical ones. michael@0: let cssTapY = viewport.cssY + aClickY; michael@0: if ((bRect.height > rect.h) && (cssTapY > rect.y + (rect.h * 1.2))) { michael@0: rect.y = cssTapY - (rect.h / 2); michael@0: } michael@0: } michael@0: michael@0: if (rect.w > viewport.cssWidth || rect.h > viewport.cssHeight) { michael@0: BrowserEventHandler.resetMaxLineBoxWidth(); michael@0: } michael@0: michael@0: sendMessageToJava(rect); michael@0: }, michael@0: };