diff options
Diffstat (limited to 'editor/libeditor/HTMLEditorDocumentCommands.cpp')
-rw-r--r-- | editor/libeditor/HTMLEditorDocumentCommands.cpp | 482 |
1 files changed, 482 insertions, 0 deletions
diff --git a/editor/libeditor/HTMLEditorDocumentCommands.cpp b/editor/libeditor/HTMLEditorDocumentCommands.cpp new file mode 100644 index 0000000000..840f4b38ee --- /dev/null +++ b/editor/libeditor/HTMLEditorDocumentCommands.cpp @@ -0,0 +1,482 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "EditorCommands.h" + +#include "EditorBase.h" // for EditorBase +#include "HTMLEditor.h" // for HTMLEditor + +#include "mozilla/BasePrincipal.h" // for nsIPrincipal::IsSystemPrincipal() +#include "mozilla/StaticPrefs_editor.h" +#include "mozilla/dom/Element.h" // for Element +#include "mozilla/dom/Document.h" // for Document +#include "mozilla/dom/HTMLInputElement.h" // for HTMLInputElement +#include "mozilla/dom/HTMLTextAreaElement.h" // for HTMLTextAreaElement + +#include "nsCommandParams.h" // for nsCommandParams +#include "nsIEditingSession.h" // for nsIEditingSession, etc +#include "nsIPrincipal.h" // for nsIPrincipal +#include "nsISupportsImpl.h" // for nsPresContext::Release +#include "nsISupportsUtils.h" // for NS_IF_ADDREF +#include "nsIURI.h" // for nsIURI +#include "nsPresContext.h" // for nsPresContext + +// defines +#define STATE_ENABLED "state_enabled" +#define STATE_ALL "state_all" +#define STATE_ATTRIBUTE "state_attribute" +#define STATE_DATA "state_data" + +namespace mozilla { + +using namespace dom; + +/***************************************************************************** + * mozilla::SetDocumentStateCommand + * + * Commands for document state that may be changed via doCommandParams + * As of 11/11/02, this is just "cmd_setDocumentModified" + * Note that you can use the same command class, SetDocumentStateCommand, + * for more than one of this type of command + * We check the input command param for different behavior + *****************************************************************************/ + +StaticRefPtr<SetDocumentStateCommand> SetDocumentStateCommand::sInstance; + +bool SetDocumentStateCommand::IsCommandEnabled(Command aCommand, + EditorBase* aEditorBase) const { + switch (aCommand) { + case Command::SetDocumentReadOnly: + return !!aEditorBase; + case Command::EnableCompatibleJoinSplitNodeDirection: + return aEditorBase && aEditorBase->IsHTMLEditor() && + aEditorBase->AsHTMLEditor()->CanChangeJoinSplitNodeDirection(); + default: + // The other commands are always enabled if given editor is an HTMLEditor. + return aEditorBase && aEditorBase->IsHTMLEditor(); + } +} + +nsresult SetDocumentStateCommand::DoCommand(Command aCommand, + EditorBase& aEditorBase, + nsIPrincipal* aPrincipal) const { + return NS_ERROR_NOT_IMPLEMENTED; +} + +nsresult SetDocumentStateCommand::DoCommandParam( + Command aCommand, const Maybe<bool>& aBoolParam, EditorBase& aEditorBase, + nsIPrincipal* aPrincipal) const { + if (NS_WARN_IF(aBoolParam.isNothing())) { + return NS_ERROR_INVALID_ARG; + } + + if (aCommand != Command::SetDocumentReadOnly && + NS_WARN_IF(!aEditorBase.IsHTMLEditor())) { + return NS_ERROR_FAILURE; + } + + switch (aCommand) { + case Command::SetDocumentModified: { + if (aBoolParam.value()) { + nsresult rv = aEditorBase.IncrementModificationCount(1); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "EditorBase::IncrementModificationCount() failed"); + return rv; + } + nsresult rv = aEditorBase.ResetModificationCount(); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "EditorBase::ResetModificationCount() failed"); + return rv; + } + case Command::SetDocumentReadOnly: { + if (aEditorBase.IsTextEditor()) { + Element* inputOrTextArea = aEditorBase.GetExposedRoot(); + if (NS_WARN_IF(!inputOrTextArea)) { + return NS_ERROR_FAILURE; + } + // Perhaps, this legacy command shouldn't work with + // `<input type="file">` and `<input type="number">. + if (inputOrTextArea->IsInNativeAnonymousSubtree()) { + return NS_ERROR_FAILURE; + } + if (RefPtr<HTMLInputElement> inputElement = + HTMLInputElement::FromNode(inputOrTextArea)) { + if (inputElement->ReadOnly() == aBoolParam.value()) { + return NS_SUCCESS_DOM_NO_OPERATION; + } + ErrorResult error; + inputElement->SetReadOnly(aBoolParam.value(), error); + return error.StealNSResult(); + } + if (RefPtr<HTMLTextAreaElement> textAreaElement = + HTMLTextAreaElement::FromNode(inputOrTextArea)) { + if (textAreaElement->ReadOnly() == aBoolParam.value()) { + return NS_SUCCESS_DOM_NO_OPERATION; + } + ErrorResult error; + textAreaElement->SetReadOnly(aBoolParam.value(), error); + return error.StealNSResult(); + } + NS_ASSERTION( + false, + "Unexpected exposed root element, fallthrough to directly make the " + "editor readonly"); + } + ErrorResult error; + if (aBoolParam.value()) { + nsresult rv = aEditorBase.AddFlags(nsIEditor::eEditorReadonlyMask); + NS_WARNING_ASSERTION( + NS_SUCCEEDED(rv), + "EditorBase::AddFlags(nsIEditor::eEditorReadonlyMask) failed"); + return rv; + } + nsresult rv = aEditorBase.RemoveFlags(nsIEditor::eEditorReadonlyMask); + NS_WARNING_ASSERTION( + NS_SUCCEEDED(rv), + "EditorBase::RemoveFlags(nsIEditor::eEditorReadonlyMask) failed"); + return rv; + } + case Command::SetDocumentUseCSS: { + nsresult rv = MOZ_KnownLive(aEditorBase.AsHTMLEditor()) + ->SetIsCSSEnabled(aBoolParam.value()); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "HTMLEditor::SetIsCSSEnabled() failed"); + return rv; + } + case Command::SetDocumentInsertBROnEnterKeyPress: { + nsresult rv = + aEditorBase.AsHTMLEditor()->SetReturnInParagraphCreatesNewParagraph( + !aBoolParam.value()); + NS_WARNING_ASSERTION( + NS_SUCCEEDED(rv), + "HTMLEditor::SetReturnInParagraphCreatesNewParagraph() failed"); + return rv; + } + case Command::ToggleObjectResizers: { + MOZ_KnownLive(aEditorBase.AsHTMLEditor()) + ->EnableObjectResizer(aBoolParam.value()); + return NS_OK; + } + case Command::ToggleInlineTableEditor: { + MOZ_KnownLive(aEditorBase.AsHTMLEditor()) + ->EnableInlineTableEditor(aBoolParam.value()); + return NS_OK; + } + case Command::ToggleAbsolutePositionEditor: { + MOZ_KnownLive(aEditorBase.AsHTMLEditor()) + ->EnableAbsolutePositionEditor(aBoolParam.value()); + return NS_OK; + } + case Command::EnableCompatibleJoinSplitNodeDirection: + MOZ_ASSERT_IF( + StaticPrefs:: + editor_join_split_direction_compatible_with_the_other_browsers() && + aPrincipal && !aPrincipal->IsSystemPrincipal(), + aBoolParam.value()); + return MOZ_KnownLive(aEditorBase.AsHTMLEditor()) + ->EnableCompatibleJoinSplitNodeDirection( + aBoolParam.value()) + ? NS_OK + : NS_SUCCESS_DOM_NO_OPERATION; + default: + return NS_ERROR_NOT_IMPLEMENTED; + } +} + +nsresult SetDocumentStateCommand::DoCommandParam( + Command aCommand, const nsACString& aCStringParam, EditorBase& aEditorBase, + nsIPrincipal* aPrincipal) const { + if (NS_WARN_IF(aCStringParam.IsVoid())) { + return NS_ERROR_INVALID_ARG; + } + + if (NS_WARN_IF(!aEditorBase.IsHTMLEditor())) { + return NS_ERROR_FAILURE; + } + + switch (aCommand) { + case Command::SetDocumentDefaultParagraphSeparator: { + if (aCStringParam.LowerCaseEqualsLiteral("div")) { + aEditorBase.AsHTMLEditor()->SetDefaultParagraphSeparator( + ParagraphSeparator::div); + return NS_OK; + } + if (aCStringParam.LowerCaseEqualsLiteral("p")) { + aEditorBase.AsHTMLEditor()->SetDefaultParagraphSeparator( + ParagraphSeparator::p); + return NS_OK; + } + if (aCStringParam.LowerCaseEqualsLiteral("br")) { + // Mozilla extension for backwards compatibility + aEditorBase.AsHTMLEditor()->SetDefaultParagraphSeparator( + ParagraphSeparator::br); + return NS_OK; + } + + // This should not be reachable from nsHTMLDocument::ExecCommand + // XXX Shouldn't return error in this case because Chrome does not throw + // exception in this case. + NS_WARNING("Invalid default paragraph separator"); + return NS_ERROR_UNEXPECTED; + } + default: + return NS_ERROR_NOT_IMPLEMENTED; + } +} + +nsresult SetDocumentStateCommand::GetCommandStateParams( + Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase, + nsIEditingSession* aEditingSession) const { + // If the result is set to STATE_ATTRIBUTE as CString value, + // queryCommandValue() returns the string value. + // Otherwise, ignored. + + // The base editor owns most state info + if (NS_WARN_IF(!aEditorBase)) { + return NS_ERROR_INVALID_ARG; + } + + if (NS_WARN_IF(!aEditorBase->IsHTMLEditor())) { + return NS_ERROR_FAILURE; + } + + // Always get the enabled state + nsresult rv = + aParams.SetBool(STATE_ENABLED, IsCommandEnabled(aCommand, aEditorBase)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + switch (aCommand) { + case Command::SetDocumentModified: { + bool modified; + rv = aEditorBase->GetDocumentModified(&modified); + if (NS_FAILED(rv)) { + NS_WARNING("EditorBase::GetDocumentModified() failed"); + return rv; + } + // XXX Nobody refers this result due to wrong type. + rv = aParams.SetBool(STATE_ATTRIBUTE, modified); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "nsCommandParams::SetBool(STATE_ATTRIBUTE) failed"); + return rv; + } + case Command::SetDocumentReadOnly: { + // XXX Nobody refers this result due to wrong type. + rv = aParams.SetBool(STATE_ATTRIBUTE, aEditorBase->IsReadonly()); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "nsCommandParams::SetBool(STATE_ATTRIBUTE) failed"); + return rv; + } + case Command::SetDocumentUseCSS: { + HTMLEditor* htmlEditor = aEditorBase->GetAsHTMLEditor(); + if (NS_WARN_IF(!htmlEditor)) { + return NS_ERROR_INVALID_ARG; + } + rv = aParams.SetBool(STATE_ALL, htmlEditor->IsCSSEnabled()); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "nsCommandParams::SetBool(STATE_ALL) failed"); + return rv; + } + case Command::SetDocumentInsertBROnEnterKeyPress: { + HTMLEditor* htmlEditor = aEditorBase->GetAsHTMLEditor(); + if (NS_WARN_IF(!htmlEditor)) { + return NS_ERROR_INVALID_ARG; + } + bool createPOnReturn; + DebugOnly<nsresult> rvIgnored = + htmlEditor->GetReturnInParagraphCreatesNewParagraph(&createPOnReturn); + NS_WARNING_ASSERTION( + NS_SUCCEEDED(rvIgnored), + "HTMLEditor::GetReturnInParagraphCreatesNewParagraph() failed"); + // XXX Nobody refers this result due to wrong type. + rv = aParams.SetBool(STATE_ATTRIBUTE, !createPOnReturn); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "nsCommandParams::SetBool(STATE_ATTRIBUTE) failed"); + return rv; + } + case Command::SetDocumentDefaultParagraphSeparator: { + HTMLEditor* htmlEditor = aEditorBase->GetAsHTMLEditor(); + if (NS_WARN_IF(!htmlEditor)) { + return NS_ERROR_INVALID_ARG; + } + + switch (htmlEditor->GetDefaultParagraphSeparator()) { + case ParagraphSeparator::div: { + DebugOnly<nsresult> rv = + aParams.SetCString(STATE_ATTRIBUTE, "div"_ns); + NS_WARNING_ASSERTION( + NS_SUCCEEDED(rv), + "Failed to set command params to return \"div\""); + return NS_OK; + } + case ParagraphSeparator::p: { + DebugOnly<nsresult> rv = aParams.SetCString(STATE_ATTRIBUTE, "p"_ns); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "Failed to set command params to return \"p\""); + return NS_OK; + } + case ParagraphSeparator::br: { + DebugOnly<nsresult> rv = aParams.SetCString(STATE_ATTRIBUTE, "br"_ns); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "Failed to set command params to return \"br\""); + return NS_OK; + } + default: + MOZ_ASSERT_UNREACHABLE("Invalid paragraph separator value"); + return NS_ERROR_UNEXPECTED; + } + } + case Command::ToggleObjectResizers: { + HTMLEditor* htmlEditor = aEditorBase->GetAsHTMLEditor(); + if (NS_WARN_IF(!htmlEditor)) { + return NS_ERROR_INVALID_ARG; + } + // We returned the result as STATE_ATTRIBUTE with bool value 60 or + // earlier. So, the result was ignored by both + // nsHTMLDocument::QueryCommandValue() and + // nsHTMLDocument::QueryCommandState(). + rv = aParams.SetBool(STATE_ALL, htmlEditor->IsObjectResizerEnabled()); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "nsCommandParams::SetBool(STATE_ALL) failed"); + return rv; + } + case Command::ToggleInlineTableEditor: { + HTMLEditor* htmlEditor = aEditorBase->GetAsHTMLEditor(); + if (NS_WARN_IF(!htmlEditor)) { + return NS_ERROR_INVALID_ARG; + } + // We returned the result as STATE_ATTRIBUTE with bool value 60 or + // earlier. So, the result was ignored by both + // nsHTMLDocument::QueryCommandValue() and + // nsHTMLDocument::QueryCommandState(). + rv = aParams.SetBool(STATE_ALL, htmlEditor->IsInlineTableEditorEnabled()); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "nsCommandParams::SetBool(STATE_ALL) failed"); + return rv; + } + case Command::ToggleAbsolutePositionEditor: { + HTMLEditor* htmlEditor = aEditorBase->GetAsHTMLEditor(); + if (NS_WARN_IF(!htmlEditor)) { + return NS_ERROR_INVALID_ARG; + } + return aParams.SetBool(STATE_ALL, + htmlEditor->IsAbsolutePositionEditorEnabled()); + } + case Command::EnableCompatibleJoinSplitNodeDirection: { + HTMLEditor* htmlEditor = aEditorBase->GetAsHTMLEditor(); + if (NS_WARN_IF(!htmlEditor)) { + return NS_ERROR_INVALID_ARG; + } + return aParams.SetBool( + STATE_ALL, htmlEditor->IsCompatibleJoinSplitNodeDirectionEnabled()); + } + default: + return NS_ERROR_NOT_IMPLEMENTED; + } +} + +/***************************************************************************** + * mozilla::DocumentStateCommand + * + * Commands just for state notification + * As of 11/21/02, possible commands are: + * "obs_documentCreated" + * "obs_documentWillBeDestroyed" + * "obs_documentLocationChanged" + * Note that you can use the same command class, DocumentStateCommand + * for these or future observer commands. + * We check the input command param for different behavior + * + * How to use: + * 1. Get the nsCommandManager for the current editor + * 2. Implement an nsIObserve object, e.g: + * + * void Observe( + * in nsISupports aSubject, // The nsCommandManager calling this + * // Observer + * in string aTopic, // command name, e.g.:"obs_documentCreated" + * // or "obs_documentWillBeDestroyed" + in wstring aData ); // ignored (set to "command_status_changed") + * + * 3. Add the observer by: + * commandManager.addObserver(observeobject, obs_documentCreated); + * 4. In the appropriate location in editorSession, editor, or commands code, + * trigger the notification of this observer by something like: + * + * RefPtr<nsCommandManager> commandManager = mDocShell->GetCommandManager(); + * commandManager->CommandStatusChanged(obs_documentCreated); + * + * 5. Use GetCommandStateParams() to obtain state information + * e.g., any creation state codes when creating an editor are + * supplied for "obs_documentCreated" command in the + * "state_data" param's value + *****************************************************************************/ + +StaticRefPtr<DocumentStateCommand> DocumentStateCommand::sInstance; + +bool DocumentStateCommand::IsCommandEnabled(Command aCommand, + EditorBase* aEditorBase) const { + // Always return false to discourage callers from using DoCommand() + return false; +} + +nsresult DocumentStateCommand::DoCommand(Command aCommand, + EditorBase& aEditorBase, + nsIPrincipal* aPrincipal) const { + return NS_ERROR_NOT_IMPLEMENTED; +} + +nsresult DocumentStateCommand::GetCommandStateParams( + Command aCommand, nsCommandParams& aParams, EditorBase* aEditorBase, + nsIEditingSession* aEditingSession) const { + switch (aCommand) { + case Command::EditorObserverDocumentCreated: { + uint32_t editorStatus = nsIEditingSession::eEditorErrorUnknown; + if (aEditingSession) { + // Current context is initially set to nsIEditingSession until editor is + // successfully created and source doc is loaded. Embedder gets error + // status if this fails. If called before startup is finished, + // status will be eEditorCreationInProgress. + nsresult rv = aEditingSession->GetEditorStatus(&editorStatus); + if (NS_FAILED(rv)) { + NS_WARNING("nsIEditingSession::GetEditorStatus() failed"); + return rv; + } + } else if (aEditorBase) { + // If current context is an editor, then everything started up OK! + editorStatus = nsIEditingSession::eEditorOK; + } + + // Note that if refCon is not-null, but is neither + // an nsIEditingSession or nsIEditor, we return "eEditorErrorUnknown" + DebugOnly<nsresult> rvIgnored = aParams.SetInt(STATE_DATA, editorStatus); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored), + "Failed to set editor status"); + return NS_OK; + } + case Command::EditorObserverDocumentLocationChanged: { + if (!aEditorBase) { + return NS_OK; + } + Document* document = aEditorBase->GetDocument(); + if (NS_WARN_IF(!document)) { + return NS_ERROR_FAILURE; + } + nsIURI* uri = document->GetDocumentURI(); + if (NS_WARN_IF(!uri)) { + return NS_ERROR_FAILURE; + } + nsresult rv = aParams.SetISupports(STATE_DATA, uri); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "nsCOmmandParms::SetISupports(STATE_DATA) failed"); + return rv; + } + default: + return NS_ERROR_NOT_IMPLEMENTED; + } +} + +} // namespace mozilla |