browser/base/content/pageinfo/pageInfo.js

Wed, 31 Dec 2014 06:55:46 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:46 +0100
changeset 1
ca08bd8f51b2
permissions
-rw-r--r--

Added tag TORBROWSER_REPLICA for changeset 6474c204b198

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

mercurial