browser/base/content/pageinfo/pageInfo.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/browser/base/content/pageinfo/pageInfo.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1278 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +const Cu = Components.utils;
     1.9 +Cu.import("resource://gre/modules/LoadContextInfo.jsm");
    1.10 +Cu.import("resource://gre/modules/Services.jsm");
    1.11 +
    1.12 +//******** define a js object to implement nsITreeView
    1.13 +function pageInfoTreeView(treeid, copycol)
    1.14 +{
    1.15 +  // copycol is the index number for the column that we want to add to
    1.16 +  // the copy-n-paste buffer when the user hits accel-c
    1.17 +  this.treeid = treeid;
    1.18 +  this.copycol = copycol;
    1.19 +  this.rows = 0;
    1.20 +  this.tree = null;
    1.21 +  this.data = [ ];
    1.22 +  this.selection = null;
    1.23 +  this.sortcol = -1;
    1.24 +  this.sortdir = false;
    1.25 +}
    1.26 +
    1.27 +pageInfoTreeView.prototype = {
    1.28 +  set rowCount(c) { throw "rowCount is a readonly property"; },
    1.29 +  get rowCount() { return this.rows; },
    1.30 +
    1.31 +  setTree: function(tree)
    1.32 +  {
    1.33 +    this.tree = tree;
    1.34 +  },
    1.35 +
    1.36 +  getCellText: function(row, column)
    1.37 +  {
    1.38 +    // row can be null, but js arrays are 0-indexed.
    1.39 +    // colidx cannot be null, but can be larger than the number
    1.40 +    // of columns in the array. In this case it's the fault of
    1.41 +    // whoever typoed while calling this function.
    1.42 +    return this.data[row][column.index] || "";
    1.43 +  },
    1.44 +
    1.45 +  setCellValue: function(row, column, value)
    1.46 +  {
    1.47 +  },
    1.48 +
    1.49 +  setCellText: function(row, column, value)
    1.50 +  {
    1.51 +    this.data[row][column.index] = value;
    1.52 +  },
    1.53 +
    1.54 +  addRow: function(row)
    1.55 +  {
    1.56 +    this.rows = this.data.push(row);
    1.57 +    this.rowCountChanged(this.rows - 1, 1);
    1.58 +    if (this.selection.count == 0 && this.rowCount && !gImageElement)
    1.59 +      this.selection.select(0);
    1.60 +  },
    1.61 +
    1.62 +  rowCountChanged: function(index, count)
    1.63 +  {
    1.64 +    this.tree.rowCountChanged(index, count);
    1.65 +  },
    1.66 +
    1.67 +  invalidate: function()
    1.68 +  {
    1.69 +    this.tree.invalidate();
    1.70 +  },
    1.71 +
    1.72 +  clear: function()
    1.73 +  {
    1.74 +    if (this.tree)
    1.75 +      this.tree.rowCountChanged(0, -this.rows);
    1.76 +    this.rows = 0;
    1.77 +    this.data = [ ];
    1.78 +  },
    1.79 +
    1.80 +  handleCopy: function(row)
    1.81 +  {
    1.82 +    return (row < 0 || this.copycol < 0) ? "" : (this.data[row][this.copycol] || "");
    1.83 +  },
    1.84 +
    1.85 +  performActionOnRow: function(action, row)
    1.86 +  {
    1.87 +    if (action == "copy") {
    1.88 +      var data = this.handleCopy(row)
    1.89 +      this.tree.treeBody.parentNode.setAttribute("copybuffer", data);
    1.90 +    }
    1.91 +  },
    1.92 +
    1.93 +  onPageMediaSort : function(columnname)
    1.94 +  {
    1.95 +    var tree = document.getElementById(this.treeid);
    1.96 +    var treecol = tree.columns.getNamedColumn(columnname);
    1.97 +
    1.98 +    this.sortdir =
    1.99 +      gTreeUtils.sort(
   1.100 +        tree,
   1.101 +        this,
   1.102 +        this.data,
   1.103 +        treecol.index,
   1.104 +        function textComparator(a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); },
   1.105 +        this.sortcol,
   1.106 +        this.sortdir
   1.107 +      );
   1.108 +
   1.109 +    this.sortcol = treecol.index;
   1.110 +  },
   1.111 +
   1.112 +  getRowProperties: function(row) { return ""; },
   1.113 +  getCellProperties: function(row, column) { return ""; },
   1.114 +  getColumnProperties: function(column) { return ""; },
   1.115 +  isContainer: function(index) { return false; },
   1.116 +  isContainerOpen: function(index) { return false; },
   1.117 +  isSeparator: function(index) { return false; },
   1.118 +  isSorted: function() { },
   1.119 +  canDrop: function(index, orientation) { return false; },
   1.120 +  drop: function(row, orientation) { return false; },
   1.121 +  getParentIndex: function(index) { return 0; },
   1.122 +  hasNextSibling: function(index, after) { return false; },
   1.123 +  getLevel: function(index) { return 0; },
   1.124 +  getImageSrc: function(row, column) { },
   1.125 +  getProgressMode: function(row, column) { },
   1.126 +  getCellValue: function(row, column) { },
   1.127 +  toggleOpenState: function(index) { },
   1.128 +  cycleHeader: function(col) { },
   1.129 +  selectionChanged: function() { },
   1.130 +  cycleCell: function(row, column) { },
   1.131 +  isEditable: function(row, column) { return false; },
   1.132 +  isSelectable: function(row, column) { return false; },
   1.133 +  performAction: function(action) { },
   1.134 +  performActionOnCell: function(action, row, column) { }
   1.135 +};
   1.136 +
   1.137 +// mmm, yummy. global variables.
   1.138 +var gWindow = null;
   1.139 +var gDocument = null;
   1.140 +var gImageElement = null;
   1.141 +
   1.142 +// column number to help using the data array
   1.143 +const COL_IMAGE_ADDRESS = 0;
   1.144 +const COL_IMAGE_TYPE    = 1;
   1.145 +const COL_IMAGE_SIZE    = 2;
   1.146 +const COL_IMAGE_ALT     = 3;
   1.147 +const COL_IMAGE_COUNT   = 4;
   1.148 +const COL_IMAGE_NODE    = 5;
   1.149 +const COL_IMAGE_BG      = 6;
   1.150 +
   1.151 +// column number to copy from, second argument to pageInfoTreeView's constructor
   1.152 +const COPYCOL_NONE = -1;
   1.153 +const COPYCOL_META_CONTENT = 1;
   1.154 +const COPYCOL_IMAGE = COL_IMAGE_ADDRESS;
   1.155 +
   1.156 +// one nsITreeView for each tree in the window
   1.157 +var gMetaView = new pageInfoTreeView('metatree', COPYCOL_META_CONTENT);
   1.158 +var gImageView = new pageInfoTreeView('imagetree', COPYCOL_IMAGE);
   1.159 +
   1.160 +gImageView.getCellProperties = function(row, col) {
   1.161 +  var data = gImageView.data[row];
   1.162 +  var item = gImageView.data[row][COL_IMAGE_NODE];
   1.163 +  var props = "";
   1.164 +  if (!checkProtocol(data) ||
   1.165 +      item instanceof HTMLEmbedElement ||
   1.166 +      (item instanceof HTMLObjectElement && !item.type.startsWith("image/")))
   1.167 +    props += "broken";
   1.168 +
   1.169 +  if (col.element.id == "image-address")
   1.170 +    props += " ltr";
   1.171 +
   1.172 +  return props;
   1.173 +};
   1.174 +
   1.175 +gImageView.getCellText = function(row, column) {
   1.176 +  var value = this.data[row][column.index];
   1.177 +  if (column.index == COL_IMAGE_SIZE) {
   1.178 +    if (value == -1) {
   1.179 +      return gStrings.unknown;
   1.180 +    } else {
   1.181 +      var kbSize = Number(Math.round(value / 1024 * 100) / 100);
   1.182 +      return gBundle.getFormattedString("mediaFileSize", [kbSize]);
   1.183 +    }
   1.184 +  }
   1.185 +  return value || "";
   1.186 +};
   1.187 +
   1.188 +gImageView.onPageMediaSort = function(columnname) {
   1.189 +  var tree = document.getElementById(this.treeid);
   1.190 +  var treecol = tree.columns.getNamedColumn(columnname);
   1.191 +
   1.192 +  var comparator;
   1.193 +  if (treecol.index == COL_IMAGE_SIZE || treecol.index == COL_IMAGE_COUNT) {
   1.194 +    comparator = function numComparator(a, b) { return a - b; };
   1.195 +  } else {
   1.196 +    comparator = function textComparator(a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); };
   1.197 +  }
   1.198 +
   1.199 +  this.sortdir =
   1.200 +    gTreeUtils.sort(
   1.201 +      tree,
   1.202 +      this,
   1.203 +      this.data,
   1.204 +      treecol.index,
   1.205 +      comparator,
   1.206 +      this.sortcol,
   1.207 +      this.sortdir
   1.208 +    );
   1.209 +
   1.210 +  this.sortcol = treecol.index;
   1.211 +};
   1.212 +
   1.213 +var gImageHash = { };
   1.214 +
   1.215 +// localized strings (will be filled in when the document is loaded)
   1.216 +// this isn't all of them, these are just the ones that would otherwise have been loaded inside a loop
   1.217 +var gStrings = { };
   1.218 +var gBundle;
   1.219 +
   1.220 +const PERMISSION_CONTRACTID     = "@mozilla.org/permissionmanager;1";
   1.221 +const PREFERENCES_CONTRACTID    = "@mozilla.org/preferences-service;1";
   1.222 +const ATOM_CONTRACTID           = "@mozilla.org/atom-service;1";
   1.223 +
   1.224 +// a number of services I'll need later
   1.225 +// the cache services
   1.226 +const nsICacheStorageService = Components.interfaces.nsICacheStorageService;
   1.227 +const nsICacheStorage = Components.interfaces.nsICacheStorage;
   1.228 +const cacheService = Components.classes["@mozilla.org/netwerk/cache-storage-service;1"].getService(nsICacheStorageService);
   1.229 +
   1.230 +var loadContextInfo = LoadContextInfo.fromLoadContext(
   1.231 +  window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
   1.232 +        .getInterface(Components.interfaces.nsIWebNavigation)
   1.233 +        .QueryInterface(Components.interfaces.nsILoadContext), false);
   1.234 +var diskStorage = cacheService.diskCacheStorage(loadContextInfo, false);
   1.235 +
   1.236 +const nsICookiePermission  = Components.interfaces.nsICookiePermission;
   1.237 +const nsIPermissionManager = Components.interfaces.nsIPermissionManager;
   1.238 +
   1.239 +const nsICertificateDialogs = Components.interfaces.nsICertificateDialogs;
   1.240 +const CERTIFICATEDIALOGS_CONTRACTID = "@mozilla.org/nsCertificateDialogs;1"
   1.241 +
   1.242 +// clipboard helper
   1.243 +try {
   1.244 +  const gClipboardHelper = Components.classes["@mozilla.org/widget/clipboardhelper;1"].getService(Components.interfaces.nsIClipboardHelper);
   1.245 +}
   1.246 +catch(e) {
   1.247 +  // do nothing, later code will handle the error
   1.248 +}
   1.249 +
   1.250 +// Interface for image loading content
   1.251 +const nsIImageLoadingContent = Components.interfaces.nsIImageLoadingContent;
   1.252 +
   1.253 +// namespaces, don't need all of these yet...
   1.254 +const XLinkNS  = "http://www.w3.org/1999/xlink";
   1.255 +const XULNS    = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
   1.256 +const XMLNS    = "http://www.w3.org/XML/1998/namespace";
   1.257 +const XHTMLNS  = "http://www.w3.org/1999/xhtml";
   1.258 +const XHTML2NS = "http://www.w3.org/2002/06/xhtml2"
   1.259 +
   1.260 +const XHTMLNSre  = "^http\:\/\/www\.w3\.org\/1999\/xhtml$";
   1.261 +const XHTML2NSre = "^http\:\/\/www\.w3\.org\/2002\/06\/xhtml2$";
   1.262 +const XHTMLre = RegExp(XHTMLNSre + "|" + XHTML2NSre, "");
   1.263 +
   1.264 +/* Overlays register functions here.
   1.265 + * These arrays are used to hold callbacks that Page Info will call at
   1.266 + * various stages. Use them by simply appending a function to them.
   1.267 + * For example, add a function to onLoadRegistry by invoking
   1.268 + *   "onLoadRegistry.push(XXXLoadFunc);"
   1.269 + * The XXXLoadFunc should be unique to the overlay module, and will be
   1.270 + * invoked as "XXXLoadFunc();"
   1.271 + */
   1.272 +
   1.273 +// These functions are called to build the data displayed in the Page
   1.274 +// Info window. The global variables gDocument and gWindow are set.
   1.275 +var onLoadRegistry = [ ];
   1.276 +
   1.277 +// These functions are called to remove old data still displayed in
   1.278 +// the window when the document whose information is displayed
   1.279 +// changes. For example, at this time, the list of images of the Media
   1.280 +// tab is cleared.
   1.281 +var onResetRegistry = [ ];
   1.282 +
   1.283 +// These are called once for each subframe of the target document and
   1.284 +// the target document itself. The frame is passed as an argument.
   1.285 +var onProcessFrame = [ ];
   1.286 +
   1.287 +// These functions are called once for each element (in all subframes, if any)
   1.288 +// in the target document. The element is passed as an argument.
   1.289 +var onProcessElement = [ ];
   1.290 +
   1.291 +// These functions are called once when all the elements in all of the target
   1.292 +// document (and all of its subframes, if any) have been processed
   1.293 +var onFinished = [ ];
   1.294 +
   1.295 +// These functions are called once when the Page Info window is closed.
   1.296 +var onUnloadRegistry = [ ];
   1.297 +
   1.298 +// These functions are called once when an image preview is shown.
   1.299 +var onImagePreviewShown = [ ];
   1.300 +
   1.301 +/* Called when PageInfo window is loaded.  Arguments are:
   1.302 + *  window.arguments[0] - (optional) an object consisting of
   1.303 + *                         - doc: (optional) document to use for source. if not provided,
   1.304 + *                                the calling window's document will be used
   1.305 + *                         - initialTab: (optional) id of the inital tab to display
   1.306 + */
   1.307 +function onLoadPageInfo()
   1.308 +{
   1.309 +  gBundle = document.getElementById("pageinfobundle");
   1.310 +  gStrings.unknown = gBundle.getString("unknown");
   1.311 +  gStrings.notSet = gBundle.getString("notset");
   1.312 +  gStrings.mediaImg = gBundle.getString("mediaImg");
   1.313 +  gStrings.mediaBGImg = gBundle.getString("mediaBGImg");
   1.314 +  gStrings.mediaBorderImg = gBundle.getString("mediaBorderImg");
   1.315 +  gStrings.mediaListImg = gBundle.getString("mediaListImg");
   1.316 +  gStrings.mediaCursor = gBundle.getString("mediaCursor");
   1.317 +  gStrings.mediaObject = gBundle.getString("mediaObject");
   1.318 +  gStrings.mediaEmbed = gBundle.getString("mediaEmbed");
   1.319 +  gStrings.mediaLink = gBundle.getString("mediaLink");
   1.320 +  gStrings.mediaInput = gBundle.getString("mediaInput");
   1.321 +  gStrings.mediaVideo = gBundle.getString("mediaVideo");
   1.322 +  gStrings.mediaAudio = gBundle.getString("mediaAudio");
   1.323 +
   1.324 +  var args = "arguments" in window &&
   1.325 +             window.arguments.length >= 1 &&
   1.326 +             window.arguments[0];
   1.327 +
   1.328 +  if (!args || !args.doc) {
   1.329 +    gWindow = window.opener.content;
   1.330 +    gDocument = gWindow.document;
   1.331 +  }
   1.332 +
   1.333 +  // init media view
   1.334 +  var imageTree = document.getElementById("imagetree");
   1.335 +  imageTree.view = gImageView;
   1.336 +
   1.337 +  /* Select the requested tab, if the name is specified */
   1.338 +  loadTab(args);
   1.339 +  Components.classes["@mozilla.org/observer-service;1"]
   1.340 +            .getService(Components.interfaces.nsIObserverService)
   1.341 +            .notifyObservers(window, "page-info-dialog-loaded", null);
   1.342 +}
   1.343 +
   1.344 +function loadPageInfo()
   1.345 +{
   1.346 +  var titleFormat = gWindow != gWindow.top ? "pageInfo.frame.title"
   1.347 +                                           : "pageInfo.page.title";
   1.348 +  document.title = gBundle.getFormattedString(titleFormat, [gDocument.location]);
   1.349 +
   1.350 +  document.getElementById("main-window").setAttribute("relatedUrl", gDocument.location);
   1.351 +
   1.352 +  // do the easy stuff first
   1.353 +  makeGeneralTab();
   1.354 +
   1.355 +  // and then the hard stuff
   1.356 +  makeTabs(gDocument, gWindow);
   1.357 +
   1.358 +  initFeedTab();
   1.359 +  onLoadPermission();
   1.360 +
   1.361 +  /* Call registered overlay init functions */
   1.362 +  onLoadRegistry.forEach(function(func) { func(); });
   1.363 +}
   1.364 +
   1.365 +function resetPageInfo(args)
   1.366 +{
   1.367 +  /* Reset Meta tags part */
   1.368 +  gMetaView.clear();
   1.369 +
   1.370 +  /* Reset Media tab */
   1.371 +  var mediaTab = document.getElementById("mediaTab");
   1.372 +  if (!mediaTab.hidden) {
   1.373 +    Components.classes["@mozilla.org/observer-service;1"]
   1.374 +              .getService(Components.interfaces.nsIObserverService)
   1.375 +              .removeObserver(imagePermissionObserver, "perm-changed");
   1.376 +    mediaTab.hidden = true;
   1.377 +  }
   1.378 +  gImageView.clear();
   1.379 +  gImageHash = {};
   1.380 +
   1.381 +  /* Reset Feeds Tab */
   1.382 +  var feedListbox = document.getElementById("feedListbox");
   1.383 +  while (feedListbox.firstChild)
   1.384 +    feedListbox.removeChild(feedListbox.firstChild);
   1.385 +
   1.386 +  /* Call registered overlay reset functions */
   1.387 +  onResetRegistry.forEach(function(func) { func(); });
   1.388 +
   1.389 +  /* Rebuild the data */
   1.390 +  loadTab(args);
   1.391 +}
   1.392 +
   1.393 +function onUnloadPageInfo()
   1.394 +{
   1.395 +  // Remove the observer, only if there is at least 1 image.
   1.396 +  if (!document.getElementById("mediaTab").hidden) {
   1.397 +    Components.classes["@mozilla.org/observer-service;1"]
   1.398 +              .getService(Components.interfaces.nsIObserverService)
   1.399 +              .removeObserver(imagePermissionObserver, "perm-changed");
   1.400 +  }
   1.401 +
   1.402 +  /* Call registered overlay unload functions */
   1.403 +  onUnloadRegistry.forEach(function(func) { func(); });
   1.404 +}
   1.405 +
   1.406 +function doHelpButton()
   1.407 +{
   1.408 +  const helpTopics = {
   1.409 +    "generalPanel":  "pageinfo_general",
   1.410 +    "mediaPanel":    "pageinfo_media",
   1.411 +    "feedPanel":     "pageinfo_feed",
   1.412 +    "permPanel":     "pageinfo_permissions",
   1.413 +    "securityPanel": "pageinfo_security"
   1.414 +  };
   1.415 +
   1.416 +  var deck  = document.getElementById("mainDeck");
   1.417 +  var helpdoc = helpTopics[deck.selectedPanel.id] || "pageinfo_general";
   1.418 +  openHelpLink(helpdoc);
   1.419 +}
   1.420 +
   1.421 +function showTab(id)
   1.422 +{
   1.423 +  var deck  = document.getElementById("mainDeck");
   1.424 +  var pagel = document.getElementById(id + "Panel");
   1.425 +  deck.selectedPanel = pagel;
   1.426 +}
   1.427 +
   1.428 +function loadTab(args)
   1.429 +{
   1.430 +  if (args && args.doc) {
   1.431 +    gDocument = args.doc;
   1.432 +    gWindow = gDocument.defaultView;
   1.433 +  }
   1.434 +
   1.435 +  gImageElement = args && args.imageElement;
   1.436 +
   1.437 +  /* Load the page info */
   1.438 +  loadPageInfo();
   1.439 +
   1.440 +  var initialTab = (args && args.initialTab) || "generalTab";
   1.441 +  var radioGroup = document.getElementById("viewGroup");
   1.442 +  initialTab = document.getElementById(initialTab) || document.getElementById("generalTab");
   1.443 +  radioGroup.selectedItem = initialTab;
   1.444 +  radioGroup.selectedItem.doCommand();
   1.445 +  radioGroup.focus();
   1.446 +}
   1.447 +
   1.448 +function onClickMore()
   1.449 +{
   1.450 +  var radioGrp = document.getElementById("viewGroup");
   1.451 +  var radioElt = document.getElementById("securityTab");
   1.452 +  radioGrp.selectedItem = radioElt;
   1.453 +  showTab('security');
   1.454 +}
   1.455 +
   1.456 +function toggleGroupbox(id)
   1.457 +{
   1.458 +  var elt = document.getElementById(id);
   1.459 +  if (elt.hasAttribute("closed")) {
   1.460 +    elt.removeAttribute("closed");
   1.461 +    if (elt.flexWhenOpened)
   1.462 +      elt.flex = elt.flexWhenOpened;
   1.463 +  }
   1.464 +  else {
   1.465 +    elt.setAttribute("closed", "true");
   1.466 +    if (elt.flex) {
   1.467 +      elt.flexWhenOpened = elt.flex;
   1.468 +      elt.flex = 0;
   1.469 +    }
   1.470 +  }
   1.471 +}
   1.472 +
   1.473 +function openCacheEntry(key, cb)
   1.474 +{
   1.475 +  var checkCacheListener = {
   1.476 +    onCacheEntryCheck: function(entry, appCache) {
   1.477 +      return Components.interfaces.nsICacheEntryOpenCallback.ENTRY_WANTED;
   1.478 +    },
   1.479 +    onCacheEntryAvailable: function(entry, isNew, appCache, status) {
   1.480 +      cb(entry);
   1.481 +    }
   1.482 +  };
   1.483 +  diskStorage.asyncOpenURI(Services.io.newURI(key, null, null), "", nsICacheStorage.OPEN_READONLY, checkCacheListener);
   1.484 +}
   1.485 +
   1.486 +function makeGeneralTab()
   1.487 +{
   1.488 +  var title = (gDocument.title) ? gBundle.getFormattedString("pageTitle", [gDocument.title]) : gBundle.getString("noPageTitle");
   1.489 +  document.getElementById("titletext").value = title;
   1.490 +
   1.491 +  var url = gDocument.location.toString();
   1.492 +  setItemValue("urltext", url);
   1.493 +
   1.494 +  var referrer = ("referrer" in gDocument && gDocument.referrer);
   1.495 +  setItemValue("refertext", referrer);
   1.496 +
   1.497 +  var mode = ("compatMode" in gDocument && gDocument.compatMode == "BackCompat") ? "generalQuirksMode" : "generalStrictMode";
   1.498 +  document.getElementById("modetext").value = gBundle.getString(mode);
   1.499 +
   1.500 +  // find out the mime type
   1.501 +  var mimeType = gDocument.contentType;
   1.502 +  setItemValue("typetext", mimeType);
   1.503 +
   1.504 +  // get the document characterset
   1.505 +  var encoding = gDocument.characterSet;
   1.506 +  document.getElementById("encodingtext").value = encoding;
   1.507 +
   1.508 +  // get the meta tags
   1.509 +  var metaNodes = gDocument.getElementsByTagName("meta");
   1.510 +  var length = metaNodes.length;
   1.511 +
   1.512 +  var metaGroup = document.getElementById("metaTags");
   1.513 +  if (!length)
   1.514 +    metaGroup.collapsed = true;
   1.515 +  else {
   1.516 +    var metaTagsCaption = document.getElementById("metaTagsCaption");
   1.517 +    if (length == 1)
   1.518 +      metaTagsCaption.label = gBundle.getString("generalMetaTag");
   1.519 +    else
   1.520 +      metaTagsCaption.label = gBundle.getFormattedString("generalMetaTags", [length]);
   1.521 +    var metaTree = document.getElementById("metatree");
   1.522 +    metaTree.treeBoxObject.view = gMetaView;
   1.523 +
   1.524 +    for (var i = 0; i < length; i++)
   1.525 +      gMetaView.addRow([metaNodes[i].name || metaNodes[i].httpEquiv, metaNodes[i].content]);
   1.526 +
   1.527 +    metaGroup.collapsed = false;
   1.528 +  }
   1.529 +
   1.530 +  // get the date of last modification
   1.531 +  var modifiedText = formatDate(gDocument.lastModified, gStrings.notSet);
   1.532 +  document.getElementById("modifiedtext").value = modifiedText;
   1.533 +
   1.534 +  // get cache info
   1.535 +  var cacheKey = url.replace(/#.*$/, "");
   1.536 +  openCacheEntry(cacheKey, function(cacheEntry) {
   1.537 +    var sizeText;
   1.538 +    if (cacheEntry) {
   1.539 +      var pageSize = cacheEntry.dataSize;
   1.540 +      var kbSize = formatNumber(Math.round(pageSize / 1024 * 100) / 100);
   1.541 +      sizeText = gBundle.getFormattedString("generalSize", [kbSize, formatNumber(pageSize)]);
   1.542 +    }
   1.543 +    setItemValue("sizetext", sizeText);
   1.544 +  });
   1.545 +
   1.546 +  securityOnLoad();
   1.547 +}
   1.548 +
   1.549 +//******** Generic Build-a-tab
   1.550 +// Assumes the views are empty. Only called once to build the tabs, and
   1.551 +// does so by farming the task off to another thread via setTimeout().
   1.552 +// The actual work is done with a TreeWalker that calls doGrab() once for
   1.553 +// each element node in the document.
   1.554 +
   1.555 +var gFrameList = [ ];
   1.556 +
   1.557 +function makeTabs(aDocument, aWindow)
   1.558 +{
   1.559 +  goThroughFrames(aDocument, aWindow);
   1.560 +  processFrames();
   1.561 +}
   1.562 +
   1.563 +function goThroughFrames(aDocument, aWindow)
   1.564 +{
   1.565 +  gFrameList.push(aDocument);
   1.566 +  if (aWindow && aWindow.frames.length > 0) {
   1.567 +    var num = aWindow.frames.length;
   1.568 +    for (var i = 0; i < num; i++)
   1.569 +      goThroughFrames(aWindow.frames[i].document, aWindow.frames[i]);  // recurse through the frames
   1.570 +  }
   1.571 +}
   1.572 +
   1.573 +function processFrames()
   1.574 +{
   1.575 +  if (gFrameList.length) {
   1.576 +    var doc = gFrameList[0];
   1.577 +    onProcessFrame.forEach(function(func) { func(doc); });
   1.578 +    var iterator = doc.createTreeWalker(doc, NodeFilter.SHOW_ELEMENT, grabAll);
   1.579 +    gFrameList.shift();
   1.580 +    setTimeout(doGrab, 10, iterator);
   1.581 +    onFinished.push(selectImage);
   1.582 +  }
   1.583 +  else
   1.584 +    onFinished.forEach(function(func) { func(); });
   1.585 +}
   1.586 +
   1.587 +function doGrab(iterator)
   1.588 +{
   1.589 +  for (var i = 0; i < 500; ++i)
   1.590 +    if (!iterator.nextNode()) {
   1.591 +      processFrames();
   1.592 +      return;
   1.593 +    }
   1.594 +
   1.595 +  setTimeout(doGrab, 10, iterator);
   1.596 +}
   1.597 +
   1.598 +function addImage(url, type, alt, elem, isBg)
   1.599 +{
   1.600 +  if (!url)
   1.601 +    return;
   1.602 +
   1.603 +  if (!gImageHash.hasOwnProperty(url))
   1.604 +    gImageHash[url] = { };
   1.605 +  if (!gImageHash[url].hasOwnProperty(type))
   1.606 +    gImageHash[url][type] = { };
   1.607 +  if (!gImageHash[url][type].hasOwnProperty(alt)) {
   1.608 +    gImageHash[url][type][alt] = gImageView.data.length;
   1.609 +    var row = [url, type, -1, alt, 1, elem, isBg];
   1.610 +    gImageView.addRow(row);
   1.611 +
   1.612 +    // Fill in cache data asynchronously
   1.613 +    openCacheEntry(url, function(cacheEntry) {
   1.614 +      // The data at row[2] corresponds to the data size.
   1.615 +      if (cacheEntry) {
   1.616 +        row[2] = cacheEntry.dataSize;
   1.617 +        // Invalidate the row to trigger a repaint.
   1.618 +        gImageView.tree.invalidateRow(gImageView.data.indexOf(row));
   1.619 +      }
   1.620 +    });
   1.621 +
   1.622 +    // Add the observer, only once.
   1.623 +    if (gImageView.data.length == 1) {
   1.624 +      document.getElementById("mediaTab").hidden = false;
   1.625 +      Components.classes["@mozilla.org/observer-service;1"]
   1.626 +                .getService(Components.interfaces.nsIObserverService)
   1.627 +                .addObserver(imagePermissionObserver, "perm-changed", false);
   1.628 +    }
   1.629 +  }
   1.630 +  else {
   1.631 +    var i = gImageHash[url][type][alt];
   1.632 +    gImageView.data[i][COL_IMAGE_COUNT]++;
   1.633 +    if (elem == gImageElement)
   1.634 +      gImageView.data[i][COL_IMAGE_NODE] = elem;
   1.635 +  }
   1.636 +}
   1.637 +
   1.638 +function grabAll(elem)
   1.639 +{
   1.640 +  // check for images defined in CSS (e.g. background, borders), any node may have multiple
   1.641 +  var computedStyle = elem.ownerDocument.defaultView.getComputedStyle(elem, "");
   1.642 +
   1.643 +  if (computedStyle) {
   1.644 +    var addImgFunc = function (label, val) {
   1.645 +      if (val.primitiveType == CSSPrimitiveValue.CSS_URI) {
   1.646 +        addImage(val.getStringValue(), label, gStrings.notSet, elem, true);
   1.647 +      }
   1.648 +      else if (val.primitiveType == CSSPrimitiveValue.CSS_STRING) {
   1.649 +        // This is for -moz-image-rect.
   1.650 +        // TODO: Reimplement once bug 714757 is fixed
   1.651 +        var strVal = val.getStringValue();
   1.652 +        if (strVal.search(/^.*url\(\"?/) > -1) {
   1.653 +          url = strVal.replace(/^.*url\(\"?/,"").replace(/\"?\).*$/,"");
   1.654 +          addImage(url, label, gStrings.notSet, elem, true);
   1.655 +        }
   1.656 +      }
   1.657 +      else if (val.cssValueType == CSSValue.CSS_VALUE_LIST) {
   1.658 +        // recursively resolve multiple nested CSS value lists
   1.659 +        for (var i = 0; i < val.length; i++)
   1.660 +          addImgFunc(label, val.item(i));
   1.661 +      }
   1.662 +    };
   1.663 +
   1.664 +    addImgFunc(gStrings.mediaBGImg, computedStyle.getPropertyCSSValue("background-image"));
   1.665 +    addImgFunc(gStrings.mediaBorderImg, computedStyle.getPropertyCSSValue("border-image-source"));
   1.666 +    addImgFunc(gStrings.mediaListImg, computedStyle.getPropertyCSSValue("list-style-image"));
   1.667 +    addImgFunc(gStrings.mediaCursor, computedStyle.getPropertyCSSValue("cursor"));
   1.668 +  }
   1.669 +
   1.670 +  // one swi^H^H^Hif-else to rule them all
   1.671 +  if (elem instanceof HTMLImageElement)
   1.672 +    addImage(elem.src, gStrings.mediaImg,
   1.673 +             (elem.hasAttribute("alt")) ? elem.alt : gStrings.notSet, elem, false);
   1.674 +  else if (elem instanceof SVGImageElement) {
   1.675 +    try {
   1.676 +      // Note: makeURLAbsolute will throw if either the baseURI is not a valid URI
   1.677 +      //       or the URI formed from the baseURI and the URL is not a valid URI
   1.678 +      var href = makeURLAbsolute(elem.baseURI, elem.href.baseVal);
   1.679 +      addImage(href, gStrings.mediaImg, "", elem, false);
   1.680 +    } catch (e) { }
   1.681 +  }
   1.682 +  else if (elem instanceof HTMLVideoElement) {
   1.683 +    addImage(elem.currentSrc, gStrings.mediaVideo, "", elem, false);
   1.684 +  }
   1.685 +  else if (elem instanceof HTMLAudioElement) {
   1.686 +    addImage(elem.currentSrc, gStrings.mediaAudio, "", elem, false);
   1.687 +  }
   1.688 +  else if (elem instanceof HTMLLinkElement) {
   1.689 +    if (elem.rel && /\bicon\b/i.test(elem.rel))
   1.690 +      addImage(elem.href, gStrings.mediaLink, "", elem, false);
   1.691 +  }
   1.692 +  else if (elem instanceof HTMLInputElement || elem instanceof HTMLButtonElement) {
   1.693 +    if (elem.type.toLowerCase() == "image")
   1.694 +      addImage(elem.src, gStrings.mediaInput,
   1.695 +               (elem.hasAttribute("alt")) ? elem.alt : gStrings.notSet, elem, false);
   1.696 +  }
   1.697 +  else if (elem instanceof HTMLObjectElement)
   1.698 +    addImage(elem.data, gStrings.mediaObject, getValueText(elem), elem, false);
   1.699 +  else if (elem instanceof HTMLEmbedElement)
   1.700 +    addImage(elem.src, gStrings.mediaEmbed, "", elem, false);
   1.701 +
   1.702 +  onProcessElement.forEach(function(func) { func(elem); });
   1.703 +
   1.704 +  return NodeFilter.FILTER_ACCEPT;
   1.705 +}
   1.706 +
   1.707 +//******** Link Stuff
   1.708 +function openURL(target)
   1.709 +{
   1.710 +  var url = target.parentNode.childNodes[2].value;
   1.711 +  window.open(url, "_blank", "chrome");
   1.712 +}
   1.713 +
   1.714 +function onBeginLinkDrag(event,urlField,descField)
   1.715 +{
   1.716 +  if (event.originalTarget.localName != "treechildren")
   1.717 +    return;
   1.718 +
   1.719 +  var tree = event.target;
   1.720 +  if (!("treeBoxObject" in tree))
   1.721 +    tree = tree.parentNode;
   1.722 +
   1.723 +  var row = tree.treeBoxObject.getRowAt(event.clientX, event.clientY);
   1.724 +  if (row == -1)
   1.725 +    return;
   1.726 +
   1.727 +  // Adding URL flavor
   1.728 +  var col = tree.columns[urlField];
   1.729 +  var url = tree.view.getCellText(row, col);
   1.730 +  col = tree.columns[descField];
   1.731 +  var desc = tree.view.getCellText(row, col);
   1.732 +
   1.733 +  var dt = event.dataTransfer;
   1.734 +  dt.setData("text/x-moz-url", url + "\n" + desc);
   1.735 +  dt.setData("text/url-list", url);
   1.736 +  dt.setData("text/plain", url);
   1.737 +}
   1.738 +
   1.739 +//******** Image Stuff
   1.740 +function getSelectedRows(tree)
   1.741 +{
   1.742 +  var start = { };
   1.743 +  var end   = { };
   1.744 +  var numRanges = tree.view.selection.getRangeCount();
   1.745 +
   1.746 +  var rowArray = [ ];
   1.747 +  for (var t = 0; t < numRanges; t++) {
   1.748 +    tree.view.selection.getRangeAt(t, start, end);
   1.749 +    for (var v = start.value; v <= end.value; v++)
   1.750 +      rowArray.push(v);
   1.751 +  }
   1.752 +
   1.753 +  return rowArray;
   1.754 +}
   1.755 +
   1.756 +function getSelectedRow(tree)
   1.757 +{
   1.758 +  var rows = getSelectedRows(tree);
   1.759 +  return (rows.length == 1) ? rows[0] : -1;
   1.760 +}
   1.761 +
   1.762 +function selectSaveFolder(aCallback)
   1.763 +{
   1.764 +  const nsILocalFile = Components.interfaces.nsILocalFile;
   1.765 +  const nsIFilePicker = Components.interfaces.nsIFilePicker;
   1.766 +  let titleText = gBundle.getString("mediaSelectFolder");
   1.767 +  let fp = Components.classes["@mozilla.org/filepicker;1"].
   1.768 +           createInstance(nsIFilePicker);
   1.769 +  let fpCallback = function fpCallback_done(aResult) {
   1.770 +    if (aResult == nsIFilePicker.returnOK) {
   1.771 +      aCallback(fp.file.QueryInterface(nsILocalFile));
   1.772 +    } else {
   1.773 +      aCallback(null);
   1.774 +    }
   1.775 +  };
   1.776 +
   1.777 +  fp.init(window, titleText, nsIFilePicker.modeGetFolder);
   1.778 +  fp.appendFilters(nsIFilePicker.filterAll);
   1.779 +  try {
   1.780 +    let prefs = Components.classes[PREFERENCES_CONTRACTID].
   1.781 +                getService(Components.interfaces.nsIPrefBranch);
   1.782 +    let initialDir = prefs.getComplexValue("browser.download.dir", nsILocalFile);
   1.783 +    if (initialDir) {
   1.784 +      fp.displayDirectory = initialDir;
   1.785 +    }
   1.786 +  } catch (ex) {
   1.787 +  }
   1.788 +  fp.open(fpCallback);
   1.789 +}
   1.790 +
   1.791 +function saveMedia()
   1.792 +{
   1.793 +  var tree = document.getElementById("imagetree");
   1.794 +  var rowArray = getSelectedRows(tree);
   1.795 +  if (rowArray.length == 1) {
   1.796 +    var row = rowArray[0];
   1.797 +    var item = gImageView.data[row][COL_IMAGE_NODE];
   1.798 +    var url = gImageView.data[row][COL_IMAGE_ADDRESS];
   1.799 +
   1.800 +    if (url) {
   1.801 +      var titleKey = "SaveImageTitle";
   1.802 +
   1.803 +      if (item instanceof HTMLVideoElement)
   1.804 +        titleKey = "SaveVideoTitle";
   1.805 +      else if (item instanceof HTMLAudioElement)
   1.806 +        titleKey = "SaveAudioTitle";
   1.807 +
   1.808 +      saveURL(url, null, titleKey, false, false, makeURI(item.baseURI), gDocument);
   1.809 +    }
   1.810 +  } else {
   1.811 +    selectSaveFolder(function(aDirectory) {
   1.812 +      if (aDirectory) {
   1.813 +        var saveAnImage = function(aURIString, aChosenData, aBaseURI) {
   1.814 +          internalSave(aURIString, null, null, null, null, false, "SaveImageTitle",
   1.815 +                       aChosenData, aBaseURI, gDocument);
   1.816 +        };
   1.817 +
   1.818 +        for (var i = 0; i < rowArray.length; i++) {
   1.819 +          var v = rowArray[i];
   1.820 +          var dir = aDirectory.clone();
   1.821 +          var item = gImageView.data[v][COL_IMAGE_NODE];
   1.822 +          var uriString = gImageView.data[v][COL_IMAGE_ADDRESS];
   1.823 +          var uri = makeURI(uriString);
   1.824 +
   1.825 +          try {
   1.826 +            uri.QueryInterface(Components.interfaces.nsIURL);
   1.827 +            dir.append(decodeURIComponent(uri.fileName));
   1.828 +          } catch(ex) {
   1.829 +            /* data: uris */
   1.830 +          }
   1.831 +
   1.832 +          if (i == 0) {
   1.833 +            saveAnImage(uriString, new AutoChosen(dir, uri), makeURI(item.baseURI));
   1.834 +          } else {
   1.835 +            // This delay is a hack which prevents the download manager
   1.836 +            // from opening many times. See bug 377339.
   1.837 +            setTimeout(saveAnImage, 200, uriString, new AutoChosen(dir, uri),
   1.838 +                       makeURI(item.baseURI));
   1.839 +          }
   1.840 +        }
   1.841 +      }
   1.842 +    });
   1.843 +  }
   1.844 +}
   1.845 +
   1.846 +function onBlockImage()
   1.847 +{
   1.848 +  var permissionManager = Components.classes[PERMISSION_CONTRACTID]
   1.849 +                                    .getService(nsIPermissionManager);
   1.850 +
   1.851 +  var checkbox = document.getElementById("blockImage");
   1.852 +  var uri = makeURI(document.getElementById("imageurltext").value);
   1.853 +  if (checkbox.checked)
   1.854 +    permissionManager.add(uri, "image", nsIPermissionManager.DENY_ACTION);
   1.855 +  else
   1.856 +    permissionManager.remove(uri.host, "image");
   1.857 +}
   1.858 +
   1.859 +function onImageSelect()
   1.860 +{
   1.861 +  var previewBox   = document.getElementById("mediaPreviewBox");
   1.862 +  var mediaSaveBox = document.getElementById("mediaSaveBox");
   1.863 +  var splitter     = document.getElementById("mediaSplitter");
   1.864 +  var tree = document.getElementById("imagetree");
   1.865 +  var count = tree.view.selection.count;
   1.866 +  if (count == 0) {
   1.867 +    previewBox.collapsed   = true;
   1.868 +    mediaSaveBox.collapsed = true;
   1.869 +    splitter.collapsed     = true;
   1.870 +    tree.flex = 1;
   1.871 +  }
   1.872 +  else if (count > 1) {
   1.873 +    splitter.collapsed     = true;
   1.874 +    previewBox.collapsed   = true;
   1.875 +    mediaSaveBox.collapsed = false;
   1.876 +    tree.flex = 1;
   1.877 +  }
   1.878 +  else {
   1.879 +    mediaSaveBox.collapsed = true;
   1.880 +    splitter.collapsed     = false;
   1.881 +    previewBox.collapsed   = false;
   1.882 +    tree.flex = 0;
   1.883 +    makePreview(getSelectedRows(tree)[0]);
   1.884 +  }
   1.885 +}
   1.886 +
   1.887 +function makePreview(row)
   1.888 +{
   1.889 +  var imageTree = document.getElementById("imagetree");
   1.890 +  var item = gImageView.data[row][COL_IMAGE_NODE];
   1.891 +  var url = gImageView.data[row][COL_IMAGE_ADDRESS];
   1.892 +  var isBG = gImageView.data[row][COL_IMAGE_BG];
   1.893 +  var isAudio = false;
   1.894 +
   1.895 +  setItemValue("imageurltext", url);
   1.896 +
   1.897 +  var imageText;
   1.898 +  if (!isBG &&
   1.899 +      !(item instanceof SVGImageElement) &&
   1.900 +      !(gDocument instanceof ImageDocument)) {
   1.901 +    imageText = item.title || item.alt;
   1.902 +
   1.903 +    if (!imageText && !(item instanceof HTMLImageElement))
   1.904 +      imageText = getValueText(item);
   1.905 +  }
   1.906 +  setItemValue("imagetext", imageText);
   1.907 +
   1.908 +  setItemValue("imagelongdesctext", item.longDesc);
   1.909 +
   1.910 +  // get cache info
   1.911 +  var cacheKey = url.replace(/#.*$/, "");
   1.912 +  openCacheEntry(cacheKey, function(cacheEntry) {
   1.913 +    // find out the file size
   1.914 +    var sizeText;
   1.915 +    if (cacheEntry) {
   1.916 +      var imageSize = cacheEntry.dataSize;
   1.917 +      var kbSize = Math.round(imageSize / 1024 * 100) / 100;
   1.918 +      sizeText = gBundle.getFormattedString("generalSize",
   1.919 +                                            [formatNumber(kbSize), formatNumber(imageSize)]);
   1.920 +    }
   1.921 +    else
   1.922 +      sizeText = gBundle.getString("mediaUnknownNotCached");
   1.923 +    setItemValue("imagesizetext", sizeText);
   1.924 +
   1.925 +    var mimeType;
   1.926 +    var numFrames = 1;
   1.927 +    if (item instanceof HTMLObjectElement ||
   1.928 +        item instanceof HTMLEmbedElement ||
   1.929 +        item instanceof HTMLLinkElement)
   1.930 +      mimeType = item.type;
   1.931 +
   1.932 +    if (!mimeType && !isBG && item instanceof nsIImageLoadingContent) {
   1.933 +      var imageRequest = item.getRequest(nsIImageLoadingContent.CURRENT_REQUEST);
   1.934 +      if (imageRequest) {
   1.935 +        mimeType = imageRequest.mimeType;
   1.936 +        var image = imageRequest.image;
   1.937 +        if (image)
   1.938 +          numFrames = image.numFrames;
   1.939 +      }
   1.940 +    }
   1.941 +
   1.942 +    if (!mimeType)
   1.943 +      mimeType = getContentTypeFromHeaders(cacheEntry);
   1.944 +
   1.945 +    // if we have a data url, get the MIME type from the url
   1.946 +    if (!mimeType && url.startsWith("data:")) {
   1.947 +      let dataMimeType = /^data:(image\/[^;,]+)/i.exec(url);
   1.948 +      if (dataMimeType)
   1.949 +        mimeType = dataMimeType[1].toLowerCase();
   1.950 +    }
   1.951 +
   1.952 +    var imageType;
   1.953 +    if (mimeType) {
   1.954 +      // We found the type, try to display it nicely
   1.955 +      let imageMimeType = /^image\/(.*)/i.exec(mimeType);
   1.956 +      if (imageMimeType) {
   1.957 +        imageType = imageMimeType[1].toUpperCase();
   1.958 +        if (numFrames > 1)
   1.959 +          imageType = gBundle.getFormattedString("mediaAnimatedImageType",
   1.960 +                                                 [imageType, numFrames]);
   1.961 +        else
   1.962 +          imageType = gBundle.getFormattedString("mediaImageType", [imageType]);
   1.963 +      }
   1.964 +      else {
   1.965 +        // the MIME type doesn't begin with image/, display the raw type
   1.966 +        imageType = mimeType;
   1.967 +      }
   1.968 +    }
   1.969 +    else {
   1.970 +      // We couldn't find the type, fall back to the value in the treeview
   1.971 +      imageType = gImageView.data[row][COL_IMAGE_TYPE];
   1.972 +    }
   1.973 +    setItemValue("imagetypetext", imageType);
   1.974 +
   1.975 +    var imageContainer = document.getElementById("theimagecontainer");
   1.976 +    var oldImage = document.getElementById("thepreviewimage");
   1.977 +
   1.978 +    var isProtocolAllowed = checkProtocol(gImageView.data[row]);
   1.979 +
   1.980 +    var newImage = new Image;
   1.981 +    newImage.id = "thepreviewimage";
   1.982 +    var physWidth = 0, physHeight = 0;
   1.983 +    var width = 0, height = 0;
   1.984 +
   1.985 +    if ((item instanceof HTMLLinkElement || item instanceof HTMLInputElement ||
   1.986 +         item instanceof HTMLImageElement ||
   1.987 +         item instanceof SVGImageElement ||
   1.988 +         (item instanceof HTMLObjectElement && mimeType && mimeType.startsWith("image/")) || isBG) && isProtocolAllowed) {
   1.989 +      newImage.setAttribute("src", url);
   1.990 +      physWidth = newImage.width || 0;
   1.991 +      physHeight = newImage.height || 0;
   1.992 +
   1.993 +      // "width" and "height" attributes must be set to newImage,
   1.994 +      // even if there is no "width" or "height attribute in item;
   1.995 +      // otherwise, the preview image cannot be displayed correctly.
   1.996 +      if (!isBG) {
   1.997 +        newImage.width = ("width" in item && item.width) || newImage.naturalWidth;
   1.998 +        newImage.height = ("height" in item && item.height) || newImage.naturalHeight;
   1.999 +      }
  1.1000 +      else {
  1.1001 +        // the Width and Height of an HTML tag should not be used for its background image
  1.1002 +        // (for example, "table" can have "width" or "height" attributes)
  1.1003 +        newImage.width = newImage.naturalWidth;
  1.1004 +        newImage.height = newImage.naturalHeight;
  1.1005 +      }
  1.1006 +
  1.1007 +      if (item instanceof SVGImageElement) {
  1.1008 +        newImage.width = item.width.baseVal.value;
  1.1009 +        newImage.height = item.height.baseVal.value;
  1.1010 +      }
  1.1011 +
  1.1012 +      width = newImage.width;
  1.1013 +      height = newImage.height;
  1.1014 +
  1.1015 +      document.getElementById("theimagecontainer").collapsed = false
  1.1016 +      document.getElementById("brokenimagecontainer").collapsed = true;
  1.1017 +    }
  1.1018 +    else if (item instanceof HTMLVideoElement && isProtocolAllowed) {
  1.1019 +      newImage = document.createElementNS("http://www.w3.org/1999/xhtml", "video");
  1.1020 +      newImage.id = "thepreviewimage";
  1.1021 +      newImage.src = url;
  1.1022 +      newImage.controls = true;
  1.1023 +      width = physWidth = item.videoWidth;
  1.1024 +      height = physHeight = item.videoHeight;
  1.1025 +
  1.1026 +      document.getElementById("theimagecontainer").collapsed = false;
  1.1027 +      document.getElementById("brokenimagecontainer").collapsed = true;
  1.1028 +    }
  1.1029 +    else if (item instanceof HTMLAudioElement && isProtocolAllowed) {
  1.1030 +      newImage = new Audio;
  1.1031 +      newImage.id = "thepreviewimage";
  1.1032 +      newImage.src = url;
  1.1033 +      newImage.controls = true;
  1.1034 +      isAudio = true;
  1.1035 +
  1.1036 +      document.getElementById("theimagecontainer").collapsed = false;
  1.1037 +      document.getElementById("brokenimagecontainer").collapsed = true;
  1.1038 +    }
  1.1039 +    else {
  1.1040 +      // fallback image for protocols not allowed (e.g., javascript:)
  1.1041 +      // or elements not [yet] handled (e.g., object, embed).
  1.1042 +      document.getElementById("brokenimagecontainer").collapsed = false;
  1.1043 +      document.getElementById("theimagecontainer").collapsed = true;
  1.1044 +    }
  1.1045 +
  1.1046 +    var imageSize = "";
  1.1047 +    if (url && !isAudio) {
  1.1048 +      if (width != physWidth || height != physHeight) {
  1.1049 +        imageSize = gBundle.getFormattedString("mediaDimensionsScaled",
  1.1050 +                                               [formatNumber(physWidth),
  1.1051 +                                                formatNumber(physHeight),
  1.1052 +                                                formatNumber(width),
  1.1053 +                                                formatNumber(height)]);
  1.1054 +      }
  1.1055 +      else {
  1.1056 +        imageSize = gBundle.getFormattedString("mediaDimensions",
  1.1057 +                                               [formatNumber(width),
  1.1058 +                                                formatNumber(height)]);
  1.1059 +      }
  1.1060 +    }
  1.1061 +    setItemValue("imagedimensiontext", imageSize);
  1.1062 +
  1.1063 +    makeBlockImage(url);
  1.1064 +
  1.1065 +    imageContainer.removeChild(oldImage);
  1.1066 +    imageContainer.appendChild(newImage);
  1.1067 +
  1.1068 +    onImagePreviewShown.forEach(function(func) { func(); });
  1.1069 +  });
  1.1070 +}
  1.1071 +
  1.1072 +function makeBlockImage(url)
  1.1073 +{
  1.1074 +  var permissionManager = Components.classes[PERMISSION_CONTRACTID]
  1.1075 +                                    .getService(nsIPermissionManager);
  1.1076 +  var prefs = Components.classes[PREFERENCES_CONTRACTID]
  1.1077 +                        .getService(Components.interfaces.nsIPrefBranch);
  1.1078 +
  1.1079 +  var checkbox = document.getElementById("blockImage");
  1.1080 +  var imagePref = prefs.getIntPref("permissions.default.image");
  1.1081 +  if (!(/^https?:/.test(url)) || imagePref == 2)
  1.1082 +    // We can't block the images from this host because either is is not
  1.1083 +    // for http(s) or we don't load images at all
  1.1084 +    checkbox.hidden = true;
  1.1085 +  else {
  1.1086 +    var uri = makeURI(url);
  1.1087 +    if (uri.host) {
  1.1088 +      checkbox.hidden = false;
  1.1089 +      checkbox.label = gBundle.getFormattedString("mediaBlockImage", [uri.host]);
  1.1090 +      var perm = permissionManager.testPermission(uri, "image");
  1.1091 +      checkbox.checked = perm == nsIPermissionManager.DENY_ACTION;
  1.1092 +    }
  1.1093 +    else
  1.1094 +      checkbox.hidden = true;
  1.1095 +  }
  1.1096 +}
  1.1097 +
  1.1098 +var imagePermissionObserver = {
  1.1099 +  observe: function (aSubject, aTopic, aData)
  1.1100 +  {
  1.1101 +    if (document.getElementById("mediaPreviewBox").collapsed)
  1.1102 +      return;
  1.1103 +
  1.1104 +    if (aTopic == "perm-changed") {
  1.1105 +      var permission = aSubject.QueryInterface(Components.interfaces.nsIPermission);
  1.1106 +      if (permission.type == "image") {
  1.1107 +        var imageTree = document.getElementById("imagetree");
  1.1108 +        var row = getSelectedRow(imageTree);
  1.1109 +        var item = gImageView.data[row][COL_IMAGE_NODE];
  1.1110 +        var url = gImageView.data[row][COL_IMAGE_ADDRESS];
  1.1111 +        if (makeURI(url).host == permission.host)
  1.1112 +          makeBlockImage(url);
  1.1113 +      }
  1.1114 +    }
  1.1115 +  }
  1.1116 +}
  1.1117 +
  1.1118 +function getContentTypeFromHeaders(cacheEntryDescriptor)
  1.1119 +{
  1.1120 +  if (!cacheEntryDescriptor)
  1.1121 +    return null;
  1.1122 +
  1.1123 +  return (/^Content-Type:\s*(.*?)\s*(?:\;|$)/mi
  1.1124 +          .exec(cacheEntryDescriptor.getMetaDataElement("response-head")))[1];
  1.1125 +}
  1.1126 +
  1.1127 +//******** Other Misc Stuff
  1.1128 +// Modified from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
  1.1129 +// parse a node to extract the contents of the node
  1.1130 +function getValueText(node)
  1.1131 +{
  1.1132 +  var valueText = "";
  1.1133 +
  1.1134 +  // form input elements don't generally contain information that is useful to our callers, so return nothing
  1.1135 +  if (node instanceof HTMLInputElement ||
  1.1136 +      node instanceof HTMLSelectElement ||
  1.1137 +      node instanceof HTMLTextAreaElement)
  1.1138 +    return valueText;
  1.1139 +
  1.1140 +  // otherwise recurse for each child
  1.1141 +  var length = node.childNodes.length;
  1.1142 +  for (var i = 0; i < length; i++) {
  1.1143 +    var childNode = node.childNodes[i];
  1.1144 +    var nodeType = childNode.nodeType;
  1.1145 +
  1.1146 +    // text nodes are where the goods are
  1.1147 +    if (nodeType == Node.TEXT_NODE)
  1.1148 +      valueText += " " + childNode.nodeValue;
  1.1149 +    // and elements can have more text inside them
  1.1150 +    else if (nodeType == Node.ELEMENT_NODE) {
  1.1151 +      // images are special, we want to capture the alt text as if the image weren't there
  1.1152 +      if (childNode instanceof HTMLImageElement)
  1.1153 +        valueText += " " + getAltText(childNode);
  1.1154 +      else
  1.1155 +        valueText += " " + getValueText(childNode);
  1.1156 +    }
  1.1157 +  }
  1.1158 +
  1.1159 +  return stripWS(valueText);
  1.1160 +}
  1.1161 +
  1.1162 +// Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
  1.1163 +// traverse the tree in search of an img or area element and grab its alt tag
  1.1164 +function getAltText(node)
  1.1165 +{
  1.1166 +  var altText = "";
  1.1167 +
  1.1168 +  if (node.alt)
  1.1169 +    return node.alt;
  1.1170 +  var length = node.childNodes.length;
  1.1171 +  for (var i = 0; i < length; i++)
  1.1172 +    if ((altText = getAltText(node.childNodes[i]) != undefined))  // stupid js warning...
  1.1173 +      return altText;
  1.1174 +  return "";
  1.1175 +}
  1.1176 +
  1.1177 +// Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
  1.1178 +// strip leading and trailing whitespace, and replace multiple consecutive whitespace characters with a single space
  1.1179 +function stripWS(text)
  1.1180 +{
  1.1181 +  var middleRE = /\s+/g;
  1.1182 +  var endRE = /(^\s+)|(\s+$)/g;
  1.1183 +
  1.1184 +  text = text.replace(middleRE, " ");
  1.1185 +  return text.replace(endRE, "");
  1.1186 +}
  1.1187 +
  1.1188 +function setItemValue(id, value)
  1.1189 +{
  1.1190 +  var item = document.getElementById(id);
  1.1191 +  if (value) {
  1.1192 +    item.parentNode.collapsed = false;
  1.1193 +    item.value = value;
  1.1194 +  }
  1.1195 +  else
  1.1196 +    item.parentNode.collapsed = true;
  1.1197 +}
  1.1198 +
  1.1199 +function formatNumber(number)
  1.1200 +{
  1.1201 +  return (+number).toLocaleString();  // coerce number to a numeric value before calling toLocaleString()
  1.1202 +}
  1.1203 +
  1.1204 +function formatDate(datestr, unknown)
  1.1205 +{
  1.1206 +  // scriptable date formatter, for pretty printing dates
  1.1207 +  var dateService = Components.classes["@mozilla.org/intl/scriptabledateformat;1"]
  1.1208 +                              .getService(Components.interfaces.nsIScriptableDateFormat);
  1.1209 +
  1.1210 +  var date = new Date(datestr);
  1.1211 +  if (!date.valueOf())
  1.1212 +    return unknown;
  1.1213 +
  1.1214 +  return dateService.FormatDateTime("", dateService.dateFormatLong,
  1.1215 +                                    dateService.timeFormatSeconds,
  1.1216 +                                    date.getFullYear(), date.getMonth()+1, date.getDate(),
  1.1217 +                                    date.getHours(), date.getMinutes(), date.getSeconds());
  1.1218 +}
  1.1219 +
  1.1220 +function doCopy()
  1.1221 +{
  1.1222 +  if (!gClipboardHelper)
  1.1223 +    return;
  1.1224 +
  1.1225 +  var elem = document.commandDispatcher.focusedElement;
  1.1226 +
  1.1227 +  if (elem && "treeBoxObject" in elem) {
  1.1228 +    var view = elem.view;
  1.1229 +    var selection = view.selection;
  1.1230 +    var text = [], tmp = '';
  1.1231 +    var min = {}, max = {};
  1.1232 +
  1.1233 +    var count = selection.getRangeCount();
  1.1234 +
  1.1235 +    for (var i = 0; i < count; i++) {
  1.1236 +      selection.getRangeAt(i, min, max);
  1.1237 +
  1.1238 +      for (var row = min.value; row <= max.value; row++) {
  1.1239 +        view.performActionOnRow("copy", row);
  1.1240 +
  1.1241 +        tmp = elem.getAttribute("copybuffer");
  1.1242 +        if (tmp)
  1.1243 +          text.push(tmp);
  1.1244 +        elem.removeAttribute("copybuffer");
  1.1245 +      }
  1.1246 +    }
  1.1247 +    gClipboardHelper.copyString(text.join("\n"), document);
  1.1248 +  }
  1.1249 +}
  1.1250 +
  1.1251 +function doSelectAll()
  1.1252 +{
  1.1253 +  var elem = document.commandDispatcher.focusedElement;
  1.1254 +
  1.1255 +  if (elem && "treeBoxObject" in elem)
  1.1256 +    elem.view.selection.selectAll();
  1.1257 +}
  1.1258 +
  1.1259 +function selectImage()
  1.1260 +{
  1.1261 +  if (!gImageElement)
  1.1262 +    return;
  1.1263 +
  1.1264 +  var tree = document.getElementById("imagetree");
  1.1265 +  for (var i = 0; i < tree.view.rowCount; i++) {
  1.1266 +    if (gImageElement == gImageView.data[i][COL_IMAGE_NODE] &&
  1.1267 +        !gImageView.data[i][COL_IMAGE_BG]) {
  1.1268 +      tree.view.selection.select(i);
  1.1269 +      tree.treeBoxObject.ensureRowIsVisible(i);
  1.1270 +      tree.focus();
  1.1271 +      return;
  1.1272 +    }
  1.1273 +  }
  1.1274 +}
  1.1275 +
  1.1276 +function checkProtocol(img)
  1.1277 +{
  1.1278 +  var url = img[COL_IMAGE_ADDRESS];
  1.1279 +  return /^data:image\//i.test(url) ||
  1.1280 +    /^(https?|ftp|file|about|chrome|resource):/.test(url);
  1.1281 +}

mercurial