tenfourfox/browser/base/content/pageinfo/pageInfo.js
Cameron Kaiser c9b2922b70 hello FPR
2017-04-19 00:56:45 -07:00

1143 lines
37 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
Components.utils.import("resource://gre/modules/LoadContextInfo.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
//******** define a js object to implement nsITreeView
function pageInfoTreeView(treeid, copycol)
{
// copycol is the index number for the column that we want to add to
// the copy-n-paste buffer when the user hits accel-c
this.treeid = treeid;
this.copycol = copycol;
this.rows = 0;
this.tree = null;
this.data = [ ];
this.selection = null;
this.sortcol = -1;
this.sortdir = false;
}
pageInfoTreeView.prototype = {
set rowCount(c) { throw "rowCount is a readonly property"; },
get rowCount() { return this.rows; },
setTree: function(tree)
{
this.tree = tree;
},
getCellText: function(row, column)
{
// row can be null, but js arrays are 0-indexed.
// colidx cannot be null, but can be larger than the number
// of columns in the array. In this case it's the fault of
// whoever typoed while calling this function.
return this.data[row][column.index] || "";
},
setCellValue: function(row, column, value)
{
},
setCellText: function(row, column, value)
{
this.data[row][column.index] = value;
},
addRow: function(row)
{
this.rows = this.data.push(row);
this.rowCountChanged(this.rows - 1, 1);
if (this.selection.count == 0 && this.rowCount && !gImageElement) {
this.selection.select(0);
}
},
addRows: function(rows)
{
this.data = this.data.concat(rows);
this.rowCountChanged(this.rows, rows.length);
this.rows = this.data.length;
if (this.selection.count == 0 && this.rowCount && !gImageElement) {
this.selection.select(0);
}
},
rowCountChanged: function(index, count)
{
this.tree.rowCountChanged(index, count);
},
invalidate: function()
{
this.tree.invalidate();
},
clear: function()
{
if (this.tree)
this.tree.rowCountChanged(0, -this.rows);
this.rows = 0;
this.data = [ ];
},
handleCopy: function(row)
{
return (row < 0 || this.copycol < 0) ? "" : (this.data[row][this.copycol] || "");
},
performActionOnRow: function(action, row)
{
if (action == "copy") {
var data = this.handleCopy(row)
this.tree.treeBody.parentNode.setAttribute("copybuffer", data);
}
},
onPageMediaSort : function(columnname)
{
var tree = document.getElementById(this.treeid);
var treecol = tree.columns.getNamedColumn(columnname);
this.sortdir =
gTreeUtils.sort(
tree,
this,
this.data,
treecol.index,
function textComparator(a, b) { return (a || "").toLowerCase().localeCompare((b || "").toLowerCase()); },
this.sortcol,
this.sortdir
);
Array.forEach(tree.columns, function(col) {
col.element.removeAttribute("sortActive");
col.element.removeAttribute("sortDirection");
});
treecol.element.setAttribute("sortActive", "true");
treecol.element.setAttribute("sortDirection", this.sortdir ?
"ascending" : "descending");
this.sortcol = treecol.index;
},
getRowProperties: function(row) { return ""; },
getCellProperties: function(row, column) { return ""; },
getColumnProperties: function(column) { return ""; },
isContainer: function(index) { return false; },
isContainerOpen: function(index) { return false; },
isSeparator: function(index) { return false; },
isSorted: function() { return this.sortcol > -1 },
canDrop: function(index, orientation) { return false; },
drop: function(row, orientation) { return false; },
getParentIndex: function(index) { return 0; },
hasNextSibling: function(index, after) { return false; },
getLevel: function(index) { return 0; },
getImageSrc: function(row, column) { },
getProgressMode: function(row, column) { },
getCellValue: function(row, column) { },
toggleOpenState: function(index) { },
cycleHeader: function(col) { },
selectionChanged: function() { },
cycleCell: function(row, column) { },
isEditable: function(row, column) { return false; },
isSelectable: function(row, column) { return false; },
performAction: function(action) { },
performActionOnCell: function(action, row, column) { }
};
// mmm, yummy. global variables.
var gDocInfo = null;
var gImageElement = null;
// column number to help using the data array
const COL_IMAGE_ADDRESS = 0;
const COL_IMAGE_TYPE = 1;
const COL_IMAGE_SIZE = 2;
const COL_IMAGE_ALT = 3;
const COL_IMAGE_COUNT = 4;
const COL_IMAGE_NODE = 5;
const COL_IMAGE_BG = 6;
// column number to copy from, second argument to pageInfoTreeView's constructor
const COPYCOL_NONE = -1;
const COPYCOL_META_CONTENT = 1;
const COPYCOL_IMAGE = COL_IMAGE_ADDRESS;
// one nsITreeView for each tree in the window
var gMetaView = new pageInfoTreeView('metatree', COPYCOL_META_CONTENT);
var gImageView = new pageInfoTreeView('imagetree', COPYCOL_IMAGE);
gImageView.getCellProperties = function(row, col) {
var data = gImageView.data[row];
var item = gImageView.data[row][COL_IMAGE_NODE];
var props = "";
if (!checkProtocol(data) ||
item instanceof HTMLEmbedElement ||
(item instanceof HTMLObjectElement && !item.type.startsWith("image/")))
props += "broken";
if (col.element.id == "image-address")
props += " ltr";
return props;
};
gImageView.getCellText = function(row, column) {
var value = this.data[row][column.index];
if (column.index == COL_IMAGE_SIZE) {
if (value == -1) {
return gStrings.unknown;
} else {
var kbSize = Number(Math.round(value / 1024 * 100) / 100);
return gBundle.getFormattedString("mediaFileSize", [kbSize]);
}
}
return value || "";
};
gImageView.onPageMediaSort = function(columnname) {
var tree = document.getElementById(this.treeid);
var treecol = tree.columns.getNamedColumn(columnname);
var comparator;
var index = treecol.index;
if (index == COL_IMAGE_SIZE || index == COL_IMAGE_COUNT) {
comparator = function numComparator(a, b) { return a - b; };
} else {
comparator = function textComparator(a, b) { return (a || "").toLowerCase().localeCompare((b || "").toLowerCase()); };
}
this.sortdir =
gTreeUtils.sort(
tree,
this,
this.data,
index,
comparator,
this.sortcol,
this.sortdir
);
Array.forEach(tree.columns, function(col) {
col.element.removeAttribute("sortActive");
col.element.removeAttribute("sortDirection");
});
treecol.element.setAttribute("sortActive", "true");
treecol.element.setAttribute("sortDirection", this.sortdir ?
"ascending" : "descending");
this.sortcol = index;
};
var gImageHash = { };
// localized strings (will be filled in when the document is loaded)
// this isn't all of them, these are just the ones that would otherwise have been loaded inside a loop
var gStrings = { };
var gBundle;
const PERMISSION_CONTRACTID = "@mozilla.org/permissionmanager;1";
const PREFERENCES_CONTRACTID = "@mozilla.org/preferences-service;1";
const ATOM_CONTRACTID = "@mozilla.org/atom-service;1";
// a number of services I'll need later
// the cache services
const nsICacheStorageService = Components.interfaces.nsICacheStorageService;
const nsICacheStorage = Components.interfaces.nsICacheStorage;
const cacheService = Components.classes["@mozilla.org/netwerk/cache-storage-service;1"].getService(nsICacheStorageService);
var loadContextInfo = LoadContextInfo.fromLoadContext(
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIWebNavigation)
.QueryInterface(Components.interfaces.nsILoadContext), false);
var diskStorage = cacheService.diskCacheStorage(loadContextInfo, false);
const nsICookiePermission = Components.interfaces.nsICookiePermission;
const nsIPermissionManager = Components.interfaces.nsIPermissionManager;
const nsICertificateDialogs = Components.interfaces.nsICertificateDialogs;
const CERTIFICATEDIALOGS_CONTRACTID = "@mozilla.org/nsCertificateDialogs;1"
// clipboard helper
function getClipboardHelper() {
try {
return Components.classes["@mozilla.org/widget/clipboardhelper;1"].getService(Components.interfaces.nsIClipboardHelper);
} catch(e) {
// do nothing, later code will handle the error
}
}
const gClipboardHelper = getClipboardHelper();
// Interface for image loading content
const nsIImageLoadingContent = Components.interfaces.nsIImageLoadingContent;
// namespaces, don't need all of these yet...
const XLinkNS = "http://www.w3.org/1999/xlink";
const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const XMLNS = "http://www.w3.org/XML/1998/namespace";
const XHTMLNS = "http://www.w3.org/1999/xhtml";
const XHTML2NS = "http://www.w3.org/2002/06/xhtml2"
const XHTMLNSre = "^http\:\/\/www\.w3\.org\/1999\/xhtml$";
const XHTML2NSre = "^http\:\/\/www\.w3\.org\/2002\/06\/xhtml2$";
const XHTMLre = RegExp(XHTMLNSre + "|" + XHTML2NSre, "");
/* Overlays register functions here.
* These arrays are used to hold callbacks that Page Info will call at
* various stages. Use them by simply appending a function to them.
* For example, add a function to onLoadRegistry by invoking
* "onLoadRegistry.push(XXXLoadFunc);"
* The XXXLoadFunc should be unique to the overlay module, and will be
* invoked as "XXXLoadFunc();"
*/
// These functions are called to build the data displayed in the Page Info window.
var onLoadRegistry = [ ];
// These functions are called to remove old data still displayed in
// the window when the document whose information is displayed
// changes. For example, at this time, the list of images of the Media
// tab is cleared.
var onResetRegistry = [ ];
// These functions are called once when all the elements in all of the target
// document (and all of its subframes, if any) have been processed
var onFinished = [ ];
// These functions are called once when the Page Info window is closed.
var onUnloadRegistry = [ ];
/* Called when PageInfo window is loaded. Arguments are:
* window.arguments[0] - (optional) an object consisting of
* - doc: (optional) document to use for source. if not provided,
* the calling window's document will be used
* - initialTab: (optional) id of the inital tab to display
*/
function onLoadPageInfo()
{
gBundle = document.getElementById("pageinfobundle");
gStrings.unknown = gBundle.getString("unknown");
gStrings.notSet = gBundle.getString("notset");
gStrings.mediaImg = gBundle.getString("mediaImg");
gStrings.mediaBGImg = gBundle.getString("mediaBGImg");
gStrings.mediaBorderImg = gBundle.getString("mediaBorderImg");
gStrings.mediaListImg = gBundle.getString("mediaListImg");
gStrings.mediaCursor = gBundle.getString("mediaCursor");
gStrings.mediaObject = gBundle.getString("mediaObject");
gStrings.mediaEmbed = gBundle.getString("mediaEmbed");
gStrings.mediaLink = gBundle.getString("mediaLink");
gStrings.mediaInput = gBundle.getString("mediaInput");
gStrings.mediaVideo = gBundle.getString("mediaVideo");
gStrings.mediaAudio = gBundle.getString("mediaAudio");
var args = "arguments" in window &&
window.arguments.length >= 1 &&
window.arguments[0];
// init media view
var imageTree = document.getElementById("imagetree");
imageTree.view = gImageView;
/* Select the requested tab, if the name is specified */
loadTab(args);
Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService)
.notifyObservers(window, "page-info-dialog-loaded", null);
}
function loadPageInfo(frameOuterWindowID, imageElement)
{
let mm = window.opener.gBrowser.selectedBrowser.messageManager;
gStrings["application/rss+xml"] = gBundle.getString("feedRss");
gStrings["application/atom+xml"] = gBundle.getString("feedAtom");
gStrings["text/xml"] = gBundle.getString("feedXML");
gStrings["application/xml"] = gBundle.getString("feedXML");
gStrings["application/rdf+xml"] = gBundle.getString("feedXML");
// Look for pageInfoListener in content.js. Sends message to listener with arguments.
mm.sendAsyncMessage("PageInfo:getData", {strings: gStrings,
frameOuterWindowID: frameOuterWindowID},
{ imageElement });
let pageInfoData;
// Get initial pageInfoData needed to display the general, feeds, permission and security tabs.
mm.addMessageListener("PageInfo:data", function onmessage(message) {
mm.removeMessageListener("PageInfo:data", onmessage);
pageInfoData = message.data;
let docInfo = pageInfoData.docInfo;
let windowInfo = pageInfoData.windowInfo;
let uri = makeURI(docInfo.documentURIObject.spec,
docInfo.documentURIObject.originCharset);
gDocInfo = docInfo;
gImageElement = pageInfoData.imageInfo;
var titleFormat = windowInfo.isTopWindow ? "pageInfo.page.title"
: "pageInfo.frame.title";
document.title = gBundle.getFormattedString(titleFormat, [docInfo.location]);
document.getElementById("main-window").setAttribute("relatedUrl", docInfo.location);
makeGeneralTab(pageInfoData.metaViewRows, docInfo);
initFeedTab(pageInfoData.feeds);
onLoadPermission(uri);
securityOnLoad(uri, windowInfo);
});
// Get the media elements from content script to setup the media tab.
mm.addMessageListener("PageInfo:mediaData", function onmessage(message) {
// Page info window was closed.
if (window.closed) {
mm.removeMessageListener("PageInfo:mediaData", onmessage);
return;
}
// The page info media fetching has been completed.
if (message.data.isComplete) {
mm.removeMessageListener("PageInfo:mediaData", onmessage);
onFinished.forEach(function(func) { func(pageInfoData); });
return;
}
addImage(message.data.imageViewRow);
selectImage();
});
/* Call registered overlay init functions */
onLoadRegistry.forEach(function(func) { func(); });
}
function resetPageInfo(args)
{
/* Reset Meta tags part */
gMetaView.clear();
/* Reset Media tab */
var mediaTab = document.getElementById("mediaTab");
if (!mediaTab.hidden) {
Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService)
.removeObserver(imagePermissionObserver, "perm-changed");
mediaTab.hidden = true;
}
gImageView.clear();
gImageHash = {};
/* Reset Feeds Tab */
var feedListbox = document.getElementById("feedListbox");
while (feedListbox.firstChild)
feedListbox.removeChild(feedListbox.firstChild);
/* Call registered overlay reset functions */
onResetRegistry.forEach(function(func) { func(); });
/* Rebuild the data */
loadTab(args);
}
function onUnloadPageInfo()
{
// Remove the observer, only if there is at least 1 image.
if (!document.getElementById("mediaTab").hidden) {
Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService)
.removeObserver(imagePermissionObserver, "perm-changed");
}
/* Call registered overlay unload functions */
onUnloadRegistry.forEach(function(func) { func(); });
}
function doHelpButton()
{
const helpTopics = {
"generalPanel": "pageinfo_general",
"mediaPanel": "pageinfo_media",
"feedPanel": "pageinfo_feed",
"permPanel": "pageinfo_permissions",
"securityPanel": "pageinfo_security"
};
var deck = document.getElementById("mainDeck");
var helpdoc = helpTopics[deck.selectedPanel.id] || "pageinfo_general";
openHelpLink(helpdoc);
}
function showTab(id)
{
var deck = document.getElementById("mainDeck");
var pagel = document.getElementById(id + "Panel");
deck.selectedPanel = pagel;
}
function loadTab(args)
{
// If the "View Image Info" context menu item was used, the related image
// element is provided as an argument. This can't be a background image.
let imageElement = args && args.imageElement;
let frameOuterWindowID = args && args.frameOuterWindowID;
/* Load the page info */
loadPageInfo(frameOuterWindowID, imageElement);
var initialTab = (args && args.initialTab) || "generalTab";
var radioGroup = document.getElementById("viewGroup");
initialTab = document.getElementById(initialTab) || document.getElementById("generalTab");
radioGroup.selectedItem = initialTab;
radioGroup.selectedItem.doCommand();
radioGroup.focus();
}
function toggleGroupbox(id)
{
var elt = document.getElementById(id);
if (elt.hasAttribute("closed")) {
elt.removeAttribute("closed");
if (elt.flexWhenOpened)
elt.flex = elt.flexWhenOpened;
}
else {
elt.setAttribute("closed", "true");
if (elt.flex) {
elt.flexWhenOpened = elt.flex;
elt.flex = 0;
}
}
}
function openCacheEntry(key, cb)
{
var checkCacheListener = {
onCacheEntryCheck: function(entry, appCache) {
return Components.interfaces.nsICacheEntryOpenCallback.ENTRY_WANTED;
},
onCacheEntryAvailable: function(entry, isNew, appCache, status) {
cb(entry);
}
};
diskStorage.asyncOpenURI(Services.io.newURI(key, null, null), "", nsICacheStorage.OPEN_READONLY, checkCacheListener);
}
function makeGeneralTab(metaViewRows, docInfo)
{
var title = (docInfo.title) ? gBundle.getFormattedString("pageTitle", [docInfo.title]) : gBundle.getString("noPageTitle");
document.getElementById("titletext").value = title;
var url = docInfo.location;
setItemValue("urltext", url);
var referrer = ("referrer" in docInfo && docInfo.referrer);
setItemValue("refertext", referrer);
var mode = ("compatMode" in docInfo && docInfo.compatMode == "BackCompat") ? "generalQuirksMode" : "generalStrictMode";
document.getElementById("modetext").value = gBundle.getString(mode);
// find out the mime type
var mimeType = docInfo.contentType;
setItemValue("typetext", mimeType);
// get the document characterset
var encoding = docInfo.characterSet;
document.getElementById("encodingtext").value = encoding;
let length = metaViewRows.length;
var metaGroup = document.getElementById("metaTags");
if (!length)
metaGroup.collapsed = true;
else {
var metaTagsCaption = document.getElementById("metaTagsCaption");
if (length == 1)
metaTagsCaption.label = gBundle.getString("generalMetaTag");
else
metaTagsCaption.label = gBundle.getFormattedString("generalMetaTags", [length]);
var metaTree = document.getElementById("metatree");
metaTree.view = gMetaView;
// Add the metaViewRows onto the general tab's meta info tree.
gMetaView.addRows(metaViewRows);
metaGroup.collapsed = false;
}
// get the date of last modification
var modifiedText = formatDate(docInfo.lastModified, gStrings.notSet);
document.getElementById("modifiedtext").value = modifiedText;
// get cache info
var cacheKey = url.replace(/#.*$/, "");
openCacheEntry(cacheKey, function(cacheEntry) {
var sizeText;
if (cacheEntry) {
var pageSize = cacheEntry.dataSize;
var kbSize = formatNumber(Math.round(pageSize / 1024 * 100) / 100);
sizeText = gBundle.getFormattedString("generalSize", [kbSize, formatNumber(pageSize)]);
}
setItemValue("sizetext", sizeText);
});
}
function addImage(imageViewRow)
{
let [url, type, alt, elem, isBg] = imageViewRow;
if (!url)
return;
if (!gImageHash.hasOwnProperty(url))
gImageHash[url] = { };
if (!gImageHash[url].hasOwnProperty(type))
gImageHash[url][type] = { };
if (!gImageHash[url][type].hasOwnProperty(alt)) {
gImageHash[url][type][alt] = gImageView.data.length;
var row = [url, type, -1, alt, 1, elem, isBg];
gImageView.addRow(row);
// Fill in cache data asynchronously
openCacheEntry(url, function(cacheEntry) {
// The data at row[2] corresponds to the data size.
if (cacheEntry) {
row[2] = cacheEntry.dataSize;
// Invalidate the row to trigger a repaint.
gImageView.tree.invalidateRow(gImageView.data.indexOf(row));
}
});
// Add the observer, only once.
if (gImageView.data.length == 1) {
document.getElementById("mediaTab").hidden = false;
Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService)
.addObserver(imagePermissionObserver, "perm-changed", false);
}
}
else {
var i = gImageHash[url][type][alt];
gImageView.data[i][COL_IMAGE_COUNT]++;
// The same image can occur several times on the page at different sizes.
// If the "View Image Info" context menu item was used, ensure we select
// the correct element.
if (!gImageView.data[i][COL_IMAGE_BG] &&
gImageElement && url == gImageElement.currentSrc &&
gImageElement.width == elem.width &&
gImageElement.height == elem.height &&
gImageElement.imageText == elem.imageText) {
gImageView.data[i][COL_IMAGE_NODE] = elem;
}
}
}
//******** Link Stuff
function openURL(target)
{
var url = target.parentNode.childNodes[2].value;
window.open(url, "_blank", "chrome");
}
function onBeginLinkDrag(event,urlField,descField)
{
if (event.originalTarget.localName != "treechildren")
return;
var tree = event.target;
if (!("treeBoxObject" in tree))
tree = tree.parentNode;
var row = tree.treeBoxObject.getRowAt(event.clientX, event.clientY);
if (row == -1)
return;
// Adding URL flavor
var col = tree.columns[urlField];
var url = tree.view.getCellText(row, col);
col = tree.columns[descField];
var desc = tree.view.getCellText(row, col);
var dt = event.dataTransfer;
dt.setData("text/x-moz-url", url + "\n" + desc);
dt.setData("text/url-list", url);
dt.setData("text/plain", url);
}
//******** Image Stuff
function getSelectedRows(tree)
{
var start = { };
var end = { };
var numRanges = tree.view.selection.getRangeCount();
var rowArray = [ ];
for (var t = 0; t < numRanges; t++) {
tree.view.selection.getRangeAt(t, start, end);
for (var v = start.value; v <= end.value; v++)
rowArray.push(v);
}
return rowArray;
}
function getSelectedRow(tree)
{
var rows = getSelectedRows(tree);
return (rows.length == 1) ? rows[0] : -1;
}
function selectSaveFolder(aCallback)
{
const nsILocalFile = Components.interfaces.nsILocalFile;
const nsIFilePicker = Components.interfaces.nsIFilePicker;
let titleText = gBundle.getString("mediaSelectFolder");
let fp = Components.classes["@mozilla.org/filepicker;1"].
createInstance(nsIFilePicker);
let fpCallback = function fpCallback_done(aResult) {
if (aResult == nsIFilePicker.returnOK) {
aCallback(fp.file.QueryInterface(nsILocalFile));
} else {
aCallback(null);
}
};
fp.init(window, titleText, nsIFilePicker.modeGetFolder);
fp.appendFilters(nsIFilePicker.filterAll);
try {
let prefs = Components.classes[PREFERENCES_CONTRACTID].
getService(Components.interfaces.nsIPrefBranch);
let initialDir = prefs.getComplexValue("browser.download.dir", nsILocalFile);
if (initialDir) {
fp.displayDirectory = initialDir;
}
} catch (ex) {
}
fp.open(fpCallback);
}
function saveMedia()
{
var tree = document.getElementById("imagetree");
var rowArray = getSelectedRows(tree);
if (rowArray.length == 1) {
var row = rowArray[0];
var item = gImageView.data[row][COL_IMAGE_NODE];
var url = gImageView.data[row][COL_IMAGE_ADDRESS];
if (url) {
var titleKey = "SaveImageTitle";
if (item instanceof HTMLVideoElement)
titleKey = "SaveVideoTitle";
else if (item instanceof HTMLAudioElement)
titleKey = "SaveAudioTitle";
saveURL(url, null, titleKey, false, false, makeURI(item.baseURI),
null, gDocInfo.isContentWindowPrivate);
}
} else {
selectSaveFolder(function(aDirectory) {
if (aDirectory) {
var saveAnImage = function(aURIString, aChosenData, aBaseURI) {
uniqueFile(aChosenData.file);
internalSave(aURIString, null, null, null, null, false, "SaveImageTitle",
aChosenData, aBaseURI, null, false, null, gDocInfo.isContentWindowPrivate);
};
for (var i = 0; i < rowArray.length; i++) {
var v = rowArray[i];
var dir = aDirectory.clone();
var item = gImageView.data[v][COL_IMAGE_NODE];
var uriString = gImageView.data[v][COL_IMAGE_ADDRESS];
var uri = makeURI(uriString);
try {
uri.QueryInterface(Components.interfaces.nsIURL);
dir.append(decodeURIComponent(uri.fileName));
} catch(ex) {
// data:/blob: uris
// Supply a dummy filename, otherwise Download Manager
// will try to delete the base directory on failure.
dir.append(gImageView.data[v][COL_IMAGE_TYPE]);
}
if (i == 0) {
saveAnImage(uriString, new AutoChosen(dir, uri), makeURI(item.baseURI));
} else {
// This delay is a hack which prevents the download manager
// from opening many times. See bug 377339.
setTimeout(saveAnImage, 200, uriString, new AutoChosen(dir, uri),
makeURI(item.baseURI));
}
}
}
});
}
}
function onBlockImage()
{
var permissionManager = Components.classes[PERMISSION_CONTRACTID]
.getService(nsIPermissionManager);
var checkbox = document.getElementById("blockImage");
var uri = makeURI(document.getElementById("imageurltext").value);
if (checkbox.checked)
permissionManager.add(uri, "image", nsIPermissionManager.DENY_ACTION);
else
permissionManager.remove(uri, "image");
}
function onImageSelect()
{
var previewBox = document.getElementById("mediaPreviewBox");
var mediaSaveBox = document.getElementById("mediaSaveBox");
var splitter = document.getElementById("mediaSplitter");
var tree = document.getElementById("imagetree");
var count = tree.view.selection.count;
if (count == 0) {
previewBox.collapsed = true;
mediaSaveBox.collapsed = true;
splitter.collapsed = true;
tree.flex = 1;
}
else if (count > 1) {
splitter.collapsed = true;
previewBox.collapsed = true;
mediaSaveBox.collapsed = false;
tree.flex = 1;
}
else {
mediaSaveBox.collapsed = true;
splitter.collapsed = false;
previewBox.collapsed = false;
tree.flex = 0;
makePreview(getSelectedRows(tree)[0]);
}
}
// Makes the media preview (image, video, etc) for the selected row on the media tab.
function makePreview(row)
{
var imageTree = document.getElementById("imagetree");
var item = gImageView.data[row][COL_IMAGE_NODE];
var url = gImageView.data[row][COL_IMAGE_ADDRESS];
var isBG = gImageView.data[row][COL_IMAGE_BG];
var isAudio = false;
setItemValue("imageurltext", url);
setItemValue("imagetext", item.imageText);
setItemValue("imagelongdesctext", item.longDesc);
// get cache info
var cacheKey = url.replace(/#.*$/, "");
openCacheEntry(cacheKey, function(cacheEntry) {
// find out the file size
var sizeText;
if (cacheEntry) {
let imageSize = cacheEntry.dataSize;
var kbSize = Math.round(imageSize / 1024 * 100) / 100;
sizeText = gBundle.getFormattedString("generalSize",
[formatNumber(kbSize), formatNumber(imageSize)]);
}
else
sizeText = gBundle.getString("mediaUnknownNotCached");
setItemValue("imagesizetext", sizeText);
var mimeType = item.mimeType || this.getContentTypeFromHeaders(cacheEntry);
var numFrames = item.numFrames;
var imageType;
if (mimeType) {
// We found the type, try to display it nicely
let imageMimeType = /^image\/(.*)/i.exec(mimeType);
if (imageMimeType) {
imageType = imageMimeType[1].toUpperCase();
if (numFrames > 1)
imageType = gBundle.getFormattedString("mediaAnimatedImageType",
[imageType, numFrames]);
else
imageType = gBundle.getFormattedString("mediaImageType", [imageType]);
}
else {
// the MIME type doesn't begin with image/, display the raw type
imageType = mimeType;
}
}
else {
// We couldn't find the type, fall back to the value in the treeview
imageType = gImageView.data[row][COL_IMAGE_TYPE];
}
setItemValue("imagetypetext", imageType);
var imageContainer = document.getElementById("theimagecontainer");
var oldImage = document.getElementById("thepreviewimage");
var isProtocolAllowed = checkProtocol(gImageView.data[row]);
var newImage = new Image;
newImage.id = "thepreviewimage";
var physWidth = 0, physHeight = 0;
var width = 0, height = 0;
if ((item.HTMLLinkElement || item.HTMLInputElement ||
item.HTMLImageElement || item.SVGImageElement ||
(item.HTMLObjectElement && mimeType && mimeType.startsWith("image/")) ||
isBG) && isProtocolAllowed) {
newImage.setAttribute("src", url);
physWidth = newImage.width || 0;
physHeight = newImage.height || 0;
// "width" and "height" attributes must be set to newImage,
// even if there is no "width" or "height attribute in item;
// otherwise, the preview image cannot be displayed correctly.
// Since the image might have been loaded out-of-process, we expect
// the item to tell us its width / height dimensions. Failing that
// the item should tell us the natural dimensions of the image. Finally
// failing that, we'll assume that the image was never loaded in the
// other process (this can be true for favicons, for example), and so
// we'll assume that we can use the natural dimensions of the newImage
// we just created. If the natural dimensions of newImage are not known
// then the image is probably broken.
if (!isBG) {
newImage.width = ("width" in item && item.width) || newImage.naturalWidth;
newImage.height = ("height" in item && item.height) || newImage.naturalHeight;
}
else {
// the Width and Height of an HTML tag should not be used for its background image
// (for example, "table" can have "width" or "height" attributes)
newImage.width = item.naturalWidth || newImage.naturalWidth;
newImage.height = item.naturalHeight || newImage.naturalHeight;
}
if (item.SVGImageElement) {
newImage.width = item.SVGImageElementWidth;
newImage.height = item.SVGImageElementHeight;
}
width = newImage.width;
height = newImage.height;
document.getElementById("theimagecontainer").collapsed = false
document.getElementById("brokenimagecontainer").collapsed = true;
}
else if (item.HTMLVideoElement && isProtocolAllowed) {
newImage = document.createElementNS("http://www.w3.org/1999/xhtml", "video");
newImage.id = "thepreviewimage";
newImage.src = url;
newImage.controls = true;
width = physWidth = item.videoWidth;
height = physHeight = item.videoHeight;
document.getElementById("theimagecontainer").collapsed = false;
document.getElementById("brokenimagecontainer").collapsed = true;
}
else if (item.HTMLAudioElement && isProtocolAllowed) {
newImage = new Audio;
newImage.id = "thepreviewimage";
newImage.src = url;
newImage.controls = true;
isAudio = true;
document.getElementById("theimagecontainer").collapsed = false;
document.getElementById("brokenimagecontainer").collapsed = true;
}
else {
// fallback image for protocols not allowed (e.g., javascript:)
// or elements not [yet] handled (e.g., object, embed).
document.getElementById("brokenimagecontainer").collapsed = false;
document.getElementById("theimagecontainer").collapsed = true;
}
let imageSize = "";
if (url && !isAudio) {
if (width != physWidth || height != physHeight) {
imageSize = gBundle.getFormattedString("mediaDimensionsScaled",
[formatNumber(physWidth),
formatNumber(physHeight),
formatNumber(width),
formatNumber(height)]);
}
else {
imageSize = gBundle.getFormattedString("mediaDimensions",
[formatNumber(width),
formatNumber(height)]);
}
}
setItemValue("imagedimensiontext", imageSize);
makeBlockImage(url);
imageContainer.removeChild(oldImage);
imageContainer.appendChild(newImage);
});
}
function makeBlockImage(url)
{
var permissionManager = Components.classes[PERMISSION_CONTRACTID]
.getService(nsIPermissionManager);
var prefs = Components.classes[PREFERENCES_CONTRACTID]
.getService(Components.interfaces.nsIPrefBranch);
var checkbox = document.getElementById("blockImage");
var imagePref = prefs.getIntPref("permissions.default.image");
if (!(/^https?:/.test(url)) || imagePref == 2)
// We can't block the images from this host because either is is not
// for http(s) or we don't load images at all
checkbox.hidden = true;
else {
var uri = makeURI(url);
if (uri.host) {
checkbox.hidden = false;
checkbox.label = gBundle.getFormattedString("mediaBlockImage", [uri.host]);
var perm = permissionManager.testPermission(uri, "image");
checkbox.checked = perm == nsIPermissionManager.DENY_ACTION;
}
else
checkbox.hidden = true;
}
}
var imagePermissionObserver = {
observe: function (aSubject, aTopic, aData)
{
if (document.getElementById("mediaPreviewBox").collapsed)
return;
if (aTopic == "perm-changed") {
var permission = aSubject.QueryInterface(Components.interfaces.nsIPermission);
if (permission.type == "image") {
var imageTree = document.getElementById("imagetree");
var row = getSelectedRow(imageTree);
var item = gImageView.data[row][COL_IMAGE_NODE];
var url = gImageView.data[row][COL_IMAGE_ADDRESS];
if (permission.matchesURI(makeURI(url), true)) {
makeBlockImage(url);
}
}
}
}
}
function getContentTypeFromHeaders(cacheEntryDescriptor)
{
if (!cacheEntryDescriptor)
return null;
let headers = cacheEntryDescriptor.getMetaDataElement("response-head");
let type = /^Content-Type:\s*(.*?)\s*(?:\;|$)/mi.exec(headers);
return type && type[1];
}
function setItemValue(id, value)
{
var item = document.getElementById(id);
if (value) {
item.parentNode.collapsed = false;
item.value = value;
}
else
item.parentNode.collapsed = true;
}
function formatNumber(number)
{
return (+number).toLocaleString(); // coerce number to a numeric value before calling toLocaleString()
}
function formatDate(datestr, unknown)
{
// scriptable date formatter, for pretty printing dates
var dateService = Components.classes["@mozilla.org/intl/scriptabledateformat;1"]
.getService(Components.interfaces.nsIScriptableDateFormat);
var date = new Date(datestr);
if (!date.valueOf())
return unknown;
return dateService.FormatDateTime("", dateService.dateFormatLong,
dateService.timeFormatSeconds,
date.getFullYear(), date.getMonth()+1, date.getDate(),
date.getHours(), date.getMinutes(), date.getSeconds());
}
function doCopy()
{
if (!gClipboardHelper)
return;
var elem = document.commandDispatcher.focusedElement;
if (elem && "treeBoxObject" in elem) {
var view = elem.view;
var selection = view.selection;
var text = [], tmp = '';
var min = {}, max = {};
var count = selection.getRangeCount();
for (var i = 0; i < count; i++) {
selection.getRangeAt(i, min, max);
for (var row = min.value; row <= max.value; row++) {
view.performActionOnRow("copy", row);
tmp = elem.getAttribute("copybuffer");
if (tmp)
text.push(tmp);
elem.removeAttribute("copybuffer");
}
}
gClipboardHelper.copyString(text.join("\n"));
}
}
function doSelectAllMedia()
{
var tree = document.getElementById("imagetree");
if (tree)
tree.view.selection.selectAll();
}
function doSelectAll()
{
var elem = document.commandDispatcher.focusedElement;
if (elem && "treeBoxObject" in elem)
elem.view.selection.selectAll();
}
function selectImage()
{
if (!gImageElement)
return;
var tree = document.getElementById("imagetree");
for (var i = 0; i < tree.view.rowCount; i++) {
// If the image row element is the image selected from the "View Image Info" context menu item.
let image = gImageView.data[i][COL_IMAGE_NODE];
if (!gImageView.data[i][COL_IMAGE_BG] &&
gImageElement.currentSrc == gImageView.data[i][COL_IMAGE_ADDRESS] &&
gImageElement.width == image.width &&
gImageElement.height == image.height &&
gImageElement.imageText == image.imageText) {
tree.view.selection.select(i);
tree.treeBoxObject.ensureRowIsVisible(i);
tree.focus();
return;
}
}
}
function checkProtocol(img)
{
var url = img[COL_IMAGE_ADDRESS];
return /^data:image\//i.test(url) ||
/^(https?|ftp|file|about|chrome|resource):/.test(url);
}