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