gecko-dev/editor/libeditor/TextEditRules.cpp

1482 строки
43 KiB
C++
Исходник Обычный вид История

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2012-05-21 15:12:37 +04:00
/* 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/TextEditRules.h"
#include "TextEditUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/EditorUtils.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/Preferences.h"
#include "mozilla/TextComposition.h"
#include "mozilla/TextEditor.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/NodeIterator.h"
#include "mozilla/dom/Selection.h"
#include "nsAString.h"
#include "nsCOMPtr.h"
#include "nsCRT.h"
#include "nsCRTGlue.h"
#include "nsComponentManagerUtils.h"
#include "nsContentUtils.h"
#include "nsDebug.h"
#include "nsError.h"
#include "nsGkAtoms.h"
#include "nsIContent.h"
#include "nsIDOMDocument.h"
#include "nsIDOMElement.h"
#include "nsIDOMNode.h"
#include "nsIDOMNodeFilter.h"
#include "nsIDOMNodeIterator.h"
#include "nsIDOMNodeList.h"
#include "nsIDOMText.h"
#include "nsNameSpaceManager.h"
#include "nsINode.h"
#include "nsIPlaintextEditor.h"
#include "nsISupportsBase.h"
#include "nsLiteralString.h"
#include "nsUnicharUtils.h"
#include "nsIHTMLCollection.h"
namespace mozilla {
using namespace dom;
Preparation for ender-based text control * added focus listener. Doesn't do much yet, but when focus notifications start appearing, we'll be ready for them. The code is in place to hide selection when we lose focus and paint selection when we get focus. That's probably not quite right, but it's a start. We will need to be able to determine the distinction between losing focus to another control within our app, and losing focus to another app. * added support for disabled and readonly states in the editor. This is accomplished by having flags set by the client, and letting the rules system deal with those flags. The flags I added are: TEXT_EDITOR_FLAG_PLAINTEXT 0x01 // only plain text editing is allowed TEXT_EDITOR_FLAG_SINGLELINE 0x02 // enter key and CR-LF handled specially TEXT_EDITOR_FLAG_PASSWORD 0x04 // text is not entered into content, only a representative character TEXT_EDITOR_FLAG_READONLY 0x08 // editing events are disabled. Editor may still accept focus. TEXT_EDITOR_FLAG_DISALBED 0x10 // all events are disabled (like scrolling). Editor will not accept focus. * added WillInsertBreak/DidInsertBreak into text rules, so flags could be checked. This gets us readonly, disabled, and single line behavior. * cleaned up the code that allocates, registers, and destroys event listeners. Thanks to Kin and Simon for cleaning up the ownership model on the listeners, it was a big help. * added support for a max text length. You can now tell the text editor, be no bigger than n characters.
1999-05-29 01:24:18 +04:00
#define CANCEL_OPERATION_IF_READONLY_OR_DISABLED \
if (IsReadonly() || IsDisabled()) \
Preparation for ender-based text control * added focus listener. Doesn't do much yet, but when focus notifications start appearing, we'll be ready for them. The code is in place to hide selection when we lose focus and paint selection when we get focus. That's probably not quite right, but it's a start. We will need to be able to determine the distinction between losing focus to another control within our app, and losing focus to another app. * added support for disabled and readonly states in the editor. This is accomplished by having flags set by the client, and letting the rules system deal with those flags. The flags I added are: TEXT_EDITOR_FLAG_PLAINTEXT 0x01 // only plain text editing is allowed TEXT_EDITOR_FLAG_SINGLELINE 0x02 // enter key and CR-LF handled specially TEXT_EDITOR_FLAG_PASSWORD 0x04 // text is not entered into content, only a representative character TEXT_EDITOR_FLAG_READONLY 0x08 // editing events are disabled. Editor may still accept focus. TEXT_EDITOR_FLAG_DISALBED 0x10 // all events are disabled (like scrolling). Editor will not accept focus. * added WillInsertBreak/DidInsertBreak into text rules, so flags could be checked. This gets us readonly, disabled, and single line behavior. * cleaned up the code that allocates, registers, and destroys event listeners. Thanks to Kin and Simon for cleaning up the ownership model on the listeners, it was a big help. * added support for a max text length. You can now tell the text editor, be no bigger than n characters.
1999-05-29 01:24:18 +04:00
{ \
*aCancel = true; \
Preparation for ender-based text control * added focus listener. Doesn't do much yet, but when focus notifications start appearing, we'll be ready for them. The code is in place to hide selection when we lose focus and paint selection when we get focus. That's probably not quite right, but it's a start. We will need to be able to determine the distinction between losing focus to another control within our app, and losing focus to another app. * added support for disabled and readonly states in the editor. This is accomplished by having flags set by the client, and letting the rules system deal with those flags. The flags I added are: TEXT_EDITOR_FLAG_PLAINTEXT 0x01 // only plain text editing is allowed TEXT_EDITOR_FLAG_SINGLELINE 0x02 // enter key and CR-LF handled specially TEXT_EDITOR_FLAG_PASSWORD 0x04 // text is not entered into content, only a representative character TEXT_EDITOR_FLAG_READONLY 0x08 // editing events are disabled. Editor may still accept focus. TEXT_EDITOR_FLAG_DISALBED 0x10 // all events are disabled (like scrolling). Editor will not accept focus. * added WillInsertBreak/DidInsertBreak into text rules, so flags could be checked. This gets us readonly, disabled, and single line behavior. * cleaned up the code that allocates, registers, and destroys event listeners. Thanks to Kin and Simon for cleaning up the ownership model on the listeners, it was a big help. * added support for a max text length. You can now tell the text editor, be no bigger than n characters.
1999-05-29 01:24:18 +04:00
return NS_OK; \
};
1999-04-05 21:21:59 +04:00
/********************************************************
* mozilla::TextEditRules
1999-04-05 21:21:59 +04:00
********************************************************/
TextEditRules::TextEditRules()
: mTextEditor(nullptr)
, mPasswordIMEIndex(0)
, mCachedSelectionOffset(0)
, mActionNesting(0)
, mLockRulesSniffing(false)
, mDidExplicitlySetInterline(false)
, mDeleteBidiImmediately(false)
, mTheAction(EditAction::none)
, mLastStart(0)
, mLastLength(0)
{
InitFields();
}
void
TextEditRules::InitFields()
{
mTextEditor = nullptr;
mPasswordText.Truncate();
mPasswordIMEText.Truncate();
mPasswordIMEIndex = 0;
mBogusNode = nullptr;
mCachedSelectionNode = nullptr;
mCachedSelectionOffset = 0;
mActionNesting = 0;
mLockRulesSniffing = false;
mDidExplicitlySetInterline = false;
mDeleteBidiImmediately = false;
mTheAction = EditAction::none;
mTimer = nullptr;
mLastStart = 0;
mLastLength = 0;
}
TextEditRules::~TextEditRules()
{
// do NOT delete mTextEditor here. We do not hold a ref count to
// mTextEditor. mTextEditor owns our lifespan.
if (mTimer) {
mTimer->Cancel();
}
}
NS_IMPL_CYCLE_COLLECTION(TextEditRules, mBogusNode, mCachedSelectionNode)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TextEditRules)
NS_INTERFACE_MAP_ENTRY(nsIEditRules)
NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditRules)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(TextEditRules)
NS_IMPL_CYCLE_COLLECTING_RELEASE(TextEditRules)
NS_IMETHODIMP
TextEditRules::Init(TextEditor* aTextEditor)
{
if (!aTextEditor) {
return NS_ERROR_NULL_POINTER;
}
InitFields();
// We hold a non-refcounted reference back to our editor.
mTextEditor = aTextEditor;
RefPtr<Selection> selection = mTextEditor->GetSelection();
NS_WARNING_ASSERTION(selection, "editor cannot get selection");
// Put in a magic br if needed. This method handles null selection,
// which should never happen anyway
nsresult rv = CreateBogusNodeIfNeeded(selection);
NS_ENSURE_SUCCESS(rv, rv);
// If the selection hasn't been set up yet, set it up collapsed to the end of
// our editable content.
int32_t rangeCount;
rv = selection->GetRangeCount(&rangeCount);
NS_ENSURE_SUCCESS(rv, rv);
if (!rangeCount) {
rv = mTextEditor->EndOfDocument();
NS_ENSURE_SUCCESS(rv, rv);
}
if (IsPlaintextEditor()) {
// ensure trailing br node
rv = CreateTrailingBRIfNeeded();
NS_ENSURE_SUCCESS(rv, rv);
}
mDeleteBidiImmediately =
Preferences::GetBool("bidi.edit.delete_immediately", false);
return NS_OK;
Preparation for ender-based text control * added focus listener. Doesn't do much yet, but when focus notifications start appearing, we'll be ready for them. The code is in place to hide selection when we lose focus and paint selection when we get focus. That's probably not quite right, but it's a start. We will need to be able to determine the distinction between losing focus to another control within our app, and losing focus to another app. * added support for disabled and readonly states in the editor. This is accomplished by having flags set by the client, and letting the rules system deal with those flags. The flags I added are: TEXT_EDITOR_FLAG_PLAINTEXT 0x01 // only plain text editing is allowed TEXT_EDITOR_FLAG_SINGLELINE 0x02 // enter key and CR-LF handled specially TEXT_EDITOR_FLAG_PASSWORD 0x04 // text is not entered into content, only a representative character TEXT_EDITOR_FLAG_READONLY 0x08 // editing events are disabled. Editor may still accept focus. TEXT_EDITOR_FLAG_DISALBED 0x10 // all events are disabled (like scrolling). Editor will not accept focus. * added WillInsertBreak/DidInsertBreak into text rules, so flags could be checked. This gets us readonly, disabled, and single line behavior. * cleaned up the code that allocates, registers, and destroys event listeners. Thanks to Kin and Simon for cleaning up the ownership model on the listeners, it was a big help. * added support for a max text length. You can now tell the text editor, be no bigger than n characters.
1999-05-29 01:24:18 +04:00
}
NS_IMETHODIMP
TextEditRules::SetInitialValue(const nsAString& aValue)
{
if (IsPasswordEditor()) {
mPasswordText = aValue;
}
return NS_OK;
}
NS_IMETHODIMP
TextEditRules::DetachEditor()
{
if (mTimer) {
mTimer->Cancel();
}
mTextEditor = nullptr;
return NS_OK;
}
NS_IMETHODIMP
TextEditRules::BeforeEdit(EditAction action,
nsIEditor::EDirection aDirection)
{
if (mLockRulesSniffing) {
return NS_OK;
}
AutoLockRulesSniffing lockIt(this);
mDidExplicitlySetInterline = false;
if (!mActionNesting) {
// let rules remember the top level action
mTheAction = action;
}
mActionNesting++;
// get the selection and cache the position before editing
NS_ENSURE_STATE(mTextEditor);
RefPtr<Selection> selection = mTextEditor->GetSelection();
NS_ENSURE_STATE(selection);
selection->GetAnchorNode(getter_AddRefs(mCachedSelectionNode));
selection->GetAnchorOffset(&mCachedSelectionOffset);
return NS_OK;
}
NS_IMETHODIMP
TextEditRules::AfterEdit(EditAction action,
nsIEditor::EDirection aDirection)
{
if (mLockRulesSniffing) {
return NS_OK;
}
AutoLockRulesSniffing lockIt(this);
NS_PRECONDITION(mActionNesting>0, "bad action nesting!");
if (!--mActionNesting) {
NS_ENSURE_STATE(mTextEditor);
RefPtr<Selection> selection = mTextEditor->GetSelection();
NS_ENSURE_STATE(selection);
NS_ENSURE_STATE(mTextEditor);
nsresult rv =
mTextEditor->HandleInlineSpellCheck(action, selection,
mCachedSelectionNode,
mCachedSelectionOffset,
nullptr, 0, nullptr, 0);
NS_ENSURE_SUCCESS(rv, rv);
// if only trailing <br> remaining remove it
rv = RemoveRedundantTrailingBR();
if (NS_FAILED(rv)) {
return rv;
}
// detect empty doc
rv = CreateBogusNodeIfNeeded(selection);
NS_ENSURE_SUCCESS(rv, rv);
// ensure trailing br node
rv = CreateTrailingBRIfNeeded();
NS_ENSURE_SUCCESS(rv, rv);
// collapse the selection to the trailing BR if it's at the end of our text node
CollapseSelectionToTrailingBRIfNeeded(selection);
}
return NS_OK;
}
NS_IMETHODIMP
TextEditRules::WillDoAction(Selection* aSelection,
RulesInfo* aInfo,
bool* aCancel,
bool* aHandled)
{
// null selection is legal
MOZ_ASSERT(aInfo && aCancel && aHandled);
*aCancel = false;
*aHandled = false;
1999-04-12 16:01:32 +04:00
// my kingdom for dynamic cast
TextRulesInfo* info = static_cast<TextRulesInfo*>(aInfo);
switch (info->action) {
case EditAction::insertBreak:
UndefineCaretBidiLevel(aSelection);
return WillInsertBreak(aSelection, aCancel, aHandled, info->maxLength);
case EditAction::insertText:
case EditAction::insertIMEText:
UndefineCaretBidiLevel(aSelection);
return WillInsertText(info->action, aSelection, aCancel, aHandled,
info->inString, info->outString, info->maxLength);
case EditAction::deleteSelection:
return WillDeleteSelection(aSelection, info->collapsedAction,
aCancel, aHandled);
case EditAction::undo:
return WillUndo(aSelection, aCancel, aHandled);
case EditAction::redo:
return WillRedo(aSelection, aCancel, aHandled);
case EditAction::setTextProperty:
return WillSetTextProperty(aSelection, aCancel, aHandled);
case EditAction::removeTextProperty:
return WillRemoveTextProperty(aSelection, aCancel, aHandled);
case EditAction::outputText:
return WillOutputText(aSelection, info->outputFormat, info->outString,
aCancel, aHandled);
case EditAction::insertElement:
// i had thought this would be html rules only. but we put pre elements
// into plaintext mail when doing quoting for reply! doh!
WillInsert(*aSelection, aCancel);
return NS_OK;
default:
return NS_ERROR_FAILURE;
1999-04-05 21:21:59 +04:00
}
}
NS_IMETHODIMP
TextEditRules::DidDoAction(Selection* aSelection,
RulesInfo* aInfo,
nsresult aResult)
{
NS_ENSURE_STATE(mTextEditor);
// don't let any txns in here move the selection around behind our back.
// Note that this won't prevent explicit selection setting from working.
AutoTransactionsConserveSelection dontSpazMySelection(mTextEditor);
NS_ENSURE_TRUE(aSelection && aInfo, NS_ERROR_NULL_POINTER);
1999-04-12 16:01:32 +04:00
// my kingdom for dynamic cast
TextRulesInfo* info = static_cast<TextRulesInfo*>(aInfo);
1999-04-12 16:01:32 +04:00
switch (info->action) {
case EditAction::insertBreak:
return DidInsertBreak(aSelection, aResult);
case EditAction::insertText:
case EditAction::insertIMEText:
1999-04-05 21:21:59 +04:00
return DidInsertText(aSelection, aResult);
case EditAction::deleteSelection:
1999-06-25 03:36:56 +04:00
return DidDeleteSelection(aSelection, info->collapsedAction, aResult);
case EditAction::undo:
1999-04-05 21:21:59 +04:00
return DidUndo(aSelection, aResult);
case EditAction::redo:
1999-04-05 21:21:59 +04:00
return DidRedo(aSelection, aResult);
case EditAction::setTextProperty:
return DidSetTextProperty(aSelection, aResult);
case EditAction::removeTextProperty:
return DidRemoveTextProperty(aSelection, aResult);
case EditAction::outputText:
1999-06-25 03:36:56 +04:00
return DidOutputText(aSelection, aResult);
default:
// Don't fail on transactions we don't handle here!
return NS_OK;
1999-04-05 21:21:59 +04:00
}
}
NS_IMETHODIMP
TextEditRules::DocumentIsEmpty(bool* aDocumentIsEmpty)
{
NS_ENSURE_TRUE(aDocumentIsEmpty, NS_ERROR_NULL_POINTER);
*aDocumentIsEmpty = (mBogusNode != nullptr);
return NS_OK;
}
1999-04-05 21:21:59 +04:00
void
TextEditRules::WillInsert(Selection& aSelection, bool* aCancel)
{
MOZ_ASSERT(aCancel);
if (IsReadonly() || IsDisabled()) {
*aCancel = true;
return;
}
Preparation for ender-based text control * added focus listener. Doesn't do much yet, but when focus notifications start appearing, we'll be ready for them. The code is in place to hide selection when we lose focus and paint selection when we get focus. That's probably not quite right, but it's a start. We will need to be able to determine the distinction between losing focus to another control within our app, and losing focus to another app. * added support for disabled and readonly states in the editor. This is accomplished by having flags set by the client, and letting the rules system deal with those flags. The flags I added are: TEXT_EDITOR_FLAG_PLAINTEXT 0x01 // only plain text editing is allowed TEXT_EDITOR_FLAG_SINGLELINE 0x02 // enter key and CR-LF handled specially TEXT_EDITOR_FLAG_PASSWORD 0x04 // text is not entered into content, only a representative character TEXT_EDITOR_FLAG_READONLY 0x08 // editing events are disabled. Editor may still accept focus. TEXT_EDITOR_FLAG_DISALBED 0x10 // all events are disabled (like scrolling). Editor will not accept focus. * added WillInsertBreak/DidInsertBreak into text rules, so flags could be checked. This gets us readonly, disabled, and single line behavior. * cleaned up the code that allocates, registers, and destroys event listeners. Thanks to Kin and Simon for cleaning up the ownership model on the listeners, it was a big help. * added support for a max text length. You can now tell the text editor, be no bigger than n characters.
1999-05-29 01:24:18 +04:00
// initialize out param
*aCancel = false;
// check for the magic content node and delete it if it exists
if (mBogusNode) {
NS_ENSURE_TRUE_VOID(mTextEditor);
mTextEditor->DeleteNode(mBogusNode);
mBogusNode = nullptr;
}
}
1999-04-12 16:01:32 +04:00
nsresult
TextEditRules::DidInsert(Selection* aSelection,
nsresult aResult)
{
return NS_OK;
}
nsresult
TextEditRules::WillInsertBreak(Selection* aSelection,
bool* aCancel,
bool* aHandled,
int32_t aMaxLength)
Preparation for ender-based text control * added focus listener. Doesn't do much yet, but when focus notifications start appearing, we'll be ready for them. The code is in place to hide selection when we lose focus and paint selection when we get focus. That's probably not quite right, but it's a start. We will need to be able to determine the distinction between losing focus to another control within our app, and losing focus to another app. * added support for disabled and readonly states in the editor. This is accomplished by having flags set by the client, and letting the rules system deal with those flags. The flags I added are: TEXT_EDITOR_FLAG_PLAINTEXT 0x01 // only plain text editing is allowed TEXT_EDITOR_FLAG_SINGLELINE 0x02 // enter key and CR-LF handled specially TEXT_EDITOR_FLAG_PASSWORD 0x04 // text is not entered into content, only a representative character TEXT_EDITOR_FLAG_READONLY 0x08 // editing events are disabled. Editor may still accept focus. TEXT_EDITOR_FLAG_DISALBED 0x10 // all events are disabled (like scrolling). Editor will not accept focus. * added WillInsertBreak/DidInsertBreak into text rules, so flags could be checked. This gets us readonly, disabled, and single line behavior. * cleaned up the code that allocates, registers, and destroys event listeners. Thanks to Kin and Simon for cleaning up the ownership model on the listeners, it was a big help. * added support for a max text length. You can now tell the text editor, be no bigger than n characters.
1999-05-29 01:24:18 +04:00
{
if (!aSelection || !aCancel || !aHandled) {
return NS_ERROR_NULL_POINTER;
}
Preparation for ender-based text control * added focus listener. Doesn't do much yet, but when focus notifications start appearing, we'll be ready for them. The code is in place to hide selection when we lose focus and paint selection when we get focus. That's probably not quite right, but it's a start. We will need to be able to determine the distinction between losing focus to another control within our app, and losing focus to another app. * added support for disabled and readonly states in the editor. This is accomplished by having flags set by the client, and letting the rules system deal with those flags. The flags I added are: TEXT_EDITOR_FLAG_PLAINTEXT 0x01 // only plain text editing is allowed TEXT_EDITOR_FLAG_SINGLELINE 0x02 // enter key and CR-LF handled specially TEXT_EDITOR_FLAG_PASSWORD 0x04 // text is not entered into content, only a representative character TEXT_EDITOR_FLAG_READONLY 0x08 // editing events are disabled. Editor may still accept focus. TEXT_EDITOR_FLAG_DISALBED 0x10 // all events are disabled (like scrolling). Editor will not accept focus. * added WillInsertBreak/DidInsertBreak into text rules, so flags could be checked. This gets us readonly, disabled, and single line behavior. * cleaned up the code that allocates, registers, and destroys event listeners. Thanks to Kin and Simon for cleaning up the ownership model on the listeners, it was a big help. * added support for a max text length. You can now tell the text editor, be no bigger than n characters.
1999-05-29 01:24:18 +04:00
CANCEL_OPERATION_IF_READONLY_OR_DISABLED
*aHandled = false;
if (IsSingleLineEditor()) {
*aCancel = true;
} else {
// handle docs with a max length
// NOTE, this function copies inString into outString for us.
NS_NAMED_LITERAL_STRING(inString, "\n");
nsAutoString outString;
bool didTruncate;
nsresult rv = TruncateInsertionIfNeeded(aSelection, &inString, &outString,
aMaxLength, &didTruncate);
NS_ENSURE_SUCCESS(rv, rv);
if (didTruncate) {
*aCancel = true;
return NS_OK;
}
*aCancel = false;
// if the selection isn't collapsed, delete it.
bool bCollapsed;
rv = aSelection->GetIsCollapsed(&bCollapsed);
NS_ENSURE_SUCCESS(rv, rv);
if (!bCollapsed) {
NS_ENSURE_STATE(mTextEditor);
rv = mTextEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
NS_ENSURE_SUCCESS(rv, rv);
}
WillInsert(*aSelection, aCancel);
// initialize out param
// we want to ignore result of WillInsert()
*aCancel = false;
Preparation for ender-based text control * added focus listener. Doesn't do much yet, but when focus notifications start appearing, we'll be ready for them. The code is in place to hide selection when we lose focus and paint selection when we get focus. That's probably not quite right, but it's a start. We will need to be able to determine the distinction between losing focus to another control within our app, and losing focus to another app. * added support for disabled and readonly states in the editor. This is accomplished by having flags set by the client, and letting the rules system deal with those flags. The flags I added are: TEXT_EDITOR_FLAG_PLAINTEXT 0x01 // only plain text editing is allowed TEXT_EDITOR_FLAG_SINGLELINE 0x02 // enter key and CR-LF handled specially TEXT_EDITOR_FLAG_PASSWORD 0x04 // text is not entered into content, only a representative character TEXT_EDITOR_FLAG_READONLY 0x08 // editing events are disabled. Editor may still accept focus. TEXT_EDITOR_FLAG_DISALBED 0x10 // all events are disabled (like scrolling). Editor will not accept focus. * added WillInsertBreak/DidInsertBreak into text rules, so flags could be checked. This gets us readonly, disabled, and single line behavior. * cleaned up the code that allocates, registers, and destroys event listeners. Thanks to Kin and Simon for cleaning up the ownership model on the listeners, it was a big help. * added support for a max text length. You can now tell the text editor, be no bigger than n characters.
1999-05-29 01:24:18 +04:00
}
return NS_OK;
}
nsresult
TextEditRules::DidInsertBreak(Selection* aSelection,
nsresult aResult)
{
return NS_OK;
}
nsresult
TextEditRules::CollapseSelectionToTrailingBRIfNeeded(Selection* aSelection)
Preparation for ender-based text control * added focus listener. Doesn't do much yet, but when focus notifications start appearing, we'll be ready for them. The code is in place to hide selection when we lose focus and paint selection when we get focus. That's probably not quite right, but it's a start. We will need to be able to determine the distinction between losing focus to another control within our app, and losing focus to another app. * added support for disabled and readonly states in the editor. This is accomplished by having flags set by the client, and letting the rules system deal with those flags. The flags I added are: TEXT_EDITOR_FLAG_PLAINTEXT 0x01 // only plain text editing is allowed TEXT_EDITOR_FLAG_SINGLELINE 0x02 // enter key and CR-LF handled specially TEXT_EDITOR_FLAG_PASSWORD 0x04 // text is not entered into content, only a representative character TEXT_EDITOR_FLAG_READONLY 0x08 // editing events are disabled. Editor may still accept focus. TEXT_EDITOR_FLAG_DISALBED 0x10 // all events are disabled (like scrolling). Editor will not accept focus. * added WillInsertBreak/DidInsertBreak into text rules, so flags could be checked. This gets us readonly, disabled, and single line behavior. * cleaned up the code that allocates, registers, and destroys event listeners. Thanks to Kin and Simon for cleaning up the ownership model on the listeners, it was a big help. * added support for a max text length. You can now tell the text editor, be no bigger than n characters.
1999-05-29 01:24:18 +04:00
{
// we only need to execute the stuff below if we are a plaintext editor.
// html editors have a different mechanism for putting in mozBR's
// (because there are a bunch more places you have to worry about it in html)
if (!IsPlaintextEditor()) {
return NS_OK;
}
NS_ENSURE_STATE(mTextEditor);
// If there is no selection ranges, we should set to the end of the editor.
// This is usually performed in TextEditRules::Init(), however, if the
// editor is reframed, this may be called by AfterEdit().
if (!aSelection->RangeCount()) {
mTextEditor->EndOfDocument();
}
// if we are at the end of the textarea, we need to set the
// selection to stick to the mozBR at the end of the textarea.
int32_t selOffset;
nsCOMPtr<nsIDOMNode> selNode;
nsresult rv =
EditorBase::GetStartNodeAndOffset(aSelection,
getter_AddRefs(selNode), &selOffset);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMText> nodeAsText = do_QueryInterface(selNode);
if (!nodeAsText) {
return NS_OK; // Nothing to do if we're not at a text node.
}
uint32_t length;
rv = nodeAsText->GetLength(&length);
NS_ENSURE_SUCCESS(rv, rv);
// nothing to do if we're not at the end of the text node
if (selOffset != int32_t(length)) {
return NS_OK;
}
int32_t parentOffset;
nsCOMPtr<nsIDOMNode> parentNode =
EditorBase::GetNodeLocation(selNode, &parentOffset);
NS_ENSURE_STATE(mTextEditor);
nsCOMPtr<nsIDOMNode> root = do_QueryInterface(mTextEditor->GetRoot());
NS_ENSURE_TRUE(root, NS_ERROR_NULL_POINTER);
if (parentNode != root) {
return NS_OK;
}
nsCOMPtr<nsIDOMNode> nextNode = mTextEditor->GetChildAt(parentNode,
parentOffset + 1);
if (nextNode && TextEditUtils::IsMozBR(nextNode)) {
rv = aSelection->Collapse(parentNode, parentOffset + 1);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
Preparation for ender-based text control * added focus listener. Doesn't do much yet, but when focus notifications start appearing, we'll be ready for them. The code is in place to hide selection when we lose focus and paint selection when we get focus. That's probably not quite right, but it's a start. We will need to be able to determine the distinction between losing focus to another control within our app, and losing focus to another app. * added support for disabled and readonly states in the editor. This is accomplished by having flags set by the client, and letting the rules system deal with those flags. The flags I added are: TEXT_EDITOR_FLAG_PLAINTEXT 0x01 // only plain text editing is allowed TEXT_EDITOR_FLAG_SINGLELINE 0x02 // enter key and CR-LF handled specially TEXT_EDITOR_FLAG_PASSWORD 0x04 // text is not entered into content, only a representative character TEXT_EDITOR_FLAG_READONLY 0x08 // editing events are disabled. Editor may still accept focus. TEXT_EDITOR_FLAG_DISALBED 0x10 // all events are disabled (like scrolling). Editor will not accept focus. * added WillInsertBreak/DidInsertBreak into text rules, so flags could be checked. This gets us readonly, disabled, and single line behavior. * cleaned up the code that allocates, registers, and destroys event listeners. Thanks to Kin and Simon for cleaning up the ownership model on the listeners, it was a big help. * added support for a max text length. You can now tell the text editor, be no bigger than n characters.
1999-05-29 01:24:18 +04:00
}
static inline already_AddRefed<nsINode>
GetTextNode(Selection* selection)
{
int32_t selOffset;
nsCOMPtr<nsINode> selNode;
nsresult rv =
EditorBase::GetStartNodeAndOffset(selection,
getter_AddRefs(selNode), &selOffset);
NS_ENSURE_SUCCESS(rv, nullptr);
if (!EditorBase::IsTextNode(selNode)) {
// This should be the root node, walk the tree looking for text nodes
RefPtr<NodeIterator> iter =
new NodeIterator(selNode, nsIDOMNodeFilter::SHOW_TEXT,
NodeFilterHolder());
while (!EditorBase::IsTextNode(selNode)) {
IgnoredErrorResult rv;
selNode = iter->NextNode(rv);
if (!selNode) {
return nullptr;
}
}
}
return selNode.forget();
}
#ifdef DEBUG
#define ASSERT_PASSWORD_LENGTHS_EQUAL() \
if (IsPasswordEditor() && mTextEditor->GetRoot()) { \
int32_t txtLen; \
mTextEditor->GetTextLength(&txtLen); \
NS_ASSERTION(mPasswordText.Length() == uint32_t(txtLen), \
"password length not equal to number of asterisks"); \
}
#else
#define ASSERT_PASSWORD_LENGTHS_EQUAL()
#endif
// static
void
TextEditRules::HandleNewLines(nsString& aString,
int32_t aNewlineHandling)
{
if (aNewlineHandling < 0) {
int32_t caretStyle;
TextEditor::GetDefaultEditorPrefs(aNewlineHandling, caretStyle);
}
switch(aNewlineHandling) {
case nsIPlaintextEditor::eNewlinesReplaceWithSpaces:
// Strip trailing newlines first so we don't wind up with trailing spaces
aString.Trim(CRLF, false, true);
aString.ReplaceChar(CRLF, ' ');
break;
case nsIPlaintextEditor::eNewlinesStrip:
aString.StripChars(CRLF);
break;
case nsIPlaintextEditor::eNewlinesPasteToFirst:
default: {
int32_t firstCRLF = aString.FindCharInSet(CRLF);
// we get first *non-empty* line.
int32_t offset = 0;
while (firstCRLF == offset) {
offset++;
firstCRLF = aString.FindCharInSet(CRLF, offset);
}
if (firstCRLF > 0) {
aString.Truncate(firstCRLF);
}
if (offset > 0) {
aString.Cut(0, offset);
}
break;
}
case nsIPlaintextEditor::eNewlinesReplaceWithCommas:
aString.Trim(CRLF, true, true);
aString.ReplaceChar(CRLF, ',');
break;
case nsIPlaintextEditor::eNewlinesStripSurroundingWhitespace: {
nsAutoString result;
uint32_t offset = 0;
while (offset < aString.Length()) {
int32_t nextCRLF = aString.FindCharInSet(CRLF, offset);
if (nextCRLF < 0) {
result.Append(nsDependentSubstring(aString, offset));
break;
}
uint32_t wsBegin = nextCRLF;
// look backwards for the first non-whitespace char
while (wsBegin > offset && NS_IS_SPACE(aString[wsBegin - 1])) {
--wsBegin;
}
result.Append(nsDependentSubstring(aString, offset, wsBegin - offset));
offset = nextCRLF + 1;
while (offset < aString.Length() && NS_IS_SPACE(aString[offset])) {
++offset;
}
}
aString = result;
break;
}
case nsIPlaintextEditor::eNewlinesPasteIntact:
// even if we're pasting newlines, don't paste leading/trailing ones
aString.Trim(CRLF, true, true);
break;
}
}
1999-04-12 16:01:32 +04:00
nsresult
TextEditRules::WillInsertText(EditAction aAction,
Selection* aSelection,
bool* aCancel,
bool* aHandled,
const nsAString* inString,
nsAString* outString,
int32_t aMaxLength)
{
if (!aSelection || !aCancel || !aHandled) {
return NS_ERROR_NULL_POINTER;
}
if (inString->IsEmpty() && aAction != EditAction::insertIMEText) {
// HACK: this is a fix for bug 19395
// I can't outlaw all empty insertions
// because IME transaction depend on them
// There is more work to do to make the
// world safe for IME.
*aCancel = true;
*aHandled = false;
return NS_OK;
}
// initialize out param
*aCancel = false;
*aHandled = true;
// handle docs with a max length
2000-03-29 18:04:26 +04:00
// NOTE, this function copies inString into outString for us.
bool truncated = false;
nsresult rv = TruncateInsertionIfNeeded(aSelection, inString, outString,
aMaxLength, &truncated);
NS_ENSURE_SUCCESS(rv, rv);
// If we're exceeding the maxlength when composing IME, we need to clean up
// the composing text, so we shouldn't return early.
if (truncated && outString->IsEmpty() &&
aAction != EditAction::insertIMEText) {
*aCancel = true;
return NS_OK;
}
int32_t start = 0;
int32_t end = 0;
// handle password field docs
if (IsPasswordEditor()) {
NS_ENSURE_STATE(mTextEditor);
nsContentUtils::GetSelectionInTextControl(aSelection,
mTextEditor->GetRoot(),
start, end);
}
// if the selection isn't collapsed, delete it.
bool bCollapsed;
rv = aSelection->GetIsCollapsed(&bCollapsed);
NS_ENSURE_SUCCESS(rv, rv);
if (!bCollapsed) {
NS_ENSURE_STATE(mTextEditor);
rv = mTextEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
NS_ENSURE_SUCCESS(rv, rv);
}
WillInsert(*aSelection, aCancel);
// initialize out param
// we want to ignore result of WillInsert()
*aCancel = false;
// handle password field data
// this has the side effect of changing all the characters in aOutString
// to the replacement character
if (IsPasswordEditor() &&
aAction == EditAction::insertIMEText) {
RemoveIMETextFromPWBuf(start, outString);
}
// People have lots of different ideas about what text fields
// should do with multiline pastes. See bugs 21032, 23485, 23485, 50935.
// The six possible options are:
// 0. paste newlines intact
// 1. paste up to the first newline (default)
// 2. replace newlines with spaces
// 3. strip newlines
// 4. replace with commas
// 5. strip newlines and surrounding whitespace
// So find out what we're expected to do:
if (IsSingleLineEditor()) {
nsAutoString tString(*outString);
NS_ENSURE_STATE(mTextEditor);
HandleNewLines(tString, mTextEditor->mNewlineHandling);
outString->Assign(tString);
}
if (IsPasswordEditor()) {
// manage the password buffer
mPasswordText.Insert(*outString, start);
if (LookAndFeel::GetEchoPassword() && !DontEchoPassword()) {
HideLastPWInput();
mLastStart = start;
mLastLength = outString->Length();
if (mTimer) {
mTimer->Cancel();
} else {
mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
}
mTimer->InitWithCallback(this, LookAndFeel::GetPasswordMaskDelay(),
nsITimer::TYPE_ONE_SHOT);
} else {
FillBufWithPWChars(outString, outString->Length());
}
}
// get the (collapsed) selection location
NS_ENSURE_STATE(aSelection->GetRangeAt(0));
nsCOMPtr<nsINode> selNode = aSelection->GetRangeAt(0)->GetStartParent();
int32_t selOffset = aSelection->GetRangeAt(0)->StartOffset();
NS_ENSURE_STATE(selNode);
// don't put text in places that can't have it
NS_ENSURE_STATE(mTextEditor);
if (!EditorBase::IsTextNode(selNode) &&
!mTextEditor->CanContainTag(*selNode, *nsGkAtoms::textTagName)) {
return NS_ERROR_FAILURE;
}
// we need to get the doc
NS_ENSURE_STATE(mTextEditor);
nsCOMPtr<nsIDocument> doc = mTextEditor->GetDocument();
NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
if (aAction == EditAction::insertIMEText) {
NS_ENSURE_STATE(mTextEditor);
// Find better insertion point to insert text.
mTextEditor->FindBetterInsertionPoint(selNode, selOffset);
// If there is one or more IME selections, its minimum offset should be
// the insertion point.
int32_t IMESelectionOffset =
mTextEditor->GetIMESelectionStartOffsetIn(selNode);
if (IMESelectionOffset >= 0) {
selOffset = IMESelectionOffset;
}
rv = mTextEditor->InsertTextImpl(*outString, address_of(selNode),
&selOffset, doc);
NS_ENSURE_SUCCESS(rv, rv);
} else {
// aAction == EditAction::insertText; find where we are
nsCOMPtr<nsINode> curNode = selNode;
int32_t curOffset = selOffset;
// don't spaz my selection in subtransactions
NS_ENSURE_STATE(mTextEditor);
AutoTransactionsConserveSelection dontSpazMySelection(mTextEditor);
rv = mTextEditor->InsertTextImpl(*outString, address_of(curNode),
&curOffset, doc);
NS_ENSURE_SUCCESS(rv, rv);
if (curNode) {
// Make the caret attach to the inserted text, unless this text ends with a LF,
// in which case make the caret attach to the next line.
bool endsWithLF =
!outString->IsEmpty() && outString->Last() == nsCRT::LF;
aSelection->SetInterlinePosition(endsWithLF);
aSelection->Collapse(curNode, curOffset);
}
As a reminder, we decided to do this based strictly content. Some support for style-based text properties is written, but not used anywhere any more. * Cleaned up split and join undo/redo. * Added TypeInState, a data struct that remembers things about text properties for collapsed selections, so you can type * Ctrl-B with an insertion point and the next character will be bold. * Added all the logic to handle inline vs. block elements when setting text properties. * Added some support for italic and underline as well. Adding these things is pretty easy now. Ctrl-B, Ctrl-I, Ctrl-U for testing bold, italic, underline. * Added all the logic to make sure we only add style tags where they're needed, so you should never get the same style tag nested within itself, except as needed for block elements. * Added methods for testing a node to see if a particular style is set. This isn't 100% done yet, but with very little work we could have toolbar buttons that respond to selection changed notification that show the state of bold, italic, underline, etc. in real time. Supports tri-state: whole selection is bold, some of selection is bold, none of selection is bold, ... * Fully undoable and redoable. * Added some debug printfs to transactions and editors. all controlled by a gNoisy static in each module. helps me track down undo/redo problems. if the output bugs people enough, I'll shut it off and re-enable it in my local tree. Noticably missing: make un-bold, make un-italic, etc. This is coming soon.
1999-04-01 21:58:07 +04:00
}
ASSERT_PASSWORD_LENGTHS_EQUAL()
return NS_OK;
As a reminder, we decided to do this based strictly content. Some support for style-based text properties is written, but not used anywhere any more. * Cleaned up split and join undo/redo. * Added TypeInState, a data struct that remembers things about text properties for collapsed selections, so you can type * Ctrl-B with an insertion point and the next character will be bold. * Added all the logic to handle inline vs. block elements when setting text properties. * Added some support for italic and underline as well. Adding these things is pretty easy now. Ctrl-B, Ctrl-I, Ctrl-U for testing bold, italic, underline. * Added all the logic to make sure we only add style tags where they're needed, so you should never get the same style tag nested within itself, except as needed for block elements. * Added methods for testing a node to see if a particular style is set. This isn't 100% done yet, but with very little work we could have toolbar buttons that respond to selection changed notification that show the state of bold, italic, underline, etc. in real time. Supports tri-state: whole selection is bold, some of selection is bold, none of selection is bold, ... * Fully undoable and redoable. * Added some debug printfs to transactions and editors. all controlled by a gNoisy static in each module. helps me track down undo/redo problems. if the output bugs people enough, I'll shut it off and re-enable it in my local tree. Noticably missing: make un-bold, make un-italic, etc. This is coming soon.
1999-04-01 21:58:07 +04:00
}
nsresult
TextEditRules::DidInsertText(Selection* aSelection,
nsresult aResult)
{
return DidInsert(aSelection, aResult);
}
nsresult
TextEditRules::WillSetTextProperty(Selection* aSelection,
bool* aCancel,
bool* aHandled)
{
if (!aSelection || !aCancel || !aHandled) {
return NS_ERROR_NULL_POINTER;
}
// XXX: should probably return a success value other than NS_OK that means "not allowed"
if (IsPlaintextEditor()) {
*aCancel = true;
}
return NS_OK;
}
nsresult
TextEditRules::DidSetTextProperty(Selection* aSelection,
nsresult aResult)
{
return NS_OK;
}
nsresult
TextEditRules::WillRemoveTextProperty(Selection* aSelection,
bool* aCancel,
bool* aHandled)
{
if (!aSelection || !aCancel || !aHandled) {
return NS_ERROR_NULL_POINTER;
}
// XXX: should probably return a success value other than NS_OK that means "not allowed"
if (IsPlaintextEditor()) {
*aCancel = true;
}
return NS_OK;
}
nsresult
TextEditRules::DidRemoveTextProperty(Selection* aSelection,
nsresult aResult)
{
return NS_OK;
}
1999-04-12 16:01:32 +04:00
nsresult
TextEditRules::WillDeleteSelection(Selection* aSelection,
nsIEditor::EDirection aCollapsedAction,
bool* aCancel,
bool* aHandled)
{
if (!aSelection || !aCancel || !aHandled) {
return NS_ERROR_NULL_POINTER;
}
Preparation for ender-based text control * added focus listener. Doesn't do much yet, but when focus notifications start appearing, we'll be ready for them. The code is in place to hide selection when we lose focus and paint selection when we get focus. That's probably not quite right, but it's a start. We will need to be able to determine the distinction between losing focus to another control within our app, and losing focus to another app. * added support for disabled and readonly states in the editor. This is accomplished by having flags set by the client, and letting the rules system deal with those flags. The flags I added are: TEXT_EDITOR_FLAG_PLAINTEXT 0x01 // only plain text editing is allowed TEXT_EDITOR_FLAG_SINGLELINE 0x02 // enter key and CR-LF handled specially TEXT_EDITOR_FLAG_PASSWORD 0x04 // text is not entered into content, only a representative character TEXT_EDITOR_FLAG_READONLY 0x08 // editing events are disabled. Editor may still accept focus. TEXT_EDITOR_FLAG_DISALBED 0x10 // all events are disabled (like scrolling). Editor will not accept focus. * added WillInsertBreak/DidInsertBreak into text rules, so flags could be checked. This gets us readonly, disabled, and single line behavior. * cleaned up the code that allocates, registers, and destroys event listeners. Thanks to Kin and Simon for cleaning up the ownership model on the listeners, it was a big help. * added support for a max text length. You can now tell the text editor, be no bigger than n characters.
1999-05-29 01:24:18 +04:00
CANCEL_OPERATION_IF_READONLY_OR_DISABLED
// initialize out param
*aCancel = false;
*aHandled = false;
// if there is only bogus content, cancel the operation
if (mBogusNode) {
*aCancel = true;
return NS_OK;
}
// If the current selection is empty (e.g the user presses backspace with
// a collapsed selection), then we want to avoid sending the selectstart
// event to the user, so we hide selection changes. However, we still
// want to send a single selectionchange event to the document, so we
// batch the selectionchange events, such that a single event fires after
// the AutoHideSelectionChanges destructor has been run.
SelectionBatcher selectionBatcher(aSelection);
AutoHideSelectionChanges hideSelection(aSelection);
nsAutoScriptBlocker scriptBlocker;
if (IsPasswordEditor()) {
NS_ENSURE_STATE(mTextEditor);
nsresult rv =
mTextEditor->ExtendSelectionForDelete(aSelection, &aCollapsedAction);
NS_ENSURE_SUCCESS(rv, rv);
// manage the password buffer
int32_t start, end;
nsContentUtils::GetSelectionInTextControl(aSelection,
mTextEditor->GetRoot(),
start, end);
if (LookAndFeel::GetEchoPassword()) {
HideLastPWInput();
mLastStart = start;
mLastLength = 0;
if (mTimer) {
mTimer->Cancel();
}
}
// Collapsed selection.
if (end == start) {
// Deleting back.
if (nsIEditor::ePrevious == aCollapsedAction && 0<start) {
mPasswordText.Cut(start-1, 1);
}
// Deleting forward.
else if (nsIEditor::eNext == aCollapsedAction) {
mPasswordText.Cut(start, 1);
}
// Otherwise nothing to do for this collapsed selection.
}
// Extended selection.
else {
mPasswordText.Cut(start, end-start);
}
} else {
nsCOMPtr<nsIDOMNode> startNode;
int32_t startOffset;
nsresult rv =
EditorBase::GetStartNodeAndOffset(aSelection, getter_AddRefs(startNode),
&startOffset);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
bool bCollapsed;
rv = aSelection->GetIsCollapsed(&bCollapsed);
NS_ENSURE_SUCCESS(rv, rv);
if (!bCollapsed) {
return NS_OK;
}
// Test for distance between caret and text that will be deleted
rv = CheckBidiLevelForDeletion(aSelection, startNode, startOffset,
aCollapsedAction, aCancel);
NS_ENSURE_SUCCESS(rv, rv);
if (*aCancel) {
return NS_OK;
}
NS_ENSURE_STATE(mTextEditor);
rv = mTextEditor->ExtendSelectionForDelete(aSelection, &aCollapsedAction);
NS_ENSURE_SUCCESS(rv, rv);
}
NS_ENSURE_STATE(mTextEditor);
nsresult rv =
mTextEditor->DeleteSelectionImpl(aCollapsedAction, nsIEditor::eStrip);
NS_ENSURE_SUCCESS(rv, rv);
*aHandled = true;
ASSERT_PASSWORD_LENGTHS_EQUAL()
return NS_OK;
}
1999-04-12 16:01:32 +04:00
nsresult
TextEditRules::DidDeleteSelection(Selection* aSelection,
nsIEditor::EDirection aCollapsedAction,
nsresult aResult)
{
nsCOMPtr<nsIDOMNode> startNode;
int32_t startOffset;
nsresult rv =
EditorBase::GetStartNodeAndOffset(aSelection,
getter_AddRefs(startNode), &startOffset);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
// delete empty text nodes at selection
if (EditorBase::IsTextNode(startNode)) {
nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(startNode);
uint32_t strLength;
rv = textNode->GetLength(&strLength);
NS_ENSURE_SUCCESS(rv, rv);
// are we in an empty text node?
if (!strLength) {
NS_ENSURE_STATE(mTextEditor);
rv = mTextEditor->DeleteNode(startNode);
NS_ENSURE_SUCCESS(rv, rv);
}
}
if (mDidExplicitlySetInterline) {
return NS_OK;
}
// We prevent the caret from sticking on the left of prior BR
// (i.e. the end of previous line) after this deletion. Bug 92124
return aSelection->SetInterlinePosition(true);
}
1999-04-12 16:01:32 +04:00
nsresult
TextEditRules::WillUndo(Selection* aSelection,
bool* aCancel,
bool* aHandled)
{
if (!aSelection || !aCancel || !aHandled) {
return NS_ERROR_NULL_POINTER;
}
Preparation for ender-based text control * added focus listener. Doesn't do much yet, but when focus notifications start appearing, we'll be ready for them. The code is in place to hide selection when we lose focus and paint selection when we get focus. That's probably not quite right, but it's a start. We will need to be able to determine the distinction between losing focus to another control within our app, and losing focus to another app. * added support for disabled and readonly states in the editor. This is accomplished by having flags set by the client, and letting the rules system deal with those flags. The flags I added are: TEXT_EDITOR_FLAG_PLAINTEXT 0x01 // only plain text editing is allowed TEXT_EDITOR_FLAG_SINGLELINE 0x02 // enter key and CR-LF handled specially TEXT_EDITOR_FLAG_PASSWORD 0x04 // text is not entered into content, only a representative character TEXT_EDITOR_FLAG_READONLY 0x08 // editing events are disabled. Editor may still accept focus. TEXT_EDITOR_FLAG_DISALBED 0x10 // all events are disabled (like scrolling). Editor will not accept focus. * added WillInsertBreak/DidInsertBreak into text rules, so flags could be checked. This gets us readonly, disabled, and single line behavior. * cleaned up the code that allocates, registers, and destroys event listeners. Thanks to Kin and Simon for cleaning up the ownership model on the listeners, it was a big help. * added support for a max text length. You can now tell the text editor, be no bigger than n characters.
1999-05-29 01:24:18 +04:00
CANCEL_OPERATION_IF_READONLY_OR_DISABLED
// initialize out param
*aCancel = false;
*aHandled = false;
return NS_OK;
}
/**
* The idea here is to see if the magic empty node has suddenly reappeared as
* the result of the undo. If it has, set our state so we remember it.
* There is a tradeoff between doing here and at redo, or doing it everywhere
* else that might care. Since undo and redo are relatively rare, it makes
* sense to take the (small) performance hit here.
*/
1999-04-12 16:01:32 +04:00
nsresult
TextEditRules::DidUndo(Selection* aSelection,
nsresult aResult)
{
NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
// If aResult is an error, we return it.
NS_ENSURE_SUCCESS(aResult, aResult);
NS_ENSURE_STATE(mTextEditor);
dom::Element* theRoot = mTextEditor->GetRoot();
NS_ENSURE_TRUE(theRoot, NS_ERROR_FAILURE);
nsIContent* node = mTextEditor->GetLeftmostChild(theRoot);
if (node && mTextEditor->IsMozEditorBogusNode(node)) {
mBogusNode = do_QueryInterface(node);
} else {
mBogusNode = nullptr;
}
return aResult;
}
1999-04-12 16:01:32 +04:00
nsresult
TextEditRules::WillRedo(Selection* aSelection,
bool* aCancel,
bool* aHandled)
{
if (!aSelection || !aCancel || !aHandled) {
return NS_ERROR_NULL_POINTER;
}
Preparation for ender-based text control * added focus listener. Doesn't do much yet, but when focus notifications start appearing, we'll be ready for them. The code is in place to hide selection when we lose focus and paint selection when we get focus. That's probably not quite right, but it's a start. We will need to be able to determine the distinction between losing focus to another control within our app, and losing focus to another app. * added support for disabled and readonly states in the editor. This is accomplished by having flags set by the client, and letting the rules system deal with those flags. The flags I added are: TEXT_EDITOR_FLAG_PLAINTEXT 0x01 // only plain text editing is allowed TEXT_EDITOR_FLAG_SINGLELINE 0x02 // enter key and CR-LF handled specially TEXT_EDITOR_FLAG_PASSWORD 0x04 // text is not entered into content, only a representative character TEXT_EDITOR_FLAG_READONLY 0x08 // editing events are disabled. Editor may still accept focus. TEXT_EDITOR_FLAG_DISALBED 0x10 // all events are disabled (like scrolling). Editor will not accept focus. * added WillInsertBreak/DidInsertBreak into text rules, so flags could be checked. This gets us readonly, disabled, and single line behavior. * cleaned up the code that allocates, registers, and destroys event listeners. Thanks to Kin and Simon for cleaning up the ownership model on the listeners, it was a big help. * added support for a max text length. You can now tell the text editor, be no bigger than n characters.
1999-05-29 01:24:18 +04:00
CANCEL_OPERATION_IF_READONLY_OR_DISABLED
// initialize out param
*aCancel = false;
*aHandled = false;
return NS_OK;
}
1999-04-12 16:01:32 +04:00
nsresult
TextEditRules::DidRedo(Selection* aSelection,
nsresult aResult)
{
if (!aSelection) {
return NS_ERROR_NULL_POINTER;
}
if (NS_FAILED(aResult)) {
return aResult; // if aResult is an error, we return it.
}
NS_ENSURE_STATE(mTextEditor);
RefPtr<Element> theRoot = mTextEditor->GetRoot();
NS_ENSURE_TRUE(theRoot, NS_ERROR_FAILURE);
nsCOMPtr<nsIHTMLCollection> nodeList =
theRoot->GetElementsByTagName(NS_LITERAL_STRING("br"));
MOZ_ASSERT(nodeList);
uint32_t len = nodeList->Length();
if (len != 1) {
// only in the case of one br could there be the bogus node
mBogusNode = nullptr;
return NS_OK;
}
RefPtr<Element> node = nodeList->Item(0);
if (mTextEditor->IsMozEditorBogusNode(node)) {
mBogusNode = do_QueryInterface(node);
} else {
mBogusNode = nullptr;
}
return NS_OK;
}
1999-06-25 03:36:56 +04:00
nsresult
TextEditRules::WillOutputText(Selection* aSelection,
const nsAString* aOutputFormat,
nsAString* aOutString,
bool* aCancel,
bool* aHandled)
1999-06-25 03:36:56 +04:00
{
// null selection ok
if (!aOutString || !aOutputFormat || !aCancel || !aHandled) {
return NS_ERROR_NULL_POINTER;
}
1999-06-25 03:36:56 +04:00
// initialize out param
*aCancel = false;
*aHandled = false;
nsAutoString outputFormat(*aOutputFormat);
ToLowerCase(outputFormat);
if (outputFormat.EqualsLiteral("text/plain")) {
// Only use these rules for plain text output.
if (IsPasswordEditor()) {
*aOutString = mPasswordText;
*aHandled = true;
} else if (mBogusNode) {
// This means there's no content, so output null string.
aOutString->Truncate();
*aHandled = true;
}
}
1999-06-25 03:36:56 +04:00
return NS_OK;
}
nsresult
TextEditRules::DidOutputText(Selection* aSelection,
nsresult aResult)
1999-06-25 03:36:56 +04:00
{
return NS_OK;
}
nsresult
TextEditRules::RemoveRedundantTrailingBR()
{
// If the bogus node exists, we have no work to do
if (mBogusNode) {
return NS_OK;
}
// Likewise, nothing to be done if we could never have inserted a trailing br
if (IsSingleLineEditor()) {
return NS_OK;
}
NS_ENSURE_STATE(mTextEditor);
RefPtr<dom::Element> body = mTextEditor->GetRoot();
if (!body) {
return NS_ERROR_NULL_POINTER;
}
uint32_t childCount = body->GetChildCount();
if (childCount > 1) {
// The trailing br is redundant if it is the only remaining child node
return NS_OK;
}
Bug 1207245 - part 6 - rename nsRefPtr<T> to RefPtr<T>; r=ehsan; a=Tomcat The bulk of this commit was generated with a script, executed at the top level of a typical source code checkout. The only non-machine-generated part was modifying MFBT's moz.build to reflect the new naming. CLOSED TREE makes big refactorings like this a piece of cake. # The main substitution. find . -name '*.cpp' -o -name '*.cc' -o -name '*.h' -o -name '*.mm' -o -name '*.idl'| \ xargs perl -p -i -e ' s/nsRefPtr\.h/RefPtr\.h/g; # handle includes s/nsRefPtr ?</RefPtr</g; # handle declarations and variables ' # Handle a special friend declaration in gfx/layers/AtomicRefCountedWithFinalize.h. perl -p -i -e 's/::nsRefPtr;/::RefPtr;/' gfx/layers/AtomicRefCountedWithFinalize.h # Handle nsRefPtr.h itself, a couple places that define constructors # from nsRefPtr, and code generators specially. We do this here, rather # than indiscriminantly s/nsRefPtr/RefPtr/, because that would rename # things like nsRefPtrHashtable. perl -p -i -e 's/nsRefPtr/RefPtr/g' \ mfbt/nsRefPtr.h \ xpcom/glue/nsCOMPtr.h \ xpcom/base/OwningNonNull.h \ ipc/ipdl/ipdl/lower.py \ ipc/ipdl/ipdl/builtin.py \ dom/bindings/Codegen.py \ python/lldbutils/lldbutils/utils.py # In our indiscriminate substitution above, we renamed # nsRefPtrGetterAddRefs, the class behind getter_AddRefs. Fix that up. find . -name '*.cpp' -o -name '*.h' -o -name '*.idl' | \ xargs perl -p -i -e 's/nsRefPtrGetterAddRefs/RefPtrGetterAddRefs/g' if [ -d .git ]; then git mv mfbt/nsRefPtr.h mfbt/RefPtr.h else hg mv mfbt/nsRefPtr.h mfbt/RefPtr.h fi --HG-- rename : mfbt/nsRefPtr.h => mfbt/RefPtr.h
2015-10-18 08:24:48 +03:00
RefPtr<nsIContent> child = body->GetFirstChild();
if (!child || !child->IsElement()) {
return NS_OK;
}
dom::Element* elem = child->AsElement();
if (!TextEditUtils::IsMozBR(elem)) {
return NS_OK;
}
// Rather than deleting this node from the DOM tree we should instead
// morph this br into the bogus node
elem->UnsetAttr(kNameSpaceID_None, nsGkAtoms::type, true);
// set mBogusNode to be this <br>
mBogusNode = do_QueryInterface(elem);
// give it the bogus node attribute
elem->SetAttr(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom,
kMOZEditorBogusNodeValue, false);
return NS_OK;
}
nsresult
TextEditRules::CreateTrailingBRIfNeeded()
{
// but only if we aren't a single line edit field
if (IsSingleLineEditor()) {
return NS_OK;
}
NS_ENSURE_STATE(mTextEditor);
dom::Element* body = mTextEditor->GetRoot();
NS_ENSURE_TRUE(body, NS_ERROR_NULL_POINTER);
nsIContent* lastChild = body->GetLastChild();
// assuming CreateBogusNodeIfNeeded() has been called first
NS_ENSURE_TRUE(lastChild, NS_ERROR_NULL_POINTER);
if (!lastChild->IsHTMLElement(nsGkAtoms::br)) {
AutoTransactionsConserveSelection dontSpazMySelection(mTextEditor);
nsCOMPtr<nsIDOMNode> domBody = do_QueryInterface(body);
return CreateMozBR(domBody, body->Length());
}
// Check to see if the trailing BR is a former bogus node - this will have
// stuck around if we previously morphed a trailing node into a bogus node.
if (!mTextEditor->IsMozEditorBogusNode(lastChild)) {
return NS_OK;
}
// Morph it back to a mozBR
lastChild->UnsetAttr(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom, false);
lastChild->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
NS_LITERAL_STRING("_moz"), true);
return NS_OK;
}
Preparation for ender-based text control * added focus listener. Doesn't do much yet, but when focus notifications start appearing, we'll be ready for them. The code is in place to hide selection when we lose focus and paint selection when we get focus. That's probably not quite right, but it's a start. We will need to be able to determine the distinction between losing focus to another control within our app, and losing focus to another app. * added support for disabled and readonly states in the editor. This is accomplished by having flags set by the client, and letting the rules system deal with those flags. The flags I added are: TEXT_EDITOR_FLAG_PLAINTEXT 0x01 // only plain text editing is allowed TEXT_EDITOR_FLAG_SINGLELINE 0x02 // enter key and CR-LF handled specially TEXT_EDITOR_FLAG_PASSWORD 0x04 // text is not entered into content, only a representative character TEXT_EDITOR_FLAG_READONLY 0x08 // editing events are disabled. Editor may still accept focus. TEXT_EDITOR_FLAG_DISALBED 0x10 // all events are disabled (like scrolling). Editor will not accept focus. * added WillInsertBreak/DidInsertBreak into text rules, so flags could be checked. This gets us readonly, disabled, and single line behavior. * cleaned up the code that allocates, registers, and destroys event listeners. Thanks to Kin and Simon for cleaning up the ownership model on the listeners, it was a big help. * added support for a max text length. You can now tell the text editor, be no bigger than n characters.
1999-05-29 01:24:18 +04:00
nsresult
TextEditRules::CreateBogusNodeIfNeeded(Selection* aSelection)
Preparation for ender-based text control * added focus listener. Doesn't do much yet, but when focus notifications start appearing, we'll be ready for them. The code is in place to hide selection when we lose focus and paint selection when we get focus. That's probably not quite right, but it's a start. We will need to be able to determine the distinction between losing focus to another control within our app, and losing focus to another app. * added support for disabled and readonly states in the editor. This is accomplished by having flags set by the client, and letting the rules system deal with those flags. The flags I added are: TEXT_EDITOR_FLAG_PLAINTEXT 0x01 // only plain text editing is allowed TEXT_EDITOR_FLAG_SINGLELINE 0x02 // enter key and CR-LF handled specially TEXT_EDITOR_FLAG_PASSWORD 0x04 // text is not entered into content, only a representative character TEXT_EDITOR_FLAG_READONLY 0x08 // editing events are disabled. Editor may still accept focus. TEXT_EDITOR_FLAG_DISALBED 0x10 // all events are disabled (like scrolling). Editor will not accept focus. * added WillInsertBreak/DidInsertBreak into text rules, so flags could be checked. This gets us readonly, disabled, and single line behavior. * cleaned up the code that allocates, registers, and destroys event listeners. Thanks to Kin and Simon for cleaning up the ownership model on the listeners, it was a big help. * added support for a max text length. You can now tell the text editor, be no bigger than n characters.
1999-05-29 01:24:18 +04:00
{
NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
NS_ENSURE_TRUE(mTextEditor, NS_ERROR_NULL_POINTER);
if (mBogusNode) {
// Let's not create more than one, ok?
return NS_OK;
}
// tell rules system to not do any post-processing
AutoRules beginRulesSniffing(mTextEditor, EditAction::ignore,
nsIEditor::eNone);
nsCOMPtr<dom::Element> body = mTextEditor->GetRoot();
if (!body) {
// We don't even have a body yet, don't insert any bogus nodes at
// this point.
return NS_OK;
}
// Now we've got the body element. Iterate over the body element's children,
// looking for editable content. If no editable content is found, insert the
// bogus node.
for (nsCOMPtr<nsIContent> bodyChild = body->GetFirstChild();
bodyChild;
bodyChild = bodyChild->GetNextSibling()) {
if (mTextEditor->IsMozEditorBogusNode(bodyChild) ||
!mTextEditor->IsEditable(body) || // XXX hoist out of the loop?
mTextEditor->IsEditable(bodyChild) ||
mTextEditor->IsBlockNode(bodyChild)) {
return NS_OK;
}
}
// Skip adding the bogus node if body is read-only.
if (!mTextEditor->IsModifiableNode(body)) {
return NS_OK;
}
// Create a br.
nsCOMPtr<Element> newContent = mTextEditor->CreateHTMLContent(nsGkAtoms::br);
NS_ENSURE_STATE(newContent);
// set mBogusNode to be the newly created <br>
mBogusNode = do_QueryInterface(newContent);
NS_ENSURE_TRUE(mBogusNode, NS_ERROR_NULL_POINTER);
// Give it a special attribute.
newContent->SetAttr(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom,
kMOZEditorBogusNodeValue, false);
// Put the node in the document.
nsCOMPtr<nsIDOMNode> bodyNode = do_QueryInterface(body);
nsresult rv = mTextEditor->InsertNode(mBogusNode, bodyNode, 0);
NS_ENSURE_SUCCESS(rv, rv);
// Set selection.
aSelection->CollapseNative(body, 0);
return NS_OK;
}
nsresult
TextEditRules::TruncateInsertionIfNeeded(Selection* aSelection,
const nsAString* aInString,
nsAString* aOutString,
int32_t aMaxLength,
bool* aTruncated)
{
if (!aSelection || !aInString || !aOutString) {
return NS_ERROR_NULL_POINTER;
}
if (!aOutString->Assign(*aInString, mozilla::fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (aTruncated) {
*aTruncated = false;
}
NS_ENSURE_STATE(mTextEditor);
if (-1 != aMaxLength && IsPlaintextEditor() &&
!mTextEditor->IsIMEComposing()) {
// Get the current text length.
// Get the length of inString.
// Get the length of the selection.
// If selection is collapsed, it is length 0.
// Subtract the length of the selection from the len(doc)
// since we'll delete the selection on insert.
// This is resultingDocLength.
// Get old length of IME composing string
// which will be replaced by new one.
// If (resultingDocLength) is at or over max, cancel the insert
// If (resultingDocLength) + (length of input) > max,
// set aOutString to subset of inString so length = max
int32_t docLength;
nsresult rv = mTextEditor->GetTextLength(&docLength);
if (NS_FAILED(rv)) {
return rv;
}
int32_t start, end;
nsContentUtils::GetSelectionInTextControl(aSelection,
mTextEditor->GetRoot(),
start, end);
TextComposition* composition = mTextEditor->GetComposition();
int32_t oldCompStrLength = composition ? composition->String().Length() : 0;
const int32_t selectionLength = end - start;
const int32_t resultingDocLength = docLength - selectionLength - oldCompStrLength;
if (resultingDocLength >= aMaxLength) {
// This call is guaranteed to reduce the capacity of the string, so it
// cannot cause an OOM.
aOutString->Truncate();
if (aTruncated) {
*aTruncated = true;
}
} else {
int32_t oldLength = aOutString->Length();
if (oldLength + resultingDocLength > aMaxLength) {
int32_t newLength = aMaxLength - resultingDocLength;
MOZ_ASSERT(newLength > 0);
char16_t newLastChar = aOutString->CharAt(newLength - 1);
char16_t removingFirstChar = aOutString->CharAt(newLength);
// Don't separate the string between a surrogate pair.
if (NS_IS_HIGH_SURROGATE(newLastChar) &&
NS_IS_LOW_SURROGATE(removingFirstChar)) {
newLength--;
}
// XXX What should we do if we're removing IVS and its preceding
// character won't be removed?
// This call is guaranteed to reduce the capacity of the string, so it
// cannot cause an OOM.
aOutString->Truncate(newLength);
if (aTruncated) {
*aTruncated = true;
}
}
}
}
return NS_OK;
Preparation for ender-based text control * added focus listener. Doesn't do much yet, but when focus notifications start appearing, we'll be ready for them. The code is in place to hide selection when we lose focus and paint selection when we get focus. That's probably not quite right, but it's a start. We will need to be able to determine the distinction between losing focus to another control within our app, and losing focus to another app. * added support for disabled and readonly states in the editor. This is accomplished by having flags set by the client, and letting the rules system deal with those flags. The flags I added are: TEXT_EDITOR_FLAG_PLAINTEXT 0x01 // only plain text editing is allowed TEXT_EDITOR_FLAG_SINGLELINE 0x02 // enter key and CR-LF handled specially TEXT_EDITOR_FLAG_PASSWORD 0x04 // text is not entered into content, only a representative character TEXT_EDITOR_FLAG_READONLY 0x08 // editing events are disabled. Editor may still accept focus. TEXT_EDITOR_FLAG_DISALBED 0x10 // all events are disabled (like scrolling). Editor will not accept focus. * added WillInsertBreak/DidInsertBreak into text rules, so flags could be checked. This gets us readonly, disabled, and single line behavior. * cleaned up the code that allocates, registers, and destroys event listeners. Thanks to Kin and Simon for cleaning up the ownership model on the listeners, it was a big help. * added support for a max text length. You can now tell the text editor, be no bigger than n characters.
1999-05-29 01:24:18 +04:00
}
void
TextEditRules::ResetIMETextPWBuf()
{
mPasswordIMEText.Truncate();
}
void
TextEditRules::RemoveIMETextFromPWBuf(int32_t& aStart,
nsAString* aIMEString)
{
MOZ_ASSERT(aIMEString);
// initialize PasswordIME
if (mPasswordIMEText.IsEmpty()) {
mPasswordIMEIndex = aStart;
} else {
// manage the password buffer
mPasswordText.Cut(mPasswordIMEIndex, mPasswordIMEText.Length());
aStart = mPasswordIMEIndex;
}
mPasswordIMEText.Assign(*aIMEString);
}
NS_IMETHODIMP
TextEditRules::Notify(nsITimer* aTimer)
{
MOZ_ASSERT(mTimer);
// Check whether our text editor's password flag was changed before this
// "hide password character" timer actually fires.
nsresult rv = IsPasswordEditor() ? HideLastPWInput() : NS_OK;
ASSERT_PASSWORD_LENGTHS_EQUAL();
mLastLength = 0;
return rv;
}
nsresult
TextEditRules::HideLastPWInput()
{
if (!mLastLength) {
// Special case, we're trying to replace a range that no longer exists
return NS_OK;
}
nsAutoString hiddenText;
FillBufWithPWChars(&hiddenText, mLastLength);
NS_ENSURE_STATE(mTextEditor);
RefPtr<Selection> selection = mTextEditor->GetSelection();
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
int32_t start, end;
nsContentUtils::GetSelectionInTextControl(selection, mTextEditor->GetRoot(),
start, end);
nsCOMPtr<nsINode> selNode = GetTextNode(selection);
NS_ENSURE_TRUE(selNode, NS_OK);
selNode->GetAsText()->ReplaceData(mLastStart, mLastLength, hiddenText);
selection->Collapse(selNode, start);
if (start != end) {
selection->Extend(selNode, end);
}
return NS_OK;
}
// static
void
TextEditRules::FillBufWithPWChars(nsAString* aOutString,
int32_t aLength)
{
MOZ_ASSERT(aOutString);
// change the output to the platform password character
char16_t passwordChar = LookAndFeel::GetPasswordCharacter();
aOutString->Truncate();
for (int32_t i = 0; i < aLength; i++) {
aOutString->Append(passwordChar);
}
}
/**
* CreateMozBR() puts a BR node with moz attribute at {inParent, inOffset}.
*/
nsresult
TextEditRules::CreateMozBR(nsIDOMNode* inParent,
int32_t inOffset,
nsIDOMNode** outBRNode)
{
NS_ENSURE_TRUE(inParent, NS_ERROR_NULL_POINTER);
nsCOMPtr<nsIDOMNode> brNode;
NS_ENSURE_STATE(mTextEditor);
nsresult rv = mTextEditor->CreateBR(inParent, inOffset, address_of(brNode));
NS_ENSURE_SUCCESS(rv, rv);
// give it special moz attr
nsCOMPtr<Element> brElem = do_QueryInterface(brNode);
if (brElem) {
rv = mTextEditor->SetAttribute(brElem, nsGkAtoms::type,
NS_LITERAL_STRING("_moz"));
NS_ENSURE_SUCCESS(rv, rv);
}
if (outBRNode) {
brNode.forget(outBRNode);
}
return NS_OK;
}
NS_IMETHODIMP
TextEditRules::DocumentModified()
{
return NS_ERROR_NOT_IMPLEMENTED;
}
bool
TextEditRules::IsPasswordEditor() const
{
return mTextEditor ? mTextEditor->IsPasswordEditor() : false;
}
bool
TextEditRules::IsSingleLineEditor() const
{
return mTextEditor ? mTextEditor->IsSingleLineEditor() : false;
}
bool
TextEditRules::IsPlaintextEditor() const
{
return mTextEditor ? mTextEditor->IsPlaintextEditor() : false;
}
bool
TextEditRules::IsReadonly() const
{
return mTextEditor ? mTextEditor->IsReadonly() : false;
}
bool
TextEditRules::IsDisabled() const
{
return mTextEditor ? mTextEditor->IsDisabled() : false;
}
bool
TextEditRules::IsMailEditor() const
{
return mTextEditor ? mTextEditor->IsMailEditor() : false;
}
bool
TextEditRules::DontEchoPassword() const
{
return mTextEditor ? mTextEditor->DontEchoPassword() : false;
}
} // namespace mozilla