Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/DebugOnly.h"
7 #include "mozilla/EventStates.h"
8 #include "mozilla/TextEvents.h"
10 #include "nsCRT.h"
12 #include "nsUnicharUtils.h"
14 #include "nsHTMLEditor.h"
15 #include "nsHTMLEditRules.h"
16 #include "nsTextEditUtils.h"
17 #include "nsHTMLEditUtils.h"
19 #include "nsHTMLEditorEventListener.h"
20 #include "TypeInState.h"
22 #include "nsHTMLURIRefObject.h"
24 #include "nsIDOMText.h"
25 #include "nsIDOMMozNamedAttrMap.h"
26 #include "nsIDOMNodeList.h"
27 #include "nsIDOMDocument.h"
28 #include "nsIDOMAttr.h"
29 #include "nsIDocumentInlines.h"
30 #include "nsIDOMEventTarget.h"
31 #include "nsIDOMKeyEvent.h"
32 #include "nsIDOMMouseEvent.h"
33 #include "nsIDOMHTMLAnchorElement.h"
34 #include "nsISelectionController.h"
35 #include "nsIDOMHTMLDocument.h"
36 #include "nsILinkHandler.h"
37 #include "nsIInlineSpellChecker.h"
39 #include "mozilla/css/Loader.h"
40 #include "nsCSSStyleSheet.h"
41 #include "nsIDOMStyleSheet.h"
43 #include "nsIContent.h"
44 #include "nsIContentIterator.h"
45 #include "nsIDOMRange.h"
46 #include "nsISupportsArray.h"
47 #include "nsContentUtils.h"
48 #include "nsIDocumentEncoder.h"
49 #include "nsIDOMDocumentFragment.h"
50 #include "nsIPresShell.h"
51 #include "nsPresContext.h"
52 #include "SetDocTitleTxn.h"
53 #include "nsFocusManager.h"
54 #include "nsPIDOMWindow.h"
56 // netwerk
57 #include "nsIURI.h"
58 #include "nsNetUtil.h"
60 // Transactionas
61 #include "nsStyleSheetTxns.h"
63 // Misc
64 #include "TextEditorTest.h"
65 #include "nsEditorUtils.h"
66 #include "nsWSRunObject.h"
67 #include "nsGkAtoms.h"
68 #include "nsIWidget.h"
70 #include "nsIFrame.h"
71 #include "nsIParserService.h"
72 #include "mozilla/dom/Selection.h"
73 #include "mozilla/dom/Element.h"
74 #include "mozilla/dom/EventTarget.h"
75 #include "mozilla/dom/HTMLBodyElement.h"
76 #include "nsTextFragment.h"
78 using namespace mozilla;
79 using namespace mozilla::dom;
80 using namespace mozilla::widget;
82 // Some utilities to handle annoying overloading of "A" tag for link and named anchor
83 static char hrefText[] = "href";
84 static char anchorTxt[] = "anchor";
85 static char namedanchorText[] = "namedanchor";
87 #define IsLinkTag(s) (s.EqualsIgnoreCase(hrefText))
88 #define IsNamedAnchorTag(s) (s.EqualsIgnoreCase(anchorTxt) || s.EqualsIgnoreCase(namedanchorText))
90 nsHTMLEditor::nsHTMLEditor()
91 : nsPlaintextEditor()
92 , mCRInParagraphCreatesParagraph(false)
93 , mSelectedCellIndex(0)
94 , mIsObjectResizingEnabled(true)
95 , mIsResizing(false)
96 , mIsAbsolutelyPositioningEnabled(true)
97 , mResizedObjectIsAbsolutelyPositioned(false)
98 , mGrabberClicked(false)
99 , mIsMoving(false)
100 , mSnapToGridEnabled(false)
101 , mIsInlineTableEditingEnabled(true)
102 , mInfoXIncrement(20)
103 , mInfoYIncrement(20)
104 , mGridSize(0)
105 {
106 }
108 nsHTMLEditor::~nsHTMLEditor()
109 {
110 // remove the rules as an action listener. Else we get a bad
111 // ownership loop later on. it's ok if the rules aren't a listener;
112 // we ignore the error.
113 nsCOMPtr<nsIEditActionListener> mListener = do_QueryInterface(mRules);
114 RemoveEditActionListener(mListener);
116 //the autopointers will clear themselves up.
117 //but we need to also remove the listeners or we have a leak
118 nsCOMPtr<nsISelection>selection;
119 nsresult result = GetSelection(getter_AddRefs(selection));
120 // if we don't get the selection, just skip this
121 if (NS_SUCCEEDED(result) && selection)
122 {
123 nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
124 nsCOMPtr<nsISelectionListener>listener;
125 listener = do_QueryInterface(mTypeInState);
126 if (listener)
127 {
128 selPriv->RemoveSelectionListener(listener);
129 }
130 listener = do_QueryInterface(mSelectionListenerP);
131 if (listener)
132 {
133 selPriv->RemoveSelectionListener(listener);
134 }
135 }
137 mTypeInState = nullptr;
138 mSelectionListenerP = nullptr;
140 // free any default style propItems
141 RemoveAllDefaultProperties();
143 if (mLinkHandler && mDocWeak)
144 {
145 nsCOMPtr<nsIPresShell> ps = GetPresShell();
147 if (ps && ps->GetPresContext())
148 {
149 ps->GetPresContext()->SetLinkHandler(mLinkHandler);
150 }
151 }
153 RemoveEventListeners();
154 }
156 void
157 nsHTMLEditor::HideAnonymousEditingUIs()
158 {
159 if (mAbsolutelyPositionedObject)
160 HideGrabber();
161 if (mInlineEditedCell)
162 HideInlineTableEditingUI();
163 if (mResizedObject)
164 HideResizers();
165 }
167 NS_IMPL_CYCLE_COLLECTION_CLASS(nsHTMLEditor)
169 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsHTMLEditor, nsPlaintextEditor)
170 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTypeInState)
171 NS_IMPL_CYCLE_COLLECTION_UNLINK(mStyleSheets)
173 tmp->HideAnonymousEditingUIs();
174 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
176 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsHTMLEditor, nsPlaintextEditor)
177 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTypeInState)
178 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheets)
180 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopLeftHandle)
181 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopHandle)
182 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopRightHandle)
183 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLeftHandle)
184 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRightHandle)
185 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBottomLeftHandle)
186 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBottomHandle)
187 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBottomRightHandle)
188 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActivatedHandle)
189 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResizingShadow)
190 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResizingInfo)
191 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResizedObject)
192 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMouseMotionListenerP)
193 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectionListenerP)
194 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResizeEventListenerP)
195 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(objectResizeEventListeners)
197 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAbsolutelyPositionedObject)
198 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGrabber)
199 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPositioningShadow)
201 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInlineEditedCell)
202 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAddColumnBeforeButton)
203 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRemoveColumnButton)
204 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAddColumnAfterButton)
205 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAddRowBeforeButton)
206 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRemoveRowButton)
207 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAddRowAfterButton)
208 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
210 NS_IMPL_ADDREF_INHERITED(nsHTMLEditor, nsEditor)
211 NS_IMPL_RELEASE_INHERITED(nsHTMLEditor, nsEditor)
213 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsHTMLEditor)
214 NS_INTERFACE_MAP_ENTRY(nsIHTMLEditor)
215 NS_INTERFACE_MAP_ENTRY(nsIHTMLObjectResizer)
216 NS_INTERFACE_MAP_ENTRY(nsIHTMLAbsPosEditor)
217 NS_INTERFACE_MAP_ENTRY(nsIHTMLInlineTableEditor)
218 NS_INTERFACE_MAP_ENTRY(nsITableEditor)
219 NS_INTERFACE_MAP_ENTRY(nsIEditorStyleSheets)
220 NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver)
221 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
222 NS_INTERFACE_MAP_END_INHERITING(nsPlaintextEditor)
225 NS_IMETHODIMP
226 nsHTMLEditor::Init(nsIDOMDocument *aDoc,
227 nsIContent *aRoot,
228 nsISelectionController *aSelCon,
229 uint32_t aFlags,
230 const nsAString& aInitialValue)
231 {
232 NS_PRECONDITION(aDoc && !aSelCon, "bad arg");
233 NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER);
234 MOZ_ASSERT(aInitialValue.IsEmpty(), "Non-empty initial values not supported");
236 nsresult result = NS_OK, rulesRes = NS_OK;
238 if (1)
239 {
240 // block to scope nsAutoEditInitRulesTrigger
241 nsAutoEditInitRulesTrigger rulesTrigger(static_cast<nsPlaintextEditor*>(this), rulesRes);
243 // Init the plaintext editor
244 result = nsPlaintextEditor::Init(aDoc, aRoot, nullptr, aFlags, aInitialValue);
245 if (NS_FAILED(result)) { return result; }
247 // Init mutation observer
248 nsCOMPtr<nsINode> document = do_QueryInterface(aDoc);
249 document->AddMutationObserverUnlessExists(this);
251 // disable Composer-only features
252 if (IsMailEditor())
253 {
254 SetAbsolutePositioningEnabled(false);
255 SetSnapToGridEnabled(false);
256 }
258 // Init the HTML-CSS utils
259 mHTMLCSSUtils = new nsHTMLCSSUtils(this);
261 // disable links
262 nsCOMPtr<nsIPresShell> presShell = GetPresShell();
263 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
264 nsPresContext *context = presShell->GetPresContext();
265 NS_ENSURE_TRUE(context, NS_ERROR_NULL_POINTER);
266 if (!IsPlaintextEditor() && !IsInteractionAllowed()) {
267 mLinkHandler = context->GetLinkHandler();
269 context->SetLinkHandler(nullptr);
270 }
272 // init the type-in state
273 mTypeInState = new TypeInState();
275 // init the selection listener for image resizing
276 mSelectionListenerP = new ResizerSelectionListener(this);
278 if (!IsInteractionAllowed()) {
279 // ignore any errors from this in case the file is missing
280 AddOverrideStyleSheet(NS_LITERAL_STRING("resource://gre/res/EditorOverride.css"));
281 }
283 nsCOMPtr<nsISelection>selection;
284 result = GetSelection(getter_AddRefs(selection));
285 if (NS_FAILED(result)) { return result; }
286 if (selection)
287 {
288 nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
289 nsCOMPtr<nsISelectionListener>listener;
290 listener = do_QueryInterface(mTypeInState);
291 if (listener) {
292 selPriv->AddSelectionListener(listener);
293 }
294 listener = do_QueryInterface(mSelectionListenerP);
295 if (listener) {
296 selPriv->AddSelectionListener(listener);
297 }
298 }
299 }
301 NS_ENSURE_SUCCESS(rulesRes, rulesRes);
302 return result;
303 }
305 NS_IMETHODIMP
306 nsHTMLEditor::PreDestroy(bool aDestroyingFrames)
307 {
308 if (mDidPreDestroy) {
309 return NS_OK;
310 }
312 nsCOMPtr<nsINode> document = do_QueryReferent(mDocWeak);
313 if (document) {
314 document->RemoveMutationObserver(this);
315 }
317 while (mStyleSheetURLs.Length())
318 {
319 RemoveOverrideStyleSheet(mStyleSheetURLs[0]);
320 }
322 // Clean up after our anonymous content -- we don't want these nodes to
323 // stay around (which they would, since the frames have an owning reference).
324 HideAnonymousEditingUIs();
326 return nsPlaintextEditor::PreDestroy(aDestroyingFrames);
327 }
329 NS_IMETHODIMP
330 nsHTMLEditor::GetRootElement(nsIDOMElement **aRootElement)
331 {
332 NS_ENSURE_ARG_POINTER(aRootElement);
334 if (mRootElement) {
335 return nsEditor::GetRootElement(aRootElement);
336 }
338 *aRootElement = nullptr;
340 // Use the HTML documents body element as the editor root if we didn't
341 // get a root element during initialization.
343 nsCOMPtr<nsIDOMElement> rootElement;
344 nsCOMPtr<nsIDOMHTMLElement> bodyElement;
345 nsresult rv = GetBodyElement(getter_AddRefs(bodyElement));
346 NS_ENSURE_SUCCESS(rv, rv);
348 if (bodyElement) {
349 rootElement = bodyElement;
350 } else {
351 // If there is no HTML body element,
352 // we should use the document root element instead.
353 nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(mDocWeak);
354 NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
356 rv = doc->GetDocumentElement(getter_AddRefs(rootElement));
357 NS_ENSURE_SUCCESS(rv, rv);
358 // Document can have no elements
359 if (!rootElement) {
360 return NS_ERROR_NOT_AVAILABLE;
361 }
362 }
364 mRootElement = do_QueryInterface(rootElement);
365 rootElement.forget(aRootElement);
367 return NS_OK;
368 }
370 already_AddRefed<nsIContent>
371 nsHTMLEditor::FindSelectionRoot(nsINode *aNode)
372 {
373 NS_PRECONDITION(aNode->IsNodeOfType(nsINode::eDOCUMENT) ||
374 aNode->IsNodeOfType(nsINode::eCONTENT),
375 "aNode must be content or document node");
377 nsCOMPtr<nsIDocument> doc = aNode->GetCurrentDoc();
378 if (!doc) {
379 return nullptr;
380 }
382 nsCOMPtr<nsIContent> content;
383 if (doc->HasFlag(NODE_IS_EDITABLE) || !aNode->IsContent()) {
384 content = doc->GetRootElement();
385 return content.forget();
386 }
387 content = aNode->AsContent();
389 // XXX If we have readonly flag, shouldn't return the element which has
390 // contenteditable="true"? However, such case isn't there without chrome
391 // permission script.
392 if (IsReadonly()) {
393 // We still want to allow selection in a readonly editor.
394 content = do_QueryInterface(GetRoot());
395 return content.forget();
396 }
398 if (!content->HasFlag(NODE_IS_EDITABLE)) {
399 // If the content is in read-write state but is not editable itself,
400 // return it as the selection root.
401 if (content->IsElement() &&
402 content->AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE)) {
403 return content.forget();
404 }
405 return nullptr;
406 }
408 // For non-readonly editors we want to find the root of the editable subtree
409 // containing aContent.
410 content = content->GetEditingHost();
411 return content.forget();
412 }
414 /* virtual */
415 void
416 nsHTMLEditor::CreateEventListeners()
417 {
418 // Don't create the handler twice
419 if (!mEventListener) {
420 mEventListener = new nsHTMLEditorEventListener();
421 }
422 }
424 nsresult
425 nsHTMLEditor::InstallEventListeners()
426 {
427 NS_ENSURE_TRUE(mDocWeak && mEventListener,
428 NS_ERROR_NOT_INITIALIZED);
430 // NOTE: nsHTMLEditor doesn't need to initialize mEventTarget here because
431 // the target must be document node and it must be referenced as weak pointer.
433 nsHTMLEditorEventListener* listener =
434 reinterpret_cast<nsHTMLEditorEventListener*>(mEventListener.get());
435 return listener->Connect(this);
436 }
438 void
439 nsHTMLEditor::RemoveEventListeners()
440 {
441 if (!mDocWeak)
442 {
443 return;
444 }
446 nsCOMPtr<nsIDOMEventTarget> target = GetDOMEventTarget();
448 if (target)
449 {
450 // Both mMouseMotionListenerP and mResizeEventListenerP can be
451 // registerd with other targets than the DOM event receiver that
452 // we can reach from here. But nonetheless, unregister the event
453 // listeners with the DOM event reveiver (if it's registerd with
454 // other targets, it'll get unregisterd once the target goes
455 // away).
457 if (mMouseMotionListenerP)
458 {
459 // mMouseMotionListenerP might be registerd either as bubbling or
460 // capturing, unregister by both.
461 target->RemoveEventListener(NS_LITERAL_STRING("mousemove"),
462 mMouseMotionListenerP, false);
463 target->RemoveEventListener(NS_LITERAL_STRING("mousemove"),
464 mMouseMotionListenerP, true);
465 }
467 if (mResizeEventListenerP)
468 {
469 target->RemoveEventListener(NS_LITERAL_STRING("resize"),
470 mResizeEventListenerP, false);
471 }
472 }
474 mMouseMotionListenerP = nullptr;
475 mResizeEventListenerP = nullptr;
477 nsPlaintextEditor::RemoveEventListeners();
478 }
480 NS_IMETHODIMP
481 nsHTMLEditor::SetFlags(uint32_t aFlags)
482 {
483 nsresult rv = nsPlaintextEditor::SetFlags(aFlags);
484 NS_ENSURE_SUCCESS(rv, rv);
486 // Sets mCSSAware to correspond to aFlags. This toggles whether CSS is
487 // used to style elements in the editor. Note that the editor is only CSS
488 // aware by default in Composer and in the mail editor.
489 mCSSAware = !NoCSS() && !IsMailEditor();
491 return NS_OK;
492 }
494 NS_IMETHODIMP
495 nsHTMLEditor::InitRules()
496 {
497 if (!mRules) {
498 // instantiate the rules for the html editor
499 mRules = new nsHTMLEditRules();
500 }
501 return mRules->Init(static_cast<nsPlaintextEditor*>(this));
502 }
504 NS_IMETHODIMP
505 nsHTMLEditor::BeginningOfDocument()
506 {
507 if (!mDocWeak) { return NS_ERROR_NOT_INITIALIZED; }
509 // get the selection
510 nsCOMPtr<nsISelection> selection;
511 nsresult res = GetSelection(getter_AddRefs(selection));
512 NS_ENSURE_SUCCESS(res, res);
513 NS_ENSURE_TRUE(selection, NS_ERROR_NOT_INITIALIZED);
515 // Get the root element.
516 nsCOMPtr<nsIDOMElement> rootElement = do_QueryInterface(GetRoot());
517 if (!rootElement) {
518 NS_WARNING("GetRoot() returned a null pointer (mRootElement is null)");
519 return NS_OK;
520 }
522 // find first editable thingy
523 bool done = false;
524 nsCOMPtr<nsIDOMNode> curNode(rootElement), selNode;
525 int32_t curOffset = 0, selOffset;
526 while (!done)
527 {
528 nsWSRunObject wsObj(this, curNode, curOffset);
529 nsCOMPtr<nsIDOMNode> visNode;
530 int32_t visOffset=0;
531 WSType visType;
532 wsObj.NextVisibleNode(curNode, curOffset, address_of(visNode), &visOffset, &visType);
533 if (visType == WSType::normalWS || visType == WSType::text) {
534 selNode = visNode;
535 selOffset = visOffset;
536 done = true;
537 } else if (visType == WSType::br || visType == WSType::special) {
538 selNode = GetNodeLocation(visNode, &selOffset);
539 done = true;
540 } else if (visType == WSType::otherBlock) {
541 // By definition of nsWSRunObject, a block element terminates
542 // a whitespace run. That is, although we are calling a method
543 // that is named "NextVisibleNode", the node returned
544 // might not be visible/editable!
545 // If the given block does not contain any visible/editable items,
546 // we want to skip it and continue our search.
548 if (!IsContainer(visNode))
549 {
550 // However, we were given a block that is not a container.
551 // Since the block can not contain anything that's visible,
552 // such a block only makes sense if it is visible by itself,
553 // like a <hr>
554 // We want to place the caret in front of that block.
556 selNode = GetNodeLocation(visNode, &selOffset);
557 done = true;
558 }
559 else
560 {
561 bool isEmptyBlock;
562 if (NS_SUCCEEDED(IsEmptyNode(visNode, &isEmptyBlock)) &&
563 isEmptyBlock)
564 {
565 // skip the empty block
566 curNode = GetNodeLocation(visNode, &curOffset);
567 ++curOffset;
568 }
569 else
570 {
571 curNode = visNode;
572 curOffset = 0;
573 }
574 // keep looping
575 }
576 }
577 else
578 {
579 // else we found nothing useful
580 selNode = curNode;
581 selOffset = curOffset;
582 done = true;
583 }
584 }
585 return selection->Collapse(selNode, selOffset);
586 }
588 nsresult
589 nsHTMLEditor::HandleKeyPressEvent(nsIDOMKeyEvent* aKeyEvent)
590 {
591 // NOTE: When you change this method, you should also change:
592 // * editor/libeditor/html/tests/test_htmleditor_keyevent_handling.html
594 if (IsReadonly() || IsDisabled()) {
595 // When we're not editable, the events are handled on nsEditor, so, we can
596 // bypass nsPlaintextEditor.
597 return nsEditor::HandleKeyPressEvent(aKeyEvent);
598 }
600 WidgetKeyboardEvent* nativeKeyEvent =
601 aKeyEvent->GetInternalNSEvent()->AsKeyboardEvent();
602 NS_ENSURE_TRUE(nativeKeyEvent, NS_ERROR_UNEXPECTED);
603 NS_ASSERTION(nativeKeyEvent->message == NS_KEY_PRESS,
604 "HandleKeyPressEvent gets non-keypress event");
606 switch (nativeKeyEvent->keyCode) {
607 case nsIDOMKeyEvent::DOM_VK_META:
608 case nsIDOMKeyEvent::DOM_VK_WIN:
609 case nsIDOMKeyEvent::DOM_VK_SHIFT:
610 case nsIDOMKeyEvent::DOM_VK_CONTROL:
611 case nsIDOMKeyEvent::DOM_VK_ALT:
612 case nsIDOMKeyEvent::DOM_VK_BACK_SPACE:
613 case nsIDOMKeyEvent::DOM_VK_DELETE:
614 // These keys are handled on nsEditor, so, we can bypass
615 // nsPlaintextEditor.
616 return nsEditor::HandleKeyPressEvent(aKeyEvent);
617 case nsIDOMKeyEvent::DOM_VK_TAB: {
618 if (IsPlaintextEditor()) {
619 // If this works as plain text editor, e.g., mail editor for plain
620 // text, should be handled on nsPlaintextEditor.
621 return nsPlaintextEditor::HandleKeyPressEvent(aKeyEvent);
622 }
624 if (IsTabbable()) {
625 return NS_OK; // let it be used for focus switching
626 }
628 if (nativeKeyEvent->IsControl() || nativeKeyEvent->IsAlt() ||
629 nativeKeyEvent->IsMeta() || nativeKeyEvent->IsOS()) {
630 return NS_OK;
631 }
633 nsCOMPtr<nsISelection> selection;
634 nsresult rv = GetSelection(getter_AddRefs(selection));
635 NS_ENSURE_SUCCESS(rv, rv);
636 int32_t offset;
637 nsCOMPtr<nsIDOMNode> node, blockParent;
638 rv = GetStartNodeAndOffset(selection, getter_AddRefs(node), &offset);
639 NS_ENSURE_SUCCESS(rv, rv);
640 NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
642 bool isBlock = false;
643 NodeIsBlock(node, &isBlock);
644 if (isBlock) {
645 blockParent = node;
646 } else {
647 blockParent = GetBlockNodeParent(node);
648 }
650 if (!blockParent) {
651 break;
652 }
654 bool handled = false;
655 if (nsHTMLEditUtils::IsTableElement(blockParent)) {
656 rv = TabInTable(nativeKeyEvent->IsShift(), &handled);
657 if (handled) {
658 ScrollSelectionIntoView(false);
659 }
660 } else if (nsHTMLEditUtils::IsListItem(blockParent)) {
661 rv = Indent(nativeKeyEvent->IsShift() ?
662 NS_LITERAL_STRING("outdent") :
663 NS_LITERAL_STRING("indent"));
664 handled = true;
665 }
666 NS_ENSURE_SUCCESS(rv, rv);
667 if (handled) {
668 return aKeyEvent->PreventDefault(); // consumed
669 }
670 if (nativeKeyEvent->IsShift()) {
671 return NS_OK; // don't type text for shift tabs
672 }
673 aKeyEvent->PreventDefault();
674 return TypedText(NS_LITERAL_STRING("\t"), eTypedText);
675 }
676 case nsIDOMKeyEvent::DOM_VK_RETURN:
677 if (nativeKeyEvent->IsControl() || nativeKeyEvent->IsAlt() ||
678 nativeKeyEvent->IsMeta() || nativeKeyEvent->IsOS()) {
679 return NS_OK;
680 }
681 aKeyEvent->PreventDefault(); // consumed
682 if (nativeKeyEvent->IsShift() && !IsPlaintextEditor()) {
683 // only inserts a br node
684 return TypedText(EmptyString(), eTypedBR);
685 }
686 // uses rules to figure out what to insert
687 return TypedText(EmptyString(), eTypedBreak);
688 }
690 // NOTE: On some keyboard layout, some characters are inputted with Control
691 // key or Alt key, but at that time, widget sets FALSE to these keys.
692 if (nativeKeyEvent->charCode == 0 || nativeKeyEvent->IsControl() ||
693 nativeKeyEvent->IsAlt() || nativeKeyEvent->IsMeta() ||
694 nativeKeyEvent->IsOS()) {
695 // we don't PreventDefault() here or keybindings like control-x won't work
696 return NS_OK;
697 }
698 aKeyEvent->PreventDefault();
699 nsAutoString str(nativeKeyEvent->charCode);
700 return TypedText(str, eTypedText);
701 }
703 static void
704 AssertParserServiceIsCorrect(nsIAtom* aTag, bool aIsBlock)
705 {
706 #ifdef DEBUG
707 // Check this against what we would have said with the old code:
708 if (aTag==nsEditProperty::p ||
709 aTag==nsEditProperty::div ||
710 aTag==nsEditProperty::blockquote ||
711 aTag==nsEditProperty::h1 ||
712 aTag==nsEditProperty::h2 ||
713 aTag==nsEditProperty::h3 ||
714 aTag==nsEditProperty::h4 ||
715 aTag==nsEditProperty::h5 ||
716 aTag==nsEditProperty::h6 ||
717 aTag==nsEditProperty::ul ||
718 aTag==nsEditProperty::ol ||
719 aTag==nsEditProperty::dl ||
720 aTag==nsEditProperty::noscript ||
721 aTag==nsEditProperty::form ||
722 aTag==nsEditProperty::hr ||
723 aTag==nsEditProperty::table ||
724 aTag==nsEditProperty::fieldset ||
725 aTag==nsEditProperty::address ||
726 aTag==nsEditProperty::col ||
727 aTag==nsEditProperty::colgroup ||
728 aTag==nsEditProperty::li ||
729 aTag==nsEditProperty::dt ||
730 aTag==nsEditProperty::dd ||
731 aTag==nsEditProperty::legend )
732 {
733 if (!aIsBlock) {
734 nsAutoString assertmsg (NS_LITERAL_STRING("Parser and editor disagree on blockness: "));
736 nsAutoString tagName;
737 aTag->ToString(tagName);
738 assertmsg.Append(tagName);
739 char* assertstr = ToNewCString(assertmsg);
740 NS_ASSERTION(aIsBlock, assertstr);
741 NS_Free(assertstr);
742 }
743 }
744 #endif // DEBUG
745 }
747 /**
748 * Returns true if the id represents an element of block type.
749 * Can be used to determine if a new paragraph should be started.
750 */
751 bool
752 nsHTMLEditor::NodeIsBlockStatic(const dom::Element* aElement)
753 {
754 MOZ_ASSERT(aElement);
756 nsIAtom* tagAtom = aElement->Tag();
757 MOZ_ASSERT(tagAtom);
759 // Nodes we know we want to treat as block
760 // even though the parser says they're not:
761 if (tagAtom==nsEditProperty::body ||
762 tagAtom==nsEditProperty::head ||
763 tagAtom==nsEditProperty::tbody ||
764 tagAtom==nsEditProperty::thead ||
765 tagAtom==nsEditProperty::tfoot ||
766 tagAtom==nsEditProperty::tr ||
767 tagAtom==nsEditProperty::th ||
768 tagAtom==nsEditProperty::td ||
769 tagAtom==nsEditProperty::li ||
770 tagAtom==nsEditProperty::dt ||
771 tagAtom==nsEditProperty::dd ||
772 tagAtom==nsEditProperty::pre)
773 {
774 return true;
775 }
777 bool isBlock;
778 #ifdef DEBUG
779 // XXX we can't use DebugOnly here because VC++ is stupid (bug 802884)
780 nsresult rv =
781 #endif
782 nsContentUtils::GetParserService()->
783 IsBlock(nsContentUtils::GetParserService()->HTMLAtomTagToId(tagAtom),
784 isBlock);
785 MOZ_ASSERT(rv == NS_OK);
787 AssertParserServiceIsCorrect(tagAtom, isBlock);
789 return isBlock;
790 }
792 nsresult
793 nsHTMLEditor::NodeIsBlockStatic(nsIDOMNode *aNode, bool *aIsBlock)
794 {
795 if (!aNode || !aIsBlock) { return NS_ERROR_NULL_POINTER; }
797 nsCOMPtr<dom::Element> element = do_QueryInterface(aNode);
798 *aIsBlock = element && NodeIsBlockStatic(element);
799 return NS_OK;
800 }
802 NS_IMETHODIMP
803 nsHTMLEditor::NodeIsBlock(nsIDOMNode *aNode, bool *aIsBlock)
804 {
805 return NodeIsBlockStatic(aNode, aIsBlock);
806 }
808 bool
809 nsHTMLEditor::IsBlockNode(nsINode *aNode)
810 {
811 return aNode && aNode->IsElement() && NodeIsBlockStatic(aNode->AsElement());
812 }
814 // Non-static version for the nsIEditor interface and JavaScript
815 NS_IMETHODIMP
816 nsHTMLEditor::SetDocumentTitle(const nsAString &aTitle)
817 {
818 nsRefPtr<SetDocTitleTxn> txn = new SetDocTitleTxn();
819 NS_ENSURE_TRUE(txn, NS_ERROR_OUT_OF_MEMORY);
821 nsresult result = txn->Init(this, &aTitle);
822 NS_ENSURE_SUCCESS(result, result);
824 //Don't let Rules System change the selection
825 nsAutoTxnsConserveSelection dontChangeSelection(this);
826 return nsEditor::DoTransaction(txn);
827 }
829 /* ------------ Block methods moved from nsEditor -------------- */
830 ///////////////////////////////////////////////////////////////////////////
831 // GetBlockNodeParent: returns enclosing block level ancestor, if any
832 //
833 already_AddRefed<nsIDOMNode>
834 nsHTMLEditor::GetBlockNodeParent(nsIDOMNode *aNode)
835 {
836 if (!aNode)
837 {
838 NS_NOTREACHED("null node passed to GetBlockNodeParent()");
839 return nullptr;
840 }
842 nsCOMPtr<nsIDOMNode> p;
843 if (NS_FAILED(aNode->GetParentNode(getter_AddRefs(p)))) // no parent, ran off top of tree
844 return nullptr;
846 nsCOMPtr<nsIDOMNode> tmp;
847 while (p)
848 {
849 bool isBlock;
850 if (NS_FAILED(NodeIsBlockStatic(p, &isBlock)) || isBlock)
851 break;
852 if (NS_FAILED(p->GetParentNode(getter_AddRefs(tmp))) || !tmp) // no parent, ran off top of tree
853 break;
855 p = tmp;
856 }
857 return p.forget();
858 }
860 static const char16_t nbsp = 160;
862 ///////////////////////////////////////////////////////////////////////////////
863 // IsNextCharInNodeWhitespace: checks the adjacent content in the same node to
864 // see if following selection is whitespace or nbsp
865 void
866 nsHTMLEditor::IsNextCharInNodeWhitespace(nsIContent* aContent,
867 int32_t aOffset,
868 bool* outIsSpace,
869 bool* outIsNBSP,
870 nsIContent** outNode,
871 int32_t* outOffset)
872 {
873 MOZ_ASSERT(aContent && outIsSpace && outIsNBSP);
874 MOZ_ASSERT((outNode && outOffset) || (!outNode && !outOffset));
875 *outIsSpace = false;
876 *outIsNBSP = false;
877 if (outNode && outOffset) {
878 *outNode = nullptr;
879 *outOffset = -1;
880 }
882 if (aContent->IsNodeOfType(nsINode::eTEXT) &&
883 (uint32_t)aOffset < aContent->Length()) {
884 char16_t ch = aContent->GetText()->CharAt(aOffset);
885 *outIsSpace = nsCRT::IsAsciiSpace(ch);
886 *outIsNBSP = (ch == nbsp);
887 if (outNode && outOffset) {
888 NS_IF_ADDREF(*outNode = aContent);
889 // yes, this is _past_ the character
890 *outOffset = aOffset + 1;
891 }
892 }
893 }
896 ///////////////////////////////////////////////////////////////////////////////
897 // IsPrevCharInNodeWhitespace: checks the adjacent content in the same node to
898 // see if following selection is whitespace
899 void
900 nsHTMLEditor::IsPrevCharInNodeWhitespace(nsIContent* aContent,
901 int32_t aOffset,
902 bool* outIsSpace,
903 bool* outIsNBSP,
904 nsIContent** outNode,
905 int32_t* outOffset)
906 {
907 MOZ_ASSERT(aContent && outIsSpace && outIsNBSP);
908 MOZ_ASSERT((outNode && outOffset) || (!outNode && !outOffset));
909 *outIsSpace = false;
910 *outIsNBSP = false;
911 if (outNode && outOffset) {
912 *outNode = nullptr;
913 *outOffset = -1;
914 }
916 if (aContent->IsNodeOfType(nsINode::eTEXT) && aOffset > 0) {
917 char16_t ch = aContent->GetText()->CharAt(aOffset - 1);
918 *outIsSpace = nsCRT::IsAsciiSpace(ch);
919 *outIsNBSP = (ch == nbsp);
920 if (outNode && outOffset) {
921 NS_IF_ADDREF(*outNode = aContent);
922 *outOffset = aOffset - 1;
923 }
924 }
925 }
929 /* ------------ End Block methods -------------- */
932 bool nsHTMLEditor::IsVisBreak(nsIDOMNode *aNode)
933 {
934 NS_ENSURE_TRUE(aNode, false);
935 if (!nsTextEditUtils::IsBreak(aNode))
936 return false;
937 // check if there is a later node in block after br
938 nsCOMPtr<nsIDOMNode> priorNode, nextNode;
939 GetPriorHTMLNode(aNode, address_of(priorNode), true);
940 GetNextHTMLNode(aNode, address_of(nextNode), true);
941 // if we are next to another break, we are visible
942 if (priorNode && nsTextEditUtils::IsBreak(priorNode))
943 return true;
944 if (nextNode && nsTextEditUtils::IsBreak(nextNode))
945 return true;
947 // if we are right before block boundary, then br not visible
948 NS_ENSURE_TRUE(nextNode, false); // this break is trailer in block, it's not visible
949 if (IsBlockNode(nextNode))
950 return false; // break is right before a block, it's not visible
952 // sigh. We have to use expensive whitespace calculation code to
953 // determine what is going on
954 nsCOMPtr<nsIDOMNode> selNode, tmp;
955 int32_t selOffset;
956 selNode = GetNodeLocation(aNode, &selOffset);
957 selOffset++; // lets look after the break
958 nsWSRunObject wsObj(this, selNode, selOffset);
959 nsCOMPtr<nsIDOMNode> visNode;
960 int32_t visOffset=0;
961 WSType visType;
962 wsObj.NextVisibleNode(selNode, selOffset, address_of(visNode), &visOffset, &visType);
963 if (visType & WSType::block) {
964 return false;
965 }
967 return true;
968 }
970 NS_IMETHODIMP
971 nsHTMLEditor::BreakIsVisible(nsIDOMNode *aNode, bool *aIsVisible)
972 {
973 NS_ENSURE_ARG_POINTER(aNode && aIsVisible);
975 *aIsVisible = IsVisBreak(aNode);
977 return NS_OK;
978 }
981 NS_IMETHODIMP
982 nsHTMLEditor::GetIsDocumentEditable(bool *aIsDocumentEditable)
983 {
984 NS_ENSURE_ARG_POINTER(aIsDocumentEditable);
986 nsCOMPtr<nsIDOMDocument> doc = GetDOMDocument();
987 *aIsDocumentEditable = doc && IsModifiable();
989 return NS_OK;
990 }
992 bool nsHTMLEditor::IsModifiable()
993 {
994 return !IsReadonly();
995 }
997 NS_IMETHODIMP
998 nsHTMLEditor::UpdateBaseURL()
999 {
1000 nsCOMPtr<nsIDOMDocument> domDoc = GetDOMDocument();
1001 NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE);
1003 // Look for an HTML <base> tag
1004 nsCOMPtr<nsIDOMNodeList> nodeList;
1005 nsresult rv = domDoc->GetElementsByTagName(NS_LITERAL_STRING("base"), getter_AddRefs(nodeList));
1006 NS_ENSURE_SUCCESS(rv, rv);
1008 nsCOMPtr<nsIDOMNode> baseNode;
1009 if (nodeList)
1010 {
1011 uint32_t count;
1012 nodeList->GetLength(&count);
1013 if (count >= 1)
1014 {
1015 rv = nodeList->Item(0, getter_AddRefs(baseNode));
1016 NS_ENSURE_SUCCESS(rv, rv);
1017 }
1018 }
1019 // If no base tag, then set baseURL to the document's URL
1020 // This is very important, else relative URLs for links and images are wrong
1021 if (!baseNode)
1022 {
1023 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
1024 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
1026 return doc->SetBaseURI(doc->GetDocumentURI());
1027 }
1028 return NS_OK;
1029 }
1031 /* This routine is needed to provide a bottleneck for typing for logging
1032 purposes. Can't use HandleKeyPress() (above) for that since it takes
1033 a nsIDOMKeyEvent* parameter. So instead we pass enough info through
1034 to TypedText() to determine what action to take, but without passing
1035 an event.
1036 */
1037 NS_IMETHODIMP
1038 nsHTMLEditor::TypedText(const nsAString& aString, ETypingAction aAction)
1039 {
1040 nsAutoPlaceHolderBatch batch(this, nsGkAtoms::TypingTxnName);
1042 if (aAction == eTypedBR) {
1043 // only inserts a br node
1044 nsCOMPtr<nsIDOMNode> brNode;
1045 return InsertBR(address_of(brNode));
1046 }
1048 return nsPlaintextEditor::TypedText(aString, aAction);
1049 }
1051 NS_IMETHODIMP nsHTMLEditor::TabInTable(bool inIsShift, bool *outHandled)
1052 {
1053 NS_ENSURE_TRUE(outHandled, NS_ERROR_NULL_POINTER);
1054 *outHandled = false;
1056 // Find enclosing table cell from the selection (cell may be the selected element)
1057 nsCOMPtr<nsIDOMElement> cellElement;
1058 // can't use |NS_LITERAL_STRING| here until |GetElementOrParentByTagName| is fixed to accept readables
1059 nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr, getter_AddRefs(cellElement));
1060 NS_ENSURE_SUCCESS(res, res);
1061 // Do nothing -- we didn't find a table cell
1062 NS_ENSURE_TRUE(cellElement, NS_OK);
1064 // find enclosing table
1065 nsCOMPtr<nsIDOMNode> tbl = GetEnclosingTable(cellElement);
1066 NS_ENSURE_TRUE(tbl, res);
1068 // advance to next cell
1069 // first create an iterator over the table
1070 nsCOMPtr<nsIContentIterator> iter =
1071 do_CreateInstance("@mozilla.org/content/post-content-iterator;1", &res);
1072 NS_ENSURE_SUCCESS(res, res);
1073 NS_ENSURE_TRUE(iter, NS_ERROR_NULL_POINTER);
1074 nsCOMPtr<nsIContent> cTbl = do_QueryInterface(tbl);
1075 nsCOMPtr<nsIContent> cBlock = do_QueryInterface(cellElement);
1076 res = iter->Init(cTbl);
1077 NS_ENSURE_SUCCESS(res, res);
1078 // position iter at block
1079 res = iter->PositionAt(cBlock);
1080 NS_ENSURE_SUCCESS(res, res);
1082 nsCOMPtr<nsIDOMNode> node;
1083 do
1084 {
1085 if (inIsShift)
1086 iter->Prev();
1087 else
1088 iter->Next();
1090 node = do_QueryInterface(iter->GetCurrentNode());
1092 if (node && nsHTMLEditUtils::IsTableCell(node) &&
1093 GetEnclosingTable(node) == tbl)
1094 {
1095 res = CollapseSelectionToDeepestNonTableFirstChild(nullptr, node);
1096 NS_ENSURE_SUCCESS(res, res);
1097 *outHandled = true;
1098 return NS_OK;
1099 }
1100 } while (!iter->IsDone());
1102 if (!(*outHandled) && !inIsShift)
1103 {
1104 // if we havent handled it yet then we must have run off the end of
1105 // the table. Insert a new row.
1106 res = InsertTableRow(1, true);
1107 NS_ENSURE_SUCCESS(res, res);
1108 *outHandled = true;
1109 // put selection in right place
1110 // Use table code to get selection and index to new row...
1111 nsCOMPtr<nsISelection>selection;
1112 nsCOMPtr<nsIDOMElement> tblElement;
1113 nsCOMPtr<nsIDOMElement> cell;
1114 int32_t row;
1115 res = GetCellContext(getter_AddRefs(selection),
1116 getter_AddRefs(tblElement),
1117 getter_AddRefs(cell),
1118 nullptr, nullptr,
1119 &row, nullptr);
1120 NS_ENSURE_SUCCESS(res, res);
1121 // ...so that we can ask for first cell in that row...
1122 res = GetCellAt(tblElement, row, 0, getter_AddRefs(cell));
1123 NS_ENSURE_SUCCESS(res, res);
1124 // ...and then set selection there.
1125 // (Note that normally you should use CollapseSelectionToDeepestNonTableFirstChild(),
1126 // but we know cell is an empty new cell, so this works fine)
1127 node = do_QueryInterface(cell);
1128 if (node) selection->Collapse(node,0);
1129 return NS_OK;
1130 }
1132 return res;
1133 }
1135 NS_IMETHODIMP nsHTMLEditor::CreateBR(nsIDOMNode *aNode, int32_t aOffset, nsCOMPtr<nsIDOMNode> *outBRNode, EDirection aSelect)
1136 {
1137 nsCOMPtr<nsIDOMNode> parent = aNode;
1138 int32_t offset = aOffset;
1139 return CreateBRImpl(address_of(parent), &offset, outBRNode, aSelect);
1140 }
1142 nsresult
1143 nsHTMLEditor::CollapseSelectionToDeepestNonTableFirstChild(nsISelection *aSelection, nsIDOMNode *aNode)
1144 {
1145 NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
1146 nsresult res;
1148 nsCOMPtr<nsISelection> selection;
1149 if (aSelection)
1150 {
1151 selection = aSelection;
1152 } else {
1153 res = GetSelection(getter_AddRefs(selection));
1154 NS_ENSURE_SUCCESS(res, res);
1155 NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
1156 }
1157 nsCOMPtr<nsIDOMNode> node = aNode;
1158 nsCOMPtr<nsIDOMNode> child;
1160 do {
1161 node->GetFirstChild(getter_AddRefs(child));
1163 if (child)
1164 {
1165 // Stop if we find a table
1166 // don't want to go into nested tables
1167 if (nsHTMLEditUtils::IsTable(child)) break;
1168 // hey, it'g gotta be a container too!
1169 if (!IsContainer(child)) break;
1170 node = child;
1171 }
1172 }
1173 while (child);
1175 selection->Collapse(node,0);
1176 return NS_OK;
1177 }
1180 // This is mostly like InsertHTMLWithCharsetAndContext,
1181 // but we can't use that because it is selection-based and
1182 // the rules code won't let us edit under the <head> node
1183 NS_IMETHODIMP
1184 nsHTMLEditor::ReplaceHeadContentsWithHTML(const nsAString& aSourceToInsert)
1185 {
1186 nsAutoRules beginRulesSniffing(this, EditAction::ignore, nsIEditor::eNone); // don't do any post processing, rules get confused
1187 nsCOMPtr<nsISelection> selection;
1188 nsresult res = GetSelection(getter_AddRefs(selection));
1189 NS_ENSURE_SUCCESS(res, res);
1190 NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
1192 ForceCompositionEnd();
1194 // Do not use nsAutoRules -- rules code won't let us insert in <head>
1195 // Use the head node as a parent and delete/insert directly
1196 nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(mDocWeak);
1197 NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
1199 nsCOMPtr<nsIDOMNodeList>nodeList;
1200 res = doc->GetElementsByTagName(NS_LITERAL_STRING("head"), getter_AddRefs(nodeList));
1201 NS_ENSURE_SUCCESS(res, res);
1202 NS_ENSURE_TRUE(nodeList, NS_ERROR_NULL_POINTER);
1204 uint32_t count;
1205 nodeList->GetLength(&count);
1206 if (count < 1) return NS_ERROR_FAILURE;
1208 nsCOMPtr<nsIDOMNode> headNode;
1209 res = nodeList->Item(0, getter_AddRefs(headNode));
1210 NS_ENSURE_SUCCESS(res, res);
1211 NS_ENSURE_TRUE(headNode, NS_ERROR_NULL_POINTER);
1213 // First, make sure there are no return chars in the source.
1214 // Bad things happen if you insert returns (instead of dom newlines, \n)
1215 // into an editor document.
1216 nsAutoString inputString (aSourceToInsert); // hope this does copy-on-write
1218 // Windows linebreaks: Map CRLF to LF:
1219 inputString.ReplaceSubstring(MOZ_UTF16("\r\n"),
1220 MOZ_UTF16("\n"));
1222 // Mac linebreaks: Map any remaining CR to LF:
1223 inputString.ReplaceSubstring(MOZ_UTF16("\r"),
1224 MOZ_UTF16("\n"));
1226 nsAutoEditBatch beginBatching(this);
1228 res = GetSelection(getter_AddRefs(selection));
1229 NS_ENSURE_SUCCESS(res, res);
1230 NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
1232 // Get the first range in the selection, for context:
1233 nsCOMPtr<nsIDOMRange> range;
1234 res = selection->GetRangeAt(0, getter_AddRefs(range));
1235 NS_ENSURE_SUCCESS(res, res);
1237 nsCOMPtr<nsIDOMDocumentFragment> docfrag;
1238 res = range->CreateContextualFragment(inputString,
1239 getter_AddRefs(docfrag));
1241 //XXXX BUG 50965: This is not returning the text between <title> ... </title>
1242 // Special code is needed in JS to handle title anyway, so it really doesn't matter!
1244 if (NS_FAILED(res))
1245 {
1246 #ifdef DEBUG
1247 printf("Couldn't create contextual fragment: error was %X\n",
1248 static_cast<uint32_t>(res));
1249 #endif
1250 return res;
1251 }
1252 NS_ENSURE_TRUE(docfrag, NS_ERROR_NULL_POINTER);
1254 nsCOMPtr<nsIDOMNode> child;
1256 // First delete all children in head
1257 do {
1258 res = headNode->GetFirstChild(getter_AddRefs(child));
1259 NS_ENSURE_SUCCESS(res, res);
1260 if (child)
1261 {
1262 res = DeleteNode(child);
1263 NS_ENSURE_SUCCESS(res, res);
1264 }
1265 } while (child);
1267 // Now insert the new nodes
1268 int32_t offsetOfNewNode = 0;
1269 nsCOMPtr<nsIDOMNode> fragmentAsNode (do_QueryInterface(docfrag));
1271 // Loop over the contents of the fragment and move into the document
1272 do {
1273 res = fragmentAsNode->GetFirstChild(getter_AddRefs(child));
1274 NS_ENSURE_SUCCESS(res, res);
1275 if (child)
1276 {
1277 res = InsertNode(child, headNode, offsetOfNewNode++);
1278 NS_ENSURE_SUCCESS(res, res);
1279 }
1280 } while (child);
1282 return res;
1283 }
1285 NS_IMETHODIMP
1286 nsHTMLEditor::RebuildDocumentFromSource(const nsAString& aSourceString)
1287 {
1288 ForceCompositionEnd();
1290 nsCOMPtr<nsISelection>selection;
1291 nsresult res = GetSelection(getter_AddRefs(selection));
1292 NS_ENSURE_SUCCESS(res, res);
1294 nsCOMPtr<nsIDOMElement> bodyElement = do_QueryInterface(GetRoot());
1295 NS_ENSURE_TRUE(bodyElement, NS_ERROR_NULL_POINTER);
1297 // Find where the <body> tag starts.
1298 nsReadingIterator<char16_t> beginbody;
1299 nsReadingIterator<char16_t> endbody;
1300 aSourceString.BeginReading(beginbody);
1301 aSourceString.EndReading(endbody);
1302 bool foundbody = CaseInsensitiveFindInReadable(NS_LITERAL_STRING("<body"),
1303 beginbody, endbody);
1305 nsReadingIterator<char16_t> beginhead;
1306 nsReadingIterator<char16_t> endhead;
1307 aSourceString.BeginReading(beginhead);
1308 aSourceString.EndReading(endhead);
1309 bool foundhead = CaseInsensitiveFindInReadable(NS_LITERAL_STRING("<head"),
1310 beginhead, endhead);
1311 // a valid head appears before the body
1312 if (foundbody && beginhead.get() > beginbody.get())
1313 foundhead = false;
1315 nsReadingIterator<char16_t> beginclosehead;
1316 nsReadingIterator<char16_t> endclosehead;
1317 aSourceString.BeginReading(beginclosehead);
1318 aSourceString.EndReading(endclosehead);
1320 // Find the index after "<head>"
1321 bool foundclosehead = CaseInsensitiveFindInReadable(
1322 NS_LITERAL_STRING("</head>"), beginclosehead, endclosehead);
1323 // a valid close head appears after a found head
1324 if (foundhead && beginhead.get() > beginclosehead.get())
1325 foundclosehead = false;
1326 // a valid close head appears before a found body
1327 if (foundbody && beginclosehead.get() > beginbody.get())
1328 foundclosehead = false;
1330 // Time to change the document
1331 nsAutoEditBatch beginBatching(this);
1333 nsReadingIterator<char16_t> endtotal;
1334 aSourceString.EndReading(endtotal);
1336 if (foundhead) {
1337 if (foundclosehead)
1338 res = ReplaceHeadContentsWithHTML(Substring(beginhead, beginclosehead));
1339 else if (foundbody)
1340 res = ReplaceHeadContentsWithHTML(Substring(beginhead, beginbody));
1341 else
1342 // XXX Without recourse to some parser/content sink/docshell hackery
1343 // we don't really know where the head ends and the body begins
1344 // so we assume that there is no body
1345 res = ReplaceHeadContentsWithHTML(Substring(beginhead, endtotal));
1346 } else {
1347 nsReadingIterator<char16_t> begintotal;
1348 aSourceString.BeginReading(begintotal);
1349 NS_NAMED_LITERAL_STRING(head, "<head>");
1350 if (foundclosehead)
1351 res = ReplaceHeadContentsWithHTML(head + Substring(begintotal, beginclosehead));
1352 else if (foundbody)
1353 res = ReplaceHeadContentsWithHTML(head + Substring(begintotal, beginbody));
1354 else
1355 // XXX Without recourse to some parser/content sink/docshell hackery
1356 // we don't really know where the head ends and the body begins
1357 // so we assume that there is no head
1358 res = ReplaceHeadContentsWithHTML(head);
1359 }
1360 NS_ENSURE_SUCCESS(res, res);
1362 res = SelectAll();
1363 NS_ENSURE_SUCCESS(res, res);
1365 if (!foundbody) {
1366 NS_NAMED_LITERAL_STRING(body, "<body>");
1367 // XXX Without recourse to some parser/content sink/docshell hackery
1368 // we don't really know where the head ends and the body begins
1369 if (foundclosehead) // assume body starts after the head ends
1370 res = LoadHTML(body + Substring(endclosehead, endtotal));
1371 else if (foundhead) // assume there is no body
1372 res = LoadHTML(body);
1373 else // assume there is no head, the entire source is body
1374 res = LoadHTML(body + aSourceString);
1375 NS_ENSURE_SUCCESS(res, res);
1377 nsCOMPtr<nsIDOMElement> divElement;
1378 res = CreateElementWithDefaults(NS_LITERAL_STRING("div"), getter_AddRefs(divElement));
1379 NS_ENSURE_SUCCESS(res, res);
1381 res = CloneAttributes(bodyElement, divElement);
1382 NS_ENSURE_SUCCESS(res, res);
1384 return BeginningOfDocument();
1385 }
1387 res = LoadHTML(Substring(beginbody, endtotal));
1388 NS_ENSURE_SUCCESS(res, res);
1390 // Now we must copy attributes user might have edited on the <body> tag
1391 // because InsertHTML (actually, CreateContextualFragment())
1392 // will never return a body node in the DOM fragment
1394 // We already know where "<body" begins
1395 nsReadingIterator<char16_t> beginclosebody = beginbody;
1396 nsReadingIterator<char16_t> endclosebody;
1397 aSourceString.EndReading(endclosebody);
1398 if (!FindInReadable(NS_LITERAL_STRING(">"),beginclosebody,endclosebody))
1399 return NS_ERROR_FAILURE;
1401 // Truncate at the end of the body tag
1402 // Kludge of the year: fool the parser by replacing "body" with "div" so we get a node
1403 nsAutoString bodyTag;
1404 bodyTag.AssignLiteral("<div ");
1405 bodyTag.Append(Substring(endbody, endclosebody));
1407 nsCOMPtr<nsIDOMRange> range;
1408 res = selection->GetRangeAt(0, getter_AddRefs(range));
1409 NS_ENSURE_SUCCESS(res, res);
1411 nsCOMPtr<nsIDOMDocumentFragment> docfrag;
1412 res = range->CreateContextualFragment(bodyTag, getter_AddRefs(docfrag));
1413 NS_ENSURE_SUCCESS(res, res);
1415 nsCOMPtr<nsIDOMNode> fragmentAsNode (do_QueryInterface(docfrag));
1416 NS_ENSURE_TRUE(fragmentAsNode, NS_ERROR_NULL_POINTER);
1418 nsCOMPtr<nsIDOMNode> child;
1419 res = fragmentAsNode->GetFirstChild(getter_AddRefs(child));
1420 NS_ENSURE_SUCCESS(res, res);
1421 NS_ENSURE_TRUE(child, NS_ERROR_NULL_POINTER);
1423 // Copy all attributes from the div child to current body element
1424 res = CloneAttributes(bodyElement, child);
1425 NS_ENSURE_SUCCESS(res, res);
1427 // place selection at first editable content
1428 return BeginningOfDocument();
1429 }
1431 void
1432 nsHTMLEditor::NormalizeEOLInsertPosition(nsIDOMNode *firstNodeToInsert,
1433 nsCOMPtr<nsIDOMNode> *insertParentNode,
1434 int32_t *insertOffset)
1435 {
1436 /*
1437 This function will either correct the position passed in,
1438 or leave the position unchanged.
1440 When the (first) item to insert is a block level element,
1441 and our insertion position is after the last visible item in a line,
1442 i.e. the insertion position is just before a visible line break <br>,
1443 we want to skip to the position just after the line break (see bug 68767)
1445 However, our logic to detect whether we should skip or not
1446 needs to be more clever.
1447 We must not skip when the caret appears to be positioned at the beginning
1448 of a block, in that case skipping the <br> would not insert the <br>
1449 at the caret position, but after the current empty line.
1451 So we have several cases to test:
1453 1) We only ever want to skip, if the next visible thing after the current position is a break
1455 2) We do not want to skip if there is no previous visible thing at all
1456 That is detected if the call to PriorVisibleNode gives us an offset of zero.
1457 Because PriorVisibleNode always positions after the prior node, we would
1458 see an offset > 0, if there were a prior node.
1460 3) We do not want to skip, if both the next and the previous visible things are breaks.
1462 4) We do not want to skip if the previous visible thing is in a different block
1463 than the insertion position.
1464 */
1466 if (!IsBlockNode(firstNodeToInsert))
1467 return;
1469 nsWSRunObject wsObj(this, *insertParentNode, *insertOffset);
1470 nsCOMPtr<nsIDOMNode> nextVisNode;
1471 nsCOMPtr<nsIDOMNode> prevVisNode;
1472 int32_t nextVisOffset=0;
1473 WSType nextVisType;
1474 int32_t prevVisOffset=0;
1475 WSType prevVisType;
1477 wsObj.NextVisibleNode(*insertParentNode, *insertOffset, address_of(nextVisNode), &nextVisOffset, &nextVisType);
1478 if (!nextVisNode)
1479 return;
1481 if (!(nextVisType & WSType::br)) {
1482 return;
1483 }
1485 wsObj.PriorVisibleNode(*insertParentNode, *insertOffset, address_of(prevVisNode), &prevVisOffset, &prevVisType);
1486 if (!prevVisNode)
1487 return;
1489 if (prevVisType & WSType::br) {
1490 return;
1491 }
1493 if (prevVisType & WSType::thisBlock) {
1494 return;
1495 }
1497 int32_t brOffset=0;
1498 nsCOMPtr<nsIDOMNode> brNode = GetNodeLocation(nextVisNode, &brOffset);
1500 *insertParentNode = brNode;
1501 *insertOffset = brOffset + 1;
1502 }
1504 NS_IMETHODIMP
1505 nsHTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement, bool aDeleteSelection)
1506 {
1507 // Protect the edit rules object from dying
1508 nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
1510 nsresult res = NS_ERROR_NOT_INITIALIZED;
1512 NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
1514 nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aElement);
1516 ForceCompositionEnd();
1517 nsAutoEditBatch beginBatching(this);
1518 nsAutoRules beginRulesSniffing(this, EditAction::insertElement, nsIEditor::eNext);
1520 nsRefPtr<Selection> selection = GetSelection();
1521 if (!selection) {
1522 return NS_ERROR_FAILURE;
1523 }
1525 // hand off to the rules system, see if it has anything to say about this
1526 bool cancel, handled;
1527 nsTextRulesInfo ruleInfo(EditAction::insertElement);
1528 ruleInfo.insertElement = aElement;
1529 res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
1530 if (cancel || (NS_FAILED(res))) return res;
1532 if (!handled)
1533 {
1534 if (aDeleteSelection)
1535 {
1536 if (!IsBlockNode(aElement)) {
1537 // E.g., inserting an image. In this case we don't need to delete any
1538 // inline wrappers before we do the insertion. Otherwise we let
1539 // DeleteSelectionAndPrepareToCreateNode do the deletion for us, which
1540 // calls DeleteSelection with aStripWrappers = eStrip.
1541 res = DeleteSelection(nsIEditor::eNone, nsIEditor::eNoStrip);
1542 NS_ENSURE_SUCCESS(res, res);
1543 }
1545 nsresult result = DeleteSelectionAndPrepareToCreateNode();
1546 NS_ENSURE_SUCCESS(result, result);
1547 }
1549 // If deleting, selection will be collapsed.
1550 // so if not, we collapse it
1551 if (!aDeleteSelection)
1552 {
1553 // Named Anchor is a special case,
1554 // We collapse to insert element BEFORE the selection
1555 // For all other tags, we insert AFTER the selection
1556 if (nsHTMLEditUtils::IsNamedAnchor(node))
1557 {
1558 selection->CollapseToStart();
1559 } else {
1560 selection->CollapseToEnd();
1561 }
1562 }
1564 nsCOMPtr<nsIDOMNode> parentSelectedNode;
1565 int32_t offsetForInsert;
1566 res = selection->GetAnchorNode(getter_AddRefs(parentSelectedNode));
1567 // XXX: ERROR_HANDLING bad XPCOM usage
1568 if (NS_SUCCEEDED(res) && NS_SUCCEEDED(selection->GetAnchorOffset(&offsetForInsert)) && parentSelectedNode)
1569 {
1570 #ifdef DEBUG_cmanske
1571 {
1572 nsAutoString name;
1573 parentSelectedNode->GetNodeName(name);
1574 printf("InsertElement: Anchor node of selection: ");
1575 wprintf(name.get());
1576 printf(" Offset: %d\n", offsetForInsert);
1577 }
1578 #endif
1580 // Adjust position based on the node we are going to insert.
1581 NormalizeEOLInsertPosition(node, address_of(parentSelectedNode), &offsetForInsert);
1583 res = InsertNodeAtPoint(node, address_of(parentSelectedNode), &offsetForInsert, false);
1584 NS_ENSURE_SUCCESS(res, res);
1585 // Set caret after element, but check for special case
1586 // of inserting table-related elements: set in first cell instead
1587 if (!SetCaretInTableCell(aElement))
1588 {
1589 res = SetCaretAfterElement(aElement);
1590 NS_ENSURE_SUCCESS(res, res);
1591 }
1592 // check for inserting a whole table at the end of a block. If so insert a br after it.
1593 if (nsHTMLEditUtils::IsTable(node))
1594 {
1595 bool isLast;
1596 res = IsLastEditableChild(node, &isLast);
1597 NS_ENSURE_SUCCESS(res, res);
1598 if (isLast)
1599 {
1600 nsCOMPtr<nsIDOMNode> brNode;
1601 res = CreateBR(parentSelectedNode, offsetForInsert+1, address_of(brNode));
1602 NS_ENSURE_SUCCESS(res, res);
1603 selection->Collapse(parentSelectedNode, offsetForInsert+1);
1604 }
1605 }
1606 }
1607 }
1608 res = mRules->DidDoAction(selection, &ruleInfo, res);
1609 return res;
1610 }
1613 /*
1614 InsertNodeAtPoint: attempts to insert aNode into the document, at a point specified by
1615 {*ioParent,*ioOffset}. Checks with strict dtd to see if containment is allowed. If not
1616 allowed, will attempt to find a parent in the parent hierarchy of *ioParent that will
1617 accept aNode as a child. If such a parent is found, will split the document tree from
1618 {*ioParent,*ioOffset} up to parent, and then insert aNode. ioParent & ioOffset are then
1619 adjusted to point to the actual location that aNode was inserted at. aNoEmptyNodes
1620 specifies if the splitting process is allowed to reslt in empty nodes.
1621 nsIDOMNode *aNode node to insert
1622 nsCOMPtr<nsIDOMNode> *ioParent insertion parent
1623 int32_t *ioOffset insertion offset
1624 bool aNoEmptyNodes splitting can result in empty nodes?
1625 */
1626 nsresult
1627 nsHTMLEditor::InsertNodeAtPoint(nsIDOMNode *aNode,
1628 nsCOMPtr<nsIDOMNode> *ioParent,
1629 int32_t *ioOffset,
1630 bool aNoEmptyNodes)
1631 {
1632 NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
1633 NS_ENSURE_TRUE(ioParent, NS_ERROR_NULL_POINTER);
1634 NS_ENSURE_TRUE(*ioParent, NS_ERROR_NULL_POINTER);
1635 NS_ENSURE_TRUE(ioOffset, NS_ERROR_NULL_POINTER);
1637 nsresult res = NS_OK;
1638 nsCOMPtr<nsIDOMNode> parent = *ioParent;
1639 nsCOMPtr<nsIDOMNode> topChild = *ioParent;
1640 nsCOMPtr<nsIDOMNode> tmp;
1641 int32_t offsetOfInsert = *ioOffset;
1643 // Search up the parent chain to find a suitable container
1644 while (!CanContain(parent, aNode)) {
1645 // If the current parent is a root (body or table element)
1646 // then go no further - we can't insert
1647 if (nsTextEditUtils::IsBody(parent) || nsHTMLEditUtils::IsTableElement(parent))
1648 return NS_ERROR_FAILURE;
1649 // Get the next parent
1650 parent->GetParentNode(getter_AddRefs(tmp));
1651 NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE);
1652 topChild = parent;
1653 parent = tmp;
1654 }
1655 if (parent != topChild)
1656 {
1657 // we need to split some levels above the original selection parent
1658 res = SplitNodeDeep(topChild, *ioParent, *ioOffset, &offsetOfInsert, aNoEmptyNodes);
1659 NS_ENSURE_SUCCESS(res, res);
1660 *ioParent = parent;
1661 *ioOffset = offsetOfInsert;
1662 }
1663 // Now we can insert the new node
1664 res = InsertNode(aNode, parent, offsetOfInsert);
1665 return res;
1666 }
1668 NS_IMETHODIMP
1669 nsHTMLEditor::SelectElement(nsIDOMElement* aElement)
1670 {
1671 nsresult res = NS_ERROR_NULL_POINTER;
1673 // Must be sure that element is contained in the document body
1674 if (IsDescendantOfEditorRoot(aElement)) {
1675 nsCOMPtr<nsISelection> selection;
1676 res = GetSelection(getter_AddRefs(selection));
1677 NS_ENSURE_SUCCESS(res, res);
1678 NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
1679 nsCOMPtr<nsIDOMNode>parent;
1680 res = aElement->GetParentNode(getter_AddRefs(parent));
1681 if (NS_SUCCEEDED(res) && parent)
1682 {
1683 int32_t offsetInParent = GetChildOffset(aElement, parent);
1685 // Collapse selection to just before desired element,
1686 res = selection->Collapse(parent, offsetInParent);
1687 if (NS_SUCCEEDED(res)) {
1688 // then extend it to just after
1689 res = selection->Extend(parent, offsetInParent + 1);
1690 }
1691 }
1692 }
1693 return res;
1694 }
1696 NS_IMETHODIMP
1697 nsHTMLEditor::SetCaretAfterElement(nsIDOMElement* aElement)
1698 {
1699 nsresult res = NS_ERROR_NULL_POINTER;
1701 // Be sure the element is contained in the document body
1702 if (aElement && IsDescendantOfEditorRoot(aElement)) {
1703 nsCOMPtr<nsISelection> selection;
1704 res = GetSelection(getter_AddRefs(selection));
1705 NS_ENSURE_SUCCESS(res, res);
1706 NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
1707 nsCOMPtr<nsIDOMNode>parent;
1708 res = aElement->GetParentNode(getter_AddRefs(parent));
1709 NS_ENSURE_SUCCESS(res, res);
1710 NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
1711 int32_t offsetInParent = GetChildOffset(aElement, parent);
1712 // Collapse selection to just after desired element,
1713 res = selection->Collapse(parent, offsetInParent + 1);
1714 }
1715 return res;
1716 }
1718 NS_IMETHODIMP
1719 nsHTMLEditor::SetParagraphFormat(const nsAString& aParagraphFormat)
1720 {
1721 nsAutoString tag; tag.Assign(aParagraphFormat);
1722 ToLowerCase(tag);
1723 if (tag.EqualsLiteral("dd") || tag.EqualsLiteral("dt"))
1724 return MakeDefinitionItem(tag);
1725 else
1726 return InsertBasicBlock(tag);
1727 }
1729 NS_IMETHODIMP
1730 nsHTMLEditor::GetParagraphState(bool *aMixed, nsAString &outFormat)
1731 {
1732 if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
1733 NS_ENSURE_TRUE(aMixed, NS_ERROR_NULL_POINTER);
1734 nsRefPtr<nsHTMLEditRules> htmlRules = static_cast<nsHTMLEditRules*>(mRules.get());
1736 return htmlRules->GetParagraphState(aMixed, outFormat);
1737 }
1739 NS_IMETHODIMP
1740 nsHTMLEditor::GetBackgroundColorState(bool *aMixed, nsAString &aOutColor)
1741 {
1742 nsresult res;
1743 if (IsCSSEnabled()) {
1744 // if we are in CSS mode, we have to check if the containing block defines
1745 // a background color
1746 res = GetCSSBackgroundColorState(aMixed, aOutColor, true);
1747 }
1748 else {
1749 // in HTML mode, we look only at page's background
1750 res = GetHTMLBackgroundColorState(aMixed, aOutColor);
1751 }
1752 return res;
1753 }
1755 NS_IMETHODIMP
1756 nsHTMLEditor::GetHighlightColorState(bool *aMixed, nsAString &aOutColor)
1757 {
1758 nsresult res = NS_OK;
1759 *aMixed = false;
1760 aOutColor.AssignLiteral("transparent");
1761 if (IsCSSEnabled()) {
1762 // in CSS mode, text background can be added by the Text Highlight button
1763 // we need to query the background of the selection without looking for
1764 // the block container of the ranges in the selection
1765 res = GetCSSBackgroundColorState(aMixed, aOutColor, false);
1766 }
1767 return res;
1768 }
1770 nsresult
1771 nsHTMLEditor::GetCSSBackgroundColorState(bool *aMixed, nsAString &aOutColor, bool aBlockLevel)
1772 {
1773 NS_ENSURE_TRUE(aMixed, NS_ERROR_NULL_POINTER);
1774 *aMixed = false;
1775 // the default background color is transparent
1776 aOutColor.AssignLiteral("transparent");
1778 // get selection
1779 nsCOMPtr<nsISelection>selection;
1780 nsresult res = GetSelection(getter_AddRefs(selection));
1781 NS_ENSURE_SUCCESS(res, res);
1783 // get selection location
1784 nsCOMPtr<nsIDOMNode> parent;
1785 int32_t offset;
1786 res = GetStartNodeAndOffset(selection, getter_AddRefs(parent), &offset);
1787 NS_ENSURE_SUCCESS(res, res);
1788 NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
1790 // is the selection collapsed?
1791 nsCOMPtr<nsIDOMNode> nodeToExamine;
1792 if (selection->Collapsed() || IsTextNode(parent)) {
1793 // we want to look at the parent and ancestors
1794 nodeToExamine = parent;
1795 } else {
1796 // otherwise we want to look at the first editable node after
1797 // {parent,offset} and its ancestors for divs with alignment on them
1798 nodeToExamine = GetChildAt(parent, offset);
1799 //GetNextNode(parent, offset, true, address_of(nodeToExamine));
1800 }
1802 NS_ENSURE_TRUE(nodeToExamine, NS_ERROR_NULL_POINTER);
1804 // is the node to examine a block ?
1805 bool isBlock;
1806 res = NodeIsBlockStatic(nodeToExamine, &isBlock);
1807 NS_ENSURE_SUCCESS(res, res);
1809 nsCOMPtr<nsIDOMNode> tmp;
1811 if (aBlockLevel) {
1812 // we are querying the block background (and not the text background), let's
1813 // climb to the block container
1814 nsCOMPtr<nsIDOMNode> blockParent = nodeToExamine;
1815 if (!isBlock) {
1816 blockParent = GetBlockNodeParent(nodeToExamine);
1817 NS_ENSURE_TRUE(blockParent, NS_OK);
1818 }
1820 // Make sure to not walk off onto the Document node
1821 nsCOMPtr<nsIDOMElement> element;
1822 do {
1823 // retrieve the computed style of background-color for blockParent
1824 mHTMLCSSUtils->GetComputedProperty(blockParent,
1825 nsEditProperty::cssBackgroundColor,
1826 aOutColor);
1827 tmp.swap(blockParent);
1828 res = tmp->GetParentNode(getter_AddRefs(blockParent));
1829 element = do_QueryInterface(blockParent);
1830 // look at parent if the queried color is transparent and if the node to
1831 // examine is not the root of the document
1832 } while (aOutColor.EqualsLiteral("transparent") && element);
1833 if (aOutColor.EqualsLiteral("transparent")) {
1834 // we have hit the root of the document and the color is still transparent !
1835 // Grumble... Let's look at the default background color because that's the
1836 // color we are looking for
1837 mHTMLCSSUtils->GetDefaultBackgroundColor(aOutColor);
1838 }
1839 }
1840 else {
1841 // no, we are querying the text background for the Text Highlight button
1842 if (IsTextNode(nodeToExamine)) {
1843 // if the node of interest is a text node, let's climb a level
1844 res = nodeToExamine->GetParentNode(getter_AddRefs(parent));
1845 NS_ENSURE_SUCCESS(res, res);
1846 nodeToExamine = parent;
1847 }
1848 do {
1849 // is the node to examine a block ?
1850 res = NodeIsBlockStatic(nodeToExamine, &isBlock);
1851 NS_ENSURE_SUCCESS(res, res);
1852 if (isBlock) {
1853 // yes it is a block; in that case, the text background color is transparent
1854 aOutColor.AssignLiteral("transparent");
1855 break;
1856 }
1857 else {
1858 // no, it's not; let's retrieve the computed style of background-color for the
1859 // node to examine
1860 mHTMLCSSUtils->GetComputedProperty(nodeToExamine, nsEditProperty::cssBackgroundColor,
1861 aOutColor);
1862 if (!aOutColor.EqualsLiteral("transparent")) {
1863 break;
1864 }
1865 }
1866 tmp.swap(nodeToExamine);
1867 res = tmp->GetParentNode(getter_AddRefs(nodeToExamine));
1868 NS_ENSURE_SUCCESS(res, res);
1869 } while ( aOutColor.EqualsLiteral("transparent") && nodeToExamine );
1870 }
1871 return NS_OK;
1872 }
1874 NS_IMETHODIMP
1875 nsHTMLEditor::GetHTMLBackgroundColorState(bool *aMixed, nsAString &aOutColor)
1876 {
1877 //TODO: We don't handle "mixed" correctly!
1878 NS_ENSURE_TRUE(aMixed, NS_ERROR_NULL_POINTER);
1879 *aMixed = false;
1880 aOutColor.Truncate();
1882 nsCOMPtr<nsIDOMElement> domElement;
1883 int32_t selectedCount;
1884 nsAutoString tagName;
1885 nsresult res = GetSelectedOrParentTableElement(tagName,
1886 &selectedCount,
1887 getter_AddRefs(domElement));
1888 NS_ENSURE_SUCCESS(res, res);
1890 nsCOMPtr<dom::Element> element = do_QueryInterface(domElement);
1892 while (element) {
1893 // We are in a cell or selected table
1894 element->GetAttr(kNameSpaceID_None, nsGkAtoms::bgcolor, aOutColor);
1896 // Done if we have a color explicitly set
1897 if (!aOutColor.IsEmpty()) {
1898 return NS_OK;
1899 }
1901 // Once we hit the body, we're done
1902 if (element->IsHTML(nsGkAtoms::body)) {
1903 return NS_OK;
1904 }
1906 // No color is set, but we need to report visible color inherited
1907 // from nested cells/tables, so search up parent chain
1908 element = element->GetParentElement();
1909 }
1911 // If no table or cell found, get page body
1912 dom::Element* bodyElement = GetRoot();
1913 NS_ENSURE_TRUE(bodyElement, NS_ERROR_NULL_POINTER);
1915 bodyElement->GetAttr(kNameSpaceID_None, nsGkAtoms::bgcolor, aOutColor);
1916 return NS_OK;
1917 }
1919 NS_IMETHODIMP
1920 nsHTMLEditor::GetListState(bool *aMixed, bool *aOL, bool *aUL, bool *aDL)
1921 {
1922 if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
1923 NS_ENSURE_TRUE(aMixed && aOL && aUL && aDL, NS_ERROR_NULL_POINTER);
1924 nsRefPtr<nsHTMLEditRules> htmlRules = static_cast<nsHTMLEditRules*>(mRules.get());
1926 return htmlRules->GetListState(aMixed, aOL, aUL, aDL);
1927 }
1929 NS_IMETHODIMP
1930 nsHTMLEditor::GetListItemState(bool *aMixed, bool *aLI, bool *aDT, bool *aDD)
1931 {
1932 if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
1933 NS_ENSURE_TRUE(aMixed && aLI && aDT && aDD, NS_ERROR_NULL_POINTER);
1935 nsRefPtr<nsHTMLEditRules> htmlRules = static_cast<nsHTMLEditRules*>(mRules.get());
1937 return htmlRules->GetListItemState(aMixed, aLI, aDT, aDD);
1938 }
1940 NS_IMETHODIMP
1941 nsHTMLEditor::GetAlignment(bool *aMixed, nsIHTMLEditor::EAlignment *aAlign)
1942 {
1943 if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
1944 NS_ENSURE_TRUE(aMixed && aAlign, NS_ERROR_NULL_POINTER);
1945 nsRefPtr<nsHTMLEditRules> htmlRules = static_cast<nsHTMLEditRules*>(mRules.get());
1947 return htmlRules->GetAlignment(aMixed, aAlign);
1948 }
1951 NS_IMETHODIMP
1952 nsHTMLEditor::GetIndentState(bool *aCanIndent, bool *aCanOutdent)
1953 {
1954 if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
1955 NS_ENSURE_TRUE(aCanIndent && aCanOutdent, NS_ERROR_NULL_POINTER);
1957 nsRefPtr<nsHTMLEditRules> htmlRules = static_cast<nsHTMLEditRules*>(mRules.get());
1959 return htmlRules->GetIndentState(aCanIndent, aCanOutdent);
1960 }
1962 NS_IMETHODIMP
1963 nsHTMLEditor::MakeOrChangeList(const nsAString& aListType, bool entireList, const nsAString& aBulletType)
1964 {
1965 nsresult res;
1966 if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
1968 // Protect the edit rules object from dying
1969 nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
1971 bool cancel, handled;
1973 nsAutoEditBatch beginBatching(this);
1974 nsAutoRules beginRulesSniffing(this, EditAction::makeList, nsIEditor::eNext);
1976 // pre-process
1977 nsRefPtr<Selection> selection = GetSelection();
1978 NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
1980 nsTextRulesInfo ruleInfo(EditAction::makeList);
1981 ruleInfo.blockType = &aListType;
1982 ruleInfo.entireList = entireList;
1983 ruleInfo.bulletType = &aBulletType;
1984 res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
1985 if (cancel || (NS_FAILED(res))) return res;
1987 if (!handled)
1988 {
1989 // Find out if the selection is collapsed:
1990 bool isCollapsed = selection->Collapsed();
1992 nsCOMPtr<nsIDOMNode> node;
1993 int32_t offset;
1994 res = GetStartNodeAndOffset(selection, getter_AddRefs(node), &offset);
1995 if (!node) res = NS_ERROR_FAILURE;
1996 NS_ENSURE_SUCCESS(res, res);
1998 if (isCollapsed)
1999 {
2000 // have to find a place to put the list
2001 nsCOMPtr<nsIDOMNode> parent = node;
2002 nsCOMPtr<nsIDOMNode> topChild = node;
2003 nsCOMPtr<nsIDOMNode> tmp;
2005 nsCOMPtr<nsIAtom> listAtom = do_GetAtom(aListType);
2006 while (!CanContainTag(parent, listAtom)) {
2007 parent->GetParentNode(getter_AddRefs(tmp));
2008 NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE);
2009 topChild = parent;
2010 parent = tmp;
2011 }
2013 if (parent != node)
2014 {
2015 // we need to split up to the child of parent
2016 res = SplitNodeDeep(topChild, node, offset, &offset);
2017 NS_ENSURE_SUCCESS(res, res);
2018 }
2020 // make a list
2021 nsCOMPtr<nsIDOMNode> newList;
2022 res = CreateNode(aListType, parent, offset, getter_AddRefs(newList));
2023 NS_ENSURE_SUCCESS(res, res);
2024 // make a list item
2025 nsCOMPtr<nsIDOMNode> newItem;
2026 res = CreateNode(NS_LITERAL_STRING("li"), newList, 0, getter_AddRefs(newItem));
2027 NS_ENSURE_SUCCESS(res, res);
2028 res = selection->Collapse(newItem,0);
2029 NS_ENSURE_SUCCESS(res, res);
2030 }
2031 }
2033 res = mRules->DidDoAction(selection, &ruleInfo, res);
2034 return res;
2035 }
2038 NS_IMETHODIMP
2039 nsHTMLEditor::RemoveList(const nsAString& aListType)
2040 {
2041 nsresult res;
2042 if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
2044 // Protect the edit rules object from dying
2045 nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
2047 bool cancel, handled;
2049 nsAutoEditBatch beginBatching(this);
2050 nsAutoRules beginRulesSniffing(this, EditAction::removeList, nsIEditor::eNext);
2052 // pre-process
2053 nsRefPtr<Selection> selection = GetSelection();
2054 NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
2056 nsTextRulesInfo ruleInfo(EditAction::removeList);
2057 if (aListType.LowerCaseEqualsLiteral("ol"))
2058 ruleInfo.bOrdered = true;
2059 else ruleInfo.bOrdered = false;
2060 res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
2061 if (cancel || (NS_FAILED(res))) return res;
2063 // no default behavior for this yet. what would it mean?
2065 res = mRules->DidDoAction(selection, &ruleInfo, res);
2066 return res;
2067 }
2069 nsresult
2070 nsHTMLEditor::MakeDefinitionItem(const nsAString& aItemType)
2071 {
2072 nsresult res;
2073 if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
2075 // Protect the edit rules object from dying
2076 nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
2078 bool cancel, handled;
2080 nsAutoEditBatch beginBatching(this);
2081 nsAutoRules beginRulesSniffing(this, EditAction::makeDefListItem, nsIEditor::eNext);
2083 // pre-process
2084 nsRefPtr<Selection> selection = GetSelection();
2085 NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
2086 nsTextRulesInfo ruleInfo(EditAction::makeDefListItem);
2087 ruleInfo.blockType = &aItemType;
2088 res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
2089 if (cancel || (NS_FAILED(res))) return res;
2091 if (!handled)
2092 {
2093 // todo: no default for now. we count on rules to handle it.
2094 }
2096 res = mRules->DidDoAction(selection, &ruleInfo, res);
2097 return res;
2098 }
2100 nsresult
2101 nsHTMLEditor::InsertBasicBlock(const nsAString& aBlockType)
2102 {
2103 nsresult res;
2104 if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
2106 // Protect the edit rules object from dying
2107 nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
2109 bool cancel, handled;
2111 nsAutoEditBatch beginBatching(this);
2112 nsAutoRules beginRulesSniffing(this, EditAction::makeBasicBlock, nsIEditor::eNext);
2114 // pre-process
2115 nsRefPtr<Selection> selection = GetSelection();
2116 NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
2117 nsTextRulesInfo ruleInfo(EditAction::makeBasicBlock);
2118 ruleInfo.blockType = &aBlockType;
2119 res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
2120 if (cancel || (NS_FAILED(res))) return res;
2122 if (!handled)
2123 {
2124 // Find out if the selection is collapsed:
2125 bool isCollapsed = selection->Collapsed();
2127 nsCOMPtr<nsIDOMNode> node;
2128 int32_t offset;
2129 res = GetStartNodeAndOffset(selection, getter_AddRefs(node), &offset);
2130 if (!node) res = NS_ERROR_FAILURE;
2131 NS_ENSURE_SUCCESS(res, res);
2133 if (isCollapsed)
2134 {
2135 // have to find a place to put the block
2136 nsCOMPtr<nsIDOMNode> parent = node;
2137 nsCOMPtr<nsIDOMNode> topChild = node;
2138 nsCOMPtr<nsIDOMNode> tmp;
2140 nsCOMPtr<nsIAtom> blockAtom = do_GetAtom(aBlockType);
2141 while (!CanContainTag(parent, blockAtom)) {
2142 parent->GetParentNode(getter_AddRefs(tmp));
2143 NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE);
2144 topChild = parent;
2145 parent = tmp;
2146 }
2148 if (parent != node)
2149 {
2150 // we need to split up to the child of parent
2151 res = SplitNodeDeep(topChild, node, offset, &offset);
2152 NS_ENSURE_SUCCESS(res, res);
2153 }
2155 // make a block
2156 nsCOMPtr<nsIDOMNode> newBlock;
2157 res = CreateNode(aBlockType, parent, offset, getter_AddRefs(newBlock));
2158 NS_ENSURE_SUCCESS(res, res);
2160 // reposition selection to inside the block
2161 res = selection->Collapse(newBlock,0);
2162 NS_ENSURE_SUCCESS(res, res);
2163 }
2164 }
2166 res = mRules->DidDoAction(selection, &ruleInfo, res);
2167 return res;
2168 }
2170 NS_IMETHODIMP
2171 nsHTMLEditor::Indent(const nsAString& aIndent)
2172 {
2173 nsresult res;
2174 if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
2176 // Protect the edit rules object from dying
2177 nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
2179 bool cancel, handled;
2180 EditAction opID = EditAction::indent;
2181 if (aIndent.LowerCaseEqualsLiteral("outdent"))
2182 {
2183 opID = EditAction::outdent;
2184 }
2185 nsAutoEditBatch beginBatching(this);
2186 nsAutoRules beginRulesSniffing(this, opID, nsIEditor::eNext);
2188 // pre-process
2189 nsRefPtr<Selection> selection = GetSelection();
2190 NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
2192 nsTextRulesInfo ruleInfo(opID);
2193 res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
2194 if (cancel || (NS_FAILED(res))) return res;
2196 if (!handled)
2197 {
2198 // Do default - insert a blockquote node if selection collapsed
2199 nsCOMPtr<nsIDOMNode> node;
2200 int32_t offset;
2201 bool isCollapsed = selection->Collapsed();
2203 res = GetStartNodeAndOffset(selection, getter_AddRefs(node), &offset);
2204 if (!node) res = NS_ERROR_FAILURE;
2205 NS_ENSURE_SUCCESS(res, res);
2207 if (aIndent.EqualsLiteral("indent"))
2208 {
2209 if (isCollapsed)
2210 {
2211 // have to find a place to put the blockquote
2212 nsCOMPtr<nsIDOMNode> parent = node;
2213 nsCOMPtr<nsIDOMNode> topChild = node;
2214 nsCOMPtr<nsIDOMNode> tmp;
2215 while (!CanContainTag(parent, nsGkAtoms::blockquote)) {
2216 parent->GetParentNode(getter_AddRefs(tmp));
2217 NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE);
2218 topChild = parent;
2219 parent = tmp;
2220 }
2222 if (parent != node)
2223 {
2224 // we need to split up to the child of parent
2225 res = SplitNodeDeep(topChild, node, offset, &offset);
2226 NS_ENSURE_SUCCESS(res, res);
2227 }
2229 // make a blockquote
2230 nsCOMPtr<nsIDOMNode> newBQ;
2231 res = CreateNode(NS_LITERAL_STRING("blockquote"), parent, offset,
2232 getter_AddRefs(newBQ));
2233 NS_ENSURE_SUCCESS(res, res);
2234 // put a space in it so layout will draw the list item
2235 res = selection->Collapse(newBQ,0);
2236 NS_ENSURE_SUCCESS(res, res);
2237 res = InsertText(NS_LITERAL_STRING(" "));
2238 NS_ENSURE_SUCCESS(res, res);
2239 // reposition selection to before the space character
2240 res = GetStartNodeAndOffset(selection, getter_AddRefs(node), &offset);
2241 NS_ENSURE_SUCCESS(res, res);
2242 res = selection->Collapse(node,0);
2243 NS_ENSURE_SUCCESS(res, res);
2244 }
2245 }
2246 }
2247 res = mRules->DidDoAction(selection, &ruleInfo, res);
2248 return res;
2249 }
2251 //TODO: IMPLEMENT ALIGNMENT!
2253 NS_IMETHODIMP
2254 nsHTMLEditor::Align(const nsAString& aAlignType)
2255 {
2256 // Protect the edit rules object from dying
2257 nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
2259 nsAutoEditBatch beginBatching(this);
2260 nsAutoRules beginRulesSniffing(this, EditAction::align, nsIEditor::eNext);
2262 nsCOMPtr<nsIDOMNode> node;
2263 bool cancel, handled;
2265 // Find out if the selection is collapsed:
2266 nsRefPtr<Selection> selection = GetSelection();
2267 NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
2268 nsTextRulesInfo ruleInfo(EditAction::align);
2269 ruleInfo.alignType = &aAlignType;
2270 nsresult res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
2271 if (cancel || NS_FAILED(res))
2272 return res;
2274 res = mRules->DidDoAction(selection, &ruleInfo, res);
2275 return res;
2276 }
2278 NS_IMETHODIMP
2279 nsHTMLEditor::GetElementOrParentByTagName(const nsAString& aTagName, nsIDOMNode *aNode, nsIDOMElement** aReturn)
2280 {
2281 NS_ENSURE_TRUE(!aTagName.IsEmpty(), NS_ERROR_NULL_POINTER);
2282 NS_ENSURE_TRUE(aReturn, NS_ERROR_NULL_POINTER);
2284 nsCOMPtr<nsINode> current = do_QueryInterface(aNode);
2285 if (!current) {
2286 // If no node supplied, get it from anchor node of current selection
2287 nsRefPtr<Selection> selection = GetSelection();
2288 NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
2290 nsCOMPtr<nsINode> anchorNode = selection->GetAnchorNode();
2291 NS_ENSURE_TRUE(anchorNode, NS_ERROR_FAILURE);
2293 // Try to get the actual selected node
2294 if (anchorNode->HasChildNodes() && anchorNode->IsContent()) {
2295 uint32_t offset = selection->AnchorOffset();
2296 current = anchorNode->GetChildAt(offset);
2297 }
2298 // anchor node is probably a text node - just use that
2299 if (!current) {
2300 current = anchorNode;
2301 }
2302 }
2304 nsCOMPtr<nsIDOMNode> currentNode = current->AsDOMNode();
2306 nsAutoString TagName(aTagName);
2307 ToLowerCase(TagName);
2308 bool getLink = IsLinkTag(TagName);
2309 bool getNamedAnchor = IsNamedAnchorTag(TagName);
2310 if ( getLink || getNamedAnchor)
2311 {
2312 TagName.AssignLiteral("a");
2313 }
2314 bool findTableCell = TagName.EqualsLiteral("td");
2315 bool findList = TagName.EqualsLiteral("list");
2317 // default is null - no element found
2318 *aReturn = nullptr;
2320 nsCOMPtr<nsIDOMNode> parent;
2321 bool bNodeFound = false;
2323 while (true)
2324 {
2325 nsAutoString currentTagName;
2326 // Test if we have a link (an anchor with href set)
2327 if ( (getLink && nsHTMLEditUtils::IsLink(currentNode)) ||
2328 (getNamedAnchor && nsHTMLEditUtils::IsNamedAnchor(currentNode)) )
2329 {
2330 bNodeFound = true;
2331 break;
2332 } else {
2333 if (findList)
2334 {
2335 // Match "ol", "ul", or "dl" for lists
2336 if (nsHTMLEditUtils::IsList(currentNode))
2337 goto NODE_FOUND;
2339 } else if (findTableCell)
2340 {
2341 // Table cells are another special case:
2342 // Match either "td" or "th" for them
2343 if (nsHTMLEditUtils::IsTableCell(currentNode))
2344 goto NODE_FOUND;
2346 } else {
2347 currentNode->GetNodeName(currentTagName);
2348 if (currentTagName.Equals(TagName, nsCaseInsensitiveStringComparator()))
2349 {
2350 NODE_FOUND:
2351 bNodeFound = true;
2352 break;
2353 }
2354 }
2355 }
2356 // Search up the parent chain
2357 // We should never fail because of root test below, but lets be safe
2358 // XXX: ERROR_HANDLING error return code lost
2359 if (NS_FAILED(currentNode->GetParentNode(getter_AddRefs(parent))) || !parent)
2360 break;
2362 // Stop searching if parent is a body tag
2363 nsAutoString parentTagName;
2364 parent->GetNodeName(parentTagName);
2365 // Note: Originally used IsRoot to stop at table cells,
2366 // but that's too messy when you are trying to find the parent table
2367 if(parentTagName.LowerCaseEqualsLiteral("body"))
2368 break;
2370 currentNode = parent;
2371 }
2373 if (!bNodeFound) {
2374 return NS_EDITOR_ELEMENT_NOT_FOUND;
2375 }
2377 nsCOMPtr<nsIDOMElement> currentElement = do_QueryInterface(currentNode);
2378 currentElement.forget(aReturn);
2379 return NS_OK;
2380 }
2382 NS_IMETHODIMP
2383 nsHTMLEditor::GetSelectedElement(const nsAString& aTagName, nsIDOMElement** aReturn)
2384 {
2385 NS_ENSURE_TRUE(aReturn , NS_ERROR_NULL_POINTER);
2387 // default is null - no element found
2388 *aReturn = nullptr;
2390 // First look for a single element in selection
2391 nsCOMPtr<nsISelection>selection;
2392 nsresult res = GetSelection(getter_AddRefs(selection));
2393 NS_ENSURE_SUCCESS(res, res);
2394 NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
2395 Selection* sel = static_cast<Selection*>(selection.get());
2397 bool bNodeFound = false;
2398 bool isCollapsed = selection->Collapsed();
2400 nsAutoString domTagName;
2401 nsAutoString TagName(aTagName);
2402 ToLowerCase(TagName);
2403 // Empty string indicates we should match any element tag
2404 bool anyTag = (TagName.IsEmpty());
2405 bool isLinkTag = IsLinkTag(TagName);
2406 bool isNamedAnchorTag = IsNamedAnchorTag(TagName);
2408 nsCOMPtr<nsIDOMElement> selectedElement;
2409 nsCOMPtr<nsIDOMRange> range;
2410 res = selection->GetRangeAt(0, getter_AddRefs(range));
2411 NS_ENSURE_SUCCESS(res, res);
2413 nsCOMPtr<nsIDOMNode> startParent;
2414 int32_t startOffset, endOffset;
2415 res = range->GetStartContainer(getter_AddRefs(startParent));
2416 NS_ENSURE_SUCCESS(res, res);
2417 res = range->GetStartOffset(&startOffset);
2418 NS_ENSURE_SUCCESS(res, res);
2420 nsCOMPtr<nsIDOMNode> endParent;
2421 res = range->GetEndContainer(getter_AddRefs(endParent));
2422 NS_ENSURE_SUCCESS(res, res);
2423 res = range->GetEndOffset(&endOffset);
2424 NS_ENSURE_SUCCESS(res, res);
2426 // Optimization for a single selected element
2427 if (startParent && startParent == endParent && (endOffset-startOffset) == 1)
2428 {
2429 nsCOMPtr<nsIDOMNode> selectedNode = GetChildAt(startParent, startOffset);
2430 NS_ENSURE_SUCCESS(res, NS_OK);
2431 if (selectedNode)
2432 {
2433 selectedNode->GetNodeName(domTagName);
2434 ToLowerCase(domTagName);
2436 // Test for appropriate node type requested
2437 if (anyTag || (TagName == domTagName) ||
2438 (isLinkTag && nsHTMLEditUtils::IsLink(selectedNode)) ||
2439 (isNamedAnchorTag && nsHTMLEditUtils::IsNamedAnchor(selectedNode)))
2440 {
2441 bNodeFound = true;
2442 selectedElement = do_QueryInterface(selectedNode);
2443 }
2444 }
2445 }
2447 if (!bNodeFound)
2448 {
2449 if (isLinkTag)
2450 {
2451 // Link tag is a special case - we return the anchor node
2452 // found for any selection that is totally within a link,
2453 // included a collapsed selection (just a caret in a link)
2454 nsCOMPtr<nsIDOMNode> anchorNode;
2455 res = selection->GetAnchorNode(getter_AddRefs(anchorNode));
2456 NS_ENSURE_SUCCESS(res, res);
2457 int32_t anchorOffset = -1;
2458 if (anchorNode)
2459 selection->GetAnchorOffset(&anchorOffset);
2461 nsCOMPtr<nsIDOMNode> focusNode;
2462 res = selection->GetFocusNode(getter_AddRefs(focusNode));
2463 NS_ENSURE_SUCCESS(res, res);
2464 int32_t focusOffset = -1;
2465 if (focusNode)
2466 selection->GetFocusOffset(&focusOffset);
2468 // Link node must be the same for both ends of selection
2469 if (NS_SUCCEEDED(res) && anchorNode)
2470 {
2471 nsCOMPtr<nsIDOMElement> parentLinkOfAnchor;
2472 res = GetElementOrParentByTagName(NS_LITERAL_STRING("href"), anchorNode, getter_AddRefs(parentLinkOfAnchor));
2473 // XXX: ERROR_HANDLING can parentLinkOfAnchor be null?
2474 if (NS_SUCCEEDED(res) && parentLinkOfAnchor)
2475 {
2476 if (isCollapsed)
2477 {
2478 // We have just a caret in the link
2479 bNodeFound = true;
2480 } else if(focusNode)
2481 { // Link node must be the same for both ends of selection
2482 nsCOMPtr<nsIDOMElement> parentLinkOfFocus;
2483 res = GetElementOrParentByTagName(NS_LITERAL_STRING("href"), focusNode, getter_AddRefs(parentLinkOfFocus));
2484 if (NS_SUCCEEDED(res) && parentLinkOfFocus == parentLinkOfAnchor)
2485 bNodeFound = true;
2486 }
2488 // We found a link node parent
2489 if (bNodeFound) {
2490 // GetElementOrParentByTagName addref'd this, so we don't need to do it here
2491 *aReturn = parentLinkOfAnchor;
2492 NS_IF_ADDREF(*aReturn);
2493 return NS_OK;
2494 }
2495 }
2496 else if (anchorOffset >= 0) // Check if link node is the only thing selected
2497 {
2498 nsCOMPtr<nsIDOMNode> anchorChild;
2499 anchorChild = GetChildAt(anchorNode,anchorOffset);
2500 if (anchorChild && nsHTMLEditUtils::IsLink(anchorChild) &&
2501 (anchorNode == focusNode) && focusOffset == (anchorOffset+1))
2502 {
2503 selectedElement = do_QueryInterface(anchorChild);
2504 bNodeFound = true;
2505 }
2506 }
2507 }
2508 }
2510 if (!isCollapsed) // Don't bother to examine selection if it is collapsed
2511 {
2512 nsRefPtr<nsRange> currange = sel->GetRangeAt(0);
2513 if (currange) {
2514 nsCOMPtr<nsIContentIterator> iter =
2515 do_CreateInstance("@mozilla.org/content/post-content-iterator;1", &res);
2516 NS_ENSURE_SUCCESS(res, res);
2518 iter->Init(currange);
2519 // loop through the content iterator for each content node
2520 while (!iter->IsDone())
2521 {
2522 // Query interface to cast nsIContent to nsIDOMNode
2523 // then get tagType to compare to aTagName
2524 // Clone node of each desired type and append it to the aDomFrag
2525 selectedElement = do_QueryInterface(iter->GetCurrentNode());
2526 if (selectedElement)
2527 {
2528 // If we already found a node, then we have another element,
2529 // thus there's not just one element selected
2530 if (bNodeFound)
2531 {
2532 bNodeFound = false;
2533 break;
2534 }
2536 selectedElement->GetNodeName(domTagName);
2537 ToLowerCase(domTagName);
2539 if (anyTag)
2540 {
2541 // Get name of first selected element
2542 selectedElement->GetTagName(TagName);
2543 ToLowerCase(TagName);
2544 anyTag = false;
2545 }
2547 // The "A" tag is a pain,
2548 // used for both link(href is set) and "Named Anchor"
2549 nsCOMPtr<nsIDOMNode> selectedNode = do_QueryInterface(selectedElement);
2550 if ( (isLinkTag && nsHTMLEditUtils::IsLink(selectedNode)) ||
2551 (isNamedAnchorTag && nsHTMLEditUtils::IsNamedAnchor(selectedNode)) )
2552 {
2553 bNodeFound = true;
2554 } else if (TagName == domTagName) { // All other tag names are handled here
2555 bNodeFound = true;
2556 }
2557 if (!bNodeFound)
2558 {
2559 // Check if node we have is really part of the selection???
2560 break;
2561 }
2562 }
2563 iter->Next();
2564 }
2565 } else {
2566 // Should never get here?
2567 isCollapsed = true;
2568 NS_WARNING("isCollapsed was FALSE, but no elements found in selection\n");
2569 }
2570 }
2571 }
2572 if (bNodeFound)
2573 {
2575 *aReturn = selectedElement;
2576 if (selectedElement)
2577 {
2578 // Getters must addref
2579 NS_ADDREF(*aReturn);
2580 }
2581 }
2582 else res = NS_EDITOR_ELEMENT_NOT_FOUND;
2584 return res;
2585 }
2587 NS_IMETHODIMP
2588 nsHTMLEditor::CreateElementWithDefaults(const nsAString& aTagName, nsIDOMElement** aReturn)
2589 {
2590 nsresult res=NS_ERROR_NOT_INITIALIZED;
2591 if (aReturn)
2592 *aReturn = nullptr;
2594 // NS_ENSURE_TRUE(aTagName && aReturn, NS_ERROR_NULL_POINTER);
2595 NS_ENSURE_TRUE(!aTagName.IsEmpty() && aReturn, NS_ERROR_NULL_POINTER);
2597 nsAutoString TagName(aTagName);
2598 ToLowerCase(TagName);
2599 nsAutoString realTagName;
2601 if (IsLinkTag(TagName) || IsNamedAnchorTag(TagName))
2602 {
2603 realTagName.AssignLiteral("a");
2604 } else {
2605 realTagName = TagName;
2606 }
2607 //We don't use editor's CreateElement because we don't want to
2608 // go through the transaction system
2610 nsCOMPtr<nsIDOMElement>newElement;
2611 nsCOMPtr<dom::Element> newContent;
2612 nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(mDocWeak);
2613 NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
2615 //new call to use instead to get proper HTML element, bug# 39919
2616 res = CreateHTMLContent(realTagName, getter_AddRefs(newContent));
2617 newElement = do_QueryInterface(newContent);
2618 if (NS_FAILED(res) || !newElement)
2619 return NS_ERROR_FAILURE;
2621 // Mark the new element dirty, so it will be formatted
2622 newElement->SetAttribute(NS_LITERAL_STRING("_moz_dirty"), EmptyString());
2624 // Set default values for new elements
2625 if (TagName.EqualsLiteral("table")) {
2626 res = newElement->SetAttribute(NS_LITERAL_STRING("cellpadding"),NS_LITERAL_STRING("2"));
2627 NS_ENSURE_SUCCESS(res, res);
2628 res = newElement->SetAttribute(NS_LITERAL_STRING("cellspacing"),NS_LITERAL_STRING("2"));
2629 NS_ENSURE_SUCCESS(res, res);
2630 res = newElement->SetAttribute(NS_LITERAL_STRING("border"),NS_LITERAL_STRING("1"));
2631 } else if (TagName.EqualsLiteral("td"))
2632 {
2633 res = SetAttributeOrEquivalent(newElement, NS_LITERAL_STRING("valign"),
2634 NS_LITERAL_STRING("top"), true);
2635 }
2636 // ADD OTHER TAGS HERE
2638 if (NS_SUCCEEDED(res))
2639 {
2640 *aReturn = newElement;
2641 // Getters must addref
2642 NS_ADDREF(*aReturn);
2643 }
2645 return res;
2646 }
2648 NS_IMETHODIMP
2649 nsHTMLEditor::InsertLinkAroundSelection(nsIDOMElement* aAnchorElement)
2650 {
2651 NS_ENSURE_TRUE(aAnchorElement, NS_ERROR_NULL_POINTER);
2653 // We must have a real selection
2654 nsCOMPtr<nsISelection> selection;
2655 nsresult res = GetSelection(getter_AddRefs(selection));
2656 if (!selection)
2657 {
2658 res = NS_ERROR_NULL_POINTER;
2659 }
2660 NS_ENSURE_SUCCESS(res, res);
2661 NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
2663 if (selection->Collapsed()) {
2664 NS_WARNING("InsertLinkAroundSelection called but there is no selection!!!");
2665 return NS_OK;
2666 }
2668 // Be sure we were given an anchor element
2669 nsCOMPtr<nsIDOMHTMLAnchorElement> anchor = do_QueryInterface(aAnchorElement);
2670 if (!anchor) {
2671 return NS_OK;
2672 }
2674 nsAutoString href;
2675 res = anchor->GetHref(href);
2676 NS_ENSURE_SUCCESS(res, res);
2677 if (href.IsEmpty()) {
2678 return NS_OK;
2679 }
2681 nsAutoEditBatch beginBatching(this);
2683 // Set all attributes found on the supplied anchor element
2684 nsCOMPtr<nsIDOMMozNamedAttrMap> attrMap;
2685 aAnchorElement->GetAttributes(getter_AddRefs(attrMap));
2686 NS_ENSURE_TRUE(attrMap, NS_ERROR_FAILURE);
2688 uint32_t count;
2689 attrMap->GetLength(&count);
2690 nsAutoString name, value;
2692 for (uint32_t i = 0; i < count; ++i) {
2693 nsCOMPtr<nsIDOMAttr> attribute;
2694 res = attrMap->Item(i, getter_AddRefs(attribute));
2695 NS_ENSURE_SUCCESS(res, res);
2697 if (attribute) {
2698 // We must clear the string buffers
2699 // because GetName, GetValue appends to previous string!
2700 name.Truncate();
2701 value.Truncate();
2703 res = attribute->GetName(name);
2704 NS_ENSURE_SUCCESS(res, res);
2706 res = attribute->GetValue(value);
2707 NS_ENSURE_SUCCESS(res, res);
2709 res = SetInlineProperty(nsEditProperty::a, name, value);
2710 NS_ENSURE_SUCCESS(res, res);
2711 }
2712 }
2713 return NS_OK;
2714 }
2716 NS_IMETHODIMP
2717 nsHTMLEditor::SetHTMLBackgroundColor(const nsAString& aColor)
2718 {
2719 NS_PRECONDITION(mDocWeak, "Missing Editor DOM Document");
2721 // Find a selected or enclosing table element to set background on
2722 nsCOMPtr<nsIDOMElement> element;
2723 int32_t selectedCount;
2724 nsAutoString tagName;
2725 nsresult res = GetSelectedOrParentTableElement(tagName, &selectedCount,
2726 getter_AddRefs(element));
2727 NS_ENSURE_SUCCESS(res, res);
2729 bool setColor = !aColor.IsEmpty();
2731 NS_NAMED_LITERAL_STRING(bgcolor, "bgcolor");
2732 if (element)
2733 {
2734 if (selectedCount > 0)
2735 {
2736 // Traverse all selected cells
2737 nsCOMPtr<nsIDOMElement> cell;
2738 res = GetFirstSelectedCell(nullptr, getter_AddRefs(cell));
2739 if (NS_SUCCEEDED(res) && cell)
2740 {
2741 while(cell)
2742 {
2743 if (setColor)
2744 res = SetAttribute(cell, bgcolor, aColor);
2745 else
2746 res = RemoveAttribute(cell, bgcolor);
2747 if (NS_FAILED(res)) break;
2749 GetNextSelectedCell(nullptr, getter_AddRefs(cell));
2750 };
2751 return res;
2752 }
2753 }
2754 // If we failed to find a cell, fall through to use originally-found element
2755 } else {
2756 // No table element -- set the background color on the body tag
2757 element = do_QueryInterface(GetRoot());
2758 NS_ENSURE_TRUE(element, NS_ERROR_NULL_POINTER);
2759 }
2760 // Use the editor method that goes through the transaction system
2761 if (setColor)
2762 res = SetAttribute(element, bgcolor, aColor);
2763 else
2764 res = RemoveAttribute(element, bgcolor);
2766 return res;
2767 }
2769 NS_IMETHODIMP nsHTMLEditor::SetBodyAttribute(const nsAString& aAttribute, const nsAString& aValue)
2770 {
2771 // TODO: Check selection for Cell, Row, Column or table and do color on appropriate level
2773 NS_ASSERTION(mDocWeak, "Missing Editor DOM Document");
2775 // Set the background color attribute on the body tag
2776 nsCOMPtr<nsIDOMElement> bodyElement = do_QueryInterface(GetRoot());
2777 NS_ENSURE_TRUE(bodyElement, NS_ERROR_NULL_POINTER);
2779 // Use the editor method that goes through the transaction system
2780 return SetAttribute(bodyElement, aAttribute, aValue);
2781 }
2783 NS_IMETHODIMP
2784 nsHTMLEditor::GetLinkedObjects(nsISupportsArray** aNodeList)
2785 {
2786 NS_ENSURE_TRUE(aNodeList, NS_ERROR_NULL_POINTER);
2788 nsresult res;
2790 res = NS_NewISupportsArray(aNodeList);
2791 NS_ENSURE_SUCCESS(res, res);
2792 NS_ENSURE_TRUE(*aNodeList, NS_ERROR_NULL_POINTER);
2794 nsCOMPtr<nsIContentIterator> iter =
2795 do_CreateInstance("@mozilla.org/content/post-content-iterator;1", &res);
2796 NS_ENSURE_TRUE(iter, NS_ERROR_NULL_POINTER);
2797 if ((NS_SUCCEEDED(res)))
2798 {
2799 nsCOMPtr<nsIDocument> doc = GetDocument();
2800 NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
2802 iter->Init(doc->GetRootElement());
2804 // loop through the content iterator for each content node
2805 while (!iter->IsDone())
2806 {
2807 nsCOMPtr<nsIDOMNode> node (do_QueryInterface(iter->GetCurrentNode()));
2808 if (node)
2809 {
2810 // Let nsURIRefObject make the hard decisions:
2811 nsCOMPtr<nsIURIRefObject> refObject;
2812 res = NS_NewHTMLURIRefObject(getter_AddRefs(refObject), node);
2813 if (NS_SUCCEEDED(res))
2814 {
2815 nsCOMPtr<nsISupports> isupp (do_QueryInterface(refObject));
2817 (*aNodeList)->AppendElement(isupp);
2818 }
2819 }
2820 iter->Next();
2821 }
2822 }
2824 return NS_OK;
2825 }
2828 NS_IMETHODIMP
2829 nsHTMLEditor::AddStyleSheet(const nsAString &aURL)
2830 {
2831 // Enable existing sheet if already loaded.
2832 if (EnableExistingStyleSheet(aURL))
2833 return NS_OK;
2835 // Lose the previously-loaded sheet so there's nothing to replace
2836 // This pattern is different from Override methods because
2837 // we must wait to remove mLastStyleSheetURL and add new sheet
2838 // at the same time (in StyleSheetLoaded callback) so they are undoable together
2839 mLastStyleSheetURL.Truncate();
2840 return ReplaceStyleSheet(aURL);
2841 }
2843 NS_IMETHODIMP
2844 nsHTMLEditor::ReplaceStyleSheet(const nsAString& aURL)
2845 {
2846 // Enable existing sheet if already loaded.
2847 if (EnableExistingStyleSheet(aURL))
2848 {
2849 // Disable last sheet if not the same as new one
2850 if (!mLastStyleSheetURL.IsEmpty() && !mLastStyleSheetURL.Equals(aURL))
2851 return EnableStyleSheet(mLastStyleSheetURL, false);
2853 return NS_OK;
2854 }
2856 // Make sure the pres shell doesn't disappear during the load.
2857 NS_ENSURE_TRUE(mDocWeak, NS_ERROR_NOT_INITIALIZED);
2858 nsCOMPtr<nsIPresShell> ps = GetPresShell();
2859 NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
2861 nsCOMPtr<nsIURI> uaURI;
2862 nsresult rv = NS_NewURI(getter_AddRefs(uaURI), aURL);
2863 NS_ENSURE_SUCCESS(rv, rv);
2865 return ps->GetDocument()->CSSLoader()->
2866 LoadSheet(uaURI, nullptr, EmptyCString(), this);
2867 }
2869 NS_IMETHODIMP
2870 nsHTMLEditor::RemoveStyleSheet(const nsAString &aURL)
2871 {
2872 nsRefPtr<nsCSSStyleSheet> sheet;
2873 nsresult rv = GetStyleSheetForURL(aURL, getter_AddRefs(sheet));
2874 NS_ENSURE_SUCCESS(rv, rv);
2875 NS_ENSURE_TRUE(sheet, NS_ERROR_UNEXPECTED);
2877 nsRefPtr<RemoveStyleSheetTxn> txn;
2878 rv = CreateTxnForRemoveStyleSheet(sheet, getter_AddRefs(txn));
2879 if (!txn) rv = NS_ERROR_NULL_POINTER;
2880 if (NS_SUCCEEDED(rv))
2881 {
2882 rv = DoTransaction(txn);
2883 if (NS_SUCCEEDED(rv))
2884 mLastStyleSheetURL.Truncate(); // forget it
2886 // Remove it from our internal list
2887 rv = RemoveStyleSheetFromList(aURL);
2888 }
2890 return rv;
2891 }
2894 NS_IMETHODIMP
2895 nsHTMLEditor::AddOverrideStyleSheet(const nsAString& aURL)
2896 {
2897 // Enable existing sheet if already loaded.
2898 if (EnableExistingStyleSheet(aURL))
2899 return NS_OK;
2901 // Make sure the pres shell doesn't disappear during the load.
2902 nsCOMPtr<nsIPresShell> ps = GetPresShell();
2903 NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
2905 nsCOMPtr<nsIURI> uaURI;
2906 nsresult rv = NS_NewURI(getter_AddRefs(uaURI), aURL);
2907 NS_ENSURE_SUCCESS(rv, rv);
2909 // We MUST ONLY load synchronous local files (no @import)
2910 // XXXbz Except this will actually try to load remote files
2911 // synchronously, of course..
2912 nsRefPtr<nsCSSStyleSheet> sheet;
2913 // Editor override style sheets may want to style Gecko anonymous boxes
2914 rv = ps->GetDocument()->CSSLoader()->
2915 LoadSheetSync(uaURI, true, true, getter_AddRefs(sheet));
2917 // Synchronous loads should ALWAYS return completed
2918 NS_ENSURE_TRUE(sheet, NS_ERROR_NULL_POINTER);
2920 // Add the override style sheet
2921 // (This checks if already exists)
2922 ps->AddOverrideStyleSheet(sheet);
2924 ps->ReconstructStyleData();
2926 // Save as the last-loaded sheet
2927 mLastOverrideStyleSheetURL = aURL;
2929 //Add URL and style sheet to our lists
2930 return AddNewStyleSheetToList(aURL, sheet);
2931 }
2933 NS_IMETHODIMP
2934 nsHTMLEditor::ReplaceOverrideStyleSheet(const nsAString& aURL)
2935 {
2936 // Enable existing sheet if already loaded.
2937 if (EnableExistingStyleSheet(aURL))
2938 {
2939 // Disable last sheet if not the same as new one
2940 if (!mLastOverrideStyleSheetURL.IsEmpty() && !mLastOverrideStyleSheetURL.Equals(aURL))
2941 return EnableStyleSheet(mLastOverrideStyleSheetURL, false);
2943 return NS_OK;
2944 }
2945 // Remove the previous sheet
2946 if (!mLastOverrideStyleSheetURL.IsEmpty())
2947 RemoveOverrideStyleSheet(mLastOverrideStyleSheetURL);
2949 return AddOverrideStyleSheet(aURL);
2950 }
2952 // Do NOT use transaction system for override style sheets
2953 NS_IMETHODIMP
2954 nsHTMLEditor::RemoveOverrideStyleSheet(const nsAString &aURL)
2955 {
2956 nsRefPtr<nsCSSStyleSheet> sheet;
2957 GetStyleSheetForURL(aURL, getter_AddRefs(sheet));
2959 // Make sure we remove the stylesheet from our internal list in all
2960 // cases.
2961 nsresult rv = RemoveStyleSheetFromList(aURL);
2963 NS_ENSURE_TRUE(sheet, NS_OK); /// Don't fail if sheet not found
2965 NS_ENSURE_TRUE(mDocWeak, NS_ERROR_NOT_INITIALIZED);
2966 nsCOMPtr<nsIPresShell> ps = GetPresShell();
2967 NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
2969 ps->RemoveOverrideStyleSheet(sheet);
2970 ps->ReconstructStyleData();
2972 // Remove it from our internal list
2973 return rv;
2974 }
2976 NS_IMETHODIMP
2977 nsHTMLEditor::EnableStyleSheet(const nsAString &aURL, bool aEnable)
2978 {
2979 nsRefPtr<nsCSSStyleSheet> sheet;
2980 nsresult rv = GetStyleSheetForURL(aURL, getter_AddRefs(sheet));
2981 NS_ENSURE_SUCCESS(rv, rv);
2982 NS_ENSURE_TRUE(sheet, NS_OK); // Don't fail if sheet not found
2984 // Ensure the style sheet is owned by our document.
2985 nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
2986 sheet->SetOwningDocument(doc);
2988 return sheet->SetDisabled(!aEnable);
2989 }
2991 bool
2992 nsHTMLEditor::EnableExistingStyleSheet(const nsAString &aURL)
2993 {
2994 nsRefPtr<nsCSSStyleSheet> sheet;
2995 nsresult rv = GetStyleSheetForURL(aURL, getter_AddRefs(sheet));
2996 NS_ENSURE_SUCCESS(rv, false);
2998 // Enable sheet if already loaded.
2999 if (sheet)
3000 {
3001 // Ensure the style sheet is owned by our document.
3002 nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
3003 sheet->SetOwningDocument(doc);
3005 sheet->SetDisabled(false);
3006 return true;
3007 }
3008 return false;
3009 }
3011 nsresult
3012 nsHTMLEditor::AddNewStyleSheetToList(const nsAString &aURL,
3013 nsCSSStyleSheet *aStyleSheet)
3014 {
3015 uint32_t countSS = mStyleSheets.Length();
3016 uint32_t countU = mStyleSheetURLs.Length();
3018 if (countSS != countU)
3019 return NS_ERROR_UNEXPECTED;
3021 if (!mStyleSheetURLs.AppendElement(aURL))
3022 return NS_ERROR_UNEXPECTED;
3024 return mStyleSheets.AppendElement(aStyleSheet) ? NS_OK : NS_ERROR_UNEXPECTED;
3025 }
3027 nsresult
3028 nsHTMLEditor::RemoveStyleSheetFromList(const nsAString &aURL)
3029 {
3030 // is it already in the list?
3031 uint32_t foundIndex;
3032 foundIndex = mStyleSheetURLs.IndexOf(aURL);
3033 if (foundIndex == mStyleSheetURLs.NoIndex)
3034 return NS_ERROR_FAILURE;
3036 // Attempt both removals; if one fails there's not much we can do.
3037 mStyleSheets.RemoveElementAt(foundIndex);
3038 mStyleSheetURLs.RemoveElementAt(foundIndex);
3040 return NS_OK;
3041 }
3043 NS_IMETHODIMP
3044 nsHTMLEditor::GetStyleSheetForURL(const nsAString &aURL,
3045 nsCSSStyleSheet **aStyleSheet)
3046 {
3047 NS_ENSURE_ARG_POINTER(aStyleSheet);
3048 *aStyleSheet = 0;
3050 // is it already in the list?
3051 uint32_t foundIndex;
3052 foundIndex = mStyleSheetURLs.IndexOf(aURL);
3053 if (foundIndex == mStyleSheetURLs.NoIndex)
3054 return NS_OK; //No sheet -- don't fail!
3056 *aStyleSheet = mStyleSheets[foundIndex];
3057 NS_ENSURE_TRUE(*aStyleSheet, NS_ERROR_FAILURE);
3059 NS_ADDREF(*aStyleSheet);
3061 return NS_OK;
3062 }
3064 NS_IMETHODIMP
3065 nsHTMLEditor::GetURLForStyleSheet(nsCSSStyleSheet *aStyleSheet,
3066 nsAString &aURL)
3067 {
3068 // is it already in the list?
3069 int32_t foundIndex = mStyleSheets.IndexOf(aStyleSheet);
3071 // Don't fail if we don't find it in our list
3072 // Note: mStyleSheets is nsCOMArray, so its IndexOf() method
3073 // returns -1 on failure.
3074 if (foundIndex == -1)
3075 return NS_OK;
3077 // Found it in the list!
3078 aURL = mStyleSheetURLs[foundIndex];
3079 return NS_OK;
3080 }
3082 /*
3083 * nsIEditorMailSupport methods
3084 */
3086 NS_IMETHODIMP
3087 nsHTMLEditor::GetEmbeddedObjects(nsISupportsArray** aNodeList)
3088 {
3089 NS_ENSURE_TRUE(aNodeList, NS_ERROR_NULL_POINTER);
3091 nsresult rv = NS_NewISupportsArray(aNodeList);
3092 NS_ENSURE_SUCCESS(rv, rv);
3093 NS_ENSURE_TRUE(*aNodeList, NS_ERROR_NULL_POINTER);
3095 nsCOMPtr<nsIContentIterator> iter =
3096 do_CreateInstance("@mozilla.org/content/post-content-iterator;1", &rv);
3097 NS_ENSURE_TRUE(iter, NS_ERROR_NULL_POINTER);
3098 NS_ENSURE_SUCCESS(rv, rv);
3100 nsCOMPtr<nsIDocument> doc = GetDocument();
3101 NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
3103 iter->Init(doc->GetRootElement());
3105 // Loop through the content iterator for each content node.
3106 while (!iter->IsDone()) {
3107 nsINode* node = iter->GetCurrentNode();
3108 if (node->IsElement()) {
3109 dom::Element* element = node->AsElement();
3111 // See if it's an image or an embed and also include all links.
3112 // Let mail decide which link to send or not
3113 if (element->IsHTML(nsGkAtoms::img) ||
3114 element->IsHTML(nsGkAtoms::embed) ||
3115 element->IsHTML(nsGkAtoms::a) ||
3116 (element->IsHTML(nsGkAtoms::body) &&
3117 element->HasAttr(kNameSpaceID_None, nsGkAtoms::background))) {
3118 nsCOMPtr<nsIDOMNode> domNode = do_QueryInterface(node);
3119 (*aNodeList)->AppendElement(domNode);
3120 }
3121 }
3122 iter->Next();
3123 }
3125 return rv;
3126 }
3129 NS_IMETHODIMP
3130 nsHTMLEditor::DeleteSelectionImpl(EDirection aAction,
3131 EStripWrappers aStripWrappers)
3132 {
3133 MOZ_ASSERT(aStripWrappers == eStrip || aStripWrappers == eNoStrip);
3135 nsresult res = nsEditor::DeleteSelectionImpl(aAction, aStripWrappers);
3136 NS_ENSURE_SUCCESS(res, res);
3138 // If we weren't asked to strip any wrappers, we're done.
3139 if (aStripWrappers == eNoStrip) {
3140 return NS_OK;
3141 }
3143 nsRefPtr<Selection> selection = GetSelection();
3144 // Just checking that the selection itself is collapsed doesn't seem to work
3145 // right in the multi-range case
3146 NS_ENSURE_STATE(selection);
3147 NS_ENSURE_STATE(selection->GetAnchorFocusRange());
3148 NS_ENSURE_STATE(selection->GetAnchorFocusRange()->Collapsed());
3150 NS_ENSURE_STATE(selection->GetAnchorNode()->IsContent());
3151 nsCOMPtr<nsIContent> content = selection->GetAnchorNode()->AsContent();
3153 // Don't strip wrappers if this is the only wrapper in the block. Then we'll
3154 // add a <br> later, so it won't be an empty wrapper in the end.
3155 nsCOMPtr<nsIContent> blockParent = content;
3156 while (blockParent && !IsBlockNode(blockParent)) {
3157 blockParent = blockParent->GetParent();
3158 }
3159 if (!blockParent) {
3160 return NS_OK;
3161 }
3162 bool emptyBlockParent;
3163 res = IsEmptyNode(blockParent, &emptyBlockParent);
3164 NS_ENSURE_SUCCESS(res, res);
3165 if (emptyBlockParent) {
3166 return NS_OK;
3167 }
3169 if (content && !IsBlockNode(content) && !content->Length() &&
3170 content->IsEditable() && content != content->GetEditingHost()) {
3171 while (content->GetParent() && !IsBlockNode(content->GetParent()) &&
3172 content->GetParent()->Length() == 1 &&
3173 content->GetParent()->IsEditable() &&
3174 content->GetParent() != content->GetEditingHost()) {
3175 content = content->GetParent();
3176 }
3177 res = DeleteNode(content);
3178 NS_ENSURE_SUCCESS(res, res);
3179 }
3181 return NS_OK;
3182 }
3185 nsresult
3186 nsHTMLEditor::DeleteNode(nsINode* aNode)
3187 {
3188 nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode);
3189 return DeleteNode(node);
3190 }
3192 NS_IMETHODIMP
3193 nsHTMLEditor::DeleteNode(nsIDOMNode* aNode)
3194 {
3195 // do nothing if the node is read-only
3196 nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
3197 if (!IsModifiableNode(aNode) && !IsMozEditorBogusNode(content)) {
3198 return NS_ERROR_FAILURE;
3199 }
3201 return nsEditor::DeleteNode(aNode);
3202 }
3204 NS_IMETHODIMP nsHTMLEditor::DeleteText(nsIDOMCharacterData *aTextNode,
3205 uint32_t aOffset,
3206 uint32_t aLength)
3207 {
3208 // do nothing if the node is read-only
3209 if (!IsModifiableNode(aTextNode)) {
3210 return NS_ERROR_FAILURE;
3211 }
3213 return nsEditor::DeleteText(aTextNode, aOffset, aLength);
3214 }
3216 NS_IMETHODIMP nsHTMLEditor::InsertTextImpl(const nsAString& aStringToInsert,
3217 nsCOMPtr<nsIDOMNode> *aInOutNode,
3218 int32_t *aInOutOffset,
3219 nsIDOMDocument *aDoc)
3220 {
3221 // do nothing if the node is read-only
3222 if (!IsModifiableNode(*aInOutNode)) {
3223 return NS_ERROR_FAILURE;
3224 }
3226 return nsEditor::InsertTextImpl(aStringToInsert, aInOutNode, aInOutOffset, aDoc);
3227 }
3229 void
3230 nsHTMLEditor::ContentAppended(nsIDocument *aDocument, nsIContent* aContainer,
3231 nsIContent* aFirstNewContent,
3232 int32_t aIndexInContainer)
3233 {
3234 DoContentInserted(aDocument, aContainer, aFirstNewContent, aIndexInContainer,
3235 eAppended);
3236 }
3238 void
3239 nsHTMLEditor::ContentInserted(nsIDocument *aDocument, nsIContent* aContainer,
3240 nsIContent* aChild, int32_t aIndexInContainer)
3241 {
3242 DoContentInserted(aDocument, aContainer, aChild, aIndexInContainer,
3243 eInserted);
3244 }
3246 void
3247 nsHTMLEditor::DoContentInserted(nsIDocument* aDocument, nsIContent* aContainer,
3248 nsIContent* aChild, int32_t aIndexInContainer,
3249 InsertedOrAppended aInsertedOrAppended)
3250 {
3251 if (!aChild) {
3252 return;
3253 }
3255 nsCOMPtr<nsIHTMLEditor> kungFuDeathGrip(this);
3257 if (ShouldReplaceRootElement()) {
3258 nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(
3259 this, &nsHTMLEditor::ResetRootElementAndEventTarget));
3260 }
3261 // We don't need to handle our own modifications
3262 else if (!mAction && (aContainer ? aContainer->IsEditable() : aDocument->IsEditable())) {
3263 if (IsMozEditorBogusNode(aChild)) {
3264 // Ignore insertion of the bogus node
3265 return;
3266 }
3267 // Protect the edit rules object from dying
3268 nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
3269 mRules->DocumentModified();
3271 // Update spellcheck for only the newly-inserted node (bug 743819)
3272 if (mInlineSpellChecker) {
3273 nsRefPtr<nsRange> range = new nsRange(aChild);
3274 int32_t endIndex = aIndexInContainer + 1;
3275 if (aInsertedOrAppended == eAppended) {
3276 // Count all the appended nodes
3277 nsIContent* sibling = aChild->GetNextSibling();
3278 while (sibling) {
3279 endIndex++;
3280 sibling = sibling->GetNextSibling();
3281 }
3282 }
3283 nsresult res = range->Set(aContainer, aIndexInContainer,
3284 aContainer, endIndex);
3285 if (NS_SUCCEEDED(res)) {
3286 mInlineSpellChecker->SpellCheckRange(range);
3287 }
3288 }
3289 }
3290 }
3292 void
3293 nsHTMLEditor::ContentRemoved(nsIDocument *aDocument, nsIContent* aContainer,
3294 nsIContent* aChild, int32_t aIndexInContainer,
3295 nsIContent* aPreviousSibling)
3296 {
3297 nsCOMPtr<nsIHTMLEditor> kungFuDeathGrip(this);
3299 if (SameCOMIdentity(aChild, mRootElement)) {
3300 nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(
3301 this, &nsHTMLEditor::ResetRootElementAndEventTarget));
3302 }
3303 // We don't need to handle our own modifications
3304 else if (!mAction && (aContainer ? aContainer->IsEditable() : aDocument->IsEditable())) {
3305 if (aChild && IsMozEditorBogusNode(aChild)) {
3306 // Ignore removal of the bogus node
3307 return;
3308 }
3309 // Protect the edit rules object from dying
3310 nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
3311 mRules->DocumentModified();
3312 }
3313 }
3315 NS_IMETHODIMP_(bool)
3316 nsHTMLEditor::IsModifiableNode(nsIDOMNode *aNode)
3317 {
3318 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
3319 return IsModifiableNode(node);
3320 }
3322 bool
3323 nsHTMLEditor::IsModifiableNode(nsINode *aNode)
3324 {
3325 return !aNode || aNode->IsEditable();
3326 }
3328 NS_IMETHODIMP
3329 nsHTMLEditor::GetIsSelectionEditable(bool* aIsSelectionEditable)
3330 {
3331 MOZ_ASSERT(aIsSelectionEditable);
3333 nsRefPtr<Selection> selection = GetSelection();
3334 NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
3336 // Per the editing spec as of June 2012: we have to have a selection whose
3337 // start and end nodes are editable, and which share an ancestor editing
3338 // host. (Bug 766387.)
3339 *aIsSelectionEditable = selection->GetRangeCount() &&
3340 selection->GetAnchorNode()->IsEditable() &&
3341 selection->GetFocusNode()->IsEditable();
3343 if (*aIsSelectionEditable) {
3344 nsINode* commonAncestor =
3345 selection->GetAnchorFocusRange()->GetCommonAncestor();
3346 while (commonAncestor && !commonAncestor->IsEditable()) {
3347 commonAncestor = commonAncestor->GetParentNode();
3348 }
3349 if (!commonAncestor) {
3350 // No editable common ancestor
3351 *aIsSelectionEditable = false;
3352 }
3353 }
3355 return NS_OK;
3356 }
3358 static nsresult
3359 SetSelectionAroundHeadChildren(nsISelection* aSelection,
3360 nsIWeakReference* aDocWeak)
3361 {
3362 // Set selection around <head> node
3363 nsCOMPtr<nsIDocument> doc = do_QueryReferent(aDocWeak);
3364 NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
3366 dom::Element* headNode = doc->GetHeadElement();
3367 NS_ENSURE_STATE(headNode);
3369 // Collapse selection to before first child of the head,
3370 nsresult rv = aSelection->CollapseNative(headNode, 0);
3371 NS_ENSURE_SUCCESS(rv, rv);
3373 // Then extend it to just after.
3374 uint32_t childCount = headNode->GetChildCount();
3375 return aSelection->ExtendNative(headNode, childCount + 1);
3376 }
3378 NS_IMETHODIMP
3379 nsHTMLEditor::GetHeadContentsAsHTML(nsAString& aOutputString)
3380 {
3381 nsRefPtr<Selection> selection = GetSelection();
3382 NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
3384 // Save current selection
3385 nsAutoSelectionReset selectionResetter(selection, this);
3387 nsresult res = SetSelectionAroundHeadChildren(selection, mDocWeak);
3388 NS_ENSURE_SUCCESS(res, res);
3390 res = OutputToString(NS_LITERAL_STRING("text/html"),
3391 nsIDocumentEncoder::OutputSelectionOnly,
3392 aOutputString);
3393 if (NS_SUCCEEDED(res))
3394 {
3395 // Selection always includes <body></body>,
3396 // so terminate there
3397 nsReadingIterator<char16_t> findIter,endFindIter;
3398 aOutputString.BeginReading(findIter);
3399 aOutputString.EndReading(endFindIter);
3400 //counting on our parser to always lower case!!!
3401 if (CaseInsensitiveFindInReadable(NS_LITERAL_STRING("<body"),
3402 findIter, endFindIter))
3403 {
3404 nsReadingIterator<char16_t> beginIter;
3405 aOutputString.BeginReading(beginIter);
3406 int32_t offset = Distance(beginIter, findIter);//get the distance
3408 nsWritingIterator<char16_t> writeIter;
3409 aOutputString.BeginWriting(writeIter);
3410 // Ensure the string ends in a newline
3411 char16_t newline ('\n');
3412 findIter.advance(-1);
3413 if (offset ==0 || (offset >0 && (*findIter) != newline)) //check for 0
3414 {
3415 writeIter.advance(offset);
3416 *writeIter = newline;
3417 aOutputString.Truncate(offset+1);
3418 }
3419 }
3420 }
3421 return res;
3422 }
3424 NS_IMETHODIMP
3425 nsHTMLEditor::DebugUnitTests(int32_t *outNumTests, int32_t *outNumTestsFailed)
3426 {
3427 #ifdef DEBUG
3428 NS_ENSURE_TRUE(outNumTests && outNumTestsFailed, NS_ERROR_NULL_POINTER);
3430 TextEditorTest *tester = new TextEditorTest();
3431 NS_ENSURE_TRUE(tester, NS_ERROR_OUT_OF_MEMORY);
3433 tester->Run(this, outNumTests, outNumTestsFailed);
3434 delete tester;
3435 return NS_OK;
3436 #else
3437 return NS_ERROR_NOT_IMPLEMENTED;
3438 #endif
3439 }
3442 NS_IMETHODIMP
3443 nsHTMLEditor::StyleSheetLoaded(nsCSSStyleSheet* aSheet, bool aWasAlternate,
3444 nsresult aStatus)
3445 {
3446 nsresult rv = NS_OK;
3447 nsAutoEditBatch batchIt(this);
3449 if (!mLastStyleSheetURL.IsEmpty())
3450 RemoveStyleSheet(mLastStyleSheetURL);
3452 nsRefPtr<AddStyleSheetTxn> txn;
3453 rv = CreateTxnForAddStyleSheet(aSheet, getter_AddRefs(txn));
3454 if (!txn) rv = NS_ERROR_NULL_POINTER;
3455 if (NS_SUCCEEDED(rv))
3456 {
3457 rv = DoTransaction(txn);
3458 if (NS_SUCCEEDED(rv))
3459 {
3460 // Get the URI, then url spec from the sheet
3461 nsAutoCString spec;
3462 rv = aSheet->GetSheetURI()->GetSpec(spec);
3464 if (NS_SUCCEEDED(rv))
3465 {
3466 // Save it so we can remove before applying the next one
3467 mLastStyleSheetURL.AssignWithConversion(spec.get());
3469 // Also save in our arrays of urls and sheets
3470 AddNewStyleSheetToList(mLastStyleSheetURL, aSheet);
3471 }
3472 }
3473 }
3475 return NS_OK;
3476 }
3479 /** All editor operations which alter the doc should be prefaced
3480 * with a call to StartOperation, naming the action and direction */
3481 NS_IMETHODIMP
3482 nsHTMLEditor::StartOperation(EditAction opID,
3483 nsIEditor::EDirection aDirection)
3484 {
3485 // Protect the edit rules object from dying
3486 nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
3488 nsEditor::StartOperation(opID, aDirection); // will set mAction, mDirection
3489 if (mRules) return mRules->BeforeEdit(mAction, mDirection);
3490 return NS_OK;
3491 }
3494 /** All editor operations which alter the doc should be followed
3495 * with a call to EndOperation */
3496 NS_IMETHODIMP
3497 nsHTMLEditor::EndOperation()
3498 {
3499 // Protect the edit rules object from dying
3500 nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
3502 // post processing
3503 nsresult res = NS_OK;
3504 if (mRules) res = mRules->AfterEdit(mAction, mDirection);
3505 nsEditor::EndOperation(); // will clear mAction, mDirection
3506 return res;
3507 }
3509 bool
3510 nsHTMLEditor::TagCanContainTag(nsIAtom* aParentTag, nsIAtom* aChildTag)
3511 {
3512 MOZ_ASSERT(aParentTag && aChildTag);
3514 nsIParserService* parserService = nsContentUtils::GetParserService();
3516 int32_t childTagEnum;
3517 // XXX Should this handle #cdata-section too?
3518 if (aChildTag == nsGkAtoms::textTagName) {
3519 childTagEnum = eHTMLTag_text;
3520 } else {
3521 childTagEnum = parserService->HTMLAtomTagToId(aChildTag);
3522 }
3524 int32_t parentTagEnum = parserService->HTMLAtomTagToId(aParentTag);
3525 return nsHTMLEditUtils::CanContain(parentTagEnum, childTagEnum);
3526 }
3528 bool
3529 nsHTMLEditor::IsContainer(nsIDOMNode *aNode)
3530 {
3531 if (!aNode) {
3532 return false;
3533 }
3535 nsAutoString stringTag;
3537 nsresult rv = aNode->GetNodeName(stringTag);
3538 NS_ENSURE_SUCCESS(rv, false);
3540 int32_t tagEnum;
3541 // XXX Should this handle #cdata-section too?
3542 if (stringTag.EqualsLiteral("#text")) {
3543 tagEnum = eHTMLTag_text;
3544 }
3545 else {
3546 tagEnum = nsContentUtils::GetParserService()->HTMLStringTagToId(stringTag);
3547 }
3549 return nsHTMLEditUtils::IsContainer(tagEnum);
3550 }
3553 NS_IMETHODIMP
3554 nsHTMLEditor::SelectEntireDocument(nsISelection *aSelection)
3555 {
3556 if (!aSelection || !mRules) { return NS_ERROR_NULL_POINTER; }
3558 // Protect the edit rules object from dying
3559 nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
3561 // get editor root node
3562 nsCOMPtr<nsIDOMElement> rootElement = do_QueryInterface(GetRoot());
3564 // is doc empty?
3565 bool bDocIsEmpty;
3566 nsresult res = mRules->DocumentIsEmpty(&bDocIsEmpty);
3567 NS_ENSURE_SUCCESS(res, res);
3569 if (bDocIsEmpty)
3570 {
3571 // if its empty dont select entire doc - that would select the bogus node
3572 return aSelection->Collapse(rootElement, 0);
3573 }
3575 return nsEditor::SelectEntireDocument(aSelection);
3576 }
3578 NS_IMETHODIMP
3579 nsHTMLEditor::SelectAll()
3580 {
3581 ForceCompositionEnd();
3583 nsresult rv;
3584 nsCOMPtr<nsISelectionController> selCon;
3585 rv = GetSelectionController(getter_AddRefs(selCon));
3586 NS_ENSURE_SUCCESS(rv, rv);
3588 nsCOMPtr<nsISelection> selection;
3589 rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
3590 getter_AddRefs(selection));
3591 NS_ENSURE_SUCCESS(rv, rv);
3593 nsCOMPtr<nsIDOMNode> anchorNode;
3594 rv = selection->GetAnchorNode(getter_AddRefs(anchorNode));
3595 NS_ENSURE_SUCCESS(rv, rv);
3597 nsCOMPtr<nsIContent> anchorContent = do_QueryInterface(anchorNode, &rv);
3598 NS_ENSURE_SUCCESS(rv, rv);
3600 // If the anchor content has independent selection, we never need to explicitly
3601 // select its children.
3602 if (anchorContent->HasIndependentSelection()) {
3603 nsCOMPtr<nsISelectionPrivate> selPriv = do_QueryInterface(selection);
3604 NS_ENSURE_TRUE(selPriv, NS_ERROR_UNEXPECTED);
3605 rv = selPriv->SetAncestorLimiter(nullptr);
3606 NS_ENSURE_SUCCESS(rv, rv);
3607 nsCOMPtr<nsIDOMNode> rootElement = do_QueryInterface(mRootElement, &rv);
3608 NS_ENSURE_SUCCESS(rv, rv);
3609 return selection->SelectAllChildren(rootElement);
3610 }
3612 nsCOMPtr<nsIPresShell> ps = GetPresShell();
3613 nsIContent *rootContent = anchorContent->GetSelectionRootContent(ps);
3614 NS_ENSURE_TRUE(rootContent, NS_ERROR_UNEXPECTED);
3616 nsCOMPtr<nsIDOMNode> rootElement = do_QueryInterface(rootContent, &rv);
3617 NS_ENSURE_SUCCESS(rv, rv);
3619 return selection->SelectAllChildren(rootElement);
3620 }
3623 // this will NOT find aAttribute unless aAttribute has a non-null value
3624 // so singleton attributes like <Table border> will not be matched!
3625 bool nsHTMLEditor::IsTextPropertySetByContent(nsIContent* aContent,
3626 nsIAtom* aProperty,
3627 const nsAString* aAttribute,
3628 const nsAString* aValue,
3629 nsAString* outValue)
3630 {
3631 MOZ_ASSERT(aContent && aProperty);
3632 MOZ_ASSERT_IF(aAttribute, aValue);
3633 bool isSet;
3634 IsTextPropertySetByContent(aContent->AsDOMNode(), aProperty, aAttribute,
3635 aValue, isSet, outValue);
3636 return isSet;
3637 }
3639 void nsHTMLEditor::IsTextPropertySetByContent(nsIDOMNode *aNode,
3640 nsIAtom *aProperty,
3641 const nsAString *aAttribute,
3642 const nsAString *aValue,
3643 bool &aIsSet,
3644 nsAString *outValue)
3645 {
3646 nsresult result;
3647 aIsSet = false; // must be initialized to false for code below to work
3648 nsAutoString propName;
3649 aProperty->ToString(propName);
3650 nsCOMPtr<nsIDOMNode>node = aNode;
3652 while (node)
3653 {
3654 nsCOMPtr<nsIDOMElement>element;
3655 element = do_QueryInterface(node);
3656 if (element)
3657 {
3658 nsAutoString tag, value;
3659 element->GetTagName(tag);
3660 if (propName.Equals(tag, nsCaseInsensitiveStringComparator()))
3661 {
3662 bool found = false;
3663 if (aAttribute && 0!=aAttribute->Length())
3664 {
3665 element->GetAttribute(*aAttribute, value);
3666 if (outValue) *outValue = value;
3667 if (!value.IsEmpty())
3668 {
3669 if (!aValue) {
3670 found = true;
3671 }
3672 else
3673 {
3674 nsString tString(*aValue);
3675 if (tString.Equals(value, nsCaseInsensitiveStringComparator())) {
3676 found = true;
3677 }
3678 else { // we found the prop with the attribute, but the value doesn't match
3679 break;
3680 }
3681 }
3682 }
3683 }
3684 else {
3685 found = true;
3686 }
3687 if (found)
3688 {
3689 aIsSet = true;
3690 break;
3691 }
3692 }
3693 }
3694 nsCOMPtr<nsIDOMNode>temp;
3695 result = node->GetParentNode(getter_AddRefs(temp));
3696 if (NS_SUCCEEDED(result) && temp) {
3697 node = temp;
3698 }
3699 else {
3700 node = nullptr;
3701 }
3702 }
3703 }
3706 //================================================================
3707 // HTML Editor methods
3708 //
3709 // Note: Table Editing methods are implemented in nsTableEditor.cpp
3710 //
3713 bool
3714 nsHTMLEditor::SetCaretInTableCell(nsIDOMElement* aElement)
3715 {
3716 nsCOMPtr<dom::Element> element = do_QueryInterface(aElement);
3717 if (!element || !element->IsHTML() ||
3718 !nsHTMLEditUtils::IsTableElement(element) ||
3719 !IsDescendantOfEditorRoot(element)) {
3720 return false;
3721 }
3723 nsIContent* node = element;
3724 while (node->HasChildren()) {
3725 node = node->GetFirstChild();
3726 }
3728 // Set selection at beginning of the found node
3729 nsCOMPtr<nsISelection> selection;
3730 nsresult rv = GetSelection(getter_AddRefs(selection));
3731 NS_ENSURE_SUCCESS(rv, false);
3732 NS_ENSURE_TRUE(selection, false);
3734 return NS_SUCCEEDED(selection->CollapseNative(node, 0));
3735 }
3737 ///////////////////////////////////////////////////////////////////////////
3738 // GetEnclosingTable: find ancestor who is a table, if any
3739 //
3740 nsCOMPtr<nsIDOMNode>
3741 nsHTMLEditor::GetEnclosingTable(nsIDOMNode *aNode)
3742 {
3743 NS_PRECONDITION(aNode, "null node passed to nsHTMLEditor::GetEnclosingTable");
3744 nsCOMPtr<nsIDOMNode> tbl, tmp, node = aNode;
3746 while (!tbl)
3747 {
3748 tmp = GetBlockNodeParent(node);
3749 if (!tmp) break;
3750 if (nsHTMLEditUtils::IsTable(tmp)) tbl = tmp;
3751 node = tmp;
3752 }
3753 return tbl;
3754 }
3757 /* this method scans the selection for adjacent text nodes
3758 * and collapses them into a single text node.
3759 * "adjacent" means literally adjacent siblings of the same parent.
3760 * Uses nsEditor::JoinNodes so action is undoable.
3761 * Should be called within the context of a batch transaction.
3762 */
3763 NS_IMETHODIMP
3764 nsHTMLEditor::CollapseAdjacentTextNodes(nsIDOMRange *aInRange)
3765 {
3766 NS_ENSURE_TRUE(aInRange, NS_ERROR_NULL_POINTER);
3767 nsAutoTxnsConserveSelection dontSpazMySelection(this);
3768 nsTArray<nsCOMPtr<nsIDOMNode> > textNodes;
3769 // we can't actually do anything during iteration, so store the text nodes in an array
3770 // don't bother ref counting them because we know we can hold them for the
3771 // lifetime of this method
3774 // build a list of editable text nodes
3775 nsresult result;
3776 nsCOMPtr<nsIContentIterator> iter =
3777 do_CreateInstance("@mozilla.org/content/subtree-content-iterator;1", &result);
3778 NS_ENSURE_SUCCESS(result, result);
3780 iter->Init(aInRange);
3782 while (!iter->IsDone())
3783 {
3784 nsINode* node = iter->GetCurrentNode();
3785 if (node->NodeType() == nsIDOMNode::TEXT_NODE &&
3786 IsEditable(static_cast<nsIContent*>(node))) {
3787 nsCOMPtr<nsIDOMNode> domNode = do_QueryInterface(node);
3788 textNodes.AppendElement(domNode);
3789 }
3791 iter->Next();
3792 }
3794 // now that I have a list of text nodes, collapse adjacent text nodes
3795 // NOTE: assumption that JoinNodes keeps the righthand node
3796 while (textNodes.Length() > 1)
3797 {
3798 // we assume a textNodes entry can't be nullptr
3799 nsIDOMNode *leftTextNode = textNodes[0];
3800 nsIDOMNode *rightTextNode = textNodes[1];
3801 NS_ASSERTION(leftTextNode && rightTextNode,"left or rightTextNode null in CollapseAdjacentTextNodes");
3803 // get the prev sibling of the right node, and see if its leftTextNode
3804 nsCOMPtr<nsIDOMNode> prevSibOfRightNode;
3805 result =
3806 rightTextNode->GetPreviousSibling(getter_AddRefs(prevSibOfRightNode));
3807 NS_ENSURE_SUCCESS(result, result);
3808 if (prevSibOfRightNode && (prevSibOfRightNode == leftTextNode))
3809 {
3810 nsCOMPtr<nsIDOMNode> parent;
3811 result = rightTextNode->GetParentNode(getter_AddRefs(parent));
3812 NS_ENSURE_SUCCESS(result, result);
3813 NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
3814 result = JoinNodes(leftTextNode, rightTextNode, parent);
3815 NS_ENSURE_SUCCESS(result, result);
3816 }
3818 textNodes.RemoveElementAt(0); // remove the leftmost text node from the list
3819 }
3821 return result;
3822 }
3824 NS_IMETHODIMP
3825 nsHTMLEditor::SetSelectionAtDocumentStart(nsISelection *aSelection)
3826 {
3827 dom::Element* rootElement = GetRoot();
3828 NS_ENSURE_TRUE(rootElement, NS_ERROR_NULL_POINTER);
3830 return aSelection->CollapseNative(rootElement, 0);
3831 }
3834 ///////////////////////////////////////////////////////////////////////////
3835 // RemoveBlockContainer: remove inNode, reparenting its children into their
3836 // the parent of inNode. In addition, INSERT ANY BR's NEEDED
3837 // TO PRESERVE IDENTITY OF REMOVED BLOCK.
3838 //
3839 nsresult
3840 nsHTMLEditor::RemoveBlockContainer(nsIDOMNode *inNode)
3841 {
3842 NS_ENSURE_TRUE(inNode, NS_ERROR_NULL_POINTER);
3843 nsresult res;
3844 nsCOMPtr<nsIDOMNode> sibling, child, unused;
3846 // Two possibilities: the container cold be empty of editable content.
3847 // If that is the case, we need to compare what is before and after inNode
3848 // to determine if we need a br.
3849 // Or it could not be empty, in which case we have to compare previous
3850 // sibling and first child to determine if we need a leading br,
3851 // and compare following sibling and last child to determine if we need a
3852 // trailing br.
3854 res = GetFirstEditableChild(inNode, address_of(child));
3855 NS_ENSURE_SUCCESS(res, res);
3857 if (child) // the case of inNode not being empty
3858 {
3859 // we need a br at start unless:
3860 // 1) previous sibling of inNode is a block, OR
3861 // 2) previous sibling of inNode is a br, OR
3862 // 3) first child of inNode is a block OR
3863 // 4) either is null
3865 res = GetPriorHTMLSibling(inNode, address_of(sibling));
3866 NS_ENSURE_SUCCESS(res, res);
3867 if (sibling && !IsBlockNode(sibling) && !nsTextEditUtils::IsBreak(sibling))
3868 {
3869 res = GetFirstEditableChild(inNode, address_of(child));
3870 NS_ENSURE_SUCCESS(res, res);
3871 if (child && !IsBlockNode(child))
3872 {
3873 // insert br node
3874 res = CreateBR(inNode, 0, address_of(unused));
3875 NS_ENSURE_SUCCESS(res, res);
3876 }
3877 }
3879 // we need a br at end unless:
3880 // 1) following sibling of inNode is a block, OR
3881 // 2) last child of inNode is a block, OR
3882 // 3) last child of inNode is a block OR
3883 // 4) either is null
3885 res = GetNextHTMLSibling(inNode, address_of(sibling));
3886 NS_ENSURE_SUCCESS(res, res);
3887 if (sibling && !IsBlockNode(sibling))
3888 {
3889 res = GetLastEditableChild(inNode, address_of(child));
3890 NS_ENSURE_SUCCESS(res, res);
3891 if (child && !IsBlockNode(child) && !nsTextEditUtils::IsBreak(child))
3892 {
3893 // insert br node
3894 uint32_t len;
3895 res = GetLengthOfDOMNode(inNode, len);
3896 NS_ENSURE_SUCCESS(res, res);
3897 res = CreateBR(inNode, (int32_t)len, address_of(unused));
3898 NS_ENSURE_SUCCESS(res, res);
3899 }
3900 }
3901 }
3902 else // the case of inNode being empty
3903 {
3904 // we need a br at start unless:
3905 // 1) previous sibling of inNode is a block, OR
3906 // 2) previous sibling of inNode is a br, OR
3907 // 3) following sibling of inNode is a block, OR
3908 // 4) following sibling of inNode is a br OR
3909 // 5) either is null
3910 res = GetPriorHTMLSibling(inNode, address_of(sibling));
3911 NS_ENSURE_SUCCESS(res, res);
3912 if (sibling && !IsBlockNode(sibling) && !nsTextEditUtils::IsBreak(sibling))
3913 {
3914 res = GetNextHTMLSibling(inNode, address_of(sibling));
3915 NS_ENSURE_SUCCESS(res, res);
3916 if (sibling && !IsBlockNode(sibling) && !nsTextEditUtils::IsBreak(sibling))
3917 {
3918 // insert br node
3919 res = CreateBR(inNode, 0, address_of(unused));
3920 NS_ENSURE_SUCCESS(res, res);
3921 }
3922 }
3923 }
3925 // now remove container
3926 return RemoveContainer(inNode);
3927 }
3930 ///////////////////////////////////////////////////////////////////////////
3931 // GetPriorHTMLSibling: returns the previous editable sibling, if there is
3932 // one within the parent
3933 //
3934 nsIContent*
3935 nsHTMLEditor::GetPriorHTMLSibling(nsINode* aNode)
3936 {
3937 MOZ_ASSERT(aNode);
3939 nsIContent* node = aNode->GetPreviousSibling();
3940 while (node && !IsEditable(node)) {
3941 node = node->GetPreviousSibling();
3942 }
3944 return node;
3945 }
3947 nsresult
3948 nsHTMLEditor::GetPriorHTMLSibling(nsIDOMNode *inNode, nsCOMPtr<nsIDOMNode> *outNode)
3949 {
3950 NS_ENSURE_TRUE(outNode, NS_ERROR_NULL_POINTER);
3951 *outNode = nullptr;
3953 nsCOMPtr<nsINode> node = do_QueryInterface(inNode);
3954 NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
3956 *outNode = do_QueryInterface(GetPriorHTMLSibling(node));
3957 return NS_OK;
3958 }
3962 ///////////////////////////////////////////////////////////////////////////
3963 // GetPriorHTMLSibling: returns the previous editable sibling, if there is
3964 // one within the parent. just like above routine but
3965 // takes a parent/offset instead of a node.
3966 //
3967 nsIContent*
3968 nsHTMLEditor::GetPriorHTMLSibling(nsINode* aParent, int32_t aOffset)
3969 {
3970 MOZ_ASSERT(aParent);
3972 nsIContent* node = aParent->GetChildAt(aOffset - 1);
3973 if (!node || IsEditable(node)) {
3974 return node;
3975 }
3977 return GetPriorHTMLSibling(node);
3978 }
3980 nsresult
3981 nsHTMLEditor::GetPriorHTMLSibling(nsIDOMNode *inParent, int32_t inOffset, nsCOMPtr<nsIDOMNode> *outNode)
3982 {
3983 NS_ENSURE_TRUE(outNode, NS_ERROR_NULL_POINTER);
3984 *outNode = nullptr;
3986 nsCOMPtr<nsINode> parent = do_QueryInterface(inParent);
3987 NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
3989 *outNode = do_QueryInterface(GetPriorHTMLSibling(parent, inOffset));
3990 return NS_OK;
3991 }
3995 ///////////////////////////////////////////////////////////////////////////
3996 // GetNextHTMLSibling: returns the next editable sibling, if there is
3997 // one within the parent
3998 //
3999 nsIContent*
4000 nsHTMLEditor::GetNextHTMLSibling(nsINode* aNode)
4001 {
4002 MOZ_ASSERT(aNode);
4004 nsIContent* node = aNode->GetNextSibling();
4005 while (node && !IsEditable(node)) {
4006 node = node->GetNextSibling();
4007 }
4009 return node;
4010 }
4012 nsresult
4013 nsHTMLEditor::GetNextHTMLSibling(nsIDOMNode *inNode, nsCOMPtr<nsIDOMNode> *outNode)
4014 {
4015 NS_ENSURE_TRUE(outNode, NS_ERROR_NULL_POINTER);
4016 *outNode = nullptr;
4018 nsCOMPtr<nsINode> node = do_QueryInterface(inNode);
4019 NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
4021 *outNode = do_QueryInterface(GetNextHTMLSibling(node));
4022 return NS_OK;
4023 }
4027 ///////////////////////////////////////////////////////////////////////////
4028 // GetNextHTMLSibling: returns the next editable sibling, if there is
4029 // one within the parent. just like above routine but
4030 // takes a parent/offset instead of a node.
4031 nsIContent*
4032 nsHTMLEditor::GetNextHTMLSibling(nsINode* aParent, int32_t aOffset)
4033 {
4034 MOZ_ASSERT(aParent);
4036 nsIContent* node = aParent->GetChildAt(aOffset + 1);
4037 if (!node || IsEditable(node)) {
4038 return node;
4039 }
4041 return GetNextHTMLSibling(node);
4042 }
4044 nsresult
4045 nsHTMLEditor::GetNextHTMLSibling(nsIDOMNode *inParent, int32_t inOffset, nsCOMPtr<nsIDOMNode> *outNode)
4046 {
4047 NS_ENSURE_TRUE(outNode, NS_ERROR_NULL_POINTER);
4048 *outNode = nullptr;
4050 nsCOMPtr<nsINode> parent = do_QueryInterface(inParent);
4051 NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
4053 *outNode = do_QueryInterface(GetNextHTMLSibling(parent, inOffset));
4054 return NS_OK;
4055 }
4059 ///////////////////////////////////////////////////////////////////////////
4060 // GetPriorHTMLNode: returns the previous editable leaf node, if there is
4061 // one within the <body>
4062 //
4063 nsIContent*
4064 nsHTMLEditor::GetPriorHTMLNode(nsINode* aNode, bool aNoBlockCrossing)
4065 {
4066 MOZ_ASSERT(aNode);
4068 if (!GetActiveEditingHost()) {
4069 return nullptr;
4070 }
4072 return GetPriorNode(aNode, true, aNoBlockCrossing);
4073 }
4075 nsresult
4076 nsHTMLEditor::GetPriorHTMLNode(nsIDOMNode* aNode,
4077 nsCOMPtr<nsIDOMNode>* aResultNode,
4078 bool aNoBlockCrossing)
4079 {
4080 NS_ENSURE_TRUE(aResultNode, NS_ERROR_NULL_POINTER);
4082 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
4083 NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
4085 *aResultNode = do_QueryInterface(GetPriorHTMLNode(node, aNoBlockCrossing));
4086 return NS_OK;
4087 }
4090 ///////////////////////////////////////////////////////////////////////////
4091 // GetPriorHTMLNode: same as above but takes {parent,offset} instead of node
4092 //
4093 nsIContent*
4094 nsHTMLEditor::GetPriorHTMLNode(nsINode* aParent, int32_t aOffset,
4095 bool aNoBlockCrossing)
4096 {
4097 MOZ_ASSERT(aParent);
4099 if (!GetActiveEditingHost()) {
4100 return nullptr;
4101 }
4103 return GetPriorNode(aParent, aOffset, true, aNoBlockCrossing);
4104 }
4106 nsresult
4107 nsHTMLEditor::GetPriorHTMLNode(nsIDOMNode* aNode, int32_t aOffset,
4108 nsCOMPtr<nsIDOMNode>* aResultNode,
4109 bool aNoBlockCrossing)
4110 {
4111 NS_ENSURE_TRUE(aResultNode, NS_ERROR_NULL_POINTER);
4113 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
4114 NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
4116 *aResultNode = do_QueryInterface(GetPriorHTMLNode(node, aOffset,
4117 aNoBlockCrossing));
4118 return NS_OK;
4119 }
4122 ///////////////////////////////////////////////////////////////////////////
4123 // GetNextHTMLNode: returns the next editable leaf node, if there is
4124 // one within the <body>
4125 //
4126 nsIContent*
4127 nsHTMLEditor::GetNextHTMLNode(nsINode* aNode, bool aNoBlockCrossing)
4128 {
4129 MOZ_ASSERT(aNode);
4131 nsIContent* result = GetNextNode(aNode, true, aNoBlockCrossing);
4133 if (result && !IsDescendantOfEditorRoot(result)) {
4134 return nullptr;
4135 }
4137 return result;
4138 }
4140 nsresult
4141 nsHTMLEditor::GetNextHTMLNode(nsIDOMNode* aNode,
4142 nsCOMPtr<nsIDOMNode>* aResultNode,
4143 bool aNoBlockCrossing)
4144 {
4145 NS_ENSURE_TRUE(aResultNode, NS_ERROR_NULL_POINTER);
4147 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
4148 NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
4150 *aResultNode = do_QueryInterface(GetNextHTMLNode(node, aNoBlockCrossing));
4151 return NS_OK;
4152 }
4155 ///////////////////////////////////////////////////////////////////////////
4156 // GetNextHTMLNode: same as above but takes {parent,offset} instead of node
4157 //
4158 nsIContent*
4159 nsHTMLEditor::GetNextHTMLNode(nsINode* aParent, int32_t aOffset,
4160 bool aNoBlockCrossing)
4161 {
4162 nsIContent* content = GetNextNode(aParent, aOffset, true, aNoBlockCrossing);
4163 if (content && !IsDescendantOfEditorRoot(content)) {
4164 return nullptr;
4165 }
4166 return content;
4167 }
4169 nsresult
4170 nsHTMLEditor::GetNextHTMLNode(nsIDOMNode* aNode, int32_t aOffset,
4171 nsCOMPtr<nsIDOMNode>* aResultNode,
4172 bool aNoBlockCrossing)
4173 {
4174 NS_ENSURE_TRUE(aResultNode, NS_ERROR_NULL_POINTER);
4176 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
4177 NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
4179 *aResultNode = do_QueryInterface(GetNextHTMLNode(node, aOffset,
4180 aNoBlockCrossing));
4181 return NS_OK;
4182 }
4185 nsresult
4186 nsHTMLEditor::IsFirstEditableChild( nsIDOMNode *aNode, bool *aOutIsFirst)
4187 {
4188 // check parms
4189 NS_ENSURE_TRUE(aOutIsFirst && aNode, NS_ERROR_NULL_POINTER);
4191 // init out parms
4192 *aOutIsFirst = false;
4194 // find first editable child and compare it to aNode
4195 nsCOMPtr<nsIDOMNode> parent, firstChild;
4196 nsresult res = aNode->GetParentNode(getter_AddRefs(parent));
4197 NS_ENSURE_SUCCESS(res, res);
4198 NS_ENSURE_TRUE(parent, NS_ERROR_FAILURE);
4199 res = GetFirstEditableChild(parent, address_of(firstChild));
4200 NS_ENSURE_SUCCESS(res, res);
4202 *aOutIsFirst = (firstChild.get() == aNode);
4203 return res;
4204 }
4207 nsresult
4208 nsHTMLEditor::IsLastEditableChild( nsIDOMNode *aNode, bool *aOutIsLast)
4209 {
4210 // check parms
4211 NS_ENSURE_TRUE(aOutIsLast && aNode, NS_ERROR_NULL_POINTER);
4213 // init out parms
4214 *aOutIsLast = false;
4216 // find last editable child and compare it to aNode
4217 nsCOMPtr<nsIDOMNode> parent, lastChild;
4218 nsresult res = aNode->GetParentNode(getter_AddRefs(parent));
4219 NS_ENSURE_SUCCESS(res, res);
4220 NS_ENSURE_TRUE(parent, NS_ERROR_FAILURE);
4221 res = GetLastEditableChild(parent, address_of(lastChild));
4222 NS_ENSURE_SUCCESS(res, res);
4224 *aOutIsLast = (lastChild.get() == aNode);
4225 return res;
4226 }
4229 nsresult
4230 nsHTMLEditor::GetFirstEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutFirstChild)
4231 {
4232 // check parms
4233 NS_ENSURE_TRUE(aOutFirstChild && aNode, NS_ERROR_NULL_POINTER);
4235 // init out parms
4236 *aOutFirstChild = nullptr;
4238 // find first editable child
4239 nsCOMPtr<nsIDOMNode> child;
4240 nsresult res = aNode->GetFirstChild(getter_AddRefs(child));
4241 NS_ENSURE_SUCCESS(res, res);
4243 while (child && !IsEditable(child))
4244 {
4245 nsCOMPtr<nsIDOMNode> tmp;
4246 res = child->GetNextSibling(getter_AddRefs(tmp));
4247 NS_ENSURE_SUCCESS(res, res);
4248 NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE);
4249 child = tmp;
4250 }
4252 *aOutFirstChild = child;
4253 return res;
4254 }
4257 nsresult
4258 nsHTMLEditor::GetLastEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutLastChild)
4259 {
4260 // check parms
4261 NS_ENSURE_TRUE(aOutLastChild && aNode, NS_ERROR_NULL_POINTER);
4263 // init out parms
4264 *aOutLastChild = aNode;
4266 // find last editable child
4267 nsCOMPtr<nsIDOMNode> child;
4268 nsresult res = aNode->GetLastChild(getter_AddRefs(child));
4269 NS_ENSURE_SUCCESS(res, res);
4271 while (child && !IsEditable(child))
4272 {
4273 nsCOMPtr<nsIDOMNode> tmp;
4274 res = child->GetPreviousSibling(getter_AddRefs(tmp));
4275 NS_ENSURE_SUCCESS(res, res);
4276 NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE);
4277 child = tmp;
4278 }
4280 *aOutLastChild = child;
4281 return res;
4282 }
4284 nsresult
4285 nsHTMLEditor::GetFirstEditableLeaf( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutFirstLeaf)
4286 {
4287 // check parms
4288 NS_ENSURE_TRUE(aOutFirstLeaf && aNode, NS_ERROR_NULL_POINTER);
4290 // init out parms
4291 *aOutFirstLeaf = aNode;
4293 // find leftmost leaf
4294 nsCOMPtr<nsIDOMNode> child;
4295 nsresult res = NS_OK;
4296 child = GetLeftmostChild(aNode);
4297 while (child && (!IsEditable(child) || !nsEditorUtils::IsLeafNode(child)))
4298 {
4299 nsCOMPtr<nsIDOMNode> tmp;
4300 res = GetNextHTMLNode(child, address_of(tmp));
4301 NS_ENSURE_SUCCESS(res, res);
4302 NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE);
4304 // only accept nodes that are descendants of aNode
4305 if (nsEditorUtils::IsDescendantOf(tmp, aNode))
4306 child = tmp;
4307 else
4308 {
4309 child = nullptr; // this will abort the loop
4310 }
4311 }
4313 *aOutFirstLeaf = child;
4314 return res;
4315 }
4318 nsresult
4319 nsHTMLEditor::GetLastEditableLeaf(nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutLastLeaf)
4320 {
4321 // check parms
4322 NS_ENSURE_TRUE(aOutLastLeaf && aNode, NS_ERROR_NULL_POINTER);
4324 // init out parms
4325 *aOutLastLeaf = nullptr;
4327 // find rightmost leaf
4328 nsCOMPtr<nsIDOMNode> child = GetRightmostChild(aNode, false);
4329 nsresult res = NS_OK;
4330 while (child && (!IsEditable(child) || !nsEditorUtils::IsLeafNode(child)))
4331 {
4332 nsCOMPtr<nsIDOMNode> tmp;
4333 res = GetPriorHTMLNode(child, address_of(tmp));
4334 NS_ENSURE_SUCCESS(res, res);
4335 NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE);
4337 // only accept nodes that are descendants of aNode
4338 if (nsEditorUtils::IsDescendantOf(tmp, aNode))
4339 child = tmp;
4340 else
4341 {
4342 child = nullptr;
4343 }
4344 }
4346 *aOutLastLeaf = child;
4347 return res;
4348 }
4351 ///////////////////////////////////////////////////////////////////////////
4352 // IsVisTextNode: figure out if textnode aTextNode has any visible content.
4353 //
4354 nsresult
4355 nsHTMLEditor::IsVisTextNode(nsIContent* aNode,
4356 bool* outIsEmptyNode,
4357 bool aSafeToAskFrames)
4358 {
4359 MOZ_ASSERT(aNode);
4360 MOZ_ASSERT(aNode->NodeType() == nsIDOMNode::TEXT_NODE);
4361 MOZ_ASSERT(outIsEmptyNode);
4363 *outIsEmptyNode = true;
4365 uint32_t length = aNode->TextLength();
4366 if (aSafeToAskFrames)
4367 {
4368 nsCOMPtr<nsISelectionController> selCon;
4369 nsresult res = GetSelectionController(getter_AddRefs(selCon));
4370 NS_ENSURE_SUCCESS(res, res);
4371 NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE);
4372 bool isVisible = false;
4373 // ask the selection controller for information about whether any
4374 // of the data in the node is really rendered. This is really
4375 // something that frames know about, but we aren't supposed to talk to frames.
4376 // So we put a call in the selection controller interface, since it's already
4377 // in bed with frames anyway. (this is a fix for bug 22227, and a
4378 // partial fix for bug 46209)
4379 res = selCon->CheckVisibilityContent(aNode, 0, length, &isVisible);
4380 NS_ENSURE_SUCCESS(res, res);
4381 if (isVisible)
4382 {
4383 *outIsEmptyNode = false;
4384 }
4385 }
4386 else if (length)
4387 {
4388 if (aNode->TextIsOnlyWhitespace())
4389 {
4390 nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode);
4391 nsWSRunObject wsRunObj(this, node, 0);
4392 nsCOMPtr<nsIDOMNode> visNode;
4393 int32_t outVisOffset=0;
4394 WSType visType;
4395 wsRunObj.NextVisibleNode(node, 0, address_of(visNode),
4396 &outVisOffset, &visType);
4397 if (visType == WSType::normalWS || visType == WSType::text) {
4398 *outIsEmptyNode = (node != visNode);
4399 }
4400 }
4401 else
4402 {
4403 *outIsEmptyNode = false;
4404 }
4405 }
4406 return NS_OK;
4407 }
4410 ///////////////////////////////////////////////////////////////////////////
4411 // IsEmptyNode: figure out if aNode is an empty node.
4412 // A block can have children and still be considered empty,
4413 // if the children are empty or non-editable.
4414 //
4415 nsresult
4416 nsHTMLEditor::IsEmptyNode( nsIDOMNode *aNode,
4417 bool *outIsEmptyNode,
4418 bool aSingleBRDoesntCount,
4419 bool aListOrCellNotEmpty,
4420 bool aSafeToAskFrames)
4421 {
4422 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
4423 return IsEmptyNode(node, outIsEmptyNode, aSingleBRDoesntCount,
4424 aListOrCellNotEmpty, aSafeToAskFrames);
4425 }
4427 nsresult
4428 nsHTMLEditor::IsEmptyNode(nsINode* aNode,
4429 bool* outIsEmptyNode,
4430 bool aSingleBRDoesntCount,
4431 bool aListOrCellNotEmpty,
4432 bool aSafeToAskFrames)
4433 {
4434 NS_ENSURE_TRUE(aNode && outIsEmptyNode, NS_ERROR_NULL_POINTER);
4435 *outIsEmptyNode = true;
4436 bool seenBR = false;
4437 return IsEmptyNodeImpl(aNode, outIsEmptyNode, aSingleBRDoesntCount,
4438 aListOrCellNotEmpty, aSafeToAskFrames, &seenBR);
4439 }
4441 ///////////////////////////////////////////////////////////////////////////
4442 // IsEmptyNodeImpl: workhorse for IsEmptyNode.
4443 //
4444 nsresult
4445 nsHTMLEditor::IsEmptyNodeImpl(nsINode* aNode,
4446 bool *outIsEmptyNode,
4447 bool aSingleBRDoesntCount,
4448 bool aListOrCellNotEmpty,
4449 bool aSafeToAskFrames,
4450 bool *aSeenBR)
4451 {
4452 NS_ENSURE_TRUE(aNode && outIsEmptyNode && aSeenBR, NS_ERROR_NULL_POINTER);
4454 if (aNode->NodeType() == nsIDOMNode::TEXT_NODE) {
4455 return IsVisTextNode(static_cast<nsIContent*>(aNode), outIsEmptyNode, aSafeToAskFrames);
4456 }
4458 // if it's not a text node (handled above) and it's not a container,
4459 // then we don't call it empty (it's an <hr>, or <br>, etc).
4460 // Also, if it's an anchor then don't treat it as empty - even though
4461 // anchors are containers, named anchors are "empty" but we don't
4462 // want to treat them as such. Also, don't call ListItems or table
4463 // cells empty if caller desires. Form Widgets not empty.
4464 if (!IsContainer(aNode->AsDOMNode()) ||
4465 (nsHTMLEditUtils::IsNamedAnchor(aNode) ||
4466 nsHTMLEditUtils::IsFormWidget(aNode) ||
4467 (aListOrCellNotEmpty &&
4468 (nsHTMLEditUtils::IsListItem(aNode) ||
4469 nsHTMLEditUtils::IsTableCell(aNode))))) {
4470 *outIsEmptyNode = false;
4471 return NS_OK;
4472 }
4474 // need this for later
4475 bool isListItemOrCell = nsHTMLEditUtils::IsListItem(aNode) ||
4476 nsHTMLEditUtils::IsTableCell(aNode);
4478 // loop over children of node. if no children, or all children are either
4479 // empty text nodes or non-editable, then node qualifies as empty
4480 for (nsCOMPtr<nsIContent> child = aNode->GetFirstChild();
4481 child;
4482 child = child->GetNextSibling()) {
4483 // Is the child editable and non-empty? if so, return false
4484 if (nsEditor::IsEditable(child)) {
4485 if (child->NodeType() == nsIDOMNode::TEXT_NODE) {
4486 nsresult rv = IsVisTextNode(child, outIsEmptyNode, aSafeToAskFrames);
4487 NS_ENSURE_SUCCESS(rv, rv);
4488 // break out if we find we aren't emtpy
4489 if (!*outIsEmptyNode) {
4490 return NS_OK;
4491 }
4492 } else {
4493 // An editable, non-text node. We need to check its content.
4494 // Is it the node we are iterating over?
4495 if (child == aNode) {
4496 break;
4497 }
4499 if (aSingleBRDoesntCount && !*aSeenBR && child->IsHTML(nsGkAtoms::br)) {
4500 // the first br in a block doesn't count if the caller so indicated
4501 *aSeenBR = true;
4502 } else {
4503 // is it an empty node of some sort?
4504 // note: list items or table cells are not considered empty
4505 // if they contain other lists or tables
4506 if (child->IsElement()) {
4507 if (isListItemOrCell) {
4508 if (nsHTMLEditUtils::IsList(child) ||
4509 child->IsHTML(nsGkAtoms::table)) {
4510 // break out if we find we aren't empty
4511 *outIsEmptyNode = false;
4512 return NS_OK;
4513 }
4514 } else if (nsHTMLEditUtils::IsFormWidget(child)) {
4515 // is it a form widget?
4516 // break out if we find we aren't empty
4517 *outIsEmptyNode = false;
4518 return NS_OK;
4519 }
4520 }
4522 bool isEmptyNode = true;
4523 nsresult rv = IsEmptyNodeImpl(child, &isEmptyNode,
4524 aSingleBRDoesntCount,
4525 aListOrCellNotEmpty, aSafeToAskFrames,
4526 aSeenBR);
4527 NS_ENSURE_SUCCESS(rv, rv);
4528 if (!isEmptyNode) {
4529 // otherwise it ain't empty
4530 *outIsEmptyNode = false;
4531 return NS_OK;
4532 }
4533 }
4534 }
4535 }
4536 }
4538 return NS_OK;
4539 }
4541 // add to aElement the CSS inline styles corresponding to the HTML attribute
4542 // aAttribute with its value aValue
4543 nsresult
4544 nsHTMLEditor::SetAttributeOrEquivalent(nsIDOMElement * aElement,
4545 const nsAString & aAttribute,
4546 const nsAString & aValue,
4547 bool aSuppressTransaction)
4548 {
4549 nsAutoScriptBlocker scriptBlocker;
4551 nsresult res = NS_OK;
4552 if (IsCSSEnabled() && mHTMLCSSUtils) {
4553 int32_t count;
4554 res = mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(aElement, nullptr, &aAttribute, &aValue, &count,
4555 aSuppressTransaction);
4556 NS_ENSURE_SUCCESS(res, res);
4557 if (count) {
4558 // we found an equivalence ; let's remove the HTML attribute itself if it is set
4559 nsAutoString existingValue;
4560 bool wasSet = false;
4561 res = GetAttributeValue(aElement, aAttribute, existingValue, &wasSet);
4562 NS_ENSURE_SUCCESS(res, res);
4563 if (wasSet) {
4564 if (aSuppressTransaction)
4565 res = aElement->RemoveAttribute(aAttribute);
4566 else
4567 res = RemoveAttribute(aElement, aAttribute);
4568 }
4569 }
4570 else {
4571 // count is an integer that represents the number of CSS declarations applied to the
4572 // element. If it is zero, we found no equivalence in this implementation for the
4573 // attribute
4574 if (aAttribute.EqualsLiteral("style")) {
4575 // if it is the style attribute, just add the new value to the existing style
4576 // attribute's value
4577 nsAutoString existingValue;
4578 bool wasSet = false;
4579 res = GetAttributeValue(aElement, NS_LITERAL_STRING("style"), existingValue, &wasSet);
4580 NS_ENSURE_SUCCESS(res, res);
4581 existingValue.AppendLiteral(" ");
4582 existingValue.Append(aValue);
4583 if (aSuppressTransaction)
4584 res = aElement->SetAttribute(aAttribute, existingValue);
4585 else
4586 res = SetAttribute(aElement, aAttribute, existingValue);
4587 }
4588 else {
4589 // we have no CSS equivalence for this attribute and it is not the style
4590 // attribute; let's set it the good'n'old HTML way
4591 if (aSuppressTransaction)
4592 res = aElement->SetAttribute(aAttribute, aValue);
4593 else
4594 res = SetAttribute(aElement, aAttribute, aValue);
4595 }
4596 }
4597 }
4598 else {
4599 // we are not in an HTML+CSS editor; let's set the attribute the HTML way
4600 if (aSuppressTransaction)
4601 res = aElement->SetAttribute(aAttribute, aValue);
4602 else
4603 res = SetAttribute(aElement, aAttribute, aValue);
4604 }
4605 return res;
4606 }
4608 nsresult
4609 nsHTMLEditor::RemoveAttributeOrEquivalent(nsIDOMElement* aElement,
4610 const nsAString& aAttribute,
4611 bool aSuppressTransaction)
4612 {
4613 nsCOMPtr<dom::Element> element = do_QueryInterface(aElement);
4614 NS_ENSURE_TRUE(element, NS_OK);
4616 nsCOMPtr<nsIAtom> attribute = do_GetAtom(aAttribute);
4617 MOZ_ASSERT(attribute);
4619 nsresult res = NS_OK;
4620 if (IsCSSEnabled() && mHTMLCSSUtils) {
4621 res = mHTMLCSSUtils->RemoveCSSEquivalentToHTMLStyle(
4622 element, nullptr, &aAttribute, nullptr, aSuppressTransaction);
4623 NS_ENSURE_SUCCESS(res, res);
4624 }
4626 if (element->HasAttr(kNameSpaceID_None, attribute)) {
4627 if (aSuppressTransaction) {
4628 res = element->UnsetAttr(kNameSpaceID_None, attribute,
4629 /* aNotify = */ true);
4630 } else {
4631 res = RemoveAttribute(aElement, aAttribute);
4632 }
4633 }
4634 return res;
4635 }
4637 nsresult
4638 nsHTMLEditor::SetIsCSSEnabled(bool aIsCSSPrefChecked)
4639 {
4640 if (!mHTMLCSSUtils) {
4641 return NS_ERROR_NOT_INITIALIZED;
4642 }
4644 mHTMLCSSUtils->SetCSSEnabled(aIsCSSPrefChecked);
4646 // Disable the eEditorNoCSSMask flag if we're enabling StyleWithCSS.
4647 uint32_t flags = mFlags;
4648 if (aIsCSSPrefChecked) {
4649 // Turn off NoCSS as we're enabling CSS
4650 flags &= ~eEditorNoCSSMask;
4651 } else {
4652 // Turn on NoCSS, as we're disabling CSS.
4653 flags |= eEditorNoCSSMask;
4654 }
4656 return SetFlags(flags);
4657 }
4659 // Set the block background color
4660 NS_IMETHODIMP
4661 nsHTMLEditor::SetCSSBackgroundColor(const nsAString& aColor)
4662 {
4663 if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
4664 ForceCompositionEnd();
4666 // Protect the edit rules object from dying
4667 nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
4669 nsRefPtr<Selection> selection = GetSelection();
4671 bool isCollapsed = selection->Collapsed();
4673 nsAutoEditBatch batchIt(this);
4674 nsAutoRules beginRulesSniffing(this, EditAction::insertElement, nsIEditor::eNext);
4675 nsAutoSelectionReset selectionResetter(selection, this);
4676 nsAutoTxnsConserveSelection dontSpazMySelection(this);
4678 bool cancel, handled;
4679 nsTextRulesInfo ruleInfo(EditAction::setTextProperty);
4680 nsresult res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
4681 NS_ENSURE_SUCCESS(res, res);
4682 if (!cancel && !handled)
4683 {
4684 // loop thru the ranges in the selection
4685 nsAutoString bgcolor; bgcolor.AssignLiteral("bgcolor");
4686 uint32_t rangeCount = selection->GetRangeCount();
4687 for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
4688 nsCOMPtr<nsIDOMNode> cachedBlockParent = nullptr;
4689 nsRefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
4690 NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
4692 // check for easy case: both range endpoints in same text node
4693 nsCOMPtr<nsIDOMNode> startNode, endNode;
4694 int32_t startOffset, endOffset;
4695 res = range->GetStartContainer(getter_AddRefs(startNode));
4696 NS_ENSURE_SUCCESS(res, res);
4697 res = range->GetEndContainer(getter_AddRefs(endNode));
4698 NS_ENSURE_SUCCESS(res, res);
4699 res = range->GetStartOffset(&startOffset);
4700 NS_ENSURE_SUCCESS(res, res);
4701 res = range->GetEndOffset(&endOffset);
4702 NS_ENSURE_SUCCESS(res, res);
4703 if ((startNode == endNode) && IsTextNode(startNode))
4704 {
4705 // let's find the block container of the text node
4706 nsCOMPtr<nsIDOMNode> blockParent;
4707 blockParent = GetBlockNodeParent(startNode);
4708 // and apply the background color to that block container
4709 if (cachedBlockParent != blockParent)
4710 {
4711 cachedBlockParent = blockParent;
4712 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(blockParent);
4713 int32_t count;
4714 res = mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(element, nullptr, &bgcolor, &aColor, &count, false);
4715 NS_ENSURE_SUCCESS(res, res);
4716 }
4717 }
4718 else if ((startNode == endNode) && nsTextEditUtils::IsBody(startNode) && isCollapsed)
4719 {
4720 // we have no block in the document, let's apply the background to the body
4721 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(startNode);
4722 int32_t count;
4723 res = mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(element, nullptr, &bgcolor, &aColor, &count, false);
4724 NS_ENSURE_SUCCESS(res, res);
4725 }
4726 else if ((startNode == endNode) && (((endOffset-startOffset) == 1) || (!startOffset && !endOffset)))
4727 {
4728 // a unique node is selected, let's also apply the background color
4729 // to the containing block, possibly the node itself
4730 nsCOMPtr<nsIDOMNode> selectedNode = GetChildAt(startNode, startOffset);
4731 bool isBlock =false;
4732 res = NodeIsBlockStatic(selectedNode, &isBlock);
4733 NS_ENSURE_SUCCESS(res, res);
4734 nsCOMPtr<nsIDOMNode> blockParent = selectedNode;
4735 if (!isBlock) {
4736 blockParent = GetBlockNodeParent(selectedNode);
4737 }
4738 if (cachedBlockParent != blockParent)
4739 {
4740 cachedBlockParent = blockParent;
4741 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(blockParent);
4742 int32_t count;
4743 res = mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(element, nullptr, &bgcolor, &aColor, &count, false);
4744 NS_ENSURE_SUCCESS(res, res);
4745 }
4746 }
4747 else
4748 {
4749 // not the easy case. range not contained in single text node.
4750 // there are up to three phases here. There are all the nodes
4751 // reported by the subtree iterator to be processed. And there
4752 // are potentially a starting textnode and an ending textnode
4753 // which are only partially contained by the range.
4755 // lets handle the nodes reported by the iterator. These nodes
4756 // are entirely contained in the selection range. We build up
4757 // a list of them (since doing operations on the document during
4758 // iteration would perturb the iterator).
4760 nsCOMPtr<nsIContentIterator> iter =
4761 do_CreateInstance("@mozilla.org/content/subtree-content-iterator;1", &res);
4762 NS_ENSURE_SUCCESS(res, res);
4763 NS_ENSURE_TRUE(iter, NS_ERROR_FAILURE);
4765 nsCOMArray<nsIDOMNode> arrayOfNodes;
4766 nsCOMPtr<nsIDOMNode> node;
4768 // iterate range and build up array
4769 res = iter->Init(range);
4770 // init returns an error if no nodes in range.
4771 // this can easily happen with the subtree
4772 // iterator if the selection doesn't contain
4773 // any *whole* nodes.
4774 if (NS_SUCCEEDED(res))
4775 {
4776 while (!iter->IsDone())
4777 {
4778 node = do_QueryInterface(iter->GetCurrentNode());
4779 NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
4781 if (IsEditable(node))
4782 {
4783 arrayOfNodes.AppendObject(node);
4784 }
4786 iter->Next();
4787 }
4788 }
4789 // first check the start parent of the range to see if it needs to
4790 // be separately handled (it does if it's a text node, due to how the
4791 // subtree iterator works - it will not have reported it).
4792 if (IsTextNode(startNode) && IsEditable(startNode))
4793 {
4794 nsCOMPtr<nsIDOMNode> blockParent;
4795 blockParent = GetBlockNodeParent(startNode);
4796 if (cachedBlockParent != blockParent)
4797 {
4798 cachedBlockParent = blockParent;
4799 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(blockParent);
4800 int32_t count;
4801 res = mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(element, nullptr, &bgcolor, &aColor, &count, false);
4802 NS_ENSURE_SUCCESS(res, res);
4803 }
4804 }
4806 // then loop through the list, set the property on each node
4807 int32_t listCount = arrayOfNodes.Count();
4808 int32_t j;
4809 for (j = 0; j < listCount; j++)
4810 {
4811 node = arrayOfNodes[j];
4812 // do we have a block here ?
4813 bool isBlock =false;
4814 res = NodeIsBlockStatic(node, &isBlock);
4815 NS_ENSURE_SUCCESS(res, res);
4816 nsCOMPtr<nsIDOMNode> blockParent = node;
4817 if (!isBlock) {
4818 // no we don't, let's find the block ancestor
4819 blockParent = GetBlockNodeParent(node);
4820 }
4821 if (cachedBlockParent != blockParent)
4822 {
4823 cachedBlockParent = blockParent;
4824 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(blockParent);
4825 int32_t count;
4826 // and set the property on it
4827 res = mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(element, nullptr, &bgcolor, &aColor, &count, false);
4828 NS_ENSURE_SUCCESS(res, res);
4829 }
4830 }
4831 arrayOfNodes.Clear();
4833 // last check the end parent of the range to see if it needs to
4834 // be separately handled (it does if it's a text node, due to how the
4835 // subtree iterator works - it will not have reported it).
4836 if (IsTextNode(endNode) && IsEditable(endNode))
4837 {
4838 nsCOMPtr<nsIDOMNode> blockParent;
4839 blockParent = GetBlockNodeParent(endNode);
4840 if (cachedBlockParent != blockParent)
4841 {
4842 cachedBlockParent = blockParent;
4843 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(blockParent);
4844 int32_t count;
4845 res = mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(element, nullptr, &bgcolor, &aColor, &count, false);
4846 NS_ENSURE_SUCCESS(res, res);
4847 }
4848 }
4849 }
4850 }
4851 }
4852 if (!cancel)
4853 {
4854 // post-process
4855 res = mRules->DidDoAction(selection, &ruleInfo, res);
4856 }
4857 return res;
4858 }
4860 NS_IMETHODIMP
4861 nsHTMLEditor::SetBackgroundColor(const nsAString& aColor)
4862 {
4863 nsresult res;
4864 if (IsCSSEnabled()) {
4865 // if we are in CSS mode, we have to apply the background color to the
4866 // containing block (or the body if we have no block-level element in
4867 // the document)
4868 res = SetCSSBackgroundColor(aColor);
4869 }
4870 else {
4871 // but in HTML mode, we can only set the document's background color
4872 res = SetHTMLBackgroundColor(aColor);
4873 }
4874 return res;
4875 }
4877 ///////////////////////////////////////////////////////////////////////////
4878 // NodesSameType: do these nodes have the same tag?
4879 //
4880 /* virtual */
4881 bool
4882 nsHTMLEditor::AreNodesSameType(nsIContent* aNode1, nsIContent* aNode2)
4883 {
4884 MOZ_ASSERT(aNode1);
4885 MOZ_ASSERT(aNode2);
4887 if (aNode1->Tag() != aNode2->Tag()) {
4888 return false;
4889 }
4891 if (!IsCSSEnabled() || !aNode1->IsHTML(nsGkAtoms::span)) {
4892 return true;
4893 }
4895 // If CSS is enabled, we are stricter about span nodes.
4896 return mHTMLCSSUtils->ElementsSameStyle(aNode1->AsDOMNode(),
4897 aNode2->AsDOMNode());
4898 }
4900 NS_IMETHODIMP
4901 nsHTMLEditor::CopyLastEditableChildStyles(nsIDOMNode * aPreviousBlock, nsIDOMNode * aNewBlock,
4902 nsIDOMNode **aOutBrNode)
4903 {
4904 *aOutBrNode = nullptr;
4905 nsCOMPtr<nsIDOMNode> child, tmp;
4906 nsresult res;
4907 // first, clear out aNewBlock. Contract is that we want only the styles from previousBlock.
4908 res = aNewBlock->GetFirstChild(getter_AddRefs(child));
4909 while (NS_SUCCEEDED(res) && child)
4910 {
4911 res = DeleteNode(child);
4912 NS_ENSURE_SUCCESS(res, res);
4913 res = aNewBlock->GetFirstChild(getter_AddRefs(child));
4914 }
4915 // now find and clone the styles
4916 child = aPreviousBlock;
4917 tmp = aPreviousBlock;
4918 while (tmp) {
4919 child = tmp;
4920 res = GetLastEditableChild(child, address_of(tmp));
4921 NS_ENSURE_SUCCESS(res, res);
4922 }
4923 while (child && nsTextEditUtils::IsBreak(child)) {
4924 nsCOMPtr<nsIDOMNode> priorNode;
4925 res = GetPriorHTMLNode(child, address_of(priorNode));
4926 NS_ENSURE_SUCCESS(res, res);
4927 child = priorNode;
4928 }
4929 nsCOMPtr<nsIDOMNode> newStyles = nullptr, deepestStyle = nullptr;
4930 while (child && (child != aPreviousBlock)) {
4931 if (nsHTMLEditUtils::IsInlineStyle(child) ||
4932 nsEditor::NodeIsType(child, nsEditProperty::span)) {
4933 nsAutoString domTagName;
4934 child->GetNodeName(domTagName);
4935 ToLowerCase(domTagName);
4936 if (newStyles) {
4937 nsCOMPtr<nsIDOMNode> newContainer;
4938 res = InsertContainerAbove(newStyles, address_of(newContainer), domTagName);
4939 NS_ENSURE_SUCCESS(res, res);
4940 newStyles = newContainer;
4941 }
4942 else {
4943 res = CreateNode(domTagName, aNewBlock, 0, getter_AddRefs(newStyles));
4944 NS_ENSURE_SUCCESS(res, res);
4945 deepestStyle = newStyles;
4946 }
4947 res = CloneAttributes(newStyles, child);
4948 NS_ENSURE_SUCCESS(res, res);
4949 }
4950 nsCOMPtr<nsIDOMNode> tmp;
4951 res = child->GetParentNode(getter_AddRefs(tmp));
4952 NS_ENSURE_SUCCESS(res, res);
4953 child = tmp;
4954 }
4955 if (deepestStyle) {
4956 nsCOMPtr<nsIDOMNode> outBRNode;
4957 res = CreateBR(deepestStyle, 0, address_of(outBRNode));
4958 NS_ENSURE_SUCCESS(res, res);
4959 // Getters must addref
4960 outBRNode.forget(aOutBrNode);
4961 }
4962 return NS_OK;
4963 }
4965 nsresult
4966 nsHTMLEditor::GetElementOrigin(nsIDOMElement * aElement, int32_t & aX, int32_t & aY)
4967 {
4968 aX = 0;
4969 aY = 0;
4971 NS_ENSURE_TRUE(mDocWeak, NS_ERROR_NOT_INITIALIZED);
4972 nsCOMPtr<nsIPresShell> ps = GetPresShell();
4973 NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
4975 nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
4976 nsIFrame *frame = content->GetPrimaryFrame();
4977 NS_ENSURE_TRUE(frame, NS_OK);
4979 nsIFrame *container = ps->GetAbsoluteContainingBlock(frame);
4980 NS_ENSURE_TRUE(container, NS_OK);
4981 nsPoint off = frame->GetOffsetTo(container);
4982 aX = nsPresContext::AppUnitsToIntCSSPixels(off.x);
4983 aY = nsPresContext::AppUnitsToIntCSSPixels(off.y);
4985 return NS_OK;
4986 }
4988 nsresult
4989 nsHTMLEditor::EndUpdateViewBatch()
4990 {
4991 nsresult res = nsEditor::EndUpdateViewBatch();
4992 NS_ENSURE_SUCCESS(res, res);
4994 // We may need to show resizing handles or update existing ones after
4995 // all transactions are done. This way of doing is preferred to DOM
4996 // mutation events listeners because all the changes the user can apply
4997 // to a document may result in multiple events, some of them quite hard
4998 // to listen too (in particular when an ancestor of the selection is
4999 // changed but the selection itself is not changed).
5000 if (mUpdateCount == 0) {
5001 nsCOMPtr<nsISelection> selection;
5002 res = GetSelection(getter_AddRefs(selection));
5003 NS_ENSURE_SUCCESS(res, res);
5004 NS_ENSURE_TRUE(selection, NS_ERROR_NOT_INITIALIZED);
5005 res = CheckSelectionStateForAnonymousButtons(selection);
5006 }
5007 return res;
5008 }
5010 NS_IMETHODIMP
5011 nsHTMLEditor::GetSelectionContainer(nsIDOMElement ** aReturn)
5012 {
5013 nsCOMPtr<nsISelection>selection;
5014 nsresult res = GetSelection(getter_AddRefs(selection));
5015 // if we don't get the selection, just skip this
5016 if (NS_FAILED(res) || !selection) return res;
5018 nsCOMPtr<nsIDOMNode> focusNode;
5020 if (selection->Collapsed()) {
5021 res = selection->GetFocusNode(getter_AddRefs(focusNode));
5022 NS_ENSURE_SUCCESS(res, res);
5023 } else {
5025 int32_t rangeCount;
5026 res = selection->GetRangeCount(&rangeCount);
5027 NS_ENSURE_SUCCESS(res, res);
5029 if (rangeCount == 1) {
5031 nsCOMPtr<nsIDOMRange> range;
5032 res = selection->GetRangeAt(0, getter_AddRefs(range));
5033 NS_ENSURE_SUCCESS(res, res);
5034 NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER);
5036 nsCOMPtr<nsIDOMNode> startContainer, endContainer;
5037 res = range->GetStartContainer(getter_AddRefs(startContainer));
5038 NS_ENSURE_SUCCESS(res, res);
5039 res = range->GetEndContainer(getter_AddRefs(endContainer));
5040 NS_ENSURE_SUCCESS(res, res);
5041 int32_t startOffset, endOffset;
5042 res = range->GetStartOffset(&startOffset);
5043 NS_ENSURE_SUCCESS(res, res);
5044 res = range->GetEndOffset(&endOffset);
5045 NS_ENSURE_SUCCESS(res, res);
5047 nsCOMPtr<nsIDOMElement> focusElement;
5048 if (startContainer == endContainer && startOffset + 1 == endOffset) {
5049 res = GetSelectedElement(EmptyString(), getter_AddRefs(focusElement));
5050 NS_ENSURE_SUCCESS(res, res);
5051 if (focusElement)
5052 focusNode = do_QueryInterface(focusElement);
5053 }
5054 if (!focusNode) {
5055 res = range->GetCommonAncestorContainer(getter_AddRefs(focusNode));
5056 NS_ENSURE_SUCCESS(res, res);
5057 }
5058 }
5059 else {
5060 int32_t i;
5061 nsCOMPtr<nsIDOMRange> range;
5062 for (i = 0; i < rangeCount; i++)
5063 {
5064 res = selection->GetRangeAt(i, getter_AddRefs(range));
5065 NS_ENSURE_SUCCESS(res, res);
5066 nsCOMPtr<nsIDOMNode> startContainer;
5067 res = range->GetStartContainer(getter_AddRefs(startContainer));
5068 if (NS_FAILED(res)) continue;
5069 if (!focusNode)
5070 focusNode = startContainer;
5071 else if (focusNode != startContainer) {
5072 res = startContainer->GetParentNode(getter_AddRefs(focusNode));
5073 NS_ENSURE_SUCCESS(res, res);
5074 break;
5075 }
5076 }
5077 }
5078 }
5080 if (focusNode) {
5081 uint16_t nodeType;
5082 focusNode->GetNodeType(&nodeType);
5083 if (nsIDOMNode::TEXT_NODE == nodeType) {
5084 nsCOMPtr<nsIDOMNode> parent;
5085 res = focusNode->GetParentNode(getter_AddRefs(parent));
5086 NS_ENSURE_SUCCESS(res, res);
5087 focusNode = parent;
5088 }
5089 }
5091 nsCOMPtr<nsIDOMElement> focusElement = do_QueryInterface(focusNode);
5092 focusElement.forget(aReturn);
5093 return NS_OK;
5094 }
5096 NS_IMETHODIMP
5097 nsHTMLEditor::IsAnonymousElement(nsIDOMElement * aElement, bool * aReturn)
5098 {
5099 NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
5100 nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
5101 *aReturn = content->IsRootOfNativeAnonymousSubtree();
5102 return NS_OK;
5103 }
5105 nsresult
5106 nsHTMLEditor::SetReturnInParagraphCreatesNewParagraph(bool aCreatesNewParagraph)
5107 {
5108 mCRInParagraphCreatesParagraph = aCreatesNewParagraph;
5109 return NS_OK;
5110 }
5112 bool
5113 nsHTMLEditor::GetReturnInParagraphCreatesNewParagraph()
5114 {
5115 return mCRInParagraphCreatesParagraph;
5116 }
5118 nsresult
5119 nsHTMLEditor::GetReturnInParagraphCreatesNewParagraph(bool *aCreatesNewParagraph)
5120 {
5121 *aCreatesNewParagraph = mCRInParagraphCreatesParagraph;
5122 return NS_OK;
5123 }
5125 already_AddRefed<nsIContent>
5126 nsHTMLEditor::GetFocusedContent()
5127 {
5128 NS_ENSURE_TRUE(mDocWeak, nullptr);
5130 nsFocusManager* fm = nsFocusManager::GetFocusManager();
5131 NS_ENSURE_TRUE(fm, nullptr);
5133 nsCOMPtr<nsIContent> focusedContent = fm->GetFocusedContent();
5135 nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
5136 bool inDesignMode = doc->HasFlag(NODE_IS_EDITABLE);
5137 if (!focusedContent) {
5138 // in designMode, nobody gets focus in most cases.
5139 if (inDesignMode && OurWindowHasFocus()) {
5140 nsCOMPtr<nsIContent> docRoot = doc->GetRootElement();
5141 return docRoot.forget();
5142 }
5143 return nullptr;
5144 }
5146 if (inDesignMode) {
5147 return OurWindowHasFocus() &&
5148 nsContentUtils::ContentIsDescendantOf(focusedContent, doc) ?
5149 focusedContent.forget() : nullptr;
5150 }
5152 // We're HTML editor for contenteditable
5154 // If the focused content isn't editable, or it has independent selection,
5155 // we don't have focus.
5156 if (!focusedContent->HasFlag(NODE_IS_EDITABLE) ||
5157 focusedContent->HasIndependentSelection()) {
5158 return nullptr;
5159 }
5160 // If our window is focused, we're focused.
5161 return OurWindowHasFocus() ? focusedContent.forget() : nullptr;
5162 }
5164 already_AddRefed<nsIContent>
5165 nsHTMLEditor::GetFocusedContentForIME()
5166 {
5167 nsCOMPtr<nsIContent> focusedContent = GetFocusedContent();
5168 if (!focusedContent) {
5169 return nullptr;
5170 }
5172 nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
5173 NS_ENSURE_TRUE(doc, nullptr);
5174 return doc->HasFlag(NODE_IS_EDITABLE) ? nullptr : focusedContent.forget();
5175 }
5177 bool
5178 nsHTMLEditor::IsActiveInDOMWindow()
5179 {
5180 NS_ENSURE_TRUE(mDocWeak, false);
5182 nsFocusManager* fm = nsFocusManager::GetFocusManager();
5183 NS_ENSURE_TRUE(fm, false);
5185 nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
5186 bool inDesignMode = doc->HasFlag(NODE_IS_EDITABLE);
5188 // If we're in designMode, we're always active in the DOM window.
5189 if (inDesignMode) {
5190 return true;
5191 }
5193 nsPIDOMWindow* ourWindow = doc->GetWindow();
5194 nsCOMPtr<nsPIDOMWindow> win;
5195 nsIContent* content =
5196 nsFocusManager::GetFocusedDescendant(ourWindow, false,
5197 getter_AddRefs(win));
5198 if (!content) {
5199 return false;
5200 }
5202 // We're HTML editor for contenteditable
5204 // If the active content isn't editable, or it has independent selection,
5205 // we're not active).
5206 if (!content->HasFlag(NODE_IS_EDITABLE) ||
5207 content->HasIndependentSelection()) {
5208 return false;
5209 }
5210 return true;
5211 }
5213 dom::Element*
5214 nsHTMLEditor::GetActiveEditingHost()
5215 {
5216 NS_ENSURE_TRUE(mDocWeak, nullptr);
5218 nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
5219 NS_ENSURE_TRUE(doc, nullptr);
5220 if (doc->HasFlag(NODE_IS_EDITABLE)) {
5221 return doc->GetBodyElement();
5222 }
5224 // We're HTML editor for contenteditable
5225 nsCOMPtr<nsISelection> selection;
5226 nsresult rv = GetSelection(getter_AddRefs(selection));
5227 NS_ENSURE_SUCCESS(rv, nullptr);
5228 nsCOMPtr<nsIDOMNode> focusNode;
5229 rv = selection->GetFocusNode(getter_AddRefs(focusNode));
5230 NS_ENSURE_SUCCESS(rv, nullptr);
5231 nsCOMPtr<nsIContent> content = do_QueryInterface(focusNode);
5232 if (!content) {
5233 return nullptr;
5234 }
5236 // If the active content isn't editable, or it has independent selection,
5237 // we're not active.
5238 if (!content->HasFlag(NODE_IS_EDITABLE) ||
5239 content->HasIndependentSelection()) {
5240 return nullptr;
5241 }
5242 return content->GetEditingHost();
5243 }
5245 already_AddRefed<mozilla::dom::EventTarget>
5246 nsHTMLEditor::GetDOMEventTarget()
5247 {
5248 // Don't use getDocument here, because we have no way of knowing
5249 // whether Init() was ever called. So we need to get the document
5250 // ourselves, if it exists.
5251 NS_PRECONDITION(mDocWeak, "This editor has not been initialized yet");
5252 nsCOMPtr<mozilla::dom::EventTarget> target = do_QueryReferent(mDocWeak);
5253 return target.forget();
5254 }
5256 bool
5257 nsHTMLEditor::ShouldReplaceRootElement()
5258 {
5259 if (!mRootElement) {
5260 // If we don't know what is our root element, we should find our root.
5261 return true;
5262 }
5264 // If we temporary set document root element to mRootElement, but there is
5265 // body element now, we should replace the root element by the body element.
5266 nsCOMPtr<nsIDOMHTMLElement> docBody;
5267 GetBodyElement(getter_AddRefs(docBody));
5268 return !SameCOMIdentity(docBody, mRootElement);
5269 }
5271 void
5272 nsHTMLEditor::ResetRootElementAndEventTarget()
5273 {
5274 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
5276 // Need to remove the event listeners first because BeginningOfDocument
5277 // could set a new root (and event target is set by InstallEventListeners())
5278 // and we won't be able to remove them from the old event target then.
5279 RemoveEventListeners();
5280 mRootElement = nullptr;
5281 nsresult rv = InstallEventListeners();
5282 if (NS_FAILED(rv)) {
5283 return;
5284 }
5286 // We must have mRootElement now.
5287 nsCOMPtr<nsIDOMElement> root;
5288 rv = GetRootElement(getter_AddRefs(root));
5289 if (NS_FAILED(rv) || !mRootElement) {
5290 return;
5291 }
5293 rv = BeginningOfDocument();
5294 if (NS_FAILED(rv)) {
5295 return;
5296 }
5298 // When this editor has focus, we need to reset the selection limiter to
5299 // new root. Otherwise, that is going to be done when this gets focus.
5300 nsCOMPtr<nsINode> node = GetFocusedNode();
5301 nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(node);
5302 if (target) {
5303 InitializeSelection(target);
5304 }
5306 SyncRealTimeSpell();
5307 }
5309 nsresult
5310 nsHTMLEditor::GetBodyElement(nsIDOMHTMLElement** aBody)
5311 {
5312 NS_PRECONDITION(mDocWeak, "bad state, null mDocWeak");
5313 nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryReferent(mDocWeak);
5314 if (!htmlDoc) {
5315 return NS_ERROR_NOT_INITIALIZED;
5316 }
5317 return htmlDoc->GetBody(aBody);
5318 }
5320 already_AddRefed<nsINode>
5321 nsHTMLEditor::GetFocusedNode()
5322 {
5323 nsCOMPtr<nsIContent> focusedContent = GetFocusedContent();
5324 if (!focusedContent) {
5325 return nullptr;
5326 }
5328 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
5329 NS_ASSERTION(fm, "Focus manager is null");
5330 nsCOMPtr<nsIDOMElement> focusedElement;
5331 fm->GetFocusedElement(getter_AddRefs(focusedElement));
5332 if (focusedElement) {
5333 nsCOMPtr<nsINode> node = do_QueryInterface(focusedElement);
5334 return node.forget();
5335 }
5337 nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
5338 return doc.forget();
5339 }
5341 bool
5342 nsHTMLEditor::OurWindowHasFocus()
5343 {
5344 NS_ENSURE_TRUE(mDocWeak, false);
5345 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
5346 NS_ENSURE_TRUE(fm, false);
5347 nsCOMPtr<nsIDOMWindow> focusedWindow;
5348 fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
5349 if (!focusedWindow) {
5350 return false;
5351 }
5352 nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
5353 nsCOMPtr<nsIDOMWindow> ourWindow = do_QueryInterface(doc->GetWindow());
5354 return ourWindow == focusedWindow;
5355 }
5357 bool
5358 nsHTMLEditor::IsAcceptableInputEvent(nsIDOMEvent* aEvent)
5359 {
5360 if (!nsEditor::IsAcceptableInputEvent(aEvent)) {
5361 return false;
5362 }
5364 NS_ENSURE_TRUE(mDocWeak, false);
5366 nsCOMPtr<nsIDOMEventTarget> target;
5367 aEvent->GetTarget(getter_AddRefs(target));
5368 NS_ENSURE_TRUE(target, false);
5370 nsCOMPtr<nsIDocument> document = do_QueryReferent(mDocWeak);
5371 if (document->HasFlag(NODE_IS_EDITABLE)) {
5372 // If this editor is in designMode and the event target is the document,
5373 // the event is for this editor.
5374 nsCOMPtr<nsIDocument> targetDocument = do_QueryInterface(target);
5375 if (targetDocument) {
5376 return targetDocument == document;
5377 }
5378 // Otherwise, check whether the event target is in this document or not.
5379 nsCOMPtr<nsIContent> targetContent = do_QueryInterface(target);
5380 NS_ENSURE_TRUE(targetContent, false);
5381 return document == targetContent->GetCurrentDoc();
5382 }
5384 // This HTML editor is for contenteditable. We need to check the validity of
5385 // the target.
5386 nsCOMPtr<nsIContent> targetContent = do_QueryInterface(target);
5387 NS_ENSURE_TRUE(targetContent, false);
5389 // If the event is a mouse event, we need to check if the target content is
5390 // the focused editing host or its descendant.
5391 nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aEvent);
5392 if (mouseEvent) {
5393 nsIContent* editingHost = GetActiveEditingHost();
5394 // If there is no active editing host, we cannot handle the mouse event
5395 // correctly.
5396 if (!editingHost) {
5397 return false;
5398 }
5399 // If clicked on non-editable root element but the body element is the
5400 // active editing host, we should assume that the click event is targetted.
5401 if (targetContent == document->GetRootElement() &&
5402 !targetContent->HasFlag(NODE_IS_EDITABLE) &&
5403 editingHost == document->GetBodyElement()) {
5404 targetContent = editingHost;
5405 }
5406 // If the target element is neither the active editing host nor a descendant
5407 // of it, we may not be able to handle the event.
5408 if (!nsContentUtils::ContentIsDescendantOf(targetContent, editingHost)) {
5409 return false;
5410 }
5411 // If the clicked element has an independent selection, we shouldn't
5412 // handle this click event.
5413 if (targetContent->HasIndependentSelection()) {
5414 return false;
5415 }
5416 // If the target content is editable, we should handle this event.
5417 return targetContent->HasFlag(NODE_IS_EDITABLE);
5418 }
5420 // If the target of the other events which target focused element isn't
5421 // editable or has an independent selection, this editor shouldn't handle the
5422 // event.
5423 if (!targetContent->HasFlag(NODE_IS_EDITABLE) ||
5424 targetContent->HasIndependentSelection()) {
5425 return false;
5426 }
5428 // Finally, check whether we're actually focused or not. When we're not
5429 // focused, we should ignore the dispatched event by script (or something)
5430 // because content editable element needs selection in itself for editing.
5431 // However, when we're not focused, it's not guaranteed.
5432 return IsActiveInDOMWindow();
5433 }
5435 NS_IMETHODIMP
5436 nsHTMLEditor::GetPreferredIMEState(IMEState *aState)
5437 {
5438 // HTML editor don't prefer the CSS ime-mode because IE didn't do so too.
5439 aState->mOpen = IMEState::DONT_CHANGE_OPEN_STATE;
5440 if (IsReadonly() || IsDisabled()) {
5441 aState->mEnabled = IMEState::DISABLED;
5442 } else {
5443 aState->mEnabled = IMEState::ENABLED;
5444 }
5445 return NS_OK;
5446 }
5448 already_AddRefed<nsIContent>
5449 nsHTMLEditor::GetInputEventTargetContent()
5450 {
5451 nsCOMPtr<nsIContent> target = GetActiveEditingHost();
5452 return target.forget();
5453 }
5455 bool
5456 nsHTMLEditor::IsEditable(nsIContent* aNode) {
5457 if (!nsPlaintextEditor::IsEditable(aNode)) {
5458 return false;
5459 }
5460 if (aNode->IsElement()) {
5461 // If we're dealing with an element, then ask it whether it's editable.
5462 return aNode->IsEditable();
5463 }
5464 // We might be dealing with a text node for example, which we always consider
5465 // to be editable.
5466 return true;
5467 }
5469 // virtual MOZ_OVERRIDE
5470 dom::Element*
5471 nsHTMLEditor::GetEditorRoot()
5472 {
5473 return GetActiveEditingHost();
5474 }