1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "mozilla/TextEditRules.h"
- #include "TextEditUtils.h"
- #include "mozilla/Assertions.h"
- #include "mozilla/EditorUtils.h"
- #include "mozilla/LookAndFeel.h"
- #include "mozilla/Preferences.h"
- #include "mozilla/TextComposition.h"
- #include "mozilla/TextEditor.h"
- #include "mozilla/dom/Element.h"
- #include "mozilla/dom/NodeIterator.h"
- #include "mozilla/dom/Selection.h"
- #include "nsAString.h"
- #include "nsCOMPtr.h"
- #include "nsCRT.h"
- #include "nsCRTGlue.h"
- #include "nsComponentManagerUtils.h"
- #include "nsContentUtils.h"
- #include "nsDebug.h"
- #include "nsError.h"
- #include "nsGkAtoms.h"
- #include "nsIContent.h"
- #include "nsIDOMDocument.h"
- #include "nsIDOMElement.h"
- #include "nsIDOMNode.h"
- #include "nsIDOMNodeFilter.h"
- #include "nsIDOMNodeIterator.h"
- #include "nsIDOMNodeList.h"
- #include "nsIDOMText.h"
- #include "nsNameSpaceManager.h"
- #include "nsINode.h"
- #include "nsIPlaintextEditor.h"
- #include "nsISupportsBase.h"
- #include "nsLiteralString.h"
- #include "nsUnicharUtils.h"
- namespace mozilla {
- using namespace dom;
- #define CANCEL_OPERATION_IF_READONLY_OR_DISABLED \
- if (IsReadonly() || IsDisabled()) \
- { \
- *aCancel = true; \
- return NS_OK; \
- };
- /********************************************************
- * mozilla::TextEditRules
- ********************************************************/
- TextEditRules::TextEditRules()
- : mTextEditor(nullptr)
- , mPasswordIMEIndex(0)
- , mCachedSelectionOffset(0)
- , mActionNesting(0)
- , mLockRulesSniffing(false)
- , mDidExplicitlySetInterline(false)
- , mDeleteBidiImmediately(false)
- , mTheAction(EditAction::none)
- , mLastStart(0)
- , mLastLength(0)
- {
- InitFields();
- }
- void
- TextEditRules::InitFields()
- {
- mTextEditor = nullptr;
- mPasswordText.Truncate();
- mPasswordIMEText.Truncate();
- mPasswordIMEIndex = 0;
- mBogusNode = nullptr;
- mCachedSelectionNode = nullptr;
- mCachedSelectionOffset = 0;
- mActionNesting = 0;
- mLockRulesSniffing = false;
- mDidExplicitlySetInterline = false;
- mDeleteBidiImmediately = false;
- mTheAction = EditAction::none;
- mTimer = nullptr;
- mLastStart = 0;
- mLastLength = 0;
- }
- TextEditRules::~TextEditRules()
- {
- // do NOT delete mTextEditor here. We do not hold a ref count to
- // mTextEditor. mTextEditor owns our lifespan.
- if (mTimer) {
- mTimer->Cancel();
- }
- }
- NS_IMPL_CYCLE_COLLECTION(TextEditRules, mBogusNode, mCachedSelectionNode)
- NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TextEditRules)
- NS_INTERFACE_MAP_ENTRY(nsIEditRules)
- NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
- NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditRules)
- NS_INTERFACE_MAP_END
- NS_IMPL_CYCLE_COLLECTING_ADDREF(TextEditRules)
- NS_IMPL_CYCLE_COLLECTING_RELEASE(TextEditRules)
- NS_IMETHODIMP
- TextEditRules::Init(TextEditor* aTextEditor)
- {
- if (!aTextEditor) {
- return NS_ERROR_NULL_POINTER;
- }
- InitFields();
- // We hold a non-refcounted reference back to our editor.
- mTextEditor = aTextEditor;
- RefPtr<Selection> selection = mTextEditor->GetSelection();
- NS_WARNING_ASSERTION(selection, "editor cannot get selection");
- // Put in a magic br if needed. This method handles null selection,
- // which should never happen anyway
- nsresult rv = CreateBogusNodeIfNeeded(selection);
- NS_ENSURE_SUCCESS(rv, rv);
- // If the selection hasn't been set up yet, set it up collapsed to the end of
- // our editable content.
- int32_t rangeCount;
- rv = selection->GetRangeCount(&rangeCount);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!rangeCount) {
- rv = mTextEditor->EndOfDocument();
- NS_ENSURE_SUCCESS(rv, rv);
- }
- if (IsPlaintextEditor()) {
- // ensure trailing br node
- rv = CreateTrailingBRIfNeeded();
- NS_ENSURE_SUCCESS(rv, rv);
- }
- mDeleteBidiImmediately =
- Preferences::GetBool("bidi.edit.delete_immediately", false);
- return NS_OK;
- }
- NS_IMETHODIMP
- TextEditRules::SetInitialValue(const nsAString& aValue)
- {
- if (IsPasswordEditor()) {
- mPasswordText = aValue;
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- TextEditRules::DetachEditor()
- {
- if (mTimer) {
- mTimer->Cancel();
- }
- mTextEditor = nullptr;
- return NS_OK;
- }
- NS_IMETHODIMP
- TextEditRules::BeforeEdit(EditAction action,
- nsIEditor::EDirection aDirection)
- {
- if (mLockRulesSniffing) {
- return NS_OK;
- }
- AutoLockRulesSniffing lockIt(this);
- mDidExplicitlySetInterline = false;
- if (!mActionNesting) {
- // let rules remember the top level action
- mTheAction = action;
- }
- mActionNesting++;
- // get the selection and cache the position before editing
- NS_ENSURE_STATE(mTextEditor);
- RefPtr<Selection> selection = mTextEditor->GetSelection();
- NS_ENSURE_STATE(selection);
- selection->GetAnchorNode(getter_AddRefs(mCachedSelectionNode));
- selection->GetAnchorOffset(&mCachedSelectionOffset);
- return NS_OK;
- }
- NS_IMETHODIMP
- TextEditRules::AfterEdit(EditAction action,
- nsIEditor::EDirection aDirection)
- {
- if (mLockRulesSniffing) {
- return NS_OK;
- }
- AutoLockRulesSniffing lockIt(this);
- NS_PRECONDITION(mActionNesting>0, "bad action nesting!");
- if (!--mActionNesting) {
- NS_ENSURE_STATE(mTextEditor);
- RefPtr<Selection> selection = mTextEditor->GetSelection();
- NS_ENSURE_STATE(selection);
- NS_ENSURE_STATE(mTextEditor);
- nsresult rv =
- mTextEditor->HandleInlineSpellCheck(action, selection,
- mCachedSelectionNode,
- mCachedSelectionOffset,
- nullptr, 0, nullptr, 0);
- NS_ENSURE_SUCCESS(rv, rv);
- // if only trailing <br> remaining remove it
- rv = RemoveRedundantTrailingBR();
- if (NS_FAILED(rv)) {
- return rv;
- }
- // detect empty doc
- rv = CreateBogusNodeIfNeeded(selection);
- NS_ENSURE_SUCCESS(rv, rv);
- // ensure trailing br node
- rv = CreateTrailingBRIfNeeded();
- NS_ENSURE_SUCCESS(rv, rv);
- // collapse the selection to the trailing BR if it's at the end of our text node
- CollapseSelectionToTrailingBRIfNeeded(selection);
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- TextEditRules::WillDoAction(Selection* aSelection,
- RulesInfo* aInfo,
- bool* aCancel,
- bool* aHandled)
- {
- // null selection is legal
- MOZ_ASSERT(aInfo && aCancel && aHandled);
- *aCancel = false;
- *aHandled = false;
- // my kingdom for dynamic cast
- TextRulesInfo* info = static_cast<TextRulesInfo*>(aInfo);
- switch (info->action) {
- case EditAction::insertBreak:
- UndefineCaretBidiLevel(aSelection);
- return WillInsertBreak(aSelection, aCancel, aHandled, info->maxLength);
- case EditAction::insertText:
- case EditAction::insertIMEText:
- UndefineCaretBidiLevel(aSelection);
- return WillInsertText(info->action, aSelection, aCancel, aHandled,
- info->inString, info->outString, info->maxLength);
- case EditAction::deleteSelection:
- return WillDeleteSelection(aSelection, info->collapsedAction,
- aCancel, aHandled);
- case EditAction::undo:
- return WillUndo(aSelection, aCancel, aHandled);
- case EditAction::redo:
- return WillRedo(aSelection, aCancel, aHandled);
- case EditAction::setTextProperty:
- return WillSetTextProperty(aSelection, aCancel, aHandled);
- case EditAction::removeTextProperty:
- return WillRemoveTextProperty(aSelection, aCancel, aHandled);
- case EditAction::outputText:
- return WillOutputText(aSelection, info->outputFormat, info->outString,
- aCancel, aHandled);
- case EditAction::insertElement:
- // i had thought this would be html rules only. but we put pre elements
- // into plaintext mail when doing quoting for reply! doh!
- WillInsert(*aSelection, aCancel);
- return NS_OK;
- default:
- return NS_ERROR_FAILURE;
- }
- }
- NS_IMETHODIMP
- TextEditRules::DidDoAction(Selection* aSelection,
- RulesInfo* aInfo,
- nsresult aResult)
- {
- NS_ENSURE_STATE(mTextEditor);
- // don't let any txns in here move the selection around behind our back.
- // Note that this won't prevent explicit selection setting from working.
- AutoTransactionsConserveSelection dontSpazMySelection(mTextEditor);
- NS_ENSURE_TRUE(aSelection && aInfo, NS_ERROR_NULL_POINTER);
- // my kingdom for dynamic cast
- TextRulesInfo* info = static_cast<TextRulesInfo*>(aInfo);
- switch (info->action) {
- case EditAction::insertBreak:
- return DidInsertBreak(aSelection, aResult);
- case EditAction::insertText:
- case EditAction::insertIMEText:
- return DidInsertText(aSelection, aResult);
- case EditAction::deleteSelection:
- return DidDeleteSelection(aSelection, info->collapsedAction, aResult);
- case EditAction::undo:
- return DidUndo(aSelection, aResult);
- case EditAction::redo:
- return DidRedo(aSelection, aResult);
- case EditAction::setTextProperty:
- return DidSetTextProperty(aSelection, aResult);
- case EditAction::removeTextProperty:
- return DidRemoveTextProperty(aSelection, aResult);
- case EditAction::outputText:
- return DidOutputText(aSelection, aResult);
- default:
- // Don't fail on transactions we don't handle here!
- return NS_OK;
- }
- }
- NS_IMETHODIMP_(bool)
- TextEditRules::DocumentIsEmpty()
- {
- return (mBogusNode != nullptr);
- }
- void
- TextEditRules::WillInsert(Selection& aSelection, bool* aCancel)
- {
- MOZ_ASSERT(aCancel);
- if (IsReadonly() || IsDisabled()) {
- *aCancel = true;
- return;
- }
- // initialize out param
- *aCancel = false;
- // check for the magic content node and delete it if it exists
- if (mBogusNode) {
- NS_ENSURE_TRUE_VOID(mTextEditor);
- mTextEditor->DeleteNode(mBogusNode);
- mBogusNode = nullptr;
- }
- }
- nsresult
- TextEditRules::DidInsert(Selection* aSelection,
- nsresult aResult)
- {
- return NS_OK;
- }
- nsresult
- TextEditRules::WillInsertBreak(Selection* aSelection,
- bool* aCancel,
- bool* aHandled,
- int32_t aMaxLength)
- {
- if (!aSelection || !aCancel || !aHandled) {
- return NS_ERROR_NULL_POINTER;
- }
- CANCEL_OPERATION_IF_READONLY_OR_DISABLED
- *aHandled = false;
- if (IsSingleLineEditor()) {
- *aCancel = true;
- } else {
- // handle docs with a max length
- // NOTE, this function copies inString into outString for us.
- NS_NAMED_LITERAL_STRING(inString, "\n");
- nsAutoString outString;
- bool didTruncate;
- nsresult rv = TruncateInsertionIfNeeded(aSelection, &inString, &outString,
- aMaxLength, &didTruncate);
- NS_ENSURE_SUCCESS(rv, rv);
- if (didTruncate) {
- *aCancel = true;
- return NS_OK;
- }
- *aCancel = false;
- // if the selection isn't collapsed, delete it.
- bool bCollapsed;
- rv = aSelection->GetIsCollapsed(&bCollapsed);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!bCollapsed) {
- NS_ENSURE_STATE(mTextEditor);
- rv = mTextEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- WillInsert(*aSelection, aCancel);
- // initialize out param
- // we want to ignore result of WillInsert()
- *aCancel = false;
- }
- return NS_OK;
- }
- nsresult
- TextEditRules::DidInsertBreak(Selection* aSelection,
- nsresult aResult)
- {
- return NS_OK;
- }
- nsresult
- TextEditRules::CollapseSelectionToTrailingBRIfNeeded(Selection* aSelection)
- {
- // we only need to execute the stuff below if we are a plaintext editor.
- // html editors have a different mechanism for putting in mozBR's
- // (because there are a bunch more places you have to worry about it in html)
- if (!IsPlaintextEditor()) {
- return NS_OK;
- }
- NS_ENSURE_STATE(mTextEditor);
- // If there is no selection ranges, we should set to the end of the editor.
- // This is usually performed in TextEditRules::Init(), however, if the
- // editor is reframed, this may be called by AfterEdit().
- if (!aSelection->RangeCount()) {
- mTextEditor->EndOfDocument();
- }
- // if we are at the end of the textarea, we need to set the
- // selection to stick to the mozBR at the end of the textarea.
- int32_t selOffset;
- nsCOMPtr<nsIDOMNode> selNode;
- nsresult rv =
- mTextEditor->GetStartNodeAndOffset(aSelection,
- getter_AddRefs(selNode), &selOffset);
- NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<nsIDOMText> nodeAsText = do_QueryInterface(selNode);
- if (!nodeAsText) {
- return NS_OK; // Nothing to do if we're not at a text node.
- }
- uint32_t length;
- rv = nodeAsText->GetLength(&length);
- NS_ENSURE_SUCCESS(rv, rv);
- // nothing to do if we're not at the end of the text node
- if (selOffset != int32_t(length)) {
- return NS_OK;
- }
- int32_t parentOffset;
- nsCOMPtr<nsIDOMNode> parentNode =
- EditorBase::GetNodeLocation(selNode, &parentOffset);
- NS_ENSURE_STATE(mTextEditor);
- nsCOMPtr<nsIDOMNode> root = do_QueryInterface(mTextEditor->GetRoot());
- NS_ENSURE_TRUE(root, NS_ERROR_NULL_POINTER);
- if (parentNode != root) {
- return NS_OK;
- }
- nsCOMPtr<nsIDOMNode> nextNode = mTextEditor->GetChildAt(parentNode,
- parentOffset + 1);
- if (nextNode && TextEditUtils::IsMozBR(nextNode)) {
- rv = aSelection->Collapse(parentNode, parentOffset + 1);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- return NS_OK;
- }
- static inline already_AddRefed<nsINode>
- GetTextNode(Selection* selection)
- {
- int32_t selOffset;
- nsCOMPtr<nsINode> selNode;
- nsresult rv =
- EditorBase::GetStartNodeAndOffset(selection,
- getter_AddRefs(selNode), &selOffset);
- NS_ENSURE_SUCCESS(rv, nullptr);
- if (!EditorBase::IsTextNode(selNode)) {
- // This should be the root node, walk the tree looking for text nodes
- RefPtr<NodeIterator> iter =
- new NodeIterator(selNode, nsIDOMNodeFilter::SHOW_TEXT,
- NodeFilterHolder());
- while (!EditorBase::IsTextNode(selNode)) {
- IgnoredErrorResult rv;
- selNode = iter->NextNode(rv);
- if (!selNode) {
- return nullptr;
- }
- }
- }
- return selNode.forget();
- }
- #ifdef DEBUG
- #define ASSERT_PASSWORD_LENGTHS_EQUAL() \
- if (IsPasswordEditor() && mTextEditor->GetRoot()) { \
- int32_t txtLen; \
- mTextEditor->GetTextLength(&txtLen); \
- NS_ASSERTION(mPasswordText.Length() == uint32_t(txtLen), \
- "password length not equal to number of asterisks"); \
- }
- #else
- #define ASSERT_PASSWORD_LENGTHS_EQUAL()
- #endif
- // static
- void
- TextEditRules::HandleNewLines(nsString& aString,
- int32_t aNewlineHandling)
- {
- if (aNewlineHandling < 0) {
- int32_t caretStyle;
- TextEditor::GetDefaultEditorPrefs(aNewlineHandling, caretStyle);
- }
- switch(aNewlineHandling) {
- case nsIPlaintextEditor::eNewlinesReplaceWithSpaces:
- // Strip trailing newlines first so we don't wind up with trailing spaces
- aString.Trim(CRLF, false, true);
- aString.ReplaceChar(CRLF, ' ');
- break;
- case nsIPlaintextEditor::eNewlinesStrip:
- aString.StripChars(CRLF);
- break;
- case nsIPlaintextEditor::eNewlinesPasteToFirst:
- default: {
- int32_t firstCRLF = aString.FindCharInSet(CRLF);
- // we get first *non-empty* line.
- int32_t offset = 0;
- while (firstCRLF == offset) {
- offset++;
- firstCRLF = aString.FindCharInSet(CRLF, offset);
- }
- if (firstCRLF > 0) {
- aString.Truncate(firstCRLF);
- }
- if (offset > 0) {
- aString.Cut(0, offset);
- }
- break;
- }
- case nsIPlaintextEditor::eNewlinesReplaceWithCommas:
- aString.Trim(CRLF, true, true);
- aString.ReplaceChar(CRLF, ',');
- break;
- case nsIPlaintextEditor::eNewlinesStripSurroundingWhitespace: {
- nsAutoString result;
- uint32_t offset = 0;
- while (offset < aString.Length()) {
- int32_t nextCRLF = aString.FindCharInSet(CRLF, offset);
- if (nextCRLF < 0) {
- result.Append(nsDependentSubstring(aString, offset));
- break;
- }
- uint32_t wsBegin = nextCRLF;
- // look backwards for the first non-whitespace char
- while (wsBegin > offset && NS_IS_SPACE(aString[wsBegin - 1])) {
- --wsBegin;
- }
- result.Append(nsDependentSubstring(aString, offset, wsBegin - offset));
- offset = nextCRLF + 1;
- while (offset < aString.Length() && NS_IS_SPACE(aString[offset])) {
- ++offset;
- }
- }
- aString = result;
- break;
- }
- case nsIPlaintextEditor::eNewlinesPasteIntact:
- // even if we're pasting newlines, don't paste leading/trailing ones
- aString.Trim(CRLF, true, true);
- break;
- }
- }
- nsresult
- TextEditRules::WillInsertText(EditAction aAction,
- Selection* aSelection,
- bool* aCancel,
- bool* aHandled,
- const nsAString* inString,
- nsAString* outString,
- int32_t aMaxLength)
- {
- if (!aSelection || !aCancel || !aHandled) {
- return NS_ERROR_NULL_POINTER;
- }
- if (inString->IsEmpty() && aAction != EditAction::insertIMEText) {
- // HACK: this is a fix for bug 19395
- // I can't outlaw all empty insertions
- // because IME transaction depend on them
- // There is more work to do to make the
- // world safe for IME.
- *aCancel = true;
- *aHandled = false;
- return NS_OK;
- }
- // initialize out param
- *aCancel = false;
- *aHandled = true;
- // handle docs with a max length
- // NOTE, this function copies inString into outString for us.
- bool truncated = false;
- nsresult rv = TruncateInsertionIfNeeded(aSelection, inString, outString,
- aMaxLength, &truncated);
- NS_ENSURE_SUCCESS(rv, rv);
- // If we're exceeding the maxlength when composing IME, we need to clean up
- // the composing text, so we shouldn't return early.
- if (truncated && outString->IsEmpty() &&
- aAction != EditAction::insertIMEText) {
- *aCancel = true;
- return NS_OK;
- }
- int32_t start = 0;
- int32_t end = 0;
- // handle password field docs
- if (IsPasswordEditor()) {
- NS_ENSURE_STATE(mTextEditor);
- nsContentUtils::GetSelectionInTextControl(aSelection,
- mTextEditor->GetRoot(),
- start, end);
- }
- // if the selection isn't collapsed, delete it.
- bool bCollapsed;
- rv = aSelection->GetIsCollapsed(&bCollapsed);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!bCollapsed) {
- NS_ENSURE_STATE(mTextEditor);
- rv = mTextEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- WillInsert(*aSelection, aCancel);
- // initialize out param
- // we want to ignore result of WillInsert()
- *aCancel = false;
- // handle password field data
- // this has the side effect of changing all the characters in aOutString
- // to the replacement character
- if (IsPasswordEditor() &&
- aAction == EditAction::insertIMEText) {
- RemoveIMETextFromPWBuf(start, outString);
- }
- // People have lots of different ideas about what text fields
- // should do with multiline pastes. See bugs 21032, 23485, 23485, 50935.
- // The six possible options are:
- // 0. paste newlines intact
- // 1. paste up to the first newline (default)
- // 2. replace newlines with spaces
- // 3. strip newlines
- // 4. replace with commas
- // 5. strip newlines and surrounding whitespace
- // So find out what we're expected to do:
- if (IsSingleLineEditor()) {
- nsAutoString tString(*outString);
- NS_ENSURE_STATE(mTextEditor);
- HandleNewLines(tString, mTextEditor->mNewlineHandling);
- outString->Assign(tString);
- }
- if (IsPasswordEditor()) {
- // manage the password buffer
- mPasswordText.Insert(*outString, start);
- if (LookAndFeel::GetEchoPassword() && !DontEchoPassword()) {
- HideLastPWInput();
- mLastStart = start;
- mLastLength = outString->Length();
- if (mTimer) {
- mTimer->Cancel();
- } else {
- mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- mTimer->InitWithCallback(this, LookAndFeel::GetPasswordMaskDelay(),
- nsITimer::TYPE_ONE_SHOT);
- } else {
- FillBufWithPWChars(outString, outString->Length());
- }
- }
- // get the (collapsed) selection location
- NS_ENSURE_STATE(aSelection->GetRangeAt(0));
- nsCOMPtr<nsINode> selNode = aSelection->GetRangeAt(0)->GetStartParent();
- int32_t selOffset = aSelection->GetRangeAt(0)->StartOffset();
- NS_ENSURE_STATE(selNode);
- // don't put text in places that can't have it
- NS_ENSURE_STATE(mTextEditor);
- if (!mTextEditor->IsTextNode(selNode) &&
- !mTextEditor->CanContainTag(*selNode, *nsGkAtoms::textTagName)) {
- return NS_ERROR_FAILURE;
- }
- // we need to get the doc
- NS_ENSURE_STATE(mTextEditor);
- nsCOMPtr<nsIDocument> doc = mTextEditor->GetDocument();
- NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
- if (aAction == EditAction::insertIMEText) {
- NS_ENSURE_STATE(mTextEditor);
- // Find better insertion point to insert text.
- mTextEditor->FindBetterInsertionPoint(selNode, selOffset);
- // If there is one or more IME selections, its minimum offset should be
- // the insertion point.
- int32_t IMESelectionOffset =
- mTextEditor->GetIMESelectionStartOffsetIn(selNode);
- if (IMESelectionOffset >= 0) {
- selOffset = IMESelectionOffset;
- }
- rv = mTextEditor->InsertTextImpl(*outString, address_of(selNode),
- &selOffset, doc);
- NS_ENSURE_SUCCESS(rv, rv);
- } else {
- // aAction == EditAction::insertText; find where we are
- nsCOMPtr<nsINode> curNode = selNode;
- int32_t curOffset = selOffset;
- // don't spaz my selection in subtransactions
- NS_ENSURE_STATE(mTextEditor);
- AutoTransactionsConserveSelection dontSpazMySelection(mTextEditor);
- rv = mTextEditor->InsertTextImpl(*outString, address_of(curNode),
- &curOffset, doc);
- NS_ENSURE_SUCCESS(rv, rv);
- if (curNode) {
- // Make the caret attach to the inserted text, unless this text ends with a LF,
- // in which case make the caret attach to the next line.
- bool endsWithLF =
- !outString->IsEmpty() && outString->Last() == nsCRT::LF;
- aSelection->SetInterlinePosition(endsWithLF);
- aSelection->Collapse(curNode, curOffset);
- }
- }
- ASSERT_PASSWORD_LENGTHS_EQUAL()
- return NS_OK;
- }
- nsresult
- TextEditRules::DidInsertText(Selection* aSelection,
- nsresult aResult)
- {
- return DidInsert(aSelection, aResult);
- }
- nsresult
- TextEditRules::WillSetTextProperty(Selection* aSelection,
- bool* aCancel,
- bool* aHandled)
- {
- if (!aSelection || !aCancel || !aHandled) {
- return NS_ERROR_NULL_POINTER;
- }
- // XXX: should probably return a success value other than NS_OK that means "not allowed"
- if (IsPlaintextEditor()) {
- *aCancel = true;
- }
- return NS_OK;
- }
- nsresult
- TextEditRules::DidSetTextProperty(Selection* aSelection,
- nsresult aResult)
- {
- return NS_OK;
- }
- nsresult
- TextEditRules::WillRemoveTextProperty(Selection* aSelection,
- bool* aCancel,
- bool* aHandled)
- {
- if (!aSelection || !aCancel || !aHandled) {
- return NS_ERROR_NULL_POINTER;
- }
- // XXX: should probably return a success value other than NS_OK that means "not allowed"
- if (IsPlaintextEditor()) {
- *aCancel = true;
- }
- return NS_OK;
- }
- nsresult
- TextEditRules::DidRemoveTextProperty(Selection* aSelection,
- nsresult aResult)
- {
- return NS_OK;
- }
- nsresult
- TextEditRules::WillDeleteSelection(Selection* aSelection,
- nsIEditor::EDirection aCollapsedAction,
- bool* aCancel,
- bool* aHandled)
- {
- if (!aSelection || !aCancel || !aHandled) {
- return NS_ERROR_NULL_POINTER;
- }
- CANCEL_OPERATION_IF_READONLY_OR_DISABLED
- // initialize out param
- *aCancel = false;
- *aHandled = false;
- // if there is only bogus content, cancel the operation
- if (mBogusNode) {
- *aCancel = true;
- return NS_OK;
- }
- // If the current selection is empty (e.g the user presses backspace with
- // a collapsed selection), then we want to avoid sending the selectstart
- // event to the user, so we hide selection changes. However, we still
- // want to send a single selectionchange event to the document, so we
- // batch the selectionchange events, such that a single event fires after
- // the AutoHideSelectionChanges destructor has been run.
- SelectionBatcher selectionBatcher(aSelection);
- AutoHideSelectionChanges hideSelection(aSelection);
- nsAutoScriptBlocker scriptBlocker;
- if (IsPasswordEditor()) {
- NS_ENSURE_STATE(mTextEditor);
- nsresult rv =
- mTextEditor->ExtendSelectionForDelete(aSelection, &aCollapsedAction);
- NS_ENSURE_SUCCESS(rv, rv);
- // manage the password buffer
- int32_t start, end;
- nsContentUtils::GetSelectionInTextControl(aSelection,
- mTextEditor->GetRoot(),
- start, end);
- if (LookAndFeel::GetEchoPassword()) {
- HideLastPWInput();
- mLastStart = start;
- mLastLength = 0;
- if (mTimer) {
- mTimer->Cancel();
- }
- }
- // Collapsed selection.
- if (end == start) {
- // Deleting back.
- if (nsIEditor::ePrevious == aCollapsedAction && 0<start) {
- mPasswordText.Cut(start-1, 1);
- }
- // Deleting forward.
- else if (nsIEditor::eNext == aCollapsedAction) {
- mPasswordText.Cut(start, 1);
- }
- // Otherwise nothing to do for this collapsed selection.
- }
- // Extended selection.
- else {
- mPasswordText.Cut(start, end-start);
- }
- } else {
- nsCOMPtr<nsIDOMNode> startNode;
- int32_t startOffset;
- NS_ENSURE_STATE(mTextEditor);
- nsresult rv =
- mTextEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(startNode),
- &startOffset);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
- bool bCollapsed;
- rv = aSelection->GetIsCollapsed(&bCollapsed);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!bCollapsed) {
- return NS_OK;
- }
- // Test for distance between caret and text that will be deleted
- rv = CheckBidiLevelForDeletion(aSelection, startNode, startOffset,
- aCollapsedAction, aCancel);
- NS_ENSURE_SUCCESS(rv, rv);
- if (*aCancel) {
- return NS_OK;
- }
- NS_ENSURE_STATE(mTextEditor);
- rv = mTextEditor->ExtendSelectionForDelete(aSelection, &aCollapsedAction);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- NS_ENSURE_STATE(mTextEditor);
- nsresult rv =
- mTextEditor->DeleteSelectionImpl(aCollapsedAction, nsIEditor::eStrip);
- NS_ENSURE_SUCCESS(rv, rv);
- *aHandled = true;
- ASSERT_PASSWORD_LENGTHS_EQUAL()
- return NS_OK;
- }
- nsresult
- TextEditRules::DidDeleteSelection(Selection* aSelection,
- nsIEditor::EDirection aCollapsedAction,
- nsresult aResult)
- {
- nsCOMPtr<nsIDOMNode> startNode;
- int32_t startOffset;
- NS_ENSURE_STATE(mTextEditor);
- nsresult rv =
- mTextEditor->GetStartNodeAndOffset(aSelection,
- getter_AddRefs(startNode), &startOffset);
- NS_ENSURE_SUCCESS(rv, rv);
- NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
- // delete empty text nodes at selection
- if (mTextEditor->IsTextNode(startNode)) {
- nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(startNode);
- uint32_t strLength;
- rv = textNode->GetLength(&strLength);
- NS_ENSURE_SUCCESS(rv, rv);
- // are we in an empty text node?
- if (!strLength) {
- rv = mTextEditor->DeleteNode(startNode);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- }
- if (mDidExplicitlySetInterline) {
- return NS_OK;
- }
- // We prevent the caret from sticking on the left of prior BR
- // (i.e. the end of previous line) after this deletion. Bug 92124
- return aSelection->SetInterlinePosition(true);
- }
- nsresult
- TextEditRules::WillUndo(Selection* aSelection,
- bool* aCancel,
- bool* aHandled)
- {
- if (!aSelection || !aCancel || !aHandled) {
- return NS_ERROR_NULL_POINTER;
- }
- CANCEL_OPERATION_IF_READONLY_OR_DISABLED
- // initialize out param
- *aCancel = false;
- *aHandled = false;
- return NS_OK;
- }
- /**
- * The idea here is to see if the magic empty node has suddenly reappeared as
- * the result of the undo. If it has, set our state so we remember it.
- * There is a tradeoff between doing here and at redo, or doing it everywhere
- * else that might care. Since undo and redo are relatively rare, it makes
- * sense to take the (small) performance hit here.
- */
- nsresult
- TextEditRules::DidUndo(Selection* aSelection,
- nsresult aResult)
- {
- NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
- // If aResult is an error, we return it.
- NS_ENSURE_SUCCESS(aResult, aResult);
- NS_ENSURE_STATE(mTextEditor);
- dom::Element* theRoot = mTextEditor->GetRoot();
- NS_ENSURE_TRUE(theRoot, NS_ERROR_FAILURE);
- nsIContent* node = mTextEditor->GetLeftmostChild(theRoot);
- if (node && mTextEditor->IsMozEditorBogusNode(node)) {
- mBogusNode = do_QueryInterface(node);
- } else {
- mBogusNode = nullptr;
- }
- return aResult;
- }
- nsresult
- TextEditRules::WillRedo(Selection* aSelection,
- bool* aCancel,
- bool* aHandled)
- {
- if (!aSelection || !aCancel || !aHandled) {
- return NS_ERROR_NULL_POINTER;
- }
- CANCEL_OPERATION_IF_READONLY_OR_DISABLED
- // initialize out param
- *aCancel = false;
- *aHandled = false;
- return NS_OK;
- }
- nsresult
- TextEditRules::DidRedo(Selection* aSelection,
- nsresult aResult)
- {
- if (!aSelection) {
- return NS_ERROR_NULL_POINTER;
- }
- if (NS_FAILED(aResult)) {
- return aResult; // if aResult is an error, we return it.
- }
- NS_ENSURE_STATE(mTextEditor);
- nsCOMPtr<nsIDOMElement> theRoot = do_QueryInterface(mTextEditor->GetRoot());
- NS_ENSURE_TRUE(theRoot, NS_ERROR_FAILURE);
- nsCOMPtr<nsIDOMHTMLCollection> nodeList;
- nsresult rv = theRoot->GetElementsByTagName(NS_LITERAL_STRING("br"),
- getter_AddRefs(nodeList));
- NS_ENSURE_SUCCESS(rv, rv);
- if (nodeList) {
- uint32_t len;
- nodeList->GetLength(&len);
- if (len != 1) {
- // only in the case of one br could there be the bogus node
- mBogusNode = nullptr;
- return NS_OK;
- }
- nsCOMPtr<nsIDOMNode> node;
- nodeList->Item(0, getter_AddRefs(node));
- nsCOMPtr<nsIContent> content = do_QueryInterface(node);
- MOZ_ASSERT(content);
- if (mTextEditor->IsMozEditorBogusNode(content)) {
- mBogusNode = node;
- } else {
- mBogusNode = nullptr;
- }
- }
- return NS_OK;
- }
- nsresult
- TextEditRules::WillOutputText(Selection* aSelection,
- const nsAString* aOutputFormat,
- nsAString* aOutString,
- bool* aCancel,
- bool* aHandled)
- {
- // null selection ok
- if (!aOutString || !aOutputFormat || !aCancel || !aHandled) {
- return NS_ERROR_NULL_POINTER;
- }
- // initialize out param
- *aCancel = false;
- *aHandled = false;
- nsAutoString outputFormat(*aOutputFormat);
- ToLowerCase(outputFormat);
- if (outputFormat.EqualsLiteral("text/plain")) {
- // Only use these rules for plain text output.
- if (IsPasswordEditor()) {
- *aOutString = mPasswordText;
- *aHandled = true;
- } else if (mBogusNode) {
- // This means there's no content, so output null string.
- aOutString->Truncate();
- *aHandled = true;
- }
- }
- return NS_OK;
- }
- nsresult
- TextEditRules::DidOutputText(Selection* aSelection,
- nsresult aResult)
- {
- return NS_OK;
- }
- nsresult
- TextEditRules::RemoveRedundantTrailingBR()
- {
- // If the bogus node exists, we have no work to do
- if (mBogusNode) {
- return NS_OK;
- }
- // Likewise, nothing to be done if we could never have inserted a trailing br
- if (IsSingleLineEditor()) {
- return NS_OK;
- }
- NS_ENSURE_STATE(mTextEditor);
- RefPtr<dom::Element> body = mTextEditor->GetRoot();
- if (!body) {
- return NS_ERROR_NULL_POINTER;
- }
- uint32_t childCount = body->GetChildCount();
- if (childCount > 1) {
- // The trailing br is redundant if it is the only remaining child node
- return NS_OK;
- }
- RefPtr<nsIContent> child = body->GetFirstChild();
- if (!child || !child->IsElement()) {
- return NS_OK;
- }
- dom::Element* elem = child->AsElement();
- if (!TextEditUtils::IsMozBR(elem)) {
- return NS_OK;
- }
- // Rather than deleting this node from the DOM tree we should instead
- // morph this br into the bogus node
- elem->UnsetAttr(kNameSpaceID_None, nsGkAtoms::type, true);
- // set mBogusNode to be this <br>
- mBogusNode = do_QueryInterface(elem);
- // give it the bogus node attribute
- elem->SetAttr(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom,
- kMOZEditorBogusNodeValue, false);
- return NS_OK;
- }
- nsresult
- TextEditRules::CreateTrailingBRIfNeeded()
- {
- // but only if we aren't a single line edit field
- if (IsSingleLineEditor()) {
- return NS_OK;
- }
- NS_ENSURE_STATE(mTextEditor);
- dom::Element* body = mTextEditor->GetRoot();
- NS_ENSURE_TRUE(body, NS_ERROR_NULL_POINTER);
- nsIContent* lastChild = body->GetLastChild();
- // assuming CreateBogusNodeIfNeeded() has been called first
- NS_ENSURE_TRUE(lastChild, NS_ERROR_NULL_POINTER);
- if (!lastChild->IsHTMLElement(nsGkAtoms::br)) {
- AutoTransactionsConserveSelection dontSpazMySelection(mTextEditor);
- nsCOMPtr<nsIDOMNode> domBody = do_QueryInterface(body);
- return CreateMozBR(domBody, body->Length());
- }
- // Check to see if the trailing BR is a former bogus node - this will have
- // stuck around if we previously morphed a trailing node into a bogus node.
- if (!mTextEditor->IsMozEditorBogusNode(lastChild)) {
- return NS_OK;
- }
- // Morph it back to a mozBR
- lastChild->UnsetAttr(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom, false);
- lastChild->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
- NS_LITERAL_STRING("_moz"), true);
- return NS_OK;
- }
- nsresult
- TextEditRules::CreateBogusNodeIfNeeded(Selection* aSelection)
- {
- NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
- NS_ENSURE_TRUE(mTextEditor, NS_ERROR_NULL_POINTER);
- if (mBogusNode) {
- // Let's not create more than one, ok?
- return NS_OK;
- }
- // tell rules system to not do any post-processing
- AutoRules beginRulesSniffing(mTextEditor, EditAction::ignore,
- nsIEditor::eNone);
- nsCOMPtr<dom::Element> body = mTextEditor->GetRoot();
- if (!body) {
- // We don't even have a body yet, don't insert any bogus nodes at
- // this point.
- return NS_OK;
- }
- // Now we've got the body element. Iterate over the body element's children,
- // looking for editable content. If no editable content is found, insert the
- // bogus node.
- for (nsCOMPtr<nsIContent> bodyChild = body->GetFirstChild();
- bodyChild;
- bodyChild = bodyChild->GetNextSibling()) {
- if (mTextEditor->IsMozEditorBogusNode(bodyChild) ||
- !mTextEditor->IsEditable(body) || // XXX hoist out of the loop?
- mTextEditor->IsEditable(bodyChild) ||
- mTextEditor->IsBlockNode(bodyChild)) {
- return NS_OK;
- }
- }
- // Skip adding the bogus node if body is read-only.
- if (!mTextEditor->IsModifiableNode(body)) {
- return NS_OK;
- }
- // Create a br.
- nsCOMPtr<Element> newContent = mTextEditor->CreateHTMLContent(nsGkAtoms::br);
- NS_ENSURE_STATE(newContent);
- // set mBogusNode to be the newly created <br>
- mBogusNode = do_QueryInterface(newContent);
- NS_ENSURE_TRUE(mBogusNode, NS_ERROR_NULL_POINTER);
- // Give it a special attribute.
- newContent->SetAttr(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom,
- kMOZEditorBogusNodeValue, false);
- // Put the node in the document.
- nsCOMPtr<nsIDOMNode> bodyNode = do_QueryInterface(body);
- nsresult rv = mTextEditor->InsertNode(mBogusNode, bodyNode, 0);
- NS_ENSURE_SUCCESS(rv, rv);
- // Set selection.
- aSelection->CollapseNative(body, 0);
- return NS_OK;
- }
- nsresult
- TextEditRules::TruncateInsertionIfNeeded(Selection* aSelection,
- const nsAString* aInString,
- nsAString* aOutString,
- int32_t aMaxLength,
- bool* aTruncated)
- {
- if (!aSelection || !aInString || !aOutString) {
- return NS_ERROR_NULL_POINTER;
- }
- if (!aOutString->Assign(*aInString, mozilla::fallible)) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- if (aTruncated) {
- *aTruncated = false;
- }
- NS_ENSURE_STATE(mTextEditor);
- if (-1 != aMaxLength && IsPlaintextEditor() &&
- !mTextEditor->IsIMEComposing()) {
- // Get the current text length.
- // Get the length of inString.
- // Get the length of the selection.
- // If selection is collapsed, it is length 0.
- // Subtract the length of the selection from the len(doc)
- // since we'll delete the selection on insert.
- // This is resultingDocLength.
- // Get old length of IME composing string
- // which will be replaced by new one.
- // If (resultingDocLength) is at or over max, cancel the insert
- // If (resultingDocLength) + (length of input) > max,
- // set aOutString to subset of inString so length = max
- int32_t docLength;
- nsresult rv = mTextEditor->GetTextLength(&docLength);
- if (NS_FAILED(rv)) {
- return rv;
- }
- int32_t start, end;
- nsContentUtils::GetSelectionInTextControl(aSelection,
- mTextEditor->GetRoot(),
- start, end);
- TextComposition* composition = mTextEditor->GetComposition();
- int32_t oldCompStrLength = composition ? composition->String().Length() : 0;
- const int32_t selectionLength = end - start;
- const int32_t resultingDocLength = docLength - selectionLength - oldCompStrLength;
- if (resultingDocLength >= aMaxLength) {
- // This call is guaranteed to reduce the capacity of the string, so it
- // cannot cause an OOM.
- aOutString->Truncate();
- if (aTruncated) {
- *aTruncated = true;
- }
- } else {
- int32_t oldLength = aOutString->Length();
- if (oldLength + resultingDocLength > aMaxLength) {
- int32_t newLength = aMaxLength - resultingDocLength;
- MOZ_ASSERT(newLength > 0);
- char16_t newLastChar = aOutString->CharAt(newLength - 1);
- char16_t removingFirstChar = aOutString->CharAt(newLength);
- // Don't separate the string between a surrogate pair.
- if (NS_IS_HIGH_SURROGATE(newLastChar) &&
- NS_IS_LOW_SURROGATE(removingFirstChar)) {
- newLength--;
- }
- // XXX What should we do if we're removing IVS and its preceding
- // character won't be removed?
- // This call is guaranteed to reduce the capacity of the string, so it
- // cannot cause an OOM.
- aOutString->Truncate(newLength);
- if (aTruncated) {
- *aTruncated = true;
- }
- }
- }
- }
- return NS_OK;
- }
- void
- TextEditRules::ResetIMETextPWBuf()
- {
- mPasswordIMEText.Truncate();
- }
- void
- TextEditRules::RemoveIMETextFromPWBuf(int32_t& aStart,
- nsAString* aIMEString)
- {
- MOZ_ASSERT(aIMEString);
- // initialize PasswordIME
- if (mPasswordIMEText.IsEmpty()) {
- mPasswordIMEIndex = aStart;
- } else {
- // manage the password buffer
- mPasswordText.Cut(mPasswordIMEIndex, mPasswordIMEText.Length());
- aStart = mPasswordIMEIndex;
- }
- mPasswordIMEText.Assign(*aIMEString);
- }
- NS_IMETHODIMP
- TextEditRules::Notify(nsITimer* aTimer)
- {
- MOZ_ASSERT(mTimer);
- // Check whether our text editor's password flag was changed before this
- // "hide password character" timer actually fires.
- nsresult rv = IsPasswordEditor() ? HideLastPWInput() : NS_OK;
- ASSERT_PASSWORD_LENGTHS_EQUAL();
- mLastLength = 0;
- return rv;
- }
- nsresult
- TextEditRules::HideLastPWInput()
- {
- if (!mLastLength) {
- // Special case, we're trying to replace a range that no longer exists
- return NS_OK;
- }
- nsAutoString hiddenText;
- FillBufWithPWChars(&hiddenText, mLastLength);
- NS_ENSURE_STATE(mTextEditor);
- RefPtr<Selection> selection = mTextEditor->GetSelection();
- NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
- int32_t start, end;
- nsContentUtils::GetSelectionInTextControl(selection, mTextEditor->GetRoot(),
- start, end);
- nsCOMPtr<nsINode> selNode = GetTextNode(selection);
- NS_ENSURE_TRUE(selNode, NS_OK);
- selNode->GetAsText()->ReplaceData(mLastStart, mLastLength, hiddenText);
- selection->Collapse(selNode, start);
- if (start != end) {
- selection->Extend(selNode, end);
- }
- return NS_OK;
- }
- // static
- void
- TextEditRules::FillBufWithPWChars(nsAString* aOutString,
- int32_t aLength)
- {
- MOZ_ASSERT(aOutString);
- // change the output to the platform password character
- char16_t passwordChar = LookAndFeel::GetPasswordCharacter();
- aOutString->Truncate();
- for (int32_t i = 0; i < aLength; i++) {
- aOutString->Append(passwordChar);
- }
- }
- /**
- * CreateMozBR() puts a BR node with moz attribute at {inParent, inOffset}.
- */
- nsresult
- TextEditRules::CreateMozBR(nsIDOMNode* inParent,
- int32_t inOffset,
- nsIDOMNode** outBRNode)
- {
- NS_ENSURE_TRUE(inParent, NS_ERROR_NULL_POINTER);
- nsCOMPtr<nsIDOMNode> brNode;
- NS_ENSURE_STATE(mTextEditor);
- nsresult rv = mTextEditor->CreateBR(inParent, inOffset, address_of(brNode));
- NS_ENSURE_SUCCESS(rv, rv);
- // give it special moz attr
- nsCOMPtr<Element> brElem = do_QueryInterface(brNode);
- if (brElem) {
- rv = mTextEditor->SetAttribute(brElem, nsGkAtoms::type,
- NS_LITERAL_STRING("_moz"));
- NS_ENSURE_SUCCESS(rv, rv);
- }
- if (outBRNode) {
- brNode.forget(outBRNode);
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- TextEditRules::DocumentModified()
- {
- return NS_ERROR_NOT_IMPLEMENTED;
- }
- bool
- TextEditRules::IsPasswordEditor() const
- {
- return mTextEditor ? mTextEditor->IsPasswordEditor() : false;
- }
- bool
- TextEditRules::IsSingleLineEditor() const
- {
- return mTextEditor ? mTextEditor->IsSingleLineEditor() : false;
- }
- bool
- TextEditRules::IsPlaintextEditor() const
- {
- return mTextEditor ? mTextEditor->IsPlaintextEditor() : false;
- }
- bool
- TextEditRules::IsReadonly() const
- {
- return mTextEditor ? mTextEditor->IsReadonly() : false;
- }
- bool
- TextEditRules::IsDisabled() const
- {
- return mTextEditor ? mTextEditor->IsDisabled() : false;
- }
- bool
- TextEditRules::IsMailEditor() const
- {
- return mTextEditor ? mTextEditor->IsMailEditor() : false;
- }
- bool
- TextEditRules::DontEchoPassword() const
- {
- return mTextEditor ? mTextEditor->DontEchoPassword() : false;
- }
- } // namespace mozilla
|