1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769
|
# -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
# 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/.
/*
* Core mail routines used by all of the major mail windows (address book,
* 3-pane, compose and stand alone message window).
* Routines to support custom toolbars in mail windows, opening up a new window
* of a particular type all live here.
* Before adding to this file, ask yourself, is this a JS routine that is going
* to be used by all of the main mail windows?
*/
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource:///modules/mailServices.js");
var gCustomizeSheet = false;
function overlayRestoreDefaultSet() {
let toolbox = null;
if ("arguments" in window && window.arguments[0])
toolbox = window.arguments[0];
else if (window.frameElement && "toolbox" in window.frameElement)
toolbox = window.frameElement.toolbox;
let mode = toolbox.getAttribute("defaultmode");
let align = toolbox.getAttribute("defaultlabelalign");
let menulist = document.getElementById("modelist");
if (mode == "full" && align == "end") {
toolbox.setAttribute("mode", "textbesideicon");
toolbox.setAttribute("labelalign", align);
overlayUpdateToolbarMode("textbesideicon");
}
else if (mode == "full" && align == ""){
toolbox.setAttribute("mode", "full");
toolbox.removeAttribute("labelalign");
overlayUpdateToolbarMode(mode);
}
restoreDefaultSet();
if (mode == "full" && align == "end") {
menulist.value = "textbesideicon";
}
}
function overlayUpdateToolbarMode(aModeValue)
{
let toolbox = null;
if ("arguments" in window && window.arguments[0])
toolbox = window.arguments[0];
else if (window.frameElement && "toolbox" in window.frameElement)
toolbox = window.frameElement.toolbox;
// If they chose a mode of textbesideicon or full,
// then map that to a mode of full, and a labelalign of true or false.
if( aModeValue == "textbesideicon" || aModeValue == "full") {
var align = aModeValue == "textbesideicon" ? "end" : "bottom";
toolbox.setAttribute("labelalign", align);
toolbox.ownerDocument.persist(toolbox.id, "labelalign");
aModeValue = "full";
}
updateToolbarMode(aModeValue);
}
function overlayOnLoad()
{
let restoreButton = document.getElementById("main-box")
.querySelector("[oncommand*='restore']");
restoreButton.setAttribute("oncommand", "overlayRestoreDefaultSet();");
// Add the textBesideIcon menu item if it's not already there.
let menuitem = document.getElementById("textbesideiconItem");
if (!menuitem) {
let menulist = document.getElementById("modelist");
let label = document.getElementById("iconsBesideText.label")
.getAttribute("value");
menuitem = menulist.appendItem(label, "textbesideicon");
menuitem.id = "textbesideiconItem";
}
// If they have a mode of full and a labelalign of true,
// then pretend the mode is textbesideicon when populating the popup.
let toolbox = null;
if ("arguments" in window && window.arguments[0])
toolbox = window.arguments[0];
else if (window.frameElement && "toolbox" in window.frameElement)
toolbox = window.frameElement.toolbox;
let toolbarWindow = document.getElementById("CustomizeToolbarWindow");
toolbarWindow.setAttribute("toolboxId", toolbox.id);
if (toolbox.getAttribute("inlinetoolbox") == "true")
toolbarWindow.setAttribute("inlinetoolbox", "true");
toolbox.setAttribute("doCustomization", "true");
let mode = toolbox.getAttribute("mode");
let align = toolbox.getAttribute("labelalign");
if (mode == "full" && align == "end")
toolbox.setAttribute("mode", "textbesideicon");
onLoad();
overlayRepositionDialog();
// Re-set and re-persist the mode, if we changed it above.
if (mode == "full" && align == "end") {
toolbox.setAttribute("mode", mode);
toolbox.ownerDocument.persist(toolbox.id, "mode");
}
}
function overlayRepositionDialog()
{
// Position the dialog so it is fully visible on the screen
// (if possible)
// Seems to be necessary to get the correct dialog height/width
window.sizeToContent();
var wH = window.outerHeight;
var wW = window.outerWidth;
var sH = window.screen.height;
var sW = window.screen.width;
var sX = window.screenX;
var sY = window.screenY;
var sAL = window.screen.availLeft;
var sAT = window.screen.availTop;
var nX = Math.max(Math.min(sX, sW - wW), sAL);
var nY = Math.max(Math.min(sY, sH - wH), sAT);
window.moveTo(nX, nY);
}
function CustomizeMailToolbar(toolboxId, customizePopupId)
{
// Disable the toolbar context menu items
var menubar = document.getElementById("mail-menubar");
for (var i = 0; i < menubar.childNodes.length; ++i)
menubar.childNodes[i].setAttribute("disabled", true);
var customizePopup = document.getElementById(customizePopupId);
customizePopup.setAttribute("disabled", "true");
var toolbox = document.getElementById(toolboxId);
var customizeURL = "chrome://global/content/customizeToolbar.xul";
gCustomizeSheet = Services.prefs.getBoolPref("toolbar.customization.usesheet");
if (gCustomizeSheet) {
var sheetFrame = document.getElementById("customizeToolbarSheetIFrame");
var panel = document.getElementById("customizeToolbarSheetPopup");
sheetFrame.hidden = false;
sheetFrame.toolbox = toolbox;
sheetFrame.panel = panel;
// The document might not have been loaded yet, if this is the first time.
// If it is already loaded, reload it so that the onload intialization code
// re-runs.
if (sheetFrame.getAttribute("src") == customizeURL)
sheetFrame.contentWindow.location.reload()
else
sheetFrame.setAttribute("src", customizeURL);
// Open the panel, but make it invisible until the iframe has loaded so
// that the user doesn't see a white flash.
panel.style.visibility = "hidden";
toolbox.addEventListener("beforecustomization", function removeProp() {
toolbox.removeEventListener("beforecustomization", removeProp, false);
panel.style.removeProperty("visibility");
}, false);
panel.openPopup(toolbox, "after_start", 0, 0);
}
else {
var wintype = document.documentElement.getAttribute("windowtype");
wintype = wintype.replace(/:/g, "");
window.openDialog(customizeURL,
"CustomizeToolbar"+wintype,
"chrome,all,dependent", toolbox);
}
}
function MailToolboxCustomizeDone(aEvent, customizePopupId)
{
if (gCustomizeSheet) {
document.getElementById("customizeToolbarSheetIFrame").hidden = true;
document.getElementById("customizeToolbarSheetPopup").hidePopup();
}
// Update global UI elements that may have been added or removed
// Re-enable parts of the UI we disabled during the dialog
var menubar = document.getElementById("mail-menubar");
for (var i = 0; i < menubar.childNodes.length; ++i)
menubar.childNodes[i].setAttribute("disabled", false);
// make sure the mail views search box is initialized
if (document.getElementById("mailviews-container"))
ViewPickerOnLoad();
// make sure the folder location picker is initialized
if (document.getElementById("folder-location-container"))
FolderPaneSelectionChange();
var customizePopup = document.getElementById(customizePopupId);
customizePopup.removeAttribute("disabled");
// make sure our toolbar buttons have the correct enabled state restored to them...
if (this.UpdateMailToolbar != undefined)
UpdateMailToolbar(focus);
let toolbox = document.querySelector('[doCustomization="true"]');
if (toolbox) {
toolbox.removeAttribute("doCustomization");
// The GetMail button is stuck in a strange state right now, since the
// customization wrapping preserves its children, but not its initialized
// state. Fix that here.
// That is also true for the File -> "Get new messages for" menuitems in both
// menus (old and new App menu). And also Go -> Folder.
// TODO bug 904223: try to fix folderWidgets.xml to not do this.
// See Bug 520457 and Bug 534448 and Bug 709733.
// Fix Bug 565045: Only treat "Get Message Button" if it is in our toolbox
for (let popup of [ toolbox.querySelector("#button-getMsgPopup"),
document.getElementById("menu_getAllNewMsgPopup"),
document.getElementById("appmenu_getAllNewMsgPopup"),
document.getElementById("menu_GoFolderPopup"),
document.getElementById("appmenu_GoFolderPopup") ])
{
if (!popup)
continue;
// .teardown() is only available here if the menu has its frame
// otherwise the folderWidgets.xml::folder-menupopup binding is not
// attached to the popup. So if it is not available, remove the items
// explicitly. Only remove elements that were generated by the binding.
if ("_teardown" in popup) {
popup._teardown();
} else {
for (let i = popup.childNodes.length - 1; i >= 0; i--) {
let child = popup.childNodes[i];
if (child.getAttribute("generated") != "true")
continue;
if ("_teardown" in child)
child._teardown();
child.remove();
}
}
}
}
}
function onViewToolbarsPopupShowing(aEvent, toolboxIds, aInsertPoint)
{
if (!Array.isArray(toolboxIds))
toolboxIds = [toolboxIds];
let popup = aEvent.target;
// Empty the menu
for (let i = popup.childNodes.length - 1; i >= 0; --i) {
let deadItem = popup.childNodes[i];
// Remove all of the nodes with the iscollapsible
// attribute.
if (deadItem.hasAttribute("iscollapsible"))
deadItem.remove();
}
// We'll insert the menuitems before the first item in the list if no insert
// point is defined.
let firstMenuItem = aInsertPoint || popup.firstChild;
for (let [, toolboxId] in Iterator(toolboxIds)) {
let toolbox = document.getElementById(toolboxId);
// We'll consider either childnodes that have a toolbarname attribute,
// or externalToolbars.
let potentialToolbars = Array.slice(
toolbox.querySelectorAll("[toolbarname]")
);
for (let externalToolbar of toolbox.externalToolbars) {
if (externalToolbar.getAttribute("prependmenuitem"))
potentialToolbars.unshift(externalToolbar);
else
potentialToolbars.push(externalToolbar);
}
for (let toolbarElement of potentialToolbars) {
// We have to bind to toolbar because Javascript doesn't do fresh
// let-bindings per Iteration.
let toolbar = toolbarElement;
let toolbarName = toolbar.getAttribute("toolbarname");
if (toolbarName) {
let menuItem = document.createElement("menuitem");
let hidingAttribute = toolbar.getAttribute("type") == "menubar" ?
"autohide" : "collapsed";
menuItem.setAttribute("type", "checkbox");
// Mark this menuitem with an iscollapsible attribute, so we
// know we can wipe it out later on.
menuItem.setAttribute("iscollapsible", true);
menuItem.setAttribute("toolbarid", toolbar.id);
menuItem.setAttribute("label", toolbarName);
menuItem.setAttribute("accesskey", toolbar.getAttribute("accesskey"));
menuItem.setAttribute("checked",
toolbar.getAttribute(hidingAttribute) != "true");
popup.insertBefore(menuItem, firstMenuItem);
let onMenuItemCommand = function(aEvent) {
let hidden = aEvent.originalTarget.getAttribute("checked") != "true";
toolbar.setAttribute(hidingAttribute, hidden);
document.persist(toolbar.id, hidingAttribute);
}
menuItem.addEventListener("command", onMenuItemCommand, false);
}
}
}
}
function toJavaScriptConsole()
{
toOpenWindowByType("global:console", "chrome://global/content/console.xul");
}
function toOpenWindowByType(inType, uri)
{
var topWindow = Services.wm.getMostRecentWindow(inType);
if (topWindow)
topWindow.focus();
else
window.open(uri, "_blank", "chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar");
}
function toMessengerWindow()
{
toOpenWindowByType("mail:3pane", "chrome://messenger/content/messenger.xul");
}
function focusOnMail(tabNo, event)
{
// this is invoked by accel-<number>
// if the window isn't visible or focused, make it so
var topWindow = Services.wm.getMostRecentWindow("mail:3pane");
if (topWindow) {
if (topWindow != window)
topWindow.focus();
else
document.getElementById('tabmail').selectTabByIndex(event, tabNo);
}
else {
window.open("chrome://messenger/content/messenger.xul",
"_blank",
"chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar");
}
}
function toAddressBook()
{
toOpenWindowByType("mail:addressbook",
"chrome://messenger/content/addressbook/addressbook.xul");
}
function showChatTab()
{
let tabmail = document.getElementById("tabmail");
if (gChatTab)
tabmail.switchToTab(gChatTab);
else
tabmail.openTab("chat", {});
}
function toImport()
{
window.openDialog("chrome://messenger/content/importDialog.xul", "importDialog",
"chrome, modal, titlebar, centerscreen");
}
function toSanitize()
{
Components.classes["@mozilla.org/mail/mailglue;1"]
.getService(Components.interfaces.nsIMailGlue)
.sanitize(window);
}
/**
* Opens the Preferences (Options) dialog.
*
* @param aPaneID ID of prefpane to select automatically.
* @param aTabID ID of tab to select on the prefpane.
* @param aOtherArgs other prefpane specific arguments
*/
function openOptionsDialog(aPaneID, aTabID, aOtherArgs)
{
let loadInContent = Services.prefs.getBoolPref("mail.preferences.inContent");
// Load the prefs in a tab?
if (loadInContent) {
// Yes, load the prefs in a tab
openPreferencesTab(aPaneID, aTabID, aOtherArgs);
} else {
// No, load the prefs in a dialog
let win = Services.wm.getMostRecentWindow("Mail:Preferences");
if (win) {
// the dialog is already open
win.focus();
if (aPaneID) {
let prefWindow = win.document.getElementById("MailPreferences");
win.selectPaneAndTab(prefWindow, aPaneID, aTabID);
}
} else {
// the dialog must be created
let instantApply = Services.prefs
.getBoolPref("browser.preferences.instantApply");
let features = "chrome,titlebar,toolbar,centerscreen" +
(instantApply ? ",dialog=no" : ",modal");
openDialog("chrome://messenger/content/preferences/preferences.xul",
"Preferences", features, aPaneID, aTabID, aOtherArgs);
}
}
}
function openAddonsMgr(aView)
{
if (aView) {
let emWindow;
let browserWindow;
let receivePong = function receivePong(aSubject, aTopic, aData) {
let browserWin = aSubject.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIWebNavigation)
.QueryInterface(Components.interfaces.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindow);
if (!emWindow || browserWin == window /* favor the current window */) {
emWindow = aSubject;
browserWindow = browserWin;
}
}
Services.obs.addObserver(receivePong, "EM-pong", false);
Services.obs.notifyObservers(null, "EM-ping", "");
Services.obs.removeObserver(receivePong, "EM-pong");
if (emWindow) {
emWindow.loadView(aView);
let tabmail = browserWindow.document.getElementById("tabmail");
tabmail.switchToTab(tabmail.getBrowserForDocument(emWindow));
emWindow.focus();
return;
}
}
openContentTab("about:addons", "tab", "addons.mozilla.org");
if (aView) {
// This must be a new load, else the ping/pong would have
// found the window above.
Services.obs.addObserver(function loadViewOnLoad(aSubject, aTopic, aData) {
Services.obs.removeObserver(loadViewOnLoad, aTopic);
aSubject.loadView(aView);
}, "EM-loaded", false);
}
}
function openActivityMgr()
{
Components.classes['@mozilla.org/activity-manager-ui;1'].
getService(Components.interfaces.nsIActivityManagerUI).show(window);
}
function openIMAccountMgr()
{
var win = Services.wm.getMostRecentWindow("Messenger:Accounts");
if (win)
win.focus();
else {
win = Services.ww.openWindow(null,
"chrome://messenger/content/chat/imAccounts.xul",
"Accounts", "chrome,resizable,centerscreen",
null);
}
return win;
}
function openIMAccountWizard()
{
const kFeatures = "chrome,centerscreen,modal,titlebar";
const kUrl = "chrome://messenger/content/chat/imAccountWizard.xul";
const kName = "IMAccountWizard";
#ifdef XP_MACOSX
// On Mac, avoid using the hidden window as a parent as that would
// make it visible.
let hiddenWindowUrl =
Services.prefs.getCharPref("browser.hiddenWindowChromeURL");
if (window.location.href == hiddenWindowUrl) {
Services.ww.openWindow(null, kUrl, kName, kFeatures, null);
return;
}
#endif
window.openDialog(kUrl, kName, kFeatures);
}
function openSavedFilesWnd()
{
let tabmail = document.getElementById("tabmail");
let downloadsBrowser = tabmail.getBrowserForDocumentId("aboutDownloads");
if (downloadsBrowser)
tabmail.switchToTab(downloadsBrowser);
else {
tabmail.openTab("chromeTab",
{ chromePage: "about:downloads",
clickHandler: "specialTabs.aboutClickHandler(event);" });
}
}
function SetBusyCursor(window, enable)
{
// setCursor() is only available for chrome windows.
// However one of our frames is the start page which
// is a non-chrome window, so check if this window has a
// setCursor method
if ("setCursor" in window) {
if (enable)
window.setCursor("progress");
else
window.setCursor("auto");
}
var numFrames = window.frames.length;
for(var i = 0; i < numFrames; i++)
SetBusyCursor(window.frames[i], enable);
}
function openAboutDialog()
{
let enumerator = Services.wm.getEnumerator("Mail:About");
while (enumerator.hasMoreElements()) {
// Only open one about window
let win = enumerator.getNext();
win.focus();
return;
}
#ifdef XP_WIN
var features = "chrome,centerscreen,dependent";
#elifdef XP_MACOSX
var features = "chrome,resizable=no,minimizable=no";
#else
var features = "chrome,centerscreen,dependent,dialog=no";
#endif
window.openDialog("chrome://messenger/content/aboutDialog.xul", "About", features);
}
/**
* Opens the support page based on the app.support.baseURL pref.
*/
function openSupportURL()
{
openFormattedURL("app.support.baseURL");
}
/**
* Fetches the url for the passed in pref name, formats it and then loads it in the default
* browser.
*
* @param aPrefName - name of the pref that holds the url we want to format and open
*/
function openFormattedURL(aPrefName)
{
var urlToOpen = Services.urlFormatter.formatURLPref(aPrefName);
var uri = Services.io.newURI(urlToOpen, null, null);
var protocolSvc = Components.classes["@mozilla.org/uriloader/external-protocol-service;1"]
.getService(Components.interfaces.nsIExternalProtocolService);
protocolSvc.loadURI(uri);
}
/**
* Prompt the user to restart the browser in safe mode.
*/
function safeModeRestart()
{
// prompt the user to confirm
let bundle = Services.strings.createBundle(
"chrome://messenger/locale/messenger.properties");
let promptTitle = bundle.GetStringFromName("safeModeRestartPromptTitle");
let promptMessage = bundle.GetStringFromName("safeModeRestartPromptMessage");
let restartText = bundle.GetStringFromName("safeModeRestartButton");
let buttonFlags = (Services.prompt.BUTTON_POS_0 *
Services.prompt.BUTTON_TITLE_IS_STRING) +
(Services.prompt.BUTTON_POS_1 *
Services.prompt.BUTTON_TITLE_CANCEL) +
Services.prompt.BUTTON_POS_0_DEFAULT;
let rv = Services.prompt.confirmEx(window, promptTitle, promptMessage,
buttonFlags, restartText, null, null,
null, {});
if (rv == 0) {
let environment = Components.classes["@mozilla.org/process/environment;1"]
.getService(Components.interfaces.nsIEnvironment);
environment.set("MOZ_SAFE_MODE_RESTART", "1");
Application.restart();
}
}
#ifndef XP_WIN
#define BROKEN_WM_Z_ORDER
#endif
function getMostRecentMailWindow() {
#ifdef BROKEN_WM_Z_ORDER
let win = Services.wm.getMostRecentWindow("mail:3pane", true);
// if we're lucky, this isn't a popup, and we can just return this
if (win && win.document.documentElement.getAttribute("chromehidden")) {
win = null;
var windowList = Services.wm.getEnumerator("mail:3pane", true);
// this is oldest to newest, so this gets a bit ugly
while (windowList.hasMoreElements()) {
var nextWin = windowList.getNext();
if (!nextWin.document.documentElement.getAttribute("chromehidden"))
win = nextWin;
}
}
#else
var windowList = Services.wm.getZOrderDOMWindowEnumerator("mail:3pane", true);
if (!windowList.hasMoreElements())
return null;
var win = windowList.getNext();
while (win.document.documentElement.getAttribute("chromehidden")) {
if (!windowList.hasMoreElements())
return null;
win = windowList.getNext();
}
#endif
return win;
}
/**
* Create a sanitized display name for an attachment in order to help prevent
* people from hiding malicious extensions behind a run of spaces, etc. To do
* this, we strip leading/trailing whitespace and collapse long runs of either
* whitespace or identical characters. Windows especially will drop trailing
* dots and whitespace from filename extensions.
*
* @param aAttachment the AttachmentInfo object
* @return a sanitized display name for the attachment
*/
function SanitizeAttachmentDisplayName(aAttachment)
{
let displayName = aAttachment.name.trim();
return displayName.replace(/\s+/g, " ")
#ifdef XP_WIN
.replace(/[ \.]+$/, "")
#endif
.replace(/(.)\1{9,}/g, "$1…$1");
}
/**
* Create a TransferData object for a message attachment, either from the
* message reader or the composer.
*
* @param aAttachment the attachment object
* @return the TransferData
*/
function CreateAttachmentTransferData(aAttachment)
{
// For now, disallow drag-and-drop on cloud attachments. In the future, we
// should allow this.
if (aAttachment.contentType == "text/x-moz-deleted" ||
aAttachment.sendViaCloud)
return null;
var name = aAttachment.name || aAttachment.displayName;
var data = new TransferData();
if (aAttachment.url && name)
{
// Only add type/filename info for non-file URLs that don't already
// have it.
if (/(^file:|&filename=)/.test(aAttachment.url))
var info = aAttachment.url;
else
var info = aAttachment.url + "&type=" + aAttachment.contentType +
"&filename=" + encodeURIComponent(name);
data.addDataForFlavour("text/x-moz-url",
info + "\n" + name + "\n" + aAttachment.size);
data.addDataForFlavour("text/x-moz-url-data", aAttachment.url);
data.addDataForFlavour("text/x-moz-url-desc", name);
data.addDataForFlavour("application/x-moz-file-promise-url",
aAttachment.url);
data.addDataForFlavour("application/x-moz-file-promise",
new nsFlavorDataProvider(), 0,
Components.interfaces.nsISupports);
}
return data;
}
function nsFlavorDataProvider()
{
}
nsFlavorDataProvider.prototype =
{
QueryInterface : function(iid)
{
if (iid.equals(Components.interfaces.nsIFlavorDataProvider) ||
iid.equals(Components.interfaces.nsISupports))
return this;
throw Components.results.NS_NOINTERFACE;
},
getFlavorData : function(aTransferable, aFlavor, aData, aDataLen)
{
// get the url for the attachment
if (aFlavor == "application/x-moz-file-promise")
{
var urlPrimitive = { };
var dataSize = { };
aTransferable.getTransferData("application/x-moz-file-promise-url",
urlPrimitive, dataSize);
var srcUrlPrimitive = urlPrimitive.value.QueryInterface(Components.interfaces.nsISupportsString);
// now get the destination file location from kFilePromiseDirectoryMime
var dirPrimitive = {};
aTransferable.getTransferData("application/x-moz-file-promise-dir",
dirPrimitive, dataSize);
var destDirectory = dirPrimitive.value.QueryInterface(Components.interfaces.nsILocalFile);
// now save the attachment to the specified location
// XXX: we need more information than just the attachment url to save it,
// fortunately, we have an array of all the current attachments so we can
// cheat and scan through them
var attachment = null;
for (let index of currentAttachments.keys())
{
attachment = currentAttachments[index];
if (attachment.url == srcUrlPrimitive)
break;
}
// call our code for saving attachments
if (attachment)
{
var name = attachment.name || attachment.displayName;
var destFilePath = messenger.saveAttachmentToFolder(attachment.contentType,
attachment.url,
encodeURIComponent(name),
attachment.uri,
destDirectory);
aData.value = destFilePath.QueryInterface(Components.interfaces.nsISupports);
aDataLen.value = 4;
}
}
}
}
|