bug 11632 - save page with images, stylesheets, objects and applets.

r=hewitt, brade, sr=hyatt


git-svn-id: svn://10.0.0.236/trunk@110364 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
ben%netscape.com
2001-12-12 05:05:12 +00:00
parent c4cb151f1d
commit 2f91c5f6d9
14 changed files with 610 additions and 207 deletions

View File

@@ -62,6 +62,7 @@ var gBrowser = null;
// focused frame URL
var gFocusedURL = null;
var gFocusedDocument = null;
/**
* We can avoid adding multiple load event listeners and save some time by adding
@@ -98,6 +99,7 @@ function contentAreaFrameFocus()
var focusedWindow = document.commandDispatcher.focusedWindow;
if (isDocumentFrame(focusedWindow)) {
gFocusedURL = focusedWindow.location.href;
gFocusedDocument = focusedWindow.document;
}
}

View File

@@ -106,7 +106,7 @@
<command id="cmd_newEditorTemplate"/>
<command id="cmd_newEditorDraft"/> -->
<command id="Browser:OpenFile" oncommand="BrowserOpenFileWindow();"/>
<command id="Browser:SavePage" oncommand="savePage();"/>
<command id="Browser:SavePage" oncommand="saveDocument(window._content.document);"/>
<command id="Browser:EditPage" oncommand="editPage(window._content.location.href,window, false);"/>
<command id="Browser:Open" oncommand="BrowserOpenWindow();"/>
<command id="cmd_printSetup" oncommand="goPageSetup();"/>
@@ -186,7 +186,7 @@
<menuitem id="menu_closeWindow" hidden="true" command="cmd_closeWindow" key="key_closeWindow" label="&closeWindow.label;"/>
<menuseparator id="menu_closeSeparator" hidden="true"/>
<menuitem label="&savePageCmd.label;" accesskey="&savePageCmd.accesskey;" key="key_savePage" command="Browser:SavePage"/>
<menuitem id="savepage" label="&saveFrameCmd.label;" accesskey="&saveFrameCmd.accesskey;" oncommand="savePage(gFocusedURL);" hidden="true"/>
<menuitem id="savepage" label="&saveFrameCmd.label;" accesskey="&saveFrameCmd.accesskey;" oncommand="savePage(gFocusedURL, gFocusedDocument);" hidden="true"/>
<menuseparator/>
<menuitem label="&editPageCmd.label;" accesskey="&editPageCmd.accesskey;" key="key_editPage" command="Browser:EditPage" />
<menuseparator/>

View File

@@ -39,6 +39,12 @@
*
* ***** END LICENSE BLOCK ***** */
/*
* - [ Dependencies ] ---------------------------------------------------------
* utilityOverlay.js:
* - gatherTextUnder
*/
var pref = null;
pref = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
@@ -205,7 +211,7 @@
saveModifier = saveModifier ? event.shiftKey : event.metaKey;
if (saveModifier) { // if saveModifier is down
savePage(href); // save the link
saveURL(href, gatherTextUnder(event.target));
return true;
}
if (event.altKey) // if alt is down

View File

@@ -126,7 +126,7 @@
<menuitem id="context-savepage"
label="&savePageCmd.label;"
accesskey="&savePageCmd.accesskey;"
oncommand="gContextMenu.savePage();"/>
oncommand="saveDocument(window._content.document);"/>
<menuitem id="context-saveframe"
label="&saveFrameCmd.label;"
accesskey="&saveFrameCmd.accesskey;"

View File

