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

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     1 <?xml version="1.0"?>
     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/. -->
     7 <!DOCTYPE bindings [
     8   <!ENTITY % findBarDTD SYSTEM "chrome://global/locale/findbar.dtd" >
     9   %findBarDTD;
    10 ]>
    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">
    16   <binding id="local-browser" extends="chrome://global/content/bindings/browser.xml#browser">
    17     <implementation type="application/javascript"
    18       implements="nsIObserver, nsIDOMEventListener, nsIMessageListener">
    20       <constructor>
    21         <![CDATA[
    22           this._frameLoader =
    23             this.QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader;
    24           this._contentViewManager =
    25             this._frameLoader.QueryInterface(Components.interfaces.nsIContentViewManager);
    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);
    35           if (this._contentViewPrototype) {
    36             this._contentViewPrototype.kDieTime = prefService.getIntPref("toolkit.browser.contentViewExpire");
    37           }
    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);
    48           this._webProgress._init();
    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);
    55           this.setAttribute("autoscrollpopup", "autoscrollerid");
    56         ]]>
    57       </constructor>
    59       <field name="_searchEngines">[]</field>
    60       <property name="searchEngines"
    61                 onget="return this._searchEngines"
    62                 readonly="true"/>
    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"/>
    69       <field name="contentWindowId">null</field>
    71       <field name="_contentTitle">null</field>
    73       <field name="_ios">
    74          Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
    75       </field>
    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;
   157             let bcr = this.getBoundingClientRect();
   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             }
   167             let scale = 1;
   168             if (!ignoreScale) {
   169               scale = this.scale;
   170             }
   172             return {
   173               x: (aClientX - bcr.left) / scale + scrollX,
   174               y: (aClientY - bcr.top) / scale + scrollY
   175             };
   176           ]]>
   177         </body>
   178       </method>
   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;
   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             }
   197             let scale = 1;
   198             if (!ignoreScale) {
   199               scale = this.scale;
   200             }
   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>
   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>
   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>
   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;
   250             let bcr = this.getBoundingClientRect();
   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             }
   260             let scale = 1;
   261             if (!ignoreScale) {
   262               scale = this.scale;
   263             }
   265             return {
   266               x: (aBrowserX * scale - scrollX + bcr.left),
   267               y: (aBrowserY * scale - scrollY + bcr.top)
   268             };
   269           ]]>
   270         </body>
   271       </method>
   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>
   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>
   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>
   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>
   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>
   347       <field name="_messageListenerLocal"><![CDATA[
   348         ({
   349           self: this,
   350           receiveMessage: function receiveMessage(aMessage) {
   351             let self = this.self;
   352             let json = aMessage.json;
   354             switch (aMessage.name) {
   355               case "DOMPopupBlocked":
   356                 self.onPopupBlocked(aMessage);
   357                 break;
   359               case "pageshow":
   360                 self.onPageShow(aMessage);
   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;
   373               case "pagehide":
   374                 self.onPageHide(aMessage);
   375                 break;
   377               case "DOMTitleChanged":
   378                 self._contentTitle = json.title;
   379                 break;
   381               case "DOMLinkAdded":
   382                 // ignore results from subdocuments
   383                 if (json.windowId != self.contentWindowId)
   384                   return;
   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;
   397               case "MozScrolledAreaChanged": {
   398                 self._contentDocumentWidth = json.width;
   399                 self._contentDocumentHeight = json.height;
   400                 self._contentDocumentLeft = (json.left < 0) ? json.left : 0;
   402                 // Recalculate whether the visible area is actually in bounds
   403                 let view = self.getRootView();
   404                 view.scrollBy(0, 0);
   405                 break;
   406               }
   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>
   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;
   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>
   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>
   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           }
   460           return type;
   461         ]]></body>
   462       </method>
   464       <field name="_webProgress"><![CDATA[
   465         ({
   466           _browser: this,
   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           },
   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;
   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) {}
   490                 if (this._browser.updateWindowId(json.contentWindowId)) {
   491                   this._browser._documentURI = json.documentURI;
   492                   this._browser._searchEngines = [];
   493                 }
   494                 break;
   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                 }
   509                 let data = this._getIdentityData(SSLStatus);
   510                 this._browser.updateWindowId(json.contentWindowId);
   511                 break;
   512             }
   513           },
   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 = {};
   522             if (status) {
   523               let cert = status.serverCert;
   525               // Human readable name of Subject
   526               result.subjectOrg = cert.organization;
   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);
   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               }
   543               // Human readable name of Certificate Authority
   544               result.caOrg =  cert.issuerOrganization || cert.issuerCommonName;
   546               if (!this._overrideService)
   547                 this._overrideService = Components.classes["@mozilla.org/security/certoverride;1"]
   548                                         .getService(Components.interfaces.nsICertOverrideService);
   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             }
   559             return result;
   560           }
   561         })
   562       ]]></field>
   564       <property name="webProgress"
   565                 readonly="true"
   566                 onget="return null"/>
   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>
   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;
   605             this._contentWindowWidth = aMessage.json.contentWindowWidth;
   606             this._contentWindowHeight = aMessage.json.contentWindowHeight;
   607          ]]>
   608         </body>
   609       </method>
   611       <method name="onPopupBlocked">
   612         <parameter name="aMessage"/>
   613         <body>
   614           <![CDATA[
   615             if (!this.pageReport) {
   616               this.pageReport = [];
   617             }
   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             };
   629             this.pageReport.push(obj);
   630             this.pageReport.reported = false;
   631             this.updatePageReport();
   632           ]]>
   633         </body>
   634       </method>
   636       <field name="_frameLoader">null</field>
   637       <field name="_contentViewManager">null</field>
   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>
   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"/>
   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"/>
   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>
   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>
   681       <!-- Used in remote tabs only. -->
   682       <method name="_updateCSSViewport">
   683         <body/>
   684       </method>
   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             });
   699             // If the window size is changing, make sure the displayport is in sync
   700             this.getRootView()._updateCacheViewport();
   701           ]]>
   702         </body>
   703       </method>
   705       <method name="getRootView">
   706         <body>
   707           <![CDATA[
   708             return this._contentView;
   709           ]]>
   710         </body>
   711       </method>
   713       <field name="_contentViewPrototype"><![CDATA[
   714         ({
   715           _scrollbox: null,
   717           init: function(aElement) {
   718             this._scrollbox = aElement.scrollBoxObject;
   719           },
   721           isRoot: function() {
   722             return false;
   723           },
   725           scrollBy: function(x, y) {
   726             this._scrollbox.scrollBy(x,y);
   727           },
   729           scrollTo: function(x, y) {
   730             this._scrollbox.scrollTo(x,y);
   731           },
   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>
   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);
   751             while (elt && !elt.scrollBoxObject)
   752               elt = elt.parentNode;
   754             if (!elt)
   755               return this._contentView;
   757             let cv = Object.create(this._contentViewPrototype);
   758             cv.init(elt);
   759             return cv;
   760           ]]>
   761         </body>
   762       </method>
   764       <field name="_contentView"><![CDATA[
   765         ({
   766           self: this,
   768           _updateCacheViewport: function() {
   769           },
   771           isRoot: function() {
   772             return true;
   773           },
   775           scrollBy: function(x, y) {
   776             let self = this.self;
   777             self.contentWindow.scrollBy(x, y);
   778           },
   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           },
   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           },
   796           toString: function() {
   797             return "[View Local]";
   798           }
   799         })
   800         ]]>
   801       </field>
   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>
   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>
   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>
   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>
   846       <field name="_contentTitle">null</field>
   848       <property name="contentTitle"
   849                 onget="return this._contentTitle;"
   850                 readonly="true"/>
   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>
   866       <property name="contentWindow"
   867                 readonly="true"
   868                 onget="return null"/>
   870       <property name="sessionHistory"
   871                 onget="return null"
   872                 readonly="true"/>
   874       <property name="markupDocumentViewer"
   875                 onget="return null"
   876                 readonly="true"/>
   878       <property name="contentViewerEdit"
   879                 onget="return null"
   880                 readonly="true"/>
   882       <property name="contentViewerFile"
   883                 onget="return null"
   884                 readonly="true"/>
   886       <field name="_charset"></field>
   888       <constructor>
   889         <![CDATA[
   890           this.messageManager.addMessageListener("scroll", this._messageListenerRemote);
   891         ]]>
   892       </constructor>
   894       <field name="scrollSync">true</field>
   896       <field name="_messageListenerRemote"><![CDATA[
   897         ({
   898           self: this,
   899           receiveMessage: function receiveMessage(aMessage) {
   900             let self = this.self;
   901             let json = aMessage.json;
   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          },
   914          doScroll: function doScroll(aX, aY, aCount) {
   915             let self = this.self;
   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));
   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>
   931       <!-- Keep a store of temporary content views. -->
   932       <field name="_contentViews">({})</field>
   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() {},
   942           isRoot: function() {
   943             return true;
   944           },
   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>
   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,
   965           kDieTime: 3000,
   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           },
   982           /** Cleanup after ourselves. */
   983           _die: function() {
   984             let timeout = this._timeout;
   985             if (timeout) {
   986               clearTimeout(timeout);
   987               this._timeout = null;
   988             }
   990             if (this._contentView && Math.abs(this._pixelsPannedSinceRefresh) > 0)
   991               this._updateCacheViewport();
   993             // We expect contentViews to contain our ID. If not, something bad
   994             // happened.
   995             delete this.self._contentViews[this._id];
   996           },
   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.
  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;
  1026             // If panning down, leave only 25% of the non-visible cache above.
  1027             if (aDirection > 0)
  1028               cachedAbove = .25;
  1030             // If panning up, Leave 75% of the non-visible cache above.
  1031             if (aDirection < 0)
  1032               cachedAbove = .75;
  1034             return (aViewportSize - aCacheSize) * cachedAbove;
  1035           },
  1037           /** Determine size of the pixel cache. */
  1038           _getCacheSize: function(viewportSize) {
  1039             let self = this.self;
  1040             let contentView = this._contentView;
  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;
  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;
  1059             return { width: cacheWidth, height: cacheHeight };
  1060           },
  1062           _sendDisplayportUpdate: function(scrollX, scrollY) {
  1063             let self = this.self;
  1064             if (!self.active)
  1065                return;
  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();
  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).
  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);
  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             });
  1094             this._pixelsPannedSinceRefresh.x = 0;
  1095             this._pixelsPannedSinceRefresh.y = 0;
  1096           },
  1098           _updateCSSViewport: function() {
  1099             let contentView = this._contentView;
  1100             this._sendDisplayportUpdate(contentView.scrollX,
  1101                                         contentView.scrollY);
  1102           },
  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);
  1117           },
  1119           _getContentSize: function() {
  1120             let self = this.self;
  1121             return { width: this._contentView.contentWidth,
  1122                      height: this._contentView.contentHeight };
  1123           },
  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 };
  1134           },
  1136           init: function(contentView) {
  1137             let self = this.self;
  1139             this._contentView = contentView;
  1140             this._id = contentView.id;
  1141             this._scale = 1;
  1142             self._contentViews[this._id] = this;
  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();
  1151           },
  1153           isRoot: function() {
  1154             return this.self._contentViewManager.rootContentView == this._contentView;
  1155           },
  1157           scrollBy: function(x, y) {
  1158             let self = this.self;
  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;
  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;
  1172             if (x == 0 && y == 0)
  1173               return;
  1175             contentView.scrollBy(x, y);
  1177             this._lastPanTime = Date.now();
  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           },
  1186           scrollTo: function(x, y) {
  1187             let contentView = this._contentView;
  1188             this.scrollBy(x - contentView.scrollX, y - contentView.scrollY);
  1189           },
  1191           _setScale: function _setScale(scale) {
  1192             this._scale = scale;
  1193             this._contentView.setScale(scale, scale);
  1194           },
  1196           getPosition: function() {
  1197             let contentView = this._contentView;
  1198             return { x: contentView.scrollX, y: contentView.scrollY };
  1200         })
  1201         ]]>
  1202       </field>
  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;
  1213           let rootView = this.getRootView();
  1214           rootView._setScale(val);
  1216           return val;
  1217         ]]></setter>
  1218       </property>
  1220       <method name="_getView">
  1221         <parameter name="contentView"/>
  1222         <body>
  1223           <![CDATA[
  1224             if (!contentView) return null;
  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;
  1236             // Not cached. Create it.
  1237             jsContentView = Object.create(this._contentViewPrototype);
  1238             jsContentView.init(contentView);
  1239             return jsContentView;
  1240           ]]>
  1241         </body>
  1242       </method>
  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>
  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>
  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>
  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>
  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);
  1296           return this._remoteFinder;
  1297         ]]></getter>
  1298       </property>
  1299     </implementation>
  1301   </binding>
  1303 </bindings>

mercurial