Bug 777732 - Remove compose windows recycling (part 1). r=mkmelin a=jorgk SM CLOSED TREE

--HG--
extra : amend_source : ca1429db926ffc959e12cc064f03e6ee4cbffb4b
This commit is contained in:
Jorg K 2016-03-28 18:40:21 +02:00
Родитель bc3e45281a
Коммит 9af4160c55
15 изменённых файлов: 102 добавлений и 797 удалений

Просмотреть файл

@ -171,90 +171,6 @@ function updateEditableFields(aDisable)
elements[i].disabled = aDisable;
}
var gComposeRecyclingListener = {
onClose: function() {
//Clear the subject
GetMsgSubjectElement().value = "";
// be sure to clear the transaction manager for the subject
GetMsgSubjectElement().editor.transactionManager.clear();
SetComposeWindowTitle();
//Reset recipients and attachments
awResetAllRows();
RemoveAllAttachments();
// We need to clear the identity popup menu in case the user will change them.
// It will be rebuilt later in ComposeStartup
ClearIdentityListPopup(document.getElementById("msgIdentityPopup"));
var identityElement = document.getElementById("msgIdentity");
identityElement.editable = false;
identityElement.setAttribute("type", "description");
var customizeMenuitem = document.getElementById("cmd_customizeFromAddress");
customizeMenuitem.removeAttribute("disabled");
customizeMenuitem.setAttribute("checked", "false");
// Do not listen to changes to spell check dictionary.
document.removeEventListener("spellcheck-changed", updateDocumentLanguage);
// Disconnect the observer, we'll attach a new one when recycling the
// window.
gLanguageObserver.disconnect();
// Stop observing dictionary removals.
dictionaryRemovalObserver.removeObserver();
// Stop gSpellChecker so personal dictionary is saved.
// We need to do this before disabling the editor.
enableInlineSpellCheck(false);
// clear any suggestions in the context menu
gSpellChecker.clearSuggestionsFromMenu();
gSpellChecker.clearDictionaryListFromMenu();
SetContentAndBodyAsUnmodified();
updateEditableFields(true);
// Clear the focus
awGetInputElement(1).removeAttribute('focused');
//Reset Boxes size
document.getElementById("headers-box").removeAttribute("height");
document.getElementById("appcontent").removeAttribute("height");
document.getElementById("addresses-box").removeAttribute("width");
//Reset menu options
document.getElementById("format_auto").setAttribute("checked", "true");
document.getElementById("priority_normal").setAttribute("checked", "true");
// Reset the Customize Toolbars panel/sheet if open.
if (getMailToolbox().customizing && gCustomizeSheet)
document.getElementById("customizeToolbarSheetIFrame")
.contentWindow.finishToolbarCustomization();
//Reset editor
EditorResetFontAndColorAttributes();
EditorCleanup();
gAttachmentNotifier.redetectKeywords();
//Release the nsIMsgComposeParams object
if (window.arguments && window.arguments[0])
window.arguments[0] = null;
document.getElementById("msgcomposeWindow").dispatchEvent(
new Event("compose-window-close", { bubbles: false , cancelable: true }));
if (gAutoSaveTimeout)
clearTimeout(gAutoSaveTimeout);
ReleaseGlobalVariables(); // This line must be the last in onClose();
},
onReopen: function(params) {
InitializeGlobalVariables();
ComposeStartup(true, params);
var event = document.createEvent('Events');
event.initEvent('compose-window-reopen', false, true);
document.getElementById("msgcomposeWindow").dispatchEvent(event);
}
};
var PrintPreviewListener = {
getPrintPreviewBrowser: function() {
var browser = document.getElementById("cppBrowser");
@ -379,7 +295,7 @@ var stateListener = {
if (gMsgCompose)
gMsgCompose.onSendNotPerformed(null, Components.results.NS_ERROR_ABORT);
MsgComposeCloseWindow(true);
MsgComposeCloseWindow();
}
}
// else if we failed to save, and we're autosaving, need to re-mark the editor
@ -1671,8 +1587,8 @@ function DoCommandClose()
if (gMsgCompose)
gMsgCompose.onSendNotPerformed(null, Components.results.NS_ERROR_ABORT);
// note: if we're not caching this window, this destroys it for us
MsgComposeCloseWindow(true);
// This destroys the window for us.
MsgComposeCloseWindow();
}
return false;
@ -2102,10 +2018,6 @@ var dictionaryRemovalObserver =
},
removeObserver: function() {
// We need to protect against double removal:
// The window can be recycled and later destroyed (at shutdown or when the
// composition style changes from HTML to plain text or vice versa) or
// only destroyed if it was never recycled before.
if (this.isAdded) {
Services.obs.removeObserver(this, "spellcheck-dictionary-remove");
this.isAdded = false;
@ -2113,7 +2025,7 @@ var dictionaryRemovalObserver =
}
}
function ComposeStartup(recycled, aParams)
function ComposeStartup(aParams)
{
// Findbar overlay
if (!document.getElementById("findbar-replaceButton")) {
@ -2297,11 +2209,7 @@ function ComposeStartup(recycled, aParams)
var editorElement = GetCurrentEditorElement();
gMsgCompose = MailServices.compose.initCompose(params, window, editorElement.docShell);
// Set the close listener.
gMsgCompose.recyclingListener = gComposeRecyclingListener;
gMsgCompose.addMsgSendListener(gSendListener);
// Lets the compose object know that we are dealing with a recycled window.
gMsgCompose.recycledWindow = recycled;
document.getElementById("returnReceiptMenu")
.setAttribute('checked', gMsgCompose.compFields.returnReceipt);
@ -2314,32 +2222,25 @@ function ComposeStartup(recycled, aParams)
SetCompositionAsPerDeliveryFormat(gSendFormat);
SelectDeliveryFormatMenuOption(gSendFormat);
// If recycle, editor is already created.
if (!recycled)
let editortype = gMsgCompose.composeHTML ? "htmlmail" : "textmail";
editorElement.makeEditable(editortype, true);
// setEditorType MUST be called before setContentWindow
if (gMsgCompose.composeHTML)
{
let editortype = gMsgCompose.composeHTML ? "htmlmail" : "textmail";
editorElement.makeEditable(editortype, true);
// setEditorType MUST be called before setContentWindow
if (gMsgCompose.composeHTML)
{
initLocalFontFaceMenu(document.getElementById("FontFacePopup"));
}
else
{
// We are editing in plain text mode.
// The SetCompositionAsPerDeliveryFormat call above already hid
// the HTML toolbar, format and insert menus.
// Also remove the delivery format from the options menu.
// We only do that when the window is first created ("!recycled")
// as we will never need to restore it since a plain text window will
// never be used for a HTML composition.
document.getElementById("outputFormatMenu").setAttribute("hidden", true);
}
// Do setup common to Message Composer and Web Composer.
EditorSharedStartup();
initLocalFontFaceMenu(document.getElementById("FontFacePopup"));
}
else
{
// We are editing in plain text mode.
// The SetCompositionAsPerDeliveryFormat call above already hid
// the HTML toolbar, format and insert menus.
// Also remove the delivery format from the options menu.
document.getElementById("outputFormatMenu").setAttribute("hidden", true);
}
// Do setup common to Message Composer and Web Composer.
EditorSharedStartup();
if (params.bodyIsLink)
{
@ -2370,33 +2271,16 @@ function ComposeStartup(recycled, aParams)
gMsgCompose.RegisterStateListener(stateListener);
if (recycled)
{
InitEditor();
// Add an observer to be called when document is done loading,
// which creates the editor.
try {
GetCurrentCommandManager().
addCommandObserver(gMsgEditorCreationObserver, "obs_documentCreated");
if (gMsgCompose.composeHTML)
{
// Force color picker on toolbar to show document colors.
onFontColorChange();
onBackgroundColorChange();
}
// Reset the priority field for recycled windows.
updatePriorityToolbarButton("Normal");
}
else
{
// Add an observer to be called when document is done loading,
// which creates the editor.
try {
GetCurrentCommandManager().
addCommandObserver(gMsgEditorCreationObserver, "obs_documentCreated");
// Load empty page to create the editor.
editorElement.webNavigation.loadURI("about:blank", 0, null, null, null);
} catch (e) {
Components.utils.reportError(e);
}
// Load empty page to create the editor.
editorElement.webNavigation.loadURI("about:blank", 0, null, null, null);
} catch (e) {
Components.utils.reportError(e);
}
gEditingDraft = gMsgCompose.compFields.draftId;
@ -2456,7 +2340,7 @@ function WizCallback(state)
else
{
// The account wizard is still closing so we can't close just yet
setTimeout(MsgComposeCloseWindow, 0, false); // Don't recycle a bogus window
setTimeout(MsgComposeCloseWindow, 0);
}
}
@ -2495,14 +2379,14 @@ function ComposeLoad()
selectNode.appendItem(other_headers_Array[i] + ":", "addr_other");
}
if (state)
ComposeStartup(false, null);
ComposeStartup(null);
}
catch (ex) {
Components.utils.reportError(ex);
Services.prompt.alert(window, getComposeBundle().getString("initErrorDlogTitle"),
getComposeBundle().getString("initErrorDlgMessage"));
MsgComposeCloseWindow(false); // Don't try to recycle a bogus window
MsgComposeCloseWindow();
return;
}
@ -3664,10 +3548,10 @@ function SetContentAndBodyAsUnmodified()
gContentChanged = false;
}
function MsgComposeCloseWindow(recycleIt)
function MsgComposeCloseWindow()
{
if (gMsgCompose)
gMsgCompose.CloseWindow(recycleIt);
gMsgCompose.CloseWindow();
else
window.close();
}
@ -4993,9 +4877,8 @@ function InitEditor()
editor.addOverrideStyleSheet("chrome://messenger/content/composerOverlay.css");
gMsgCompose.initEditor(editor, window.content);
// We always go through this function everytime we init an editor, be it a
// recycled editor, or a fresh one. First step is making sure we can spell
// check.
// We always go through this function everytime we init an editor.
// First step is making sure we can spell check.
gSpellChecker.init(editor);
document.getElementById('menu_inlineSpellCheck')
.setAttribute('disabled', !gSpellChecker.canSpellCheck);

