/* -*- 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"mozilla/ComposerCommandsUpdater.h"// for ComposerCommandsUpdater #include"mozilla/FlushType.h"// for FlushType::Frames #include"mozilla/HTMLEditor.h"// for HTMLEditor #include"mozilla/mozalloc.h"// for operator new #include"mozilla/PresShell.h"// for PresShell #include"mozilla/Try.h"// for MOZ_TRY #include"nsAString.h" #include"nsBaseCommandController.h"// for nsBaseCommandController #include"nsCommandManager.h"// for nsCommandManager #include"nsComponentManagerUtils.h"// for do_CreateInstance #include"nsContentUtils.h" #include"nsDebug.h"// for NS_ENSURE_SUCCESS, etc #include"nsDocShell.h"// for nsDocShell #include"nsEditingSession.h" #include"nsError.h"// for NS_ERROR_FAILURE, NS_OK, etc #include"nsIChannel.h"// for nsIChannel #include"nsIDocumentViewer.h"// for nsIDocumentViewer #include"nsIControllers.h"// for nsIControllers #include"nsID.h"// for NS_GET_IID, etc #include"nsHTMLDocument.h"// for nsHTMLDocument #include"nsIDocShell.h"// for nsIDocShell #include"mozilla/dom/Document.h"// for Document #include"nsIEditor.h"// for nsIEditor #include"nsIInterfaceRequestorUtils.h"// for do_GetInterface #include"nsIRefreshURI.h"// for nsIRefreshURI #include"nsIRequest.h"// for nsIRequest #include"nsITimer.h"// for nsITimer, etc #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"nsPIDOMWindow.h"// for nsPIDOMWindow #include"nsPresContext.h"// for nsPresContext #include"nsReadableUtils.h"// for AppendUTF16toUTF8 #include"nsStringFwd.h"// for nsString #include"mozilla/dom/BrowsingContext.h"// for BrowsingContext #include"mozilla/dom/Selection.h"// for AutoHideSelectionChanges, etc #include"mozilla/dom/WindowContext.h"// for WindowContext #include"nsFrameSelection.h"// for nsFrameSelection #include"nsBaseCommandController.h"// for nsBaseCommandController #include"mozilla/dom/LoadURIOptionsBinding.h"
// 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(
nsBaseCommandController::CreateEditingController, 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(
nsBaseCommandController::CreateHTMLEditorDocStateController, 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(MOZ_KnownLive(*window));
// 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;
}
bool IsSupportedTextType(const nsAString& aMIMEType) { // 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 static constexpr nsLiteralString sSupportedTextTypes[] = {
u"text/plain"_ns,
u"text/css"_ns,
u"text/rdf"_ns,
u"text/xsl"_ns,
u"text/javascript"_ns, // obsolete type
u"text/ecmascript"_ns, // obsolete type
u"application/javascript"_ns,
u"application/ecmascript"_ns,
u"application/x-javascript"_ns, // obsolete type
u"text/xul"_ns // obsolete type
};
for (const nsLiteralString& supportedTextType : sSupportedTextTypes) { if (aMIMEType.Equals(supportedTextType)) { returntrue;
}
}
// 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
nsAutoString mimeType;
// then lets check the mime type if (RefPtr<Document> doc = aWindow.GetDoc()) {
doc->GetContentType(mimeType);
if (IsSupportedTextType(mimeType)) {
mEditorType.AssignLiteral("text");
mimeType.AssignLiteral("text/plain");
} elseif (!doc->IsHTMLOrXHTML()) { // Neither an acceptable text or html type.
mEditorStatus = eEditorErrorCantEditMimeType;
// Turn editor into HTML -- we will load blank page later
mEditorType.AssignLiteral("html");
mimeType.AssignLiteral("text/html");
}
// Flush out frame construction to make sure that the subframe's // presshell is set up if it needs to be.
doc->FlushPendingNotifications(mozilla::FlushType::Frames); if (mMakeWholeDocumentEditable) {
doc->SetDocumentEditableFlag(true); // Enable usage of the execCommand API
doc->SetEditingState(Document::EditingState::eDesignMode);
}
} bool needHTMLController = false;
if (mEditorType.EqualsLiteral("textmail")) {
mEditorFlags = nsIEditor::eEditorPlaintextMask |
nsIEditor::eEditorEnableWrapHackMask |
nsIEditor::eEditorMailMask;
} elseif (mEditorType.EqualsLiteral("text")) {
mEditorFlags =
nsIEditor::eEditorPlaintextMask | nsIEditor::eEditorEnableWrapHackMask;
} elseif (mEditorType.EqualsLiteral("htmlmail")) { if (mimeType.EqualsLiteral("text/html")) {
needHTMLController = true;
mEditorFlags = nsIEditor::eEditorMailMask;
} else { // Set the flags back to textplain.
mEditorFlags = nsIEditor::eEditorPlaintextMask |
nsIEditor::eEditorEnableWrapHackMask;
}
} else { // Defaulted to html
needHTMLController = true;
}
if (mInteractive) {
mEditorFlags |= nsIEditor::eEditorAllowInteraction;
}
// make the UI state maintainer
RefPtr<ComposerCommandsUpdater> commandsUpdater = new ComposerCommandsUpdater();
mComposerCommandsUpdater = commandsUpdater;
// now init the state maintainer // This allows notification of error state // even if we don't create an editor
commandsUpdater->Init(aWindow);
if (mEditorStatus != eEditorCreationInProgress) {
commandsUpdater->OnHTMLEditorCreated();
// At this point we have made a final decision that we don't support // editing the current document. This is an internal failure state, but // we return NS_OK to avoid throwing an exception from the designMode // setter for web compatibility. The document editing APIs will tell the // developer if editing has been disabled because we're in a document type // that doesn't support editing. return NS_OK;
}
// Create editor and do other things // only if we haven't found some error above, const RefPtr<nsDocShell> docShell = nsDocShell::Cast(aWindow.GetDocShell()); if (NS_WARN_IF(!docShell)) { return NS_ERROR_FAILURE;
} const RefPtr<PresShell> presShell = docShell->GetPresShell(); if (NS_WARN_IF(!presShell)) { return NS_ERROR_FAILURE;
}
if (!mInteractive) { // Disable animation of images in this document:
nsPresContext* presContext = presShell->GetPresContext();
NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
// 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);
AutoHideSelectionChanges hideSelectionChanges(fs);
// create and set editor // Try to reuse an existing editor
nsCOMPtr<nsIEditor> editor = do_QueryReferent(mExistingEditor);
RefPtr<HTMLEditor> htmlEditor = HTMLEditor::GetFrom(editor);
MOZ_ASSERT_IF(editor, htmlEditor); if (htmlEditor) {
htmlEditor->PreDestroy();
} else {
htmlEditor = new HTMLEditor(*doc);
mExistingEditor =
do_GetWeakReference(static_cast<nsIEditor*>(htmlEditor.get()));
} // set the editor on the docShell. The docShell now owns it.
rv = docShell->SetHTMLEditor(htmlEditor);
NS_ENSURE_SUCCESS(rv, rv);
// setup the HTML editor command controller if (needHTMLController) { // The third controller takes an nsIEditor as the context
rv = SetupEditorCommandController(
nsBaseCommandController::CreateHTMLEditorController, &aWindow, static_cast<nsIEditor*>(htmlEditor), &mHTMLCommandControllerId);
NS_ENSURE_SUCCESS(rv, rv);
}
// Set mimetype on editor
rv = htmlEditor->SetContentsMIMEType(mimeType);
NS_ENSURE_SUCCESS(rv, rv);
RefPtr<Selection> selection = htmlEditor->GetSelection(); if (NS_WARN_IF(!selection)) { return NS_ERROR_FAILURE;
}
// Set context on all controllers to be the editor
rv = SetEditorOnControllers(aWindow, htmlEditor);
NS_ENSURE_SUCCESS(rv, rv);
// Everything went fine!
mEditorStatus = eEditorOK;
// This will trigger documentCreation notification return htmlEditor->PostCreate();
}
// Removes all listeners and controllers from aWindow and aEditor. void nsEditingSession::RemoveListenersAndControllers(
nsPIDOMWindowOuter* aWindow, HTMLEditor* aHTMLEditor) { if (!mComposerCommandsUpdater || !aHTMLEditor) { return;
}
// Remove all the listeners
RefPtr<ComposerCommandsUpdater> composertCommandsUpdater =
std::move(mComposerCommandsUpdater);
MOZ_ASSERT(!mComposerCommandsUpdater);
aHTMLEditor->Detach(*composertCommandsUpdater);
// Remove editor controllers from the window now that we're not // editing in that window any more.
RemoveEditorControllers(aWindow);
}
RefPtr<HTMLEditor> htmlEditor = docShell->GetHTMLEditor(); if (stopEditing) {
doc->TearingDownEditor();
}
if (mComposerCommandsUpdater && htmlEditor) { // Null out the editor on the controllers first to prevent their weak // references from pointing to a destroyed editor.
SetEditorOnControllers(*window, 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);
if (stopEditing) { // Make things the way they were before we started editing.
RestoreJS(window->GetCurrentInnerWindow());
RestoreAnimationMode(window);
if (mMakeWholeDocumentEditable) {
doc->SetDocumentEditableFlag(false);
doc->SetEditingState(Document::EditingState::eOff);
}
}
// Notify the location-changed observer that // the document URL has changed
nsIDocShell* docShell = piWindow->GetDocShell();
NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
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;
}
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;
}
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;
nsCString 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;
}
// 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?
needsSetup = !docShell->GetHTMLEditor();
}
if (needsSetup) {
mCanCreateEditor = false;
rv = SetupEditorOnWindow(MOZ_KnownLive(*window)); 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;
}
// Set the error state -- we will create an editor anyway // and load empty doc later if (aStatus == NS_ERROR_FILE_NOT_FOUND) {
mEditorStatus = eEditorErrorFileNotFound;
}
// 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
}
Set up this editing session for one or more editors
----------------------------------------------------------------------------*/
nsresult nsEditingSession::PrepareForEditing(nsPIDOMWindowOuter* aWindow) { if (mProgressListenerRegistered) { return NS_OK;
}
Create a command controller, append to controllers, get and return the controller ID, and set the context
----------------------------------------------------------------------------*/
nsresult nsEditingSession::SetupEditorCommandController(
nsEditingSession::ControllerCreatorFn aControllerCreatorFn,
mozIDOMWindowProxy* aWindow, nsISupports* aContext,
uint32_t* aControllerId) {
NS_ENSURE_ARG_POINTER(aControllerCreatorFn);
NS_ENSURE_ARG_POINTER(aWindow);
NS_ENSURE_ARG_POINTER(aContext);
NS_ENSURE_ARG_POINTER(aControllerId);
// We only have to create each singleton controller once // We know this has happened once we have a controllerId value if (!*aControllerId) {
RefPtr<nsBaseCommandController> commandController = aControllerCreatorFn();
NS_ENSURE_TRUE(commandController, NS_ERROR_FAILURE);
// 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, commandController);
NS_ENSURE_SUCCESS(rv, rv);
// Remember the ID for the controller
rv = controllers->GetControllerId(commandController, aControllerId);
NS_ENSURE_SUCCESS(rv, rv);
}
// Set the context return SetContextOnControllerById(controllers, aContext, *aControllerId);
}
NS_ASSERTION(mComposerCommandsUpdater, "mComposerCommandsUpdater should exist.");
// Kill any existing reload timer if (mLoadBlankDocTimer) {
mLoadBlankDocTimer->Cancel();
mLoadBlankDocTimer = nullptr;
}
// Remove controllers, webprogress listener, and otherwise // make things the way they were before we started editing.
RemoveEditorControllers(aWindow);
RemoveWebProgressListener(aWindow);
RestoreJS(aWindow->GetCurrentInnerWindow());
RestoreAnimationMode(aWindow);
// Kill our weak reference to our original window, in case // it changes on restore, or otherwise dies.
mDocShell = nullptr;
// The third controller takes an nsIEditor as the context
rv = SetupEditorCommandController(
nsBaseCommandController::CreateHTMLEditorController, aWindow, static_cast<nsIEditor*>(htmlEditor.get()), &mHTMLCommandControllerId);
NS_ENSURE_SUCCESS(rv, rv);
// Set context on all controllers to be the editor
rv = SetEditorOnControllers(*aWindow, htmlEditor);
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
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.