!import
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1 //@line 40 "/home/visbrero/mnt/roisin/rev_control/hg/mozilla/toolkit/content/contentAreaUtils.js"
2
3 /**
4 * urlSecurityCheck: JavaScript wrapper for checkLoadURIWithPrincipal
5 * and checkLoadURIStrWithPrincipal.
6 * If |aPrincipal| is not allowed to link to |aURL|, this function throws with
7 * an error message.
8 *
9 * @param aURL
10 * The URL a page has linked to. This could be passed either as a string
11 * or as a nsIURI object.
12 * @param aPrincipal
13 * The principal of the document from which aURL came.
14 * @param aFlags
15 * Flags to be passed to checkLoadURIStr. If undefined,
16 * nsIScriptSecurityManager.STANDARD will be passed.
17 */
urlSecurityCheck
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
18 function urlSecurityCheck(aURL, aPrincipal, aFlags)
19 {
20 const nsIScriptSecurityManager =
21 Components.interfaces.nsIScriptSecurityManager;
22 var secMan = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
23 .getService(nsIScriptSecurityManager);
24 if (aFlags === undefined)
25 aFlags = nsIScriptSecurityManager.STANDARD;
26
27 try {
28 if (aURL instanceof Components.interfaces.nsIURI)
29 secMan.checkLoadURIWithPrincipal(aPrincipal, aURL, aFlags);
30 else
31 secMan.checkLoadURIStrWithPrincipal(aPrincipal, aURL, aFlags);
32 } catch (e) {
33 // XXXmano: dump the principal url here too
34 throw "Load of " + aURL + " denied.";
35 }
36 }
37
38 /**
39 * Determine whether or not a given focused DOMWindow is in the content area.
40 **/
isContentFrame
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
41 function isContentFrame(aFocusedWindow)
42 {
43 if (!aFocusedWindow)
44 return false;
45
46 return (aFocusedWindow.top == window.content);
47 }
48
49
50 // Clientelle: (Make sure you don't break any of these)
51 // - File -> Save Page/Frame As...
52 // - Context -> Save Page/Frame As...
53 // - Context -> Save Link As...
54 // - Alt-Click links in web pages
55 // - Alt-Click links in the UI
56 //
57 // Try saving each of these types:
58 // - A complete webpage using File->Save Page As, and Context->Save Page As
59 // - A webpage as HTML only using the above methods
60 // - A webpage as Text only using the above methods
61 // - An image with an extension (e.g. .jpg) in its file name, using
62 // Context->Save Image As...
63 // - An image without an extension (e.g. a banner ad on cnn.com) using
64 // the above method.
65 // - A linked document using Save Link As...
66 // - A linked document using Alt-click Save Link As...
67 //
saveURL
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
68 function saveURL(aURL, aFileName, aFilePickerTitleKey, aShouldBypassCache,
69 aSkipPrompt, aReferrer)
70 {
71 internalSave(aURL, null, aFileName, null, null, aShouldBypassCache,
72 aFilePickerTitleKey, null, aReferrer, aSkipPrompt);
73 }
74
75 // Just like saveURL, but will get some info off the image before
76 // calling internalSave
77 // Clientelle: (Make sure you don't break any of these)
78 // - Context -> Save Image As...
79 const imgICache = Components.interfaces.imgICache;
80 const nsISupportsCString = Components.interfaces.nsISupportsCString;
81
saveImageURL
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
82 function saveImageURL(aURL, aFileName, aFilePickerTitleKey, aShouldBypassCache,
83 aSkipPrompt, aReferrer)
84 {
85 var contentType = null;
86 var contentDisposition = null;
87 if (!aShouldBypassCache) {
88 try {
89 var imageCache = Components.classes["@mozilla.org/image/cache;1"]
90 .getService(imgICache);
91 var props =
92 imageCache.findEntryProperties(makeURI(aURL, getCharsetforSave(null)));
93 if (props) {
94 contentType = props.get("type", nsISupportsCString);
95 contentDisposition = props.get("content-disposition",
96 nsISupportsCString);
97 }
98 } catch (e) {
99 // Failure to get type and content-disposition off the image is non-fatal
100 }
101 }
102 internalSave(aURL, null, aFileName, contentDisposition, contentType,
103 aShouldBypassCache, aFilePickerTitleKey, null, aReferrer, aSkipPrompt);
104 }
105
saveFrameDocument
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
106 function saveFrameDocument()
107 {
108 var focusedWindow = document.commandDispatcher.focusedWindow;
109 if (isContentFrame(focusedWindow))
110 saveDocument(focusedWindow.document);
111 }
112
saveDocument
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
113 function saveDocument(aDocument, aSkipPrompt)
114 {
115 if (!aDocument)
116 throw "Must have a document when calling saveDocument";
117
118 // We want to use cached data because the document is currently visible.
119 var contentDisposition = null;
120 try {
121 contentDisposition =
122 aDocument.defaultView
123 .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
124 .getInterface(Components.interfaces.nsIDOMWindowUtils)
125 .getDocumentMetadata("content-disposition");
126 } catch (ex) {
127 // Failure to get a content-disposition is ok
128 }
129 internalSave(aDocument.location.href, aDocument, null, contentDisposition,
130 aDocument.contentType, false, null, null,
131 aDocument.referrer ? makeURI(aDocument.referrer) : null,
132 aSkipPrompt);
133 }
134
DownloadListener
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
135 function DownloadListener(win, transfer) {
makeClosure
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
136 function makeClosure(name) {
anon:137:11
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
137 return function() {
138 transfer[name].apply(transfer, arguments);
139 }
140 }
141
142 this.window = win;
143
144 // Now... we need to forward all calls to our transfer
145 for (var i in transfer) {
146 if (i != "QueryInterface")
147 this[i] = makeClosure(i);
148 }
149 }
150
151 DownloadListener.prototype = {
dl_qi
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
152 QueryInterface: function dl_qi(aIID)
153 {
154 if (aIID.equals(Components.interfaces.nsIInterfaceRequestor) ||
155 aIID.equals(Components.interfaces.nsIWebProgressListener) ||
156 aIID.equals(Components.interfaces.nsIWebProgressListener2) ||
157 aIID.equals(Components.interfaces.nsISupports)) {
158 return this;
159 }
160 throw Components.results.NS_ERROR_NO_INTERFACE;
161 },
162
dl_gi
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
163 getInterface: function dl_gi(aIID)
164 {
165 if (aIID.equals(Components.interfaces.nsIAuthPrompt) ||
166 aIID.equals(Components.interfaces.nsIAuthPrompt2)) {
167 var ww =
168 Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
169 .getService(Components.interfaces.nsIPromptFactory);
170 return ww.getPrompt(this.window, aIID);
171 }
172
173 throw Components.results.NS_ERROR_NO_INTERFACE;
174 }
175 }
176
177 const kSaveAsType_Complete = 0; // Save document with attached objects.
178 // const kSaveAsType_URL = 1; // Save document or URL by itself.
179 const kSaveAsType_Text = 2; // Save document, converting to plain text.
180
181 /**
182 * internalSave: Used when saving a document or URL. This method:
183 * - Determines a local target filename to use (unless parameter
184 * aChosenData is non-null)
185 * - Determines content-type if possible
186 * - Prompts the user to confirm the destination filename and save mode
187 * (content-type affects this)
188 * - Creates a 'Persist' object (which will perform the saving in the
189 * background) and then starts it.
190 *
191 * @param aURL The String representation of the URL of the document being saved
192 * @param aDocument The document to be saved
193 * @param aDefaultFileName The caller-provided suggested filename if we don't
194 * find a better one
195 * @param aContentDisposition The caller-provided content-disposition header
196 * to use.
197 * @param aContentType The caller-provided content-type to use
198 * @param aShouldBypassCache If true, the document will always be refetched
199 * from the server
200 * @param aFilePickerTitleKey Alternate title for the file picker
201 * @param aChosenData If non-null this contains an instance of object AutoChosen
202 * (see below) which holds pre-determined data so that the user does not
203 * need to be prompted for a target filename.
204 * @param aReferrer the referrer URI object (not URL string) to use, or null
205 if no referrer should be sent.
206 * @param aSkipPrompt If true, the file will be saved to the default download folder.
207 */
internalSave
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
208 function internalSave(aURL, aDocument, aDefaultFileName, aContentDisposition,
209 aContentType, aShouldBypassCache, aFilePickerTitleKey,
210 aChosenData, aReferrer, aSkipPrompt)
211 {
212 if (aSkipPrompt == undefined)
213 aSkipPrompt = false;
214
215 // Note: aDocument == null when this code is used by save-link-as...
216 var saveMode = GetSaveModeForContentType(aContentType);
217 var isDocument = aDocument != null && saveMode != SAVEMODE_FILEONLY;
218 var saveAsType = kSaveAsType_Complete;
219
220 var file, fileURL;
221 // Find the URI object for aURL and the FileName/Extension to use when saving.
222 // FileName/Extension will be ignored if aChosenData supplied.
223 var fileInfo = new FileInfo(aDefaultFileName);
224 if (aChosenData)
225 file = aChosenData.file;
226 else {
227 var charset = null;
228 if (aDocument)
229 charset = aDocument.characterSet;
230 else if (aReferrer)
231 charset = aReferrer.originCharset;
232 initFileInfo(fileInfo, aURL, charset, aDocument,
233 aContentType, aContentDisposition);
234 var fpParams = {
235 fpTitleKey: aFilePickerTitleKey,
236 isDocument: isDocument,
237 fileInfo: fileInfo,
238 contentType: aContentType,
239 saveMode: saveMode,
240 saveAsType: saveAsType,
241 file: file,
242 fileURL: fileURL
243 };
244
245 if (!getTargetFile(fpParams, aSkipPrompt))
246 // If the method returned false this is because the user cancelled from
247 // the save file picker dialog.
248 return;
249
250 saveAsType = fpParams.saveAsType;
251 saveMode = fpParams.saveMode;
252 file = fpParams.file;
253 fileURL = fpParams.fileURL;
254 }
255
256 if (!fileURL)
257 fileURL = makeFileURI(file);
258
259 // XXX We depend on the following holding true in appendFiltersForContentType():
260 // If we should save as a complete page, the saveAsType is kSaveAsType_Complete.
261 // If we should save as text, the saveAsType is kSaveAsType_Text.
262 var useSaveDocument = isDocument &&
263 (((saveMode & SAVEMODE_COMPLETE_DOM) && (saveAsType == kSaveAsType_Complete)) ||
264 ((saveMode & SAVEMODE_COMPLETE_TEXT) && (saveAsType == kSaveAsType_Text)));
265 // If we're saving a document, and are saving either in complete mode or
266 // as converted text, pass the document to the web browser persist component.
267 // If we're just saving the HTML (second option in the list), send only the URI.
268 var source = useSaveDocument ? aDocument : fileInfo.uri;
269 var persistArgs = {
270 source : source,
271 contentType : (!aChosenData && useSaveDocument &&
272 saveAsType == kSaveAsType_Text) ?
273 "text/plain" : null,
274 target : fileURL,
275 postData : isDocument ? getPostData() : null,
276 bypassCache : aShouldBypassCache
277 };
278
279 var persist = makeWebBrowserPersist();
280
281 // Calculate persist flags.
282 const nsIWBP = Components.interfaces.nsIWebBrowserPersist;
283 const flags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
284 if (aShouldBypassCache)
285 persist.persistFlags = flags | nsIWBP.PERSIST_FLAGS_BYPASS_CACHE;
286 else
287 persist.persistFlags = flags | nsIWBP.PERSIST_FLAGS_FROM_CACHE;
288
289 // Leave it to WebBrowserPersist to discover the encoding type (or lack thereof):
290 persist.persistFlags |= nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
291
292 // Create download and initiate it (below)
293 var tr = Components.classes["@mozilla.org/transfer;1"].createInstance(Components.interfaces.nsITransfer);
294
295 if (useSaveDocument) {
296 // Saving a Document, not a URI:
297 var filesFolder = null;
298 if (persistArgs.contentType != "text/plain") {
299 // Create the local directory into which to save associated files.
300 filesFolder = file.clone();
301
302 var nameWithoutExtension = getFileBaseName(filesFolder.leafName);
303 var filesFolderLeafName = getStringBundle().formatStringFromName("filesFolder",
304 [nameWithoutExtension],
305 1);
306
307 filesFolder.leafName = filesFolderLeafName;
308 }
309
310 var encodingFlags = 0;
311 if (persistArgs.contentType == "text/plain") {
312 encodingFlags |= nsIWBP.ENCODE_FLAGS_FORMATTED;
313 encodingFlags |= nsIWBP.ENCODE_FLAGS_ABSOLUTE_LINKS;
314 encodingFlags |= nsIWBP.ENCODE_FLAGS_NOFRAMES_CONTENT;
315 }
316 else {
317 encodingFlags |= nsIWBP.ENCODE_FLAGS_ENCODE_BASIC_ENTITIES;
318 }
319
320 const kWrapColumn = 80;
321 tr.init((aChosenData ? aChosenData.uri : fileInfo.uri),
322 persistArgs.target, "", null, null, null, persist);
323 persist.progressListener = new DownloadListener(window, tr);
324 persist.saveDocument(persistArgs.source, persistArgs.target, filesFolder,
325 persistArgs.contentType, encodingFlags, kWrapColumn);
326 } else {
327 tr.init((aChosenData ? aChosenData.uri : source),
328 persistArgs.target, "", null, null, null, persist);
329 persist.progressListener = new DownloadListener(window, tr);
330 persist.saveURI((aChosenData ? aChosenData.uri : source),
331 null, aReferrer, persistArgs.postData, null,
332 persistArgs.target);
333 }
334 }
335
336 /**
337 * Structure for holding info about automatically supplied parameters for
338 * internalSave(...). This allows parameters to be supplied so the user does not
339 * need to be prompted for file info.
340 * @param aFileAutoChosen This is an nsILocalFile object that has been
341 * pre-determined as the filename for the target to save to
342 * @param aUriAutoChosen This is the nsIURI object for the target
343 */
AutoChosen
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
344 function AutoChosen(aFileAutoChosen, aUriAutoChosen) {
345 this.file = aFileAutoChosen;
346 this.uri = aUriAutoChosen;
347 }
348
349 /**
350 * Structure for holding info about a URL and the target filename it should be
351 * saved to. This structure is populated by initFileInfo(...).
352 * @param aSuggestedFileName This is used by initFileInfo(...) when it
353 * cannot 'discover' the filename from the url
354 * @param aFileName The target filename
355 * @param aFileBaseName The filename without the file extension
356 * @param aFileExt The extension of the filename
357 * @param aUri An nsIURI object for the url that is being saved
358 */
FileInfo
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
359 function FileInfo(aSuggestedFileName, aFileName, aFileBaseName, aFileExt, aUri) {
360 this.suggestedFileName = aSuggestedFileName;
361 this.fileName = aFileName;
362 this.fileBaseName = aFileBaseName;
363 this.fileExt = aFileExt;
364 this.uri = aUri;
365 }
366
367 /**
368 * Determine what the 'default' filename string is, its file extension and the
369 * filename without the extension. This filename is used when prompting the user
370 * for confirmation in the file picker dialog.
371 * @param aFI A FileInfo structure into which we'll put the results of this method.
372 * @param aURL The String representation of the URL of the document being saved
373 * @param aURLCharset The charset of aURL.
374 * @param aDocument The document to be saved
375 * @param aContentType The content type we're saving, if it could be
376 * determined by the caller.
377 * @param aContentDisposition The content-disposition header for the object
378 * we're saving, if it could be determined by the caller.
379 */
initFileInfo
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
380 function initFileInfo(aFI, aURL, aURLCharset, aDocument,
381 aContentType, aContentDisposition)
382 {
383 try {
384 // Get an nsIURI object from aURL if possible:
385 try {
386 aFI.uri = makeURI(aURL, aURLCharset);
387 // Assuming nsiUri is valid, calling QueryInterface(...) on it will
388 // populate extra object fields (eg filename and file extension).
389 var url = aFI.uri.QueryInterface(Components.interfaces.nsIURL);
390 aFI.fileExt = url.fileExtension;
391 } catch (e) {
392 }
393
394 // Get the default filename:
395 aFI.fileName = getDefaultFileName((aFI.suggestedFileName || aFI.fileName),
396 aFI.uri, aDocument, aContentDisposition);
397 // If aFI.fileExt is still blank, consider: aFI.suggestedFileName is supplied
398 // if saveURL(...) was the original caller (hence both aContentType and
399 // aDocument are blank). If they were saving a link to a website then make
400 // the extension .htm .
401 if (!aFI.fileExt && !aDocument && !aContentType && (/^http(s?):\/\//i.test(aURL))) {
402 aFI.fileExt = "htm";
403 aFI.fileBaseName = aFI.fileName;
404 } else {
405 aFI.fileExt = getDefaultExtension(aFI.fileName, aFI.uri, aContentType);
406 aFI.fileBaseName = getFileBaseName(aFI.fileName);
407 }
408 } catch (e) {
409 }
410 }
411
getTargetFile
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
412 function getTargetFile(aFpP, aSkipPrompt)
413 {
414 const prefSvcContractID = "@mozilla.org/preferences-service;1";
415 const prefSvcIID = Components.interfaces.nsIPrefService;
416 var prefs = Components.classes[prefSvcContractID]
417 .getService(prefSvcIID).getBranch("browser.download.");
418
419 const nsILocalFile = Components.interfaces.nsILocalFile;
420
421 // For information on download folder preferences, see
422 // mozilla/browser/components/preferences/main.js
423
424 var useDownloadDir = prefs.getBoolPref("useDownloadDir");
425 var dir = null;
426
427 // Default to lastDir if useDownloadDir is false, and lastDir
428 // is configured and valid. Otherwise, use the user's default
429 // downloads directory configured through download prefs.
430 var dnldMgr = Components.classes["@mozilla.org/download-manager;1"]
431 .getService(Components.interfaces.nsIDownloadManager);
432 try {
433 var lastDir = prefs.getComplexValue("lastDir", nsILocalFile);
434 if ((!aSkipPrompt || !useDownloadDir) && lastDir.exists())
435 dir = lastDir;
436 else
437 dir = dnldMgr.userDownloadsDirectory;
438 } catch(ex) {
439 dir = dnldMgr.userDownloadsDirectory;
440 }
441
442 if (!aSkipPrompt || !useDownloadDir || !dir || (dir && !dir.exists())) {
443 if (!dir || (dir && !dir.exists())) {
444 // Default to desktop.
445 var fileLocator = Components.classes["@mozilla.org/file/directory_service;1"]
446 .getService(Components.interfaces.nsIProperties);
447 dir = fileLocator.get("Desk", nsILocalFile);
448 }
449
450 var fp = makeFilePicker();
451 var titleKey = aFpP.fpTitleKey || "SaveLinkTitle";
452 var bundle = getStringBundle();
453 fp.init(window, bundle.GetStringFromName(titleKey),
454 Components.interfaces.nsIFilePicker.modeSave);
455
456 fp.defaultExtension = aFpP.fileInfo.fileExt;
457 fp.defaultString = getNormalizedLeafName(aFpP.fileInfo.fileName,
458 aFpP.fileInfo.fileExt);
459 appendFiltersForContentType(fp, aFpP.contentType, aFpP.fileInfo.fileExt,
460 aFpP.saveMode);
461
462 if (dir)
463 fp.displayDirectory = dir;
464
465 if (aFpP.isDocument) {
466 try {
467 fp.filterIndex = prefs.getIntPref("save_converter_index");
468 }
469 catch (e) {
470 }
471 }
472
473 if (fp.show() == Components.interfaces.nsIFilePicker.returnCancel || !fp.file)
474 return false;
475
476 var directory = fp.file.parent.QueryInterface(nsILocalFile);
477 prefs.setComplexValue("lastDir", nsILocalFile, directory);
478
479 fp.file.leafName = validateFileName(fp.file.leafName);
480 aFpP.saveAsType = fp.filterIndex;
481 aFpP.file = fp.file;
482 aFpP.fileURL = fp.fileURL;
483
484 if (aFpP.isDocument)
485 prefs.setIntPref("save_converter_index", aFpP.saveAsType);
486 }
487 else {
488 dir.append(getNormalizedLeafName(aFpP.fileInfo.fileName,
489 aFpP.fileInfo.fileExt));
490 var file = dir;
491
492 // Since we're automatically downloading, we don't get the file picker's
493 // logic to check for existing files, so we need to do that here.
494 //
495 // Note - this code is identical to that in
496 // mozilla/toolkit/mozapps/downloads/src/nsHelperAppDlg.js.in
497 // If you are updating this code, update that code too! We can't share code
498 // here since that code is called in a js component.
499 var collisionCount = 0;
500 while (file.exists()) {
501 collisionCount++;
502 if (collisionCount == 1) {
503 // Append "(2)" before the last dot in (or at the end of) the filename
504 // special case .ext.gz etc files so we don't wind up with .tar(2).gz
505 if (file.leafName.match(/\.[^\.]{1,3}\.(gz|bz2|Z)$/i))
506 file.leafName = file.leafName.replace(/\.[^\.]{1,3}\.(gz|bz2|Z)$/i, "(2)$&");
507 else
508 file.leafName = file.leafName.replace(/(\.[^\.]*)?$/, "(2)$&");
509 }
510 else {
511 // replace the last (n) in the filename with (n+1)
512 file.leafName = file.leafName.replace(/^(.*\()\d+\)/, "$1" + (collisionCount+1) + ")");
513 }
514 }
515 aFpP.file = file;
516 }
517
518 return true;
519 }
520
521 // We have no DOM, and can only save the URL as is.
522 const SAVEMODE_FILEONLY = 0x00;
523 // We have a DOM and can save as complete.
524 const SAVEMODE_COMPLETE_DOM = 0x01;
525 // We have a DOM which we can serialize as text.
526 const SAVEMODE_COMPLETE_TEXT = 0x02;
527
528 // If we are able to save a complete DOM, the 'save as complete' filter
529 // must be the first filter appended. The 'save page only' counterpart
530 // must be the second filter appended. And the 'save as complete text'
531 // filter must be the third filter appended.
appendFiltersForContentType
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
532 function appendFiltersForContentType(aFilePicker, aContentType, aFileExtension, aSaveMode)
533 {
534 var bundle = getStringBundle();
535 // The bundle name for saving only a specific content type.
536 var bundleName;
537 // The corresponding filter string for a specific content type.
538 var filterString;
539
540 // XXX all the cases that are handled explicitly here MUST be handled
541 // in GetSaveModeForContentType to return a non-fileonly filter.
542 switch (aContentType) {
543 case "text/html":
544 bundleName = "WebPageHTMLOnlyFilter";
545 filterString = "*.htm; *.html";
546 break;
547
548 case "application/xhtml+xml":
549 bundleName = "WebPageXHTMLOnlyFilter";
550 filterString = "*.xht; *.xhtml";
551 break;
552
553 case "image/svg+xml":
554 bundleName = "WebPageSVGOnlyFilter";
555 filterString = "*.svg; *.svgz";
556 break;
557
558 case "text/xml":
559 case "application/xml":
560 bundleName = "WebPageXMLOnlyFilter";
561 filterString = "*.xml";
562 break;
563
564 default:
565 if (aSaveMode != SAVEMODE_FILEONLY)
566 throw "Invalid save mode for type '" + aContentType + "'";
567
568 var mimeInfo = getMIMEInfoForType(aContentType, aFileExtension);
569 if (mimeInfo) {
570
571 var extEnumerator = mimeInfo.getFileExtensions();
572
573 var extString = "";
574 while (extEnumerator.hasMore()) {
575 var extension = extEnumerator.getNext();
576 if (extString)
577 extString += "; "; // If adding more than one extension,
578 // separate by semi-colon
579 extString += "*." + extension;
580 }
581
582 if (extString)
583 aFilePicker.appendFilter(mimeInfo.description, extString);
584 }
585
586 break;
587 }
588
589 if (aSaveMode & SAVEMODE_COMPLETE_DOM) {
590 aFilePicker.appendFilter(bundle.GetStringFromName("WebPageCompleteFilter"), filterString);
591 // We should always offer a choice to save document only if
592 // we allow saving as complete.
593 aFilePicker.appendFilter(bundle.GetStringFromName(bundleName), filterString);
594 }
595
596 if (aSaveMode & SAVEMODE_COMPLETE_TEXT)
597 aFilePicker.appendFilters(Components.interfaces.nsIFilePicker.filterText);
598
599 // Always append the all files (*) filter
600 aFilePicker.appendFilters(Components.interfaces.nsIFilePicker.filterAll);
601 }
602
getPostData
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
603 function getPostData()
604 {
605 try {
606 var sessionHistory = getWebNavigation().sessionHistory;
607 return sessionHistory.getEntryAtIndex(sessionHistory.index, false)
608 .QueryInterface(Components.interfaces.nsISHEntry)
609 .postData;
610 }
611 catch (e) {
612 }
613 return null;
614 }
615
getStringBundle
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
616 function getStringBundle()
617 {
618 const bundleURL = "chrome://global/locale/contentAreaCommands.properties";
619
620 const sbsContractID = "@mozilla.org/intl/stringbundle;1";
621 const sbsIID = Components.interfaces.nsIStringBundleService;
622 const sbs = Components.classes[sbsContractID].getService(sbsIID);
623
624 const lsContractID = "@mozilla.org/intl/nslocaleservice;1";
625 const lsIID = Components.interfaces.nsILocaleService;
626 const ls = Components.classes[lsContractID].getService(lsIID);
627 var appLocale = ls.getApplicationLocale();
628 return sbs.createBundle(bundleURL, appLocale);
629 }
630
makeWebBrowserPersist
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
631 function makeWebBrowserPersist()
632 {
633 const persistContractID = "@mozilla.org/embedding/browser/nsWebBrowserPersist;1";
634 const persistIID = Components.interfaces.nsIWebBrowserPersist;
635 return Components.classes[persistContractID].createInstance(persistIID);
636 }
637
638 /**
639 * Constructs a new URI, using nsIIOService.
640 * @param aURL The URI spec.
641 * @param aOriginCharset The charset of the URI.
642 * @param aBaseURI Base URI to resolve aURL, or null.
643 * @return an nsIURI object based on aURL.
644 */
makeURI
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
645 function makeURI(aURL, aOriginCharset, aBaseURI)
646 {
647 var ioService = Components.classes["@mozilla.org/network/io-service;1"]
648 .getService(Components.interfaces.nsIIOService);
649 return ioService.newURI(aURL, aOriginCharset, aBaseURI);
650 }
651
makeFileURI
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
652 function makeFileURI(aFile)
653 {
654 var ioService = Components.classes["@mozilla.org/network/io-service;1"]
655 .getService(Components.interfaces.nsIIOService);
656 return ioService.newFileURI(aFile);
657 }
658
makeFilePicker
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
659 function makeFilePicker()
660 {
661 const fpContractID = "@mozilla.org/filepicker;1";
662 const fpIID = Components.interfaces.nsIFilePicker;
663 return Components.classes[fpContractID].createInstance(fpIID);
664 }
665
getMIMEService
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
666 function getMIMEService()
667 {
668 const mimeSvcContractID = "@mozilla.org/mime;1";
669 const mimeSvcIID = Components.interfaces.nsIMIMEService;
670 const mimeSvc = Components.classes[mimeSvcContractID].getService(mimeSvcIID);
671 return mimeSvc;
672 }
673
674 // Given aFileName, find the fileName without the extension on the end.
getFileBaseName
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
675 function getFileBaseName(aFileName)
676 {
677 // Remove the file extension from aFileName:
678 return aFileName.replace(/\.[^.]*$/, "");
679 }
680
getMIMETypeForURI
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
681 function getMIMETypeForURI(aURI)
682 {
683 try {
684 return getMIMEService().getTypeFromURI(aURI);
685 }
686 catch (e) {
687 }
688 return null;
689 }
690
getMIMEInfoForType
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
691 function getMIMEInfoForType(aMIMEType, aExtension)
692 {
693 if (aMIMEType || aExtension) {
694 try {
695 return getMIMEService().getFromTypeAndExtension(aMIMEType, aExtension);
696 }
697 catch (e) {
698 }
699 }
700 return null;
701 }
702
getDefaultFileName
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
703 function getDefaultFileName(aDefaultFileName, aURI, aDocument,
704 aContentDisposition)
705 {
706 // 1) look for a filename in the content-disposition header, if any
707 if (aContentDisposition) {
708 const mhpContractID = "@mozilla.org/network/mime-hdrparam;1";
709 const mhpIID = Components.interfaces.nsIMIMEHeaderParam;
710 const mhp = Components.classes[mhpContractID].getService(mhpIID);
711 var dummy = { value: null }; // Need an out param...
712 var charset = getCharsetforSave(aDocument);
713
714 var fileName = null;
715 try {
716 fileName = mhp.getParameter(aContentDisposition, "filename", charset,
717 true, dummy);
718 }
719 catch (e) {
720 try {
721 fileName = mhp.getParameter(aContentDisposition, "name", charset, true,
722 dummy);
723 }
724 catch (e) {
725 }
726 }
727 if (fileName)
728 return fileName;
729 }
730
731 try {
732 var url = aURI.QueryInterface(Components.interfaces.nsIURL);
733 if (url.fileName != "") {
734 // 2) Use the actual file name, if present
735 var textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
736 .getService(Components.interfaces.nsITextToSubURI);
737 return validateFileName(textToSubURI.unEscapeURIForUI(url.originCharset || "UTF-8", url.fileName));
738 }
739 } catch (e) {
740 // This is something like a data: and so forth URI... no filename here.
741 }
742
743 if (aDocument) {
744 var docTitle = validateFileName(aDocument.title).replace(/^\s+|\s+$/g, "");
745 if (docTitle) {
746 // 3) Use the document title
747 return docTitle;
748 }
749 }
750
751 if (aDefaultFileName)
752 // 4) Use the caller-provided name, if any
753 return validateFileName(aDefaultFileName);
754
755 // 5) If this is a directory, use the last directory name
756 var path = aURI.path.match(/\/([^\/]+)\/$/);
757 if (path && path.length > 1)
758 return validateFileName(path[1]);
759
760 try {
761 if (aURI.host)
762 // 6) Use the host.
763 return aURI.host;
764 } catch (e) {
765 // Some files have no information at all, like Javascript generated pages
766 }
767 try {
768 // 7) Use the default file name
769 return getStringBundle().GetStringFromName("DefaultSaveFileName");
770 } catch (e) {
771 //in case localized string cannot be found
772 }
773 // 8) If all else fails, use "index"
774 return "index";
775 }
776
validateFileName
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
777 function validateFileName(aFileName)
778 {
779 var re = /[\/]+/g;
780 if (navigator.appVersion.indexOf("Windows") != -1) {
781 re = /[\\\/\|]+/g;
782 aFileName = aFileName.replace(/[\"]+/g, "'");
783 aFileName = aFileName.replace(/[\*\:\?]+/g, " ");
784 aFileName = aFileName.replace(/[\<]+/g, "(");
785 aFileName = aFileName.replace(/[\>]+/g, ")");
786 }
787 else if (navigator.appVersion.indexOf("Macintosh") != -1)
788 re = /[\:\/]+/g;
789
790 return aFileName.replace(re, "_");
791 }
792
getNormalizedLeafName
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
793 function getNormalizedLeafName(aFile, aDefaultExtension)
794 {
795 if (!aDefaultExtension)
796 return aFile;
797
798 //@line 840 "/home/visbrero/mnt/roisin/rev_control/hg/mozilla/toolkit/content/contentAreaUtils.js"
799
800 // Remove leading dots
801 aFile = aFile.replace(/^\.+/, "");
802
803 // Fix up the file name we're saving to to include the default extension
804 var i = aFile.lastIndexOf(".");
805 if (aFile.substr(i + 1) != aDefaultExtension)
806 return aFile + "." + aDefaultExtension;
807
808 return aFile;
809 }
810
getDefaultExtension
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
811 function getDefaultExtension(aFilename, aURI, aContentType)
812 {
813 if (aContentType == "text/plain" || aContentType == "application/octet-stream" || aURI.scheme == "ftp")
814 return ""; // temporary fix for bug 120327
815
816 // First try the extension from the filename
817 const stdURLContractID = "@mozilla.org/network/standard-url;1";
818 const stdURLIID = Components.interfaces.nsIURL;
819 var url = Components.classes[stdURLContractID].createInstance(stdURLIID);
820 url.filePath = aFilename;
821
822 var ext = url.fileExtension;
823
824 // This mirrors some code in nsExternalHelperAppService::DoContent
825 // Use the filename first and then the URI if that fails
826
827 var mimeInfo = getMIMEInfoForType(aContentType, ext);
828
829 if (ext && mimeInfo && mimeInfo.extensionExists(ext))
830 return ext;
831
832 // Well, that failed. Now try the extension from the URI
833 var urlext;
834 try {
835 url = aURI.QueryInterface(Components.interfaces.nsIURL);
836 urlext = url.fileExtension;
837 } catch (e) {
838 }
839
840 if (urlext && mimeInfo && mimeInfo.extensionExists(urlext)) {
841 return urlext;
842 }
843 else {
844 try {
845 if (mimeInfo)
846 return mimeInfo.primaryExtension;
847 }
848 catch (e) {
849 }
850 // Fall back on the extensions in the filename and URI for lack
851 // of anything better.
852 return ext || urlext;
853 }
854 }
855
GetSaveModeForContentType
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
856 function GetSaveModeForContentType(aContentType)
857 {
858 var saveMode = SAVEMODE_FILEONLY;
859 switch (aContentType) {
860 case "text/html":
861 case "application/xhtml+xml":
862 case "image/svg+xml":
863 saveMode |= SAVEMODE_COMPLETE_TEXT;
864 // Fall through
865 case "text/xml":
866 case "application/xml":
867 saveMode |= SAVEMODE_COMPLETE_DOM;
868 break;
869 }
870
871 return saveMode;
872 }
873
getCharsetforSave
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
874 function getCharsetforSave(aDocument)
875 {
876 if (aDocument)
877 return aDocument.characterSet;
878
879 if (document.commandDispatcher.focusedWindow)
880 return document.commandDispatcher.focusedWindow.document.characterSet;
881
882 return window.content.document.characterSet;
883 }
884
885 /**
886 * Open a URL from chrome, determining if we can handle it internally or need to
887 * launch an external application to handle it.
888 * @param aURL The URL to be opened
889 */
openURL
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
890 function openURL(aURL)
891 {
892 var ios = Components.classes["@mozilla.org/network/io-service;1"]
893 .getService(Components.interfaces.nsIIOService);
894 var uri = ios.newURI(aURL, null, null);
895
896 var protocolSvc = Components.classes["@mozilla.org/uriloader/external-protocol-service;1"]
897 .getService(Components.interfaces.nsIExternalProtocolService);
898
899 if (!protocolSvc.isExposedProtocol(uri.scheme)) {
900 // If we're not a browser, use the external protocol service to load the URI.
901 protocolSvc.loadUrl(uri);
902 }
903 else {
904 var loadgroup = Components.classes["@mozilla.org/network/load-group;1"]
905 .createInstance(Components.interfaces.nsILoadGroup);
906 var appstartup = Components.classes["@mozilla.org/toolkit/app-startup;1"]
907 .getService(Components.interfaces.nsIAppStartup);
908
909 var loadListener = {
ll_start
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
910 onStartRequest: function ll_start(aRequest, aContext) {
911 appstartup.enterLastWindowClosingSurvivalArea();
912 },
ll_stop
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
913 onStopRequest: function ll_stop(aRequest, aContext, aStatusCode) {
914 appstartup.exitLastWindowClosingSurvivalArea();
915 },
ll_QI
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
916 QueryInterface: function ll_QI(iid) {
917 if (iid.equals(Components.interfaces.nsISupports) ||
918 iid.equals(Components.interfaces.nsIRequestObserver) ||
919 iid.equals(Components.interfaces.nsISupportsWeakReference))
920 return this;
921 throw Components.results.NS_ERROR_NO_INTERFACE;
922 }
923 }
924 loadgroup.groupObserver = loadListener;
925
926 var uriListener = {
onStartURIOpen
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
927 onStartURIOpen: function(uri) { return false; },
doContent
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
928 doContent: function(ctype, preferred, request, handler) { return false; },
isPreferred
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
929 isPreferred: function(ctype, desired) { return false; },
canHandleContent
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
930 canHandleContent: function(ctype, preferred, desired) { return false; },
931 loadCookie: null,
932 parentContentListener: null,
getInterface
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
933 getInterface: function(iid) {
934 if (iid.equals(Components.interfaces.nsIURIContentListener))
935 return this;
936 if (iid.equals(Components.interfaces.nsILoadGroup))
937 return loadgroup;
938 throw Components.results.NS_ERROR_NO_INTERFACE;
939 }
940 }
941
942 var channel = ios.newChannelFromURI(uri);
943 var uriLoader = Components.classes["@mozilla.org/uriloader;1"]
944 .getService(Components.interfaces.nsIURILoader);
945 uriLoader.openURI(channel, true, uriListener);
946 }
947 }