Просмотреть файл

@ -73,8 +73,7 @@ function onComposerReOpen()
addEventListener("load", smimeComposeOnLoad, false);
// this function gets called multiple times,
// but only on first open, not on composer recycling
// this function gets called multiple times.
function smimeComposeOnLoad()
{
removeEventListener("load", smimeComposeOnLoad, false);

Просмотреть файл

@ -554,42 +554,6 @@ function test_disabled_attachment_reminder() {
Services.prefs.setBoolPref(kReminderPref, true);
}
/**
* Bug 1099866
* Check if reminder does not stay open on compose window reopen
* due to window recycling.
*/
function test_recycling_attachment_reminder() {
let recycledWindows = Services.prefs.getIntPref("mail.compose.max_recycled_windows");
assert_true(recycledWindows > 0);
// Open a sample message with no attachment keywords.
let cwc = open_compose_new_mail();
setupComposeWin(cwc, "test@example.invalid", "Testing recycling a reminder!",
"Some body...");
// There should be no attachment notification.
assert_automatic_reminder_state(cwc, false);
// Add some keyword so the automatic notification
// could potentially show up.
setupComposeWin(cwc, "", "", " and look for your attachment!");
// Give the notification time to appear. It should.
wait_for_reminder_state(cwc, true);
close_compose_window(cwc, true);
// Another compose window without any keywords.
cwc = open_compose_new_mail();
setupComposeWin(cwc, "test@example.invalid", "Testing reminder after recycling!",
"Some body...");
// There should be no attachment notification.
assert_automatic_reminder_state(cwc, false);
close_compose_window(cwc);
}
/**
* Click the send button and handle the send error dialog popping up.
* It will return us back to the compose window.

Просмотреть файл

@ -159,8 +159,10 @@ function test_encoding_upgrade_html_compose() {
throw new Error("Chinese text not in msg; CHINESE=" + CHINESE +
", draftMsg2Content=" + draftMsg2Content);
// Ctrl+Shift+Return = Send Later
compWin.keypress(null, "VK_RETURN", {shiftKey: true, accelKey: true});
compWin.window.setTimeout(function() {
// Ctrl+Shift+Return = Send Later
compWin.keypress(null, "VK_RETURN", {shiftKey: true, accelKey: true});
}, 0);
be_in_folder(outboxFolder);
let outMsg = select_click_row(0);
@ -232,8 +234,10 @@ function test_encoding_upgrade_plaintext_compose() {
throw new Error("Chinese text not in msg; CHINESE=" + CHINESE +
", draftMsg2Content=" + draftMsg2Content);
// Ctrl+Shift+Return = Send Later.
compWin.keypress(null, "VK_RETURN", {shiftKey: true, accelKey: true});
compWin.window.setTimeout(function() {
// Ctrl+Shift+Return = Send Later.
compWin.keypress(null, "VK_RETURN", {shiftKey: true, accelKey: true});
}, 0);
be_in_folder(outboxFolder);
let outMsg = select_click_row(0);

Просмотреть файл

@ -143,7 +143,7 @@ function test_save_delivery_format() {
close_compose_window(cwc);
// Open a new composition see if the menu is again at default value, not the one
// chosen above, even in a recycled compose window.
// chosen above.
cwc = open_compose_new_mail();
assert_format_value("format_auto", Ci.nsIMsgCompSendFormat.AskUser);

Просмотреть файл

@ -91,13 +91,6 @@ interface nsIMsgComposeNotificationType
native nsString(nsString);
[ref] native nsStringRef(nsString);
/* recycling listener interface */
[scriptable, uuid(0b28cc56-1dd2-11b2-bbe4-99e6a314f8ba)]
interface nsIMsgComposeRecyclingListener : nsISupports {
void onClose();
void onReopen(in nsIMsgComposeParams params);
};
[scriptable, uuid(c6544b6b-06dd-43ac-89b5-949d7c81bb7b)]
interface nsIMsgCompose : nsIMsgSendListener {
@ -140,7 +133,7 @@ interface nsIMsgCompose : nsIMsgSendListener {
in string accountKey);
/* ... */
void CloseWindow(in boolean reclycleIt);
void CloseWindow();
/* ... */
void abort();
@ -284,12 +277,6 @@ interface nsIMsgCompose : nsIMsgSendListener {
/* ... */
[noscript] void getQuotingToFollow(out boolean quotingToFollow);
/* ... */
attribute nsIMsgComposeRecyclingListener recyclingListener;
/* ... */
attribute boolean recycledWindow;
readonly attribute string originalMsgURI;
attribute boolean deleteDraft;

Просмотреть файл

@ -46,8 +46,7 @@ interface nsIMsgComposeService : nsISupports {
void OpenComposeWindowWithParams(in string msgComposeWindowURL, in nsIMsgComposeParams params);
/**
* Creates an nsIMsgCompose instance, initalizes it, and manages the window
* recycling cache.
* Creates an nsIMsgCompose instance and initalizes it.
*
* @param aParams An nsIMsgComposeParams object containing the initial
* details for the compose.
@ -73,8 +72,6 @@ interface nsIMsgComposeService : nsISupports {
readonly attribute boolean logComposePerformance;
[noscript] boolean determineComposeHTML(in nsIMsgIdentity aIdentity, in MSG_ComposeFormat aFormat);
[noscript] void cacheWindow(in mozIDOMWindowProxy aWindow, in boolean aComposeHTML, in nsIMsgComposeRecyclingListener listener);
boolean isCachedWindow(in mozIDOMWindowProxy aWindow);
/**
* given a mailto url, parse the attributes and turn them into a nsIMsgComposeParams object
@ -130,7 +127,7 @@ interface nsIMsgComposeService : nsISupports {
in nsIMsgCompose aMsgCompose);
/**
* When an editor docShell is being closed (or recycled), you should
* When an editor docShell is being closed, you should
* unregister it from this service. nsIMsgCompose normally calls this
* automatically for items passed to initCompose.
*

Просмотреть файл

@ -187,7 +187,6 @@ nsMsgCompose::nsMsgCompose()
prefBranch->GetBoolPref("converter.html2txt.structs", &mConvertStructs);
m_composeHTML = false;
mRecycledWindow = true;
}
@ -610,17 +609,6 @@ nsMsgCompose::ConvertAndLoadComposeWindow(nsString& aPrefix,
TranslateLineEnding(aBuf);
TranslateLineEnding(aSignature);
// We're going to be inserting stuff, and MsgComposeCommands
// may have set the editor to readonly in the recycled case.
// So set it back to writable.
// Note! enableEditableFields in gComposeRecyclingListener::onReopen
// will redundantly set this flag to writable, but it gets there
// too late.
uint32_t flags = 0;
m_editor->GetFlags(&flags);
flags &= ~nsIPlaintextEditor::eEditorReadonlyMask;
m_editor->SetFlags(flags);
m_editor->EnableUndo(false);
// Ok - now we need to figure out the charset of the aBuf we are going to send
@ -1488,34 +1476,6 @@ NS_IMETHODIMP nsMsgCompose::SendMsg(MSG_DeliverMode deliverMode, nsIMsgIdentity
return rv;
}
// XXX when do we break this ref to the listener?
NS_IMETHODIMP nsMsgCompose::SetRecyclingListener(nsIMsgComposeRecyclingListener *aRecyclingListener)
{
mRecyclingListener = aRecyclingListener;
return NS_OK;
}
NS_IMETHODIMP nsMsgCompose::GetRecyclingListener(nsIMsgComposeRecyclingListener **aRecyclingListener)
{
NS_ENSURE_ARG_POINTER(aRecyclingListener);
*aRecyclingListener = mRecyclingListener;
NS_IF_ADDREF(*aRecyclingListener);
return NS_OK;
}
/* attribute boolean recycledWindow; */
NS_IMETHODIMP nsMsgCompose::GetRecycledWindow(bool *aRecycledWindow)
{
NS_ENSURE_ARG_POINTER(aRecycledWindow);
*aRecycledWindow = mRecycledWindow;
return NS_OK;
}
NS_IMETHODIMP nsMsgCompose::SetRecycledWindow(bool aRecycledWindow)
{
mRecycledWindow = aRecycledWindow;
return NS_OK;
}
/* attribute boolean deleteDraft */
NS_IMETHODIMP nsMsgCompose::GetDeleteDraft(bool *aDeleteDraft)
{
@ -1553,7 +1513,7 @@ bool nsMsgCompose::IsLastWindow()
return true;
}
NS_IMETHODIMP nsMsgCompose::CloseWindow(bool recycleIt)
NS_IMETHODIMP nsMsgCompose::CloseWindow(void)
{
nsresult rv;
@ -1569,55 +1529,13 @@ NS_IMETHODIMP nsMsgCompose::CloseWindow(bool recycleIt)
// temporary files.
mMsgSend = nullptr;
recycleIt = recycleIt && !IsLastWindow();
if (recycleIt)
{
rv = composeService->CacheWindow(m_window, m_composeHTML, mRecyclingListener);
if (NS_SUCCEEDED(rv))
{
nsCOMPtr<nsIHTMLEditor> htmlEditor (do_QueryInterface(m_editor));
NS_ASSERTION(htmlEditor, "no editor");
if (htmlEditor)
{
// XXX clear undo txn manager?
rv = m_editor->EnableUndo(false);
NS_ENSURE_SUCCESS(rv,rv);
rv = htmlEditor->RebuildDocumentFromSource(EmptyString());
NS_ENSURE_SUCCESS(rv,rv);
rv = m_editor->EnableUndo(true);
NS_ENSURE_SUCCESS(rv,rv);
SetBodyModified(false);
}
if (mRecyclingListener)
{
mRecyclingListener->OnClose();
/**
* In order to really free the memory, we need to call the JS garbage collector for our window.
* If we don't call GC, the nsIMsgCompose object held by JS will not be released despite we set
* the JS global that held it to null. Each time we reopen a recycled window, we allocate a new
* nsIMsgCompose that we really need to be released when we recycle the window. In fact despite
* we call GC here, the release won't occur right away. But if we don't call it, the release
* will happen only when we physically close the window which will happen only on quit.
*/
nsJSContext::PokeGC(JS::gcreason::NSJSCONTEXT_DESTROY);
}
return NS_OK;
}
}
//We are going away for real, we need to do some clean up first
if (m_baseWindow)
{
if (m_editor)
{
/* The editor will be destroyed during yje close window.
* Set it to null to be sure we won't use it anymore
*/
// The editor will be destroyed during the close window.
// Set it to null to be sure we won't use it anymore.
m_editor = nullptr;
}
nsIBaseWindow * window = m_baseWindow;
@ -1625,6 +1543,7 @@ NS_IMETHODIMP nsMsgCompose::CloseWindow(bool recycleIt)
rv = window->Destroy();
}
m_window = nullptr;
return rv;
}
@ -3759,7 +3678,7 @@ nsresult nsMsgComposeSendListener::OnStopSending(const char *aMsgID, nsresult aS
progress->CloseProgressDialog(false);
}
if (hasDomWindow)
msgCompose->CloseWindow(true);
msgCompose->CloseWindow();
}
}
}
@ -3772,8 +3691,8 @@ nsresult nsMsgComposeSendListener::OnStopSending(const char *aMsgID, nsresult aS
progress->CloseProgressDialog(false);
}
if (hasDomWindow)
msgCompose->CloseWindow(true); // if we fail on the simple GetFcc call, close the window to be safe and avoid
// windows hanging around to prevent the app from exiting.
msgCompose->CloseWindow(); // if we fail on the simple GetFcc call, close the window to be safe and avoid
// windows hanging around to prevent the app from exiting.
}
// Remove the current draft msg when sending draft is done.
@ -3875,7 +3794,7 @@ nsMsgComposeSendListener::OnStopCopy(nsresult aStatus)
msgCompose->SetDeleteDraft(true);
RemoveCurrentDraftMessage(msgCompose, true);
}
msgCompose->CloseWindow(true);
msgCompose->CloseWindow();
}
}
msgCompose->ClearMessageSend();

