<?xml version="1.0"?>

<!--
   - The contents of this file are subject to the Mozilla Public
   - License Version 1.1 (the "License"); you may not use this file
   - except in compliance with the License. You may obtain a copy of
   - the License at http://www.mozilla.org/MPL/
   -
   - Software distributed under the License is distributed on an "AS
   - IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
   - implied. See the License for the specific language governing
   - rights and limitations under the License.
   -
   - The Original Code is this file as it was released on
   - March 28, 2001.
   -
   - The Initial Developer of the Original Code is Peter Annema.
   - Portions created by Peter Annema are Copyright (C) 2001
   - Peter Annema.  All Rights Reserved.
   -
   - Contributor(s):
   -   Peter Annema <disttsc@bart.nl> (Original Author of <browser>)
   -
   - Alternatively, the contents of this file may be used under the
   - terms of the GNU General Public License Version 2 or later (the
   - "GPL"), in which case the provisions of the GPL are applicable
   - instead of those above.  If you wish to allow use of your
   - version of this file only under the terms of the GPL and not to
   - allow others to use your version of this file under the MPL,
   - indicate your decision by deleting the provisions above and
   - replace them with the notice and other provisions required by
   - the GPL.  If you do not delete the provisions above, a recipient
   - may use your version of this file under either the MPL or the
   - GPL.
  -->

<!DOCTYPE window [
  <!ENTITY % findBarDTD SYSTEM "chrome://global/locale/findBar.dtd" >
  %findBarDTD;
]>

