michael@0: // -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- 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 michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: let Cc = Components.classes; michael@0: let Ci = Components.interfaces; michael@0: let Cu = Components.utils; michael@0: let Cr = Components.results; michael@0: michael@0: /** michael@0: * Misc. front end utilities for apzc management. michael@0: * the pref: layers.async-pan-zoom.enabled is true. michael@0: */ michael@0: michael@0: var APZCObserver = { michael@0: _debugEvents: false, michael@0: _enabled: false, michael@0: michael@0: get enabled() { michael@0: return this._enabled; michael@0: }, michael@0: michael@0: init: function() { michael@0: this._enabled = Services.prefs.getBoolPref(kAsyncPanZoomEnabled); michael@0: if (!this._enabled) { michael@0: return; michael@0: } michael@0: michael@0: let os = Services.obs; michael@0: os.addObserver(this, "apzc-transform-begin", false); michael@0: michael@0: Elements.tabList.addEventListener("TabSelect", this, true); michael@0: Elements.browsers.addEventListener("pageshow", this, true); michael@0: messageManager.addMessageListener("Content:ZoomToRect", this); michael@0: }, michael@0: michael@0: shutdown: function shutdown() { michael@0: if (!this._enabled) { michael@0: return; michael@0: } michael@0: michael@0: let os = Services.obs; michael@0: os.removeObserver(this, "apzc-transform-begin"); michael@0: michael@0: Elements.tabList.removeEventListener("TabSelect", this, true); michael@0: Elements.browsers.removeEventListener("pageshow", this, true); michael@0: messageManager.removeMessageListener("Content:ZoomToRect", this); michael@0: }, michael@0: michael@0: handleEvent: function APZC_handleEvent(aEvent) { michael@0: switch (aEvent.type) { michael@0: case 'TabSelect': michael@0: this._resetDisplayPort(); michael@0: break; michael@0: michael@0: case 'pageshow': michael@0: if (aEvent.target != Browser.selectedBrowser.contentDocument) { michael@0: break; michael@0: } michael@0: this._resetDisplayPort(); michael@0: break; michael@0: } michael@0: }, michael@0: michael@0: observe: function ao_observe(aSubject, aTopic, aData) { michael@0: if (aTopic == "apzc-transform-begin") { michael@0: // When we're panning, hide the main scrollbars by setting imprecise michael@0: // input (which sets a property on the browser which hides the scrollbar michael@0: // via CSS). This reduces jittering from left to right. We may be able michael@0: // to get rid of this once we implement axis locking in /gfx APZC. michael@0: if (InputSourceHelper.isPrecise) { michael@0: InputSourceHelper._imprecise(); michael@0: } michael@0: } michael@0: }, michael@0: michael@0: receiveMessage: function(aMessage) { michael@0: let json = aMessage.json; michael@0: let browser = aMessage.target; michael@0: switch (aMessage.name) { michael@0: case "Content:ZoomToRect": { michael@0: let { presShellId, viewId } = json; michael@0: let rect = Rect.fromRect(json.rect); michael@0: if (this.isRectZoomedIn(rect, browser.contentViewportBounds)) { michael@0: // If we're already zoomed in, zoom out instead. michael@0: rect = new Rect(0,0,0,0); michael@0: } michael@0: let data = [rect.x, rect.y, rect.width, rect.height, presShellId, viewId].join(","); michael@0: Services.obs.notifyObservers(null, "apzc-zoom-to-rect", data); michael@0: } michael@0: } michael@0: }, michael@0: michael@0: /** michael@0: * Check to see if the area of the rect visible in the viewport is michael@0: * approximately the max area of the rect we can show. michael@0: * Based on code from BrowserElementPanning.js michael@0: */ michael@0: isRectZoomedIn: function (aRect, aViewport) { michael@0: let overlap = aViewport.intersect(aRect); michael@0: let overlapArea = overlap.width * overlap.height; michael@0: let availHeight = Math.min(aRect.width * aViewport.height / aViewport.width, aRect.height); michael@0: let showing = overlapArea / (aRect.width * availHeight); michael@0: let ratioW = (aRect.width / aViewport.width); michael@0: let ratioH = (aRect.height / aViewport.height); michael@0: michael@0: return (showing > 0.9 && (ratioW > 0.9 || ratioH > 0.9)); michael@0: }, michael@0: michael@0: _resetDisplayPort: function () { michael@0: // Start off with something reasonable. The apzc will handle these michael@0: // calculations once scrolling starts. michael@0: let doc = Browser.selectedBrowser.contentDocument.documentElement; michael@0: // While running tests, sometimes this can be null. If we don't have a michael@0: // root document, there's no point in setting a scrollable display port. michael@0: if (!doc) { michael@0: return; michael@0: } michael@0: let win = Browser.selectedBrowser.contentWindow; michael@0: let factor = 0.2; michael@0: let portX = 0; michael@0: let portY = 0; michael@0: let portWidth = ContentAreaObserver.width; michael@0: let portHeight = ContentAreaObserver.height; michael@0: michael@0: if (portWidth < doc.scrollWidth) { michael@0: portWidth += ContentAreaObserver.width * factor; michael@0: if (portWidth > doc.scrollWidth) { michael@0: portWidth = doc.scrollWidth; michael@0: } michael@0: } michael@0: if (portHeight < doc.scrollHeight) { michael@0: portHeight += ContentAreaObserver.height * factor; michael@0: if (portHeight > doc.scrollHeight) { michael@0: portHeight = doc.scrollHeight; michael@0: } michael@0: } michael@0: if (win.scrollX > 0) { michael@0: portX -= ContentAreaObserver.width * factor; michael@0: } michael@0: if (win.scrollY > 0) { michael@0: portY -= ContentAreaObserver.height * factor; michael@0: } michael@0: let cwu = Browser.selectedBrowser.contentWindow michael@0: .QueryInterface(Ci.nsIInterfaceRequestor) michael@0: .getInterface(Ci.nsIDOMWindowUtils); michael@0: cwu.setDisplayPortForElement(portX, portY, michael@0: portWidth, portHeight, michael@0: Browser.selectedBrowser.contentDocument.documentElement, michael@0: 0); michael@0: } michael@0: };