<?xml version="1.0"?>

<!DOCTYPE bindings SYSTEM "chrome://mozapps/locale/update/updates.dtd">

<bindings id="updatesBindings"
          xmlns="http://www.mozilla.org/xbl"
          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
          xmlns:xbl="http://www.mozilla.org/xbl">
  
  <binding id="remotecontent">
    <content>
      <xul:vbox flex="1" class="loadingBox">
        <xul:hbox align="start">
          <xul:image class="remoteLoadingThrobber" xbl:inherits="state"/>
          <xul:vbox flex="1">
            <xul:description flex="1" class="remoteLoadingMessage" crop="right"/>
          </xul:vbox>
        </xul:hbox>
      </xul:vbox>
      <xul:vbox flex="1">
        <xul:browser class="remoteContent" type="content" disablehistory="true"
                     flex="1"/>
      </xul:vbox>
      <xul:stringbundle anonid="strings" 
                        src="chrome://mozapps/locale/update/updates.properties"/>
    </content>
    <implementation>
      <constructor><![CDATA[
        // just to be safe, turn off js, auth, plugins, and subframes
        var docShell = this._content.docShell;
        docShell.allowJavascript = false;
        docShell.allowAuth = false;
        docShell.allowPlugins = false;
        //allow redirects
        //docShell.allowMetaRedirects = false;
        docShell.allowSubframes = false;
        //allow images
        //docShell.allowImages = false;
      ]]></constructor>

      <destructor><![CDATA[
        // clean up the listener but you may not have one if you never showed
        // the page with a <remotecontent> element
        if (this._remoteProgressListener) 
          this._content.webProgress
              .removeProgressListener(this._remoteProgressListener);
      ]]></destructor>

      <field name="_message">
        document.getAnonymousElementByAttribute(this, "class", "remoteLoadingMessage");
      </field>
      <field name="_content">
        document.getAnonymousElementByAttribute(this, "class", "remoteContent");
      </field>
      <field name="_strings">
        document.getAnonymousElementByAttribute(this, "anonid", "strings");
      </field>

      <field name="update_name">null</field>
      <field name="update_version">null</field>
      <field name="_remoteProgressListener">null</field>

      <method name="_setMessageValue">
        <parameter name="value"/>
        <body><![CDATA[
          while (this._message.hasChildNodes())
            this._message.removeChild(this._message.firstChild);
          this._message.appendChild(document.createTextNode(value));
        ]]></body>
      </method>

      <method name="onError">
        <body><![CDATA[
          this.setAttribute("selectedIndex", "0");

          var errorPropertyName = this.getAttribute("id") + "NotFound";
          var statusText = this._strings.getFormattedString(errorPropertyName, 
                                                            [this.update_name]);
          this._setMessageValue(statusText);
          this.setAttribute("state", "error");
          var e = document.createEvent("Events");
          e.initEvent("load", false, true);
          this.dispatchEvent(e);
        ]]></body>
      </method>

      <method name="onLoad">
        <body><![CDATA[
          var doc = this._content.contentDocument;
          var type = this.getAttribute("remotetype");
          if (!doc.body.hasAttribute(type)) {
            this.onError();
            return;
          }
          // The remote html can tell us to zoom out the page if the page
          // will create scrollbars by adding a zoomOutToFit attribute to the
          // body of the billboard's html. Since zooming out will not always
          // remove the scrollbars even though the page fits in the client
          // region after zooming out the web page should hide the scrollbar(s)
          // using css. If you choose to use zoomOutToFit you should also verify
          // it is working the way you expect it to with the billboard you have
          // created. Possible values for zoomOutToFit are:
          // both: zoom out if the width or height is greater than the client
          //       region.
          // height: zoom out if the height is greater than the client region.
          // width: zoom out if the width is greater than the client region.
          if (doc.body.hasAttribute("zoomOutToFit")) {
            let zoomAdjustWidth = 1;
            let zoomAdjustHeight = 1;
            let zoomOutType = doc.body.getAttribute("zoomOutToFit");
            if ((zoomOutType == "both" || zoomOutType == "width") &&
                doc.width > this.clientWidth)
              zoomAdjustWidth = Math.floor((this.clientWidth / doc.width) * 100) * .01;

            if ((zoomOutType == "both" || zoomOutType == "height") &&
                doc.height > this.clientHeight)
              zoomAdjustHeight = Math.floor((this.clientHeight / doc.height) * 100) * .01;

            var zoomAdjust = zoomAdjustWidth < zoomAdjustHeight ? zoomAdjustWidth
                                                                : zoomAdjustHeight;
            if (zoomAdjust < 1)
              this._content.markupDocumentViewer.fullZoom = zoomAdjust;
          }

          this.setAttribute("selectedIndex", "1");
          this.setAttribute("state", "loaded");

          var e = document.createEvent("Events");
          e.initEvent("load", false, true);
          this.dispatchEvent(e);
        ]]></body>
      </method>

      <method name="onStart">
        <body><![CDATA[
          this.setAttribute("selectedIndex", "0");

          this.setAttribute("state", "loading");
          var loadingPropertyName = this.getAttribute("id") + "Downloading";
          this._setMessageValue(this._strings.getFormattedString(
            loadingPropertyName, [this.update_name, this.update_version]));
        ]]></body>
      </method>

      <property name="url">
        <getter><![CDATA[
          return this.getAttribute("url");
        ]]></getter>
        <setter><![CDATA[
          if (this._remoteProgressListener) 
            this._content.webProgress
                .removeProgressListener(this._remoteProgressListener);

          var self = this;

          this._remoteProgressListener = {
            QueryInterface: function(aIID) 
            {
              if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
                  aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
                  aIID.equals(Components.interfaces.nsISupports))
                return this;
              throw Components.results.NS_NOINTERFACE;
            },
            onLocationChange: function() {},
            onProgressChange: function() {},
            onStatusChange:   function() {},
            onSecurityChange: function() {},
            onStateChange:    function(aWebProgress, aRequest, 
                                       aStateFlags, aStatus) 
            {
              if (!aRequest)
                return;

              const nsIWebProgressListener = 
                Components.interfaces.nsIWebProgressListener;
 
              if (aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) 
              {
                if (aStateFlags & nsIWebProgressListener.STATE_START) 
                  self.onStart();
                else if (aStateFlags & nsIWebProgressListener.STATE_STOP)
                {
                  const nsIHttpChannel = Components.interfaces.nsIHttpChannel;
                  try {
                    var channel = aRequest.QueryInterface(nsIHttpChannel);
                    if (channel.requestSucceeded) 
                    {
                      // if we get here, the load was OK, report success
                      self.onLoad();
                      return;
                    }
                  }
                  catch (ex) 
                  {
                    // the channel.requestSucceeded can throw
                    // NS_ERROR_NOT_AVAILABLE if you can't reach the server
                    // so we treat that as an error
                  }

                  // everything else (like a 404) is an error
                  self.onError();
                }
              }
            }
          }

          this._content.webProgress
              .addProgressListener(this._remoteProgressListener,
               Components.interfaces.nsIWebProgress.NOTIFY_ALL);

          // load the url now that the listener is registered
          this._content.setAttribute("src", val);
        ]]></setter>
      </property>

      <property name="testFileUrl">
        <getter><![CDATA[
          return this.getAttribute("testFileUrl");
        ]]></getter>
        <setter><![CDATA[
          this._content.setAttribute("src", val);
          this.onLoad()
        ]]></setter>
      </property>

      <method name="stopDownloading">
        <body><![CDATA[
          this._content.stop();
        ]]></body>
      </method>
    </implementation>
  </binding>

  <binding id="updateheader" extends="chrome://global/content/bindings/wizard.xml#wizard-header">
    <resources>
      <stylesheet src="chrome://global/skin/wizard.css"/>
    </resources>
    <content>
      <xul:hbox class="wizard-header update-header" flex="1">
        <xul:vbox class="wizard-header-box-1">
          <xul:vbox class="wizard-header-box-text">
            <xul:label class="wizard-header-label" xbl:inherits="value=label"/>
          </xul:vbox>
        </xul:vbox>
      </xul:hbox>
    </content>
  </binding>

  <binding id="update" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
    <content>
      <xul:hbox>
        <xul:label class="update-name" xbl:inherits="value=name" flex="1" crop="right"/>
        <xul:label xbl:inherits="href=detailsURL,hidden=hideDetailsURL" class="text-link"
                   value="&update.details.label;"/>
      </xul:hbox>
      <xul:label class="update-type" xbl:inherits="value=type"/>
      <xul:grid>
        <xul:columns>
          <xul:column class="update-label-column"/>
          <xul:column flex="1"/>
        </xul:columns>
        <xul:rows>
          <xul:row>
            <xul:label class="update-installedOn-label">&update.installedOn.label;</xul:label>
            <xul:label class="update-installedOn-value" xbl:inherits="value=installDate" flex="1" crop="right"/>
          </xul:row>
          <xul:row>
            <xul:label class="update-status-label">&update.status.label;</xul:label>
            <xul:description class="update-status-value" flex="1"/>
          </xul:row>
        </xul:rows>
      </xul:grid>
    </content>
    <implementation>
      <property name="name"
                onget="return this.getAttribute('name');"
                onset="this.setAttribute('name', val); return val;"/>
      <property name="detailsURL"
                onget="return this.getAttribute('detailsURL');"
                onset="this.setAttribute('detailsURL', val); return val;"/>
      <property name="installDate"
                onget="return this.getAttribute('installDate');"
                onset="this.setAttribute('installDate', val); return val;"/>
      <property name="type"
                onget="return this.getAttribute('type');"
                onset="this.setAttribute('type', val); return val;"/>
      <property name="hideDetailsURL"
                onget="return this.getAttribute('hideDetailsURL');"
                onset="this.setAttribute('hideDetailsURL', val); return val;"/>
      <property name="status"
                onget="return this.getAttribute('status');">
        <setter><![CDATA[
          this.setAttribute("status", val);
          var field = document.getAnonymousElementByAttribute(this, "class", "update-status-value");
          while (field.hasChildNodes())
            field.removeChild(field.firstChild);
          field.appendChild(document.createTextNode(val));
          return val;
        ]]></setter>
      </property>
    </implementation>
  </binding>
</bindings>

