2001-09-25 22:53:13 +00:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
2012-05-21 11:12:37 +00: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/. */
|
1999-03-10 19:53:26 +00:00
|
|
|
|
2016-07-09 02:34:41 +00:00
|
|
|
#include "mozilla/TextEditRules.h"
|
2014-12-02 05:07:42 +00:00
|
|
|
|
2017-12-21 05:52:32 +00:00
|
|
|
#include "HTMLEditRules.h"
|
2016-07-07 04:44:32 +00:00
|
|
|
#include "TextEditUtils.h"
|
2012-07-13 06:33:42 +00:00
|
|
|
#include "mozilla/Assertions.h"
|
2018-01-12 10:01:04 +00:00
|
|
|
#include "mozilla/EditAction.h"
|
2017-11-07 10:50:25 +00:00
|
|
|
#include "mozilla/EditorDOMPoint.h"
|
2016-07-08 05:03:31 +00:00
|
|
|
#include "mozilla/EditorUtils.h"
|
2012-07-13 06:33:42 +00:00
|
|
|
#include "mozilla/LookAndFeel.h"
|
|
|
|
#include "mozilla/Preferences.h"
|
2014-04-03 04:18:37 +00:00
|
|
|
#include "mozilla/TextComposition.h"
|
2016-07-09 02:54:50 +00:00
|
|
|
#include "mozilla/TextEditor.h"
|
2012-07-13 06:33:42 +00:00
|
|
|
#include "mozilla/dom/Element.h"
|
2018-02-01 19:26:12 +00:00
|
|
|
#include "mozilla/dom/NodeFilterBinding.h"
|
2016-07-09 02:34:41 +00:00
|
|
|
#include "mozilla/dom/NodeIterator.h"
|
|
|
|
#include "mozilla/dom/Selection.h"
|
2012-07-13 06:33:42 +00:00
|
|
|
#include "nsAString.h"
|
1999-03-10 19:53:26 +00:00
|
|
|
#include "nsCOMPtr.h"
|
2012-07-13 06:33:42 +00:00
|
|
|
#include "nsCRT.h"
|
|
|
|
#include "nsCRTGlue.h"
|
|
|
|
#include "nsComponentManagerUtils.h"
|
|
|
|
#include "nsContentUtils.h"
|
|
|
|
#include "nsDebug.h"
|
|
|
|
#include "nsError.h"
|
|
|
|
#include "nsGkAtoms.h"
|
|
|
|
#include "nsIContent.h"
|
2017-06-16 10:08:10 +00:00
|
|
|
#include "nsIDocumentEncoder.h"
|
2014-02-27 23:04:46 +00:00
|
|
|
#include "nsNameSpaceManager.h"
|
2012-07-13 06:33:42 +00:00
|
|
|
#include "nsINode.h"
|
|
|
|
#include "nsIPlaintextEditor.h"
|
|
|
|
#include "nsISupportsBase.h"
|
|
|
|
#include "nsLiteralString.h"
|
2017-05-25 05:30:50 +00:00
|
|
|
#include "nsTextNode.h"
|
2012-07-13 06:33:42 +00:00
|
|
|
#include "nsUnicharUtils.h"
|
2017-02-02 15:32:58 +00:00
|
|
|
#include "nsIHTMLCollection.h"
|
2017-05-25 05:30:50 +00:00
|
|
|
#include "nsPrintfCString.h"
|
2011-06-17 00:59:29 +00:00
|
|
|
|
2016-07-09 02:34:41 +00:00
|
|
|
namespace mozilla {
|
|
|
|
|
|
|
|
using namespace dom;
|
2011-06-17 00:59:29 +00:00
|
|
|
|
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-28 21:24:18 +00:00
|
|
|
#define CANCEL_OPERATION_IF_READONLY_OR_DISABLED \
|
2018-11-03 11:20:06 +00:00
|
|
|
if (IsReadonly() || IsDisabled()) { \
|
2011-10-17 14:59:28 +00:00
|
|
|
*aCancel = true; \
|
2018-11-03 11:20:06 +00:00
|
|
|
return NS_OK; \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY_OF_DISABLED \
|
|
|
|
if (IsReadonly() || IsDisabled()) { \
|
|
|
|
return EditActionCanceled(NS_OK); \
|
|
|
|
}
|
1999-03-13 04:53:21 +00:00
|
|
|
|
1999-04-05 17:21:59 +00:00
|
|
|
/********************************************************
|
2016-07-09 02:34:41 +00:00
|
|
|
* mozilla::TextEditRules
|
1999-04-05 17:21:59 +00:00
|
|
|
********************************************************/
|
1999-03-29 08:02:05 +00:00
|
|
|
|
2016-07-09 02:34:41 +00:00
|
|
|
TextEditRules::TextEditRules()
|
2016-07-09 02:54:50 +00:00
|
|
|
: mTextEditor(nullptr),
|
2018-04-26 13:41:34 +00:00
|
|
|
mData(nullptr),
|
2016-01-12 18:16:59 +00:00
|
|
|
mPasswordIMEIndex(0),
|
|
|
|
mCachedSelectionOffset(0),
|
|
|
|
mActionNesting(0),
|
|
|
|
mLockRulesSniffing(false),
|
|
|
|
mDidExplicitlySetInterline(false),
|
|
|
|
mDeleteBidiImmediately(false),
|
2017-12-21 05:52:32 +00:00
|
|
|
mIsHTMLEditRules(false),
|
2018-05-28 11:29:34 +00:00
|
|
|
mTopLevelEditSubAction(EditSubAction::eNone),
|
2016-01-12 18:16:59 +00:00
|
|
|
mLastStart(0),
|
|
|
|
mLastLength(0) {
|
2014-05-09 21:02:29 +00:00
|
|
|
InitFields();
|
|
|
|
}
|
|
|
|
|
2016-07-09 02:34:41 +00:00
|
|
|
void TextEditRules::InitFields() {
|
2016-07-09 02:54:50 +00:00
|
|
|
mTextEditor = nullptr;
|
2014-05-09 21:02:29 +00:00
|
|
|
mPasswordText.Truncate();
|
|
|
|
mPasswordIMEText.Truncate();
|
|
|
|
mPasswordIMEIndex = 0;
|
|
|
|
mBogusNode = nullptr;
|
|
|
|
mCachedSelectionNode = nullptr;
|
|
|
|
mCachedSelectionOffset = 0;
|
|
|
|
mActionNesting = 0;
|
|
|
|
mLockRulesSniffing = false;
|
|
|
|
mDidExplicitlySetInterline = false;
|
|
|
|
mDeleteBidiImmediately = false;
|
2018-05-28 11:29:34 +00:00
|
|
|
mTopLevelEditSubAction = EditSubAction::eNone;
|
2014-05-09 21:02:29 +00:00
|
|
|
mTimer = nullptr;
|
|
|
|
mLastStart = 0;
|
|
|
|
mLastLength = 0;
|
1999-03-10 19:53:26 +00:00
|
|
|
}
|
|
|
|
|
2016-07-09 02:34:41 +00:00
|
|
|
TextEditRules::~TextEditRules() {
|
2016-07-09 02:54:50 +00:00
|
|
|
// do NOT delete mTextEditor here. We do not hold a ref count to
|
|
|
|
// mTextEditor. mTextEditor owns our lifespan.
|
2009-11-08 03:01:29 +00:00
|
|
|
|
2016-10-24 02:27:45 +00:00
|
|
|
if (mTimer) {
|
2009-11-08 03:01:29 +00:00
|
|
|
mTimer->Cancel();
|
2016-10-24 02:27:45 +00:00
|
|
|
}
|
1999-03-10 19:53:26 +00:00
|
|
|
}
|
|
|
|
|
2017-12-21 05:52:32 +00:00
|
|
|
HTMLEditRules* TextEditRules::AsHTMLEditRules() {
|
|
|
|
return mIsHTMLEditRules ? static_cast<HTMLEditRules*>(this) : nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const HTMLEditRules* TextEditRules::AsHTMLEditRules() const {
|
|
|
|
return mIsHTMLEditRules ? static_cast<const HTMLEditRules*>(this) : nullptr;
|
|
|
|
}
|
|
|
|
|
2016-07-09 02:34:41 +00:00
|
|
|
NS_IMPL_CYCLE_COLLECTION(TextEditRules, mBogusNode, mCachedSelectionNode)
|
2000-03-24 00:26:47 +00:00
|
|
|
|
2016-07-09 02:34:41 +00:00
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TextEditRules)
|
2009-11-18 18:45:26 +00:00
|
|
|
NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
|
2017-07-26 18:18:20 +00:00
|
|
|
NS_INTERFACE_MAP_ENTRY(nsINamed)
|
2017-12-21 05:52:32 +00:00
|
|
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimerCallback)
|
2009-05-09 04:59:25 +00:00
|
|
|
NS_INTERFACE_MAP_END
|
|
|
|
|
2016-07-09 02:34:41 +00:00
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(TextEditRules)
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(TextEditRules)
|
1999-03-29 08:02:05 +00:00
|
|
|
|
2016-07-09 02:54:50 +00:00
|
|
|
nsresult TextEditRules::Init(TextEditor* aTextEditor) {
|
2018-04-26 13:41:34 +00:00
|
|
|
if (NS_WARN_IF(!aTextEditor)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
|
|
|
|
Selection* selection = aTextEditor->GetSelection();
|
|
|
|
if (NS_WARN_IF(!selection)) {
|
|
|
|
return NS_ERROR_FAILURE;
|
2016-07-09 02:34:41 +00:00
|
|
|
}
|
1999-08-09 01:37:50 +00:00
|
|
|
|
2014-05-09 21:02:29 +00:00
|
|
|
InitFields();
|
|
|
|
|
2016-07-09 02:34:41 +00:00
|
|
|
// We hold a non-refcounted reference back to our editor.
|
2016-07-09 02:54:50 +00:00
|
|
|
mTextEditor = aTextEditor;
|
2018-10-30 10:02:58 +00:00
|
|
|
AutoSafeEditorData setData(*this, *mTextEditor);
|
2000-01-13 10:17:35 +00:00
|
|
|
|
2018-04-26 13:41:34 +00:00
|
|
|
// Put in a magic <br> if needed. This method handles null selection,
|
2005-03-24 19:00:01 +00:00
|
|
|
// which should never happen anyway
|
2018-05-10 06:03:21 +00:00
|
|
|
nsresult rv = CreateBogusNodeIfNeeded();
|
2018-04-26 13:41:34 +00:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
fixes:
14753, 29843, 39864, 40141,
40139, 36679, 39542, 34729,
34855, 37216, 39292, 26447
r=sfraser,cmanske,fm; a=beppe
2000-05-24 23:00:24 +00:00
|
|
|
|
2010-02-02 04:00:12 +00:00
|
|
|
// If the selection hasn't been set up yet, set it up collapsed to the end of
|
|
|
|
// our editable content.
|
2018-10-30 10:02:58 +00:00
|
|
|
if (!SelectionRefPtr()->RangeCount()) {
|
2018-10-30 10:01:38 +00:00
|
|
|
rv = TextEditorRef().CollapseSelectionToEnd();
|
2018-04-26 13:41:34 +00:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2010-02-02 04:00:12 +00:00
|
|
|
}
|
|
|
|
|
2016-10-24 02:27:45 +00:00
|
|
|
if (IsPlaintextEditor()) {
|
2003-07-18 14:12:51 +00:00
|
|
|
// ensure trailing br node
|
2016-10-19 09:09:33 +00:00
|
|
|
rv = CreateTrailingBRIfNeeded();
|
2018-04-26 13:41:34 +00:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2002-08-23 17:57:51 +00:00
|
|
|
}
|
2003-07-18 14:12:51 +00:00
|
|
|
|
2018-04-26 13:41:34 +00:00
|
|
|
// XXX We should use AddBoolVarCache and use "current" value at initializing.
|
2011-06-17 00:59:29 +00:00
|
|
|
mDeleteBidiImmediately =
|
2011-09-29 06:19:26 +00:00
|
|
|
Preferences::GetBool("bidi.edit.delete_immediately", false);
|
2006-03-07 06:03:37 +00:00
|
|
|
|
2016-10-19 09:09:33 +00:00
|
|
|
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-28 21:24:18 +00:00
|
|
|
}
|
|
|
|
|
2016-07-09 02:34:41 +00:00
|
|
|
nsresult TextEditRules::SetInitialValue(const nsAString& aValue) {
|
2014-04-14 12:33:47 +00:00
|
|
|
if (IsPasswordEditor()) {
|
|
|
|
mPasswordText = aValue;
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2016-07-09 02:34:41 +00:00
|
|
|
nsresult TextEditRules::DetachEditor() {
|
2016-10-24 02:27:45 +00:00
|
|
|
if (mTimer) {
|
2009-11-08 03:01:29 +00:00
|
|
|
mTimer->Cancel();
|
2016-10-24 02:27:45 +00:00
|
|
|
}
|
2016-07-09 02:54:50 +00:00
|
|
|
mTextEditor = nullptr;
|
2009-05-09 04:59:24 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
Bug 1463985 - part 1: Rename EditAction to EditSubAction and related stuff r=m_kato
When we implement InputEvent.inputType, we need to set a stack class to record
which edit action is currently handled. However, currently, we call smaller
jobs as edit action. For example, when user types a character at selecting
some characters, then, EditAction::deleteSelection is performed first, then,
EditAction::insertText is performed. However, for the InputEvent.inputType,
we need inserText information. So, for making new enum EditAction, we need
to rename current EditAction to EditSubAction.
And also this renames related stuff:
EditorBase::mIsInEditAction -> EditorBase::mIsInEditSubAction
EditorBase::IsInEditAction() -> EditorBase::IsInEditSubAction()
EditorBase::mAction -> EditorBase::mTopLevelEditSubAction
TextEditRules::mTheAction -> TextEditRules::mTopLevelEditSubAction
EditorBase::StartOperation() ->
EditorBase::OnStartToHandleTopLevelEditSubAction()
EditorBase::EndOperation() ->
EditorBase::OnEndHandlingTopLevelEditSubAction()
AutoRules -> AutoTopLevelEditSubActionNotifier
RulesInfo -> EditSubActionInfo
MozReview-Commit-ID: cvSkPUjFm1
--HG--
extra : rebase_source : baf527a3e353b7a8ebe9a46be2243b059c500234
2018-05-28 11:12:34 +00:00
|
|
|
nsresult TextEditRules::BeforeEdit(EditSubAction aEditSubAction,
|
2016-07-09 02:34:41 +00:00
|
|
|
nsIEditor::EDirection aDirection) {
|
2018-04-24 06:23:01 +00:00
|
|
|
if (NS_WARN_IF(!CanHandleEditAction())) {
|
|
|
|
return NS_ERROR_EDITOR_DESTROYED;
|
|
|
|
}
|
|
|
|
|
2016-10-24 02:27:45 +00:00
|
|
|
if (mLockRulesSniffing) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2015-05-28 15:58:42 +00:00
|
|
|
|
2016-06-24 12:19:18 +00:00
|
|
|
AutoLockRulesSniffing lockIt(this);
|
2011-10-17 14:59:28 +00:00
|
|
|
mDidExplicitlySetInterline = false;
|
2016-10-24 02:27:45 +00:00
|
|
|
if (!mActionNesting) {
|
2010-07-20 13:04:14 +00:00
|
|
|
// let rules remember the top level action
|
Bug 1463985 - part 1: Rename EditAction to EditSubAction and related stuff r=m_kato
When we implement InputEvent.inputType, we need to set a stack class to record
which edit action is currently handled. However, currently, we call smaller
jobs as edit action. For example, when user types a character at selecting
some characters, then, EditAction::deleteSelection is performed first, then,
EditAction::insertText is performed. However, for the InputEvent.inputType,
we need inserText information. So, for making new enum EditAction, we need
to rename current EditAction to EditSubAction.
And also this renames related stuff:
EditorBase::mIsInEditAction -> EditorBase::mIsInEditSubAction
EditorBase::IsInEditAction() -> EditorBase::IsInEditSubAction()
EditorBase::mAction -> EditorBase::mTopLevelEditSubAction
TextEditRules::mTheAction -> TextEditRules::mTopLevelEditSubAction
EditorBase::StartOperation() ->
EditorBase::OnStartToHandleTopLevelEditSubAction()
EditorBase::EndOperation() ->
EditorBase::OnEndHandlingTopLevelEditSubAction()
AutoRules -> AutoTopLevelEditSubActionNotifier
RulesInfo -> EditSubActionInfo
MozReview-Commit-ID: cvSkPUjFm1
--HG--
extra : rebase_source : baf527a3e353b7a8ebe9a46be2243b059c500234
2018-05-28 11:12:34 +00:00
|
|
|
mTopLevelEditSubAction = aEditSubAction;
|
2010-07-20 13:04:14 +00:00
|
|
|
}
|
|
|
|
mActionNesting++;
|
2015-05-28 15:58:42 +00:00
|
|
|
|
2018-05-28 13:20:43 +00:00
|
|
|
if (aEditSubAction == EditSubAction::eSetText) {
|
2017-05-31 05:50:51 +00:00
|
|
|
// setText replaces all text, so mCachedSelectionNode might be invalid on
|
|
|
|
// AfterEdit.
|
|
|
|
// Since this will be used as start position of spellchecker, we should
|
|
|
|
// use root instead.
|
2018-04-26 13:41:34 +00:00
|
|
|
mCachedSelectionNode = mTextEditor->GetRoot();
|
2017-05-31 05:50:51 +00:00
|
|
|
mCachedSelectionOffset = 0;
|
|
|
|
} else {
|
2018-04-26 13:41:34 +00:00
|
|
|
Selection* selection = mTextEditor->GetSelection();
|
|
|
|
if (NS_WARN_IF(!selection)) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2017-05-31 05:50:51 +00:00
|
|
|
mCachedSelectionNode = selection->GetAnchorNode();
|
2017-08-25 07:12:39 +00:00
|
|
|
mCachedSelectionOffset = selection->AnchorOffset();
|
2017-05-31 05:50:51 +00:00
|
|
|
}
|
2005-02-01 21:12:53 +00:00
|
|
|
|
1999-12-07 08:30:19 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
Bug 1463985 - part 1: Rename EditAction to EditSubAction and related stuff r=m_kato
When we implement InputEvent.inputType, we need to set a stack class to record
which edit action is currently handled. However, currently, we call smaller
jobs as edit action. For example, when user types a character at selecting
some characters, then, EditAction::deleteSelection is performed first, then,
EditAction::insertText is performed. However, for the InputEvent.inputType,
we need inserText information. So, for making new enum EditAction, we need
to rename current EditAction to EditSubAction.
And also this renames related stuff:
EditorBase::mIsInEditAction -> EditorBase::mIsInEditSubAction
EditorBase::IsInEditAction() -> EditorBase::IsInEditSubAction()
EditorBase::mAction -> EditorBase::mTopLevelEditSubAction
TextEditRules::mTheAction -> TextEditRules::mTopLevelEditSubAction
EditorBase::StartOperation() ->
EditorBase::OnStartToHandleTopLevelEditSubAction()
EditorBase::EndOperation() ->
EditorBase::OnEndHandlingTopLevelEditSubAction()
AutoRules -> AutoTopLevelEditSubActionNotifier
RulesInfo -> EditSubActionInfo
MozReview-Commit-ID: cvSkPUjFm1
--HG--
extra : rebase_source : baf527a3e353b7a8ebe9a46be2243b059c500234
2018-05-28 11:12:34 +00:00
|
|
|
nsresult TextEditRules::AfterEdit(EditSubAction aEditSubAction,
|
2016-07-09 02:34:41 +00:00
|
|
|
nsIEditor::EDirection aDirection) {
|
2018-04-24 06:23:01 +00:00
|
|
|
if (NS_WARN_IF(!CanHandleEditAction())) {
|
|
|
|
return NS_ERROR_EDITOR_DESTROYED;
|
|
|
|
}
|
|
|
|
|
2016-10-24 02:27:45 +00:00
|
|
|
if (mLockRulesSniffing) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2015-05-28 15:58:42 +00:00
|
|
|
|
2016-06-24 12:19:18 +00:00
|
|
|
AutoLockRulesSniffing lockIt(this);
|
2015-05-28 15:58:42 +00:00
|
|
|
|
2018-04-28 19:50:58 +00:00
|
|
|
MOZ_ASSERT(mActionNesting > 0, "bad action nesting!");
|
2016-10-24 02:27:45 +00:00
|
|
|
if (!--mActionNesting) {
|
2018-10-30 10:02:58 +00:00
|
|
|
AutoSafeEditorData setData(*this, *mTextEditor);
|
2015-05-28 15:58:42 +00:00
|
|
|
|
2018-10-30 10:01:38 +00:00
|
|
|
nsresult rv = TextEditorRef().HandleInlineSpellCheck(
|
2018-04-26 13:41:34 +00:00
|
|
|
aEditSubAction, mCachedSelectionNode, mCachedSelectionOffset, nullptr,
|
|
|
|
0, nullptr, 0);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2005-02-01 21:12:53 +00:00
|
|
|
|
2017-05-31 05:50:51 +00:00
|
|
|
// no longer uses mCachedSelectionNode, so release it.
|
|
|
|
mCachedSelectionNode = nullptr;
|
|
|
|
|
2011-08-12 19:53:10 +00:00
|
|
|
// if only trailing <br> remaining remove it
|
2016-10-19 09:09:33 +00:00
|
|
|
rv = RemoveRedundantTrailingBR();
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return rv;
|
|
|
|
}
|
2011-08-12 19:53:10 +00:00
|
|
|
|
1999-12-07 08:30:19 +00:00
|
|
|
// detect empty doc
|
2018-05-10 06:03:21 +00:00
|
|
|
rv = CreateBogusNodeIfNeeded();
|
2018-04-26 13:41:34 +00:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2015-05-28 15:58:42 +00:00
|
|
|
|
2011-08-12 19:53:10 +00:00
|
|
|
// ensure trailing br node
|
2016-10-19 09:09:33 +00:00
|
|
|
rv = CreateTrailingBRIfNeeded();
|
2018-04-26 13:41:34 +00:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2010-09-01 22:06:52 +00:00
|
|
|
|
2018-05-11 09:40:47 +00:00
|
|
|
// Collapse the selection to the trailing moz-<br> if it's at the end of
|
|
|
|
// our text node.
|
|
|
|
rv = CollapseSelectionToTrailingBRIfNeeded();
|
|
|
|
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
|
|
|
|
return NS_ERROR_EDITOR_DESTROYED;
|
|
|
|
}
|
|
|
|
NS_WARNING_ASSERTION(
|
|
|
|
NS_SUCCEEDED(rv),
|
|
|
|
"Failed to selection to after the text node in TextEditor");
|
1999-12-07 08:30:19 +00:00
|
|
|
}
|
2016-10-19 09:09:33 +00:00
|
|
|
return NS_OK;
|
1999-12-07 08:30:19 +00:00
|
|
|
}
|
|
|
|
|
2018-10-30 10:02:58 +00:00
|
|
|
nsresult TextEditRules::WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
|
2016-07-09 02:34:41 +00:00
|
|
|
bool* aHandled) {
|
2018-04-24 06:23:01 +00:00
|
|
|
if (NS_WARN_IF(!CanHandleEditAction())) {
|
|
|
|
return NS_ERROR_EDITOR_DESTROYED;
|
2018-04-26 13:41:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ASSERT(aCancel);
|
|
|
|
MOZ_ASSERT(aHandled);
|
1999-06-08 06:04:51 +00:00
|
|
|
|
2011-10-17 14:59:28 +00:00
|
|
|
*aCancel = false;
|
|
|
|
*aHandled = false;
|
1999-04-12 12:01:32 +00:00
|
|
|
|
2018-10-30 10:02:58 +00:00
|
|
|
AutoSafeEditorData setData(*this, *mTextEditor);
|
2018-04-26 13:41:34 +00:00
|
|
|
|
1999-04-12 12:01:32 +00:00
|
|
|
// my kingdom for dynamic cast
|
Bug 1463985 - part 1: Rename EditAction to EditSubAction and related stuff r=m_kato
When we implement InputEvent.inputType, we need to set a stack class to record
which edit action is currently handled. However, currently, we call smaller
jobs as edit action. For example, when user types a character at selecting
some characters, then, EditAction::deleteSelection is performed first, then,
EditAction::insertText is performed. However, for the InputEvent.inputType,
we need inserText information. So, for making new enum EditAction, we need
to rename current EditAction to EditSubAction.
And also this renames related stuff:
EditorBase::mIsInEditAction -> EditorBase::mIsInEditSubAction
EditorBase::IsInEditAction() -> EditorBase::IsInEditSubAction()
EditorBase::mAction -> EditorBase::mTopLevelEditSubAction
TextEditRules::mTheAction -> TextEditRules::mTopLevelEditSubAction
EditorBase::StartOperation() ->
EditorBase::OnStartToHandleTopLevelEditSubAction()
EditorBase::EndOperation() ->
EditorBase::OnEndHandlingTopLevelEditSubAction()
AutoRules -> AutoTopLevelEditSubActionNotifier
RulesInfo -> EditSubActionInfo
MozReview-Commit-ID: cvSkPUjFm1
--HG--
extra : rebase_source : baf527a3e353b7a8ebe9a46be2243b059c500234
2018-05-28 11:12:34 +00:00
|
|
|
switch (aInfo.mEditSubAction) {
|
2018-11-03 11:22:13 +00:00
|
|
|
case EditSubAction::eInsertLineBreak: {
|
2018-05-10 06:03:21 +00:00
|
|
|
UndefineCaretBidiLevel();
|
2018-11-03 11:20:06 +00:00
|
|
|
EditActionResult result = WillInsertLineBreak(aInfo.maxLength);
|
|
|
|
if (NS_WARN_IF(result.Failed())) {
|
|
|
|
return result.Rv();
|
|
|
|
}
|
|
|
|
*aCancel = result.Canceled();
|
|
|
|
*aHandled = result.Handled();
|
|
|
|
MOZ_ASSERT(!result.Ignored());
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2018-05-28 12:36:47 +00:00
|
|
|
case EditSubAction::eInsertText:
|
2018-05-28 12:44:39 +00:00
|
|
|
case EditSubAction::eInsertTextComingFromIME:
|
2018-05-10 06:03:21 +00:00
|
|
|
UndefineCaretBidiLevel();
|
Bug 1463985 - part 1: Rename EditAction to EditSubAction and related stuff r=m_kato
When we implement InputEvent.inputType, we need to set a stack class to record
which edit action is currently handled. However, currently, we call smaller
jobs as edit action. For example, when user types a character at selecting
some characters, then, EditAction::deleteSelection is performed first, then,
EditAction::insertText is performed. However, for the InputEvent.inputType,
we need inserText information. So, for making new enum EditAction, we need
to rename current EditAction to EditSubAction.
And also this renames related stuff:
EditorBase::mIsInEditAction -> EditorBase::mIsInEditSubAction
EditorBase::IsInEditAction() -> EditorBase::IsInEditSubAction()
EditorBase::mAction -> EditorBase::mTopLevelEditSubAction
TextEditRules::mTheAction -> TextEditRules::mTopLevelEditSubAction
EditorBase::StartOperation() ->
EditorBase::OnStartToHandleTopLevelEditSubAction()
EditorBase::EndOperation() ->
EditorBase::OnEndHandlingTopLevelEditSubAction()
AutoRules -> AutoTopLevelEditSubActionNotifier
RulesInfo -> EditSubActionInfo
MozReview-Commit-ID: cvSkPUjFm1
--HG--
extra : rebase_source : baf527a3e353b7a8ebe9a46be2243b059c500234
2018-05-28 11:12:34 +00:00
|
|
|
return WillInsertText(aInfo.mEditSubAction, aCancel, aHandled,
|
|
|
|
aInfo.inString, aInfo.outString, aInfo.maxLength);
|
2018-05-28 13:20:43 +00:00
|
|
|
case EditSubAction::eSetText:
|
2018-05-10 06:03:21 +00:00
|
|
|
UndefineCaretBidiLevel();
|
Bug 1463985 - part 1: Rename EditAction to EditSubAction and related stuff r=m_kato
When we implement InputEvent.inputType, we need to set a stack class to record
which edit action is currently handled. However, currently, we call smaller
jobs as edit action. For example, when user types a character at selecting
some characters, then, EditAction::deleteSelection is performed first, then,
EditAction::insertText is performed. However, for the InputEvent.inputType,
we need inserText information. So, for making new enum EditAction, we need
to rename current EditAction to EditSubAction.
And also this renames related stuff:
EditorBase::mIsInEditAction -> EditorBase::mIsInEditSubAction
EditorBase::IsInEditAction() -> EditorBase::IsInEditSubAction()
EditorBase::mAction -> EditorBase::mTopLevelEditSubAction
TextEditRules::mTheAction -> TextEditRules::mTopLevelEditSubAction
EditorBase::StartOperation() ->
EditorBase::OnStartToHandleTopLevelEditSubAction()
EditorBase::EndOperation() ->
EditorBase::OnEndHandlingTopLevelEditSubAction()
AutoRules -> AutoTopLevelEditSubActionNotifier
RulesInfo -> EditSubActionInfo
MozReview-Commit-ID: cvSkPUjFm1
--HG--
extra : rebase_source : baf527a3e353b7a8ebe9a46be2243b059c500234
2018-05-28 11:12:34 +00:00
|
|
|
return WillSetText(aCancel, aHandled, aInfo.inString, aInfo.maxLength);
|
2018-05-28 12:49:56 +00:00
|
|
|
case EditSubAction::eDeleteSelectedContent:
|
Bug 1463985 - part 1: Rename EditAction to EditSubAction and related stuff r=m_kato
When we implement InputEvent.inputType, we need to set a stack class to record
which edit action is currently handled. However, currently, we call smaller
jobs as edit action. For example, when user types a character at selecting
some characters, then, EditAction::deleteSelection is performed first, then,
EditAction::insertText is performed. However, for the InputEvent.inputType,
we need inserText information. So, for making new enum EditAction, we need
to rename current EditAction to EditSubAction.
And also this renames related stuff:
EditorBase::mIsInEditAction -> EditorBase::mIsInEditSubAction
EditorBase::IsInEditAction() -> EditorBase::IsInEditSubAction()
EditorBase::mAction -> EditorBase::mTopLevelEditSubAction
TextEditRules::mTheAction -> TextEditRules::mTopLevelEditSubAction
EditorBase::StartOperation() ->
EditorBase::OnStartToHandleTopLevelEditSubAction()
EditorBase::EndOperation() ->
EditorBase::OnEndHandlingTopLevelEditSubAction()
AutoRules -> AutoTopLevelEditSubActionNotifier
RulesInfo -> EditSubActionInfo
MozReview-Commit-ID: cvSkPUjFm1
--HG--
extra : rebase_source : baf527a3e353b7a8ebe9a46be2243b059c500234
2018-05-28 11:12:34 +00:00
|
|
|
return WillDeleteSelection(aInfo.collapsedAction, aCancel, aHandled);
|
2018-05-28 11:36:06 +00:00
|
|
|
case EditSubAction::eUndo:
|
2018-05-10 06:03:21 +00:00
|
|
|
return WillUndo(aCancel, aHandled);
|
2018-05-28 11:36:06 +00:00
|
|
|
case EditSubAction::eRedo:
|
2018-05-10 06:03:21 +00:00
|
|
|
return WillRedo(aCancel, aHandled);
|
2018-05-28 13:05:10 +00:00
|
|
|
case EditSubAction::eSetTextProperty:
|
2018-05-10 06:03:21 +00:00
|
|
|
return WillSetTextProperty(aCancel, aHandled);
|
2018-05-28 13:05:10 +00:00
|
|
|
case EditSubAction::eRemoveTextProperty:
|
2018-05-10 06:03:21 +00:00
|
|
|
return WillRemoveTextProperty(aCancel, aHandled);
|
2018-05-28 13:09:55 +00:00
|
|
|
case EditSubAction::eComputeTextToOutput:
|
Bug 1463985 - part 1: Rename EditAction to EditSubAction and related stuff r=m_kato
When we implement InputEvent.inputType, we need to set a stack class to record
which edit action is currently handled. However, currently, we call smaller
jobs as edit action. For example, when user types a character at selecting
some characters, then, EditAction::deleteSelection is performed first, then,
EditAction::insertText is performed. However, for the InputEvent.inputType,
we need inserText information. So, for making new enum EditAction, we need
to rename current EditAction to EditSubAction.
And also this renames related stuff:
EditorBase::mIsInEditAction -> EditorBase::mIsInEditSubAction
EditorBase::IsInEditAction() -> EditorBase::IsInEditSubAction()
EditorBase::mAction -> EditorBase::mTopLevelEditSubAction
TextEditRules::mTheAction -> TextEditRules::mTopLevelEditSubAction
EditorBase::StartOperation() ->
EditorBase::OnStartToHandleTopLevelEditSubAction()
EditorBase::EndOperation() ->
EditorBase::OnEndHandlingTopLevelEditSubAction()
AutoRules -> AutoTopLevelEditSubActionNotifier
RulesInfo -> EditSubActionInfo
MozReview-Commit-ID: cvSkPUjFm1
--HG--
extra : rebase_source : baf527a3e353b7a8ebe9a46be2243b059c500234
2018-05-28 11:12:34 +00:00
|
|
|
return WillOutputText(aInfo.outputFormat, aInfo.outString, aInfo.flags,
|
2018-05-10 06:03:21 +00:00
|
|
|
aCancel, aHandled);
|
2018-05-28 14:13:41 +00:00
|
|
|
case EditSubAction::eInsertElement:
|
2012-05-05 18:52:29 +00:00
|
|
|
// i had thought this would be html rules only. but we put pre elements
|
|
|
|
// into plaintext mail when doing quoting for reply! doh!
|
2018-05-11 10:06:07 +00:00
|
|
|
return WillInsert(aCancel);
|
2012-05-05 18:52:29 +00:00
|
|
|
default:
|
|
|
|
return NS_ERROR_FAILURE;
|
1999-04-05 17:21:59 +00:00
|
|
|
}
|
1999-03-29 08:02:05 +00:00
|
|
|
}
|
2012-05-22 09:37:17 +00:00
|
|
|
|
2018-10-30 10:02:58 +00:00
|
|
|
nsresult TextEditRules::DidDoAction(EditSubActionInfo& aInfo,
|
2016-07-09 02:34:41 +00:00
|
|
|
nsresult aResult) {
|
2018-04-24 06:23:01 +00:00
|
|
|
if (NS_WARN_IF(!CanHandleEditAction())) {
|
|
|
|
return NS_ERROR_EDITOR_DESTROYED;
|
2018-04-26 13:41:34 +00:00
|
|
|
}
|
|
|
|
|
2018-10-30 10:02:58 +00:00
|
|
|
AutoSafeEditorData setData(*this, *mTextEditor);
|
2018-04-26 13:41:34 +00:00
|
|
|
|
2003-07-18 14:12:51 +00:00
|
|
|
// don't let any txns in here move the selection around behind our back.
|
2000-01-05 12:24:10 +00:00
|
|
|
// Note that this won't prevent explicit selection setting from working.
|
2018-08-01 12:30:14 +00:00
|
|
|
AutoTransactionsConserveSelection dontChangeMySelection(TextEditorRef());
|
2015-05-28 15:58:42 +00:00
|
|
|
|
Bug 1463985 - part 1: Rename EditAction to EditSubAction and related stuff r=m_kato
When we implement InputEvent.inputType, we need to set a stack class to record
which edit action is currently handled. However, currently, we call smaller
jobs as edit action. For example, when user types a character at selecting
some characters, then, EditAction::deleteSelection is performed first, then,
EditAction::insertText is performed. However, for the InputEvent.inputType,
we need inserText information. So, for making new enum EditAction, we need
to rename current EditAction to EditSubAction.
And also this renames related stuff:
EditorBase::mIsInEditAction -> EditorBase::mIsInEditSubAction
EditorBase::IsInEditAction() -> EditorBase::IsInEditSubAction()
EditorBase::mAction -> EditorBase::mTopLevelEditSubAction
TextEditRules::mTheAction -> TextEditRules::mTopLevelEditSubAction
EditorBase::StartOperation() ->
EditorBase::OnStartToHandleTopLevelEditSubAction()
EditorBase::EndOperation() ->
EditorBase::OnEndHandlingTopLevelEditSubAction()
AutoRules -> AutoTopLevelEditSubActionNotifier
RulesInfo -> EditSubActionInfo
MozReview-Commit-ID: cvSkPUjFm1
--HG--
extra : rebase_source : baf527a3e353b7a8ebe9a46be2243b059c500234
2018-05-28 11:12:34 +00:00
|
|
|
switch (aInfo.mEditSubAction) {
|
2018-05-28 12:49:56 +00:00
|
|
|
case EditSubAction::eDeleteSelectedContent:
|
2018-05-11 07:48:29 +00:00
|
|
|
return DidDeleteSelection();
|
2018-05-28 11:36:06 +00:00
|
|
|
case EditSubAction::eUndo:
|
2018-05-10 06:03:21 +00:00
|
|
|
return DidUndo(aResult);
|
2018-05-28 11:36:06 +00:00
|
|
|
case EditSubAction::eRedo:
|
2018-05-10 06:03:21 +00:00
|
|
|
return DidRedo(aResult);
|
2012-05-05 18:52:29 +00:00
|
|
|
default:
|
|
|
|
// Don't fail on transactions we don't handle here!
|
|
|
|
return NS_OK;
|
1999-04-05 17:21:59 +00:00
|
|
|
}
|
1999-03-29 08:02:05 +00:00
|
|
|
}
|
1999-09-08 23:32:04 +00:00
|
|
|
|
2017-05-11 05:03:26 +00:00
|
|
|
bool TextEditRules::DocumentIsEmpty() {
|
2017-08-24 10:53:34 +00:00
|
|
|
bool retVal = false;
|
2018-07-18 08:44:14 +00:00
|
|
|
if (!mTextEditor || NS_FAILED(mTextEditor->IsEmpty(&retVal))) {
|
2017-08-24 10:53:34 +00:00
|
|
|
retVal = true;
|
2017-06-27 22:42:09 +00:00
|
|
|
}
|
|
|
|
|
2017-08-24 10:53:34 +00:00
|
|
|
return retVal;
|
1999-09-08 23:32:04 +00:00
|
|
|
}
|
1999-04-05 17:21:59 +00:00
|
|
|
|
2018-05-10 06:03:21 +00:00
|
|
|
nsresult TextEditRules::WillInsert(bool* aCancel) {
|
2018-04-26 15:09:10 +00:00
|
|
|
MOZ_ASSERT(IsEditorDataAvailable());
|
2015-05-28 15:58:42 +00:00
|
|
|
|
2016-04-23 09:30:17 +00:00
|
|
|
if (IsReadonly() || IsDisabled()) {
|
2018-05-11 10:06:07 +00:00
|
|
|
if (aCancel) {
|
|
|
|
*aCancel = true;
|
|
|
|
}
|
|
|
|
return NS_OK;
|
2016-04-23 09:30:17 +00:00
|
|
|
}
|
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-28 21:24:18 +00:00
|
|
|
|
1999-03-13 04:53:21 +00:00
|
|
|
// initialize out param
|
2018-05-11 10:06:07 +00:00
|
|
|
if (aCancel) {
|
|
|
|
*aCancel = false;
|
|
|
|
}
|
2015-05-28 15:58:42 +00:00
|
|
|
|
1999-03-13 04:53:21 +00:00
|
|
|
// check for the magic content node and delete it if it exists
|
2018-05-11 10:06:07 +00:00
|
|
|
if (!mBogusNode) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2019-05-07 22:27:29 +00:00
|
|
|
// A mutation event listener may recreate bogus node again during the
|
|
|
|
// call of DeleteNodeWithTransaction(). So, move it first.
|
|
|
|
nsCOMPtr<nsIContent> bogusNode(std::move(mBogusNode));
|
2018-05-11 10:06:07 +00:00
|
|
|
DebugOnly<nsresult> rv =
|
2019-05-07 22:27:29 +00:00
|
|
|
MOZ_KnownLive(TextEditorRef()).DeleteNodeWithTransaction(*bogusNode);
|
2018-05-11 10:06:07 +00:00
|
|
|
if (NS_WARN_IF(!CanHandleEditAction())) {
|
|
|
|
return NS_ERROR_EDITOR_DESTROYED;
|
1999-03-13 04:53:21 +00:00
|
|
|
}
|
2018-05-11 10:06:07 +00:00
|
|
|
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to remove the bogus node");
|
|
|
|
return NS_OK;
|
1999-03-13 04:53:21 +00:00
|
|
|
}
|
|
|
|
|
2018-11-03 11:20:06 +00:00
|
|
|
EditActionResult TextEditRules::WillInsertLineBreak(int32_t aMaxLength) {
|
2018-04-26 15:09:10 +00:00
|
|
|
MOZ_ASSERT(IsEditorDataAvailable());
|
2018-11-03 11:20:06 +00:00
|
|
|
MOZ_ASSERT(!IsSingleLineEditor());
|
|
|
|
|
|
|
|
CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY_OF_DISABLED
|
|
|
|
|
|
|
|
// 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(&inString.AsString(), &outString,
|
|
|
|
aMaxLength, &didTruncate);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return EditActionIgnored(rv);
|
|
|
|
}
|
|
|
|
if (didTruncate) {
|
|
|
|
return EditActionCanceled();
|
2016-10-24 02:27:45 +00:00
|
|
|
}
|
2018-11-02 08:58:29 +00:00
|
|
|
|
2018-11-03 11:20:06 +00:00
|
|
|
// if the selection isn't collapsed, delete it.
|
|
|
|
if (!SelectionRefPtr()->IsCollapsed()) {
|
2019-05-07 22:27:29 +00:00
|
|
|
rv = MOZ_KnownLive(TextEditorRef())
|
|
|
|
.DeleteSelectionAsSubAction(nsIEditor::eNone, nsIEditor::eStrip);
|
2018-11-03 11:20:06 +00:00
|
|
|
if (NS_WARN_IF(!CanHandleEditAction())) {
|
|
|
|
return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
|
2018-11-03 00:08:42 +00:00
|
|
|
}
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
2018-11-03 11:20:06 +00:00
|
|
|
return EditActionIgnored(rv);
|
2018-11-03 00:08:42 +00:00
|
|
|
}
|
2018-11-02 08:58:29 +00:00
|
|
|
}
|
2018-11-03 11:20:06 +00:00
|
|
|
|
|
|
|
rv = WillInsert();
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return EditActionIgnored(rv);
|
|
|
|
}
|
|
|
|
|
2018-11-03 11:21:15 +00:00
|
|
|
// get the (collapsed) selection location
|
|
|
|
nsRange* firstRange = SelectionRefPtr()->GetRangeAt(0);
|
|
|
|
if (NS_WARN_IF(!firstRange)) {
|
|
|
|
return EditActionIgnored(NS_ERROR_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
EditorRawDOMPoint pointToInsert(firstRange->StartRef());
|
|
|
|
if (NS_WARN_IF(!pointToInsert.IsSet())) {
|
|
|
|
return EditActionIgnored(NS_ERROR_FAILURE);
|
|
|
|
}
|
|
|
|
MOZ_ASSERT(pointToInsert.IsSetAndValid());
|
|
|
|
|
|
|
|
// Don't put text in places that can't have it.
|
|
|
|
if (!pointToInsert.IsInTextNode() &&
|
|
|
|
!TextEditorRef().CanContainTag(*pointToInsert.GetContainer(),
|
|
|
|
*nsGkAtoms::textTagName)) {
|
|
|
|
return EditActionIgnored(NS_ERROR_FAILURE);
|
|
|
|
}
|
|
|
|
|
2019-01-02 13:05:23 +00:00
|
|
|
RefPtr<Document> doc = TextEditorRef().GetDocument();
|
2018-11-03 11:21:15 +00:00
|
|
|
if (NS_WARN_IF(!doc)) {
|
|
|
|
return EditActionIgnored(NS_ERROR_NOT_INITIALIZED);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't change my selection in sub-transactions.
|
|
|
|
AutoTransactionsConserveSelection dontChangeMySelection(TextEditorRef());
|
|
|
|
|
|
|
|
// Insert a linefeed character.
|
|
|
|
EditorRawDOMPoint pointAfterInsertedLineBreak;
|
2019-03-30 11:55:29 +00:00
|
|
|
rv = MOZ_KnownLive(TextEditorRef())
|
|
|
|
.InsertTextWithTransaction(*doc, NS_LITERAL_STRING("\n"),
|
|
|
|
pointToInsert,
|
|
|
|
&pointAfterInsertedLineBreak);
|
2018-11-03 11:21:15 +00:00
|
|
|
if (NS_WARN_IF(!pointAfterInsertedLineBreak.IsSet())) {
|
|
|
|
return EditActionIgnored(NS_ERROR_FAILURE);
|
|
|
|
}
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return EditActionIgnored(rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
// set the selection to the correct location
|
|
|
|
MOZ_ASSERT(
|
|
|
|
!pointAfterInsertedLineBreak.GetChild(),
|
|
|
|
"After inserting text into a text node, pointAfterInsertedLineBreak."
|
|
|
|
"GetChild() should be nullptr");
|
|
|
|
rv = SelectionRefPtr()->Collapse(pointAfterInsertedLineBreak);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return EditActionIgnored(rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
// see if we're at the end of the editor range
|
|
|
|
EditorRawDOMPoint endPoint(EditorBase::GetEndPoint(*SelectionRefPtr()));
|
|
|
|
if (endPoint == pointAfterInsertedLineBreak) {
|
|
|
|
// SetInterlinePosition(true) means we want the caret to stick to the
|
|
|
|
// content on the "right". We want the caret to stick to whatever is
|
|
|
|
// past the break. This is because the break is on the same line we
|
|
|
|
// were on, but the next content will be on the following line.
|
|
|
|
SelectionRefPtr()->SetInterlinePosition(true, IgnoreErrors());
|
|
|
|
}
|
|
|
|
|
|
|
|
return EditActionHandled();
|
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-28 21:24:18 +00:00
|
|
|
}
|
|
|
|
|
2018-05-10 06:03:21 +00:00
|
|
|
nsresult TextEditRules::CollapseSelectionToTrailingBRIfNeeded() {
|
2018-04-26 15:09:10 +00:00
|
|
|
MOZ_ASSERT(IsEditorDataAvailable());
|
|
|
|
|
1999-12-07 08:30:19 +00: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
|
2015-05-28 15:58:42 +00:00
|
|
|
// (because there are a bunch more places you have to worry about it in html)
|
2010-04-12 02:35:18 +00:00
|
|
|
if (!IsPlaintextEditor()) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
1999-12-07 08:30:19 +00:00
|
|
|
|
2015-05-28 04:50:44 +00:00
|
|
|
// If there is no selection ranges, we should set to the end of the editor.
|
2016-07-09 02:34:41 +00:00
|
|
|
// This is usually performed in TextEditRules::Init(), however, if the
|
2015-05-28 04:50:44 +00:00
|
|
|
// editor is reframed, this may be called by AfterEdit().
|
2018-10-30 10:02:58 +00:00
|
|
|
if (!SelectionRefPtr()->RangeCount()) {
|
2018-10-30 10:01:38 +00:00
|
|
|
TextEditorRef().CollapseSelectionToEnd();
|
2018-05-11 09:40:47 +00:00
|
|
|
if (NS_WARN_IF(!CanHandleEditAction())) {
|
|
|
|
return NS_ERROR_EDITOR_DESTROYED;
|
2018-03-15 12:25:41 +00:00
|
|
|
}
|
2017-05-29 02:28:21 +00:00
|
|
|
}
|
2010-09-02 23:54:23 +00:00
|
|
|
|
2018-03-15 12:25:41 +00:00
|
|
|
// If we are at the end of the <textarea> element, we need to set the
|
|
|
|
// selection to stick to the moz-<br> at the end of the <textarea>.
|
2018-05-08 08:30:05 +00:00
|
|
|
EditorRawDOMPoint selectionStartPoint(
|
2018-10-30 10:02:58 +00:00
|
|
|
EditorBase::GetStartPoint(*SelectionRefPtr()));
|
2018-03-15 12:25:41 +00:00
|
|
|
if (NS_WARN_IF(!selectionStartPoint.IsSet())) {
|
|
|
|
return NS_ERROR_FAILURE;
|
2016-10-24 02:27:45 +00:00
|
|
|
}
|
2010-07-19 20:19:27 +00:00
|
|
|
|
2018-03-15 12:25:41 +00:00
|
|
|
// Nothing to do if we're not at the end of the text node.
|
|
|
|
if (!selectionStartPoint.IsInTextNode() ||
|
|
|
|
!selectionStartPoint.IsEndOfContainer()) {
|
2010-11-04 20:45:39 +00:00
|
|
|
return NS_OK;
|
2016-10-24 02:27:45 +00:00
|
|
|
}
|
2010-07-19 20:19:27 +00:00
|
|
|
|
2018-04-26 15:09:10 +00:00
|
|
|
Element* rootElement = TextEditorRef().GetRoot();
|
2018-03-15 12:25:41 +00:00
|
|
|
if (NS_WARN_IF(!rootElement)) {
|
2017-05-29 02:28:21 +00:00
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
}
|
2018-03-15 12:25:41 +00:00
|
|
|
nsINode* parentNode = selectionStartPoint.GetContainer()->GetParentNode();
|
|
|
|
if (parentNode != rootElement) {
|
2016-10-24 02:27:45 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
2003-07-18 14:12:51 +00:00
|
|
|
|
2018-03-15 12:25:41 +00:00
|
|
|
nsINode* nextNode = selectionStartPoint.GetContainer()->GetNextSibling();
|
|
|
|
if (!nextNode || !TextEditUtils::IsMozBR(nextNode)) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
EditorRawDOMPoint afterStartContainer(selectionStartPoint.GetContainer());
|
|
|
|
if (NS_WARN_IF(!afterStartContainer.AdvanceOffset())) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
ErrorResult error;
|
2018-10-30 10:02:58 +00:00
|
|
|
SelectionRefPtr()->Collapse(afterStartContainer, error);
|
2018-05-11 09:40:47 +00:00
|
|
|
if (NS_WARN_IF(!CanHandleEditAction())) {
|
|
|
|
error.SuppressException();
|
|
|
|
return NS_ERROR_EDITOR_DESTROYED;
|
|
|
|
}
|
2018-03-15 12:25:41 +00:00
|
|
|
if (NS_WARN_IF(error.Failed())) {
|
|
|
|
return error.StealNSResult();
|
1999-11-29 08:28:46 +00:00
|
|
|
}
|
2016-10-19 09:09:33 +00:00
|
|
|
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-28 21:24:18 +00:00
|
|
|
}
|
|
|
|
|
Bug 1463327 - part 1: Change scope of some methods of EditorBase which won't be called by non-helper classes of editing to protected r=m_kato
EditorBase (and other editor classes) have 2 type of public methods. One is
true-public methods. I.e., they should be able to be called by anybody.
E.g., command handlers, event listeners, or JS via nsIEditor interface.
The other is semi-public methods. They are not called by the above examples
but called by other classes which are helper classes to handle edit actions.
E.g., TextEditRules, HTMLEditRules, HTMLEditUtils, CSSEditUtils and Transaction
classes.
When we will implement InputEvent.inputType, we need to create new stack
class and create its instance at every true-public methods to manage current
inputType (like TextEditRules::AutoSafeEditorData). Therefore, it should not
happen that new code starts to call semi-public methods without the new
stack class instance.
For preventing this issue, we should make EditorBase have only the true-public
methods as public. The other public methods should be protected and their
users should be friend classes. Then, we can protect such method from external
classes.
Note that this patch just moves the methods without any changes in EditorBase.h
(except removes GetName() since there is no body of this method and removes
IterDirection since it's unused).
MozReview-Commit-ID: HBseKLL6pxx
--HG--
extra : rebase_source : 2251ff659d831d01a6778d38f4e2714fcf2d6ef4
2018-05-22 07:08:43 +00:00
|
|
|
already_AddRefed<nsINode>
|
|
|
|
TextEditRules::GetTextNodeAroundSelectionStartContainer() {
|
|
|
|
MOZ_ASSERT(IsEditorDataAvailable());
|
|
|
|
|
|
|
|
EditorRawDOMPoint selectionStartPoint(
|
2018-10-30 10:02:58 +00:00
|
|
|
EditorBase::GetStartPoint(*SelectionRefPtr()));
|
2018-03-15 12:25:41 +00:00
|
|
|
if (NS_WARN_IF(!selectionStartPoint.IsSet())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
if (selectionStartPoint.IsInTextNode()) {
|
|
|
|
nsCOMPtr<nsINode> node = selectionStartPoint.GetContainer();
|
|
|
|
return node.forget();
|
|
|
|
}
|
|
|
|
// This should be the root node, walk the tree looking for text nodes.
|
Bug 1463327 - part 1: Change scope of some methods of EditorBase which won't be called by non-helper classes of editing to protected r=m_kato
EditorBase (and other editor classes) have 2 type of public methods. One is
true-public methods. I.e., they should be able to be called by anybody.
E.g., command handlers, event listeners, or JS via nsIEditor interface.
The other is semi-public methods. They are not called by the above examples
but called by other classes which are helper classes to handle edit actions.
E.g., TextEditRules, HTMLEditRules, HTMLEditUtils, CSSEditUtils and Transaction
classes.
When we will implement InputEvent.inputType, we need to create new stack
class and create its instance at every true-public methods to manage current
inputType (like TextEditRules::AutoSafeEditorData). Therefore, it should not
happen that new code starts to call semi-public methods without the new
stack class instance.
For preventing this issue, we should make EditorBase have only the true-public
methods as public. The other public methods should be protected and their
users should be friend classes. Then, we can protect such method from external
classes.
Note that this patch just moves the methods without any changes in EditorBase.h
(except removes GetName() since there is no body of this method and removes
IterDirection since it's unused).
MozReview-Commit-ID: HBseKLL6pxx
--HG--
extra : rebase_source : 2251ff659d831d01a6778d38f4e2714fcf2d6ef4
2018-05-22 07:08:43 +00:00
|
|
|
// XXX NodeIterator sets mutation observer even for this temporary use.
|
|
|
|
// It's too expensive if this is called from a hot path.
|
2018-03-15 12:25:41 +00:00
|
|
|
nsCOMPtr<nsINode> node = selectionStartPoint.GetContainer();
|
|
|
|
RefPtr<NodeIterator> iter =
|
2018-06-25 21:20:54 +00:00
|
|
|
new NodeIterator(node, NodeFilter_Binding::SHOW_TEXT, nullptr);
|
2018-03-15 12:25:41 +00:00
|
|
|
while (!EditorBase::IsTextNode(node)) {
|
|
|
|
node = iter->NextNode(IgnoreErrors());
|
|
|
|
if (!node) {
|
|
|
|
return nullptr;
|
2009-11-25 17:50:35 +00:00
|
|
|
}
|
|
|
|
}
|
2018-03-15 12:25:41 +00:00
|
|
|
return node.forget();
|
2009-11-25 17:50:35 +00:00
|
|
|
}
|
2018-03-15 12:25:41 +00:00
|
|
|
|
2009-11-25 17:50:35 +00:00
|
|
|
#ifdef DEBUG
|
|
|
|
# define ASSERT_PASSWORD_LENGTHS_EQUAL() \
|
2016-07-09 02:54:50 +00:00
|
|
|
if (IsPasswordEditor() && mTextEditor->GetRoot()) { \
|
2012-08-22 15:56:38 +00:00
|
|
|
int32_t txtLen; \
|
2016-07-09 02:54:50 +00:00
|
|
|
mTextEditor->GetTextLength(&txtLen); \
|
2012-08-22 15:56:38 +00:00
|
|
|
NS_ASSERTION(mPasswordText.Length() == uint32_t(txtLen), \
|
2009-11-25 17:50:35 +00:00
|
|
|
"password length not equal to number of asterisks"); \
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
# define ASSERT_PASSWORD_LENGTHS_EQUAL()
|
|
|
|
#endif
|
2001-04-07 00:45:26 +00:00
|
|
|
|
2018-07-24 08:46:12 +00:00
|
|
|
void TextEditRules::HandleNewLines(nsString& aString) {
|
|
|
|
static const char16_t kLF = static_cast<char16_t>('\n');
|
|
|
|
MOZ_ASSERT(IsEditorDataAvailable());
|
|
|
|
MOZ_ASSERT(aString.FindChar(static_cast<uint16_t>('\r')) == kNotFound);
|
|
|
|
|
|
|
|
// First of all, check if aString contains '\n' since if the string
|
|
|
|
// does not include it, we don't need to do nothing here.
|
|
|
|
int32_t firstLF = aString.FindChar(kLF, 0);
|
|
|
|
if (firstLF == kNotFound) {
|
|
|
|
return;
|
2010-02-01 18:12:31 +00:00
|
|
|
}
|
|
|
|
|
2018-07-24 08:46:12 +00:00
|
|
|
switch (TextEditorRef().mNewlineHandling) {
|
2016-10-24 02:27:45 +00:00
|
|
|
case nsIPlaintextEditor::eNewlinesReplaceWithSpaces:
|
2018-07-24 08:46:12 +00:00
|
|
|
// Default of Firefox:
|
2016-10-24 02:27:45 +00:00
|
|
|
// Strip trailing newlines first so we don't wind up with trailing spaces
|
2018-07-24 08:46:12 +00:00
|
|
|
aString.Trim(LFSTR, false, true);
|
|
|
|
aString.ReplaceChar(kLF, ' ');
|
2016-10-24 02:27:45 +00:00
|
|
|
break;
|
|
|
|
case nsIPlaintextEditor::eNewlinesStrip:
|
2018-07-24 08:46:12 +00:00
|
|
|
aString.StripChar(kLF);
|
2016-10-24 02:27:45 +00:00
|
|
|
break;
|
|
|
|
case nsIPlaintextEditor::eNewlinesPasteToFirst:
|
|
|
|
default: {
|
2010-02-01 18:12:31 +00:00
|
|
|
// we get first *non-empty* line.
|
2012-08-22 15:56:38 +00:00
|
|
|
int32_t offset = 0;
|
2018-07-24 08:46:12 +00:00
|
|
|
while (firstLF == offset) {
|
2010-02-01 18:12:31 +00:00
|
|
|
offset++;
|
2018-07-24 08:46:12 +00:00
|
|
|
firstLF = aString.FindChar(kLF, offset);
|
2010-02-01 18:12:31 +00:00
|
|
|
}
|
2018-07-24 08:46:12 +00:00
|
|
|
if (firstLF > 0) {
|
|
|
|
aString.Truncate(firstLF);
|
2016-10-24 02:27:45 +00:00
|
|
|
}
|
|
|
|
if (offset > 0) {
|
2010-02-01 18:12:31 +00:00
|
|
|
aString.Cut(0, offset);
|
2016-10-24 02:27:45 +00:00
|
|
|
}
|
|
|
|
break;
|
2010-02-01 18:12:31 +00:00
|
|
|
}
|
2016-10-24 02:27:45 +00:00
|
|
|
case nsIPlaintextEditor::eNewlinesReplaceWithCommas:
|
2018-07-24 08:46:12 +00:00
|
|
|
// Default of Thunderbird:
|
|
|
|
aString.Trim(LFSTR, true, true);
|
|
|
|
aString.ReplaceChar(kLF, ',');
|
2016-10-24 02:27:45 +00:00
|
|
|
break;
|
|
|
|
case nsIPlaintextEditor::eNewlinesStripSurroundingWhitespace: {
|
|
|
|
nsAutoString result;
|
2013-01-15 23:44:16 +00:00
|
|
|
uint32_t offset = 0;
|
2016-10-24 02:27:45 +00:00
|
|
|
while (offset < aString.Length()) {
|
2018-07-24 08:46:12 +00:00
|
|
|
int32_t nextLF = !offset ? firstLF : aString.FindChar(kLF, offset);
|
|
|
|
if (nextLF < 0) {
|
2013-01-15 23:44:16 +00:00
|
|
|
result.Append(nsDependentSubstring(aString, offset));
|
|
|
|
break;
|
|
|
|
}
|
2018-07-24 08:46:12 +00:00
|
|
|
uint32_t wsBegin = nextLF;
|
2010-02-01 18:12:31 +00:00
|
|
|
// look backwards for the first non-whitespace char
|
2016-10-24 02:27:45 +00:00
|
|
|
while (wsBegin > offset && NS_IS_SPACE(aString[wsBegin - 1])) {
|
2010-02-01 18:12:31 +00:00
|
|
|
--wsBegin;
|
2016-10-24 02:27:45 +00:00
|
|
|
}
|
2013-01-15 23:44:16 +00:00
|
|
|
result.Append(nsDependentSubstring(aString, offset, wsBegin - offset));
|
2018-07-24 08:46:12 +00:00
|
|
|
offset = nextLF + 1;
|
2016-10-24 02:27:45 +00:00
|
|
|
while (offset < aString.Length() && NS_IS_SPACE(aString[offset])) {
|
2013-01-15 23:44:16 +00:00
|
|
|
++offset;
|
2016-10-24 02:27:45 +00:00
|
|
|
}
|
2010-02-01 18:12:31 +00:00
|
|
|
}
|
2013-01-15 23:44:16 +00:00
|
|
|
aString = result;
|
2016-10-24 02:27:45 +00:00
|
|
|
break;
|
2010-02-01 18:12:31 +00:00
|
|
|
}
|
2016-10-24 02:27:45 +00:00
|
|
|
case nsIPlaintextEditor::eNewlinesPasteIntact:
|
|
|
|
// even if we're pasting newlines, don't paste leading/trailing ones
|
2018-07-24 08:46:12 +00:00
|
|
|
aString.Trim(LFSTR, true, true);
|
2016-10-24 02:27:45 +00:00
|
|
|
break;
|
2010-02-01 18:12:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Bug 1463985 - part 1: Rename EditAction to EditSubAction and related stuff r=m_kato
When we implement InputEvent.inputType, we need to set a stack class to record
which edit action is currently handled. However, currently, we call smaller
jobs as edit action. For example, when user types a character at selecting
some characters, then, EditAction::deleteSelection is performed first, then,
EditAction::insertText is performed. However, for the InputEvent.inputType,
we need inserText information. So, for making new enum EditAction, we need
to rename current EditAction to EditSubAction.
And also this renames related stuff:
EditorBase::mIsInEditAction -> EditorBase::mIsInEditSubAction
EditorBase::IsInEditAction() -> EditorBase::IsInEditSubAction()
EditorBase::mAction -> EditorBase::mTopLevelEditSubAction
TextEditRules::mTheAction -> TextEditRules::mTopLevelEditSubAction
EditorBase::StartOperation() ->
EditorBase::OnStartToHandleTopLevelEditSubAction()
EditorBase::EndOperation() ->
EditorBase::OnEndHandlingTopLevelEditSubAction()
AutoRules -> AutoTopLevelEditSubActionNotifier
RulesInfo -> EditSubActionInfo
MozReview-Commit-ID: cvSkPUjFm1
--HG--
extra : rebase_source : baf527a3e353b7a8ebe9a46be2243b059c500234
2018-05-28 11:12:34 +00:00
|
|
|
nsresult TextEditRules::WillInsertText(EditSubAction aEditSubAction,
|
2016-07-09 02:34:41 +00:00
|
|
|
bool* aCancel, bool* aHandled,
|
|
|
|
const nsAString* inString,
|
|
|
|
nsAString* outString,
|
|
|
|
int32_t aMaxLength) {
|
2018-04-26 15:09:10 +00:00
|
|
|
MOZ_ASSERT(IsEditorDataAvailable());
|
|
|
|
|
2018-05-10 06:03:21 +00:00
|
|
|
if (NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
2016-10-24 02:27:45 +00:00
|
|
|
}
|
1999-06-08 06:04:51 +00:00
|
|
|
|
2018-05-28 12:44:39 +00:00
|
|
|
if (inString->IsEmpty() &&
|
|
|
|
aEditSubAction != EditSubAction::eInsertTextComingFromIME) {
|
2000-02-07 01:42:14 +00:00
|
|
|
// HACK: this is a fix for bug 19395
|
|
|
|
// I can't outlaw all empty insertions
|
|
|
|
// because IME transaction depend on them
|
2015-05-28 15:58:42 +00:00
|
|
|
// There is more work to do to make the
|
2000-02-07 01:42:14 +00:00
|
|
|
// world safe for IME.
|
2011-10-17 14:59:28 +00:00
|
|
|
*aCancel = true;
|
|
|
|
*aHandled = false;
|
2000-02-07 01:42:14 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
2015-05-28 15:58:42 +00:00
|
|
|
|
2000-03-29 12:53:23 +00:00
|
|
|
// initialize out param
|
2011-10-17 14:59:28 +00:00
|
|
|
*aCancel = false;
|
|
|
|
*aHandled = true;
|
2000-03-29 13:45:08 +00:00
|
|
|
|
|
|
|
// handle docs with a max length
|
2000-03-29 14:04:26 +00:00
|
|
|
// NOTE, this function copies inString into outString for us.
|
2011-09-29 06:19:26 +00:00
|
|
|
bool truncated = false;
|
2018-05-10 06:03:21 +00:00
|
|
|
nsresult rv =
|
|
|
|
TruncateInsertionIfNeeded(inString, outString, aMaxLength, &truncated);
|
2018-05-11 09:29:54 +00:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2011-05-11 04:52:45 +00:00
|
|
|
// If we're exceeding the maxlength when composing IME, we need to clean up
|
|
|
|
// the composing text, so we shouldn't return early.
|
2012-05-05 18:52:29 +00:00
|
|
|
if (truncated && outString->IsEmpty() &&
|
2018-05-28 12:44:39 +00:00
|
|
|
aEditSubAction != EditSubAction::eInsertTextComingFromIME) {
|
2011-10-17 14:59:28 +00:00
|
|
|
*aCancel = true;
|
2010-09-24 19:10:53 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
2015-05-28 15:58:42 +00:00
|
|
|
|
2017-03-09 19:44:45 +00:00
|
|
|
uint32_t start = 0;
|
|
|
|
uint32_t end = 0;
|
2003-07-18 14:12:51 +00:00
|
|
|
|
2000-03-29 13:45:08 +00:00
|
|
|
// handle password field docs
|
2012-06-10 23:44:50 +00:00
|
|
|
if (IsPasswordEditor()) {
|
2018-10-30 10:02:58 +00:00
|
|
|
nsContentUtils::GetSelectionInTextControl(
|
2018-04-26 15:09:10 +00:00
|
|
|
SelectionRefPtr(), TextEditorRef().GetRoot(), start, end);
|
2000-03-29 13:45:08 +00:00
|
|
|
}
|
2000-02-07 02:48:36 +00:00
|
|
|
|
1999-11-29 08:28:46 +00:00
|
|
|
// if the selection isn't collapsed, delete it.
|
2018-10-30 10:02:58 +00:00
|
|
|
if (!SelectionRefPtr()->IsCollapsed()) {
|
2019-05-07 22:27:29 +00:00
|
|
|
rv = MOZ_KnownLive(TextEditorRef())
|
|
|
|
.DeleteSelectionAsSubAction(nsIEditor::eNone, nsIEditor::eStrip);
|
2018-05-11 09:29:54 +00:00
|
|
|
if (NS_WARN_IF(!CanHandleEditAction())) {
|
|
|
|
return NS_ERROR_EDITOR_DESTROYED;
|
|
|
|
}
|
2018-04-26 15:09:10 +00:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
1999-11-29 08:28:46 +00:00
|
|
|
}
|
|
|
|
|
2018-05-11 10:06:07 +00:00
|
|
|
rv = WillInsert(aCancel);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2015-05-28 15:58:42 +00:00
|
|
|
|
2000-03-29 13:45:08 +00:00
|
|
|
// handle password field data
|
|
|
|
// this has the side effect of changing all the characters in aOutString
|
|
|
|
// to the replacement character
|
2016-10-24 02:27:45 +00:00
|
|
|
if (IsPasswordEditor() &&
|
2018-05-28 12:44:39 +00:00
|
|
|
aEditSubAction == EditSubAction::eInsertTextComingFromIME) {
|
2016-10-24 02:27:45 +00:00
|
|
|
RemoveIMETextFromPWBuf(start, outString);
|
2000-03-29 13:45:08 +00:00
|
|
|
}
|
|
|
|
|
2000-09-01 21:12:43 +00:00
|
|
|
// People have lots of different ideas about what text fields
|
|
|
|
// should do with multiline pastes. See bugs 21032, 23485, 23485, 50935.
|
2005-12-20 20:12:54 +00:00
|
|
|
// The six possible options are:
|
2000-09-01 21:12:43 +00:00
|
|
|
// 0. paste newlines intact
|
2005-12-20 20:12:54 +00:00
|
|
|
// 1. paste up to the first newline (default)
|
2000-09-01 21:12:43 +00:00
|
|
|
// 2. replace newlines with spaces
|
|
|
|
// 3. strip newlines
|
2004-01-29 16:55:01 +00:00
|
|
|
// 4. replace with commas
|
2005-12-20 20:12:54 +00:00
|
|
|
// 5. strip newlines and surrounding whitespace
|
2000-09-01 21:12:43 +00:00
|
|
|
// So find out what we're expected to do:
|
2016-10-24 02:27:45 +00:00
|
|
|
if (IsSingleLineEditor()) {
|
2001-12-11 07:21:10 +00:00
|
|
|
nsAutoString tString(*outString);
|
2018-07-24 08:46:12 +00:00
|
|
|
// XXX Some callers of TextEditor::InsertTextAsAction() already make the
|
|
|
|
// string use only \n as a linebreaker. However, they are not hot
|
|
|
|
// path and nsContentUtils::PlatformToDOMLineBreaks() does nothing
|
|
|
|
// if the string doesn't include \r. So, let's convert linebreakers
|
|
|
|
// here. Note that there are too many callers of
|
|
|
|
// TextEditor::InsertTextAsAction(). So, it's difficult to keep
|
|
|
|
// maintaining all of them won't reach here without \r nor \r\n.
|
|
|
|
nsContentUtils::PlatformToDOMLineBreaks(tString);
|
|
|
|
HandleNewLines(tString);
|
2001-12-11 07:21:10 +00:00
|
|
|
outString->Assign(tString);
|
|
|
|
}
|
|
|
|
|
2016-10-24 02:27:45 +00:00
|
|
|
if (IsPasswordEditor()) {
|
2009-11-02 15:37:25 +00:00
|
|
|
// manage the password buffer
|
|
|
|
mPasswordText.Insert(*outString, start);
|
|
|
|
|
2018-11-02 07:33:58 +00:00
|
|
|
if (!DontEchoPassword()) {
|
2018-10-25 03:35:26 +00:00
|
|
|
nsresult rv = HideLastPasswordInputInternal();
|
2009-11-02 15:37:25 +00:00
|
|
|
mLastStart = start;
|
|
|
|
mLastLength = outString->Length();
|
2016-10-24 02:27:45 +00:00
|
|
|
if (mTimer) {
|
2009-11-02 15:37:25 +00:00
|
|
|
mTimer->Cancel();
|
2018-05-11 07:08:47 +00:00
|
|
|
}
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
if (!mTimer) {
|
2017-09-25 02:57:48 +00:00
|
|
|
mTimer = NS_NewTimer();
|
2009-11-02 15:37:25 +00:00
|
|
|
}
|
2012-07-13 02:26:20 +00:00
|
|
|
mTimer->InitWithCallback(this, LookAndFeel::GetPasswordMaskDelay(),
|
|
|
|
nsITimer::TYPE_ONE_SHOT);
|
2016-10-24 02:27:45 +00:00
|
|
|
} else {
|
2012-06-01 11:03:17 +00:00
|
|
|
FillBufWithPWChars(outString, outString->Length());
|
2009-11-02 15:37:25 +00:00
|
|
|
}
|
2000-03-29 13:45:08 +00:00
|
|
|
}
|
|
|
|
|
2000-03-29 12:53:23 +00:00
|
|
|
// get the (collapsed) selection location
|
2018-10-30 10:02:58 +00:00
|
|
|
nsRange* firstRange = SelectionRefPtr()->GetRangeAt(0);
|
2018-05-08 08:30:05 +00:00
|
|
|
if (NS_WARN_IF(!firstRange)) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
EditorRawDOMPoint atStartOfSelection(firstRange->StartRef());
|
2017-11-08 12:55:10 +00:00
|
|
|
if (NS_WARN_IF(!atStartOfSelection.IsSetAndValid())) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2000-02-07 01:42:14 +00:00
|
|
|
|
2003-07-18 14:12:51 +00:00
|
|
|
// don't put text in places that can't have it
|
2017-12-07 09:45:52 +00:00
|
|
|
if (!atStartOfSelection.IsInTextNode() &&
|
2018-04-26 15:09:10 +00:00
|
|
|
!TextEditorRef().CanContainTag(*atStartOfSelection.GetContainer(),
|
|
|
|
*nsGkAtoms::textTagName)) {
|
2000-03-29 12:53:23 +00:00
|
|
|
return NS_ERROR_FAILURE;
|
2012-05-01 06:34:52 +00:00
|
|
|
}
|
2000-01-15 14:29:29 +00:00
|
|
|
|
2000-03-29 12:53:23 +00:00
|
|
|
// we need to get the doc
|
2019-01-02 13:05:23 +00:00
|
|
|
RefPtr<Document> doc = TextEditorRef().GetDocument();
|
2018-04-26 15:09:10 +00:00
|
|
|
if (NS_WARN_IF(!doc)) {
|
|
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
}
|
2015-05-28 15:58:42 +00:00
|
|
|
|
2018-05-28 12:44:39 +00:00
|
|
|
if (aEditSubAction == EditSubAction::eInsertTextComingFromIME) {
|
Bug 1530649 - Improve composition string handling which ends with whitespaces r=m_kato
If insertion string ends with ASCII whitespace and there is no following
content in the block, `HTMLEditRules::AdjustWhitespaces()` needs to insert
`<br>` element. It's called only by `HTMLEditRules::AfterEditInner()` and
that does only simple things with `WSRunObject`. Therefore, this moves the
code into `AfterEditInner()`.
For making it adjust the whitespaces, `HTMLEditRules::WillInsertText()` needs
to notify `AfterEditInner()` of dirty range with `mDocChangeRange`. Therefore,
this patch makes it set `mDocChangeRange` manually after inserting composition
string.
On the other hand, there is another bug. `WSRunObject` was designed to treat
only inserting text for `WSRunObject::InsertText()`. I.e., not designed to
treat replacing existing composition string with new string. Therefore,
`WSRunObject::InsertText()` adjusts whitespaces only around start of
composition string. Therefore, if composition string ends with an ASCII
whitespace, it's not replaced with NBSP and that causes:
- failing `WSRunObject::AdjustWhitespaces()` inserts `<br>` element at
`AfterEditInner()` of committing composition.
- then, next composition's first `WSRunObject::InsertText()` removes the
last whitespace due to not followed by `<br>` nor any other content.
Therefore, this patch makes `WSRunObject` takes 2 DOM points to be able to
treat replaced range.
In strictly speaking, the latter change require more changes and tests for
supporting replacement with any other methods. However, it's risky and out
of scope of this bug.
Differential Revision: https://phabricator.services.mozilla.com/D26423
--HG--
extra : moz-landing-system : lando
2019-04-09 05:28:38 +00:00
|
|
|
EditorRawDOMPoint compositionStartPoint =
|
|
|
|
TextEditorRef().GetCompositionStartPoint();
|
|
|
|
if (!compositionStartPoint.IsSet()) {
|
|
|
|
compositionStartPoint =
|
|
|
|
TextEditorRef().FindBetterInsertionPoint(atStartOfSelection);
|
2015-06-04 17:06:09 +00:00
|
|
|
}
|
Bug 1530649 - Improve composition string handling which ends with whitespaces r=m_kato
If insertion string ends with ASCII whitespace and there is no following
content in the block, `HTMLEditRules::AdjustWhitespaces()` needs to insert
`<br>` element. It's called only by `HTMLEditRules::AfterEditInner()` and
that does only simple things with `WSRunObject`. Therefore, this moves the
code into `AfterEditInner()`.
For making it adjust the whitespaces, `HTMLEditRules::WillInsertText()` needs
to notify `AfterEditInner()` of dirty range with `mDocChangeRange`. Therefore,
this patch makes it set `mDocChangeRange` manually after inserting composition
string.
On the other hand, there is another bug. `WSRunObject` was designed to treat
only inserting text for `WSRunObject::InsertText()`. I.e., not designed to
treat replacing existing composition string with new string. Therefore,
`WSRunObject::InsertText()` adjusts whitespaces only around start of
composition string. Therefore, if composition string ends with an ASCII
whitespace, it's not replaced with NBSP and that causes:
- failing `WSRunObject::AdjustWhitespaces()` inserts `<br>` element at
`AfterEditInner()` of committing composition.
- then, next composition's first `WSRunObject::InsertText()` removes the
last whitespace due to not followed by `<br>` nor any other content.
Therefore, this patch makes `WSRunObject` takes 2 DOM points to be able to
treat replaced range.
In strictly speaking, the latter change require more changes and tests for
supporting replacement with any other methods. However, it's risky and out
of scope of this bug.
Differential Revision: https://phabricator.services.mozilla.com/D26423
--HG--
extra : moz-landing-system : lando
2019-04-09 05:28:38 +00:00
|
|
|
rv =
|
|
|
|
MOZ_KnownLive(TextEditorRef())
|
|
|
|
.InsertTextWithTransaction(*doc, *outString, compositionStartPoint);
|
2018-05-11 09:29:54 +00:00
|
|
|
if (NS_WARN_IF(!CanHandleEditAction())) {
|
|
|
|
return NS_ERROR_EDITOR_DESTROYED;
|
|
|
|
}
|
2018-04-26 15:09:10 +00:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2012-05-05 18:52:29 +00:00
|
|
|
} else {
|
2018-05-28 12:36:47 +00:00
|
|
|
// aEditSubAction == EditSubAction::eInsertText
|
2003-07-18 14:12:51 +00:00
|
|
|
|
2017-07-31 15:20:08 +00:00
|
|
|
// don't change my selection in subtransactions
|
2018-08-01 12:30:14 +00:00
|
|
|
AutoTransactionsConserveSelection dontChangeMySelection(TextEditorRef());
|
2010-07-11 20:26:26 +00:00
|
|
|
|
2017-11-08 16:00:36 +00:00
|
|
|
EditorRawDOMPoint pointAfterStringInserted;
|
2019-03-30 11:55:29 +00:00
|
|
|
rv = MOZ_KnownLive(TextEditorRef())
|
|
|
|
.InsertTextWithTransaction(*doc, *outString, atStartOfSelection,
|
|
|
|
&pointAfterStringInserted);
|
2018-05-11 09:29:54 +00:00
|
|
|
if (NS_WARN_IF(!CanHandleEditAction())) {
|
|
|
|
return NS_ERROR_EDITOR_DESTROYED;
|
|
|
|
}
|
2018-04-12 08:58:14 +00:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2001-04-07 00:45:26 +00:00
|
|
|
|
2017-11-08 16:00:36 +00:00
|
|
|
if (pointAfterStringInserted.IsSet()) {
|
2015-05-28 15:58:42 +00:00
|
|
|
// Make the caret attach to the inserted text, unless this text ends with
|
2005-10-06 08:19:51 +00:00
|
|
|
// a LF, in which case make the caret attach to the next line.
|
2010-07-11 20:26:26 +00:00
|
|
|
bool endsWithLF = !outString->IsEmpty() && outString->Last() == nsCRT::LF;
|
2018-05-08 08:30:05 +00:00
|
|
|
IgnoredErrorResult error;
|
2018-10-30 10:02:58 +00:00
|
|
|
SelectionRefPtr()->SetInterlinePosition(endsWithLF, error);
|
2018-05-08 08:30:05 +00:00
|
|
|
NS_WARNING_ASSERTION(!error.Failed(),
|
|
|
|
"Failed to set or unset interline position");
|
2010-07-11 20:26:26 +00:00
|
|
|
|
2017-12-07 10:08:56 +00:00
|
|
|
MOZ_ASSERT(
|
|
|
|
!pointAfterStringInserted.GetChild(),
|
2017-11-08 16:00:36 +00:00
|
|
|
"After inserting text into a text node, pointAfterStringInserted."
|
2017-12-07 10:08:56 +00:00
|
|
|
"GetChild() should be nullptr");
|
2018-05-08 08:30:05 +00:00
|
|
|
error = IgnoredErrorResult();
|
2018-10-30 10:02:58 +00:00
|
|
|
SelectionRefPtr()->Collapse(pointAfterStringInserted, error);
|
2018-05-11 09:29:54 +00:00
|
|
|
if (NS_WARN_IF(!CanHandleEditAction())) {
|
|
|
|
return NS_ERROR_EDITOR_DESTROYED;
|
|
|
|
}
|
2018-05-08 08:30:05 +00:00
|
|
|
NS_WARNING_ASSERTION(
|
|
|
|
!error.Failed(),
|
|
|
|
"Failed to collapse selection after inserting string");
|
2005-10-06 08:19:51 +00:00
|
|
|
}
|
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 17:58:07 +00:00
|
|
|
}
|
2009-11-25 17:50:35 +00:00
|
|
|
ASSERT_PASSWORD_LENGTHS_EQUAL()
|
2016-10-19 09:09:33 +00:00
|
|
|
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 17:58:07 +00:00
|
|
|
}
|
|
|
|
|
2018-05-10 06:03:21 +00:00
|
|
|
nsresult TextEditRules::WillSetText(bool* aCancel, bool* aHandled,
|
2017-05-25 05:30:50 +00:00
|
|
|
const nsAString* aString,
|
|
|
|
int32_t aMaxLength) {
|
2018-04-26 15:09:10 +00:00
|
|
|
MOZ_ASSERT(IsEditorDataAvailable());
|
2017-05-25 05:30:50 +00:00
|
|
|
MOZ_ASSERT(aCancel);
|
|
|
|
MOZ_ASSERT(aHandled);
|
|
|
|
MOZ_ASSERT(aString);
|
2018-07-24 08:46:12 +00:00
|
|
|
MOZ_ASSERT(aString->FindChar(static_cast<char16_t>('\r')) == kNotFound);
|
2017-05-25 05:30:50 +00:00
|
|
|
|
|
|
|
CANCEL_OPERATION_IF_READONLY_OR_DISABLED
|
|
|
|
|
|
|
|
*aHandled = false;
|
|
|
|
*aCancel = false;
|
|
|
|
|
2018-07-03 13:25:52 +00:00
|
|
|
if (!IsPlaintextEditor() || TextEditorRef().IsIMEComposing() ||
|
|
|
|
TextEditorRef().IsUndoRedoEnabled() ||
|
2018-11-20 14:34:32 +00:00
|
|
|
TextEditorRef().GetEditAction() == EditAction::eReplaceText ||
|
2017-05-25 05:30:50 +00:00
|
|
|
aMaxLength != -1) {
|
2018-07-03 13:25:52 +00:00
|
|
|
// SetTextImpl only supports plain text editor without IME and
|
|
|
|
// when we don't need to make it undoable.
|
2017-05-25 05:30:50 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2018-11-02 07:33:58 +00:00
|
|
|
if (IsPasswordEditor() && !DontEchoPassword()) {
|
2017-05-25 05:30:50 +00:00
|
|
|
// Echo password timer will implement on InsertText.
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2018-05-11 10:06:07 +00:00
|
|
|
nsresult rv = WillInsert(aCancel);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2017-05-25 05:30:50 +00:00
|
|
|
|
2018-04-26 15:09:10 +00:00
|
|
|
RefPtr<Element> rootElement = TextEditorRef().GetRoot();
|
Bug 1548751 - Make TextEditRules::WillSetText() use fast path even if it's for <textarea> element r=m_kato
As far as I've tested, `TextEditor` has the following structure patterns:
1. If it's for a non-empty `<input>` element, it has only one text node.
2. If it's for an empty `<input>` element, it has only bogus node.
3. If it's for a non-empty `<textarea>` element, it has a text node and
`moz-<br>` element. Additionally they are followed by `<scrollbar>` and
`<resizer>` elements.
4. If it's for an empty `<textarea>` element, it has a `moz-<br>` element
followed by `<scrollbar>` and `<resizer>` elements.
Additionally, `TextEditRules::WillInsert()` always removes bogus node if
there is. So, in the case #2, there is no children.
Fortunately, we don't support XUL addons anymore on Firefox. However, in
other products like Thunderbird, the tree may be changed as unexpected.
Therefore, we still need to keep checking the tree, but we can use the fast
path for `<textarea>` element too.
Differential Revision: https://phabricator.services.mozilla.com/D30012
--HG--
extra : moz-landing-system : lando
2019-05-07 05:07:14 +00:00
|
|
|
nsIContent* firstChild = rootElement->GetFirstChild();
|
2017-05-25 05:30:50 +00:00
|
|
|
|
Bug 1548751 - Make TextEditRules::WillSetText() use fast path even if it's for <textarea> element r=m_kato
As far as I've tested, `TextEditor` has the following structure patterns:
1. If it's for a non-empty `<input>` element, it has only one text node.
2. If it's for an empty `<input>` element, it has only bogus node.
3. If it's for a non-empty `<textarea>` element, it has a text node and
`moz-<br>` element. Additionally they are followed by `<scrollbar>` and
`<resizer>` elements.
4. If it's for an empty `<textarea>` element, it has a `moz-<br>` element
followed by `<scrollbar>` and `<resizer>` elements.
Additionally, `TextEditRules::WillInsert()` always removes bogus node if
there is. So, in the case #2, there is no children.
Fortunately, we don't support XUL addons anymore on Firefox. However, in
other products like Thunderbird, the tree may be changed as unexpected.
Therefore, we still need to keep checking the tree, but we can use the fast
path for `<textarea>` element too.
Differential Revision: https://phabricator.services.mozilla.com/D30012
--HG--
extra : moz-landing-system : lando
2019-05-07 05:07:14 +00:00
|
|
|
// We can use this fast path only when:
|
|
|
|
// - we need to insert a text node.
|
|
|
|
// - we need to replace content of existing text node.
|
|
|
|
// Additionally, for avoiding odd result, we should check whether we're in
|
|
|
|
// usual condition.
|
|
|
|
if (IsSingleLineEditor()) {
|
|
|
|
// If we're a single line text editor, i.e., <input>, there is only bogus-
|
|
|
|
// <br> element. Otherwise, there should be only one text node. But note
|
|
|
|
// that even if there is a bogus node, it's already been removed by
|
|
|
|
// WillInsert(). So, at here, there should be only one text node or no
|
|
|
|
// children.
|
|
|
|
if (firstChild &&
|
|
|
|
(!EditorBase::IsTextNode(firstChild) || firstChild->GetNextSibling())) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// If we're a multiline text editor, i.e., <textarea>, there is a moz-<br>
|
|
|
|
// element followed by scrollbar/resizer elements. Otherwise, a text node
|
|
|
|
// is followed by them.
|
|
|
|
if (!firstChild) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
if (EditorBase::IsTextNode(firstChild)) {
|
|
|
|
if (!firstChild->GetNextSibling() ||
|
|
|
|
!TextEditUtils::IsMozBR(firstChild->GetNextSibling())) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
} else if (!TextEditUtils::IsMozBR(firstChild)) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2017-05-25 05:30:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nsAutoString tString(*aString);
|
|
|
|
|
|
|
|
if (IsPasswordEditor()) {
|
|
|
|
mPasswordText.Assign(tString);
|
|
|
|
FillBufWithPWChars(&tString, tString.Length());
|
|
|
|
} else if (IsSingleLineEditor()) {
|
2018-07-24 08:46:12 +00:00
|
|
|
HandleNewLines(tString);
|
2017-05-25 05:30:50 +00:00
|
|
|
}
|
|
|
|
|
Bug 1548751 - Make TextEditRules::WillSetText() use fast path even if it's for <textarea> element r=m_kato
As far as I've tested, `TextEditor` has the following structure patterns:
1. If it's for a non-empty `<input>` element, it has only one text node.
2. If it's for an empty `<input>` element, it has only bogus node.
3. If it's for a non-empty `<textarea>` element, it has a text node and
`moz-<br>` element. Additionally they are followed by `<scrollbar>` and
`<resizer>` elements.
4. If it's for an empty `<textarea>` element, it has a `moz-<br>` element
followed by `<scrollbar>` and `<resizer>` elements.
Additionally, `TextEditRules::WillInsert()` always removes bogus node if
there is. So, in the case #2, there is no children.
Fortunately, we don't support XUL addons anymore on Firefox. However, in
other products like Thunderbird, the tree may be changed as unexpected.
Therefore, we still need to keep checking the tree, but we can use the fast
path for `<textarea>` element too.
Differential Revision: https://phabricator.services.mozilla.com/D30012
--HG--
extra : moz-landing-system : lando
2019-05-07 05:07:14 +00:00
|
|
|
if (!firstChild || !EditorBase::IsTextNode(firstChild)) {
|
2017-05-25 05:30:50 +00:00
|
|
|
if (tString.IsEmpty()) {
|
|
|
|
*aHandled = true;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2019-01-02 13:05:23 +00:00
|
|
|
RefPtr<Document> doc = TextEditorRef().GetDocument();
|
2017-05-25 05:30:50 +00:00
|
|
|
if (NS_WARN_IF(!doc)) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2017-08-18 07:05:16 +00:00
|
|
|
RefPtr<nsTextNode> newNode = EditorBase::CreateTextNode(*doc, tString);
|
2017-05-25 05:30:50 +00:00
|
|
|
if (NS_WARN_IF(!newNode)) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2019-03-30 11:55:29 +00:00
|
|
|
nsresult rv = MOZ_KnownLive(TextEditorRef())
|
|
|
|
.InsertNodeWithTransaction(
|
2019-05-08 09:40:17 +00:00
|
|
|
*newNode, EditorDOMPoint(rootElement, 0));
|
2018-05-11 08:33:55 +00:00
|
|
|
if (NS_WARN_IF(!CanHandleEditAction())) {
|
|
|
|
return NS_ERROR_EDITOR_DESTROYED;
|
|
|
|
}
|
2017-05-25 05:30:50 +00:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
*aHandled = true;
|
|
|
|
|
|
|
|
ASSERT_PASSWORD_LENGTHS_EQUAL();
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2017-06-27 22:42:09 +00:00
|
|
|
// Even if empty text, we don't remove text node and set empty text
|
|
|
|
// for performance
|
Bug 1548751 - Make TextEditRules::WillSetText() use fast path even if it's for <textarea> element r=m_kato
As far as I've tested, `TextEditor` has the following structure patterns:
1. If it's for a non-empty `<input>` element, it has only one text node.
2. If it's for an empty `<input>` element, it has only bogus node.
3. If it's for a non-empty `<textarea>` element, it has a text node and
`moz-<br>` element. Additionally they are followed by `<scrollbar>` and
`<resizer>` elements.
4. If it's for an empty `<textarea>` element, it has a `moz-<br>` element
followed by `<scrollbar>` and `<resizer>` elements.
Additionally, `TextEditRules::WillInsert()` always removes bogus node if
there is. So, in the case #2, there is no children.
Fortunately, we don't support XUL addons anymore on Firefox. However, in
other products like Thunderbird, the tree may be changed as unexpected.
Therefore, we still need to keep checking the tree, but we can use the fast
path for `<textarea>` element too.
Differential Revision: https://phabricator.services.mozilla.com/D30012
--HG--
extra : moz-landing-system : lando
2019-05-07 05:07:14 +00:00
|
|
|
RefPtr<Text> textNode = firstChild->GetAsText();
|
|
|
|
if (MOZ_UNLIKELY(NS_WARN_IF(!textNode))) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
rv = TextEditorRef().SetTextImpl(tString, *textNode);
|
2018-05-11 08:33:55 +00:00
|
|
|
if (NS_WARN_IF(!CanHandleEditAction())) {
|
|
|
|
return NS_ERROR_EDITOR_DESTROYED;
|
|
|
|
}
|
2017-05-25 05:30:50 +00:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
Bug 1548751 - Make TextEditRules::WillSetText() use fast path even if it's for <textarea> element r=m_kato
As far as I've tested, `TextEditor` has the following structure patterns:
1. If it's for a non-empty `<input>` element, it has only one text node.
2. If it's for an empty `<input>` element, it has only bogus node.
3. If it's for a non-empty `<textarea>` element, it has a text node and
`moz-<br>` element. Additionally they are followed by `<scrollbar>` and
`<resizer>` elements.
4. If it's for an empty `<textarea>` element, it has a `moz-<br>` element
followed by `<scrollbar>` and `<resizer>` elements.
Additionally, `TextEditRules::WillInsert()` always removes bogus node if
there is. So, in the case #2, there is no children.
Fortunately, we don't support XUL addons anymore on Firefox. However, in
other products like Thunderbird, the tree may be changed as unexpected.
Therefore, we still need to keep checking the tree, but we can use the fast
path for `<textarea>` element too.
Differential Revision: https://phabricator.services.mozilla.com/D30012
--HG--
extra : moz-landing-system : lando
2019-05-07 05:07:14 +00:00
|
|
|
// If we replaced non-empty value with empty string, we need to delete the
|
|
|
|
// text node.
|
|
|
|
if (tString.IsEmpty()) {
|
|
|
|
DebugOnly<nsresult> rvIgnored = DidDeleteSelection();
|
|
|
|
MOZ_ASSERT(rvIgnored != NS_ERROR_EDITOR_DESTROYED);
|
|
|
|
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
|
|
|
|
"DidDeleteSelection() failed");
|
|
|
|
}
|
|
|
|
|
2017-05-25 05:30:50 +00:00
|
|
|
*aHandled = true;
|
|
|
|
|
|
|
|
ASSERT_PASSWORD_LENGTHS_EQUAL();
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2018-05-10 06:03:21 +00:00
|
|
|
nsresult TextEditRules::WillSetTextProperty(bool* aCancel, bool* aHandled) {
|
|
|
|
if (NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
2016-10-24 02:27:45 +00:00
|
|
|
}
|
1999-06-08 06:04:51 +00:00
|
|
|
|
|
|
|
// XXX: should probably return a success value other than NS_OK that means
|
|
|
|
// "not allowed"
|
2010-04-12 02:35:18 +00:00
|
|
|
if (IsPlaintextEditor()) {
|
2011-10-17 14:59:28 +00:00
|
|
|
*aCancel = true;
|
1999-06-08 06:04:51 +00:00
|
|
|
}
|
2003-07-18 14:12:51 +00:00
|
|
|
return NS_OK;
|
1999-06-08 06:04:51 +00:00
|
|
|
}
|
|
|
|
|
2018-05-10 06:03:21 +00:00
|
|
|
nsresult TextEditRules::WillRemoveTextProperty(bool* aCancel, bool* aHandled) {
|
|
|
|
if (NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
2016-10-24 02:27:45 +00:00
|
|
|
}
|
1999-06-08 06:04:51 +00:00
|
|
|
|
|
|
|
// XXX: should probably return a success value other than NS_OK that means
|
|
|
|
// "not allowed"
|
2010-04-12 02:35:18 +00:00
|
|
|
if (IsPlaintextEditor()) {
|
2011-10-17 14:59:28 +00:00
|
|
|
*aCancel = true;
|
1999-06-08 06:04:51 +00:00
|
|
|
}
|
2003-07-18 14:12:51 +00:00
|
|
|
return NS_OK;
|
1999-06-08 06:04:51 +00:00
|
|
|
}
|
|
|
|
|
2018-05-10 06:03:21 +00:00
|
|
|
nsresult TextEditRules::WillDeleteSelection(
|
|
|
|
nsIEditor::EDirection aCollapsedAction, bool* aCancel, bool* aHandled) {
|
2018-04-26 15:09:10 +00:00
|
|
|
MOZ_ASSERT(IsEditorDataAvailable());
|
|
|
|
|
2018-05-10 06:03:21 +00:00
|
|
|
if (NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
2016-10-24 02:27:45 +00:00
|
|
|
}
|
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-28 21:24:18 +00:00
|
|
|
CANCEL_OPERATION_IF_READONLY_OR_DISABLED
|
|
|
|
|
1999-03-12 02:28:24 +00:00
|
|
|
// initialize out param
|
2011-10-17 14:59:28 +00:00
|
|
|
*aCancel = false;
|
|
|
|
*aHandled = false;
|
2015-05-28 15:58:42 +00:00
|
|
|
|
1999-03-13 04:53:21 +00:00
|
|
|
// if there is only bogus content, cancel the operation
|
1999-03-15 00:57:32 +00:00
|
|
|
if (mBogusNode) {
|
2011-10-17 14:59:28 +00:00
|
|
|
*aCancel = true;
|
1999-03-15 00:57:32 +00:00
|
|
|
return NS_OK;
|
1999-03-13 04:53:21 +00:00
|
|
|
}
|
2018-05-11 08:15:53 +00:00
|
|
|
nsresult rv =
|
|
|
|
DeleteSelectionWithTransaction(aCollapsedAction, aCancel, aHandled);
|
|
|
|
// DeleteSelectionWithTransaction() creates SelectionBatcher. Therefore,
|
|
|
|
// quitting from it might cause having destroyed the editor.
|
|
|
|
if (NS_WARN_IF(!CanHandleEditAction())) {
|
|
|
|
return NS_ERROR_EDITOR_DESTROYED;
|
|
|
|
}
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult TextEditRules::DeleteSelectionWithTransaction(
|
|
|
|
nsIEditor::EDirection aCollapsedAction, bool* aCancel, bool* aHandled) {
|
|
|
|
MOZ_ASSERT(IsEditorDataAvailable());
|
|
|
|
MOZ_ASSERT(aCancel);
|
|
|
|
MOZ_ASSERT(aHandled);
|
2003-07-18 14:12:51 +00:00
|
|
|
|
2015-08-12 17:26:01 +00:00
|
|
|
// 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.
|
2018-10-30 10:02:58 +00:00
|
|
|
SelectionBatcher selectionBatcher(SelectionRefPtr());
|
|
|
|
AutoHideSelectionChanges hideSelection(SelectionRefPtr());
|
2013-02-22 23:40:09 +00:00
|
|
|
nsAutoScriptBlocker scriptBlocker;
|
2003-07-18 14:12:51 +00:00
|
|
|
|
2016-10-24 02:27:45 +00:00
|
|
|
if (IsPasswordEditor()) {
|
2018-10-30 10:01:38 +00:00
|
|
|
nsresult rv = TextEditorRef().ExtendSelectionForDelete(&aCollapsedAction);
|
2018-05-08 08:30:05 +00:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2008-12-04 13:17:48 +00:00
|
|
|
|
2001-04-26 20:14:26 +00:00
|
|
|
// manage the password buffer
|
2017-03-09 19:44:45 +00:00
|
|
|
uint32_t start, end;
|
2018-10-30 10:02:58 +00:00
|
|
|
nsContentUtils::GetSelectionInTextControl(
|
2018-04-26 15:09:10 +00:00
|
|
|
SelectionRefPtr(), TextEditorRef().GetRoot(), start, end);
|
2009-11-25 17:50:35 +00:00
|
|
|
|
2011-09-09 02:27:12 +00:00
|
|
|
if (LookAndFeel::GetEchoPassword()) {
|
2018-10-25 03:35:26 +00:00
|
|
|
rv = HideLastPasswordInputInternal();
|
2009-11-25 17:50:35 +00:00
|
|
|
mLastStart = start;
|
|
|
|
mLastLength = 0;
|
2016-10-24 02:27:45 +00:00
|
|
|
if (mTimer) {
|
2009-11-25 17:50:35 +00:00
|
|
|
mTimer->Cancel();
|
|
|
|
}
|
2018-05-11 07:08:47 +00:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2009-11-25 17:50:35 +00:00
|
|
|
}
|
|
|
|
|
2016-10-24 02:27:45 +00:00
|
|
|
// Collapsed selection.
|
|
|
|
if (end == start) {
|
|
|
|
// Deleting back.
|
2017-03-09 19:44:45 +00:00
|
|
|
if (nsIEditor::ePrevious == aCollapsedAction && start > 0) {
|
2001-04-26 20:14:26 +00:00
|
|
|
mPasswordText.Cut(start - 1, 1);
|
|
|
|
}
|
2016-10-24 02:27:45 +00:00
|
|
|
// Deleting forward.
|
|
|
|
else if (nsIEditor::eNext == aCollapsedAction) {
|
2001-04-26 20:14:26 +00:00
|
|
|
mPasswordText.Cut(start, 1);
|
|
|
|
}
|
2016-10-24 02:27:45 +00:00
|
|
|
// Otherwise nothing to do for this collapsed selection.
|
2001-04-26 20:14:26 +00:00
|
|
|
}
|
2016-10-24 02:27:45 +00:00
|
|
|
// Extended selection.
|
|
|
|
else {
|
2001-04-26 20:14:26 +00:00
|
|
|
mPasswordText.Cut(start, end - start);
|
|
|
|
}
|
2016-10-24 02:27:45 +00:00
|
|
|
} else {
|
2018-03-15 12:25:41 +00:00
|
|
|
EditorRawDOMPoint selectionStartPoint(
|
2018-10-30 10:02:58 +00:00
|
|
|
EditorBase::GetStartPoint(*SelectionRefPtr()));
|
2018-03-15 12:25:41 +00:00
|
|
|
if (NS_WARN_IF(!selectionStartPoint.IsSet())) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2015-05-28 15:58:42 +00:00
|
|
|
|
2018-10-30 10:02:58 +00:00
|
|
|
if (!SelectionRefPtr()->IsCollapsed()) {
|
2010-07-26 19:11:08 +00:00
|
|
|
return NS_OK;
|
2016-10-24 02:27:45 +00:00
|
|
|
}
|
2003-07-18 14:12:51 +00:00
|
|
|
|
2008-12-04 13:17:48 +00:00
|
|
|
// Test for distance between caret and text that will be deleted
|
2018-05-10 06:03:21 +00:00
|
|
|
nsresult rv = CheckBidiLevelForDeletion(selectionStartPoint,
|
|
|
|
aCollapsedAction, aCancel);
|
2018-03-15 09:38:46 +00:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2016-10-24 02:27:45 +00:00
|
|
|
if (*aCancel) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2003-09-27 04:18:26 +00:00
|
|
|
|
2018-10-30 10:01:38 +00:00
|
|
|
rv = TextEditorRef().ExtendSelectionForDelete(&aCollapsedAction);
|
2018-04-26 15:09:10 +00:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2001-04-26 20:14:26 +00:00
|
|
|
}
|
|
|
|
|
2019-05-07 22:27:29 +00:00
|
|
|
nsresult rv =
|
|
|
|
MOZ_KnownLive(TextEditorRef())
|
|
|
|
.DeleteSelectionWithTransaction(aCollapsedAction, nsIEditor::eStrip);
|
2018-05-11 08:15:53 +00:00
|
|
|
if (NS_WARN_IF(!CanHandleEditAction())) {
|
|
|
|
return NS_ERROR_EDITOR_DESTROYED;
|
|
|
|
}
|
2018-04-26 15:09:10 +00:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2008-12-04 13:17:48 +00:00
|
|
|
|
2011-10-17 14:59:28 +00:00
|
|
|
*aHandled = true;
|
2009-11-25 17:50:35 +00:00
|
|
|
ASSERT_PASSWORD_LENGTHS_EQUAL()
|
2008-12-04 13:17:48 +00:00
|
|
|
return NS_OK;
|
1999-03-12 02:28:24 +00:00
|
|
|
}
|
|
|
|
|
2018-05-11 07:48:29 +00:00
|
|
|
nsresult TextEditRules::DidDeleteSelection() {
|
2018-04-26 15:09:10 +00:00
|
|
|
MOZ_ASSERT(IsEditorDataAvailable());
|
|
|
|
|
2019-05-07 22:27:29 +00:00
|
|
|
EditorDOMPoint selectionStartPoint(
|
2018-10-30 10:02:58 +00:00
|
|
|
EditorBase::GetStartPoint(*SelectionRefPtr()));
|
2018-03-15 12:25:41 +00:00
|
|
|
if (NS_WARN_IF(!selectionStartPoint.IsSet())) {
|
2017-05-29 02:28:21 +00:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2015-05-28 15:58:42 +00:00
|
|
|
|
2018-03-15 12:25:41 +00:00
|
|
|
// Delete empty text nodes at selection.
|
|
|
|
if (selectionStartPoint.IsInTextNode() &&
|
|
|
|
!selectionStartPoint.GetContainer()->Length()) {
|
2019-05-07 22:27:29 +00:00
|
|
|
nsresult rv = MOZ_KnownLive(TextEditorRef())
|
|
|
|
.DeleteNodeWithTransaction(
|
|
|
|
MOZ_KnownLive(*selectionStartPoint.GetContainer()));
|
2018-05-11 07:48:29 +00:00
|
|
|
if (NS_WARN_IF(!CanHandleEditAction())) {
|
|
|
|
return NS_ERROR_EDITOR_DESTROYED;
|
|
|
|
}
|
2018-03-15 12:25:41 +00:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
1999-03-10 19:53:26 +00:00
|
|
|
}
|
2018-03-15 12:25:41 +00:00
|
|
|
|
2016-10-19 09:09:33 +00:00
|
|
|
if (mDidExplicitlySetInterline) {
|
|
|
|
return NS_OK;
|
2003-05-14 13:20:09 +00:00
|
|
|
}
|
2016-10-19 09:09:33 +00:00
|
|
|
// We prevent the caret from sticking on the left of prior BR
|
|
|
|
// (i.e. the end of previous line) after this deletion. Bug 92124
|
2018-05-08 17:52:37 +00:00
|
|
|
ErrorResult err;
|
2018-10-30 10:02:58 +00:00
|
|
|
SelectionRefPtr()->SetInterlinePosition(true, err);
|
2018-05-08 08:30:05 +00:00
|
|
|
NS_WARNING_ASSERTION(!err.Failed(), "Failed to set interline position");
|
2018-05-08 17:52:37 +00:00
|
|
|
return err.StealNSResult();
|
1999-03-10 19:53:26 +00:00
|
|
|
}
|
|
|
|
|
2018-05-10 06:03:21 +00:00
|
|
|
nsresult TextEditRules::WillUndo(bool* aCancel, bool* aHandled) {
|
|
|
|
if (NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
2016-10-24 02:27:45 +00:00
|
|
|
}
|
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-28 21:24:18 +00:00
|
|
|
CANCEL_OPERATION_IF_READONLY_OR_DISABLED
|
1999-03-15 05:08:30 +00:00
|
|
|
// initialize out param
|
2011-10-17 14:59:28 +00:00
|
|
|
*aCancel = false;
|
|
|
|
*aHandled = false;
|
1999-03-15 05:08:30 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2018-05-10 06:03:21 +00:00
|
|
|
nsresult TextEditRules::DidUndo(nsresult aResult) {
|
2018-04-26 15:09:10 +00:00
|
|
|
MOZ_ASSERT(IsEditorDataAvailable());
|
|
|
|
|
2012-01-11 08:23:07 +00:00
|
|
|
// If aResult is an error, we return it.
|
2018-05-10 06:03:21 +00:00
|
|
|
if (NS_WARN_IF(NS_FAILED(aResult))) {
|
|
|
|
return aResult;
|
|
|
|
}
|
2012-01-11 08:23:07 +00:00
|
|
|
|
2018-04-26 15:09:10 +00:00
|
|
|
Element* rootElement = TextEditorRef().GetRoot();
|
|
|
|
if (NS_WARN_IF(!rootElement)) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2018-05-18 03:38:32 +00:00
|
|
|
|
|
|
|
// 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.
|
2018-04-26 15:09:10 +00:00
|
|
|
nsIContent* node = TextEditorRef().GetLeftmostChild(rootElement);
|
|
|
|
if (node && TextEditorRef().IsMozEditorBogusNode(node)) {
|
2017-04-24 10:40:12 +00:00
|
|
|
mBogusNode = node;
|
2012-01-11 08:23:07 +00:00
|
|
|
} else {
|
2012-07-30 14:20:58 +00:00
|
|
|
mBogusNode = nullptr;
|
1999-03-15 05:08:30 +00:00
|
|
|
}
|
2012-01-11 08:23:07 +00:00
|
|
|
return aResult;
|
1999-03-15 05:08:30 +00:00
|
|
|
}
|
|
|
|
|
2018-05-10 06:03:21 +00:00
|
|
|
nsresult TextEditRules::WillRedo(bool* aCancel, bool* aHandled) {
|
|
|
|
if (NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
2016-10-24 02:27:45 +00:00
|
|
|
}
|
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-28 21:24:18 +00:00
|
|
|
CANCEL_OPERATION_IF_READONLY_OR_DISABLED
|
1999-03-15 05:08:30 +00:00
|
|
|
// initialize out param
|
2011-10-17 14:59:28 +00:00
|
|
|
*aCancel = false;
|
|
|
|
*aHandled = false;
|
1999-03-15 05:08:30 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2018-05-10 06:03:21 +00:00
|
|
|
nsresult TextEditRules::DidRedo(nsresult aResult) {
|
2018-04-26 15:09:10 +00:00
|
|
|
MOZ_ASSERT(IsEditorDataAvailable());
|
|
|
|
|
2016-10-19 09:09:33 +00:00
|
|
|
if (NS_FAILED(aResult)) {
|
|
|
|
return aResult; // if aResult is an error, we return it.
|
|
|
|
}
|
2015-05-28 15:58:42 +00:00
|
|
|
|
2018-04-26 15:09:10 +00:00
|
|
|
Element* rootElement = TextEditorRef().GetRoot();
|
|
|
|
if (NS_WARN_IF(!rootElement)) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2012-01-04 15:15:09 +00:00
|
|
|
|
2017-02-02 15:32:58 +00:00
|
|
|
nsCOMPtr<nsIHTMLCollection> nodeList =
|
2018-04-26 15:09:10 +00:00
|
|
|
rootElement->GetElementsByTagName(NS_LITERAL_STRING("br"));
|
2017-02-02 15:32:58 +00:00
|
|
|
MOZ_ASSERT(nodeList);
|
|
|
|
uint32_t len = nodeList->Length();
|
2016-10-19 09:09:33 +00:00
|
|
|
|
2017-02-02 15:32:58 +00:00
|
|
|
if (len != 1) {
|
|
|
|
// only in the case of one br could there be the bogus node
|
|
|
|
mBogusNode = nullptr;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2016-10-19 09:09:33 +00:00
|
|
|
|
2018-04-26 15:09:10 +00:00
|
|
|
Element* brElement = nodeList->Item(0);
|
|
|
|
if (TextEditorRef().IsMozEditorBogusNode(brElement)) {
|
|
|
|
mBogusNode = brElement;
|
2017-02-02 15:32:58 +00:00
|
|
|
} else {
|
|
|
|
mBogusNode = nullptr;
|
1999-03-15 05:08:30 +00:00
|
|
|
}
|
2016-10-19 09:09:33 +00:00
|
|
|
return NS_OK;
|
1999-03-15 05:08:30 +00:00
|
|
|
}
|
|
|
|
|
2018-05-10 06:03:21 +00:00
|
|
|
nsresult TextEditRules::WillOutputText(const nsAString* aOutputFormat,
|
2016-07-09 02:34:41 +00:00
|
|
|
nsAString* aOutString, uint32_t aFlags,
|
|
|
|
bool* aCancel, bool* aHandled) {
|
2018-04-26 15:09:10 +00:00
|
|
|
MOZ_ASSERT(IsEditorDataAvailable());
|
|
|
|
|
1999-06-24 23:36:56 +00:00
|
|
|
// null selection ok
|
2017-06-16 10:08:10 +00:00
|
|
|
if (NS_WARN_IF(!aOutString) || NS_WARN_IF(!aOutputFormat) ||
|
|
|
|
NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
|
2016-10-24 02:27:45 +00:00
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
}
|
1999-06-24 23:36:56 +00:00
|
|
|
|
|
|
|
// initialize out param
|
2011-10-17 14:59:28 +00:00
|
|
|
*aCancel = false;
|
|
|
|
*aHandled = false;
|
1999-09-09 19:39:36 +00:00
|
|
|
|
2017-06-16 10:08:10 +00:00
|
|
|
if (!aOutputFormat->LowerCaseEqualsLiteral("text/plain")) {
|
|
|
|
return NS_OK;
|
1999-08-24 13:48:08 +00:00
|
|
|
}
|
2017-06-16 10:08:10 +00:00
|
|
|
|
|
|
|
// XXX Looks like that even if it's password field, we need to use the
|
|
|
|
// expensive path if the caller requests some complicated handling.
|
|
|
|
// However, changing the behavior for password field might cause
|
|
|
|
// security issue. So, be careful when you touch here.
|
|
|
|
if (IsPasswordEditor()) {
|
|
|
|
*aOutString = mPasswordText;
|
|
|
|
*aHandled = true;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there is a bogus node, there's no content. So output empty string.
|
|
|
|
if (mBogusNode) {
|
|
|
|
aOutString->Truncate();
|
|
|
|
*aHandled = true;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If it's necessary to check selection range or the editor wraps hard,
|
|
|
|
// we need some complicated handling. In such case, we need to use the
|
|
|
|
// expensive path.
|
|
|
|
// XXX Anything else what we cannot return plain text simply?
|
|
|
|
if (aFlags & nsIDocumentEncoder::OutputSelectionOnly ||
|
|
|
|
aFlags & nsIDocumentEncoder::OutputWrap) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If it's neither <input type="text"> nor <textarea>, e.g., an HTML editor
|
|
|
|
// which is in plaintext mode (e.g., plaintext email composer on Thunderbird),
|
|
|
|
// it should be handled by the expensive path.
|
2018-04-26 15:09:10 +00:00
|
|
|
if (TextEditorRef().AsHTMLEditor()) {
|
2017-06-16 10:08:10 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2018-04-26 15:09:10 +00:00
|
|
|
Element* root = TextEditorRef().GetRoot();
|
2017-06-16 10:08:10 +00:00
|
|
|
if (!root) { // Don't warn it, this is possible, e.g., 997805.html
|
|
|
|
aOutString->Truncate();
|
|
|
|
*aHandled = true;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIContent* firstChild = root->GetFirstChild();
|
|
|
|
if (!firstChild) {
|
|
|
|
aOutString->Truncate();
|
|
|
|
*aHandled = true;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If it's an <input type="text"> element, the DOM tree should be:
|
|
|
|
// <div class="anonymous-div">
|
|
|
|
// #text
|
|
|
|
// </div>
|
|
|
|
//
|
|
|
|
// If it's a <textarea> element, the DOM tree should be:
|
|
|
|
// <div class="anonymous-div">
|
|
|
|
// #text (if there is)
|
|
|
|
// <br type="_moz">
|
|
|
|
// <scrollbar orient="horizontal">
|
|
|
|
// ...
|
|
|
|
// </div>
|
|
|
|
|
|
|
|
Text* text = firstChild->GetAsText();
|
|
|
|
nsIContent* firstChildExceptText =
|
|
|
|
text ? firstChild->GetNextSibling() : firstChild;
|
|
|
|
// If the DOM tree is unexpected, fall back to the expensive path.
|
|
|
|
bool isInput = IsSingleLineEditor();
|
|
|
|
bool isTextarea = !isInput;
|
|
|
|
if (NS_WARN_IF(isInput && firstChildExceptText) ||
|
|
|
|
NS_WARN_IF(isTextarea && !firstChildExceptText) ||
|
|
|
|
NS_WARN_IF(isTextarea && !TextEditUtils::IsMozBR(firstChildExceptText) &&
|
|
|
|
!firstChildExceptText->IsXULElement(nsGkAtoms::scrollbar))) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there is no text node in the expected DOM tree, we can say that it's
|
|
|
|
// just empty.
|
|
|
|
if (!text) {
|
|
|
|
aOutString->Truncate();
|
|
|
|
*aHandled = true;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, the text is the value.
|
2018-03-19 19:18:07 +00:00
|
|
|
text->GetData(*aOutString);
|
2017-06-16 10:08:10 +00:00
|
|
|
|
|
|
|
*aHandled = true;
|
1999-06-24 23:36:56 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2016-07-09 02:34:41 +00:00
|
|
|
nsresult TextEditRules::RemoveRedundantTrailingBR() {
|
2018-04-26 15:09:10 +00:00
|
|
|
MOZ_ASSERT(IsEditorDataAvailable());
|
|
|
|
|
2011-08-12 19:53:10 +00:00
|
|
|
// If the bogus node exists, we have no work to do
|
2016-10-24 02:27:45 +00:00
|
|
|
if (mBogusNode) {
|
2011-08-12 19:53:10 +00:00
|
|
|
return NS_OK;
|
2016-10-24 02:27:45 +00:00
|
|
|
}
|
2011-08-12 19:53:10 +00:00
|
|
|
|
|
|
|
// Likewise, nothing to be done if we could never have inserted a trailing br
|
2016-10-24 02:27:45 +00:00
|
|
|
if (IsSingleLineEditor()) {
|
2011-08-12 19:53:10 +00:00
|
|
|
return NS_OK;
|
2016-10-24 02:27:45 +00:00
|
|
|
}
|
2011-08-12 19:53:10 +00:00
|
|
|
|
2018-04-26 15:09:10 +00:00
|
|
|
Element* rootElement = TextEditorRef().GetRoot();
|
|
|
|
if (NS_WARN_IF(!rootElement)) {
|
2011-08-12 19:53:10 +00:00
|
|
|
return NS_ERROR_NULL_POINTER;
|
2016-10-24 02:27:45 +00:00
|
|
|
}
|
2011-08-12 19:53:10 +00:00
|
|
|
|
2018-04-26 15:09:10 +00:00
|
|
|
uint32_t childCount = rootElement->GetChildCount();
|
2012-01-11 08:23:07 +00:00
|
|
|
if (childCount > 1) {
|
|
|
|
// The trailing br is redundant if it is the only remaining child node
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2011-08-12 19:53:10 +00:00
|
|
|
|
2018-04-26 15:09:10 +00:00
|
|
|
RefPtr<nsIContent> child = rootElement->GetFirstChild();
|
2012-01-11 08:23:07 +00:00
|
|
|
if (!child || !child->IsElement()) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2011-08-12 19:53:10 +00:00
|
|
|
|
2018-04-26 15:09:10 +00:00
|
|
|
RefPtr<Element> childElement = child->AsElement();
|
|
|
|
if (!TextEditUtils::IsMozBR(childElement)) {
|
2012-01-11 08:23:07 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
2011-08-12 19:53:10 +00:00
|
|
|
|
2012-01-11 08:23:07 +00:00
|
|
|
// Rather than deleting this node from the DOM tree we should instead
|
|
|
|
// morph this br into the bogus node
|
2018-04-26 15:09:10 +00:00
|
|
|
childElement->UnsetAttr(kNameSpaceID_None, nsGkAtoms::type, true);
|
2018-05-11 07:36:07 +00:00
|
|
|
if (NS_WARN_IF(!CanHandleEditAction())) {
|
|
|
|
return NS_ERROR_EDITOR_DESTROYED;
|
|
|
|
}
|
2011-08-12 19:53:10 +00:00
|
|
|
|
2012-01-11 08:23:07 +00:00
|
|
|
// set mBogusNode to be this <br>
|
2018-04-26 15:09:10 +00:00
|
|
|
mBogusNode = childElement;
|
2011-08-12 19:53:10 +00:00
|
|
|
|
2012-01-11 08:23:07 +00:00
|
|
|
// give it the bogus node attribute
|
2018-04-26 15:09:10 +00:00
|
|
|
childElement->SetAttr(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom,
|
|
|
|
kMOZEditorBogusNodeValue, false);
|
2011-08-12 19:53:10 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2016-07-09 02:34:41 +00:00
|
|
|
nsresult TextEditRules::CreateTrailingBRIfNeeded() {
|
2018-04-26 15:09:10 +00:00
|
|
|
MOZ_ASSERT(IsEditorDataAvailable());
|
|
|
|
|
2002-04-06 19:07:47 +00:00
|
|
|
// but only if we aren't a single line edit field
|
2012-05-05 09:00:05 +00:00
|
|
|
if (IsSingleLineEditor()) {
|
2002-04-06 19:07:47 +00:00
|
|
|
return NS_OK;
|
2012-05-05 09:00:05 +00:00
|
|
|
}
|
|
|
|
|
2018-04-26 15:09:10 +00:00
|
|
|
Element* rootElement = TextEditorRef().GetRoot();
|
|
|
|
if (NS_WARN_IF(!rootElement)) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2012-05-05 09:00:05 +00:00
|
|
|
|
2018-04-26 15:09:10 +00:00
|
|
|
nsCOMPtr<nsIContent> lastChild = rootElement->GetLastChild();
|
2002-04-06 19:07:47 +00:00
|
|
|
// assuming CreateBogusNodeIfNeeded() has been called first
|
2018-04-26 15:09:10 +00:00
|
|
|
if (NS_WARN_IF(!lastChild)) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2003-07-18 14:12:51 +00:00
|
|
|
|
2015-03-03 11:08:59 +00:00
|
|
|
if (!lastChild->IsHTMLElement(nsGkAtoms::br)) {
|
2018-08-01 12:30:14 +00:00
|
|
|
AutoTransactionsConserveSelection dontChangeMySelection(TextEditorRef());
|
2019-05-08 09:40:17 +00:00
|
|
|
EditorDOMPoint endOfRoot;
|
2018-04-26 15:09:10 +00:00
|
|
|
endOfRoot.SetToEndOf(rootElement);
|
2018-05-11 06:52:24 +00:00
|
|
|
CreateElementResult createMozBrResult = CreateMozBR(endOfRoot);
|
|
|
|
if (NS_WARN_IF(createMozBrResult.Failed())) {
|
|
|
|
return createMozBrResult.Rv();
|
2017-12-11 07:37:10 +00:00
|
|
|
}
|
|
|
|
return NS_OK;
|
2012-05-05 09:00:05 +00:00
|
|
|
}
|
2012-04-12 20:55:48 +00:00
|
|
|
|
2012-05-05 09:00:05 +00:00
|
|
|
// 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.
|
2018-04-26 15:09:10 +00:00
|
|
|
if (!TextEditorRef().IsMozEditorBogusNode(lastChild)) {
|
2012-05-05 09:00:05 +00:00
|
|
|
return NS_OK;
|
2002-04-06 19:07:47 +00:00
|
|
|
}
|
2012-05-05 09:00:05 +00:00
|
|
|
|
|
|
|
// Morph it back to a mozBR
|
2017-12-05 17:05:51 +00:00
|
|
|
lastChild->AsElement()->UnsetAttr(kNameSpaceID_None,
|
|
|
|
kMOZEditorBogusNodeAttrAtom, false);
|
|
|
|
lastChild->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
|
|
|
|
NS_LITERAL_STRING("_moz"), true);
|
2018-05-11 07:31:53 +00:00
|
|
|
if (NS_WARN_IF(!CanHandleEditAction())) {
|
|
|
|
return NS_ERROR_EDITOR_DESTROYED;
|
|
|
|
}
|
2012-05-05 09:00:05 +00:00
|
|
|
return NS_OK;
|
2002-04-06 19:07:47 +00:00
|
|
|
}
|
2000-01-13 10:17:35 +00:00
|
|
|
|
2018-05-10 06:03:21 +00:00
|
|
|
nsresult TextEditRules::CreateBogusNodeIfNeeded() {
|
2018-04-26 15:09:10 +00:00
|
|
|
MOZ_ASSERT(IsEditorDataAvailable());
|
|
|
|
|
2012-02-01 10:54:22 +00:00
|
|
|
if (mBogusNode) {
|
|
|
|
// Let's not create more than one, ok?
|
|
|
|
return NS_OK;
|
|
|
|
}
|
1999-12-07 08:30:19 +00:00
|
|
|
|
|
|
|
// tell rules system to not do any post-processing
|
Bug 1463985 - part 1: Rename EditAction to EditSubAction and related stuff r=m_kato
When we implement InputEvent.inputType, we need to set a stack class to record
which edit action is currently handled. However, currently, we call smaller
jobs as edit action. For example, when user types a character at selecting
some characters, then, EditAction::deleteSelection is performed first, then,
EditAction::insertText is performed. However, for the InputEvent.inputType,
we need inserText information. So, for making new enum EditAction, we need
to rename current EditAction to EditSubAction.
And also this renames related stuff:
EditorBase::mIsInEditAction -> EditorBase::mIsInEditSubAction
EditorBase::IsInEditAction() -> EditorBase::IsInEditSubAction()
EditorBase::mAction -> EditorBase::mTopLevelEditSubAction
TextEditRules::mTheAction -> TextEditRules::mTopLevelEditSubAction
EditorBase::StartOperation() ->
EditorBase::OnStartToHandleTopLevelEditSubAction()
EditorBase::EndOperation() ->
EditorBase::OnEndHandlingTopLevelEditSubAction()
AutoRules -> AutoTopLevelEditSubActionNotifier
RulesInfo -> EditSubActionInfo
MozReview-Commit-ID: cvSkPUjFm1
--HG--
extra : rebase_source : baf527a3e353b7a8ebe9a46be2243b059c500234
2018-05-28 11:12:34 +00:00
|
|
|
AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
|
2018-05-28 15:06:24 +00:00
|
|
|
TextEditorRef(), EditSubAction::eCreateBogusNode, nsIEditor::eNone);
|
2007-10-03 12:16:50 +00:00
|
|
|
|
2018-04-26 15:09:10 +00:00
|
|
|
RefPtr<Element> rootElement = TextEditorRef().GetRoot();
|
|
|
|
if (!rootElement) {
|
2012-02-01 10:54:22 +00:00
|
|
|
// We don't even have a body yet, don't insert any bogus nodes at
|
2005-03-24 19:00:01 +00:00
|
|
|
// this point.
|
|
|
|
return NS_OK;
|
|
|
|
}
|
1999-08-19 13:30:48 +00:00
|
|
|
|
2012-02-01 10:54:22 +00:00
|
|
|
// 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.
|
2018-04-26 15:09:10 +00:00
|
|
|
bool isRootEditable = TextEditorRef().IsEditable(rootElement);
|
|
|
|
for (nsIContent* rootChild = rootElement->GetFirstChild(); rootChild;
|
|
|
|
rootChild = rootChild->GetNextSibling()) {
|
|
|
|
if (TextEditorRef().IsMozEditorBogusNode(rootChild) || !isRootEditable ||
|
|
|
|
TextEditorRef().IsEditable(rootChild) ||
|
|
|
|
TextEditorRef().IsBlockNode(rootChild)) {
|
2012-02-01 10:54:22 +00:00
|
|
|
return NS_OK;
|
1999-08-19 13:30:48 +00:00
|
|
|
}
|
|
|
|
}
|
2003-07-18 14:12:51 +00:00
|
|
|
|
2012-02-01 10:54:22 +00:00
|
|
|
// Skip adding the bogus node if body is read-only.
|
2018-08-03 11:10:46 +00:00
|
|
|
if (!TextEditorRef().IsModifiableNode(*rootElement)) {
|
2012-02-01 10:54:22 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
1999-08-19 13:30:48 +00:00
|
|
|
|
2012-02-01 10:54:22 +00:00
|
|
|
// Create a br.
|
2018-04-26 15:09:10 +00:00
|
|
|
RefPtr<Element> newBrElement =
|
|
|
|
TextEditorRef().CreateHTMLContent(nsGkAtoms::br);
|
2018-05-11 07:24:15 +00:00
|
|
|
if (NS_WARN_IF(!CanHandleEditAction())) {
|
|
|
|
return NS_ERROR_EDITOR_DESTROYED;
|
|
|
|
}
|
2018-04-26 15:09:10 +00:00
|
|
|
if (NS_WARN_IF(!newBrElement)) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
fixes:
14753, 29843, 39864, 40141,
40139, 36679, 39542, 34729,
34855, 37216, 39292, 26447
r=sfraser,cmanske,fm; a=beppe
2000-05-24 23:00:24 +00:00
|
|
|
|
2012-02-01 10:54:22 +00:00
|
|
|
// set mBogusNode to be the newly created <br>
|
2018-04-26 15:09:10 +00:00
|
|
|
mBogusNode = newBrElement;
|
2012-02-01 10:54:22 +00:00
|
|
|
|
|
|
|
// Give it a special attribute.
|
2018-04-26 15:09:10 +00:00
|
|
|
newBrElement->SetAttr(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom,
|
|
|
|
kMOZEditorBogusNodeValue, false);
|
2019-03-30 11:55:29 +00:00
|
|
|
if (NS_WARN_IF(mBogusNode != newBrElement)) {
|
|
|
|
return NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE;
|
|
|
|
}
|
2012-02-01 10:54:22 +00:00
|
|
|
|
|
|
|
// Put the node in the document.
|
2019-03-30 11:55:29 +00:00
|
|
|
nsresult rv = MOZ_KnownLive(TextEditorRef())
|
2019-05-08 09:40:17 +00:00
|
|
|
.InsertNodeWithTransaction(*newBrElement,
|
|
|
|
EditorDOMPoint(rootElement, 0));
|
2018-05-11 07:24:15 +00:00
|
|
|
if (NS_WARN_IF(!CanHandleEditAction())) {
|
|
|
|
return NS_ERROR_EDITOR_DESTROYED;
|
|
|
|
}
|
2017-11-22 12:25:05 +00:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2012-02-01 10:54:22 +00:00
|
|
|
|
|
|
|
// Set selection.
|
2018-05-08 08:30:05 +00:00
|
|
|
IgnoredErrorResult error;
|
2018-10-30 10:02:58 +00:00
|
|
|
SelectionRefPtr()->Collapse(EditorRawDOMPoint(rootElement, 0), error);
|
2018-05-11 07:24:15 +00:00
|
|
|
if (NS_WARN_IF(!CanHandleEditAction())) {
|
|
|
|
return NS_ERROR_EDITOR_DESTROYED;
|
|
|
|
}
|
2018-05-08 08:30:05 +00:00
|
|
|
NS_WARNING_ASSERTION(
|
|
|
|
!error.Failed(),
|
|
|
|
"Failed to collapse selection at start of the root element");
|
2012-02-01 10:54:22 +00:00
|
|
|
return NS_OK;
|
1999-08-24 08:56:51 +00:00
|
|
|
}
|
|
|
|
|
2018-05-10 06:03:21 +00:00
|
|
|
nsresult TextEditRules::TruncateInsertionIfNeeded(const nsAString* aInString,
|
2016-07-09 02:34:41 +00:00
|
|
|
nsAString* aOutString,
|
|
|
|
int32_t aMaxLength,
|
|
|
|
bool* aTruncated) {
|
2018-04-26 15:09:10 +00:00
|
|
|
MOZ_ASSERT(IsEditorDataAvailable());
|
|
|
|
|
2018-05-10 06:03:21 +00:00
|
|
|
if (NS_WARN_IF(!aInString) || NS_WARN_IF(!aOutString)) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
2016-10-24 02:27:45 +00:00
|
|
|
}
|
2015-05-28 15:58:42 +00:00
|
|
|
|
2015-01-28 09:00:40 +00:00
|
|
|
if (!aOutString->Assign(*aInString, mozilla::fallible)) {
|
2015-01-16 14:09:55 +00:00
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
2010-08-27 05:36:09 +00:00
|
|
|
if (aTruncated) {
|
2011-10-17 14:59:28 +00:00
|
|
|
*aTruncated = false;
|
2010-08-27 05:36:09 +00:00
|
|
|
}
|
2015-05-28 15:58:42 +00:00
|
|
|
|
2016-07-09 02:54:50 +00:00
|
|
|
if (-1 != aMaxLength && IsPlaintextEditor() &&
|
2018-04-26 15:09:10 +00:00
|
|
|
!TextEditorRef().IsIMEComposing()) {
|
1999-08-24 08:56:51 +00:00
|
|
|
// Get the current text length.
|
|
|
|
// Get the length of inString.
|
1999-12-07 08:30:19 +00:00
|
|
|
// Get the length of the selection.
|
|
|
|
// If selection is collapsed, it is length 0.
|
2015-05-28 15:58:42 +00:00
|
|
|
// Subtract the length of the selection from the len(doc)
|
1999-12-07 08:30:19 +00:00
|
|
|
// since we'll delete the selection on insert.
|
|
|
|
// This is resultingDocLength.
|
2002-12-22 01:51:14 +00:00
|
|
|
// Get old length of IME composing string
|
|
|
|
// which will be replaced by new one.
|
1999-08-24 08:56:51 +00:00
|
|
|
// If (resultingDocLength) is at or over max, cancel the insert
|
2015-05-28 15:58:42 +00:00
|
|
|
// If (resultingDocLength) + (length of input) > max,
|
1999-12-07 08:30:19 +00:00
|
|
|
// set aOutString to subset of inString so length = max
|
2012-08-22 15:56:38 +00:00
|
|
|
int32_t docLength;
|
2018-04-26 15:09:10 +00:00
|
|
|
nsresult rv = TextEditorRef().GetTextLength(&docLength);
|
2016-10-19 09:09:33 +00:00
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return rv;
|
|
|
|
}
|
2005-10-17 00:54:02 +00:00
|
|
|
|
2017-03-09 19:44:45 +00:00
|
|
|
uint32_t start, end;
|
2018-10-30 10:02:58 +00:00
|
|
|
nsContentUtils::GetSelectionInTextControl(
|
2018-04-26 15:09:10 +00:00
|
|
|
SelectionRefPtr(), TextEditorRef().GetRoot(), start, end);
|
2005-10-17 00:54:02 +00:00
|
|
|
|
2018-04-26 15:09:10 +00:00
|
|
|
TextComposition* composition = TextEditorRef().GetComposition();
|
2017-03-09 19:44:45 +00:00
|
|
|
uint32_t oldCompStrLength =
|
|
|
|
composition ? composition->String().Length() : 0;
|
2005-10-17 00:54:02 +00:00
|
|
|
|
2017-03-09 19:44:45 +00:00
|
|
|
const uint32_t selectionLength = end - start;
|
2012-08-22 15:56:38 +00:00
|
|
|
const int32_t resultingDocLength =
|
|
|
|
docLength - selectionLength - oldCompStrLength;
|
2016-10-24 02:27:45 +00:00
|
|
|
if (resultingDocLength >= aMaxLength) {
|
2015-01-16 14:09:55 +00:00
|
|
|
// This call is guaranteed to reduce the capacity of the string, so it
|
|
|
|
// cannot cause an OOM.
|
2003-07-18 02:27:19 +00:00
|
|
|
aOutString->Truncate();
|
2010-08-27 05:36:09 +00:00
|
|
|
if (aTruncated) {
|
2011-10-17 14:59:28 +00:00
|
|
|
*aTruncated = true;
|
2010-08-27 05:36:09 +00:00
|
|
|
}
|
2016-10-24 02:27:45 +00:00
|
|
|
} else {
|
2014-06-21 03:17:48 +00:00
|
|
|
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?
|
2015-01-16 14:09:55 +00:00
|
|
|
// This call is guaranteed to reduce the capacity of the string, so it
|
|
|
|
// cannot cause an OOM.
|
2014-06-21 03:17:48 +00:00
|
|
|
aOutString->Truncate(newLength);
|
2010-08-27 05:36:09 +00:00
|
|
|
if (aTruncated) {
|
2011-10-17 14:59:28 +00:00
|
|
|
*aTruncated = true;
|
2010-08-27 05:36:09 +00:00
|
|
|
}
|
1999-08-24 08:56:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-10-19 09:09:33 +00:00
|
|
|
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-28 21:24:18 +00:00
|
|
|
}
|
1999-07-01 13:42:03 +00:00
|
|
|
|
2016-07-09 02:34:41 +00:00
|
|
|
void TextEditRules::ResetIMETextPWBuf() { mPasswordIMEText.Truncate(); }
|
2001-06-06 01:21:05 +00:00
|
|
|
|
2017-03-09 19:44:45 +00:00
|
|
|
void TextEditRules::RemoveIMETextFromPWBuf(uint32_t& aStart,
|
2016-07-09 02:34:41 +00:00
|
|
|
nsAString* aIMEString) {
|
2012-06-01 11:03:17 +00:00
|
|
|
MOZ_ASSERT(aIMEString);
|
2001-06-06 01:21:05 +00:00
|
|
|
|
|
|
|
// initialize PasswordIME
|
2003-07-18 14:12:51 +00:00
|
|
|
if (mPasswordIMEText.IsEmpty()) {
|
2001-06-06 01:21:05 +00:00
|
|
|
mPasswordIMEIndex = aStart;
|
2016-10-24 02:27:45 +00:00
|
|
|
} else {
|
2003-07-18 14:12:51 +00:00
|
|
|
// manage the password buffer
|
|
|
|
mPasswordText.Cut(mPasswordIMEIndex, mPasswordIMEText.Length());
|
|
|
|
aStart = mPasswordIMEIndex;
|
2001-06-06 01:21:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
mPasswordIMEText.Assign(*aIMEString);
|
|
|
|
}
|
1999-08-24 08:56:51 +00:00
|
|
|
|
2016-07-09 02:34:41 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
TextEditRules::Notify(nsITimer* aTimer) {
|
2012-11-06 18:45:58 +00:00
|
|
|
MOZ_ASSERT(mTimer);
|
|
|
|
|
2018-04-26 13:41:34 +00:00
|
|
|
if (NS_WARN_IF(!mTextEditor)) {
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
}
|
|
|
|
|
2018-10-25 03:35:26 +00:00
|
|
|
// Check whether our text editor's password flag was changed before this
|
|
|
|
// "hide password character" timer actually fires.
|
|
|
|
if (!IsPasswordEditor()) {
|
|
|
|
return NS_OK;
|
2018-04-26 13:41:34 +00:00
|
|
|
}
|
|
|
|
|
2018-10-25 03:35:26 +00:00
|
|
|
RefPtr<TextEditor> textEditor(mTextEditor);
|
|
|
|
nsresult rv = textEditor->HideLastPasswordInput();
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2018-10-30 10:02:58 +00:00
|
|
|
nsresult TextEditRules::HideLastPasswordInput() {
|
|
|
|
MOZ_ASSERT(mTextEditor);
|
2018-10-25 03:35:26 +00:00
|
|
|
MOZ_ASSERT(IsPasswordEditor());
|
|
|
|
|
2018-10-30 10:02:58 +00:00
|
|
|
AutoSafeEditorData setData(*this, *mTextEditor);
|
2018-04-26 13:41:34 +00:00
|
|
|
|
2018-10-25 03:35:26 +00:00
|
|
|
nsresult rv = HideLastPasswordInputInternal();
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
2009-11-25 17:50:35 +00:00
|
|
|
ASSERT_PASSWORD_LENGTHS_EQUAL();
|
|
|
|
mLastLength = 0;
|
2018-10-25 03:35:26 +00:00
|
|
|
return NS_OK;
|
2009-11-02 15:37:25 +00:00
|
|
|
}
|
|
|
|
|
2017-07-26 18:18:20 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
TextEditRules::GetName(nsACString& aName) {
|
|
|
|
aName.AssignLiteral("TextEditRules");
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2018-10-25 03:35:26 +00:00
|
|
|
nsresult TextEditRules::HideLastPasswordInputInternal() {
|
2018-04-26 15:09:10 +00:00
|
|
|
MOZ_ASSERT(IsEditorDataAvailable());
|
|
|
|
|
2009-11-25 17:50:35 +00:00
|
|
|
if (!mLastLength) {
|
|
|
|
// Special case, we're trying to replace a range that no longer exists
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsAutoString hiddenText;
|
|
|
|
FillBufWithPWChars(&hiddenText, mLastLength);
|
|
|
|
|
2017-03-09 19:44:45 +00:00
|
|
|
uint32_t start, end;
|
2018-10-30 10:02:58 +00:00
|
|
|
nsContentUtils::GetSelectionInTextControl(
|
2018-04-26 15:09:10 +00:00
|
|
|
SelectionRefPtr(), TextEditorRef().GetRoot(), start, end);
|
2009-11-25 17:50:35 +00:00
|
|
|
|
Bug 1463327 - part 1: Change scope of some methods of EditorBase which won't be called by non-helper classes of editing to protected r=m_kato
EditorBase (and other editor classes) have 2 type of public methods. One is
true-public methods. I.e., they should be able to be called by anybody.
E.g., command handlers, event listeners, or JS via nsIEditor interface.
The other is semi-public methods. They are not called by the above examples
but called by other classes which are helper classes to handle edit actions.
E.g., TextEditRules, HTMLEditRules, HTMLEditUtils, CSSEditUtils and Transaction
classes.
When we will implement InputEvent.inputType, we need to create new stack
class and create its instance at every true-public methods to manage current
inputType (like TextEditRules::AutoSafeEditorData). Therefore, it should not
happen that new code starts to call semi-public methods without the new
stack class instance.
For preventing this issue, we should make EditorBase have only the true-public
methods as public. The other public methods should be protected and their
users should be friend classes. Then, we can protect such method from external
classes.
Note that this patch just moves the methods without any changes in EditorBase.h
(except removes GetName() since there is no body of this method and removes
IterDirection since it's unused).
MozReview-Commit-ID: HBseKLL6pxx
--HG--
extra : rebase_source : 2251ff659d831d01a6778d38f4e2714fcf2d6ef4
2018-05-22 07:08:43 +00:00
|
|
|
nsCOMPtr<nsINode> selNode = GetTextNodeAroundSelectionStartContainer();
|
2018-05-08 08:30:05 +00:00
|
|
|
if (NS_WARN_IF(!selNode)) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2015-05-28 15:58:42 +00:00
|
|
|
|
2018-03-19 19:45:34 +00:00
|
|
|
selNode->GetAsText()->ReplaceData(mLastStart, mLastLength, hiddenText,
|
|
|
|
IgnoreErrors());
|
2018-05-11 07:08:47 +00:00
|
|
|
if (NS_WARN_IF(!CanHandleEditAction())) {
|
|
|
|
return NS_ERROR_EDITOR_DESTROYED;
|
|
|
|
}
|
2019-03-26 10:09:47 +00:00
|
|
|
IgnoredErrorResult ignoredError;
|
|
|
|
MOZ_KnownLive(SelectionRefPtr())
|
|
|
|
->SetStartAndEndInLimiter(RawRangeBoundary(selNode, start),
|
|
|
|
RawRangeBoundary(selNode, end), ignoredError);
|
2018-05-11 07:08:47 +00:00
|
|
|
if (NS_WARN_IF(!CanHandleEditAction())) {
|
|
|
|
return NS_ERROR_EDITOR_DESTROYED;
|
|
|
|
}
|
2019-03-26 10:09:47 +00:00
|
|
|
NS_WARNING_ASSERTION(!ignoredError.Failed(), "Failed to set selection");
|
2009-11-02 15:37:25 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2010-02-01 18:12:31 +00:00
|
|
|
// static
|
2016-07-09 02:34:41 +00:00
|
|
|
void TextEditRules::FillBufWithPWChars(nsAString* aOutString, int32_t aLength) {
|
2012-06-01 11:03:17 +00:00
|
|
|
MOZ_ASSERT(aOutString);
|
1999-08-24 08:56:51 +00:00
|
|
|
|
2006-10-23 20:48:05 +00:00
|
|
|
// change the output to the platform password character
|
2014-01-04 15:02:17 +00:00
|
|
|
char16_t passwordChar = LookAndFeel::GetPasswordCharacter();
|
2006-10-23 20:48:05 +00:00
|
|
|
|
2003-07-18 02:27:19 +00:00
|
|
|
aOutString->Truncate();
|
2016-10-24 02:27:45 +00:00
|
|
|
for (int32_t i = 0; i < aLength; i++) {
|
2006-10-23 20:48:05 +00:00
|
|
|
aOutString->Append(passwordChar);
|
2016-10-24 02:27:45 +00:00
|
|
|
}
|
1999-08-24 08:56:51 +00:00
|
|
|
}
|
|
|
|
|
2018-05-11 06:52:24 +00:00
|
|
|
CreateElementResult TextEditRules::CreateBRInternal(
|
2019-05-08 09:40:17 +00:00
|
|
|
const EditorDOMPoint& aPointToInsert, bool aCreateMozBR) {
|
2018-04-26 15:09:10 +00:00
|
|
|
MOZ_ASSERT(IsEditorDataAvailable());
|
2017-12-11 07:37:10 +00:00
|
|
|
|
2018-04-26 15:09:10 +00:00
|
|
|
if (NS_WARN_IF(!aPointToInsert.IsSet())) {
|
2018-05-11 06:52:24 +00:00
|
|
|
return CreateElementResult(NS_ERROR_FAILURE);
|
2017-11-27 06:05:04 +00:00
|
|
|
}
|
1999-11-29 08:28:46 +00:00
|
|
|
|
2018-04-16 10:21:29 +00:00
|
|
|
RefPtr<Element> brElement =
|
2019-05-07 22:34:28 +00:00
|
|
|
MOZ_KnownLive(TextEditorRef())
|
|
|
|
.InsertBrElementWithTransaction(aPointToInsert);
|
2018-05-11 06:52:24 +00:00
|
|
|
if (NS_WARN_IF(!CanHandleEditAction())) {
|
|
|
|
return CreateElementResult(NS_ERROR_EDITOR_DESTROYED);
|
|
|
|
}
|
2017-12-11 07:37:10 +00:00
|
|
|
if (NS_WARN_IF(!brElement)) {
|
2018-05-11 06:52:24 +00:00
|
|
|
return CreateElementResult(NS_ERROR_FAILURE);
|
2017-11-27 06:05:04 +00:00
|
|
|
}
|
1999-11-29 08:28:46 +00:00
|
|
|
|
|
|
|
// give it special moz attr
|
2018-05-11 06:52:24 +00:00
|
|
|
if (!aCreateMozBR) {
|
|
|
|
return CreateElementResult(brElement.forget());
|
1999-11-29 08:28:46 +00:00
|
|
|
}
|
2012-05-05 09:00:05 +00:00
|
|
|
|
2018-05-11 06:52:24 +00:00
|
|
|
// XXX Why do we need to set this attribute with transaction?
|
2019-05-08 06:26:25 +00:00
|
|
|
nsresult rv = MOZ_KnownLive(TextEditorRef())
|
|
|
|
.SetAttributeWithTransaction(*brElement, *nsGkAtoms::type,
|
|
|
|
NS_LITERAL_STRING("_moz"));
|
2018-05-11 06:52:24 +00:00
|
|
|
// XXX Don't we need to remove the new <br> element from the DOM tree
|
|
|
|
// in these case?
|
|
|
|
if (NS_WARN_IF(!CanHandleEditAction())) {
|
|
|
|
return CreateElementResult(NS_ERROR_EDITOR_DESTROYED);
|
|
|
|
}
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return CreateElementResult(NS_ERROR_FAILURE);
|
|
|
|
}
|
|
|
|
return CreateElementResult(brElement.forget());
|
1999-11-29 08:28:46 +00:00
|
|
|
}
|
2010-11-11 21:40:52 +00:00
|
|
|
|
2016-07-09 02:34:41 +00:00
|
|
|
bool TextEditRules::IsPasswordEditor() const {
|
2016-07-09 02:54:50 +00:00
|
|
|
return mTextEditor ? mTextEditor->IsPasswordEditor() : false;
|
2016-07-09 02:34:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool TextEditRules::IsSingleLineEditor() const {
|
2016-07-09 02:54:50 +00:00
|
|
|
return mTextEditor ? mTextEditor->IsSingleLineEditor() : false;
|
2016-07-09 02:34:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool TextEditRules::IsPlaintextEditor() const {
|
2016-07-09 02:54:50 +00:00
|
|
|
return mTextEditor ? mTextEditor->IsPlaintextEditor() : false;
|
2016-07-09 02:34:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool TextEditRules::IsReadonly() const {
|
2016-07-09 02:54:50 +00:00
|
|
|
return mTextEditor ? mTextEditor->IsReadonly() : false;
|
2016-07-09 02:34:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool TextEditRules::IsDisabled() const {
|
2016-07-09 02:54:50 +00:00
|
|
|
return mTextEditor ? mTextEditor->IsDisabled() : false;
|
2016-07-09 02:34:41 +00:00
|
|
|
}
|
|
|
|
bool TextEditRules::IsMailEditor() const {
|
2016-07-09 02:54:50 +00:00
|
|
|
return mTextEditor ? mTextEditor->IsMailEditor() : false;
|
2016-07-09 02:34:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool TextEditRules::DontEchoPassword() const {
|
2018-11-02 07:33:58 +00:00
|
|
|
if (!mTextEditor) {
|
|
|
|
// XXX Why do we use echo password if editor is null?
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!LookAndFeel::GetEchoPassword() || mTextEditor->DontEchoPassword()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mTextEditor->GetEditAction() != EditAction::eDrop &&
|
|
|
|
mTextEditor->GetEditAction() != EditAction::ePaste) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2016-07-09 02:34:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace mozilla
|