nsresult nsTextEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBool *aHandled, PRInt32 aMaxLength) { if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; } CANCEL_OPERATION_IF_READONLY_OR_DISABLED *aHandled = PR_FALSE; if (IsSingleLineEditor()) { *aCancel = PR_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; PRBool didTruncate; nsresult res = TruncateInsertionIfNeeded(aSelection, &inString, &outString, aMaxLength, &didTruncate); NS_ENSURE_SUCCESS(res, res); if (didTruncate) { *aCancel = PR_TRUE; return NS_OK; } *aCancel = PR_FALSE; // if the selection isn't collapsed, delete it. PRBool bCollapsed; res = aSelection->GetIsCollapsed(&bCollapsed); NS_ENSURE_SUCCESS(res, res); if (!bCollapsed) { res = mEditor->DeleteSelection(nsIEditor::eNone); NS_ENSURE_SUCCESS(res, res); } res = WillInsert(aSelection, aCancel); NS_ENSURE_SUCCESS(res, res); // initialize out param // we want to ignore result of WillInsert() *aCancel = PR_FALSE; } 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.AsString(), &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::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; } uint32_t start = 0; uint32_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)->GetStartContainer(); int32_t selOffset = aSelection->GetRangeAt(0)->StartOffset(); NS_ENSURE_STATE(selNode); // don't put text in places that can't have it NS_ENSURE_STATE(mTextEditor); if (!EditorBase::IsTextNode(selNode) && !mTextEditor->CanContainTag(*selNode, *nsGkAtoms::textTagName)) { return NS_ERROR_FAILURE; } // we need to get the doc NS_ENSURE_STATE(mTextEditor); nsCOMPtr<nsIDocument> doc = mTextEditor->GetDocument(); NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED); if (aAction == EditAction::insertIMEText) { NS_ENSURE_STATE(mTextEditor); // Find better insertion point to insert text. mTextEditor->FindBetterInsertionPoint(selNode, selOffset); // If there is one or more IME selections, its minimum offset should be // the insertion point. int32_t IMESelectionOffset = mTextEditor->GetIMESelectionStartOffsetIn(selNode); if (IMESelectionOffset >= 0) { selOffset = IMESelectionOffset; } rv = mTextEditor->InsertTextImpl(*outString, address_of(selNode), &selOffset, doc); NS_ENSURE_SUCCESS(rv, rv); } else { // aAction == EditAction::insertText; find where we are nsCOMPtr<nsINode> curNode = selNode; int32_t curOffset = selOffset; // don't change my selection in subtransactions NS_ENSURE_STATE(mTextEditor); AutoTransactionsConserveSelection dontChangeMySelection(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 nsTextEditRules::WillInsertText(PRInt32 aAction, nsISelection *aSelection, PRBool *aCancel, PRBool *aHandled, const nsAString *inString, nsAString *outString, PRInt32 aMaxLength) { if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; } if (inString->IsEmpty() && (aAction != kInsertTextIME)) { // 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 = PR_TRUE; *aHandled = PR_FALSE; return NS_OK; } // initialize out param *aCancel = PR_FALSE; *aHandled = PR_TRUE; // handle docs with a max length // NOTE, this function copies inString into outString for us. PRBool truncated = PR_FALSE; nsresult res = TruncateInsertionIfNeeded(aSelection, inString, outString, aMaxLength, &truncated); NS_ENSURE_SUCCESS(res, res); if (truncated && outString->IsEmpty()) { *aCancel = PR_TRUE; return NS_OK; } PRUint32 start = 0; PRUint32 end = 0; // handle password field docs if (IsPasswordEditor()) { res = mEditor->GetTextSelectionOffsets(aSelection, start, end); NS_ASSERTION((NS_SUCCEEDED(res)), "getTextSelectionOffsets failed!"); NS_ENSURE_SUCCESS(res, res); } // if the selection isn't collapsed, delete it. PRBool bCollapsed; res = aSelection->GetIsCollapsed(&bCollapsed); NS_ENSURE_SUCCESS(res, res); if (!bCollapsed) { res = mEditor->DeleteSelection(nsIEditor::eNone); NS_ENSURE_SUCCESS(res, res); } res = WillInsert(aSelection, aCancel); NS_ENSURE_SUCCESS(res, res); // initialize out param // we want to ignore result of WillInsert() *aCancel = PR_FALSE; // handle password field data // this has the side effect of changing all the characters in aOutString // to the replacement character if (IsPasswordEditor()) { if (aAction == kInsertTextIME) { res = RemoveIMETextFromPWBuf(start, outString); NS_ENSURE_SUCCESS(res, res); } } // 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); HandleNewLines(tString, mEditor->mNewlineHandling); outString->Assign(tString); } if (IsPasswordEditor()) { // manage the password buffer mPasswordText.Insert(*outString, start); nsCOMPtr<nsILookAndFeel> lookAndFeel = do_GetService(kLookAndFeelCID); if (lookAndFeel->GetEchoPassword() && !DontEchoPassword()) { HideLastPWInput(); mLastStart = start; mLastLength = outString->Length(); if (mTimer) { mTimer->Cancel(); } else { mTimer = do_CreateInstance("@mozilla.org/timer;1", &res); NS_ENSURE_SUCCESS(res, res); } mTimer->InitWithCallback(this, 600, nsITimer::TYPE_ONE_SHOT); } else { res = FillBufWithPWChars(outString, outString->Length()); NS_ENSURE_SUCCESS(res, res); } } // get the (collapsed) selection location nsCOMPtr<nsIDOMNode> selNode; PRInt32 selOffset; res = mEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset); NS_ENSURE_SUCCESS(res, res); // don't put text in places that can't have it if (!mEditor->IsTextNode(selNode) && !mEditor->CanContainTag(selNode, NS_LITERAL_STRING("#text"))) return NS_ERROR_FAILURE; // we need to get the doc nsCOMPtr<nsIDOMDocument>doc; res = mEditor->GetDocument(getter_AddRefs(doc)); NS_ENSURE_SUCCESS(res, res); NS_ENSURE_TRUE(doc, NS_ERROR_NULL_POINTER); if (aAction == kInsertTextIME) { res = mEditor->InsertTextImpl(*outString, address_of(selNode), &selOffset, doc); NS_ENSURE_SUCCESS(res, res); } else // aAction == kInsertText { // find where we are nsCOMPtr<nsIDOMNode> curNode = selNode; PRInt32 curOffset = selOffset; // don't spaz my selection in subtransactions nsAutoTxnsConserveSelection dontSpazMySelection(mEditor); res = mEditor->InsertTextImpl(*outString, address_of(curNode), &curOffset, doc); NS_ENSURE_SUCCESS(res, res); 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. PRBool endsWithLF = !outString->IsEmpty() && outString->Last() == nsCRT::LF; nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(aSelection)); selPrivate->SetInterlinePosition(endsWithLF); aSelection->Collapse(curNode, curOffset); } } ASSERT_PASSWORD_LENGTHS_EQUAL() return res; }
nsresult nsTextEditRules::WillInsertText(PRInt32 aAction, nsISelection *aSelection, PRBool *aCancel, PRBool *aHandled, const nsAString *inString, nsAString *outString, PRInt32 aMaxLength) { if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; } if (inString->IsEmpty() && (aAction != kInsertTextIME)) { // 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 = PR_TRUE; *aHandled = PR_FALSE; return NS_OK; } // initialize out param *aCancel = PR_FALSE; *aHandled = PR_TRUE; // handle docs with a max length // NOTE, this function copies inString into outString for us. nsresult res = TruncateInsertionIfNeeded(aSelection, inString, outString, aMaxLength); NS_ENSURE_SUCCESS(res, res); PRUint32 start = 0; PRUint32 end = 0; // handle password field docs if (IsPasswordEditor()) { res = mEditor->GetTextSelectionOffsets(aSelection, start, end); NS_ASSERTION((NS_SUCCEEDED(res)), "getTextSelectionOffsets failed!"); NS_ENSURE_SUCCESS(res, res); } // if the selection isn't collapsed, delete it. PRBool bCollapsed; res = aSelection->GetIsCollapsed(&bCollapsed); NS_ENSURE_SUCCESS(res, res); if (!bCollapsed) { res = mEditor->DeleteSelection(nsIEditor::eNone); NS_ENSURE_SUCCESS(res, res); } res = WillInsert(aSelection, aCancel); NS_ENSURE_SUCCESS(res, res); // initialize out param // we want to ignore result of WillInsert() *aCancel = PR_FALSE; // handle password field data // this has the side effect of changing all the characters in aOutString // to the replacement character if (IsPasswordEditor()) { if (aAction == kInsertTextIME) { res = RemoveIMETextFromPWBuf(start, outString); NS_ENSURE_SUCCESS(res, res); } } // 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); HandleNewLines(tString, mEditor->mNewlineHandling); outString->Assign(tString); } if (IsPasswordEditor()) { // manage the password buffer mPasswordText.Insert(*outString, start); nsCOMPtr<nsILookAndFeel> lookAndFeel = do_GetService(kLookAndFeelCID); if (lookAndFeel->GetEchoPassword() && !DontEchoPassword()) { HideLastPWInput(); mLastStart = start; mLastLength = outString->Length(); if (mTimer) { mTimer->Cancel(); } else { mTimer = do_CreateInstance("@mozilla.org/timer;1", &res); NS_ENSURE_SUCCESS(res, res); } mTimer->InitWithCallback(this, 600, nsITimer::TYPE_ONE_SHOT); } else { res = FillBufWithPWChars(outString, outString->Length()); NS_ENSURE_SUCCESS(res, res); } } // get the (collapsed) selection location nsCOMPtr<nsIDOMNode> selNode; PRInt32 selOffset; res = mEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset); NS_ENSURE_SUCCESS(res, res); // don't put text in places that can't have it if (!mEditor->IsTextNode(selNode) && !mEditor->CanContainTag(selNode, NS_LITERAL_STRING("#text"))) return NS_ERROR_FAILURE; // we need to get the doc nsCOMPtr<nsIDOMDocument>doc; res = mEditor->GetDocument(getter_AddRefs(doc)); NS_ENSURE_SUCCESS(res, res); NS_ENSURE_TRUE(doc, NS_ERROR_NULL_POINTER); if (aAction == kInsertTextIME) { res = mEditor->InsertTextImpl(*outString, address_of(selNode), &selOffset, doc); NS_ENSURE_SUCCESS(res, res); } else // aAction == kInsertText { // find where we are nsCOMPtr<nsIDOMNode> curNode = selNode; PRInt32 curOffset = selOffset; // is our text going to be PREformatted? // We remember this so that we know how to handle tabs. PRBool isPRE; res = mEditor->IsPreformatted(selNode, &isPRE); NS_ENSURE_SUCCESS(res, res); // don't spaz my selection in subtransactions nsAutoTxnsConserveSelection dontSpazMySelection(mEditor); nsString tString(*outString); const PRUnichar *unicodeBuf = tString.get(); nsCOMPtr<nsIDOMNode> unused; PRInt32 pos = 0; // for efficiency, break out the pre case separately. This is because // it's a lot cheaper to search the input string for only newlines than // it is to search for both tabs and newlines. if (isPRE) { while (unicodeBuf && (pos != -1) && ((PRUint32)pos < tString.Length())) { PRInt32 oldPos = pos; PRInt32 subStrLen; pos = tString.FindChar(nsCRT::LF, oldPos); if (pos != -1) { subStrLen = pos - oldPos; // if first char is newline, then use just it if (subStrLen == 0) subStrLen = 1; } else { subStrLen = tString.Length() - oldPos; pos = tString.Length(); } nsDependentSubstring subStr(tString, oldPos, subStrLen); // is it a return? if (subStr.EqualsLiteral(LFSTR)) { if (IsSingleLineEditor()) { NS_ASSERTION((mEditor->mNewlineHandling == nsIPlaintextEditor::eNewlinesPasteIntact), "Newline improperly getting into single-line edit field!"); res = mEditor->InsertTextImpl(subStr, address_of(curNode), &curOffset, doc); } else { res = mEditor->CreateBRImpl(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone); // If the newline is the last character in the string, and the BR we // just inserted is the last node in the content tree, we need to add // a mozBR so that a blank line is created. if (NS_SUCCEEDED(res) && curNode && pos == (PRInt32)(tString.Length() - 1)) { nsCOMPtr<nsIDOMNode> nextChild = mEditor->GetChildAt(curNode, curOffset); if (!nextChild) { // We must be at the end since there isn't a nextChild. // // curNode and curOffset should be set to the position after // the BR we added above, so just create a mozBR at that position. // // Note that we don't update curOffset after we've created/inserted // the mozBR since we never want the selection to be placed after it. res = CreateMozBR(curNode, curOffset, address_of(unused)); } } } pos++; } else { res = mEditor->InsertTextImpl(subStr, address_of(curNode), &curOffset, doc); } NS_ENSURE_SUCCESS(res, res); } } else { char specialChars[] = {TAB, nsCRT::LF, 0}; while (unicodeBuf && (pos != -1) && ((PRUint32)pos < tString.Length())) { PRInt32 oldPos = pos; PRInt32 subStrLen; pos = tString.FindCharInSet(specialChars, oldPos); if (pos != -1) { subStrLen = pos - oldPos; // if first char is newline, then use just it if (subStrLen == 0) subStrLen = 1; } else { subStrLen = tString.Length() - oldPos; pos = tString.Length(); } nsDependentSubstring subStr(tString, oldPos, subStrLen); // is it a tab? if (subStr.EqualsLiteral("\t")) { res = mEditor->InsertTextImpl(NS_LITERAL_STRING(" "), address_of(curNode), &curOffset, doc); pos++; } // is it a return? else if (subStr.EqualsLiteral(LFSTR)) { res = mEditor->CreateBRImpl(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone); pos++; } else { res = mEditor->InsertTextImpl(subStr, address_of(curNode), &curOffset, doc); } NS_ENSURE_SUCCESS(res, res); } } outString->Assign(tString); if (curNode) { aSelection->Collapse(curNode, curOffset); // 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. PRBool endsWithLF = !tString.IsEmpty() && tString.get()[tString.Length() - 1] == nsCRT::LF; nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(aSelection)); selPrivate->SetInterlinePosition(endsWithLF); } } ASSERT_PASSWORD_LENGTHS_EQUAL() return res; }