home *** CD-ROM | disk | FTP | other *** search
/ GameStar 2005 October / Gamestar_77_2005-10_dvd.iso / Programy / nsb-install-8-0.exe / chrome / comm.jar / content / communicator / nsContextMenu.js < prev    next >
Encoding:
JavaScript  |  2005-07-29  |  41.9 KB  |  971 lines

  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /*
  3.  * The contents of this file are subject to the Netscape Public
  4.  * License Version 1.1 (the "License"); you may not use this file
  5.  * except in compliance with the License. You may obtain a copy of
  6.  * the License at http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the License is distributed on an "AS
  9.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  10.  * implied. See the License for the specific language governing
  11.  * rights and limitations under the License.
  12.  *
  13.  * The Original Code is Mozilla Communicator client code,
  14.  * released March 31, 1998.
  15.  *
  16.  * The Initial Developer of the Original Code is Netscape Communications
  17.  * Corporation.  Portions created by Netscape are
  18.  * Copyright (C) 1998 Netscape Communications Corporation. All
  19.  * Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  *     William A. ("PowerGUI") Law <law@netscape.com>
  23.  *     Blake Ross <blakeross@telocity.com>
  24.  *     Gervase Markham <gerv@gerv.net>
  25.  */
  26.  
  27. /*------------------------------ nsContextMenu ---------------------------------
  28. |   This JavaScript "class" is used to implement the browser's content-area    |
  29. |   context menu.                                                              |
  30. |                                                                              |
  31. |   For usage, see references to this class in navigator.xul.                  |
  32. |                                                                              |
  33. |   Currently, this code is relatively useless for any other purpose.  In the  |
  34. |   longer term, this code will be restructured to make it more reusable.      |
  35. ------------------------------------------------------------------------------*/
  36. function nsContextMenu( xulMenu ) {
  37.     this.target         = null;
  38.     this.menu           = null;
  39.     this.popupURL       = null;
  40.     this.onTextInput    = false;
  41.     this.onImage        = false;
  42.     this.onLink         = false;
  43.     this.onMailtoLink   = false;
  44.     this.onSaveableLink = false;
  45.     this.onMetaDataItem = false;
  46.     this.onMathML       = false;
  47.     this.link           = false;
  48.     this.inFrame        = false;
  49.     this.hasBGImage     = false;
  50.     this.isTextSelected = false;
  51.     this.inDirList      = false;
  52.     this.shouldDisplay  = true;
  53.  
  54.     // Initialize new menu.
  55.     this.initMenu( xulMenu );
  56. }
  57.  
  58. // Prototype for nsContextMenu "class."
  59. nsContextMenu.prototype = {
  60.     // onDestroy is a no-op at this point.
  61.     onDestroy : function () {
  62.     },
  63.     // Initialize context menu.
  64.     initMenu : function ( popup ) {
  65.         const xulNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
  66.         if ( document.popupNode.namespaceURI == xulNS ) {
  67.           this.shouldDisplay = false;
  68.           return;
  69.         }
  70.         // Save menu.
  71.         this.menu = popup;
  72.  
  73.         // Get contextual info.
  74.         this.setTarget( document.popupNode );
  75.         
  76.         this.isTextSelected = this.isTextSelection();
  77.  
  78.         this.initPopupURL();
  79.  
  80.         // Initialize (disable/remove) menu items.
  81.         this.initItems();
  82.     },
  83.     initItems : function () {
  84.         this.initOpenItems();
  85.         this.initNavigationItems();
  86.         this.initViewItems();
  87.         this.initMiscItems();
  88.         this.initSaveItems();
  89.         this.initClipboardItems();
  90.         this.initMetadataItems();
  91.     },
  92.     initOpenItems : function () {
  93.         var showOpen = this.onSaveableLink || ( this.inDirList && this.onLink );
  94.  
  95.         this.showItem( "context-openlink", showOpen );
  96.         this.showItem( "context-openlinkintab", showOpen );
  97.  
  98.         this.showItem( "context-sep-open", showOpen );
  99.     },
  100.     initNavigationItems : function () {
  101.         // Back determined by canGoBack broadcaster.
  102.         this.setItemAttrFromNode( "context-back", "disabled", "canGoBack" );
  103.  
  104.         // Forward determined by canGoForward broadcaster.
  105.         this.setItemAttrFromNode( "context-forward", "disabled", "canGoForward" );
  106.  
  107.         var showNav = !( this.isTextSelected || this.onLink || this.onImage || this.onTextInput );
  108.         
  109.         this.showItem( "context-back", showNav );
  110.         this.showItem( "context-forward", showNav );
  111.  
  112.         this.showItem( "context-reload", showNav );
  113.         
  114.         this.showItem( "context-stop", showNav );
  115.         this.showItem( "context-sep-stop", showNav );
  116.  
  117.         // XXX: Stop is determined in navigator.js; the canStop broadcaster is broken
  118.         //this.setItemAttrFromNode( "context-stop", "disabled", "canStop" );
  119.     },
  120.     initSaveItems : function () {
  121.         this.showItem( "context-savepage", 
  122.                        !( this.inDirList || this.isTextSelected || this.onTextInput || this.onStandaloneImage ||
  123.                          (this.onLink && this.onImage)));
  124.  
  125.         // Save link depends on whether we're in a link.
  126.         this.showItem( "context-savelink", this.onSaveableLink );
  127.  
  128.         // Save image depends on whether there is one.
  129.         this.showItem( "context-saveimage", this.onImage || this.onStandaloneImage);
  130.         
  131.         this.showItem( "context-sendimage", this.onImage || this.onStandaloneImage);
  132.     },
  133.     initViewItems : function () {
  134.         // View source is always OK, unless in directory listing.
  135.         this.showItem( "context-viewpartialsource-selection", this.isTextSelected && !this.onTextInput );
  136.         this.showItem( "context-viewpartialsource-mathml", this.onMathML && !this.isTextSelected );
  137.  
  138.         var showView = !( this.inDirList || this.onImage || this.isTextSelected || this.onLink || this.onTextInput );
  139.  
  140.         this.showItem( "context-viewsource", showView );
  141.         this.showItem( "context-viewinfo", showView );
  142.  
  143.         this.showItem( "context-sep-properties", !( this.inDirList || this.isTextSelected || this.onTextInput ) );
  144.         // Set As Wallpaper depends on whether an image was clicked on, and only works on Windows.
  145.         var isWin = navigator.appVersion.indexOf("Windows") != -1;
  146.         this.showItem( "context-setWallpaper", isWin && (this.onImage || this.onStandaloneImage));
  147.  
  148.         this.showItem( "context-sep-image", this.onImage || this.onStandaloneImage);
  149.  
  150.         if( isWin && this.onImage )
  151.             // Disable the Set As Wallpaper menu item if we're still trying to load the image
  152.           this.setItemAttr( "context-setWallpaper", "disabled", (("complete" in this.target) && !this.target.complete) ? "true" : null );
  153.  
  154.         this.showItem( "context-fitimage", this.onStandaloneImage && _content.document.imageResizingEnabled );
  155.         if ( this.onStandaloneImage && _content.document.imageResizingEnabled ) {
  156.           this.setItemAttr( "context-fitimage", "disabled", _content.document.imageIsOverflowing ? null : "true");
  157.           this.setItemAttr( "context-fitimage", "checked", _content.document.imageIsResized ? "true" : null);
  158.         }
  159.  
  160.         // View Image depends on whether an image was clicked on.
  161.         this.showItem( "context-viewimage", this.onImage && !this.onStandaloneImage);
  162.  
  163.         // View background image depends on whether there is one.
  164.         this.showItem( "context-viewbgimage", showView && !this.onStandaloneImage);
  165.         this.showItem( "context-sep-viewbgimage", showView && !this.onStandaloneImage);
  166.         this.setItemAttr( "context-viewbgimage", "disabled", this.hasBGImage ? null : "true");
  167.     },
  168.     initMiscItems : function () {
  169.         // Use "Bookmark This Link" if on a link.
  170.         this.showItem( "context-bookmarkpage", !( this.isTextSelected || this.onTextInput || this.onStandaloneImage ) );
  171.         this.showItem( "context-bookmarklink", this.onLink && !this.onMailtoLink );
  172.         this.showItem( "context-searchselect", this.isTextSelected && !this.onTextInput );
  173.         this.showItem( "frame", this.inFrame );
  174.         this.showItem( "frame-sep", this.inFrame );
  175.         var blocking = true;
  176.         if (this.popupURL)
  177.           try {
  178.             const PM = Components.classes["@mozilla.org/PopupWindowManager;1"]
  179.                        .getService(Components.interfaces.nsIPopupWindowManager);
  180.             blocking = PM.testPermission(this.popupURL) ==
  181.                        Components.interfaces.nsIPopupWindowManager.DENY_POPUP;
  182.           } catch (e) {
  183.           }
  184.  
  185.         this.showItem( "popupwindow-reject", this.popupURL && !blocking);
  186.         this.showItem( "popupwindow-allow", this.popupURL && blocking);
  187.         this.showItem( "context-sep-popup", this.popupURL);
  188.     },
  189.     initClipboardItems : function () {
  190.  
  191.         // Copy depends on whether there is selected text.
  192.         // Enabling this context menu item is now done through the global
  193.         // command updating system
  194.         // this.setItemAttr( "context-copy", "disabled", !this.isTextSelected() );
  195.  
  196.         goUpdateGlobalEditMenuItems();
  197.  
  198.         this.showItem( "context-undo", this.onTextInput );
  199.         this.showItem( "context-redo", this.onTextInput );
  200.         this.showItem( "context-sep-undo", this.onTextInput );
  201.         this.showItem( "context-cut", this.onTextInput );
  202.         this.showItem( "context-copy", this.isTextSelected || this.onTextInput);
  203.         this.showItem( "context-paste", this.onTextInput );
  204.         this.showItem( "context-delete", this.onTextInput );
  205.         this.showItem( "context-sep-paste", this.onTextInput );
  206.         this.showItem( "context-selectall", true );
  207.         this.showItem( "context-sep-selectall", this.isTextSelected && !this.onTextInput );
  208.         // In a text area there will be nothing after select all, so we don't want a sep
  209.         // Otherwise, if there's text selected then there are extra menu items
  210.         // (search for selection and view selection source), so we do want a sep
  211.  
  212.         // XXX dr
  213.         // ------
  214.         // nsDocumentViewer.cpp has code to determine whether we're
  215.         // on a link or an image. we really ought to be using that...
  216.  
  217.         // Copy email link depends on whether we're on an email link.
  218.         this.showItem( "context-copyemail", this.onMailtoLink );
  219.  
  220.         // Copy link location depends on whether we're on a link.
  221.         this.showItem( "context-copylink", this.onLink );
  222.         this.showItem( "context-sep-copylink", this.onLink );
  223.  
  224.         // Copy image location depends on whether we're on an image.
  225.         this.showItem( "context-copyimage", this.onImage );
  226.         this.showItem( "context-sep-copyimage", this.onImage );
  227.     },
  228.     initMetadataItems : function () {
  229.         // Show if user clicked on something which has metadata.
  230.         this.showItem( "context-metadata", this.onMetaDataItem );
  231.     },
  232.     // Set various context menu attributes based on the state of the world.
  233.     setTarget : function ( node ) {
  234.         // Initialize contextual info.
  235.         this.onImage    = false;
  236.         this.onStandaloneImage = false;
  237.         this.onMetaDataItem = false;
  238.         this.onTextInput = false;
  239.         this.imageURL   = "";
  240.         this.onLink     = false;
  241.         this.onMathML   = false;
  242.         this.inFrame    = false;
  243.         this.hasBGImage = false;
  244.         this.bgImageURL = "";
  245.  
  246.         // Remember the node that was clicked.
  247.         this.target = node;
  248.  
  249.         // See if the user clicked on an image.
  250.         if ( this.target.nodeType == Node.ELEMENT_NODE ) {
  251.              if ( this.target instanceof HTMLImageElement ) {
  252.                 this.onImage = true;
  253.                 this.imageURL = this.target.src;
  254.  
  255.                 var documentType = window._content.document.contentType;
  256.                 if ( documentType.substr(0,6) == "image/" )
  257.                     this.onStandaloneImage = true;
  258.  
  259.                 // Look for image map.
  260.                 var mapName = this.target.getAttribute( "usemap" );
  261.                 if ( mapName ) {
  262.                     // Find map.
  263.                     var map = this.target.ownerDocument.getElementById( mapName.substr(1) );
  264.                     if ( map ) {
  265.                         // Search child <area>s for a match.
  266.                         var areas = map.childNodes;
  267.                         //XXX Client side image maps are too hard for now!
  268.                         areas.length = 0;
  269.                         for ( var i = 0; i < areas.length && !this.onLink; i++ ) {
  270.                             var area = areas[i];
  271.                             if ( area instanceof HTMLAreaElement ) {
  272.                                 // Get type (rect/circle/polygon/default).
  273.                                 var type = area.getAttribute( "type" );
  274.                                 var coords = this.parseCoords( area );
  275.                                 switch ( type.toUpperCase() ) {
  276.                                     case "RECT":
  277.                                     case "RECTANGLE":
  278.                                         break;
  279.                                     case "CIRC":
  280.                                     case "CIRCLE":
  281.                                         break;
  282.                                     case "POLY":
  283.                                     case "POLYGON":
  284.                                         break;
  285.                                     case "DEFAULT":
  286.                                         // Default matches entire image.
  287.                                         this.onLink = true;
  288.                                         this.link = area;
  289.                                         this.onSaveableLink = this.isLinkSaveable( this.link );
  290.                                         break;
  291.                                 }
  292.                             }
  293.                         }
  294.                     }
  295.                 }
  296.              } else if ( this.target instanceof HTMLObjectElement
  297.                          &&
  298.                          // See if object tag is for an image.
  299.                          this.objectIsImage( this.target ) ) {
  300.                 // This is an image.
  301.                 this.onImage = true;
  302.                 // URL must be constructed.
  303.                 this.imageURL = this.objectImageURL( this.target );
  304.              } else if ( this.target instanceof HTMLInputElement ) {
  305.                type = this.target.getAttribute("type");
  306.                if(type && type.toUpperCase() == "IMAGE") {
  307.                  this.onImage = true;
  308.                  // Convert src attribute to absolute URL.
  309.                  this.imageURL = this.makeURLAbsolute( this.target.baseURI,
  310.                                                        this.target.src );
  311.                } else /* if (this.target.getAttribute( "type" ).toUpperCase() == "TEXT") */ {
  312.                  this.onTextInput = this.isTargetATextBox(this.target);
  313.                }
  314.             } else if ( this.target instanceof HTMLTextAreaElement ) {
  315.                  this.onTextInput = true;
  316.             } else if ( this.target instanceof HTMLHtmlElement ) {
  317.                // pages with multiple <body>s are lame. we'll teach them a lesson.
  318.                var bodyElt = this.target.ownerDocument.getElementsByTagName("body")[0];
  319.                if ( bodyElt ) {
  320.                  var computedURL = this.getComputedURL( bodyElt, "background-image" );
  321.                  if ( computedURL ) {
  322.                    this.hasBGImage = true;
  323.                    this.bgImageURL = this.makeURLAbsolute( bodyElt.baseURI,
  324.                                                            computedURL );
  325.                  }
  326.                }
  327.             } else if ( "HTTPIndex" in _content &&
  328.                         _content.HTTPIndex instanceof Components.interfaces.nsIHTTPIndex ) {
  329.                 this.inDirList = true;
  330.                 // Bubble outward till we get to an element with URL attribute
  331.                 // (which should be the href).
  332.                 var root = this.target;
  333.                 while ( root && !this.link ) {
  334.                     if ( root.tagName == "tree" ) {
  335.                         // Hit root of tree; must have clicked in empty space;
  336.                         // thus, no link.
  337.                         break;
  338.                     }
  339.                     if ( root.getAttribute( "URL" ) ) {
  340.                         // Build pseudo link object so link-related functions work.
  341.                         this.onLink = true;
  342.                         this.link = { href : root.getAttribute("URL"),
  343.                                       getAttribute: function (attr) {
  344.                                           if (attr == "title") {
  345.                                               return root.firstChild.firstChild.getAttribute("label");
  346.                                           } else {
  347.                                               return "";
  348.                                           }
  349.                                       }
  350.                                     };
  351.                         // If element is a directory, then you can't save it.
  352.                         if ( root.getAttribute( "container" ) == "true" ) {
  353.                             this.onSaveableLink = false;
  354.                         } else {
  355.                             this.onSaveableLink = true;
  356.                         }
  357.                     } else {
  358.                         root = root.parentNode;
  359.                     }
  360.                 }
  361.             }
  362.         }
  363.  
  364.         // We have meta data on images.
  365.         this.onMetaDataItem = this.onImage;
  366.         
  367.         // See if the user clicked on MathML
  368.         const NS_MathML = "http://www.w3.org/1998/Math/MathML";
  369.         if ((this.target.nodeType == Node.TEXT_NODE &&
  370.              this.target.parentNode.namespaceURI == NS_MathML)
  371.              || (this.target.namespaceURI == NS_MathML))
  372.           this.onMathML = true;
  373.  
  374.         // See if the user clicked in a frame.
  375.         if ( this.target.ownerDocument != window._content.document ) {
  376.             this.inFrame = true;
  377.         }
  378.         
  379.         // Bubble out, looking for items of interest
  380.         var elem = this.target;
  381.         while ( elem ) {
  382.             if ( elem.nodeType == Node.ELEMENT_NODE ) {
  383.                 // Link?
  384.                 if ( !this.onLink && 
  385.                     ( (elem instanceof HTMLAnchorElement && elem.href) ||
  386.                       elem instanceof HTMLAreaElement ||
  387.                       elem instanceof HTMLLinkElement ||
  388.                       elem.getAttributeNS( "http://www.w3.org/1999/xlink", "type") == "simple" ) ) {
  389.                     // Clicked on a link.
  390.                     this.onLink = true;
  391.                     this.onMetaDataItem = true;
  392.                     // Remember corresponding element.
  393.                     this.link = elem;
  394.                     this.onMailtoLink = this.isLinkType( "mailto:", this.link );
  395.                     // Remember if it is saveable.
  396.                     this.onSaveableLink = this.isLinkSaveable( this.link );
  397.                 }
  398.                 
  399.                 // Text input?
  400.                 if ( !this.onTextInput ) {
  401.                     // Clicked on a link.
  402.                     this.onTextInput = this.isTargetATextBox(elem);
  403.                 }
  404.                 
  405.                 // Metadata item?
  406.                 if ( !this.onMetaDataItem ) {
  407.                     // We currently display metadata on anything which fits
  408.                     // the below test.
  409.                     if ( ( elem instanceof HTMLQuoteElement && elem.cite)    ||
  410.                          ( elem instanceof HTMLTableElement && elem.summary) ||
  411.                          ( elem instanceof HTMLModElement &&
  412.                              ( elem.cite || elem.dateTime ) )                ||
  413.                          ( elem instanceof HTMLElement &&
  414.                              ( elem.title || elem.lang ) ) ) {
  415.                         dump("On metadata item.\n");
  416.                         this.onMetaDataItem = true;
  417.                     }
  418.                 }
  419.  
  420.                 // Background image?  Don't bother if we've already found a 
  421.                 // background image further down the hierarchy.  Otherwise,
  422.                 // we look for the computed background-image style.
  423.                 if ( !this.hasBGImage ) {
  424.                     var bgImgUrl = this.getComputedURL( elem, "background-image" );
  425.                     if ( bgImgUrl ) {
  426.                         this.hasBGImage = true;
  427.                         this.bgImageURL = this.makeURLAbsolute( elem.baseURI,
  428.                                                                 bgImgUrl );
  429.                     }
  430.                 }
  431.             }
  432.             elem = elem.parentNode;    
  433.         }
  434.     },
  435.     initPopupURL: function() {
  436.       // quick check: if no opener, it can't be a popup
  437.       if (!window.content.opener)
  438.         return;
  439.       try {
  440.         var show = false;
  441.         // is it a popup window?
  442.         const CI = Components.interfaces;
  443.         var xulwin = window
  444.                     .QueryInterface(CI.nsIInterfaceRequestor)
  445.                     .getInterface(CI.nsIWebNavigation)
  446.                     .QueryInterface(CI.nsIDocShellTreeItem)
  447.                     .treeOwner
  448.                     .QueryInterface(CI.nsIInterfaceRequestor)
  449.                     .getInterface(CI.nsIXULWindow);
  450.         if (xulwin.contextFlags &
  451.             CI.nsIWindowCreator2.PARENT_IS_LOADING_OR_RUNNING_TIMEOUT) {
  452.           // do the pref settings allow site-by-site popup management?
  453.           const PB = Components.classes["@mozilla.org/preferences-service;1"]
  454.                      .getService(CI.nsIPrefBranch);
  455.           show = !PB.getBoolPref("dom.disable_open_during_load");
  456.         }
  457.         if (show) {
  458.           // initialize popupURL
  459.           const IOS = Components.classes["@mozilla.org/network/io-service;1"]
  460.                       .getService(CI.nsIIOService);
  461.           var spec = Components.lookupMethod(window.content.opener, "location")
  462.                      .call();
  463.           this.popupURL = IOS.newURI(spec, null, null);
  464.  
  465.           // but cancel if it's an unsuitable URL
  466.           const PM = Components.classes["@mozilla.org/PopupWindowManager;1"]
  467.                      .getService(CI.nsIPopupWindowManager);
  468.         }
  469.       } catch(e) {
  470.       }
  471.     },
  472.     // Returns the computed style attribute for the given element.
  473.     getComputedStyle: function( elem, prop ) {
  474.          return elem.ownerDocument.defaultView.getComputedStyle( elem, '' ).getPropertyValue( prop );
  475.     },
  476.     // Returns a "url"-type computed style attribute value, with the url() stripped.
  477.     getComputedURL: function( elem, prop ) {
  478.          var url = elem.ownerDocument.defaultView.getComputedStyle( elem, '' ).getPropertyCSSValue( prop );
  479.          return ( url.primitiveType == CSSPrimitiveValue.CSS_URI ) ? url.getStringValue() : null;
  480.     },
  481.     // Returns true iff clicked on link is saveable.
  482.     isLinkSaveable : function ( link ) {
  483.         // We don't do the Right Thing for news/snews yet, so turn them off
  484.         // until we do.
  485.         return !(this.isLinkType( "mailto:" , link )     ||
  486.                  this.isLinkType( "javascript:" , link ) ||
  487.                  this.isLinkType( "news:", link )        || 
  488.                  this.isLinkType( "snews:", link ) ); 
  489.     },
  490.     // Returns true iff clicked on link is of type given.
  491.     isLinkType : function ( linktype, link ) {        
  492.         try {
  493.             // Test for missing protocol property.
  494.             if ( !link.protocol ) {
  495.                 // We must resort to testing the URL string :-(.
  496.                 var protocol;
  497.                 if ( link.href ) {
  498.                     protocol = link.href.substr( 0, linktype.length );
  499.                 } else {
  500.                     protocol = link.getAttributeNS("http://www.w3.org/1999/xlink","href");
  501.                     if ( protocol ) {
  502.                         protocol = protocol.substr( 0, linktype.length );
  503.                     }
  504.                 }
  505.                 return protocol.toLowerCase() === linktype;        
  506.             } else {
  507.                 // Presume all but javascript: urls are saveable.
  508.                 return link.protocol.toLowerCase() === linktype;
  509.             }
  510.         } catch (e) {
  511.             // something was wrong with the link,
  512.             // so we won't be able to save it anyway
  513.             return false;
  514.         }
  515.     },
  516.     // Block popup windows
  517.     rejectPopupWindows: function(andClose) {
  518.       const PM = Components.classes["@mozilla.org/PopupWindowManager;1"]
  519.                  .getService(Components.interfaces.nsIPopupWindowManager);
  520.       PM.add(this.popupURL, false);
  521.       if (andClose) {
  522.         const OS = Components.classes["@mozilla.org/observer-service;1"]
  523.                    .getService(Components.interfaces.nsIObserverService);
  524.         OS.notifyObservers(window, "popup-perm-close", this.popupURL.spec);
  525.       }
  526.     },
  527.     // Unblock popup windows
  528.     allowPopupWindows: function() {
  529.       const PM = Components.classes["@mozilla.org/PopupWindowManager;1"]
  530.                  .getService(Components.interfaces.nsIPopupWindowManager);
  531.       PM.add(this.popupURL, true);
  532.     },
  533.     // Open linked-to URL in a new window.
  534.     openLink : function () {
  535.         // Determine linked-to URL.
  536.         openNewWindowWith( this.linkURL(), true );
  537.     },
  538.     // Open linked-to URL in a new tab.
  539.     openLinkInTab : function () {
  540.         // Determine linked-to URL.
  541.         openNewTabWith( this.linkURL(), true, false );
  542.     },
  543.     // Open frame in a new tab.
  544.     openFrameInTab : function () {
  545.         // Determine linked-to URL.
  546.         openNewTabWith( this.target.ownerDocument.location.href );
  547.     },
  548.     // Reload clicked-in frame.
  549.     reloadFrame : function () {
  550.         this.target.ownerDocument.location.reload();
  551.     },
  552.     // Open clicked-in frame in its own window.
  553.     openFrame : function () {
  554.         openNewWindowWith( this.target.ownerDocument.location.href );
  555.     },
  556.     // Open clicked-in frame in the same window
  557.     showOnlyThisFrame : function () {
  558.         window.loadURI(this.target.ownerDocument.location.href);
  559.     },
  560.     // View Partial Source
  561.     viewPartialSource : function ( context ) {
  562.         var focusedWindow = document.commandDispatcher.focusedWindow;
  563.         if (focusedWindow == window)
  564.           focusedWindow = _content;
  565.         var docCharset = null;
  566.         if (focusedWindow)
  567.           docCharset = "charset=" + focusedWindow.document.characterSet;
  568.  
  569.         // "View Selection Source" and others such as "View MathML Source"
  570.         // are mutually exclusive, with the precedence given to the selection
  571.         // when there is one
  572.         var reference = null;
  573.         if (context == "selection")
  574.           reference = focusedWindow.getSelection();
  575.         else if (context == "mathml")
  576.           reference = this.target;
  577.         else
  578.           throw "not reached";
  579.  
  580.         var docUrl = null; // unused (and play nice for fragments generated via XSLT too)
  581.         window.openDialog("chrome://navigator/content/viewPartialSource.xul",
  582.                           "_blank", "scrollbars,resizable,chrome,dialog=no",
  583.                           docUrl, docCharset, reference, context);
  584.     },
  585.     // Open new "view source" window with the frame's URL.
  586.     viewFrameSource : function () {
  587.         BrowserViewSourceOfDocument(this.target.ownerDocument);
  588.     },
  589.     viewInfo : function () {
  590.         BrowserPageInfo();
  591.     },
  592.     viewFrameInfo : function () {
  593.         BrowserPageInfo(this.target.ownerDocument);
  594.     },
  595.     toggleImageSize : function () {
  596.         _content.document.toggleImageSize();
  597.     },
  598.     // Change current window to the URL of the image.
  599.     viewImage : function () {
  600.         openTopWin( this.imageURL );
  601.     },
  602.     // Change current window to the URL of the background image.
  603.     viewBGImage : function () {
  604.         openTopWin( this.bgImageURL );
  605.     },
  606.     setWallpaper: function() {
  607.       // Confirm since it's annoying if you hit this accidentally.
  608.       var promptService       = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);
  609.       var gNavigatorBundle    = document.getElementById("bundle_navigator");
  610.       var promptTitle         = gNavigatorBundle.getString("wallpaperConfirmTitle");
  611.       var promptMsg           = gNavigatorBundle.getString("wallpaperConfirmMsg");
  612.       var promptConfirmButton = gNavigatorBundle.getString("wallpaperConfirmButton");
  613.  
  614.       var buttonPressed = promptService.confirmEx(window, promptTitle, promptMsg,
  615.                                                    (promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0) +
  616.                                                    (promptService.BUTTON_TITLE_CANCEL    * promptService.BUTTON_POS_1),
  617.                                                    promptConfirmButton, null, null, null, {value:0});
  618.  
  619.       if (buttonPressed != 0)
  620.         return;
  621.  
  622.       var winhooks = Components.classes[ "@mozilla.org/winhooks;1" ].
  623.                        getService(Components.interfaces.nsIWindowsHooks);
  624.       
  625.       winhooks.setImageAsWallpaper(this.target, false);
  626.     },    
  627.     // Save URL of clicked-on frame.
  628.     saveFrame : function () {
  629.         saveDocument( this.target.ownerDocument );
  630.     },
  631.     // Save URL of clicked-on link.
  632.     saveLink : function () {
  633.         saveURL( this.linkURL(), this.linkText(), null, true );
  634.     },
  635.     // Save URL of clicked-on image.
  636.     saveImage : function () {
  637.         saveURL( this.imageURL, null, "SaveImageTitle", false );
  638.     },
  639.     // Generate email address and put it on clipboard.
  640.     copyEmail : function () {
  641.         // Copy the comma-separated list of email addresses only.
  642.         // There are other ways of embedding email addresses in a mailto:
  643.         // link, but such complex parsing is beyond us.
  644.         var url = this.linkURL();
  645.         var qmark = url.indexOf( "?" );
  646.         var addresses;
  647.         
  648.         if ( qmark > 7 ) {                   // 7 == length of "mailto:"
  649.             addresses = url.substring( 7, qmark );
  650.         } else {
  651.             addresses = url.substr( 7 );
  652.         }
  653.  
  654.         // Let's try to unescape it using a character set
  655.         // in case the address is not ASCII.
  656.         try {
  657.           var characterSet = Components.lookupMethod(this.target.ownerDocument, "characterSet")
  658.                                        .call(this.target.ownerDocument);
  659.           const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
  660.                                          .getService(Components.interfaces.nsITextToSubURI);
  661.           addresses = textToSubURI.unEscapeNonAsciiURI(characterSet, addresses);
  662.         }
  663.         catch(ex) {
  664.           // Do nothing.
  665.         }
  666.  
  667.         var clipboard = this.getService( "@mozilla.org/widget/clipboardhelper;1",
  668.                                          Components.interfaces.nsIClipboardHelper );
  669.         clipboard.copyString(addresses);
  670.     },    
  671.     addBookmark : function() {
  672.       var docshell = document.getElementById( "content" ).webNavigation;
  673.       BookmarksUtils.addBookmark( docshell.currentURI.spec,
  674.                                   docshell.document.title,
  675.                                   docshell.document.charset,
  676.                                   false );
  677.     },
  678.     addBookmarkForFrame : function() {
  679.       var doc = this.target.ownerDocument;
  680.       var uri = doc.location.href;
  681.       var title = doc.title;
  682.       if ( !title )
  683.         title = uri;
  684.       BookmarksUtils.addBookmark( uri,
  685.                                   title,
  686.                                   doc.charset,
  687.                                   false );
  688.     },
  689.     // Open Metadata window for node
  690.     showMetadata : function () {
  691.         window.openDialog(  "chrome://navigator/content/metadata.xul",
  692.                             "_blank",
  693.                             "scrollbars,resizable,chrome,dialog=no",
  694.                             this.target);
  695.     },
  696.  
  697.     ///////////////
  698.     // Utilities //
  699.     ///////////////
  700.  
  701.     // Create instance of component given contractId and iid (as string).
  702.     createInstance : function ( contractId, iidName ) {
  703.         var iid = Components.interfaces[ iidName ];
  704.         return Components.classes[ contractId ].createInstance( iid );
  705.     },
  706.     // Get service given contractId and iid (as string).
  707.     getService : function ( contractId, iidName ) {
  708.         var iid = Components.interfaces[ iidName ];
  709.         return Components.classes[ contractId ].getService( iid );
  710.     },
  711.     // Show/hide one item (specified via name or the item element itself).
  712.     showItem : function ( itemOrId, show ) {
  713.         var item = itemOrId.constructor == String ? document.getElementById(itemOrId) : itemOrId;
  714.         if (item) 
  715.           item.hidden = !show;
  716.     },
  717.     // Set given attribute of specified context-menu item.  If the
  718.     // value is null, then it removes the attribute (which works
  719.     // nicely for the disabled attribute).
  720.     setItemAttr : function ( id, attr, val ) {
  721.         var elem = document.getElementById( id );
  722.         if ( elem ) {
  723.             if ( val == null ) {
  724.                 // null indicates attr should be removed.
  725.                 elem.removeAttribute( attr );
  726.             } else {
  727.                 // Set attr=val.
  728.                 elem.setAttribute( attr, val );
  729.             }
  730.         }
  731.     },
  732.     // Set context menu attribute according to like attribute of another node
  733.     // (such as a broadcaster).
  734.     setItemAttrFromNode : function ( item_id, attr, other_id ) {
  735.         var elem = document.getElementById( other_id );
  736.         if ( elem && elem.getAttribute( attr ) == "true" ) {
  737.             this.setItemAttr( item_id, attr, "true" );
  738.         } else {
  739.             this.setItemAttr( item_id, attr, null );
  740.         }
  741.     },
  742.     // Temporary workaround for DOM api not yet implemented by XUL nodes.
  743.     cloneNode : function ( item ) {
  744.         // Create another element like the one we're cloning.
  745.         var node = document.createElement( item.tagName );
  746.  
  747.         // Copy attributes from argument item to the new one.
  748.         var attrs = item.attributes;
  749.         for ( var i = 0; i < attrs.length; i++ ) {
  750.             var attr = attrs.item( i );
  751.             node.setAttribute( attr.nodeName, attr.nodeValue );
  752.         }
  753.  
  754.         // Voila!
  755.         return node;
  756.     },
  757.     // Generate fully-qualified URL for clicked-on link.
  758.     linkURL : function () {
  759.         if (this.link.href) {
  760.           return this.link.href;
  761.         }
  762.         var href = this.link.getAttributeNS("http://www.w3.org/1999/xlink","href");
  763.         if (!href || !href.match(/\S/)) {
  764.           throw "Empty href"; // Without this we try to save as the current doc, for example, HTML case also throws if empty
  765.         }
  766.         href = this.makeURLAbsolute(this.link.baseURI,href);
  767.         return href;
  768.     },
  769.     // Get text of link.
  770.     linkText : function () {
  771.         var text = gatherTextUnder( this.link );
  772.         if (!text || !text.match(/\S/)) {
  773.           text = this.link.getAttribute("title");
  774.           if (!text || !text.match(/\S/)) {
  775.             text = this.link.getAttribute("alt");
  776.             if (!text || !text.match(/\S/)) {
  777.               if (this.link.href) {                
  778.                 text = this.link.href;
  779.               } else {
  780.                 text = getAttributeNS("http://www.w3.org/1999/xlink", "href");
  781.                 if (text && text.match(/\S/)) {
  782.                   text = this.makeURLAbsolute(this.link.baseURI, text);
  783.                 }
  784.               }
  785.             }
  786.           }
  787.         }
  788.  
  789.         return text;
  790.     },
  791.  
  792.     //Get selected object and convert it to a string to get
  793.     //selected text.   Only use the first 15 chars.
  794.     isTextSelection : function() {
  795.         var result = false;
  796.         var selection = this.searchSelected(16);
  797.  
  798.         var bundle = srGetStrBundle("chrome://communicator/locale/contentAreaCommands.properties");
  799.  
  800.         var searchSelectText;
  801.         if (selection != "") {
  802.             searchSelectText = selection.toString();
  803.             if (searchSelectText.length > 15)
  804.                 searchSelectText = searchSelectText.substr(0,15) + "...";
  805.             result = true;
  806.  
  807.           // format "Search for <selection>" string to show in menu
  808.           searchSelectText = bundle.formatStringFromName("searchText",
  809.                                                          [searchSelectText], 1);
  810.           this.setItemAttr("context-searchselect", "label", searchSelectText);
  811.         } 
  812.         return result;
  813.     },
  814.     
  815.     searchSelected : function( charlen ) {
  816.         var focusedWindow = document.commandDispatcher.focusedWindow;
  817.         // MERC: SL  use of focusedWindow.__proto__ is deprecated.
  818.         // http://developer-test.mozilla.org/docs/Safely_accessing_content_DOM_from_chrome
  819.         
  820.         var searchStr = focusedWindow.getSelection().toString();
  821.         //dump("searchStr is " + searchStr + "\n");
  822.         
  823.         // searching for more than 150 chars makes no sense
  824.         if (!charlen)
  825.             charlen = 150;
  826.         if (charlen < searchStr.length) {
  827.             // only use the first charlen important chars. see bug 221361
  828.             var pattern = new RegExp("^(?:\\s*.){0," + charlen + "}");
  829.             pattern.test(searchStr);
  830.             searchStr = RegExp.lastMatch;
  831.         }
  832.         searchStr = searchStr.replace(/\s*(.*?)\s*$/, "$1");
  833.         searchStr = searchStr.replace(/\s+/g, " ");
  834.         return searchStr;
  835.     },
  836.     
  837.     // Determine if target <object> is an image.
  838.     objectIsImage : function ( objElem ) {
  839.         var result = false;
  840.         // Get type and data attributes.
  841.         var type = objElem.getAttribute( "type" );
  842.         var data = objElem.getAttribute( "data" );
  843.         // Presume any mime type of the form "image/..." is an image.
  844.         // There must be a data= attribute with an URL, also.
  845.         if ( type.substring( 0, 6 ) == "image/" && data && data != "" ) {
  846.             result = true;
  847.         }
  848.         return result;
  849.     },
  850.     // Extract image URL from <object> tag.
  851.     objectImageURL : function ( objElem ) {
  852.         // Extract url from data= attribute.
  853.         var data = objElem.getAttribute( "data" );
  854.         // Make it absolute.
  855.         return this.makeURLAbsolute( objElem.baseURI, data );
  856.     },
  857.     // Convert relative URL to absolute, using document's <base>.
  858.     makeURLAbsolute : function ( base, url ) {
  859.         // Construct nsIURL.
  860.         var ioService = Components.classes["@mozilla.org/network/io-service;1"]
  861.                       .getService(Components.interfaces.nsIIOService);
  862.         var baseURI  = ioService.newURI(base, null, null);
  863.         
  864.         return ioService.newURI(baseURI.resolve(url), null, null).spec;
  865.     },
  866.     // Parse coords= attribute and return array.
  867.     parseCoords : function ( area ) {
  868.         return [];
  869.     },
  870.     toString : function () {
  871.         return "contextMenu.target     = " + this.target + "\n" +
  872.                "contextMenu.onImage    = " + this.onImage + "\n" +
  873.                "contextMenu.onLink     = " + this.onLink + "\n" +
  874.                "contextMenu.link       = " + this.link + "\n" +
  875.                "contextMenu.inFrame    = " + this.inFrame + "\n" +
  876.                "contextMenu.hasBGImage = " + this.hasBGImage + "\n";
  877.     },
  878.     isTargetATextBox : function ( node )
  879.     {
  880.       if (node instanceof HTMLInputElement)
  881.         return (node.type == "text" || node.type == "password")
  882.  
  883.       return (node instanceof HTMLTextAreaElement);
  884.     },
  885.  
  886.     // Determines whether or not the separator with the specified ID should be 
  887.     // shown or not by determining if there are any non-hidden items between it
  888.     // and the previous separator. 
  889.     shouldShowSeparator : function ( aSeparatorID )
  890.     {
  891.       var separator = document.getElementById(aSeparatorID);
  892.       if (separator) {
  893.         var sibling = separator.previousSibling;
  894.         while (sibling && sibling.localName != "menuseparator") {
  895.           if (sibling.getAttribute("hidden") != "true")
  896.             return true;
  897.           sibling = sibling.previousSibling;
  898.         }
  899.       }
  900.       return false;  
  901.     }
  902. };
  903.  
  904. /*************************************************************************
  905.  *
  906.  *   nsDefaultEngine : nsIObserver
  907.  *
  908.  *************************************************************************/
  909. function nsDefaultEngine()
  910. {
  911.     try
  912.     {
  913.         var pb = Components.classes["@mozilla.org/preferences-service;1"].
  914.                    getService(Components.interfaces.nsIPrefBranch);
  915.         var pbi = pb.QueryInterface(
  916.                     Components.interfaces.nsIPrefBranchInternal);
  917.         pbi.addObserver(this.domain, this, false);
  918.  
  919.         // reuse code by explicitly invoking initial |observe| call
  920.         // to initialize the |icon| and |name| member variables
  921.         this.observe(pb, "", this.domain);
  922.     }
  923.     catch (ex)
  924.     {
  925.     }
  926. }
  927.  
  928. nsDefaultEngine.prototype = 
  929. {
  930.     name: "",
  931.     icon: "",
  932.     domain: "browser.search.defaultengine",
  933.  
  934.     // nsIObserver implementation
  935.     observe: function(aPrefBranch, aTopic, aPrefName)
  936.     {
  937.         try
  938.         {
  939.             var rdf = Components.
  940.                         classes["@mozilla.org/rdf/rdf-service;1"].
  941.                         getService(Components.interfaces.nsIRDFService);
  942.             var ds = rdf.GetDataSource("rdf:internetsearch");
  943.             var defaultEngine = aPrefBranch.getCharPref(aPrefName);
  944.             var res = rdf.GetResource(defaultEngine);
  945.  
  946.             // get engine ``pretty'' name
  947.             const kNC_Name = rdf.GetResource(
  948.                                "http://home.netscape.com/NC-rdf#Name");
  949.             var engineName = ds.GetTarget(res, kNC_Name, true);
  950.             if (engineName)
  951.             {
  952.                 this.name = engineName.QueryInterface(
  953.                               Components.interfaces.nsIRDFLiteral).Value;
  954.             }
  955.  
  956.             // get URL to engine vendor icon
  957.             const kNC_Icon = rdf.GetResource(
  958.                                "http://home.netscape.com/NC-rdf#Icon");
  959.             var iconURL = ds.GetTarget(res, kNC_Icon, true);
  960.             if (iconURL)
  961.             {
  962.                 this.icon = iconURL.QueryInterface(
  963.                   Components.interfaces.nsIRDFLiteral).Value;
  964.             }
  965.         }
  966.         catch (ex)
  967.         {
  968.         }
  969.     }
  970. }
  971.