зеркало из https://github.com/mozilla/gecko-dev.git
455 строки
17 KiB
C++
455 строки
17 KiB
C++
/* -*- 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 "mozilla/EditorCommands.h"
|
|
|
|
#include "mozilla/EditorBase.h" // for EditorBase
|
|
#include "mozilla/HTMLEditor.h" // for HTMLEditor
|
|
#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 {
|
|
if (aCommand == Command::SetDocumentReadOnly) {
|
|
return !!aEditorBase;
|
|
}
|
|
// 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;
|
|
}
|
|
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());
|
|
}
|
|
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
|