<bindings id="browserBindings"
          xmlns="http://www.mozilla.org/xbl"
          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

  <binding id="browser" extends="xul:browser">
    <implementation type="application/x-javascript" implements="nsIAccessibleProvider, nsIObserver">
      <property name="accessible">
        <getter>
          <![CDATA[
            var accService = Components.classes["@mozilla.org/accessibilityService;1"].getService(Components.interfaces.nsIAccessibilityService);
            return accService.createOuterDocAccessible(this);
          ]]>
        </getter>
      </property>

      <property name="autoscrollEnabled">
        <getter>
          <![CDATA[
            if (this.getAttribute("autoscroll") == "false")
              return false;
            
            var enabled = true;
            try {
              enabled = this.mPrefs.getBoolPref("general.autoScroll");
            }
            catch(ex) {
            }
            
            return enabled;
          ]]>
        </getter>
      </property>

      <property name="canGoBack"
                onget="return this.webNavigation.canGoBack;"
                readonly="true"/>

      <property name="canGoForward"
                onget="return this.webNavigation.canGoForward;"
                readonly="true"/>

      <method name="goBack">
        <body>
          <![CDATA[
            var webNavigation = this.webNavigation;
            if (webNavigation.canGoBack)
              webNavigation.goBack();
          ]]>
        </body>
      </method>

      <method name="goForward">
        <body>
          <![CDATA[
            var webNavigation = this.webNavigation;
            if (webNavigation.canGoForward)
              webNavigation.goForward();
          ]]>
        </body>
      </method>

      <method name="reload">
        <body>
          <![CDATA[
            const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
            const flags = nsIWebNavigation.LOAD_FLAGS_NONE;
            this.reloadWithFlags(flags);
          ]]>
        </body>
      </method>

      <method name="reloadWithFlags">
        <parameter name="aFlags"/>
        <body>
          <![CDATA[
            this.webNavigation.reload(aFlags);
          ]]>
        </body>
      </method>

      <method name="stop">
        <body>
          <![CDATA[
            const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
            const flags = nsIWebNavigation.STOP_ALL;
            this.webNavigation.stop(flags);
          ]]>
        </body>
      </method>

      <!-- throws exception for unknown schemes -->
      <method name="loadURI">
        <parameter name="aURI"/>
        <parameter name="aReferrerURI"/>
        <parameter name="aCharset"/>
        <body>
          <![CDATA[
            const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
            const flags = nsIWebNavigation.LOAD_FLAGS_NONE;
            this.loadURIWithFlags(aURI, flags, aReferrerURI, aCharset);
          ]]>
        </body>
      </method>

      <!-- throws exception for unknown schemes -->
      <method name="loadURIWithFlags">
        <parameter name="aURI"/>
        <parameter name="aFlags"/>
        <parameter name="aReferrerURI"/>
        <parameter name="aCharset"/>
        <parameter name="aPostData"/>
        <body>
          <![CDATA[
            if (!aURI)
              aURI = "about:blank";

            if (aCharset) {
              try { 
                this.documentCharsetInfo.parentCharset = this.mAtomService.getAtom(aCharset);
              }
              catch (e) {
              }
            }
            this.mFavIconURL = null;
            this.webNavigation.loadURI(aURI, aFlags, aReferrerURI, aPostData, null);
          ]]>
        </body>
      </method>

      <method name="goHome">
        <body>
          <![CDATA[
            try {
              this.loadURI(this.homePage);
            }
            catch (e) {
            }
          ]]>
        </body>
      </method>

      <property name="homePage">
        <getter>
          <![CDATA[
            var uri;

            if (this.hasAttribute("homepage"))
              uri = this.getAttribute("homepage");
            else
              uri = "http://www.mozilla.org/"; // widget pride

            return uri;
          ]]>
        </getter>
        <setter>
          <![CDATA[
            this.setAttribute("homepage", val);
            return val;
          ]]>
        </setter>
      </property>

      <method name="gotoIndex">
        <parameter name="aIndex"/>
        <body>
          <![CDATA[
            this.webNavigation.gotoIndex(aIndex);
          ]]>
        </body>
      </method>

      <property name="currentURI"
                onget="return this.webNavigation.currentURI;"
                readonly="true"/>

      <property name="preferences"
                onget="return Components.classes['@mozilla.org/preferences-service;1'].getService(Components.interfaces.nsIPrefService);"
                readonly="true"/>

      <property name="docShell"
                onget="return this.boxObject.QueryInterface(Components.interfaces.nsIBrowserBoxObject).docShell;"
                readonly="true"/>

      <property name="webNavigation"
                onget="return this.docShell.QueryInterface(Components.interfaces.nsIWebNavigation);"
                readonly="true"/>

      <field name="_webBrowserFind">null</field>

      <property name="webBrowserFind"
                readonly="true">
        <getter>
        <![CDATA[
          if (!this._webBrowserFind)
            this._webBrowserFind = this.docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIWebBrowserFind);
          return this._webBrowserFind;
        ]]>
        </getter>
      </property>

      <field name="_fastFind">null</field>
      <property name="fastFind"
                readonly="true">
        <getter>
        <![CDATA[
          if (!this._fastFind) {
            var n = this.parentNode;
            while (n && n.localName != "tabbrowser")
              n = n.parentNode;

            if (n && n.mCurrentBrowser == this)
              return this._fastFind = n.fastFind

            this._fastFind = Components.classes["@mozilla.org/typeaheadfind;1"]
                                       .createInstance(Components.interfaces.nsITypeAheadFind);                                
            this._fastFind.init(this.docShell);
          }
          return this._fastFind;
        ]]>
        </getter>
      </property>
      
      <property name="findString" readonly="true">
        <getter>
        <![CDATA[
          return this.fastFind.searchString;
        ]]>
        </getter>
      </property>
      
      <property name="webProgress"
                readonly="true"
                onget="return this.docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIWebProgress);"/>

      <property name="contentWindow"
                readonly="true"
                onget="return this.docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindow);"/>

      <property name="sessionHistory"
                onget="return this.webNavigation.sessionHistory;"
                readonly="true"/>

      <property name="markupDocumentViewer"
                onget="return this.docShell.contentViewer.QueryInterface(Components.interfaces.nsIMarkupDocumentViewer);"
                readonly="true"/>

      <property name="contentViewerEdit"
                onget="return this.docShell.contentViewer.QueryInterface(Components.interfaces.nsIContentViewerEdit);"
                readonly="true"/>

      <property name="contentViewerFile"
                onget="return this.docShell.contentViewer.QueryInterface(Components.interfaces.nsIContentViewerFile);"
                readonly="true"/>

      <property name="documentCharsetInfo"
                onget="return this.docShell.documentCharsetInfo;"
                readonly="true"/>

      <property name="contentDocument"
                onget="return this.webNavigation.document;"
                readonly="true"/>

      <property name="contentTitle"
                onget="return Components.lookupMethod(this.contentDocument, 'title').call(this.contentDocument);"
                readonly="true"/>

      <field name="mPrefs" readonly="true">
        Components.classes['@mozilla.org/preferences-service;1']
                  .getService(Components.interfaces.nsIPrefService)
                  .getBranch(null);
      </field>

      <field name="mAtomService" readonly="true">
        Components.classes['@mozilla.org/atom-service;1']
                  .getService(Components.interfaces.nsIAtomService);
      </field>

      <field name="_mStrBundle">null</field>

      <property name="mStrBundle">
        <getter>
        <![CDATA[
          if (!this._mStrBundle) {
            // need to create string bundle manually instead of using <xul:stringbundle/>
            // see bug 63370 for details
            var localeService = Components.classes["@mozilla.org/intl/nslocaleservice;1"]
                                  .getService(Components.interfaces.nsILocaleService);
            var stringBundleService = Components.classes["@mozilla.org/intl/stringbundle;1"]
                                  .getService(Components.interfaces.nsIStringBundleService);
            var bundleURL = "chrome://global/locale/tabbrowser.properties";
            this._mStrBundle = stringBundleService.createBundle(bundleURL, localeService.getApplicationLocale());
          }
          return this._mStrBundle;
        ]]></getter>
      </property>

      <method name="addProgressListener">
        <parameter name="aListener"/>
        <body>
          <![CDATA[
            this.webProgress.addProgressListener(aListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
          ]]>
        </body>
      </method>

      <method name="removeProgressListener">
        <parameter name="aListener"/>
        <body>
          <![CDATA[
            this.webProgress.removeProgressListener(aListener);
         ]]>
        </body>
      </method>

      <method name="attachFormFill">
        <body>
          <![CDATA[
          if (!this.mFormFillAttached && this.hasAttribute("autocompletepopup")) {
            // hoop up the form fill autocomplete controller
            var controller = Components.classes["@mozilla.org/satchel/form-fill-controller;1"].
                               getService(Components.interfaces.nsIFormFillController);

            var popup = document.getElementById(this.getAttribute("autocompletepopup"));
            if (popup)
              controller.attachToBrowser(this.docShell, popup.QueryInterface(Components.interfaces.nsIAutoCompletePopup));
            
            this.mFormFillAttached = true;
          }
          ]]>
        </body>
      </method>

      <method name="detachFormFill">
        <body>
          <![CDATA[
          if (this.mFormFillAttached) {
            // hoop up the form fill autocomplete controller
            var controller = Components.classes["@mozilla.org/satchel/form-fill-controller;1"].
                               getService(Components.interfaces.nsIFormFillController);
            controller.detachFromBrowser(this.docShell);
            
            this.mFormFillAttached = false;
          }
          ]]>
        </body>
      </method>
      
      <method name="findChildShell">
        <parameter name="aDocShell"/>
        <parameter name="aSoughtURI"/>
        <body>
          <![CDATA[
            if (aDocShell.QueryInterface(Components.interfaces.nsIWebNavigation)
                         .currentURI.spec == aSoughtURI.spec)
              return aDocShell;
            var node = aDocShell.QueryInterface(
                                   Components.interfaces.nsIDocShellTreeNode);
            for (var i = 0; i < node.childCount; ++i) {
              var docShell = node.getChildAt(i);
              docShell = this.findChildShell(docShell, aSoughtURI);
              if (docShell)
                return docShell;
            }
            return null;
          ]]>
        </body>
      </method>

      <method name="onLoad">
        <parameter name="aEvent"/>
        <body>
          <![CDATA[
            this.attachFormFill();
            if (this.pageReport) {
              var i = 0;
              while (i < this.pageReport.length) {
                if (this.findChildShell(this.docShell,
                                        this.pageReport[i].requestingWindowURI))
                  i++;
                else
                  this.pageReport.splice(i, 1);
              }
              if (this.pageReport.length == 0) {
                this.pageReport = null;
                this.updatePageReport();
              }
            }
         ]]>
        </body>
      </method>

      <method name="onUnload">
        <parameter name="aEvent"/>
        <body>
          <![CDATA[
            if (this.pageReport) {
              this.pageReport = null;
              this.updatePageReport();
            }
         ]]>
        </body>
      </method>

      <method name="updatePageReport">
        <body>
          <![CDATA[
            var n = this.parentNode;
            while (n && n.localName != "tabbrowser")
              n = n.parentNode;

            if (!n || n.mCurrentBrowser != this) return;
           
            var event = document.createEvent("Events");
            event.initEvent("DOMUpdatePageReport", true, true);
            n.dispatchEvent(event);
          ]]>
        </body>
      </method>

      <method name="onPopupBlocked">
        <parameter name="evt"/>
        <body>
          <![CDATA[
            if (!this.pageReport) {
              this.pageReport = new Array();
              this.updatePageReport(); 
            }

            var obj = { requestingWindowURI: evt.requestingWindowURI,
                        popupWindowURI: evt.popupWindowURI,
                        popupWindowFeatures: evt.popupWindowFeatures };
            this.pageReport.push(obj);
          ]]> 
        </body>
      </method>

      <field name="pageReport">null</field>

      <field name="mDragDropHandler">
        null
      </field>

      <field name="securityUI">
        null
      </field>
      
      <field name="userTypedClear">
        true
      </field>

      <field name="_userTypedValue">
        null
      </field>

      <property name="userTypedValue"
                onget="return this._userTypedValue;"
                onset="this.userTypedClear = false; return this._userTypedValue = val;"/>

      <field name="mFormFillAttached">
        false
      </field>
      
      <field name="focusedWindow">
        null
      </field>

      <field name="focusedElement">
        null
      </field>     
      
      <field name="isShowingMessage">
        false
      </field>

      <field name="mFavIconURL">null</field>

      <constructor>
        <![CDATA[
          try {
            if (!this.hasAttribute("disablehistory")) {
              // wire up session history
              this.webNavigation.sessionHistory = Components.classes["@mozilla.org/browser/shistory;1"].createInstance(Components.interfaces.nsISHistory);
              var os = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
              os.addObserver(this, "browser:purge-session-history", false);
              // enable global history
              this.docShell.QueryInterface(Components.interfaces.nsIDocShellHistory).useGlobalHistory = true;
            }
          }
          catch (e) {
          }
          try {
            this.mDragDropHandler = Components.classes["@mozilla.org:/content/content-area-dragdrop;1"].createInstance(Components.interfaces.nsIDragDropHandler);
            this.mDragDropHandler.hookupTo(this, null);
          }
          catch (e) {
          }
          try {
            const SECUREBROWSERUI_CONTRACTID = "@mozilla.org/secure_browser_ui;1";
            if (!this.hasAttribute("disablesecurity") &&
                SECUREBROWSERUI_CONTRACTID in Components.classes) {
              this.securityUI = Components.classes[SECUREBROWSERUI_CONTRACTID].createInstance(Components.interfaces.nsISecureBrowserUI);
              this.securityUI.init(this.contentWindow);
            }
          }
          catch (e) {
          }

          // Listen for first load for lazy attachment to form fill controller
          this.addEventListener("load", this.onLoad, true);
          this.addEventListener("unload", this.onUnload, true);
          this.addEventListener("DOMPopupBlocked", this.onPopupBlocked, false);
        ]]>
      </constructor>
      
      <destructor>
        <![CDATA[
          this.destroy();
        ]]>
      </destructor>

      <!-- This is necessary because the destructor doesn't always get called when
           we are removed from a tabbrowser. This will be explicitly called by tabbrowser -->
      <method name="destroy">
        <body>
          <![CDATA[
          if (!this.hasAttribute("disablehistory")) {
            var os = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
            os.removeObserver(this, "browser:purge-session-history", false);
          }

          if (this.mDragDropHandler)
            this.mDragDropHandler.detach();
          this.mDragDropHandler = null;

          this.detachFormFill();
          
          this.securityUI = null;
          this._fastFind = null;

          this.removeEventListener("load", this.onLoad, true);
          this.removeEventListener("unload", this.onUnload, true);
          this.removeEventListener("DOMPopupBlocked", this.onPopupBlocked, true);
          ]]>
        </body>
      </method>
      
      <method name="observe">
        <parameter name="aSubject"/>
        <parameter name="aTopic"/>
        <parameter name="aState"/>
        <body>
          <![CDATA[
            if (aTopic != "browser:purge-session-history")
              return;
              
            var purge = this.sessionHistory.count;
            if (this.currentURI != "about:blank")
              --purge; // Don't remove the page the user's staring at from shistory
              
            this.sessionHistory.PurgeHistory(purge);
          ]]>
        </body>
      </method>
          
      <field name="_AUTOSCROLL_SPEED">3</field>
      <field name="_AUTOSCROLL_SNAP">10</field>
      <field name="_clientFrameDoc">null</field>
      <field name="_isScrolling">false</field>
      <field name="_autoScrollMarkerImage">null</field>
      <field name="_snapOn">false</field>
      <field name="_scrollCount">0</field>
      <field name="_startX">null</field>
      <field name="_startY">null</field>
      <field name="_clientX">null</field>
      <field name="_clientY">null</field>

      <method name="stopScroll">
        <body>
          <![CDATA[
            this._isScrolling = false;
            if (this._autoScrollMarkerImage) {
              this._autoScrollMarkerImage.style.display = 'none';  // seems to avoid blocking when autoscroll is initited during pageload
              this._autoScrollMarkerImage.parentNode.removeChild(this._autoScrollMarkerImage);
            }
            this._autoScrollMarkerImage = null;
         ]]>
       </body>
     </method>
     
     <method name="autoScrollLoop">
       <body>
         <![CDATA[
           if (this._isScrolling) {
             var x = (this._clientX - this._startX) / this._AUTOSCROLL_SPEED;
             var y = (this._clientY - this._startY) / this._AUTOSCROLL_SPEED;
         
             if(Math.abs(x) > 0 && Math.abs(y) > 0)
             {
               if (this._scrollCount++ % 2)
                 y = 0;
               else
                 x = 0;
             }
         
             this._clientFrameDoc.defaultView.scrollBy(x, y);
             setTimeout(function foo(a) { a.autoScrollLoop() }, 5, this);
           }
         ]]>
       </body>
     </method>
     <method name="isAutoscrollBlocker">
       <parameter name="node"/>
       <body>
         <![CDATA[
           var mmPaste = false;
           var mmScrollbarPosition = false;

           try {
             mmPaste = this.mPrefs.getBoolPref("middlemouse.paste");
           }
           catch (ex) {
           }

           try {
             mmScrollbarPosition = this.mPrefs.getBoolPref("middlemouse.scrollbarPosition");
           }
           catch (ex) {
           }

           while (node) {
             if ((node instanceof HTMLAnchorElement || node instanceof HTMLAreaElement) && node.hasAttribute("href"))
               return true;

             if (mmPaste && (node instanceof HTMLInputElement || node instanceof HTMLTextAreaElement))
               return true;

             if (node instanceof XULElement && mmScrollbarPosition
                 && (node.localName == "scrollbar" || node.localName == "scrollcorner"))
               return true;

             node = node.parentNode;
           }
           return false;
         ]]>
       </body>
     </method>
     <method name="showAutoscrollMarker">
       <parameter name="evt"/>
       <body>
         <![CDATA[
           var scrollCursor  = new Array("move", "n-resize", "e-resize");
           var docBox = this._clientFrameDoc.getBoxObjectFor(this._clientFrameDoc.documentElement);
           var left = evt.screenX - docBox.screenX;
           var top = evt.screenY - docBox.screenY;
           
           
           var documentWidth = docBox.width;
           var documentHeight = docBox.height;
           var windowWidth = window.innerWidth;
           var windowHeight = window.innerHeight;
           var scrollType = 0;
           if (windowHeight < documentHeight && windowWidth >= documentWidth)
             scrollType = 1;
           else if (windowHeight >= documentHeight && windowWidth < documentWidth)
             scrollType = 2;
     
           var imageWidth = 28;
           var imageHeight = 28;
           
           // marker
           var el = this._clientFrameDoc.createElementNS("http://www.w3.org/1999/xhtml", "img");
           
           var scrollImages  = new Array("chrome://global/content/bindings/autoscroll_all.png",
                                         "chrome://global/content/bindings/autoscroll_v.png",
                                         "chrome://global/content/bindings/autoscroll_h.png");
           
           el.src = scrollImages[scrollType];         
           el.style.position = "fixed";
           el.style.left = left - imageWidth / 2 + "px";
           el.style.top = top - imageHeight / 2 + "px";
           el.style.width = imageWidth + "px";
           el.style.height = imageHeight + "px";
           el.style.cursor = scrollCursor[scrollType];
           el.style.zIndex = 2147483647; //Max Int
           el.style.borderStyle = "none";
           el.style.padding = "0";
           el.style.margin = "0";

           this._clientFrameDoc.documentElement.appendChild(el);
           
           this._autoScrollMarkerImage = el;
         ]]>
       </body>
     </method>

     <field name="_findInstData">null</field>
     <property name="findInstData" readonly="true">
        <getter>
          <![CDATA[
            if (!this._findInstData) {
              this._findInstData = new nsFindInstData();
              this._findInstData.browser = this;
              // defaults for rootSearchWindow and currentSearchWindow are fine here
            }
            return this._findInstData;
          ]]>
        </getter>
     </property>

     <property name="canFindAgain" readonly="true" onget="return canFindAgainInPage();"/>

     <method name="find">
       <body>
         <![CDATA[
           findInPage(this.findInstData)
         ]]>
       </body>
     </method>

     <method name="findAgain">
       <body>
         <![CDATA[
           findAgainInPage(this.findInstData, false)
         ]]>
       </body>
     </method>

     <method name="findPrevious">
       <body>
         <![CDATA[
           findAgainInPage(this.findInstData, true)
         ]]>
       </body>
     </method>

    </implementation>

    <handlers>
      <handler event="keypress" keycode="VK_F7">
        <![CDATA[
          if (!event.isTrusted)
            return;

          // Toggle browse with caret mode
          var browseWithCaretOn = false;
          var warn = true;

          try {
            warn = this.mPrefs.getBoolPref("accessibility.warn_on_browsewithcaret");
          } catch (ex) {
          }

          try {
            browseWithCaretOn = this.mPrefs.getBoolPref("accessibility.browsewithcaret");
          } catch (ex) {
          }
          if (warn && !browseWithCaretOn) {
            var checkValue = {value:false};
            promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);

            var buttonPressed = promptService.confirmEx(window, 
              this.mStrBundle.GetStringFromName('browsewithcaret.checkWindowTitle'), 
              this.mStrBundle.GetStringFromName('browsewithcaret.checkLabel'),
              (promptService.BUTTON_TITLE_YES * promptService.BUTTON_POS_0) +
              (promptService.BUTTON_TITLE_NO * promptService.BUTTON_POS_1),
              null, null, null, this.mStrBundle.GetStringFromName('browsewithcaret.checkMsg'),
              checkValue);
            if (buttonPressed != 0)
              return;
            if (checkValue.value) {
              try {
                this.mPrefs.setBoolPref("accessibility.warn_on_browsewithcaret", false);
              }
              catch (ex) {
              }
            }
          }

          // Toggle the pref
          try {
            this.mPrefs.setBoolPref("accessibility.browsewithcaret",!browseWithCaretOn);
          } catch (ex) {
          }
        ]]>
      </handler>
      <handler event="mouseup">
        <![CDATA[
          if (!this.autoscrollEnabled)
            return;
          if (!this._snapOn)
            this.stopScroll();
        ]]>        
      </handler>
      <handler event="mousedown">
        <![CDATA[
          if (this._isScrolling) {
            stopScroll();
          } else if (event.button == 1) {
          if (!this.autoscrollEnabled || this.isAutoscrollBlocker(event.originalTarget))
            return;

              this._startX = event.clientX;
              this._startY = event.clientY;
              this._clientFrameDoc = event.originalTarget.ownerDocument;
              this._isScrolling = true;
              this._snapOn = true; 
              this.showAutoscrollMarker(event);
              window.setTimeout(function foo(a) { a.autoScrollLoop() }, 5, this);
            }
        ]]>
      </handler>
      <handler event="mousemove">
        <![CDATA[          
          this._clientX = event.clientX;
          this._clientY = event.clientY;
        
          if (this._isScrolling) {
            var x = this._clientX - this._startX;
            var y = this._clientY - this._startY;
        
            if ((x > this._AUTOSCROLL_SNAP || x < -this._AUTOSCROLL_SNAP) || (y > this._AUTOSCROLL_SNAP || y < -this._AUTOSCROLL_SNAP))
              this._snapOn = false;            
          }
        ]]>
      </handler>
    </handlers>

  </binding>
  
  <binding id="browsermessage" extends="xul:hbox">
    <content align="center">
      <xul:hbox anonid="messagePopupContainer" align="center" flex="1">
        <xul:image       anonid="messageImage"   class="messageImage"/>
        <xul:description anonid="messageText"    class="messageText" flex="1"/>
      </xul:hbox>
      <xul:button       anonid="messageButton"  class="messageButton"/>
      <xul:spacer class="tabs-right"/>
      <xul:hbox hidden="true" anonid="messageClose" class="tabs-closebutton-box" 
                align="center" pack="end">
        <xul:toolbarbutton ondblclick="event.preventBubble();" 
                           class="tabs-closebutton close-button" 
                           oncommand="this.parentNode.parentNode.hide();"/>
      </xul:hbox>
    </content>
    <implementation>
      <property name="type" onget="return this.getAttribute('type');"
                            onset="this.setAttribute('type', val); return val;"/>

      <field name="_imageElement">
        document.getAnonymousElementByAttribute(this, "anonid", "messageImage");
      </field>
      <property name="image">
        <getter>
          <![CDATA[
            return this._imageElement.getAttribute("src");
          ]]>
        </getter>
        <setter>
          <![CDATA[
            this._imageElement.setAttribute("src", val);
            return val;
          ]]>        
        </setter>
      </property>
      
      <field name="docShell">
        null;
      </field>
      <field name="source">
        "";
      </field>
      <property name="popup">
        <setter>
          <![CDATA[
            if (val)
              document.getAnonymousElementByAttribute(this, "anonid", "messagePopupContainer").setAttribute("popup", val);
            else
              document.getAnonymousElementByAttribute(this, "anonid", "messagePopupContainer").removeAttribute("popup");
            return val;
          ]]>
        </setter>
        <getter>
          <![CDATA[
            return document.getAnonymousElementByAttribute(this, "anonid", "messagePopupContainer").getAttribute("popup");
          ]]>
        </getter>
      </property>

      <field name="_textElement">
        document.getAnonymousElementByAttribute(this, "anonid", "messageText");
      </field>
      <field name="_text">
        "";
      </field>
      <property name="text">
        <getter>
          <![CDATA[
            return this._text;
          ]]>
        </getter>
        <setter>
          <![CDATA[
            this._text = val;
            while (this._textElement.hasChildNodes())
              this._textElement.removeChild(this._textElement.firstChild);
            this._textElement.appendChild(document.createTextNode(val));
            return val;
          ]]>        
        </setter>
      </property>
      
      <field name="_buttonElement">
        document.getAnonymousElementByAttribute(this, "anonid", "messageButton");
      </field>
      <property name="buttonText">
        <getter>
          <![CDATA[
            return this._buttonElement.getAttribute("label");
          ]]>
        </getter>
        <setter>
          <![CDATA[
            if (val != "") {
              this._buttonElement.removeAttribute("style");
              this._buttonElement.setAttribute("label", val);
            }
            else
              this._buttonElement.setAttribute("style", "max-width: 1px; visibility: hidden;");
            return val;
          ]]>        
        </setter>
      </property>
      <property name="closeButton">
        <getter>
          <![CDATA[
            return document.getAnonymousElementByAttribute(this, "anonid", "messageClose");
          ]]>
        </getter>
        <setter>
          <![CDATA[
            var elm = document.getAnonymousElementByAttribute(this, "anonid", "messageClose");
            elm.hidden = !val;
            return val;
          ]]>        
        </setter>
      </property>
      <method name="hide">
        <body>
          <![CDATA[
            this.hidden = true;
          ]]>
        </body>
      </method>
    </implementation>
    <handlers>
      <handler event="click">
        if (event.originalTarget.getAttribute("anonid") == "messageButton") {
          var os = Components.classes["@mozilla.org/observer-service;1"]
                             .getService(Components.interfaces.nsIObserverService);
          var subject = this.docShell ? this.docShell : null;
          os.notifyObservers(subject, this.source, "");
        }
      </handler>
    </handlers>
  </binding>

</bindings>
