1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/editor/libeditor/text/nsPlaintextEditor.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1614 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 + 1.10 +#include "mozilla/Assertions.h" 1.11 +#include "mozilla/Preferences.h" 1.12 +#include "mozilla/dom/Selection.h" 1.13 +#include "mozilla/TextComposition.h" 1.14 +#include "mozilla/TextEvents.h" 1.15 +#include "mozilla/dom/Element.h" 1.16 +#include "mozilla/mozalloc.h" 1.17 +#include "nsAString.h" 1.18 +#include "nsAutoPtr.h" 1.19 +#include "nsCRT.h" 1.20 +#include "nsCaret.h" 1.21 +#include "nsCharTraits.h" 1.22 +#include "nsComponentManagerUtils.h" 1.23 +#include "nsContentCID.h" 1.24 +#include "nsCopySupport.h" 1.25 +#include "nsDebug.h" 1.26 +#include "nsDependentSubstring.h" 1.27 +#include "nsEditRules.h" 1.28 +#include "nsEditorUtils.h" // nsAutoEditBatch, nsAutoRules 1.29 +#include "nsError.h" 1.30 +#include "nsGkAtoms.h" 1.31 +#include "nsIClipboard.h" 1.32 +#include "nsIContent.h" 1.33 +#include "nsIContentIterator.h" 1.34 +#include "nsIDOMCharacterData.h" 1.35 +#include "nsIDOMDocument.h" 1.36 +#include "nsIDOMElement.h" 1.37 +#include "nsIDOMEventTarget.h" 1.38 +#include "nsIDOMKeyEvent.h" 1.39 +#include "nsIDOMNode.h" 1.40 +#include "nsIDOMNodeList.h" 1.41 +#include "nsIDocumentEncoder.h" 1.42 +#include "nsIEditorIMESupport.h" 1.43 +#include "nsNameSpaceManager.h" 1.44 +#include "nsINode.h" 1.45 +#include "nsIPresShell.h" 1.46 +#include "nsISelection.h" 1.47 +#include "nsISelectionController.h" 1.48 +#include "nsISelectionPrivate.h" 1.49 +#include "nsISupportsPrimitives.h" 1.50 +#include "nsITransferable.h" 1.51 +#include "nsIWeakReferenceUtils.h" 1.52 +#include "nsInternetCiter.h" 1.53 +#include "nsLiteralString.h" 1.54 +#include "nsPlaintextEditor.h" 1.55 +#include "nsReadableUtils.h" 1.56 +#include "nsServiceManagerUtils.h" 1.57 +#include "nsString.h" 1.58 +#include "nsStringFwd.h" 1.59 +#include "nsSubstringTuple.h" 1.60 +#include "nsTextEditRules.h" 1.61 +#include "nsTextEditUtils.h" 1.62 +#include "nsUnicharUtils.h" 1.63 +#include "nsXPCOM.h" 1.64 + 1.65 +class nsIOutputStream; 1.66 +class nsISupports; 1.67 +class nsISupportsArray; 1.68 + 1.69 +using namespace mozilla; 1.70 +using namespace mozilla::dom; 1.71 + 1.72 +nsPlaintextEditor::nsPlaintextEditor() 1.73 +: nsEditor() 1.74 +, mRules(nullptr) 1.75 +, mWrapToWindow(false) 1.76 +, mWrapColumn(0) 1.77 +, mMaxTextLength(-1) 1.78 +, mInitTriggerCounter(0) 1.79 +, mNewlineHandling(nsIPlaintextEditor::eNewlinesPasteToFirst) 1.80 +#ifdef XP_WIN 1.81 +, mCaretStyle(1) 1.82 +#else 1.83 +, mCaretStyle(0) 1.84 +#endif 1.85 +{ 1.86 + // check the "single line editor newline handling" 1.87 + // and "caret behaviour in selection" prefs 1.88 + GetDefaultEditorPrefs(mNewlineHandling, mCaretStyle); 1.89 +} 1.90 + 1.91 +nsPlaintextEditor::~nsPlaintextEditor() 1.92 +{ 1.93 + // Remove event listeners. Note that if we had an HTML editor, 1.94 + // it installed its own instead of these 1.95 + RemoveEventListeners(); 1.96 + 1.97 + if (mRules) 1.98 + mRules->DetachEditor(); 1.99 +} 1.100 + 1.101 +NS_IMPL_CYCLE_COLLECTION_CLASS(nsPlaintextEditor) 1.102 + 1.103 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsPlaintextEditor, nsEditor) 1.104 + if (tmp->mRules) 1.105 + tmp->mRules->DetachEditor(); 1.106 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mRules) 1.107 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.108 + 1.109 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsPlaintextEditor, nsEditor) 1.110 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRules) 1.111 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.112 + 1.113 +NS_IMPL_ADDREF_INHERITED(nsPlaintextEditor, nsEditor) 1.114 +NS_IMPL_RELEASE_INHERITED(nsPlaintextEditor, nsEditor) 1.115 + 1.116 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsPlaintextEditor) 1.117 + NS_INTERFACE_MAP_ENTRY(nsIPlaintextEditor) 1.118 + NS_INTERFACE_MAP_ENTRY(nsIEditorMailSupport) 1.119 +NS_INTERFACE_MAP_END_INHERITING(nsEditor) 1.120 + 1.121 + 1.122 +NS_IMETHODIMP nsPlaintextEditor::Init(nsIDOMDocument *aDoc, 1.123 + nsIContent *aRoot, 1.124 + nsISelectionController *aSelCon, 1.125 + uint32_t aFlags, 1.126 + const nsAString& aInitialValue) 1.127 +{ 1.128 + NS_PRECONDITION(aDoc, "bad arg"); 1.129 + NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER); 1.130 + 1.131 + nsresult res = NS_OK, rulesRes = NS_OK; 1.132 + if (mRules) { 1.133 + mRules->DetachEditor(); 1.134 + } 1.135 + 1.136 + { 1.137 + // block to scope nsAutoEditInitRulesTrigger 1.138 + nsAutoEditInitRulesTrigger rulesTrigger(this, rulesRes); 1.139 + 1.140 + // Init the base editor 1.141 + res = nsEditor::Init(aDoc, aRoot, aSelCon, aFlags, aInitialValue); 1.142 + } 1.143 + 1.144 + NS_ENSURE_SUCCESS(rulesRes, rulesRes); 1.145 + 1.146 + // mRules may not have been initialized yet, when this is called via 1.147 + // nsHTMLEditor::Init. 1.148 + if (mRules) { 1.149 + mRules->SetInitialValue(aInitialValue); 1.150 + } 1.151 + 1.152 + return res; 1.153 +} 1.154 + 1.155 +static int32_t sNewlineHandlingPref = -1, 1.156 + sCaretStylePref = -1; 1.157 + 1.158 +static void 1.159 +EditorPrefsChangedCallback(const char *aPrefName, void *) 1.160 +{ 1.161 + if (nsCRT::strcmp(aPrefName, "editor.singleLine.pasteNewlines") == 0) { 1.162 + sNewlineHandlingPref = 1.163 + Preferences::GetInt("editor.singleLine.pasteNewlines", 1.164 + nsIPlaintextEditor::eNewlinesPasteToFirst); 1.165 + } else if (nsCRT::strcmp(aPrefName, "layout.selection.caret_style") == 0) { 1.166 + sCaretStylePref = Preferences::GetInt("layout.selection.caret_style", 1.167 +#ifdef XP_WIN 1.168 + 1); 1.169 + if (sCaretStylePref == 0) 1.170 + sCaretStylePref = 1; 1.171 +#else 1.172 + 0); 1.173 +#endif 1.174 + } 1.175 +} 1.176 + 1.177 +// static 1.178 +void 1.179 +nsPlaintextEditor::GetDefaultEditorPrefs(int32_t &aNewlineHandling, 1.180 + int32_t &aCaretStyle) 1.181 +{ 1.182 + if (sNewlineHandlingPref == -1) { 1.183 + Preferences::RegisterCallback(EditorPrefsChangedCallback, 1.184 + "editor.singleLine.pasteNewlines"); 1.185 + EditorPrefsChangedCallback("editor.singleLine.pasteNewlines", nullptr); 1.186 + Preferences::RegisterCallback(EditorPrefsChangedCallback, 1.187 + "layout.selection.caret_style"); 1.188 + EditorPrefsChangedCallback("layout.selection.caret_style", nullptr); 1.189 + } 1.190 + 1.191 + aNewlineHandling = sNewlineHandlingPref; 1.192 + aCaretStyle = sCaretStylePref; 1.193 +} 1.194 + 1.195 +void 1.196 +nsPlaintextEditor::BeginEditorInit() 1.197 +{ 1.198 + mInitTriggerCounter++; 1.199 +} 1.200 + 1.201 +nsresult 1.202 +nsPlaintextEditor::EndEditorInit() 1.203 +{ 1.204 + nsresult res = NS_OK; 1.205 + NS_PRECONDITION(mInitTriggerCounter > 0, "ended editor init before we began?"); 1.206 + mInitTriggerCounter--; 1.207 + if (mInitTriggerCounter == 0) 1.208 + { 1.209 + res = InitRules(); 1.210 + if (NS_SUCCEEDED(res)) { 1.211 + // Throw away the old transaction manager if this is not the first time that 1.212 + // we're initializing the editor. 1.213 + EnableUndo(false); 1.214 + EnableUndo(true); 1.215 + } 1.216 + } 1.217 + return res; 1.218 +} 1.219 + 1.220 +NS_IMETHODIMP 1.221 +nsPlaintextEditor::SetDocumentCharacterSet(const nsACString& characterSet) 1.222 +{ 1.223 + nsresult rv = nsEditor::SetDocumentCharacterSet(characterSet); 1.224 + NS_ENSURE_SUCCESS(rv, rv); 1.225 + 1.226 + // Update META charset element. 1.227 + nsCOMPtr<nsIDOMDocument> domdoc = GetDOMDocument(); 1.228 + NS_ENSURE_TRUE(domdoc, NS_ERROR_NOT_INITIALIZED); 1.229 + 1.230 + if (UpdateMetaCharset(domdoc, characterSet)) { 1.231 + return NS_OK; 1.232 + } 1.233 + 1.234 + nsCOMPtr<nsIDOMNodeList> headList; 1.235 + rv = domdoc->GetElementsByTagName(NS_LITERAL_STRING("head"), getter_AddRefs(headList)); 1.236 + NS_ENSURE_SUCCESS(rv, rv); 1.237 + NS_ENSURE_TRUE(headList, NS_OK); 1.238 + 1.239 + nsCOMPtr<nsIDOMNode> headNode; 1.240 + headList->Item(0, getter_AddRefs(headNode)); 1.241 + NS_ENSURE_TRUE(headNode, NS_OK); 1.242 + 1.243 + // Create a new meta charset tag 1.244 + nsCOMPtr<nsIDOMNode> resultNode; 1.245 + rv = CreateNode(NS_LITERAL_STRING("meta"), headNode, 0, getter_AddRefs(resultNode)); 1.246 + NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); 1.247 + NS_ENSURE_TRUE(resultNode, NS_OK); 1.248 + 1.249 + // Set attributes to the created element 1.250 + if (characterSet.IsEmpty()) { 1.251 + return NS_OK; 1.252 + } 1.253 + 1.254 + nsCOMPtr<dom::Element> metaElement = do_QueryInterface(resultNode); 1.255 + if (!metaElement) { 1.256 + return NS_OK; 1.257 + } 1.258 + 1.259 + // not undoable, undo should undo CreateNode 1.260 + metaElement->SetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, 1.261 + NS_LITERAL_STRING("Content-Type"), true); 1.262 + metaElement->SetAttr(kNameSpaceID_None, nsGkAtoms::content, 1.263 + NS_LITERAL_STRING("text/html;charset=") + 1.264 + NS_ConvertASCIItoUTF16(characterSet), 1.265 + true); 1.266 + return NS_OK; 1.267 +} 1.268 + 1.269 +bool 1.270 +nsPlaintextEditor::UpdateMetaCharset(nsIDOMDocument* aDocument, 1.271 + const nsACString& aCharacterSet) 1.272 +{ 1.273 + MOZ_ASSERT(aDocument); 1.274 + // get a list of META tags 1.275 + nsCOMPtr<nsIDOMNodeList> list; 1.276 + nsresult rv = aDocument->GetElementsByTagName(NS_LITERAL_STRING("meta"), 1.277 + getter_AddRefs(list)); 1.278 + NS_ENSURE_SUCCESS(rv, false); 1.279 + NS_ENSURE_TRUE(list, false); 1.280 + 1.281 + nsCOMPtr<nsINodeList> metaList = do_QueryInterface(list); 1.282 + 1.283 + uint32_t listLength = 0; 1.284 + metaList->GetLength(&listLength); 1.285 + 1.286 + for (uint32_t i = 0; i < listLength; ++i) { 1.287 + nsCOMPtr<nsIContent> metaNode = metaList->Item(i); 1.288 + MOZ_ASSERT(metaNode); 1.289 + 1.290 + if (!metaNode->IsElement()) { 1.291 + continue; 1.292 + } 1.293 + 1.294 + nsAutoString currentValue; 1.295 + metaNode->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, currentValue); 1.296 + 1.297 + if (!FindInReadable(NS_LITERAL_STRING("content-type"), 1.298 + currentValue, 1.299 + nsCaseInsensitiveStringComparator())) { 1.300 + continue; 1.301 + } 1.302 + 1.303 + metaNode->GetAttr(kNameSpaceID_None, nsGkAtoms::content, currentValue); 1.304 + 1.305 + NS_NAMED_LITERAL_STRING(charsetEquals, "charset="); 1.306 + nsAString::const_iterator originalStart, start, end; 1.307 + originalStart = currentValue.BeginReading(start); 1.308 + currentValue.EndReading(end); 1.309 + if (!FindInReadable(charsetEquals, start, end, 1.310 + nsCaseInsensitiveStringComparator())) { 1.311 + continue; 1.312 + } 1.313 + 1.314 + // set attribute to <original prefix> charset=text/html 1.315 + nsCOMPtr<nsIDOMElement> metaElement = do_QueryInterface(metaNode); 1.316 + MOZ_ASSERT(metaElement); 1.317 + rv = nsEditor::SetAttribute(metaElement, NS_LITERAL_STRING("content"), 1.318 + Substring(originalStart, start) + 1.319 + charsetEquals + 1.320 + NS_ConvertASCIItoUTF16(aCharacterSet)); 1.321 + return NS_SUCCEEDED(rv); 1.322 + } 1.323 + return false; 1.324 +} 1.325 + 1.326 +NS_IMETHODIMP nsPlaintextEditor::InitRules() 1.327 +{ 1.328 + if (!mRules) { 1.329 + // instantiate the rules for this text editor 1.330 + mRules = new nsTextEditRules(); 1.331 + } 1.332 + return mRules->Init(this); 1.333 +} 1.334 + 1.335 + 1.336 +NS_IMETHODIMP 1.337 +nsPlaintextEditor::GetIsDocumentEditable(bool *aIsDocumentEditable) 1.338 +{ 1.339 + NS_ENSURE_ARG_POINTER(aIsDocumentEditable); 1.340 + 1.341 + nsCOMPtr<nsIDOMDocument> doc = GetDOMDocument(); 1.342 + *aIsDocumentEditable = doc && IsModifiable(); 1.343 + 1.344 + return NS_OK; 1.345 +} 1.346 + 1.347 +bool nsPlaintextEditor::IsModifiable() 1.348 +{ 1.349 + return !IsReadonly(); 1.350 +} 1.351 + 1.352 +nsresult 1.353 +nsPlaintextEditor::HandleKeyPressEvent(nsIDOMKeyEvent* aKeyEvent) 1.354 +{ 1.355 + // NOTE: When you change this method, you should also change: 1.356 + // * editor/libeditor/text/tests/test_texteditor_keyevent_handling.html 1.357 + // * editor/libeditor/html/tests/test_htmleditor_keyevent_handling.html 1.358 + // 1.359 + // And also when you add new key handling, you need to change the subclass's 1.360 + // HandleKeyPressEvent()'s switch statement. 1.361 + 1.362 + if (IsReadonly() || IsDisabled()) { 1.363 + // When we're not editable, the events handled on nsEditor. 1.364 + return nsEditor::HandleKeyPressEvent(aKeyEvent); 1.365 + } 1.366 + 1.367 + WidgetKeyboardEvent* nativeKeyEvent = 1.368 + aKeyEvent->GetInternalNSEvent()->AsKeyboardEvent(); 1.369 + NS_ENSURE_TRUE(nativeKeyEvent, NS_ERROR_UNEXPECTED); 1.370 + NS_ASSERTION(nativeKeyEvent->message == NS_KEY_PRESS, 1.371 + "HandleKeyPressEvent gets non-keypress event"); 1.372 + 1.373 + switch (nativeKeyEvent->keyCode) { 1.374 + case nsIDOMKeyEvent::DOM_VK_META: 1.375 + case nsIDOMKeyEvent::DOM_VK_WIN: 1.376 + case nsIDOMKeyEvent::DOM_VK_SHIFT: 1.377 + case nsIDOMKeyEvent::DOM_VK_CONTROL: 1.378 + case nsIDOMKeyEvent::DOM_VK_ALT: 1.379 + case nsIDOMKeyEvent::DOM_VK_BACK_SPACE: 1.380 + case nsIDOMKeyEvent::DOM_VK_DELETE: 1.381 + // These keys are handled on nsEditor 1.382 + return nsEditor::HandleKeyPressEvent(aKeyEvent); 1.383 + case nsIDOMKeyEvent::DOM_VK_TAB: { 1.384 + if (IsTabbable()) { 1.385 + return NS_OK; // let it be used for focus switching 1.386 + } 1.387 + 1.388 + if (nativeKeyEvent->IsShift() || nativeKeyEvent->IsControl() || 1.389 + nativeKeyEvent->IsAlt() || nativeKeyEvent->IsMeta() || 1.390 + nativeKeyEvent->IsOS()) { 1.391 + return NS_OK; 1.392 + } 1.393 + 1.394 + // else we insert the tab straight through 1.395 + aKeyEvent->PreventDefault(); 1.396 + return TypedText(NS_LITERAL_STRING("\t"), eTypedText); 1.397 + } 1.398 + case nsIDOMKeyEvent::DOM_VK_RETURN: 1.399 + if (IsSingleLineEditor() || nativeKeyEvent->IsControl() || 1.400 + nativeKeyEvent->IsAlt() || nativeKeyEvent->IsMeta() || 1.401 + nativeKeyEvent->IsOS()) { 1.402 + return NS_OK; 1.403 + } 1.404 + aKeyEvent->PreventDefault(); 1.405 + return TypedText(EmptyString(), eTypedBreak); 1.406 + } 1.407 + 1.408 + // NOTE: On some keyboard layout, some characters are inputted with Control 1.409 + // key or Alt key, but at that time, widget sets FALSE to these keys. 1.410 + if (nativeKeyEvent->charCode == 0 || nativeKeyEvent->IsControl() || 1.411 + nativeKeyEvent->IsAlt() || nativeKeyEvent->IsMeta() || 1.412 + nativeKeyEvent->IsOS()) { 1.413 + // we don't PreventDefault() here or keybindings like control-x won't work 1.414 + return NS_OK; 1.415 + } 1.416 + aKeyEvent->PreventDefault(); 1.417 + nsAutoString str(nativeKeyEvent->charCode); 1.418 + return TypedText(str, eTypedText); 1.419 +} 1.420 + 1.421 +/* This routine is needed to provide a bottleneck for typing for logging 1.422 + purposes. Can't use HandleKeyPress() (above) for that since it takes 1.423 + a nsIDOMKeyEvent* parameter. So instead we pass enough info through 1.424 + to TypedText() to determine what action to take, but without passing 1.425 + an event. 1.426 + */ 1.427 +NS_IMETHODIMP 1.428 +nsPlaintextEditor::TypedText(const nsAString& aString, ETypingAction aAction) 1.429 +{ 1.430 + nsAutoPlaceHolderBatch batch(this, nsGkAtoms::TypingTxnName); 1.431 + 1.432 + switch (aAction) { 1.433 + case eTypedText: 1.434 + return InsertText(aString); 1.435 + case eTypedBreak: 1.436 + return InsertLineBreak(); 1.437 + default: 1.438 + // eTypedBR is only for HTML 1.439 + return NS_ERROR_FAILURE; 1.440 + } 1.441 +} 1.442 + 1.443 +nsresult 1.444 +nsPlaintextEditor::CreateBRImpl(nsCOMPtr<nsIDOMNode>* aInOutParent, 1.445 + int32_t* aInOutOffset, 1.446 + nsCOMPtr<nsIDOMNode>* outBRNode, 1.447 + EDirection aSelect) 1.448 +{ 1.449 + NS_ENSURE_TRUE(aInOutParent && *aInOutParent && aInOutOffset && outBRNode, NS_ERROR_NULL_POINTER); 1.450 + *outBRNode = nullptr; 1.451 + nsresult res; 1.452 + 1.453 + // we need to insert a br. unfortunately, we may have to split a text node to do it. 1.454 + nsCOMPtr<nsIDOMNode> node = *aInOutParent; 1.455 + int32_t theOffset = *aInOutOffset; 1.456 + nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(node); 1.457 + NS_NAMED_LITERAL_STRING(brType, "br"); 1.458 + nsCOMPtr<nsIDOMNode> brNode; 1.459 + if (nodeAsText) 1.460 + { 1.461 + int32_t offset; 1.462 + uint32_t len; 1.463 + nodeAsText->GetLength(&len); 1.464 + nsCOMPtr<nsIDOMNode> tmp = GetNodeLocation(node, &offset); 1.465 + NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE); 1.466 + if (!theOffset) 1.467 + { 1.468 + // we are already set to go 1.469 + } 1.470 + else if (theOffset == (int32_t)len) 1.471 + { 1.472 + // update offset to point AFTER the text node 1.473 + offset++; 1.474 + } 1.475 + else 1.476 + { 1.477 + // split the text node 1.478 + res = SplitNode(node, theOffset, getter_AddRefs(tmp)); 1.479 + NS_ENSURE_SUCCESS(res, res); 1.480 + tmp = GetNodeLocation(node, &offset); 1.481 + } 1.482 + // create br 1.483 + res = CreateNode(brType, tmp, offset, getter_AddRefs(brNode)); 1.484 + NS_ENSURE_SUCCESS(res, res); 1.485 + *aInOutParent = tmp; 1.486 + *aInOutOffset = offset+1; 1.487 + } 1.488 + else 1.489 + { 1.490 + res = CreateNode(brType, node, theOffset, getter_AddRefs(brNode)); 1.491 + NS_ENSURE_SUCCESS(res, res); 1.492 + (*aInOutOffset)++; 1.493 + } 1.494 + 1.495 + *outBRNode = brNode; 1.496 + if (*outBRNode && (aSelect != eNone)) 1.497 + { 1.498 + int32_t offset; 1.499 + nsCOMPtr<nsIDOMNode> parent = GetNodeLocation(*outBRNode, &offset); 1.500 + 1.501 + nsCOMPtr<nsISelection> selection; 1.502 + res = GetSelection(getter_AddRefs(selection)); 1.503 + NS_ENSURE_SUCCESS(res, res); 1.504 + nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection)); 1.505 + if (aSelect == eNext) 1.506 + { 1.507 + // position selection after br 1.508 + selPriv->SetInterlinePosition(true); 1.509 + res = selection->Collapse(parent, offset+1); 1.510 + } 1.511 + else if (aSelect == ePrevious) 1.512 + { 1.513 + // position selection before br 1.514 + selPriv->SetInterlinePosition(true); 1.515 + res = selection->Collapse(parent, offset); 1.516 + } 1.517 + } 1.518 + return NS_OK; 1.519 +} 1.520 + 1.521 + 1.522 +NS_IMETHODIMP nsPlaintextEditor::CreateBR(nsIDOMNode *aNode, int32_t aOffset, nsCOMPtr<nsIDOMNode> *outBRNode, EDirection aSelect) 1.523 +{ 1.524 + nsCOMPtr<nsIDOMNode> parent = aNode; 1.525 + int32_t offset = aOffset; 1.526 + return CreateBRImpl(address_of(parent), &offset, outBRNode, aSelect); 1.527 +} 1.528 + 1.529 +nsresult 1.530 +nsPlaintextEditor::InsertBR(nsCOMPtr<nsIDOMNode>* outBRNode) 1.531 +{ 1.532 + NS_ENSURE_TRUE(outBRNode, NS_ERROR_NULL_POINTER); 1.533 + *outBRNode = nullptr; 1.534 + 1.535 + // calling it text insertion to trigger moz br treatment by rules 1.536 + nsAutoRules beginRulesSniffing(this, EditAction::insertText, nsIEditor::eNext); 1.537 + 1.538 + nsCOMPtr<nsISelection> selection; 1.539 + nsresult res = GetSelection(getter_AddRefs(selection)); 1.540 + NS_ENSURE_SUCCESS(res, res); 1.541 + 1.542 + if (!selection->Collapsed()) { 1.543 + res = DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip); 1.544 + NS_ENSURE_SUCCESS(res, res); 1.545 + } 1.546 + 1.547 + nsCOMPtr<nsIDOMNode> selNode; 1.548 + int32_t selOffset; 1.549 + res = GetStartNodeAndOffset(selection, getter_AddRefs(selNode), &selOffset); 1.550 + NS_ENSURE_SUCCESS(res, res); 1.551 + 1.552 + res = CreateBR(selNode, selOffset, outBRNode); 1.553 + NS_ENSURE_SUCCESS(res, res); 1.554 + 1.555 + // position selection after br 1.556 + selNode = GetNodeLocation(*outBRNode, &selOffset); 1.557 + nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection)); 1.558 + selPriv->SetInterlinePosition(true); 1.559 + return selection->Collapse(selNode, selOffset+1); 1.560 +} 1.561 + 1.562 +nsresult 1.563 +nsPlaintextEditor::ExtendSelectionForDelete(nsISelection *aSelection, 1.564 + nsIEditor::EDirection *aAction) 1.565 +{ 1.566 + nsresult result = NS_OK; 1.567 + 1.568 + bool bCollapsed = aSelection->Collapsed(); 1.569 + 1.570 + if (*aAction == eNextWord || *aAction == ePreviousWord 1.571 + || (*aAction == eNext && bCollapsed) 1.572 + || (*aAction == ePrevious && bCollapsed) 1.573 + || *aAction == eToBeginningOfLine || *aAction == eToEndOfLine) 1.574 + { 1.575 + nsCOMPtr<nsISelectionController> selCont; 1.576 + GetSelectionController(getter_AddRefs(selCont)); 1.577 + NS_ENSURE_TRUE(selCont, NS_ERROR_NO_INTERFACE); 1.578 + 1.579 + switch (*aAction) 1.580 + { 1.581 + case eNextWord: 1.582 + result = selCont->WordExtendForDelete(true); 1.583 + // DeleteSelectionImpl doesn't handle these actions 1.584 + // because it's inside batching, so don't confuse it: 1.585 + *aAction = eNone; 1.586 + break; 1.587 + case ePreviousWord: 1.588 + result = selCont->WordExtendForDelete(false); 1.589 + *aAction = eNone; 1.590 + break; 1.591 + case eNext: 1.592 + result = selCont->CharacterExtendForDelete(); 1.593 + // Don't set aAction to eNone (see Bug 502259) 1.594 + break; 1.595 + case ePrevious: { 1.596 + // Only extend the selection where the selection is after a UTF-16 1.597 + // surrogate pair. For other cases we don't want to do that, in order 1.598 + // to make sure that pressing backspace will only delete the last 1.599 + // typed character. 1.600 + nsCOMPtr<nsIDOMNode> node; 1.601 + int32_t offset; 1.602 + result = GetStartNodeAndOffset(aSelection, getter_AddRefs(node), &offset); 1.603 + NS_ENSURE_SUCCESS(result, result); 1.604 + NS_ENSURE_TRUE(node, NS_ERROR_FAILURE); 1.605 + 1.606 + if (IsTextNode(node)) { 1.607 + nsCOMPtr<nsIDOMCharacterData> charData = do_QueryInterface(node); 1.608 + if (charData) { 1.609 + nsAutoString data; 1.610 + result = charData->GetData(data); 1.611 + NS_ENSURE_SUCCESS(result, result); 1.612 + 1.613 + if (offset > 1 && 1.614 + NS_IS_LOW_SURROGATE(data[offset - 1]) && 1.615 + NS_IS_HIGH_SURROGATE(data[offset - 2])) { 1.616 + result = selCont->CharacterExtendForBackspace(); 1.617 + } 1.618 + } 1.619 + } 1.620 + break; 1.621 + } 1.622 + case eToBeginningOfLine: 1.623 + selCont->IntraLineMove(true, false); // try to move to end 1.624 + result = selCont->IntraLineMove(false, true); // select to beginning 1.625 + *aAction = eNone; 1.626 + break; 1.627 + case eToEndOfLine: 1.628 + result = selCont->IntraLineMove(true, true); 1.629 + *aAction = eNext; 1.630 + break; 1.631 + default: // avoid several compiler warnings 1.632 + result = NS_OK; 1.633 + break; 1.634 + } 1.635 + } 1.636 + return result; 1.637 +} 1.638 + 1.639 +nsresult 1.640 +nsPlaintextEditor::DeleteSelection(EDirection aAction, 1.641 + EStripWrappers aStripWrappers) 1.642 +{ 1.643 + MOZ_ASSERT(aStripWrappers == eStrip || aStripWrappers == eNoStrip); 1.644 + 1.645 + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } 1.646 + 1.647 + // Protect the edit rules object from dying 1.648 + nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules); 1.649 + 1.650 + nsresult result; 1.651 + 1.652 + // delete placeholder txns merge. 1.653 + nsAutoPlaceHolderBatch batch(this, nsGkAtoms::DeleteTxnName); 1.654 + nsAutoRules beginRulesSniffing(this, EditAction::deleteSelection, aAction); 1.655 + 1.656 + // pre-process 1.657 + nsRefPtr<Selection> selection = GetSelection(); 1.658 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.659 + 1.660 + // If there is an existing selection when an extended delete is requested, 1.661 + // platforms that use "caret-style" caret positioning collapse the 1.662 + // selection to the start and then create a new selection. 1.663 + // Platforms that use "selection-style" caret positioning just delete the 1.664 + // existing selection without extending it. 1.665 + if (!selection->Collapsed() && 1.666 + (aAction == eNextWord || aAction == ePreviousWord || 1.667 + aAction == eToBeginningOfLine || aAction == eToEndOfLine)) 1.668 + { 1.669 + if (mCaretStyle == 1) 1.670 + { 1.671 + result = selection->CollapseToStart(); 1.672 + NS_ENSURE_SUCCESS(result, result); 1.673 + } 1.674 + else 1.675 + { 1.676 + aAction = eNone; 1.677 + } 1.678 + } 1.679 + 1.680 + nsTextRulesInfo ruleInfo(EditAction::deleteSelection); 1.681 + ruleInfo.collapsedAction = aAction; 1.682 + ruleInfo.stripWrappers = aStripWrappers; 1.683 + bool cancel, handled; 1.684 + result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); 1.685 + NS_ENSURE_SUCCESS(result, result); 1.686 + if (!cancel && !handled) 1.687 + { 1.688 + result = DeleteSelectionImpl(aAction, aStripWrappers); 1.689 + } 1.690 + if (!cancel) 1.691 + { 1.692 + // post-process 1.693 + result = mRules->DidDoAction(selection, &ruleInfo, result); 1.694 + } 1.695 + 1.696 + return result; 1.697 +} 1.698 + 1.699 +NS_IMETHODIMP nsPlaintextEditor::InsertText(const nsAString &aStringToInsert) 1.700 +{ 1.701 + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } 1.702 + 1.703 + // Protect the edit rules object from dying 1.704 + nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules); 1.705 + 1.706 + EditAction opID = EditAction::insertText; 1.707 + if (mComposition) { 1.708 + opID = EditAction::insertIMEText; 1.709 + } 1.710 + nsAutoPlaceHolderBatch batch(this, nullptr); 1.711 + nsAutoRules beginRulesSniffing(this, opID, nsIEditor::eNext); 1.712 + 1.713 + // pre-process 1.714 + nsRefPtr<Selection> selection = GetSelection(); 1.715 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.716 + nsAutoString resultString; 1.717 + // XXX can we trust instring to outlive ruleInfo, 1.718 + // XXX and ruleInfo not to refer to instring in its dtor? 1.719 + //nsAutoString instring(aStringToInsert); 1.720 + nsTextRulesInfo ruleInfo(opID); 1.721 + ruleInfo.inString = &aStringToInsert; 1.722 + ruleInfo.outString = &resultString; 1.723 + ruleInfo.maxLength = mMaxTextLength; 1.724 + 1.725 + bool cancel, handled; 1.726 + nsresult res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); 1.727 + NS_ENSURE_SUCCESS(res, res); 1.728 + if (!cancel && !handled) 1.729 + { 1.730 + // we rely on rules code for now - no default implementation 1.731 + } 1.732 + if (!cancel) 1.733 + { 1.734 + // post-process 1.735 + res = mRules->DidDoAction(selection, &ruleInfo, res); 1.736 + } 1.737 + return res; 1.738 +} 1.739 + 1.740 +NS_IMETHODIMP nsPlaintextEditor::InsertLineBreak() 1.741 +{ 1.742 + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } 1.743 + 1.744 + // Protect the edit rules object from dying 1.745 + nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules); 1.746 + 1.747 + nsAutoEditBatch beginBatching(this); 1.748 + nsAutoRules beginRulesSniffing(this, EditAction::insertBreak, nsIEditor::eNext); 1.749 + 1.750 + // pre-process 1.751 + nsRefPtr<Selection> selection = GetSelection(); 1.752 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.753 + 1.754 + // Batching the selection and moving nodes out from under the caret causes 1.755 + // caret turds. Ask the shell to invalidate the caret now to avoid the turds. 1.756 + nsCOMPtr<nsIPresShell> shell = GetPresShell(); 1.757 + NS_ENSURE_TRUE(shell, NS_ERROR_NOT_INITIALIZED); 1.758 + shell->MaybeInvalidateCaretPosition(); 1.759 + 1.760 + nsTextRulesInfo ruleInfo(EditAction::insertBreak); 1.761 + ruleInfo.maxLength = mMaxTextLength; 1.762 + bool cancel, handled; 1.763 + nsresult res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); 1.764 + NS_ENSURE_SUCCESS(res, res); 1.765 + if (!cancel && !handled) 1.766 + { 1.767 + // get the (collapsed) selection location 1.768 + nsCOMPtr<nsIDOMNode> selNode; 1.769 + int32_t selOffset; 1.770 + res = GetStartNodeAndOffset(selection, getter_AddRefs(selNode), &selOffset); 1.771 + NS_ENSURE_SUCCESS(res, res); 1.772 + 1.773 + // don't put text in places that can't have it 1.774 + if (!IsTextNode(selNode) && !CanContainTag(selNode, nsGkAtoms::textTagName)) { 1.775 + return NS_ERROR_FAILURE; 1.776 + } 1.777 + 1.778 + // we need to get the doc 1.779 + nsCOMPtr<nsIDOMDocument> doc = GetDOMDocument(); 1.780 + NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED); 1.781 + 1.782 + // don't spaz my selection in subtransactions 1.783 + nsAutoTxnsConserveSelection dontSpazMySelection(this); 1.784 + 1.785 + // insert a linefeed character 1.786 + res = InsertTextImpl(NS_LITERAL_STRING("\n"), address_of(selNode), 1.787 + &selOffset, doc); 1.788 + if (!selNode) res = NS_ERROR_NULL_POINTER; // don't return here, so DidDoAction is called 1.789 + if (NS_SUCCEEDED(res)) 1.790 + { 1.791 + // set the selection to the correct location 1.792 + res = selection->Collapse(selNode, selOffset); 1.793 + 1.794 + if (NS_SUCCEEDED(res)) 1.795 + { 1.796 + // see if we're at the end of the editor range 1.797 + nsCOMPtr<nsIDOMNode> endNode; 1.798 + int32_t endOffset; 1.799 + res = GetEndNodeAndOffset(selection, getter_AddRefs(endNode), &endOffset); 1.800 + 1.801 + if (NS_SUCCEEDED(res) && endNode == selNode && endOffset == selOffset) 1.802 + { 1.803 + // SetInterlinePosition(true) means we want the caret to stick to the content on the "right". 1.804 + // We want the caret to stick to whatever is past the break. This is 1.805 + // because the break is on the same line we were on, but the next content 1.806 + // will be on the following line. 1.807 + selection->SetInterlinePosition(true); 1.808 + } 1.809 + } 1.810 + } 1.811 + } 1.812 + if (!cancel) 1.813 + { 1.814 + // post-process, always called if WillInsertBreak didn't return cancel==true 1.815 + res = mRules->DidDoAction(selection, &ruleInfo, res); 1.816 + } 1.817 + 1.818 + return res; 1.819 +} 1.820 + 1.821 +nsresult 1.822 +nsPlaintextEditor::BeginIMEComposition(WidgetCompositionEvent* aEvent) 1.823 +{ 1.824 + NS_ENSURE_TRUE(!mComposition, NS_OK); 1.825 + 1.826 + if (IsPasswordEditor()) { 1.827 + NS_ENSURE_TRUE(mRules, NS_ERROR_NULL_POINTER); 1.828 + // Protect the edit rules object from dying 1.829 + nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules); 1.830 + 1.831 + nsTextEditRules *textEditRules = 1.832 + static_cast<nsTextEditRules*>(mRules.get()); 1.833 + textEditRules->ResetIMETextPWBuf(); 1.834 + } 1.835 + 1.836 + return nsEditor::BeginIMEComposition(aEvent); 1.837 +} 1.838 + 1.839 +nsresult 1.840 +nsPlaintextEditor::UpdateIMEComposition(nsIDOMEvent* aDOMTextEvent) 1.841 +{ 1.842 + NS_ABORT_IF_FALSE(aDOMTextEvent, "aDOMTextEvent must not be nullptr"); 1.843 + 1.844 + WidgetTextEvent* widgetTextEvent = 1.845 + aDOMTextEvent->GetInternalNSEvent()->AsTextEvent(); 1.846 + NS_ENSURE_TRUE(widgetTextEvent, NS_ERROR_INVALID_ARG); 1.847 + 1.848 + EnsureComposition(widgetTextEvent); 1.849 + 1.850 + nsCOMPtr<nsIPresShell> ps = GetPresShell(); 1.851 + NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED); 1.852 + 1.853 + nsCOMPtr<nsISelection> selection; 1.854 + nsresult rv = GetSelection(getter_AddRefs(selection)); 1.855 + NS_ENSURE_SUCCESS(rv, rv); 1.856 + 1.857 + nsRefPtr<nsCaret> caretP = ps->GetCaret(); 1.858 + 1.859 + { 1.860 + TextComposition::TextEventHandlingMarker 1.861 + textEventHandlingMarker(mComposition, widgetTextEvent); 1.862 + 1.863 + nsAutoPlaceHolderBatch batch(this, nsGkAtoms::IMETxnName); 1.864 + 1.865 + rv = InsertText(widgetTextEvent->theText); 1.866 + 1.867 + if (caretP) { 1.868 + caretP->SetCaretDOMSelection(selection); 1.869 + } 1.870 + } 1.871 + 1.872 + // If still composing, we should fire input event via observer. 1.873 + // Note that if committed, we don't need to notify it since it will be 1.874 + // notified at followed compositionend event. 1.875 + // NOTE: We must notify after the auto batch will be gone. 1.876 + if (IsIMEComposing()) { 1.877 + NotifyEditorObservers(); 1.878 + } 1.879 + 1.880 + return rv; 1.881 +} 1.882 + 1.883 +already_AddRefed<nsIContent> 1.884 +nsPlaintextEditor::GetInputEventTargetContent() 1.885 +{ 1.886 + nsCOMPtr<nsIContent> target = do_QueryInterface(mEventTarget); 1.887 + return target.forget(); 1.888 +} 1.889 + 1.890 +NS_IMETHODIMP 1.891 +nsPlaintextEditor::GetDocumentIsEmpty(bool *aDocumentIsEmpty) 1.892 +{ 1.893 + NS_ENSURE_TRUE(aDocumentIsEmpty, NS_ERROR_NULL_POINTER); 1.894 + 1.895 + NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED); 1.896 + 1.897 + // Protect the edit rules object from dying 1.898 + nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules); 1.899 + 1.900 + return mRules->DocumentIsEmpty(aDocumentIsEmpty); 1.901 +} 1.902 + 1.903 +NS_IMETHODIMP 1.904 +nsPlaintextEditor::GetTextLength(int32_t *aCount) 1.905 +{ 1.906 + NS_ASSERTION(aCount, "null pointer"); 1.907 + 1.908 + // initialize out params 1.909 + *aCount = 0; 1.910 + 1.911 + // special-case for empty document, to account for the bogus node 1.912 + bool docEmpty; 1.913 + nsresult rv = GetDocumentIsEmpty(&docEmpty); 1.914 + NS_ENSURE_SUCCESS(rv, rv); 1.915 + if (docEmpty) 1.916 + return NS_OK; 1.917 + 1.918 + dom::Element *rootElement = GetRoot(); 1.919 + NS_ENSURE_TRUE(rootElement, NS_ERROR_NULL_POINTER); 1.920 + 1.921 + nsCOMPtr<nsIContentIterator> iter = 1.922 + do_CreateInstance("@mozilla.org/content/post-content-iterator;1", &rv); 1.923 + NS_ENSURE_SUCCESS(rv, rv); 1.924 + 1.925 + uint32_t totalLength = 0; 1.926 + iter->Init(rootElement); 1.927 + for (; !iter->IsDone(); iter->Next()) { 1.928 + nsCOMPtr<nsIDOMNode> currentNode = do_QueryInterface(iter->GetCurrentNode()); 1.929 + nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(currentNode); 1.930 + if (textNode && IsEditable(currentNode)) { 1.931 + uint32_t length; 1.932 + textNode->GetLength(&length); 1.933 + totalLength += length; 1.934 + } 1.935 + } 1.936 + 1.937 + *aCount = totalLength; 1.938 + return NS_OK; 1.939 +} 1.940 + 1.941 +NS_IMETHODIMP 1.942 +nsPlaintextEditor::SetMaxTextLength(int32_t aMaxTextLength) 1.943 +{ 1.944 + mMaxTextLength = aMaxTextLength; 1.945 + return NS_OK; 1.946 +} 1.947 + 1.948 +NS_IMETHODIMP 1.949 +nsPlaintextEditor::GetMaxTextLength(int32_t* aMaxTextLength) 1.950 +{ 1.951 + NS_ENSURE_TRUE(aMaxTextLength, NS_ERROR_INVALID_POINTER); 1.952 + *aMaxTextLength = mMaxTextLength; 1.953 + return NS_OK; 1.954 +} 1.955 + 1.956 +// 1.957 +// Get the wrap width 1.958 +// 1.959 +NS_IMETHODIMP 1.960 +nsPlaintextEditor::GetWrapWidth(int32_t *aWrapColumn) 1.961 +{ 1.962 + NS_ENSURE_TRUE( aWrapColumn, NS_ERROR_NULL_POINTER); 1.963 + 1.964 + *aWrapColumn = mWrapColumn; 1.965 + return NS_OK; 1.966 +} 1.967 + 1.968 +// 1.969 +// See if the style value includes this attribute, and if it does, 1.970 +// cut out everything from the attribute to the next semicolon. 1.971 +// 1.972 +static void CutStyle(const char* stylename, nsString& styleValue) 1.973 +{ 1.974 + // Find the current wrapping type: 1.975 + int32_t styleStart = styleValue.Find(stylename, true); 1.976 + if (styleStart >= 0) 1.977 + { 1.978 + int32_t styleEnd = styleValue.Find(";", false, styleStart); 1.979 + if (styleEnd > styleStart) 1.980 + styleValue.Cut(styleStart, styleEnd - styleStart + 1); 1.981 + else 1.982 + styleValue.Cut(styleStart, styleValue.Length() - styleStart); 1.983 + } 1.984 +} 1.985 + 1.986 +// 1.987 +// Change the wrap width on the root of this document. 1.988 +// 1.989 +NS_IMETHODIMP 1.990 +nsPlaintextEditor::SetWrapWidth(int32_t aWrapColumn) 1.991 +{ 1.992 + SetWrapColumn(aWrapColumn); 1.993 + 1.994 + // Make sure we're a plaintext editor, otherwise we shouldn't 1.995 + // do the rest of this. 1.996 + if (!IsPlaintextEditor()) 1.997 + return NS_OK; 1.998 + 1.999 + // Ought to set a style sheet here ... 1.1000 + // Probably should keep around an mPlaintextStyleSheet for this purpose. 1.1001 + dom::Element *rootElement = GetRoot(); 1.1002 + NS_ENSURE_TRUE(rootElement, NS_ERROR_NULL_POINTER); 1.1003 + 1.1004 + // Get the current style for this root element: 1.1005 + nsAutoString styleValue; 1.1006 + rootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::style, styleValue); 1.1007 + 1.1008 + // We'll replace styles for these values: 1.1009 + CutStyle("white-space", styleValue); 1.1010 + CutStyle("width", styleValue); 1.1011 + CutStyle("font-family", styleValue); 1.1012 + 1.1013 + // If we have other style left, trim off any existing semicolons 1.1014 + // or whitespace, then add a known semicolon-space: 1.1015 + if (!styleValue.IsEmpty()) 1.1016 + { 1.1017 + styleValue.Trim("; \t", false, true); 1.1018 + styleValue.AppendLiteral("; "); 1.1019 + } 1.1020 + 1.1021 + // Make sure we have fixed-width font. This should be done for us, 1.1022 + // but it isn't, see bug 22502, so we have to add "font: -moz-fixed;". 1.1023 + // Only do this if we're wrapping. 1.1024 + if (IsWrapHackEnabled() && aWrapColumn >= 0) 1.1025 + styleValue.AppendLiteral("font-family: -moz-fixed; "); 1.1026 + 1.1027 + // If "mail.compose.wrap_to_window_width" is set, and we're a mail editor, 1.1028 + // then remember our wrap width (for output purposes) but set the visual 1.1029 + // wrapping to window width. 1.1030 + // We may reset mWrapToWindow here, based on the pref's current value. 1.1031 + if (IsMailEditor()) 1.1032 + { 1.1033 + mWrapToWindow = 1.1034 + Preferences::GetBool("mail.compose.wrap_to_window_width", mWrapToWindow); 1.1035 + } 1.1036 + 1.1037 + // and now we're ready to set the new whitespace/wrapping style. 1.1038 + if (aWrapColumn > 0 && !mWrapToWindow) // Wrap to a fixed column 1.1039 + { 1.1040 + styleValue.AppendLiteral("white-space: pre-wrap; width: "); 1.1041 + styleValue.AppendInt(aWrapColumn); 1.1042 + styleValue.AppendLiteral("ch;"); 1.1043 + } 1.1044 + else if (mWrapToWindow || aWrapColumn == 0) 1.1045 + styleValue.AppendLiteral("white-space: pre-wrap;"); 1.1046 + else 1.1047 + styleValue.AppendLiteral("white-space: pre;"); 1.1048 + 1.1049 + return rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::style, styleValue, true); 1.1050 +} 1.1051 + 1.1052 +NS_IMETHODIMP 1.1053 +nsPlaintextEditor::SetWrapColumn(int32_t aWrapColumn) 1.1054 +{ 1.1055 + mWrapColumn = aWrapColumn; 1.1056 + return NS_OK; 1.1057 +} 1.1058 + 1.1059 +// 1.1060 +// Get the newline handling for this editor 1.1061 +// 1.1062 +NS_IMETHODIMP 1.1063 +nsPlaintextEditor::GetNewlineHandling(int32_t *aNewlineHandling) 1.1064 +{ 1.1065 + NS_ENSURE_ARG_POINTER(aNewlineHandling); 1.1066 + 1.1067 + *aNewlineHandling = mNewlineHandling; 1.1068 + return NS_OK; 1.1069 +} 1.1070 + 1.1071 +// 1.1072 +// Change the newline handling for this editor 1.1073 +// 1.1074 +NS_IMETHODIMP 1.1075 +nsPlaintextEditor::SetNewlineHandling(int32_t aNewlineHandling) 1.1076 +{ 1.1077 + mNewlineHandling = aNewlineHandling; 1.1078 + 1.1079 + return NS_OK; 1.1080 +} 1.1081 + 1.1082 +NS_IMETHODIMP 1.1083 +nsPlaintextEditor::Undo(uint32_t aCount) 1.1084 +{ 1.1085 + // Protect the edit rules object from dying 1.1086 + nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules); 1.1087 + 1.1088 + nsAutoUpdateViewBatch beginViewBatching(this); 1.1089 + 1.1090 + ForceCompositionEnd(); 1.1091 + 1.1092 + nsAutoRules beginRulesSniffing(this, EditAction::undo, nsIEditor::eNone); 1.1093 + 1.1094 + nsTextRulesInfo ruleInfo(EditAction::undo); 1.1095 + nsRefPtr<Selection> selection = GetSelection(); 1.1096 + bool cancel, handled; 1.1097 + nsresult result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); 1.1098 + 1.1099 + if (!cancel && NS_SUCCEEDED(result)) 1.1100 + { 1.1101 + result = nsEditor::Undo(aCount); 1.1102 + result = mRules->DidDoAction(selection, &ruleInfo, result); 1.1103 + } 1.1104 + 1.1105 + NotifyEditorObservers(); 1.1106 + return result; 1.1107 +} 1.1108 + 1.1109 +NS_IMETHODIMP 1.1110 +nsPlaintextEditor::Redo(uint32_t aCount) 1.1111 +{ 1.1112 + // Protect the edit rules object from dying 1.1113 + nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules); 1.1114 + 1.1115 + nsAutoUpdateViewBatch beginViewBatching(this); 1.1116 + 1.1117 + ForceCompositionEnd(); 1.1118 + 1.1119 + nsAutoRules beginRulesSniffing(this, EditAction::redo, nsIEditor::eNone); 1.1120 + 1.1121 + nsTextRulesInfo ruleInfo(EditAction::redo); 1.1122 + nsRefPtr<Selection> selection = GetSelection(); 1.1123 + bool cancel, handled; 1.1124 + nsresult result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); 1.1125 + 1.1126 + if (!cancel && NS_SUCCEEDED(result)) 1.1127 + { 1.1128 + result = nsEditor::Redo(aCount); 1.1129 + result = mRules->DidDoAction(selection, &ruleInfo, result); 1.1130 + } 1.1131 + 1.1132 + NotifyEditorObservers(); 1.1133 + return result; 1.1134 +} 1.1135 + 1.1136 +bool 1.1137 +nsPlaintextEditor::CanCutOrCopy() 1.1138 +{ 1.1139 + nsCOMPtr<nsISelection> selection; 1.1140 + if (NS_FAILED(GetSelection(getter_AddRefs(selection)))) 1.1141 + return false; 1.1142 + 1.1143 + return !selection->Collapsed(); 1.1144 +} 1.1145 + 1.1146 +bool 1.1147 +nsPlaintextEditor::FireClipboardEvent(int32_t aType, int32_t aSelectionType) 1.1148 +{ 1.1149 + if (aType == NS_PASTE) 1.1150 + ForceCompositionEnd(); 1.1151 + 1.1152 + nsCOMPtr<nsIPresShell> presShell = GetPresShell(); 1.1153 + NS_ENSURE_TRUE(presShell, false); 1.1154 + 1.1155 + nsCOMPtr<nsISelection> selection; 1.1156 + if (NS_FAILED(GetSelection(getter_AddRefs(selection)))) 1.1157 + return false; 1.1158 + 1.1159 + if (!nsCopySupport::FireClipboardEvent(aType, aSelectionType, presShell, selection)) 1.1160 + return false; 1.1161 + 1.1162 + // If the event handler caused the editor to be destroyed, return false. 1.1163 + // Otherwise return true to indicate that the event was not cancelled. 1.1164 + return !mDidPreDestroy; 1.1165 +} 1.1166 + 1.1167 +NS_IMETHODIMP nsPlaintextEditor::Cut() 1.1168 +{ 1.1169 + if (FireClipboardEvent(NS_CUT, nsIClipboard::kGlobalClipboard)) 1.1170 + return DeleteSelection(eNone, eStrip); 1.1171 + return NS_OK; 1.1172 +} 1.1173 + 1.1174 +NS_IMETHODIMP nsPlaintextEditor::CanCut(bool *aCanCut) 1.1175 +{ 1.1176 + NS_ENSURE_ARG_POINTER(aCanCut); 1.1177 + *aCanCut = IsModifiable() && CanCutOrCopy(); 1.1178 + return NS_OK; 1.1179 +} 1.1180 + 1.1181 +NS_IMETHODIMP nsPlaintextEditor::Copy() 1.1182 +{ 1.1183 + FireClipboardEvent(NS_COPY, nsIClipboard::kGlobalClipboard); 1.1184 + return NS_OK; 1.1185 +} 1.1186 + 1.1187 +NS_IMETHODIMP nsPlaintextEditor::CanCopy(bool *aCanCopy) 1.1188 +{ 1.1189 + NS_ENSURE_ARG_POINTER(aCanCopy); 1.1190 + *aCanCopy = CanCutOrCopy(); 1.1191 + return NS_OK; 1.1192 +} 1.1193 + 1.1194 +// Shared between OutputToString and OutputToStream 1.1195 +NS_IMETHODIMP 1.1196 +nsPlaintextEditor::GetAndInitDocEncoder(const nsAString& aFormatType, 1.1197 + uint32_t aFlags, 1.1198 + const nsACString& aCharset, 1.1199 + nsIDocumentEncoder** encoder) 1.1200 +{ 1.1201 + nsresult rv = NS_OK; 1.1202 + 1.1203 + nsAutoCString formatType(NS_DOC_ENCODER_CONTRACTID_BASE); 1.1204 + LossyAppendUTF16toASCII(aFormatType, formatType); 1.1205 + nsCOMPtr<nsIDocumentEncoder> docEncoder (do_CreateInstance(formatType.get(), &rv)); 1.1206 + NS_ENSURE_SUCCESS(rv, rv); 1.1207 + 1.1208 + nsCOMPtr<nsIDOMDocument> domDoc = do_QueryReferent(mDocWeak); 1.1209 + NS_ASSERTION(domDoc, "Need a document"); 1.1210 + 1.1211 + rv = docEncoder->Init(domDoc, aFormatType, aFlags); 1.1212 + NS_ENSURE_SUCCESS(rv, rv); 1.1213 + 1.1214 + if (!aCharset.IsEmpty() && !aCharset.EqualsLiteral("null")) { 1.1215 + docEncoder->SetCharset(aCharset); 1.1216 + } 1.1217 + 1.1218 + int32_t wc; 1.1219 + (void) GetWrapWidth(&wc); 1.1220 + if (wc >= 0) 1.1221 + (void) docEncoder->SetWrapColumn(wc); 1.1222 + 1.1223 + // Set the selection, if appropriate. 1.1224 + // We do this either if the OutputSelectionOnly flag is set, 1.1225 + // in which case we use our existing selection ... 1.1226 + if (aFlags & nsIDocumentEncoder::OutputSelectionOnly) 1.1227 + { 1.1228 + nsCOMPtr<nsISelection> selection; 1.1229 + rv = GetSelection(getter_AddRefs(selection)); 1.1230 + NS_ENSURE_SUCCESS(rv, rv); 1.1231 + if (selection) { 1.1232 + rv = docEncoder->SetSelection(selection); 1.1233 + NS_ENSURE_SUCCESS(rv, rv); 1.1234 + } 1.1235 + } 1.1236 + // ... or if the root element is not a body, 1.1237 + // in which case we set the selection to encompass the root. 1.1238 + else 1.1239 + { 1.1240 + dom::Element* rootElement = GetRoot(); 1.1241 + NS_ENSURE_TRUE(rootElement, NS_ERROR_FAILURE); 1.1242 + if (!rootElement->IsHTML(nsGkAtoms::body)) { 1.1243 + rv = docEncoder->SetNativeContainerNode(rootElement); 1.1244 + NS_ENSURE_SUCCESS(rv, rv); 1.1245 + } 1.1246 + } 1.1247 + 1.1248 + docEncoder.forget(encoder); 1.1249 + return NS_OK; 1.1250 +} 1.1251 + 1.1252 + 1.1253 +NS_IMETHODIMP 1.1254 +nsPlaintextEditor::OutputToString(const nsAString& aFormatType, 1.1255 + uint32_t aFlags, 1.1256 + nsAString& aOutputString) 1.1257 +{ 1.1258 + // Protect the edit rules object from dying 1.1259 + nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules); 1.1260 + 1.1261 + nsString resultString; 1.1262 + nsTextRulesInfo ruleInfo(EditAction::outputText); 1.1263 + ruleInfo.outString = &resultString; 1.1264 + // XXX Struct should store a nsAReadable* 1.1265 + nsAutoString str(aFormatType); 1.1266 + ruleInfo.outputFormat = &str; 1.1267 + bool cancel, handled; 1.1268 + nsresult rv = mRules->WillDoAction(nullptr, &ruleInfo, &cancel, &handled); 1.1269 + if (cancel || NS_FAILED(rv)) { return rv; } 1.1270 + if (handled) 1.1271 + { // this case will get triggered by password fields 1.1272 + aOutputString.Assign(*(ruleInfo.outString)); 1.1273 + return rv; 1.1274 + } 1.1275 + 1.1276 + nsAutoCString charsetStr; 1.1277 + rv = GetDocumentCharacterSet(charsetStr); 1.1278 + if(NS_FAILED(rv) || charsetStr.IsEmpty()) 1.1279 + charsetStr.AssignLiteral("ISO-8859-1"); 1.1280 + 1.1281 + nsCOMPtr<nsIDocumentEncoder> encoder; 1.1282 + rv = GetAndInitDocEncoder(aFormatType, aFlags, charsetStr, getter_AddRefs(encoder)); 1.1283 + NS_ENSURE_SUCCESS(rv, rv); 1.1284 + return encoder->EncodeToString(aOutputString); 1.1285 +} 1.1286 + 1.1287 +NS_IMETHODIMP 1.1288 +nsPlaintextEditor::OutputToStream(nsIOutputStream* aOutputStream, 1.1289 + const nsAString& aFormatType, 1.1290 + const nsACString& aCharset, 1.1291 + uint32_t aFlags) 1.1292 +{ 1.1293 + nsresult rv; 1.1294 + 1.1295 + // special-case for empty document when requesting plain text, 1.1296 + // to account for the bogus text node. 1.1297 + // XXX Should there be a similar test in OutputToString? 1.1298 + if (aFormatType.EqualsLiteral("text/plain")) 1.1299 + { 1.1300 + bool docEmpty; 1.1301 + rv = GetDocumentIsEmpty(&docEmpty); 1.1302 + NS_ENSURE_SUCCESS(rv, rv); 1.1303 + 1.1304 + if (docEmpty) 1.1305 + return NS_OK; // output nothing 1.1306 + } 1.1307 + 1.1308 + nsCOMPtr<nsIDocumentEncoder> encoder; 1.1309 + rv = GetAndInitDocEncoder(aFormatType, aFlags, aCharset, 1.1310 + getter_AddRefs(encoder)); 1.1311 + 1.1312 + NS_ENSURE_SUCCESS(rv, rv); 1.1313 + 1.1314 + return encoder->EncodeToStream(aOutputStream); 1.1315 +} 1.1316 + 1.1317 +NS_IMETHODIMP 1.1318 +nsPlaintextEditor::InsertTextWithQuotations(const nsAString &aStringToInsert) 1.1319 +{ 1.1320 + return InsertText(aStringToInsert); 1.1321 +} 1.1322 + 1.1323 +NS_IMETHODIMP 1.1324 +nsPlaintextEditor::PasteAsQuotation(int32_t aSelectionType) 1.1325 +{ 1.1326 + // Get Clipboard Service 1.1327 + nsresult rv; 1.1328 + nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv)); 1.1329 + NS_ENSURE_SUCCESS(rv, rv); 1.1330 + 1.1331 + // Get the nsITransferable interface for getting the data from the clipboard 1.1332 + nsCOMPtr<nsITransferable> trans; 1.1333 + rv = PrepareTransferable(getter_AddRefs(trans)); 1.1334 + if (NS_SUCCEEDED(rv) && trans) 1.1335 + { 1.1336 + // Get the Data from the clipboard 1.1337 + clipboard->GetData(trans, aSelectionType); 1.1338 + 1.1339 + // Now we ask the transferable for the data 1.1340 + // it still owns the data, we just have a pointer to it. 1.1341 + // If it can't support a "text" output of the data the call will fail 1.1342 + nsCOMPtr<nsISupports> genericDataObj; 1.1343 + uint32_t len; 1.1344 + char* flav = nullptr; 1.1345 + rv = trans->GetAnyTransferData(&flav, getter_AddRefs(genericDataObj), 1.1346 + &len); 1.1347 + if (NS_FAILED(rv) || !flav) 1.1348 + { 1.1349 +#ifdef DEBUG_akkana 1.1350 + printf("PasteAsPlaintextQuotation: GetAnyTransferData failed, %d\n", rv); 1.1351 +#endif 1.1352 + return rv; 1.1353 + } 1.1354 +#ifdef DEBUG_clipboard 1.1355 + printf("Got flavor [%s]\n", flav); 1.1356 +#endif 1.1357 + if (0 == nsCRT::strcmp(flav, kUnicodeMime) || 1.1358 + 0 == nsCRT::strcmp(flav, kMozTextInternal)) 1.1359 + { 1.1360 + nsCOMPtr<nsISupportsString> textDataObj ( do_QueryInterface(genericDataObj) ); 1.1361 + if (textDataObj && len > 0) 1.1362 + { 1.1363 + nsAutoString stuffToPaste; 1.1364 + textDataObj->GetData ( stuffToPaste ); 1.1365 + nsAutoEditBatch beginBatching(this); 1.1366 + rv = InsertAsQuotation(stuffToPaste, 0); 1.1367 + } 1.1368 + } 1.1369 + NS_Free(flav); 1.1370 + } 1.1371 + 1.1372 + return rv; 1.1373 +} 1.1374 + 1.1375 +NS_IMETHODIMP 1.1376 +nsPlaintextEditor::InsertAsQuotation(const nsAString& aQuotedText, 1.1377 + nsIDOMNode **aNodeInserted) 1.1378 +{ 1.1379 + // Protect the edit rules object from dying 1.1380 + nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules); 1.1381 + 1.1382 + // Let the citer quote it for us: 1.1383 + nsString quotedStuff; 1.1384 + nsresult rv = nsInternetCiter::GetCiteString(aQuotedText, quotedStuff); 1.1385 + NS_ENSURE_SUCCESS(rv, rv); 1.1386 + 1.1387 + // It's best to put a blank line after the quoted text so that mails 1.1388 + // written without thinking won't be so ugly. 1.1389 + if (!aQuotedText.IsEmpty() && (aQuotedText.Last() != char16_t('\n'))) 1.1390 + quotedStuff.Append(char16_t('\n')); 1.1391 + 1.1392 + // get selection 1.1393 + nsRefPtr<Selection> selection = GetSelection(); 1.1394 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.1395 + 1.1396 + nsAutoEditBatch beginBatching(this); 1.1397 + nsAutoRules beginRulesSniffing(this, EditAction::insertText, nsIEditor::eNext); 1.1398 + 1.1399 + // give rules a chance to handle or cancel 1.1400 + nsTextRulesInfo ruleInfo(EditAction::insertElement); 1.1401 + bool cancel, handled; 1.1402 + rv = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); 1.1403 + NS_ENSURE_SUCCESS(rv, rv); 1.1404 + if (cancel) return NS_OK; // rules canceled the operation 1.1405 + if (!handled) 1.1406 + { 1.1407 + rv = InsertText(quotedStuff); 1.1408 + 1.1409 + // XXX Should set *aNodeInserted to the first node inserted 1.1410 + if (aNodeInserted && NS_SUCCEEDED(rv)) 1.1411 + { 1.1412 + *aNodeInserted = 0; 1.1413 + //NS_IF_ADDREF(*aNodeInserted); 1.1414 + } 1.1415 + } 1.1416 + return rv; 1.1417 +} 1.1418 + 1.1419 +NS_IMETHODIMP 1.1420 +nsPlaintextEditor::PasteAsCitedQuotation(const nsAString& aCitation, 1.1421 + int32_t aSelectionType) 1.1422 +{ 1.1423 + return NS_ERROR_NOT_IMPLEMENTED; 1.1424 +} 1.1425 + 1.1426 +NS_IMETHODIMP 1.1427 +nsPlaintextEditor::InsertAsCitedQuotation(const nsAString& aQuotedText, 1.1428 + const nsAString& aCitation, 1.1429 + bool aInsertHTML, 1.1430 + nsIDOMNode **aNodeInserted) 1.1431 +{ 1.1432 + return InsertAsQuotation(aQuotedText, aNodeInserted); 1.1433 +} 1.1434 + 1.1435 +nsresult 1.1436 +nsPlaintextEditor::SharedOutputString(uint32_t aFlags, 1.1437 + bool* aIsCollapsed, 1.1438 + nsAString& aResult) 1.1439 +{ 1.1440 + nsCOMPtr<nsISelection> selection; 1.1441 + nsresult rv = GetSelection(getter_AddRefs(selection)); 1.1442 + NS_ENSURE_SUCCESS(rv, rv); 1.1443 + NS_ENSURE_TRUE(selection, NS_ERROR_NOT_INITIALIZED); 1.1444 + 1.1445 + *aIsCollapsed = selection->Collapsed(); 1.1446 + 1.1447 + if (!*aIsCollapsed) 1.1448 + aFlags |= nsIDocumentEncoder::OutputSelectionOnly; 1.1449 + // If the selection isn't collapsed, we'll use the whole document. 1.1450 + 1.1451 + return OutputToString(NS_LITERAL_STRING("text/plain"), aFlags, aResult); 1.1452 +} 1.1453 + 1.1454 +NS_IMETHODIMP 1.1455 +nsPlaintextEditor::Rewrap(bool aRespectNewlines) 1.1456 +{ 1.1457 + int32_t wrapCol; 1.1458 + nsresult rv = GetWrapWidth(&wrapCol); 1.1459 + NS_ENSURE_SUCCESS(rv, NS_OK); 1.1460 + 1.1461 + // Rewrap makes no sense if there's no wrap column; default to 72. 1.1462 + if (wrapCol <= 0) 1.1463 + wrapCol = 72; 1.1464 + 1.1465 +#ifdef DEBUG_akkana 1.1466 + printf("nsPlaintextEditor::Rewrap to %ld columns\n", (long)wrapCol); 1.1467 +#endif 1.1468 + 1.1469 + nsAutoString current; 1.1470 + bool isCollapsed; 1.1471 + rv = SharedOutputString(nsIDocumentEncoder::OutputFormatted 1.1472 + | nsIDocumentEncoder::OutputLFLineBreak, 1.1473 + &isCollapsed, current); 1.1474 + NS_ENSURE_SUCCESS(rv, rv); 1.1475 + 1.1476 + nsString wrapped; 1.1477 + uint32_t firstLineOffset = 0; // XXX need to reset this if there is a selection 1.1478 + rv = nsInternetCiter::Rewrap(current, wrapCol, firstLineOffset, aRespectNewlines, 1.1479 + wrapped); 1.1480 + NS_ENSURE_SUCCESS(rv, rv); 1.1481 + 1.1482 + if (isCollapsed) // rewrap the whole document 1.1483 + SelectAll(); 1.1484 + 1.1485 + return InsertTextWithQuotations(wrapped); 1.1486 +} 1.1487 + 1.1488 +NS_IMETHODIMP 1.1489 +nsPlaintextEditor::StripCites() 1.1490 +{ 1.1491 +#ifdef DEBUG_akkana 1.1492 + printf("nsPlaintextEditor::StripCites()\n"); 1.1493 +#endif 1.1494 + 1.1495 + nsAutoString current; 1.1496 + bool isCollapsed; 1.1497 + nsresult rv = SharedOutputString(nsIDocumentEncoder::OutputFormatted, 1.1498 + &isCollapsed, current); 1.1499 + NS_ENSURE_SUCCESS(rv, rv); 1.1500 + 1.1501 + nsString stripped; 1.1502 + rv = nsInternetCiter::StripCites(current, stripped); 1.1503 + NS_ENSURE_SUCCESS(rv, rv); 1.1504 + 1.1505 + if (isCollapsed) // rewrap the whole document 1.1506 + { 1.1507 + rv = SelectAll(); 1.1508 + NS_ENSURE_SUCCESS(rv, rv); 1.1509 + } 1.1510 + 1.1511 + return InsertText(stripped); 1.1512 +} 1.1513 + 1.1514 +NS_IMETHODIMP 1.1515 +nsPlaintextEditor::GetEmbeddedObjects(nsISupportsArray** aNodeList) 1.1516 +{ 1.1517 + *aNodeList = 0; 1.1518 + return NS_OK; 1.1519 +} 1.1520 + 1.1521 + 1.1522 +/** All editor operations which alter the doc should be prefaced 1.1523 + * with a call to StartOperation, naming the action and direction */ 1.1524 +NS_IMETHODIMP 1.1525 +nsPlaintextEditor::StartOperation(EditAction opID, 1.1526 + nsIEditor::EDirection aDirection) 1.1527 +{ 1.1528 + // Protect the edit rules object from dying 1.1529 + nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules); 1.1530 + 1.1531 + nsEditor::StartOperation(opID, aDirection); // will set mAction, mDirection 1.1532 + if (mRules) return mRules->BeforeEdit(mAction, mDirection); 1.1533 + return NS_OK; 1.1534 +} 1.1535 + 1.1536 + 1.1537 +/** All editor operations which alter the doc should be followed 1.1538 + * with a call to EndOperation */ 1.1539 +NS_IMETHODIMP 1.1540 +nsPlaintextEditor::EndOperation() 1.1541 +{ 1.1542 + // Protect the edit rules object from dying 1.1543 + nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules); 1.1544 + 1.1545 + // post processing 1.1546 + nsresult res = NS_OK; 1.1547 + if (mRules) res = mRules->AfterEdit(mAction, mDirection); 1.1548 + nsEditor::EndOperation(); // will clear mAction, mDirection 1.1549 + return res; 1.1550 +} 1.1551 + 1.1552 + 1.1553 +NS_IMETHODIMP 1.1554 +nsPlaintextEditor::SelectEntireDocument(nsISelection *aSelection) 1.1555 +{ 1.1556 + if (!aSelection || !mRules) { return NS_ERROR_NULL_POINTER; } 1.1557 + 1.1558 + // Protect the edit rules object from dying 1.1559 + nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules); 1.1560 + 1.1561 + // is doc empty? 1.1562 + bool bDocIsEmpty; 1.1563 + if (NS_SUCCEEDED(mRules->DocumentIsEmpty(&bDocIsEmpty)) && bDocIsEmpty) 1.1564 + { 1.1565 + // get root node 1.1566 + nsCOMPtr<nsIDOMElement> rootElement = do_QueryInterface(GetRoot()); 1.1567 + NS_ENSURE_TRUE(rootElement, NS_ERROR_FAILURE); 1.1568 + 1.1569 + // if it's empty don't select entire doc - that would select the bogus node 1.1570 + return aSelection->Collapse(rootElement, 0); 1.1571 + } 1.1572 + 1.1573 + nsresult rv = nsEditor::SelectEntireDocument(aSelection); 1.1574 + NS_ENSURE_SUCCESS(rv, rv); 1.1575 + 1.1576 + // Don't select the trailing BR node if we have one 1.1577 + int32_t selOffset; 1.1578 + nsCOMPtr<nsIDOMNode> selNode; 1.1579 + rv = GetEndNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset); 1.1580 + NS_ENSURE_SUCCESS(rv, rv); 1.1581 + 1.1582 + nsCOMPtr<nsIDOMNode> childNode = GetChildAt(selNode, selOffset - 1); 1.1583 + 1.1584 + if (childNode && nsTextEditUtils::IsMozBR(childNode)) { 1.1585 + int32_t parentOffset; 1.1586 + nsCOMPtr<nsIDOMNode> parentNode = GetNodeLocation(childNode, &parentOffset); 1.1587 + 1.1588 + return aSelection->Extend(parentNode, parentOffset); 1.1589 + } 1.1590 + 1.1591 + return NS_OK; 1.1592 +} 1.1593 + 1.1594 +already_AddRefed<mozilla::dom::EventTarget> 1.1595 +nsPlaintextEditor::GetDOMEventTarget() 1.1596 +{ 1.1597 + nsCOMPtr<mozilla::dom::EventTarget> copy = mEventTarget; 1.1598 + return copy.forget(); 1.1599 +} 1.1600 + 1.1601 + 1.1602 +nsresult 1.1603 +nsPlaintextEditor::SetAttributeOrEquivalent(nsIDOMElement * aElement, 1.1604 + const nsAString & aAttribute, 1.1605 + const nsAString & aValue, 1.1606 + bool aSuppressTransaction) 1.1607 +{ 1.1608 + return nsEditor::SetAttribute(aElement, aAttribute, aValue); 1.1609 +} 1.1610 + 1.1611 +nsresult 1.1612 +nsPlaintextEditor::RemoveAttributeOrEquivalent(nsIDOMElement * aElement, 1.1613 + const nsAString & aAttribute, 1.1614 + bool aSuppressTransaction) 1.1615 +{ 1.1616 + return nsEditor::RemoveAttribute(aElement, aAttribute); 1.1617 +}