Просмотреть файл

@ -113,8 +113,6 @@ protected:
QuotingOutputStreamListener *mQuoteStreamListener;
nsCOMPtr<nsIOutputStream> mBaseStream;
nsCOMPtr<nsIMsgComposeRecyclingListener> mRecyclingListener;
bool mRecycledWindow;
nsCOMPtr<nsIMsgSend> mMsgSend; // for composition back end
nsCOMPtr<nsIMsgProgress> mProgress; // use by the back end to report progress to the front end

Просмотреть файл

@ -72,8 +72,6 @@
#define DEFAULT_CHROME "chrome://messenger/content/messengercompose/messengercompose.xul"
#define PREF_MAIL_COMPOSE_MAXRECYCLEDWINDOWS "mail.compose.max_recycled_windows"
#define PREF_MAILNEWS_REPLY_QUOTING_SELECTION "mailnews.reply_quoting_selection"
#define PREF_MAILNEWS_REPLY_QUOTING_SELECTION_MULTI_WORD "mailnews.reply_quoting_selection.multi_word"
#define PREF_MAILNEWS_REPLY_QUOTING_SELECTION_ONLY_IF "mailnews.reply_quoting_selection.only_if_chars"
@ -118,45 +116,21 @@ nsMsgComposeService::nsMsgComposeService()
mPreviousTime = mStartTime;
#endif
mMaxRecycledWindows = 0;
mCachedWindows = nullptr;
}
NS_IMPL_ISUPPORTS(nsMsgComposeService,
nsIMsgComposeService,
nsIObserver,
ICOMMANDLINEHANDLER,
nsISupportsWeakReference)
nsMsgComposeService::~nsMsgComposeService()
{
if (mCachedWindows)
{
DeleteCachedWindows();
delete [] mCachedWindows;
}
mOpenComposeWindows.Clear();
}
nsresult nsMsgComposeService::Init()
{
nsresult rv = NS_OK;
// Register observers
// Register for quit application and profile change, we will need to clear the cache.
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (observerService)
{
rv = observerService->AddObserver(this, "quit-application", true);
rv = observerService->AddObserver(this, "profile-do-change", true);
}
// Register some pref observer
nsCOMPtr<nsIPrefBranch> pbi = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (pbi)
rv = pbi->AddObserver(PREF_MAIL_COMPOSE_MAXRECYCLEDWINDOWS, this, true);
Reset();
@ -171,41 +145,11 @@ nsresult nsMsgComposeService::Init()
void nsMsgComposeService::Reset()
{
nsresult rv = NS_OK;
if (mCachedWindows)
{
DeleteCachedWindows();
delete [] mCachedWindows;
mCachedWindows = nullptr;
mMaxRecycledWindows = 0;
}
mOpenComposeWindows.Clear();
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
if(prefs)
rv = prefs->GetIntPref(PREF_MAIL_COMPOSE_MAXRECYCLEDWINDOWS, &mMaxRecycledWindows);
if (NS_SUCCEEDED(rv) && mMaxRecycledWindows > 0)
{
mCachedWindows = new nsMsgCachedWindowInfo[mMaxRecycledWindows];
if (!mCachedWindows)
mMaxRecycledWindows = 0;
}
rv = prefs->GetBoolPref("mailnews.logComposePerformance", &mLogComposePerformance);
return;
}
void nsMsgComposeService::DeleteCachedWindows()
{
int32_t i;
for (i = 0; i < mMaxRecycledWindows; i ++)
{
CloseHiddenCachedWindow(mCachedWindows[i].window);
mCachedWindows[i].Clear();
}
if (prefs)
prefs->GetBoolPref("mailnews.logComposePerformance", &mLogComposePerformance);
}
// Function to open a message compose window and pass an nsIMsgComposeParams
@ -235,38 +179,7 @@ nsMsgComposeService::OpenComposeWindowWithParams(const char *chrome,
params->SetIdentity(identity);
}
//if we have a cached window for the default chrome, try to reuse it...
if (!chrome || PL_strcasecmp(chrome, DEFAULT_CHROME) == 0)
{
MSG_ComposeFormat format;
params->GetFormat(&format);
bool composeHTML = true;
rv = DetermineComposeHTML(identity, format, &composeHTML);
if (NS_SUCCEEDED(rv))
{
int32_t i;
for (i = 0; i < mMaxRecycledWindows; i ++)
{
if (mCachedWindows[i].window && (mCachedWindows[i].htmlCompose == composeHTML) && mCachedWindows[i].listener)
{
/* We need to save the window pointer as OnReopen will call nsMsgComposeService::InitCompose which will
clear the cache entry if everything goes well
*/
nsCOMPtr<mozIDOMWindowProxy> domWindow(mCachedWindows[i].window);
nsCOMPtr<nsIXULWindow> xulWindow(mCachedWindows[i].xulWindow);
rv = ShowCachedComposeWindow(domWindow, xulWindow, true);
if (NS_SUCCEEDED(rv))
{
mCachedWindows[i].listener->OnReopen(params);
return NS_OK;
}
}
}
}
}
//Else, create a new one...
// Create a new window.
nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
if (!wwatch)
return NS_ERROR_FAILURE;
@ -286,63 +199,6 @@ nsMsgComposeService::OpenComposeWindowWithParams(const char *chrome,
return rv;
}
void nsMsgComposeService::CloseHiddenCachedWindow(mozIDOMWindowProxy *domWindow)
{
if (domWindow)
{
nsCOMPtr<nsIDocShell> docshell;
if (domWindow)
{
nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(domWindow);
nsCOMPtr<nsIDocShellTreeItem> treeItem =
do_QueryInterface(window->GetDocShell());
if (treeItem)
{
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
treeItem->GetTreeOwner(getter_AddRefs(treeOwner));
if (treeOwner)
{
nsCOMPtr<nsIBaseWindow> baseWindow;
baseWindow = do_QueryInterface(treeOwner);
if (baseWindow) {
// HACK ALERT: when we hid this window we fired the "xul-window-destroyed"
// notification for it. Now that it's being really-destroyed it will fire that
// notification *again* for itself. The appstartup code maintains an internal
// reference count of windows that block app shutdown: we want to increment that
// count without cancelling app shutdown (so don't use "xul-window-registered").
nsCOMPtr<nsIAppStartup> appStartup(do_GetService(NS_APPSTARTUP_CONTRACTID));
if (appStartup)
appStartup->EnterLastWindowClosingSurvivalArea();
baseWindow->Destroy();
}
}
}
}
}
}
NS_IMETHODIMP
nsMsgComposeService::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *someData)
{
if (!strcmp(aTopic, "profile-do-change") || !strcmp(aTopic, "quit-application"))
{
DeleteCachedWindows();
return NS_OK;
}
if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID))
{
nsDependentString prefName(someData);
if (prefName.EqualsLiteral(PREF_MAIL_COMPOSE_MAXRECYCLEDWINDOWS))
Reset();
return NS_OK;
}
return NS_OK;
}
NS_IMETHODIMP
nsMsgComposeService::DetermineComposeHTML(nsIMsgIdentity *aIdentity, MSG_ComposeFormat aFormat, bool *aComposeHTML)
{
@ -714,15 +570,6 @@ NS_IMETHODIMP nsMsgComposeService::InitCompose(nsIMsgComposeParams *aParams,
nsIDocShell *aDocShell,
nsIMsgCompose **_retval)
{
// We need to remove the window from the cache.
int32_t i;
for (i = 0; i < mMaxRecycledWindows; i ++)
if (mCachedWindows[i].window == aWindow)
{
mCachedWindows[i].Clear();
break;
}
nsresult rv;
nsCOMPtr<nsIMsgCompose> msgCompose =
do_CreateInstance(NS_MSGCOMPOSE_CONTRACTID, &rv);
@ -790,89 +637,6 @@ NS_IMETHODIMP nsMsgComposeService::TimeStamp(const char * label, bool resetTime)
return NS_OK;
}
NS_IMETHODIMP
nsMsgComposeService::IsCachedWindow(mozIDOMWindowProxy *aCachedWindow, bool *aIsCachedWindow)
{
NS_ENSURE_ARG_POINTER(aCachedWindow);
NS_ENSURE_ARG_POINTER(aIsCachedWindow);
int32_t i;
for (i = 0; i < mMaxRecycledWindows; i ++)
if (mCachedWindows[i].window.get() == aCachedWindow)
{
*aIsCachedWindow = true;
return NS_OK;
}
*aIsCachedWindow = false;
return NS_OK;
}
NS_IMETHODIMP
nsMsgComposeService::CacheWindow(mozIDOMWindowProxy *aWindow, bool aComposeHTML, nsIMsgComposeRecyclingListener * aListener)
{
NS_ENSURE_ARG_POINTER(aWindow);
NS_ENSURE_ARG_POINTER(aListener);
nsresult rv;
nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(aWindow);
nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(window->GetDocShell(), &rv));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr <nsIDocShellTreeOwner> treeOwner;
rv = treeItem->GetTreeOwner(getter_AddRefs(treeOwner));
NS_ENSURE_SUCCESS(rv,rv);
nsCOMPtr<nsIXULWindow> xulWindow(do_GetInterface(treeOwner, &rv));
NS_ENSURE_SUCCESS(rv, rv);
int32_t i;
int32_t sameTypeId = -1;
int32_t oppositeTypeId = -1;
for (i = 0; i < mMaxRecycledWindows; i ++)
{
if (!mCachedWindows[i].window)
{
rv = ShowCachedComposeWindow(aWindow, xulWindow, false);
if (NS_SUCCEEDED(rv))
mCachedWindows[i].Initialize(aWindow, xulWindow, aListener, aComposeHTML);
return rv;
}
else
if (mCachedWindows[i].htmlCompose == aComposeHTML)
{
if (sameTypeId == -1)
sameTypeId = i;
}
else
{
if (oppositeTypeId == -1)
oppositeTypeId = i;
}
}
/* Looks like the cache is full. In the case we try to cache a type (html or plain text) of compose window which is not
already cached, we should replace an opposite one with this new one. That would allow users to be able to take advantage
of the cached compose window when they switch from one type to another one
*/
if (sameTypeId == -1 && oppositeTypeId != -1)
{
CloseHiddenCachedWindow(mCachedWindows[oppositeTypeId].window);
mCachedWindows[oppositeTypeId].Clear();
rv = ShowCachedComposeWindow(aWindow, xulWindow, false);
if (NS_SUCCEEDED(rv))
mCachedWindows[oppositeTypeId].Initialize(aWindow, xulWindow, aListener, aComposeHTML);
return rv;
}
return NS_ERROR_NOT_AVAILABLE;
}
class nsMsgTemplateReplyHelper final: public nsIStreamListener,
public nsIUrlListener
{
@ -1295,74 +1059,6 @@ nsMsgComposeService::ForwardMessage(const nsAString &forwardTo,
return folder->AddMessageDispositionState(aMsgHdr, nsIMsgFolder::nsMsgDispositionState_Forwarded);
}
nsresult nsMsgComposeService::ShowCachedComposeWindow(mozIDOMWindowProxy *aComposeWindow, nsIXULWindow *aXULWindow, bool aShow)
{
nsresult rv = NS_OK;
nsCOMPtr<nsIObserverService> obs =
mozilla::services::GetObserverService();
NS_ENSURE_TRUE(obs, NS_ERROR_UNEXPECTED);
NS_ENSURE_TRUE(aComposeWindow, NS_ERROR_FAILURE);
nsCOMPtr <nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(aComposeWindow);
nsIDocShell *docShell = window->GetDocShell();
nsCOMPtr <nsIDocShellTreeItem> treeItem = do_QueryInterface(docShell, &rv);
NS_ENSURE_SUCCESS(rv,rv);
nsCOMPtr <nsIDocShellTreeOwner> treeOwner;
rv = treeItem->GetTreeOwner(getter_AddRefs(treeOwner));
NS_ENSURE_SUCCESS(rv,rv);
if (treeOwner) {
// the window need to be sticky before we hide it.
nsCOMPtr<nsIContentViewer> contentViewer;
rv = docShell->GetContentViewer(getter_AddRefs(contentViewer));
NS_ENSURE_SUCCESS(rv,rv);
rv = contentViewer->SetSticky(!aShow);
NS_ENSURE_SUCCESS(rv,rv);
// disable (enable) the cached window
nsCOMPtr<nsIBaseWindow> baseWindow;
baseWindow = do_QueryInterface(treeOwner, &rv);
NS_ENSURE_SUCCESS(rv,rv);
baseWindow->SetEnabled(aShow);
NS_ENSURE_SUCCESS(rv,rv);
nsCOMPtr<nsIWindowMediator> windowMediator =
do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv,rv);
// if showing, reinstate the window with the mediator
if (aShow) {
rv = windowMediator->RegisterWindow(aXULWindow);
NS_ENSURE_SUCCESS(rv,rv);
obs->NotifyObservers(aXULWindow, "xul-window-registered", nullptr);
}
// hide (show) the cached window
rv = baseWindow->SetVisibility(aShow);
NS_ENSURE_SUCCESS(rv,rv);
// if hiding, remove the window from the mediator,
// so that it will be removed from the task list
if (!aShow) {
rv = windowMediator->UnregisterWindow(aXULWindow);
NS_ENSURE_SUCCESS(rv,rv);
obs->NotifyObservers(aXULWindow, "xul-window-destroyed", nullptr);
}
}
else {
rv = NS_ERROR_FAILURE;
}
return rv;
}
nsresult nsMsgComposeService::AddGlobalHtmlDomains()
{

Просмотреть файл

@ -17,32 +17,8 @@
#include "nsICommandLineHandler.h"
#define ICOMMANDLINEHANDLER nsICommandLineHandler
class nsMsgCachedWindowInfo
{
public:
void Initialize(mozIDOMWindowProxy *aWindow, nsIXULWindow *aXULWindow, nsIMsgComposeRecyclingListener *aListener, bool aHtmlCompose)
{
window = aWindow;
xulWindow = aXULWindow;
listener = aListener;
htmlCompose = aHtmlCompose;
}
void Clear()
{
window = nullptr;
listener = nullptr;
}
nsCOMPtr<mozIDOMWindowProxy> window;
nsCOMPtr<nsIXULWindow> xulWindow;
nsCOMPtr<nsIMsgComposeRecyclingListener> listener;
bool htmlCompose;
};
class nsMsgComposeService :
public nsIMsgComposeService,
public nsIObserver,
public ICOMMANDLINEHANDLER,
public nsSupportsWeakReference
{
@ -51,7 +27,6 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSIMSGCOMPOSESERVICE
NS_DECL_NSIOBSERVER
NS_DECL_NSICOMMANDLINEHANDLER
nsresult Init();
@ -63,11 +38,6 @@ private:
virtual ~nsMsgComposeService();
bool mLogComposePerformance;
int32_t mMaxRecycledWindows;
nsMsgCachedWindowInfo *mCachedWindows;
void CloseHiddenCachedWindow(mozIDOMWindowProxy *domWindow);
nsresult LoadDraftOrTemplate(const nsACString& aMsgURI, nsMimeOutputType aOutType,
nsIMsgIdentity * aIdentity, const char * aOriginalMsgURI,
nsIMsgDBHdr * aOrigMsgHdr, bool aForwardInline,
@ -84,8 +54,6 @@ private:
bool overrideComposeFormat,
nsIMsgWindow *aMsgWindow);
nsresult ShowCachedComposeWindow(mozIDOMWindowProxy *aComposeWindow, nsIXULWindow *aXULWindow, bool aShow);
// hash table mapping dom windows to nsIMsgCompose objects
nsInterfaceHashtable<nsISupportsHashKey, nsIWeakReference> mOpenComposeWindows;

Просмотреть файл

@ -71,8 +71,7 @@ function onComposerReOpen()
addEventListener("load", smimeComposeOnLoad, false);
// this function gets called multiple times,
// but only on first open, not on composer recycling
// this function gets called multiple times
function smimeComposeOnLoad()
{
removeEventListener("load", smimeComposeOnLoad, false);

Просмотреть файл

@ -732,8 +732,6 @@ pref("mailnews.ui.select_addresses_results.version", 1);
// to hide the non default columns in the advanced directory search dialog
// see abCommon.js and ABSearchDialog.js
pref("mailnews.ui.advanced_directory_search_results.version", 1);
//If set to a number greater than 0, msg compose windows will be recycled in order to open them quickly
pref("mail.compose.max_recycled_windows", 1);
// from/recipient columns will be replaced with correspondents column
pref("mailnews.ui.upgrade.correspondents", true);

Просмотреть файл

@ -143,87 +143,6 @@ function enableEditableFields()
}
var gComposeRecyclingListener = {
onClose: function() {
//Reset recipients and attachments
awResetAllRows();
RemoveAllAttachments();
// We need to clear the identity popup menu in case the user will change them. It will be rebuilded later in ComposeStartup
ClearIdentityListPopup(document.getElementById("msgIdentityPopup"));
// Stop listening to changes in the spell check dictionary.
document.removeEventListener("spellcheck-changed", updateDocumentLanguage);
// Stop InlineSpellCheckerUI so personal dictionary is saved.
// We need to do this before disabling the editor.
EnableInlineSpellCheck(false);
// clear any suggestions in the context menu
InlineSpellCheckerUI.clearSuggestionsFromMenu();
InlineSpellCheckerUI.clearDictionaryListFromMenu();
//Clear the subject
GetMsgSubjectElement().value = "";
SetComposeWindowTitle();
SetContentAndBodyAsUnmodified();
disableEditableFields();
ReleaseGlobalVariables();
// Clear the focus
awGetInputElement(1).removeAttribute('focused');
//Reset Boxes size
document.getElementById("compose-toolbox").removeAttribute("height");
document.getElementById("appcontent").removeAttribute("height");
document.getElementById("addresses-box").removeAttribute("width");
document.getElementById("attachments-box").removeAttribute("width");
//Reset menu options
document.getElementById("format_auto").setAttribute("checked", "true");
//Reset toolbars that could be hidden
if (gHideMenus) {
document.getElementById("formatMenu").hidden = false;
document.getElementById("insertMenu").hidden = false;
var showFormat = document.getElementById("menu_showFormatToolbar")
showFormat.hidden = false;
if (showFormat.getAttribute("checked") == "true")
document.getElementById("FormatToolbar").hidden = false;
}
//Reset the Customize Toolbars panel/sheet if open.
if (getMailToolbox().customizing && gCustomizeSheet)
document.getElementById("customizeToolbarSheetIFrame")
.contentWindow.finishToolbarCustomization();
//Reset editor
EditorResetFontAndColorAttributes();
EditorCleanup();
//Release the nsIMsgComposeParams object
if (window.arguments && window.arguments[0])
window.arguments[0] = null;
var event = document.createEvent('Events');
event.initEvent('compose-window-close', false, true);
document.getElementById("msgcomposeWindow").dispatchEvent(event);
if (gAutoSaveTimeout)
clearTimeout(gAutoSaveTimeout);
},
onReopen: function(params) {
// Reset focus to avoid undesirable visual effect when reopening the window
InitializeGlobalVariables();
ComposeStartup(true, params);
var event = document.createEvent('Events');
event.initEvent('compose-window-reopen', false, true);
document.getElementById("msgcomposeWindow").dispatchEvent(event);
}
};
var stateListener = {
NotifyComposeFieldsReady: function() {
ComposeFieldsReady();
@ -251,7 +170,7 @@ var stateListener = {
if (gMsgCompose)
gMsgCompose.onSendNotPerformed(null, Components.results.NS_ERROR_ABORT);
MsgComposeCloseWindow(true);
MsgComposeCloseWindow();
}
}
// else if we failed to save, and we're autosaving, need to re-mark the editor
@ -734,7 +653,7 @@ function DoCommandClose()
gMsgCompose.onSendNotPerformed(null, Components.results.NS_ERROR_ABORT);
// note: if we're not caching this window, this destroys it for us
MsgComposeCloseWindow(true);
MsgComposeCloseWindow();
}
return false;
@ -930,7 +849,7 @@ function handleMailtoArgs(mailtoUrl)
return null;
}
function ComposeStartup(recycled, aParams)
function ComposeStartup(aParams)
{
var params = null; // New way to pass parameters to the compose window as a nsIMsgComposeParameters object
var args = null; // old way, parameters are passed as a string
@ -1055,12 +974,6 @@ function ComposeStartup(recycled, aParams)
editorElement.docShell);
if (gMsgCompose)
{
// set the close listener
gMsgCompose.recyclingListener = gComposeRecyclingListener;
//Lets the compose object knows that we are dealing with a recycled window
gMsgCompose.recycledWindow = recycled;
if (!editorElement)
{
dump("Failed to get editor element!\n");
@ -1076,32 +989,28 @@ function ComposeStartup(recycled, aParams)
document.getElementById("menu_inlineSpellCheck")
.setAttribute("checked", getPref("mail.spellcheck.inline"));
// If recycle, editor is already created
if (!recycled)
try {
var editortype = gMsgCompose.composeHTML ? "htmlmail" : "textmail";
editorElement.makeEditable(editortype, true);
} catch (e) { dump(" FAILED TO START EDITOR: "+e+"\n"); }
// setEditorType MUST be call before setContentWindow
if (gMsgCompose.composeHTML)
{
try {
var editortype = gMsgCompose.composeHTML ? "htmlmail" : "textmail";
editorElement.makeEditable(editortype, true);
} catch (e) { dump(" FAILED TO START EDITOR: "+e+"\n"); }
// setEditorType MUST be call before setContentWindow
if (gMsgCompose.composeHTML)
{
initLocalFontFaceMenu(document.getElementById("FontFacePopup"));
}
else
{
//Remove HTML toolbar, format and insert menus as we are editing in plain text mode
document.getElementById("outputFormatMenu").setAttribute("hidden", true);
document.getElementById("FormatToolbar").setAttribute("hidden", true);
document.getElementById("formatMenu").setAttribute("hidden", true);
document.getElementById("insertMenu").setAttribute("hidden", true);
document.getElementById("menu_showFormatToolbar").setAttribute("hidden", true);
}
// Do setup common to Message Composer and Web Composer
EditorSharedStartup();
initLocalFontFaceMenu(document.getElementById("FontFacePopup"));
}
else
{
//Remove HTML toolbar, format and insert menus as we are editing in plain text mode
document.getElementById("outputFormatMenu").setAttribute("hidden", true);
document.getElementById("FormatToolbar").setAttribute("hidden", true);
document.getElementById("formatMenu").setAttribute("hidden", true);
document.getElementById("insertMenu").setAttribute("hidden", true);
document.getElementById("menu_showFormatToolbar").setAttribute("hidden", true);
}
// Do setup common to Message Composer and Web Composer
EditorSharedStartup();
var msgCompFields = gMsgCompose.compFields;
if (msgCompFields)
@ -1138,35 +1047,20 @@ function ComposeStartup(recycled, aParams)
gMsgCompose.RegisterStateListener(stateListener);
if (recycled)
{
InitEditor(GetCurrentEditor());
// Add an observer to be called when document is done loading,
// which creates the editor
try {
GetCurrentCommandManager().
addCommandObserver(gMsgEditorCreationObserver, "obs_documentCreated");
if (gMsgCompose.composeHTML)
{
// Force color picker on toolbar to show document colors
onFontColorChange();
onBackgroundColorChange();
// XXX todo: reset paragraph select to "Body Text"
}
}
else
{
// Add an observer to be called when document is done loading,
// which creates the editor
try {
GetCurrentCommandManager().
addCommandObserver(gMsgEditorCreationObserver, "obs_documentCreated");
// Load empty page to create the editor
editorElement.webNavigation.loadURI("about:blank", // uri string
0, // load flags
null, // referrer
null, // post-data stream
null);
} catch (e) {
dump(" Failed to startup editor: "+e+"\n");
}
// Load empty page to create the editor
editorElement.webNavigation.loadURI("about:blank", // uri string
0, // load flags
null, // referrer
null, // post-data stream
null);
} catch (e) {
dump(" Failed to startup editor: "+e+"\n");
}
}
}
@ -1228,7 +1122,7 @@ function WizCallback(state)
else
{
// The account wizard is still closing so we can't close just yet
setTimeout(MsgComposeCloseWindow, 0, false); // Don't recycle a bogus window
setTimeout(MsgComposeCloseWindow, 0);
}
}
@ -1269,7 +1163,7 @@ function ComposeLoad()
var errorMsg = sComposeMsgsBundle.getString("initErrorDlgMessage");
Services.prompt.alert(window, errorTitle, errorMsg);
MsgComposeCloseWindow(false); // Don't try to recycle a bogus window
MsgComposeCloseWindow();
return;
}
if (gLogComposePerformance)
@ -2162,10 +2056,10 @@ function SetContentAndBodyAsUnmodified()
gContentChanged = false;
}
function MsgComposeCloseWindow(recycleIt)
function MsgComposeCloseWindow()
{
if (gMsgCompose)
gMsgCompose.CloseWindow(recycleIt);
gMsgCompose.CloseWindow();
else
window.close();
}

Просмотреть файл

@ -283,7 +283,6 @@ nsThunderbirdProfileMigrator::PrefTransform gTransforms[] = {
MAKESAMETYPEPREFTRANSFORM("mail.compose.autosaveinterval", Int),
MAKESAMETYPEPREFTRANSFORM("mail.compose.dont_attach_source_of_local_network_links", Bool),
MAKESAMETYPEPREFTRANSFORM("mail.compose.dontWarnMail2Newsgroup", Bool),
MAKESAMETYPEPREFTRANSFORM("mail.compose.max_recycled_windows", Int),
MAKESAMETYPEPREFTRANSFORM("mail.compose.other.header", String),
MAKESAMETYPEPREFTRANSFORM("mail.compose.wrap_to_window_width", Bool),
MAKESAMETYPEPREFTRANSFORM("mail.content_disposition_type", Int),