|
1 <?xml version="1.0"?> |
|
2 |
|
3 <!-- This Source Code Form is subject to the terms of the Mozilla Public |
|
4 - License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> |
|
6 |
|
7 <!DOCTYPE bindings [ |
|
8 <!ENTITY % findBarDTD SYSTEM "chrome://global/locale/findbar.dtd" > |
|
9 %findBarDTD; |
|
10 ]> |
|
11 |
|
12 <bindings id="browser-bindings" |
|
13 xmlns="http://www.mozilla.org/xbl" |
|
14 xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> |
|
15 |
|
16 <binding id="local-browser" extends="chrome://global/content/bindings/browser.xml#browser"> |
|
17 <implementation type="application/javascript" |
|
18 implements="nsIObserver, nsIDOMEventListener, nsIMessageListener"> |
|
19 |
|
20 <constructor> |
|
21 <![CDATA[ |
|
22 this._frameLoader = |
|
23 this.QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader; |
|
24 this._contentViewManager = |
|
25 this._frameLoader.QueryInterface(Components.interfaces.nsIContentViewManager); |
|
26 |
|
27 let prefService = |
|
28 Components.classes["@mozilla.org/preferences-service;1"] |
|
29 .getService(Components.interfaces.nsIPrefBranch); |
|
30 this._cacheRatioWidth = |
|
31 Math.max(1, prefService.getIntPref("toolkit.browser.cacheRatioWidth") / 1000); |
|
32 this._cacheRatioHeight = |
|
33 Math.max(1, prefService.getIntPref("toolkit.browser.cacheRatioHeight") / 1000); |
|
34 |
|
35 if (this._contentViewPrototype) { |
|
36 this._contentViewPrototype.kDieTime = prefService.getIntPref("toolkit.browser.contentViewExpire"); |
|
37 } |
|
38 |
|
39 this.messageManager.loadFrameScript("chrome://browser/content/bindings/browser.js", true); |
|
40 this.messageManager.addMessageListener("DOMTitleChanged", this._messageListenerLocal); |
|
41 this.messageManager.addMessageListener("DOMLinkAdded", this._messageListenerLocal); |
|
42 this.messageManager.addMessageListener("pageshow", this._messageListenerLocal); |
|
43 this.messageManager.addMessageListener("pagehide", this._messageListenerLocal); |
|
44 this.messageManager.addMessageListener("DOMPopupBlocked", this._messageListenerLocal); |
|
45 this.messageManager.addMessageListener("MozScrolledAreaChanged", this._messageListenerLocal); |
|
46 this.messageManager.addMessageListener("Content:UpdateDisplayPort", this._messageListenerLocal); |
|
47 |
|
48 this._webProgress._init(); |
|
49 |
|
50 // Remove event listeners added by toolkit <browser> binding. |
|
51 this.removeEventListener("pageshow", this.onPageShow, true); |
|
52 this.removeEventListener("pagehide", this.onPageHide, true); |
|
53 this.removeEventListener("DOMPopupBlocked", this.onPopupBlocked, true); |
|
54 |
|
55 this.setAttribute("autoscrollpopup", "autoscrollerid"); |
|
56 ]]> |
|
57 </constructor> |
|
58 |
|
59 <field name="_searchEngines">[]</field> |
|
60 <property name="searchEngines" |
|
61 onget="return this._searchEngines" |
|
62 readonly="true"/> |
|
63 |
|
64 <field name="_documentURI">null</field> |
|
65 <property name="documentURI" |
|
66 onget="return this._documentURI ? this._ios.newURI(this._documentURI, null, null) : null" |
|
67 readonly="true"/> |
|
68 |
|
69 <field name="contentWindowId">null</field> |
|
70 |
|
71 <field name="_contentTitle">null</field> |
|
72 |
|
73 <field name="_ios"> |
|
74 Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService); |
|
75 </field> |
|
76 |
|
77 <!-- |
|
78 * Point Conversion Routines - browsers may be shifted by UI such that |
|
79 * a client point in an event does not coincide with a css position. |
|
80 * Examples include the notification bar, which pushes the browser down, |
|
81 * or deck movement when forms are positioned above the keyboard. |
|
82 * |
|
83 * Client to browser conversion: |
|
84 * |
|
85 * ptClientToBrowser |
|
86 * Convert client coordinates in device pixels to page-relative |
|
87 * coordinates in CSS pixels. |
|
88 * |
|
89 * @param aClientX, aClientY - client coordinates to convert. |
|
90 * @param aIgnoreScroll ignore root frame scroll. |
|
91 * @param aIgnoreScale ignore current scale factor. |
|
92 * @return { x: converted x coordinate, y: converted y coordinate } |
|
93 * |
|
94 * rectClientToBrowser |
|
95 * Convert a client Rect() in device pixels to page-relative |
|
96 * coordinates in CSS pixels. |
|
97 * |
|
98 * @param aRect - client Rect to convert. |
|
99 * @param aIgnoreScroll ignore root frame scroll. |
|
100 * @param aIgnoreScale ignore current scale factor. |
|
101 * @return converted Rect() |
|
102 * |
|
103 * ctobx, ctoby |
|
104 * Convert individual x and y coordinates. |
|
105 * |
|
106 * @param aX or aY - browser coordinate |
|
107 * @param aIgnoreScroll ignore root frame scroll. |
|
108 * @param aIgnoreScale ignore current scale factor. |
|
109 * @return converted coordinate |
|
110 * |
|
111 * Browser to client conversion: |
|
112 * |
|
113 * ptBrowserToClient |
|
114 * Convert browser coordinates in css pixels to client (screen) coordinates |
|
115 * in device pixels. Useful in positioning UI elements at event targets. |
|
116 * |
|
117 * @param aBrowserX, aBrowserY - browser coordinates to convert. |
|
118 * @param aIgnoreScroll ignore root frame scroll. |
|
119 * @param aIgnoreScale ignore current scale factor. |
|
120 * @return { x: converted x coordinate, y: converted y coordinate } |
|
121 * |
|
122 * msgBrowserToClient |
|
123 * Converts a message manager message with coordinates stored in |
|
124 * aMessage.json.xPos, aMessage.json.yPos. |
|
125 * |
|
126 * @param aMessage - message manager message |
|
127 * @param aIgnoreScroll ignore root frame scroll. |
|
128 * @param aIgnoreScale ignore current scale factor. |
|
129 * @return { x: converted x coordinate, y: converted y coordinate } |
|
130 * |
|
131 * rectBrowserToClient |
|
132 * Converts a rect (left, top, right, bottom). |
|
133 * |
|
134 * @param aRect - rect to convert |
|
135 * @param aIgnoreScroll ignore root frame scroll. |
|
136 * @param aIgnoreScale ignore current scale factor. |
|
137 * @return { left:, top:, right:, bottom: } |
|
138 * |
|
139 * btocx, btocy |
|
140 * Convert individual x and y coordinates. |
|
141 * |
|
142 * @param aX or aY - client coordinate |
|
143 * @param aIgnoreScroll ignore root frame scroll. |
|
144 * @param aIgnoreScale ignore current scale factor. |
|
145 * @return converted coordinate |
|
146 --> |
|
147 <method name="ptClientToBrowser"> |
|
148 <parameter name="aClientX"/> |
|
149 <parameter name="aClientY"/> |
|
150 <parameter name="aIgnoreScroll"/> |
|
151 <parameter name="aIgnoreScale"/> |
|
152 <body> |
|
153 <![CDATA[ |
|
154 let ignoreScroll = aIgnoreScroll || false; |
|
155 let ignoreScale = aIgnoreScale || false; |
|
156 |
|
157 let bcr = this.getBoundingClientRect(); |
|
158 |
|
159 let scrollX = 0; |
|
160 let scrollY = 0; |
|
161 if (!ignoreScroll) { |
|
162 let scroll = this.getRootView().getPosition(); |
|
163 scrollX = scroll.x; |
|
164 scrollY = scroll.y; |
|
165 } |
|
166 |
|
167 let scale = 1; |
|
168 if (!ignoreScale) { |
|
169 scale = this.scale; |
|
170 } |
|
171 |
|
172 return { |
|
173 x: (aClientX - bcr.left) / scale + scrollX, |
|
174 y: (aClientY - bcr.top) / scale + scrollY |
|
175 }; |
|
176 ]]> |
|
177 </body> |
|
178 </method> |
|
179 |
|
180 <method name="rectClientToBrowser"> |
|
181 <parameter name="aClientRect"/> |
|
182 <parameter name="aIgnoreScroll"/> |
|
183 <parameter name="aIgnoreScale"/> |
|
184 <body> |
|
185 <![CDATA[ |
|
186 let ignoreScroll = aIgnoreScroll || false; |
|
187 let ignoreScale = aIgnoreScale || false; |
|
188 |
|
189 let scrollX = 0; |
|
190 let scrollY = 0; |
|
191 if (!ignoreScroll) { |
|
192 let scroll = this.getRootView().getPosition(); |
|
193 scrollX = scroll.x; |
|
194 scrollY = scroll.y; |
|
195 } |
|
196 |
|
197 let scale = 1; |
|
198 if (!ignoreScale) { |
|
199 scale = this.scale; |
|
200 } |
|
201 |
|
202 let bcr = this.getBoundingClientRect(); |
|
203 let clientRect = Rect.fromRect(aClientRect); |
|
204 return new Rect( |
|
205 (clientRect.x - bcr.left) / scale + scrollX, |
|
206 (clientRect.y - bcr.top) / scale + scrollY, |
|
207 clientRect.width / scale, |
|
208 clientRect.height / scale |
|
209 ); |
|
210 ]]> |
|
211 </body> |
|
212 </method> |
|
213 |
|
214 <method name="ctobx"> |
|
215 <parameter name="aX"/> |
|
216 <parameter name="aIgnoreScroll"/> |
|
217 <parameter name="aIgnoreScale"/> |
|
218 <body> |
|
219 <![CDATA[ |
|
220 let y = 0; |
|
221 let result = this.ptClientToBrowser(aX, y, aIgnoreScroll, aIgnoreScale); |
|
222 return result.x; |
|
223 ]]> |
|
224 </body> |
|
225 </method> |
|
226 |
|
227 <method name="ctoby"> |
|
228 <parameter name="aY"/> |
|
229 <parameter name="aIgnoreScroll"/> |
|
230 <parameter name="aIgnoreScale"/> |
|
231 <body> |
|
232 <![CDATA[ |
|
233 let x = 0; |
|
234 let result = this.ptClientToBrowser(x, aY, aIgnoreScroll, aIgnoreScale); |
|
235 return result.y; |
|
236 ]]> |
|
237 </body> |
|
238 </method> |
|
239 |
|
240 <method name="ptBrowserToClient"> |
|
241 <parameter name="aBrowserX"/> |
|
242 <parameter name="aBrowserY"/> |
|
243 <parameter name="aIgnoreScroll"/> |
|
244 <parameter name="aIgnoreScale"/> |
|
245 <body> |
|
246 <![CDATA[ |
|
247 let ignoreScroll = aIgnoreScroll || false; |
|
248 let ignoreScale = aIgnoreScale || false; |
|
249 |
|
250 let bcr = this.getBoundingClientRect(); |
|
251 |
|
252 let scrollX = 0; |
|
253 let scrollY = 0; |
|
254 if (!ignoreScroll) { |
|
255 let scroll = this.getRootView().getPosition(); |
|
256 scrollX = scroll.x; |
|
257 scrollY = scroll.y; |
|
258 } |
|
259 |
|
260 let scale = 1; |
|
261 if (!ignoreScale) { |
|
262 scale = this.scale; |
|
263 } |
|
264 |
|
265 return { |
|
266 x: (aBrowserX * scale - scrollX + bcr.left), |
|
267 y: (aBrowserY * scale - scrollY + bcr.top) |
|
268 }; |
|
269 ]]> |
|
270 </body> |
|
271 </method> |
|
272 |
|
273 <method name="msgBrowserToClient"> |
|
274 <parameter name="aMessage"/> |
|
275 <parameter name="aIgnoreScroll"/> |
|
276 <parameter name="aIgnoreScale"/> |
|
277 <body> |
|
278 <![CDATA[ |
|
279 let x = aMessage.json.xPos; |
|
280 let y = aMessage.json.yPos; |
|
281 return this.ptBrowserToClient(x, y, aIgnoreScroll, aIgnoreScale); |
|
282 ]]> |
|
283 </body> |
|
284 </method> |
|
285 |
|
286 <method name="rectBrowserToClient"> |
|
287 <parameter name="aRect"/> |
|
288 <parameter name="aIgnoreScroll"/> |
|
289 <parameter name="aIgnoreScale"/> |
|
290 <body> |
|
291 <![CDATA[ |
|
292 let left = aRect.left; |
|
293 let top = aRect.top; |
|
294 let right = aRect.right; |
|
295 let bottom = aRect.bottom; |
|
296 let a = this.ptBrowserToClient(left, top, aIgnoreScroll, aIgnoreScale); |
|
297 let b = this.ptBrowserToClient(right, bottom, aIgnoreScroll, aIgnoreScale); |
|
298 return { |
|
299 left: a.x, |
|
300 top: a.y, |
|
301 right: b.x, |
|
302 bottom: b.y |
|
303 }; |
|
304 ]]> |
|
305 </body> |
|
306 </method> |
|
307 |
|
308 <method name="btocx"> |
|
309 <parameter name="aX"/> |
|
310 <parameter name="aIgnoreScroll"/> |
|
311 <parameter name="aIgnoreScale"/> |
|
312 <body> |
|
313 <![CDATA[ |
|
314 let y = 0; |
|
315 let result = this.ptBrowserToClient(aX, y, aIgnoreScroll, aIgnoreScale); |
|
316 return result.x; |
|
317 ]]> |
|
318 </body> |
|
319 </method> |
|
320 |
|
321 <method name="btocy"> |
|
322 <parameter name="aY"/> |
|
323 <parameter name="aIgnoreScroll"/> |
|
324 <parameter name="aIgnoreScale"/> |
|
325 <body> |
|
326 <![CDATA[ |
|
327 let x = 0; |
|
328 let result = this.ptBrowserToClient(x, aY, aIgnoreScroll, aIgnoreScale); |
|
329 return result.y; |
|
330 ]]> |
|
331 </body> |
|
332 </method> |
|
333 |
|
334 <property name="scale"> |
|
335 <getter><![CDATA[ |
|
336 let cwu = this.contentDocument |
|
337 .defaultView |
|
338 .QueryInterface(Components.interfaces.nsIInterfaceRequestor) |
|
339 .getInterface(Components.interfaces.nsIDOMWindowUtils); |
|
340 let resx = {}, resy = {}; |
|
341 cwu.getResolution(resx, resy); |
|
342 // Resolution set by the apzc and is symmetric. |
|
343 return resx.value; |
|
344 ]]></getter> |
|
345 </property> |
|
346 |
|
347 <field name="_messageListenerLocal"><![CDATA[ |
|
348 ({ |
|
349 self: this, |
|
350 receiveMessage: function receiveMessage(aMessage) { |
|
351 let self = this.self; |
|
352 let json = aMessage.json; |
|
353 |
|
354 switch (aMessage.name) { |
|
355 case "DOMPopupBlocked": |
|
356 self.onPopupBlocked(aMessage); |
|
357 break; |
|
358 |
|
359 case "pageshow": |
|
360 self.onPageShow(aMessage); |
|
361 |
|
362 if (!self.mIconURL && self._documentURI) { |
|
363 let iconURL = null; |
|
364 if (self.shouldLoadFavicon()) { |
|
365 // Use documentURI in the favicon construction so that we |
|
366 // do the right thing with about:-style error pages. Bug 515188 |
|
367 iconURL = self.documentURI.prePath + "/favicon.ico"; |
|
368 } |
|
369 self.loadFavicon(iconURL, null); |
|
370 } |
|
371 break; |
|
372 |
|
373 case "pagehide": |
|
374 self.onPageHide(aMessage); |
|
375 break; |
|
376 |
|
377 case "DOMTitleChanged": |
|
378 self._contentTitle = json.title; |
|
379 break; |
|
380 |
|
381 case "DOMLinkAdded": |
|
382 // ignore results from subdocuments |
|
383 if (json.windowId != self.contentWindowId) |
|
384 return; |
|
385 |
|
386 let linkType = self._getLinkType(json); |
|
387 switch(linkType) { |
|
388 case "icon": |
|
389 self.loadFavicon(json.href, json.charset); |
|
390 break; |
|
391 case "search": |
|
392 self._searchEngines.push({ title: json.title, href: json.href }); |
|
393 break; |
|
394 } |
|
395 break; |
|
396 |
|
397 case "MozScrolledAreaChanged": { |
|
398 self._contentDocumentWidth = json.width; |
|
399 self._contentDocumentHeight = json.height; |
|
400 self._contentDocumentLeft = (json.left < 0) ? json.left : 0; |
|
401 |
|
402 // Recalculate whether the visible area is actually in bounds |
|
403 let view = self.getRootView(); |
|
404 view.scrollBy(0, 0); |
|
405 break; |
|
406 } |
|
407 |
|
408 case "Content:UpdateDisplayPort": { |
|
409 // Recalculate whether the visible area is actually in bounds |
|
410 let view = self.getRootView(); |
|
411 view.scrollBy(0, 0); |
|
412 view._updateCacheViewport(); |
|
413 break; |
|
414 } |
|
415 } |
|
416 } |
|
417 }) |
|
418 ]]></field> |
|
419 |
|
420 <method name="loadFavicon"> |
|
421 <parameter name="aURL"/> |
|
422 <parameter name="aCharset"/> |
|
423 <body><![CDATA[ |
|
424 try { // newURI call is throwing for chrome URI |
|
425 let iconURI = this._ios.newURI(aURL, aCharset, null); |
|
426 if (gFaviconService.isFailedFavicon(iconURI)) |
|
427 return; |
|
428 |
|
429 gFaviconService.setAndFetchFaviconForPage(this.currentURI, iconURI, true, |
|
430 gFaviconService.FAVICON_LOAD_NON_PRIVATE); |
|
431 this.mIconURL = iconURI.spec; |
|
432 } catch (e) { |
|
433 this.mIconURL = null; |
|
434 } |
|
435 ]]></body> |
|
436 </method> |
|
437 |
|
438 <method name="shouldLoadFavicon"> |
|
439 <body><![CDATA[ |
|
440 let docURI = this.documentURI; |
|
441 return (docURI && ("schemeIs" in docURI) && |
|
442 (docURI.schemeIs("http") || docURI.schemeIs("https"))); |
|
443 ]]></body> |
|
444 </method> |
|
445 |
|
446 <method name="_getLinkType"> |
|
447 <parameter name="aLink" /> |
|
448 <body><![CDATA[ |
|
449 let type = ""; |
|
450 if (/\bicon\b/i.test(aLink.rel)) { |
|
451 type = "icon"; |
|
452 } |
|
453 else if (/\bsearch\b/i.test(aLink.rel) && aLink.type && aLink.title) { |
|
454 let linkType = aLink.type.replace(/^\s+|\s*(?:;.*)?$/g, "").toLowerCase(); |
|
455 if (linkType == "application/opensearchdescription+xml" && /^(?:https?|ftp):/i.test(aLink.href)) { |
|
456 type = "search"; |
|
457 } |
|
458 } |
|
459 |
|
460 return type; |
|
461 ]]></body> |
|
462 </method> |
|
463 |
|
464 <field name="_webProgress"><![CDATA[ |
|
465 ({ |
|
466 _browser: this, |
|
467 |
|
468 _init: function() { |
|
469 this._browser.messageManager.addMessageListener("Content:StateChange", this); |
|
470 this._browser.messageManager.addMessageListener("Content:LocationChange", this); |
|
471 this._browser.messageManager.addMessageListener("Content:SecurityChange", this); |
|
472 }, |
|
473 |
|
474 receiveMessage: function(aMessage) { |
|
475 let json = aMessage.json; |
|
476 switch (aMessage.name) { |
|
477 case "Content:StateChange": |
|
478 this._browser.updateWindowId(json.contentWindowId); |
|
479 break; |
|
480 |
|
481 case "Content:LocationChange": |
|
482 try { |
|
483 let locationURI = this._browser._ios.newURI(json.location, null, null); |
|
484 this._browser.webNavigation._currentURI = locationURI; |
|
485 this._browser.webNavigation.canGoBack = json.canGoBack; |
|
486 this._browser.webNavigation.canGoForward = json.canGoForward; |
|
487 this._browser._charset = json.charset; |
|
488 } catch(e) {} |
|
489 |
|
490 if (this._browser.updateWindowId(json.contentWindowId)) { |
|
491 this._browser._documentURI = json.documentURI; |
|
492 this._browser._searchEngines = []; |
|
493 } |
|
494 break; |
|
495 |
|
496 case "Content:SecurityChange": |
|
497 let serhelper = Components.classes["@mozilla.org/network/serialization-helper;1"] |
|
498 .getService(Components.interfaces.nsISerializationHelper); |
|
499 let SSLStatus = json.SSLStatusAsString ? serhelper.deserializeObject(json.SSLStatusAsString) : null; |
|
500 if (SSLStatus) { |
|
501 SSLStatus.QueryInterface(Components.interfaces.nsISSLStatus); |
|
502 // We must check the Extended Validation (EV) state here, on the chrome |
|
503 // process, because NSS is needed for that determination. |
|
504 if (SSLStatus && SSLStatus.isExtendedValidation) { |
|
505 json.state |= Components.interfaces.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL; |
|
506 } |
|
507 } |
|
508 |
|
509 let data = this._getIdentityData(SSLStatus); |
|
510 this._browser.updateWindowId(json.contentWindowId); |
|
511 break; |
|
512 } |
|
513 }, |
|
514 |
|
515 /** |
|
516 * Helper to parse out the important parts of the SSL cert for use in constructing |
|
517 * identity UI strings |
|
518 */ |
|
519 _getIdentityData: function(status) { |
|
520 let result = {}; |
|
521 |
|
522 if (status) { |
|
523 let cert = status.serverCert; |
|
524 |
|
525 // Human readable name of Subject |
|
526 result.subjectOrg = cert.organization; |
|
527 |
|
528 // SubjectName fields, broken up for individual access |
|
529 if (cert.subjectName) { |
|
530 result.subjectNameFields = {}; |
|
531 cert.subjectName.split(",").forEach(function(v) { |
|
532 var field = v.split("="); |
|
533 if (field[1]) |
|
534 this[field[0]] = field[1]; |
|
535 }, result.subjectNameFields); |
|
536 |
|
537 // Call out city, state, and country specifically |
|
538 result.city = result.subjectNameFields.L; |
|
539 result.state = result.subjectNameFields.ST; |
|
540 result.country = result.subjectNameFields.C; |
|
541 } |
|
542 |
|
543 // Human readable name of Certificate Authority |
|
544 result.caOrg = cert.issuerOrganization || cert.issuerCommonName; |
|
545 |
|
546 if (!this._overrideService) |
|
547 this._overrideService = Components.classes["@mozilla.org/security/certoverride;1"] |
|
548 .getService(Components.interfaces.nsICertOverrideService); |
|
549 |
|
550 // Check whether this site is a security exception. |
|
551 let currentURI = this._browser.webNavigation._currentURI; |
|
552 if (currentURI) { |
|
553 result.isException = this._overrideService.hasMatchingOverride(currentURI.asciiHost, currentURI.port, cert, {}, {}); |
|
554 } else { |
|
555 result.isException = false; |
|
556 } |
|
557 } |
|
558 |
|
559 return result; |
|
560 } |
|
561 }) |
|
562 ]]></field> |
|
563 |
|
564 <property name="webProgress" |
|
565 readonly="true" |
|
566 onget="return null"/> |
|
567 |
|
568 <method name="onPageShow"> |
|
569 <parameter name="aMessage"/> |
|
570 <body> |
|
571 <![CDATA[ |
|
572 this.attachFormFill(); |
|
573 if (this.pageReport) { |
|
574 var json = aMessage.json; |
|
575 var i = 0; |
|
576 while (i < this.pageReport.length) { |
|
577 // Filter out irrelevant reports. |
|
578 if (this.pageReport[i].requestingWindowId == json.windowId) |
|
579 i++; |
|
580 else |
|
581 this.pageReport.splice(i, 1); |
|
582 } |
|
583 if (this.pageReport.length == 0) { |
|
584 this.pageReport = null; |
|
585 this.updatePageReport(); |
|
586 } |
|
587 } |
|
588 ]]> |
|
589 </body> |
|
590 </method> |
|
591 |
|
592 <method name="onPageHide"> |
|
593 <parameter name="aMessage"/> |
|
594 <body> |
|
595 <![CDATA[ |
|
596 if (this.pageReport) { |
|
597 this.pageReport = null; |
|
598 this.updatePageReport(); |
|
599 } |
|
600 // Delete the feeds cache if we're hiding the topmost page |
|
601 // (as opposed to one of its iframes). |
|
602 if (this.feeds && aMessage.target == this) |
|
603 this.feeds = null; |
|
604 |
|
605 this._contentWindowWidth = aMessage.json.contentWindowWidth; |
|
606 this._contentWindowHeight = aMessage.json.contentWindowHeight; |
|
607 ]]> |
|
608 </body> |
|
609 </method> |
|
610 |
|
611 <method name="onPopupBlocked"> |
|
612 <parameter name="aMessage"/> |
|
613 <body> |
|
614 <![CDATA[ |
|
615 if (!this.pageReport) { |
|
616 this.pageReport = []; |
|
617 } |
|
618 |
|
619 let json = aMessage.json; |
|
620 // XXX Replacing requestingWindow && requestingDocument affects |
|
621 // http://mxr.mozilla.org/mozilla-central/source/browser/base/content/browser.js#500 |
|
622 var obj = { |
|
623 requestingWindowId: json.windowId, |
|
624 popupWindowURI: this._ios.newURI(json.popupWindowURI.spec, json.popupWindowURI.charset, null), |
|
625 popupWindowFeatures: json.popupWindowFeatures, |
|
626 popupWindowName: json.popupWindowName |
|
627 }; |
|
628 |
|
629 this.pageReport.push(obj); |
|
630 this.pageReport.reported = false; |
|
631 this.updatePageReport(); |
|
632 ]]> |
|
633 </body> |
|
634 </method> |
|
635 |
|
636 <field name="_frameLoader">null</field> |
|
637 <field name="_contentViewManager">null</field> |
|
638 |
|
639 <!-- |
|
640 * Returns the current content viewport bounds in browser coordinates |
|
641 * taking into account zoom and scroll. |
|
642 * |
|
643 * @return Rect() |
|
644 --> |
|
645 <property name="contentViewportBounds"> |
|
646 <getter><![CDATA[ |
|
647 return this.rectClientToBrowser(this.getBoundingClientRect()); |
|
648 ]]></getter> |
|
649 </property> |
|
650 |
|
651 <!-- Dimensions of content window --> |
|
652 <field name="_contentWindowWidth">0</field> |
|
653 <field name="_contentWindowHeight">0</field> |
|
654 <property name="contentWindowWidth" |
|
655 onget="return this._contentWindowWidth;" |
|
656 readonly="true"/> |
|
657 <property name="contentWindowHeight" |
|
658 onget="return this._contentWindowHeight;" |
|
659 readonly="true"/> |
|
660 |
|
661 <!-- Dimensions of content document --> |
|
662 <field name="_contentDocumentWidth">0</field> |
|
663 <field name="_contentDocumentHeight">0</field> |
|
664 <property name="contentDocumentWidth" |
|
665 onget="return this._contentDocumentWidth;" |
|
666 readonly="true"/> |
|
667 <property name="contentDocumentHeight" |
|
668 onget="return this._contentDocumentHeight;" |
|
669 readonly="true"/> |
|
670 |
|
671 <!-- If this attribute is negative this indicate the document is rtl and |
|
672 some operations like panning or calculating the cache area should |
|
673 take it into account. This is useless for non-remote browser --> |
|
674 <field name="_contentDocumentLeft">0</field> |
|
675 |
|
676 <!-- These counters are used to update the cached viewport after they reach a certain |
|
677 threshold when scrolling --> |
|
678 <field name="_cacheRatioWidth">1</field> |
|
679 <field name="_cacheRatioHeight">1</field> |
|
680 |
|
681 <!-- Used in remote tabs only. --> |
|
682 <method name="_updateCSSViewport"> |
|
683 <body/> |
|
684 </method> |
|
685 |
|
686 <!-- Sets size of CSS viewport, which affects how page is layout. --> |
|
687 <method name="setWindowSize"> |
|
688 <parameter name="width"/> |
|
689 <parameter name="height"/> |
|
690 <body> |
|
691 <![CDATA[ |
|
692 this._contentWindowWidth = width; |
|
693 this._contentWindowHeight = height; |
|
694 this.messageManager.sendAsyncMessage("Content:SetWindowSize", { |
|
695 width: width, |
|
696 height: height |
|
697 }); |
|
698 |
|
699 // If the window size is changing, make sure the displayport is in sync |
|
700 this.getRootView()._updateCacheViewport(); |
|
701 ]]> |
|
702 </body> |
|
703 </method> |
|
704 |
|
705 <method name="getRootView"> |
|
706 <body> |
|
707 <![CDATA[ |
|
708 return this._contentView; |
|
709 ]]> |
|
710 </body> |
|
711 </method> |
|
712 |
|
713 <field name="_contentViewPrototype"><![CDATA[ |
|
714 ({ |
|
715 _scrollbox: null, |
|
716 |
|
717 init: function(aElement) { |
|
718 this._scrollbox = aElement.scrollBoxObject; |
|
719 }, |
|
720 |
|
721 isRoot: function() { |
|
722 return false; |
|
723 }, |
|
724 |
|
725 scrollBy: function(x, y) { |
|
726 this._scrollbox.scrollBy(x,y); |
|
727 }, |
|
728 |
|
729 scrollTo: function(x, y) { |
|
730 this._scrollbox.scrollTo(x,y); |
|
731 }, |
|
732 |
|
733 getPosition: function() { |
|
734 let x = {}, y = {}; |
|
735 this._scrollbox.getPosition(x, y); |
|
736 return { x: x.value, y: y.value }; |
|
737 } |
|
738 }) |
|
739 ]]> |
|
740 </field> |
|
741 |
|
742 <method name="getViewAt"> |
|
743 <parameter name="x"/> |
|
744 <parameter name="y"/> |
|
745 <body> |
|
746 <![CDATA[ |
|
747 let cwu = this.contentDocument.defaultView.QueryInterface(Components.interfaces.nsIInterfaceRequestor) |
|
748 .getInterface(Components.interfaces.nsIDOMWindowUtils); |
|
749 let elt = cwu.elementFromPoint(x, y, false, false); |
|
750 |
|
751 while (elt && !elt.scrollBoxObject) |
|
752 elt = elt.parentNode; |
|
753 |
|
754 if (!elt) |
|
755 return this._contentView; |
|
756 |
|
757 let cv = Object.create(this._contentViewPrototype); |
|
758 cv.init(elt); |
|
759 return cv; |
|
760 ]]> |
|
761 </body> |
|
762 </method> |
|
763 |
|
764 <field name="_contentView"><![CDATA[ |
|
765 ({ |
|
766 self: this, |
|
767 |
|
768 _updateCacheViewport: function() { |
|
769 }, |
|
770 |
|
771 isRoot: function() { |
|
772 return true; |
|
773 }, |
|
774 |
|
775 scrollBy: function(x, y) { |
|
776 let self = this.self; |
|
777 self.contentWindow.scrollBy(x, y); |
|
778 }, |
|
779 |
|
780 scrollTo: function(x, y) { |
|
781 let self = this.self; |
|
782 x = Math.floor(Math.max(0, Math.min(self.contentDocumentWidth, x))); |
|
783 y = Math.floor(Math.max(0, Math.min(self.contentDocumentHeight, y))); |
|
784 self.contentWindow.scrollTo(x, y); |
|
785 }, |
|
786 |
|
787 getPosition: function() { |
|
788 let self = this.self; |
|
789 let scrollX = {}, scrollY = {}; |
|
790 let cwu = self.contentWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor). |
|
791 getInterface(Components.interfaces.nsIDOMWindowUtils); |
|
792 cwu.getScrollXY(false, scrollX, scrollY); |
|
793 return { x: scrollX.value, y: scrollY.value }; |
|
794 }, |
|
795 |
|
796 toString: function() { |
|
797 return "[View Local]"; |
|
798 } |
|
799 }) |
|
800 ]]> |
|
801 </field> |
|
802 |
|
803 <method name="updateWindowId"> |
|
804 <parameter name="aNewId"/> |
|
805 <body><![CDATA[ |
|
806 if (this.contentWindowId != aNewId) { |
|
807 this.contentWindowId = aNewId; |
|
808 return true; |
|
809 } |
|
810 return false; |
|
811 ]]></body> |
|
812 </method> |
|
813 |
|
814 <field name="_active">false</field> |
|
815 <property name="active" onget="return this._active;"> |
|
816 <setter><![CDATA[ |
|
817 // Do not change displayport on local tabs! |
|
818 this._active = val; |
|
819 this.docShellIsActive = this._active; |
|
820 ]]></setter> |
|
821 </property> |
|
822 </implementation> |
|
823 </binding> |
|
824 |
|
825 <binding id="remote-browser" extends="#local-browser"> |
|
826 <implementation type="application/javascript" implements="nsIObserver, nsIDOMEventListener, nsIMessageListener"> |
|
827 <property name="autoscrollEnabled"> |
|
828 <getter> |
|
829 <![CDATA[ |
|
830 throw "autoscrollEnabled: Supports Remote?"; |
|
831 ]]> |
|
832 </getter> |
|
833 </property> |
|
834 |
|
835 <property name="docShell" |
|
836 readonly="true"> |
|
837 <getter><![CDATA[ |
|
838 return { |
|
839 forcedCharset : this._charset, |
|
840 parentCharset : "", |
|
841 parentCharsetSource : 0 |
|
842 } |
|
843 ]]></getter> |
|
844 </property> |
|
845 |
|
846 <field name="_contentTitle">null</field> |
|
847 |
|
848 <property name="contentTitle" |
|
849 onget="return this._contentTitle;" |
|
850 readonly="true"/> |
|
851 |
|
852 <field name="_remoteWebNavigation">null</field> |
|
853 <property name="webNavigation" readonly="true"> |
|
854 <getter> |
|
855 <![CDATA[ |
|
856 if (!this._remoteWebNavigation) { |
|
857 let jsm = "resource://gre/modules/RemoteWebNavigation.jsm"; |
|
858 let RemoteWebNavigation = Components.utils.import(jsm, {}).RemoteWebNavigation; |
|
859 this._remoteWebNavigation = new RemoteWebNavigation(this); |
|
860 } |
|
861 return this._remoteWebNavigation; |
|
862 ]]> |
|
863 </getter> |
|
864 </property> |
|
865 |
|
866 <property name="contentWindow" |
|
867 readonly="true" |
|
868 onget="return null"/> |
|
869 |
|
870 <property name="sessionHistory" |
|
871 onget="return null" |
|
872 readonly="true"/> |
|
873 |
|
874 <property name="markupDocumentViewer" |
|
875 onget="return null" |
|
876 readonly="true"/> |
|
877 |
|
878 <property name="contentViewerEdit" |
|
879 onget="return null" |
|
880 readonly="true"/> |
|
881 |
|
882 <property name="contentViewerFile" |
|
883 onget="return null" |
|
884 readonly="true"/> |
|
885 |
|
886 <field name="_charset"></field> |
|
887 |
|
888 <constructor> |
|
889 <![CDATA[ |
|
890 this.messageManager.addMessageListener("scroll", this._messageListenerRemote); |
|
891 ]]> |
|
892 </constructor> |
|
893 |
|
894 <field name="scrollSync">true</field> |
|
895 |
|
896 <field name="_messageListenerRemote"><![CDATA[ |
|
897 ({ |
|
898 self: this, |
|
899 receiveMessage: function receiveMessage(aMessage) { |
|
900 let self = this.self; |
|
901 let json = aMessage.json; |
|
902 |
|
903 switch (aMessage.name) { |
|
904 case "scroll": |
|
905 if (!json.isRoot) |
|
906 return; |
|
907 if (!self.scrollSync) |
|
908 return; |
|
909 this.doScroll(json.scrollOffset.x, json.scrollOffset.y, 0); |
|
910 break; |
|
911 } |
|
912 }, |
|
913 |
|
914 doScroll: function doScroll(aX, aY, aCount) { |
|
915 let self = this.self; |
|
916 |
|
917 // Use floor so that we always guarantee top-left corner of content is visible. |
|
918 let view = self.getRootView(); |
|
919 view.scrollTo(Math.floor(aX * self.scale), Math.floor(aY * self.scale)); |
|
920 |
|
921 let position = view.getPosition(); |
|
922 if ((position.x != aX * self.scale || position.y != aY * self.scale) && aCount < 3) { |
|
923 setTimeout((function() { |
|
924 this.doScroll(aX, aY, ++aCount); |
|
925 }).bind(this), 0); |
|
926 } |
|
927 } |
|
928 }) |
|
929 ]]></field> |
|
930 |
|
931 <!-- Keep a store of temporary content views. --> |
|
932 <field name="_contentViews">({})</field> |
|
933 |
|
934 <!-- There is a point before a page has loaded where a root content view |
|
935 may not exist. We use this so that we don't have to worry about doing |
|
936 an if check every time we want to scroll. --> |
|
937 <field name="_contentNoop"><![CDATA[ |
|
938 ({ |
|
939 _updateCacheViewport: function() {}, |
|
940 _getViewportSize: function() {}, |
|
941 |
|
942 isRoot: function() { |
|
943 return true; |
|
944 }, |
|
945 |
|
946 _scale: 1, |
|
947 _setScale: function(scale) {}, |
|
948 scrollBy: function(x, y) {}, |
|
949 scrollTo: function(x, y) {}, |
|
950 getPosition: function() { |
|
951 return { x: 0, y: 0 }; |
|
952 } |
|
953 }) |
|
954 ]]></field> |
|
955 |
|
956 <field name="_contentViewPrototype"><![CDATA[ |
|
957 ({ |
|
958 self: this, |
|
959 _id: null, |
|
960 _contentView: null, |
|
961 _timeout: null, |
|
962 _pixelsPannedSinceRefresh: { x: 0, y: 0 }, |
|
963 _lastPanTime: 0, |
|
964 |
|
965 kDieTime: 3000, |
|
966 |
|
967 /** |
|
968 * Die if we haven't panned in a while. |
|
969 * |
|
970 * Since we keep a map of active content views, we need to regularly |
|
971 * check if they are necessary so that every single thing the user |
|
972 * pans is not kept in memory forever. |
|
973 */ |
|
974 _dieIfOld: function() { |
|
975 if (Date.now() - this._lastPanTime >= this.kDieTime) |
|
976 this._die(); |
|
977 else |
|
978 // This doesn't need to be exact, just be sure to clean up at some point. |
|
979 this._timeout = setTimeout(this._dieIfOld.bind(this), this.kDieTime); |
|
980 }, |
|
981 |
|
982 /** Cleanup after ourselves. */ |
|
983 _die: function() { |
|
984 let timeout = this._timeout; |
|
985 if (timeout) { |
|
986 clearTimeout(timeout); |
|
987 this._timeout = null; |
|
988 } |
|
989 |
|
990 if (this._contentView && Math.abs(this._pixelsPannedSinceRefresh) > 0) |
|
991 this._updateCacheViewport(); |
|
992 |
|
993 // We expect contentViews to contain our ID. If not, something bad |
|
994 // happened. |
|
995 delete this.self._contentViews[this._id]; |
|
996 }, |
|
997 |
|
998 /** |
|
999 * Given the cache size and the viewport size, this determines where the cache |
|
1000 * should start relative to the scroll position. This adjusts the position based |
|
1001 * on which direction the user is panning, so that we use our cache as |
|
1002 * effectively as possible. |
|
1003 * |
|
1004 * @param aDirection Negative means user is panning to the left or above |
|
1005 * Zero means user did not pan |
|
1006 * Positive means user is panning to the right or below |
|
1007 * @param aViewportSize The width or height of the viewport |
|
1008 * @param aCacheSize The width or height of the displayport |
|
1009 */ |
|
1010 _getRelativeCacheStart: function(aDirection, aViewportSize, aCacheSize) { |
|
1011 // Remember that this is relative to the viewport scroll position. |
|
1012 // Let's assume we are thinking about the y-axis. |
|
1013 // The extreme cases: |
|
1014 // |0| would mean that there is no content available above |
|
1015 // |aViewportSize - aCacheSize| would mean no content available below |
|
1016 // |
|
1017 // Taking the average of the extremes puts equal amounts of cache on the |
|
1018 // top and bottom of the viewport. If we think of this like a weighted |
|
1019 // average, .5 is the sweet spot where equals amounts of content are |
|
1020 // above and below the visible area. |
|
1021 // |
|
1022 // This weight is therefore how much of the cache is above (or to the |
|
1023 // left) the visible area. |
|
1024 let cachedAbove = .5; |
|
1025 |
|
1026 // If panning down, leave only 25% of the non-visible cache above. |
|
1027 if (aDirection > 0) |
|
1028 cachedAbove = .25; |
|
1029 |
|
1030 // If panning up, Leave 75% of the non-visible cache above. |
|
1031 if (aDirection < 0) |
|
1032 cachedAbove = .75; |
|
1033 |
|
1034 return (aViewportSize - aCacheSize) * cachedAbove; |
|
1035 }, |
|
1036 |
|
1037 /** Determine size of the pixel cache. */ |
|
1038 _getCacheSize: function(viewportSize) { |
|
1039 let self = this.self; |
|
1040 let contentView = this._contentView; |
|
1041 |
|
1042 let cacheWidth = self._cacheRatioWidth * viewportSize.width; |
|
1043 let cacheHeight = self._cacheRatioHeight * viewportSize.height; |
|
1044 let contentSize = this._getContentSize(); |
|
1045 let contentWidth = contentSize.width; |
|
1046 let contentHeight = contentSize.height; |
|
1047 |
|
1048 // There are common cases, such as long skinny pages, where our cache size is |
|
1049 // bigger than our content size. In those cases, we take that sliver of leftover |
|
1050 // space and apply it to the other dimension. |
|
1051 if (contentWidth < cacheWidth) { |
|
1052 cacheHeight += (cacheWidth - contentWidth) * cacheHeight / cacheWidth; |
|
1053 cacheWidth = contentWidth; |
|
1054 } else if (contentHeight < cacheHeight) { |
|
1055 cacheWidth += (cacheHeight - contentHeight) * cacheWidth / cacheHeight; |
|
1056 cacheHeight = contentHeight; |
|
1057 } |
|
1058 |
|
1059 return { width: cacheWidth, height: cacheHeight }; |
|
1060 }, |
|
1061 |
|
1062 _sendDisplayportUpdate: function(scrollX, scrollY) { |
|
1063 let self = this.self; |
|
1064 if (!self.active) |
|
1065 return; |
|
1066 |
|
1067 let contentView = this._contentView; |
|
1068 let viewportSize = this._getViewportSize(); |
|
1069 let cacheSize = this._getCacheSize(viewportSize); |
|
1070 let cacheX = this._getRelativeCacheStart(this._pixelsPannedSinceRefresh.x, viewportSize.width, cacheSize.width) + contentView.scrollX; |
|
1071 let cacheY = this._getRelativeCacheStart(this._pixelsPannedSinceRefresh.y, viewportSize.height, cacheSize.height) + contentView.scrollY; |
|
1072 let contentSize = this._getContentSize(); |
|
1073 |
|
1074 // Use our pixels efficiently and don't try to cache things outside of content |
|
1075 // boundaries (The left bound can be negative because of RTL). |
|
1076 |
|
1077 let rootScale = self.scale; |
|
1078 let leftBound = self._contentDocumentLeft * rootScale; |
|
1079 let bounds = new Rect(leftBound, 0, contentSize.width, contentSize.height); |
|
1080 let displayport = new Rect(cacheX, cacheY, cacheSize.width, cacheSize.height); |
|
1081 displayport.translateInside(bounds); |
|
1082 |
|
1083 self.messageManager.sendAsyncMessage("Content:SetCacheViewport", { |
|
1084 scrollX: Math.round(scrollX) / rootScale, |
|
1085 scrollY: Math.round(scrollY) / rootScale, |
|
1086 x: Math.round(displayport.x) / rootScale, |
|
1087 y: Math.round(displayport.y) / rootScale, |
|
1088 w: Math.round(displayport.width) / rootScale, |
|
1089 h: Math.round(displayport.height) / rootScale, |
|
1090 scale: rootScale, |
|
1091 id: contentView.id |
|
1092 }); |
|
1093 |
|
1094 this._pixelsPannedSinceRefresh.x = 0; |
|
1095 this._pixelsPannedSinceRefresh.y = 0; |
|
1096 }, |
|
1097 |
|
1098 _updateCSSViewport: function() { |
|
1099 let contentView = this._contentView; |
|
1100 this._sendDisplayportUpdate(contentView.scrollX, |
|
1101 contentView.scrollY); |
|
1102 }, |
|
1103 |
|
1104 /** |
|
1105 * The cache viewport is what parts of content is cached in the parent process for |
|
1106 * fast scrolling. This syncs that up with the current projection viewport. |
|
1107 */ |
|
1108 _updateCacheViewport: function() { |
|
1109 // Do not update scroll values for content. |
|
1110 if (this.isRoot()) |
|
1111 this._sendDisplayportUpdate(-1, -1); |
|
1112 else { |
|
1113 let contentView = this._contentView; |
|
1114 this._sendDisplayportUpdate(contentView.scrollX, |
|
1115 contentView.scrollY); |
|
1116 } |
|
1117 }, |
|
1118 |
|
1119 _getContentSize: function() { |
|
1120 let self = this.self; |
|
1121 return { width: this._contentView.contentWidth, |
|
1122 height: this._contentView.contentHeight }; |
|
1123 }, |
|
1124 |
|
1125 _getViewportSize: function() { |
|
1126 let self = this.self; |
|
1127 if (this.isRoot()) { |
|
1128 let bcr = self.getBoundingClientRect(); |
|
1129 return { width: bcr.width, height: bcr.height }; |
|
1130 } else { |
|
1131 return { width: this._contentView.viewportWidth, |
|
1132 height: this._contentView.viewportHeight }; |
|
1133 } |
|
1134 }, |
|
1135 |
|
1136 init: function(contentView) { |
|
1137 let self = this.self; |
|
1138 |
|
1139 this._contentView = contentView; |
|
1140 this._id = contentView.id; |
|
1141 this._scale = 1; |
|
1142 self._contentViews[this._id] = this; |
|
1143 |
|
1144 if (!this.isRoot()) { |
|
1145 // Non-root content views are short lived. |
|
1146 this._timeout = setTimeout(this._dieIfOld.bind(this), this.kDieTime); |
|
1147 // This iframe may not have a display port yet, so build up a cache |
|
1148 // immediately. |
|
1149 this._updateCacheViewport(); |
|
1150 } |
|
1151 }, |
|
1152 |
|
1153 isRoot: function() { |
|
1154 return this.self._contentViewManager.rootContentView == this._contentView; |
|
1155 }, |
|
1156 |
|
1157 scrollBy: function(x, y) { |
|
1158 let self = this.self; |
|
1159 |
|
1160 // Bounding content rectangle is in device pixels |
|
1161 let contentView = this._contentView; |
|
1162 let viewportSize = this._getViewportSize(); |
|
1163 let contentSize = this._getContentSize(); |
|
1164 // Calculate document dimensions in device pixels |
|
1165 let scrollRangeX = contentSize.width - viewportSize.width; |
|
1166 let scrollRangeY = contentSize.height - viewportSize.height; |
|
1167 |
|
1168 let leftOffset = self._contentDocumentLeft * this._scale; |
|
1169 x = Math.floor(Math.max(leftOffset, Math.min(scrollRangeX + leftOffset, contentView.scrollX + x))) - contentView.scrollX; |
|
1170 y = Math.floor(Math.max(0, Math.min(scrollRangeY, contentView.scrollY + y))) - contentView.scrollY; |
|
1171 |
|
1172 if (x == 0 && y == 0) |
|
1173 return; |
|
1174 |
|
1175 contentView.scrollBy(x, y); |
|
1176 |
|
1177 this._lastPanTime = Date.now(); |
|
1178 |
|
1179 this._pixelsPannedSinceRefresh.x += x; |
|
1180 this._pixelsPannedSinceRefresh.y += y; |
|
1181 if (Math.abs(this._pixelsPannedSinceRefresh.x) > 20 || |
|
1182 Math.abs(this._pixelsPannedSinceRefresh.y) > 20) |
|
1183 this._updateCacheViewport(); |
|
1184 }, |
|
1185 |
|
1186 scrollTo: function(x, y) { |
|
1187 let contentView = this._contentView; |
|
1188 this.scrollBy(x - contentView.scrollX, y - contentView.scrollY); |
|
1189 }, |
|
1190 |
|
1191 _setScale: function _setScale(scale) { |
|
1192 this._scale = scale; |
|
1193 this._contentView.setScale(scale, scale); |
|
1194 }, |
|
1195 |
|
1196 getPosition: function() { |
|
1197 let contentView = this._contentView; |
|
1198 return { x: contentView.scrollX, y: contentView.scrollY }; |
|
1199 } |
|
1200 }) |
|
1201 ]]> |
|
1202 </field> |
|
1203 |
|
1204 <!-- The ratio of CSS pixels to device pixels. --> |
|
1205 <property name="scale"> |
|
1206 <getter><![CDATA[ |
|
1207 return this.getRootView()._scale; |
|
1208 ]]></getter> |
|
1209 <setter><![CDATA[ |
|
1210 if (val <= 0 || val == this.scale) |
|
1211 return; |
|
1212 |
|
1213 let rootView = this.getRootView(); |
|
1214 rootView._setScale(val); |
|
1215 |
|
1216 return val; |
|
1217 ]]></setter> |
|
1218 </property> |
|
1219 |
|
1220 <method name="_getView"> |
|
1221 <parameter name="contentView"/> |
|
1222 <body> |
|
1223 <![CDATA[ |
|
1224 if (!contentView) return null; |
|
1225 |
|
1226 // See if we have cached it. |
|
1227 let id = contentView.id; |
|
1228 let jsContentView = this._contentViews[id]; |
|
1229 if (jsContentView) { |
|
1230 // Content view may have changed if it became inactive for a |
|
1231 // little while. |
|
1232 jsContentView._contentView = contentView; |
|
1233 return jsContentView; |
|
1234 } |
|
1235 |
|
1236 // Not cached. Create it. |
|
1237 jsContentView = Object.create(this._contentViewPrototype); |
|
1238 jsContentView.init(contentView); |
|
1239 return jsContentView; |
|
1240 ]]> |
|
1241 </body> |
|
1242 </method> |
|
1243 |
|
1244 <!-- Get root content view. --> |
|
1245 <method name="getRootView"> |
|
1246 <body> |
|
1247 <![CDATA[ |
|
1248 let contentView = this._contentViewManager.rootContentView; |
|
1249 return this._getView(contentView) || this._contentNoop; |
|
1250 ]]> |
|
1251 </body> |
|
1252 </method> |
|
1253 |
|
1254 <!-- Get contentView for position (x, y) relative to the browser element --> |
|
1255 <method name="getViewAt"> |
|
1256 <parameter name="x"/> |
|
1257 <parameter name="y"/> |
|
1258 <body> |
|
1259 <![CDATA[ |
|
1260 let manager = this._contentViewManager; |
|
1261 let contentView = manager.getContentViewsIn(x, y, 0, 0, 0, 0)[0] || |
|
1262 manager.rootContentView; |
|
1263 return this._getView(contentView); |
|
1264 ]]> |
|
1265 </body> |
|
1266 </method> |
|
1267 |
|
1268 <!-- Synchronize the CSS viewport with the projection viewport. --> |
|
1269 <method name="_updateCSSViewport"> |
|
1270 <body> |
|
1271 <![CDATA[ |
|
1272 let rootView = this.getRootView(); |
|
1273 rootView._updateCSSViewport(); |
|
1274 ]]> |
|
1275 </body> |
|
1276 </method> |
|
1277 |
|
1278 <property name="active" onget="return this._active;"> |
|
1279 <setter><![CDATA[ |
|
1280 this._active = val; |
|
1281 let keepVisible = false; |
|
1282 this.messageManager.sendAsyncMessage((val ? "Content:Activate" : "Content:Deactivate"), { keepviewport: keepVisible }); |
|
1283 if (val) |
|
1284 this.getRootView()._updateCacheViewport(); |
|
1285 ]]></setter> |
|
1286 </property> |
|
1287 |
|
1288 <field name="_remoteFinder">null</field> |
|
1289 <property name="finder" readonly="true"> |
|
1290 <getter><![CDATA[ |
|
1291 if (!this._remoteFinder) { |
|
1292 let jsm = "resource://gre/modules/RemoteFinder.jsm"; |
|
1293 let RemoteFinder = Cu.import(jsm, {}).RemoteFinder; |
|
1294 this._remoteFinder = new RemoteFinder(this); |
|
1295 } |
|
1296 return this._remoteFinder; |
|
1297 ]]></getter> |
|
1298 </property> |
|
1299 </implementation> |
|
1300 |
|
1301 </binding> |
|
1302 |
|
1303 </bindings> |