browser/metro/base/content/bindings/browser.xml

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:5ccc6a9efdee
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>

mercurial