1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/editor/libeditor/html/nsHTMLEditRules.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,9440 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 sw=2 et tw=79: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include <stdlib.h> 1.11 + 1.12 +#include "mozilla/Assertions.h" 1.13 +#include "mozilla/MathAlgorithms.h" 1.14 +#include "mozilla/Preferences.h" 1.15 +#include "mozilla/dom/Selection.h" 1.16 +#include "mozilla/dom/Element.h" 1.17 +#include "mozilla/mozalloc.h" 1.18 +#include "nsAString.h" 1.19 +#include "nsAlgorithm.h" 1.20 +#include "nsCOMArray.h" 1.21 +#include "nsCRT.h" 1.22 +#include "nsCRTGlue.h" 1.23 +#include "nsComponentManagerUtils.h" 1.24 +#include "nsContentUtils.h" 1.25 +#include "nsDebug.h" 1.26 +#include "nsEditProperty.h" 1.27 +#include "nsEditor.h" 1.28 +#include "nsEditorUtils.h" 1.29 +#include "nsError.h" 1.30 +#include "nsGkAtoms.h" 1.31 +#include "nsHTMLCSSUtils.h" 1.32 +#include "nsHTMLEditRules.h" 1.33 +#include "nsHTMLEditUtils.h" 1.34 +#include "nsHTMLEditor.h" 1.35 +#include "nsIAtom.h" 1.36 +#include "nsIContent.h" 1.37 +#include "nsIContentIterator.h" 1.38 +#include "nsID.h" 1.39 +#include "nsIDOMCharacterData.h" 1.40 +#include "nsIDOMDocument.h" 1.41 +#include "nsIDOMElement.h" 1.42 +#include "nsIDOMNode.h" 1.43 +#include "nsIDOMRange.h" 1.44 +#include "nsIDOMText.h" 1.45 +#include "nsIHTMLAbsPosEditor.h" 1.46 +#include "nsIHTMLDocument.h" 1.47 +#include "nsINode.h" 1.48 +#include "nsISelection.h" 1.49 +#include "nsISelectionPrivate.h" 1.50 +#include "nsLiteralString.h" 1.51 +#include "nsPlaintextEditor.h" 1.52 +#include "nsRange.h" 1.53 +#include "nsReadableUtils.h" 1.54 +#include "nsString.h" 1.55 +#include "nsStringFwd.h" 1.56 +#include "nsTArray.h" 1.57 +#include "nsTextEditUtils.h" 1.58 +#include "nsThreadUtils.h" 1.59 +#include "nsUnicharUtils.h" 1.60 +#include "nsWSRunObject.h" 1.61 +#include <algorithm> 1.62 + 1.63 +class nsISupports; 1.64 +class nsRulesInfo; 1.65 + 1.66 +using namespace mozilla; 1.67 +using namespace mozilla::dom; 1.68 + 1.69 +//const static char* kMOZEditorBogusNodeAttr="MOZ_EDITOR_BOGUS_NODE"; 1.70 +//const static char* kMOZEditorBogusNodeValue="TRUE"; 1.71 + 1.72 +enum 1.73 +{ 1.74 + kLonely = 0, 1.75 + kPrevSib = 1, 1.76 + kNextSib = 2, 1.77 + kBothSibs = 3 1.78 +}; 1.79 + 1.80 +/******************************************************** 1.81 + * first some helpful functors we will use 1.82 + ********************************************************/ 1.83 + 1.84 +static bool IsBlockNode(nsIDOMNode* node) 1.85 +{ 1.86 + bool isBlock (false); 1.87 + nsHTMLEditor::NodeIsBlockStatic(node, &isBlock); 1.88 + return isBlock; 1.89 +} 1.90 + 1.91 +static bool IsInlineNode(nsIDOMNode* node) 1.92 +{ 1.93 + return !IsBlockNode(node); 1.94 +} 1.95 + 1.96 +static bool 1.97 +IsStyleCachePreservingAction(EditAction action) 1.98 +{ 1.99 + return action == EditAction::deleteSelection || 1.100 + action == EditAction::insertBreak || 1.101 + action == EditAction::makeList || 1.102 + action == EditAction::indent || 1.103 + action == EditAction::outdent || 1.104 + action == EditAction::align || 1.105 + action == EditAction::makeBasicBlock || 1.106 + action == EditAction::removeList || 1.107 + action == EditAction::makeDefListItem || 1.108 + action == EditAction::insertElement || 1.109 + action == EditAction::insertQuotation; 1.110 +} 1.111 + 1.112 +class nsTableCellAndListItemFunctor : public nsBoolDomIterFunctor 1.113 +{ 1.114 + public: 1.115 + virtual bool operator()(nsIDOMNode* aNode) // used to build list of all li's, td's & th's iterator covers 1.116 + { 1.117 + if (nsHTMLEditUtils::IsTableCell(aNode)) return true; 1.118 + if (nsHTMLEditUtils::IsListItem(aNode)) return true; 1.119 + return false; 1.120 + } 1.121 +}; 1.122 + 1.123 +class nsBRNodeFunctor : public nsBoolDomIterFunctor 1.124 +{ 1.125 + public: 1.126 + virtual bool operator()(nsIDOMNode* aNode) 1.127 + { 1.128 + if (nsTextEditUtils::IsBreak(aNode)) return true; 1.129 + return false; 1.130 + } 1.131 +}; 1.132 + 1.133 +class nsEmptyEditableFunctor : public nsBoolDomIterFunctor 1.134 +{ 1.135 + public: 1.136 + nsEmptyEditableFunctor(nsHTMLEditor* editor) : mHTMLEditor(editor) {} 1.137 + virtual bool operator()(nsIDOMNode* aNode) 1.138 + { 1.139 + if (mHTMLEditor->IsEditable(aNode) && 1.140 + (nsHTMLEditUtils::IsListItem(aNode) || 1.141 + nsHTMLEditUtils::IsTableCellOrCaption(aNode))) 1.142 + { 1.143 + bool bIsEmptyNode; 1.144 + nsresult res = mHTMLEditor->IsEmptyNode(aNode, &bIsEmptyNode, false, false); 1.145 + NS_ENSURE_SUCCESS(res, false); 1.146 + if (bIsEmptyNode) 1.147 + return true; 1.148 + } 1.149 + return false; 1.150 + } 1.151 + protected: 1.152 + nsHTMLEditor* mHTMLEditor; 1.153 +}; 1.154 + 1.155 +class nsEditableTextFunctor : public nsBoolDomIterFunctor 1.156 +{ 1.157 + public: 1.158 + nsEditableTextFunctor(nsHTMLEditor* editor) : mHTMLEditor(editor) {} 1.159 + virtual bool operator()(nsIDOMNode* aNode) 1.160 + { 1.161 + if (nsEditor::IsTextNode(aNode) && mHTMLEditor->IsEditable(aNode)) 1.162 + { 1.163 + return true; 1.164 + } 1.165 + return false; 1.166 + } 1.167 + protected: 1.168 + nsHTMLEditor* mHTMLEditor; 1.169 +}; 1.170 + 1.171 + 1.172 +/******************************************************** 1.173 + * Constructor/Destructor 1.174 + ********************************************************/ 1.175 + 1.176 +nsHTMLEditRules::nsHTMLEditRules() 1.177 +{ 1.178 + InitFields(); 1.179 +} 1.180 + 1.181 +void 1.182 +nsHTMLEditRules::InitFields() 1.183 +{ 1.184 + mHTMLEditor = nullptr; 1.185 + mDocChangeRange = nullptr; 1.186 + mListenerEnabled = true; 1.187 + mReturnInEmptyLIKillsList = true; 1.188 + mDidDeleteSelection = false; 1.189 + mDidRangedDelete = false; 1.190 + mRestoreContentEditableCount = false; 1.191 + mUtilRange = nullptr; 1.192 + mJoinOffset = 0; 1.193 + mNewBlock = nullptr; 1.194 + mRangeItem = new nsRangeStore(); 1.195 + // populate mCachedStyles 1.196 + mCachedStyles[0] = StyleCache(nsEditProperty::b, EmptyString(), EmptyString()); 1.197 + mCachedStyles[1] = StyleCache(nsEditProperty::i, EmptyString(), EmptyString()); 1.198 + mCachedStyles[2] = StyleCache(nsEditProperty::u, EmptyString(), EmptyString()); 1.199 + mCachedStyles[3] = StyleCache(nsEditProperty::font, NS_LITERAL_STRING("face"), EmptyString()); 1.200 + mCachedStyles[4] = StyleCache(nsEditProperty::font, NS_LITERAL_STRING("size"), EmptyString()); 1.201 + mCachedStyles[5] = StyleCache(nsEditProperty::font, NS_LITERAL_STRING("color"), EmptyString()); 1.202 + mCachedStyles[6] = StyleCache(nsEditProperty::tt, EmptyString(), EmptyString()); 1.203 + mCachedStyles[7] = StyleCache(nsEditProperty::em, EmptyString(), EmptyString()); 1.204 + mCachedStyles[8] = StyleCache(nsEditProperty::strong, EmptyString(), EmptyString()); 1.205 + mCachedStyles[9] = StyleCache(nsEditProperty::dfn, EmptyString(), EmptyString()); 1.206 + mCachedStyles[10] = StyleCache(nsEditProperty::code, EmptyString(), EmptyString()); 1.207 + mCachedStyles[11] = StyleCache(nsEditProperty::samp, EmptyString(), EmptyString()); 1.208 + mCachedStyles[12] = StyleCache(nsEditProperty::var, EmptyString(), EmptyString()); 1.209 + mCachedStyles[13] = StyleCache(nsEditProperty::cite, EmptyString(), EmptyString()); 1.210 + mCachedStyles[14] = StyleCache(nsEditProperty::abbr, EmptyString(), EmptyString()); 1.211 + mCachedStyles[15] = StyleCache(nsEditProperty::acronym, EmptyString(), EmptyString()); 1.212 + mCachedStyles[16] = StyleCache(nsEditProperty::cssBackgroundColor, EmptyString(), EmptyString()); 1.213 + mCachedStyles[17] = StyleCache(nsEditProperty::sub, EmptyString(), EmptyString()); 1.214 + mCachedStyles[18] = StyleCache(nsEditProperty::sup, EmptyString(), EmptyString()); 1.215 +} 1.216 + 1.217 +nsHTMLEditRules::~nsHTMLEditRules() 1.218 +{ 1.219 + // remove ourselves as a listener to edit actions 1.220 + // In some cases, we have already been removed by 1.221 + // ~nsHTMLEditor, in which case we will get a null pointer here 1.222 + // which we ignore. But this allows us to add the ability to 1.223 + // switch rule sets on the fly if we want. 1.224 + if (mHTMLEditor) 1.225 + mHTMLEditor->RemoveEditActionListener(this); 1.226 +} 1.227 + 1.228 +/******************************************************** 1.229 + * XPCOM Cruft 1.230 + ********************************************************/ 1.231 + 1.232 +NS_IMPL_ADDREF_INHERITED(nsHTMLEditRules, nsTextEditRules) 1.233 +NS_IMPL_RELEASE_INHERITED(nsHTMLEditRules, nsTextEditRules) 1.234 +NS_IMPL_QUERY_INTERFACE_INHERITED(nsHTMLEditRules, nsTextEditRules, nsIEditActionListener) 1.235 + 1.236 + 1.237 +/******************************************************** 1.238 + * Public methods 1.239 + ********************************************************/ 1.240 + 1.241 +NS_IMETHODIMP 1.242 +nsHTMLEditRules::Init(nsPlaintextEditor *aEditor) 1.243 +{ 1.244 + InitFields(); 1.245 + 1.246 + mHTMLEditor = static_cast<nsHTMLEditor*>(aEditor); 1.247 + nsresult res; 1.248 + 1.249 + // call through to base class Init 1.250 + res = nsTextEditRules::Init(aEditor); 1.251 + NS_ENSURE_SUCCESS(res, res); 1.252 + 1.253 + // cache any prefs we care about 1.254 + static const char kPrefName[] = 1.255 + "editor.html.typing.returnInEmptyListItemClosesList"; 1.256 + nsAdoptingCString returnInEmptyLIKillsList = 1.257 + Preferences::GetCString(kPrefName); 1.258 + 1.259 + // only when "false", becomes FALSE. Otherwise (including empty), TRUE. 1.260 + // XXX Why was this pref designed as a string and not bool? 1.261 + mReturnInEmptyLIKillsList = !returnInEmptyLIKillsList.EqualsLiteral("false"); 1.262 + 1.263 + // make a utility range for use by the listenter 1.264 + nsCOMPtr<nsINode> node = mHTMLEditor->GetRoot(); 1.265 + if (!node) { 1.266 + node = mHTMLEditor->GetDocument(); 1.267 + } 1.268 + 1.269 + NS_ENSURE_STATE(node); 1.270 + 1.271 + mUtilRange = new nsRange(node); 1.272 + 1.273 + // set up mDocChangeRange to be whole doc 1.274 + // temporarily turn off rules sniffing 1.275 + nsAutoLockRulesSniffing lockIt((nsTextEditRules*)this); 1.276 + if (!mDocChangeRange) { 1.277 + mDocChangeRange = new nsRange(node); 1.278 + } 1.279 + 1.280 + if (node->IsElement()) { 1.281 + ErrorResult rv; 1.282 + mDocChangeRange->SelectNode(*node, rv); 1.283 + res = AdjustSpecialBreaks(node); 1.284 + NS_ENSURE_SUCCESS(res, res); 1.285 + } 1.286 + 1.287 + // add ourselves as a listener to edit actions 1.288 + res = mHTMLEditor->AddEditActionListener(this); 1.289 + 1.290 + return res; 1.291 +} 1.292 + 1.293 +NS_IMETHODIMP 1.294 +nsHTMLEditRules::DetachEditor() 1.295 +{ 1.296 + if (mHTMLEditor) { 1.297 + mHTMLEditor->RemoveEditActionListener(this); 1.298 + } 1.299 + mHTMLEditor = nullptr; 1.300 + return nsTextEditRules::DetachEditor(); 1.301 +} 1.302 + 1.303 +NS_IMETHODIMP 1.304 +nsHTMLEditRules::BeforeEdit(EditAction action, 1.305 + nsIEditor::EDirection aDirection) 1.306 +{ 1.307 + if (mLockRulesSniffing) return NS_OK; 1.308 + 1.309 + nsAutoLockRulesSniffing lockIt((nsTextEditRules*)this); 1.310 + mDidExplicitlySetInterline = false; 1.311 + 1.312 + if (!mActionNesting++) 1.313 + { 1.314 + // clear our flag about if just deleted a range 1.315 + mDidRangedDelete = false; 1.316 + 1.317 + // remember where our selection was before edit action took place: 1.318 + 1.319 + // get selection 1.320 + nsCOMPtr<nsISelection> selection; 1.321 + NS_ENSURE_STATE(mHTMLEditor); 1.322 + nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection)); 1.323 + NS_ENSURE_SUCCESS(res, res); 1.324 + 1.325 + // get the selection start location 1.326 + nsCOMPtr<nsIDOMNode> selStartNode, selEndNode; 1.327 + int32_t selOffset; 1.328 + NS_ENSURE_STATE(mHTMLEditor); 1.329 + res = mHTMLEditor->GetStartNodeAndOffset(selection, getter_AddRefs(selStartNode), &selOffset); 1.330 + NS_ENSURE_SUCCESS(res, res); 1.331 + mRangeItem->startNode = selStartNode; 1.332 + mRangeItem->startOffset = selOffset; 1.333 + 1.334 + // get the selection end location 1.335 + NS_ENSURE_STATE(mHTMLEditor); 1.336 + res = mHTMLEditor->GetEndNodeAndOffset(selection, getter_AddRefs(selEndNode), &selOffset); 1.337 + NS_ENSURE_SUCCESS(res, res); 1.338 + mRangeItem->endNode = selEndNode; 1.339 + mRangeItem->endOffset = selOffset; 1.340 + 1.341 + // register this range with range updater to track this as we perturb the doc 1.342 + NS_ENSURE_STATE(mHTMLEditor); 1.343 + (mHTMLEditor->mRangeUpdater).RegisterRangeItem(mRangeItem); 1.344 + 1.345 + // clear deletion state bool 1.346 + mDidDeleteSelection = false; 1.347 + 1.348 + // clear out mDocChangeRange and mUtilRange 1.349 + if(mDocChangeRange) 1.350 + { 1.351 + // clear out our accounting of what changed 1.352 + mDocChangeRange->Reset(); 1.353 + } 1.354 + if(mUtilRange) 1.355 + { 1.356 + // ditto for mUtilRange. 1.357 + mUtilRange->Reset(); 1.358 + } 1.359 + 1.360 + // remember current inline styles for deletion and normal insertion operations 1.361 + if (action == EditAction::insertText || 1.362 + action == EditAction::insertIMEText || 1.363 + action == EditAction::deleteSelection || 1.364 + IsStyleCachePreservingAction(action)) { 1.365 + nsCOMPtr<nsIDOMNode> selNode = selStartNode; 1.366 + if (aDirection == nsIEditor::eNext) 1.367 + selNode = selEndNode; 1.368 + res = CacheInlineStyles(selNode); 1.369 + NS_ENSURE_SUCCESS(res, res); 1.370 + } 1.371 + 1.372 + // Stabilize the document against contenteditable count changes 1.373 + NS_ENSURE_STATE(mHTMLEditor); 1.374 + nsCOMPtr<nsIDOMDocument> doc = mHTMLEditor->GetDOMDocument(); 1.375 + NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED); 1.376 + nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(doc); 1.377 + NS_ENSURE_TRUE(htmlDoc, NS_ERROR_FAILURE); 1.378 + if (htmlDoc->GetEditingState() == nsIHTMLDocument::eContentEditable) { 1.379 + htmlDoc->ChangeContentEditableCount(nullptr, +1); 1.380 + mRestoreContentEditableCount = true; 1.381 + } 1.382 + 1.383 + // check that selection is in subtree defined by body node 1.384 + ConfirmSelectionInBody(); 1.385 + // let rules remember the top level action 1.386 + mTheAction = action; 1.387 + } 1.388 + return NS_OK; 1.389 +} 1.390 + 1.391 + 1.392 +NS_IMETHODIMP 1.393 +nsHTMLEditRules::AfterEdit(EditAction action, 1.394 + nsIEditor::EDirection aDirection) 1.395 +{ 1.396 + if (mLockRulesSniffing) return NS_OK; 1.397 + 1.398 + nsAutoLockRulesSniffing lockIt(this); 1.399 + 1.400 + NS_PRECONDITION(mActionNesting>0, "bad action nesting!"); 1.401 + nsresult res = NS_OK; 1.402 + if (!--mActionNesting) 1.403 + { 1.404 + // do all the tricky stuff 1.405 + res = AfterEditInner(action, aDirection); 1.406 + 1.407 + // free up selectionState range item 1.408 + NS_ENSURE_STATE(mHTMLEditor); 1.409 + (mHTMLEditor->mRangeUpdater).DropRangeItem(mRangeItem); 1.410 + 1.411 + // Reset the contenteditable count to its previous value 1.412 + if (mRestoreContentEditableCount) { 1.413 + NS_ENSURE_STATE(mHTMLEditor); 1.414 + nsCOMPtr<nsIDOMDocument> doc = mHTMLEditor->GetDOMDocument(); 1.415 + NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED); 1.416 + nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(doc); 1.417 + NS_ENSURE_TRUE(htmlDoc, NS_ERROR_FAILURE); 1.418 + if (htmlDoc->GetEditingState() == nsIHTMLDocument::eContentEditable) { 1.419 + htmlDoc->ChangeContentEditableCount(nullptr, -1); 1.420 + } 1.421 + mRestoreContentEditableCount = false; 1.422 + } 1.423 + } 1.424 + 1.425 + return res; 1.426 +} 1.427 + 1.428 + 1.429 +nsresult 1.430 +nsHTMLEditRules::AfterEditInner(EditAction action, 1.431 + nsIEditor::EDirection aDirection) 1.432 +{ 1.433 + ConfirmSelectionInBody(); 1.434 + if (action == EditAction::ignore) return NS_OK; 1.435 + 1.436 + nsCOMPtr<nsISelection>selection; 1.437 + NS_ENSURE_STATE(mHTMLEditor); 1.438 + nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection)); 1.439 + NS_ENSURE_SUCCESS(res, res); 1.440 + 1.441 + nsCOMPtr<nsIDOMNode> rangeStartParent, rangeEndParent; 1.442 + int32_t rangeStartOffset = 0, rangeEndOffset = 0; 1.443 + // do we have a real range to act on? 1.444 + bool bDamagedRange = false; 1.445 + if (mDocChangeRange) 1.446 + { 1.447 + mDocChangeRange->GetStartContainer(getter_AddRefs(rangeStartParent)); 1.448 + mDocChangeRange->GetEndContainer(getter_AddRefs(rangeEndParent)); 1.449 + mDocChangeRange->GetStartOffset(&rangeStartOffset); 1.450 + mDocChangeRange->GetEndOffset(&rangeEndOffset); 1.451 + if (rangeStartParent && rangeEndParent) 1.452 + bDamagedRange = true; 1.453 + } 1.454 + 1.455 + if (bDamagedRange && !((action == EditAction::undo) || (action == EditAction::redo))) 1.456 + { 1.457 + // don't let any txns in here move the selection around behind our back. 1.458 + // Note that this won't prevent explicit selection setting from working. 1.459 + NS_ENSURE_STATE(mHTMLEditor); 1.460 + nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor); 1.461 + 1.462 + // expand the "changed doc range" as needed 1.463 + res = PromoteRange(mDocChangeRange, action); 1.464 + NS_ENSURE_SUCCESS(res, res); 1.465 + 1.466 + // if we did a ranged deletion, make sure we have a place to put caret. 1.467 + // Note we only want to do this if the overall operation was deletion, 1.468 + // not if deletion was done along the way for EditAction::loadHTML, EditAction::insertText, etc. 1.469 + // That's why this is here rather than DidDeleteSelection(). 1.470 + if ((action == EditAction::deleteSelection) && mDidRangedDelete) 1.471 + { 1.472 + res = InsertBRIfNeeded(selection); 1.473 + NS_ENSURE_SUCCESS(res, res); 1.474 + } 1.475 + 1.476 + // add in any needed <br>s, and remove any unneeded ones. 1.477 + res = AdjustSpecialBreaks(); 1.478 + NS_ENSURE_SUCCESS(res, res); 1.479 + 1.480 + // merge any adjacent text nodes 1.481 + if ( (action != EditAction::insertText && 1.482 + action != EditAction::insertIMEText) ) 1.483 + { 1.484 + NS_ENSURE_STATE(mHTMLEditor); 1.485 + res = mHTMLEditor->CollapseAdjacentTextNodes(mDocChangeRange); 1.486 + NS_ENSURE_SUCCESS(res, res); 1.487 + } 1.488 + 1.489 + // clean up any empty nodes in the selection 1.490 + res = RemoveEmptyNodes(); 1.491 + NS_ENSURE_SUCCESS(res, res); 1.492 + 1.493 + // attempt to transform any unneeded nbsp's into spaces after doing various operations 1.494 + if ((action == EditAction::insertText) || 1.495 + (action == EditAction::insertIMEText) || 1.496 + (action == EditAction::deleteSelection) || 1.497 + (action == EditAction::insertBreak) || 1.498 + (action == EditAction::htmlPaste || 1.499 + (action == EditAction::loadHTML))) 1.500 + { 1.501 + res = AdjustWhitespace(selection); 1.502 + NS_ENSURE_SUCCESS(res, res); 1.503 + 1.504 + // also do this for original selection endpoints. 1.505 + NS_ENSURE_STATE(mHTMLEditor); 1.506 + nsWSRunObject(mHTMLEditor, mRangeItem->startNode, 1.507 + mRangeItem->startOffset).AdjustWhitespace(); 1.508 + // we only need to handle old selection endpoint if it was different from start 1.509 + if (mRangeItem->startNode != mRangeItem->endNode || 1.510 + mRangeItem->startOffset != mRangeItem->endOffset) { 1.511 + NS_ENSURE_STATE(mHTMLEditor); 1.512 + nsWSRunObject(mHTMLEditor, mRangeItem->endNode, 1.513 + mRangeItem->endOffset).AdjustWhitespace(); 1.514 + } 1.515 + } 1.516 + 1.517 + // if we created a new block, make sure selection lands in it 1.518 + if (mNewBlock) 1.519 + { 1.520 + res = PinSelectionToNewBlock(selection); 1.521 + mNewBlock = 0; 1.522 + } 1.523 + 1.524 + // adjust selection for insert text, html paste, and delete actions 1.525 + if ((action == EditAction::insertText) || 1.526 + (action == EditAction::insertIMEText) || 1.527 + (action == EditAction::deleteSelection) || 1.528 + (action == EditAction::insertBreak) || 1.529 + (action == EditAction::htmlPaste || 1.530 + (action == EditAction::loadHTML))) 1.531 + { 1.532 + res = AdjustSelection(selection, aDirection); 1.533 + NS_ENSURE_SUCCESS(res, res); 1.534 + } 1.535 + 1.536 + // check for any styles which were removed inappropriately 1.537 + if (action == EditAction::insertText || 1.538 + action == EditAction::insertIMEText || 1.539 + action == EditAction::deleteSelection || 1.540 + IsStyleCachePreservingAction(action)) { 1.541 + NS_ENSURE_STATE(mHTMLEditor); 1.542 + mHTMLEditor->mTypeInState->UpdateSelState(selection); 1.543 + res = ReapplyCachedStyles(); 1.544 + NS_ENSURE_SUCCESS(res, res); 1.545 + ClearCachedStyles(); 1.546 + } 1.547 + } 1.548 + 1.549 + NS_ENSURE_STATE(mHTMLEditor); 1.550 + 1.551 + res = mHTMLEditor->HandleInlineSpellCheck(action, selection, 1.552 + mRangeItem->startNode, 1.553 + mRangeItem->startOffset, 1.554 + rangeStartParent, rangeStartOffset, 1.555 + rangeEndParent, rangeEndOffset); 1.556 + NS_ENSURE_SUCCESS(res, res); 1.557 + 1.558 + // detect empty doc 1.559 + res = CreateBogusNodeIfNeeded(selection); 1.560 + 1.561 + // adjust selection HINT if needed 1.562 + NS_ENSURE_SUCCESS(res, res); 1.563 + 1.564 + if (!mDidExplicitlySetInterline) 1.565 + { 1.566 + res = CheckInterlinePosition(selection); 1.567 + } 1.568 + 1.569 + return res; 1.570 +} 1.571 + 1.572 + 1.573 +NS_IMETHODIMP 1.574 +nsHTMLEditRules::WillDoAction(Selection* aSelection, 1.575 + nsRulesInfo* aInfo, 1.576 + bool* aCancel, 1.577 + bool* aHandled) 1.578 +{ 1.579 + MOZ_ASSERT(aInfo && aCancel && aHandled); 1.580 + 1.581 + *aCancel = false; 1.582 + *aHandled = false; 1.583 + 1.584 + // my kingdom for dynamic cast 1.585 + nsTextRulesInfo *info = static_cast<nsTextRulesInfo*>(aInfo); 1.586 + 1.587 + // Deal with actions for which we don't need to check whether the selection is 1.588 + // editable. 1.589 + if (info->action == EditAction::outputText || 1.590 + info->action == EditAction::undo || 1.591 + info->action == EditAction::redo) { 1.592 + return nsTextEditRules::WillDoAction(aSelection, aInfo, aCancel, aHandled); 1.593 + } 1.594 + 1.595 + // Nothing to do if there's no selection to act on 1.596 + if (!aSelection) { 1.597 + return NS_OK; 1.598 + } 1.599 + NS_ENSURE_TRUE(aSelection->GetRangeCount(), NS_OK); 1.600 + 1.601 + nsRefPtr<nsRange> range = aSelection->GetRangeAt(0); 1.602 + nsCOMPtr<nsINode> selStartNode = range->GetStartParent(); 1.603 + 1.604 + NS_ENSURE_STATE(mHTMLEditor); 1.605 + if (!mHTMLEditor->IsModifiableNode(selStartNode)) { 1.606 + *aCancel = true; 1.607 + return NS_OK; 1.608 + } 1.609 + 1.610 + nsCOMPtr<nsINode> selEndNode = range->GetEndParent(); 1.611 + 1.612 + if (selStartNode != selEndNode) { 1.613 + NS_ENSURE_STATE(mHTMLEditor); 1.614 + if (!mHTMLEditor->IsModifiableNode(selEndNode)) { 1.615 + *aCancel = true; 1.616 + return NS_OK; 1.617 + } 1.618 + 1.619 + NS_ENSURE_STATE(mHTMLEditor); 1.620 + if (!mHTMLEditor->IsModifiableNode(range->GetCommonAncestor())) { 1.621 + *aCancel = true; 1.622 + return NS_OK; 1.623 + } 1.624 + } 1.625 + 1.626 + switch (info->action) { 1.627 + case EditAction::insertText: 1.628 + case EditAction::insertIMEText: 1.629 + return WillInsertText(info->action, aSelection, aCancel, aHandled, 1.630 + info->inString, info->outString, info->maxLength); 1.631 + case EditAction::loadHTML: 1.632 + return WillLoadHTML(aSelection, aCancel); 1.633 + case EditAction::insertBreak: 1.634 + return WillInsertBreak(aSelection, aCancel, aHandled); 1.635 + case EditAction::deleteSelection: 1.636 + return WillDeleteSelection(aSelection, info->collapsedAction, 1.637 + info->stripWrappers, aCancel, aHandled); 1.638 + case EditAction::makeList: 1.639 + return WillMakeList(aSelection, info->blockType, info->entireList, 1.640 + info->bulletType, aCancel, aHandled); 1.641 + case EditAction::indent: 1.642 + return WillIndent(aSelection, aCancel, aHandled); 1.643 + case EditAction::outdent: 1.644 + return WillOutdent(aSelection, aCancel, aHandled); 1.645 + case EditAction::setAbsolutePosition: 1.646 + return WillAbsolutePosition(aSelection, aCancel, aHandled); 1.647 + case EditAction::removeAbsolutePosition: 1.648 + return WillRemoveAbsolutePosition(aSelection, aCancel, aHandled); 1.649 + case EditAction::align: 1.650 + return WillAlign(aSelection, info->alignType, aCancel, aHandled); 1.651 + case EditAction::makeBasicBlock: 1.652 + return WillMakeBasicBlock(aSelection, info->blockType, aCancel, aHandled); 1.653 + case EditAction::removeList: 1.654 + return WillRemoveList(aSelection, info->bOrdered, aCancel, aHandled); 1.655 + case EditAction::makeDefListItem: 1.656 + return WillMakeDefListItem(aSelection, info->blockType, info->entireList, 1.657 + aCancel, aHandled); 1.658 + case EditAction::insertElement: 1.659 + return WillInsert(aSelection, aCancel); 1.660 + case EditAction::decreaseZIndex: 1.661 + return WillRelativeChangeZIndex(aSelection, -1, aCancel, aHandled); 1.662 + case EditAction::increaseZIndex: 1.663 + return WillRelativeChangeZIndex(aSelection, 1, aCancel, aHandled); 1.664 + default: 1.665 + return nsTextEditRules::WillDoAction(aSelection, aInfo, 1.666 + aCancel, aHandled); 1.667 + } 1.668 +} 1.669 + 1.670 + 1.671 +NS_IMETHODIMP 1.672 +nsHTMLEditRules::DidDoAction(nsISelection *aSelection, 1.673 + nsRulesInfo *aInfo, nsresult aResult) 1.674 +{ 1.675 + nsTextRulesInfo *info = static_cast<nsTextRulesInfo*>(aInfo); 1.676 + switch (info->action) 1.677 + { 1.678 + case EditAction::insertBreak: 1.679 + return DidInsertBreak(aSelection, aResult); 1.680 + case EditAction::deleteSelection: 1.681 + return DidDeleteSelection(aSelection, info->collapsedAction, aResult); 1.682 + case EditAction::makeBasicBlock: 1.683 + case EditAction::indent: 1.684 + case EditAction::outdent: 1.685 + case EditAction::align: 1.686 + return DidMakeBasicBlock(aSelection, aInfo, aResult); 1.687 + case EditAction::setAbsolutePosition: { 1.688 + nsresult rv = DidMakeBasicBlock(aSelection, aInfo, aResult); 1.689 + NS_ENSURE_SUCCESS(rv, rv); 1.690 + return DidAbsolutePosition(); 1.691 + } 1.692 + default: 1.693 + // pass thru to nsTextEditRules 1.694 + return nsTextEditRules::DidDoAction(aSelection, aInfo, aResult); 1.695 + } 1.696 +} 1.697 + 1.698 +nsresult 1.699 +nsHTMLEditRules::GetListState(bool *aMixed, bool *aOL, bool *aUL, bool *aDL) 1.700 +{ 1.701 + NS_ENSURE_TRUE(aMixed && aOL && aUL && aDL, NS_ERROR_NULL_POINTER); 1.702 + *aMixed = false; 1.703 + *aOL = false; 1.704 + *aUL = false; 1.705 + *aDL = false; 1.706 + bool bNonList = false; 1.707 + 1.708 + nsCOMArray<nsIDOMNode> arrayOfNodes; 1.709 + nsresult res = GetListActionNodes(arrayOfNodes, false, true); 1.710 + NS_ENSURE_SUCCESS(res, res); 1.711 + 1.712 + // Examine list type for nodes in selection. 1.713 + int32_t listCount = arrayOfNodes.Count(); 1.714 + for (int32_t i = listCount - 1; i >= 0; --i) { 1.715 + nsIDOMNode* curDOMNode = arrayOfNodes[i]; 1.716 + nsCOMPtr<dom::Element> curElement = do_QueryInterface(curDOMNode); 1.717 + 1.718 + if (!curElement) { 1.719 + bNonList = true; 1.720 + } else if (curElement->IsHTML(nsGkAtoms::ul)) { 1.721 + *aUL = true; 1.722 + } else if (curElement->IsHTML(nsGkAtoms::ol)) { 1.723 + *aOL = true; 1.724 + } else if (curElement->IsHTML(nsGkAtoms::li)) { 1.725 + if (dom::Element* parent = curElement->GetParentElement()) { 1.726 + if (parent->IsHTML(nsGkAtoms::ul)) { 1.727 + *aUL = true; 1.728 + } else if (parent->IsHTML(nsGkAtoms::ol)) { 1.729 + *aOL = true; 1.730 + } 1.731 + } 1.732 + } else if (curElement->IsHTML(nsGkAtoms::dl) || 1.733 + curElement->IsHTML(nsGkAtoms::dt) || 1.734 + curElement->IsHTML(nsGkAtoms::dd)) { 1.735 + *aDL = true; 1.736 + } else { 1.737 + bNonList = true; 1.738 + } 1.739 + } 1.740 + 1.741 + // hokey arithmetic with booleans 1.742 + if ((*aUL + *aOL + *aDL + bNonList) > 1) { 1.743 + *aMixed = true; 1.744 + } 1.745 + 1.746 + return NS_OK; 1.747 +} 1.748 + 1.749 +nsresult 1.750 +nsHTMLEditRules::GetListItemState(bool *aMixed, bool *aLI, bool *aDT, bool *aDD) 1.751 +{ 1.752 + NS_ENSURE_TRUE(aMixed && aLI && aDT && aDD, NS_ERROR_NULL_POINTER); 1.753 + *aMixed = false; 1.754 + *aLI = false; 1.755 + *aDT = false; 1.756 + *aDD = false; 1.757 + bool bNonList = false; 1.758 + 1.759 + nsCOMArray<nsIDOMNode> arrayOfNodes; 1.760 + nsresult res = GetListActionNodes(arrayOfNodes, false, true); 1.761 + NS_ENSURE_SUCCESS(res, res); 1.762 + 1.763 + // examine list type for nodes in selection 1.764 + int32_t listCount = arrayOfNodes.Count(); 1.765 + for (int32_t i = listCount - 1; i >= 0; --i) { 1.766 + nsIDOMNode* curNode = arrayOfNodes[i]; 1.767 + nsCOMPtr<dom::Element> element = do_QueryInterface(curNode); 1.768 + if (!element) { 1.769 + bNonList = true; 1.770 + } else if (element->IsHTML(nsGkAtoms::ul) || 1.771 + element->IsHTML(nsGkAtoms::ol) || 1.772 + element->IsHTML(nsGkAtoms::li)) { 1.773 + *aLI = true; 1.774 + } else if (element->IsHTML(nsGkAtoms::dt)) { 1.775 + *aDT = true; 1.776 + } else if (element->IsHTML(nsGkAtoms::dd)) { 1.777 + *aDD = true; 1.778 + } else if (element->IsHTML(nsGkAtoms::dl)) { 1.779 + // need to look inside dl and see which types of items it has 1.780 + bool bDT, bDD; 1.781 + GetDefinitionListItemTypes(element, &bDT, &bDD); 1.782 + *aDT |= bDT; 1.783 + *aDD |= bDD; 1.784 + } else { 1.785 + bNonList = true; 1.786 + } 1.787 + } 1.788 + 1.789 + // hokey arithmetic with booleans 1.790 + if ( (*aDT + *aDD + bNonList) > 1) *aMixed = true; 1.791 + 1.792 + return NS_OK; 1.793 +} 1.794 + 1.795 +nsresult 1.796 +nsHTMLEditRules::GetAlignment(bool *aMixed, nsIHTMLEditor::EAlignment *aAlign) 1.797 +{ 1.798 + // for now, just return first alignment. we'll lie about 1.799 + // if it's mixed. This is for efficiency 1.800 + // given that our current ui doesn't care if it's mixed. 1.801 + // cmanske: NOT TRUE! We would like to pay attention to mixed state 1.802 + // in Format | Align submenu! 1.803 + 1.804 + // this routine assumes that alignment is done ONLY via divs 1.805 + 1.806 + // default alignment is left 1.807 + NS_ENSURE_TRUE(aMixed && aAlign, NS_ERROR_NULL_POINTER); 1.808 + *aMixed = false; 1.809 + *aAlign = nsIHTMLEditor::eLeft; 1.810 + 1.811 + // get selection 1.812 + nsCOMPtr<nsISelection>selection; 1.813 + NS_ENSURE_STATE(mHTMLEditor); 1.814 + nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection)); 1.815 + NS_ENSURE_SUCCESS(res, res); 1.816 + 1.817 + // get selection location 1.818 + NS_ENSURE_STATE(mHTMLEditor); 1.819 + nsCOMPtr<nsIDOMElement> rootElem = do_QueryInterface(mHTMLEditor->GetRoot()); 1.820 + NS_ENSURE_TRUE(rootElem, NS_ERROR_FAILURE); 1.821 + 1.822 + int32_t offset, rootOffset; 1.823 + nsCOMPtr<nsIDOMNode> parent = nsEditor::GetNodeLocation(rootElem, &rootOffset); 1.824 + NS_ENSURE_STATE(mHTMLEditor); 1.825 + res = mHTMLEditor->GetStartNodeAndOffset(selection, getter_AddRefs(parent), &offset); 1.826 + NS_ENSURE_SUCCESS(res, res); 1.827 + 1.828 + // is the selection collapsed? 1.829 + nsCOMPtr<nsIDOMNode> nodeToExamine; 1.830 + if (selection->Collapsed()) { 1.831 + // if it is, we want to look at 'parent' and its ancestors 1.832 + // for divs with alignment on them 1.833 + nodeToExamine = parent; 1.834 + } 1.835 + else if (!mHTMLEditor) { 1.836 + return NS_ERROR_UNEXPECTED; 1.837 + } 1.838 + else if (mHTMLEditor->IsTextNode(parent)) 1.839 + { 1.840 + // if we are in a text node, then that is the node of interest 1.841 + nodeToExamine = parent; 1.842 + } 1.843 + else if (nsEditor::NodeIsType(parent, nsEditProperty::html) && 1.844 + offset == rootOffset) 1.845 + { 1.846 + // if we have selected the body, let's look at the first editable node 1.847 + NS_ENSURE_STATE(mHTMLEditor); 1.848 + mHTMLEditor->GetNextNode(parent, offset, true, address_of(nodeToExamine)); 1.849 + } 1.850 + else 1.851 + { 1.852 + nsCOMArray<nsIDOMRange> arrayOfRanges; 1.853 + res = GetPromotedRanges(selection, arrayOfRanges, EditAction::align); 1.854 + NS_ENSURE_SUCCESS(res, res); 1.855 + 1.856 + // use these ranges to construct a list of nodes to act on. 1.857 + nsCOMArray<nsIDOMNode> arrayOfNodes; 1.858 + res = GetNodesForOperation(arrayOfRanges, arrayOfNodes, 1.859 + EditAction::align, true); 1.860 + NS_ENSURE_SUCCESS(res, res); 1.861 + nodeToExamine = arrayOfNodes.SafeObjectAt(0); 1.862 + } 1.863 + 1.864 + NS_ENSURE_TRUE(nodeToExamine, NS_ERROR_NULL_POINTER); 1.865 + 1.866 + NS_NAMED_LITERAL_STRING(typeAttrName, "align"); 1.867 + nsIAtom *dummyProperty = nullptr; 1.868 + nsCOMPtr<nsIDOMNode> blockParent; 1.869 + NS_ENSURE_STATE(mHTMLEditor); 1.870 + if (mHTMLEditor->IsBlockNode(nodeToExamine)) 1.871 + blockParent = nodeToExamine; 1.872 + else { 1.873 + NS_ENSURE_STATE(mHTMLEditor); 1.874 + blockParent = mHTMLEditor->GetBlockNodeParent(nodeToExamine); 1.875 + } 1.876 + 1.877 + NS_ENSURE_TRUE(blockParent, NS_ERROR_FAILURE); 1.878 + 1.879 + NS_ENSURE_STATE(mHTMLEditor); 1.880 + if (mHTMLEditor->IsCSSEnabled()) 1.881 + { 1.882 + nsCOMPtr<nsIContent> blockParentContent = do_QueryInterface(blockParent); 1.883 + NS_ENSURE_STATE(mHTMLEditor); 1.884 + if (blockParentContent && 1.885 + mHTMLEditor->mHTMLCSSUtils->IsCSSEditableProperty(blockParentContent, dummyProperty, &typeAttrName)) 1.886 + { 1.887 + // we are in CSS mode and we know how to align this element with CSS 1.888 + nsAutoString value; 1.889 + // let's get the value(s) of text-align or margin-left/margin-right 1.890 + NS_ENSURE_STATE(mHTMLEditor); 1.891 + mHTMLEditor->mHTMLCSSUtils->GetCSSEquivalentToHTMLInlineStyleSet( 1.892 + blockParentContent, dummyProperty, &typeAttrName, value, 1.893 + nsHTMLCSSUtils::eComputed); 1.894 + if (value.EqualsLiteral("center") || 1.895 + value.EqualsLiteral("-moz-center") || 1.896 + value.EqualsLiteral("auto auto")) 1.897 + { 1.898 + *aAlign = nsIHTMLEditor::eCenter; 1.899 + return NS_OK; 1.900 + } 1.901 + if (value.EqualsLiteral("right") || 1.902 + value.EqualsLiteral("-moz-right") || 1.903 + value.EqualsLiteral("auto 0px")) 1.904 + { 1.905 + *aAlign = nsIHTMLEditor::eRight; 1.906 + return NS_OK; 1.907 + } 1.908 + if (value.EqualsLiteral("justify")) 1.909 + { 1.910 + *aAlign = nsIHTMLEditor::eJustify; 1.911 + return NS_OK; 1.912 + } 1.913 + *aAlign = nsIHTMLEditor::eLeft; 1.914 + return NS_OK; 1.915 + } 1.916 + } 1.917 + 1.918 + // check up the ladder for divs with alignment 1.919 + nsCOMPtr<nsIDOMNode> temp = nodeToExamine; 1.920 + bool isFirstNodeToExamine = true; 1.921 + while (nodeToExamine) 1.922 + { 1.923 + if (!isFirstNodeToExamine && nsHTMLEditUtils::IsTable(nodeToExamine)) 1.924 + { 1.925 + // the node to examine is a table and this is not the first node 1.926 + // we examine; let's break here to materialize the 'inline-block' 1.927 + // behaviour of html tables regarding to text alignment 1.928 + return NS_OK; 1.929 + } 1.930 + if (nsHTMLEditUtils::SupportsAlignAttr(nodeToExamine)) 1.931 + { 1.932 + // check for alignment 1.933 + nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(nodeToExamine); 1.934 + if (elem) 1.935 + { 1.936 + nsAutoString typeAttrVal; 1.937 + res = elem->GetAttribute(NS_LITERAL_STRING("align"), typeAttrVal); 1.938 + ToLowerCase(typeAttrVal); 1.939 + if (NS_SUCCEEDED(res) && typeAttrVal.Length()) 1.940 + { 1.941 + if (typeAttrVal.EqualsLiteral("center")) 1.942 + *aAlign = nsIHTMLEditor::eCenter; 1.943 + else if (typeAttrVal.EqualsLiteral("right")) 1.944 + *aAlign = nsIHTMLEditor::eRight; 1.945 + else if (typeAttrVal.EqualsLiteral("justify")) 1.946 + *aAlign = nsIHTMLEditor::eJustify; 1.947 + else 1.948 + *aAlign = nsIHTMLEditor::eLeft; 1.949 + return res; 1.950 + } 1.951 + } 1.952 + } 1.953 + isFirstNodeToExamine = false; 1.954 + res = nodeToExamine->GetParentNode(getter_AddRefs(temp)); 1.955 + if (NS_FAILED(res)) temp = nullptr; 1.956 + nodeToExamine = temp; 1.957 + } 1.958 + return NS_OK; 1.959 +} 1.960 + 1.961 +nsIAtom* MarginPropertyAtomForIndent(nsHTMLCSSUtils* aHTMLCSSUtils, nsIDOMNode* aNode) { 1.962 + nsAutoString direction; 1.963 + aHTMLCSSUtils->GetComputedProperty(aNode, nsEditProperty::cssDirection, direction); 1.964 + return direction.EqualsLiteral("rtl") ? 1.965 + nsEditProperty::cssMarginRight : nsEditProperty::cssMarginLeft; 1.966 +} 1.967 + 1.968 +nsresult 1.969 +nsHTMLEditRules::GetIndentState(bool *aCanIndent, bool *aCanOutdent) 1.970 +{ 1.971 + NS_ENSURE_TRUE(aCanIndent && aCanOutdent, NS_ERROR_FAILURE); 1.972 + *aCanIndent = true; 1.973 + *aCanOutdent = false; 1.974 + 1.975 + // get selection 1.976 + nsCOMPtr<nsISelection>selection; 1.977 + NS_ENSURE_STATE(mHTMLEditor); 1.978 + nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection)); 1.979 + NS_ENSURE_SUCCESS(res, res); 1.980 + nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection)); 1.981 + NS_ENSURE_TRUE(selPriv, NS_ERROR_FAILURE); 1.982 + 1.983 + // contruct a list of nodes to act on. 1.984 + nsCOMArray<nsIDOMNode> arrayOfNodes; 1.985 + res = GetNodesFromSelection(selection, EditAction::indent, 1.986 + arrayOfNodes, true); 1.987 + NS_ENSURE_SUCCESS(res, res); 1.988 + 1.989 + // examine nodes in selection for blockquotes or list elements; 1.990 + // these we can outdent. Note that we return true for canOutdent 1.991 + // if *any* of the selection is outdentable, rather than all of it. 1.992 + int32_t listCount = arrayOfNodes.Count(); 1.993 + int32_t i; 1.994 + NS_ENSURE_STATE(mHTMLEditor); 1.995 + bool useCSS = mHTMLEditor->IsCSSEnabled(); 1.996 + for (i=listCount-1; i>=0; i--) 1.997 + { 1.998 + nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i]; 1.999 + 1.1000 + if (nsHTMLEditUtils::IsNodeThatCanOutdent(curNode)) 1.1001 + { 1.1002 + *aCanOutdent = true; 1.1003 + break; 1.1004 + } 1.1005 + else if (useCSS) { 1.1006 + // we are in CSS mode, indentation is done using the margin-left (or margin-right) property 1.1007 + NS_ENSURE_STATE(mHTMLEditor); 1.1008 + nsIAtom* marginProperty = MarginPropertyAtomForIndent(mHTMLEditor->mHTMLCSSUtils, curNode); 1.1009 + nsAutoString value; 1.1010 + // retrieve its specified value 1.1011 + NS_ENSURE_STATE(mHTMLEditor); 1.1012 + mHTMLEditor->mHTMLCSSUtils->GetSpecifiedProperty(curNode, marginProperty, value); 1.1013 + float f; 1.1014 + nsCOMPtr<nsIAtom> unit; 1.1015 + // get its number part and its unit 1.1016 + NS_ENSURE_STATE(mHTMLEditor); 1.1017 + mHTMLEditor->mHTMLCSSUtils->ParseLength(value, &f, getter_AddRefs(unit)); 1.1018 + // if the number part is strictly positive, outdent is possible 1.1019 + if (0 < f) { 1.1020 + *aCanOutdent = true; 1.1021 + break; 1.1022 + } 1.1023 + } 1.1024 + } 1.1025 + 1.1026 + if (!*aCanOutdent) 1.1027 + { 1.1028 + // if we haven't found something to outdent yet, also check the parents 1.1029 + // of selection endpoints. We might have a blockquote or list item 1.1030 + // in the parent hierarchy. 1.1031 + 1.1032 + // gather up info we need for test 1.1033 + NS_ENSURE_STATE(mHTMLEditor); 1.1034 + nsCOMPtr<nsIDOMNode> parent, tmp, root = do_QueryInterface(mHTMLEditor->GetRoot()); 1.1035 + NS_ENSURE_TRUE(root, NS_ERROR_NULL_POINTER); 1.1036 + nsCOMPtr<nsISelection> selection; 1.1037 + int32_t selOffset; 1.1038 + NS_ENSURE_STATE(mHTMLEditor); 1.1039 + res = mHTMLEditor->GetSelection(getter_AddRefs(selection)); 1.1040 + NS_ENSURE_SUCCESS(res, res); 1.1041 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.1042 + 1.1043 + // test start parent hierarchy 1.1044 + NS_ENSURE_STATE(mHTMLEditor); 1.1045 + res = mHTMLEditor->GetStartNodeAndOffset(selection, getter_AddRefs(parent), &selOffset); 1.1046 + NS_ENSURE_SUCCESS(res, res); 1.1047 + while (parent && (parent!=root)) 1.1048 + { 1.1049 + if (nsHTMLEditUtils::IsNodeThatCanOutdent(parent)) 1.1050 + { 1.1051 + *aCanOutdent = true; 1.1052 + break; 1.1053 + } 1.1054 + tmp=parent; 1.1055 + tmp->GetParentNode(getter_AddRefs(parent)); 1.1056 + } 1.1057 + 1.1058 + // test end parent hierarchy 1.1059 + NS_ENSURE_STATE(mHTMLEditor); 1.1060 + res = mHTMLEditor->GetEndNodeAndOffset(selection, getter_AddRefs(parent), &selOffset); 1.1061 + NS_ENSURE_SUCCESS(res, res); 1.1062 + while (parent && (parent!=root)) 1.1063 + { 1.1064 + if (nsHTMLEditUtils::IsNodeThatCanOutdent(parent)) 1.1065 + { 1.1066 + *aCanOutdent = true; 1.1067 + break; 1.1068 + } 1.1069 + tmp=parent; 1.1070 + tmp->GetParentNode(getter_AddRefs(parent)); 1.1071 + } 1.1072 + } 1.1073 + return res; 1.1074 +} 1.1075 + 1.1076 + 1.1077 +nsresult 1.1078 +nsHTMLEditRules::GetParagraphState(bool *aMixed, nsAString &outFormat) 1.1079 +{ 1.1080 + // This routine is *heavily* tied to our ui choices in the paragraph 1.1081 + // style popup. I can't see a way around that. 1.1082 + NS_ENSURE_TRUE(aMixed, NS_ERROR_NULL_POINTER); 1.1083 + *aMixed = true; 1.1084 + outFormat.Truncate(0); 1.1085 + 1.1086 + bool bMixed = false; 1.1087 + // using "x" as an uninitialized value, since "" is meaningful 1.1088 + nsAutoString formatStr(NS_LITERAL_STRING("x")); 1.1089 + 1.1090 + nsCOMArray<nsIDOMNode> arrayOfNodes; 1.1091 + nsresult res = GetParagraphFormatNodes(arrayOfNodes, true); 1.1092 + NS_ENSURE_SUCCESS(res, res); 1.1093 + 1.1094 + // post process list. We need to replace any block nodes that are not format 1.1095 + // nodes with their content. This is so we only have to look "up" the hierarchy 1.1096 + // to find format nodes, instead of both up and down. 1.1097 + int32_t listCount = arrayOfNodes.Count(); 1.1098 + int32_t i; 1.1099 + for (i=listCount-1; i>=0; i--) 1.1100 + { 1.1101 + nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i]; 1.1102 + nsAutoString format; 1.1103 + // if it is a known format node we have it easy 1.1104 + if (IsBlockNode(curNode) && !nsHTMLEditUtils::IsFormatNode(curNode)) 1.1105 + { 1.1106 + // arrayOfNodes.RemoveObject(curNode); 1.1107 + res = AppendInnerFormatNodes(arrayOfNodes, curNode); 1.1108 + NS_ENSURE_SUCCESS(res, res); 1.1109 + } 1.1110 + } 1.1111 + 1.1112 + // we might have an empty node list. if so, find selection parent 1.1113 + // and put that on the list 1.1114 + listCount = arrayOfNodes.Count(); 1.1115 + if (!listCount) 1.1116 + { 1.1117 + nsCOMPtr<nsIDOMNode> selNode; 1.1118 + int32_t selOffset; 1.1119 + nsCOMPtr<nsISelection>selection; 1.1120 + NS_ENSURE_STATE(mHTMLEditor); 1.1121 + res = mHTMLEditor->GetSelection(getter_AddRefs(selection)); 1.1122 + NS_ENSURE_SUCCESS(res, res); 1.1123 + NS_ENSURE_STATE(mHTMLEditor); 1.1124 + res = mHTMLEditor->GetStartNodeAndOffset(selection, getter_AddRefs(selNode), &selOffset); 1.1125 + NS_ENSURE_SUCCESS(res, res); 1.1126 + NS_ENSURE_TRUE(selNode, NS_ERROR_NULL_POINTER); 1.1127 + arrayOfNodes.AppendObject(selNode); 1.1128 + listCount = 1; 1.1129 + } 1.1130 + 1.1131 + // remember root node 1.1132 + NS_ENSURE_STATE(mHTMLEditor); 1.1133 + nsCOMPtr<nsIDOMElement> rootElem = do_QueryInterface(mHTMLEditor->GetRoot()); 1.1134 + NS_ENSURE_TRUE(rootElem, NS_ERROR_NULL_POINTER); 1.1135 + 1.1136 + // loop through the nodes in selection and examine their paragraph format 1.1137 + for (i=listCount-1; i>=0; i--) 1.1138 + { 1.1139 + nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i]; 1.1140 + nsAutoString format; 1.1141 + // if it is a known format node we have it easy 1.1142 + if (nsHTMLEditUtils::IsFormatNode(curNode)) 1.1143 + GetFormatString(curNode, format); 1.1144 + else if (IsBlockNode(curNode)) 1.1145 + { 1.1146 + // this is a div or some other non-format block. 1.1147 + // we should ignore it. Its children were appended to this list 1.1148 + // by AppendInnerFormatNodes() call above. We will get needed 1.1149 + // info when we examine them instead. 1.1150 + continue; 1.1151 + } 1.1152 + else 1.1153 + { 1.1154 + nsCOMPtr<nsIDOMNode> node, tmp = curNode; 1.1155 + tmp->GetParentNode(getter_AddRefs(node)); 1.1156 + while (node) 1.1157 + { 1.1158 + if (node == rootElem) 1.1159 + { 1.1160 + format.Truncate(0); 1.1161 + break; 1.1162 + } 1.1163 + else if (nsHTMLEditUtils::IsFormatNode(node)) 1.1164 + { 1.1165 + GetFormatString(node, format); 1.1166 + break; 1.1167 + } 1.1168 + // else keep looking up 1.1169 + tmp = node; 1.1170 + tmp->GetParentNode(getter_AddRefs(node)); 1.1171 + } 1.1172 + } 1.1173 + 1.1174 + // if this is the first node, we've found, remember it as the format 1.1175 + if (formatStr.EqualsLiteral("x")) 1.1176 + formatStr = format; 1.1177 + // else make sure it matches previously found format 1.1178 + else if (format != formatStr) 1.1179 + { 1.1180 + bMixed = true; 1.1181 + break; 1.1182 + } 1.1183 + } 1.1184 + 1.1185 + *aMixed = bMixed; 1.1186 + outFormat = formatStr; 1.1187 + return res; 1.1188 +} 1.1189 + 1.1190 +nsresult 1.1191 +nsHTMLEditRules::AppendInnerFormatNodes(nsCOMArray<nsIDOMNode>& aArray, 1.1192 + nsIDOMNode *aNode) 1.1193 +{ 1.1194 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.1195 + NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER); 1.1196 + 1.1197 + return AppendInnerFormatNodes(aArray, node); 1.1198 +} 1.1199 + 1.1200 +nsresult 1.1201 +nsHTMLEditRules::AppendInnerFormatNodes(nsCOMArray<nsIDOMNode>& aArray, 1.1202 + nsINode* aNode) 1.1203 +{ 1.1204 + MOZ_ASSERT(aNode); 1.1205 + 1.1206 + // we only need to place any one inline inside this node onto 1.1207 + // the list. They are all the same for purposes of determining 1.1208 + // paragraph style. We use foundInline to track this as we are 1.1209 + // going through the children in the loop below. 1.1210 + bool foundInline = false; 1.1211 + for (nsIContent* child = aNode->GetFirstChild(); 1.1212 + child; 1.1213 + child = child->GetNextSibling()) { 1.1214 + bool isBlock = IsBlockNode(child->AsDOMNode()); 1.1215 + bool isFormat = nsHTMLEditUtils::IsFormatNode(child); 1.1216 + if (isBlock && !isFormat) { 1.1217 + // if it's a div, etc, recurse 1.1218 + AppendInnerFormatNodes(aArray, child); 1.1219 + } else if (isFormat) { 1.1220 + aArray.AppendObject(child->AsDOMNode()); 1.1221 + } else if (!foundInline) { 1.1222 + // if this is the first inline we've found, use it 1.1223 + foundInline = true; 1.1224 + aArray.AppendObject(child->AsDOMNode()); 1.1225 + } 1.1226 + } 1.1227 + return NS_OK; 1.1228 +} 1.1229 + 1.1230 +nsresult 1.1231 +nsHTMLEditRules::GetFormatString(nsIDOMNode *aNode, nsAString &outFormat) 1.1232 +{ 1.1233 + NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER); 1.1234 + 1.1235 + if (nsHTMLEditUtils::IsFormatNode(aNode)) 1.1236 + { 1.1237 + nsCOMPtr<nsIAtom> atom = nsEditor::GetTag(aNode); 1.1238 + atom->ToString(outFormat); 1.1239 + } 1.1240 + else 1.1241 + outFormat.Truncate(); 1.1242 + 1.1243 + return NS_OK; 1.1244 +} 1.1245 + 1.1246 +/******************************************************** 1.1247 + * Protected rules methods 1.1248 + ********************************************************/ 1.1249 + 1.1250 +nsresult 1.1251 +nsHTMLEditRules::WillInsert(nsISelection *aSelection, bool *aCancel) 1.1252 +{ 1.1253 + nsresult res = nsTextEditRules::WillInsert(aSelection, aCancel); 1.1254 + NS_ENSURE_SUCCESS(res, res); 1.1255 + 1.1256 + // Adjust selection to prevent insertion after a moz-BR. 1.1257 + // this next only works for collapsed selections right now, 1.1258 + // because selection is a pain to work with when not collapsed. 1.1259 + // (no good way to extend start or end of selection), so we ignore 1.1260 + // those types of selections. 1.1261 + if (!aSelection->Collapsed()) { 1.1262 + return NS_OK; 1.1263 + } 1.1264 + 1.1265 + // if we are after a mozBR in the same block, then move selection 1.1266 + // to be before it 1.1267 + nsCOMPtr<nsIDOMNode> selNode, priorNode; 1.1268 + int32_t selOffset; 1.1269 + // get the (collapsed) selection location 1.1270 + NS_ENSURE_STATE(mHTMLEditor); 1.1271 + res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), 1.1272 + &selOffset); 1.1273 + NS_ENSURE_SUCCESS(res, res); 1.1274 + // get prior node 1.1275 + NS_ENSURE_STATE(mHTMLEditor); 1.1276 + res = mHTMLEditor->GetPriorHTMLNode(selNode, selOffset, 1.1277 + address_of(priorNode)); 1.1278 + if (NS_SUCCEEDED(res) && priorNode && nsTextEditUtils::IsMozBR(priorNode)) 1.1279 + { 1.1280 + nsCOMPtr<nsIDOMNode> block1, block2; 1.1281 + if (IsBlockNode(selNode)) { 1.1282 + block1 = selNode; 1.1283 + } 1.1284 + else { 1.1285 + NS_ENSURE_STATE(mHTMLEditor); 1.1286 + block1 = mHTMLEditor->GetBlockNodeParent(selNode); 1.1287 + } 1.1288 + NS_ENSURE_STATE(mHTMLEditor); 1.1289 + block2 = mHTMLEditor->GetBlockNodeParent(priorNode); 1.1290 + 1.1291 + if (block1 == block2) 1.1292 + { 1.1293 + // if we are here then the selection is right after a mozBR 1.1294 + // that is in the same block as the selection. We need to move 1.1295 + // the selection start to be before the mozBR. 1.1296 + selNode = nsEditor::GetNodeLocation(priorNode, &selOffset); 1.1297 + res = aSelection->Collapse(selNode,selOffset); 1.1298 + NS_ENSURE_SUCCESS(res, res); 1.1299 + } 1.1300 + } 1.1301 + 1.1302 + if (mDidDeleteSelection && 1.1303 + (mTheAction == EditAction::insertText || 1.1304 + mTheAction == EditAction::insertIMEText || 1.1305 + mTheAction == EditAction::deleteSelection)) { 1.1306 + res = ReapplyCachedStyles(); 1.1307 + NS_ENSURE_SUCCESS(res, res); 1.1308 + } 1.1309 + // For most actions we want to clear the cached styles, but there are 1.1310 + // exceptions 1.1311 + if (!IsStyleCachePreservingAction(mTheAction)) { 1.1312 + ClearCachedStyles(); 1.1313 + } 1.1314 + 1.1315 + return NS_OK; 1.1316 +} 1.1317 + 1.1318 +nsresult 1.1319 +nsHTMLEditRules::WillInsertText(EditAction aAction, 1.1320 + Selection* aSelection, 1.1321 + bool *aCancel, 1.1322 + bool *aHandled, 1.1323 + const nsAString *inString, 1.1324 + nsAString *outString, 1.1325 + int32_t aMaxLength) 1.1326 +{ 1.1327 + if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; } 1.1328 + 1.1329 + if (inString->IsEmpty() && aAction != EditAction::insertIMEText) { 1.1330 + // HACK: this is a fix for bug 19395 1.1331 + // I can't outlaw all empty insertions 1.1332 + // because IME transaction depend on them 1.1333 + // There is more work to do to make the 1.1334 + // world safe for IME. 1.1335 + *aCancel = true; 1.1336 + *aHandled = false; 1.1337 + return NS_OK; 1.1338 + } 1.1339 + 1.1340 + // initialize out param 1.1341 + *aCancel = false; 1.1342 + *aHandled = true; 1.1343 + nsresult res; 1.1344 + nsCOMPtr<nsIDOMNode> selNode; 1.1345 + int32_t selOffset; 1.1346 + 1.1347 + // If the selection isn't collapsed, delete it. Don't delete existing inline 1.1348 + // tags, because we're hopefully going to insert text (bug 787432). 1.1349 + if (!aSelection->Collapsed()) { 1.1350 + NS_ENSURE_STATE(mHTMLEditor); 1.1351 + res = mHTMLEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eNoStrip); 1.1352 + NS_ENSURE_SUCCESS(res, res); 1.1353 + } 1.1354 + 1.1355 + res = WillInsert(aSelection, aCancel); 1.1356 + NS_ENSURE_SUCCESS(res, res); 1.1357 + // initialize out param 1.1358 + // we want to ignore result of WillInsert() 1.1359 + *aCancel = false; 1.1360 + 1.1361 + // we need to get the doc 1.1362 + NS_ENSURE_STATE(mHTMLEditor); 1.1363 + nsCOMPtr<nsIDOMDocument> doc = mHTMLEditor->GetDOMDocument(); 1.1364 + NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED); 1.1365 + 1.1366 + // for every property that is set, insert a new inline style node 1.1367 + res = CreateStyleForInsertText(aSelection, doc); 1.1368 + NS_ENSURE_SUCCESS(res, res); 1.1369 + 1.1370 + // get the (collapsed) selection location 1.1371 + NS_ENSURE_STATE(mHTMLEditor); 1.1372 + res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset); 1.1373 + NS_ENSURE_SUCCESS(res, res); 1.1374 + 1.1375 + // dont put text in places that can't have it 1.1376 + NS_ENSURE_STATE(mHTMLEditor); 1.1377 + if (!mHTMLEditor->IsTextNode(selNode) && 1.1378 + (!mHTMLEditor || 1.1379 + !mHTMLEditor->CanContainTag(selNode, nsGkAtoms::textTagName))) { 1.1380 + return NS_ERROR_FAILURE; 1.1381 + } 1.1382 + 1.1383 + if (aAction == EditAction::insertIMEText) { 1.1384 + // Right now the nsWSRunObject code bails on empty strings, but IME needs 1.1385 + // the InsertTextImpl() call to still happen since empty strings are meaningful there. 1.1386 + if (inString->IsEmpty()) 1.1387 + { 1.1388 + NS_ENSURE_STATE(mHTMLEditor); 1.1389 + res = mHTMLEditor->InsertTextImpl(*inString, address_of(selNode), &selOffset, doc); 1.1390 + } 1.1391 + else 1.1392 + { 1.1393 + NS_ENSURE_STATE(mHTMLEditor); 1.1394 + nsWSRunObject wsObj(mHTMLEditor, selNode, selOffset); 1.1395 + res = wsObj.InsertText(*inString, address_of(selNode), &selOffset, doc); 1.1396 + } 1.1397 + NS_ENSURE_SUCCESS(res, res); 1.1398 + } 1.1399 + else // aAction == kInsertText 1.1400 + { 1.1401 + // find where we are 1.1402 + nsCOMPtr<nsIDOMNode> curNode = selNode; 1.1403 + int32_t curOffset = selOffset; 1.1404 + 1.1405 + // is our text going to be PREformatted? 1.1406 + // We remember this so that we know how to handle tabs. 1.1407 + bool isPRE; 1.1408 + NS_ENSURE_STATE(mHTMLEditor); 1.1409 + res = mHTMLEditor->IsPreformatted(selNode, &isPRE); 1.1410 + NS_ENSURE_SUCCESS(res, res); 1.1411 + 1.1412 + // turn off the edit listener: we know how to 1.1413 + // build the "doc changed range" ourselves, and it's 1.1414 + // must faster to do it once here than to track all 1.1415 + // the changes one at a time. 1.1416 + nsAutoLockListener lockit(&mListenerEnabled); 1.1417 + 1.1418 + // don't spaz my selection in subtransactions 1.1419 + NS_ENSURE_STATE(mHTMLEditor); 1.1420 + nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor); 1.1421 + nsAutoString tString(*inString); 1.1422 + const char16_t *unicodeBuf = tString.get(); 1.1423 + nsCOMPtr<nsIDOMNode> unused; 1.1424 + int32_t pos = 0; 1.1425 + NS_NAMED_LITERAL_STRING(newlineStr, LFSTR); 1.1426 + 1.1427 + // for efficiency, break out the pre case separately. This is because 1.1428 + // its a lot cheaper to search the input string for only newlines than 1.1429 + // it is to search for both tabs and newlines. 1.1430 + if (isPRE || IsPlaintextEditor()) 1.1431 + { 1.1432 + while (unicodeBuf && (pos != -1) && (pos < (int32_t)(*inString).Length())) 1.1433 + { 1.1434 + int32_t oldPos = pos; 1.1435 + int32_t subStrLen; 1.1436 + pos = tString.FindChar(nsCRT::LF, oldPos); 1.1437 + 1.1438 + if (pos != -1) 1.1439 + { 1.1440 + subStrLen = pos - oldPos; 1.1441 + // if first char is newline, then use just it 1.1442 + if (subStrLen == 0) 1.1443 + subStrLen = 1; 1.1444 + } 1.1445 + else 1.1446 + { 1.1447 + subStrLen = tString.Length() - oldPos; 1.1448 + pos = tString.Length(); 1.1449 + } 1.1450 + 1.1451 + nsDependentSubstring subStr(tString, oldPos, subStrLen); 1.1452 + 1.1453 + // is it a return? 1.1454 + if (subStr.Equals(newlineStr)) 1.1455 + { 1.1456 + NS_ENSURE_STATE(mHTMLEditor); 1.1457 + res = mHTMLEditor->CreateBRImpl(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone); 1.1458 + pos++; 1.1459 + } 1.1460 + else 1.1461 + { 1.1462 + NS_ENSURE_STATE(mHTMLEditor); 1.1463 + res = mHTMLEditor->InsertTextImpl(subStr, address_of(curNode), &curOffset, doc); 1.1464 + } 1.1465 + NS_ENSURE_SUCCESS(res, res); 1.1466 + } 1.1467 + } 1.1468 + else 1.1469 + { 1.1470 + NS_NAMED_LITERAL_STRING(tabStr, "\t"); 1.1471 + NS_NAMED_LITERAL_STRING(spacesStr, " "); 1.1472 + char specialChars[] = {TAB, nsCRT::LF, 0}; 1.1473 + while (unicodeBuf && (pos != -1) && (pos < (int32_t)inString->Length())) 1.1474 + { 1.1475 + int32_t oldPos = pos; 1.1476 + int32_t subStrLen; 1.1477 + pos = tString.FindCharInSet(specialChars, oldPos); 1.1478 + 1.1479 + if (pos != -1) 1.1480 + { 1.1481 + subStrLen = pos - oldPos; 1.1482 + // if first char is newline, then use just it 1.1483 + if (subStrLen == 0) 1.1484 + subStrLen = 1; 1.1485 + } 1.1486 + else 1.1487 + { 1.1488 + subStrLen = tString.Length() - oldPos; 1.1489 + pos = tString.Length(); 1.1490 + } 1.1491 + 1.1492 + nsDependentSubstring subStr(tString, oldPos, subStrLen); 1.1493 + NS_ENSURE_STATE(mHTMLEditor); 1.1494 + nsWSRunObject wsObj(mHTMLEditor, curNode, curOffset); 1.1495 + 1.1496 + // is it a tab? 1.1497 + if (subStr.Equals(tabStr)) 1.1498 + { 1.1499 + res = wsObj.InsertText(spacesStr, address_of(curNode), &curOffset, doc); 1.1500 + NS_ENSURE_SUCCESS(res, res); 1.1501 + pos++; 1.1502 + } 1.1503 + // is it a return? 1.1504 + else if (subStr.Equals(newlineStr)) 1.1505 + { 1.1506 + res = wsObj.InsertBreak(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone); 1.1507 + NS_ENSURE_SUCCESS(res, res); 1.1508 + pos++; 1.1509 + } 1.1510 + else 1.1511 + { 1.1512 + res = wsObj.InsertText(subStr, address_of(curNode), &curOffset, doc); 1.1513 + NS_ENSURE_SUCCESS(res, res); 1.1514 + } 1.1515 + NS_ENSURE_SUCCESS(res, res); 1.1516 + } 1.1517 + } 1.1518 + nsCOMPtr<nsISelection> selection(aSelection); 1.1519 + nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection)); 1.1520 + selPriv->SetInterlinePosition(false); 1.1521 + if (curNode) aSelection->Collapse(curNode, curOffset); 1.1522 + // manually update the doc changed range so that AfterEdit will clean up 1.1523 + // the correct portion of the document. 1.1524 + if (!mDocChangeRange) 1.1525 + { 1.1526 + nsCOMPtr<nsINode> node = do_QueryInterface(selNode); 1.1527 + NS_ENSURE_STATE(node); 1.1528 + mDocChangeRange = new nsRange(node); 1.1529 + } 1.1530 + res = mDocChangeRange->SetStart(selNode, selOffset); 1.1531 + NS_ENSURE_SUCCESS(res, res); 1.1532 + if (curNode) 1.1533 + res = mDocChangeRange->SetEnd(curNode, curOffset); 1.1534 + else 1.1535 + res = mDocChangeRange->SetEnd(selNode, selOffset); 1.1536 + NS_ENSURE_SUCCESS(res, res); 1.1537 + } 1.1538 + return res; 1.1539 +} 1.1540 + 1.1541 +nsresult 1.1542 +nsHTMLEditRules::WillLoadHTML(nsISelection *aSelection, bool *aCancel) 1.1543 +{ 1.1544 + NS_ENSURE_TRUE(aSelection && aCancel, NS_ERROR_NULL_POINTER); 1.1545 + 1.1546 + *aCancel = false; 1.1547 + 1.1548 + // Delete mBogusNode if it exists. If we really need one, 1.1549 + // it will be added during post-processing in AfterEditInner(). 1.1550 + 1.1551 + if (mBogusNode) 1.1552 + { 1.1553 + mEditor->DeleteNode(mBogusNode); 1.1554 + mBogusNode = nullptr; 1.1555 + } 1.1556 + 1.1557 + return NS_OK; 1.1558 +} 1.1559 + 1.1560 +nsresult 1.1561 +nsHTMLEditRules::WillInsertBreak(Selection* aSelection, 1.1562 + bool* aCancel, bool* aHandled) 1.1563 +{ 1.1564 + if (!aSelection || !aCancel || !aHandled) { 1.1565 + return NS_ERROR_NULL_POINTER; 1.1566 + } 1.1567 + // initialize out params 1.1568 + *aCancel = false; 1.1569 + *aHandled = false; 1.1570 + 1.1571 + // if the selection isn't collapsed, delete it. 1.1572 + nsresult res = NS_OK; 1.1573 + if (!aSelection->Collapsed()) { 1.1574 + NS_ENSURE_STATE(mHTMLEditor); 1.1575 + res = mHTMLEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip); 1.1576 + NS_ENSURE_SUCCESS(res, res); 1.1577 + } 1.1578 + 1.1579 + res = WillInsert(aSelection, aCancel); 1.1580 + NS_ENSURE_SUCCESS(res, res); 1.1581 + 1.1582 + // initialize out param 1.1583 + // we want to ignore result of WillInsert() 1.1584 + *aCancel = false; 1.1585 + 1.1586 + // split any mailcites in the way. 1.1587 + // should we abort this if we encounter table cell boundaries? 1.1588 + if (IsMailEditor()) { 1.1589 + res = SplitMailCites(aSelection, IsPlaintextEditor(), aHandled); 1.1590 + NS_ENSURE_SUCCESS(res, res); 1.1591 + if (*aHandled) { 1.1592 + return NS_OK; 1.1593 + } 1.1594 + } 1.1595 + 1.1596 + // smart splitting rules 1.1597 + nsCOMPtr<nsIDOMNode> node; 1.1598 + int32_t offset; 1.1599 + 1.1600 + NS_ENSURE_STATE(mHTMLEditor); 1.1601 + res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(node), 1.1602 + &offset); 1.1603 + NS_ENSURE_SUCCESS(res, res); 1.1604 + NS_ENSURE_TRUE(node, NS_ERROR_FAILURE); 1.1605 + 1.1606 + // do nothing if the node is read-only 1.1607 + NS_ENSURE_STATE(mHTMLEditor); 1.1608 + if (!mHTMLEditor->IsModifiableNode(node)) { 1.1609 + *aCancel = true; 1.1610 + return NS_OK; 1.1611 + } 1.1612 + 1.1613 + // identify the block 1.1614 + nsCOMPtr<nsIDOMNode> blockParent; 1.1615 + if (IsBlockNode(node)) { 1.1616 + blockParent = node; 1.1617 + } else { 1.1618 + NS_ENSURE_STATE(mHTMLEditor); 1.1619 + blockParent = mHTMLEditor->GetBlockNodeParent(node); 1.1620 + } 1.1621 + NS_ENSURE_TRUE(blockParent, NS_ERROR_FAILURE); 1.1622 + 1.1623 + // if the active editing host is an inline element, or if the active editing 1.1624 + // host is the block parent itself, just append a br. 1.1625 + NS_ENSURE_STATE(mHTMLEditor); 1.1626 + nsCOMPtr<nsIContent> hostContent = mHTMLEditor->GetActiveEditingHost(); 1.1627 + nsCOMPtr<nsIDOMNode> hostNode = do_QueryInterface(hostContent); 1.1628 + if (!nsEditorUtils::IsDescendantOf(blockParent, hostNode)) { 1.1629 + res = StandardBreakImpl(node, offset, aSelection); 1.1630 + NS_ENSURE_SUCCESS(res, res); 1.1631 + *aHandled = true; 1.1632 + return NS_OK; 1.1633 + } 1.1634 + 1.1635 + // if block is empty, populate with br. (for example, imagine a div that 1.1636 + // contains the word "text". the user selects "text" and types return. 1.1637 + // "text" is deleted leaving an empty block. we want to put in one br to 1.1638 + // make block have a line. then code further below will put in a second br.) 1.1639 + bool isEmpty; 1.1640 + IsEmptyBlock(blockParent, &isEmpty); 1.1641 + if (isEmpty) { 1.1642 + uint32_t blockLen; 1.1643 + NS_ENSURE_STATE(mHTMLEditor); 1.1644 + res = mHTMLEditor->GetLengthOfDOMNode(blockParent, blockLen); 1.1645 + NS_ENSURE_SUCCESS(res, res); 1.1646 + nsCOMPtr<nsIDOMNode> brNode; 1.1647 + NS_ENSURE_STATE(mHTMLEditor); 1.1648 + res = mHTMLEditor->CreateBR(blockParent, blockLen, address_of(brNode)); 1.1649 + NS_ENSURE_SUCCESS(res, res); 1.1650 + } 1.1651 + 1.1652 + nsCOMPtr<nsIDOMNode> listItem = IsInListItem(blockParent); 1.1653 + if (listItem && listItem != hostNode) { 1.1654 + ReturnInListItem(aSelection, listItem, node, offset); 1.1655 + *aHandled = true; 1.1656 + return NS_OK; 1.1657 + } else if (nsHTMLEditUtils::IsHeader(blockParent)) { 1.1658 + // headers: close (or split) header 1.1659 + ReturnInHeader(aSelection, blockParent, node, offset); 1.1660 + *aHandled = true; 1.1661 + return NS_OK; 1.1662 + } else if (nsHTMLEditUtils::IsParagraph(blockParent)) { 1.1663 + // paragraphs: special rules to look for <br>s 1.1664 + res = ReturnInParagraph(aSelection, blockParent, node, offset, 1.1665 + aCancel, aHandled); 1.1666 + NS_ENSURE_SUCCESS(res, res); 1.1667 + // fall through, we may not have handled it in ReturnInParagraph() 1.1668 + } 1.1669 + 1.1670 + // if not already handled then do the standard thing 1.1671 + if (!(*aHandled)) { 1.1672 + *aHandled = true; 1.1673 + return StandardBreakImpl(node, offset, aSelection); 1.1674 + } 1.1675 + return NS_OK; 1.1676 +} 1.1677 + 1.1678 +nsresult 1.1679 +nsHTMLEditRules::StandardBreakImpl(nsIDOMNode* aNode, int32_t aOffset, 1.1680 + nsISelection* aSelection) 1.1681 +{ 1.1682 + nsCOMPtr<nsIDOMNode> brNode; 1.1683 + bool bAfterBlock = false; 1.1684 + bool bBeforeBlock = false; 1.1685 + nsresult res = NS_OK; 1.1686 + nsCOMPtr<nsIDOMNode> node(aNode); 1.1687 + nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(aSelection)); 1.1688 + 1.1689 + if (IsPlaintextEditor()) { 1.1690 + NS_ENSURE_STATE(mHTMLEditor); 1.1691 + res = mHTMLEditor->CreateBR(node, aOffset, address_of(brNode)); 1.1692 + } else { 1.1693 + NS_ENSURE_STATE(mHTMLEditor); 1.1694 + nsWSRunObject wsObj(mHTMLEditor, node, aOffset); 1.1695 + nsCOMPtr<nsIDOMNode> visNode, linkNode; 1.1696 + int32_t visOffset = 0, newOffset; 1.1697 + WSType wsType; 1.1698 + wsObj.PriorVisibleNode(node, aOffset, address_of(visNode), 1.1699 + &visOffset, &wsType); 1.1700 + if (wsType & WSType::block) { 1.1701 + bAfterBlock = true; 1.1702 + } 1.1703 + wsObj.NextVisibleNode(node, aOffset, address_of(visNode), 1.1704 + &visOffset, &wsType); 1.1705 + if (wsType & WSType::block) { 1.1706 + bBeforeBlock = true; 1.1707 + } 1.1708 + NS_ENSURE_STATE(mHTMLEditor); 1.1709 + if (mHTMLEditor->IsInLink(node, address_of(linkNode))) { 1.1710 + // split the link 1.1711 + nsCOMPtr<nsIDOMNode> linkParent; 1.1712 + res = linkNode->GetParentNode(getter_AddRefs(linkParent)); 1.1713 + NS_ENSURE_SUCCESS(res, res); 1.1714 + NS_ENSURE_STATE(mHTMLEditor); 1.1715 + res = mHTMLEditor->SplitNodeDeep(linkNode, node, aOffset, 1.1716 + &newOffset, true); 1.1717 + NS_ENSURE_SUCCESS(res, res); 1.1718 + // reset {node,aOffset} to the point where link was split 1.1719 + node = linkParent; 1.1720 + aOffset = newOffset; 1.1721 + } 1.1722 + res = wsObj.InsertBreak(address_of(node), &aOffset, 1.1723 + address_of(brNode), nsIEditor::eNone); 1.1724 + } 1.1725 + NS_ENSURE_SUCCESS(res, res); 1.1726 + node = nsEditor::GetNodeLocation(brNode, &aOffset); 1.1727 + NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER); 1.1728 + if (bAfterBlock && bBeforeBlock) { 1.1729 + // we just placed a br between block boundaries. This is the one case 1.1730 + // where we want the selection to be before the br we just placed, as the 1.1731 + // br will be on a new line, rather than at end of prior line. 1.1732 + selPriv->SetInterlinePosition(true); 1.1733 + res = aSelection->Collapse(node, aOffset); 1.1734 + } else { 1.1735 + NS_ENSURE_STATE(mHTMLEditor); 1.1736 + nsWSRunObject wsObj(mHTMLEditor, node, aOffset+1); 1.1737 + nsCOMPtr<nsIDOMNode> secondBR; 1.1738 + int32_t visOffset = 0; 1.1739 + WSType wsType; 1.1740 + wsObj.NextVisibleNode(node, aOffset+1, address_of(secondBR), 1.1741 + &visOffset, &wsType); 1.1742 + if (wsType == WSType::br) { 1.1743 + // the next thing after the break we inserted is another break. Move 1.1744 + // the 2nd break to be the first breaks sibling. This will prevent them 1.1745 + // from being in different inline nodes, which would break 1.1746 + // SetInterlinePosition(). It will also assure that if the user clicks 1.1747 + // away and then clicks back on their new blank line, they will still 1.1748 + // get the style from the line above. 1.1749 + int32_t brOffset; 1.1750 + nsCOMPtr<nsIDOMNode> brParent = nsEditor::GetNodeLocation(secondBR, &brOffset); 1.1751 + if (brParent != node || brOffset != aOffset + 1) { 1.1752 + NS_ENSURE_STATE(mHTMLEditor); 1.1753 + res = mHTMLEditor->MoveNode(secondBR, node, aOffset+1); 1.1754 + NS_ENSURE_SUCCESS(res, res); 1.1755 + } 1.1756 + } 1.1757 + // SetInterlinePosition(true) means we want the caret to stick to the 1.1758 + // content on the "right". We want the caret to stick to whatever is past 1.1759 + // the break. This is because the break is on the same line we were on, 1.1760 + // but the next content will be on the following line. 1.1761 + 1.1762 + // An exception to this is if the break has a next sibling that is a block 1.1763 + // node. Then we stick to the left to avoid an uber caret. 1.1764 + nsCOMPtr<nsIDOMNode> siblingNode; 1.1765 + brNode->GetNextSibling(getter_AddRefs(siblingNode)); 1.1766 + if (siblingNode && IsBlockNode(siblingNode)) { 1.1767 + selPriv->SetInterlinePosition(false); 1.1768 + } else { 1.1769 + selPriv->SetInterlinePosition(true); 1.1770 + } 1.1771 + res = aSelection->Collapse(node, aOffset+1); 1.1772 + } 1.1773 + return res; 1.1774 +} 1.1775 + 1.1776 +nsresult 1.1777 +nsHTMLEditRules::DidInsertBreak(nsISelection *aSelection, nsresult aResult) 1.1778 +{ 1.1779 + return NS_OK; 1.1780 +} 1.1781 + 1.1782 + 1.1783 +nsresult 1.1784 +nsHTMLEditRules::SplitMailCites(nsISelection *aSelection, bool aPlaintext, bool *aHandled) 1.1785 +{ 1.1786 + NS_ENSURE_TRUE(aSelection && aHandled, NS_ERROR_NULL_POINTER); 1.1787 + nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(aSelection)); 1.1788 + nsCOMPtr<nsIDOMNode> citeNode, selNode, leftCite, rightCite; 1.1789 + int32_t selOffset, newOffset; 1.1790 + NS_ENSURE_STATE(mHTMLEditor); 1.1791 + nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset); 1.1792 + NS_ENSURE_SUCCESS(res, res); 1.1793 + res = GetTopEnclosingMailCite(selNode, address_of(citeNode), aPlaintext); 1.1794 + NS_ENSURE_SUCCESS(res, res); 1.1795 + if (citeNode) 1.1796 + { 1.1797 + // If our selection is just before a break, nudge it to be 1.1798 + // just after it. This does two things for us. It saves us the trouble of having to add 1.1799 + // a break here ourselves to preserve the "blockness" of the inline span mailquote 1.1800 + // (in the inline case), and : 1.1801 + // it means the break won't end up making an empty line that happens to be inside a 1.1802 + // mailquote (in either inline or block case). 1.1803 + // The latter can confuse a user if they click there and start typing, 1.1804 + // because being in the mailquote may affect wrapping behavior, or font color, etc. 1.1805 + NS_ENSURE_STATE(mHTMLEditor); 1.1806 + nsWSRunObject wsObj(mHTMLEditor, selNode, selOffset); 1.1807 + nsCOMPtr<nsIDOMNode> visNode; 1.1808 + int32_t visOffset=0; 1.1809 + WSType wsType; 1.1810 + wsObj.NextVisibleNode(selNode, selOffset, address_of(visNode), 1.1811 + &visOffset, &wsType); 1.1812 + if (wsType == WSType::br) { 1.1813 + // ok, we are just before a break. is it inside the mailquote? 1.1814 + int32_t unused; 1.1815 + if (nsEditorUtils::IsDescendantOf(visNode, citeNode, &unused)) 1.1816 + { 1.1817 + // it is. so lets reset our selection to be just after it. 1.1818 + NS_ENSURE_STATE(mHTMLEditor); 1.1819 + selNode = mHTMLEditor->GetNodeLocation(visNode, &selOffset); 1.1820 + ++selOffset; 1.1821 + } 1.1822 + } 1.1823 + 1.1824 + nsCOMPtr<nsIDOMNode> brNode; 1.1825 + NS_ENSURE_STATE(mHTMLEditor); 1.1826 + res = mHTMLEditor->SplitNodeDeep(citeNode, selNode, selOffset, &newOffset, 1.1827 + true, address_of(leftCite), address_of(rightCite)); 1.1828 + NS_ENSURE_SUCCESS(res, res); 1.1829 + res = citeNode->GetParentNode(getter_AddRefs(selNode)); 1.1830 + NS_ENSURE_SUCCESS(res, res); 1.1831 + NS_ENSURE_STATE(mHTMLEditor); 1.1832 + res = mHTMLEditor->CreateBR(selNode, newOffset, address_of(brNode)); 1.1833 + NS_ENSURE_SUCCESS(res, res); 1.1834 + // want selection before the break, and on same line 1.1835 + selPriv->SetInterlinePosition(true); 1.1836 + res = aSelection->Collapse(selNode, newOffset); 1.1837 + NS_ENSURE_SUCCESS(res, res); 1.1838 + // if citeNode wasn't a block, we might also want another break before it. 1.1839 + // We need to examine the content both before the br we just added and also 1.1840 + // just after it. If we don't have another br or block boundary adjacent, 1.1841 + // then we will need a 2nd br added to achieve blank line that user expects. 1.1842 + if (IsInlineNode(citeNode)) 1.1843 + { 1.1844 + NS_ENSURE_STATE(mHTMLEditor); 1.1845 + nsWSRunObject wsObj(mHTMLEditor, selNode, newOffset); 1.1846 + nsCOMPtr<nsIDOMNode> visNode; 1.1847 + int32_t visOffset=0; 1.1848 + WSType wsType; 1.1849 + wsObj.PriorVisibleNode(selNode, newOffset, address_of(visNode), 1.1850 + &visOffset, &wsType); 1.1851 + if (wsType == WSType::normalWS || wsType == WSType::text || 1.1852 + wsType == WSType::special) { 1.1853 + NS_ENSURE_STATE(mHTMLEditor); 1.1854 + nsWSRunObject wsObjAfterBR(mHTMLEditor, selNode, newOffset+1); 1.1855 + wsObjAfterBR.NextVisibleNode(selNode, newOffset+1, address_of(visNode), 1.1856 + &visOffset, &wsType); 1.1857 + if (wsType == WSType::normalWS || wsType == WSType::text || 1.1858 + wsType == WSType::special) { 1.1859 + NS_ENSURE_STATE(mHTMLEditor); 1.1860 + res = mHTMLEditor->CreateBR(selNode, newOffset, address_of(brNode)); 1.1861 + NS_ENSURE_SUCCESS(res, res); 1.1862 + } 1.1863 + } 1.1864 + } 1.1865 + // delete any empty cites 1.1866 + bool bEmptyCite = false; 1.1867 + if (leftCite) 1.1868 + { 1.1869 + NS_ENSURE_STATE(mHTMLEditor); 1.1870 + res = mHTMLEditor->IsEmptyNode(leftCite, &bEmptyCite, true, false); 1.1871 + if (NS_SUCCEEDED(res) && bEmptyCite) { 1.1872 + NS_ENSURE_STATE(mHTMLEditor); 1.1873 + res = mHTMLEditor->DeleteNode(leftCite); 1.1874 + } 1.1875 + NS_ENSURE_SUCCESS(res, res); 1.1876 + } 1.1877 + if (rightCite) 1.1878 + { 1.1879 + NS_ENSURE_STATE(mHTMLEditor); 1.1880 + res = mHTMLEditor->IsEmptyNode(rightCite, &bEmptyCite, true, false); 1.1881 + if (NS_SUCCEEDED(res) && bEmptyCite) { 1.1882 + NS_ENSURE_STATE(mHTMLEditor); 1.1883 + res = mHTMLEditor->DeleteNode(rightCite); 1.1884 + } 1.1885 + NS_ENSURE_SUCCESS(res, res); 1.1886 + } 1.1887 + *aHandled = true; 1.1888 + } 1.1889 + return NS_OK; 1.1890 +} 1.1891 + 1.1892 + 1.1893 +nsresult 1.1894 +nsHTMLEditRules::WillDeleteSelection(Selection* aSelection, 1.1895 + nsIEditor::EDirection aAction, 1.1896 + nsIEditor::EStripWrappers aStripWrappers, 1.1897 + bool* aCancel, 1.1898 + bool* aHandled) 1.1899 +{ 1.1900 + MOZ_ASSERT(aStripWrappers == nsIEditor::eStrip || 1.1901 + aStripWrappers == nsIEditor::eNoStrip); 1.1902 + 1.1903 + if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; } 1.1904 + // initialize out param 1.1905 + *aCancel = false; 1.1906 + *aHandled = false; 1.1907 + 1.1908 + // remember that we did a selection deletion. Used by CreateStyleForInsertText() 1.1909 + mDidDeleteSelection = true; 1.1910 + 1.1911 + // if there is only bogus content, cancel the operation 1.1912 + if (mBogusNode) 1.1913 + { 1.1914 + *aCancel = true; 1.1915 + return NS_OK; 1.1916 + } 1.1917 + 1.1918 + bool bCollapsed = aSelection->Collapsed(), join = false; 1.1919 + 1.1920 + // origCollapsed is used later to determine whether we should join 1.1921 + // blocks. We don't really care about bCollapsed because it will be 1.1922 + // modified by ExtendSelectionForDelete later. JoinBlocks should 1.1923 + // happen if the original selection is collapsed and the cursor is 1.1924 + // at the end of a block element, in which case ExtendSelectionForDelete 1.1925 + // would always make the selection not collapsed. 1.1926 + bool origCollapsed = bCollapsed; 1.1927 + nsCOMPtr<nsIDOMNode> startNode, selNode; 1.1928 + int32_t startOffset, selOffset; 1.1929 + 1.1930 + // first check for table selection mode. If so, 1.1931 + // hand off to table editor. 1.1932 + nsCOMPtr<nsIDOMElement> cell; 1.1933 + NS_ENSURE_STATE(mHTMLEditor); 1.1934 + nsresult res = mHTMLEditor->GetFirstSelectedCell(nullptr, getter_AddRefs(cell)); 1.1935 + if (NS_SUCCEEDED(res) && cell) { 1.1936 + NS_ENSURE_STATE(mHTMLEditor); 1.1937 + res = mHTMLEditor->DeleteTableCellContents(); 1.1938 + *aHandled = true; 1.1939 + return res; 1.1940 + } 1.1941 + cell = nullptr; 1.1942 + 1.1943 + NS_ENSURE_STATE(mHTMLEditor); 1.1944 + res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(startNode), &startOffset); 1.1945 + NS_ENSURE_SUCCESS(res, res); 1.1946 + NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE); 1.1947 + 1.1948 + if (bCollapsed) 1.1949 + { 1.1950 + // if we are inside an empty block, delete it. 1.1951 + NS_ENSURE_STATE(mHTMLEditor); 1.1952 + nsCOMPtr<nsIContent> hostContent = mHTMLEditor->GetActiveEditingHost(); 1.1953 + nsCOMPtr<nsIDOMNode> hostNode = do_QueryInterface(hostContent); 1.1954 + NS_ENSURE_TRUE(hostNode, NS_ERROR_FAILURE); 1.1955 + res = CheckForEmptyBlock(startNode, hostNode, aSelection, aHandled); 1.1956 + NS_ENSURE_SUCCESS(res, res); 1.1957 + if (*aHandled) return NS_OK; 1.1958 + 1.1959 + // Test for distance between caret and text that will be deleted 1.1960 + res = CheckBidiLevelForDeletion(aSelection, startNode, startOffset, aAction, aCancel); 1.1961 + NS_ENSURE_SUCCESS(res, res); 1.1962 + if (*aCancel) return NS_OK; 1.1963 + 1.1964 + NS_ENSURE_STATE(mHTMLEditor); 1.1965 + res = mHTMLEditor->ExtendSelectionForDelete(aSelection, &aAction); 1.1966 + NS_ENSURE_SUCCESS(res, res); 1.1967 + 1.1968 + // We should delete nothing. 1.1969 + if (aAction == nsIEditor::eNone) 1.1970 + return NS_OK; 1.1971 + 1.1972 + // ExtendSelectionForDelete() may have changed the selection, update it 1.1973 + NS_ENSURE_STATE(mHTMLEditor); 1.1974 + res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(startNode), &startOffset); 1.1975 + NS_ENSURE_SUCCESS(res, res); 1.1976 + NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE); 1.1977 + 1.1978 + bCollapsed = aSelection->Collapsed(); 1.1979 + } 1.1980 + 1.1981 + if (bCollapsed) 1.1982 + { 1.1983 + // what's in the direction we are deleting? 1.1984 + NS_ENSURE_STATE(mHTMLEditor); 1.1985 + nsWSRunObject wsObj(mHTMLEditor, startNode, startOffset); 1.1986 + nsCOMPtr<nsIDOMNode> visNode; 1.1987 + int32_t visOffset; 1.1988 + WSType wsType; 1.1989 + 1.1990 + // find next visible node 1.1991 + if (aAction == nsIEditor::eNext) 1.1992 + wsObj.NextVisibleNode(startNode, startOffset, address_of(visNode), 1.1993 + &visOffset, &wsType); 1.1994 + else 1.1995 + wsObj.PriorVisibleNode(startNode, startOffset, address_of(visNode), 1.1996 + &visOffset, &wsType); 1.1997 + 1.1998 + if (!visNode) // can't find anything to delete! 1.1999 + { 1.2000 + *aCancel = true; 1.2001 + return res; 1.2002 + } 1.2003 + 1.2004 + if (wsType == WSType::normalWS) { 1.2005 + // we found some visible ws to delete. Let ws code handle it. 1.2006 + if (aAction == nsIEditor::eNext) 1.2007 + res = wsObj.DeleteWSForward(); 1.2008 + else 1.2009 + res = wsObj.DeleteWSBackward(); 1.2010 + *aHandled = true; 1.2011 + NS_ENSURE_SUCCESS(res, res); 1.2012 + res = InsertBRIfNeeded(aSelection); 1.2013 + return res; 1.2014 + } else if (wsType == WSType::text) { 1.2015 + // found normal text to delete. 1.2016 + int32_t so = visOffset; 1.2017 + int32_t eo = visOffset+1; 1.2018 + if (aAction == nsIEditor::ePrevious) 1.2019 + { 1.2020 + if (so == 0) return NS_ERROR_UNEXPECTED; 1.2021 + so--; 1.2022 + eo--; 1.2023 + } 1.2024 + else 1.2025 + { 1.2026 + nsCOMPtr<nsIDOMRange> range; 1.2027 + res = aSelection->GetRangeAt(0, getter_AddRefs(range)); 1.2028 + NS_ENSURE_SUCCESS(res, res); 1.2029 + 1.2030 +#ifdef DEBUG 1.2031 + nsIDOMNode *container; 1.2032 + 1.2033 + res = range->GetStartContainer(&container); 1.2034 + NS_ENSURE_SUCCESS(res, res); 1.2035 + NS_ASSERTION(container == visNode, "selection start not in visNode"); 1.2036 + 1.2037 + res = range->GetEndContainer(&container); 1.2038 + NS_ENSURE_SUCCESS(res, res); 1.2039 + NS_ASSERTION(container == visNode, "selection end not in visNode"); 1.2040 +#endif 1.2041 + 1.2042 + res = range->GetStartOffset(&so); 1.2043 + NS_ENSURE_SUCCESS(res, res); 1.2044 + res = range->GetEndOffset(&eo); 1.2045 + NS_ENSURE_SUCCESS(res, res); 1.2046 + } 1.2047 + NS_ENSURE_STATE(mHTMLEditor); 1.2048 + res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor, address_of(visNode), &so, address_of(visNode), &eo); 1.2049 + NS_ENSURE_SUCCESS(res, res); 1.2050 + nsCOMPtr<nsIDOMCharacterData> nodeAsText(do_QueryInterface(visNode)); 1.2051 + NS_ENSURE_STATE(mHTMLEditor); 1.2052 + res = mHTMLEditor->DeleteText(nodeAsText, std::min(so, eo), DeprecatedAbs(eo - so)); 1.2053 + *aHandled = true; 1.2054 + NS_ENSURE_SUCCESS(res, res); 1.2055 + res = InsertBRIfNeeded(aSelection); 1.2056 + return res; 1.2057 + } else if (wsType == WSType::special || wsType == WSType::br || 1.2058 + nsHTMLEditUtils::IsHR(visNode)) { 1.2059 + // short circuit for invisible breaks. delete them and recurse. 1.2060 + if (nsTextEditUtils::IsBreak(visNode) && 1.2061 + (!mHTMLEditor || !mHTMLEditor->IsVisBreak(visNode))) 1.2062 + { 1.2063 + NS_ENSURE_STATE(mHTMLEditor); 1.2064 + res = mHTMLEditor->DeleteNode(visNode); 1.2065 + NS_ENSURE_SUCCESS(res, res); 1.2066 + return WillDeleteSelection(aSelection, aAction, aStripWrappers, 1.2067 + aCancel, aHandled); 1.2068 + } 1.2069 + 1.2070 + // special handling for backspace when positioned after <hr> 1.2071 + if (aAction == nsIEditor::ePrevious && nsHTMLEditUtils::IsHR(visNode)) 1.2072 + { 1.2073 + /* 1.2074 + Only if the caret is positioned at the end-of-hr-line position, 1.2075 + we want to delete the <hr>. 1.2076 + 1.2077 + In other words, we only want to delete, if 1.2078 + our selection position (indicated by startNode and startOffset) 1.2079 + is the position directly after the <hr>, 1.2080 + on the same line as the <hr>. 1.2081 + 1.2082 + To detect this case we check: 1.2083 + startNode == parentOfVisNode 1.2084 + and 1.2085 + startOffset -1 == visNodeOffsetToVisNodeParent 1.2086 + and 1.2087 + interline position is false (left) 1.2088 + 1.2089 + In any other case we set the position to 1.2090 + startnode -1 and interlineposition to false, 1.2091 + only moving the caret to the end-of-hr-line position. 1.2092 + */ 1.2093 + 1.2094 + bool moveOnly = true; 1.2095 + 1.2096 + selNode = nsEditor::GetNodeLocation(visNode, &selOffset); 1.2097 + 1.2098 + bool interLineIsRight; 1.2099 + res = aSelection->GetInterlinePosition(&interLineIsRight); 1.2100 + NS_ENSURE_SUCCESS(res, res); 1.2101 + 1.2102 + if (startNode == selNode && 1.2103 + startOffset -1 == selOffset && 1.2104 + !interLineIsRight) 1.2105 + { 1.2106 + moveOnly = false; 1.2107 + } 1.2108 + 1.2109 + if (moveOnly) 1.2110 + { 1.2111 + // Go to the position after the <hr>, but to the end of the <hr> line 1.2112 + // by setting the interline position to left. 1.2113 + ++selOffset; 1.2114 + res = aSelection->Collapse(selNode, selOffset); 1.2115 + aSelection->SetInterlinePosition(false); 1.2116 + mDidExplicitlySetInterline = true; 1.2117 + *aHandled = true; 1.2118 + 1.2119 + // There is one exception to the move only case. 1.2120 + // If the <hr> is followed by a <br> we want to delete the <br>. 1.2121 + 1.2122 + WSType otherWSType; 1.2123 + nsCOMPtr<nsIDOMNode> otherNode; 1.2124 + int32_t otherOffset; 1.2125 + 1.2126 + wsObj.NextVisibleNode(startNode, startOffset, address_of(otherNode), 1.2127 + &otherOffset, &otherWSType); 1.2128 + 1.2129 + if (otherWSType == WSType::br) { 1.2130 + // Delete the <br> 1.2131 + 1.2132 + NS_ENSURE_STATE(mHTMLEditor); 1.2133 + res = nsWSRunObject::PrepareToDeleteNode(mHTMLEditor, otherNode); 1.2134 + NS_ENSURE_SUCCESS(res, res); 1.2135 + NS_ENSURE_STATE(mHTMLEditor); 1.2136 + res = mHTMLEditor->DeleteNode(otherNode); 1.2137 + NS_ENSURE_SUCCESS(res, res); 1.2138 + } 1.2139 + 1.2140 + return NS_OK; 1.2141 + } 1.2142 + // else continue with normal delete code 1.2143 + } 1.2144 + 1.2145 + // found break or image, or hr. 1.2146 + NS_ENSURE_STATE(mHTMLEditor); 1.2147 + res = nsWSRunObject::PrepareToDeleteNode(mHTMLEditor, visNode); 1.2148 + NS_ENSURE_SUCCESS(res, res); 1.2149 + // remember sibling to visnode, if any 1.2150 + nsCOMPtr<nsIDOMNode> sibling, stepbrother; 1.2151 + NS_ENSURE_STATE(mHTMLEditor); 1.2152 + mHTMLEditor->GetPriorHTMLSibling(visNode, address_of(sibling)); 1.2153 + // delete the node, and join like nodes if appropriate 1.2154 + NS_ENSURE_STATE(mHTMLEditor); 1.2155 + res = mHTMLEditor->DeleteNode(visNode); 1.2156 + NS_ENSURE_SUCCESS(res, res); 1.2157 + // we did something, so lets say so. 1.2158 + *aHandled = true; 1.2159 + // is there a prior node and are they siblings? 1.2160 + if (sibling) { 1.2161 + NS_ENSURE_STATE(mHTMLEditor); 1.2162 + mHTMLEditor->GetNextHTMLSibling(sibling, address_of(stepbrother)); 1.2163 + } 1.2164 + if (startNode == stepbrother) 1.2165 + { 1.2166 + // are they both text nodes? 1.2167 + NS_ENSURE_STATE(mHTMLEditor); 1.2168 + if (mHTMLEditor->IsTextNode(startNode) && 1.2169 + (!mHTMLEditor || mHTMLEditor->IsTextNode(sibling))) 1.2170 + { 1.2171 + NS_ENSURE_STATE(mHTMLEditor); 1.2172 + // if so, join them! 1.2173 + res = JoinNodesSmart(sibling, startNode, address_of(selNode), &selOffset); 1.2174 + NS_ENSURE_SUCCESS(res, res); 1.2175 + // fix up selection 1.2176 + res = aSelection->Collapse(selNode, selOffset); 1.2177 + } 1.2178 + } 1.2179 + NS_ENSURE_SUCCESS(res, res); 1.2180 + res = InsertBRIfNeeded(aSelection); 1.2181 + return res; 1.2182 + } else if (wsType == WSType::otherBlock) { 1.2183 + // make sure it's not a table element. If so, cancel the operation 1.2184 + // (translation: users cannot backspace or delete across table cells) 1.2185 + if (nsHTMLEditUtils::IsTableElement(visNode)) 1.2186 + { 1.2187 + *aCancel = true; 1.2188 + return NS_OK; 1.2189 + } 1.2190 + 1.2191 + // next to a block. See if we are between a block and a br. If so, we really 1.2192 + // want to delete the br. Else join content at selection to the block. 1.2193 + 1.2194 + bool bDeletedBR = false; 1.2195 + WSType otherWSType; 1.2196 + nsCOMPtr<nsIDOMNode> otherNode; 1.2197 + int32_t otherOffset; 1.2198 + 1.2199 + // find node in other direction 1.2200 + if (aAction == nsIEditor::eNext) 1.2201 + wsObj.PriorVisibleNode(startNode, startOffset, address_of(otherNode), 1.2202 + &otherOffset, &otherWSType); 1.2203 + else 1.2204 + wsObj.NextVisibleNode(startNode, startOffset, address_of(otherNode), 1.2205 + &otherOffset, &otherWSType); 1.2206 + 1.2207 + // first find the adjacent node in the block 1.2208 + nsCOMPtr<nsIDOMNode> leafNode, leftNode, rightNode; 1.2209 + if (aAction == nsIEditor::ePrevious) 1.2210 + { 1.2211 + NS_ENSURE_STATE(mHTMLEditor); 1.2212 + res = mHTMLEditor->GetLastEditableLeaf( visNode, address_of(leafNode)); 1.2213 + NS_ENSURE_SUCCESS(res, res); 1.2214 + leftNode = leafNode; 1.2215 + rightNode = startNode; 1.2216 + } 1.2217 + else 1.2218 + { 1.2219 + NS_ENSURE_STATE(mHTMLEditor); 1.2220 + res = mHTMLEditor->GetFirstEditableLeaf( visNode, address_of(leafNode)); 1.2221 + NS_ENSURE_SUCCESS(res, res); 1.2222 + leftNode = startNode; 1.2223 + rightNode = leafNode; 1.2224 + } 1.2225 + 1.2226 + if (nsTextEditUtils::IsBreak(otherNode)) 1.2227 + { 1.2228 + NS_ENSURE_STATE(mHTMLEditor); 1.2229 + res = mHTMLEditor->DeleteNode(otherNode); 1.2230 + NS_ENSURE_SUCCESS(res, res); 1.2231 + *aHandled = true; 1.2232 + bDeletedBR = true; 1.2233 + } 1.2234 + 1.2235 + // don't cross table boundaries 1.2236 + if (leftNode && rightNode && InDifferentTableElements(leftNode, rightNode)) { 1.2237 + return NS_OK; 1.2238 + } 1.2239 + 1.2240 + if (bDeletedBR) 1.2241 + { 1.2242 + // put selection at edge of block and we are done. 1.2243 + nsCOMPtr<nsIDOMNode> newSelNode; 1.2244 + int32_t newSelOffset; 1.2245 + res = GetGoodSelPointForNode(leafNode, aAction, address_of(newSelNode), &newSelOffset); 1.2246 + NS_ENSURE_SUCCESS(res, res); 1.2247 + aSelection->Collapse(newSelNode, newSelOffset); 1.2248 + return res; 1.2249 + } 1.2250 + 1.2251 + // else we are joining content to block 1.2252 + 1.2253 + nsCOMPtr<nsIDOMNode> selPointNode = startNode; 1.2254 + int32_t selPointOffset = startOffset; 1.2255 + { 1.2256 + NS_ENSURE_STATE(mHTMLEditor); 1.2257 + nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, address_of(selPointNode), &selPointOffset); 1.2258 + res = JoinBlocks(leftNode, rightNode, aCancel); 1.2259 + *aHandled = true; 1.2260 + NS_ENSURE_SUCCESS(res, res); 1.2261 + } 1.2262 + aSelection->Collapse(selPointNode, selPointOffset); 1.2263 + return res; 1.2264 + } else if (wsType == WSType::thisBlock) { 1.2265 + // at edge of our block. Look beside it and see if we can join to an adjacent block 1.2266 + 1.2267 + // make sure it's not a table element. If so, cancel the operation 1.2268 + // (translation: users cannot backspace or delete across table cells) 1.2269 + if (nsHTMLEditUtils::IsTableElement(visNode)) 1.2270 + { 1.2271 + *aCancel = true; 1.2272 + return NS_OK; 1.2273 + } 1.2274 + 1.2275 + // first find the relavent nodes 1.2276 + nsCOMPtr<nsIDOMNode> leftNode, rightNode; 1.2277 + if (aAction == nsIEditor::ePrevious) 1.2278 + { 1.2279 + NS_ENSURE_STATE(mHTMLEditor); 1.2280 + res = mHTMLEditor->GetPriorHTMLNode(visNode, address_of(leftNode)); 1.2281 + NS_ENSURE_SUCCESS(res, res); 1.2282 + rightNode = startNode; 1.2283 + } 1.2284 + else 1.2285 + { 1.2286 + NS_ENSURE_STATE(mHTMLEditor); 1.2287 + res = mHTMLEditor->GetNextHTMLNode( visNode, address_of(rightNode)); 1.2288 + NS_ENSURE_SUCCESS(res, res); 1.2289 + leftNode = startNode; 1.2290 + } 1.2291 + 1.2292 + // nothing to join 1.2293 + if (!leftNode || !rightNode) 1.2294 + { 1.2295 + *aCancel = true; 1.2296 + return NS_OK; 1.2297 + } 1.2298 + 1.2299 + // don't cross table boundaries -- cancel it 1.2300 + if (InDifferentTableElements(leftNode, rightNode)) { 1.2301 + *aCancel = true; 1.2302 + return NS_OK; 1.2303 + } 1.2304 + 1.2305 + nsCOMPtr<nsIDOMNode> selPointNode = startNode; 1.2306 + int32_t selPointOffset = startOffset; 1.2307 + { 1.2308 + NS_ENSURE_STATE(mHTMLEditor); 1.2309 + nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, address_of(selPointNode), &selPointOffset); 1.2310 + res = JoinBlocks(leftNode, rightNode, aCancel); 1.2311 + *aHandled = true; 1.2312 + NS_ENSURE_SUCCESS(res, res); 1.2313 + } 1.2314 + aSelection->Collapse(selPointNode, selPointOffset); 1.2315 + return res; 1.2316 + } 1.2317 + } 1.2318 + 1.2319 + 1.2320 + // else we have a non collapsed selection 1.2321 + // first adjust the selection 1.2322 + res = ExpandSelectionForDeletion(aSelection); 1.2323 + NS_ENSURE_SUCCESS(res, res); 1.2324 + 1.2325 + // remember that we did a ranged delete for the benefit of AfterEditInner(). 1.2326 + mDidRangedDelete = true; 1.2327 + 1.2328 + // refresh start and end points 1.2329 + NS_ENSURE_STATE(mHTMLEditor); 1.2330 + res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(startNode), &startOffset); 1.2331 + NS_ENSURE_SUCCESS(res, res); 1.2332 + NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE); 1.2333 + nsCOMPtr<nsIDOMNode> endNode; 1.2334 + int32_t endOffset; 1.2335 + NS_ENSURE_STATE(mHTMLEditor); 1.2336 + res = mHTMLEditor->GetEndNodeAndOffset(aSelection, getter_AddRefs(endNode), &endOffset); 1.2337 + NS_ENSURE_SUCCESS(res, res); 1.2338 + NS_ENSURE_TRUE(endNode, NS_ERROR_FAILURE); 1.2339 + 1.2340 + // figure out if the endpoints are in nodes that can be merged 1.2341 + // adjust surrounding whitespace in preperation to delete selection 1.2342 + if (!IsPlaintextEditor()) 1.2343 + { 1.2344 + NS_ENSURE_STATE(mHTMLEditor); 1.2345 + nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor); 1.2346 + res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor, 1.2347 + address_of(startNode), &startOffset, 1.2348 + address_of(endNode), &endOffset); 1.2349 + NS_ENSURE_SUCCESS(res, res); 1.2350 + } 1.2351 + 1.2352 + { 1.2353 + // track location of where we are deleting 1.2354 + NS_ENSURE_STATE(mHTMLEditor); 1.2355 + nsAutoTrackDOMPoint startTracker(mHTMLEditor->mRangeUpdater, 1.2356 + address_of(startNode), &startOffset); 1.2357 + nsAutoTrackDOMPoint endTracker(mHTMLEditor->mRangeUpdater, 1.2358 + address_of(endNode), &endOffset); 1.2359 + // we are handling all ranged deletions directly now. 1.2360 + *aHandled = true; 1.2361 + 1.2362 + if (endNode == startNode) 1.2363 + { 1.2364 + NS_ENSURE_STATE(mHTMLEditor); 1.2365 + res = mHTMLEditor->DeleteSelectionImpl(aAction, aStripWrappers); 1.2366 + NS_ENSURE_SUCCESS(res, res); 1.2367 + } 1.2368 + else 1.2369 + { 1.2370 + // figure out mailcite ancestors 1.2371 + nsCOMPtr<nsIDOMNode> endCiteNode, startCiteNode; 1.2372 + res = GetTopEnclosingMailCite(startNode, address_of(startCiteNode), 1.2373 + IsPlaintextEditor()); 1.2374 + NS_ENSURE_SUCCESS(res, res); 1.2375 + res = GetTopEnclosingMailCite(endNode, address_of(endCiteNode), 1.2376 + IsPlaintextEditor()); 1.2377 + NS_ENSURE_SUCCESS(res, res); 1.2378 + 1.2379 + // if we only have a mailcite at one of the two endpoints, set the directionality 1.2380 + // of the deletion so that the selection will end up outside the mailcite. 1.2381 + if (startCiteNode && !endCiteNode) 1.2382 + { 1.2383 + aAction = nsIEditor::eNext; 1.2384 + } 1.2385 + else if (!startCiteNode && endCiteNode) 1.2386 + { 1.2387 + aAction = nsIEditor::ePrevious; 1.2388 + } 1.2389 + 1.2390 + // figure out block parents 1.2391 + nsCOMPtr<nsIDOMNode> leftParent; 1.2392 + nsCOMPtr<nsIDOMNode> rightParent; 1.2393 + if (IsBlockNode(startNode)) 1.2394 + leftParent = startNode; 1.2395 + else { 1.2396 + NS_ENSURE_STATE(mHTMLEditor); 1.2397 + leftParent = mHTMLEditor->GetBlockNodeParent(startNode); 1.2398 + } 1.2399 + 1.2400 + if (IsBlockNode(endNode)) 1.2401 + rightParent = endNode; 1.2402 + else { 1.2403 + NS_ENSURE_STATE(mHTMLEditor); 1.2404 + rightParent = mHTMLEditor->GetBlockNodeParent(endNode); 1.2405 + } 1.2406 + 1.2407 + // are endpoint block parents the same? use default deletion 1.2408 + if (leftParent == rightParent) 1.2409 + { 1.2410 + NS_ENSURE_STATE(mHTMLEditor); 1.2411 + res = mHTMLEditor->DeleteSelectionImpl(aAction, aStripWrappers); 1.2412 + } 1.2413 + else 1.2414 + { 1.2415 + // deleting across blocks 1.2416 + // are the blocks of same type? 1.2417 + NS_ENSURE_STATE(leftParent && rightParent); 1.2418 + 1.2419 + // are the blocks siblings? 1.2420 + nsCOMPtr<nsIDOMNode> leftBlockParent; 1.2421 + nsCOMPtr<nsIDOMNode> rightBlockParent; 1.2422 + leftParent->GetParentNode(getter_AddRefs(leftBlockParent)); 1.2423 + rightParent->GetParentNode(getter_AddRefs(rightBlockParent)); 1.2424 + 1.2425 + // MOOSE: this could conceivably screw up a table.. fix me. 1.2426 + if ( (leftBlockParent == rightBlockParent) 1.2427 + && (!mHTMLEditor || mHTMLEditor->NodesSameType(leftParent, rightParent)) ) 1.2428 + { 1.2429 + NS_ENSURE_STATE(mHTMLEditor); 1.2430 + if (nsHTMLEditUtils::IsParagraph(leftParent)) 1.2431 + { 1.2432 + // first delete the selection 1.2433 + NS_ENSURE_STATE(mHTMLEditor); 1.2434 + res = mHTMLEditor->DeleteSelectionImpl(aAction, aStripWrappers); 1.2435 + NS_ENSURE_SUCCESS(res, res); 1.2436 + // then join para's, insert break 1.2437 + NS_ENSURE_STATE(mHTMLEditor); 1.2438 + res = mHTMLEditor->JoinNodeDeep(leftParent,rightParent,address_of(selNode),&selOffset); 1.2439 + NS_ENSURE_SUCCESS(res, res); 1.2440 + // fix up selection 1.2441 + res = aSelection->Collapse(selNode,selOffset); 1.2442 + return res; 1.2443 + } 1.2444 + if (nsHTMLEditUtils::IsListItem(leftParent) 1.2445 + || nsHTMLEditUtils::IsHeader(leftParent)) 1.2446 + { 1.2447 + // first delete the selection 1.2448 + NS_ENSURE_STATE(mHTMLEditor); 1.2449 + res = mHTMLEditor->DeleteSelectionImpl(aAction, aStripWrappers); 1.2450 + NS_ENSURE_SUCCESS(res, res); 1.2451 + // join blocks 1.2452 + NS_ENSURE_STATE(mHTMLEditor); 1.2453 + res = mHTMLEditor->JoinNodeDeep(leftParent,rightParent,address_of(selNode),&selOffset); 1.2454 + NS_ENSURE_SUCCESS(res, res); 1.2455 + // fix up selection 1.2456 + res = aSelection->Collapse(selNode,selOffset); 1.2457 + return res; 1.2458 + } 1.2459 + } 1.2460 + 1.2461 + // else blocks not same type, or not siblings. Delete everything except 1.2462 + // table elements. 1.2463 + join = true; 1.2464 + 1.2465 + uint32_t rangeCount = aSelection->GetRangeCount(); 1.2466 + for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) { 1.2467 + nsRefPtr<nsRange> range = aSelection->GetRangeAt(rangeIdx); 1.2468 + 1.2469 + // build a list of nodes in the range 1.2470 + nsCOMArray<nsIDOMNode> arrayOfNodes; 1.2471 + nsTrivialFunctor functor; 1.2472 + nsDOMSubtreeIterator iter; 1.2473 + res = iter.Init(range); 1.2474 + NS_ENSURE_SUCCESS(res, res); 1.2475 + res = iter.AppendList(functor, arrayOfNodes); 1.2476 + NS_ENSURE_SUCCESS(res, res); 1.2477 + 1.2478 + // now that we have the list, delete non table elements 1.2479 + int32_t listCount = arrayOfNodes.Count(); 1.2480 + for (int32_t j = 0; j < listCount; j++) { 1.2481 + nsCOMPtr<nsINode> somenode = do_QueryInterface(arrayOfNodes[0]); 1.2482 + NS_ENSURE_STATE(somenode); 1.2483 + DeleteNonTableElements(somenode); 1.2484 + arrayOfNodes.RemoveObjectAt(0); 1.2485 + // If something visible is deleted, no need to join. 1.2486 + // Visible means all nodes except non-visible textnodes and breaks. 1.2487 + if (join && origCollapsed) { 1.2488 + if (!somenode->IsContent()) { 1.2489 + join = false; 1.2490 + continue; 1.2491 + } 1.2492 + nsCOMPtr<nsIContent> content = somenode->AsContent(); 1.2493 + if (content->NodeType() == nsIDOMNode::TEXT_NODE) { 1.2494 + NS_ENSURE_STATE(mHTMLEditor); 1.2495 + mHTMLEditor->IsVisTextNode(content, &join, true); 1.2496 + } else { 1.2497 + NS_ENSURE_STATE(mHTMLEditor); 1.2498 + join = content->IsHTML(nsGkAtoms::br) && 1.2499 + !mHTMLEditor->IsVisBreak(somenode->AsDOMNode()); 1.2500 + } 1.2501 + } 1.2502 + } 1.2503 + } 1.2504 + 1.2505 + // check endopints for possible text deletion. 1.2506 + // we can assume that if text node is found, we can 1.2507 + // delete to end or to begining as appropriate, 1.2508 + // since the case where both sel endpoints in same 1.2509 + // text node was already handled (we wouldn't be here) 1.2510 + NS_ENSURE_STATE(mHTMLEditor); 1.2511 + if ( mHTMLEditor->IsTextNode(startNode) ) 1.2512 + { 1.2513 + // delete to last character 1.2514 + nsCOMPtr<nsIDOMCharacterData>nodeAsText; 1.2515 + uint32_t len; 1.2516 + nodeAsText = do_QueryInterface(startNode); 1.2517 + nodeAsText->GetLength(&len); 1.2518 + if (len > (uint32_t)startOffset) 1.2519 + { 1.2520 + NS_ENSURE_STATE(mHTMLEditor); 1.2521 + res = mHTMLEditor->DeleteText(nodeAsText,startOffset,len-startOffset); 1.2522 + NS_ENSURE_SUCCESS(res, res); 1.2523 + } 1.2524 + } 1.2525 + NS_ENSURE_STATE(mHTMLEditor); 1.2526 + if ( mHTMLEditor->IsTextNode(endNode) ) 1.2527 + { 1.2528 + // delete to first character 1.2529 + nsCOMPtr<nsIDOMCharacterData>nodeAsText; 1.2530 + nodeAsText = do_QueryInterface(endNode); 1.2531 + if (endOffset) 1.2532 + { 1.2533 + NS_ENSURE_STATE(mHTMLEditor); 1.2534 + res = mHTMLEditor->DeleteText(nodeAsText,0,endOffset); 1.2535 + NS_ENSURE_SUCCESS(res, res); 1.2536 + } 1.2537 + } 1.2538 + 1.2539 + if (join) { 1.2540 + res = JoinBlocks(leftParent, rightParent, aCancel); 1.2541 + NS_ENSURE_SUCCESS(res, res); 1.2542 + } 1.2543 + } 1.2544 + } 1.2545 + } 1.2546 + //If we're joining blocks: if deleting forward the selection should be 1.2547 + //collapsed to the end of the selection, if deleting backward the selection 1.2548 + //should be collapsed to the beginning of the selection. But if we're not 1.2549 + //joining then the selection should collapse to the beginning of the 1.2550 + //selection if we'redeleting forward, because the end of the selection will 1.2551 + //still be in the next block. And same thing for deleting backwards 1.2552 + //(selection should collapse to the end, because the beginning will still 1.2553 + //be in the first block). See Bug 507936 1.2554 + if (join ? aAction == nsIEditor::eNext : aAction == nsIEditor::ePrevious) 1.2555 + { 1.2556 + res = aSelection->Collapse(endNode,endOffset); 1.2557 + } 1.2558 + else 1.2559 + { 1.2560 + res = aSelection->Collapse(startNode,startOffset); 1.2561 + } 1.2562 + return res; 1.2563 +} 1.2564 + 1.2565 + 1.2566 +/***************************************************************************************************** 1.2567 +* InsertBRIfNeeded: determines if a br is needed for current selection to not be spastic. 1.2568 +* If so, it inserts one. Callers responsibility to only call with collapsed selection. 1.2569 +* nsISelection *aSelection the collapsed selection 1.2570 +*/ 1.2571 +nsresult 1.2572 +nsHTMLEditRules::InsertBRIfNeeded(nsISelection *aSelection) 1.2573 +{ 1.2574 + NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER); 1.2575 + 1.2576 + // get selection 1.2577 + nsCOMPtr<nsIDOMNode> node; 1.2578 + int32_t offset; 1.2579 + nsresult res = mEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(node), &offset); 1.2580 + NS_ENSURE_SUCCESS(res, res); 1.2581 + NS_ENSURE_TRUE(node, NS_ERROR_FAILURE); 1.2582 + 1.2583 + // inline elements don't need any br 1.2584 + if (!IsBlockNode(node)) 1.2585 + return res; 1.2586 + 1.2587 + // examine selection 1.2588 + NS_ENSURE_STATE(mHTMLEditor); 1.2589 + nsWSRunObject wsObj(mHTMLEditor, node, offset); 1.2590 + if (((wsObj.mStartReason & WSType::block) || 1.2591 + (wsObj.mStartReason & WSType::br)) && 1.2592 + (wsObj.mEndReason & WSType::block)) { 1.2593 + // if we are tucked between block boundaries then insert a br 1.2594 + // first check that we are allowed to 1.2595 + NS_ENSURE_STATE(mHTMLEditor); 1.2596 + if (mHTMLEditor->CanContainTag(node, nsGkAtoms::br)) { 1.2597 + nsCOMPtr<nsIDOMNode> brNode; 1.2598 + NS_ENSURE_STATE(mHTMLEditor); 1.2599 + res = mHTMLEditor->CreateBR(node, offset, address_of(brNode), nsIEditor::ePrevious); 1.2600 + } 1.2601 + } 1.2602 + return res; 1.2603 +} 1.2604 + 1.2605 +/***************************************************************************************************** 1.2606 +* GetGoodSelPointForNode: Finds where at a node you would want to set the selection if you were 1.2607 +* trying to have a caret next to it. 1.2608 +* nsIDOMNode *aNode the node 1.2609 +* nsIEditor::EDirection aAction which edge to find: eNext indicates beginning, ePrevious ending 1.2610 +* nsCOMPtr<nsIDOMNode> *outSelNode desired sel node 1.2611 +* int32_t *outSelOffset desired sel offset 1.2612 +*/ 1.2613 +nsresult 1.2614 +nsHTMLEditRules::GetGoodSelPointForNode(nsIDOMNode *aNode, nsIEditor::EDirection aAction, 1.2615 + nsCOMPtr<nsIDOMNode> *outSelNode, int32_t *outSelOffset) 1.2616 +{ 1.2617 + NS_ENSURE_TRUE(aNode && outSelNode && outSelOffset, NS_ERROR_NULL_POINTER); 1.2618 + 1.2619 + nsresult res = NS_OK; 1.2620 + 1.2621 + // default values 1.2622 + *outSelNode = aNode; 1.2623 + *outSelOffset = 0; 1.2624 + 1.2625 + NS_ENSURE_STATE(mHTMLEditor); 1.2626 + if (mHTMLEditor->IsTextNode(aNode) || 1.2627 + !mHTMLEditor || mHTMLEditor->IsContainer(aNode)) 1.2628 + { 1.2629 + NS_ENSURE_STATE(mHTMLEditor); 1.2630 + if (aAction == nsIEditor::ePrevious) 1.2631 + { 1.2632 + uint32_t len; 1.2633 + res = mHTMLEditor->GetLengthOfDOMNode(aNode, len); 1.2634 + *outSelOffset = int32_t(len); 1.2635 + NS_ENSURE_SUCCESS(res, res); 1.2636 + } 1.2637 + } 1.2638 + else 1.2639 + { 1.2640 + *outSelNode = nsEditor::GetNodeLocation(aNode, outSelOffset); 1.2641 + if (!nsTextEditUtils::IsBreak(aNode) || 1.2642 + !mHTMLEditor || mHTMLEditor->IsVisBreak(aNode)) 1.2643 + { 1.2644 + NS_ENSURE_STATE(mHTMLEditor); 1.2645 + if (aAction == nsIEditor::ePrevious) 1.2646 + (*outSelOffset)++; 1.2647 + } 1.2648 + } 1.2649 + return res; 1.2650 +} 1.2651 + 1.2652 + 1.2653 +/***************************************************************************************************** 1.2654 +* JoinBlocks: this method is used to join two block elements. The right element is always joined 1.2655 +* to the left element. If the elements are the same type and not nested within each other, 1.2656 +* JoinNodesSmart is called (example, joining two list items together into one). If the elements 1.2657 +* are not the same type, or one is a descendant of the other, we instead destroy the right block 1.2658 +* placing its children into leftblock. DTD containment rules are followed throughout. 1.2659 +* nsCOMPtr<nsIDOMNode> *aLeftBlock pointer to the left block 1.2660 +* nsCOMPtr<nsIDOMNode> *aRightBlock pointer to the right block; will have contents moved to left block 1.2661 +* bool *aCanceled return TRUE if we had to cancel operation 1.2662 +*/ 1.2663 +nsresult 1.2664 +nsHTMLEditRules::JoinBlocks(nsIDOMNode *aLeftNode, 1.2665 + nsIDOMNode *aRightNode, 1.2666 + bool *aCanceled) 1.2667 +{ 1.2668 + NS_ENSURE_ARG_POINTER(aLeftNode && aRightNode); 1.2669 + 1.2670 + nsCOMPtr<nsIDOMNode> aLeftBlock, aRightBlock; 1.2671 + 1.2672 + if (IsBlockNode(aLeftNode)) { 1.2673 + aLeftBlock = aLeftNode; 1.2674 + } else if (aLeftNode) { 1.2675 + NS_ENSURE_STATE(mHTMLEditor); 1.2676 + aLeftBlock = mHTMLEditor->GetBlockNodeParent(aLeftNode); 1.2677 + } 1.2678 + 1.2679 + if (IsBlockNode(aRightNode)) { 1.2680 + aRightBlock = aRightNode; 1.2681 + } else if (aRightNode) { 1.2682 + NS_ENSURE_STATE(mHTMLEditor); 1.2683 + aRightBlock = mHTMLEditor->GetBlockNodeParent(aRightNode); 1.2684 + } 1.2685 + 1.2686 + // sanity checks 1.2687 + NS_ENSURE_TRUE(aLeftBlock && aRightBlock, NS_ERROR_NULL_POINTER); 1.2688 + NS_ENSURE_STATE(aLeftBlock != aRightBlock); 1.2689 + 1.2690 + if (nsHTMLEditUtils::IsTableElement(aLeftBlock) || 1.2691 + nsHTMLEditUtils::IsTableElement(aRightBlock)) { 1.2692 + // do not try to merge table elements 1.2693 + *aCanceled = true; 1.2694 + return NS_OK; 1.2695 + } 1.2696 + 1.2697 + // make sure we don't try to move thing's into HR's, which look like blocks but aren't containers 1.2698 + if (nsHTMLEditUtils::IsHR(aLeftBlock)) { 1.2699 + NS_ENSURE_STATE(mHTMLEditor); 1.2700 + nsCOMPtr<nsIDOMNode> realLeft = mHTMLEditor->GetBlockNodeParent(aLeftBlock); 1.2701 + aLeftBlock = realLeft; 1.2702 + } 1.2703 + if (nsHTMLEditUtils::IsHR(aRightBlock)) { 1.2704 + NS_ENSURE_STATE(mHTMLEditor); 1.2705 + nsCOMPtr<nsIDOMNode> realRight = mHTMLEditor->GetBlockNodeParent(aRightBlock); 1.2706 + aRightBlock = realRight; 1.2707 + } 1.2708 + 1.2709 + // bail if both blocks the same 1.2710 + if (aLeftBlock == aRightBlock) { 1.2711 + *aCanceled = true; 1.2712 + return NS_OK; 1.2713 + } 1.2714 + 1.2715 + // Joining a list item to its parent is a NOP. 1.2716 + if (nsHTMLEditUtils::IsList(aLeftBlock) && 1.2717 + nsHTMLEditUtils::IsListItem(aRightBlock)) { 1.2718 + nsCOMPtr<nsIDOMNode> rightParent; 1.2719 + aRightBlock->GetParentNode(getter_AddRefs(rightParent)); 1.2720 + if (rightParent == aLeftBlock) { 1.2721 + return NS_OK; 1.2722 + } 1.2723 + } 1.2724 + 1.2725 + // special rule here: if we are trying to join list items, and they are in different lists, 1.2726 + // join the lists instead. 1.2727 + bool bMergeLists = false; 1.2728 + nsIAtom* existingList = nsGkAtoms::_empty; 1.2729 + int32_t theOffset; 1.2730 + nsCOMPtr<nsIDOMNode> leftList, rightList; 1.2731 + if (nsHTMLEditUtils::IsListItem(aLeftBlock) && 1.2732 + nsHTMLEditUtils::IsListItem(aRightBlock)) { 1.2733 + aLeftBlock->GetParentNode(getter_AddRefs(leftList)); 1.2734 + aRightBlock->GetParentNode(getter_AddRefs(rightList)); 1.2735 + if (leftList && rightList && (leftList!=rightList)) 1.2736 + { 1.2737 + // there are some special complications if the lists are descendants of 1.2738 + // the other lists' items. Note that it is ok for them to be descendants 1.2739 + // of the other lists themselves, which is the usual case for sublists 1.2740 + // in our impllementation. 1.2741 + if (!nsEditorUtils::IsDescendantOf(leftList, aRightBlock, &theOffset) && 1.2742 + !nsEditorUtils::IsDescendantOf(rightList, aLeftBlock, &theOffset)) 1.2743 + { 1.2744 + aLeftBlock = leftList; 1.2745 + aRightBlock = rightList; 1.2746 + bMergeLists = true; 1.2747 + NS_ENSURE_STATE(mHTMLEditor); 1.2748 + existingList = mHTMLEditor->GetTag(leftList); 1.2749 + } 1.2750 + } 1.2751 + } 1.2752 + 1.2753 + NS_ENSURE_STATE(mHTMLEditor); 1.2754 + nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor); 1.2755 + 1.2756 + nsresult res = NS_OK; 1.2757 + int32_t rightOffset = 0; 1.2758 + int32_t leftOffset = -1; 1.2759 + 1.2760 + // theOffset below is where you find yourself in aRightBlock when you traverse upwards 1.2761 + // from aLeftBlock 1.2762 + if (nsEditorUtils::IsDescendantOf(aLeftBlock, aRightBlock, &rightOffset)) { 1.2763 + // tricky case. left block is inside right block. 1.2764 + // Do ws adjustment. This just destroys non-visible ws at boundaries we will be joining. 1.2765 + rightOffset++; 1.2766 + NS_ENSURE_STATE(mHTMLEditor); 1.2767 + res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor, 1.2768 + address_of(aLeftBlock), 1.2769 + nsWSRunObject::kBlockEnd); 1.2770 + NS_ENSURE_SUCCESS(res, res); 1.2771 + NS_ENSURE_STATE(mHTMLEditor); 1.2772 + res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor, 1.2773 + address_of(aRightBlock), 1.2774 + nsWSRunObject::kAfterBlock, 1.2775 + &rightOffset); 1.2776 + NS_ENSURE_SUCCESS(res, res); 1.2777 + // Do br adjustment. 1.2778 + nsCOMPtr<nsIDOMNode> brNode; 1.2779 + res = CheckForInvisibleBR(aLeftBlock, kBlockEnd, address_of(brNode)); 1.2780 + NS_ENSURE_SUCCESS(res, res); 1.2781 + if (bMergeLists) 1.2782 + { 1.2783 + // idea here is to take all children in rightList that are past 1.2784 + // theOffset, and pull them into leftlist. 1.2785 + nsCOMPtr<nsIDOMNode> childToMove; 1.2786 + nsCOMPtr<nsIContent> parent(do_QueryInterface(rightList)); 1.2787 + NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER); 1.2788 + 1.2789 + nsIContent *child = parent->GetChildAt(theOffset); 1.2790 + while (child) 1.2791 + { 1.2792 + childToMove = do_QueryInterface(child); 1.2793 + NS_ENSURE_STATE(mHTMLEditor); 1.2794 + res = mHTMLEditor->MoveNode(childToMove, leftList, -1); 1.2795 + NS_ENSURE_SUCCESS(res, res); 1.2796 + 1.2797 + child = parent->GetChildAt(rightOffset); 1.2798 + } 1.2799 + } 1.2800 + else 1.2801 + { 1.2802 + res = MoveBlock(aLeftBlock, aRightBlock, leftOffset, rightOffset); 1.2803 + } 1.2804 + NS_ENSURE_STATE(mHTMLEditor); 1.2805 + if (brNode) mHTMLEditor->DeleteNode(brNode); 1.2806 + // theOffset below is where you find yourself in aLeftBlock when you traverse upwards 1.2807 + // from aRightBlock 1.2808 + } else if (nsEditorUtils::IsDescendantOf(aRightBlock, aLeftBlock, &leftOffset)) { 1.2809 + // tricky case. right block is inside left block. 1.2810 + // Do ws adjustment. This just destroys non-visible ws at boundaries we will be joining. 1.2811 + NS_ENSURE_STATE(mHTMLEditor); 1.2812 + res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor, 1.2813 + address_of(aRightBlock), 1.2814 + nsWSRunObject::kBlockStart); 1.2815 + NS_ENSURE_SUCCESS(res, res); 1.2816 + NS_ENSURE_STATE(mHTMLEditor); 1.2817 + res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor, 1.2818 + address_of(aLeftBlock), 1.2819 + nsWSRunObject::kBeforeBlock, 1.2820 + &leftOffset); 1.2821 + NS_ENSURE_SUCCESS(res, res); 1.2822 + // Do br adjustment. 1.2823 + nsCOMPtr<nsIDOMNode> brNode; 1.2824 + res = CheckForInvisibleBR(aLeftBlock, kBeforeBlock, address_of(brNode), 1.2825 + leftOffset); 1.2826 + NS_ENSURE_SUCCESS(res, res); 1.2827 + if (bMergeLists) 1.2828 + { 1.2829 + res = MoveContents(rightList, leftList, &leftOffset); 1.2830 + } 1.2831 + else 1.2832 + { 1.2833 + // Left block is a parent of right block, and the parent of the previous 1.2834 + // visible content. Right block is a child and contains the contents we 1.2835 + // want to move. 1.2836 + 1.2837 + int32_t previousContentOffset; 1.2838 + nsCOMPtr<nsIDOMNode> previousContentParent; 1.2839 + 1.2840 + if (aLeftNode == aLeftBlock) { 1.2841 + // We are working with valid HTML, aLeftNode is a block node, and is 1.2842 + // therefore allowed to contain aRightBlock. This is the simple case, 1.2843 + // we will simply move the content in aRightBlock out of its block. 1.2844 + previousContentParent = aLeftBlock; 1.2845 + previousContentOffset = leftOffset; 1.2846 + } else { 1.2847 + // We try to work as well as possible with HTML that's already invalid. 1.2848 + // Although "right block" is a block, and a block must not be contained 1.2849 + // in inline elements, reality is that broken documents do exist. The 1.2850 + // DIRECT parent of "left NODE" might be an inline element. Previous 1.2851 + // versions of this code skipped inline parents until the first block 1.2852 + // parent was found (and used "left block" as the destination). 1.2853 + // However, in some situations this strategy moves the content to an 1.2854 + // unexpected position. (see bug 200416) The new idea is to make the 1.2855 + // moving content a sibling, next to the previous visible content. 1.2856 + 1.2857 + previousContentParent = 1.2858 + nsEditor::GetNodeLocation(aLeftNode, &previousContentOffset); 1.2859 + 1.2860 + // We want to move our content just after the previous visible node. 1.2861 + previousContentOffset++; 1.2862 + } 1.2863 + 1.2864 + // Because we don't want the moving content to receive the style of the 1.2865 + // previous content, we split the previous content's style. 1.2866 + 1.2867 + NS_ENSURE_STATE(mHTMLEditor); 1.2868 + nsCOMPtr<nsINode> editorRoot = mHTMLEditor->GetEditorRoot(); 1.2869 + if (!editorRoot || aLeftNode != editorRoot->AsDOMNode()) { 1.2870 + nsCOMPtr<nsIDOMNode> splittedPreviousContent; 1.2871 + NS_ENSURE_STATE(mHTMLEditor); 1.2872 + res = mHTMLEditor->SplitStyleAbovePoint(address_of(previousContentParent), 1.2873 + &previousContentOffset, 1.2874 + nullptr, nullptr, nullptr, 1.2875 + address_of(splittedPreviousContent)); 1.2876 + NS_ENSURE_SUCCESS(res, res); 1.2877 + 1.2878 + if (splittedPreviousContent) { 1.2879 + previousContentParent = 1.2880 + nsEditor::GetNodeLocation(splittedPreviousContent, 1.2881 + &previousContentOffset); 1.2882 + } 1.2883 + } 1.2884 + 1.2885 + res = MoveBlock(previousContentParent, aRightBlock, 1.2886 + previousContentOffset, rightOffset); 1.2887 + } 1.2888 + NS_ENSURE_STATE(mHTMLEditor); 1.2889 + if (brNode) mHTMLEditor->DeleteNode(brNode); 1.2890 + } 1.2891 + else 1.2892 + { 1.2893 + // normal case. blocks are siblings, or at least close enough to siblings. An example 1.2894 + // of the latter is a <p>paragraph</p><ul><li>one<li>two<li>three</ul>. The first 1.2895 + // li and the p are not true siblings, but we still want to join them if you backspace 1.2896 + // from li into p. 1.2897 + 1.2898 + // adjust whitespace at block boundaries 1.2899 + NS_ENSURE_STATE(mHTMLEditor); 1.2900 + res = nsWSRunObject::PrepareToJoinBlocks(mHTMLEditor, aLeftBlock, aRightBlock); 1.2901 + NS_ENSURE_SUCCESS(res, res); 1.2902 + // Do br adjustment. 1.2903 + nsCOMPtr<nsIDOMNode> brNode; 1.2904 + res = CheckForInvisibleBR(aLeftBlock, kBlockEnd, address_of(brNode)); 1.2905 + NS_ENSURE_SUCCESS(res, res); 1.2906 + NS_ENSURE_STATE(mHTMLEditor); 1.2907 + if (bMergeLists || mHTMLEditor->NodesSameType(aLeftBlock, aRightBlock)) { 1.2908 + // nodes are same type. merge them. 1.2909 + nsCOMPtr<nsIDOMNode> parent; 1.2910 + int32_t offset; 1.2911 + res = JoinNodesSmart(aLeftBlock, aRightBlock, address_of(parent), &offset); 1.2912 + if (NS_SUCCEEDED(res) && bMergeLists) 1.2913 + { 1.2914 + nsCOMPtr<nsIDOMNode> newBlock; 1.2915 + res = ConvertListType(aRightBlock, address_of(newBlock), 1.2916 + existingList, nsGkAtoms::li); 1.2917 + } 1.2918 + } 1.2919 + else 1.2920 + { 1.2921 + // nodes are disimilar types. 1.2922 + res = MoveBlock(aLeftBlock, aRightBlock, leftOffset, rightOffset); 1.2923 + } 1.2924 + if (NS_SUCCEEDED(res) && brNode) 1.2925 + { 1.2926 + NS_ENSURE_STATE(mHTMLEditor); 1.2927 + res = mHTMLEditor->DeleteNode(brNode); 1.2928 + } 1.2929 + } 1.2930 + return res; 1.2931 +} 1.2932 + 1.2933 + 1.2934 +/***************************************************************************************************** 1.2935 +* MoveBlock: this method is used to move the content from rightBlock into leftBlock 1.2936 +* Note that the "block" might merely be inline nodes between <br>s, or between blocks, etc. 1.2937 +* DTD containment rules are followed throughout. 1.2938 +* nsIDOMNode *aLeftBlock parent to receive moved content 1.2939 +* nsIDOMNode *aRightBlock parent to provide moved content 1.2940 +* int32_t aLeftOffset offset in aLeftBlock to move content to 1.2941 +* int32_t aRightOffset offset in aRightBlock to move content from 1.2942 +*/ 1.2943 +nsresult 1.2944 +nsHTMLEditRules::MoveBlock(nsIDOMNode *aLeftBlock, nsIDOMNode *aRightBlock, int32_t aLeftOffset, int32_t aRightOffset) 1.2945 +{ 1.2946 + nsCOMArray<nsIDOMNode> arrayOfNodes; 1.2947 + nsCOMPtr<nsISupports> isupports; 1.2948 + // GetNodesFromPoint is the workhorse that figures out what we wnat to move. 1.2949 + nsresult res = GetNodesFromPoint(::DOMPoint(aRightBlock,aRightOffset), 1.2950 + EditAction::makeList, arrayOfNodes, true); 1.2951 + NS_ENSURE_SUCCESS(res, res); 1.2952 + int32_t listCount = arrayOfNodes.Count(); 1.2953 + int32_t i; 1.2954 + for (i=0; i<listCount; i++) 1.2955 + { 1.2956 + // get the node to act on 1.2957 + nsIDOMNode* curNode = arrayOfNodes[i]; 1.2958 + if (IsBlockNode(curNode)) 1.2959 + { 1.2960 + // For block nodes, move their contents only, then delete block. 1.2961 + res = MoveContents(curNode, aLeftBlock, &aLeftOffset); 1.2962 + NS_ENSURE_SUCCESS(res, res); 1.2963 + NS_ENSURE_STATE(mHTMLEditor); 1.2964 + res = mHTMLEditor->DeleteNode(curNode); 1.2965 + } 1.2966 + else 1.2967 + { 1.2968 + // otherwise move the content as is, checking against the dtd. 1.2969 + res = MoveNodeSmart(curNode, aLeftBlock, &aLeftOffset); 1.2970 + } 1.2971 + } 1.2972 + return res; 1.2973 +} 1.2974 + 1.2975 +/***************************************************************************************************** 1.2976 +* MoveNodeSmart: this method is used to move node aSource to (aDest,aOffset). 1.2977 +* DTD containment rules are followed throughout. aOffset is updated to point _after_ 1.2978 +* inserted content. 1.2979 +* nsIDOMNode *aSource the selection. 1.2980 +* nsIDOMNode *aDest parent to receive moved content 1.2981 +* int32_t *aOffset offset in aDest to move content to 1.2982 +*/ 1.2983 +nsresult 1.2984 +nsHTMLEditRules::MoveNodeSmart(nsIDOMNode *aSource, nsIDOMNode *aDest, int32_t *aOffset) 1.2985 +{ 1.2986 + NS_ENSURE_TRUE(aSource && aDest && aOffset, NS_ERROR_NULL_POINTER); 1.2987 + 1.2988 + nsresult res; 1.2989 + // check if this node can go into the destination node 1.2990 + NS_ENSURE_STATE(mHTMLEditor); 1.2991 + if (mHTMLEditor->CanContain(aDest, aSource)) { 1.2992 + // if it can, move it there 1.2993 + NS_ENSURE_STATE(mHTMLEditor); 1.2994 + res = mHTMLEditor->MoveNode(aSource, aDest, *aOffset); 1.2995 + NS_ENSURE_SUCCESS(res, res); 1.2996 + if (*aOffset != -1) ++(*aOffset); 1.2997 + } 1.2998 + else 1.2999 + { 1.3000 + // if it can't, move its children, and then delete it. 1.3001 + res = MoveContents(aSource, aDest, aOffset); 1.3002 + NS_ENSURE_SUCCESS(res, res); 1.3003 + NS_ENSURE_STATE(mHTMLEditor); 1.3004 + res = mHTMLEditor->DeleteNode(aSource); 1.3005 + NS_ENSURE_SUCCESS(res, res); 1.3006 + } 1.3007 + return NS_OK; 1.3008 +} 1.3009 + 1.3010 +/***************************************************************************************************** 1.3011 +* MoveContents: this method is used to move node the _contents_ of aSource to (aDest,aOffset). 1.3012 +* DTD containment rules are followed throughout. aOffset is updated to point _after_ 1.3013 +* inserted content. aSource is deleted. 1.3014 +* nsIDOMNode *aSource the selection. 1.3015 +* nsIDOMNode *aDest parent to receive moved content 1.3016 +* int32_t *aOffset offset in aDest to move content to 1.3017 +*/ 1.3018 +nsresult 1.3019 +nsHTMLEditRules::MoveContents(nsIDOMNode *aSource, nsIDOMNode *aDest, int32_t *aOffset) 1.3020 +{ 1.3021 + NS_ENSURE_TRUE(aSource && aDest && aOffset, NS_ERROR_NULL_POINTER); 1.3022 + if (aSource == aDest) return NS_ERROR_ILLEGAL_VALUE; 1.3023 + NS_ENSURE_STATE(mHTMLEditor); 1.3024 + NS_ASSERTION(!mHTMLEditor->IsTextNode(aSource), "#text does not have contents"); 1.3025 + 1.3026 + nsCOMPtr<nsIDOMNode> child; 1.3027 + nsAutoString tag; 1.3028 + nsresult res; 1.3029 + aSource->GetFirstChild(getter_AddRefs(child)); 1.3030 + while (child) 1.3031 + { 1.3032 + res = MoveNodeSmart(child, aDest, aOffset); 1.3033 + NS_ENSURE_SUCCESS(res, res); 1.3034 + aSource->GetFirstChild(getter_AddRefs(child)); 1.3035 + } 1.3036 + return NS_OK; 1.3037 +} 1.3038 + 1.3039 + 1.3040 +nsresult 1.3041 +nsHTMLEditRules::DeleteNonTableElements(nsINode* aNode) 1.3042 +{ 1.3043 + MOZ_ASSERT(aNode); 1.3044 + if (!nsHTMLEditUtils::IsTableElementButNotTable(aNode)) { 1.3045 + NS_ENSURE_STATE(mHTMLEditor); 1.3046 + return mHTMLEditor->DeleteNode(aNode->AsDOMNode()); 1.3047 + } 1.3048 + 1.3049 + for (int32_t i = aNode->GetChildCount() - 1; i >= 0; --i) { 1.3050 + nsresult rv = DeleteNonTableElements(aNode->GetChildAt(i)); 1.3051 + NS_ENSURE_SUCCESS(rv, rv); 1.3052 + } 1.3053 + return NS_OK; 1.3054 +} 1.3055 + 1.3056 +nsresult 1.3057 +nsHTMLEditRules::DidDeleteSelection(nsISelection *aSelection, 1.3058 + nsIEditor::EDirection aDir, 1.3059 + nsresult aResult) 1.3060 +{ 1.3061 + if (!aSelection) { return NS_ERROR_NULL_POINTER; } 1.3062 + 1.3063 + // find where we are 1.3064 + nsCOMPtr<nsIDOMNode> startNode; 1.3065 + int32_t startOffset; 1.3066 + nsresult res = mEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(startNode), &startOffset); 1.3067 + NS_ENSURE_SUCCESS(res, res); 1.3068 + NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE); 1.3069 + 1.3070 + // find any enclosing mailcite 1.3071 + nsCOMPtr<nsIDOMNode> citeNode; 1.3072 + res = GetTopEnclosingMailCite(startNode, address_of(citeNode), 1.3073 + IsPlaintextEditor()); 1.3074 + NS_ENSURE_SUCCESS(res, res); 1.3075 + if (citeNode) { 1.3076 + nsCOMPtr<nsINode> cite = do_QueryInterface(citeNode); 1.3077 + bool isEmpty = true, seenBR = false; 1.3078 + NS_ENSURE_STATE(mHTMLEditor); 1.3079 + mHTMLEditor->IsEmptyNodeImpl(cite, &isEmpty, true, true, false, &seenBR); 1.3080 + if (isEmpty) 1.3081 + { 1.3082 + nsCOMPtr<nsIDOMNode> brNode; 1.3083 + int32_t offset; 1.3084 + nsCOMPtr<nsIDOMNode> parent = nsEditor::GetNodeLocation(citeNode, &offset); 1.3085 + NS_ENSURE_STATE(mHTMLEditor); 1.3086 + res = mHTMLEditor->DeleteNode(citeNode); 1.3087 + NS_ENSURE_SUCCESS(res, res); 1.3088 + if (parent && seenBR) 1.3089 + { 1.3090 + NS_ENSURE_STATE(mHTMLEditor); 1.3091 + res = mHTMLEditor->CreateBR(parent, offset, address_of(brNode)); 1.3092 + NS_ENSURE_SUCCESS(res, res); 1.3093 + aSelection->Collapse(parent, offset); 1.3094 + } 1.3095 + } 1.3096 + } 1.3097 + 1.3098 + // call through to base class 1.3099 + return nsTextEditRules::DidDeleteSelection(aSelection, aDir, aResult); 1.3100 +} 1.3101 + 1.3102 +nsresult 1.3103 +nsHTMLEditRules::WillMakeList(Selection* aSelection, 1.3104 + const nsAString* aListType, 1.3105 + bool aEntireList, 1.3106 + const nsAString* aBulletType, 1.3107 + bool* aCancel, 1.3108 + bool* aHandled, 1.3109 + const nsAString* aItemType) 1.3110 +{ 1.3111 + if (!aSelection || !aListType || !aCancel || !aHandled) { 1.3112 + return NS_ERROR_NULL_POINTER; 1.3113 + } 1.3114 + nsCOMPtr<nsIAtom> listTypeAtom = do_GetAtom(*aListType); 1.3115 + NS_ENSURE_TRUE(listTypeAtom, NS_ERROR_OUT_OF_MEMORY); 1.3116 + 1.3117 + nsresult res = WillInsert(aSelection, aCancel); 1.3118 + NS_ENSURE_SUCCESS(res, res); 1.3119 + 1.3120 + // initialize out param 1.3121 + // we want to ignore result of WillInsert() 1.3122 + *aCancel = false; 1.3123 + *aHandled = false; 1.3124 + 1.3125 + // deduce what tag to use for list items 1.3126 + nsCOMPtr<nsIAtom> itemType; 1.3127 + if (aItemType) { 1.3128 + itemType = do_GetAtom(*aItemType); 1.3129 + NS_ENSURE_TRUE(itemType, NS_ERROR_OUT_OF_MEMORY); 1.3130 + } else if (listTypeAtom == nsGkAtoms::dl) { 1.3131 + itemType = nsGkAtoms::dd; 1.3132 + } else { 1.3133 + itemType = nsGkAtoms::li; 1.3134 + } 1.3135 + 1.3136 + // convert the selection ranges into "promoted" selection ranges: 1.3137 + // this basically just expands the range to include the immediate 1.3138 + // block parent, and then further expands to include any ancestors 1.3139 + // whose children are all in the range 1.3140 + 1.3141 + *aHandled = true; 1.3142 + 1.3143 + res = NormalizeSelection(aSelection); 1.3144 + NS_ENSURE_SUCCESS(res, res); 1.3145 + NS_ENSURE_STATE(mHTMLEditor); 1.3146 + nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor); 1.3147 + 1.3148 + nsCOMArray<nsIDOMNode> arrayOfNodes; 1.3149 + res = GetListActionNodes(arrayOfNodes, aEntireList); 1.3150 + NS_ENSURE_SUCCESS(res, res); 1.3151 + 1.3152 + int32_t listCount = arrayOfNodes.Count(); 1.3153 + 1.3154 + // check if all our nodes are <br>s, or empty inlines 1.3155 + bool bOnlyBreaks = true; 1.3156 + for (int32_t j = 0; j < listCount; j++) { 1.3157 + nsIDOMNode* curNode = arrayOfNodes[j]; 1.3158 + // if curNode is not a Break or empty inline, we're done 1.3159 + if (!nsTextEditUtils::IsBreak(curNode) && !IsEmptyInline(curNode)) { 1.3160 + bOnlyBreaks = false; 1.3161 + break; 1.3162 + } 1.3163 + } 1.3164 + 1.3165 + // if no nodes, we make empty list. Ditto if the user tried to make a list 1.3166 + // of some # of breaks. 1.3167 + if (!listCount || bOnlyBreaks) { 1.3168 + nsCOMPtr<nsIDOMNode> parent, theList, theListItem; 1.3169 + int32_t offset; 1.3170 + 1.3171 + // if only breaks, delete them 1.3172 + if (bOnlyBreaks) { 1.3173 + for (int32_t j = 0; j < (int32_t)listCount; j++) { 1.3174 + NS_ENSURE_STATE(mHTMLEditor); 1.3175 + res = mHTMLEditor->DeleteNode(arrayOfNodes[j]); 1.3176 + NS_ENSURE_SUCCESS(res, res); 1.3177 + } 1.3178 + } 1.3179 + 1.3180 + // get selection location 1.3181 + NS_ENSURE_STATE(mHTMLEditor); 1.3182 + res = mHTMLEditor->GetStartNodeAndOffset(aSelection, 1.3183 + getter_AddRefs(parent), &offset); 1.3184 + NS_ENSURE_SUCCESS(res, res); 1.3185 + 1.3186 + // make sure we can put a list here 1.3187 + NS_ENSURE_STATE(mHTMLEditor); 1.3188 + if (!mHTMLEditor->CanContainTag(parent, listTypeAtom)) { 1.3189 + *aCancel = true; 1.3190 + return NS_OK; 1.3191 + } 1.3192 + res = SplitAsNeeded(aListType, address_of(parent), &offset); 1.3193 + NS_ENSURE_SUCCESS(res, res); 1.3194 + NS_ENSURE_STATE(mHTMLEditor); 1.3195 + res = mHTMLEditor->CreateNode(*aListType, parent, offset, 1.3196 + getter_AddRefs(theList)); 1.3197 + NS_ENSURE_SUCCESS(res, res); 1.3198 + NS_ENSURE_STATE(mHTMLEditor); 1.3199 + res = mHTMLEditor->CreateNode(nsDependentAtomString(itemType), theList, 0, 1.3200 + getter_AddRefs(theListItem)); 1.3201 + NS_ENSURE_SUCCESS(res, res); 1.3202 + // remember our new block for postprocessing 1.3203 + mNewBlock = theListItem; 1.3204 + // put selection in new list item 1.3205 + res = aSelection->Collapse(theListItem, 0); 1.3206 + // to prevent selection resetter from overriding us 1.3207 + selectionResetter.Abort(); 1.3208 + *aHandled = true; 1.3209 + return res; 1.3210 + } 1.3211 + 1.3212 + // if there is only one node in the array, and it is a list, div, or 1.3213 + // blockquote, then look inside of it until we find inner list or content. 1.3214 + 1.3215 + res = LookInsideDivBQandList(arrayOfNodes); 1.3216 + NS_ENSURE_SUCCESS(res, res); 1.3217 + 1.3218 + // Ok, now go through all the nodes and put then in the list, 1.3219 + // or whatever is approriate. Wohoo! 1.3220 + 1.3221 + listCount = arrayOfNodes.Count(); 1.3222 + nsCOMPtr<nsIDOMNode> curParent; 1.3223 + nsCOMPtr<nsIDOMNode> curList; 1.3224 + nsCOMPtr<nsIDOMNode> prevListItem; 1.3225 + 1.3226 + for (int32_t i = 0; i < listCount; i++) { 1.3227 + // here's where we actually figure out what to do 1.3228 + nsCOMPtr<nsIDOMNode> newBlock; 1.3229 + nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i]; 1.3230 + int32_t offset; 1.3231 + curParent = nsEditor::GetNodeLocation(curNode, &offset); 1.3232 + 1.3233 + // make sure we don't assemble content that is in different table cells 1.3234 + // into the same list. respect table cell boundaries when listifying. 1.3235 + if (curList && InDifferentTableElements(curList, curNode)) { 1.3236 + curList = nullptr; 1.3237 + } 1.3238 + 1.3239 + // if curNode is a Break, delete it, and quit remembering prev list item 1.3240 + if (nsTextEditUtils::IsBreak(curNode)) { 1.3241 + NS_ENSURE_STATE(mHTMLEditor); 1.3242 + res = mHTMLEditor->DeleteNode(curNode); 1.3243 + NS_ENSURE_SUCCESS(res, res); 1.3244 + prevListItem = 0; 1.3245 + continue; 1.3246 + } else if (IsEmptyInline(curNode)) { 1.3247 + // if curNode is an empty inline container, delete it 1.3248 + NS_ENSURE_STATE(mHTMLEditor); 1.3249 + res = mHTMLEditor->DeleteNode(curNode); 1.3250 + NS_ENSURE_SUCCESS(res, res); 1.3251 + continue; 1.3252 + } 1.3253 + 1.3254 + if (nsHTMLEditUtils::IsList(curNode)) { 1.3255 + // do we have a curList already? 1.3256 + if (curList && !nsEditorUtils::IsDescendantOf(curNode, curList)) { 1.3257 + // move all of our children into curList. cheezy way to do it: move 1.3258 + // whole list and then RemoveContainer() on the list. ConvertListType 1.3259 + // first: that routine handles converting the list item types, if 1.3260 + // needed 1.3261 + NS_ENSURE_STATE(mHTMLEditor); 1.3262 + res = mHTMLEditor->MoveNode(curNode, curList, -1); 1.3263 + NS_ENSURE_SUCCESS(res, res); 1.3264 + res = ConvertListType(curNode, address_of(newBlock), listTypeAtom, 1.3265 + itemType); 1.3266 + NS_ENSURE_SUCCESS(res, res); 1.3267 + NS_ENSURE_STATE(mHTMLEditor); 1.3268 + res = mHTMLEditor->RemoveBlockContainer(newBlock); 1.3269 + NS_ENSURE_SUCCESS(res, res); 1.3270 + } else { 1.3271 + // replace list with new list type 1.3272 + res = ConvertListType(curNode, address_of(newBlock), listTypeAtom, 1.3273 + itemType); 1.3274 + NS_ENSURE_SUCCESS(res, res); 1.3275 + curList = newBlock; 1.3276 + } 1.3277 + prevListItem = 0; 1.3278 + continue; 1.3279 + } 1.3280 + 1.3281 + if (nsHTMLEditUtils::IsListItem(curNode)) { 1.3282 + NS_ENSURE_STATE(mHTMLEditor); 1.3283 + if (mHTMLEditor->GetTag(curParent) != listTypeAtom) { 1.3284 + // list item is in wrong type of list. if we don't have a curList, 1.3285 + // split the old list and make a new list of correct type. 1.3286 + if (!curList || nsEditorUtils::IsDescendantOf(curNode, curList)) { 1.3287 + NS_ENSURE_STATE(mHTMLEditor); 1.3288 + res = mHTMLEditor->SplitNode(curParent, offset, 1.3289 + getter_AddRefs(newBlock)); 1.3290 + NS_ENSURE_SUCCESS(res, res); 1.3291 + int32_t offset; 1.3292 + nsCOMPtr<nsIDOMNode> parent = nsEditor::GetNodeLocation(curParent, &offset); 1.3293 + NS_ENSURE_STATE(mHTMLEditor); 1.3294 + res = mHTMLEditor->CreateNode(*aListType, parent, offset, 1.3295 + getter_AddRefs(curList)); 1.3296 + NS_ENSURE_SUCCESS(res, res); 1.3297 + } 1.3298 + // move list item to new list 1.3299 + NS_ENSURE_STATE(mHTMLEditor); 1.3300 + res = mHTMLEditor->MoveNode(curNode, curList, -1); 1.3301 + NS_ENSURE_SUCCESS(res, res); 1.3302 + // convert list item type if needed 1.3303 + NS_ENSURE_STATE(mHTMLEditor); 1.3304 + if (!mHTMLEditor->NodeIsType(curNode, itemType)) { 1.3305 + NS_ENSURE_STATE(mHTMLEditor); 1.3306 + res = mHTMLEditor->ReplaceContainer(curNode, address_of(newBlock), 1.3307 + nsDependentAtomString(itemType)); 1.3308 + NS_ENSURE_SUCCESS(res, res); 1.3309 + } 1.3310 + } else { 1.3311 + // item is in right type of list. But we might still have to move it. 1.3312 + // and we might need to convert list item types. 1.3313 + if (!curList) { 1.3314 + curList = curParent; 1.3315 + } else if (curParent != curList) { 1.3316 + // move list item to new list 1.3317 + NS_ENSURE_STATE(mHTMLEditor); 1.3318 + res = mHTMLEditor->MoveNode(curNode, curList, -1); 1.3319 + NS_ENSURE_SUCCESS(res, res); 1.3320 + } 1.3321 + NS_ENSURE_STATE(mHTMLEditor); 1.3322 + if (!mHTMLEditor->NodeIsType(curNode, itemType)) { 1.3323 + NS_ENSURE_STATE(mHTMLEditor); 1.3324 + res = mHTMLEditor->ReplaceContainer(curNode, address_of(newBlock), 1.3325 + nsDependentAtomString(itemType)); 1.3326 + NS_ENSURE_SUCCESS(res, res); 1.3327 + } 1.3328 + } 1.3329 + nsCOMPtr<nsIDOMElement> curElement = do_QueryInterface(curNode); 1.3330 + NS_NAMED_LITERAL_STRING(typestr, "type"); 1.3331 + if (aBulletType && !aBulletType->IsEmpty()) { 1.3332 + NS_ENSURE_STATE(mHTMLEditor); 1.3333 + res = mHTMLEditor->SetAttribute(curElement, typestr, *aBulletType); 1.3334 + } else { 1.3335 + NS_ENSURE_STATE(mHTMLEditor); 1.3336 + res = mHTMLEditor->RemoveAttribute(curElement, typestr); 1.3337 + } 1.3338 + NS_ENSURE_SUCCESS(res, res); 1.3339 + continue; 1.3340 + } 1.3341 + 1.3342 + // if we hit a div clear our prevListItem, insert divs contents 1.3343 + // into our node array, and remove the div 1.3344 + if (nsHTMLEditUtils::IsDiv(curNode)) { 1.3345 + prevListItem = nullptr; 1.3346 + int32_t j = i + 1; 1.3347 + res = GetInnerContent(curNode, arrayOfNodes, &j); 1.3348 + NS_ENSURE_SUCCESS(res, res); 1.3349 + NS_ENSURE_STATE(mHTMLEditor); 1.3350 + res = mHTMLEditor->RemoveContainer(curNode); 1.3351 + NS_ENSURE_SUCCESS(res, res); 1.3352 + listCount = arrayOfNodes.Count(); 1.3353 + continue; 1.3354 + } 1.3355 + 1.3356 + // need to make a list to put things in if we haven't already, 1.3357 + if (!curList) { 1.3358 + res = SplitAsNeeded(aListType, address_of(curParent), &offset); 1.3359 + NS_ENSURE_SUCCESS(res, res); 1.3360 + NS_ENSURE_STATE(mHTMLEditor); 1.3361 + res = mHTMLEditor->CreateNode(*aListType, curParent, offset, 1.3362 + getter_AddRefs(curList)); 1.3363 + NS_ENSURE_SUCCESS(res, res); 1.3364 + // remember our new block for postprocessing 1.3365 + mNewBlock = curList; 1.3366 + // curList is now the correct thing to put curNode in 1.3367 + prevListItem = 0; 1.3368 + } 1.3369 + 1.3370 + // if curNode isn't a list item, we must wrap it in one 1.3371 + nsCOMPtr<nsIDOMNode> listItem; 1.3372 + if (!nsHTMLEditUtils::IsListItem(curNode)) { 1.3373 + if (IsInlineNode(curNode) && prevListItem) { 1.3374 + // this is a continuation of some inline nodes that belong together in 1.3375 + // the same list item. use prevListItem 1.3376 + NS_ENSURE_STATE(mHTMLEditor); 1.3377 + res = mHTMLEditor->MoveNode(curNode, prevListItem, -1); 1.3378 + NS_ENSURE_SUCCESS(res, res); 1.3379 + } else { 1.3380 + // don't wrap li around a paragraph. instead replace paragraph with li 1.3381 + if (nsHTMLEditUtils::IsParagraph(curNode)) { 1.3382 + NS_ENSURE_STATE(mHTMLEditor); 1.3383 + res = mHTMLEditor->ReplaceContainer(curNode, address_of(listItem), 1.3384 + nsDependentAtomString(itemType)); 1.3385 + } else { 1.3386 + NS_ENSURE_STATE(mHTMLEditor); 1.3387 + res = mHTMLEditor->InsertContainerAbove(curNode, address_of(listItem), 1.3388 + nsDependentAtomString(itemType)); 1.3389 + } 1.3390 + NS_ENSURE_SUCCESS(res, res); 1.3391 + if (IsInlineNode(curNode)) { 1.3392 + prevListItem = listItem; 1.3393 + } else { 1.3394 + prevListItem = nullptr; 1.3395 + } 1.3396 + } 1.3397 + } else { 1.3398 + listItem = curNode; 1.3399 + } 1.3400 + 1.3401 + if (listItem) { 1.3402 + // if we made a new list item, deal with it: tuck the listItem into the 1.3403 + // end of the active list 1.3404 + NS_ENSURE_STATE(mHTMLEditor); 1.3405 + res = mHTMLEditor->MoveNode(listItem, curList, -1); 1.3406 + NS_ENSURE_SUCCESS(res, res); 1.3407 + } 1.3408 + } 1.3409 + 1.3410 + return res; 1.3411 +} 1.3412 + 1.3413 + 1.3414 +nsresult 1.3415 +nsHTMLEditRules::WillRemoveList(Selection* aSelection, 1.3416 + bool aOrdered, 1.3417 + bool *aCancel, 1.3418 + bool *aHandled) 1.3419 +{ 1.3420 + if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; } 1.3421 + // initialize out param 1.3422 + *aCancel = false; 1.3423 + *aHandled = true; 1.3424 + 1.3425 + nsresult res = NormalizeSelection(aSelection); 1.3426 + NS_ENSURE_SUCCESS(res, res); 1.3427 + NS_ENSURE_STATE(mHTMLEditor); 1.3428 + nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor); 1.3429 + 1.3430 + nsCOMArray<nsIDOMRange> arrayOfRanges; 1.3431 + res = GetPromotedRanges(aSelection, arrayOfRanges, EditAction::makeList); 1.3432 + NS_ENSURE_SUCCESS(res, res); 1.3433 + 1.3434 + // use these ranges to contruct a list of nodes to act on. 1.3435 + nsCOMArray<nsIDOMNode> arrayOfNodes; 1.3436 + res = GetListActionNodes(arrayOfNodes, false); 1.3437 + NS_ENSURE_SUCCESS(res, res); 1.3438 + 1.3439 + // Remove all non-editable nodes. Leave them be. 1.3440 + int32_t listCount = arrayOfNodes.Count(); 1.3441 + int32_t i; 1.3442 + for (i=listCount-1; i>=0; i--) 1.3443 + { 1.3444 + nsIDOMNode* testNode = arrayOfNodes[i]; 1.3445 + NS_ENSURE_STATE(mHTMLEditor); 1.3446 + if (!mHTMLEditor->IsEditable(testNode)) 1.3447 + { 1.3448 + arrayOfNodes.RemoveObjectAt(i); 1.3449 + } 1.3450 + } 1.3451 + 1.3452 + // reset list count 1.3453 + listCount = arrayOfNodes.Count(); 1.3454 + 1.3455 + // Only act on lists or list items in the array 1.3456 + nsCOMPtr<nsIDOMNode> curParent; 1.3457 + for (i=0; i<listCount; i++) 1.3458 + { 1.3459 + // here's where we actually figure out what to do 1.3460 + nsIDOMNode* curNode = arrayOfNodes[i]; 1.3461 + int32_t offset; 1.3462 + curParent = nsEditor::GetNodeLocation(curNode, &offset); 1.3463 + 1.3464 + if (nsHTMLEditUtils::IsListItem(curNode)) // unlist this listitem 1.3465 + { 1.3466 + bool bOutOfList; 1.3467 + do 1.3468 + { 1.3469 + res = PopListItem(curNode, &bOutOfList); 1.3470 + NS_ENSURE_SUCCESS(res, res); 1.3471 + } while (!bOutOfList); // keep popping it out until it's not in a list anymore 1.3472 + } 1.3473 + else if (nsHTMLEditUtils::IsList(curNode)) // node is a list, move list items out 1.3474 + { 1.3475 + res = RemoveListStructure(curNode); 1.3476 + NS_ENSURE_SUCCESS(res, res); 1.3477 + } 1.3478 + } 1.3479 + return res; 1.3480 +} 1.3481 + 1.3482 + 1.3483 +nsresult 1.3484 +nsHTMLEditRules::WillMakeDefListItem(Selection* aSelection, 1.3485 + const nsAString *aItemType, 1.3486 + bool aEntireList, 1.3487 + bool *aCancel, 1.3488 + bool *aHandled) 1.3489 +{ 1.3490 + // for now we let WillMakeList handle this 1.3491 + NS_NAMED_LITERAL_STRING(listType, "dl"); 1.3492 + return WillMakeList(aSelection, &listType, aEntireList, nullptr, aCancel, aHandled, aItemType); 1.3493 +} 1.3494 + 1.3495 +nsresult 1.3496 +nsHTMLEditRules::WillMakeBasicBlock(Selection* aSelection, 1.3497 + const nsAString *aBlockType, 1.3498 + bool *aCancel, 1.3499 + bool *aHandled) 1.3500 +{ 1.3501 + if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; } 1.3502 + // initialize out param 1.3503 + *aCancel = false; 1.3504 + *aHandled = false; 1.3505 + 1.3506 + nsresult res = WillInsert(aSelection, aCancel); 1.3507 + NS_ENSURE_SUCCESS(res, res); 1.3508 + // initialize out param 1.3509 + // we want to ignore result of WillInsert() 1.3510 + *aCancel = false; 1.3511 + res = NormalizeSelection(aSelection); 1.3512 + NS_ENSURE_SUCCESS(res, res); 1.3513 + NS_ENSURE_STATE(mHTMLEditor); 1.3514 + nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor); 1.3515 + NS_ENSURE_STATE(mHTMLEditor); 1.3516 + nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor); 1.3517 + *aHandled = true; 1.3518 + nsString tString(*aBlockType); 1.3519 + 1.3520 + // contruct a list of nodes to act on. 1.3521 + nsCOMArray<nsIDOMNode> arrayOfNodes; 1.3522 + res = GetNodesFromSelection(aSelection, EditAction::makeBasicBlock, 1.3523 + arrayOfNodes); 1.3524 + NS_ENSURE_SUCCESS(res, res); 1.3525 + 1.3526 + // Remove all non-editable nodes. Leave them be. 1.3527 + int32_t listCount = arrayOfNodes.Count(); 1.3528 + int32_t i; 1.3529 + for (i=listCount-1; i>=0; i--) 1.3530 + { 1.3531 + NS_ENSURE_STATE(mHTMLEditor); 1.3532 + if (!mHTMLEditor->IsEditable(arrayOfNodes[i])) 1.3533 + { 1.3534 + arrayOfNodes.RemoveObjectAt(i); 1.3535 + } 1.3536 + } 1.3537 + 1.3538 + // reset list count 1.3539 + listCount = arrayOfNodes.Count(); 1.3540 + 1.3541 + // if nothing visible in list, make an empty block 1.3542 + if (ListIsEmptyLine(arrayOfNodes)) 1.3543 + { 1.3544 + nsCOMPtr<nsIDOMNode> parent, theBlock; 1.3545 + int32_t offset; 1.3546 + 1.3547 + // get selection location 1.3548 + NS_ENSURE_STATE(mHTMLEditor); 1.3549 + res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(parent), &offset); 1.3550 + NS_ENSURE_SUCCESS(res, res); 1.3551 + if (tString.EqualsLiteral("normal") || 1.3552 + tString.IsEmpty() ) // we are removing blocks (going to "body text") 1.3553 + { 1.3554 + nsCOMPtr<nsIDOMNode> curBlock = parent; 1.3555 + if (!IsBlockNode(curBlock)) { 1.3556 + NS_ENSURE_STATE(mHTMLEditor); 1.3557 + curBlock = mHTMLEditor->GetBlockNodeParent(parent); 1.3558 + } 1.3559 + nsCOMPtr<nsIDOMNode> curBlockPar; 1.3560 + NS_ENSURE_TRUE(curBlock, NS_ERROR_NULL_POINTER); 1.3561 + curBlock->GetParentNode(getter_AddRefs(curBlockPar)); 1.3562 + if (nsHTMLEditUtils::IsFormatNode(curBlock)) 1.3563 + { 1.3564 + // if the first editable node after selection is a br, consume it. Otherwise 1.3565 + // it gets pushed into a following block after the split, which is visually bad. 1.3566 + nsCOMPtr<nsIDOMNode> brNode; 1.3567 + NS_ENSURE_STATE(mHTMLEditor); 1.3568 + res = mHTMLEditor->GetNextHTMLNode(parent, offset, address_of(brNode)); 1.3569 + NS_ENSURE_SUCCESS(res, res); 1.3570 + if (brNode && nsTextEditUtils::IsBreak(brNode)) 1.3571 + { 1.3572 + NS_ENSURE_STATE(mHTMLEditor); 1.3573 + res = mHTMLEditor->DeleteNode(brNode); 1.3574 + NS_ENSURE_SUCCESS(res, res); 1.3575 + } 1.3576 + // do the splits! 1.3577 + NS_ENSURE_STATE(mHTMLEditor); 1.3578 + res = mHTMLEditor->SplitNodeDeep(curBlock, parent, offset, &offset, true); 1.3579 + NS_ENSURE_SUCCESS(res, res); 1.3580 + // put a br at the split point 1.3581 + NS_ENSURE_STATE(mHTMLEditor); 1.3582 + res = mHTMLEditor->CreateBR(curBlockPar, offset, address_of(brNode)); 1.3583 + NS_ENSURE_SUCCESS(res, res); 1.3584 + // put selection at the split point 1.3585 + res = aSelection->Collapse(curBlockPar, offset); 1.3586 + selectionResetter.Abort(); // to prevent selection reseter from overriding us. 1.3587 + *aHandled = true; 1.3588 + } 1.3589 + // else nothing to do! 1.3590 + } 1.3591 + else // we are making a block 1.3592 + { 1.3593 + // consume a br, if needed 1.3594 + nsCOMPtr<nsIDOMNode> brNode; 1.3595 + NS_ENSURE_STATE(mHTMLEditor); 1.3596 + res = mHTMLEditor->GetNextHTMLNode(parent, offset, address_of(brNode), true); 1.3597 + NS_ENSURE_SUCCESS(res, res); 1.3598 + if (brNode && nsTextEditUtils::IsBreak(brNode)) 1.3599 + { 1.3600 + NS_ENSURE_STATE(mHTMLEditor); 1.3601 + res = mHTMLEditor->DeleteNode(brNode); 1.3602 + NS_ENSURE_SUCCESS(res, res); 1.3603 + // we don't need to act on this node any more 1.3604 + arrayOfNodes.RemoveObject(brNode); 1.3605 + } 1.3606 + // make sure we can put a block here 1.3607 + res = SplitAsNeeded(aBlockType, address_of(parent), &offset); 1.3608 + NS_ENSURE_SUCCESS(res, res); 1.3609 + NS_ENSURE_STATE(mHTMLEditor); 1.3610 + res = mHTMLEditor->CreateNode(*aBlockType, parent, offset, getter_AddRefs(theBlock)); 1.3611 + NS_ENSURE_SUCCESS(res, res); 1.3612 + // remember our new block for postprocessing 1.3613 + mNewBlock = theBlock; 1.3614 + // delete anything that was in the list of nodes 1.3615 + for (int32_t j = arrayOfNodes.Count() - 1; j >= 0; --j) 1.3616 + { 1.3617 + nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[0]; 1.3618 + NS_ENSURE_STATE(mHTMLEditor); 1.3619 + res = mHTMLEditor->DeleteNode(curNode); 1.3620 + NS_ENSURE_SUCCESS(res, res); 1.3621 + arrayOfNodes.RemoveObjectAt(0); 1.3622 + } 1.3623 + // put selection in new block 1.3624 + res = aSelection->Collapse(theBlock,0); 1.3625 + selectionResetter.Abort(); // to prevent selection reseter from overriding us. 1.3626 + *aHandled = true; 1.3627 + } 1.3628 + return res; 1.3629 + } 1.3630 + else 1.3631 + { 1.3632 + // Ok, now go through all the nodes and make the right kind of blocks, 1.3633 + // or whatever is approriate. Wohoo! 1.3634 + // Note: blockquote is handled a little differently 1.3635 + if (tString.EqualsLiteral("blockquote")) 1.3636 + res = MakeBlockquote(arrayOfNodes); 1.3637 + else if (tString.EqualsLiteral("normal") || 1.3638 + tString.IsEmpty() ) 1.3639 + res = RemoveBlockStyle(arrayOfNodes); 1.3640 + else 1.3641 + res = ApplyBlockStyle(arrayOfNodes, aBlockType); 1.3642 + return res; 1.3643 + } 1.3644 + return res; 1.3645 +} 1.3646 + 1.3647 +nsresult 1.3648 +nsHTMLEditRules::DidMakeBasicBlock(nsISelection *aSelection, 1.3649 + nsRulesInfo *aInfo, nsresult aResult) 1.3650 +{ 1.3651 + NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER); 1.3652 + // check for empty block. if so, put a moz br in it. 1.3653 + if (!aSelection->Collapsed()) { 1.3654 + return NS_OK; 1.3655 + } 1.3656 + 1.3657 + nsCOMPtr<nsIDOMNode> parent; 1.3658 + int32_t offset; 1.3659 + nsresult res = nsEditor::GetStartNodeAndOffset(aSelection, getter_AddRefs(parent), &offset); 1.3660 + NS_ENSURE_SUCCESS(res, res); 1.3661 + res = InsertMozBRIfNeeded(parent); 1.3662 + return res; 1.3663 +} 1.3664 + 1.3665 +nsresult 1.3666 +nsHTMLEditRules::WillIndent(Selection* aSelection, 1.3667 + bool* aCancel, bool* aHandled) 1.3668 +{ 1.3669 + nsresult res; 1.3670 + NS_ENSURE_STATE(mHTMLEditor); 1.3671 + if (mHTMLEditor->IsCSSEnabled()) { 1.3672 + res = WillCSSIndent(aSelection, aCancel, aHandled); 1.3673 + } 1.3674 + else { 1.3675 + res = WillHTMLIndent(aSelection, aCancel, aHandled); 1.3676 + } 1.3677 + return res; 1.3678 +} 1.3679 + 1.3680 +nsresult 1.3681 +nsHTMLEditRules::WillCSSIndent(Selection* aSelection, 1.3682 + bool* aCancel, bool* aHandled) 1.3683 +{ 1.3684 + if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; } 1.3685 + 1.3686 + nsresult res = WillInsert(aSelection, aCancel); 1.3687 + NS_ENSURE_SUCCESS(res, res); 1.3688 + 1.3689 + // initialize out param 1.3690 + // we want to ignore result of WillInsert() 1.3691 + *aCancel = false; 1.3692 + *aHandled = true; 1.3693 + 1.3694 + res = NormalizeSelection(aSelection); 1.3695 + NS_ENSURE_SUCCESS(res, res); 1.3696 + NS_ENSURE_STATE(mHTMLEditor); 1.3697 + nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor); 1.3698 + nsCOMArray<nsIDOMRange> arrayOfRanges; 1.3699 + nsCOMArray<nsIDOMNode> arrayOfNodes; 1.3700 + 1.3701 + // short circuit: detect case of collapsed selection inside an <li>. 1.3702 + // just sublist that <li>. This prevents bug 97797. 1.3703 + 1.3704 + nsCOMPtr<nsIDOMNode> liNode; 1.3705 + if (aSelection->Collapsed()) { 1.3706 + nsCOMPtr<nsIDOMNode> node, block; 1.3707 + int32_t offset; 1.3708 + NS_ENSURE_STATE(mHTMLEditor); 1.3709 + nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(node), &offset); 1.3710 + NS_ENSURE_SUCCESS(res, res); 1.3711 + if (IsBlockNode(node)) { 1.3712 + block = node; 1.3713 + } else { 1.3714 + NS_ENSURE_STATE(mHTMLEditor); 1.3715 + block = mHTMLEditor->GetBlockNodeParent(node); 1.3716 + } 1.3717 + if (block && nsHTMLEditUtils::IsListItem(block)) 1.3718 + liNode = block; 1.3719 + } 1.3720 + 1.3721 + if (liNode) 1.3722 + { 1.3723 + arrayOfNodes.AppendObject(liNode); 1.3724 + } 1.3725 + else 1.3726 + { 1.3727 + // convert the selection ranges into "promoted" selection ranges: 1.3728 + // this basically just expands the range to include the immediate 1.3729 + // block parent, and then further expands to include any ancestors 1.3730 + // whose children are all in the range 1.3731 + res = GetNodesFromSelection(aSelection, EditAction::indent, arrayOfNodes); 1.3732 + NS_ENSURE_SUCCESS(res, res); 1.3733 + } 1.3734 + 1.3735 + NS_NAMED_LITERAL_STRING(quoteType, "blockquote"); 1.3736 + // if nothing visible in list, make an empty block 1.3737 + if (ListIsEmptyLine(arrayOfNodes)) 1.3738 + { 1.3739 + nsCOMPtr<nsIDOMNode> parent, theBlock; 1.3740 + int32_t offset; 1.3741 + nsAutoString quoteType(NS_LITERAL_STRING("div")); 1.3742 + // get selection location 1.3743 + NS_ENSURE_STATE(mHTMLEditor); 1.3744 + res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(parent), &offset); 1.3745 + NS_ENSURE_SUCCESS(res, res); 1.3746 + // make sure we can put a block here 1.3747 + res = SplitAsNeeded("eType, address_of(parent), &offset); 1.3748 + NS_ENSURE_SUCCESS(res, res); 1.3749 + NS_ENSURE_STATE(mHTMLEditor); 1.3750 + res = mHTMLEditor->CreateNode(quoteType, parent, offset, getter_AddRefs(theBlock)); 1.3751 + NS_ENSURE_SUCCESS(res, res); 1.3752 + // remember our new block for postprocessing 1.3753 + mNewBlock = theBlock; 1.3754 + RelativeChangeIndentationOfElementNode(theBlock, +1); 1.3755 + // delete anything that was in the list of nodes 1.3756 + for (int32_t j = arrayOfNodes.Count() - 1; j >= 0; --j) 1.3757 + { 1.3758 + nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[0]; 1.3759 + NS_ENSURE_STATE(mHTMLEditor); 1.3760 + res = mHTMLEditor->DeleteNode(curNode); 1.3761 + NS_ENSURE_SUCCESS(res, res); 1.3762 + arrayOfNodes.RemoveObjectAt(0); 1.3763 + } 1.3764 + // put selection in new block 1.3765 + res = aSelection->Collapse(theBlock,0); 1.3766 + selectionResetter.Abort(); // to prevent selection reseter from overriding us. 1.3767 + *aHandled = true; 1.3768 + return res; 1.3769 + } 1.3770 + 1.3771 + // Ok, now go through all the nodes and put them in a blockquote, 1.3772 + // or whatever is appropriate. Wohoo! 1.3773 + int32_t i; 1.3774 + nsCOMPtr<nsIDOMNode> curParent; 1.3775 + nsCOMPtr<nsIDOMNode> curQuote; 1.3776 + nsCOMPtr<nsIDOMNode> curList; 1.3777 + nsCOMPtr<nsIDOMNode> sibling; 1.3778 + int32_t listCount = arrayOfNodes.Count(); 1.3779 + for (i=0; i<listCount; i++) 1.3780 + { 1.3781 + // here's where we actually figure out what to do 1.3782 + nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i]; 1.3783 + 1.3784 + // Ignore all non-editable nodes. Leave them be. 1.3785 + NS_ENSURE_STATE(mHTMLEditor); 1.3786 + if (!mHTMLEditor->IsEditable(curNode)) continue; 1.3787 + 1.3788 + int32_t offset; 1.3789 + curParent = nsEditor::GetNodeLocation(curNode, &offset); 1.3790 + 1.3791 + // some logic for putting list items into nested lists... 1.3792 + if (nsHTMLEditUtils::IsList(curParent)) 1.3793 + { 1.3794 + sibling = nullptr; 1.3795 + 1.3796 + // Check for whether we should join a list that follows curNode. 1.3797 + // We do this if the next element is a list, and the list is of the 1.3798 + // same type (li/ol) as curNode was a part it. 1.3799 + NS_ENSURE_STATE(mHTMLEditor); 1.3800 + mHTMLEditor->GetNextHTMLSibling(curNode, address_of(sibling)); 1.3801 + if (sibling && nsHTMLEditUtils::IsList(sibling)) 1.3802 + { 1.3803 + nsAutoString curListTag, siblingListTag; 1.3804 + nsEditor::GetTagString(curParent, curListTag); 1.3805 + nsEditor::GetTagString(sibling, siblingListTag); 1.3806 + if (curListTag == siblingListTag) 1.3807 + { 1.3808 + NS_ENSURE_STATE(mHTMLEditor); 1.3809 + res = mHTMLEditor->MoveNode(curNode, sibling, 0); 1.3810 + NS_ENSURE_SUCCESS(res, res); 1.3811 + continue; 1.3812 + } 1.3813 + } 1.3814 + // Check for whether we should join a list that preceeds curNode. 1.3815 + // We do this if the previous element is a list, and the list is of 1.3816 + // the same type (li/ol) as curNode was a part of. 1.3817 + NS_ENSURE_STATE(mHTMLEditor); 1.3818 + mHTMLEditor->GetPriorHTMLSibling(curNode, address_of(sibling)); 1.3819 + if (sibling && nsHTMLEditUtils::IsList(sibling)) 1.3820 + { 1.3821 + nsAutoString curListTag, siblingListTag; 1.3822 + nsEditor::GetTagString(curParent, curListTag); 1.3823 + nsEditor::GetTagString(sibling, siblingListTag); 1.3824 + if (curListTag == siblingListTag) 1.3825 + { 1.3826 + NS_ENSURE_STATE(mHTMLEditor); 1.3827 + res = mHTMLEditor->MoveNode(curNode, sibling, -1); 1.3828 + NS_ENSURE_SUCCESS(res, res); 1.3829 + continue; 1.3830 + } 1.3831 + } 1.3832 + sibling = nullptr; 1.3833 + 1.3834 + // check to see if curList is still appropriate. Which it is if 1.3835 + // curNode is still right after it in the same list. 1.3836 + if (curList) { 1.3837 + NS_ENSURE_STATE(mHTMLEditor); 1.3838 + mHTMLEditor->GetPriorHTMLSibling(curNode, address_of(sibling)); 1.3839 + } 1.3840 + 1.3841 + if (!curList || (sibling && sibling != curList)) 1.3842 + { 1.3843 + nsAutoString listTag; 1.3844 + nsEditor::GetTagString(curParent,listTag); 1.3845 + ToLowerCase(listTag); 1.3846 + // create a new nested list of correct type 1.3847 + res = SplitAsNeeded(&listTag, address_of(curParent), &offset); 1.3848 + NS_ENSURE_SUCCESS(res, res); 1.3849 + NS_ENSURE_STATE(mHTMLEditor); 1.3850 + res = mHTMLEditor->CreateNode(listTag, curParent, offset, getter_AddRefs(curList)); 1.3851 + NS_ENSURE_SUCCESS(res, res); 1.3852 + // curList is now the correct thing to put curNode in 1.3853 + // remember our new block for postprocessing 1.3854 + mNewBlock = curList; 1.3855 + } 1.3856 + // tuck the node into the end of the active list 1.3857 + uint32_t listLen; 1.3858 + NS_ENSURE_STATE(mHTMLEditor); 1.3859 + res = mHTMLEditor->GetLengthOfDOMNode(curList, listLen); 1.3860 + NS_ENSURE_SUCCESS(res, res); 1.3861 + NS_ENSURE_STATE(mHTMLEditor); 1.3862 + res = mHTMLEditor->MoveNode(curNode, curList, listLen); 1.3863 + NS_ENSURE_SUCCESS(res, res); 1.3864 + } 1.3865 + 1.3866 + else // not a list item 1.3867 + { 1.3868 + if (IsBlockNode(curNode)) { 1.3869 + RelativeChangeIndentationOfElementNode(curNode, +1); 1.3870 + curQuote = nullptr; 1.3871 + } 1.3872 + else { 1.3873 + if (!curQuote) 1.3874 + { 1.3875 + // First, check that our element can contain a div. 1.3876 + if (!mEditor->CanContainTag(curParent, nsGkAtoms::div)) { 1.3877 + return NS_OK; // cancelled 1.3878 + } 1.3879 + 1.3880 + NS_NAMED_LITERAL_STRING(divquoteType, "div"); 1.3881 + res = SplitAsNeeded(&divquoteType, address_of(curParent), &offset); 1.3882 + NS_ENSURE_SUCCESS(res, res); 1.3883 + NS_ENSURE_STATE(mHTMLEditor); 1.3884 + res = mHTMLEditor->CreateNode(divquoteType, curParent, offset, getter_AddRefs(curQuote)); 1.3885 + NS_ENSURE_SUCCESS(res, res); 1.3886 + RelativeChangeIndentationOfElementNode(curQuote, +1); 1.3887 + // remember our new block for postprocessing 1.3888 + mNewBlock = curQuote; 1.3889 + // curQuote is now the correct thing to put curNode in 1.3890 + } 1.3891 + 1.3892 + // tuck the node into the end of the active blockquote 1.3893 + uint32_t quoteLen; 1.3894 + NS_ENSURE_STATE(mHTMLEditor); 1.3895 + res = mHTMLEditor->GetLengthOfDOMNode(curQuote, quoteLen); 1.3896 + NS_ENSURE_SUCCESS(res, res); 1.3897 + NS_ENSURE_STATE(mHTMLEditor); 1.3898 + res = mHTMLEditor->MoveNode(curNode, curQuote, quoteLen); 1.3899 + NS_ENSURE_SUCCESS(res, res); 1.3900 + } 1.3901 + } 1.3902 + } 1.3903 + return res; 1.3904 +} 1.3905 + 1.3906 +nsresult 1.3907 +nsHTMLEditRules::WillHTMLIndent(Selection* aSelection, 1.3908 + bool* aCancel, bool* aHandled) 1.3909 +{ 1.3910 + if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; } 1.3911 + nsresult res = WillInsert(aSelection, aCancel); 1.3912 + NS_ENSURE_SUCCESS(res, res); 1.3913 + 1.3914 + // initialize out param 1.3915 + // we want to ignore result of WillInsert() 1.3916 + *aCancel = false; 1.3917 + *aHandled = true; 1.3918 + 1.3919 + res = NormalizeSelection(aSelection); 1.3920 + NS_ENSURE_SUCCESS(res, res); 1.3921 + NS_ENSURE_STATE(mHTMLEditor); 1.3922 + nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor); 1.3923 + 1.3924 + // convert the selection ranges into "promoted" selection ranges: 1.3925 + // this basically just expands the range to include the immediate 1.3926 + // block parent, and then further expands to include any ancestors 1.3927 + // whose children are all in the range 1.3928 + 1.3929 + nsCOMArray<nsIDOMRange> arrayOfRanges; 1.3930 + res = GetPromotedRanges(aSelection, arrayOfRanges, EditAction::indent); 1.3931 + NS_ENSURE_SUCCESS(res, res); 1.3932 + 1.3933 + // use these ranges to contruct a list of nodes to act on. 1.3934 + nsCOMArray<nsIDOMNode> arrayOfNodes; 1.3935 + res = GetNodesForOperation(arrayOfRanges, arrayOfNodes, EditAction::indent); 1.3936 + NS_ENSURE_SUCCESS(res, res); 1.3937 + 1.3938 + NS_NAMED_LITERAL_STRING(quoteType, "blockquote"); 1.3939 + 1.3940 + // if nothing visible in list, make an empty block 1.3941 + if (ListIsEmptyLine(arrayOfNodes)) 1.3942 + { 1.3943 + nsCOMPtr<nsIDOMNode> parent, theBlock; 1.3944 + int32_t offset; 1.3945 + 1.3946 + // get selection location 1.3947 + NS_ENSURE_STATE(mHTMLEditor); 1.3948 + res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(parent), &offset); 1.3949 + NS_ENSURE_SUCCESS(res, res); 1.3950 + // make sure we can put a block here 1.3951 + res = SplitAsNeeded("eType, address_of(parent), &offset); 1.3952 + NS_ENSURE_SUCCESS(res, res); 1.3953 + NS_ENSURE_STATE(mHTMLEditor); 1.3954 + res = mHTMLEditor->CreateNode(quoteType, parent, offset, getter_AddRefs(theBlock)); 1.3955 + NS_ENSURE_SUCCESS(res, res); 1.3956 + // remember our new block for postprocessing 1.3957 + mNewBlock = theBlock; 1.3958 + // delete anything that was in the list of nodes 1.3959 + for (int32_t j = arrayOfNodes.Count() - 1; j >= 0; --j) 1.3960 + { 1.3961 + nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[0]; 1.3962 + NS_ENSURE_STATE(mHTMLEditor); 1.3963 + res = mHTMLEditor->DeleteNode(curNode); 1.3964 + NS_ENSURE_SUCCESS(res, res); 1.3965 + arrayOfNodes.RemoveObjectAt(0); 1.3966 + } 1.3967 + // put selection in new block 1.3968 + res = aSelection->Collapse(theBlock,0); 1.3969 + selectionResetter.Abort(); // to prevent selection reseter from overriding us. 1.3970 + *aHandled = true; 1.3971 + return res; 1.3972 + } 1.3973 + 1.3974 + // Ok, now go through all the nodes and put them in a blockquote, 1.3975 + // or whatever is appropriate. Wohoo! 1.3976 + int32_t i; 1.3977 + nsCOMPtr<nsIDOMNode> curParent, curQuote, curList, indentedLI, sibling; 1.3978 + int32_t listCount = arrayOfNodes.Count(); 1.3979 + for (i=0; i<listCount; i++) 1.3980 + { 1.3981 + // here's where we actually figure out what to do 1.3982 + nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i]; 1.3983 + 1.3984 + // Ignore all non-editable nodes. Leave them be. 1.3985 + NS_ENSURE_STATE(mHTMLEditor); 1.3986 + if (!mHTMLEditor->IsEditable(curNode)) continue; 1.3987 + 1.3988 + int32_t offset; 1.3989 + curParent = nsEditor::GetNodeLocation(curNode, &offset); 1.3990 + 1.3991 + // some logic for putting list items into nested lists... 1.3992 + if (nsHTMLEditUtils::IsList(curParent)) 1.3993 + { 1.3994 + sibling = nullptr; 1.3995 + 1.3996 + // Check for whether we should join a list that follows curNode. 1.3997 + // We do this if the next element is a list, and the list is of the 1.3998 + // same type (li/ol) as curNode was a part it. 1.3999 + NS_ENSURE_STATE(mHTMLEditor); 1.4000 + mHTMLEditor->GetNextHTMLSibling(curNode, address_of(sibling)); 1.4001 + if (sibling && nsHTMLEditUtils::IsList(sibling)) 1.4002 + { 1.4003 + nsAutoString curListTag, siblingListTag; 1.4004 + nsEditor::GetTagString(curParent, curListTag); 1.4005 + nsEditor::GetTagString(sibling, siblingListTag); 1.4006 + if (curListTag == siblingListTag) 1.4007 + { 1.4008 + NS_ENSURE_STATE(mHTMLEditor); 1.4009 + res = mHTMLEditor->MoveNode(curNode, sibling, 0); 1.4010 + NS_ENSURE_SUCCESS(res, res); 1.4011 + continue; 1.4012 + } 1.4013 + } 1.4014 + 1.4015 + // Check for whether we should join a list that preceeds curNode. 1.4016 + // We do this if the previous element is a list, and the list is of 1.4017 + // the same type (li/ol) as curNode was a part of. 1.4018 + NS_ENSURE_STATE(mHTMLEditor); 1.4019 + mHTMLEditor->GetPriorHTMLSibling(curNode, address_of(sibling)); 1.4020 + if (sibling && nsHTMLEditUtils::IsList(sibling)) 1.4021 + { 1.4022 + nsAutoString curListTag, siblingListTag; 1.4023 + nsEditor::GetTagString(curParent, curListTag); 1.4024 + nsEditor::GetTagString(sibling, siblingListTag); 1.4025 + if (curListTag == siblingListTag) 1.4026 + { 1.4027 + NS_ENSURE_STATE(mHTMLEditor); 1.4028 + res = mHTMLEditor->MoveNode(curNode, sibling, -1); 1.4029 + NS_ENSURE_SUCCESS(res, res); 1.4030 + continue; 1.4031 + } 1.4032 + } 1.4033 + 1.4034 + sibling = nullptr; 1.4035 + 1.4036 + // check to see if curList is still appropriate. Which it is if 1.4037 + // curNode is still right after it in the same list. 1.4038 + if (curList) { 1.4039 + NS_ENSURE_STATE(mHTMLEditor); 1.4040 + mHTMLEditor->GetPriorHTMLSibling(curNode, address_of(sibling)); 1.4041 + } 1.4042 + 1.4043 + if (!curList || (sibling && sibling != curList) ) 1.4044 + { 1.4045 + nsAutoString listTag; 1.4046 + nsEditor::GetTagString(curParent,listTag); 1.4047 + ToLowerCase(listTag); 1.4048 + // create a new nested list of correct type 1.4049 + res = SplitAsNeeded(&listTag, address_of(curParent), &offset); 1.4050 + NS_ENSURE_SUCCESS(res, res); 1.4051 + NS_ENSURE_STATE(mHTMLEditor); 1.4052 + res = mHTMLEditor->CreateNode(listTag, curParent, offset, getter_AddRefs(curList)); 1.4053 + NS_ENSURE_SUCCESS(res, res); 1.4054 + // curList is now the correct thing to put curNode in 1.4055 + // remember our new block for postprocessing 1.4056 + mNewBlock = curList; 1.4057 + } 1.4058 + // tuck the node into the end of the active list 1.4059 + NS_ENSURE_STATE(mHTMLEditor); 1.4060 + res = mHTMLEditor->MoveNode(curNode, curList, -1); 1.4061 + NS_ENSURE_SUCCESS(res, res); 1.4062 + // forget curQuote, if any 1.4063 + curQuote = nullptr; 1.4064 + } 1.4065 + 1.4066 + else // not a list item, use blockquote? 1.4067 + { 1.4068 + // if we are inside a list item, we don't want to blockquote, we want 1.4069 + // to sublist the list item. We may have several nodes listed in the 1.4070 + // array of nodes to act on, that are in the same list item. Since 1.4071 + // we only want to indent that li once, we must keep track of the most 1.4072 + // recent indented list item, and not indent it if we find another node 1.4073 + // to act on that is still inside the same li. 1.4074 + nsCOMPtr<nsIDOMNode> listitem=IsInListItem(curNode); 1.4075 + if (listitem) 1.4076 + { 1.4077 + if (indentedLI == listitem) continue; // already indented this list item 1.4078 + curParent = nsEditor::GetNodeLocation(listitem, &offset); 1.4079 + // check to see if curList is still appropriate. Which it is if 1.4080 + // curNode is still right after it in the same list. 1.4081 + if (curList) 1.4082 + { 1.4083 + sibling = nullptr; 1.4084 + NS_ENSURE_STATE(mHTMLEditor); 1.4085 + mHTMLEditor->GetPriorHTMLSibling(curNode, address_of(sibling)); 1.4086 + } 1.4087 + 1.4088 + if (!curList || (sibling && sibling != curList) ) 1.4089 + { 1.4090 + nsAutoString listTag; 1.4091 + nsEditor::GetTagString(curParent,listTag); 1.4092 + ToLowerCase(listTag); 1.4093 + // create a new nested list of correct type 1.4094 + res = SplitAsNeeded(&listTag, address_of(curParent), &offset); 1.4095 + NS_ENSURE_SUCCESS(res, res); 1.4096 + NS_ENSURE_STATE(mHTMLEditor); 1.4097 + res = mHTMLEditor->CreateNode(listTag, curParent, offset, getter_AddRefs(curList)); 1.4098 + NS_ENSURE_SUCCESS(res, res); 1.4099 + } 1.4100 + NS_ENSURE_STATE(mHTMLEditor); 1.4101 + res = mHTMLEditor->MoveNode(listitem, curList, -1); 1.4102 + NS_ENSURE_SUCCESS(res, res); 1.4103 + // remember we indented this li 1.4104 + indentedLI = listitem; 1.4105 + } 1.4106 + 1.4107 + else 1.4108 + { 1.4109 + // need to make a blockquote to put things in if we haven't already, 1.4110 + // or if this node doesn't go in blockquote we used earlier. 1.4111 + // One reason it might not go in prio blockquote is if we are now 1.4112 + // in a different table cell. 1.4113 + if (curQuote && InDifferentTableElements(curQuote, curNode)) { 1.4114 + curQuote = nullptr; 1.4115 + } 1.4116 + 1.4117 + if (!curQuote) 1.4118 + { 1.4119 + // First, check that our element can contain a blockquote. 1.4120 + if (!mEditor->CanContainTag(curParent, nsGkAtoms::blockquote)) { 1.4121 + return NS_OK; // cancelled 1.4122 + } 1.4123 + 1.4124 + res = SplitAsNeeded("eType, address_of(curParent), &offset); 1.4125 + NS_ENSURE_SUCCESS(res, res); 1.4126 + NS_ENSURE_STATE(mHTMLEditor); 1.4127 + res = mHTMLEditor->CreateNode(quoteType, curParent, offset, getter_AddRefs(curQuote)); 1.4128 + NS_ENSURE_SUCCESS(res, res); 1.4129 + // remember our new block for postprocessing 1.4130 + mNewBlock = curQuote; 1.4131 + // curQuote is now the correct thing to put curNode in 1.4132 + } 1.4133 + 1.4134 + // tuck the node into the end of the active blockquote 1.4135 + NS_ENSURE_STATE(mHTMLEditor); 1.4136 + res = mHTMLEditor->MoveNode(curNode, curQuote, -1); 1.4137 + NS_ENSURE_SUCCESS(res, res); 1.4138 + // forget curList, if any 1.4139 + curList = nullptr; 1.4140 + } 1.4141 + } 1.4142 + } 1.4143 + return res; 1.4144 +} 1.4145 + 1.4146 + 1.4147 +nsresult 1.4148 +nsHTMLEditRules::WillOutdent(Selection* aSelection, 1.4149 + bool* aCancel, bool* aHandled) 1.4150 +{ 1.4151 + if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; } 1.4152 + // initialize out param 1.4153 + *aCancel = false; 1.4154 + *aHandled = true; 1.4155 + nsresult res = NS_OK; 1.4156 + nsCOMPtr<nsIDOMNode> rememberedLeftBQ, rememberedRightBQ; 1.4157 + NS_ENSURE_STATE(mHTMLEditor); 1.4158 + bool useCSS = mHTMLEditor->IsCSSEnabled(); 1.4159 + 1.4160 + res = NormalizeSelection(aSelection); 1.4161 + NS_ENSURE_SUCCESS(res, res); 1.4162 + // some scoping for selection resetting - we may need to tweak it 1.4163 + { 1.4164 + NS_ENSURE_STATE(mHTMLEditor); 1.4165 + nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor); 1.4166 + 1.4167 + // convert the selection ranges into "promoted" selection ranges: 1.4168 + // this basically just expands the range to include the immediate 1.4169 + // block parent, and then further expands to include any ancestors 1.4170 + // whose children are all in the range 1.4171 + nsCOMArray<nsIDOMNode> arrayOfNodes; 1.4172 + res = GetNodesFromSelection(aSelection, EditAction::outdent, 1.4173 + arrayOfNodes); 1.4174 + NS_ENSURE_SUCCESS(res, res); 1.4175 + 1.4176 + // Ok, now go through all the nodes and remove a level of blockquoting, 1.4177 + // or whatever is appropriate. Wohoo! 1.4178 + 1.4179 + nsCOMPtr<nsIDOMNode> curBlockQuote, firstBQChild, lastBQChild; 1.4180 + bool curBlockQuoteIsIndentedWithCSS = false; 1.4181 + int32_t listCount = arrayOfNodes.Count(); 1.4182 + int32_t i; 1.4183 + nsCOMPtr<nsIDOMNode> curParent; 1.4184 + for (i=0; i<listCount; i++) 1.4185 + { 1.4186 + // here's where we actually figure out what to do 1.4187 + nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i]; 1.4188 + int32_t offset; 1.4189 + curParent = nsEditor::GetNodeLocation(curNode, &offset); 1.4190 + 1.4191 + // is it a blockquote? 1.4192 + if (nsHTMLEditUtils::IsBlockquote(curNode)) 1.4193 + { 1.4194 + // if it is a blockquote, remove it. 1.4195 + // So we need to finish up dealng with any curBlockQuote first. 1.4196 + if (curBlockQuote) 1.4197 + { 1.4198 + res = OutdentPartOfBlock(curBlockQuote, firstBQChild, lastBQChild, 1.4199 + curBlockQuoteIsIndentedWithCSS, 1.4200 + address_of(rememberedLeftBQ), 1.4201 + address_of(rememberedRightBQ)); 1.4202 + NS_ENSURE_SUCCESS(res, res); 1.4203 + curBlockQuote = 0; firstBQChild = 0; lastBQChild = 0; 1.4204 + curBlockQuoteIsIndentedWithCSS = false; 1.4205 + } 1.4206 + NS_ENSURE_STATE(mHTMLEditor); 1.4207 + res = mHTMLEditor->RemoveBlockContainer(curNode); 1.4208 + NS_ENSURE_SUCCESS(res, res); 1.4209 + continue; 1.4210 + } 1.4211 + // is it a block with a 'margin' property? 1.4212 + if (useCSS && IsBlockNode(curNode)) 1.4213 + { 1.4214 + NS_ENSURE_STATE(mHTMLEditor); 1.4215 + nsIAtom* marginProperty = MarginPropertyAtomForIndent(mHTMLEditor->mHTMLCSSUtils, curNode); 1.4216 + nsAutoString value; 1.4217 + NS_ENSURE_STATE(mHTMLEditor); 1.4218 + mHTMLEditor->mHTMLCSSUtils->GetSpecifiedProperty(curNode, marginProperty, value); 1.4219 + float f; 1.4220 + nsCOMPtr<nsIAtom> unit; 1.4221 + NS_ENSURE_STATE(mHTMLEditor); 1.4222 + mHTMLEditor->mHTMLCSSUtils->ParseLength(value, &f, getter_AddRefs(unit)); 1.4223 + if (f > 0) 1.4224 + { 1.4225 + RelativeChangeIndentationOfElementNode(curNode, -1); 1.4226 + continue; 1.4227 + } 1.4228 + } 1.4229 + // is it a list item? 1.4230 + if (nsHTMLEditUtils::IsListItem(curNode)) 1.4231 + { 1.4232 + // if it is a list item, that means we are not outdenting whole list. 1.4233 + // So we need to finish up dealing with any curBlockQuote, and then 1.4234 + // pop this list item. 1.4235 + if (curBlockQuote) 1.4236 + { 1.4237 + res = OutdentPartOfBlock(curBlockQuote, firstBQChild, lastBQChild, 1.4238 + curBlockQuoteIsIndentedWithCSS, 1.4239 + address_of(rememberedLeftBQ), 1.4240 + address_of(rememberedRightBQ)); 1.4241 + NS_ENSURE_SUCCESS(res, res); 1.4242 + curBlockQuote = 0; firstBQChild = 0; lastBQChild = 0; 1.4243 + curBlockQuoteIsIndentedWithCSS = false; 1.4244 + } 1.4245 + bool bOutOfList; 1.4246 + res = PopListItem(curNode, &bOutOfList); 1.4247 + NS_ENSURE_SUCCESS(res, res); 1.4248 + continue; 1.4249 + } 1.4250 + // do we have a blockquote that we are already committed to removing? 1.4251 + if (curBlockQuote) 1.4252 + { 1.4253 + // if so, is this node a descendant? 1.4254 + if (nsEditorUtils::IsDescendantOf(curNode, curBlockQuote)) 1.4255 + { 1.4256 + lastBQChild = curNode; 1.4257 + continue; // then we don't need to do anything different for this node 1.4258 + } 1.4259 + else 1.4260 + { 1.4261 + // otherwise, we have progressed beyond end of curBlockQuote, 1.4262 + // so lets handle it now. We need to remove the portion of 1.4263 + // curBlockQuote that contains [firstBQChild - lastBQChild]. 1.4264 + res = OutdentPartOfBlock(curBlockQuote, firstBQChild, lastBQChild, 1.4265 + curBlockQuoteIsIndentedWithCSS, 1.4266 + address_of(rememberedLeftBQ), 1.4267 + address_of(rememberedRightBQ)); 1.4268 + NS_ENSURE_SUCCESS(res, res); 1.4269 + curBlockQuote = 0; firstBQChild = 0; lastBQChild = 0; 1.4270 + curBlockQuoteIsIndentedWithCSS = false; 1.4271 + // fall out and handle curNode 1.4272 + } 1.4273 + } 1.4274 + 1.4275 + // are we inside a blockquote? 1.4276 + nsCOMPtr<nsIDOMNode> n = curNode; 1.4277 + nsCOMPtr<nsIDOMNode> tmp; 1.4278 + curBlockQuoteIsIndentedWithCSS = false; 1.4279 + // keep looking up the hierarchy as long as we don't hit the body or the 1.4280 + // active editing host or a table element (other than an entire table) 1.4281 + while (!nsTextEditUtils::IsBody(n) && mHTMLEditor && 1.4282 + mHTMLEditor->IsDescendantOfEditorRoot(n) 1.4283 + && (nsHTMLEditUtils::IsTable(n) || !nsHTMLEditUtils::IsTableElement(n))) 1.4284 + { 1.4285 + n->GetParentNode(getter_AddRefs(tmp)); 1.4286 + if (!tmp) { 1.4287 + break; 1.4288 + } 1.4289 + n = tmp; 1.4290 + if (nsHTMLEditUtils::IsBlockquote(n)) 1.4291 + { 1.4292 + // if so, remember it, and remember first node we are taking out of it. 1.4293 + curBlockQuote = n; 1.4294 + firstBQChild = curNode; 1.4295 + lastBQChild = curNode; 1.4296 + break; 1.4297 + } 1.4298 + else if (useCSS) 1.4299 + { 1.4300 + NS_ENSURE_STATE(mHTMLEditor); 1.4301 + nsIAtom* marginProperty = MarginPropertyAtomForIndent(mHTMLEditor->mHTMLCSSUtils, curNode); 1.4302 + nsAutoString value; 1.4303 + NS_ENSURE_STATE(mHTMLEditor); 1.4304 + mHTMLEditor->mHTMLCSSUtils->GetSpecifiedProperty(n, marginProperty, value); 1.4305 + float f; 1.4306 + nsCOMPtr<nsIAtom> unit; 1.4307 + NS_ENSURE_STATE(mHTMLEditor); 1.4308 + mHTMLEditor->mHTMLCSSUtils->ParseLength(value, &f, getter_AddRefs(unit)); 1.4309 + if (f > 0 && !(nsHTMLEditUtils::IsList(curParent) && nsHTMLEditUtils::IsList(curNode))) 1.4310 + { 1.4311 + curBlockQuote = n; 1.4312 + firstBQChild = curNode; 1.4313 + lastBQChild = curNode; 1.4314 + curBlockQuoteIsIndentedWithCSS = true; 1.4315 + break; 1.4316 + } 1.4317 + } 1.4318 + } 1.4319 + 1.4320 + if (!curBlockQuote) 1.4321 + { 1.4322 + // could not find an enclosing blockquote for this node. handle list cases. 1.4323 + if (nsHTMLEditUtils::IsList(curParent)) // move node out of list 1.4324 + { 1.4325 + if (nsHTMLEditUtils::IsList(curNode)) // just unwrap this sublist 1.4326 + { 1.4327 + NS_ENSURE_STATE(mHTMLEditor); 1.4328 + res = mHTMLEditor->RemoveBlockContainer(curNode); 1.4329 + NS_ENSURE_SUCCESS(res, res); 1.4330 + } 1.4331 + // handled list item case above 1.4332 + } 1.4333 + else if (nsHTMLEditUtils::IsList(curNode)) // node is a list, but parent is non-list: move list items out 1.4334 + { 1.4335 + nsCOMPtr<nsIDOMNode> child; 1.4336 + curNode->GetLastChild(getter_AddRefs(child)); 1.4337 + while (child) 1.4338 + { 1.4339 + if (nsHTMLEditUtils::IsListItem(child)) 1.4340 + { 1.4341 + bool bOutOfList; 1.4342 + res = PopListItem(child, &bOutOfList); 1.4343 + NS_ENSURE_SUCCESS(res, res); 1.4344 + } 1.4345 + else if (nsHTMLEditUtils::IsList(child)) 1.4346 + { 1.4347 + // We have an embedded list, so move it out from under the 1.4348 + // parent list. Be sure to put it after the parent list 1.4349 + // because this loop iterates backwards through the parent's 1.4350 + // list of children. 1.4351 + 1.4352 + NS_ENSURE_STATE(mHTMLEditor); 1.4353 + res = mHTMLEditor->MoveNode(child, curParent, offset + 1); 1.4354 + NS_ENSURE_SUCCESS(res, res); 1.4355 + } 1.4356 + else 1.4357 + { 1.4358 + // delete any non- list items for now 1.4359 + NS_ENSURE_STATE(mHTMLEditor); 1.4360 + res = mHTMLEditor->DeleteNode(child); 1.4361 + NS_ENSURE_SUCCESS(res, res); 1.4362 + } 1.4363 + curNode->GetLastChild(getter_AddRefs(child)); 1.4364 + } 1.4365 + // delete the now-empty list 1.4366 + NS_ENSURE_STATE(mHTMLEditor); 1.4367 + res = mHTMLEditor->RemoveBlockContainer(curNode); 1.4368 + NS_ENSURE_SUCCESS(res, res); 1.4369 + } 1.4370 + else if (useCSS) { 1.4371 + nsCOMPtr<nsIDOMElement> element; 1.4372 + nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(curNode); 1.4373 + if (textNode) { 1.4374 + // We want to outdent the parent of text nodes 1.4375 + nsCOMPtr<nsIDOMNode> parent; 1.4376 + textNode->GetParentNode(getter_AddRefs(parent)); 1.4377 + element = do_QueryInterface(parent); 1.4378 + } else { 1.4379 + element = do_QueryInterface(curNode); 1.4380 + } 1.4381 + if (element) { 1.4382 + RelativeChangeIndentationOfElementNode(element, -1); 1.4383 + } 1.4384 + } 1.4385 + } 1.4386 + } 1.4387 + if (curBlockQuote) 1.4388 + { 1.4389 + // we have a blockquote we haven't finished handling 1.4390 + res = OutdentPartOfBlock(curBlockQuote, firstBQChild, lastBQChild, 1.4391 + curBlockQuoteIsIndentedWithCSS, 1.4392 + address_of(rememberedLeftBQ), 1.4393 + address_of(rememberedRightBQ)); 1.4394 + NS_ENSURE_SUCCESS(res, res); 1.4395 + } 1.4396 + } 1.4397 + // make sure selection didn't stick to last piece of content in old bq 1.4398 + // (only a problem for collapsed selections) 1.4399 + if (rememberedLeftBQ || rememberedRightBQ) { 1.4400 + if (aSelection->Collapsed()) { 1.4401 + // push selection past end of rememberedLeftBQ 1.4402 + nsCOMPtr<nsIDOMNode> sNode; 1.4403 + int32_t sOffset; 1.4404 + NS_ENSURE_STATE(mHTMLEditor); 1.4405 + mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(sNode), &sOffset); 1.4406 + if (rememberedLeftBQ && 1.4407 + ((sNode == rememberedLeftBQ) || nsEditorUtils::IsDescendantOf(sNode, rememberedLeftBQ))) 1.4408 + { 1.4409 + // selection is inside rememberedLeftBQ - push it past it. 1.4410 + sNode = nsEditor::GetNodeLocation(rememberedLeftBQ, &sOffset); 1.4411 + sOffset++; 1.4412 + aSelection->Collapse(sNode, sOffset); 1.4413 + } 1.4414 + // and pull selection before beginning of rememberedRightBQ 1.4415 + NS_ENSURE_STATE(mHTMLEditor); 1.4416 + mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(sNode), &sOffset); 1.4417 + if (rememberedRightBQ && 1.4418 + ((sNode == rememberedRightBQ) || nsEditorUtils::IsDescendantOf(sNode, rememberedRightBQ))) 1.4419 + { 1.4420 + // selection is inside rememberedRightBQ - push it before it. 1.4421 + sNode = nsEditor::GetNodeLocation(rememberedRightBQ, &sOffset); 1.4422 + aSelection->Collapse(sNode, sOffset); 1.4423 + } 1.4424 + } 1.4425 + return NS_OK; 1.4426 + } 1.4427 + return res; 1.4428 +} 1.4429 + 1.4430 + 1.4431 +/////////////////////////////////////////////////////////////////////////// 1.4432 +// RemovePartOfBlock: split aBlock and move aStartChild to aEndChild out 1.4433 +// of aBlock. return left side of block (if any) in 1.4434 +// aLeftNode. return right side of block (if any) in 1.4435 +// aRightNode. 1.4436 +// 1.4437 +nsresult 1.4438 +nsHTMLEditRules::RemovePartOfBlock(nsIDOMNode *aBlock, 1.4439 + nsIDOMNode *aStartChild, 1.4440 + nsIDOMNode *aEndChild, 1.4441 + nsCOMPtr<nsIDOMNode> *aLeftNode, 1.4442 + nsCOMPtr<nsIDOMNode> *aRightNode) 1.4443 +{ 1.4444 + nsCOMPtr<nsIDOMNode> middleNode; 1.4445 + nsresult res = SplitBlock(aBlock, aStartChild, aEndChild, 1.4446 + aLeftNode, aRightNode, 1.4447 + address_of(middleNode)); 1.4448 + NS_ENSURE_SUCCESS(res, res); 1.4449 + // get rid of part of blockquote we are outdenting 1.4450 + 1.4451 + NS_ENSURE_STATE(mHTMLEditor); 1.4452 + return mHTMLEditor->RemoveBlockContainer(aBlock); 1.4453 +} 1.4454 + 1.4455 +nsresult 1.4456 +nsHTMLEditRules::SplitBlock(nsIDOMNode *aBlock, 1.4457 + nsIDOMNode *aStartChild, 1.4458 + nsIDOMNode *aEndChild, 1.4459 + nsCOMPtr<nsIDOMNode> *aLeftNode, 1.4460 + nsCOMPtr<nsIDOMNode> *aRightNode, 1.4461 + nsCOMPtr<nsIDOMNode> *aMiddleNode) 1.4462 +{ 1.4463 + NS_ENSURE_TRUE(aBlock && aStartChild && aEndChild, NS_ERROR_NULL_POINTER); 1.4464 + 1.4465 + nsCOMPtr<nsIDOMNode> leftNode, rightNode; 1.4466 + int32_t startOffset, endOffset, offset; 1.4467 + nsresult res; 1.4468 + 1.4469 + // get split point location 1.4470 + nsCOMPtr<nsIDOMNode> startParent = nsEditor::GetNodeLocation(aStartChild, &startOffset); 1.4471 + 1.4472 + // do the splits! 1.4473 + NS_ENSURE_STATE(mHTMLEditor); 1.4474 + res = mHTMLEditor->SplitNodeDeep(aBlock, startParent, startOffset, &offset, 1.4475 + true, address_of(leftNode), address_of(rightNode)); 1.4476 + NS_ENSURE_SUCCESS(res, res); 1.4477 + if (rightNode) aBlock = rightNode; 1.4478 + 1.4479 + // remember left portion of block if caller requested 1.4480 + if (aLeftNode) 1.4481 + *aLeftNode = leftNode; 1.4482 + 1.4483 + // get split point location 1.4484 + nsCOMPtr<nsIDOMNode> endParent = nsEditor::GetNodeLocation(aEndChild, &endOffset); 1.4485 + endOffset++; // want to be after lastBQChild 1.4486 + 1.4487 + // do the splits! 1.4488 + NS_ENSURE_STATE(mHTMLEditor); 1.4489 + res = mHTMLEditor->SplitNodeDeep(aBlock, endParent, endOffset, &offset, 1.4490 + true, address_of(leftNode), address_of(rightNode)); 1.4491 + NS_ENSURE_SUCCESS(res, res); 1.4492 + if (leftNode) aBlock = leftNode; 1.4493 + 1.4494 + // remember right portion of block if caller requested 1.4495 + if (aRightNode) 1.4496 + *aRightNode = rightNode; 1.4497 + 1.4498 + if (aMiddleNode) 1.4499 + *aMiddleNode = aBlock; 1.4500 + 1.4501 + return NS_OK; 1.4502 +} 1.4503 + 1.4504 +nsresult 1.4505 +nsHTMLEditRules::OutdentPartOfBlock(nsIDOMNode *aBlock, 1.4506 + nsIDOMNode *aStartChild, 1.4507 + nsIDOMNode *aEndChild, 1.4508 + bool aIsBlockIndentedWithCSS, 1.4509 + nsCOMPtr<nsIDOMNode> *aLeftNode, 1.4510 + nsCOMPtr<nsIDOMNode> *aRightNode) 1.4511 +{ 1.4512 + nsCOMPtr<nsIDOMNode> middleNode; 1.4513 + nsresult res = SplitBlock(aBlock, aStartChild, aEndChild, 1.4514 + aLeftNode, 1.4515 + aRightNode, 1.4516 + address_of(middleNode)); 1.4517 + NS_ENSURE_SUCCESS(res, res); 1.4518 + if (aIsBlockIndentedWithCSS) { 1.4519 + res = RelativeChangeIndentationOfElementNode(middleNode, -1); 1.4520 + } else { 1.4521 + NS_ENSURE_STATE(mHTMLEditor); 1.4522 + res = mHTMLEditor->RemoveBlockContainer(middleNode); 1.4523 + } 1.4524 + return res; 1.4525 +} 1.4526 + 1.4527 +/////////////////////////////////////////////////////////////////////////// 1.4528 +// ConvertListType: convert list type and list item type. 1.4529 +// 1.4530 +// 1.4531 +nsresult 1.4532 +nsHTMLEditRules::ConvertListType(nsIDOMNode* aList, 1.4533 + nsCOMPtr<nsIDOMNode>* outList, 1.4534 + nsIAtom* aListType, 1.4535 + nsIAtom* aItemType) 1.4536 +{ 1.4537 + MOZ_ASSERT(aListType); 1.4538 + MOZ_ASSERT(aItemType); 1.4539 + 1.4540 + NS_ENSURE_TRUE(aList && outList, NS_ERROR_NULL_POINTER); 1.4541 + nsCOMPtr<nsINode> list = do_QueryInterface(aList); 1.4542 + NS_ENSURE_STATE(list); 1.4543 + 1.4544 + nsCOMPtr<dom::Element> outNode; 1.4545 + nsresult rv = ConvertListType(list, getter_AddRefs(outNode), aListType, aItemType); 1.4546 + *outList = outNode ? outNode->AsDOMNode() : nullptr; 1.4547 + return rv; 1.4548 +} 1.4549 + 1.4550 +nsresult 1.4551 +nsHTMLEditRules::ConvertListType(nsINode* aList, 1.4552 + dom::Element** aOutList, 1.4553 + nsIAtom* aListType, 1.4554 + nsIAtom* aItemType) 1.4555 +{ 1.4556 + MOZ_ASSERT(aList); 1.4557 + MOZ_ASSERT(aOutList); 1.4558 + MOZ_ASSERT(aListType); 1.4559 + MOZ_ASSERT(aItemType); 1.4560 + 1.4561 + nsCOMPtr<nsINode> child = aList->GetFirstChild(); 1.4562 + while (child) 1.4563 + { 1.4564 + if (child->IsElement()) { 1.4565 + dom::Element* element = child->AsElement(); 1.4566 + if (nsHTMLEditUtils::IsListItem(element) && !element->IsHTML(aItemType)) { 1.4567 + nsCOMPtr<dom::Element> temp; 1.4568 + nsresult rv = 1.4569 + mHTMLEditor->ReplaceContainer(child, getter_AddRefs(temp), 1.4570 + nsDependentAtomString(aItemType)); 1.4571 + NS_ENSURE_SUCCESS(rv, rv); 1.4572 + child = temp.forget(); 1.4573 + } else if (nsHTMLEditUtils::IsList(element) && 1.4574 + !element->IsHTML(aListType)) { 1.4575 + nsCOMPtr<dom::Element> temp; 1.4576 + nsresult rv = 1.4577 + ConvertListType(child, getter_AddRefs(temp), aListType, aItemType); 1.4578 + NS_ENSURE_SUCCESS(rv, rv); 1.4579 + child = temp.forget(); 1.4580 + } 1.4581 + } 1.4582 + child = child->GetNextSibling(); 1.4583 + } 1.4584 + 1.4585 + if (aList->IsElement() && aList->AsElement()->IsHTML(aListType)) { 1.4586 + nsCOMPtr<dom::Element> list = aList->AsElement(); 1.4587 + list.forget(aOutList); 1.4588 + return NS_OK; 1.4589 + } 1.4590 + 1.4591 + return mHTMLEditor->ReplaceContainer(aList, aOutList, 1.4592 + nsDependentAtomString(aListType)); 1.4593 +} 1.4594 + 1.4595 + 1.4596 +/////////////////////////////////////////////////////////////////////////// 1.4597 +// CreateStyleForInsertText: take care of clearing and setting appropriate 1.4598 +// style nodes for text insertion. 1.4599 +// 1.4600 +// 1.4601 +nsresult 1.4602 +nsHTMLEditRules::CreateStyleForInsertText(nsISelection *aSelection, 1.4603 + nsIDOMDocument *aDoc) 1.4604 +{ 1.4605 + MOZ_ASSERT(aSelection && aDoc && mHTMLEditor->mTypeInState); 1.4606 + 1.4607 + bool weDidSomething = false; 1.4608 + nsCOMPtr<nsIDOMNode> node, tmp; 1.4609 + int32_t offset; 1.4610 + NS_ENSURE_STATE(mHTMLEditor); 1.4611 + nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, 1.4612 + getter_AddRefs(node), 1.4613 + &offset); 1.4614 + NS_ENSURE_SUCCESS(res, res); 1.4615 + 1.4616 + // next examine our present style and make sure default styles are either 1.4617 + // present or explicitly overridden. If neither, add the default style to 1.4618 + // the TypeInState 1.4619 + int32_t length = mHTMLEditor->mDefaultStyles.Length(); 1.4620 + for (int32_t j = 0; j < length; j++) { 1.4621 + PropItem* propItem = mHTMLEditor->mDefaultStyles[j]; 1.4622 + MOZ_ASSERT(propItem); 1.4623 + bool bFirst, bAny, bAll; 1.4624 + 1.4625 + // GetInlineProperty also examine TypeInState. The only gotcha here is 1.4626 + // that a cleared property looks like an unset property. For now I'm 1.4627 + // assuming that's not a problem: that default styles will always be 1.4628 + // multivalue styles (like font face or size) where clearing the style 1.4629 + // means we want to go back to the default. If we ever wanted a "toggle" 1.4630 + // style like bold for a default, though, I'll have to add code to detect 1.4631 + // the difference between unset and explicitly cleared, else user would 1.4632 + // never be able to unbold, for instance. 1.4633 + nsAutoString curValue; 1.4634 + NS_ENSURE_STATE(mHTMLEditor); 1.4635 + res = mHTMLEditor->GetInlinePropertyBase(propItem->tag, &propItem->attr, 1.4636 + nullptr, &bFirst, &bAny, &bAll, 1.4637 + &curValue, false); 1.4638 + NS_ENSURE_SUCCESS(res, res); 1.4639 + 1.4640 + if (!bAny) { 1.4641 + // no style set for this prop/attr 1.4642 + mHTMLEditor->mTypeInState->SetProp(propItem->tag, propItem->attr, 1.4643 + propItem->value); 1.4644 + } 1.4645 + } 1.4646 + 1.4647 + nsCOMPtr<nsIDOMElement> rootElement; 1.4648 + res = aDoc->GetDocumentElement(getter_AddRefs(rootElement)); 1.4649 + NS_ENSURE_SUCCESS(res, res); 1.4650 + 1.4651 + // process clearing any styles first 1.4652 + nsAutoPtr<PropItem> item(mHTMLEditor->mTypeInState->TakeClearProperty()); 1.4653 + while (item && node != rootElement) { 1.4654 + NS_ENSURE_STATE(mHTMLEditor); 1.4655 + res = mHTMLEditor->ClearStyle(address_of(node), &offset, 1.4656 + item->tag, &item->attr); 1.4657 + NS_ENSURE_SUCCESS(res, res); 1.4658 + item = mHTMLEditor->mTypeInState->TakeClearProperty(); 1.4659 + weDidSomething = true; 1.4660 + } 1.4661 + 1.4662 + // then process setting any styles 1.4663 + int32_t relFontSize = mHTMLEditor->mTypeInState->TakeRelativeFontSize(); 1.4664 + item = mHTMLEditor->mTypeInState->TakeSetProperty(); 1.4665 + 1.4666 + if (item || relFontSize) { 1.4667 + // we have at least one style to add; make a new text node to insert style 1.4668 + // nodes above. 1.4669 + if (mHTMLEditor->IsTextNode(node)) { 1.4670 + // if we are in a text node, split it 1.4671 + NS_ENSURE_STATE(mHTMLEditor); 1.4672 + res = mHTMLEditor->SplitNodeDeep(node, node, offset, &offset); 1.4673 + NS_ENSURE_SUCCESS(res, res); 1.4674 + node->GetParentNode(getter_AddRefs(tmp)); 1.4675 + node = tmp; 1.4676 + } 1.4677 + if (!mHTMLEditor->IsContainer(node)) { 1.4678 + return NS_OK; 1.4679 + } 1.4680 + nsCOMPtr<nsIDOMNode> newNode; 1.4681 + nsCOMPtr<nsIDOMText> nodeAsText; 1.4682 + res = aDoc->CreateTextNode(EmptyString(), getter_AddRefs(nodeAsText)); 1.4683 + NS_ENSURE_SUCCESS(res, res); 1.4684 + NS_ENSURE_TRUE(nodeAsText, NS_ERROR_NULL_POINTER); 1.4685 + newNode = do_QueryInterface(nodeAsText); 1.4686 + NS_ENSURE_STATE(mHTMLEditor); 1.4687 + res = mHTMLEditor->InsertNode(newNode, node, offset); 1.4688 + NS_ENSURE_SUCCESS(res, res); 1.4689 + node = newNode; 1.4690 + offset = 0; 1.4691 + weDidSomething = true; 1.4692 + 1.4693 + if (relFontSize) { 1.4694 + // dir indicated bigger versus smaller. 1 = bigger, -1 = smaller 1.4695 + int32_t dir = relFontSize > 0 ? 1 : -1; 1.4696 + for (int32_t j = 0; j < DeprecatedAbs(relFontSize); j++) { 1.4697 + NS_ENSURE_STATE(mHTMLEditor); 1.4698 + res = mHTMLEditor->RelativeFontChangeOnTextNode(dir, nodeAsText, 1.4699 + 0, -1); 1.4700 + NS_ENSURE_SUCCESS(res, res); 1.4701 + } 1.4702 + } 1.4703 + 1.4704 + while (item) { 1.4705 + NS_ENSURE_STATE(mHTMLEditor); 1.4706 + res = mHTMLEditor->SetInlinePropertyOnNode(node, item->tag, &item->attr, 1.4707 + &item->value); 1.4708 + NS_ENSURE_SUCCESS(res, res); 1.4709 + item = mHTMLEditor->mTypeInState->TakeSetProperty(); 1.4710 + } 1.4711 + } 1.4712 + if (weDidSomething) { 1.4713 + return aSelection->Collapse(node, offset); 1.4714 + } 1.4715 + 1.4716 + return NS_OK; 1.4717 +} 1.4718 + 1.4719 + 1.4720 +/////////////////////////////////////////////////////////////////////////// 1.4721 +// IsEmptyBlock: figure out if aNode is (or is inside) an empty block. 1.4722 +// A block can have children and still be considered empty, 1.4723 +// if the children are empty or non-editable. 1.4724 +// 1.4725 +nsresult 1.4726 +nsHTMLEditRules::IsEmptyBlock(nsIDOMNode *aNode, 1.4727 + bool *outIsEmptyBlock, 1.4728 + bool aMozBRDoesntCount, 1.4729 + bool aListItemsNotEmpty) 1.4730 +{ 1.4731 + NS_ENSURE_TRUE(aNode && outIsEmptyBlock, NS_ERROR_NULL_POINTER); 1.4732 + *outIsEmptyBlock = true; 1.4733 + 1.4734 +// nsresult res = NS_OK; 1.4735 + nsCOMPtr<nsIDOMNode> nodeToTest; 1.4736 + if (IsBlockNode(aNode)) nodeToTest = do_QueryInterface(aNode); 1.4737 +// else nsCOMPtr<nsIDOMElement> block; 1.4738 +// looks like I forgot to finish this. Wonder what I was going to do? 1.4739 + 1.4740 + NS_ENSURE_TRUE(nodeToTest, NS_ERROR_NULL_POINTER); 1.4741 + return mHTMLEditor->IsEmptyNode(nodeToTest, outIsEmptyBlock, 1.4742 + aMozBRDoesntCount, aListItemsNotEmpty); 1.4743 +} 1.4744 + 1.4745 + 1.4746 +nsresult 1.4747 +nsHTMLEditRules::WillAlign(Selection* aSelection, 1.4748 + const nsAString *alignType, 1.4749 + bool *aCancel, 1.4750 + bool *aHandled) 1.4751 +{ 1.4752 + if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; } 1.4753 + 1.4754 + nsresult res = WillInsert(aSelection, aCancel); 1.4755 + NS_ENSURE_SUCCESS(res, res); 1.4756 + 1.4757 + // initialize out param 1.4758 + // we want to ignore result of WillInsert() 1.4759 + *aCancel = false; 1.4760 + *aHandled = false; 1.4761 + 1.4762 + res = NormalizeSelection(aSelection); 1.4763 + NS_ENSURE_SUCCESS(res, res); 1.4764 + nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor); 1.4765 + 1.4766 + // convert the selection ranges into "promoted" selection ranges: 1.4767 + // this basically just expands the range to include the immediate 1.4768 + // block parent, and then further expands to include any ancestors 1.4769 + // whose children are all in the range 1.4770 + *aHandled = true; 1.4771 + nsCOMArray<nsIDOMNode> arrayOfNodes; 1.4772 + res = GetNodesFromSelection(aSelection, EditAction::align, arrayOfNodes); 1.4773 + NS_ENSURE_SUCCESS(res, res); 1.4774 + 1.4775 + // if we don't have any nodes, or we have only a single br, then we are 1.4776 + // creating an empty alignment div. We have to do some different things for these. 1.4777 + bool emptyDiv = false; 1.4778 + int32_t listCount = arrayOfNodes.Count(); 1.4779 + if (!listCount) emptyDiv = true; 1.4780 + if (listCount == 1) 1.4781 + { 1.4782 + nsCOMPtr<nsIDOMNode> theNode = arrayOfNodes[0]; 1.4783 + 1.4784 + if (nsHTMLEditUtils::SupportsAlignAttr(theNode)) 1.4785 + { 1.4786 + // the node is a table element, an horiz rule, a paragraph, a div 1.4787 + // or a section header; in HTML 4, it can directly carry the ALIGN 1.4788 + // attribute and we don't need to make a div! If we are in CSS mode, 1.4789 + // all the work is done in AlignBlock 1.4790 + nsCOMPtr<nsIDOMElement> theElem = do_QueryInterface(theNode); 1.4791 + res = AlignBlock(theElem, alignType, true); 1.4792 + NS_ENSURE_SUCCESS(res, res); 1.4793 + return NS_OK; 1.4794 + } 1.4795 + 1.4796 + if (nsTextEditUtils::IsBreak(theNode)) 1.4797 + { 1.4798 + // The special case emptyDiv code (below) that consumes BRs can 1.4799 + // cause tables to split if the start node of the selection is 1.4800 + // not in a table cell or caption, for example parent is a <tr>. 1.4801 + // Avoid this unnecessary splitting if possible by leaving emptyDiv 1.4802 + // FALSE so that we fall through to the normal case alignment code. 1.4803 + // 1.4804 + // XXX: It seems a little error prone for the emptyDiv special 1.4805 + // case code to assume that the start node of the selection 1.4806 + // is the parent of the single node in the arrayOfNodes, as 1.4807 + // the paragraph above points out. Do we rely on the selection 1.4808 + // start node because of the fact that arrayOfNodes can be empty? 1.4809 + // We should probably revisit this issue. - kin 1.4810 + 1.4811 + nsCOMPtr<nsIDOMNode> parent; 1.4812 + int32_t offset; 1.4813 + NS_ENSURE_STATE(mHTMLEditor); 1.4814 + res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(parent), &offset); 1.4815 + 1.4816 + if (!nsHTMLEditUtils::IsTableElement(parent) || nsHTMLEditUtils::IsTableCellOrCaption(parent)) 1.4817 + emptyDiv = true; 1.4818 + } 1.4819 + } 1.4820 + if (emptyDiv) 1.4821 + { 1.4822 + int32_t offset; 1.4823 + nsCOMPtr<nsIDOMNode> brNode, parent, theDiv, sib; 1.4824 + NS_NAMED_LITERAL_STRING(divType, "div"); 1.4825 + NS_ENSURE_STATE(mHTMLEditor); 1.4826 + res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(parent), &offset); 1.4827 + NS_ENSURE_SUCCESS(res, res); 1.4828 + res = SplitAsNeeded(&divType, address_of(parent), &offset); 1.4829 + NS_ENSURE_SUCCESS(res, res); 1.4830 + // consume a trailing br, if any. This is to keep an alignment from 1.4831 + // creating extra lines, if possible. 1.4832 + NS_ENSURE_STATE(mHTMLEditor); 1.4833 + res = mHTMLEditor->GetNextHTMLNode(parent, offset, address_of(brNode)); 1.4834 + NS_ENSURE_SUCCESS(res, res); 1.4835 + if (brNode && nsTextEditUtils::IsBreak(brNode)) 1.4836 + { 1.4837 + // making use of html structure... if next node after where 1.4838 + // we are putting our div is not a block, then the br we 1.4839 + // found is in same block we are, so its safe to consume it. 1.4840 + NS_ENSURE_STATE(mHTMLEditor); 1.4841 + res = mHTMLEditor->GetNextHTMLSibling(parent, offset, address_of(sib)); 1.4842 + NS_ENSURE_SUCCESS(res, res); 1.4843 + if (!IsBlockNode(sib)) 1.4844 + { 1.4845 + NS_ENSURE_STATE(mHTMLEditor); 1.4846 + res = mHTMLEditor->DeleteNode(brNode); 1.4847 + NS_ENSURE_SUCCESS(res, res); 1.4848 + } 1.4849 + } 1.4850 + NS_ENSURE_STATE(mHTMLEditor); 1.4851 + res = mHTMLEditor->CreateNode(divType, parent, offset, getter_AddRefs(theDiv)); 1.4852 + NS_ENSURE_SUCCESS(res, res); 1.4853 + // remember our new block for postprocessing 1.4854 + mNewBlock = theDiv; 1.4855 + // set up the alignment on the div, using HTML or CSS 1.4856 + nsCOMPtr<nsIDOMElement> divElem = do_QueryInterface(theDiv); 1.4857 + res = AlignBlock(divElem, alignType, true); 1.4858 + NS_ENSURE_SUCCESS(res, res); 1.4859 + *aHandled = true; 1.4860 + // put in a moz-br so that it won't get deleted 1.4861 + res = CreateMozBR(theDiv, 0); 1.4862 + NS_ENSURE_SUCCESS(res, res); 1.4863 + res = aSelection->Collapse(theDiv, 0); 1.4864 + selectionResetter.Abort(); // don't reset our selection in this case. 1.4865 + return res; 1.4866 + } 1.4867 + 1.4868 + // Next we detect all the transitions in the array, where a transition 1.4869 + // means that adjacent nodes in the array don't have the same parent. 1.4870 + 1.4871 + nsTArray<bool> transitionList; 1.4872 + res = MakeTransitionList(arrayOfNodes, transitionList); 1.4873 + NS_ENSURE_SUCCESS(res, res); 1.4874 + 1.4875 + // Ok, now go through all the nodes and give them an align attrib or put them in a div, 1.4876 + // or whatever is appropriate. Wohoo! 1.4877 + 1.4878 + nsCOMPtr<nsIDOMNode> curParent; 1.4879 + nsCOMPtr<nsIDOMNode> curDiv; 1.4880 + bool useCSS = mHTMLEditor->IsCSSEnabled(); 1.4881 + for (int32_t i = 0; i < listCount; ++i) { 1.4882 + // here's where we actually figure out what to do 1.4883 + nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i]; 1.4884 + 1.4885 + // Ignore all non-editable nodes. Leave them be. 1.4886 + if (!mHTMLEditor->IsEditable(curNode)) continue; 1.4887 + 1.4888 + int32_t offset; 1.4889 + curParent = nsEditor::GetNodeLocation(curNode, &offset); 1.4890 + 1.4891 + // the node is a table element, an horiz rule, a paragraph, a div 1.4892 + // or a section header; in HTML 4, it can directly carry the ALIGN 1.4893 + // attribute and we don't need to nest it, just set the alignment. 1.4894 + // In CSS, assign the corresponding CSS styles in AlignBlock 1.4895 + if (nsHTMLEditUtils::SupportsAlignAttr(curNode)) 1.4896 + { 1.4897 + nsCOMPtr<nsIDOMElement> curElem = do_QueryInterface(curNode); 1.4898 + res = AlignBlock(curElem, alignType, false); 1.4899 + NS_ENSURE_SUCCESS(res, res); 1.4900 + // clear out curDiv so that we don't put nodes after this one into it 1.4901 + curDiv = 0; 1.4902 + continue; 1.4903 + } 1.4904 + 1.4905 + // Skip insignificant formatting text nodes to prevent 1.4906 + // unnecessary structure splitting! 1.4907 + bool isEmptyTextNode = false; 1.4908 + if (nsEditor::IsTextNode(curNode) && 1.4909 + ((nsHTMLEditUtils::IsTableElement(curParent) && !nsHTMLEditUtils::IsTableCellOrCaption(curParent)) || 1.4910 + nsHTMLEditUtils::IsList(curParent) || 1.4911 + (NS_SUCCEEDED(mHTMLEditor->IsEmptyNode(curNode, &isEmptyTextNode)) && isEmptyTextNode))) 1.4912 + continue; 1.4913 + 1.4914 + // if it's a list item, or a list 1.4915 + // inside a list, forget any "current" div, and instead put divs inside 1.4916 + // the appropriate block (td, li, etc) 1.4917 + if ( nsHTMLEditUtils::IsListItem(curNode) 1.4918 + || nsHTMLEditUtils::IsList(curNode)) 1.4919 + { 1.4920 + res = RemoveAlignment(curNode, *alignType, true); 1.4921 + NS_ENSURE_SUCCESS(res, res); 1.4922 + if (useCSS) { 1.4923 + nsCOMPtr<nsIDOMElement> curElem = do_QueryInterface(curNode); 1.4924 + NS_NAMED_LITERAL_STRING(attrName, "align"); 1.4925 + int32_t count; 1.4926 + mHTMLEditor->mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(curNode, nullptr, 1.4927 + &attrName, alignType, 1.4928 + &count, false); 1.4929 + curDiv = 0; 1.4930 + continue; 1.4931 + } 1.4932 + else if (nsHTMLEditUtils::IsList(curParent)) { 1.4933 + // if we don't use CSS, add a contraint to list element : they have 1.4934 + // to be inside another list, ie >= second level of nesting 1.4935 + res = AlignInnerBlocks(curNode, alignType); 1.4936 + NS_ENSURE_SUCCESS(res, res); 1.4937 + curDiv = 0; 1.4938 + continue; 1.4939 + } 1.4940 + // clear out curDiv so that we don't put nodes after this one into it 1.4941 + } 1.4942 + 1.4943 + // need to make a div to put things in if we haven't already, 1.4944 + // or if this node doesn't go in div we used earlier. 1.4945 + if (!curDiv || transitionList[i]) 1.4946 + { 1.4947 + // First, check that our element can contain a div. 1.4948 + NS_NAMED_LITERAL_STRING(divType, "div"); 1.4949 + if (!mEditor->CanContainTag(curParent, nsGkAtoms::div)) { 1.4950 + return NS_OK; // cancelled 1.4951 + } 1.4952 + 1.4953 + res = SplitAsNeeded(&divType, address_of(curParent), &offset); 1.4954 + NS_ENSURE_SUCCESS(res, res); 1.4955 + NS_ENSURE_STATE(mHTMLEditor); 1.4956 + res = mHTMLEditor->CreateNode(divType, curParent, offset, getter_AddRefs(curDiv)); 1.4957 + NS_ENSURE_SUCCESS(res, res); 1.4958 + // remember our new block for postprocessing 1.4959 + mNewBlock = curDiv; 1.4960 + // set up the alignment on the div 1.4961 + nsCOMPtr<nsIDOMElement> divElem = do_QueryInterface(curDiv); 1.4962 + res = AlignBlock(divElem, alignType, true); 1.4963 + //nsAutoString attr(NS_LITERAL_STRING("align")); 1.4964 + //res = mHTMLEditor->SetAttribute(divElem, attr, *alignType); 1.4965 + //NS_ENSURE_SUCCESS(res, res); 1.4966 + // curDiv is now the correct thing to put curNode in 1.4967 + } 1.4968 + 1.4969 + // tuck the node into the end of the active div 1.4970 + NS_ENSURE_STATE(mHTMLEditor); 1.4971 + res = mHTMLEditor->MoveNode(curNode, curDiv, -1); 1.4972 + NS_ENSURE_SUCCESS(res, res); 1.4973 + } 1.4974 + 1.4975 + return res; 1.4976 +} 1.4977 + 1.4978 + 1.4979 +/////////////////////////////////////////////////////////////////////////// 1.4980 +// AlignInnerBlocks: align inside table cells or list items 1.4981 +// 1.4982 +nsresult 1.4983 +nsHTMLEditRules::AlignInnerBlocks(nsIDOMNode *aNode, const nsAString *alignType) 1.4984 +{ 1.4985 + NS_ENSURE_TRUE(aNode && alignType, NS_ERROR_NULL_POINTER); 1.4986 + nsresult res; 1.4987 + 1.4988 + // gather list of table cells or list items 1.4989 + nsCOMArray<nsIDOMNode> arrayOfNodes; 1.4990 + nsTableCellAndListItemFunctor functor; 1.4991 + nsDOMIterator iter; 1.4992 + res = iter.Init(aNode); 1.4993 + NS_ENSURE_SUCCESS(res, res); 1.4994 + res = iter.AppendList(functor, arrayOfNodes); 1.4995 + NS_ENSURE_SUCCESS(res, res); 1.4996 + 1.4997 + // now that we have the list, align their contents as requested 1.4998 + int32_t listCount = arrayOfNodes.Count(); 1.4999 + int32_t j; 1.5000 + 1.5001 + for (j = 0; j < listCount; j++) 1.5002 + { 1.5003 + nsIDOMNode* node = arrayOfNodes[0]; 1.5004 + res = AlignBlockContents(node, alignType); 1.5005 + NS_ENSURE_SUCCESS(res, res); 1.5006 + arrayOfNodes.RemoveObjectAt(0); 1.5007 + } 1.5008 + 1.5009 + return res; 1.5010 +} 1.5011 + 1.5012 + 1.5013 +/////////////////////////////////////////////////////////////////////////// 1.5014 +// AlignBlockContents: align contents of a block element 1.5015 +// 1.5016 +nsresult 1.5017 +nsHTMLEditRules::AlignBlockContents(nsIDOMNode *aNode, const nsAString *alignType) 1.5018 +{ 1.5019 + NS_ENSURE_TRUE(aNode && alignType, NS_ERROR_NULL_POINTER); 1.5020 + nsresult res; 1.5021 + nsCOMPtr <nsIDOMNode> firstChild, lastChild, divNode; 1.5022 + 1.5023 + bool useCSS = mHTMLEditor->IsCSSEnabled(); 1.5024 + 1.5025 + NS_ENSURE_STATE(mHTMLEditor); 1.5026 + res = mHTMLEditor->GetFirstEditableChild(aNode, address_of(firstChild)); 1.5027 + NS_ENSURE_SUCCESS(res, res); 1.5028 + NS_ENSURE_STATE(mHTMLEditor); 1.5029 + res = mHTMLEditor->GetLastEditableChild(aNode, address_of(lastChild)); 1.5030 + NS_ENSURE_SUCCESS(res, res); 1.5031 + NS_NAMED_LITERAL_STRING(attr, "align"); 1.5032 + if (!firstChild) 1.5033 + { 1.5034 + // this cell has no content, nothing to align 1.5035 + } 1.5036 + else if ((firstChild==lastChild) && nsHTMLEditUtils::IsDiv(firstChild)) 1.5037 + { 1.5038 + // the cell already has a div containing all of its content: just 1.5039 + // act on this div. 1.5040 + nsCOMPtr<nsIDOMElement> divElem = do_QueryInterface(firstChild); 1.5041 + if (useCSS) { 1.5042 + NS_ENSURE_STATE(mHTMLEditor); 1.5043 + res = mHTMLEditor->SetAttributeOrEquivalent(divElem, attr, *alignType, false); 1.5044 + } 1.5045 + else { 1.5046 + NS_ENSURE_STATE(mHTMLEditor); 1.5047 + res = mHTMLEditor->SetAttribute(divElem, attr, *alignType); 1.5048 + } 1.5049 + NS_ENSURE_SUCCESS(res, res); 1.5050 + } 1.5051 + else 1.5052 + { 1.5053 + // else we need to put in a div, set the alignment, and toss in all the children 1.5054 + NS_ENSURE_STATE(mHTMLEditor); 1.5055 + res = mHTMLEditor->CreateNode(NS_LITERAL_STRING("div"), aNode, 0, getter_AddRefs(divNode)); 1.5056 + NS_ENSURE_SUCCESS(res, res); 1.5057 + // set up the alignment on the div 1.5058 + nsCOMPtr<nsIDOMElement> divElem = do_QueryInterface(divNode); 1.5059 + if (useCSS) { 1.5060 + NS_ENSURE_STATE(mHTMLEditor); 1.5061 + res = mHTMLEditor->SetAttributeOrEquivalent(divElem, attr, *alignType, false); 1.5062 + } 1.5063 + else { 1.5064 + NS_ENSURE_STATE(mHTMLEditor); 1.5065 + res = mHTMLEditor->SetAttribute(divElem, attr, *alignType); 1.5066 + } 1.5067 + NS_ENSURE_SUCCESS(res, res); 1.5068 + // tuck the children into the end of the active div 1.5069 + while (lastChild && (lastChild != divNode)) 1.5070 + { 1.5071 + NS_ENSURE_STATE(mHTMLEditor); 1.5072 + res = mHTMLEditor->MoveNode(lastChild, divNode, 0); 1.5073 + NS_ENSURE_SUCCESS(res, res); 1.5074 + NS_ENSURE_STATE(mHTMLEditor); 1.5075 + res = mHTMLEditor->GetLastEditableChild(aNode, address_of(lastChild)); 1.5076 + NS_ENSURE_SUCCESS(res, res); 1.5077 + } 1.5078 + } 1.5079 + return res; 1.5080 +} 1.5081 + 1.5082 +/////////////////////////////////////////////////////////////////////////// 1.5083 +// CheckForEmptyBlock: Called by WillDeleteSelection to detect and handle 1.5084 +// case of deleting from inside an empty block. 1.5085 +// 1.5086 +nsresult 1.5087 +nsHTMLEditRules::CheckForEmptyBlock(nsIDOMNode *aStartNode, 1.5088 + nsIDOMNode *aBodyNode, 1.5089 + nsISelection *aSelection, 1.5090 + bool *aHandled) 1.5091 +{ 1.5092 + // If the editing host is an inline element, bail out early. 1.5093 + if (IsInlineNode(aBodyNode)) { 1.5094 + return NS_OK; 1.5095 + } 1.5096 + // if we are inside an empty block, delete it. 1.5097 + // Note: do NOT delete table elements this way. 1.5098 + nsresult res = NS_OK; 1.5099 + nsCOMPtr<nsIDOMNode> block, emptyBlock; 1.5100 + if (IsBlockNode(aStartNode)) 1.5101 + block = aStartNode; 1.5102 + else 1.5103 + block = mHTMLEditor->GetBlockNodeParent(aStartNode); 1.5104 + bool bIsEmptyNode; 1.5105 + if (block != aBodyNode) // efficiency hack. avoiding IsEmptyNode() call when in body 1.5106 + { 1.5107 + NS_ENSURE_STATE(mHTMLEditor); 1.5108 + res = mHTMLEditor->IsEmptyNode(block, &bIsEmptyNode, true, false); 1.5109 + NS_ENSURE_SUCCESS(res, res); 1.5110 + while (bIsEmptyNode && !nsHTMLEditUtils::IsTableElement(block) && (block != aBodyNode)) 1.5111 + { 1.5112 + emptyBlock = block; 1.5113 + block = mHTMLEditor->GetBlockNodeParent(emptyBlock); 1.5114 + NS_ENSURE_STATE(mHTMLEditor); 1.5115 + res = mHTMLEditor->IsEmptyNode(block, &bIsEmptyNode, true, false); 1.5116 + NS_ENSURE_SUCCESS(res, res); 1.5117 + } 1.5118 + } 1.5119 + 1.5120 + nsCOMPtr<nsIContent> emptyContent = do_QueryInterface(emptyBlock); 1.5121 + if (emptyBlock && emptyContent->IsEditable()) 1.5122 + { 1.5123 + int32_t offset; 1.5124 + nsCOMPtr<nsIDOMNode> blockParent = nsEditor::GetNodeLocation(emptyBlock, &offset); 1.5125 + NS_ENSURE_TRUE(blockParent && offset >= 0, NS_ERROR_FAILURE); 1.5126 + 1.5127 + if (nsHTMLEditUtils::IsListItem(emptyBlock)) 1.5128 + { 1.5129 + // are we the first list item in the list? 1.5130 + bool bIsFirst; 1.5131 + NS_ENSURE_STATE(mHTMLEditor); 1.5132 + res = mHTMLEditor->IsFirstEditableChild(emptyBlock, &bIsFirst); 1.5133 + NS_ENSURE_SUCCESS(res, res); 1.5134 + if (bIsFirst) 1.5135 + { 1.5136 + int32_t listOffset; 1.5137 + nsCOMPtr<nsIDOMNode> listParent = nsEditor::GetNodeLocation(blockParent, 1.5138 + &listOffset); 1.5139 + NS_ENSURE_TRUE(listParent && listOffset >= 0, NS_ERROR_FAILURE); 1.5140 + // if we are a sublist, skip the br creation 1.5141 + if (!nsHTMLEditUtils::IsList(listParent)) 1.5142 + { 1.5143 + // create a br before list 1.5144 + nsCOMPtr<nsIDOMNode> brNode; 1.5145 + NS_ENSURE_STATE(mHTMLEditor); 1.5146 + res = mHTMLEditor->CreateBR(listParent, listOffset, address_of(brNode)); 1.5147 + NS_ENSURE_SUCCESS(res, res); 1.5148 + // adjust selection to be right before it 1.5149 + res = aSelection->Collapse(listParent, listOffset); 1.5150 + NS_ENSURE_SUCCESS(res, res); 1.5151 + } 1.5152 + // else just let selection perculate up. We'll adjust it in AfterEdit() 1.5153 + } 1.5154 + } 1.5155 + else 1.5156 + { 1.5157 + // adjust selection to be right after it 1.5158 + res = aSelection->Collapse(blockParent, offset+1); 1.5159 + NS_ENSURE_SUCCESS(res, res); 1.5160 + } 1.5161 + NS_ENSURE_STATE(mHTMLEditor); 1.5162 + res = mHTMLEditor->DeleteNode(emptyBlock); 1.5163 + *aHandled = true; 1.5164 + } 1.5165 + return res; 1.5166 +} 1.5167 + 1.5168 +nsresult 1.5169 +nsHTMLEditRules::CheckForInvisibleBR(nsIDOMNode *aBlock, 1.5170 + BRLocation aWhere, 1.5171 + nsCOMPtr<nsIDOMNode> *outBRNode, 1.5172 + int32_t aOffset) 1.5173 +{ 1.5174 + NS_ENSURE_TRUE(aBlock && outBRNode, NS_ERROR_NULL_POINTER); 1.5175 + *outBRNode = nullptr; 1.5176 + 1.5177 + nsCOMPtr<nsIDOMNode> testNode; 1.5178 + int32_t testOffset = 0; 1.5179 + bool runTest = false; 1.5180 + 1.5181 + if (aWhere == kBlockEnd) 1.5182 + { 1.5183 + nsCOMPtr<nsIDOMNode> rightmostNode = 1.5184 + mHTMLEditor->GetRightmostChild(aBlock, true); // no block crossing 1.5185 + 1.5186 + if (rightmostNode) 1.5187 + { 1.5188 + int32_t nodeOffset; 1.5189 + nsCOMPtr<nsIDOMNode> nodeParent = nsEditor::GetNodeLocation(rightmostNode, 1.5190 + &nodeOffset); 1.5191 + runTest = true; 1.5192 + testNode = nodeParent; 1.5193 + // use offset + 1, because we want the last node included in our 1.5194 + // evaluation 1.5195 + testOffset = nodeOffset + 1; 1.5196 + } 1.5197 + } 1.5198 + else if (aOffset) 1.5199 + { 1.5200 + runTest = true; 1.5201 + testNode = aBlock; 1.5202 + // we'll check everything to the left of the input position 1.5203 + testOffset = aOffset; 1.5204 + } 1.5205 + 1.5206 + if (runTest) 1.5207 + { 1.5208 + nsWSRunObject wsTester(mHTMLEditor, testNode, testOffset); 1.5209 + if (WSType::br == wsTester.mStartReason) { 1.5210 + *outBRNode = wsTester.mStartReasonNode; 1.5211 + } 1.5212 + } 1.5213 + 1.5214 + return NS_OK; 1.5215 +} 1.5216 + 1.5217 + 1.5218 +/////////////////////////////////////////////////////////////////////////// 1.5219 +// GetInnerContent: aList and aTbl allow the caller to specify what kind 1.5220 +// of content to "look inside". If aTbl is true, look inside 1.5221 +// any table content, and insert the inner content into the 1.5222 +// supplied issupportsarray at offset aIndex. 1.5223 +// Similarly with aList and list content. 1.5224 +// aIndex is updated to point past inserted elements. 1.5225 +// 1.5226 +nsresult 1.5227 +nsHTMLEditRules::GetInnerContent(nsIDOMNode *aNode, nsCOMArray<nsIDOMNode> &outArrayOfNodes, 1.5228 + int32_t *aIndex, bool aList, bool aTbl) 1.5229 +{ 1.5230 + NS_ENSURE_TRUE(aNode && aIndex, NS_ERROR_NULL_POINTER); 1.5231 + 1.5232 + nsCOMPtr<nsIDOMNode> node; 1.5233 + 1.5234 + nsresult res = mHTMLEditor->GetFirstEditableChild(aNode, address_of(node)); 1.5235 + while (NS_SUCCEEDED(res) && node) 1.5236 + { 1.5237 + if ( ( aList && (nsHTMLEditUtils::IsList(node) || 1.5238 + nsHTMLEditUtils::IsListItem(node) ) ) 1.5239 + || ( aTbl && nsHTMLEditUtils::IsTableElement(node) ) ) 1.5240 + { 1.5241 + res = GetInnerContent(node, outArrayOfNodes, aIndex, aList, aTbl); 1.5242 + NS_ENSURE_SUCCESS(res, res); 1.5243 + } 1.5244 + else 1.5245 + { 1.5246 + outArrayOfNodes.InsertObjectAt(node, *aIndex); 1.5247 + (*aIndex)++; 1.5248 + } 1.5249 + nsCOMPtr<nsIDOMNode> tmp; 1.5250 + res = node->GetNextSibling(getter_AddRefs(tmp)); 1.5251 + node = tmp; 1.5252 + } 1.5253 + 1.5254 + return res; 1.5255 +} 1.5256 + 1.5257 +/////////////////////////////////////////////////////////////////////////// 1.5258 +// ExpandSelectionForDeletion: this promotes our selection to include blocks 1.5259 +// that have all their children selected. 1.5260 +// 1.5261 +nsresult 1.5262 +nsHTMLEditRules::ExpandSelectionForDeletion(nsISelection *aSelection) 1.5263 +{ 1.5264 + NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER); 1.5265 + 1.5266 + // don't need to touch collapsed selections 1.5267 + if (aSelection->Collapsed()) { 1.5268 + return NS_OK; 1.5269 + } 1.5270 + 1.5271 + int32_t rangeCount; 1.5272 + nsresult res = aSelection->GetRangeCount(&rangeCount); 1.5273 + NS_ENSURE_SUCCESS(res, res); 1.5274 + 1.5275 + // we don't need to mess with cell selections, and we assume multirange selections are those. 1.5276 + if (rangeCount != 1) return NS_OK; 1.5277 + 1.5278 + // find current sel start and end 1.5279 + nsCOMPtr<nsIDOMRange> range; 1.5280 + res = aSelection->GetRangeAt(0, getter_AddRefs(range)); 1.5281 + NS_ENSURE_SUCCESS(res, res); 1.5282 + NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER); 1.5283 + nsCOMPtr<nsIDOMNode> selStartNode, selEndNode, selCommon; 1.5284 + int32_t selStartOffset, selEndOffset; 1.5285 + 1.5286 + res = range->GetStartContainer(getter_AddRefs(selStartNode)); 1.5287 + NS_ENSURE_SUCCESS(res, res); 1.5288 + res = range->GetStartOffset(&selStartOffset); 1.5289 + NS_ENSURE_SUCCESS(res, res); 1.5290 + res = range->GetEndContainer(getter_AddRefs(selEndNode)); 1.5291 + NS_ENSURE_SUCCESS(res, res); 1.5292 + res = range->GetEndOffset(&selEndOffset); 1.5293 + NS_ENSURE_SUCCESS(res, res); 1.5294 + 1.5295 + // find current selection common block parent 1.5296 + res = range->GetCommonAncestorContainer(getter_AddRefs(selCommon)); 1.5297 + NS_ENSURE_SUCCESS(res, res); 1.5298 + if (!IsBlockNode(selCommon)) 1.5299 + selCommon = nsHTMLEditor::GetBlockNodeParent(selCommon); 1.5300 + 1.5301 + // set up for loops and cache our root element 1.5302 + bool stillLooking = true; 1.5303 + nsCOMPtr<nsIDOMNode> visNode, firstBRParent; 1.5304 + int32_t visOffset=0, firstBROffset=0; 1.5305 + WSType wsType; 1.5306 + nsCOMPtr<nsIContent> rootContent = mHTMLEditor->GetActiveEditingHost(); 1.5307 + nsCOMPtr<nsIDOMNode> rootElement = do_QueryInterface(rootContent); 1.5308 + NS_ENSURE_TRUE(rootElement, NS_ERROR_FAILURE); 1.5309 + 1.5310 + // find previous visible thingy before start of selection 1.5311 + if ((selStartNode!=selCommon) && (selStartNode!=rootElement)) 1.5312 + { 1.5313 + while (stillLooking) 1.5314 + { 1.5315 + nsWSRunObject wsObj(mHTMLEditor, selStartNode, selStartOffset); 1.5316 + wsObj.PriorVisibleNode(selStartNode, selStartOffset, address_of(visNode), 1.5317 + &visOffset, &wsType); 1.5318 + if (wsType == WSType::thisBlock) { 1.5319 + // we want to keep looking up. But stop if we are crossing table element 1.5320 + // boundaries, or if we hit the root. 1.5321 + if ( nsHTMLEditUtils::IsTableElement(wsObj.mStartReasonNode) || 1.5322 + (selCommon == wsObj.mStartReasonNode) || 1.5323 + (rootElement == wsObj.mStartReasonNode) ) 1.5324 + { 1.5325 + stillLooking = false; 1.5326 + } 1.5327 + else 1.5328 + { 1.5329 + selStartNode = nsEditor::GetNodeLocation(wsObj.mStartReasonNode, 1.5330 + &selStartOffset); 1.5331 + } 1.5332 + } 1.5333 + else 1.5334 + { 1.5335 + stillLooking = false; 1.5336 + } 1.5337 + } 1.5338 + } 1.5339 + 1.5340 + stillLooking = true; 1.5341 + // find next visible thingy after end of selection 1.5342 + if ((selEndNode!=selCommon) && (selEndNode!=rootElement)) 1.5343 + { 1.5344 + while (stillLooking) 1.5345 + { 1.5346 + nsWSRunObject wsObj(mHTMLEditor, selEndNode, selEndOffset); 1.5347 + wsObj.NextVisibleNode(selEndNode, selEndOffset, address_of(visNode), 1.5348 + &visOffset, &wsType); 1.5349 + if (wsType == WSType::br) { 1.5350 + if (mHTMLEditor->IsVisBreak(wsObj.mEndReasonNode)) 1.5351 + { 1.5352 + stillLooking = false; 1.5353 + } 1.5354 + else 1.5355 + { 1.5356 + if (!firstBRParent) 1.5357 + { 1.5358 + firstBRParent = selEndNode; 1.5359 + firstBROffset = selEndOffset; 1.5360 + } 1.5361 + selEndNode = nsEditor::GetNodeLocation(wsObj.mEndReasonNode, &selEndOffset); 1.5362 + ++selEndOffset; 1.5363 + } 1.5364 + } else if (wsType == WSType::thisBlock) { 1.5365 + // we want to keep looking up. But stop if we are crossing table element 1.5366 + // boundaries, or if we hit the root. 1.5367 + if ( nsHTMLEditUtils::IsTableElement(wsObj.mEndReasonNode) || 1.5368 + (selCommon == wsObj.mEndReasonNode) || 1.5369 + (rootElement == wsObj.mEndReasonNode) ) 1.5370 + { 1.5371 + stillLooking = false; 1.5372 + } 1.5373 + else 1.5374 + { 1.5375 + selEndNode = nsEditor::GetNodeLocation(wsObj.mEndReasonNode, &selEndOffset); 1.5376 + ++selEndOffset; 1.5377 + } 1.5378 + } 1.5379 + else 1.5380 + { 1.5381 + stillLooking = false; 1.5382 + } 1.5383 + } 1.5384 + } 1.5385 + // now set the selection to the new range 1.5386 + aSelection->Collapse(selStartNode, selStartOffset); 1.5387 + 1.5388 + // expand selection endpoint only if we didnt pass a br, 1.5389 + // or if we really needed to pass that br (ie, its block is now 1.5390 + // totally selected) 1.5391 + bool doEndExpansion = true; 1.5392 + if (firstBRParent) 1.5393 + { 1.5394 + // find block node containing br 1.5395 + nsCOMPtr<nsIDOMNode> brBlock = firstBRParent; 1.5396 + if (!IsBlockNode(brBlock)) 1.5397 + brBlock = nsHTMLEditor::GetBlockNodeParent(brBlock); 1.5398 + bool nodeBefore=false, nodeAfter=false; 1.5399 + 1.5400 + // create a range that represents expanded selection 1.5401 + nsCOMPtr<nsINode> node = do_QueryInterface(selStartNode); 1.5402 + NS_ENSURE_STATE(node); 1.5403 + nsRefPtr<nsRange> range = new nsRange(node); 1.5404 + res = range->SetStart(selStartNode, selStartOffset); 1.5405 + NS_ENSURE_SUCCESS(res, res); 1.5406 + res = range->SetEnd(selEndNode, selEndOffset); 1.5407 + NS_ENSURE_SUCCESS(res, res); 1.5408 + 1.5409 + // check if block is entirely inside range 1.5410 + nsCOMPtr<nsIContent> brContentBlock = do_QueryInterface(brBlock); 1.5411 + res = nsRange::CompareNodeToRange(brContentBlock, range, &nodeBefore, &nodeAfter); 1.5412 + 1.5413 + // if block isn't contained, forgo grabbing the br in the expanded selection 1.5414 + if (nodeBefore || nodeAfter) 1.5415 + doEndExpansion = false; 1.5416 + } 1.5417 + if (doEndExpansion) 1.5418 + { 1.5419 + res = aSelection->Extend(selEndNode, selEndOffset); 1.5420 + } 1.5421 + else 1.5422 + { 1.5423 + // only expand to just before br 1.5424 + res = aSelection->Extend(firstBRParent, firstBROffset); 1.5425 + } 1.5426 + 1.5427 + return res; 1.5428 +} 1.5429 + 1.5430 + 1.5431 +/////////////////////////////////////////////////////////////////////////// 1.5432 +// NormalizeSelection: tweak non-collapsed selections to be more "natural". 1.5433 +// Idea here is to adjust selection endpoint so that they do not cross 1.5434 +// breaks or block boundaries unless something editable beyond that boundary 1.5435 +// is also selected. This adjustment makes it much easier for the various 1.5436 +// block operations to determine what nodes to act on. 1.5437 +// 1.5438 +nsresult 1.5439 +nsHTMLEditRules::NormalizeSelection(nsISelection *inSelection) 1.5440 +{ 1.5441 + NS_ENSURE_TRUE(inSelection, NS_ERROR_NULL_POINTER); 1.5442 + 1.5443 + // don't need to touch collapsed selections 1.5444 + if (inSelection->Collapsed()) { 1.5445 + return NS_OK; 1.5446 + } 1.5447 + 1.5448 + int32_t rangeCount; 1.5449 + nsresult res = inSelection->GetRangeCount(&rangeCount); 1.5450 + NS_ENSURE_SUCCESS(res, res); 1.5451 + 1.5452 + // we don't need to mess with cell selections, and we assume multirange selections are those. 1.5453 + if (rangeCount != 1) return NS_OK; 1.5454 + 1.5455 + nsCOMPtr<nsIDOMRange> range; 1.5456 + res = inSelection->GetRangeAt(0, getter_AddRefs(range)); 1.5457 + NS_ENSURE_SUCCESS(res, res); 1.5458 + NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER); 1.5459 + nsCOMPtr<nsIDOMNode> startNode, endNode; 1.5460 + int32_t startOffset, endOffset; 1.5461 + nsCOMPtr<nsIDOMNode> newStartNode, newEndNode; 1.5462 + int32_t newStartOffset, newEndOffset; 1.5463 + 1.5464 + res = range->GetStartContainer(getter_AddRefs(startNode)); 1.5465 + NS_ENSURE_SUCCESS(res, res); 1.5466 + res = range->GetStartOffset(&startOffset); 1.5467 + NS_ENSURE_SUCCESS(res, res); 1.5468 + res = range->GetEndContainer(getter_AddRefs(endNode)); 1.5469 + NS_ENSURE_SUCCESS(res, res); 1.5470 + res = range->GetEndOffset(&endOffset); 1.5471 + NS_ENSURE_SUCCESS(res, res); 1.5472 + 1.5473 + // adjusted values default to original values 1.5474 + newStartNode = startNode; 1.5475 + newStartOffset = startOffset; 1.5476 + newEndNode = endNode; 1.5477 + newEndOffset = endOffset; 1.5478 + 1.5479 + // some locals we need for whitespace code 1.5480 + nsCOMPtr<nsIDOMNode> someNode; 1.5481 + int32_t offset; 1.5482 + WSType wsType; 1.5483 + 1.5484 + // let the whitespace code do the heavy lifting 1.5485 + nsWSRunObject wsEndObj(mHTMLEditor, endNode, endOffset); 1.5486 + // is there any intervening visible whitespace? if so we can't push selection past that, 1.5487 + // it would visibly change maening of users selection 1.5488 + wsEndObj.PriorVisibleNode(endNode, endOffset, address_of(someNode), 1.5489 + &offset, &wsType); 1.5490 + if (wsType != WSType::text && wsType != WSType::normalWS) { 1.5491 + // eThisBlock and eOtherBlock conveniently distinquish cases 1.5492 + // of going "down" into a block and "up" out of a block. 1.5493 + if (wsEndObj.mStartReason == WSType::otherBlock) { 1.5494 + // endpoint is just after the close of a block. 1.5495 + nsCOMPtr<nsIDOMNode> child = mHTMLEditor->GetRightmostChild(wsEndObj.mStartReasonNode, true); 1.5496 + if (child) 1.5497 + { 1.5498 + newEndNode = nsEditor::GetNodeLocation(child, &newEndOffset); 1.5499 + ++newEndOffset; // offset *after* child 1.5500 + } 1.5501 + // else block is empty - we can leave selection alone here, i think. 1.5502 + } else if (wsEndObj.mStartReason == WSType::thisBlock) { 1.5503 + // endpoint is just after start of this block 1.5504 + nsCOMPtr<nsIDOMNode> child; 1.5505 + NS_ENSURE_STATE(mHTMLEditor); 1.5506 + res = mHTMLEditor->GetPriorHTMLNode(endNode, endOffset, address_of(child)); 1.5507 + if (child) 1.5508 + { 1.5509 + newEndNode = nsEditor::GetNodeLocation(child, &newEndOffset); 1.5510 + ++newEndOffset; // offset *after* child 1.5511 + } 1.5512 + // else block is empty - we can leave selection alone here, i think. 1.5513 + } else if (wsEndObj.mStartReason == WSType::br) { 1.5514 + // endpoint is just after break. lets adjust it to before it. 1.5515 + newEndNode = nsEditor::GetNodeLocation(wsEndObj.mStartReasonNode, 1.5516 + &newEndOffset); 1.5517 + } 1.5518 + } 1.5519 + 1.5520 + 1.5521 + // similar dealio for start of range 1.5522 + nsWSRunObject wsStartObj(mHTMLEditor, startNode, startOffset); 1.5523 + // is there any intervening visible whitespace? if so we can't push selection past that, 1.5524 + // it would visibly change maening of users selection 1.5525 + wsStartObj.NextVisibleNode(startNode, startOffset, address_of(someNode), 1.5526 + &offset, &wsType); 1.5527 + if (wsType != WSType::text && wsType != WSType::normalWS) { 1.5528 + // eThisBlock and eOtherBlock conveniently distinquish cases 1.5529 + // of going "down" into a block and "up" out of a block. 1.5530 + if (wsStartObj.mEndReason == WSType::otherBlock) { 1.5531 + // startpoint is just before the start of a block. 1.5532 + nsCOMPtr<nsIDOMNode> child = mHTMLEditor->GetLeftmostChild(wsStartObj.mEndReasonNode, true); 1.5533 + if (child) 1.5534 + { 1.5535 + newStartNode = nsEditor::GetNodeLocation(child, &newStartOffset); 1.5536 + } 1.5537 + // else block is empty - we can leave selection alone here, i think. 1.5538 + } else if (wsStartObj.mEndReason == WSType::thisBlock) { 1.5539 + // startpoint is just before end of this block 1.5540 + nsCOMPtr<nsIDOMNode> child; 1.5541 + NS_ENSURE_STATE(mHTMLEditor); 1.5542 + res = mHTMLEditor->GetNextHTMLNode(startNode, startOffset, address_of(child)); 1.5543 + if (child) 1.5544 + { 1.5545 + newStartNode = nsEditor::GetNodeLocation(child, &newStartOffset); 1.5546 + } 1.5547 + // else block is empty - we can leave selection alone here, i think. 1.5548 + } else if (wsStartObj.mEndReason == WSType::br) { 1.5549 + // startpoint is just before a break. lets adjust it to after it. 1.5550 + newStartNode = nsEditor::GetNodeLocation(wsStartObj.mEndReasonNode, 1.5551 + &newStartOffset); 1.5552 + ++newStartOffset; // offset *after* break 1.5553 + } 1.5554 + } 1.5555 + 1.5556 + // there is a demented possiblity we have to check for. We might have a very strange selection 1.5557 + // that is not collapsed and yet does not contain any editable content, and satisfies some of the 1.5558 + // above conditions that cause tweaking. In this case we don't want to tweak the selection into 1.5559 + // a block it was never in, etc. There are a variety of strategies one might use to try to 1.5560 + // detect these cases, but I think the most straightforward is to see if the adjusted locations 1.5561 + // "cross" the old values: ie, new end before old start, or new start after old end. If so 1.5562 + // then just leave things alone. 1.5563 + 1.5564 + int16_t comp; 1.5565 + comp = nsContentUtils::ComparePoints(startNode, startOffset, 1.5566 + newEndNode, newEndOffset); 1.5567 + if (comp == 1) return NS_OK; // new end before old start 1.5568 + comp = nsContentUtils::ComparePoints(newStartNode, newStartOffset, 1.5569 + endNode, endOffset); 1.5570 + if (comp == 1) return NS_OK; // new start after old end 1.5571 + 1.5572 + // otherwise set selection to new values. 1.5573 + inSelection->Collapse(newStartNode, newStartOffset); 1.5574 + inSelection->Extend(newEndNode, newEndOffset); 1.5575 + return NS_OK; 1.5576 +} 1.5577 + 1.5578 + 1.5579 +/////////////////////////////////////////////////////////////////////////// 1.5580 +// GetPromotedPoint: figure out where a start or end point for a block 1.5581 +// operation really is 1.5582 +void 1.5583 +nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode* aNode, 1.5584 + int32_t aOffset, 1.5585 + EditAction actionID, 1.5586 + nsCOMPtr<nsIDOMNode>* outNode, 1.5587 + int32_t* outOffset) 1.5588 +{ 1.5589 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.5590 + MOZ_ASSERT(node && outNode && outOffset); 1.5591 + 1.5592 + // default values 1.5593 + *outNode = node->AsDOMNode(); 1.5594 + *outOffset = aOffset; 1.5595 + 1.5596 + // we do one thing for text actions, something else entirely for other 1.5597 + // actions 1.5598 + if (actionID == EditAction::insertText || 1.5599 + actionID == EditAction::insertIMEText || 1.5600 + actionID == EditAction::insertBreak || 1.5601 + actionID == EditAction::deleteText) { 1.5602 + bool isSpace, isNBSP; 1.5603 + nsCOMPtr<nsIContent> content = do_QueryInterface(node), temp; 1.5604 + // for text actions, we want to look backwards (or forwards, as 1.5605 + // appropriate) for additional whitespace or nbsp's. We may have to act on 1.5606 + // these later even though they are outside of the initial selection. Even 1.5607 + // if they are in another node! 1.5608 + while (content) { 1.5609 + int32_t offset; 1.5610 + if (aWhere == kStart) { 1.5611 + NS_ENSURE_TRUE(mHTMLEditor, /* void */); 1.5612 + mHTMLEditor->IsPrevCharInNodeWhitespace(content, *outOffset, 1.5613 + &isSpace, &isNBSP, 1.5614 + getter_AddRefs(temp), &offset); 1.5615 + } else { 1.5616 + NS_ENSURE_TRUE(mHTMLEditor, /* void */); 1.5617 + mHTMLEditor->IsNextCharInNodeWhitespace(content, *outOffset, 1.5618 + &isSpace, &isNBSP, 1.5619 + getter_AddRefs(temp), &offset); 1.5620 + } 1.5621 + if (isSpace || isNBSP) { 1.5622 + content = temp; 1.5623 + *outOffset = offset; 1.5624 + } else { 1.5625 + break; 1.5626 + } 1.5627 + } 1.5628 + 1.5629 + *outNode = content->AsDOMNode(); 1.5630 + return; 1.5631 + } 1.5632 + 1.5633 + int32_t offset = aOffset; 1.5634 + 1.5635 + // else not a text section. In this case we want to see if we should grab 1.5636 + // any adjacent inline nodes and/or parents and other ancestors 1.5637 + if (aWhere == kStart) { 1.5638 + // some special casing for text nodes 1.5639 + if (node->IsNodeOfType(nsINode::eTEXT)) { 1.5640 + if (!node->GetParentNode()) { 1.5641 + // Okay, can't promote any further 1.5642 + return; 1.5643 + } 1.5644 + offset = node->GetParentNode()->IndexOf(node); 1.5645 + node = node->GetParentNode(); 1.5646 + } 1.5647 + 1.5648 + // look back through any further inline nodes that aren't across a <br> 1.5649 + // from us, and that are enclosed in the same block. 1.5650 + NS_ENSURE_TRUE(mHTMLEditor, /* void */); 1.5651 + nsCOMPtr<nsINode> priorNode = 1.5652 + mHTMLEditor->GetPriorHTMLNode(node, offset, true); 1.5653 + 1.5654 + while (priorNode && priorNode->GetParentNode() && 1.5655 + mHTMLEditor && !mHTMLEditor->IsVisBreak(priorNode->AsDOMNode()) && 1.5656 + !IsBlockNode(priorNode->AsDOMNode())) { 1.5657 + offset = priorNode->GetParentNode()->IndexOf(priorNode); 1.5658 + node = priorNode->GetParentNode(); 1.5659 + NS_ENSURE_TRUE(mHTMLEditor, /* void */); 1.5660 + priorNode = mHTMLEditor->GetPriorHTMLNode(node, offset, true); 1.5661 + } 1.5662 + 1.5663 + // finding the real start for this point. look up the tree for as long as 1.5664 + // we are the first node in the container, and as long as we haven't hit 1.5665 + // the body node. 1.5666 + NS_ENSURE_TRUE(mHTMLEditor, /* void */); 1.5667 + nsCOMPtr<nsIContent> nearNode = 1.5668 + mHTMLEditor->GetPriorHTMLNode(node, offset, true); 1.5669 + while (!nearNode && node->Tag() != nsGkAtoms::body && 1.5670 + node->GetParentNode()) { 1.5671 + // some cutoffs are here: we don't need to also include them in the 1.5672 + // aWhere == kEnd case. as long as they are in one or the other it will 1.5673 + // work. special case for outdent: don't keep looking up if we have 1.5674 + // found a blockquote element to act on 1.5675 + if (actionID == EditAction::outdent && 1.5676 + node->Tag() == nsGkAtoms::blockquote) { 1.5677 + break; 1.5678 + } 1.5679 + 1.5680 + int32_t parentOffset = node->GetParentNode()->IndexOf(node); 1.5681 + nsCOMPtr<nsINode> parent = node->GetParentNode(); 1.5682 + 1.5683 + // Don't walk past the editable section. Note that we need to check 1.5684 + // before walking up to a parent because we need to return the parent 1.5685 + // object, so the parent itself might not be in the editable area, but 1.5686 + // it's OK if we're not performing a block-level action. 1.5687 + bool blockLevelAction = actionID == EditAction::indent || 1.5688 + actionID == EditAction::outdent || 1.5689 + actionID == EditAction::align || 1.5690 + actionID == EditAction::makeBasicBlock; 1.5691 + NS_ENSURE_TRUE(mHTMLEditor, /* void */); 1.5692 + if (!mHTMLEditor->IsDescendantOfEditorRoot(parent) && 1.5693 + (blockLevelAction || !mHTMLEditor || 1.5694 + !mHTMLEditor->IsDescendantOfEditorRoot(node))) { 1.5695 + NS_ENSURE_TRUE(mHTMLEditor, /* void */); 1.5696 + break; 1.5697 + } 1.5698 + 1.5699 + node = parent; 1.5700 + offset = parentOffset; 1.5701 + NS_ENSURE_TRUE(mHTMLEditor, /* void */); 1.5702 + nearNode = mHTMLEditor->GetPriorHTMLNode(node, offset, true); 1.5703 + } 1.5704 + *outNode = node->AsDOMNode(); 1.5705 + *outOffset = offset; 1.5706 + return; 1.5707 + } 1.5708 + 1.5709 + // aWhere == kEnd 1.5710 + // some special casing for text nodes 1.5711 + if (node->IsNodeOfType(nsINode::eTEXT)) { 1.5712 + if (!node->GetParentNode()) { 1.5713 + // Okay, can't promote any further 1.5714 + return; 1.5715 + } 1.5716 + // want to be after the text node 1.5717 + offset = 1 + node->GetParentNode()->IndexOf(node); 1.5718 + node = node->GetParentNode(); 1.5719 + } 1.5720 + 1.5721 + // look ahead through any further inline nodes that aren't across a <br> from 1.5722 + // us, and that are enclosed in the same block. 1.5723 + NS_ENSURE_TRUE(mHTMLEditor, /* void */); 1.5724 + nsCOMPtr<nsIContent> nextNode = 1.5725 + mHTMLEditor->GetNextHTMLNode(node, offset, true); 1.5726 + 1.5727 + while (nextNode && !IsBlockNode(nextNode->AsDOMNode()) && 1.5728 + nextNode->GetParentNode()) { 1.5729 + offset = 1 + nextNode->GetParentNode()->IndexOf(nextNode); 1.5730 + node = nextNode->GetParentNode(); 1.5731 + NS_ENSURE_TRUE(mHTMLEditor, /* void */); 1.5732 + if (mHTMLEditor->IsVisBreak(nextNode->AsDOMNode())) { 1.5733 + break; 1.5734 + } 1.5735 + NS_ENSURE_TRUE(mHTMLEditor, /* void */); 1.5736 + nextNode = mHTMLEditor->GetNextHTMLNode(node, offset, true); 1.5737 + } 1.5738 + 1.5739 + // finding the real end for this point. look up the tree for as long as we 1.5740 + // are the last node in the container, and as long as we haven't hit the body 1.5741 + // node. 1.5742 + NS_ENSURE_TRUE(mHTMLEditor, /* void */); 1.5743 + nsCOMPtr<nsIContent> nearNode = 1.5744 + mHTMLEditor->GetNextHTMLNode(node, offset, true); 1.5745 + while (!nearNode && node->Tag() != nsGkAtoms::body && 1.5746 + node->GetParentNode()) { 1.5747 + int32_t parentOffset = node->GetParentNode()->IndexOf(node); 1.5748 + nsCOMPtr<nsINode> parent = node->GetParentNode(); 1.5749 + 1.5750 + // Don't walk past the editable section. Note that we need to check before 1.5751 + // walking up to a parent because we need to return the parent object, so 1.5752 + // the parent itself might not be in the editable area, but it's OK. 1.5753 + if ((!mHTMLEditor || !mHTMLEditor->IsDescendantOfEditorRoot(node)) && 1.5754 + (!mHTMLEditor || !mHTMLEditor->IsDescendantOfEditorRoot(parent))) { 1.5755 + NS_ENSURE_TRUE(mHTMLEditor, /* void */); 1.5756 + break; 1.5757 + } 1.5758 + 1.5759 + node = parent; 1.5760 + // we want to be AFTER nearNode 1.5761 + offset = parentOffset + 1; 1.5762 + NS_ENSURE_TRUE(mHTMLEditor, /* void */); 1.5763 + nearNode = mHTMLEditor->GetNextHTMLNode(node, offset, true); 1.5764 + } 1.5765 + *outNode = node->AsDOMNode(); 1.5766 + *outOffset = offset; 1.5767 +} 1.5768 + 1.5769 + 1.5770 +/////////////////////////////////////////////////////////////////////////// 1.5771 +// GetPromotedRanges: run all the selection range endpoint through 1.5772 +// GetPromotedPoint() 1.5773 +// 1.5774 +nsresult 1.5775 +nsHTMLEditRules::GetPromotedRanges(nsISelection *inSelection, 1.5776 + nsCOMArray<nsIDOMRange> &outArrayOfRanges, 1.5777 + EditAction inOperationType) 1.5778 +{ 1.5779 + NS_ENSURE_TRUE(inSelection, NS_ERROR_NULL_POINTER); 1.5780 + 1.5781 + int32_t rangeCount; 1.5782 + nsresult res = inSelection->GetRangeCount(&rangeCount); 1.5783 + NS_ENSURE_SUCCESS(res, res); 1.5784 + 1.5785 + int32_t i; 1.5786 + nsCOMPtr<nsIDOMRange> selectionRange; 1.5787 + nsCOMPtr<nsIDOMRange> opRange; 1.5788 + 1.5789 + for (i = 0; i < rangeCount; i++) 1.5790 + { 1.5791 + res = inSelection->GetRangeAt(i, getter_AddRefs(selectionRange)); 1.5792 + NS_ENSURE_SUCCESS(res, res); 1.5793 + 1.5794 + // clone range so we don't muck with actual selection ranges 1.5795 + res = selectionRange->CloneRange(getter_AddRefs(opRange)); 1.5796 + NS_ENSURE_SUCCESS(res, res); 1.5797 + 1.5798 + // make a new adjusted range to represent the appropriate block content. 1.5799 + // The basic idea is to push out the range endpoints 1.5800 + // to truly enclose the blocks that we will affect. 1.5801 + // This call alters opRange. 1.5802 + res = PromoteRange(opRange, inOperationType); 1.5803 + NS_ENSURE_SUCCESS(res, res); 1.5804 + 1.5805 + // stuff new opRange into array 1.5806 + outArrayOfRanges.AppendObject(opRange); 1.5807 + } 1.5808 + return res; 1.5809 +} 1.5810 + 1.5811 + 1.5812 +/////////////////////////////////////////////////////////////////////////// 1.5813 +// PromoteRange: expand a range to include any parents for which all 1.5814 +// editable children are already in range. 1.5815 +// 1.5816 +nsresult 1.5817 +nsHTMLEditRules::PromoteRange(nsIDOMRange *inRange, 1.5818 + EditAction inOperationType) 1.5819 +{ 1.5820 + NS_ENSURE_TRUE(inRange, NS_ERROR_NULL_POINTER); 1.5821 + nsresult res; 1.5822 + nsCOMPtr<nsIDOMNode> startNode, endNode; 1.5823 + int32_t startOffset, endOffset; 1.5824 + 1.5825 + res = inRange->GetStartContainer(getter_AddRefs(startNode)); 1.5826 + NS_ENSURE_SUCCESS(res, res); 1.5827 + res = inRange->GetStartOffset(&startOffset); 1.5828 + NS_ENSURE_SUCCESS(res, res); 1.5829 + res = inRange->GetEndContainer(getter_AddRefs(endNode)); 1.5830 + NS_ENSURE_SUCCESS(res, res); 1.5831 + res = inRange->GetEndOffset(&endOffset); 1.5832 + NS_ENSURE_SUCCESS(res, res); 1.5833 + 1.5834 + // MOOSE major hack: 1.5835 + // GetPromotedPoint doesn't really do the right thing for collapsed ranges 1.5836 + // inside block elements that contain nothing but a solo <br>. It's easier 1.5837 + // to put a workaround here than to revamp GetPromotedPoint. :-( 1.5838 + if ( (startNode == endNode) && (startOffset == endOffset)) 1.5839 + { 1.5840 + nsCOMPtr<nsIDOMNode> block; 1.5841 + if (IsBlockNode(startNode)) { 1.5842 + block = startNode; 1.5843 + } else { 1.5844 + NS_ENSURE_STATE(mHTMLEditor); 1.5845 + block = mHTMLEditor->GetBlockNodeParent(startNode); 1.5846 + } 1.5847 + if (block) 1.5848 + { 1.5849 + bool bIsEmptyNode = false; 1.5850 + // check for the editing host 1.5851 + NS_ENSURE_STATE(mHTMLEditor); 1.5852 + nsIContent *rootContent = mHTMLEditor->GetActiveEditingHost(); 1.5853 + nsCOMPtr<nsINode> rootNode = do_QueryInterface(rootContent); 1.5854 + nsCOMPtr<nsINode> blockNode = do_QueryInterface(block); 1.5855 + NS_ENSURE_TRUE(rootNode && blockNode, NS_ERROR_UNEXPECTED); 1.5856 + // Make sure we don't go higher than our root element in the content tree 1.5857 + if (!nsContentUtils::ContentIsDescendantOf(rootNode, blockNode)) 1.5858 + { 1.5859 + NS_ENSURE_STATE(mHTMLEditor); 1.5860 + res = mHTMLEditor->IsEmptyNode(block, &bIsEmptyNode, true, false); 1.5861 + } 1.5862 + if (bIsEmptyNode) 1.5863 + { 1.5864 + uint32_t numChildren; 1.5865 + nsEditor::GetLengthOfDOMNode(block, numChildren); 1.5866 + startNode = block; 1.5867 + endNode = block; 1.5868 + startOffset = 0; 1.5869 + endOffset = numChildren; 1.5870 + } 1.5871 + } 1.5872 + } 1.5873 + 1.5874 + // make a new adjusted range to represent the appropriate block content. 1.5875 + // this is tricky. the basic idea is to push out the range endpoints 1.5876 + // to truly enclose the blocks that we will affect 1.5877 + 1.5878 + nsCOMPtr<nsIDOMNode> opStartNode; 1.5879 + nsCOMPtr<nsIDOMNode> opEndNode; 1.5880 + int32_t opStartOffset, opEndOffset; 1.5881 + nsCOMPtr<nsIDOMRange> opRange; 1.5882 + 1.5883 + GetPromotedPoint(kStart, startNode, startOffset, inOperationType, 1.5884 + address_of(opStartNode), &opStartOffset); 1.5885 + GetPromotedPoint(kEnd, endNode, endOffset, inOperationType, 1.5886 + address_of(opEndNode), &opEndOffset); 1.5887 + 1.5888 + // Make sure that the new range ends up to be in the editable section. 1.5889 + NS_ENSURE_STATE(mHTMLEditor); 1.5890 + if (!mHTMLEditor->IsDescendantOfEditorRoot(nsEditor::GetNodeAtRangeOffsetPoint(opStartNode, opStartOffset)) || 1.5891 + !mHTMLEditor || // Check again, since it may have gone away 1.5892 + !mHTMLEditor->IsDescendantOfEditorRoot(nsEditor::GetNodeAtRangeOffsetPoint(opEndNode, opEndOffset - 1))) { 1.5893 + NS_ENSURE_STATE(mHTMLEditor); 1.5894 + return NS_OK; 1.5895 + } 1.5896 + 1.5897 + res = inRange->SetStart(opStartNode, opStartOffset); 1.5898 + NS_ENSURE_SUCCESS(res, res); 1.5899 + res = inRange->SetEnd(opEndNode, opEndOffset); 1.5900 + return res; 1.5901 +} 1.5902 + 1.5903 +class nsUniqueFunctor : public nsBoolDomIterFunctor 1.5904 +{ 1.5905 +public: 1.5906 + nsUniqueFunctor(nsCOMArray<nsIDOMNode> &aArray) : mArray(aArray) 1.5907 + { 1.5908 + } 1.5909 + virtual bool operator()(nsIDOMNode* aNode) // used to build list of all nodes iterator covers 1.5910 + { 1.5911 + return mArray.IndexOf(aNode) < 0; 1.5912 + } 1.5913 + 1.5914 +private: 1.5915 + nsCOMArray<nsIDOMNode> &mArray; 1.5916 +}; 1.5917 + 1.5918 +/////////////////////////////////////////////////////////////////////////// 1.5919 +// GetNodesForOperation: run through the ranges in the array and construct 1.5920 +// a new array of nodes to be acted on. 1.5921 +// 1.5922 +nsresult 1.5923 +nsHTMLEditRules::GetNodesForOperation(nsCOMArray<nsIDOMRange>& inArrayOfRanges, 1.5924 + nsCOMArray<nsIDOMNode>& outArrayOfNodes, 1.5925 + EditAction inOperationType, 1.5926 + bool aDontTouchContent) 1.5927 +{ 1.5928 + int32_t rangeCount = inArrayOfRanges.Count(); 1.5929 + 1.5930 + int32_t i; 1.5931 + nsCOMPtr<nsIDOMRange> opRange; 1.5932 + 1.5933 + nsresult res = NS_OK; 1.5934 + 1.5935 + // bust up any inlines that cross our range endpoints, 1.5936 + // but only if we are allowed to touch content. 1.5937 + 1.5938 + if (!aDontTouchContent) 1.5939 + { 1.5940 + nsTArray<nsRefPtr<nsRangeStore> > rangeItemArray; 1.5941 + if (!rangeItemArray.AppendElements(rangeCount)) { 1.5942 + return NS_ERROR_OUT_OF_MEMORY; 1.5943 + } 1.5944 + 1.5945 + NS_ASSERTION(static_cast<uint32_t>(rangeCount) == rangeItemArray.Length(), 1.5946 + "How did that happen?"); 1.5947 + 1.5948 + // first register ranges for special editor gravity 1.5949 + for (i = 0; i < rangeCount; i++) 1.5950 + { 1.5951 + opRange = inArrayOfRanges[0]; 1.5952 + rangeItemArray[i] = new nsRangeStore(); 1.5953 + rangeItemArray[i]->StoreRange(opRange); 1.5954 + NS_ENSURE_STATE(mHTMLEditor); 1.5955 + mHTMLEditor->mRangeUpdater.RegisterRangeItem(rangeItemArray[i]); 1.5956 + inArrayOfRanges.RemoveObjectAt(0); 1.5957 + } 1.5958 + // now bust up inlines. Safe to start at rangeCount-1, since we 1.5959 + // asserted we have enough items above. 1.5960 + for (i = rangeCount-1; i >= 0 && NS_SUCCEEDED(res); i--) 1.5961 + { 1.5962 + res = BustUpInlinesAtRangeEndpoints(*rangeItemArray[i]); 1.5963 + } 1.5964 + // then unregister the ranges 1.5965 + for (i = 0; i < rangeCount; i++) 1.5966 + { 1.5967 + nsRangeStore* item = rangeItemArray[i]; 1.5968 + NS_ENSURE_STATE(mHTMLEditor); 1.5969 + mHTMLEditor->mRangeUpdater.DropRangeItem(item); 1.5970 + nsRefPtr<nsRange> range; 1.5971 + nsresult res2 = item->GetRange(getter_AddRefs(range)); 1.5972 + opRange = range; 1.5973 + if (NS_FAILED(res2) && NS_SUCCEEDED(res)) { 1.5974 + // Remember the failure, but keep going so we make sure to unregister 1.5975 + // all our range items. 1.5976 + res = res2; 1.5977 + } 1.5978 + inArrayOfRanges.AppendObject(opRange); 1.5979 + } 1.5980 + NS_ENSURE_SUCCESS(res, res); 1.5981 + } 1.5982 + // gather up a list of all the nodes 1.5983 + for (i = 0; i < rangeCount; i++) 1.5984 + { 1.5985 + opRange = inArrayOfRanges[i]; 1.5986 + 1.5987 + nsDOMSubtreeIterator iter; 1.5988 + res = iter.Init(opRange); 1.5989 + NS_ENSURE_SUCCESS(res, res); 1.5990 + if (outArrayOfNodes.Count() == 0) { 1.5991 + nsTrivialFunctor functor; 1.5992 + res = iter.AppendList(functor, outArrayOfNodes); 1.5993 + NS_ENSURE_SUCCESS(res, res); 1.5994 + } 1.5995 + else { 1.5996 + // We don't want duplicates in outArrayOfNodes, so we use an 1.5997 + // iterator/functor that only return nodes that are not already in 1.5998 + // outArrayOfNodes. 1.5999 + nsCOMArray<nsIDOMNode> nodes; 1.6000 + nsUniqueFunctor functor(outArrayOfNodes); 1.6001 + res = iter.AppendList(functor, nodes); 1.6002 + NS_ENSURE_SUCCESS(res, res); 1.6003 + if (!outArrayOfNodes.AppendObjects(nodes)) 1.6004 + return NS_ERROR_OUT_OF_MEMORY; 1.6005 + } 1.6006 + } 1.6007 + 1.6008 + // certain operations should not act on li's and td's, but rather inside 1.6009 + // them. alter the list as needed 1.6010 + if (inOperationType == EditAction::makeBasicBlock) { 1.6011 + int32_t listCount = outArrayOfNodes.Count(); 1.6012 + for (i=listCount-1; i>=0; i--) 1.6013 + { 1.6014 + nsCOMPtr<nsIDOMNode> node = outArrayOfNodes[i]; 1.6015 + if (nsHTMLEditUtils::IsListItem(node)) 1.6016 + { 1.6017 + int32_t j=i; 1.6018 + outArrayOfNodes.RemoveObjectAt(i); 1.6019 + res = GetInnerContent(node, outArrayOfNodes, &j); 1.6020 + NS_ENSURE_SUCCESS(res, res); 1.6021 + } 1.6022 + } 1.6023 + } 1.6024 + // indent/outdent already do something special for list items, but 1.6025 + // we still need to make sure we don't act on table elements 1.6026 + else if (inOperationType == EditAction::outdent || 1.6027 + inOperationType == EditAction::indent || 1.6028 + inOperationType == EditAction::setAbsolutePosition) { 1.6029 + int32_t listCount = outArrayOfNodes.Count(); 1.6030 + for (i=listCount-1; i>=0; i--) 1.6031 + { 1.6032 + nsCOMPtr<nsIDOMNode> node = outArrayOfNodes[i]; 1.6033 + if (nsHTMLEditUtils::IsTableElementButNotTable(node)) 1.6034 + { 1.6035 + int32_t j=i; 1.6036 + outArrayOfNodes.RemoveObjectAt(i); 1.6037 + res = GetInnerContent(node, outArrayOfNodes, &j); 1.6038 + NS_ENSURE_SUCCESS(res, res); 1.6039 + } 1.6040 + } 1.6041 + } 1.6042 + // outdent should look inside of divs. 1.6043 + if (inOperationType == EditAction::outdent && 1.6044 + (!mHTMLEditor || !mHTMLEditor->IsCSSEnabled())) { 1.6045 + NS_ENSURE_STATE(mHTMLEditor); 1.6046 + int32_t listCount = outArrayOfNodes.Count(); 1.6047 + for (i=listCount-1; i>=0; i--) 1.6048 + { 1.6049 + nsCOMPtr<nsIDOMNode> node = outArrayOfNodes[i]; 1.6050 + if (nsHTMLEditUtils::IsDiv(node)) 1.6051 + { 1.6052 + int32_t j=i; 1.6053 + outArrayOfNodes.RemoveObjectAt(i); 1.6054 + res = GetInnerContent(node, outArrayOfNodes, &j, false, false); 1.6055 + NS_ENSURE_SUCCESS(res, res); 1.6056 + } 1.6057 + } 1.6058 + } 1.6059 + 1.6060 + 1.6061 + // post process the list to break up inline containers that contain br's. 1.6062 + // but only for operations that might care, like making lists or para's... 1.6063 + if (inOperationType == EditAction::makeBasicBlock || 1.6064 + inOperationType == EditAction::makeList || 1.6065 + inOperationType == EditAction::align || 1.6066 + inOperationType == EditAction::setAbsolutePosition || 1.6067 + inOperationType == EditAction::indent || 1.6068 + inOperationType == EditAction::outdent) { 1.6069 + int32_t listCount = outArrayOfNodes.Count(); 1.6070 + for (i=listCount-1; i>=0; i--) 1.6071 + { 1.6072 + nsCOMPtr<nsIDOMNode> node = outArrayOfNodes[i]; 1.6073 + if (!aDontTouchContent && IsInlineNode(node) && 1.6074 + (!mHTMLEditor || mHTMLEditor->IsContainer(node)) && 1.6075 + (!mHTMLEditor || !mHTMLEditor->IsTextNode(node))) 1.6076 + { 1.6077 + NS_ENSURE_STATE(mHTMLEditor); 1.6078 + nsCOMArray<nsIDOMNode> arrayOfInlines; 1.6079 + res = BustUpInlinesAtBRs(node, arrayOfInlines); 1.6080 + NS_ENSURE_SUCCESS(res, res); 1.6081 + // put these nodes in outArrayOfNodes, replacing the current node 1.6082 + outArrayOfNodes.RemoveObjectAt(i); 1.6083 + outArrayOfNodes.InsertObjectsAt(arrayOfInlines, i); 1.6084 + } 1.6085 + } 1.6086 + } 1.6087 + return res; 1.6088 +} 1.6089 + 1.6090 + 1.6091 + 1.6092 +/////////////////////////////////////////////////////////////////////////// 1.6093 +// GetChildNodesForOperation: 1.6094 +// 1.6095 +nsresult 1.6096 +nsHTMLEditRules::GetChildNodesForOperation(nsIDOMNode *inNode, 1.6097 + nsCOMArray<nsIDOMNode>& outArrayOfNodes) 1.6098 +{ 1.6099 + nsCOMPtr<nsINode> node = do_QueryInterface(inNode); 1.6100 + NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER); 1.6101 + 1.6102 + for (nsIContent* child = node->GetFirstChild(); 1.6103 + child; 1.6104 + child = child->GetNextSibling()) { 1.6105 + nsIDOMNode* childNode = child->AsDOMNode(); 1.6106 + if (!outArrayOfNodes.AppendObject(childNode)) { 1.6107 + return NS_ERROR_FAILURE; 1.6108 + } 1.6109 + } 1.6110 + return NS_OK; 1.6111 +} 1.6112 + 1.6113 + 1.6114 + 1.6115 +/////////////////////////////////////////////////////////////////////////// 1.6116 +// GetListActionNodes: 1.6117 +// 1.6118 +nsresult 1.6119 +nsHTMLEditRules::GetListActionNodes(nsCOMArray<nsIDOMNode> &outArrayOfNodes, 1.6120 + bool aEntireList, 1.6121 + bool aDontTouchContent) 1.6122 +{ 1.6123 + nsresult res = NS_OK; 1.6124 + 1.6125 + nsCOMPtr<nsISelection>selection; 1.6126 + NS_ENSURE_STATE(mHTMLEditor); 1.6127 + res = mHTMLEditor->GetSelection(getter_AddRefs(selection)); 1.6128 + NS_ENSURE_SUCCESS(res, res); 1.6129 + Selection* sel = static_cast<Selection*>(selection.get()); 1.6130 + NS_ENSURE_TRUE(sel, NS_ERROR_FAILURE); 1.6131 + // added this in so that ui code can ask to change an entire list, even if selection 1.6132 + // is only in part of it. used by list item dialog. 1.6133 + if (aEntireList) 1.6134 + { 1.6135 + uint32_t rangeCount = sel->GetRangeCount(); 1.6136 + for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) { 1.6137 + nsRefPtr<nsRange> range = sel->GetRangeAt(rangeIdx); 1.6138 + nsCOMPtr<nsIDOMNode> commonParent, parent, tmp; 1.6139 + range->GetCommonAncestorContainer(getter_AddRefs(commonParent)); 1.6140 + if (commonParent) 1.6141 + { 1.6142 + parent = commonParent; 1.6143 + while (parent) 1.6144 + { 1.6145 + if (nsHTMLEditUtils::IsList(parent)) 1.6146 + { 1.6147 + outArrayOfNodes.AppendObject(parent); 1.6148 + break; 1.6149 + } 1.6150 + parent->GetParentNode(getter_AddRefs(tmp)); 1.6151 + parent = tmp; 1.6152 + } 1.6153 + } 1.6154 + } 1.6155 + // if we didn't find any nodes this way, then try the normal way. perhaps the 1.6156 + // selection spans multiple lists but with no common list parent. 1.6157 + if (outArrayOfNodes.Count()) return NS_OK; 1.6158 + } 1.6159 + 1.6160 + { 1.6161 + // We don't like other people messing with our selection! 1.6162 + NS_ENSURE_STATE(mHTMLEditor); 1.6163 + nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor); 1.6164 + 1.6165 + // contruct a list of nodes to act on. 1.6166 + res = GetNodesFromSelection(selection, EditAction::makeList, 1.6167 + outArrayOfNodes, aDontTouchContent); 1.6168 + NS_ENSURE_SUCCESS(res, res); 1.6169 + } 1.6170 + 1.6171 + // pre process our list of nodes... 1.6172 + int32_t listCount = outArrayOfNodes.Count(); 1.6173 + int32_t i; 1.6174 + for (i=listCount-1; i>=0; i--) 1.6175 + { 1.6176 + nsCOMPtr<nsIDOMNode> testNode = outArrayOfNodes[i]; 1.6177 + 1.6178 + // Remove all non-editable nodes. Leave them be. 1.6179 + NS_ENSURE_STATE(mHTMLEditor); 1.6180 + if (!mHTMLEditor->IsEditable(testNode)) 1.6181 + { 1.6182 + outArrayOfNodes.RemoveObjectAt(i); 1.6183 + } 1.6184 + 1.6185 + // scan for table elements and divs. If we find table elements other than table, 1.6186 + // replace it with a list of any editable non-table content. 1.6187 + if (nsHTMLEditUtils::IsTableElementButNotTable(testNode)) 1.6188 + { 1.6189 + int32_t j=i; 1.6190 + outArrayOfNodes.RemoveObjectAt(i); 1.6191 + res = GetInnerContent(testNode, outArrayOfNodes, &j, false); 1.6192 + NS_ENSURE_SUCCESS(res, res); 1.6193 + } 1.6194 + } 1.6195 + 1.6196 + // if there is only one node in the array, and it is a list, div, or blockquote, 1.6197 + // then look inside of it until we find inner list or content. 1.6198 + res = LookInsideDivBQandList(outArrayOfNodes); 1.6199 + return res; 1.6200 +} 1.6201 + 1.6202 + 1.6203 +/////////////////////////////////////////////////////////////////////////// 1.6204 +// LookInsideDivBQandList: 1.6205 +// 1.6206 +nsresult 1.6207 +nsHTMLEditRules::LookInsideDivBQandList(nsCOMArray<nsIDOMNode>& aNodeArray) 1.6208 +{ 1.6209 + // if there is only one node in the array, and it is a list, div, or blockquote, 1.6210 + // then look inside of it until we find inner list or content. 1.6211 + int32_t listCount = aNodeArray.Count(); 1.6212 + if (listCount != 1) { 1.6213 + return NS_OK; 1.6214 + } 1.6215 + 1.6216 + nsCOMPtr<nsINode> curNode = do_QueryInterface(aNodeArray[0]); 1.6217 + NS_ENSURE_STATE(curNode); 1.6218 + 1.6219 + while (curNode->IsElement() && 1.6220 + (curNode->AsElement()->IsHTML(nsGkAtoms::div) || 1.6221 + nsHTMLEditUtils::IsList(curNode) || 1.6222 + curNode->AsElement()->IsHTML(nsGkAtoms::blockquote))) { 1.6223 + // dive as long as there is only one child, and it is a list, div, blockquote 1.6224 + NS_ENSURE_STATE(mHTMLEditor); 1.6225 + uint32_t numChildren = mHTMLEditor->CountEditableChildren(curNode); 1.6226 + if (numChildren != 1) { 1.6227 + break; 1.6228 + } 1.6229 + 1.6230 + // keep diving 1.6231 + // XXX One would expect to dive into the one editable node. 1.6232 + nsIContent* tmp = curNode->GetFirstChild(); 1.6233 + if (!tmp->IsElement()) { 1.6234 + break; 1.6235 + } 1.6236 + 1.6237 + dom::Element* element = tmp->AsElement(); 1.6238 + if (!element->IsHTML(nsGkAtoms::div) && 1.6239 + !nsHTMLEditUtils::IsList(element) && 1.6240 + !element->IsHTML(nsGkAtoms::blockquote)) { 1.6241 + break; 1.6242 + } 1.6243 + 1.6244 + // check editablility XXX floppy moose 1.6245 + curNode = tmp; 1.6246 + } 1.6247 + 1.6248 + // we've found innermost list/blockquote/div: 1.6249 + // replace the one node in the array with these nodes 1.6250 + aNodeArray.RemoveObjectAt(0); 1.6251 + if (curNode->IsElement() && 1.6252 + (curNode->AsElement()->IsHTML(nsGkAtoms::div) || 1.6253 + curNode->AsElement()->IsHTML(nsGkAtoms::blockquote))) { 1.6254 + int32_t j = 0; 1.6255 + return GetInnerContent(curNode->AsDOMNode(), aNodeArray, &j, false, false); 1.6256 + } 1.6257 + 1.6258 + aNodeArray.AppendObject(curNode->AsDOMNode()); 1.6259 + return NS_OK; 1.6260 +} 1.6261 + 1.6262 + 1.6263 +/////////////////////////////////////////////////////////////////////////// 1.6264 +// GetDefinitionListItemTypes: 1.6265 +// 1.6266 +void 1.6267 +nsHTMLEditRules::GetDefinitionListItemTypes(dom::Element* aElement, bool* aDT, bool* aDD) 1.6268 +{ 1.6269 + MOZ_ASSERT(aElement); 1.6270 + MOZ_ASSERT(aElement->IsHTML(nsGkAtoms::dl)); 1.6271 + MOZ_ASSERT(aDT); 1.6272 + MOZ_ASSERT(aDD); 1.6273 + 1.6274 + *aDT = *aDD = false; 1.6275 + for (nsIContent* child = aElement->GetFirstChild(); 1.6276 + child; 1.6277 + child = child->GetNextSibling()) { 1.6278 + if (child->IsHTML(nsGkAtoms::dt)) { 1.6279 + *aDT = true; 1.6280 + } else if (child->IsHTML(nsGkAtoms::dd)) { 1.6281 + *aDD = true; 1.6282 + } 1.6283 + } 1.6284 +} 1.6285 + 1.6286 +/////////////////////////////////////////////////////////////////////////// 1.6287 +// GetParagraphFormatNodes: 1.6288 +// 1.6289 +nsresult 1.6290 +nsHTMLEditRules::GetParagraphFormatNodes(nsCOMArray<nsIDOMNode>& outArrayOfNodes, 1.6291 + bool aDontTouchContent) 1.6292 +{ 1.6293 + nsCOMPtr<nsISelection>selection; 1.6294 + NS_ENSURE_STATE(mHTMLEditor); 1.6295 + nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection)); 1.6296 + NS_ENSURE_SUCCESS(res, res); 1.6297 + 1.6298 + // contruct a list of nodes to act on. 1.6299 + res = GetNodesFromSelection(selection, EditAction::makeBasicBlock, 1.6300 + outArrayOfNodes, aDontTouchContent); 1.6301 + NS_ENSURE_SUCCESS(res, res); 1.6302 + 1.6303 + // pre process our list of nodes... 1.6304 + int32_t listCount = outArrayOfNodes.Count(); 1.6305 + int32_t i; 1.6306 + for (i=listCount-1; i>=0; i--) 1.6307 + { 1.6308 + nsCOMPtr<nsIDOMNode> testNode = outArrayOfNodes[i]; 1.6309 + 1.6310 + // Remove all non-editable nodes. Leave them be. 1.6311 + NS_ENSURE_STATE(mHTMLEditor); 1.6312 + if (!mHTMLEditor->IsEditable(testNode)) 1.6313 + { 1.6314 + outArrayOfNodes.RemoveObjectAt(i); 1.6315 + } 1.6316 + 1.6317 + // scan for table elements. If we find table elements other than table, 1.6318 + // replace it with a list of any editable non-table content. Ditto for list elements. 1.6319 + if (nsHTMLEditUtils::IsTableElement(testNode) || 1.6320 + nsHTMLEditUtils::IsList(testNode) || 1.6321 + nsHTMLEditUtils::IsListItem(testNode) ) 1.6322 + { 1.6323 + int32_t j=i; 1.6324 + outArrayOfNodes.RemoveObjectAt(i); 1.6325 + res = GetInnerContent(testNode, outArrayOfNodes, &j); 1.6326 + NS_ENSURE_SUCCESS(res, res); 1.6327 + } 1.6328 + } 1.6329 + return res; 1.6330 +} 1.6331 + 1.6332 + 1.6333 +/////////////////////////////////////////////////////////////////////////// 1.6334 +// BustUpInlinesAtRangeEndpoints: 1.6335 +// 1.6336 +nsresult 1.6337 +nsHTMLEditRules::BustUpInlinesAtRangeEndpoints(nsRangeStore &item) 1.6338 +{ 1.6339 + nsresult res = NS_OK; 1.6340 + bool isCollapsed = ((item.startNode == item.endNode) && (item.startOffset == item.endOffset)); 1.6341 + 1.6342 + nsCOMPtr<nsIDOMNode> endInline = GetHighestInlineParent(item.endNode); 1.6343 + 1.6344 + // if we have inline parents above range endpoints, split them 1.6345 + if (endInline && !isCollapsed) 1.6346 + { 1.6347 + nsCOMPtr<nsIDOMNode> resultEndNode; 1.6348 + int32_t resultEndOffset; 1.6349 + endInline->GetParentNode(getter_AddRefs(resultEndNode)); 1.6350 + NS_ENSURE_STATE(mHTMLEditor); 1.6351 + res = mHTMLEditor->SplitNodeDeep(endInline, item.endNode, item.endOffset, 1.6352 + &resultEndOffset, true); 1.6353 + NS_ENSURE_SUCCESS(res, res); 1.6354 + // reset range 1.6355 + item.endNode = resultEndNode; item.endOffset = resultEndOffset; 1.6356 + } 1.6357 + 1.6358 + nsCOMPtr<nsIDOMNode> startInline = GetHighestInlineParent(item.startNode); 1.6359 + 1.6360 + if (startInline) 1.6361 + { 1.6362 + nsCOMPtr<nsIDOMNode> resultStartNode; 1.6363 + int32_t resultStartOffset; 1.6364 + startInline->GetParentNode(getter_AddRefs(resultStartNode)); 1.6365 + NS_ENSURE_STATE(mHTMLEditor); 1.6366 + res = mHTMLEditor->SplitNodeDeep(startInline, item.startNode, item.startOffset, 1.6367 + &resultStartOffset, true); 1.6368 + NS_ENSURE_SUCCESS(res, res); 1.6369 + // reset range 1.6370 + item.startNode = resultStartNode; item.startOffset = resultStartOffset; 1.6371 + } 1.6372 + 1.6373 + return res; 1.6374 +} 1.6375 + 1.6376 + 1.6377 + 1.6378 +/////////////////////////////////////////////////////////////////////////// 1.6379 +// BustUpInlinesAtBRs: 1.6380 +// 1.6381 +nsresult 1.6382 +nsHTMLEditRules::BustUpInlinesAtBRs(nsIDOMNode *inNode, 1.6383 + nsCOMArray<nsIDOMNode>& outArrayOfNodes) 1.6384 +{ 1.6385 + NS_ENSURE_TRUE(inNode, NS_ERROR_NULL_POINTER); 1.6386 + 1.6387 + // first step is to build up a list of all the break nodes inside 1.6388 + // the inline container. 1.6389 + nsCOMArray<nsIDOMNode> arrayOfBreaks; 1.6390 + nsBRNodeFunctor functor; 1.6391 + nsDOMIterator iter; 1.6392 + nsresult res = iter.Init(inNode); 1.6393 + NS_ENSURE_SUCCESS(res, res); 1.6394 + res = iter.AppendList(functor, arrayOfBreaks); 1.6395 + NS_ENSURE_SUCCESS(res, res); 1.6396 + 1.6397 + // if there aren't any breaks, just put inNode itself in the array 1.6398 + int32_t listCount = arrayOfBreaks.Count(); 1.6399 + if (!listCount) 1.6400 + { 1.6401 + if (!outArrayOfNodes.AppendObject(inNode)) 1.6402 + return NS_ERROR_FAILURE; 1.6403 + } 1.6404 + else 1.6405 + { 1.6406 + // else we need to bust up inNode along all the breaks 1.6407 + nsCOMPtr<nsIDOMNode> breakNode; 1.6408 + nsCOMPtr<nsIDOMNode> inlineParentNode; 1.6409 + nsCOMPtr<nsIDOMNode> leftNode; 1.6410 + nsCOMPtr<nsIDOMNode> rightNode; 1.6411 + nsCOMPtr<nsIDOMNode> splitDeepNode = inNode; 1.6412 + nsCOMPtr<nsIDOMNode> splitParentNode; 1.6413 + int32_t splitOffset, resultOffset, i; 1.6414 + inNode->GetParentNode(getter_AddRefs(inlineParentNode)); 1.6415 + 1.6416 + for (i=0; i< listCount; i++) 1.6417 + { 1.6418 + breakNode = arrayOfBreaks[i]; 1.6419 + NS_ENSURE_TRUE(breakNode, NS_ERROR_NULL_POINTER); 1.6420 + NS_ENSURE_TRUE(splitDeepNode, NS_ERROR_NULL_POINTER); 1.6421 + splitParentNode = nsEditor::GetNodeLocation(breakNode, &splitOffset); 1.6422 + NS_ENSURE_STATE(mHTMLEditor); 1.6423 + res = mHTMLEditor->SplitNodeDeep(splitDeepNode, splitParentNode, splitOffset, 1.6424 + &resultOffset, false, address_of(leftNode), address_of(rightNode)); 1.6425 + NS_ENSURE_SUCCESS(res, res); 1.6426 + // put left node in node list 1.6427 + if (leftNode) 1.6428 + { 1.6429 + // might not be a left node. a break might have been at the very 1.6430 + // beginning of inline container, in which case splitnodedeep 1.6431 + // would not actually split anything 1.6432 + if (!outArrayOfNodes.AppendObject(leftNode)) 1.6433 + return NS_ERROR_FAILURE; 1.6434 + } 1.6435 + // move break outside of container and also put in node list 1.6436 + NS_ENSURE_STATE(mHTMLEditor); 1.6437 + res = mHTMLEditor->MoveNode(breakNode, inlineParentNode, resultOffset); 1.6438 + NS_ENSURE_SUCCESS(res, res); 1.6439 + if (!outArrayOfNodes.AppendObject(breakNode)) 1.6440 + return NS_ERROR_FAILURE; 1.6441 + // now rightNode becomes the new node to split 1.6442 + splitDeepNode = rightNode; 1.6443 + } 1.6444 + // now tack on remaining rightNode, if any, to the list 1.6445 + if (rightNode) 1.6446 + { 1.6447 + if (!outArrayOfNodes.AppendObject(rightNode)) 1.6448 + return NS_ERROR_FAILURE; 1.6449 + } 1.6450 + } 1.6451 + return res; 1.6452 +} 1.6453 + 1.6454 + 1.6455 +nsCOMPtr<nsIDOMNode> 1.6456 +nsHTMLEditRules::GetHighestInlineParent(nsIDOMNode* aNode) 1.6457 +{ 1.6458 + NS_ENSURE_TRUE(aNode, nullptr); 1.6459 + if (IsBlockNode(aNode)) return nullptr; 1.6460 + nsCOMPtr<nsIDOMNode> inlineNode, node=aNode; 1.6461 + 1.6462 + while (node && IsInlineNode(node)) 1.6463 + { 1.6464 + inlineNode = node; 1.6465 + inlineNode->GetParentNode(getter_AddRefs(node)); 1.6466 + } 1.6467 + return inlineNode; 1.6468 +} 1.6469 + 1.6470 + 1.6471 +/////////////////////////////////////////////////////////////////////////// 1.6472 +// GetNodesFromPoint: given a particular operation, construct a list 1.6473 +// of nodes from a point that will be operated on. 1.6474 +// 1.6475 +nsresult 1.6476 +nsHTMLEditRules::GetNodesFromPoint(::DOMPoint point, 1.6477 + EditAction operation, 1.6478 + nsCOMArray<nsIDOMNode> &arrayOfNodes, 1.6479 + bool dontTouchContent) 1.6480 +{ 1.6481 + nsresult res; 1.6482 + 1.6483 + // get our point 1.6484 + nsCOMPtr<nsIDOMNode> node; 1.6485 + int32_t offset; 1.6486 + point.GetPoint(node, offset); 1.6487 + 1.6488 + // use it to make a range 1.6489 + nsCOMPtr<nsINode> nativeNode = do_QueryInterface(node); 1.6490 + NS_ENSURE_STATE(nativeNode); 1.6491 + nsRefPtr<nsRange> range = new nsRange(nativeNode); 1.6492 + res = range->SetStart(node, offset); 1.6493 + NS_ENSURE_SUCCESS(res, res); 1.6494 + /* SetStart() will also set the end for this new range 1.6495 + res = range->SetEnd(node, offset); 1.6496 + NS_ENSURE_SUCCESS(res, res); */ 1.6497 + 1.6498 + // expand the range to include adjacent inlines 1.6499 + res = PromoteRange(range, operation); 1.6500 + NS_ENSURE_SUCCESS(res, res); 1.6501 + 1.6502 + // make array of ranges 1.6503 + nsCOMArray<nsIDOMRange> arrayOfRanges; 1.6504 + 1.6505 + // stuff new opRange into array 1.6506 + arrayOfRanges.AppendObject(range); 1.6507 + 1.6508 + // use these ranges to contruct a list of nodes to act on. 1.6509 + res = GetNodesForOperation(arrayOfRanges, arrayOfNodes, operation, dontTouchContent); 1.6510 + return res; 1.6511 +} 1.6512 + 1.6513 + 1.6514 +/////////////////////////////////////////////////////////////////////////// 1.6515 +// GetNodesFromSelection: given a particular operation, construct a list 1.6516 +// of nodes from the selection that will be operated on. 1.6517 +// 1.6518 +nsresult 1.6519 +nsHTMLEditRules::GetNodesFromSelection(nsISelection *selection, 1.6520 + EditAction operation, 1.6521 + nsCOMArray<nsIDOMNode>& arrayOfNodes, 1.6522 + bool dontTouchContent) 1.6523 +{ 1.6524 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.6525 + nsresult res; 1.6526 + 1.6527 + // promote selection ranges 1.6528 + nsCOMArray<nsIDOMRange> arrayOfRanges; 1.6529 + res = GetPromotedRanges(selection, arrayOfRanges, operation); 1.6530 + NS_ENSURE_SUCCESS(res, res); 1.6531 + 1.6532 + // use these ranges to contruct a list of nodes to act on. 1.6533 + res = GetNodesForOperation(arrayOfRanges, arrayOfNodes, operation, dontTouchContent); 1.6534 + return res; 1.6535 +} 1.6536 + 1.6537 + 1.6538 +/////////////////////////////////////////////////////////////////////////// 1.6539 +// MakeTransitionList: detect all the transitions in the array, where a 1.6540 +// transition means that adjacent nodes in the array 1.6541 +// don't have the same parent. 1.6542 +// 1.6543 +nsresult 1.6544 +nsHTMLEditRules::MakeTransitionList(nsCOMArray<nsIDOMNode>& inArrayOfNodes, 1.6545 + nsTArray<bool> &inTransitionArray) 1.6546 +{ 1.6547 + uint32_t listCount = inArrayOfNodes.Count(); 1.6548 + inTransitionArray.EnsureLengthAtLeast(listCount); 1.6549 + uint32_t i; 1.6550 + nsCOMPtr<nsIDOMNode> prevElementParent; 1.6551 + nsCOMPtr<nsIDOMNode> curElementParent; 1.6552 + 1.6553 + for (i=0; i<listCount; i++) 1.6554 + { 1.6555 + nsIDOMNode* transNode = inArrayOfNodes[i]; 1.6556 + transNode->GetParentNode(getter_AddRefs(curElementParent)); 1.6557 + if (curElementParent != prevElementParent) 1.6558 + { 1.6559 + // different parents, or separated by <br>: transition point 1.6560 + inTransitionArray[i] = true; 1.6561 + } 1.6562 + else 1.6563 + { 1.6564 + // same parents: these nodes grew up together 1.6565 + inTransitionArray[i] = false; 1.6566 + } 1.6567 + prevElementParent = curElementParent; 1.6568 + } 1.6569 + return NS_OK; 1.6570 +} 1.6571 + 1.6572 + 1.6573 + 1.6574 +/******************************************************** 1.6575 + * main implementation methods 1.6576 + ********************************************************/ 1.6577 + 1.6578 +/////////////////////////////////////////////////////////////////////////// 1.6579 +// IsInListItem: if aNode is the descendant of a listitem, return that li. 1.6580 +// But table element boundaries are stoppers on the search. 1.6581 +// Also stops on the active editor host (contenteditable). 1.6582 +// Also test if aNode is an li itself. 1.6583 +// 1.6584 +already_AddRefed<nsIDOMNode> 1.6585 +nsHTMLEditRules::IsInListItem(nsIDOMNode* aNode) 1.6586 +{ 1.6587 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.6588 + nsCOMPtr<nsIDOMNode> retval = do_QueryInterface(IsInListItem(node)); 1.6589 + return retval.forget(); 1.6590 +} 1.6591 + 1.6592 +nsINode* 1.6593 +nsHTMLEditRules::IsInListItem(nsINode* aNode) 1.6594 +{ 1.6595 + NS_ENSURE_TRUE(aNode, nullptr); 1.6596 + if (nsHTMLEditUtils::IsListItem(aNode)) { 1.6597 + return aNode; 1.6598 + } 1.6599 + 1.6600 + nsINode* parent = aNode->GetParentNode(); 1.6601 + while (parent && mHTMLEditor && mHTMLEditor->IsDescendantOfEditorRoot(parent) && 1.6602 + !nsHTMLEditUtils::IsTableElement(parent)) { 1.6603 + if (nsHTMLEditUtils::IsListItem(parent)) { 1.6604 + return parent; 1.6605 + } 1.6606 + parent = parent->GetParentNode(); 1.6607 + } 1.6608 + return nullptr; 1.6609 +} 1.6610 + 1.6611 + 1.6612 +/////////////////////////////////////////////////////////////////////////// 1.6613 +// ReturnInHeader: do the right thing for returns pressed in headers 1.6614 +// 1.6615 +nsresult 1.6616 +nsHTMLEditRules::ReturnInHeader(nsISelection *aSelection, 1.6617 + nsIDOMNode *aHeader, 1.6618 + nsIDOMNode *aNode, 1.6619 + int32_t aOffset) 1.6620 +{ 1.6621 + NS_ENSURE_TRUE(aSelection && aHeader && aNode, NS_ERROR_NULL_POINTER); 1.6622 + 1.6623 + // remeber where the header is 1.6624 + int32_t offset; 1.6625 + nsCOMPtr<nsIDOMNode> headerParent = nsEditor::GetNodeLocation(aHeader, &offset); 1.6626 + 1.6627 + // get ws code to adjust any ws 1.6628 + nsCOMPtr<nsIDOMNode> selNode = aNode; 1.6629 + NS_ENSURE_STATE(mHTMLEditor); 1.6630 + nsresult res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor, 1.6631 + address_of(selNode), 1.6632 + &aOffset); 1.6633 + NS_ENSURE_SUCCESS(res, res); 1.6634 + 1.6635 + // split the header 1.6636 + int32_t newOffset; 1.6637 + NS_ENSURE_STATE(mHTMLEditor); 1.6638 + res = mHTMLEditor->SplitNodeDeep( aHeader, selNode, aOffset, &newOffset); 1.6639 + NS_ENSURE_SUCCESS(res, res); 1.6640 + 1.6641 + // if the leftand heading is empty, put a mozbr in it 1.6642 + nsCOMPtr<nsIDOMNode> prevItem; 1.6643 + NS_ENSURE_STATE(mHTMLEditor); 1.6644 + mHTMLEditor->GetPriorHTMLSibling(aHeader, address_of(prevItem)); 1.6645 + if (prevItem && nsHTMLEditUtils::IsHeader(prevItem)) 1.6646 + { 1.6647 + bool bIsEmptyNode; 1.6648 + NS_ENSURE_STATE(mHTMLEditor); 1.6649 + res = mHTMLEditor->IsEmptyNode(prevItem, &bIsEmptyNode); 1.6650 + NS_ENSURE_SUCCESS(res, res); 1.6651 + if (bIsEmptyNode) { 1.6652 + res = CreateMozBR(prevItem, 0); 1.6653 + NS_ENSURE_SUCCESS(res, res); 1.6654 + } 1.6655 + } 1.6656 + 1.6657 + // if the new (righthand) header node is empty, delete it 1.6658 + bool isEmpty; 1.6659 + res = IsEmptyBlock(aHeader, &isEmpty, true); 1.6660 + NS_ENSURE_SUCCESS(res, res); 1.6661 + if (isEmpty) 1.6662 + { 1.6663 + NS_ENSURE_STATE(mHTMLEditor); 1.6664 + res = mHTMLEditor->DeleteNode(aHeader); 1.6665 + NS_ENSURE_SUCCESS(res, res); 1.6666 + // layout tells the caret to blink in a weird place 1.6667 + // if we don't place a break after the header. 1.6668 + nsCOMPtr<nsIDOMNode> sibling; 1.6669 + NS_ENSURE_STATE(mHTMLEditor); 1.6670 + res = mHTMLEditor->GetNextHTMLSibling(headerParent, offset+1, address_of(sibling)); 1.6671 + NS_ENSURE_SUCCESS(res, res); 1.6672 + if (!sibling || !nsTextEditUtils::IsBreak(sibling)) 1.6673 + { 1.6674 + ClearCachedStyles(); 1.6675 + NS_ENSURE_STATE(mHTMLEditor); 1.6676 + mHTMLEditor->mTypeInState->ClearAllProps(); 1.6677 + 1.6678 + // create a paragraph 1.6679 + NS_NAMED_LITERAL_STRING(pType, "p"); 1.6680 + nsCOMPtr<nsIDOMNode> pNode; 1.6681 + NS_ENSURE_STATE(mHTMLEditor); 1.6682 + res = mHTMLEditor->CreateNode(pType, headerParent, offset+1, getter_AddRefs(pNode)); 1.6683 + NS_ENSURE_SUCCESS(res, res); 1.6684 + 1.6685 + // append a <br> to it 1.6686 + nsCOMPtr<nsIDOMNode> brNode; 1.6687 + NS_ENSURE_STATE(mHTMLEditor); 1.6688 + res = mHTMLEditor->CreateBR(pNode, 0, address_of(brNode)); 1.6689 + NS_ENSURE_SUCCESS(res, res); 1.6690 + 1.6691 + // set selection to before the break 1.6692 + res = aSelection->Collapse(pNode, 0); 1.6693 + } 1.6694 + else 1.6695 + { 1.6696 + headerParent = nsEditor::GetNodeLocation(sibling, &offset); 1.6697 + // put selection after break 1.6698 + res = aSelection->Collapse(headerParent,offset+1); 1.6699 + } 1.6700 + } 1.6701 + else 1.6702 + { 1.6703 + // put selection at front of righthand heading 1.6704 + res = aSelection->Collapse(aHeader,0); 1.6705 + } 1.6706 + return res; 1.6707 +} 1.6708 + 1.6709 +/////////////////////////////////////////////////////////////////////////// 1.6710 +// ReturnInParagraph: do the right thing for returns pressed in paragraphs 1.6711 +// 1.6712 +nsresult 1.6713 +nsHTMLEditRules::ReturnInParagraph(nsISelection* aSelection, 1.6714 + nsIDOMNode* aPara, 1.6715 + nsIDOMNode* aNode, 1.6716 + int32_t aOffset, 1.6717 + bool* aCancel, 1.6718 + bool* aHandled) 1.6719 +{ 1.6720 + if (!aSelection || !aPara || !aNode || !aCancel || !aHandled) { 1.6721 + return NS_ERROR_NULL_POINTER; 1.6722 + } 1.6723 + *aCancel = false; 1.6724 + *aHandled = false; 1.6725 + nsresult res; 1.6726 + 1.6727 + int32_t offset; 1.6728 + nsCOMPtr<nsIDOMNode> parent = nsEditor::GetNodeLocation(aNode, &offset); 1.6729 + 1.6730 + NS_ENSURE_STATE(mHTMLEditor); 1.6731 + bool doesCRCreateNewP = mHTMLEditor->GetReturnInParagraphCreatesNewParagraph(); 1.6732 + 1.6733 + bool newBRneeded = false; 1.6734 + nsCOMPtr<nsIDOMNode> sibling; 1.6735 + 1.6736 + NS_ENSURE_STATE(mHTMLEditor); 1.6737 + if (aNode == aPara && doesCRCreateNewP) { 1.6738 + // we are at the edges of the block, newBRneeded not needed! 1.6739 + sibling = aNode; 1.6740 + } else if (mHTMLEditor->IsTextNode(aNode)) { 1.6741 + nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(aNode); 1.6742 + uint32_t strLength; 1.6743 + res = textNode->GetLength(&strLength); 1.6744 + NS_ENSURE_SUCCESS(res, res); 1.6745 + 1.6746 + // at beginning of text node? 1.6747 + if (!aOffset) { 1.6748 + // is there a BR prior to it? 1.6749 + NS_ENSURE_STATE(mHTMLEditor); 1.6750 + mHTMLEditor->GetPriorHTMLSibling(aNode, address_of(sibling)); 1.6751 + if (!sibling || !mHTMLEditor || !mHTMLEditor->IsVisBreak(sibling) || 1.6752 + nsTextEditUtils::HasMozAttr(sibling)) { 1.6753 + NS_ENSURE_STATE(mHTMLEditor); 1.6754 + newBRneeded = true; 1.6755 + } 1.6756 + } else if (aOffset == (int32_t)strLength) { 1.6757 + // we're at the end of text node... 1.6758 + // is there a BR after to it? 1.6759 + NS_ENSURE_STATE(mHTMLEditor); 1.6760 + res = mHTMLEditor->GetNextHTMLSibling(aNode, address_of(sibling)); 1.6761 + if (!sibling || !mHTMLEditor || !mHTMLEditor->IsVisBreak(sibling) || 1.6762 + nsTextEditUtils::HasMozAttr(sibling)) { 1.6763 + NS_ENSURE_STATE(mHTMLEditor); 1.6764 + newBRneeded = true; 1.6765 + offset++; 1.6766 + } 1.6767 + } else { 1.6768 + if (doesCRCreateNewP) { 1.6769 + nsCOMPtr<nsIDOMNode> tmp; 1.6770 + res = mEditor->SplitNode(aNode, aOffset, getter_AddRefs(tmp)); 1.6771 + NS_ENSURE_SUCCESS(res, res); 1.6772 + aNode = tmp; 1.6773 + } 1.6774 + 1.6775 + newBRneeded = true; 1.6776 + offset++; 1.6777 + } 1.6778 + } else { 1.6779 + // not in a text node. 1.6780 + // is there a BR prior to it? 1.6781 + nsCOMPtr<nsIDOMNode> nearNode, selNode = aNode; 1.6782 + NS_ENSURE_STATE(mHTMLEditor); 1.6783 + res = mHTMLEditor->GetPriorHTMLNode(aNode, aOffset, address_of(nearNode)); 1.6784 + NS_ENSURE_SUCCESS(res, res); 1.6785 + NS_ENSURE_STATE(mHTMLEditor); 1.6786 + if (!nearNode || !mHTMLEditor->IsVisBreak(nearNode) || 1.6787 + nsTextEditUtils::HasMozAttr(nearNode)) { 1.6788 + // is there a BR after it? 1.6789 + NS_ENSURE_STATE(mHTMLEditor); 1.6790 + res = mHTMLEditor->GetNextHTMLNode(aNode, aOffset, address_of(nearNode)); 1.6791 + NS_ENSURE_SUCCESS(res, res); 1.6792 + NS_ENSURE_STATE(mHTMLEditor); 1.6793 + if (!nearNode || !mHTMLEditor->IsVisBreak(nearNode) || 1.6794 + nsTextEditUtils::HasMozAttr(nearNode)) { 1.6795 + newBRneeded = true; 1.6796 + } 1.6797 + } 1.6798 + if (!newBRneeded) { 1.6799 + sibling = nearNode; 1.6800 + } 1.6801 + } 1.6802 + if (newBRneeded) { 1.6803 + // if CR does not create a new P, default to BR creation 1.6804 + NS_ENSURE_TRUE(doesCRCreateNewP, NS_OK); 1.6805 + 1.6806 + nsCOMPtr<nsIDOMNode> brNode; 1.6807 + NS_ENSURE_STATE(mHTMLEditor); 1.6808 + res = mHTMLEditor->CreateBR(parent, offset, address_of(brNode)); 1.6809 + sibling = brNode; 1.6810 + } 1.6811 + nsCOMPtr<nsIDOMNode> selNode = aNode; 1.6812 + *aHandled = true; 1.6813 + return SplitParagraph(aPara, sibling, aSelection, address_of(selNode), &aOffset); 1.6814 +} 1.6815 + 1.6816 +/////////////////////////////////////////////////////////////////////////// 1.6817 +// SplitParagraph: split a paragraph at selection point, possibly deleting a br 1.6818 +// 1.6819 +nsresult 1.6820 +nsHTMLEditRules::SplitParagraph(nsIDOMNode *aPara, 1.6821 + nsIDOMNode *aBRNode, 1.6822 + nsISelection *aSelection, 1.6823 + nsCOMPtr<nsIDOMNode> *aSelNode, 1.6824 + int32_t *aOffset) 1.6825 +{ 1.6826 + NS_ENSURE_TRUE(aPara && aBRNode && aSelNode && *aSelNode && aOffset && aSelection, NS_ERROR_NULL_POINTER); 1.6827 + nsresult res = NS_OK; 1.6828 + 1.6829 + // split para 1.6830 + int32_t newOffset; 1.6831 + // get ws code to adjust any ws 1.6832 + nsCOMPtr<nsIDOMNode> leftPara, rightPara; 1.6833 + NS_ENSURE_STATE(mHTMLEditor); 1.6834 + res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor, aSelNode, aOffset); 1.6835 + NS_ENSURE_SUCCESS(res, res); 1.6836 + // split the paragraph 1.6837 + NS_ENSURE_STATE(mHTMLEditor); 1.6838 + res = mHTMLEditor->SplitNodeDeep(aPara, *aSelNode, *aOffset, &newOffset, false, 1.6839 + address_of(leftPara), address_of(rightPara)); 1.6840 + NS_ENSURE_SUCCESS(res, res); 1.6841 + // get rid of the break, if it is visible (otherwise it may be needed to prevent an empty p) 1.6842 + NS_ENSURE_STATE(mHTMLEditor); 1.6843 + if (mHTMLEditor->IsVisBreak(aBRNode)) 1.6844 + { 1.6845 + NS_ENSURE_STATE(mHTMLEditor); 1.6846 + res = mHTMLEditor->DeleteNode(aBRNode); 1.6847 + NS_ENSURE_SUCCESS(res, res); 1.6848 + } 1.6849 + 1.6850 + // remove ID attribute on the paragraph we just created 1.6851 + nsCOMPtr<nsIDOMElement> rightElt = do_QueryInterface(rightPara); 1.6852 + NS_ENSURE_STATE(mHTMLEditor); 1.6853 + res = mHTMLEditor->RemoveAttribute(rightElt, NS_LITERAL_STRING("id")); 1.6854 + NS_ENSURE_SUCCESS(res, res); 1.6855 + 1.6856 + // check both halves of para to see if we need mozBR 1.6857 + res = InsertMozBRIfNeeded(leftPara); 1.6858 + NS_ENSURE_SUCCESS(res, res); 1.6859 + res = InsertMozBRIfNeeded(rightPara); 1.6860 + NS_ENSURE_SUCCESS(res, res); 1.6861 + 1.6862 + // selection to beginning of right hand para; 1.6863 + // look inside any containers that are up front. 1.6864 + NS_ENSURE_STATE(mHTMLEditor); 1.6865 + nsCOMPtr<nsIDOMNode> child = mHTMLEditor->GetLeftmostChild(rightPara, true); 1.6866 + NS_ENSURE_STATE(mHTMLEditor); 1.6867 + if (mHTMLEditor->IsTextNode(child) || !mHTMLEditor || 1.6868 + mHTMLEditor->IsContainer(child)) 1.6869 + { 1.6870 + NS_ENSURE_STATE(mHTMLEditor); 1.6871 + aSelection->Collapse(child,0); 1.6872 + } 1.6873 + else 1.6874 + { 1.6875 + int32_t offset; 1.6876 + nsCOMPtr<nsIDOMNode> parent = nsEditor::GetNodeLocation(child, &offset); 1.6877 + aSelection->Collapse(parent,offset); 1.6878 + } 1.6879 + return res; 1.6880 +} 1.6881 + 1.6882 + 1.6883 +/////////////////////////////////////////////////////////////////////////// 1.6884 +// ReturnInListItem: do the right thing for returns pressed in list items 1.6885 +// 1.6886 +nsresult 1.6887 +nsHTMLEditRules::ReturnInListItem(nsISelection *aSelection, 1.6888 + nsIDOMNode *aListItem, 1.6889 + nsIDOMNode *aNode, 1.6890 + int32_t aOffset) 1.6891 +{ 1.6892 + NS_ENSURE_TRUE(aSelection && aListItem && aNode, NS_ERROR_NULL_POINTER); 1.6893 + nsCOMPtr<nsISelection> selection(aSelection); 1.6894 + nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection)); 1.6895 + nsresult res = NS_OK; 1.6896 + 1.6897 + nsCOMPtr<nsIDOMNode> listitem; 1.6898 + 1.6899 + // sanity check 1.6900 + NS_PRECONDITION(true == nsHTMLEditUtils::IsListItem(aListItem), 1.6901 + "expected a list item and didn't get one"); 1.6902 + 1.6903 + // get the listitem parent and the active editing host. 1.6904 + NS_ENSURE_STATE(mHTMLEditor); 1.6905 + nsIContent* rootContent = mHTMLEditor->GetActiveEditingHost(); 1.6906 + nsCOMPtr<nsIDOMNode> rootNode = do_QueryInterface(rootContent); 1.6907 + int32_t itemOffset; 1.6908 + nsCOMPtr<nsIDOMNode> list = nsEditor::GetNodeLocation(aListItem, &itemOffset); 1.6909 + 1.6910 + // if we are in an empty listitem, then we want to pop up out of the list 1.6911 + // but only if prefs says it's ok and if the parent isn't the active editing host. 1.6912 + bool isEmpty; 1.6913 + res = IsEmptyBlock(aListItem, &isEmpty, true, false); 1.6914 + NS_ENSURE_SUCCESS(res, res); 1.6915 + if (isEmpty && (rootNode != list) && mReturnInEmptyLIKillsList) 1.6916 + { 1.6917 + // get the list offset now -- before we might eventually split the list 1.6918 + int32_t offset; 1.6919 + nsCOMPtr<nsIDOMNode> listparent = nsEditor::GetNodeLocation(list, &offset); 1.6920 + 1.6921 + // are we the last list item in the list? 1.6922 + bool bIsLast; 1.6923 + NS_ENSURE_STATE(mHTMLEditor); 1.6924 + res = mHTMLEditor->IsLastEditableChild(aListItem, &bIsLast); 1.6925 + NS_ENSURE_SUCCESS(res, res); 1.6926 + if (!bIsLast) 1.6927 + { 1.6928 + // we need to split the list! 1.6929 + nsCOMPtr<nsIDOMNode> tempNode; 1.6930 + NS_ENSURE_STATE(mHTMLEditor); 1.6931 + res = mHTMLEditor->SplitNode(list, itemOffset, getter_AddRefs(tempNode)); 1.6932 + NS_ENSURE_SUCCESS(res, res); 1.6933 + } 1.6934 + 1.6935 + // are we in a sublist? 1.6936 + if (nsHTMLEditUtils::IsList(listparent)) //in a sublist 1.6937 + { 1.6938 + // if so, move this list item out of this list and into the grandparent list 1.6939 + NS_ENSURE_STATE(mHTMLEditor); 1.6940 + res = mHTMLEditor->MoveNode(aListItem,listparent,offset+1); 1.6941 + NS_ENSURE_SUCCESS(res, res); 1.6942 + res = aSelection->Collapse(aListItem,0); 1.6943 + } 1.6944 + else 1.6945 + { 1.6946 + // otherwise kill this listitem 1.6947 + NS_ENSURE_STATE(mHTMLEditor); 1.6948 + res = mHTMLEditor->DeleteNode(aListItem); 1.6949 + NS_ENSURE_SUCCESS(res, res); 1.6950 + 1.6951 + // time to insert a paragraph 1.6952 + NS_NAMED_LITERAL_STRING(pType, "p"); 1.6953 + nsCOMPtr<nsIDOMNode> pNode; 1.6954 + NS_ENSURE_STATE(mHTMLEditor); 1.6955 + res = mHTMLEditor->CreateNode(pType, listparent, offset+1, getter_AddRefs(pNode)); 1.6956 + NS_ENSURE_SUCCESS(res, res); 1.6957 + 1.6958 + // append a <br> to it 1.6959 + nsCOMPtr<nsIDOMNode> brNode; 1.6960 + NS_ENSURE_STATE(mHTMLEditor); 1.6961 + res = mHTMLEditor->CreateBR(pNode, 0, address_of(brNode)); 1.6962 + NS_ENSURE_SUCCESS(res, res); 1.6963 + 1.6964 + // set selection to before the break 1.6965 + res = aSelection->Collapse(pNode, 0); 1.6966 + } 1.6967 + return res; 1.6968 + } 1.6969 + 1.6970 + // else we want a new list item at the same list level. 1.6971 + // get ws code to adjust any ws 1.6972 + nsCOMPtr<nsIDOMNode> selNode = aNode; 1.6973 + NS_ENSURE_STATE(mHTMLEditor); 1.6974 + res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor, address_of(selNode), &aOffset); 1.6975 + NS_ENSURE_SUCCESS(res, res); 1.6976 + // now split list item 1.6977 + int32_t newOffset; 1.6978 + NS_ENSURE_STATE(mHTMLEditor); 1.6979 + res = mHTMLEditor->SplitNodeDeep( aListItem, selNode, aOffset, &newOffset, false); 1.6980 + NS_ENSURE_SUCCESS(res, res); 1.6981 + // hack: until I can change the damaged doc range code back to being 1.6982 + // extra inclusive, I have to manually detect certain list items that 1.6983 + // may be left empty. 1.6984 + nsCOMPtr<nsIDOMNode> prevItem; 1.6985 + NS_ENSURE_STATE(mHTMLEditor); 1.6986 + mHTMLEditor->GetPriorHTMLSibling(aListItem, address_of(prevItem)); 1.6987 + 1.6988 + if (prevItem && nsHTMLEditUtils::IsListItem(prevItem)) 1.6989 + { 1.6990 + bool bIsEmptyNode; 1.6991 + NS_ENSURE_STATE(mHTMLEditor); 1.6992 + res = mHTMLEditor->IsEmptyNode(prevItem, &bIsEmptyNode); 1.6993 + NS_ENSURE_SUCCESS(res, res); 1.6994 + if (bIsEmptyNode) { 1.6995 + res = CreateMozBR(prevItem, 0); 1.6996 + NS_ENSURE_SUCCESS(res, res); 1.6997 + } else { 1.6998 + NS_ENSURE_STATE(mHTMLEditor); 1.6999 + res = mHTMLEditor->IsEmptyNode(aListItem, &bIsEmptyNode, true); 1.7000 + NS_ENSURE_SUCCESS(res, res); 1.7001 + if (bIsEmptyNode) 1.7002 + { 1.7003 + nsCOMPtr<nsIAtom> nodeAtom = nsEditor::GetTag(aListItem); 1.7004 + if (nodeAtom == nsEditProperty::dd || nodeAtom == nsEditProperty::dt) 1.7005 + { 1.7006 + int32_t itemOffset; 1.7007 + nsCOMPtr<nsIDOMNode> list = nsEditor::GetNodeLocation(aListItem, &itemOffset); 1.7008 + 1.7009 + nsAutoString listTag((nodeAtom == nsEditProperty::dt) ? NS_LITERAL_STRING("dd") : NS_LITERAL_STRING("dt")); 1.7010 + nsCOMPtr<nsIDOMNode> newListItem; 1.7011 + NS_ENSURE_STATE(mHTMLEditor); 1.7012 + res = mHTMLEditor->CreateNode(listTag, list, itemOffset+1, getter_AddRefs(newListItem)); 1.7013 + NS_ENSURE_SUCCESS(res, res); 1.7014 + res = mEditor->DeleteNode(aListItem); 1.7015 + NS_ENSURE_SUCCESS(res, res); 1.7016 + return aSelection->Collapse(newListItem, 0); 1.7017 + } 1.7018 + 1.7019 + nsCOMPtr<nsIDOMNode> brNode; 1.7020 + NS_ENSURE_STATE(mHTMLEditor); 1.7021 + res = mHTMLEditor->CopyLastEditableChildStyles(prevItem, aListItem, getter_AddRefs(brNode)); 1.7022 + NS_ENSURE_SUCCESS(res, res); 1.7023 + if (brNode) 1.7024 + { 1.7025 + int32_t offset; 1.7026 + nsCOMPtr<nsIDOMNode> brParent = nsEditor::GetNodeLocation(brNode, &offset); 1.7027 + return aSelection->Collapse(brParent, offset); 1.7028 + } 1.7029 + } 1.7030 + else 1.7031 + { 1.7032 + NS_ENSURE_STATE(mHTMLEditor); 1.7033 + nsWSRunObject wsObj(mHTMLEditor, aListItem, 0); 1.7034 + nsCOMPtr<nsIDOMNode> visNode; 1.7035 + int32_t visOffset = 0; 1.7036 + WSType wsType; 1.7037 + wsObj.NextVisibleNode(aListItem, 0, address_of(visNode), 1.7038 + &visOffset, &wsType); 1.7039 + if (wsType == WSType::special || wsType == WSType::br || 1.7040 + nsHTMLEditUtils::IsHR(visNode)) { 1.7041 + int32_t offset; 1.7042 + nsCOMPtr<nsIDOMNode> parent = nsEditor::GetNodeLocation(visNode, &offset); 1.7043 + return aSelection->Collapse(parent, offset); 1.7044 + } 1.7045 + else 1.7046 + { 1.7047 + return aSelection->Collapse(visNode, visOffset); 1.7048 + } 1.7049 + } 1.7050 + } 1.7051 + } 1.7052 + res = aSelection->Collapse(aListItem,0); 1.7053 + return res; 1.7054 +} 1.7055 + 1.7056 + 1.7057 +/////////////////////////////////////////////////////////////////////////// 1.7058 +// MakeBlockquote: put the list of nodes into one or more blockquotes. 1.7059 +// 1.7060 +nsresult 1.7061 +nsHTMLEditRules::MakeBlockquote(nsCOMArray<nsIDOMNode>& arrayOfNodes) 1.7062 +{ 1.7063 + // the idea here is to put the nodes into a minimal number of 1.7064 + // blockquotes. When the user blockquotes something, they expect 1.7065 + // one blockquote. That may not be possible (for instance, if they 1.7066 + // have two table cells selected, you need two blockquotes inside the cells). 1.7067 + 1.7068 + nsresult res = NS_OK; 1.7069 + 1.7070 + nsCOMPtr<nsIDOMNode> curNode, curParent, curBlock, newBlock; 1.7071 + int32_t offset; 1.7072 + int32_t listCount = arrayOfNodes.Count(); 1.7073 + 1.7074 + nsCOMPtr<nsIDOMNode> prevParent; 1.7075 + 1.7076 + int32_t i; 1.7077 + for (i=0; i<listCount; i++) 1.7078 + { 1.7079 + // get the node to act on, and its location 1.7080 + curNode = arrayOfNodes[i]; 1.7081 + curParent = nsEditor::GetNodeLocation(curNode, &offset); 1.7082 + 1.7083 + // if the node is a table element or list item, dive inside 1.7084 + if (nsHTMLEditUtils::IsTableElementButNotTable(curNode) || 1.7085 + nsHTMLEditUtils::IsListItem(curNode)) 1.7086 + { 1.7087 + curBlock = 0; // forget any previous block 1.7088 + // recursion time 1.7089 + nsCOMArray<nsIDOMNode> childArray; 1.7090 + res = GetChildNodesForOperation(curNode, childArray); 1.7091 + NS_ENSURE_SUCCESS(res, res); 1.7092 + res = MakeBlockquote(childArray); 1.7093 + NS_ENSURE_SUCCESS(res, res); 1.7094 + } 1.7095 + 1.7096 + // if the node has different parent than previous node, 1.7097 + // further nodes in a new parent 1.7098 + if (prevParent) 1.7099 + { 1.7100 + nsCOMPtr<nsIDOMNode> temp; 1.7101 + curNode->GetParentNode(getter_AddRefs(temp)); 1.7102 + if (temp != prevParent) 1.7103 + { 1.7104 + curBlock = 0; // forget any previous blockquote node we were using 1.7105 + prevParent = temp; 1.7106 + } 1.7107 + } 1.7108 + else 1.7109 + 1.7110 + { 1.7111 + curNode->GetParentNode(getter_AddRefs(prevParent)); 1.7112 + } 1.7113 + 1.7114 + // if no curBlock, make one 1.7115 + if (!curBlock) 1.7116 + { 1.7117 + NS_NAMED_LITERAL_STRING(quoteType, "blockquote"); 1.7118 + res = SplitAsNeeded("eType, address_of(curParent), &offset); 1.7119 + NS_ENSURE_SUCCESS(res, res); 1.7120 + NS_ENSURE_STATE(mHTMLEditor); 1.7121 + res = mHTMLEditor->CreateNode(quoteType, curParent, offset, getter_AddRefs(curBlock)); 1.7122 + NS_ENSURE_SUCCESS(res, res); 1.7123 + // remember our new block for postprocessing 1.7124 + mNewBlock = curBlock; 1.7125 + // note: doesn't matter if we set mNewBlock multiple times. 1.7126 + } 1.7127 + 1.7128 + NS_ENSURE_STATE(mHTMLEditor); 1.7129 + res = mHTMLEditor->MoveNode(curNode, curBlock, -1); 1.7130 + NS_ENSURE_SUCCESS(res, res); 1.7131 + } 1.7132 + return res; 1.7133 +} 1.7134 + 1.7135 + 1.7136 + 1.7137 +/////////////////////////////////////////////////////////////////////////// 1.7138 +// RemoveBlockStyle: make the nodes have no special block type. 1.7139 +// 1.7140 +nsresult 1.7141 +nsHTMLEditRules::RemoveBlockStyle(nsCOMArray<nsIDOMNode>& arrayOfNodes) 1.7142 +{ 1.7143 + // intent of this routine is to be used for converting to/from 1.7144 + // headers, paragraphs, pre, and address. Those blocks 1.7145 + // that pretty much just contain inline things... 1.7146 + 1.7147 + nsresult res = NS_OK; 1.7148 + 1.7149 + nsCOMPtr<nsIDOMNode> curBlock, firstNode, lastNode; 1.7150 + int32_t listCount = arrayOfNodes.Count(); 1.7151 + for (int32_t i = 0; i < listCount; ++i) { 1.7152 + // get the node to act on, and its location 1.7153 + nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i]; 1.7154 + 1.7155 + nsCOMPtr<dom::Element> curElement = do_QueryInterface(curNode); 1.7156 + 1.7157 + // if curNode is a address, p, header, address, or pre, remove it 1.7158 + if (curElement && nsHTMLEditUtils::IsFormatNode(curElement)) { 1.7159 + // process any partial progress saved 1.7160 + if (curBlock) 1.7161 + { 1.7162 + res = RemovePartOfBlock(curBlock, firstNode, lastNode); 1.7163 + NS_ENSURE_SUCCESS(res, res); 1.7164 + curBlock = 0; firstNode = 0; lastNode = 0; 1.7165 + } 1.7166 + // remove curent block 1.7167 + NS_ENSURE_STATE(mHTMLEditor); 1.7168 + res = mHTMLEditor->RemoveBlockContainer(curNode); 1.7169 + NS_ENSURE_SUCCESS(res, res); 1.7170 + } else if (curElement && 1.7171 + (curElement->IsHTML(nsGkAtoms::table) || 1.7172 + curElement->IsHTML(nsGkAtoms::tr) || 1.7173 + curElement->IsHTML(nsGkAtoms::tbody) || 1.7174 + curElement->IsHTML(nsGkAtoms::td) || 1.7175 + nsHTMLEditUtils::IsList(curElement) || 1.7176 + curElement->IsHTML(nsGkAtoms::li) || 1.7177 + curElement->IsHTML(nsGkAtoms::blockquote) || 1.7178 + curElement->IsHTML(nsGkAtoms::div))) { 1.7179 + // process any partial progress saved 1.7180 + if (curBlock) 1.7181 + { 1.7182 + res = RemovePartOfBlock(curBlock, firstNode, lastNode); 1.7183 + NS_ENSURE_SUCCESS(res, res); 1.7184 + curBlock = 0; firstNode = 0; lastNode = 0; 1.7185 + } 1.7186 + // recursion time 1.7187 + nsCOMArray<nsIDOMNode> childArray; 1.7188 + res = GetChildNodesForOperation(curNode, childArray); 1.7189 + NS_ENSURE_SUCCESS(res, res); 1.7190 + res = RemoveBlockStyle(childArray); 1.7191 + NS_ENSURE_SUCCESS(res, res); 1.7192 + } 1.7193 + else if (IsInlineNode(curNode)) 1.7194 + { 1.7195 + if (curBlock) 1.7196 + { 1.7197 + // if so, is this node a descendant? 1.7198 + if (nsEditorUtils::IsDescendantOf(curNode, curBlock)) 1.7199 + { 1.7200 + lastNode = curNode; 1.7201 + continue; // then we don't need to do anything different for this node 1.7202 + } 1.7203 + else 1.7204 + { 1.7205 + // otherwise, we have progressed beyond end of curBlock, 1.7206 + // so lets handle it now. We need to remove the portion of 1.7207 + // curBlock that contains [firstNode - lastNode]. 1.7208 + res = RemovePartOfBlock(curBlock, firstNode, lastNode); 1.7209 + NS_ENSURE_SUCCESS(res, res); 1.7210 + curBlock = 0; firstNode = 0; lastNode = 0; 1.7211 + // fall out and handle curNode 1.7212 + } 1.7213 + } 1.7214 + NS_ENSURE_STATE(mHTMLEditor); 1.7215 + curBlock = mHTMLEditor->GetBlockNodeParent(curNode); 1.7216 + if (nsHTMLEditUtils::IsFormatNode(curBlock)) 1.7217 + { 1.7218 + firstNode = curNode; 1.7219 + lastNode = curNode; 1.7220 + } 1.7221 + else 1.7222 + curBlock = 0; // not a block kind that we care about. 1.7223 + } 1.7224 + else 1.7225 + { // some node that is already sans block style. skip over it and 1.7226 + // process any partial progress saved 1.7227 + if (curBlock) 1.7228 + { 1.7229 + res = RemovePartOfBlock(curBlock, firstNode, lastNode); 1.7230 + NS_ENSURE_SUCCESS(res, res); 1.7231 + curBlock = 0; firstNode = 0; lastNode = 0; 1.7232 + } 1.7233 + } 1.7234 + } 1.7235 + // process any partial progress saved 1.7236 + if (curBlock) 1.7237 + { 1.7238 + res = RemovePartOfBlock(curBlock, firstNode, lastNode); 1.7239 + NS_ENSURE_SUCCESS(res, res); 1.7240 + curBlock = 0; firstNode = 0; lastNode = 0; 1.7241 + } 1.7242 + return res; 1.7243 +} 1.7244 + 1.7245 + 1.7246 +/////////////////////////////////////////////////////////////////////////// 1.7247 +// ApplyBlockStyle: do whatever it takes to make the list of nodes into 1.7248 +// one or more blocks of type blockTag. 1.7249 +// 1.7250 +nsresult 1.7251 +nsHTMLEditRules::ApplyBlockStyle(nsCOMArray<nsIDOMNode>& arrayOfNodes, const nsAString *aBlockTag) 1.7252 +{ 1.7253 + // intent of this routine is to be used for converting to/from 1.7254 + // headers, paragraphs, pre, and address. Those blocks 1.7255 + // that pretty much just contain inline things... 1.7256 + 1.7257 + NS_ENSURE_TRUE(aBlockTag, NS_ERROR_NULL_POINTER); 1.7258 + nsresult res = NS_OK; 1.7259 + 1.7260 + nsCOMPtr<nsIDOMNode> curNode, curParent, curBlock, newBlock; 1.7261 + int32_t offset; 1.7262 + int32_t listCount = arrayOfNodes.Count(); 1.7263 + nsString tString(*aBlockTag);////MJUDGE SCC NEED HELP 1.7264 + 1.7265 + // Remove all non-editable nodes. Leave them be. 1.7266 + int32_t j; 1.7267 + for (j=listCount-1; j>=0; j--) 1.7268 + { 1.7269 + NS_ENSURE_STATE(mHTMLEditor); 1.7270 + if (!mHTMLEditor->IsEditable(arrayOfNodes[j])) 1.7271 + { 1.7272 + arrayOfNodes.RemoveObjectAt(j); 1.7273 + } 1.7274 + } 1.7275 + 1.7276 + // reset list count 1.7277 + listCount = arrayOfNodes.Count(); 1.7278 + 1.7279 + int32_t i; 1.7280 + for (i=0; i<listCount; i++) 1.7281 + { 1.7282 + // get the node to act on, and its location 1.7283 + curNode = arrayOfNodes[i]; 1.7284 + curParent = nsEditor::GetNodeLocation(curNode, &offset); 1.7285 + nsAutoString curNodeTag; 1.7286 + nsEditor::GetTagString(curNode, curNodeTag); 1.7287 + ToLowerCase(curNodeTag); 1.7288 + 1.7289 + // is it already the right kind of block? 1.7290 + if (curNodeTag == *aBlockTag) 1.7291 + { 1.7292 + curBlock = 0; // forget any previous block used for previous inline nodes 1.7293 + continue; // do nothing to this block 1.7294 + } 1.7295 + 1.7296 + // if curNode is a address, p, header, address, or pre, replace 1.7297 + // it with a new block of correct type. 1.7298 + // xxx floppy moose: pre can't hold everything the others can 1.7299 + if (nsHTMLEditUtils::IsMozDiv(curNode) || 1.7300 + nsHTMLEditUtils::IsFormatNode(curNode)) 1.7301 + { 1.7302 + curBlock = 0; // forget any previous block used for previous inline nodes 1.7303 + NS_ENSURE_STATE(mHTMLEditor); 1.7304 + res = mHTMLEditor->ReplaceContainer(curNode, address_of(newBlock), *aBlockTag, 1.7305 + nullptr, nullptr, true); 1.7306 + NS_ENSURE_SUCCESS(res, res); 1.7307 + } 1.7308 + else if (nsHTMLEditUtils::IsTable(curNode) || 1.7309 + (curNodeTag.EqualsLiteral("tbody")) || 1.7310 + (curNodeTag.EqualsLiteral("tr")) || 1.7311 + (curNodeTag.EqualsLiteral("td")) || 1.7312 + nsHTMLEditUtils::IsList(curNode) || 1.7313 + (curNodeTag.EqualsLiteral("li")) || 1.7314 + nsHTMLEditUtils::IsBlockquote(curNode) || 1.7315 + nsHTMLEditUtils::IsDiv(curNode)) 1.7316 + { 1.7317 + curBlock = 0; // forget any previous block used for previous inline nodes 1.7318 + // recursion time 1.7319 + nsCOMArray<nsIDOMNode> childArray; 1.7320 + res = GetChildNodesForOperation(curNode, childArray); 1.7321 + NS_ENSURE_SUCCESS(res, res); 1.7322 + int32_t childCount = childArray.Count(); 1.7323 + if (childCount) 1.7324 + { 1.7325 + res = ApplyBlockStyle(childArray, aBlockTag); 1.7326 + NS_ENSURE_SUCCESS(res, res); 1.7327 + } 1.7328 + else 1.7329 + { 1.7330 + // make sure we can put a block here 1.7331 + res = SplitAsNeeded(aBlockTag, address_of(curParent), &offset); 1.7332 + NS_ENSURE_SUCCESS(res, res); 1.7333 + nsCOMPtr<nsIDOMNode> theBlock; 1.7334 + NS_ENSURE_STATE(mHTMLEditor); 1.7335 + res = mHTMLEditor->CreateNode(*aBlockTag, curParent, offset, getter_AddRefs(theBlock)); 1.7336 + NS_ENSURE_SUCCESS(res, res); 1.7337 + // remember our new block for postprocessing 1.7338 + mNewBlock = theBlock; 1.7339 + } 1.7340 + } 1.7341 + 1.7342 + // if the node is a break, we honor it by putting further nodes in a new parent 1.7343 + else if (curNodeTag.EqualsLiteral("br")) 1.7344 + { 1.7345 + if (curBlock) 1.7346 + { 1.7347 + curBlock = 0; // forget any previous block used for previous inline nodes 1.7348 + NS_ENSURE_STATE(mHTMLEditor); 1.7349 + res = mHTMLEditor->DeleteNode(curNode); 1.7350 + NS_ENSURE_SUCCESS(res, res); 1.7351 + } 1.7352 + else 1.7353 + { 1.7354 + // the break is the first (or even only) node we encountered. Create a 1.7355 + // block for it. 1.7356 + res = SplitAsNeeded(aBlockTag, address_of(curParent), &offset); 1.7357 + NS_ENSURE_SUCCESS(res, res); 1.7358 + NS_ENSURE_STATE(mHTMLEditor); 1.7359 + res = mHTMLEditor->CreateNode(*aBlockTag, curParent, offset, getter_AddRefs(curBlock)); 1.7360 + NS_ENSURE_SUCCESS(res, res); 1.7361 + // remember our new block for postprocessing 1.7362 + mNewBlock = curBlock; 1.7363 + // note: doesn't matter if we set mNewBlock multiple times. 1.7364 + NS_ENSURE_STATE(mHTMLEditor); 1.7365 + res = mHTMLEditor->MoveNode(curNode, curBlock, -1); 1.7366 + NS_ENSURE_SUCCESS(res, res); 1.7367 + } 1.7368 + } 1.7369 + 1.7370 + 1.7371 + // if curNode is inline, pull it into curBlock 1.7372 + // note: it's assumed that consecutive inline nodes in the 1.7373 + // arrayOfNodes are actually members of the same block parent. 1.7374 + // this happens to be true now as a side effect of how 1.7375 + // arrayOfNodes is contructed, but some additional logic should 1.7376 + // be added here if that should change 1.7377 + 1.7378 + else if (IsInlineNode(curNode)) 1.7379 + { 1.7380 + // if curNode is a non editable, drop it if we are going to <pre> 1.7381 + NS_ENSURE_STATE(mHTMLEditor); 1.7382 + if (tString.LowerCaseEqualsLiteral("pre") 1.7383 + && (!mHTMLEditor->IsEditable(curNode))) 1.7384 + continue; // do nothing to this block 1.7385 + 1.7386 + // if no curBlock, make one 1.7387 + if (!curBlock) 1.7388 + { 1.7389 + res = SplitAsNeeded(aBlockTag, address_of(curParent), &offset); 1.7390 + NS_ENSURE_SUCCESS(res, res); 1.7391 + NS_ENSURE_STATE(mHTMLEditor); 1.7392 + res = mHTMLEditor->CreateNode(*aBlockTag, curParent, offset, getter_AddRefs(curBlock)); 1.7393 + NS_ENSURE_SUCCESS(res, res); 1.7394 + // remember our new block for postprocessing 1.7395 + mNewBlock = curBlock; 1.7396 + // note: doesn't matter if we set mNewBlock multiple times. 1.7397 + } 1.7398 + 1.7399 + // if curNode is a Break, replace it with a return if we are going to <pre> 1.7400 + // xxx floppy moose 1.7401 + 1.7402 + // this is a continuation of some inline nodes that belong together in 1.7403 + // the same block item. use curBlock 1.7404 + NS_ENSURE_STATE(mHTMLEditor); 1.7405 + res = mHTMLEditor->MoveNode(curNode, curBlock, -1); 1.7406 + NS_ENSURE_SUCCESS(res, res); 1.7407 + } 1.7408 + } 1.7409 + return res; 1.7410 +} 1.7411 + 1.7412 + 1.7413 +/////////////////////////////////////////////////////////////////////////// 1.7414 +// SplitAsNeeded: given a tag name, split inOutParent up to the point 1.7415 +// where we can insert the tag. Adjust inOutParent and 1.7416 +// inOutOffset to pint to new location for tag. 1.7417 +nsresult 1.7418 +nsHTMLEditRules::SplitAsNeeded(const nsAString *aTag, 1.7419 + nsCOMPtr<nsIDOMNode> *inOutParent, 1.7420 + int32_t *inOutOffset) 1.7421 +{ 1.7422 + NS_ENSURE_TRUE(aTag && inOutParent && inOutOffset, NS_ERROR_NULL_POINTER); 1.7423 + NS_ENSURE_TRUE(*inOutParent, NS_ERROR_NULL_POINTER); 1.7424 + nsCOMPtr<nsIDOMNode> tagParent, temp, splitNode, parent = *inOutParent; 1.7425 + nsresult res = NS_OK; 1.7426 + nsCOMPtr<nsIAtom> tagAtom = do_GetAtom(*aTag); 1.7427 + 1.7428 + // check that we have a place that can legally contain the tag 1.7429 + while (!tagParent) 1.7430 + { 1.7431 + // sniffing up the parent tree until we find 1.7432 + // a legal place for the block 1.7433 + if (!parent) break; 1.7434 + // Don't leave the active editing host 1.7435 + NS_ENSURE_STATE(mHTMLEditor); 1.7436 + if (!mHTMLEditor->IsDescendantOfEditorRoot(parent)) { 1.7437 + nsCOMPtr<nsIContent> parentContent = do_QueryInterface(parent); 1.7438 + NS_ENSURE_STATE(mHTMLEditor); 1.7439 + if (parentContent != mHTMLEditor->GetActiveEditingHost()) { 1.7440 + break; 1.7441 + } 1.7442 + } 1.7443 + NS_ENSURE_STATE(mHTMLEditor); 1.7444 + if (mHTMLEditor->CanContainTag(parent, tagAtom)) { 1.7445 + tagParent = parent; 1.7446 + break; 1.7447 + } 1.7448 + splitNode = parent; 1.7449 + parent->GetParentNode(getter_AddRefs(temp)); 1.7450 + parent = temp; 1.7451 + } 1.7452 + if (!tagParent) 1.7453 + { 1.7454 + // could not find a place to build tag! 1.7455 + return NS_ERROR_FAILURE; 1.7456 + } 1.7457 + if (splitNode) 1.7458 + { 1.7459 + // we found a place for block, but above inOutParent. We need to split nodes. 1.7460 + NS_ENSURE_STATE(mHTMLEditor); 1.7461 + res = mHTMLEditor->SplitNodeDeep(splitNode, *inOutParent, *inOutOffset, inOutOffset); 1.7462 + NS_ENSURE_SUCCESS(res, res); 1.7463 + *inOutParent = tagParent; 1.7464 + } 1.7465 + return res; 1.7466 +} 1.7467 + 1.7468 +/////////////////////////////////////////////////////////////////////////// 1.7469 +// JoinNodesSmart: join two nodes, doing whatever makes sense for their 1.7470 +// children (which often means joining them, too). 1.7471 +// aNodeLeft & aNodeRight must be same type of node. 1.7472 +nsresult 1.7473 +nsHTMLEditRules::JoinNodesSmart( nsIDOMNode *aNodeLeft, 1.7474 + nsIDOMNode *aNodeRight, 1.7475 + nsCOMPtr<nsIDOMNode> *aOutMergeParent, 1.7476 + int32_t *aOutMergeOffset) 1.7477 +{ 1.7478 + // check parms 1.7479 + NS_ENSURE_TRUE(aNodeLeft && 1.7480 + aNodeRight && 1.7481 + aOutMergeParent && 1.7482 + aOutMergeOffset, NS_ERROR_NULL_POINTER); 1.7483 + 1.7484 + nsresult res = NS_OK; 1.7485 + // caller responsible for: 1.7486 + // left & right node are same type 1.7487 + int32_t parOffset; 1.7488 + nsCOMPtr<nsIDOMNode> rightParent; 1.7489 + nsCOMPtr<nsIDOMNode> parent = nsEditor::GetNodeLocation(aNodeLeft, &parOffset); 1.7490 + aNodeRight->GetParentNode(getter_AddRefs(rightParent)); 1.7491 + 1.7492 + // if they don't have the same parent, first move the 'right' node 1.7493 + // to after the 'left' one 1.7494 + if (parent != rightParent) 1.7495 + { 1.7496 + NS_ENSURE_STATE(mHTMLEditor); 1.7497 + res = mHTMLEditor->MoveNode(aNodeRight, parent, parOffset); 1.7498 + NS_ENSURE_SUCCESS(res, res); 1.7499 + } 1.7500 + 1.7501 + // defaults for outParams 1.7502 + *aOutMergeParent = aNodeRight; 1.7503 + NS_ENSURE_STATE(mHTMLEditor); 1.7504 + res = mHTMLEditor->GetLengthOfDOMNode(aNodeLeft, *((uint32_t*)aOutMergeOffset)); 1.7505 + NS_ENSURE_SUCCESS(res, res); 1.7506 + 1.7507 + // separate join rules for differing blocks 1.7508 + if (nsHTMLEditUtils::IsList(aNodeLeft) || 1.7509 + !mHTMLEditor || 1.7510 + mHTMLEditor->IsTextNode(aNodeLeft)) 1.7511 + { 1.7512 + // for list's, merge shallow (wouldn't want to combine list items) 1.7513 + NS_ENSURE_STATE(mHTMLEditor); 1.7514 + res = mHTMLEditor->JoinNodes(aNodeLeft, aNodeRight, parent); 1.7515 + NS_ENSURE_SUCCESS(res, res); 1.7516 + return res; 1.7517 + } 1.7518 + else 1.7519 + { 1.7520 + // remember the last left child, and firt right child 1.7521 + nsCOMPtr<nsIDOMNode> lastLeft, firstRight; 1.7522 + NS_ENSURE_STATE(mHTMLEditor); 1.7523 + res = mHTMLEditor->GetLastEditableChild(aNodeLeft, address_of(lastLeft)); 1.7524 + NS_ENSURE_SUCCESS(res, res); 1.7525 + NS_ENSURE_STATE(mHTMLEditor); 1.7526 + res = mHTMLEditor->GetFirstEditableChild(aNodeRight, address_of(firstRight)); 1.7527 + NS_ENSURE_SUCCESS(res, res); 1.7528 + 1.7529 + // for list items, divs, etc, merge smart 1.7530 + NS_ENSURE_STATE(mHTMLEditor); 1.7531 + res = mHTMLEditor->JoinNodes(aNodeLeft, aNodeRight, parent); 1.7532 + NS_ENSURE_SUCCESS(res, res); 1.7533 + 1.7534 + if (lastLeft && firstRight && mHTMLEditor && 1.7535 + mHTMLEditor->NodesSameType(lastLeft, firstRight) && 1.7536 + (nsEditor::IsTextNode(lastLeft) || 1.7537 + !mHTMLEditor || 1.7538 + mHTMLEditor->mHTMLCSSUtils->ElementsSameStyle(lastLeft, firstRight))) { 1.7539 + NS_ENSURE_STATE(mHTMLEditor); 1.7540 + return JoinNodesSmart(lastLeft, firstRight, aOutMergeParent, aOutMergeOffset); 1.7541 + } 1.7542 + } 1.7543 + return res; 1.7544 +} 1.7545 + 1.7546 + 1.7547 +nsresult 1.7548 +nsHTMLEditRules::GetTopEnclosingMailCite(nsIDOMNode *aNode, 1.7549 + nsCOMPtr<nsIDOMNode> *aOutCiteNode, 1.7550 + bool aPlainText) 1.7551 +{ 1.7552 + // check parms 1.7553 + NS_ENSURE_TRUE(aNode && aOutCiteNode, NS_ERROR_NULL_POINTER); 1.7554 + 1.7555 + nsresult res = NS_OK; 1.7556 + nsCOMPtr<nsIDOMNode> node, parentNode; 1.7557 + node = do_QueryInterface(aNode); 1.7558 + 1.7559 + while (node) 1.7560 + { 1.7561 + if ( (aPlainText && nsHTMLEditUtils::IsPre(node)) || 1.7562 + nsHTMLEditUtils::IsMailCite(node) ) 1.7563 + *aOutCiteNode = node; 1.7564 + if (nsTextEditUtils::IsBody(node)) break; 1.7565 + 1.7566 + res = node->GetParentNode(getter_AddRefs(parentNode)); 1.7567 + NS_ENSURE_SUCCESS(res, res); 1.7568 + node = parentNode; 1.7569 + } 1.7570 + 1.7571 + return res; 1.7572 +} 1.7573 + 1.7574 + 1.7575 +nsresult 1.7576 +nsHTMLEditRules::CacheInlineStyles(nsIDOMNode *aNode) 1.7577 +{ 1.7578 + NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER); 1.7579 + 1.7580 + NS_ENSURE_STATE(mHTMLEditor); 1.7581 + bool useCSS = mHTMLEditor->IsCSSEnabled(); 1.7582 + 1.7583 + for (int32_t j = 0; j < SIZE_STYLE_TABLE; ++j) 1.7584 + { 1.7585 + bool isSet = false; 1.7586 + nsAutoString outValue; 1.7587 + // Don't use CSS for <font size>, we don't support it usefully (bug 780035) 1.7588 + if (!useCSS || (mCachedStyles[j].tag == nsGkAtoms::font && 1.7589 + mCachedStyles[j].attr.EqualsLiteral("size"))) { 1.7590 + NS_ENSURE_STATE(mHTMLEditor); 1.7591 + mHTMLEditor->IsTextPropertySetByContent(aNode, mCachedStyles[j].tag, 1.7592 + &(mCachedStyles[j].attr), nullptr, 1.7593 + isSet, &outValue); 1.7594 + } 1.7595 + else 1.7596 + { 1.7597 + NS_ENSURE_STATE(mHTMLEditor); 1.7598 + mHTMLEditor->mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(aNode, 1.7599 + mCachedStyles[j].tag, &(mCachedStyles[j].attr), isSet, outValue, 1.7600 + nsHTMLCSSUtils::eComputed); 1.7601 + } 1.7602 + if (isSet) 1.7603 + { 1.7604 + mCachedStyles[j].mPresent = true; 1.7605 + mCachedStyles[j].value.Assign(outValue); 1.7606 + } 1.7607 + } 1.7608 + return NS_OK; 1.7609 +} 1.7610 + 1.7611 + 1.7612 +nsresult 1.7613 +nsHTMLEditRules::ReapplyCachedStyles() 1.7614 +{ 1.7615 + // The idea here is to examine our cached list of styles and see if any have 1.7616 + // been removed. If so, add typeinstate for them, so that they will be 1.7617 + // reinserted when new content is added. 1.7618 + 1.7619 + // remember if we are in css mode 1.7620 + NS_ENSURE_STATE(mHTMLEditor); 1.7621 + bool useCSS = mHTMLEditor->IsCSSEnabled(); 1.7622 + 1.7623 + // get selection point; if it doesn't exist, we have nothing to do 1.7624 + NS_ENSURE_STATE(mHTMLEditor); 1.7625 + nsRefPtr<Selection> selection = mHTMLEditor->GetSelection(); 1.7626 + MOZ_ASSERT(selection); 1.7627 + if (!selection->GetRangeCount()) { 1.7628 + // Nothing to do 1.7629 + return NS_OK; 1.7630 + } 1.7631 + nsCOMPtr<nsIContent> selNode = 1.7632 + do_QueryInterface(selection->GetRangeAt(0)->GetStartParent()); 1.7633 + if (!selNode) { 1.7634 + // Nothing to do 1.7635 + return NS_OK; 1.7636 + } 1.7637 + 1.7638 + for (int32_t i = 0; i < SIZE_STYLE_TABLE; ++i) { 1.7639 + if (mCachedStyles[i].mPresent) { 1.7640 + bool bFirst, bAny, bAll; 1.7641 + bFirst = bAny = bAll = false; 1.7642 + 1.7643 + nsAutoString curValue; 1.7644 + if (useCSS) { 1.7645 + // check computed style first in css case 1.7646 + NS_ENSURE_STATE(mHTMLEditor); 1.7647 + bAny = mHTMLEditor->mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet( 1.7648 + selNode, mCachedStyles[i].tag, &(mCachedStyles[i].attr), curValue, 1.7649 + nsHTMLCSSUtils::eComputed); 1.7650 + } 1.7651 + if (!bAny) { 1.7652 + // then check typeinstate and html style 1.7653 + NS_ENSURE_STATE(mHTMLEditor); 1.7654 + nsresult res = mHTMLEditor->GetInlinePropertyBase(mCachedStyles[i].tag, 1.7655 + &(mCachedStyles[i].attr), 1.7656 + &(mCachedStyles[i].value), 1.7657 + &bFirst, &bAny, &bAll, 1.7658 + &curValue, false); 1.7659 + NS_ENSURE_SUCCESS(res, res); 1.7660 + } 1.7661 + // this style has disappeared through deletion. Add to our typeinstate: 1.7662 + if (!bAny || IsStyleCachePreservingAction(mTheAction)) { 1.7663 + NS_ENSURE_STATE(mHTMLEditor); 1.7664 + mHTMLEditor->mTypeInState->SetProp(mCachedStyles[i].tag, 1.7665 + mCachedStyles[i].attr, 1.7666 + mCachedStyles[i].value); 1.7667 + } 1.7668 + } 1.7669 + } 1.7670 + 1.7671 + return NS_OK; 1.7672 +} 1.7673 + 1.7674 + 1.7675 +void 1.7676 +nsHTMLEditRules::ClearCachedStyles() 1.7677 +{ 1.7678 + // clear the mPresent bits in mCachedStyles array 1.7679 + for (uint32_t j = 0; j < SIZE_STYLE_TABLE; j++) { 1.7680 + mCachedStyles[j].mPresent = false; 1.7681 + mCachedStyles[j].value.Truncate(); 1.7682 + } 1.7683 +} 1.7684 + 1.7685 + 1.7686 +nsresult 1.7687 +nsHTMLEditRules::AdjustSpecialBreaks(bool aSafeToAskFrames) 1.7688 +{ 1.7689 + nsCOMArray<nsIDOMNode> arrayOfNodes; 1.7690 + nsCOMPtr<nsISupports> isupports; 1.7691 + int32_t nodeCount,j; 1.7692 + 1.7693 + // gather list of empty nodes 1.7694 + NS_ENSURE_STATE(mHTMLEditor); 1.7695 + nsEmptyEditableFunctor functor(mHTMLEditor); 1.7696 + nsDOMIterator iter; 1.7697 + nsresult res = iter.Init(mDocChangeRange); 1.7698 + NS_ENSURE_SUCCESS(res, res); 1.7699 + res = iter.AppendList(functor, arrayOfNodes); 1.7700 + NS_ENSURE_SUCCESS(res, res); 1.7701 + 1.7702 + // put moz-br's into these empty li's and td's 1.7703 + nodeCount = arrayOfNodes.Count(); 1.7704 + for (j = 0; j < nodeCount; j++) 1.7705 + { 1.7706 + // need to put br at END of node. It may have 1.7707 + // empty containers in it and still pass the "IsEmptynode" test, 1.7708 + // and we want the br's to be after them. Also, we want the br 1.7709 + // to be after the selection if the selection is in this node. 1.7710 + uint32_t len; 1.7711 + nsCOMPtr<nsIDOMNode> theNode = arrayOfNodes[0]; 1.7712 + arrayOfNodes.RemoveObjectAt(0); 1.7713 + res = nsEditor::GetLengthOfDOMNode(theNode, len); 1.7714 + NS_ENSURE_SUCCESS(res, res); 1.7715 + res = CreateMozBR(theNode, (int32_t)len); 1.7716 + NS_ENSURE_SUCCESS(res, res); 1.7717 + } 1.7718 + 1.7719 + return res; 1.7720 +} 1.7721 + 1.7722 +nsresult 1.7723 +nsHTMLEditRules::AdjustWhitespace(nsISelection *aSelection) 1.7724 +{ 1.7725 + // get selection point 1.7726 + nsCOMPtr<nsIDOMNode> selNode; 1.7727 + int32_t selOffset; 1.7728 + NS_ENSURE_STATE(mHTMLEditor); 1.7729 + nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset); 1.7730 + NS_ENSURE_SUCCESS(res, res); 1.7731 + 1.7732 + // ask whitespace object to tweak nbsp's 1.7733 + NS_ENSURE_STATE(mHTMLEditor); 1.7734 + return nsWSRunObject(mHTMLEditor, selNode, selOffset).AdjustWhitespace(); 1.7735 +} 1.7736 + 1.7737 +nsresult 1.7738 +nsHTMLEditRules::PinSelectionToNewBlock(nsISelection *aSelection) 1.7739 +{ 1.7740 + NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER); 1.7741 + if (!aSelection->Collapsed()) { 1.7742 + return NS_OK; 1.7743 + } 1.7744 + 1.7745 + // get the (collapsed) selection location 1.7746 + nsCOMPtr<nsIDOMNode> selNode, temp; 1.7747 + int32_t selOffset; 1.7748 + NS_ENSURE_STATE(mHTMLEditor); 1.7749 + nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset); 1.7750 + NS_ENSURE_SUCCESS(res, res); 1.7751 + temp = selNode; 1.7752 + 1.7753 + // use ranges and sRangeHelper to compare sel point to new block 1.7754 + nsCOMPtr<nsINode> node = do_QueryInterface(selNode); 1.7755 + NS_ENSURE_STATE(node); 1.7756 + nsRefPtr<nsRange> range = new nsRange(node); 1.7757 + res = range->SetStart(selNode, selOffset); 1.7758 + NS_ENSURE_SUCCESS(res, res); 1.7759 + res = range->SetEnd(selNode, selOffset); 1.7760 + NS_ENSURE_SUCCESS(res, res); 1.7761 + nsCOMPtr<nsIContent> block (do_QueryInterface(mNewBlock)); 1.7762 + NS_ENSURE_TRUE(block, NS_ERROR_NO_INTERFACE); 1.7763 + bool nodeBefore, nodeAfter; 1.7764 + res = nsRange::CompareNodeToRange(block, range, &nodeBefore, &nodeAfter); 1.7765 + NS_ENSURE_SUCCESS(res, res); 1.7766 + 1.7767 + if (nodeBefore && nodeAfter) 1.7768 + return NS_OK; // selection is inside block 1.7769 + else if (nodeBefore) 1.7770 + { 1.7771 + // selection is after block. put at end of block. 1.7772 + nsCOMPtr<nsIDOMNode> tmp = mNewBlock; 1.7773 + NS_ENSURE_STATE(mHTMLEditor); 1.7774 + mHTMLEditor->GetLastEditableChild(mNewBlock, address_of(tmp)); 1.7775 + uint32_t endPoint; 1.7776 + NS_ENSURE_STATE(mHTMLEditor); 1.7777 + if (mHTMLEditor->IsTextNode(tmp) || !mHTMLEditor || 1.7778 + mHTMLEditor->IsContainer(tmp)) 1.7779 + { 1.7780 + NS_ENSURE_STATE(mHTMLEditor); 1.7781 + res = nsEditor::GetLengthOfDOMNode(tmp, endPoint); 1.7782 + NS_ENSURE_SUCCESS(res, res); 1.7783 + } 1.7784 + else 1.7785 + { 1.7786 + tmp = nsEditor::GetNodeLocation(tmp, (int32_t*)&endPoint); 1.7787 + endPoint++; // want to be after this node 1.7788 + } 1.7789 + return aSelection->Collapse(tmp, (int32_t)endPoint); 1.7790 + } 1.7791 + else 1.7792 + { 1.7793 + // selection is before block. put at start of block. 1.7794 + nsCOMPtr<nsIDOMNode> tmp = mNewBlock; 1.7795 + NS_ENSURE_STATE(mHTMLEditor); 1.7796 + mHTMLEditor->GetFirstEditableChild(mNewBlock, address_of(tmp)); 1.7797 + int32_t offset; 1.7798 + if (!(mHTMLEditor->IsTextNode(tmp) || !mHTMLEditor || 1.7799 + mHTMLEditor->IsContainer(tmp))) 1.7800 + { 1.7801 + tmp = nsEditor::GetNodeLocation(tmp, &offset); 1.7802 + } 1.7803 + NS_ENSURE_STATE(mHTMLEditor); 1.7804 + return aSelection->Collapse(tmp, 0); 1.7805 + } 1.7806 +} 1.7807 + 1.7808 +nsresult 1.7809 +nsHTMLEditRules::CheckInterlinePosition(nsISelection *aSelection) 1.7810 +{ 1.7811 + NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER); 1.7812 + nsCOMPtr<nsISelection> selection(aSelection); 1.7813 + nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection)); 1.7814 + 1.7815 + // if the selection isn't collapsed, do nothing. 1.7816 + if (!aSelection->Collapsed()) { 1.7817 + return NS_OK; 1.7818 + } 1.7819 + 1.7820 + // get the (collapsed) selection location 1.7821 + nsCOMPtr<nsIDOMNode> selNode, node; 1.7822 + int32_t selOffset; 1.7823 + NS_ENSURE_STATE(mHTMLEditor); 1.7824 + nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset); 1.7825 + NS_ENSURE_SUCCESS(res, res); 1.7826 + 1.7827 + // First, let's check to see if we are after a <br>. We take care of this 1.7828 + // special-case first so that we don't accidentally fall through into one 1.7829 + // of the other conditionals. 1.7830 + NS_ENSURE_STATE(mHTMLEditor); 1.7831 + mHTMLEditor->GetPriorHTMLNode(selNode, selOffset, address_of(node), true); 1.7832 + if (node && nsTextEditUtils::IsBreak(node)) 1.7833 + { 1.7834 + selPriv->SetInterlinePosition(true); 1.7835 + return NS_OK; 1.7836 + } 1.7837 + 1.7838 + // are we after a block? If so try set caret to following content 1.7839 + NS_ENSURE_STATE(mHTMLEditor); 1.7840 + mHTMLEditor->GetPriorHTMLSibling(selNode, selOffset, address_of(node)); 1.7841 + if (node && IsBlockNode(node)) 1.7842 + { 1.7843 + selPriv->SetInterlinePosition(true); 1.7844 + return NS_OK; 1.7845 + } 1.7846 + 1.7847 + // are we before a block? If so try set caret to prior content 1.7848 + NS_ENSURE_STATE(mHTMLEditor); 1.7849 + mHTMLEditor->GetNextHTMLSibling(selNode, selOffset, address_of(node)); 1.7850 + if (node && IsBlockNode(node)) 1.7851 + selPriv->SetInterlinePosition(false); 1.7852 + return NS_OK; 1.7853 +} 1.7854 + 1.7855 +nsresult 1.7856 +nsHTMLEditRules::AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection aAction) 1.7857 +{ 1.7858 + NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER); 1.7859 + nsCOMPtr<nsISelection> selection(aSelection); 1.7860 + nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection)); 1.7861 + 1.7862 + // if the selection isn't collapsed, do nothing. 1.7863 + // moose: one thing to do instead is check for the case of 1.7864 + // only a single break selected, and collapse it. Good thing? Beats me. 1.7865 + if (!aSelection->Collapsed()) { 1.7866 + return NS_OK; 1.7867 + } 1.7868 + 1.7869 + // get the (collapsed) selection location 1.7870 + nsCOMPtr<nsIDOMNode> selNode, temp; 1.7871 + int32_t selOffset; 1.7872 + NS_ENSURE_STATE(mHTMLEditor); 1.7873 + nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset); 1.7874 + NS_ENSURE_SUCCESS(res, res); 1.7875 + temp = selNode; 1.7876 + 1.7877 + // are we in an editable node? 1.7878 + NS_ENSURE_STATE(mHTMLEditor); 1.7879 + while (!mHTMLEditor->IsEditable(selNode)) 1.7880 + { 1.7881 + // scan up the tree until we find an editable place to be 1.7882 + selNode = nsEditor::GetNodeLocation(temp, &selOffset); 1.7883 + NS_ENSURE_TRUE(selNode, NS_ERROR_FAILURE); 1.7884 + temp = selNode; 1.7885 + NS_ENSURE_STATE(mHTMLEditor); 1.7886 + } 1.7887 + 1.7888 + // make sure we aren't in an empty block - user will see no cursor. If this 1.7889 + // is happening, put a <br> in the block if allowed. 1.7890 + nsCOMPtr<nsIDOMNode> theblock; 1.7891 + if (IsBlockNode(selNode)) { 1.7892 + theblock = selNode; 1.7893 + } else { 1.7894 + NS_ENSURE_STATE(mHTMLEditor); 1.7895 + theblock = mHTMLEditor->GetBlockNodeParent(selNode); 1.7896 + } 1.7897 + NS_ENSURE_STATE(mHTMLEditor); 1.7898 + if (theblock && mHTMLEditor->IsEditable(theblock)) { 1.7899 + bool bIsEmptyNode; 1.7900 + NS_ENSURE_STATE(mHTMLEditor); 1.7901 + res = mHTMLEditor->IsEmptyNode(theblock, &bIsEmptyNode, false, false); 1.7902 + NS_ENSURE_SUCCESS(res, res); 1.7903 + // check if br can go into the destination node 1.7904 + NS_ENSURE_STATE(mHTMLEditor); 1.7905 + if (bIsEmptyNode && mHTMLEditor->CanContainTag(selNode, nsGkAtoms::br)) { 1.7906 + NS_ENSURE_STATE(mHTMLEditor); 1.7907 + nsCOMPtr<nsIDOMNode> rootNode = do_QueryInterface(mHTMLEditor->GetRoot()); 1.7908 + NS_ENSURE_TRUE(rootNode, NS_ERROR_FAILURE); 1.7909 + if (selNode == rootNode) 1.7910 + { 1.7911 + // Our root node is completely empty. Don't add a <br> here. 1.7912 + // AfterEditInner() will add one for us when it calls 1.7913 + // CreateBogusNodeIfNeeded()! 1.7914 + return NS_OK; 1.7915 + } 1.7916 + 1.7917 + // we know we can skip the rest of this routine given the cirumstance 1.7918 + return CreateMozBR(selNode, selOffset); 1.7919 + } 1.7920 + } 1.7921 + 1.7922 + // are we in a text node? 1.7923 + nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(selNode); 1.7924 + if (textNode) 1.7925 + return NS_OK; // we LIKE it when we are in a text node. that RULZ 1.7926 + 1.7927 + // do we need to insert a special mozBR? We do if we are: 1.7928 + // 1) prior node is in same block where selection is AND 1.7929 + // 2) prior node is a br AND 1.7930 + // 3) that br is not visible 1.7931 + 1.7932 + nsCOMPtr<nsIDOMNode> nearNode; 1.7933 + NS_ENSURE_STATE(mHTMLEditor); 1.7934 + res = mHTMLEditor->GetPriorHTMLNode(selNode, selOffset, address_of(nearNode)); 1.7935 + NS_ENSURE_SUCCESS(res, res); 1.7936 + if (nearNode) 1.7937 + { 1.7938 + // is nearNode also a descendant of same block? 1.7939 + nsCOMPtr<nsIDOMNode> block, nearBlock; 1.7940 + if (IsBlockNode(selNode)) { 1.7941 + block = selNode; 1.7942 + } else { 1.7943 + NS_ENSURE_STATE(mHTMLEditor); 1.7944 + block = mHTMLEditor->GetBlockNodeParent(selNode); 1.7945 + } 1.7946 + NS_ENSURE_STATE(mHTMLEditor); 1.7947 + nearBlock = mHTMLEditor->GetBlockNodeParent(nearNode); 1.7948 + if (block == nearBlock) 1.7949 + { 1.7950 + if (nearNode && nsTextEditUtils::IsBreak(nearNode) ) 1.7951 + { 1.7952 + NS_ENSURE_STATE(mHTMLEditor); 1.7953 + if (!mHTMLEditor->IsVisBreak(nearNode)) 1.7954 + { 1.7955 + // need to insert special moz BR. Why? Because if we don't 1.7956 + // the user will see no new line for the break. Also, things 1.7957 + // like table cells won't grow in height. 1.7958 + nsCOMPtr<nsIDOMNode> brNode; 1.7959 + res = CreateMozBR(selNode, selOffset, getter_AddRefs(brNode)); 1.7960 + NS_ENSURE_SUCCESS(res, res); 1.7961 + selNode = nsEditor::GetNodeLocation(brNode, &selOffset); 1.7962 + // selection stays *before* moz-br, sticking to it 1.7963 + selPriv->SetInterlinePosition(true); 1.7964 + res = aSelection->Collapse(selNode,selOffset); 1.7965 + NS_ENSURE_SUCCESS(res, res); 1.7966 + } 1.7967 + else 1.7968 + { 1.7969 + nsCOMPtr<nsIDOMNode> nextNode; 1.7970 + NS_ENSURE_STATE(mHTMLEditor); 1.7971 + mHTMLEditor->GetNextHTMLNode(nearNode, address_of(nextNode), true); 1.7972 + if (nextNode && nsTextEditUtils::IsMozBR(nextNode)) 1.7973 + { 1.7974 + // selection between br and mozbr. make it stick to mozbr 1.7975 + // so that it will be on blank line. 1.7976 + selPriv->SetInterlinePosition(true); 1.7977 + } 1.7978 + } 1.7979 + } 1.7980 + } 1.7981 + } 1.7982 + 1.7983 + // we aren't in a textnode: are we adjacent to text or a break or an image? 1.7984 + NS_ENSURE_STATE(mHTMLEditor); 1.7985 + res = mHTMLEditor->GetPriorHTMLNode(selNode, selOffset, address_of(nearNode), true); 1.7986 + NS_ENSURE_SUCCESS(res, res); 1.7987 + if (nearNode && (nsTextEditUtils::IsBreak(nearNode) 1.7988 + || nsEditor::IsTextNode(nearNode) 1.7989 + || nsHTMLEditUtils::IsImage(nearNode) 1.7990 + || nsHTMLEditUtils::IsHR(nearNode))) 1.7991 + return NS_OK; // this is a good place for the caret to be 1.7992 + NS_ENSURE_STATE(mHTMLEditor); 1.7993 + res = mHTMLEditor->GetNextHTMLNode(selNode, selOffset, address_of(nearNode), true); 1.7994 + NS_ENSURE_SUCCESS(res, res); 1.7995 + if (nearNode && (nsTextEditUtils::IsBreak(nearNode) 1.7996 + || nsEditor::IsTextNode(nearNode) 1.7997 + || nsHTMLEditUtils::IsImage(nearNode) 1.7998 + || nsHTMLEditUtils::IsHR(nearNode))) 1.7999 + return NS_OK; // this is a good place for the caret to be 1.8000 + 1.8001 + // look for a nearby text node. 1.8002 + // prefer the correct direction. 1.8003 + res = FindNearSelectableNode(selNode, selOffset, aAction, address_of(nearNode)); 1.8004 + NS_ENSURE_SUCCESS(res, res); 1.8005 + 1.8006 + if (nearNode) 1.8007 + { 1.8008 + // is the nearnode a text node? 1.8009 + textNode = do_QueryInterface(nearNode); 1.8010 + if (textNode) 1.8011 + { 1.8012 + int32_t offset = 0; 1.8013 + // put selection in right place: 1.8014 + if (aAction == nsIEditor::ePrevious) 1.8015 + textNode->GetLength((uint32_t*)&offset); 1.8016 + res = aSelection->Collapse(nearNode,offset); 1.8017 + } 1.8018 + else // must be break or image 1.8019 + { 1.8020 + selNode = nsEditor::GetNodeLocation(nearNode, &selOffset); 1.8021 + if (aAction == nsIEditor::ePrevious) selOffset++; // want to be beyond it if we backed up to it 1.8022 + res = aSelection->Collapse(selNode, selOffset); 1.8023 + } 1.8024 + } 1.8025 + return res; 1.8026 +} 1.8027 + 1.8028 + 1.8029 +nsresult 1.8030 +nsHTMLEditRules::FindNearSelectableNode(nsIDOMNode *aSelNode, 1.8031 + int32_t aSelOffset, 1.8032 + nsIEditor::EDirection &aDirection, 1.8033 + nsCOMPtr<nsIDOMNode> *outSelectableNode) 1.8034 +{ 1.8035 + NS_ENSURE_TRUE(aSelNode && outSelectableNode, NS_ERROR_NULL_POINTER); 1.8036 + *outSelectableNode = nullptr; 1.8037 + nsresult res = NS_OK; 1.8038 + 1.8039 + nsCOMPtr<nsIDOMNode> nearNode, curNode; 1.8040 + if (aDirection == nsIEditor::ePrevious) { 1.8041 + NS_ENSURE_STATE(mHTMLEditor); 1.8042 + res = mHTMLEditor->GetPriorHTMLNode(aSelNode, aSelOffset, address_of(nearNode)); 1.8043 + } else { 1.8044 + NS_ENSURE_STATE(mHTMLEditor); 1.8045 + res = mHTMLEditor->GetNextHTMLNode(aSelNode, aSelOffset, address_of(nearNode)); 1.8046 + } 1.8047 + NS_ENSURE_SUCCESS(res, res); 1.8048 + 1.8049 + if (!nearNode) // try the other direction then 1.8050 + { 1.8051 + if (aDirection == nsIEditor::ePrevious) 1.8052 + aDirection = nsIEditor::eNext; 1.8053 + else 1.8054 + aDirection = nsIEditor::ePrevious; 1.8055 + 1.8056 + if (aDirection == nsIEditor::ePrevious) { 1.8057 + NS_ENSURE_STATE(mHTMLEditor); 1.8058 + res = mHTMLEditor->GetPriorHTMLNode(aSelNode, aSelOffset, address_of(nearNode)); 1.8059 + } else { 1.8060 + NS_ENSURE_STATE(mHTMLEditor); 1.8061 + res = mHTMLEditor->GetNextHTMLNode(aSelNode, aSelOffset, address_of(nearNode)); 1.8062 + } 1.8063 + NS_ENSURE_SUCCESS(res, res); 1.8064 + } 1.8065 + 1.8066 + // scan in the right direction until we find an eligible text node, 1.8067 + // but don't cross any breaks, images, or table elements. 1.8068 + NS_ENSURE_STATE(mHTMLEditor); 1.8069 + while (nearNode && !(mHTMLEditor->IsTextNode(nearNode) 1.8070 + || nsTextEditUtils::IsBreak(nearNode) 1.8071 + || nsHTMLEditUtils::IsImage(nearNode))) 1.8072 + { 1.8073 + curNode = nearNode; 1.8074 + if (aDirection == nsIEditor::ePrevious) { 1.8075 + NS_ENSURE_STATE(mHTMLEditor); 1.8076 + res = mHTMLEditor->GetPriorHTMLNode(curNode, address_of(nearNode)); 1.8077 + } else { 1.8078 + NS_ENSURE_STATE(mHTMLEditor); 1.8079 + res = mHTMLEditor->GetNextHTMLNode(curNode, address_of(nearNode)); 1.8080 + } 1.8081 + NS_ENSURE_SUCCESS(res, res); 1.8082 + NS_ENSURE_STATE(mHTMLEditor); 1.8083 + } 1.8084 + 1.8085 + if (nearNode) 1.8086 + { 1.8087 + // don't cross any table elements 1.8088 + if (InDifferentTableElements(nearNode, aSelNode)) { 1.8089 + return NS_OK; 1.8090 + } 1.8091 + 1.8092 + // otherwise, ok, we have found a good spot to put the selection 1.8093 + *outSelectableNode = do_QueryInterface(nearNode); 1.8094 + } 1.8095 + return res; 1.8096 +} 1.8097 + 1.8098 + 1.8099 +bool nsHTMLEditRules::InDifferentTableElements(nsIDOMNode* aNode1, 1.8100 + nsIDOMNode* aNode2) 1.8101 +{ 1.8102 + nsCOMPtr<nsINode> node1 = do_QueryInterface(aNode1); 1.8103 + nsCOMPtr<nsINode> node2 = do_QueryInterface(aNode2); 1.8104 + return InDifferentTableElements(node1, node2); 1.8105 +} 1.8106 + 1.8107 +bool 1.8108 +nsHTMLEditRules::InDifferentTableElements(nsINode* aNode1, nsINode* aNode2) 1.8109 +{ 1.8110 + MOZ_ASSERT(aNode1 && aNode2); 1.8111 + 1.8112 + while (aNode1 && !nsHTMLEditUtils::IsTableElement(aNode1)) { 1.8113 + aNode1 = aNode1->GetParentNode(); 1.8114 + } 1.8115 + 1.8116 + while (aNode2 && !nsHTMLEditUtils::IsTableElement(aNode2)) { 1.8117 + aNode2 = aNode2->GetParentNode(); 1.8118 + } 1.8119 + 1.8120 + return aNode1 != aNode2; 1.8121 +} 1.8122 + 1.8123 + 1.8124 +nsresult 1.8125 +nsHTMLEditRules::RemoveEmptyNodes() 1.8126 +{ 1.8127 + // some general notes on the algorithm used here: the goal is to examine all the 1.8128 + // nodes in mDocChangeRange, and remove the empty ones. We do this by using a 1.8129 + // content iterator to traverse all the nodes in the range, and placing the empty 1.8130 + // nodes into an array. After finishing the iteration, we delete the empty nodes 1.8131 + // in the array. (they cannot be deleted as we find them becasue that would 1.8132 + // invalidate the iterator.) 1.8133 + // Since checking to see if a node is empty can be costly for nodes with many 1.8134 + // descendants, there are some optimizations made. I rely on the fact that the 1.8135 + // iterator is post-order: it will visit children of a node before visiting the 1.8136 + // parent node. So if I find that a child node is not empty, I know that its 1.8137 + // parent is not empty without even checking. So I put the parent on a "skipList" 1.8138 + // which is just a voidArray of nodes I can skip the empty check on. If I 1.8139 + // encounter a node on the skiplist, i skip the processing for that node and replace 1.8140 + // its slot in the skiplist with that node's parent. 1.8141 + // An interseting idea is to go ahead and regard parent nodes that are NOT on the 1.8142 + // skiplist as being empty (without even doing the IsEmptyNode check) on the theory 1.8143 + // that if they weren't empty, we would have encountered a non-empty child earlier 1.8144 + // and thus put this parent node on the skiplist. 1.8145 + // Unfortunately I can't use that strategy here, because the range may include 1.8146 + // some children of a node while excluding others. Thus I could find all the 1.8147 + // _examined_ children empty, but still not have an empty parent. 1.8148 + 1.8149 + // need an iterator 1.8150 + nsCOMPtr<nsIContentIterator> iter = 1.8151 + do_CreateInstance("@mozilla.org/content/post-content-iterator;1"); 1.8152 + NS_ENSURE_TRUE(iter, NS_ERROR_NULL_POINTER); 1.8153 + 1.8154 + nsresult res = iter->Init(mDocChangeRange); 1.8155 + NS_ENSURE_SUCCESS(res, res); 1.8156 + 1.8157 + nsCOMArray<nsINode> arrayOfEmptyNodes, arrayOfEmptyCites; 1.8158 + nsTArray<nsCOMPtr<nsINode> > skipList; 1.8159 + 1.8160 + // check for empty nodes 1.8161 + while (!iter->IsDone()) { 1.8162 + nsINode* node = iter->GetCurrentNode(); 1.8163 + NS_ENSURE_TRUE(node, NS_ERROR_FAILURE); 1.8164 + 1.8165 + nsINode* parent = node->GetParentNode(); 1.8166 + 1.8167 + uint32_t idx = skipList.IndexOf(node); 1.8168 + if (idx != skipList.NoIndex) { 1.8169 + // this node is on our skip list. Skip processing for this node, 1.8170 + // and replace its value in the skip list with the value of its parent 1.8171 + skipList[idx] = parent; 1.8172 + } else { 1.8173 + bool bIsCandidate = false; 1.8174 + bool bIsEmptyNode = false; 1.8175 + bool bIsMailCite = false; 1.8176 + 1.8177 + if (node->IsElement()) { 1.8178 + dom::Element* element = node->AsElement(); 1.8179 + if (element->IsHTML(nsGkAtoms::body)) { 1.8180 + // don't delete the body 1.8181 + } else if ((bIsMailCite = nsHTMLEditUtils::IsMailCite(element)) || 1.8182 + element->IsHTML(nsGkAtoms::a) || 1.8183 + nsHTMLEditUtils::IsInlineStyle(element) || 1.8184 + nsHTMLEditUtils::IsList(element) || 1.8185 + element->IsHTML(nsGkAtoms::div)) { 1.8186 + // only consider certain nodes to be empty for purposes of removal 1.8187 + bIsCandidate = true; 1.8188 + } else if (nsHTMLEditUtils::IsFormatNode(element) || 1.8189 + nsHTMLEditUtils::IsListItem(element) || 1.8190 + element->IsHTML(nsGkAtoms::blockquote)) { 1.8191 + // these node types are candidates if selection is not in them 1.8192 + // if it is one of these, don't delete if selection inside. 1.8193 + // this is so we can create empty headings, etc, for the 1.8194 + // user to type into. 1.8195 + bool bIsSelInNode; 1.8196 + res = SelectionEndpointInNode(node, &bIsSelInNode); 1.8197 + NS_ENSURE_SUCCESS(res, res); 1.8198 + if (!bIsSelInNode) 1.8199 + { 1.8200 + bIsCandidate = true; 1.8201 + } 1.8202 + } 1.8203 + } 1.8204 + 1.8205 + if (bIsCandidate) { 1.8206 + // we delete mailcites even if they have a solo br in them 1.8207 + // other nodes we require to be empty 1.8208 + NS_ENSURE_STATE(mHTMLEditor); 1.8209 + res = mHTMLEditor->IsEmptyNode(node->AsDOMNode(), &bIsEmptyNode, 1.8210 + bIsMailCite, true); 1.8211 + NS_ENSURE_SUCCESS(res, res); 1.8212 + if (bIsEmptyNode) { 1.8213 + if (bIsMailCite) { 1.8214 + // mailcites go on a separate list from other empty nodes 1.8215 + arrayOfEmptyCites.AppendObject(node); 1.8216 + } else { 1.8217 + arrayOfEmptyNodes.AppendObject(node); 1.8218 + } 1.8219 + } 1.8220 + } 1.8221 + 1.8222 + if (!bIsEmptyNode) { 1.8223 + // put parent on skip list 1.8224 + skipList.AppendElement(parent); 1.8225 + } 1.8226 + } 1.8227 + 1.8228 + iter->Next(); 1.8229 + } 1.8230 + 1.8231 + // now delete the empty nodes 1.8232 + int32_t nodeCount = arrayOfEmptyNodes.Count(); 1.8233 + for (int32_t j = 0; j < nodeCount; j++) { 1.8234 + nsCOMPtr<nsIDOMNode> delNode = arrayOfEmptyNodes[0]->AsDOMNode(); 1.8235 + arrayOfEmptyNodes.RemoveObjectAt(0); 1.8236 + NS_ENSURE_STATE(mHTMLEditor); 1.8237 + if (mHTMLEditor->IsModifiableNode(delNode)) { 1.8238 + NS_ENSURE_STATE(mHTMLEditor); 1.8239 + res = mHTMLEditor->DeleteNode(delNode); 1.8240 + NS_ENSURE_SUCCESS(res, res); 1.8241 + } 1.8242 + } 1.8243 + 1.8244 + // now delete the empty mailcites 1.8245 + // this is a separate step because we want to pull out any br's and preserve them. 1.8246 + nodeCount = arrayOfEmptyCites.Count(); 1.8247 + for (int32_t j = 0; j < nodeCount; j++) { 1.8248 + nsCOMPtr<nsIDOMNode> delNode = arrayOfEmptyCites[0]->AsDOMNode(); 1.8249 + arrayOfEmptyCites.RemoveObjectAt(0); 1.8250 + bool bIsEmptyNode; 1.8251 + NS_ENSURE_STATE(mHTMLEditor); 1.8252 + res = mHTMLEditor->IsEmptyNode(delNode, &bIsEmptyNode, false, true); 1.8253 + NS_ENSURE_SUCCESS(res, res); 1.8254 + if (!bIsEmptyNode) 1.8255 + { 1.8256 + // we are deleting a cite that has just a br. We want to delete cite, 1.8257 + // but preserve br. 1.8258 + nsCOMPtr<nsIDOMNode> parent, brNode; 1.8259 + int32_t offset; 1.8260 + parent = nsEditor::GetNodeLocation(delNode, &offset); 1.8261 + NS_ENSURE_STATE(mHTMLEditor); 1.8262 + res = mHTMLEditor->CreateBR(parent, offset, address_of(brNode)); 1.8263 + NS_ENSURE_SUCCESS(res, res); 1.8264 + } 1.8265 + NS_ENSURE_STATE(mHTMLEditor); 1.8266 + res = mHTMLEditor->DeleteNode(delNode); 1.8267 + NS_ENSURE_SUCCESS(res, res); 1.8268 + } 1.8269 + 1.8270 + return res; 1.8271 +} 1.8272 + 1.8273 +nsresult 1.8274 +nsHTMLEditRules::SelectionEndpointInNode(nsINode* aNode, bool* aResult) 1.8275 +{ 1.8276 + NS_ENSURE_TRUE(aNode && aResult, NS_ERROR_NULL_POINTER); 1.8277 + 1.8278 + nsIDOMNode* node = aNode->AsDOMNode(); 1.8279 + 1.8280 + *aResult = false; 1.8281 + 1.8282 + nsCOMPtr<nsISelection>selection; 1.8283 + NS_ENSURE_STATE(mHTMLEditor); 1.8284 + nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection)); 1.8285 + NS_ENSURE_SUCCESS(res, res); 1.8286 + 1.8287 + Selection* sel = static_cast<Selection*>(selection.get()); 1.8288 + uint32_t rangeCount = sel->GetRangeCount(); 1.8289 + for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) { 1.8290 + nsRefPtr<nsRange> range = sel->GetRangeAt(rangeIdx); 1.8291 + nsCOMPtr<nsIDOMNode> startParent, endParent; 1.8292 + range->GetStartContainer(getter_AddRefs(startParent)); 1.8293 + if (startParent) 1.8294 + { 1.8295 + if (node == startParent) { 1.8296 + *aResult = true; 1.8297 + return NS_OK; 1.8298 + } 1.8299 + if (nsEditorUtils::IsDescendantOf(startParent, node)) { 1.8300 + *aResult = true; 1.8301 + return NS_OK; 1.8302 + } 1.8303 + } 1.8304 + range->GetEndContainer(getter_AddRefs(endParent)); 1.8305 + if (startParent == endParent) continue; 1.8306 + if (endParent) 1.8307 + { 1.8308 + if (node == endParent) { 1.8309 + *aResult = true; 1.8310 + return NS_OK; 1.8311 + } 1.8312 + if (nsEditorUtils::IsDescendantOf(endParent, node)) { 1.8313 + *aResult = true; 1.8314 + return NS_OK; 1.8315 + } 1.8316 + } 1.8317 + } 1.8318 + return res; 1.8319 +} 1.8320 + 1.8321 +/////////////////////////////////////////////////////////////////////////// 1.8322 +// IsEmptyInline: return true if aNode is an empty inline container 1.8323 +// 1.8324 +// 1.8325 +bool 1.8326 +nsHTMLEditRules::IsEmptyInline(nsIDOMNode *aNode) 1.8327 +{ 1.8328 + if (aNode && IsInlineNode(aNode) && mHTMLEditor && 1.8329 + mHTMLEditor->IsContainer(aNode)) 1.8330 + { 1.8331 + bool bEmpty; 1.8332 + NS_ENSURE_TRUE(mHTMLEditor, false); 1.8333 + mHTMLEditor->IsEmptyNode(aNode, &bEmpty); 1.8334 + return bEmpty; 1.8335 + } 1.8336 + return false; 1.8337 +} 1.8338 + 1.8339 + 1.8340 +bool 1.8341 +nsHTMLEditRules::ListIsEmptyLine(nsCOMArray<nsIDOMNode> &arrayOfNodes) 1.8342 +{ 1.8343 + // we have a list of nodes which we are candidates for being moved 1.8344 + // into a new block. Determine if it's anything more than a blank line. 1.8345 + // Look for editable content above and beyond one single BR. 1.8346 + int32_t listCount = arrayOfNodes.Count(); 1.8347 + NS_ENSURE_TRUE(listCount, true); 1.8348 + nsCOMPtr<nsIDOMNode> somenode; 1.8349 + int32_t j, brCount=0; 1.8350 + for (j = 0; j < listCount; j++) 1.8351 + { 1.8352 + somenode = arrayOfNodes[j]; 1.8353 + NS_ENSURE_TRUE(mHTMLEditor, false); 1.8354 + if (somenode && mHTMLEditor->IsEditable(somenode)) 1.8355 + { 1.8356 + if (nsTextEditUtils::IsBreak(somenode)) 1.8357 + { 1.8358 + // first break doesn't count 1.8359 + if (brCount) return false; 1.8360 + brCount++; 1.8361 + } 1.8362 + else if (IsEmptyInline(somenode)) 1.8363 + { 1.8364 + // empty inline, keep looking 1.8365 + } 1.8366 + else return false; 1.8367 + } 1.8368 + } 1.8369 + return true; 1.8370 +} 1.8371 + 1.8372 + 1.8373 +nsresult 1.8374 +nsHTMLEditRules::PopListItem(nsIDOMNode *aListItem, bool *aOutOfList) 1.8375 +{ 1.8376 + // check parms 1.8377 + NS_ENSURE_TRUE(aListItem && aOutOfList, NS_ERROR_NULL_POINTER); 1.8378 + 1.8379 + // init out params 1.8380 + *aOutOfList = false; 1.8381 + 1.8382 + nsCOMPtr<nsIDOMNode> curNode( do_QueryInterface(aListItem)); 1.8383 + int32_t offset; 1.8384 + nsCOMPtr<nsIDOMNode> curParent = nsEditor::GetNodeLocation(curNode, &offset); 1.8385 + 1.8386 + if (!nsHTMLEditUtils::IsListItem(curNode)) 1.8387 + return NS_ERROR_FAILURE; 1.8388 + 1.8389 + // if it's first or last list item, don't need to split the list 1.8390 + // otherwise we do. 1.8391 + int32_t parOffset; 1.8392 + nsCOMPtr<nsIDOMNode> curParPar = nsEditor::GetNodeLocation(curParent, &parOffset); 1.8393 + 1.8394 + bool bIsFirstListItem; 1.8395 + NS_ENSURE_STATE(mHTMLEditor); 1.8396 + nsresult res = mHTMLEditor->IsFirstEditableChild(curNode, &bIsFirstListItem); 1.8397 + NS_ENSURE_SUCCESS(res, res); 1.8398 + 1.8399 + bool bIsLastListItem; 1.8400 + NS_ENSURE_STATE(mHTMLEditor); 1.8401 + res = mHTMLEditor->IsLastEditableChild(curNode, &bIsLastListItem); 1.8402 + NS_ENSURE_SUCCESS(res, res); 1.8403 + 1.8404 + if (!bIsFirstListItem && !bIsLastListItem) 1.8405 + { 1.8406 + // split the list 1.8407 + nsCOMPtr<nsIDOMNode> newBlock; 1.8408 + NS_ENSURE_STATE(mHTMLEditor); 1.8409 + res = mHTMLEditor->SplitNode(curParent, offset, getter_AddRefs(newBlock)); 1.8410 + NS_ENSURE_SUCCESS(res, res); 1.8411 + } 1.8412 + 1.8413 + if (!bIsFirstListItem) parOffset++; 1.8414 + 1.8415 + NS_ENSURE_STATE(mHTMLEditor); 1.8416 + res = mHTMLEditor->MoveNode(curNode, curParPar, parOffset); 1.8417 + NS_ENSURE_SUCCESS(res, res); 1.8418 + 1.8419 + // unwrap list item contents if they are no longer in a list 1.8420 + if (!nsHTMLEditUtils::IsList(curParPar) 1.8421 + && nsHTMLEditUtils::IsListItem(curNode)) 1.8422 + { 1.8423 + NS_ENSURE_STATE(mHTMLEditor); 1.8424 + res = mHTMLEditor->RemoveBlockContainer(curNode); 1.8425 + NS_ENSURE_SUCCESS(res, res); 1.8426 + *aOutOfList = true; 1.8427 + } 1.8428 + return res; 1.8429 +} 1.8430 + 1.8431 +nsresult 1.8432 +nsHTMLEditRules::RemoveListStructure(nsIDOMNode *aList) 1.8433 +{ 1.8434 + NS_ENSURE_ARG_POINTER(aList); 1.8435 + 1.8436 + nsresult res; 1.8437 + 1.8438 + nsCOMPtr<nsIDOMNode> child; 1.8439 + aList->GetFirstChild(getter_AddRefs(child)); 1.8440 + 1.8441 + while (child) 1.8442 + { 1.8443 + if (nsHTMLEditUtils::IsListItem(child)) 1.8444 + { 1.8445 + bool bOutOfList; 1.8446 + do 1.8447 + { 1.8448 + res = PopListItem(child, &bOutOfList); 1.8449 + NS_ENSURE_SUCCESS(res, res); 1.8450 + } while (!bOutOfList); // keep popping it out until it's not in a list anymore 1.8451 + } 1.8452 + else if (nsHTMLEditUtils::IsList(child)) 1.8453 + { 1.8454 + res = RemoveListStructure(child); 1.8455 + NS_ENSURE_SUCCESS(res, res); 1.8456 + } 1.8457 + else 1.8458 + { 1.8459 + // delete any non- list items for now 1.8460 + NS_ENSURE_STATE(mHTMLEditor); 1.8461 + res = mHTMLEditor->DeleteNode(child); 1.8462 + NS_ENSURE_SUCCESS(res, res); 1.8463 + } 1.8464 + aList->GetFirstChild(getter_AddRefs(child)); 1.8465 + } 1.8466 + // delete the now-empty list 1.8467 + NS_ENSURE_STATE(mHTMLEditor); 1.8468 + res = mHTMLEditor->RemoveBlockContainer(aList); 1.8469 + NS_ENSURE_SUCCESS(res, res); 1.8470 + 1.8471 + return res; 1.8472 +} 1.8473 + 1.8474 + 1.8475 +nsresult 1.8476 +nsHTMLEditRules::ConfirmSelectionInBody() 1.8477 +{ 1.8478 + nsresult res = NS_OK; 1.8479 + 1.8480 + // get the body 1.8481 + NS_ENSURE_STATE(mHTMLEditor); 1.8482 + nsCOMPtr<nsIDOMElement> rootElement = do_QueryInterface(mHTMLEditor->GetRoot()); 1.8483 + NS_ENSURE_TRUE(rootElement, NS_ERROR_UNEXPECTED); 1.8484 + 1.8485 + // get the selection 1.8486 + nsCOMPtr<nsISelection>selection; 1.8487 + NS_ENSURE_STATE(mHTMLEditor); 1.8488 + res = mHTMLEditor->GetSelection(getter_AddRefs(selection)); 1.8489 + NS_ENSURE_SUCCESS(res, res); 1.8490 + 1.8491 + // get the selection start location 1.8492 + nsCOMPtr<nsIDOMNode> selNode, temp, parent; 1.8493 + int32_t selOffset; 1.8494 + NS_ENSURE_STATE(mHTMLEditor); 1.8495 + res = mHTMLEditor->GetStartNodeAndOffset(selection, getter_AddRefs(selNode), &selOffset); 1.8496 + NS_ENSURE_SUCCESS(res, res); 1.8497 + temp = selNode; 1.8498 + 1.8499 + // check that selNode is inside body 1.8500 + while (temp && !nsTextEditUtils::IsBody(temp)) 1.8501 + { 1.8502 + res = temp->GetParentNode(getter_AddRefs(parent)); 1.8503 + temp = parent; 1.8504 + } 1.8505 + 1.8506 + // if we aren't in the body, force the issue 1.8507 + if (!temp) 1.8508 + { 1.8509 +// uncomment this to see when we get bad selections 1.8510 +// NS_NOTREACHED("selection not in body"); 1.8511 + selection->Collapse(rootElement, 0); 1.8512 + } 1.8513 + 1.8514 + // get the selection end location 1.8515 + NS_ENSURE_STATE(mHTMLEditor); 1.8516 + res = mHTMLEditor->GetEndNodeAndOffset(selection, getter_AddRefs(selNode), &selOffset); 1.8517 + NS_ENSURE_SUCCESS(res, res); 1.8518 + temp = selNode; 1.8519 + 1.8520 + // check that selNode is inside body 1.8521 + while (temp && !nsTextEditUtils::IsBody(temp)) 1.8522 + { 1.8523 + res = temp->GetParentNode(getter_AddRefs(parent)); 1.8524 + temp = parent; 1.8525 + } 1.8526 + 1.8527 + // if we aren't in the body, force the issue 1.8528 + if (!temp) 1.8529 + { 1.8530 +// uncomment this to see when we get bad selections 1.8531 +// NS_NOTREACHED("selection not in body"); 1.8532 + selection->Collapse(rootElement, 0); 1.8533 + } 1.8534 + 1.8535 + return res; 1.8536 +} 1.8537 + 1.8538 + 1.8539 +nsresult 1.8540 +nsHTMLEditRules::UpdateDocChangeRange(nsIDOMRange *aRange) 1.8541 +{ 1.8542 + nsresult res = NS_OK; 1.8543 + 1.8544 + // first make sure aRange is in the document. It might not be if 1.8545 + // portions of our editting action involved manipulating nodes 1.8546 + // prior to placing them in the document (e.g., populating a list item 1.8547 + // before placing it in its list) 1.8548 + nsCOMPtr<nsIDOMNode> startNode; 1.8549 + res = aRange->GetStartContainer(getter_AddRefs(startNode)); 1.8550 + NS_ENSURE_SUCCESS(res, res); 1.8551 + NS_ENSURE_STATE(mHTMLEditor); 1.8552 + if (!mHTMLEditor->IsDescendantOfRoot(startNode)) { 1.8553 + // just return - we don't need to adjust mDocChangeRange in this case 1.8554 + return NS_OK; 1.8555 + } 1.8556 + 1.8557 + if (!mDocChangeRange) 1.8558 + { 1.8559 + // clone aRange. 1.8560 + nsCOMPtr<nsIDOMRange> range; 1.8561 + res = aRange->CloneRange(getter_AddRefs(range)); 1.8562 + mDocChangeRange = static_cast<nsRange*>(range.get()); 1.8563 + } 1.8564 + else 1.8565 + { 1.8566 + int16_t result; 1.8567 + 1.8568 + // compare starts of ranges 1.8569 + res = mDocChangeRange->CompareBoundaryPoints(nsIDOMRange::START_TO_START, aRange, &result); 1.8570 + if (res == NS_ERROR_NOT_INITIALIZED) { 1.8571 + // This will happen is mDocChangeRange is non-null, but the range is 1.8572 + // uninitialized. In this case we'll set the start to aRange start. 1.8573 + // The same test won't be needed further down since after we've set 1.8574 + // the start the range will be collapsed to that point. 1.8575 + result = 1; 1.8576 + res = NS_OK; 1.8577 + } 1.8578 + NS_ENSURE_SUCCESS(res, res); 1.8579 + if (result > 0) // positive result means mDocChangeRange start is after aRange start 1.8580 + { 1.8581 + int32_t startOffset; 1.8582 + res = aRange->GetStartOffset(&startOffset); 1.8583 + NS_ENSURE_SUCCESS(res, res); 1.8584 + res = mDocChangeRange->SetStart(startNode, startOffset); 1.8585 + NS_ENSURE_SUCCESS(res, res); 1.8586 + } 1.8587 + 1.8588 + // compare ends of ranges 1.8589 + res = mDocChangeRange->CompareBoundaryPoints(nsIDOMRange::END_TO_END, aRange, &result); 1.8590 + NS_ENSURE_SUCCESS(res, res); 1.8591 + if (result < 0) // negative result means mDocChangeRange end is before aRange end 1.8592 + { 1.8593 + nsCOMPtr<nsIDOMNode> endNode; 1.8594 + int32_t endOffset; 1.8595 + res = aRange->GetEndContainer(getter_AddRefs(endNode)); 1.8596 + NS_ENSURE_SUCCESS(res, res); 1.8597 + res = aRange->GetEndOffset(&endOffset); 1.8598 + NS_ENSURE_SUCCESS(res, res); 1.8599 + res = mDocChangeRange->SetEnd(endNode, endOffset); 1.8600 + NS_ENSURE_SUCCESS(res, res); 1.8601 + } 1.8602 + } 1.8603 + return res; 1.8604 +} 1.8605 + 1.8606 +nsresult 1.8607 +nsHTMLEditRules::InsertMozBRIfNeeded(nsIDOMNode *aNode) 1.8608 +{ 1.8609 + NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER); 1.8610 + if (!IsBlockNode(aNode)) return NS_OK; 1.8611 + 1.8612 + bool isEmpty; 1.8613 + NS_ENSURE_STATE(mHTMLEditor); 1.8614 + nsresult res = mHTMLEditor->IsEmptyNode(aNode, &isEmpty); 1.8615 + NS_ENSURE_SUCCESS(res, res); 1.8616 + if (!isEmpty) { 1.8617 + return NS_OK; 1.8618 + } 1.8619 + 1.8620 + return CreateMozBR(aNode, 0); 1.8621 +} 1.8622 + 1.8623 +NS_IMETHODIMP 1.8624 +nsHTMLEditRules::WillCreateNode(const nsAString& aTag, nsIDOMNode *aParent, int32_t aPosition) 1.8625 +{ 1.8626 + return NS_OK; 1.8627 +} 1.8628 + 1.8629 +NS_IMETHODIMP 1.8630 +nsHTMLEditRules::DidCreateNode(const nsAString& aTag, 1.8631 + nsIDOMNode *aNode, 1.8632 + nsIDOMNode *aParent, 1.8633 + int32_t aPosition, 1.8634 + nsresult aResult) 1.8635 +{ 1.8636 + if (!mListenerEnabled) { 1.8637 + return NS_OK; 1.8638 + } 1.8639 + // assumption that Join keeps the righthand node 1.8640 + nsresult res = mUtilRange->SelectNode(aNode); 1.8641 + NS_ENSURE_SUCCESS(res, res); 1.8642 + res = UpdateDocChangeRange(mUtilRange); 1.8643 + return res; 1.8644 +} 1.8645 + 1.8646 + 1.8647 +NS_IMETHODIMP 1.8648 +nsHTMLEditRules::WillInsertNode(nsIDOMNode *aNode, nsIDOMNode *aParent, int32_t aPosition) 1.8649 +{ 1.8650 + return NS_OK; 1.8651 +} 1.8652 + 1.8653 + 1.8654 +NS_IMETHODIMP 1.8655 +nsHTMLEditRules::DidInsertNode(nsIDOMNode *aNode, 1.8656 + nsIDOMNode *aParent, 1.8657 + int32_t aPosition, 1.8658 + nsresult aResult) 1.8659 +{ 1.8660 + if (!mListenerEnabled) { 1.8661 + return NS_OK; 1.8662 + } 1.8663 + nsresult res = mUtilRange->SelectNode(aNode); 1.8664 + NS_ENSURE_SUCCESS(res, res); 1.8665 + res = UpdateDocChangeRange(mUtilRange); 1.8666 + return res; 1.8667 +} 1.8668 + 1.8669 + 1.8670 +NS_IMETHODIMP 1.8671 +nsHTMLEditRules::WillDeleteNode(nsIDOMNode *aChild) 1.8672 +{ 1.8673 + if (!mListenerEnabled) { 1.8674 + return NS_OK; 1.8675 + } 1.8676 + nsresult res = mUtilRange->SelectNode(aChild); 1.8677 + NS_ENSURE_SUCCESS(res, res); 1.8678 + res = UpdateDocChangeRange(mUtilRange); 1.8679 + return res; 1.8680 +} 1.8681 + 1.8682 + 1.8683 +NS_IMETHODIMP 1.8684 +nsHTMLEditRules::DidDeleteNode(nsIDOMNode *aChild, nsresult aResult) 1.8685 +{ 1.8686 + return NS_OK; 1.8687 +} 1.8688 + 1.8689 + 1.8690 +NS_IMETHODIMP 1.8691 +nsHTMLEditRules::WillSplitNode(nsIDOMNode *aExistingRightNode, int32_t aOffset) 1.8692 +{ 1.8693 + return NS_OK; 1.8694 +} 1.8695 + 1.8696 + 1.8697 +NS_IMETHODIMP 1.8698 +nsHTMLEditRules::DidSplitNode(nsIDOMNode *aExistingRightNode, 1.8699 + int32_t aOffset, 1.8700 + nsIDOMNode *aNewLeftNode, 1.8701 + nsresult aResult) 1.8702 +{ 1.8703 + if (!mListenerEnabled) { 1.8704 + return NS_OK; 1.8705 + } 1.8706 + nsresult res = mUtilRange->SetStart(aNewLeftNode, 0); 1.8707 + NS_ENSURE_SUCCESS(res, res); 1.8708 + res = mUtilRange->SetEnd(aExistingRightNode, 0); 1.8709 + NS_ENSURE_SUCCESS(res, res); 1.8710 + res = UpdateDocChangeRange(mUtilRange); 1.8711 + return res; 1.8712 +} 1.8713 + 1.8714 + 1.8715 +NS_IMETHODIMP 1.8716 +nsHTMLEditRules::WillJoinNodes(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, nsIDOMNode *aParent) 1.8717 +{ 1.8718 + if (!mListenerEnabled) { 1.8719 + return NS_OK; 1.8720 + } 1.8721 + // remember split point 1.8722 + nsresult res = nsEditor::GetLengthOfDOMNode(aLeftNode, mJoinOffset); 1.8723 + return res; 1.8724 +} 1.8725 + 1.8726 + 1.8727 +NS_IMETHODIMP 1.8728 +nsHTMLEditRules::DidJoinNodes(nsIDOMNode *aLeftNode, 1.8729 + nsIDOMNode *aRightNode, 1.8730 + nsIDOMNode *aParent, 1.8731 + nsresult aResult) 1.8732 +{ 1.8733 + if (!mListenerEnabled) { 1.8734 + return NS_OK; 1.8735 + } 1.8736 + // assumption that Join keeps the righthand node 1.8737 + nsresult res = mUtilRange->SetStart(aRightNode, mJoinOffset); 1.8738 + NS_ENSURE_SUCCESS(res, res); 1.8739 + res = mUtilRange->SetEnd(aRightNode, mJoinOffset); 1.8740 + NS_ENSURE_SUCCESS(res, res); 1.8741 + res = UpdateDocChangeRange(mUtilRange); 1.8742 + return res; 1.8743 +} 1.8744 + 1.8745 + 1.8746 +NS_IMETHODIMP 1.8747 +nsHTMLEditRules::WillInsertText(nsIDOMCharacterData *aTextNode, int32_t aOffset, const nsAString &aString) 1.8748 +{ 1.8749 + return NS_OK; 1.8750 +} 1.8751 + 1.8752 + 1.8753 +NS_IMETHODIMP 1.8754 +nsHTMLEditRules::DidInsertText(nsIDOMCharacterData *aTextNode, 1.8755 + int32_t aOffset, 1.8756 + const nsAString &aString, 1.8757 + nsresult aResult) 1.8758 +{ 1.8759 + if (!mListenerEnabled) { 1.8760 + return NS_OK; 1.8761 + } 1.8762 + int32_t length = aString.Length(); 1.8763 + nsCOMPtr<nsIDOMNode> theNode = do_QueryInterface(aTextNode); 1.8764 + nsresult res = mUtilRange->SetStart(theNode, aOffset); 1.8765 + NS_ENSURE_SUCCESS(res, res); 1.8766 + res = mUtilRange->SetEnd(theNode, aOffset+length); 1.8767 + NS_ENSURE_SUCCESS(res, res); 1.8768 + res = UpdateDocChangeRange(mUtilRange); 1.8769 + return res; 1.8770 +} 1.8771 + 1.8772 + 1.8773 +NS_IMETHODIMP 1.8774 +nsHTMLEditRules::WillDeleteText(nsIDOMCharacterData *aTextNode, int32_t aOffset, int32_t aLength) 1.8775 +{ 1.8776 + return NS_OK; 1.8777 +} 1.8778 + 1.8779 + 1.8780 +NS_IMETHODIMP 1.8781 +nsHTMLEditRules::DidDeleteText(nsIDOMCharacterData *aTextNode, 1.8782 + int32_t aOffset, 1.8783 + int32_t aLength, 1.8784 + nsresult aResult) 1.8785 +{ 1.8786 + if (!mListenerEnabled) { 1.8787 + return NS_OK; 1.8788 + } 1.8789 + nsCOMPtr<nsIDOMNode> theNode = do_QueryInterface(aTextNode); 1.8790 + nsresult res = mUtilRange->SetStart(theNode, aOffset); 1.8791 + NS_ENSURE_SUCCESS(res, res); 1.8792 + res = mUtilRange->SetEnd(theNode, aOffset); 1.8793 + NS_ENSURE_SUCCESS(res, res); 1.8794 + res = UpdateDocChangeRange(mUtilRange); 1.8795 + return res; 1.8796 +} 1.8797 + 1.8798 +NS_IMETHODIMP 1.8799 +nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection) 1.8800 +{ 1.8801 + if (!mListenerEnabled) { 1.8802 + return NS_OK; 1.8803 + } 1.8804 + // get the (collapsed) selection location 1.8805 + nsCOMPtr<nsIDOMNode> selNode; 1.8806 + int32_t selOffset; 1.8807 + 1.8808 + NS_ENSURE_STATE(mHTMLEditor); 1.8809 + nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset); 1.8810 + NS_ENSURE_SUCCESS(res, res); 1.8811 + res = mUtilRange->SetStart(selNode, selOffset); 1.8812 + NS_ENSURE_SUCCESS(res, res); 1.8813 + NS_ENSURE_STATE(mHTMLEditor); 1.8814 + res = mHTMLEditor->GetEndNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset); 1.8815 + NS_ENSURE_SUCCESS(res, res); 1.8816 + res = mUtilRange->SetEnd(selNode, selOffset); 1.8817 + NS_ENSURE_SUCCESS(res, res); 1.8818 + res = UpdateDocChangeRange(mUtilRange); 1.8819 + return res; 1.8820 +} 1.8821 + 1.8822 +NS_IMETHODIMP 1.8823 +nsHTMLEditRules::DidDeleteSelection(nsISelection *aSelection) 1.8824 +{ 1.8825 + return NS_OK; 1.8826 +} 1.8827 + 1.8828 +// Let's remove all alignment hints in the children of aNode; it can 1.8829 +// be an ALIGN attribute (in case we just remove it) or a CENTER 1.8830 +// element (here we have to remove the container and keep its 1.8831 +// children). We break on tables and don't look at their children. 1.8832 +nsresult 1.8833 +nsHTMLEditRules::RemoveAlignment(nsIDOMNode * aNode, const nsAString & aAlignType, bool aChildrenOnly) 1.8834 +{ 1.8835 + NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER); 1.8836 + 1.8837 + NS_ENSURE_STATE(mHTMLEditor); 1.8838 + if (mHTMLEditor->IsTextNode(aNode) || nsHTMLEditUtils::IsTable(aNode)) return NS_OK; 1.8839 + nsresult res = NS_OK; 1.8840 + 1.8841 + nsCOMPtr<nsIDOMNode> child = aNode,tmp; 1.8842 + if (aChildrenOnly) 1.8843 + { 1.8844 + aNode->GetFirstChild(getter_AddRefs(child)); 1.8845 + } 1.8846 + NS_ENSURE_STATE(mHTMLEditor); 1.8847 + bool useCSS = mHTMLEditor->IsCSSEnabled(); 1.8848 + 1.8849 + while (child) 1.8850 + { 1.8851 + if (aChildrenOnly) { 1.8852 + // get the next sibling right now because we could have to remove child 1.8853 + child->GetNextSibling(getter_AddRefs(tmp)); 1.8854 + } 1.8855 + else 1.8856 + { 1.8857 + tmp = nullptr; 1.8858 + } 1.8859 + bool isBlock; 1.8860 + NS_ENSURE_STATE(mHTMLEditor); 1.8861 + res = mHTMLEditor->NodeIsBlockStatic(child, &isBlock); 1.8862 + NS_ENSURE_SUCCESS(res, res); 1.8863 + 1.8864 + if (nsEditor::NodeIsType(child, nsEditProperty::center)) 1.8865 + { 1.8866 + // the current node is a CENTER element 1.8867 + // first remove children's alignment 1.8868 + res = RemoveAlignment(child, aAlignType, true); 1.8869 + NS_ENSURE_SUCCESS(res, res); 1.8870 + 1.8871 + // we may have to insert BRs in first and last position of element's children 1.8872 + // if the nodes before/after are not blocks and not BRs 1.8873 + res = MakeSureElemStartsOrEndsOnCR(child); 1.8874 + NS_ENSURE_SUCCESS(res, res); 1.8875 + 1.8876 + // now remove the CENTER container 1.8877 + NS_ENSURE_STATE(mHTMLEditor); 1.8878 + res = mHTMLEditor->RemoveContainer(child); 1.8879 + NS_ENSURE_SUCCESS(res, res); 1.8880 + } 1.8881 + else if (isBlock || nsHTMLEditUtils::IsHR(child)) 1.8882 + { 1.8883 + // the current node is a block element 1.8884 + nsCOMPtr<nsIDOMElement> curElem = do_QueryInterface(child); 1.8885 + if (nsHTMLEditUtils::SupportsAlignAttr(child)) 1.8886 + { 1.8887 + // remove the ALIGN attribute if this element can have it 1.8888 + NS_ENSURE_STATE(mHTMLEditor); 1.8889 + res = mHTMLEditor->RemoveAttribute(curElem, NS_LITERAL_STRING("align")); 1.8890 + NS_ENSURE_SUCCESS(res, res); 1.8891 + } 1.8892 + if (useCSS) 1.8893 + { 1.8894 + if (nsHTMLEditUtils::IsTable(child) || nsHTMLEditUtils::IsHR(child)) 1.8895 + { 1.8896 + NS_ENSURE_STATE(mHTMLEditor); 1.8897 + res = mHTMLEditor->SetAttributeOrEquivalent(curElem, NS_LITERAL_STRING("align"), aAlignType, false); 1.8898 + } 1.8899 + else 1.8900 + { 1.8901 + nsAutoString dummyCssValue; 1.8902 + NS_ENSURE_STATE(mHTMLEditor); 1.8903 + res = mHTMLEditor->mHTMLCSSUtils->RemoveCSSInlineStyle(child, nsEditProperty::cssTextAlign, dummyCssValue); 1.8904 + } 1.8905 + NS_ENSURE_SUCCESS(res, res); 1.8906 + } 1.8907 + if (!nsHTMLEditUtils::IsTable(child)) 1.8908 + { 1.8909 + // unless this is a table, look at children 1.8910 + res = RemoveAlignment(child, aAlignType, true); 1.8911 + NS_ENSURE_SUCCESS(res, res); 1.8912 + } 1.8913 + } 1.8914 + child = tmp; 1.8915 + } 1.8916 + return NS_OK; 1.8917 +} 1.8918 + 1.8919 +// Let's insert a BR as first (resp. last) child of aNode if its 1.8920 +// first (resp. last) child is not a block nor a BR, and if the 1.8921 +// previous (resp. next) sibling is not a block nor a BR 1.8922 +nsresult 1.8923 +nsHTMLEditRules::MakeSureElemStartsOrEndsOnCR(nsIDOMNode *aNode, bool aStarts) 1.8924 +{ 1.8925 + NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER); 1.8926 + 1.8927 + nsCOMPtr<nsIDOMNode> child; 1.8928 + nsresult res; 1.8929 + if (aStarts) 1.8930 + { 1.8931 + NS_ENSURE_STATE(mHTMLEditor); 1.8932 + res = mHTMLEditor->GetFirstEditableChild(aNode, address_of(child)); 1.8933 + } 1.8934 + else 1.8935 + { 1.8936 + NS_ENSURE_STATE(mHTMLEditor); 1.8937 + res = mHTMLEditor->GetLastEditableChild(aNode, address_of(child)); 1.8938 + } 1.8939 + NS_ENSURE_SUCCESS(res, res); 1.8940 + NS_ENSURE_TRUE(child, NS_OK); 1.8941 + bool isChildBlock; 1.8942 + NS_ENSURE_STATE(mHTMLEditor); 1.8943 + res = mHTMLEditor->NodeIsBlockStatic(child, &isChildBlock); 1.8944 + NS_ENSURE_SUCCESS(res, res); 1.8945 + bool foundCR = false; 1.8946 + if (isChildBlock || nsTextEditUtils::IsBreak(child)) 1.8947 + { 1.8948 + foundCR = true; 1.8949 + } 1.8950 + else 1.8951 + { 1.8952 + nsCOMPtr<nsIDOMNode> sibling; 1.8953 + if (aStarts) 1.8954 + { 1.8955 + NS_ENSURE_STATE(mHTMLEditor); 1.8956 + res = mHTMLEditor->GetPriorHTMLSibling(aNode, address_of(sibling)); 1.8957 + } 1.8958 + else 1.8959 + { 1.8960 + NS_ENSURE_STATE(mHTMLEditor); 1.8961 + res = mHTMLEditor->GetNextHTMLSibling(aNode, address_of(sibling)); 1.8962 + } 1.8963 + NS_ENSURE_SUCCESS(res, res); 1.8964 + if (sibling) 1.8965 + { 1.8966 + bool isBlock; 1.8967 + NS_ENSURE_STATE(mHTMLEditor); 1.8968 + res = mHTMLEditor->NodeIsBlockStatic(sibling, &isBlock); 1.8969 + NS_ENSURE_SUCCESS(res, res); 1.8970 + if (isBlock || nsTextEditUtils::IsBreak(sibling)) 1.8971 + { 1.8972 + foundCR = true; 1.8973 + } 1.8974 + } 1.8975 + else 1.8976 + { 1.8977 + foundCR = true; 1.8978 + } 1.8979 + } 1.8980 + if (!foundCR) 1.8981 + { 1.8982 + int32_t offset = 0; 1.8983 + if (!aStarts) { 1.8984 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.8985 + NS_ENSURE_STATE(node); 1.8986 + offset = node->GetChildCount(); 1.8987 + } 1.8988 + nsCOMPtr<nsIDOMNode> brNode; 1.8989 + NS_ENSURE_STATE(mHTMLEditor); 1.8990 + res = mHTMLEditor->CreateBR(aNode, offset, address_of(brNode)); 1.8991 + NS_ENSURE_SUCCESS(res, res); 1.8992 + } 1.8993 + return NS_OK; 1.8994 +} 1.8995 + 1.8996 +nsresult 1.8997 +nsHTMLEditRules::MakeSureElemStartsOrEndsOnCR(nsIDOMNode *aNode) 1.8998 +{ 1.8999 + nsresult res = MakeSureElemStartsOrEndsOnCR(aNode, false); 1.9000 + NS_ENSURE_SUCCESS(res, res); 1.9001 + res = MakeSureElemStartsOrEndsOnCR(aNode, true); 1.9002 + return res; 1.9003 +} 1.9004 + 1.9005 +nsresult 1.9006 +nsHTMLEditRules::AlignBlock(nsIDOMElement * aElement, const nsAString * aAlignType, bool aContentsOnly) 1.9007 +{ 1.9008 + NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER); 1.9009 + 1.9010 + nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aElement); 1.9011 + bool isBlock = IsBlockNode(node); 1.9012 + if (!isBlock && !nsHTMLEditUtils::IsHR(node)) { 1.9013 + // we deal only with blocks; early way out 1.9014 + return NS_OK; 1.9015 + } 1.9016 + 1.9017 + nsresult res = RemoveAlignment(node, *aAlignType, aContentsOnly); 1.9018 + NS_ENSURE_SUCCESS(res, res); 1.9019 + NS_NAMED_LITERAL_STRING(attr, "align"); 1.9020 + NS_ENSURE_STATE(mHTMLEditor); 1.9021 + if (mHTMLEditor->IsCSSEnabled()) { 1.9022 + // let's use CSS alignment; we use margin-left and margin-right for tables 1.9023 + // and text-align for other block-level elements 1.9024 + NS_ENSURE_STATE(mHTMLEditor); 1.9025 + res = mHTMLEditor->SetAttributeOrEquivalent(aElement, attr, *aAlignType, false); 1.9026 + NS_ENSURE_SUCCESS(res, res); 1.9027 + } 1.9028 + else { 1.9029 + // HTML case; this code is supposed to be called ONLY if the element 1.9030 + // supports the align attribute but we'll never know... 1.9031 + if (nsHTMLEditUtils::SupportsAlignAttr(node)) { 1.9032 + NS_ENSURE_STATE(mHTMLEditor); 1.9033 + res = mHTMLEditor->SetAttribute(aElement, attr, *aAlignType); 1.9034 + NS_ENSURE_SUCCESS(res, res); 1.9035 + } 1.9036 + } 1.9037 + return NS_OK; 1.9038 +} 1.9039 + 1.9040 +nsresult 1.9041 +nsHTMLEditRules::RelativeChangeIndentationOfElementNode(nsIDOMNode *aNode, int8_t aRelativeChange) 1.9042 +{ 1.9043 + NS_ENSURE_ARG_POINTER(aNode); 1.9044 + 1.9045 + if (aRelativeChange != 1 && aRelativeChange != -1) { 1.9046 + return NS_ERROR_ILLEGAL_VALUE; 1.9047 + } 1.9048 + 1.9049 + nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode); 1.9050 + if (!element) { 1.9051 + return NS_OK; 1.9052 + } 1.9053 + 1.9054 + NS_ENSURE_STATE(mHTMLEditor); 1.9055 + nsIAtom* marginProperty = MarginPropertyAtomForIndent(mHTMLEditor->mHTMLCSSUtils, element); 1.9056 + nsAutoString value; 1.9057 + NS_ENSURE_STATE(mHTMLEditor); 1.9058 + mHTMLEditor->mHTMLCSSUtils->GetSpecifiedProperty(aNode, marginProperty, value); 1.9059 + float f; 1.9060 + nsCOMPtr<nsIAtom> unit; 1.9061 + NS_ENSURE_STATE(mHTMLEditor); 1.9062 + mHTMLEditor->mHTMLCSSUtils->ParseLength(value, &f, getter_AddRefs(unit)); 1.9063 + if (0 == f) { 1.9064 + nsAutoString defaultLengthUnit; 1.9065 + NS_ENSURE_STATE(mHTMLEditor); 1.9066 + mHTMLEditor->mHTMLCSSUtils->GetDefaultLengthUnit(defaultLengthUnit); 1.9067 + unit = do_GetAtom(defaultLengthUnit); 1.9068 + } 1.9069 + if (nsEditProperty::cssInUnit == unit) 1.9070 + f += NS_EDITOR_INDENT_INCREMENT_IN * aRelativeChange; 1.9071 + else if (nsEditProperty::cssCmUnit == unit) 1.9072 + f += NS_EDITOR_INDENT_INCREMENT_CM * aRelativeChange; 1.9073 + else if (nsEditProperty::cssMmUnit == unit) 1.9074 + f += NS_EDITOR_INDENT_INCREMENT_MM * aRelativeChange; 1.9075 + else if (nsEditProperty::cssPtUnit == unit) 1.9076 + f += NS_EDITOR_INDENT_INCREMENT_PT * aRelativeChange; 1.9077 + else if (nsEditProperty::cssPcUnit == unit) 1.9078 + f += NS_EDITOR_INDENT_INCREMENT_PC * aRelativeChange; 1.9079 + else if (nsEditProperty::cssEmUnit == unit) 1.9080 + f += NS_EDITOR_INDENT_INCREMENT_EM * aRelativeChange; 1.9081 + else if (nsEditProperty::cssExUnit == unit) 1.9082 + f += NS_EDITOR_INDENT_INCREMENT_EX * aRelativeChange; 1.9083 + else if (nsEditProperty::cssPxUnit == unit) 1.9084 + f += NS_EDITOR_INDENT_INCREMENT_PX * aRelativeChange; 1.9085 + else if (nsEditProperty::cssPercentUnit == unit) 1.9086 + f += NS_EDITOR_INDENT_INCREMENT_PERCENT * aRelativeChange; 1.9087 + 1.9088 + if (0 < f) { 1.9089 + nsAutoString newValue; 1.9090 + newValue.AppendFloat(f); 1.9091 + newValue.Append(nsDependentAtomString(unit)); 1.9092 + NS_ENSURE_STATE(mHTMLEditor); 1.9093 + mHTMLEditor->mHTMLCSSUtils->SetCSSProperty(element, marginProperty, newValue, false); 1.9094 + return NS_OK; 1.9095 + } 1.9096 + 1.9097 + NS_ENSURE_STATE(mHTMLEditor); 1.9098 + mHTMLEditor->mHTMLCSSUtils->RemoveCSSProperty(element, marginProperty, value, false); 1.9099 + 1.9100 + // remove unnecessary DIV blocks: 1.9101 + // we could skip this section but that would cause a FAIL in 1.9102 + // editor/libeditor/html/tests/browserscope/richtext.html, which expects 1.9103 + // to unapply a CSS "indent" (<div style="margin-left: 40px;">) by 1.9104 + // removing the DIV container instead of just removing the CSS property. 1.9105 + nsCOMPtr<dom::Element> node = do_QueryInterface(aNode); 1.9106 + if (!node || !node->IsHTML(nsGkAtoms::div) || 1.9107 + !mHTMLEditor || 1.9108 + node == mHTMLEditor->GetActiveEditingHost() || 1.9109 + !mHTMLEditor->IsDescendantOfEditorRoot(node) || 1.9110 + nsHTMLEditor::HasAttributes(node)) { 1.9111 + NS_ENSURE_STATE(mHTMLEditor); 1.9112 + return NS_OK; 1.9113 + } 1.9114 + 1.9115 + NS_ENSURE_STATE(mHTMLEditor); 1.9116 + return mHTMLEditor->RemoveContainer(element); 1.9117 +} 1.9118 + 1.9119 +// 1.9120 +// Support for Absolute Positioning 1.9121 +// 1.9122 + 1.9123 +nsresult 1.9124 +nsHTMLEditRules::WillAbsolutePosition(Selection* aSelection, 1.9125 + bool* aCancel, bool* aHandled) 1.9126 +{ 1.9127 + if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; } 1.9128 + nsresult res = WillInsert(aSelection, aCancel); 1.9129 + NS_ENSURE_SUCCESS(res, res); 1.9130 + 1.9131 + // initialize out param 1.9132 + // we want to ignore result of WillInsert() 1.9133 + *aCancel = false; 1.9134 + *aHandled = true; 1.9135 + 1.9136 + nsCOMPtr<nsIDOMElement> focusElement; 1.9137 + NS_ENSURE_STATE(mHTMLEditor); 1.9138 + res = mHTMLEditor->GetSelectionContainer(getter_AddRefs(focusElement)); 1.9139 + if (focusElement) { 1.9140 + nsCOMPtr<nsIDOMNode> node = do_QueryInterface(focusElement); 1.9141 + if (nsHTMLEditUtils::IsImage(node)) { 1.9142 + mNewBlock = node; 1.9143 + return NS_OK; 1.9144 + } 1.9145 + } 1.9146 + 1.9147 + res = NormalizeSelection(aSelection); 1.9148 + NS_ENSURE_SUCCESS(res, res); 1.9149 + NS_ENSURE_STATE(mHTMLEditor); 1.9150 + nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor); 1.9151 + 1.9152 + // convert the selection ranges into "promoted" selection ranges: 1.9153 + // this basically just expands the range to include the immediate 1.9154 + // block parent, and then further expands to include any ancestors 1.9155 + // whose children are all in the range 1.9156 + 1.9157 + nsCOMArray<nsIDOMRange> arrayOfRanges; 1.9158 + res = GetPromotedRanges(aSelection, arrayOfRanges, 1.9159 + EditAction::setAbsolutePosition); 1.9160 + NS_ENSURE_SUCCESS(res, res); 1.9161 + 1.9162 + // use these ranges to contruct a list of nodes to act on. 1.9163 + nsCOMArray<nsIDOMNode> arrayOfNodes; 1.9164 + res = GetNodesForOperation(arrayOfRanges, arrayOfNodes, 1.9165 + EditAction::setAbsolutePosition); 1.9166 + NS_ENSURE_SUCCESS(res, res); 1.9167 + 1.9168 + NS_NAMED_LITERAL_STRING(divType, "div"); 1.9169 + 1.9170 + 1.9171 + // if nothing visible in list, make an empty block 1.9172 + if (ListIsEmptyLine(arrayOfNodes)) 1.9173 + { 1.9174 + nsCOMPtr<nsIDOMNode> parent, thePositionedDiv; 1.9175 + int32_t offset; 1.9176 + 1.9177 + // get selection location 1.9178 + NS_ENSURE_STATE(mHTMLEditor); 1.9179 + res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(parent), &offset); 1.9180 + NS_ENSURE_SUCCESS(res, res); 1.9181 + // make sure we can put a block here 1.9182 + res = SplitAsNeeded(&divType, address_of(parent), &offset); 1.9183 + NS_ENSURE_SUCCESS(res, res); 1.9184 + NS_ENSURE_STATE(mHTMLEditor); 1.9185 + res = mHTMLEditor->CreateNode(divType, parent, offset, getter_AddRefs(thePositionedDiv)); 1.9186 + NS_ENSURE_SUCCESS(res, res); 1.9187 + // remember our new block for postprocessing 1.9188 + mNewBlock = thePositionedDiv; 1.9189 + // delete anything that was in the list of nodes 1.9190 + for (int32_t j = arrayOfNodes.Count() - 1; j >= 0; --j) 1.9191 + { 1.9192 + nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[0]; 1.9193 + NS_ENSURE_STATE(mHTMLEditor); 1.9194 + res = mHTMLEditor->DeleteNode(curNode); 1.9195 + NS_ENSURE_SUCCESS(res, res); 1.9196 + arrayOfNodes.RemoveObjectAt(0); 1.9197 + } 1.9198 + // put selection in new block 1.9199 + res = aSelection->Collapse(thePositionedDiv,0); 1.9200 + selectionResetter.Abort(); // to prevent selection reseter from overriding us. 1.9201 + *aHandled = true; 1.9202 + return res; 1.9203 + } 1.9204 + 1.9205 + // Ok, now go through all the nodes and put them in a blockquote, 1.9206 + // or whatever is appropriate. Wohoo! 1.9207 + int32_t i; 1.9208 + nsCOMPtr<nsIDOMNode> curParent, curPositionedDiv, curList, indentedLI, sibling; 1.9209 + int32_t listCount = arrayOfNodes.Count(); 1.9210 + for (i=0; i<listCount; i++) 1.9211 + { 1.9212 + // here's where we actually figure out what to do 1.9213 + nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i]; 1.9214 + 1.9215 + // Ignore all non-editable nodes. Leave them be. 1.9216 + NS_ENSURE_STATE(mHTMLEditor); 1.9217 + if (!mHTMLEditor->IsEditable(curNode)) continue; 1.9218 + 1.9219 + int32_t offset; 1.9220 + curParent = nsEditor::GetNodeLocation(curNode, &offset); 1.9221 + 1.9222 + // some logic for putting list items into nested lists... 1.9223 + if (nsHTMLEditUtils::IsList(curParent)) 1.9224 + { 1.9225 + // check to see if curList is still appropriate. Which it is if 1.9226 + // curNode is still right after it in the same list. 1.9227 + if (curList) 1.9228 + { 1.9229 + sibling = nullptr; 1.9230 + NS_ENSURE_STATE(mHTMLEditor); 1.9231 + mHTMLEditor->GetPriorHTMLSibling(curNode, address_of(sibling)); 1.9232 + } 1.9233 + 1.9234 + if (!curList || (sibling && sibling != curList) ) 1.9235 + { 1.9236 + nsAutoString listTag; 1.9237 + nsEditor::GetTagString(curParent,listTag); 1.9238 + ToLowerCase(listTag); 1.9239 + // create a new nested list of correct type 1.9240 + res = SplitAsNeeded(&listTag, address_of(curParent), &offset); 1.9241 + NS_ENSURE_SUCCESS(res, res); 1.9242 + if (!curPositionedDiv) { 1.9243 + int32_t parentOffset; 1.9244 + nsCOMPtr<nsIDOMNode> curParentParent = nsEditor::GetNodeLocation(curParent, &parentOffset); 1.9245 + NS_ENSURE_STATE(mHTMLEditor); 1.9246 + res = mHTMLEditor->CreateNode(divType, curParentParent, parentOffset, getter_AddRefs(curPositionedDiv)); 1.9247 + mNewBlock = curPositionedDiv; 1.9248 + } 1.9249 + NS_ENSURE_STATE(mHTMLEditor); 1.9250 + res = mHTMLEditor->CreateNode(listTag, curPositionedDiv, -1, getter_AddRefs(curList)); 1.9251 + NS_ENSURE_SUCCESS(res, res); 1.9252 + // curList is now the correct thing to put curNode in 1.9253 + // remember our new block for postprocessing 1.9254 + // mNewBlock = curList; 1.9255 + } 1.9256 + // tuck the node into the end of the active list 1.9257 + NS_ENSURE_STATE(mHTMLEditor); 1.9258 + res = mHTMLEditor->MoveNode(curNode, curList, -1); 1.9259 + NS_ENSURE_SUCCESS(res, res); 1.9260 + // forget curPositionedDiv, if any 1.9261 + // curPositionedDiv = nullptr; 1.9262 + } 1.9263 + 1.9264 + else // not a list item, use blockquote? 1.9265 + { 1.9266 + // if we are inside a list item, we don't want to blockquote, we want 1.9267 + // to sublist the list item. We may have several nodes listed in the 1.9268 + // array of nodes to act on, that are in the same list item. Since 1.9269 + // we only want to indent that li once, we must keep track of the most 1.9270 + // recent indented list item, and not indent it if we find another node 1.9271 + // to act on that is still inside the same li. 1.9272 + nsCOMPtr<nsIDOMNode> listitem=IsInListItem(curNode); 1.9273 + if (listitem) 1.9274 + { 1.9275 + if (indentedLI == listitem) continue; // already indented this list item 1.9276 + curParent = nsEditor::GetNodeLocation(listitem, &offset); 1.9277 + // check to see if curList is still appropriate. Which it is if 1.9278 + // curNode is still right after it in the same list. 1.9279 + if (curList) 1.9280 + { 1.9281 + sibling = nullptr; 1.9282 + NS_ENSURE_STATE(mHTMLEditor); 1.9283 + mHTMLEditor->GetPriorHTMLSibling(curNode, address_of(sibling)); 1.9284 + } 1.9285 + 1.9286 + if (!curList || (sibling && sibling != curList) ) 1.9287 + { 1.9288 + nsAutoString listTag; 1.9289 + nsEditor::GetTagString(curParent,listTag); 1.9290 + ToLowerCase(listTag); 1.9291 + // create a new nested list of correct type 1.9292 + res = SplitAsNeeded(&listTag, address_of(curParent), &offset); 1.9293 + NS_ENSURE_SUCCESS(res, res); 1.9294 + if (!curPositionedDiv) { 1.9295 + int32_t parentOffset; 1.9296 + nsCOMPtr<nsIDOMNode> curParentParent = nsEditor::GetNodeLocation(curParent, &parentOffset); 1.9297 + NS_ENSURE_STATE(mHTMLEditor); 1.9298 + res = mHTMLEditor->CreateNode(divType, curParentParent, parentOffset, getter_AddRefs(curPositionedDiv)); 1.9299 + mNewBlock = curPositionedDiv; 1.9300 + } 1.9301 + NS_ENSURE_STATE(mHTMLEditor); 1.9302 + res = mHTMLEditor->CreateNode(listTag, curPositionedDiv, -1, getter_AddRefs(curList)); 1.9303 + NS_ENSURE_SUCCESS(res, res); 1.9304 + } 1.9305 + NS_ENSURE_STATE(mHTMLEditor); 1.9306 + res = mHTMLEditor->MoveNode(listitem, curList, -1); 1.9307 + NS_ENSURE_SUCCESS(res, res); 1.9308 + // remember we indented this li 1.9309 + indentedLI = listitem; 1.9310 + } 1.9311 + 1.9312 + else 1.9313 + { 1.9314 + // need to make a div to put things in if we haven't already 1.9315 + 1.9316 + if (!curPositionedDiv) 1.9317 + { 1.9318 + if (nsHTMLEditUtils::IsDiv(curNode)) 1.9319 + { 1.9320 + curPositionedDiv = curNode; 1.9321 + mNewBlock = curPositionedDiv; 1.9322 + curList = nullptr; 1.9323 + continue; 1.9324 + } 1.9325 + res = SplitAsNeeded(&divType, address_of(curParent), &offset); 1.9326 + NS_ENSURE_SUCCESS(res, res); 1.9327 + NS_ENSURE_STATE(mHTMLEditor); 1.9328 + res = mHTMLEditor->CreateNode(divType, curParent, offset, getter_AddRefs(curPositionedDiv)); 1.9329 + NS_ENSURE_SUCCESS(res, res); 1.9330 + // remember our new block for postprocessing 1.9331 + mNewBlock = curPositionedDiv; 1.9332 + // curPositionedDiv is now the correct thing to put curNode in 1.9333 + } 1.9334 + 1.9335 + // tuck the node into the end of the active blockquote 1.9336 + NS_ENSURE_STATE(mHTMLEditor); 1.9337 + res = mHTMLEditor->MoveNode(curNode, curPositionedDiv, -1); 1.9338 + NS_ENSURE_SUCCESS(res, res); 1.9339 + // forget curList, if any 1.9340 + curList = nullptr; 1.9341 + } 1.9342 + } 1.9343 + } 1.9344 + return res; 1.9345 +} 1.9346 + 1.9347 +nsresult 1.9348 +nsHTMLEditRules::DidAbsolutePosition() 1.9349 +{ 1.9350 + NS_ENSURE_STATE(mHTMLEditor); 1.9351 + nsCOMPtr<nsIHTMLAbsPosEditor> absPosHTMLEditor = mHTMLEditor; 1.9352 + nsCOMPtr<nsIDOMElement> elt = do_QueryInterface(mNewBlock); 1.9353 + return absPosHTMLEditor->AbsolutelyPositionElement(elt, true); 1.9354 +} 1.9355 + 1.9356 +nsresult 1.9357 +nsHTMLEditRules::WillRemoveAbsolutePosition(Selection* aSelection, 1.9358 + bool* aCancel, bool* aHandled) { 1.9359 + if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; } 1.9360 + nsresult res = WillInsert(aSelection, aCancel); 1.9361 + NS_ENSURE_SUCCESS(res, res); 1.9362 + 1.9363 + // initialize out param 1.9364 + // we want to ignore aCancel from WillInsert() 1.9365 + *aCancel = false; 1.9366 + *aHandled = true; 1.9367 + 1.9368 + nsCOMPtr<nsIDOMElement> elt; 1.9369 + NS_ENSURE_STATE(mHTMLEditor); 1.9370 + res = mHTMLEditor->GetAbsolutelyPositionedSelectionContainer(getter_AddRefs(elt)); 1.9371 + NS_ENSURE_SUCCESS(res, res); 1.9372 + 1.9373 + NS_ENSURE_STATE(mHTMLEditor); 1.9374 + nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor); 1.9375 + 1.9376 + NS_ENSURE_STATE(mHTMLEditor); 1.9377 + nsCOMPtr<nsIHTMLAbsPosEditor> absPosHTMLEditor = mHTMLEditor; 1.9378 + return absPosHTMLEditor->AbsolutelyPositionElement(elt, false); 1.9379 +} 1.9380 + 1.9381 +nsresult 1.9382 +nsHTMLEditRules::WillRelativeChangeZIndex(Selection* aSelection, 1.9383 + int32_t aChange, 1.9384 + bool *aCancel, 1.9385 + bool * aHandled) 1.9386 +{ 1.9387 + if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; } 1.9388 + nsresult res = WillInsert(aSelection, aCancel); 1.9389 + NS_ENSURE_SUCCESS(res, res); 1.9390 + 1.9391 + // initialize out param 1.9392 + // we want to ignore aCancel from WillInsert() 1.9393 + *aCancel = false; 1.9394 + *aHandled = true; 1.9395 + 1.9396 + nsCOMPtr<nsIDOMElement> elt; 1.9397 + NS_ENSURE_STATE(mHTMLEditor); 1.9398 + res = mHTMLEditor->GetAbsolutelyPositionedSelectionContainer(getter_AddRefs(elt)); 1.9399 + NS_ENSURE_SUCCESS(res, res); 1.9400 + 1.9401 + NS_ENSURE_STATE(mHTMLEditor); 1.9402 + nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor); 1.9403 + 1.9404 + NS_ENSURE_STATE(mHTMLEditor); 1.9405 + nsCOMPtr<nsIHTMLAbsPosEditor> absPosHTMLEditor = mHTMLEditor; 1.9406 + int32_t zIndex; 1.9407 + return absPosHTMLEditor->RelativeChangeElementZIndex(elt, aChange, &zIndex); 1.9408 +} 1.9409 + 1.9410 +NS_IMETHODIMP 1.9411 +nsHTMLEditRules::DocumentModified() 1.9412 +{ 1.9413 + nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, &nsHTMLEditRules::DocumentModifiedWorker)); 1.9414 + return NS_OK; 1.9415 +} 1.9416 + 1.9417 +void 1.9418 +nsHTMLEditRules::DocumentModifiedWorker() 1.9419 +{ 1.9420 + if (!mHTMLEditor) { 1.9421 + return; 1.9422 + } 1.9423 + 1.9424 + // DeleteNode below may cause a flush, which could destroy the editor 1.9425 + nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker; 1.9426 + 1.9427 + nsCOMPtr<nsIHTMLEditor> kungFuDeathGrip(mHTMLEditor); 1.9428 + nsCOMPtr<nsISelection> selection; 1.9429 + nsresult rv = mHTMLEditor->GetSelection(getter_AddRefs(selection)); 1.9430 + if (NS_FAILED(rv)) { 1.9431 + return; 1.9432 + } 1.9433 + 1.9434 + // Delete our bogus node, if we have one, since the document might not be 1.9435 + // empty any more. 1.9436 + if (mBogusNode) { 1.9437 + mEditor->DeleteNode(mBogusNode); 1.9438 + mBogusNode = nullptr; 1.9439 + } 1.9440 + 1.9441 + // Try to recreate the bogus node if needed. 1.9442 + CreateBogusNodeIfNeeded(selection); 1.9443 +}