pageInfo.js
Go to the documentation of this file.
1 # -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 # ***** BEGIN LICENSE BLOCK *****
3 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 #
5 # The contents of this file are subject to the Mozilla Public License Version
6 # 1.1 (the "License"); you may not use this file except in compliance with
7 # the License. You may obtain a copy of the License at
8 # http://www.mozilla.org/MPL/
9 #
10 # Software distributed under the License is distributed on an "AS IS" basis,
11 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 # for the specific language governing rights and limitations under the
13 # License.
14 #
15 # The Original Code is mozilla.org code.
16 #
17 # The Initial Developer of the Original Code is
18 # Netscape Communications Corporation.
19 # Portions created by the Initial Developer are Copyright (C) 1998-2004
20 # the Initial Developer. All Rights Reserved.
21 #
22 # Contributor(s):
23 # smorrison@gte.com
24 # Terry Hayes <thayes@netscape.com>
25 # Daniel Brooks <db48x@yahoo.com>
26 # Florian QUEZE <f.qu@queze.net>
27 # Erik Fabert <jerfa@yahoo.com>
28 #
29 # Alternatively, the contents of this file may be used under the terms of
30 # either the GNU General Public License Version 2 or later (the "GPL"), or
31 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 # in which case the provisions of the GPL or the LGPL are applicable instead
33 # of those above. If you wish to allow use of your version of this file only
34 # under the terms of either the GPL or the LGPL, and not to allow others to
35 # use your version of this file under the terms of the MPL, indicate your
36 # decision by deleting the provisions above and replace them with the notice
37 # and other provisions required by the GPL or the LGPL. If you do not delete
38 # the provisions above, a recipient may use your version of this file under
39 # the terms of any one of the MPL, the GPL or the LGPL.
40 #
41 # ***** END LICENSE BLOCK *****
42 
43 //******** define a js object to implement nsITreeView
44 function pageInfoTreeView(copycol)
45 {
46  // copycol is the index number for the column that we want to add to
47  // the copy-n-paste buffer when the user hits accel-c
48  this.copycol = copycol;
49  this.rows = 0;
50  this.tree = null;
51  this.data = [ ];
52  this.selection = null;
53  this.sortcol = null;
54  this.sortdir = 0;
55 }
56 
57 pageInfoTreeView.prototype = {
58  set rowCount(c) { throw "rowCount is a readonly property"; },
59  get rowCount() { return this.rows; },
60 
61  setTree: function(tree)
62  {
63  this.tree = tree;
64  },
65 
66  getCellText: function(row, column)
67  {
68  // row can be null, but js arrays are 0-indexed.
69  // colidx cannot be null, but can be larger than the number
70  // of columns in the array. In this case it's the fault of
71  // whoever typoed while calling this function.
72  return this.data[row][column.index] || "";
73  },
74 
75  setCellValue: function(row, column, value)
76  {
77  },
78 
79  setCellText: function(row, column, value)
80  {
81  this.data[row][column.index] = value;
82  },
83 
84  addRow: function(row)
85  {
86  this.rows = this.data.push(row);
87  this.rowCountChanged(this.rows - 1, 1);
88  if (this.selection.count == 0 && this.rowCount && !gImageElement)
89  this.selection.select(0);
90  },
91 
92  rowCountChanged: function(index, count)
93  {
94  this.tree.rowCountChanged(index, count);
95  },
96 
97  invalidate: function()
98  {
99  this.tree.invalidate();
100  },
101 
102  clear: function()
103  {
104  if (this.tree)
105  this.tree.rowCountChanged(0, -this.rows);
106  this.rows = 0;
107  this.data = [ ];
108  },
109 
110  handleCopy: function(row)
111  {
112  return (row < 0 || this.copycol < 0) ? "" : (this.data[row][this.copycol] || "");
113  },
114 
115  performActionOnRow: function(action, row)
116  {
117  if (action == "copy") {
118  var data = this.handleCopy(row)
119  this.tree.treeBody.parentNode.setAttribute("copybuffer", data);
120  }
121  },
122 
123  getRowProperties: function(row, prop) { },
124  getCellProperties: function(row, column, prop) { },
125  getColumnProperties: function(column, prop) { },
126  isContainer: function(index) { return false; },
127  isContainerOpen: function(index) { return false; },
128  isSeparator: function(index) { return false; },
129  isSorted: function() { },
130  canDrop: function(index, orientation) { return false; },
131  drop: function(row, orientation) { return false; },
132  getParentIndex: function(index) { return 0; },
133  hasNextSibling: function(index, after) { return false; },
134  getLevel: function(index) { return 0; },
135  getImageSrc: function(row, column) { },
136  getProgressMode: function(row, column) { },
137  getCellValue: function(row, column) { },
138  toggleOpenState: function(index) { },
139  cycleHeader: function(col) { },
140  selectionChanged: function() { },
141  cycleCell: function(row, column) { },
142  isEditable: function(row, column) { return false; },
143  isSelectable: function(row, column) { return false; },
144  performAction: function(action) { },
145  performActionOnCell: function(action, row, column) { }
146 };
147 
148 // mmm, yummy. global variables.
149 var gWindow = null;
152 
153 // column number to help using the data array
155 const COL_IMAGE_TYPE = 1;
156 const COL_IMAGE_SIZE = 2;
157 const COL_IMAGE_ALT = 3;
158 const COL_IMAGE_COUNT = 4;
159 const COL_IMAGE_NODE = 5;
160 const COL_IMAGE_BG = 6;
161 
162 // column number to copy from, second argument to pageInfoTreeView's constructor
163 const COPYCOL_NONE = -1;
166 
167 // one nsITreeView for each tree in the window
170 
171 gImageView.getCellProperties = function(row, col, props) {
172  var aserv = Components.classes[ATOM_CONTRACTID]
173  .getService(Components.interfaces.nsIAtomService);
174 
175  if (gImageView.data[row][COL_IMAGE_SIZE] == gStrings.unknown &&
176  !/^https:/.test(gImageView.data[row][COL_IMAGE_ADDRESS]))
177  props.AppendElement(aserv.getAtom("broken"));
178 };
179 
180 var gImageHash = { };
181 
182 // localized strings (will be filled in when the document is loaded)
183 // this isn't all of them, these are just the ones that would otherwise have been loaded inside a loop
184 var gStrings = { };
186 
187 const PERMISSION_CONTRACTID = "@mozilla.org/permissionmanager;1";
188 const PREFERENCES_CONTRACTID = "@mozilla.org/preferences-service;1";
189 const ATOM_CONTRACTID = "@mozilla.org/atom-service;1";
190 
191 // a number of services I'll need later
192 // the cache services
193 const nsICacheService = Components.interfaces.nsICacheService;
194 const ACCESS_READ = Components.interfaces.nsICache.ACCESS_READ;
195 const cacheService = Components.classes["@mozilla.org/network/cache-service;1"].getService(nsICacheService);
196 var httpCacheSession = cacheService.createSession("HTTP", 0, true);
197 httpCacheSession.doomEntriesIfExpired = false;
198 var ftpCacheSession = cacheService.createSession("FTP", 0, true);
199 ftpCacheSession.doomEntriesIfExpired = false;
200 
201 const nsICookiePermission = Components.interfaces.nsICookiePermission;
202 const nsIPermissionManager = Components.interfaces.nsIPermissionManager;
203 
204 const nsICertificateDialogs = Components.interfaces.nsICertificateDialogs;
205 const CERTIFICATEDIALOGS_CONTRACTID = "@mozilla.org/nsCertificateDialogs;1"
206 
207 // clipboard helper
208 try {
209  const gClipboardHelper = Components.classes["@mozilla.org/widget/clipboardhelper;1"].getService(Components.interfaces.nsIClipboardHelper);
210 }
211 catch(e) {
212  // do nothing, later code will handle the error
213 }
214 
215 // Interface for image loading content
216 const nsIImageLoadingContent = Components.interfaces.nsIImageLoadingContent;
217 
218 // namespaces, don't need all of these yet...
219 const XLinkNS = "http://www.w3.org/1999/xlink";
220 const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
221 const XMLNS = "http://www.w3.org/XML/1998/namespace";
222 const XHTMLNS = "http://www.w3.org/1999/xhtml";
223 const XHTML2NS = "http://www.w3.org/2002/06/xhtml2"
224 
225 const XHTMLNSre = "^http\:\/\/www\.w3\.org\/1999\/xhtml$";
226 const XHTML2NSre = "^http\:\/\/www\.w3\.org\/2002\/06\/xhtml2$";
227 const XHTMLre = RegExp(XHTMLNSre + "|" + XHTML2NSre, "");
228 
229 /* Overlays register functions here.
230  * These arrays are used to hold callbacks that Page Info will call at
231  * various stages. Use them by simply appending a function to them.
232  * For example, add a function to onLoadRegistry by invoking
233  * "onLoadRegistry.push(XXXLoadFunc);"
234  * The XXXLoadFunc should be unique to the overlay module, and will be
235  * invoked as "XXXLoadFunc();"
236  */
237 
238 // These functions are called to build the data displayed in the Page
239 // Info window. The global variables gDocument and gWindow are set.
240 var onLoadRegistry = [ ];
241 
242 // These functions are called to remove old data still displayed in
243 // the window when the document whose information is displayed
244 // changes. For example, at this time, the list of images of the Media
245 // tab is cleared.
246 var onResetRegistry = [ ];
247 
248 // These are called once for each subframe of the target document and
249 // the target document itself. The frame is passed as an argument.
250 var onProcessFrame = [ ];
251 
252 // These functions are called once for each element (in all subframes, if any)
253 // in the target document. The element is passed as an argument.
255 
256 // These functions are called once when all the elements in all of the target
257 // document (and all of its subframes, if any) have been processed
258 var onFinished = [ ];
259 
260 // These functions are called once when the Page Info window is closed.
262 
263 
264 /* Called when PageInfo window is loaded. Arguments are:
265  * window.arguments[0] - (optional) an object consisting of
266  * - doc: (optional) document to use for source. if not provided,
267  * the calling window's document will be used
268  * - initialTab: (optional) id of the inital tab to display
269  */
270 function onLoadPageInfo()
271 {
272  gBundle = document.getElementById("pageinfobundle");
273  gStrings.unknown = gBundle.getString("unknown");
274  gStrings.notSet = gBundle.getString("notset");
275  gStrings.mediaImg = gBundle.getString("mediaImg");
276  gStrings.mediaBGImg = gBundle.getString("mediaBGImg");
277  gStrings.mediaObject = gBundle.getString("mediaObject");
278  gStrings.mediaEmbed = gBundle.getString("mediaEmbed");
279  gStrings.mediaLink = gBundle.getString("mediaLink");
280  gStrings.mediaInput = gBundle.getString("mediaInput");
281 
282  var args = "arguments" in window &&
283  window.arguments.length >= 1 &&
284  window.arguments[0];
285 
286  if (!args || !args.doc) {
287  gWindow = window.opener.content;
288  gDocument = gWindow.document;
289  }
290 
291  // init media view
292  var imageTree = document.getElementById("imagetree");
293  imageTree.view = gImageView;
294 
295  /* Select the requested tab, if the name is specified */
296  loadTab(args);
297  Components.classes["@mozilla.org/observer-service;1"]
298  .getService(Components.interfaces.nsIObserverService)
299  .notifyObservers(window, "page-info-dialog-loaded", null);
300 }
301 
302 function loadPageInfo()
303 {
304  var titleFormat = gWindow != gWindow.top ? "pageInfo.frame.title"
305  : "pageInfo.page.title";
306  document.title = gBundle.getFormattedString(titleFormat, [gDocument.location]);
307 
308  document.getElementById("main-window").setAttribute("relatedUrl", gDocument.location);
309 
310  // do the easy stuff first
311  makeGeneralTab();
312 
313  // and then the hard stuff
315 
316  initFeedTab();
318 
319  /* Call registered overlay init functions */
320  onLoadRegistry.forEach(function(func) { func(); });
321 }
322 
324 {
325  /* Reset Meta tags part */
326  gMetaView.clear();
327 
328  /* Reset Media tab */
329  var mediaTab = document.getElementById("mediaTab");
330  if (!mediaTab.hidden) {
331  Components.classes["@mozilla.org/observer-service;1"]
332  .getService(Components.interfaces.nsIObserverService)
333  .removeObserver(imagePermissionObserver, "perm-changed");
334  mediaTab.hidden = true;
335  }
336  gImageView.clear();
337  gImageHash = {};
338 
339  /* Reset Feeds Tab */
340  var feedListbox = document.getElementById("feedListbox");
341  while (feedListbox.firstChild)
342  feedListbox.removeChild(feedListbox.firstChild);
343 
344  /* Call registered overlay reset functions */
345  onResetRegistry.forEach(function(func) { func(); });
346 
347  /* Rebuild the data */
348  loadTab(args);
349 }
350 
352 {
353  // Remove the observer, only if there is at least 1 image.
354  if (!document.getElementById("mediaTab").hidden) {
355  Components.classes["@mozilla.org/observer-service;1"]
356  .getService(Components.interfaces.nsIObserverService)
357  .removeObserver(imagePermissionObserver, "perm-changed");
358  }
359 
360  /* Call registered overlay unload functions */
361  onUnloadRegistry.forEach(function(func) { func(); });
362 }
363 
364 function doHelpButton()
365 {
366  const helpTopics = {
367  "generalPanel": "pageinfo_general",
368  "mediaPanel": "pageinfo_media",
369  "feedPanel": "pageinfo_feed",
370  "permPanel": "pageinfo_permissions",
371  "securityPanel": "pageinfo_security"
372  };
373 
374  var deck = document.getElementById("mainDeck");
375  var helpdoc = helpTopics[deck.selectedPanel.id] || "pageinfo_general";
376  openHelpLink(helpdoc);
377 }
378 
379 function showTab(id)
380 {
381  var deck = document.getElementById("mainDeck");
382  var pagel = document.getElementById(id + "Panel");
383  deck.selectedPanel = pagel;
384 }
385 
386 function loadTab(args)
387 {
388  if (args && args.doc) {
389  gDocument = args.doc;
390  gWindow = gDocument.defaultView;
391  }
392 
393  gImageElement = args && args.imageElement;
394 
395  /* Load the page info */
396  loadPageInfo();
397 
398  var initialTab = (args && args.initialTab) || "generalTab";
399  var radioGroup = document.getElementById("viewGroup");
400  initialTab = document.getElementById(initialTab) || document.getElementById("generalTab");
401  radioGroup.selectedItem = initialTab;
402  radioGroup.selectedItem.doCommand();
403  radioGroup.focus();
404 }
405 
406 function onClickMore()
407 {
408  var radioGrp = document.getElementById("viewGroup");
409  var radioElt = document.getElementById("securityTab");
410  radioGrp.selectedItem = radioElt;
411  showTab('security');
412 }
413 
414 function toggleGroupbox(id)
415 {
416  var elt = document.getElementById(id);
417  if (elt.hasAttribute("closed")) {
418  elt.removeAttribute("closed");
419  if (elt.flexWhenOpened)
420  elt.flex = elt.flexWhenOpened;
421  }
422  else {
423  elt.setAttribute("closed", "true");
424  if (elt.flex) {
425  elt.flexWhenOpened = elt.flex;
426  elt.flex = 0;
427  }
428  }
429 }
430 
431 function makeGeneralTab()
432 {
433  var title = (gDocument.title) ? gBundle.getFormattedString("pageTitle", [gDocument.title]) : gBundle.getString("noPageTitle");
434  document.getElementById("titletext").value = title;
435 
436  var url = gDocument.location.toString();
437  setItemValue("urltext", url);
438 
439  var referrer = ("referrer" in gDocument && gDocument.referrer);
440  setItemValue("refertext", referrer);
441 
442  var mode = ("compatMode" in gDocument && gDocument.compatMode == "BackCompat") ? "generalQuirksMode" : "generalStrictMode";
443  document.getElementById("modetext").value = gBundle.getString(mode);
444 
445  // find out the mime type
446  var mimeType = gDocument.contentType;
447  setItemValue("typetext", mimeType);
448 
449  // get the document characterset
450  var encoding = gDocument.characterSet;
451  document.getElementById("encodingtext").value = encoding;
452 
453  // get the meta tags
454  var metaNodes = gDocument.getElementsByTagName("meta");
455  var length = metaNodes.length;
456 
457  var metaGroup = document.getElementById("metaTags");
458  if (!length)
459  metaGroup.collapsed = true;
460  else {
461  var metaTagsCaption = document.getElementById("metaTagsCaption");
462  if (length == 1)
463  metaTagsCaption.label = gBundle.getString("generalMetaTag");
464  else
465  metaTagsCaption.label = gBundle.getFormattedString("generalMetaTags", [length]);
466  var metaTree = document.getElementById("metatree");
467  metaTree.treeBoxObject.view = gMetaView;
468 
469  for (var i = 0; i < length; i++)
470  gMetaView.addRow([metaNodes[i].name || metaNodes[i].httpEquiv, metaNodes[i].content]);
471 
472  metaGroup.collapsed = false;
473  }
474 
475  // get the date of last modification
476  var modifiedText = formatDate(gDocument.lastModified, gStrings.notSet);
477  document.getElementById("modifiedtext").value = modifiedText;
478 
479  // get cache info
480  var cacheKey = url.replace(/#.*$/, "");
481  try {
482  var cacheEntryDescriptor = httpCacheSession.openCacheEntry(cacheKey, ACCESS_READ, false);
483  }
484  catch(ex) {
485  try {
486  cacheEntryDescriptor = ftpCacheSession.openCacheEntry(cacheKey, ACCESS_READ, false);
487  }
488  catch(ex2) { }
489  }
490 
491  var sizeText;
492  if (cacheEntryDescriptor) {
493  var pageSize = cacheEntryDescriptor.dataSize;
494  var kbSize = formatNumber(Math.round(pageSize / 1024 * 100) / 100);
495  sizeText = gBundle.getFormattedString("generalSize", [kbSize, formatNumber(pageSize)]);
496  }
497  setItemValue("sizetext", sizeText);
498 
499  securityOnLoad();
500 }
501 
502 //******** Generic Build-a-tab
503 // Assumes the views are empty. Only called once to build the tabs, and
504 // does so by farming the task off to another thread via setTimeout().
505 // The actual work is done with a TreeWalker that calls doGrab() once for
506 // each element node in the document.
507 
508 var gFrameList = [ ];
509 
510 function makeTabs(aDocument, aWindow)
511 {
512  goThroughFrames(aDocument, aWindow);
513  processFrames();
514 }
515 
516 function goThroughFrames(aDocument, aWindow)
517 {
518  gFrameList.push(aDocument);
519  if (aWindow && aWindow.frames.length > 0) {
520  var num = aWindow.frames.length;
521  for (var i = 0; i < num; i++)
522  goThroughFrames(aWindow.frames[i].document, aWindow.frames[i]); // recurse through the frames
523  }
524 }
525 
526 function processFrames()
527 {
528  if (gFrameList.length) {
529  var doc = gFrameList[0];
530  onProcessFrame.forEach(function(func) { func(doc); });
531  var iterator = doc.createTreeWalker(doc, NodeFilter.SHOW_ELEMENT, grabAll, true);
532  gFrameList.shift();
533  setTimeout(doGrab, 16, iterator);
534  onFinished.push(selectImage);
535  }
536  else
537  onFinished.forEach(function(func) { func(); });
538 }
539 
540 function doGrab(iterator)
541 {
542  for (var i = 0; i < 50; ++i)
543  if (!iterator.nextNode()) {
544  processFrames();
545  return;
546  }
547 
548  setTimeout(doGrab, 16, iterator);
549 }
550 
551 function addImage(url, type, alt, elem, isBg)
552 {
553  if (!url)
554  return;
555 
556  if (!gImageHash.hasOwnProperty(url))
557  gImageHash[url] = { };
558  if (!gImageHash[url].hasOwnProperty(type))
559  gImageHash[url][type] = { };
560  if (!gImageHash[url][type].hasOwnProperty(alt)) {
561  gImageHash[url][type][alt] = gImageView.data.length;
562  try {
563  // open for READ, in non-blocking mode
564  var cacheEntryDescriptor = httpCacheSession.openCacheEntry(url, ACCESS_READ, false);
565  }
566  catch(ex) {
567  try {
568  // open for READ, in non-blocking mode
569  cacheEntryDescriptor = ftpCacheSession.openCacheEntry(url, ACCESS_READ, false);
570  }
571  catch(ex2) { }
572  }
573 
574  var sizeText;
575  if (cacheEntryDescriptor) {
576  var pageSize = cacheEntryDescriptor.dataSize;
577  var kbSize = formatNumber(Math.round(pageSize / 1024 * 100) / 100);
578  sizeText = gBundle.getFormattedString("mediaFileSize", [kbSize]);
579  }
580  else
581  sizeText = gStrings.unknown;
582  gImageView.addRow([url, type, sizeText, alt, 1, elem, isBg]);
583 
584  // Add the observer, only once.
585  if (gImageView.data.length == 1) {
586  document.getElementById("mediaTab").hidden = false;
587  Components.classes["@mozilla.org/observer-service;1"]
588  .getService(Components.interfaces.nsIObserverService)
589  .addObserver(imagePermissionObserver, "perm-changed", false);
590  }
591  }
592  else {
593  var i = gImageHash[url][type][alt];
594  gImageView.data[i][COL_IMAGE_COUNT]++;
595  if (elem == gImageElement)
596  gImageView.data[i][COL_IMAGE_NODE] = elem;
597  }
598 }
599 
600 function grabAll(elem)
601 {
602  // check for background images, any node may have multiple
603  var computedStyle = elem.ownerDocument.defaultView.getComputedStyle(elem, "");
604  if (computedStyle) {
605  Array.forEach(computedStyle.getPropertyCSSValue("background-image"), function (url) {
606  if (url.primitiveType == CSSPrimitiveValue.CSS_URI)
607  addImage(url.getStringValue(), gStrings.mediaBGImg, gStrings.notSet, elem, true);
608  });
609  }
610 
611  // one swi^H^H^Hif-else to rule them all
612  if (elem instanceof HTMLImageElement)
613  addImage(elem.src, gStrings.mediaImg,
614  (elem.hasAttribute("alt")) ? elem.alt : gStrings.notSet, elem, false);
615 #ifdef MOZ_SVG
616  else if (elem instanceof SVGImageElement) {
617  try {
618  // Note: makeURLAbsolute will throw if either the baseURI is not a valid URI
619  // or the URI formed from the baseURI and the URL is not a valid URI
620  var href = makeURLAbsolute(elem.baseURI, elem.href.baseVal);
621  addImage(href, gStrings.mediaImg, "", elem, false);
622  } catch (e) { }
623  }
624 #endif
625  else if (elem instanceof HTMLLinkElement) {
626  if (elem.rel && /\bicon\b/i.test(elem.rel))
627  addImage(elem.href, gStrings.mediaLink, "", elem, false);
628  }
629  else if (elem instanceof HTMLInputElement || elem instanceof HTMLButtonElement) {
630  if (elem.type.toLowerCase() == "image")
631  addImage(elem.src, gStrings.mediaInput,
632  (elem.hasAttribute("alt")) ? elem.alt : gStrings.notSet, elem, false);
633  }
634  else if (elem instanceof HTMLObjectElement)
635  addImage(elem.data, gStrings.mediaObject, getValueText(elem), elem, false);
636  else if (elem instanceof HTMLEmbedElement)
637  addImage(elem.src, gStrings.mediaEmbed, "", elem, false);
638 
639  onProcessElement.forEach(function(func) { func(elem); });
640 
641  return NodeFilter.FILTER_ACCEPT;
642 }
643 
644 //******** Link Stuff
645 function openURL(target)
646 {
647  var url = target.parentNode.childNodes[2].value;
648  window.open(url, "_blank", "chrome");
649 }
650 
651 function onBeginLinkDrag(event,urlField,descField)
652 {
653  if (event.originalTarget.localName != "treechildren")
654  return;
655 
656  var tree = event.target;
657  if (!("treeBoxObject" in tree))
658  tree = tree.parentNode;
659 
660  var row = tree.treeBoxObject.getRowAt(event.clientX, event.clientY);
661  if (row == -1)
662  return;
663 
664  // Adding URL flavor
665  var col = tree.columns[urlField];
666  var url = tree.view.getCellText(row, col);
667  col = tree.columns[descField];
668  var desc = tree.view.getCellText(row, col);
669 
670  var dt = event.dataTransfer;
671  dt.setData("text/x-moz-url", url + "\n" + desc);
672  dt.setData("text/url-list", url);
673  dt.setData("text/plain", url);
674 }
675 
676 //******** Image Stuff
677 function getSelectedImage(tree)
678 {
679  if (!gImageView.rowCount)
680  return null;
681 
682  // Only works if only one item is selected
683  var clickedRow = tree.currentIndex;
684  // image-node
685  return gImageView.data[clickedRow][COL_IMAGE_NODE];
686 }
687 
689 {
690  const nsILocalFile = Components.interfaces.nsILocalFile;
691  const nsIFilePicker = Components.interfaces.nsIFilePicker;
692  var fp = Components.classes["@mozilla.org/filepicker;1"]
693  .createInstance(nsIFilePicker);
694 
695  var titleText = gBundle.getString("mediaSelectFolder");
696  fp.init(window, titleText, nsIFilePicker.modeGetFolder);
697  try {
698  var prefs = Components.classes[PREFERENCES_CONTRACTID]
699  .getService(Components.interfaces.nsIPrefBranch2);
700 
701  var initialDir = prefs.getComplexValue("browser.download.dir", nsILocalFile);
702  if (initialDir)
703  fp.displayDirectory = initialDir;
704  }
705  catch (ex) { }
706 
707  fp.appendFilters(nsIFilePicker.filterAll);
708  var ret = fp.show();
709 
710  if (ret == nsIFilePicker.returnOK)
711  return fp.file.QueryInterface(nsILocalFile);
712  return null;
713 }
714 
715 function saveMedia()
716 {
717  var tree = document.getElementById("imagetree");
718  var count = tree.view.selection.count;
719  if (count == 1) {
720  var item = getSelectedImage(tree);
721  var url = gImageView.data[tree.currentIndex][COL_IMAGE_ADDRESS];
722 
723  if (url)
724  saveURL(url, null, "SaveImageTitle", false, false, makeURI(item.baseURI));
725  }
726  else {
727  var odir = selectSaveFolder();
728  var start = { };
729  var end = { };
730  var numRanges = tree.view.selection.getRangeCount();
731 
732  var rowArray = [ ];
733  for (var t = 0; t < numRanges; t++) {
734  tree.view.selection.getRangeAt(t, start, end);
735  for (var v = start.value; v <= end.value; v++)
736  rowArray.push(v);
737  }
738 
739  var saveAnImage = function(aURIString, aChosenData, aBaseURI) {
740  internalSave(aURIString, null, null, null, null, false, "SaveImageTitle",
741  aChosenData, aBaseURI);
742  }
743 
744  for (var i = 0; i < rowArray.length; i++) {
745  var v = rowArray[i];
746  var dir = odir.clone();
747  var item = gImageView.data[v][COL_IMAGE_NODE];
748  var uriString = gImageView.data[v][COL_IMAGE_ADDRESS];
749  var uri = makeURI(uriString);
750 
751  try {
752  uri.QueryInterface(Components.interfaces.nsIURL);
753  dir.append(decodeURIComponent(uri.fileName));
754  }
755  catch(ex) { /* data: uris */ }
756 
757  if (i == 0)
758  saveAnImage(uriString, new AutoChosen(dir, uri), makeURI(item.baseURI));
759  else {
760  // This delay is a hack which prevents the download manager
761  // from opening many times. See bug 377339.
762  setTimeout(saveAnImage, 200, uriString, new AutoChosen(dir, uri),
763  makeURI(item.baseURI));
764  }
765  }
766  }
767 }
768 
769 function onBlockImage()
770 {
771  var permissionManager = Components.classes[PERMISSION_CONTRACTID]
772  .getService(nsIPermissionManager);
773 
774  var checkbox = document.getElementById("blockImage");
775  var uri = makeURI(document.getElementById("imageurltext").value);
776  if (checkbox.checked)
777  permissionManager.add(uri, "image", nsIPermissionManager.DENY_ACTION);
778  else
779  permissionManager.remove(uri.host, "image");
780 }
781 
782 function onImageSelect()
783 {
784  var previewBox = document.getElementById("mediaPreviewBox");
785  var mediaSaveBox = document.getElementById("mediaSaveBox");
786  var splitter = document.getElementById("mediaSplitter");
787  var tree = document.getElementById("imagetree");
788  var count = tree.view.selection.count;
789  if (count == 0) {
790  previewBox.collapsed = true;
791  mediaSaveBox.collapsed = true;
792  splitter.collapsed = true;
793  tree.flex = 1;
794  }
795  else if (count > 1) {
796  splitter.collapsed = true;
797  previewBox.collapsed = true;
798  mediaSaveBox.collapsed = false;
799  tree.flex = 1;
800  }
801  else {
802  mediaSaveBox.collapsed = true;
803  splitter.collapsed = false;
804  previewBox.collapsed = false;
805  tree.flex = 0;
806  makePreview(tree.view.selection.currentIndex);
807  }
808 }
809 
810 function makePreview(row)
811 {
812  var imageTree = document.getElementById("imagetree");
813  var item = getSelectedImage(imageTree);
814  var url = gImageView.data[row][COL_IMAGE_ADDRESS];
815  var isBG = gImageView.data[row][COL_IMAGE_BG];
816 
817  setItemValue("imageurltext", url);
818 
819  var imageText;
820  if (!isBG &&
821 #ifdef MOZ_SVG
822  !(item instanceof SVGImageElement) &&
823 #endif
824  !(gDocument instanceof ImageDocument)) {
825  imageText = item.title || item.alt;
826 
827  if (!imageText && !(item instanceof HTMLImageElement))
828  imageText = getValueText(item);
829  }
830  setItemValue("imagetext", imageText);
831 
832  setItemValue("imagelongdesctext", item.longDesc);
833 
834  // get cache info
835  var cacheKey = url.replace(/#.*$/, "");
836  try {
837  // open for READ, in non-blocking mode
838  var cacheEntryDescriptor = httpCacheSession.openCacheEntry(cacheKey, ACCESS_READ, false);
839  }
840  catch(ex) {
841  try {
842  // open for READ, in non-blocking mode
843  cacheEntryDescriptor = ftpCacheSession.openCacheEntry(cacheKey, ACCESS_READ, false);
844  }
845  catch(ex2) { }
846  }
847 
848  // find out the file size
849  var sizeText;
850  if (cacheEntryDescriptor) {
851  var imageSize = cacheEntryDescriptor.dataSize;
852  var kbSize = Math.round(imageSize / 1024 * 100) / 100;
853  sizeText = gBundle.getFormattedString("generalSize",
854  [formatNumber(kbSize), formatNumber(imageSize)]);
855  }
856  else
857  sizeText = gBundle.getString("mediaUnknownNotCached");
858  setItemValue("imagesizetext", sizeText);
859 
860  var mimeType;
861  var numFrames = 1;
862  if (item instanceof HTMLObjectElement ||
863  item instanceof HTMLEmbedElement ||
864  item instanceof HTMLLinkElement)
865  mimeType = item.type;
866 
867  if (!mimeType && !isBG && item instanceof nsIImageLoadingContent) {
868  var imageRequest = item.getRequest(nsIImageLoadingContent.CURRENT_REQUEST);
869  if (imageRequest) {
870  mimeType = imageRequest.mimeType;
871  var image = imageRequest.image;
872  if (image)
873  numFrames = image.numFrames;
874  }
875  }
876  if (!mimeType)
877  mimeType = getContentTypeFromHeaders(cacheEntryDescriptor);
878 
879  var imageType;
880  if (mimeType) {
881  // We found the type, try to display it nicely
882  var imageMimeType = /^image\/(.*)/.exec(mimeType);
883  if (imageMimeType) {
884  imageType = imageMimeType[1].toUpperCase();
885  if (numFrames > 1)
886  imageType = gBundle.getFormattedString("mediaAnimatedImageType",
887  [imageType, numFrames]);
888  else
889  imageType = gBundle.getFormattedString("mediaImageType", [imageType]);
890  }
891  else {
892  // the MIME type doesn't begin with image/, display the raw type
893  imageType = mimeType;
894  }
895  }
896  else {
897  // We couldn't find the type, fall back to the value in the treeview
898  imageType = gImageView.data[row][COL_IMAGE_TYPE];
899  }
900  setItemValue("imagetypetext", imageType);
901 
902  var imageContainer = document.getElementById("theimagecontainer");
903  var oldImage = document.getElementById("thepreviewimage");
904 
905  const regex = /^(https?|ftp|file|gopher|about|chrome|resource):/;
906  var isProtocolAllowed = regex.test(url);
907  if (/^data:/.test(url) && /^image\//.test(mimeType))
908  isProtocolAllowed = true;
909 
910  var newImage = new Image();
911  newImage.setAttribute("id", "thepreviewimage");
912  var physWidth = 0, physHeight = 0;
913  var width = 0, height = 0;
914 
915  if ((item instanceof HTMLLinkElement || item instanceof HTMLInputElement ||
916  item instanceof HTMLImageElement ||
917 #ifdef MOZ_SVG
918  item instanceof SVGImageElement ||
919 #endif
920  (item instanceof HTMLObjectElement && /^image\//.test(mimeType)) || isBG) && isProtocolAllowed) {
921  newImage.setAttribute("src", url);
922  physWidth = newImage.width || 0;
923  physHeight = newImage.height || 0;
924 
925  // "width" and "height" attributes must be set to newImage,
926  // even if there is no "width" or "height attribute in item;
927  // otherwise, the preview image cannot be displayed correctly.
928  if (!isBG) {
929  newImage.width = ("width" in item && item.width) || newImage.naturalWidth;
930  newImage.height = ("height" in item && item.height) || newImage.naturalHeight;
931  }
932  else {
933  // the Width and Height of an HTML tag should not be used for its background image
934  // (for example, "table" can have "width" or "height" attributes)
935  newImage.width = newImage.naturalWidth;
936  newImage.height = newImage.naturalHeight;
937  }
938 
939 #ifdef MOZ_SVG
940  if (item instanceof SVGImageElement) {
941  newImage.width = item.width.baseVal.value;
942  newImage.height = item.height.baseVal.value;
943  }
944 #endif
945 
946  width = newImage.width;
947  height = newImage.height;
948 
949  document.getElementById("theimagecontainer").collapsed = false
950  document.getElementById("brokenimagecontainer").collapsed = true;
951  }
952  else {
953  // fallback image for protocols not allowed (e.g., data: or javascript:)
954  // or elements not [yet] handled (e.g., object, embed).
955  document.getElementById("brokenimagecontainer").collapsed = false;
956  document.getElementById("theimagecontainer").collapsed = true;
957  }
958 
959  var imageSize = "";
960  if (url) {
961  if (width != physWidth || height != physHeight) {
962  imageSize = gBundle.getFormattedString("mediaDimensionsScaled",
963  [formatNumber(physWidth),
964  formatNumber(physHeight),
966  formatNumber(height)]);
967  }
968  else {
969  imageSize = gBundle.getFormattedString("mediaDimensions",
971  formatNumber(height)]);
972  }
973  }
974  setItemValue("imagedimensiontext", imageSize);
975 
977 
978  imageContainer.removeChild(oldImage);
979  imageContainer.appendChild(newImage);
980 }
981 
983 {
984  var permissionManager = Components.classes[PERMISSION_CONTRACTID]
985  .getService(nsIPermissionManager);
986  var prefs = Components.classes[PREFERENCES_CONTRACTID]
987  .getService(Components.interfaces.nsIPrefBranch2);
988 
989  var checkbox = document.getElementById("blockImage");
990  var imagePref = prefs.getIntPref("permissions.default.image");
991  if (!(/^https?:/.test(url)) || imagePref == 2)
992  // We can't block the images from this host because either is is not
993  // for http(s) or we don't load images at all
994  checkbox.hidden = true;
995  else {
996  var uri = makeURI(url);
997  if (uri.host) {
998  checkbox.hidden = false;
999  checkbox.label = gBundle.getFormattedString("mediaBlockImage", [uri.host]);
1000  var perm = permissionManager.testPermission(uri, "image");
1001  checkbox.checked = perm == nsIPermissionManager.DENY_ACTION;
1002  }
1003  else
1004  checkbox.hidden = true;
1005  }
1006 }
1007 
1008 var imagePermissionObserver = {
1009  observe: function (aSubject, aTopic, aData)
1010  {
1011  if (document.getElementById("mediaPreviewBox").collapsed)
1012  return;
1013 
1014  if (aTopic == "perm-changed") {
1015  var permission = aSubject.QueryInterface(Components.interfaces.nsIPermission);
1016  if (permission.type == "image") {
1017  var imageTree = document.getElementById("imagetree");
1018  var row = imageTree.currentIndex;
1019  var item = gImageView.data[row][COL_IMAGE_NODE];
1020  var url = gImageView.data[row][COL_IMAGE_ADDRESS];
1021  if (makeURI(url).host == permission.host)
1022  makeBlockImage(url);
1023  }
1024  }
1025  }
1026 }
1027 
1028 function getContentTypeFromHeaders(cacheEntryDescriptor)
1029 {
1030  if (!cacheEntryDescriptor)
1031  return null;
1032 
1033  return (/^Content-Type:\s*(.*?)\s*(?:\;|$)/mi
1034  .exec(cacheEntryDescriptor.getMetaDataElement("response-head")))[1];
1035 }
1036 
1037 //******** Other Misc Stuff
1038 // Modified from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
1039 // parse a node to extract the contents of the node
1040 function getValueText(node)
1041 {
1042  var valueText = "";
1043 
1044  // form input elements don't generally contain information that is useful to our callers, so return nothing
1045  if (node instanceof HTMLInputElement ||
1046  node instanceof HTMLSelectElement ||
1047  node instanceof HTMLTextAreaElement)
1048  return valueText;
1049 
1050  // otherwise recurse for each child
1051  var length = node.childNodes.length;
1052  for (var i = 0; i < length; i++) {
1053  var childNode = node.childNodes[i];
1054  var nodeType = childNode.nodeType;
1055 
1056  // text nodes are where the goods are
1057  if (nodeType == Node.TEXT_NODE)
1058  valueText += " " + childNode.nodeValue;
1059  // and elements can have more text inside them
1060  else if (nodeType == Node.ELEMENT_NODE) {
1061  // images are special, we want to capture the alt text as if the image weren't there
1062  if (childNode instanceof HTMLImageElement)
1063  valueText += " " + getAltText(childNode);
1064  else
1065  valueText += " " + getValueText(childNode);
1066  }
1067  }
1068 
1069  return stripWS(valueText);
1070 }
1071 
1072 // Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
1073 // traverse the tree in search of an img or area element and grab its alt tag
1074 function getAltText(node)
1075 {
1076  var altText = "";
1077 
1078  if (node.alt)
1079  return node.alt;
1080  var length = node.childNodes.length;
1081  for (var i = 0; i < length; i++)
1082  if ((altText = getAltText(node.childNodes[i]) != undefined)) // stupid js warning...
1083  return altText;
1084  return "";
1085 }
1086 
1087 // Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
1088 // strip leading and trailing whitespace, and replace multiple consecutive whitespace characters with a single space
1089 function stripWS(text)
1090 {
1091  var middleRE = /\s+/g;
1092  var endRE = /(^\s+)|(\s+$)/g;
1093 
1094  text = text.replace(middleRE, " ");
1095  return text.replace(endRE, "");
1096 }
1097 
1098 function setItemValue(id, value)
1099 {
1100  var item = document.getElementById(id);
1101  if (value) {
1102  item.parentNode.collapsed = false;
1103  item.value = value;
1104  }
1105  else
1106  item.parentNode.collapsed = true;
1107 }
1108 
1109 function formatNumber(number)
1110 {
1111  return (+number).toLocaleString(); // coerce number to a numeric value before calling toLocaleString()
1112 }
1113 
1114 function formatDate(datestr, unknown)
1115 {
1116  // scriptable date formatter, for pretty printing dates
1117  var dateService = Components.classes["@mozilla.org/intl/scriptabledateformat;1"]
1118  .getService(Components.interfaces.nsIScriptableDateFormat);
1119 
1120  var date = new Date(datestr);
1121  if (!date.valueOf())
1122  return unknown;
1123 
1124  return dateService.FormatDateTime("", dateService.dateFormatLong,
1125  dateService.timeFormatSeconds,
1126  date.getFullYear(), date.getMonth()+1, date.getDate(),
1127  date.getHours(), date.getMinutes(), date.getSeconds());
1128 }
1129 
1130 function doCopy()
1131 {
1132  if (!gClipboardHelper)
1133  return;
1134 
1135  var elem = document.commandDispatcher.focusedElement;
1136 
1137  if (elem && "treeBoxObject" in elem) {
1138  var view = elem.view;
1139  var selection = view.selection;
1140  var text = [], tmp = '';
1141  var min = {}, max = {};
1142 
1143  var count = selection.getRangeCount();
1144 
1145  for (var i = 0; i < count; i++) {
1146  selection.getRangeAt(i, min, max);
1147 
1148  for (var row = min.value; row <= max.value; row++) {
1149  view.performActionOnRow("copy", row);
1150 
1151  tmp = elem.getAttribute("copybuffer");
1152  if (tmp)
1153  text.push(tmp);
1154  elem.removeAttribute("copybuffer");
1155  }
1156  }
1157  gClipboardHelper.copyString(text.join("\n"));
1158  }
1159 }
1160 
1161 function doSelectAll()
1162 {
1163  var elem = document.commandDispatcher.focusedElement;
1164 
1165  if (elem && "treeBoxObject" in elem)
1166  elem.view.selection.selectAll();
1167 }
1168 
1169 function selectImage() {
1170  if (!gImageElement)
1171  return;
1172 
1173  var tree = document.getElementById("imagetree");
1174  for (var i = 0; i < tree.view.rowCount; i++) {
1175  if (gImageElement == gImageView.data[i][COL_IMAGE_NODE] &&
1176  !gImageView.data[i][COL_IMAGE_BG]) {
1177  tree.view.selection.select(i);
1178  tree.treeBoxObject.ensureRowIsVisible(i);
1179  tree.focus();
1180  return;
1181  }
1182  }
1183 }
function start(ch)
function saveMedia()
Definition: pageInfo.js:715
const PREFERENCES_CONTRACTID
Definition: pageInfo.js:188
const COL_IMAGE_SIZE
Definition: pageInfo.js:156
function doGrab(iterator)
Definition: pageInfo.js:540
const COL_IMAGE_BG
Definition: pageInfo.js:160
var args
Definition: alert.js:8
const XHTML2NS
Definition: pageInfo.js:223
function pageInfoTreeView(copycol)
Definition: pageInfo.js:44
function securityOnLoad()
Definition: security.js:188
_setDateDatepicker date
const XULNS
Definition: pageInfo.js:220
function onImageSelect()
Definition: pageInfo.js:782
function toggleGroupbox(id)
Definition: pageInfo.js:414
function makeTabs(aDocument, aWindow)
Definition: pageInfo.js:510
var gDocument
Definition: pageInfo.js:150
const COL_IMAGE_ALT
Definition: pageInfo.js:157
function doc() browser.contentDocument
const CERTIFICATEDIALOGS_CONTRACTID
Definition: pageInfo.js:205
function clear(dbq)
const COL_IMAGE_ADDRESS
Definition: pageInfo.js:154
const COPYCOL_META_CONTENT
Definition: pageInfo.js:164
var gImageHash
Definition: pageInfo.js:180
function openHelpLink(aHelpTopic, aCalledFromModal)
return elem[name]
var event
function makeURLAbsolute(aBase, aUrl)
function grabAll(elem)
Definition: pageInfo.js:600
const COPYCOL_NONE
Definition: pageInfo.js:163
function addImage(url, type, alt, elem, isBg)
Definition: pageInfo.js:551
function onBlockImage()
Definition: pageInfo.js:769
ATOM dd M d M dd M d M d M d M d M formatDate
var httpCacheSession
Definition: pageInfo.js:196
var onLoadRegistry
Definition: pageInfo.js:240
var ftpCacheSession
Definition: pageInfo.js:198
const XHTMLNS
Definition: pageInfo.js:222
var onProcessFrame
Definition: pageInfo.js:250
const XHTMLre
Definition: pageInfo.js:227
function getSelectedImage(tree)
Definition: pageInfo.js:677
const nsICertificateDialogs
Definition: pageInfo.js:204
function width(ele) rect(ele).width
let window
function addRow(name, type, url)
Definition: feeds.js:84
gImageView getCellProperties
Definition: pageInfo.js:171
function makeGeneralTab()
Definition: pageInfo.js:431
var t
var count
Definition: test_bug7406.js:32
_collectFormDataForFrame aDocument
function loadPageInfo()
Definition: pageInfo.js:302
function makeURI(aURLSpec, aCharset)
Definition: FeedWriter.js:71
setItemValue("imagedimensiontext", imageSize)
function showTab(id)
Definition: pageInfo.js:379
Element Properties href
function num(elem, prop)
const COL_IMAGE_NODE
Definition: pageInfo.js:159
const nsIFilePicker
function onUnloadPageInfo()
Definition: pageInfo.js:351
var gStrings
Definition: pageInfo.js:184
aWindow setTimeout(function(){_this.restoreHistory(aWindow, aTabs, aTabData, aIdMap);}, 0)
function onBeginLinkDrag(event, urlField, descField)
Definition: pageInfo.js:651
function makePreview(row)
Definition: pageInfo.js:810
function goThroughFrames(aDocument, aWindow)
Definition: pageInfo.js:516
function initFeedTab()
Definition: feeds.js:39
var onProcessElement
Definition: pageInfo.js:254
function loadTab(args)
Definition: pageInfo.js:386
function doHelpButton()
Definition: pageInfo.js:364
return null
Definition: FeedWriter.js:1143
_updateDatepicker height
return ret
let node
function onClickMore()
Definition: pageInfo.js:406
const nsIPermissionManager
Definition: pageInfo.js:202
function onLoadPermission()
Definition: permissions.js:87
const cacheService
Definition: pageInfo.js:195
var onUnloadRegistry
Definition: pageInfo.js:261
var uri
Definition: FeedWriter.js:1135
function url(spec)
var gWindow
Definition: pageInfo.js:149
var prefs
Definition: FeedWriter.js:1169
countRef value
Definition: FeedWriter.js:1423
const XLinkNS
Definition: pageInfo.js:219
const COL_IMAGE_COUNT
Definition: pageInfo.js:158
function onLoadPageInfo()
Definition: pageInfo.js:270
const XMLNS
Definition: pageInfo.js:221
var imageSize
Definition: pageInfo.js:959
var gMetaView
Definition: pageInfo.js:168
function selectSaveFolder()
Definition: pageInfo.js:688
const PERMISSION_CONTRACTID
Definition: pageInfo.js:187
const XHTML2NSre
Definition: pageInfo.js:226
var onFinished
Definition: pageInfo.js:258
function resetPageInfo(args)
Definition: pageInfo.js:323
var gImageView
Definition: pageInfo.js:169
#define min(a, b)
var gImageElement
Definition: pageInfo.js:151
var gBundle
Definition: pageInfo.js:185
observe data
Definition: FeedWriter.js:1329
const ACCESS_READ
Definition: pageInfo.js:194
const COPYCOL_IMAGE
Definition: pageInfo.js:165
var gFrameList
Definition: pageInfo.js:508
const ATOM_CONTRACTID
Definition: pageInfo.js:189
var onResetRegistry
Definition: pageInfo.js:246
_getSelectedPageStyle s i
const nsICookiePermission
Definition: pageInfo.js:201
const nsICacheService
Definition: pageInfo.js:193
const COL_IMAGE_TYPE
Definition: pageInfo.js:155
_updateTextAndScrollDataForFrame aData
var file
sbDeviceFirmwareAutoCheckForUpdate prototype observe
makeBlockImage(url)
Definition: pageInfo.js:982
function openURL(target)
Definition: pageInfo.js:645
function processFrames()
Definition: pageInfo.js:526