1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=2 sw=2 et tw=79: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Pierre Phaneuf <pp@ludusdesign.com>
25 : * Daniel Glazman <glazman@netscape.com>
26 : * Neil Deakin <neil@mozdevgroup.com>
27 : * Mats Palmgren <matspal@gmail.com>
28 : *
29 : * Alternatively, the contents of this file may be used under the terms of
30 : * either of the GNU General Public License Version 2 or later (the "GPL"),
31 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 : * in which case the provisions of the GPL or the LGPL are applicable instead
33 : * of those above. If you wish to allow use of your version of this file only
34 : * under the terms of either the GPL or the LGPL, and not to allow others to
35 : * use your version of this file under the terms of the MPL, indicate your
36 : * decision by deleting the provisions above and replace them with the notice
37 : * and other provisions required by the GPL or the LGPL. If you do not delete
38 : * the provisions above, a recipient may use your version of this file under
39 : * the terms of any one of the MPL, the GPL or the LGPL.
40 : *
41 : * ***** END LICENSE BLOCK ***** */
42 :
43 : #include "nsHTMLEditRules.h"
44 :
45 : #include "nsEditor.h"
46 : #include "nsTextEditUtils.h"
47 : #include "nsHTMLEditUtils.h"
48 : #include "nsHTMLCSSUtils.h"
49 : #include "nsHTMLEditor.h"
50 :
51 : #include "nsIServiceManager.h"
52 : #include "nsCRT.h"
53 : #include "nsIContent.h"
54 : #include "nsIContentIterator.h"
55 : #include "nsIDOMNode.h"
56 : #include "nsIDOMText.h"
57 : #include "nsIDOMElement.h"
58 : #include "nsIDOMNodeList.h"
59 : #include "nsISelection.h"
60 : #include "nsISelectionPrivate.h"
61 : #include "nsISelectionController.h"
62 : #include "nsIDOMRange.h"
63 : #include "nsIDOMCharacterData.h"
64 : #include "nsIEnumerator.h"
65 : #include "nsIDOMNamedNodeMap.h"
66 : #include "nsRange.h"
67 :
68 : #include "nsEditorUtils.h"
69 : #include "nsWSRunObject.h"
70 :
71 : #include "InsertTextTxn.h"
72 : #include "DeleteTextTxn.h"
73 : #include "nsReadableUtils.h"
74 : #include "nsUnicharUtils.h"
75 :
76 : #include "nsFrameSelection.h"
77 : #include "nsContentUtils.h"
78 : #include "nsTArray.h"
79 : #include "nsIHTMLDocument.h"
80 :
81 : #include "mozilla/Preferences.h"
82 : #include "mozilla/dom/Element.h"
83 :
84 : using namespace mozilla;
85 :
86 : //const static char* kMOZEditorBogusNodeAttr="MOZ_EDITOR_BOGUS_NODE";
87 : //const static char* kMOZEditorBogusNodeValue="TRUE";
88 :
89 : enum
90 : {
91 : kLonely = 0,
92 : kPrevSib = 1,
93 : kNextSib = 2,
94 : kBothSibs = 3
95 : };
96 :
97 : /********************************************************
98 : * first some helpful funcotrs we will use
99 : ********************************************************/
100 :
101 0 : static bool IsBlockNode(nsIDOMNode* node)
102 : {
103 0 : bool isBlock (false);
104 0 : nsHTMLEditor::NodeIsBlockStatic(node, &isBlock);
105 0 : return isBlock;
106 : }
107 :
108 0 : static bool IsInlineNode(nsIDOMNode* node)
109 : {
110 0 : return !IsBlockNode(node);
111 : }
112 :
113 : class nsTableCellAndListItemFunctor : public nsBoolDomIterFunctor
114 0 : {
115 : public:
116 0 : virtual bool operator()(nsIDOMNode* aNode) // used to build list of all li's, td's & th's iterator covers
117 : {
118 0 : if (nsHTMLEditUtils::IsTableCell(aNode)) return true;
119 0 : if (nsHTMLEditUtils::IsListItem(aNode)) return true;
120 0 : return false;
121 : }
122 : };
123 :
124 : class nsBRNodeFunctor : public nsBoolDomIterFunctor
125 0 : {
126 : public:
127 0 : virtual bool operator()(nsIDOMNode* aNode)
128 : {
129 0 : if (nsTextEditUtils::IsBreak(aNode)) return true;
130 0 : return false;
131 : }
132 : };
133 :
134 : class nsEmptyEditableFunctor : public nsBoolDomIterFunctor
135 : {
136 : public:
137 0 : nsEmptyEditableFunctor(nsHTMLEditor* editor) : mHTMLEditor(editor) {}
138 0 : virtual bool operator()(nsIDOMNode* aNode)
139 : {
140 0 : if (mHTMLEditor->IsEditable(aNode) &&
141 0 : (nsHTMLEditUtils::IsListItem(aNode) ||
142 0 : nsHTMLEditUtils::IsTableCellOrCaption(aNode)))
143 : {
144 : bool bIsEmptyNode;
145 0 : nsresult res = mHTMLEditor->IsEmptyNode(aNode, &bIsEmptyNode, false, false);
146 0 : NS_ENSURE_SUCCESS(res, false);
147 0 : if (bIsEmptyNode)
148 0 : return true;
149 : }
150 0 : return false;
151 : }
152 : protected:
153 : nsHTMLEditor* mHTMLEditor;
154 : };
155 :
156 : class nsEditableTextFunctor : public nsBoolDomIterFunctor
157 : {
158 : public:
159 : nsEditableTextFunctor(nsHTMLEditor* editor) : mHTMLEditor(editor) {}
160 : virtual bool operator()(nsIDOMNode* aNode)
161 : {
162 : if (nsEditor::IsTextNode(aNode) && mHTMLEditor->IsEditable(aNode))
163 : {
164 : return true;
165 : }
166 : return false;
167 : }
168 : protected:
169 : nsHTMLEditor* mHTMLEditor;
170 : };
171 :
172 :
173 : /********************************************************
174 : * Constructor/Destructor
175 : ********************************************************/
176 :
177 0 : nsHTMLEditRules::nsHTMLEditRules() :
178 : mDocChangeRange(nsnull)
179 : ,mListenerEnabled(true)
180 : ,mReturnInEmptyLIKillsList(true)
181 : ,mDidDeleteSelection(false)
182 : ,mDidRangedDelete(false)
183 : ,mRestoreContentEditableCount(false)
184 : ,mUtilRange(nsnull)
185 0 : ,mJoinOffset(0)
186 : {
187 : // populate mCachedStyles
188 0 : mCachedStyles[0] = StyleCache(nsEditProperty::b, EmptyString(), EmptyString());
189 0 : mCachedStyles[1] = StyleCache(nsEditProperty::i, EmptyString(), EmptyString());
190 0 : mCachedStyles[2] = StyleCache(nsEditProperty::u, EmptyString(), EmptyString());
191 0 : mCachedStyles[3] = StyleCache(nsEditProperty::font, NS_LITERAL_STRING("face"), EmptyString());
192 0 : mCachedStyles[4] = StyleCache(nsEditProperty::font, NS_LITERAL_STRING("size"), EmptyString());
193 0 : mCachedStyles[5] = StyleCache(nsEditProperty::font, NS_LITERAL_STRING("color"), EmptyString());
194 0 : mCachedStyles[6] = StyleCache(nsEditProperty::tt, EmptyString(), EmptyString());
195 0 : mCachedStyles[7] = StyleCache(nsEditProperty::em, EmptyString(), EmptyString());
196 0 : mCachedStyles[8] = StyleCache(nsEditProperty::strong, EmptyString(), EmptyString());
197 0 : mCachedStyles[9] = StyleCache(nsEditProperty::dfn, EmptyString(), EmptyString());
198 0 : mCachedStyles[10] = StyleCache(nsEditProperty::code, EmptyString(), EmptyString());
199 0 : mCachedStyles[11] = StyleCache(nsEditProperty::samp, EmptyString(), EmptyString());
200 0 : mCachedStyles[12] = StyleCache(nsEditProperty::var, EmptyString(), EmptyString());
201 0 : mCachedStyles[13] = StyleCache(nsEditProperty::cite, EmptyString(), EmptyString());
202 0 : mCachedStyles[14] = StyleCache(nsEditProperty::abbr, EmptyString(), EmptyString());
203 0 : mCachedStyles[15] = StyleCache(nsEditProperty::acronym, EmptyString(), EmptyString());
204 0 : mCachedStyles[16] = StyleCache(nsEditProperty::cssBackgroundColor, EmptyString(), EmptyString());
205 0 : mCachedStyles[17] = StyleCache(nsEditProperty::sub, EmptyString(), EmptyString());
206 0 : mCachedStyles[18] = StyleCache(nsEditProperty::sup, EmptyString(), EmptyString());
207 0 : }
208 :
209 0 : nsHTMLEditRules::~nsHTMLEditRules()
210 : {
211 : // remove ourselves as a listener to edit actions
212 : // In some cases, we have already been removed by
213 : // ~nsHTMLEditor, in which case we will get a null pointer here
214 : // which we ignore. But this allows us to add the ability to
215 : // switch rule sets on the fly if we want.
216 0 : if (mHTMLEditor)
217 0 : mHTMLEditor->RemoveEditActionListener(this);
218 0 : }
219 :
220 : /********************************************************
221 : * XPCOM Cruft
222 : ********************************************************/
223 :
224 0 : NS_IMPL_ADDREF_INHERITED(nsHTMLEditRules, nsTextEditRules)
225 0 : NS_IMPL_RELEASE_INHERITED(nsHTMLEditRules, nsTextEditRules)
226 0 : NS_IMPL_QUERY_INTERFACE_INHERITED1(nsHTMLEditRules, nsTextEditRules, nsIEditActionListener)
227 :
228 :
229 : /********************************************************
230 : * Public methods
231 : ********************************************************/
232 :
233 : NS_IMETHODIMP
234 0 : nsHTMLEditRules::Init(nsPlaintextEditor *aEditor)
235 : {
236 0 : mHTMLEditor = static_cast<nsHTMLEditor*>(aEditor);
237 : nsresult res;
238 :
239 : // call through to base class Init
240 0 : res = nsTextEditRules::Init(aEditor);
241 0 : NS_ENSURE_SUCCESS(res, res);
242 :
243 : // cache any prefs we care about
244 : static const char kPrefName[] =
245 : "editor.html.typing.returnInEmptyListItemClosesList";
246 : nsAdoptingCString returnInEmptyLIKillsList =
247 0 : Preferences::GetCString(kPrefName);
248 :
249 : // only when "false", becomes FALSE. Otherwise (including empty), TRUE.
250 : // XXX Why was this pref designed as a string and not bool?
251 0 : mReturnInEmptyLIKillsList = !returnInEmptyLIKillsList.EqualsLiteral("false");
252 :
253 : // make a utility range for use by the listenter
254 0 : mUtilRange = new nsRange();
255 :
256 : // set up mDocChangeRange to be whole doc
257 0 : nsCOMPtr<nsIDOMElement> rootElem = do_QueryInterface(mHTMLEditor->GetRoot());
258 0 : if (rootElem)
259 : {
260 : // temporarily turn off rules sniffing
261 0 : nsAutoLockRulesSniffing lockIt((nsTextEditRules*)this);
262 0 : if (!mDocChangeRange)
263 : {
264 0 : mDocChangeRange = new nsRange();
265 : }
266 0 : mDocChangeRange->SelectNode(rootElem);
267 0 : res = AdjustSpecialBreaks();
268 0 : NS_ENSURE_SUCCESS(res, res);
269 : }
270 :
271 : // add ourselves as a listener to edit actions
272 0 : res = mHTMLEditor->AddEditActionListener(this);
273 :
274 0 : return res;
275 : }
276 :
277 : NS_IMETHODIMP
278 0 : nsHTMLEditRules::DetachEditor()
279 : {
280 0 : mHTMLEditor = nsnull;
281 0 : return nsTextEditRules::DetachEditor();
282 : }
283 :
284 : NS_IMETHODIMP
285 0 : nsHTMLEditRules::BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection)
286 : {
287 0 : if (mLockRulesSniffing) return NS_OK;
288 :
289 0 : nsAutoLockRulesSniffing lockIt((nsTextEditRules*)this);
290 0 : mDidExplicitlySetInterline = false;
291 :
292 0 : if (!mActionNesting++)
293 : {
294 : // clear our flag about if just deleted a range
295 0 : mDidRangedDelete = false;
296 :
297 : // remember where our selection was before edit action took place:
298 :
299 : // get selection
300 0 : nsCOMPtr<nsISelection> selection;
301 0 : nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
302 0 : NS_ENSURE_SUCCESS(res, res);
303 :
304 : // get the selection start location
305 0 : nsCOMPtr<nsIDOMNode> selStartNode, selEndNode;
306 : PRInt32 selOffset;
307 0 : res = mHTMLEditor->GetStartNodeAndOffset(selection, getter_AddRefs(selStartNode), &selOffset);
308 0 : NS_ENSURE_SUCCESS(res, res);
309 0 : mRangeItem.startNode = selStartNode;
310 0 : mRangeItem.startOffset = selOffset;
311 :
312 : // get the selection end location
313 0 : res = mHTMLEditor->GetEndNodeAndOffset(selection, getter_AddRefs(selEndNode), &selOffset);
314 0 : NS_ENSURE_SUCCESS(res, res);
315 0 : mRangeItem.endNode = selEndNode;
316 0 : mRangeItem.endOffset = selOffset;
317 :
318 : // register this range with range updater to track this as we perturb the doc
319 0 : (mHTMLEditor->mRangeUpdater).RegisterRangeItem(&mRangeItem);
320 :
321 : // clear deletion state bool
322 0 : mDidDeleteSelection = false;
323 :
324 : // clear out mDocChangeRange and mUtilRange
325 0 : if(mDocChangeRange)
326 : {
327 : // clear out our accounting of what changed
328 0 : mDocChangeRange->Reset();
329 : }
330 0 : if(mUtilRange)
331 : {
332 : // ditto for mUtilRange.
333 0 : mUtilRange->Reset();
334 : }
335 :
336 : // remember current inline styles for deletion and normal insertion operations
337 0 : if ((action == nsEditor::kOpInsertText) ||
338 : (action == nsEditor::kOpInsertIMEText) ||
339 : (action == nsEditor::kOpDeleteSelection) ||
340 : (action == nsEditor::kOpInsertBreak))
341 : {
342 0 : nsCOMPtr<nsIDOMNode> selNode = selStartNode;
343 0 : if (aDirection == nsIEditor::eNext)
344 0 : selNode = selEndNode;
345 0 : res = CacheInlineStyles(selNode);
346 0 : NS_ENSURE_SUCCESS(res, res);
347 : }
348 :
349 : // Stabilize the document against contenteditable count changes
350 0 : nsCOMPtr<nsIDOMDocument> doc;
351 0 : res = mHTMLEditor->GetDocument(getter_AddRefs(doc));
352 0 : NS_ENSURE_SUCCESS(res, res);
353 0 : nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(doc);
354 0 : NS_ENSURE_TRUE(htmlDoc, NS_ERROR_FAILURE);
355 0 : if (htmlDoc->GetEditingState() == nsIHTMLDocument::eContentEditable) {
356 0 : htmlDoc->ChangeContentEditableCount(nsnull, +1);
357 0 : mRestoreContentEditableCount = true;
358 : }
359 :
360 : // check that selection is in subtree defined by body node
361 0 : ConfirmSelectionInBody();
362 : // let rules remember the top level action
363 0 : mTheAction = action;
364 : }
365 0 : return NS_OK;
366 : }
367 :
368 :
369 : NS_IMETHODIMP
370 0 : nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection)
371 : {
372 0 : if (mLockRulesSniffing) return NS_OK;
373 :
374 0 : nsAutoLockRulesSniffing lockIt(this);
375 :
376 0 : NS_PRECONDITION(mActionNesting>0, "bad action nesting!");
377 0 : nsresult res = NS_OK;
378 0 : if (!--mActionNesting)
379 : {
380 : // do all the tricky stuff
381 0 : res = AfterEditInner(action, aDirection);
382 :
383 : // free up selectionState range item
384 0 : (mHTMLEditor->mRangeUpdater).DropRangeItem(&mRangeItem);
385 :
386 : // Reset the contenteditable count to its previous value
387 0 : if (mRestoreContentEditableCount) {
388 0 : nsCOMPtr<nsIDOMDocument> doc;
389 0 : res = mHTMLEditor->GetDocument(getter_AddRefs(doc));
390 0 : NS_ENSURE_SUCCESS(res, res);
391 0 : nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(doc);
392 0 : NS_ENSURE_TRUE(htmlDoc, NS_ERROR_FAILURE);
393 0 : if (htmlDoc->GetEditingState() == nsIHTMLDocument::eContentEditable) {
394 0 : htmlDoc->ChangeContentEditableCount(nsnull, -1);
395 : }
396 0 : mRestoreContentEditableCount = false;
397 : }
398 : }
399 :
400 0 : return res;
401 : }
402 :
403 :
404 : nsresult
405 0 : nsHTMLEditRules::AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection)
406 : {
407 0 : ConfirmSelectionInBody();
408 0 : if (action == nsEditor::kOpIgnore) return NS_OK;
409 :
410 0 : nsCOMPtr<nsISelection>selection;
411 0 : nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
412 0 : NS_ENSURE_SUCCESS(res, res);
413 :
414 0 : nsCOMPtr<nsIDOMNode> rangeStartParent, rangeEndParent;
415 0 : PRInt32 rangeStartOffset = 0, rangeEndOffset = 0;
416 : // do we have a real range to act on?
417 0 : bool bDamagedRange = false;
418 0 : if (mDocChangeRange)
419 : {
420 0 : mDocChangeRange->GetStartContainer(getter_AddRefs(rangeStartParent));
421 0 : mDocChangeRange->GetEndContainer(getter_AddRefs(rangeEndParent));
422 0 : mDocChangeRange->GetStartOffset(&rangeStartOffset);
423 0 : mDocChangeRange->GetEndOffset(&rangeEndOffset);
424 0 : if (rangeStartParent && rangeEndParent)
425 0 : bDamagedRange = true;
426 : }
427 :
428 0 : if (bDamagedRange && !((action == nsEditor::kOpUndo) || (action == nsEditor::kOpRedo)))
429 : {
430 : // don't let any txns in here move the selection around behind our back.
431 : // Note that this won't prevent explicit selection setting from working.
432 0 : nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
433 :
434 : // expand the "changed doc range" as needed
435 0 : res = PromoteRange(mDocChangeRange, action);
436 0 : NS_ENSURE_SUCCESS(res, res);
437 :
438 : // if we did a ranged deletion, make sure we have a place to put caret.
439 : // Note we only want to do this if the overall operation was deletion,
440 : // not if deletion was done along the way for kOpLoadHTML, kOpInsertText, etc.
441 : // That's why this is here rather than DidDeleteSelection().
442 0 : if ((action == nsEditor::kOpDeleteSelection) && mDidRangedDelete)
443 : {
444 0 : res = InsertBRIfNeeded(selection);
445 0 : NS_ENSURE_SUCCESS(res, res);
446 : }
447 :
448 : // add in any needed <br>s, and remove any unneeded ones.
449 0 : res = AdjustSpecialBreaks();
450 0 : NS_ENSURE_SUCCESS(res, res);
451 :
452 : // merge any adjacent text nodes
453 0 : if ( (action != nsEditor::kOpInsertText &&
454 : action != nsEditor::kOpInsertIMEText) )
455 : {
456 0 : res = mHTMLEditor->CollapseAdjacentTextNodes(mDocChangeRange);
457 0 : NS_ENSURE_SUCCESS(res, res);
458 : }
459 :
460 : // clean up any empty nodes in the selection
461 0 : res = RemoveEmptyNodes();
462 0 : NS_ENSURE_SUCCESS(res, res);
463 :
464 : // attempt to transform any unneeded nbsp's into spaces after doing various operations
465 0 : if ((action == nsEditor::kOpInsertText) ||
466 : (action == nsEditor::kOpInsertIMEText) ||
467 : (action == nsEditor::kOpDeleteSelection) ||
468 : (action == nsEditor::kOpInsertBreak) ||
469 : (action == nsHTMLEditor::kOpHTMLPaste ||
470 : (action == nsHTMLEditor::kOpLoadHTML)))
471 : {
472 0 : res = AdjustWhitespace(selection);
473 0 : NS_ENSURE_SUCCESS(res, res);
474 :
475 : // also do this for original selection endpoints.
476 0 : nsWSRunObject(mHTMLEditor, mRangeItem.startNode, mRangeItem.startOffset).AdjustWhitespace();
477 : // we only need to handle old selection endpoint if it was different from start
478 0 : if ((mRangeItem.startNode != mRangeItem.endNode) || (mRangeItem.startOffset != mRangeItem.endOffset))
479 : {
480 0 : nsWSRunObject(mHTMLEditor, mRangeItem.endNode, mRangeItem.endOffset).AdjustWhitespace();
481 : }
482 : }
483 :
484 : // if we created a new block, make sure selection lands in it
485 0 : if (mNewBlock)
486 : {
487 0 : res = PinSelectionToNewBlock(selection);
488 0 : mNewBlock = 0;
489 : }
490 :
491 : // adjust selection for insert text, html paste, and delete actions
492 0 : if ((action == nsEditor::kOpInsertText) ||
493 : (action == nsEditor::kOpInsertIMEText) ||
494 : (action == nsEditor::kOpDeleteSelection) ||
495 : (action == nsEditor::kOpInsertBreak) ||
496 : (action == nsHTMLEditor::kOpHTMLPaste ||
497 : (action == nsHTMLEditor::kOpLoadHTML)))
498 : {
499 0 : res = AdjustSelection(selection, aDirection);
500 0 : NS_ENSURE_SUCCESS(res, res);
501 : }
502 :
503 : // check for any styles which were removed inappropriately
504 0 : if ((action == nsEditor::kOpInsertText) ||
505 : (action == nsEditor::kOpInsertIMEText) ||
506 : (action == nsEditor::kOpDeleteSelection) ||
507 : (action == nsEditor::kOpInsertBreak))
508 : {
509 0 : mHTMLEditor->mTypeInState->UpdateSelState(selection);
510 0 : res = ReapplyCachedStyles();
511 0 : NS_ENSURE_SUCCESS(res, res);
512 0 : res = ClearCachedStyles();
513 0 : NS_ENSURE_SUCCESS(res, res);
514 : }
515 : }
516 :
517 : res = mHTMLEditor->HandleInlineSpellCheck(action, selection,
518 : mRangeItem.startNode, mRangeItem.startOffset,
519 : rangeStartParent, rangeStartOffset,
520 0 : rangeEndParent, rangeEndOffset);
521 0 : NS_ENSURE_SUCCESS(res, res);
522 :
523 : // detect empty doc
524 0 : res = CreateBogusNodeIfNeeded(selection);
525 :
526 : // adjust selection HINT if needed
527 0 : NS_ENSURE_SUCCESS(res, res);
528 :
529 0 : if (!mDidExplicitlySetInterline)
530 : {
531 0 : res = CheckInterlinePosition(selection);
532 : }
533 :
534 0 : return res;
535 : }
536 :
537 :
538 : NS_IMETHODIMP
539 0 : nsHTMLEditRules::WillDoAction(nsISelection *aSelection,
540 : nsRulesInfo *aInfo,
541 : bool *aCancel,
542 : bool *aHandled)
543 : {
544 0 : NS_ENSURE_TRUE(aInfo && aCancel && aHandled, NS_ERROR_NULL_POINTER);
545 : #if defined(DEBUG_ftang)
546 : printf("nsHTMLEditRules::WillDoAction action = %d\n", aInfo->action);
547 : #endif
548 :
549 0 : *aCancel = false;
550 0 : *aHandled = false;
551 :
552 : // my kingdom for dynamic cast
553 0 : nsTextRulesInfo *info = static_cast<nsTextRulesInfo*>(aInfo);
554 :
555 : // Deal with actions for which we don't need to check whether the selection is
556 : // editable.
557 0 : if (info->action == kOutputText ||
558 : info->action == kUndo ||
559 : info->action == kRedo)
560 : {
561 0 : return nsTextEditRules::WillDoAction(aSelection, aInfo, aCancel, aHandled);
562 : }
563 :
564 0 : nsCOMPtr<nsIDOMRange> domRange;
565 0 : nsresult rv = aSelection->GetRangeAt(0, getter_AddRefs(domRange));
566 0 : NS_ENSURE_SUCCESS(rv, rv);
567 :
568 0 : nsCOMPtr<nsIDOMNode> selStartNode;
569 0 : rv = domRange->GetStartContainer(getter_AddRefs(selStartNode));
570 0 : NS_ENSURE_SUCCESS(rv, rv);
571 :
572 0 : if (!mHTMLEditor->IsModifiableNode(selStartNode))
573 : {
574 0 : *aCancel = true;
575 :
576 0 : return NS_OK;
577 : }
578 :
579 0 : nsCOMPtr<nsIDOMNode> selEndNode;
580 0 : rv = domRange->GetEndContainer(getter_AddRefs(selEndNode));
581 0 : NS_ENSURE_SUCCESS(rv, rv);
582 :
583 0 : if (selStartNode != selEndNode)
584 : {
585 0 : if (!mHTMLEditor->IsModifiableNode(selEndNode))
586 : {
587 0 : *aCancel = true;
588 :
589 0 : return NS_OK;
590 : }
591 :
592 0 : nsRange* range = static_cast<nsRange*>(domRange.get());
593 : nsCOMPtr<nsIDOMNode> ancestor =
594 0 : do_QueryInterface(range->GetCommonAncestor());
595 0 : if (!mHTMLEditor->IsModifiableNode(ancestor))
596 : {
597 0 : *aCancel = true;
598 :
599 0 : return NS_OK;
600 : }
601 : }
602 :
603 0 : switch (info->action)
604 : {
605 : case kInsertText:
606 : case kInsertTextIME:
607 : return WillInsertText(info->action,
608 : aSelection,
609 : aCancel,
610 : aHandled,
611 : info->inString,
612 : info->outString,
613 0 : info->maxLength);
614 : case kLoadHTML:
615 0 : return WillLoadHTML(aSelection, aCancel);
616 : case kInsertBreak:
617 0 : return WillInsertBreak(aSelection, aCancel, aHandled);
618 : case kDeleteSelection:
619 0 : return WillDeleteSelection(aSelection, info->collapsedAction, aCancel, aHandled);
620 : case kMakeList:
621 0 : return WillMakeList(aSelection, info->blockType, info->entireList, info->bulletType, aCancel, aHandled);
622 : case kIndent:
623 0 : return WillIndent(aSelection, aCancel, aHandled);
624 : case kOutdent:
625 0 : return WillOutdent(aSelection, aCancel, aHandled);
626 : case kSetAbsolutePosition:
627 0 : return WillAbsolutePosition(aSelection, aCancel, aHandled);
628 : case kRemoveAbsolutePosition:
629 0 : return WillRemoveAbsolutePosition(aSelection, aCancel, aHandled);
630 : case kAlign:
631 0 : return WillAlign(aSelection, info->alignType, aCancel, aHandled);
632 : case kMakeBasicBlock:
633 0 : return WillMakeBasicBlock(aSelection, info->blockType, aCancel, aHandled);
634 : case kRemoveList:
635 0 : return WillRemoveList(aSelection, info->bOrdered, aCancel, aHandled);
636 : case kMakeDefListItem:
637 0 : return WillMakeDefListItem(aSelection, info->blockType, info->entireList, aCancel, aHandled);
638 : case kInsertElement:
639 0 : return WillInsert(aSelection, aCancel);
640 : case kDecreaseZIndex:
641 0 : return WillRelativeChangeZIndex(aSelection, -1, aCancel, aHandled);
642 : case kIncreaseZIndex:
643 0 : return WillRelativeChangeZIndex(aSelection, 1, aCancel, aHandled);
644 : }
645 0 : return nsTextEditRules::WillDoAction(aSelection, aInfo, aCancel, aHandled);
646 : }
647 :
648 :
649 : NS_IMETHODIMP
650 0 : nsHTMLEditRules::DidDoAction(nsISelection *aSelection,
651 : nsRulesInfo *aInfo, nsresult aResult)
652 : {
653 0 : nsTextRulesInfo *info = static_cast<nsTextRulesInfo*>(aInfo);
654 0 : switch (info->action)
655 : {
656 : case kInsertBreak:
657 0 : return DidInsertBreak(aSelection, aResult);
658 : case kDeleteSelection:
659 0 : return DidDeleteSelection(aSelection, info->collapsedAction, aResult);
660 : case kMakeBasicBlock:
661 : case kIndent:
662 : case kOutdent:
663 : case kAlign:
664 0 : return DidMakeBasicBlock(aSelection, aInfo, aResult);
665 : case kSetAbsolutePosition: {
666 0 : nsresult rv = DidMakeBasicBlock(aSelection, aInfo, aResult);
667 0 : NS_ENSURE_SUCCESS(rv, rv);
668 0 : return DidAbsolutePosition();
669 : }
670 : }
671 :
672 : // default: pass thru to nsTextEditRules
673 0 : return nsTextEditRules::DidDoAction(aSelection, aInfo, aResult);
674 : }
675 :
676 : nsresult
677 0 : nsHTMLEditRules::GetListState(bool *aMixed, bool *aOL, bool *aUL, bool *aDL)
678 : {
679 0 : NS_ENSURE_TRUE(aMixed && aOL && aUL && aDL, NS_ERROR_NULL_POINTER);
680 0 : *aMixed = false;
681 0 : *aOL = false;
682 0 : *aUL = false;
683 0 : *aDL = false;
684 0 : bool bNonList = false;
685 :
686 0 : nsCOMArray<nsIDOMNode> arrayOfNodes;
687 0 : nsresult res = GetListActionNodes(arrayOfNodes, false, true);
688 0 : NS_ENSURE_SUCCESS(res, res);
689 :
690 : // examine list type for nodes in selection
691 0 : PRInt32 listCount = arrayOfNodes.Count();
692 : PRInt32 i;
693 0 : for (i=listCount-1; i>=0; i--)
694 : {
695 0 : nsIDOMNode* curNode = arrayOfNodes[i];
696 :
697 0 : if (nsHTMLEditUtils::IsUnorderedList(curNode))
698 0 : *aUL = true;
699 0 : else if (nsHTMLEditUtils::IsOrderedList(curNode))
700 0 : *aOL = true;
701 0 : else if (nsEditor::NodeIsType(curNode, nsEditProperty::li))
702 : {
703 0 : nsCOMPtr<nsIDOMNode> parent;
704 : PRInt32 offset;
705 0 : res = nsEditor::GetNodeLocation(curNode, address_of(parent), &offset);
706 0 : NS_ENSURE_SUCCESS(res, res);
707 0 : if (nsHTMLEditUtils::IsUnorderedList(parent))
708 0 : *aUL = true;
709 0 : else if (nsHTMLEditUtils::IsOrderedList(parent))
710 0 : *aOL = true;
711 : }
712 0 : else if (nsEditor::NodeIsType(curNode, nsEditProperty::dl) ||
713 0 : nsEditor::NodeIsType(curNode, nsEditProperty::dt) ||
714 0 : nsEditor::NodeIsType(curNode, nsEditProperty::dd) )
715 : {
716 0 : *aDL = true;
717 : }
718 0 : else bNonList = true;
719 : }
720 :
721 : // hokey arithmetic with booleans
722 0 : if ( (*aUL + *aOL + *aDL + bNonList) > 1) *aMixed = true;
723 :
724 0 : return res;
725 : }
726 :
727 : nsresult
728 0 : nsHTMLEditRules::GetListItemState(bool *aMixed, bool *aLI, bool *aDT, bool *aDD)
729 : {
730 0 : NS_ENSURE_TRUE(aMixed && aLI && aDT && aDD, NS_ERROR_NULL_POINTER);
731 0 : *aMixed = false;
732 0 : *aLI = false;
733 0 : *aDT = false;
734 0 : *aDD = false;
735 0 : bool bNonList = false;
736 :
737 0 : nsCOMArray<nsIDOMNode> arrayOfNodes;
738 0 : nsresult res = GetListActionNodes(arrayOfNodes, false, true);
739 0 : NS_ENSURE_SUCCESS(res, res);
740 :
741 : // examine list type for nodes in selection
742 0 : PRInt32 listCount = arrayOfNodes.Count();
743 : PRInt32 i;
744 0 : for (i = listCount-1; i>=0; i--)
745 : {
746 0 : nsIDOMNode* curNode = arrayOfNodes[i];
747 :
748 0 : if (nsHTMLEditUtils::IsUnorderedList(curNode) ||
749 0 : nsHTMLEditUtils::IsOrderedList(curNode) ||
750 0 : nsEditor::NodeIsType(curNode, nsEditProperty::li) )
751 : {
752 0 : *aLI = true;
753 : }
754 0 : else if (nsEditor::NodeIsType(curNode, nsEditProperty::dt))
755 : {
756 0 : *aDT = true;
757 : }
758 0 : else if (nsEditor::NodeIsType(curNode, nsEditProperty::dd))
759 : {
760 0 : *aDD = true;
761 : }
762 0 : else if (nsEditor::NodeIsType(curNode, nsEditProperty::dl))
763 : {
764 : // need to look inside dl and see which types of items it has
765 : bool bDT, bDD;
766 0 : res = GetDefinitionListItemTypes(curNode, bDT, bDD);
767 0 : NS_ENSURE_SUCCESS(res, res);
768 0 : *aDT |= bDT;
769 0 : *aDD |= bDD;
770 : }
771 0 : else bNonList = true;
772 : }
773 :
774 : // hokey arithmetic with booleans
775 0 : if ( (*aDT + *aDD + bNonList) > 1) *aMixed = true;
776 :
777 0 : return res;
778 : }
779 :
780 : nsresult
781 0 : nsHTMLEditRules::GetAlignment(bool *aMixed, nsIHTMLEditor::EAlignment *aAlign)
782 : {
783 : // for now, just return first alignment. we'll lie about
784 : // if it's mixed. This is for efficiency
785 : // given that our current ui doesn't care if it's mixed.
786 : // cmanske: NOT TRUE! We would like to pay attention to mixed state
787 : // in Format | Align submenu!
788 :
789 : // this routine assumes that alignment is done ONLY via divs
790 :
791 : // default alignment is left
792 0 : NS_ENSURE_TRUE(aMixed && aAlign, NS_ERROR_NULL_POINTER);
793 0 : *aMixed = false;
794 0 : *aAlign = nsIHTMLEditor::eLeft;
795 :
796 : // get selection
797 0 : nsCOMPtr<nsISelection>selection;
798 0 : nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
799 0 : NS_ENSURE_SUCCESS(res, res);
800 :
801 : // get selection location
802 0 : nsCOMPtr<nsIDOMNode> parent;
803 0 : nsCOMPtr<nsIDOMElement> rootElem = do_QueryInterface(mHTMLEditor->GetRoot());
804 0 : NS_ENSURE_TRUE(rootElem, NS_ERROR_FAILURE);
805 :
806 : PRInt32 offset, rootOffset;
807 0 : res = nsEditor::GetNodeLocation(rootElem, address_of(parent), &rootOffset);
808 0 : NS_ENSURE_SUCCESS(res, res);
809 0 : res = mHTMLEditor->GetStartNodeAndOffset(selection, getter_AddRefs(parent), &offset);
810 0 : NS_ENSURE_SUCCESS(res, res);
811 :
812 : // is the selection collapsed?
813 : bool bCollapsed;
814 0 : res = selection->GetIsCollapsed(&bCollapsed);
815 0 : NS_ENSURE_SUCCESS(res, res);
816 0 : nsCOMPtr<nsIDOMNode> nodeToExamine;
817 0 : nsCOMPtr<nsISupports> isupports;
818 0 : if (bCollapsed)
819 : {
820 : // if it is, we want to look at 'parent' and its ancestors
821 : // for divs with alignment on them
822 0 : nodeToExamine = parent;
823 : }
824 0 : else if (mHTMLEditor->IsTextNode(parent))
825 : {
826 : // if we are in a text node, then that is the node of interest
827 0 : nodeToExamine = parent;
828 : }
829 0 : else if (nsEditor::NodeIsType(parent, nsEditProperty::html) &&
830 : offset == rootOffset)
831 : {
832 : // if we have selected the body, let's look at the first editable node
833 0 : mHTMLEditor->GetNextNode(parent, offset, true, address_of(nodeToExamine));
834 : }
835 : else
836 : {
837 0 : nsCOMArray<nsIDOMRange> arrayOfRanges;
838 0 : res = GetPromotedRanges(selection, arrayOfRanges, kAlign);
839 0 : NS_ENSURE_SUCCESS(res, res);
840 :
841 : // use these ranges to construct a list of nodes to act on.
842 0 : nsCOMArray<nsIDOMNode> arrayOfNodes;
843 0 : res = GetNodesForOperation(arrayOfRanges, arrayOfNodes, kAlign, true);
844 0 : NS_ENSURE_SUCCESS(res, res);
845 0 : nodeToExamine = arrayOfNodes.SafeObjectAt(0);
846 : }
847 :
848 0 : NS_ENSURE_TRUE(nodeToExamine, NS_ERROR_NULL_POINTER);
849 :
850 0 : NS_NAMED_LITERAL_STRING(typeAttrName, "align");
851 0 : nsIAtom *dummyProperty = nsnull;
852 0 : nsCOMPtr<nsIDOMNode> blockParent;
853 0 : if (mHTMLEditor->IsBlockNode(nodeToExamine))
854 0 : blockParent = nodeToExamine;
855 : else
856 0 : blockParent = mHTMLEditor->GetBlockNodeParent(nodeToExamine);
857 :
858 0 : NS_ENSURE_TRUE(blockParent, NS_ERROR_FAILURE);
859 :
860 0 : if (mHTMLEditor->IsCSSEnabled())
861 : {
862 0 : nsCOMPtr<nsIContent> blockParentContent = do_QueryInterface(blockParent);
863 0 : if (blockParentContent &&
864 0 : mHTMLEditor->mHTMLCSSUtils->IsCSSEditableProperty(blockParentContent, dummyProperty, &typeAttrName))
865 : {
866 : // we are in CSS mode and we know how to align this element with CSS
867 0 : nsAutoString value;
868 : // let's get the value(s) of text-align or margin-left/margin-right
869 : mHTMLEditor->mHTMLCSSUtils->GetCSSEquivalentToHTMLInlineStyleSet(
870 : blockParentContent, dummyProperty, &typeAttrName, value,
871 0 : COMPUTED_STYLE_TYPE);
872 0 : if (value.EqualsLiteral("center") ||
873 0 : value.EqualsLiteral("-moz-center") ||
874 0 : value.EqualsLiteral("auto auto"))
875 : {
876 0 : *aAlign = nsIHTMLEditor::eCenter;
877 0 : return NS_OK;
878 : }
879 0 : if (value.EqualsLiteral("right") ||
880 0 : value.EqualsLiteral("-moz-right") ||
881 0 : value.EqualsLiteral("auto 0px"))
882 : {
883 0 : *aAlign = nsIHTMLEditor::eRight;
884 0 : return NS_OK;
885 : }
886 0 : if (value.EqualsLiteral("justify"))
887 : {
888 0 : *aAlign = nsIHTMLEditor::eJustify;
889 0 : return NS_OK;
890 : }
891 0 : *aAlign = nsIHTMLEditor::eLeft;
892 0 : return NS_OK;
893 : }
894 : }
895 :
896 : // check up the ladder for divs with alignment
897 0 : nsCOMPtr<nsIDOMNode> temp = nodeToExamine;
898 0 : bool isFirstNodeToExamine = true;
899 0 : while (nodeToExamine)
900 : {
901 0 : if (!isFirstNodeToExamine && nsHTMLEditUtils::IsTable(nodeToExamine))
902 : {
903 : // the node to examine is a table and this is not the first node
904 : // we examine; let's break here to materialize the 'inline-block'
905 : // behaviour of html tables regarding to text alignment
906 0 : return NS_OK;
907 : }
908 0 : if (nsHTMLEditUtils::SupportsAlignAttr(nodeToExamine))
909 : {
910 : // check for alignment
911 0 : nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(nodeToExamine);
912 0 : if (elem)
913 : {
914 0 : nsAutoString typeAttrVal;
915 0 : res = elem->GetAttribute(NS_LITERAL_STRING("align"), typeAttrVal);
916 0 : ToLowerCase(typeAttrVal);
917 0 : if (NS_SUCCEEDED(res) && typeAttrVal.Length())
918 : {
919 0 : if (typeAttrVal.EqualsLiteral("center"))
920 0 : *aAlign = nsIHTMLEditor::eCenter;
921 0 : else if (typeAttrVal.EqualsLiteral("right"))
922 0 : *aAlign = nsIHTMLEditor::eRight;
923 0 : else if (typeAttrVal.EqualsLiteral("justify"))
924 0 : *aAlign = nsIHTMLEditor::eJustify;
925 : else
926 0 : *aAlign = nsIHTMLEditor::eLeft;
927 0 : return res;
928 : }
929 : }
930 : }
931 0 : isFirstNodeToExamine = false;
932 0 : res = nodeToExamine->GetParentNode(getter_AddRefs(temp));
933 0 : if (NS_FAILED(res)) temp = nsnull;
934 0 : nodeToExamine = temp;
935 : }
936 0 : return NS_OK;
937 : }
938 :
939 0 : nsIAtom* MarginPropertyAtomForIndent(nsHTMLCSSUtils* aHTMLCSSUtils, nsIDOMNode* aNode) {
940 0 : nsAutoString direction;
941 0 : aHTMLCSSUtils->GetComputedProperty(aNode, nsEditProperty::cssDirection, direction);
942 0 : return direction.EqualsLiteral("rtl") ?
943 0 : nsEditProperty::cssMarginRight : nsEditProperty::cssMarginLeft;
944 : }
945 :
946 : nsresult
947 0 : nsHTMLEditRules::GetIndentState(bool *aCanIndent, bool *aCanOutdent)
948 : {
949 0 : NS_ENSURE_TRUE(aCanIndent && aCanOutdent, NS_ERROR_FAILURE);
950 0 : *aCanIndent = true;
951 0 : *aCanOutdent = false;
952 :
953 : // get selection
954 0 : nsCOMPtr<nsISelection>selection;
955 0 : nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
956 0 : NS_ENSURE_SUCCESS(res, res);
957 0 : nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
958 0 : NS_ENSURE_TRUE(selPriv, NS_ERROR_FAILURE);
959 :
960 : // contruct a list of nodes to act on.
961 0 : nsCOMArray<nsIDOMNode> arrayOfNodes;
962 0 : res = GetNodesFromSelection(selection, kIndent, arrayOfNodes, true);
963 0 : NS_ENSURE_SUCCESS(res, res);
964 :
965 : // examine nodes in selection for blockquotes or list elements;
966 : // these we can outdent. Note that we return true for canOutdent
967 : // if *any* of the selection is outdentable, rather than all of it.
968 0 : PRInt32 listCount = arrayOfNodes.Count();
969 : PRInt32 i;
970 0 : bool useCSS = mHTMLEditor->IsCSSEnabled();
971 0 : for (i=listCount-1; i>=0; i--)
972 : {
973 0 : nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
974 :
975 0 : if (nsHTMLEditUtils::IsNodeThatCanOutdent(curNode))
976 : {
977 0 : *aCanOutdent = true;
978 : break;
979 : }
980 0 : else if (useCSS) {
981 : // we are in CSS mode, indentation is done using the margin-left (or margin-right) property
982 0 : nsIAtom* marginProperty = MarginPropertyAtomForIndent(mHTMLEditor->mHTMLCSSUtils, curNode);
983 0 : nsAutoString value;
984 : // retrieve its specified value
985 0 : mHTMLEditor->mHTMLCSSUtils->GetSpecifiedProperty(curNode, marginProperty, value);
986 : float f;
987 0 : nsCOMPtr<nsIAtom> unit;
988 : // get its number part and its unit
989 0 : mHTMLEditor->mHTMLCSSUtils->ParseLength(value, &f, getter_AddRefs(unit));
990 : // if the number part is strictly positive, outdent is possible
991 0 : if (0 < f) {
992 0 : *aCanOutdent = true;
993 : break;
994 : }
995 : }
996 : }
997 :
998 0 : if (!*aCanOutdent)
999 : {
1000 : // if we haven't found something to outdent yet, also check the parents
1001 : // of selection endpoints. We might have a blockquote or list item
1002 : // in the parent hierarchy.
1003 :
1004 : // gather up info we need for test
1005 0 : nsCOMPtr<nsIDOMNode> parent, tmp, root = do_QueryInterface(mHTMLEditor->GetRoot());
1006 0 : NS_ENSURE_TRUE(root, NS_ERROR_NULL_POINTER);
1007 0 : nsCOMPtr<nsISelection> selection;
1008 : PRInt32 selOffset;
1009 0 : res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
1010 0 : NS_ENSURE_SUCCESS(res, res);
1011 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
1012 :
1013 : // test start parent hierarchy
1014 0 : res = mHTMLEditor->GetStartNodeAndOffset(selection, getter_AddRefs(parent), &selOffset);
1015 0 : NS_ENSURE_SUCCESS(res, res);
1016 0 : while (parent && (parent!=root))
1017 : {
1018 0 : if (nsHTMLEditUtils::IsNodeThatCanOutdent(parent))
1019 : {
1020 0 : *aCanOutdent = true;
1021 0 : break;
1022 : }
1023 0 : tmp=parent;
1024 0 : tmp->GetParentNode(getter_AddRefs(parent));
1025 : }
1026 :
1027 : // test end parent hierarchy
1028 0 : res = mHTMLEditor->GetEndNodeAndOffset(selection, getter_AddRefs(parent), &selOffset);
1029 0 : NS_ENSURE_SUCCESS(res, res);
1030 0 : while (parent && (parent!=root))
1031 : {
1032 0 : if (nsHTMLEditUtils::IsNodeThatCanOutdent(parent))
1033 : {
1034 0 : *aCanOutdent = true;
1035 0 : break;
1036 : }
1037 0 : tmp=parent;
1038 0 : tmp->GetParentNode(getter_AddRefs(parent));
1039 : }
1040 : }
1041 0 : return res;
1042 : }
1043 :
1044 :
1045 : nsresult
1046 0 : nsHTMLEditRules::GetParagraphState(bool *aMixed, nsAString &outFormat)
1047 : {
1048 : // This routine is *heavily* tied to our ui choices in the paragraph
1049 : // style popup. I can't see a way around that.
1050 0 : NS_ENSURE_TRUE(aMixed, NS_ERROR_NULL_POINTER);
1051 0 : *aMixed = true;
1052 0 : outFormat.Truncate(0);
1053 :
1054 0 : bool bMixed = false;
1055 : // using "x" as an uninitialized value, since "" is meaningful
1056 0 : nsAutoString formatStr(NS_LITERAL_STRING("x"));
1057 :
1058 0 : nsCOMArray<nsIDOMNode> arrayOfNodes;
1059 0 : nsresult res = GetParagraphFormatNodes(arrayOfNodes, true);
1060 0 : NS_ENSURE_SUCCESS(res, res);
1061 :
1062 : // post process list. We need to replace any block nodes that are not format
1063 : // nodes with their content. This is so we only have to look "up" the hierarchy
1064 : // to find format nodes, instead of both up and down.
1065 0 : PRInt32 listCount = arrayOfNodes.Count();
1066 : PRInt32 i;
1067 0 : for (i=listCount-1; i>=0; i--)
1068 : {
1069 0 : nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
1070 0 : nsAutoString format;
1071 : // if it is a known format node we have it easy
1072 0 : if (IsBlockNode(curNode) && !nsHTMLEditUtils::IsFormatNode(curNode))
1073 : {
1074 : // arrayOfNodes.RemoveObject(curNode);
1075 0 : res = AppendInnerFormatNodes(arrayOfNodes, curNode);
1076 0 : NS_ENSURE_SUCCESS(res, res);
1077 : }
1078 : }
1079 :
1080 : // we might have an empty node list. if so, find selection parent
1081 : // and put that on the list
1082 0 : listCount = arrayOfNodes.Count();
1083 0 : if (!listCount)
1084 : {
1085 0 : nsCOMPtr<nsIDOMNode> selNode;
1086 : PRInt32 selOffset;
1087 0 : nsCOMPtr<nsISelection>selection;
1088 0 : res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
1089 0 : NS_ENSURE_SUCCESS(res, res);
1090 0 : res = mHTMLEditor->GetStartNodeAndOffset(selection, getter_AddRefs(selNode), &selOffset);
1091 0 : NS_ENSURE_SUCCESS(res, res);
1092 0 : NS_ENSURE_TRUE(selNode, NS_ERROR_NULL_POINTER);
1093 0 : arrayOfNodes.AppendObject(selNode);
1094 0 : listCount = 1;
1095 : }
1096 :
1097 : // remember root node
1098 0 : nsCOMPtr<nsIDOMElement> rootElem = do_QueryInterface(mHTMLEditor->GetRoot());
1099 0 : NS_ENSURE_TRUE(rootElem, NS_ERROR_NULL_POINTER);
1100 :
1101 : // loop through the nodes in selection and examine their paragraph format
1102 0 : for (i=listCount-1; i>=0; i--)
1103 : {
1104 0 : nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
1105 0 : nsAutoString format;
1106 : // if it is a known format node we have it easy
1107 0 : if (nsHTMLEditUtils::IsFormatNode(curNode))
1108 0 : GetFormatString(curNode, format);
1109 0 : else if (IsBlockNode(curNode))
1110 : {
1111 : // this is a div or some other non-format block.
1112 : // we should ignore it. Its children were appended to this list
1113 : // by AppendInnerFormatNodes() call above. We will get needed
1114 : // info when we examine them instead.
1115 0 : continue;
1116 : }
1117 : else
1118 : {
1119 0 : nsCOMPtr<nsIDOMNode> node, tmp = curNode;
1120 0 : tmp->GetParentNode(getter_AddRefs(node));
1121 0 : while (node)
1122 : {
1123 0 : if (node == rootElem)
1124 : {
1125 0 : format.Truncate(0);
1126 0 : break;
1127 : }
1128 0 : else if (nsHTMLEditUtils::IsFormatNode(node))
1129 : {
1130 0 : GetFormatString(node, format);
1131 0 : break;
1132 : }
1133 : // else keep looking up
1134 0 : tmp = node;
1135 0 : tmp->GetParentNode(getter_AddRefs(node));
1136 : }
1137 : }
1138 :
1139 : // if this is the first node, we've found, remember it as the format
1140 0 : if (formatStr.EqualsLiteral("x"))
1141 0 : formatStr = format;
1142 : // else make sure it matches previously found format
1143 0 : else if (format != formatStr)
1144 : {
1145 0 : bMixed = true;
1146 : break;
1147 : }
1148 : }
1149 :
1150 0 : *aMixed = bMixed;
1151 0 : outFormat = formatStr;
1152 0 : return res;
1153 : }
1154 :
1155 : nsresult
1156 0 : nsHTMLEditRules::AppendInnerFormatNodes(nsCOMArray<nsIDOMNode>& aArray,
1157 : nsIDOMNode *aNode)
1158 : {
1159 0 : NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
1160 :
1161 0 : nsCOMPtr<nsIDOMNodeList> childList;
1162 0 : nsCOMPtr<nsIDOMNode> child;
1163 :
1164 0 : aNode->GetChildNodes(getter_AddRefs(childList));
1165 0 : NS_ENSURE_TRUE(childList, NS_OK);
1166 0 : PRUint32 len, j=0;
1167 0 : childList->GetLength(&len);
1168 :
1169 : // we only need to place any one inline inside this node onto
1170 : // the list. They are all the same for purposes of determining
1171 : // paragraph style. We use foundInline to track this as we are
1172 : // going through the children in the loop below.
1173 0 : bool foundInline = false;
1174 0 : while (j < len)
1175 : {
1176 0 : childList->Item(j, getter_AddRefs(child));
1177 0 : bool isBlock = IsBlockNode(child);
1178 0 : bool isFormat = nsHTMLEditUtils::IsFormatNode(child);
1179 0 : if (isBlock && !isFormat) // if it's a div, etc, recurse
1180 0 : AppendInnerFormatNodes(aArray, child);
1181 0 : else if (isFormat)
1182 : {
1183 0 : aArray.AppendObject(child);
1184 : }
1185 0 : else if (!foundInline) // if this is the first inline we've found, use it
1186 : {
1187 0 : foundInline = true;
1188 0 : aArray.AppendObject(child);
1189 : }
1190 0 : j++;
1191 : }
1192 0 : return NS_OK;
1193 : }
1194 :
1195 : nsresult
1196 0 : nsHTMLEditRules::GetFormatString(nsIDOMNode *aNode, nsAString &outFormat)
1197 : {
1198 0 : NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
1199 :
1200 0 : if (nsHTMLEditUtils::IsFormatNode(aNode))
1201 : {
1202 0 : nsCOMPtr<nsIAtom> atom = nsEditor::GetTag(aNode);
1203 0 : atom->ToString(outFormat);
1204 : }
1205 : else
1206 0 : outFormat.Truncate();
1207 :
1208 0 : return NS_OK;
1209 : }
1210 :
1211 : /********************************************************
1212 : * Protected rules methods
1213 : ********************************************************/
1214 :
1215 : nsresult
1216 0 : nsHTMLEditRules::WillInsert(nsISelection *aSelection, bool *aCancel)
1217 : {
1218 0 : nsresult res = nsTextEditRules::WillInsert(aSelection, aCancel);
1219 0 : NS_ENSURE_SUCCESS(res, res);
1220 :
1221 : // Adjust selection to prevent insertion after a moz-BR.
1222 : // this next only works for collapsed selections right now,
1223 : // because selection is a pain to work with when not collapsed.
1224 : // (no good way to extend start or end of selection), so we ignore
1225 : // those types of selections.
1226 : bool bCollapsed;
1227 0 : res = aSelection->GetIsCollapsed(&bCollapsed);
1228 0 : NS_ENSURE_SUCCESS(res, res);
1229 0 : if (!bCollapsed) {
1230 0 : return NS_OK;
1231 : }
1232 :
1233 : // if we are after a mozBR in the same block, then move selection
1234 : // to be before it
1235 0 : nsCOMPtr<nsIDOMNode> selNode, priorNode;
1236 : PRInt32 selOffset;
1237 : // get the (collapsed) selection location
1238 0 : res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode),
1239 0 : &selOffset);
1240 0 : NS_ENSURE_SUCCESS(res, res);
1241 : // get prior node
1242 : res = mHTMLEditor->GetPriorHTMLNode(selNode, selOffset,
1243 0 : address_of(priorNode));
1244 0 : if (NS_SUCCEEDED(res) && priorNode && nsTextEditUtils::IsMozBR(priorNode))
1245 : {
1246 0 : nsCOMPtr<nsIDOMNode> block1, block2;
1247 0 : if (IsBlockNode(selNode)) block1 = selNode;
1248 0 : else block1 = mHTMLEditor->GetBlockNodeParent(selNode);
1249 0 : block2 = mHTMLEditor->GetBlockNodeParent(priorNode);
1250 :
1251 0 : if (block1 == block2)
1252 : {
1253 : // if we are here then the selection is right after a mozBR
1254 : // that is in the same block as the selection. We need to move
1255 : // the selection start to be before the mozBR.
1256 0 : res = nsEditor::GetNodeLocation(priorNode, address_of(selNode), &selOffset);
1257 0 : NS_ENSURE_SUCCESS(res, res);
1258 0 : res = aSelection->Collapse(selNode,selOffset);
1259 0 : NS_ENSURE_SUCCESS(res, res);
1260 : }
1261 : }
1262 :
1263 : // we need to get the doc
1264 0 : nsCOMPtr<nsIDOMDocument>doc;
1265 0 : res = mHTMLEditor->GetDocument(getter_AddRefs(doc));
1266 0 : NS_ENSURE_SUCCESS(res, res);
1267 0 : NS_ENSURE_TRUE(doc, NS_ERROR_NULL_POINTER);
1268 :
1269 : // for every property that is set, insert a new inline style node
1270 0 : return CreateStyleForInsertText(aSelection, doc);
1271 : }
1272 :
1273 : #ifdef XXX_DEAD_CODE
1274 : nsresult
1275 : nsHTMLEditRules::DidInsert(nsISelection *aSelection, nsresult aResult)
1276 : {
1277 : return nsTextEditRules::DidInsert(aSelection, aResult);
1278 : }
1279 : #endif
1280 :
1281 : nsresult
1282 0 : nsHTMLEditRules::WillInsertText(PRInt32 aAction,
1283 : nsISelection *aSelection,
1284 : bool *aCancel,
1285 : bool *aHandled,
1286 : const nsAString *inString,
1287 : nsAString *outString,
1288 : PRInt32 aMaxLength)
1289 : {
1290 0 : if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
1291 :
1292 :
1293 :
1294 0 : if (inString->IsEmpty() && (aAction != kInsertTextIME))
1295 : {
1296 : // HACK: this is a fix for bug 19395
1297 : // I can't outlaw all empty insertions
1298 : // because IME transaction depend on them
1299 : // There is more work to do to make the
1300 : // world safe for IME.
1301 0 : *aCancel = true;
1302 0 : *aHandled = false;
1303 0 : return NS_OK;
1304 : }
1305 :
1306 : // initialize out param
1307 0 : *aCancel = false;
1308 0 : *aHandled = true;
1309 : nsresult res;
1310 0 : nsCOMPtr<nsIDOMNode> selNode;
1311 : PRInt32 selOffset;
1312 :
1313 : // if the selection isn't collapsed, delete it.
1314 : bool bCollapsed;
1315 0 : res = aSelection->GetIsCollapsed(&bCollapsed);
1316 0 : NS_ENSURE_SUCCESS(res, res);
1317 0 : if (!bCollapsed)
1318 : {
1319 0 : res = mHTMLEditor->DeleteSelection(nsIEditor::eNone);
1320 0 : NS_ENSURE_SUCCESS(res, res);
1321 : }
1322 :
1323 0 : res = WillInsert(aSelection, aCancel);
1324 0 : NS_ENSURE_SUCCESS(res, res);
1325 : // initialize out param
1326 : // we want to ignore result of WillInsert()
1327 0 : *aCancel = false;
1328 :
1329 : // get the (collapsed) selection location
1330 0 : res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
1331 0 : NS_ENSURE_SUCCESS(res, res);
1332 :
1333 : // dont put text in places that can't have it
1334 0 : if (!mHTMLEditor->IsTextNode(selNode) &&
1335 0 : !mHTMLEditor->CanContainTag(selNode, NS_LITERAL_STRING("#text")))
1336 0 : return NS_ERROR_FAILURE;
1337 :
1338 : // we need to get the doc
1339 0 : nsCOMPtr<nsIDOMDocument>doc;
1340 0 : res = mHTMLEditor->GetDocument(getter_AddRefs(doc));
1341 0 : NS_ENSURE_SUCCESS(res, res);
1342 0 : NS_ENSURE_TRUE(doc, NS_ERROR_NULL_POINTER);
1343 :
1344 0 : if (aAction == kInsertTextIME)
1345 : {
1346 : // Right now the nsWSRunObject code bails on empty strings, but IME needs
1347 : // the InsertTextImpl() call to still happen since empty strings are meaningful there.
1348 0 : if (inString->IsEmpty())
1349 : {
1350 0 : res = mHTMLEditor->InsertTextImpl(*inString, address_of(selNode), &selOffset, doc);
1351 : }
1352 : else
1353 : {
1354 0 : nsWSRunObject wsObj(mHTMLEditor, selNode, selOffset);
1355 0 : res = wsObj.InsertText(*inString, address_of(selNode), &selOffset, doc);
1356 : }
1357 0 : NS_ENSURE_SUCCESS(res, res);
1358 : }
1359 : else // aAction == kInsertText
1360 : {
1361 : // find where we are
1362 0 : nsCOMPtr<nsIDOMNode> curNode = selNode;
1363 0 : PRInt32 curOffset = selOffset;
1364 :
1365 : // is our text going to be PREformatted?
1366 : // We remember this so that we know how to handle tabs.
1367 : bool isPRE;
1368 0 : res = mHTMLEditor->IsPreformatted(selNode, &isPRE);
1369 0 : NS_ENSURE_SUCCESS(res, res);
1370 :
1371 : // turn off the edit listener: we know how to
1372 : // build the "doc changed range" ourselves, and it's
1373 : // must faster to do it once here than to track all
1374 : // the changes one at a time.
1375 0 : nsAutoLockListener lockit(&mListenerEnabled);
1376 :
1377 : // don't spaz my selection in subtransactions
1378 0 : nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
1379 0 : nsAutoString tString(*inString);
1380 0 : const PRUnichar *unicodeBuf = tString.get();
1381 0 : nsCOMPtr<nsIDOMNode> unused;
1382 0 : PRInt32 pos = 0;
1383 0 : NS_NAMED_LITERAL_STRING(newlineStr, LFSTR);
1384 :
1385 : // for efficiency, break out the pre case separately. This is because
1386 : // its a lot cheaper to search the input string for only newlines than
1387 : // it is to search for both tabs and newlines.
1388 0 : if (isPRE || IsPlaintextEditor())
1389 : {
1390 0 : while (unicodeBuf && (pos != -1) && (pos < (PRInt32)(*inString).Length()))
1391 : {
1392 0 : PRInt32 oldPos = pos;
1393 : PRInt32 subStrLen;
1394 0 : pos = tString.FindChar(nsCRT::LF, oldPos);
1395 :
1396 0 : if (pos != -1)
1397 : {
1398 0 : subStrLen = pos - oldPos;
1399 : // if first char is newline, then use just it
1400 0 : if (subStrLen == 0)
1401 0 : subStrLen = 1;
1402 : }
1403 : else
1404 : {
1405 0 : subStrLen = tString.Length() - oldPos;
1406 0 : pos = tString.Length();
1407 : }
1408 :
1409 0 : nsDependentSubstring subStr(tString, oldPos, subStrLen);
1410 :
1411 : // is it a return?
1412 0 : if (subStr.Equals(newlineStr))
1413 : {
1414 0 : res = mHTMLEditor->CreateBRImpl(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone);
1415 0 : pos++;
1416 : }
1417 : else
1418 : {
1419 0 : res = mHTMLEditor->InsertTextImpl(subStr, address_of(curNode), &curOffset, doc);
1420 : }
1421 0 : NS_ENSURE_SUCCESS(res, res);
1422 : }
1423 : }
1424 : else
1425 : {
1426 0 : NS_NAMED_LITERAL_STRING(tabStr, "\t");
1427 0 : NS_NAMED_LITERAL_STRING(spacesStr, " ");
1428 0 : char specialChars[] = {TAB, nsCRT::LF, 0};
1429 0 : while (unicodeBuf && (pos != -1) && (pos < (PRInt32)inString->Length()))
1430 : {
1431 0 : PRInt32 oldPos = pos;
1432 : PRInt32 subStrLen;
1433 0 : pos = tString.FindCharInSet(specialChars, oldPos);
1434 :
1435 0 : if (pos != -1)
1436 : {
1437 0 : subStrLen = pos - oldPos;
1438 : // if first char is newline, then use just it
1439 0 : if (subStrLen == 0)
1440 0 : subStrLen = 1;
1441 : }
1442 : else
1443 : {
1444 0 : subStrLen = tString.Length() - oldPos;
1445 0 : pos = tString.Length();
1446 : }
1447 :
1448 0 : nsDependentSubstring subStr(tString, oldPos, subStrLen);
1449 0 : nsWSRunObject wsObj(mHTMLEditor, curNode, curOffset);
1450 :
1451 : // is it a tab?
1452 0 : if (subStr.Equals(tabStr))
1453 : {
1454 0 : res = wsObj.InsertText(spacesStr, address_of(curNode), &curOffset, doc);
1455 0 : NS_ENSURE_SUCCESS(res, res);
1456 0 : pos++;
1457 : }
1458 : // is it a return?
1459 0 : else if (subStr.Equals(newlineStr))
1460 : {
1461 0 : res = wsObj.InsertBreak(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone);
1462 0 : NS_ENSURE_SUCCESS(res, res);
1463 0 : pos++;
1464 : }
1465 : else
1466 : {
1467 0 : res = wsObj.InsertText(subStr, address_of(curNode), &curOffset, doc);
1468 0 : NS_ENSURE_SUCCESS(res, res);
1469 : }
1470 0 : NS_ENSURE_SUCCESS(res, res);
1471 : }
1472 : }
1473 0 : nsCOMPtr<nsISelection> selection(aSelection);
1474 0 : nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
1475 0 : selPriv->SetInterlinePosition(false);
1476 0 : if (curNode) aSelection->Collapse(curNode, curOffset);
1477 : // manually update the doc changed range so that AfterEdit will clean up
1478 : // the correct portion of the document.
1479 0 : if (!mDocChangeRange)
1480 : {
1481 0 : mDocChangeRange = new nsRange();
1482 : }
1483 0 : res = mDocChangeRange->SetStart(selNode, selOffset);
1484 0 : NS_ENSURE_SUCCESS(res, res);
1485 0 : if (curNode)
1486 0 : res = mDocChangeRange->SetEnd(curNode, curOffset);
1487 : else
1488 0 : res = mDocChangeRange->SetEnd(selNode, selOffset);
1489 0 : NS_ENSURE_SUCCESS(res, res);
1490 : }
1491 0 : return res;
1492 : }
1493 :
1494 : nsresult
1495 0 : nsHTMLEditRules::WillLoadHTML(nsISelection *aSelection, bool *aCancel)
1496 : {
1497 0 : NS_ENSURE_TRUE(aSelection && aCancel, NS_ERROR_NULL_POINTER);
1498 :
1499 0 : *aCancel = false;
1500 :
1501 : // Delete mBogusNode if it exists. If we really need one,
1502 : // it will be added during post-processing in AfterEditInner().
1503 :
1504 0 : if (mBogusNode)
1505 : {
1506 0 : mEditor->DeleteNode(mBogusNode);
1507 0 : mBogusNode = nsnull;
1508 : }
1509 :
1510 0 : return NS_OK;
1511 : }
1512 :
1513 : nsresult
1514 0 : nsHTMLEditRules::WillInsertBreak(nsISelection *aSelection, bool *aCancel, bool *aHandled)
1515 : {
1516 0 : if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
1517 : // initialize out param
1518 0 : *aCancel = false;
1519 0 : *aHandled = false;
1520 :
1521 : // if the selection isn't collapsed, delete it.
1522 : bool bCollapsed;
1523 0 : nsresult res = aSelection->GetIsCollapsed(&bCollapsed);
1524 0 : NS_ENSURE_SUCCESS(res, res);
1525 0 : if (!bCollapsed)
1526 : {
1527 0 : res = mHTMLEditor->DeleteSelection(nsIEditor::eNone);
1528 0 : NS_ENSURE_SUCCESS(res, res);
1529 : }
1530 :
1531 0 : res = WillInsert(aSelection, aCancel);
1532 0 : NS_ENSURE_SUCCESS(res, res);
1533 :
1534 : // initialize out param
1535 : // we want to ignore result of WillInsert()
1536 0 : *aCancel = false;
1537 :
1538 : // split any mailcites in the way.
1539 : // should we abort this if we encounter table cell boundaries?
1540 0 : if (IsMailEditor())
1541 : {
1542 0 : res = SplitMailCites(aSelection, IsPlaintextEditor(), aHandled);
1543 0 : NS_ENSURE_SUCCESS(res, res);
1544 0 : if (*aHandled) return NS_OK;
1545 : }
1546 :
1547 : // smart splitting rules
1548 0 : nsCOMPtr<nsIDOMNode> node;
1549 : PRInt32 offset;
1550 :
1551 0 : res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(node), &offset);
1552 0 : NS_ENSURE_SUCCESS(res, res);
1553 0 : NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
1554 :
1555 : // do nothing if the node is read-only
1556 0 : if (!mHTMLEditor->IsModifiableNode(node))
1557 : {
1558 0 : *aCancel = true;
1559 0 : return NS_OK;
1560 : }
1561 :
1562 : // identify the block
1563 0 : nsCOMPtr<nsIDOMNode> blockParent;
1564 0 : if (IsBlockNode(node))
1565 0 : blockParent = node;
1566 : else
1567 0 : blockParent = mHTMLEditor->GetBlockNodeParent(node);
1568 0 : NS_ENSURE_TRUE(blockParent, NS_ERROR_FAILURE);
1569 :
1570 : // if the active editing host is an inline element,
1571 : // or if the active editing host is the block parent itself,
1572 : // just append a br.
1573 0 : nsCOMPtr<nsIContent> hostContent = mHTMLEditor->GetActiveEditingHost();
1574 0 : nsCOMPtr<nsIDOMNode> hostNode = do_QueryInterface(hostContent);
1575 0 : if (!nsEditorUtils::IsDescendantOf(blockParent, hostNode))
1576 : {
1577 0 : res = StandardBreakImpl(node, offset, aSelection);
1578 0 : NS_ENSURE_SUCCESS(res, res);
1579 0 : *aHandled = true;
1580 0 : return NS_OK;
1581 : }
1582 :
1583 : // if block is empty, populate with br.
1584 : // (for example, imagine a div that contains the word "text". the user selects
1585 : // "text" and types return. "text" is deleted leaving an empty block. we want
1586 : // to put in one br to make block have a line. then code further below will put
1587 : // in a second br.)
1588 : bool isEmpty;
1589 0 : res = IsEmptyBlock(blockParent, &isEmpty);
1590 0 : if (isEmpty)
1591 : {
1592 : PRUint32 blockLen;
1593 0 : res = mHTMLEditor->GetLengthOfDOMNode(blockParent, blockLen);
1594 0 : NS_ENSURE_SUCCESS(res, res);
1595 0 : nsCOMPtr<nsIDOMNode> brNode;
1596 0 : res = mHTMLEditor->CreateBR(blockParent, blockLen, address_of(brNode));
1597 0 : NS_ENSURE_SUCCESS(res, res);
1598 : }
1599 :
1600 0 : nsCOMPtr<nsIDOMNode> listItem = IsInListItem(blockParent);
1601 0 : if (listItem && listItem != hostNode)
1602 : {
1603 0 : res = ReturnInListItem(aSelection, listItem, node, offset);
1604 0 : *aHandled = true;
1605 0 : return NS_OK;
1606 : }
1607 :
1608 : // headers: close (or split) header
1609 0 : else if (nsHTMLEditUtils::IsHeader(blockParent))
1610 : {
1611 0 : res = ReturnInHeader(aSelection, blockParent, node, offset);
1612 0 : *aHandled = true;
1613 0 : return NS_OK;
1614 : }
1615 :
1616 : // paragraphs: special rules to look for <br>s
1617 0 : else if (nsHTMLEditUtils::IsParagraph(blockParent))
1618 : {
1619 0 : res = ReturnInParagraph(aSelection, blockParent, node, offset, aCancel, aHandled);
1620 0 : NS_ENSURE_SUCCESS(res, res);
1621 : // fall through, we may not have handled it in ReturnInParagraph()
1622 : }
1623 :
1624 : // if not already handled then do the standard thing
1625 0 : if (!(*aHandled))
1626 : {
1627 0 : res = StandardBreakImpl(node, offset, aSelection);
1628 0 : *aHandled = true;
1629 : }
1630 0 : return res;
1631 : }
1632 :
1633 : nsresult
1634 0 : nsHTMLEditRules::StandardBreakImpl(nsIDOMNode *aNode, PRInt32 aOffset, nsISelection *aSelection)
1635 : {
1636 0 : nsCOMPtr<nsIDOMNode> brNode;
1637 0 : bool bAfterBlock = false;
1638 0 : bool bBeforeBlock = false;
1639 0 : nsresult res = NS_OK;
1640 0 : nsCOMPtr<nsIDOMNode> node(aNode);
1641 0 : nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(aSelection));
1642 :
1643 0 : if (IsPlaintextEditor())
1644 : {
1645 0 : res = mHTMLEditor->CreateBR(node, aOffset, address_of(brNode));
1646 : }
1647 : else
1648 : {
1649 0 : nsWSRunObject wsObj(mHTMLEditor, node, aOffset);
1650 0 : nsCOMPtr<nsIDOMNode> visNode, linkNode;
1651 0 : PRInt32 visOffset=0, newOffset;
1652 : PRInt16 wsType;
1653 0 : res = wsObj.PriorVisibleNode(node, aOffset, address_of(visNode), &visOffset, &wsType);
1654 0 : NS_ENSURE_SUCCESS(res, res);
1655 0 : if (wsType & nsWSRunObject::eBlock)
1656 0 : bAfterBlock = true;
1657 0 : res = wsObj.NextVisibleNode(node, aOffset, address_of(visNode), &visOffset, &wsType);
1658 0 : NS_ENSURE_SUCCESS(res, res);
1659 0 : if (wsType & nsWSRunObject::eBlock)
1660 0 : bBeforeBlock = true;
1661 0 : if (mHTMLEditor->IsInLink(node, address_of(linkNode)))
1662 : {
1663 : // split the link
1664 0 : nsCOMPtr<nsIDOMNode> linkParent;
1665 0 : res = linkNode->GetParentNode(getter_AddRefs(linkParent));
1666 0 : NS_ENSURE_SUCCESS(res, res);
1667 0 : res = mHTMLEditor->SplitNodeDeep(linkNode, node, aOffset, &newOffset, true);
1668 0 : NS_ENSURE_SUCCESS(res, res);
1669 : // reset {node,aOffset} to the point where link was split
1670 0 : node = linkParent;
1671 0 : aOffset = newOffset;
1672 : }
1673 0 : res = wsObj.InsertBreak(address_of(node), &aOffset, address_of(brNode), nsIEditor::eNone);
1674 : }
1675 0 : NS_ENSURE_SUCCESS(res, res);
1676 0 : res = nsEditor::GetNodeLocation(brNode, address_of(node), &aOffset);
1677 0 : NS_ENSURE_SUCCESS(res, res);
1678 0 : if (bAfterBlock && bBeforeBlock)
1679 : {
1680 : // we just placed a br between block boundaries.
1681 : // This is the one case where we want the selection to be before
1682 : // the br we just placed, as the br will be on a new line,
1683 : // rather than at end of prior line.
1684 0 : selPriv->SetInterlinePosition(true);
1685 0 : res = aSelection->Collapse(node, aOffset);
1686 : }
1687 : else
1688 : {
1689 0 : nsWSRunObject wsObj(mHTMLEditor, node, aOffset+1);
1690 0 : nsCOMPtr<nsIDOMNode> secondBR;
1691 0 : PRInt32 visOffset=0;
1692 : PRInt16 wsType;
1693 0 : res = wsObj.NextVisibleNode(node, aOffset+1, address_of(secondBR), &visOffset, &wsType);
1694 0 : NS_ENSURE_SUCCESS(res, res);
1695 0 : if (wsType==nsWSRunObject::eBreak)
1696 : {
1697 : // the next thing after the break we inserted is another break. Move the 2nd
1698 : // break to be the first breaks sibling. This will prevent them from being
1699 : // in different inline nodes, which would break SetInterlinePosition(). It will
1700 : // also assure that if the user clicks away and then clicks back on their new
1701 : // blank line, they will still get the style from the line above.
1702 0 : nsCOMPtr<nsIDOMNode> brParent;
1703 : PRInt32 brOffset;
1704 0 : res = nsEditor::GetNodeLocation(secondBR, address_of(brParent), &brOffset);
1705 0 : NS_ENSURE_SUCCESS(res, res);
1706 0 : if ((brParent != node) || (brOffset != (aOffset+1)))
1707 : {
1708 0 : res = mHTMLEditor->MoveNode(secondBR, node, aOffset+1);
1709 0 : NS_ENSURE_SUCCESS(res, res);
1710 : }
1711 : }
1712 : // SetInterlinePosition(true) means we want the caret to stick to the content on the "right".
1713 : // We want the caret to stick to whatever is past the break. This is
1714 : // because the break is on the same line we were on, but the next content
1715 : // will be on the following line.
1716 :
1717 : // An exception to this is if the break has a next sibling that is a block node.
1718 : // Then we stick to the left to avoid an uber caret.
1719 0 : nsCOMPtr<nsIDOMNode> siblingNode;
1720 0 : brNode->GetNextSibling(getter_AddRefs(siblingNode));
1721 0 : if (siblingNode && IsBlockNode(siblingNode))
1722 0 : selPriv->SetInterlinePosition(false);
1723 : else
1724 0 : selPriv->SetInterlinePosition(true);
1725 0 : res = aSelection->Collapse(node, aOffset+1);
1726 : }
1727 0 : return res;
1728 : }
1729 :
1730 : nsresult
1731 0 : nsHTMLEditRules::DidInsertBreak(nsISelection *aSelection, nsresult aResult)
1732 : {
1733 0 : return NS_OK;
1734 : }
1735 :
1736 :
1737 : nsresult
1738 0 : nsHTMLEditRules::SplitMailCites(nsISelection *aSelection, bool aPlaintext, bool *aHandled)
1739 : {
1740 0 : NS_ENSURE_TRUE(aSelection && aHandled, NS_ERROR_NULL_POINTER);
1741 0 : nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(aSelection));
1742 0 : nsCOMPtr<nsIDOMNode> citeNode, selNode, leftCite, rightCite;
1743 : PRInt32 selOffset, newOffset;
1744 0 : nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
1745 0 : NS_ENSURE_SUCCESS(res, res);
1746 0 : res = GetTopEnclosingMailCite(selNode, address_of(citeNode), aPlaintext);
1747 0 : NS_ENSURE_SUCCESS(res, res);
1748 0 : if (citeNode)
1749 : {
1750 : // If our selection is just before a break, nudge it to be
1751 : // just after it. This does two things for us. It saves us the trouble of having to add
1752 : // a break here ourselves to preserve the "blockness" of the inline span mailquote
1753 : // (in the inline case), and :
1754 : // it means the break won't end up making an empty line that happens to be inside a
1755 : // mailquote (in either inline or block case).
1756 : // The latter can confuse a user if they click there and start typing,
1757 : // because being in the mailquote may affect wrapping behavior, or font color, etc.
1758 0 : nsWSRunObject wsObj(mHTMLEditor, selNode, selOffset);
1759 0 : nsCOMPtr<nsIDOMNode> visNode;
1760 0 : PRInt32 visOffset=0;
1761 : PRInt16 wsType;
1762 0 : res = wsObj.NextVisibleNode(selNode, selOffset, address_of(visNode), &visOffset, &wsType);
1763 0 : NS_ENSURE_SUCCESS(res, res);
1764 0 : if (wsType==nsWSRunObject::eBreak)
1765 : {
1766 : // ok, we are just before a break. is it inside the mailquote?
1767 : PRInt32 unused;
1768 0 : if (nsEditorUtils::IsDescendantOf(visNode, citeNode, &unused))
1769 : {
1770 : // it is. so lets reset our selection to be just after it.
1771 0 : res = mHTMLEditor->GetNodeLocation(visNode, address_of(selNode), &selOffset);
1772 0 : NS_ENSURE_SUCCESS(res, res);
1773 0 : ++selOffset;
1774 : }
1775 : }
1776 :
1777 0 : nsCOMPtr<nsIDOMNode> brNode;
1778 : res = mHTMLEditor->SplitNodeDeep(citeNode, selNode, selOffset, &newOffset,
1779 0 : true, address_of(leftCite), address_of(rightCite));
1780 0 : NS_ENSURE_SUCCESS(res, res);
1781 0 : res = citeNode->GetParentNode(getter_AddRefs(selNode));
1782 0 : NS_ENSURE_SUCCESS(res, res);
1783 0 : res = mHTMLEditor->CreateBR(selNode, newOffset, address_of(brNode));
1784 0 : NS_ENSURE_SUCCESS(res, res);
1785 : // want selection before the break, and on same line
1786 0 : selPriv->SetInterlinePosition(true);
1787 0 : res = aSelection->Collapse(selNode, newOffset);
1788 0 : NS_ENSURE_SUCCESS(res, res);
1789 : // if citeNode wasn't a block, we might also want another break before it.
1790 : // We need to examine the content both before the br we just added and also
1791 : // just after it. If we don't have another br or block boundary adjacent,
1792 : // then we will need a 2nd br added to achieve blank line that user expects.
1793 0 : if (IsInlineNode(citeNode))
1794 : {
1795 0 : nsWSRunObject wsObj(mHTMLEditor, selNode, newOffset);
1796 0 : nsCOMPtr<nsIDOMNode> visNode;
1797 0 : PRInt32 visOffset=0;
1798 : PRInt16 wsType;
1799 0 : res = wsObj.PriorVisibleNode(selNode, newOffset, address_of(visNode), &visOffset, &wsType);
1800 0 : NS_ENSURE_SUCCESS(res, res);
1801 0 : if ((wsType==nsWSRunObject::eNormalWS) ||
1802 : (wsType==nsWSRunObject::eText) ||
1803 : (wsType==nsWSRunObject::eSpecial))
1804 : {
1805 0 : nsWSRunObject wsObjAfterBR(mHTMLEditor, selNode, newOffset+1);
1806 0 : res = wsObjAfterBR.NextVisibleNode(selNode, newOffset+1, address_of(visNode), &visOffset, &wsType);
1807 0 : NS_ENSURE_SUCCESS(res, res);
1808 0 : if ((wsType==nsWSRunObject::eNormalWS) ||
1809 : (wsType==nsWSRunObject::eText) ||
1810 : (wsType==nsWSRunObject::eSpecial))
1811 : {
1812 0 : res = mHTMLEditor->CreateBR(selNode, newOffset, address_of(brNode));
1813 0 : NS_ENSURE_SUCCESS(res, res);
1814 : }
1815 : }
1816 : }
1817 : // delete any empty cites
1818 0 : bool bEmptyCite = false;
1819 0 : if (leftCite)
1820 : {
1821 0 : res = mHTMLEditor->IsEmptyNode(leftCite, &bEmptyCite, true, false);
1822 0 : if (NS_SUCCEEDED(res) && bEmptyCite)
1823 0 : res = mHTMLEditor->DeleteNode(leftCite);
1824 0 : NS_ENSURE_SUCCESS(res, res);
1825 : }
1826 0 : if (rightCite)
1827 : {
1828 0 : res = mHTMLEditor->IsEmptyNode(rightCite, &bEmptyCite, true, false);
1829 0 : if (NS_SUCCEEDED(res) && bEmptyCite)
1830 0 : res = mHTMLEditor->DeleteNode(rightCite);
1831 0 : NS_ENSURE_SUCCESS(res, res);
1832 : }
1833 0 : *aHandled = true;
1834 : }
1835 0 : return NS_OK;
1836 : }
1837 :
1838 :
1839 : nsresult
1840 0 : nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
1841 : nsIEditor::EDirection aAction,
1842 : bool *aCancel,
1843 : bool *aHandled)
1844 : {
1845 :
1846 0 : if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
1847 : // initialize out param
1848 0 : *aCancel = false;
1849 0 : *aHandled = false;
1850 :
1851 : // remember that we did a selection deletion. Used by CreateStyleForInsertText()
1852 0 : mDidDeleteSelection = true;
1853 :
1854 : // if there is only bogus content, cancel the operation
1855 0 : if (mBogusNode)
1856 : {
1857 0 : *aCancel = true;
1858 0 : return NS_OK;
1859 : }
1860 :
1861 0 : nsresult res = NS_OK;
1862 0 : bool bCollapsed, join = false;
1863 0 : res = aSelection->GetIsCollapsed(&bCollapsed);
1864 0 : NS_ENSURE_SUCCESS(res, res);
1865 :
1866 : // origCollapsed is used later to determine whether we should join
1867 : // blocks. We don't really care about bCollapsed because it will be
1868 : // modified by ExtendSelectionForDelete later. JoinBlocks should
1869 : // happen if the original selection is collapsed and the cursor is
1870 : // at the end of a block element, in which case ExtendSelectionForDelete
1871 : // would always make the selection not collapsed.
1872 0 : bool origCollapsed = bCollapsed;
1873 0 : nsCOMPtr<nsIDOMNode> startNode, selNode;
1874 : PRInt32 startOffset, selOffset;
1875 :
1876 : // first check for table selection mode. If so,
1877 : // hand off to table editor.
1878 : {
1879 0 : nsCOMPtr<nsIDOMElement> cell;
1880 0 : res = mHTMLEditor->GetFirstSelectedCell(nsnull, getter_AddRefs(cell));
1881 0 : if (NS_SUCCEEDED(res) && cell)
1882 : {
1883 0 : res = mHTMLEditor->DeleteTableCellContents();
1884 0 : *aHandled = true;
1885 0 : return res;
1886 : }
1887 : }
1888 :
1889 0 : res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(startNode), &startOffset);
1890 0 : NS_ENSURE_SUCCESS(res, res);
1891 0 : NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
1892 :
1893 0 : if (bCollapsed)
1894 : {
1895 : // if we are inside an empty block, delete it.
1896 0 : nsCOMPtr<nsIContent> hostContent = mHTMLEditor->GetActiveEditingHost();
1897 0 : nsCOMPtr<nsIDOMNode> hostNode = do_QueryInterface(hostContent);
1898 0 : NS_ENSURE_TRUE(hostNode, NS_ERROR_FAILURE);
1899 0 : res = CheckForEmptyBlock(startNode, hostNode, aSelection, aHandled);
1900 0 : NS_ENSURE_SUCCESS(res, res);
1901 0 : if (*aHandled) return NS_OK;
1902 :
1903 : // Test for distance between caret and text that will be deleted
1904 0 : res = CheckBidiLevelForDeletion(aSelection, startNode, startOffset, aAction, aCancel);
1905 0 : NS_ENSURE_SUCCESS(res, res);
1906 0 : if (*aCancel) return NS_OK;
1907 :
1908 0 : res = mHTMLEditor->ExtendSelectionForDelete(aSelection, &aAction);
1909 0 : NS_ENSURE_SUCCESS(res, res);
1910 :
1911 : // We should delete nothing.
1912 0 : if (aAction == nsIEditor::eNone)
1913 0 : return NS_OK;
1914 :
1915 : // ExtendSelectionForDelete() may have changed the selection, update it
1916 0 : res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(startNode), &startOffset);
1917 0 : NS_ENSURE_SUCCESS(res, res);
1918 0 : NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
1919 :
1920 0 : res = aSelection->GetIsCollapsed(&bCollapsed);
1921 0 : NS_ENSURE_SUCCESS(res, res);
1922 : }
1923 :
1924 0 : if (bCollapsed)
1925 : {
1926 : // what's in the direction we are deleting?
1927 0 : nsWSRunObject wsObj(mHTMLEditor, startNode, startOffset);
1928 0 : nsCOMPtr<nsIDOMNode> visNode;
1929 : PRInt32 visOffset;
1930 : PRInt16 wsType;
1931 :
1932 : // find next visible node
1933 0 : if (aAction == nsIEditor::eNext)
1934 0 : res = wsObj.NextVisibleNode(startNode, startOffset, address_of(visNode), &visOffset, &wsType);
1935 : else
1936 0 : res = wsObj.PriorVisibleNode(startNode, startOffset, address_of(visNode), &visOffset, &wsType);
1937 0 : NS_ENSURE_SUCCESS(res, res);
1938 :
1939 0 : if (!visNode) // can't find anything to delete!
1940 : {
1941 0 : *aCancel = true;
1942 0 : return res;
1943 : }
1944 :
1945 0 : if (wsType==nsWSRunObject::eNormalWS)
1946 : {
1947 : // we found some visible ws to delete. Let ws code handle it.
1948 0 : if (aAction == nsIEditor::eNext)
1949 0 : res = wsObj.DeleteWSForward();
1950 : else
1951 0 : res = wsObj.DeleteWSBackward();
1952 0 : *aHandled = true;
1953 0 : NS_ENSURE_SUCCESS(res, res);
1954 0 : res = InsertBRIfNeeded(aSelection);
1955 0 : return res;
1956 : }
1957 0 : else if (wsType==nsWSRunObject::eText)
1958 : {
1959 : // found normal text to delete.
1960 0 : PRInt32 so = visOffset;
1961 0 : PRInt32 eo = visOffset+1;
1962 0 : if (aAction == nsIEditor::ePrevious)
1963 : {
1964 0 : if (so == 0) return NS_ERROR_UNEXPECTED;
1965 0 : so--;
1966 0 : eo--;
1967 : }
1968 : else
1969 : {
1970 0 : nsCOMPtr<nsIDOMRange> range;
1971 0 : res = aSelection->GetRangeAt(0, getter_AddRefs(range));
1972 0 : NS_ENSURE_SUCCESS(res, res);
1973 :
1974 : #ifdef DEBUG
1975 : nsIDOMNode *container;
1976 :
1977 0 : res = range->GetStartContainer(&container);
1978 0 : NS_ENSURE_SUCCESS(res, res);
1979 0 : NS_ASSERTION(container == visNode, "selection start not in visNode");
1980 :
1981 0 : res = range->GetEndContainer(&container);
1982 0 : NS_ENSURE_SUCCESS(res, res);
1983 0 : NS_ASSERTION(container == visNode, "selection end not in visNode");
1984 : #endif
1985 :
1986 0 : res = range->GetStartOffset(&so);
1987 0 : NS_ENSURE_SUCCESS(res, res);
1988 0 : res = range->GetEndOffset(&eo);
1989 0 : NS_ENSURE_SUCCESS(res, res);
1990 : }
1991 0 : res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor, address_of(visNode), &so, address_of(visNode), &eo);
1992 0 : NS_ENSURE_SUCCESS(res, res);
1993 0 : nsCOMPtr<nsIDOMCharacterData> nodeAsText(do_QueryInterface(visNode));
1994 0 : res = mHTMLEditor->DeleteText(nodeAsText, NS_MIN(so, eo), NS_ABS(eo - so));
1995 0 : *aHandled = true;
1996 0 : NS_ENSURE_SUCCESS(res, res);
1997 0 : res = InsertBRIfNeeded(aSelection);
1998 0 : return res;
1999 : }
2000 0 : else if ( (wsType==nsWSRunObject::eSpecial) ||
2001 : (wsType==nsWSRunObject::eBreak) ||
2002 0 : nsHTMLEditUtils::IsHR(visNode) )
2003 : {
2004 : // short circuit for invisible breaks. delete them and recurse.
2005 0 : if (nsTextEditUtils::IsBreak(visNode) && !mHTMLEditor->IsVisBreak(visNode))
2006 : {
2007 0 : res = mHTMLEditor->DeleteNode(visNode);
2008 0 : NS_ENSURE_SUCCESS(res, res);
2009 0 : return WillDeleteSelection(aSelection, aAction, aCancel, aHandled);
2010 : }
2011 :
2012 : // special handling for backspace when positioned after <hr>
2013 0 : if (aAction == nsIEditor::ePrevious && nsHTMLEditUtils::IsHR(visNode))
2014 : {
2015 : /*
2016 : Only if the caret is positioned at the end-of-hr-line position,
2017 : we want to delete the <hr>.
2018 :
2019 : In other words, we only want to delete, if
2020 : our selection position (indicated by startNode and startOffset)
2021 : is the position directly after the <hr>,
2022 : on the same line as the <hr>.
2023 :
2024 : To detect this case we check:
2025 : startNode == parentOfVisNode
2026 : and
2027 : startOffset -1 == visNodeOffsetToVisNodeParent
2028 : and
2029 : interline position is false (left)
2030 :
2031 : In any other case we set the position to
2032 : startnode -1 and interlineposition to false,
2033 : only moving the caret to the end-of-hr-line position.
2034 : */
2035 :
2036 0 : bool moveOnly = true;
2037 :
2038 0 : res = nsEditor::GetNodeLocation(visNode, address_of(selNode), &selOffset);
2039 0 : NS_ENSURE_SUCCESS(res, res);
2040 :
2041 : bool interLineIsRight;
2042 0 : nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(aSelection));
2043 0 : res = selPriv->GetInterlinePosition(&interLineIsRight);
2044 0 : NS_ENSURE_SUCCESS(res, res);
2045 :
2046 0 : if (startNode == selNode &&
2047 : startOffset -1 == selOffset &&
2048 0 : !interLineIsRight)
2049 : {
2050 0 : moveOnly = false;
2051 : }
2052 :
2053 0 : if (moveOnly)
2054 : {
2055 : // Go to the position after the <hr>, but to the end of the <hr> line
2056 : // by setting the interline position to left.
2057 0 : ++selOffset;
2058 0 : res = aSelection->Collapse(selNode, selOffset);
2059 0 : selPriv->SetInterlinePosition(false);
2060 0 : mDidExplicitlySetInterline = true;
2061 0 : *aHandled = true;
2062 :
2063 : // There is one exception to the move only case.
2064 : // If the <hr> is followed by a <br> we want to delete the <br>.
2065 :
2066 : PRInt16 otherWSType;
2067 0 : nsCOMPtr<nsIDOMNode> otherNode;
2068 : PRInt32 otherOffset;
2069 :
2070 0 : res = wsObj.NextVisibleNode(startNode, startOffset, address_of(otherNode), &otherOffset, &otherWSType);
2071 0 : NS_ENSURE_SUCCESS(res, res);
2072 :
2073 0 : if (otherWSType == nsWSRunObject::eBreak)
2074 : {
2075 : // Delete the <br>
2076 :
2077 0 : res = nsWSRunObject::PrepareToDeleteNode(mHTMLEditor, otherNode);
2078 0 : NS_ENSURE_SUCCESS(res, res);
2079 0 : res = mHTMLEditor->DeleteNode(otherNode);
2080 0 : NS_ENSURE_SUCCESS(res, res);
2081 : }
2082 :
2083 0 : return NS_OK;
2084 : }
2085 : // else continue with normal delete code
2086 : }
2087 :
2088 : // found break or image, or hr.
2089 0 : res = nsWSRunObject::PrepareToDeleteNode(mHTMLEditor, visNode);
2090 0 : NS_ENSURE_SUCCESS(res, res);
2091 : // remember sibling to visnode, if any
2092 0 : nsCOMPtr<nsIDOMNode> sibling, stepbrother;
2093 0 : mHTMLEditor->GetPriorHTMLSibling(visNode, address_of(sibling));
2094 : // delete the node, and join like nodes if appropriate
2095 0 : res = mHTMLEditor->DeleteNode(visNode);
2096 0 : NS_ENSURE_SUCCESS(res, res);
2097 : // we did something, so lets say so.
2098 0 : *aHandled = true;
2099 : // is there a prior node and are they siblings?
2100 0 : if (sibling)
2101 0 : mHTMLEditor->GetNextHTMLSibling(sibling, address_of(stepbrother));
2102 0 : if (startNode == stepbrother)
2103 : {
2104 : // are they both text nodes?
2105 0 : if (mHTMLEditor->IsTextNode(startNode) && mHTMLEditor->IsTextNode(sibling))
2106 : {
2107 : // if so, join them!
2108 0 : res = JoinNodesSmart(sibling, startNode, address_of(selNode), &selOffset);
2109 0 : NS_ENSURE_SUCCESS(res, res);
2110 : // fix up selection
2111 0 : res = aSelection->Collapse(selNode, selOffset);
2112 : }
2113 : }
2114 0 : NS_ENSURE_SUCCESS(res, res);
2115 0 : res = InsertBRIfNeeded(aSelection);
2116 0 : return res;
2117 : }
2118 0 : else if (wsType==nsWSRunObject::eOtherBlock)
2119 : {
2120 : // make sure it's not a table element. If so, cancel the operation
2121 : // (translation: users cannot backspace or delete across table cells)
2122 0 : if (nsHTMLEditUtils::IsTableElement(visNode))
2123 : {
2124 0 : *aCancel = true;
2125 0 : return NS_OK;
2126 : }
2127 :
2128 : // next to a block. See if we are between a block and a br. If so, we really
2129 : // want to delete the br. Else join content at selection to the block.
2130 :
2131 0 : bool bDeletedBR = false;
2132 : PRInt16 otherWSType;
2133 0 : nsCOMPtr<nsIDOMNode> otherNode;
2134 : PRInt32 otherOffset;
2135 :
2136 : // find node in other direction
2137 0 : if (aAction == nsIEditor::eNext)
2138 0 : res = wsObj.PriorVisibleNode(startNode, startOffset, address_of(otherNode), &otherOffset, &otherWSType);
2139 : else
2140 0 : res = wsObj.NextVisibleNode(startNode, startOffset, address_of(otherNode), &otherOffset, &otherWSType);
2141 0 : NS_ENSURE_SUCCESS(res, res);
2142 :
2143 : // first find the adjacent node in the block
2144 0 : nsCOMPtr<nsIDOMNode> leafNode, leftNode, rightNode, leftParent, rightParent;
2145 0 : if (aAction == nsIEditor::ePrevious)
2146 : {
2147 0 : res = mHTMLEditor->GetLastEditableLeaf( visNode, address_of(leafNode));
2148 0 : NS_ENSURE_SUCCESS(res, res);
2149 0 : leftNode = leafNode;
2150 0 : rightNode = startNode;
2151 : }
2152 : else
2153 : {
2154 0 : res = mHTMLEditor->GetFirstEditableLeaf( visNode, address_of(leafNode));
2155 0 : NS_ENSURE_SUCCESS(res, res);
2156 0 : leftNode = startNode;
2157 0 : rightNode = leafNode;
2158 : }
2159 :
2160 0 : if (nsTextEditUtils::IsBreak(otherNode))
2161 : {
2162 0 : res = mHTMLEditor->DeleteNode(otherNode);
2163 0 : NS_ENSURE_SUCCESS(res, res);
2164 0 : *aHandled = true;
2165 0 : bDeletedBR = true;
2166 : }
2167 :
2168 : // don't cross table boundaries
2169 0 : if (leftNode && rightNode)
2170 : {
2171 : bool bInDifTblElems;
2172 0 : res = InDifferentTableElements(leftNode, rightNode, &bInDifTblElems);
2173 0 : if (NS_FAILED(res) || bInDifTblElems) return res;
2174 : }
2175 :
2176 0 : if (bDeletedBR)
2177 : {
2178 : // put selection at edge of block and we are done.
2179 0 : nsCOMPtr<nsIDOMNode> newSelNode;
2180 : PRInt32 newSelOffset;
2181 0 : res = GetGoodSelPointForNode(leafNode, aAction, address_of(newSelNode), &newSelOffset);
2182 0 : NS_ENSURE_SUCCESS(res, res);
2183 0 : aSelection->Collapse(newSelNode, newSelOffset);
2184 0 : return res;
2185 : }
2186 :
2187 : // else we are joining content to block
2188 :
2189 : // find the relavent blocks
2190 0 : if (IsBlockNode(leftNode))
2191 0 : leftParent = leftNode;
2192 : else
2193 0 : leftParent = mHTMLEditor->GetBlockNodeParent(leftNode);
2194 0 : if (IsBlockNode(rightNode))
2195 0 : rightParent = rightNode;
2196 : else
2197 0 : rightParent = mHTMLEditor->GetBlockNodeParent(rightNode);
2198 :
2199 : // sanity checks
2200 0 : NS_ENSURE_TRUE(leftParent && rightParent, NS_ERROR_NULL_POINTER);
2201 0 : if (leftParent == rightParent)
2202 0 : return NS_ERROR_UNEXPECTED;
2203 :
2204 : // now join them
2205 0 : nsCOMPtr<nsIDOMNode> selPointNode = startNode;
2206 0 : PRInt32 selPointOffset = startOffset;
2207 : {
2208 0 : nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, address_of(selPointNode), &selPointOffset);
2209 0 : res = JoinBlocks(address_of(leftParent), address_of(rightParent), aCancel);
2210 0 : *aHandled = true;
2211 : }
2212 0 : aSelection->Collapse(selPointNode, selPointOffset);
2213 0 : return res;
2214 : }
2215 0 : else if (wsType==nsWSRunObject::eThisBlock)
2216 : {
2217 : // at edge of our block. Look beside it and see if we can join to an adjacent block
2218 :
2219 : // make sure it's not a table element. If so, cancel the operation
2220 : // (translation: users cannot backspace or delete across table cells)
2221 0 : if (nsHTMLEditUtils::IsTableElement(visNode))
2222 : {
2223 0 : *aCancel = true;
2224 0 : return NS_OK;
2225 : }
2226 :
2227 : // first find the relavent nodes
2228 0 : nsCOMPtr<nsIDOMNode> leftNode, rightNode, leftParent, rightParent;
2229 0 : if (aAction == nsIEditor::ePrevious)
2230 : {
2231 0 : res = mHTMLEditor->GetPriorHTMLNode(visNode, address_of(leftNode));
2232 0 : NS_ENSURE_SUCCESS(res, res);
2233 0 : rightNode = startNode;
2234 : }
2235 : else
2236 : {
2237 0 : res = mHTMLEditor->GetNextHTMLNode( visNode, address_of(rightNode));
2238 0 : NS_ENSURE_SUCCESS(res, res);
2239 0 : leftNode = startNode;
2240 : }
2241 :
2242 : // nothing to join
2243 0 : if (!leftNode || !rightNode)
2244 : {
2245 0 : *aCancel = true;
2246 0 : return NS_OK;
2247 : }
2248 :
2249 : // don't cross table boundaries
2250 : bool bInDifTblElems;
2251 0 : res = InDifferentTableElements(leftNode, rightNode, &bInDifTblElems);
2252 0 : if (NS_FAILED(res) || bInDifTblElems) return res;
2253 :
2254 : // find the relavent blocks
2255 0 : if (IsBlockNode(leftNode))
2256 0 : leftParent = leftNode;
2257 : else
2258 0 : leftParent = mHTMLEditor->GetBlockNodeParent(leftNode);
2259 0 : if (IsBlockNode(rightNode))
2260 0 : rightParent = rightNode;
2261 : else
2262 0 : rightParent = mHTMLEditor->GetBlockNodeParent(rightNode);
2263 :
2264 : // sanity checks
2265 0 : NS_ENSURE_TRUE(leftParent && rightParent, NS_ERROR_NULL_POINTER);
2266 0 : if (leftParent == rightParent)
2267 0 : return NS_ERROR_UNEXPECTED;
2268 :
2269 : // now join them
2270 0 : nsCOMPtr<nsIDOMNode> selPointNode = startNode;
2271 0 : PRInt32 selPointOffset = startOffset;
2272 : {
2273 0 : nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, address_of(selPointNode), &selPointOffset);
2274 0 : res = JoinBlocks(address_of(leftParent), address_of(rightParent), aCancel);
2275 0 : *aHandled = true;
2276 : }
2277 0 : aSelection->Collapse(selPointNode, selPointOffset);
2278 0 : return res;
2279 : }
2280 : }
2281 :
2282 :
2283 : // else we have a non collapsed selection
2284 : // first adjust the selection
2285 0 : res = ExpandSelectionForDeletion(aSelection);
2286 0 : NS_ENSURE_SUCCESS(res, res);
2287 :
2288 : // remember that we did a ranged delete for the benefit of AfterEditInner().
2289 0 : mDidRangedDelete = true;
2290 :
2291 : // refresh start and end points
2292 0 : res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(startNode), &startOffset);
2293 0 : NS_ENSURE_SUCCESS(res, res);
2294 0 : NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
2295 0 : nsCOMPtr<nsIDOMNode> endNode;
2296 : PRInt32 endOffset;
2297 0 : res = mHTMLEditor->GetEndNodeAndOffset(aSelection, getter_AddRefs(endNode), &endOffset);
2298 0 : NS_ENSURE_SUCCESS(res, res);
2299 0 : NS_ENSURE_TRUE(endNode, NS_ERROR_FAILURE);
2300 :
2301 : // figure out if the endpoints are in nodes that can be merged
2302 : // adjust surrounding whitespace in preperation to delete selection
2303 0 : if (!IsPlaintextEditor())
2304 : {
2305 0 : nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
2306 : res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor,
2307 : address_of(startNode), &startOffset,
2308 0 : address_of(endNode), &endOffset);
2309 0 : NS_ENSURE_SUCCESS(res, res);
2310 : }
2311 :
2312 : {
2313 : // track end location of where we are deleting
2314 0 : nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, address_of(endNode), &endOffset);
2315 : // we are handling all ranged deletions directly now.
2316 0 : *aHandled = true;
2317 :
2318 0 : if (endNode == startNode)
2319 : {
2320 0 : res = mHTMLEditor->DeleteSelectionImpl(aAction);
2321 0 : NS_ENSURE_SUCCESS(res, res);
2322 : }
2323 : else
2324 : {
2325 : // figure out mailcite ancestors
2326 0 : nsCOMPtr<nsIDOMNode> endCiteNode, startCiteNode;
2327 : res = GetTopEnclosingMailCite(startNode, address_of(startCiteNode),
2328 0 : IsPlaintextEditor());
2329 0 : NS_ENSURE_SUCCESS(res, res);
2330 : res = GetTopEnclosingMailCite(endNode, address_of(endCiteNode),
2331 0 : IsPlaintextEditor());
2332 0 : NS_ENSURE_SUCCESS(res, res);
2333 :
2334 : // if we only have a mailcite at one of the two endpoints, set the directionality
2335 : // of the deletion so that the selection will end up outside the mailcite.
2336 0 : if (startCiteNode && !endCiteNode)
2337 : {
2338 0 : aAction = nsIEditor::eNext;
2339 : }
2340 0 : else if (!startCiteNode && endCiteNode)
2341 : {
2342 0 : aAction = nsIEditor::ePrevious;
2343 : }
2344 :
2345 : // figure out block parents
2346 0 : nsCOMPtr<nsIDOMNode> leftParent;
2347 0 : nsCOMPtr<nsIDOMNode> rightParent;
2348 0 : if (IsBlockNode(startNode))
2349 0 : leftParent = startNode;
2350 : else
2351 0 : leftParent = mHTMLEditor->GetBlockNodeParent(startNode);
2352 0 : if (IsBlockNode(endNode))
2353 0 : rightParent = endNode;
2354 : else
2355 0 : rightParent = mHTMLEditor->GetBlockNodeParent(endNode);
2356 :
2357 : // are endpoint block parents the same? use default deletion
2358 0 : if (leftParent == rightParent)
2359 : {
2360 0 : res = mHTMLEditor->DeleteSelectionImpl(aAction);
2361 : }
2362 : else
2363 : {
2364 : // deleting across blocks
2365 : // are the blocks of same type?
2366 0 : NS_ENSURE_STATE(leftParent && rightParent);
2367 :
2368 : // are the blocks siblings?
2369 0 : nsCOMPtr<nsIDOMNode> leftBlockParent;
2370 0 : nsCOMPtr<nsIDOMNode> rightBlockParent;
2371 0 : leftParent->GetParentNode(getter_AddRefs(leftBlockParent));
2372 0 : rightParent->GetParentNode(getter_AddRefs(rightBlockParent));
2373 :
2374 : // MOOSE: this could conceivably screw up a table.. fix me.
2375 0 : if ( (leftBlockParent == rightBlockParent)
2376 0 : && (mHTMLEditor->NodesSameType(leftParent, rightParent)) )
2377 : {
2378 0 : if (nsHTMLEditUtils::IsParagraph(leftParent))
2379 : {
2380 : // first delete the selection
2381 0 : res = mHTMLEditor->DeleteSelectionImpl(aAction);
2382 0 : NS_ENSURE_SUCCESS(res, res);
2383 : // then join para's, insert break
2384 0 : res = mHTMLEditor->JoinNodeDeep(leftParent,rightParent,address_of(selNode),&selOffset);
2385 0 : NS_ENSURE_SUCCESS(res, res);
2386 : // fix up selection
2387 0 : res = aSelection->Collapse(selNode,selOffset);
2388 0 : return res;
2389 : }
2390 0 : if (nsHTMLEditUtils::IsListItem(leftParent)
2391 0 : || nsHTMLEditUtils::IsHeader(leftParent))
2392 : {
2393 : // first delete the selection
2394 0 : res = mHTMLEditor->DeleteSelectionImpl(aAction);
2395 0 : NS_ENSURE_SUCCESS(res, res);
2396 : // join blocks
2397 0 : res = mHTMLEditor->JoinNodeDeep(leftParent,rightParent,address_of(selNode),&selOffset);
2398 0 : NS_ENSURE_SUCCESS(res, res);
2399 : // fix up selection
2400 0 : res = aSelection->Collapse(selNode,selOffset);
2401 0 : return res;
2402 : }
2403 : }
2404 :
2405 : // else blocks not same type, or not siblings. Delete everything except
2406 : // table elements.
2407 0 : nsCOMPtr<nsIEnumerator> enumerator;
2408 0 : nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(aSelection));
2409 0 : res = selPriv->GetEnumerator(getter_AddRefs(enumerator));
2410 0 : NS_ENSURE_SUCCESS(res, res);
2411 0 : NS_ENSURE_TRUE(enumerator, NS_ERROR_UNEXPECTED);
2412 :
2413 0 : join = true;
2414 :
2415 0 : for (enumerator->First(); NS_OK!=enumerator->IsDone(); enumerator->Next())
2416 : {
2417 0 : nsCOMPtr<nsISupports> currentItem;
2418 0 : res = enumerator->CurrentItem(getter_AddRefs(currentItem));
2419 0 : NS_ENSURE_SUCCESS(res, res);
2420 0 : NS_ENSURE_TRUE(currentItem, NS_ERROR_UNEXPECTED);
2421 :
2422 : // build a list of nodes in the range
2423 0 : nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
2424 0 : nsCOMArray<nsIDOMNode> arrayOfNodes;
2425 0 : nsTrivialFunctor functor;
2426 0 : nsDOMSubtreeIterator iter;
2427 0 : res = iter.Init(range);
2428 0 : NS_ENSURE_SUCCESS(res, res);
2429 0 : res = iter.AppendList(functor, arrayOfNodes);
2430 0 : NS_ENSURE_SUCCESS(res, res);
2431 :
2432 : // now that we have the list, delete non table elements
2433 0 : PRInt32 listCount = arrayOfNodes.Count();
2434 0 : for (PRInt32 j = 0; j < listCount; j++) {
2435 0 : nsIDOMNode* somenode = arrayOfNodes[0];
2436 0 : res = DeleteNonTableElements(somenode);
2437 0 : arrayOfNodes.RemoveObjectAt(0);
2438 : // If something visible is deleted, no need to join.
2439 : // Visible means all nodes except non-visible textnodes and breaks.
2440 0 : if (join && origCollapsed) {
2441 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(somenode);
2442 0 : if (!content) {
2443 0 : join = false;
2444 0 : } else if (content->NodeType() == nsIDOMNode::TEXT_NODE) {
2445 0 : mHTMLEditor->IsVisTextNode(content, &join, true);
2446 : } else {
2447 0 : join = content->IsHTML(nsGkAtoms::br) &&
2448 0 : !mHTMLEditor->IsVisBreak(somenode);
2449 : }
2450 : }
2451 : }
2452 : }
2453 :
2454 : // check endopints for possible text deletion.
2455 : // we can assume that if text node is found, we can
2456 : // delete to end or to begining as appropriate,
2457 : // since the case where both sel endpoints in same
2458 : // text node was already handled (we wouldn't be here)
2459 0 : if ( mHTMLEditor->IsTextNode(startNode) )
2460 : {
2461 : // delete to last character
2462 0 : nsCOMPtr<nsIDOMCharacterData>nodeAsText;
2463 : PRUint32 len;
2464 0 : nodeAsText = do_QueryInterface(startNode);
2465 0 : nodeAsText->GetLength(&len);
2466 0 : if (len > (PRUint32)startOffset)
2467 : {
2468 0 : res = mHTMLEditor->DeleteText(nodeAsText,startOffset,len-startOffset);
2469 0 : NS_ENSURE_SUCCESS(res, res);
2470 : }
2471 : }
2472 0 : if ( mHTMLEditor->IsTextNode(endNode) )
2473 : {
2474 : // delete to first character
2475 0 : nsCOMPtr<nsIDOMCharacterData>nodeAsText;
2476 0 : nodeAsText = do_QueryInterface(endNode);
2477 0 : if (endOffset)
2478 : {
2479 0 : res = mHTMLEditor->DeleteText(nodeAsText,0,endOffset);
2480 0 : NS_ENSURE_SUCCESS(res, res);
2481 : }
2482 : }
2483 :
2484 0 : if (join) {
2485 : res = JoinBlocks(address_of(leftParent), address_of(rightParent),
2486 0 : aCancel);
2487 0 : NS_ENSURE_SUCCESS(res, res);
2488 : }
2489 : }
2490 : }
2491 : }
2492 : //If we're joining blocks: if deleting forward the selection should be
2493 : //collapsed to the end of the selection, if deleting backward the selection
2494 : //should be collapsed to the beginning of the selection. But if we're not
2495 : //joining then the selection should collapse to the beginning of the
2496 : //selection if we'redeleting forward, because the end of the selection will
2497 : //still be in the next block. And same thing for deleting backwards
2498 : //(selection should collapse to the end, because the beginning will still
2499 : //be in the first block). See Bug 507936
2500 0 : if (join ? aAction == nsIEditor::eNext : aAction == nsIEditor::ePrevious)
2501 : {
2502 0 : res = aSelection->Collapse(endNode,endOffset);
2503 : }
2504 : else
2505 : {
2506 0 : res = aSelection->Collapse(startNode,startOffset);
2507 : }
2508 0 : return res;
2509 : }
2510 :
2511 :
2512 : /*****************************************************************************************************
2513 : * InsertBRIfNeeded: determines if a br is needed for current selection to not be spastic.
2514 : * If so, it inserts one. Callers responsibility to only call with collapsed selection.
2515 : * nsISelection *aSelection the collapsed selection
2516 : */
2517 : nsresult
2518 0 : nsHTMLEditRules::InsertBRIfNeeded(nsISelection *aSelection)
2519 : {
2520 0 : NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
2521 :
2522 : // get selection
2523 0 : nsCOMPtr<nsIDOMNode> node;
2524 : PRInt32 offset;
2525 0 : nsresult res = mEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(node), &offset);
2526 0 : NS_ENSURE_SUCCESS(res, res);
2527 0 : NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
2528 :
2529 : // inline elements don't need any br
2530 0 : if (!IsBlockNode(node))
2531 0 : return res;
2532 :
2533 : // examine selection
2534 0 : nsWSRunObject wsObj(mHTMLEditor, node, offset);
2535 0 : if (((wsObj.mStartReason & nsWSRunObject::eBlock) || (wsObj.mStartReason & nsWSRunObject::eBreak))
2536 : && (wsObj.mEndReason & nsWSRunObject::eBlock))
2537 : {
2538 : // if we are tucked between block boundaries then insert a br
2539 : // first check that we are allowed to
2540 0 : if (mHTMLEditor->CanContainTag(node, NS_LITERAL_STRING("br")))
2541 : {
2542 0 : nsCOMPtr<nsIDOMNode> brNode;
2543 0 : res = mHTMLEditor->CreateBR(node, offset, address_of(brNode), nsIEditor::ePrevious);
2544 : }
2545 : }
2546 0 : return res;
2547 : }
2548 :
2549 : /*****************************************************************************************************
2550 : * GetGoodSelPointForNode: Finds where at a node you would want to set the selection if you were
2551 : * trying to have a caret next to it.
2552 : * nsIDOMNode *aNode the node
2553 : * nsIEditor::EDirection aAction which edge to find: eNext indicates beginning, ePrevious ending
2554 : * nsCOMPtr<nsIDOMNode> *outSelNode desired sel node
2555 : * PRInt32 *outSelOffset desired sel offset
2556 : */
2557 : nsresult
2558 0 : nsHTMLEditRules::GetGoodSelPointForNode(nsIDOMNode *aNode, nsIEditor::EDirection aAction,
2559 : nsCOMPtr<nsIDOMNode> *outSelNode, PRInt32 *outSelOffset)
2560 : {
2561 0 : NS_ENSURE_TRUE(aNode && outSelNode && outSelOffset, NS_ERROR_NULL_POINTER);
2562 :
2563 0 : nsresult res = NS_OK;
2564 :
2565 : // default values
2566 0 : *outSelNode = aNode;
2567 0 : *outSelOffset = 0;
2568 :
2569 0 : if (mHTMLEditor->IsTextNode(aNode) || mHTMLEditor->IsContainer(aNode))
2570 : {
2571 0 : if (aAction == nsIEditor::ePrevious)
2572 : {
2573 : PRUint32 len;
2574 0 : res = mHTMLEditor->GetLengthOfDOMNode(aNode, len);
2575 0 : *outSelOffset = PRInt32(len);
2576 0 : NS_ENSURE_SUCCESS(res, res);
2577 : }
2578 : }
2579 : else
2580 : {
2581 0 : res = nsEditor::GetNodeLocation(aNode, outSelNode, outSelOffset);
2582 0 : NS_ENSURE_SUCCESS(res, res);
2583 0 : if (!nsTextEditUtils::IsBreak(aNode) || mHTMLEditor->IsVisBreak(aNode))
2584 : {
2585 0 : if (aAction == nsIEditor::ePrevious)
2586 0 : (*outSelOffset)++;
2587 : }
2588 : }
2589 0 : return res;
2590 : }
2591 :
2592 :
2593 : /*****************************************************************************************************
2594 : * JoinBlocks: this method is used to join two block elements. The right element is always joined
2595 : * to the left element. If the elements are the same type and not nested within each other,
2596 : * JoinNodesSmart is called (example, joining two list items together into one). If the elements
2597 : * are not the same type, or one is a descendant of the other, we instead destroy the right block
2598 : * placing its children into leftblock. DTD containment rules are followed throughout.
2599 : * nsCOMPtr<nsIDOMNode> *aLeftBlock pointer to the left block
2600 : * nsCOMPtr<nsIDOMNode> *aRightBlock pointer to the right block; will have contents moved to left block
2601 : * bool *aCanceled return TRUE if we had to cancel operation
2602 : */
2603 : nsresult
2604 0 : nsHTMLEditRules::JoinBlocks(nsCOMPtr<nsIDOMNode> *aLeftBlock,
2605 : nsCOMPtr<nsIDOMNode> *aRightBlock,
2606 : bool *aCanceled)
2607 : {
2608 0 : NS_ENSURE_TRUE(aLeftBlock && aRightBlock && *aLeftBlock && *aRightBlock, NS_ERROR_NULL_POINTER);
2609 0 : if (nsHTMLEditUtils::IsTableElement(*aLeftBlock) || nsHTMLEditUtils::IsTableElement(*aRightBlock))
2610 : {
2611 : // do not try to merge table elements
2612 0 : *aCanceled = true;
2613 0 : return NS_OK;
2614 : }
2615 :
2616 : // make sure we don't try to move thing's into HR's, which look like blocks but aren't containers
2617 0 : if (nsHTMLEditUtils::IsHR(*aLeftBlock))
2618 : {
2619 0 : nsCOMPtr<nsIDOMNode> realLeft = mHTMLEditor->GetBlockNodeParent(*aLeftBlock);
2620 0 : *aLeftBlock = realLeft;
2621 : }
2622 0 : if (nsHTMLEditUtils::IsHR(*aRightBlock))
2623 : {
2624 0 : nsCOMPtr<nsIDOMNode> realRight = mHTMLEditor->GetBlockNodeParent(*aRightBlock);
2625 0 : *aRightBlock = realRight;
2626 : }
2627 :
2628 : // bail if both blocks the same
2629 0 : if (*aLeftBlock == *aRightBlock)
2630 : {
2631 0 : *aCanceled = true;
2632 0 : return NS_OK;
2633 : }
2634 :
2635 : // Joining a list item to its parent is a NOP.
2636 0 : if (nsHTMLEditUtils::IsList(*aLeftBlock) && nsHTMLEditUtils::IsListItem(*aRightBlock))
2637 : {
2638 0 : nsCOMPtr<nsIDOMNode> rightParent;
2639 0 : (*aRightBlock)->GetParentNode(getter_AddRefs(rightParent));
2640 0 : if (rightParent == *aLeftBlock)
2641 0 : return NS_OK;
2642 : }
2643 :
2644 : // special rule here: if we are trying to join list items, and they are in different lists,
2645 : // join the lists instead.
2646 0 : bool bMergeLists = false;
2647 0 : nsAutoString existingListStr;
2648 : PRInt32 theOffset;
2649 0 : nsCOMPtr<nsIDOMNode> leftList, rightList;
2650 0 : if (nsHTMLEditUtils::IsListItem(*aLeftBlock) && nsHTMLEditUtils::IsListItem(*aRightBlock))
2651 : {
2652 0 : (*aLeftBlock)->GetParentNode(getter_AddRefs(leftList));
2653 0 : (*aRightBlock)->GetParentNode(getter_AddRefs(rightList));
2654 0 : if (leftList && rightList && (leftList!=rightList))
2655 : {
2656 : // there are some special complications if the lists are descendants of
2657 : // the other lists' items. Note that it is ok for them to be descendants
2658 : // of the other lists themselves, which is the usual case for sublists
2659 : // in our impllementation.
2660 0 : if (!nsEditorUtils::IsDescendantOf(leftList, *aRightBlock, &theOffset) &&
2661 0 : !nsEditorUtils::IsDescendantOf(rightList, *aLeftBlock, &theOffset))
2662 : {
2663 0 : *aLeftBlock = leftList;
2664 0 : *aRightBlock = rightList;
2665 0 : bMergeLists = true;
2666 0 : mHTMLEditor->GetTagString(leftList, existingListStr);
2667 0 : ToLowerCase(existingListStr);
2668 : }
2669 : }
2670 : }
2671 :
2672 0 : nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
2673 :
2674 0 : nsresult res = NS_OK;
2675 0 : PRInt32 rightOffset = 0;
2676 0 : PRInt32 leftOffset = -1;
2677 :
2678 : // theOffset below is where you find yourself in aRightBlock when you traverse upwards
2679 : // from aLeftBlock
2680 0 : if (nsEditorUtils::IsDescendantOf(*aLeftBlock, *aRightBlock, &rightOffset))
2681 : {
2682 : // tricky case. left block is inside right block.
2683 : // Do ws adjustment. This just destroys non-visible ws at boundaries we will be joining.
2684 0 : rightOffset++;
2685 0 : res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor, aLeftBlock, nsWSRunObject::kBlockEnd);
2686 0 : NS_ENSURE_SUCCESS(res, res);
2687 0 : res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor, aRightBlock, nsWSRunObject::kAfterBlock, &rightOffset);
2688 0 : NS_ENSURE_SUCCESS(res, res);
2689 : // Do br adjustment.
2690 0 : nsCOMPtr<nsIDOMNode> brNode;
2691 0 : res = CheckForInvisibleBR(*aLeftBlock, kBlockEnd, address_of(brNode));
2692 0 : NS_ENSURE_SUCCESS(res, res);
2693 0 : if (bMergeLists)
2694 : {
2695 : // idea here is to take all children in rightList that are past
2696 : // theOffset, and pull them into leftlist.
2697 0 : nsCOMPtr<nsIDOMNode> childToMove;
2698 0 : nsCOMPtr<nsIContent> parent(do_QueryInterface(rightList));
2699 0 : NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
2700 :
2701 0 : nsIContent *child = parent->GetChildAt(theOffset);
2702 0 : while (child)
2703 : {
2704 0 : childToMove = do_QueryInterface(child);
2705 0 : res = mHTMLEditor->MoveNode(childToMove, leftList, -1);
2706 0 : NS_ENSURE_SUCCESS(res, res);
2707 :
2708 0 : child = parent->GetChildAt(rightOffset);
2709 : }
2710 : }
2711 : else
2712 : {
2713 0 : res = MoveBlock(*aLeftBlock, *aRightBlock, leftOffset, rightOffset);
2714 : }
2715 0 : if (brNode) mHTMLEditor->DeleteNode(brNode);
2716 : }
2717 : // theOffset below is where you find yourself in aLeftBlock when you traverse upwards
2718 : // from aRightBlock
2719 0 : else if (nsEditorUtils::IsDescendantOf(*aRightBlock, *aLeftBlock, &leftOffset))
2720 : {
2721 : // tricky case. right block is inside left block.
2722 : // Do ws adjustment. This just destroys non-visible ws at boundaries we will be joining.
2723 0 : res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor, aRightBlock, nsWSRunObject::kBlockStart);
2724 0 : NS_ENSURE_SUCCESS(res, res);
2725 0 : res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor, aLeftBlock, nsWSRunObject::kBeforeBlock, &leftOffset);
2726 0 : NS_ENSURE_SUCCESS(res, res);
2727 : // Do br adjustment.
2728 0 : nsCOMPtr<nsIDOMNode> brNode;
2729 0 : res = CheckForInvisibleBR(*aLeftBlock, kBeforeBlock, address_of(brNode), leftOffset);
2730 0 : NS_ENSURE_SUCCESS(res, res);
2731 0 : if (bMergeLists)
2732 : {
2733 0 : res = MoveContents(rightList, leftList, &leftOffset);
2734 : }
2735 : else
2736 : {
2737 0 : res = MoveBlock(*aLeftBlock, *aRightBlock, leftOffset, rightOffset);
2738 : }
2739 0 : if (brNode) mHTMLEditor->DeleteNode(brNode);
2740 : }
2741 : else
2742 : {
2743 : // normal case. blocks are siblings, or at least close enough to siblings. An example
2744 : // of the latter is a <p>paragraph</p><ul><li>one<li>two<li>three</ul>. The first
2745 : // li and the p are not true siblings, but we still want to join them if you backspace
2746 : // from li into p.
2747 :
2748 : // adjust whitespace at block boundaries
2749 0 : res = nsWSRunObject::PrepareToJoinBlocks(mHTMLEditor, *aLeftBlock, *aRightBlock);
2750 0 : NS_ENSURE_SUCCESS(res, res);
2751 : // Do br adjustment.
2752 0 : nsCOMPtr<nsIDOMNode> brNode;
2753 0 : res = CheckForInvisibleBR(*aLeftBlock, kBlockEnd, address_of(brNode));
2754 0 : NS_ENSURE_SUCCESS(res, res);
2755 0 : if (bMergeLists || mHTMLEditor->NodesSameType(*aLeftBlock, *aRightBlock))
2756 : {
2757 : // nodes are same type. merge them.
2758 0 : nsCOMPtr<nsIDOMNode> parent;
2759 : PRInt32 offset;
2760 0 : res = JoinNodesSmart(*aLeftBlock, *aRightBlock, address_of(parent), &offset);
2761 0 : if (NS_SUCCEEDED(res) && bMergeLists)
2762 : {
2763 0 : nsCOMPtr<nsIDOMNode> newBlock;
2764 0 : res = ConvertListType(*aRightBlock, address_of(newBlock), existingListStr, NS_LITERAL_STRING("li"));
2765 : }
2766 : }
2767 : else
2768 : {
2769 : // nodes are disimilar types.
2770 0 : res = MoveBlock(*aLeftBlock, *aRightBlock, leftOffset, rightOffset);
2771 : }
2772 0 : if (NS_SUCCEEDED(res) && brNode)
2773 : {
2774 0 : res = mHTMLEditor->DeleteNode(brNode);
2775 : }
2776 : }
2777 0 : return res;
2778 : }
2779 :
2780 :
2781 : /*****************************************************************************************************
2782 : * MoveBlock: this method is used to move the content from rightBlock into leftBlock
2783 : * Note that the "block" might merely be inline nodes between <br>s, or between blocks, etc.
2784 : * DTD containment rules are followed throughout.
2785 : * nsIDOMNode *aLeftBlock parent to receive moved content
2786 : * nsIDOMNode *aRightBlock parent to provide moved content
2787 : * PRInt32 aLeftOffset offset in aLeftBlock to move content to
2788 : * PRInt32 aRightOffset offset in aRightBlock to move content from
2789 : */
2790 : nsresult
2791 0 : nsHTMLEditRules::MoveBlock(nsIDOMNode *aLeftBlock, nsIDOMNode *aRightBlock, PRInt32 aLeftOffset, PRInt32 aRightOffset)
2792 : {
2793 0 : nsCOMArray<nsIDOMNode> arrayOfNodes;
2794 0 : nsCOMPtr<nsISupports> isupports;
2795 : // GetNodesFromPoint is the workhorse that figures out what we wnat to move.
2796 0 : nsresult res = GetNodesFromPoint(DOMPoint(aRightBlock,aRightOffset), kMakeList, arrayOfNodes, true);
2797 0 : NS_ENSURE_SUCCESS(res, res);
2798 0 : PRInt32 listCount = arrayOfNodes.Count();
2799 : PRInt32 i;
2800 0 : for (i=0; i<listCount; i++)
2801 : {
2802 : // get the node to act on
2803 0 : nsIDOMNode* curNode = arrayOfNodes[i];
2804 0 : if (IsBlockNode(curNode))
2805 : {
2806 : // For block nodes, move their contents only, then delete block.
2807 0 : res = MoveContents(curNode, aLeftBlock, &aLeftOffset);
2808 0 : NS_ENSURE_SUCCESS(res, res);
2809 0 : res = mHTMLEditor->DeleteNode(curNode);
2810 : }
2811 : else
2812 : {
2813 : // otherwise move the content as is, checking against the dtd.
2814 0 : res = MoveNodeSmart(curNode, aLeftBlock, &aLeftOffset);
2815 : }
2816 : }
2817 0 : return res;
2818 : }
2819 :
2820 : /*****************************************************************************************************
2821 : * MoveNodeSmart: this method is used to move node aSource to (aDest,aOffset).
2822 : * DTD containment rules are followed throughout. aOffset is updated to point _after_
2823 : * inserted content.
2824 : * nsIDOMNode *aSource the selection.
2825 : * nsIDOMNode *aDest parent to receive moved content
2826 : * PRInt32 *aOffset offset in aDest to move content to
2827 : */
2828 : nsresult
2829 0 : nsHTMLEditRules::MoveNodeSmart(nsIDOMNode *aSource, nsIDOMNode *aDest, PRInt32 *aOffset)
2830 : {
2831 0 : NS_ENSURE_TRUE(aSource && aDest && aOffset, NS_ERROR_NULL_POINTER);
2832 :
2833 0 : nsAutoString tag;
2834 : nsresult res;
2835 0 : res = mHTMLEditor->GetTagString(aSource, tag);
2836 0 : NS_ENSURE_SUCCESS(res, res);
2837 0 : ToLowerCase(tag);
2838 : // check if this node can go into the destination node
2839 0 : if (mHTMLEditor->CanContainTag(aDest, tag))
2840 : {
2841 : // if it can, move it there
2842 0 : res = mHTMLEditor->MoveNode(aSource, aDest, *aOffset);
2843 0 : NS_ENSURE_SUCCESS(res, res);
2844 0 : if (*aOffset != -1) ++(*aOffset);
2845 : }
2846 : else
2847 : {
2848 : // if it can't, move its children, and then delete it.
2849 0 : res = MoveContents(aSource, aDest, aOffset);
2850 0 : NS_ENSURE_SUCCESS(res, res);
2851 0 : res = mHTMLEditor->DeleteNode(aSource);
2852 0 : NS_ENSURE_SUCCESS(res, res);
2853 : }
2854 0 : return NS_OK;
2855 : }
2856 :
2857 : /*****************************************************************************************************
2858 : * MoveContents: this method is used to move node the _contents_ of aSource to (aDest,aOffset).
2859 : * DTD containment rules are followed throughout. aOffset is updated to point _after_
2860 : * inserted content. aSource is deleted.
2861 : * nsIDOMNode *aSource the selection.
2862 : * nsIDOMNode *aDest parent to receive moved content
2863 : * PRInt32 *aOffset offset in aDest to move content to
2864 : */
2865 : nsresult
2866 0 : nsHTMLEditRules::MoveContents(nsIDOMNode *aSource, nsIDOMNode *aDest, PRInt32 *aOffset)
2867 : {
2868 0 : NS_ENSURE_TRUE(aSource && aDest && aOffset, NS_ERROR_NULL_POINTER);
2869 0 : if (aSource == aDest) return NS_ERROR_ILLEGAL_VALUE;
2870 0 : NS_ASSERTION(!mHTMLEditor->IsTextNode(aSource), "#text does not have contents");
2871 :
2872 0 : nsCOMPtr<nsIDOMNode> child;
2873 0 : nsAutoString tag;
2874 : nsresult res;
2875 0 : aSource->GetFirstChild(getter_AddRefs(child));
2876 0 : while (child)
2877 : {
2878 0 : res = MoveNodeSmart(child, aDest, aOffset);
2879 0 : NS_ENSURE_SUCCESS(res, res);
2880 0 : aSource->GetFirstChild(getter_AddRefs(child));
2881 : }
2882 0 : return NS_OK;
2883 : }
2884 :
2885 :
2886 : nsresult
2887 0 : nsHTMLEditRules::DeleteNonTableElements(nsIDOMNode *aNode)
2888 : {
2889 0 : NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
2890 0 : nsresult res = NS_OK;
2891 0 : if (nsHTMLEditUtils::IsTableElementButNotTable(aNode))
2892 : {
2893 0 : nsCOMPtr<nsIDOMNodeList> children;
2894 0 : aNode->GetChildNodes(getter_AddRefs(children));
2895 0 : if (children)
2896 : {
2897 : PRUint32 len;
2898 0 : children->GetLength(&len);
2899 0 : NS_ENSURE_TRUE(len, NS_OK);
2900 : PRInt32 j;
2901 0 : for (j=len-1; j>=0; j--)
2902 : {
2903 0 : nsCOMPtr<nsIDOMNode> node;
2904 0 : children->Item(j,getter_AddRefs(node));
2905 0 : res = DeleteNonTableElements(node);
2906 0 : NS_ENSURE_SUCCESS(res, res);
2907 :
2908 : }
2909 : }
2910 : }
2911 : else
2912 : {
2913 0 : res = mHTMLEditor->DeleteNode(aNode);
2914 0 : NS_ENSURE_SUCCESS(res, res);
2915 : }
2916 0 : return res;
2917 : }
2918 :
2919 : nsresult
2920 0 : nsHTMLEditRules::DidDeleteSelection(nsISelection *aSelection,
2921 : nsIEditor::EDirection aDir,
2922 : nsresult aResult)
2923 : {
2924 0 : if (!aSelection) { return NS_ERROR_NULL_POINTER; }
2925 :
2926 : // find where we are
2927 0 : nsCOMPtr<nsIDOMNode> startNode;
2928 : PRInt32 startOffset;
2929 0 : nsresult res = mEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(startNode), &startOffset);
2930 0 : NS_ENSURE_SUCCESS(res, res);
2931 0 : NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
2932 :
2933 : // find any enclosing mailcite
2934 0 : nsCOMPtr<nsIDOMNode> citeNode;
2935 : res = GetTopEnclosingMailCite(startNode, address_of(citeNode),
2936 0 : IsPlaintextEditor());
2937 0 : NS_ENSURE_SUCCESS(res, res);
2938 0 : if (citeNode) {
2939 0 : nsCOMPtr<nsINode> cite = do_QueryInterface(citeNode);
2940 0 : bool isEmpty = true, seenBR = false;
2941 0 : mHTMLEditor->IsEmptyNodeImpl(cite, &isEmpty, true, true, false, &seenBR);
2942 0 : if (isEmpty)
2943 : {
2944 0 : nsCOMPtr<nsIDOMNode> parent, brNode;
2945 : PRInt32 offset;
2946 0 : nsEditor::GetNodeLocation(citeNode, address_of(parent), &offset);
2947 0 : res = mHTMLEditor->DeleteNode(citeNode);
2948 0 : NS_ENSURE_SUCCESS(res, res);
2949 0 : if (parent && seenBR)
2950 : {
2951 0 : res = mHTMLEditor->CreateBR(parent, offset, address_of(brNode));
2952 0 : NS_ENSURE_SUCCESS(res, res);
2953 0 : aSelection->Collapse(parent, offset);
2954 : }
2955 : }
2956 : }
2957 :
2958 : // call through to base class
2959 0 : return nsTextEditRules::DidDeleteSelection(aSelection, aDir, aResult);
2960 : }
2961 :
2962 : nsresult
2963 0 : nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
2964 : const nsAString *aListType,
2965 : bool aEntireList,
2966 : const nsAString *aBulletType,
2967 : bool *aCancel,
2968 : bool *aHandled,
2969 : const nsAString *aItemType)
2970 : {
2971 0 : if (!aSelection || !aListType || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
2972 :
2973 0 : nsresult res = WillInsert(aSelection, aCancel);
2974 0 : NS_ENSURE_SUCCESS(res, res);
2975 :
2976 : // initialize out param
2977 : // we want to ignore result of WillInsert()
2978 0 : *aCancel = false;
2979 0 : *aHandled = false;
2980 :
2981 : // deduce what tag to use for list items
2982 0 : nsAutoString itemType;
2983 0 : if (aItemType)
2984 0 : itemType = *aItemType;
2985 0 : else if (aListType->LowerCaseEqualsLiteral("dl"))
2986 0 : itemType.AssignLiteral("dd");
2987 : else
2988 0 : itemType.AssignLiteral("li");
2989 :
2990 : // convert the selection ranges into "promoted" selection ranges:
2991 : // this basically just expands the range to include the immediate
2992 : // block parent, and then further expands to include any ancestors
2993 : // whose children are all in the range
2994 :
2995 0 : *aHandled = true;
2996 :
2997 0 : res = NormalizeSelection(aSelection);
2998 0 : NS_ENSURE_SUCCESS(res, res);
2999 0 : nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
3000 :
3001 0 : nsCOMArray<nsIDOMNode> arrayOfNodes;
3002 0 : res = GetListActionNodes(arrayOfNodes, aEntireList);
3003 0 : NS_ENSURE_SUCCESS(res, res);
3004 :
3005 0 : PRInt32 listCount = arrayOfNodes.Count();
3006 :
3007 : // check if all our nodes are <br>s, or empty inlines
3008 0 : bool bOnlyBreaks = true;
3009 : PRInt32 j;
3010 0 : for (j=0; j<listCount; j++)
3011 : {
3012 0 : nsIDOMNode* curNode = arrayOfNodes[j];
3013 : // if curNode is not a Break or empty inline, we're done
3014 0 : if ( (!nsTextEditUtils::IsBreak(curNode)) && (!IsEmptyInline(curNode)) )
3015 : {
3016 0 : bOnlyBreaks = false;
3017 0 : break;
3018 : }
3019 : }
3020 :
3021 : // if no nodes, we make empty list. Ditto if the user tried to make a list of some # of breaks.
3022 0 : if (!listCount || bOnlyBreaks)
3023 : {
3024 0 : nsCOMPtr<nsIDOMNode> parent, theList, theListItem;
3025 : PRInt32 offset;
3026 :
3027 : // if only breaks, delete them
3028 0 : if (bOnlyBreaks)
3029 : {
3030 0 : for (j=0; j<(PRInt32)listCount; j++)
3031 : {
3032 0 : res = mHTMLEditor->DeleteNode(arrayOfNodes[j]);
3033 0 : NS_ENSURE_SUCCESS(res, res);
3034 : }
3035 : }
3036 :
3037 : // get selection location
3038 0 : res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(parent), &offset);
3039 0 : NS_ENSURE_SUCCESS(res, res);
3040 :
3041 : // make sure we can put a list here
3042 0 : if (!mHTMLEditor->CanContainTag(parent, *aListType)) {
3043 0 : *aCancel = true;
3044 0 : return NS_OK;
3045 : }
3046 0 : res = SplitAsNeeded(aListType, address_of(parent), &offset);
3047 0 : NS_ENSURE_SUCCESS(res, res);
3048 0 : res = mHTMLEditor->CreateNode(*aListType, parent, offset, getter_AddRefs(theList));
3049 0 : NS_ENSURE_SUCCESS(res, res);
3050 0 : res = mHTMLEditor->CreateNode(itemType, theList, 0, getter_AddRefs(theListItem));
3051 0 : NS_ENSURE_SUCCESS(res, res);
3052 : // remember our new block for postprocessing
3053 0 : mNewBlock = theListItem;
3054 : // put selection in new list item
3055 0 : res = aSelection->Collapse(theListItem,0);
3056 0 : selectionResetter.Abort(); // to prevent selection reseter from overriding us.
3057 0 : *aHandled = true;
3058 0 : return res;
3059 : }
3060 :
3061 : // if there is only one node in the array, and it is a list, div, or blockquote,
3062 : // then look inside of it until we find inner list or content.
3063 :
3064 0 : res = LookInsideDivBQandList(arrayOfNodes);
3065 0 : NS_ENSURE_SUCCESS(res, res);
3066 :
3067 : // Ok, now go through all the nodes and put then in the list,
3068 : // or whatever is approriate. Wohoo!
3069 :
3070 0 : listCount = arrayOfNodes.Count();
3071 0 : nsCOMPtr<nsIDOMNode> curParent;
3072 0 : nsCOMPtr<nsIDOMNode> curList;
3073 0 : nsCOMPtr<nsIDOMNode> prevListItem;
3074 :
3075 : PRInt32 i;
3076 0 : for (i=0; i<listCount; i++)
3077 : {
3078 : // here's where we actually figure out what to do
3079 0 : nsCOMPtr<nsIDOMNode> newBlock;
3080 0 : nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
3081 : PRInt32 offset;
3082 0 : res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset);
3083 0 : NS_ENSURE_SUCCESS(res, res);
3084 :
3085 : // make sure we don't assemble content that is in different table cells into the same list.
3086 : // respect table cell boundaries when listifying.
3087 0 : if (curList)
3088 : {
3089 : bool bInDifTblElems;
3090 0 : res = InDifferentTableElements(curList, curNode, &bInDifTblElems);
3091 0 : NS_ENSURE_SUCCESS(res, res);
3092 0 : if (bInDifTblElems)
3093 0 : curList = nsnull;
3094 : }
3095 :
3096 : // if curNode is a Break, delete it, and quit remembering prev list item
3097 0 : if (nsTextEditUtils::IsBreak(curNode))
3098 : {
3099 0 : res = mHTMLEditor->DeleteNode(curNode);
3100 0 : NS_ENSURE_SUCCESS(res, res);
3101 0 : prevListItem = 0;
3102 0 : continue;
3103 : }
3104 : // if curNode is an empty inline container, delete it
3105 0 : else if (IsEmptyInline(curNode))
3106 : {
3107 0 : res = mHTMLEditor->DeleteNode(curNode);
3108 0 : NS_ENSURE_SUCCESS(res, res);
3109 0 : continue;
3110 : }
3111 :
3112 0 : if (nsHTMLEditUtils::IsList(curNode))
3113 : {
3114 0 : nsAutoString existingListStr;
3115 0 : res = mHTMLEditor->GetTagString(curNode, existingListStr);
3116 0 : ToLowerCase(existingListStr);
3117 : // do we have a curList already?
3118 0 : if (curList && !nsEditorUtils::IsDescendantOf(curNode, curList))
3119 : {
3120 : // move all of our children into curList.
3121 : // cheezy way to do it: move whole list and then
3122 : // RemoveContainer() on the list.
3123 : // ConvertListType first: that routine
3124 : // handles converting the list item types, if needed
3125 0 : res = mHTMLEditor->MoveNode(curNode, curList, -1);
3126 0 : NS_ENSURE_SUCCESS(res, res);
3127 0 : res = ConvertListType(curNode, address_of(newBlock), *aListType, itemType);
3128 0 : NS_ENSURE_SUCCESS(res, res);
3129 0 : res = mHTMLEditor->RemoveBlockContainer(newBlock);
3130 0 : NS_ENSURE_SUCCESS(res, res);
3131 : }
3132 : else
3133 : {
3134 : // replace list with new list type
3135 0 : res = ConvertListType(curNode, address_of(newBlock), *aListType, itemType);
3136 0 : NS_ENSURE_SUCCESS(res, res);
3137 0 : curList = newBlock;
3138 : }
3139 0 : prevListItem = 0;
3140 0 : continue;
3141 : }
3142 :
3143 0 : if (nsHTMLEditUtils::IsListItem(curNode))
3144 : {
3145 0 : nsAutoString existingListStr;
3146 0 : res = mHTMLEditor->GetTagString(curParent, existingListStr);
3147 0 : ToLowerCase(existingListStr);
3148 0 : if ( existingListStr != *aListType )
3149 : {
3150 : // list item is in wrong type of list.
3151 : // if we don't have a curList, split the old list
3152 : // and make a new list of correct type.
3153 0 : if (!curList || nsEditorUtils::IsDescendantOf(curNode, curList))
3154 : {
3155 0 : res = mHTMLEditor->SplitNode(curParent, offset, getter_AddRefs(newBlock));
3156 0 : NS_ENSURE_SUCCESS(res, res);
3157 0 : nsCOMPtr<nsIDOMNode> p;
3158 : PRInt32 o;
3159 0 : res = nsEditor::GetNodeLocation(curParent, address_of(p), &o);
3160 0 : NS_ENSURE_SUCCESS(res, res);
3161 0 : res = mHTMLEditor->CreateNode(*aListType, p, o, getter_AddRefs(curList));
3162 0 : NS_ENSURE_SUCCESS(res, res);
3163 : }
3164 : // move list item to new list
3165 0 : res = mHTMLEditor->MoveNode(curNode, curList, -1);
3166 0 : NS_ENSURE_SUCCESS(res, res);
3167 : // convert list item type if needed
3168 0 : if (!mHTMLEditor->NodeIsTypeString(curNode,itemType))
3169 : {
3170 0 : res = mHTMLEditor->ReplaceContainer(curNode, address_of(newBlock), itemType);
3171 0 : NS_ENSURE_SUCCESS(res, res);
3172 : }
3173 : }
3174 : else
3175 : {
3176 : // item is in right type of list. But we might still have to move it.
3177 : // and we might need to convert list item types.
3178 0 : if (!curList)
3179 0 : curList = curParent;
3180 : else
3181 : {
3182 0 : if (curParent != curList)
3183 : {
3184 : // move list item to new list
3185 0 : res = mHTMLEditor->MoveNode(curNode, curList, -1);
3186 0 : NS_ENSURE_SUCCESS(res, res);
3187 : }
3188 : }
3189 0 : if (!mHTMLEditor->NodeIsTypeString(curNode,itemType))
3190 : {
3191 0 : res = mHTMLEditor->ReplaceContainer(curNode, address_of(newBlock), itemType);
3192 0 : NS_ENSURE_SUCCESS(res, res);
3193 : }
3194 : }
3195 0 : nsCOMPtr<nsIDOMElement> curElement = do_QueryInterface(curNode);
3196 0 : NS_NAMED_LITERAL_STRING(typestr, "type");
3197 0 : if (aBulletType && !aBulletType->IsEmpty()) {
3198 0 : res = mHTMLEditor->SetAttribute(curElement, typestr, *aBulletType);
3199 : }
3200 : else {
3201 0 : res = mHTMLEditor->RemoveAttribute(curElement, typestr);
3202 : }
3203 0 : NS_ENSURE_SUCCESS(res, res);
3204 0 : continue;
3205 : }
3206 :
3207 : // if we hit a div clear our prevListItem, insert divs contents
3208 : // into our node array, and remove the div
3209 0 : if (nsHTMLEditUtils::IsDiv(curNode))
3210 : {
3211 0 : prevListItem = nsnull;
3212 0 : PRInt32 j=i+1;
3213 0 : res = GetInnerContent(curNode, arrayOfNodes, &j);
3214 0 : NS_ENSURE_SUCCESS(res, res);
3215 0 : res = mHTMLEditor->RemoveContainer(curNode);
3216 0 : NS_ENSURE_SUCCESS(res, res);
3217 0 : listCount = arrayOfNodes.Count();
3218 0 : continue;
3219 : }
3220 :
3221 : // need to make a list to put things in if we haven't already,
3222 0 : if (!curList)
3223 : {
3224 0 : res = SplitAsNeeded(aListType, address_of(curParent), &offset);
3225 0 : NS_ENSURE_SUCCESS(res, res);
3226 0 : res = mHTMLEditor->CreateNode(*aListType, curParent, offset, getter_AddRefs(curList));
3227 0 : NS_ENSURE_SUCCESS(res, res);
3228 : // remember our new block for postprocessing
3229 0 : mNewBlock = curList;
3230 : // curList is now the correct thing to put curNode in
3231 0 : prevListItem = 0;
3232 : }
3233 :
3234 : // if curNode isn't a list item, we must wrap it in one
3235 0 : nsCOMPtr<nsIDOMNode> listItem;
3236 0 : if (!nsHTMLEditUtils::IsListItem(curNode))
3237 : {
3238 0 : if (IsInlineNode(curNode) && prevListItem)
3239 : {
3240 : // this is a continuation of some inline nodes that belong together in
3241 : // the same list item. use prevListItem
3242 0 : res = mHTMLEditor->MoveNode(curNode, prevListItem, -1);
3243 0 : NS_ENSURE_SUCCESS(res, res);
3244 : }
3245 : else
3246 : {
3247 : // don't wrap li around a paragraph. instead replace paragraph with li
3248 0 : if (nsHTMLEditUtils::IsParagraph(curNode))
3249 : {
3250 0 : res = mHTMLEditor->ReplaceContainer(curNode, address_of(listItem), itemType);
3251 : }
3252 : else
3253 : {
3254 0 : res = mHTMLEditor->InsertContainerAbove(curNode, address_of(listItem), itemType);
3255 : }
3256 0 : NS_ENSURE_SUCCESS(res, res);
3257 0 : if (IsInlineNode(curNode))
3258 0 : prevListItem = listItem;
3259 : else
3260 0 : prevListItem = nsnull;
3261 : }
3262 : }
3263 : else
3264 : {
3265 0 : listItem = curNode;
3266 : }
3267 :
3268 0 : if (listItem) // if we made a new list item, deal with it
3269 : {
3270 : // tuck the listItem into the end of the active list
3271 0 : res = mHTMLEditor->MoveNode(listItem, curList, -1);
3272 0 : NS_ENSURE_SUCCESS(res, res);
3273 : }
3274 : }
3275 :
3276 0 : return res;
3277 : }
3278 :
3279 :
3280 : nsresult
3281 0 : nsHTMLEditRules::WillRemoveList(nsISelection *aSelection,
3282 : bool aOrdered,
3283 : bool *aCancel,
3284 : bool *aHandled)
3285 : {
3286 0 : if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
3287 : // initialize out param
3288 0 : *aCancel = false;
3289 0 : *aHandled = true;
3290 :
3291 0 : nsresult res = NormalizeSelection(aSelection);
3292 0 : NS_ENSURE_SUCCESS(res, res);
3293 0 : nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
3294 :
3295 0 : nsCOMArray<nsIDOMRange> arrayOfRanges;
3296 0 : res = GetPromotedRanges(aSelection, arrayOfRanges, kMakeList);
3297 0 : NS_ENSURE_SUCCESS(res, res);
3298 :
3299 : // use these ranges to contruct a list of nodes to act on.
3300 0 : nsCOMArray<nsIDOMNode> arrayOfNodes;
3301 0 : res = GetListActionNodes(arrayOfNodes, false);
3302 0 : NS_ENSURE_SUCCESS(res, res);
3303 :
3304 : // Remove all non-editable nodes. Leave them be.
3305 0 : PRInt32 listCount = arrayOfNodes.Count();
3306 : PRInt32 i;
3307 0 : for (i=listCount-1; i>=0; i--)
3308 : {
3309 0 : nsIDOMNode* testNode = arrayOfNodes[i];
3310 0 : if (!mHTMLEditor->IsEditable(testNode))
3311 : {
3312 0 : arrayOfNodes.RemoveObjectAt(i);
3313 : }
3314 : }
3315 :
3316 : // reset list count
3317 0 : listCount = arrayOfNodes.Count();
3318 :
3319 : // Only act on lists or list items in the array
3320 0 : nsCOMPtr<nsIDOMNode> curParent;
3321 0 : for (i=0; i<listCount; i++)
3322 : {
3323 : // here's where we actually figure out what to do
3324 0 : nsIDOMNode* curNode = arrayOfNodes[i];
3325 : PRInt32 offset;
3326 0 : res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset);
3327 0 : NS_ENSURE_SUCCESS(res, res);
3328 :
3329 0 : if (nsHTMLEditUtils::IsListItem(curNode)) // unlist this listitem
3330 : {
3331 : bool bOutOfList;
3332 0 : do
3333 : {
3334 0 : res = PopListItem(curNode, &bOutOfList);
3335 0 : NS_ENSURE_SUCCESS(res, res);
3336 0 : } while (!bOutOfList); // keep popping it out until it's not in a list anymore
3337 : }
3338 0 : else if (nsHTMLEditUtils::IsList(curNode)) // node is a list, move list items out
3339 : {
3340 0 : res = RemoveListStructure(curNode);
3341 0 : NS_ENSURE_SUCCESS(res, res);
3342 : }
3343 : }
3344 0 : return res;
3345 : }
3346 :
3347 :
3348 : nsresult
3349 0 : nsHTMLEditRules::WillMakeDefListItem(nsISelection *aSelection,
3350 : const nsAString *aItemType,
3351 : bool aEntireList,
3352 : bool *aCancel,
3353 : bool *aHandled)
3354 : {
3355 : // for now we let WillMakeList handle this
3356 0 : NS_NAMED_LITERAL_STRING(listType, "dl");
3357 0 : return WillMakeList(aSelection, &listType, aEntireList, nsnull, aCancel, aHandled, aItemType);
3358 : }
3359 :
3360 : nsresult
3361 0 : nsHTMLEditRules::WillMakeBasicBlock(nsISelection *aSelection,
3362 : const nsAString *aBlockType,
3363 : bool *aCancel,
3364 : bool *aHandled)
3365 : {
3366 0 : if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
3367 : // initialize out param
3368 0 : *aCancel = false;
3369 0 : *aHandled = false;
3370 :
3371 0 : nsresult res = WillInsert(aSelection, aCancel);
3372 0 : NS_ENSURE_SUCCESS(res, res);
3373 : // initialize out param
3374 : // we want to ignore result of WillInsert()
3375 0 : *aCancel = false;
3376 0 : res = NormalizeSelection(aSelection);
3377 0 : NS_ENSURE_SUCCESS(res, res);
3378 0 : nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
3379 0 : nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
3380 0 : *aHandled = true;
3381 0 : nsString tString(*aBlockType);
3382 :
3383 : // contruct a list of nodes to act on.
3384 0 : nsCOMArray<nsIDOMNode> arrayOfNodes;
3385 0 : res = GetNodesFromSelection(aSelection, kMakeBasicBlock, arrayOfNodes);
3386 0 : NS_ENSURE_SUCCESS(res, res);
3387 :
3388 : // Remove all non-editable nodes. Leave them be.
3389 0 : PRInt32 listCount = arrayOfNodes.Count();
3390 : PRInt32 i;
3391 0 : for (i=listCount-1; i>=0; i--)
3392 : {
3393 0 : if (!mHTMLEditor->IsEditable(arrayOfNodes[i]))
3394 : {
3395 0 : arrayOfNodes.RemoveObjectAt(i);
3396 : }
3397 : }
3398 :
3399 : // reset list count
3400 0 : listCount = arrayOfNodes.Count();
3401 :
3402 : // if nothing visible in list, make an empty block
3403 0 : if (ListIsEmptyLine(arrayOfNodes))
3404 : {
3405 0 : nsCOMPtr<nsIDOMNode> parent, theBlock;
3406 : PRInt32 offset;
3407 :
3408 : // get selection location
3409 0 : res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(parent), &offset);
3410 0 : NS_ENSURE_SUCCESS(res, res);
3411 0 : if (tString.EqualsLiteral("normal") ||
3412 0 : tString.IsEmpty() ) // we are removing blocks (going to "body text")
3413 : {
3414 0 : nsCOMPtr<nsIDOMNode> curBlock = parent;
3415 0 : if (!IsBlockNode(curBlock))
3416 0 : curBlock = mHTMLEditor->GetBlockNodeParent(parent);
3417 0 : nsCOMPtr<nsIDOMNode> curBlockPar;
3418 0 : NS_ENSURE_TRUE(curBlock, NS_ERROR_NULL_POINTER);
3419 0 : curBlock->GetParentNode(getter_AddRefs(curBlockPar));
3420 0 : if (nsHTMLEditUtils::IsFormatNode(curBlock))
3421 : {
3422 : // if the first editable node after selection is a br, consume it. Otherwise
3423 : // it gets pushed into a following block after the split, which is visually bad.
3424 0 : nsCOMPtr<nsIDOMNode> brNode;
3425 0 : res = mHTMLEditor->GetNextHTMLNode(parent, offset, address_of(brNode));
3426 0 : NS_ENSURE_SUCCESS(res, res);
3427 0 : if (brNode && nsTextEditUtils::IsBreak(brNode))
3428 : {
3429 0 : res = mHTMLEditor->DeleteNode(brNode);
3430 0 : NS_ENSURE_SUCCESS(res, res);
3431 : }
3432 : // do the splits!
3433 0 : res = mHTMLEditor->SplitNodeDeep(curBlock, parent, offset, &offset, true);
3434 0 : NS_ENSURE_SUCCESS(res, res);
3435 : // put a br at the split point
3436 0 : res = mHTMLEditor->CreateBR(curBlockPar, offset, address_of(brNode));
3437 0 : NS_ENSURE_SUCCESS(res, res);
3438 : // put selection at the split point
3439 0 : res = aSelection->Collapse(curBlockPar, offset);
3440 0 : selectionResetter.Abort(); // to prevent selection reseter from overriding us.
3441 0 : *aHandled = true;
3442 : }
3443 : // else nothing to do!
3444 : }
3445 : else // we are making a block
3446 : {
3447 : // consume a br, if needed
3448 0 : nsCOMPtr<nsIDOMNode> brNode;
3449 0 : res = mHTMLEditor->GetNextHTMLNode(parent, offset, address_of(brNode), true);
3450 0 : NS_ENSURE_SUCCESS(res, res);
3451 0 : if (brNode && nsTextEditUtils::IsBreak(brNode))
3452 : {
3453 0 : res = mHTMLEditor->DeleteNode(brNode);
3454 0 : NS_ENSURE_SUCCESS(res, res);
3455 : // we don't need to act on this node any more
3456 0 : arrayOfNodes.RemoveObject(brNode);
3457 : }
3458 : // make sure we can put a block here
3459 0 : res = SplitAsNeeded(aBlockType, address_of(parent), &offset);
3460 0 : NS_ENSURE_SUCCESS(res, res);
3461 0 : res = mHTMLEditor->CreateNode(*aBlockType, parent, offset, getter_AddRefs(theBlock));
3462 0 : NS_ENSURE_SUCCESS(res, res);
3463 : // remember our new block for postprocessing
3464 0 : mNewBlock = theBlock;
3465 : // delete anything that was in the list of nodes
3466 0 : for (PRInt32 j = arrayOfNodes.Count() - 1; j >= 0; --j)
3467 : {
3468 0 : nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[0];
3469 0 : res = mHTMLEditor->DeleteNode(curNode);
3470 0 : NS_ENSURE_SUCCESS(res, res);
3471 0 : res = arrayOfNodes.RemoveObjectAt(0);
3472 0 : NS_ENSURE_SUCCESS(res, res);
3473 : }
3474 : // put selection in new block
3475 0 : res = aSelection->Collapse(theBlock,0);
3476 0 : selectionResetter.Abort(); // to prevent selection reseter from overriding us.
3477 0 : *aHandled = true;
3478 : }
3479 0 : return res;
3480 : }
3481 : else
3482 : {
3483 : // Ok, now go through all the nodes and make the right kind of blocks,
3484 : // or whatever is approriate. Wohoo!
3485 : // Note: blockquote is handled a little differently
3486 0 : if (tString.EqualsLiteral("blockquote"))
3487 0 : res = MakeBlockquote(arrayOfNodes);
3488 0 : else if (tString.EqualsLiteral("normal") ||
3489 0 : tString.IsEmpty() )
3490 0 : res = RemoveBlockStyle(arrayOfNodes);
3491 : else
3492 0 : res = ApplyBlockStyle(arrayOfNodes, aBlockType);
3493 0 : return res;
3494 : }
3495 : return res;
3496 : }
3497 :
3498 : nsresult
3499 0 : nsHTMLEditRules::DidMakeBasicBlock(nsISelection *aSelection,
3500 : nsRulesInfo *aInfo, nsresult aResult)
3501 : {
3502 0 : NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
3503 : // check for empty block. if so, put a moz br in it.
3504 : bool isCollapsed;
3505 0 : nsresult res = aSelection->GetIsCollapsed(&isCollapsed);
3506 0 : NS_ENSURE_SUCCESS(res, res);
3507 0 : if (!isCollapsed) {
3508 0 : return NS_OK;
3509 : }
3510 :
3511 0 : nsCOMPtr<nsIDOMNode> parent;
3512 : PRInt32 offset;
3513 0 : res = nsEditor::GetStartNodeAndOffset(aSelection, getter_AddRefs(parent), &offset);
3514 0 : NS_ENSURE_SUCCESS(res, res);
3515 0 : res = InsertMozBRIfNeeded(parent);
3516 0 : return res;
3517 : }
3518 :
3519 : nsresult
3520 0 : nsHTMLEditRules::WillIndent(nsISelection *aSelection, bool *aCancel, bool * aHandled)
3521 : {
3522 : nsresult res;
3523 0 : if (mHTMLEditor->IsCSSEnabled()) {
3524 0 : res = WillCSSIndent(aSelection, aCancel, aHandled);
3525 : }
3526 : else {
3527 0 : res = WillHTMLIndent(aSelection, aCancel, aHandled);
3528 : }
3529 0 : return res;
3530 : }
3531 :
3532 : nsresult
3533 0 : nsHTMLEditRules::WillCSSIndent(nsISelection *aSelection, bool *aCancel, bool * aHandled)
3534 : {
3535 0 : if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
3536 :
3537 0 : nsresult res = WillInsert(aSelection, aCancel);
3538 0 : NS_ENSURE_SUCCESS(res, res);
3539 :
3540 : // initialize out param
3541 : // we want to ignore result of WillInsert()
3542 0 : *aCancel = false;
3543 0 : *aHandled = true;
3544 :
3545 0 : res = NormalizeSelection(aSelection);
3546 0 : NS_ENSURE_SUCCESS(res, res);
3547 0 : nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
3548 0 : nsCOMArray<nsIDOMRange> arrayOfRanges;
3549 0 : nsCOMArray<nsIDOMNode> arrayOfNodes;
3550 :
3551 : // short circuit: detect case of collapsed selection inside an <li>.
3552 : // just sublist that <li>. This prevents bug 97797.
3553 :
3554 : bool bCollapsed;
3555 0 : nsCOMPtr<nsIDOMNode> liNode;
3556 0 : res = aSelection->GetIsCollapsed(&bCollapsed);
3557 0 : NS_ENSURE_SUCCESS(res, res);
3558 0 : if (bCollapsed)
3559 : {
3560 0 : nsCOMPtr<nsIDOMNode> node, block;
3561 : PRInt32 offset;
3562 0 : nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(node), &offset);
3563 0 : NS_ENSURE_SUCCESS(res, res);
3564 0 : if (IsBlockNode(node))
3565 0 : block = node;
3566 : else
3567 0 : block = mHTMLEditor->GetBlockNodeParent(node);
3568 0 : if (block && nsHTMLEditUtils::IsListItem(block))
3569 0 : liNode = block;
3570 : }
3571 :
3572 0 : if (liNode)
3573 : {
3574 0 : arrayOfNodes.AppendObject(liNode);
3575 : }
3576 : else
3577 : {
3578 : // convert the selection ranges into "promoted" selection ranges:
3579 : // this basically just expands the range to include the immediate
3580 : // block parent, and then further expands to include any ancestors
3581 : // whose children are all in the range
3582 0 : res = GetNodesFromSelection(aSelection, kIndent, arrayOfNodes);
3583 0 : NS_ENSURE_SUCCESS(res, res);
3584 : }
3585 :
3586 0 : NS_NAMED_LITERAL_STRING(quoteType, "blockquote");
3587 : // if nothing visible in list, make an empty block
3588 0 : if (ListIsEmptyLine(arrayOfNodes))
3589 : {
3590 0 : nsCOMPtr<nsIDOMNode> parent, theBlock;
3591 : PRInt32 offset;
3592 0 : nsAutoString quoteType(NS_LITERAL_STRING("div"));
3593 : // get selection location
3594 0 : res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(parent), &offset);
3595 0 : NS_ENSURE_SUCCESS(res, res);
3596 : // make sure we can put a block here
3597 0 : res = SplitAsNeeded("eType, address_of(parent), &offset);
3598 0 : NS_ENSURE_SUCCESS(res, res);
3599 0 : res = mHTMLEditor->CreateNode(quoteType, parent, offset, getter_AddRefs(theBlock));
3600 0 : NS_ENSURE_SUCCESS(res, res);
3601 : // remember our new block for postprocessing
3602 0 : mNewBlock = theBlock;
3603 0 : RelativeChangeIndentationOfElementNode(theBlock, +1);
3604 : // delete anything that was in the list of nodes
3605 0 : for (PRInt32 j = arrayOfNodes.Count() - 1; j >= 0; --j)
3606 : {
3607 0 : nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[0];
3608 0 : res = mHTMLEditor->DeleteNode(curNode);
3609 0 : NS_ENSURE_SUCCESS(res, res);
3610 0 : res = arrayOfNodes.RemoveObjectAt(0);
3611 0 : NS_ENSURE_SUCCESS(res, res);
3612 : }
3613 : // put selection in new block
3614 0 : res = aSelection->Collapse(theBlock,0);
3615 0 : selectionResetter.Abort(); // to prevent selection reseter from overriding us.
3616 0 : *aHandled = true;
3617 0 : return res;
3618 : }
3619 :
3620 : // Ok, now go through all the nodes and put them in a blockquote,
3621 : // or whatever is appropriate. Wohoo!
3622 : PRInt32 i;
3623 0 : nsCOMPtr<nsIDOMNode> curParent;
3624 0 : nsCOMPtr<nsIDOMNode> curQuote;
3625 0 : nsCOMPtr<nsIDOMNode> curList;
3626 0 : nsCOMPtr<nsIDOMNode> sibling;
3627 0 : PRInt32 listCount = arrayOfNodes.Count();
3628 0 : for (i=0; i<listCount; i++)
3629 : {
3630 : // here's where we actually figure out what to do
3631 0 : nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
3632 :
3633 : // Ignore all non-editable nodes. Leave them be.
3634 0 : if (!mHTMLEditor->IsEditable(curNode)) continue;
3635 :
3636 : PRInt32 offset;
3637 0 : res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset);
3638 0 : NS_ENSURE_SUCCESS(res, res);
3639 :
3640 : // some logic for putting list items into nested lists...
3641 0 : if (nsHTMLEditUtils::IsList(curParent))
3642 : {
3643 0 : sibling = nsnull;
3644 :
3645 : // Check for whether we should join a list that follows curNode.
3646 : // We do this if the next element is a list, and the list is of the
3647 : // same type (li/ol) as curNode was a part it.
3648 0 : mHTMLEditor->GetNextHTMLSibling(curNode, address_of(sibling));
3649 0 : if (sibling && nsHTMLEditUtils::IsList(sibling))
3650 : {
3651 0 : nsAutoString curListTag, siblingListTag;
3652 0 : nsEditor::GetTagString(curParent, curListTag);
3653 0 : nsEditor::GetTagString(sibling, siblingListTag);
3654 0 : if (curListTag == siblingListTag)
3655 : {
3656 0 : res = mHTMLEditor->MoveNode(curNode, sibling, 0);
3657 0 : NS_ENSURE_SUCCESS(res, res);
3658 0 : continue;
3659 : }
3660 : }
3661 : // Check for whether we should join a list that preceeds curNode.
3662 : // We do this if the previous element is a list, and the list is of
3663 : // the same type (li/ol) as curNode was a part of.
3664 0 : mHTMLEditor->GetPriorHTMLSibling(curNode, address_of(sibling));
3665 0 : if (sibling && nsHTMLEditUtils::IsList(sibling))
3666 : {
3667 0 : nsAutoString curListTag, siblingListTag;
3668 0 : nsEditor::GetTagString(curParent, curListTag);
3669 0 : nsEditor::GetTagString(sibling, siblingListTag);
3670 0 : if (curListTag == siblingListTag)
3671 : {
3672 0 : res = mHTMLEditor->MoveNode(curNode, sibling, -1);
3673 0 : NS_ENSURE_SUCCESS(res, res);
3674 0 : continue;
3675 : }
3676 : }
3677 0 : sibling = nsnull;
3678 :
3679 : // check to see if curList is still appropriate. Which it is if
3680 : // curNode is still right after it in the same list.
3681 0 : if (curList)
3682 0 : mHTMLEditor->GetPriorHTMLSibling(curNode, address_of(sibling));
3683 :
3684 0 : if (!curList || (sibling && sibling != curList))
3685 : {
3686 0 : nsAutoString listTag;
3687 0 : nsEditor::GetTagString(curParent,listTag);
3688 0 : ToLowerCase(listTag);
3689 : // create a new nested list of correct type
3690 0 : res = SplitAsNeeded(&listTag, address_of(curParent), &offset);
3691 0 : NS_ENSURE_SUCCESS(res, res);
3692 0 : res = mHTMLEditor->CreateNode(listTag, curParent, offset, getter_AddRefs(curList));
3693 0 : NS_ENSURE_SUCCESS(res, res);
3694 : // curList is now the correct thing to put curNode in
3695 : // remember our new block for postprocessing
3696 0 : mNewBlock = curList;
3697 : }
3698 : // tuck the node into the end of the active list
3699 : PRUint32 listLen;
3700 0 : res = mHTMLEditor->GetLengthOfDOMNode(curList, listLen);
3701 0 : NS_ENSURE_SUCCESS(res, res);
3702 0 : res = mHTMLEditor->MoveNode(curNode, curList, listLen);
3703 0 : NS_ENSURE_SUCCESS(res, res);
3704 : }
3705 :
3706 : else // not a list item
3707 : {
3708 0 : if (IsBlockNode(curNode)) {
3709 0 : RelativeChangeIndentationOfElementNode(curNode, +1);
3710 0 : curQuote = nsnull;
3711 : }
3712 : else {
3713 0 : if (!curQuote)
3714 : {
3715 : // First, check that our element can contain a div.
3716 0 : NS_NAMED_LITERAL_STRING(divquoteType, "div");
3717 0 : if (!mEditor->CanContainTag(curParent, divquoteType))
3718 0 : return NS_OK; // cancelled
3719 :
3720 0 : res = SplitAsNeeded(&divquoteType, address_of(curParent), &offset);
3721 0 : NS_ENSURE_SUCCESS(res, res);
3722 0 : res = mHTMLEditor->CreateNode(divquoteType, curParent, offset, getter_AddRefs(curQuote));
3723 0 : NS_ENSURE_SUCCESS(res, res);
3724 0 : RelativeChangeIndentationOfElementNode(curQuote, +1);
3725 : // remember our new block for postprocessing
3726 0 : mNewBlock = curQuote;
3727 : // curQuote is now the correct thing to put curNode in
3728 : }
3729 :
3730 : // tuck the node into the end of the active blockquote
3731 : PRUint32 quoteLen;
3732 0 : res = mHTMLEditor->GetLengthOfDOMNode(curQuote, quoteLen);
3733 0 : NS_ENSURE_SUCCESS(res, res);
3734 0 : res = mHTMLEditor->MoveNode(curNode, curQuote, quoteLen);
3735 0 : NS_ENSURE_SUCCESS(res, res);
3736 : }
3737 : }
3738 : }
3739 0 : return res;
3740 : }
3741 :
3742 : nsresult
3743 0 : nsHTMLEditRules::WillHTMLIndent(nsISelection *aSelection, bool *aCancel, bool * aHandled)
3744 : {
3745 0 : if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
3746 0 : nsresult res = WillInsert(aSelection, aCancel);
3747 0 : NS_ENSURE_SUCCESS(res, res);
3748 :
3749 : // initialize out param
3750 : // we want to ignore result of WillInsert()
3751 0 : *aCancel = false;
3752 0 : *aHandled = true;
3753 :
3754 0 : res = NormalizeSelection(aSelection);
3755 0 : NS_ENSURE_SUCCESS(res, res);
3756 0 : nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
3757 :
3758 : // convert the selection ranges into "promoted" selection ranges:
3759 : // this basically just expands the range to include the immediate
3760 : // block parent, and then further expands to include any ancestors
3761 : // whose children are all in the range
3762 :
3763 0 : nsCOMArray<nsIDOMRange> arrayOfRanges;
3764 0 : res = GetPromotedRanges(aSelection, arrayOfRanges, kIndent);
3765 0 : NS_ENSURE_SUCCESS(res, res);
3766 :
3767 : // use these ranges to contruct a list of nodes to act on.
3768 0 : nsCOMArray<nsIDOMNode> arrayOfNodes;
3769 0 : res = GetNodesForOperation(arrayOfRanges, arrayOfNodes, kIndent);
3770 0 : NS_ENSURE_SUCCESS(res, res);
3771 :
3772 0 : NS_NAMED_LITERAL_STRING(quoteType, "blockquote");
3773 :
3774 : // if nothing visible in list, make an empty block
3775 0 : if (ListIsEmptyLine(arrayOfNodes))
3776 : {
3777 0 : nsCOMPtr<nsIDOMNode> parent, theBlock;
3778 : PRInt32 offset;
3779 :
3780 : // get selection location
3781 0 : res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(parent), &offset);
3782 0 : NS_ENSURE_SUCCESS(res, res);
3783 : // make sure we can put a block here
3784 0 : res = SplitAsNeeded("eType, address_of(parent), &offset);
3785 0 : NS_ENSURE_SUCCESS(res, res);
3786 0 : res = mHTMLEditor->CreateNode(quoteType, parent, offset, getter_AddRefs(theBlock));
3787 0 : NS_ENSURE_SUCCESS(res, res);
3788 : // remember our new block for postprocessing
3789 0 : mNewBlock = theBlock;
3790 : // delete anything that was in the list of nodes
3791 0 : for (PRInt32 j = arrayOfNodes.Count() - 1; j >= 0; --j)
3792 : {
3793 0 : nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[0];
3794 0 : res = mHTMLEditor->DeleteNode(curNode);
3795 0 : NS_ENSURE_SUCCESS(res, res);
3796 0 : res = arrayOfNodes.RemoveObjectAt(0);
3797 0 : NS_ENSURE_SUCCESS(res, res);
3798 : }
3799 : // put selection in new block
3800 0 : res = aSelection->Collapse(theBlock,0);
3801 0 : selectionResetter.Abort(); // to prevent selection reseter from overriding us.
3802 0 : *aHandled = true;
3803 0 : return res;
3804 : }
3805 :
3806 : // Ok, now go through all the nodes and put them in a blockquote,
3807 : // or whatever is appropriate. Wohoo!
3808 : PRInt32 i;
3809 0 : nsCOMPtr<nsIDOMNode> curParent, curQuote, curList, indentedLI, sibling;
3810 0 : PRInt32 listCount = arrayOfNodes.Count();
3811 0 : for (i=0; i<listCount; i++)
3812 : {
3813 : // here's where we actually figure out what to do
3814 0 : nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
3815 :
3816 : // Ignore all non-editable nodes. Leave them be.
3817 0 : if (!mHTMLEditor->IsEditable(curNode)) continue;
3818 :
3819 : PRInt32 offset;
3820 0 : res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset);
3821 0 : NS_ENSURE_SUCCESS(res, res);
3822 :
3823 : // some logic for putting list items into nested lists...
3824 0 : if (nsHTMLEditUtils::IsList(curParent))
3825 : {
3826 0 : sibling = nsnull;
3827 :
3828 : // Check for whether we should join a list that follows curNode.
3829 : // We do this if the next element is a list, and the list is of the
3830 : // same type (li/ol) as curNode was a part it.
3831 0 : mHTMLEditor->GetNextHTMLSibling(curNode, address_of(sibling));
3832 0 : if (sibling && nsHTMLEditUtils::IsList(sibling))
3833 : {
3834 0 : nsAutoString curListTag, siblingListTag;
3835 0 : nsEditor::GetTagString(curParent, curListTag);
3836 0 : nsEditor::GetTagString(sibling, siblingListTag);
3837 0 : if (curListTag == siblingListTag)
3838 : {
3839 0 : res = mHTMLEditor->MoveNode(curNode, sibling, 0);
3840 0 : NS_ENSURE_SUCCESS(res, res);
3841 0 : continue;
3842 : }
3843 : }
3844 :
3845 : // Check for whether we should join a list that preceeds curNode.
3846 : // We do this if the previous element is a list, and the list is of
3847 : // the same type (li/ol) as curNode was a part of.
3848 0 : mHTMLEditor->GetPriorHTMLSibling(curNode, address_of(sibling));
3849 0 : if (sibling && nsHTMLEditUtils::IsList(sibling))
3850 : {
3851 0 : nsAutoString curListTag, siblingListTag;
3852 0 : nsEditor::GetTagString(curParent, curListTag);
3853 0 : nsEditor::GetTagString(sibling, siblingListTag);
3854 0 : if (curListTag == siblingListTag)
3855 : {
3856 0 : res = mHTMLEditor->MoveNode(curNode, sibling, -1);
3857 0 : NS_ENSURE_SUCCESS(res, res);
3858 0 : continue;
3859 : }
3860 : }
3861 :
3862 0 : sibling = nsnull;
3863 :
3864 : // check to see if curList is still appropriate. Which it is if
3865 : // curNode is still right after it in the same list.
3866 0 : if (curList)
3867 0 : mHTMLEditor->GetPriorHTMLSibling(curNode, address_of(sibling));
3868 :
3869 0 : if (!curList || (sibling && sibling != curList) )
3870 : {
3871 0 : nsAutoString listTag;
3872 0 : nsEditor::GetTagString(curParent,listTag);
3873 0 : ToLowerCase(listTag);
3874 : // create a new nested list of correct type
3875 0 : res = SplitAsNeeded(&listTag, address_of(curParent), &offset);
3876 0 : NS_ENSURE_SUCCESS(res, res);
3877 0 : res = mHTMLEditor->CreateNode(listTag, curParent, offset, getter_AddRefs(curList));
3878 0 : NS_ENSURE_SUCCESS(res, res);
3879 : // curList is now the correct thing to put curNode in
3880 : // remember our new block for postprocessing
3881 0 : mNewBlock = curList;
3882 : }
3883 : // tuck the node into the end of the active list
3884 0 : res = mHTMLEditor->MoveNode(curNode, curList, -1);
3885 0 : NS_ENSURE_SUCCESS(res, res);
3886 : // forget curQuote, if any
3887 0 : curQuote = nsnull;
3888 : }
3889 :
3890 : else // not a list item, use blockquote?
3891 : {
3892 : // if we are inside a list item, we don't want to blockquote, we want
3893 : // to sublist the list item. We may have several nodes listed in the
3894 : // array of nodes to act on, that are in the same list item. Since
3895 : // we only want to indent that li once, we must keep track of the most
3896 : // recent indented list item, and not indent it if we find another node
3897 : // to act on that is still inside the same li.
3898 0 : nsCOMPtr<nsIDOMNode> listitem=IsInListItem(curNode);
3899 0 : if (listitem)
3900 : {
3901 0 : if (indentedLI == listitem) continue; // already indented this list item
3902 0 : res = nsEditor::GetNodeLocation(listitem, address_of(curParent), &offset);
3903 0 : NS_ENSURE_SUCCESS(res, res);
3904 : // check to see if curList is still appropriate. Which it is if
3905 : // curNode is still right after it in the same list.
3906 0 : if (curList)
3907 : {
3908 0 : sibling = nsnull;
3909 0 : mHTMLEditor->GetPriorHTMLSibling(curNode, address_of(sibling));
3910 : }
3911 :
3912 0 : if (!curList || (sibling && sibling != curList) )
3913 : {
3914 0 : nsAutoString listTag;
3915 0 : nsEditor::GetTagString(curParent,listTag);
3916 0 : ToLowerCase(listTag);
3917 : // create a new nested list of correct type
3918 0 : res = SplitAsNeeded(&listTag, address_of(curParent), &offset);
3919 0 : NS_ENSURE_SUCCESS(res, res);
3920 0 : res = mHTMLEditor->CreateNode(listTag, curParent, offset, getter_AddRefs(curList));
3921 0 : NS_ENSURE_SUCCESS(res, res);
3922 : }
3923 0 : res = mHTMLEditor->MoveNode(listitem, curList, -1);
3924 0 : NS_ENSURE_SUCCESS(res, res);
3925 : // remember we indented this li
3926 0 : indentedLI = listitem;
3927 : }
3928 :
3929 : else
3930 : {
3931 : // need to make a blockquote to put things in if we haven't already,
3932 : // or if this node doesn't go in blockquote we used earlier.
3933 : // One reason it might not go in prio blockquote is if we are now
3934 : // in a different table cell.
3935 0 : if (curQuote)
3936 : {
3937 : bool bInDifTblElems;
3938 0 : res = InDifferentTableElements(curQuote, curNode, &bInDifTblElems);
3939 0 : NS_ENSURE_SUCCESS(res, res);
3940 0 : if (bInDifTblElems)
3941 0 : curQuote = nsnull;
3942 : }
3943 :
3944 0 : if (!curQuote)
3945 : {
3946 : // First, check that our element can contain a blockquote.
3947 0 : if (!mEditor->CanContainTag(curParent, quoteType))
3948 0 : return NS_OK; // cancelled
3949 :
3950 0 : res = SplitAsNeeded("eType, address_of(curParent), &offset);
3951 0 : NS_ENSURE_SUCCESS(res, res);
3952 0 : res = mHTMLEditor->CreateNode(quoteType, curParent, offset, getter_AddRefs(curQuote));
3953 0 : NS_ENSURE_SUCCESS(res, res);
3954 : // remember our new block for postprocessing
3955 0 : mNewBlock = curQuote;
3956 : // curQuote is now the correct thing to put curNode in
3957 : }
3958 :
3959 : // tuck the node into the end of the active blockquote
3960 0 : res = mHTMLEditor->MoveNode(curNode, curQuote, -1);
3961 0 : NS_ENSURE_SUCCESS(res, res);
3962 : // forget curList, if any
3963 0 : curList = nsnull;
3964 : }
3965 : }
3966 : }
3967 0 : return res;
3968 : }
3969 :
3970 :
3971 : nsresult
3972 0 : nsHTMLEditRules::WillOutdent(nsISelection *aSelection, bool *aCancel, bool *aHandled)
3973 : {
3974 0 : if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
3975 : // initialize out param
3976 0 : *aCancel = false;
3977 0 : *aHandled = true;
3978 0 : nsresult res = NS_OK;
3979 0 : nsCOMPtr<nsIDOMNode> rememberedLeftBQ, rememberedRightBQ;
3980 0 : bool useCSS = mHTMLEditor->IsCSSEnabled();
3981 :
3982 0 : res = NormalizeSelection(aSelection);
3983 0 : NS_ENSURE_SUCCESS(res, res);
3984 : // some scoping for selection resetting - we may need to tweak it
3985 : {
3986 0 : nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
3987 :
3988 : // convert the selection ranges into "promoted" selection ranges:
3989 : // this basically just expands the range to include the immediate
3990 : // block parent, and then further expands to include any ancestors
3991 : // whose children are all in the range
3992 0 : nsCOMArray<nsIDOMNode> arrayOfNodes;
3993 0 : res = GetNodesFromSelection(aSelection, kOutdent, arrayOfNodes);
3994 0 : NS_ENSURE_SUCCESS(res, res);
3995 :
3996 : // Ok, now go through all the nodes and remove a level of blockquoting,
3997 : // or whatever is appropriate. Wohoo!
3998 :
3999 0 : nsCOMPtr<nsIDOMNode> curBlockQuote, firstBQChild, lastBQChild;
4000 0 : bool curBlockQuoteIsIndentedWithCSS = false;
4001 0 : PRInt32 listCount = arrayOfNodes.Count();
4002 : PRInt32 i;
4003 0 : nsCOMPtr<nsIDOMNode> curParent;
4004 0 : for (i=0; i<listCount; i++)
4005 : {
4006 : // here's where we actually figure out what to do
4007 0 : nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
4008 : PRInt32 offset;
4009 0 : res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset);
4010 0 : NS_ENSURE_SUCCESS(res, res);
4011 :
4012 : // is it a blockquote?
4013 0 : if (nsHTMLEditUtils::IsBlockquote(curNode))
4014 : {
4015 : // if it is a blockquote, remove it.
4016 : // So we need to finish up dealng with any curBlockQuote first.
4017 0 : if (curBlockQuote)
4018 : {
4019 : res = OutdentPartOfBlock(curBlockQuote, firstBQChild, lastBQChild,
4020 : curBlockQuoteIsIndentedWithCSS,
4021 : address_of(rememberedLeftBQ),
4022 0 : address_of(rememberedRightBQ));
4023 0 : NS_ENSURE_SUCCESS(res, res);
4024 0 : curBlockQuote = 0; firstBQChild = 0; lastBQChild = 0;
4025 0 : curBlockQuoteIsIndentedWithCSS = false;
4026 : }
4027 0 : res = mHTMLEditor->RemoveBlockContainer(curNode);
4028 0 : NS_ENSURE_SUCCESS(res, res);
4029 0 : continue;
4030 : }
4031 : // is it a block with a 'margin' property?
4032 0 : if (useCSS && IsBlockNode(curNode))
4033 : {
4034 0 : nsIAtom* marginProperty = MarginPropertyAtomForIndent(mHTMLEditor->mHTMLCSSUtils, curNode);
4035 0 : nsAutoString value;
4036 0 : mHTMLEditor->mHTMLCSSUtils->GetSpecifiedProperty(curNode, marginProperty, value);
4037 : float f;
4038 0 : nsCOMPtr<nsIAtom> unit;
4039 0 : mHTMLEditor->mHTMLCSSUtils->ParseLength(value, &f, getter_AddRefs(unit));
4040 0 : if (f > 0)
4041 : {
4042 0 : RelativeChangeIndentationOfElementNode(curNode, -1);
4043 0 : continue;
4044 : }
4045 : }
4046 : // is it a list item?
4047 0 : if (nsHTMLEditUtils::IsListItem(curNode))
4048 : {
4049 : // if it is a list item, that means we are not outdenting whole list.
4050 : // So we need to finish up dealing with any curBlockQuote, and then
4051 : // pop this list item.
4052 0 : if (curBlockQuote)
4053 : {
4054 : res = OutdentPartOfBlock(curBlockQuote, firstBQChild, lastBQChild,
4055 : curBlockQuoteIsIndentedWithCSS,
4056 : address_of(rememberedLeftBQ),
4057 0 : address_of(rememberedRightBQ));
4058 0 : NS_ENSURE_SUCCESS(res, res);
4059 0 : curBlockQuote = 0; firstBQChild = 0; lastBQChild = 0;
4060 0 : curBlockQuoteIsIndentedWithCSS = false;
4061 : }
4062 : bool bOutOfList;
4063 0 : res = PopListItem(curNode, &bOutOfList);
4064 0 : NS_ENSURE_SUCCESS(res, res);
4065 0 : continue;
4066 : }
4067 : // do we have a blockquote that we are already committed to removing?
4068 0 : if (curBlockQuote)
4069 : {
4070 : // if so, is this node a descendant?
4071 0 : if (nsEditorUtils::IsDescendantOf(curNode, curBlockQuote))
4072 : {
4073 0 : lastBQChild = curNode;
4074 0 : continue; // then we don't need to do anything different for this node
4075 : }
4076 : else
4077 : {
4078 : // otherwise, we have progressed beyond end of curBlockQuote,
4079 : // so lets handle it now. We need to remove the portion of
4080 : // curBlockQuote that contains [firstBQChild - lastBQChild].
4081 : res = OutdentPartOfBlock(curBlockQuote, firstBQChild, lastBQChild,
4082 : curBlockQuoteIsIndentedWithCSS,
4083 : address_of(rememberedLeftBQ),
4084 0 : address_of(rememberedRightBQ));
4085 0 : NS_ENSURE_SUCCESS(res, res);
4086 0 : curBlockQuote = 0; firstBQChild = 0; lastBQChild = 0;
4087 0 : curBlockQuoteIsIndentedWithCSS = false;
4088 : // fall out and handle curNode
4089 : }
4090 : }
4091 :
4092 : // are we inside a blockquote?
4093 0 : nsCOMPtr<nsIDOMNode> n = curNode;
4094 0 : nsCOMPtr<nsIDOMNode> tmp;
4095 0 : curBlockQuoteIsIndentedWithCSS = false;
4096 : // keep looking up the hierarchy as long as we don't hit the body or the
4097 : // active editing host or a table element (other than an entire table)
4098 0 : while (!nsTextEditUtils::IsBody(n) && mHTMLEditor->IsNodeInActiveEditor(n)
4099 0 : && (nsHTMLEditUtils::IsTable(n) || !nsHTMLEditUtils::IsTableElement(n)))
4100 : {
4101 0 : n->GetParentNode(getter_AddRefs(tmp));
4102 0 : if (!tmp) {
4103 0 : break;
4104 : }
4105 0 : n = tmp;
4106 0 : if (nsHTMLEditUtils::IsBlockquote(n))
4107 : {
4108 : // if so, remember it, and remember first node we are taking out of it.
4109 0 : curBlockQuote = n;
4110 0 : firstBQChild = curNode;
4111 0 : lastBQChild = curNode;
4112 0 : break;
4113 : }
4114 0 : else if (useCSS)
4115 : {
4116 0 : nsIAtom* marginProperty = MarginPropertyAtomForIndent(mHTMLEditor->mHTMLCSSUtils, curNode);
4117 0 : nsAutoString value;
4118 0 : mHTMLEditor->mHTMLCSSUtils->GetSpecifiedProperty(n, marginProperty, value);
4119 : float f;
4120 0 : nsCOMPtr<nsIAtom> unit;
4121 0 : mHTMLEditor->mHTMLCSSUtils->ParseLength(value, &f, getter_AddRefs(unit));
4122 0 : if (f > 0 && !(nsHTMLEditUtils::IsList(curParent) && nsHTMLEditUtils::IsList(curNode)))
4123 : {
4124 0 : curBlockQuote = n;
4125 0 : firstBQChild = curNode;
4126 0 : lastBQChild = curNode;
4127 0 : curBlockQuoteIsIndentedWithCSS = true;
4128 : break;
4129 : }
4130 : }
4131 : }
4132 :
4133 0 : if (!curBlockQuote)
4134 : {
4135 : // could not find an enclosing blockquote for this node. handle list cases.
4136 0 : if (nsHTMLEditUtils::IsList(curParent)) // move node out of list
4137 : {
4138 0 : if (nsHTMLEditUtils::IsList(curNode)) // just unwrap this sublist
4139 : {
4140 0 : res = mHTMLEditor->RemoveBlockContainer(curNode);
4141 0 : NS_ENSURE_SUCCESS(res, res);
4142 : }
4143 : // handled list item case above
4144 : }
4145 0 : else if (nsHTMLEditUtils::IsList(curNode)) // node is a list, but parent is non-list: move list items out
4146 : {
4147 0 : nsCOMPtr<nsIDOMNode> child;
4148 0 : curNode->GetLastChild(getter_AddRefs(child));
4149 0 : while (child)
4150 : {
4151 0 : if (nsHTMLEditUtils::IsListItem(child))
4152 : {
4153 : bool bOutOfList;
4154 0 : res = PopListItem(child, &bOutOfList);
4155 0 : NS_ENSURE_SUCCESS(res, res);
4156 : }
4157 0 : else if (nsHTMLEditUtils::IsList(child))
4158 : {
4159 : // We have an embedded list, so move it out from under the
4160 : // parent list. Be sure to put it after the parent list
4161 : // because this loop iterates backwards through the parent's
4162 : // list of children.
4163 :
4164 0 : res = mHTMLEditor->MoveNode(child, curParent, offset + 1);
4165 0 : NS_ENSURE_SUCCESS(res, res);
4166 : }
4167 : else
4168 : {
4169 : // delete any non- list items for now
4170 0 : res = mHTMLEditor->DeleteNode(child);
4171 0 : NS_ENSURE_SUCCESS(res, res);
4172 : }
4173 0 : curNode->GetLastChild(getter_AddRefs(child));
4174 : }
4175 : // delete the now-empty list
4176 0 : res = mHTMLEditor->RemoveBlockContainer(curNode);
4177 0 : NS_ENSURE_SUCCESS(res, res);
4178 : }
4179 0 : else if (useCSS) {
4180 0 : nsCOMPtr<nsIDOMElement> element;
4181 0 : nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(curNode);
4182 0 : if (textNode) {
4183 : // We want to outdent the parent of text nodes
4184 0 : nsCOMPtr<nsIDOMNode> parent;
4185 0 : textNode->GetParentNode(getter_AddRefs(parent));
4186 0 : element = do_QueryInterface(parent);
4187 : } else {
4188 0 : element = do_QueryInterface(curNode);
4189 : }
4190 0 : if (element) {
4191 0 : RelativeChangeIndentationOfElementNode(element, -1);
4192 : }
4193 : }
4194 : }
4195 : }
4196 0 : if (curBlockQuote)
4197 : {
4198 : // we have a blockquote we haven't finished handling
4199 : res = OutdentPartOfBlock(curBlockQuote, firstBQChild, lastBQChild,
4200 : curBlockQuoteIsIndentedWithCSS,
4201 : address_of(rememberedLeftBQ),
4202 0 : address_of(rememberedRightBQ));
4203 0 : NS_ENSURE_SUCCESS(res, res);
4204 : }
4205 : }
4206 : // make sure selection didn't stick to last piece of content in old bq
4207 : // (only a problem for collapsed selections)
4208 0 : if (rememberedLeftBQ || rememberedRightBQ)
4209 : {
4210 : bool bCollapsed;
4211 0 : res = aSelection->GetIsCollapsed(&bCollapsed);
4212 0 : if (bCollapsed)
4213 : {
4214 : // push selection past end of rememberedLeftBQ
4215 0 : nsCOMPtr<nsIDOMNode> sNode;
4216 : PRInt32 sOffset;
4217 0 : mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(sNode), &sOffset);
4218 0 : if (rememberedLeftBQ &&
4219 0 : ((sNode == rememberedLeftBQ) || nsEditorUtils::IsDescendantOf(sNode, rememberedLeftBQ)))
4220 : {
4221 : // selection is inside rememberedLeftBQ - push it past it.
4222 0 : nsEditor::GetNodeLocation(rememberedLeftBQ, address_of(sNode), &sOffset);
4223 0 : sOffset++;
4224 0 : aSelection->Collapse(sNode, sOffset);
4225 : }
4226 : // and pull selection before beginning of rememberedRightBQ
4227 0 : mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(sNode), &sOffset);
4228 0 : if (rememberedRightBQ &&
4229 0 : ((sNode == rememberedRightBQ) || nsEditorUtils::IsDescendantOf(sNode, rememberedRightBQ)))
4230 : {
4231 : // selection is inside rememberedRightBQ - push it before it.
4232 0 : nsEditor::GetNodeLocation(rememberedRightBQ, address_of(sNode), &sOffset);
4233 0 : aSelection->Collapse(sNode, sOffset);
4234 : }
4235 : }
4236 : }
4237 0 : return res;
4238 : }
4239 :
4240 :
4241 : ///////////////////////////////////////////////////////////////////////////
4242 : // RemovePartOfBlock: split aBlock and move aStartChild to aEndChild out
4243 : // of aBlock. return left side of block (if any) in
4244 : // aLeftNode. return right side of block (if any) in
4245 : // aRightNode.
4246 : //
4247 : nsresult
4248 0 : nsHTMLEditRules::RemovePartOfBlock(nsIDOMNode *aBlock,
4249 : nsIDOMNode *aStartChild,
4250 : nsIDOMNode *aEndChild,
4251 : nsCOMPtr<nsIDOMNode> *aLeftNode,
4252 : nsCOMPtr<nsIDOMNode> *aRightNode)
4253 : {
4254 0 : nsCOMPtr<nsIDOMNode> middleNode;
4255 : nsresult res = SplitBlock(aBlock, aStartChild, aEndChild,
4256 : aLeftNode, aRightNode,
4257 0 : address_of(middleNode));
4258 0 : NS_ENSURE_SUCCESS(res, res);
4259 : // get rid of part of blockquote we are outdenting
4260 :
4261 0 : return mHTMLEditor->RemoveBlockContainer(aBlock);
4262 : }
4263 :
4264 : nsresult
4265 0 : nsHTMLEditRules::SplitBlock(nsIDOMNode *aBlock,
4266 : nsIDOMNode *aStartChild,
4267 : nsIDOMNode *aEndChild,
4268 : nsCOMPtr<nsIDOMNode> *aLeftNode,
4269 : nsCOMPtr<nsIDOMNode> *aRightNode,
4270 : nsCOMPtr<nsIDOMNode> *aMiddleNode)
4271 : {
4272 0 : NS_ENSURE_TRUE(aBlock && aStartChild && aEndChild, NS_ERROR_NULL_POINTER);
4273 :
4274 0 : nsCOMPtr<nsIDOMNode> startParent, endParent, leftNode, rightNode;
4275 : PRInt32 startOffset, endOffset, offset;
4276 : nsresult res;
4277 :
4278 : // get split point location
4279 0 : res = nsEditor::GetNodeLocation(aStartChild, address_of(startParent), &startOffset);
4280 0 : NS_ENSURE_SUCCESS(res, res);
4281 :
4282 : // do the splits!
4283 : res = mHTMLEditor->SplitNodeDeep(aBlock, startParent, startOffset, &offset,
4284 0 : true, address_of(leftNode), address_of(rightNode));
4285 0 : NS_ENSURE_SUCCESS(res, res);
4286 0 : if (rightNode) aBlock = rightNode;
4287 :
4288 : // remember left portion of block if caller requested
4289 0 : if (aLeftNode)
4290 0 : *aLeftNode = leftNode;
4291 :
4292 : // get split point location
4293 0 : res = nsEditor::GetNodeLocation(aEndChild, address_of(endParent), &endOffset);
4294 0 : NS_ENSURE_SUCCESS(res, res);
4295 0 : endOffset++; // want to be after lastBQChild
4296 :
4297 : // do the splits!
4298 : res = mHTMLEditor->SplitNodeDeep(aBlock, endParent, endOffset, &offset,
4299 0 : true, address_of(leftNode), address_of(rightNode));
4300 0 : NS_ENSURE_SUCCESS(res, res);
4301 0 : if (leftNode) aBlock = leftNode;
4302 :
4303 : // remember right portion of block if caller requested
4304 0 : if (aRightNode)
4305 0 : *aRightNode = rightNode;
4306 :
4307 0 : if (aMiddleNode)
4308 0 : *aMiddleNode = aBlock;
4309 :
4310 0 : return NS_OK;
4311 : }
4312 :
4313 : nsresult
4314 0 : nsHTMLEditRules::OutdentPartOfBlock(nsIDOMNode *aBlock,
4315 : nsIDOMNode *aStartChild,
4316 : nsIDOMNode *aEndChild,
4317 : bool aIsBlockIndentedWithCSS,
4318 : nsCOMPtr<nsIDOMNode> *aLeftNode,
4319 : nsCOMPtr<nsIDOMNode> *aRightNode)
4320 : {
4321 0 : nsCOMPtr<nsIDOMNode> middleNode;
4322 : nsresult res = SplitBlock(aBlock, aStartChild, aEndChild,
4323 : aLeftNode,
4324 : aRightNode,
4325 0 : address_of(middleNode));
4326 0 : NS_ENSURE_SUCCESS(res, res);
4327 0 : if (aIsBlockIndentedWithCSS)
4328 0 : res = RelativeChangeIndentationOfElementNode(middleNode, -1);
4329 : else
4330 0 : res = mHTMLEditor->RemoveBlockContainer(middleNode);
4331 0 : return res;
4332 : }
4333 :
4334 : ///////////////////////////////////////////////////////////////////////////
4335 : // ConvertListType: convert list type and list item type.
4336 : //
4337 : //
4338 : nsresult
4339 0 : nsHTMLEditRules::ConvertListType(nsIDOMNode *aList,
4340 : nsCOMPtr<nsIDOMNode> *outList,
4341 : const nsAString& aListType,
4342 : const nsAString& aItemType)
4343 : {
4344 0 : NS_ENSURE_TRUE(aList && outList, NS_ERROR_NULL_POINTER);
4345 0 : *outList = aList; // we might not need to change the list
4346 0 : nsresult res = NS_OK;
4347 0 : nsCOMPtr<nsIDOMNode> child, temp;
4348 0 : aList->GetFirstChild(getter_AddRefs(child));
4349 0 : while (child)
4350 : {
4351 0 : if (nsHTMLEditUtils::IsListItem(child) && !nsEditor::NodeIsTypeString(child, aItemType))
4352 : {
4353 0 : res = mHTMLEditor->ReplaceContainer(child, address_of(temp), aItemType);
4354 0 : NS_ENSURE_SUCCESS(res, res);
4355 0 : child = temp;
4356 : }
4357 0 : else if (nsHTMLEditUtils::IsList(child) && !nsEditor::NodeIsTypeString(child, aListType))
4358 : {
4359 0 : res = ConvertListType(child, address_of(temp), aListType, aItemType);
4360 0 : NS_ENSURE_SUCCESS(res, res);
4361 0 : child = temp;
4362 : }
4363 0 : child->GetNextSibling(getter_AddRefs(temp));
4364 0 : child = temp;
4365 : }
4366 0 : if (!nsEditor::NodeIsTypeString(aList, aListType))
4367 : {
4368 0 : res = mHTMLEditor->ReplaceContainer(aList, outList, aListType);
4369 : }
4370 0 : return res;
4371 : }
4372 :
4373 :
4374 : ///////////////////////////////////////////////////////////////////////////
4375 : // CreateStyleForInsertText: take care of clearing and setting appropriate
4376 : // style nodes for text insertion.
4377 : //
4378 : //
4379 : nsresult
4380 0 : nsHTMLEditRules::CreateStyleForInsertText(nsISelection *aSelection, nsIDOMDocument *aDoc)
4381 : {
4382 0 : NS_ENSURE_TRUE(aSelection && aDoc, NS_ERROR_NULL_POINTER);
4383 0 : NS_ENSURE_TRUE(mHTMLEditor->mTypeInState, NS_ERROR_NULL_POINTER);
4384 :
4385 0 : bool weDidSometing = false;
4386 0 : nsCOMPtr<nsIDOMNode> node, tmp;
4387 : PRInt32 offset;
4388 0 : nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(node), &offset);
4389 0 : NS_ENSURE_SUCCESS(res, res);
4390 0 : nsAutoPtr<PropItem> item;
4391 :
4392 : // if we deleted selection then also for cached styles
4393 0 : if (mDidDeleteSelection &&
4394 : ((mTheAction == nsEditor::kOpInsertText ) ||
4395 : (mTheAction == nsEditor::kOpInsertIMEText) ||
4396 : (mTheAction == nsEditor::kOpInsertBreak) ||
4397 : (mTheAction == nsEditor::kOpDeleteSelection)))
4398 : {
4399 0 : res = ReapplyCachedStyles();
4400 0 : NS_ENSURE_SUCCESS(res, res);
4401 : }
4402 : // either way we clear the cached styles array
4403 0 : res = ClearCachedStyles();
4404 0 : NS_ENSURE_SUCCESS(res, res);
4405 :
4406 : // next examine our present style and make sure default styles are either present or
4407 : // explicitly overridden. If neither, add the default style to the TypeInState
4408 0 : PRInt32 j, defcon = mHTMLEditor->mDefaultStyles.Length();
4409 0 : for (j=0; j<defcon; j++)
4410 : {
4411 0 : PropItem *propItem = mHTMLEditor->mDefaultStyles[j];
4412 0 : NS_ENSURE_TRUE(propItem, NS_ERROR_NULL_POINTER);
4413 : bool bFirst, bAny, bAll;
4414 :
4415 : // GetInlineProperty also examine TypeInState. The only gotcha here is that a cleared
4416 : // property looks like an unset property. For now I'm assuming that's not a problem:
4417 : // that default styles will always be multivalue styles (like font face or size) where
4418 : // clearing the style means we want to go back to the default. If we ever wanted a
4419 : // "toggle" style like bold for a default, though, I'll have to add code to detect the
4420 : // difference between unset and explicitly cleared, else user would never be able to
4421 : // unbold, for instance.
4422 0 : nsAutoString curValue;
4423 : res = mHTMLEditor->GetInlinePropertyBase(propItem->tag, &(propItem->attr), nsnull,
4424 0 : &bFirst, &bAny, &bAll, &curValue, false);
4425 0 : NS_ENSURE_SUCCESS(res, res);
4426 :
4427 0 : if (!bAny) // no style set for this prop/attr
4428 : {
4429 0 : mHTMLEditor->mTypeInState->SetProp(propItem->tag, propItem->attr, propItem->value);
4430 : }
4431 : }
4432 :
4433 0 : nsCOMPtr<nsIDOMElement> rootElement;
4434 0 : res = aDoc->GetDocumentElement(getter_AddRefs(rootElement));
4435 0 : NS_ENSURE_SUCCESS(res, res);
4436 :
4437 : // process clearing any styles first
4438 0 : mHTMLEditor->mTypeInState->TakeClearProperty(getter_Transfers(item));
4439 0 : while (item && node != rootElement)
4440 : {
4441 0 : nsCOMPtr<nsIDOMNode> leftNode, rightNode, secondSplitParent, newSelParent, savedBR;
4442 0 : res = mHTMLEditor->SplitStyleAbovePoint(address_of(node), &offset, item->tag, &item->attr, address_of(leftNode), address_of(rightNode));
4443 0 : NS_ENSURE_SUCCESS(res, res);
4444 : bool bIsEmptyNode;
4445 0 : if (leftNode)
4446 : {
4447 0 : mHTMLEditor->IsEmptyNode(leftNode, &bIsEmptyNode, false, true);
4448 0 : if (bIsEmptyNode)
4449 : {
4450 : // delete leftNode if it became empty
4451 0 : res = mEditor->DeleteNode(leftNode);
4452 0 : NS_ENSURE_SUCCESS(res, res);
4453 : }
4454 : }
4455 0 : if (rightNode)
4456 : {
4457 0 : secondSplitParent = mHTMLEditor->GetLeftmostChild(rightNode);
4458 : // don't try to split non-containers (br's, images, hr's, etc)
4459 0 : if (!secondSplitParent) secondSplitParent = rightNode;
4460 0 : if (!mHTMLEditor->IsContainer(secondSplitParent))
4461 : {
4462 0 : if (nsTextEditUtils::IsBreak(secondSplitParent))
4463 0 : savedBR = secondSplitParent;
4464 :
4465 0 : secondSplitParent->GetParentNode(getter_AddRefs(tmp));
4466 0 : secondSplitParent = tmp;
4467 : }
4468 0 : offset = 0;
4469 0 : res = mHTMLEditor->SplitStyleAbovePoint(address_of(secondSplitParent), &offset, item->tag, &(item->attr), address_of(leftNode), address_of(rightNode));
4470 0 : NS_ENSURE_SUCCESS(res, res);
4471 : // should be impossible to not get a new leftnode here
4472 0 : NS_ENSURE_TRUE(leftNode, NS_ERROR_FAILURE);
4473 0 : newSelParent = mHTMLEditor->GetLeftmostChild(leftNode);
4474 0 : if (!newSelParent) newSelParent = leftNode;
4475 : // if rightNode starts with a br, suck it out of right node and into leftNode.
4476 : // This is so we you don't revert back to the previous style if you happen to click at the end of a line.
4477 0 : if (savedBR)
4478 : {
4479 0 : res = mEditor->MoveNode(savedBR, newSelParent, 0);
4480 0 : NS_ENSURE_SUCCESS(res, res);
4481 : }
4482 0 : mHTMLEditor->IsEmptyNode(rightNode, &bIsEmptyNode, false, true);
4483 0 : if (bIsEmptyNode)
4484 : {
4485 : // delete rightNode if it became empty
4486 0 : res = mEditor->DeleteNode(rightNode);
4487 0 : NS_ENSURE_SUCCESS(res, res);
4488 : }
4489 : // remove the style on this new hierarchy
4490 0 : PRInt32 newSelOffset = 0;
4491 : {
4492 : // track the point at the new hierarchy.
4493 : // This is so we can know where to put the selection after we call
4494 : // RemoveStyleInside(). RemoveStyleInside() could remove any and all of those nodes,
4495 : // so I have to use the range tracking system to find the right spot to put selection.
4496 0 : nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, address_of(newSelParent), &newSelOffset);
4497 0 : res = mHTMLEditor->RemoveStyleInside(leftNode, item->tag, &(item->attr));
4498 0 : NS_ENSURE_SUCCESS(res, res);
4499 : }
4500 : // reset our node offset values to the resulting new sel point
4501 0 : node = newSelParent;
4502 0 : offset = newSelOffset;
4503 : }
4504 0 : mHTMLEditor->mTypeInState->TakeClearProperty(getter_Transfers(item));
4505 0 : weDidSometing = true;
4506 : }
4507 :
4508 : // then process setting any styles
4509 : PRInt32 relFontSize;
4510 :
4511 0 : res = mHTMLEditor->mTypeInState->TakeRelativeFontSize(&relFontSize);
4512 0 : NS_ENSURE_SUCCESS(res, res);
4513 0 : res = mHTMLEditor->mTypeInState->TakeSetProperty(getter_Transfers(item));
4514 0 : NS_ENSURE_SUCCESS(res, res);
4515 :
4516 0 : if (item || relFontSize) // we have at least one style to add; make a
4517 : { // new text node to insert style nodes above.
4518 0 : if (mHTMLEditor->IsTextNode(node))
4519 : {
4520 : // if we are in a text node, split it
4521 0 : res = mHTMLEditor->SplitNodeDeep(node, node, offset, &offset);
4522 0 : NS_ENSURE_SUCCESS(res, res);
4523 0 : node->GetParentNode(getter_AddRefs(tmp));
4524 0 : node = tmp;
4525 : }
4526 0 : if (!mHTMLEditor->IsContainer(node))
4527 : {
4528 0 : return NS_OK;
4529 : }
4530 0 : nsCOMPtr<nsIDOMNode> newNode;
4531 0 : nsCOMPtr<nsIDOMText> nodeAsText;
4532 0 : res = aDoc->CreateTextNode(EmptyString(), getter_AddRefs(nodeAsText));
4533 0 : NS_ENSURE_SUCCESS(res, res);
4534 0 : NS_ENSURE_TRUE(nodeAsText, NS_ERROR_NULL_POINTER);
4535 0 : newNode = do_QueryInterface(nodeAsText);
4536 0 : res = mHTMLEditor->InsertNode(newNode, node, offset);
4537 0 : NS_ENSURE_SUCCESS(res, res);
4538 0 : node = newNode;
4539 0 : offset = 0;
4540 0 : weDidSometing = true;
4541 :
4542 0 : if (relFontSize)
4543 : {
4544 : PRInt32 j, dir;
4545 : // dir indicated bigger versus smaller. 1 = bigger, -1 = smaller
4546 0 : if (relFontSize > 0) dir=1;
4547 0 : else dir = -1;
4548 0 : for (j=0; j<abs(relFontSize); j++)
4549 : {
4550 0 : res = mHTMLEditor->RelativeFontChangeOnTextNode(dir, nodeAsText, 0, -1);
4551 0 : NS_ENSURE_SUCCESS(res, res);
4552 : }
4553 : }
4554 :
4555 0 : while (item)
4556 : {
4557 0 : res = mHTMLEditor->SetInlinePropertyOnNode(node, item->tag, &item->attr, &item->value);
4558 0 : NS_ENSURE_SUCCESS(res, res);
4559 0 : mHTMLEditor->mTypeInState->TakeSetProperty(getter_Transfers(item));
4560 : }
4561 : }
4562 0 : if (weDidSometing)
4563 0 : return aSelection->Collapse(node, offset);
4564 :
4565 0 : return res;
4566 : }
4567 :
4568 :
4569 : ///////////////////////////////////////////////////////////////////////////
4570 : // IsEmptyBlock: figure out if aNode is (or is inside) an empty block.
4571 : // A block can have children and still be considered empty,
4572 : // if the children are empty or non-editable.
4573 : //
4574 : nsresult
4575 0 : nsHTMLEditRules::IsEmptyBlock(nsIDOMNode *aNode,
4576 : bool *outIsEmptyBlock,
4577 : bool aMozBRDoesntCount,
4578 : bool aListItemsNotEmpty)
4579 : {
4580 0 : NS_ENSURE_TRUE(aNode && outIsEmptyBlock, NS_ERROR_NULL_POINTER);
4581 0 : *outIsEmptyBlock = true;
4582 :
4583 : // nsresult res = NS_OK;
4584 0 : nsCOMPtr<nsIDOMNode> nodeToTest;
4585 0 : if (IsBlockNode(aNode)) nodeToTest = do_QueryInterface(aNode);
4586 : // else nsCOMPtr<nsIDOMElement> block;
4587 : // looks like I forgot to finish this. Wonder what I was going to do?
4588 :
4589 0 : NS_ENSURE_TRUE(nodeToTest, NS_ERROR_NULL_POINTER);
4590 : return mHTMLEditor->IsEmptyNode(nodeToTest, outIsEmptyBlock,
4591 0 : aMozBRDoesntCount, aListItemsNotEmpty);
4592 : }
4593 :
4594 :
4595 : nsresult
4596 0 : nsHTMLEditRules::WillAlign(nsISelection *aSelection,
4597 : const nsAString *alignType,
4598 : bool *aCancel,
4599 : bool *aHandled)
4600 : {
4601 0 : if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
4602 :
4603 0 : nsresult res = WillInsert(aSelection, aCancel);
4604 0 : NS_ENSURE_SUCCESS(res, res);
4605 :
4606 : // initialize out param
4607 : // we want to ignore result of WillInsert()
4608 0 : *aCancel = false;
4609 0 : *aHandled = false;
4610 :
4611 0 : res = NormalizeSelection(aSelection);
4612 0 : NS_ENSURE_SUCCESS(res, res);
4613 0 : nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
4614 :
4615 : // convert the selection ranges into "promoted" selection ranges:
4616 : // this basically just expands the range to include the immediate
4617 : // block parent, and then further expands to include any ancestors
4618 : // whose children are all in the range
4619 0 : *aHandled = true;
4620 0 : nsCOMArray<nsIDOMNode> arrayOfNodes;
4621 0 : res = GetNodesFromSelection(aSelection, kAlign, arrayOfNodes);
4622 0 : NS_ENSURE_SUCCESS(res, res);
4623 :
4624 : // if we don't have any nodes, or we have only a single br, then we are
4625 : // creating an empty alignment div. We have to do some different things for these.
4626 0 : bool emptyDiv = false;
4627 0 : PRInt32 listCount = arrayOfNodes.Count();
4628 0 : if (!listCount) emptyDiv = true;
4629 0 : if (listCount == 1)
4630 : {
4631 0 : nsCOMPtr<nsIDOMNode> theNode = arrayOfNodes[0];
4632 :
4633 0 : if (nsHTMLEditUtils::SupportsAlignAttr(theNode))
4634 : {
4635 : // the node is a table element, an horiz rule, a paragraph, a div
4636 : // or a section header; in HTML 4, it can directly carry the ALIGN
4637 : // attribute and we don't need to make a div! If we are in CSS mode,
4638 : // all the work is done in AlignBlock
4639 0 : nsCOMPtr<nsIDOMElement> theElem = do_QueryInterface(theNode);
4640 0 : res = AlignBlock(theElem, alignType, true);
4641 0 : NS_ENSURE_SUCCESS(res, res);
4642 0 : return NS_OK;
4643 : }
4644 :
4645 0 : if (nsTextEditUtils::IsBreak(theNode))
4646 : {
4647 : // The special case emptyDiv code (below) that consumes BRs can
4648 : // cause tables to split if the start node of the selection is
4649 : // not in a table cell or caption, for example parent is a <tr>.
4650 : // Avoid this unnecessary splitting if possible by leaving emptyDiv
4651 : // FALSE so that we fall through to the normal case alignment code.
4652 : //
4653 : // XXX: It seems a little error prone for the emptyDiv special
4654 : // case code to assume that the start node of the selection
4655 : // is the parent of the single node in the arrayOfNodes, as
4656 : // the paragraph above points out. Do we rely on the selection
4657 : // start node because of the fact that arrayOfNodes can be empty?
4658 : // We should probably revisit this issue. - kin
4659 :
4660 0 : nsCOMPtr<nsIDOMNode> parent;
4661 : PRInt32 offset;
4662 0 : res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(parent), &offset);
4663 :
4664 0 : if (!nsHTMLEditUtils::IsTableElement(parent) || nsHTMLEditUtils::IsTableCellOrCaption(parent))
4665 0 : emptyDiv = true;
4666 : }
4667 : }
4668 0 : if (emptyDiv)
4669 : {
4670 : PRInt32 offset;
4671 0 : nsCOMPtr<nsIDOMNode> brNode, parent, theDiv, sib;
4672 0 : NS_NAMED_LITERAL_STRING(divType, "div");
4673 0 : res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(parent), &offset);
4674 0 : NS_ENSURE_SUCCESS(res, res);
4675 0 : res = SplitAsNeeded(&divType, address_of(parent), &offset);
4676 0 : NS_ENSURE_SUCCESS(res, res);
4677 : // consume a trailing br, if any. This is to keep an alignment from
4678 : // creating extra lines, if possible.
4679 0 : res = mHTMLEditor->GetNextHTMLNode(parent, offset, address_of(brNode));
4680 0 : NS_ENSURE_SUCCESS(res, res);
4681 0 : if (brNode && nsTextEditUtils::IsBreak(brNode))
4682 : {
4683 : // making use of html structure... if next node after where
4684 : // we are putting our div is not a block, then the br we
4685 : // found is in same block we are, so its safe to consume it.
4686 0 : res = mHTMLEditor->GetNextHTMLSibling(parent, offset, address_of(sib));
4687 0 : NS_ENSURE_SUCCESS(res, res);
4688 0 : if (!IsBlockNode(sib))
4689 : {
4690 0 : res = mHTMLEditor->DeleteNode(brNode);
4691 0 : NS_ENSURE_SUCCESS(res, res);
4692 : }
4693 : }
4694 0 : res = mHTMLEditor->CreateNode(divType, parent, offset, getter_AddRefs(theDiv));
4695 0 : NS_ENSURE_SUCCESS(res, res);
4696 : // remember our new block for postprocessing
4697 0 : mNewBlock = theDiv;
4698 : // set up the alignment on the div, using HTML or CSS
4699 0 : nsCOMPtr<nsIDOMElement> divElem = do_QueryInterface(theDiv);
4700 0 : res = AlignBlock(divElem, alignType, true);
4701 0 : NS_ENSURE_SUCCESS(res, res);
4702 0 : *aHandled = true;
4703 : // put in a moz-br so that it won't get deleted
4704 0 : res = CreateMozBR(theDiv, 0, address_of(brNode));
4705 0 : NS_ENSURE_SUCCESS(res, res);
4706 0 : res = aSelection->Collapse(theDiv, 0);
4707 0 : selectionResetter.Abort(); // don't reset our selection in this case.
4708 0 : return res;
4709 : }
4710 :
4711 : // Next we detect all the transitions in the array, where a transition
4712 : // means that adjacent nodes in the array don't have the same parent.
4713 :
4714 0 : nsTArray<bool> transitionList;
4715 0 : res = MakeTransitionList(arrayOfNodes, transitionList);
4716 0 : NS_ENSURE_SUCCESS(res, res);
4717 :
4718 : // Ok, now go through all the nodes and give them an align attrib or put them in a div,
4719 : // or whatever is appropriate. Wohoo!
4720 :
4721 0 : nsCOMPtr<nsIDOMNode> curParent;
4722 0 : nsCOMPtr<nsIDOMNode> curDiv;
4723 0 : bool useCSS = mHTMLEditor->IsCSSEnabled();
4724 0 : for (PRInt32 i = 0; i < listCount; ++i) {
4725 : // here's where we actually figure out what to do
4726 0 : nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
4727 :
4728 : // Ignore all non-editable nodes. Leave them be.
4729 0 : if (!mHTMLEditor->IsEditable(curNode)) continue;
4730 :
4731 : PRInt32 offset;
4732 0 : res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset);
4733 0 : NS_ENSURE_SUCCESS(res, res);
4734 :
4735 : // the node is a table element, an horiz rule, a paragraph, a div
4736 : // or a section header; in HTML 4, it can directly carry the ALIGN
4737 : // attribute and we don't need to nest it, just set the alignment.
4738 : // In CSS, assign the corresponding CSS styles in AlignBlock
4739 0 : if (nsHTMLEditUtils::SupportsAlignAttr(curNode))
4740 : {
4741 0 : nsCOMPtr<nsIDOMElement> curElem = do_QueryInterface(curNode);
4742 0 : res = AlignBlock(curElem, alignType, false);
4743 0 : NS_ENSURE_SUCCESS(res, res);
4744 : // clear out curDiv so that we don't put nodes after this one into it
4745 0 : curDiv = 0;
4746 0 : continue;
4747 : }
4748 :
4749 : // Skip insignificant formatting text nodes to prevent
4750 : // unnecessary structure splitting!
4751 0 : if (nsEditor::IsTextNode(curNode) &&
4752 0 : ((nsHTMLEditUtils::IsTableElement(curParent) && !nsHTMLEditUtils::IsTableCellOrCaption(curParent)) ||
4753 0 : nsHTMLEditUtils::IsList(curParent)))
4754 0 : continue;
4755 :
4756 : // if it's a list item, or a list
4757 : // inside a list, forget any "current" div, and instead put divs inside
4758 : // the appropriate block (td, li, etc)
4759 0 : if ( nsHTMLEditUtils::IsListItem(curNode)
4760 0 : || nsHTMLEditUtils::IsList(curNode))
4761 : {
4762 0 : res = RemoveAlignment(curNode, *alignType, true);
4763 0 : NS_ENSURE_SUCCESS(res, res);
4764 0 : if (useCSS) {
4765 0 : nsCOMPtr<nsIDOMElement> curElem = do_QueryInterface(curNode);
4766 0 : NS_NAMED_LITERAL_STRING(attrName, "align");
4767 : PRInt32 count;
4768 : mHTMLEditor->mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(curNode, nsnull,
4769 : &attrName, alignType,
4770 0 : &count, false);
4771 0 : curDiv = 0;
4772 0 : continue;
4773 : }
4774 0 : else if (nsHTMLEditUtils::IsList(curParent)) {
4775 : // if we don't use CSS, add a contraint to list element : they have
4776 : // to be inside another list, ie >= second level of nesting
4777 0 : res = AlignInnerBlocks(curNode, alignType);
4778 0 : NS_ENSURE_SUCCESS(res, res);
4779 0 : curDiv = 0;
4780 0 : continue;
4781 : }
4782 : // clear out curDiv so that we don't put nodes after this one into it
4783 : }
4784 :
4785 : // need to make a div to put things in if we haven't already,
4786 : // or if this node doesn't go in div we used earlier.
4787 0 : if (!curDiv || transitionList[i])
4788 : {
4789 : // First, check that our element can contain a div.
4790 0 : NS_NAMED_LITERAL_STRING(divType, "div");
4791 0 : if (!mEditor->CanContainTag(curParent, divType))
4792 0 : return NS_OK; // cancelled
4793 :
4794 0 : res = SplitAsNeeded(&divType, address_of(curParent), &offset);
4795 0 : NS_ENSURE_SUCCESS(res, res);
4796 0 : res = mHTMLEditor->CreateNode(divType, curParent, offset, getter_AddRefs(curDiv));
4797 0 : NS_ENSURE_SUCCESS(res, res);
4798 : // remember our new block for postprocessing
4799 0 : mNewBlock = curDiv;
4800 : // set up the alignment on the div
4801 0 : nsCOMPtr<nsIDOMElement> divElem = do_QueryInterface(curDiv);
4802 0 : res = AlignBlock(divElem, alignType, true);
4803 : //nsAutoString attr(NS_LITERAL_STRING("align"));
4804 : //res = mHTMLEditor->SetAttribute(divElem, attr, *alignType);
4805 : //NS_ENSURE_SUCCESS(res, res);
4806 : // curDiv is now the correct thing to put curNode in
4807 : }
4808 :
4809 : // tuck the node into the end of the active div
4810 0 : res = mHTMLEditor->MoveNode(curNode, curDiv, -1);
4811 0 : NS_ENSURE_SUCCESS(res, res);
4812 : }
4813 :
4814 0 : return res;
4815 : }
4816 :
4817 :
4818 : ///////////////////////////////////////////////////////////////////////////
4819 : // AlignInnerBlocks: align inside table cells or list items
4820 : //
4821 : nsresult
4822 0 : nsHTMLEditRules::AlignInnerBlocks(nsIDOMNode *aNode, const nsAString *alignType)
4823 : {
4824 0 : NS_ENSURE_TRUE(aNode && alignType, NS_ERROR_NULL_POINTER);
4825 : nsresult res;
4826 :
4827 : // gather list of table cells or list items
4828 0 : nsCOMArray<nsIDOMNode> arrayOfNodes;
4829 0 : nsTableCellAndListItemFunctor functor;
4830 0 : nsDOMIterator iter;
4831 0 : res = iter.Init(aNode);
4832 0 : NS_ENSURE_SUCCESS(res, res);
4833 0 : res = iter.AppendList(functor, arrayOfNodes);
4834 0 : NS_ENSURE_SUCCESS(res, res);
4835 :
4836 : // now that we have the list, align their contents as requested
4837 0 : PRInt32 listCount = arrayOfNodes.Count();
4838 : PRInt32 j;
4839 :
4840 0 : for (j = 0; j < listCount; j++)
4841 : {
4842 0 : nsIDOMNode* node = arrayOfNodes[0];
4843 0 : res = AlignBlockContents(node, alignType);
4844 0 : NS_ENSURE_SUCCESS(res, res);
4845 0 : arrayOfNodes.RemoveObjectAt(0);
4846 : }
4847 :
4848 0 : return res;
4849 : }
4850 :
4851 :
4852 : ///////////////////////////////////////////////////////////////////////////
4853 : // AlignBlockContents: align contents of a block element
4854 : //
4855 : nsresult
4856 0 : nsHTMLEditRules::AlignBlockContents(nsIDOMNode *aNode, const nsAString *alignType)
4857 : {
4858 0 : NS_ENSURE_TRUE(aNode && alignType, NS_ERROR_NULL_POINTER);
4859 : nsresult res;
4860 0 : nsCOMPtr <nsIDOMNode> firstChild, lastChild, divNode;
4861 :
4862 0 : bool useCSS = mHTMLEditor->IsCSSEnabled();
4863 :
4864 0 : res = mHTMLEditor->GetFirstEditableChild(aNode, address_of(firstChild));
4865 0 : NS_ENSURE_SUCCESS(res, res);
4866 0 : res = mHTMLEditor->GetLastEditableChild(aNode, address_of(lastChild));
4867 0 : NS_ENSURE_SUCCESS(res, res);
4868 0 : NS_NAMED_LITERAL_STRING(attr, "align");
4869 0 : if (!firstChild)
4870 : {
4871 : // this cell has no content, nothing to align
4872 : }
4873 0 : else if ((firstChild==lastChild) && nsHTMLEditUtils::IsDiv(firstChild))
4874 : {
4875 : // the cell already has a div containing all of its content: just
4876 : // act on this div.
4877 0 : nsCOMPtr<nsIDOMElement> divElem = do_QueryInterface(firstChild);
4878 0 : if (useCSS) {
4879 0 : res = mHTMLEditor->SetAttributeOrEquivalent(divElem, attr, *alignType, false);
4880 : }
4881 : else {
4882 0 : res = mHTMLEditor->SetAttribute(divElem, attr, *alignType);
4883 : }
4884 0 : NS_ENSURE_SUCCESS(res, res);
4885 : }
4886 : else
4887 : {
4888 : // else we need to put in a div, set the alignment, and toss in all the children
4889 0 : res = mHTMLEditor->CreateNode(NS_LITERAL_STRING("div"), aNode, 0, getter_AddRefs(divNode));
4890 0 : NS_ENSURE_SUCCESS(res, res);
4891 : // set up the alignment on the div
4892 0 : nsCOMPtr<nsIDOMElement> divElem = do_QueryInterface(divNode);
4893 0 : if (useCSS) {
4894 0 : res = mHTMLEditor->SetAttributeOrEquivalent(divElem, attr, *alignType, false);
4895 : }
4896 : else {
4897 0 : res = mHTMLEditor->SetAttribute(divElem, attr, *alignType);
4898 : }
4899 0 : NS_ENSURE_SUCCESS(res, res);
4900 : // tuck the children into the end of the active div
4901 0 : while (lastChild && (lastChild != divNode))
4902 : {
4903 0 : res = mHTMLEditor->MoveNode(lastChild, divNode, 0);
4904 0 : NS_ENSURE_SUCCESS(res, res);
4905 0 : res = mHTMLEditor->GetLastEditableChild(aNode, address_of(lastChild));
4906 0 : NS_ENSURE_SUCCESS(res, res);
4907 : }
4908 : }
4909 0 : return res;
4910 : }
4911 :
4912 : ///////////////////////////////////////////////////////////////////////////
4913 : // CheckForEmptyBlock: Called by WillDeleteSelection to detect and handle
4914 : // case of deleting from inside an empty block.
4915 : //
4916 : nsresult
4917 0 : nsHTMLEditRules::CheckForEmptyBlock(nsIDOMNode *aStartNode,
4918 : nsIDOMNode *aBodyNode,
4919 : nsISelection *aSelection,
4920 : bool *aHandled)
4921 : {
4922 : // If the editing host is an inline element, bail out early.
4923 0 : if (IsInlineNode(aBodyNode)) {
4924 0 : return NS_OK;
4925 : }
4926 : // if we are inside an empty block, delete it.
4927 : // Note: do NOT delete table elements this way.
4928 0 : nsresult res = NS_OK;
4929 0 : nsCOMPtr<nsIDOMNode> block, emptyBlock;
4930 0 : if (IsBlockNode(aStartNode))
4931 0 : block = aStartNode;
4932 : else
4933 0 : block = mHTMLEditor->GetBlockNodeParent(aStartNode);
4934 : bool bIsEmptyNode;
4935 0 : if (block != aBodyNode) // efficiency hack. avoiding IsEmptyNode() call when in body
4936 : {
4937 0 : res = mHTMLEditor->IsEmptyNode(block, &bIsEmptyNode, true, false);
4938 0 : NS_ENSURE_SUCCESS(res, res);
4939 0 : while (bIsEmptyNode && !nsHTMLEditUtils::IsTableElement(block) && (block != aBodyNode))
4940 : {
4941 0 : emptyBlock = block;
4942 0 : block = mHTMLEditor->GetBlockNodeParent(emptyBlock);
4943 0 : res = mHTMLEditor->IsEmptyNode(block, &bIsEmptyNode, true, false);
4944 0 : NS_ENSURE_SUCCESS(res, res);
4945 : }
4946 : }
4947 :
4948 0 : nsCOMPtr<nsIContent> emptyContent = do_QueryInterface(emptyBlock);
4949 0 : if (emptyBlock && emptyContent->IsEditable())
4950 : {
4951 0 : nsCOMPtr<nsIDOMNode> blockParent;
4952 : PRInt32 offset;
4953 0 : res = nsEditor::GetNodeLocation(emptyBlock, address_of(blockParent), &offset);
4954 0 : NS_ENSURE_SUCCESS(res, res);
4955 0 : NS_ENSURE_TRUE(blockParent && offset >= 0, NS_ERROR_FAILURE);
4956 :
4957 0 : if (nsHTMLEditUtils::IsListItem(emptyBlock))
4958 : {
4959 : // are we the first list item in the list?
4960 : bool bIsFirst;
4961 0 : res = mHTMLEditor->IsFirstEditableChild(emptyBlock, &bIsFirst);
4962 0 : NS_ENSURE_SUCCESS(res, res);
4963 0 : if (bIsFirst)
4964 : {
4965 0 : nsCOMPtr<nsIDOMNode> listParent;
4966 : PRInt32 listOffset;
4967 0 : res = nsEditor::GetNodeLocation(blockParent, address_of(listParent), &listOffset);
4968 0 : NS_ENSURE_SUCCESS(res, res);
4969 0 : NS_ENSURE_TRUE(listParent && listOffset >= 0, NS_ERROR_FAILURE);
4970 : // if we are a sublist, skip the br creation
4971 0 : if (!nsHTMLEditUtils::IsList(listParent))
4972 : {
4973 : // create a br before list
4974 0 : nsCOMPtr<nsIDOMNode> brNode;
4975 0 : res = mHTMLEditor->CreateBR(listParent, listOffset, address_of(brNode));
4976 0 : NS_ENSURE_SUCCESS(res, res);
4977 : // adjust selection to be right before it
4978 0 : res = aSelection->Collapse(listParent, listOffset);
4979 0 : NS_ENSURE_SUCCESS(res, res);
4980 : }
4981 : // else just let selection perculate up. We'll adjust it in AfterEdit()
4982 : }
4983 : }
4984 : else
4985 : {
4986 : // adjust selection to be right after it
4987 0 : res = aSelection->Collapse(blockParent, offset+1);
4988 0 : NS_ENSURE_SUCCESS(res, res);
4989 : }
4990 0 : res = mHTMLEditor->DeleteNode(emptyBlock);
4991 0 : *aHandled = true;
4992 : }
4993 0 : return res;
4994 : }
4995 :
4996 : nsresult
4997 0 : nsHTMLEditRules::CheckForInvisibleBR(nsIDOMNode *aBlock,
4998 : BRLocation aWhere,
4999 : nsCOMPtr<nsIDOMNode> *outBRNode,
5000 : PRInt32 aOffset)
5001 : {
5002 0 : NS_ENSURE_TRUE(aBlock && outBRNode, NS_ERROR_NULL_POINTER);
5003 0 : *outBRNode = nsnull;
5004 :
5005 0 : nsCOMPtr<nsIDOMNode> testNode;
5006 0 : PRInt32 testOffset = 0;
5007 0 : bool runTest = false;
5008 :
5009 0 : if (aWhere == kBlockEnd)
5010 : {
5011 : nsCOMPtr<nsIDOMNode> rightmostNode =
5012 0 : mHTMLEditor->GetRightmostChild(aBlock, true); // no block crossing
5013 :
5014 0 : if (rightmostNode)
5015 : {
5016 0 : nsCOMPtr<nsIDOMNode> nodeParent;
5017 : PRInt32 nodeOffset;
5018 :
5019 0 : if (NS_SUCCEEDED(nsEditor::GetNodeLocation(rightmostNode,
5020 : address_of(nodeParent),
5021 : &nodeOffset)))
5022 : {
5023 0 : runTest = true;
5024 0 : testNode = nodeParent;
5025 : // use offset + 1, because we want the last node included in our evaluation
5026 0 : testOffset = nodeOffset + 1;
5027 : }
5028 : }
5029 : }
5030 0 : else if (aOffset)
5031 : {
5032 0 : runTest = true;
5033 0 : testNode = aBlock;
5034 : // we'll check everything to the left of the input position
5035 0 : testOffset = aOffset;
5036 : }
5037 :
5038 0 : if (runTest)
5039 : {
5040 0 : nsWSRunObject wsTester(mHTMLEditor, testNode, testOffset);
5041 0 : if (nsWSRunObject::eBreak == wsTester.mStartReason)
5042 : {
5043 0 : *outBRNode = wsTester.mStartReasonNode;
5044 : }
5045 : }
5046 :
5047 0 : return NS_OK;
5048 : }
5049 :
5050 :
5051 : ///////////////////////////////////////////////////////////////////////////
5052 : // GetInnerContent: aList and aTbl allow the caller to specify what kind
5053 : // of content to "look inside". If aTbl is true, look inside
5054 : // any table content, and insert the inner content into the
5055 : // supplied issupportsarray at offset aIndex.
5056 : // Similarly with aList and list content.
5057 : // aIndex is updated to point past inserted elements.
5058 : //
5059 : nsresult
5060 0 : nsHTMLEditRules::GetInnerContent(nsIDOMNode *aNode, nsCOMArray<nsIDOMNode> &outArrayOfNodes,
5061 : PRInt32 *aIndex, bool aList, bool aTbl)
5062 : {
5063 0 : NS_ENSURE_TRUE(aNode && aIndex, NS_ERROR_NULL_POINTER);
5064 :
5065 0 : nsCOMPtr<nsIDOMNode> node;
5066 :
5067 0 : nsresult res = mHTMLEditor->GetFirstEditableChild(aNode, address_of(node));
5068 0 : while (NS_SUCCEEDED(res) && node)
5069 : {
5070 0 : if ( ( aList && (nsHTMLEditUtils::IsList(node) ||
5071 0 : nsHTMLEditUtils::IsListItem(node) ) )
5072 0 : || ( aTbl && nsHTMLEditUtils::IsTableElement(node) ) )
5073 : {
5074 0 : res = GetInnerContent(node, outArrayOfNodes, aIndex, aList, aTbl);
5075 0 : NS_ENSURE_SUCCESS(res, res);
5076 : }
5077 : else
5078 : {
5079 0 : outArrayOfNodes.InsertObjectAt(node, *aIndex);
5080 0 : (*aIndex)++;
5081 : }
5082 0 : nsCOMPtr<nsIDOMNode> tmp;
5083 0 : res = node->GetNextSibling(getter_AddRefs(tmp));
5084 0 : node = tmp;
5085 : }
5086 :
5087 0 : return res;
5088 : }
5089 :
5090 : ///////////////////////////////////////////////////////////////////////////
5091 : // ExpandSelectionForDeletion: this promotes our selection to include blocks
5092 : // that have all their children selected.
5093 : //
5094 : nsresult
5095 0 : nsHTMLEditRules::ExpandSelectionForDeletion(nsISelection *aSelection)
5096 : {
5097 0 : NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
5098 :
5099 : // don't need to touch collapsed selections
5100 : bool bCollapsed;
5101 0 : nsresult res = aSelection->GetIsCollapsed(&bCollapsed);
5102 0 : NS_ENSURE_SUCCESS(res, res);
5103 0 : if (bCollapsed) return res;
5104 :
5105 : PRInt32 rangeCount;
5106 0 : res = aSelection->GetRangeCount(&rangeCount);
5107 0 : NS_ENSURE_SUCCESS(res, res);
5108 :
5109 : // we don't need to mess with cell selections, and we assume multirange selections are those.
5110 0 : if (rangeCount != 1) return NS_OK;
5111 :
5112 : // find current sel start and end
5113 0 : nsCOMPtr<nsIDOMRange> range;
5114 0 : res = aSelection->GetRangeAt(0, getter_AddRefs(range));
5115 0 : NS_ENSURE_SUCCESS(res, res);
5116 0 : NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER);
5117 0 : nsCOMPtr<nsIDOMNode> selStartNode, selEndNode, selCommon;
5118 : PRInt32 selStartOffset, selEndOffset;
5119 :
5120 0 : res = range->GetStartContainer(getter_AddRefs(selStartNode));
5121 0 : NS_ENSURE_SUCCESS(res, res);
5122 0 : res = range->GetStartOffset(&selStartOffset);
5123 0 : NS_ENSURE_SUCCESS(res, res);
5124 0 : res = range->GetEndContainer(getter_AddRefs(selEndNode));
5125 0 : NS_ENSURE_SUCCESS(res, res);
5126 0 : res = range->GetEndOffset(&selEndOffset);
5127 0 : NS_ENSURE_SUCCESS(res, res);
5128 :
5129 : // find current selection common block parent
5130 0 : res = range->GetCommonAncestorContainer(getter_AddRefs(selCommon));
5131 0 : NS_ENSURE_SUCCESS(res, res);
5132 0 : if (!IsBlockNode(selCommon))
5133 0 : selCommon = nsHTMLEditor::GetBlockNodeParent(selCommon);
5134 :
5135 : // set up for loops and cache our root element
5136 0 : bool stillLooking = true;
5137 0 : nsCOMPtr<nsIDOMNode> visNode, firstBRParent;
5138 0 : PRInt32 visOffset=0, firstBROffset=0;
5139 : PRInt16 wsType;
5140 0 : nsCOMPtr<nsIContent> rootContent = mHTMLEditor->GetActiveEditingHost();
5141 0 : nsCOMPtr<nsIDOMNode> rootElement = do_QueryInterface(rootContent);
5142 0 : NS_ENSURE_TRUE(rootElement, NS_ERROR_FAILURE);
5143 :
5144 : // find previous visible thingy before start of selection
5145 0 : if ((selStartNode!=selCommon) && (selStartNode!=rootElement))
5146 : {
5147 0 : while (stillLooking)
5148 : {
5149 0 : nsWSRunObject wsObj(mHTMLEditor, selStartNode, selStartOffset);
5150 0 : res = wsObj.PriorVisibleNode(selStartNode, selStartOffset, address_of(visNode), &visOffset, &wsType);
5151 0 : NS_ENSURE_SUCCESS(res, res);
5152 0 : if (wsType == nsWSRunObject::eThisBlock)
5153 : {
5154 : // we want to keep looking up. But stop if we are crossing table element
5155 : // boundaries, or if we hit the root.
5156 0 : if ( nsHTMLEditUtils::IsTableElement(wsObj.mStartReasonNode) ||
5157 0 : (selCommon == wsObj.mStartReasonNode) ||
5158 0 : (rootElement == wsObj.mStartReasonNode) )
5159 : {
5160 0 : stillLooking = false;
5161 : }
5162 : else
5163 : {
5164 0 : nsEditor::GetNodeLocation(wsObj.mStartReasonNode, address_of(selStartNode), &selStartOffset);
5165 : }
5166 : }
5167 : else
5168 : {
5169 0 : stillLooking = false;
5170 : }
5171 : }
5172 : }
5173 :
5174 0 : stillLooking = true;
5175 : // find next visible thingy after end of selection
5176 0 : if ((selEndNode!=selCommon) && (selEndNode!=rootElement))
5177 : {
5178 0 : while (stillLooking)
5179 : {
5180 0 : nsWSRunObject wsObj(mHTMLEditor, selEndNode, selEndOffset);
5181 0 : res = wsObj.NextVisibleNode(selEndNode, selEndOffset, address_of(visNode), &visOffset, &wsType);
5182 0 : NS_ENSURE_SUCCESS(res, res);
5183 0 : if (wsType == nsWSRunObject::eBreak)
5184 : {
5185 0 : if (mHTMLEditor->IsVisBreak(wsObj.mEndReasonNode))
5186 : {
5187 0 : stillLooking = false;
5188 : }
5189 : else
5190 : {
5191 0 : if (!firstBRParent)
5192 : {
5193 0 : firstBRParent = selEndNode;
5194 0 : firstBROffset = selEndOffset;
5195 : }
5196 0 : nsEditor::GetNodeLocation(wsObj.mEndReasonNode, address_of(selEndNode), &selEndOffset);
5197 0 : ++selEndOffset;
5198 : }
5199 : }
5200 0 : else if (wsType == nsWSRunObject::eThisBlock)
5201 : {
5202 : // we want to keep looking up. But stop if we are crossing table element
5203 : // boundaries, or if we hit the root.
5204 0 : if ( nsHTMLEditUtils::IsTableElement(wsObj.mEndReasonNode) ||
5205 0 : (selCommon == wsObj.mEndReasonNode) ||
5206 0 : (rootElement == wsObj.mEndReasonNode) )
5207 : {
5208 0 : stillLooking = false;
5209 : }
5210 : else
5211 : {
5212 0 : nsEditor::GetNodeLocation(wsObj.mEndReasonNode, address_of(selEndNode), &selEndOffset);
5213 0 : ++selEndOffset;
5214 : }
5215 : }
5216 : else
5217 : {
5218 0 : stillLooking = false;
5219 : }
5220 : }
5221 : }
5222 : // now set the selection to the new range
5223 0 : aSelection->Collapse(selStartNode, selStartOffset);
5224 :
5225 : // expand selection endpoint only if we didnt pass a br,
5226 : // or if we really needed to pass that br (ie, its block is now
5227 : // totally selected)
5228 0 : bool doEndExpansion = true;
5229 0 : if (firstBRParent)
5230 : {
5231 : // find block node containing br
5232 0 : nsCOMPtr<nsIDOMNode> brBlock = firstBRParent;
5233 0 : if (!IsBlockNode(brBlock))
5234 0 : brBlock = nsHTMLEditor::GetBlockNodeParent(brBlock);
5235 0 : bool nodeBefore=false, nodeAfter=false;
5236 :
5237 : // create a range that represents expanded selection
5238 0 : nsRefPtr<nsRange> range = new nsRange();
5239 0 : res = range->SetStart(selStartNode, selStartOffset);
5240 0 : NS_ENSURE_SUCCESS(res, res);
5241 0 : res = range->SetEnd(selEndNode, selEndOffset);
5242 0 : NS_ENSURE_SUCCESS(res, res);
5243 :
5244 : // check if block is entirely inside range
5245 0 : nsCOMPtr<nsIContent> brContentBlock = do_QueryInterface(brBlock);
5246 0 : res = nsRange::CompareNodeToRange(brContentBlock, range, &nodeBefore, &nodeAfter);
5247 :
5248 : // if block isn't contained, forgo grabbing the br in the expanded selection
5249 0 : if (nodeBefore || nodeAfter)
5250 0 : doEndExpansion = false;
5251 : }
5252 0 : if (doEndExpansion)
5253 : {
5254 0 : res = aSelection->Extend(selEndNode, selEndOffset);
5255 : }
5256 : else
5257 : {
5258 : // only expand to just before br
5259 0 : res = aSelection->Extend(firstBRParent, firstBROffset);
5260 : }
5261 :
5262 0 : return res;
5263 : }
5264 :
5265 : #ifdef XXX_DEAD_CODE
5266 : ///////////////////////////////////////////////////////////////////////////
5267 : // AtStartOfBlock: is node/offset at the start of the editable material in this block?
5268 : //
5269 : bool
5270 : nsHTMLEditRules::AtStartOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aBlock)
5271 : {
5272 : nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(aNode);
5273 : if (nodeAsText && aOffset) return false; // there are chars in front of us
5274 :
5275 : nsCOMPtr<nsIDOMNode> priorNode;
5276 : nsresult res = mHTMLEditor->GetPriorHTMLNode(aNode, aOffset, address_of(priorNode));
5277 : NS_ENSURE_SUCCESS(res, true);
5278 : NS_ENSURE_TRUE(priorNode, true);
5279 : nsCOMPtr<nsIDOMNode> blockParent = mHTMLEditor->GetBlockNodeParent(priorNode);
5280 : if (blockParent && (blockParent == aBlock)) return false;
5281 : return true;
5282 : }
5283 :
5284 :
5285 : ///////////////////////////////////////////////////////////////////////////
5286 : // AtEndOfBlock: is node/offset at the end of the editable material in this block?
5287 : //
5288 : bool
5289 : nsHTMLEditRules::AtEndOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aBlock)
5290 : {
5291 : nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(aNode);
5292 : if (nodeAsText)
5293 : {
5294 : PRUint32 strLength;
5295 : nodeAsText->GetLength(&strLength);
5296 : if ((PRInt32)strLength > aOffset) return false; // there are chars in after us
5297 : }
5298 : nsCOMPtr<nsIDOMNode> nextNode;
5299 : nsresult res = mHTMLEditor->GetNextHTMLNode(aNode, aOffset, address_of(nextNode));
5300 : NS_ENSURE_SUCCESS(res, true);
5301 : NS_ENSURE_TRUE(nextNode, true);
5302 : nsCOMPtr<nsIDOMNode> blockParent = mHTMLEditor->GetBlockNodeParent(nextNode);
5303 : if (blockParent && (blockParent == aBlock)) return false;
5304 : return true;
5305 : }
5306 :
5307 :
5308 : ///////////////////////////////////////////////////////////////////////////
5309 : // CreateMozDiv: makes a div with type = _moz
5310 : //
5311 : nsresult
5312 : nsHTMLEditRules::CreateMozDiv(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr<nsIDOMNode> *outDiv)
5313 : {
5314 : NS_ENSURE_TRUE(inParent && outDiv, NS_ERROR_NULL_POINTER);
5315 : nsAutoString divType= "div";
5316 : *outDiv = nsnull;
5317 : nsresult res = mHTMLEditor->CreateNode(divType, inParent, inOffset, getter_AddRefs(*outDiv));
5318 : NS_ENSURE_SUCCESS(res, res);
5319 : // give it special moz attr
5320 : nsCOMPtr<nsIDOMElement> mozDivElem = do_QueryInterface(*outDiv);
5321 : res = mHTMLEditor->SetAttribute(mozDivElem, "type", "_moz");
5322 : NS_ENSURE_SUCCESS(res, res);
5323 : res = AddTrailerBR(*outDiv);
5324 : return res;
5325 : }
5326 : #endif
5327 :
5328 :
5329 : ///////////////////////////////////////////////////////////////////////////
5330 : // NormalizeSelection: tweak non-collapsed selections to be more "natural".
5331 : // Idea here is to adjust selection endpoint so that they do not cross
5332 : // breaks or block boundaries unless something editable beyond that boundary
5333 : // is also selected. This adjustment makes it much easier for the various
5334 : // block operations to determine what nodes to act on.
5335 : //
5336 : nsresult
5337 0 : nsHTMLEditRules::NormalizeSelection(nsISelection *inSelection)
5338 : {
5339 0 : NS_ENSURE_TRUE(inSelection, NS_ERROR_NULL_POINTER);
5340 :
5341 : // don't need to touch collapsed selections
5342 : bool bCollapsed;
5343 0 : nsresult res = inSelection->GetIsCollapsed(&bCollapsed);
5344 0 : NS_ENSURE_SUCCESS(res, res);
5345 0 : if (bCollapsed) return res;
5346 :
5347 : PRInt32 rangeCount;
5348 0 : res = inSelection->GetRangeCount(&rangeCount);
5349 0 : NS_ENSURE_SUCCESS(res, res);
5350 :
5351 : // we don't need to mess with cell selections, and we assume multirange selections are those.
5352 0 : if (rangeCount != 1) return NS_OK;
5353 :
5354 0 : nsCOMPtr<nsIDOMRange> range;
5355 0 : res = inSelection->GetRangeAt(0, getter_AddRefs(range));
5356 0 : NS_ENSURE_SUCCESS(res, res);
5357 0 : NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER);
5358 0 : nsCOMPtr<nsIDOMNode> startNode, endNode;
5359 : PRInt32 startOffset, endOffset;
5360 0 : nsCOMPtr<nsIDOMNode> newStartNode, newEndNode;
5361 : PRInt32 newStartOffset, newEndOffset;
5362 :
5363 0 : res = range->GetStartContainer(getter_AddRefs(startNode));
5364 0 : NS_ENSURE_SUCCESS(res, res);
5365 0 : res = range->GetStartOffset(&startOffset);
5366 0 : NS_ENSURE_SUCCESS(res, res);
5367 0 : res = range->GetEndContainer(getter_AddRefs(endNode));
5368 0 : NS_ENSURE_SUCCESS(res, res);
5369 0 : res = range->GetEndOffset(&endOffset);
5370 0 : NS_ENSURE_SUCCESS(res, res);
5371 :
5372 : // adjusted values default to original values
5373 0 : newStartNode = startNode;
5374 0 : newStartOffset = startOffset;
5375 0 : newEndNode = endNode;
5376 0 : newEndOffset = endOffset;
5377 :
5378 : // some locals we need for whitespace code
5379 0 : nsCOMPtr<nsIDOMNode> someNode;
5380 : PRInt32 offset;
5381 : PRInt16 wsType;
5382 :
5383 : // let the whitespace code do the heavy lifting
5384 0 : nsWSRunObject wsEndObj(mHTMLEditor, endNode, endOffset);
5385 : // is there any intervening visible whitespace? if so we can't push selection past that,
5386 : // it would visibly change maening of users selection
5387 0 : res = wsEndObj.PriorVisibleNode(endNode, endOffset, address_of(someNode), &offset, &wsType);
5388 0 : NS_ENSURE_SUCCESS(res, res);
5389 0 : if ((wsType != nsWSRunObject::eText) && (wsType != nsWSRunObject::eNormalWS))
5390 : {
5391 : // eThisBlock and eOtherBlock conveniently distinquish cases
5392 : // of going "down" into a block and "up" out of a block.
5393 0 : if (wsEndObj.mStartReason == nsWSRunObject::eOtherBlock)
5394 : {
5395 : // endpoint is just after the close of a block.
5396 0 : nsCOMPtr<nsIDOMNode> child = mHTMLEditor->GetRightmostChild(wsEndObj.mStartReasonNode, true);
5397 0 : if (child)
5398 : {
5399 0 : res = nsEditor::GetNodeLocation(child, address_of(newEndNode), &newEndOffset);
5400 0 : NS_ENSURE_SUCCESS(res, res);
5401 0 : ++newEndOffset; // offset *after* child
5402 : }
5403 : // else block is empty - we can leave selection alone here, i think.
5404 : }
5405 0 : else if (wsEndObj.mStartReason == nsWSRunObject::eThisBlock)
5406 : {
5407 : // endpoint is just after start of this block
5408 0 : nsCOMPtr<nsIDOMNode> child;
5409 0 : res = mHTMLEditor->GetPriorHTMLNode(endNode, endOffset, address_of(child));
5410 0 : if (child)
5411 : {
5412 0 : res = nsEditor::GetNodeLocation(child, address_of(newEndNode), &newEndOffset);
5413 0 : NS_ENSURE_SUCCESS(res, res);
5414 0 : ++newEndOffset; // offset *after* child
5415 : }
5416 : // else block is empty - we can leave selection alone here, i think.
5417 : }
5418 0 : else if (wsEndObj.mStartReason == nsWSRunObject::eBreak)
5419 : {
5420 : // endpoint is just after break. lets adjust it to before it.
5421 0 : res = nsEditor::GetNodeLocation(wsEndObj.mStartReasonNode, address_of(newEndNode), &newEndOffset);
5422 0 : NS_ENSURE_SUCCESS(res, res);
5423 : }
5424 : }
5425 :
5426 :
5427 : // similar dealio for start of range
5428 0 : nsWSRunObject wsStartObj(mHTMLEditor, startNode, startOffset);
5429 : // is there any intervening visible whitespace? if so we can't push selection past that,
5430 : // it would visibly change maening of users selection
5431 0 : res = wsStartObj.NextVisibleNode(startNode, startOffset, address_of(someNode), &offset, &wsType);
5432 0 : NS_ENSURE_SUCCESS(res, res);
5433 0 : if ((wsType != nsWSRunObject::eText) && (wsType != nsWSRunObject::eNormalWS))
5434 : {
5435 : // eThisBlock and eOtherBlock conveniently distinquish cases
5436 : // of going "down" into a block and "up" out of a block.
5437 0 : if (wsStartObj.mEndReason == nsWSRunObject::eOtherBlock)
5438 : {
5439 : // startpoint is just before the start of a block.
5440 0 : nsCOMPtr<nsIDOMNode> child = mHTMLEditor->GetLeftmostChild(wsStartObj.mEndReasonNode, true);
5441 0 : if (child)
5442 : {
5443 0 : res = nsEditor::GetNodeLocation(child, address_of(newStartNode), &newStartOffset);
5444 0 : NS_ENSURE_SUCCESS(res, res);
5445 : }
5446 : // else block is empty - we can leave selection alone here, i think.
5447 : }
5448 0 : else if (wsStartObj.mEndReason == nsWSRunObject::eThisBlock)
5449 : {
5450 : // startpoint is just before end of this block
5451 0 : nsCOMPtr<nsIDOMNode> child;
5452 0 : res = mHTMLEditor->GetNextHTMLNode(startNode, startOffset, address_of(child));
5453 0 : if (child)
5454 : {
5455 0 : res = nsEditor::GetNodeLocation(child, address_of(newStartNode), &newStartOffset);
5456 0 : NS_ENSURE_SUCCESS(res, res);
5457 : }
5458 : // else block is empty - we can leave selection alone here, i think.
5459 : }
5460 0 : else if (wsStartObj.mEndReason == nsWSRunObject::eBreak)
5461 : {
5462 : // startpoint is just before a break. lets adjust it to after it.
5463 0 : res = nsEditor::GetNodeLocation(wsStartObj.mEndReasonNode, address_of(newStartNode), &newStartOffset);
5464 0 : NS_ENSURE_SUCCESS(res, res);
5465 0 : ++newStartOffset; // offset *after* break
5466 : }
5467 : }
5468 :
5469 : // there is a demented possiblity we have to check for. We might have a very strange selection
5470 : // that is not collapsed and yet does not contain any editable content, and satisfies some of the
5471 : // above conditions that cause tweaking. In this case we don't want to tweak the selection into
5472 : // a block it was never in, etc. There are a variety of strategies one might use to try to
5473 : // detect these cases, but I think the most straightforward is to see if the adjusted locations
5474 : // "cross" the old values: ie, new end before old start, or new start after old end. If so
5475 : // then just leave things alone.
5476 :
5477 : PRInt16 comp;
5478 : comp = nsContentUtils::ComparePoints(startNode, startOffset,
5479 0 : newEndNode, newEndOffset);
5480 0 : if (comp == 1) return NS_OK; // new end before old start
5481 : comp = nsContentUtils::ComparePoints(newStartNode, newStartOffset,
5482 0 : endNode, endOffset);
5483 0 : if (comp == 1) return NS_OK; // new start after old end
5484 :
5485 : // otherwise set selection to new values.
5486 0 : inSelection->Collapse(newStartNode, newStartOffset);
5487 0 : inSelection->Extend(newEndNode, newEndOffset);
5488 0 : return NS_OK;
5489 : }
5490 :
5491 :
5492 : ///////////////////////////////////////////////////////////////////////////
5493 : // GetPromotedPoint: figure out where a start or end point for a block
5494 : // operation really is
5495 : nsresult
5496 0 : nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt32 aOffset,
5497 : PRInt32 actionID, nsCOMPtr<nsIDOMNode> *outNode, PRInt32 *outOffset)
5498 : {
5499 0 : nsresult res = NS_OK;
5500 0 : nsCOMPtr<nsIDOMNode> nearNode, node = aNode;
5501 0 : nsCOMPtr<nsIDOMNode> parent = aNode;
5502 0 : PRInt32 pOffset, offset = aOffset;
5503 :
5504 : // default values
5505 0 : *outNode = node;
5506 0 : *outOffset = offset;
5507 :
5508 : // we do one thing for text actions, something else entirely for other actions
5509 0 : if (actionID == nsEditor::kOpInsertText ||
5510 : actionID == nsEditor::kOpInsertIMEText ||
5511 : actionID == nsEditor::kOpInsertBreak ||
5512 : actionID == nsEditor::kOpDeleteText)
5513 : {
5514 : bool isSpace, isNBSP;
5515 0 : nsCOMPtr<nsIDOMNode> temp;
5516 : // for text actions, we want to look backwards (or forwards, as appropriate)
5517 : // for additional whitespace or nbsp's. We may have to act on these later even though
5518 : // they are outside of the initial selection. Even if they are in another node!
5519 0 : if (aWhere == kStart)
5520 : {
5521 0 : do
5522 : {
5523 : PRInt32 prevOffset;
5524 0 : res = mHTMLEditor->IsPrevCharWhitespace(node, offset, &isSpace, &isNBSP, address_of(temp), &prevOffset);
5525 0 : NS_ENSURE_SUCCESS(res, res);
5526 0 : if (isSpace || isNBSP) {
5527 0 : node = temp;
5528 0 : offset = prevOffset;
5529 : } else {
5530 0 : break;
5531 : }
5532 0 : } while (node);
5533 :
5534 0 : *outNode = node;
5535 0 : *outOffset = offset;
5536 : }
5537 0 : else if (aWhere == kEnd)
5538 : {
5539 0 : do
5540 : {
5541 : PRInt32 nextOffset;
5542 0 : res = mHTMLEditor->IsNextCharWhitespace(node, offset, &isSpace, &isNBSP, address_of(temp), &nextOffset);
5543 0 : NS_ENSURE_SUCCESS(res, res);
5544 0 : if (isSpace || isNBSP) {
5545 0 : node = temp;
5546 0 : offset = nextOffset;
5547 : } else {
5548 0 : break;
5549 : }
5550 0 : } while (node);
5551 :
5552 0 : *outNode = node;
5553 0 : *outOffset = offset;
5554 : }
5555 0 : return res;
5556 : }
5557 :
5558 : // else not a text section. In this case we want to see if we should
5559 : // grab any adjacent inline nodes and/or parents and other ancestors
5560 0 : if (aWhere == kStart)
5561 : {
5562 : // some special casing for text nodes
5563 0 : if (nsEditor::IsTextNode(aNode))
5564 : {
5565 0 : res = nsEditor::GetNodeLocation(aNode, address_of(node), &offset);
5566 0 : NS_ENSURE_SUCCESS(res, res);
5567 : }
5568 :
5569 : // look back through any further inline nodes that
5570 : // aren't across a <br> from us, and that are enclosed in the same block.
5571 0 : nsCOMPtr<nsIDOMNode> priorNode;
5572 0 : res = mHTMLEditor->GetPriorHTMLNode(node, offset, address_of(priorNode), true);
5573 :
5574 0 : while (priorNode && NS_SUCCEEDED(res))
5575 : {
5576 0 : if (mHTMLEditor->IsVisBreak(priorNode))
5577 0 : break;
5578 0 : if (IsBlockNode(priorNode))
5579 0 : break;
5580 0 : res = nsEditor::GetNodeLocation(priorNode, address_of(node), &offset);
5581 0 : NS_ENSURE_SUCCESS(res, res);
5582 0 : res = mHTMLEditor->GetPriorHTMLNode(node, offset, address_of(priorNode), true);
5583 0 : NS_ENSURE_SUCCESS(res, res);
5584 : }
5585 :
5586 :
5587 : // finding the real start for this point. look up the tree for as long as we are the
5588 : // first node in the container, and as long as we haven't hit the body node.
5589 0 : res = mHTMLEditor->GetPriorHTMLNode(node, offset, address_of(nearNode), true);
5590 0 : NS_ENSURE_SUCCESS(res, res);
5591 0 : while (!nearNode && !nsTextEditUtils::IsBody(node))
5592 : {
5593 : // some cutoffs are here: we don't need to also include them in the aWhere == kEnd case.
5594 : // as long as they are in one or the other it will work.
5595 : // special case for outdent: don't keep looking up
5596 : // if we have found a blockquote element to act on
5597 0 : if ((actionID == nsHTMLEditor::kOpOutdent) && nsHTMLEditUtils::IsBlockquote(node))
5598 0 : break;
5599 :
5600 0 : res = nsEditor::GetNodeLocation(node, address_of(parent), &pOffset);
5601 0 : NS_ENSURE_SUCCESS(res, res);
5602 :
5603 : // Don't walk past the editable section. Note that we need to check
5604 : // before walking up to a parent because we need to return the parent
5605 : // object, so the parent itself might not be in the editable area, but
5606 : // it's OK if we're not performing a block-level action.
5607 : bool blockLevelAction = (actionID == nsHTMLEditor::kOpIndent)
5608 : || (actionID == nsHTMLEditor::kOpOutdent)
5609 : || (actionID == nsHTMLEditor::kOpAlign)
5610 0 : || (actionID == nsHTMLEditor::kOpMakeBasicBlock);
5611 0 : if (!mHTMLEditor->IsNodeInActiveEditor(parent) &&
5612 0 : (blockLevelAction || !mHTMLEditor->IsNodeInActiveEditor(node))) {
5613 0 : break;
5614 : }
5615 :
5616 0 : node = parent;
5617 0 : offset = pOffset;
5618 0 : res = mHTMLEditor->GetPriorHTMLNode(node, offset, address_of(nearNode), true);
5619 0 : NS_ENSURE_SUCCESS(res, res);
5620 : }
5621 0 : *outNode = node;
5622 0 : *outOffset = offset;
5623 0 : return res;
5624 : }
5625 :
5626 0 : if (aWhere == kEnd)
5627 : {
5628 : // some special casing for text nodes
5629 0 : if (nsEditor::IsTextNode(aNode))
5630 : {
5631 0 : res = nsEditor::GetNodeLocation(aNode, address_of(node), &offset);
5632 0 : NS_ENSURE_SUCCESS(res, res);
5633 0 : offset++; // want to be after the text node
5634 : }
5635 :
5636 : // look ahead through any further inline nodes that
5637 : // aren't across a <br> from us, and that are enclosed in the same block.
5638 0 : nsCOMPtr<nsIDOMNode> nextNode;
5639 0 : res = mHTMLEditor->GetNextHTMLNode(node, offset, address_of(nextNode), true);
5640 :
5641 0 : while (nextNode && NS_SUCCEEDED(res))
5642 : {
5643 0 : if (IsBlockNode(nextNode))
5644 0 : break;
5645 0 : res = nsEditor::GetNodeLocation(nextNode, address_of(node), &offset);
5646 0 : NS_ENSURE_SUCCESS(res, res);
5647 0 : offset++;
5648 0 : if (mHTMLEditor->IsVisBreak(nextNode))
5649 0 : break;
5650 0 : res = mHTMLEditor->GetNextHTMLNode(node, offset, address_of(nextNode), true);
5651 0 : NS_ENSURE_SUCCESS(res, res);
5652 : }
5653 :
5654 : // finding the real end for this point. look up the tree for as long as we are the
5655 : // last node in the container, and as long as we haven't hit the body node.
5656 0 : res = mHTMLEditor->GetNextHTMLNode(node, offset, address_of(nearNode), true);
5657 0 : NS_ENSURE_SUCCESS(res, res);
5658 0 : while (!nearNode && !nsTextEditUtils::IsBody(node))
5659 : {
5660 0 : res = nsEditor::GetNodeLocation(node, address_of(parent), &pOffset);
5661 0 : NS_ENSURE_SUCCESS(res, res);
5662 :
5663 : // Don't walk past the editable section. Note that we need to check
5664 : // before walking up to a parent because we need to return the parent
5665 : // object, so the parent itself might not be in the editable area, but
5666 : // it's OK.
5667 0 : if (!mHTMLEditor->IsNodeInActiveEditor(node) &&
5668 0 : !mHTMLEditor->IsNodeInActiveEditor(parent)) {
5669 0 : break;
5670 : }
5671 :
5672 0 : node = parent;
5673 0 : offset = pOffset+1; // we want to be AFTER nearNode
5674 0 : res = mHTMLEditor->GetNextHTMLNode(node, offset, address_of(nearNode), true);
5675 0 : NS_ENSURE_SUCCESS(res, res);
5676 : }
5677 0 : *outNode = node;
5678 0 : *outOffset = offset;
5679 0 : return res;
5680 : }
5681 :
5682 0 : return res;
5683 : }
5684 :
5685 :
5686 : ///////////////////////////////////////////////////////////////////////////
5687 : // GetPromotedRanges: run all the selection range endpoint through
5688 : // GetPromotedPoint()
5689 : //
5690 : nsresult
5691 0 : nsHTMLEditRules::GetPromotedRanges(nsISelection *inSelection,
5692 : nsCOMArray<nsIDOMRange> &outArrayOfRanges,
5693 : PRInt32 inOperationType)
5694 : {
5695 0 : NS_ENSURE_TRUE(inSelection, NS_ERROR_NULL_POINTER);
5696 :
5697 : PRInt32 rangeCount;
5698 0 : nsresult res = inSelection->GetRangeCount(&rangeCount);
5699 0 : NS_ENSURE_SUCCESS(res, res);
5700 :
5701 : PRInt32 i;
5702 0 : nsCOMPtr<nsIDOMRange> selectionRange;
5703 0 : nsCOMPtr<nsIDOMRange> opRange;
5704 :
5705 0 : for (i = 0; i < rangeCount; i++)
5706 : {
5707 0 : res = inSelection->GetRangeAt(i, getter_AddRefs(selectionRange));
5708 0 : NS_ENSURE_SUCCESS(res, res);
5709 :
5710 : // clone range so we don't muck with actual selection ranges
5711 0 : res = selectionRange->CloneRange(getter_AddRefs(opRange));
5712 0 : NS_ENSURE_SUCCESS(res, res);
5713 :
5714 : // make a new adjusted range to represent the appropriate block content.
5715 : // The basic idea is to push out the range endpoints
5716 : // to truly enclose the blocks that we will affect.
5717 : // This call alters opRange.
5718 0 : res = PromoteRange(opRange, inOperationType);
5719 0 : NS_ENSURE_SUCCESS(res, res);
5720 :
5721 : // stuff new opRange into array
5722 0 : outArrayOfRanges.AppendObject(opRange);
5723 : }
5724 0 : return res;
5725 : }
5726 :
5727 :
5728 : ///////////////////////////////////////////////////////////////////////////
5729 : // PromoteRange: expand a range to include any parents for which all
5730 : // editable children are already in range.
5731 : //
5732 : nsresult
5733 0 : nsHTMLEditRules::PromoteRange(nsIDOMRange *inRange,
5734 : PRInt32 inOperationType)
5735 : {
5736 0 : NS_ENSURE_TRUE(inRange, NS_ERROR_NULL_POINTER);
5737 : nsresult res;
5738 0 : nsCOMPtr<nsIDOMNode> startNode, endNode;
5739 : PRInt32 startOffset, endOffset;
5740 :
5741 0 : res = inRange->GetStartContainer(getter_AddRefs(startNode));
5742 0 : NS_ENSURE_SUCCESS(res, res);
5743 0 : res = inRange->GetStartOffset(&startOffset);
5744 0 : NS_ENSURE_SUCCESS(res, res);
5745 0 : res = inRange->GetEndContainer(getter_AddRefs(endNode));
5746 0 : NS_ENSURE_SUCCESS(res, res);
5747 0 : res = inRange->GetEndOffset(&endOffset);
5748 0 : NS_ENSURE_SUCCESS(res, res);
5749 :
5750 : // MOOSE major hack:
5751 : // GetPromotedPoint doesn't really do the right thing for collapsed ranges
5752 : // inside block elements that contain nothing but a solo <br>. It's easier
5753 : // to put a workaround here than to revamp GetPromotedPoint. :-(
5754 0 : if ( (startNode == endNode) && (startOffset == endOffset))
5755 : {
5756 0 : nsCOMPtr<nsIDOMNode> block;
5757 0 : if (IsBlockNode(startNode))
5758 0 : block = startNode;
5759 : else
5760 0 : block = mHTMLEditor->GetBlockNodeParent(startNode);
5761 0 : if (block)
5762 : {
5763 0 : bool bIsEmptyNode = false;
5764 : // check for the editing host
5765 0 : nsIContent *rootContent = mHTMLEditor->GetActiveEditingHost();
5766 0 : nsCOMPtr<nsINode> rootNode = do_QueryInterface(rootContent);
5767 0 : nsCOMPtr<nsINode> blockNode = do_QueryInterface(block);
5768 0 : NS_ENSURE_TRUE(rootNode && blockNode, NS_ERROR_UNEXPECTED);
5769 : // Make sure we don't go higher than our root element in the content tree
5770 0 : if (!nsContentUtils::ContentIsDescendantOf(rootNode, blockNode))
5771 : {
5772 0 : res = mHTMLEditor->IsEmptyNode(block, &bIsEmptyNode, true, false);
5773 : }
5774 0 : if (bIsEmptyNode)
5775 : {
5776 : PRUint32 numChildren;
5777 0 : nsEditor::GetLengthOfDOMNode(block, numChildren);
5778 0 : startNode = block;
5779 0 : endNode = block;
5780 0 : startOffset = 0;
5781 0 : endOffset = numChildren;
5782 : }
5783 : }
5784 : }
5785 :
5786 : // make a new adjusted range to represent the appropriate block content.
5787 : // this is tricky. the basic idea is to push out the range endpoints
5788 : // to truly enclose the blocks that we will affect
5789 :
5790 0 : nsCOMPtr<nsIDOMNode> opStartNode;
5791 0 : nsCOMPtr<nsIDOMNode> opEndNode;
5792 : PRInt32 opStartOffset, opEndOffset;
5793 0 : nsCOMPtr<nsIDOMRange> opRange;
5794 :
5795 0 : res = GetPromotedPoint( kStart, startNode, startOffset, inOperationType, address_of(opStartNode), &opStartOffset);
5796 0 : NS_ENSURE_SUCCESS(res, res);
5797 0 : res = GetPromotedPoint( kEnd, endNode, endOffset, inOperationType, address_of(opEndNode), &opEndOffset);
5798 0 : NS_ENSURE_SUCCESS(res, res);
5799 :
5800 : // Make sure that the new range ends up to be in the editable section.
5801 0 : if (!mHTMLEditor->IsNodeInActiveEditor(nsEditor::GetNodeAtRangeOffsetPoint(opStartNode, opStartOffset)) ||
5802 0 : !mHTMLEditor->IsNodeInActiveEditor(nsEditor::GetNodeAtRangeOffsetPoint(opEndNode, opEndOffset - 1))) {
5803 0 : return NS_OK;
5804 : }
5805 :
5806 0 : res = inRange->SetStart(opStartNode, opStartOffset);
5807 0 : NS_ENSURE_SUCCESS(res, res);
5808 0 : res = inRange->SetEnd(opEndNode, opEndOffset);
5809 0 : return res;
5810 : }
5811 :
5812 : class nsUniqueFunctor : public nsBoolDomIterFunctor
5813 : {
5814 : public:
5815 0 : nsUniqueFunctor(nsCOMArray<nsIDOMNode> &aArray) : mArray(aArray)
5816 : {
5817 0 : }
5818 0 : virtual bool operator()(nsIDOMNode* aNode) // used to build list of all nodes iterator covers
5819 : {
5820 0 : return mArray.IndexOf(aNode) < 0;
5821 : }
5822 :
5823 : private:
5824 : nsCOMArray<nsIDOMNode> &mArray;
5825 : };
5826 :
5827 : ///////////////////////////////////////////////////////////////////////////
5828 : // GetNodesForOperation: run through the ranges in the array and construct
5829 : // a new array of nodes to be acted on.
5830 : //
5831 : nsresult
5832 0 : nsHTMLEditRules::GetNodesForOperation(nsCOMArray<nsIDOMRange>& inArrayOfRanges,
5833 : nsCOMArray<nsIDOMNode>& outArrayOfNodes,
5834 : PRInt32 inOperationType,
5835 : bool aDontTouchContent)
5836 : {
5837 0 : PRInt32 rangeCount = inArrayOfRanges.Count();
5838 :
5839 : PRInt32 i;
5840 0 : nsCOMPtr<nsIDOMRange> opRange;
5841 :
5842 0 : nsresult res = NS_OK;
5843 :
5844 : // bust up any inlines that cross our range endpoints,
5845 : // but only if we are allowed to touch content.
5846 :
5847 0 : if (!aDontTouchContent)
5848 : {
5849 0 : nsAutoTArray<nsRangeStore, 16> rangeItemArray;
5850 0 : if (!rangeItemArray.AppendElements(rangeCount)) {
5851 0 : return NS_ERROR_OUT_OF_MEMORY;
5852 : }
5853 :
5854 0 : NS_ASSERTION(static_cast<PRUint32>(rangeCount) == rangeItemArray.Length(),
5855 : "How did that happen?");
5856 :
5857 : // first register ranges for special editor gravity
5858 0 : for (i = 0; i < rangeCount; i++)
5859 : {
5860 0 : opRange = inArrayOfRanges[0];
5861 0 : nsRangeStore *item = rangeItemArray.Elements() + i;
5862 0 : item->StoreRange(opRange);
5863 0 : mHTMLEditor->mRangeUpdater.RegisterRangeItem(item);
5864 0 : inArrayOfRanges.RemoveObjectAt(0);
5865 : }
5866 : // now bust up inlines. Safe to start at rangeCount-1, since we
5867 : // asserted we have enough items above.
5868 0 : for (i = rangeCount-1; i >= 0 && NS_SUCCEEDED(res); i--)
5869 : {
5870 0 : res = BustUpInlinesAtRangeEndpoints(rangeItemArray[i]);
5871 : }
5872 : // then unregister the ranges
5873 0 : for (i = 0; i < rangeCount; i++)
5874 : {
5875 0 : nsRangeStore *item = rangeItemArray.Elements() + i;
5876 0 : mHTMLEditor->mRangeUpdater.DropRangeItem(item);
5877 0 : nsRefPtr<nsRange> range;
5878 0 : nsresult res2 = item->GetRange(getter_AddRefs(range));
5879 0 : opRange = range;
5880 0 : if (NS_FAILED(res2) && NS_SUCCEEDED(res)) {
5881 : // Remember the failure, but keep going so we make sure to unregister
5882 : // all our range items.
5883 0 : res = res2;
5884 : }
5885 0 : inArrayOfRanges.AppendObject(opRange);
5886 : }
5887 0 : NS_ENSURE_SUCCESS(res, res);
5888 : }
5889 : // gather up a list of all the nodes
5890 0 : for (i = 0; i < rangeCount; i++)
5891 : {
5892 0 : opRange = inArrayOfRanges[i];
5893 :
5894 0 : nsDOMSubtreeIterator iter;
5895 0 : res = iter.Init(opRange);
5896 0 : NS_ENSURE_SUCCESS(res, res);
5897 0 : if (outArrayOfNodes.Count() == 0) {
5898 0 : nsTrivialFunctor functor;
5899 0 : res = iter.AppendList(functor, outArrayOfNodes);
5900 0 : NS_ENSURE_SUCCESS(res, res);
5901 : }
5902 : else {
5903 : // We don't want duplicates in outArrayOfNodes, so we use an
5904 : // iterator/functor that only return nodes that are not already in
5905 : // outArrayOfNodes.
5906 0 : nsCOMArray<nsIDOMNode> nodes;
5907 0 : nsUniqueFunctor functor(outArrayOfNodes);
5908 0 : res = iter.AppendList(functor, nodes);
5909 0 : NS_ENSURE_SUCCESS(res, res);
5910 0 : if (!outArrayOfNodes.AppendObjects(nodes))
5911 0 : return NS_ERROR_OUT_OF_MEMORY;
5912 : }
5913 : }
5914 :
5915 : // certain operations should not act on li's and td's, but rather inside
5916 : // them. alter the list as needed
5917 0 : if (inOperationType == kMakeBasicBlock)
5918 : {
5919 0 : PRInt32 listCount = outArrayOfNodes.Count();
5920 0 : for (i=listCount-1; i>=0; i--)
5921 : {
5922 0 : nsCOMPtr<nsIDOMNode> node = outArrayOfNodes[i];
5923 0 : if (nsHTMLEditUtils::IsListItem(node))
5924 : {
5925 0 : PRInt32 j=i;
5926 0 : outArrayOfNodes.RemoveObjectAt(i);
5927 0 : res = GetInnerContent(node, outArrayOfNodes, &j);
5928 0 : NS_ENSURE_SUCCESS(res, res);
5929 : }
5930 : }
5931 : }
5932 : // indent/outdent already do something special for list items, but
5933 : // we still need to make sure we don't act on table elements
5934 0 : else if ( (inOperationType == kOutdent) ||
5935 : (inOperationType == kIndent) ||
5936 : (inOperationType == kSetAbsolutePosition))
5937 : {
5938 0 : PRInt32 listCount = outArrayOfNodes.Count();
5939 0 : for (i=listCount-1; i>=0; i--)
5940 : {
5941 0 : nsCOMPtr<nsIDOMNode> node = outArrayOfNodes[i];
5942 0 : if (nsHTMLEditUtils::IsTableElementButNotTable(node))
5943 : {
5944 0 : PRInt32 j=i;
5945 0 : outArrayOfNodes.RemoveObjectAt(i);
5946 0 : res = GetInnerContent(node, outArrayOfNodes, &j);
5947 0 : NS_ENSURE_SUCCESS(res, res);
5948 : }
5949 : }
5950 : }
5951 : // outdent should look inside of divs.
5952 0 : if (inOperationType == kOutdent && !mHTMLEditor->IsCSSEnabled())
5953 : {
5954 0 : PRInt32 listCount = outArrayOfNodes.Count();
5955 0 : for (i=listCount-1; i>=0; i--)
5956 : {
5957 0 : nsCOMPtr<nsIDOMNode> node = outArrayOfNodes[i];
5958 0 : if (nsHTMLEditUtils::IsDiv(node))
5959 : {
5960 0 : PRInt32 j=i;
5961 0 : outArrayOfNodes.RemoveObjectAt(i);
5962 0 : res = GetInnerContent(node, outArrayOfNodes, &j, false, false);
5963 0 : NS_ENSURE_SUCCESS(res, res);
5964 : }
5965 : }
5966 : }
5967 :
5968 :
5969 : // post process the list to break up inline containers that contain br's.
5970 : // but only for operations that might care, like making lists or para's...
5971 0 : if ( (inOperationType == kMakeBasicBlock) ||
5972 : (inOperationType == kMakeList) ||
5973 : (inOperationType == kAlign) ||
5974 : (inOperationType == kSetAbsolutePosition) ||
5975 : (inOperationType == kIndent) ||
5976 : (inOperationType == kOutdent) )
5977 : {
5978 0 : PRInt32 listCount = outArrayOfNodes.Count();
5979 0 : for (i=listCount-1; i>=0; i--)
5980 : {
5981 0 : nsCOMPtr<nsIDOMNode> node = outArrayOfNodes[i];
5982 0 : if (!aDontTouchContent && IsInlineNode(node)
5983 0 : && mHTMLEditor->IsContainer(node) && !mHTMLEditor->IsTextNode(node))
5984 : {
5985 0 : nsCOMArray<nsIDOMNode> arrayOfInlines;
5986 0 : res = BustUpInlinesAtBRs(node, arrayOfInlines);
5987 0 : NS_ENSURE_SUCCESS(res, res);
5988 : // put these nodes in outArrayOfNodes, replacing the current node
5989 0 : outArrayOfNodes.RemoveObjectAt(i);
5990 0 : outArrayOfNodes.InsertObjectsAt(arrayOfInlines, i);
5991 : }
5992 : }
5993 : }
5994 0 : return res;
5995 : }
5996 :
5997 :
5998 :
5999 : ///////////////////////////////////////////////////////////////////////////
6000 : // GetChildNodesForOperation:
6001 : //
6002 : nsresult
6003 0 : nsHTMLEditRules::GetChildNodesForOperation(nsIDOMNode *inNode,
6004 : nsCOMArray<nsIDOMNode>& outArrayOfNodes)
6005 : {
6006 0 : NS_ENSURE_TRUE(inNode, NS_ERROR_NULL_POINTER);
6007 :
6008 0 : nsCOMPtr<nsIDOMNodeList> childNodes;
6009 0 : nsresult res = inNode->GetChildNodes(getter_AddRefs(childNodes));
6010 0 : NS_ENSURE_SUCCESS(res, res);
6011 0 : NS_ENSURE_TRUE(childNodes, NS_ERROR_NULL_POINTER);
6012 : PRUint32 childCount;
6013 0 : res = childNodes->GetLength(&childCount);
6014 0 : NS_ENSURE_SUCCESS(res, res);
6015 :
6016 : PRUint32 i;
6017 0 : nsCOMPtr<nsIDOMNode> node;
6018 0 : for (i = 0; i < childCount; i++)
6019 : {
6020 0 : res = childNodes->Item( i, getter_AddRefs(node));
6021 0 : NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
6022 0 : if (!outArrayOfNodes.AppendObject(node))
6023 0 : return NS_ERROR_FAILURE;
6024 : }
6025 0 : return res;
6026 : }
6027 :
6028 :
6029 :
6030 : ///////////////////////////////////////////////////////////////////////////
6031 : // GetListActionNodes:
6032 : //
6033 : nsresult
6034 0 : nsHTMLEditRules::GetListActionNodes(nsCOMArray<nsIDOMNode> &outArrayOfNodes,
6035 : bool aEntireList,
6036 : bool aDontTouchContent)
6037 : {
6038 0 : nsresult res = NS_OK;
6039 :
6040 0 : nsCOMPtr<nsISelection>selection;
6041 0 : res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
6042 0 : NS_ENSURE_SUCCESS(res, res);
6043 0 : nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
6044 0 : NS_ENSURE_TRUE(selPriv, NS_ERROR_FAILURE);
6045 : // added this in so that ui code can ask to change an entire list, even if selection
6046 : // is only in part of it. used by list item dialog.
6047 0 : if (aEntireList)
6048 : {
6049 0 : nsCOMPtr<nsIEnumerator> enumerator;
6050 0 : res = selPriv->GetEnumerator(getter_AddRefs(enumerator));
6051 0 : NS_ENSURE_SUCCESS(res, res);
6052 0 : NS_ENSURE_TRUE(enumerator, NS_ERROR_UNEXPECTED);
6053 :
6054 0 : for (enumerator->First(); NS_OK!=enumerator->IsDone(); enumerator->Next())
6055 : {
6056 0 : nsCOMPtr<nsISupports> currentItem;
6057 0 : res = enumerator->CurrentItem(getter_AddRefs(currentItem));
6058 0 : NS_ENSURE_SUCCESS(res, res);
6059 0 : NS_ENSURE_TRUE(currentItem, NS_ERROR_UNEXPECTED);
6060 :
6061 0 : nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
6062 0 : nsCOMPtr<nsIDOMNode> commonParent, parent, tmp;
6063 0 : range->GetCommonAncestorContainer(getter_AddRefs(commonParent));
6064 0 : if (commonParent)
6065 : {
6066 0 : parent = commonParent;
6067 0 : while (parent)
6068 : {
6069 0 : if (nsHTMLEditUtils::IsList(parent))
6070 : {
6071 0 : outArrayOfNodes.AppendObject(parent);
6072 0 : break;
6073 : }
6074 0 : parent->GetParentNode(getter_AddRefs(tmp));
6075 0 : parent = tmp;
6076 : }
6077 : }
6078 : }
6079 : // if we didn't find any nodes this way, then try the normal way. perhaps the
6080 : // selection spans multiple lists but with no common list parent.
6081 0 : if (outArrayOfNodes.Count()) return NS_OK;
6082 : }
6083 :
6084 : {
6085 : // We don't like other people messing with our selection!
6086 0 : nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
6087 :
6088 : // contruct a list of nodes to act on.
6089 0 : res = GetNodesFromSelection(selection, kMakeList, outArrayOfNodes, aDontTouchContent);
6090 0 : NS_ENSURE_SUCCESS(res, res);
6091 : }
6092 :
6093 : // pre process our list of nodes...
6094 0 : PRInt32 listCount = outArrayOfNodes.Count();
6095 : PRInt32 i;
6096 0 : for (i=listCount-1; i>=0; i--)
6097 : {
6098 0 : nsCOMPtr<nsIDOMNode> testNode = outArrayOfNodes[i];
6099 :
6100 : // Remove all non-editable nodes. Leave them be.
6101 0 : if (!mHTMLEditor->IsEditable(testNode))
6102 : {
6103 0 : outArrayOfNodes.RemoveObjectAt(i);
6104 : }
6105 :
6106 : // scan for table elements and divs. If we find table elements other than table,
6107 : // replace it with a list of any editable non-table content.
6108 0 : if (nsHTMLEditUtils::IsTableElementButNotTable(testNode))
6109 : {
6110 0 : PRInt32 j=i;
6111 0 : outArrayOfNodes.RemoveObjectAt(i);
6112 0 : res = GetInnerContent(testNode, outArrayOfNodes, &j, false);
6113 0 : NS_ENSURE_SUCCESS(res, res);
6114 : }
6115 : }
6116 :
6117 : // if there is only one node in the array, and it is a list, div, or blockquote,
6118 : // then look inside of it until we find inner list or content.
6119 0 : res = LookInsideDivBQandList(outArrayOfNodes);
6120 0 : return res;
6121 : }
6122 :
6123 :
6124 : ///////////////////////////////////////////////////////////////////////////
6125 : // LookInsideDivBQandList:
6126 : //
6127 : nsresult
6128 0 : nsHTMLEditRules::LookInsideDivBQandList(nsCOMArray<nsIDOMNode>& aNodeArray)
6129 : {
6130 : // if there is only one node in the array, and it is a list, div, or blockquote,
6131 : // then look inside of it until we find inner list or content.
6132 0 : nsresult res = NS_OK;
6133 0 : PRInt32 listCount = aNodeArray.Count();
6134 0 : if (listCount == 1)
6135 : {
6136 0 : nsCOMPtr<nsIDOMNode> curNode = aNodeArray[0];
6137 :
6138 0 : while (nsHTMLEditUtils::IsDiv(curNode)
6139 0 : || nsHTMLEditUtils::IsList(curNode)
6140 0 : || nsHTMLEditUtils::IsBlockquote(curNode))
6141 : {
6142 : // dive as long as there is only one child, and it is a list, div, blockquote
6143 : PRUint32 numChildren;
6144 0 : res = mHTMLEditor->CountEditableChildren(curNode, numChildren);
6145 0 : NS_ENSURE_SUCCESS(res, res);
6146 :
6147 0 : if (numChildren == 1)
6148 : {
6149 : // keep diving
6150 0 : nsCOMPtr <nsIDOMNode> tmpNode = nsEditor::GetChildAt(curNode, 0);
6151 0 : if (nsHTMLEditUtils::IsDiv(tmpNode)
6152 0 : || nsHTMLEditUtils::IsList(tmpNode)
6153 0 : || nsHTMLEditUtils::IsBlockquote(tmpNode))
6154 : {
6155 : // check editablility XXX floppy moose
6156 0 : curNode = tmpNode;
6157 : }
6158 : else break;
6159 : }
6160 0 : else break;
6161 : }
6162 : // we've found innermost list/blockquote/div:
6163 : // replace the one node in the array with these nodes
6164 0 : aNodeArray.RemoveObjectAt(0);
6165 0 : if ((nsHTMLEditUtils::IsDiv(curNode) || nsHTMLEditUtils::IsBlockquote(curNode)))
6166 : {
6167 0 : PRInt32 j=0;
6168 0 : res = GetInnerContent(curNode, aNodeArray, &j, false, false);
6169 : }
6170 : else
6171 : {
6172 0 : aNodeArray.AppendObject(curNode);
6173 : }
6174 : }
6175 0 : return res;
6176 : }
6177 :
6178 :
6179 : ///////////////////////////////////////////////////////////////////////////
6180 : // GetDefinitionListItemTypes:
6181 : //
6182 : nsresult
6183 0 : nsHTMLEditRules::GetDefinitionListItemTypes(nsIDOMNode *aNode, bool &aDT, bool &aDD)
6184 : {
6185 0 : NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
6186 0 : aDT = aDD = false;
6187 0 : nsresult res = NS_OK;
6188 0 : nsCOMPtr<nsIDOMNode> child, temp;
6189 0 : res = aNode->GetFirstChild(getter_AddRefs(child));
6190 0 : while (child && NS_SUCCEEDED(res))
6191 : {
6192 0 : if (nsEditor::NodeIsType(child, nsEditProperty::dt)) aDT = true;
6193 0 : else if (nsEditor::NodeIsType(child, nsEditProperty::dd)) aDD = true;
6194 0 : res = child->GetNextSibling(getter_AddRefs(temp));
6195 0 : child = temp;
6196 : }
6197 0 : return res;
6198 : }
6199 :
6200 : ///////////////////////////////////////////////////////////////////////////
6201 : // GetParagraphFormatNodes:
6202 : //
6203 : nsresult
6204 0 : nsHTMLEditRules::GetParagraphFormatNodes(nsCOMArray<nsIDOMNode>& outArrayOfNodes,
6205 : bool aDontTouchContent)
6206 : {
6207 0 : nsCOMPtr<nsISelection>selection;
6208 0 : nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
6209 0 : NS_ENSURE_SUCCESS(res, res);
6210 :
6211 : // contruct a list of nodes to act on.
6212 0 : res = GetNodesFromSelection(selection, kMakeBasicBlock, outArrayOfNodes, aDontTouchContent);
6213 0 : NS_ENSURE_SUCCESS(res, res);
6214 :
6215 : // pre process our list of nodes...
6216 0 : PRInt32 listCount = outArrayOfNodes.Count();
6217 : PRInt32 i;
6218 0 : for (i=listCount-1; i>=0; i--)
6219 : {
6220 0 : nsCOMPtr<nsIDOMNode> testNode = outArrayOfNodes[i];
6221 :
6222 : // Remove all non-editable nodes. Leave them be.
6223 0 : if (!mHTMLEditor->IsEditable(testNode))
6224 : {
6225 0 : outArrayOfNodes.RemoveObjectAt(i);
6226 : }
6227 :
6228 : // scan for table elements. If we find table elements other than table,
6229 : // replace it with a list of any editable non-table content. Ditto for list elements.
6230 0 : if (nsHTMLEditUtils::IsTableElement(testNode) ||
6231 0 : nsHTMLEditUtils::IsList(testNode) ||
6232 0 : nsHTMLEditUtils::IsListItem(testNode) )
6233 : {
6234 0 : PRInt32 j=i;
6235 0 : outArrayOfNodes.RemoveObjectAt(i);
6236 0 : res = GetInnerContent(testNode, outArrayOfNodes, &j);
6237 0 : NS_ENSURE_SUCCESS(res, res);
6238 : }
6239 : }
6240 0 : return res;
6241 : }
6242 :
6243 :
6244 : ///////////////////////////////////////////////////////////////////////////
6245 : // BustUpInlinesAtRangeEndpoints:
6246 : //
6247 : nsresult
6248 0 : nsHTMLEditRules::BustUpInlinesAtRangeEndpoints(nsRangeStore &item)
6249 : {
6250 0 : nsresult res = NS_OK;
6251 0 : bool isCollapsed = ((item.startNode == item.endNode) && (item.startOffset == item.endOffset));
6252 :
6253 0 : nsCOMPtr<nsIDOMNode> endInline = GetHighestInlineParent(item.endNode);
6254 :
6255 : // if we have inline parents above range endpoints, split them
6256 0 : if (endInline && !isCollapsed)
6257 : {
6258 0 : nsCOMPtr<nsIDOMNode> resultEndNode;
6259 : PRInt32 resultEndOffset;
6260 0 : endInline->GetParentNode(getter_AddRefs(resultEndNode));
6261 : res = mHTMLEditor->SplitNodeDeep(endInline, item.endNode, item.endOffset,
6262 0 : &resultEndOffset, true);
6263 0 : NS_ENSURE_SUCCESS(res, res);
6264 : // reset range
6265 0 : item.endNode = resultEndNode; item.endOffset = resultEndOffset;
6266 : }
6267 :
6268 0 : nsCOMPtr<nsIDOMNode> startInline = GetHighestInlineParent(item.startNode);
6269 :
6270 0 : if (startInline)
6271 : {
6272 0 : nsCOMPtr<nsIDOMNode> resultStartNode;
6273 : PRInt32 resultStartOffset;
6274 0 : startInline->GetParentNode(getter_AddRefs(resultStartNode));
6275 : res = mHTMLEditor->SplitNodeDeep(startInline, item.startNode, item.startOffset,
6276 0 : &resultStartOffset, true);
6277 0 : NS_ENSURE_SUCCESS(res, res);
6278 : // reset range
6279 0 : item.startNode = resultStartNode; item.startOffset = resultStartOffset;
6280 : }
6281 :
6282 0 : return res;
6283 : }
6284 :
6285 :
6286 :
6287 : ///////////////////////////////////////////////////////////////////////////
6288 : // BustUpInlinesAtBRs:
6289 : //
6290 : nsresult
6291 0 : nsHTMLEditRules::BustUpInlinesAtBRs(nsIDOMNode *inNode,
6292 : nsCOMArray<nsIDOMNode>& outArrayOfNodes)
6293 : {
6294 0 : NS_ENSURE_TRUE(inNode, NS_ERROR_NULL_POINTER);
6295 :
6296 : // first step is to build up a list of all the break nodes inside
6297 : // the inline container.
6298 0 : nsCOMArray<nsIDOMNode> arrayOfBreaks;
6299 0 : nsBRNodeFunctor functor;
6300 0 : nsDOMIterator iter;
6301 0 : nsresult res = iter.Init(inNode);
6302 0 : NS_ENSURE_SUCCESS(res, res);
6303 0 : res = iter.AppendList(functor, arrayOfBreaks);
6304 0 : NS_ENSURE_SUCCESS(res, res);
6305 :
6306 : // if there aren't any breaks, just put inNode itself in the array
6307 0 : PRInt32 listCount = arrayOfBreaks.Count();
6308 0 : if (!listCount)
6309 : {
6310 0 : if (!outArrayOfNodes.AppendObject(inNode))
6311 0 : return NS_ERROR_FAILURE;
6312 : }
6313 : else
6314 : {
6315 : // else we need to bust up inNode along all the breaks
6316 0 : nsCOMPtr<nsIDOMNode> breakNode;
6317 0 : nsCOMPtr<nsIDOMNode> inlineParentNode;
6318 0 : nsCOMPtr<nsIDOMNode> leftNode;
6319 0 : nsCOMPtr<nsIDOMNode> rightNode;
6320 0 : nsCOMPtr<nsIDOMNode> splitDeepNode = inNode;
6321 0 : nsCOMPtr<nsIDOMNode> splitParentNode;
6322 : PRInt32 splitOffset, resultOffset, i;
6323 0 : inNode->GetParentNode(getter_AddRefs(inlineParentNode));
6324 :
6325 0 : for (i=0; i< listCount; i++)
6326 : {
6327 0 : breakNode = arrayOfBreaks[i];
6328 0 : NS_ENSURE_TRUE(breakNode, NS_ERROR_NULL_POINTER);
6329 0 : NS_ENSURE_TRUE(splitDeepNode, NS_ERROR_NULL_POINTER);
6330 0 : res = nsEditor::GetNodeLocation(breakNode, address_of(splitParentNode), &splitOffset);
6331 0 : NS_ENSURE_SUCCESS(res, res);
6332 : res = mHTMLEditor->SplitNodeDeep(splitDeepNode, splitParentNode, splitOffset,
6333 0 : &resultOffset, false, address_of(leftNode), address_of(rightNode));
6334 0 : NS_ENSURE_SUCCESS(res, res);
6335 : // put left node in node list
6336 0 : if (leftNode)
6337 : {
6338 : // might not be a left node. a break might have been at the very
6339 : // beginning of inline container, in which case splitnodedeep
6340 : // would not actually split anything
6341 0 : if (!outArrayOfNodes.AppendObject(leftNode))
6342 0 : return NS_ERROR_FAILURE;
6343 : }
6344 : // move break outside of container and also put in node list
6345 0 : res = mHTMLEditor->MoveNode(breakNode, inlineParentNode, resultOffset);
6346 0 : NS_ENSURE_SUCCESS(res, res);
6347 0 : if (!outArrayOfNodes.AppendObject(breakNode))
6348 0 : return NS_ERROR_FAILURE;
6349 : // now rightNode becomes the new node to split
6350 0 : splitDeepNode = rightNode;
6351 : }
6352 : // now tack on remaining rightNode, if any, to the list
6353 0 : if (rightNode)
6354 : {
6355 0 : if (!outArrayOfNodes.AppendObject(rightNode))
6356 0 : return NS_ERROR_FAILURE;
6357 : }
6358 : }
6359 0 : return res;
6360 : }
6361 :
6362 :
6363 : nsCOMPtr<nsIDOMNode>
6364 0 : nsHTMLEditRules::GetHighestInlineParent(nsIDOMNode* aNode)
6365 : {
6366 0 : NS_ENSURE_TRUE(aNode, nsnull);
6367 0 : if (IsBlockNode(aNode)) return nsnull;
6368 0 : nsCOMPtr<nsIDOMNode> inlineNode, node=aNode;
6369 :
6370 0 : while (node && IsInlineNode(node))
6371 : {
6372 0 : inlineNode = node;
6373 0 : inlineNode->GetParentNode(getter_AddRefs(node));
6374 : }
6375 0 : return inlineNode;
6376 : }
6377 :
6378 :
6379 : ///////////////////////////////////////////////////////////////////////////
6380 : // GetNodesFromPoint: given a particular operation, construct a list
6381 : // of nodes from a point that will be operated on.
6382 : //
6383 : nsresult
6384 0 : nsHTMLEditRules::GetNodesFromPoint(DOMPoint point,
6385 : PRInt32 operation,
6386 : nsCOMArray<nsIDOMNode> &arrayOfNodes,
6387 : bool dontTouchContent)
6388 : {
6389 : nsresult res;
6390 :
6391 : // get our point
6392 0 : nsCOMPtr<nsIDOMNode> node;
6393 : PRInt32 offset;
6394 0 : point.GetPoint(node, offset);
6395 :
6396 : // use it to make a range
6397 0 : nsRefPtr<nsRange> range = new nsRange();
6398 0 : res = range->SetStart(node, offset);
6399 0 : NS_ENSURE_SUCCESS(res, res);
6400 : /* SetStart() will also set the end for this new range
6401 : res = range->SetEnd(node, offset);
6402 : NS_ENSURE_SUCCESS(res, res); */
6403 :
6404 : // expand the range to include adjacent inlines
6405 0 : res = PromoteRange(range, operation);
6406 0 : NS_ENSURE_SUCCESS(res, res);
6407 :
6408 : // make array of ranges
6409 0 : nsCOMArray<nsIDOMRange> arrayOfRanges;
6410 :
6411 : // stuff new opRange into array
6412 0 : arrayOfRanges.AppendObject(range);
6413 :
6414 : // use these ranges to contruct a list of nodes to act on.
6415 0 : res = GetNodesForOperation(arrayOfRanges, arrayOfNodes, operation, dontTouchContent);
6416 0 : return res;
6417 : }
6418 :
6419 :
6420 : ///////////////////////////////////////////////////////////////////////////
6421 : // GetNodesFromSelection: given a particular operation, construct a list
6422 : // of nodes from the selection that will be operated on.
6423 : //
6424 : nsresult
6425 0 : nsHTMLEditRules::GetNodesFromSelection(nsISelection *selection,
6426 : PRInt32 operation,
6427 : nsCOMArray<nsIDOMNode>& arrayOfNodes,
6428 : bool dontTouchContent)
6429 : {
6430 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
6431 : nsresult res;
6432 :
6433 : // promote selection ranges
6434 0 : nsCOMArray<nsIDOMRange> arrayOfRanges;
6435 0 : res = GetPromotedRanges(selection, arrayOfRanges, operation);
6436 0 : NS_ENSURE_SUCCESS(res, res);
6437 :
6438 : // use these ranges to contruct a list of nodes to act on.
6439 0 : res = GetNodesForOperation(arrayOfRanges, arrayOfNodes, operation, dontTouchContent);
6440 0 : return res;
6441 : }
6442 :
6443 :
6444 : ///////////////////////////////////////////////////////////////////////////
6445 : // MakeTransitionList: detect all the transitions in the array, where a
6446 : // transition means that adjacent nodes in the array
6447 : // don't have the same parent.
6448 : //
6449 : nsresult
6450 0 : nsHTMLEditRules::MakeTransitionList(nsCOMArray<nsIDOMNode>& inArrayOfNodes,
6451 : nsTArray<bool> &inTransitionArray)
6452 : {
6453 0 : PRUint32 listCount = inArrayOfNodes.Count();
6454 0 : inTransitionArray.EnsureLengthAtLeast(listCount);
6455 : PRUint32 i;
6456 0 : nsCOMPtr<nsIDOMNode> prevElementParent;
6457 0 : nsCOMPtr<nsIDOMNode> curElementParent;
6458 :
6459 0 : for (i=0; i<listCount; i++)
6460 : {
6461 0 : nsIDOMNode* transNode = inArrayOfNodes[i];
6462 0 : transNode->GetParentNode(getter_AddRefs(curElementParent));
6463 0 : if (curElementParent != prevElementParent)
6464 : {
6465 : // different parents, or separated by <br>: transition point
6466 0 : inTransitionArray[i] = true;
6467 : }
6468 : else
6469 : {
6470 : // same parents: these nodes grew up together
6471 0 : inTransitionArray[i] = false;
6472 : }
6473 0 : prevElementParent = curElementParent;
6474 : }
6475 0 : return NS_OK;
6476 : }
6477 :
6478 :
6479 :
6480 : /********************************************************
6481 : * main implementation methods
6482 : ********************************************************/
6483 :
6484 : ///////////////////////////////////////////////////////////////////////////
6485 : // IsInListItem: if aNode is the descendant of a listitem, return that li.
6486 : // But table element boundaries are stoppers on the search.
6487 : // Also stops on the active editor host (contenteditable).
6488 : // Also test if aNode is an li itself.
6489 : //
6490 : already_AddRefed<nsIDOMNode>
6491 0 : nsHTMLEditRules::IsInListItem(nsIDOMNode* aNode)
6492 : {
6493 0 : nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
6494 0 : nsCOMPtr<nsIDOMNode> retval = do_QueryInterface(IsInListItem(node));
6495 0 : return retval.forget();
6496 : }
6497 :
6498 : nsINode*
6499 0 : nsHTMLEditRules::IsInListItem(nsINode* aNode)
6500 : {
6501 0 : NS_ENSURE_TRUE(aNode, nsnull);
6502 0 : if (aNode->IsElement() && nsHTMLEditUtils::IsListItem(aNode->AsElement())) {
6503 0 : return aNode;
6504 : }
6505 :
6506 0 : nsINode* parent = aNode->GetNodeParent();
6507 0 : while (parent && mHTMLEditor->IsNodeInActiveEditor(parent) &&
6508 0 : !(parent->IsElement() &&
6509 0 : nsHTMLEditUtils::IsTableElement(parent->AsElement()))) {
6510 0 : if (nsHTMLEditUtils::IsListItem(parent->AsElement())) {
6511 0 : return parent;
6512 : }
6513 0 : parent = parent->GetNodeParent();
6514 : }
6515 0 : return nsnull;
6516 : }
6517 :
6518 :
6519 : ///////////////////////////////////////////////////////////////////////////
6520 : // ReturnInHeader: do the right thing for returns pressed in headers
6521 : //
6522 : nsresult
6523 0 : nsHTMLEditRules::ReturnInHeader(nsISelection *aSelection,
6524 : nsIDOMNode *aHeader,
6525 : nsIDOMNode *aNode,
6526 : PRInt32 aOffset)
6527 : {
6528 0 : NS_ENSURE_TRUE(aSelection && aHeader && aNode, NS_ERROR_NULL_POINTER);
6529 :
6530 : // remeber where the header is
6531 0 : nsCOMPtr<nsIDOMNode> headerParent;
6532 : PRInt32 offset;
6533 0 : nsresult res = nsEditor::GetNodeLocation(aHeader, address_of(headerParent), &offset);
6534 0 : NS_ENSURE_SUCCESS(res, res);
6535 :
6536 : // get ws code to adjust any ws
6537 0 : nsCOMPtr<nsIDOMNode> selNode = aNode;
6538 0 : res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor, address_of(selNode), &aOffset);
6539 0 : NS_ENSURE_SUCCESS(res, res);
6540 :
6541 : // split the header
6542 : PRInt32 newOffset;
6543 0 : res = mHTMLEditor->SplitNodeDeep( aHeader, selNode, aOffset, &newOffset);
6544 0 : NS_ENSURE_SUCCESS(res, res);
6545 :
6546 : // if the leftand heading is empty, put a mozbr in it
6547 0 : nsCOMPtr<nsIDOMNode> prevItem;
6548 0 : mHTMLEditor->GetPriorHTMLSibling(aHeader, address_of(prevItem));
6549 0 : if (prevItem && nsHTMLEditUtils::IsHeader(prevItem))
6550 : {
6551 : bool bIsEmptyNode;
6552 0 : res = mHTMLEditor->IsEmptyNode(prevItem, &bIsEmptyNode);
6553 0 : NS_ENSURE_SUCCESS(res, res);
6554 0 : if (bIsEmptyNode)
6555 : {
6556 0 : nsCOMPtr<nsIDOMNode> brNode;
6557 0 : res = CreateMozBR(prevItem, 0, address_of(brNode));
6558 0 : NS_ENSURE_SUCCESS(res, res);
6559 : }
6560 : }
6561 :
6562 : // if the new (righthand) header node is empty, delete it
6563 : bool isEmpty;
6564 0 : res = IsEmptyBlock(aHeader, &isEmpty, true);
6565 0 : NS_ENSURE_SUCCESS(res, res);
6566 0 : if (isEmpty)
6567 : {
6568 0 : res = mHTMLEditor->DeleteNode(aHeader);
6569 0 : NS_ENSURE_SUCCESS(res, res);
6570 : // layout tells the caret to blink in a weird place
6571 : // if we don't place a break after the header.
6572 0 : nsCOMPtr<nsIDOMNode> sibling;
6573 0 : res = mHTMLEditor->GetNextHTMLSibling(headerParent, offset+1, address_of(sibling));
6574 0 : NS_ENSURE_SUCCESS(res, res);
6575 0 : if (!sibling || !nsTextEditUtils::IsBreak(sibling))
6576 : {
6577 : // create a paragraph
6578 0 : NS_NAMED_LITERAL_STRING(pType, "p");
6579 0 : nsCOMPtr<nsIDOMNode> pNode;
6580 0 : res = mHTMLEditor->CreateNode(pType, headerParent, offset+1, getter_AddRefs(pNode));
6581 0 : NS_ENSURE_SUCCESS(res, res);
6582 :
6583 : // append a <br> to it
6584 0 : nsCOMPtr<nsIDOMNode> brNode;
6585 0 : res = mHTMLEditor->CreateBR(pNode, 0, address_of(brNode));
6586 0 : NS_ENSURE_SUCCESS(res, res);
6587 :
6588 : // set selection to before the break
6589 0 : res = aSelection->Collapse(pNode, 0);
6590 : }
6591 : else
6592 : {
6593 0 : res = nsEditor::GetNodeLocation(sibling, address_of(headerParent), &offset);
6594 0 : NS_ENSURE_SUCCESS(res, res);
6595 : // put selection after break
6596 0 : res = aSelection->Collapse(headerParent,offset+1);
6597 : }
6598 : }
6599 : else
6600 : {
6601 : // put selection at front of righthand heading
6602 0 : res = aSelection->Collapse(aHeader,0);
6603 : }
6604 0 : return res;
6605 : }
6606 :
6607 : ///////////////////////////////////////////////////////////////////////////
6608 : // ReturnInParagraph: do the right thing for returns pressed in paragraphs
6609 : //
6610 : nsresult
6611 0 : nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
6612 : nsIDOMNode *aPara,
6613 : nsIDOMNode *aNode,
6614 : PRInt32 aOffset,
6615 : bool *aCancel,
6616 : bool *aHandled)
6617 : {
6618 0 : if (!aSelection || !aPara || !aNode || !aCancel || !aHandled)
6619 0 : { return NS_ERROR_NULL_POINTER; }
6620 0 : *aCancel = false;
6621 0 : *aHandled = false;
6622 :
6623 0 : nsCOMPtr<nsIDOMNode> parent;
6624 : PRInt32 offset;
6625 0 : nsresult res = nsEditor::GetNodeLocation(aNode, address_of(parent), &offset);
6626 0 : NS_ENSURE_SUCCESS(res, res);
6627 :
6628 : bool doesCRCreateNewP;
6629 0 : res = mHTMLEditor->GetReturnInParagraphCreatesNewParagraph(&doesCRCreateNewP);
6630 0 : NS_ENSURE_SUCCESS(res, res);
6631 :
6632 0 : bool newBRneeded = false;
6633 0 : nsCOMPtr<nsIDOMNode> sibling;
6634 :
6635 0 : if (aNode == aPara && doesCRCreateNewP) {
6636 : // we are at the edges of the block, newBRneeded not needed!
6637 0 : sibling = aNode;
6638 : }
6639 0 : else if (mHTMLEditor->IsTextNode(aNode))
6640 : {
6641 0 : nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(aNode);
6642 : PRUint32 strLength;
6643 0 : res = textNode->GetLength(&strLength);
6644 0 : NS_ENSURE_SUCCESS(res, res);
6645 :
6646 : // at beginning of text node?
6647 0 : if (!aOffset)
6648 : {
6649 : // is there a BR prior to it?
6650 0 : mHTMLEditor->GetPriorHTMLSibling(aNode, address_of(sibling));
6651 0 : if (!sibling ||
6652 0 : !mHTMLEditor->IsVisBreak(sibling) || nsTextEditUtils::HasMozAttr(sibling))
6653 : {
6654 0 : newBRneeded = true;
6655 : }
6656 : }
6657 0 : else if (aOffset == (PRInt32)strLength)
6658 : {
6659 : // we're at the end of text node...
6660 : // is there a BR after to it?
6661 0 : res = mHTMLEditor->GetNextHTMLSibling(aNode, address_of(sibling));
6662 0 : if (!sibling ||
6663 0 : !mHTMLEditor->IsVisBreak(sibling) || nsTextEditUtils::HasMozAttr(sibling))
6664 : {
6665 0 : newBRneeded = true;
6666 0 : offset++;
6667 : }
6668 : }
6669 : else
6670 : {
6671 0 : if (doesCRCreateNewP)
6672 : {
6673 0 : nsCOMPtr<nsIDOMNode> tmp;
6674 0 : res = mEditor->SplitNode(aNode, aOffset, getter_AddRefs(tmp));
6675 0 : NS_ENSURE_SUCCESS(res, res);
6676 0 : aNode = tmp;
6677 : }
6678 :
6679 0 : newBRneeded = true;
6680 0 : offset++;
6681 : }
6682 : }
6683 : else
6684 : {
6685 : // not in a text node.
6686 : // is there a BR prior to it?
6687 0 : nsCOMPtr<nsIDOMNode> nearNode, selNode = aNode;
6688 0 : res = mHTMLEditor->GetPriorHTMLNode(aNode, aOffset, address_of(nearNode));
6689 0 : NS_ENSURE_SUCCESS(res, res);
6690 0 : if (!nearNode || !mHTMLEditor->IsVisBreak(nearNode) || nsTextEditUtils::HasMozAttr(nearNode))
6691 : {
6692 : // is there a BR after it?
6693 0 : res = mHTMLEditor->GetNextHTMLNode(aNode, aOffset, address_of(nearNode));
6694 0 : NS_ENSURE_SUCCESS(res, res);
6695 0 : if (!nearNode || !mHTMLEditor->IsVisBreak(nearNode) || nsTextEditUtils::HasMozAttr(nearNode))
6696 : {
6697 0 : newBRneeded = true;
6698 : }
6699 : }
6700 0 : if (!newBRneeded)
6701 0 : sibling = nearNode;
6702 : }
6703 0 : if (newBRneeded)
6704 : {
6705 : // if CR does not create a new P, default to BR creation
6706 0 : NS_ENSURE_TRUE(doesCRCreateNewP, NS_OK);
6707 :
6708 0 : nsCOMPtr<nsIDOMNode> brNode;
6709 0 : res = mHTMLEditor->CreateBR(parent, offset, address_of(brNode));
6710 0 : sibling = brNode;
6711 : }
6712 0 : nsCOMPtr<nsIDOMNode> selNode = aNode;
6713 0 : *aHandled = true;
6714 0 : return SplitParagraph(aPara, sibling, aSelection, address_of(selNode), &aOffset);
6715 : }
6716 :
6717 : ///////////////////////////////////////////////////////////////////////////
6718 : // SplitParagraph: split a paragraph at selection point, possibly deleting a br
6719 : //
6720 : nsresult
6721 0 : nsHTMLEditRules::SplitParagraph(nsIDOMNode *aPara,
6722 : nsIDOMNode *aBRNode,
6723 : nsISelection *aSelection,
6724 : nsCOMPtr<nsIDOMNode> *aSelNode,
6725 : PRInt32 *aOffset)
6726 : {
6727 0 : NS_ENSURE_TRUE(aPara && aBRNode && aSelNode && *aSelNode && aOffset && aSelection, NS_ERROR_NULL_POINTER);
6728 0 : nsresult res = NS_OK;
6729 :
6730 : // split para
6731 : PRInt32 newOffset;
6732 : // get ws code to adjust any ws
6733 0 : nsCOMPtr<nsIDOMNode> leftPara, rightPara;
6734 0 : res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor, aSelNode, aOffset);
6735 0 : NS_ENSURE_SUCCESS(res, res);
6736 : // split the paragraph
6737 : res = mHTMLEditor->SplitNodeDeep(aPara, *aSelNode, *aOffset, &newOffset, false,
6738 0 : address_of(leftPara), address_of(rightPara));
6739 0 : NS_ENSURE_SUCCESS(res, res);
6740 : // get rid of the break, if it is visible (otherwise it may be needed to prevent an empty p)
6741 0 : if (mHTMLEditor->IsVisBreak(aBRNode))
6742 : {
6743 0 : res = mHTMLEditor->DeleteNode(aBRNode);
6744 0 : NS_ENSURE_SUCCESS(res, res);
6745 : }
6746 :
6747 : // remove ID attribute on the paragraph we just created
6748 0 : nsCOMPtr<nsIDOMElement> rightElt = do_QueryInterface(rightPara);
6749 0 : res = mHTMLEditor->RemoveAttribute(rightElt, NS_LITERAL_STRING("id"));
6750 0 : NS_ENSURE_SUCCESS(res, res);
6751 :
6752 : // check both halves of para to see if we need mozBR
6753 0 : res = InsertMozBRIfNeeded(leftPara);
6754 0 : NS_ENSURE_SUCCESS(res, res);
6755 0 : res = InsertMozBRIfNeeded(rightPara);
6756 0 : NS_ENSURE_SUCCESS(res, res);
6757 :
6758 : // selection to beginning of right hand para;
6759 : // look inside any containers that are up front.
6760 0 : nsCOMPtr<nsIDOMNode> child = mHTMLEditor->GetLeftmostChild(rightPara, true);
6761 0 : if (mHTMLEditor->IsTextNode(child) || mHTMLEditor->IsContainer(child))
6762 : {
6763 0 : aSelection->Collapse(child,0);
6764 : }
6765 : else
6766 : {
6767 0 : nsCOMPtr<nsIDOMNode> parent;
6768 : PRInt32 offset;
6769 0 : res = nsEditor::GetNodeLocation(child, address_of(parent), &offset);
6770 0 : aSelection->Collapse(parent,offset);
6771 : }
6772 0 : return res;
6773 : }
6774 :
6775 :
6776 : ///////////////////////////////////////////////////////////////////////////
6777 : // ReturnInListItem: do the right thing for returns pressed in list items
6778 : //
6779 : nsresult
6780 0 : nsHTMLEditRules::ReturnInListItem(nsISelection *aSelection,
6781 : nsIDOMNode *aListItem,
6782 : nsIDOMNode *aNode,
6783 : PRInt32 aOffset)
6784 : {
6785 0 : NS_ENSURE_TRUE(aSelection && aListItem && aNode, NS_ERROR_NULL_POINTER);
6786 0 : nsCOMPtr<nsISelection> selection(aSelection);
6787 0 : nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
6788 0 : nsresult res = NS_OK;
6789 :
6790 0 : nsCOMPtr<nsIDOMNode> listitem;
6791 :
6792 : // sanity check
6793 0 : NS_PRECONDITION(true == nsHTMLEditUtils::IsListItem(aListItem),
6794 : "expected a list item and didn't get one");
6795 :
6796 : // get the listitem parent and the active editing host.
6797 0 : nsIContent* rootContent = mHTMLEditor->GetActiveEditingHost();
6798 0 : nsCOMPtr<nsIDOMNode> rootNode = do_QueryInterface(rootContent);
6799 0 : nsCOMPtr<nsIDOMNode> list;
6800 : PRInt32 itemOffset;
6801 0 : res = nsEditor::GetNodeLocation(aListItem, address_of(list), &itemOffset);
6802 0 : NS_ENSURE_SUCCESS(res, res);
6803 :
6804 : // if we are in an empty listitem, then we want to pop up out of the list
6805 : // but only if prefs says it's ok and if the parent isn't the active editing host.
6806 : bool isEmpty;
6807 0 : res = IsEmptyBlock(aListItem, &isEmpty, true, false);
6808 0 : NS_ENSURE_SUCCESS(res, res);
6809 0 : if (isEmpty && (rootNode != list) && mReturnInEmptyLIKillsList)
6810 : {
6811 : // get the list offset now -- before we might eventually split the list
6812 0 : nsCOMPtr<nsIDOMNode> listparent;
6813 : PRInt32 offset;
6814 0 : res = nsEditor::GetNodeLocation(list, address_of(listparent), &offset);
6815 0 : NS_ENSURE_SUCCESS(res, res);
6816 :
6817 : // are we the last list item in the list?
6818 : bool bIsLast;
6819 0 : res = mHTMLEditor->IsLastEditableChild(aListItem, &bIsLast);
6820 0 : NS_ENSURE_SUCCESS(res, res);
6821 0 : if (!bIsLast)
6822 : {
6823 : // we need to split the list!
6824 0 : nsCOMPtr<nsIDOMNode> tempNode;
6825 0 : res = mHTMLEditor->SplitNode(list, itemOffset, getter_AddRefs(tempNode));
6826 0 : NS_ENSURE_SUCCESS(res, res);
6827 : }
6828 :
6829 : // are we in a sublist?
6830 0 : if (nsHTMLEditUtils::IsList(listparent)) //in a sublist
6831 : {
6832 : // if so, move this list item out of this list and into the grandparent list
6833 0 : res = mHTMLEditor->MoveNode(aListItem,listparent,offset+1);
6834 0 : NS_ENSURE_SUCCESS(res, res);
6835 0 : res = aSelection->Collapse(aListItem,0);
6836 : }
6837 : else
6838 : {
6839 : // otherwise kill this listitem
6840 0 : res = mHTMLEditor->DeleteNode(aListItem);
6841 0 : NS_ENSURE_SUCCESS(res, res);
6842 :
6843 : // time to insert a paragraph
6844 0 : NS_NAMED_LITERAL_STRING(pType, "p");
6845 0 : nsCOMPtr<nsIDOMNode> pNode;
6846 0 : res = mHTMLEditor->CreateNode(pType, listparent, offset+1, getter_AddRefs(pNode));
6847 0 : NS_ENSURE_SUCCESS(res, res);
6848 :
6849 : // append a <br> to it
6850 0 : nsCOMPtr<nsIDOMNode> brNode;
6851 0 : res = mHTMLEditor->CreateBR(pNode, 0, address_of(brNode));
6852 0 : NS_ENSURE_SUCCESS(res, res);
6853 :
6854 : // set selection to before the break
6855 0 : res = aSelection->Collapse(pNode, 0);
6856 : }
6857 0 : return res;
6858 : }
6859 :
6860 : // else we want a new list item at the same list level.
6861 : // get ws code to adjust any ws
6862 0 : nsCOMPtr<nsIDOMNode> selNode = aNode;
6863 0 : res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor, address_of(selNode), &aOffset);
6864 0 : NS_ENSURE_SUCCESS(res, res);
6865 : // now split list item
6866 : PRInt32 newOffset;
6867 0 : res = mHTMLEditor->SplitNodeDeep( aListItem, selNode, aOffset, &newOffset, false);
6868 0 : NS_ENSURE_SUCCESS(res, res);
6869 : // hack: until I can change the damaged doc range code back to being
6870 : // extra inclusive, I have to manually detect certain list items that
6871 : // may be left empty.
6872 0 : nsCOMPtr<nsIDOMNode> prevItem;
6873 0 : mHTMLEditor->GetPriorHTMLSibling(aListItem, address_of(prevItem));
6874 :
6875 0 : if (prevItem && nsHTMLEditUtils::IsListItem(prevItem))
6876 : {
6877 : bool bIsEmptyNode;
6878 0 : res = mHTMLEditor->IsEmptyNode(prevItem, &bIsEmptyNode);
6879 0 : NS_ENSURE_SUCCESS(res, res);
6880 0 : if (bIsEmptyNode)
6881 : {
6882 0 : nsCOMPtr<nsIDOMNode> brNode;
6883 0 : res = CreateMozBR(prevItem, 0, address_of(brNode));
6884 0 : NS_ENSURE_SUCCESS(res, res);
6885 : }
6886 : else
6887 : {
6888 0 : res = mHTMLEditor->IsEmptyNode(aListItem, &bIsEmptyNode, true);
6889 0 : NS_ENSURE_SUCCESS(res, res);
6890 0 : if (bIsEmptyNode)
6891 : {
6892 0 : nsCOMPtr<nsIAtom> nodeAtom = nsEditor::GetTag(aListItem);
6893 0 : if (nodeAtom == nsEditProperty::dd || nodeAtom == nsEditProperty::dt)
6894 : {
6895 0 : nsCOMPtr<nsIDOMNode> list;
6896 : PRInt32 itemOffset;
6897 0 : res = nsEditor::GetNodeLocation(aListItem, address_of(list), &itemOffset);
6898 0 : NS_ENSURE_SUCCESS(res, res);
6899 :
6900 0 : nsAutoString listTag((nodeAtom == nsEditProperty::dt) ? NS_LITERAL_STRING("dd") : NS_LITERAL_STRING("dt"));
6901 0 : nsCOMPtr<nsIDOMNode> newListItem;
6902 0 : res = mHTMLEditor->CreateNode(listTag, list, itemOffset+1, getter_AddRefs(newListItem));
6903 0 : NS_ENSURE_SUCCESS(res, res);
6904 0 : res = mEditor->DeleteNode(aListItem);
6905 0 : NS_ENSURE_SUCCESS(res, res);
6906 0 : return aSelection->Collapse(newListItem, 0);
6907 : }
6908 :
6909 0 : nsCOMPtr<nsIDOMNode> brNode;
6910 0 : res = mHTMLEditor->CopyLastEditableChildStyles(prevItem, aListItem, getter_AddRefs(brNode));
6911 0 : NS_ENSURE_SUCCESS(res, res);
6912 0 : if (brNode)
6913 : {
6914 0 : nsCOMPtr<nsIDOMNode> brParent;
6915 : PRInt32 offset;
6916 0 : res = nsEditor::GetNodeLocation(brNode, address_of(brParent), &offset);
6917 0 : return aSelection->Collapse(brParent, offset);
6918 : }
6919 : }
6920 : else
6921 : {
6922 0 : nsWSRunObject wsObj(mHTMLEditor, aListItem, 0);
6923 0 : nsCOMPtr<nsIDOMNode> visNode;
6924 0 : PRInt32 visOffset = 0;
6925 : PRInt16 wsType;
6926 0 : res = wsObj.NextVisibleNode(aListItem, 0, address_of(visNode), &visOffset, &wsType);
6927 0 : NS_ENSURE_SUCCESS(res, res);
6928 0 : if ( (wsType==nsWSRunObject::eSpecial) ||
6929 : (wsType==nsWSRunObject::eBreak) ||
6930 0 : nsHTMLEditUtils::IsHR(visNode) )
6931 : {
6932 0 : nsCOMPtr<nsIDOMNode> parent;
6933 : PRInt32 offset;
6934 0 : res = nsEditor::GetNodeLocation(visNode, address_of(parent), &offset);
6935 0 : NS_ENSURE_SUCCESS(res, res);
6936 0 : return aSelection->Collapse(parent, offset);
6937 : }
6938 : else
6939 : {
6940 0 : return aSelection->Collapse(visNode, visOffset);
6941 : }
6942 : }
6943 : }
6944 : }
6945 0 : res = aSelection->Collapse(aListItem,0);
6946 0 : return res;
6947 : }
6948 :
6949 :
6950 : ///////////////////////////////////////////////////////////////////////////
6951 : // MakeBlockquote: put the list of nodes into one or more blockquotes.
6952 : //
6953 : nsresult
6954 0 : nsHTMLEditRules::MakeBlockquote(nsCOMArray<nsIDOMNode>& arrayOfNodes)
6955 : {
6956 : // the idea here is to put the nodes into a minimal number of
6957 : // blockquotes. When the user blockquotes something, they expect
6958 : // one blockquote. That may not be possible (for instance, if they
6959 : // have two table cells selected, you need two blockquotes inside the cells).
6960 :
6961 0 : nsresult res = NS_OK;
6962 :
6963 0 : nsCOMPtr<nsIDOMNode> curNode, curParent, curBlock, newBlock;
6964 : PRInt32 offset;
6965 0 : PRInt32 listCount = arrayOfNodes.Count();
6966 :
6967 0 : nsCOMPtr<nsIDOMNode> prevParent;
6968 :
6969 : PRInt32 i;
6970 0 : for (i=0; i<listCount; i++)
6971 : {
6972 : // get the node to act on, and its location
6973 0 : curNode = arrayOfNodes[i];
6974 0 : res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset);
6975 0 : NS_ENSURE_SUCCESS(res, res);
6976 :
6977 : // if the node is a table element or list item, dive inside
6978 0 : if (nsHTMLEditUtils::IsTableElementButNotTable(curNode) ||
6979 0 : nsHTMLEditUtils::IsListItem(curNode))
6980 : {
6981 0 : curBlock = 0; // forget any previous block
6982 : // recursion time
6983 0 : nsCOMArray<nsIDOMNode> childArray;
6984 0 : res = GetChildNodesForOperation(curNode, childArray);
6985 0 : NS_ENSURE_SUCCESS(res, res);
6986 0 : res = MakeBlockquote(childArray);
6987 0 : NS_ENSURE_SUCCESS(res, res);
6988 : }
6989 :
6990 : // if the node has different parent than previous node,
6991 : // further nodes in a new parent
6992 0 : if (prevParent)
6993 : {
6994 0 : nsCOMPtr<nsIDOMNode> temp;
6995 0 : curNode->GetParentNode(getter_AddRefs(temp));
6996 0 : if (temp != prevParent)
6997 : {
6998 0 : curBlock = 0; // forget any previous blockquote node we were using
6999 0 : prevParent = temp;
7000 : }
7001 : }
7002 : else
7003 :
7004 : {
7005 0 : curNode->GetParentNode(getter_AddRefs(prevParent));
7006 : }
7007 :
7008 : // if no curBlock, make one
7009 0 : if (!curBlock)
7010 : {
7011 0 : NS_NAMED_LITERAL_STRING(quoteType, "blockquote");
7012 0 : res = SplitAsNeeded("eType, address_of(curParent), &offset);
7013 0 : NS_ENSURE_SUCCESS(res, res);
7014 0 : res = mHTMLEditor->CreateNode(quoteType, curParent, offset, getter_AddRefs(curBlock));
7015 0 : NS_ENSURE_SUCCESS(res, res);
7016 : // remember our new block for postprocessing
7017 0 : mNewBlock = curBlock;
7018 : // note: doesn't matter if we set mNewBlock multiple times.
7019 : }
7020 :
7021 0 : res = mHTMLEditor->MoveNode(curNode, curBlock, -1);
7022 0 : NS_ENSURE_SUCCESS(res, res);
7023 : }
7024 0 : return res;
7025 : }
7026 :
7027 :
7028 :
7029 : ///////////////////////////////////////////////////////////////////////////
7030 : // RemoveBlockStyle: make the nodes have no special block type.
7031 : //
7032 : nsresult
7033 0 : nsHTMLEditRules::RemoveBlockStyle(nsCOMArray<nsIDOMNode>& arrayOfNodes)
7034 : {
7035 : // intent of this routine is to be used for converting to/from
7036 : // headers, paragraphs, pre, and address. Those blocks
7037 : // that pretty much just contain inline things...
7038 :
7039 0 : nsresult res = NS_OK;
7040 :
7041 0 : nsCOMPtr<nsIDOMNode> curNode, curParent, curBlock, firstNode, lastNode;
7042 : PRInt32 offset;
7043 0 : PRInt32 listCount = arrayOfNodes.Count();
7044 :
7045 : PRInt32 i;
7046 0 : for (i=0; i<listCount; i++)
7047 : {
7048 : // get the node to act on, and its location
7049 0 : curNode = arrayOfNodes[i];
7050 0 : res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset);
7051 0 : NS_ENSURE_SUCCESS(res, res);
7052 0 : nsAutoString curNodeTag, curBlockTag;
7053 0 : nsEditor::GetTagString(curNode, curNodeTag);
7054 0 : ToLowerCase(curNodeTag);
7055 :
7056 : // if curNode is a address, p, header, address, or pre, remove it
7057 0 : if (nsHTMLEditUtils::IsFormatNode(curNode))
7058 : {
7059 : // process any partial progress saved
7060 0 : if (curBlock)
7061 : {
7062 0 : res = RemovePartOfBlock(curBlock, firstNode, lastNode);
7063 0 : NS_ENSURE_SUCCESS(res, res);
7064 0 : curBlock = 0; firstNode = 0; lastNode = 0;
7065 : }
7066 : // remove curent block
7067 0 : res = mHTMLEditor->RemoveBlockContainer(curNode);
7068 0 : NS_ENSURE_SUCCESS(res, res);
7069 : }
7070 0 : else if (nsHTMLEditUtils::IsTable(curNode) ||
7071 0 : nsHTMLEditUtils::IsTableRow(curNode) ||
7072 0 : (curNodeTag.EqualsLiteral("tbody")) ||
7073 0 : (curNodeTag.EqualsLiteral("td")) ||
7074 0 : nsHTMLEditUtils::IsList(curNode) ||
7075 0 : (curNodeTag.EqualsLiteral("li")) ||
7076 0 : nsHTMLEditUtils::IsBlockquote(curNode) ||
7077 0 : nsHTMLEditUtils::IsDiv(curNode))
7078 : {
7079 : // process any partial progress saved
7080 0 : if (curBlock)
7081 : {
7082 0 : res = RemovePartOfBlock(curBlock, firstNode, lastNode);
7083 0 : NS_ENSURE_SUCCESS(res, res);
7084 0 : curBlock = 0; firstNode = 0; lastNode = 0;
7085 : }
7086 : // recursion time
7087 0 : nsCOMArray<nsIDOMNode> childArray;
7088 0 : res = GetChildNodesForOperation(curNode, childArray);
7089 0 : NS_ENSURE_SUCCESS(res, res);
7090 0 : res = RemoveBlockStyle(childArray);
7091 0 : NS_ENSURE_SUCCESS(res, res);
7092 : }
7093 0 : else if (IsInlineNode(curNode))
7094 : {
7095 0 : if (curBlock)
7096 : {
7097 : // if so, is this node a descendant?
7098 0 : if (nsEditorUtils::IsDescendantOf(curNode, curBlock))
7099 : {
7100 0 : lastNode = curNode;
7101 0 : continue; // then we don't need to do anything different for this node
7102 : }
7103 : else
7104 : {
7105 : // otherwise, we have progressed beyond end of curBlock,
7106 : // so lets handle it now. We need to remove the portion of
7107 : // curBlock that contains [firstNode - lastNode].
7108 0 : res = RemovePartOfBlock(curBlock, firstNode, lastNode);
7109 0 : NS_ENSURE_SUCCESS(res, res);
7110 0 : curBlock = 0; firstNode = 0; lastNode = 0;
7111 : // fall out and handle curNode
7112 : }
7113 : }
7114 0 : curBlock = mHTMLEditor->GetBlockNodeParent(curNode);
7115 0 : if (nsHTMLEditUtils::IsFormatNode(curBlock))
7116 : {
7117 0 : firstNode = curNode;
7118 0 : lastNode = curNode;
7119 : }
7120 : else
7121 0 : curBlock = 0; // not a block kind that we care about.
7122 : }
7123 : else
7124 : { // some node that is already sans block style. skip over it and
7125 : // process any partial progress saved
7126 0 : if (curBlock)
7127 : {
7128 0 : res = RemovePartOfBlock(curBlock, firstNode, lastNode);
7129 0 : NS_ENSURE_SUCCESS(res, res);
7130 0 : curBlock = 0; firstNode = 0; lastNode = 0;
7131 : }
7132 : }
7133 : }
7134 : // process any partial progress saved
7135 0 : if (curBlock)
7136 : {
7137 0 : res = RemovePartOfBlock(curBlock, firstNode, lastNode);
7138 0 : NS_ENSURE_SUCCESS(res, res);
7139 0 : curBlock = 0; firstNode = 0; lastNode = 0;
7140 : }
7141 0 : return res;
7142 : }
7143 :
7144 :
7145 : ///////////////////////////////////////////////////////////////////////////
7146 : // ApplyBlockStyle: do whatever it takes to make the list of nodes into
7147 : // one or more blocks of type blockTag.
7148 : //
7149 : nsresult
7150 0 : nsHTMLEditRules::ApplyBlockStyle(nsCOMArray<nsIDOMNode>& arrayOfNodes, const nsAString *aBlockTag)
7151 : {
7152 : // intent of this routine is to be used for converting to/from
7153 : // headers, paragraphs, pre, and address. Those blocks
7154 : // that pretty much just contain inline things...
7155 :
7156 0 : NS_ENSURE_TRUE(aBlockTag, NS_ERROR_NULL_POINTER);
7157 0 : nsresult res = NS_OK;
7158 :
7159 0 : nsCOMPtr<nsIDOMNode> curNode, curParent, curBlock, newBlock;
7160 : PRInt32 offset;
7161 0 : PRInt32 listCount = arrayOfNodes.Count();
7162 0 : nsString tString(*aBlockTag);////MJUDGE SCC NEED HELP
7163 :
7164 : // Remove all non-editable nodes. Leave them be.
7165 : PRInt32 j;
7166 0 : for (j=listCount-1; j>=0; j--)
7167 : {
7168 0 : if (!mHTMLEditor->IsEditable(arrayOfNodes[j]))
7169 : {
7170 0 : arrayOfNodes.RemoveObjectAt(j);
7171 : }
7172 : }
7173 :
7174 : // reset list count
7175 0 : listCount = arrayOfNodes.Count();
7176 :
7177 : PRInt32 i;
7178 0 : for (i=0; i<listCount; i++)
7179 : {
7180 : // get the node to act on, and its location
7181 0 : curNode = arrayOfNodes[i];
7182 0 : res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset);
7183 0 : NS_ENSURE_SUCCESS(res, res);
7184 0 : nsAutoString curNodeTag;
7185 0 : nsEditor::GetTagString(curNode, curNodeTag);
7186 0 : ToLowerCase(curNodeTag);
7187 :
7188 : // is it already the right kind of block?
7189 0 : if (curNodeTag == *aBlockTag)
7190 : {
7191 0 : curBlock = 0; // forget any previous block used for previous inline nodes
7192 0 : continue; // do nothing to this block
7193 : }
7194 :
7195 : // if curNode is a address, p, header, address, or pre, replace
7196 : // it with a new block of correct type.
7197 : // xxx floppy moose: pre can't hold everything the others can
7198 0 : if (nsHTMLEditUtils::IsMozDiv(curNode) ||
7199 0 : nsHTMLEditUtils::IsFormatNode(curNode))
7200 : {
7201 0 : curBlock = 0; // forget any previous block used for previous inline nodes
7202 : res = mHTMLEditor->ReplaceContainer(curNode, address_of(newBlock), *aBlockTag,
7203 0 : nsnull, nsnull, true);
7204 0 : NS_ENSURE_SUCCESS(res, res);
7205 : }
7206 0 : else if (nsHTMLEditUtils::IsTable(curNode) ||
7207 0 : (curNodeTag.EqualsLiteral("tbody")) ||
7208 0 : (curNodeTag.EqualsLiteral("tr")) ||
7209 0 : (curNodeTag.EqualsLiteral("td")) ||
7210 0 : nsHTMLEditUtils::IsList(curNode) ||
7211 0 : (curNodeTag.EqualsLiteral("li")) ||
7212 0 : nsHTMLEditUtils::IsBlockquote(curNode) ||
7213 0 : nsHTMLEditUtils::IsDiv(curNode))
7214 : {
7215 0 : curBlock = 0; // forget any previous block used for previous inline nodes
7216 : // recursion time
7217 0 : nsCOMArray<nsIDOMNode> childArray;
7218 0 : res = GetChildNodesForOperation(curNode, childArray);
7219 0 : NS_ENSURE_SUCCESS(res, res);
7220 0 : PRInt32 childCount = childArray.Count();
7221 0 : if (childCount)
7222 : {
7223 0 : res = ApplyBlockStyle(childArray, aBlockTag);
7224 0 : NS_ENSURE_SUCCESS(res, res);
7225 : }
7226 : else
7227 : {
7228 : // make sure we can put a block here
7229 0 : res = SplitAsNeeded(aBlockTag, address_of(curParent), &offset);
7230 0 : NS_ENSURE_SUCCESS(res, res);
7231 0 : nsCOMPtr<nsIDOMNode> theBlock;
7232 0 : res = mHTMLEditor->CreateNode(*aBlockTag, curParent, offset, getter_AddRefs(theBlock));
7233 0 : NS_ENSURE_SUCCESS(res, res);
7234 : // remember our new block for postprocessing
7235 0 : mNewBlock = theBlock;
7236 : }
7237 : }
7238 :
7239 : // if the node is a break, we honor it by putting further nodes in a new parent
7240 0 : else if (curNodeTag.EqualsLiteral("br"))
7241 : {
7242 0 : if (curBlock)
7243 : {
7244 0 : curBlock = 0; // forget any previous block used for previous inline nodes
7245 0 : res = mHTMLEditor->DeleteNode(curNode);
7246 0 : NS_ENSURE_SUCCESS(res, res);
7247 : }
7248 : else
7249 : {
7250 : // the break is the first (or even only) node we encountered. Create a
7251 : // block for it.
7252 0 : res = SplitAsNeeded(aBlockTag, address_of(curParent), &offset);
7253 0 : NS_ENSURE_SUCCESS(res, res);
7254 0 : res = mHTMLEditor->CreateNode(*aBlockTag, curParent, offset, getter_AddRefs(curBlock));
7255 0 : NS_ENSURE_SUCCESS(res, res);
7256 : // remember our new block for postprocessing
7257 0 : mNewBlock = curBlock;
7258 : // note: doesn't matter if we set mNewBlock multiple times.
7259 0 : res = mHTMLEditor->MoveNode(curNode, curBlock, -1);
7260 0 : NS_ENSURE_SUCCESS(res, res);
7261 : }
7262 : }
7263 :
7264 :
7265 : // if curNode is inline, pull it into curBlock
7266 : // note: it's assumed that consecutive inline nodes in the
7267 : // arrayOfNodes are actually members of the same block parent.
7268 : // this happens to be true now as a side effect of how
7269 : // arrayOfNodes is contructed, but some additional logic should
7270 : // be added here if that should change
7271 :
7272 0 : else if (IsInlineNode(curNode))
7273 : {
7274 : // if curNode is a non editable, drop it if we are going to <pre>
7275 0 : if (tString.LowerCaseEqualsLiteral("pre")
7276 0 : && (!mHTMLEditor->IsEditable(curNode)))
7277 0 : continue; // do nothing to this block
7278 :
7279 : // if no curBlock, make one
7280 0 : if (!curBlock)
7281 : {
7282 0 : res = SplitAsNeeded(aBlockTag, address_of(curParent), &offset);
7283 0 : NS_ENSURE_SUCCESS(res, res);
7284 0 : res = mHTMLEditor->CreateNode(*aBlockTag, curParent, offset, getter_AddRefs(curBlock));
7285 0 : NS_ENSURE_SUCCESS(res, res);
7286 : // remember our new block for postprocessing
7287 0 : mNewBlock = curBlock;
7288 : // note: doesn't matter if we set mNewBlock multiple times.
7289 : }
7290 :
7291 : // if curNode is a Break, replace it with a return if we are going to <pre>
7292 : // xxx floppy moose
7293 :
7294 : // this is a continuation of some inline nodes that belong together in
7295 : // the same block item. use curBlock
7296 0 : res = mHTMLEditor->MoveNode(curNode, curBlock, -1);
7297 0 : NS_ENSURE_SUCCESS(res, res);
7298 : }
7299 : }
7300 0 : return res;
7301 : }
7302 :
7303 :
7304 : ///////////////////////////////////////////////////////////////////////////
7305 : // SplitAsNeeded: given a tag name, split inOutParent up to the point
7306 : // where we can insert the tag. Adjust inOutParent and
7307 : // inOutOffset to pint to new location for tag.
7308 : nsresult
7309 0 : nsHTMLEditRules::SplitAsNeeded(const nsAString *aTag,
7310 : nsCOMPtr<nsIDOMNode> *inOutParent,
7311 : PRInt32 *inOutOffset)
7312 : {
7313 0 : NS_ENSURE_TRUE(aTag && inOutParent && inOutOffset, NS_ERROR_NULL_POINTER);
7314 0 : NS_ENSURE_TRUE(*inOutParent, NS_ERROR_NULL_POINTER);
7315 0 : nsCOMPtr<nsIDOMNode> tagParent, temp, splitNode, parent = *inOutParent;
7316 0 : nsresult res = NS_OK;
7317 :
7318 : // check that we have a place that can legally contain the tag
7319 0 : while (!tagParent)
7320 : {
7321 : // sniffing up the parent tree until we find
7322 : // a legal place for the block
7323 0 : if (!parent) break;
7324 : // Don't leave the active editing host
7325 0 : if (!mHTMLEditor->IsNodeInActiveEditor(parent)) {
7326 0 : nsCOMPtr<nsIContent> parentContent = do_QueryInterface(parent);
7327 0 : if (parentContent != mHTMLEditor->GetActiveEditingHost()) {
7328 : break;
7329 : }
7330 : }
7331 0 : if (mHTMLEditor->CanContainTag(parent, *aTag))
7332 : {
7333 0 : tagParent = parent;
7334 0 : break;
7335 : }
7336 0 : splitNode = parent;
7337 0 : parent->GetParentNode(getter_AddRefs(temp));
7338 0 : parent = temp;
7339 : }
7340 0 : if (!tagParent)
7341 : {
7342 : // could not find a place to build tag!
7343 0 : return NS_ERROR_FAILURE;
7344 : }
7345 0 : if (splitNode)
7346 : {
7347 : // we found a place for block, but above inOutParent. We need to split nodes.
7348 0 : res = mHTMLEditor->SplitNodeDeep(splitNode, *inOutParent, *inOutOffset, inOutOffset);
7349 0 : NS_ENSURE_SUCCESS(res, res);
7350 0 : *inOutParent = tagParent;
7351 : }
7352 0 : return res;
7353 : }
7354 :
7355 : ///////////////////////////////////////////////////////////////////////////
7356 : // JoinNodesSmart: join two nodes, doing whatever makes sense for their
7357 : // children (which often means joining them, too).
7358 : // aNodeLeft & aNodeRight must be same type of node.
7359 : nsresult
7360 0 : nsHTMLEditRules::JoinNodesSmart( nsIDOMNode *aNodeLeft,
7361 : nsIDOMNode *aNodeRight,
7362 : nsCOMPtr<nsIDOMNode> *aOutMergeParent,
7363 : PRInt32 *aOutMergeOffset)
7364 : {
7365 : // check parms
7366 0 : NS_ENSURE_TRUE(aNodeLeft &&
7367 : aNodeRight &&
7368 : aOutMergeParent &&
7369 : aOutMergeOffset, NS_ERROR_NULL_POINTER);
7370 :
7371 0 : nsresult res = NS_OK;
7372 : // caller responsible for:
7373 : // left & right node are same type
7374 : PRInt32 parOffset;
7375 0 : nsCOMPtr<nsIDOMNode> parent, rightParent;
7376 0 : res = nsEditor::GetNodeLocation(aNodeLeft, address_of(parent), &parOffset);
7377 0 : NS_ENSURE_SUCCESS(res, res);
7378 0 : aNodeRight->GetParentNode(getter_AddRefs(rightParent));
7379 :
7380 : // if they don't have the same parent, first move the 'right' node
7381 : // to after the 'left' one
7382 0 : if (parent != rightParent)
7383 : {
7384 0 : res = mHTMLEditor->MoveNode(aNodeRight, parent, parOffset);
7385 0 : NS_ENSURE_SUCCESS(res, res);
7386 : }
7387 :
7388 : // defaults for outParams
7389 0 : *aOutMergeParent = aNodeRight;
7390 0 : res = mHTMLEditor->GetLengthOfDOMNode(aNodeLeft, *((PRUint32*)aOutMergeOffset));
7391 0 : NS_ENSURE_SUCCESS(res, res);
7392 :
7393 : // separate join rules for differing blocks
7394 0 : if (nsHTMLEditUtils::IsList(aNodeLeft) ||
7395 0 : mHTMLEditor->IsTextNode(aNodeLeft))
7396 : {
7397 : // for list's, merge shallow (wouldn't want to combine list items)
7398 0 : res = mHTMLEditor->JoinNodes(aNodeLeft, aNodeRight, parent);
7399 0 : NS_ENSURE_SUCCESS(res, res);
7400 0 : return res;
7401 : }
7402 : else
7403 : {
7404 : // remember the last left child, and firt right child
7405 0 : nsCOMPtr<nsIDOMNode> lastLeft, firstRight;
7406 0 : res = mHTMLEditor->GetLastEditableChild(aNodeLeft, address_of(lastLeft));
7407 0 : NS_ENSURE_SUCCESS(res, res);
7408 0 : res = mHTMLEditor->GetFirstEditableChild(aNodeRight, address_of(firstRight));
7409 0 : NS_ENSURE_SUCCESS(res, res);
7410 :
7411 : // for list items, divs, etc, merge smart
7412 0 : res = mHTMLEditor->JoinNodes(aNodeLeft, aNodeRight, parent);
7413 0 : NS_ENSURE_SUCCESS(res, res);
7414 :
7415 0 : if (lastLeft && firstRight &&
7416 0 : mHTMLEditor->NodesSameType(lastLeft, firstRight) &&
7417 0 : (nsEditor::IsTextNode(lastLeft) ||
7418 0 : mHTMLEditor->mHTMLCSSUtils->ElementsSameStyle(lastLeft, firstRight)))
7419 0 : return JoinNodesSmart(lastLeft, firstRight, aOutMergeParent, aOutMergeOffset);
7420 : }
7421 0 : return res;
7422 : }
7423 :
7424 :
7425 : nsresult
7426 0 : nsHTMLEditRules::GetTopEnclosingMailCite(nsIDOMNode *aNode,
7427 : nsCOMPtr<nsIDOMNode> *aOutCiteNode,
7428 : bool aPlainText)
7429 : {
7430 : // check parms
7431 0 : NS_ENSURE_TRUE(aNode && aOutCiteNode, NS_ERROR_NULL_POINTER);
7432 :
7433 0 : nsresult res = NS_OK;
7434 0 : nsCOMPtr<nsIDOMNode> node, parentNode;
7435 0 : node = do_QueryInterface(aNode);
7436 :
7437 0 : while (node)
7438 : {
7439 0 : if ( (aPlainText && nsHTMLEditUtils::IsPre(node)) ||
7440 0 : nsHTMLEditUtils::IsMailCite(node) )
7441 0 : *aOutCiteNode = node;
7442 0 : if (nsTextEditUtils::IsBody(node)) break;
7443 :
7444 0 : res = node->GetParentNode(getter_AddRefs(parentNode));
7445 0 : NS_ENSURE_SUCCESS(res, res);
7446 0 : node = parentNode;
7447 : }
7448 :
7449 0 : return res;
7450 : }
7451 :
7452 :
7453 : nsresult
7454 0 : nsHTMLEditRules::CacheInlineStyles(nsIDOMNode *aNode)
7455 : {
7456 0 : NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
7457 :
7458 0 : bool useCSS = mHTMLEditor->IsCSSEnabled();
7459 :
7460 0 : for (PRInt32 j = 0; j < SIZE_STYLE_TABLE; ++j)
7461 : {
7462 0 : bool isSet = false;
7463 0 : nsAutoString outValue;
7464 0 : nsCOMPtr<nsIDOMNode> resultNode;
7465 0 : if (!useCSS)
7466 : {
7467 : mHTMLEditor->IsTextPropertySetByContent(aNode, mCachedStyles[j].tag, &(mCachedStyles[j].attr), nsnull,
7468 0 : isSet, getter_AddRefs(resultNode), &outValue);
7469 : }
7470 : else
7471 : {
7472 : mHTMLEditor->mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(aNode, mCachedStyles[j].tag, &(mCachedStyles[j].attr),
7473 0 : isSet, outValue, COMPUTED_STYLE_TYPE);
7474 : }
7475 0 : if (isSet)
7476 : {
7477 0 : mCachedStyles[j].mPresent = true;
7478 0 : mCachedStyles[j].value.Assign(outValue);
7479 : }
7480 : }
7481 0 : return NS_OK;
7482 : }
7483 :
7484 :
7485 : nsresult
7486 0 : nsHTMLEditRules::ReapplyCachedStyles()
7487 : {
7488 : // The idea here is to examine our cached list of styles
7489 : // and see if any have been removed. If so, add typeinstate
7490 : // for them, so that they will be reinserted when new
7491 : // content is added.
7492 :
7493 : // When we apply cached styles to TypeInState, we always want
7494 : // to blow away prior TypeInState:
7495 0 : mHTMLEditor->mTypeInState->Reset();
7496 :
7497 : // remember if we are in css mode
7498 0 : bool useCSS = mHTMLEditor->IsCSSEnabled();
7499 :
7500 : // get selection point
7501 0 : nsCOMPtr<nsISelection>selection;
7502 0 : nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
7503 0 : NS_ENSURE_SUCCESS(res, res);
7504 0 : nsCOMPtr<nsIDOMNode> selNode;
7505 : PRInt32 selOffset;
7506 0 : res = mHTMLEditor->GetStartNodeAndOffset(selection, getter_AddRefs(selNode), &selOffset);
7507 0 : NS_ENSURE_SUCCESS(res, res);
7508 :
7509 0 : for (PRInt32 j = 0; j < SIZE_STYLE_TABLE; ++j)
7510 : {
7511 0 : if (mCachedStyles[j].mPresent)
7512 : {
7513 : bool bFirst, bAny, bAll;
7514 0 : bFirst = bAny = bAll = false;
7515 :
7516 0 : nsAutoString curValue;
7517 0 : if (useCSS) // check computed style first in css case
7518 : {
7519 : mHTMLEditor->mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(selNode, mCachedStyles[j].tag, &(mCachedStyles[j].attr),
7520 0 : bAny, curValue, COMPUTED_STYLE_TYPE);
7521 : }
7522 0 : if (!bAny) // then check typeinstate and html style
7523 : {
7524 : res = mHTMLEditor->GetInlinePropertyBase(mCachedStyles[j].tag, &(mCachedStyles[j].attr), &(mCachedStyles[j].value),
7525 0 : &bFirst, &bAny, &bAll, &curValue, false);
7526 0 : NS_ENSURE_SUCCESS(res, res);
7527 : }
7528 : // this style has disappeared through deletion. Add it onto our typeinstate:
7529 0 : if (!bAny)
7530 : {
7531 0 : mHTMLEditor->mTypeInState->SetProp(mCachedStyles[j].tag, mCachedStyles[j].attr, mCachedStyles[j].value);
7532 : }
7533 : }
7534 : }
7535 0 : return NS_OK;
7536 : }
7537 :
7538 :
7539 : nsresult
7540 0 : nsHTMLEditRules::ClearCachedStyles()
7541 : {
7542 : // clear the mPresent bits in mCachedStyles array
7543 :
7544 : PRInt32 j;
7545 0 : for (j=0; j<SIZE_STYLE_TABLE; j++)
7546 : {
7547 0 : mCachedStyles[j].mPresent = false;
7548 0 : mCachedStyles[j].value.Truncate(0);
7549 : }
7550 0 : return NS_OK;
7551 : }
7552 :
7553 :
7554 : nsresult
7555 0 : nsHTMLEditRules::AdjustSpecialBreaks(bool aSafeToAskFrames)
7556 : {
7557 0 : nsCOMArray<nsIDOMNode> arrayOfNodes;
7558 0 : nsCOMPtr<nsISupports> isupports;
7559 : PRInt32 nodeCount,j;
7560 :
7561 : // gather list of empty nodes
7562 0 : nsEmptyEditableFunctor functor(mHTMLEditor);
7563 0 : nsDOMIterator iter;
7564 0 : nsresult res = iter.Init(mDocChangeRange);
7565 0 : NS_ENSURE_SUCCESS(res, res);
7566 0 : res = iter.AppendList(functor, arrayOfNodes);
7567 0 : NS_ENSURE_SUCCESS(res, res);
7568 :
7569 : // put moz-br's into these empty li's and td's
7570 0 : nodeCount = arrayOfNodes.Count();
7571 0 : for (j = 0; j < nodeCount; j++)
7572 : {
7573 : // need to put br at END of node. It may have
7574 : // empty containers in it and still pass the "IsEmptynode" test,
7575 : // and we want the br's to be after them. Also, we want the br
7576 : // to be after the selection if the selection is in this node.
7577 : PRUint32 len;
7578 0 : nsCOMPtr<nsIDOMNode> brNode, theNode = arrayOfNodes[0];
7579 0 : arrayOfNodes.RemoveObjectAt(0);
7580 0 : res = nsEditor::GetLengthOfDOMNode(theNode, len);
7581 0 : NS_ENSURE_SUCCESS(res, res);
7582 0 : res = CreateMozBR(theNode, (PRInt32)len, address_of(brNode));
7583 0 : NS_ENSURE_SUCCESS(res, res);
7584 : }
7585 :
7586 0 : return res;
7587 : }
7588 :
7589 : nsresult
7590 0 : nsHTMLEditRules::AdjustWhitespace(nsISelection *aSelection)
7591 : {
7592 : // get selection point
7593 0 : nsCOMPtr<nsIDOMNode> selNode;
7594 : PRInt32 selOffset;
7595 0 : nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
7596 0 : NS_ENSURE_SUCCESS(res, res);
7597 :
7598 : // ask whitespace object to tweak nbsp's
7599 0 : return nsWSRunObject(mHTMLEditor, selNode, selOffset).AdjustWhitespace();
7600 : }
7601 :
7602 : nsresult
7603 0 : nsHTMLEditRules::PinSelectionToNewBlock(nsISelection *aSelection)
7604 : {
7605 0 : NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
7606 : bool bCollapsed;
7607 0 : nsresult res = aSelection->GetIsCollapsed(&bCollapsed);
7608 0 : NS_ENSURE_SUCCESS(res, res);
7609 0 : if (!bCollapsed) {
7610 0 : return NS_OK;
7611 : }
7612 :
7613 : // get the (collapsed) selection location
7614 0 : nsCOMPtr<nsIDOMNode> selNode, temp;
7615 : PRInt32 selOffset;
7616 0 : res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
7617 0 : NS_ENSURE_SUCCESS(res, res);
7618 0 : temp = selNode;
7619 :
7620 : // use ranges and sRangeHelper to compare sel point to new block
7621 0 : nsRefPtr<nsRange> range = new nsRange();
7622 0 : res = range->SetStart(selNode, selOffset);
7623 0 : NS_ENSURE_SUCCESS(res, res);
7624 0 : res = range->SetEnd(selNode, selOffset);
7625 0 : NS_ENSURE_SUCCESS(res, res);
7626 0 : nsCOMPtr<nsIContent> block (do_QueryInterface(mNewBlock));
7627 0 : NS_ENSURE_TRUE(block, NS_ERROR_NO_INTERFACE);
7628 : bool nodeBefore, nodeAfter;
7629 0 : res = nsRange::CompareNodeToRange(block, range, &nodeBefore, &nodeAfter);
7630 0 : NS_ENSURE_SUCCESS(res, res);
7631 :
7632 0 : if (nodeBefore && nodeAfter)
7633 0 : return NS_OK; // selection is inside block
7634 0 : else if (nodeBefore)
7635 : {
7636 : // selection is after block. put at end of block.
7637 0 : nsCOMPtr<nsIDOMNode> tmp = mNewBlock;
7638 0 : mHTMLEditor->GetLastEditableChild(mNewBlock, address_of(tmp));
7639 : PRUint32 endPoint;
7640 0 : if (mHTMLEditor->IsTextNode(tmp) || mHTMLEditor->IsContainer(tmp))
7641 : {
7642 0 : res = nsEditor::GetLengthOfDOMNode(tmp, endPoint);
7643 0 : NS_ENSURE_SUCCESS(res, res);
7644 : }
7645 : else
7646 : {
7647 0 : nsCOMPtr<nsIDOMNode> tmp2;
7648 0 : res = nsEditor::GetNodeLocation(tmp, address_of(tmp2), (PRInt32*)&endPoint);
7649 0 : NS_ENSURE_SUCCESS(res, res);
7650 0 : tmp = tmp2;
7651 0 : endPoint++; // want to be after this node
7652 : }
7653 0 : return aSelection->Collapse(tmp, (PRInt32)endPoint);
7654 : }
7655 : else
7656 : {
7657 : // selection is before block. put at start of block.
7658 0 : nsCOMPtr<nsIDOMNode> tmp = mNewBlock;
7659 0 : mHTMLEditor->GetFirstEditableChild(mNewBlock, address_of(tmp));
7660 : PRInt32 offset;
7661 0 : if (!(mHTMLEditor->IsTextNode(tmp) || mHTMLEditor->IsContainer(tmp)))
7662 : {
7663 0 : nsCOMPtr<nsIDOMNode> tmp2;
7664 0 : res = nsEditor::GetNodeLocation(tmp, address_of(tmp2), &offset);
7665 0 : NS_ENSURE_SUCCESS(res, res);
7666 0 : tmp = tmp2;
7667 : }
7668 0 : return aSelection->Collapse(tmp, 0);
7669 : }
7670 : }
7671 :
7672 : nsresult
7673 0 : nsHTMLEditRules::CheckInterlinePosition(nsISelection *aSelection)
7674 : {
7675 0 : NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
7676 0 : nsCOMPtr<nsISelection> selection(aSelection);
7677 0 : nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
7678 :
7679 : // if the selection isn't collapsed, do nothing.
7680 : bool bCollapsed;
7681 0 : nsresult res = aSelection->GetIsCollapsed(&bCollapsed);
7682 0 : NS_ENSURE_SUCCESS(res, res);
7683 0 : if (!bCollapsed) {
7684 0 : return NS_OK;
7685 : }
7686 :
7687 : // get the (collapsed) selection location
7688 0 : nsCOMPtr<nsIDOMNode> selNode, node;
7689 : PRInt32 selOffset;
7690 0 : res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
7691 0 : NS_ENSURE_SUCCESS(res, res);
7692 :
7693 : // are we after a block? If so try set caret to following content
7694 0 : mHTMLEditor->GetPriorHTMLSibling(selNode, selOffset, address_of(node));
7695 0 : if (node && IsBlockNode(node))
7696 : {
7697 0 : selPriv->SetInterlinePosition(true);
7698 0 : return NS_OK;
7699 : }
7700 :
7701 : // are we before a block? If so try set caret to prior content
7702 0 : mHTMLEditor->GetNextHTMLSibling(selNode, selOffset, address_of(node));
7703 0 : if (node && IsBlockNode(node))
7704 : {
7705 0 : selPriv->SetInterlinePosition(false);
7706 0 : return NS_OK;
7707 : }
7708 :
7709 : // are we after a <br>? If so we want to stick to whatever is after <br>.
7710 0 : mHTMLEditor->GetPriorHTMLNode(selNode, selOffset, address_of(node), true);
7711 0 : if (node && nsTextEditUtils::IsBreak(node))
7712 0 : selPriv->SetInterlinePosition(true);
7713 0 : return NS_OK;
7714 : }
7715 :
7716 : nsresult
7717 0 : nsHTMLEditRules::AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection aAction)
7718 : {
7719 0 : NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
7720 0 : nsCOMPtr<nsISelection> selection(aSelection);
7721 0 : nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
7722 :
7723 : // if the selection isn't collapsed, do nothing.
7724 : // moose: one thing to do instead is check for the case of
7725 : // only a single break selected, and collapse it. Good thing? Beats me.
7726 : bool bCollapsed;
7727 0 : nsresult res = aSelection->GetIsCollapsed(&bCollapsed);
7728 0 : NS_ENSURE_SUCCESS(res, res);
7729 0 : if (!bCollapsed) {
7730 0 : return NS_OK;
7731 : }
7732 :
7733 : // get the (collapsed) selection location
7734 0 : nsCOMPtr<nsIDOMNode> selNode, temp;
7735 : PRInt32 selOffset;
7736 0 : res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
7737 0 : NS_ENSURE_SUCCESS(res, res);
7738 0 : temp = selNode;
7739 :
7740 : // are we in an editable node?
7741 0 : while (!mHTMLEditor->IsEditable(selNode))
7742 : {
7743 : // scan up the tree until we find an editable place to be
7744 0 : res = nsEditor::GetNodeLocation(temp, address_of(selNode), &selOffset);
7745 0 : NS_ENSURE_SUCCESS(res, res);
7746 0 : NS_ENSURE_TRUE(selNode, NS_ERROR_FAILURE);
7747 0 : temp = selNode;
7748 : }
7749 :
7750 : // make sure we aren't in an empty block - user will see no cursor. If this
7751 : // is happening, put a <br> in the block if allowed.
7752 0 : nsCOMPtr<nsIDOMNode> theblock;
7753 0 : if (IsBlockNode(selNode)) theblock = selNode;
7754 0 : else theblock = mHTMLEditor->GetBlockNodeParent(selNode);
7755 0 : if (theblock && mHTMLEditor->IsEditable(theblock)) {
7756 : bool bIsEmptyNode;
7757 0 : res = mHTMLEditor->IsEmptyNode(theblock, &bIsEmptyNode, false, false);
7758 0 : NS_ENSURE_SUCCESS(res, res);
7759 : // check if br can go into the destination node
7760 0 : if (bIsEmptyNode && mHTMLEditor->CanContainTag(selNode, NS_LITERAL_STRING("br")))
7761 : {
7762 0 : nsCOMPtr<nsIDOMNode> rootNode = do_QueryInterface(mHTMLEditor->GetRoot());
7763 0 : NS_ENSURE_TRUE(rootNode, NS_ERROR_FAILURE);
7764 0 : if (selNode == rootNode)
7765 : {
7766 : // Our root node is completely empty. Don't add a <br> here.
7767 : // AfterEditInner() will add one for us when it calls
7768 : // CreateBogusNodeIfNeeded()!
7769 0 : return NS_OK;
7770 : }
7771 :
7772 0 : nsCOMPtr<nsIDOMNode> brNode;
7773 : // we know we can skip the rest of this routine given the cirumstance
7774 0 : return CreateMozBR(selNode, selOffset, address_of(brNode));
7775 : }
7776 : }
7777 :
7778 : // are we in a text node?
7779 0 : nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(selNode);
7780 0 : if (textNode)
7781 0 : return NS_OK; // we LIKE it when we are in a text node. that RULZ
7782 :
7783 : // do we need to insert a special mozBR? We do if we are:
7784 : // 1) prior node is in same block where selection is AND
7785 : // 2) prior node is a br AND
7786 : // 3) that br is not visible
7787 :
7788 0 : nsCOMPtr<nsIDOMNode> nearNode;
7789 0 : res = mHTMLEditor->GetPriorHTMLNode(selNode, selOffset, address_of(nearNode));
7790 0 : NS_ENSURE_SUCCESS(res, res);
7791 0 : if (nearNode)
7792 : {
7793 : // is nearNode also a descendant of same block?
7794 0 : nsCOMPtr<nsIDOMNode> block, nearBlock;
7795 0 : if (IsBlockNode(selNode)) block = selNode;
7796 0 : else block = mHTMLEditor->GetBlockNodeParent(selNode);
7797 0 : nearBlock = mHTMLEditor->GetBlockNodeParent(nearNode);
7798 0 : if (block == nearBlock)
7799 : {
7800 0 : if (nearNode && nsTextEditUtils::IsBreak(nearNode) )
7801 : {
7802 0 : if (!mHTMLEditor->IsVisBreak(nearNode))
7803 : {
7804 : // need to insert special moz BR. Why? Because if we don't
7805 : // the user will see no new line for the break. Also, things
7806 : // like table cells won't grow in height.
7807 0 : nsCOMPtr<nsIDOMNode> brNode;
7808 0 : res = CreateMozBR(selNode, selOffset, address_of(brNode));
7809 0 : NS_ENSURE_SUCCESS(res, res);
7810 0 : res = nsEditor::GetNodeLocation(brNode, address_of(selNode), &selOffset);
7811 0 : NS_ENSURE_SUCCESS(res, res);
7812 : // selection stays *before* moz-br, sticking to it
7813 0 : selPriv->SetInterlinePosition(true);
7814 0 : res = aSelection->Collapse(selNode,selOffset);
7815 0 : NS_ENSURE_SUCCESS(res, res);
7816 : }
7817 : else
7818 : {
7819 0 : nsCOMPtr<nsIDOMNode> nextNode;
7820 0 : mHTMLEditor->GetNextHTMLNode(nearNode, address_of(nextNode), true);
7821 0 : if (nextNode && nsTextEditUtils::IsMozBR(nextNode))
7822 : {
7823 : // selection between br and mozbr. make it stick to mozbr
7824 : // so that it will be on blank line.
7825 0 : selPriv->SetInterlinePosition(true);
7826 : }
7827 : }
7828 : }
7829 : }
7830 : }
7831 :
7832 : // we aren't in a textnode: are we adjacent to text or a break or an image?
7833 0 : res = mHTMLEditor->GetPriorHTMLNode(selNode, selOffset, address_of(nearNode), true);
7834 0 : NS_ENSURE_SUCCESS(res, res);
7835 0 : if (nearNode && (nsTextEditUtils::IsBreak(nearNode)
7836 0 : || nsEditor::IsTextNode(nearNode)
7837 0 : || nsHTMLEditUtils::IsImage(nearNode)
7838 0 : || nsHTMLEditUtils::IsHR(nearNode)))
7839 0 : return NS_OK; // this is a good place for the caret to be
7840 0 : res = mHTMLEditor->GetNextHTMLNode(selNode, selOffset, address_of(nearNode), true);
7841 0 : NS_ENSURE_SUCCESS(res, res);
7842 0 : if (nearNode && (nsTextEditUtils::IsBreak(nearNode)
7843 0 : || nsEditor::IsTextNode(nearNode)
7844 0 : || nsHTMLEditUtils::IsImage(nearNode)
7845 0 : || nsHTMLEditUtils::IsHR(nearNode)))
7846 0 : return NS_OK; // this is a good place for the caret to be
7847 :
7848 : // look for a nearby text node.
7849 : // prefer the correct direction.
7850 0 : res = FindNearSelectableNode(selNode, selOffset, aAction, address_of(nearNode));
7851 0 : NS_ENSURE_SUCCESS(res, res);
7852 :
7853 0 : if (nearNode)
7854 : {
7855 : // is the nearnode a text node?
7856 0 : textNode = do_QueryInterface(nearNode);
7857 0 : if (textNode)
7858 : {
7859 0 : PRInt32 offset = 0;
7860 : // put selection in right place:
7861 0 : if (aAction == nsIEditor::ePrevious)
7862 0 : textNode->GetLength((PRUint32*)&offset);
7863 0 : res = aSelection->Collapse(nearNode,offset);
7864 : }
7865 : else // must be break or image
7866 : {
7867 0 : res = nsEditor::GetNodeLocation(nearNode, address_of(selNode), &selOffset);
7868 0 : NS_ENSURE_SUCCESS(res, res);
7869 0 : if (aAction == nsIEditor::ePrevious) selOffset++; // want to be beyond it if we backed up to it
7870 0 : res = aSelection->Collapse(selNode, selOffset);
7871 : }
7872 : }
7873 0 : return res;
7874 : }
7875 :
7876 :
7877 : nsresult
7878 0 : nsHTMLEditRules::FindNearSelectableNode(nsIDOMNode *aSelNode,
7879 : PRInt32 aSelOffset,
7880 : nsIEditor::EDirection &aDirection,
7881 : nsCOMPtr<nsIDOMNode> *outSelectableNode)
7882 : {
7883 0 : NS_ENSURE_TRUE(aSelNode && outSelectableNode, NS_ERROR_NULL_POINTER);
7884 0 : *outSelectableNode = nsnull;
7885 0 : nsresult res = NS_OK;
7886 :
7887 0 : nsCOMPtr<nsIDOMNode> nearNode, curNode;
7888 0 : if (aDirection == nsIEditor::ePrevious)
7889 0 : res = mHTMLEditor->GetPriorHTMLNode(aSelNode, aSelOffset, address_of(nearNode));
7890 : else
7891 0 : res = mHTMLEditor->GetNextHTMLNode(aSelNode, aSelOffset, address_of(nearNode));
7892 0 : NS_ENSURE_SUCCESS(res, res);
7893 :
7894 0 : if (!nearNode) // try the other direction then
7895 : {
7896 0 : if (aDirection == nsIEditor::ePrevious)
7897 0 : aDirection = nsIEditor::eNext;
7898 : else
7899 0 : aDirection = nsIEditor::ePrevious;
7900 :
7901 0 : if (aDirection == nsIEditor::ePrevious)
7902 0 : res = mHTMLEditor->GetPriorHTMLNode(aSelNode, aSelOffset, address_of(nearNode));
7903 : else
7904 0 : res = mHTMLEditor->GetNextHTMLNode(aSelNode, aSelOffset, address_of(nearNode));
7905 0 : NS_ENSURE_SUCCESS(res, res);
7906 : }
7907 :
7908 : // scan in the right direction until we find an eligible text node,
7909 : // but don't cross any breaks, images, or table elements.
7910 0 : while (nearNode && !(mHTMLEditor->IsTextNode(nearNode)
7911 0 : || nsTextEditUtils::IsBreak(nearNode)
7912 0 : || nsHTMLEditUtils::IsImage(nearNode)))
7913 : {
7914 0 : curNode = nearNode;
7915 0 : if (aDirection == nsIEditor::ePrevious)
7916 0 : res = mHTMLEditor->GetPriorHTMLNode(curNode, address_of(nearNode));
7917 : else
7918 0 : res = mHTMLEditor->GetNextHTMLNode(curNode, address_of(nearNode));
7919 0 : NS_ENSURE_SUCCESS(res, res);
7920 : }
7921 :
7922 0 : if (nearNode)
7923 : {
7924 : // don't cross any table elements
7925 : bool bInDifTblElems;
7926 0 : res = InDifferentTableElements(nearNode, aSelNode, &bInDifTblElems);
7927 0 : NS_ENSURE_SUCCESS(res, res);
7928 0 : if (bInDifTblElems) return NS_OK;
7929 :
7930 : // otherwise, ok, we have found a good spot to put the selection
7931 0 : *outSelectableNode = do_QueryInterface(nearNode);
7932 : }
7933 0 : return res;
7934 : }
7935 :
7936 :
7937 : nsresult
7938 0 : nsHTMLEditRules::InDifferentTableElements(nsIDOMNode *aNode1, nsIDOMNode *aNode2, bool *aResult)
7939 : {
7940 0 : NS_ASSERTION(aNode1 && aNode2 && aResult, "null args");
7941 0 : NS_ENSURE_TRUE(aNode1 && aNode2 && aResult, NS_ERROR_NULL_POINTER);
7942 :
7943 0 : nsCOMPtr<nsIDOMNode> tn1, tn2, node = aNode1, temp;
7944 0 : *aResult = false;
7945 :
7946 0 : while (node && !nsHTMLEditUtils::IsTableElement(node))
7947 : {
7948 0 : node->GetParentNode(getter_AddRefs(temp));
7949 0 : node = temp;
7950 : }
7951 0 : tn1 = node;
7952 :
7953 0 : node = aNode2;
7954 0 : while (node && !nsHTMLEditUtils::IsTableElement(node))
7955 : {
7956 0 : node->GetParentNode(getter_AddRefs(temp));
7957 0 : node = temp;
7958 : }
7959 0 : tn2 = node;
7960 :
7961 0 : *aResult = (tn1 != tn2);
7962 :
7963 0 : return NS_OK;
7964 : }
7965 :
7966 :
7967 : nsresult
7968 0 : nsHTMLEditRules::RemoveEmptyNodes()
7969 : {
7970 0 : nsCOMArray<nsIDOMNode> arrayOfEmptyNodes, arrayOfEmptyCites;
7971 0 : nsCOMPtr<nsISupports> isupports;
7972 : PRInt32 nodeCount,j;
7973 :
7974 : // some general notes on the algorithm used here: the goal is to examine all the
7975 : // nodes in mDocChangeRange, and remove the empty ones. We do this by using a
7976 : // content iterator to traverse all the nodes in the range, and placing the empty
7977 : // nodes into an array. After finishing the iteration, we delete the empty nodes
7978 : // in the array. (they cannot be deleted as we find them becasue that would
7979 : // invalidate the iterator.)
7980 : // Since checking to see if a node is empty can be costly for nodes with many
7981 : // descendants, there are some optimizations made. I rely on the fact that the
7982 : // iterator is post-order: it will visit children of a node before visiting the
7983 : // parent node. So if I find that a child node is not empty, I know that its
7984 : // parent is not empty without even checking. So I put the parent on a "skipList"
7985 : // which is just a voidArray of nodes I can skip the empty check on. If I
7986 : // encounter a node on the skiplist, i skip the processing for that node and replace
7987 : // its slot in the skiplist with that node's parent.
7988 : // An interseting idea is to go ahead and regard parent nodes that are NOT on the
7989 : // skiplist as being empty (without even doing the IsEmptyNode check) on the theory
7990 : // that if they weren't empty, we would have encountered a non-empty child earlier
7991 : // and thus put this parent node on the skiplist.
7992 : // Unfortunately I can't use that strategy here, because the range may include
7993 : // some children of a node while excluding others. Thus I could find all the
7994 : // _examined_ children empty, but still not have an empty parent.
7995 :
7996 : // need an iterator
7997 : nsCOMPtr<nsIContentIterator> iter =
7998 0 : do_CreateInstance("@mozilla.org/content/post-content-iterator;1");
7999 0 : NS_ENSURE_TRUE(iter, NS_ERROR_NULL_POINTER);
8000 :
8001 0 : nsresult res = iter->Init(mDocChangeRange);
8002 0 : NS_ENSURE_SUCCESS(res, res);
8003 :
8004 0 : nsTArray<nsIDOMNode*> skipList;
8005 :
8006 : // check for empty nodes
8007 0 : while (!iter->IsDone())
8008 : {
8009 0 : nsCOMPtr<nsIDOMNode> node, parent;
8010 :
8011 0 : node = do_QueryInterface(iter->GetCurrentNode());
8012 0 : NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
8013 :
8014 0 : node->GetParentNode(getter_AddRefs(parent));
8015 :
8016 0 : PRUint32 idx = skipList.IndexOf(node);
8017 0 : if (idx != skipList.NoIndex)
8018 : {
8019 : // this node is on our skip list. Skip processing for this node,
8020 : // and replace its value in the skip list with the value of its parent
8021 0 : skipList[idx] = parent;
8022 : }
8023 : else
8024 : {
8025 0 : bool bIsCandidate = false;
8026 0 : bool bIsEmptyNode = false;
8027 0 : bool bIsMailCite = false;
8028 :
8029 : // don't delete the body
8030 0 : if (!nsTextEditUtils::IsBody(node))
8031 : {
8032 : // only consider certain nodes to be empty for purposes of removal
8033 0 : if ( (bIsMailCite = nsHTMLEditUtils::IsMailCite(node)) ||
8034 0 : nsEditor::NodeIsType(node, nsEditProperty::a) ||
8035 0 : nsHTMLEditUtils::IsInlineStyle(node) ||
8036 0 : nsHTMLEditUtils::IsList(node) ||
8037 0 : nsHTMLEditUtils::IsDiv(node) )
8038 : {
8039 0 : bIsCandidate = true;
8040 : }
8041 : // these node types are candidates if selection is not in them
8042 0 : else if (nsHTMLEditUtils::IsFormatNode(node) ||
8043 0 : nsHTMLEditUtils::IsListItem(node) ||
8044 0 : nsHTMLEditUtils::IsBlockquote(node) )
8045 : {
8046 : // if it is one of these, don't delete if selection inside.
8047 : // this is so we can create empty headings, etc, for the
8048 : // user to type into.
8049 : bool bIsSelInNode;
8050 0 : res = SelectionEndpointInNode(node, &bIsSelInNode);
8051 0 : NS_ENSURE_SUCCESS(res, res);
8052 0 : if (!bIsSelInNode)
8053 : {
8054 0 : bIsCandidate = true;
8055 : }
8056 : }
8057 : }
8058 :
8059 0 : if (bIsCandidate)
8060 : {
8061 0 : if (bIsMailCite) // we delete mailcites even if they have a solo br in them
8062 0 : res = mHTMLEditor->IsEmptyNode(node, &bIsEmptyNode, true, true);
8063 : else // other nodes we require to be empty
8064 0 : res = mHTMLEditor->IsEmptyNode(node, &bIsEmptyNode, false, true);
8065 0 : NS_ENSURE_SUCCESS(res, res);
8066 0 : if (bIsEmptyNode)
8067 : {
8068 0 : if (bIsMailCite) // mailcites go on a separate list from other empty nodes
8069 : {
8070 0 : arrayOfEmptyCites.AppendObject(node);
8071 : }
8072 : else
8073 : {
8074 0 : arrayOfEmptyNodes.AppendObject(node);
8075 : }
8076 : }
8077 : }
8078 :
8079 0 : if (!bIsEmptyNode)
8080 : {
8081 : // put parent on skip list
8082 0 : skipList.AppendElement(parent);
8083 : }
8084 : }
8085 :
8086 0 : iter->Next();
8087 : }
8088 :
8089 : // now delete the empty nodes
8090 0 : nodeCount = arrayOfEmptyNodes.Count();
8091 0 : for (j = 0; j < nodeCount; j++)
8092 : {
8093 0 : nsCOMPtr<nsIDOMNode> delNode = arrayOfEmptyNodes[0];
8094 0 : arrayOfEmptyNodes.RemoveObjectAt(0);
8095 0 : if (mHTMLEditor->IsModifiableNode(delNode)) {
8096 0 : res = mHTMLEditor->DeleteNode(delNode);
8097 0 : NS_ENSURE_SUCCESS(res, res);
8098 : }
8099 : }
8100 :
8101 : // now delete the empty mailcites
8102 : // this is a separate step because we want to pull out any br's and preserve them.
8103 0 : nodeCount = arrayOfEmptyCites.Count();
8104 0 : for (j = 0; j < nodeCount; j++)
8105 : {
8106 0 : nsCOMPtr<nsIDOMNode> delNode = arrayOfEmptyCites[0];
8107 0 : arrayOfEmptyCites.RemoveObjectAt(0);
8108 : bool bIsEmptyNode;
8109 0 : res = mHTMLEditor->IsEmptyNode(delNode, &bIsEmptyNode, false, true);
8110 0 : NS_ENSURE_SUCCESS(res, res);
8111 0 : if (!bIsEmptyNode)
8112 : {
8113 : // we are deleting a cite that has just a br. We want to delete cite,
8114 : // but preserve br.
8115 0 : nsCOMPtr<nsIDOMNode> parent, brNode;
8116 : PRInt32 offset;
8117 0 : res = nsEditor::GetNodeLocation(delNode, address_of(parent), &offset);
8118 0 : NS_ENSURE_SUCCESS(res, res);
8119 0 : res = mHTMLEditor->CreateBR(parent, offset, address_of(brNode));
8120 0 : NS_ENSURE_SUCCESS(res, res);
8121 : }
8122 0 : res = mHTMLEditor->DeleteNode(delNode);
8123 0 : NS_ENSURE_SUCCESS(res, res);
8124 : }
8125 :
8126 0 : return res;
8127 : }
8128 :
8129 : nsresult
8130 0 : nsHTMLEditRules::SelectionEndpointInNode(nsIDOMNode *aNode, bool *aResult)
8131 : {
8132 0 : NS_ENSURE_TRUE(aNode && aResult, NS_ERROR_NULL_POINTER);
8133 :
8134 0 : *aResult = false;
8135 :
8136 0 : nsCOMPtr<nsISelection>selection;
8137 0 : nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
8138 0 : NS_ENSURE_SUCCESS(res, res);
8139 0 : nsCOMPtr<nsISelectionPrivate>selPriv(do_QueryInterface(selection));
8140 :
8141 0 : nsCOMPtr<nsIEnumerator> enumerator;
8142 0 : res = selPriv->GetEnumerator(getter_AddRefs(enumerator));
8143 0 : NS_ENSURE_SUCCESS(res, res);
8144 0 : NS_ENSURE_TRUE(enumerator, NS_ERROR_UNEXPECTED);
8145 :
8146 0 : for (enumerator->First(); NS_OK!=enumerator->IsDone(); enumerator->Next())
8147 : {
8148 0 : nsCOMPtr<nsISupports> currentItem;
8149 0 : res = enumerator->CurrentItem(getter_AddRefs(currentItem));
8150 0 : NS_ENSURE_SUCCESS(res, res);
8151 0 : NS_ENSURE_TRUE(currentItem, NS_ERROR_UNEXPECTED);
8152 :
8153 0 : nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
8154 0 : nsCOMPtr<nsIDOMNode> startParent, endParent;
8155 0 : range->GetStartContainer(getter_AddRefs(startParent));
8156 0 : if (startParent)
8157 : {
8158 0 : if (aNode == startParent)
8159 : {
8160 0 : *aResult = true;
8161 0 : return NS_OK;
8162 : }
8163 0 : if (nsEditorUtils::IsDescendantOf(startParent, aNode))
8164 : {
8165 0 : *aResult = true;
8166 0 : return NS_OK;
8167 : }
8168 : }
8169 0 : range->GetEndContainer(getter_AddRefs(endParent));
8170 0 : if (startParent == endParent) continue;
8171 0 : if (endParent)
8172 : {
8173 0 : if (aNode == endParent)
8174 : {
8175 0 : *aResult = true;
8176 0 : return NS_OK;
8177 : }
8178 0 : if (nsEditorUtils::IsDescendantOf(endParent, aNode))
8179 : {
8180 0 : *aResult = true;
8181 0 : return NS_OK;
8182 : }
8183 : }
8184 : }
8185 0 : return res;
8186 : }
8187 :
8188 : ///////////////////////////////////////////////////////////////////////////
8189 : // IsEmptyInline: return true if aNode is an empty inline container
8190 : //
8191 : //
8192 : bool
8193 0 : nsHTMLEditRules::IsEmptyInline(nsIDOMNode *aNode)
8194 : {
8195 0 : if (aNode && IsInlineNode(aNode) && mHTMLEditor->IsContainer(aNode))
8196 : {
8197 : bool bEmpty;
8198 0 : mHTMLEditor->IsEmptyNode(aNode, &bEmpty);
8199 0 : return bEmpty;
8200 : }
8201 0 : return false;
8202 : }
8203 :
8204 :
8205 : bool
8206 0 : nsHTMLEditRules::ListIsEmptyLine(nsCOMArray<nsIDOMNode> &arrayOfNodes)
8207 : {
8208 : // we have a list of nodes which we are candidates for being moved
8209 : // into a new block. Determine if it's anything more than a blank line.
8210 : // Look for editable content above and beyond one single BR.
8211 0 : PRInt32 listCount = arrayOfNodes.Count();
8212 0 : NS_ENSURE_TRUE(listCount, true);
8213 0 : nsCOMPtr<nsIDOMNode> somenode;
8214 0 : PRInt32 j, brCount=0;
8215 0 : for (j = 0; j < listCount; j++)
8216 : {
8217 0 : somenode = arrayOfNodes[j];
8218 0 : if (somenode && mHTMLEditor->IsEditable(somenode))
8219 : {
8220 0 : if (nsTextEditUtils::IsBreak(somenode))
8221 : {
8222 : // first break doesn't count
8223 0 : if (brCount) return false;
8224 0 : brCount++;
8225 : }
8226 0 : else if (IsEmptyInline(somenode))
8227 : {
8228 : // empty inline, keep looking
8229 : }
8230 0 : else return false;
8231 : }
8232 : }
8233 0 : return true;
8234 : }
8235 :
8236 :
8237 : nsresult
8238 0 : nsHTMLEditRules::PopListItem(nsIDOMNode *aListItem, bool *aOutOfList)
8239 : {
8240 : // check parms
8241 0 : NS_ENSURE_TRUE(aListItem && aOutOfList, NS_ERROR_NULL_POINTER);
8242 :
8243 : // init out params
8244 0 : *aOutOfList = false;
8245 :
8246 0 : nsCOMPtr<nsIDOMNode> curParent;
8247 0 : nsCOMPtr<nsIDOMNode> curNode( do_QueryInterface(aListItem));
8248 : PRInt32 offset;
8249 0 : nsresult res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset);
8250 0 : NS_ENSURE_SUCCESS(res, res);
8251 :
8252 0 : if (!nsHTMLEditUtils::IsListItem(curNode))
8253 0 : return NS_ERROR_FAILURE;
8254 :
8255 : // if it's first or last list item, don't need to split the list
8256 : // otherwise we do.
8257 0 : nsCOMPtr<nsIDOMNode> curParPar;
8258 : PRInt32 parOffset;
8259 0 : res = nsEditor::GetNodeLocation(curParent, address_of(curParPar), &parOffset);
8260 0 : NS_ENSURE_SUCCESS(res, res);
8261 :
8262 : bool bIsFirstListItem;
8263 0 : res = mHTMLEditor->IsFirstEditableChild(curNode, &bIsFirstListItem);
8264 0 : NS_ENSURE_SUCCESS(res, res);
8265 :
8266 : bool bIsLastListItem;
8267 0 : res = mHTMLEditor->IsLastEditableChild(curNode, &bIsLastListItem);
8268 0 : NS_ENSURE_SUCCESS(res, res);
8269 :
8270 0 : if (!bIsFirstListItem && !bIsLastListItem)
8271 : {
8272 : // split the list
8273 0 : nsCOMPtr<nsIDOMNode> newBlock;
8274 0 : res = mHTMLEditor->SplitNode(curParent, offset, getter_AddRefs(newBlock));
8275 0 : NS_ENSURE_SUCCESS(res, res);
8276 : }
8277 :
8278 0 : if (!bIsFirstListItem) parOffset++;
8279 :
8280 0 : res = mHTMLEditor->MoveNode(curNode, curParPar, parOffset);
8281 0 : NS_ENSURE_SUCCESS(res, res);
8282 :
8283 : // unwrap list item contents if they are no longer in a list
8284 0 : if (!nsHTMLEditUtils::IsList(curParPar)
8285 0 : && nsHTMLEditUtils::IsListItem(curNode))
8286 : {
8287 0 : res = mHTMLEditor->RemoveBlockContainer(curNode);
8288 0 : NS_ENSURE_SUCCESS(res, res);
8289 0 : *aOutOfList = true;
8290 : }
8291 0 : return res;
8292 : }
8293 :
8294 : nsresult
8295 0 : nsHTMLEditRules::RemoveListStructure(nsIDOMNode *aList)
8296 : {
8297 0 : NS_ENSURE_ARG_POINTER(aList);
8298 :
8299 : nsresult res;
8300 :
8301 0 : nsCOMPtr<nsIDOMNode> child;
8302 0 : aList->GetFirstChild(getter_AddRefs(child));
8303 :
8304 0 : while (child)
8305 : {
8306 0 : if (nsHTMLEditUtils::IsListItem(child))
8307 : {
8308 : bool bOutOfList;
8309 0 : do
8310 : {
8311 0 : res = PopListItem(child, &bOutOfList);
8312 0 : NS_ENSURE_SUCCESS(res, res);
8313 0 : } while (!bOutOfList); // keep popping it out until it's not in a list anymore
8314 : }
8315 0 : else if (nsHTMLEditUtils::IsList(child))
8316 : {
8317 0 : res = RemoveListStructure(child);
8318 0 : NS_ENSURE_SUCCESS(res, res);
8319 : }
8320 : else
8321 : {
8322 : // delete any non- list items for now
8323 0 : res = mHTMLEditor->DeleteNode(child);
8324 0 : NS_ENSURE_SUCCESS(res, res);
8325 : }
8326 0 : aList->GetFirstChild(getter_AddRefs(child));
8327 : }
8328 : // delete the now-empty list
8329 0 : res = mHTMLEditor->RemoveBlockContainer(aList);
8330 0 : NS_ENSURE_SUCCESS(res, res);
8331 :
8332 0 : return res;
8333 : }
8334 :
8335 :
8336 : nsresult
8337 0 : nsHTMLEditRules::ConfirmSelectionInBody()
8338 : {
8339 0 : nsresult res = NS_OK;
8340 :
8341 : // get the body
8342 0 : nsCOMPtr<nsIDOMElement> rootElement = do_QueryInterface(mHTMLEditor->GetRoot());
8343 0 : NS_ENSURE_TRUE(rootElement, NS_ERROR_UNEXPECTED);
8344 :
8345 : // get the selection
8346 0 : nsCOMPtr<nsISelection>selection;
8347 0 : res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
8348 0 : NS_ENSURE_SUCCESS(res, res);
8349 :
8350 : // get the selection start location
8351 0 : nsCOMPtr<nsIDOMNode> selNode, temp, parent;
8352 : PRInt32 selOffset;
8353 0 : res = mHTMLEditor->GetStartNodeAndOffset(selection, getter_AddRefs(selNode), &selOffset);
8354 0 : NS_ENSURE_SUCCESS(res, res);
8355 0 : temp = selNode;
8356 :
8357 : // check that selNode is inside body
8358 0 : while (temp && !nsTextEditUtils::IsBody(temp))
8359 : {
8360 0 : res = temp->GetParentNode(getter_AddRefs(parent));
8361 0 : temp = parent;
8362 : }
8363 :
8364 : // if we aren't in the body, force the issue
8365 0 : if (!temp)
8366 : {
8367 : // uncomment this to see when we get bad selections
8368 : // NS_NOTREACHED("selection not in body");
8369 0 : selection->Collapse(rootElement, 0);
8370 : }
8371 :
8372 : // get the selection end location
8373 0 : res = mHTMLEditor->GetEndNodeAndOffset(selection, getter_AddRefs(selNode), &selOffset);
8374 0 : NS_ENSURE_SUCCESS(res, res);
8375 0 : temp = selNode;
8376 :
8377 : // check that selNode is inside body
8378 0 : while (temp && !nsTextEditUtils::IsBody(temp))
8379 : {
8380 0 : res = temp->GetParentNode(getter_AddRefs(parent));
8381 0 : temp = parent;
8382 : }
8383 :
8384 : // if we aren't in the body, force the issue
8385 0 : if (!temp)
8386 : {
8387 : // uncomment this to see when we get bad selections
8388 : // NS_NOTREACHED("selection not in body");
8389 0 : selection->Collapse(rootElement, 0);
8390 : }
8391 :
8392 0 : return res;
8393 : }
8394 :
8395 :
8396 : nsresult
8397 0 : nsHTMLEditRules::UpdateDocChangeRange(nsIDOMRange *aRange)
8398 : {
8399 0 : nsresult res = NS_OK;
8400 :
8401 : // first make sure aRange is in the document. It might not be if
8402 : // portions of our editting action involved manipulating nodes
8403 : // prior to placing them in the document (e.g., populating a list item
8404 : // before placing it in its list)
8405 0 : nsCOMPtr<nsIDOMNode> startNode;
8406 0 : res = aRange->GetStartContainer(getter_AddRefs(startNode));
8407 0 : NS_ENSURE_SUCCESS(res, res);
8408 0 : if (!mHTMLEditor->IsDescendantOfBody(startNode))
8409 : {
8410 : // just return - we don't need to adjust mDocChangeRange in this case
8411 0 : return NS_OK;
8412 : }
8413 :
8414 0 : if (!mDocChangeRange)
8415 : {
8416 : // clone aRange.
8417 0 : nsCOMPtr<nsIDOMRange> range;
8418 0 : res = aRange->CloneRange(getter_AddRefs(range));
8419 0 : mDocChangeRange = static_cast<nsRange*>(range.get());
8420 : }
8421 : else
8422 : {
8423 : PRInt16 result;
8424 :
8425 : // compare starts of ranges
8426 0 : res = mDocChangeRange->CompareBoundaryPoints(nsIDOMRange::START_TO_START, aRange, &result);
8427 0 : if (res == NS_ERROR_NOT_INITIALIZED) {
8428 : // This will happen is mDocChangeRange is non-null, but the range is
8429 : // uninitialized. In this case we'll set the start to aRange start.
8430 : // The same test won't be needed further down since after we've set
8431 : // the start the range will be collapsed to that point.
8432 0 : result = 1;
8433 0 : res = NS_OK;
8434 : }
8435 0 : NS_ENSURE_SUCCESS(res, res);
8436 0 : if (result > 0) // positive result means mDocChangeRange start is after aRange start
8437 : {
8438 : PRInt32 startOffset;
8439 0 : res = aRange->GetStartOffset(&startOffset);
8440 0 : NS_ENSURE_SUCCESS(res, res);
8441 0 : res = mDocChangeRange->SetStart(startNode, startOffset);
8442 0 : NS_ENSURE_SUCCESS(res, res);
8443 : }
8444 :
8445 : // compare ends of ranges
8446 0 : res = mDocChangeRange->CompareBoundaryPoints(nsIDOMRange::END_TO_END, aRange, &result);
8447 0 : NS_ENSURE_SUCCESS(res, res);
8448 0 : if (result < 0) // negative result means mDocChangeRange end is before aRange end
8449 : {
8450 0 : nsCOMPtr<nsIDOMNode> endNode;
8451 : PRInt32 endOffset;
8452 0 : res = aRange->GetEndContainer(getter_AddRefs(endNode));
8453 0 : NS_ENSURE_SUCCESS(res, res);
8454 0 : res = aRange->GetEndOffset(&endOffset);
8455 0 : NS_ENSURE_SUCCESS(res, res);
8456 0 : res = mDocChangeRange->SetEnd(endNode, endOffset);
8457 0 : NS_ENSURE_SUCCESS(res, res);
8458 : }
8459 : }
8460 0 : return res;
8461 : }
8462 :
8463 : nsresult
8464 0 : nsHTMLEditRules::InsertMozBRIfNeeded(nsIDOMNode *aNode)
8465 : {
8466 0 : NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
8467 0 : if (!IsBlockNode(aNode)) return NS_OK;
8468 :
8469 : bool isEmpty;
8470 0 : nsCOMPtr<nsIDOMNode> brNode;
8471 0 : nsresult res = mHTMLEditor->IsEmptyNode(aNode, &isEmpty);
8472 0 : NS_ENSURE_SUCCESS(res, res);
8473 0 : if (isEmpty)
8474 : {
8475 0 : res = CreateMozBR(aNode, 0, address_of(brNode));
8476 : }
8477 0 : return res;
8478 : }
8479 :
8480 : NS_IMETHODIMP
8481 0 : nsHTMLEditRules::WillCreateNode(const nsAString& aTag, nsIDOMNode *aParent, PRInt32 aPosition)
8482 : {
8483 0 : return NS_OK;
8484 : }
8485 :
8486 : NS_IMETHODIMP
8487 0 : nsHTMLEditRules::DidCreateNode(const nsAString& aTag,
8488 : nsIDOMNode *aNode,
8489 : nsIDOMNode *aParent,
8490 : PRInt32 aPosition,
8491 : nsresult aResult)
8492 : {
8493 0 : if (!mListenerEnabled) {
8494 0 : return NS_OK;
8495 : }
8496 : // assumption that Join keeps the righthand node
8497 0 : nsresult res = mUtilRange->SelectNode(aNode);
8498 0 : NS_ENSURE_SUCCESS(res, res);
8499 0 : res = UpdateDocChangeRange(mUtilRange);
8500 0 : return res;
8501 : }
8502 :
8503 :
8504 : NS_IMETHODIMP
8505 0 : nsHTMLEditRules::WillInsertNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aPosition)
8506 : {
8507 0 : return NS_OK;
8508 : }
8509 :
8510 :
8511 : NS_IMETHODIMP
8512 0 : nsHTMLEditRules::DidInsertNode(nsIDOMNode *aNode,
8513 : nsIDOMNode *aParent,
8514 : PRInt32 aPosition,
8515 : nsresult aResult)
8516 : {
8517 0 : if (!mListenerEnabled) {
8518 0 : return NS_OK;
8519 : }
8520 0 : nsresult res = mUtilRange->SelectNode(aNode);
8521 0 : NS_ENSURE_SUCCESS(res, res);
8522 0 : res = UpdateDocChangeRange(mUtilRange);
8523 0 : return res;
8524 : }
8525 :
8526 :
8527 : NS_IMETHODIMP
8528 0 : nsHTMLEditRules::WillDeleteNode(nsIDOMNode *aChild)
8529 : {
8530 0 : if (!mListenerEnabled) {
8531 0 : return NS_OK;
8532 : }
8533 0 : nsresult res = mUtilRange->SelectNode(aChild);
8534 0 : NS_ENSURE_SUCCESS(res, res);
8535 0 : res = UpdateDocChangeRange(mUtilRange);
8536 0 : return res;
8537 : }
8538 :
8539 :
8540 : NS_IMETHODIMP
8541 0 : nsHTMLEditRules::DidDeleteNode(nsIDOMNode *aChild, nsresult aResult)
8542 : {
8543 0 : return NS_OK;
8544 : }
8545 :
8546 :
8547 : NS_IMETHODIMP
8548 0 : nsHTMLEditRules::WillSplitNode(nsIDOMNode *aExistingRightNode, PRInt32 aOffset)
8549 : {
8550 0 : return NS_OK;
8551 : }
8552 :
8553 :
8554 : NS_IMETHODIMP
8555 0 : nsHTMLEditRules::DidSplitNode(nsIDOMNode *aExistingRightNode,
8556 : PRInt32 aOffset,
8557 : nsIDOMNode *aNewLeftNode,
8558 : nsresult aResult)
8559 : {
8560 0 : if (!mListenerEnabled) {
8561 0 : return NS_OK;
8562 : }
8563 0 : nsresult res = mUtilRange->SetStart(aNewLeftNode, 0);
8564 0 : NS_ENSURE_SUCCESS(res, res);
8565 0 : res = mUtilRange->SetEnd(aExistingRightNode, 0);
8566 0 : NS_ENSURE_SUCCESS(res, res);
8567 0 : res = UpdateDocChangeRange(mUtilRange);
8568 0 : return res;
8569 : }
8570 :
8571 :
8572 : NS_IMETHODIMP
8573 0 : nsHTMLEditRules::WillJoinNodes(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, nsIDOMNode *aParent)
8574 : {
8575 0 : if (!mListenerEnabled) {
8576 0 : return NS_OK;
8577 : }
8578 : // remember split point
8579 0 : nsresult res = nsEditor::GetLengthOfDOMNode(aLeftNode, mJoinOffset);
8580 0 : return res;
8581 : }
8582 :
8583 :
8584 : NS_IMETHODIMP
8585 0 : nsHTMLEditRules::DidJoinNodes(nsIDOMNode *aLeftNode,
8586 : nsIDOMNode *aRightNode,
8587 : nsIDOMNode *aParent,
8588 : nsresult aResult)
8589 : {
8590 0 : if (!mListenerEnabled) {
8591 0 : return NS_OK;
8592 : }
8593 : // assumption that Join keeps the righthand node
8594 0 : nsresult res = mUtilRange->SetStart(aRightNode, mJoinOffset);
8595 0 : NS_ENSURE_SUCCESS(res, res);
8596 0 : res = mUtilRange->SetEnd(aRightNode, mJoinOffset);
8597 0 : NS_ENSURE_SUCCESS(res, res);
8598 0 : res = UpdateDocChangeRange(mUtilRange);
8599 0 : return res;
8600 : }
8601 :
8602 :
8603 : NS_IMETHODIMP
8604 0 : nsHTMLEditRules::WillInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsAString &aString)
8605 : {
8606 0 : return NS_OK;
8607 : }
8608 :
8609 :
8610 : NS_IMETHODIMP
8611 0 : nsHTMLEditRules::DidInsertText(nsIDOMCharacterData *aTextNode,
8612 : PRInt32 aOffset,
8613 : const nsAString &aString,
8614 : nsresult aResult)
8615 : {
8616 0 : if (!mListenerEnabled) {
8617 0 : return NS_OK;
8618 : }
8619 0 : PRInt32 length = aString.Length();
8620 0 : nsCOMPtr<nsIDOMNode> theNode = do_QueryInterface(aTextNode);
8621 0 : nsresult res = mUtilRange->SetStart(theNode, aOffset);
8622 0 : NS_ENSURE_SUCCESS(res, res);
8623 0 : res = mUtilRange->SetEnd(theNode, aOffset+length);
8624 0 : NS_ENSURE_SUCCESS(res, res);
8625 0 : res = UpdateDocChangeRange(mUtilRange);
8626 0 : return res;
8627 : }
8628 :
8629 :
8630 : NS_IMETHODIMP
8631 0 : nsHTMLEditRules::WillDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength)
8632 : {
8633 0 : return NS_OK;
8634 : }
8635 :
8636 :
8637 : NS_IMETHODIMP
8638 0 : nsHTMLEditRules::DidDeleteText(nsIDOMCharacterData *aTextNode,
8639 : PRInt32 aOffset,
8640 : PRInt32 aLength,
8641 : nsresult aResult)
8642 : {
8643 0 : if (!mListenerEnabled) {
8644 0 : return NS_OK;
8645 : }
8646 0 : nsCOMPtr<nsIDOMNode> theNode = do_QueryInterface(aTextNode);
8647 0 : nsresult res = mUtilRange->SetStart(theNode, aOffset);
8648 0 : NS_ENSURE_SUCCESS(res, res);
8649 0 : res = mUtilRange->SetEnd(theNode, aOffset);
8650 0 : NS_ENSURE_SUCCESS(res, res);
8651 0 : res = UpdateDocChangeRange(mUtilRange);
8652 0 : return res;
8653 : }
8654 :
8655 : NS_IMETHODIMP
8656 0 : nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection)
8657 : {
8658 0 : if (!mListenerEnabled) {
8659 0 : return NS_OK;
8660 : }
8661 : // get the (collapsed) selection location
8662 0 : nsCOMPtr<nsIDOMNode> selNode;
8663 : PRInt32 selOffset;
8664 :
8665 0 : nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
8666 0 : NS_ENSURE_SUCCESS(res, res);
8667 0 : res = mUtilRange->SetStart(selNode, selOffset);
8668 0 : NS_ENSURE_SUCCESS(res, res);
8669 0 : res = mHTMLEditor->GetEndNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
8670 0 : NS_ENSURE_SUCCESS(res, res);
8671 0 : res = mUtilRange->SetEnd(selNode, selOffset);
8672 0 : NS_ENSURE_SUCCESS(res, res);
8673 0 : res = UpdateDocChangeRange(mUtilRange);
8674 0 : return res;
8675 : }
8676 :
8677 : NS_IMETHODIMP
8678 0 : nsHTMLEditRules::DidDeleteSelection(nsISelection *aSelection)
8679 : {
8680 0 : return NS_OK;
8681 : }
8682 :
8683 : // Let's remove all alignment hints in the children of aNode; it can
8684 : // be an ALIGN attribute (in case we just remove it) or a CENTER
8685 : // element (here we have to remove the container and keep its
8686 : // children). We break on tables and don't look at their children.
8687 : nsresult
8688 0 : nsHTMLEditRules::RemoveAlignment(nsIDOMNode * aNode, const nsAString & aAlignType, bool aChildrenOnly)
8689 : {
8690 0 : NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
8691 :
8692 0 : if (mHTMLEditor->IsTextNode(aNode) || nsHTMLEditUtils::IsTable(aNode)) return NS_OK;
8693 0 : nsresult res = NS_OK;
8694 :
8695 0 : nsCOMPtr<nsIDOMNode> child = aNode,tmp;
8696 0 : if (aChildrenOnly)
8697 : {
8698 0 : aNode->GetFirstChild(getter_AddRefs(child));
8699 : }
8700 0 : bool useCSS = mHTMLEditor->IsCSSEnabled();
8701 :
8702 0 : while (child)
8703 : {
8704 0 : if (aChildrenOnly) {
8705 : // get the next sibling right now because we could have to remove child
8706 0 : child->GetNextSibling(getter_AddRefs(tmp));
8707 : }
8708 : else
8709 : {
8710 0 : tmp = nsnull;
8711 : }
8712 : bool isBlock;
8713 0 : res = mHTMLEditor->NodeIsBlockStatic(child, &isBlock);
8714 0 : NS_ENSURE_SUCCESS(res, res);
8715 :
8716 0 : if (nsEditor::NodeIsType(child, nsEditProperty::center))
8717 : {
8718 : // the current node is a CENTER element
8719 : // first remove children's alignment
8720 0 : res = RemoveAlignment(child, aAlignType, true);
8721 0 : NS_ENSURE_SUCCESS(res, res);
8722 :
8723 : // we may have to insert BRs in first and last position of element's children
8724 : // if the nodes before/after are not blocks and not BRs
8725 0 : res = MakeSureElemStartsOrEndsOnCR(child);
8726 0 : NS_ENSURE_SUCCESS(res, res);
8727 :
8728 : // now remove the CENTER container
8729 0 : res = mHTMLEditor->RemoveContainer(child);
8730 0 : NS_ENSURE_SUCCESS(res, res);
8731 : }
8732 0 : else if (isBlock || nsHTMLEditUtils::IsHR(child))
8733 : {
8734 : // the current node is a block element
8735 0 : nsCOMPtr<nsIDOMElement> curElem = do_QueryInterface(child);
8736 0 : if (nsHTMLEditUtils::SupportsAlignAttr(child))
8737 : {
8738 : // remove the ALIGN attribute if this element can have it
8739 0 : res = mHTMLEditor->RemoveAttribute(curElem, NS_LITERAL_STRING("align"));
8740 0 : NS_ENSURE_SUCCESS(res, res);
8741 : }
8742 0 : if (useCSS)
8743 : {
8744 0 : if (nsHTMLEditUtils::IsTable(child) || nsHTMLEditUtils::IsHR(child))
8745 : {
8746 0 : res = mHTMLEditor->SetAttributeOrEquivalent(curElem, NS_LITERAL_STRING("align"), aAlignType, false);
8747 : }
8748 : else
8749 : {
8750 0 : nsAutoString dummyCssValue;
8751 0 : res = mHTMLEditor->mHTMLCSSUtils->RemoveCSSInlineStyle(child, nsEditProperty::cssTextAlign, dummyCssValue);
8752 : }
8753 0 : NS_ENSURE_SUCCESS(res, res);
8754 : }
8755 0 : if (!nsHTMLEditUtils::IsTable(child))
8756 : {
8757 : // unless this is a table, look at children
8758 0 : res = RemoveAlignment(child, aAlignType, true);
8759 0 : NS_ENSURE_SUCCESS(res, res);
8760 : }
8761 : }
8762 0 : child = tmp;
8763 : }
8764 0 : return NS_OK;
8765 : }
8766 :
8767 : // Let's insert a BR as first (resp. last) child of aNode if its
8768 : // first (resp. last) child is not a block nor a BR, and if the
8769 : // previous (resp. next) sibling is not a block nor a BR
8770 : nsresult
8771 0 : nsHTMLEditRules::MakeSureElemStartsOrEndsOnCR(nsIDOMNode *aNode, bool aStarts)
8772 : {
8773 0 : NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
8774 :
8775 0 : nsCOMPtr<nsIDOMNode> child;
8776 : nsresult res;
8777 0 : if (aStarts)
8778 : {
8779 0 : res = mHTMLEditor->GetFirstEditableChild(aNode, address_of(child));
8780 : }
8781 : else
8782 : {
8783 0 : res = mHTMLEditor->GetLastEditableChild(aNode, address_of(child));
8784 : }
8785 0 : NS_ENSURE_SUCCESS(res, res);
8786 0 : NS_ENSURE_TRUE(child, NS_OK);
8787 : bool isChildBlock;
8788 0 : res = mHTMLEditor->NodeIsBlockStatic(child, &isChildBlock);
8789 0 : NS_ENSURE_SUCCESS(res, res);
8790 0 : bool foundCR = false;
8791 0 : if (isChildBlock || nsTextEditUtils::IsBreak(child))
8792 : {
8793 0 : foundCR = true;
8794 : }
8795 : else
8796 : {
8797 0 : nsCOMPtr<nsIDOMNode> sibling;
8798 0 : if (aStarts)
8799 : {
8800 0 : res = mHTMLEditor->GetPriorHTMLSibling(aNode, address_of(sibling));
8801 : }
8802 : else
8803 : {
8804 0 : res = mHTMLEditor->GetNextHTMLSibling(aNode, address_of(sibling));
8805 : }
8806 0 : NS_ENSURE_SUCCESS(res, res);
8807 0 : if (sibling)
8808 : {
8809 : bool isBlock;
8810 0 : res = mHTMLEditor->NodeIsBlockStatic(sibling, &isBlock);
8811 0 : NS_ENSURE_SUCCESS(res, res);
8812 0 : if (isBlock || nsTextEditUtils::IsBreak(sibling))
8813 : {
8814 0 : foundCR = true;
8815 : }
8816 : }
8817 : else
8818 : {
8819 0 : foundCR = true;
8820 : }
8821 : }
8822 0 : if (!foundCR)
8823 : {
8824 0 : nsCOMPtr<nsIDOMNode> brNode;
8825 0 : PRInt32 offset = 0;
8826 0 : if (!aStarts)
8827 : {
8828 0 : nsCOMPtr<nsIDOMNodeList> childNodes;
8829 0 : res = aNode->GetChildNodes(getter_AddRefs(childNodes));
8830 0 : NS_ENSURE_SUCCESS(res, res);
8831 0 : NS_ENSURE_TRUE(childNodes, NS_ERROR_NULL_POINTER);
8832 : PRUint32 childCount;
8833 0 : res = childNodes->GetLength(&childCount);
8834 0 : NS_ENSURE_SUCCESS(res, res);
8835 0 : offset = childCount;
8836 : }
8837 0 : res = mHTMLEditor->CreateBR(aNode, offset, address_of(brNode));
8838 0 : NS_ENSURE_SUCCESS(res, res);
8839 : }
8840 0 : return NS_OK;
8841 : }
8842 :
8843 : nsresult
8844 0 : nsHTMLEditRules::MakeSureElemStartsOrEndsOnCR(nsIDOMNode *aNode)
8845 : {
8846 0 : nsresult res = MakeSureElemStartsOrEndsOnCR(aNode, false);
8847 0 : NS_ENSURE_SUCCESS(res, res);
8848 0 : res = MakeSureElemStartsOrEndsOnCR(aNode, true);
8849 0 : return res;
8850 : }
8851 :
8852 : nsresult
8853 0 : nsHTMLEditRules::AlignBlock(nsIDOMElement * aElement, const nsAString * aAlignType, bool aContentsOnly)
8854 : {
8855 0 : NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
8856 :
8857 0 : nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aElement);
8858 0 : bool isBlock = IsBlockNode(node);
8859 0 : if (!isBlock && !nsHTMLEditUtils::IsHR(node)) {
8860 : // we deal only with blocks; early way out
8861 0 : return NS_OK;
8862 : }
8863 :
8864 0 : nsresult res = RemoveAlignment(node, *aAlignType, aContentsOnly);
8865 0 : NS_ENSURE_SUCCESS(res, res);
8866 0 : NS_NAMED_LITERAL_STRING(attr, "align");
8867 0 : if (mHTMLEditor->IsCSSEnabled()) {
8868 : // let's use CSS alignment; we use margin-left and margin-right for tables
8869 : // and text-align for other block-level elements
8870 0 : res = mHTMLEditor->SetAttributeOrEquivalent(aElement, attr, *aAlignType, false);
8871 0 : NS_ENSURE_SUCCESS(res, res);
8872 : }
8873 : else {
8874 : // HTML case; this code is supposed to be called ONLY if the element
8875 : // supports the align attribute but we'll never know...
8876 0 : if (nsHTMLEditUtils::SupportsAlignAttr(node)) {
8877 0 : res = mHTMLEditor->SetAttribute(aElement, attr, *aAlignType);
8878 0 : NS_ENSURE_SUCCESS(res, res);
8879 : }
8880 : }
8881 0 : return NS_OK;
8882 : }
8883 :
8884 : nsresult
8885 0 : nsHTMLEditRules::RelativeChangeIndentationOfElementNode(nsIDOMNode *aNode, PRInt8 aRelativeChange)
8886 : {
8887 0 : NS_ENSURE_ARG_POINTER(aNode);
8888 :
8889 0 : if (aRelativeChange != 1 && aRelativeChange != -1) {
8890 0 : return NS_ERROR_ILLEGAL_VALUE;
8891 : }
8892 :
8893 0 : nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
8894 0 : if (!element) {
8895 0 : return NS_OK;
8896 : }
8897 :
8898 0 : nsIAtom* marginProperty = MarginPropertyAtomForIndent(mHTMLEditor->mHTMLCSSUtils, element);
8899 0 : nsAutoString value;
8900 0 : mHTMLEditor->mHTMLCSSUtils->GetSpecifiedProperty(aNode, marginProperty, value);
8901 : float f;
8902 0 : nsCOMPtr<nsIAtom> unit;
8903 0 : mHTMLEditor->mHTMLCSSUtils->ParseLength(value, &f, getter_AddRefs(unit));
8904 0 : if (0 == f) {
8905 0 : nsAutoString defaultLengthUnit;
8906 0 : mHTMLEditor->mHTMLCSSUtils->GetDefaultLengthUnit(defaultLengthUnit);
8907 0 : unit = do_GetAtom(defaultLengthUnit);
8908 : }
8909 0 : if (nsEditProperty::cssInUnit == unit)
8910 0 : f += NS_EDITOR_INDENT_INCREMENT_IN * aRelativeChange;
8911 0 : else if (nsEditProperty::cssCmUnit == unit)
8912 0 : f += NS_EDITOR_INDENT_INCREMENT_CM * aRelativeChange;
8913 0 : else if (nsEditProperty::cssMmUnit == unit)
8914 0 : f += NS_EDITOR_INDENT_INCREMENT_MM * aRelativeChange;
8915 0 : else if (nsEditProperty::cssPtUnit == unit)
8916 0 : f += NS_EDITOR_INDENT_INCREMENT_PT * aRelativeChange;
8917 0 : else if (nsEditProperty::cssPcUnit == unit)
8918 0 : f += NS_EDITOR_INDENT_INCREMENT_PC * aRelativeChange;
8919 0 : else if (nsEditProperty::cssEmUnit == unit)
8920 0 : f += NS_EDITOR_INDENT_INCREMENT_EM * aRelativeChange;
8921 0 : else if (nsEditProperty::cssExUnit == unit)
8922 0 : f += NS_EDITOR_INDENT_INCREMENT_EX * aRelativeChange;
8923 0 : else if (nsEditProperty::cssPxUnit == unit)
8924 0 : f += NS_EDITOR_INDENT_INCREMENT_PX * aRelativeChange;
8925 0 : else if (nsEditProperty::cssPercentUnit == unit)
8926 0 : f += NS_EDITOR_INDENT_INCREMENT_PERCENT * aRelativeChange;
8927 :
8928 0 : if (0 < f) {
8929 0 : nsAutoString newValue;
8930 0 : newValue.AppendFloat(f);
8931 0 : newValue.Append(nsDependentAtomString(unit));
8932 0 : mHTMLEditor->mHTMLCSSUtils->SetCSSProperty(element, marginProperty, newValue, false);
8933 0 : return NS_OK;
8934 : }
8935 :
8936 0 : mHTMLEditor->mHTMLCSSUtils->RemoveCSSProperty(element, marginProperty, value, false);
8937 :
8938 : // remove unnecessary DIV blocks:
8939 : // we could skip this section but that would cause a FAIL in
8940 : // editor/libeditor/html/tests/browserscope/richtext.html, which expects
8941 : // to unapply a CSS "indent" (<div style="margin-left: 40px;">) by
8942 : // removing the DIV container instead of just removing the CSS property.
8943 0 : nsCOMPtr<dom::Element> node = do_QueryInterface(aNode);
8944 0 : if (!node || !node->IsHTML(nsGkAtoms::div) ||
8945 0 : node == mHTMLEditor->GetActiveEditingHost() ||
8946 0 : !mHTMLEditor->IsNodeInActiveEditor(node) ||
8947 0 : nsHTMLEditor::HasAttributes(node)) {
8948 0 : return NS_OK;
8949 : }
8950 :
8951 0 : return mHTMLEditor->RemoveContainer(element);
8952 : }
8953 :
8954 : //
8955 : // Support for Absolute Positioning
8956 : //
8957 :
8958 : nsresult
8959 0 : nsHTMLEditRules::WillAbsolutePosition(nsISelection *aSelection, bool *aCancel, bool * aHandled)
8960 : {
8961 0 : if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
8962 0 : nsresult res = WillInsert(aSelection, aCancel);
8963 0 : NS_ENSURE_SUCCESS(res, res);
8964 :
8965 : // initialize out param
8966 : // we want to ignore result of WillInsert()
8967 0 : *aCancel = false;
8968 0 : *aHandled = true;
8969 :
8970 0 : nsCOMPtr<nsIDOMElement> focusElement;
8971 0 : res = mHTMLEditor->GetSelectionContainer(getter_AddRefs(focusElement));
8972 0 : if (focusElement) {
8973 0 : nsCOMPtr<nsIDOMNode> node = do_QueryInterface(focusElement);
8974 0 : if (nsHTMLEditUtils::IsImage(node)) {
8975 0 : mNewBlock = node;
8976 0 : return NS_OK;
8977 : }
8978 : }
8979 :
8980 0 : res = NormalizeSelection(aSelection);
8981 0 : NS_ENSURE_SUCCESS(res, res);
8982 0 : nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
8983 :
8984 : // convert the selection ranges into "promoted" selection ranges:
8985 : // this basically just expands the range to include the immediate
8986 : // block parent, and then further expands to include any ancestors
8987 : // whose children are all in the range
8988 :
8989 0 : nsCOMArray<nsIDOMRange> arrayOfRanges;
8990 0 : res = GetPromotedRanges(aSelection, arrayOfRanges, kSetAbsolutePosition);
8991 0 : NS_ENSURE_SUCCESS(res, res);
8992 :
8993 : // use these ranges to contruct a list of nodes to act on.
8994 0 : nsCOMArray<nsIDOMNode> arrayOfNodes;
8995 0 : res = GetNodesForOperation(arrayOfRanges, arrayOfNodes, kSetAbsolutePosition);
8996 0 : NS_ENSURE_SUCCESS(res, res);
8997 :
8998 0 : NS_NAMED_LITERAL_STRING(divType, "div");
8999 :
9000 :
9001 : // if nothing visible in list, make an empty block
9002 0 : if (ListIsEmptyLine(arrayOfNodes))
9003 : {
9004 0 : nsCOMPtr<nsIDOMNode> parent, thePositionedDiv;
9005 : PRInt32 offset;
9006 :
9007 : // get selection location
9008 0 : res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(parent), &offset);
9009 0 : NS_ENSURE_SUCCESS(res, res);
9010 : // make sure we can put a block here
9011 0 : res = SplitAsNeeded(&divType, address_of(parent), &offset);
9012 0 : NS_ENSURE_SUCCESS(res, res);
9013 0 : res = mHTMLEditor->CreateNode(divType, parent, offset, getter_AddRefs(thePositionedDiv));
9014 0 : NS_ENSURE_SUCCESS(res, res);
9015 : // remember our new block for postprocessing
9016 0 : mNewBlock = thePositionedDiv;
9017 : // delete anything that was in the list of nodes
9018 0 : for (PRInt32 j = arrayOfNodes.Count() - 1; j >= 0; --j)
9019 : {
9020 0 : nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[0];
9021 0 : res = mHTMLEditor->DeleteNode(curNode);
9022 0 : NS_ENSURE_SUCCESS(res, res);
9023 0 : res = arrayOfNodes.RemoveObjectAt(0);
9024 0 : NS_ENSURE_SUCCESS(res, res);
9025 : }
9026 : // put selection in new block
9027 0 : res = aSelection->Collapse(thePositionedDiv,0);
9028 0 : selectionResetter.Abort(); // to prevent selection reseter from overriding us.
9029 0 : *aHandled = true;
9030 0 : return res;
9031 : }
9032 :
9033 : // Ok, now go through all the nodes and put them in a blockquote,
9034 : // or whatever is appropriate. Wohoo!
9035 : PRInt32 i;
9036 0 : nsCOMPtr<nsIDOMNode> curParent, curPositionedDiv, curList, indentedLI, sibling;
9037 0 : PRInt32 listCount = arrayOfNodes.Count();
9038 0 : for (i=0; i<listCount; i++)
9039 : {
9040 : // here's where we actually figure out what to do
9041 0 : nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
9042 :
9043 : // Ignore all non-editable nodes. Leave them be.
9044 0 : if (!mHTMLEditor->IsEditable(curNode)) continue;
9045 :
9046 : PRInt32 offset;
9047 0 : res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset);
9048 0 : NS_ENSURE_SUCCESS(res, res);
9049 :
9050 : // some logic for putting list items into nested lists...
9051 0 : if (nsHTMLEditUtils::IsList(curParent))
9052 : {
9053 : // check to see if curList is still appropriate. Which it is if
9054 : // curNode is still right after it in the same list.
9055 0 : if (curList)
9056 : {
9057 0 : sibling = nsnull;
9058 0 : mHTMLEditor->GetPriorHTMLSibling(curNode, address_of(sibling));
9059 : }
9060 :
9061 0 : if (!curList || (sibling && sibling != curList) )
9062 : {
9063 0 : nsAutoString listTag;
9064 0 : nsEditor::GetTagString(curParent,listTag);
9065 0 : ToLowerCase(listTag);
9066 : // create a new nested list of correct type
9067 0 : res = SplitAsNeeded(&listTag, address_of(curParent), &offset);
9068 0 : NS_ENSURE_SUCCESS(res, res);
9069 0 : if (!curPositionedDiv) {
9070 : PRInt32 parentOffset;
9071 0 : nsCOMPtr<nsIDOMNode> curParentParent;
9072 0 : res = nsEditor::GetNodeLocation(curParent, address_of(curParentParent), &parentOffset);
9073 0 : res = mHTMLEditor->CreateNode(divType, curParentParent, parentOffset, getter_AddRefs(curPositionedDiv));
9074 0 : mNewBlock = curPositionedDiv;
9075 : }
9076 0 : res = mHTMLEditor->CreateNode(listTag, curPositionedDiv, -1, getter_AddRefs(curList));
9077 0 : NS_ENSURE_SUCCESS(res, res);
9078 : // curList is now the correct thing to put curNode in
9079 : // remember our new block for postprocessing
9080 : // mNewBlock = curList;
9081 : }
9082 : // tuck the node into the end of the active list
9083 0 : res = mHTMLEditor->MoveNode(curNode, curList, -1);
9084 0 : NS_ENSURE_SUCCESS(res, res);
9085 : // forget curPositionedDiv, if any
9086 : // curPositionedDiv = nsnull;
9087 : }
9088 :
9089 : else // not a list item, use blockquote?
9090 : {
9091 : // if we are inside a list item, we don't want to blockquote, we want
9092 : // to sublist the list item. We may have several nodes listed in the
9093 : // array of nodes to act on, that are in the same list item. Since
9094 : // we only want to indent that li once, we must keep track of the most
9095 : // recent indented list item, and not indent it if we find another node
9096 : // to act on that is still inside the same li.
9097 0 : nsCOMPtr<nsIDOMNode> listitem=IsInListItem(curNode);
9098 0 : if (listitem)
9099 : {
9100 0 : if (indentedLI == listitem) continue; // already indented this list item
9101 0 : res = nsEditor::GetNodeLocation(listitem, address_of(curParent), &offset);
9102 0 : NS_ENSURE_SUCCESS(res, res);
9103 : // check to see if curList is still appropriate. Which it is if
9104 : // curNode is still right after it in the same list.
9105 0 : if (curList)
9106 : {
9107 0 : sibling = nsnull;
9108 0 : mHTMLEditor->GetPriorHTMLSibling(curNode, address_of(sibling));
9109 : }
9110 :
9111 0 : if (!curList || (sibling && sibling != curList) )
9112 : {
9113 0 : nsAutoString listTag;
9114 0 : nsEditor::GetTagString(curParent,listTag);
9115 0 : ToLowerCase(listTag);
9116 : // create a new nested list of correct type
9117 0 : res = SplitAsNeeded(&listTag, address_of(curParent), &offset);
9118 0 : NS_ENSURE_SUCCESS(res, res);
9119 0 : if (!curPositionedDiv) {
9120 : PRInt32 parentOffset;
9121 0 : nsCOMPtr<nsIDOMNode> curParentParent;
9122 0 : res = nsEditor::GetNodeLocation(curParent, address_of(curParentParent), &parentOffset);
9123 0 : res = mHTMLEditor->CreateNode(divType, curParentParent, parentOffset, getter_AddRefs(curPositionedDiv));
9124 0 : mNewBlock = curPositionedDiv;
9125 : }
9126 0 : res = mHTMLEditor->CreateNode(listTag, curPositionedDiv, -1, getter_AddRefs(curList));
9127 0 : NS_ENSURE_SUCCESS(res, res);
9128 : }
9129 0 : res = mHTMLEditor->MoveNode(listitem, curList, -1);
9130 0 : NS_ENSURE_SUCCESS(res, res);
9131 : // remember we indented this li
9132 0 : indentedLI = listitem;
9133 : }
9134 :
9135 : else
9136 : {
9137 : // need to make a div to put things in if we haven't already
9138 :
9139 0 : if (!curPositionedDiv)
9140 : {
9141 0 : if (nsHTMLEditUtils::IsDiv(curNode))
9142 : {
9143 0 : curPositionedDiv = curNode;
9144 0 : mNewBlock = curPositionedDiv;
9145 0 : curList = nsnull;
9146 0 : continue;
9147 : }
9148 0 : res = SplitAsNeeded(&divType, address_of(curParent), &offset);
9149 0 : NS_ENSURE_SUCCESS(res, res);
9150 0 : res = mHTMLEditor->CreateNode(divType, curParent, offset, getter_AddRefs(curPositionedDiv));
9151 0 : NS_ENSURE_SUCCESS(res, res);
9152 : // remember our new block for postprocessing
9153 0 : mNewBlock = curPositionedDiv;
9154 : // curPositionedDiv is now the correct thing to put curNode in
9155 : }
9156 :
9157 : // tuck the node into the end of the active blockquote
9158 0 : res = mHTMLEditor->MoveNode(curNode, curPositionedDiv, -1);
9159 0 : NS_ENSURE_SUCCESS(res, res);
9160 : // forget curList, if any
9161 0 : curList = nsnull;
9162 : }
9163 : }
9164 : }
9165 0 : return res;
9166 : }
9167 :
9168 : nsresult
9169 0 : nsHTMLEditRules::DidAbsolutePosition()
9170 : {
9171 0 : nsCOMPtr<nsIHTMLAbsPosEditor> absPosHTMLEditor = mHTMLEditor;
9172 0 : nsCOMPtr<nsIDOMElement> elt = do_QueryInterface(mNewBlock);
9173 0 : return absPosHTMLEditor->AbsolutelyPositionElement(elt, true);
9174 : }
9175 :
9176 : nsresult
9177 0 : nsHTMLEditRules::WillRemoveAbsolutePosition(nsISelection *aSelection, bool *aCancel, bool * aHandled)
9178 : {
9179 0 : if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
9180 0 : nsresult res = WillInsert(aSelection, aCancel);
9181 0 : NS_ENSURE_SUCCESS(res, res);
9182 :
9183 : // initialize out param
9184 : // we want to ignore aCancel from WillInsert()
9185 0 : *aCancel = false;
9186 0 : *aHandled = true;
9187 :
9188 0 : nsCOMPtr<nsIDOMElement> elt;
9189 0 : res = mHTMLEditor->GetAbsolutelyPositionedSelectionContainer(getter_AddRefs(elt));
9190 0 : NS_ENSURE_SUCCESS(res, res);
9191 :
9192 0 : nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
9193 :
9194 0 : nsCOMPtr<nsIHTMLAbsPosEditor> absPosHTMLEditor = mHTMLEditor;
9195 0 : return absPosHTMLEditor->AbsolutelyPositionElement(elt, false);
9196 : }
9197 :
9198 : nsresult
9199 0 : nsHTMLEditRules::WillRelativeChangeZIndex(nsISelection *aSelection,
9200 : PRInt32 aChange,
9201 : bool *aCancel,
9202 : bool * aHandled)
9203 : {
9204 0 : if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
9205 0 : nsresult res = WillInsert(aSelection, aCancel);
9206 0 : NS_ENSURE_SUCCESS(res, res);
9207 :
9208 : // initialize out param
9209 : // we want to ignore aCancel from WillInsert()
9210 0 : *aCancel = false;
9211 0 : *aHandled = true;
9212 :
9213 0 : nsCOMPtr<nsIDOMElement> elt;
9214 0 : res = mHTMLEditor->GetAbsolutelyPositionedSelectionContainer(getter_AddRefs(elt));
9215 0 : NS_ENSURE_SUCCESS(res, res);
9216 :
9217 0 : nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
9218 :
9219 0 : nsCOMPtr<nsIHTMLAbsPosEditor> absPosHTMLEditor = mHTMLEditor;
9220 : PRInt32 zIndex;
9221 0 : return absPosHTMLEditor->RelativeChangeElementZIndex(elt, aChange, &zIndex);
9222 : }
9223 :
9224 : NS_IMETHODIMP
9225 0 : nsHTMLEditRules::DocumentModified()
9226 : {
9227 0 : nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, &nsHTMLEditRules::DocumentModifiedWorker));
9228 0 : return NS_OK;
9229 : }
9230 :
9231 : void
9232 0 : nsHTMLEditRules::DocumentModifiedWorker()
9233 : {
9234 0 : if (!mHTMLEditor) {
9235 0 : return;
9236 : }
9237 :
9238 : // DeleteNode below may cause a flush, which could destroy the editor
9239 0 : nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
9240 :
9241 0 : nsCOMPtr<nsIHTMLEditor> kungFuDeathGrip(mHTMLEditor);
9242 0 : nsCOMPtr<nsISelection> selection;
9243 0 : nsresult rv = mHTMLEditor->GetSelection(getter_AddRefs(selection));
9244 0 : if (NS_FAILED(rv)) {
9245 : return;
9246 : }
9247 :
9248 : // Delete our bogus node, if we have one, since the document might not be
9249 : // empty any more.
9250 0 : if (mBogusNode) {
9251 0 : mEditor->DeleteNode(mBogusNode);
9252 0 : mBogusNode = nsnull;
9253 : }
9254 :
9255 : // Try to recreate the bogus node if needed.
9256 0 : CreateBogusNodeIfNeeded(selection);
9257 : }
|