1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* ***** BEGIN LICENSE BLOCK *****
3
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
5
* The contents of this file are subject to the Netscape Public License
6
* Version 1.1 (the "License"); you may not use this file except in
7
* compliance with the License. You may obtain a copy of the License at
8
* http://www.mozilla.org/NPL/
10
* Software distributed under the License is distributed on an "AS IS" basis,
11
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12
* for the specific language governing rights and limitations under the
15
* The Original Code is mozilla.org code.
17
* The Initial Developer of the Original Code is
18
* Netscape Communications Corporation.
19
* Portions created by the Initial Developer are Copyright (C) 1998
20
* the Initial Developer. All Rights Reserved.
23
* Pierre Phaneuf <pp@ludusdesign.com>
24
* Daniel Glazman <glazman@netscape.com>
26
* Alternatively, the contents of this file may be used under the terms of
27
* either the GNU General Public License Version 2 or later (the "GPL"), or
28
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29
* in which case the provisions of the GPL or the LGPL are applicable instead
30
* of those above. If you wish to allow use of your version of this file only
31
* under the terms of either the GPL or the LGPL, and not to allow others to
32
* use your version of this file under the terms of the NPL, indicate your
33
* decision by deleting the provisions above and replace them with the notice
34
* and other provisions required by the GPL or the LGPL. If you do not delete
35
* the provisions above, a recipient may use your version of this file under
36
* the terms of any one of the NPL, the GPL or the LGPL.
38
* ***** END LICENSE BLOCK ***** */
40
/* build on macs with low memory */
41
#if defined(XP_MAC) && defined(MOZ_MAC_LOWMEM)
42
#pragma optimization_level 1
45
#include "nsHTMLEditRules.h"
48
#include "nsTextEditUtils.h"
49
#include "nsHTMLEditUtils.h"
50
#include "nsHTMLCSSUtils.h"
51
#include "nsHTMLEditor.h"
53
#include "nsIServiceManager.h"
55
#include "nsIContent.h"
56
#include "nsIContentIterator.h"
57
#include "nsIDOMNode.h"
58
#include "nsIDOMText.h"
59
#include "nsIDOMElement.h"
60
#include "nsIDOMNodeList.h"
61
#include "nsISelection.h"
62
#include "nsISelectionPrivate.h"
63
#include "nsISelectionController.h"
64
#include "nsIDOMRange.h"
65
#include "nsIDOMNSRange.h"
66
#include "nsIDOMCharacterData.h"
67
#include "nsIEnumerator.h"
68
#include "nsIPresShell.h"
69
#include "nsIPrefBranch.h"
70
#include "nsIPrefService.h"
71
#include "nsIDOMNamedNodeMap.h"
73
#include "nsEditorUtils.h"
74
#include "nsWSRunObject.h"
76
#include "InsertTextTxn.h"
77
#include "DeleteTextTxn.h"
78
#include "nsReadableUtils.h"
79
#include "nsUnicharUtils.h"
82
//const static char* kMOZEditorBogusNodeAttr="MOZ_EDITOR_BOGUS_NODE";
83
//const static char* kMOZEditorBogusNodeValue="TRUE";
93
/********************************************************
94
* first some helpful funcotrs we will use
95
********************************************************/
97
static PRBool IsBlockNode(nsIDOMNode* node)
99
PRBool isBlock (PR_FALSE);
100
nsHTMLEditor::NodeIsBlockStatic(node, &isBlock);
104
static PRBool IsInlineNode(nsIDOMNode* node)
106
return !IsBlockNode(node);
109
class nsTableCellAndListItemFunctor : public nsBoolDomIterFunctor
112
virtual PRBool operator()(nsIDOMNode* aNode) // used to build list of all li's, td's & th's iterator covers
114
if (nsHTMLEditUtils::IsTableCell(aNode)) return PR_TRUE;
115
if (nsHTMLEditUtils::IsListItem(aNode)) return PR_TRUE;
120
class nsBRNodeFunctor : public nsBoolDomIterFunctor
123
virtual PRBool operator()(nsIDOMNode* aNode)
125
if (nsTextEditUtils::IsBreak(aNode)) return PR_TRUE;
130
class nsEmptyFunctor : public nsBoolDomIterFunctor
133
nsEmptyFunctor(nsHTMLEditor* editor) : mHTMLEditor(editor) {}
134
virtual PRBool operator()(nsIDOMNode* aNode)
136
if (nsHTMLEditUtils::IsListItem(aNode) ||
137
nsHTMLEditUtils::IsTableCellOrCaption(aNode) ||
138
nsHTMLEditUtils::IsHeader(aNode) ||
139
nsHTMLEditUtils::IsParagraph(aNode) ||
140
nsHTMLEditUtils::IsBlockquote(aNode) ||
141
nsHTMLEditUtils::IsPre(aNode) ||
142
nsHTMLEditUtils::IsAddress(aNode) ||
143
nsHTMLEditUtils::IsDiv(aNode))
146
nsresult res = mHTMLEditor->IsEmptyNode(aNode, &bIsEmptyNode, PR_FALSE, PR_FALSE);
147
if (NS_FAILED(res)) return PR_FALSE;
154
nsHTMLEditor* mHTMLEditor;
157
class nsEditableTextFunctor : public nsBoolDomIterFunctor
160
nsEditableTextFunctor(nsHTMLEditor* editor) : mHTMLEditor(editor) {}
161
virtual PRBool operator()(nsIDOMNode* aNode)
163
if (nsEditor::IsTextNode(aNode) && mHTMLEditor->IsEditable(aNode))
170
nsHTMLEditor* mHTMLEditor;
174
/********************************************************
175
* routine for making new rules instance
176
********************************************************/
179
NS_NewHTMLEditRules(nsIEditRules** aInstancePtrResult)
181
nsHTMLEditRules * rules = new nsHTMLEditRules();
183
return rules->QueryInterface(NS_GET_IID(nsIEditRules), (void**) aInstancePtrResult);
184
return NS_ERROR_OUT_OF_MEMORY;
187
/********************************************************
188
* Constructor/Destructor
189
********************************************************/
191
nsHTMLEditRules::nsHTMLEditRules() :
192
mDocChangeRange(nsnull)
193
,mListenerEnabled(PR_TRUE)
194
,mReturnInEmptyLIKillsList(PR_TRUE)
195
,mDidDeleteSelection(PR_FALSE)
196
,mDidRangedDelete(PR_FALSE)
200
nsString emptyString;
201
// populate mCachedStyles
202
mCachedStyles[0] = StyleCache(nsEditProperty::b, emptyString, emptyString);
203
mCachedStyles[1] = StyleCache(nsEditProperty::i, emptyString, emptyString);
204
mCachedStyles[2] = StyleCache(nsEditProperty::u, emptyString, emptyString);
205
mCachedStyles[3] = StyleCache(nsEditProperty::font, NS_LITERAL_STRING("face"), emptyString);
206
mCachedStyles[4] = StyleCache(nsEditProperty::font, NS_LITERAL_STRING("size"), emptyString);
207
mCachedStyles[5] = StyleCache(nsEditProperty::font, NS_LITERAL_STRING("color"), emptyString);
208
mCachedStyles[6] = StyleCache(nsEditProperty::tt, emptyString, emptyString);
209
mCachedStyles[7] = StyleCache(nsEditProperty::em, emptyString, emptyString);
210
mCachedStyles[8] = StyleCache(nsEditProperty::strong, emptyString, emptyString);
211
mCachedStyles[9] = StyleCache(nsEditProperty::dfn, emptyString, emptyString);
212
mCachedStyles[10] = StyleCache(nsEditProperty::code, emptyString, emptyString);
213
mCachedStyles[11] = StyleCache(nsEditProperty::samp, emptyString, emptyString);
214
mCachedStyles[12] = StyleCache(nsEditProperty::var, emptyString, emptyString);
215
mCachedStyles[13] = StyleCache(nsEditProperty::cite, emptyString, emptyString);
216
mCachedStyles[14] = StyleCache(nsEditProperty::abbr, emptyString, emptyString);
217
mCachedStyles[15] = StyleCache(nsEditProperty::acronym, emptyString, emptyString);
218
mCachedStyles[16] = StyleCache(nsEditProperty::cssBackgroundColor, emptyString, emptyString);
219
mCachedStyles[17] = StyleCache(nsEditProperty::sub, emptyString, emptyString);
220
mCachedStyles[18] = StyleCache(nsEditProperty::sup, emptyString, emptyString);
223
nsHTMLEditRules::~nsHTMLEditRules()
225
// remove ourselves as a listener to edit actions
226
// In the normal case, we have already been removed by
227
// ~nsHTMLEditor, in which case we will get an error here
228
// which we ignore. But this allows us to add the ability to
229
// switch rule sets on the fly if we want.
230
mHTMLEditor->RemoveEditActionListener(this);
233
/********************************************************
235
********************************************************/
237
NS_IMPL_ADDREF_INHERITED(nsHTMLEditRules, nsTextEditRules)
238
NS_IMPL_RELEASE_INHERITED(nsHTMLEditRules, nsTextEditRules)
239
NS_IMPL_QUERY_INTERFACE3(nsHTMLEditRules, nsIHTMLEditRules, nsIEditRules, nsIEditActionListener)
242
/********************************************************
244
********************************************************/
247
nsHTMLEditRules::Init(nsPlaintextEditor *aEditor, PRUint32 aFlags)
249
mHTMLEditor = NS_STATIC_CAST(nsHTMLEditor*, aEditor);
252
// call through to base class Init
253
res = nsTextEditRules::Init(aEditor, aFlags);
254
if (NS_FAILED(res)) return res;
256
// cache any prefs we care about
257
nsCOMPtr<nsIPrefBranch> prefBranch =
258
do_GetService(NS_PREFSERVICE_CONTRACTID, &res);
259
if (NS_FAILED(res)) return res;
261
char *returnInEmptyLIKillsList = 0;
262
res = prefBranch->GetCharPref("editor.html.typing.returnInEmptyListItemClosesList",
263
&returnInEmptyLIKillsList);
265
if (NS_SUCCEEDED(res) && returnInEmptyLIKillsList)
267
if (!strncmp(returnInEmptyLIKillsList, "false", 5))
268
mReturnInEmptyLIKillsList = PR_FALSE;
270
mReturnInEmptyLIKillsList = PR_TRUE;
274
mReturnInEmptyLIKillsList = PR_TRUE;
277
// make a utility range for use by the listenter
278
mUtilRange = do_CreateInstance("@mozilla.org/content/range;1");
279
if (!mUtilRange) return NS_ERROR_NULL_POINTER;
281
// set up mDocChangeRange to be whole doc
282
nsCOMPtr<nsIDOMElement> bodyElem;
283
nsCOMPtr<nsIDOMNode> bodyNode;
284
mHTMLEditor->GetRootElement(getter_AddRefs(bodyElem));
285
bodyNode = do_QueryInterface(bodyElem);
288
// temporarily turn off rules sniffing
289
nsAutoLockRulesSniffing lockIt((nsTextEditRules*)this);
290
if (!mDocChangeRange)
292
mDocChangeRange = do_CreateInstance("@mozilla.org/content/range;1");
293
if (!mDocChangeRange) return NS_ERROR_NULL_POINTER;
295
mDocChangeRange->SelectNode(bodyNode);
296
res = AdjustSpecialBreaks();
297
if (NS_FAILED(res)) return res;
300
// add ourselves as a listener to edit actions
301
res = mHTMLEditor->AddEditActionListener(this);
308
nsHTMLEditRules::BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection)
310
if (mLockRulesSniffing) return NS_OK;
312
nsAutoLockRulesSniffing lockIt((nsTextEditRules*)this);
313
mDidExplicitlySetInterline = PR_FALSE;
317
// clear our flag about if just deleted a range
318
mDidRangedDelete = PR_FALSE;
320
// remember where our selection was before edit action took place:
323
nsCOMPtr<nsISelection>selection;
324
nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
325
if (NS_FAILED(res)) return res;
327
// get the selection start location
328
nsCOMPtr<nsIDOMNode> selStartNode, selEndNode;
330
res = mHTMLEditor->GetStartNodeAndOffset(selection, address_of(selStartNode), &selOffset);
331
if (NS_FAILED(res)) return res;
332
mRangeItem.startNode = selStartNode;
333
mRangeItem.startOffset = selOffset;
335
// get the selection end location
336
res = mHTMLEditor->GetEndNodeAndOffset(selection, address_of(selEndNode), &selOffset);
337
if (NS_FAILED(res)) return res;
338
mRangeItem.endNode = selEndNode;
339
mRangeItem.endOffset = selOffset;
341
// register this range with range updater to track this as we perturb the doc
342
(mHTMLEditor->mRangeUpdater).RegisterRangeItem(&mRangeItem);
344
// clear deletion state bool
345
mDidDeleteSelection = PR_FALSE;
347
// clear out mDocChangeRange and mUtilRange
348
nsCOMPtr<nsIDOMNSRange> nsrange;
351
nsrange = do_QueryInterface(mDocChangeRange);
353
return NS_ERROR_FAILURE;
354
nsrange->NSDetach(); // clear out our accounting of what changed
358
nsrange = do_QueryInterface(mUtilRange);
360
return NS_ERROR_FAILURE;
361
nsrange->NSDetach(); // ditto for mUtilRange.
364
// remember current inline styles for deletion and normal insertion operations
365
if ((action == nsEditor::kOpInsertText) ||
366
(action == nsEditor::kOpInsertIMEText) ||
367
(action == nsEditor::kOpDeleteSelection) ||
368
(action == nsEditor::kOpInsertBreak))
370
nsCOMPtr<nsIDOMNode> selNode = selStartNode;
371
if (aDirection == nsIEditor::eNext)
372
selNode = selEndNode;
373
res = CacheInlineStyles(selNode);
374
if (NS_FAILED(res)) return res;
377
// check that selection is in subtree defined by body node
378
ConfirmSelectionInBody();
379
// let rules remember the top level action
388
nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection)
390
if (mLockRulesSniffing) return NS_OK;
392
nsAutoLockRulesSniffing lockIt(this);
394
NS_PRECONDITION(mActionNesting>0, "bad action nesting!");
395
nsresult res = NS_OK;
396
if (!--mActionNesting)
398
// do all the tricky stuff
399
res = AfterEditInner(action, aDirection);
401
// free up selectionState range item
402
(mHTMLEditor->mRangeUpdater).DropRangeItem(&mRangeItem);
404
/* After inserting text the cursor Bidi level must be set to the level of the inserted text.
405
* This is difficult, because we cannot know what the level is until after the Bidi algorithm
406
* is applied to the whole paragraph.
408
* So we set the cursor Bidi level to UNDEFINED here, and the caret code will set it correctly later
410
if (action == nsEditor::kOpInsertText) {
411
nsCOMPtr<nsIPresShell> shell;
412
mEditor->GetPresShell(getter_AddRefs(shell));
414
shell->UndefineCaretBidiLevel();
424
nsHTMLEditRules::AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection)
426
ConfirmSelectionInBody();
427
if (action == nsEditor::kOpIgnore) return NS_OK;
429
nsCOMPtr<nsISelection>selection;
430
nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
431
if (NS_FAILED(res)) return res;
433
nsCOMPtr<nsIDOMNode> rangeStartParent, rangeEndParent;
434
PRInt32 rangeStartOffset = 0, rangeEndOffset = 0;
436
// do we have a real range to act on?
437
PRBool bDamagedRange = PR_FALSE;
440
mDocChangeRange->GetStartContainer(getter_AddRefs(rangeStartParent));
441
mDocChangeRange->GetEndContainer(getter_AddRefs(rangeEndParent));
442
mDocChangeRange->GetStartOffset(&rangeStartOffset);
443
mDocChangeRange->GetEndOffset(&rangeEndOffset);
444
if (rangeStartParent && rangeEndParent)
445
bDamagedRange = PR_TRUE;
448
if (bDamagedRange && !((action == nsEditor::kOpUndo) || (action == nsEditor::kOpRedo)))
450
// dont let any txns in here move the selection around behind our back.
451
// Note that this won't prevent explicit selection setting from working.
452
nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
454
// expand the "changed doc range" as needed
455
res = PromoteRange(mDocChangeRange, action);
456
if (NS_FAILED(res)) return res;
458
// if we did a ranged deletion, make sure we have a place to put caret.
459
// Note we only want to do this if the overall operation was deletion,
460
// not if deletion was done along the way for kOpLoadHTML, kOpInsertText, etc.
461
// That's why this is here rather than DidDeleteSelection().
462
if ((action == nsEditor::kOpDeleteSelection) && mDidRangedDelete)
464
res = InsertBRIfNeeded(selection);
465
if (NS_FAILED(res)) return res;
468
// add in any needed <br>s, and remove any unneeded ones.
469
res = AdjustSpecialBreaks();
470
if (NS_FAILED(res)) return res;
472
// merge any adjacent text nodes
473
if ( (action != nsEditor::kOpInsertText &&
474
action != nsEditor::kOpInsertIMEText) )
476
res = mHTMLEditor->CollapseAdjacentTextNodes(mDocChangeRange);
477
if (NS_FAILED(res)) return res;
480
// replace newlines with breaks.
481
// MOOSE: This is buttUgly. A better way to
482
// organize the action enum is in order.
483
if (// (action == nsEditor::kOpInsertText) ||
484
// (action == nsEditor::kOpInsertIMEText) ||
485
(action == nsHTMLEditor::kOpInsertElement) ||
486
(action == nsHTMLEditor::kOpInsertQuotation) ||
487
(action == nsEditor::kOpInsertNode) ||
488
(action == nsHTMLEditor::kOpHTMLPaste ||
489
(action == nsHTMLEditor::kOpLoadHTML)))
491
res = ReplaceNewlines(mDocChangeRange);
492
if (NS_FAILED(res)) return res;
495
// clean up any empty nodes in the selection
496
res = RemoveEmptyNodes();
497
if (NS_FAILED(res)) return res;
499
// attempt to transform any unneeded nbsp's into spaces after doing various operations
500
if ((action == nsEditor::kOpInsertText) ||
501
(action == nsEditor::kOpInsertIMEText) ||
502
(action == nsEditor::kOpDeleteSelection) ||
503
(action == nsEditor::kOpInsertBreak) ||
504
(action == nsHTMLEditor::kOpHTMLPaste ||
505
(action == nsHTMLEditor::kOpLoadHTML)))
507
res = AdjustWhitespace(selection);
508
if (NS_FAILED(res)) return res;
510
// also do this for original selection endpoints.
511
nsWSRunObject(mHTMLEditor, mRangeItem.startNode, mRangeItem.startOffset).AdjustWhitespace();
512
// we only need to handle old selection endpoint if it was different from start
513
if ((mRangeItem.startNode != mRangeItem.endNode) || (mRangeItem.startOffset != mRangeItem.endOffset))
515
nsWSRunObject(mHTMLEditor, mRangeItem.endNode, mRangeItem.endOffset).AdjustWhitespace();
519
// if we created a new block, make sure selection lands in it
522
res = PinSelectionToNewBlock(selection);
526
// adjust selection for insert text, html paste, and delete actions
527
if ((action == nsEditor::kOpInsertText) ||
528
(action == nsEditor::kOpInsertIMEText) ||
529
(action == nsEditor::kOpDeleteSelection) ||
530
(action == nsEditor::kOpInsertBreak) ||
531
(action == nsHTMLEditor::kOpHTMLPaste ||
532
(action == nsHTMLEditor::kOpLoadHTML)))
534
res = AdjustSelection(selection, aDirection);
535
if (NS_FAILED(res)) return res;
538
// check for any styles which were removed inappropriately
539
if ((action == nsEditor::kOpInsertText) ||
540
(action == nsEditor::kOpInsertIMEText) ||
541
(action == nsEditor::kOpDeleteSelection) ||
542
(action == nsEditor::kOpInsertBreak))
544
mHTMLEditor->mTypeInState->UpdateSelState(selection);
545
res = ReapplyCachedStyles();
546
if (NS_FAILED(res)) return res;
547
res = ClearCachedStyles();
548
if (NS_FAILED(res)) return res;
552
res = mHTMLEditor->HandleRealTimeSpellCheck(action,selection,
553
mRangeItem.startNode,mRangeItem.startOffset,
554
rangeStartParent,rangeStartOffset,
555
rangeEndParent,rangeEndOffset);
556
if (NS_FAILED(res)) return res;
559
res = CreateBogusNodeIfNeeded(selection);
561
// adjust selection HINT if needed
562
if (NS_FAILED(res)) return res;
564
if (!mDidExplicitlySetInterline)
566
res = CheckInterlinePosition(selection);
574
nsHTMLEditRules::WillDoAction(nsISelection *aSelection,
579
if (!aInfo || !aCancel || !aHandled)
580
return NS_ERROR_NULL_POINTER;
581
#if defined(DEBUG_ftang)
582
printf("nsHTMLEditRules::WillDoAction action = %d\n", aInfo->action);
586
*aHandled = PR_FALSE;
588
// my kingdom for dynamic cast
589
nsTextRulesInfo *info = NS_STATIC_CAST(nsTextRulesInfo*, aInfo);
591
switch (info->action)
595
return WillInsertText(info->action,
603
return WillLoadHTML(aSelection, aCancel);
605
return WillInsertBreak(aSelection, aCancel, aHandled);
606
case kDeleteSelection:
607
return WillDeleteSelection(aSelection, info->collapsedAction, aCancel, aHandled);
609
return WillMakeList(aSelection, info->blockType, info->entireList, info->bulletType, aCancel, aHandled);
611
return WillIndent(aSelection, aCancel, aHandled);
613
return WillOutdent(aSelection, aCancel, aHandled);
614
case kSetAbsolutePosition:
615
case kMakeComplexBlock:
616
return WillBlockifySelection(aSelection, aCancel, aHandled);
617
case kRemoveAbsolutePosition:
618
return WillRemoveAbsolutePosition(aSelection, aCancel, aHandled);
620
return WillAlign(aSelection, info->alignType, aCancel, aHandled);
621
case kMakeBasicBlock:
622
return WillMakeBasicBlock(aSelection, info->blockType, aCancel, aHandled);
624
return WillRemoveList(aSelection, info->bOrdered, aCancel, aHandled);
625
case kMakeDefListItem:
626
return WillMakeDefListItem(aSelection, info->blockType, info->entireList, aCancel, aHandled);
628
return WillInsert(aSelection, aCancel);
629
case kDecreaseZIndex:
630
return WillRelativeChangeZIndex(aSelection, -1, aCancel, aHandled);
631
case kIncreaseZIndex:
632
return WillRelativeChangeZIndex(aSelection, 1, aCancel, aHandled);
634
return nsTextEditRules::WillDoAction(aSelection, aInfo, aCancel, aHandled);
639
nsHTMLEditRules::DidDoAction(nsISelection *aSelection,
640
nsRulesInfo *aInfo, nsresult aResult)
642
nsTextRulesInfo *info = NS_STATIC_CAST(nsTextRulesInfo*, aInfo);
644
switch (info->action)
647
return DidInsertBreak(aSelection, aResult);
648
case kDeleteSelection:
649
return DidDeleteSelection(aSelection, info->collapsedAction, aResult);
650
case kMakeBasicBlock:
654
return DidMakeBasicBlock(aSelection, aInfo, aResult);
655
case kSetAbsolutePosition: {
656
rv = DidMakeBasicBlock(aSelection, aInfo, aResult);
657
if (NS_FAILED(rv)) return rv;
658
return DidAbsolutePosition();
659
case kMakeComplexBlock:
660
rv = DidMakeBasicBlock(aSelection, aInfo, aResult);
661
if (NS_FAILED(rv)) return rv;
662
return DidMakeComplexBlock(info->styleAttr);
666
// default: pass thru to nsTextEditRules
667
return nsTextEditRules::DidDoAction(aSelection, aInfo, aResult);
670
/********************************************************
671
* nsIHTMLEditRules methods
672
********************************************************/
675
nsHTMLEditRules::GetListState(PRBool *aMixed, PRBool *aOL, PRBool *aUL, PRBool *aDL)
677
if (!aMixed || !aOL || !aUL || !aDL)
678
return NS_ERROR_NULL_POINTER;
683
PRBool bNonList = PR_FALSE;
685
nsCOMArray<nsIDOMNode> arrayOfNodes;
686
nsresult res = GetListActionNodes(arrayOfNodes, PR_FALSE, PR_TRUE);
687
if (NS_FAILED(res)) return res;
689
// examine list type for nodes in selection
690
PRInt32 listCount = arrayOfNodes.Count();
692
for (i=listCount-1; i>=0; i--)
694
nsIDOMNode* curNode = arrayOfNodes[i];
696
if (nsHTMLEditUtils::IsUnorderedList(curNode))
698
else if (nsHTMLEditUtils::IsOrderedList(curNode))
700
else if (nsEditor::NodeIsType(curNode, nsEditProperty::li))
702
nsCOMPtr<nsIDOMNode> parent;
704
res = nsEditor::GetNodeLocation(curNode, address_of(parent), &offset);
705
if (NS_FAILED(res)) return res;
706
if (nsHTMLEditUtils::IsUnorderedList(parent))
708
else if (nsHTMLEditUtils::IsOrderedList(parent))
711
else if (nsEditor::NodeIsType(curNode, nsEditProperty::dl) ||
712
nsEditor::NodeIsType(curNode, nsEditProperty::dt) ||
713
nsEditor::NodeIsType(curNode, nsEditProperty::dd) )
717
else bNonList = PR_TRUE;
720
// hokey arithmetic with booleans
721
if ( (*aUL + *aOL + *aDL + bNonList) > 1) *aMixed = PR_TRUE;
727
nsHTMLEditRules::GetListItemState(PRBool *aMixed, PRBool *aLI, PRBool *aDT, PRBool *aDD)
729
if (!aMixed || !aLI || !aDT || !aDD)
730
return NS_ERROR_NULL_POINTER;
735
PRBool bNonList = PR_FALSE;
737
nsCOMArray<nsIDOMNode> arrayOfNodes;
738
nsresult res = GetListActionNodes(arrayOfNodes, PR_FALSE, PR_TRUE);
739
if (NS_FAILED(res)) return res;
741
// examine list type for nodes in selection
742
PRInt32 listCount = arrayOfNodes.Count();
744
for (i = listCount-1; i>=0; i--)
746
nsIDOMNode* curNode = arrayOfNodes[i];
748
if (nsHTMLEditUtils::IsUnorderedList(curNode) ||
749
nsHTMLEditUtils::IsOrderedList(curNode) ||
750
nsEditor::NodeIsType(curNode, nsEditProperty::li) )
754
else if (nsEditor::NodeIsType(curNode, nsEditProperty::dt))
758
else if (nsEditor::NodeIsType(curNode, nsEditProperty::dd))
762
else if (nsEditor::NodeIsType(curNode, nsEditProperty::dl))
764
// need to look inside dl and see which types of items it has
766
res = GetDefinitionListItemTypes(curNode, bDT, bDD);
767
if (NS_FAILED(res)) return res;
771
else bNonList = PR_TRUE;
774
// hokey arithmetic with booleans
775
if ( (*aDT + *aDD + bNonList) > 1) *aMixed = PR_TRUE;
781
nsHTMLEditRules::GetAlignment(PRBool *aMixed, nsIHTMLEditor::EAlignment *aAlign)
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!
789
// this routine assumes that alignment is done ONLY via divs
791
// default alignment is left
792
if (!aMixed || !aAlign)
793
return NS_ERROR_NULL_POINTER;
795
*aAlign = nsIHTMLEditor::eLeft;
798
nsCOMPtr<nsISelection>selection;
799
nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
800
if (NS_FAILED(res)) return res;
802
// get selection location
803
nsCOMPtr<nsIDOMNode> parent;
804
nsCOMPtr<nsIDOMElement> rootElem;
805
PRInt32 offset, rootOffset;
806
res = mHTMLEditor->GetRootElement(getter_AddRefs(rootElem));
807
if (NS_FAILED(res)) return res;
808
res = nsEditor::GetNodeLocation(rootElem, address_of(parent), &rootOffset);
809
if (NS_FAILED(res)) return res;
810
res = mHTMLEditor->GetStartNodeAndOffset(selection, address_of(parent), &offset);
811
if (NS_FAILED(res)) return res;
813
// is the selection collapsed?
815
res = selection->GetIsCollapsed(&bCollapsed);
816
if (NS_FAILED(res)) return res;
817
nsCOMPtr<nsIDOMNode> nodeToExamine;
818
nsCOMPtr<nsISupports> isupports;
821
// if it is, we want to look at 'parent' and it's ancestors
822
// for divs with alignment on them
823
nodeToExamine = parent;
825
else if (mHTMLEditor->IsTextNode(parent))
827
// if we are in a text node, then that is the node of interest
828
nodeToExamine = parent;
830
else if (nsEditor::NodeIsType(parent, nsEditProperty::html) &&
831
offset == rootOffset)
833
// if we have selected the body, let's look at the first editable node
834
mHTMLEditor->GetNextNode(parent, offset, PR_TRUE, address_of(nodeToExamine));
838
nsCOMArray<nsIDOMRange> arrayOfRanges;
839
res = GetPromotedRanges(selection, arrayOfRanges, kAlign);
840
if (NS_FAILED(res)) return res;
842
// use these ranges to construct a list of nodes to act on.
843
nsCOMArray<nsIDOMNode> arrayOfNodes;
844
res = GetNodesForOperation(arrayOfRanges, arrayOfNodes, kAlign, PR_TRUE);
845
if (NS_FAILED(res)) return res;
846
nodeToExamine = arrayOfNodes[0];
849
if (!nodeToExamine) return NS_ERROR_NULL_POINTER;
852
mHTMLEditor->GetIsCSSEnabled(&useCSS);
853
NS_NAMED_LITERAL_STRING(typeAttrName, "align");
854
nsIAtom *dummyProperty = nsnull;
855
nsCOMPtr<nsIDOMNode> blockParent;
856
if (mHTMLEditor->IsBlockNode(nodeToExamine))
857
blockParent = nodeToExamine;
859
blockParent = mHTMLEditor->GetBlockNodeParent(nodeToExamine);
861
if (!blockParent) return NS_ERROR_FAILURE;
865
nsCOMPtr<nsIContent> blockParentContent = do_QueryInterface(blockParent);
866
if (blockParentContent &&
867
mHTMLEditor->mHTMLCSSUtils->IsCSSEditableProperty(blockParent, dummyProperty, &typeAttrName))
869
// we are in CSS mode and we know how to align this element with CSS
871
// let's get the value(s) of text-align or margin-left/margin-right
872
mHTMLEditor->mHTMLCSSUtils->GetCSSEquivalentToHTMLInlineStyleSet(blockParent,
876
COMPUTED_STYLE_TYPE);
877
if (value.Equals(NS_LITERAL_STRING("center")) ||
878
value.Equals(NS_LITERAL_STRING("-moz-center")) ||
879
value.Equals(NS_LITERAL_STRING("auto auto")))
881
*aAlign = nsIHTMLEditor::eCenter;
884
if (value.Equals(NS_LITERAL_STRING("right")) ||
885
value.Equals(NS_LITERAL_STRING("-moz-right")) ||
886
value.Equals(NS_LITERAL_STRING("auto 0px")))
888
*aAlign = nsIHTMLEditor::eRight;
891
if (value.Equals(NS_LITERAL_STRING("justify")))
893
*aAlign = nsIHTMLEditor::eJustify;
896
*aAlign = nsIHTMLEditor::eLeft;
901
// check up the ladder for divs with alignment
902
nsCOMPtr<nsIDOMNode> temp = nodeToExamine;
903
PRBool isFirstNodeToExamine = PR_TRUE;
904
while (nodeToExamine)
906
if (!isFirstNodeToExamine && nsHTMLEditUtils::IsTable(nodeToExamine))
908
// the node to examine is a table and this is not the first node
909
// we examine; let's break here to materialize the 'inline-block'
910
// behaviour of html tables regarding to text alignment
913
if (nsHTMLEditUtils::SupportsAlignAttr(nodeToExamine))
915
// check for alignment
916
nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(nodeToExamine);
919
nsAutoString typeAttrVal;
920
res = elem->GetAttribute(NS_LITERAL_STRING("align"), typeAttrVal);
921
ToLowerCase(typeAttrVal);
922
if (NS_SUCCEEDED(res) && typeAttrVal.Length())
924
if (typeAttrVal.Equals(NS_LITERAL_STRING("center")))
925
*aAlign = nsIHTMLEditor::eCenter;
926
else if (typeAttrVal.Equals(NS_LITERAL_STRING("right")))
927
*aAlign = nsIHTMLEditor::eRight;
928
else if (typeAttrVal.Equals(NS_LITERAL_STRING("justify")))
929
*aAlign = nsIHTMLEditor::eJustify;
931
*aAlign = nsIHTMLEditor::eLeft;
936
isFirstNodeToExamine = PR_FALSE;
937
res = nodeToExamine->GetParentNode(getter_AddRefs(temp));
938
if (NS_FAILED(res)) temp = nsnull;
939
nodeToExamine = temp;
945
nsHTMLEditRules::GetIndentState(PRBool *aCanIndent, PRBool *aCanOutdent)
947
if (!aCanIndent || !aCanOutdent)
948
return NS_ERROR_FAILURE;
949
*aCanIndent = PR_TRUE;
950
*aCanOutdent = PR_FALSE;
953
nsCOMPtr<nsISelection>selection;
954
nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
955
if (NS_FAILED(res)) return res;
956
nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
958
return NS_ERROR_FAILURE;
960
// contruct a list of nodes to act on.
961
nsCOMArray<nsIDOMNode> arrayOfNodes;
962
res = GetNodesFromSelection(selection, kIndent, arrayOfNodes, PR_TRUE);
963
if (NS_FAILED(res)) return res;
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
PRInt32 listCount = arrayOfNodes.Count();
971
mHTMLEditor->GetIsCSSEnabled(&useCSS);
972
for (i=listCount-1; i>=0; i--)
974
nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
976
if (nsHTMLEditUtils::IsNodeThatCanOutdent(curNode))
978
*aCanOutdent = PR_TRUE;
982
// we are in CSS mode, indentation is done using the margin-left property
984
// retrieve its specified value
985
mHTMLEditor->mHTMLCSSUtils->GetSpecifiedProperty(curNode, nsEditProperty::cssMarginLeft, value);
987
nsCOMPtr<nsIAtom> unit;
988
// get its number part and its unit
989
mHTMLEditor->mHTMLCSSUtils->ParseLength(value, &f, getter_AddRefs(unit));
990
// if the number part is strictly positive, outdent is possible
992
*aCanOutdent = PR_TRUE;
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 heirarchy.
1004
// gather up info we need for test
1005
nsCOMPtr<nsIDOMNode> parent, tmp, root;
1006
nsCOMPtr<nsIDOMElement> rootElem;
1007
nsCOMPtr<nsISelection> selection;
1009
res = mHTMLEditor->GetRootElement(getter_AddRefs(rootElem));
1010
if (NS_FAILED(res)) return res;
1011
if (!rootElem) return NS_ERROR_NULL_POINTER;
1012
root = do_QueryInterface(rootElem);
1013
if (!root) return NS_ERROR_NO_INTERFACE;
1014
res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
1015
if (NS_FAILED(res)) return res;
1016
if (!selection) return NS_ERROR_NULL_POINTER;
1018
// test start parent hierarchy
1019
res = mHTMLEditor->GetStartNodeAndOffset(selection, address_of(parent), &selOffset);
1020
if (NS_FAILED(res)) return res;
1021
while (parent && (parent!=root))
1023
if (nsHTMLEditUtils::IsNodeThatCanOutdent(parent))
1025
*aCanOutdent = PR_TRUE;
1029
tmp->GetParentNode(getter_AddRefs(parent));
1032
// test end parent hierarchy
1033
res = mHTMLEditor->GetEndNodeAndOffset(selection, address_of(parent), &selOffset);
1034
if (NS_FAILED(res)) return res;
1035
while (parent && (parent!=root))
1037
if (nsHTMLEditUtils::IsNodeThatCanOutdent(parent))
1039
*aCanOutdent = PR_TRUE;
1043
tmp->GetParentNode(getter_AddRefs(parent));
1051
nsHTMLEditRules::GetParagraphState(PRBool *aMixed, nsAString &outFormat)
1053
// This routine is *heavily* tied to our ui choices in the paragraph
1054
// style popup. I cant see a way around that.
1056
return NS_ERROR_NULL_POINTER;
1058
outFormat.Truncate(0);
1060
PRBool bMixed = PR_FALSE;
1061
// using "x" as an uninitialized value, since "" is meaningful
1062
nsAutoString formatStr(NS_LITERAL_STRING("x"));
1064
nsCOMArray<nsIDOMNode> arrayOfNodes;
1065
nsresult res = GetParagraphFormatNodes(arrayOfNodes, PR_TRUE);
1066
if (NS_FAILED(res)) return res;
1068
// post process list. We need to replace any block nodes that are not format
1069
// nodes with their content. This is so we only have to look "up" the heirarchy
1070
// to find format nodes, instead of both up and down.
1071
PRInt32 listCount = arrayOfNodes.Count();
1073
for (i=listCount-1; i>=0; i--)
1075
nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
1076
nsAutoString format;
1077
// if it is a known format node we have it easy
1078
if (IsBlockNode(curNode) && !nsHTMLEditUtils::IsFormatNode(curNode))
1080
// arrayOfNodes.RemoveObject(curNode);
1081
res = AppendInnerFormatNodes(arrayOfNodes, curNode);
1082
if (NS_FAILED(res)) return res;
1086
// we might have an empty node list. if so, find selection parent
1087
// and put that on the list
1088
listCount = arrayOfNodes.Count();
1091
nsCOMPtr<nsIDOMNode> selNode;
1093
nsCOMPtr<nsISelection>selection;
1094
res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
1095
if (NS_FAILED(res)) return res;
1096
res = mHTMLEditor->GetStartNodeAndOffset(selection, address_of(selNode), &selOffset);
1097
if (NS_FAILED(res)) return res;
1098
if (!selNode) return NS_ERROR_NULL_POINTER;
1099
arrayOfNodes.AppendObject(selNode);
1103
// remember root node
1104
nsCOMPtr<nsIDOMElement> rootElem;
1105
res = mHTMLEditor->GetRootElement(getter_AddRefs(rootElem));
1106
if (NS_FAILED(res)) return res;
1107
if (!rootElem) return NS_ERROR_NULL_POINTER;
1109
// loop through the nodes in selection and examine their paragraph format
1110
for (i=listCount-1; i>=0; i--)
1112
nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
1113
nsAutoString format;
1114
// if it is a known format node we have it easy
1115
if (nsHTMLEditUtils::IsFormatNode(curNode))
1116
GetFormatString(curNode, format);
1117
else if (IsBlockNode(curNode))
1119
// this is a div or some other non-format block.
1120
// we should ignore it. It's children were appended to this list
1121
// by AppendInnerFormatNodes() call above. We will get needed
1122
// info when we examine them instead.
1127
nsCOMPtr<nsIDOMNode> node, tmp = curNode;
1128
tmp->GetParentNode(getter_AddRefs(node));
1131
if (node == rootElem)
1136
else if (nsHTMLEditUtils::IsFormatNode(node))
1138
GetFormatString(node, format);
1141
// else keep looking up
1143
tmp->GetParentNode(getter_AddRefs(node));
1147
// if this is the first node, we've found, remember it as the format
1148
if (formatStr.Equals(NS_LITERAL_STRING("x")))
1150
// else make sure it matches previously found format
1151
else if (format != formatStr)
1159
outFormat = formatStr;
1164
nsHTMLEditRules::AppendInnerFormatNodes(nsCOMArray<nsIDOMNode>& aArray,
1167
if (!aNode) return NS_ERROR_NULL_POINTER;
1169
nsCOMPtr<nsIDOMNodeList> childList;
1170
nsCOMPtr<nsIDOMNode> child;
1172
aNode->GetChildNodes(getter_AddRefs(childList));
1173
if (!childList) return NS_OK;
1175
childList->GetLength(&len);
1177
// we only need to place any one inline inside this node onto
1178
// the list. They are all the same for purposes of determining
1179
// paragraph style. We use foundInline to track this as we are
1180
// going through the children in the loop below.
1181
PRBool foundInline = PR_FALSE;
1184
childList->Item(j, getter_AddRefs(child));
1185
PRBool isBlock = IsBlockNode(child);
1186
PRBool isFormat = nsHTMLEditUtils::IsFormatNode(child);
1187
if (isBlock && !isFormat) // if it's a div, etc, recurse
1188
AppendInnerFormatNodes(aArray, child);
1191
aArray.AppendObject(child);
1193
else if (!foundInline) // if this is the first inline we've found, use it
1195
foundInline = PR_TRUE;
1196
aArray.AppendObject(child);
1204
nsHTMLEditRules::GetFormatString(nsIDOMNode *aNode, nsAString &outFormat)
1206
if (!aNode) return NS_ERROR_NULL_POINTER;
1208
if (nsHTMLEditUtils::IsFormatNode(aNode))
1210
nsCOMPtr<nsIAtom> atom = nsEditor::GetTag(aNode);
1211
atom->ToString(outFormat);
1214
outFormat.Truncate();
1219
/********************************************************
1220
* Protected rules methods
1221
********************************************************/
1224
nsHTMLEditRules::WillInsert(nsISelection *aSelection, PRBool *aCancel)
1226
nsresult res = nsTextEditRules::WillInsert(aSelection, aCancel);
1227
if (NS_FAILED(res)) return res;
1229
// Adjust selection to prevent insertion after a moz-BR.
1230
// this next only works for collapsed selections right now,
1231
// because selection is a pain to work with when not collapsed.
1232
// (no good way to extend start or end of selection)
1234
res = aSelection->GetIsCollapsed(&bCollapsed);
1235
if (NS_FAILED(res)) return res;
1236
if (!bCollapsed) return NS_OK;
1238
// if we are after a mozBR in the same block, then move selection
1240
nsCOMPtr<nsIDOMNode> selNode, priorNode;
1242
// get the (collapsed) selection location
1243
res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(selNode),
1245
if (NS_FAILED(res)) return res;
1247
res = mHTMLEditor->GetPriorHTMLNode(selNode, selOffset,
1248
address_of(priorNode));
1249
if (NS_SUCCEEDED(res) && priorNode && nsTextEditUtils::IsMozBR(priorNode))
1251
nsCOMPtr<nsIDOMNode> block1, block2;
1252
if (IsBlockNode(selNode)) block1 = selNode;
1253
else block1 = mHTMLEditor->GetBlockNodeParent(selNode);
1254
block2 = mHTMLEditor->GetBlockNodeParent(priorNode);
1256
if (block1 == block2)
1258
// if we are here then the selection is right after a mozBR
1259
// that is in the same block as the selection. We need to move
1260
// the selection start to be before the mozBR.
1261
res = nsEditor::GetNodeLocation(priorNode, address_of(selNode), &selOffset);
1262
if (NS_FAILED(res)) return res;
1263
res = aSelection->Collapse(selNode,selOffset);
1264
if (NS_FAILED(res)) return res;
1268
// we need to get the doc
1269
nsCOMPtr<nsIDOMDocument>doc;
1270
res = mHTMLEditor->GetDocument(getter_AddRefs(doc));
1271
if (NS_FAILED(res)) return res;
1272
if (!doc) return NS_ERROR_NULL_POINTER;
1274
// for every property that is set, insert a new inline style node
1275
return CreateStyleForInsertText(aSelection, doc);
1278
#ifdef XXX_DEAD_CODE
1280
nsHTMLEditRules::DidInsert(nsISelection *aSelection, nsresult aResult)
1282
return nsTextEditRules::DidInsert(aSelection, aResult);
1287
nsHTMLEditRules::WillInsertText(PRInt32 aAction,
1288
nsISelection *aSelection,
1291
const nsAString *inString,
1292
nsAString *outString,
1295
if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
1299
if (inString->IsEmpty() && (aAction != kInsertTextIME))
1301
// HACK: this is a fix for bug 19395
1302
// I can't outlaw all empty insertions
1303
// because IME transaction depend on them
1304
// There is more work to do to make the
1305
// world safe for IME.
1307
*aHandled = PR_FALSE;
1311
// initialize out param
1312
*aCancel = PR_FALSE;
1313
*aHandled = PR_TRUE;
1315
nsCOMPtr<nsIDOMNode> selNode;
1318
PRBool bPlaintext = mFlags & nsIPlaintextEditor::eEditorPlaintextMask;
1320
// if the selection isn't collapsed, delete it.
1322
res = aSelection->GetIsCollapsed(&bCollapsed);
1323
if (NS_FAILED(res)) return res;
1326
PRBool hasMozSourceView = PR_FALSE;
1327
res = mHTMLEditor->GetIsColoredSourceView(&hasMozSourceView);
1328
if (NS_FAILED(res)) return res;
1330
PRBool createNewLI = PR_FALSE;
1332
if (hasMozSourceView)
1334
nsCOMPtr<nsIDOMNode> selStartNode, selEndNode;
1335
PRInt32 selStartOffset, selEndOffset;
1336
res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(selStartNode), &selStartOffset);
1337
if (NS_FAILED(res)) return res;
1339
// get the selection end location
1340
res = mHTMLEditor->GetEndNodeAndOffset(aSelection, address_of(selEndNode), &selEndOffset);
1341
if (NS_FAILED(res)) return res;
1343
createNewLI = (nsHTMLEditUtils::IsOrderedList(selStartNode) &&
1344
nsHTMLEditUtils::IsOrderedList(selEndNode));
1349
nsCOMPtr<nsIDOMElement> newLi;
1350
res = mHTMLEditor->CreateElementWithDefaults(NS_LITERAL_STRING("li"),
1351
getter_AddRefs(newLi));
1352
if (NS_FAILED(res)) return res;
1353
res = mHTMLEditor->InsertElementAtSelection(newLi, PR_TRUE);
1354
if (NS_FAILED(res)) return res;
1355
InsertMozBRIfNeeded(newLi);
1356
aSelection->Collapse(newLi, 0);
1360
res = mHTMLEditor->DeleteSelection(nsIEditor::eNone);
1361
if (NS_FAILED(res)) return res;
1365
res = WillInsert(aSelection, aCancel);
1366
if (NS_FAILED(res)) return res;
1367
// initialize out param
1368
// we want to ignore result of WillInsert()
1369
*aCancel = PR_FALSE;
1371
// get the (collapsed) selection location
1372
res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
1373
if (NS_FAILED(res)) return res;
1375
// dont put text in places that cant have it
1376
if (!mHTMLEditor->IsTextNode(selNode) && !mHTMLEditor->CanContainTag(selNode, NS_LITERAL_STRING("__moz_text")))
1377
return NS_ERROR_FAILURE;
1379
// we need to get the doc
1380
nsCOMPtr<nsIDOMDocument>doc;
1381
res = mHTMLEditor->GetDocument(getter_AddRefs(doc));
1382
if (NS_FAILED(res)) return res;
1383
if (!doc) return NS_ERROR_NULL_POINTER;
1385
if (aAction == kInsertTextIME)
1387
// Right now the nsWSRunObject code bails on empty strings, but IME needs
1388
// the InsertTextImpl() call to still happen since empty strings are meaningful there.
1389
if (inString->IsEmpty())
1391
res = mHTMLEditor->InsertTextImpl(*inString, address_of(selNode), &selOffset, doc);
1395
nsWSRunObject wsObj(mHTMLEditor, selNode, selOffset);
1396
res = wsObj.InsertText(*inString, address_of(selNode), &selOffset, doc);
1398
if (NS_FAILED(res)) return res;
1400
else // aAction == kInsertText
1402
// find where we are
1403
nsCOMPtr<nsIDOMNode> curNode = selNode;
1404
PRInt32 curOffset = selOffset;
1406
// is our text going to be PREformatted?
1407
// We remember this so that we know how to handle tabs.
1409
res = mHTMLEditor->IsPreformatted(selNode, &isPRE);
1410
if (NS_FAILED(res)) return res;
1412
// turn off the edit listener: we know how to
1413
// build the "doc changed range" ourselves, and it's
1414
// must faster to do it once here than to track all
1415
// the changes one at a time.
1416
nsAutoLockListener lockit(&mListenerEnabled);
1418
// dont spaz my selection in subtransactions
1419
nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
1420
nsAutoString tString(*inString);
1421
const PRUnichar *unicodeBuf = tString.get();
1422
nsCOMPtr<nsIDOMNode> unused;
1424
NS_NAMED_LITERAL_STRING(newlineStr, LFSTR);
1426
// for efficiency, break out the pre case separately. This is because
1427
// its a lot cheaper to search the input string for only newlines than
1428
// it is to search for both tabs and newlines.
1429
if (isPRE || bPlaintext)
1431
while (unicodeBuf && (pos != -1) && (pos < (PRInt32)(*inString).Length()))
1433
PRInt32 oldPos = pos;
1435
pos = tString.FindChar(nsCRT::LF, oldPos);
1439
subStrLen = pos - oldPos;
1440
// if first char is newline, then use just it
1446
subStrLen = tString.Length() - oldPos;
1447
pos = tString.Length();
1450
nsDependentSubstring subStr(tString, oldPos, subStrLen);
1453
if (subStr.Equals(newlineStr))
1455
res = mHTMLEditor->CreateBRImpl(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone);
1460
res = mHTMLEditor->InsertTextImpl(subStr, address_of(curNode), &curOffset, doc);
1462
if (NS_FAILED(res)) return res;
1467
NS_NAMED_LITERAL_STRING(tabStr, "\t");
1468
NS_NAMED_LITERAL_STRING(spacesStr, " ");
1469
char specialChars[] = {TAB, nsCRT::LF, 0};
1470
while (unicodeBuf && (pos != -1) && (pos < (PRInt32)inString->Length()))
1472
PRInt32 oldPos = pos;
1474
pos = tString.FindCharInSet(specialChars, oldPos);
1478
subStrLen = pos - oldPos;
1479
// if first char is newline, then use just it
1485
subStrLen = tString.Length() - oldPos;
1486
pos = tString.Length();
1489
nsDependentSubstring subStr(tString, oldPos, subStrLen);
1490
nsWSRunObject wsObj(mHTMLEditor, curNode, curOffset);
1493
if (subStr.Equals(tabStr))
1495
res = wsObj.InsertText(spacesStr, address_of(curNode), &curOffset, doc);
1496
if (NS_FAILED(res)) return res;
1500
else if (subStr.Equals(newlineStr))
1502
res = wsObj.InsertBreak(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone);
1503
if (NS_FAILED(res)) return res;
1508
res = wsObj.InsertText(subStr, address_of(curNode), &curOffset, doc);
1509
if (NS_FAILED(res)) return res;
1511
if (NS_FAILED(res)) return res;
1514
nsCOMPtr<nsISelection> selection(aSelection);
1515
nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
1516
selPriv->SetInterlinePosition(PR_FALSE);
1517
if (curNode) aSelection->Collapse(curNode, curOffset);
1518
// manually update the doc changed range so that AfterEdit will clean up
1519
// the correct portion of the document.
1520
if (!mDocChangeRange)
1522
mDocChangeRange = do_CreateInstance("@mozilla.org/content/range;1");
1523
if (!mDocChangeRange) return NS_ERROR_NULL_POINTER;
1525
res = mDocChangeRange->SetStart(selNode, selOffset);
1526
if (NS_FAILED(res)) return res;
1528
res = mDocChangeRange->SetEnd(curNode, curOffset);
1530
res = mDocChangeRange->SetEnd(selNode, selOffset);
1531
if (NS_FAILED(res)) return res;
1537
nsHTMLEditRules::WillLoadHTML(nsISelection *aSelection, PRBool *aCancel)
1539
if (!aSelection || !aCancel) return NS_ERROR_NULL_POINTER;
1541
*aCancel = PR_FALSE;
1543
// Delete mBogusNode if it exists. If we really need one,
1544
// it will be added during post-processing in AfterEditInner().
1548
mEditor->DeleteNode(mBogusNode);
1549
mBogusNode = nsnull;
1556
nsHTMLEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBool *aHandled)
1558
if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
1559
// initialize out param
1560
*aCancel = PR_FALSE;
1561
*aHandled = PR_FALSE;
1563
PRBool bPlaintext = mFlags & nsIPlaintextEditor::eEditorPlaintextMask;
1565
// if the selection isn't collapsed, delete it.
1567
nsresult res = aSelection->GetIsCollapsed(&bCollapsed);
1568
if (NS_FAILED(res)) return res;
1571
res = mHTMLEditor->DeleteSelection(nsIEditor::eNone);
1572
if (NS_FAILED(res)) return res;
1575
res = WillInsert(aSelection, aCancel);
1576
if (NS_FAILED(res)) return res;
1578
// initialize out param
1579
// we want to ignore result of WillInsert()
1580
*aCancel = PR_FALSE;
1582
// split any mailcites in the way.
1583
// should we abort this if we encounter table cell boundaries?
1584
if (mFlags & nsIPlaintextEditor::eEditorMailMask)
1586
res = SplitMailCites(aSelection, bPlaintext, aHandled);
1587
if (NS_FAILED(res)) return res;
1588
if (*aHandled) return NS_OK;
1591
// smart splitting rules
1592
nsCOMPtr<nsIDOMNode> node;
1595
res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(node), &offset);
1596
if (NS_FAILED(res)) return res;
1597
if (!node) return NS_ERROR_FAILURE;
1599
// identify the block
1600
nsCOMPtr<nsIDOMNode> blockParent;
1602
if (IsBlockNode(node))
1605
blockParent = mHTMLEditor->GetBlockNodeParent(node);
1607
if (!blockParent) return NS_ERROR_FAILURE;
1609
// if block is empty, populate with br.
1610
// (for example, imagine a div that contains the word "text". the user selects
1611
// "text" and types return. "text" is deleted leaving an empty block. we want
1612
// to put in one br to make block have a line. then code further below will put
1615
res = IsEmptyBlock(blockParent, &isEmpty);
1619
res = mHTMLEditor->GetLengthOfDOMNode(blockParent, blockLen);
1620
if (NS_FAILED(res)) return res;
1621
nsCOMPtr<nsIDOMNode> brNode;
1622
res = mHTMLEditor->CreateBR(blockParent, blockLen, address_of(brNode));
1623
if (NS_FAILED(res)) return res;
1626
nsCOMPtr<nsIDOMNode> listItem = IsInListItem(blockParent);
1629
res = ReturnInListItem(aSelection, listItem, node, offset);
1630
*aHandled = PR_TRUE;
1634
// headers: close (or split) header
1635
else if (nsHTMLEditUtils::IsHeader(blockParent))
1637
res = ReturnInHeader(aSelection, blockParent, node, offset);
1638
*aHandled = PR_TRUE;
1642
// paragraphs: special rules to look for <br>s
1643
else if (nsHTMLEditUtils::IsParagraph(blockParent))
1645
res = ReturnInParagraph(aSelection, blockParent, node, offset, aCancel, aHandled);
1646
if (NS_FAILED(res)) return res;
1647
// fall through, we may not have handled it in ReturnInParagraph()
1650
// if not already handled then do the standard thing
1653
res = StandardBreakImpl(node, offset, aSelection);
1654
*aHandled = PR_TRUE;
1660
nsHTMLEditRules::StandardBreakImpl(nsIDOMNode *aNode, PRInt32 aOffset, nsISelection *aSelection)
1662
nsCOMPtr<nsIDOMNode> brNode;
1663
PRBool bAfterBlock = PR_FALSE;
1664
PRBool bBeforeBlock = PR_FALSE;
1665
nsresult res = NS_OK;
1666
nsCOMPtr<nsIDOMNode> node(aNode);
1667
nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(aSelection));
1669
if (mFlags & nsIPlaintextEditor::eEditorPlaintextMask)
1671
res = mHTMLEditor->CreateBR(node, aOffset, address_of(brNode));
1675
nsWSRunObject wsObj(mHTMLEditor, node, aOffset);
1676
nsCOMPtr<nsIDOMNode> visNode, linkNode;
1677
PRInt32 visOffset=0, newOffset;
1679
res = wsObj.PriorVisibleNode(node, aOffset, address_of(visNode), &visOffset, &wsType);
1680
if (NS_FAILED(res)) return res;
1681
if (wsType & nsWSRunObject::eBlock)
1682
bAfterBlock = PR_TRUE;
1683
res = wsObj.NextVisibleNode(node, aOffset, address_of(visNode), &visOffset, &wsType);
1684
if (NS_FAILED(res)) return res;
1685
if (wsType & nsWSRunObject::eBlock)
1686
bBeforeBlock = PR_TRUE;
1687
if (mHTMLEditor->IsInLink(node, address_of(linkNode)))
1690
nsCOMPtr<nsIDOMNode> linkParent;
1691
res = linkNode->GetParentNode(getter_AddRefs(linkParent));
1692
if (NS_FAILED(res)) return res;
1693
res = mHTMLEditor->SplitNodeDeep(linkNode, node, aOffset, &newOffset, PR_TRUE);
1694
if (NS_FAILED(res)) return res;
1695
// reset {node,aOffset} to the point where link was split
1697
aOffset = newOffset;
1699
res = wsObj.InsertBreak(address_of(node), &aOffset, address_of(brNode), nsIEditor::eNone);
1701
if (NS_FAILED(res)) return res;
1702
res = nsEditor::GetNodeLocation(brNode, address_of(node), &aOffset);
1703
if (NS_FAILED(res)) return res;
1704
if (bAfterBlock && bBeforeBlock)
1706
// we just placed a br between block boundaries.
1707
// This is the one case where we want the selection to be before
1708
// the br we just placed, as the br will be on a new line,
1709
// rather than at end of prior line.
1710
selPriv->SetInterlinePosition(PR_TRUE);
1711
res = aSelection->Collapse(node, aOffset);
1715
nsWSRunObject wsObj(mHTMLEditor, node, aOffset+1);
1716
nsCOMPtr<nsIDOMNode> secondBR;
1717
PRInt32 visOffset=0;
1719
res = wsObj.NextVisibleNode(node, aOffset+1, address_of(secondBR), &visOffset, &wsType);
1720
if (NS_FAILED(res)) return res;
1721
if (wsType==nsWSRunObject::eBreak)
1723
// the next thing after the break we inserted is another break. Move the 2nd
1724
// break to be the first breaks sibling. This will prevent them from being
1725
// in different inline nodes, which would break SetInterlinePosition(). It will
1726
// also assure that if the user clicks away and then clicks back on their new
1727
// blank line, they will still get the style from the line above.
1728
nsCOMPtr<nsIDOMNode> brParent;
1730
res = nsEditor::GetNodeLocation(secondBR, address_of(brParent), &brOffset);
1731
if (NS_FAILED(res)) return res;
1732
if ((brParent != node) || (brOffset != (aOffset+1)))
1734
res = mHTMLEditor->MoveNode(secondBR, node, aOffset+1);
1735
if (NS_FAILED(res)) return res;
1738
// SetInterlinePosition(PR_TRUE) means we want the caret to stick to the content on the "right".
1739
// We want the caret to stick to whatever is past the break. This is
1740
// because the break is on the same line we were on, but the next content
1741
// will be on the following line.
1743
// An exception to this is if the break has a next sibling that is a block node.
1744
// Then we stick to the left to avoid an uber caret.
1745
nsCOMPtr<nsIDOMNode> siblingNode;
1746
brNode->GetNextSibling(getter_AddRefs(siblingNode));
1747
if (siblingNode && IsBlockNode(siblingNode))
1748
selPriv->SetInterlinePosition(PR_FALSE);
1750
selPriv->SetInterlinePosition(PR_TRUE);
1751
res = aSelection->Collapse(node, aOffset+1);
1757
nsHTMLEditRules::DidInsertBreak(nsISelection *aSelection, nsresult aResult)
1764
nsHTMLEditRules::SplitMailCites(nsISelection *aSelection, PRBool aPlaintext, PRBool *aHandled)
1766
if (!aSelection || !aHandled)
1767
return NS_ERROR_NULL_POINTER;
1768
nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(aSelection));
1769
nsCOMPtr<nsIDOMNode> citeNode, selNode, leftCite, rightCite;
1770
PRInt32 selOffset, newOffset;
1771
nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
1772
if (NS_FAILED(res)) return res;
1773
res = GetTopEnclosingMailCite(selNode, address_of(citeNode), aPlaintext);
1774
if (NS_FAILED(res)) return res;
1777
// If our selection is just before a break, nudge it to be
1778
// just after it. This does two things for us. It saves us the trouble of having to add
1779
// a break here ourselves to preserve the "blockness" of the inline span mailquote
1780
// (in the inline case), and :
1781
// it means the break wont end up making an empty line that happens to be inside a
1782
// mailquote (in either inline or block case).
1783
// The latter can confuse a user if they click there and start typing,
1784
// because being in the mailquote may affect wrapping behavior, or font color, etc.
1785
nsWSRunObject wsObj(mHTMLEditor, selNode, selOffset);
1786
nsCOMPtr<nsIDOMNode> visNode;
1787
PRInt32 visOffset=0;
1789
res = wsObj.NextVisibleNode(selNode, selOffset, address_of(visNode), &visOffset, &wsType);
1790
if (NS_FAILED(res)) return res;
1791
if (wsType==nsWSRunObject::eBreak)
1793
// ok, we are just before a break. is it inside the mailquote?
1795
if (nsEditorUtils::IsDescendantOf(visNode, citeNode, &unused))
1797
// it is. so lets reset our selection to be just after it.
1798
res = mHTMLEditor->GetNodeLocation(visNode, address_of(selNode), &selOffset);
1799
if (NS_FAILED(res)) return res;
1804
nsCOMPtr<nsIDOMNode> brNode;
1805
res = mHTMLEditor->SplitNodeDeep(citeNode, selNode, selOffset, &newOffset,
1806
PR_TRUE, address_of(leftCite), address_of(rightCite));
1807
if (NS_FAILED(res)) return res;
1808
res = citeNode->GetParentNode(getter_AddRefs(selNode));
1809
if (NS_FAILED(res)) return res;
1810
res = mHTMLEditor->CreateBR(selNode, newOffset, address_of(brNode));
1811
if (NS_FAILED(res)) return res;
1812
// want selection before the break, and on same line
1813
selPriv->SetInterlinePosition(PR_TRUE);
1814
res = aSelection->Collapse(selNode, newOffset);
1815
if (NS_FAILED(res)) return res;
1816
// if citeNode wasn't a block, we might also want another break before it.
1817
// We need to examine the content both before the br we just added and also
1818
// just after it. If we dont have another br or block boundary adjacent,
1819
// then we will need a 2nd br added to achieve blank line that user expects.
1820
if (IsInlineNode(citeNode))
1822
nsWSRunObject wsObj(mHTMLEditor, selNode, newOffset);
1823
nsCOMPtr<nsIDOMNode> visNode;
1824
PRInt32 visOffset=0;
1826
res = wsObj.PriorVisibleNode(selNode, newOffset, address_of(visNode), &visOffset, &wsType);
1827
if (NS_FAILED(res)) return res;
1828
if ((wsType==nsWSRunObject::eNormalWS) ||
1829
(wsType==nsWSRunObject::eText) ||
1830
(wsType==nsWSRunObject::eSpecial))
1832
nsWSRunObject wsObjAfterBR(mHTMLEditor, selNode, newOffset+1);
1833
res = wsObjAfterBR.NextVisibleNode(selNode, newOffset+1, address_of(visNode), &visOffset, &wsType);
1834
if (NS_FAILED(res)) return res;
1835
if ((wsType==nsWSRunObject::eNormalWS) ||
1836
(wsType==nsWSRunObject::eText) ||
1837
(wsType==nsWSRunObject::eSpecial))
1839
res = mHTMLEditor->CreateBR(selNode, newOffset, address_of(brNode));
1840
if (NS_FAILED(res)) return res;
1844
// delete any empty cites
1845
PRBool bEmptyCite = PR_FALSE;
1848
res = mHTMLEditor->IsEmptyNode(leftCite, &bEmptyCite, PR_TRUE, PR_FALSE);
1849
if (NS_SUCCEEDED(res) && bEmptyCite)
1850
res = mHTMLEditor->DeleteNode(leftCite);
1851
if (NS_FAILED(res)) return res;
1855
res = mHTMLEditor->IsEmptyNode(rightCite, &bEmptyCite, PR_TRUE, PR_FALSE);
1856
if (NS_SUCCEEDED(res) && bEmptyCite)
1857
res = mHTMLEditor->DeleteNode(rightCite);
1858
if (NS_FAILED(res)) return res;
1860
*aHandled = PR_TRUE;
1867
nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
1868
nsIEditor::EDirection aAction,
1873
if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
1874
// initialize out param
1875
*aCancel = PR_FALSE;
1876
*aHandled = PR_FALSE;
1878
// remember that we did a selection deletion. Used by CreateStyleForInsertText()
1879
mDidDeleteSelection = PR_TRUE;
1881
// if there is only bogus content, cancel the operation
1888
nsresult res = NS_OK;
1889
PRBool bPlaintext = mFlags & nsIPlaintextEditor::eEditorPlaintextMask;
1892
res = aSelection->GetIsCollapsed(&bCollapsed);
1893
if (NS_FAILED(res)) return res;
1895
nsCOMPtr<nsIDOMNode> startNode, selNode;
1896
PRInt32 startOffset, selOffset;
1898
// first check for table selection mode. If so,
1899
// hand off to table editor.
1901
nsCOMPtr<nsIDOMElement> cell;
1902
res = mHTMLEditor->GetFirstSelectedCell(nsnull, getter_AddRefs(cell));
1903
if (NS_SUCCEEDED(res) && cell)
1905
res = mHTMLEditor->DeleteTableCellContents();
1906
*aHandled = PR_TRUE;
1911
res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(startNode), &startOffset);
1912
if (NS_FAILED(res)) return res;
1913
if (!startNode) return NS_ERROR_FAILURE;
1915
// get the root element
1916
nsCOMPtr<nsIDOMElement> bodyNode;
1918
res = mHTMLEditor->GetRootElement(getter_AddRefs(bodyNode));
1919
if (NS_FAILED(res)) return res;
1920
if (!bodyNode) return NS_ERROR_UNEXPECTED;
1925
// if we are inside an empty block, delete it.
1926
res = CheckForEmptyBlock(startNode, bodyNode, aSelection, aHandled);
1927
if (NS_FAILED(res)) return res;
1928
if (*aHandled) return NS_OK;
1930
// Test for distance between caret and text that will be deleted
1931
res = CheckBidiLevelForDeletion(startNode, startOffset, aAction, aCancel);
1932
if (NS_FAILED(res)) return res;
1933
if (*aCancel) return NS_OK;
1936
// what's in the direction we are deleting?
1937
nsWSRunObject wsObj(mHTMLEditor, startNode, startOffset);
1938
nsCOMPtr<nsIDOMNode> visNode;
1942
// find next visible node
1943
if (aAction == nsIEditor::eNext)
1944
res = wsObj.NextVisibleNode(startNode, startOffset, address_of(visNode), &visOffset, &wsType);
1946
res = wsObj.PriorVisibleNode(startNode, startOffset, address_of(visNode), &visOffset, &wsType);
1947
if (NS_FAILED(res)) return res;
1949
if (!visNode) // can't find anything to delete!
1955
if (wsType==nsWSRunObject::eNormalWS)
1957
// we found some visible ws to delete. Let ws code handle it.
1958
if (aAction == nsIEditor::eNext)
1959
res = wsObj.DeleteWSForward();
1961
res = wsObj.DeleteWSBackward();
1962
*aHandled = PR_TRUE;
1963
if (NS_FAILED(res)) return res;
1964
res = InsertBRIfNeeded(aSelection);
1967
else if (wsType==nsWSRunObject::eText)
1969
// found normal text to delete.
1970
PRInt32 so = visOffset;
1971
PRInt32 eo = visOffset+1;
1972
if (aAction == nsIEditor::ePrevious)
1974
if (so == 0) return NS_ERROR_UNEXPECTED;
1978
res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor, address_of(visNode), &so, address_of(visNode), &eo);
1979
if (NS_FAILED(res)) return res;
1980
nsCOMPtr<nsIDOMCharacterData> nodeAsText(do_QueryInterface(visNode));
1981
res = mHTMLEditor->DeleteText(nodeAsText,so,1);
1982
*aHandled = PR_TRUE;
1983
if (NS_FAILED(res)) return res;
1984
res = InsertBRIfNeeded(aSelection);
1987
else if ( (wsType==nsWSRunObject::eSpecial) ||
1988
(wsType==nsWSRunObject::eBreak) ||
1989
nsHTMLEditUtils::IsHR(visNode) )
1991
// short circuit for invisible breaks. delete them and recurse.
1992
if (nsTextEditUtils::IsBreak(visNode) && !mHTMLEditor->IsVisBreak(visNode))
1994
res = mHTMLEditor->DeleteNode(visNode);
1995
if (NS_FAILED(res)) return res;
1996
return WillDeleteSelection(aSelection, aAction, aCancel, aHandled);
1999
// special handling for backspace when positioned after <hr>
2000
if (aAction == nsIEditor::ePrevious && nsHTMLEditUtils::IsHR(visNode))
2003
Only if the caret is positioned at the end-of-hr-line position,
2004
we want to delete the <hr>.
2006
In other words, we only want to delete, if
2007
our selection position (indicated by startNode and startOffset)
2008
is the position directly after the <hr>,
2009
on the same line as the <hr>.
2011
To detect this case we check:
2012
startNode == parentOfVisNode
2014
startOffset -1 == visNodeOffsetToVisNodeParent
2016
interline position is false (left)
2018
In any other case we set the position to
2019
startnode -1 and interlineposition to false,
2020
only moving the caret to the end-of-hr-line position.
2023
PRBool moveOnly = PR_TRUE;
2025
res = nsEditor::GetNodeLocation(visNode, address_of(selNode), &selOffset);
2026
if (NS_FAILED(res)) return res;
2028
PRBool interLineIsRight;
2029
nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(aSelection));
2030
res = selPriv->GetInterlinePosition(&interLineIsRight);
2031
if (NS_FAILED(res)) return res;
2033
if (startNode == selNode &&
2034
startOffset -1 == selOffset &&
2037
moveOnly = PR_FALSE;
2042
// Go to the position after the <hr>, but to the end of the <hr> line
2043
// by setting the interline position to left.
2045
res = aSelection->Collapse(selNode, selOffset);
2046
selPriv->SetInterlinePosition(PR_FALSE);
2047
mDidExplicitlySetInterline = PR_TRUE;
2048
*aHandled = PR_TRUE;
2050
// There is one exception to the move only case.
2051
// If the <hr> is followed by a <br> we want to delete the <br>.
2053
PRInt16 otherWSType;
2054
nsCOMPtr<nsIDOMNode> otherNode;
2055
PRInt32 otherOffset;
2057
res = wsObj.NextVisibleNode(startNode, startOffset, address_of(otherNode), &otherOffset, &otherWSType);
2058
if (NS_FAILED(res)) return res;
2060
if (otherWSType == nsWSRunObject::eBreak)
2064
res = nsWSRunObject::PrepareToDeleteNode(mHTMLEditor, otherNode);
2065
if (NS_FAILED(res)) return res;
2066
res = mHTMLEditor->DeleteNode(otherNode);
2067
if (NS_FAILED(res)) return res;
2072
// else continue with normal delete code
2075
// found break or image, or hr.
2076
res = nsWSRunObject::PrepareToDeleteNode(mHTMLEditor, visNode);
2077
if (NS_FAILED(res)) return res;
2078
// remember sibling to visnode, if any
2079
nsCOMPtr<nsIDOMNode> sibling, stepbrother;
2080
mHTMLEditor->GetPriorHTMLSibling(visNode, address_of(sibling));
2081
// delete the node, and join like nodes if appropriate
2082
res = mHTMLEditor->DeleteNode(visNode);
2083
if (NS_FAILED(res)) return res;
2084
// we did something, so lets say so.
2085
*aHandled = PR_TRUE;
2086
// is there a prior node and are they siblings?
2088
mHTMLEditor->GetNextHTMLSibling(sibling, address_of(stepbrother));
2089
if (startNode == stepbrother)
2091
// are they both text nodes?
2092
if (mHTMLEditor->IsTextNode(startNode) && mHTMLEditor->IsTextNode(sibling))
2094
// if so, join them!
2095
res = JoinNodesSmart(sibling, startNode, address_of(selNode), &selOffset);
2096
if (NS_FAILED(res)) return res;
2098
res = aSelection->Collapse(selNode, selOffset);
2101
if (NS_FAILED(res)) return res;
2102
res = InsertBRIfNeeded(aSelection);
2105
else if (wsType==nsWSRunObject::eOtherBlock)
2107
// make sure it's not a table element. If so, cancel the operation
2108
// (translation: users cannot backspace or delete across table cells)
2109
if (nsHTMLEditUtils::IsTableElement(visNode))
2115
// next to a block. See if we are between a block and a br. If so, we really
2116
// want to delete the br. Else join content at selection to the block.
2118
PRBool bDeletedBR = PR_FALSE;
2119
PRInt16 otherWSType;
2120
nsCOMPtr<nsIDOMNode> otherNode;
2121
PRInt32 otherOffset;
2123
// find node in other direction
2124
if (aAction == nsIEditor::eNext)
2125
res = wsObj.PriorVisibleNode(startNode, startOffset, address_of(otherNode), &otherOffset, &otherWSType);
2127
res = wsObj.NextVisibleNode(startNode, startOffset, address_of(otherNode), &otherOffset, &otherWSType);
2128
if (NS_FAILED(res)) return res;
2130
// first find the adjacent node in the block
2131
nsCOMPtr<nsIDOMNode> leafNode, leftNode, rightNode, leftParent, rightParent;
2132
if (aAction == nsIEditor::ePrevious)
2134
res = mHTMLEditor->GetLastEditableLeaf( visNode, address_of(leafNode));
2135
if (NS_FAILED(res)) return res;
2136
leftNode = leafNode;
2137
rightNode = startNode;
2141
res = mHTMLEditor->GetFirstEditableLeaf( visNode, address_of(leafNode));
2142
if (NS_FAILED(res)) return res;
2143
leftNode = startNode;
2144
rightNode = leafNode;
2147
if (nsTextEditUtils::IsBreak(otherNode))
2149
res = mHTMLEditor->DeleteNode(otherNode);
2150
if (NS_FAILED(res)) return res;
2151
*aHandled = PR_TRUE;
2152
bDeletedBR = PR_TRUE;
2155
// dont cross table boundaries
2156
if (leftNode && rightNode)
2158
PRBool bInDifTblElems;
2159
res = InDifferentTableElements(leftNode, rightNode, &bInDifTblElems);
2160
if (NS_FAILED(res) || bInDifTblElems) return res;
2165
// put selection at edge of block and we are done.
2166
nsCOMPtr<nsIDOMNode> newSelNode;
2167
PRInt32 newSelOffset;
2168
res = GetGoodSelPointForNode(leafNode, aAction, address_of(newSelNode), &newSelOffset);
2169
if (NS_FAILED(res)) return res;
2170
aSelection->Collapse(newSelNode, newSelOffset);
2174
// else we are joining content to block
2176
// find the relavent blocks
2177
if (IsBlockNode(leftNode))
2178
leftParent = leftNode;
2180
leftParent = mHTMLEditor->GetBlockNodeParent(leftNode);
2181
if (IsBlockNode(rightNode))
2182
rightParent = rightNode;
2184
rightParent = mHTMLEditor->GetBlockNodeParent(rightNode);
2187
if (!leftParent || !rightParent)
2188
return NS_ERROR_NULL_POINTER;
2189
if (leftParent == rightParent)
2190
return NS_ERROR_UNEXPECTED;
2193
nsCOMPtr<nsIDOMNode> selPointNode = startNode;
2194
PRInt32 selPointOffset = startOffset;
2196
nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, address_of(selPointNode), &selPointOffset);
2197
res = JoinBlocks(address_of(leftParent), address_of(rightParent), aCancel);
2198
*aHandled = PR_TRUE;
2200
aSelection->Collapse(selPointNode, selPointOffset);
2203
else if (wsType==nsWSRunObject::eThisBlock)
2205
// at edge of our block. Look beside it and see if we can join to an adjacent block
2207
// make sure it's not a table element. If so, cancel the operation
2208
// (translation: users cannot backspace or delete across table cells)
2209
if (nsHTMLEditUtils::IsTableElement(visNode))
2215
// first find the relavent nodes
2216
nsCOMPtr<nsIDOMNode> leftNode, rightNode, leftParent, rightParent;
2217
if (aAction == nsIEditor::ePrevious)
2219
res = mHTMLEditor->GetPriorHTMLNode(visNode, address_of(leftNode));
2220
if (NS_FAILED(res)) return res;
2221
rightNode = startNode;
2225
res = mHTMLEditor->GetNextHTMLNode( visNode, address_of(rightNode));
2226
if (NS_FAILED(res)) return res;
2227
leftNode = startNode;
2230
// dont cross table boundaries
2231
PRBool bInDifTblElems;
2232
res = InDifferentTableElements(leftNode, rightNode, &bInDifTblElems);
2233
if (NS_FAILED(res) || bInDifTblElems)
2235
// Indicating that the operation had to stop because we crossed
2236
// a table cell boundary
2241
// find the relavent blocks
2242
if (IsBlockNode(leftNode))
2243
leftParent = leftNode;
2245
leftParent = mHTMLEditor->GetBlockNodeParent(leftNode);
2246
if (IsBlockNode(rightNode))
2247
rightParent = rightNode;
2249
rightParent = mHTMLEditor->GetBlockNodeParent(rightNode);
2252
if (!leftParent || !rightParent)
2253
return NS_ERROR_NULL_POINTER;
2254
if (leftParent == rightParent)
2255
return NS_ERROR_UNEXPECTED;
2258
nsCOMPtr<nsIDOMNode> selPointNode = startNode;
2259
PRInt32 selPointOffset = startOffset;
2261
nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, address_of(selPointNode), &selPointOffset);
2262
res = JoinBlocks(address_of(leftParent), address_of(rightParent), aCancel);
2263
*aHandled = PR_TRUE;
2265
aSelection->Collapse(selPointNode, selPointOffset);
2271
// else we have a non collapsed selection
2272
// first adjust the selection
2273
res = ExpandSelectionForDeletion(aSelection);
2274
if (NS_FAILED(res)) return res;
2276
// remember that we did a ranged delete for the benefit of AfterEditInner().
2277
mDidRangedDelete = PR_TRUE;
2279
// refresh start and end points
2280
res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(startNode), &startOffset);
2281
if (NS_FAILED(res)) return res;
2282
if (!startNode) return NS_ERROR_FAILURE;
2283
nsCOMPtr<nsIDOMNode> endNode;
2285
res = mHTMLEditor->GetEndNodeAndOffset(aSelection, address_of(endNode), &endOffset);
2286
if (NS_FAILED(res)) return res;
2287
if (!endNode) return NS_ERROR_FAILURE;
2289
// figure out if the endpoints are in nodes that can be merged
2290
// adjust surrounding whitespace in preperation to delete selection
2293
nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
2294
res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor,
2295
address_of(startNode), &startOffset,
2296
address_of(endNode), &endOffset);
2297
if (NS_FAILED(res)) return res;
2301
// track end location of where we are deleting
2302
nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, address_of(endNode), &endOffset);
2303
// we are handling all ranged deletions directly now.
2304
*aHandled = PR_TRUE;
2306
if (endNode == startNode)
2308
res = mHTMLEditor->DeleteSelectionImpl(aAction);
2309
if (NS_FAILED(res)) return res;
2313
// figure out mailcite ancestors
2314
nsCOMPtr<nsIDOMNode> endCiteNode, startCiteNode;
2315
res = GetTopEnclosingMailCite(startNode, address_of(startCiteNode),
2316
mFlags & nsIPlaintextEditor::eEditorPlaintextMask);
2317
if (NS_FAILED(res)) return res;
2318
res = GetTopEnclosingMailCite(endNode, address_of(endCiteNode),
2319
mFlags & nsIPlaintextEditor::eEditorPlaintextMask);
2320
if (NS_FAILED(res)) return res;
2322
// if we only have a mailcite at one of the two endpoints, set the directionality
2323
// of the deletion so that the selection will end up outside the mailcite.
2324
if (startCiteNode && !endCiteNode)
2326
aAction = nsIEditor::eNext;
2328
else if (!startCiteNode && endCiteNode)
2330
aAction = nsIEditor::ePrevious;
2333
// figure out block parents
2334
nsCOMPtr<nsIDOMNode> leftParent;
2335
nsCOMPtr<nsIDOMNode> rightParent;
2336
if (IsBlockNode(startNode))
2337
leftParent = startNode;
2339
leftParent = mHTMLEditor->GetBlockNodeParent(startNode);
2340
if (IsBlockNode(endNode))
2341
rightParent = endNode;
2343
rightParent = mHTMLEditor->GetBlockNodeParent(endNode);
2345
// are endpoint block parents the same? use default deletion
2346
if (leftParent == rightParent)
2348
res = mHTMLEditor->DeleteSelectionImpl(aAction);
2352
// deleting across blocks
2353
// are the blocks of same type?
2355
// are the blocks siblings?
2356
nsCOMPtr<nsIDOMNode> leftBlockParent;
2357
nsCOMPtr<nsIDOMNode> rightBlockParent;
2358
leftParent->GetParentNode(getter_AddRefs(leftBlockParent));
2359
rightParent->GetParentNode(getter_AddRefs(rightBlockParent));
2361
// MOOSE: this could conceivably screw up a table.. fix me.
2362
if ( (leftBlockParent == rightBlockParent)
2363
&& (mHTMLEditor->NodesSameType(leftParent, rightParent)) )
2365
if (nsHTMLEditUtils::IsParagraph(leftParent))
2367
// first delete the selection
2368
res = mHTMLEditor->DeleteSelectionImpl(aAction);
2369
if (NS_FAILED(res)) return res;
2370
// then join para's, insert break
2371
res = mHTMLEditor->JoinNodeDeep(leftParent,rightParent,address_of(selNode),&selOffset);
2372
if (NS_FAILED(res)) return res;
2374
res = aSelection->Collapse(selNode,selOffset);
2377
if (nsHTMLEditUtils::IsListItem(leftParent)
2378
|| nsHTMLEditUtils::IsHeader(leftParent))
2380
// first delete the selection
2381
res = mHTMLEditor->DeleteSelectionImpl(aAction);
2382
if (NS_FAILED(res)) return res;
2384
res = mHTMLEditor->JoinNodeDeep(leftParent,rightParent,address_of(selNode),&selOffset);
2385
if (NS_FAILED(res)) return res;
2387
res = aSelection->Collapse(selNode,selOffset);
2392
// else blocks not same type, or not siblings. Delete everything except
2394
nsCOMPtr<nsIEnumerator> enumerator;
2395
nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(aSelection));
2396
res = selPriv->GetEnumerator(getter_AddRefs(enumerator));
2397
if (NS_FAILED(res)) return res;
2398
if (!enumerator) return NS_ERROR_UNEXPECTED;
2400
for (enumerator->First(); NS_OK!=enumerator->IsDone(); enumerator->Next())
2402
nsCOMPtr<nsISupports> currentItem;
2403
res = enumerator->CurrentItem(getter_AddRefs(currentItem));
2404
if (NS_FAILED(res)) return res;
2405
if (!currentItem) return NS_ERROR_UNEXPECTED;
2407
// build a list of nodes in the range
2408
nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
2409
nsCOMArray<nsIDOMNode> arrayOfNodes;
2410
nsTrivialFunctor functor;
2411
nsDOMSubtreeIterator iter;
2412
res = iter.Init(range);
2413
if (NS_FAILED(res)) return res;
2414
res = iter.AppendList(functor, arrayOfNodes);
2415
if (NS_FAILED(res)) return res;
2417
// now that we have the list, delete non table elements
2418
PRInt32 listCount = arrayOfNodes.Count();
2421
for (j = 0; j < listCount; j++)
2423
nsIDOMNode* somenode = arrayOfNodes[0];
2424
res = DeleteNonTableElements(somenode);
2425
arrayOfNodes.RemoveObjectAt(0);
2429
// check endopints for possible text deletion.
2430
// we can assume that if text node is found, we can
2431
// delete to end or to begining as appropriate,
2432
// since the case where both sel endpoints in same
2433
// text node was already handled (we wouldn't be here)
2434
if ( mHTMLEditor->IsTextNode(startNode) )
2436
// delete to last character
2437
nsCOMPtr<nsIDOMCharacterData>nodeAsText;
2439
nodeAsText = do_QueryInterface(startNode);
2440
nodeAsText->GetLength(&len);
2441
if (len > (PRUint32)startOffset)
2443
res = mHTMLEditor->DeleteText(nodeAsText,startOffset,len-startOffset);
2444
if (NS_FAILED(res)) return res;
2447
if ( mHTMLEditor->IsTextNode(endNode) )
2449
// delete to first character
2450
nsCOMPtr<nsIDOMCharacterData>nodeAsText;
2451
nodeAsText = do_QueryInterface(endNode);
2454
res = mHTMLEditor->DeleteText(nodeAsText,0,endOffset);
2455
if (NS_FAILED(res)) return res;
2461
if (aAction == nsIEditor::eNext)
2463
res = aSelection->Collapse(endNode,endOffset);
2467
res = aSelection->Collapse(startNode,startOffset);
2473
/*****************************************************************************************************
2474
* InsertBRIfNeeded: determines if a br is needed for current selection to not be spastic.
2475
* If so, it inserts one. Callers responsibility to only call with collapsed selection.
2476
* nsISelection *aSelection the collapsed selection
2479
nsHTMLEditRules::InsertBRIfNeeded(nsISelection *aSelection)
2482
return NS_ERROR_NULL_POINTER;
2485
nsCOMPtr<nsIDOMNode> node;
2487
nsresult res = mEditor->GetStartNodeAndOffset(aSelection, address_of(node), &offset);
2488
if (NS_FAILED(res)) return res;
2489
if (!node) return NS_ERROR_FAILURE;
2491
// examine selection
2492
nsWSRunObject wsObj(mHTMLEditor, node, offset);
2493
if (((wsObj.mStartReason & nsWSRunObject::eBlock) || (wsObj.mStartReason & nsWSRunObject::eBreak))
2494
&& (wsObj.mEndReason & nsWSRunObject::eBlock))
2496
// if we are tucked between block boundaries then insert a br
2497
// first check that we are allowed to
2498
if (mHTMLEditor->CanContainTag(node, NS_LITERAL_STRING("br")))
2500
nsCOMPtr<nsIDOMNode> brNode;
2501
res = mHTMLEditor->CreateBR(node, offset, address_of(brNode), nsIEditor::ePrevious);
2507
/*****************************************************************************************************
2508
* GetGoodSelPointForNode: Finds where at a node you would want to set the selection if you were
2509
* trying to have a caret next to it.
2510
* nsIDOMNode *aNode the node
2511
* nsIEditor::EDirection aAction which edge to find: eNext indicates beginning, ePrevious ending
2512
* nsCOMPtr<nsIDOMNode> *outSelNode desired sel node
2513
* PRInt32 *outSelOffset desired sel offset
2516
nsHTMLEditRules::GetGoodSelPointForNode(nsIDOMNode *aNode, nsIEditor::EDirection aAction,
2517
nsCOMPtr<nsIDOMNode> *outSelNode, PRInt32 *outSelOffset)
2519
if (!aNode || !outSelNode || !outSelOffset)
2520
return NS_ERROR_NULL_POINTER;
2522
nsresult res = NS_OK;
2525
*outSelNode = aNode;
2528
if (mHTMLEditor->IsTextNode(aNode) || mHTMLEditor->IsContainer(aNode))
2530
if (aAction == nsIEditor::ePrevious)
2533
res = mHTMLEditor->GetLengthOfDOMNode(aNode, len);
2534
*outSelOffset = PRInt32(len);
2535
if (NS_FAILED(res)) return res;
2540
res = nsEditor::GetNodeLocation(aNode, outSelNode, outSelOffset);
2541
if (NS_FAILED(res)) return res;
2542
if (!nsTextEditUtils::IsBreak(aNode) || mHTMLEditor->IsVisBreak(aNode))
2544
if (aAction == nsIEditor::ePrevious)
2552
/*****************************************************************************************************
2553
* JoinBlocks: this method is used to join two block elements. The right element is always joined
2554
* to the left element. If the elements are the same type and not nested within each other,
2555
* JoinNodesSmart is called (example, joining two list items together into one). If the elements
2556
* are not the same type, or one is a descendant of the other, we instead destroy the right block
2557
* placing it's children into leftblock. DTD containment rules are followed throughout.
2558
* nsCOMPtr<nsIDOMNode> *aLeftBlock pointer to the left block
2559
* nsCOMPtr<nsIDOMNode> *aRightBlock pointer to the right block; will have contents moved to left block
2560
* PRBool *aCanceled return TRUE if we had to cancel operation
2563
nsHTMLEditRules::JoinBlocks(nsCOMPtr<nsIDOMNode> *aLeftBlock,
2564
nsCOMPtr<nsIDOMNode> *aRightBlock,
2567
if (!aLeftBlock || !aRightBlock || !*aLeftBlock || !*aRightBlock) return NS_ERROR_NULL_POINTER;
2568
if (nsHTMLEditUtils::IsTableElement(*aLeftBlock) || nsHTMLEditUtils::IsTableElement(*aRightBlock))
2570
// do not try to merge table elements
2571
*aCanceled = PR_TRUE;
2575
// make sure we dont try to move thing's into HR's, which look like blocks but aren't containers
2576
if (nsHTMLEditUtils::IsHR(*aLeftBlock))
2578
nsCOMPtr<nsIDOMNode> realLeft = mHTMLEditor->GetBlockNodeParent(*aLeftBlock);
2579
*aLeftBlock = realLeft;
2581
if (nsHTMLEditUtils::IsHR(*aRightBlock))
2583
nsCOMPtr<nsIDOMNode> realRight = mHTMLEditor->GetBlockNodeParent(*aRightBlock);
2584
*aRightBlock = realRight;
2587
// bail if both blocks the same
2588
if (*aLeftBlock == *aRightBlock)
2590
*aCanceled = PR_TRUE;
2594
// special rule here: if we are trying to join list items, and they are in different lists,
2595
// join the lists instead.
2596
PRBool bMergeLists = PR_FALSE;
2597
nsAutoString existingListStr;
2599
nsCOMPtr<nsIDOMNode> leftList, rightList;
2600
if (nsHTMLEditUtils::IsListItem(*aLeftBlock) && nsHTMLEditUtils::IsListItem(*aRightBlock))
2602
(*aLeftBlock)->GetParentNode(getter_AddRefs(leftList));
2603
(*aRightBlock)->GetParentNode(getter_AddRefs(rightList));
2604
if (leftList && rightList && (leftList!=rightList))
2606
// there are some special complications if the lists are descendants of
2607
// the other lists' items. Note that it is ok for them to be descendants
2608
// of the other lists themselves, which is the usual case for sublists
2609
// in our impllementation.
2610
if (!nsEditorUtils::IsDescendantOf(leftList, *aRightBlock, &theOffset) &&
2611
!nsEditorUtils::IsDescendantOf(rightList, *aLeftBlock, &theOffset))
2613
*aLeftBlock = leftList;
2614
*aRightBlock = rightList;
2615
bMergeLists = PR_TRUE;
2616
mHTMLEditor->GetTagString(leftList, existingListStr);
2617
ToLowerCase(existingListStr);
2622
nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
2624
nsresult res = NS_OK;
2625
PRInt32 rightOffset = 0;
2626
PRInt32 leftOffset = -1;
2628
// theOffset below is where you find yourself in aRightBlock when you traverse upwards
2630
if (nsEditorUtils::IsDescendantOf(*aLeftBlock, *aRightBlock, &rightOffset))
2632
// tricky case. left block is inside right block.
2633
// Do ws adjustment. This just destroys non-visible ws at boundaries we will be joining.
2635
res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor, aLeftBlock, nsWSRunObject::kBlockEnd);
2636
if (NS_FAILED(res)) return res;
2637
res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor, aRightBlock, nsWSRunObject::kAfterBlock, &rightOffset);
2638
if (NS_FAILED(res)) return res;
2639
// Do br adjustment.
2640
nsCOMPtr<nsIDOMNode> brNode;
2641
res = CheckForInvisibleBR(*aLeftBlock, kBlockEnd, address_of(brNode));
2642
if (NS_FAILED(res)) return res;
2645
// idea here is to take all children in rightList that are past
2646
// theOffset, and pull them into leftlist.
2647
nsCOMPtr<nsIDOMNode> childToMove;
2648
nsCOMPtr<nsIContent> parent(do_QueryInterface(rightList));
2650
return NS_ERROR_NULL_POINTER;
2652
nsIContent *child = parent->GetChildAt(theOffset);
2655
childToMove = do_QueryInterface(child);
2656
res = mHTMLEditor->MoveNode(childToMove, leftList, -1);
2660
child = parent->GetChildAt(rightOffset);
2665
res = MoveBlock(*aLeftBlock, *aRightBlock, leftOffset, rightOffset);
2667
if (brNode) mHTMLEditor->DeleteNode(brNode);
2669
// theOffset below is where you find yourself in aLeftBlock when you traverse upwards
2671
else if (nsEditorUtils::IsDescendantOf(*aRightBlock, *aLeftBlock, &leftOffset))
2673
// tricky case. right block is inside left block.
2674
// Do ws adjustment. This just destroys non-visible ws at boundaries we will be joining.
2675
res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor, aRightBlock, nsWSRunObject::kBlockStart);
2676
if (NS_FAILED(res)) return res;
2677
res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor, aLeftBlock, nsWSRunObject::kBeforeBlock, &leftOffset);
2678
if (NS_FAILED(res)) return res;
2679
// Do br adjustment.
2680
nsCOMPtr<nsIDOMNode> brNode;
2681
res = CheckForInvisibleBR(*aLeftBlock, kBeforeBlock, address_of(brNode), leftOffset);
2682
if (NS_FAILED(res)) return res;
2685
res = MoveContents(rightList, leftList, &leftOffset);
2689
res = MoveBlock(*aLeftBlock, *aRightBlock, leftOffset, rightOffset);
2691
if (brNode) mHTMLEditor->DeleteNode(brNode);
2695
// normal case. blocks are siblings, or at least close enough to siblings. An example
2696
// of the latter is a <p>paragraph</p><ul><li>one<li>two<li>three</ul>. The first
2697
// li and the p are not true siblings, but we still want to join them if you backspace
2700
// adjust whitespace at block boundaries
2701
res = nsWSRunObject::PrepareToJoinBlocks(mHTMLEditor, *aLeftBlock, *aRightBlock);
2702
if (NS_FAILED(res)) return res;
2703
// Do br adjustment.
2704
nsCOMPtr<nsIDOMNode> brNode;
2705
res = CheckForInvisibleBR(*aLeftBlock, kBlockEnd, address_of(brNode));
2706
if (NS_FAILED(res)) return res;
2707
if (bMergeLists || mHTMLEditor->NodesSameType(*aLeftBlock, *aRightBlock))
2709
// nodes are same type. merge them.
2710
nsCOMPtr<nsIDOMNode> parent;
2712
res = JoinNodesSmart(*aLeftBlock, *aRightBlock, address_of(parent), &offset);
2713
if (NS_FAILED(res)) return res;
2714
nsCOMPtr<nsIDOMNode> newBlock;
2715
res = ConvertListType(*aRightBlock, address_of(newBlock), existingListStr, NS_LITERAL_STRING("li"));
2719
// nodes are disimilar types.
2720
res = MoveBlock(*aLeftBlock, *aRightBlock, leftOffset, rightOffset);
2722
if (brNode) mHTMLEditor->DeleteNode(brNode);
2728
/*****************************************************************************************************
2729
* MoveBlock: this method is used to move the content from rightBlock into leftBlock
2730
* Note that the "block" might merely be inline nodes between <br>s, or between blocks, etc.
2731
* DTD containment rules are followed throughout.
2732
* nsIDOMNode *aLeftBlock parent to receive moved content
2733
* nsIDOMNode *aRightBlock parent to provide moved content
2734
* PRInt32 aLeftOffset offset in aLeftBlock to move content to
2735
* PRInt32 aRightOffset offset in aLeftBlock to move content to
2738
nsHTMLEditRules::MoveBlock(nsIDOMNode *aLeftBlock, nsIDOMNode *aRightBlock, PRInt32 aLeftOffset, PRInt32 aRightOffset)
2740
nsCOMArray<nsIDOMNode> arrayOfNodes;
2741
nsCOMPtr<nsISupports> isupports;
2742
// GetNodesFromPoint is the workhorse that figures out what we wnat to move.
2743
nsresult res = GetNodesFromPoint(DOMPoint(aRightBlock,aRightOffset), kMakeList, arrayOfNodes, PR_TRUE);
2744
if (NS_FAILED(res)) return res;
2745
PRInt32 listCount = arrayOfNodes.Count();
2747
for (i=0; i<listCount; i++)
2749
// get the node to act on
2750
nsIDOMNode* curNode = arrayOfNodes[i];
2751
if (IsBlockNode(curNode))
2753
// For block nodes, move their contents only, then delete block.
2754
res = MoveContents(curNode, aLeftBlock, &aLeftOffset);
2755
if (NS_FAILED(res)) return res;
2756
res = mHTMLEditor->DeleteNode(curNode);
2760
// otherwise move the content as is, checking against the dtd.
2761
res = MoveNodeSmart(curNode, aLeftBlock, &aLeftOffset);
2767
/*****************************************************************************************************
2768
* MoveNodeSmart: this method is used to move node aSource to (aDest,aOffset).
2769
* DTD containment rules are followed throughout. aOffset is updated to point _after_
2771
* nsIDOMNode *aSource the selection.
2772
* nsIDOMNode *aDest parent to receive moved content
2773
* PRInt32 *aOffset offset in aNewParent to move content to
2776
nsHTMLEditRules::MoveNodeSmart(nsIDOMNode *aSource, nsIDOMNode *aDest, PRInt32 *aOffset)
2778
if (!aSource || !aDest || !aOffset) return NS_ERROR_NULL_POINTER;
2782
res = mHTMLEditor->GetTagString(aSource, tag);
2783
if (NS_FAILED(res)) return res;
2785
// check if this node can go into the destination node
2786
if (mHTMLEditor->CanContainTag(aDest, tag))
2788
// if it can, move it there
2789
res = mHTMLEditor->MoveNode(aSource, aDest, *aOffset);
2790
if (NS_FAILED(res)) return res;
2791
if (*aOffset != -1) ++(*aOffset);
2795
// if it can't, move it's children, and then delete it.
2796
res = MoveContents(aSource, aDest, aOffset);
2797
if (NS_FAILED(res)) return res;
2798
res = mHTMLEditor->DeleteNode(aSource);
2799
if (NS_FAILED(res)) return res;
2804
/*****************************************************************************************************
2805
* MoveContents: this method is used to move node the _contents_ of aSource to (aDest,aOffset).
2806
* DTD containment rules are followed throughout. aOffset is updated to point _after_
2807
* inserted content. aSource is deleted.
2808
* nsIDOMNode *aSource the selection.
2809
* nsIDOMNode *aDest parent to receive moved content
2810
* PRInt32 *aOffset offset in aNewParent to move content to
2813
nsHTMLEditRules::MoveContents(nsIDOMNode *aSource, nsIDOMNode *aDest, PRInt32 *aOffset)
2815
if (!aSource || !aDest || !aOffset) return NS_ERROR_NULL_POINTER;
2816
if (aSource == aDest) return NS_ERROR_ILLEGAL_VALUE;
2818
nsCOMPtr<nsIDOMNode> child;
2821
aSource->GetFirstChild(getter_AddRefs(child));
2824
res = MoveNodeSmart(child, aDest, aOffset);
2825
if (NS_FAILED(res)) return res;
2826
aSource->GetFirstChild(getter_AddRefs(child));
2833
nsHTMLEditRules::DeleteNonTableElements(nsIDOMNode *aNode)
2835
if (!aNode) return NS_ERROR_NULL_POINTER;
2836
nsresult res = NS_OK;
2837
if (nsHTMLEditUtils::IsTableElementButNotTable(aNode))
2839
nsCOMPtr<nsIDOMNodeList> children;
2840
aNode->GetChildNodes(getter_AddRefs(children));
2844
children->GetLength(&len);
2845
if (!len) return NS_OK;
2847
for (j=len-1; j>=0; j--)
2849
nsCOMPtr<nsIDOMNode> node;
2850
children->Item(j,getter_AddRefs(node));
2851
res = DeleteNonTableElements(node);
2852
if (NS_FAILED(res)) return res;
2859
res = mHTMLEditor->DeleteNode(aNode);
2860
if (NS_FAILED(res)) return res;
2866
nsHTMLEditRules::DidDeleteSelection(nsISelection *aSelection,
2867
nsIEditor::EDirection aDir,
2870
if (!aSelection) { return NS_ERROR_NULL_POINTER; }
2872
// find where we are
2873
nsCOMPtr<nsIDOMNode> startNode;
2874
PRInt32 startOffset;
2875
nsresult res = mEditor->GetStartNodeAndOffset(aSelection, address_of(startNode), &startOffset);
2876
if (NS_FAILED(res)) return res;
2877
if (!startNode) return NS_ERROR_FAILURE;
2879
// find any enclosing mailcite
2880
nsCOMPtr<nsIDOMNode> citeNode;
2881
res = GetTopEnclosingMailCite(startNode, address_of(citeNode),
2882
mFlags & nsIPlaintextEditor::eEditorPlaintextMask);
2883
if (NS_FAILED(res)) return res;
2886
PRBool isEmpty = PR_TRUE, seenBR = PR_FALSE;
2887
mHTMLEditor->IsEmptyNodeImpl(citeNode, &isEmpty, PR_TRUE, PR_TRUE, PR_FALSE, &seenBR);
2890
nsCOMPtr<nsIDOMNode> parent, brNode;
2892
nsEditor::GetNodeLocation(citeNode, address_of(parent), &offset);
2893
res = mHTMLEditor->DeleteNode(citeNode);
2894
if (NS_FAILED(res)) return res;
2895
if (parent && seenBR)
2897
res = mHTMLEditor->CreateBR(parent, offset, address_of(brNode));
2898
if (NS_FAILED(res)) return res;
2899
aSelection->Collapse(parent, offset);
2904
// call through to base class
2905
return nsTextEditRules::DidDeleteSelection(aSelection, aDir, aResult);
2909
nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
2910
const nsAString *aListType,
2912
const nsAString *aBulletType,
2915
const nsAString *aItemType)
2917
if (!aSelection || !aListType || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
2919
nsresult res = WillInsert(aSelection, aCancel);
2920
if (NS_FAILED(res)) return res;
2922
// initialize out param
2923
// we want to ignore result of WillInsert()
2924
*aCancel = PR_FALSE;
2925
*aHandled = PR_FALSE;
2927
// deduce what tag to use for list items
2928
nsAutoString itemType;
2930
itemType = *aItemType;
2931
else if (aListType->Equals(NS_LITERAL_STRING("dl"),nsCaseInsensitiveStringComparator()))
2932
itemType.Assign(NS_LITERAL_STRING("dd"));
2934
itemType.Assign(NS_LITERAL_STRING("li"));
2936
// convert the selection ranges into "promoted" selection ranges:
2937
// this basically just expands the range to include the immediate
2938
// block parent, and then further expands to include any ancestors
2939
// whose children are all in the range
2941
*aHandled = PR_TRUE;
2943
res = NormalizeSelection(aSelection);
2944
if (NS_FAILED(res)) return res;
2945
nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
2947
nsCOMArray<nsIDOMNode> arrayOfNodes;
2948
res = GetListActionNodes(arrayOfNodes, aEntireList);
2949
if (NS_FAILED(res)) return res;
2951
PRInt32 listCount = arrayOfNodes.Count();
2953
// check if all our nodes are <br>s, or empty inlines
2954
PRBool bOnlyBreaks = PR_TRUE;
2956
for (j=0; j<listCount; j++)
2958
nsIDOMNode* curNode = arrayOfNodes[j];
2959
// if curNode is not a Break or empty inline, we're done
2960
if ( (!nsTextEditUtils::IsBreak(curNode)) && (!IsEmptyInline(curNode)) )
2962
bOnlyBreaks = PR_FALSE;
2967
// if no nodes, we make empty list. Ditto if the user tried to make a list of some # of breaks.
2968
if (!listCount || bOnlyBreaks)
2970
nsCOMPtr<nsIDOMNode> parent, theList, theListItem;
2973
// if only breaks, delete them
2976
for (j=0; j<(PRInt32)listCount; j++)
2978
res = mHTMLEditor->DeleteNode(arrayOfNodes[j]);
2979
if (NS_FAILED(res)) return res;
2983
// get selection location
2984
res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(parent), &offset);
2985
if (NS_FAILED(res)) return res;
2987
// make sure we can put a list here
2988
res = SplitAsNeeded(aListType, address_of(parent), &offset);
2989
if (NS_FAILED(res)) return res;
2990
res = mHTMLEditor->CreateNode(*aListType, parent, offset, getter_AddRefs(theList));
2991
if (NS_FAILED(res)) return res;
2992
res = mHTMLEditor->CreateNode(itemType, theList, 0, getter_AddRefs(theListItem));
2993
if (NS_FAILED(res)) return res;
2994
// remember our new block for postprocessing
2995
mNewBlock = theListItem;
2996
// put selection in new list item
2997
res = aSelection->Collapse(theListItem,0);
2998
selectionResetter.Abort(); // to prevent selection reseter from overriding us.
2999
*aHandled = PR_TRUE;
3003
// if there is only one node in the array, and it is a list, div, or blockquote,
3004
// then look inside of it until we find inner list or content.
3006
res = LookInsideDivBQandList(arrayOfNodes);
3007
if (NS_FAILED(res)) return res;
3009
// Ok, now go through all the nodes and put then in the list,
3010
// or whatever is approriate. Wohoo!
3012
listCount = arrayOfNodes.Count();
3013
nsCOMPtr<nsIDOMNode> curParent;
3014
nsCOMPtr<nsIDOMNode> curList;
3015
nsCOMPtr<nsIDOMNode> prevListItem;
3018
for (i=0; i<listCount; i++)
3020
// here's where we actually figure out what to do
3021
nsCOMPtr<nsIDOMNode> newBlock;
3022
nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
3024
res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset);
3025
if (NS_FAILED(res)) return res;
3027
// make sure we dont assemble content that is in different table cells into the same list.
3028
// respect table cell boundaries when listifying.
3031
PRBool bInDifTblElems;
3032
res = InDifferentTableElements(curList, curNode, &bInDifTblElems);
3033
if (NS_FAILED(res)) return res;
3038
// if curNode is a Break, delete it, and quit remembering prev list item
3039
if (nsTextEditUtils::IsBreak(curNode))
3041
res = mHTMLEditor->DeleteNode(curNode);
3042
if (NS_FAILED(res)) return res;
3046
// if curNode is an empty inline container, delete it
3047
else if (IsEmptyInline(curNode))
3049
res = mHTMLEditor->DeleteNode(curNode);
3050
if (NS_FAILED(res)) return res;
3054
if (nsHTMLEditUtils::IsList(curNode))
3056
nsAutoString existingListStr;
3057
res = mHTMLEditor->GetTagString(curNode, existingListStr);
3058
ToLowerCase(existingListStr);
3059
// do we have a curList already?
3060
if (curList && !nsEditorUtils::IsDescendantOf(curNode, curList))
3062
// move all of our children into curList.
3063
// cheezy way to do it: move whole list and then
3064
// RemoveContainer() on the list.
3065
// ConvertListType first: that routine
3066
// handles converting the list item types, if needed
3067
res = mHTMLEditor->MoveNode(curNode, curList, -1);
3068
if (NS_FAILED(res)) return res;
3069
res = ConvertListType(curNode, address_of(newBlock), *aListType, itemType);
3070
if (NS_FAILED(res)) return res;
3071
res = mHTMLEditor->RemoveBlockContainer(newBlock);
3072
if (NS_FAILED(res)) return res;
3076
// replace list with new list type
3077
res = ConvertListType(curNode, address_of(newBlock), *aListType, itemType);
3078
if (NS_FAILED(res)) return res;
3085
if (nsHTMLEditUtils::IsListItem(curNode))
3087
nsAutoString existingListStr;
3088
res = mHTMLEditor->GetTagString(curParent, existingListStr);
3089
ToLowerCase(existingListStr);
3090
if ( existingListStr != *aListType )
3092
// list item is in wrong type of list.
3093
// if we dont have a curList, split the old list
3094
// and make a new list of correct type.
3095
if (!curList || nsEditorUtils::IsDescendantOf(curNode, curList))
3097
res = mHTMLEditor->SplitNode(curParent, offset, getter_AddRefs(newBlock));
3098
if (NS_FAILED(res)) return res;
3099
nsCOMPtr<nsIDOMNode> p;
3101
res = nsEditor::GetNodeLocation(curParent, address_of(p), &o);
3102
if (NS_FAILED(res)) return res;
3103
res = mHTMLEditor->CreateNode(*aListType, p, o, getter_AddRefs(curList));
3104
if (NS_FAILED(res)) return res;
3106
// move list item to new list
3107
res = mHTMLEditor->MoveNode(curNode, curList, -1);
3108
if (NS_FAILED(res)) return res;
3109
// convert list item type if needed
3110
if (!mHTMLEditor->NodeIsTypeString(curNode,itemType))
3112
res = mHTMLEditor->ReplaceContainer(curNode, address_of(newBlock), itemType);
3113
if (NS_FAILED(res)) return res;
3118
// item is in right type of list. But we might still have to move it.
3119
// and we might need to convert list item types.
3121
curList = curParent;
3124
if (curParent != curList)
3126
// move list item to new list
3127
res = mHTMLEditor->MoveNode(curNode, curList, -1);
3128
if (NS_FAILED(res)) return res;
3131
if (!mHTMLEditor->NodeIsTypeString(curNode,itemType))
3133
res = mHTMLEditor->ReplaceContainer(curNode, address_of(newBlock), itemType);
3134
if (NS_FAILED(res)) return res;
3137
nsCOMPtr<nsIDOMElement> curElement = do_QueryInterface(curNode);
3138
NS_NAMED_LITERAL_STRING(typestr, "type");
3139
if (aBulletType && !aBulletType->IsEmpty()) {
3140
res = mHTMLEditor->SetAttributeOrEquivalent(curElement, typestr, *aBulletType, PR_TRUE);
3143
res = mHTMLEditor->RemoveAttributeOrEquivalent(curElement, typestr, PR_TRUE);
3145
if (NS_FAILED(res)) return res;
3149
// if we hit a div clear our prevListItem, insert divs contents
3150
// into our node array, and remove the div
3151
if (nsHTMLEditUtils::IsDiv(curNode))
3153
prevListItem = nsnull;
3155
res = GetInnerContent(curNode, arrayOfNodes, &j);
3156
if (NS_FAILED(res)) return res;
3157
res = mHTMLEditor->RemoveContainer(curNode);
3158
if (NS_FAILED(res)) return res;
3159
listCount = arrayOfNodes.Count();
3163
// need to make a list to put things in if we haven't already,
3166
res = SplitAsNeeded(aListType, address_of(curParent), &offset);
3167
if (NS_FAILED(res)) return res;
3168
res = mHTMLEditor->CreateNode(*aListType, curParent, offset, getter_AddRefs(curList));
3169
if (NS_FAILED(res)) return res;
3170
// remember our new block for postprocessing
3171
mNewBlock = curList;
3172
// curList is now the correct thing to put curNode in
3176
// if curNode isn't a list item, we must wrap it in one
3177
nsCOMPtr<nsIDOMNode> listItem;
3178
if (!nsHTMLEditUtils::IsListItem(curNode))
3180
if (IsInlineNode(curNode) && prevListItem)
3182
// this is a continuation of some inline nodes that belong together in
3183
// the same list item. use prevListItem
3184
res = mHTMLEditor->MoveNode(curNode, prevListItem, -1);
3185
if (NS_FAILED(res)) return res;
3189
// don't wrap li around a paragraph. instead replace paragraph with li
3190
if (nsHTMLEditUtils::IsParagraph(curNode))
3192
res = mHTMLEditor->ReplaceContainer(curNode, address_of(listItem), itemType);
3196
res = mHTMLEditor->InsertContainerAbove(curNode, address_of(listItem), itemType);
3198
if (NS_FAILED(res)) return res;
3199
if (IsInlineNode(curNode))
3200
prevListItem = listItem;
3202
prevListItem = nsnull;
3210
if (listItem) // if we made a new list item, deal with it
3212
// tuck the listItem into the end of the active list
3213
res = mHTMLEditor->MoveNode(listItem, curList, -1);
3214
if (NS_FAILED(res)) return res;
3223
nsHTMLEditRules::WillRemoveList(nsISelection *aSelection,
3228
if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
3229
// initialize out param
3230
*aCancel = PR_FALSE;
3231
*aHandled = PR_TRUE;
3233
nsresult res = NormalizeSelection(aSelection);
3234
if (NS_FAILED(res)) return res;
3235
nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
3237
nsCOMArray<nsIDOMRange> arrayOfRanges;
3238
res = GetPromotedRanges(aSelection, arrayOfRanges, kMakeList);
3239
if (NS_FAILED(res)) return res;
3241
// use these ranges to contruct a list of nodes to act on.
3242
nsCOMArray<nsIDOMNode> arrayOfNodes;
3243
res = GetListActionNodes(arrayOfNodes, PR_FALSE);
3244
if (NS_FAILED(res)) return res;
3246
// Remove all non-editable nodes. Leave them be.
3247
PRInt32 listCount = arrayOfNodes.Count();
3249
for (i=listCount-1; i>=0; i--)
3251
nsIDOMNode* testNode = arrayOfNodes[i];
3252
if (!mHTMLEditor->IsEditable(testNode))
3254
arrayOfNodes.RemoveObjectAt(i);
3259
listCount = arrayOfNodes.Count();
3261
// Only act on lists or list items in the array
3262
nsCOMPtr<nsIDOMNode> curParent;
3263
for (i=0; i<listCount; i++)
3265
// here's where we actually figure out what to do
3266
nsIDOMNode* curNode = arrayOfNodes[i];
3268
res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset);
3269
if (NS_FAILED(res)) return res;
3271
if (nsHTMLEditUtils::IsListItem(curNode)) // unlist this listitem
3276
res = PopListItem(curNode, &bOutOfList);
3277
if (NS_FAILED(res)) return res;
3278
} while (!bOutOfList); // keep popping it out until it's not in a list anymore
3280
else if (nsHTMLEditUtils::IsList(curNode)) // node is a list, move list items out
3282
res = RemoveListStructure(curNode);
3283
if (NS_FAILED(res)) return res;
3291
nsHTMLEditRules::WillMakeDefListItem(nsISelection *aSelection,
3292
const nsAString *aItemType,
3297
// for now we let WillMakeList handle this
3298
NS_NAMED_LITERAL_STRING(listType, "dl");
3299
return WillMakeList(aSelection, &listType, aEntireList, nsnull, aCancel, aHandled, aItemType);
3303
nsHTMLEditRules::WillMakeBasicBlock(nsISelection *aSelection,
3304
const nsAString *aBlockType,
3308
if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
3309
// initialize out param
3310
*aCancel = PR_FALSE;
3311
*aHandled = PR_FALSE;
3313
nsresult res = WillInsert(aSelection, aCancel);
3314
if (NS_FAILED(res)) return res;
3315
// initialize out param
3316
// we want to ignore result of WillInsert()
3317
*aCancel = PR_FALSE;
3318
res = NormalizeSelection(aSelection);
3319
if (NS_FAILED(res)) return res;
3320
nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
3321
nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
3322
*aHandled = PR_TRUE;
3323
nsString tString(*aBlockType);
3325
// contruct a list of nodes to act on.
3326
nsCOMArray<nsIDOMNode> arrayOfNodes;
3327
res = GetNodesFromSelection(aSelection, kMakeBasicBlock, arrayOfNodes);
3328
if (NS_FAILED(res)) return res;
3330
// Remove all non-editable nodes. Leave them be.
3331
PRInt32 listCount = arrayOfNodes.Count();
3333
for (i=listCount-1; i>=0; i--)
3335
if (!mHTMLEditor->IsEditable(arrayOfNodes[i]))
3337
arrayOfNodes.RemoveObjectAt(i);
3342
listCount = arrayOfNodes.Count();
3344
// if nothing visible in list, make an empty block
3345
if (ListIsEmptyLine(arrayOfNodes))
3347
nsCOMPtr<nsIDOMNode> parent, theBlock;
3350
// get selection location
3351
res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(parent), &offset);
3352
if (NS_FAILED(res)) return res;
3353
if (tString.Equals(NS_LITERAL_STRING("normal")) ||
3354
tString.IsEmpty() ) // we are removing blocks (going to "body text")
3356
nsCOMPtr<nsIDOMNode> curBlock = parent;
3357
if (!IsBlockNode(curBlock))
3358
curBlock = mHTMLEditor->GetBlockNodeParent(parent);
3359
nsCOMPtr<nsIDOMNode> curBlockPar;
3360
if (!curBlock) return NS_ERROR_NULL_POINTER;
3361
curBlock->GetParentNode(getter_AddRefs(curBlockPar));
3362
if (nsHTMLEditUtils::IsFormatNode(curBlock))
3364
// if the first editable node after selection is a br, consume it. Otherwise
3365
// it gets pushed into a following block after the split, which is visually bad.
3366
nsCOMPtr<nsIDOMNode> brNode;
3367
res = mHTMLEditor->GetNextHTMLNode(parent, offset, address_of(brNode));
3368
if (NS_FAILED(res)) return res;
3369
if (brNode && nsTextEditUtils::IsBreak(brNode))
3371
res = mHTMLEditor->DeleteNode(brNode);
3372
if (NS_FAILED(res)) return res;
3375
res = mHTMLEditor->SplitNodeDeep(curBlock, parent, offset, &offset, PR_TRUE);
3376
if (NS_FAILED(res)) return res;
3377
// put a br at the split point
3378
res = mHTMLEditor->CreateBR(curBlockPar, offset, address_of(brNode));
3379
if (NS_FAILED(res)) return res;
3380
// put selection at the split point
3381
res = aSelection->Collapse(curBlockPar, offset);
3382
selectionResetter.Abort(); // to prevent selection reseter from overriding us.
3383
*aHandled = PR_TRUE;
3385
// else nothing to do!
3387
else // we are making a block
3389
// consume a br, if needed
3390
nsCOMPtr<nsIDOMNode> brNode;
3391
res = mHTMLEditor->GetNextHTMLNode(parent, offset, address_of(brNode), PR_TRUE);
3392
if (NS_FAILED(res)) return res;
3393
if (brNode && nsTextEditUtils::IsBreak(brNode))
3395
res = mHTMLEditor->DeleteNode(brNode);
3396
if (NS_FAILED(res)) return res;
3398
// make sure we can put a block here
3399
res = SplitAsNeeded(aBlockType, address_of(parent), &offset);
3400
if (NS_FAILED(res)) return res;
3401
res = mHTMLEditor->CreateNode(*aBlockType, parent, offset, getter_AddRefs(theBlock));
3402
if (NS_FAILED(res)) return res;
3403
// remember our new block for postprocessing
3404
mNewBlock = theBlock;
3405
// delete anything that was in the list of nodes
3406
nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[0];
3409
res = mHTMLEditor->DeleteNode(curNode);
3410
if (NS_FAILED(res)) return res;
3411
res = arrayOfNodes.RemoveObjectAt(0);
3412
if (NS_FAILED(res)) return res;
3413
curNode = arrayOfNodes[0];
3415
// put selection in new block
3416
res = aSelection->Collapse(theBlock,0);
3417
selectionResetter.Abort(); // to prevent selection reseter from overriding us.
3418
*aHandled = PR_TRUE;
3424
// Ok, now go through all the nodes and make the right kind of blocks,
3425
// or whatever is approriate. Wohoo!
3426
// Note: blockquote is handled a little differently
3427
if (tString.Equals(NS_LITERAL_STRING("blockquote")))
3428
res = MakeBlockquote(arrayOfNodes);
3429
else if (tString.Equals(NS_LITERAL_STRING("normal")) ||
3431
res = RemoveBlockStyle(arrayOfNodes);
3433
res = ApplyBlockStyle(arrayOfNodes, aBlockType);
3440
nsHTMLEditRules::DidMakeBasicBlock(nsISelection *aSelection,
3441
nsRulesInfo *aInfo, nsresult aResult)
3443
if (!aSelection) return NS_ERROR_NULL_POINTER;
3444
// check for empty block. if so, put a moz br in it.
3446
nsresult res = aSelection->GetIsCollapsed(&isCollapsed);
3447
if (NS_FAILED(res)) return res;
3448
if (!isCollapsed) return NS_OK;
3450
nsCOMPtr<nsIDOMNode> parent;
3452
res = nsEditor::GetStartNodeAndOffset(aSelection, address_of(parent), &offset);
3453
if (NS_FAILED(res)) return res;
3454
res = InsertMozBRIfNeeded(parent);
3459
nsHTMLEditRules::WillIndent(nsISelection *aSelection, PRBool *aCancel, PRBool * aHandled)
3463
mHTMLEditor->GetIsCSSEnabled(&useCSS);
3466
res = WillCSSIndent(aSelection, aCancel, aHandled);
3469
res = WillHTMLIndent(aSelection, aCancel, aHandled);
3475
nsHTMLEditRules::WillCSSIndent(nsISelection *aSelection, PRBool *aCancel, PRBool * aHandled)
3477
if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
3479
nsresult res = WillInsert(aSelection, aCancel);
3480
if (NS_FAILED(res)) return res;
3482
// initialize out param
3483
// we want to ignore result of WillInsert()
3484
*aCancel = PR_FALSE;
3485
*aHandled = PR_TRUE;
3487
res = NormalizeSelection(aSelection);
3488
if (NS_FAILED(res)) return res;
3489
nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
3490
nsCOMArray<nsIDOMRange> arrayOfRanges;
3491
nsCOMArray<nsIDOMNode> arrayOfNodes;
3493
// short circuit: detect case of collapsed selection inside an <li>.
3494
// just sublist that <li>. This prevents bug 97797.
3497
nsCOMPtr<nsIDOMNode> liNode;
3498
res = aSelection->GetIsCollapsed(&bCollapsed);
3499
if (NS_FAILED(res)) return res;
3502
nsCOMPtr<nsIDOMNode> node, block;
3504
nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(node), &offset);
3505
if (NS_FAILED(res)) return res;
3506
if (IsBlockNode(node))
3509
block = mHTMLEditor->GetBlockNodeParent(node);
3510
if (block && nsHTMLEditUtils::IsListItem(block))
3516
arrayOfNodes.AppendObject(liNode);
3520
// convert the selection ranges into "promoted" selection ranges:
3521
// this basically just expands the range to include the immediate
3522
// block parent, and then further expands to include any ancestors
3523
// whose children are all in the range
3524
res = GetNodesFromSelection(aSelection, kIndent, arrayOfNodes);
3525
if (NS_FAILED(res)) return res;
3528
NS_NAMED_LITERAL_STRING(quoteType, "blockquote");
3529
// if nothing visible in list, make an empty block
3530
if (ListIsEmptyLine(arrayOfNodes))
3532
nsCOMPtr<nsIDOMNode> parent, theBlock;
3534
nsAutoString quoteType(NS_LITERAL_STRING("div"));
3535
// get selection location
3536
res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(parent), &offset);
3537
if (NS_FAILED(res)) return res;
3538
// make sure we can put a block here
3539
res = SplitAsNeeded("eType, address_of(parent), &offset);
3540
if (NS_FAILED(res)) return res;
3541
res = mHTMLEditor->CreateNode(quoteType, parent, offset, getter_AddRefs(theBlock));
3542
if (NS_FAILED(res)) return res;
3543
// remember our new block for postprocessing
3544
mNewBlock = theBlock;
3545
RelativeChangeIndentationOfElementNode(theBlock, +1);
3546
// delete anything that was in the list of nodes
3547
nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[0];
3550
res = mHTMLEditor->DeleteNode(curNode);
3551
if (NS_FAILED(res)) return res;
3552
res = arrayOfNodes.RemoveObjectAt(0);
3553
if (NS_FAILED(res)) return res;
3554
curNode = arrayOfNodes[0];
3556
// put selection in new block
3557
res = aSelection->Collapse(theBlock,0);
3558
selectionResetter.Abort(); // to prevent selection reseter from overriding us.
3559
*aHandled = PR_TRUE;
3562
// Next we detect all the transitions in the array, where a transition
3563
// means that adjacent nodes in the array don't have the same parent.
3565
nsVoidArray transitionList;
3566
res = MakeTransitionList(arrayOfNodes, transitionList);
3567
if (NS_FAILED(res)) return res;
3569
// Ok, now go through all the nodes and put them in a blockquote,
3570
// or whatever is appropriate. Wohoo!
3572
nsCOMPtr<nsIDOMNode> curParent;
3573
nsCOMPtr<nsIDOMNode> curQuote;
3574
nsCOMPtr<nsIDOMNode> curList;
3575
PRInt32 listCount = arrayOfNodes.Count();
3576
for (i=0; i<listCount; i++)
3578
// here's where we actually figure out what to do
3579
nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
3581
// Ignore all non-editable nodes. Leave them be.
3582
if (!mHTMLEditor->IsEditable(curNode)) continue;
3585
res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset);
3586
if (NS_FAILED(res)) return res;
3588
// some logic for putting list items into nested lists...
3589
if (nsHTMLEditUtils::IsList(curParent))
3591
if (!curList || transitionList[i])
3593
nsAutoString listTag;
3594
nsEditor::GetTagString(curParent,listTag);
3595
ToLowerCase(listTag);
3596
// create a new nested list of correct type
3597
res = SplitAsNeeded(&listTag, address_of(curParent), &offset);
3598
if (NS_FAILED(res)) return res;
3599
res = mHTMLEditor->CreateNode(listTag, curParent, offset, getter_AddRefs(curList));
3600
if (NS_FAILED(res)) return res;
3601
// curList is now the correct thing to put curNode in
3602
// remember our new block for postprocessing
3603
mNewBlock = curList;
3605
// tuck the node into the end of the active list
3607
res = mHTMLEditor->GetLengthOfDOMNode(curList, listLen);
3608
if (NS_FAILED(res)) return res;
3609
res = mHTMLEditor->MoveNode(curNode, curList, listLen);
3610
if (NS_FAILED(res)) return res;
3613
else // not a list item
3615
if (IsBlockNode(curNode)) {
3616
RelativeChangeIndentationOfElementNode(curNode, +1);
3620
if (!curQuote) // || transitionList[i])
3622
NS_NAMED_LITERAL_STRING(divquoteType, "div");
3623
res = SplitAsNeeded(&divquoteType, address_of(curParent), &offset);
3624
if (NS_FAILED(res)) return res;
3625
res = mHTMLEditor->CreateNode(divquoteType, curParent, offset, getter_AddRefs(curQuote));
3626
if (NS_FAILED(res)) return res;
3627
RelativeChangeIndentationOfElementNode(curQuote, +1);
3628
// remember our new block for postprocessing
3629
mNewBlock = curQuote;
3630
// curQuote is now the correct thing to put curNode in
3633
// tuck the node into the end of the active blockquote
3635
res = mHTMLEditor->GetLengthOfDOMNode(curQuote, quoteLen);
3636
if (NS_FAILED(res)) return res;
3637
res = mHTMLEditor->MoveNode(curNode, curQuote, quoteLen);
3638
if (NS_FAILED(res)) return res;
3646
nsHTMLEditRules::WillHTMLIndent(nsISelection *aSelection, PRBool *aCancel, PRBool * aHandled)
3648
if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
3649
nsresult res = WillInsert(aSelection, aCancel);
3650
if (NS_FAILED(res)) return res;
3652
// initialize out param
3653
// we want to ignore result of WillInsert()
3654
*aCancel = PR_FALSE;
3655
*aHandled = PR_TRUE;
3657
res = NormalizeSelection(aSelection);
3658
if (NS_FAILED(res)) return res;
3659
nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
3661
// convert the selection ranges into "promoted" selection ranges:
3662
// this basically just expands the range to include the immediate
3663
// block parent, and then further expands to include any ancestors
3664
// whose children are all in the range
3666
nsCOMArray<nsIDOMRange> arrayOfRanges;
3667
res = GetPromotedRanges(aSelection, arrayOfRanges, kIndent);
3668
if (NS_FAILED(res)) return res;
3670
// use these ranges to contruct a list of nodes to act on.
3671
nsCOMArray<nsIDOMNode> arrayOfNodes;
3672
res = GetNodesForOperation(arrayOfRanges, arrayOfNodes, kIndent);
3673
if (NS_FAILED(res)) return res;
3675
NS_NAMED_LITERAL_STRING(quoteType, "blockquote");
3677
// if nothing visible in list, make an empty block
3678
if (ListIsEmptyLine(arrayOfNodes))
3680
nsCOMPtr<nsIDOMNode> parent, theBlock;
3683
// get selection location
3684
res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(parent), &offset);
3685
if (NS_FAILED(res)) return res;
3686
// make sure we can put a block here
3687
res = SplitAsNeeded("eType, address_of(parent), &offset);
3688
if (NS_FAILED(res)) return res;
3689
res = mHTMLEditor->CreateNode(quoteType, parent, offset, getter_AddRefs(theBlock));
3690
if (NS_FAILED(res)) return res;
3691
// remember our new block for postprocessing
3692
mNewBlock = theBlock;
3693
// delete anything that was in the list of nodes
3694
nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[0];
3697
res = mHTMLEditor->DeleteNode(curNode);
3698
if (NS_FAILED(res)) return res;
3699
res = arrayOfNodes.RemoveObjectAt(0);
3700
if (NS_FAILED(res)) return res;
3701
curNode = arrayOfNodes[0];
3703
// put selection in new block
3704
res = aSelection->Collapse(theBlock,0);
3705
selectionResetter.Abort(); // to prevent selection reseter from overriding us.
3706
*aHandled = PR_TRUE;
3710
// Ok, now go through all the nodes and put them in a blockquote,
3711
// or whatever is appropriate. Wohoo!
3713
nsCOMPtr<nsIDOMNode> curParent, curQuote, curList, indentedLI, sibling;
3714
PRInt32 listCount = arrayOfNodes.Count();
3715
for (i=0; i<listCount; i++)
3717
// here's where we actually figure out what to do
3718
nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
3720
// Ignore all non-editable nodes. Leave them be.
3721
if (!mHTMLEditor->IsEditable(curNode)) continue;
3724
res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset);
3725
if (NS_FAILED(res)) return res;
3727
// some logic for putting list items into nested lists...
3728
if (nsHTMLEditUtils::IsList(curParent))
3730
// check to see if curList is still appropriate. Which it is if
3731
// curNode is still right after it in the same list.
3735
mHTMLEditor->GetPriorHTMLSibling(curNode, address_of(sibling));
3738
if (!curList || (sibling && sibling != curList) )
3740
nsAutoString listTag;
3741
nsEditor::GetTagString(curParent,listTag);
3742
ToLowerCase(listTag);
3743
// create a new nested list of correct type
3744
res = SplitAsNeeded(&listTag, address_of(curParent), &offset);
3745
if (NS_FAILED(res)) return res;
3746
res = mHTMLEditor->CreateNode(listTag, curParent, offset, getter_AddRefs(curList));
3747
if (NS_FAILED(res)) return res;
3748
// curList is now the correct thing to put curNode in
3749
// remember our new block for postprocessing
3750
mNewBlock = curList;
3752
// tuck the node into the end of the active list
3753
res = mHTMLEditor->MoveNode(curNode, curList, -1);
3754
if (NS_FAILED(res)) return res;
3755
// forget curQuote, if any
3759
else // not a list item, use blockquote?
3761
// if we are inside a list item, we dont want to blockquote, we want
3762
// to sublist the list item. We may have several nodes listed in the
3763
// array of nodes to act on, that are in the same list item. Since
3764
// we only want to indent that li once, we must keep track of the most
3765
// recent indented list item, and not indent it if we find another node
3766
// to act on that is still inside the same li.
3767
nsCOMPtr<nsIDOMNode> listitem=IsInListItem(curNode);
3770
if (indentedLI == listitem) continue; // already indented this list item
3771
res = nsEditor::GetNodeLocation(listitem, address_of(curParent), &offset);
3772
if (NS_FAILED(res)) return res;
3773
// check to see if curList is still appropriate. Which it is if
3774
// curNode is still right after it in the same list.
3778
mHTMLEditor->GetPriorHTMLSibling(curNode, address_of(sibling));
3781
if (!curList || (sibling && sibling != curList) )
3783
nsAutoString listTag;
3784
nsEditor::GetTagString(curParent,listTag);
3785
ToLowerCase(listTag);
3786
// create a new nested list of correct type
3787
res = SplitAsNeeded(&listTag, address_of(curParent), &offset);
3788
if (NS_FAILED(res)) return res;
3789
res = mHTMLEditor->CreateNode(listTag, curParent, offset, getter_AddRefs(curList));
3790
if (NS_FAILED(res)) return res;
3792
res = mHTMLEditor->MoveNode(listitem, curList, -1);
3793
if (NS_FAILED(res)) return res;
3794
// remember we indented this li
3795
indentedLI = listitem;
3800
// need to make a blockquote to put things in if we haven't already,
3801
// or if this node doesn't go in blockquote we used earlier.
3802
// One reason it might not go in prio blockquote is if we are now
3803
// in a different table cell.
3806
PRBool bInDifTblElems;
3807
res = InDifferentTableElements(curQuote, curNode, &bInDifTblElems);
3808
if (NS_FAILED(res)) return res;
3815
res = SplitAsNeeded("eType, address_of(curParent), &offset);
3816
if (NS_FAILED(res)) return res;
3817
res = mHTMLEditor->CreateNode(quoteType, curParent, offset, getter_AddRefs(curQuote));
3818
if (NS_FAILED(res)) return res;
3819
// remember our new block for postprocessing
3820
mNewBlock = curQuote;
3821
// curQuote is now the correct thing to put curNode in
3824
// tuck the node into the end of the active blockquote
3825
res = mHTMLEditor->MoveNode(curNode, curQuote, -1);
3826
if (NS_FAILED(res)) return res;
3827
// forget curList, if any
3837
nsHTMLEditRules::WillOutdent(nsISelection *aSelection, PRBool *aCancel, PRBool *aHandled)
3839
if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
3840
// initialize out param
3841
*aCancel = PR_FALSE;
3842
*aHandled = PR_TRUE;
3843
nsresult res = NS_OK;
3844
nsCOMPtr<nsIDOMNode> rememberedLeftBQ, rememberedRightBQ;
3846
mHTMLEditor->GetIsCSSEnabled(&useCSS);
3848
res = NormalizeSelection(aSelection);
3849
if (NS_FAILED(res)) return res;
3850
// some scoping for selection resetting - we may need to tweak it
3852
nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
3854
// convert the selection ranges into "promoted" selection ranges:
3855
// this basically just expands the range to include the immediate
3856
// block parent, and then further expands to include any ancestors
3857
// whose children are all in the range
3858
nsCOMArray<nsIDOMNode> arrayOfNodes;
3859
res = GetNodesFromSelection(aSelection, kOutdent, arrayOfNodes);
3860
if (NS_FAILED(res)) return res;
3862
// Ok, now go through all the nodes and remove a level of blockquoting,
3863
// or whatever is appropriate. Wohoo!
3865
nsCOMPtr<nsIDOMNode> curBlockQuote, firstBQChild, lastBQChild;
3866
PRBool curBlockQuoteIsIndentedWithCSS = PR_FALSE;
3867
PRInt32 listCount = arrayOfNodes.Count();
3869
nsCOMPtr<nsIDOMNode> curParent;
3870
for (i=0; i<listCount; i++)
3872
// here's where we actually figure out what to do
3873
nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
3875
res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset);
3876
if (NS_FAILED(res)) return res;
3878
// is it a blockquote?
3879
if (nsHTMLEditUtils::IsBlockquote(curNode))
3881
// if it is a blockquote, remove it.
3882
// So we need to finish up dealng with any curBlockQuote first.
3885
res = OutdentPartOfBlock(curBlockQuote, firstBQChild, lastBQChild,
3886
curBlockQuoteIsIndentedWithCSS,
3887
address_of(rememberedLeftBQ),
3888
address_of(rememberedRightBQ));
3889
if (NS_FAILED(res)) return res;
3890
curBlockQuote = 0; firstBQChild = 0; lastBQChild = 0;
3891
curBlockQuoteIsIndentedWithCSS = PR_FALSE;
3893
res = mHTMLEditor->RemoveBlockContainer(curNode);
3894
if (NS_FAILED(res)) return res;
3897
// is it a list item?
3898
if (nsHTMLEditUtils::IsListItem(curNode))
3900
// if it is a list item, that means we are not outdenting whole list.
3901
// So we need to finish up dealng with any curBlockQuote, and then
3902
// pop this list item.
3905
res = OutdentPartOfBlock(curBlockQuote, firstBQChild, lastBQChild,
3906
curBlockQuoteIsIndentedWithCSS,
3907
address_of(rememberedLeftBQ),
3908
address_of(rememberedRightBQ));
3909
if (NS_FAILED(res)) return res;
3910
curBlockQuote = 0; firstBQChild = 0; lastBQChild = 0;
3911
curBlockQuoteIsIndentedWithCSS = PR_FALSE;
3914
res = PopListItem(curNode, &bOutOfList);
3915
if (NS_FAILED(res)) return res;
3918
// do we have a blockquote that we are already committed to removing?
3921
// if so, is this node a descendant?
3922
if (nsEditorUtils::IsDescendantOf(curNode, curBlockQuote))
3924
lastBQChild = curNode;
3925
continue; // then we dont need to do anything different for this node
3929
// otherwise, we have progressed beyond end of curBlockQuote,
3930
// so lets handle it now. We need to remove the portion of
3931
// curBlockQuote that contains [firstBQChild - lastBQChild].
3932
res = OutdentPartOfBlock(curBlockQuote, firstBQChild, lastBQChild,
3933
curBlockQuoteIsIndentedWithCSS,
3934
address_of(rememberedLeftBQ),
3935
address_of(rememberedRightBQ));
3936
if (NS_FAILED(res)) return res;
3937
curBlockQuote = 0; firstBQChild = 0; lastBQChild = 0;
3938
curBlockQuoteIsIndentedWithCSS = PR_FALSE;
3939
// fall out and handle curNode
3943
// are we inside a blockquote?
3944
nsCOMPtr<nsIDOMNode> n = curNode;
3945
nsCOMPtr<nsIDOMNode> tmp;
3946
curBlockQuoteIsIndentedWithCSS = PR_FALSE;
3947
// keep looking up the heirarchy as long as we dont hit the body or a table element
3948
// (other than an entire table)
3949
while (!nsTextEditUtils::IsBody(n) &&
3950
(nsHTMLEditUtils::IsTable(n) || !nsHTMLEditUtils::IsTableElement(n)))
3952
n->GetParentNode(getter_AddRefs(tmp));
3954
if (nsHTMLEditUtils::IsBlockquote(n))
3956
// if so, remember it, and remember first node we are taking out of it.
3958
firstBQChild = curNode;
3959
lastBQChild = curNode;
3965
mHTMLEditor->mHTMLCSSUtils->GetSpecifiedProperty(n, nsEditProperty::cssMarginLeft, value);
3968
mHTMLEditor->mHTMLCSSUtils->ParseLength(value, &f, &unit);
3972
firstBQChild = curNode;
3973
lastBQChild = curNode;
3974
curBlockQuoteIsIndentedWithCSS = PR_TRUE;
3982
// could not find an enclosing blockquote for this node. handle list cases.
3983
if (nsHTMLEditUtils::IsList(curParent)) // move node out of list
3985
if (nsHTMLEditUtils::IsList(curNode)) // just unwrap this sublist
3987
res = mHTMLEditor->RemoveBlockContainer(curNode);
3988
if (NS_FAILED(res)) return res;
3990
// handled list item case above
3992
else if (!useCSS && nsHTMLEditUtils::IsList(curNode)) // node is a list, but parent is non-list: move list items out
3994
nsCOMPtr<nsIDOMNode> child;
3995
curNode->GetLastChild(getter_AddRefs(child));
3998
if (nsHTMLEditUtils::IsListItem(child))
4001
res = PopListItem(child, &bOutOfList);
4002
if (NS_FAILED(res)) return res;
4004
else if (nsHTMLEditUtils::IsList(child))
4006
// We have an embedded list, so move it out from under the
4007
// parent list. Be sure to put it after the parent list
4008
// because this loop iterates backwards through the parent's
4009
// list of children.
4011
res = mHTMLEditor->MoveNode(child, curParent, offset + 1);
4012
if (NS_FAILED(res)) return res;
4016
// delete any non- list items for now
4017
res = mHTMLEditor->DeleteNode(child);
4018
if (NS_FAILED(res)) return res;
4020
curNode->GetLastChild(getter_AddRefs(child));
4022
// delete the now-empty list
4023
res = mHTMLEditor->RemoveBlockContainer(curNode);
4024
if (NS_FAILED(res)) return res;
4027
RelativeChangeIndentationOfElementNode(curNode, -1);
4033
// we have a blockquote we haven't finished handling
4034
res = OutdentPartOfBlock(curBlockQuote, firstBQChild, lastBQChild,
4035
curBlockQuoteIsIndentedWithCSS,
4036
address_of(rememberedLeftBQ),
4037
address_of(rememberedRightBQ));
4038
if (NS_FAILED(res)) return res;
4041
// make sure selection didn't stick to last piece of content in old bq
4042
// (only a problem for collapsed selections)
4043
if (rememberedLeftBQ || rememberedRightBQ)
4046
res = aSelection->GetIsCollapsed(&bCollapsed);
4049
// push selection past end of rememberedLeftBQ
4050
nsCOMPtr<nsIDOMNode> sNode;
4052
mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(sNode), &sOffset);
4053
if (rememberedLeftBQ &&
4054
((sNode == rememberedLeftBQ) || nsEditorUtils::IsDescendantOf(sNode, rememberedLeftBQ)))
4056
// selection is inside rememberedLeftBQ - push it past it.
4057
nsEditor::GetNodeLocation(rememberedLeftBQ, address_of(sNode), &sOffset);
4059
aSelection->Collapse(sNode, sOffset);
4061
// and pull selection before beginning of rememberedRightBQ
4062
mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(sNode), &sOffset);
4063
if (rememberedRightBQ &&
4064
((sNode == rememberedRightBQ) || nsEditorUtils::IsDescendantOf(sNode, rememberedRightBQ)))
4066
// selection is inside rememberedRightBQ - push it before it.
4067
nsEditor::GetNodeLocation(rememberedRightBQ, address_of(sNode), &sOffset);
4068
aSelection->Collapse(sNode, sOffset);
4076
///////////////////////////////////////////////////////////////////////////
4077
// RemovePartOfBlock: split aBlock and move aStartChild to aEndChild out
4078
// of aBlock. return left side of block (if any) in
4079
// aLeftNode. return right side of block (if any) in
4083
nsHTMLEditRules::RemovePartOfBlock(nsIDOMNode *aBlock,
4084
nsIDOMNode *aStartChild,
4085
nsIDOMNode *aEndChild,
4086
nsCOMPtr<nsIDOMNode> *aLeftNode,
4087
nsCOMPtr<nsIDOMNode> *aRightNode)
4089
nsCOMPtr<nsIDOMNode> middleNode;
4090
nsresult res = SplitBlock(aBlock, aStartChild, aEndChild,
4091
aLeftNode, aRightNode,
4092
address_of(middleNode));
4093
if (NS_FAILED(res)) return res;
4094
// get rid of part of blockquote we are outdenting
4096
return mHTMLEditor->RemoveBlockContainer(aBlock);
4100
nsHTMLEditRules::SplitBlock(nsIDOMNode *aBlock,
4101
nsIDOMNode *aStartChild,
4102
nsIDOMNode *aEndChild,
4103
nsCOMPtr<nsIDOMNode> *aLeftNode,
4104
nsCOMPtr<nsIDOMNode> *aRightNode,
4105
nsCOMPtr<nsIDOMNode> *aMiddleNode)
4107
if (!aBlock || !aStartChild || !aEndChild)
4108
return NS_ERROR_NULL_POINTER;
4110
nsCOMPtr<nsIDOMNode> startParent, endParent, leftNode, rightNode;
4111
PRInt32 startOffset, endOffset, offset;
4114
// get split point location
4115
res = nsEditor::GetNodeLocation(aStartChild, address_of(startParent), &startOffset);
4116
if (NS_FAILED(res)) return res;
4119
res = mHTMLEditor->SplitNodeDeep(aBlock, startParent, startOffset, &offset,
4120
PR_TRUE, address_of(leftNode), address_of(rightNode));
4121
if (NS_FAILED(res)) return res;
4122
if (rightNode) aBlock = rightNode;
4124
// remember left portion of block if caller requested
4126
*aLeftNode = leftNode;
4128
// get split point location
4129
res = nsEditor::GetNodeLocation(aEndChild, address_of(endParent), &endOffset);
4130
if (NS_FAILED(res)) return res;
4131
endOffset++; // want to be after lastBQChild
4134
res = mHTMLEditor->SplitNodeDeep(aBlock, endParent, endOffset, &offset,
4135
PR_TRUE, address_of(leftNode), address_of(rightNode));
4136
if (NS_FAILED(res)) return res;
4137
if (leftNode) aBlock = leftNode;
4139
// remember right portion of block if caller requested
4141
*aRightNode = rightNode;
4144
*aMiddleNode = aBlock;
4150
nsHTMLEditRules::OutdentPartOfBlock(nsIDOMNode *aBlock,
4151
nsIDOMNode *aStartChild,
4152
nsIDOMNode *aEndChild,
4153
PRBool aIsBlockIndentedWithCSS,
4154
nsCOMPtr<nsIDOMNode> *aLeftNode,
4155
nsCOMPtr<nsIDOMNode> *aRightNode)
4157
nsCOMPtr<nsIDOMNode> middleNode;
4158
nsresult res = SplitBlock(aBlock, aStartChild, aEndChild,
4161
address_of(middleNode));
4162
if (NS_FAILED(res)) return res;
4163
if (aIsBlockIndentedWithCSS)
4164
res = RelativeChangeIndentationOfElementNode(middleNode, -1);
4166
res = mHTMLEditor->RemoveBlockContainer(middleNode);
4170
///////////////////////////////////////////////////////////////////////////
4171
// ConvertListType: convert list type and list item type.
4175
nsHTMLEditRules::ConvertListType(nsIDOMNode *aList,
4176
nsCOMPtr<nsIDOMNode> *outList,
4177
const nsAString& aListType,
4178
const nsAString& aItemType)
4180
if (!aList || !outList) return NS_ERROR_NULL_POINTER;
4181
*outList = aList; // we might not need to change the list
4182
nsresult res = NS_OK;
4183
nsCOMPtr<nsIDOMNode> child, temp;
4184
aList->GetFirstChild(getter_AddRefs(child));
4187
if (nsHTMLEditUtils::IsListItem(child) && !nsEditor::NodeIsTypeString(child, aItemType))
4189
res = mHTMLEditor->ReplaceContainer(child, address_of(temp), aItemType);
4190
if (NS_FAILED(res)) return res;
4193
else if (nsHTMLEditUtils::IsList(child) && !nsEditor::NodeIsTypeString(child, aListType))
4195
res = ConvertListType(child, address_of(temp), aListType, aItemType);
4196
if (NS_FAILED(res)) return res;
4199
child->GetNextSibling(getter_AddRefs(temp));
4202
if (!nsEditor::NodeIsTypeString(aList, aListType))
4204
res = mHTMLEditor->ReplaceContainer(aList, outList, aListType);
4210
///////////////////////////////////////////////////////////////////////////
4211
// CreateStyleForInsertText: take care of clearing and setting appropriate
4212
// style nodes for text insertion.
4216
nsHTMLEditRules::CreateStyleForInsertText(nsISelection *aSelection, nsIDOMDocument *aDoc)
4218
if (!aSelection || !aDoc) return NS_ERROR_NULL_POINTER;
4219
if (!mHTMLEditor->mTypeInState) return NS_ERROR_NULL_POINTER;
4221
PRBool weDidSometing = PR_FALSE;
4222
nsCOMPtr<nsIDOMNode> node, tmp;
4224
nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(node), &offset);
4225
if (NS_FAILED(res)) return res;
4226
PropItem *item = nsnull;
4228
// if we deleted selection then also for cached styles
4229
if (mDidDeleteSelection &&
4230
((mTheAction == nsEditor::kOpInsertText ) ||
4231
(mTheAction == nsEditor::kOpInsertIMEText) ||
4232
(mTheAction == nsEditor::kOpInsertBreak) ||
4233
(mTheAction == nsEditor::kOpDeleteSelection)))
4235
res = ReapplyCachedStyles();
4236
if (NS_FAILED(res)) return res;
4238
// either way we clear the cached styles array
4239
res = ClearCachedStyles();
4240
if (NS_FAILED(res)) return res;
4242
// next examine our present style and make sure default styles are either present or
4243
// explicitly overridden. If neither, add the default style to the TypeInState
4244
PRInt32 j, defcon = mHTMLEditor->mDefaultStyles.Count();
4245
for (j=0; j<defcon; j++)
4247
PropItem *propItem = (PropItem*)mHTMLEditor->mDefaultStyles[j];
4249
return NS_ERROR_NULL_POINTER;
4250
PRBool bFirst, bAny, bAll;
4252
// GetInlineProperty also examine TypeInState. The only gotcha here is that a cleared
4253
// property looks like an unset property. For now I'm assuming that's not a problem:
4254
// that default styles will always be multivalue styles (like font face or size) where
4255
// clearing the style means we want to go back to the default. If we ever wanted a
4256
// "toggle" style like bold for a default, though, I'll have to add code to detect the
4257
// difference between unset and explicitly cleared, else user would never be able to
4258
// unbold, for instance.
4259
nsAutoString curValue;
4260
res = mHTMLEditor->GetInlinePropertyBase(propItem->tag, &(propItem->attr), nsnull,
4261
&bFirst, &bAny, &bAll, &curValue, PR_FALSE);
4262
if (NS_FAILED(res)) return res;
4264
if (!bAny) // no style set for this prop/attr
4266
mHTMLEditor->mTypeInState->SetProp(propItem->tag, propItem->attr, propItem->value);
4270
// process clearing any styles first
4271
mHTMLEditor->mTypeInState->TakeClearProperty(&item);
4274
nsCOMPtr<nsIDOMNode> leftNode, rightNode, secondSplitParent, newSelParent, savedBR;
4275
res = mHTMLEditor->SplitStyleAbovePoint(address_of(node), &offset, item->tag, &item->attr, address_of(leftNode), address_of(rightNode));
4276
if (NS_FAILED(res)) return res;
4277
PRBool bIsEmptyNode;
4280
mHTMLEditor->IsEmptyNode(leftNode, &bIsEmptyNode, PR_FALSE, PR_TRUE);
4283
// delete leftNode if it became empty
4284
res = mEditor->DeleteNode(leftNode);
4285
if (NS_FAILED(res)) return res;
4290
secondSplitParent = mHTMLEditor->GetLeftmostChild(rightNode);
4291
// don't try to split non-containers (br's, images, hr's, etc)
4292
if (!secondSplitParent) secondSplitParent = rightNode;
4293
if (!mHTMLEditor->IsContainer(secondSplitParent))
4295
if (nsTextEditUtils::IsBreak(secondSplitParent))
4296
savedBR = secondSplitParent;
4298
secondSplitParent->GetParentNode(getter_AddRefs(tmp));
4299
secondSplitParent = tmp;
4302
res = mHTMLEditor->SplitStyleAbovePoint(address_of(secondSplitParent), &offset, item->tag, &(item->attr), address_of(leftNode), address_of(rightNode));
4303
if (NS_FAILED(res)) return res;
4304
// should be impossible to not get a new leftnode here
4305
if (!leftNode) return NS_ERROR_FAILURE;
4306
newSelParent = mHTMLEditor->GetLeftmostChild(leftNode);
4307
if (!newSelParent) newSelParent = leftNode;
4308
// if rightNode starts with a br, suck it out of right node and into leftNode.
4309
// This is so we you don't revert back to the previous style if you happen to click at the end of a line.
4312
res = mEditor->MoveNode(savedBR, newSelParent, 0);
4313
if (NS_FAILED(res)) return res;
4315
mHTMLEditor->IsEmptyNode(rightNode, &bIsEmptyNode, PR_FALSE, PR_TRUE);
4318
// delete rightNode if it became empty
4319
res = mEditor->DeleteNode(rightNode);
4320
if (NS_FAILED(res)) return res;
4322
// remove the style on this new heirarchy
4323
PRInt32 newSelOffset = 0;
4325
// track the point at the new heirarchy.
4326
// This is so we can know where to put the selection after we call
4327
// RemoveStyleInside(). RemoveStyleInside() could remove any and all of those nodes,
4328
// so I have to use the range tracking system to find the right spot to put selection.
4329
nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, address_of(newSelParent), &newSelOffset);
4330
res = mHTMLEditor->RemoveStyleInside(leftNode, item->tag, &(item->attr), nsnull);
4331
if (NS_FAILED(res)) return res;
4333
// reset our node offset values to the resulting new sel point
4334
node = newSelParent;
4335
offset = newSelOffset;
4337
// we own item now (TakeClearProperty hands ownership to us)
4339
mHTMLEditor->mTypeInState->TakeClearProperty(&item);
4340
weDidSometing = PR_TRUE;
4343
// then process setting any styles
4344
PRInt32 relFontSize;
4346
res = mHTMLEditor->mTypeInState->TakeRelativeFontSize(&relFontSize);
4347
if (NS_FAILED(res)) return res;
4348
res = mHTMLEditor->mTypeInState->TakeSetProperty(&item);
4349
if (NS_FAILED(res)) return res;
4351
if (item || relFontSize) // we have at least one style to add; make a
4352
{ // new text node to insert style nodes above.
4353
if (mHTMLEditor->IsTextNode(node))
4355
// if we are in a text node, split it
4356
res = mHTMLEditor->SplitNodeDeep(node, node, offset, &offset);
4357
if (NS_FAILED(res)) return res;
4358
node->GetParentNode(getter_AddRefs(tmp));
4361
nsCOMPtr<nsIDOMNode> newNode;
4362
nsCOMPtr<nsIDOMText> nodeAsText;
4363
res = aDoc->CreateTextNode(nsAutoString(), getter_AddRefs(nodeAsText));
4364
if (NS_FAILED(res)) return res;
4365
if (!nodeAsText) return NS_ERROR_NULL_POINTER;
4366
newNode = do_QueryInterface(nodeAsText);
4367
res = mHTMLEditor->InsertNode(newNode, node, offset);
4368
if (NS_FAILED(res)) return res;
4371
weDidSometing = PR_TRUE;
4376
// dir indicated bigger versus smaller. 1 = bigger, -1 = smaller
4377
if (relFontSize > 0) dir=1;
4379
for (j=0; j<abs(relFontSize); j++)
4381
res = mHTMLEditor->RelativeFontChangeOnTextNode(dir, nodeAsText, 0, -1);
4382
if (NS_FAILED(res)) return res;
4388
res = mHTMLEditor->SetInlinePropertyOnNode(node, item->tag, &item->attr, &item->value);
4389
if (NS_FAILED(res)) return res;
4390
// we own item now (TakeSetProperty hands ownership to us)
4392
mHTMLEditor->mTypeInState->TakeSetProperty(&item);
4396
return aSelection->Collapse(node, offset);
4402
///////////////////////////////////////////////////////////////////////////
4403
// IsEmptyBlock: figure out if aNode is (or is inside) an empty block.
4404
// A block can have children and still be considered empty,
4405
// if the children are empty or non-editable.
4408
nsHTMLEditRules::IsEmptyBlock(nsIDOMNode *aNode,
4409
PRBool *outIsEmptyBlock,
4410
PRBool aMozBRDoesntCount,
4411
PRBool aListItemsNotEmpty)
4413
if (!aNode || !outIsEmptyBlock) return NS_ERROR_NULL_POINTER;
4414
*outIsEmptyBlock = PR_TRUE;
4416
// nsresult res = NS_OK;
4417
nsCOMPtr<nsIDOMNode> nodeToTest;
4418
if (IsBlockNode(aNode)) nodeToTest = do_QueryInterface(aNode);
4419
// else nsCOMPtr<nsIDOMElement> block;
4420
// looks like I forgot to finish this. Wonder what I was going to do?
4422
if (!nodeToTest) return NS_ERROR_NULL_POINTER;
4423
return mHTMLEditor->IsEmptyNode(nodeToTest, outIsEmptyBlock,
4424
aMozBRDoesntCount, aListItemsNotEmpty);
4429
nsHTMLEditRules::WillAlign(nsISelection *aSelection,
4430
const nsAString *alignType,
4434
if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
4436
nsresult res = WillInsert(aSelection, aCancel);
4437
if (NS_FAILED(res)) return res;
4439
// initialize out param
4440
// we want to ignore result of WillInsert()
4441
*aCancel = PR_FALSE;
4442
*aHandled = PR_FALSE;
4444
res = NormalizeSelection(aSelection);
4445
if (NS_FAILED(res)) return res;
4446
nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
4448
// convert the selection ranges into "promoted" selection ranges:
4449
// this basically just expands the range to include the immediate
4450
// block parent, and then further expands to include any ancestors
4451
// whose children are all in the range
4452
*aHandled = PR_TRUE;
4453
nsCOMArray<nsIDOMNode> arrayOfNodes;
4454
res = GetNodesFromSelection(aSelection, kAlign, arrayOfNodes);
4455
if (NS_FAILED(res)) return res;
4457
// if we don't have any nodes, or we have only a single br, then we are
4458
// creating an empty alignment div. We have to do some different things for these.
4459
PRBool emptyDiv = PR_FALSE;
4460
PRInt32 listCount = arrayOfNodes.Count();
4461
if (!listCount) emptyDiv = PR_TRUE;
4464
nsCOMPtr<nsIDOMNode> theNode = arrayOfNodes[0];
4466
if (nsHTMLEditUtils::SupportsAlignAttr(theNode))
4468
// the node is a table element, an horiz rule, a paragraph, a div
4469
// or a section header; in HTML 4, it can directly carry the ALIGN
4470
// attribute and we don't need to make a div! If we are in CSS mode,
4471
// all the work is done in AlignBlock
4472
nsCOMPtr<nsIDOMElement> theElem = do_QueryInterface(theNode);
4473
res = AlignBlock(theElem, alignType, PR_TRUE);
4474
if (NS_FAILED(res)) return res;
4478
if (nsTextEditUtils::IsBreak(theNode))
4480
// The special case emptyDiv code (below) that consumes BRs can
4481
// cause tables to split if the start node of the selection is
4482
// not in a table cell or caption, for example parent is a <tr>.
4483
// Avoid this unnecessary splitting if possible by leaving emptyDiv
4484
// FALSE so that we fall through to the normal case alignment code.
4486
// XXX: It seems a little error prone for the emptyDiv special
4487
// case code to assume that the start node of the selection
4488
// is the parent of the single node in the arrayOfNodes, as
4489
// the paragraph above points out. Do we rely on the selection
4490
// start node because of the fact that arrayOfNodes can be empty?
4491
// We should probably revisit this issue. - kin
4493
nsCOMPtr<nsIDOMNode> parent;
4495
res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(parent), &offset);
4497
if (!nsHTMLEditUtils::IsTableElement(parent) || nsHTMLEditUtils::IsTableCellOrCaption(parent))
4504
nsCOMPtr<nsIDOMNode> brNode, parent, theDiv, sib;
4505
NS_NAMED_LITERAL_STRING(divType, "div");
4506
res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(parent), &offset);
4507
if (NS_FAILED(res)) return res;
4508
res = SplitAsNeeded(&divType, address_of(parent), &offset);
4509
if (NS_FAILED(res)) return res;
4510
// consume a trailing br, if any. This is to keep an alignment from
4511
// creating extra lines, if possible.
4512
res = mHTMLEditor->GetNextHTMLNode(parent, offset, address_of(brNode));
4513
if (NS_FAILED(res)) return res;
4514
if (brNode && nsTextEditUtils::IsBreak(brNode))
4516
// making use of html structure... if next node after where
4517
// we are putting our div is not a block, then the br we
4518
// found is in same block we are, so its safe to consume it.
4519
res = mHTMLEditor->GetNextHTMLSibling(parent, offset, address_of(sib));
4520
if (NS_FAILED(res)) return res;
4521
if (!IsBlockNode(sib))
4523
res = mHTMLEditor->DeleteNode(brNode);
4524
if (NS_FAILED(res)) return res;
4527
res = mHTMLEditor->CreateNode(divType, parent, offset, getter_AddRefs(theDiv));
4528
if (NS_FAILED(res)) return res;
4529
// remember our new block for postprocessing
4531
// set up the alignment on the div, using HTML or CSS
4532
nsCOMPtr<nsIDOMElement> divElem = do_QueryInterface(theDiv);
4533
res = AlignBlock(divElem, alignType, PR_TRUE);
4534
if (NS_FAILED(res)) return res;
4535
*aHandled = PR_TRUE;
4536
// put in a moz-br so that it won't get deleted
4537
res = CreateMozBR(theDiv, 0, address_of(brNode));
4538
if (NS_FAILED(res)) return res;
4539
res = aSelection->Collapse(theDiv, 0);
4540
selectionResetter.Abort(); // dont reset our selection in this case.
4544
// Next we detect all the transitions in the array, where a transition
4545
// means that adjacent nodes in the array don't have the same parent.
4547
nsVoidArray transitionList;
4548
res = MakeTransitionList(arrayOfNodes, transitionList);
4549
if (NS_FAILED(res)) return res;
4551
// Ok, now go through all the nodes and give them an align attrib or put them in a div,
4552
// or whatever is appropriate. Wohoo!
4555
nsCOMPtr<nsIDOMNode> curParent;
4556
nsCOMPtr<nsIDOMNode> curDiv;
4558
mHTMLEditor->GetIsCSSEnabled(&useCSS);
4559
for (i=0; i<listCount; i++)
4561
// here's where we actually figure out what to do
4562
nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
4564
res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset);
4565
if (NS_FAILED(res)) return res;
4567
// the node is a table element, an horiz rule, a paragraph, a div
4568
// or a section header; in HTML 4, it can directly carry the ALIGN
4569
// attribute and we don't need to nest it, just set the alignment.
4570
// In CSS, assign the corresponding CSS styles in AlignBlock
4571
if (nsHTMLEditUtils::SupportsAlignAttr(curNode))
4573
nsCOMPtr<nsIDOMElement> curElem = do_QueryInterface(curNode);
4574
res = AlignBlock(curElem, alignType, PR_FALSE);
4575
if (NS_FAILED(res)) return res;
4576
// clear out curDiv so that we don't put nodes after this one into it
4581
// Skip insignificant formatting text nodes to prevent
4582
// unnecessary structure splitting!
4583
if (nsEditor::IsTextNode(curNode) &&
4584
((nsHTMLEditUtils::IsTableElement(curParent) && !nsHTMLEditUtils::IsTableCellOrCaption(curParent)) ||
4585
nsHTMLEditUtils::IsList(curParent)))
4588
// if it's a list item, or a list
4589
// inside a list, forget any "current" div, and instead put divs inside
4590
// the appropriate block (td, li, etc)
4591
if ( nsHTMLEditUtils::IsListItem(curNode)
4592
|| nsHTMLEditUtils::IsList(curNode))
4594
res = RemoveAlignment(curNode, *alignType, PR_TRUE);
4595
if (NS_FAILED(res)) return res;
4597
nsCOMPtr<nsIDOMElement> curElem = do_QueryInterface(curNode);
4598
NS_NAMED_LITERAL_STRING(attrName, "align");
4600
mHTMLEditor->mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(curNode, nsnull,
4601
&attrName, alignType,
4606
else if (nsHTMLEditUtils::IsList(curParent)) {
4607
// if we don't use CSS, add a contraint to list element : they have
4608
// to be inside another list, ie >= second level of nesting
4609
res = AlignInnerBlocks(curNode, alignType);
4610
if (NS_FAILED(res)) return res;
4614
// clear out curDiv so that we don't put nodes after this one into it
4617
// need to make a div to put things in if we haven't already,
4618
// or if this node doesn't go in div we used earlier.
4619
if (!curDiv || transitionList[i])
4621
NS_NAMED_LITERAL_STRING(divType, "div");
4622
res = SplitAsNeeded(&divType, address_of(curParent), &offset);
4623
if (NS_FAILED(res)) return res;
4624
res = mHTMLEditor->CreateNode(divType, curParent, offset, getter_AddRefs(curDiv));
4625
if (NS_FAILED(res)) return res;
4626
// remember our new block for postprocessing
4628
// set up the alignment on the div
4629
nsCOMPtr<nsIDOMElement> divElem = do_QueryInterface(curDiv);
4630
res = AlignBlock(divElem, alignType, PR_TRUE);
4631
// nsAutoString attr(NS_LITERAL_STRING("align"));
4632
// res = mHTMLEditor->SetAttribute(divElem, attr, *alignType);
4633
// if (NS_FAILED(res)) return res;
4634
// curDiv is now the correct thing to put curNode in
4637
// tuck the node into the end of the active div
4638
res = mHTMLEditor->MoveNode(curNode, curDiv, -1);
4639
if (NS_FAILED(res)) return res;
4646
///////////////////////////////////////////////////////////////////////////
4647
// AlignInnerBlocks: align inside table cells or list items
4650
nsHTMLEditRules::AlignInnerBlocks(nsIDOMNode *aNode, const nsAString *alignType)
4652
if (!aNode || !alignType) return NS_ERROR_NULL_POINTER;
4655
// gather list of table cells or list items
4656
nsCOMArray<nsIDOMNode> arrayOfNodes;
4657
nsTableCellAndListItemFunctor functor;
4659
res = iter.Init(aNode);
4660
if (NS_FAILED(res)) return res;
4661
res = iter.AppendList(functor, arrayOfNodes);
4662
if (NS_FAILED(res)) return res;
4664
// now that we have the list, align their contents as requested
4665
PRInt32 listCount = arrayOfNodes.Count();
4668
for (j = 0; j < listCount; j++)
4670
nsIDOMNode* node = arrayOfNodes[0];
4671
res = AlignBlockContents(node, alignType);
4672
if (NS_FAILED(res)) return res;
4673
arrayOfNodes.RemoveObjectAt(0);
4680
///////////////////////////////////////////////////////////////////////////
4681
// AlignBlockContents: align contents of a block element
4684
nsHTMLEditRules::AlignBlockContents(nsIDOMNode *aNode, const nsAString *alignType)
4686
if (!aNode || !alignType) return NS_ERROR_NULL_POINTER;
4688
nsCOMPtr <nsIDOMNode> firstChild, lastChild, divNode;
4691
mHTMLEditor->GetIsCSSEnabled(&useCSS);
4693
res = mHTMLEditor->GetFirstEditableChild(aNode, address_of(firstChild));
4694
if (NS_FAILED(res)) return res;
4695
res = mHTMLEditor->GetLastEditableChild(aNode, address_of(lastChild));
4696
if (NS_FAILED(res)) return res;
4697
NS_NAMED_LITERAL_STRING(attr, "align");
4700
// this cell has no content, nothing to align
4702
else if ((firstChild==lastChild) && nsHTMLEditUtils::IsDiv(firstChild))
4704
// the cell already has a div containing all of it's content: just
4706
nsCOMPtr<nsIDOMElement> divElem = do_QueryInterface(firstChild);
4708
res = mHTMLEditor->SetAttributeOrEquivalent(divElem, attr, *alignType, PR_FALSE);
4711
res = mHTMLEditor->SetAttribute(divElem, attr, *alignType);
4713
if (NS_FAILED(res)) return res;
4717
// else we need to put in a div, set the alignment, and toss in all the children
4718
res = mHTMLEditor->CreateNode(NS_LITERAL_STRING("div"), aNode, 0, getter_AddRefs(divNode));
4719
if (NS_FAILED(res)) return res;
4720
// set up the alignment on the div
4721
nsCOMPtr<nsIDOMElement> divElem = do_QueryInterface(divNode);
4723
res = mHTMLEditor->SetAttributeOrEquivalent(divElem, attr, *alignType, PR_FALSE);
4726
res = mHTMLEditor->SetAttribute(divElem, attr, *alignType);
4728
if (NS_FAILED(res)) return res;
4729
// tuck the children into the end of the active div
4730
while (lastChild && (lastChild != divNode))
4732
res = mHTMLEditor->MoveNode(lastChild, divNode, 0);
4733
if (NS_FAILED(res)) return res;
4734
res = mHTMLEditor->GetLastEditableChild(aNode, address_of(lastChild));
4735
if (NS_FAILED(res)) return res;
4741
///////////////////////////////////////////////////////////////////////////
4742
// CheckForEmptyBlock: Called by WillDeleteSelection to detect and handle
4743
// case of deleting from inside an empty block.
4746
nsHTMLEditRules::CheckForEmptyBlock(nsIDOMNode *aStartNode,
4747
nsIDOMNode *aBodyNode,
4748
nsISelection *aSelection,
4751
// if we are inside an empty block, delete it.
4752
// Note: do NOT delete table elements this way.
4753
nsresult res = NS_OK;
4754
nsCOMPtr<nsIDOMNode> block, emptyBlock;
4755
if (IsBlockNode(aStartNode))
4758
block = mHTMLEditor->GetBlockNodeParent(aStartNode);
4759
PRBool bIsEmptyNode;
4760
if (block != aBodyNode) // efficiency hack. avoiding IsEmptyNode() call when in body
4762
res = mHTMLEditor->IsEmptyNode(block, &bIsEmptyNode, PR_TRUE, PR_FALSE);
4763
if (NS_FAILED(res)) return res;
4764
while (bIsEmptyNode && !nsHTMLEditUtils::IsTableElement(block) && (block != aBodyNode))
4767
block = mHTMLEditor->GetBlockNodeParent(emptyBlock);
4768
res = mHTMLEditor->IsEmptyNode(block, &bIsEmptyNode, PR_TRUE, PR_FALSE);
4769
if (NS_FAILED(res)) return res;
4775
nsCOMPtr<nsIDOMNode> blockParent;
4777
res = nsEditor::GetNodeLocation(emptyBlock, address_of(blockParent), &offset);
4778
if (NS_FAILED(res)) return res;
4779
if (!blockParent || offset < 0) return NS_ERROR_FAILURE;
4781
if (nsHTMLEditUtils::IsListItem(emptyBlock))
4783
// are we the first list item in the list?
4785
res = mHTMLEditor->IsFirstEditableChild(emptyBlock, &bIsFirst);
4786
if (NS_FAILED(res)) return res;
4789
nsCOMPtr<nsIDOMNode> listParent;
4791
res = nsEditor::GetNodeLocation(blockParent, address_of(listParent), &listOffset);
4792
if (NS_FAILED(res)) return res;
4793
if (!listParent || listOffset < 0) return NS_ERROR_FAILURE;
4794
// if we are a sublist, skip the br creation
4795
if (!nsHTMLEditUtils::IsList(listParent))
4797
// create a br before list
4798
nsCOMPtr<nsIDOMNode> brNode;
4799
res = mHTMLEditor->CreateBR(listParent, listOffset, address_of(brNode));
4800
if (NS_FAILED(res)) return res;
4801
// adjust selection to be right before it
4802
res = aSelection->Collapse(listParent, listOffset);
4803
if (NS_FAILED(res)) return res;
4805
// else just let selection perculate up. We'll adjust it in AfterEdit()
4810
// adjust selection to be right after it
4811
res = aSelection->Collapse(blockParent, offset+1);
4812
if (NS_FAILED(res)) return res;
4814
res = mHTMLEditor->DeleteNode(emptyBlock);
4815
*aHandled = PR_TRUE;
4821
nsHTMLEditRules::CheckForInvisibleBR(nsIDOMNode *aBlock,
4823
nsCOMPtr<nsIDOMNode> *outBRNode,
4826
if (!aBlock || !outBRNode) return NS_ERROR_NULL_POINTER;
4827
*outBRNode = nsnull;
4829
nsCOMPtr<nsIDOMNode> testNode;
4830
PRInt32 testOffset = 0;
4831
PRBool runTest = PR_FALSE;
4833
if (aWhere == kBlockEnd)
4835
nsCOMPtr<nsIDOMNode> rightmostNode;
4836
rightmostNode = mHTMLEditor->GetRightmostChild(aBlock, PR_TRUE); // no block crossing
4840
nsCOMPtr<nsIDOMNode> nodeParent;
4843
if (NS_SUCCEEDED(nsEditor::GetNodeLocation(rightmostNode,
4844
address_of(nodeParent),
4848
testNode = nodeParent;
4849
// use offset + 1, because we want the last node included in our evaluation
4850
testOffset = nodeOffset + 1;
4858
// we'll check everything to the left of the input position
4859
testOffset = aOffset;
4864
nsWSRunObject wsTester(mHTMLEditor, testNode, testOffset);
4865
if (nsWSRunObject::eBreak == wsTester.mStartReason)
4867
*outBRNode = wsTester.mStartReasonNode;
4875
///////////////////////////////////////////////////////////////////////////
4876
// GetInnerContent: aList and aTbl allow the caller to specify what kind
4877
// of content to "look inside". If aTbl is true, look inside
4878
// any table content, and insert the inner content into the
4879
// supplied issupportsarray at offset aIndex.
4880
// Similarly with aList and list content.
4881
// aIndex is updated to point past inserted elements.
4884
nsHTMLEditRules::GetInnerContent(nsIDOMNode *aNode, nsCOMArray<nsIDOMNode> &outArrayOfNodes,
4885
PRInt32 *aIndex, PRBool aList, PRBool aTbl)
4887
if (!aNode || !aIndex) return NS_ERROR_NULL_POINTER;
4889
nsCOMPtr<nsIDOMNode> node;
4891
nsresult res = mHTMLEditor->GetFirstEditableChild(aNode, address_of(node));
4892
while (NS_SUCCEEDED(res) && node)
4894
if ( ( aList && (nsHTMLEditUtils::IsList(node) ||
4895
nsHTMLEditUtils::IsListItem(node) ) )
4896
|| ( aTbl && nsHTMLEditUtils::IsTableElement(node) ) )
4898
res = GetInnerContent(node, outArrayOfNodes, aIndex, aList, aTbl);
4899
if (NS_FAILED(res)) return res;
4903
outArrayOfNodes.InsertObjectAt(node, *aIndex);
4906
nsCOMPtr<nsIDOMNode> tmp;
4907
res = node->GetNextSibling(getter_AddRefs(tmp));
4914
///////////////////////////////////////////////////////////////////////////
4915
// ExpandSelectionForDeletion: this promotes our selection to include blocks
4916
// that have all their children selected.
4919
nsHTMLEditRules::ExpandSelectionForDeletion(nsISelection *aSelection)
4922
return NS_ERROR_NULL_POINTER;
4924
// don't need to touch collapsed selections
4926
nsresult res = aSelection->GetIsCollapsed(&bCollapsed);
4927
if (NS_FAILED(res)) return res;
4928
if (bCollapsed) return res;
4931
res = aSelection->GetRangeCount(&rangeCount);
4932
if (NS_FAILED(res)) return res;
4934
// we don't need to mess with cell selections, and we assume multirange selections are those.
4935
if (rangeCount != 1) return NS_OK;
4937
// find current sel start and end
4938
nsCOMPtr<nsIDOMRange> range;
4939
res = aSelection->GetRangeAt(0, getter_AddRefs(range));
4940
if (NS_FAILED(res)) return res;
4941
if (!range) return NS_ERROR_NULL_POINTER;
4942
nsCOMPtr<nsIDOMNode> selStartNode, selEndNode, selCommon;
4943
PRInt32 selStartOffset, selEndOffset;
4945
res = range->GetStartContainer(getter_AddRefs(selStartNode));
4946
if (NS_FAILED(res)) return res;
4947
res = range->GetStartOffset(&selStartOffset);
4948
if (NS_FAILED(res)) return res;
4949
res = range->GetEndContainer(getter_AddRefs(selEndNode));
4950
if (NS_FAILED(res)) return res;
4951
res = range->GetEndOffset(&selEndOffset);
4952
if (NS_FAILED(res)) return res;
4954
// find current selection common block parent
4955
res = range->GetCommonAncestorContainer(getter_AddRefs(selCommon));
4956
if (NS_FAILED(res)) return res;
4957
if (!IsBlockNode(selCommon))
4958
selCommon = nsHTMLEditor::GetBlockNodeParent(selCommon);
4960
// set up for loops and cache our root element
4961
PRBool stillLooking = PR_TRUE;
4962
nsCOMPtr<nsIDOMNode> visNode, firstBRParent;
4963
PRInt32 visOffset=0, firstBROffset=0;
4965
nsCOMPtr<nsIDOMElement> rootElement;
4966
res = mHTMLEditor->GetRootElement(getter_AddRefs(rootElement));
4968
// find previous visible thingy before start of selection
4969
if ((selStartNode!=selCommon) && (selStartNode!=rootElement))
4971
while (stillLooking)
4973
nsWSRunObject wsObj(mHTMLEditor, selStartNode, selStartOffset);
4974
res = wsObj.PriorVisibleNode(selStartNode, selStartOffset, address_of(visNode), &visOffset, &wsType);
4975
if (NS_FAILED(res)) return res;
4976
if (wsType == nsWSRunObject::eThisBlock)
4978
// we want to keep looking up. But stop if we are crossing table element
4979
// boundaries, or if we hit the root.
4980
if ( nsHTMLEditUtils::IsTableElement(wsObj.mStartReasonNode) ||
4981
(selCommon == wsObj.mStartReasonNode) ||
4982
(rootElement == wsObj.mStartReasonNode) )
4984
stillLooking = PR_FALSE;
4988
nsEditor::GetNodeLocation(wsObj.mStartReasonNode, address_of(selStartNode), &selStartOffset);
4993
stillLooking = PR_FALSE;
4998
stillLooking = PR_TRUE;
4999
// find next visible thingy after end of selection
5000
if ((selEndNode!=selCommon) && (selEndNode!=rootElement))
5002
while (stillLooking)
5004
nsWSRunObject wsObj(mHTMLEditor, selEndNode, selEndOffset);
5005
res = wsObj.NextVisibleNode(selEndNode, selEndOffset, address_of(visNode), &visOffset, &wsType);
5006
if (NS_FAILED(res)) return res;
5007
if (wsType == nsWSRunObject::eBreak)
5009
if (mHTMLEditor->IsVisBreak(wsObj.mEndReasonNode))
5011
stillLooking = PR_FALSE;
5017
firstBRParent = selEndNode;
5018
firstBROffset = selEndOffset;
5020
nsEditor::GetNodeLocation(wsObj.mEndReasonNode, address_of(selEndNode), &selEndOffset);
5024
else if (wsType == nsWSRunObject::eThisBlock)
5026
// we want to keep looking up. But stop if we are crossing table element
5027
// boundaries, or if we hit the root.
5028
if ( nsHTMLEditUtils::IsTableElement(wsObj.mEndReasonNode) ||
5029
(selCommon == wsObj.mEndReasonNode) ||
5030
(rootElement == wsObj.mEndReasonNode) )
5032
stillLooking = PR_FALSE;
5036
nsEditor::GetNodeLocation(wsObj.mEndReasonNode, address_of(selEndNode), &selEndOffset);
5042
stillLooking = PR_FALSE;
5046
// now set the selection to the new range
5047
aSelection->Collapse(selStartNode, selStartOffset);
5049
// expand selection endpoint only if we didnt pass a br,
5050
// or if we really needed to pass that br (ie, it's block is now
5051
// totally selected)
5052
PRBool doEndExpansion = PR_TRUE;
5055
// find block node containing br
5056
nsCOMPtr<nsIDOMNode> brBlock = firstBRParent;
5057
if (!IsBlockNode(brBlock))
5058
brBlock = nsHTMLEditor::GetBlockNodeParent(brBlock);
5059
PRBool nodeBefore=PR_FALSE, nodeAfter=PR_FALSE;
5061
// create a range that represents expanded selection
5062
nsCOMPtr<nsIDOMRange> range = do_CreateInstance("@mozilla.org/content/range;1");
5063
if (!range) return NS_ERROR_NULL_POINTER;
5064
res = range->SetStart(selStartNode, selStartOffset);
5065
if (NS_FAILED(res)) return res;
5066
res = range->SetEnd(selEndNode, selEndOffset);
5067
if (NS_FAILED(res)) return res;
5069
// check if block is entirely inside range
5070
nsCOMPtr<nsIContent> brContentBlock = do_QueryInterface(brBlock);
5071
res = mHTMLEditor->mRangeHelper->CompareNodeToRange(brContentBlock, range, &nodeBefore, &nodeAfter);
5073
// if block isn't contained, forgo grabbing the br in the expanded selection
5074
if (nodeBefore || nodeAfter)
5075
doEndExpansion = PR_FALSE;
5079
res = aSelection->Extend(selEndNode, selEndOffset);
5083
// only expand to just before br
5084
res = aSelection->Extend(firstBRParent, firstBROffset);
5090
#ifdef XXX_DEAD_CODE
5091
///////////////////////////////////////////////////////////////////////////
5092
// AtStartOfBlock: is node/offset at the start of the editable material in this block?
5095
nsHTMLEditRules::AtStartOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aBlock)
5097
nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(aNode);
5098
if (nodeAsText && aOffset) return PR_FALSE; // there are chars in front of us
5100
nsCOMPtr<nsIDOMNode> priorNode;
5101
nsresult res = mHTMLEditor->GetPriorHTMLNode(aNode, aOffset, address_of(priorNode));
5102
if (NS_FAILED(res)) return PR_TRUE;
5103
if (!priorNode) return PR_TRUE;
5104
nsCOMPtr<nsIDOMNode> blockParent = mHTMLEditor->GetBlockNodeParent(priorNode);
5105
if (blockParent && (blockParent == aBlock)) return PR_FALSE;
5110
///////////////////////////////////////////////////////////////////////////
5111
// AtEndOfBlock: is node/offset at the end of the editable material in this block?
5114
nsHTMLEditRules::AtEndOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aBlock)
5116
nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(aNode);
5120
nodeAsText->GetLength(&strLength);
5121
if ((PRInt32)strLength > aOffset) return PR_FALSE; // there are chars in after us
5123
nsCOMPtr<nsIDOMNode> nextNode;
5124
nsresult res = mHTMLEditor->GetNextHTMLNode(aNode, aOffset, address_of(nextNode));
5125
if (NS_FAILED(res)) return PR_TRUE;
5126
if (!nextNode) return PR_TRUE;
5127
nsCOMPtr<nsIDOMNode> blockParent = mHTMLEditor->GetBlockNodeParent(nextNode);
5128
if (blockParent && (blockParent == aBlock)) return PR_FALSE;
5133
///////////////////////////////////////////////////////////////////////////
5134
// CreateMozDiv: makes a div with type = _moz
5137
nsHTMLEditRules::CreateMozDiv(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr<nsIDOMNode> *outDiv)
5139
if (!inParent || !outDiv) return NS_ERROR_NULL_POINTER;
5140
nsAutoString divType= "div";
5142
nsresult res = mHTMLEditor->CreateNode(divType, inParent, inOffset, getter_AddRefs(*outDiv));
5143
if (NS_FAILED(res)) return res;
5144
// give it special moz attr
5145
nsCOMPtr<nsIDOMElement> mozDivElem = do_QueryInterface(*outDiv);
5146
res = mHTMLEditor->SetAttribute(mozDivElem, "type", "_moz");
5147
if (NS_FAILED(res)) return res;
5148
res = AddTrailerBR(*outDiv);
5154
///////////////////////////////////////////////////////////////////////////
5155
// NormalizeSelection: tweak non-collapsed selections to be more "natural".
5156
// Idea here is to adjust selection endpoint so that they do not cross
5157
// breaks or block boundaries unless something editable beyond that boundary
5158
// is also selected. This adjustment makes it much easier for the various
5159
// block operations to determine what nodes to act on.
5162
nsHTMLEditRules::NormalizeSelection(nsISelection *inSelection)
5164
if (!inSelection) return NS_ERROR_NULL_POINTER;
5166
// don't need to touch collapsed selections
5168
nsresult res = inSelection->GetIsCollapsed(&bCollapsed);
5169
if (NS_FAILED(res)) return res;
5170
if (bCollapsed) return res;
5173
res = inSelection->GetRangeCount(&rangeCount);
5174
if (NS_FAILED(res)) return res;
5176
// we don't need to mess with cell selections, and we assume multirange selections are those.
5177
if (rangeCount != 1) return NS_OK;
5179
nsCOMPtr<nsIDOMRange> range;
5180
res = inSelection->GetRangeAt(0, getter_AddRefs(range));
5181
if (NS_FAILED(res)) return res;
5182
if (!range) return NS_ERROR_NULL_POINTER;
5183
nsCOMPtr<nsIDOMNode> startNode, endNode;
5184
PRInt32 startOffset, endOffset;
5185
nsCOMPtr<nsIDOMNode> newStartNode, newEndNode;
5186
PRInt32 newStartOffset, newEndOffset;
5188
res = range->GetStartContainer(getter_AddRefs(startNode));
5189
if (NS_FAILED(res)) return res;
5190
res = range->GetStartOffset(&startOffset);
5191
if (NS_FAILED(res)) return res;
5192
res = range->GetEndContainer(getter_AddRefs(endNode));
5193
if (NS_FAILED(res)) return res;
5194
res = range->GetEndOffset(&endOffset);
5195
if (NS_FAILED(res)) return res;
5197
// adjusted values default to original values
5198
newStartNode = startNode;
5199
newStartOffset = startOffset;
5200
newEndNode = endNode;
5201
newEndOffset = endOffset;
5203
// some locals we need for whitespace code
5204
nsCOMPtr<nsIDOMNode> someNode;
5208
// let the whitespace code do the heavy lifting
5209
nsWSRunObject wsEndObj(mHTMLEditor, endNode, endOffset);
5210
// is there any intervening visible whitespace? if so we can't push selection past that,
5211
// it would visibly change maening of users selection
5212
res = wsEndObj.PriorVisibleNode(endNode, endOffset, address_of(someNode), &offset, &wsType);
5213
if (NS_FAILED(res)) return res;
5214
if ((wsType != nsWSRunObject::eText) && (wsType != nsWSRunObject::eNormalWS))
5216
// eThisBlock and eOtherBlock conveniently distinquish cases
5217
// of going "down" into a block and "up" out of a block.
5218
if (wsEndObj.mStartReason == nsWSRunObject::eOtherBlock)
5220
// endpoint is just after the close of a block.
5221
nsCOMPtr<nsIDOMNode> child = mHTMLEditor->GetRightmostChild(wsEndObj.mStartReasonNode, PR_TRUE);
5224
res = nsEditor::GetNodeLocation(child, address_of(newEndNode), &newEndOffset);
5225
if (NS_FAILED(res)) return res;
5226
++newEndOffset; // offset *after* child
5228
// else block is empty - we can leave selection alone here, i think.
5230
else if (wsEndObj.mStartReason == nsWSRunObject::eThisBlock)
5232
// endpoint is just after start of this block
5233
nsCOMPtr<nsIDOMNode> child;
5234
res = mHTMLEditor->GetPriorHTMLNode(endNode, endOffset, address_of(child));
5237
res = nsEditor::GetNodeLocation(child, address_of(newEndNode), &newEndOffset);
5238
if (NS_FAILED(res)) return res;
5239
++newEndOffset; // offset *after* child
5241
// else block is empty - we can leave selection alone here, i think.
5243
else if (wsEndObj.mStartReason == nsWSRunObject::eBreak)
5245
// endpoint is just after break. lets adjust it to before it.
5246
res = nsEditor::GetNodeLocation(wsEndObj.mStartReasonNode, address_of(newEndNode), &newEndOffset);
5247
if (NS_FAILED(res)) return res;
5252
// similar dealio for start of range
5253
nsWSRunObject wsStartObj(mHTMLEditor, startNode, startOffset);
5254
// is there any intervening visible whitespace? if so we can't push selection past that,
5255
// it would visibly change maening of users selection
5256
res = wsStartObj.NextVisibleNode(startNode, startOffset, address_of(someNode), &offset, &wsType);
5257
if (NS_FAILED(res)) return res;
5258
if ((wsType != nsWSRunObject::eText) && (wsType != nsWSRunObject::eNormalWS))
5260
// eThisBlock and eOtherBlock conveniently distinquish cases
5261
// of going "down" into a block and "up" out of a block.
5262
if (wsStartObj.mEndReason == nsWSRunObject::eOtherBlock)
5264
// startpoint is just before the start of a block.
5265
nsCOMPtr<nsIDOMNode> child = mHTMLEditor->GetLeftmostChild(wsStartObj.mEndReasonNode, PR_TRUE);
5268
res = nsEditor::GetNodeLocation(child, address_of(newStartNode), &newStartOffset);
5269
if (NS_FAILED(res)) return res;
5271
// else block is empty - we can leave selection alone here, i think.
5273
else if (wsStartObj.mEndReason == nsWSRunObject::eThisBlock)
5275
// startpoint is just before end of this block
5276
nsCOMPtr<nsIDOMNode> child;
5277
res = mHTMLEditor->GetNextHTMLNode(startNode, startOffset, address_of(child));
5280
res = nsEditor::GetNodeLocation(child, address_of(newStartNode), &newStartOffset);
5281
if (NS_FAILED(res)) return res;
5283
// else block is empty - we can leave selection alone here, i think.
5285
else if (wsStartObj.mEndReason == nsWSRunObject::eBreak)
5287
// startpoint is just before a break. lets adjust it to after it.
5288
res = nsEditor::GetNodeLocation(wsStartObj.mEndReasonNode, address_of(newStartNode), &newStartOffset);
5289
if (NS_FAILED(res)) return res;
5290
++newStartOffset; // offset *after* break
5294
// there is a demented possiblity we have to check for. We might have a very strange selection
5295
// that is not collapsed and yet does not contain any editable content, and satisfies some of the
5296
// above conditions that cause tweaking. In this case we don't want to tweak the selection into
5297
// a block it was never in, etc. There are a variety of strategies one might use to try to
5298
// detect these cases, but I think the most straightforward is to see if the adjusted locations
5299
// "cross" the old values: ie, new end before old start, or new start after old end. If so
5300
// then just leave things alone.
5303
comp = mHTMLEditor->mRangeHelper->ComparePoints(startNode, startOffset, newEndNode, newEndOffset);
5304
if (comp == 1) return NS_OK; // new end before old start
5305
comp = mHTMLEditor->mRangeHelper->ComparePoints(newStartNode, newStartOffset, endNode, endOffset);
5306
if (comp == 1) return NS_OK; // new start after old end
5308
// otherwise set selection to new values.
5309
inSelection->Collapse(newStartNode, newStartOffset);
5310
inSelection->Extend(newEndNode, newEndOffset);
5315
///////////////////////////////////////////////////////////////////////////
5316
// GetPromotedPoint: figure out where a start or end point for a block
5317
// operation really is
5319
nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt32 aOffset,
5320
PRInt32 actionID, nsCOMPtr<nsIDOMNode> *outNode, PRInt32 *outOffset)
5322
nsresult res = NS_OK;
5323
nsCOMPtr<nsIDOMNode> nearNode, node = aNode;
5324
nsCOMPtr<nsIDOMNode> parent = aNode;
5325
PRInt32 pOffset, offset = aOffset;
5329
*outOffset = offset;
5331
// we do one thing for InsertText actions, something else entirely for other actions
5332
if (actionID == kInsertText)
5334
PRBool isSpace, isNBSP;
5335
nsCOMPtr<nsIDOMNode> temp;
5336
// for insert text or delete actions, we want to look backwards (or forwards, as appropriate)
5337
// for additional whitespace or nbsp's. We may have to act on these later even though
5338
// they are outside of the initial selection. Even if they are in another node!
5339
if (aWhere == kStart)
5343
res = mHTMLEditor->IsPrevCharWhitespace(node, offset, &isSpace, &isNBSP, address_of(temp), &offset);
5344
if (NS_FAILED(res)) return res;
5345
if (isSpace || isNBSP) node = temp;
5350
*outOffset = offset;
5352
else if (aWhere == kEnd)
5356
res = mHTMLEditor->IsNextCharWhitespace(node, offset, &isSpace, &isNBSP, address_of(temp), &offset);
5357
if (NS_FAILED(res)) return res;
5358
if (isSpace || isNBSP) node = temp;
5363
*outOffset = offset;
5368
// else not kInsertText. In this case we want to see if we should
5369
// grab any adjacent inline nodes and/or parents and other ancestors
5370
if (aWhere == kStart)
5372
// some special casing for text nodes
5373
if (nsEditor::IsTextNode(aNode))
5375
res = nsEditor::GetNodeLocation(aNode, address_of(node), &offset);
5376
if (NS_FAILED(res)) return res;
5379
// look back through any further inline nodes that
5380
// aren't across a <br> from us, and that are enclosed in the same block.
5381
nsCOMPtr<nsIDOMNode> priorNode;
5382
res = mHTMLEditor->GetPriorHTMLNode(node, offset, address_of(priorNode), PR_TRUE);
5384
while (priorNode && NS_SUCCEEDED(res))
5386
if (mHTMLEditor->IsVisBreak(priorNode))
5388
if (IsBlockNode(priorNode))
5390
res = nsEditor::GetNodeLocation(priorNode, address_of(node), &offset);
5391
if (NS_FAILED(res)) return res;
5392
res = mHTMLEditor->GetPriorHTMLNode(node, offset, address_of(priorNode), PR_TRUE);
5393
if (NS_FAILED(res)) return res;
5397
// finding the real start for this point. look up the tree for as long as we are the
5398
// first node in the container, and as long as we haven't hit the body node.
5399
res = mHTMLEditor->GetPriorHTMLNode(node, offset, address_of(nearNode), PR_TRUE);
5400
if (NS_FAILED(res)) return res;
5401
while (!nearNode && !nsTextEditUtils::IsBody(node))
5403
// some cutoffs are here: we don't need to also include them in the aWhere == kEnd case.
5404
// as long as they are in one or the other it will work.
5405
// special case for outdent: don't keep looking up
5406
// if we have found a blockquote element to act on
5407
if ((actionID == kOutdent) && nsHTMLEditUtils::IsBlockquote(node))
5410
res = nsEditor::GetNodeLocation(node, address_of(parent), &pOffset);
5411
if (NS_FAILED(res)) return res;
5414
res = mHTMLEditor->GetPriorHTMLNode(node, offset, address_of(nearNode), PR_TRUE);
5415
if (NS_FAILED(res)) return res;
5418
*outOffset = offset;
5424
// some special casing for text nodes
5425
if (nsEditor::IsTextNode(aNode))
5427
res = nsEditor::GetNodeLocation(aNode, address_of(node), &offset);
5428
if (NS_FAILED(res)) return res;
5429
offset++; // want to be after the text node
5432
// look ahead through any further inline nodes that
5433
// aren't across a <br> from us, and that are enclosed in the same block.
5434
nsCOMPtr<nsIDOMNode> nextNode;
5435
res = mHTMLEditor->GetNextHTMLNode(node, offset, address_of(nextNode), PR_TRUE);
5437
while (nextNode && NS_SUCCEEDED(res))
5439
if (IsBlockNode(nextNode))
5441
res = nsEditor::GetNodeLocation(nextNode, address_of(node), &offset);
5442
if (NS_FAILED(res)) return res;
5444
if (mHTMLEditor->IsVisBreak(nextNode))
5446
res = mHTMLEditor->GetNextHTMLNode(node, offset, address_of(nextNode), PR_TRUE);
5447
if (NS_FAILED(res)) return res;
5450
// finding the real end for this point. look up the tree for as long as we are the
5451
// last node in the container, and as long as we haven't hit the body node.
5452
res = mHTMLEditor->GetNextHTMLNode(node, offset, address_of(nearNode), PR_TRUE);
5453
if (NS_FAILED(res)) return res;
5454
while (!nearNode && !nsTextEditUtils::IsBody(node))
5456
res = nsEditor::GetNodeLocation(node, address_of(parent), &pOffset);
5457
if (NS_FAILED(res)) return res;
5459
offset = pOffset+1; // we want to be AFTER nearNode
5460
res = mHTMLEditor->GetNextHTMLNode(node, offset, address_of(nearNode), PR_TRUE);
5461
if (NS_FAILED(res)) return res;
5464
*outOffset = offset;
5472
///////////////////////////////////////////////////////////////////////////
5473
// GetPromotedRanges: run all the selection range endpoint through
5474
// GetPromotedPoint()
5477
nsHTMLEditRules::GetPromotedRanges(nsISelection *inSelection,
5478
nsCOMArray<nsIDOMRange> &outArrayOfRanges,
5479
PRInt32 inOperationType)
5481
if (!inSelection) return NS_ERROR_NULL_POINTER;
5484
nsresult res = inSelection->GetRangeCount(&rangeCount);
5485
if (NS_FAILED(res)) return res;
5488
nsCOMPtr<nsIDOMRange> selectionRange;
5489
nsCOMPtr<nsIDOMRange> opRange;
5491
for (i = 0; i < rangeCount; i++)
5493
res = inSelection->GetRangeAt(i, getter_AddRefs(selectionRange));
5494
if (NS_FAILED(res)) return res;
5496
// clone range so we dont muck with actual selection ranges
5497
res = selectionRange->CloneRange(getter_AddRefs(opRange));
5498
if (NS_FAILED(res)) return res;
5500
// make a new adjusted range to represent the appropriate block content.
5501
// The basic idea is to push out the range endpoints
5502
// to truly enclose the blocks that we will affect.
5503
// This call alters opRange.
5504
res = PromoteRange(opRange, inOperationType);
5505
if (NS_FAILED(res)) return res;
5507
// stuff new opRange into array
5508
outArrayOfRanges.AppendObject(opRange);
5514
///////////////////////////////////////////////////////////////////////////
5515
// PromoteRange: expand a range to include any parents for which all
5516
// editable children are already in range.
5519
nsHTMLEditRules::PromoteRange(nsIDOMRange *inRange,
5520
PRInt32 inOperationType)
5522
if (!inRange) return NS_ERROR_NULL_POINTER;
5524
nsCOMPtr<nsIDOMNode> startNode, endNode;
5525
PRInt32 startOffset, endOffset;
5527
res = inRange->GetStartContainer(getter_AddRefs(startNode));
5528
if (NS_FAILED(res)) return res;
5529
res = inRange->GetStartOffset(&startOffset);
5530
if (NS_FAILED(res)) return res;
5531
res = inRange->GetEndContainer(getter_AddRefs(endNode));
5532
if (NS_FAILED(res)) return res;
5533
res = inRange->GetEndOffset(&endOffset);
5534
if (NS_FAILED(res)) return res;
5536
// MOOSE major hack:
5537
// GetPromotedPoint doesn't really do the right thing for collapsed ranges
5538
// inside block elements that contain nothing but a solo <br>. It's easier
5539
// to put a workaround here than to revamp GetPromotedPoint. :-(
5540
if ( (startNode == endNode) && (startOffset == endOffset))
5542
nsCOMPtr<nsIDOMNode> block;
5543
if (IsBlockNode(startNode))
5546
block = mHTMLEditor->GetBlockNodeParent(startNode);
5549
PRBool bIsEmptyNode = PR_FALSE;
5551
nsCOMPtr<nsIDOMElement> bodyElement;
5552
nsCOMPtr<nsIDOMNode> bodyNode;
5553
res = mHTMLEditor->GetRootElement(getter_AddRefs(bodyElement));
5554
if (NS_FAILED(res)) return res;
5555
if (!bodyElement) return NS_ERROR_UNEXPECTED;
5556
bodyNode = do_QueryInterface(bodyElement);
5557
if (block != bodyNode)
5559
// ok, not body, check if empty
5560
res = mHTMLEditor->IsEmptyNode(block, &bIsEmptyNode, PR_TRUE, PR_FALSE);
5564
PRUint32 numChildren;
5565
nsEditor::GetLengthOfDOMNode(block, numChildren);
5569
endOffset = numChildren;
5574
// make a new adjusted range to represent the appropriate block content.
5575
// this is tricky. the basic idea is to push out the range endpoints
5576
// to truly enclose the blocks that we will affect
5578
nsCOMPtr<nsIDOMNode> opStartNode;
5579
nsCOMPtr<nsIDOMNode> opEndNode;
5580
PRInt32 opStartOffset, opEndOffset;
5581
nsCOMPtr<nsIDOMRange> opRange;
5583
res = GetPromotedPoint( kStart, startNode, startOffset, inOperationType, address_of(opStartNode), &opStartOffset);
5584
if (NS_FAILED(res)) return res;
5585
res = GetPromotedPoint( kEnd, endNode, endOffset, inOperationType, address_of(opEndNode), &opEndOffset);
5586
if (NS_FAILED(res)) return res;
5587
res = inRange->SetStart(opStartNode, opStartOffset);
5588
if (NS_FAILED(res)) return res;
5589
res = inRange->SetEnd(opEndNode, opEndOffset);
5593
///////////////////////////////////////////////////////////////////////////
5594
// GetNodesForOperation: run through the ranges in the array and construct
5595
// a new array of nodes to be acted on.
5598
nsHTMLEditRules::GetNodesForOperation(nsCOMArray<nsIDOMRange>& inArrayOfRanges,
5599
nsCOMArray<nsIDOMNode>& outArrayOfNodes,
5600
PRInt32 inOperationType,
5601
PRBool aDontTouchContent)
5603
PRInt32 rangeCount = inArrayOfRanges.Count();
5606
nsCOMPtr<nsIDOMRange> opRange;
5609
mHTMLEditor->GetIsCSSEnabled(&useCSS);
5611
nsresult res = NS_OK;
5613
// bust up any inlines that cross our range endpoints,
5614
// but only if we are allowed to touch content.
5616
if (!aDontTouchContent)
5618
nsVoidArray rangeItemArray;
5619
// first register ranges for special editor gravity
5620
// XXXbz doesn't this leak all the nsRangeStore structs on error
5622
for (i = 0; i < (PRInt32)rangeCount; i++)
5624
opRange = inArrayOfRanges[0];
5625
nsRangeStore *item = new nsRangeStore();
5626
if (!item) return NS_ERROR_NULL_POINTER;
5627
item->StoreRange(opRange);
5628
mHTMLEditor->mRangeUpdater.RegisterRangeItem(item);
5629
rangeItemArray.AppendElement((void*)item);
5630
inArrayOfRanges.RemoveObjectAt(0);
5632
// now bust up inlines
5633
for (i = rangeCount-1; i >= 0; i--)
5635
nsRangeStore *item = (nsRangeStore*)rangeItemArray.ElementAt(i);
5636
res = BustUpInlinesAtRangeEndpoints(*item);
5637
if (NS_FAILED(res)) return res;
5639
// then unregister the ranges
5640
for (i = 0; i < rangeCount; i++)
5642
nsRangeStore *item = (nsRangeStore*)rangeItemArray.ElementAt(0);
5643
if (!item) return NS_ERROR_NULL_POINTER;
5644
rangeItemArray.RemoveElementAt(0);
5645
mHTMLEditor->mRangeUpdater.DropRangeItem(item);
5646
res = item->GetRange(address_of(opRange));
5647
if (NS_FAILED(res)) return res;
5649
inArrayOfRanges.AppendObject(opRange);
5652
// gather up a list of all the nodes
5653
for (i = 0; i < rangeCount; i++)
5655
opRange = inArrayOfRanges[i];
5657
nsTrivialFunctor functor;
5658
nsDOMSubtreeIterator iter;
5659
res = iter.Init(opRange);
5660
if (NS_FAILED(res)) return res;
5661
res = iter.AppendList(functor, outArrayOfNodes);
5662
if (NS_FAILED(res)) return res;
5665
// certain operations should not act on li's and td's, but rather inside
5666
// them. alter the list as needed
5667
if (inOperationType == kMakeBasicBlock)
5669
PRInt32 listCount = outArrayOfNodes.Count();
5670
for (i=listCount-1; i>=0; i--)
5672
nsCOMPtr<nsIDOMNode> node = outArrayOfNodes[i];
5673
if (nsHTMLEditUtils::IsListItem(node))
5676
outArrayOfNodes.RemoveObjectAt(i);
5677
res = GetInnerContent(node, outArrayOfNodes, &j);
5678
if (NS_FAILED(res)) return res;
5682
// indent/outdent already do something special for list items, but
5683
// we still need to make sure we dont act on table elements
5684
else if ( (inOperationType == kOutdent) ||
5685
(inOperationType == kIndent) ||
5686
(inOperationType == kSetAbsolutePosition ||
5687
(inOperationType == kMakeComplexBlock)) )
5689
PRInt32 listCount = outArrayOfNodes.Count();
5690
for (i=listCount-1; i>=0; i--)
5692
nsCOMPtr<nsIDOMNode> node = outArrayOfNodes[i];
5693
if (nsHTMLEditUtils::IsTableElementButNotTable(node))
5696
outArrayOfNodes.RemoveObjectAt(i);
5697
res = GetInnerContent(node, outArrayOfNodes, &j);
5698
if (NS_FAILED(res)) return res;
5702
// outdent should look inside of divs.
5703
if (inOperationType == kOutdent && !useCSS)
5705
PRInt32 listCount = outArrayOfNodes.Count();
5706
for (i=listCount-1; i>=0; i--)
5708
nsCOMPtr<nsIDOMNode> node = outArrayOfNodes[i];
5709
if (nsHTMLEditUtils::IsDiv(node))
5712
outArrayOfNodes.RemoveObjectAt(i);
5713
res = GetInnerContent(node, outArrayOfNodes, &j, PR_FALSE, PR_FALSE);
5714
if (NS_FAILED(res)) return res;
5720
// post process the list to break up inline containers that contain br's.
5721
// but only for operations that might care, like making lists or para's...
5722
if ( (inOperationType == kMakeBasicBlock) ||
5723
(inOperationType == kMakeList) ||
5724
(inOperationType == kAlign) ||
5725
(inOperationType == kSetAbsolutePosition) ||
5726
(inOperationType == kMakeComplexBlock) ||
5727
(inOperationType == kIndent) ||
5728
(inOperationType == kOutdent) )
5730
PRInt32 listCount = outArrayOfNodes.Count();
5731
for (i=listCount-1; i>=0; i--)
5733
nsCOMPtr<nsIDOMNode> node = outArrayOfNodes[i];
5734
if (!aDontTouchContent && IsInlineNode(node)
5735
&& mHTMLEditor->IsContainer(node) && !mHTMLEditor->IsTextNode(node))
5737
nsCOMArray<nsIDOMNode> arrayOfInlines;
5738
res = BustUpInlinesAtBRs(node, arrayOfInlines);
5739
if (NS_FAILED(res)) return res;
5740
// put these nodes in outArrayOfNodes, replacing the current node
5741
outArrayOfNodes.RemoveObjectAt(i);
5742
outArrayOfNodes.InsertObjectsAt(arrayOfInlines, i);
5751
///////////////////////////////////////////////////////////////////////////
5752
// GetChildNodesForOperation:
5755
nsHTMLEditRules::GetChildNodesForOperation(nsIDOMNode *inNode,
5756
nsCOMArray<nsIDOMNode>& outArrayOfNodes)
5758
if (!inNode) return NS_ERROR_NULL_POINTER;
5760
nsCOMPtr<nsIDOMNodeList> childNodes;
5761
nsresult res = inNode->GetChildNodes(getter_AddRefs(childNodes));
5762
if (NS_FAILED(res)) return res;
5763
if (!childNodes) return NS_ERROR_NULL_POINTER;
5764
PRUint32 childCount;
5765
res = childNodes->GetLength(&childCount);
5766
if (NS_FAILED(res)) return res;
5769
nsCOMPtr<nsIDOMNode> node;
5770
for (i = 0; i < childCount; i++)
5772
res = childNodes->Item( i, getter_AddRefs(node));
5773
if (!node) return NS_ERROR_FAILURE;
5774
if (!outArrayOfNodes.AppendObject(node))
5775
return NS_ERROR_FAILURE;
5782
///////////////////////////////////////////////////////////////////////////
5783
// GetListActionNodes:
5786
nsHTMLEditRules::GetListActionNodes(nsCOMArray<nsIDOMNode> &outArrayOfNodes,
5788
PRBool aDontTouchContent)
5790
nsresult res = NS_OK;
5792
nsCOMPtr<nsISelection>selection;
5793
res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
5794
if (NS_FAILED(res)) return res;
5795
nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
5797
return NS_ERROR_FAILURE;
5798
// added this in so that ui code can ask to change an entire list, even if selection
5799
// is only in part of it. used by list item dialog.
5802
nsCOMPtr<nsIEnumerator> enumerator;
5803
res = selPriv->GetEnumerator(getter_AddRefs(enumerator));
5804
if (NS_FAILED(res)) return res;
5805
if (!enumerator) return NS_ERROR_UNEXPECTED;
5807
for (enumerator->First(); NS_OK!=enumerator->IsDone(); enumerator->Next())
5809
nsCOMPtr<nsISupports> currentItem;
5810
res = enumerator->CurrentItem(getter_AddRefs(currentItem));
5811
if (NS_FAILED(res)) return res;
5812
if (!currentItem) return NS_ERROR_UNEXPECTED;
5814
nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
5815
nsCOMPtr<nsIDOMNode> commonParent, parent, tmp;
5816
range->GetCommonAncestorContainer(getter_AddRefs(commonParent));
5819
parent = commonParent;
5822
if (nsHTMLEditUtils::IsList(parent))
5824
outArrayOfNodes.AppendObject(parent);
5827
parent->GetParentNode(getter_AddRefs(tmp));
5832
// if we didn't find any nodes this way, then try the normal way. perhaps the
5833
// selection spans multiple lists but with no common list parent.
5834
if (outArrayOfNodes.Count()) return NS_OK;
5837
// contruct a list of nodes to act on.
5838
res = GetNodesFromSelection(selection, kMakeList, outArrayOfNodes, aDontTouchContent);
5839
if (NS_FAILED(res)) return res;
5841
// pre process our list of nodes...
5842
PRInt32 listCount = outArrayOfNodes.Count();
5844
for (i=listCount-1; i>=0; i--)
5846
nsCOMPtr<nsIDOMNode> testNode = outArrayOfNodes[i];
5848
// Remove all non-editable nodes. Leave them be.
5849
if (!mHTMLEditor->IsEditable(testNode))
5851
outArrayOfNodes.RemoveObjectAt(i);
5854
// scan for table elements and divs. If we find table elements other than table,
5855
// replace it with a list of any editable non-table content.
5856
if (nsHTMLEditUtils::IsTableElementButNotTable(testNode))
5859
outArrayOfNodes.RemoveObjectAt(i);
5860
res = GetInnerContent(testNode, outArrayOfNodes, &j, PR_FALSE);
5861
if (NS_FAILED(res)) return res;
5865
// if there is only one node in the array, and it is a list, div, or blockquote,
5866
// then look inside of it until we find inner list or content.
5867
res = LookInsideDivBQandList(outArrayOfNodes);
5872
///////////////////////////////////////////////////////////////////////////
5873
// LookInsideDivBQandList:
5876
nsHTMLEditRules::LookInsideDivBQandList(nsCOMArray<nsIDOMNode>& aNodeArray)
5878
// if there is only one node in the array, and it is a list, div, or blockquote,
5879
// then look inside of it until we find inner list or content.
5880
nsresult res = NS_OK;
5881
PRInt32 listCount = aNodeArray.Count();
5884
nsCOMPtr<nsIDOMNode> curNode = aNodeArray[0];
5886
while (nsHTMLEditUtils::IsDiv(curNode)
5887
|| nsHTMLEditUtils::IsList(curNode)
5888
|| nsHTMLEditUtils::IsBlockquote(curNode))
5890
// dive as long as there is only one child, and it is a list, div, blockquote
5891
PRUint32 numChildren;
5892
res = mHTMLEditor->CountEditableChildren(curNode, numChildren);
5893
if (NS_FAILED(res)) return res;
5895
if (numChildren == 1)
5898
nsCOMPtr <nsIDOMNode> tmpNode = nsEditor::GetChildAt(curNode, 0);
5899
if (nsHTMLEditUtils::IsDiv(tmpNode)
5900
|| nsHTMLEditUtils::IsList(tmpNode)
5901
|| nsHTMLEditUtils::IsBlockquote(tmpNode))
5903
// check editablility XXX floppy moose
5910
// we've found innermost list/blockquote/div:
5911
// replace the one node in the array with these nodes
5912
aNodeArray.RemoveObjectAt(0);
5913
if ((nsHTMLEditUtils::IsDiv(curNode) || nsHTMLEditUtils::IsBlockquote(curNode)))
5916
res = GetInnerContent(curNode, aNodeArray, &j, PR_FALSE, PR_FALSE);
5920
aNodeArray.AppendObject(curNode);
5927
///////////////////////////////////////////////////////////////////////////
5928
// GetDefinitionListItemTypes:
5931
nsHTMLEditRules::GetDefinitionListItemTypes(nsIDOMNode *aNode, PRBool &aDT, PRBool &aDD)
5933
if (!aNode) return NS_ERROR_NULL_POINTER;
5934
aDT = aDD = PR_FALSE;
5935
nsresult res = NS_OK;
5936
nsCOMPtr<nsIDOMNode> child, temp;
5937
res = aNode->GetFirstChild(getter_AddRefs(child));
5938
while (child && NS_SUCCEEDED(res))
5940
if (nsEditor::NodeIsType(child, nsEditProperty::dt)) aDT = PR_TRUE;
5941
else if (nsEditor::NodeIsType(child, nsEditProperty::dd)) aDD = PR_TRUE;
5942
res = child->GetNextSibling(getter_AddRefs(temp));
5948
///////////////////////////////////////////////////////////////////////////
5949
// GetParagraphFormatNodes:
5952
nsHTMLEditRules::GetParagraphFormatNodes(nsCOMArray<nsIDOMNode>& outArrayOfNodes,
5953
PRBool aDontTouchContent)
5955
nsCOMPtr<nsISelection>selection;
5956
nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
5957
if (NS_FAILED(res)) return res;
5959
// contruct a list of nodes to act on.
5960
res = GetNodesFromSelection(selection, kMakeBasicBlock, outArrayOfNodes, aDontTouchContent);
5961
if (NS_FAILED(res)) return res;
5963
// pre process our list of nodes...
5964
PRInt32 listCount = outArrayOfNodes.Count();
5966
for (i=listCount-1; i>=0; i--)
5968
nsCOMPtr<nsIDOMNode> testNode = outArrayOfNodes[i];
5970
// Remove all non-editable nodes. Leave them be.
5971
if (!mHTMLEditor->IsEditable(testNode))
5973
outArrayOfNodes.RemoveObjectAt(i);
5976
// scan for table elements. If we find table elements other than table,
5977
// replace it with a list of any editable non-table content. Ditto for list elements.
5978
if (nsHTMLEditUtils::IsTableElement(testNode) ||
5979
nsHTMLEditUtils::IsList(testNode) ||
5980
nsHTMLEditUtils::IsListItem(testNode) )
5983
outArrayOfNodes.RemoveObjectAt(i);
5984
res = GetInnerContent(testNode, outArrayOfNodes, &j);
5985
if (NS_FAILED(res)) return res;
5992
///////////////////////////////////////////////////////////////////////////
5993
// BustUpInlinesAtRangeEndpoints:
5996
nsHTMLEditRules::BustUpInlinesAtRangeEndpoints(nsRangeStore &item)
5998
nsresult res = NS_OK;
5999
PRBool isCollapsed = ((item.startNode == item.endNode) && (item.startOffset == item.endOffset));
6001
nsCOMPtr<nsIDOMNode> endInline = GetHighestInlineParent(item.endNode);
6003
// if we have inline parents above range endpoints, split them
6004
if (endInline && !isCollapsed)
6006
nsCOMPtr<nsIDOMNode> resultEndNode;
6007
PRInt32 resultEndOffset;
6008
endInline->GetParentNode(getter_AddRefs(resultEndNode));
6009
res = mHTMLEditor->SplitNodeDeep(endInline, item.endNode, item.endOffset,
6010
&resultEndOffset, PR_TRUE);
6011
if (NS_FAILED(res)) return res;
6013
item.endNode = resultEndNode; item.endOffset = resultEndOffset;
6016
nsCOMPtr<nsIDOMNode> startInline = GetHighestInlineParent(item.startNode);
6020
nsCOMPtr<nsIDOMNode> resultStartNode;
6021
PRInt32 resultStartOffset;
6022
startInline->GetParentNode(getter_AddRefs(resultStartNode));
6023
res = mHTMLEditor->SplitNodeDeep(startInline, item.startNode, item.startOffset,
6024
&resultStartOffset, PR_TRUE);
6025
if (NS_FAILED(res)) return res;
6027
item.startNode = resultStartNode; item.startOffset = resultStartOffset;
6035
///////////////////////////////////////////////////////////////////////////
6036
// BustUpInlinesAtBRs:
6039
nsHTMLEditRules::BustUpInlinesAtBRs(nsIDOMNode *inNode,
6040
nsCOMArray<nsIDOMNode>& outArrayOfNodes)
6042
if (!inNode) return NS_ERROR_NULL_POINTER;
6044
// first step is to build up a list of all the break nodes inside
6045
// the inline container.
6046
nsCOMArray<nsIDOMNode> arrayOfBreaks;
6047
nsBRNodeFunctor functor;
6049
nsresult res = iter.Init(inNode);
6050
if (NS_FAILED(res)) return res;
6051
res = iter.AppendList(functor, arrayOfBreaks);
6052
if (NS_FAILED(res)) return res;
6054
// if there aren't any breaks, just put inNode itself in the array
6055
PRInt32 listCount = arrayOfBreaks.Count();
6058
if (!outArrayOfNodes.AppendObject(inNode))
6059
return NS_ERROR_FAILURE;
6063
// else we need to bust up inNode along all the breaks
6064
nsCOMPtr<nsIDOMNode> breakNode;
6065
nsCOMPtr<nsIDOMNode> inlineParentNode;
6066
nsCOMPtr<nsIDOMNode> leftNode;
6067
nsCOMPtr<nsIDOMNode> rightNode;
6068
nsCOMPtr<nsIDOMNode> splitDeepNode = inNode;
6069
nsCOMPtr<nsIDOMNode> splitParentNode;
6070
PRInt32 splitOffset, resultOffset, i;
6071
inNode->GetParentNode(getter_AddRefs(inlineParentNode));
6073
for (i=0; i< listCount; i++)
6075
breakNode = arrayOfBreaks[i];
6076
if (!breakNode) return NS_ERROR_NULL_POINTER;
6077
if (!splitDeepNode) return NS_ERROR_NULL_POINTER;
6078
res = nsEditor::GetNodeLocation(breakNode, address_of(splitParentNode), &splitOffset);
6079
if (NS_FAILED(res)) return res;
6080
res = mHTMLEditor->SplitNodeDeep(splitDeepNode, splitParentNode, splitOffset,
6081
&resultOffset, PR_FALSE, address_of(leftNode), address_of(rightNode));
6082
if (NS_FAILED(res)) return res;
6083
// put left node in node list
6086
// might not be a left node. a break might have been at the very
6087
// beginning of inline container, in which case splitnodedeep
6088
// would not actually split anything
6089
if (!outArrayOfNodes.AppendObject(leftNode))
6090
return NS_ERROR_FAILURE;
6092
// move break outside of container and also put in node list
6093
res = mHTMLEditor->MoveNode(breakNode, inlineParentNode, resultOffset);
6094
if (NS_FAILED(res)) return res;
6095
if (!outArrayOfNodes.AppendObject(breakNode))
6096
return NS_ERROR_FAILURE;
6097
// now rightNode becomes the new node to split
6098
splitDeepNode = rightNode;
6100
// now tack on remaining rightNode, if any, to the list
6103
if (!outArrayOfNodes.AppendObject(rightNode))
6104
return NS_ERROR_FAILURE;
6111
nsCOMPtr<nsIDOMNode>
6112
nsHTMLEditRules::GetHighestInlineParent(nsIDOMNode* aNode)
6114
if (!aNode) return nsnull;
6115
if (IsBlockNode(aNode)) return nsnull;
6116
nsCOMPtr<nsIDOMNode> inlineNode, node=aNode;
6118
while (node && IsInlineNode(node))
6121
inlineNode->GetParentNode(getter_AddRefs(node));
6127
///////////////////////////////////////////////////////////////////////////
6128
// GetNodesFromPoint: given a particular operation, construct a list
6129
// of nodes from a point that will be operated on.
6132
nsHTMLEditRules::GetNodesFromPoint(DOMPoint point,
6134
nsCOMArray<nsIDOMNode> &arrayOfNodes,
6135
PRBool dontTouchContent)
6140
nsCOMPtr<nsIDOMNode> node;
6142
point.GetPoint(node, offset);
6144
// use it to make a range
6145
nsCOMPtr<nsIDOMRange> range = do_CreateInstance("@mozilla.org/content/range;1");
6146
res = range->SetStart(node, offset);
6147
if (NS_FAILED(res)) return res;
6148
/* SetStart() will also set the end for this new range
6149
res = range->SetEnd(node, offset);
6150
if (NS_FAILED(res)) return res; */
6152
// expand the range to include adjacent inlines
6153
res = PromoteRange(range, operation);
6154
if (NS_FAILED(res)) return res;
6156
// make array of ranges
6157
nsCOMArray<nsIDOMRange> arrayOfRanges;
6159
// stuff new opRange into array
6160
arrayOfRanges.AppendObject(range);
6162
// use these ranges to contruct a list of nodes to act on.
6163
res = GetNodesForOperation(arrayOfRanges, arrayOfNodes, operation, dontTouchContent);
6168
///////////////////////////////////////////////////////////////////////////
6169
// GetNodesFromSelection: given a particular operation, construct a list
6170
// of nodes from the selection that will be operated on.
6173
nsHTMLEditRules::GetNodesFromSelection(nsISelection *selection,
6175
nsCOMArray<nsIDOMNode>& arrayOfNodes,
6176
PRBool dontTouchContent)
6178
if (!selection) return NS_ERROR_NULL_POINTER;
6181
// promote selection ranges
6182
nsCOMArray<nsIDOMRange> arrayOfRanges;
6183
res = GetPromotedRanges(selection, arrayOfRanges, operation);
6184
if (NS_FAILED(res)) return res;
6186
// use these ranges to contruct a list of nodes to act on.
6187
res = GetNodesForOperation(arrayOfRanges, arrayOfNodes, operation, dontTouchContent);
6192
///////////////////////////////////////////////////////////////////////////
6193
// MakeTransitionList: detect all the transitions in the array, where a
6194
// transition means that adjacent nodes in the array
6195
// don't have the same parent.
6198
nsHTMLEditRules::MakeTransitionList(nsCOMArray<nsIDOMNode>& inArrayOfNodes,
6199
nsVoidArray &inTransitionArray)
6201
PRInt32 listCount = inArrayOfNodes.Count();
6203
nsVoidArray transitionList;
6204
nsCOMPtr<nsIDOMNode> prevElementParent;
6205
nsCOMPtr<nsIDOMNode> curElementParent;
6207
for (i=0; i<listCount; i++)
6209
nsIDOMNode* transNode = inArrayOfNodes[i];
6210
transNode->GetParentNode(getter_AddRefs(curElementParent));
6211
if (curElementParent != prevElementParent)
6213
// different parents, or separated by <br>: transition point
6214
inTransitionArray.InsertElementAt((void*)PR_TRUE,i);
6218
// same parents: these nodes grew up together
6219
inTransitionArray.InsertElementAt((void*)PR_FALSE,i);
6221
prevElementParent = curElementParent;
6228
/********************************************************
6229
* main implementation methods
6230
********************************************************/
6232
///////////////////////////////////////////////////////////////////////////
6233
// IsInListItem: if aNode is the descendant of a listitem, return that li.
6234
// But table element boundaries are stoppers on the search.
6235
// Also test if aNode is an li itself.
6237
nsCOMPtr<nsIDOMNode>
6238
nsHTMLEditRules::IsInListItem(nsIDOMNode *aNode)
6240
if (!aNode) return nsnull;
6241
if (nsHTMLEditUtils::IsListItem(aNode)) return aNode;
6243
nsCOMPtr<nsIDOMNode> parent, tmp;
6244
aNode->GetParentNode(getter_AddRefs(parent));
6248
if (nsHTMLEditUtils::IsTableElement(parent)) return nsnull;
6249
if (nsHTMLEditUtils::IsListItem(parent)) return parent;
6250
tmp=parent; tmp->GetParentNode(getter_AddRefs(parent));
6256
///////////////////////////////////////////////////////////////////////////
6257
// ReturnInHeader: do the right thing for returns pressed in headers
6260
nsHTMLEditRules::ReturnInHeader(nsISelection *aSelection,
6261
nsIDOMNode *aHeader,
6265
if (!aSelection || !aHeader || !aNode) return NS_ERROR_NULL_POINTER;
6267
// remeber where the header is
6268
nsCOMPtr<nsIDOMNode> headerParent;
6270
nsresult res = nsEditor::GetNodeLocation(aHeader, address_of(headerParent), &offset);
6271
if (NS_FAILED(res)) return res;
6273
// get ws code to adjust any ws
6274
nsCOMPtr<nsIDOMNode> selNode = aNode;
6275
res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor, address_of(selNode), &aOffset);
6276
if (NS_FAILED(res)) return res;
6280
res = mHTMLEditor->SplitNodeDeep( aHeader, selNode, aOffset, &newOffset);
6281
if (NS_FAILED(res)) return res;
6283
// if the leftand heading is empty, put a mozbr in it
6284
nsCOMPtr<nsIDOMNode> prevItem;
6285
mHTMLEditor->GetPriorHTMLSibling(aHeader, address_of(prevItem));
6286
if (prevItem && nsHTMLEditUtils::IsHeader(prevItem))
6288
PRBool bIsEmptyNode;
6289
res = mHTMLEditor->IsEmptyNode(prevItem, &bIsEmptyNode);
6290
if (NS_FAILED(res)) return res;
6293
nsCOMPtr<nsIDOMNode> brNode;
6294
// res = CreateMozBR(prevItem, 0, address_of(brNode));
6295
res = mEditor->CreateBR(prevItem, 0, address_of(brNode));
6296
if (NS_FAILED(res)) return res;
6300
// if the new (righthand) header node is empty, delete it
6302
res = IsEmptyBlock(aHeader, &isEmpty, PR_TRUE);
6303
if (NS_FAILED(res)) return res;
6306
res = mHTMLEditor->DeleteNode(aHeader);
6307
if (NS_FAILED(res)) return res;
6308
// layout tells the caret to blink in a weird place
6309
// if we dont place a break after the header.
6310
nsCOMPtr<nsIDOMNode> sibling;
6311
res = mHTMLEditor->GetNextHTMLSibling(headerParent, offset+1, address_of(sibling));
6312
if (NS_FAILED(res)) return res;
6313
if (!sibling || !nsTextEditUtils::IsBreak(sibling))
6315
res = CreateMozBR(headerParent, offset+1, address_of(sibling));
6316
// res = mEditor->CreateBR(headerParent, offset+1, address_of(sibling));
6317
if (NS_FAILED(res)) return res;
6319
res = nsEditor::GetNodeLocation(sibling, address_of(headerParent), &offset);
6320
if (NS_FAILED(res)) return res;
6321
// put selection after break
6322
res = aSelection->Collapse(headerParent,offset+1);
6326
// put selection at front of righthand heading
6327
res = aSelection->Collapse(aHeader,0);
6332
///////////////////////////////////////////////////////////////////////////
6333
// ReturnInParagraph: do the right thing for returns pressed in paragraphs
6336
nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
6343
if (!aSelection || !aPara || !aNode || !aCancel || !aHandled)
6344
{ return NS_ERROR_NULL_POINTER; }
6347
*aCancel = PR_FALSE;
6348
*aHandled = PR_FALSE;
6350
nsCOMPtr<nsIDOMNode> parent;
6352
nsresult res = nsEditor::GetNodeLocation(aNode, address_of(parent), &offset);
6353
if (NS_FAILED(res)) return res;
6355
PRBool doesCRCreateNewP;
6356
res = mHTMLEditor->GetReturnInParagraphCreatesNewParagraph(&doesCRCreateNewP);
6357
if (NS_FAILED(res)) return res;
6359
PRBool newBRneeded = PR_FALSE;
6360
nsCOMPtr<nsIDOMNode> sibling;
6362
if (mHTMLEditor->IsTextNode(aNode))
6364
nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(aNode);
6366
res = textNode->GetLength(&strLength);
6367
if (NS_FAILED(res)) return res;
6369
// at beginning of text node?
6372
// is there a BR prior to it?
6373
mHTMLEditor->GetPriorHTMLSibling(aNode, address_of(sibling));
6375
!mHTMLEditor->IsVisBreak(sibling) || nsTextEditUtils::HasMozAttr(sibling))
6377
newBRneeded = PR_TRUE;
6380
else if (aOffset == (PRInt32)strLength)
6382
// we're at the end of text node...
6383
// is there a BR after to it?
6384
res = mHTMLEditor->GetNextHTMLSibling(aNode, address_of(sibling));
6386
!mHTMLEditor->IsVisBreak(sibling) || nsTextEditUtils::HasMozAttr(sibling))
6388
newBRneeded = PR_TRUE;
6394
if (doesCRCreateNewP)
6396
nsCOMPtr<nsIDOMNode> tmp;
6397
res = mEditor->SplitNode(aNode, aOffset, getter_AddRefs(tmp));
6398
if (NS_FAILED(res)) return res;
6402
newBRneeded = PR_TRUE;
6408
// not in a text node.
6409
// is there a BR prior to it?
6410
nsCOMPtr<nsIDOMNode> nearNode, selNode = aNode;
6411
res = mHTMLEditor->GetPriorHTMLNode(aNode, aOffset, address_of(nearNode));
6412
if (NS_FAILED(res)) return res;
6413
if (!nearNode || !mHTMLEditor->IsVisBreak(nearNode) || nsTextEditUtils::HasMozAttr(nearNode))
6415
// is there a BR after it?
6416
res = mHTMLEditor->GetNextHTMLNode(aNode, aOffset, address_of(nearNode));
6417
if (NS_FAILED(res)) return res;
6418
if (!nearNode || !mHTMLEditor->IsVisBreak(nearNode) || nsTextEditUtils::HasMozAttr(nearNode))
6420
newBRneeded = PR_TRUE;
6428
// if CR does not create a new P, default to BR creation
6429
if (!doesCRCreateNewP)
6432
nsCOMPtr<nsIDOMNode> brNode;
6433
res = CreateMozBR(parent, offset, address_of(brNode));
6436
nsCOMPtr<nsIDOMNode> selNode = aNode;
6437
*aHandled = PR_TRUE;
6438
return SplitParagraph(aPara, sibling, aSelection, address_of(selNode), &aOffset);
6441
///////////////////////////////////////////////////////////////////////////
6442
// SplitParagraph: split a paragraph at selection point, possibly deleting a br
6445
nsHTMLEditRules::SplitParagraph(nsIDOMNode *aPara,
6446
nsIDOMNode *aBRNode,
6447
nsISelection *aSelection,
6448
nsCOMPtr<nsIDOMNode> *aSelNode,
6451
if (!aPara || !aBRNode || !aSelNode || !*aSelNode || !aOffset || !aSelection)
6452
return NS_ERROR_NULL_POINTER;
6453
nsresult res = NS_OK;
6457
// get ws code to adjust any ws
6458
nsCOMPtr<nsIDOMNode> leftPara, rightPara;
6459
res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor, aSelNode, aOffset);
6460
if (NS_FAILED(res)) return res;
6461
// split the paragraph
6462
res = mHTMLEditor->SplitNodeDeep(aPara, *aSelNode, *aOffset, &newOffset, PR_FALSE,
6463
address_of(leftPara), address_of(rightPara));
6464
if (NS_FAILED(res)) return res;
6465
// get rid of the break, if it is visible (otherwise it may be needed to prevent an empty p)
6466
if (mHTMLEditor->IsVisBreak(aBRNode))
6468
res = mHTMLEditor->DeleteNode(aBRNode);
6469
if (NS_FAILED(res)) return res;
6472
// check both halves of para to see if we need mozBR
6473
res = InsertMozBRIfNeeded(leftPara);
6474
if (NS_FAILED(res)) return res;
6475
res = InsertMozBRIfNeeded(rightPara);
6476
if (NS_FAILED(res)) return res;
6478
// selection to beginning of right hand para;
6479
// look inside any containers that are up front.
6480
nsCOMPtr<nsIDOMNode> child = mHTMLEditor->GetLeftmostChild(rightPara, PR_TRUE);
6481
if (mHTMLEditor->IsTextNode(child) || mHTMLEditor->IsContainer(child))
6483
aSelection->Collapse(child,0);
6487
nsCOMPtr<nsIDOMNode> parent;
6489
res = nsEditor::GetNodeLocation(child, address_of(parent), &offset);
6490
aSelection->Collapse(parent,offset);
6496
///////////////////////////////////////////////////////////////////////////
6497
// ReturnInListItem: do the right thing for returns pressed in list items
6500
nsHTMLEditRules::ReturnInListItem(nsISelection *aSelection,
6501
nsIDOMNode *aListItem,
6505
if (!aSelection || !aListItem || !aNode) return NS_ERROR_NULL_POINTER;
6506
nsCOMPtr<nsISelection> selection(aSelection);
6507
nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
6508
nsresult res = NS_OK;
6510
nsCOMPtr<nsIDOMNode> listitem;
6513
NS_PRECONDITION(PR_TRUE == nsHTMLEditUtils::IsListItem(aListItem),
6514
"expected a list item and didn't get one");
6516
// if we are in an empty listitem, then we want to pop up out of the list
6518
res = IsEmptyBlock(aListItem, &isEmpty, PR_TRUE, PR_FALSE);
6519
if (NS_FAILED(res)) return res;
6520
PRBool hasMozSourceView = PR_FALSE;
6521
res = mHTMLEditor->GetIsColoredSourceView(&hasMozSourceView);
6522
if (NS_FAILED(res)) return res;
6523
if (isEmpty && mReturnInEmptyLIKillsList && !hasMozSourceView) // but only if prefs says it's ok
6525
nsCOMPtr<nsIDOMNode> list, listparent;
6526
PRInt32 offset, itemOffset;
6527
res = nsEditor::GetNodeLocation(aListItem, address_of(list), &itemOffset);
6528
if (NS_FAILED(res)) return res;
6529
res = nsEditor::GetNodeLocation(list, address_of(listparent), &offset);
6530
if (NS_FAILED(res)) return res;
6532
// are we the last list item in the list?
6534
res = mHTMLEditor->IsLastEditableChild(aListItem, &bIsLast);
6535
if (NS_FAILED(res)) return res;
6538
// we need to split the list!
6539
nsCOMPtr<nsIDOMNode> tempNode;
6540
res = mHTMLEditor->SplitNode(list, itemOffset, getter_AddRefs(tempNode));
6541
if (NS_FAILED(res)) return res;
6543
// are we in a sublist?
6544
if (nsHTMLEditUtils::IsList(listparent)) //in a sublist
6546
// if so, move this list item out of this list and into the grandparent list
6547
res = mHTMLEditor->MoveNode(aListItem,listparent,offset+1);
6548
if (NS_FAILED(res)) return res;
6549
res = aSelection->Collapse(aListItem,0);
6553
// otherwise kill this listitem
6554
res = mHTMLEditor->DeleteNode(aListItem);
6555
if (NS_FAILED(res)) return res;
6557
// time to insert a break
6558
nsCOMPtr<nsIDOMNode> brNode;
6559
res = CreateMozBR(listparent, offset+1, address_of(brNode));
6560
if (NS_FAILED(res)) return res;
6562
// set selection to before the moz br
6563
selPriv->SetInterlinePosition(PR_TRUE);
6564
res = aSelection->Collapse(listparent,offset+1);
6569
// else we want a new list item at the same list level.
6570
// get ws code to adjust any ws
6571
nsCOMPtr<nsIDOMNode> selNode = aNode;
6572
res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor, address_of(selNode), &aOffset);
6573
if (NS_FAILED(res)) return res;
6574
// now split list item
6576
res = mHTMLEditor->SplitNodeDeep( aListItem, selNode, aOffset, &newOffset, PR_FALSE);
6577
if (NS_FAILED(res)) return res;
6578
// hack: until I can change the damaged doc range code back to being
6579
// extra inclusive, I have to manually detect certain list items that
6580
// may be left empty.
6581
nsCOMPtr<nsIDOMNode> prevItem;
6582
mHTMLEditor->GetPriorHTMLSibling(aListItem, address_of(prevItem));
6584
if (prevItem && nsHTMLEditUtils::IsListItem(prevItem))
6586
PRBool bIsEmptyNode;
6587
res = mHTMLEditor->IsEmptyNode(prevItem, &bIsEmptyNode);
6588
if (NS_FAILED(res)) return res;
6591
nsCOMPtr<nsIDOMNode> brNode;
6592
res = CreateMozBR(prevItem, 0, address_of(brNode));
6593
if (NS_FAILED(res)) return res;
6597
res = mHTMLEditor->IsEmptyNode(aListItem, &bIsEmptyNode, PR_TRUE);
6598
if (NS_FAILED(res)) return res;
6601
nsCOMPtr<nsIAtom> nodeAtom = nsEditor::GetTag(aListItem);
6602
if (nodeAtom == nsEditProperty::dd || nodeAtom == nsEditProperty::dt)
6604
nsCOMPtr<nsIDOMNode> list;
6606
res = nsEditor::GetNodeLocation(aListItem, address_of(list), &itemOffset);
6607
if (NS_FAILED(res)) return res;
6609
nsAutoString listTag((nodeAtom == nsEditProperty::dt) ? NS_LITERAL_STRING("dd") : NS_LITERAL_STRING("dt"));
6610
nsCOMPtr<nsIDOMNode> newListItem;
6611
res = mHTMLEditor->CreateNode(listTag, list, itemOffset+1, getter_AddRefs(newListItem));
6612
if (NS_FAILED(res)) return res;
6613
res = mEditor->DeleteNode(aListItem);
6614
if (NS_FAILED(res)) return res;
6615
return aSelection->Collapse(newListItem, 0);
6618
nsCOMPtr<nsIDOMNode> brNode;
6619
res = mHTMLEditor->CopyLastEditableChildStyles(prevItem, aListItem, getter_AddRefs(brNode));
6620
if (NS_FAILED(res)) return res;
6623
nsCOMPtr<nsIDOMNode> brParent;
6625
res = nsEditor::GetNodeLocation(brNode, address_of(brParent), &offset);
6626
return aSelection->Collapse(brParent, offset);
6631
nsWSRunObject wsObj(mHTMLEditor, aListItem, 0);
6632
nsCOMPtr<nsIDOMNode> visNode;
6633
PRInt32 visOffset = 0;
6635
res = wsObj.NextVisibleNode(aListItem, 0, address_of(visNode), &visOffset, &wsType);
6636
if (NS_FAILED(res)) return res;
6637
if ( (wsType==nsWSRunObject::eSpecial) ||
6638
(wsType==nsWSRunObject::eBreak) ||
6639
nsHTMLEditUtils::IsHR(visNode) )
6641
nsCOMPtr<nsIDOMNode> parent;
6643
res = nsEditor::GetNodeLocation(visNode, address_of(parent), &offset);
6644
if (NS_FAILED(res)) return res;
6645
return aSelection->Collapse(parent, offset);
6649
return aSelection->Collapse(visNode, visOffset);
6654
res = aSelection->Collapse(aListItem,0);
6659
///////////////////////////////////////////////////////////////////////////
6660
// MakeBlockquote: put the list of nodes into one or more blockquotes.
6663
nsHTMLEditRules::MakeBlockquote(nsCOMArray<nsIDOMNode>& arrayOfNodes)
6665
// the idea here is to put the nodes into a minimal number of
6666
// blockquotes. When the user blockquotes something, they expect
6667
// one blockquote. That may not be possible (for instance, if they
6668
// have two table cells selected, you need two blockquotes inside the cells).
6670
nsresult res = NS_OK;
6672
nsCOMPtr<nsIDOMNode> curNode, curParent, curBlock, newBlock;
6674
PRInt32 listCount = arrayOfNodes.Count();
6676
nsCOMPtr<nsIDOMNode> prevParent;
6679
for (i=0; i<listCount; i++)
6681
// get the node to act on, and it's location
6682
curNode = arrayOfNodes[i];
6683
res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset);
6684
if (NS_FAILED(res)) return res;
6686
// if the node is a table element or list item, dive inside
6687
if (nsHTMLEditUtils::IsTableElementButNotTable(curNode) ||
6688
nsHTMLEditUtils::IsListItem(curNode))
6690
curBlock = 0; // forget any previous block
6692
nsCOMArray<nsIDOMNode> childArray;
6693
res = GetChildNodesForOperation(curNode, childArray);
6694
if (NS_FAILED(res)) return res;
6695
res = MakeBlockquote(childArray);
6696
if (NS_FAILED(res)) return res;
6699
// if the node has different parent than previous node,
6700
// further nodes in a new parent
6703
nsCOMPtr<nsIDOMNode> temp;
6704
curNode->GetParentNode(getter_AddRefs(temp));
6705
if (temp != prevParent)
6707
curBlock = 0; // forget any previous blockquote node we were using
6714
curNode->GetParentNode(getter_AddRefs(prevParent));
6717
// if no curBlock, make one
6720
NS_NAMED_LITERAL_STRING(quoteType, "blockquote");
6721
res = SplitAsNeeded("eType, address_of(curParent), &offset);
6722
if (NS_FAILED(res)) return res;
6723
res = mHTMLEditor->CreateNode(quoteType, curParent, offset, getter_AddRefs(curBlock));
6724
if (NS_FAILED(res)) return res;
6725
// remember our new block for postprocessing
6726
mNewBlock = curBlock;
6727
// note: doesn't matter if we set mNewBlock multiple times.
6730
res = mHTMLEditor->MoveNode(curNode, curBlock, -1);
6731
if (NS_FAILED(res)) return res;
6738
///////////////////////////////////////////////////////////////////////////
6739
// RemoveBlockStyle: make the nodes have no special block type.
6742
nsHTMLEditRules::RemoveBlockStyle(nsCOMArray<nsIDOMNode>& arrayOfNodes)
6744
// intent of this routine is to be used for converting to/from
6745
// headers, paragraphs, pre, and address. Those blocks
6746
// that pretty much just contain inline things...
6748
nsresult res = NS_OK;
6750
nsCOMPtr<nsIDOMNode> curNode, curParent, curBlock, firstNode, lastNode;
6752
PRInt32 listCount = arrayOfNodes.Count();
6755
for (i=0; i<listCount; i++)
6757
// get the node to act on, and it's location
6758
curNode = arrayOfNodes[i];
6759
res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset);
6760
if (NS_FAILED(res)) return res;
6761
nsAutoString curNodeTag, curBlockTag;
6762
nsEditor::GetTagString(curNode, curNodeTag);
6763
ToLowerCase(curNodeTag);
6765
// if curNode is a address, p, header, address, or pre, remove it
6766
if (nsHTMLEditUtils::IsFormatNode(curNode))
6768
// process any partial progress saved
6771
res = RemovePartOfBlock(curBlock, firstNode, lastNode);
6772
if (NS_FAILED(res)) return res;
6773
curBlock = 0; firstNode = 0; lastNode = 0;
6775
// remove curent block
6776
res = mHTMLEditor->RemoveBlockContainer(curNode);
6777
if (NS_FAILED(res)) return res;
6779
else if (nsHTMLEditUtils::IsTable(curNode) ||
6780
nsHTMLEditUtils::IsTableRow(curNode) ||
6781
(curNodeTag.Equals(NS_LITERAL_STRING("tbody"))) ||
6782
(curNodeTag.Equals(NS_LITERAL_STRING("td"))) ||
6783
nsHTMLEditUtils::IsList(curNode) ||
6784
(curNodeTag.Equals(NS_LITERAL_STRING("li"))) ||
6785
nsHTMLEditUtils::IsBlockquote(curNode) ||
6786
nsHTMLEditUtils::IsDiv(curNode))
6788
// process any partial progress saved
6791
res = RemovePartOfBlock(curBlock, firstNode, lastNode);
6792
if (NS_FAILED(res)) return res;
6793
curBlock = 0; firstNode = 0; lastNode = 0;
6796
nsCOMArray<nsIDOMNode> childArray;
6797
res = GetChildNodesForOperation(curNode, childArray);
6798
if (NS_FAILED(res)) return res;
6799
res = RemoveBlockStyle(childArray);
6800
if (NS_FAILED(res)) return res;
6802
else if (IsInlineNode(curNode))
6806
// if so, is this node a descendant?
6807
if (nsEditorUtils::IsDescendantOf(curNode, curBlock))
6810
continue; // then we dont need to do anything different for this node
6814
// otherwise, we have progressed beyond end of curBlock,
6815
// so lets handle it now. We need to remove the portion of
6816
// curBlock that contains [firstNode - lastNode].
6817
res = RemovePartOfBlock(curBlock, firstNode, lastNode);
6818
if (NS_FAILED(res)) return res;
6819
curBlock = 0; firstNode = 0; lastNode = 0;
6820
// fall out and handle curNode
6823
curBlock = mHTMLEditor->GetBlockNodeParent(curNode);
6824
if (nsHTMLEditUtils::IsFormatNode(curBlock))
6826
firstNode = curNode;
6830
curBlock = 0; // not a block kind that we care about.
6833
{ // some node that is already sans block style. skip over it and
6834
// process any partial progress saved
6837
res = RemovePartOfBlock(curBlock, firstNode, lastNode);
6838
if (NS_FAILED(res)) return res;
6839
curBlock = 0; firstNode = 0; lastNode = 0;
6843
// process any partial progress saved
6846
res = RemovePartOfBlock(curBlock, firstNode, lastNode);
6847
if (NS_FAILED(res)) return res;
6848
curBlock = 0; firstNode = 0; lastNode = 0;
6854
///////////////////////////////////////////////////////////////////////////
6855
// ApplyBlockStyle: do whatever it takes to make the list of nodes into
6856
// one or more blocks of type blockTag.
6859
nsHTMLEditRules::ApplyBlockStyle(nsCOMArray<nsIDOMNode>& arrayOfNodes, const nsAString *aBlockTag)
6861
// intent of this routine is to be used for converting to/from
6862
// headers, paragraphs, pre, and address. Those blocks
6863
// that pretty much just contain inline things...
6865
if (!aBlockTag) return NS_ERROR_NULL_POINTER;
6866
nsresult res = NS_OK;
6868
nsCOMPtr<nsIDOMNode> curNode, curParent, curBlock, newBlock;
6870
PRInt32 listCount = arrayOfNodes.Count();
6871
nsString tString(*aBlockTag);////MJUDGE SCC NEED HELP
6873
// Remove all non-editable nodes. Leave them be.
6875
for (j=listCount-1; j>=0; j--)
6877
if (!mHTMLEditor->IsEditable(arrayOfNodes[j]))
6879
arrayOfNodes.RemoveObjectAt(j);
6884
listCount = arrayOfNodes.Count();
6887
for (i=0; i<listCount; i++)
6889
// get the node to act on, and it's location
6890
curNode = arrayOfNodes[i];
6891
res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset);
6892
if (NS_FAILED(res)) return res;
6893
nsAutoString curNodeTag;
6894
nsEditor::GetTagString(curNode, curNodeTag);
6895
ToLowerCase(curNodeTag);
6897
// is it already the right kind of block?
6898
if (curNodeTag == *aBlockTag)
6900
curBlock = 0; // forget any previous block used for previous inline nodes
6901
continue; // do nothing to this block
6904
// if curNode is a address, p, header, address, or pre, replace
6905
// it with a new block of correct type.
6906
// xxx floppy moose: pre cant hold everything the others can
6907
if (nsHTMLEditUtils::IsMozDiv(curNode) ||
6908
nsHTMLEditUtils::IsFormatNode(curNode))
6910
curBlock = 0; // forget any previous block used for previous inline nodes
6911
res = mHTMLEditor->ReplaceContainer(curNode, address_of(newBlock), *aBlockTag);
6912
if (NS_FAILED(res)) return res;
6914
else if (nsHTMLEditUtils::IsTable(curNode) ||
6915
(curNodeTag.Equals(NS_LITERAL_STRING("tbody"))) ||
6916
(curNodeTag.Equals(NS_LITERAL_STRING("tr"))) ||
6917
(curNodeTag.Equals(NS_LITERAL_STRING("td"))) ||
6918
nsHTMLEditUtils::IsList(curNode) ||
6919
(curNodeTag.Equals(NS_LITERAL_STRING("li"))) ||
6920
nsHTMLEditUtils::IsBlockquote(curNode) ||
6921
nsHTMLEditUtils::IsDiv(curNode))
6923
curBlock = 0; // forget any previous block used for previous inline nodes
6925
nsCOMArray<nsIDOMNode> childArray;
6926
res = GetChildNodesForOperation(curNode, childArray);
6927
if (NS_FAILED(res)) return res;
6928
PRInt32 childCount = childArray.Count();
6931
res = ApplyBlockStyle(childArray, aBlockTag);
6932
if (NS_FAILED(res)) return res;
6936
// make sure we can put a block here
6937
res = SplitAsNeeded(aBlockTag, address_of(curParent), &offset);
6938
if (NS_FAILED(res)) return res;
6939
nsCOMPtr<nsIDOMNode> theBlock;
6940
res = mHTMLEditor->CreateNode(*aBlockTag, curParent, offset, getter_AddRefs(theBlock));
6941
if (NS_FAILED(res)) return res;
6942
// remember our new block for postprocessing
6943
mNewBlock = theBlock;
6947
// if the node is a break, we honor it by putting further nodes in a new parent
6948
else if (curNodeTag.Equals(NS_LITERAL_STRING("br")))
6952
curBlock = 0; // forget any previous block used for previous inline nodes
6953
res = mHTMLEditor->DeleteNode(curNode);
6954
if (NS_FAILED(res)) return res;
6958
// the break is the first (or even only) node we encountered. Create a
6960
res = SplitAsNeeded(aBlockTag, address_of(curParent), &offset);
6961
if (NS_FAILED(res)) return res;
6962
res = mHTMLEditor->CreateNode(*aBlockTag, curParent, offset, getter_AddRefs(curBlock));
6963
if (NS_FAILED(res)) return res;
6964
// remember our new block for postprocessing
6965
mNewBlock = curBlock;
6966
// note: doesn't matter if we set mNewBlock multiple times.
6967
res = mHTMLEditor->MoveNode(curNode, curBlock, -1);
6968
if (NS_FAILED(res)) return res;
6973
// if curNode is inline, pull it into curBlock
6974
// note: it's assumed that consecutive inline nodes in the
6975
// arrayOfNodes are actually members of the same block parent.
6976
// this happens to be true now as a side effect of how
6977
// arrayOfNodes is contructed, but some additional logic should
6978
// be added here if that should change
6980
else if (IsInlineNode(curNode))
6982
// if curNode is a non editable, drop it if we are going to <pre>
6983
if (tString.Equals(NS_LITERAL_STRING("pre"),nsCaseInsensitiveStringComparator())
6984
&& (!mHTMLEditor->IsEditable(curNode)))
6985
continue; // do nothing to this block
6987
// if no curBlock, make one
6990
res = SplitAsNeeded(aBlockTag, address_of(curParent), &offset);
6991
if (NS_FAILED(res)) return res;
6992
res = mHTMLEditor->CreateNode(*aBlockTag, curParent, offset, getter_AddRefs(curBlock));
6993
if (NS_FAILED(res)) return res;
6994
// remember our new block for postprocessing
6995
mNewBlock = curBlock;
6996
// note: doesn't matter if we set mNewBlock multiple times.
6999
// if curNode is a Break, replace it with a return if we are going to <pre>
7002
// this is a continuation of some inline nodes that belong together in
7003
// the same block item. use curBlock
7004
res = mHTMLEditor->MoveNode(curNode, curBlock, -1);
7005
if (NS_FAILED(res)) return res;
7012
///////////////////////////////////////////////////////////////////////////
7013
// SplitAsNeeded: given a tag name, split inOutParent up to the point
7014
// where we can insert the tag. Adjust inOutParent and
7015
// inOutOffset to pint to new location for tag.
7017
nsHTMLEditRules::SplitAsNeeded(const nsAString *aTag,
7018
nsCOMPtr<nsIDOMNode> *inOutParent,
7019
PRInt32 *inOutOffset)
7021
if (!aTag || !inOutParent || !inOutOffset) return NS_ERROR_NULL_POINTER;
7022
if (!*inOutParent) return NS_ERROR_NULL_POINTER;
7023
nsCOMPtr<nsIDOMNode> tagParent, temp, splitNode, parent = *inOutParent;
7024
nsresult res = NS_OK;
7026
// check that we have a place that can legally contain the tag
7029
// sniffing up the parent tree until we find
7030
// a legal place for the block
7032
if (mHTMLEditor->CanContainTag(parent, *aTag))
7038
parent->GetParentNode(getter_AddRefs(temp));
7043
// could not find a place to build tag!
7044
return NS_ERROR_FAILURE;
7048
// we found a place for block, but above inOutParent. We need to split nodes.
7049
res = mHTMLEditor->SplitNodeDeep(splitNode, *inOutParent, *inOutOffset, inOutOffset);
7050
if (NS_FAILED(res)) return res;
7051
*inOutParent = tagParent;
7056
///////////////////////////////////////////////////////////////////////////
7057
// JoinNodesSmart: join two nodes, doing whatever makes sense for their
7058
// children (which often means joining them, too).
7059
// aNodeLeft & aNodeRight must be same type of node.
7061
nsHTMLEditRules::JoinNodesSmart( nsIDOMNode *aNodeLeft,
7062
nsIDOMNode *aNodeRight,
7063
nsCOMPtr<nsIDOMNode> *aOutMergeParent,
7064
PRInt32 *aOutMergeOffset)
7071
return NS_ERROR_NULL_POINTER;
7073
nsresult res = NS_OK;
7074
// caller responsible for:
7075
// left & right node are same type
7077
nsCOMPtr<nsIDOMNode> parent, rightParent;
7078
res = nsEditor::GetNodeLocation(aNodeLeft, address_of(parent), &parOffset);
7079
if (NS_FAILED(res)) return res;
7080
aNodeRight->GetParentNode(getter_AddRefs(rightParent));
7082
// if they don't have the same parent, first move the 'right' node
7083
// to after the 'left' one
7084
if (parent != rightParent)
7086
res = mHTMLEditor->MoveNode(aNodeRight, parent, parOffset);
7087
if (NS_FAILED(res)) return res;
7090
// defaults for outParams
7091
*aOutMergeParent = aNodeRight;
7092
res = mHTMLEditor->GetLengthOfDOMNode(aNodeLeft, *((PRUint32*)aOutMergeOffset));
7093
if (NS_FAILED(res)) return res;
7095
// separate join rules for differing blocks
7096
if (nsHTMLEditUtils::IsParagraph(aNodeLeft))
7098
// for para's, merge deep & add a <br> after merging
7099
res = mHTMLEditor->JoinNodeDeep(aNodeLeft, aNodeRight, aOutMergeParent, aOutMergeOffset);
7100
if (NS_FAILED(res)) return res;
7101
// now we need to insert a br.
7102
nsCOMPtr<nsIDOMNode> brNode;
7103
res = mHTMLEditor->CreateBR(*aOutMergeParent, *aOutMergeOffset, address_of(brNode));
7104
if (NS_FAILED(res)) return res;
7105
res = nsEditor::GetNodeLocation(brNode, aOutMergeParent, aOutMergeOffset);
7106
if (NS_FAILED(res)) return res;
7107
(*aOutMergeOffset)++;
7110
else if (nsHTMLEditUtils::IsList(aNodeLeft)
7111
|| mHTMLEditor->IsTextNode(aNodeLeft))
7113
// for list's, merge shallow (wouldn't want to combine list items)
7114
res = mHTMLEditor->JoinNodes(aNodeLeft, aNodeRight, parent);
7115
if (NS_FAILED(res)) return res;
7120
// remember the last left child, and firt right child
7121
nsCOMPtr<nsIDOMNode> lastLeft, firstRight;
7122
res = mHTMLEditor->GetLastEditableChild(aNodeLeft, address_of(lastLeft));
7123
if (NS_FAILED(res)) return res;
7124
res = mHTMLEditor->GetFirstEditableChild(aNodeRight, address_of(firstRight));
7125
if (NS_FAILED(res)) return res;
7127
// for list items, divs, etc, merge smart
7128
res = mHTMLEditor->JoinNodes(aNodeLeft, aNodeRight, parent);
7129
if (NS_FAILED(res)) return res;
7131
if (lastLeft && firstRight && mHTMLEditor->NodesSameType(lastLeft, firstRight))
7133
return JoinNodesSmart(lastLeft, firstRight, aOutMergeParent, aOutMergeOffset);
7141
nsHTMLEditRules::GetTopEnclosingMailCite(nsIDOMNode *aNode,
7142
nsCOMPtr<nsIDOMNode> *aOutCiteNode,
7146
if (!aNode || !aOutCiteNode)
7147
return NS_ERROR_NULL_POINTER;
7149
nsresult res = NS_OK;
7150
nsCOMPtr<nsIDOMNode> node, parentNode;
7151
node = do_QueryInterface(aNode);
7155
if ( (aPlainText && nsHTMLEditUtils::IsPre(node)) ||
7156
nsHTMLEditUtils::IsMailCite(node) )
7157
*aOutCiteNode = node;
7158
if (nsTextEditUtils::IsBody(node)) break;
7160
res = node->GetParentNode(getter_AddRefs(parentNode));
7161
if (NS_FAILED(res)) return res;
7170
nsHTMLEditRules::CacheInlineStyles(nsIDOMNode *aNode)
7172
if (!aNode) return NS_ERROR_NULL_POINTER;
7175
mHTMLEditor->GetIsCSSEnabled(&useCSS);
7178
for (j=0; j<SIZE_STYLE_TABLE; j++)
7180
PRBool isSet = PR_FALSE;
7181
nsAutoString outValue;
7182
nsCOMPtr<nsIDOMNode> resultNode;
7185
mHTMLEditor->IsTextPropertySetByContent(aNode, mCachedStyles[j].tag, &(mCachedStyles[j].attr), nsnull,
7186
isSet, getter_AddRefs(resultNode), &outValue);
7190
mHTMLEditor->mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(aNode, mCachedStyles[j].tag, &(mCachedStyles[j].attr),
7191
isSet, outValue, COMPUTED_STYLE_TYPE);
7195
mCachedStyles[j].mPresent = PR_TRUE;
7196
mCachedStyles[j].value.Assign(outValue);
7204
nsHTMLEditRules::ReapplyCachedStyles()
7206
// The idea here is to examine our cached list of styles
7207
// and see if any have been removed. If so, add typeinstate
7208
// for them, so that they will be reinserted when new
7209
// content is added.
7211
// When we apply cached styles to TypeInState, we always want
7212
// to blow away prior TypeInState:
7213
mHTMLEditor->mTypeInState->Reset();
7215
// remember if we are in css mode
7217
mHTMLEditor->GetIsCSSEnabled(&useCSS);
7219
// get selection point
7220
nsCOMPtr<nsISelection>selection;
7221
nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
7222
if (NS_FAILED(res)) return res;
7223
nsCOMPtr<nsIDOMNode> selNode;
7225
res = mHTMLEditor->GetStartNodeAndOffset(selection, address_of(selNode), &selOffset);
7226
if (NS_FAILED(res)) return res;
7230
for (j=0; j<SIZE_STYLE_TABLE; j++)
7232
if (mCachedStyles[j].mPresent)
7234
PRBool bFirst, bAny, bAll;
7235
bFirst = bAny = bAll = PR_FALSE;
7237
nsAutoString curValue;
7238
if (useCSS) // check computed style first in css case
7240
mHTMLEditor->mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(selNode, mCachedStyles[j].tag, &(mCachedStyles[j].attr),
7241
bAny, curValue, COMPUTED_STYLE_TYPE);
7243
if (!bAny) // then check typeinstate and html style
7245
res = mHTMLEditor->GetInlinePropertyBase(mCachedStyles[j].tag, &(mCachedStyles[j].attr), &(mCachedStyles[j].value),
7246
&bFirst, &bAny, &bAll, &curValue, PR_FALSE);
7247
if (NS_FAILED(res)) return res;
7249
// this style has disappeared through deletion. Add it onto our typeinstate:
7252
mHTMLEditor->mTypeInState->SetProp(mCachedStyles[j].tag, mCachedStyles[j].attr, mCachedStyles[j].value);
7261
nsHTMLEditRules::ClearCachedStyles()
7263
// clear the mPresent bits in mCachedStyles array
7266
for (j=0; j<SIZE_STYLE_TABLE; j++)
7268
mCachedStyles[j].mPresent = PR_FALSE;
7269
mCachedStyles[j].value.Truncate(0);
7276
nsHTMLEditRules::AdjustSpecialBreaks(PRBool aSafeToAskFrames)
7278
nsCOMArray<nsIDOMNode> arrayOfNodes;
7279
nsCOMPtr<nsISupports> isupports;
7280
PRInt32 nodeCount,j;
7282
// gather list of empty nodes
7283
nsEmptyFunctor functor(mHTMLEditor);
7285
nsresult res = iter.Init(mDocChangeRange);
7286
if (NS_FAILED(res)) return res;
7287
res = iter.AppendList(functor, arrayOfNodes);
7288
if (NS_FAILED(res)) return res;
7290
// put moz-br's into these empty li's and td's
7291
nodeCount = arrayOfNodes.Count();
7292
for (j = 0; j < nodeCount; j++)
7294
// need to put br at END of node. It may have
7295
// empty containers in it and still pass the "IsEmptynode" test,
7296
// and we want the br's to be after them. Also, we want the br
7297
// to be after the selection if the selection is in this node.
7299
nsCOMPtr<nsIDOMNode> brNode, theNode = arrayOfNodes[0];
7300
arrayOfNodes.RemoveObjectAt(0);
7301
res = nsEditor::GetLengthOfDOMNode(theNode, len);
7302
if (NS_FAILED(res)) return res;
7303
res = CreateMozBR(theNode, (PRInt32)len, address_of(brNode));
7304
if (NS_FAILED(res)) return res;
7311
nsHTMLEditRules::AdjustWhitespace(nsISelection *aSelection)
7313
// get selection point
7314
nsCOMPtr<nsIDOMNode> selNode;
7316
nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
7317
if (NS_FAILED(res)) return res;
7319
// ask whitespace object to tweak nbsp's
7320
return nsWSRunObject(mHTMLEditor, selNode, selOffset).AdjustWhitespace();
7324
nsHTMLEditRules::PinSelectionToNewBlock(nsISelection *aSelection)
7326
if (!aSelection) return NS_ERROR_NULL_POINTER;
7328
nsresult res = aSelection->GetIsCollapsed(&bCollapsed);
7329
if (NS_FAILED(res)) return res;
7330
if (!bCollapsed) return res;
7332
// get the (collapsed) selection location
7333
nsCOMPtr<nsIDOMNode> selNode, temp;
7335
res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
7336
if (NS_FAILED(res)) return res;
7339
// use ranges and mRangeHelper to compare sel point to new block
7340
nsCOMPtr<nsIDOMRange> range = do_CreateInstance("@mozilla.org/content/range;1");
7341
res = range->SetStart(selNode, selOffset);
7342
if (NS_FAILED(res)) return res;
7343
res = range->SetEnd(selNode, selOffset);
7344
if (NS_FAILED(res)) return res;
7345
nsCOMPtr<nsIContent> block (do_QueryInterface(mNewBlock));
7346
if (!block) return NS_ERROR_NO_INTERFACE;
7347
PRBool nodeBefore, nodeAfter;
7348
res = mHTMLEditor->mRangeHelper->CompareNodeToRange(block, range, &nodeBefore, &nodeAfter);
7349
if (NS_FAILED(res)) return res;
7351
if (nodeBefore && nodeAfter)
7352
return NS_OK; // selection is inside block
7353
else if (nodeBefore)
7355
// selection is after block. put at end of block.
7356
nsCOMPtr<nsIDOMNode> tmp = mNewBlock;
7357
mHTMLEditor->GetLastEditableChild(mNewBlock, address_of(tmp));
7359
if (mHTMLEditor->IsTextNode(tmp) || mHTMLEditor->IsContainer(tmp))
7361
res = nsEditor::GetLengthOfDOMNode(tmp, endPoint);
7362
if (NS_FAILED(res)) return res;
7366
nsCOMPtr<nsIDOMNode> tmp2;
7367
res = nsEditor::GetNodeLocation(tmp, address_of(tmp2), (PRInt32*)&endPoint);
7368
if (NS_FAILED(res)) return res;
7370
endPoint++; // want to be after this node
7372
return aSelection->Collapse(tmp, (PRInt32)endPoint);
7376
// selection is before block. put at start of block.
7377
nsCOMPtr<nsIDOMNode> tmp = mNewBlock;
7378
mHTMLEditor->GetFirstEditableChild(mNewBlock, address_of(tmp));
7380
if (!(mHTMLEditor->IsTextNode(tmp) || mHTMLEditor->IsContainer(tmp)))
7382
nsCOMPtr<nsIDOMNode> tmp2;
7383
res = nsEditor::GetNodeLocation(tmp, address_of(tmp2), &offset);
7384
if (NS_FAILED(res)) return res;
7387
return aSelection->Collapse(tmp, 0);
7392
nsHTMLEditRules::CheckInterlinePosition(nsISelection *aSelection)
7394
if (!aSelection) return NS_ERROR_NULL_POINTER;
7395
nsCOMPtr<nsISelection> selection(aSelection);
7396
nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
7398
// if the selection isn't collapsed, do nothing.
7400
nsresult res = aSelection->GetIsCollapsed(&bCollapsed);
7401
if (NS_FAILED(res)) return res;
7402
if (!bCollapsed) return res;
7404
// get the (collapsed) selection location
7405
nsCOMPtr<nsIDOMNode> selNode, node;
7407
res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
7408
if (NS_FAILED(res)) return res;
7410
// are we after a block? If so try set caret to following content
7411
mHTMLEditor->GetPriorHTMLSibling(selNode, selOffset, address_of(node));
7412
if (node && IsBlockNode(node))
7414
selPriv->SetInterlinePosition(PR_TRUE);
7418
// are we before a block? If so try set caret to prior content
7419
mHTMLEditor->GetNextHTMLSibling(selNode, selOffset, address_of(node));
7420
if (node && IsBlockNode(node))
7422
selPriv->SetInterlinePosition(PR_FALSE);
7426
// are we after a <br>? If so we want to stick to whatever is after <br>.
7427
mHTMLEditor->GetPriorHTMLNode(selNode, selOffset, address_of(node), PR_TRUE);
7428
if (node && nsTextEditUtils::IsBreak(node))
7429
selPriv->SetInterlinePosition(PR_TRUE);
7434
nsHTMLEditRules::AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection aAction)
7436
if (!aSelection) return NS_ERROR_NULL_POINTER;
7437
nsCOMPtr<nsISelection> selection(aSelection);
7438
nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
7440
// if the selection isn't collapsed, do nothing.
7441
// moose: one thing to do instead is check for the case of
7442
// only a single break selected, and collapse it. Good thing? Beats me.
7444
nsresult res = aSelection->GetIsCollapsed(&bCollapsed);
7445
if (NS_FAILED(res)) return res;
7446
if (!bCollapsed) return res;
7448
// get the (collapsed) selection location
7449
nsCOMPtr<nsIDOMNode> selNode, temp;
7451
res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
7452
if (NS_FAILED(res)) return res;
7455
// are we in an editable node?
7456
while (!mHTMLEditor->IsEditable(selNode))
7458
// scan up the tree until we find an editable place to be
7459
res = nsEditor::GetNodeLocation(temp, address_of(selNode), &selOffset);
7460
if (NS_FAILED(res)) return res;
7461
if (!selNode) return NS_ERROR_FAILURE;
7465
// make sure we aren't in an empty block - user will see no cursor. If this
7466
// is happening, put a <br> in the block if allowed.
7467
nsCOMPtr<nsIDOMNode> theblock;
7468
if (IsBlockNode(selNode)) theblock = selNode;
7469
else theblock = mHTMLEditor->GetBlockNodeParent(selNode);
7470
PRBool bIsEmptyNode;
7471
res = mHTMLEditor->IsEmptyNode(theblock, &bIsEmptyNode, PR_FALSE, PR_FALSE);
7472
if (NS_FAILED(res)) return res;
7473
// check if br can go into the destination node
7474
if (bIsEmptyNode && mHTMLEditor->CanContainTag(selNode, NS_LITERAL_STRING("br")))
7476
nsCOMPtr<nsIDOMElement> rootElement;
7477
res = mHTMLEditor->GetRootElement(getter_AddRefs(rootElement));
7478
if (NS_FAILED(res)) return res;
7479
if (!rootElement) return NS_ERROR_FAILURE;
7480
nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(rootElement));
7481
if (selNode == rootNode)
7483
// Our root node is completely empty. Don't add a <br> here.
7484
// AfterEditInner() will add one for us when it calls
7485
// CreateBogusNodeIfNeeded()!
7489
nsCOMPtr<nsIDOMNode> brNode;
7490
// we know we can skip the rest of this routine given the cirumstance
7491
return CreateMozBR(selNode, selOffset, address_of(brNode));
7494
// are we in a text node?
7495
nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(selNode);
7497
return NS_OK; // we LIKE it when we are in a text node. that RULZ
7499
// do we need to insert a special mozBR? We do if we are:
7500
// 1) prior node is in same block where selection is AND
7501
// 2) prior node is a br AND
7502
// 3) that br is not visible
7504
nsCOMPtr<nsIDOMNode> nearNode;
7505
res = mHTMLEditor->GetPriorHTMLNode(selNode, selOffset, address_of(nearNode));
7506
if (NS_FAILED(res)) return res;
7509
// is nearNode also a descendant of same block?
7510
nsCOMPtr<nsIDOMNode> block, nearBlock;
7511
if (IsBlockNode(selNode)) block = selNode;
7512
else block = mHTMLEditor->GetBlockNodeParent(selNode);
7513
nearBlock = mHTMLEditor->GetBlockNodeParent(nearNode);
7514
if (block == nearBlock)
7516
if (nearNode && nsTextEditUtils::IsBreak(nearNode) )
7518
if (!mHTMLEditor->IsVisBreak(nearNode))
7520
// need to insert special moz BR. Why? Because if we don't
7521
// the user will see no new line for the break. Also, things
7522
// like table cells won't grow in height.
7523
nsCOMPtr<nsIDOMNode> brNode;
7524
res = CreateMozBR(selNode, selOffset, address_of(brNode));
7525
if (NS_FAILED(res)) return res;
7526
res = nsEditor::GetNodeLocation(brNode, address_of(selNode), &selOffset);
7527
if (NS_FAILED(res)) return res;
7528
// selection stays *before* moz-br, sticking to it
7529
selPriv->SetInterlinePosition(PR_TRUE);
7530
res = aSelection->Collapse(selNode,selOffset);
7531
if (NS_FAILED(res)) return res;
7535
nsCOMPtr<nsIDOMNode> nextNode;
7536
mHTMLEditor->GetNextHTMLNode(nearNode, address_of(nextNode), PR_TRUE);
7537
if (nextNode && nsTextEditUtils::IsMozBR(nextNode))
7539
// selection between br and mozbr. make it stick to mozbr
7540
// so that it will be on blank line.
7541
selPriv->SetInterlinePosition(PR_TRUE);
7548
// we aren't in a textnode: are we adjacent to text or a break or an image?
7549
res = mHTMLEditor->GetPriorHTMLNode(selNode, selOffset, address_of(nearNode), PR_TRUE);
7550
if (NS_FAILED(res)) return res;
7551
if (nearNode && (nsTextEditUtils::IsBreak(nearNode)
7552
|| nsEditor::IsTextNode(nearNode)
7553
|| nsHTMLEditUtils::IsImage(nearNode)
7554
|| nsHTMLEditUtils::IsHR(nearNode)))
7555
return NS_OK; // this is a good place for the caret to be
7556
res = mHTMLEditor->GetNextHTMLNode(selNode, selOffset, address_of(nearNode), PR_TRUE);
7557
if (NS_FAILED(res)) return res;
7558
if (nearNode && (nsTextEditUtils::IsBreak(nearNode)
7559
|| nsEditor::IsTextNode(nearNode)
7560
|| nsHTMLEditUtils::IsImage(nearNode)
7561
|| nsHTMLEditUtils::IsHR(nearNode)))
7562
return NS_OK; // this is a good place for the caret to be
7564
// look for a nearby text node.
7565
// prefer the correct direction.
7566
res = FindNearSelectableNode(selNode, selOffset, aAction, address_of(nearNode));
7567
if (NS_FAILED(res)) return res;
7571
// is the nearnode a text node?
7572
textNode = do_QueryInterface(nearNode);
7576
// put selection in right place:
7577
if (aAction == nsIEditor::ePrevious)
7578
textNode->GetLength((PRUint32*)&offset);
7579
res = aSelection->Collapse(nearNode,offset);
7581
else // must be break or image
7583
res = nsEditor::GetNodeLocation(nearNode, address_of(selNode), &selOffset);
7584
if (NS_FAILED(res)) return res;
7585
if (aAction == nsIEditor::ePrevious) selOffset++; // want to be beyond it if we backed up to it
7586
res = aSelection->Collapse(selNode, selOffset);
7594
nsHTMLEditRules::FindNearSelectableNode(nsIDOMNode *aSelNode,
7596
nsIEditor::EDirection &aDirection,
7597
nsCOMPtr<nsIDOMNode> *outSelectableNode)
7599
if (!aSelNode || !outSelectableNode) return NS_ERROR_NULL_POINTER;
7600
*outSelectableNode = nsnull;
7601
nsresult res = NS_OK;
7603
nsCOMPtr<nsIDOMNode> nearNode, curNode;
7604
if (aDirection == nsIEditor::ePrevious)
7605
res = mHTMLEditor->GetPriorHTMLNode(aSelNode, aSelOffset, address_of(nearNode));
7607
res = mHTMLEditor->GetNextHTMLNode(aSelNode, aSelOffset, address_of(nearNode));
7608
if (NS_FAILED(res)) return res;
7610
if (!nearNode) // try the other direction then
7612
if (aDirection == nsIEditor::ePrevious)
7613
aDirection = nsIEditor::eNext;
7615
aDirection = nsIEditor::ePrevious;
7617
if (aDirection == nsIEditor::ePrevious)
7618
res = mHTMLEditor->GetPriorHTMLNode(aSelNode, aSelOffset, address_of(nearNode));
7620
res = mHTMLEditor->GetNextHTMLNode(aSelNode, aSelOffset, address_of(nearNode));
7621
if (NS_FAILED(res)) return res;
7624
// scan in the right direction until we find an eligible text node,
7625
// but dont cross any breaks, images, or table elements.
7626
while (nearNode && !(mHTMLEditor->IsTextNode(nearNode)
7627
|| nsTextEditUtils::IsBreak(nearNode)
7628
|| nsHTMLEditUtils::IsImage(nearNode)))
7631
if (aDirection == nsIEditor::ePrevious)
7632
res = mHTMLEditor->GetPriorHTMLNode(curNode, address_of(nearNode));
7634
res = mHTMLEditor->GetNextHTMLNode(curNode, address_of(nearNode));
7635
if (NS_FAILED(res)) return res;
7640
// dont cross any table elements
7641
PRBool bInDifTblElems;
7642
res = InDifferentTableElements(nearNode, aSelNode, &bInDifTblElems);
7643
if (NS_FAILED(res)) return res;
7644
if (bInDifTblElems) return NS_OK;
7646
// otherwise, ok, we have found a good spot to put the selection
7647
*outSelectableNode = do_QueryInterface(nearNode);
7654
nsHTMLEditRules::InDifferentTableElements(nsIDOMNode *aNode1, nsIDOMNode *aNode2, PRBool *aResult)
7656
NS_ASSERTION(aNode1 && aNode2 && aResult, "null args");
7657
if (!aNode1 || !aNode2 || !aResult) return NS_ERROR_NULL_POINTER;
7659
nsCOMPtr<nsIDOMNode> tn1, tn2, node = aNode1, temp;
7660
*aResult = PR_FALSE;
7662
while (node && !nsHTMLEditUtils::IsTableElement(node))
7664
node->GetParentNode(getter_AddRefs(temp));
7670
while (node && !nsHTMLEditUtils::IsTableElement(node))
7672
node->GetParentNode(getter_AddRefs(temp));
7677
*aResult = (tn1 != tn2);
7684
nsHTMLEditRules::RemoveEmptyNodes()
7686
nsCOMArray<nsIDOMNode> arrayOfEmptyNodes, arrayOfEmptyCites;
7687
nsCOMPtr<nsISupports> isupports;
7688
PRInt32 nodeCount,j;
7690
// some general notes on the algorithm used here: the goal is to examine all the
7691
// nodes in mDocChangeRange, and remove the empty ones. We do this by using a
7692
// content iterator to traverse all the nodes in the range, and placing the empty
7693
// nodes into an array. After finishing the iteration, we delete the empty nodes
7694
// in the array. (they cannot be deleted as we find them becasue that would
7695
// invalidate the iterator.)
7696
// Since checking to see if a node is empty can be costly for nodes with many
7697
// descendants, there are some optimizations made. I rely on the fact that the
7698
// iterator is post-order: it will visit children of a node before visiting the
7699
// parent node. So if I find that a child node is not empty, I know that it's
7700
// parent is not empty without even checking. So I put the parent on a "skipList"
7701
// which is just a voidArray of nodes I can skip the empty check on. If I
7702
// encounter a node on the skiplist, i skip the processing for that node and replace
7703
// it's slot in the skiplist with that node's parent.
7704
// An interseting idea is to go ahead and regard parent nodes that are NOT on the
7705
// skiplist as being empty (without even doing the IsEmptyNode check) on the theory
7706
// that if they weren't empty, we would have encountered a non-empty child earlier
7707
// and thus put this parent node on the skiplist.
7708
// Unfortunately I can't use that strategy here, because the range may include
7709
// some children of a node while excluding others. Thus I could find all the
7710
// _examined_ children empty, but still not have an empty parent.
7713
nsCOMPtr<nsIContentIterator> iter =
7714
do_CreateInstance("@mozilla.org/content/post-content-iterator;1");
7715
if (!iter) return NS_ERROR_NULL_POINTER;
7717
nsresult res = iter->Init(mDocChangeRange);
7718
if (NS_FAILED(res)) return res;
7720
nsVoidArray skipList;
7722
// check for empty nodes
7723
while (!iter->IsDone())
7725
nsCOMPtr<nsIDOMNode> node, parent;
7727
node = do_QueryInterface(iter->GetCurrentNode());
7729
return NS_ERROR_FAILURE;
7731
node->GetParentNode(getter_AddRefs(parent));
7733
PRInt32 idx = skipList.IndexOf((void*)node);
7736
// this node is on our skip list. Skip processing for this node,
7737
// and replace it's value in the skip list with the value of it's parent
7738
skipList.ReplaceElementAt((void*)parent, idx);
7742
PRBool bIsCandidate = PR_FALSE;
7743
PRBool bIsEmptyNode = PR_FALSE;
7744
PRBool bIsMailCite = PR_FALSE;
7746
// don't delete the body
7747
if (!nsTextEditUtils::IsBody(node))
7749
// only consider certain nodes to be empty for purposes of removal
7750
if ( (bIsMailCite = nsHTMLEditUtils::IsMailCite(node)) ||
7751
nsEditor::NodeIsType(node, nsEditProperty::a) ||
7752
nsHTMLEditUtils::IsInlineStyle(node) ||
7753
nsHTMLEditUtils::IsList(node) ||
7754
nsHTMLEditUtils::IsDiv(node) )
7756
bIsCandidate = PR_TRUE;
7758
// these node types are candidates if selection is not in them
7759
else if (nsHTMLEditUtils::IsFormatNode(node) ||
7760
nsHTMLEditUtils::IsListItem(node) ||
7761
nsHTMLEditUtils::IsBlockquote(node) )
7763
// if it is one of these, don't delete if selection inside.
7764
// this is so we can create empty headings, etc, for the
7765
// user to type into.
7766
PRBool bIsSelInNode;
7767
res = SelectionEndpointInNode(node, &bIsSelInNode);
7768
if (NS_FAILED(res)) return res;
7771
bIsCandidate = PR_TRUE;
7778
if (bIsMailCite) // we delete mailcites even if they have a solo br in them
7779
res = mHTMLEditor->IsEmptyNode(node, &bIsEmptyNode, PR_TRUE, PR_TRUE);
7780
else // other nodes we require to be empty
7781
res = mHTMLEditor->IsEmptyNode(node, &bIsEmptyNode, PR_FALSE, PR_TRUE);
7782
if (NS_FAILED(res)) return res;
7785
if (bIsMailCite) // mailcites go on a separate list from other empty nodes
7787
arrayOfEmptyCites.AppendObject(node);
7791
arrayOfEmptyNodes.AppendObject(node);
7798
// put parent on skip list
7799
skipList.AppendElement((void*)parent);
7806
// now delete the empty nodes
7807
nodeCount = arrayOfEmptyNodes.Count();
7808
for (j = 0; j < nodeCount; j++)
7810
nsCOMPtr<nsIDOMNode> delNode = arrayOfEmptyNodes[0];
7811
arrayOfEmptyNodes.RemoveObjectAt(0);
7812
res = mHTMLEditor->DeleteNode(delNode);
7813
if (NS_FAILED(res)) return res;
7816
// now delete the empty mailcites
7817
// this is a separate step because we want to pull out any br's and preserve them.
7818
nodeCount = arrayOfEmptyCites.Count();
7819
for (j = 0; j < nodeCount; j++)
7821
nsCOMPtr<nsIDOMNode> delNode = arrayOfEmptyCites[0];
7822
arrayOfEmptyCites.RemoveObjectAt(0);
7823
PRBool bIsEmptyNode;
7824
res = mHTMLEditor->IsEmptyNode(delNode, &bIsEmptyNode, PR_FALSE, PR_TRUE);
7825
if (NS_FAILED(res)) return res;
7828
// we are deleting a cite that has just a br. We want to delete cite,
7830
nsCOMPtr<nsIDOMNode> parent, brNode;
7832
res = nsEditor::GetNodeLocation(delNode, address_of(parent), &offset);
7833
if (NS_FAILED(res)) return res;
7834
res = mHTMLEditor->CreateBR(parent, offset, address_of(brNode));
7835
if (NS_FAILED(res)) return res;
7837
res = mHTMLEditor->DeleteNode(delNode);
7838
if (NS_FAILED(res)) return res;
7845
nsHTMLEditRules::SelectionEndpointInNode(nsIDOMNode *aNode, PRBool *aResult)
7847
if (!aNode || !aResult) return NS_ERROR_NULL_POINTER;
7849
*aResult = PR_FALSE;
7851
nsCOMPtr<nsISelection>selection;
7852
nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
7853
if (NS_FAILED(res)) return res;
7854
nsCOMPtr<nsISelectionPrivate>selPriv(do_QueryInterface(selection));
7856
nsCOMPtr<nsIEnumerator> enumerator;
7857
res = selPriv->GetEnumerator(getter_AddRefs(enumerator));
7858
if (NS_FAILED(res)) return res;
7859
if (!enumerator) return NS_ERROR_UNEXPECTED;
7861
for (enumerator->First(); NS_OK!=enumerator->IsDone(); enumerator->Next())
7863
nsCOMPtr<nsISupports> currentItem;
7864
res = enumerator->CurrentItem(getter_AddRefs(currentItem));
7865
if (NS_FAILED(res)) return res;
7866
if (!currentItem) return NS_ERROR_UNEXPECTED;
7868
nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
7869
nsCOMPtr<nsIDOMNode> startParent, endParent;
7870
range->GetStartContainer(getter_AddRefs(startParent));
7873
if (aNode == startParent)
7878
if (nsEditorUtils::IsDescendantOf(startParent, aNode))
7884
range->GetEndContainer(getter_AddRefs(endParent));
7885
if (startParent == endParent) continue;
7888
if (aNode == endParent)
7893
if (nsEditorUtils::IsDescendantOf(endParent, aNode))
7903
///////////////////////////////////////////////////////////////////////////
7904
// IsEmptyInline: return true if aNode is an empty inline container
7908
nsHTMLEditRules::IsEmptyInline(nsIDOMNode *aNode)
7910
if (aNode && IsInlineNode(aNode) && mHTMLEditor->IsContainer(aNode))
7913
mHTMLEditor->IsEmptyNode(aNode, &bEmpty);
7921
nsHTMLEditRules::ListIsEmptyLine(nsCOMArray<nsIDOMNode> &arrayOfNodes)
7923
// we have a list of nodes which we are candidates for being moved
7924
// into a new block. Determine if it's anything more than a blank line.
7925
// Look for editable content above and beyond one single BR.
7926
PRInt32 listCount = arrayOfNodes.Count();
7927
if (!listCount) return PR_TRUE;
7928
nsCOMPtr<nsIDOMNode> somenode;
7929
PRInt32 j, brCount=0;
7930
for (j = 0; j < listCount; j++)
7932
somenode = arrayOfNodes[j];
7933
if (somenode && mHTMLEditor->IsEditable(somenode))
7935
if (nsTextEditUtils::IsBreak(somenode))
7937
// first break doesn't count
7938
if (brCount) return PR_FALSE;
7941
else if (IsEmptyInline(somenode))
7943
// empty inline, keep looking
7945
else return PR_FALSE;
7953
nsHTMLEditRules::PopListItem(nsIDOMNode *aListItem, PRBool *aOutOfList)
7956
if (!aListItem || !aOutOfList)
7957
return NS_ERROR_NULL_POINTER;
7960
*aOutOfList = PR_FALSE;
7962
nsCOMPtr<nsIDOMNode> curParent;
7963
nsCOMPtr<nsIDOMNode> curNode( do_QueryInterface(aListItem));
7965
nsresult res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset);
7966
if (NS_FAILED(res)) return res;
7968
if (!nsHTMLEditUtils::IsListItem(curNode))
7969
return NS_ERROR_FAILURE;
7971
// if it's first or last list item, dont need to split the list
7973
nsCOMPtr<nsIDOMNode> curParPar;
7975
res = nsEditor::GetNodeLocation(curParent, address_of(curParPar), &parOffset);
7976
if (NS_FAILED(res)) return res;
7978
PRBool bIsFirstListItem;
7979
res = mHTMLEditor->IsFirstEditableChild(curNode, &bIsFirstListItem);
7980
if (NS_FAILED(res)) return res;
7982
PRBool bIsLastListItem;
7983
res = mHTMLEditor->IsLastEditableChild(curNode, &bIsLastListItem);
7984
if (NS_FAILED(res)) return res;
7986
if (!bIsFirstListItem && !bIsLastListItem)
7989
nsCOMPtr<nsIDOMNode> newBlock;
7990
res = mHTMLEditor->SplitNode(curParent, offset, getter_AddRefs(newBlock));
7991
if (NS_FAILED(res)) return res;
7994
if (!bIsFirstListItem) parOffset++;
7996
res = mHTMLEditor->MoveNode(curNode, curParPar, parOffset);
7997
if (NS_FAILED(res)) return res;
7999
// unwrap list item contents if they are no longer in a list
8000
if (!nsHTMLEditUtils::IsList(curParPar)
8001
&& nsHTMLEditUtils::IsListItem(curNode))
8003
res = mHTMLEditor->RemoveBlockContainer(curNode);
8004
if (NS_FAILED(res)) return res;
8005
*aOutOfList = PR_TRUE;
8011
nsHTMLEditRules::RemoveListStructure(nsIDOMNode *aList)
8013
NS_ENSURE_ARG_POINTER(aList);
8017
nsCOMPtr<nsIDOMNode> child;
8018
aList->GetFirstChild(getter_AddRefs(child));
8022
if (nsHTMLEditUtils::IsListItem(child))
8027
res = PopListItem(child, &bOutOfList);
8028
if (NS_FAILED(res)) return res;
8029
} while (!bOutOfList); // keep popping it out until it's not in a list anymore
8031
else if (nsHTMLEditUtils::IsList(child))
8033
res = RemoveListStructure(child);
8034
if (NS_FAILED(res)) return res;
8038
// delete any non- list items for now
8039
res = mHTMLEditor->DeleteNode(child);
8040
if (NS_FAILED(res)) return res;
8042
aList->GetFirstChild(getter_AddRefs(child));
8044
// delete the now-empty list
8045
res = mHTMLEditor->RemoveBlockContainer(aList);
8046
if (NS_FAILED(res)) return res;
8053
nsHTMLEditRules::ConfirmSelectionInBody()
8055
nsresult res = NS_OK;
8056
nsCOMPtr<nsIDOMElement> bodyElement;
8057
nsCOMPtr<nsIDOMNode> bodyNode;
8060
res = mHTMLEditor->GetRootElement(getter_AddRefs(bodyElement));
8061
if (NS_FAILED(res)) return res;
8062
if (!bodyElement) return NS_ERROR_UNEXPECTED;
8063
bodyNode = do_QueryInterface(bodyElement);
8065
// get the selection
8066
nsCOMPtr<nsISelection>selection;
8067
res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
8068
if (NS_FAILED(res)) return res;
8070
// get the selection start location
8071
nsCOMPtr<nsIDOMNode> selNode, temp, parent;
8073
res = mHTMLEditor->GetStartNodeAndOffset(selection, address_of(selNode), &selOffset);
8074
if (NS_FAILED(res)) return res;
8077
// check that selNode is inside body
8078
while (temp && !nsTextEditUtils::IsBody(temp))
8080
res = temp->GetParentNode(getter_AddRefs(parent));
8084
// if we aren't in the body, force the issue
8087
// uncomment this to see when we get bad selections
8088
// NS_NOTREACHED("selection not in body");
8089
selection->Collapse(bodyNode,0);
8092
// get the selection end location
8093
res = mHTMLEditor->GetEndNodeAndOffset(selection, address_of(selNode), &selOffset);
8094
if (NS_FAILED(res)) return res;
8097
// check that selNode is inside body
8098
while (temp && !nsTextEditUtils::IsBody(temp))
8100
res = temp->GetParentNode(getter_AddRefs(parent));
8104
// if we aren't in the body, force the issue
8107
// uncomment this to see when we get bad selections
8108
// NS_NOTREACHED("selection not in body");
8109
selection->Collapse(bodyNode,0);
8117
nsHTMLEditRules::UpdateDocChangeRange(nsIDOMRange *aRange)
8119
nsresult res = NS_OK;
8121
// first make sure aRange is in the document. It might not be if
8122
// portions of our editting action involved manipulating nodes
8123
// prior to placing them in the document (e.g., populating a list item
8124
// before placing it in it's list)
8125
nsCOMPtr<nsIDOMNode> startNode;
8126
res = aRange->GetStartContainer(getter_AddRefs(startNode));
8127
if (NS_FAILED(res)) return res;
8128
if (!mHTMLEditor->IsDescendantOfBody(startNode))
8130
// just return - we don't need to adjust mDocChangeRange in this case
8134
if (!mDocChangeRange)
8137
res = aRange->CloneRange(getter_AddRefs(mDocChangeRange));
8144
// compare starts of ranges
8145
res = mDocChangeRange->CompareBoundaryPoints(nsIDOMRange::START_TO_START, aRange, &result);
8146
if (NS_FAILED(res)) return res;
8147
if (result > 0) // positive result means mDocChangeRange start is after aRange start
8149
PRInt32 startOffset;
8150
res = aRange->GetStartOffset(&startOffset);
8151
if (NS_FAILED(res)) return res;
8152
res = mDocChangeRange->SetStart(startNode, startOffset);
8153
if (NS_FAILED(res)) return res;
8156
// compare ends of ranges
8157
res = mDocChangeRange->CompareBoundaryPoints(nsIDOMRange::END_TO_END, aRange, &result);
8158
if (NS_FAILED(res)) return res;
8159
if (result < 0) // negative result means mDocChangeRange end is before aRange end
8161
nsCOMPtr<nsIDOMNode> endNode;
8163
res = aRange->GetEndContainer(getter_AddRefs(endNode));
8164
if (NS_FAILED(res)) return res;
8165
res = aRange->GetEndOffset(&endOffset);
8166
if (NS_FAILED(res)) return res;
8167
res = mDocChangeRange->SetEnd(endNode, endOffset);
8168
if (NS_FAILED(res)) return res;
8175
nsHTMLEditRules::InsertMozBRIfNeeded(nsIDOMNode *aNode)
8177
if (!aNode) return NS_ERROR_NULL_POINTER;
8178
if (!IsBlockNode(aNode)) return NS_OK;
8181
nsCOMPtr<nsIDOMNode> brNode;
8182
nsresult res = mHTMLEditor->IsEmptyNode(aNode, &isEmpty);
8183
if (NS_FAILED(res)) return res;
8186
res = CreateMozBR(aNode, 0, address_of(brNode));
8193
#pragma mark nsIEditActionListener methods
8198
nsHTMLEditRules::WillCreateNode(const nsAString& aTag, nsIDOMNode *aParent, PRInt32 aPosition)
8204
nsHTMLEditRules::DidCreateNode(const nsAString& aTag,
8206
nsIDOMNode *aParent,
8210
if (!mListenerEnabled) return NS_OK;
8211
// assumption that Join keeps the righthand node
8212
nsresult res = mUtilRange->SelectNode(aNode);
8213
if (NS_FAILED(res)) return res;
8214
res = UpdateDocChangeRange(mUtilRange);
8220
nsHTMLEditRules::WillInsertNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aPosition)
8227
nsHTMLEditRules::DidInsertNode(nsIDOMNode *aNode,
8228
nsIDOMNode *aParent,
8232
if (!mListenerEnabled) return NS_OK;
8233
nsresult res = mUtilRange->SelectNode(aNode);
8234
if (NS_FAILED(res)) return res;
8235
res = UpdateDocChangeRange(mUtilRange);
8241
nsHTMLEditRules::WillDeleteNode(nsIDOMNode *aChild)
8243
if (!mListenerEnabled) return NS_OK;
8244
nsresult res = mUtilRange->SelectNode(aChild);
8245
if (NS_FAILED(res)) return res;
8246
res = UpdateDocChangeRange(mUtilRange);
8252
nsHTMLEditRules::DidDeleteNode(nsIDOMNode *aChild, nsresult aResult)
8259
nsHTMLEditRules::WillSplitNode(nsIDOMNode *aExistingRightNode, PRInt32 aOffset)
8266
nsHTMLEditRules::DidSplitNode(nsIDOMNode *aExistingRightNode,
8268
nsIDOMNode *aNewLeftNode,
8271
if (!mListenerEnabled) return NS_OK;
8272
nsresult res = mUtilRange->SetStart(aNewLeftNode, 0);
8273
if (NS_FAILED(res)) return res;
8274
res = mUtilRange->SetEnd(aExistingRightNode, 0);
8275
if (NS_FAILED(res)) return res;
8276
res = UpdateDocChangeRange(mUtilRange);
8282
nsHTMLEditRules::WillJoinNodes(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, nsIDOMNode *aParent)
8284
if (!mListenerEnabled) return NS_OK;
8285
// remember split point
8286
nsresult res = nsEditor::GetLengthOfDOMNode(aLeftNode, mJoinOffset);
8292
nsHTMLEditRules::DidJoinNodes(nsIDOMNode *aLeftNode,
8293
nsIDOMNode *aRightNode,
8294
nsIDOMNode *aParent,
8297
if (!mListenerEnabled) return NS_OK;
8298
// assumption that Join keeps the righthand node
8299
nsresult res = mUtilRange->SetStart(aRightNode, mJoinOffset);
8300
if (NS_FAILED(res)) return res;
8301
res = mUtilRange->SetEnd(aRightNode, mJoinOffset);
8302
if (NS_FAILED(res)) return res;
8303
res = UpdateDocChangeRange(mUtilRange);
8309
nsHTMLEditRules::WillInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsAString &aString)
8316
nsHTMLEditRules::DidInsertText(nsIDOMCharacterData *aTextNode,
8318
const nsAString &aString,
8321
if (!mListenerEnabled) return NS_OK;
8322
PRInt32 length = aString.Length();
8323
nsCOMPtr<nsIDOMNode> theNode = do_QueryInterface(aTextNode);
8324
nsresult res = mUtilRange->SetStart(theNode, aOffset);
8325
if (NS_FAILED(res)) return res;
8326
res = mUtilRange->SetEnd(theNode, aOffset+length);
8327
if (NS_FAILED(res)) return res;
8328
res = UpdateDocChangeRange(mUtilRange);
8334
nsHTMLEditRules::WillDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength)
8341
nsHTMLEditRules::DidDeleteText(nsIDOMCharacterData *aTextNode,
8346
if (!mListenerEnabled) return NS_OK;
8347
nsCOMPtr<nsIDOMNode> theNode = do_QueryInterface(aTextNode);
8348
nsresult res = mUtilRange->SetStart(theNode, aOffset);
8349
if (NS_FAILED(res)) return res;
8350
res = mUtilRange->SetEnd(theNode, aOffset);
8351
if (NS_FAILED(res)) return res;
8352
res = UpdateDocChangeRange(mUtilRange);
8357
nsHTMLEditRules::WillDeleteRange(nsIDOMRange *aRange)
8359
if (!mListenerEnabled) return NS_OK;
8360
// get the (collapsed) selection location
8361
return UpdateDocChangeRange(aRange);
8365
nsHTMLEditRules::DidDeleteRange(nsIDOMRange *aRange)
8371
nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection)
8373
if (!mListenerEnabled) return NS_OK;
8374
// get the (collapsed) selection location
8375
nsCOMPtr<nsIDOMNode> selNode;
8378
nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
8379
if (NS_FAILED(res)) return res;
8380
res = mUtilRange->SetStart(selNode, selOffset);
8381
if (NS_FAILED(res)) return res;
8382
res = mHTMLEditor->GetEndNodeAndOffset(aSelection, address_of(selNode), &selOffset);
8383
if (NS_FAILED(res)) return res;
8384
res = mUtilRange->SetEnd(selNode, selOffset);
8385
if (NS_FAILED(res)) return res;
8386
res = UpdateDocChangeRange(mUtilRange);
8391
nsHTMLEditRules::DidDeleteSelection(nsISelection *aSelection)
8396
// Let's remove all alignment hints in the children of aNode; it can
8397
// be an ALIGN attribute (in case we just remove it) or a CENTER
8398
// element (here we have to remove the container and keep its
8399
// children). We break on tables and don't look at their children.
8401
nsHTMLEditRules::RemoveAlignment(nsIDOMNode * aNode, const nsAString & aAlignType, PRBool aChildrenOnly)
8403
if (!aNode) return NS_ERROR_NULL_POINTER;
8405
if (mHTMLEditor->IsTextNode(aNode) || nsHTMLEditUtils::IsTable(aNode)) return NS_OK;
8406
nsresult res = NS_OK;
8408
nsCOMPtr<nsIDOMNode> child = aNode,tmp;
8411
aNode->GetFirstChild(getter_AddRefs(child));
8414
mHTMLEditor->GetIsCSSEnabled(&useCSS);
8418
if (aChildrenOnly) {
8419
// get the next sibling right now because we could have to remove child
8420
child->GetNextSibling(getter_AddRefs(tmp));
8427
res = mHTMLEditor->NodeIsBlockStatic(child, &isBlock);
8428
if (NS_FAILED(res)) return res;
8430
if ((isBlock && !nsHTMLEditUtils::IsDiv(child)) || nsHTMLEditUtils::IsHR(child))
8432
// the current node is a block element
8433
nsCOMPtr<nsIDOMElement> curElem = do_QueryInterface(child);
8434
if (nsHTMLEditUtils::SupportsAlignAttr(child))
8436
// remove the ALIGN attribute if this element can have it
8437
res = mHTMLEditor->RemoveAttribute(curElem, NS_LITERAL_STRING("align"));
8438
if (NS_FAILED(res)) return res;
8442
if (nsHTMLEditUtils::IsTable(child) || nsHTMLEditUtils::IsHR(child))
8444
res = mHTMLEditor->SetAttributeOrEquivalent(curElem, NS_LITERAL_STRING("align"), aAlignType, PR_FALSE);
8448
nsAutoString dummyCssValue;
8449
res = mHTMLEditor->mHTMLCSSUtils->RemoveCSSInlineStyle(child, nsEditProperty::cssTextAlign, dummyCssValue);
8451
if (NS_FAILED(res)) return res;
8453
if (!nsHTMLEditUtils::IsTable(child))
8455
// unless this is a table, look at children
8456
res = RemoveAlignment(child, aAlignType, PR_TRUE);
8457
if (NS_FAILED(res)) return res;
8460
else if (nsEditor::NodeIsType(child, nsEditProperty::center)
8461
|| nsHTMLEditUtils::IsDiv(child))
8463
// this is a CENTER or a DIV element and we have to remove it
8464
// first remove children's alignment
8465
res = RemoveAlignment(child, aAlignType, PR_TRUE);
8466
if (NS_FAILED(res)) return res;
8468
if (useCSS && nsHTMLEditUtils::IsDiv(child))
8470
// if we are in CSS mode and if the element is a DIV, let's remove it
8471
// if it does not carry any style hint (style attr, class or ID)
8472
nsAutoString dummyCssValue;
8473
res = mHTMLEditor->mHTMLCSSUtils->RemoveCSSInlineStyle(child, nsEditProperty::cssTextAlign, dummyCssValue);
8474
if (NS_FAILED(res)) return res;
8475
nsCOMPtr<nsIDOMElement> childElt = do_QueryInterface(child);
8476
PRBool hasStyleOrIdOrClass;
8477
res = mHTMLEditor->HasStyleOrIdOrClass(childElt, &hasStyleOrIdOrClass);
8478
if (NS_FAILED(res)) return res;
8479
if (!hasStyleOrIdOrClass)
8481
// we may have to insert BRs in first and last position of DIV's children
8482
// if the nodes before/after are not blocks and not BRs
8483
res = MakeSureElemStartsOrEndsOnCR(child);
8484
if (NS_FAILED(res)) return res;
8485
res = mHTMLEditor->RemoveContainer(child);
8486
if (NS_FAILED(res)) return res;
8491
// we may have to insert BRs in first and last position of element's children
8492
// if the nodes before/after are not blocks and not BRs
8493
res = MakeSureElemStartsOrEndsOnCR(child);
8494
if (NS_FAILED(res)) return res;
8496
// in HTML mode, let's remove the element
8497
res = mHTMLEditor->RemoveContainer(child);
8498
if (NS_FAILED(res)) return res;
8506
// Let's insert a BR as first (resp. last) child of aNode if its
8507
// first (resp. last) child is not a block nor a BR, and if the
8508
// previous (resp. next) sibling is not a block nor a BR
8510
nsHTMLEditRules::MakeSureElemStartsOrEndsOnCR(nsIDOMNode *aNode, PRBool aStarts)
8512
if (!aNode) return NS_ERROR_NULL_POINTER;
8514
nsCOMPtr<nsIDOMNode> child;
8518
res = mHTMLEditor->GetFirstEditableChild(aNode, address_of(child));
8522
res = mHTMLEditor->GetLastEditableChild(aNode, address_of(child));
8524
if (NS_FAILED(res)) return res;
8525
if (!child) return NS_OK;
8526
PRBool isChildBlock;
8527
res = mHTMLEditor->NodeIsBlockStatic(child, &isChildBlock);
8528
if (NS_FAILED(res)) return res;
8529
PRBool foundCR = PR_FALSE;
8530
if (isChildBlock || nsTextEditUtils::IsBreak(child))
8536
nsCOMPtr<nsIDOMNode> sibling;
8539
res = mHTMLEditor->GetPriorHTMLSibling(aNode, address_of(sibling));
8543
res = mHTMLEditor->GetNextHTMLSibling(aNode, address_of(sibling));
8545
if (NS_FAILED(res)) return res;
8549
res = mHTMLEditor->NodeIsBlockStatic(sibling, &isBlock);
8550
if (NS_FAILED(res)) return res;
8551
if (isBlock || nsTextEditUtils::IsBreak(sibling))
8563
nsCOMPtr<nsIDOMNode> brNode;
8567
nsCOMPtr<nsIDOMNodeList> childNodes;
8568
res = aNode->GetChildNodes(getter_AddRefs(childNodes));
8569
if (NS_FAILED(res)) return res;
8570
if (!childNodes) return NS_ERROR_NULL_POINTER;
8571
PRUint32 childCount;
8572
res = childNodes->GetLength(&childCount);
8573
if (NS_FAILED(res)) return res;
8574
offset = childCount;
8576
res = mHTMLEditor->CreateBR(aNode, offset, address_of(brNode));
8577
if (NS_FAILED(res)) return res;
8583
nsHTMLEditRules::MakeSureElemStartsOrEndsOnCR(nsIDOMNode *aNode)
8585
nsresult res = MakeSureElemStartsOrEndsOnCR(aNode, PR_FALSE);
8586
if (NS_FAILED(res)) return res;
8587
res = MakeSureElemStartsOrEndsOnCR(aNode, PR_TRUE);
8592
nsHTMLEditRules::AlignBlock(nsIDOMElement * aElement, const nsAString * aAlignType, PRBool aContentsOnly)
8594
if (!aElement) return NS_ERROR_NULL_POINTER;
8596
nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aElement);
8597
PRBool isBlock = IsBlockNode(node);
8598
if (!isBlock && !nsHTMLEditUtils::IsHR(node)) {
8599
// we deal only with blocks; early way out
8603
nsresult res = RemoveAlignment(node, *aAlignType, aContentsOnly);
8604
if (NS_FAILED(res)) return res;
8605
NS_NAMED_LITERAL_STRING(attr, "align");
8607
mHTMLEditor->GetIsCSSEnabled(&useCSS);
8609
// let's use CSS alignment; we use margin-left and margin-right for tables
8610
// and text-align for other block-level elements
8611
res = mHTMLEditor->SetAttributeOrEquivalent(aElement, attr, *aAlignType, PR_FALSE);
8612
if (NS_FAILED(res)) return res;
8615
// HTML case; this code is supposed to be called ONLY if the element
8616
// supports the align attribute but we'll never know...
8617
if (nsHTMLEditUtils::SupportsAlignAttr(node)) {
8618
res = mHTMLEditor->SetAttribute(aElement, attr, *aAlignType);
8619
if (NS_FAILED(res)) return res;
8626
nsHTMLEditRules::RelativeChangeIndentationOfElementNode(nsIDOMNode *aNode, PRInt8 aRelativeChange)
8628
NS_ENSURE_ARG_POINTER(aNode);
8630
if ( !( (aRelativeChange==1) || (aRelativeChange==-1) ) )
8631
return NS_ERROR_ILLEGAL_VALUE;
8633
nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
8634
NS_ASSERTION(element, "not an element node");
8639
mHTMLEditor->mHTMLCSSUtils->GetSpecifiedProperty(aNode, nsEditProperty::cssMarginLeft, value);
8642
mHTMLEditor->mHTMLCSSUtils->ParseLength(value, &f, &unit);
8644
NS_IF_RELEASE(unit);
8645
nsAutoString defaultLengthUnit;
8646
mHTMLEditor->mHTMLCSSUtils->GetDefaultLengthUnit(defaultLengthUnit);
8647
unit = NS_NewAtom(defaultLengthUnit);
8649
nsAutoString unitString;
8650
unit->ToString(unitString);
8651
if (nsEditProperty::cssInUnit == unit)
8652
f += NS_EDITOR_INDENT_INCREMENT_IN * aRelativeChange;
8653
else if (nsEditProperty::cssCmUnit == unit)
8654
f += NS_EDITOR_INDENT_INCREMENT_CM * aRelativeChange;
8655
else if (nsEditProperty::cssMmUnit == unit)
8656
f += NS_EDITOR_INDENT_INCREMENT_MM * aRelativeChange;
8657
else if (nsEditProperty::cssPtUnit == unit)
8658
f += NS_EDITOR_INDENT_INCREMENT_PT * aRelativeChange;
8659
else if (nsEditProperty::cssPcUnit == unit)
8660
f += NS_EDITOR_INDENT_INCREMENT_PC * aRelativeChange;
8661
else if (nsEditProperty::cssEmUnit == unit)
8662
f += NS_EDITOR_INDENT_INCREMENT_EM * aRelativeChange;
8663
else if (nsEditProperty::cssExUnit == unit)
8664
f += NS_EDITOR_INDENT_INCREMENT_EX * aRelativeChange;
8665
else if (nsEditProperty::cssPxUnit == unit)
8666
f += NS_EDITOR_INDENT_INCREMENT_PX * aRelativeChange;
8667
else if (nsEditProperty::cssPercentUnit == unit)
8668
f += NS_EDITOR_INDENT_INCREMENT_PERCENT * aRelativeChange;
8670
NS_IF_RELEASE(unit);
8673
nsAutoString newValue;
8674
newValue.AppendFloat(f);
8675
newValue.Append(unitString);
8676
mHTMLEditor->mHTMLCSSUtils->SetCSSProperty(element, nsEditProperty::cssMarginLeft, newValue, PR_FALSE);
8679
mHTMLEditor->mHTMLCSSUtils->RemoveCSSProperty(element, nsEditProperty::cssMarginLeft, value, PR_FALSE);
8680
if (nsHTMLEditUtils::IsDiv(aNode)) {
8681
// we deal with a DIV ; let's see if it is useless and if we can remove it
8682
nsCOMPtr<nsIDOMNamedNodeMap> attributeList;
8683
res = element->GetAttributes(getter_AddRefs(attributeList));
8684
if (NS_FAILED(res)) return res;
8686
attributeList->GetLength(&count);
8688
// the DIV has no attribute at all, let's remove it
8689
res = mHTMLEditor->RemoveContainer(element);
8690
if (NS_FAILED(res)) return res;
8692
else if (1 == count) {
8693
nsCOMPtr<nsIDOMNode> styleAttributeNode;
8694
res = attributeList->GetNamedItem(NS_LITERAL_STRING("style"),
8695
getter_AddRefs(styleAttributeNode));
8696
if (!styleAttributeNode) {
8697
res = mHTMLEditor->RemoveContainer(element);
8698
if (NS_FAILED(res)) return res;
8708
// Support for Absolute Positioning
8712
nsHTMLEditRules::WillBlockifySelection(nsISelection *aSelection, PRBool *aCancel, PRBool * aHandled)
8714
if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
8715
nsresult res = WillInsert(aSelection, aCancel);
8716
if (NS_FAILED(res)) return res;
8718
// initialize out param
8719
// we want to ignore result of WillInsert()
8720
*aCancel = PR_FALSE;
8721
*aHandled = PR_TRUE;
8723
nsCOMPtr<nsIDOMElement> focusElement;
8724
res = mHTMLEditor->GetSelectionContainer(getter_AddRefs(focusElement));
8726
nsCOMPtr<nsIDOMNode> node = do_QueryInterface(focusElement);
8727
if (nsHTMLEditUtils::IsImage(node)) {
8733
res = NormalizeSelection(aSelection);
8734
if (NS_FAILED(res)) return res;
8735
nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
8737
// convert the selection ranges into "promoted" selection ranges:
8738
// this basically just expands the range to include the immediate
8739
// block parent, and then further expands to include any ancestors
8740
// whose children are all in the range
8742
nsCOMArray<nsIDOMRange> arrayOfRanges;
8743
res = GetPromotedRanges(aSelection, arrayOfRanges, kSetAbsolutePosition);
8744
if (NS_FAILED(res)) return res;
8746
// use these ranges to contruct a list of nodes to act on.
8747
nsCOMArray<nsIDOMNode> arrayOfNodes;
8748
res = GetNodesForOperation(arrayOfRanges, arrayOfNodes, kSetAbsolutePosition);
8749
if (NS_FAILED(res)) return res;
8751
NS_NAMED_LITERAL_STRING(divType, "div");
8754
// if nothing visible in list, make an empty block
8755
if (ListIsEmptyLine(arrayOfNodes))
8757
nsCOMPtr<nsIDOMNode> parent, thePositionedDiv;
8760
// get selection location
8761
res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(parent), &offset);
8762
if (NS_FAILED(res)) return res;
8763
// make sure we can put a block here
8764
res = SplitAsNeeded(&divType, address_of(parent), &offset);
8765
if (NS_FAILED(res)) return res;
8766
res = mHTMLEditor->CreateNode(divType, parent, offset, getter_AddRefs(thePositionedDiv));
8767
if (NS_FAILED(res)) return res;
8768
// remember our new block for postprocessing
8769
mNewBlock = thePositionedDiv;
8770
// delete anything that was in the list of nodes
8771
nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[0];
8774
res = mHTMLEditor->DeleteNode(curNode);
8775
if (NS_FAILED(res)) return res;
8776
res = arrayOfNodes.RemoveObjectAt(0);
8777
if (NS_FAILED(res)) return res;
8778
curNode = arrayOfNodes[0];
8780
// put selection in new block
8781
res = aSelection->Collapse(thePositionedDiv,0);
8782
selectionResetter.Abort(); // to prevent selection reseter from overriding us.
8783
*aHandled = PR_TRUE;
8787
// Ok, now go through all the nodes and put them in a blockquote,
8788
// or whatever is appropriate. Wohoo!
8790
nsCOMPtr<nsIDOMNode> curParent, curPositionedDiv, curList, indentedLI, sibling;
8791
PRInt32 listCount = arrayOfNodes.Count();
8792
for (i=0; i<listCount; i++)
8794
// here's where we actually figure out what to do
8795
nsCOMPtr<nsIDOMNode> curNode = arrayOfNodes[i];
8797
// Ignore all non-editable nodes. Leave them be.
8798
if (!mHTMLEditor->IsEditable(curNode)) continue;
8801
res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset);
8802
if (NS_FAILED(res)) return res;
8804
// some logic for putting list items into nested lists...
8805
if (nsHTMLEditUtils::IsList(curParent))
8807
// check to see if curList is still appropriate. Which it is if
8808
// curNode is still right after it in the same list.
8812
mHTMLEditor->GetPriorHTMLSibling(curNode, address_of(sibling));
8815
if (!curList || (sibling && sibling != curList) )
8817
nsAutoString listTag;
8818
nsEditor::GetTagString(curParent,listTag);
8819
ToLowerCase(listTag);
8820
// create a new nested list of correct type
8821
res = SplitAsNeeded(&listTag, address_of(curParent), &offset);
8822
if (NS_FAILED(res)) return res;
8823
if (!curPositionedDiv) {
8824
PRInt32 parentOffset;
8825
nsCOMPtr<nsIDOMNode> curParentParent;
8826
res = nsEditor::GetNodeLocation(curParent, address_of(curParentParent), &parentOffset);
8827
res = mHTMLEditor->CreateNode(divType, curParentParent, parentOffset, getter_AddRefs(curPositionedDiv));
8828
mNewBlock = curPositionedDiv;
8830
res = mHTMLEditor->CreateNode(listTag, curPositionedDiv, -1, getter_AddRefs(curList));
8831
if (NS_FAILED(res)) return res;
8832
// curList is now the correct thing to put curNode in
8833
// remember our new block for postprocessing
8834
// mNewBlock = curList;
8836
// tuck the node into the end of the active list
8837
res = mHTMLEditor->MoveNode(curNode, curList, -1);
8838
if (NS_FAILED(res)) return res;
8839
// forget curPositionedDiv, if any
8840
// curPositionedDiv = nsnull;
8843
else // not a list item, use blockquote?
8845
// if we are inside a list item, we dont want to blockquote, we want
8846
// to sublist the list item. We may have several nodes listed in the
8847
// array of nodes to act on, that are in the same list item. Since
8848
// we only want to indent that li once, we must keep track of the most
8849
// recent indented list item, and not indent it if we find another node
8850
// to act on that is still inside the same li.
8851
nsCOMPtr<nsIDOMNode> listitem=IsInListItem(curNode);
8854
if (indentedLI == listitem) continue; // already indented this list item
8855
res = nsEditor::GetNodeLocation(listitem, address_of(curParent), &offset);
8856
if (NS_FAILED(res)) return res;
8857
// check to see if curList is still appropriate. Which it is if
8858
// curNode is still right after it in the same list.
8862
mHTMLEditor->GetPriorHTMLSibling(curNode, address_of(sibling));
8865
if (!curList || (sibling && sibling != curList) )
8867
nsAutoString listTag;
8868
nsEditor::GetTagString(curParent,listTag);
8869
ToLowerCase(listTag);
8870
// create a new nested list of correct type
8871
res = SplitAsNeeded(&listTag, address_of(curParent), &offset);
8872
if (NS_FAILED(res)) return res;
8873
if (!curPositionedDiv) {
8874
PRInt32 parentOffset;
8875
nsCOMPtr<nsIDOMNode> curParentParent;
8876
res = nsEditor::GetNodeLocation(curParent, address_of(curParentParent), &parentOffset);
8877
res = mHTMLEditor->CreateNode(divType, curParentParent, parentOffset, getter_AddRefs(curPositionedDiv));
8878
mNewBlock = curPositionedDiv;
8880
res = mHTMLEditor->CreateNode(listTag, curPositionedDiv, -1, getter_AddRefs(curList));
8881
if (NS_FAILED(res)) return res;
8883
res = mHTMLEditor->MoveNode(listitem, curList, -1);
8884
if (NS_FAILED(res)) return res;
8885
// remember we indented this li
8886
indentedLI = listitem;
8891
// need to make a div to put things in if we haven't already
8893
if (!curPositionedDiv)
8895
if (nsHTMLEditUtils::IsDiv(curNode))
8897
curPositionedDiv = curNode;
8898
mNewBlock = curPositionedDiv;
8902
res = SplitAsNeeded(&divType, address_of(curParent), &offset);
8903
if (NS_FAILED(res)) return res;
8904
res = mHTMLEditor->CreateNode(divType, curParent, offset, getter_AddRefs(curPositionedDiv));
8905
if (NS_FAILED(res)) return res;
8906
// remember our new block for postprocessing
8907
mNewBlock = curPositionedDiv;
8908
// curPositionedDiv is now the correct thing to put curNode in
8911
// tuck the node into the end of the active blockquote
8912
res = mHTMLEditor->MoveNode(curNode, curPositionedDiv, -1);
8913
if (NS_FAILED(res)) return res;
8914
// forget curList, if any
8923
nsHTMLEditRules::DidAbsolutePosition()
8925
nsCOMPtr<nsIHTMLAbsPosEditor> absPosHTMLEditor = mHTMLEditor;
8926
nsCOMPtr<nsIDOMElement> elt = do_QueryInterface(mNewBlock);
8927
return absPosHTMLEditor->AbsolutelyPositionElement(elt, PR_TRUE);
8931
nsHTMLEditRules::DidMakeComplexBlock(const nsAString *aStyleAttr)
8933
nsCOMPtr<nsIDOMElement> elt = do_QueryInterface(mNewBlock);
8934
return mHTMLEditor->SetAttribute(elt, NS_LITERAL_STRING("style"), *aStyleAttr);
8938
nsHTMLEditRules::WillRemoveAbsolutePosition(nsISelection *aSelection, PRBool *aCancel, PRBool * aHandled)
8940
if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
8941
nsresult res = WillInsert(aSelection, aCancel);
8942
if (NS_FAILED(res)) return res;
8944
// initialize out param
8945
// we want to ignore aCancel from WillInsert()
8946
*aCancel = PR_FALSE;
8947
*aHandled = PR_TRUE;
8949
nsCOMPtr<nsIDOMElement> elt;
8950
res = mHTMLEditor->GetAbsolutelyPositionedSelectionContainer(getter_AddRefs(elt));
8951
if (NS_FAILED(res)) return res;
8953
nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
8955
nsCOMPtr<nsIHTMLAbsPosEditor> absPosHTMLEditor = mHTMLEditor;
8956
return absPosHTMLEditor->AbsolutelyPositionElement(elt, PR_FALSE);
8960
nsHTMLEditRules::WillRelativeChangeZIndex(nsISelection *aSelection,
8965
if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
8966
nsresult res = WillInsert(aSelection, aCancel);
8967
if (NS_FAILED(res)) return res;
8969
// initialize out param
8970
// we want to ignore aCancel from WillInsert()
8971
*aCancel = PR_FALSE;
8972
*aHandled = PR_TRUE;
8974
nsCOMPtr<nsIDOMElement> elt;
8975
res = mHTMLEditor->GetAbsolutelyPositionedSelectionContainer(getter_AddRefs(elt));
8976
if (NS_FAILED(res)) return res;
8978
nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
8980
nsCOMPtr<nsIHTMLAbsPosEditor> absPosHTMLEditor = mHTMLEditor;
8982
return absPosHTMLEditor->RelativeChangeElementZIndex(elt, aChange, &zIndex);