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 dontChangeMySelection(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 nsTextEditRules::CreateTrailingBRIfNeeded() { // but only if we aren't a single line edit field if (IsSingleLineEditor()) return NS_OK; nsIDOMNode *body = mEditor->GetRoot(); NS_ENSURE_TRUE(body, NS_ERROR_NULL_POINTER); nsCOMPtr<nsIDOMNode> lastChild; nsresult res = body->GetLastChild(getter_AddRefs(lastChild)); // assuming CreateBogusNodeIfNeeded() has been called first NS_ENSURE_SUCCESS(res, res); NS_ENSURE_TRUE(lastChild, NS_ERROR_NULL_POINTER); if (!nsTextEditUtils::IsBreak(lastChild)) { nsAutoTxnsConserveSelection dontSpazMySelection(mEditor); PRUint32 rootLen; res = mEditor->GetLengthOfDOMNode(body, rootLen); NS_ENSURE_SUCCESS(res, res); nsCOMPtr<nsIDOMNode> unused; res = CreateMozBR(body, rootLen, address_of(unused)); } return res; }
nsresult nsTextEditRules::CreateTrailingBRIfNeeded() { // but only if we aren't a single line edit field if (mFlags & nsIPlaintextEditor::eEditorSingleLineMask) return NS_OK; nsIDOMNode *body = mEditor->GetRoot(); if (!body) return NS_ERROR_NULL_POINTER; nsCOMPtr<nsIDOMNode> lastChild; nsresult res = body->GetLastChild(getter_AddRefs(lastChild)); // assuming CreateBogusNodeIfNeeded() has been called first if (NS_FAILED(res)) return res; if (!lastChild) return NS_ERROR_NULL_POINTER; if (!nsTextEditUtils::IsBreak(lastChild)) { nsAutoTxnsConserveSelection dontSpazMySelection(mEditor); PRUint32 rootLen; res = mEditor->GetLengthOfDOMNode(body, rootLen); if (NS_FAILED(res)) return res; nsCOMPtr<nsIDOMNode> unused; res = CreateMozBR(body, rootLen, address_of(unused)); } return res; }
nsresult nsTextEditRules::DidInsertBreak(nsISelection *aSelection, nsresult aResult) { // 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; } // if we are at the end of the document, we need to insert // a special mozBR following the normal br, and then set the // selection to stick to the mozBR. PRInt32 selOffset; nsCOMPtr<nsIDOMNode> selNode; nsresult res; res = mEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset); NS_ENSURE_SUCCESS(res, res); // confirm we are at end of document if (selOffset == 0) return NS_OK; // can't be after a br if we are at offset 0 nsIDOMElement *rootElem = mEditor->GetRoot(); nsCOMPtr<nsIDOMNode> root = do_QueryInterface(rootElem); NS_ENSURE_TRUE(root, NS_ERROR_NULL_POINTER); if (selNode != root) return NS_OK; // must be inside text node or somewhere other than end of root nsCOMPtr<nsIDOMNode> temp = mEditor->GetChildAt(selNode, selOffset); if (temp) return NS_OK; // can't be at end if there is a node after us. nsCOMPtr<nsIDOMNode> nearNode = mEditor->GetChildAt(selNode, selOffset-1); if (nearNode && nsTextEditUtils::IsBreak(nearNode) && !nsTextEditUtils::IsMozBR(nearNode)) { nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(aSelection)); // need to insert special moz BR. Why? Because if we don't // the user will see no new line for the break. Also, things // like table cells won't grow in height. nsCOMPtr<nsIDOMNode> brNode; res = CreateMozBR(selNode, selOffset, address_of(brNode)); NS_ENSURE_SUCCESS(res, res); res = nsEditor::GetNodeLocation(brNode, address_of(selNode), &selOffset); NS_ENSURE_SUCCESS(res, res); selPrivate->SetInterlinePosition(PR_TRUE); res = aSelection->Collapse(selNode, selOffset); NS_ENSURE_SUCCESS(res, res); } 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; }