editor/libeditor/html/nsHTMLEditor.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

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)
  1011     uint32_t count;
  1012     nodeList->GetLength(&count);
  1013     if (count >= 1)
  1015       rv = nodeList->Item(0, getter_AddRefs(baseNode));
  1016       NS_ENSURE_SUCCESS(rv, rv);
  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)
  1023     nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
  1024     NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
  1026     return doc->SetBaseURI(doc->GetDocumentURI());
  1028   return NS_OK;
  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)
  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));
  1048   return nsPlaintextEditor::TypedText(aString, aAction);
  1051 NS_IMETHODIMP nsHTMLEditor::TabInTable(bool inIsShift, bool *outHandled)
  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
  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)
  1095       res = CollapseSelectionToDeepestNonTableFirstChild(nullptr, node);
  1096       NS_ENSURE_SUCCESS(res, res);
  1097       *outHandled = true;
  1098       return NS_OK;
  1100   } while (!iter->IsDone());
  1102   if (!(*outHandled) && !inIsShift)
  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;
  1132   return res;
  1135 NS_IMETHODIMP nsHTMLEditor::CreateBR(nsIDOMNode *aNode, int32_t aOffset, nsCOMPtr<nsIDOMNode> *outBRNode, EDirection aSelect)
  1137   nsCOMPtr<nsIDOMNode> parent = aNode;
  1138   int32_t offset = aOffset;
  1139   return CreateBRImpl(address_of(parent), &offset, outBRNode, aSelect);
  1142 nsresult 
  1143 nsHTMLEditor::CollapseSelectionToDeepestNonTableFirstChild(nsISelection *aSelection, nsIDOMNode *aNode)
  1145   NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
  1146   nsresult res;
  1148   nsCOMPtr<nsISelection> selection;
  1149   if (aSelection)
  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);
  1157   nsCOMPtr<nsIDOMNode> node = aNode;
  1158   nsCOMPtr<nsIDOMNode> child;
  1160   do {
  1161     node->GetFirstChild(getter_AddRefs(child));
  1163     if (child)
  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;
  1173   while (child);
  1175   selection->Collapse(node,0);
  1176   return NS_OK;
  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)
  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))
  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;
  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)
  1262       res = DeleteNode(child);
  1263       NS_ENSURE_SUCCESS(res, res);
  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)
  1277       res = InsertNode(child, headNode, offsetOfNewNode++);
  1278       NS_ENSURE_SUCCESS(res, res);
  1280   } while (child);
  1282   return res;
  1285 NS_IMETHODIMP
  1286 nsHTMLEditor::RebuildDocumentFromSource(const nsAString& aSourceString)
  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);
  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();
  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();
  1431 void
  1432 nsHTMLEditor::NormalizeEOLInsertPosition(nsIDOMNode *firstNodeToInsert,
  1433                                      nsCOMPtr<nsIDOMNode> *insertParentNode,
  1434                                      int32_t *insertOffset)
  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;
  1485   wsObj.PriorVisibleNode(*insertParentNode, *insertOffset, address_of(prevVisNode), &prevVisOffset, &prevVisType);
  1486   if (!prevVisNode)
  1487     return;
  1489   if (prevVisType & WSType::br) {
  1490     return;
  1493   if (prevVisType & WSType::thisBlock) {
  1494     return;
  1497   int32_t brOffset=0;
  1498   nsCOMPtr<nsIDOMNode> brNode = GetNodeLocation(nextVisNode, &brOffset);
  1500   *insertParentNode = brNode;
  1501   *insertOffset = brOffset + 1;
  1504 NS_IMETHODIMP
  1505 nsHTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement, bool aDeleteSelection)
  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;
  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)
  1534     if (aDeleteSelection)
  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);
  1545       nsresult result = DeleteSelectionAndPrepareToCreateNode();
  1546       NS_ENSURE_SUCCESS(result, result);
  1549     // If deleting, selection will be collapsed.
  1550     // so if not, we collapse it
  1551     if (!aDeleteSelection)
  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))
  1558         selection->CollapseToStart();
  1559       } else {
  1560         selection->CollapseToEnd();
  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)
  1570 #ifdef DEBUG_cmanske
  1572       nsAutoString name;
  1573       parentSelectedNode->GetNodeName(name);
  1574       printf("InsertElement: Anchor node of selection: ");
  1575       wprintf(name.get());
  1576       printf(" Offset: %d\n", offsetForInsert);
  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))
  1589         res = SetCaretAfterElement(aElement);
  1590         NS_ENSURE_SUCCESS(res, res);
  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))
  1595         bool isLast;
  1596         res = IsLastEditableChild(node, &isLast);
  1597         NS_ENSURE_SUCCESS(res, res);
  1598         if (isLast)
  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);
  1608   res = mRules->DidDoAction(selection, &ruleInfo, res);
  1609   return res;
  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)
  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;
  1655   if (parent != topChild)
  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;
  1663   // Now we can insert the new node
  1664   res = InsertNode(aNode, parent, offsetOfInsert);
  1665   return res;
  1668 NS_IMETHODIMP
  1669 nsHTMLEditor::SelectElement(nsIDOMElement* aElement)
  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)
  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);
  1693   return res;
  1696 NS_IMETHODIMP
  1697 nsHTMLEditor::SetCaretAfterElement(nsIDOMElement* aElement)
  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);
  1715   return res;
  1718 NS_IMETHODIMP 
  1719 nsHTMLEditor::SetParagraphFormat(const nsAString& aParagraphFormat)
  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);
  1729 NS_IMETHODIMP 
  1730 nsHTMLEditor::GetParagraphState(bool *aMixed, nsAString &outFormat)
  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);
  1739 NS_IMETHODIMP
  1740 nsHTMLEditor::GetBackgroundColorState(bool *aMixed, nsAString &aOutColor)
  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);
  1748   else {
  1749     // in HTML mode, we look only at page's background
  1750     res = GetHTMLBackgroundColorState(aMixed, aOutColor);
  1752   return res;
  1755 NS_IMETHODIMP
  1756 nsHTMLEditor::GetHighlightColorState(bool *aMixed, nsAString &aOutColor)
  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);
  1767   return res;
  1770 nsresult
  1771 nsHTMLEditor::GetCSSBackgroundColorState(bool *aMixed, nsAString &aOutColor, bool aBlockLevel)
  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));
  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);
  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);
  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;
  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;
  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;
  1866       tmp.swap(nodeToExamine);
  1867       res = tmp->GetParentNode(getter_AddRefs(nodeToExamine));
  1868       NS_ENSURE_SUCCESS(res, res);
  1869     } while ( aOutColor.EqualsLiteral("transparent") && nodeToExamine );
  1871   return NS_OK;
  1874 NS_IMETHODIMP 
  1875 nsHTMLEditor::GetHTMLBackgroundColorState(bool *aMixed, nsAString &aOutColor)
  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;
  1901     // Once we hit the body, we're done
  1902     if (element->IsHTML(nsGkAtoms::body)) {
  1903       return NS_OK;
  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();
  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;
  1919 NS_IMETHODIMP 
  1920 nsHTMLEditor::GetListState(bool *aMixed, bool *aOL, bool *aUL, bool *aDL)
  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);
  1929 NS_IMETHODIMP 
  1930 nsHTMLEditor::GetListItemState(bool *aMixed, bool *aLI, bool *aDT, bool *aDD)
  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);
  1940 NS_IMETHODIMP
  1941 nsHTMLEditor::GetAlignment(bool *aMixed, nsIHTMLEditor::EAlignment *aAlign)
  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);
  1951 NS_IMETHODIMP 
  1952 nsHTMLEditor::GetIndentState(bool *aCanIndent, bool *aCanOutdent)
  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);
  1962 NS_IMETHODIMP
  1963 nsHTMLEditor::MakeOrChangeList(const nsAString& aListType, bool entireList, const nsAString& aBulletType)
  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)
  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)
  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;
  2013       if (parent != node)
  2015         // we need to split up to the child of parent
  2016         res = SplitNodeDeep(topChild, node, offset, &offset);
  2017         NS_ENSURE_SUCCESS(res, res);
  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);
  2033   res = mRules->DidDoAction(selection, &ruleInfo, res);
  2034   return res;
  2038 NS_IMETHODIMP
  2039 nsHTMLEditor::RemoveList(const nsAString& aListType)
  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;
  2069 nsresult
  2070 nsHTMLEditor::MakeDefinitionItem(const nsAString& aItemType)
  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)
  2093     // todo: no default for now.  we count on rules to handle it.
  2096   res = mRules->DidDoAction(selection, &ruleInfo, res);
  2097   return res;
  2100 nsresult
  2101 nsHTMLEditor::InsertBasicBlock(const nsAString& aBlockType)
  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)
  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)
  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;
  2148       if (parent != node)
  2150         // we need to split up to the child of parent
  2151         res = SplitNodeDeep(topChild, node, offset, &offset);
  2152         NS_ENSURE_SUCCESS(res, res);
  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);  
  2166   res = mRules->DidDoAction(selection, &ruleInfo, res);
  2167   return res;
  2170 NS_IMETHODIMP
  2171 nsHTMLEditor::Indent(const nsAString& aIndent)
  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"))
  2183     opID = EditAction::outdent;
  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)
  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"))
  2209       if (isCollapsed)
  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;
  2222         if (parent != node)
  2224           // we need to split up to the child of parent
  2225           res = SplitNodeDeep(topChild, node, offset, &offset);
  2226           NS_ENSURE_SUCCESS(res, res);
  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);
  2247   res = mRules->DidDoAction(selection, &ruleInfo, res);
  2248   return res;
  2251 //TODO: IMPLEMENT ALIGNMENT!
  2253 NS_IMETHODIMP
  2254 nsHTMLEditor::Align(const nsAString& aAlignType)
  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;
  2278 NS_IMETHODIMP
  2279 nsHTMLEditor::GetElementOrParentByTagName(const nsAString& aTagName, nsIDOMNode *aNode, nsIDOMElement** aReturn)
  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);
  2298     // anchor node is probably a text node - just use that
  2299     if (!current) {
  2300       current = anchorNode;
  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)
  2312     TagName.AssignLiteral("a");  
  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)
  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)) )
  2330       bNodeFound = true;
  2331       break;
  2332     } else {
  2333       if (findList)
  2335         // Match "ol", "ul", or "dl" for lists
  2336         if (nsHTMLEditUtils::IsList(currentNode))
  2337           goto NODE_FOUND;
  2339       } else if (findTableCell)
  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()))
  2350 NODE_FOUND:
  2351           bNodeFound = true;
  2352           break;
  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;
  2373   if (!bNodeFound) {
  2374     return NS_EDITOR_ELEMENT_NOT_FOUND;
  2377   nsCOMPtr<nsIDOMElement> currentElement = do_QueryInterface(currentNode);
  2378   currentElement.forget(aReturn);
  2379   return NS_OK;
  2382 NS_IMETHODIMP
  2383 nsHTMLEditor::GetSelectedElement(const nsAString& aTagName, nsIDOMElement** aReturn)
  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)
  2429     nsCOMPtr<nsIDOMNode> selectedNode = GetChildAt(startParent, startOffset);
  2430     NS_ENSURE_SUCCESS(res, NS_OK);
  2431     if (selectedNode)
  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)))
  2441         bNodeFound = true;
  2442         selectedElement = do_QueryInterface(selectedNode);
  2447   if (!bNodeFound)
  2449     if (isLinkTag)
  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)
  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)
  2476           if (isCollapsed)
  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;
  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;
  2496         else if (anchorOffset >= 0)  // Check if link node is the only thing selected
  2498           nsCOMPtr<nsIDOMNode> anchorChild;
  2499           anchorChild = GetChildAt(anchorNode,anchorOffset);
  2500           if (anchorChild && nsHTMLEditUtils::IsLink(anchorChild) && 
  2501               (anchorNode == focusNode) && focusOffset == (anchorOffset+1))
  2503             selectedElement = do_QueryInterface(anchorChild);
  2504             bNodeFound = true;
  2510     if (!isCollapsed)   // Don't bother to examine selection if it is collapsed
  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())
  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)
  2528             // If we already found a node, then we have another element,
  2529             //  thus there's not just one element selected
  2530             if (bNodeFound)
  2532               bNodeFound = false;
  2533               break;
  2536             selectedElement->GetNodeName(domTagName);
  2537             ToLowerCase(domTagName);
  2539             if (anyTag)
  2541               // Get name of first selected element
  2542               selectedElement->GetTagName(TagName);
  2543               ToLowerCase(TagName);
  2544               anyTag = false;
  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)) )
  2553               bNodeFound = true;
  2554             } else if (TagName == domTagName) { // All other tag names are handled here
  2555               bNodeFound = true;
  2557             if (!bNodeFound)
  2559               // Check if node we have is really part of the selection???
  2560               break;
  2563           iter->Next();
  2565       } else {
  2566         // Should never get here?
  2567         isCollapsed = true;
  2568         NS_WARNING("isCollapsed was FALSE, but no elements found in selection\n");
  2572   if (bNodeFound)
  2575     *aReturn = selectedElement;
  2576     if (selectedElement)
  2578       // Getters must addref
  2579       NS_ADDREF(*aReturn);
  2582   else res = NS_EDITOR_ELEMENT_NOT_FOUND;
  2584   return res;
  2587 NS_IMETHODIMP
  2588 nsHTMLEditor::CreateElementWithDefaults(const nsAString& aTagName, nsIDOMElement** aReturn)
  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))
  2603     realTagName.AssignLiteral("a");
  2604   } else {
  2605     realTagName = TagName;
  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"))
  2633     res = SetAttributeOrEquivalent(newElement, NS_LITERAL_STRING("valign"),
  2634                                    NS_LITERAL_STRING("top"), true);
  2636   // ADD OTHER TAGS HERE
  2638   if (NS_SUCCEEDED(res))
  2640     *aReturn = newElement;
  2641     // Getters must addref
  2642     NS_ADDREF(*aReturn);
  2645   return res;
  2648 NS_IMETHODIMP
  2649 nsHTMLEditor::InsertLinkAroundSelection(nsIDOMElement* aAnchorElement)
  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)
  2658     res = NS_ERROR_NULL_POINTER;
  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;
  2668   // Be sure we were given an anchor element
  2669   nsCOMPtr<nsIDOMHTMLAnchorElement> anchor = do_QueryInterface(aAnchorElement);
  2670   if (!anchor) {
  2671     return NS_OK;
  2674   nsAutoString href;
  2675   res = anchor->GetHref(href);
  2676   NS_ENSURE_SUCCESS(res, res);
  2677   if (href.IsEmpty()) {
  2678     return NS_OK;
  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);
  2713   return NS_OK;
  2716 NS_IMETHODIMP
  2717 nsHTMLEditor::SetHTMLBackgroundColor(const nsAString& aColor)
  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)
  2734     if (selectedCount > 0)
  2736       // Traverse all selected cells
  2737       nsCOMPtr<nsIDOMElement> cell;
  2738       res = GetFirstSelectedCell(nullptr, getter_AddRefs(cell));
  2739       if (NS_SUCCEEDED(res) && cell)
  2741         while(cell)
  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;
  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);
  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;
  2769 NS_IMETHODIMP nsHTMLEditor::SetBodyAttribute(const nsAString& aAttribute, const nsAString& aValue)
  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);
  2783 NS_IMETHODIMP
  2784 nsHTMLEditor::GetLinkedObjects(nsISupportsArray** aNodeList)
  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)))
  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())
  2807       nsCOMPtr<nsIDOMNode> node (do_QueryInterface(iter->GetCurrentNode()));
  2808       if (node)
  2810         // Let nsURIRefObject make the hard decisions:
  2811         nsCOMPtr<nsIURIRefObject> refObject;
  2812         res = NS_NewHTMLURIRefObject(getter_AddRefs(refObject), node);
  2813         if (NS_SUCCEEDED(res))
  2815           nsCOMPtr<nsISupports> isupp (do_QueryInterface(refObject));
  2817           (*aNodeList)->AppendElement(isupp);
  2820       iter->Next();
  2824   return NS_OK;
  2828 NS_IMETHODIMP
  2829 nsHTMLEditor::AddStyleSheet(const nsAString &aURL)
  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);
  2843 NS_IMETHODIMP
  2844 nsHTMLEditor::ReplaceStyleSheet(const nsAString& aURL)
  2846   // Enable existing sheet if already loaded.
  2847   if (EnableExistingStyleSheet(aURL))
  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;
  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);
  2869 NS_IMETHODIMP
  2870 nsHTMLEditor::RemoveStyleSheet(const nsAString &aURL)
  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))
  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);
  2890   return rv;
  2894 NS_IMETHODIMP
  2895 nsHTMLEditor::AddOverrideStyleSheet(const nsAString& aURL)
  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);
  2933 NS_IMETHODIMP
  2934 nsHTMLEditor::ReplaceOverrideStyleSheet(const nsAString& aURL)
  2936   // Enable existing sheet if already loaded.
  2937   if (EnableExistingStyleSheet(aURL))
  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;
  2945   // Remove the previous sheet
  2946   if (!mLastOverrideStyleSheetURL.IsEmpty())
  2947     RemoveOverrideStyleSheet(mLastOverrideStyleSheetURL);
  2949   return AddOverrideStyleSheet(aURL);
  2952 // Do NOT use transaction system for override style sheets
  2953 NS_IMETHODIMP
  2954 nsHTMLEditor::RemoveOverrideStyleSheet(const nsAString &aURL)
  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;
  2976 NS_IMETHODIMP
  2977 nsHTMLEditor::EnableStyleSheet(const nsAString &aURL, bool aEnable)
  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);
  2991 bool
  2992 nsHTMLEditor::EnableExistingStyleSheet(const nsAString &aURL)
  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)
  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;
  3008   return false;
  3011 nsresult
  3012 nsHTMLEditor::AddNewStyleSheetToList(const nsAString &aURL,
  3013                                      nsCSSStyleSheet *aStyleSheet)
  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;
  3027 nsresult
  3028 nsHTMLEditor::RemoveStyleSheetFromList(const nsAString &aURL)
  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;
  3043 NS_IMETHODIMP
  3044 nsHTMLEditor::GetStyleSheetForURL(const nsAString &aURL,
  3045                                   nsCSSStyleSheet **aStyleSheet)
  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;
  3064 NS_IMETHODIMP
  3065 nsHTMLEditor::GetURLForStyleSheet(nsCSSStyleSheet *aStyleSheet,
  3066                                   nsAString &aURL)
  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;
  3082 /*
  3083  * nsIEditorMailSupport methods
  3084  */
  3086 NS_IMETHODIMP
  3087 nsHTMLEditor::GetEmbeddedObjects(nsISupportsArray** aNodeList)
  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);
  3122     iter->Next();
  3125   return rv;
  3129 NS_IMETHODIMP
  3130 nsHTMLEditor::DeleteSelectionImpl(EDirection aAction,
  3131                                   EStripWrappers aStripWrappers)
  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;
  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();
  3159   if (!blockParent) {
  3160     return NS_OK;
  3162   bool emptyBlockParent;
  3163   res = IsEmptyNode(blockParent, &emptyBlockParent);
  3164   NS_ENSURE_SUCCESS(res, res);
  3165   if (emptyBlockParent) {
  3166     return NS_OK;
  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();
  3177     res = DeleteNode(content);
  3178     NS_ENSURE_SUCCESS(res, res);
  3181   return NS_OK;
  3185 nsresult
  3186 nsHTMLEditor::DeleteNode(nsINode* aNode)
  3188   nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode);
  3189   return DeleteNode(node);
  3192 NS_IMETHODIMP
  3193 nsHTMLEditor::DeleteNode(nsIDOMNode* aNode)
  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;
  3201   return nsEditor::DeleteNode(aNode);
  3204 NS_IMETHODIMP nsHTMLEditor::DeleteText(nsIDOMCharacterData *aTextNode,
  3205                                        uint32_t             aOffset,
  3206                                        uint32_t             aLength)
  3208   // do nothing if the node is read-only
  3209   if (!IsModifiableNode(aTextNode)) {
  3210     return NS_ERROR_FAILURE;
  3213   return nsEditor::DeleteText(aTextNode, aOffset, aLength);
  3216 NS_IMETHODIMP nsHTMLEditor::InsertTextImpl(const nsAString& aStringToInsert, 
  3217                                            nsCOMPtr<nsIDOMNode> *aInOutNode, 
  3218                                            int32_t *aInOutOffset,
  3219                                            nsIDOMDocument *aDoc)
  3221   // do nothing if the node is read-only
  3222   if (!IsModifiableNode(*aInOutNode)) {
  3223     return NS_ERROR_FAILURE;
  3226   return nsEditor::InsertTextImpl(aStringToInsert, aInOutNode, aInOutOffset, aDoc);
  3229 void
  3230 nsHTMLEditor::ContentAppended(nsIDocument *aDocument, nsIContent* aContainer,
  3231                               nsIContent* aFirstNewContent,
  3232                               int32_t aIndexInContainer)
  3234   DoContentInserted(aDocument, aContainer, aFirstNewContent, aIndexInContainer,
  3235                     eAppended);
  3238 void
  3239 nsHTMLEditor::ContentInserted(nsIDocument *aDocument, nsIContent* aContainer,
  3240                               nsIContent* aChild, int32_t aIndexInContainer)
  3242   DoContentInserted(aDocument, aContainer, aChild, aIndexInContainer,
  3243                     eInserted);
  3246 void
  3247 nsHTMLEditor::DoContentInserted(nsIDocument* aDocument, nsIContent* aContainer,
  3248                                 nsIContent* aChild, int32_t aIndexInContainer,
  3249                                 InsertedOrAppended aInsertedOrAppended)
  3251   if (!aChild) {
  3252     return;
  3255   nsCOMPtr<nsIHTMLEditor> kungFuDeathGrip(this);
  3257   if (ShouldReplaceRootElement()) {
  3258     nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(
  3259       this, &nsHTMLEditor::ResetRootElementAndEventTarget));
  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;
  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();
  3283       nsresult res = range->Set(aContainer, aIndexInContainer,
  3284                                 aContainer, endIndex);
  3285       if (NS_SUCCEEDED(res)) {
  3286         mInlineSpellChecker->SpellCheckRange(range);
  3292 void
  3293 nsHTMLEditor::ContentRemoved(nsIDocument *aDocument, nsIContent* aContainer,
  3294                              nsIContent* aChild, int32_t aIndexInContainer,
  3295                              nsIContent* aPreviousSibling)
  3297   nsCOMPtr<nsIHTMLEditor> kungFuDeathGrip(this);
  3299   if (SameCOMIdentity(aChild, mRootElement)) {
  3300     nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(
  3301       this, &nsHTMLEditor::ResetRootElementAndEventTarget));
  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;
  3309     // Protect the edit rules object from dying
  3310     nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
  3311     mRules->DocumentModified();
  3315 NS_IMETHODIMP_(bool)
  3316 nsHTMLEditor::IsModifiableNode(nsIDOMNode *aNode)
  3318   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
  3319   return IsModifiableNode(node);
  3322 bool
  3323 nsHTMLEditor::IsModifiableNode(nsINode *aNode)
  3325   return !aNode || aNode->IsEditable();
  3328 NS_IMETHODIMP
  3329 nsHTMLEditor::GetIsSelectionEditable(bool* aIsSelectionEditable)
  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();
  3349     if (!commonAncestor) {
  3350       // No editable common ancestor
  3351       *aIsSelectionEditable = false;
  3355   return NS_OK;
  3358 static nsresult
  3359 SetSelectionAroundHeadChildren(nsISelection* aSelection,
  3360                                nsIWeakReference* aDocWeak)
  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);
  3378 NS_IMETHODIMP
  3379 nsHTMLEditor::GetHeadContentsAsHTML(nsAString& aOutputString)
  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))
  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))
  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
  3415         writeIter.advance(offset);
  3416         *writeIter = newline;
  3417         aOutputString.Truncate(offset+1);
  3421   return res;
  3424 NS_IMETHODIMP
  3425 nsHTMLEditor::DebugUnitTests(int32_t *outNumTests, int32_t *outNumTestsFailed)
  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
  3442 NS_IMETHODIMP 
  3443 nsHTMLEditor::StyleSheetLoaded(nsCSSStyleSheet* aSheet, bool aWasAlternate,
  3444                                nsresult aStatus)
  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))
  3457     rv = DoTransaction(txn);
  3458     if (NS_SUCCEEDED(rv))
  3460       // Get the URI, then url spec from the sheet
  3461       nsAutoCString spec;
  3462       rv = aSheet->GetSheetURI()->GetSpec(spec);
  3464       if (NS_SUCCEEDED(rv))
  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);
  3475   return NS_OK;
  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)
  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;
  3494 /** All editor operations which alter the doc should be followed
  3495  *  with a call to EndOperation */
  3496 NS_IMETHODIMP
  3497 nsHTMLEditor::EndOperation()
  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;
  3509 bool 
  3510 nsHTMLEditor::TagCanContainTag(nsIAtom* aParentTag, nsIAtom* aChildTag)
  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);
  3524   int32_t parentTagEnum = parserService->HTMLAtomTagToId(aParentTag);
  3525   return nsHTMLEditUtils::CanContain(parentTagEnum, childTagEnum);
  3528 bool
  3529 nsHTMLEditor::IsContainer(nsIDOMNode *aNode)
  3531   if (!aNode) {
  3532     return false;
  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;
  3545   else {
  3546     tagEnum = nsContentUtils::GetParserService()->HTMLStringTagToId(stringTag);
  3549   return nsHTMLEditUtils::IsContainer(tagEnum);
  3553 NS_IMETHODIMP 
  3554 nsHTMLEditor::SelectEntireDocument(nsISelection *aSelection)
  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)
  3571     // if its empty dont select entire doc - that would select the bogus node
  3572     return aSelection->Collapse(rootElement, 0);
  3575   return nsEditor::SelectEntireDocument(aSelection);
  3578 NS_IMETHODIMP
  3579 nsHTMLEditor::SelectAll()
  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);
  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);
  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)
  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;
  3639 void nsHTMLEditor::IsTextPropertySetByContent(nsIDOMNode        *aNode,
  3640                                               nsIAtom           *aProperty, 
  3641                                               const nsAString   *aAttribute, 
  3642                                               const nsAString   *aValue, 
  3643                                               bool              &aIsSet,
  3644                                               nsAString *outValue)
  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)
  3654     nsCOMPtr<nsIDOMElement>element;
  3655     element = do_QueryInterface(node);
  3656     if (element)
  3658       nsAutoString tag, value;
  3659       element->GetTagName(tag);
  3660       if (propName.Equals(tag, nsCaseInsensitiveStringComparator()))
  3662         bool found = false;
  3663         if (aAttribute && 0!=aAttribute->Length())
  3665           element->GetAttribute(*aAttribute, value);
  3666           if (outValue) *outValue = value;
  3667           if (!value.IsEmpty())
  3669             if (!aValue) {
  3670               found = true;
  3672             else
  3674               nsString tString(*aValue);
  3675               if (tString.Equals(value, nsCaseInsensitiveStringComparator())) {
  3676                 found = true;
  3678               else {  // we found the prop with the attribute, but the value doesn't match
  3679                 break;
  3684         else { 
  3685           found = true;
  3687         if (found)
  3689           aIsSet = true;
  3690           break;
  3694     nsCOMPtr<nsIDOMNode>temp;
  3695     result = node->GetParentNode(getter_AddRefs(temp));
  3696     if (NS_SUCCEEDED(result) && temp) {
  3697       node = temp;
  3699     else {
  3700       node = nullptr;
  3706 //================================================================
  3707 // HTML Editor methods
  3708 //
  3709 // Note: Table Editing methods are implemented in nsTableEditor.cpp
  3710 //
  3713 bool
  3714 nsHTMLEditor::SetCaretInTableCell(nsIDOMElement* aElement)
  3716   nsCOMPtr<dom::Element> element = do_QueryInterface(aElement);
  3717   if (!element || !element->IsHTML() ||
  3718       !nsHTMLEditUtils::IsTableElement(element) ||
  3719       !IsDescendantOfEditorRoot(element)) {
  3720     return false;
  3723   nsIContent* node = element;
  3724   while (node->HasChildren()) {
  3725     node = node->GetFirstChild();
  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));
  3737 ///////////////////////////////////////////////////////////////////////////
  3738 // GetEnclosingTable: find ancestor who is a table, if any
  3739 //                  
  3740 nsCOMPtr<nsIDOMNode> 
  3741 nsHTMLEditor::GetEnclosingTable(nsIDOMNode *aNode)
  3743   NS_PRECONDITION(aNode, "null node passed to nsHTMLEditor::GetEnclosingTable");
  3744   nsCOMPtr<nsIDOMNode> tbl, tmp, node = aNode;
  3746   while (!tbl)
  3748     tmp = GetBlockNodeParent(node);
  3749     if (!tmp) break;
  3750     if (nsHTMLEditUtils::IsTable(tmp)) tbl = tmp;
  3751     node = tmp;
  3753   return tbl;
  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)
  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())
  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);
  3791     iter->Next();
  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)
  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))
  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);
  3818     textNodes.RemoveElementAt(0); // remove the leftmost text node from the list
  3821   return result;
  3824 NS_IMETHODIMP 
  3825 nsHTMLEditor::SetSelectionAtDocumentStart(nsISelection *aSelection)
  3827   dom::Element* rootElement = GetRoot();
  3828   NS_ENSURE_TRUE(rootElement, NS_ERROR_NULL_POINTER);
  3830   return aSelection->CollapseNative(rootElement, 0);
  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)
  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
  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))
  3869       res = GetFirstEditableChild(inNode, address_of(child));
  3870       NS_ENSURE_SUCCESS(res, res);
  3871       if (child && !IsBlockNode(child))
  3873         // insert br node
  3874         res = CreateBR(inNode, 0, address_of(unused));
  3875         NS_ENSURE_SUCCESS(res, res);
  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))
  3889       res = GetLastEditableChild(inNode, address_of(child));
  3890       NS_ENSURE_SUCCESS(res, res);
  3891       if (child && !IsBlockNode(child) && !nsTextEditUtils::IsBreak(child))
  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);
  3902   else  // the case of inNode being empty
  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))
  3914       res = GetNextHTMLSibling(inNode, address_of(sibling));
  3915       NS_ENSURE_SUCCESS(res, res);
  3916       if (sibling && !IsBlockNode(sibling) && !nsTextEditUtils::IsBreak(sibling))
  3918         // insert br node
  3919         res = CreateBR(inNode, 0, address_of(unused));
  3920         NS_ENSURE_SUCCESS(res, res);
  3925   // now remove container
  3926   return RemoveContainer(inNode);
  3930 ///////////////////////////////////////////////////////////////////////////
  3931 // GetPriorHTMLSibling: returns the previous editable sibling, if there is
  3932 //                   one within the parent
  3933 //                       
  3934 nsIContent*
  3935 nsHTMLEditor::GetPriorHTMLSibling(nsINode* aNode)
  3937   MOZ_ASSERT(aNode);
  3939   nsIContent* node = aNode->GetPreviousSibling();
  3940   while (node && !IsEditable(node)) {
  3941     node = node->GetPreviousSibling();
  3944   return node;
  3947 nsresult
  3948 nsHTMLEditor::GetPriorHTMLSibling(nsIDOMNode *inNode, nsCOMPtr<nsIDOMNode> *outNode)
  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;
  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)
  3970   MOZ_ASSERT(aParent);
  3972   nsIContent* node = aParent->GetChildAt(aOffset - 1);
  3973   if (!node || IsEditable(node)) {
  3974     return node;
  3977   return GetPriorHTMLSibling(node);
  3980 nsresult
  3981 nsHTMLEditor::GetPriorHTMLSibling(nsIDOMNode *inParent, int32_t inOffset, nsCOMPtr<nsIDOMNode> *outNode)
  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;
  3995 ///////////////////////////////////////////////////////////////////////////
  3996 // GetNextHTMLSibling: returns the next editable sibling, if there is
  3997 //                   one within the parent
  3998 //                       
  3999 nsIContent*
  4000 nsHTMLEditor::GetNextHTMLSibling(nsINode* aNode)
  4002   MOZ_ASSERT(aNode);
  4004   nsIContent* node = aNode->GetNextSibling();
  4005   while (node && !IsEditable(node)) {
  4006     node = node->GetNextSibling();
  4009   return node;
  4012 nsresult
  4013 nsHTMLEditor::GetNextHTMLSibling(nsIDOMNode *inNode, nsCOMPtr<nsIDOMNode> *outNode)
  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;
  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)
  4034   MOZ_ASSERT(aParent);
  4036   nsIContent* node = aParent->GetChildAt(aOffset + 1);
  4037   if (!node || IsEditable(node)) {
  4038     return node;
  4041   return GetNextHTMLSibling(node);
  4044 nsresult
  4045 nsHTMLEditor::GetNextHTMLSibling(nsIDOMNode *inParent, int32_t inOffset, nsCOMPtr<nsIDOMNode> *outNode)
  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;
  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)
  4066   MOZ_ASSERT(aNode);
  4068   if (!GetActiveEditingHost()) {
  4069     return nullptr;
  4072   return GetPriorNode(aNode, true, aNoBlockCrossing);
  4075 nsresult
  4076 nsHTMLEditor::GetPriorHTMLNode(nsIDOMNode* aNode,
  4077                                nsCOMPtr<nsIDOMNode>* aResultNode,
  4078                                bool aNoBlockCrossing)
  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;
  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)
  4097   MOZ_ASSERT(aParent);
  4099   if (!GetActiveEditingHost()) {
  4100     return nullptr;
  4103   return GetPriorNode(aParent, aOffset, true, aNoBlockCrossing);
  4106 nsresult
  4107 nsHTMLEditor::GetPriorHTMLNode(nsIDOMNode* aNode, int32_t aOffset,
  4108                                nsCOMPtr<nsIDOMNode>* aResultNode,
  4109                                bool aNoBlockCrossing)
  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;
  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)
  4129   MOZ_ASSERT(aNode);
  4131   nsIContent* result = GetNextNode(aNode, true, aNoBlockCrossing);
  4133   if (result && !IsDescendantOfEditorRoot(result)) {
  4134     return nullptr;
  4137   return result;
  4140 nsresult
  4141 nsHTMLEditor::GetNextHTMLNode(nsIDOMNode* aNode,
  4142                               nsCOMPtr<nsIDOMNode>* aResultNode,
  4143                               bool aNoBlockCrossing)
  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;
  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)
  4162   nsIContent* content = GetNextNode(aParent, aOffset, true, aNoBlockCrossing);
  4163   if (content && !IsDescendantOfEditorRoot(content)) {
  4164     return nullptr;
  4166   return content;
  4169 nsresult
  4170 nsHTMLEditor::GetNextHTMLNode(nsIDOMNode* aNode, int32_t aOffset,
  4171                               nsCOMPtr<nsIDOMNode>* aResultNode,
  4172                               bool aNoBlockCrossing)
  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;
  4185 nsresult 
  4186 nsHTMLEditor::IsFirstEditableChild( nsIDOMNode *aNode, bool *aOutIsFirst)
  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;
  4207 nsresult 
  4208 nsHTMLEditor::IsLastEditableChild( nsIDOMNode *aNode, bool *aOutIsLast)
  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;
  4229 nsresult 
  4230 nsHTMLEditor::GetFirstEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutFirstChild)
  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))
  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;
  4252   *aOutFirstChild = child;
  4253   return res;
  4257 nsresult 
  4258 nsHTMLEditor::GetLastEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutLastChild)
  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))
  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;
  4280   *aOutLastChild = child;
  4281   return res;
  4284 nsresult 
  4285 nsHTMLEditor::GetFirstEditableLeaf( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutFirstLeaf)
  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)))
  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
  4309       child = nullptr;  // this will abort the loop
  4313   *aOutFirstLeaf = child;
  4314   return res;
  4318 nsresult 
  4319 nsHTMLEditor::GetLastEditableLeaf(nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutLastLeaf)
  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)))
  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
  4342       child = nullptr;
  4346   *aOutLastLeaf = child;
  4347   return res;
  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)
  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)
  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) 
  4383       *outIsEmptyNode = false;
  4386   else if (length)
  4388     if (aNode->TextIsOnlyWhitespace())
  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);
  4401     else
  4403       *outIsEmptyNode = false;
  4406   return NS_OK;  
  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)
  4422   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
  4423   return IsEmptyNode(node, outIsEmptyNode, aSingleBRDoesntCount,
  4424                      aListOrCellNotEmpty, aSafeToAskFrames);
  4427 nsresult
  4428 nsHTMLEditor::IsEmptyNode(nsINode* aNode,
  4429                           bool* outIsEmptyNode,
  4430                           bool aSingleBRDoesntCount,
  4431                           bool aListOrCellNotEmpty,
  4432                           bool aSafeToAskFrames)
  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);
  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)
  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);
  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;
  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;
  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;
  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;
  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;
  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;
  4538   return NS_OK;
  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)
  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);
  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);
  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);
  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);
  4605   return res;
  4608 nsresult
  4609 nsHTMLEditor::RemoveAttributeOrEquivalent(nsIDOMElement* aElement,
  4610                                           const nsAString& aAttribute,
  4611                                           bool aSuppressTransaction)
  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);
  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);
  4634   return res;
  4637 nsresult
  4638 nsHTMLEditor::SetIsCSSEnabled(bool aIsCSSPrefChecked)
  4640   if (!mHTMLCSSUtils) {
  4641     return NS_ERROR_NOT_INITIALIZED;
  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;
  4656   return SetFlags(flags);
  4659 // Set the block background color
  4660 NS_IMETHODIMP
  4661 nsHTMLEditor::SetCSSBackgroundColor(const nsAString& aColor)
  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)
  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))
  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)
  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);
  4718       else if ((startNode == endNode) && nsTextEditUtils::IsBody(startNode) && isCollapsed)
  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);
  4726       else if ((startNode == endNode) && (((endOffset-startOffset) == 1) || (!startOffset && !endOffset)))
  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);
  4738         if (cachedBlockParent != blockParent)
  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);
  4747       else
  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))
  4776           while (!iter->IsDone())
  4778             node = do_QueryInterface(iter->GetCurrentNode());
  4779             NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
  4781             if (IsEditable(node))
  4783               arrayOfNodes.AppendObject(node);
  4786             iter->Next();
  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))
  4794           nsCOMPtr<nsIDOMNode> blockParent;
  4795           blockParent = GetBlockNodeParent(startNode);
  4796           if (cachedBlockParent != blockParent)
  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);
  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++)
  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);
  4821           if (cachedBlockParent != blockParent)
  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);
  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))
  4838           nsCOMPtr<nsIDOMNode> blockParent;
  4839           blockParent = GetBlockNodeParent(endNode);
  4840           if (cachedBlockParent != blockParent)
  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);
  4852   if (!cancel)
  4854     // post-process
  4855     res = mRules->DidDoAction(selection, &ruleInfo, res);
  4857   return res;
  4860 NS_IMETHODIMP
  4861 nsHTMLEditor::SetBackgroundColor(const nsAString& aColor)
  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);
  4870   else {
  4871     // but in HTML mode, we can only set the document's background color
  4872     res = SetHTMLBackgroundColor(aColor);
  4874   return res;
  4877 ///////////////////////////////////////////////////////////////////////////
  4878 // NodesSameType: do these nodes have the same tag?
  4879 //                    
  4880 /* virtual */
  4881 bool
  4882 nsHTMLEditor::AreNodesSameType(nsIContent* aNode1, nsIContent* aNode2)
  4884   MOZ_ASSERT(aNode1);
  4885   MOZ_ASSERT(aNode2);
  4887   if (aNode1->Tag() != aNode2->Tag()) {
  4888     return false;
  4891   if (!IsCSSEnabled() || !aNode1->IsHTML(nsGkAtoms::span)) {
  4892     return true;
  4895   // If CSS is enabled, we are stricter about span nodes.
  4896   return mHTMLCSSUtils->ElementsSameStyle(aNode1->AsDOMNode(),
  4897                                           aNode2->AsDOMNode());
  4900 NS_IMETHODIMP
  4901 nsHTMLEditor::CopyLastEditableChildStyles(nsIDOMNode * aPreviousBlock, nsIDOMNode * aNewBlock,
  4902                                           nsIDOMNode **aOutBrNode)
  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)
  4911     res = DeleteNode(child);
  4912     NS_ENSURE_SUCCESS(res, res);
  4913     res = aNewBlock->GetFirstChild(getter_AddRefs(child));
  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);
  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;
  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;
  4942       else {
  4943         res = CreateNode(domTagName, aNewBlock, 0, getter_AddRefs(newStyles));
  4944         NS_ENSURE_SUCCESS(res, res);
  4945         deepestStyle = newStyles;
  4947       res = CloneAttributes(newStyles, child);
  4948       NS_ENSURE_SUCCESS(res, res);
  4950     nsCOMPtr<nsIDOMNode> tmp;
  4951     res = child->GetParentNode(getter_AddRefs(tmp));
  4952     NS_ENSURE_SUCCESS(res, res);
  4953     child = tmp;
  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);
  4962   return NS_OK;
  4965 nsresult
  4966 nsHTMLEditor::GetElementOrigin(nsIDOMElement * aElement, int32_t & aX, int32_t & aY)
  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;
  4988 nsresult
  4989 nsHTMLEditor::EndUpdateViewBatch()
  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);
  5007   return res;
  5010 NS_IMETHODIMP
  5011 nsHTMLEditor::GetSelectionContainer(nsIDOMElement ** aReturn)
  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);
  5054       if (!focusNode) {
  5055         res = range->GetCommonAncestorContainer(getter_AddRefs(focusNode));
  5056         NS_ENSURE_SUCCESS(res, res);
  5059     else {
  5060       int32_t i;
  5061       nsCOMPtr<nsIDOMRange> range;
  5062       for (i = 0; i < rangeCount; i++)
  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;
  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;
  5091   nsCOMPtr<nsIDOMElement> focusElement = do_QueryInterface(focusNode);
  5092   focusElement.forget(aReturn);
  5093   return NS_OK;
  5096 NS_IMETHODIMP
  5097 nsHTMLEditor::IsAnonymousElement(nsIDOMElement * aElement, bool * aReturn)
  5099   NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
  5100   nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
  5101   *aReturn = content->IsRootOfNativeAnonymousSubtree();
  5102   return NS_OK;
  5105 nsresult
  5106 nsHTMLEditor::SetReturnInParagraphCreatesNewParagraph(bool aCreatesNewParagraph)
  5108   mCRInParagraphCreatesParagraph = aCreatesNewParagraph;
  5109   return NS_OK;
  5112 bool
  5113 nsHTMLEditor::GetReturnInParagraphCreatesNewParagraph()
  5115   return mCRInParagraphCreatesParagraph;
  5118 nsresult
  5119 nsHTMLEditor::GetReturnInParagraphCreatesNewParagraph(bool *aCreatesNewParagraph)
  5121   *aCreatesNewParagraph = mCRInParagraphCreatesParagraph;
  5122   return NS_OK;
  5125 already_AddRefed<nsIContent>
  5126 nsHTMLEditor::GetFocusedContent()
  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();
  5143     return nullptr;
  5146   if (inDesignMode) {
  5147     return OurWindowHasFocus() &&
  5148       nsContentUtils::ContentIsDescendantOf(focusedContent, doc) ?
  5149       focusedContent.forget() : nullptr;
  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;
  5160   // If our window is focused, we're focused.
  5161   return OurWindowHasFocus() ? focusedContent.forget() : nullptr;
  5164 already_AddRefed<nsIContent>
  5165 nsHTMLEditor::GetFocusedContentForIME()
  5167   nsCOMPtr<nsIContent> focusedContent = GetFocusedContent();
  5168   if (!focusedContent) {
  5169     return nullptr;
  5172   nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
  5173   NS_ENSURE_TRUE(doc, nullptr);
  5174   return doc->HasFlag(NODE_IS_EDITABLE) ? nullptr : focusedContent.forget();
  5177 bool
  5178 nsHTMLEditor::IsActiveInDOMWindow()
  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;
  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;
  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;
  5210   return true;
  5213 dom::Element*
  5214 nsHTMLEditor::GetActiveEditingHost()
  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();
  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;
  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;
  5242   return content->GetEditingHost();
  5245 already_AddRefed<mozilla::dom::EventTarget>
  5246 nsHTMLEditor::GetDOMEventTarget()
  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();
  5256 bool
  5257 nsHTMLEditor::ShouldReplaceRootElement()
  5259   if (!mRootElement) {
  5260     // If we don't know what is our root element, we should find our root.
  5261     return true;
  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);
  5271 void
  5272 nsHTMLEditor::ResetRootElementAndEventTarget()
  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;
  5286   // We must have mRootElement now.
  5287   nsCOMPtr<nsIDOMElement> root;
  5288   rv = GetRootElement(getter_AddRefs(root));
  5289   if (NS_FAILED(rv) || !mRootElement) {
  5290     return;
  5293   rv = BeginningOfDocument();
  5294   if (NS_FAILED(rv)) {
  5295     return;
  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);
  5306   SyncRealTimeSpell();
  5309 nsresult
  5310 nsHTMLEditor::GetBodyElement(nsIDOMHTMLElement** aBody)
  5312   NS_PRECONDITION(mDocWeak, "bad state, null mDocWeak");
  5313   nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryReferent(mDocWeak);
  5314   if (!htmlDoc) {
  5315     return NS_ERROR_NOT_INITIALIZED;
  5317   return htmlDoc->GetBody(aBody);
  5320 already_AddRefed<nsINode>
  5321 nsHTMLEditor::GetFocusedNode()
  5323   nsCOMPtr<nsIContent> focusedContent = GetFocusedContent();
  5324   if (!focusedContent) {
  5325     return nullptr;
  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();
  5337   nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
  5338   return doc.forget();
  5341 bool
  5342 nsHTMLEditor::OurWindowHasFocus()
  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;
  5352   nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
  5353   nsCOMPtr<nsIDOMWindow> ourWindow = do_QueryInterface(doc->GetWindow());
  5354   return ourWindow == focusedWindow;
  5357 bool
  5358 nsHTMLEditor::IsAcceptableInputEvent(nsIDOMEvent* aEvent)
  5360   if (!nsEditor::IsAcceptableInputEvent(aEvent)) {
  5361     return false;
  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;
  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();
  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;
  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;
  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;
  5411     // If the clicked element has an independent selection, we shouldn't
  5412     // handle this click event.
  5413     if (targetContent->HasIndependentSelection()) {
  5414       return false;
  5416     // If the target content is editable, we should handle this event.
  5417     return targetContent->HasFlag(NODE_IS_EDITABLE);
  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;
  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();
  5435 NS_IMETHODIMP
  5436 nsHTMLEditor::GetPreferredIMEState(IMEState *aState)
  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;
  5445   return NS_OK;
  5448 already_AddRefed<nsIContent>
  5449 nsHTMLEditor::GetInputEventTargetContent()
  5451   nsCOMPtr<nsIContent> target = GetActiveEditingHost();
  5452   return target.forget();
  5455 bool
  5456 nsHTMLEditor::IsEditable(nsIContent* aNode) {
  5457   if (!nsPlaintextEditor::IsEditable(aNode)) {
  5458     return false;
  5460   if (aNode->IsElement()) {
  5461     // If we're dealing with an element, then ask it whether it's editable.
  5462     return aNode->IsEditable();
  5464   // We might be dealing with a text node for example, which we always consider
  5465   // to be editable.
  5466   return true;
  5469 // virtual MOZ_OVERRIDE
  5470 dom::Element*
  5471 nsHTMLEditor::GetEditorRoot()
  5473   return GetActiveEditingHost();

mercurial