home *** CD-ROM | disk | FTP | other *** search
/ ftp.swcp.com / ftp.swcp.com.zip / ftp.swcp.com / mac / mozilla-macos9-1.3.1.sea.bin / Mozilla1.3.1 / Chrome / comm.jar / content / navigator / pageInfo.js < prev    next >
Text File  |  2003-06-08  |  36KB  |  1,075 lines

  1. /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: NPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Netscape Public
  6.  * License Version 1.1 (the "License"); you may not use this file
  7.  * except in compliance with the License. You may obtain a copy of
  8.  * the License at http://www.mozilla.org/NPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS
  11.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  12.  * implied. See the License for the specific language governing
  13.  * rights and limitations under the License.
  14.  *
  15.  * The Original Code is mozilla.org code.
  16.  *
  17.  * The Initial Developer of the Original Code is Netscape
  18.  * Communications Corporation.  Portions created by Netscape are
  19.  * Copyright (C) 1998 Netscape Communications Corporation. All
  20.  * Rights Reserved.
  21.  *
  22.  * Contributor(s): smorrison@gte.com
  23.  *   Terry Hayes <thayes@netscape.com>
  24.  *   Daniel Brooks <db48x@yahoo.com>
  25.  *
  26.  * Alternatively, the contents of this file may be used under the terms of
  27.  * either the GNU General Public License Version 2 or later (the "GPL"), or 
  28.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  29.  * in which case the provisions of the GPL or the LGPL are applicable instead
  30.  * of those above. If you wish to allow use of your version of this file only
  31.  * under the terms of either the GPL or the LGPL, and not to allow others to
  32.  * use your version of this file under the terms of the NPL, indicate your
  33.  * decision by deleting the provisions above and replace them with the notice
  34.  * and other provisions required by the GPL or the LGPL. If you do not delete
  35.  * the provisions above, a recipient may use your version of this file under
  36.  * the terms of any one of the NPL, the GPL or the LGPL.
  37.  *
  38.  * ***** END LICENSE BLOCK *****
  39. */
  40.  
  41. //******** define a js object to implement nsITreeView
  42. function pageInfoTreeView(columnids, copycol)
  43. {
  44.   // columnids is an array of strings indicating the names of the columns, in order
  45.   this.columnids = columnids;
  46.   this.colcount = columnids.length
  47.  
  48.   // copycol is the index number for the column that we want to add to 
  49.   // the copy-n-paste buffer when the user hits accel-c
  50.   this.copycol = copycol;
  51.   this.rows = 0;
  52.   this.tree = null;
  53.   this.data = new Array;
  54.   this.selection = null;
  55.   this.sortcol = null;
  56.   this.sortdir = 0;
  57. }
  58.  
  59. pageInfoTreeView.prototype = {
  60.   set rowCount(c) { throw "rowCount is a readonly property"; },
  61.   get rowCount() { return this.rows; },
  62.  
  63.   setTree: function(tree) 
  64.   {
  65.     this.tree = tree;
  66.   },
  67.  
  68.   getCellText: function(row, column)
  69.   {
  70.     var colidx = 0;
  71.     // loop through the list of column names to find the index to the column 
  72.     // we should be worrying about. very much a hack, but what can you do?
  73.     while(colidx < this.colcount && column != this.columnids[colidx])
  74.       colidx++;
  75.  
  76.     // row can be null, but js arrays are 0-indexed.
  77.     // colidx cannot be null, but can be larger than the number
  78.     // of columns in the array (when column is a string not in 
  79.     // this.columnids.) In this case it's the fault of
  80.     // whoever typoed while calling this function.
  81.     return this.data[row][colidx] || "";
  82.   },
  83.  
  84.   setCellText: function(row, column, value) 
  85.   {
  86.     var colidx = 0;
  87.     // loop through the list of column names to find the index 
  88.     // to the column the should be worrying about. very much a hack, but
  89.     // what can you do? 
  90.     // XXX: I think there's a better way to do this now...
  91.     while(colidx < this.colcount && column != this.columnids[colidx])
  92.       colidx++;
  93.     this.data[row][colidx] = value;
  94.   },
  95.  
  96.   addRow: function(row)
  97.   {
  98.     this.rows = this.data.push(row);
  99.     this.rowCountChanged(this.rows - 1, 1);
  100.   },
  101.  
  102.   addRows: function(rows)
  103.   {
  104.     var length = rows.length;
  105.     for(var i = 0; i < length; i++)
  106.       this.rows = this.data.push(rows[i]);
  107.     this.rowCountChanged(this.rows - length, length);
  108.   },
  109.  
  110.   rowCountChanged: function(index, count)
  111.   {
  112.     this.tree.rowCountChanged(index, count);
  113.   },
  114.  
  115.   invalidate: function()
  116.   {
  117.     this.tree.invalidate();
  118.   },
  119.  
  120.   clear: function()
  121.   {
  122.     this.data = new Array;
  123.     this.rows = 0;
  124.   },
  125.  
  126.   handleCopy: function(row)
  127.   {
  128.     return (row < 0 || this.copycol < 0) ? "" : (this.data[row][this.copycol] || "");
  129.   },
  130.  
  131.   performActionOnRow: function(action, row)
  132.   {
  133.     if (action == "copy")
  134.     {
  135.       var data = this.handleCopy(row)
  136.       this.tree.treeBody.parentNode.setAttribute("copybuffer", data);
  137.     }
  138.   },
  139.  
  140.   getRowProperties: function(row, column, prop) { },
  141.   getCellProperties: function(row, prop) { },
  142.   getColumnProperties: function(column, elem, prop) { },
  143.   isContainer: function(index) { return false; },
  144.   isContainerOpen: function(index) { return false; },
  145.   isSeparator: function(index) { return false; },
  146.   isSorted: function() { },
  147.   canDropOn: function(index) { return false; },
  148.   canDropBeforeAfter: function(index, before) { return false; },
  149.   drop: function(row, orientation) { return false; },
  150.   getParentIndex: function(index) { return 0; },
  151.   hasNextSibling: function(index, after) { return false; },
  152.   getLevel: function(index) { return 0; },
  153.   getImageSrc: function(row, column) { },
  154.   getProgressMode: function(row, column) { },
  155.   getCellValue: function(row, column) { },
  156.   toggleOpenState: function(index) { },
  157.   cycleHeader: function(col, elem) { },
  158.   selectionChanged: function() { },
  159.   cycleCell: function(row, column) { },
  160.   isEditable: function(row, column) { return false; },
  161.   performAction: function(action) { },
  162.   performActionOnCell: function(action, row, column) { }
  163. };
  164.  
  165. // mmm, yummy. global variables.
  166. var theWindow = null;
  167. var theDocument = null;
  168.  
  169. // column number to copy from, second argument to pageInfoTreeView's constructor
  170. const COPYCOL_NONE = -1;
  171. const COPYCOL_META_CONTENT = 1;
  172. const COPYCOL_FORM_ACTION = 2;
  173. const COPYCOL_LINK_ADDRESS = 1;
  174. const COPYCOL_IMAGE_ADDRESS = 0;
  175.  
  176. // one nsITreeView for each tree in the window
  177. var metaView = new pageInfoTreeView(["meta-name","meta-content"], COPYCOL_META_CONTENT);
  178. var formView = new pageInfoTreeView(["form-name","form-method","form-action","form-node"], COPYCOL_FORM_ACTION);
  179. var fieldView = new pageInfoTreeView(["field-label","field-field","field-type","field-value"], COPYCOL_NONE);
  180. var linkView = new pageInfoTreeView(["link-name","link-address","link-type"], COPYCOL_LINK_ADDRESS);
  181. var imageView = new pageInfoTreeView(["image-address","image-type","image-alt","image-node", "image-bg"], COPYCOL_IMAGE_ADDRESS);
  182.  
  183. var intervalID = null;
  184.  
  185. // localized strings (will be filled in when the document is loaded)
  186. // this isn't all of them, these are just the ones that would otherwise have been loaded inside a loop
  187. var gStrings = {}
  188. var theBundle;
  189.  
  190. const DRAGSERVICE_CONTRACTID    = "@mozilla.org/widget/dragservice;1";
  191. const TRANSFERABLE_CONTRACTID   = "@mozilla.org/widget/transferable;1";
  192. const ARRAY_CONTRACTID          = "@mozilla.org/supports-array;1";
  193. const STRING_CONTRACTID         = "@mozilla.org/supports-string;1";
  194.  
  195. // a number of services I'll need later
  196. // the cache services
  197. const nsICacheService = Components.interfaces.nsICacheService;
  198. const cacheService = Components.classes["@mozilla.org/network/cache-service;1"].getService(nsICacheService);
  199. var httpCacheSession = cacheService.createSession("HTTP", 0, true);
  200. httpCacheSession.doomEntriesIfExpired = false;
  201. var ftpCacheSession = cacheService.createSession("FTP", 0, true);
  202. ftpCacheSession.doomEntriesIfExpired = false;
  203.  
  204. // scriptable date formater, for pretty printing dates
  205. const nsIScriptableDateFormat = Components.interfaces.nsIScriptableDateFormat;
  206. var dateService = Components.classes["@mozilla.org/intl/scriptabledateformat;1"].getService(nsIScriptableDateFormat);
  207.  
  208. // clipboard helper
  209. try
  210. {
  211.   const gClipboardHelper = Components.classes["@mozilla.org/widget/clipboardhelper;1"].getService(Components.interfaces.nsIClipboardHelper);
  212. }
  213. catch(e)
  214. {
  215.   // do nothing, later code will handle the error
  216. }
  217.  
  218. // interfaces for the different html elements
  219. const nsIAnchorElement   = Components.interfaces.nsIDOMHTMLAnchorElement
  220. const nsIImageElement    = Components.interfaces.nsIDOMHTMLImageElement
  221. const nsIAreaElement     = Components.interfaces.nsIDOMHTMLAreaElement
  222. const nsILinkElement     = Components.interfaces.nsIDOMHTMLLinkElement
  223. const nsIInputElement    = Components.interfaces.nsIDOMHTMLInputElement
  224. const nsIFormElement     = Components.interfaces.nsIDOMHTMLFormElement
  225. const nsIAppletElement   = Components.interfaces.nsIDOMHTMLAppletElement
  226. const nsIObjectElement   = Components.interfaces.nsIDOMHTMLObjectElement
  227. const nsIEmbedElement    = Components.interfaces.nsIDOMHTMLEmbedElement
  228. const nsIButtonElement   = Components.interfaces.nsIDOMHTMLButtonElement
  229. const nsISelectElement   = Components.interfaces.nsIDOMHTMLSelectElement
  230. const nsITextareaElement = Components.interfaces.nsIDOMHTMLTextAreaElement
  231.  
  232. // namespaces, don't need all of these yet...
  233. const XLinkNS  = "http://www.w3.org/1999/xlink";
  234. const XULNS    = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
  235. const XMLNS    = "http://www.w3.org/XML/1998/namespace";
  236. const XHTMLNS  = "http://www.w3.org/1999/xhtml";
  237. const XHTML2NS = "http://www.w3.org/2002/06/xhtml2"
  238.  
  239. const XHTMLNSre  = "^http\:\/\/www\.w3\.org\/1999\/xhtml$";
  240. const XHTML2NSre = "^http\:\/\/www\.w3\.org\/2002\/06\/xhtml2$";
  241. const XHTMLre = RegExp(XHTMLNSre + "|" + XHTML2NSre, "");
  242.  
  243. /* Overlays register init functions here.
  244.  *   Add functions to call by invoking "onLoadRegistry.append(XXXLoadFunc);"
  245.  *   The XXXLoadFunc should be unique to the overlay module, and will be
  246.  *   invoked as "XXXLoadFunc();"
  247.  */
  248. var onLoadRegistry = [ ];
  249.  
  250. /* Called when PageInfo window is loaded.  Arguments are:
  251.  *  window.arguments[0] - document to use for source (null=Page Info, otherwise Frame Info)
  252.  *  window.arguments[1] - tab name to display first (may be null)
  253. */
  254. function onLoadPageInfo()
  255. {
  256.   //dump("===============================================================================\n");
  257.  
  258.   theBundle = document.getElementById("pageinfobundle");
  259.   gStrings.unknown = theBundle.getString("unknown");
  260.   gStrings.notSet = theBundle.getString("notset");
  261.   gStrings.emptyString = theBundle.getString("emptystring");
  262.   gStrings.linkAnchor = theBundle.getString("linkAnchor");
  263.   gStrings.linkArea = theBundle.getString("linkArea");
  264.   gStrings.linkSubmit = theBundle.getString("linkSubmit");
  265.   gStrings.linkSubmission = theBundle.getString("linkSubmission");
  266.   gStrings.linkRel = theBundle.getString("linkRel");
  267.   gStrings.linkStylesheet = theBundle.getString("linkStylesheet");
  268.   gStrings.linkRev = theBundle.getString("linkRev");
  269.   gStrings.linkX = theBundle.getString("linkX");
  270.   gStrings.mediaImg = theBundle.getString("mediaImg");
  271.   gStrings.mediaBGImg = theBundle.getString("mediaBGImg");
  272.   gStrings.mediaApplet = theBundle.getString("mediaApplet");
  273.   gStrings.mediaObject = theBundle.getString("mediaObject");
  274.   gStrings.mediaEmbed = theBundle.getString("mediaEmbed");
  275.   gStrings.mediaLink = theBundle.getString("mediaLink");
  276.   gStrings.mediaInput = theBundle.getString("mediaInput");
  277.  
  278.   var docTitle = "";
  279.   if ("arguments" in window && window.arguments.length >= 1 && window.arguments[0])
  280.   {
  281.     theWindow = null;
  282.     theDocument = window.arguments[0];
  283.     docTitle = theBundle.getString("frameInfo.title");
  284.   } 
  285.   else 
  286.   {
  287.     if ("gBrowser" in window.opener)
  288.       theWindow = window.opener.gBrowser.contentWindow;
  289.     else
  290.       theWindow = window.opener.frames[0];
  291.     theDocument = theWindow.document;
  292.  
  293.     docTitle = theBundle.getString("pageInfo.title");
  294.   }
  295.  
  296.   document.title = docTitle;
  297.  
  298.   // do the easy stuff first
  299.   makeGeneralTab();
  300.  
  301.   // and then the hard stuff
  302.   makeTabs(theDocument, theWindow);
  303.  
  304.   /* Call registered overlay init functions */
  305.   for (x in onLoadRegistry)
  306.   {
  307.     onLoadRegistry[x]();
  308.   }
  309.  
  310.   /* Select the requested tab, if the name is specified */
  311.   if ("arguments" in window && window.arguments.length > 1)
  312.   {
  313.     var tabName = window.arguments[1];
  314.  
  315.     if (tabName)
  316.     {
  317.       var tabControl = document.getElementById("tabbox");
  318.       var tab = document.getElementById(tabName);
  319.  
  320.       if (tabControl && tab)
  321.       {
  322.         tabControl.selectedTab = tab;
  323.       }
  324.     }
  325.   }
  326. }
  327.  
  328. function doHelpButton() {
  329.   var tabControl = document.getElementById("tabbox");
  330.   switch (tabControl.selectedTab.id) {
  331.     case "generalTab":
  332.       helpdoc = "pageinfo_general";
  333.       break;
  334.     case "formsTab":
  335.       helpdoc = "pageinfo_forms";
  336.       break;
  337.     case "linksTab":
  338.       helpdoc = "pageinfo_links";
  339.       break;
  340.     case "mediaTab":
  341.       helpdoc = "pageinfo_media";
  342.       break;
  343.     case "securityTab":
  344.       helpdoc = "pageinfo_security";
  345.       break;
  346.     case "p3pTab":
  347.       helpdoc = "pageinfo_privacy";
  348.       break;
  349.     default:
  350.       helpdoc = "pageinfo_general";
  351.       break;
  352.   }
  353.   openHelp(helpdoc);  
  354. }
  355.  
  356. function makeGeneralTab()
  357. {
  358.   var title = (theDocument.title) ? theBundle.getFormattedString("pageTitle", [theDocument.title]) : theBundle.getString("noPageTitle");
  359.   document.getElementById("titletext").value = title;
  360.  
  361.   var url = theDocument.location;
  362.   document.getElementById("urltext").value = url;
  363.  
  364.   var mode = ("compatMode" in theDocument && theDocument.compatMode == "BackCompat") ? theBundle.getString("generalQuirksMode") : theBundle.getString("generalStrictMode");
  365.   document.getElementById("modetext").value = mode;
  366.  
  367.   var referrer = (theDocument.referrer) ? theDocument.referrer : theBundle.getString("generalNoReferrer");
  368.   document.getElementById('refertext').value = referrer;
  369.  
  370.   // find out the mime type
  371.   var mimeType = theDocument.contentType || gStrings.unknown;
  372.   document.getElementById("typetext").value = mimeType;
  373.   
  374.   // get the meta tags
  375.   var metaNodes = theDocument.getElementsByTagName("meta");
  376.   var metaTree = document.getElementById("metatree");
  377.  
  378.   metaTree.treeBoxObject.view = metaView;
  379.  
  380.   var length = metaNodes.length;
  381.   for (var i = 0; i < length; i++)
  382.     metaView.addRow([metaNodes[i].name || metaNodes[i].httpEquiv, metaNodes[i].content]);
  383.  
  384.   metaView.rowCountChanged(0, length);
  385.   
  386.   // get the document characterset
  387.   var encoding = theDocument.characterSet;
  388.   document.getElementById("encodingtext").value = encoding;
  389.  
  390.   // get the date of last modification
  391.   var modifiedText = formatDate(theDocument.lastModified, gStrings.notSet);
  392.   document.getElementById("modifiedtext").value = modifiedText;
  393.   
  394.   // get cache info
  395.   var sourceText = theBundle.getString("generalNotCached");
  396.   var expirationText = theBundle.getString("generalNoExpiration");
  397.   var sizeText = gStrings.unknown;
  398.  
  399.   var pageSize = 0; 
  400.   var kbSize = 0;
  401.   var expirationTime = 0;
  402.  
  403.   try
  404.   {
  405.     var cacheEntryDescriptor = httpCacheSession.openCacheEntry(url, Components.interfaces.nsICache.ACCESS_READ, false);
  406.     if (cacheEntryDescriptor)
  407.     { 
  408.       switch(cacheEntryDescriptor.deviceID)
  409.       {
  410.         case "disk":
  411.           sourceText = theBundle.getString("generalDiskCache");
  412.           break;
  413.         case "memory":
  414.           sourceText = theBundle.getString("generalMemoryCache");
  415.           break;
  416.         default:
  417.           sourceText = cacheEntryDescriptor.deviceID;
  418.           break;
  419.       }
  420.  
  421.       pageSize = cacheEntryDescriptor.dataSize;
  422.       kbSize = pageSize / 1024;
  423.       sizeText = theBundle.getFormattedString("generalSize", [Math.round(kbSize*100)/100, pageSize]);
  424.  
  425.       expirationText = formatDate(cacheEntryDescriptor.expirationTime*1000, gStrings.notSet);
  426.     }
  427.   }
  428.   catch(ex)
  429.   {
  430.     try
  431.     {
  432.       cacheEntryDescriptor = ftpCacheSession.openCacheEntry(url, Components.interfaces.nsICache.ACCESS_READ, false);
  433.       if (cacheEntryDescriptor)
  434.       {
  435.         switch(cacheEntryDescriptor.deviceID)
  436.         {
  437.           case "disk":
  438.             sourceText = theBundle.getString("generalDiskCache");
  439.             break;
  440.           case "memory":
  441.             sourceText = theBundle.getString("generalMemoryCache");
  442.             break;
  443.           default:
  444.             sourceText = cacheEntryDescriptor.deviceID;
  445.             break;
  446.         }
  447.  
  448.         pageSize = cacheEntryDescriptor.dataSize;
  449.         kbSize = pageSize / 1024;
  450.         sizeText = theBundle.getFormattedString("generalSize", [Math.round(kbSize*100)/100, pageSize]);
  451.  
  452.         expirationText = formatDate(cacheEntryDescriptor.expirationTime*1000, gStrings.notSet);
  453.       }
  454.     }
  455.     catch(ex2)
  456.     {
  457.       sourceText = theBundle.getString("generalNotCached");
  458.     }
  459.   }
  460.   document.getElementById("sourcetext").value = sourceText;
  461.   document.getElementById("expirestext").value = expirationText;
  462.   document.getElementById("sizetext").value = sizeText;
  463. }
  464.  
  465. //******** Generic Build-a-tab
  466. // Assumes the views are empty. Only called once to build the tabs, and
  467. // does so by farming the task off to another thread via setTimeout().
  468. // The actual work is done with a TreeWalker that calls doGrab() once for
  469. // each element node in the document.
  470.  
  471. function makeTabs(aDocument, aWindow)
  472. {
  473.   if (aWindow && aWindow.frames.length > 0)
  474.   {
  475.     var num = aWindow.frames.length;
  476.     for (var i = 0; i < num; i++)
  477.       makeTabs(aWindow.frames[i].document, aWindow.frames[i]);  // recurse through the frames
  478.   }
  479.  
  480.   var formTree = document.getElementById("formtree");
  481.   var linkTree = document.getElementById("linktree");
  482.   var imageTree = document.getElementById("imagetree");
  483.  
  484.   formTree.treeBoxObject.view = formView;
  485.   linkTree.treeBoxObject.view = linkView;
  486.   imageTree.treeBoxObject.view = imageView;
  487.   
  488.   var iterator = aDocument.createTreeWalker(aDocument, NodeFilter.SHOW_ELEMENT, grabAll, true);
  489.  
  490.   var meter = document.getElementById("piProgress");
  491.  
  492.   meter.setAttribute("value", 1);
  493.  
  494.   setTimeout(doGrab, 1, iterator, meter, 0);
  495. }
  496.  
  497. function doGrab(iterator, meter, i)
  498. {
  499.   if (iterator.nextNode())
  500.   {
  501.     setTimeout(doGrab, 1, iterator, meter, i);
  502.   }
  503.   else
  504.   {
  505.     meter.setAttribute("value", 0);
  506.     meter.setAttribute("mode", "determined");
  507.     meter.setAttribute("hidden", "true");
  508.     document.getElementById("piSpacer").setAttribute("flex", 1);
  509.   }
  510. }
  511.  
  512. function ensureSelection(view)
  513. {
  514.   if (view.selection.count == 0) // only select something if nothing is currently selected
  515.     view.selection.select(0);
  516. }
  517.  
  518. function grabAll(elem)
  519. {
  520.   var linktext;
  521.  
  522.   // check for background images, any node may have one
  523.   var url = elem.ownerDocument.defaultView.getComputedStyle(elem, "").getPropertyCSSValue("background-image");
  524.   if (url && url.primitiveType == CSSPrimitiveValue.CSS_URI)
  525.     imageView.addRow([url.getStringValue(), gStrings.mediaBGImg, gStrings.notSet, elem, true]);
  526.  
  527.   // one swi^H^H^Hif-else to rule them all
  528.   if (elem instanceof nsIAnchorElement)
  529.   {
  530.     linktext = getValueText(elem);
  531.     linkView.addRow([linktext, getAbsoluteURL(elem.href, elem), gStrings.linkAnchor, elem.target]);
  532.   }
  533.   else if (elem instanceof nsIImageElement)
  534.   {
  535.     imageView.addRow([getAbsoluteURL(elem.src, elem), gStrings.mediaImg, (elem.hasAttribute("alt")) ? elem.alt : gStrings.notSet, elem, false]);
  536.   }
  537.   else if (elem instanceof nsIAreaElement)
  538.   {
  539.     linkView.addRow([elem.alt, getAbsoluteURL(elem.href, elem), gStrings.linkArea, elem.target]);
  540.   }
  541.   else if (elem instanceof nsILinkElement)
  542.   {
  543.     if (elem.rel)
  544.     {
  545.       var rel = elem.rel;
  546.       if (/\bicon\b/i.test(rel))
  547.         imageView.addRow([getAbsoluteURL(elem.href, elem), gStrings.mediaLink, "", elem, false]);
  548.       else if (/\bstylesheet\b/i.test(rel))
  549.         linkView.addRow([elem.rel, getAbsoluteURL(elem.href, elem), gStrings.linkStylesheet, elem.target]);
  550.       else
  551.         linkView.addRow([elem.rel, getAbsoluteURL(elem.href, elem), gStrings.linkRel, elem.target]);
  552.     }
  553.     else
  554.       linkView.addRow([elem.rev, getAbsoluteURL(elem.href, elem), gStrings.linkRev, elem.target]);
  555.  
  556.   }
  557.   else if (elem instanceof nsIInputElement)
  558.   {
  559.     if (/^image$/i.test(elem.type))
  560.       imageView.addRow([getAbsoluteURL(elem.src, elem), gStrings.mediaInput, (elem.hasAttribute("alt")) ? elem.alt : gStrings.notSet, elem, false]);
  561.     else if (/^submit$/i.test(elem.type))
  562.       linkView.addRow([elem.value || gStrings.linkSubmit, getAbsoluteURL(elem.form.getAttribute("action"), elem), gStrings.linkSubmission, elem.form.getAttribute("target")]); // use getAttribute() due to bug 122128
  563.   }
  564.   else if (elem instanceof nsIFormElement)
  565.   {
  566.     formView.addRow([elem.name, elem.method, getAbsoluteURL(elem.getAttribute("action"), elem), elem]);  // use getAttribute() because of bug 122128
  567.   }
  568.   else if (elem instanceof nsIAppletElement)
  569.   {
  570.     //XXX When Java is enabled, the DOM model for <APPLET> is broken. Bug #59686.
  571.     // Also, some reports of a crash with Java in Media tab (bug 136535), and mixed
  572.     // content from two hosts (bug 136539) so just drop applets from Page Info when
  573.     // Java is on. For the 1.0.1 branch; get a real fix on the trunk.
  574.     if (!navigator.javaEnabled())
  575.       imageView.addRow([getAbsoluteURL(elem.code || elem.object, elem), gStrings.mediaApplet, "", elem, false]);
  576.   }
  577.   else if (elem instanceof nsIObjectElement)
  578.   {
  579.     imageView.addRow([getAbsoluteURL(elem.data, elem), gStrings.mediaObject, getValueText(elem), elem, false]);
  580.   }
  581.   else if (elem instanceof nsIEmbedElement)
  582.   {
  583.     imageView.addRow([getAbsoluteURL(elem.src, elem), gStrings.mediaEmbed, "", elem, false]);
  584.   }
  585.   else
  586.     if (elem.hasAttributeNS(XLinkNS, "href"))
  587.     {
  588.       linktext = getValueText(elem);
  589.       linkView.addRow([linktext, getAbsoluteURL(elem.href, elem), gStrings.linkX, ""]);
  590.     }
  591.  
  592.   return NodeFilter.FILTER_ACCEPT;
  593. }
  594.  
  595. //******** Form Stuff
  596. function onFormSelect()
  597. {
  598.   var formTree = document.getElementById("formtree");
  599.   if (!formView.rowCount) return;
  600.  
  601.   if (formView.selection.count == 1)
  602.   {
  603.     var formPreview = document.getElementById("formpreview");
  604.     fieldView.clear();
  605.     formPreview.treeBoxObject.view = fieldView;
  606.  
  607.     var clickedRow = formView.selection.currentIndex;
  608.     var form = formView.getCellText(clickedRow, "form-node");
  609.  
  610.     var ft = null;
  611.     if (form.name)
  612.       ft = theBundle.getFormattedString("formTitle", [form.name]);
  613.     else
  614.       ft = theBundle.getString("formUntitled");
  615.  
  616.     document.getElementById("formname").value = ft || theBundle.getString("formUntitled");
  617.     document.getElementById("formenctype").value = form.encoding || theBundle.getString("default");
  618.     document.getElementById("formtarget").value = form.target || theBundle.getString("formDefaultTarget");
  619.  
  620.     var formfields = findFormControls(form);
  621.  
  622.     var length = formfields.length;
  623.     var i = 0;
  624.  
  625.     var checked = theBundle.getString("formChecked");
  626.     var unchecked = theBundle.getString("formUnchecked");    
  627.  
  628.     for (i = 0; i < length; i++)
  629.     {
  630.       var elem = formfields[i], val;
  631.  
  632.       if (elem instanceof nsIButtonElement)
  633.         val = getValueText(elem);
  634.       else
  635.         val = (/^password$/i.test(elem.type)) ? theBundle.getString("formPassword") : elem.value;
  636.  
  637.       fieldView.addRow(["", elem.id || elem.name, elem.type, val]);
  638.     }
  639.  
  640.     var labels = form.getElementsByTagName("label");
  641.     var llength = labels.length;
  642.  
  643.     for (i = 0; i < llength; i++)
  644.     {
  645.       var whatfor = labels[i].hasAttribute("for") ?
  646.         theDocument.getElementById(labels[i].getAttribute("for")) :
  647.         findFirstControl(labels[i]);
  648.  
  649.       if (whatfor && (whatfor.form == form)) {
  650.         var labeltext = getValueText(labels[i]);
  651.         for (var j = 0; j < length; j++)
  652.           if (formfields[j] == whatfor)
  653.             fieldView.setCellText(j, "field-label", labeltext);
  654.       }
  655.     }
  656.  
  657.     fieldView.rowCountChanged(0, length);
  658.   }
  659. }
  660.  
  661. function FormControlFilter(node) 
  662. {
  663.   if (node instanceof nsIInputElement || node instanceof nsISelectElement ||
  664.       node instanceof nsIButtonElement || node instanceof nsITextareaElement ||
  665.       node instanceof nsIObjectElement)
  666.       return NodeFilter.FILTER_ACCEPT;
  667.     return NodeFilter.FILTER_SKIP;
  668. }
  669.  
  670. function findFirstControl(node)
  671. {
  672.   var iterator = theDocument.createTreeWalker(node, NodeFilter.SHOW_ELEMENT, FormControlFilter, true);
  673.  
  674.   return iterator.nextNode();
  675. }
  676.  
  677. function findFormControls(node)
  678. {
  679.   var iterator = theDocument.createTreeWalker(node, NodeFilter.SHOW_ELEMENT, FormControlFilter, true);
  680.  
  681.   var list = [];
  682.   while (iterator.nextNode())
  683.     list.push(iterator.currentNode);
  684.  
  685.   return list;
  686. }
  687.  
  688. //******** Link Stuff
  689. function openURL(target)
  690. {
  691.   var url = target.parentNode.childNodes[2].value;
  692.   window.open(url, "_blank", "chrome");
  693. }
  694.  
  695. function onBeginLinkDrag(event,urlField,descField)
  696. {
  697.   if (event.originalTarget.localName != "treechildren")
  698.     return;
  699.  
  700.   var tree = event.target;
  701.   if (!("treeBoxObject" in tree))
  702.     tree = tree.parentNode;
  703.  
  704.   var row = {};
  705.   var col = {};
  706.   var elt = {};
  707.   tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, elt);
  708.   if (row.value == -1)
  709.     return;
  710.  
  711.   // Getting drag-system needed services
  712.   var dragService = Components.classes[DRAGSERVICE_CONTRACTID].getService().QueryInterface(Components.interfaces.nsIDragService);
  713.   var transArray = Components.classes[ARRAY_CONTRACTID].createInstance(Components.interfaces.nsISupportsArray);
  714.   if (!transArray)
  715.     return;
  716.   var trans = Components.classes[TRANSFERABLE_CONTRACTID].createInstance(Components.interfaces.nsITransferable);
  717.   if (!trans)
  718.     return;
  719.   
  720.   // Adding URL flavor
  721.   trans.addDataFlavor("text/x-moz-url");
  722.   var url = tree.treeBoxObject.view.getCellText(row.value, urlField);
  723.   var desc = tree.treeBoxObject.view.getCellText(row.value, descField);
  724.   var stringURL = Components.classes[STRING_CONTRACTID].createInstance(Components.interfaces.nsISupportsString);
  725.   stringURL.data = url + "\n"+desc;
  726.   trans.setTransferData("text/x-moz-url", stringURL, stringURL.data.length * 2 );
  727.   transArray.AppendElement(trans.QueryInterface(Components.interfaces.nsISupports));
  728.  
  729.   dragService.invokeDragSession(event.target, transArray, null, dragService.DRAGDROP_ACTION_NONE);
  730. }
  731.  
  732. //******** Image Stuff
  733. function getSource(item)
  734. {
  735.   // Return the correct source without strict warnings
  736.   if (item.href != null)
  737.     return item.href;
  738.   if (item.src != null)
  739.     return item.src;
  740.   return null;
  741. }
  742.  
  743. function getSelectedImage(tree)
  744. {
  745.   if (!imageView.rowCount) return null;
  746.  
  747.   // Only works if only one item is selected
  748.   var clickedRow = tree.treeBoxObject.selection.currentIndex;
  749.   return imageView.getCellText(clickedRow, "image-node");
  750. }
  751.  
  752. function saveMedia()
  753. {
  754.   var tree = document.getElementById("imagetree");
  755.   var item = getSelectedImage(tree);
  756.   var url = getAbsoluteURL(getSource(item), item);
  757.  
  758.   if (url)
  759.     saveURL(url, null, 'SaveImageTitle', false );
  760. }
  761.  
  762. function onImageSelect()
  763. {
  764.   var tree = document.getElementById("imagetree");
  765.   var saveAsButton = document.getElementById("imagesaveasbutton");
  766.  
  767.   if (tree.treeBoxObject.selection.count == 1)
  768.   {
  769.     makePreview(tree.treeBoxObject.selection.currentIndex);
  770.     saveAsButton.setAttribute("disabled", "false");
  771.   }
  772.   else
  773.     saveAsButton.setAttribute("disabled", "true");
  774. }
  775.  
  776. function makePreview(row)
  777. {
  778.   var item = getSelectedImage(document.getElementById("imagetree"));
  779.   var url = imageView.getCellText(row, "image-address");
  780.   var isBG = imageView.getCellText(row, "image-bg");
  781.  
  782.   document.getElementById("imageurltext").value = url;
  783.   document.getElementById("imagetitletext").value = item.title || gStrings.notSet;
  784.  
  785.   var altText = null;
  786.   if (item.hasAttribute("alt") && ("alt" in item))
  787.     altText = item.alt;
  788.   else if (!isBG)
  789.     altText = getValueText(item);
  790.   if (altText == null)
  791.     altText = gStrings.notSet;
  792.   var textbox=document.getElementById("imagealttext");
  793.   
  794.   // IMO all text that is not really the value text should go in italics
  795.   // What if somebody has <img alt="Not specified">? =)
  796.   // We can't use textbox.style because of bug 7639
  797.   if (!altText) {
  798.       textbox.value = gStrings.emptyString;
  799.       textbox.setAttribute("style","font-style:italic");
  800.   } else {
  801.       textbox.value = altText;
  802.       textbox.setAttribute("style","font-style:inherit");
  803.   }
  804.   document.getElementById("imagelongdesctext").value = ("longDesc" in item && item.longDesc) || gStrings.notSet;
  805.  
  806.   // get cache info
  807.   var sourceText = theBundle.getString("generalNotCached");
  808.   var expirationText = gStrings.unknown;
  809.   var sizeText = gStrings.unknown;
  810.  
  811.   var pageSize = 0; 
  812.   var kbSize = 0;
  813.   var expirationTime = 0;
  814.   var expirationDate = null;
  815.  
  816.   try
  817.   {
  818.     var cacheEntryDescriptor = httpCacheSession.openCacheEntry(url, Components.interfaces.nsICache.ACCESS_READ, false);   // open for READ, in non-blocking mode
  819.     if (cacheEntryDescriptor)
  820.     {
  821.       switch(cacheEntryDescriptor.deviceID)
  822.       {
  823.         case "disk":
  824.           sourceText = theBundle.getString("generalDiskCache");
  825.           break;
  826.         case "memory":
  827.           sourceText = theBundle.getString("generalMemoryCache");
  828.           break;
  829.         default:
  830.           sourceText = cacheEntryDescriptor.deviceID;
  831.           break;
  832.       }
  833.     }
  834.   }
  835.   catch(ex)
  836.   {
  837.     try
  838.     {
  839.       cacheEntryDescriptor = ftpCacheSession.openCacheEntry(url, Components.interfaces.nsICache.ACCESS_READ, false);   // open for READ, in non-blocking mode
  840.       if (cacheEntryDescriptor)
  841.       {
  842.         switch(cacheEntryDescriptor.deviceID)
  843.         {
  844.           case "disk":
  845.             sourceText = theBundle.getString("generalDiskCache");
  846.             break;
  847.           case "memory":
  848.             sourceText = theBundle.getString("generalMemoryCache");
  849.             break;
  850.           default:
  851.             sourceText = cacheEntryDescriptor.deviceID;
  852.             break;
  853.         }
  854.       }
  855.     }
  856.     catch(ex2)
  857.     {
  858.       sourceText = theBundle.getString("generalNotCached");
  859.     }
  860.   }
  861.  
  862.   // find out the mime type, file size and expiration date
  863.   var mimeType = gStrings.unknown, httpType;
  864.   if (cacheEntryDescriptor)
  865.   {
  866.     var headers, match;
  867.  
  868.     pageSize = cacheEntryDescriptor.dataSize;
  869.     kbSize = pageSize / 1024;
  870.     sizeText = theBundle.getFormattedString("generalSize", [Math.round(kbSize*100)/100, pageSize]);
  871.  
  872.     expirationText = formatDate(cacheEntryDescriptor.expirationTime*1000, gStrings.notSet);
  873.  
  874.     headers = cacheEntryDescriptor.getMetaDataElement("response-head");
  875.  
  876.     match = /^Content-Type:\s*(.*?)\s*(?:\;|$)/mi.exec(headers);
  877.     if (match)
  878.       httpType = match[1];
  879.   }
  880.  
  881.   if (!(item instanceof nsIInputElement))
  882.     mimeType = ("type" in item && item.type) ||
  883.                ("codeType" in item && item.codeType) ||
  884.                ("contentType" in item && item.contentType) ||
  885.                httpType || gStrings.unknown;
  886.  
  887.   document.getElementById("imagetypetext").value = mimeType;
  888.   document.getElementById("imagesourcetext").value = sourceText;
  889.   document.getElementById("imageexpirestext").value = expirationText;
  890.   document.getElementById("imagesizetext").value = sizeText;
  891.  
  892.   var imageContainer = document.getElementById("theimagecontainer");
  893.   var oldImage = document.getElementById("thepreviewimage");
  894.  
  895.   var regex = new RegExp("^(https?|ftp|file|gopher)://");
  896.   var absoluteURL = getAbsoluteURL(url, item);
  897.   var isProtocolAllowed = regex.test(absoluteURL); 
  898.   var newImage = new Image();
  899.   newImage.setAttribute("id", "thepreviewimage");
  900.   var physWidth = 0, physHeight = 0;
  901.  
  902.   if ((item instanceof nsILinkElement || item instanceof nsIInputElement || 
  903.        item instanceof nsIImageElement || isBG) && isProtocolAllowed)  
  904.   {
  905.     newImage.src = absoluteURL;
  906.     physWidth = newImage.width;
  907.     physHeight = newImage.height;
  908.  
  909.     if ("width" in item && item.width)
  910.       newImage.width = item.width;
  911.     if ("height" in item && item.height)
  912.       newImage.height = item.height;
  913.   } 
  914.   else 
  915.   {
  916.     // fallback image for protocols not allowed (e.g., data: or javascript:) 
  917.     // or elements not [yet] handled (e.g., object, embed). XXX blank??
  918.     newImage.src = "resource:///res/loading-image.gif";
  919.     newImage.width = 40;
  920.     newImage.height = 40;
  921.   }
  922.  
  923.   var width = ("width" in item && item.width) || ("width" in newImage && newImage.width) || "0";
  924.   var height = ("height" in item && item.height) || ("height" in newImage && newImage.height) || "0";
  925.  
  926.   document.getElementById("imageSize").value = theBundle.getFormattedString("mediaSize", [width, height]);
  927.  
  928.   if (width != physWidth || height != physHeight)
  929.   {
  930.     document.getElementById("physSize").removeAttribute("hidden");
  931.     document.getElementById("physSize").value = theBundle.getFormattedString("mediaPhysSize", [physWidth, physHeight]);
  932.   }
  933.   else
  934.     document.getElementById("physSize").setAttribute("hidden", "true");
  935.  
  936.  
  937.   imageContainer.removeChild(oldImage);
  938.   imageContainer.appendChild(newImage);
  939. }
  940.  
  941.  
  942. //******** Other Misc Stuff
  943. // Modified from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
  944. // parse a node to extract the contents of the node
  945. // linkNode doesn't really _have_ to be link
  946. function getValueText(linkNode)
  947. {
  948.   var valueText = "";
  949.   
  950.   var length = linkNode.childNodes.length;
  951.   for (var i = 0; i < length; i++)
  952.   {
  953.     var childNode = linkNode.childNodes[i];
  954.     var nodeType = childNode.nodeType;
  955.     if (nodeType == Node.TEXT_NODE)
  956.       valueText += " " + childNode.nodeValue;
  957.     else if (nodeType == Node.ELEMENT_NODE)
  958.     {
  959.       if (childNode instanceof nsIImageElement)
  960.         valueText += " " + getAltText(childNode);
  961.       else
  962.         valueText += " " + getValueText(childNode);
  963.     }
  964.   }
  965.  
  966.   return stripWS(valueText);
  967. }
  968.  
  969. // Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
  970. // traverse the tree in search of an img or area element and grab its alt tag
  971. function getAltText(node)
  972. {
  973.   var altText = "";
  974.   
  975.   if (node.alt)
  976.     return node.alt;
  977.   var length = node.childNodes.length;
  978.   for (var i = 0; i < length; i++)
  979.     if ((altText = getAltText(node.childNodes[i]) != undefined))  // stupid js warning...
  980.       return altText;
  981.   return "";
  982. }
  983.  
  984. // Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
  985. // strip leading and trailing whitespace, and replace multiple consecutive whitespace characters with a single space
  986. function stripWS(text)
  987. {
  988.   var middleRE = /\s+/g;
  989.   var endRE = /(^\s+)|(\s+$)/g;
  990.  
  991.   text = text.replace(middleRE, " ");
  992.   return text.replace(endRE, "");
  993. }
  994.  
  995. function formatDate(datestr, unknown)
  996. {
  997.   var date = new Date(datestr);
  998.   return (date.valueOf()) ? dateService.FormatDateTime("", dateService.dateFormatLong, dateService.timeFormatSeconds, date.getFullYear(), date.getMonth()+1, date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds()) : unknown;
  999. }
  1000.  
  1001. /*
  1002.  * Takes care of XMLBase and <base>
  1003.  * url is the possibly relative url.
  1004.  * node is the node where the url was given (needed for XMLBase)
  1005.  *
  1006.  * This function is called in many places as a workaround for bug 72524
  1007.  * Once bug 72522 is fixed this code should use the Node.baseURI attribute
  1008.  *
  1009.  * for node==null or url=="", empty string is returned
  1010.  *
  1011.  * This is basically just copied from http://lxr.mozilla.org/seamonkey/source/xpfe/browser/resources/content/metadata.js,
  1012.  * though I've modified it so that it doesn't assign to .spec
  1013.  */
  1014.  
  1015. function getAbsoluteURL(url, node)
  1016. {
  1017.   if (!url || !node)
  1018.     return "";
  1019.   var urlArr = new Array(url);
  1020.  
  1021.   var doc = node.ownerDocument;
  1022.   if (node.nodeType == Node.ATTRIBUTE_NODE)
  1023.     node = node.ownerElement;
  1024.  
  1025.   while (node && node.nodeType == Node.ELEMENT_NODE) 
  1026.   {
  1027.     var att = node.getAttributeNS(XMLNS, "base");
  1028.     if (att != "")
  1029.       urlArr.unshift(att);
  1030.  
  1031.     node = node.parentNode;
  1032.   }
  1033.  
  1034.   // Look for a <base>.
  1035.   var baseTags = doc.getElementsByTagNameNS(XHTMLNS, "base");
  1036.  
  1037.   if (baseTags && baseTags.length) 
  1038.   {
  1039.     urlArr.unshift(baseTags[baseTags.length - 1].getAttribute("href"));
  1040.   }
  1041.  
  1042.   // resolve everything from bottom up, starting with document location
  1043.   var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
  1044.   var URL = ioService.newURI(doc.location.href, null, null);
  1045.  
  1046.   for (var i=0; i<urlArr.length; i++) 
  1047.   {
  1048.     try
  1049.     {
  1050.       URL = ioService.newURI(urlArr[i], URL.originCharset, URL);
  1051.     }
  1052.     catch (ex)
  1053.     {
  1054.       ; // do nothing
  1055.     }
  1056.   }
  1057.  
  1058.   return URL.spec;
  1059. }
  1060.  
  1061. function doCopy(event)
  1062. {
  1063.   if (!gClipboardHelper) 
  1064.     return;
  1065.  
  1066.   var elem = event.originalTarget;
  1067.   if (elem && "treeBoxObject" in elem)
  1068.     elem.treeBoxObject.view.performActionOnRow("copy", elem.currentIndex);
  1069.  
  1070.   var text = elem.getAttribute("copybuffer");
  1071.   if (text) 
  1072.     gClipboardHelper.copyString(text);
  1073. }
  1074.  
  1075.