1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
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 of the GNU General Public License Version 2 or later (the "GPL"),
28 * or 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 MPL, 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 MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
41 #include "nsReadableUtils.h"
42 #include "nsUnicharUtils.h"
44 #include "nsHTMLEditor.h"
45 #include "nsHTMLEditRules.h"
46 #include "nsTextEditUtils.h"
47 #include "nsHTMLEditUtils.h"
49 #include "nsHTMLEditorEventListener.h"
50 #include "TypeInState.h"
52 #include "nsHTMLURIRefObject.h"
54 #include "nsIDOMText.h"
55 #include "nsIDOMNodeList.h"
56 #include "nsIDOMDocument.h"
57 #include "nsIDOMAttr.h"
58 #include "nsIDocument.h"
59 #include "nsIDOMEventTarget.h"
60 #include "nsIDOM3EventTarget.h"
61 #include "nsIDOMKeyEvent.h"
62 #include "nsIDOMKeyListener.h"
63 #include "nsIDOMMouseListener.h"
64 #include "nsIDOMMouseEvent.h"
65 #include "nsISelection.h"
66 #include "nsISelectionPrivate.h"
67 #include "nsIDOMHTMLAnchorElement.h"
68 #include "nsISelectionController.h"
69 #include "nsIDOMHTMLDocument.h"
70 #include "nsIDOMHTMLHtmlElement.h"
71 #include "nsGUIEvent.h"
72 #include "nsIDOMEventGroup.h"
73 #include "nsILinkHandler.h"
75 #include "mozilla/css/Loader.h"
76 #include "nsCSSStyleSheet.h"
77 #include "nsIDOMStyleSheet.h"
78 #include "nsIDocumentObserver.h"
79 #include "nsIDocumentStateListener.h"
81 #include "nsIEnumerator.h"
82 #include "nsIContent.h"
83 #include "nsIContentIterator.h"
84 #include "nsIDOMRange.h"
85 #include "nsIDOMNSRange.h"
86 #include "nsIRangeUtils.h"
87 #include "nsISupportsArray.h"
88 #include "nsContentUtils.h"
90 #include "nsIComponentManager.h"
91 #include "nsIServiceManager.h"
92 #include "nsIDocumentEncoder.h"
93 #include "nsIDOMDocumentFragment.h"
94 #include "nsIPresShell.h"
95 #include "nsPresContext.h"
97 #include "nsISupportsPrimitives.h"
98 #include "SetDocTitleTxn.h"
99 #include "nsGUIEvent.h"
100 #include "nsTextFragment.h"
101 #include "nsFocusManager.h"
102 #include "nsPIDOMWindow.h"
106 #include "nsNetUtil.h"
108 // Drag & Drop, Clipboard
109 #include "nsIClipboard.h"
110 #include "nsITransferable.h"
111 #include "nsIDragService.h"
112 #include "nsIDOMNSUIEvent.h"
113 #include "nsIContentFilter.h"
116 #include "nsStyleSheetTxns.h"
119 #include "TextEditorTest.h"
120 #include "nsEditorUtils.h"
121 #include "nsWSRunObject.h"
122 #include "nsHTMLObjectResizer.h"
123 #include "nsGkAtoms.h"
125 #include "nsIFrame.h"
127 #include "nsIParserService.h"
128 #include "nsIEventStateManager.h"
130 // Some utilities to handle annoying overloading of "A" tag for link and named anchor
131 static char hrefText
[] = "href";
132 static char anchorTxt
[] = "anchor";
133 static char namedanchorText
[] = "namedanchor";
135 nsIRangeUtils
* nsHTMLEditor::sRangeHelper
;
137 // some prototypes for rules creation shortcuts
138 nsresult
NS_NewTextEditRules(nsIEditRules
** aInstancePtrResult
);
139 nsresult
NS_NewHTMLEditRules(nsIEditRules
** aInstancePtrResult
);
141 #define IsLinkTag(s) (s.EqualsIgnoreCase(hrefText))
142 #define IsNamedAnchorTag(s) (s.EqualsIgnoreCase(anchorTxt) || s.EqualsIgnoreCase(namedanchorText))
144 nsHTMLEditor::nsHTMLEditor()
145 : nsPlaintextEditor()
146 , mIgnoreSpuriousDragEvent(PR_FALSE
)
147 , mCRInParagraphCreatesParagraph(PR_FALSE
)
148 , mSelectedCellIndex(0)
149 , mIsObjectResizingEnabled(PR_TRUE
)
150 , mIsResizing(PR_FALSE
)
151 , mIsAbsolutelyPositioningEnabled(PR_TRUE
)
152 , mResizedObjectIsAbsolutelyPositioned(PR_FALSE
)
153 , mGrabberClicked(PR_FALSE
)
154 , mIsMoving(PR_FALSE
)
155 , mSnapToGridEnabled(PR_FALSE
)
156 , mIsInlineTableEditingEnabled(PR_TRUE
)
157 , mInfoXIncrement(20)
158 , mInfoYIncrement(20)
163 nsHTMLEditor::~nsHTMLEditor()
165 // remove the rules as an action listener. Else we get a bad
166 // ownership loop later on. it's ok if the rules aren't a listener;
167 // we ignore the error.
168 nsCOMPtr
<nsIEditActionListener
> mListener
= do_QueryInterface(mRules
);
169 RemoveEditActionListener(mListener
);
171 // Clean up after our anonymous content -- we don't want these nodes to
172 // stay around (which they would, since the frames have an owning reference).
174 if (mAbsolutelyPositionedObject
)
176 if (mInlineEditedCell
)
177 HideInlineTableEditingUI();
181 //the autopointers will clear themselves up.
182 //but we need to also remove the listeners or we have a leak
183 nsCOMPtr
<nsISelection
>selection
;
184 nsresult result
= GetSelection(getter_AddRefs(selection
));
185 // if we don't get the selection, just skip this
186 if (NS_SUCCEEDED(result
) && selection
)
188 nsCOMPtr
<nsISelectionPrivate
> selPriv(do_QueryInterface(selection
));
189 nsCOMPtr
<nsISelectionListener
>listener
;
190 listener
= do_QueryInterface(mTypeInState
);
193 selPriv
->RemoveSelectionListener(listener
);
195 listener
= do_QueryInterface(mSelectionListenerP
);
198 selPriv
->RemoveSelectionListener(listener
);
202 mTypeInState
= nsnull
;
203 mSelectionListenerP
= nsnull
;
205 // free any default style propItems
206 RemoveAllDefaultProperties();
208 while (mStyleSheetURLs
.Length())
210 RemoveOverrideStyleSheet(mStyleSheetURLs
[0]);
213 if (mLinkHandler
&& mPresShellWeak
)
215 nsCOMPtr
<nsIPresShell
> ps
= do_QueryReferent(mPresShellWeak
);
217 if (ps
&& ps
->GetPresContext())
219 ps
->GetPresContext()->SetLinkHandler(mLinkHandler
);
223 RemoveEventListeners();
228 nsHTMLEditor::Shutdown()
230 NS_IF_RELEASE(sRangeHelper
);
233 NS_IMPL_ADDREF_INHERITED(nsHTMLEditor
, nsEditor
)
234 NS_IMPL_RELEASE_INHERITED(nsHTMLEditor
, nsEditor
)
236 NS_INTERFACE_MAP_BEGIN(nsHTMLEditor
)
237 NS_INTERFACE_MAP_ENTRY(nsIHTMLEditor
)
238 NS_INTERFACE_MAP_ENTRY(nsIHTMLEditor_MOZILLA_2_0_BRANCH
)
239 NS_INTERFACE_MAP_ENTRY(nsIHTMLObjectResizer
)
240 NS_INTERFACE_MAP_ENTRY(nsIHTMLAbsPosEditor
)
241 NS_INTERFACE_MAP_ENTRY(nsIHTMLInlineTableEditor
)
242 NS_INTERFACE_MAP_ENTRY(nsITableEditor
)
243 NS_INTERFACE_MAP_ENTRY(nsIEditorStyleSheets
)
244 NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver
)
245 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver
)
246 NS_INTERFACE_MAP_END_INHERITING(nsPlaintextEditor
)
250 nsHTMLEditor::Init(nsIDOMDocument
*aDoc
, nsIPresShell
*aPresShell
,
251 nsIContent
*aRoot
, nsISelectionController
*aSelCon
,
254 NS_PRECONDITION(aDoc
&& aPresShell
, "bad arg");
255 NS_ENSURE_TRUE(aDoc
&& aPresShell
, NS_ERROR_NULL_POINTER
);
257 nsresult result
= NS_OK
, rulesRes
= NS_OK
;
259 // make a range util object for comparing dom points
261 result
= CallGetService("@mozilla.org/content/range-utils;1",
263 NS_ENSURE_TRUE(sRangeHelper
, result
);
268 // block to scope nsAutoEditInitRulesTrigger
269 nsAutoEditInitRulesTrigger
rulesTrigger(static_cast<nsPlaintextEditor
*>(this), rulesRes
);
271 // Init the plaintext editor
272 result
= nsPlaintextEditor::Init(aDoc
, aPresShell
, aRoot
, aSelCon
, aFlags
);
273 if (NS_FAILED(result
)) { return result
; }
275 // Init mutation observer
276 nsCOMPtr
<nsINode
> document
= do_QueryInterface(aDoc
);
277 document
->AddMutationObserverUnlessExists(this);
279 // disable Composer-only features
282 SetAbsolutePositioningEnabled(PR_FALSE
);
283 SetSnapToGridEnabled(PR_FALSE
);
286 // Init the HTML-CSS utils
287 result
= NS_NewHTMLCSSUtils(getter_Transfers(mHTMLCSSUtils
));
288 if (NS_FAILED(result
)) { return result
; }
289 mHTMLCSSUtils
->Init(this);
292 nsPresContext
*context
= aPresShell
->GetPresContext();
293 NS_ENSURE_TRUE(context
, NS_ERROR_NULL_POINTER
);
294 if (!IsPlaintextEditor() && !IsInteractionAllowed()) {
295 mLinkHandler
= context
->GetLinkHandler();
297 context
->SetLinkHandler(nsnull
);
300 // init the type-in state
301 mTypeInState
= new TypeInState();
302 if (!mTypeInState
) {return NS_ERROR_NULL_POINTER
;}
304 // init the selection listener for image resizing
305 mSelectionListenerP
= new ResizerSelectionListener(this);
306 if (!mSelectionListenerP
) {return NS_ERROR_NULL_POINTER
;}
308 if (!IsInteractionAllowed()) {
309 // ignore any errors from this in case the file is missing
310 AddOverrideStyleSheet(NS_LITERAL_STRING("resource://gre/res/EditorOverride.css"));
313 nsCOMPtr
<nsISelection
>selection
;
314 result
= GetSelection(getter_AddRefs(selection
));
315 if (NS_FAILED(result
)) { return result
; }
318 nsCOMPtr
<nsISelectionPrivate
> selPriv(do_QueryInterface(selection
));
319 nsCOMPtr
<nsISelectionListener
>listener
;
320 listener
= do_QueryInterface(mTypeInState
);
322 selPriv
->AddSelectionListener(listener
);
324 listener
= do_QueryInterface(mSelectionListenerP
);
326 selPriv
->AddSelectionListener(listener
);
331 NS_ENSURE_SUCCESS(rulesRes
, rulesRes
);
336 nsHTMLEditor::PreDestroy(PRBool aDestroyingFrames
)
338 if (mDidPreDestroy
) {
342 nsCOMPtr
<nsINode
> document
= do_QueryReferent(mDocWeak
);
344 document
->RemoveMutationObserver(this);
347 return nsPlaintextEditor::PreDestroy(aDestroyingFrames
);
351 nsHTMLEditor::GetRootElement(nsIDOMElement
**aRootElement
)
353 NS_ENSURE_ARG_POINTER(aRootElement
);
356 return nsEditor::GetRootElement(aRootElement
);
359 *aRootElement
= nsnull
;
361 // Use the HTML documents body element as the editor root if we didn't
362 // get a root element during initialization.
364 nsCOMPtr
<nsIDOMHTMLElement
> bodyElement
;
365 nsresult rv
= GetBodyElement(getter_AddRefs(bodyElement
));
366 NS_ENSURE_SUCCESS(rv
, rv
);
369 mRootElement
= bodyElement
;
371 // If there is no HTML body element,
372 // we should use the document root element instead.
373 nsCOMPtr
<nsIDOMDocument
> doc
= do_QueryReferent(mDocWeak
);
374 NS_ENSURE_TRUE(doc
, NS_ERROR_NOT_INITIALIZED
);
376 rv
= doc
->GetDocumentElement(getter_AddRefs(mRootElement
));
377 NS_ENSURE_SUCCESS(rv
, rv
);
378 // Document can have no elements
380 return NS_ERROR_NOT_AVAILABLE
;
384 *aRootElement
= mRootElement
;
385 NS_ADDREF(*aRootElement
);
390 already_AddRefed
<nsIContent
>
391 nsHTMLEditor::FindSelectionRoot(nsINode
*aNode
)
393 NS_PRECONDITION(aNode
->IsNodeOfType(nsINode::eDOCUMENT
) ||
394 aNode
->IsNodeOfType(nsINode::eCONTENT
),
395 "aNode must be content or document node");
397 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(aNode
);
398 nsCOMPtr
<nsIDocument
> doc
= aNode
->GetCurrentDoc();
403 if (doc
->HasFlag(NODE_IS_EDITABLE
) || !content
) {
404 content
= doc
->GetRootElement();
405 return content
.forget();
408 // XXX If we have readonly flag, shouldn't return the element which has
409 // contenteditable="true"? However, such case isn't there without chrome
410 // permission script.
412 // We still want to allow selection in a readonly editor.
413 content
= do_QueryInterface(GetRoot());
414 return content
.forget();
417 if (!content
->HasFlag(NODE_IS_EDITABLE
)) {
421 // For non-readonly editors we want to find the root of the editable subtree
422 // containing aContent.
423 content
= content
->GetEditingHost();
424 return content
.forget();
428 nsHTMLEditor::CreateEventListeners()
430 // Don't create the handler twice
433 mEventListener
= do_QueryInterface(
434 static_cast<nsIDOMKeyListener
*>(new nsHTMLEditorEventListener()));
435 NS_ENSURE_TRUE(mEventListener
, NS_ERROR_OUT_OF_MEMORY
);
440 nsHTMLEditor::InstallEventListeners()
442 NS_ENSURE_TRUE(mDocWeak
&& mPresShellWeak
&& mEventListener
,
443 NS_ERROR_NOT_INITIALIZED
);
445 // NOTE: nsHTMLEditor doesn't need to initialize mEventTarget here because
446 // the target must be document node and it must be referenced as weak pointer.
448 nsHTMLEditorEventListener
* listener
=
449 reinterpret_cast<nsHTMLEditorEventListener
*>(mEventListener
.get());
450 return listener
->Connect(this);
454 nsHTMLEditor::RemoveEventListeners()
461 nsCOMPtr
<nsPIDOMEventTarget
> piTarget
= GetPIDOMEventTarget();
462 nsCOMPtr
<nsIDOMEventTarget
> target
= do_QueryInterface(piTarget
);
464 if (piTarget
&& target
)
466 // Both mMouseMotionListenerP and mResizeEventListenerP can be
467 // registerd with other targets than the DOM event receiver that
468 // we can reach from here. But nonetheless, unregister the event
469 // listeners with the DOM event reveiver (if it's registerd with
470 // other targets, it'll get unregisterd once the target goes
473 if (mMouseMotionListenerP
)
475 // mMouseMotionListenerP might be registerd either by IID or
476 // name, unregister by both.
477 piTarget
->RemoveEventListenerByIID(mMouseMotionListenerP
,
478 NS_GET_IID(nsIDOMMouseMotionListener
));
480 target
->RemoveEventListener(NS_LITERAL_STRING("mousemove"),
481 mMouseMotionListenerP
, PR_TRUE
);
484 if (mResizeEventListenerP
)
486 target
->RemoveEventListener(NS_LITERAL_STRING("resize"),
487 mResizeEventListenerP
, PR_FALSE
);
491 mMouseMotionListenerP
= nsnull
;
492 mResizeEventListenerP
= nsnull
;
494 nsPlaintextEditor::RemoveEventListeners();
498 nsHTMLEditor::SetFlags(PRUint32 aFlags
)
500 nsresult rv
= nsPlaintextEditor::SetFlags(aFlags
);
501 NS_ENSURE_SUCCESS(rv
, rv
);
503 // Sets mCSSAware to correspond to aFlags. This toggles whether CSS is
504 // used to style elements in the editor. Note that the editor is only CSS
505 // aware by default in Composer and in the mail editor.
506 mCSSAware
= !NoCSS() && !IsMailEditor();
512 nsHTMLEditor::InitRules()
514 // instantiate the rules for the html editor
515 nsresult res
= NS_NewHTMLEditRules(getter_AddRefs(mRules
));
516 NS_ENSURE_SUCCESS(res
, res
);
517 NS_ENSURE_TRUE(mRules
, NS_ERROR_UNEXPECTED
);
518 res
= mRules
->Init(static_cast<nsPlaintextEditor
*>(this));
524 nsHTMLEditor::BeginningOfDocument()
526 if (!mDocWeak
|| !mPresShellWeak
) { return NS_ERROR_NOT_INITIALIZED
; }
529 nsCOMPtr
<nsISelection
> selection
;
530 nsresult res
= GetSelection(getter_AddRefs(selection
));
531 NS_ENSURE_SUCCESS(res
, res
);
532 NS_ENSURE_TRUE(selection
, NS_ERROR_NOT_INITIALIZED
);
534 // Get the root element.
535 nsIDOMElement
*rootElement
= GetRoot();
537 NS_WARNING("GetRoot() returned a null pointer (mRootElement is null)");
541 // find first editable thingy
542 PRBool done
= PR_FALSE
;
543 nsCOMPtr
<nsIDOMNode
> curNode(rootElement
), selNode
;
544 PRInt32 curOffset
= 0, selOffset
;
547 nsWSRunObject
wsObj(this, curNode
, curOffset
);
548 nsCOMPtr
<nsIDOMNode
> visNode
;
551 wsObj
.NextVisibleNode(curNode
, curOffset
, address_of(visNode
), &visOffset
, &visType
);
552 if ((visType
==nsWSRunObject::eNormalWS
) ||
553 (visType
==nsWSRunObject::eText
))
556 selOffset
= visOffset
;
559 else if ((visType
==nsWSRunObject::eBreak
) ||
560 (visType
==nsWSRunObject::eSpecial
))
562 res
= GetNodeLocation(visNode
, address_of(selNode
), &selOffset
);
563 NS_ENSURE_SUCCESS(res
, res
);
566 else if (visType
==nsWSRunObject::eOtherBlock
)
568 // By definition of nsWSRunObject, a block element terminates
569 // a whitespace run. That is, although we are calling a method
570 // that is named "NextVisibleNode", the node returned
571 // might not be visible/editable!
572 // If the given block does not contain any visible/editable items,
573 // we want to skip it and continue our search.
575 if (!IsContainer(visNode
))
577 // However, we were given a block that is not a container.
578 // Since the block can not contain anything that's visible,
579 // such a block only makes sense if it is visible by itself,
581 // We want to place the caret in front of that block.
583 res
= GetNodeLocation(visNode
, address_of(selNode
), &selOffset
);
584 NS_ENSURE_SUCCESS(res
, res
);
590 if (NS_SUCCEEDED(IsEmptyNode(visNode
, &isEmptyBlock
)) &&
593 // skip the empty block
594 res
= GetNodeLocation(visNode
, address_of(curNode
), &curOffset
);
595 NS_ENSURE_SUCCESS(res
, res
);
608 // else we found nothing useful
610 selOffset
= curOffset
;
614 return selection
->Collapse(selNode
, selOffset
);
618 nsHTMLEditor::HandleKeyPressEvent(nsIDOMKeyEvent
* aKeyEvent
)
620 // NOTE: When you change this method, you should also change:
621 // * editor/libeditor/html/tests/test_htmleditor_keyevent_handling.html
623 if (IsReadonly() || IsDisabled()) {
624 // When we're not editable, the events are handled on nsEditor, so, we can
625 // bypass nsPlaintextEditor.
626 return nsEditor::HandleKeyPressEvent(aKeyEvent
);
629 nsKeyEvent
* nativeKeyEvent
= GetNativeKeyEvent(aKeyEvent
);
630 NS_ENSURE_TRUE(nativeKeyEvent
, NS_ERROR_UNEXPECTED
);
631 NS_ASSERTION(nativeKeyEvent
->message
== NS_KEY_PRESS
,
632 "HandleKeyPressEvent gets non-keypress event");
634 switch (nativeKeyEvent
->keyCode
) {
635 case nsIDOMKeyEvent::DOM_VK_META
:
636 case nsIDOMKeyEvent::DOM_VK_SHIFT
:
637 case nsIDOMKeyEvent::DOM_VK_CONTROL
:
638 case nsIDOMKeyEvent::DOM_VK_ALT
:
639 case nsIDOMKeyEvent::DOM_VK_BACK_SPACE
:
640 case nsIDOMKeyEvent::DOM_VK_DELETE
:
641 // These keys are handled on nsEditor, so, we can bypass
642 // nsPlaintextEditor.
643 return nsEditor::HandleKeyPressEvent(aKeyEvent
);
644 case nsIDOMKeyEvent::DOM_VK_TAB
: {
645 if (IsPlaintextEditor()) {
646 // If this works as plain text editor, e.g., mail editor for plain
647 // text, should be handled on nsPlaintextEditor.
648 return nsPlaintextEditor::HandleKeyPressEvent(aKeyEvent
);
652 return NS_OK
; // let it be used for focus switching
655 if (nativeKeyEvent
->isControl
|| nativeKeyEvent
->isAlt
||
656 nativeKeyEvent
->isMeta
) {
660 nsCOMPtr
<nsISelection
> selection
;
661 nsresult rv
= GetSelection(getter_AddRefs(selection
));
662 NS_ENSURE_SUCCESS(rv
, rv
);
664 nsCOMPtr
<nsIDOMNode
> node
, blockParent
;
665 rv
= GetStartNodeAndOffset(selection
, getter_AddRefs(node
), &offset
);
666 NS_ENSURE_SUCCESS(rv
, rv
);
667 NS_ENSURE_TRUE(node
, NS_ERROR_FAILURE
);
669 PRBool isBlock
= PR_FALSE
;
670 NodeIsBlock(node
, &isBlock
);
674 blockParent
= GetBlockNodeParent(node
);
681 PRBool handled
= PR_FALSE
;
682 if (nsHTMLEditUtils::IsTableElement(blockParent
)) {
683 rv
= TabInTable(nativeKeyEvent
->isShift
, &handled
);
685 ScrollSelectionIntoView(PR_FALSE
);
687 } else if (nsHTMLEditUtils::IsListItem(blockParent
)) {
688 rv
= Indent(nativeKeyEvent
->isShift
?
689 NS_LITERAL_STRING("outdent") :
690 NS_LITERAL_STRING("indent"));
693 NS_ENSURE_SUCCESS(rv
, rv
);
695 return aKeyEvent
->PreventDefault(); // consumed
697 if (nativeKeyEvent
->isShift
) {
698 return NS_OK
; // don't type text for shift tabs
700 aKeyEvent
->PreventDefault();
701 return TypedText(NS_LITERAL_STRING("\t"), eTypedText
);
703 case nsIDOMKeyEvent::DOM_VK_RETURN
:
704 case nsIDOMKeyEvent::DOM_VK_ENTER
:
705 if (nativeKeyEvent
->isControl
|| nativeKeyEvent
->isAlt
||
706 nativeKeyEvent
->isMeta
) {
709 aKeyEvent
->PreventDefault(); // consumed
710 if (nativeKeyEvent
->isShift
&& !IsPlaintextEditor()) {
711 // only inserts a br node
712 return TypedText(EmptyString(), eTypedBR
);
714 // uses rules to figure out what to insert
715 return TypedText(EmptyString(), eTypedBreak
);
718 // NOTE: On some keyboard layout, some characters are inputted with Control
719 // key or Alt key, but at that time, widget sets FALSE to these keys.
720 if (nativeKeyEvent
->charCode
== 0 || nativeKeyEvent
->isControl
||
721 nativeKeyEvent
->isAlt
|| nativeKeyEvent
->isMeta
) {
722 // we don't PreventDefault() here or keybindings like control-x won't work
725 aKeyEvent
->PreventDefault();
726 nsAutoString
str(nativeKeyEvent
->charCode
);
727 return TypedText(str
, eTypedText
);
731 * Returns true if the id represents an element of block type.
732 * Can be used to determine if a new paragraph should be started.
735 nsHTMLEditor::NodeIsBlockStatic(nsIDOMNode
*aNode
, PRBool
*aIsBlock
)
737 if (!aNode
|| !aIsBlock
) { return NS_ERROR_NULL_POINTER
; }
739 *aIsBlock
= PR_FALSE
;
741 #define USE_PARSER_FOR_BLOCKNESS 1
742 #ifdef USE_PARSER_FOR_BLOCKNESS
745 nsCOMPtr
<nsIDOMElement
>element
= do_QueryInterface(aNode
);
748 // We don't have an element -- probably a text node
752 nsIAtom
*tagAtom
= GetTag(aNode
);
753 NS_ENSURE_TRUE(tagAtom
, NS_ERROR_NULL_POINTER
);
755 // Nodes we know we want to treat as block
756 // even though the parser says they're not:
757 if (tagAtom
==nsEditProperty::body
||
758 tagAtom
==nsEditProperty::head
||
759 tagAtom
==nsEditProperty::tbody
||
760 tagAtom
==nsEditProperty::thead
||
761 tagAtom
==nsEditProperty::tfoot
||
762 tagAtom
==nsEditProperty::tr
||
763 tagAtom
==nsEditProperty::th
||
764 tagAtom
==nsEditProperty::td
||
765 tagAtom
==nsEditProperty::li
||
766 tagAtom
==nsEditProperty::dt
||
767 tagAtom
==nsEditProperty::dd
||
768 tagAtom
==nsEditProperty::pre
)
774 rv
= nsContentUtils::GetParserService()->
775 IsBlock(nsContentUtils::GetParserService()->HTMLAtomTagToId(tagAtom
),
779 // Check this against what we would have said with the old code:
780 if (tagAtom
==nsEditProperty::p
||
781 tagAtom
==nsEditProperty::div
||
782 tagAtom
==nsEditProperty::blockquote
||
783 tagAtom
==nsEditProperty::h1
||
784 tagAtom
==nsEditProperty::h2
||
785 tagAtom
==nsEditProperty::h3
||
786 tagAtom
==nsEditProperty::h4
||
787 tagAtom
==nsEditProperty::h5
||
788 tagAtom
==nsEditProperty::h6
||
789 tagAtom
==nsEditProperty::ul
||
790 tagAtom
==nsEditProperty::ol
||
791 tagAtom
==nsEditProperty::dl
||
792 tagAtom
==nsEditProperty::noscript
||
793 tagAtom
==nsEditProperty::form
||
794 tagAtom
==nsEditProperty::hr
||
795 tagAtom
==nsEditProperty::table
||
796 tagAtom
==nsEditProperty::fieldset
||
797 tagAtom
==nsEditProperty::address
||
798 tagAtom
==nsEditProperty::caption
||
799 tagAtom
==nsEditProperty::col
||
800 tagAtom
==nsEditProperty::colgroup
||
801 tagAtom
==nsEditProperty::li
||
802 tagAtom
==nsEditProperty::dt
||
803 tagAtom
==nsEditProperty::dd
||
804 tagAtom
==nsEditProperty::legend
)
808 nsAutoString
assertmsg (NS_LITERAL_STRING("Parser and editor disagree on blockness: "));
810 nsAutoString tagName
;
811 rv
= element
->GetTagName(tagName
);
812 NS_ENSURE_SUCCESS(rv
, rv
);
814 assertmsg
.Append(tagName
);
815 char* assertstr
= ToNewCString(assertmsg
);
816 NS_ASSERTION(*aIsBlock
, assertstr
);
823 #else /* USE_PARSER_FOR_BLOCKNESS */
824 nsresult result
= NS_ERROR_FAILURE
;
825 *aIsBlock
= PR_FALSE
;
826 nsCOMPtr
<nsIDOMElement
>element
;
827 element
= do_QueryInterface(aNode
);
830 nsAutoString tagName
;
831 result
= element
->GetTagName(tagName
);
832 if (NS_SUCCEEDED(result
))
834 ToLowerCase(tagName
);
835 nsCOMPtr
<nsIAtom
> tagAtom
= do_GetAtom(tagName
);
836 if (!tagAtom
) { return NS_ERROR_NULL_POINTER
; }
838 if (tagAtom
==nsEditProperty::p
||
839 tagAtom
==nsEditProperty::div
||
840 tagAtom
==nsEditProperty::blockquote
||
841 tagAtom
==nsEditProperty::h1
||
842 tagAtom
==nsEditProperty::h2
||
843 tagAtom
==nsEditProperty::h3
||
844 tagAtom
==nsEditProperty::h4
||
845 tagAtom
==nsEditProperty::h5
||
846 tagAtom
==nsEditProperty::h6
||
847 tagAtom
==nsEditProperty::ul
||
848 tagAtom
==nsEditProperty::ol
||
849 tagAtom
==nsEditProperty::dl
||
850 tagAtom
==nsEditProperty::pre
||
851 tagAtom
==nsEditProperty::noscript
||
852 tagAtom
==nsEditProperty::form
||
853 tagAtom
==nsEditProperty::hr
||
854 tagAtom
==nsEditProperty::fieldset
||
855 tagAtom
==nsEditProperty::address
||
856 tagAtom
==nsEditProperty::body
||
857 tagAtom
==nsEditProperty::caption
||
858 tagAtom
==nsEditProperty::table
||
859 tagAtom
==nsEditProperty::tbody
||
860 tagAtom
==nsEditProperty::thead
||
861 tagAtom
==nsEditProperty::tfoot
||
862 tagAtom
==nsEditProperty::tr
||
863 tagAtom
==nsEditProperty::td
||
864 tagAtom
==nsEditProperty::th
||
865 tagAtom
==nsEditProperty::col
||
866 tagAtom
==nsEditProperty::colgroup
||
867 tagAtom
==nsEditProperty::li
||
868 tagAtom
==nsEditProperty::dt
||
869 tagAtom
==nsEditProperty::dd
||
870 tagAtom
==nsEditProperty::legend
)
877 // We don't have an element -- probably a text node
878 nsCOMPtr
<nsIDOMCharacterData
>nodeAsText
= do_QueryInterface(aNode
);
881 *aIsBlock
= PR_FALSE
;
887 #endif /* USE_PARSER_FOR_BLOCKNESS */
891 nsHTMLEditor::NodeIsBlock(nsIDOMNode
*aNode
, PRBool
*aIsBlock
)
893 return NodeIsBlockStatic(aNode
, aIsBlock
);
897 nsHTMLEditor::IsBlockNode(nsIDOMNode
*aNode
)
900 NodeIsBlockStatic(aNode
, &isBlock
);
904 // Non-static version for the nsIEditor interface and JavaScript
906 nsHTMLEditor::SetDocumentTitle(const nsAString
&aTitle
)
908 nsRefPtr
<SetDocTitleTxn
> txn
= new SetDocTitleTxn();
909 NS_ENSURE_TRUE(txn
, NS_ERROR_OUT_OF_MEMORY
);
911 nsresult result
= txn
->Init(this, &aTitle
);
912 NS_ENSURE_SUCCESS(result
, result
);
914 //Don't let Rules System change the selection
915 nsAutoTxnsConserveSelection
dontChangeSelection(this);
916 return nsEditor::DoTransaction(txn
);
919 /* ------------ Block methods moved from nsEditor -------------- */
920 ///////////////////////////////////////////////////////////////////////////
921 // GetBlockNodeParent: returns enclosing block level ancestor, if any
924 nsHTMLEditor::GetBlockNodeParent(nsIDOMNode
*aNode
)
926 nsCOMPtr
<nsIDOMNode
> tmp
;
927 nsCOMPtr
<nsIDOMNode
> p
;
931 NS_NOTREACHED("null node passed to GetBlockNodeParent()");
935 if (NS_FAILED(aNode
->GetParentNode(getter_AddRefs(p
)))) // no parent, ran off top of tree
941 if (NS_FAILED(NodeIsBlockStatic(p
, &isBlock
)) || isBlock
)
943 if ( NS_FAILED(p
->GetParentNode(getter_AddRefs(tmp
))) || !tmp
) // no parent, ran off top of tree
951 ///////////////////////////////////////////////////////////////////////////
952 // GetBlockSection: return leftmost/rightmost nodes in aChild's block
955 nsHTMLEditor::GetBlockSection(nsIDOMNode
*aChild
,
956 nsIDOMNode
**aLeftNode
,
957 nsIDOMNode
**aRightNode
)
959 nsresult result
= NS_OK
;
960 if (!aChild
|| !aLeftNode
|| !aRightNode
) {return NS_ERROR_NULL_POINTER
;}
962 *aRightNode
= aChild
;
964 nsCOMPtr
<nsIDOMNode
>sibling
;
965 result
= aChild
->GetPreviousSibling(getter_AddRefs(sibling
));
966 while ((NS_SUCCEEDED(result
)) && sibling
)
969 NodeIsBlockStatic(sibling
, &isBlock
);
972 nsCOMPtr
<nsIDOMCharacterData
>nodeAsText
= do_QueryInterface(sibling
);
976 // XXX: needs some logic to work for other leaf nodes besides text!
978 *aLeftNode
= sibling
;
979 result
= (*aLeftNode
)->GetPreviousSibling(getter_AddRefs(sibling
));
981 NS_ADDREF((*aLeftNode
));
982 // now do the right side
983 result
= aChild
->GetNextSibling(getter_AddRefs(sibling
));
984 while ((NS_SUCCEEDED(result
)) && sibling
)
987 NodeIsBlockStatic(sibling
, &isBlock
);
990 nsCOMPtr
<nsIDOMCharacterData
>nodeAsText
= do_QueryInterface(sibling
);
995 *aRightNode
= sibling
;
996 result
= (*aRightNode
)->GetNextSibling(getter_AddRefs(sibling
));
998 NS_ADDREF((*aRightNode
));
1004 ///////////////////////////////////////////////////////////////////////////
1005 // GetBlockSectionsForRange: return list of block sections that intersect
1008 nsHTMLEditor::GetBlockSectionsForRange(nsIDOMRange
*aRange
,
1009 nsCOMArray
<nsIDOMRange
>& aSections
)
1011 if (!aRange
) {return NS_ERROR_NULL_POINTER
;}
1014 nsCOMPtr
<nsIContentIterator
>iter
=
1015 do_CreateInstance("@mozilla.org/content/post-content-iterator;1", &result
);
1016 if ((NS_SUCCEEDED(result
)) && iter
)
1018 nsCOMPtr
<nsIDOMRange
> lastRange
;
1020 while (iter
->IsDone())
1022 nsCOMPtr
<nsIContent
> currentContent
=
1023 do_QueryInterface(iter
->GetCurrentNode());
1025 nsCOMPtr
<nsIDOMNode
>currentNode
= do_QueryInterface(currentContent
);
1028 // <BR> divides block content ranges. We can achieve this by nulling out lastRange
1029 if (currentContent
->Tag() == nsEditProperty::br
)
1035 PRBool isNotInlineOrText
;
1036 result
= NodeIsBlockStatic(currentNode
, &isNotInlineOrText
);
1037 if (isNotInlineOrText
)
1040 currentNode
->GetNodeType(&nodeType
);
1041 if (nsIDOMNode::TEXT_NODE
== nodeType
) {
1042 isNotInlineOrText
= PR_TRUE
;
1045 if (PR_FALSE
==isNotInlineOrText
)
1047 nsCOMPtr
<nsIDOMNode
>leftNode
;
1048 nsCOMPtr
<nsIDOMNode
>rightNode
;
1049 result
= GetBlockSection(currentNode
,
1050 getter_AddRefs(leftNode
),
1051 getter_AddRefs(rightNode
));
1052 if ((NS_SUCCEEDED(result
)) && leftNode
&& rightNode
)
1054 // add range to the list if it doesn't overlap with the previous range
1055 PRBool addRange
=PR_TRUE
;
1058 nsCOMPtr
<nsIDOMNode
> lastStartNode
;
1059 nsCOMPtr
<nsIDOMElement
> blockParentOfLastStartNode
;
1060 lastRange
->GetStartContainer(getter_AddRefs(lastStartNode
));
1061 blockParentOfLastStartNode
= do_QueryInterface(GetBlockNodeParent(lastStartNode
));
1062 if (blockParentOfLastStartNode
)
1064 nsCOMPtr
<nsIDOMElement
> blockParentOfLeftNode
;
1065 blockParentOfLeftNode
= do_QueryInterface(GetBlockNodeParent(leftNode
));
1066 if (blockParentOfLeftNode
)
1068 if (blockParentOfLastStartNode
==blockParentOfLeftNode
) {
1069 addRange
= PR_FALSE
;
1074 if (PR_TRUE
==addRange
)
1076 nsCOMPtr
<nsIDOMRange
> range
=
1077 do_CreateInstance("@mozilla.org/content/range;1", &result
);
1078 if ((NS_SUCCEEDED(result
)) && range
)
1079 { // initialize the range
1080 range
->SetStart(leftNode
, 0);
1081 range
->SetEnd(rightNode
, 0);
1082 aSections
.AppendObject(range
);
1083 lastRange
= do_QueryInterface(range
);
1090 /* do not check result here, and especially do not return the result code.
1091 * we rely on iter->IsDone to tell us when the iteration is complete
1100 ///////////////////////////////////////////////////////////////////////////
1101 // NextNodeInBlock: gets the next/prev node in the block, if any. Next node
1102 // must be an element or text node, others are ignored
1103 nsCOMPtr
<nsIDOMNode
>
1104 nsHTMLEditor::NextNodeInBlock(nsIDOMNode
*aNode
, IterDirection aDir
)
1106 nsCOMPtr
<nsIDOMNode
> nullNode
;
1107 nsCOMPtr
<nsIContent
> content
;
1108 nsCOMPtr
<nsIContent
> blockContent
;
1109 nsCOMPtr
<nsIDOMNode
> node
;
1110 nsCOMPtr
<nsIDOMNode
> blockParent
;
1112 NS_ENSURE_TRUE(aNode
, nullNode
);
1115 nsCOMPtr
<nsIContentIterator
> iter
=
1116 do_CreateInstance("@mozilla.org/content/post-content-iterator;1", &rv
);
1117 NS_ENSURE_SUCCESS(rv
, nullNode
);
1119 // much gnashing of teeth as we twit back and forth between content and domnode types
1120 content
= do_QueryInterface(aNode
);
1122 if (NS_SUCCEEDED(NodeIsBlockStatic(aNode
, &isBlock
)) && isBlock
)
1124 blockParent
= aNode
;
1128 blockParent
= GetBlockNodeParent(aNode
);
1130 NS_ENSURE_TRUE(blockParent
, nullNode
);
1131 blockContent
= do_QueryInterface(blockParent
);
1132 NS_ENSURE_TRUE(blockContent
, nullNode
);
1134 if (NS_FAILED(iter
->Init(blockContent
))) return nullNode
;
1135 if (NS_FAILED(iter
->PositionAt(content
))) return nullNode
;
1137 while (!iter
->IsDone())
1139 // ignore nodes that aren't elements or text, or that are the
1141 node
= do_QueryInterface(iter
->GetCurrentNode());
1142 if (node
&& IsTextOrElementNode(node
) && node
!= blockParent
&&
1146 if (aDir
== kIterForward
)
1155 static const PRUnichar nbsp
= 160;
1157 ///////////////////////////////////////////////////////////////////////////
1158 // IsNextCharWhitespace: checks the adjacent content in the same block
1159 // to see if following selection is whitespace or nbsp
1161 nsHTMLEditor::IsNextCharWhitespace(nsIDOMNode
*aParentNode
,
1165 nsCOMPtr
<nsIDOMNode
> *outNode
,
1168 NS_ENSURE_TRUE(outIsSpace
&& outIsNBSP
, NS_ERROR_NULL_POINTER
);
1169 *outIsSpace
= PR_FALSE
;
1170 *outIsNBSP
= PR_FALSE
;
1171 if (outNode
) *outNode
= nsnull
;
1172 if (outOffset
) *outOffset
= -1;
1174 nsAutoString tempString
;
1176 nsCOMPtr
<nsIDOMText
> textNode
= do_QueryInterface(aParentNode
);
1179 textNode
->GetLength(&strLength
);
1180 if ((PRUint32
)aOffset
< strLength
)
1182 // easy case: next char is in same node
1183 textNode
->SubstringData(aOffset
,aOffset
+1,tempString
);
1184 *outIsSpace
= nsCRT::IsAsciiSpace(tempString
.First());
1185 *outIsNBSP
= (tempString
.First() == nbsp
);
1186 if (outNode
) *outNode
= do_QueryInterface(aParentNode
);
1187 if (outOffset
) *outOffset
= aOffset
+1; // yes, this is _past_ the character;
1192 // harder case: next char in next node.
1193 nsCOMPtr
<nsIDOMNode
> node
= NextNodeInBlock(aParentNode
, kIterForward
);
1194 nsCOMPtr
<nsIDOMNode
> tmp
;
1197 PRBool
isBlock (PR_FALSE
);
1198 NodeIsBlock(node
, &isBlock
);
1199 if (isBlock
) // skip over bold, italic, link, ect nodes
1201 if (IsTextNode(node
) && IsEditable(node
))
1203 textNode
= do_QueryInterface(node
);
1204 textNode
->GetLength(&strLength
);
1207 textNode
->SubstringData(0,1,tempString
);
1208 *outIsSpace
= nsCRT::IsAsciiSpace(tempString
.First());
1209 *outIsNBSP
= (tempString
.First() == nbsp
);
1210 if (outNode
) *outNode
= do_QueryInterface(node
);
1211 if (outOffset
) *outOffset
= 1; // yes, this is _past_ the character;
1214 // else it's an empty text node, or not editable; skip it.
1216 else // node is an image or some other thingy that doesn't count as whitespace
1222 node
= NextNodeInBlock(tmp
, kIterForward
);
1229 ///////////////////////////////////////////////////////////////////////////
1230 // IsPrevCharWhitespace: checks the adjacent content in the same block
1231 // to see if following selection is whitespace
1233 nsHTMLEditor::IsPrevCharWhitespace(nsIDOMNode
*aParentNode
,
1237 nsCOMPtr
<nsIDOMNode
> *outNode
,
1240 NS_ENSURE_TRUE(outIsSpace
&& outIsNBSP
, NS_ERROR_NULL_POINTER
);
1241 *outIsSpace
= PR_FALSE
;
1242 *outIsNBSP
= PR_FALSE
;
1243 if (outNode
) *outNode
= nsnull
;
1244 if (outOffset
) *outOffset
= -1;
1246 nsAutoString tempString
;
1248 nsCOMPtr
<nsIDOMText
> textNode
= do_QueryInterface(aParentNode
);
1253 // easy case: prev char is in same node
1254 textNode
->SubstringData(aOffset
-1,aOffset
,tempString
);
1255 *outIsSpace
= nsCRT::IsAsciiSpace(tempString
.First());
1256 *outIsNBSP
= (tempString
.First() == nbsp
);
1257 if (outNode
) *outNode
= do_QueryInterface(aParentNode
);
1258 if (outOffset
) *outOffset
= aOffset
-1;
1263 // harder case: prev char in next node
1264 nsCOMPtr
<nsIDOMNode
> node
= NextNodeInBlock(aParentNode
, kIterBackward
);
1265 nsCOMPtr
<nsIDOMNode
> tmp
;
1268 PRBool
isBlock (PR_FALSE
);
1269 NodeIsBlock(node
, &isBlock
);
1270 if (isBlock
) // skip over bold, italic, link, ect nodes
1272 if (IsTextNode(node
) && IsEditable(node
))
1274 textNode
= do_QueryInterface(node
);
1275 textNode
->GetLength(&strLength
);
1278 // you could use nsIContent::TextIsOnlyWhitespace here
1279 textNode
->SubstringData(strLength
-1,strLength
,tempString
);
1280 *outIsSpace
= nsCRT::IsAsciiSpace(tempString
.First());
1281 *outIsNBSP
= (tempString
.First() == nbsp
);
1282 if (outNode
) *outNode
= do_QueryInterface(aParentNode
);
1283 if (outOffset
) *outOffset
= strLength
-1;
1286 // else it's an empty text node, or not editable; skip it.
1288 else // node is an image or some other thingy that doesn't count as whitespace
1293 // otherwise we found a node we want to skip, keep going
1295 node
= NextNodeInBlock(tmp
, kIterBackward
);
1304 /* ------------ End Block methods -------------- */
1307 PRBool
nsHTMLEditor::IsVisBreak(nsIDOMNode
*aNode
)
1309 NS_ENSURE_TRUE(aNode
, PR_FALSE
);
1310 if (!nsTextEditUtils::IsBreak(aNode
))
1312 // check if there is a later node in block after br
1313 nsCOMPtr
<nsIDOMNode
> priorNode
, nextNode
;
1314 GetPriorHTMLNode(aNode
, address_of(priorNode
), PR_TRUE
);
1315 GetNextHTMLNode(aNode
, address_of(nextNode
), PR_TRUE
);
1316 // if we are next to another break, we are visible
1317 if (priorNode
&& nsTextEditUtils::IsBreak(priorNode
))
1319 if (nextNode
&& nsTextEditUtils::IsBreak(nextNode
))
1322 // if we are right before block boundary, then br not visible
1323 NS_ENSURE_TRUE(nextNode
, PR_FALSE
); // this break is trailer in block, it's not visible
1324 if (IsBlockNode(nextNode
))
1325 return PR_FALSE
; // break is right before a block, it's not visible
1327 // sigh. We have to use expensive whitespace calculation code to
1328 // determine what is going on
1329 nsCOMPtr
<nsIDOMNode
> selNode
, tmp
;
1331 GetNodeLocation(aNode
, address_of(selNode
), &selOffset
);
1332 selOffset
++; // lets look after the break
1333 nsWSRunObject
wsObj(this, selNode
, selOffset
);
1334 nsCOMPtr
<nsIDOMNode
> visNode
;
1335 PRInt32 visOffset
=0;
1337 wsObj
.NextVisibleNode(selNode
, selOffset
, address_of(visNode
), &visOffset
, &visType
);
1338 if (visType
& nsWSRunObject::eBlock
)
1345 nsHTMLEditor::BreakIsVisible(nsIDOMNode
*aNode
, PRBool
*aIsVisible
)
1347 NS_ENSURE_ARG_POINTER(aNode
&& aIsVisible
);
1349 *aIsVisible
= IsVisBreak(aNode
);
1356 nsHTMLEditor::GetIsDocumentEditable(PRBool
*aIsDocumentEditable
)
1358 NS_ENSURE_ARG_POINTER(aIsDocumentEditable
);
1360 nsCOMPtr
<nsIDOMDocument
> doc
;
1361 GetDocument(getter_AddRefs(doc
));
1362 *aIsDocumentEditable
= doc
? IsModifiable() : PR_FALSE
;
1367 PRBool
nsHTMLEditor::IsModifiable()
1369 return !IsReadonly();
1374 #pragma mark nsIHTMLEditor methods
1379 nsHTMLEditor::UpdateBaseURL()
1381 nsCOMPtr
<nsIDOMDocument
> domDoc
;
1382 GetDocument(getter_AddRefs(domDoc
));
1383 NS_ENSURE_TRUE(domDoc
, NS_ERROR_FAILURE
);
1385 // Look for an HTML <base> tag
1386 nsCOMPtr
<nsIDOMNodeList
> nodeList
;
1387 nsresult rv
= domDoc
->GetElementsByTagName(NS_LITERAL_STRING("base"), getter_AddRefs(nodeList
));
1388 NS_ENSURE_SUCCESS(rv
, rv
);
1390 nsCOMPtr
<nsIDOMNode
> baseNode
;
1394 nodeList
->GetLength(&count
);
1397 rv
= nodeList
->Item(0, getter_AddRefs(baseNode
));
1398 NS_ENSURE_SUCCESS(rv
, rv
);
1401 // If no base tag, then set baseURL to the document's URL
1402 // This is very important, else relative URLs for links and images are wrong
1405 nsCOMPtr
<nsIDocument
> doc
= do_QueryInterface(domDoc
);
1406 NS_ENSURE_TRUE(doc
, NS_ERROR_FAILURE
);
1408 return doc
->SetBaseURI(doc
->GetDocumentURI());
1413 /* This routine is needed to provide a bottleneck for typing for logging
1414 purposes. Can't use HandleKeyPress() (above) for that since it takes
1415 a nsIDOMKeyEvent* parameter. So instead we pass enough info through
1416 to TypedText() to determine what action to take, but without passing
1419 NS_IMETHODIMP
nsHTMLEditor::TypedText(const nsAString
& aString
,
1422 nsAutoPlaceHolderBatch
batch(this, nsGkAtoms::TypingTxnName
);
1429 return nsPlaintextEditor::TypedText(aString
, aAction
);
1433 nsCOMPtr
<nsIDOMNode
> brNode
;
1434 return InsertBR(address_of(brNode
)); // only inserts a br node
1437 return NS_ERROR_FAILURE
;
1440 NS_IMETHODIMP
nsHTMLEditor::TabInTable(PRBool inIsShift
, PRBool
*outHandled
)
1442 NS_ENSURE_TRUE(outHandled
, NS_ERROR_NULL_POINTER
);
1443 *outHandled
= PR_FALSE
;
1445 // Find enclosing table cell from the selection (cell may be the selected element)
1446 nsCOMPtr
<nsIDOMElement
> cellElement
;
1447 // can't use |NS_LITERAL_STRING| here until |GetElementOrParentByTagName| is fixed to accept readables
1448 nsresult res
= GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nsnull
, getter_AddRefs(cellElement
));
1449 NS_ENSURE_SUCCESS(res
, res
);
1450 // Do nothing -- we didn't find a table cell
1451 NS_ENSURE_TRUE(cellElement
, NS_OK
);
1453 // find enclosing table
1454 nsCOMPtr
<nsIDOMNode
> tbl
= GetEnclosingTable(cellElement
);
1455 NS_ENSURE_TRUE(tbl
, res
);
1457 // advance to next cell
1458 // first create an iterator over the table
1459 nsCOMPtr
<nsIContentIterator
> iter
=
1460 do_CreateInstance("@mozilla.org/content/post-content-iterator;1", &res
);
1461 NS_ENSURE_SUCCESS(res
, res
);
1462 NS_ENSURE_TRUE(iter
, NS_ERROR_NULL_POINTER
);
1463 nsCOMPtr
<nsIContent
> cTbl
= do_QueryInterface(tbl
);
1464 nsCOMPtr
<nsIContent
> cBlock
= do_QueryInterface(cellElement
);
1465 res
= iter
->Init(cTbl
);
1466 NS_ENSURE_SUCCESS(res
, res
);
1467 // position iter at block
1468 res
= iter
->PositionAt(cBlock
);
1469 NS_ENSURE_SUCCESS(res
, res
);
1471 nsCOMPtr
<nsIDOMNode
> node
;
1479 node
= do_QueryInterface(iter
->GetCurrentNode());
1481 if (node
&& nsHTMLEditUtils::IsTableCell(node
) &&
1482 GetEnclosingTable(node
) == tbl
)
1484 res
= CollapseSelectionToDeepestNonTableFirstChild(nsnull
, node
);
1485 NS_ENSURE_SUCCESS(res
, res
);
1486 *outHandled
= PR_TRUE
;
1489 } while (!iter
->IsDone());
1491 if (!(*outHandled
) && !inIsShift
)
1493 // if we havent handled it yet then we must have run off the end of
1494 // the table. Insert a new row.
1495 res
= InsertTableRow(1, PR_TRUE
);
1496 NS_ENSURE_SUCCESS(res
, res
);
1497 *outHandled
= PR_TRUE
;
1498 // put selection in right place
1499 // Use table code to get selection and index to new row...
1500 nsCOMPtr
<nsISelection
>selection
;
1501 nsCOMPtr
<nsIDOMElement
> tblElement
;
1502 nsCOMPtr
<nsIDOMElement
> cell
;
1504 res
= GetCellContext(getter_AddRefs(selection
),
1505 getter_AddRefs(tblElement
),
1506 getter_AddRefs(cell
),
1509 NS_ENSURE_SUCCESS(res
, res
);
1510 // ...so that we can ask for first cell in that row...
1511 res
= GetCellAt(tblElement
, row
, 0, getter_AddRefs(cell
));
1512 NS_ENSURE_SUCCESS(res
, res
);
1513 // ...and then set selection there.
1514 // (Note that normally you should use CollapseSelectionToDeepestNonTableFirstChild(),
1515 // but we know cell is an empty new cell, so this works fine)
1516 node
= do_QueryInterface(cell
);
1517 if (node
) selection
->Collapse(node
,0);
1524 NS_IMETHODIMP
nsHTMLEditor::CreateBRImpl(nsCOMPtr
<nsIDOMNode
> *aInOutParent
,
1525 PRInt32
*aInOutOffset
,
1526 nsCOMPtr
<nsIDOMNode
> *outBRNode
,
1529 NS_ENSURE_TRUE(aInOutParent
&& *aInOutParent
&& aInOutOffset
&& outBRNode
, NS_ERROR_NULL_POINTER
);
1530 *outBRNode
= nsnull
;
1533 // we need to insert a br. unfortunately, we may have to split a text node to do it.
1534 nsCOMPtr
<nsIDOMNode
> node
= *aInOutParent
;
1535 PRInt32 theOffset
= *aInOutOffset
;
1536 nsCOMPtr
<nsIDOMCharacterData
> nodeAsText
= do_QueryInterface(node
);
1537 NS_NAMED_LITERAL_STRING(brType
, "br");
1538 nsCOMPtr
<nsIDOMNode
> brNode
;
1541 nsCOMPtr
<nsIDOMNode
> tmp
;
1544 nodeAsText
->GetLength(&len
);
1545 GetNodeLocation(node
, address_of(tmp
), &offset
);
1546 NS_ENSURE_TRUE(tmp
, NS_ERROR_FAILURE
);
1549 // we are already set to go
1551 else if (theOffset
== (PRInt32
)len
)
1553 // update offset to point AFTER the text node
1558 // split the text node
1559 res
= SplitNode(node
, theOffset
, getter_AddRefs(tmp
));
1560 NS_ENSURE_SUCCESS(res
, res
);
1561 res
= GetNodeLocation(node
, address_of(tmp
), &offset
);
1562 NS_ENSURE_SUCCESS(res
, res
);
1565 res
= CreateNode(brType
, tmp
, offset
, getter_AddRefs(brNode
));
1566 NS_ENSURE_SUCCESS(res
, res
);
1567 *aInOutParent
= tmp
;
1568 *aInOutOffset
= offset
+1;
1572 res
= CreateNode(brType
, node
, theOffset
, getter_AddRefs(brNode
));
1573 NS_ENSURE_SUCCESS(res
, res
);
1577 *outBRNode
= brNode
;
1578 if (*outBRNode
&& (aSelect
!= eNone
))
1580 nsCOMPtr
<nsISelection
> selection
;
1581 nsCOMPtr
<nsIDOMNode
> parent
;
1583 res
= GetSelection(getter_AddRefs(selection
));
1584 NS_ENSURE_SUCCESS(res
, res
);
1585 nsCOMPtr
<nsISelectionPrivate
> selPriv(do_QueryInterface(selection
));
1586 res
= GetNodeLocation(*outBRNode
, address_of(parent
), &offset
);
1587 NS_ENSURE_SUCCESS(res
, res
);
1588 if (aSelect
== eNext
)
1590 // position selection after br
1591 selPriv
->SetInterlinePosition(PR_TRUE
);
1592 res
= selection
->Collapse(parent
, offset
+1);
1594 else if (aSelect
== ePrevious
)
1596 // position selection before br
1597 selPriv
->SetInterlinePosition(PR_TRUE
);
1598 res
= selection
->Collapse(parent
, offset
);
1605 NS_IMETHODIMP
nsHTMLEditor::CreateBR(nsIDOMNode
*aNode
, PRInt32 aOffset
, nsCOMPtr
<nsIDOMNode
> *outBRNode
, EDirection aSelect
)
1607 nsCOMPtr
<nsIDOMNode
> parent
= aNode
;
1608 PRInt32 offset
= aOffset
;
1609 return CreateBRImpl(address_of(parent
), &offset
, outBRNode
, aSelect
);
1612 NS_IMETHODIMP
nsHTMLEditor::InsertBR(nsCOMPtr
<nsIDOMNode
> *outBRNode
)
1615 nsCOMPtr
<nsISelection
> selection
;
1617 NS_ENSURE_TRUE(outBRNode
, NS_ERROR_NULL_POINTER
);
1618 *outBRNode
= nsnull
;
1620 // calling it text insertion to trigger moz br treatment by rules
1621 nsAutoRules
beginRulesSniffing(this, kOpInsertText
, nsIEditor::eNext
);
1623 nsresult res
= GetSelection(getter_AddRefs(selection
));
1624 NS_ENSURE_SUCCESS(res
, res
);
1625 nsCOMPtr
<nsISelectionPrivate
> selPriv(do_QueryInterface(selection
));
1626 res
= selection
->GetIsCollapsed(&bCollapsed
);
1627 NS_ENSURE_SUCCESS(res
, res
);
1630 res
= DeleteSelection(nsIEditor::eNone
);
1631 NS_ENSURE_SUCCESS(res
, res
);
1633 nsCOMPtr
<nsIDOMNode
> selNode
;
1635 res
= GetStartNodeAndOffset(selection
, getter_AddRefs(selNode
), &selOffset
);
1636 NS_ENSURE_SUCCESS(res
, res
);
1638 res
= CreateBR(selNode
, selOffset
, outBRNode
);
1639 NS_ENSURE_SUCCESS(res
, res
);
1641 // position selection after br
1642 res
= GetNodeLocation(*outBRNode
, address_of(selNode
), &selOffset
);
1643 NS_ENSURE_SUCCESS(res
, res
);
1644 selPriv
->SetInterlinePosition(PR_TRUE
);
1645 res
= selection
->Collapse(selNode
, selOffset
+1);
1651 nsHTMLEditor::CollapseSelectionToDeepestNonTableFirstChild(nsISelection
*aSelection
, nsIDOMNode
*aNode
)
1653 NS_ENSURE_TRUE(aNode
, NS_ERROR_NULL_POINTER
);
1656 nsCOMPtr
<nsISelection
> selection
;
1659 selection
= aSelection
;
1661 res
= GetSelection(getter_AddRefs(selection
));
1662 NS_ENSURE_SUCCESS(res
, res
);
1663 NS_ENSURE_TRUE(selection
, NS_ERROR_FAILURE
);
1665 nsCOMPtr
<nsIDOMNode
> node
= aNode
;
1666 nsCOMPtr
<nsIDOMNode
> child
;
1669 node
->GetFirstChild(getter_AddRefs(child
));
1673 // Stop if we find a table
1674 // don't want to go into nested tables
1675 if (nsHTMLEditUtils::IsTable(child
)) break;
1676 // hey, it'g gotta be a container too!
1677 if (!IsContainer(child
)) break;
1683 selection
->Collapse(node
,0);
1688 // This is mostly like InsertHTMLWithCharsetAndContext,
1689 // but we can't use that because it is selection-based and
1690 // the rules code won't let us edit under the <head> node
1692 nsHTMLEditor::ReplaceHeadContentsWithHTML(const nsAString
& aSourceToInsert
)
1694 nsAutoRules
beginRulesSniffing(this, kOpIgnore
, nsIEditor::eNone
); // don't do any post processing, rules get confused
1695 nsCOMPtr
<nsISelection
> selection
;
1696 nsresult res
= GetSelection(getter_AddRefs(selection
));
1697 NS_ENSURE_SUCCESS(res
, res
);
1698 NS_ENSURE_TRUE(selection
, NS_ERROR_NULL_POINTER
);
1700 ForceCompositionEnd();
1702 // Do not use nsAutoRules -- rules code won't let us insert in <head>
1703 // Use the head node as a parent and delete/insert directly
1704 nsCOMPtr
<nsIDOMDocument
> doc
= do_QueryReferent(mDocWeak
);
1705 NS_ENSURE_TRUE(doc
, NS_ERROR_NOT_INITIALIZED
);
1707 nsCOMPtr
<nsIDOMNodeList
>nodeList
;
1708 res
= doc
->GetElementsByTagName(NS_LITERAL_STRING("head"), getter_AddRefs(nodeList
));
1709 NS_ENSURE_SUCCESS(res
, res
);
1710 NS_ENSURE_TRUE(nodeList
, NS_ERROR_NULL_POINTER
);
1713 nodeList
->GetLength(&count
);
1714 if (count
< 1) return NS_ERROR_FAILURE
;
1716 nsCOMPtr
<nsIDOMNode
> headNode
;
1717 res
= nodeList
->Item(0, getter_AddRefs(headNode
));
1718 NS_ENSURE_SUCCESS(res
, res
);
1719 NS_ENSURE_TRUE(headNode
, NS_ERROR_NULL_POINTER
);
1721 // First, make sure there are no return chars in the source.
1722 // Bad things happen if you insert returns (instead of dom newlines, \n)
1723 // into an editor document.
1724 nsAutoString
inputString (aSourceToInsert
); // hope this does copy-on-write
1726 // Windows linebreaks: Map CRLF to LF:
1727 inputString
.ReplaceSubstring(NS_LITERAL_STRING("\r\n").get(),
1728 NS_LITERAL_STRING("\n").get());
1730 // Mac linebreaks: Map any remaining CR to LF:
1731 inputString
.ReplaceSubstring(NS_LITERAL_STRING("\r").get(),
1732 NS_LITERAL_STRING("\n").get());
1734 nsAutoEditBatch
beginBatching(this);
1736 res
= GetSelection(getter_AddRefs(selection
));
1737 NS_ENSURE_SUCCESS(res
, res
);
1738 NS_ENSURE_TRUE(selection
, NS_ERROR_NULL_POINTER
);
1740 // Get the first range in the selection, for context:
1741 nsCOMPtr
<nsIDOMRange
> range
;
1742 res
= selection
->GetRangeAt(0, getter_AddRefs(range
));
1743 NS_ENSURE_SUCCESS(res
, res
);
1745 nsCOMPtr
<nsIDOMNSRange
> nsrange (do_QueryInterface(range
));
1746 NS_ENSURE_TRUE(nsrange
, NS_ERROR_NO_INTERFACE
);
1747 nsCOMPtr
<nsIDOMDocumentFragment
> docfrag
;
1748 res
= nsrange
->CreateContextualFragment(inputString
,
1749 getter_AddRefs(docfrag
));
1751 //XXXX BUG 50965: This is not returning the text between <title> ... </title>
1752 // Special code is needed in JS to handle title anyway, so it really doesn't matter!
1757 printf("Couldn't create contextual fragment: error was %d\n", res
);
1761 NS_ENSURE_TRUE(docfrag
, NS_ERROR_NULL_POINTER
);
1763 nsCOMPtr
<nsIDOMNode
> child
;
1765 // First delete all children in head
1767 res
= headNode
->GetFirstChild(getter_AddRefs(child
));
1768 NS_ENSURE_SUCCESS(res
, res
);
1771 res
= DeleteNode(child
);
1772 NS_ENSURE_SUCCESS(res
, res
);
1776 // Now insert the new nodes
1777 PRInt32 offsetOfNewNode
= 0;
1778 nsCOMPtr
<nsIDOMNode
> fragmentAsNode (do_QueryInterface(docfrag
));
1780 // Loop over the contents of the fragment and move into the document
1782 res
= fragmentAsNode
->GetFirstChild(getter_AddRefs(child
));
1783 NS_ENSURE_SUCCESS(res
, res
);
1786 res
= InsertNode(child
, headNode
, offsetOfNewNode
++);
1787 NS_ENSURE_SUCCESS(res
, res
);
1795 nsHTMLEditor::RebuildDocumentFromSource(const nsAString
& aSourceString
)
1797 ForceCompositionEnd();
1799 nsCOMPtr
<nsISelection
>selection
;
1800 nsresult res
= GetSelection(getter_AddRefs(selection
));
1801 NS_ENSURE_SUCCESS(res
, res
);
1803 nsIDOMElement
*bodyElement
= GetRoot();
1804 NS_ENSURE_SUCCESS(res
, res
);
1805 NS_ENSURE_TRUE(bodyElement
, NS_ERROR_NULL_POINTER
);
1807 // Find where the <body> tag starts.
1808 nsReadingIterator
<PRUnichar
> beginbody
;
1809 nsReadingIterator
<PRUnichar
> endbody
;
1810 aSourceString
.BeginReading(beginbody
);
1811 aSourceString
.EndReading(endbody
);
1812 PRBool foundbody
= CaseInsensitiveFindInReadable(NS_LITERAL_STRING("<body"),
1813 beginbody
, endbody
);
1815 nsReadingIterator
<PRUnichar
> beginhead
;
1816 nsReadingIterator
<PRUnichar
> endhead
;
1817 aSourceString
.BeginReading(beginhead
);
1818 aSourceString
.EndReading(endhead
);
1819 PRBool foundhead
= CaseInsensitiveFindInReadable(NS_LITERAL_STRING("<head"),
1820 beginhead
, endhead
);
1822 nsReadingIterator
<PRUnichar
> beginclosehead
;
1823 nsReadingIterator
<PRUnichar
> endclosehead
;
1824 aSourceString
.BeginReading(beginclosehead
);
1825 aSourceString
.EndReading(endclosehead
);
1827 // Find the index after "<head>"
1828 PRBool foundclosehead
= CaseInsensitiveFindInReadable(
1829 NS_LITERAL_STRING("</head>"), beginclosehead
, endclosehead
);
1831 // Time to change the document
1832 nsAutoEditBatch
beginBatching(this);
1834 nsReadingIterator
<PRUnichar
> endtotal
;
1835 aSourceString
.EndReading(endtotal
);
1839 res
= ReplaceHeadContentsWithHTML(Substring(beginhead
, beginclosehead
));
1841 res
= ReplaceHeadContentsWithHTML(Substring(beginhead
, beginbody
));
1843 // XXX Without recourse to some parser/content sink/docshell hackery
1844 // we don't really know where the head ends and the body begins
1845 // so we assume that there is no body
1846 res
= ReplaceHeadContentsWithHTML(Substring(beginhead
, endtotal
));
1848 nsReadingIterator
<PRUnichar
> begintotal
;
1849 aSourceString
.BeginReading(begintotal
);
1850 NS_NAMED_LITERAL_STRING(head
, "<head>");
1852 res
= ReplaceHeadContentsWithHTML(head
+ Substring(begintotal
, beginclosehead
));
1854 res
= ReplaceHeadContentsWithHTML(head
+ Substring(begintotal
, beginbody
));
1856 // XXX Without recourse to some parser/content sink/docshell hackery
1857 // we don't really know where the head ends and the body begins
1858 // so we assume that there is no head
1859 res
= ReplaceHeadContentsWithHTML(head
);
1861 NS_ENSURE_SUCCESS(res
, res
);
1864 NS_ENSURE_SUCCESS(res
, res
);
1867 NS_NAMED_LITERAL_STRING(body
, "<body>");
1868 // XXX Without recourse to some parser/content sink/docshell hackery
1869 // we don't really know where the head ends and the body begins
1870 if (foundclosehead
) // assume body starts after the head ends
1871 res
= LoadHTML(body
+ Substring(endclosehead
, endtotal
));
1872 else if (foundhead
) // assume there is no body
1873 res
= LoadHTML(body
);
1874 else // assume there is no head, the entire source is body
1875 res
= LoadHTML(body
+ aSourceString
);
1876 NS_ENSURE_SUCCESS(res
, res
);
1878 nsCOMPtr
<nsIDOMElement
> divElement
;
1879 res
= CreateElementWithDefaults(NS_LITERAL_STRING("div"), getter_AddRefs(divElement
));
1880 NS_ENSURE_SUCCESS(res
, res
);
1882 res
= CloneAttributes(bodyElement
, divElement
);
1883 NS_ENSURE_SUCCESS(res
, res
);
1885 return BeginningOfDocument();
1888 res
= LoadHTML(Substring(beginbody
, endtotal
));
1889 NS_ENSURE_SUCCESS(res
, res
);
1891 // Now we must copy attributes user might have edited on the <body> tag
1892 // because InsertHTML (actually, CreateContextualFragment())
1893 // will never return a body node in the DOM fragment
1895 // We already know where "<body" begins
1896 nsReadingIterator
<PRUnichar
> beginclosebody
= beginbody
;
1897 nsReadingIterator
<PRUnichar
> endclosebody
;
1898 aSourceString
.EndReading(endclosebody
);
1899 if (!FindInReadable(NS_LITERAL_STRING(">"),beginclosebody
,endclosebody
))
1900 return NS_ERROR_FAILURE
;
1902 // Truncate at the end of the body tag
1903 // Kludge of the year: fool the parser by replacing "body" with "div" so we get a node
1904 nsAutoString bodyTag
;
1905 bodyTag
.AssignLiteral("<div ");
1906 bodyTag
.Append(Substring(endbody
, endclosebody
));
1908 nsCOMPtr
<nsIDOMRange
> range
;
1909 res
= selection
->GetRangeAt(0, getter_AddRefs(range
));
1910 NS_ENSURE_SUCCESS(res
, res
);
1912 nsCOMPtr
<nsIDOMNSRange
> nsrange (do_QueryInterface(range
));
1913 NS_ENSURE_TRUE(nsrange
, NS_ERROR_NO_INTERFACE
);
1915 nsCOMPtr
<nsIDOMDocumentFragment
> docfrag
;
1916 res
= nsrange
->CreateContextualFragment(bodyTag
, getter_AddRefs(docfrag
));
1917 NS_ENSURE_SUCCESS(res
, res
);
1919 nsCOMPtr
<nsIDOMNode
> fragmentAsNode (do_QueryInterface(docfrag
));
1920 NS_ENSURE_TRUE(fragmentAsNode
, NS_ERROR_NULL_POINTER
);
1922 nsCOMPtr
<nsIDOMNode
> child
;
1923 res
= fragmentAsNode
->GetFirstChild(getter_AddRefs(child
));
1924 NS_ENSURE_SUCCESS(res
, res
);
1925 NS_ENSURE_TRUE(child
, NS_ERROR_NULL_POINTER
);
1927 // Copy all attributes from the div child to current body element
1928 res
= CloneAttributes(bodyElement
, child
);
1929 NS_ENSURE_SUCCESS(res
, res
);
1931 // place selection at first editable content
1932 return BeginningOfDocument();
1936 nsHTMLEditor::NormalizeEOLInsertPosition(nsIDOMNode
*firstNodeToInsert
,
1937 nsCOMPtr
<nsIDOMNode
> *insertParentNode
,
1938 PRInt32
*insertOffset
)
1941 This function will either correct the position passed in,
1942 or leave the position unchanged.
1944 When the (first) item to insert is a block level element,
1945 and our insertion position is after the last visible item in a line,
1946 i.e. the insertion position is just before a visible line break <br>,
1947 we want to skip to the position just after the line break (see bug 68767)
1949 However, our logic to detect whether we should skip or not
1950 needs to be more clever.
1951 We must not skip when the caret appears to be positioned at the beginning
1952 of a block, in that case skipping the <br> would not insert the <br>
1953 at the caret position, but after the current empty line.
1955 So we have several cases to test:
1957 1) We only ever want to skip, if the next visible thing after the current position is a break
1959 2) We do not want to skip if there is no previous visible thing at all
1960 That is detected if the call to PriorVisibleNode gives us an offset of zero.
1961 Because PriorVisibleNode always positions after the prior node, we would
1962 see an offset > 0, if there were a prior node.
1964 3) We do not want to skip, if both the next and the previous visible things are breaks.
1966 4) We do not want to skip if the previous visible thing is in a different block
1967 than the insertion position.
1970 if (!IsBlockNode(firstNodeToInsert
))
1973 nsWSRunObject
wsObj(this, *insertParentNode
, *insertOffset
);
1974 nsCOMPtr
<nsIDOMNode
> nextVisNode
;
1975 nsCOMPtr
<nsIDOMNode
> prevVisNode
;
1976 PRInt32 nextVisOffset
=0;
1977 PRInt16 nextVisType
=0;
1978 PRInt32 prevVisOffset
=0;
1979 PRInt16 prevVisType
=0;
1981 wsObj
.NextVisibleNode(*insertParentNode
, *insertOffset
, address_of(nextVisNode
), &nextVisOffset
, &nextVisType
);
1985 if (! (nextVisType
& nsWSRunObject::eBreak
))
1988 wsObj
.PriorVisibleNode(*insertParentNode
, *insertOffset
, address_of(prevVisNode
), &prevVisOffset
, &prevVisType
);
1992 if (prevVisType
& nsWSRunObject::eBreak
)
1995 if (prevVisType
& nsWSRunObject::eThisBlock
)
1998 nsCOMPtr
<nsIDOMNode
> brNode
;
2001 GetNodeLocation(nextVisNode
, address_of(brNode
), &brOffset
);
2003 *insertParentNode
= brNode
;
2004 *insertOffset
= brOffset
+ 1;
2008 nsHTMLEditor::InsertElementAtSelection(nsIDOMElement
* aElement
, PRBool aDeleteSelection
)
2010 // Protect the edit rules object from dying
2011 nsCOMPtr
<nsIEditRules
> kungFuDeathGrip(mRules
);
2013 nsresult res
= NS_ERROR_NOT_INITIALIZED
;
2015 NS_ENSURE_TRUE(aElement
, NS_ERROR_NULL_POINTER
);
2017 nsCOMPtr
<nsIDOMNode
> node
= do_QueryInterface(aElement
);
2019 ForceCompositionEnd();
2020 nsAutoEditBatch
beginBatching(this);
2021 nsAutoRules
beginRulesSniffing(this, kOpInsertElement
, nsIEditor::eNext
);
2023 nsCOMPtr
<nsISelection
>selection
;
2024 res
= GetSelection(getter_AddRefs(selection
));
2025 if (NS_FAILED(res
) || !selection
)
2026 return NS_ERROR_FAILURE
;
2028 // hand off to the rules system, see if it has anything to say about this
2029 PRBool cancel
, handled
;
2030 nsTextRulesInfo
ruleInfo(nsTextEditRules::kInsertElement
);
2031 ruleInfo
.insertElement
= aElement
;
2032 res
= mRules
->WillDoAction(selection
, &ruleInfo
, &cancel
, &handled
);
2033 if (cancel
|| (NS_FAILED(res
))) return res
;
2037 if (aDeleteSelection
)
2039 nsCOMPtr
<nsIDOMNode
> tempNode
;
2041 nsresult result
= DeleteSelectionAndPrepareToCreateNode(tempNode
,tempOffset
);
2042 NS_ENSURE_SUCCESS(result
, result
);
2045 // If deleting, selection will be collapsed.
2046 // so if not, we collapse it
2047 if (!aDeleteSelection
)
2049 // Named Anchor is a special case,
2050 // We collapse to insert element BEFORE the selection
2051 // For all other tags, we insert AFTER the selection
2052 if (nsHTMLEditUtils::IsNamedAnchor(node
))
2054 selection
->CollapseToStart();
2056 selection
->CollapseToEnd();
2060 nsCOMPtr
<nsIDOMNode
> parentSelectedNode
;
2061 PRInt32 offsetForInsert
;
2062 res
= selection
->GetAnchorNode(getter_AddRefs(parentSelectedNode
));
2063 // XXX: ERROR_HANDLING bad XPCOM usage
2064 if (NS_SUCCEEDED(res
) && NS_SUCCEEDED(selection
->GetAnchorOffset(&offsetForInsert
)) && parentSelectedNode
)
2066 #ifdef DEBUG_cmanske
2069 parentSelectedNode
->GetNodeName(name
);
2070 printf("InsertElement: Anchor node of selection: ");
2071 wprintf(name
.get());
2072 printf(" Offset: %d\n", offsetForInsert
);
2076 // Adjust position based on the node we are going to insert.
2077 NormalizeEOLInsertPosition(node
, address_of(parentSelectedNode
), &offsetForInsert
);
2079 res
= InsertNodeAtPoint(node
, address_of(parentSelectedNode
), &offsetForInsert
, PR_FALSE
);
2080 NS_ENSURE_SUCCESS(res
, res
);
2081 // Set caret after element, but check for special case
2082 // of inserting table-related elements: set in first cell instead
2083 if (!SetCaretInTableCell(aElement
))
2085 res
= SetCaretAfterElement(aElement
);
2086 NS_ENSURE_SUCCESS(res
, res
);
2088 // check for inserting a whole table at the end of a block. If so insert a br after it.
2089 if (nsHTMLEditUtils::IsTable(node
))
2092 res
= IsLastEditableChild(node
, &isLast
);
2093 NS_ENSURE_SUCCESS(res
, res
);
2096 nsCOMPtr
<nsIDOMNode
> brNode
;
2097 res
= CreateBR(parentSelectedNode
, offsetForInsert
+1, address_of(brNode
));
2098 NS_ENSURE_SUCCESS(res
, res
);
2099 selection
->Collapse(parentSelectedNode
, offsetForInsert
+1);
2104 res
= mRules
->DidDoAction(selection
, &ruleInfo
, res
);
2110 InsertNodeAtPoint: attempts to insert aNode into the document, at a point specified by
2111 {*ioParent,*ioOffset}. Checks with strict dtd to see if containment is allowed. If not
2112 allowed, will attempt to find a parent in the parent hierarchy of *ioParent that will
2113 accept aNode as a child. If such a parent is found, will split the document tree from
2114 {*ioParent,*ioOffset} up to parent, and then insert aNode. ioParent & ioOffset are then
2115 adjusted to point to the actual location that aNode was inserted at. aNoEmptyNodes
2116 specifies if the splitting process is allowed to reslt in empty nodes.
2117 nsIDOMNode *aNode node to insert
2118 nsCOMPtr<nsIDOMNode> *ioParent insertion parent
2119 PRInt32 *ioOffset insertion offset
2120 PRBool aNoEmptyNodes splitting can result in empty nodes?
2123 nsHTMLEditor::InsertNodeAtPoint(nsIDOMNode
*aNode
,
2124 nsCOMPtr
<nsIDOMNode
> *ioParent
,
2126 PRBool aNoEmptyNodes
)
2128 NS_ENSURE_TRUE(aNode
, NS_ERROR_NULL_POINTER
);
2129 NS_ENSURE_TRUE(ioParent
, NS_ERROR_NULL_POINTER
);
2130 NS_ENSURE_TRUE(*ioParent
, NS_ERROR_NULL_POINTER
);
2131 NS_ENSURE_TRUE(ioOffset
, NS_ERROR_NULL_POINTER
);
2133 nsresult res
= NS_OK
;
2134 nsAutoString tagName
;
2135 aNode
->GetNodeName(tagName
);
2136 ToLowerCase(tagName
);
2137 nsCOMPtr
<nsIDOMNode
> parent
= *ioParent
;
2138 nsCOMPtr
<nsIDOMNode
> topChild
= *ioParent
;
2139 nsCOMPtr
<nsIDOMNode
> tmp
;
2140 PRInt32 offsetOfInsert
= *ioOffset
;
2142 // Search up the parent chain to find a suitable container
2143 while (!CanContainTag(parent
, tagName
))
2145 // If the current parent is a root (body or table element)
2146 // then go no further - we can't insert
2147 if (nsTextEditUtils::IsBody(parent
) || nsHTMLEditUtils::IsTableElement(parent
))
2148 return NS_ERROR_FAILURE
;
2149 // Get the next parent
2150 parent
->GetParentNode(getter_AddRefs(tmp
));
2151 NS_ENSURE_TRUE(tmp
, NS_ERROR_FAILURE
);
2155 if (parent
!= topChild
)
2157 // we need to split some levels above the original selection parent
2158 res
= SplitNodeDeep(topChild
, *ioParent
, *ioOffset
, &offsetOfInsert
, aNoEmptyNodes
);
2159 NS_ENSURE_SUCCESS(res
, res
);
2161 *ioOffset
= offsetOfInsert
;
2163 // Now we can insert the new node
2164 res
= InsertNode(aNode
, parent
, offsetOfInsert
);
2169 nsHTMLEditor::SelectElement(nsIDOMElement
* aElement
)
2171 nsresult res
= NS_ERROR_NULL_POINTER
;
2173 // Must be sure that element is contained in the document body
2174 if (IsElementInBody(aElement
))
2176 nsCOMPtr
<nsISelection
> selection
;
2177 res
= GetSelection(getter_AddRefs(selection
));
2178 NS_ENSURE_SUCCESS(res
, res
);
2179 NS_ENSURE_TRUE(selection
, NS_ERROR_NULL_POINTER
);
2180 nsCOMPtr
<nsIDOMNode
>parent
;
2181 res
= aElement
->GetParentNode(getter_AddRefs(parent
));
2182 if (NS_SUCCEEDED(res
) && parent
)
2184 PRInt32 offsetInParent
;
2185 res
= GetChildOffset(aElement
, parent
, offsetInParent
);
2187 if (NS_SUCCEEDED(res
))
2189 // Collapse selection to just before desired element,
2190 res
= selection
->Collapse(parent
, offsetInParent
);
2191 if (NS_SUCCEEDED(res
)) {
2192 // then extend it to just after
2193 res
= selection
->Extend(parent
, offsetInParent
+1);
2202 nsHTMLEditor::SetCaretAfterElement(nsIDOMElement
* aElement
)
2204 nsresult res
= NS_ERROR_NULL_POINTER
;
2206 // Be sure the element is contained in the document body
2207 if (aElement
&& IsElementInBody(aElement
))
2209 nsCOMPtr
<nsISelection
> selection
;
2210 res
= GetSelection(getter_AddRefs(selection
));
2211 NS_ENSURE_SUCCESS(res
, res
);
2212 NS_ENSURE_TRUE(selection
, NS_ERROR_NULL_POINTER
);
2213 nsCOMPtr
<nsIDOMNode
>parent
;
2214 res
= aElement
->GetParentNode(getter_AddRefs(parent
));
2215 NS_ENSURE_SUCCESS(res
, res
);
2216 NS_ENSURE_TRUE(parent
, NS_ERROR_NULL_POINTER
);
2217 PRInt32 offsetInParent
;
2218 res
= GetChildOffset(aElement
, parent
, offsetInParent
);
2219 if (NS_SUCCEEDED(res
))
2221 // Collapse selection to just after desired element,
2222 res
= selection
->Collapse(parent
, offsetInParent
+1);
2223 #if 0 //def DEBUG_cmanske
2226 parent
->GetNodeName(name
);
2227 printf("SetCaretAfterElement: Parent node: ");
2228 wprintf(name
.get());
2229 printf(" Offset: %d\n\nHTML:\n", offsetInParent
+1);
2230 nsAutoString
Format("text/html");
2231 nsAutoString ContentsAs
;
2232 OutputToString(Format
, 2, ContentsAs
);
2233 wprintf(ContentsAs
.get());
2242 nsHTMLEditor::SetParagraphFormat(const nsAString
& aParagraphFormat
)
2244 nsAutoString tag
; tag
.Assign(aParagraphFormat
);
2246 if (tag
.EqualsLiteral("dd") || tag
.EqualsLiteral("dt"))
2247 return MakeDefinitionItem(tag
);
2249 return InsertBasicBlock(tag
);
2253 nsHTMLEditor::GetParagraphState(PRBool
*aMixed
, nsAString
&outFormat
)
2255 if (!mRules
) { return NS_ERROR_NOT_INITIALIZED
; }
2256 NS_ENSURE_TRUE(aMixed
, NS_ERROR_NULL_POINTER
);
2257 nsCOMPtr
<nsIHTMLEditRules
> htmlRules
= do_QueryInterface(mRules
);
2258 NS_ENSURE_TRUE(htmlRules
, NS_ERROR_FAILURE
);
2260 return htmlRules
->GetParagraphState(aMixed
, outFormat
);
2264 nsHTMLEditor::GetBackgroundColorState(PRBool
*aMixed
, nsAString
&aOutColor
)
2268 GetIsCSSEnabled(&useCSS
);
2270 // if we are in CSS mode, we have to check if the containing block defines
2271 // a background color
2272 res
= GetCSSBackgroundColorState(aMixed
, aOutColor
, PR_TRUE
);
2275 // in HTML mode, we look only at page's background
2276 res
= GetHTMLBackgroundColorState(aMixed
, aOutColor
);
2282 nsHTMLEditor::GetHighlightColorState(PRBool
*aMixed
, nsAString
&aOutColor
)
2284 nsresult res
= NS_OK
;
2286 GetIsCSSEnabled(&useCSS
);
2288 aOutColor
.AssignLiteral("transparent");
2290 // in CSS mode, text background can be added by the Text Highlight button
2291 // we need to query the background of the selection without looking for
2292 // the block container of the ranges in the selection
2293 res
= GetCSSBackgroundColorState(aMixed
, aOutColor
, PR_FALSE
);
2299 nsHTMLEditor::GetCSSBackgroundColorState(PRBool
*aMixed
, nsAString
&aOutColor
, PRBool aBlockLevel
)
2301 NS_ENSURE_TRUE(aMixed
, NS_ERROR_NULL_POINTER
);
2303 // the default background color is transparent
2304 aOutColor
.AssignLiteral("transparent");
2307 nsCOMPtr
<nsISelection
>selection
;
2308 nsresult res
= GetSelection(getter_AddRefs(selection
));
2309 NS_ENSURE_SUCCESS(res
, res
);
2311 // get selection location
2312 nsCOMPtr
<nsIDOMNode
> parent
;
2314 res
= GetStartNodeAndOffset(selection
, getter_AddRefs(parent
), &offset
);
2315 NS_ENSURE_SUCCESS(res
, res
);
2316 NS_ENSURE_TRUE(parent
, NS_ERROR_NULL_POINTER
);
2318 // is the selection collapsed?
2320 res
= selection
->GetIsCollapsed(&bCollapsed
);
2321 NS_ENSURE_SUCCESS(res
, res
);
2322 nsCOMPtr
<nsIDOMNode
> nodeToExamine
;
2323 if (bCollapsed
|| IsTextNode(parent
))
2325 // we want to look at the parent and ancestors
2326 nodeToExamine
= parent
;
2330 // otherwise we want to look at the first editable node after
2331 // {parent,offset} and it's ancestors for divs with alignment on them
2332 nodeToExamine
= GetChildAt(parent
, offset
);
2333 //GetNextNode(parent, offset, PR_TRUE, address_of(nodeToExamine));
2336 NS_ENSURE_TRUE(nodeToExamine
, NS_ERROR_NULL_POINTER
);
2338 // is the node to examine a block ?
2340 res
= NodeIsBlockStatic(nodeToExamine
, &isBlock
);
2341 NS_ENSURE_SUCCESS(res
, res
);
2343 nsCOMPtr
<nsIDOMNode
> tmp
;
2346 // we are querying the block background (and not the text background), let's
2347 // climb to the block container
2348 nsCOMPtr
<nsIDOMNode
> blockParent
= nodeToExamine
;
2350 blockParent
= GetBlockNodeParent(nodeToExamine
);
2351 NS_ENSURE_TRUE(blockParent
, NS_OK
);
2354 // Make sure to not walk off onto the Document node
2355 nsCOMPtr
<nsIDOMElement
> element
;
2357 // retrieve the computed style of background-color for blockParent
2358 mHTMLCSSUtils
->GetComputedProperty(blockParent
,
2359 nsEditProperty::cssBackgroundColor
,
2361 tmp
.swap(blockParent
);
2362 res
= tmp
->GetParentNode(getter_AddRefs(blockParent
));
2363 element
= do_QueryInterface(blockParent
);
2364 // look at parent if the queried color is transparent and if the node to
2365 // examine is not the root of the document
2366 } while (aOutColor
.EqualsLiteral("transparent") && element
);
2367 if (aOutColor
.EqualsLiteral("transparent")) {
2368 // we have hit the root of the document and the color is still transparent !
2369 // Grumble... Let's look at the default background color because that's the
2370 // color we are looking for
2371 mHTMLCSSUtils
->GetDefaultBackgroundColor(aOutColor
);
2375 // no, we are querying the text background for the Text Highlight button
2376 if (IsTextNode(nodeToExamine
)) {
2377 // if the node of interest is a text node, let's climb a level
2378 res
= nodeToExamine
->GetParentNode(getter_AddRefs(parent
));
2379 NS_ENSURE_SUCCESS(res
, res
);
2380 nodeToExamine
= parent
;
2383 // is the node to examine a block ?
2384 res
= NodeIsBlockStatic(nodeToExamine
, &isBlock
);
2385 NS_ENSURE_SUCCESS(res
, res
);
2387 // yes it is a block; in that case, the text background color is transparent
2388 aOutColor
.AssignLiteral("transparent");
2392 // no, it's not; let's retrieve the computed style of background-color for the
2394 mHTMLCSSUtils
->GetComputedProperty(nodeToExamine
, nsEditProperty::cssBackgroundColor
,
2396 if (!aOutColor
.EqualsLiteral("transparent")) {
2400 tmp
.swap(nodeToExamine
);
2401 res
= tmp
->GetParentNode(getter_AddRefs(nodeToExamine
));
2402 NS_ENSURE_SUCCESS(res
, res
);
2403 } while ( aOutColor
.EqualsLiteral("transparent") && nodeToExamine
);
2409 nsHTMLEditor::GetHTMLBackgroundColorState(PRBool
*aMixed
, nsAString
&aOutColor
)
2411 //TODO: We don't handle "mixed" correctly!
2412 NS_ENSURE_TRUE(aMixed
, NS_ERROR_NULL_POINTER
);
2414 aOutColor
.Truncate();
2416 nsCOMPtr
<nsIDOMElement
> element
;
2417 PRInt32 selectedCount
;
2418 nsAutoString tagName
;
2419 nsresult res
= GetSelectedOrParentTableElement(tagName
,
2421 getter_AddRefs(element
));
2422 NS_ENSURE_SUCCESS(res
, res
);
2424 NS_NAMED_LITERAL_STRING(styleName
, "bgcolor");
2428 // We are in a cell or selected table
2429 res
= element
->GetAttribute(styleName
, aOutColor
);
2430 NS_ENSURE_SUCCESS(res
, res
);
2432 // Done if we have a color explicitly set
2433 if (!aOutColor
.IsEmpty())
2436 // Once we hit the body, we're done
2437 if(nsTextEditUtils::IsBody(element
)) return NS_OK
;
2439 // No color is set, but we need to report visible color inherited
2440 // from nested cells/tables, so search up parent chain
2441 nsCOMPtr
<nsIDOMNode
> parentNode
;
2442 res
= element
->GetParentNode(getter_AddRefs(parentNode
));
2443 NS_ENSURE_SUCCESS(res
, res
);
2444 element
= do_QueryInterface(parentNode
);
2447 // If no table or cell found, get page body
2448 element
= GetRoot();
2449 NS_ENSURE_TRUE(element
, NS_ERROR_NULL_POINTER
);
2451 return element
->GetAttribute(styleName
, aOutColor
);
2455 nsHTMLEditor::GetListState(PRBool
*aMixed
, PRBool
*aOL
, PRBool
*aUL
, PRBool
*aDL
)
2457 if (!mRules
) { return NS_ERROR_NOT_INITIALIZED
; }
2458 NS_ENSURE_TRUE(aMixed
&& aOL
&& aUL
&& aDL
, NS_ERROR_NULL_POINTER
);
2459 nsCOMPtr
<nsIHTMLEditRules
> htmlRules
= do_QueryInterface(mRules
);
2460 NS_ENSURE_TRUE(htmlRules
, NS_ERROR_FAILURE
);
2462 return htmlRules
->GetListState(aMixed
, aOL
, aUL
, aDL
);
2466 nsHTMLEditor::GetListItemState(PRBool
*aMixed
, PRBool
*aLI
, PRBool
*aDT
, PRBool
*aDD
)
2468 if (!mRules
) { return NS_ERROR_NOT_INITIALIZED
; }
2469 NS_ENSURE_TRUE(aMixed
&& aLI
&& aDT
&& aDD
, NS_ERROR_NULL_POINTER
);
2471 nsCOMPtr
<nsIHTMLEditRules
> htmlRules
= do_QueryInterface(mRules
);
2472 NS_ENSURE_TRUE(htmlRules
, NS_ERROR_FAILURE
);
2474 return htmlRules
->GetListItemState(aMixed
, aLI
, aDT
, aDD
);
2478 nsHTMLEditor::GetAlignment(PRBool
*aMixed
, nsIHTMLEditor::EAlignment
*aAlign
)
2480 if (!mRules
) { return NS_ERROR_NOT_INITIALIZED
; }
2481 NS_ENSURE_TRUE(aMixed
&& aAlign
, NS_ERROR_NULL_POINTER
);
2482 nsCOMPtr
<nsIHTMLEditRules
> htmlRules
= do_QueryInterface(mRules
);
2483 NS_ENSURE_TRUE(htmlRules
, NS_ERROR_FAILURE
);
2485 return htmlRules
->GetAlignment(aMixed
, aAlign
);
2490 nsHTMLEditor::GetIndentState(PRBool
*aCanIndent
, PRBool
*aCanOutdent
)
2492 if (!mRules
) { return NS_ERROR_NOT_INITIALIZED
; }
2493 NS_ENSURE_TRUE(aCanIndent
&& aCanOutdent
, NS_ERROR_NULL_POINTER
);
2495 nsCOMPtr
<nsIHTMLEditRules
> htmlRules
= do_QueryInterface(mRules
);
2496 NS_ENSURE_TRUE(htmlRules
, NS_ERROR_FAILURE
);
2498 return htmlRules
->GetIndentState(aCanIndent
, aCanOutdent
);
2502 nsHTMLEditor::MakeOrChangeList(const nsAString
& aListType
, PRBool entireList
, const nsAString
& aBulletType
)
2505 if (!mRules
) { return NS_ERROR_NOT_INITIALIZED
; }
2507 // Protect the edit rules object from dying
2508 nsCOMPtr
<nsIEditRules
> kungFuDeathGrip(mRules
);
2510 nsCOMPtr
<nsISelection
> selection
;
2511 PRBool cancel
, handled
;
2513 nsAutoEditBatch
beginBatching(this);
2514 nsAutoRules
beginRulesSniffing(this, kOpMakeList
, nsIEditor::eNext
);
2517 res
= GetSelection(getter_AddRefs(selection
));
2518 NS_ENSURE_SUCCESS(res
, res
);
2519 NS_ENSURE_TRUE(selection
, NS_ERROR_NULL_POINTER
);
2521 nsTextRulesInfo
ruleInfo(nsTextEditRules::kMakeList
);
2522 ruleInfo
.blockType
= &aListType
;
2523 ruleInfo
.entireList
= entireList
;
2524 ruleInfo
.bulletType
= &aBulletType
;
2525 res
= mRules
->WillDoAction(selection
, &ruleInfo
, &cancel
, &handled
);
2526 if (cancel
|| (NS_FAILED(res
))) return res
;
2530 // Find out if the selection is collapsed:
2532 res
= selection
->GetIsCollapsed(&isCollapsed
);
2533 NS_ENSURE_SUCCESS(res
, res
);
2535 nsCOMPtr
<nsIDOMNode
> node
;
2538 res
= GetStartNodeAndOffset(selection
, getter_AddRefs(node
), &offset
);
2539 if (!node
) res
= NS_ERROR_FAILURE
;
2540 NS_ENSURE_SUCCESS(res
, res
);
2544 // have to find a place to put the list
2545 nsCOMPtr
<nsIDOMNode
> parent
= node
;
2546 nsCOMPtr
<nsIDOMNode
> topChild
= node
;
2547 nsCOMPtr
<nsIDOMNode
> tmp
;
2549 while ( !CanContainTag(parent
, aListType
))
2551 parent
->GetParentNode(getter_AddRefs(tmp
));
2552 NS_ENSURE_TRUE(tmp
, NS_ERROR_FAILURE
);
2559 // we need to split up to the child of parent
2560 res
= SplitNodeDeep(topChild
, node
, offset
, &offset
);
2561 NS_ENSURE_SUCCESS(res
, res
);
2565 nsCOMPtr
<nsIDOMNode
> newList
;
2566 res
= CreateNode(aListType
, parent
, offset
, getter_AddRefs(newList
));
2567 NS_ENSURE_SUCCESS(res
, res
);
2569 nsCOMPtr
<nsIDOMNode
> newItem
;
2570 res
= CreateNode(NS_LITERAL_STRING("li"), newList
, 0, getter_AddRefs(newItem
));
2571 NS_ENSURE_SUCCESS(res
, res
);
2572 res
= selection
->Collapse(newItem
,0);
2573 NS_ENSURE_SUCCESS(res
, res
);
2577 res
= mRules
->DidDoAction(selection
, &ruleInfo
, res
);
2583 nsHTMLEditor::RemoveList(const nsAString
& aListType
)
2586 if (!mRules
) { return NS_ERROR_NOT_INITIALIZED
; }
2588 // Protect the edit rules object from dying
2589 nsCOMPtr
<nsIEditRules
> kungFuDeathGrip(mRules
);
2591 nsCOMPtr
<nsISelection
> selection
;
2592 PRBool cancel
, handled
;
2594 nsAutoEditBatch
beginBatching(this);
2595 nsAutoRules
beginRulesSniffing(this, kOpRemoveList
, nsIEditor::eNext
);
2598 res
= GetSelection(getter_AddRefs(selection
));
2599 NS_ENSURE_SUCCESS(res
, res
);
2600 NS_ENSURE_TRUE(selection
, NS_ERROR_NULL_POINTER
);
2602 nsTextRulesInfo
ruleInfo(nsTextEditRules::kRemoveList
);
2603 if (aListType
.LowerCaseEqualsLiteral("ol"))
2604 ruleInfo
.bOrdered
= PR_TRUE
;
2605 else ruleInfo
.bOrdered
= PR_FALSE
;
2606 res
= mRules
->WillDoAction(selection
, &ruleInfo
, &cancel
, &handled
);
2607 if (cancel
|| (NS_FAILED(res
))) return res
;
2609 // no default behavior for this yet. what would it mean?
2611 res
= mRules
->DidDoAction(selection
, &ruleInfo
, res
);
2616 nsHTMLEditor::MakeDefinitionItem(const nsAString
& aItemType
)
2619 if (!mRules
) { return NS_ERROR_NOT_INITIALIZED
; }
2621 // Protect the edit rules object from dying
2622 nsCOMPtr
<nsIEditRules
> kungFuDeathGrip(mRules
);
2624 nsCOMPtr
<nsISelection
> selection
;
2625 PRBool cancel
, handled
;
2627 nsAutoEditBatch
beginBatching(this);
2628 nsAutoRules
beginRulesSniffing(this, kOpMakeDefListItem
, nsIEditor::eNext
);
2631 res
= GetSelection(getter_AddRefs(selection
));
2632 NS_ENSURE_SUCCESS(res
, res
);
2633 NS_ENSURE_TRUE(selection
, NS_ERROR_NULL_POINTER
);
2634 nsTextRulesInfo
ruleInfo(nsTextEditRules::kMakeDefListItem
);
2635 ruleInfo
.blockType
= &aItemType
;
2636 res
= mRules
->WillDoAction(selection
, &ruleInfo
, &cancel
, &handled
);
2637 if (cancel
|| (NS_FAILED(res
))) return res
;
2641 // todo: no default for now. we count on rules to handle it.
2644 res
= mRules
->DidDoAction(selection
, &ruleInfo
, res
);
2649 nsHTMLEditor::InsertBasicBlock(const nsAString
& aBlockType
)
2652 if (!mRules
) { return NS_ERROR_NOT_INITIALIZED
; }
2654 // Protect the edit rules object from dying
2655 nsCOMPtr
<nsIEditRules
> kungFuDeathGrip(mRules
);
2657 nsCOMPtr
<nsISelection
> selection
;
2658 PRBool cancel
, handled
;
2660 nsAutoEditBatch
beginBatching(this);
2661 nsAutoRules
beginRulesSniffing(this, kOpMakeBasicBlock
, nsIEditor::eNext
);
2664 res
= GetSelection(getter_AddRefs(selection
));
2665 NS_ENSURE_SUCCESS(res
, res
);
2666 NS_ENSURE_TRUE(selection
, NS_ERROR_NULL_POINTER
);
2667 nsTextRulesInfo
ruleInfo(nsTextEditRules::kMakeBasicBlock
);
2668 ruleInfo
.blockType
= &aBlockType
;
2669 res
= mRules
->WillDoAction(selection
, &ruleInfo
, &cancel
, &handled
);
2670 if (cancel
|| (NS_FAILED(res
))) return res
;
2674 // Find out if the selection is collapsed:
2676 res
= selection
->GetIsCollapsed(&isCollapsed
);
2677 NS_ENSURE_SUCCESS(res
, res
);
2679 nsCOMPtr
<nsIDOMNode
> node
;
2682 res
= GetStartNodeAndOffset(selection
, getter_AddRefs(node
), &offset
);
2683 if (!node
) res
= NS_ERROR_FAILURE
;
2684 NS_ENSURE_SUCCESS(res
, res
);
2688 // have to find a place to put the block
2689 nsCOMPtr
<nsIDOMNode
> parent
= node
;
2690 nsCOMPtr
<nsIDOMNode
> topChild
= node
;
2691 nsCOMPtr
<nsIDOMNode
> tmp
;
2693 while ( !CanContainTag(parent
, aBlockType
))
2695 parent
->GetParentNode(getter_AddRefs(tmp
));
2696 NS_ENSURE_TRUE(tmp
, NS_ERROR_FAILURE
);
2703 // we need to split up to the child of parent
2704 res
= SplitNodeDeep(topChild
, node
, offset
, &offset
);
2705 NS_ENSURE_SUCCESS(res
, res
);
2709 nsCOMPtr
<nsIDOMNode
> newBlock
;
2710 res
= CreateNode(aBlockType
, parent
, offset
, getter_AddRefs(newBlock
));
2711 NS_ENSURE_SUCCESS(res
, res
);
2713 // reposition selection to inside the block
2714 res
= selection
->Collapse(newBlock
,0);
2715 NS_ENSURE_SUCCESS(res
, res
);
2719 res
= mRules
->DidDoAction(selection
, &ruleInfo
, res
);
2724 nsHTMLEditor::Indent(const nsAString
& aIndent
)
2727 if (!mRules
) { return NS_ERROR_NOT_INITIALIZED
; }
2729 // Protect the edit rules object from dying
2730 nsCOMPtr
<nsIEditRules
> kungFuDeathGrip(mRules
);
2732 PRBool cancel
, handled
;
2733 PRInt32 theAction
= nsTextEditRules::kIndent
;
2734 PRInt32 opID
= kOpIndent
;
2735 if (aIndent
.LowerCaseEqualsLiteral("outdent"))
2737 theAction
= nsTextEditRules::kOutdent
;
2740 nsAutoEditBatch
beginBatching(this);
2741 nsAutoRules
beginRulesSniffing(this, opID
, nsIEditor::eNext
);
2744 nsCOMPtr
<nsISelection
> selection
;
2745 res
= GetSelection(getter_AddRefs(selection
));
2746 NS_ENSURE_SUCCESS(res
, res
);
2747 NS_ENSURE_TRUE(selection
, NS_ERROR_NULL_POINTER
);
2749 nsTextRulesInfo
ruleInfo(theAction
);
2750 res
= mRules
->WillDoAction(selection
, &ruleInfo
, &cancel
, &handled
);
2751 if (cancel
|| (NS_FAILED(res
))) return res
;
2755 // Do default - insert a blockquote node if selection collapsed
2756 nsCOMPtr
<nsIDOMNode
> node
;
2759 res
= selection
->GetIsCollapsed(&isCollapsed
);
2760 NS_ENSURE_SUCCESS(res
, res
);
2762 res
= GetStartNodeAndOffset(selection
, getter_AddRefs(node
), &offset
);
2763 if (!node
) res
= NS_ERROR_FAILURE
;
2764 NS_ENSURE_SUCCESS(res
, res
);
2766 if (aIndent
.EqualsLiteral("indent"))
2770 // have to find a place to put the blockquote
2771 nsCOMPtr
<nsIDOMNode
> parent
= node
;
2772 nsCOMPtr
<nsIDOMNode
> topChild
= node
;
2773 nsCOMPtr
<nsIDOMNode
> tmp
;
2774 NS_NAMED_LITERAL_STRING(bq
, "blockquote");
2775 while ( !CanContainTag(parent
, bq
))
2777 parent
->GetParentNode(getter_AddRefs(tmp
));
2778 NS_ENSURE_TRUE(tmp
, NS_ERROR_FAILURE
);
2785 // we need to split up to the child of parent
2786 res
= SplitNodeDeep(topChild
, node
, offset
, &offset
);
2787 NS_ENSURE_SUCCESS(res
, res
);
2790 // make a blockquote
2791 nsCOMPtr
<nsIDOMNode
> newBQ
;
2792 res
= CreateNode(bq
, parent
, offset
, getter_AddRefs(newBQ
));
2793 NS_ENSURE_SUCCESS(res
, res
);
2794 // put a space in it so layout will draw the list item
2795 res
= selection
->Collapse(newBQ
,0);
2796 NS_ENSURE_SUCCESS(res
, res
);
2797 res
= InsertText(NS_LITERAL_STRING(" "));
2798 NS_ENSURE_SUCCESS(res
, res
);
2799 // reposition selection to before the space character
2800 res
= GetStartNodeAndOffset(selection
, getter_AddRefs(node
), &offset
);
2801 NS_ENSURE_SUCCESS(res
, res
);
2802 res
= selection
->Collapse(node
,0);
2803 NS_ENSURE_SUCCESS(res
, res
);
2807 res
= mRules
->DidDoAction(selection
, &ruleInfo
, res
);
2811 //TODO: IMPLEMENT ALIGNMENT!
2814 nsHTMLEditor::Align(const nsAString
& aAlignType
)
2816 // Protect the edit rules object from dying
2817 nsCOMPtr
<nsIEditRules
> kungFuDeathGrip(mRules
);
2819 nsAutoEditBatch
beginBatching(this);
2820 nsAutoRules
beginRulesSniffing(this, kOpAlign
, nsIEditor::eNext
);
2822 nsCOMPtr
<nsIDOMNode
> node
;
2823 PRBool cancel
, handled
;
2825 // Find out if the selection is collapsed:
2826 nsCOMPtr
<nsISelection
> selection
;
2827 nsresult res
= GetSelection(getter_AddRefs(selection
));
2828 NS_ENSURE_SUCCESS(res
, res
);
2829 NS_ENSURE_TRUE(selection
, NS_ERROR_NULL_POINTER
);
2830 nsTextRulesInfo
ruleInfo(nsTextEditRules::kAlign
);
2831 ruleInfo
.alignType
= &aAlignType
;
2832 res
= mRules
->WillDoAction(selection
, &ruleInfo
, &cancel
, &handled
);
2833 if (cancel
|| NS_FAILED(res
))
2836 res
= mRules
->DidDoAction(selection
, &ruleInfo
, res
);
2841 nsHTMLEditor::GetElementOrParentByTagName(const nsAString
& aTagName
, nsIDOMNode
*aNode
, nsIDOMElement
** aReturn
)
2843 if (aTagName
.IsEmpty() || !aReturn
)
2844 return NS_ERROR_NULL_POINTER
;
2846 nsresult res
= NS_OK
;
2847 nsCOMPtr
<nsIDOMNode
> currentNode
;
2850 currentNode
= aNode
;
2853 // If no node supplied, get it from anchor node of current selection
2854 nsCOMPtr
<nsISelection
>selection
;
2855 res
= GetSelection(getter_AddRefs(selection
));
2856 NS_ENSURE_SUCCESS(res
, res
);
2857 NS_ENSURE_TRUE(selection
, NS_ERROR_NULL_POINTER
);
2859 nsCOMPtr
<nsIDOMNode
> anchorNode
;
2860 res
= selection
->GetAnchorNode(getter_AddRefs(anchorNode
));
2861 if(NS_FAILED(res
)) return res
;
2862 NS_ENSURE_TRUE(anchorNode
, NS_ERROR_FAILURE
);
2864 // Try to get the actual selected node
2865 PRBool hasChildren
= PR_FALSE
;
2866 anchorNode
->HasChildNodes(&hasChildren
);
2870 res
= selection
->GetAnchorOffset(&offset
);
2871 if(NS_FAILED(res
)) return res
;
2872 currentNode
= nsEditor::GetChildAt(anchorNode
, offset
);
2874 // anchor node is probably a text node - just use that
2876 currentNode
= anchorNode
;
2879 nsAutoString
TagName(aTagName
);
2880 ToLowerCase(TagName
);
2881 PRBool getLink
= IsLinkTag(TagName
);
2882 PRBool getNamedAnchor
= IsNamedAnchorTag(TagName
);
2883 if ( getLink
|| getNamedAnchor
)
2885 TagName
.AssignLiteral("a");
2887 PRBool findTableCell
= TagName
.EqualsLiteral("td");
2888 PRBool findList
= TagName
.EqualsLiteral("list");
2890 // default is null - no element found
2893 nsCOMPtr
<nsIDOMNode
> parent
;
2894 PRBool bNodeFound
= PR_FALSE
;
2898 nsAutoString currentTagName
;
2899 // Test if we have a link (an anchor with href set)
2900 if ( (getLink
&& nsHTMLEditUtils::IsLink(currentNode
)) ||
2901 (getNamedAnchor
&& nsHTMLEditUtils::IsNamedAnchor(currentNode
)) )
2903 bNodeFound
= PR_TRUE
;
2908 // Match "ol", "ul", or "dl" for lists
2909 if (nsHTMLEditUtils::IsList(currentNode
))
2912 } else if (findTableCell
)
2914 // Table cells are another special case:
2915 // Match either "td" or "th" for them
2916 if (nsHTMLEditUtils::IsTableCell(currentNode
))
2920 currentNode
->GetNodeName(currentTagName
);
2921 if (currentTagName
.Equals(TagName
, nsCaseInsensitiveStringComparator()))
2924 bNodeFound
= PR_TRUE
;
2929 // Search up the parent chain
2930 // We should never fail because of root test below, but lets be safe
2931 // XXX: ERROR_HANDLING error return code lost
2932 if (NS_FAILED(currentNode
->GetParentNode(getter_AddRefs(parent
))) || !parent
)
2935 // Stop searching if parent is a body tag
2936 nsAutoString parentTagName
;
2937 parent
->GetNodeName(parentTagName
);
2938 // Note: Originally used IsRoot to stop at table cells,
2939 // but that's too messy when you are trying to find the parent table
2941 //if (NS_FAILED(IsRootTag(parentTagName, isRoot)) || isRoot)
2942 if(parentTagName
.LowerCaseEqualsLiteral("body"))
2945 currentNode
= parent
;
2949 nsCOMPtr
<nsIDOMElement
> currentElement
= do_QueryInterface(currentNode
);
2952 *aReturn
= currentElement
;
2953 // Getters must addref
2954 NS_ADDREF(*aReturn
);
2957 else res
= NS_EDITOR_ELEMENT_NOT_FOUND
;
2963 nsHTMLEditor::GetSelectedElement(const nsAString
& aTagName
, nsIDOMElement
** aReturn
)
2965 NS_ENSURE_TRUE(aReturn
, NS_ERROR_NULL_POINTER
);
2967 // default is null - no element found
2970 // First look for a single element in selection
2971 nsCOMPtr
<nsISelection
>selection
;
2972 nsresult res
= GetSelection(getter_AddRefs(selection
));
2973 NS_ENSURE_SUCCESS(res
, res
);
2974 NS_ENSURE_TRUE(selection
, NS_ERROR_NULL_POINTER
);
2975 nsCOMPtr
<nsISelectionPrivate
> selPriv(do_QueryInterface(selection
));
2977 PRBool bNodeFound
= PR_FALSE
;
2978 res
=NS_ERROR_NOT_INITIALIZED
;
2980 selection
->GetIsCollapsed(&isCollapsed
);
2982 nsAutoString domTagName
;
2983 nsAutoString
TagName(aTagName
);
2984 ToLowerCase(TagName
);
2985 // Empty string indicates we should match any element tag
2986 PRBool anyTag
= (TagName
.IsEmpty());
2987 PRBool isLinkTag
= IsLinkTag(TagName
);
2988 PRBool isNamedAnchorTag
= IsNamedAnchorTag(TagName
);
2990 nsCOMPtr
<nsIDOMElement
> selectedElement
;
2991 nsCOMPtr
<nsIDOMRange
> range
;
2992 res
= selection
->GetRangeAt(0, getter_AddRefs(range
));
2993 NS_ENSURE_SUCCESS(res
, res
);
2995 nsCOMPtr
<nsIDOMNode
> startParent
;
2996 PRInt32 startOffset
, endOffset
;
2997 res
= range
->GetStartContainer(getter_AddRefs(startParent
));
2998 NS_ENSURE_SUCCESS(res
, res
);
2999 res
= range
->GetStartOffset(&startOffset
);
3000 NS_ENSURE_SUCCESS(res
, res
);
3002 nsCOMPtr
<nsIDOMNode
> endParent
;
3003 res
= range
->GetEndContainer(getter_AddRefs(endParent
));
3004 NS_ENSURE_SUCCESS(res
, res
);
3005 res
= range
->GetEndOffset(&endOffset
);
3006 NS_ENSURE_SUCCESS(res
, res
);
3008 // Optimization for a single selected element
3009 if (startParent
&& startParent
== endParent
&& (endOffset
-startOffset
) == 1)
3011 nsCOMPtr
<nsIDOMNode
> selectedNode
= GetChildAt(startParent
, startOffset
);
3012 NS_ENSURE_SUCCESS(res
, NS_OK
);
3015 selectedNode
->GetNodeName(domTagName
);
3016 ToLowerCase(domTagName
);
3018 // Test for appropriate node type requested
3019 if (anyTag
|| (TagName
== domTagName
) ||
3020 (isLinkTag
&& nsHTMLEditUtils::IsLink(selectedNode
)) ||
3021 (isNamedAnchorTag
&& nsHTMLEditUtils::IsNamedAnchor(selectedNode
)))
3023 bNodeFound
= PR_TRUE
;
3024 selectedElement
= do_QueryInterface(selectedNode
);
3033 // Link tag is a special case - we return the anchor node
3034 // found for any selection that is totally within a link,
3035 // included a collapsed selection (just a caret in a link)
3036 nsCOMPtr
<nsIDOMNode
> anchorNode
;
3037 res
= selection
->GetAnchorNode(getter_AddRefs(anchorNode
));
3038 NS_ENSURE_SUCCESS(res
, res
);
3039 PRInt32 anchorOffset
= -1;
3041 selection
->GetAnchorOffset(&anchorOffset
);
3043 nsCOMPtr
<nsIDOMNode
> focusNode
;
3044 res
= selection
->GetFocusNode(getter_AddRefs(focusNode
));
3045 NS_ENSURE_SUCCESS(res
, res
);
3046 PRInt32 focusOffset
= -1;
3048 selection
->GetFocusOffset(&focusOffset
);
3050 // Link node must be the same for both ends of selection
3051 if (NS_SUCCEEDED(res
) && anchorNode
)
3053 #ifdef DEBUG_cmanske
3056 anchorNode
->GetNodeName(name
);
3057 printf("GetSelectedElement: Anchor node of selection: ");
3058 wprintf(name
.get());
3059 printf(" Offset: %d\n", anchorOffset
);
3060 focusNode
->GetNodeName(name
);
3061 printf("Focus node of selection: ");
3062 wprintf(name
.get());
3063 printf(" Offset: %d\n", focusOffset
);
3066 nsCOMPtr
<nsIDOMElement
> parentLinkOfAnchor
;
3067 res
= GetElementOrParentByTagName(NS_LITERAL_STRING("href"), anchorNode
, getter_AddRefs(parentLinkOfAnchor
));
3068 // XXX: ERROR_HANDLING can parentLinkOfAnchor be null?
3069 if (NS_SUCCEEDED(res
) && parentLinkOfAnchor
)
3073 // We have just a caret in the link
3074 bNodeFound
= PR_TRUE
;
3075 } else if(focusNode
)
3076 { // Link node must be the same for both ends of selection
3077 nsCOMPtr
<nsIDOMElement
> parentLinkOfFocus
;
3078 res
= GetElementOrParentByTagName(NS_LITERAL_STRING("href"), focusNode
, getter_AddRefs(parentLinkOfFocus
));
3079 if (NS_SUCCEEDED(res
) && parentLinkOfFocus
== parentLinkOfAnchor
)
3080 bNodeFound
= PR_TRUE
;
3083 // We found a link node parent
3085 // GetElementOrParentByTagName addref'd this, so we don't need to do it here
3086 *aReturn
= parentLinkOfAnchor
;
3087 NS_IF_ADDREF(*aReturn
);
3091 else if (anchorOffset
>= 0) // Check if link node is the only thing selected
3093 nsCOMPtr
<nsIDOMNode
> anchorChild
;
3094 anchorChild
= GetChildAt(anchorNode
,anchorOffset
);
3095 if (anchorChild
&& nsHTMLEditUtils::IsLink(anchorChild
) &&
3096 (anchorNode
== focusNode
) && focusOffset
== (anchorOffset
+1))
3098 selectedElement
= do_QueryInterface(anchorChild
);
3099 bNodeFound
= PR_TRUE
;
3105 if (!isCollapsed
) // Don't bother to examine selection if it is collapsed
3107 nsCOMPtr
<nsIEnumerator
> enumerator
;
3108 res
= selPriv
->GetEnumerator(getter_AddRefs(enumerator
));
3109 if (NS_SUCCEEDED(res
))
3112 return NS_ERROR_NULL_POINTER
;
3114 enumerator
->First();
3115 nsCOMPtr
<nsISupports
> currentItem
;
3116 res
= enumerator
->CurrentItem(getter_AddRefs(currentItem
));
3117 if ((NS_SUCCEEDED(res
)) && currentItem
)
3119 nsCOMPtr
<nsIDOMRange
> currange( do_QueryInterface(currentItem
) );
3120 nsCOMPtr
<nsIContentIterator
> iter
=
3121 do_CreateInstance("@mozilla.org/content/post-content-iterator;1", &res
);
3122 NS_ENSURE_SUCCESS(res
, res
);
3124 iter
->Init(currange
);
3125 // loop through the content iterator for each content node
3126 while (!iter
->IsDone())
3128 // Query interface to cast nsIContent to nsIDOMNode
3129 // then get tagType to compare to aTagName
3130 // Clone node of each desired type and append it to the aDomFrag
3131 selectedElement
= do_QueryInterface(iter
->GetCurrentNode());
3132 if (selectedElement
)
3134 // If we already found a node, then we have another element,
3135 // thus there's not just one element selected
3138 bNodeFound
= PR_FALSE
;
3142 selectedElement
->GetNodeName(domTagName
);
3143 ToLowerCase(domTagName
);
3147 // Get name of first selected element
3148 selectedElement
->GetTagName(TagName
);
3149 ToLowerCase(TagName
);
3153 // The "A" tag is a pain,
3154 // used for both link(href is set) and "Named Anchor"
3155 nsCOMPtr
<nsIDOMNode
> selectedNode
= do_QueryInterface(selectedElement
);
3156 if ( (isLinkTag
&& nsHTMLEditUtils::IsLink(selectedNode
)) ||
3157 (isNamedAnchorTag
&& nsHTMLEditUtils::IsNamedAnchor(selectedNode
)) )
3159 bNodeFound
= PR_TRUE
;
3160 } else if (TagName
== domTagName
) { // All other tag names are handled here
3161 bNodeFound
= PR_TRUE
;
3165 // Check if node we have is really part of the selection???
3172 // Should never get here?
3173 isCollapsed
= PR_TRUE
;
3174 printf("isCollapsed was FALSE, but no elements found in selection\n");
3177 printf("Could not create enumerator for GetSelectionProperties\n");
3184 *aReturn
= selectedElement
;
3185 if (selectedElement
)
3187 // Getters must addref
3188 NS_ADDREF(*aReturn
);
3191 else res
= NS_EDITOR_ELEMENT_NOT_FOUND
;
3197 nsHTMLEditor::CreateElementWithDefaults(const nsAString
& aTagName
, nsIDOMElement
** aReturn
)
3199 nsresult res
=NS_ERROR_NOT_INITIALIZED
;
3203 // NS_ENSURE_TRUE(aTagName && aReturn, NS_ERROR_NULL_POINTER);
3204 NS_ENSURE_TRUE(!aTagName
.IsEmpty() && aReturn
, NS_ERROR_NULL_POINTER
);
3206 nsAutoString
TagName(aTagName
);
3207 ToLowerCase(TagName
);
3208 nsAutoString realTagName
;
3210 if (IsLinkTag(TagName
) || IsNamedAnchorTag(TagName
))
3212 realTagName
.AssignLiteral("a");
3214 realTagName
= TagName
;
3216 //We don't use editor's CreateElement because we don't want to
3217 // go through the transaction system
3219 nsCOMPtr
<nsIDOMElement
>newElement
;
3220 nsCOMPtr
<nsIContent
> newContent
;
3221 nsCOMPtr
<nsIDOMDocument
> doc
= do_QueryReferent(mDocWeak
);
3222 NS_ENSURE_TRUE(doc
, NS_ERROR_NOT_INITIALIZED
);
3224 //new call to use instead to get proper HTML element, bug# 39919
3225 res
= CreateHTMLContent(realTagName
, getter_AddRefs(newContent
));
3226 newElement
= do_QueryInterface(newContent
);
3227 if (NS_FAILED(res
) || !newElement
)
3228 return NS_ERROR_FAILURE
;
3230 // Mark the new element dirty, so it will be formatted
3231 newElement
->SetAttribute(NS_LITERAL_STRING("_moz_dirty"), EmptyString());
3233 // Set default values for new elements
3234 if (TagName
.EqualsLiteral("hr"))
3236 // Note that we read the user's attributes for these from prefs (in InsertHLine JS)
3237 res
= SetAttributeOrEquivalent(newElement
, NS_LITERAL_STRING("width"),
3238 NS_LITERAL_STRING("100%"), PR_TRUE
);
3239 NS_ENSURE_SUCCESS(res
, res
);
3240 res
= SetAttributeOrEquivalent(newElement
, NS_LITERAL_STRING("size"),
3241 NS_LITERAL_STRING("2"), PR_TRUE
);
3242 } else if (TagName
.EqualsLiteral("table"))
3244 res
= newElement
->SetAttribute(NS_LITERAL_STRING("cellpadding"),NS_LITERAL_STRING("2"));
3245 NS_ENSURE_SUCCESS(res
, res
);
3246 res
= newElement
->SetAttribute(NS_LITERAL_STRING("cellspacing"),NS_LITERAL_STRING("2"));
3247 NS_ENSURE_SUCCESS(res
, res
);
3248 res
= newElement
->SetAttribute(NS_LITERAL_STRING("border"),NS_LITERAL_STRING("1"));
3249 } else if (TagName
.EqualsLiteral("td"))
3251 res
= SetAttributeOrEquivalent(newElement
, NS_LITERAL_STRING("valign"),
3252 NS_LITERAL_STRING("top"), PR_TRUE
);
3254 // ADD OTHER TAGS HERE
3256 if (NS_SUCCEEDED(res
))
3258 *aReturn
= newElement
;
3259 // Getters must addref
3260 NS_ADDREF(*aReturn
);
3267 nsHTMLEditor::InsertLinkAroundSelection(nsIDOMElement
* aAnchorElement
)
3269 nsresult res
=NS_ERROR_NULL_POINTER
;
3270 nsCOMPtr
<nsISelection
> selection
;
3272 NS_ENSURE_TRUE(aAnchorElement
, NS_ERROR_NULL_POINTER
);
3275 // We must have a real selection
3276 res
= GetSelection(getter_AddRefs(selection
));
3279 res
= NS_ERROR_NULL_POINTER
;
3281 NS_ENSURE_SUCCESS(res
, res
);
3282 NS_ENSURE_TRUE(selection
, NS_ERROR_NULL_POINTER
);
3285 res
= selection
->GetIsCollapsed(&isCollapsed
);
3287 isCollapsed
= PR_TRUE
;
3291 printf("InsertLinkAroundSelection called but there is no selection!!!\n");
3294 // Be sure we were given an anchor element
3295 nsCOMPtr
<nsIDOMHTMLAnchorElement
> anchor
= do_QueryInterface(aAnchorElement
);
3299 res
= anchor
->GetHref(href
);
3300 NS_ENSURE_SUCCESS(res
, res
);
3301 if (!href
.IsEmpty())
3303 nsAutoEditBatch
beginBatching(this);
3305 // Set all attributes found on the supplied anchor element
3306 nsCOMPtr
<nsIDOMNamedNodeMap
> attrMap
;
3307 aAnchorElement
->GetAttributes(getter_AddRefs(attrMap
));
3308 NS_ENSURE_TRUE(attrMap
, NS_ERROR_FAILURE
);
3311 attrMap
->GetLength(&count
);
3312 nsAutoString name
, value
;
3314 for (i
= 0; i
< count
; i
++)
3316 nsCOMPtr
<nsIDOMNode
> attrNode
;
3317 res
= attrMap
->Item(i
, getter_AddRefs(attrNode
));
3318 NS_ENSURE_SUCCESS(res
, res
);
3322 nsCOMPtr
<nsIDOMAttr
> attribute
= do_QueryInterface(attrNode
);
3325 // We must clear the string buffers
3326 // because GetName, GetValue appends to previous string!
3330 res
= attribute
->GetName(name
);
3331 NS_ENSURE_SUCCESS(res
, res
);
3333 res
= attribute
->GetValue(value
);
3334 NS_ENSURE_SUCCESS(res
, res
);
3336 res
= SetInlineProperty(nsEditProperty::a
, name
, value
);
3337 NS_ENSURE_SUCCESS(res
, res
);
3348 nsHTMLEditor::SetHTMLBackgroundColor(const nsAString
& aColor
)
3350 NS_PRECONDITION(mDocWeak
, "Missing Editor DOM Document");
3352 // Find a selected or enclosing table element to set background on
3353 nsCOMPtr
<nsIDOMElement
> element
;
3354 PRInt32 selectedCount
;
3355 nsAutoString tagName
;
3356 nsresult res
= GetSelectedOrParentTableElement(tagName
, &selectedCount
,
3357 getter_AddRefs(element
));
3358 NS_ENSURE_SUCCESS(res
, res
);
3360 PRBool setColor
= !aColor
.IsEmpty();
3362 NS_NAMED_LITERAL_STRING(bgcolor
, "bgcolor");
3365 if (selectedCount
> 0)
3367 // Traverse all selected cells
3368 nsCOMPtr
<nsIDOMElement
> cell
;
3369 res
= GetFirstSelectedCell(nsnull
, getter_AddRefs(cell
));
3370 if (NS_SUCCEEDED(res
) && cell
)
3375 res
= SetAttribute(cell
, bgcolor
, aColor
);
3377 res
= RemoveAttribute(cell
, bgcolor
);
3378 if (NS_FAILED(res
)) break;
3380 GetNextSelectedCell(nsnull
, getter_AddRefs(cell
));
3385 // If we failed to find a cell, fall through to use originally-found element
3387 // No table element -- set the background color on the body tag
3388 element
= GetRoot();
3389 NS_ENSURE_TRUE(element
, NS_ERROR_NULL_POINTER
);
3391 // Use the editor method that goes through the transaction system
3393 res
= SetAttribute(element
, bgcolor
, aColor
);
3395 res
= RemoveAttribute(element
, bgcolor
);
3400 NS_IMETHODIMP
nsHTMLEditor::SetBodyAttribute(const nsAString
& aAttribute
, const nsAString
& aValue
)
3402 // TODO: Check selection for Cell, Row, Column or table and do color on appropriate level
3404 NS_ASSERTION(mDocWeak
, "Missing Editor DOM Document");
3406 // Set the background color attribute on the body tag
3407 nsIDOMElement
*bodyElement
= GetRoot();
3409 NS_ENSURE_TRUE(bodyElement
, NS_ERROR_NULL_POINTER
);
3411 // Use the editor method that goes through the transaction system
3412 return SetAttribute(bodyElement
, aAttribute
, aValue
);
3416 nsHTMLEditor::GetLinkedObjects(nsISupportsArray
** aNodeList
)
3418 NS_ENSURE_TRUE(aNodeList
, NS_ERROR_NULL_POINTER
);
3422 res
= NS_NewISupportsArray(aNodeList
);
3423 NS_ENSURE_SUCCESS(res
, res
);
3424 NS_ENSURE_TRUE(*aNodeList
, NS_ERROR_NULL_POINTER
);
3426 nsCOMPtr
<nsIContentIterator
> iter
=
3427 do_CreateInstance("@mozilla.org/content/post-content-iterator;1", &res
);
3428 NS_ENSURE_TRUE(iter
, NS_ERROR_NULL_POINTER
);
3429 if ((NS_SUCCEEDED(res
)))
3431 nsCOMPtr
<nsIDOMDocument
> domdoc
;
3432 nsEditor::GetDocument(getter_AddRefs(domdoc
));
3433 NS_ENSURE_TRUE(domdoc
, NS_ERROR_UNEXPECTED
);
3435 nsCOMPtr
<nsIDocument
> doc (do_QueryInterface(domdoc
));
3436 NS_ENSURE_TRUE(doc
, NS_ERROR_UNEXPECTED
);
3438 iter
->Init(doc
->GetRootElement());
3440 // loop through the content iterator for each content node
3441 while (!iter
->IsDone())
3443 nsCOMPtr
<nsIDOMNode
> node (do_QueryInterface(iter
->GetCurrentNode()));
3446 // Let nsURIRefObject make the hard decisions:
3447 nsCOMPtr
<nsIURIRefObject
> refObject
;
3448 res
= NS_NewHTMLURIRefObject(getter_AddRefs(refObject
), node
);
3449 if (NS_SUCCEEDED(res
))
3451 nsCOMPtr
<nsISupports
> isupp (do_QueryInterface(refObject
));
3453 (*aNodeList
)->AppendElement(isupp
);
3465 #pragma mark nsIEditorStyleSheets methods
3470 nsHTMLEditor::AddStyleSheet(const nsAString
&aURL
)
3472 // Enable existing sheet if already loaded.
3473 if (EnableExistingStyleSheet(aURL
))
3476 // Lose the previously-loaded sheet so there's nothing to replace
3477 // This pattern is different from Override methods because
3478 // we must wait to remove mLastStyleSheetURL and add new sheet
3479 // at the same time (in StyleSheetLoaded callback) so they are undoable together
3480 mLastStyleSheetURL
.Truncate();
3481 return ReplaceStyleSheet(aURL
);
3485 nsHTMLEditor::ReplaceStyleSheet(const nsAString
& aURL
)
3487 // Enable existing sheet if already loaded.
3488 if (EnableExistingStyleSheet(aURL
))
3490 // Disable last sheet if not the same as new one
3491 if (!mLastStyleSheetURL
.IsEmpty() && !mLastStyleSheetURL
.Equals(aURL
))
3492 return EnableStyleSheet(mLastStyleSheetURL
, PR_FALSE
);
3497 // Make sure the pres shell doesn't disappear during the load.
3498 NS_ENSURE_TRUE(mPresShellWeak
, NS_ERROR_NOT_INITIALIZED
);
3499 nsCOMPtr
<nsIPresShell
> ps
= do_QueryReferent(mPresShellWeak
);
3500 NS_ENSURE_TRUE(ps
, NS_ERROR_NOT_INITIALIZED
);
3502 nsCOMPtr
<nsIURI
> uaURI
;
3503 nsresult rv
= NS_NewURI(getter_AddRefs(uaURI
), aURL
);
3504 NS_ENSURE_SUCCESS(rv
, rv
);
3506 return ps
->GetDocument()->CSSLoader()->
3507 LoadSheet(uaURI
, nsnull
, EmptyCString(), this);
3511 nsHTMLEditor::RemoveStyleSheet(const nsAString
&aURL
)
3513 nsRefPtr
<nsCSSStyleSheet
> sheet
;
3514 nsresult rv
= GetStyleSheetForURL(aURL
, getter_AddRefs(sheet
));
3515 NS_ENSURE_SUCCESS(rv
, rv
);
3516 NS_ENSURE_TRUE(sheet
, NS_ERROR_UNEXPECTED
);
3518 nsRefPtr
<RemoveStyleSheetTxn
> txn
;
3519 rv
= CreateTxnForRemoveStyleSheet(sheet
, getter_AddRefs(txn
));
3520 if (!txn
) rv
= NS_ERROR_NULL_POINTER
;
3521 if (NS_SUCCEEDED(rv
))
3523 rv
= DoTransaction(txn
);
3524 if (NS_SUCCEEDED(rv
))
3525 mLastStyleSheetURL
.Truncate(); // forget it
3527 // Remove it from our internal list
3528 rv
= RemoveStyleSheetFromList(aURL
);
3536 nsHTMLEditor::AddOverrideStyleSheet(const nsAString
& aURL
)
3538 // Enable existing sheet if already loaded.
3539 if (EnableExistingStyleSheet(aURL
))
3542 // Make sure the pres shell doesn't disappear during the load.
3543 nsCOMPtr
<nsIPresShell
> ps
= do_QueryReferent(mPresShellWeak
);
3544 NS_ENSURE_TRUE(ps
, NS_ERROR_NOT_INITIALIZED
);
3546 nsCOMPtr
<nsIURI
> uaURI
;
3547 nsresult rv
= NS_NewURI(getter_AddRefs(uaURI
), aURL
);
3548 NS_ENSURE_SUCCESS(rv
, rv
);
3550 // We MUST ONLY load synchronous local files (no @import)
3551 // XXXbz Except this will actually try to load remote files
3552 // synchronously, of course..
3553 nsRefPtr
<nsCSSStyleSheet
> sheet
;
3554 // Editor override style sheets may want to style Gecko anonymous boxes
3555 rv
= ps
->GetDocument()->CSSLoader()->
3556 LoadSheetSync(uaURI
, PR_TRUE
, PR_TRUE
, getter_AddRefs(sheet
));
3558 // Synchronous loads should ALWAYS return completed
3559 NS_ENSURE_TRUE(sheet
, NS_ERROR_NULL_POINTER
);
3561 // Add the override style sheet
3562 // (This checks if already exists)
3563 ps
->AddOverrideStyleSheet(sheet
);
3565 ps
->ReconstructStyleData();
3567 // Save as the last-loaded sheet
3568 mLastOverrideStyleSheetURL
= aURL
;
3570 //Add URL and style sheet to our lists
3571 return AddNewStyleSheetToList(aURL
, sheet
);
3575 nsHTMLEditor::ReplaceOverrideStyleSheet(const nsAString
& aURL
)
3577 // Enable existing sheet if already loaded.
3578 if (EnableExistingStyleSheet(aURL
))
3580 // Disable last sheet if not the same as new one
3581 if (!mLastOverrideStyleSheetURL
.IsEmpty() && !mLastOverrideStyleSheetURL
.Equals(aURL
))
3582 return EnableStyleSheet(mLastOverrideStyleSheetURL
, PR_FALSE
);
3586 // Remove the previous sheet
3587 if (!mLastOverrideStyleSheetURL
.IsEmpty())
3588 RemoveOverrideStyleSheet(mLastOverrideStyleSheetURL
);
3590 return AddOverrideStyleSheet(aURL
);
3593 // Do NOT use transaction system for override style sheets
3595 nsHTMLEditor::RemoveOverrideStyleSheet(const nsAString
&aURL
)
3597 nsRefPtr
<nsCSSStyleSheet
> sheet
;
3598 GetStyleSheetForURL(aURL
, getter_AddRefs(sheet
));
3600 // Make sure we remove the stylesheet from our internal list in all
3602 nsresult rv
= RemoveStyleSheetFromList(aURL
);
3604 NS_ENSURE_TRUE(sheet
, NS_OK
); /// Don't fail if sheet not found
3606 NS_ENSURE_TRUE(mPresShellWeak
, NS_ERROR_NOT_INITIALIZED
);
3607 nsCOMPtr
<nsIPresShell
> ps
= do_QueryReferent(mPresShellWeak
);
3608 NS_ENSURE_TRUE(ps
, NS_ERROR_NOT_INITIALIZED
);
3610 ps
->RemoveOverrideStyleSheet(sheet
);
3611 ps
->ReconstructStyleData();
3613 // Remove it from our internal list
3618 nsHTMLEditor::EnableStyleSheet(const nsAString
&aURL
, PRBool aEnable
)
3620 nsRefPtr
<nsCSSStyleSheet
> sheet
;
3621 nsresult rv
= GetStyleSheetForURL(aURL
, getter_AddRefs(sheet
));
3622 NS_ENSURE_SUCCESS(rv
, rv
);
3623 NS_ENSURE_TRUE(sheet
, NS_OK
); // Don't fail if sheet not found
3625 // Ensure the style sheet is owned by our document.
3626 nsCOMPtr
<nsIDocument
> doc
= do_QueryReferent(mDocWeak
);
3627 sheet
->SetOwningDocument(doc
);
3629 return sheet
->SetDisabled(!aEnable
);
3633 nsHTMLEditor::EnableExistingStyleSheet(const nsAString
&aURL
)
3635 nsRefPtr
<nsCSSStyleSheet
> sheet
;
3636 nsresult rv
= GetStyleSheetForURL(aURL
, getter_AddRefs(sheet
));
3637 NS_ENSURE_SUCCESS(rv
, PR_FALSE
);
3639 // Enable sheet if already loaded.
3642 // Ensure the style sheet is owned by our document.
3643 nsCOMPtr
<nsIDocument
> doc
= do_QueryReferent(mDocWeak
);
3644 sheet
->SetOwningDocument(doc
);
3646 sheet
->SetDisabled(PR_FALSE
);
3653 nsHTMLEditor::AddNewStyleSheetToList(const nsAString
&aURL
,
3654 nsCSSStyleSheet
*aStyleSheet
)
3656 PRUint32 countSS
= mStyleSheets
.Length();
3657 PRUint32 countU
= mStyleSheetURLs
.Length();
3659 if (countSS
!= countU
)
3660 return NS_ERROR_UNEXPECTED
;
3662 if (!mStyleSheetURLs
.AppendElement(aURL
))
3663 return NS_ERROR_UNEXPECTED
;
3665 return mStyleSheets
.AppendElement(aStyleSheet
) ? NS_OK
: NS_ERROR_UNEXPECTED
;
3669 nsHTMLEditor::RemoveStyleSheetFromList(const nsAString
&aURL
)
3671 // is it already in the list?
3672 PRUint32 foundIndex
;
3673 foundIndex
= mStyleSheetURLs
.IndexOf(aURL
);
3674 if (foundIndex
== mStyleSheetURLs
.NoIndex
)
3675 return NS_ERROR_FAILURE
;
3677 // Attempt both removals; if one fails there's not much we can do.
3678 mStyleSheets
.RemoveElementAt(foundIndex
);
3679 mStyleSheetURLs
.RemoveElementAt(foundIndex
);
3685 nsHTMLEditor::GetStyleSheetForURL(const nsAString
&aURL
,
3686 nsCSSStyleSheet
**aStyleSheet
)
3688 NS_ENSURE_ARG_POINTER(aStyleSheet
);
3691 // is it already in the list?
3692 PRUint32 foundIndex
;
3693 foundIndex
= mStyleSheetURLs
.IndexOf(aURL
);
3694 if (foundIndex
== mStyleSheetURLs
.NoIndex
)
3695 return NS_OK
; //No sheet -- don't fail!
3697 *aStyleSheet
= mStyleSheets
[foundIndex
];
3698 NS_ENSURE_TRUE(*aStyleSheet
, NS_ERROR_FAILURE
);
3700 NS_ADDREF(*aStyleSheet
);
3706 nsHTMLEditor::GetURLForStyleSheet(nsCSSStyleSheet
*aStyleSheet
,
3709 // is it already in the list?
3710 PRInt32 foundIndex
= mStyleSheets
.IndexOf(aStyleSheet
);
3712 // Don't fail if we don't find it in our list
3713 // Note: mStyleSheets is nsCOMArray, so its IndexOf() method
3714 // returns -1 on failure.
3715 if (foundIndex
== -1)
3718 // Found it in the list!
3719 aURL
= mStyleSheetURLs
[foundIndex
];
3724 * nsIEditorMailSupport methods
3728 nsHTMLEditor::GetEmbeddedObjects(nsISupportsArray
** aNodeList
)
3730 NS_ENSURE_TRUE(aNodeList
, NS_ERROR_NULL_POINTER
);
3734 res
= NS_NewISupportsArray(aNodeList
);
3735 NS_ENSURE_SUCCESS(res
, res
);
3736 NS_ENSURE_TRUE(*aNodeList
, NS_ERROR_NULL_POINTER
);
3738 nsCOMPtr
<nsIContentIterator
> iter
=
3739 do_CreateInstance("@mozilla.org/content/post-content-iterator;1", &res
);
3740 NS_ENSURE_TRUE(iter
, NS_ERROR_NULL_POINTER
);
3741 if ((NS_SUCCEEDED(res
)))
3743 nsCOMPtr
<nsIDOMDocument
> domdoc
;
3744 nsEditor::GetDocument(getter_AddRefs(domdoc
));
3745 NS_ENSURE_TRUE(domdoc
, NS_ERROR_UNEXPECTED
);
3747 nsCOMPtr
<nsIDocument
> doc (do_QueryInterface(domdoc
));
3748 NS_ENSURE_TRUE(doc
, NS_ERROR_UNEXPECTED
);
3750 iter
->Init(doc
->GetRootElement());
3752 // loop through the content iterator for each content node
3753 while (!iter
->IsDone())
3755 nsCOMPtr
<nsIDOMNode
> node (do_QueryInterface(iter
->GetCurrentNode()));
3758 nsAutoString tagName
;
3759 node
->GetNodeName(tagName
);
3760 ToLowerCase(tagName
);
3762 // See if it's an image or an embed and also include all links.
3763 // Let mail decide which link to send or not
3764 if (tagName
.EqualsLiteral("img") || tagName
.EqualsLiteral("embed") ||
3765 tagName
.EqualsLiteral("a"))
3766 (*aNodeList
)->AppendElement(node
);
3767 else if (tagName
.EqualsLiteral("body"))
3769 nsCOMPtr
<nsIDOMElement
> element
= do_QueryInterface(node
);
3772 PRBool hasBackground
= PR_FALSE
;
3773 if (NS_SUCCEEDED(element
->HasAttribute(NS_LITERAL_STRING("background"), &hasBackground
)) && hasBackground
)
3774 (*aNodeList
)->AppendElement(node
);
3788 #pragma mark nsIEditor overrides
3792 NS_IMETHODIMP
nsHTMLEditor::DeleteNode(nsIDOMNode
* aNode
)
3794 // do nothing if the node is read-only
3795 if (!IsModifiableNode(aNode
) && !IsMozEditorBogusNode(aNode
)) {
3796 return NS_ERROR_FAILURE
;
3799 nsCOMPtr
<nsIDOMNode
> selectAllNode
= FindUserSelectAllNode(aNode
);
3803 return nsEditor::DeleteNode(selectAllNode
);
3805 return nsEditor::DeleteNode(aNode
);
3808 NS_IMETHODIMP
nsHTMLEditor::DeleteText(nsIDOMCharacterData
*aTextNode
,
3812 // do nothing if the node is read-only
3813 if (!IsModifiableNode(aTextNode
)) {
3814 return NS_ERROR_FAILURE
;
3817 nsCOMPtr
<nsIDOMNode
> selectAllNode
= FindUserSelectAllNode(aTextNode
);
3821 return nsEditor::DeleteNode(selectAllNode
);
3823 return nsEditor::DeleteText(aTextNode
, aOffset
, aLength
);
3826 NS_IMETHODIMP
nsHTMLEditor::InsertTextImpl(const nsAString
& aStringToInsert
,
3827 nsCOMPtr
<nsIDOMNode
> *aInOutNode
,
3828 PRInt32
*aInOutOffset
,
3829 nsIDOMDocument
*aDoc
)
3831 // do nothing if the node is read-only
3832 if (!IsModifiableNode(*aInOutNode
)) {
3833 return NS_ERROR_FAILURE
;
3836 return nsEditor::InsertTextImpl(aStringToInsert
, aInOutNode
, aInOutOffset
, aDoc
);
3841 #pragma mark nsStubMutationObserver overrides
3846 nsHTMLEditor::ContentAppended(nsIDocument
*aDocument
, nsIContent
* aContainer
,
3847 nsIContent
* aFirstNewContent
,
3848 PRInt32
/* unused */)
3850 ContentInserted(aDocument
, aContainer
, aFirstNewContent
, 0);
3854 nsHTMLEditor::ContentInserted(nsIDocument
*aDocument
, nsIContent
* aContainer
,
3855 nsIContent
* aChild
, PRInt32
/* unused */)
3861 nsCOMPtr
<nsIHTMLEditor
> kungFuDeathGrip(this);
3863 if (ShouldReplaceRootElement()) {
3864 ResetRootElementAndEventTarget();
3866 // We don't need to handle our own modifications
3867 else if (!mAction
&& (aContainer
? aContainer
->IsEditable() : aDocument
->IsEditable())) {
3868 nsCOMPtr
<nsIDOMNode
> node
= do_QueryInterface(aChild
);
3869 if (node
&& IsMozEditorBogusNode(node
)) {
3870 // Ignore insertion of the bogus node
3873 mRules
->DocumentModified();
3878 nsHTMLEditor::ContentRemoved(nsIDocument
*aDocument
, nsIContent
* aContainer
,
3879 nsIContent
* aChild
, PRInt32 aIndexInContainer
,
3880 nsIContent
* aPreviousSibling
)
3882 nsCOMPtr
<nsIHTMLEditor
> kungFuDeathGrip(this);
3884 if (SameCOMIdentity(aChild
, mRootElement
)) {
3885 ResetRootElementAndEventTarget();
3887 // We don't need to handle our own modifications
3888 else if (!mAction
&& (aContainer
? aContainer
->IsEditable() : aDocument
->IsEditable())) {
3889 nsCOMPtr
<nsIDOMNode
> node
= do_QueryInterface(aChild
);
3890 if (node
&& IsMozEditorBogusNode(node
)) {
3891 // Ignore removal of the bogus node
3894 mRules
->DocumentModified();
3900 #pragma mark support utils
3904 /* This routine examines aNode and it's ancestors looking for any node which has the
3905 -moz-user-select: all style lit. Return the highest such ancestor. */
3906 nsCOMPtr
<nsIDOMNode
> nsHTMLEditor::FindUserSelectAllNode(nsIDOMNode
*aNode
)
3908 nsCOMPtr
<nsIDOMNode
> resultNode
; // starts out empty
3909 nsCOMPtr
<nsIDOMNode
> node
= aNode
;
3910 nsIDOMElement
*root
= GetRoot();
3911 if (!nsEditorUtils::IsDescendantOf(aNode
, root
))
3914 // retrieve the computed style of -moz-user-select for aNode
3915 nsAutoString mozUserSelectValue
;
3918 mHTMLCSSUtils
->GetComputedProperty(node
, nsEditProperty::cssMozUserSelect
, mozUserSelectValue
);
3919 if (mozUserSelectValue
.EqualsLiteral("all"))
3925 nsCOMPtr
<nsIDOMNode
> tmp
;
3926 node
->GetParentNode(getter_AddRefs(tmp
));
3938 NS_IMETHODIMP_(PRBool
)
3939 nsHTMLEditor::IsModifiableNode(nsIDOMNode
*aNode
)
3941 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(aNode
);
3943 return !content
|| !content
->IntrinsicState().HasState(NS_EVENT_STATE_MOZ_READONLY
);
3946 static nsresult
SetSelectionAroundHeadChildren(nsCOMPtr
<nsISelection
> aSelection
, nsWeakPtr aDocWeak
)
3948 nsresult res
= NS_OK
;
3949 // Set selection around <head> node
3950 nsCOMPtr
<nsIDOMDocument
> doc
= do_QueryReferent(aDocWeak
);
3951 NS_ENSURE_TRUE(doc
, NS_ERROR_NOT_INITIALIZED
);
3953 nsCOMPtr
<nsIDOMNodeList
>nodeList
;
3954 res
= doc
->GetElementsByTagName(NS_LITERAL_STRING("head"), getter_AddRefs(nodeList
));
3955 NS_ENSURE_SUCCESS(res
, res
);
3956 NS_ENSURE_TRUE(nodeList
, NS_ERROR_NULL_POINTER
);
3959 nodeList
->GetLength(&count
);
3960 if (count
< 1) return NS_ERROR_FAILURE
;
3962 nsCOMPtr
<nsIDOMNode
> headNode
;
3963 res
= nodeList
->Item(0, getter_AddRefs(headNode
));
3964 NS_ENSURE_SUCCESS(res
, res
);
3965 NS_ENSURE_TRUE(headNode
, NS_ERROR_NULL_POINTER
);
3967 // Collapse selection to before first child of the head,
3968 res
= aSelection
->Collapse(headNode
, 0);
3969 NS_ENSURE_SUCCESS(res
, res
);
3971 // then extend it to just after
3972 nsCOMPtr
<nsIDOMNodeList
> childNodes
;
3973 res
= headNode
->GetChildNodes(getter_AddRefs(childNodes
));
3974 NS_ENSURE_SUCCESS(res
, res
);
3975 NS_ENSURE_TRUE(childNodes
, NS_ERROR_NULL_POINTER
);
3976 PRUint32 childCount
;
3977 childNodes
->GetLength(&childCount
);
3979 return aSelection
->Extend(headNode
, childCount
+1);
3983 nsHTMLEditor::GetHeadContentsAsHTML(nsAString
& aOutputString
)
3985 nsCOMPtr
<nsISelection
> selection
;
3986 nsresult res
= GetSelection(getter_AddRefs(selection
));
3987 NS_ENSURE_SUCCESS(res
, res
);
3988 NS_ENSURE_TRUE(selection
, NS_ERROR_NULL_POINTER
);
3990 // Save current selection
3991 nsAutoSelectionReset
selectionResetter(selection
, this);
3993 res
= SetSelectionAroundHeadChildren(selection
, mDocWeak
);
3994 NS_ENSURE_SUCCESS(res
, res
);
3996 res
= OutputToString(NS_LITERAL_STRING("text/html"),
3997 nsIDocumentEncoder::OutputSelectionOnly
,
3999 if (NS_SUCCEEDED(res
))
4001 // Selection always includes <body></body>,
4002 // so terminate there
4003 nsReadingIterator
<PRUnichar
> findIter
,endFindIter
;
4004 aOutputString
.BeginReading(findIter
);
4005 aOutputString
.EndReading(endFindIter
);
4006 //counting on our parser to always lower case!!!
4007 if (CaseInsensitiveFindInReadable(NS_LITERAL_STRING("<body"),
4008 findIter
, endFindIter
))
4010 nsReadingIterator
<PRUnichar
> beginIter
;
4011 aOutputString
.BeginReading(beginIter
);
4012 PRInt32 offset
= Distance(beginIter
, findIter
);//get the distance
4014 nsWritingIterator
<PRUnichar
> writeIter
;
4015 aOutputString
.BeginWriting(writeIter
);
4016 // Ensure the string ends in a newline
4017 PRUnichar
newline ('\n');
4018 findIter
.advance(-1);
4019 if (offset
==0 || (offset
>0 && (*findIter
) != newline
)) //check for 0
4021 writeIter
.advance(offset
);
4022 *writeIter
= newline
;
4023 aOutputString
.Truncate(offset
+1);
4031 nsHTMLEditor::DebugUnitTests(PRInt32
*outNumTests
, PRInt32
*outNumTestsFailed
)
4034 NS_ENSURE_TRUE(outNumTests
&& outNumTestsFailed
, NS_ERROR_NULL_POINTER
);
4036 TextEditorTest
*tester
= new TextEditorTest();
4037 NS_ENSURE_TRUE(tester
, NS_ERROR_OUT_OF_MEMORY
);
4039 tester
->Run(this, outNumTests
, outNumTestsFailed
);
4043 return NS_ERROR_NOT_IMPLEMENTED
;
4049 #pragma mark StyleSheet utils
4055 nsHTMLEditor::StyleSheetLoaded(nsCSSStyleSheet
* aSheet
, PRBool aWasAlternate
,
4058 nsresult rv
= NS_OK
;
4059 nsAutoEditBatch
batchIt(this);
4061 if (!mLastStyleSheetURL
.IsEmpty())
4062 RemoveStyleSheet(mLastStyleSheetURL
);
4064 nsRefPtr
<AddStyleSheetTxn
> txn
;
4065 rv
= CreateTxnForAddStyleSheet(aSheet
, getter_AddRefs(txn
));
4066 if (!txn
) rv
= NS_ERROR_NULL_POINTER
;
4067 if (NS_SUCCEEDED(rv
))
4069 rv
= DoTransaction(txn
);
4070 if (NS_SUCCEEDED(rv
))
4072 // Get the URI, then url spec from the sheet
4074 rv
= aSheet
->GetSheetURI()->GetSpec(spec
);
4076 if (NS_SUCCEEDED(rv
))
4078 // Save it so we can remove before applying the next one
4079 mLastStyleSheetURL
.AssignWithConversion(spec
.get());
4081 // Also save in our arrays of urls and sheets
4082 AddNewStyleSheetToList(mLastStyleSheetURL
, aSheet
);
4092 #pragma mark nsEditor overrides
4097 /** All editor operations which alter the doc should be prefaced
4098 * with a call to StartOperation, naming the action and direction */
4100 nsHTMLEditor::StartOperation(PRInt32 opID
, nsIEditor::EDirection aDirection
)
4102 // Protect the edit rules object from dying
4103 nsCOMPtr
<nsIEditRules
> kungFuDeathGrip(mRules
);
4105 nsEditor::StartOperation(opID
, aDirection
); // will set mAction, mDirection
4106 if (mRules
) return mRules
->BeforeEdit(mAction
, mDirection
);
4111 /** All editor operations which alter the doc should be followed
4112 * with a call to EndOperation */
4114 nsHTMLEditor::EndOperation()
4116 // Protect the edit rules object from dying
4117 nsCOMPtr
<nsIEditRules
> kungFuDeathGrip(mRules
);
4120 nsresult res
= NS_OK
;
4121 if (mRules
) res
= mRules
->AfterEdit(mAction
, mDirection
);
4122 nsEditor::EndOperation(); // will clear mAction, mDirection
4127 nsHTMLEditor::TagCanContainTag(const nsAString
& aParentTag
, const nsAString
& aChildTag
)
4129 nsIParserService
* parserService
= nsContentUtils::GetParserService();
4131 PRInt32 childTagEnum
;
4132 // XXX Should this handle #cdata-section too?
4133 if (aChildTag
.EqualsLiteral("#text")) {
4134 childTagEnum
= eHTMLTag_text
;
4137 childTagEnum
= parserService
->HTMLStringTagToId(aChildTag
);
4140 PRInt32 parentTagEnum
= parserService
->HTMLStringTagToId(aParentTag
);
4141 NS_ASSERTION(parentTagEnum
< NS_HTML_TAG_MAX
,
4142 "Fix the caller, this type of node can never contain children.");
4144 return nsHTMLEditUtils::CanContain(parentTagEnum
, childTagEnum
);
4148 nsHTMLEditor::IsContainer(nsIDOMNode
*aNode
)
4154 nsAutoString stringTag
;
4156 nsresult rv
= aNode
->GetNodeName(stringTag
);
4157 NS_ENSURE_SUCCESS(rv
, PR_FALSE
);
4160 // XXX Should this handle #cdata-section too?
4161 if (stringTag
.EqualsLiteral("#text")) {
4162 tagEnum
= eHTMLTag_text
;
4165 tagEnum
= nsContentUtils::GetParserService()->HTMLStringTagToId(stringTag
);
4168 return nsHTMLEditUtils::IsContainer(tagEnum
);
4173 nsHTMLEditor::SelectEntireDocument(nsISelection
*aSelection
)
4175 if (!aSelection
|| !mRules
) { return NS_ERROR_NULL_POINTER
; }
4177 // Protect the edit rules object from dying
4178 nsCOMPtr
<nsIEditRules
> kungFuDeathGrip(mRules
);
4180 // get editor root node
4181 nsIDOMElement
*rootElement
= GetRoot();
4185 nsresult res
= mRules
->DocumentIsEmpty(&bDocIsEmpty
);
4186 NS_ENSURE_SUCCESS(res
, res
);
4190 // if its empty dont select entire doc - that would select the bogus node
4191 return aSelection
->Collapse(rootElement
, 0);
4194 return nsEditor::SelectEntireDocument(aSelection
);
4198 nsHTMLEditor::SelectAll()
4200 ForceCompositionEnd();
4203 nsCOMPtr
<nsISelectionController
> selCon
= do_QueryReferent(mSelConWeak
, &rv
);
4204 NS_ENSURE_SUCCESS(rv
, rv
);
4206 nsCOMPtr
<nsISelection
> selection
;
4207 rv
= selCon
->GetSelection(nsISelectionController::SELECTION_NORMAL
,
4208 getter_AddRefs(selection
));
4209 NS_ENSURE_SUCCESS(rv
, rv
);
4211 nsCOMPtr
<nsIDOMNode
> anchorNode
;
4212 rv
= selection
->GetAnchorNode(getter_AddRefs(anchorNode
));
4213 NS_ENSURE_SUCCESS(rv
, rv
);
4215 nsCOMPtr
<nsIContent
> anchorContent
= do_QueryInterface(anchorNode
, &rv
);
4216 NS_ENSURE_SUCCESS(rv
, rv
);
4218 // If the anchor content has independent selection, we never need to explicitly
4219 // select its children.
4220 if (anchorContent
->HasIndependentSelection()) {
4221 nsCOMPtr
<nsISelectionPrivate
> selPriv
= do_QueryInterface(selection
);
4222 NS_ENSURE_TRUE(selPriv
, NS_ERROR_UNEXPECTED
);
4223 rv
= selPriv
->SetAncestorLimiter(nsnull
);
4224 NS_ENSURE_SUCCESS(rv
, rv
);
4225 return selection
->SelectAllChildren(mRootElement
);
4228 nsCOMPtr
<nsIPresShell
> ps
= do_QueryReferent(mPresShellWeak
);
4229 nsIContent
*rootContent
= anchorContent
->GetSelectionRootContent(ps
);
4230 NS_ENSURE_TRUE(rootContent
, NS_ERROR_UNEXPECTED
);
4232 nsCOMPtr
<nsIDOMNode
> rootElement
= do_QueryInterface(rootContent
, &rv
);
4233 NS_ENSURE_SUCCESS(rv
, rv
);
4235 return selection
->SelectAllChildren(rootElement
);
4241 #pragma mark Random methods
4245 // this will NOT find aAttribute unless aAttribute has a non-null value
4246 // so singleton attributes like <Table border> will not be matched!
4247 void nsHTMLEditor::IsTextPropertySetByContent(nsIDOMNode
*aNode
,
4249 const nsAString
*aAttribute
,
4250 const nsAString
*aValue
,
4252 nsIDOMNode
**aStyleNode
,
4253 nsAString
*outValue
)
4256 aIsSet
= PR_FALSE
; // must be initialized to false for code below to work
4257 nsAutoString propName
;
4258 aProperty
->ToString(propName
);
4259 nsCOMPtr
<nsIDOMNode
>node
= aNode
;
4263 nsCOMPtr
<nsIDOMElement
>element
;
4264 element
= do_QueryInterface(node
);
4267 nsAutoString tag
, value
;
4268 element
->GetTagName(tag
);
4269 if (propName
.Equals(tag
, nsCaseInsensitiveStringComparator()))
4271 PRBool found
= PR_FALSE
;
4272 if (aAttribute
&& 0!=aAttribute
->Length())
4274 element
->GetAttribute(*aAttribute
, value
);
4275 if (outValue
) *outValue
= value
;
4276 if (!value
.IsEmpty())
4283 nsString
tString(*aValue
);
4284 if (tString
.Equals(value
, nsCaseInsensitiveStringComparator())) {
4287 else { // we found the prop with the attribute, but the value doesn't match
4303 nsCOMPtr
<nsIDOMNode
>temp
;
4304 result
= node
->GetParentNode(getter_AddRefs(temp
));
4305 if (NS_SUCCEEDED(result
) && temp
) {
4318 //================================================================
4319 // HTML Editor methods
4321 // Note: Table Editing methods are implemented in nsTableEditor.cpp
4325 PRBool
nsHTMLEditor::IsElementInBody(nsIDOMElement
* aElement
)
4327 return nsTextEditUtils::InBody(aElement
, this);
4331 nsHTMLEditor::SetCaretInTableCell(nsIDOMElement
* aElement
)
4333 PRBool caretIsSet
= PR_FALSE
;
4335 if (aElement
&& IsElementInBody(aElement
))
4337 nsresult res
= NS_OK
;
4338 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(aElement
);
4341 nsIAtom
*atom
= content
->Tag();
4342 if (atom
== nsEditProperty::table
||
4343 atom
== nsEditProperty::tbody
||
4344 atom
== nsEditProperty::thead
||
4345 atom
== nsEditProperty::tfoot
||
4346 atom
== nsEditProperty::caption
||
4347 atom
== nsEditProperty::tr
||
4348 atom
== nsEditProperty::td
)
4350 nsCOMPtr
<nsIDOMNode
> node
= do_QueryInterface(aElement
);
4351 nsCOMPtr
<nsIDOMNode
> parent
;
4352 // This MUST succeed if IsElementInBody was TRUE
4353 node
->GetParentNode(getter_AddRefs(parent
));
4354 nsCOMPtr
<nsIDOMNode
>firstChild
;
4355 // Find deepest child
4357 while (NS_SUCCEEDED(node
->HasChildNodes(&hasChild
)) && hasChild
)
4359 if (NS_SUCCEEDED(node
->GetFirstChild(getter_AddRefs(firstChild
))))
4365 // Set selection at beginning of deepest node
4366 nsCOMPtr
<nsISelection
> selection
;
4367 res
= GetSelection(getter_AddRefs(selection
));
4368 if (NS_SUCCEEDED(res
) && selection
&& firstChild
)
4370 res
= selection
->Collapse(firstChild
, 0);
4371 if (NS_SUCCEEDED(res
))
4372 caretIsSet
= PR_TRUE
;
4383 nsHTMLEditor::IsRootTag(nsString
&aTag
, PRBool
&aIsTag
)
4385 static char bodyTag
[] = "body";
4386 static char tdTag
[] = "td";
4387 static char thTag
[] = "th";
4388 static char captionTag
[] = "caption";
4389 if (aTag
.EqualsIgnoreCase(bodyTag
) ||
4390 aTag
.EqualsIgnoreCase(tdTag
) ||
4391 aTag
.EqualsIgnoreCase(thTag
) ||
4392 aTag
.EqualsIgnoreCase(captionTag
) )
4402 ///////////////////////////////////////////////////////////////////////////
4403 // GetEnclosingTable: find ancestor who is a table, if any
4405 nsCOMPtr
<nsIDOMNode
>
4406 nsHTMLEditor::GetEnclosingTable(nsIDOMNode
*aNode
)
4408 NS_PRECONDITION(aNode
, "null node passed to nsHTMLEditor::GetEnclosingTable");
4409 nsCOMPtr
<nsIDOMNode
> tbl
, tmp
, node
= aNode
;
4413 tmp
= GetBlockNodeParent(node
);
4415 if (nsHTMLEditUtils::IsTable(tmp
)) tbl
= tmp
;
4425 #ifdef PRE_NODE_IN_BODY
4426 nsCOMPtr
<nsIDOMElement
> nsHTMLEditor::FindPreElement()
4428 nsCOMPtr
<nsIDOMDocument
> domdoc
;
4429 nsEditor::GetDocument(getter_AddRefs(domdoc
));
4430 NS_ENSURE_TRUE(domdoc
, 0);
4432 nsCOMPtr
<nsIDocument
> doc (do_QueryInterface(domdoc
));
4433 NS_ENSURE_TRUE(doc
, 0);
4435 nsCOMPtr
<nsIContent
> rootContent
= doc
->GetRootElement();
4436 NS_ENSURE_TRUE(rootContent
, 0);
4438 nsCOMPtr
<nsIDOMNode
> rootNode (do_QueryInterface(rootContent
));
4439 NS_ENSURE_TRUE(rootNode
, 0);
4441 nsString
prestr ("PRE"); // GetFirstNodeOfType requires capitals
4442 nsCOMPtr
<nsIDOMNode
> preNode
;
4443 if (NS_FAILED(nsEditor::GetFirstNodeOfType(rootNode
, prestr
,
4444 getter_AddRefs(preNode
))))
4447 return do_QueryInterface(preNode
);
4449 #endif /* PRE_NODE_IN_BODY */
4451 /* this method scans the selection for adjacent text nodes
4452 * and collapses them into a single text node.
4453 * "adjacent" means literally adjacent siblings of the same parent.
4454 * Uses nsEditor::JoinNodes so action is undoable.
4455 * Should be called within the context of a batch transaction.
4458 nsHTMLEditor::CollapseAdjacentTextNodes(nsIDOMRange
*aInRange
)
4460 NS_ENSURE_TRUE(aInRange
, NS_ERROR_NULL_POINTER
);
4461 nsAutoTxnsConserveSelection
dontSpazMySelection(this);
4462 nsTArray
<nsIDOMNode
*> textNodes
;
4463 // we can't actually do anything during iteration, so store the text nodes in an array
4464 // don't bother ref counting them because we know we can hold them for the
4465 // lifetime of this method
4468 // build a list of editable text nodes
4470 nsCOMPtr
<nsIContentIterator
> iter
=
4471 do_CreateInstance("@mozilla.org/content/subtree-content-iterator;1", &result
);
4472 NS_ENSURE_SUCCESS(result
, result
);
4474 iter
->Init(aInRange
);
4476 while (!iter
->IsDone())
4478 nsCOMPtr
<nsIDOMCharacterData
> text
= do_QueryInterface(iter
->GetCurrentNode());
4479 if (text
&& IsEditable(text
))
4481 textNodes
.AppendElement(text
);
4487 // now that I have a list of text nodes, collapse adjacent text nodes
4488 // NOTE: assumption that JoinNodes keeps the righthand node
4489 while (textNodes
.Length() > 1)
4491 // we assume a textNodes entry can't be nsnull
4492 nsIDOMNode
*leftTextNode
= textNodes
[0];
4493 nsIDOMNode
*rightTextNode
= textNodes
[1];
4494 NS_ASSERTION(leftTextNode
&& rightTextNode
,"left or rightTextNode null in CollapseAdjacentTextNodes");
4496 // get the prev sibling of the right node, and see if it's leftTextNode
4497 nsCOMPtr
<nsIDOMNode
> prevSibOfRightNode
;
4499 rightTextNode
->GetPreviousSibling(getter_AddRefs(prevSibOfRightNode
));
4500 NS_ENSURE_SUCCESS(result
, result
);
4501 if (prevSibOfRightNode
&& (prevSibOfRightNode
== leftTextNode
))
4503 nsCOMPtr
<nsIDOMNode
> parent
;
4504 result
= rightTextNode
->GetParentNode(getter_AddRefs(parent
));
4505 NS_ENSURE_SUCCESS(result
, result
);
4506 NS_ENSURE_TRUE(parent
, NS_ERROR_NULL_POINTER
);
4507 result
= JoinNodes(leftTextNode
, rightTextNode
, parent
);
4508 NS_ENSURE_SUCCESS(result
, result
);
4511 textNodes
.RemoveElementAt(0); // remove the leftmost text node from the list
4518 nsHTMLEditor::SetSelectionAtDocumentStart(nsISelection
*aSelection
)
4520 nsIDOMElement
*rootElement
= GetRoot();
4521 NS_ENSURE_TRUE(rootElement
, NS_ERROR_NULL_POINTER
);
4523 return aSelection
->Collapse(rootElement
,0);
4530 ///////////////////////////////////////////////////////////////////////////
4531 // RemoveBlockContainer: remove inNode, reparenting it's children into their
4532 // the parent of inNode. In addition, INSERT ANY BR's NEEDED
4533 // TO PRESERVE IDENTITY OF REMOVED BLOCK.
4536 nsHTMLEditor::RemoveBlockContainer(nsIDOMNode
*inNode
)
4538 NS_ENSURE_TRUE(inNode
, NS_ERROR_NULL_POINTER
);
4540 nsCOMPtr
<nsIDOMNode
> sibling
, child
, unused
;
4542 // Two possibilities: the container cold be empty of editable content.
4543 // If that is the case, we need to compare what is before and after inNode
4544 // to determine if we need a br.
4545 // Or it could not be empty, in which case we have to compare previous
4546 // sibling and first child to determine if we need a leading br,
4547 // and compare following sibling and last child to determine if we need a
4550 res
= GetFirstEditableChild(inNode
, address_of(child
));
4551 NS_ENSURE_SUCCESS(res
, res
);
4553 if (child
) // the case of inNode not being empty
4555 // we need a br at start unless:
4556 // 1) previous sibling of inNode is a block, OR
4557 // 2) previous sibling of inNode is a br, OR
4558 // 3) first child of inNode is a block OR
4559 // 4) either is null
4561 res
= GetPriorHTMLSibling(inNode
, address_of(sibling
));
4562 NS_ENSURE_SUCCESS(res
, res
);
4563 if (sibling
&& !IsBlockNode(sibling
) && !nsTextEditUtils::IsBreak(sibling
))
4565 res
= GetFirstEditableChild(inNode
, address_of(child
));
4566 NS_ENSURE_SUCCESS(res
, res
);
4567 if (child
&& !IsBlockNode(child
))
4570 res
= CreateBR(inNode
, 0, address_of(unused
));
4571 NS_ENSURE_SUCCESS(res
, res
);
4575 // we need a br at end unless:
4576 // 1) following sibling of inNode is a block, OR
4577 // 2) last child of inNode is a block, OR
4578 // 3) last child of inNode is a block OR
4579 // 4) either is null
4581 res
= GetNextHTMLSibling(inNode
, address_of(sibling
));
4582 NS_ENSURE_SUCCESS(res
, res
);
4583 if (sibling
&& !IsBlockNode(sibling
))
4585 res
= GetLastEditableChild(inNode
, address_of(child
));
4586 NS_ENSURE_SUCCESS(res
, res
);
4587 if (child
&& !IsBlockNode(child
) && !nsTextEditUtils::IsBreak(child
))
4591 res
= GetLengthOfDOMNode(inNode
, len
);
4592 NS_ENSURE_SUCCESS(res
, res
);
4593 res
= CreateBR(inNode
, (PRInt32
)len
, address_of(unused
));
4594 NS_ENSURE_SUCCESS(res
, res
);
4598 else // the case of inNode being empty
4600 // we need a br at start unless:
4601 // 1) previous sibling of inNode is a block, OR
4602 // 2) previous sibling of inNode is a br, OR
4603 // 3) following sibling of inNode is a block, OR
4604 // 4) following sibling of inNode is a br OR
4605 // 5) either is null
4606 res
= GetPriorHTMLSibling(inNode
, address_of(sibling
));
4607 NS_ENSURE_SUCCESS(res
, res
);
4608 if (sibling
&& !IsBlockNode(sibling
) && !nsTextEditUtils::IsBreak(sibling
))
4610 res
= GetNextHTMLSibling(inNode
, address_of(sibling
));
4611 NS_ENSURE_SUCCESS(res
, res
);
4612 if (sibling
&& !IsBlockNode(sibling
) && !nsTextEditUtils::IsBreak(sibling
))
4615 res
= CreateBR(inNode
, 0, address_of(unused
));
4616 NS_ENSURE_SUCCESS(res
, res
);
4621 // now remove container
4622 return RemoveContainer(inNode
);
4626 ///////////////////////////////////////////////////////////////////////////
4627 // GetPriorHTMLSibling: returns the previous editable sibling, if there is
4628 // one within the parent
4631 nsHTMLEditor::GetPriorHTMLSibling(nsIDOMNode
*inNode
, nsCOMPtr
<nsIDOMNode
> *outNode
)
4633 NS_ENSURE_TRUE(outNode
&& inNode
, NS_ERROR_NULL_POINTER
);
4634 nsresult res
= NS_OK
;
4636 nsCOMPtr
<nsIDOMNode
> temp
, node
= do_QueryInterface(inNode
);
4640 res
= node
->GetPreviousSibling(getter_AddRefs(temp
));
4641 NS_ENSURE_SUCCESS(res
, res
);
4642 NS_ENSURE_TRUE(temp
, NS_OK
); // return null sibling
4643 // if it's editable, we're done
4644 if (IsEditable(temp
)) break;
4645 // otherwise try again
4654 ///////////////////////////////////////////////////////////////////////////
4655 // GetPriorHTMLSibling: returns the previous editable sibling, if there is
4656 // one within the parent. just like above routine but
4657 // takes a parent/offset instead of a node.
4660 nsHTMLEditor::GetPriorHTMLSibling(nsIDOMNode
*inParent
, PRInt32 inOffset
, nsCOMPtr
<nsIDOMNode
> *outNode
)
4662 NS_ENSURE_TRUE(outNode
&& inParent
, NS_ERROR_NULL_POINTER
);
4663 nsresult res
= NS_OK
;
4665 NS_ENSURE_TRUE(inOffset
, NS_OK
); // return null sibling if at offset zero
4666 nsCOMPtr
<nsIDOMNode
> node
= nsEditor::GetChildAt(inParent
,inOffset
-1);
4667 if (IsEditable(node
))
4673 return GetPriorHTMLSibling(node
, outNode
);
4678 ///////////////////////////////////////////////////////////////////////////
4679 // GetNextHTMLSibling: returns the next editable sibling, if there is
4680 // one within the parent
4683 nsHTMLEditor::GetNextHTMLSibling(nsIDOMNode
*inNode
, nsCOMPtr
<nsIDOMNode
> *outNode
)
4685 NS_ENSURE_TRUE(outNode
, NS_ERROR_NULL_POINTER
);
4686 nsresult res
= NS_OK
;
4688 nsCOMPtr
<nsIDOMNode
> temp
, node
= do_QueryInterface(inNode
);
4692 res
= node
->GetNextSibling(getter_AddRefs(temp
));
4693 NS_ENSURE_SUCCESS(res
, res
);
4694 NS_ENSURE_TRUE(temp
, NS_OK
); // return null sibling
4695 // if it's editable, we're done
4696 if (IsEditable(temp
)) break;
4697 // otherwise try again
4706 ///////////////////////////////////////////////////////////////////////////
4707 // GetNextHTMLSibling: returns the next editable sibling, if there is
4708 // one within the parent. just like above routine but
4709 // takes a parent/offset instead of a node.
4712 nsHTMLEditor::GetNextHTMLSibling(nsIDOMNode
*inParent
, PRInt32 inOffset
, nsCOMPtr
<nsIDOMNode
> *outNode
)
4714 NS_ENSURE_TRUE(outNode
&& inParent
, NS_ERROR_NULL_POINTER
);
4715 nsresult res
= NS_OK
;
4717 nsCOMPtr
<nsIDOMNode
> node
= nsEditor::GetChildAt(inParent
,inOffset
);
4718 NS_ENSURE_TRUE(node
, NS_OK
); // return null sibling if no sibling
4719 if (IsEditable(node
))
4725 return GetPriorHTMLSibling(node
, outNode
);
4730 ///////////////////////////////////////////////////////////////////////////
4731 // GetPriorHTMLNode: returns the previous editable leaf node, if there is
4732 // one within the <body>
4735 nsHTMLEditor::GetPriorHTMLNode(nsIDOMNode
*inNode
, nsCOMPtr
<nsIDOMNode
> *outNode
, PRBool bNoBlockCrossing
)
4737 NS_ENSURE_TRUE(outNode
, NS_ERROR_NULL_POINTER
);
4738 nsresult res
= GetPriorNode(inNode
, PR_TRUE
, address_of(*outNode
), bNoBlockCrossing
);
4739 NS_ENSURE_SUCCESS(res
, res
);
4741 // if it's not in the body, then zero it out
4742 if (*outNode
&& !nsTextEditUtils::InBody(*outNode
, this))
4750 ///////////////////////////////////////////////////////////////////////////
4751 // GetPriorHTMLNode: same as above but takes {parent,offset} instead of node
4754 nsHTMLEditor::GetPriorHTMLNode(nsIDOMNode
*inParent
, PRInt32 inOffset
, nsCOMPtr
<nsIDOMNode
> *outNode
, PRBool bNoBlockCrossing
)
4756 NS_ENSURE_TRUE(outNode
, NS_ERROR_NULL_POINTER
);
4757 nsresult res
= GetPriorNode(inParent
, inOffset
, PR_TRUE
, address_of(*outNode
), bNoBlockCrossing
);
4758 NS_ENSURE_SUCCESS(res
, res
);
4760 // if it's not in the body, then zero it out
4761 if (*outNode
&& !nsTextEditUtils::InBody(*outNode
, this))
4769 ///////////////////////////////////////////////////////////////////////////
4770 // GetNextHTMLNode: returns the next editable leaf node, if there is
4771 // one within the <body>
4774 nsHTMLEditor::GetNextHTMLNode(nsIDOMNode
*inNode
, nsCOMPtr
<nsIDOMNode
> *outNode
, PRBool bNoBlockCrossing
)
4776 NS_ENSURE_TRUE(outNode
, NS_ERROR_NULL_POINTER
);
4777 nsresult res
= GetNextNode(inNode
, PR_TRUE
, address_of(*outNode
), bNoBlockCrossing
);
4778 NS_ENSURE_SUCCESS(res
, res
);
4780 // if it's not in the body, then zero it out
4781 if (*outNode
&& !nsTextEditUtils::InBody(*outNode
, this))
4789 ///////////////////////////////////////////////////////////////////////////
4790 // GetNHTMLextNode: same as above but takes {parent,offset} instead of node
4793 nsHTMLEditor::GetNextHTMLNode(nsIDOMNode
*inParent
, PRInt32 inOffset
, nsCOMPtr
<nsIDOMNode
> *outNode
, PRBool bNoBlockCrossing
)
4795 NS_ENSURE_TRUE(outNode
, NS_ERROR_NULL_POINTER
);
4796 nsresult res
= GetNextNode(inParent
, inOffset
, PR_TRUE
, address_of(*outNode
), bNoBlockCrossing
);
4797 NS_ENSURE_SUCCESS(res
, res
);
4799 // if it's not in the body, then zero it out
4800 if (*outNode
&& !nsTextEditUtils::InBody(*outNode
, this))
4809 nsHTMLEditor::IsFirstEditableChild( nsIDOMNode
*aNode
, PRBool
*aOutIsFirst
)
4812 NS_ENSURE_TRUE(aOutIsFirst
&& aNode
, NS_ERROR_NULL_POINTER
);
4815 *aOutIsFirst
= PR_FALSE
;
4817 // find first editable child and compare it to aNode
4818 nsCOMPtr
<nsIDOMNode
> parent
, firstChild
;
4819 nsresult res
= aNode
->GetParentNode(getter_AddRefs(parent
));
4820 NS_ENSURE_SUCCESS(res
, res
);
4821 NS_ENSURE_TRUE(parent
, NS_ERROR_FAILURE
);
4822 res
= GetFirstEditableChild(parent
, address_of(firstChild
));
4823 NS_ENSURE_SUCCESS(res
, res
);
4825 *aOutIsFirst
= (firstChild
.get() == aNode
);
4831 nsHTMLEditor::IsLastEditableChild( nsIDOMNode
*aNode
, PRBool
*aOutIsLast
)
4834 NS_ENSURE_TRUE(aOutIsLast
&& aNode
, NS_ERROR_NULL_POINTER
);
4837 *aOutIsLast
= PR_FALSE
;
4839 // find last editable child and compare it to aNode
4840 nsCOMPtr
<nsIDOMNode
> parent
, lastChild
;
4841 nsresult res
= aNode
->GetParentNode(getter_AddRefs(parent
));
4842 NS_ENSURE_SUCCESS(res
, res
);
4843 NS_ENSURE_TRUE(parent
, NS_ERROR_FAILURE
);
4844 res
= GetLastEditableChild(parent
, address_of(lastChild
));
4845 NS_ENSURE_SUCCESS(res
, res
);
4847 *aOutIsLast
= (lastChild
.get() == aNode
);
4853 nsHTMLEditor::GetFirstEditableChild( nsIDOMNode
*aNode
, nsCOMPtr
<nsIDOMNode
> *aOutFirstChild
)
4856 NS_ENSURE_TRUE(aOutFirstChild
&& aNode
, NS_ERROR_NULL_POINTER
);
4859 *aOutFirstChild
= nsnull
;
4861 // find first editable child
4862 nsCOMPtr
<nsIDOMNode
> child
;
4863 nsresult res
= aNode
->GetFirstChild(getter_AddRefs(child
));
4864 NS_ENSURE_SUCCESS(res
, res
);
4866 while (child
&& !IsEditable(child
))
4868 nsCOMPtr
<nsIDOMNode
> tmp
;
4869 res
= child
->GetNextSibling(getter_AddRefs(tmp
));
4870 NS_ENSURE_SUCCESS(res
, res
);
4871 NS_ENSURE_TRUE(tmp
, NS_ERROR_FAILURE
);
4875 *aOutFirstChild
= child
;
4881 nsHTMLEditor::GetLastEditableChild( nsIDOMNode
*aNode
, nsCOMPtr
<nsIDOMNode
> *aOutLastChild
)
4884 NS_ENSURE_TRUE(aOutLastChild
&& aNode
, NS_ERROR_NULL_POINTER
);
4887 *aOutLastChild
= aNode
;
4889 // find last editable child
4890 nsCOMPtr
<nsIDOMNode
> child
;
4891 nsresult res
= aNode
->GetLastChild(getter_AddRefs(child
));
4892 NS_ENSURE_SUCCESS(res
, res
);
4894 while (child
&& !IsEditable(child
))
4896 nsCOMPtr
<nsIDOMNode
> tmp
;
4897 res
= child
->GetPreviousSibling(getter_AddRefs(tmp
));
4898 NS_ENSURE_SUCCESS(res
, res
);
4899 NS_ENSURE_TRUE(tmp
, NS_ERROR_FAILURE
);
4903 *aOutLastChild
= child
;
4908 nsHTMLEditor::GetFirstEditableLeaf( nsIDOMNode
*aNode
, nsCOMPtr
<nsIDOMNode
> *aOutFirstLeaf
)
4911 NS_ENSURE_TRUE(aOutFirstLeaf
&& aNode
, NS_ERROR_NULL_POINTER
);
4914 *aOutFirstLeaf
= aNode
;
4916 // find leftmost leaf
4917 nsCOMPtr
<nsIDOMNode
> child
;
4918 nsresult res
= NS_OK
;
4919 child
= GetLeftmostChild(aNode
);
4920 while (child
&& (!IsEditable(child
) || !nsEditorUtils::IsLeafNode(child
)))
4922 nsCOMPtr
<nsIDOMNode
> tmp
;
4923 res
= GetNextHTMLNode(child
, address_of(tmp
));
4924 NS_ENSURE_SUCCESS(res
, res
);
4925 NS_ENSURE_TRUE(tmp
, NS_ERROR_FAILURE
);
4927 // only accept nodes that are descendants of aNode
4928 if (nsEditorUtils::IsDescendantOf(tmp
, aNode
))
4932 child
= nsnull
; // this will abort the loop
4936 *aOutFirstLeaf
= child
;
4942 nsHTMLEditor::GetLastEditableLeaf( nsIDOMNode
*aNode
, nsCOMPtr
<nsIDOMNode
> *aOutLastLeaf
)
4945 NS_ENSURE_TRUE(aOutLastLeaf
&& aNode
, NS_ERROR_NULL_POINTER
);
4948 *aOutLastLeaf
= nsnull
;
4950 // find rightmost leaf
4951 nsCOMPtr
<nsIDOMNode
> child
;
4952 nsresult res
= NS_OK
;
4953 child
= GetRightmostChild(aNode
, PR_FALSE
);
4954 while (child
&& (!IsEditable(child
) || !nsEditorUtils::IsLeafNode(child
)))
4956 nsCOMPtr
<nsIDOMNode
> tmp
;
4957 res
= GetPriorHTMLNode(child
, address_of(tmp
));
4958 NS_ENSURE_SUCCESS(res
, res
);
4959 NS_ENSURE_TRUE(tmp
, NS_ERROR_FAILURE
);
4961 // only accept nodes that are descendants of aNode
4962 if (nsEditorUtils::IsDescendantOf(tmp
, aNode
))
4970 *aOutLastLeaf
= child
;
4975 nsHTMLEditor::IsTextInDirtyFrameVisible(nsIDOMNode
*aNode
)
4977 PRBool isEmptyTextNode
;
4978 nsresult res
= IsVisTextNode(aNode
, &isEmptyTextNode
, PR_FALSE
);
4981 // We are following the historical decision:
4982 // if we don't know, we say it's visible...
4987 return !isEmptyTextNode
;
4991 ///////////////////////////////////////////////////////////////////////////
4992 // IsVisTextNode: figure out if textnode aTextNode has any visible content.
4995 nsHTMLEditor::IsVisTextNode( nsIDOMNode
*aNode
,
4996 PRBool
*outIsEmptyNode
,
4997 PRBool aSafeToAskFrames
)
4999 NS_ENSURE_TRUE(aNode
&& outIsEmptyNode
, NS_ERROR_NULL_POINTER
);
5000 *outIsEmptyNode
= PR_TRUE
;
5001 nsresult res
= NS_OK
;
5003 nsCOMPtr
<nsIContent
> textContent
= do_QueryInterface(aNode
);
5004 // callers job to only call us with text nodes
5005 if (!textContent
|| !textContent
->IsNodeOfType(nsINode::eTEXT
))
5006 return NS_ERROR_NULL_POINTER
;
5007 PRUint32 length
= textContent
->TextLength();
5008 if (aSafeToAskFrames
)
5010 nsCOMPtr
<nsISelectionController
> selCon
;
5011 res
= GetSelectionController(getter_AddRefs(selCon
));
5012 NS_ENSURE_SUCCESS(res
, res
);
5013 NS_ENSURE_TRUE(selCon
, NS_ERROR_FAILURE
);
5014 PRBool isVisible
= PR_FALSE
;
5015 // ask the selection controller for information about whether any
5016 // of the data in the node is really rendered. This is really
5017 // something that frames know about, but we aren't supposed to talk to frames.
5018 // So we put a call in the selection controller interface, since it's already
5019 // in bed with frames anyway. (this is a fix for bug 22227, and a
5020 // partial fix for bug 46209)
5021 res
= selCon
->CheckVisibility(aNode
, 0, length
, &isVisible
);
5022 NS_ENSURE_SUCCESS(res
, res
);
5025 *outIsEmptyNode
= PR_FALSE
;
5030 if (textContent
->TextIsOnlyWhitespace())
5032 nsWSRunObject
wsRunObj(this, aNode
, 0);
5033 nsCOMPtr
<nsIDOMNode
> visNode
;
5034 PRInt32 outVisOffset
=0;
5036 res
= wsRunObj
.NextVisibleNode(aNode
, 0, address_of(visNode
), &outVisOffset
, &visType
);
5037 NS_ENSURE_SUCCESS(res
, res
);
5038 if ( (visType
== nsWSRunObject::eNormalWS
) ||
5039 (visType
== nsWSRunObject::eText
) )
5041 *outIsEmptyNode
= (aNode
!= visNode
);
5046 *outIsEmptyNode
= PR_FALSE
;
5053 ///////////////////////////////////////////////////////////////////////////
5054 // IsEmptyNode: figure out if aNode is an empty node.
5055 // A block can have children and still be considered empty,
5056 // if the children are empty or non-editable.
5059 nsHTMLEditor::IsEmptyNode( nsIDOMNode
*aNode
,
5060 PRBool
*outIsEmptyNode
,
5061 PRBool aSingleBRDoesntCount
,
5062 PRBool aListOrCellNotEmpty
,
5063 PRBool aSafeToAskFrames
)
5065 NS_ENSURE_TRUE(aNode
&& outIsEmptyNode
, NS_ERROR_NULL_POINTER
);
5066 *outIsEmptyNode
= PR_TRUE
;
5067 PRBool seenBR
= PR_FALSE
;
5068 return IsEmptyNodeImpl(aNode
, outIsEmptyNode
, aSingleBRDoesntCount
,
5069 aListOrCellNotEmpty
, aSafeToAskFrames
, &seenBR
);
5072 ///////////////////////////////////////////////////////////////////////////
5073 // IsEmptyNodeImpl: workhorse for IsEmptyNode.
5076 nsHTMLEditor::IsEmptyNodeImpl( nsIDOMNode
*aNode
,
5077 PRBool
*outIsEmptyNode
,
5078 PRBool aSingleBRDoesntCount
,
5079 PRBool aListOrCellNotEmpty
,
5080 PRBool aSafeToAskFrames
,
5083 NS_ENSURE_TRUE(aNode
&& outIsEmptyNode
&& aSeenBR
, NS_ERROR_NULL_POINTER
);
5084 nsresult res
= NS_OK
;
5086 if (nsEditor::IsTextNode(aNode
))
5088 res
= IsVisTextNode(aNode
, outIsEmptyNode
, aSafeToAskFrames
);
5092 // if it's not a text node (handled above) and it's not a container,
5093 // then we don't call it empty (it's an <hr>, or <br>, etc).
5094 // Also, if it's an anchor then don't treat it as empty - even though
5095 // anchors are containers, named anchors are "empty" but we don't
5096 // want to treat them as such. Also, don't call ListItems or table
5097 // cells empty if caller desires. Form Widgets not empty.
5098 if (!IsContainer(aNode
) || nsHTMLEditUtils::IsNamedAnchor(aNode
) ||
5099 nsHTMLEditUtils::IsFormWidget(aNode
) ||
5100 (aListOrCellNotEmpty
&& nsHTMLEditUtils::IsListItem(aNode
)) ||
5101 (aListOrCellNotEmpty
&& nsHTMLEditUtils::IsTableCell(aNode
)) )
5103 *outIsEmptyNode
= PR_FALSE
;
5107 // need this for later
5108 PRBool isListItemOrCell
=
5109 nsHTMLEditUtils::IsListItem(aNode
) || nsHTMLEditUtils::IsTableCell(aNode
);
5111 // loop over children of node. if no children, or all children are either
5112 // empty text nodes or non-editable, then node qualifies as empty
5113 nsCOMPtr
<nsIDOMNode
> child
;
5114 aNode
->GetFirstChild(getter_AddRefs(child
));
5118 nsCOMPtr
<nsIDOMNode
> node
= child
;
5119 // is the node editable and non-empty? if so, return false
5120 if (nsEditor::IsEditable(node
))
5122 if (nsEditor::IsTextNode(node
))
5124 res
= IsVisTextNode(node
, outIsEmptyNode
, aSafeToAskFrames
);
5125 NS_ENSURE_SUCCESS(res
, res
);
5126 NS_ENSURE_TRUE(*outIsEmptyNode
, NS_OK
); // break out if we find we aren't emtpy
5128 else // an editable, non-text node. we need to check it's content.
5130 // is it the node we are iterating over?
5131 if (node
== aNode
) break;
5132 else if (aSingleBRDoesntCount
&& !*aSeenBR
&& nsTextEditUtils::IsBreak(node
))
5134 // the first br in a block doesn't count if the caller so indicated
5139 // is it an empty node of some sort?
5140 // note: list items or table cells are not considered empty
5141 // if they contain other lists or tables
5142 if (isListItemOrCell
)
5144 if (nsHTMLEditUtils::IsList(node
) || nsHTMLEditUtils::IsTable(node
))
5145 { // break out if we find we aren't empty
5146 *outIsEmptyNode
= PR_FALSE
;
5150 // is it a form widget?
5151 else if (nsHTMLEditUtils::IsFormWidget(aNode
))
5152 { // break out if we find we aren't empty
5153 *outIsEmptyNode
= PR_FALSE
;
5157 PRBool isEmptyNode
= PR_TRUE
;
5158 res
= IsEmptyNodeImpl(node
, &isEmptyNode
, aSingleBRDoesntCount
,
5159 aListOrCellNotEmpty
, aSafeToAskFrames
, aSeenBR
);
5160 NS_ENSURE_SUCCESS(res
, res
);
5163 // otherwise it ain't empty
5164 *outIsEmptyNode
= PR_FALSE
;
5170 node
->GetNextSibling(getter_AddRefs(child
));
5176 // add to aElement the CSS inline styles corresponding to the HTML attribute
5177 // aAttribute with its value aValue
5179 nsHTMLEditor::SetAttributeOrEquivalent(nsIDOMElement
* aElement
,
5180 const nsAString
& aAttribute
,
5181 const nsAString
& aValue
,
5182 PRBool aSuppressTransaction
)
5185 nsresult res
= NS_OK
;
5186 GetIsCSSEnabled(&useCSS
);
5187 if (useCSS
&& mHTMLCSSUtils
) {
5189 res
= mHTMLCSSUtils
->SetCSSEquivalentToHTMLStyle(aElement
, nsnull
, &aAttribute
, &aValue
, &count
,
5190 aSuppressTransaction
);
5191 NS_ENSURE_SUCCESS(res
, res
);
5193 // we found an equivalence ; let's remove the HTML attribute itself if it is set
5194 nsAutoString existingValue
;
5195 PRBool wasSet
= PR_FALSE
;
5196 res
= GetAttributeValue(aElement
, aAttribute
, existingValue
, &wasSet
);
5197 NS_ENSURE_SUCCESS(res
, res
);
5199 if (aSuppressTransaction
)
5200 res
= aElement
->RemoveAttribute(aAttribute
);
5202 res
= RemoveAttribute(aElement
, aAttribute
);
5206 // count is an integer that represents the number of CSS declarations applied to the
5207 // element. If it is zero, we found no equivalence in this implementation for the
5209 if (aAttribute
.EqualsLiteral("style")) {
5210 // if it is the style attribute, just add the new value to the existing style
5211 // attribute's value
5212 nsAutoString existingValue
;
5213 PRBool wasSet
= PR_FALSE
;
5214 res
= GetAttributeValue(aElement
, NS_LITERAL_STRING("style"), existingValue
, &wasSet
);
5215 NS_ENSURE_SUCCESS(res
, res
);
5216 existingValue
.AppendLiteral(" ");
5217 existingValue
.Append(aValue
);
5218 if (aSuppressTransaction
)
5219 res
= aElement
->SetAttribute(aAttribute
, existingValue
);
5221 res
= SetAttribute(aElement
, aAttribute
, existingValue
);
5224 // we have no CSS equivalence for this attribute and it is not the style
5225 // attribute; let's set it the good'n'old HTML way
5226 if (aSuppressTransaction
)
5227 res
= aElement
->SetAttribute(aAttribute
, aValue
);
5229 res
= SetAttribute(aElement
, aAttribute
, aValue
);
5234 // we are not in an HTML+CSS editor; let's set the attribute the HTML way
5235 if (aSuppressTransaction
)
5236 res
= aElement
->SetAttribute(aAttribute
, aValue
);
5238 res
= SetAttribute(aElement
, aAttribute
, aValue
);
5244 nsHTMLEditor::RemoveAttributeOrEquivalent(nsIDOMElement
* aElement
,
5245 const nsAString
& aAttribute
,
5246 PRBool aSuppressTransaction
)
5249 nsresult res
= NS_OK
;
5250 GetIsCSSEnabled(&useCSS
);
5251 if (useCSS
&& mHTMLCSSUtils
) {
5252 res
= mHTMLCSSUtils
->RemoveCSSEquivalentToHTMLStyle(aElement
, nsnull
, &aAttribute
, nsnull
,
5253 aSuppressTransaction
);
5254 NS_ENSURE_SUCCESS(res
, res
);
5257 nsAutoString existingValue
;
5258 PRBool wasSet
= PR_FALSE
;
5259 res
= GetAttributeValue(aElement
, aAttribute
, existingValue
, &wasSet
);
5260 NS_ENSURE_SUCCESS(res
, res
);
5262 if (aSuppressTransaction
)
5263 res
= aElement
->RemoveAttribute(aAttribute
);
5265 res
= RemoveAttribute(aElement
, aAttribute
);
5271 nsHTMLEditor::SetIsCSSEnabled(PRBool aIsCSSPrefChecked
)
5273 nsresult err
= NS_ERROR_NOT_INITIALIZED
;
5276 err
= mHTMLCSSUtils
->SetCSSEnabled(aIsCSSPrefChecked
);
5278 // Disable the eEditorNoCSSMask flag if we're enabling StyleWithCSS.
5279 if (NS_SUCCEEDED(err
)) {
5280 PRUint32 flags
= mFlags
;
5281 if (aIsCSSPrefChecked
) {
5282 // Turn off NoCSS as we're enabling CSS
5283 flags
&= ~eEditorNoCSSMask
;
5285 // Turn on NoCSS, as we're disabling CSS.
5286 flags
|= eEditorNoCSSMask
;
5289 err
= SetFlags(flags
);
5290 NS_ENSURE_SUCCESS(err
, err
);
5295 // Set the block background color
5297 nsHTMLEditor::SetCSSBackgroundColor(const nsAString
& aColor
)
5299 if (!mRules
) { return NS_ERROR_NOT_INITIALIZED
; }
5300 ForceCompositionEnd();
5302 // Protect the edit rules object from dying
5303 nsCOMPtr
<nsIEditRules
> kungFuDeathGrip(mRules
);
5306 nsCOMPtr
<nsISelection
>selection
;
5307 res
= GetSelection(getter_AddRefs(selection
));
5308 NS_ENSURE_SUCCESS(res
, res
);
5309 NS_ENSURE_TRUE(selection
, NS_ERROR_NULL_POINTER
);
5310 nsCOMPtr
<nsISelectionPrivate
> selPriv(do_QueryInterface(selection
));
5313 selection
->GetIsCollapsed(&isCollapsed
);
5315 nsAutoEditBatch
batchIt(this);
5316 nsAutoRules
beginRulesSniffing(this, kOpInsertElement
, nsIEditor::eNext
);
5317 nsAutoSelectionReset
selectionResetter(selection
, this);
5318 nsAutoTxnsConserveSelection
dontSpazMySelection(this);
5320 PRBool cancel
, handled
;
5321 nsTextRulesInfo
ruleInfo(nsTextEditRules::kSetTextProperty
);
5322 res
= mRules
->WillDoAction(selection
, &ruleInfo
, &cancel
, &handled
);
5323 NS_ENSURE_SUCCESS(res
, res
);
5324 if (!cancel
&& !handled
)
5326 // get selection range enumerator
5327 nsCOMPtr
<nsIEnumerator
> enumerator
;
5328 res
= selPriv
->GetEnumerator(getter_AddRefs(enumerator
));
5329 NS_ENSURE_SUCCESS(res
, res
);
5330 NS_ENSURE_TRUE(enumerator
, NS_ERROR_FAILURE
);
5332 // loop thru the ranges in the selection
5333 enumerator
->First();
5334 nsCOMPtr
<nsISupports
> currentItem
;
5335 nsAutoString bgcolor
; bgcolor
.AssignLiteral("bgcolor");
5336 nsCOMPtr
<nsIDOMNode
> cachedBlockParent
= nsnull
;
5337 while ((NS_ENUMERATOR_FALSE
== enumerator
->IsDone()))
5339 res
= enumerator
->CurrentItem(getter_AddRefs(currentItem
));
5340 NS_ENSURE_SUCCESS(res
, res
);
5341 NS_ENSURE_TRUE(currentItem
, NS_ERROR_FAILURE
);
5343 nsCOMPtr
<nsIDOMRange
> range( do_QueryInterface(currentItem
) );
5345 // check for easy case: both range endpoints in same text node
5346 nsCOMPtr
<nsIDOMNode
> startNode
, endNode
;
5347 PRInt32 startOffset
, endOffset
;
5348 res
= range
->GetStartContainer(getter_AddRefs(startNode
));
5349 NS_ENSURE_SUCCESS(res
, res
);
5350 res
= range
->GetEndContainer(getter_AddRefs(endNode
));
5351 NS_ENSURE_SUCCESS(res
, res
);
5352 res
= range
->GetStartOffset(&startOffset
);
5353 NS_ENSURE_SUCCESS(res
, res
);
5354 res
= range
->GetEndOffset(&endOffset
);
5355 NS_ENSURE_SUCCESS(res
, res
);
5356 if ((startNode
== endNode
) && IsTextNode(startNode
))
5358 // let's find the block container of the text node
5359 nsCOMPtr
<nsIDOMNode
> blockParent
;
5360 blockParent
= GetBlockNodeParent(startNode
);
5361 // and apply the background color to that block container
5362 if (cachedBlockParent
!= blockParent
)
5364 cachedBlockParent
= blockParent
;
5365 nsCOMPtr
<nsIDOMElement
> element
= do_QueryInterface(blockParent
);
5367 res
= mHTMLCSSUtils
->SetCSSEquivalentToHTMLStyle(element
, nsnull
, &bgcolor
, &aColor
, &count
, PR_FALSE
);
5368 NS_ENSURE_SUCCESS(res
, res
);
5371 else if ((startNode
== endNode
) && nsTextEditUtils::IsBody(startNode
) && isCollapsed
)
5373 // we have no block in the document, let's apply the background to the body
5374 nsCOMPtr
<nsIDOMElement
> element
= do_QueryInterface(startNode
);
5376 res
= mHTMLCSSUtils
->SetCSSEquivalentToHTMLStyle(element
, nsnull
, &bgcolor
, &aColor
, &count
, PR_FALSE
);
5377 NS_ENSURE_SUCCESS(res
, res
);
5379 else if ((startNode
== endNode
) && (((endOffset
-startOffset
) == 1) || (!startOffset
&& !endOffset
)))
5381 // a unique node is selected, let's also apply the background color
5382 // to the containing block, possibly the node itself
5383 nsCOMPtr
<nsIDOMNode
> selectedNode
= GetChildAt(startNode
, startOffset
);
5384 PRBool isBlock
=PR_FALSE
;
5385 res
= NodeIsBlockStatic(selectedNode
, &isBlock
);
5386 NS_ENSURE_SUCCESS(res
, res
);
5387 nsCOMPtr
<nsIDOMNode
> blockParent
= selectedNode
;
5389 blockParent
= GetBlockNodeParent(selectedNode
);
5391 if (cachedBlockParent
!= blockParent
)
5393 cachedBlockParent
= blockParent
;
5394 nsCOMPtr
<nsIDOMElement
> element
= do_QueryInterface(blockParent
);
5396 res
= mHTMLCSSUtils
->SetCSSEquivalentToHTMLStyle(element
, nsnull
, &bgcolor
, &aColor
, &count
, PR_FALSE
);
5397 NS_ENSURE_SUCCESS(res
, res
);
5402 // not the easy case. range not contained in single text node.
5403 // there are up to three phases here. There are all the nodes
5404 // reported by the subtree iterator to be processed. And there
5405 // are potentially a starting textnode and an ending textnode
5406 // which are only partially contained by the range.
5408 // lets handle the nodes reported by the iterator. These nodes
5409 // are entirely contained in the selection range. We build up
5410 // a list of them (since doing operations on the document during
5411 // iteration would perturb the iterator).
5413 nsCOMPtr
<nsIContentIterator
> iter
=
5414 do_CreateInstance("@mozilla.org/content/subtree-content-iterator;1", &res
);
5415 NS_ENSURE_SUCCESS(res
, res
);
5416 NS_ENSURE_TRUE(iter
, NS_ERROR_FAILURE
);
5418 nsCOMArray
<nsIDOMNode
> arrayOfNodes
;
5419 nsCOMPtr
<nsIDOMNode
> node
;
5421 // iterate range and build up array
5422 res
= iter
->Init(range
);
5423 // init returns an error if no nodes in range.
5424 // this can easily happen with the subtree
5425 // iterator if the selection doesn't contain
5426 // any *whole* nodes.
5427 if (NS_SUCCEEDED(res
))
5429 while (!iter
->IsDone())
5431 node
= do_QueryInterface(iter
->GetCurrentNode());
5432 NS_ENSURE_TRUE(node
, NS_ERROR_FAILURE
);
5434 if (IsEditable(node
))
5436 arrayOfNodes
.AppendObject(node
);
5442 // first check the start parent of the range to see if it needs to
5443 // be separately handled (it does if it's a text node, due to how the
5444 // subtree iterator works - it will not have reported it).
5445 if (IsTextNode(startNode
) && IsEditable(startNode
))
5447 nsCOMPtr
<nsIDOMNode
> blockParent
;
5448 blockParent
= GetBlockNodeParent(startNode
);
5449 if (cachedBlockParent
!= blockParent
)
5451 cachedBlockParent
= blockParent
;
5452 nsCOMPtr
<nsIDOMElement
> element
= do_QueryInterface(blockParent
);
5454 res
= mHTMLCSSUtils
->SetCSSEquivalentToHTMLStyle(element
, nsnull
, &bgcolor
, &aColor
, &count
, PR_FALSE
);
5455 NS_ENSURE_SUCCESS(res
, res
);
5459 // then loop through the list, set the property on each node
5460 PRInt32 listCount
= arrayOfNodes
.Count();
5462 for (j
= 0; j
< listCount
; j
++)
5464 node
= arrayOfNodes
[j
];
5465 // do we have a block here ?
5466 PRBool isBlock
=PR_FALSE
;
5467 res
= NodeIsBlockStatic(node
, &isBlock
);
5468 NS_ENSURE_SUCCESS(res
, res
);
5469 nsCOMPtr
<nsIDOMNode
> blockParent
= node
;
5471 // no we don't, let's find the block ancestor
5472 blockParent
= GetBlockNodeParent(node
);
5474 if (cachedBlockParent
!= blockParent
)
5476 cachedBlockParent
= blockParent
;
5477 nsCOMPtr
<nsIDOMElement
> element
= do_QueryInterface(blockParent
);
5479 // and set the property on it
5480 res
= mHTMLCSSUtils
->SetCSSEquivalentToHTMLStyle(element
, nsnull
, &bgcolor
, &aColor
, &count
, PR_FALSE
);
5481 NS_ENSURE_SUCCESS(res
, res
);
5484 arrayOfNodes
.Clear();
5486 // last check the end parent of the range to see if it needs to
5487 // be separately handled (it does if it's a text node, due to how the
5488 // subtree iterator works - it will not have reported it).
5489 if (IsTextNode(endNode
) && IsEditable(endNode
))
5491 nsCOMPtr
<nsIDOMNode
> blockParent
;
5492 blockParent
= GetBlockNodeParent(endNode
);
5493 if (cachedBlockParent
!= blockParent
)
5495 cachedBlockParent
= blockParent
;
5496 nsCOMPtr
<nsIDOMElement
> element
= do_QueryInterface(blockParent
);
5498 res
= mHTMLCSSUtils
->SetCSSEquivalentToHTMLStyle(element
, nsnull
, &bgcolor
, &aColor
, &count
, PR_FALSE
);
5499 NS_ENSURE_SUCCESS(res
, res
);
5509 res
= mRules
->DidDoAction(selection
, &ruleInfo
, res
);
5515 nsHTMLEditor::SetBackgroundColor(const nsAString
& aColor
)
5519 GetIsCSSEnabled(&useCSS
);
5521 // if we are in CSS mode, we have to apply the background color to the
5522 // containing block (or the body if we have no block-level element in
5524 res
= SetCSSBackgroundColor(aColor
);
5527 // but in HTML mode, we can only set the document's background color
5528 res
= SetHTMLBackgroundColor(aColor
);
5533 ///////////////////////////////////////////////////////////////////////////
5534 // NodesSameType: do these nodes have the same tag?
5537 nsHTMLEditor::NodesSameType(nsIDOMNode
*aNode1
, nsIDOMNode
*aNode2
)
5539 if (!aNode1
|| !aNode2
)
5541 NS_NOTREACHED("null node passed to nsEditor::NodesSameType()");
5546 GetIsCSSEnabled(&useCSS
);
5548 nsIAtom
*tag1
= GetTag(aNode1
);
5550 if (tag1
== GetTag(aNode2
)) {
5551 if (useCSS
&& tag1
== nsEditProperty::span
) {
5552 if (mHTMLCSSUtils
->ElementsSameStyle(aNode1
, aNode2
)) {
5564 nsHTMLEditor::CopyLastEditableChildStyles(nsIDOMNode
* aPreviousBlock
, nsIDOMNode
* aNewBlock
,
5565 nsIDOMNode
**aOutBrNode
)
5567 *aOutBrNode
= nsnull
;
5568 nsCOMPtr
<nsIDOMNode
> child
, tmp
;
5570 // first, clear out aNewBlock. Contract is that we want only the styles from previousBlock.
5571 res
= aNewBlock
->GetFirstChild(getter_AddRefs(child
));
5572 while (NS_SUCCEEDED(res
) && child
)
5574 res
= DeleteNode(child
);
5575 NS_ENSURE_SUCCESS(res
, res
);
5576 res
= aNewBlock
->GetFirstChild(getter_AddRefs(child
));
5578 // now find and clone the styles
5579 child
= aPreviousBlock
;
5580 tmp
= aPreviousBlock
;
5583 res
= GetLastEditableChild(child
, address_of(tmp
));
5584 NS_ENSURE_SUCCESS(res
, res
);
5586 while (child
&& nsTextEditUtils::IsBreak(child
)) {
5587 nsCOMPtr
<nsIDOMNode
> priorNode
;
5588 res
= GetPriorHTMLNode(child
, address_of(priorNode
));
5589 NS_ENSURE_SUCCESS(res
, res
);
5592 nsCOMPtr
<nsIDOMNode
> newStyles
= nsnull
, deepestStyle
= nsnull
;
5593 while (child
&& (child
!= aPreviousBlock
)) {
5594 if (nsHTMLEditUtils::IsInlineStyle(child
) ||
5595 nsEditor::NodeIsType(child
, nsEditProperty::span
)) {
5596 nsAutoString domTagName
;
5597 child
->GetNodeName(domTagName
);
5598 ToLowerCase(domTagName
);
5600 nsCOMPtr
<nsIDOMNode
> newContainer
;
5601 res
= InsertContainerAbove(newStyles
, address_of(newContainer
), domTagName
);
5602 NS_ENSURE_SUCCESS(res
, res
);
5603 newStyles
= newContainer
;
5606 res
= CreateNode(domTagName
, aNewBlock
, 0, getter_AddRefs(newStyles
));
5607 NS_ENSURE_SUCCESS(res
, res
);
5608 deepestStyle
= newStyles
;
5610 res
= CloneAttributes(newStyles
, child
);
5611 NS_ENSURE_SUCCESS(res
, res
);
5613 nsCOMPtr
<nsIDOMNode
> tmp
;
5614 res
= child
->GetParentNode(getter_AddRefs(tmp
));
5615 NS_ENSURE_SUCCESS(res
, res
);
5619 nsCOMPtr
<nsIDOMNode
> outBRNode
;
5620 res
= CreateBR(deepestStyle
, 0, address_of(outBRNode
));
5621 NS_ENSURE_SUCCESS(res
, res
);
5622 // Getters must addref
5623 *aOutBrNode
= outBRNode
;
5624 NS_ADDREF(*aOutBrNode
);
5630 nsHTMLEditor::GetElementOrigin(nsIDOMElement
* aElement
, PRInt32
& aX
, PRInt32
& aY
)
5635 NS_ENSURE_TRUE(mPresShellWeak
, NS_ERROR_NOT_INITIALIZED
);
5636 nsCOMPtr
<nsIPresShell
> ps
= do_QueryReferent(mPresShellWeak
);
5637 NS_ENSURE_TRUE(ps
, NS_ERROR_NOT_INITIALIZED
);
5639 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(aElement
);
5640 nsIFrame
*frame
= content
->GetPrimaryFrame();
5641 NS_ENSURE_TRUE(frame
, NS_OK
);
5643 nsIFrame
*container
= ps
->GetAbsoluteContainingBlock(frame
);
5644 NS_ENSURE_TRUE(container
, NS_OK
);
5645 nsPoint off
= frame
->GetOffsetTo(container
);
5646 aX
= nsPresContext::AppUnitsToIntCSSPixels(off
.x
);
5647 aY
= nsPresContext::AppUnitsToIntCSSPixels(off
.y
);
5653 nsHTMLEditor::EndUpdateViewBatch()
5655 nsresult res
= nsEditor::EndUpdateViewBatch();
5656 NS_ENSURE_SUCCESS(res
, res
);
5658 // We may need to show resizing handles or update existing ones after
5659 // all transactions are done. This way of doing is preferred to DOM
5660 // mutation events listeners because all the changes the user can apply
5661 // to a document may result in multiple events, some of them quite hard
5662 // to listen too (in particular when an ancestor of the selection is
5663 // changed but the selection itself is not changed).
5664 if (mUpdateCount
== 0) {
5665 nsCOMPtr
<nsISelection
> selection
;
5666 res
= GetSelection(getter_AddRefs(selection
));
5667 NS_ENSURE_SUCCESS(res
, res
);
5668 NS_ENSURE_TRUE(selection
, NS_ERROR_NOT_INITIALIZED
);
5669 res
= CheckSelectionStateForAnonymousButtons(selection
);
5675 nsHTMLEditor::IgnoreSpuriousDragEvent(PRBool aIgnoreSpuriousDragEvent
)
5677 mIgnoreSpuriousDragEvent
= aIgnoreSpuriousDragEvent
;
5682 nsHTMLEditor::GetSelectionContainer(nsIDOMElement
** aReturn
)
5684 nsCOMPtr
<nsISelection
>selection
;
5685 nsresult res
= GetSelection(getter_AddRefs(selection
));
5686 // if we don't get the selection, just skip this
5687 if (NS_FAILED(res
) || !selection
) return res
;
5690 res
= selection
->GetIsCollapsed(&bCollapsed
);
5691 NS_ENSURE_SUCCESS(res
, res
);
5693 nsCOMPtr
<nsIDOMNode
> focusNode
;
5696 res
= selection
->GetFocusNode(getter_AddRefs(focusNode
));
5697 NS_ENSURE_SUCCESS(res
, res
);
5702 res
= selection
->GetRangeCount(&rangeCount
);
5703 NS_ENSURE_SUCCESS(res
, res
);
5705 if (rangeCount
== 1) {
5707 nsCOMPtr
<nsIDOMRange
> range
;
5708 res
= selection
->GetRangeAt(0, getter_AddRefs(range
));
5709 NS_ENSURE_SUCCESS(res
, res
);
5710 NS_ENSURE_TRUE(range
, NS_ERROR_NULL_POINTER
);
5712 nsCOMPtr
<nsIDOMNode
> startContainer
, endContainer
;
5713 res
= range
->GetStartContainer(getter_AddRefs(startContainer
));
5714 NS_ENSURE_SUCCESS(res
, res
);
5715 res
= range
->GetEndContainer(getter_AddRefs(endContainer
));
5716 NS_ENSURE_SUCCESS(res
, res
);
5717 PRInt32 startOffset
, endOffset
;
5718 res
= range
->GetStartOffset(&startOffset
);
5719 NS_ENSURE_SUCCESS(res
, res
);
5720 res
= range
->GetEndOffset(&endOffset
);
5721 NS_ENSURE_SUCCESS(res
, res
);
5723 nsCOMPtr
<nsIDOMElement
> focusElement
;
5724 if (startContainer
== endContainer
&& startOffset
+ 1 == endOffset
) {
5725 res
= GetSelectedElement(EmptyString(), getter_AddRefs(focusElement
));
5726 NS_ENSURE_SUCCESS(res
, res
);
5728 focusNode
= do_QueryInterface(focusElement
);
5731 res
= range
->GetCommonAncestorContainer(getter_AddRefs(focusNode
));
5732 NS_ENSURE_SUCCESS(res
, res
);
5737 nsCOMPtr
<nsIDOMRange
> range
;
5738 for (i
= 0; i
< rangeCount
; i
++)
5740 res
= selection
->GetRangeAt(i
, getter_AddRefs(range
));
5741 NS_ENSURE_SUCCESS(res
, res
);
5742 nsCOMPtr
<nsIDOMNode
> startContainer
;
5743 res
= range
->GetStartContainer(getter_AddRefs(startContainer
));
5744 if (NS_FAILED(res
)) continue;
5746 focusNode
= startContainer
;
5747 else if (focusNode
!= startContainer
) {
5748 res
= startContainer
->GetParentNode(getter_AddRefs(focusNode
));
5749 NS_ENSURE_SUCCESS(res
, res
);
5758 focusNode
->GetNodeType(&nodeType
);
5759 if (nsIDOMNode::TEXT_NODE
== nodeType
) {
5760 nsCOMPtr
<nsIDOMNode
> parent
;
5761 res
= focusNode
->GetParentNode(getter_AddRefs(parent
));
5762 NS_ENSURE_SUCCESS(res
, res
);
5767 nsCOMPtr
<nsIDOMElement
> focusElement
= do_QueryInterface(focusNode
);
5768 *aReturn
= focusElement
;
5769 NS_IF_ADDREF(*aReturn
);
5775 nsHTMLEditor::IsAnonymousElement(nsIDOMElement
* aElement
, PRBool
* aReturn
)
5777 NS_ENSURE_TRUE(aElement
, NS_ERROR_NULL_POINTER
);
5778 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(aElement
);
5779 *aReturn
= content
->IsRootOfNativeAnonymousSubtree();
5784 nsHTMLEditor::SetReturnInParagraphCreatesNewParagraph(PRBool aCreatesNewParagraph
)
5786 mCRInParagraphCreatesParagraph
= aCreatesNewParagraph
;
5791 nsHTMLEditor::GetReturnInParagraphCreatesNewParagraph(PRBool
*aCreatesNewParagraph
)
5793 *aCreatesNewParagraph
= mCRInParagraphCreatesParagraph
;
5797 already_AddRefed
<nsIContent
>
5798 nsHTMLEditor::GetFocusedContent()
5800 NS_ENSURE_TRUE(mDocWeak
, nsnull
);
5802 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
5803 NS_ENSURE_TRUE(fm
, nsnull
);
5805 nsCOMPtr
<nsIContent
> focusedContent
= fm
->GetFocusedContent();
5807 nsCOMPtr
<nsIDocument
> doc
= do_QueryReferent(mDocWeak
);
5808 PRBool inDesignMode
= doc
->HasFlag(NODE_IS_EDITABLE
);
5809 if (!focusedContent
) {
5810 // in designMode, nobody gets focus in most cases.
5811 if (inDesignMode
&& OurWindowHasFocus()) {
5812 nsCOMPtr
<nsIContent
> docRoot
= doc
->GetRootElement();
5813 return docRoot
.forget();
5819 return OurWindowHasFocus() &&
5820 nsContentUtils::ContentIsDescendantOf(focusedContent
, doc
) ?
5821 focusedContent
.forget() : nsnull
;
5824 // We're HTML editor for contenteditable
5826 // If the focused content isn't editable, or it has independent selection,
5827 // we don't have focus.
5828 if (!focusedContent
->HasFlag(NODE_IS_EDITABLE
) ||
5829 focusedContent
->HasIndependentSelection()) {
5832 // If our window is focused, we're focused.
5833 return OurWindowHasFocus() ? focusedContent
.forget() : nsnull
;
5837 nsHTMLEditor::IsActiveInDOMWindow()
5839 NS_ENSURE_TRUE(mDocWeak
, PR_FALSE
);
5841 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
5842 NS_ENSURE_TRUE(fm
, PR_FALSE
);
5844 nsCOMPtr
<nsIDocument
> doc
= do_QueryReferent(mDocWeak
);
5845 PRBool inDesignMode
= doc
->HasFlag(NODE_IS_EDITABLE
);
5847 // If we're in designMode, we're always active in the DOM window.
5852 nsPIDOMWindow
* ourWindow
= doc
->GetWindow();
5853 nsCOMPtr
<nsPIDOMWindow
> win
;
5854 nsIContent
* content
=
5855 nsFocusManager::GetFocusedDescendant(ourWindow
, PR_FALSE
,
5856 getter_AddRefs(win
));
5861 // We're HTML editor for contenteditable
5863 // If the active content isn't editable, or it has independent selection,
5864 // we're not active).
5865 if (!content
->HasFlag(NODE_IS_EDITABLE
) ||
5866 content
->HasIndependentSelection()) {
5872 already_AddRefed
<nsPIDOMEventTarget
>
5873 nsHTMLEditor::GetPIDOMEventTarget()
5875 // Don't use getDocument here, because we have no way of knowing
5876 // whether Init() was ever called. So we need to get the document
5877 // ourselves, if it exists.
5878 NS_PRECONDITION(mDocWeak
, "This editor has not been initialized yet");
5879 nsCOMPtr
<nsPIDOMEventTarget
> piTarget
= do_QueryReferent(mDocWeak
.get());
5880 return piTarget
.forget();
5884 nsHTMLEditor::ShouldReplaceRootElement()
5886 if (!mRootElement
) {
5887 // If we don't know what is our root element, we should find our root.
5891 // If we temporary set document root element to mRootElement, but there is
5892 // body element now, we should replace the root element by the body element.
5893 nsCOMPtr
<nsIDOMHTMLElement
> docBody
;
5894 GetBodyElement(getter_AddRefs(docBody
));
5895 return !SameCOMIdentity(docBody
, mRootElement
);
5899 nsHTMLEditor::ResetRootElementAndEventTarget()
5901 nsCOMPtr
<nsIMutationObserver
> kungFuDeathGrip(this);
5903 // Need to remove the event listeners first because BeginningOfDocument
5904 // could set a new root (and event target is set by InstallEventListeners())
5905 // and we won't be able to remove them from the old event target then.
5906 RemoveEventListeners();
5907 mRootElement
= nsnull
;
5908 nsresult rv
= InstallEventListeners();
5909 NS_ENSURE_SUCCESS(rv
, );
5911 // We must have mRootElement now.
5912 nsCOMPtr
<nsIDOMElement
> root
;
5913 rv
= GetRootElement(getter_AddRefs(root
));
5914 NS_ENSURE_SUCCESS(rv
, );
5915 NS_ENSURE_TRUE(mRootElement
, );
5917 rv
= BeginningOfDocument();
5918 NS_ENSURE_SUCCESS(rv
, );
5920 // When this editor has focus, we need to reset the selection limiter to
5921 // new root. Otherwise, that is going to be done when this gets focus.
5922 nsCOMPtr
<nsINode
> node
= GetFocusedNode();
5923 nsCOMPtr
<nsIDOMEventTarget
> target
= do_QueryInterface(node
);
5925 InitializeSelection(target
);
5928 SyncRealTimeSpell();
5932 nsHTMLEditor::GetBodyElement(nsIDOMHTMLElement
** aBody
)
5934 NS_PRECONDITION(mDocWeak
, "bad state, null mDocWeak");
5935 nsCOMPtr
<nsIDOMHTMLDocument
> htmlDoc
= do_QueryReferent(mDocWeak
);
5937 return NS_ERROR_NOT_INITIALIZED
;
5939 nsCOMPtr
<nsIDOMHTMLElement
> bodyElement
;
5940 return htmlDoc
->GetBody(aBody
);
5943 already_AddRefed
<nsINode
>
5944 nsHTMLEditor::GetFocusedNode()
5946 nsCOMPtr
<nsIContent
> focusedContent
= GetFocusedContent();
5947 if (!focusedContent
) {
5951 nsIFocusManager
* fm
= nsFocusManager::GetFocusManager();
5952 NS_ASSERTION(fm
, "Focus manager is null");
5953 nsCOMPtr
<nsIDOMElement
> focusedElement
;
5954 fm
->GetFocusedElement(getter_AddRefs(focusedElement
));
5955 if (focusedElement
) {
5956 nsCOMPtr
<nsINode
> node
= do_QueryInterface(focusedElement
);
5957 return node
.forget();
5960 nsCOMPtr
<nsIDocument
> doc
= do_QueryReferent(mDocWeak
);
5961 nsCOMPtr
<nsINode
> node
= do_QueryInterface(doc
);
5962 return node
.forget();
5966 nsHTMLEditor::OurWindowHasFocus()
5968 NS_ENSURE_TRUE(mDocWeak
, PR_FALSE
);
5969 nsIFocusManager
* fm
= nsFocusManager::GetFocusManager();
5970 NS_ENSURE_TRUE(fm
, PR_FALSE
);
5971 nsCOMPtr
<nsIDOMWindow
> focusedWindow
;
5972 fm
->GetFocusedWindow(getter_AddRefs(focusedWindow
));
5973 if (!focusedWindow
) {
5976 nsCOMPtr
<nsIDocument
> doc
= do_QueryReferent(mDocWeak
);
5977 nsCOMPtr
<nsIDOMWindow
> ourWindow
= do_QueryInterface(doc
->GetWindow());
5978 return ourWindow
== focusedWindow
;
5982 nsHTMLEditor::IsAcceptableInputEvent(nsIDOMEvent
* aEvent
)
5984 if (!nsEditor::IsAcceptableInputEvent(aEvent
)) {
5988 NS_ENSURE_TRUE(mDocWeak
, PR_FALSE
);
5990 nsCOMPtr
<nsIDOMEventTarget
> target
;
5991 aEvent
->GetTarget(getter_AddRefs(target
));
5992 NS_ENSURE_TRUE(target
, PR_FALSE
);
5994 nsCOMPtr
<nsIDocument
> document
= do_QueryReferent(mDocWeak
);
5995 if (document
->HasFlag(NODE_IS_EDITABLE
)) {
5996 // If this editor is in designMode and the event target is the document,
5997 // the event is for this editor.
5998 nsCOMPtr
<nsIDocument
> targetDocument
= do_QueryInterface(target
);
5999 if (targetDocument
) {
6000 return targetDocument
== document
;
6002 // Otherwise, check whether the event target is in this document or not.
6003 nsCOMPtr
<nsIContent
> targetContent
= do_QueryInterface(target
);
6004 NS_ENSURE_TRUE(targetContent
, PR_FALSE
);
6005 return document
== targetContent
->GetCurrentDoc();
6008 // If this is for contenteditable, we should check whether the target is
6010 nsCOMPtr
<nsIContent
> targetContent
= do_QueryInterface(target
);
6011 NS_ENSURE_TRUE(targetContent
, PR_FALSE
);
6012 if (!targetContent
->HasFlag(NODE_IS_EDITABLE
) ||
6013 targetContent
->HasIndependentSelection()) {
6017 // Finally, check whether we're actually focused or not. When we're not
6018 // focused, we should ignore the dispatched event by script (or something)
6019 // because content editable element needs selection in itself for editing.
6020 // However, when we're not focused, it's not guaranteed.
6021 return IsActiveInDOMWindow();
6025 nsHTMLEditor::GetPreferredIMEState(PRUint32
*aState
)
6027 if (IsReadonly() || IsDisabled()) {
6028 *aState
= nsIContent::IME_STATUS_DISABLE
;
6032 // HTML editor don't prefer the CSS ime-mode because IE didn't do so too.
6033 *aState
= nsIContent::IME_STATUS_ENABLE
;