@@ -20,6 +20,7 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Goodger <ben@netscape.com> (Save File)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@@ -39,123 +40,440 @@
* Determine whether or not a given focused DOMWindow is in the content
* area.
**/
function isDocumentFrame(aFocusedWindow)
function isDocumentFrame(aFocusedWindow)
{
var contentFrames = _content.frames;
if (contentFrames.length) {
for (var i = 0; i < contentFrames.length; ++i) {
if (aFocusedWindow == contentFrames[i])
return true;
}
}
return false;
}
function urlSecurityCheck(url, doc)
{
// URL Loading Security Check
var focusedWindow = doc.commandDispatcher.focusedWindow;
var sourceWin = isDocumentFrame(focusedWindow) ? focusedWindow.location.href : focusedWindow._content.location.href;
const nsIScriptSecurityManager = Components.interfaces.nsIScriptSecurityManager;
var secMan = Components.classes["@mozilla.org/scriptsecuritymanager;1"].getService().
QueryInterface(nsIScriptSecurityManager);
try {
secMan.checkLoadURIStr(sourceWin, url, nsIScriptSecurityManager.STANDARD);
} catch (e) {
throw "Load of " + url + " denied.";
}
}
function openNewWindowWith(url)
{
urlSecurityCheck(url, document);
var newWin;
var wintype = document.firstChild.getAttribute('windowtype');
// if and only if the current window is a browser window and it has a document with a character
// set, then extract the current charset menu setting from the current document and use it to
// initialize the new browser window...
if (window && (wintype == "navigator:browser") &&
window._content && window._content.document) {
var DocCharset = window._content.document.characterSet;
var charsetArg = "charset="+DocCharset;
//we should "inherit" the charset menu setting in a new window
newWin = window.openDialog( getBrowserURL(), "_blank", "chrome,all,dialog=no", url, charsetArg, true );
}
else { // forget about the charset information.
newWin = window.openDialog( getBrowserURL(), "_blank", "chrome,all,dialog=no", url, null, true );
}
// Fix new window.
newWin.saveFileAndPos = true;
}
function openNewTabWith(url)
{
urlSecurityCheck(url, document);
var wintype = document.firstChild.getAttribute('windowtype');
// if and only if the current window is a browser window and it has a document with a character
// set, then extract the current charset menu setting from the current document and use it to
// initialize the new browser window...
if (window && (wintype == "navigator:browser")) {
var browser=getBrowser();
var t = browser.addTab(url); // open link in new tab
if (pref && !pref.getBoolPref("browser.tabs.loadInBackground"))
browser.selectedTab = t;
}
// Fix new window.
newWin.saveFileAndPos = true;
}
function findParentNode(node, parentNode)
{
if (node && node.nodeType == Node.TEXT_NODE) {
node = node.parentNode;
}
while (node) {
var nodeName = node.localName;
if (!nodeName)
return null;
nodeName = nodeName.toLowerCase();
if (nodeName == "body" || nodeName == "html" ||
nodeName == "#document") {
return null;
}
if (nodeName == parentNode)
return node;
node = node.parentNode;
}
return null;
}
// Clientelle: (Make sure you don't break any of these)
// - File -> Save Page/Frame As...
// - Context -> Save Page/Frame As...
// - Context -> Save Link As...
// - Context -> Save Image As...
// - Shift-Click Save Link As
//
// Try saving each of these types:
// - A complete webpage using File->Save Page As, and Context->Save Page As
// - A webpage as HTML only using the above methods
// - A webpage as Text only using the above methods
// - An image with an extension (e.g. .jpg) in its file name, using
// Context->Save Image As...
// - An image without an extension (e.g. a banner ad on cnn.com) using
// the above method.
// - A linked document using Save Link As...
// - A linked document using shift-click Save Link As...
//
function saveURL(aURL, aFileName, aFilePickerTitleKey)
{
var data = {
fileName: aFileName,
filePickerTitle: aFilePickerTitleKey
};
var sniffer = new nsHeaderSniffer(aURL, foundHeaderInfo, data);
}
function foundHeaderInfo(aSniffer, aData)
{
var contentType = aSniffer.contentType;
var fp = makeFilePicker();
var titleKey = aData.filePickerTitle || "SaveLinkTitle";
var bundle = getStringBundle();
fp.init(window, bundle.GetStringFromName(titleKey),
Components.interfaces.nsIFilePicker.modeSave);
appendFiltersForContentType(fp, aSniffer.contentType);
// Determine what the 'default' string to display in the File Picker dialog
// should be.
var defaultFileName = getDefaultFileName(aData.fileName,
aSniffer.suggestedFileName,
aSniffer.uri);
fp.defaultString = getNormalizedLeafName(defaultFileName, contentType);
if (fp.show() == Components.interfaces.nsIFilePicker.returnCancel || !fp.file)
return;
fp.file.leafName = validateFileName(fp.file.leafName);
fp.file.leafName = getNormalizedLeafName(fp.file.leafName, contentType);
var persistArgs = {
source : aSniffer.uri,
target : fp.file,
postData : getPostData()
};
openDialog("chrome://global/content/helperAppDldProgress.xul", "",
"chrome,titlebar,minizable,dialog=yes",
makeWebBrowserPersist(), persistArgs);
}
function nsHeaderSniffer(aURL, aCallback, aData)
{
this.mPersist = makeWebBrowserPersist();
this.mCallback = aCallback;
this.mData = aData;
this.mPersist.progressListener = this;
this.mTempFile = makeTempFile();
while (this.mTempFile.exists())
this.mTempFile = makeTempFile();
const stdURLContractID = "@mozilla.org/network/standard-url;1";
const stdURLIID = Components.interfaces.nsIURI;
this.uri = Components.classes[stdURLContractID].createInstance(stdURLIID);
this.uri.spec = aURL;
this.mPersist.saveURI(this.uri, null, this.mTempFile);
}
nsHeaderSniffer.prototype = {
onStateChange: function (aWebProgress, aRequest, aStateFlags, aStatus)
{
var contentFrames = _content.frames;
if (contentFrames.length) {
for (var i = 0; i < contentFrames.length; ++i) {
if (aFocusedWindow == contentFrames[i])
return true;
if (aStateFlags & Components.interfaces.nsIWebProgressListener.STATE_START) {
try {
var channel = aRequest.QueryInterface(Components.interfaces.nsIChannel);
this.contentType = channel.contentType;
try {
var httpChannel = aRequest.QueryInterface(Components.interfaces.nsIHttpChannel);
this.mContentDisposition = httpChannel.getResponseHeader("content-disposition");
}
catch (e) {
}
this.mPersist.cancelSave();
if (this.mTempFile.exists())
this.mTempFile.remove(false);
this.mCallback(this, this.mData);
}
catch (e) {
}
}
return false;
}
function urlSecurityCheck(url, doc) {
// URL Loading Security Check
var focusedWindow = doc.commandDispatcher.focusedWindow;
var sourceWin = isDocumentFrame(focusedWindow) ? focusedWindow.location.href : focusedWindow._content.location.href;
const nsIScriptSecurityManager = Components.interfaces.nsIScriptSecurityManager;
var secMan = Components.classes["@mozilla.org/scriptsecuritymanager;1"].getService().
QueryInterface(nsIScriptSecurityManager);
try {
secMan.checkLoadURIStr(sourceWin, url, nsIScriptSecurityManager.STANDARD);
} catch (e) {
throw "Load of " + url + " denied.";
}
}
function openNewWindowWith(url) {
urlSecurityCheck(url, document);
var newWin;
var wintype = document.firstChild.getAttribute('windowtype');
// if and only if the current window is a browser window and it has a document with a character
// set, then extract the current charset menu setting from the current document and use it to
// initialize the new browser window...
if (window && (wintype == "navigator:browser") &&
window._content && window._content.document) {
var DocCharset = window._content.document.characterSet;
var charsetArg = "charset="+DocCharset;
//we should "inherit" the charset menu setting in a new window
newWin = window.openDialog( getBrowserURL(), "_blank", "chrome,all,dialog=no", url, charsetArg, true );
}
else { // forget about the charset information.
newWin = window.openDialog( getBrowserURL(), "_blank", "chrome,all,dialog=no", url, null, true );
}
// Fix new window.
newWin.saveFileAndPos = true;
}
function openNewTabWith(url) {
urlSecurityCheck(url, document);
var wintype = document.firstChild.getAttribute('windowtype');
// if and only if the current window is a browser window and it has a document with a character
// set, then extract the current charset menu setting from the current document and use it to
// initialize the new browser window...
if (window && (wintype == "navigator:browser")) {
var browser=getBrowser();
var t = browser.addTab(url); // open link in new tab
if (pref && !pref.getBoolPref("browser.tabs.loadInBackground"))
browser.selectedTab = t;
}
// Fix new window.
newWin.saveFileAndPos = true;
}
function savePage(url)
},
onLocationChange: function (aWebProgress, aRequest, aLocation) { },
onStatusChange: function (aWebProgress, aRequest, aStatus, aMessage) { },
onSecurityChange: function (aWebProgress, aRequest, aState) { },
onProgressChange: function (aWebProgress, aRequest, aCurSelfProgress,
aMaxSelfProgress, aCurTotalProgress,
aMaxTotalProgress) { },
get suggestedFileName()
{
var postData = null; // No post data, usually.
var cacheKey = null;
// Default is to save current page.
if ( !url )
url = window._content.location.href;
try {
var sessionHistory = getWebNavigation().sessionHistory;
var entry = sessionHistory.getEntryAtIndex(sessionHistory.index, false).QueryInterface(Components.interfaces.nsISHEntry);
postData = entry.postData;
cacheKey = entry.cacheKey;
} catch(e) {
}
// Use stream xfer component to prompt for destination and save.
var xfer = Components.classes["@mozilla.org/appshell/component/xfer;1"].getService(Components.interfaces["nsIStreamTransfer"]);
try {
xfer.SelectFileAndTransferLocationSpec( url, window, "", "", true, postData, cacheKey );
} catch( exception ) {
return false;
}
return true;
}
//Note: "function editPage(url)" was moved to utilityOverlay.js
function findParentNode(node, parentNode)
{
if (node && node.nodeType == Node.TEXT_NODE) {
node = node.parentNode;
}
while (node) {
var nodeName = node.localName;
if (!nodeName)
return null;
nodeName = nodeName.toLowerCase();
if (nodeName == "body" || nodeName == "html" ||
nodeName == "#document") {
return null;
var filename = "";
var name = this.mContentDisposition;
if (name) {
var ix = name.indexOf("filename=");
if (ix > 0) {
filename = name.substr(ix, name.length);
if (filename != "") {
ix = filename.lastIndexOf(";");
if (ix > 0)
filename = filename.substr(0, ix);
// XXX strip out quotes;
}
}
if (nodeName == parentNode)
return node;
node = node.parentNode;
}
return null;
}
return filename;
}
};
function saveDocument(aDocument)
{
// When this function is called, we assume we're saving text/html or
// compatible.
var fp = makeFilePicker();
var bundle = getStringBundle();
fp.init(window, bundle.GetStringFromName("SavePageTitle"),
Components.interfaces.nsIFilePicker.modeSave);
appendFiltersForContentType(fp, "text/html", MODE_COMPLETE);
// Determine what the 'default' string to display in the File Picker dialog
// should be.
const stdURLContractID = "@mozilla.org/network/standard-url;1";
const stdURLIID = Components.interfaces.nsIURI;
var uri = Components.classes[stdURLContractID].createInstance(stdURLIID);
uri.spec = aDocument.URL;
var defaultFileName = getDefaultFileName(null, null, uri, aDocument);
fp.defaultString = getNormalizedLeafName(defaultFileName, "text/html");
if (fp.show() == Components.interfaces.nsIFilePicker.returnCancel || !fp.file)
return;
var contentType = fp.filterIndex == 2 ? "text/unicode" : "text/html";
fp.file.leafName = validateFileName(fp.file.leafName);
fp.file.leafName = getNormalizedLeafName(fp.file.URL, contentType);
var persistArgs = {
source : fp.filterIndex == 0 ? aDocument : uri,
// XXX turn this on when Adam lands the ability to save as a specific content
// type
// contentType : fp.filterIndex == 2 ? "text/unicode" : "text/html";
target : fp.file,
postData : getPostData()
};
openDialog("chrome://global/content/helperAppDldProgress.xul", "",
"chrome,titlebar,minizable,dialog=yes",
makeWebBrowserPersist(), persistArgs);
}
const MODE_COMPLETE = 0;
const MODE_HTMLONLY = 1;
function appendFiltersForContentType(aFilePicker, aContentType, aSaveMode)
{
var bundle = getStringBundle();
switch (aContentType) {
case "text/html":
if (aSaveMode == MODE_COMPLETE)
aFilePicker.appendFilter(bundle.GetStringFromName("WebPageCompleteFilter"), "*.htm; *.html");
aFilePicker.appendFilter(bundle.GetStringFromName("WebPageHTMLOnlyFilter"), "*.htm; *.html");
aFilePicker.appendFilter(bundle.GetStringFromName("TextOnlyFilter"), "*.txt");
break;
default:
var mimeInfo = getMIMEInfoForType(aContentType);
if (mimeInfo) {
var extCount = { };
var extList = { };
mimeInfo.GetFileExtensions(extCount, extList);
var extString = "";
for (var i = 0; i < extCount.value; ++i) {
if (i > 0)
extString += "; "; // If adding more than one extension, separate by semi-colon
extString += "*." + extList.value[i];
}
aFilePicker.appendFilter(mimeInfo.Description, extString);
}
else
aFilePicker.appendFilter(bundle.GetStringFromName("AllFilesFilter"), "*.*");
break;
}
}
function getPostData()
{
try {
var sessionHistory = getWebNavigation().sessionHistory;
entry = sessionHistory.getEntryAtIndex(sessionHistory.index, false);
entry = entry.QueryInterface(Components.interfaces.nsISHEntry);
return entry.postData;
}
catch (e) {
}
return null;
}
function getStringBundle()
{
const bundleURL = "chrome://communicator/locale/contentAreaCommands.properties";
const sbsContractID = "@mozilla.org/intl/stringbundle;1";
const sbsIID = Components.interfaces.nsIStringBundleService;
const sbs = Components.classes[sbsContractID].getService(sbsIID);
const lsContractID = "@mozilla.org/intl/nslocaleservice;1";
const lsIID = Components.interfaces.nsILocaleService;
const ls = Components.classes[lsContractID].getService(lsIID);
var appLocale = ls.GetApplicationLocale();
return sbs.createBundle(bundleURL, appLocale);
}
function makeWebBrowserPersist()
{
const persistContractID = "@mozilla.org/embedding/browser/nsWebBrowserPersist;1";
const persistIID = Components.interfaces.nsIWebBrowserPersist;
return Components.classes[persistContractID].createInstance(persistIID);
}
function makeFilePicker()
{
const fpContractID = "@mozilla.org/filepicker;1";
const fpIID = Components.interfaces.nsIFilePicker;
return Components.classes[fpContractID].createInstance(fpIID);
}
function makeTempFile()
{
const mimeTypes = "TmpD";
const flContractID = "@mozilla.org/file/directory_service;1";
const flIID = Components.interfaces.nsIProperties;
var fileLocator = Components.classes[flContractID].getService(flIID);
var tempFile = fileLocator.get(mimeTypes, Components.interfaces.nsIFile);
tempFile.append("~sav" + Math.floor(Math.random() * 1000) + ".tmp");
return tempFile;
}
function getMIMEInfoForType(aMIMEType)
{
const mimeSvcCID = "{03af31da-3109-11d3-8cd0-0060b0fc14a3}";
const mimeSvcIID = Components.interfaces.nsIMIMEService;
const mimeSvc = Components.classesByID[mimeSvcCID].getService(mimeSvcIID);
try {
return mimeSvc.GetFromMIMEType(aMIMEType);
}
catch (e) {
}
return null;
}
function getDefaultFileName(aDefaultFileName, aNameFromHeaders, aDocumentURI, aDocument)
{
if (aDefaultFileName)
return validateFileName(aDefaultFileName); // 1) Use the caller-provided name, if any
if (aNameFromHeaders)
return validateFileName(aNameFromHeaders); // 2) Use the name suggested by the HTTP headers
if (aDocument && aDocument.title != "")
return validateFileName(aDocument.title) // 3) Use the document title
var url = aDocumentURI.QueryInterface(Components.interfaces.nsIURL);
if (url.fileName != "")
return url.fileName; // 4) Use the actual file name, if present
return aDocumentURI.host; // 5) Use the host.
}
function validateFileName(aFileName)
{
var re = /[\/]+/g;
if (navigator.appVersion.indexOf("Windows") != -1) {
re = /[\\\/\|]+/g;
aFileName = aFileName.replace(/[\"]+/g, "'");
aFileName = aFileName.replace(/[\*\:\?]+/g, " ");
aFileName = aFileName.replace(/[\<]+/g, "(");
aFileName = aFileName.replace(/[\>]+/g, ")");
}
else if (navigator.appVersion.indexOf("Macintosh") != -1)
re = /[\:\/]+/g;
return aFileName.replace(re, "_");
}
function getNormalizedLeafName(aFile, aContentType)
{
// Fix up the file name we're saving to so that if the user enters
// no extension, an appropriate one is appended.
var leafName = aFile;
var mimeInfo = getMIMEInfoForType(aContentType);
if (mimeInfo) {
var extCount = { };
var extList = { };
mimeInfo.GetFileExtensions(extCount, extList);
const stdURLContractID = "@mozilla.org/network/standard-url;1";
const stdURLIID = Components.interfaces.nsIURI;
var uri = Components.classes[stdURLContractID].createInstance(stdURLIID);
uri.spec = "file:///" + aFile;
var url = uri.QueryInterface(Components.interfaces.nsIURL);
if (aContentType == "text/html") {
if ((url.fileExtension &&
url.fileExtension != "htm" && url.fileExtension != "html") ||
(!url.fileExtension))
return leafName + ".html";
}
if (!url.fileExtension)
return leafName + "." + extList.value[0];
}
return leafName;
}

View File

@@ -467,15 +467,15 @@ nsContextMenu.prototype = {
},
// Save URL of clicked-on frame.
saveFrame : function () {
this.savePage( this.target.ownerDocument.location.href, true );
saveDocument( this.target.ownerDocument );
},
// Save URL of clicked-on link.
saveLink : function () {
this.savePage( this.linkURL(), false );
saveURL( this.linkURL(), this.linkText() );
},
// Save URL of clicked-on image.
saveImage : function () {
this.savePage( this.imageURL, true );
saveURL( this.imageURL, null, "SaveImageTitle" );
},
// Generate email address and put it on clipboard.
copyEmail : function () {
@@ -578,53 +578,9 @@ nsContextMenu.prototype = {
},
// Get text of link (if possible).
linkText : function () {
var text = this.gatherTextUnder( this.link );
var text = gatherTextUnder( this.link );
return text;
},
// Gather all descendent text under given document node.
gatherTextUnder : function ( root ) {
var text = "";
var node = root.firstChild;
var depth = 1;
while ( node && depth > 0 ) {
// See if this node is text.
if ( node.nodeName == "#text" ) {
// Add this text to our collection.
text += " " + node.data;
} else if ( node.nodeType == Node.ELEMENT_NODE
&& node.localName.toUpperCase() == "IMG" ) {
// If it has an alt= attribute, use that.
var altText = node.getAttribute( "alt" );
if ( altText && altText != "" ) {
text = altText;
break;
}
}
// Find next node to test.
// First, see if this node has children.
if ( node.hasChildNodes() ) {
// Go to first child.
node = node.firstChild;
depth++;
} else {
// No children, try next sibling.
if ( node.nextSibling ) {
node = node.nextSibling;
} else {
// Last resort is our next oldest uncle/aunt.
node = node.parentNode.nextSibling;
depth--;
}
}
}
// Strip leading whitespace.
text = text.replace( /^\s+/, "" );
// Strip trailing whitespace.
text = text.replace( /\s+$/, "" );
// Compress remaining whitespace.
text = text.replace( /\s+/g, " " );
return text;
},
// Returns "true" if there's no text selected, null otherwise.
isNoTextSelected : function ( event ) {
// Not implemented so all text-selected-based options are disabled.
@@ -686,34 +642,6 @@ nsContextMenu.prototype = {
return ioService.newURI(baseURI.resolve(url), null).spec;
},
// Save specified URL in user-selected file.
savePage : function ( url, doNotValidate ) {
var postData = null; // No post data, usually.
var cacheKey = null;
// Default is to save current page.
if ( !url ) {
url = window._content.location.href;
try {
var sessionHistory = getWebNavigation().sessionHistory;
var entry = sessionHistory.getEntryAtIndex(sessionHistory.index, false).QueryInterface(Components.interfaces.nsISHEntry);
postData = entry.postData;
cacheKey = entry.cacheKey;
} catch(e) {
}
}
// Use stream xfer component to prompt for destination and save.
var xfer = this.getService( "@mozilla.org/appshell/component/xfer;1",
"nsIStreamTransfer" );
try {
xfer.SelectFileAndTransferLocationSpec( url, window, "", "", doNotValidate, postData, cacheKey );
} catch( exception ) {
// Failed (or cancelled), give them another chance.
}
return;
},
// Parse coords= attribute and return array.
parseCoords : function ( area ) {
return [];

View File

@@ -381,6 +381,52 @@ function extractFileNameFromUrl(urlstr)
return urlstr.slice(urlstr.lastIndexOf( "/" )+1);
}
// Gather all descendent text under given document node.
function gatherTextUnder ( root )
{
var text = "";
var node = root.firstChild;
var depth = 1;
while ( node && depth > 0 ) {
// See if this node is text.
if ( node.nodeName == "#text" ) {
// Add this text to our collection.
text += " " + node.data;
} else if ( node.nodeType == Node.ELEMENT_NODE
&& node.localName.toUpperCase() == "IMG" ) {
// If it has an alt= attribute, use that.
var altText = node.getAttribute( "alt" );
if ( altText && altText != "" ) {
text = altText;
break;
}
}
// Find next node to test.
// First, see if this node has children.
if ( node.hasChildNodes() ) {
// Go to first child.
node = node.firstChild;
depth++;
} else {
// No children, try next sibling.
if ( node.nextSibling ) {
node = node.nextSibling;
} else {
// Last resort is our next oldest uncle/aunt.
node = node.parentNode.nextSibling;
depth--;
}
}
}
// Strip leading whitespace.
text = text.replace( /^\s+/, "" );
// Strip trailing whitespace.
text = text.replace( /\s+$/, "" );
// Compress remaining whitespace.
text = text.replace( /\s+/g, " " );
return text;
}
var offlineObserver = {
observe: function(subject, topic, state) {
// sanity checks

View File

@@ -2,3 +2,12 @@
saveImageAs=Save Image (%S)...
searchText=Search for '%S'
SavePageTitle=Save Web Page
SaveImageTitle=Save Picture
SaveLinkTitle=Save As
WebPageCompleteFilter=Web Page, complete (*.htm;*.html)
WebPageHTMLOnlyFilter=Web Page, HTML only (*.htm;*.html)
TextOnlyFilter=Text File (*.txt)
AllFilesFilter=All Files (*.*)

View File

@@ -93,6 +93,8 @@ function filepickerLoad() {
filterMenuList.selectedIndex = 0;
var filterBox = document.getElementById("filterBox");
filterBox.removeAttribute("hidden");
filterMenuList.selectedIndex = o.filterIndex;
} else if (filePickerMode == nsIFilePicker.modeGetFolder) {
outlinerView.showOnlyDirectories = true;
}
@@ -166,6 +168,10 @@ function openOnOK()
gotoDirectory(dir);
retvals.file = dir;
retvals.buttonStatus = nsIFilePicker.returnCancel;
var filterMenuList = document.getElementById("filterMenuList");
retvals.filterIndex = filterMenuList.selectedIndex;
return false;
}

View File

@@ -63,6 +63,7 @@ function nsFilePicker()
/* attributes */
this.mDefaultString = "";
this.mFilterIndex = 0;
if (lastDirectory) {
this.mDisplayDirectory = Components.classes[LOCAL_FILE_CONTRACTID].createInstance(nsILocalFile);
this.mDisplayDirectory.initWithUnicodePath(lastDirectory);
@@ -99,6 +100,10 @@ nsFilePicker.prototype = {
set defaultString(a) { this.mDefaultString = a; },
get defaultString() { return this.mDefaultString; },
/* attribute long filterIndex; */
set filterIndex(a) { this.mFilterIndex = a; },
get filterInrex() { return this.mFilterIndex; },
/* methods */
init: function(parent, title, mode) {
this.mParentWindow = parent;
@@ -151,6 +156,7 @@ nsFilePicker.prototype = {
o.mode = this.mMode;
o.displayDirectory = this.mDisplayDirectory;
o.defaultString = this.mDefaultString;
o.filterIndex = this.mFilterIndex;</pre>
o.filters = new Object();
o.filters.titles = this.mFilterTitles;
o.filters.types = this.mFilters;
@@ -179,6 +185,7 @@ nsFilePicker.prototype = {
"chrome,modal,titlebar,resizable=yes,dependent=yes",
o);
this.mFile = o.retvals.file;
this.mFilterIndex = o.retvals.filterindex;</pre>
lastDirectory = o.retvals.directory;
return o.retvals.buttonStatus;
} catch(ex) { dump("unable to open file picker\n" + ex + "\n"); }

View File

@@ -63,6 +63,7 @@ function nsFilePicker()
/* attributes */
this.mDefaultString = "";
this.mFilterIndex = 0;
if (lastDirectory) {
this.mDisplayDirectory = Components.classes[LOCAL_FILE_CONTRACTID].createInstance(nsILocalFile);
this.mDisplayDirectory.initWithUnicodePath(lastDirectory);
@@ -99,6 +100,10 @@ nsFilePicker.prototype = {
set defaultString(a) { this.mDefaultString = a; },
get defaultString() { return this.mDefaultString; },
/* attribute long filterIndex; */
set filterIndex(a) { this.mFilterIndex = a; },
get filterInrex() { return this.mFilterIndex; },
/* methods */
init: function(parent, title, mode) {
this.mParentWindow = parent;
@@ -151,6 +156,7 @@ nsFilePicker.prototype = {
o.mode = this.mMode;
o.displayDirectory = this.mDisplayDirectory;
o.defaultString = this.mDefaultString;
o.filterIndex = this.mFilterIndex;</pre>
o.filters = new Object();
o.filters.titles = this.mFilterTitles;
o.filters.types = this.mFilters;
@@ -179,6 +185,7 @@ nsFilePicker.prototype = {
"chrome,modal,titlebar,resizable=yes,dependent=yes",
o);
this.mFile = o.retvals.file;
this.mFilterIndex = o.retvals.filterindex;</pre>
lastDirectory = o.retvals.directory;
return o.retvals.buttonStatus;
} catch(ex) { dump("unable to open file picker\n" + ex + "\n"); }

View File

@@ -31,6 +31,8 @@ var dialog;
// the helperAppLoader is a nsIHelperAppLauncher object
var helperAppLoader;
var webBrowserPersist;
var persistArgs;
// random global variables...
var completed = false;
@@ -67,13 +69,15 @@ var progressListener = {
percentMsg = replaceInsert( percentMsg, 1, 100 );
dialog.progressText.setAttribute("label", percentMsg);
processEndOfDownload();
const nsIWebBrowserPersist = Components.interfaces.nsIWebBrowserPersist;
if (helperAppLoader || webBrowserPersist &&
webBrowserPersist.currentState == nsIWebBrowserPersist.PERSIST_STATE_FINISHED)
setTimeout("processEndOfDownload()", 300);
}
},
onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress)
{
if (!gRestartChecked)
{
gRestartChecked = true;
@@ -281,11 +285,29 @@ function loadDialog()
{
var sourceUrlValue = {};
var initialDownloadTimeValue = {};
// targetFile is global because we are going to want re-use later one...
targetFile = helperAppLoader.getDownloadInfo(sourceUrlValue, initialDownloadTimeValue);
var sourceUrl = null;
// targetFile is global because we are going to want re-use later one...
if (helperAppLoader) {
targetFile = helperAppLoader.getDownloadInfo(sourceUrlValue, initialDownloadTimeValue);
var sourceUrl = sourceUrlValue.value;
startTime = initialDownloadTimeValue.value / 1000;
sourceUrl = sourceUrlValue.value;
startTime = initialDownloadTimeValue.value / 1000;
}
else if (webBrowserPersist) {
// When saving web pages, the file we're saving into is passed to us as a parameter.
try {
persistArgs.source.QueryInterface(Components.interfaces.nsIURI);
sourceUrl = persistArgs.source;
}
catch (e) {
sourceUrl = { spec: persistArgs.source.URL };
}
// When saving web pages, we don't need to do anything special to receive the time
// at which the transfer started, so just assume it started 'now'.
startTime = ( new Date() ).getTime();
}
// set the elapsed time on the first pass...
var now = ( new Date() ).getTime();
@@ -312,10 +334,15 @@ function replaceInsert( text, index, value ) {
function onLoad() {
// Set global variables.
helperAppLoader = window.arguments[0].QueryInterface( Components.interfaces.nsIHelperAppLauncher );
if ( !helperAppLoader ) {
dump( "Invalid argument to downloadProgress.xul\n" );
try {
helperAppLoader = window.arguments[0].QueryInterface( Components.interfaces.nsIHelperAppLauncher );
}
catch (e) {
webBrowserPersist = window.arguments[0].QueryInterface( Components.interfaces.nsIWebBrowserPersist );
}
if ( !helperAppLoader && !webBrowserPersist ) {
dump( "Invalid argument to helperAppDldProgress.xul\n" );
window.close()
return;
}
@@ -342,12 +369,44 @@ function onLoad() {
var object = this;
doSetOKCancel("", function () { return object.onCancel();});
// set our web progress listener on the helper app launcher
if (helperAppLoader)
helperAppLoader.setWebProgressListener(progressListener);
else if (webBrowserPersist) {
webBrowserPersist.progressListener = progressListener;
persistArgs = window.arguments[1];
targetFile = persistArgs.target;
try {
var uri = persistArgs.source.QueryInterface(Components.interfaces.nsIURI);
webBrowserPersist.saveURI(uri, persistArgs.postData, targetFile);
}
catch (e) {
// Saving a Document, not a URI:
// Create the local directory into which to save associated files.
const lfContractID = "@mozilla.org/file/local;1";
const lfIID = Components.interfaces.nsILocalFile;
var filesFolder = Components .classes[lfContractID].createInstance(lfIID);
filesFolder.initWithUnicodePath(persistArgs.target.unicodePath);
var nameWithoutExtension = filesFolder.leafName;
nameWithoutExtension = nameWithoutExtension.substring(0, nameWithoutExtension.lastIndexOf("."));
var filesFolderLeafName = getString("filesFolder");
filesFolderLeafName = filesFolderLeafName.replace(/\^BASE\^/, nameWithoutExtension);
filesFolder.leafName = filesFolderLeafName;
filesFolder.create(lfIID.DIRECTORY_TYPE, 0644);
webBrowserPersist.saveDocument(persistArgs.source, targetFile, filesFolder);
}
}
// Fill dialog.
loadDialog();
// set our web progress listener on the helper app launcher
helperAppLoader.setWebProgressListener(progressListener);
if ( window.opener ) {
moveToAlertPosition();
} else {
@@ -396,6 +455,10 @@ function onCancel ()
catch( exception ) {}
}
else if (webBrowserPersist)
{
webBrowserPersist.cancelSave();
}
// Close up dialog by returning true.
return true;
@@ -445,8 +508,10 @@ function setupPostProgressUI()
// and enable the open and open folder buttons on the dialog.
function processEndOfDownload()
{
if (!keepProgressWindowUpBox.checked)
if (!keepProgressWindowUpBox.checked) {
closeWindow(); // shut down, we are all done.
return;
}
// o.t the user has asked the window to stay open so leave it open and enable the open and open new folder buttons
setupPostProgressUI();

View File

@@ -57,6 +57,7 @@ Contributor(s):
<data id="dialog.strings.longTimeFormat">&longTimeFormat;</data>
<data id="dialog.strings.unknownTime">&unknownTime;</data>
<data id="dialog.strings.pausedMsg">&pausedMsg;</data>
<data id="dialog.strings.filesFolder">&filesFolder;</data>
<grid flex="1">
<columns>

View File

@@ -85,3 +85,11 @@
<!ENTITY keepProgressDialogUpMsg.label "Keep this window open after the download is complete.">
<!ENTITY openFolder.label "Reveal Location">
<!ENTITY open.label "Launch File">
<!-- LOCALIZATION NOTE (filesFolder):
This is the name of the folder that is created parallel to a HTML file
when it is saved "With Images". The ^BASE^ section is replaced with the
leaf name of the file being saved (minus extension). -->
<!ENTITY filesFolder "^BASE^_files">