summaryrefslogtreecommitdiffstats
path: root/editor/composer/nsEditingSession.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'editor/composer/nsEditingSession.cpp')
-rw-r--r--editor/composer/nsEditingSession.cpp1392
1 files changed, 1392 insertions, 0 deletions
diff --git a/editor/composer/nsEditingSession.cpp b/editor/composer/nsEditingSession.cpp
new file mode 100644
index 000000000..677b3b50f
--- /dev/null
+++ b/editor/composer/nsEditingSession.cpp
@@ -0,0 +1,1392 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=78: */
+/* 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/. */
+
+#include <string.h> // for nullptr, strcmp
+
+#include "imgIContainer.h" // for imgIContainer, etc
+#include "mozFlushType.h" // for mozFlushType::Flush_Frames
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsAString.h"
+#include "nsComponentManagerUtils.h" // for do_CreateInstance
+#include "nsComposerCommandsUpdater.h" // for nsComposerCommandsUpdater
+#include "nsDebug.h" // for NS_ENSURE_SUCCESS, etc
+#include "nsEditingSession.h"
+#include "nsError.h" // for NS_ERROR_FAILURE, NS_OK, etc
+#include "nsIChannel.h" // for nsIChannel
+#include "nsICommandManager.h" // for nsICommandManager
+#include "nsIContentViewer.h" // for nsIContentViewer
+#include "nsIController.h" // for nsIController
+#include "nsIControllerContext.h" // for nsIControllerContext
+#include "nsIControllers.h" // for nsIControllers
+#include "nsID.h" // for NS_GET_IID, etc
+#include "nsIDOMDocument.h" // for nsIDOMDocument
+#include "nsIDOMHTMLDocument.h" // for nsIDOMHTMLDocument
+#include "nsIDOMWindow.h" // for nsIDOMWindow
+#include "nsIDocShell.h" // for nsIDocShell
+#include "nsIDocument.h" // for nsIDocument
+#include "nsIDocumentStateListener.h"
+#include "nsIEditor.h" // for nsIEditor
+#include "nsIHTMLDocument.h" // for nsIHTMLDocument, etc
+#include "nsIInterfaceRequestorUtils.h" // for do_GetInterface
+#include "nsIPlaintextEditor.h" // for nsIPlaintextEditor, etc
+#include "nsIPresShell.h" // for nsIPresShell
+#include "nsIRefreshURI.h" // for nsIRefreshURI
+#include "nsIRequest.h" // for nsIRequest
+#include "nsISelection.h" // for nsISelection
+#include "nsISelectionPrivate.h" // for nsISelectionPrivate
+#include "nsITimer.h" // for nsITimer, etc
+#include "nsITransactionManager.h" // for nsITransactionManager
+#include "nsIWeakReference.h" // for nsISupportsWeakReference, etc
+#include "nsIWebNavigation.h" // for nsIWebNavigation
+#include "nsIWebProgress.h" // for nsIWebProgress, etc
+#include "nsLiteralString.h" // for NS_LITERAL_STRING
+#include "nsPICommandUpdater.h" // for nsPICommandUpdater
+#include "nsPIDOMWindow.h" // for nsPIDOMWindow
+#include "nsPresContext.h" // for nsPresContext
+#include "nsReadableUtils.h" // for AppendUTF16toUTF8
+#include "nsStringFwd.h" // for nsAFlatString
+#include "mozilla/dom/Selection.h" // for AutoHideSelectionChanges
+#include "nsFrameSelection.h" // for nsFrameSelection
+
+class nsISupports;
+class nsIURI;
+
+/*---------------------------------------------------------------------------
+
+ nsEditingSession
+
+----------------------------------------------------------------------------*/
+nsEditingSession::nsEditingSession()
+: mDoneSetup(false)
+, mCanCreateEditor(false)
+, mInteractive(false)
+, mMakeWholeDocumentEditable(true)
+, mDisabledJSAndPlugins(false)
+, mScriptsEnabled(true)
+, mPluginsEnabled(true)
+, mProgressListenerRegistered(false)
+, mImageAnimationMode(0)
+, mEditorFlags(0)
+, mEditorStatus(eEditorOK)
+, mBaseCommandControllerId(0)
+, mDocStateControllerId(0)
+, mHTMLCommandControllerId(0)
+{
+}
+
+/*---------------------------------------------------------------------------
+
+ ~nsEditingSession
+
+----------------------------------------------------------------------------*/
+nsEditingSession::~nsEditingSession()
+{
+ // Must cancel previous timer?
+ if (mLoadBlankDocTimer)
+ mLoadBlankDocTimer->Cancel();
+}
+
+NS_IMPL_ISUPPORTS(nsEditingSession, nsIEditingSession, nsIWebProgressListener,
+ nsISupportsWeakReference)
+
+/*---------------------------------------------------------------------------
+
+ MakeWindowEditable
+
+ aEditorType string, "html" "htmlsimple" "text" "textsimple"
+ void makeWindowEditable(in nsIDOMWindow aWindow, in string aEditorType,
+ in boolean aDoAfterUriLoad,
+ in boolean aMakeWholeDocumentEditable,
+ in boolean aInteractive);
+----------------------------------------------------------------------------*/
+#define DEFAULT_EDITOR_TYPE "html"
+
+NS_IMETHODIMP
+nsEditingSession::MakeWindowEditable(mozIDOMWindowProxy* aWindow,
+ const char *aEditorType,
+ bool aDoAfterUriLoad,
+ bool aMakeWholeDocumentEditable,
+ bool aInteractive)
+{
+ mEditorType.Truncate();
+ mEditorFlags = 0;
+
+ NS_ENSURE_TRUE(aWindow, NS_ERROR_FAILURE);
+ auto* window = nsPIDOMWindowOuter::From(aWindow);
+
+ // disable plugins
+ nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
+ NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
+
+ mDocShell = do_GetWeakReference(docShell);
+ mInteractive = aInteractive;
+ mMakeWholeDocumentEditable = aMakeWholeDocumentEditable;
+
+ nsresult rv;
+ if (!mInteractive) {
+ rv = DisableJSAndPlugins(aWindow);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Always remove existing editor
+ TearDownEditorOnWindow(aWindow);
+
+ // Tells embedder that startup is in progress
+ mEditorStatus = eEditorCreationInProgress;
+
+ //temporary to set editor type here. we will need different classes soon.
+ if (!aEditorType)
+ aEditorType = DEFAULT_EDITOR_TYPE;
+ mEditorType = aEditorType;
+
+ // if all this does is setup listeners and I don't need listeners,
+ // can't this step be ignored?? (based on aDoAfterURILoad)
+ rv = PrepareForEditing(window);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // set the flag on the docShell to say that it's editable
+ rv = docShell->MakeEditable(aDoAfterUriLoad);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Setup commands common to plaintext and html editors,
+ // including the document creation observers
+ // the first is an editing controller
+ rv = SetupEditorCommandController("@mozilla.org/editor/editingcontroller;1",
+ aWindow,
+ static_cast<nsIEditingSession*>(this),
+ &mBaseCommandControllerId);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // The second is a controller to monitor doc state,
+ // such as creation and "dirty flag"
+ rv = SetupEditorCommandController("@mozilla.org/editor/editordocstatecontroller;1",
+ aWindow,
+ static_cast<nsIEditingSession*>(this),
+ &mDocStateControllerId);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // aDoAfterUriLoad can be false only when making an existing window editable
+ if (!aDoAfterUriLoad) {
+ rv = SetupEditorOnWindow(aWindow);
+
+ // mEditorStatus is set to the error reason
+ // Since this is used only when editing an existing page,
+ // it IS ok to destroy current editor
+ if (NS_FAILED(rv)) {
+ TearDownEditorOnWindow(aWindow);
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsEditingSession::DisableJSAndPlugins(mozIDOMWindowProxy* aWindow)
+{
+ NS_ENSURE_TRUE(aWindow, NS_ERROR_FAILURE);
+ nsIDocShell *docShell = nsPIDOMWindowOuter::From(aWindow)->GetDocShell();
+ NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
+
+ bool tmp;
+ nsresult rv = docShell->GetAllowJavascript(&tmp);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mScriptsEnabled = tmp;
+
+ rv = docShell->SetAllowJavascript(false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Disable plugins in this document:
+ mPluginsEnabled = docShell->PluginsAllowedInCurrentDoc();
+
+ rv = docShell->SetAllowPlugins(false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mDisabledJSAndPlugins = true;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsEditingSession::RestoreJSAndPlugins(mozIDOMWindowProxy* aWindow)
+{
+ if (!mDisabledJSAndPlugins) {
+ return NS_OK;
+ }
+
+ mDisabledJSAndPlugins = false;
+
+ NS_ENSURE_TRUE(aWindow, NS_ERROR_FAILURE);
+ nsIDocShell *docShell = nsPIDOMWindowOuter::From(aWindow)->GetDocShell();
+ NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
+
+ nsresult rv = docShell->SetAllowJavascript(mScriptsEnabled);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Disable plugins in this document:
+ return docShell->SetAllowPlugins(mPluginsEnabled);
+}
+
+NS_IMETHODIMP
+nsEditingSession::GetJsAndPluginsDisabled(bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = mDisabledJSAndPlugins;
+ return NS_OK;
+}
+
+/*---------------------------------------------------------------------------
+
+ WindowIsEditable
+
+ boolean windowIsEditable (in nsIDOMWindow aWindow);
+----------------------------------------------------------------------------*/
+NS_IMETHODIMP
+nsEditingSession::WindowIsEditable(mozIDOMWindowProxy* aWindow,
+ bool *outIsEditable)
+{
+ NS_ENSURE_STATE(aWindow);
+ nsCOMPtr<nsIDocShell> docShell = nsPIDOMWindowOuter::From(aWindow)->GetDocShell();
+ NS_ENSURE_STATE(docShell);
+
+ return docShell->GetEditable(outIsEditable);
+}
+
+
+// These are MIME types that are automatically parsed as "text/plain"
+// and thus we can edit them as plaintext
+// Note: in older versions, we attempted to convert the mimetype of
+// the network channel for these and "text/xml" to "text/plain",
+// but further investigation reveals that strategy doesn't work
+const char* const gSupportedTextTypes[] = {
+ "text/plain",
+ "text/css",
+ "text/rdf",
+ "text/xsl",
+ "text/javascript", // obsolete type
+ "text/ecmascript", // obsolete type
+ "application/javascript",
+ "application/ecmascript",
+ "application/x-javascript", // obsolete type
+ "text/xul", // obsolete type
+ "application/vnd.mozilla.xul+xml",
+ nullptr // IMPORTANT! Null must be at end
+};
+
+bool
+IsSupportedTextType(const char* aMIMEType)
+{
+ NS_ENSURE_TRUE(aMIMEType, false);
+
+ for (size_t i = 0; gSupportedTextTypes[i]; ++i) {
+ if (!strcmp(gSupportedTextTypes[i], aMIMEType)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*---------------------------------------------------------------------------
+
+ SetupEditorOnWindow
+
+ nsIEditor setupEditorOnWindow (in nsIDOMWindow aWindow);
+----------------------------------------------------------------------------*/
+NS_IMETHODIMP
+nsEditingSession::SetupEditorOnWindow(mozIDOMWindowProxy* aWindow)
+{
+ mDoneSetup = true;
+
+ NS_ENSURE_TRUE(aWindow, NS_ERROR_FAILURE);
+ auto* window = nsPIDOMWindowOuter::From(aWindow);
+
+ nsresult rv;
+
+ //MIME CHECKING
+ //must get the content type
+ // Note: the doc gets this from the network channel during StartPageLoad,
+ // so we don't have to get it from there ourselves
+ nsAutoCString mimeCType;
+
+ //then lets check the mime type
+ if (nsCOMPtr<nsIDocument> doc = window->GetDoc()) {
+ nsAutoString mimeType;
+ if (NS_SUCCEEDED(doc->GetContentType(mimeType)))
+ AppendUTF16toUTF8(mimeType, mimeCType);
+
+ if (IsSupportedTextType(mimeCType.get())) {
+ mEditorType.AssignLiteral("text");
+ mimeCType = "text/plain";
+ } else if (!mimeCType.EqualsLiteral("text/html") &&
+ !mimeCType.EqualsLiteral("application/xhtml+xml")) {
+ // Neither an acceptable text or html type.
+ mEditorStatus = eEditorErrorCantEditMimeType;
+
+ // Turn editor into HTML -- we will load blank page later
+ mEditorType.AssignLiteral("html");
+ mimeCType.AssignLiteral("text/html");
+ }
+
+ // Flush out frame construction to make sure that the subframe's
+ // presshell is set up if it needs to be.
+ nsCOMPtr<nsIDocument> document = do_QueryInterface(doc);
+ if (document) {
+ document->FlushPendingNotifications(Flush_Frames);
+ if (mMakeWholeDocumentEditable) {
+ document->SetEditableFlag(true);
+ nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(document);
+ if (htmlDocument) {
+ // Enable usage of the execCommand API
+ htmlDocument->SetEditingState(nsIHTMLDocument::eDesignMode);
+ }
+ }
+ }
+ }
+ bool needHTMLController = false;
+
+ const char *classString = "@mozilla.org/editor/htmleditor;1";
+ if (mEditorType.EqualsLiteral("textmail")) {
+ mEditorFlags = nsIPlaintextEditor::eEditorPlaintextMask |
+ nsIPlaintextEditor::eEditorEnableWrapHackMask |
+ nsIPlaintextEditor::eEditorMailMask;
+ } else if (mEditorType.EqualsLiteral("text")) {
+ mEditorFlags = nsIPlaintextEditor::eEditorPlaintextMask |
+ nsIPlaintextEditor::eEditorEnableWrapHackMask;
+ } else if (mEditorType.EqualsLiteral("htmlmail")) {
+ if (mimeCType.EqualsLiteral("text/html")) {
+ needHTMLController = true;
+ mEditorFlags = nsIPlaintextEditor::eEditorMailMask;
+ } else {
+ // Set the flags back to textplain.
+ mEditorFlags = nsIPlaintextEditor::eEditorPlaintextMask |
+ nsIPlaintextEditor::eEditorEnableWrapHackMask;
+ }
+ } else {
+ // Defaulted to html
+ needHTMLController = true;
+ }
+
+ if (mInteractive) {
+ mEditorFlags |= nsIPlaintextEditor::eEditorAllowInteraction;
+ }
+
+ // make the UI state maintainer
+ mStateMaintainer = new nsComposerCommandsUpdater();
+
+ // now init the state maintainer
+ // This allows notification of error state
+ // even if we don't create an editor
+ rv = mStateMaintainer->Init(window);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (mEditorStatus != eEditorCreationInProgress) {
+ mStateMaintainer->NotifyDocumentCreated();
+ return NS_ERROR_FAILURE;
+ }
+
+ // Create editor and do other things
+ // only if we haven't found some error above,
+ nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
+ NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
+ nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
+ NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
+
+ if (!mInteractive) {
+ // Disable animation of images in this document:
+ nsPresContext* presContext = presShell->GetPresContext();
+ NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
+
+ mImageAnimationMode = presContext->ImageAnimationMode();
+ presContext->SetImageAnimationMode(imgIContainer::kDontAnimMode);
+ }
+
+ // Hide selection changes during initialization, in order to hide this
+ // from web pages.
+ RefPtr<nsFrameSelection> fs = presShell->FrameSelection();
+ NS_ENSURE_TRUE(fs, NS_ERROR_FAILURE);
+ mozilla::dom::AutoHideSelectionChanges hideSelectionChanges(fs);
+
+ // create and set editor
+ // Try to reuse an existing editor
+ nsCOMPtr<nsIEditor> editor = do_QueryReferent(mExistingEditor);
+ if (editor) {
+ editor->PreDestroy(false);
+ } else {
+ editor = do_CreateInstance(classString, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mExistingEditor = do_GetWeakReference(editor);
+ }
+ // set the editor on the docShell. The docShell now owns it.
+ rv = docShell->SetEditor(editor);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // setup the HTML editor command controller
+ if (needHTMLController) {
+ // 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 mimetype on editor
+ rv = editor->SetContentsMIMEType(mimeCType.get());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIContentViewer> contentViewer;
+ rv = docShell->GetContentViewer(getter_AddRefs(contentViewer));
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(contentViewer, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsIDOMDocument> domDoc;
+ rv = contentViewer->GetDOMDocument(getter_AddRefs(domDoc));
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE);
+
+ // Set up as a doc state listener
+ // Important! We must have this to broadcast the "obs_documentCreated" message
+ rv = editor->AddDocumentStateListener(mStateMaintainer);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = editor->Init(domDoc, nullptr /* root content */,
+ nullptr, mEditorFlags, EmptyString());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISelection> selection;
+ editor->GetSelection(getter_AddRefs(selection));
+ nsCOMPtr<nsISelectionPrivate> selPriv = do_QueryInterface(selection);
+ NS_ENSURE_TRUE(selPriv, NS_ERROR_FAILURE);
+
+ rv = selPriv->AddSelectionListener(mStateMaintainer);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // and as a transaction listener
+ nsCOMPtr<nsITransactionManager> txnMgr;
+ editor->GetTransactionManager(getter_AddRefs(txnMgr));
+ if (txnMgr) {
+ txnMgr->AddListener(mStateMaintainer);
+ }
+
+ // Set context on all controllers to be the editor
+ rv = SetEditorOnControllers(aWindow, editor);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // 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(nsPIDOMWindowOuter* aWindow,
+ nsIEditor *aEditor)
+{
+ if (!mStateMaintainer || !aEditor) {
+ return;
+ }
+
+ // Remove all the listeners
+ nsCOMPtr<nsISelection> selection;
+ aEditor->GetSelection(getter_AddRefs(selection));
+ nsCOMPtr<nsISelectionPrivate> selPriv = do_QueryInterface(selection);
+ if (selPriv)
+ selPriv->RemoveSelectionListener(mStateMaintainer);
+
+ aEditor->RemoveDocumentStateListener(mStateMaintainer);
+
+ nsCOMPtr<nsITransactionManager> 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
+
+ void tearDownEditorOnWindow (in nsIDOMWindow aWindow);
+----------------------------------------------------------------------------*/
+NS_IMETHODIMP
+nsEditingSession::TearDownEditorOnWindow(mozIDOMWindowProxy *aWindow)
+{
+ if (!mDoneSetup) {
+ return NS_OK;
+ }
+
+ NS_ENSURE_TRUE(aWindow, NS_ERROR_NULL_POINTER);
+
+ nsresult rv;
+
+ // Kill any existing reload timer
+ if (mLoadBlankDocTimer) {
+ mLoadBlankDocTimer->Cancel();
+ mLoadBlankDocTimer = nullptr;
+ }
+
+ mDoneSetup = false;
+
+ // Check if we're turning off editing (from contentEditable or designMode).
+ auto* window = nsPIDOMWindowOuter::From(aWindow);
+
+ nsCOMPtr<nsIDocument> doc = window->GetDoc();
+ nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(doc);
+ bool stopEditing = htmlDoc && htmlDoc->IsEditingOn();
+ if (stopEditing) {
+ RemoveWebProgressListener(window);
+ }
+
+ nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
+ NS_ENSURE_STATE(docShell);
+
+ nsCOMPtr<nsIEditor> editor;
+ rv = docShell->GetEditor(getter_AddRefs(editor));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (stopEditing) {
+ htmlDoc->TearingDownEditor(editor);
+ }
+
+ if (mStateMaintainer && editor) {
+ // Null out the editor on the controllers first to prevent their weak
+ // references from pointing to a destroyed editor.
+ SetEditorOnControllers(aWindow, nullptr);
+ }
+
+ // Null out the editor on the docShell to trigger PreDestroy which
+ // needs to happen before document state listeners are removed below.
+ docShell->SetEditor(nullptr);
+
+ RemoveListenersAndControllers(window, editor);
+
+ if (stopEditing) {
+ // Make things the way they were before we started editing.
+ RestoreJSAndPlugins(aWindow);
+ RestoreAnimationMode(window);
+
+ if (mMakeWholeDocumentEditable) {
+ doc->SetEditableFlag(false);
+ nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(doc);
+ if (htmlDocument) {
+ htmlDocument->SetEditingState(nsIHTMLDocument::eOff);
+ }
+ }
+ }
+
+ return rv;
+}
+
+/*---------------------------------------------------------------------------
+
+ GetEditorForFrame
+
+ nsIEditor getEditorForFrame (in nsIDOMWindow aWindow);
+----------------------------------------------------------------------------*/
+NS_IMETHODIMP
+nsEditingSession::GetEditorForWindow(mozIDOMWindowProxy* aWindow,
+ nsIEditor **outEditor)
+{
+ NS_ENSURE_STATE(aWindow);
+ nsCOMPtr<nsIDocShell> docShell = nsPIDOMWindowOuter::From(aWindow)->GetDocShell();
+ NS_ENSURE_STATE(docShell);
+
+ return docShell->GetEditor(outEditor);
+}
+
+/*---------------------------------------------------------------------------
+
+ OnStateChange
+
+----------------------------------------------------------------------------*/
+NS_IMETHODIMP
+nsEditingSession::OnStateChange(nsIWebProgress *aWebProgress,
+ nsIRequest *aRequest,
+ uint32_t aStateFlags, nsresult aStatus)
+{
+
+#ifdef NOISY_DOC_LOADING
+ nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
+ if (channel) {
+ nsAutoCString contentType;
+ channel->GetContentType(contentType);
+ if (!contentType.IsEmpty()) {
+ printf(" ++++++ MIMETYPE = %s\n", contentType.get());
+ }
+ }
+#endif
+
+ //
+ // A Request has started...
+ //
+ if (aStateFlags & nsIWebProgressListener::STATE_START) {
+#ifdef NOISY_DOC_LOADING
+ {
+ nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
+ if (channel) {
+ nsCOMPtr<nsIURI> uri;
+ channel->GetURI(getter_AddRefs(uri));
+ if (uri) {
+ nsXPIDLCString spec;
+ uri->GetSpec(spec);
+ printf(" **** STATE_START: CHANNEL URI=%s, flags=%x\n",
+ spec.get(), aStateFlags);
+ }
+ } else {
+ printf(" STATE_START: NO CHANNEL flags=%x\n", aStateFlags);
+ }
+ }
+#endif
+ // Page level notification...
+ if (aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK) {
+ nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
+ StartPageLoad(channel);
+#ifdef NOISY_DOC_LOADING
+ printf("STATE_START & STATE_IS_NETWORK flags=%x\n", aStateFlags);
+#endif
+ }
+
+ // Document level notification...
+ if (aStateFlags & nsIWebProgressListener::STATE_IS_DOCUMENT &&
+ !(aStateFlags & nsIWebProgressListener::STATE_RESTORING)) {
+#ifdef NOISY_DOC_LOADING
+ printf("STATE_START & STATE_IS_DOCUMENT flags=%x\n", aStateFlags);
+#endif
+
+ bool progressIsForTargetDocument =
+ IsProgressForTargetDocument(aWebProgress);
+
+ if (progressIsForTargetDocument) {
+ nsCOMPtr<mozIDOMWindowProxy> window;
+ aWebProgress->GetDOMWindow(getter_AddRefs(window));
+
+ auto* piWindow = nsPIDOMWindowOuter::From(window);
+ nsCOMPtr<nsIDocument> doc = piWindow->GetDoc();
+
+ nsCOMPtr<nsIHTMLDocument> htmlDoc(do_QueryInterface(doc));
+
+ if (htmlDoc && htmlDoc->IsWriting()) {
+ nsCOMPtr<nsIDOMHTMLDocument> htmlDomDoc = do_QueryInterface(doc);
+ nsAutoString designMode;
+ htmlDomDoc->GetDesignMode(designMode);
+
+ if (designMode.EqualsLiteral("on")) {
+ // This notification is for data coming in through
+ // document.open/write/close(), ignore it.
+
+ return NS_OK;
+ }
+ }
+
+ mCanCreateEditor = true;
+ StartDocumentLoad(aWebProgress, progressIsForTargetDocument);
+ }
+ }
+ }
+ //
+ // A Request is being processed
+ //
+ else if (aStateFlags & nsIWebProgressListener::STATE_TRANSFERRING) {
+ if (aStateFlags & nsIWebProgressListener::STATE_IS_DOCUMENT) {
+ // document transfer started
+ }
+ }
+ //
+ // Got a redirection
+ //
+ else if (aStateFlags & nsIWebProgressListener::STATE_REDIRECTING) {
+ if (aStateFlags & nsIWebProgressListener::STATE_IS_DOCUMENT) {
+ // got a redirect
+ }
+ }
+ //
+ // A network or document Request has finished...
+ //
+ else if (aStateFlags & nsIWebProgressListener::STATE_STOP) {
+#ifdef NOISY_DOC_LOADING
+ {
+ nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
+ if (channel) {
+ nsCOMPtr<nsIURI> uri;
+ channel->GetURI(getter_AddRefs(uri));
+ if (uri) {
+ nsXPIDLCString spec;
+ uri->GetSpec(spec);
+ printf(" **** STATE_STOP: CHANNEL URI=%s, flags=%x\n",
+ spec.get(), aStateFlags);
+ }
+ } else {
+ printf(" STATE_STOP: NO CHANNEL flags=%x\n", aStateFlags);
+ }
+ }
+#endif
+
+ // Document level notification...
+ if (aStateFlags & nsIWebProgressListener::STATE_IS_DOCUMENT) {
+ nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
+ EndDocumentLoad(aWebProgress, channel, aStatus,
+ IsProgressForTargetDocument(aWebProgress));
+#ifdef NOISY_DOC_LOADING
+ printf("STATE_STOP & STATE_IS_DOCUMENT flags=%x\n", aStateFlags);
+#endif
+ }
+
+ // Page level notification...
+ if (aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK) {
+ nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
+ (void)EndPageLoad(aWebProgress, channel, aStatus);
+#ifdef NOISY_DOC_LOADING
+ printf("STATE_STOP & STATE_IS_NETWORK flags=%x\n", aStateFlags);
+#endif
+ }
+ }
+
+ return NS_OK;
+}
+
+/*---------------------------------------------------------------------------
+
+ OnProgressChange
+
+----------------------------------------------------------------------------*/
+NS_IMETHODIMP
+nsEditingSession::OnProgressChange(nsIWebProgress *aWebProgress,
+ nsIRequest *aRequest,
+ int32_t aCurSelfProgress,
+ int32_t aMaxSelfProgress,
+ int32_t aCurTotalProgress,
+ int32_t aMaxTotalProgress)
+{
+ NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+/*---------------------------------------------------------------------------
+
+ OnLocationChange
+
+----------------------------------------------------------------------------*/
+NS_IMETHODIMP
+nsEditingSession::OnLocationChange(nsIWebProgress *aWebProgress,
+ nsIRequest *aRequest, nsIURI *aURI,
+ uint32_t aFlags)
+{
+ nsCOMPtr<mozIDOMWindowProxy> domWindow;
+ nsresult rv = aWebProgress->GetDOMWindow(getter_AddRefs(domWindow));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ auto* piWindow = nsPIDOMWindowOuter::From(domWindow);
+
+ nsCOMPtr<nsIDocument> doc = piWindow->GetDoc();
+ NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
+
+ doc->SetDocumentURI(aURI);
+
+ // Notify the location-changed observer that
+ // the document URL has changed
+ nsIDocShell *docShell = piWindow->GetDocShell();
+ NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsICommandManager> commandManager = docShell->GetCommandManager();
+ nsCOMPtr<nsPICommandUpdater> commandUpdater =
+ do_QueryInterface(commandManager);
+ NS_ENSURE_TRUE(commandUpdater, NS_ERROR_FAILURE);
+
+ return commandUpdater->CommandStatusChanged("obs_documentLocationChanged");
+}
+
+/*---------------------------------------------------------------------------
+
+ OnStatusChange
+
+----------------------------------------------------------------------------*/
+NS_IMETHODIMP
+nsEditingSession::OnStatusChange(nsIWebProgress *aWebProgress,
+ nsIRequest *aRequest,
+ nsresult aStatus,
+ const char16_t *aMessage)
+{
+ NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+/*---------------------------------------------------------------------------
+
+ OnSecurityChange
+
+----------------------------------------------------------------------------*/
+NS_IMETHODIMP
+nsEditingSession::OnSecurityChange(nsIWebProgress *aWebProgress,
+ nsIRequest *aRequest, uint32_t state)
+{
+ NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+
+/*---------------------------------------------------------------------------
+
+ IsProgressForTargetDocument
+
+ Check that this notification is for our document.
+----------------------------------------------------------------------------*/
+
+bool
+nsEditingSession::IsProgressForTargetDocument(nsIWebProgress *aWebProgress)
+{
+ nsCOMPtr<nsIWebProgress> editedWebProgress = do_QueryReferent(mDocShell);
+ return editedWebProgress == aWebProgress;
+}
+
+
+/*---------------------------------------------------------------------------
+
+ GetEditorStatus
+
+ Called during GetCommandStateParams("obs_documentCreated"...)
+ to determine if editor was created and document
+ was loaded successfully
+----------------------------------------------------------------------------*/
+NS_IMETHODIMP
+nsEditingSession::GetEditorStatus(uint32_t *aStatus)
+{
+ NS_ENSURE_ARG_POINTER(aStatus);
+ *aStatus = mEditorStatus;
+ return NS_OK;
+}
+
+/*---------------------------------------------------------------------------
+
+ StartDocumentLoad
+
+ Called on start of load in a single frame
+----------------------------------------------------------------------------*/
+nsresult
+nsEditingSession::StartDocumentLoad(nsIWebProgress *aWebProgress,
+ bool aIsToBeMadeEditable)
+{
+#ifdef NOISY_DOC_LOADING
+ printf("======= StartDocumentLoad ========\n");
+#endif
+
+ NS_ENSURE_ARG_POINTER(aWebProgress);
+
+ if (aIsToBeMadeEditable) {
+ mEditorStatus = eEditorCreationInProgress;
+ }
+
+ return NS_OK;
+}
+
+/*---------------------------------------------------------------------------
+
+ EndDocumentLoad
+
+ Called on end of load in a single frame
+----------------------------------------------------------------------------*/
+nsresult
+nsEditingSession::EndDocumentLoad(nsIWebProgress *aWebProgress,
+ nsIChannel* aChannel, nsresult aStatus,
+ bool aIsToBeMadeEditable)
+{
+ NS_ENSURE_ARG_POINTER(aWebProgress);
+
+#ifdef NOISY_DOC_LOADING
+ printf("======= EndDocumentLoad ========\n");
+ printf("with status %d, ", aStatus);
+ nsCOMPtr<nsIURI> uri;
+ nsXPIDLCString spec;
+ if (NS_SUCCEEDED(aChannel->GetURI(getter_AddRefs(uri)))) {
+ uri->GetSpec(spec);
+ printf(" uri %s\n", spec.get());
+ }
+#endif
+
+ // We want to call the base class EndDocumentLoad,
+ // but avoid some of the stuff
+ // that nsDocShell does (need to refactor).
+
+ // OK, time to make an editor on this document
+ nsCOMPtr<mozIDOMWindowProxy> domWindow;
+ aWebProgress->GetDOMWindow(getter_AddRefs(domWindow));
+ NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE);
+
+ // Set the error state -- we will create an editor
+ // anyway and load empty doc later
+ if (aIsToBeMadeEditable && aStatus == NS_ERROR_FILE_NOT_FOUND) {
+ mEditorStatus = eEditorErrorFileNotFound;
+ }
+
+ nsIDocShell *docShell = nsPIDOMWindowOuter::From(domWindow)->GetDocShell();
+ NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE); // better error handling?
+
+ // cancel refresh from meta tags
+ // we need to make sure that all pages in editor (whether editable or not)
+ // can't refresh contents being edited
+ nsCOMPtr<nsIRefreshURI> refreshURI = do_QueryInterface(docShell);
+ if (refreshURI) {
+ refreshURI->CancelRefreshURITimers();
+ }
+
+ nsresult rv = NS_OK;
+
+ // did someone set the flag to make this shell editable?
+ if (aIsToBeMadeEditable && mCanCreateEditor) {
+ bool makeEditable;
+ docShell->GetEditable(&makeEditable);
+
+ if (makeEditable) {
+ // To keep pre Gecko 1.9 behavior, setup editor always when
+ // mMakeWholeDocumentEditable.
+ bool needsSetup = false;
+ if (mMakeWholeDocumentEditable) {
+ needsSetup = true;
+ } else {
+ // do we already have an editor here?
+ nsCOMPtr<nsIEditor> editor;
+ rv = docShell->GetEditor(getter_AddRefs(editor));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ needsSetup = !editor;
+ }
+
+ if (needsSetup) {
+ mCanCreateEditor = false;
+ rv = SetupEditorOnWindow(domWindow);
+ if (NS_FAILED(rv)) {
+ // If we had an error, setup timer to load a blank page later
+ if (mLoadBlankDocTimer) {
+ // Must cancel previous timer?
+ mLoadBlankDocTimer->Cancel();
+ mLoadBlankDocTimer = nullptr;
+ }
+
+ mLoadBlankDocTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mEditorStatus = eEditorCreationInProgress;
+ mLoadBlankDocTimer->InitWithFuncCallback(
+ nsEditingSession::TimerCallback,
+ static_cast<void*> (mDocShell.get()),
+ 10, nsITimer::TYPE_ONE_SHOT);
+ }
+ }
+ }
+ }
+ return rv;
+}
+
+
+void
+nsEditingSession::TimerCallback(nsITimer* aTimer, void* aClosure)
+{
+ nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(static_cast<nsIWeakReference*> (aClosure));
+ if (docShell) {
+ nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(docShell));
+ if (webNav) {
+ webNav->LoadURI(u"about:blank", 0, nullptr, nullptr, nullptr);
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------
+
+ StartPageLoad
+
+ Called on start load of the entire page (incl. subframes)
+----------------------------------------------------------------------------*/
+nsresult
+nsEditingSession::StartPageLoad(nsIChannel *aChannel)
+{
+#ifdef NOISY_DOC_LOADING
+ printf("======= StartPageLoad ========\n");
+#endif
+ return NS_OK;
+}
+
+/*---------------------------------------------------------------------------
+
+ EndPageLoad
+
+ Called on end load of the entire page (incl. subframes)
+----------------------------------------------------------------------------*/
+nsresult
+nsEditingSession::EndPageLoad(nsIWebProgress *aWebProgress,
+ nsIChannel* aChannel, nsresult aStatus)
+{
+#ifdef NOISY_DOC_LOADING
+ printf("======= EndPageLoad ========\n");
+ printf(" with status %d, ", aStatus);
+ nsCOMPtr<nsIURI> uri;
+ nsXPIDLCString spec;
+ if (NS_SUCCEEDED(aChannel->GetURI(getter_AddRefs(uri)))) {
+ uri->GetSpec(spec);
+ printf("uri %s\n", spec.get());
+ }
+
+ nsAutoCString contentType;
+ aChannel->GetContentType(contentType);
+ if (!contentType.IsEmpty()) {
+ printf(" flags = %d, status = %d, MIMETYPE = %s\n",
+ mEditorFlags, mEditorStatus, contentType.get());
+ }
+#endif
+
+ // Set the error state -- we will create an editor anyway
+ // and load empty doc later
+ if (aStatus == NS_ERROR_FILE_NOT_FOUND) {
+ mEditorStatus = eEditorErrorFileNotFound;
+ }
+
+ nsCOMPtr<mozIDOMWindowProxy> domWindow;
+ aWebProgress->GetDOMWindow(getter_AddRefs(domWindow));
+
+ nsIDocShell *docShell =
+ domWindow ? nsPIDOMWindowOuter::From(domWindow)->GetDocShell() : nullptr;
+ NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
+
+ // cancel refresh from meta tags
+ // we need to make sure that all pages in editor (whether editable or not)
+ // can't refresh contents being edited
+ nsCOMPtr<nsIRefreshURI> refreshURI = do_QueryInterface(docShell);
+ if (refreshURI) {
+ refreshURI->CancelRefreshURITimers();
+ }
+
+#if 0
+ // Shouldn't we do this when we want to edit sub-frames?
+ return MakeWindowEditable(domWindow, "html", false, mInteractive);
+#else
+ return NS_OK;
+#endif
+}
+
+/*---------------------------------------------------------------------------
+
+ PrepareForEditing
+
+ Set up this editing session for one or more editors
+----------------------------------------------------------------------------*/
+nsresult
+nsEditingSession::PrepareForEditing(nsPIDOMWindowOuter* aWindow)
+{
+ if (mProgressListenerRegistered) {
+ return NS_OK;
+ }
+
+ nsIDocShell *docShell = aWindow ? aWindow->GetDocShell() : nullptr;
+
+ // register callback
+ nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(docShell);
+ NS_ENSURE_TRUE(webProgress, NS_ERROR_FAILURE);
+
+ nsresult rv =
+ webProgress->AddProgressListener(this,
+ (nsIWebProgress::NOTIFY_STATE_NETWORK |
+ nsIWebProgress::NOTIFY_STATE_DOCUMENT |
+ nsIWebProgress::NOTIFY_LOCATION));
+
+ mProgressListenerRegistered = NS_SUCCEEDED(rv);
+
+ return rv;
+}
+
+/*---------------------------------------------------------------------------
+
+ SetupEditorCommandController
+
+ Create a command controller, append to controllers,
+ get and return the controller ID, and set the context
+----------------------------------------------------------------------------*/
+nsresult
+nsEditingSession::SetupEditorCommandController(
+ const char *aControllerClassName,
+ mozIDOMWindowProxy *aWindow,
+ nsISupports *aContext,
+ uint32_t *aControllerId)
+{
+ NS_ENSURE_ARG_POINTER(aControllerClassName);
+ NS_ENSURE_ARG_POINTER(aWindow);
+ NS_ENSURE_ARG_POINTER(aContext);
+ NS_ENSURE_ARG_POINTER(aControllerId);
+
+ auto* piWindow = nsPIDOMWindowOuter::From(aWindow);
+ MOZ_ASSERT(piWindow);
+
+ nsCOMPtr<nsIControllers> controllers;
+ nsresult rv = piWindow->GetControllers(getter_AddRefs(controllers));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // We only have to create each singleton controller once
+ // We know this has happened once we have a controllerId value
+ if (!*aControllerId) {
+ nsCOMPtr<nsIController> controller;
+ controller = do_CreateInstance(aControllerClassName, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // We must insert at head of the list to be sure our
+ // controller is found before other implementations
+ // (e.g., not-implemented versions by browser)
+ rv = controllers->InsertControllerAt(0, controller);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Remember the ID for the controller
+ rv = controllers->GetControllerId(controller, aControllerId);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Set the context
+ return SetContextOnControllerById(controllers, aContext, *aControllerId);
+}
+
+/*---------------------------------------------------------------------------
+
+ SetEditorOnControllers
+
+ Set the editor on the controller(s) for this window
+----------------------------------------------------------------------------*/
+NS_IMETHODIMP
+nsEditingSession::SetEditorOnControllers(mozIDOMWindowProxy* aWindow,
+ nsIEditor* aEditor)
+{
+ NS_ENSURE_TRUE(aWindow, NS_ERROR_NULL_POINTER);
+
+ auto* piWindow = nsPIDOMWindowOuter::From(aWindow);
+
+ nsCOMPtr<nsIControllers> controllers;
+ nsresult rv = piWindow->GetControllers(getter_AddRefs(controllers));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISupports> editorAsISupports = do_QueryInterface(aEditor);
+ if (mBaseCommandControllerId) {
+ rv = SetContextOnControllerById(controllers, editorAsISupports,
+ mBaseCommandControllerId);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ if (mDocStateControllerId) {
+ rv = SetContextOnControllerById(controllers, editorAsISupports,
+ mDocStateControllerId);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ if (mHTMLCommandControllerId) {
+ rv = SetContextOnControllerById(controllers, editorAsISupports,
+ mHTMLCommandControllerId);
+ }
+
+ return rv;
+}
+
+nsresult
+nsEditingSession::SetContextOnControllerById(nsIControllers* aControllers,
+ nsISupports* aContext,
+ uint32_t aID)
+{
+ NS_ENSURE_ARG_POINTER(aControllers);
+
+ // aContext can be null (when destroying editor)
+ nsCOMPtr<nsIController> controller;
+ aControllers->GetControllerById(aID, getter_AddRefs(controller));
+
+ // ok with nil controller
+ nsCOMPtr<nsIControllerContext> editorController =
+ do_QueryInterface(controller);
+ NS_ENSURE_TRUE(editorController, NS_ERROR_FAILURE);
+
+ return editorController->SetCommandContext(aContext);
+}
+
+void
+nsEditingSession::RemoveEditorControllers(nsPIDOMWindowOuter* aWindow)
+{
+ // Remove editor controllers from the aWindow, call when we're
+ // tearing down/detaching editor.
+
+ nsCOMPtr<nsIControllers> controllers;
+ if (aWindow) {
+ aWindow->GetControllers(getter_AddRefs(controllers));
+ }
+
+ if (controllers) {
+ nsCOMPtr<nsIController> 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(nsPIDOMWindowOuter* aWindow)
+{
+ nsIDocShell *docShell = aWindow ? aWindow->GetDocShell() : nullptr;
+ nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(docShell);
+ if (webProgress) {
+ webProgress->RemoveProgressListener(this);
+ mProgressListenerRegistered = false;
+ }
+}
+
+void
+nsEditingSession::RestoreAnimationMode(nsPIDOMWindowOuter* aWindow)
+{
+ if (mInteractive) {
+ return;
+ }
+
+ nsCOMPtr<nsIDocShell> docShell = aWindow ? aWindow->GetDocShell() : nullptr;
+ NS_ENSURE_TRUE_VOID(docShell);
+ nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
+ NS_ENSURE_TRUE_VOID(presShell);
+ nsPresContext* presContext = presShell->GetPresContext();
+ NS_ENSURE_TRUE_VOID(presContext);
+
+ presContext->SetImageAnimationMode(mImageAnimationMode);
+}
+
+nsresult
+nsEditingSession::DetachFromWindow(mozIDOMWindowProxy* aWindow)
+{
+ NS_ENSURE_TRUE(mDoneSetup, NS_OK);
+
+ NS_ASSERTION(mStateMaintainer, "mStateMaintainer should exist.");
+
+ // Kill any existing reload timer
+ if (mLoadBlankDocTimer) {
+ mLoadBlankDocTimer->Cancel();
+ mLoadBlankDocTimer = nullptr;
+ }
+
+ auto* window = nsPIDOMWindowOuter::From(aWindow);
+
+ // Remove controllers, webprogress listener, and otherwise
+ // make things the way they were before we started editing.
+ RemoveEditorControllers(window);
+ RemoveWebProgressListener(window);
+ RestoreJSAndPlugins(aWindow);
+ RestoreAnimationMode(window);
+
+ // Kill our weak reference to our original window, in case
+ // it changes on restore, or otherwise dies.
+ mDocShell = nullptr;
+
+ return NS_OK;
+}
+
+nsresult
+nsEditingSession::ReattachToWindow(mozIDOMWindowProxy* aWindow)
+{
+ NS_ENSURE_TRUE(mDoneSetup, NS_OK);
+ NS_ENSURE_TRUE(aWindow, NS_ERROR_FAILURE);
+
+ NS_ASSERTION(mStateMaintainer, "mStateMaintainer should exist.");
+
+ // Imitate nsEditorDocShell::MakeEditable() to reattach the
+ // old editor ot the window.
+ nsresult rv;
+
+ auto* window = nsPIDOMWindowOuter::From(aWindow);
+ nsIDocShell *docShell = window->GetDocShell();
+ NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
+ mDocShell = do_GetWeakReference(docShell);
+
+ // 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(window);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Setup the command controllers again.
+ rv = SetupEditorCommandController("@mozilla.org/editor/editingcontroller;1",
+ aWindow,
+ static_cast<nsIEditingSession*>(this),
+ &mBaseCommandControllerId);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = SetupEditorCommandController("@mozilla.org/editor/editordocstatecontroller;1",
+ aWindow,
+ static_cast<nsIEditingSession*>(this),
+ &mDocStateControllerId);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (mStateMaintainer) {
+ mStateMaintainer->Init(window);
+ }
+
+ // Get editor
+ nsCOMPtr<nsIEditor> editor;
+ rv = GetEditorForWindow(aWindow, getter_AddRefs(editor));
+ NS_ENSURE_TRUE(editor, NS_ERROR_FAILURE);
+
+ if (!mInteractive) {
+ // Disable animation of images in this document:
+ nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
+ NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
+ nsPresContext* presContext = presShell->GetPresContext();
+ NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
+
+ mImageAnimationMode = presContext->ImageAnimationMode();
+ presContext->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
+ {
+ bool 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;
+}