diff --git a/content/html/document/src/nsHTMLDocument.cpp b/content/html/document/src/nsHTMLDocument.cpp
index e5e2d5bb7bc..c42303e3fc1 100644
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -3961,6 +3961,13 @@ static PRBool HasPresShell(nsPIDOMWindow *aWindow)
return presShell != nsnull;
}
+nsresult
+nsHTMLDocument::SetEditingState(EditingState aState)
+{
+ mEditingState = aState;
+ return NS_OK;
+}
+
nsresult
nsHTMLDocument::EditingStateChanged()
{
diff --git a/content/html/document/src/nsHTMLDocument.h b/content/html/document/src/nsHTMLDocument.h
index a25da1cb774..8c25a4c0ed6 100644
--- a/content/html/document/src/nsHTMLDocument.h
+++ b/content/html/document/src/nsHTMLDocument.h
@@ -221,6 +221,8 @@ public:
mFragmentParser = aParser;
}
+ virtual nsresult SetEditingState(EditingState aState);
+
protected:
nsresult GetBodySize(PRInt32* aWidth,
PRInt32* aHeight);
diff --git a/content/html/document/src/nsIHTMLDocument.h b/content/html/document/src/nsIHTMLDocument.h
index 1c15f4616e2..583a44b33f3 100644
--- a/content/html/document/src/nsIHTMLDocument.h
+++ b/content/html/document/src/nsIHTMLDocument.h
@@ -55,11 +55,11 @@ class nsIDOMHTMLBodyElement;
class nsIScriptElement;
class nsIEditor;
-// Update htmldocument.gqi when updating this IID!
-// bfd644d6-92cc-4560-a329-f02ba0c91ca5
+// 19d63a6c-cc94-499c-892a-955add772e10
#define NS_IHTMLDOCUMENT_IID \
-{ 0xbfd644d6, 0x92cc, 0x4560, \
- { 0xa3, 0x29, 0xf0, 0x2b, 0xa0, 0xc9, 0x1c, 0xa5 } }
+{ 0x19d63a6c, 0xcc94, 0x499c, \
+ { 0x89, 0x2a, 0x95, 0x5a, 0xdd, 0x77, 0x2e, 0x10 } }
+
/**
* HTML document extensions to nsIDocument.
@@ -165,6 +165,13 @@ public:
*/
virtual EditingState GetEditingState() = 0;
+ /**
+ * Set the editing state of the document. Don't use this if you want
+ * to enable/disable editing, call EditingStateChanged() or
+ * SetDesignMode().
+ */
+ virtual nsresult SetEditingState(EditingState aState) = 0;
+
/**
* Returns the result of document.all[aID] which can either be a node
* or a nodelist depending on if there are multiple nodes with the same
diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
index b1b81d35003..78db0a9a6e5 100644
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -120,6 +120,7 @@
#include "nsCDefaultURIFixup.h"
#include "nsDocShellEnumerator.h"
#include "nsSHistory.h"
+#include "nsDocShellEditorData.h"
// Helper Classes
#include "nsDOMError.h"
@@ -306,7 +307,6 @@ nsDocShell::nsDocShell():
mDefaultScrollbarPref(Scrollbar_Auto, Scrollbar_Auto),
mPreviousTransIndex(-1),
mLoadedTransIndex(-1),
- mEditorData(nsnull),
mTreeOwner(nsnull),
mChromeEventHandler(nsnull)
#ifdef DEBUG
@@ -1013,10 +1013,10 @@ nsDocShell::FirePageHideNotification(PRBool aIsUnload)
}
}
- // Now make sure our editor, if any, is torn down before we go
+ // Now make sure our editor, if any, is detached before we go
// any farther.
if (mEditorData && aIsUnload) {
- mEditorData->TearDownEditor();
+ DetachEditorFromWindow();
}
return NS_OK;
@@ -3352,6 +3352,12 @@ nsDocShell::Reload(PRUint32 aReloadFlags)
nsnull); // No nsIRequest
}
+
+ // Need to purge detached editor here, else when we reload a page,
+ // the detached editor state causes SetDesignMode() to fail.
+ if (mOSHE)
+ mOSHE->SetEditorData(nsnull);
+
return rv;
}
@@ -3663,8 +3669,7 @@ nsDocShell::Destroy()
// Stop any URLs that are currently being loaded...
Stop(nsIWebNavigation::STOP_ALL);
- delete mEditorData;
- mEditorData = 0;
+ mEditorData = nsnull;
mTransferableHookData = nsnull;
@@ -5303,6 +5308,67 @@ nsDocShell::CanSavePresentation(PRUint32 aLoadType,
return PR_TRUE;
}
+PRBool
+nsDocShell::HasDetachedEditor()
+{
+ return (mOSHE && mOSHE->HasDetachedEditor()) ||
+ (mLSHE && mLSHE->HasDetachedEditor());
+}
+
+void
+nsDocShell::ReattachEditorToWindow(nsIDOMWindow *aWindow, nsISHEntry *aSHEntry)
+{
+ NS_ASSERTION(!mEditorData,
+ "Why reattach an editor when we already have one?");
+ NS_ASSERTION(aWindow,
+ "Need a window to reattach to.");
+ NS_ASSERTION(HasDetachedEditor(),
+ "Reattaching when there's not a detached editor.");
+
+ if (mEditorData || !aWindow || !aSHEntry)
+ return;
+
+ mEditorData = aSHEntry->ForgetEditorData();
+ if (mEditorData) {
+ nsresult res = mEditorData->ReattachToWindow(aWindow);
+ NS_ASSERTION(NS_SUCCEEDED(res), "Failed to reattach editing session");
+ }
+}
+
+void
+nsDocShell::DetachEditorFromWindow(nsISHEntry *aSHEntry)
+{
+ if (!aSHEntry || !mEditorData)
+ return;
+
+ NS_ASSERTION(!aSHEntry->HasDetachedEditor(),
+ "Why detach an editor twice?");
+
+ nsresult res = mEditorData->DetachFromWindow();
+ NS_ASSERTION(NS_SUCCEEDED(res), "Failed to detach editor");
+
+ if (NS_SUCCEEDED(res)) {
+ // Make aSHEntry hold the owning ref to the editor data.
+ aSHEntry->SetEditorData(mEditorData.forget());
+ }
+
+#ifdef DEBUG
+ {
+ PRBool isEditable;
+ GetEditable(&isEditable);
+ NS_ASSERTION(!isEditable,
+ "Window is still editable after detaching editor.");
+ }
+#endif // DEBUG
+
+}
+
+void
+nsDocShell::DetachEditorFromWindow()
+{
+ DetachEditorFromWindow(mOSHE);
+}
+
nsresult
nsDocShell::CaptureState()
{
@@ -5920,6 +5986,11 @@ nsDocShell::RestoreFromHistory()
}
}
+ if (HasDetachedEditor()) {
+ nsCOMPtr domWin = do_QueryInterface(privWin);
+ ReattachEditorToWindow(domWin, mLSHE);
+ }
+
// Simulate the completion of the load.
nsDocShell::FinishRestore();
@@ -7045,7 +7116,11 @@ nsDocShell::InternalLoad(nsIURI * aURI,
}
mLoadType = aLoadType;
-
+
+ // Detach the current editor so that it can be restored from the
+ // bfcache later.
+ DetachEditorFromWindow();
+
// mLSHE should be assigned to aSHEntry, only after Stop() has
// been called. But when loading an error page, do not clear the
// mLSHE for the real page.
@@ -7110,6 +7185,22 @@ nsDocShell::InternalLoad(nsIURI * aURI,
DisplayLoadError(rv, aURI, nsnull, chan);
}
+ if (aSHEntry) {
+ if (aLoadType & LOAD_CMD_HISTORY) {
+ // We've just loaded a page from session history. Reattach
+ // its editing session if it has one.
+ nsCOMPtr domWin;
+ CallGetInterface(this, static_cast(getter_AddRefs(domWin)));
+ ReattachEditorToWindow(domWin, aSHEntry);
+ } else {
+ // This is a non-history load from a session history entry. Purge any
+ // previous editing sessions, so that the the editing session will
+ // be recreated. This can happen when we reload something that's
+ // in the bfcache.
+ aSHEntry->SetEditorData(nsnull);
+ }
+ }
+
return rv;
}
@@ -8916,10 +9007,10 @@ nsDocShell::EnsureScriptEnvironment()
NS_IMETHODIMP
nsDocShell::EnsureEditorData()
{
- if (!mEditorData && !mIsBeingDestroyed)
- {
+ NS_ASSERTION(!HasDetachedEditor(), "EnsureEditorData() called when detached.\n");
+
+ if (!mEditorData && !mIsBeingDestroyed && !HasDetachedEditor()) {
mEditorData = new nsDocShellEditorData(this);
- if (!mEditorData) return NS_ERROR_OUT_OF_MEMORY;
}
return mEditorData ? NS_OK : NS_ERROR_NOT_AVAILABLE;
diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h
index 7a419b76224..fc23d3d7284 100644
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -525,6 +525,9 @@ protected:
// we are it's still OK to load this URI.
PRBool IsOKToLoadURI(nsIURI* aURI);
+ void ReattachEditorToWindow(nsIDOMWindow *aWindow, nsISHEntry *aSHEntry);
+ void DetachEditorFromWindow(nsISHEntry *aSHEntry);
+
protected:
// Override the parent setter from nsDocLoader
virtual nsresult SetDocLoaderParent(nsDocLoader * aLoader);
@@ -642,8 +645,8 @@ protected:
PRInt32 mPreviousTransIndex;
PRInt32 mLoadedTransIndex;
- // Editor stuff
- nsDocShellEditorData* mEditorData; // editor data, if any
+ // Editor data, if this document is designMode or contentEditable.
+ nsAutoPtr mEditorData;
// Transferable hooks/callbacks
nsCOMPtr mTransferableHookData;
@@ -674,6 +677,9 @@ protected:
static nsIURIFixup *sURIFixup;
+ // Returns true when the currently open document has a detached editor
+ // waiting to be reattached.
+ PRBool HasDetachedEditor();
public:
class InterfaceRequestorProxy : public nsIInterfaceRequestor {
diff --git a/docshell/base/nsDocShellEditorData.cpp b/docshell/base/nsDocShellEditorData.cpp
index 9b69dce9887..f0e0832bc8a 100644
--- a/docshell/base/nsDocShellEditorData.cpp
+++ b/docshell/base/nsDocShellEditorData.cpp
@@ -40,13 +40,11 @@
#include "nsIComponentManager.h"
#include "nsIInterfaceRequestorUtils.h"
-
#include "nsIDOMWindow.h"
#include "nsIDocShellTreeItem.h"
-
+#include "nsIDOMDocument.h"
#include "nsDocShellEditorData.h"
-
/*---------------------------------------------------------------------------
nsDocShellEditorData
@@ -56,6 +54,7 @@
nsDocShellEditorData::nsDocShellEditorData(nsIDocShell* inOwningDocShell)
: mDocShell(inOwningDocShell)
, mMakeEditable(PR_FALSE)
+, mIsDetached(PR_FALSE)
{
NS_ASSERTION(mDocShell, "Where is my docShell?");
}
@@ -74,18 +73,12 @@ nsDocShellEditorData::~nsDocShellEditorData()
void
nsDocShellEditorData::TearDownEditor()
{
- if (mEditingSession)
- {
- nsCOMPtr domWindow = do_GetInterface(mDocShell);
- // This will eventually call nsDocShellEditorData::SetEditor(nsnull)
- // which will call mEditorPreDestroy() and delete the editor
- mEditingSession->TearDownEditorOnWindow(domWindow);
- }
- else if (mEditor) // Should never have this w/o nsEditingSession!
- {
+ NS_ASSERTION(mIsDetached, "We should be detached before tearing down");
+ if (mEditor) {
mEditor->PreDestroy();
- mEditor = nsnull; // explicit clear to make destruction order predictable
+ mEditor = nsnull;
}
+ mEditingSession = nsnull;
}
@@ -95,7 +88,7 @@ nsDocShellEditorData::TearDownEditor()
----------------------------------------------------------------------------*/
nsresult
-nsDocShellEditorData::MakeEditable(PRBool inWaitForUriLoad /*, PRBool inEditable */)
+nsDocShellEditorData::MakeEditable(PRBool inWaitForUriLoad)
{
if (mMakeEditable)
return NS_OK;
@@ -218,6 +211,7 @@ nsresult
nsDocShellEditorData::EnsureEditingSession()
{
NS_ASSERTION(mDocShell, "Should have docShell here");
+ NS_ASSERTION(!mIsDetached, "This will stomp editing session!");
nsresult rv = NS_OK;
@@ -230,3 +224,44 @@ nsDocShellEditorData::EnsureEditingSession()
return rv;
}
+nsresult
+nsDocShellEditorData::DetachFromWindow()
+{
+ NS_ASSERTION(mEditingSession,
+ "Can't detach when we don't have a session to detach!");
+
+ nsCOMPtr domWindow = do_GetInterface(mDocShell);
+ nsresult rv = mEditingSession->DetachFromWindow(domWindow);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mIsDetached = PR_TRUE;
+ mDetachedMakeEditable = mMakeEditable;
+ mMakeEditable = PR_FALSE;
+
+ nsCOMPtr domDoc;
+ domWindow->GetDocument(getter_AddRefs(domDoc));
+ nsCOMPtr htmlDoc = do_QueryInterface(domDoc);
+ if (htmlDoc)
+ mDetachedEditingState = htmlDoc->GetEditingState();
+
+ return NS_OK;
+}
+
+nsresult
+nsDocShellEditorData::ReattachToWindow(nsIDOMWindow *aWindow)
+{
+ nsCOMPtr domWindow = do_GetInterface(mDocShell);
+ nsresult rv = mEditingSession->ReattachToWindow(domWindow);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mIsDetached = PR_FALSE;
+ mMakeEditable = mDetachedMakeEditable;
+
+ nsCOMPtr domDoc;
+ domWindow->GetDocument(getter_AddRefs(domDoc));
+ nsCOMPtr htmlDoc = do_QueryInterface(domDoc);
+ if (htmlDoc)
+ htmlDoc->SetEditingState(mDetachedEditingState);
+
+ return NS_OK;
+}
diff --git a/docshell/base/nsDocShellEditorData.h b/docshell/base/nsDocShellEditorData.h
index 80077963b4f..df34ff0e89b 100644
--- a/docshell/base/nsDocShellEditorData.h
+++ b/docshell/base/nsDocShellEditorData.h
@@ -52,53 +52,54 @@
#endif
+#include "nsIHTMLDocument.h"
#include "nsIEditor.h"
-
-// a non-XPCOM class that is used to store per-docshell editor-related
-// data.
+class nsIDOMWindow;
class nsDocShellEditorData
{
public:
- nsDocShellEditorData(nsIDocShell* inOwningDocShell);
- ~nsDocShellEditorData();
-
+ nsDocShellEditorData(nsIDocShell* inOwningDocShell);
+ ~nsDocShellEditorData();
- // set a flag to say this frame should be editable when the next url loads
- nsresult MakeEditable(PRBool inWaitForUriLoad);
-
- PRBool GetEditable();
-
- // actually create the editor for this docShell
- nsresult CreateEditor();
-
- // get the editing session. The editing session always lives on the content
- // root docShell; this call may crawl up the frame tree to find it.
- nsresult GetEditingSession(nsIEditingSession **outEditingSession);
-
- // get the editor for this docShell. May return null but NS_OK
- nsresult GetEditor(nsIEditor **outEditor);
-
- // set the editor on this docShell
- nsresult SetEditor(nsIEditor *inEditor);
-
- // Tear down the editor on this docshell, if any.
- void TearDownEditor();
-
-protected:
-
- nsresult EnsureEditingSession();
+ nsresult MakeEditable(PRBool inWaitForUriLoad);
+ PRBool GetEditable();
+ nsresult CreateEditor();
+ nsresult GetEditingSession(nsIEditingSession **outEditingSession);
+ nsresult GetEditor(nsIEditor **outEditor);
+ nsresult SetEditor(nsIEditor *inEditor);
+ void TearDownEditor();
+ nsresult DetachFromWindow();
+ nsresult ReattachToWindow(nsIDOMWindow *aWindow);
protected:
- nsIDocShell* mDocShell; // the doc shell that owns us. Weak ref, since it always outlives us.
-
- nsCOMPtr mEditingSession; // only present for the content root docShell. Session is owned here
+ nsresult EnsureEditingSession();
- PRBool mMakeEditable; // indicates whether to make an editor after a url load
- nsCOMPtr mEditor; // if this frame is editable, store editor here. Editor is owned here.
+ // The doc shell that owns us. Weak ref, since it always outlives us.
+ nsIDocShell* mDocShell;
+
+ // Only present for the content root docShell. Session is owned here.
+ nsCOMPtr mEditingSession;
+
+ // Indicates whether to make an editor after a url load.
+ PRBool mMakeEditable;
+
+ // If this frame is editable, store editor here. Editor is owned here.
+ nsCOMPtr mEditor;
+
+ // Denotes if the editor is detached from its window. The editor is detached
+ // while it's stored in the session history bfcache.
+ PRBool mIsDetached;
+
+ // Backup for mMakeEditable while the editor is detached.
+ PRBool mDetachedMakeEditable;
+
+ // Backup for the corresponding nsIHTMLDocument's editing state while
+ // the editor is detached.
+ nsIHTMLDocument::EditingState mDetachedEditingState;
};
diff --git a/docshell/base/nsIDocShell.idl b/docshell/base/nsIDocShell.idl
index 36f31898648..dfae54e4c6d 100644
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -457,5 +457,11 @@ interface nsIDocShell : nsISupports
* known JAR type).
*/
readonly attribute boolean channelIsUnsafe;
+
+ /**
+ * Disconnects this docshell's editor from its window, and stores the
+ * editor data in the open document's session history entry.
+ */
+ [noscript, notxpcom] void DetachEditorFromWindow();
};
diff --git a/docshell/shistory/public/nsISHEntry.idl b/docshell/shistory/public/nsISHEntry.idl
index 064d172aef7..b729de82c0c 100644
--- a/docshell/shistory/public/nsISHEntry.idl
+++ b/docshell/shistory/public/nsISHEntry.idl
@@ -52,8 +52,11 @@ interface nsIDocShellTreeItem;
interface nsISupportsArray;
%{C++
struct nsRect;
+class nsDocShellEditorData;
%}
[ref] native nsRect(nsRect);
+[ptr] native nsDocShellEditorDataPtr(nsDocShellEditorData);
+
[scriptable, uuid(abe54136-49e5-44ca-a749-290038c6b85d)]
interface nsISHEntry : nsIHistoryEntry
@@ -188,6 +191,23 @@ interface nsISHEntry : nsIHistoryEntry
* came from.
*/
attribute nsISupports owner;
+
+ /**
+ * Gets the owning pointer to the editor data assosicated with
+ * this shistory entry. This forgets its pointer, so free it when
+ * you're done.
+ */
+ [noscript, notxpcom] nsDocShellEditorDataPtr forgetEditorData();
+
+ /**
+ * Sets the owning pointer to the editor data assosicated with
+ * this shistory entry. Unless forgetEditorData() is called, this
+ * shentry will destroy the editor data when it's destroyed.
+ */
+ [noscript, notxpcom] void setEditorData(in nsDocShellEditorDataPtr aData);
+
+ /** Returns true if this shistory entry is storing a detached editor. */
+ [noscript, notxpcom] boolean hasDetachedEditor();
};
diff --git a/docshell/shistory/src/Makefile.in b/docshell/shistory/src/Makefile.in
index b98ff16a54c..5a6c9a9bd5e 100644
--- a/docshell/shistory/src/Makefile.in
+++ b/docshell/shistory/src/Makefile.in
@@ -59,6 +59,8 @@ REQUIRES = xpcom \
content \
widget \
nkcache \
+ editor \
+ composer \
$(NULL)
CPPSRCS = nsSHEntry.cpp \
@@ -72,3 +74,4 @@ EXTRA_DSO_LDOPTS = \
include $(topsrcdir)/config/rules.mk
+LOCAL_INCLUDES += -I$(srcdir)/../../base
diff --git a/docshell/shistory/src/nsSHEntry.cpp b/docshell/shistory/src/nsSHEntry.cpp
index 3550d7a9826..7d807bc48a1 100644
--- a/docshell/shistory/src/nsSHEntry.cpp
+++ b/docshell/shistory/src/nsSHEntry.cpp
@@ -54,6 +54,8 @@
#include "nsIWebNavigation.h"
#include "nsISHistory.h"
#include "nsISHistoryInternal.h"
+#include "nsDocShellEditorData.h"
+#include "nsIDocShell.h"
// Hardcode this to time out unused content viewers after 30 minutes
#define CONTENT_VIEWER_TIMEOUT_SECONDS 30*60
@@ -161,6 +163,8 @@ nsSHEntry::~nsSHEntry()
viewer->Destroy();
}
+ mEditorData = nsnull;
+
#ifdef DEBUG
// This is not happening as far as I can tell from breakpad as of early November 2007
nsExpirationTracker::Iterator iterator(gHistoryTracker);
@@ -833,3 +837,25 @@ nsSHEntry::DocumentMutated()
// Warning! The call to DropPresentationState could have dropped the last
// reference to this nsSHEntry, so no accessing members beyond here.
}
+
+nsDocShellEditorData*
+nsSHEntry::ForgetEditorData()
+{
+ return mEditorData.forget();
+}
+
+void
+nsSHEntry::SetEditorData(nsDocShellEditorData* aData)
+{
+ NS_ASSERTION(!(aData && mEditorData),
+ "We're going to overwrite an owning ref!");
+ if (mEditorData != aData)
+ mEditorData = aData;
+}
+
+PRBool
+nsSHEntry::HasDetachedEditor()
+{
+ return mEditorData != nsnull;
+}
+
diff --git a/docshell/shistory/src/nsSHEntry.h b/docshell/shistory/src/nsSHEntry.h
index 95de45337fd..e87c6f4a99f 100644
--- a/docshell/shistory/src/nsSHEntry.h
+++ b/docshell/shistory/src/nsSHEntry.h
@@ -45,6 +45,7 @@
#include "nsCOMArray.h"
#include "nsString.h"
#include "nsVoidArray.h"
+#include "nsAutoPtr.h"
// Interfaces needed
#include "nsIContentViewer.h"
@@ -59,6 +60,7 @@
#include "nsSupportsArray.h"
#include "nsIMutationObserver.h"
#include "nsExpirationTracker.h"
+#include "nsDocShellEditorData.h"
class nsSHEntry : public nsISHEntry,
public nsISHContainer,
@@ -113,6 +115,7 @@ private:
nsCOMPtr mRefreshURIList;
nsCOMPtr mOwner;
nsExpirationState mExpirationState;
+ nsAutoPtr mEditorData;
};
#endif /* nsSHEntry_h */
diff --git a/docshell/test/navigation/Makefile.in b/docshell/test/navigation/Makefile.in
index 69817c8055a..bc8cbf436df 100644
--- a/docshell/test/navigation/Makefile.in
+++ b/docshell/test/navigation/Makefile.in
@@ -49,6 +49,7 @@ _TEST_FILES = \
test_bug270414.html \
test_bug278916.html \
test_bug279495.html \
+ test_bug386782.html \
test_child.html \
test_grandchild.html \
test_sibling-off-domain.html \
diff --git a/docshell/test/navigation/test_bug386782.html b/docshell/test/navigation/test_bug386782.html
new file mode 100644
index 00000000000..f255a857a78
--- /dev/null
+++ b/docshell/test/navigation/test_bug386782.html
@@ -0,0 +1,136 @@
+
+
+
+
+ Test for Bug 386782
+
+
+
+
+
+
+
+
+
+Mozilla Bug 386782
+
+
+
+
+
+
+
+
+
+
diff --git a/editor/composer/public/nsIEditingSession.idl b/editor/composer/public/nsIEditingSession.idl
index dd1a3181d31..d27715b851e 100644
--- a/editor/composer/public/nsIEditingSession.idl
+++ b/editor/composer/public/nsIEditingSession.idl
@@ -116,5 +116,17 @@ interface nsIEditingSession : nsISupports
* were before the last call to disableJSAndPlugins.
*/
void restoreJSAndPlugins(in nsIDOMWindow aWindow);
+
+ /**
+ * Removes all the editor's controllers/listeners etc and makes the window
+ * uneditable.
+ */
+ void detachFromWindow(in nsIDOMWindow aWindow);
+
+ /**
+ * Undos detachFromWindow(), reattaches this editing session/editor
+ * to the window.
+ */
+ void reattachToWindow(in nsIDOMWindow aWindow);
};
diff --git a/editor/composer/src/nsEditingSession.cpp b/editor/composer/src/nsEditingSession.cpp
index 3110ba142c4..c00bed5a953 100644
--- a/editor/composer/src/nsEditingSession.cpp
+++ b/editor/composer/src/nsEditingSession.cpp
@@ -248,6 +248,9 @@ nsEditingSession::DisableJSAndPlugins(nsIDOMWindow *aWindow)
NS_IMETHODIMP
nsEditingSession::RestoreJSAndPlugins(nsIDOMWindow *aWindow)
{
+ if (!mDisabledJSAndPlugins)
+ return NS_OK;
+
mDisabledJSAndPlugins = PR_FALSE;
nsIDocShell *docShell = GetDocShellFromWindow(aWindow);
@@ -410,26 +413,17 @@ nsEditingSession::SetupEditorOnWindow(nsIDOMWindow *aWindow)
}
// make the UI state maintainer
- nsComposerCommandsUpdater *stateMaintainer;
- NS_NEWXPCOM(stateMaintainer, nsComposerCommandsUpdater);
- mStateMaintainer = static_cast(stateMaintainer);
-
- if (!mStateMaintainer) return NS_ERROR_OUT_OF_MEMORY;
+ mStateMaintainer = new nsComposerCommandsUpdater();
// now init the state maintainer
// This allows notification of error state
// even if we don't create an editor
- rv = stateMaintainer->Init(aWindow);
+ rv = mStateMaintainer->Init(aWindow);
if (NS_FAILED(rv)) return rv;
if (mEditorStatus != eEditorCreationInProgress)
{
- // We had an earlier error -- force notification of document creation
- nsCOMPtr docListener =
- do_QueryInterface(mStateMaintainer);
- if (docListener)
- docListener->NotifyDocumentCreated();
-
+ mStateMaintainer->NotifyDocumentCreated();
return NS_ERROR_FAILURE;
}
@@ -484,8 +478,7 @@ nsEditingSession::SetupEditorOnWindow(nsIDOMWindow *aWindow)
// Set up as a doc state listener
// Important! We must have this to broadcast the "obs_documentCreated" message
- rv = editor->AddDocumentStateListener(
- static_cast(stateMaintainer));
+ rv = editor->AddDocumentStateListener(mStateMaintainer);
if (NS_FAILED(rv)) return rv;
// XXXbz we really shouldn't need a presShell here!
@@ -504,15 +497,14 @@ nsEditingSession::SetupEditorOnWindow(nsIDOMWindow *aWindow)
nsCOMPtr selPriv = do_QueryInterface(selection);
if (!selPriv) return NS_ERROR_FAILURE;
- rv = selPriv->AddSelectionListener(stateMaintainer);
+ rv = selPriv->AddSelectionListener(mStateMaintainer);
if (NS_FAILED(rv)) return rv;
// and as a transaction listener
nsCOMPtr txnMgr;
editor->GetTransactionManager(getter_AddRefs(txnMgr));
if (txnMgr)
- txnMgr->AddListener(static_cast
- (stateMaintainer));
+ txnMgr->AddListener(mStateMaintainer);
// Set context on all controllers to be the editor
rv = SetEditorOnControllers(aWindow, editor);
@@ -521,11 +513,37 @@ nsEditingSession::SetupEditorOnWindow(nsIDOMWindow *aWindow)
// Everything went fine!
mEditorStatus = eEditorOK;
-
// This will trigger documentCreation notification
return editor->PostCreate();
}
+// Removes all listeners and controllers from aWindow and aEditor.
+void
+nsEditingSession::RemoveListenersAndControllers(nsIDOMWindow *aWindow,
+ nsIEditor *aEditor)
+{
+ if (!mStateMaintainer || !aEditor)
+ return;
+
+ // Remove all the listeners
+ nsCOMPtr selection;
+ aEditor->GetSelection(getter_AddRefs(selection));
+ nsCOMPtr selPriv = do_QueryInterface(selection);
+ if (selPriv)
+ selPriv->RemoveSelectionListener(mStateMaintainer);
+
+ aEditor->RemoveDocumentStateListener(mStateMaintainer);
+
+ nsCOMPtr txnMgr;
+ aEditor->GetTransactionManager(getter_AddRefs(txnMgr));
+ if (txnMgr)
+ txnMgr->RemoveListener(mStateMaintainer);
+
+ // Remove editor controllers from the window now that we're not
+ // editing in that window any more.
+ RemoveEditorControllers(aWindow);
+}
+
/*---------------------------------------------------------------------------
TearDownEditorOnWindow
@@ -538,6 +556,9 @@ nsEditingSession::TearDownEditorOnWindow(nsIDOMWindow *aWindow)
if (!mDoneSetup)
return NS_OK;
+ if (!aWindow)
+ return NS_ERROR_NULL_POINTER;
+
nsresult rv;
// Kill any existing reload timer
@@ -547,8 +568,6 @@ nsEditingSession::TearDownEditorOnWindow(nsIDOMWindow *aWindow)
mLoadBlankDocTimer = nsnull;
}
- nsIDocShell *docShell = GetDocShellFromWindow(aWindow);
-
mDoneSetup = PR_FALSE;
// Check if we're turning off editing (from contentEditable or designMode).
@@ -556,14 +575,8 @@ nsEditingSession::TearDownEditorOnWindow(nsIDOMWindow *aWindow)
aWindow->GetDocument(getter_AddRefs(domDoc));
nsCOMPtr htmlDoc = do_QueryInterface(domDoc);
PRBool stopEditing = htmlDoc && htmlDoc->IsEditingOn();
- if (stopEditing) {
- nsCOMPtr webProgress = do_GetInterface(docShell);
- if (webProgress) {
- webProgress->RemoveProgressListener(this);
-
- mProgressListenerRegistered = PR_FALSE;
- }
- }
+ if (stopEditing)
+ RemoveWebProgressListener(aWindow);
nsCOMPtr editorDocShell;
rv = GetEditorDocShellFromWindow(aWindow, getter_AddRefs(editorDocShell));
@@ -576,107 +589,27 @@ nsEditingSession::TearDownEditorOnWindow(nsIDOMWindow *aWindow)
if (stopEditing)
htmlDoc->TearingDownEditor(editor);
- // null out the editor on the controllers first to prevent their weak
- // references from pointing to a destroyed editor
if (mStateMaintainer && editor)
{
- // null out the editor on the controllers
+ // Null out the editor on the controllers first to prevent their weak
+ // references from pointing to a destroyed editor.
SetEditorOnControllers(aWindow, nsnull);
}
- // null out the editor on the docShell to trigger PreDestroy which
- // needs to happen before document state listeners are removed below
+ // Null out the editor on the docShell to trigger PreDestroy which
+ // needs to happen before document state listeners are removed below.
editorDocShell->SetEditor(nsnull);
- if (mStateMaintainer)
+ RemoveListenersAndControllers(aWindow, editor);
+
+ if (stopEditing)
{
- if (editor)
+ // Make things the way they were before we started editing.
+ RestoreJSAndPlugins(aWindow);
+ RestoreAnimationMode(aWindow);
+
+ if (mMakeWholeDocumentEditable)
{
- // If we had an editor -- we are loading a new URL into existing window
-
- // Remove all the listeners
- nsCOMPtr selection;
- editor->GetSelection(getter_AddRefs(selection));
- nsCOMPtr selPriv = do_QueryInterface(selection);
- if (selPriv)
- {
- nsCOMPtr listener =
- do_QueryInterface(mStateMaintainer);
- selPriv->RemoveSelectionListener(listener);
- }
-
- nsCOMPtr docListener =
- do_QueryInterface(mStateMaintainer);
- editor->RemoveDocumentStateListener(docListener);
-
- nsCOMPtr txnMgr;
- editor->GetTransactionManager(getter_AddRefs(txnMgr));
- if (txnMgr)
- {
- nsCOMPtr transactionListener =
- do_QueryInterface(mStateMaintainer);
- txnMgr->RemoveListener(transactionListener);
- }
- }
-
- // Remove editor controllers from the window now that we're not
- // editing in that window any more.
-
- nsCOMPtr domWindowInt(do_QueryInterface(aWindow));
-
- nsCOMPtr controllers;
- domWindowInt->GetControllers(getter_AddRefs(controllers));
-
- if (controllers) {
- nsCOMPtr controller;
-
- if (mBaseCommandControllerId) {
- controllers->GetControllerById(mBaseCommandControllerId,
- getter_AddRefs(controller));
-
- if (controller) {
- controllers->RemoveController(controller);
- }
- }
-
- if (mDocStateControllerId) {
- controllers->GetControllerById(mDocStateControllerId,
- getter_AddRefs(controller));
-
- if (controller) {
- controllers->RemoveController(controller);
- }
- }
-
- if (mHTMLCommandControllerId) {
- controllers->GetControllerById(mHTMLCommandControllerId,
- getter_AddRefs(controller));
-
- if (controller) {
- controllers->RemoveController(controller);
- }
- }
- }
-
- // Clear IDs to trigger creation of new controllers
- mBaseCommandControllerId = 0;
- mDocStateControllerId = 0;
- mHTMLCommandControllerId = 0;
- }
-
- if (stopEditing) {
- if (mDisabledJSAndPlugins) {
- // Make things the way they were before we started editing.
- RestoreJSAndPlugins(aWindow);
- }
-
- if (!mInteractive) {
- nsCOMPtr utils(do_GetInterface(aWindow));
- if (utils)
- utils->SetImageAnimationMode(mImageAnimationMode);
- }
-
- if (mMakeWholeDocumentEditable) {
nsCOMPtr doc = do_QueryInterface(domDoc, &rv);
NS_ENSURE_SUCCESS(rv, rv);
@@ -1020,7 +953,9 @@ nsEditingSession::StartDocumentLoad(nsIWebProgress *aWebProgress,
aWebProgress->GetDOMWindow(getter_AddRefs(domWindow));
if (domWindow)
{
- TearDownEditorOnWindow(domWindow);
+ nsIDocShell *docShell = GetDocShellFromWindow(domWindow);
+ if (!docShell) return NS_ERROR_FAILURE;
+ docShell->DetachEditorFromWindow();
}
if (aIsToBeMadeEditable)
@@ -1387,3 +1322,179 @@ nsEditingSession::SetContextOnControllerById(nsIControllers* aControllers,
return editorController->SetCommandContext(aContext);
}
+
+void
+nsEditingSession::RemoveEditorControllers(nsIDOMWindow *aWindow)
+{
+ // Remove editor controllers from the aWindow, call when we're
+ // tearing down/detaching editor.
+ nsCOMPtr domWindowInt(do_QueryInterface(aWindow));
+
+ nsCOMPtr controllers;
+ if (domWindowInt)
+ domWindowInt->GetControllers(getter_AddRefs(controllers));
+
+ if (controllers)
+ {
+ nsCOMPtr controller;
+ if (mBaseCommandControllerId)
+ {
+ controllers->GetControllerById(mBaseCommandControllerId,
+ getter_AddRefs(controller));
+ if (controller)
+ controllers->RemoveController(controller);
+ }
+
+ if (mDocStateControllerId)
+ {
+ controllers->GetControllerById(mDocStateControllerId,
+ getter_AddRefs(controller));
+ if (controller)
+ controllers->RemoveController(controller);
+ }
+
+ if (mHTMLCommandControllerId)
+ {
+ controllers->GetControllerById(mHTMLCommandControllerId,
+ getter_AddRefs(controller));
+ if (controller)
+ controllers->RemoveController(controller);
+ }
+ }
+
+ // Clear IDs to trigger creation of new controllers.
+ mBaseCommandControllerId = 0;
+ mDocStateControllerId = 0;
+ mHTMLCommandControllerId = 0;
+}
+
+void
+nsEditingSession::RemoveWebProgressListener(nsIDOMWindow *aWindow)
+{
+ nsIDocShell *docShell = GetDocShellFromWindow(aWindow);
+ nsCOMPtr webProgress = do_GetInterface(docShell);
+ if (webProgress)
+ {
+ webProgress->RemoveProgressListener(this);
+ mProgressListenerRegistered = PR_FALSE;
+ }
+}
+
+void
+nsEditingSession::RestoreAnimationMode(nsIDOMWindow *aWindow)
+{
+ if (!mInteractive)
+ {
+ nsCOMPtr utils(do_GetInterface(aWindow));
+ if (utils)
+ utils->SetImageAnimationMode(mImageAnimationMode);
+ }
+}
+
+nsresult
+nsEditingSession::DetachFromWindow(nsIDOMWindow* aWindow)
+{
+ NS_ASSERTION(mEditorFlags != 0, "mEditorFlags should not be 0");
+ NS_ASSERTION(mStateMaintainer, "mStateMaintainer should exist.");
+
+ // Kill any existing reload timer
+ if (mLoadBlankDocTimer)
+ {
+ mLoadBlankDocTimer->Cancel();
+ mLoadBlankDocTimer = nsnull;
+ }
+
+ // Remove controllers, webprogress listener, and otherwise
+ // make things the way they were before we started editing.
+ RemoveEditorControllers(aWindow);
+ RemoveWebProgressListener(aWindow);
+ RestoreJSAndPlugins(aWindow);
+ RestoreAnimationMode(aWindow);
+
+ // Kill our weak reference to our original window, in case
+ // it changes on restore, or otherwise dies.
+ mWindowToBeEdited = nsnull;
+
+ return NS_OK;
+}
+
+nsresult
+nsEditingSession::ReattachToWindow(nsIDOMWindow* aWindow)
+{
+ NS_ASSERTION(mEditorFlags != 0, "mEditorFlags should still be valid...");
+ NS_ASSERTION(mStateMaintainer, "mStateMaintainer should exist.");
+
+ // Imitate nsEditorDocShell::MakeEditable() to reattach the
+ // old editor ot the window.
+ nsresult rv;
+
+ mWindowToBeEdited = do_GetWeakReference(aWindow);
+
+ // Disable plugins.
+ if (!mInteractive)
+ {
+ rv = DisableJSAndPlugins(aWindow);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Tells embedder that startup is in progress.
+ mEditorStatus = eEditorCreationInProgress;
+
+ // Adds back web progress listener.
+ rv = PrepareForEditing(aWindow);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Setup the command controllers again.
+ rv = SetupEditorCommandController("@mozilla.org/editor/editorcontroller;1",
+ aWindow,
+ static_cast(this),
+ &mBaseCommandControllerId);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = SetupEditorCommandController("@mozilla.org/editor/editordocstatecontroller;1",
+ aWindow,
+ static_cast(this),
+ &mDocStateControllerId);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (mStateMaintainer)
+ mStateMaintainer->Init(aWindow);
+
+ // Get editor
+ nsCOMPtr editor;
+ rv = GetEditorForWindow(aWindow, getter_AddRefs(editor));
+ if (!editor)
+ return NS_ERROR_FAILURE;
+
+ if (!mInteractive)
+ {
+ // Disable animation of images in this document:
+ nsCOMPtr utils(do_GetInterface(aWindow));
+ if (!utils) return NS_ERROR_FAILURE;
+
+ rv = utils->GetImageAnimationMode(&mImageAnimationMode);
+ NS_ENSURE_SUCCESS(rv, rv);
+ utils->SetImageAnimationMode(imgIContainer::kDontAnimMode);
+ }
+
+ // The third controller takes an nsIEditor as the context
+ rv = SetupEditorCommandController("@mozilla.org/editor/htmleditorcontroller;1",
+ aWindow, editor,
+ &mHTMLCommandControllerId);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Set context on all controllers to be the editor
+ rv = SetEditorOnControllers(aWindow, editor);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+#ifdef DEBUG
+ {
+ PRBool isEditable;
+ rv = WindowIsEditable(aWindow, &isEditable);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ASSERTION(isEditable, "Window is not editable after reattaching editor.");
+ }
+#endif // DEBUG
+
+ return NS_OK;
+}
diff --git a/editor/composer/src/nsEditingSession.h b/editor/composer/src/nsEditingSession.h
index fa4e48c2a8c..dc870d7ddba 100644
--- a/editor/composer/src/nsEditingSession.h
+++ b/editor/composer/src/nsEditingSession.h
@@ -45,6 +45,7 @@
#endif
#include "nsITimer.h"
+#include "nsAutoPtr.h"
#ifndef __gen_nsIWebProgressListener_h__
#include "nsIWebProgressListener.h"
@@ -119,6 +120,12 @@ protected:
PRBool IsProgressForTargetDocument(nsIWebProgress *aWebProgress);
+ void RemoveEditorControllers(nsIDOMWindow *aWindow);
+ void RemoveWebProgressListener(nsIDOMWindow *aWindow);
+ void RestoreAnimationMode(nsIDOMWindow *aWindow);
+ void RemoveListenersAndControllers(nsIDOMWindow *aWindow,
+ nsIEditor *aEditor);
+
protected:
PRPackedBool mDoneSetup; // have we prepared for editing yet?
@@ -149,7 +156,7 @@ protected:
// THE REMAINING MEMBER VARIABLES WILL BECOME A SET WHEN WE EDIT
// MORE THAN ONE EDITOR PER EDITING SESSION
- nsCOMPtr mStateMaintainer; // we hold the owning ref to this
+ nsRefPtr mStateMaintainer;
nsWeakPtr mWindowToBeEdited;