summaryrefslogtreecommitdiffstats
path: root/editor/libeditor/HTMLEditorDocumentCommands.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--editor/libeditor/HTMLEditorDocumentCommands.cpp482
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