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 +}