|
1 // -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 let Cc = Components.classes; |
|
7 let Ci = Components.interfaces; |
|
8 let Cu = Components.utils; |
|
9 let Cr = Components.results; |
|
10 |
|
11 /** |
|
12 * Misc. front end utilities for apzc management. |
|
13 * the pref: layers.async-pan-zoom.enabled is true. |
|
14 */ |
|
15 |
|
16 var APZCObserver = { |
|
17 _debugEvents: false, |
|
18 _enabled: false, |
|
19 |
|
20 get enabled() { |
|
21 return this._enabled; |
|
22 }, |
|
23 |
|
24 init: function() { |
|
25 this._enabled = Services.prefs.getBoolPref(kAsyncPanZoomEnabled); |
|
26 if (!this._enabled) { |
|
27 return; |
|
28 } |
|
29 |
|
30 let os = Services.obs; |
|
31 os.addObserver(this, "apzc-transform-begin", false); |
|
32 |
|
33 Elements.tabList.addEventListener("TabSelect", this, true); |
|
34 Elements.browsers.addEventListener("pageshow", this, true); |
|
35 messageManager.addMessageListener("Content:ZoomToRect", this); |
|
36 }, |
|
37 |
|
38 shutdown: function shutdown() { |
|
39 if (!this._enabled) { |
|
40 return; |
|
41 } |
|
42 |
|
43 let os = Services.obs; |
|
44 os.removeObserver(this, "apzc-transform-begin"); |
|
45 |
|
46 Elements.tabList.removeEventListener("TabSelect", this, true); |
|
47 Elements.browsers.removeEventListener("pageshow", this, true); |
|
48 messageManager.removeMessageListener("Content:ZoomToRect", this); |
|
49 }, |
|
50 |
|
51 handleEvent: function APZC_handleEvent(aEvent) { |
|
52 switch (aEvent.type) { |
|
53 case 'TabSelect': |
|
54 this._resetDisplayPort(); |
|
55 break; |
|
56 |
|
57 case 'pageshow': |
|
58 if (aEvent.target != Browser.selectedBrowser.contentDocument) { |
|
59 break; |
|
60 } |
|
61 this._resetDisplayPort(); |
|
62 break; |
|
63 } |
|
64 }, |
|
65 |
|
66 observe: function ao_observe(aSubject, aTopic, aData) { |
|
67 if (aTopic == "apzc-transform-begin") { |
|
68 // When we're panning, hide the main scrollbars by setting imprecise |
|
69 // input (which sets a property on the browser which hides the scrollbar |
|
70 // via CSS). This reduces jittering from left to right. We may be able |
|
71 // to get rid of this once we implement axis locking in /gfx APZC. |
|
72 if (InputSourceHelper.isPrecise) { |
|
73 InputSourceHelper._imprecise(); |
|
74 } |
|
75 } |
|
76 }, |
|
77 |
|
78 receiveMessage: function(aMessage) { |
|
79 let json = aMessage.json; |
|
80 let browser = aMessage.target; |
|
81 switch (aMessage.name) { |
|
82 case "Content:ZoomToRect": { |
|
83 let { presShellId, viewId } = json; |
|
84 let rect = Rect.fromRect(json.rect); |
|
85 if (this.isRectZoomedIn(rect, browser.contentViewportBounds)) { |
|
86 // If we're already zoomed in, zoom out instead. |
|
87 rect = new Rect(0,0,0,0); |
|
88 } |
|
89 let data = [rect.x, rect.y, rect.width, rect.height, presShellId, viewId].join(","); |
|
90 Services.obs.notifyObservers(null, "apzc-zoom-to-rect", data); |
|
91 } |
|
92 } |
|
93 }, |
|
94 |
|
95 /** |
|
96 * Check to see if the area of the rect visible in the viewport is |
|
97 * approximately the max area of the rect we can show. |
|
98 * Based on code from BrowserElementPanning.js |
|
99 */ |
|
100 isRectZoomedIn: function (aRect, aViewport) { |
|
101 let overlap = aViewport.intersect(aRect); |
|
102 let overlapArea = overlap.width * overlap.height; |
|
103 let availHeight = Math.min(aRect.width * aViewport.height / aViewport.width, aRect.height); |
|
104 let showing = overlapArea / (aRect.width * availHeight); |
|
105 let ratioW = (aRect.width / aViewport.width); |
|
106 let ratioH = (aRect.height / aViewport.height); |
|
107 |
|
108 return (showing > 0.9 && (ratioW > 0.9 || ratioH > 0.9)); |
|
109 }, |
|
110 |
|
111 _resetDisplayPort: function () { |
|
112 // Start off with something reasonable. The apzc will handle these |
|
113 // calculations once scrolling starts. |
|
114 let doc = Browser.selectedBrowser.contentDocument.documentElement; |
|
115 // While running tests, sometimes this can be null. If we don't have a |
|
116 // root document, there's no point in setting a scrollable display port. |
|
117 if (!doc) { |
|
118 return; |
|
119 } |
|
120 let win = Browser.selectedBrowser.contentWindow; |
|
121 let factor = 0.2; |
|
122 let portX = 0; |
|
123 let portY = 0; |
|
124 let portWidth = ContentAreaObserver.width; |
|
125 let portHeight = ContentAreaObserver.height; |
|
126 |
|
127 if (portWidth < doc.scrollWidth) { |
|
128 portWidth += ContentAreaObserver.width * factor; |
|
129 if (portWidth > doc.scrollWidth) { |
|
130 portWidth = doc.scrollWidth; |
|
131 } |
|
132 } |
|
133 if (portHeight < doc.scrollHeight) { |
|
134 portHeight += ContentAreaObserver.height * factor; |
|
135 if (portHeight > doc.scrollHeight) { |
|
136 portHeight = doc.scrollHeight; |
|
137 } |
|
138 } |
|
139 if (win.scrollX > 0) { |
|
140 portX -= ContentAreaObserver.width * factor; |
|
141 } |
|
142 if (win.scrollY > 0) { |
|
143 portY -= ContentAreaObserver.height * factor; |
|
144 } |
|
145 let cwu = Browser.selectedBrowser.contentWindow |
|
146 .QueryInterface(Ci.nsIInterfaceRequestor) |
|
147 .getInterface(Ci.nsIDOMWindowUtils); |
|
148 cwu.setDisplayPortForElement(portX, portY, |
|
149 portWidth, portHeight, |
|
150 Browser.selectedBrowser.contentDocument.documentElement, |
|
151 0); |
|
152 } |
|
153 }; |