1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/editor/libeditor/html/nsHTMLEditor.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,5474 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "mozilla/DebugOnly.h" 1.10 +#include "mozilla/EventStates.h" 1.11 +#include "mozilla/TextEvents.h" 1.12 + 1.13 +#include "nsCRT.h" 1.14 + 1.15 +#include "nsUnicharUtils.h" 1.16 + 1.17 +#include "nsHTMLEditor.h" 1.18 +#include "nsHTMLEditRules.h" 1.19 +#include "nsTextEditUtils.h" 1.20 +#include "nsHTMLEditUtils.h" 1.21 + 1.22 +#include "nsHTMLEditorEventListener.h" 1.23 +#include "TypeInState.h" 1.24 + 1.25 +#include "nsHTMLURIRefObject.h" 1.26 + 1.27 +#include "nsIDOMText.h" 1.28 +#include "nsIDOMMozNamedAttrMap.h" 1.29 +#include "nsIDOMNodeList.h" 1.30 +#include "nsIDOMDocument.h" 1.31 +#include "nsIDOMAttr.h" 1.32 +#include "nsIDocumentInlines.h" 1.33 +#include "nsIDOMEventTarget.h" 1.34 +#include "nsIDOMKeyEvent.h" 1.35 +#include "nsIDOMMouseEvent.h" 1.36 +#include "nsIDOMHTMLAnchorElement.h" 1.37 +#include "nsISelectionController.h" 1.38 +#include "nsIDOMHTMLDocument.h" 1.39 +#include "nsILinkHandler.h" 1.40 +#include "nsIInlineSpellChecker.h" 1.41 + 1.42 +#include "mozilla/css/Loader.h" 1.43 +#include "nsCSSStyleSheet.h" 1.44 +#include "nsIDOMStyleSheet.h" 1.45 + 1.46 +#include "nsIContent.h" 1.47 +#include "nsIContentIterator.h" 1.48 +#include "nsIDOMRange.h" 1.49 +#include "nsISupportsArray.h" 1.50 +#include "nsContentUtils.h" 1.51 +#include "nsIDocumentEncoder.h" 1.52 +#include "nsIDOMDocumentFragment.h" 1.53 +#include "nsIPresShell.h" 1.54 +#include "nsPresContext.h" 1.55 +#include "SetDocTitleTxn.h" 1.56 +#include "nsFocusManager.h" 1.57 +#include "nsPIDOMWindow.h" 1.58 + 1.59 +// netwerk 1.60 +#include "nsIURI.h" 1.61 +#include "nsNetUtil.h" 1.62 + 1.63 +// Transactionas 1.64 +#include "nsStyleSheetTxns.h" 1.65 + 1.66 +// Misc 1.67 +#include "TextEditorTest.h" 1.68 +#include "nsEditorUtils.h" 1.69 +#include "nsWSRunObject.h" 1.70 +#include "nsGkAtoms.h" 1.71 +#include "nsIWidget.h" 1.72 + 1.73 +#include "nsIFrame.h" 1.74 +#include "nsIParserService.h" 1.75 +#include "mozilla/dom/Selection.h" 1.76 +#include "mozilla/dom/Element.h" 1.77 +#include "mozilla/dom/EventTarget.h" 1.78 +#include "mozilla/dom/HTMLBodyElement.h" 1.79 +#include "nsTextFragment.h" 1.80 + 1.81 +using namespace mozilla; 1.82 +using namespace mozilla::dom; 1.83 +using namespace mozilla::widget; 1.84 + 1.85 +// Some utilities to handle annoying overloading of "A" tag for link and named anchor 1.86 +static char hrefText[] = "href"; 1.87 +static char anchorTxt[] = "anchor"; 1.88 +static char namedanchorText[] = "namedanchor"; 1.89 + 1.90 +#define IsLinkTag(s) (s.EqualsIgnoreCase(hrefText)) 1.91 +#define IsNamedAnchorTag(s) (s.EqualsIgnoreCase(anchorTxt) || s.EqualsIgnoreCase(namedanchorText)) 1.92 + 1.93 +nsHTMLEditor::nsHTMLEditor() 1.94 +: nsPlaintextEditor() 1.95 +, mCRInParagraphCreatesParagraph(false) 1.96 +, mSelectedCellIndex(0) 1.97 +, mIsObjectResizingEnabled(true) 1.98 +, mIsResizing(false) 1.99 +, mIsAbsolutelyPositioningEnabled(true) 1.100 +, mResizedObjectIsAbsolutelyPositioned(false) 1.101 +, mGrabberClicked(false) 1.102 +, mIsMoving(false) 1.103 +, mSnapToGridEnabled(false) 1.104 +, mIsInlineTableEditingEnabled(true) 1.105 +, mInfoXIncrement(20) 1.106 +, mInfoYIncrement(20) 1.107 +, mGridSize(0) 1.108 +{ 1.109 +} 1.110 + 1.111 +nsHTMLEditor::~nsHTMLEditor() 1.112 +{ 1.113 + // remove the rules as an action listener. Else we get a bad 1.114 + // ownership loop later on. it's ok if the rules aren't a listener; 1.115 + // we ignore the error. 1.116 + nsCOMPtr<nsIEditActionListener> mListener = do_QueryInterface(mRules); 1.117 + RemoveEditActionListener(mListener); 1.118 + 1.119 + //the autopointers will clear themselves up. 1.120 + //but we need to also remove the listeners or we have a leak 1.121 + nsCOMPtr<nsISelection>selection; 1.122 + nsresult result = GetSelection(getter_AddRefs(selection)); 1.123 + // if we don't get the selection, just skip this 1.124 + if (NS_SUCCEEDED(result) && selection) 1.125 + { 1.126 + nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection)); 1.127 + nsCOMPtr<nsISelectionListener>listener; 1.128 + listener = do_QueryInterface(mTypeInState); 1.129 + if (listener) 1.130 + { 1.131 + selPriv->RemoveSelectionListener(listener); 1.132 + } 1.133 + listener = do_QueryInterface(mSelectionListenerP); 1.134 + if (listener) 1.135 + { 1.136 + selPriv->RemoveSelectionListener(listener); 1.137 + } 1.138 + } 1.139 + 1.140 + mTypeInState = nullptr; 1.141 + mSelectionListenerP = nullptr; 1.142 + 1.143 + // free any default style propItems 1.144 + RemoveAllDefaultProperties(); 1.145 + 1.146 + if (mLinkHandler && mDocWeak) 1.147 + { 1.148 + nsCOMPtr<nsIPresShell> ps = GetPresShell(); 1.149 + 1.150 + if (ps && ps->GetPresContext()) 1.151 + { 1.152 + ps->GetPresContext()->SetLinkHandler(mLinkHandler); 1.153 + } 1.154 + } 1.155 + 1.156 + RemoveEventListeners(); 1.157 +} 1.158 + 1.159 +void 1.160 +nsHTMLEditor::HideAnonymousEditingUIs() 1.161 +{ 1.162 + if (mAbsolutelyPositionedObject) 1.163 + HideGrabber(); 1.164 + if (mInlineEditedCell) 1.165 + HideInlineTableEditingUI(); 1.166 + if (mResizedObject) 1.167 + HideResizers(); 1.168 +} 1.169 + 1.170 +NS_IMPL_CYCLE_COLLECTION_CLASS(nsHTMLEditor) 1.171 + 1.172 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsHTMLEditor, nsPlaintextEditor) 1.173 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mTypeInState) 1.174 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mStyleSheets) 1.175 + 1.176 + tmp->HideAnonymousEditingUIs(); 1.177 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.178 + 1.179 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsHTMLEditor, nsPlaintextEditor) 1.180 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTypeInState) 1.181 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheets) 1.182 + 1.183 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopLeftHandle) 1.184 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopHandle) 1.185 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopRightHandle) 1.186 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLeftHandle) 1.187 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRightHandle) 1.188 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBottomLeftHandle) 1.189 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBottomHandle) 1.190 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBottomRightHandle) 1.191 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActivatedHandle) 1.192 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResizingShadow) 1.193 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResizingInfo) 1.194 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResizedObject) 1.195 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMouseMotionListenerP) 1.196 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectionListenerP) 1.197 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResizeEventListenerP) 1.198 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(objectResizeEventListeners) 1.199 + 1.200 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAbsolutelyPositionedObject) 1.201 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGrabber) 1.202 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPositioningShadow) 1.203 + 1.204 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInlineEditedCell) 1.205 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAddColumnBeforeButton) 1.206 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRemoveColumnButton) 1.207 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAddColumnAfterButton) 1.208 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAddRowBeforeButton) 1.209 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRemoveRowButton) 1.210 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAddRowAfterButton) 1.211 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.212 + 1.213 +NS_IMPL_ADDREF_INHERITED(nsHTMLEditor, nsEditor) 1.214 +NS_IMPL_RELEASE_INHERITED(nsHTMLEditor, nsEditor) 1.215 + 1.216 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsHTMLEditor) 1.217 + NS_INTERFACE_MAP_ENTRY(nsIHTMLEditor) 1.218 + NS_INTERFACE_MAP_ENTRY(nsIHTMLObjectResizer) 1.219 + NS_INTERFACE_MAP_ENTRY(nsIHTMLAbsPosEditor) 1.220 + NS_INTERFACE_MAP_ENTRY(nsIHTMLInlineTableEditor) 1.221 + NS_INTERFACE_MAP_ENTRY(nsITableEditor) 1.222 + NS_INTERFACE_MAP_ENTRY(nsIEditorStyleSheets) 1.223 + NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver) 1.224 + NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) 1.225 +NS_INTERFACE_MAP_END_INHERITING(nsPlaintextEditor) 1.226 + 1.227 + 1.228 +NS_IMETHODIMP 1.229 +nsHTMLEditor::Init(nsIDOMDocument *aDoc, 1.230 + nsIContent *aRoot, 1.231 + nsISelectionController *aSelCon, 1.232 + uint32_t aFlags, 1.233 + const nsAString& aInitialValue) 1.234 +{ 1.235 + NS_PRECONDITION(aDoc && !aSelCon, "bad arg"); 1.236 + NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER); 1.237 + MOZ_ASSERT(aInitialValue.IsEmpty(), "Non-empty initial values not supported"); 1.238 + 1.239 + nsresult result = NS_OK, rulesRes = NS_OK; 1.240 + 1.241 + if (1) 1.242 + { 1.243 + // block to scope nsAutoEditInitRulesTrigger 1.244 + nsAutoEditInitRulesTrigger rulesTrigger(static_cast<nsPlaintextEditor*>(this), rulesRes); 1.245 + 1.246 + // Init the plaintext editor 1.247 + result = nsPlaintextEditor::Init(aDoc, aRoot, nullptr, aFlags, aInitialValue); 1.248 + if (NS_FAILED(result)) { return result; } 1.249 + 1.250 + // Init mutation observer 1.251 + nsCOMPtr<nsINode> document = do_QueryInterface(aDoc); 1.252 + document->AddMutationObserverUnlessExists(this); 1.253 + 1.254 + // disable Composer-only features 1.255 + if (IsMailEditor()) 1.256 + { 1.257 + SetAbsolutePositioningEnabled(false); 1.258 + SetSnapToGridEnabled(false); 1.259 + } 1.260 + 1.261 + // Init the HTML-CSS utils 1.262 + mHTMLCSSUtils = new nsHTMLCSSUtils(this); 1.263 + 1.264 + // disable links 1.265 + nsCOMPtr<nsIPresShell> presShell = GetPresShell(); 1.266 + NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE); 1.267 + nsPresContext *context = presShell->GetPresContext(); 1.268 + NS_ENSURE_TRUE(context, NS_ERROR_NULL_POINTER); 1.269 + if (!IsPlaintextEditor() && !IsInteractionAllowed()) { 1.270 + mLinkHandler = context->GetLinkHandler(); 1.271 + 1.272 + context->SetLinkHandler(nullptr); 1.273 + } 1.274 + 1.275 + // init the type-in state 1.276 + mTypeInState = new TypeInState(); 1.277 + 1.278 + // init the selection listener for image resizing 1.279 + mSelectionListenerP = new ResizerSelectionListener(this); 1.280 + 1.281 + if (!IsInteractionAllowed()) { 1.282 + // ignore any errors from this in case the file is missing 1.283 + AddOverrideStyleSheet(NS_LITERAL_STRING("resource://gre/res/EditorOverride.css")); 1.284 + } 1.285 + 1.286 + nsCOMPtr<nsISelection>selection; 1.287 + result = GetSelection(getter_AddRefs(selection)); 1.288 + if (NS_FAILED(result)) { return result; } 1.289 + if (selection) 1.290 + { 1.291 + nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection)); 1.292 + nsCOMPtr<nsISelectionListener>listener; 1.293 + listener = do_QueryInterface(mTypeInState); 1.294 + if (listener) { 1.295 + selPriv->AddSelectionListener(listener); 1.296 + } 1.297 + listener = do_QueryInterface(mSelectionListenerP); 1.298 + if (listener) { 1.299 + selPriv->AddSelectionListener(listener); 1.300 + } 1.301 + } 1.302 + } 1.303 + 1.304 + NS_ENSURE_SUCCESS(rulesRes, rulesRes); 1.305 + return result; 1.306 +} 1.307 + 1.308 +NS_IMETHODIMP 1.309 +nsHTMLEditor::PreDestroy(bool aDestroyingFrames) 1.310 +{ 1.311 + if (mDidPreDestroy) { 1.312 + return NS_OK; 1.313 + } 1.314 + 1.315 + nsCOMPtr<nsINode> document = do_QueryReferent(mDocWeak); 1.316 + if (document) { 1.317 + document->RemoveMutationObserver(this); 1.318 + } 1.319 + 1.320 + while (mStyleSheetURLs.Length()) 1.321 + { 1.322 + RemoveOverrideStyleSheet(mStyleSheetURLs[0]); 1.323 + } 1.324 + 1.325 + // Clean up after our anonymous content -- we don't want these nodes to 1.326 + // stay around (which they would, since the frames have an owning reference). 1.327 + HideAnonymousEditingUIs(); 1.328 + 1.329 + return nsPlaintextEditor::PreDestroy(aDestroyingFrames); 1.330 +} 1.331 + 1.332 +NS_IMETHODIMP 1.333 +nsHTMLEditor::GetRootElement(nsIDOMElement **aRootElement) 1.334 +{ 1.335 + NS_ENSURE_ARG_POINTER(aRootElement); 1.336 + 1.337 + if (mRootElement) { 1.338 + return nsEditor::GetRootElement(aRootElement); 1.339 + } 1.340 + 1.341 + *aRootElement = nullptr; 1.342 + 1.343 + // Use the HTML documents body element as the editor root if we didn't 1.344 + // get a root element during initialization. 1.345 + 1.346 + nsCOMPtr<nsIDOMElement> rootElement; 1.347 + nsCOMPtr<nsIDOMHTMLElement> bodyElement; 1.348 + nsresult rv = GetBodyElement(getter_AddRefs(bodyElement)); 1.349 + NS_ENSURE_SUCCESS(rv, rv); 1.350 + 1.351 + if (bodyElement) { 1.352 + rootElement = bodyElement; 1.353 + } else { 1.354 + // If there is no HTML body element, 1.355 + // we should use the document root element instead. 1.356 + nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(mDocWeak); 1.357 + NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED); 1.358 + 1.359 + rv = doc->GetDocumentElement(getter_AddRefs(rootElement)); 1.360 + NS_ENSURE_SUCCESS(rv, rv); 1.361 + // Document can have no elements 1.362 + if (!rootElement) { 1.363 + return NS_ERROR_NOT_AVAILABLE; 1.364 + } 1.365 + } 1.366 + 1.367 + mRootElement = do_QueryInterface(rootElement); 1.368 + rootElement.forget(aRootElement); 1.369 + 1.370 + return NS_OK; 1.371 +} 1.372 + 1.373 +already_AddRefed<nsIContent> 1.374 +nsHTMLEditor::FindSelectionRoot(nsINode *aNode) 1.375 +{ 1.376 + NS_PRECONDITION(aNode->IsNodeOfType(nsINode::eDOCUMENT) || 1.377 + aNode->IsNodeOfType(nsINode::eCONTENT), 1.378 + "aNode must be content or document node"); 1.379 + 1.380 + nsCOMPtr<nsIDocument> doc = aNode->GetCurrentDoc(); 1.381 + if (!doc) { 1.382 + return nullptr; 1.383 + } 1.384 + 1.385 + nsCOMPtr<nsIContent> content; 1.386 + if (doc->HasFlag(NODE_IS_EDITABLE) || !aNode->IsContent()) { 1.387 + content = doc->GetRootElement(); 1.388 + return content.forget(); 1.389 + } 1.390 + content = aNode->AsContent(); 1.391 + 1.392 + // XXX If we have readonly flag, shouldn't return the element which has 1.393 + // contenteditable="true"? However, such case isn't there without chrome 1.394 + // permission script. 1.395 + if (IsReadonly()) { 1.396 + // We still want to allow selection in a readonly editor. 1.397 + content = do_QueryInterface(GetRoot()); 1.398 + return content.forget(); 1.399 + } 1.400 + 1.401 + if (!content->HasFlag(NODE_IS_EDITABLE)) { 1.402 + // If the content is in read-write state but is not editable itself, 1.403 + // return it as the selection root. 1.404 + if (content->IsElement() && 1.405 + content->AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE)) { 1.406 + return content.forget(); 1.407 + } 1.408 + return nullptr; 1.409 + } 1.410 + 1.411 + // For non-readonly editors we want to find the root of the editable subtree 1.412 + // containing aContent. 1.413 + content = content->GetEditingHost(); 1.414 + return content.forget(); 1.415 +} 1.416 + 1.417 +/* virtual */ 1.418 +void 1.419 +nsHTMLEditor::CreateEventListeners() 1.420 +{ 1.421 + // Don't create the handler twice 1.422 + if (!mEventListener) { 1.423 + mEventListener = new nsHTMLEditorEventListener(); 1.424 + } 1.425 +} 1.426 + 1.427 +nsresult 1.428 +nsHTMLEditor::InstallEventListeners() 1.429 +{ 1.430 + NS_ENSURE_TRUE(mDocWeak && mEventListener, 1.431 + NS_ERROR_NOT_INITIALIZED); 1.432 + 1.433 + // NOTE: nsHTMLEditor doesn't need to initialize mEventTarget here because 1.434 + // the target must be document node and it must be referenced as weak pointer. 1.435 + 1.436 + nsHTMLEditorEventListener* listener = 1.437 + reinterpret_cast<nsHTMLEditorEventListener*>(mEventListener.get()); 1.438 + return listener->Connect(this); 1.439 +} 1.440 + 1.441 +void 1.442 +nsHTMLEditor::RemoveEventListeners() 1.443 +{ 1.444 + if (!mDocWeak) 1.445 + { 1.446 + return; 1.447 + } 1.448 + 1.449 + nsCOMPtr<nsIDOMEventTarget> target = GetDOMEventTarget(); 1.450 + 1.451 + if (target) 1.452 + { 1.453 + // Both mMouseMotionListenerP and mResizeEventListenerP can be 1.454 + // registerd with other targets than the DOM event receiver that 1.455 + // we can reach from here. But nonetheless, unregister the event 1.456 + // listeners with the DOM event reveiver (if it's registerd with 1.457 + // other targets, it'll get unregisterd once the target goes 1.458 + // away). 1.459 + 1.460 + if (mMouseMotionListenerP) 1.461 + { 1.462 + // mMouseMotionListenerP might be registerd either as bubbling or 1.463 + // capturing, unregister by both. 1.464 + target->RemoveEventListener(NS_LITERAL_STRING("mousemove"), 1.465 + mMouseMotionListenerP, false); 1.466 + target->RemoveEventListener(NS_LITERAL_STRING("mousemove"), 1.467 + mMouseMotionListenerP, true); 1.468 + } 1.469 + 1.470 + if (mResizeEventListenerP) 1.471 + { 1.472 + target->RemoveEventListener(NS_LITERAL_STRING("resize"), 1.473 + mResizeEventListenerP, false); 1.474 + } 1.475 + } 1.476 + 1.477 + mMouseMotionListenerP = nullptr; 1.478 + mResizeEventListenerP = nullptr; 1.479 + 1.480 + nsPlaintextEditor::RemoveEventListeners(); 1.481 +} 1.482 + 1.483 +NS_IMETHODIMP 1.484 +nsHTMLEditor::SetFlags(uint32_t aFlags) 1.485 +{ 1.486 + nsresult rv = nsPlaintextEditor::SetFlags(aFlags); 1.487 + NS_ENSURE_SUCCESS(rv, rv); 1.488 + 1.489 + // Sets mCSSAware to correspond to aFlags. This toggles whether CSS is 1.490 + // used to style elements in the editor. Note that the editor is only CSS 1.491 + // aware by default in Composer and in the mail editor. 1.492 + mCSSAware = !NoCSS() && !IsMailEditor(); 1.493 + 1.494 + return NS_OK; 1.495 +} 1.496 + 1.497 +NS_IMETHODIMP 1.498 +nsHTMLEditor::InitRules() 1.499 +{ 1.500 + if (!mRules) { 1.501 + // instantiate the rules for the html editor 1.502 + mRules = new nsHTMLEditRules(); 1.503 + } 1.504 + return mRules->Init(static_cast<nsPlaintextEditor*>(this)); 1.505 +} 1.506 + 1.507 +NS_IMETHODIMP 1.508 +nsHTMLEditor::BeginningOfDocument() 1.509 +{ 1.510 + if (!mDocWeak) { return NS_ERROR_NOT_INITIALIZED; } 1.511 + 1.512 + // get the selection 1.513 + nsCOMPtr<nsISelection> selection; 1.514 + nsresult res = GetSelection(getter_AddRefs(selection)); 1.515 + NS_ENSURE_SUCCESS(res, res); 1.516 + NS_ENSURE_TRUE(selection, NS_ERROR_NOT_INITIALIZED); 1.517 + 1.518 + // Get the root element. 1.519 + nsCOMPtr<nsIDOMElement> rootElement = do_QueryInterface(GetRoot()); 1.520 + if (!rootElement) { 1.521 + NS_WARNING("GetRoot() returned a null pointer (mRootElement is null)"); 1.522 + return NS_OK; 1.523 + } 1.524 + 1.525 + // find first editable thingy 1.526 + bool done = false; 1.527 + nsCOMPtr<nsIDOMNode> curNode(rootElement), selNode; 1.528 + int32_t curOffset = 0, selOffset; 1.529 + while (!done) 1.530 + { 1.531 + nsWSRunObject wsObj(this, curNode, curOffset); 1.532 + nsCOMPtr<nsIDOMNode> visNode; 1.533 + int32_t visOffset=0; 1.534 + WSType visType; 1.535 + wsObj.NextVisibleNode(curNode, curOffset, address_of(visNode), &visOffset, &visType); 1.536 + if (visType == WSType::normalWS || visType == WSType::text) { 1.537 + selNode = visNode; 1.538 + selOffset = visOffset; 1.539 + done = true; 1.540 + } else if (visType == WSType::br || visType == WSType::special) { 1.541 + selNode = GetNodeLocation(visNode, &selOffset); 1.542 + done = true; 1.543 + } else if (visType == WSType::otherBlock) { 1.544 + // By definition of nsWSRunObject, a block element terminates 1.545 + // a whitespace run. That is, although we are calling a method 1.546 + // that is named "NextVisibleNode", the node returned 1.547 + // might not be visible/editable! 1.548 + // If the given block does not contain any visible/editable items, 1.549 + // we want to skip it and continue our search. 1.550 + 1.551 + if (!IsContainer(visNode)) 1.552 + { 1.553 + // However, we were given a block that is not a container. 1.554 + // Since the block can not contain anything that's visible, 1.555 + // such a block only makes sense if it is visible by itself, 1.556 + // like a <hr> 1.557 + // We want to place the caret in front of that block. 1.558 + 1.559 + selNode = GetNodeLocation(visNode, &selOffset); 1.560 + done = true; 1.561 + } 1.562 + else 1.563 + { 1.564 + bool isEmptyBlock; 1.565 + if (NS_SUCCEEDED(IsEmptyNode(visNode, &isEmptyBlock)) && 1.566 + isEmptyBlock) 1.567 + { 1.568 + // skip the empty block 1.569 + curNode = GetNodeLocation(visNode, &curOffset); 1.570 + ++curOffset; 1.571 + } 1.572 + else 1.573 + { 1.574 + curNode = visNode; 1.575 + curOffset = 0; 1.576 + } 1.577 + // keep looping 1.578 + } 1.579 + } 1.580 + else 1.581 + { 1.582 + // else we found nothing useful 1.583 + selNode = curNode; 1.584 + selOffset = curOffset; 1.585 + done = true; 1.586 + } 1.587 + } 1.588 + return selection->Collapse(selNode, selOffset); 1.589 +} 1.590 + 1.591 +nsresult 1.592 +nsHTMLEditor::HandleKeyPressEvent(nsIDOMKeyEvent* aKeyEvent) 1.593 +{ 1.594 + // NOTE: When you change this method, you should also change: 1.595 + // * editor/libeditor/html/tests/test_htmleditor_keyevent_handling.html 1.596 + 1.597 + if (IsReadonly() || IsDisabled()) { 1.598 + // When we're not editable, the events are handled on nsEditor, so, we can 1.599 + // bypass nsPlaintextEditor. 1.600 + return nsEditor::HandleKeyPressEvent(aKeyEvent); 1.601 + } 1.602 + 1.603 + WidgetKeyboardEvent* nativeKeyEvent = 1.604 + aKeyEvent->GetInternalNSEvent()->AsKeyboardEvent(); 1.605 + NS_ENSURE_TRUE(nativeKeyEvent, NS_ERROR_UNEXPECTED); 1.606 + NS_ASSERTION(nativeKeyEvent->message == NS_KEY_PRESS, 1.607 + "HandleKeyPressEvent gets non-keypress event"); 1.608 + 1.609 + switch (nativeKeyEvent->keyCode) { 1.610 + case nsIDOMKeyEvent::DOM_VK_META: 1.611 + case nsIDOMKeyEvent::DOM_VK_WIN: 1.612 + case nsIDOMKeyEvent::DOM_VK_SHIFT: 1.613 + case nsIDOMKeyEvent::DOM_VK_CONTROL: 1.614 + case nsIDOMKeyEvent::DOM_VK_ALT: 1.615 + case nsIDOMKeyEvent::DOM_VK_BACK_SPACE: 1.616 + case nsIDOMKeyEvent::DOM_VK_DELETE: 1.617 + // These keys are handled on nsEditor, so, we can bypass 1.618 + // nsPlaintextEditor. 1.619 + return nsEditor::HandleKeyPressEvent(aKeyEvent); 1.620 + case nsIDOMKeyEvent::DOM_VK_TAB: { 1.621 + if (IsPlaintextEditor()) { 1.622 + // If this works as plain text editor, e.g., mail editor for plain 1.623 + // text, should be handled on nsPlaintextEditor. 1.624 + return nsPlaintextEditor::HandleKeyPressEvent(aKeyEvent); 1.625 + } 1.626 + 1.627 + if (IsTabbable()) { 1.628 + return NS_OK; // let it be used for focus switching 1.629 + } 1.630 + 1.631 + if (nativeKeyEvent->IsControl() || nativeKeyEvent->IsAlt() || 1.632 + nativeKeyEvent->IsMeta() || nativeKeyEvent->IsOS()) { 1.633 + return NS_OK; 1.634 + } 1.635 + 1.636 + nsCOMPtr<nsISelection> selection; 1.637 + nsresult rv = GetSelection(getter_AddRefs(selection)); 1.638 + NS_ENSURE_SUCCESS(rv, rv); 1.639 + int32_t offset; 1.640 + nsCOMPtr<nsIDOMNode> node, blockParent; 1.641 + rv = GetStartNodeAndOffset(selection, getter_AddRefs(node), &offset); 1.642 + NS_ENSURE_SUCCESS(rv, rv); 1.643 + NS_ENSURE_TRUE(node, NS_ERROR_FAILURE); 1.644 + 1.645 + bool isBlock = false; 1.646 + NodeIsBlock(node, &isBlock); 1.647 + if (isBlock) { 1.648 + blockParent = node; 1.649 + } else { 1.650 + blockParent = GetBlockNodeParent(node); 1.651 + } 1.652 + 1.653 + if (!blockParent) { 1.654 + break; 1.655 + } 1.656 + 1.657 + bool handled = false; 1.658 + if (nsHTMLEditUtils::IsTableElement(blockParent)) { 1.659 + rv = TabInTable(nativeKeyEvent->IsShift(), &handled); 1.660 + if (handled) { 1.661 + ScrollSelectionIntoView(false); 1.662 + } 1.663 + } else if (nsHTMLEditUtils::IsListItem(blockParent)) { 1.664 + rv = Indent(nativeKeyEvent->IsShift() ? 1.665 + NS_LITERAL_STRING("outdent") : 1.666 + NS_LITERAL_STRING("indent")); 1.667 + handled = true; 1.668 + } 1.669 + NS_ENSURE_SUCCESS(rv, rv); 1.670 + if (handled) { 1.671 + return aKeyEvent->PreventDefault(); // consumed 1.672 + } 1.673 + if (nativeKeyEvent->IsShift()) { 1.674 + return NS_OK; // don't type text for shift tabs 1.675 + } 1.676 + aKeyEvent->PreventDefault(); 1.677 + return TypedText(NS_LITERAL_STRING("\t"), eTypedText); 1.678 + } 1.679 + case nsIDOMKeyEvent::DOM_VK_RETURN: 1.680 + if (nativeKeyEvent->IsControl() || nativeKeyEvent->IsAlt() || 1.681 + nativeKeyEvent->IsMeta() || nativeKeyEvent->IsOS()) { 1.682 + return NS_OK; 1.683 + } 1.684 + aKeyEvent->PreventDefault(); // consumed 1.685 + if (nativeKeyEvent->IsShift() && !IsPlaintextEditor()) { 1.686 + // only inserts a br node 1.687 + return TypedText(EmptyString(), eTypedBR); 1.688 + } 1.689 + // uses rules to figure out what to insert 1.690 + return TypedText(EmptyString(), eTypedBreak); 1.691 + } 1.692 + 1.693 + // NOTE: On some keyboard layout, some characters are inputted with Control 1.694 + // key or Alt key, but at that time, widget sets FALSE to these keys. 1.695 + if (nativeKeyEvent->charCode == 0 || nativeKeyEvent->IsControl() || 1.696 + nativeKeyEvent->IsAlt() || nativeKeyEvent->IsMeta() || 1.697 + nativeKeyEvent->IsOS()) { 1.698 + // we don't PreventDefault() here or keybindings like control-x won't work 1.699 + return NS_OK; 1.700 + } 1.701 + aKeyEvent->PreventDefault(); 1.702 + nsAutoString str(nativeKeyEvent->charCode); 1.703 + return TypedText(str, eTypedText); 1.704 +} 1.705 + 1.706 +static void 1.707 +AssertParserServiceIsCorrect(nsIAtom* aTag, bool aIsBlock) 1.708 +{ 1.709 +#ifdef DEBUG 1.710 + // Check this against what we would have said with the old code: 1.711 + if (aTag==nsEditProperty::p || 1.712 + aTag==nsEditProperty::div || 1.713 + aTag==nsEditProperty::blockquote || 1.714 + aTag==nsEditProperty::h1 || 1.715 + aTag==nsEditProperty::h2 || 1.716 + aTag==nsEditProperty::h3 || 1.717 + aTag==nsEditProperty::h4 || 1.718 + aTag==nsEditProperty::h5 || 1.719 + aTag==nsEditProperty::h6 || 1.720 + aTag==nsEditProperty::ul || 1.721 + aTag==nsEditProperty::ol || 1.722 + aTag==nsEditProperty::dl || 1.723 + aTag==nsEditProperty::noscript || 1.724 + aTag==nsEditProperty::form || 1.725 + aTag==nsEditProperty::hr || 1.726 + aTag==nsEditProperty::table || 1.727 + aTag==nsEditProperty::fieldset || 1.728 + aTag==nsEditProperty::address || 1.729 + aTag==nsEditProperty::col || 1.730 + aTag==nsEditProperty::colgroup || 1.731 + aTag==nsEditProperty::li || 1.732 + aTag==nsEditProperty::dt || 1.733 + aTag==nsEditProperty::dd || 1.734 + aTag==nsEditProperty::legend ) 1.735 + { 1.736 + if (!aIsBlock) { 1.737 + nsAutoString assertmsg (NS_LITERAL_STRING("Parser and editor disagree on blockness: ")); 1.738 + 1.739 + nsAutoString tagName; 1.740 + aTag->ToString(tagName); 1.741 + assertmsg.Append(tagName); 1.742 + char* assertstr = ToNewCString(assertmsg); 1.743 + NS_ASSERTION(aIsBlock, assertstr); 1.744 + NS_Free(assertstr); 1.745 + } 1.746 + } 1.747 +#endif // DEBUG 1.748 +} 1.749 + 1.750 +/** 1.751 + * Returns true if the id represents an element of block type. 1.752 + * Can be used to determine if a new paragraph should be started. 1.753 + */ 1.754 +bool 1.755 +nsHTMLEditor::NodeIsBlockStatic(const dom::Element* aElement) 1.756 +{ 1.757 + MOZ_ASSERT(aElement); 1.758 + 1.759 + nsIAtom* tagAtom = aElement->Tag(); 1.760 + MOZ_ASSERT(tagAtom); 1.761 + 1.762 + // Nodes we know we want to treat as block 1.763 + // even though the parser says they're not: 1.764 + if (tagAtom==nsEditProperty::body || 1.765 + tagAtom==nsEditProperty::head || 1.766 + tagAtom==nsEditProperty::tbody || 1.767 + tagAtom==nsEditProperty::thead || 1.768 + tagAtom==nsEditProperty::tfoot || 1.769 + tagAtom==nsEditProperty::tr || 1.770 + tagAtom==nsEditProperty::th || 1.771 + tagAtom==nsEditProperty::td || 1.772 + tagAtom==nsEditProperty::li || 1.773 + tagAtom==nsEditProperty::dt || 1.774 + tagAtom==nsEditProperty::dd || 1.775 + tagAtom==nsEditProperty::pre) 1.776 + { 1.777 + return true; 1.778 + } 1.779 + 1.780 + bool isBlock; 1.781 +#ifdef DEBUG 1.782 + // XXX we can't use DebugOnly here because VC++ is stupid (bug 802884) 1.783 + nsresult rv = 1.784 +#endif 1.785 + nsContentUtils::GetParserService()-> 1.786 + IsBlock(nsContentUtils::GetParserService()->HTMLAtomTagToId(tagAtom), 1.787 + isBlock); 1.788 + MOZ_ASSERT(rv == NS_OK); 1.789 + 1.790 + AssertParserServiceIsCorrect(tagAtom, isBlock); 1.791 + 1.792 + return isBlock; 1.793 +} 1.794 + 1.795 +nsresult 1.796 +nsHTMLEditor::NodeIsBlockStatic(nsIDOMNode *aNode, bool *aIsBlock) 1.797 +{ 1.798 + if (!aNode || !aIsBlock) { return NS_ERROR_NULL_POINTER; } 1.799 + 1.800 + nsCOMPtr<dom::Element> element = do_QueryInterface(aNode); 1.801 + *aIsBlock = element && NodeIsBlockStatic(element); 1.802 + return NS_OK; 1.803 +} 1.804 + 1.805 +NS_IMETHODIMP 1.806 +nsHTMLEditor::NodeIsBlock(nsIDOMNode *aNode, bool *aIsBlock) 1.807 +{ 1.808 + return NodeIsBlockStatic(aNode, aIsBlock); 1.809 +} 1.810 + 1.811 +bool 1.812 +nsHTMLEditor::IsBlockNode(nsINode *aNode) 1.813 +{ 1.814 + return aNode && aNode->IsElement() && NodeIsBlockStatic(aNode->AsElement()); 1.815 +} 1.816 + 1.817 +// Non-static version for the nsIEditor interface and JavaScript 1.818 +NS_IMETHODIMP 1.819 +nsHTMLEditor::SetDocumentTitle(const nsAString &aTitle) 1.820 +{ 1.821 + nsRefPtr<SetDocTitleTxn> txn = new SetDocTitleTxn(); 1.822 + NS_ENSURE_TRUE(txn, NS_ERROR_OUT_OF_MEMORY); 1.823 + 1.824 + nsresult result = txn->Init(this, &aTitle); 1.825 + NS_ENSURE_SUCCESS(result, result); 1.826 + 1.827 + //Don't let Rules System change the selection 1.828 + nsAutoTxnsConserveSelection dontChangeSelection(this); 1.829 + return nsEditor::DoTransaction(txn); 1.830 +} 1.831 + 1.832 +/* ------------ Block methods moved from nsEditor -------------- */ 1.833 +/////////////////////////////////////////////////////////////////////////// 1.834 +// GetBlockNodeParent: returns enclosing block level ancestor, if any 1.835 +// 1.836 +already_AddRefed<nsIDOMNode> 1.837 +nsHTMLEditor::GetBlockNodeParent(nsIDOMNode *aNode) 1.838 +{ 1.839 + if (!aNode) 1.840 + { 1.841 + NS_NOTREACHED("null node passed to GetBlockNodeParent()"); 1.842 + return nullptr; 1.843 + } 1.844 + 1.845 + nsCOMPtr<nsIDOMNode> p; 1.846 + if (NS_FAILED(aNode->GetParentNode(getter_AddRefs(p)))) // no parent, ran off top of tree 1.847 + return nullptr; 1.848 + 1.849 + nsCOMPtr<nsIDOMNode> tmp; 1.850 + while (p) 1.851 + { 1.852 + bool isBlock; 1.853 + if (NS_FAILED(NodeIsBlockStatic(p, &isBlock)) || isBlock) 1.854 + break; 1.855 + if (NS_FAILED(p->GetParentNode(getter_AddRefs(tmp))) || !tmp) // no parent, ran off top of tree 1.856 + break; 1.857 + 1.858 + p = tmp; 1.859 + } 1.860 + return p.forget(); 1.861 +} 1.862 + 1.863 +static const char16_t nbsp = 160; 1.864 + 1.865 +/////////////////////////////////////////////////////////////////////////////// 1.866 +// IsNextCharInNodeWhitespace: checks the adjacent content in the same node to 1.867 +// see if following selection is whitespace or nbsp 1.868 +void 1.869 +nsHTMLEditor::IsNextCharInNodeWhitespace(nsIContent* aContent, 1.870 + int32_t aOffset, 1.871 + bool* outIsSpace, 1.872 + bool* outIsNBSP, 1.873 + nsIContent** outNode, 1.874 + int32_t* outOffset) 1.875 +{ 1.876 + MOZ_ASSERT(aContent && outIsSpace && outIsNBSP); 1.877 + MOZ_ASSERT((outNode && outOffset) || (!outNode && !outOffset)); 1.878 + *outIsSpace = false; 1.879 + *outIsNBSP = false; 1.880 + if (outNode && outOffset) { 1.881 + *outNode = nullptr; 1.882 + *outOffset = -1; 1.883 + } 1.884 + 1.885 + if (aContent->IsNodeOfType(nsINode::eTEXT) && 1.886 + (uint32_t)aOffset < aContent->Length()) { 1.887 + char16_t ch = aContent->GetText()->CharAt(aOffset); 1.888 + *outIsSpace = nsCRT::IsAsciiSpace(ch); 1.889 + *outIsNBSP = (ch == nbsp); 1.890 + if (outNode && outOffset) { 1.891 + NS_IF_ADDREF(*outNode = aContent); 1.892 + // yes, this is _past_ the character 1.893 + *outOffset = aOffset + 1; 1.894 + } 1.895 + } 1.896 +} 1.897 + 1.898 + 1.899 +/////////////////////////////////////////////////////////////////////////////// 1.900 +// IsPrevCharInNodeWhitespace: checks the adjacent content in the same node to 1.901 +// see if following selection is whitespace 1.902 +void 1.903 +nsHTMLEditor::IsPrevCharInNodeWhitespace(nsIContent* aContent, 1.904 + int32_t aOffset, 1.905 + bool* outIsSpace, 1.906 + bool* outIsNBSP, 1.907 + nsIContent** outNode, 1.908 + int32_t* outOffset) 1.909 +{ 1.910 + MOZ_ASSERT(aContent && outIsSpace && outIsNBSP); 1.911 + MOZ_ASSERT((outNode && outOffset) || (!outNode && !outOffset)); 1.912 + *outIsSpace = false; 1.913 + *outIsNBSP = false; 1.914 + if (outNode && outOffset) { 1.915 + *outNode = nullptr; 1.916 + *outOffset = -1; 1.917 + } 1.918 + 1.919 + if (aContent->IsNodeOfType(nsINode::eTEXT) && aOffset > 0) { 1.920 + char16_t ch = aContent->GetText()->CharAt(aOffset - 1); 1.921 + *outIsSpace = nsCRT::IsAsciiSpace(ch); 1.922 + *outIsNBSP = (ch == nbsp); 1.923 + if (outNode && outOffset) { 1.924 + NS_IF_ADDREF(*outNode = aContent); 1.925 + *outOffset = aOffset - 1; 1.926 + } 1.927 + } 1.928 +} 1.929 + 1.930 + 1.931 + 1.932 +/* ------------ End Block methods -------------- */ 1.933 + 1.934 + 1.935 +bool nsHTMLEditor::IsVisBreak(nsIDOMNode *aNode) 1.936 +{ 1.937 + NS_ENSURE_TRUE(aNode, false); 1.938 + if (!nsTextEditUtils::IsBreak(aNode)) 1.939 + return false; 1.940 + // check if there is a later node in block after br 1.941 + nsCOMPtr<nsIDOMNode> priorNode, nextNode; 1.942 + GetPriorHTMLNode(aNode, address_of(priorNode), true); 1.943 + GetNextHTMLNode(aNode, address_of(nextNode), true); 1.944 + // if we are next to another break, we are visible 1.945 + if (priorNode && nsTextEditUtils::IsBreak(priorNode)) 1.946 + return true; 1.947 + if (nextNode && nsTextEditUtils::IsBreak(nextNode)) 1.948 + return true; 1.949 + 1.950 + // if we are right before block boundary, then br not visible 1.951 + NS_ENSURE_TRUE(nextNode, false); // this break is trailer in block, it's not visible 1.952 + if (IsBlockNode(nextNode)) 1.953 + return false; // break is right before a block, it's not visible 1.954 + 1.955 + // sigh. We have to use expensive whitespace calculation code to 1.956 + // determine what is going on 1.957 + nsCOMPtr<nsIDOMNode> selNode, tmp; 1.958 + int32_t selOffset; 1.959 + selNode = GetNodeLocation(aNode, &selOffset); 1.960 + selOffset++; // lets look after the break 1.961 + nsWSRunObject wsObj(this, selNode, selOffset); 1.962 + nsCOMPtr<nsIDOMNode> visNode; 1.963 + int32_t visOffset=0; 1.964 + WSType visType; 1.965 + wsObj.NextVisibleNode(selNode, selOffset, address_of(visNode), &visOffset, &visType); 1.966 + if (visType & WSType::block) { 1.967 + return false; 1.968 + } 1.969 + 1.970 + return true; 1.971 +} 1.972 + 1.973 +NS_IMETHODIMP 1.974 +nsHTMLEditor::BreakIsVisible(nsIDOMNode *aNode, bool *aIsVisible) 1.975 +{ 1.976 + NS_ENSURE_ARG_POINTER(aNode && aIsVisible); 1.977 + 1.978 + *aIsVisible = IsVisBreak(aNode); 1.979 + 1.980 + return NS_OK; 1.981 +} 1.982 + 1.983 + 1.984 +NS_IMETHODIMP 1.985 +nsHTMLEditor::GetIsDocumentEditable(bool *aIsDocumentEditable) 1.986 +{ 1.987 + NS_ENSURE_ARG_POINTER(aIsDocumentEditable); 1.988 + 1.989 + nsCOMPtr<nsIDOMDocument> doc = GetDOMDocument(); 1.990 + *aIsDocumentEditable = doc && IsModifiable(); 1.991 + 1.992 + return NS_OK; 1.993 +} 1.994 + 1.995 +bool nsHTMLEditor::IsModifiable() 1.996 +{ 1.997 + return !IsReadonly(); 1.998 +} 1.999 + 1.1000 +NS_IMETHODIMP 1.1001 +nsHTMLEditor::UpdateBaseURL() 1.1002 +{ 1.1003 + nsCOMPtr<nsIDOMDocument> domDoc = GetDOMDocument(); 1.1004 + NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE); 1.1005 + 1.1006 + // Look for an HTML <base> tag 1.1007 + nsCOMPtr<nsIDOMNodeList> nodeList; 1.1008 + nsresult rv = domDoc->GetElementsByTagName(NS_LITERAL_STRING("base"), getter_AddRefs(nodeList)); 1.1009 + NS_ENSURE_SUCCESS(rv, rv); 1.1010 + 1.1011 + nsCOMPtr<nsIDOMNode> baseNode; 1.1012 + if (nodeList) 1.1013 + { 1.1014 + uint32_t count; 1.1015 + nodeList->GetLength(&count); 1.1016 + if (count >= 1) 1.1017 + { 1.1018 + rv = nodeList->Item(0, getter_AddRefs(baseNode)); 1.1019 + NS_ENSURE_SUCCESS(rv, rv); 1.1020 + } 1.1021 + } 1.1022 + // If no base tag, then set baseURL to the document's URL 1.1023 + // This is very important, else relative URLs for links and images are wrong 1.1024 + if (!baseNode) 1.1025 + { 1.1026 + nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc); 1.1027 + NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); 1.1028 + 1.1029 + return doc->SetBaseURI(doc->GetDocumentURI()); 1.1030 + } 1.1031 + return NS_OK; 1.1032 +} 1.1033 + 1.1034 +/* This routine is needed to provide a bottleneck for typing for logging 1.1035 + purposes. Can't use HandleKeyPress() (above) for that since it takes 1.1036 + a nsIDOMKeyEvent* parameter. So instead we pass enough info through 1.1037 + to TypedText() to determine what action to take, but without passing 1.1038 + an event. 1.1039 + */ 1.1040 +NS_IMETHODIMP 1.1041 +nsHTMLEditor::TypedText(const nsAString& aString, ETypingAction aAction) 1.1042 +{ 1.1043 + nsAutoPlaceHolderBatch batch(this, nsGkAtoms::TypingTxnName); 1.1044 + 1.1045 + if (aAction == eTypedBR) { 1.1046 + // only inserts a br node 1.1047 + nsCOMPtr<nsIDOMNode> brNode; 1.1048 + return InsertBR(address_of(brNode)); 1.1049 + } 1.1050 + 1.1051 + return nsPlaintextEditor::TypedText(aString, aAction); 1.1052 +} 1.1053 + 1.1054 +NS_IMETHODIMP nsHTMLEditor::TabInTable(bool inIsShift, bool *outHandled) 1.1055 +{ 1.1056 + NS_ENSURE_TRUE(outHandled, NS_ERROR_NULL_POINTER); 1.1057 + *outHandled = false; 1.1058 + 1.1059 + // Find enclosing table cell from the selection (cell may be the selected element) 1.1060 + nsCOMPtr<nsIDOMElement> cellElement; 1.1061 + // can't use |NS_LITERAL_STRING| here until |GetElementOrParentByTagName| is fixed to accept readables 1.1062 + nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nullptr, getter_AddRefs(cellElement)); 1.1063 + NS_ENSURE_SUCCESS(res, res); 1.1064 + // Do nothing -- we didn't find a table cell 1.1065 + NS_ENSURE_TRUE(cellElement, NS_OK); 1.1066 + 1.1067 + // find enclosing table 1.1068 + nsCOMPtr<nsIDOMNode> tbl = GetEnclosingTable(cellElement); 1.1069 + NS_ENSURE_TRUE(tbl, res); 1.1070 + 1.1071 + // advance to next cell 1.1072 + // first create an iterator over the table 1.1073 + nsCOMPtr<nsIContentIterator> iter = 1.1074 + do_CreateInstance("@mozilla.org/content/post-content-iterator;1", &res); 1.1075 + NS_ENSURE_SUCCESS(res, res); 1.1076 + NS_ENSURE_TRUE(iter, NS_ERROR_NULL_POINTER); 1.1077 + nsCOMPtr<nsIContent> cTbl = do_QueryInterface(tbl); 1.1078 + nsCOMPtr<nsIContent> cBlock = do_QueryInterface(cellElement); 1.1079 + res = iter->Init(cTbl); 1.1080 + NS_ENSURE_SUCCESS(res, res); 1.1081 + // position iter at block 1.1082 + res = iter->PositionAt(cBlock); 1.1083 + NS_ENSURE_SUCCESS(res, res); 1.1084 + 1.1085 + nsCOMPtr<nsIDOMNode> node; 1.1086 + do 1.1087 + { 1.1088 + if (inIsShift) 1.1089 + iter->Prev(); 1.1090 + else 1.1091 + iter->Next(); 1.1092 + 1.1093 + node = do_QueryInterface(iter->GetCurrentNode()); 1.1094 + 1.1095 + if (node && nsHTMLEditUtils::IsTableCell(node) && 1.1096 + GetEnclosingTable(node) == tbl) 1.1097 + { 1.1098 + res = CollapseSelectionToDeepestNonTableFirstChild(nullptr, node); 1.1099 + NS_ENSURE_SUCCESS(res, res); 1.1100 + *outHandled = true; 1.1101 + return NS_OK; 1.1102 + } 1.1103 + } while (!iter->IsDone()); 1.1104 + 1.1105 + if (!(*outHandled) && !inIsShift) 1.1106 + { 1.1107 + // if we havent handled it yet then we must have run off the end of 1.1108 + // the table. Insert a new row. 1.1109 + res = InsertTableRow(1, true); 1.1110 + NS_ENSURE_SUCCESS(res, res); 1.1111 + *outHandled = true; 1.1112 + // put selection in right place 1.1113 + // Use table code to get selection and index to new row... 1.1114 + nsCOMPtr<nsISelection>selection; 1.1115 + nsCOMPtr<nsIDOMElement> tblElement; 1.1116 + nsCOMPtr<nsIDOMElement> cell; 1.1117 + int32_t row; 1.1118 + res = GetCellContext(getter_AddRefs(selection), 1.1119 + getter_AddRefs(tblElement), 1.1120 + getter_AddRefs(cell), 1.1121 + nullptr, nullptr, 1.1122 + &row, nullptr); 1.1123 + NS_ENSURE_SUCCESS(res, res); 1.1124 + // ...so that we can ask for first cell in that row... 1.1125 + res = GetCellAt(tblElement, row, 0, getter_AddRefs(cell)); 1.1126 + NS_ENSURE_SUCCESS(res, res); 1.1127 + // ...and then set selection there. 1.1128 + // (Note that normally you should use CollapseSelectionToDeepestNonTableFirstChild(), 1.1129 + // but we know cell is an empty new cell, so this works fine) 1.1130 + node = do_QueryInterface(cell); 1.1131 + if (node) selection->Collapse(node,0); 1.1132 + return NS_OK; 1.1133 + } 1.1134 + 1.1135 + return res; 1.1136 +} 1.1137 + 1.1138 +NS_IMETHODIMP nsHTMLEditor::CreateBR(nsIDOMNode *aNode, int32_t aOffset, nsCOMPtr<nsIDOMNode> *outBRNode, EDirection aSelect) 1.1139 +{ 1.1140 + nsCOMPtr<nsIDOMNode> parent = aNode; 1.1141 + int32_t offset = aOffset; 1.1142 + return CreateBRImpl(address_of(parent), &offset, outBRNode, aSelect); 1.1143 +} 1.1144 + 1.1145 +nsresult 1.1146 +nsHTMLEditor::CollapseSelectionToDeepestNonTableFirstChild(nsISelection *aSelection, nsIDOMNode *aNode) 1.1147 +{ 1.1148 + NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER); 1.1149 + nsresult res; 1.1150 + 1.1151 + nsCOMPtr<nsISelection> selection; 1.1152 + if (aSelection) 1.1153 + { 1.1154 + selection = aSelection; 1.1155 + } else { 1.1156 + res = GetSelection(getter_AddRefs(selection)); 1.1157 + NS_ENSURE_SUCCESS(res, res); 1.1158 + NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); 1.1159 + } 1.1160 + nsCOMPtr<nsIDOMNode> node = aNode; 1.1161 + nsCOMPtr<nsIDOMNode> child; 1.1162 + 1.1163 + do { 1.1164 + node->GetFirstChild(getter_AddRefs(child)); 1.1165 + 1.1166 + if (child) 1.1167 + { 1.1168 + // Stop if we find a table 1.1169 + // don't want to go into nested tables 1.1170 + if (nsHTMLEditUtils::IsTable(child)) break; 1.1171 + // hey, it'g gotta be a container too! 1.1172 + if (!IsContainer(child)) break; 1.1173 + node = child; 1.1174 + } 1.1175 + } 1.1176 + while (child); 1.1177 + 1.1178 + selection->Collapse(node,0); 1.1179 + return NS_OK; 1.1180 +} 1.1181 + 1.1182 + 1.1183 +// This is mostly like InsertHTMLWithCharsetAndContext, 1.1184 +// but we can't use that because it is selection-based and 1.1185 +// the rules code won't let us edit under the <head> node 1.1186 +NS_IMETHODIMP 1.1187 +nsHTMLEditor::ReplaceHeadContentsWithHTML(const nsAString& aSourceToInsert) 1.1188 +{ 1.1189 + nsAutoRules beginRulesSniffing(this, EditAction::ignore, nsIEditor::eNone); // don't do any post processing, rules get confused 1.1190 + nsCOMPtr<nsISelection> selection; 1.1191 + nsresult res = GetSelection(getter_AddRefs(selection)); 1.1192 + NS_ENSURE_SUCCESS(res, res); 1.1193 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.1194 + 1.1195 + ForceCompositionEnd(); 1.1196 + 1.1197 + // Do not use nsAutoRules -- rules code won't let us insert in <head> 1.1198 + // Use the head node as a parent and delete/insert directly 1.1199 + nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(mDocWeak); 1.1200 + NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED); 1.1201 + 1.1202 + nsCOMPtr<nsIDOMNodeList>nodeList; 1.1203 + res = doc->GetElementsByTagName(NS_LITERAL_STRING("head"), getter_AddRefs(nodeList)); 1.1204 + NS_ENSURE_SUCCESS(res, res); 1.1205 + NS_ENSURE_TRUE(nodeList, NS_ERROR_NULL_POINTER); 1.1206 + 1.1207 + uint32_t count; 1.1208 + nodeList->GetLength(&count); 1.1209 + if (count < 1) return NS_ERROR_FAILURE; 1.1210 + 1.1211 + nsCOMPtr<nsIDOMNode> headNode; 1.1212 + res = nodeList->Item(0, getter_AddRefs(headNode)); 1.1213 + NS_ENSURE_SUCCESS(res, res); 1.1214 + NS_ENSURE_TRUE(headNode, NS_ERROR_NULL_POINTER); 1.1215 + 1.1216 + // First, make sure there are no return chars in the source. 1.1217 + // Bad things happen if you insert returns (instead of dom newlines, \n) 1.1218 + // into an editor document. 1.1219 + nsAutoString inputString (aSourceToInsert); // hope this does copy-on-write 1.1220 + 1.1221 + // Windows linebreaks: Map CRLF to LF: 1.1222 + inputString.ReplaceSubstring(MOZ_UTF16("\r\n"), 1.1223 + MOZ_UTF16("\n")); 1.1224 + 1.1225 + // Mac linebreaks: Map any remaining CR to LF: 1.1226 + inputString.ReplaceSubstring(MOZ_UTF16("\r"), 1.1227 + MOZ_UTF16("\n")); 1.1228 + 1.1229 + nsAutoEditBatch beginBatching(this); 1.1230 + 1.1231 + res = GetSelection(getter_AddRefs(selection)); 1.1232 + NS_ENSURE_SUCCESS(res, res); 1.1233 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.1234 + 1.1235 + // Get the first range in the selection, for context: 1.1236 + nsCOMPtr<nsIDOMRange> range; 1.1237 + res = selection->GetRangeAt(0, getter_AddRefs(range)); 1.1238 + NS_ENSURE_SUCCESS(res, res); 1.1239 + 1.1240 + nsCOMPtr<nsIDOMDocumentFragment> docfrag; 1.1241 + res = range->CreateContextualFragment(inputString, 1.1242 + getter_AddRefs(docfrag)); 1.1243 + 1.1244 + //XXXX BUG 50965: This is not returning the text between <title> ... </title> 1.1245 + // Special code is needed in JS to handle title anyway, so it really doesn't matter! 1.1246 + 1.1247 + if (NS_FAILED(res)) 1.1248 + { 1.1249 +#ifdef DEBUG 1.1250 + printf("Couldn't create contextual fragment: error was %X\n", 1.1251 + static_cast<uint32_t>(res)); 1.1252 +#endif 1.1253 + return res; 1.1254 + } 1.1255 + NS_ENSURE_TRUE(docfrag, NS_ERROR_NULL_POINTER); 1.1256 + 1.1257 + nsCOMPtr<nsIDOMNode> child; 1.1258 + 1.1259 + // First delete all children in head 1.1260 + do { 1.1261 + res = headNode->GetFirstChild(getter_AddRefs(child)); 1.1262 + NS_ENSURE_SUCCESS(res, res); 1.1263 + if (child) 1.1264 + { 1.1265 + res = DeleteNode(child); 1.1266 + NS_ENSURE_SUCCESS(res, res); 1.1267 + } 1.1268 + } while (child); 1.1269 + 1.1270 + // Now insert the new nodes 1.1271 + int32_t offsetOfNewNode = 0; 1.1272 + nsCOMPtr<nsIDOMNode> fragmentAsNode (do_QueryInterface(docfrag)); 1.1273 + 1.1274 + // Loop over the contents of the fragment and move into the document 1.1275 + do { 1.1276 + res = fragmentAsNode->GetFirstChild(getter_AddRefs(child)); 1.1277 + NS_ENSURE_SUCCESS(res, res); 1.1278 + if (child) 1.1279 + { 1.1280 + res = InsertNode(child, headNode, offsetOfNewNode++); 1.1281 + NS_ENSURE_SUCCESS(res, res); 1.1282 + } 1.1283 + } while (child); 1.1284 + 1.1285 + return res; 1.1286 +} 1.1287 + 1.1288 +NS_IMETHODIMP 1.1289 +nsHTMLEditor::RebuildDocumentFromSource(const nsAString& aSourceString) 1.1290 +{ 1.1291 + ForceCompositionEnd(); 1.1292 + 1.1293 + nsCOMPtr<nsISelection>selection; 1.1294 + nsresult res = GetSelection(getter_AddRefs(selection)); 1.1295 + NS_ENSURE_SUCCESS(res, res); 1.1296 + 1.1297 + nsCOMPtr<nsIDOMElement> bodyElement = do_QueryInterface(GetRoot()); 1.1298 + NS_ENSURE_TRUE(bodyElement, NS_ERROR_NULL_POINTER); 1.1299 + 1.1300 + // Find where the <body> tag starts. 1.1301 + nsReadingIterator<char16_t> beginbody; 1.1302 + nsReadingIterator<char16_t> endbody; 1.1303 + aSourceString.BeginReading(beginbody); 1.1304 + aSourceString.EndReading(endbody); 1.1305 + bool foundbody = CaseInsensitiveFindInReadable(NS_LITERAL_STRING("<body"), 1.1306 + beginbody, endbody); 1.1307 + 1.1308 + nsReadingIterator<char16_t> beginhead; 1.1309 + nsReadingIterator<char16_t> endhead; 1.1310 + aSourceString.BeginReading(beginhead); 1.1311 + aSourceString.EndReading(endhead); 1.1312 + bool foundhead = CaseInsensitiveFindInReadable(NS_LITERAL_STRING("<head"), 1.1313 + beginhead, endhead); 1.1314 + // a valid head appears before the body 1.1315 + if (foundbody && beginhead.get() > beginbody.get()) 1.1316 + foundhead = false; 1.1317 + 1.1318 + nsReadingIterator<char16_t> beginclosehead; 1.1319 + nsReadingIterator<char16_t> endclosehead; 1.1320 + aSourceString.BeginReading(beginclosehead); 1.1321 + aSourceString.EndReading(endclosehead); 1.1322 + 1.1323 + // Find the index after "<head>" 1.1324 + bool foundclosehead = CaseInsensitiveFindInReadable( 1.1325 + NS_LITERAL_STRING("</head>"), beginclosehead, endclosehead); 1.1326 + // a valid close head appears after a found head 1.1327 + if (foundhead && beginhead.get() > beginclosehead.get()) 1.1328 + foundclosehead = false; 1.1329 + // a valid close head appears before a found body 1.1330 + if (foundbody && beginclosehead.get() > beginbody.get()) 1.1331 + foundclosehead = false; 1.1332 + 1.1333 + // Time to change the document 1.1334 + nsAutoEditBatch beginBatching(this); 1.1335 + 1.1336 + nsReadingIterator<char16_t> endtotal; 1.1337 + aSourceString.EndReading(endtotal); 1.1338 + 1.1339 + if (foundhead) { 1.1340 + if (foundclosehead) 1.1341 + res = ReplaceHeadContentsWithHTML(Substring(beginhead, beginclosehead)); 1.1342 + else if (foundbody) 1.1343 + res = ReplaceHeadContentsWithHTML(Substring(beginhead, beginbody)); 1.1344 + else 1.1345 + // XXX Without recourse to some parser/content sink/docshell hackery 1.1346 + // we don't really know where the head ends and the body begins 1.1347 + // so we assume that there is no body 1.1348 + res = ReplaceHeadContentsWithHTML(Substring(beginhead, endtotal)); 1.1349 + } else { 1.1350 + nsReadingIterator<char16_t> begintotal; 1.1351 + aSourceString.BeginReading(begintotal); 1.1352 + NS_NAMED_LITERAL_STRING(head, "<head>"); 1.1353 + if (foundclosehead) 1.1354 + res = ReplaceHeadContentsWithHTML(head + Substring(begintotal, beginclosehead)); 1.1355 + else if (foundbody) 1.1356 + res = ReplaceHeadContentsWithHTML(head + Substring(begintotal, beginbody)); 1.1357 + else 1.1358 + // XXX Without recourse to some parser/content sink/docshell hackery 1.1359 + // we don't really know where the head ends and the body begins 1.1360 + // so we assume that there is no head 1.1361 + res = ReplaceHeadContentsWithHTML(head); 1.1362 + } 1.1363 + NS_ENSURE_SUCCESS(res, res); 1.1364 + 1.1365 + res = SelectAll(); 1.1366 + NS_ENSURE_SUCCESS(res, res); 1.1367 + 1.1368 + if (!foundbody) { 1.1369 + NS_NAMED_LITERAL_STRING(body, "<body>"); 1.1370 + // XXX Without recourse to some parser/content sink/docshell hackery 1.1371 + // we don't really know where the head ends and the body begins 1.1372 + if (foundclosehead) // assume body starts after the head ends 1.1373 + res = LoadHTML(body + Substring(endclosehead, endtotal)); 1.1374 + else if (foundhead) // assume there is no body 1.1375 + res = LoadHTML(body); 1.1376 + else // assume there is no head, the entire source is body 1.1377 + res = LoadHTML(body + aSourceString); 1.1378 + NS_ENSURE_SUCCESS(res, res); 1.1379 + 1.1380 + nsCOMPtr<nsIDOMElement> divElement; 1.1381 + res = CreateElementWithDefaults(NS_LITERAL_STRING("div"), getter_AddRefs(divElement)); 1.1382 + NS_ENSURE_SUCCESS(res, res); 1.1383 + 1.1384 + res = CloneAttributes(bodyElement, divElement); 1.1385 + NS_ENSURE_SUCCESS(res, res); 1.1386 + 1.1387 + return BeginningOfDocument(); 1.1388 + } 1.1389 + 1.1390 + res = LoadHTML(Substring(beginbody, endtotal)); 1.1391 + NS_ENSURE_SUCCESS(res, res); 1.1392 + 1.1393 + // Now we must copy attributes user might have edited on the <body> tag 1.1394 + // because InsertHTML (actually, CreateContextualFragment()) 1.1395 + // will never return a body node in the DOM fragment 1.1396 + 1.1397 + // We already know where "<body" begins 1.1398 + nsReadingIterator<char16_t> beginclosebody = beginbody; 1.1399 + nsReadingIterator<char16_t> endclosebody; 1.1400 + aSourceString.EndReading(endclosebody); 1.1401 + if (!FindInReadable(NS_LITERAL_STRING(">"),beginclosebody,endclosebody)) 1.1402 + return NS_ERROR_FAILURE; 1.1403 + 1.1404 + // Truncate at the end of the body tag 1.1405 + // Kludge of the year: fool the parser by replacing "body" with "div" so we get a node 1.1406 + nsAutoString bodyTag; 1.1407 + bodyTag.AssignLiteral("<div "); 1.1408 + bodyTag.Append(Substring(endbody, endclosebody)); 1.1409 + 1.1410 + nsCOMPtr<nsIDOMRange> range; 1.1411 + res = selection->GetRangeAt(0, getter_AddRefs(range)); 1.1412 + NS_ENSURE_SUCCESS(res, res); 1.1413 + 1.1414 + nsCOMPtr<nsIDOMDocumentFragment> docfrag; 1.1415 + res = range->CreateContextualFragment(bodyTag, getter_AddRefs(docfrag)); 1.1416 + NS_ENSURE_SUCCESS(res, res); 1.1417 + 1.1418 + nsCOMPtr<nsIDOMNode> fragmentAsNode (do_QueryInterface(docfrag)); 1.1419 + NS_ENSURE_TRUE(fragmentAsNode, NS_ERROR_NULL_POINTER); 1.1420 + 1.1421 + nsCOMPtr<nsIDOMNode> child; 1.1422 + res = fragmentAsNode->GetFirstChild(getter_AddRefs(child)); 1.1423 + NS_ENSURE_SUCCESS(res, res); 1.1424 + NS_ENSURE_TRUE(child, NS_ERROR_NULL_POINTER); 1.1425 + 1.1426 + // Copy all attributes from the div child to current body element 1.1427 + res = CloneAttributes(bodyElement, child); 1.1428 + NS_ENSURE_SUCCESS(res, res); 1.1429 + 1.1430 + // place selection at first editable content 1.1431 + return BeginningOfDocument(); 1.1432 +} 1.1433 + 1.1434 +void 1.1435 +nsHTMLEditor::NormalizeEOLInsertPosition(nsIDOMNode *firstNodeToInsert, 1.1436 + nsCOMPtr<nsIDOMNode> *insertParentNode, 1.1437 + int32_t *insertOffset) 1.1438 +{ 1.1439 + /* 1.1440 + This function will either correct the position passed in, 1.1441 + or leave the position unchanged. 1.1442 + 1.1443 + When the (first) item to insert is a block level element, 1.1444 + and our insertion position is after the last visible item in a line, 1.1445 + i.e. the insertion position is just before a visible line break <br>, 1.1446 + we want to skip to the position just after the line break (see bug 68767) 1.1447 + 1.1448 + However, our logic to detect whether we should skip or not 1.1449 + needs to be more clever. 1.1450 + We must not skip when the caret appears to be positioned at the beginning 1.1451 + of a block, in that case skipping the <br> would not insert the <br> 1.1452 + at the caret position, but after the current empty line. 1.1453 + 1.1454 + So we have several cases to test: 1.1455 + 1.1456 + 1) We only ever want to skip, if the next visible thing after the current position is a break 1.1457 + 1.1458 + 2) We do not want to skip if there is no previous visible thing at all 1.1459 + That is detected if the call to PriorVisibleNode gives us an offset of zero. 1.1460 + Because PriorVisibleNode always positions after the prior node, we would 1.1461 + see an offset > 0, if there were a prior node. 1.1462 + 1.1463 + 3) We do not want to skip, if both the next and the previous visible things are breaks. 1.1464 + 1.1465 + 4) We do not want to skip if the previous visible thing is in a different block 1.1466 + than the insertion position. 1.1467 + */ 1.1468 + 1.1469 + if (!IsBlockNode(firstNodeToInsert)) 1.1470 + return; 1.1471 + 1.1472 + nsWSRunObject wsObj(this, *insertParentNode, *insertOffset); 1.1473 + nsCOMPtr<nsIDOMNode> nextVisNode; 1.1474 + nsCOMPtr<nsIDOMNode> prevVisNode; 1.1475 + int32_t nextVisOffset=0; 1.1476 + WSType nextVisType; 1.1477 + int32_t prevVisOffset=0; 1.1478 + WSType prevVisType; 1.1479 + 1.1480 + wsObj.NextVisibleNode(*insertParentNode, *insertOffset, address_of(nextVisNode), &nextVisOffset, &nextVisType); 1.1481 + if (!nextVisNode) 1.1482 + return; 1.1483 + 1.1484 + if (!(nextVisType & WSType::br)) { 1.1485 + return; 1.1486 + } 1.1487 + 1.1488 + wsObj.PriorVisibleNode(*insertParentNode, *insertOffset, address_of(prevVisNode), &prevVisOffset, &prevVisType); 1.1489 + if (!prevVisNode) 1.1490 + return; 1.1491 + 1.1492 + if (prevVisType & WSType::br) { 1.1493 + return; 1.1494 + } 1.1495 + 1.1496 + if (prevVisType & WSType::thisBlock) { 1.1497 + return; 1.1498 + } 1.1499 + 1.1500 + int32_t brOffset=0; 1.1501 + nsCOMPtr<nsIDOMNode> brNode = GetNodeLocation(nextVisNode, &brOffset); 1.1502 + 1.1503 + *insertParentNode = brNode; 1.1504 + *insertOffset = brOffset + 1; 1.1505 +} 1.1506 + 1.1507 +NS_IMETHODIMP 1.1508 +nsHTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement, bool aDeleteSelection) 1.1509 +{ 1.1510 + // Protect the edit rules object from dying 1.1511 + nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules); 1.1512 + 1.1513 + nsresult res = NS_ERROR_NOT_INITIALIZED; 1.1514 + 1.1515 + NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER); 1.1516 + 1.1517 + nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aElement); 1.1518 + 1.1519 + ForceCompositionEnd(); 1.1520 + nsAutoEditBatch beginBatching(this); 1.1521 + nsAutoRules beginRulesSniffing(this, EditAction::insertElement, nsIEditor::eNext); 1.1522 + 1.1523 + nsRefPtr<Selection> selection = GetSelection(); 1.1524 + if (!selection) { 1.1525 + return NS_ERROR_FAILURE; 1.1526 + } 1.1527 + 1.1528 + // hand off to the rules system, see if it has anything to say about this 1.1529 + bool cancel, handled; 1.1530 + nsTextRulesInfo ruleInfo(EditAction::insertElement); 1.1531 + ruleInfo.insertElement = aElement; 1.1532 + res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); 1.1533 + if (cancel || (NS_FAILED(res))) return res; 1.1534 + 1.1535 + if (!handled) 1.1536 + { 1.1537 + if (aDeleteSelection) 1.1538 + { 1.1539 + if (!IsBlockNode(aElement)) { 1.1540 + // E.g., inserting an image. In this case we don't need to delete any 1.1541 + // inline wrappers before we do the insertion. Otherwise we let 1.1542 + // DeleteSelectionAndPrepareToCreateNode do the deletion for us, which 1.1543 + // calls DeleteSelection with aStripWrappers = eStrip. 1.1544 + res = DeleteSelection(nsIEditor::eNone, nsIEditor::eNoStrip); 1.1545 + NS_ENSURE_SUCCESS(res, res); 1.1546 + } 1.1547 + 1.1548 + nsresult result = DeleteSelectionAndPrepareToCreateNode(); 1.1549 + NS_ENSURE_SUCCESS(result, result); 1.1550 + } 1.1551 + 1.1552 + // If deleting, selection will be collapsed. 1.1553 + // so if not, we collapse it 1.1554 + if (!aDeleteSelection) 1.1555 + { 1.1556 + // Named Anchor is a special case, 1.1557 + // We collapse to insert element BEFORE the selection 1.1558 + // For all other tags, we insert AFTER the selection 1.1559 + if (nsHTMLEditUtils::IsNamedAnchor(node)) 1.1560 + { 1.1561 + selection->CollapseToStart(); 1.1562 + } else { 1.1563 + selection->CollapseToEnd(); 1.1564 + } 1.1565 + } 1.1566 + 1.1567 + nsCOMPtr<nsIDOMNode> parentSelectedNode; 1.1568 + int32_t offsetForInsert; 1.1569 + res = selection->GetAnchorNode(getter_AddRefs(parentSelectedNode)); 1.1570 + // XXX: ERROR_HANDLING bad XPCOM usage 1.1571 + if (NS_SUCCEEDED(res) && NS_SUCCEEDED(selection->GetAnchorOffset(&offsetForInsert)) && parentSelectedNode) 1.1572 + { 1.1573 +#ifdef DEBUG_cmanske 1.1574 + { 1.1575 + nsAutoString name; 1.1576 + parentSelectedNode->GetNodeName(name); 1.1577 + printf("InsertElement: Anchor node of selection: "); 1.1578 + wprintf(name.get()); 1.1579 + printf(" Offset: %d\n", offsetForInsert); 1.1580 + } 1.1581 +#endif 1.1582 + 1.1583 + // Adjust position based on the node we are going to insert. 1.1584 + NormalizeEOLInsertPosition(node, address_of(parentSelectedNode), &offsetForInsert); 1.1585 + 1.1586 + res = InsertNodeAtPoint(node, address_of(parentSelectedNode), &offsetForInsert, false); 1.1587 + NS_ENSURE_SUCCESS(res, res); 1.1588 + // Set caret after element, but check for special case 1.1589 + // of inserting table-related elements: set in first cell instead 1.1590 + if (!SetCaretInTableCell(aElement)) 1.1591 + { 1.1592 + res = SetCaretAfterElement(aElement); 1.1593 + NS_ENSURE_SUCCESS(res, res); 1.1594 + } 1.1595 + // check for inserting a whole table at the end of a block. If so insert a br after it. 1.1596 + if (nsHTMLEditUtils::IsTable(node)) 1.1597 + { 1.1598 + bool isLast; 1.1599 + res = IsLastEditableChild(node, &isLast); 1.1600 + NS_ENSURE_SUCCESS(res, res); 1.1601 + if (isLast) 1.1602 + { 1.1603 + nsCOMPtr<nsIDOMNode> brNode; 1.1604 + res = CreateBR(parentSelectedNode, offsetForInsert+1, address_of(brNode)); 1.1605 + NS_ENSURE_SUCCESS(res, res); 1.1606 + selection->Collapse(parentSelectedNode, offsetForInsert+1); 1.1607 + } 1.1608 + } 1.1609 + } 1.1610 + } 1.1611 + res = mRules->DidDoAction(selection, &ruleInfo, res); 1.1612 + return res; 1.1613 +} 1.1614 + 1.1615 + 1.1616 +/* 1.1617 + InsertNodeAtPoint: attempts to insert aNode into the document, at a point specified by 1.1618 + {*ioParent,*ioOffset}. Checks with strict dtd to see if containment is allowed. If not 1.1619 + allowed, will attempt to find a parent in the parent hierarchy of *ioParent that will 1.1620 + accept aNode as a child. If such a parent is found, will split the document tree from 1.1621 + {*ioParent,*ioOffset} up to parent, and then insert aNode. ioParent & ioOffset are then 1.1622 + adjusted to point to the actual location that aNode was inserted at. aNoEmptyNodes 1.1623 + specifies if the splitting process is allowed to reslt in empty nodes. 1.1624 + nsIDOMNode *aNode node to insert 1.1625 + nsCOMPtr<nsIDOMNode> *ioParent insertion parent 1.1626 + int32_t *ioOffset insertion offset 1.1627 + bool aNoEmptyNodes splitting can result in empty nodes? 1.1628 +*/ 1.1629 +nsresult 1.1630 +nsHTMLEditor::InsertNodeAtPoint(nsIDOMNode *aNode, 1.1631 + nsCOMPtr<nsIDOMNode> *ioParent, 1.1632 + int32_t *ioOffset, 1.1633 + bool aNoEmptyNodes) 1.1634 +{ 1.1635 + NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER); 1.1636 + NS_ENSURE_TRUE(ioParent, NS_ERROR_NULL_POINTER); 1.1637 + NS_ENSURE_TRUE(*ioParent, NS_ERROR_NULL_POINTER); 1.1638 + NS_ENSURE_TRUE(ioOffset, NS_ERROR_NULL_POINTER); 1.1639 + 1.1640 + nsresult res = NS_OK; 1.1641 + nsCOMPtr<nsIDOMNode> parent = *ioParent; 1.1642 + nsCOMPtr<nsIDOMNode> topChild = *ioParent; 1.1643 + nsCOMPtr<nsIDOMNode> tmp; 1.1644 + int32_t offsetOfInsert = *ioOffset; 1.1645 + 1.1646 + // Search up the parent chain to find a suitable container 1.1647 + while (!CanContain(parent, aNode)) { 1.1648 + // If the current parent is a root (body or table element) 1.1649 + // then go no further - we can't insert 1.1650 + if (nsTextEditUtils::IsBody(parent) || nsHTMLEditUtils::IsTableElement(parent)) 1.1651 + return NS_ERROR_FAILURE; 1.1652 + // Get the next parent 1.1653 + parent->GetParentNode(getter_AddRefs(tmp)); 1.1654 + NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE); 1.1655 + topChild = parent; 1.1656 + parent = tmp; 1.1657 + } 1.1658 + if (parent != topChild) 1.1659 + { 1.1660 + // we need to split some levels above the original selection parent 1.1661 + res = SplitNodeDeep(topChild, *ioParent, *ioOffset, &offsetOfInsert, aNoEmptyNodes); 1.1662 + NS_ENSURE_SUCCESS(res, res); 1.1663 + *ioParent = parent; 1.1664 + *ioOffset = offsetOfInsert; 1.1665 + } 1.1666 + // Now we can insert the new node 1.1667 + res = InsertNode(aNode, parent, offsetOfInsert); 1.1668 + return res; 1.1669 +} 1.1670 + 1.1671 +NS_IMETHODIMP 1.1672 +nsHTMLEditor::SelectElement(nsIDOMElement* aElement) 1.1673 +{ 1.1674 + nsresult res = NS_ERROR_NULL_POINTER; 1.1675 + 1.1676 + // Must be sure that element is contained in the document body 1.1677 + if (IsDescendantOfEditorRoot(aElement)) { 1.1678 + nsCOMPtr<nsISelection> selection; 1.1679 + res = GetSelection(getter_AddRefs(selection)); 1.1680 + NS_ENSURE_SUCCESS(res, res); 1.1681 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.1682 + nsCOMPtr<nsIDOMNode>parent; 1.1683 + res = aElement->GetParentNode(getter_AddRefs(parent)); 1.1684 + if (NS_SUCCEEDED(res) && parent) 1.1685 + { 1.1686 + int32_t offsetInParent = GetChildOffset(aElement, parent); 1.1687 + 1.1688 + // Collapse selection to just before desired element, 1.1689 + res = selection->Collapse(parent, offsetInParent); 1.1690 + if (NS_SUCCEEDED(res)) { 1.1691 + // then extend it to just after 1.1692 + res = selection->Extend(parent, offsetInParent + 1); 1.1693 + } 1.1694 + } 1.1695 + } 1.1696 + return res; 1.1697 +} 1.1698 + 1.1699 +NS_IMETHODIMP 1.1700 +nsHTMLEditor::SetCaretAfterElement(nsIDOMElement* aElement) 1.1701 +{ 1.1702 + nsresult res = NS_ERROR_NULL_POINTER; 1.1703 + 1.1704 + // Be sure the element is contained in the document body 1.1705 + if (aElement && IsDescendantOfEditorRoot(aElement)) { 1.1706 + nsCOMPtr<nsISelection> selection; 1.1707 + res = GetSelection(getter_AddRefs(selection)); 1.1708 + NS_ENSURE_SUCCESS(res, res); 1.1709 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.1710 + nsCOMPtr<nsIDOMNode>parent; 1.1711 + res = aElement->GetParentNode(getter_AddRefs(parent)); 1.1712 + NS_ENSURE_SUCCESS(res, res); 1.1713 + NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER); 1.1714 + int32_t offsetInParent = GetChildOffset(aElement, parent); 1.1715 + // Collapse selection to just after desired element, 1.1716 + res = selection->Collapse(parent, offsetInParent + 1); 1.1717 + } 1.1718 + return res; 1.1719 +} 1.1720 + 1.1721 +NS_IMETHODIMP 1.1722 +nsHTMLEditor::SetParagraphFormat(const nsAString& aParagraphFormat) 1.1723 +{ 1.1724 + nsAutoString tag; tag.Assign(aParagraphFormat); 1.1725 + ToLowerCase(tag); 1.1726 + if (tag.EqualsLiteral("dd") || tag.EqualsLiteral("dt")) 1.1727 + return MakeDefinitionItem(tag); 1.1728 + else 1.1729 + return InsertBasicBlock(tag); 1.1730 +} 1.1731 + 1.1732 +NS_IMETHODIMP 1.1733 +nsHTMLEditor::GetParagraphState(bool *aMixed, nsAString &outFormat) 1.1734 +{ 1.1735 + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } 1.1736 + NS_ENSURE_TRUE(aMixed, NS_ERROR_NULL_POINTER); 1.1737 + nsRefPtr<nsHTMLEditRules> htmlRules = static_cast<nsHTMLEditRules*>(mRules.get()); 1.1738 + 1.1739 + return htmlRules->GetParagraphState(aMixed, outFormat); 1.1740 +} 1.1741 + 1.1742 +NS_IMETHODIMP 1.1743 +nsHTMLEditor::GetBackgroundColorState(bool *aMixed, nsAString &aOutColor) 1.1744 +{ 1.1745 + nsresult res; 1.1746 + if (IsCSSEnabled()) { 1.1747 + // if we are in CSS mode, we have to check if the containing block defines 1.1748 + // a background color 1.1749 + res = GetCSSBackgroundColorState(aMixed, aOutColor, true); 1.1750 + } 1.1751 + else { 1.1752 + // in HTML mode, we look only at page's background 1.1753 + res = GetHTMLBackgroundColorState(aMixed, aOutColor); 1.1754 + } 1.1755 + return res; 1.1756 +} 1.1757 + 1.1758 +NS_IMETHODIMP 1.1759 +nsHTMLEditor::GetHighlightColorState(bool *aMixed, nsAString &aOutColor) 1.1760 +{ 1.1761 + nsresult res = NS_OK; 1.1762 + *aMixed = false; 1.1763 + aOutColor.AssignLiteral("transparent"); 1.1764 + if (IsCSSEnabled()) { 1.1765 + // in CSS mode, text background can be added by the Text Highlight button 1.1766 + // we need to query the background of the selection without looking for 1.1767 + // the block container of the ranges in the selection 1.1768 + res = GetCSSBackgroundColorState(aMixed, aOutColor, false); 1.1769 + } 1.1770 + return res; 1.1771 +} 1.1772 + 1.1773 +nsresult 1.1774 +nsHTMLEditor::GetCSSBackgroundColorState(bool *aMixed, nsAString &aOutColor, bool aBlockLevel) 1.1775 +{ 1.1776 + NS_ENSURE_TRUE(aMixed, NS_ERROR_NULL_POINTER); 1.1777 + *aMixed = false; 1.1778 + // the default background color is transparent 1.1779 + aOutColor.AssignLiteral("transparent"); 1.1780 + 1.1781 + // get selection 1.1782 + nsCOMPtr<nsISelection>selection; 1.1783 + nsresult res = GetSelection(getter_AddRefs(selection)); 1.1784 + NS_ENSURE_SUCCESS(res, res); 1.1785 + 1.1786 + // get selection location 1.1787 + nsCOMPtr<nsIDOMNode> parent; 1.1788 + int32_t offset; 1.1789 + res = GetStartNodeAndOffset(selection, getter_AddRefs(parent), &offset); 1.1790 + NS_ENSURE_SUCCESS(res, res); 1.1791 + NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER); 1.1792 + 1.1793 + // is the selection collapsed? 1.1794 + nsCOMPtr<nsIDOMNode> nodeToExamine; 1.1795 + if (selection->Collapsed() || IsTextNode(parent)) { 1.1796 + // we want to look at the parent and ancestors 1.1797 + nodeToExamine = parent; 1.1798 + } else { 1.1799 + // otherwise we want to look at the first editable node after 1.1800 + // {parent,offset} and its ancestors for divs with alignment on them 1.1801 + nodeToExamine = GetChildAt(parent, offset); 1.1802 + //GetNextNode(parent, offset, true, address_of(nodeToExamine)); 1.1803 + } 1.1804 + 1.1805 + NS_ENSURE_TRUE(nodeToExamine, NS_ERROR_NULL_POINTER); 1.1806 + 1.1807 + // is the node to examine a block ? 1.1808 + bool isBlock; 1.1809 + res = NodeIsBlockStatic(nodeToExamine, &isBlock); 1.1810 + NS_ENSURE_SUCCESS(res, res); 1.1811 + 1.1812 + nsCOMPtr<nsIDOMNode> tmp; 1.1813 + 1.1814 + if (aBlockLevel) { 1.1815 + // we are querying the block background (and not the text background), let's 1.1816 + // climb to the block container 1.1817 + nsCOMPtr<nsIDOMNode> blockParent = nodeToExamine; 1.1818 + if (!isBlock) { 1.1819 + blockParent = GetBlockNodeParent(nodeToExamine); 1.1820 + NS_ENSURE_TRUE(blockParent, NS_OK); 1.1821 + } 1.1822 + 1.1823 + // Make sure to not walk off onto the Document node 1.1824 + nsCOMPtr<nsIDOMElement> element; 1.1825 + do { 1.1826 + // retrieve the computed style of background-color for blockParent 1.1827 + mHTMLCSSUtils->GetComputedProperty(blockParent, 1.1828 + nsEditProperty::cssBackgroundColor, 1.1829 + aOutColor); 1.1830 + tmp.swap(blockParent); 1.1831 + res = tmp->GetParentNode(getter_AddRefs(blockParent)); 1.1832 + element = do_QueryInterface(blockParent); 1.1833 + // look at parent if the queried color is transparent and if the node to 1.1834 + // examine is not the root of the document 1.1835 + } while (aOutColor.EqualsLiteral("transparent") && element); 1.1836 + if (aOutColor.EqualsLiteral("transparent")) { 1.1837 + // we have hit the root of the document and the color is still transparent ! 1.1838 + // Grumble... Let's look at the default background color because that's the 1.1839 + // color we are looking for 1.1840 + mHTMLCSSUtils->GetDefaultBackgroundColor(aOutColor); 1.1841 + } 1.1842 + } 1.1843 + else { 1.1844 + // no, we are querying the text background for the Text Highlight button 1.1845 + if (IsTextNode(nodeToExamine)) { 1.1846 + // if the node of interest is a text node, let's climb a level 1.1847 + res = nodeToExamine->GetParentNode(getter_AddRefs(parent)); 1.1848 + NS_ENSURE_SUCCESS(res, res); 1.1849 + nodeToExamine = parent; 1.1850 + } 1.1851 + do { 1.1852 + // is the node to examine a block ? 1.1853 + res = NodeIsBlockStatic(nodeToExamine, &isBlock); 1.1854 + NS_ENSURE_SUCCESS(res, res); 1.1855 + if (isBlock) { 1.1856 + // yes it is a block; in that case, the text background color is transparent 1.1857 + aOutColor.AssignLiteral("transparent"); 1.1858 + break; 1.1859 + } 1.1860 + else { 1.1861 + // no, it's not; let's retrieve the computed style of background-color for the 1.1862 + // node to examine 1.1863 + mHTMLCSSUtils->GetComputedProperty(nodeToExamine, nsEditProperty::cssBackgroundColor, 1.1864 + aOutColor); 1.1865 + if (!aOutColor.EqualsLiteral("transparent")) { 1.1866 + break; 1.1867 + } 1.1868 + } 1.1869 + tmp.swap(nodeToExamine); 1.1870 + res = tmp->GetParentNode(getter_AddRefs(nodeToExamine)); 1.1871 + NS_ENSURE_SUCCESS(res, res); 1.1872 + } while ( aOutColor.EqualsLiteral("transparent") && nodeToExamine ); 1.1873 + } 1.1874 + return NS_OK; 1.1875 +} 1.1876 + 1.1877 +NS_IMETHODIMP 1.1878 +nsHTMLEditor::GetHTMLBackgroundColorState(bool *aMixed, nsAString &aOutColor) 1.1879 +{ 1.1880 + //TODO: We don't handle "mixed" correctly! 1.1881 + NS_ENSURE_TRUE(aMixed, NS_ERROR_NULL_POINTER); 1.1882 + *aMixed = false; 1.1883 + aOutColor.Truncate(); 1.1884 + 1.1885 + nsCOMPtr<nsIDOMElement> domElement; 1.1886 + int32_t selectedCount; 1.1887 + nsAutoString tagName; 1.1888 + nsresult res = GetSelectedOrParentTableElement(tagName, 1.1889 + &selectedCount, 1.1890 + getter_AddRefs(domElement)); 1.1891 + NS_ENSURE_SUCCESS(res, res); 1.1892 + 1.1893 + nsCOMPtr<dom::Element> element = do_QueryInterface(domElement); 1.1894 + 1.1895 + while (element) { 1.1896 + // We are in a cell or selected table 1.1897 + element->GetAttr(kNameSpaceID_None, nsGkAtoms::bgcolor, aOutColor); 1.1898 + 1.1899 + // Done if we have a color explicitly set 1.1900 + if (!aOutColor.IsEmpty()) { 1.1901 + return NS_OK; 1.1902 + } 1.1903 + 1.1904 + // Once we hit the body, we're done 1.1905 + if (element->IsHTML(nsGkAtoms::body)) { 1.1906 + return NS_OK; 1.1907 + } 1.1908 + 1.1909 + // No color is set, but we need to report visible color inherited 1.1910 + // from nested cells/tables, so search up parent chain 1.1911 + element = element->GetParentElement(); 1.1912 + } 1.1913 + 1.1914 + // If no table or cell found, get page body 1.1915 + dom::Element* bodyElement = GetRoot(); 1.1916 + NS_ENSURE_TRUE(bodyElement, NS_ERROR_NULL_POINTER); 1.1917 + 1.1918 + bodyElement->GetAttr(kNameSpaceID_None, nsGkAtoms::bgcolor, aOutColor); 1.1919 + return NS_OK; 1.1920 +} 1.1921 + 1.1922 +NS_IMETHODIMP 1.1923 +nsHTMLEditor::GetListState(bool *aMixed, bool *aOL, bool *aUL, bool *aDL) 1.1924 +{ 1.1925 + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } 1.1926 + NS_ENSURE_TRUE(aMixed && aOL && aUL && aDL, NS_ERROR_NULL_POINTER); 1.1927 + nsRefPtr<nsHTMLEditRules> htmlRules = static_cast<nsHTMLEditRules*>(mRules.get()); 1.1928 + 1.1929 + return htmlRules->GetListState(aMixed, aOL, aUL, aDL); 1.1930 +} 1.1931 + 1.1932 +NS_IMETHODIMP 1.1933 +nsHTMLEditor::GetListItemState(bool *aMixed, bool *aLI, bool *aDT, bool *aDD) 1.1934 +{ 1.1935 + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } 1.1936 + NS_ENSURE_TRUE(aMixed && aLI && aDT && aDD, NS_ERROR_NULL_POINTER); 1.1937 + 1.1938 + nsRefPtr<nsHTMLEditRules> htmlRules = static_cast<nsHTMLEditRules*>(mRules.get()); 1.1939 + 1.1940 + return htmlRules->GetListItemState(aMixed, aLI, aDT, aDD); 1.1941 +} 1.1942 + 1.1943 +NS_IMETHODIMP 1.1944 +nsHTMLEditor::GetAlignment(bool *aMixed, nsIHTMLEditor::EAlignment *aAlign) 1.1945 +{ 1.1946 + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } 1.1947 + NS_ENSURE_TRUE(aMixed && aAlign, NS_ERROR_NULL_POINTER); 1.1948 + nsRefPtr<nsHTMLEditRules> htmlRules = static_cast<nsHTMLEditRules*>(mRules.get()); 1.1949 + 1.1950 + return htmlRules->GetAlignment(aMixed, aAlign); 1.1951 +} 1.1952 + 1.1953 + 1.1954 +NS_IMETHODIMP 1.1955 +nsHTMLEditor::GetIndentState(bool *aCanIndent, bool *aCanOutdent) 1.1956 +{ 1.1957 + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } 1.1958 + NS_ENSURE_TRUE(aCanIndent && aCanOutdent, NS_ERROR_NULL_POINTER); 1.1959 + 1.1960 + nsRefPtr<nsHTMLEditRules> htmlRules = static_cast<nsHTMLEditRules*>(mRules.get()); 1.1961 + 1.1962 + return htmlRules->GetIndentState(aCanIndent, aCanOutdent); 1.1963 +} 1.1964 + 1.1965 +NS_IMETHODIMP 1.1966 +nsHTMLEditor::MakeOrChangeList(const nsAString& aListType, bool entireList, const nsAString& aBulletType) 1.1967 +{ 1.1968 + nsresult res; 1.1969 + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } 1.1970 + 1.1971 + // Protect the edit rules object from dying 1.1972 + nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules); 1.1973 + 1.1974 + bool cancel, handled; 1.1975 + 1.1976 + nsAutoEditBatch beginBatching(this); 1.1977 + nsAutoRules beginRulesSniffing(this, EditAction::makeList, nsIEditor::eNext); 1.1978 + 1.1979 + // pre-process 1.1980 + nsRefPtr<Selection> selection = GetSelection(); 1.1981 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.1982 + 1.1983 + nsTextRulesInfo ruleInfo(EditAction::makeList); 1.1984 + ruleInfo.blockType = &aListType; 1.1985 + ruleInfo.entireList = entireList; 1.1986 + ruleInfo.bulletType = &aBulletType; 1.1987 + res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); 1.1988 + if (cancel || (NS_FAILED(res))) return res; 1.1989 + 1.1990 + if (!handled) 1.1991 + { 1.1992 + // Find out if the selection is collapsed: 1.1993 + bool isCollapsed = selection->Collapsed(); 1.1994 + 1.1995 + nsCOMPtr<nsIDOMNode> node; 1.1996 + int32_t offset; 1.1997 + res = GetStartNodeAndOffset(selection, getter_AddRefs(node), &offset); 1.1998 + if (!node) res = NS_ERROR_FAILURE; 1.1999 + NS_ENSURE_SUCCESS(res, res); 1.2000 + 1.2001 + if (isCollapsed) 1.2002 + { 1.2003 + // have to find a place to put the list 1.2004 + nsCOMPtr<nsIDOMNode> parent = node; 1.2005 + nsCOMPtr<nsIDOMNode> topChild = node; 1.2006 + nsCOMPtr<nsIDOMNode> tmp; 1.2007 + 1.2008 + nsCOMPtr<nsIAtom> listAtom = do_GetAtom(aListType); 1.2009 + while (!CanContainTag(parent, listAtom)) { 1.2010 + parent->GetParentNode(getter_AddRefs(tmp)); 1.2011 + NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE); 1.2012 + topChild = parent; 1.2013 + parent = tmp; 1.2014 + } 1.2015 + 1.2016 + if (parent != node) 1.2017 + { 1.2018 + // we need to split up to the child of parent 1.2019 + res = SplitNodeDeep(topChild, node, offset, &offset); 1.2020 + NS_ENSURE_SUCCESS(res, res); 1.2021 + } 1.2022 + 1.2023 + // make a list 1.2024 + nsCOMPtr<nsIDOMNode> newList; 1.2025 + res = CreateNode(aListType, parent, offset, getter_AddRefs(newList)); 1.2026 + NS_ENSURE_SUCCESS(res, res); 1.2027 + // make a list item 1.2028 + nsCOMPtr<nsIDOMNode> newItem; 1.2029 + res = CreateNode(NS_LITERAL_STRING("li"), newList, 0, getter_AddRefs(newItem)); 1.2030 + NS_ENSURE_SUCCESS(res, res); 1.2031 + res = selection->Collapse(newItem,0); 1.2032 + NS_ENSURE_SUCCESS(res, res); 1.2033 + } 1.2034 + } 1.2035 + 1.2036 + res = mRules->DidDoAction(selection, &ruleInfo, res); 1.2037 + return res; 1.2038 +} 1.2039 + 1.2040 + 1.2041 +NS_IMETHODIMP 1.2042 +nsHTMLEditor::RemoveList(const nsAString& aListType) 1.2043 +{ 1.2044 + nsresult res; 1.2045 + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } 1.2046 + 1.2047 + // Protect the edit rules object from dying 1.2048 + nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules); 1.2049 + 1.2050 + bool cancel, handled; 1.2051 + 1.2052 + nsAutoEditBatch beginBatching(this); 1.2053 + nsAutoRules beginRulesSniffing(this, EditAction::removeList, nsIEditor::eNext); 1.2054 + 1.2055 + // pre-process 1.2056 + nsRefPtr<Selection> selection = GetSelection(); 1.2057 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.2058 + 1.2059 + nsTextRulesInfo ruleInfo(EditAction::removeList); 1.2060 + if (aListType.LowerCaseEqualsLiteral("ol")) 1.2061 + ruleInfo.bOrdered = true; 1.2062 + else ruleInfo.bOrdered = false; 1.2063 + res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); 1.2064 + if (cancel || (NS_FAILED(res))) return res; 1.2065 + 1.2066 + // no default behavior for this yet. what would it mean? 1.2067 + 1.2068 + res = mRules->DidDoAction(selection, &ruleInfo, res); 1.2069 + return res; 1.2070 +} 1.2071 + 1.2072 +nsresult 1.2073 +nsHTMLEditor::MakeDefinitionItem(const nsAString& aItemType) 1.2074 +{ 1.2075 + nsresult res; 1.2076 + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } 1.2077 + 1.2078 + // Protect the edit rules object from dying 1.2079 + nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules); 1.2080 + 1.2081 + bool cancel, handled; 1.2082 + 1.2083 + nsAutoEditBatch beginBatching(this); 1.2084 + nsAutoRules beginRulesSniffing(this, EditAction::makeDefListItem, nsIEditor::eNext); 1.2085 + 1.2086 + // pre-process 1.2087 + nsRefPtr<Selection> selection = GetSelection(); 1.2088 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.2089 + nsTextRulesInfo ruleInfo(EditAction::makeDefListItem); 1.2090 + ruleInfo.blockType = &aItemType; 1.2091 + res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); 1.2092 + if (cancel || (NS_FAILED(res))) return res; 1.2093 + 1.2094 + if (!handled) 1.2095 + { 1.2096 + // todo: no default for now. we count on rules to handle it. 1.2097 + } 1.2098 + 1.2099 + res = mRules->DidDoAction(selection, &ruleInfo, res); 1.2100 + return res; 1.2101 +} 1.2102 + 1.2103 +nsresult 1.2104 +nsHTMLEditor::InsertBasicBlock(const nsAString& aBlockType) 1.2105 +{ 1.2106 + nsresult res; 1.2107 + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } 1.2108 + 1.2109 + // Protect the edit rules object from dying 1.2110 + nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules); 1.2111 + 1.2112 + bool cancel, handled; 1.2113 + 1.2114 + nsAutoEditBatch beginBatching(this); 1.2115 + nsAutoRules beginRulesSniffing(this, EditAction::makeBasicBlock, nsIEditor::eNext); 1.2116 + 1.2117 + // pre-process 1.2118 + nsRefPtr<Selection> selection = GetSelection(); 1.2119 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.2120 + nsTextRulesInfo ruleInfo(EditAction::makeBasicBlock); 1.2121 + ruleInfo.blockType = &aBlockType; 1.2122 + res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); 1.2123 + if (cancel || (NS_FAILED(res))) return res; 1.2124 + 1.2125 + if (!handled) 1.2126 + { 1.2127 + // Find out if the selection is collapsed: 1.2128 + bool isCollapsed = selection->Collapsed(); 1.2129 + 1.2130 + nsCOMPtr<nsIDOMNode> node; 1.2131 + int32_t offset; 1.2132 + res = GetStartNodeAndOffset(selection, getter_AddRefs(node), &offset); 1.2133 + if (!node) res = NS_ERROR_FAILURE; 1.2134 + NS_ENSURE_SUCCESS(res, res); 1.2135 + 1.2136 + if (isCollapsed) 1.2137 + { 1.2138 + // have to find a place to put the block 1.2139 + nsCOMPtr<nsIDOMNode> parent = node; 1.2140 + nsCOMPtr<nsIDOMNode> topChild = node; 1.2141 + nsCOMPtr<nsIDOMNode> tmp; 1.2142 + 1.2143 + nsCOMPtr<nsIAtom> blockAtom = do_GetAtom(aBlockType); 1.2144 + while (!CanContainTag(parent, blockAtom)) { 1.2145 + parent->GetParentNode(getter_AddRefs(tmp)); 1.2146 + NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE); 1.2147 + topChild = parent; 1.2148 + parent = tmp; 1.2149 + } 1.2150 + 1.2151 + if (parent != node) 1.2152 + { 1.2153 + // we need to split up to the child of parent 1.2154 + res = SplitNodeDeep(topChild, node, offset, &offset); 1.2155 + NS_ENSURE_SUCCESS(res, res); 1.2156 + } 1.2157 + 1.2158 + // make a block 1.2159 + nsCOMPtr<nsIDOMNode> newBlock; 1.2160 + res = CreateNode(aBlockType, parent, offset, getter_AddRefs(newBlock)); 1.2161 + NS_ENSURE_SUCCESS(res, res); 1.2162 + 1.2163 + // reposition selection to inside the block 1.2164 + res = selection->Collapse(newBlock,0); 1.2165 + NS_ENSURE_SUCCESS(res, res); 1.2166 + } 1.2167 + } 1.2168 + 1.2169 + res = mRules->DidDoAction(selection, &ruleInfo, res); 1.2170 + return res; 1.2171 +} 1.2172 + 1.2173 +NS_IMETHODIMP 1.2174 +nsHTMLEditor::Indent(const nsAString& aIndent) 1.2175 +{ 1.2176 + nsresult res; 1.2177 + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } 1.2178 + 1.2179 + // Protect the edit rules object from dying 1.2180 + nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules); 1.2181 + 1.2182 + bool cancel, handled; 1.2183 + EditAction opID = EditAction::indent; 1.2184 + if (aIndent.LowerCaseEqualsLiteral("outdent")) 1.2185 + { 1.2186 + opID = EditAction::outdent; 1.2187 + } 1.2188 + nsAutoEditBatch beginBatching(this); 1.2189 + nsAutoRules beginRulesSniffing(this, opID, nsIEditor::eNext); 1.2190 + 1.2191 + // pre-process 1.2192 + nsRefPtr<Selection> selection = GetSelection(); 1.2193 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.2194 + 1.2195 + nsTextRulesInfo ruleInfo(opID); 1.2196 + res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); 1.2197 + if (cancel || (NS_FAILED(res))) return res; 1.2198 + 1.2199 + if (!handled) 1.2200 + { 1.2201 + // Do default - insert a blockquote node if selection collapsed 1.2202 + nsCOMPtr<nsIDOMNode> node; 1.2203 + int32_t offset; 1.2204 + bool isCollapsed = selection->Collapsed(); 1.2205 + 1.2206 + res = GetStartNodeAndOffset(selection, getter_AddRefs(node), &offset); 1.2207 + if (!node) res = NS_ERROR_FAILURE; 1.2208 + NS_ENSURE_SUCCESS(res, res); 1.2209 + 1.2210 + if (aIndent.EqualsLiteral("indent")) 1.2211 + { 1.2212 + if (isCollapsed) 1.2213 + { 1.2214 + // have to find a place to put the blockquote 1.2215 + nsCOMPtr<nsIDOMNode> parent = node; 1.2216 + nsCOMPtr<nsIDOMNode> topChild = node; 1.2217 + nsCOMPtr<nsIDOMNode> tmp; 1.2218 + while (!CanContainTag(parent, nsGkAtoms::blockquote)) { 1.2219 + parent->GetParentNode(getter_AddRefs(tmp)); 1.2220 + NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE); 1.2221 + topChild = parent; 1.2222 + parent = tmp; 1.2223 + } 1.2224 + 1.2225 + if (parent != node) 1.2226 + { 1.2227 + // we need to split up to the child of parent 1.2228 + res = SplitNodeDeep(topChild, node, offset, &offset); 1.2229 + NS_ENSURE_SUCCESS(res, res); 1.2230 + } 1.2231 + 1.2232 + // make a blockquote 1.2233 + nsCOMPtr<nsIDOMNode> newBQ; 1.2234 + res = CreateNode(NS_LITERAL_STRING("blockquote"), parent, offset, 1.2235 + getter_AddRefs(newBQ)); 1.2236 + NS_ENSURE_SUCCESS(res, res); 1.2237 + // put a space in it so layout will draw the list item 1.2238 + res = selection->Collapse(newBQ,0); 1.2239 + NS_ENSURE_SUCCESS(res, res); 1.2240 + res = InsertText(NS_LITERAL_STRING(" ")); 1.2241 + NS_ENSURE_SUCCESS(res, res); 1.2242 + // reposition selection to before the space character 1.2243 + res = GetStartNodeAndOffset(selection, getter_AddRefs(node), &offset); 1.2244 + NS_ENSURE_SUCCESS(res, res); 1.2245 + res = selection->Collapse(node,0); 1.2246 + NS_ENSURE_SUCCESS(res, res); 1.2247 + } 1.2248 + } 1.2249 + } 1.2250 + res = mRules->DidDoAction(selection, &ruleInfo, res); 1.2251 + return res; 1.2252 +} 1.2253 + 1.2254 +//TODO: IMPLEMENT ALIGNMENT! 1.2255 + 1.2256 +NS_IMETHODIMP 1.2257 +nsHTMLEditor::Align(const nsAString& aAlignType) 1.2258 +{ 1.2259 + // Protect the edit rules object from dying 1.2260 + nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules); 1.2261 + 1.2262 + nsAutoEditBatch beginBatching(this); 1.2263 + nsAutoRules beginRulesSniffing(this, EditAction::align, nsIEditor::eNext); 1.2264 + 1.2265 + nsCOMPtr<nsIDOMNode> node; 1.2266 + bool cancel, handled; 1.2267 + 1.2268 + // Find out if the selection is collapsed: 1.2269 + nsRefPtr<Selection> selection = GetSelection(); 1.2270 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.2271 + nsTextRulesInfo ruleInfo(EditAction::align); 1.2272 + ruleInfo.alignType = &aAlignType; 1.2273 + nsresult res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); 1.2274 + if (cancel || NS_FAILED(res)) 1.2275 + return res; 1.2276 + 1.2277 + res = mRules->DidDoAction(selection, &ruleInfo, res); 1.2278 + return res; 1.2279 +} 1.2280 + 1.2281 +NS_IMETHODIMP 1.2282 +nsHTMLEditor::GetElementOrParentByTagName(const nsAString& aTagName, nsIDOMNode *aNode, nsIDOMElement** aReturn) 1.2283 +{ 1.2284 + NS_ENSURE_TRUE(!aTagName.IsEmpty(), NS_ERROR_NULL_POINTER); 1.2285 + NS_ENSURE_TRUE(aReturn, NS_ERROR_NULL_POINTER); 1.2286 + 1.2287 + nsCOMPtr<nsINode> current = do_QueryInterface(aNode); 1.2288 + if (!current) { 1.2289 + // If no node supplied, get it from anchor node of current selection 1.2290 + nsRefPtr<Selection> selection = GetSelection(); 1.2291 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.2292 + 1.2293 + nsCOMPtr<nsINode> anchorNode = selection->GetAnchorNode(); 1.2294 + NS_ENSURE_TRUE(anchorNode, NS_ERROR_FAILURE); 1.2295 + 1.2296 + // Try to get the actual selected node 1.2297 + if (anchorNode->HasChildNodes() && anchorNode->IsContent()) { 1.2298 + uint32_t offset = selection->AnchorOffset(); 1.2299 + current = anchorNode->GetChildAt(offset); 1.2300 + } 1.2301 + // anchor node is probably a text node - just use that 1.2302 + if (!current) { 1.2303 + current = anchorNode; 1.2304 + } 1.2305 + } 1.2306 + 1.2307 + nsCOMPtr<nsIDOMNode> currentNode = current->AsDOMNode(); 1.2308 + 1.2309 + nsAutoString TagName(aTagName); 1.2310 + ToLowerCase(TagName); 1.2311 + bool getLink = IsLinkTag(TagName); 1.2312 + bool getNamedAnchor = IsNamedAnchorTag(TagName); 1.2313 + if ( getLink || getNamedAnchor) 1.2314 + { 1.2315 + TagName.AssignLiteral("a"); 1.2316 + } 1.2317 + bool findTableCell = TagName.EqualsLiteral("td"); 1.2318 + bool findList = TagName.EqualsLiteral("list"); 1.2319 + 1.2320 + // default is null - no element found 1.2321 + *aReturn = nullptr; 1.2322 + 1.2323 + nsCOMPtr<nsIDOMNode> parent; 1.2324 + bool bNodeFound = false; 1.2325 + 1.2326 + while (true) 1.2327 + { 1.2328 + nsAutoString currentTagName; 1.2329 + // Test if we have a link (an anchor with href set) 1.2330 + if ( (getLink && nsHTMLEditUtils::IsLink(currentNode)) || 1.2331 + (getNamedAnchor && nsHTMLEditUtils::IsNamedAnchor(currentNode)) ) 1.2332 + { 1.2333 + bNodeFound = true; 1.2334 + break; 1.2335 + } else { 1.2336 + if (findList) 1.2337 + { 1.2338 + // Match "ol", "ul", or "dl" for lists 1.2339 + if (nsHTMLEditUtils::IsList(currentNode)) 1.2340 + goto NODE_FOUND; 1.2341 + 1.2342 + } else if (findTableCell) 1.2343 + { 1.2344 + // Table cells are another special case: 1.2345 + // Match either "td" or "th" for them 1.2346 + if (nsHTMLEditUtils::IsTableCell(currentNode)) 1.2347 + goto NODE_FOUND; 1.2348 + 1.2349 + } else { 1.2350 + currentNode->GetNodeName(currentTagName); 1.2351 + if (currentTagName.Equals(TagName, nsCaseInsensitiveStringComparator())) 1.2352 + { 1.2353 +NODE_FOUND: 1.2354 + bNodeFound = true; 1.2355 + break; 1.2356 + } 1.2357 + } 1.2358 + } 1.2359 + // Search up the parent chain 1.2360 + // We should never fail because of root test below, but lets be safe 1.2361 + // XXX: ERROR_HANDLING error return code lost 1.2362 + if (NS_FAILED(currentNode->GetParentNode(getter_AddRefs(parent))) || !parent) 1.2363 + break; 1.2364 + 1.2365 + // Stop searching if parent is a body tag 1.2366 + nsAutoString parentTagName; 1.2367 + parent->GetNodeName(parentTagName); 1.2368 + // Note: Originally used IsRoot to stop at table cells, 1.2369 + // but that's too messy when you are trying to find the parent table 1.2370 + if(parentTagName.LowerCaseEqualsLiteral("body")) 1.2371 + break; 1.2372 + 1.2373 + currentNode = parent; 1.2374 + } 1.2375 + 1.2376 + if (!bNodeFound) { 1.2377 + return NS_EDITOR_ELEMENT_NOT_FOUND; 1.2378 + } 1.2379 + 1.2380 + nsCOMPtr<nsIDOMElement> currentElement = do_QueryInterface(currentNode); 1.2381 + currentElement.forget(aReturn); 1.2382 + return NS_OK; 1.2383 +} 1.2384 + 1.2385 +NS_IMETHODIMP 1.2386 +nsHTMLEditor::GetSelectedElement(const nsAString& aTagName, nsIDOMElement** aReturn) 1.2387 +{ 1.2388 + NS_ENSURE_TRUE(aReturn , NS_ERROR_NULL_POINTER); 1.2389 + 1.2390 + // default is null - no element found 1.2391 + *aReturn = nullptr; 1.2392 + 1.2393 + // First look for a single element in selection 1.2394 + nsCOMPtr<nsISelection>selection; 1.2395 + nsresult res = GetSelection(getter_AddRefs(selection)); 1.2396 + NS_ENSURE_SUCCESS(res, res); 1.2397 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.2398 + Selection* sel = static_cast<Selection*>(selection.get()); 1.2399 + 1.2400 + bool bNodeFound = false; 1.2401 + bool isCollapsed = selection->Collapsed(); 1.2402 + 1.2403 + nsAutoString domTagName; 1.2404 + nsAutoString TagName(aTagName); 1.2405 + ToLowerCase(TagName); 1.2406 + // Empty string indicates we should match any element tag 1.2407 + bool anyTag = (TagName.IsEmpty()); 1.2408 + bool isLinkTag = IsLinkTag(TagName); 1.2409 + bool isNamedAnchorTag = IsNamedAnchorTag(TagName); 1.2410 + 1.2411 + nsCOMPtr<nsIDOMElement> selectedElement; 1.2412 + nsCOMPtr<nsIDOMRange> range; 1.2413 + res = selection->GetRangeAt(0, getter_AddRefs(range)); 1.2414 + NS_ENSURE_SUCCESS(res, res); 1.2415 + 1.2416 + nsCOMPtr<nsIDOMNode> startParent; 1.2417 + int32_t startOffset, endOffset; 1.2418 + res = range->GetStartContainer(getter_AddRefs(startParent)); 1.2419 + NS_ENSURE_SUCCESS(res, res); 1.2420 + res = range->GetStartOffset(&startOffset); 1.2421 + NS_ENSURE_SUCCESS(res, res); 1.2422 + 1.2423 + nsCOMPtr<nsIDOMNode> endParent; 1.2424 + res = range->GetEndContainer(getter_AddRefs(endParent)); 1.2425 + NS_ENSURE_SUCCESS(res, res); 1.2426 + res = range->GetEndOffset(&endOffset); 1.2427 + NS_ENSURE_SUCCESS(res, res); 1.2428 + 1.2429 + // Optimization for a single selected element 1.2430 + if (startParent && startParent == endParent && (endOffset-startOffset) == 1) 1.2431 + { 1.2432 + nsCOMPtr<nsIDOMNode> selectedNode = GetChildAt(startParent, startOffset); 1.2433 + NS_ENSURE_SUCCESS(res, NS_OK); 1.2434 + if (selectedNode) 1.2435 + { 1.2436 + selectedNode->GetNodeName(domTagName); 1.2437 + ToLowerCase(domTagName); 1.2438 + 1.2439 + // Test for appropriate node type requested 1.2440 + if (anyTag || (TagName == domTagName) || 1.2441 + (isLinkTag && nsHTMLEditUtils::IsLink(selectedNode)) || 1.2442 + (isNamedAnchorTag && nsHTMLEditUtils::IsNamedAnchor(selectedNode))) 1.2443 + { 1.2444 + bNodeFound = true; 1.2445 + selectedElement = do_QueryInterface(selectedNode); 1.2446 + } 1.2447 + } 1.2448 + } 1.2449 + 1.2450 + if (!bNodeFound) 1.2451 + { 1.2452 + if (isLinkTag) 1.2453 + { 1.2454 + // Link tag is a special case - we return the anchor node 1.2455 + // found for any selection that is totally within a link, 1.2456 + // included a collapsed selection (just a caret in a link) 1.2457 + nsCOMPtr<nsIDOMNode> anchorNode; 1.2458 + res = selection->GetAnchorNode(getter_AddRefs(anchorNode)); 1.2459 + NS_ENSURE_SUCCESS(res, res); 1.2460 + int32_t anchorOffset = -1; 1.2461 + if (anchorNode) 1.2462 + selection->GetAnchorOffset(&anchorOffset); 1.2463 + 1.2464 + nsCOMPtr<nsIDOMNode> focusNode; 1.2465 + res = selection->GetFocusNode(getter_AddRefs(focusNode)); 1.2466 + NS_ENSURE_SUCCESS(res, res); 1.2467 + int32_t focusOffset = -1; 1.2468 + if (focusNode) 1.2469 + selection->GetFocusOffset(&focusOffset); 1.2470 + 1.2471 + // Link node must be the same for both ends of selection 1.2472 + if (NS_SUCCEEDED(res) && anchorNode) 1.2473 + { 1.2474 + nsCOMPtr<nsIDOMElement> parentLinkOfAnchor; 1.2475 + res = GetElementOrParentByTagName(NS_LITERAL_STRING("href"), anchorNode, getter_AddRefs(parentLinkOfAnchor)); 1.2476 + // XXX: ERROR_HANDLING can parentLinkOfAnchor be null? 1.2477 + if (NS_SUCCEEDED(res) && parentLinkOfAnchor) 1.2478 + { 1.2479 + if (isCollapsed) 1.2480 + { 1.2481 + // We have just a caret in the link 1.2482 + bNodeFound = true; 1.2483 + } else if(focusNode) 1.2484 + { // Link node must be the same for both ends of selection 1.2485 + nsCOMPtr<nsIDOMElement> parentLinkOfFocus; 1.2486 + res = GetElementOrParentByTagName(NS_LITERAL_STRING("href"), focusNode, getter_AddRefs(parentLinkOfFocus)); 1.2487 + if (NS_SUCCEEDED(res) && parentLinkOfFocus == parentLinkOfAnchor) 1.2488 + bNodeFound = true; 1.2489 + } 1.2490 + 1.2491 + // We found a link node parent 1.2492 + if (bNodeFound) { 1.2493 + // GetElementOrParentByTagName addref'd this, so we don't need to do it here 1.2494 + *aReturn = parentLinkOfAnchor; 1.2495 + NS_IF_ADDREF(*aReturn); 1.2496 + return NS_OK; 1.2497 + } 1.2498 + } 1.2499 + else if (anchorOffset >= 0) // Check if link node is the only thing selected 1.2500 + { 1.2501 + nsCOMPtr<nsIDOMNode> anchorChild; 1.2502 + anchorChild = GetChildAt(anchorNode,anchorOffset); 1.2503 + if (anchorChild && nsHTMLEditUtils::IsLink(anchorChild) && 1.2504 + (anchorNode == focusNode) && focusOffset == (anchorOffset+1)) 1.2505 + { 1.2506 + selectedElement = do_QueryInterface(anchorChild); 1.2507 + bNodeFound = true; 1.2508 + } 1.2509 + } 1.2510 + } 1.2511 + } 1.2512 + 1.2513 + if (!isCollapsed) // Don't bother to examine selection if it is collapsed 1.2514 + { 1.2515 + nsRefPtr<nsRange> currange = sel->GetRangeAt(0); 1.2516 + if (currange) { 1.2517 + nsCOMPtr<nsIContentIterator> iter = 1.2518 + do_CreateInstance("@mozilla.org/content/post-content-iterator;1", &res); 1.2519 + NS_ENSURE_SUCCESS(res, res); 1.2520 + 1.2521 + iter->Init(currange); 1.2522 + // loop through the content iterator for each content node 1.2523 + while (!iter->IsDone()) 1.2524 + { 1.2525 + // Query interface to cast nsIContent to nsIDOMNode 1.2526 + // then get tagType to compare to aTagName 1.2527 + // Clone node of each desired type and append it to the aDomFrag 1.2528 + selectedElement = do_QueryInterface(iter->GetCurrentNode()); 1.2529 + if (selectedElement) 1.2530 + { 1.2531 + // If we already found a node, then we have another element, 1.2532 + // thus there's not just one element selected 1.2533 + if (bNodeFound) 1.2534 + { 1.2535 + bNodeFound = false; 1.2536 + break; 1.2537 + } 1.2538 + 1.2539 + selectedElement->GetNodeName(domTagName); 1.2540 + ToLowerCase(domTagName); 1.2541 + 1.2542 + if (anyTag) 1.2543 + { 1.2544 + // Get name of first selected element 1.2545 + selectedElement->GetTagName(TagName); 1.2546 + ToLowerCase(TagName); 1.2547 + anyTag = false; 1.2548 + } 1.2549 + 1.2550 + // The "A" tag is a pain, 1.2551 + // used for both link(href is set) and "Named Anchor" 1.2552 + nsCOMPtr<nsIDOMNode> selectedNode = do_QueryInterface(selectedElement); 1.2553 + if ( (isLinkTag && nsHTMLEditUtils::IsLink(selectedNode)) || 1.2554 + (isNamedAnchorTag && nsHTMLEditUtils::IsNamedAnchor(selectedNode)) ) 1.2555 + { 1.2556 + bNodeFound = true; 1.2557 + } else if (TagName == domTagName) { // All other tag names are handled here 1.2558 + bNodeFound = true; 1.2559 + } 1.2560 + if (!bNodeFound) 1.2561 + { 1.2562 + // Check if node we have is really part of the selection??? 1.2563 + break; 1.2564 + } 1.2565 + } 1.2566 + iter->Next(); 1.2567 + } 1.2568 + } else { 1.2569 + // Should never get here? 1.2570 + isCollapsed = true; 1.2571 + NS_WARNING("isCollapsed was FALSE, but no elements found in selection\n"); 1.2572 + } 1.2573 + } 1.2574 + } 1.2575 + if (bNodeFound) 1.2576 + { 1.2577 + 1.2578 + *aReturn = selectedElement; 1.2579 + if (selectedElement) 1.2580 + { 1.2581 + // Getters must addref 1.2582 + NS_ADDREF(*aReturn); 1.2583 + } 1.2584 + } 1.2585 + else res = NS_EDITOR_ELEMENT_NOT_FOUND; 1.2586 + 1.2587 + return res; 1.2588 +} 1.2589 + 1.2590 +NS_IMETHODIMP 1.2591 +nsHTMLEditor::CreateElementWithDefaults(const nsAString& aTagName, nsIDOMElement** aReturn) 1.2592 +{ 1.2593 + nsresult res=NS_ERROR_NOT_INITIALIZED; 1.2594 + if (aReturn) 1.2595 + *aReturn = nullptr; 1.2596 + 1.2597 +// NS_ENSURE_TRUE(aTagName && aReturn, NS_ERROR_NULL_POINTER); 1.2598 + NS_ENSURE_TRUE(!aTagName.IsEmpty() && aReturn, NS_ERROR_NULL_POINTER); 1.2599 + 1.2600 + nsAutoString TagName(aTagName); 1.2601 + ToLowerCase(TagName); 1.2602 + nsAutoString realTagName; 1.2603 + 1.2604 + if (IsLinkTag(TagName) || IsNamedAnchorTag(TagName)) 1.2605 + { 1.2606 + realTagName.AssignLiteral("a"); 1.2607 + } else { 1.2608 + realTagName = TagName; 1.2609 + } 1.2610 + //We don't use editor's CreateElement because we don't want to 1.2611 + // go through the transaction system 1.2612 + 1.2613 + nsCOMPtr<nsIDOMElement>newElement; 1.2614 + nsCOMPtr<dom::Element> newContent; 1.2615 + nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(mDocWeak); 1.2616 + NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED); 1.2617 + 1.2618 + //new call to use instead to get proper HTML element, bug# 39919 1.2619 + res = CreateHTMLContent(realTagName, getter_AddRefs(newContent)); 1.2620 + newElement = do_QueryInterface(newContent); 1.2621 + if (NS_FAILED(res) || !newElement) 1.2622 + return NS_ERROR_FAILURE; 1.2623 + 1.2624 + // Mark the new element dirty, so it will be formatted 1.2625 + newElement->SetAttribute(NS_LITERAL_STRING("_moz_dirty"), EmptyString()); 1.2626 + 1.2627 + // Set default values for new elements 1.2628 + if (TagName.EqualsLiteral("table")) { 1.2629 + res = newElement->SetAttribute(NS_LITERAL_STRING("cellpadding"),NS_LITERAL_STRING("2")); 1.2630 + NS_ENSURE_SUCCESS(res, res); 1.2631 + res = newElement->SetAttribute(NS_LITERAL_STRING("cellspacing"),NS_LITERAL_STRING("2")); 1.2632 + NS_ENSURE_SUCCESS(res, res); 1.2633 + res = newElement->SetAttribute(NS_LITERAL_STRING("border"),NS_LITERAL_STRING("1")); 1.2634 + } else if (TagName.EqualsLiteral("td")) 1.2635 + { 1.2636 + res = SetAttributeOrEquivalent(newElement, NS_LITERAL_STRING("valign"), 1.2637 + NS_LITERAL_STRING("top"), true); 1.2638 + } 1.2639 + // ADD OTHER TAGS HERE 1.2640 + 1.2641 + if (NS_SUCCEEDED(res)) 1.2642 + { 1.2643 + *aReturn = newElement; 1.2644 + // Getters must addref 1.2645 + NS_ADDREF(*aReturn); 1.2646 + } 1.2647 + 1.2648 + return res; 1.2649 +} 1.2650 + 1.2651 +NS_IMETHODIMP 1.2652 +nsHTMLEditor::InsertLinkAroundSelection(nsIDOMElement* aAnchorElement) 1.2653 +{ 1.2654 + NS_ENSURE_TRUE(aAnchorElement, NS_ERROR_NULL_POINTER); 1.2655 + 1.2656 + // We must have a real selection 1.2657 + nsCOMPtr<nsISelection> selection; 1.2658 + nsresult res = GetSelection(getter_AddRefs(selection)); 1.2659 + if (!selection) 1.2660 + { 1.2661 + res = NS_ERROR_NULL_POINTER; 1.2662 + } 1.2663 + NS_ENSURE_SUCCESS(res, res); 1.2664 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.2665 + 1.2666 + if (selection->Collapsed()) { 1.2667 + NS_WARNING("InsertLinkAroundSelection called but there is no selection!!!"); 1.2668 + return NS_OK; 1.2669 + } 1.2670 + 1.2671 + // Be sure we were given an anchor element 1.2672 + nsCOMPtr<nsIDOMHTMLAnchorElement> anchor = do_QueryInterface(aAnchorElement); 1.2673 + if (!anchor) { 1.2674 + return NS_OK; 1.2675 + } 1.2676 + 1.2677 + nsAutoString href; 1.2678 + res = anchor->GetHref(href); 1.2679 + NS_ENSURE_SUCCESS(res, res); 1.2680 + if (href.IsEmpty()) { 1.2681 + return NS_OK; 1.2682 + } 1.2683 + 1.2684 + nsAutoEditBatch beginBatching(this); 1.2685 + 1.2686 + // Set all attributes found on the supplied anchor element 1.2687 + nsCOMPtr<nsIDOMMozNamedAttrMap> attrMap; 1.2688 + aAnchorElement->GetAttributes(getter_AddRefs(attrMap)); 1.2689 + NS_ENSURE_TRUE(attrMap, NS_ERROR_FAILURE); 1.2690 + 1.2691 + uint32_t count; 1.2692 + attrMap->GetLength(&count); 1.2693 + nsAutoString name, value; 1.2694 + 1.2695 + for (uint32_t i = 0; i < count; ++i) { 1.2696 + nsCOMPtr<nsIDOMAttr> attribute; 1.2697 + res = attrMap->Item(i, getter_AddRefs(attribute)); 1.2698 + NS_ENSURE_SUCCESS(res, res); 1.2699 + 1.2700 + if (attribute) { 1.2701 + // We must clear the string buffers 1.2702 + // because GetName, GetValue appends to previous string! 1.2703 + name.Truncate(); 1.2704 + value.Truncate(); 1.2705 + 1.2706 + res = attribute->GetName(name); 1.2707 + NS_ENSURE_SUCCESS(res, res); 1.2708 + 1.2709 + res = attribute->GetValue(value); 1.2710 + NS_ENSURE_SUCCESS(res, res); 1.2711 + 1.2712 + res = SetInlineProperty(nsEditProperty::a, name, value); 1.2713 + NS_ENSURE_SUCCESS(res, res); 1.2714 + } 1.2715 + } 1.2716 + return NS_OK; 1.2717 +} 1.2718 + 1.2719 +NS_IMETHODIMP 1.2720 +nsHTMLEditor::SetHTMLBackgroundColor(const nsAString& aColor) 1.2721 +{ 1.2722 + NS_PRECONDITION(mDocWeak, "Missing Editor DOM Document"); 1.2723 + 1.2724 + // Find a selected or enclosing table element to set background on 1.2725 + nsCOMPtr<nsIDOMElement> element; 1.2726 + int32_t selectedCount; 1.2727 + nsAutoString tagName; 1.2728 + nsresult res = GetSelectedOrParentTableElement(tagName, &selectedCount, 1.2729 + getter_AddRefs(element)); 1.2730 + NS_ENSURE_SUCCESS(res, res); 1.2731 + 1.2732 + bool setColor = !aColor.IsEmpty(); 1.2733 + 1.2734 + NS_NAMED_LITERAL_STRING(bgcolor, "bgcolor"); 1.2735 + if (element) 1.2736 + { 1.2737 + if (selectedCount > 0) 1.2738 + { 1.2739 + // Traverse all selected cells 1.2740 + nsCOMPtr<nsIDOMElement> cell; 1.2741 + res = GetFirstSelectedCell(nullptr, getter_AddRefs(cell)); 1.2742 + if (NS_SUCCEEDED(res) && cell) 1.2743 + { 1.2744 + while(cell) 1.2745 + { 1.2746 + if (setColor) 1.2747 + res = SetAttribute(cell, bgcolor, aColor); 1.2748 + else 1.2749 + res = RemoveAttribute(cell, bgcolor); 1.2750 + if (NS_FAILED(res)) break; 1.2751 + 1.2752 + GetNextSelectedCell(nullptr, getter_AddRefs(cell)); 1.2753 + }; 1.2754 + return res; 1.2755 + } 1.2756 + } 1.2757 + // If we failed to find a cell, fall through to use originally-found element 1.2758 + } else { 1.2759 + // No table element -- set the background color on the body tag 1.2760 + element = do_QueryInterface(GetRoot()); 1.2761 + NS_ENSURE_TRUE(element, NS_ERROR_NULL_POINTER); 1.2762 + } 1.2763 + // Use the editor method that goes through the transaction system 1.2764 + if (setColor) 1.2765 + res = SetAttribute(element, bgcolor, aColor); 1.2766 + else 1.2767 + res = RemoveAttribute(element, bgcolor); 1.2768 + 1.2769 + return res; 1.2770 +} 1.2771 + 1.2772 +NS_IMETHODIMP nsHTMLEditor::SetBodyAttribute(const nsAString& aAttribute, const nsAString& aValue) 1.2773 +{ 1.2774 + // TODO: Check selection for Cell, Row, Column or table and do color on appropriate level 1.2775 + 1.2776 + NS_ASSERTION(mDocWeak, "Missing Editor DOM Document"); 1.2777 + 1.2778 + // Set the background color attribute on the body tag 1.2779 + nsCOMPtr<nsIDOMElement> bodyElement = do_QueryInterface(GetRoot()); 1.2780 + NS_ENSURE_TRUE(bodyElement, NS_ERROR_NULL_POINTER); 1.2781 + 1.2782 + // Use the editor method that goes through the transaction system 1.2783 + return SetAttribute(bodyElement, aAttribute, aValue); 1.2784 +} 1.2785 + 1.2786 +NS_IMETHODIMP 1.2787 +nsHTMLEditor::GetLinkedObjects(nsISupportsArray** aNodeList) 1.2788 +{ 1.2789 + NS_ENSURE_TRUE(aNodeList, NS_ERROR_NULL_POINTER); 1.2790 + 1.2791 + nsresult res; 1.2792 + 1.2793 + res = NS_NewISupportsArray(aNodeList); 1.2794 + NS_ENSURE_SUCCESS(res, res); 1.2795 + NS_ENSURE_TRUE(*aNodeList, NS_ERROR_NULL_POINTER); 1.2796 + 1.2797 + nsCOMPtr<nsIContentIterator> iter = 1.2798 + do_CreateInstance("@mozilla.org/content/post-content-iterator;1", &res); 1.2799 + NS_ENSURE_TRUE(iter, NS_ERROR_NULL_POINTER); 1.2800 + if ((NS_SUCCEEDED(res))) 1.2801 + { 1.2802 + nsCOMPtr<nsIDocument> doc = GetDocument(); 1.2803 + NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED); 1.2804 + 1.2805 + iter->Init(doc->GetRootElement()); 1.2806 + 1.2807 + // loop through the content iterator for each content node 1.2808 + while (!iter->IsDone()) 1.2809 + { 1.2810 + nsCOMPtr<nsIDOMNode> node (do_QueryInterface(iter->GetCurrentNode())); 1.2811 + if (node) 1.2812 + { 1.2813 + // Let nsURIRefObject make the hard decisions: 1.2814 + nsCOMPtr<nsIURIRefObject> refObject; 1.2815 + res = NS_NewHTMLURIRefObject(getter_AddRefs(refObject), node); 1.2816 + if (NS_SUCCEEDED(res)) 1.2817 + { 1.2818 + nsCOMPtr<nsISupports> isupp (do_QueryInterface(refObject)); 1.2819 + 1.2820 + (*aNodeList)->AppendElement(isupp); 1.2821 + } 1.2822 + } 1.2823 + iter->Next(); 1.2824 + } 1.2825 + } 1.2826 + 1.2827 + return NS_OK; 1.2828 +} 1.2829 + 1.2830 + 1.2831 +NS_IMETHODIMP 1.2832 +nsHTMLEditor::AddStyleSheet(const nsAString &aURL) 1.2833 +{ 1.2834 + // Enable existing sheet if already loaded. 1.2835 + if (EnableExistingStyleSheet(aURL)) 1.2836 + return NS_OK; 1.2837 + 1.2838 + // Lose the previously-loaded sheet so there's nothing to replace 1.2839 + // This pattern is different from Override methods because 1.2840 + // we must wait to remove mLastStyleSheetURL and add new sheet 1.2841 + // at the same time (in StyleSheetLoaded callback) so they are undoable together 1.2842 + mLastStyleSheetURL.Truncate(); 1.2843 + return ReplaceStyleSheet(aURL); 1.2844 +} 1.2845 + 1.2846 +NS_IMETHODIMP 1.2847 +nsHTMLEditor::ReplaceStyleSheet(const nsAString& aURL) 1.2848 +{ 1.2849 + // Enable existing sheet if already loaded. 1.2850 + if (EnableExistingStyleSheet(aURL)) 1.2851 + { 1.2852 + // Disable last sheet if not the same as new one 1.2853 + if (!mLastStyleSheetURL.IsEmpty() && !mLastStyleSheetURL.Equals(aURL)) 1.2854 + return EnableStyleSheet(mLastStyleSheetURL, false); 1.2855 + 1.2856 + return NS_OK; 1.2857 + } 1.2858 + 1.2859 + // Make sure the pres shell doesn't disappear during the load. 1.2860 + NS_ENSURE_TRUE(mDocWeak, NS_ERROR_NOT_INITIALIZED); 1.2861 + nsCOMPtr<nsIPresShell> ps = GetPresShell(); 1.2862 + NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED); 1.2863 + 1.2864 + nsCOMPtr<nsIURI> uaURI; 1.2865 + nsresult rv = NS_NewURI(getter_AddRefs(uaURI), aURL); 1.2866 + NS_ENSURE_SUCCESS(rv, rv); 1.2867 + 1.2868 + return ps->GetDocument()->CSSLoader()-> 1.2869 + LoadSheet(uaURI, nullptr, EmptyCString(), this); 1.2870 +} 1.2871 + 1.2872 +NS_IMETHODIMP 1.2873 +nsHTMLEditor::RemoveStyleSheet(const nsAString &aURL) 1.2874 +{ 1.2875 + nsRefPtr<nsCSSStyleSheet> sheet; 1.2876 + nsresult rv = GetStyleSheetForURL(aURL, getter_AddRefs(sheet)); 1.2877 + NS_ENSURE_SUCCESS(rv, rv); 1.2878 + NS_ENSURE_TRUE(sheet, NS_ERROR_UNEXPECTED); 1.2879 + 1.2880 + nsRefPtr<RemoveStyleSheetTxn> txn; 1.2881 + rv = CreateTxnForRemoveStyleSheet(sheet, getter_AddRefs(txn)); 1.2882 + if (!txn) rv = NS_ERROR_NULL_POINTER; 1.2883 + if (NS_SUCCEEDED(rv)) 1.2884 + { 1.2885 + rv = DoTransaction(txn); 1.2886 + if (NS_SUCCEEDED(rv)) 1.2887 + mLastStyleSheetURL.Truncate(); // forget it 1.2888 + 1.2889 + // Remove it from our internal list 1.2890 + rv = RemoveStyleSheetFromList(aURL); 1.2891 + } 1.2892 + 1.2893 + return rv; 1.2894 +} 1.2895 + 1.2896 + 1.2897 +NS_IMETHODIMP 1.2898 +nsHTMLEditor::AddOverrideStyleSheet(const nsAString& aURL) 1.2899 +{ 1.2900 + // Enable existing sheet if already loaded. 1.2901 + if (EnableExistingStyleSheet(aURL)) 1.2902 + return NS_OK; 1.2903 + 1.2904 + // Make sure the pres shell doesn't disappear during the load. 1.2905 + nsCOMPtr<nsIPresShell> ps = GetPresShell(); 1.2906 + NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED); 1.2907 + 1.2908 + nsCOMPtr<nsIURI> uaURI; 1.2909 + nsresult rv = NS_NewURI(getter_AddRefs(uaURI), aURL); 1.2910 + NS_ENSURE_SUCCESS(rv, rv); 1.2911 + 1.2912 + // We MUST ONLY load synchronous local files (no @import) 1.2913 + // XXXbz Except this will actually try to load remote files 1.2914 + // synchronously, of course.. 1.2915 + nsRefPtr<nsCSSStyleSheet> sheet; 1.2916 + // Editor override style sheets may want to style Gecko anonymous boxes 1.2917 + rv = ps->GetDocument()->CSSLoader()-> 1.2918 + LoadSheetSync(uaURI, true, true, getter_AddRefs(sheet)); 1.2919 + 1.2920 + // Synchronous loads should ALWAYS return completed 1.2921 + NS_ENSURE_TRUE(sheet, NS_ERROR_NULL_POINTER); 1.2922 + 1.2923 + // Add the override style sheet 1.2924 + // (This checks if already exists) 1.2925 + ps->AddOverrideStyleSheet(sheet); 1.2926 + 1.2927 + ps->ReconstructStyleData(); 1.2928 + 1.2929 + // Save as the last-loaded sheet 1.2930 + mLastOverrideStyleSheetURL = aURL; 1.2931 + 1.2932 + //Add URL and style sheet to our lists 1.2933 + return AddNewStyleSheetToList(aURL, sheet); 1.2934 +} 1.2935 + 1.2936 +NS_IMETHODIMP 1.2937 +nsHTMLEditor::ReplaceOverrideStyleSheet(const nsAString& aURL) 1.2938 +{ 1.2939 + // Enable existing sheet if already loaded. 1.2940 + if (EnableExistingStyleSheet(aURL)) 1.2941 + { 1.2942 + // Disable last sheet if not the same as new one 1.2943 + if (!mLastOverrideStyleSheetURL.IsEmpty() && !mLastOverrideStyleSheetURL.Equals(aURL)) 1.2944 + return EnableStyleSheet(mLastOverrideStyleSheetURL, false); 1.2945 + 1.2946 + return NS_OK; 1.2947 + } 1.2948 + // Remove the previous sheet 1.2949 + if (!mLastOverrideStyleSheetURL.IsEmpty()) 1.2950 + RemoveOverrideStyleSheet(mLastOverrideStyleSheetURL); 1.2951 + 1.2952 + return AddOverrideStyleSheet(aURL); 1.2953 +} 1.2954 + 1.2955 +// Do NOT use transaction system for override style sheets 1.2956 +NS_IMETHODIMP 1.2957 +nsHTMLEditor::RemoveOverrideStyleSheet(const nsAString &aURL) 1.2958 +{ 1.2959 + nsRefPtr<nsCSSStyleSheet> sheet; 1.2960 + GetStyleSheetForURL(aURL, getter_AddRefs(sheet)); 1.2961 + 1.2962 + // Make sure we remove the stylesheet from our internal list in all 1.2963 + // cases. 1.2964 + nsresult rv = RemoveStyleSheetFromList(aURL); 1.2965 + 1.2966 + NS_ENSURE_TRUE(sheet, NS_OK); /// Don't fail if sheet not found 1.2967 + 1.2968 + NS_ENSURE_TRUE(mDocWeak, NS_ERROR_NOT_INITIALIZED); 1.2969 + nsCOMPtr<nsIPresShell> ps = GetPresShell(); 1.2970 + NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED); 1.2971 + 1.2972 + ps->RemoveOverrideStyleSheet(sheet); 1.2973 + ps->ReconstructStyleData(); 1.2974 + 1.2975 + // Remove it from our internal list 1.2976 + return rv; 1.2977 +} 1.2978 + 1.2979 +NS_IMETHODIMP 1.2980 +nsHTMLEditor::EnableStyleSheet(const nsAString &aURL, bool aEnable) 1.2981 +{ 1.2982 + nsRefPtr<nsCSSStyleSheet> sheet; 1.2983 + nsresult rv = GetStyleSheetForURL(aURL, getter_AddRefs(sheet)); 1.2984 + NS_ENSURE_SUCCESS(rv, rv); 1.2985 + NS_ENSURE_TRUE(sheet, NS_OK); // Don't fail if sheet not found 1.2986 + 1.2987 + // Ensure the style sheet is owned by our document. 1.2988 + nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak); 1.2989 + sheet->SetOwningDocument(doc); 1.2990 + 1.2991 + return sheet->SetDisabled(!aEnable); 1.2992 +} 1.2993 + 1.2994 +bool 1.2995 +nsHTMLEditor::EnableExistingStyleSheet(const nsAString &aURL) 1.2996 +{ 1.2997 + nsRefPtr<nsCSSStyleSheet> sheet; 1.2998 + nsresult rv = GetStyleSheetForURL(aURL, getter_AddRefs(sheet)); 1.2999 + NS_ENSURE_SUCCESS(rv, false); 1.3000 + 1.3001 + // Enable sheet if already loaded. 1.3002 + if (sheet) 1.3003 + { 1.3004 + // Ensure the style sheet is owned by our document. 1.3005 + nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak); 1.3006 + sheet->SetOwningDocument(doc); 1.3007 + 1.3008 + sheet->SetDisabled(false); 1.3009 + return true; 1.3010 + } 1.3011 + return false; 1.3012 +} 1.3013 + 1.3014 +nsresult 1.3015 +nsHTMLEditor::AddNewStyleSheetToList(const nsAString &aURL, 1.3016 + nsCSSStyleSheet *aStyleSheet) 1.3017 +{ 1.3018 + uint32_t countSS = mStyleSheets.Length(); 1.3019 + uint32_t countU = mStyleSheetURLs.Length(); 1.3020 + 1.3021 + if (countSS != countU) 1.3022 + return NS_ERROR_UNEXPECTED; 1.3023 + 1.3024 + if (!mStyleSheetURLs.AppendElement(aURL)) 1.3025 + return NS_ERROR_UNEXPECTED; 1.3026 + 1.3027 + return mStyleSheets.AppendElement(aStyleSheet) ? NS_OK : NS_ERROR_UNEXPECTED; 1.3028 +} 1.3029 + 1.3030 +nsresult 1.3031 +nsHTMLEditor::RemoveStyleSheetFromList(const nsAString &aURL) 1.3032 +{ 1.3033 + // is it already in the list? 1.3034 + uint32_t foundIndex; 1.3035 + foundIndex = mStyleSheetURLs.IndexOf(aURL); 1.3036 + if (foundIndex == mStyleSheetURLs.NoIndex) 1.3037 + return NS_ERROR_FAILURE; 1.3038 + 1.3039 + // Attempt both removals; if one fails there's not much we can do. 1.3040 + mStyleSheets.RemoveElementAt(foundIndex); 1.3041 + mStyleSheetURLs.RemoveElementAt(foundIndex); 1.3042 + 1.3043 + return NS_OK; 1.3044 +} 1.3045 + 1.3046 +NS_IMETHODIMP 1.3047 +nsHTMLEditor::GetStyleSheetForURL(const nsAString &aURL, 1.3048 + nsCSSStyleSheet **aStyleSheet) 1.3049 +{ 1.3050 + NS_ENSURE_ARG_POINTER(aStyleSheet); 1.3051 + *aStyleSheet = 0; 1.3052 + 1.3053 + // is it already in the list? 1.3054 + uint32_t foundIndex; 1.3055 + foundIndex = mStyleSheetURLs.IndexOf(aURL); 1.3056 + if (foundIndex == mStyleSheetURLs.NoIndex) 1.3057 + return NS_OK; //No sheet -- don't fail! 1.3058 + 1.3059 + *aStyleSheet = mStyleSheets[foundIndex]; 1.3060 + NS_ENSURE_TRUE(*aStyleSheet, NS_ERROR_FAILURE); 1.3061 + 1.3062 + NS_ADDREF(*aStyleSheet); 1.3063 + 1.3064 + return NS_OK; 1.3065 +} 1.3066 + 1.3067 +NS_IMETHODIMP 1.3068 +nsHTMLEditor::GetURLForStyleSheet(nsCSSStyleSheet *aStyleSheet, 1.3069 + nsAString &aURL) 1.3070 +{ 1.3071 + // is it already in the list? 1.3072 + int32_t foundIndex = mStyleSheets.IndexOf(aStyleSheet); 1.3073 + 1.3074 + // Don't fail if we don't find it in our list 1.3075 + // Note: mStyleSheets is nsCOMArray, so its IndexOf() method 1.3076 + // returns -1 on failure. 1.3077 + if (foundIndex == -1) 1.3078 + return NS_OK; 1.3079 + 1.3080 + // Found it in the list! 1.3081 + aURL = mStyleSheetURLs[foundIndex]; 1.3082 + return NS_OK; 1.3083 +} 1.3084 + 1.3085 +/* 1.3086 + * nsIEditorMailSupport methods 1.3087 + */ 1.3088 + 1.3089 +NS_IMETHODIMP 1.3090 +nsHTMLEditor::GetEmbeddedObjects(nsISupportsArray** aNodeList) 1.3091 +{ 1.3092 + NS_ENSURE_TRUE(aNodeList, NS_ERROR_NULL_POINTER); 1.3093 + 1.3094 + nsresult rv = NS_NewISupportsArray(aNodeList); 1.3095 + NS_ENSURE_SUCCESS(rv, rv); 1.3096 + NS_ENSURE_TRUE(*aNodeList, NS_ERROR_NULL_POINTER); 1.3097 + 1.3098 + nsCOMPtr<nsIContentIterator> iter = 1.3099 + do_CreateInstance("@mozilla.org/content/post-content-iterator;1", &rv); 1.3100 + NS_ENSURE_TRUE(iter, NS_ERROR_NULL_POINTER); 1.3101 + NS_ENSURE_SUCCESS(rv, rv); 1.3102 + 1.3103 + nsCOMPtr<nsIDocument> doc = GetDocument(); 1.3104 + NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED); 1.3105 + 1.3106 + iter->Init(doc->GetRootElement()); 1.3107 + 1.3108 + // Loop through the content iterator for each content node. 1.3109 + while (!iter->IsDone()) { 1.3110 + nsINode* node = iter->GetCurrentNode(); 1.3111 + if (node->IsElement()) { 1.3112 + dom::Element* element = node->AsElement(); 1.3113 + 1.3114 + // See if it's an image or an embed and also include all links. 1.3115 + // Let mail decide which link to send or not 1.3116 + if (element->IsHTML(nsGkAtoms::img) || 1.3117 + element->IsHTML(nsGkAtoms::embed) || 1.3118 + element->IsHTML(nsGkAtoms::a) || 1.3119 + (element->IsHTML(nsGkAtoms::body) && 1.3120 + element->HasAttr(kNameSpaceID_None, nsGkAtoms::background))) { 1.3121 + nsCOMPtr<nsIDOMNode> domNode = do_QueryInterface(node); 1.3122 + (*aNodeList)->AppendElement(domNode); 1.3123 + } 1.3124 + } 1.3125 + iter->Next(); 1.3126 + } 1.3127 + 1.3128 + return rv; 1.3129 +} 1.3130 + 1.3131 + 1.3132 +NS_IMETHODIMP 1.3133 +nsHTMLEditor::DeleteSelectionImpl(EDirection aAction, 1.3134 + EStripWrappers aStripWrappers) 1.3135 +{ 1.3136 + MOZ_ASSERT(aStripWrappers == eStrip || aStripWrappers == eNoStrip); 1.3137 + 1.3138 + nsresult res = nsEditor::DeleteSelectionImpl(aAction, aStripWrappers); 1.3139 + NS_ENSURE_SUCCESS(res, res); 1.3140 + 1.3141 + // If we weren't asked to strip any wrappers, we're done. 1.3142 + if (aStripWrappers == eNoStrip) { 1.3143 + return NS_OK; 1.3144 + } 1.3145 + 1.3146 + nsRefPtr<Selection> selection = GetSelection(); 1.3147 + // Just checking that the selection itself is collapsed doesn't seem to work 1.3148 + // right in the multi-range case 1.3149 + NS_ENSURE_STATE(selection); 1.3150 + NS_ENSURE_STATE(selection->GetAnchorFocusRange()); 1.3151 + NS_ENSURE_STATE(selection->GetAnchorFocusRange()->Collapsed()); 1.3152 + 1.3153 + NS_ENSURE_STATE(selection->GetAnchorNode()->IsContent()); 1.3154 + nsCOMPtr<nsIContent> content = selection->GetAnchorNode()->AsContent(); 1.3155 + 1.3156 + // Don't strip wrappers if this is the only wrapper in the block. Then we'll 1.3157 + // add a <br> later, so it won't be an empty wrapper in the end. 1.3158 + nsCOMPtr<nsIContent> blockParent = content; 1.3159 + while (blockParent && !IsBlockNode(blockParent)) { 1.3160 + blockParent = blockParent->GetParent(); 1.3161 + } 1.3162 + if (!blockParent) { 1.3163 + return NS_OK; 1.3164 + } 1.3165 + bool emptyBlockParent; 1.3166 + res = IsEmptyNode(blockParent, &emptyBlockParent); 1.3167 + NS_ENSURE_SUCCESS(res, res); 1.3168 + if (emptyBlockParent) { 1.3169 + return NS_OK; 1.3170 + } 1.3171 + 1.3172 + if (content && !IsBlockNode(content) && !content->Length() && 1.3173 + content->IsEditable() && content != content->GetEditingHost()) { 1.3174 + while (content->GetParent() && !IsBlockNode(content->GetParent()) && 1.3175 + content->GetParent()->Length() == 1 && 1.3176 + content->GetParent()->IsEditable() && 1.3177 + content->GetParent() != content->GetEditingHost()) { 1.3178 + content = content->GetParent(); 1.3179 + } 1.3180 + res = DeleteNode(content); 1.3181 + NS_ENSURE_SUCCESS(res, res); 1.3182 + } 1.3183 + 1.3184 + return NS_OK; 1.3185 +} 1.3186 + 1.3187 + 1.3188 +nsresult 1.3189 +nsHTMLEditor::DeleteNode(nsINode* aNode) 1.3190 +{ 1.3191 + nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode); 1.3192 + return DeleteNode(node); 1.3193 +} 1.3194 + 1.3195 +NS_IMETHODIMP 1.3196 +nsHTMLEditor::DeleteNode(nsIDOMNode* aNode) 1.3197 +{ 1.3198 + // do nothing if the node is read-only 1.3199 + nsCOMPtr<nsIContent> content = do_QueryInterface(aNode); 1.3200 + if (!IsModifiableNode(aNode) && !IsMozEditorBogusNode(content)) { 1.3201 + return NS_ERROR_FAILURE; 1.3202 + } 1.3203 + 1.3204 + return nsEditor::DeleteNode(aNode); 1.3205 +} 1.3206 + 1.3207 +NS_IMETHODIMP nsHTMLEditor::DeleteText(nsIDOMCharacterData *aTextNode, 1.3208 + uint32_t aOffset, 1.3209 + uint32_t aLength) 1.3210 +{ 1.3211 + // do nothing if the node is read-only 1.3212 + if (!IsModifiableNode(aTextNode)) { 1.3213 + return NS_ERROR_FAILURE; 1.3214 + } 1.3215 + 1.3216 + return nsEditor::DeleteText(aTextNode, aOffset, aLength); 1.3217 +} 1.3218 + 1.3219 +NS_IMETHODIMP nsHTMLEditor::InsertTextImpl(const nsAString& aStringToInsert, 1.3220 + nsCOMPtr<nsIDOMNode> *aInOutNode, 1.3221 + int32_t *aInOutOffset, 1.3222 + nsIDOMDocument *aDoc) 1.3223 +{ 1.3224 + // do nothing if the node is read-only 1.3225 + if (!IsModifiableNode(*aInOutNode)) { 1.3226 + return NS_ERROR_FAILURE; 1.3227 + } 1.3228 + 1.3229 + return nsEditor::InsertTextImpl(aStringToInsert, aInOutNode, aInOutOffset, aDoc); 1.3230 +} 1.3231 + 1.3232 +void 1.3233 +nsHTMLEditor::ContentAppended(nsIDocument *aDocument, nsIContent* aContainer, 1.3234 + nsIContent* aFirstNewContent, 1.3235 + int32_t aIndexInContainer) 1.3236 +{ 1.3237 + DoContentInserted(aDocument, aContainer, aFirstNewContent, aIndexInContainer, 1.3238 + eAppended); 1.3239 +} 1.3240 + 1.3241 +void 1.3242 +nsHTMLEditor::ContentInserted(nsIDocument *aDocument, nsIContent* aContainer, 1.3243 + nsIContent* aChild, int32_t aIndexInContainer) 1.3244 +{ 1.3245 + DoContentInserted(aDocument, aContainer, aChild, aIndexInContainer, 1.3246 + eInserted); 1.3247 +} 1.3248 + 1.3249 +void 1.3250 +nsHTMLEditor::DoContentInserted(nsIDocument* aDocument, nsIContent* aContainer, 1.3251 + nsIContent* aChild, int32_t aIndexInContainer, 1.3252 + InsertedOrAppended aInsertedOrAppended) 1.3253 +{ 1.3254 + if (!aChild) { 1.3255 + return; 1.3256 + } 1.3257 + 1.3258 + nsCOMPtr<nsIHTMLEditor> kungFuDeathGrip(this); 1.3259 + 1.3260 + if (ShouldReplaceRootElement()) { 1.3261 + nsContentUtils::AddScriptRunner(NS_NewRunnableMethod( 1.3262 + this, &nsHTMLEditor::ResetRootElementAndEventTarget)); 1.3263 + } 1.3264 + // We don't need to handle our own modifications 1.3265 + else if (!mAction && (aContainer ? aContainer->IsEditable() : aDocument->IsEditable())) { 1.3266 + if (IsMozEditorBogusNode(aChild)) { 1.3267 + // Ignore insertion of the bogus node 1.3268 + return; 1.3269 + } 1.3270 + // Protect the edit rules object from dying 1.3271 + nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules); 1.3272 + mRules->DocumentModified(); 1.3273 + 1.3274 + // Update spellcheck for only the newly-inserted node (bug 743819) 1.3275 + if (mInlineSpellChecker) { 1.3276 + nsRefPtr<nsRange> range = new nsRange(aChild); 1.3277 + int32_t endIndex = aIndexInContainer + 1; 1.3278 + if (aInsertedOrAppended == eAppended) { 1.3279 + // Count all the appended nodes 1.3280 + nsIContent* sibling = aChild->GetNextSibling(); 1.3281 + while (sibling) { 1.3282 + endIndex++; 1.3283 + sibling = sibling->GetNextSibling(); 1.3284 + } 1.3285 + } 1.3286 + nsresult res = range->Set(aContainer, aIndexInContainer, 1.3287 + aContainer, endIndex); 1.3288 + if (NS_SUCCEEDED(res)) { 1.3289 + mInlineSpellChecker->SpellCheckRange(range); 1.3290 + } 1.3291 + } 1.3292 + } 1.3293 +} 1.3294 + 1.3295 +void 1.3296 +nsHTMLEditor::ContentRemoved(nsIDocument *aDocument, nsIContent* aContainer, 1.3297 + nsIContent* aChild, int32_t aIndexInContainer, 1.3298 + nsIContent* aPreviousSibling) 1.3299 +{ 1.3300 + nsCOMPtr<nsIHTMLEditor> kungFuDeathGrip(this); 1.3301 + 1.3302 + if (SameCOMIdentity(aChild, mRootElement)) { 1.3303 + nsContentUtils::AddScriptRunner(NS_NewRunnableMethod( 1.3304 + this, &nsHTMLEditor::ResetRootElementAndEventTarget)); 1.3305 + } 1.3306 + // We don't need to handle our own modifications 1.3307 + else if (!mAction && (aContainer ? aContainer->IsEditable() : aDocument->IsEditable())) { 1.3308 + if (aChild && IsMozEditorBogusNode(aChild)) { 1.3309 + // Ignore removal of the bogus node 1.3310 + return; 1.3311 + } 1.3312 + // Protect the edit rules object from dying 1.3313 + nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules); 1.3314 + mRules->DocumentModified(); 1.3315 + } 1.3316 +} 1.3317 + 1.3318 +NS_IMETHODIMP_(bool) 1.3319 +nsHTMLEditor::IsModifiableNode(nsIDOMNode *aNode) 1.3320 +{ 1.3321 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.3322 + return IsModifiableNode(node); 1.3323 +} 1.3324 + 1.3325 +bool 1.3326 +nsHTMLEditor::IsModifiableNode(nsINode *aNode) 1.3327 +{ 1.3328 + return !aNode || aNode->IsEditable(); 1.3329 +} 1.3330 + 1.3331 +NS_IMETHODIMP 1.3332 +nsHTMLEditor::GetIsSelectionEditable(bool* aIsSelectionEditable) 1.3333 +{ 1.3334 + MOZ_ASSERT(aIsSelectionEditable); 1.3335 + 1.3336 + nsRefPtr<Selection> selection = GetSelection(); 1.3337 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.3338 + 1.3339 + // Per the editing spec as of June 2012: we have to have a selection whose 1.3340 + // start and end nodes are editable, and which share an ancestor editing 1.3341 + // host. (Bug 766387.) 1.3342 + *aIsSelectionEditable = selection->GetRangeCount() && 1.3343 + selection->GetAnchorNode()->IsEditable() && 1.3344 + selection->GetFocusNode()->IsEditable(); 1.3345 + 1.3346 + if (*aIsSelectionEditable) { 1.3347 + nsINode* commonAncestor = 1.3348 + selection->GetAnchorFocusRange()->GetCommonAncestor(); 1.3349 + while (commonAncestor && !commonAncestor->IsEditable()) { 1.3350 + commonAncestor = commonAncestor->GetParentNode(); 1.3351 + } 1.3352 + if (!commonAncestor) { 1.3353 + // No editable common ancestor 1.3354 + *aIsSelectionEditable = false; 1.3355 + } 1.3356 + } 1.3357 + 1.3358 + return NS_OK; 1.3359 +} 1.3360 + 1.3361 +static nsresult 1.3362 +SetSelectionAroundHeadChildren(nsISelection* aSelection, 1.3363 + nsIWeakReference* aDocWeak) 1.3364 +{ 1.3365 + // Set selection around <head> node 1.3366 + nsCOMPtr<nsIDocument> doc = do_QueryReferent(aDocWeak); 1.3367 + NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED); 1.3368 + 1.3369 + dom::Element* headNode = doc->GetHeadElement(); 1.3370 + NS_ENSURE_STATE(headNode); 1.3371 + 1.3372 + // Collapse selection to before first child of the head, 1.3373 + nsresult rv = aSelection->CollapseNative(headNode, 0); 1.3374 + NS_ENSURE_SUCCESS(rv, rv); 1.3375 + 1.3376 + // Then extend it to just after. 1.3377 + uint32_t childCount = headNode->GetChildCount(); 1.3378 + return aSelection->ExtendNative(headNode, childCount + 1); 1.3379 +} 1.3380 + 1.3381 +NS_IMETHODIMP 1.3382 +nsHTMLEditor::GetHeadContentsAsHTML(nsAString& aOutputString) 1.3383 +{ 1.3384 + nsRefPtr<Selection> selection = GetSelection(); 1.3385 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.3386 + 1.3387 + // Save current selection 1.3388 + nsAutoSelectionReset selectionResetter(selection, this); 1.3389 + 1.3390 + nsresult res = SetSelectionAroundHeadChildren(selection, mDocWeak); 1.3391 + NS_ENSURE_SUCCESS(res, res); 1.3392 + 1.3393 + res = OutputToString(NS_LITERAL_STRING("text/html"), 1.3394 + nsIDocumentEncoder::OutputSelectionOnly, 1.3395 + aOutputString); 1.3396 + if (NS_SUCCEEDED(res)) 1.3397 + { 1.3398 + // Selection always includes <body></body>, 1.3399 + // so terminate there 1.3400 + nsReadingIterator<char16_t> findIter,endFindIter; 1.3401 + aOutputString.BeginReading(findIter); 1.3402 + aOutputString.EndReading(endFindIter); 1.3403 + //counting on our parser to always lower case!!! 1.3404 + if (CaseInsensitiveFindInReadable(NS_LITERAL_STRING("<body"), 1.3405 + findIter, endFindIter)) 1.3406 + { 1.3407 + nsReadingIterator<char16_t> beginIter; 1.3408 + aOutputString.BeginReading(beginIter); 1.3409 + int32_t offset = Distance(beginIter, findIter);//get the distance 1.3410 + 1.3411 + nsWritingIterator<char16_t> writeIter; 1.3412 + aOutputString.BeginWriting(writeIter); 1.3413 + // Ensure the string ends in a newline 1.3414 + char16_t newline ('\n'); 1.3415 + findIter.advance(-1); 1.3416 + if (offset ==0 || (offset >0 && (*findIter) != newline)) //check for 0 1.3417 + { 1.3418 + writeIter.advance(offset); 1.3419 + *writeIter = newline; 1.3420 + aOutputString.Truncate(offset+1); 1.3421 + } 1.3422 + } 1.3423 + } 1.3424 + return res; 1.3425 +} 1.3426 + 1.3427 +NS_IMETHODIMP 1.3428 +nsHTMLEditor::DebugUnitTests(int32_t *outNumTests, int32_t *outNumTestsFailed) 1.3429 +{ 1.3430 +#ifdef DEBUG 1.3431 + NS_ENSURE_TRUE(outNumTests && outNumTestsFailed, NS_ERROR_NULL_POINTER); 1.3432 + 1.3433 + TextEditorTest *tester = new TextEditorTest(); 1.3434 + NS_ENSURE_TRUE(tester, NS_ERROR_OUT_OF_MEMORY); 1.3435 + 1.3436 + tester->Run(this, outNumTests, outNumTestsFailed); 1.3437 + delete tester; 1.3438 + return NS_OK; 1.3439 +#else 1.3440 + return NS_ERROR_NOT_IMPLEMENTED; 1.3441 +#endif 1.3442 +} 1.3443 + 1.3444 + 1.3445 +NS_IMETHODIMP 1.3446 +nsHTMLEditor::StyleSheetLoaded(nsCSSStyleSheet* aSheet, bool aWasAlternate, 1.3447 + nsresult aStatus) 1.3448 +{ 1.3449 + nsresult rv = NS_OK; 1.3450 + nsAutoEditBatch batchIt(this); 1.3451 + 1.3452 + if (!mLastStyleSheetURL.IsEmpty()) 1.3453 + RemoveStyleSheet(mLastStyleSheetURL); 1.3454 + 1.3455 + nsRefPtr<AddStyleSheetTxn> txn; 1.3456 + rv = CreateTxnForAddStyleSheet(aSheet, getter_AddRefs(txn)); 1.3457 + if (!txn) rv = NS_ERROR_NULL_POINTER; 1.3458 + if (NS_SUCCEEDED(rv)) 1.3459 + { 1.3460 + rv = DoTransaction(txn); 1.3461 + if (NS_SUCCEEDED(rv)) 1.3462 + { 1.3463 + // Get the URI, then url spec from the sheet 1.3464 + nsAutoCString spec; 1.3465 + rv = aSheet->GetSheetURI()->GetSpec(spec); 1.3466 + 1.3467 + if (NS_SUCCEEDED(rv)) 1.3468 + { 1.3469 + // Save it so we can remove before applying the next one 1.3470 + mLastStyleSheetURL.AssignWithConversion(spec.get()); 1.3471 + 1.3472 + // Also save in our arrays of urls and sheets 1.3473 + AddNewStyleSheetToList(mLastStyleSheetURL, aSheet); 1.3474 + } 1.3475 + } 1.3476 + } 1.3477 + 1.3478 + return NS_OK; 1.3479 +} 1.3480 + 1.3481 + 1.3482 +/** All editor operations which alter the doc should be prefaced 1.3483 + * with a call to StartOperation, naming the action and direction */ 1.3484 +NS_IMETHODIMP 1.3485 +nsHTMLEditor::StartOperation(EditAction opID, 1.3486 + nsIEditor::EDirection aDirection) 1.3487 +{ 1.3488 + // Protect the edit rules object from dying 1.3489 + nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules); 1.3490 + 1.3491 + nsEditor::StartOperation(opID, aDirection); // will set mAction, mDirection 1.3492 + if (mRules) return mRules->BeforeEdit(mAction, mDirection); 1.3493 + return NS_OK; 1.3494 +} 1.3495 + 1.3496 + 1.3497 +/** All editor operations which alter the doc should be followed 1.3498 + * with a call to EndOperation */ 1.3499 +NS_IMETHODIMP 1.3500 +nsHTMLEditor::EndOperation() 1.3501 +{ 1.3502 + // Protect the edit rules object from dying 1.3503 + nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules); 1.3504 + 1.3505 + // post processing 1.3506 + nsresult res = NS_OK; 1.3507 + if (mRules) res = mRules->AfterEdit(mAction, mDirection); 1.3508 + nsEditor::EndOperation(); // will clear mAction, mDirection 1.3509 + return res; 1.3510 +} 1.3511 + 1.3512 +bool 1.3513 +nsHTMLEditor::TagCanContainTag(nsIAtom* aParentTag, nsIAtom* aChildTag) 1.3514 +{ 1.3515 + MOZ_ASSERT(aParentTag && aChildTag); 1.3516 + 1.3517 + nsIParserService* parserService = nsContentUtils::GetParserService(); 1.3518 + 1.3519 + int32_t childTagEnum; 1.3520 + // XXX Should this handle #cdata-section too? 1.3521 + if (aChildTag == nsGkAtoms::textTagName) { 1.3522 + childTagEnum = eHTMLTag_text; 1.3523 + } else { 1.3524 + childTagEnum = parserService->HTMLAtomTagToId(aChildTag); 1.3525 + } 1.3526 + 1.3527 + int32_t parentTagEnum = parserService->HTMLAtomTagToId(aParentTag); 1.3528 + return nsHTMLEditUtils::CanContain(parentTagEnum, childTagEnum); 1.3529 +} 1.3530 + 1.3531 +bool 1.3532 +nsHTMLEditor::IsContainer(nsIDOMNode *aNode) 1.3533 +{ 1.3534 + if (!aNode) { 1.3535 + return false; 1.3536 + } 1.3537 + 1.3538 + nsAutoString stringTag; 1.3539 + 1.3540 + nsresult rv = aNode->GetNodeName(stringTag); 1.3541 + NS_ENSURE_SUCCESS(rv, false); 1.3542 + 1.3543 + int32_t tagEnum; 1.3544 + // XXX Should this handle #cdata-section too? 1.3545 + if (stringTag.EqualsLiteral("#text")) { 1.3546 + tagEnum = eHTMLTag_text; 1.3547 + } 1.3548 + else { 1.3549 + tagEnum = nsContentUtils::GetParserService()->HTMLStringTagToId(stringTag); 1.3550 + } 1.3551 + 1.3552 + return nsHTMLEditUtils::IsContainer(tagEnum); 1.3553 +} 1.3554 + 1.3555 + 1.3556 +NS_IMETHODIMP 1.3557 +nsHTMLEditor::SelectEntireDocument(nsISelection *aSelection) 1.3558 +{ 1.3559 + if (!aSelection || !mRules) { return NS_ERROR_NULL_POINTER; } 1.3560 + 1.3561 + // Protect the edit rules object from dying 1.3562 + nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules); 1.3563 + 1.3564 + // get editor root node 1.3565 + nsCOMPtr<nsIDOMElement> rootElement = do_QueryInterface(GetRoot()); 1.3566 + 1.3567 + // is doc empty? 1.3568 + bool bDocIsEmpty; 1.3569 + nsresult res = mRules->DocumentIsEmpty(&bDocIsEmpty); 1.3570 + NS_ENSURE_SUCCESS(res, res); 1.3571 + 1.3572 + if (bDocIsEmpty) 1.3573 + { 1.3574 + // if its empty dont select entire doc - that would select the bogus node 1.3575 + return aSelection->Collapse(rootElement, 0); 1.3576 + } 1.3577 + 1.3578 + return nsEditor::SelectEntireDocument(aSelection); 1.3579 +} 1.3580 + 1.3581 +NS_IMETHODIMP 1.3582 +nsHTMLEditor::SelectAll() 1.3583 +{ 1.3584 + ForceCompositionEnd(); 1.3585 + 1.3586 + nsresult rv; 1.3587 + nsCOMPtr<nsISelectionController> selCon; 1.3588 + rv = GetSelectionController(getter_AddRefs(selCon)); 1.3589 + NS_ENSURE_SUCCESS(rv, rv); 1.3590 + 1.3591 + nsCOMPtr<nsISelection> selection; 1.3592 + rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, 1.3593 + getter_AddRefs(selection)); 1.3594 + NS_ENSURE_SUCCESS(rv, rv); 1.3595 + 1.3596 + nsCOMPtr<nsIDOMNode> anchorNode; 1.3597 + rv = selection->GetAnchorNode(getter_AddRefs(anchorNode)); 1.3598 + NS_ENSURE_SUCCESS(rv, rv); 1.3599 + 1.3600 + nsCOMPtr<nsIContent> anchorContent = do_QueryInterface(anchorNode, &rv); 1.3601 + NS_ENSURE_SUCCESS(rv, rv); 1.3602 + 1.3603 + // If the anchor content has independent selection, we never need to explicitly 1.3604 + // select its children. 1.3605 + if (anchorContent->HasIndependentSelection()) { 1.3606 + nsCOMPtr<nsISelectionPrivate> selPriv = do_QueryInterface(selection); 1.3607 + NS_ENSURE_TRUE(selPriv, NS_ERROR_UNEXPECTED); 1.3608 + rv = selPriv->SetAncestorLimiter(nullptr); 1.3609 + NS_ENSURE_SUCCESS(rv, rv); 1.3610 + nsCOMPtr<nsIDOMNode> rootElement = do_QueryInterface(mRootElement, &rv); 1.3611 + NS_ENSURE_SUCCESS(rv, rv); 1.3612 + return selection->SelectAllChildren(rootElement); 1.3613 + } 1.3614 + 1.3615 + nsCOMPtr<nsIPresShell> ps = GetPresShell(); 1.3616 + nsIContent *rootContent = anchorContent->GetSelectionRootContent(ps); 1.3617 + NS_ENSURE_TRUE(rootContent, NS_ERROR_UNEXPECTED); 1.3618 + 1.3619 + nsCOMPtr<nsIDOMNode> rootElement = do_QueryInterface(rootContent, &rv); 1.3620 + NS_ENSURE_SUCCESS(rv, rv); 1.3621 + 1.3622 + return selection->SelectAllChildren(rootElement); 1.3623 +} 1.3624 + 1.3625 + 1.3626 +// this will NOT find aAttribute unless aAttribute has a non-null value 1.3627 +// so singleton attributes like <Table border> will not be matched! 1.3628 +bool nsHTMLEditor::IsTextPropertySetByContent(nsIContent* aContent, 1.3629 + nsIAtom* aProperty, 1.3630 + const nsAString* aAttribute, 1.3631 + const nsAString* aValue, 1.3632 + nsAString* outValue) 1.3633 +{ 1.3634 + MOZ_ASSERT(aContent && aProperty); 1.3635 + MOZ_ASSERT_IF(aAttribute, aValue); 1.3636 + bool isSet; 1.3637 + IsTextPropertySetByContent(aContent->AsDOMNode(), aProperty, aAttribute, 1.3638 + aValue, isSet, outValue); 1.3639 + return isSet; 1.3640 +} 1.3641 + 1.3642 +void nsHTMLEditor::IsTextPropertySetByContent(nsIDOMNode *aNode, 1.3643 + nsIAtom *aProperty, 1.3644 + const nsAString *aAttribute, 1.3645 + const nsAString *aValue, 1.3646 + bool &aIsSet, 1.3647 + nsAString *outValue) 1.3648 +{ 1.3649 + nsresult result; 1.3650 + aIsSet = false; // must be initialized to false for code below to work 1.3651 + nsAutoString propName; 1.3652 + aProperty->ToString(propName); 1.3653 + nsCOMPtr<nsIDOMNode>node = aNode; 1.3654 + 1.3655 + while (node) 1.3656 + { 1.3657 + nsCOMPtr<nsIDOMElement>element; 1.3658 + element = do_QueryInterface(node); 1.3659 + if (element) 1.3660 + { 1.3661 + nsAutoString tag, value; 1.3662 + element->GetTagName(tag); 1.3663 + if (propName.Equals(tag, nsCaseInsensitiveStringComparator())) 1.3664 + { 1.3665 + bool found = false; 1.3666 + if (aAttribute && 0!=aAttribute->Length()) 1.3667 + { 1.3668 + element->GetAttribute(*aAttribute, value); 1.3669 + if (outValue) *outValue = value; 1.3670 + if (!value.IsEmpty()) 1.3671 + { 1.3672 + if (!aValue) { 1.3673 + found = true; 1.3674 + } 1.3675 + else 1.3676 + { 1.3677 + nsString tString(*aValue); 1.3678 + if (tString.Equals(value, nsCaseInsensitiveStringComparator())) { 1.3679 + found = true; 1.3680 + } 1.3681 + else { // we found the prop with the attribute, but the value doesn't match 1.3682 + break; 1.3683 + } 1.3684 + } 1.3685 + } 1.3686 + } 1.3687 + else { 1.3688 + found = true; 1.3689 + } 1.3690 + if (found) 1.3691 + { 1.3692 + aIsSet = true; 1.3693 + break; 1.3694 + } 1.3695 + } 1.3696 + } 1.3697 + nsCOMPtr<nsIDOMNode>temp; 1.3698 + result = node->GetParentNode(getter_AddRefs(temp)); 1.3699 + if (NS_SUCCEEDED(result) && temp) { 1.3700 + node = temp; 1.3701 + } 1.3702 + else { 1.3703 + node = nullptr; 1.3704 + } 1.3705 + } 1.3706 +} 1.3707 + 1.3708 + 1.3709 +//================================================================ 1.3710 +// HTML Editor methods 1.3711 +// 1.3712 +// Note: Table Editing methods are implemented in nsTableEditor.cpp 1.3713 +// 1.3714 + 1.3715 + 1.3716 +bool 1.3717 +nsHTMLEditor::SetCaretInTableCell(nsIDOMElement* aElement) 1.3718 +{ 1.3719 + nsCOMPtr<dom::Element> element = do_QueryInterface(aElement); 1.3720 + if (!element || !element->IsHTML() || 1.3721 + !nsHTMLEditUtils::IsTableElement(element) || 1.3722 + !IsDescendantOfEditorRoot(element)) { 1.3723 + return false; 1.3724 + } 1.3725 + 1.3726 + nsIContent* node = element; 1.3727 + while (node->HasChildren()) { 1.3728 + node = node->GetFirstChild(); 1.3729 + } 1.3730 + 1.3731 + // Set selection at beginning of the found node 1.3732 + nsCOMPtr<nsISelection> selection; 1.3733 + nsresult rv = GetSelection(getter_AddRefs(selection)); 1.3734 + NS_ENSURE_SUCCESS(rv, false); 1.3735 + NS_ENSURE_TRUE(selection, false); 1.3736 + 1.3737 + return NS_SUCCEEDED(selection->CollapseNative(node, 0)); 1.3738 +} 1.3739 + 1.3740 +/////////////////////////////////////////////////////////////////////////// 1.3741 +// GetEnclosingTable: find ancestor who is a table, if any 1.3742 +// 1.3743 +nsCOMPtr<nsIDOMNode> 1.3744 +nsHTMLEditor::GetEnclosingTable(nsIDOMNode *aNode) 1.3745 +{ 1.3746 + NS_PRECONDITION(aNode, "null node passed to nsHTMLEditor::GetEnclosingTable"); 1.3747 + nsCOMPtr<nsIDOMNode> tbl, tmp, node = aNode; 1.3748 + 1.3749 + while (!tbl) 1.3750 + { 1.3751 + tmp = GetBlockNodeParent(node); 1.3752 + if (!tmp) break; 1.3753 + if (nsHTMLEditUtils::IsTable(tmp)) tbl = tmp; 1.3754 + node = tmp; 1.3755 + } 1.3756 + return tbl; 1.3757 +} 1.3758 + 1.3759 + 1.3760 +/* this method scans the selection for adjacent text nodes 1.3761 + * and collapses them into a single text node. 1.3762 + * "adjacent" means literally adjacent siblings of the same parent. 1.3763 + * Uses nsEditor::JoinNodes so action is undoable. 1.3764 + * Should be called within the context of a batch transaction. 1.3765 + */ 1.3766 +NS_IMETHODIMP 1.3767 +nsHTMLEditor::CollapseAdjacentTextNodes(nsIDOMRange *aInRange) 1.3768 +{ 1.3769 + NS_ENSURE_TRUE(aInRange, NS_ERROR_NULL_POINTER); 1.3770 + nsAutoTxnsConserveSelection dontSpazMySelection(this); 1.3771 + nsTArray<nsCOMPtr<nsIDOMNode> > textNodes; 1.3772 + // we can't actually do anything during iteration, so store the text nodes in an array 1.3773 + // don't bother ref counting them because we know we can hold them for the 1.3774 + // lifetime of this method 1.3775 + 1.3776 + 1.3777 + // build a list of editable text nodes 1.3778 + nsresult result; 1.3779 + nsCOMPtr<nsIContentIterator> iter = 1.3780 + do_CreateInstance("@mozilla.org/content/subtree-content-iterator;1", &result); 1.3781 + NS_ENSURE_SUCCESS(result, result); 1.3782 + 1.3783 + iter->Init(aInRange); 1.3784 + 1.3785 + while (!iter->IsDone()) 1.3786 + { 1.3787 + nsINode* node = iter->GetCurrentNode(); 1.3788 + if (node->NodeType() == nsIDOMNode::TEXT_NODE && 1.3789 + IsEditable(static_cast<nsIContent*>(node))) { 1.3790 + nsCOMPtr<nsIDOMNode> domNode = do_QueryInterface(node); 1.3791 + textNodes.AppendElement(domNode); 1.3792 + } 1.3793 + 1.3794 + iter->Next(); 1.3795 + } 1.3796 + 1.3797 + // now that I have a list of text nodes, collapse adjacent text nodes 1.3798 + // NOTE: assumption that JoinNodes keeps the righthand node 1.3799 + while (textNodes.Length() > 1) 1.3800 + { 1.3801 + // we assume a textNodes entry can't be nullptr 1.3802 + nsIDOMNode *leftTextNode = textNodes[0]; 1.3803 + nsIDOMNode *rightTextNode = textNodes[1]; 1.3804 + NS_ASSERTION(leftTextNode && rightTextNode,"left or rightTextNode null in CollapseAdjacentTextNodes"); 1.3805 + 1.3806 + // get the prev sibling of the right node, and see if its leftTextNode 1.3807 + nsCOMPtr<nsIDOMNode> prevSibOfRightNode; 1.3808 + result = 1.3809 + rightTextNode->GetPreviousSibling(getter_AddRefs(prevSibOfRightNode)); 1.3810 + NS_ENSURE_SUCCESS(result, result); 1.3811 + if (prevSibOfRightNode && (prevSibOfRightNode == leftTextNode)) 1.3812 + { 1.3813 + nsCOMPtr<nsIDOMNode> parent; 1.3814 + result = rightTextNode->GetParentNode(getter_AddRefs(parent)); 1.3815 + NS_ENSURE_SUCCESS(result, result); 1.3816 + NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER); 1.3817 + result = JoinNodes(leftTextNode, rightTextNode, parent); 1.3818 + NS_ENSURE_SUCCESS(result, result); 1.3819 + } 1.3820 + 1.3821 + textNodes.RemoveElementAt(0); // remove the leftmost text node from the list 1.3822 + } 1.3823 + 1.3824 + return result; 1.3825 +} 1.3826 + 1.3827 +NS_IMETHODIMP 1.3828 +nsHTMLEditor::SetSelectionAtDocumentStart(nsISelection *aSelection) 1.3829 +{ 1.3830 + dom::Element* rootElement = GetRoot(); 1.3831 + NS_ENSURE_TRUE(rootElement, NS_ERROR_NULL_POINTER); 1.3832 + 1.3833 + return aSelection->CollapseNative(rootElement, 0); 1.3834 +} 1.3835 + 1.3836 + 1.3837 +/////////////////////////////////////////////////////////////////////////// 1.3838 +// RemoveBlockContainer: remove inNode, reparenting its children into their 1.3839 +// the parent of inNode. In addition, INSERT ANY BR's NEEDED 1.3840 +// TO PRESERVE IDENTITY OF REMOVED BLOCK. 1.3841 +// 1.3842 +nsresult 1.3843 +nsHTMLEditor::RemoveBlockContainer(nsIDOMNode *inNode) 1.3844 +{ 1.3845 + NS_ENSURE_TRUE(inNode, NS_ERROR_NULL_POINTER); 1.3846 + nsresult res; 1.3847 + nsCOMPtr<nsIDOMNode> sibling, child, unused; 1.3848 + 1.3849 + // Two possibilities: the container cold be empty of editable content. 1.3850 + // If that is the case, we need to compare what is before and after inNode 1.3851 + // to determine if we need a br. 1.3852 + // Or it could not be empty, in which case we have to compare previous 1.3853 + // sibling and first child to determine if we need a leading br, 1.3854 + // and compare following sibling and last child to determine if we need a 1.3855 + // trailing br. 1.3856 + 1.3857 + res = GetFirstEditableChild(inNode, address_of(child)); 1.3858 + NS_ENSURE_SUCCESS(res, res); 1.3859 + 1.3860 + if (child) // the case of inNode not being empty 1.3861 + { 1.3862 + // we need a br at start unless: 1.3863 + // 1) previous sibling of inNode is a block, OR 1.3864 + // 2) previous sibling of inNode is a br, OR 1.3865 + // 3) first child of inNode is a block OR 1.3866 + // 4) either is null 1.3867 + 1.3868 + res = GetPriorHTMLSibling(inNode, address_of(sibling)); 1.3869 + NS_ENSURE_SUCCESS(res, res); 1.3870 + if (sibling && !IsBlockNode(sibling) && !nsTextEditUtils::IsBreak(sibling)) 1.3871 + { 1.3872 + res = GetFirstEditableChild(inNode, address_of(child)); 1.3873 + NS_ENSURE_SUCCESS(res, res); 1.3874 + if (child && !IsBlockNode(child)) 1.3875 + { 1.3876 + // insert br node 1.3877 + res = CreateBR(inNode, 0, address_of(unused)); 1.3878 + NS_ENSURE_SUCCESS(res, res); 1.3879 + } 1.3880 + } 1.3881 + 1.3882 + // we need a br at end unless: 1.3883 + // 1) following sibling of inNode is a block, OR 1.3884 + // 2) last child of inNode is a block, OR 1.3885 + // 3) last child of inNode is a block OR 1.3886 + // 4) either is null 1.3887 + 1.3888 + res = GetNextHTMLSibling(inNode, address_of(sibling)); 1.3889 + NS_ENSURE_SUCCESS(res, res); 1.3890 + if (sibling && !IsBlockNode(sibling)) 1.3891 + { 1.3892 + res = GetLastEditableChild(inNode, address_of(child)); 1.3893 + NS_ENSURE_SUCCESS(res, res); 1.3894 + if (child && !IsBlockNode(child) && !nsTextEditUtils::IsBreak(child)) 1.3895 + { 1.3896 + // insert br node 1.3897 + uint32_t len; 1.3898 + res = GetLengthOfDOMNode(inNode, len); 1.3899 + NS_ENSURE_SUCCESS(res, res); 1.3900 + res = CreateBR(inNode, (int32_t)len, address_of(unused)); 1.3901 + NS_ENSURE_SUCCESS(res, res); 1.3902 + } 1.3903 + } 1.3904 + } 1.3905 + else // the case of inNode being empty 1.3906 + { 1.3907 + // we need a br at start unless: 1.3908 + // 1) previous sibling of inNode is a block, OR 1.3909 + // 2) previous sibling of inNode is a br, OR 1.3910 + // 3) following sibling of inNode is a block, OR 1.3911 + // 4) following sibling of inNode is a br OR 1.3912 + // 5) either is null 1.3913 + res = GetPriorHTMLSibling(inNode, address_of(sibling)); 1.3914 + NS_ENSURE_SUCCESS(res, res); 1.3915 + if (sibling && !IsBlockNode(sibling) && !nsTextEditUtils::IsBreak(sibling)) 1.3916 + { 1.3917 + res = GetNextHTMLSibling(inNode, address_of(sibling)); 1.3918 + NS_ENSURE_SUCCESS(res, res); 1.3919 + if (sibling && !IsBlockNode(sibling) && !nsTextEditUtils::IsBreak(sibling)) 1.3920 + { 1.3921 + // insert br node 1.3922 + res = CreateBR(inNode, 0, address_of(unused)); 1.3923 + NS_ENSURE_SUCCESS(res, res); 1.3924 + } 1.3925 + } 1.3926 + } 1.3927 + 1.3928 + // now remove container 1.3929 + return RemoveContainer(inNode); 1.3930 +} 1.3931 + 1.3932 + 1.3933 +/////////////////////////////////////////////////////////////////////////// 1.3934 +// GetPriorHTMLSibling: returns the previous editable sibling, if there is 1.3935 +// one within the parent 1.3936 +// 1.3937 +nsIContent* 1.3938 +nsHTMLEditor::GetPriorHTMLSibling(nsINode* aNode) 1.3939 +{ 1.3940 + MOZ_ASSERT(aNode); 1.3941 + 1.3942 + nsIContent* node = aNode->GetPreviousSibling(); 1.3943 + while (node && !IsEditable(node)) { 1.3944 + node = node->GetPreviousSibling(); 1.3945 + } 1.3946 + 1.3947 + return node; 1.3948 +} 1.3949 + 1.3950 +nsresult 1.3951 +nsHTMLEditor::GetPriorHTMLSibling(nsIDOMNode *inNode, nsCOMPtr<nsIDOMNode> *outNode) 1.3952 +{ 1.3953 + NS_ENSURE_TRUE(outNode, NS_ERROR_NULL_POINTER); 1.3954 + *outNode = nullptr; 1.3955 + 1.3956 + nsCOMPtr<nsINode> node = do_QueryInterface(inNode); 1.3957 + NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER); 1.3958 + 1.3959 + *outNode = do_QueryInterface(GetPriorHTMLSibling(node)); 1.3960 + return NS_OK; 1.3961 +} 1.3962 + 1.3963 + 1.3964 + 1.3965 +/////////////////////////////////////////////////////////////////////////// 1.3966 +// GetPriorHTMLSibling: returns the previous editable sibling, if there is 1.3967 +// one within the parent. just like above routine but 1.3968 +// takes a parent/offset instead of a node. 1.3969 +// 1.3970 +nsIContent* 1.3971 +nsHTMLEditor::GetPriorHTMLSibling(nsINode* aParent, int32_t aOffset) 1.3972 +{ 1.3973 + MOZ_ASSERT(aParent); 1.3974 + 1.3975 + nsIContent* node = aParent->GetChildAt(aOffset - 1); 1.3976 + if (!node || IsEditable(node)) { 1.3977 + return node; 1.3978 + } 1.3979 + 1.3980 + return GetPriorHTMLSibling(node); 1.3981 +} 1.3982 + 1.3983 +nsresult 1.3984 +nsHTMLEditor::GetPriorHTMLSibling(nsIDOMNode *inParent, int32_t inOffset, nsCOMPtr<nsIDOMNode> *outNode) 1.3985 +{ 1.3986 + NS_ENSURE_TRUE(outNode, NS_ERROR_NULL_POINTER); 1.3987 + *outNode = nullptr; 1.3988 + 1.3989 + nsCOMPtr<nsINode> parent = do_QueryInterface(inParent); 1.3990 + NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER); 1.3991 + 1.3992 + *outNode = do_QueryInterface(GetPriorHTMLSibling(parent, inOffset)); 1.3993 + return NS_OK; 1.3994 +} 1.3995 + 1.3996 + 1.3997 + 1.3998 +/////////////////////////////////////////////////////////////////////////// 1.3999 +// GetNextHTMLSibling: returns the next editable sibling, if there is 1.4000 +// one within the parent 1.4001 +// 1.4002 +nsIContent* 1.4003 +nsHTMLEditor::GetNextHTMLSibling(nsINode* aNode) 1.4004 +{ 1.4005 + MOZ_ASSERT(aNode); 1.4006 + 1.4007 + nsIContent* node = aNode->GetNextSibling(); 1.4008 + while (node && !IsEditable(node)) { 1.4009 + node = node->GetNextSibling(); 1.4010 + } 1.4011 + 1.4012 + return node; 1.4013 +} 1.4014 + 1.4015 +nsresult 1.4016 +nsHTMLEditor::GetNextHTMLSibling(nsIDOMNode *inNode, nsCOMPtr<nsIDOMNode> *outNode) 1.4017 +{ 1.4018 + NS_ENSURE_TRUE(outNode, NS_ERROR_NULL_POINTER); 1.4019 + *outNode = nullptr; 1.4020 + 1.4021 + nsCOMPtr<nsINode> node = do_QueryInterface(inNode); 1.4022 + NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER); 1.4023 + 1.4024 + *outNode = do_QueryInterface(GetNextHTMLSibling(node)); 1.4025 + return NS_OK; 1.4026 +} 1.4027 + 1.4028 + 1.4029 + 1.4030 +/////////////////////////////////////////////////////////////////////////// 1.4031 +// GetNextHTMLSibling: returns the next editable sibling, if there is 1.4032 +// one within the parent. just like above routine but 1.4033 +// takes a parent/offset instead of a node. 1.4034 +nsIContent* 1.4035 +nsHTMLEditor::GetNextHTMLSibling(nsINode* aParent, int32_t aOffset) 1.4036 +{ 1.4037 + MOZ_ASSERT(aParent); 1.4038 + 1.4039 + nsIContent* node = aParent->GetChildAt(aOffset + 1); 1.4040 + if (!node || IsEditable(node)) { 1.4041 + return node; 1.4042 + } 1.4043 + 1.4044 + return GetNextHTMLSibling(node); 1.4045 +} 1.4046 + 1.4047 +nsresult 1.4048 +nsHTMLEditor::GetNextHTMLSibling(nsIDOMNode *inParent, int32_t inOffset, nsCOMPtr<nsIDOMNode> *outNode) 1.4049 +{ 1.4050 + NS_ENSURE_TRUE(outNode, NS_ERROR_NULL_POINTER); 1.4051 + *outNode = nullptr; 1.4052 + 1.4053 + nsCOMPtr<nsINode> parent = do_QueryInterface(inParent); 1.4054 + NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER); 1.4055 + 1.4056 + *outNode = do_QueryInterface(GetNextHTMLSibling(parent, inOffset)); 1.4057 + return NS_OK; 1.4058 +} 1.4059 + 1.4060 + 1.4061 + 1.4062 +/////////////////////////////////////////////////////////////////////////// 1.4063 +// GetPriorHTMLNode: returns the previous editable leaf node, if there is 1.4064 +// one within the <body> 1.4065 +// 1.4066 +nsIContent* 1.4067 +nsHTMLEditor::GetPriorHTMLNode(nsINode* aNode, bool aNoBlockCrossing) 1.4068 +{ 1.4069 + MOZ_ASSERT(aNode); 1.4070 + 1.4071 + if (!GetActiveEditingHost()) { 1.4072 + return nullptr; 1.4073 + } 1.4074 + 1.4075 + return GetPriorNode(aNode, true, aNoBlockCrossing); 1.4076 +} 1.4077 + 1.4078 +nsresult 1.4079 +nsHTMLEditor::GetPriorHTMLNode(nsIDOMNode* aNode, 1.4080 + nsCOMPtr<nsIDOMNode>* aResultNode, 1.4081 + bool aNoBlockCrossing) 1.4082 +{ 1.4083 + NS_ENSURE_TRUE(aResultNode, NS_ERROR_NULL_POINTER); 1.4084 + 1.4085 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.4086 + NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER); 1.4087 + 1.4088 + *aResultNode = do_QueryInterface(GetPriorHTMLNode(node, aNoBlockCrossing)); 1.4089 + return NS_OK; 1.4090 +} 1.4091 + 1.4092 + 1.4093 +/////////////////////////////////////////////////////////////////////////// 1.4094 +// GetPriorHTMLNode: same as above but takes {parent,offset} instead of node 1.4095 +// 1.4096 +nsIContent* 1.4097 +nsHTMLEditor::GetPriorHTMLNode(nsINode* aParent, int32_t aOffset, 1.4098 + bool aNoBlockCrossing) 1.4099 +{ 1.4100 + MOZ_ASSERT(aParent); 1.4101 + 1.4102 + if (!GetActiveEditingHost()) { 1.4103 + return nullptr; 1.4104 + } 1.4105 + 1.4106 + return GetPriorNode(aParent, aOffset, true, aNoBlockCrossing); 1.4107 +} 1.4108 + 1.4109 +nsresult 1.4110 +nsHTMLEditor::GetPriorHTMLNode(nsIDOMNode* aNode, int32_t aOffset, 1.4111 + nsCOMPtr<nsIDOMNode>* aResultNode, 1.4112 + bool aNoBlockCrossing) 1.4113 +{ 1.4114 + NS_ENSURE_TRUE(aResultNode, NS_ERROR_NULL_POINTER); 1.4115 + 1.4116 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.4117 + NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER); 1.4118 + 1.4119 + *aResultNode = do_QueryInterface(GetPriorHTMLNode(node, aOffset, 1.4120 + aNoBlockCrossing)); 1.4121 + return NS_OK; 1.4122 +} 1.4123 + 1.4124 + 1.4125 +/////////////////////////////////////////////////////////////////////////// 1.4126 +// GetNextHTMLNode: returns the next editable leaf node, if there is 1.4127 +// one within the <body> 1.4128 +// 1.4129 +nsIContent* 1.4130 +nsHTMLEditor::GetNextHTMLNode(nsINode* aNode, bool aNoBlockCrossing) 1.4131 +{ 1.4132 + MOZ_ASSERT(aNode); 1.4133 + 1.4134 + nsIContent* result = GetNextNode(aNode, true, aNoBlockCrossing); 1.4135 + 1.4136 + if (result && !IsDescendantOfEditorRoot(result)) { 1.4137 + return nullptr; 1.4138 + } 1.4139 + 1.4140 + return result; 1.4141 +} 1.4142 + 1.4143 +nsresult 1.4144 +nsHTMLEditor::GetNextHTMLNode(nsIDOMNode* aNode, 1.4145 + nsCOMPtr<nsIDOMNode>* aResultNode, 1.4146 + bool aNoBlockCrossing) 1.4147 +{ 1.4148 + NS_ENSURE_TRUE(aResultNode, NS_ERROR_NULL_POINTER); 1.4149 + 1.4150 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.4151 + NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER); 1.4152 + 1.4153 + *aResultNode = do_QueryInterface(GetNextHTMLNode(node, aNoBlockCrossing)); 1.4154 + return NS_OK; 1.4155 +} 1.4156 + 1.4157 + 1.4158 +/////////////////////////////////////////////////////////////////////////// 1.4159 +// GetNextHTMLNode: same as above but takes {parent,offset} instead of node 1.4160 +// 1.4161 +nsIContent* 1.4162 +nsHTMLEditor::GetNextHTMLNode(nsINode* aParent, int32_t aOffset, 1.4163 + bool aNoBlockCrossing) 1.4164 +{ 1.4165 + nsIContent* content = GetNextNode(aParent, aOffset, true, aNoBlockCrossing); 1.4166 + if (content && !IsDescendantOfEditorRoot(content)) { 1.4167 + return nullptr; 1.4168 + } 1.4169 + return content; 1.4170 +} 1.4171 + 1.4172 +nsresult 1.4173 +nsHTMLEditor::GetNextHTMLNode(nsIDOMNode* aNode, int32_t aOffset, 1.4174 + nsCOMPtr<nsIDOMNode>* aResultNode, 1.4175 + bool aNoBlockCrossing) 1.4176 +{ 1.4177 + NS_ENSURE_TRUE(aResultNode, NS_ERROR_NULL_POINTER); 1.4178 + 1.4179 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.4180 + NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER); 1.4181 + 1.4182 + *aResultNode = do_QueryInterface(GetNextHTMLNode(node, aOffset, 1.4183 + aNoBlockCrossing)); 1.4184 + return NS_OK; 1.4185 +} 1.4186 + 1.4187 + 1.4188 +nsresult 1.4189 +nsHTMLEditor::IsFirstEditableChild( nsIDOMNode *aNode, bool *aOutIsFirst) 1.4190 +{ 1.4191 + // check parms 1.4192 + NS_ENSURE_TRUE(aOutIsFirst && aNode, NS_ERROR_NULL_POINTER); 1.4193 + 1.4194 + // init out parms 1.4195 + *aOutIsFirst = false; 1.4196 + 1.4197 + // find first editable child and compare it to aNode 1.4198 + nsCOMPtr<nsIDOMNode> parent, firstChild; 1.4199 + nsresult res = aNode->GetParentNode(getter_AddRefs(parent)); 1.4200 + NS_ENSURE_SUCCESS(res, res); 1.4201 + NS_ENSURE_TRUE(parent, NS_ERROR_FAILURE); 1.4202 + res = GetFirstEditableChild(parent, address_of(firstChild)); 1.4203 + NS_ENSURE_SUCCESS(res, res); 1.4204 + 1.4205 + *aOutIsFirst = (firstChild.get() == aNode); 1.4206 + return res; 1.4207 +} 1.4208 + 1.4209 + 1.4210 +nsresult 1.4211 +nsHTMLEditor::IsLastEditableChild( nsIDOMNode *aNode, bool *aOutIsLast) 1.4212 +{ 1.4213 + // check parms 1.4214 + NS_ENSURE_TRUE(aOutIsLast && aNode, NS_ERROR_NULL_POINTER); 1.4215 + 1.4216 + // init out parms 1.4217 + *aOutIsLast = false; 1.4218 + 1.4219 + // find last editable child and compare it to aNode 1.4220 + nsCOMPtr<nsIDOMNode> parent, lastChild; 1.4221 + nsresult res = aNode->GetParentNode(getter_AddRefs(parent)); 1.4222 + NS_ENSURE_SUCCESS(res, res); 1.4223 + NS_ENSURE_TRUE(parent, NS_ERROR_FAILURE); 1.4224 + res = GetLastEditableChild(parent, address_of(lastChild)); 1.4225 + NS_ENSURE_SUCCESS(res, res); 1.4226 + 1.4227 + *aOutIsLast = (lastChild.get() == aNode); 1.4228 + return res; 1.4229 +} 1.4230 + 1.4231 + 1.4232 +nsresult 1.4233 +nsHTMLEditor::GetFirstEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutFirstChild) 1.4234 +{ 1.4235 + // check parms 1.4236 + NS_ENSURE_TRUE(aOutFirstChild && aNode, NS_ERROR_NULL_POINTER); 1.4237 + 1.4238 + // init out parms 1.4239 + *aOutFirstChild = nullptr; 1.4240 + 1.4241 + // find first editable child 1.4242 + nsCOMPtr<nsIDOMNode> child; 1.4243 + nsresult res = aNode->GetFirstChild(getter_AddRefs(child)); 1.4244 + NS_ENSURE_SUCCESS(res, res); 1.4245 + 1.4246 + while (child && !IsEditable(child)) 1.4247 + { 1.4248 + nsCOMPtr<nsIDOMNode> tmp; 1.4249 + res = child->GetNextSibling(getter_AddRefs(tmp)); 1.4250 + NS_ENSURE_SUCCESS(res, res); 1.4251 + NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE); 1.4252 + child = tmp; 1.4253 + } 1.4254 + 1.4255 + *aOutFirstChild = child; 1.4256 + return res; 1.4257 +} 1.4258 + 1.4259 + 1.4260 +nsresult 1.4261 +nsHTMLEditor::GetLastEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutLastChild) 1.4262 +{ 1.4263 + // check parms 1.4264 + NS_ENSURE_TRUE(aOutLastChild && aNode, NS_ERROR_NULL_POINTER); 1.4265 + 1.4266 + // init out parms 1.4267 + *aOutLastChild = aNode; 1.4268 + 1.4269 + // find last editable child 1.4270 + nsCOMPtr<nsIDOMNode> child; 1.4271 + nsresult res = aNode->GetLastChild(getter_AddRefs(child)); 1.4272 + NS_ENSURE_SUCCESS(res, res); 1.4273 + 1.4274 + while (child && !IsEditable(child)) 1.4275 + { 1.4276 + nsCOMPtr<nsIDOMNode> tmp; 1.4277 + res = child->GetPreviousSibling(getter_AddRefs(tmp)); 1.4278 + NS_ENSURE_SUCCESS(res, res); 1.4279 + NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE); 1.4280 + child = tmp; 1.4281 + } 1.4282 + 1.4283 + *aOutLastChild = child; 1.4284 + return res; 1.4285 +} 1.4286 + 1.4287 +nsresult 1.4288 +nsHTMLEditor::GetFirstEditableLeaf( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutFirstLeaf) 1.4289 +{ 1.4290 + // check parms 1.4291 + NS_ENSURE_TRUE(aOutFirstLeaf && aNode, NS_ERROR_NULL_POINTER); 1.4292 + 1.4293 + // init out parms 1.4294 + *aOutFirstLeaf = aNode; 1.4295 + 1.4296 + // find leftmost leaf 1.4297 + nsCOMPtr<nsIDOMNode> child; 1.4298 + nsresult res = NS_OK; 1.4299 + child = GetLeftmostChild(aNode); 1.4300 + while (child && (!IsEditable(child) || !nsEditorUtils::IsLeafNode(child))) 1.4301 + { 1.4302 + nsCOMPtr<nsIDOMNode> tmp; 1.4303 + res = GetNextHTMLNode(child, address_of(tmp)); 1.4304 + NS_ENSURE_SUCCESS(res, res); 1.4305 + NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE); 1.4306 + 1.4307 + // only accept nodes that are descendants of aNode 1.4308 + if (nsEditorUtils::IsDescendantOf(tmp, aNode)) 1.4309 + child = tmp; 1.4310 + else 1.4311 + { 1.4312 + child = nullptr; // this will abort the loop 1.4313 + } 1.4314 + } 1.4315 + 1.4316 + *aOutFirstLeaf = child; 1.4317 + return res; 1.4318 +} 1.4319 + 1.4320 + 1.4321 +nsresult 1.4322 +nsHTMLEditor::GetLastEditableLeaf(nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutLastLeaf) 1.4323 +{ 1.4324 + // check parms 1.4325 + NS_ENSURE_TRUE(aOutLastLeaf && aNode, NS_ERROR_NULL_POINTER); 1.4326 + 1.4327 + // init out parms 1.4328 + *aOutLastLeaf = nullptr; 1.4329 + 1.4330 + // find rightmost leaf 1.4331 + nsCOMPtr<nsIDOMNode> child = GetRightmostChild(aNode, false); 1.4332 + nsresult res = NS_OK; 1.4333 + while (child && (!IsEditable(child) || !nsEditorUtils::IsLeafNode(child))) 1.4334 + { 1.4335 + nsCOMPtr<nsIDOMNode> tmp; 1.4336 + res = GetPriorHTMLNode(child, address_of(tmp)); 1.4337 + NS_ENSURE_SUCCESS(res, res); 1.4338 + NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE); 1.4339 + 1.4340 + // only accept nodes that are descendants of aNode 1.4341 + if (nsEditorUtils::IsDescendantOf(tmp, aNode)) 1.4342 + child = tmp; 1.4343 + else 1.4344 + { 1.4345 + child = nullptr; 1.4346 + } 1.4347 + } 1.4348 + 1.4349 + *aOutLastLeaf = child; 1.4350 + return res; 1.4351 +} 1.4352 + 1.4353 + 1.4354 +/////////////////////////////////////////////////////////////////////////// 1.4355 +// IsVisTextNode: figure out if textnode aTextNode has any visible content. 1.4356 +// 1.4357 +nsresult 1.4358 +nsHTMLEditor::IsVisTextNode(nsIContent* aNode, 1.4359 + bool* outIsEmptyNode, 1.4360 + bool aSafeToAskFrames) 1.4361 +{ 1.4362 + MOZ_ASSERT(aNode); 1.4363 + MOZ_ASSERT(aNode->NodeType() == nsIDOMNode::TEXT_NODE); 1.4364 + MOZ_ASSERT(outIsEmptyNode); 1.4365 + 1.4366 + *outIsEmptyNode = true; 1.4367 + 1.4368 + uint32_t length = aNode->TextLength(); 1.4369 + if (aSafeToAskFrames) 1.4370 + { 1.4371 + nsCOMPtr<nsISelectionController> selCon; 1.4372 + nsresult res = GetSelectionController(getter_AddRefs(selCon)); 1.4373 + NS_ENSURE_SUCCESS(res, res); 1.4374 + NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE); 1.4375 + bool isVisible = false; 1.4376 + // ask the selection controller for information about whether any 1.4377 + // of the data in the node is really rendered. This is really 1.4378 + // something that frames know about, but we aren't supposed to talk to frames. 1.4379 + // So we put a call in the selection controller interface, since it's already 1.4380 + // in bed with frames anyway. (this is a fix for bug 22227, and a 1.4381 + // partial fix for bug 46209) 1.4382 + res = selCon->CheckVisibilityContent(aNode, 0, length, &isVisible); 1.4383 + NS_ENSURE_SUCCESS(res, res); 1.4384 + if (isVisible) 1.4385 + { 1.4386 + *outIsEmptyNode = false; 1.4387 + } 1.4388 + } 1.4389 + else if (length) 1.4390 + { 1.4391 + if (aNode->TextIsOnlyWhitespace()) 1.4392 + { 1.4393 + nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode); 1.4394 + nsWSRunObject wsRunObj(this, node, 0); 1.4395 + nsCOMPtr<nsIDOMNode> visNode; 1.4396 + int32_t outVisOffset=0; 1.4397 + WSType visType; 1.4398 + wsRunObj.NextVisibleNode(node, 0, address_of(visNode), 1.4399 + &outVisOffset, &visType); 1.4400 + if (visType == WSType::normalWS || visType == WSType::text) { 1.4401 + *outIsEmptyNode = (node != visNode); 1.4402 + } 1.4403 + } 1.4404 + else 1.4405 + { 1.4406 + *outIsEmptyNode = false; 1.4407 + } 1.4408 + } 1.4409 + return NS_OK; 1.4410 +} 1.4411 + 1.4412 + 1.4413 +/////////////////////////////////////////////////////////////////////////// 1.4414 +// IsEmptyNode: figure out if aNode is an empty node. 1.4415 +// A block can have children and still be considered empty, 1.4416 +// if the children are empty or non-editable. 1.4417 +// 1.4418 +nsresult 1.4419 +nsHTMLEditor::IsEmptyNode( nsIDOMNode *aNode, 1.4420 + bool *outIsEmptyNode, 1.4421 + bool aSingleBRDoesntCount, 1.4422 + bool aListOrCellNotEmpty, 1.4423 + bool aSafeToAskFrames) 1.4424 +{ 1.4425 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.4426 + return IsEmptyNode(node, outIsEmptyNode, aSingleBRDoesntCount, 1.4427 + aListOrCellNotEmpty, aSafeToAskFrames); 1.4428 +} 1.4429 + 1.4430 +nsresult 1.4431 +nsHTMLEditor::IsEmptyNode(nsINode* aNode, 1.4432 + bool* outIsEmptyNode, 1.4433 + bool aSingleBRDoesntCount, 1.4434 + bool aListOrCellNotEmpty, 1.4435 + bool aSafeToAskFrames) 1.4436 +{ 1.4437 + NS_ENSURE_TRUE(aNode && outIsEmptyNode, NS_ERROR_NULL_POINTER); 1.4438 + *outIsEmptyNode = true; 1.4439 + bool seenBR = false; 1.4440 + return IsEmptyNodeImpl(aNode, outIsEmptyNode, aSingleBRDoesntCount, 1.4441 + aListOrCellNotEmpty, aSafeToAskFrames, &seenBR); 1.4442 +} 1.4443 + 1.4444 +/////////////////////////////////////////////////////////////////////////// 1.4445 +// IsEmptyNodeImpl: workhorse for IsEmptyNode. 1.4446 +// 1.4447 +nsresult 1.4448 +nsHTMLEditor::IsEmptyNodeImpl(nsINode* aNode, 1.4449 + bool *outIsEmptyNode, 1.4450 + bool aSingleBRDoesntCount, 1.4451 + bool aListOrCellNotEmpty, 1.4452 + bool aSafeToAskFrames, 1.4453 + bool *aSeenBR) 1.4454 +{ 1.4455 + NS_ENSURE_TRUE(aNode && outIsEmptyNode && aSeenBR, NS_ERROR_NULL_POINTER); 1.4456 + 1.4457 + if (aNode->NodeType() == nsIDOMNode::TEXT_NODE) { 1.4458 + return IsVisTextNode(static_cast<nsIContent*>(aNode), outIsEmptyNode, aSafeToAskFrames); 1.4459 + } 1.4460 + 1.4461 + // if it's not a text node (handled above) and it's not a container, 1.4462 + // then we don't call it empty (it's an <hr>, or <br>, etc). 1.4463 + // Also, if it's an anchor then don't treat it as empty - even though 1.4464 + // anchors are containers, named anchors are "empty" but we don't 1.4465 + // want to treat them as such. Also, don't call ListItems or table 1.4466 + // cells empty if caller desires. Form Widgets not empty. 1.4467 + if (!IsContainer(aNode->AsDOMNode()) || 1.4468 + (nsHTMLEditUtils::IsNamedAnchor(aNode) || 1.4469 + nsHTMLEditUtils::IsFormWidget(aNode) || 1.4470 + (aListOrCellNotEmpty && 1.4471 + (nsHTMLEditUtils::IsListItem(aNode) || 1.4472 + nsHTMLEditUtils::IsTableCell(aNode))))) { 1.4473 + *outIsEmptyNode = false; 1.4474 + return NS_OK; 1.4475 + } 1.4476 + 1.4477 + // need this for later 1.4478 + bool isListItemOrCell = nsHTMLEditUtils::IsListItem(aNode) || 1.4479 + nsHTMLEditUtils::IsTableCell(aNode); 1.4480 + 1.4481 + // loop over children of node. if no children, or all children are either 1.4482 + // empty text nodes or non-editable, then node qualifies as empty 1.4483 + for (nsCOMPtr<nsIContent> child = aNode->GetFirstChild(); 1.4484 + child; 1.4485 + child = child->GetNextSibling()) { 1.4486 + // Is the child editable and non-empty? if so, return false 1.4487 + if (nsEditor::IsEditable(child)) { 1.4488 + if (child->NodeType() == nsIDOMNode::TEXT_NODE) { 1.4489 + nsresult rv = IsVisTextNode(child, outIsEmptyNode, aSafeToAskFrames); 1.4490 + NS_ENSURE_SUCCESS(rv, rv); 1.4491 + // break out if we find we aren't emtpy 1.4492 + if (!*outIsEmptyNode) { 1.4493 + return NS_OK; 1.4494 + } 1.4495 + } else { 1.4496 + // An editable, non-text node. We need to check its content. 1.4497 + // Is it the node we are iterating over? 1.4498 + if (child == aNode) { 1.4499 + break; 1.4500 + } 1.4501 + 1.4502 + if (aSingleBRDoesntCount && !*aSeenBR && child->IsHTML(nsGkAtoms::br)) { 1.4503 + // the first br in a block doesn't count if the caller so indicated 1.4504 + *aSeenBR = true; 1.4505 + } else { 1.4506 + // is it an empty node of some sort? 1.4507 + // note: list items or table cells are not considered empty 1.4508 + // if they contain other lists or tables 1.4509 + if (child->IsElement()) { 1.4510 + if (isListItemOrCell) { 1.4511 + if (nsHTMLEditUtils::IsList(child) || 1.4512 + child->IsHTML(nsGkAtoms::table)) { 1.4513 + // break out if we find we aren't empty 1.4514 + *outIsEmptyNode = false; 1.4515 + return NS_OK; 1.4516 + } 1.4517 + } else if (nsHTMLEditUtils::IsFormWidget(child)) { 1.4518 + // is it a form widget? 1.4519 + // break out if we find we aren't empty 1.4520 + *outIsEmptyNode = false; 1.4521 + return NS_OK; 1.4522 + } 1.4523 + } 1.4524 + 1.4525 + bool isEmptyNode = true; 1.4526 + nsresult rv = IsEmptyNodeImpl(child, &isEmptyNode, 1.4527 + aSingleBRDoesntCount, 1.4528 + aListOrCellNotEmpty, aSafeToAskFrames, 1.4529 + aSeenBR); 1.4530 + NS_ENSURE_SUCCESS(rv, rv); 1.4531 + if (!isEmptyNode) { 1.4532 + // otherwise it ain't empty 1.4533 + *outIsEmptyNode = false; 1.4534 + return NS_OK; 1.4535 + } 1.4536 + } 1.4537 + } 1.4538 + } 1.4539 + } 1.4540 + 1.4541 + return NS_OK; 1.4542 +} 1.4543 + 1.4544 +// add to aElement the CSS inline styles corresponding to the HTML attribute 1.4545 +// aAttribute with its value aValue 1.4546 +nsresult 1.4547 +nsHTMLEditor::SetAttributeOrEquivalent(nsIDOMElement * aElement, 1.4548 + const nsAString & aAttribute, 1.4549 + const nsAString & aValue, 1.4550 + bool aSuppressTransaction) 1.4551 +{ 1.4552 + nsAutoScriptBlocker scriptBlocker; 1.4553 + 1.4554 + nsresult res = NS_OK; 1.4555 + if (IsCSSEnabled() && mHTMLCSSUtils) { 1.4556 + int32_t count; 1.4557 + res = mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(aElement, nullptr, &aAttribute, &aValue, &count, 1.4558 + aSuppressTransaction); 1.4559 + NS_ENSURE_SUCCESS(res, res); 1.4560 + if (count) { 1.4561 + // we found an equivalence ; let's remove the HTML attribute itself if it is set 1.4562 + nsAutoString existingValue; 1.4563 + bool wasSet = false; 1.4564 + res = GetAttributeValue(aElement, aAttribute, existingValue, &wasSet); 1.4565 + NS_ENSURE_SUCCESS(res, res); 1.4566 + if (wasSet) { 1.4567 + if (aSuppressTransaction) 1.4568 + res = aElement->RemoveAttribute(aAttribute); 1.4569 + else 1.4570 + res = RemoveAttribute(aElement, aAttribute); 1.4571 + } 1.4572 + } 1.4573 + else { 1.4574 + // count is an integer that represents the number of CSS declarations applied to the 1.4575 + // element. If it is zero, we found no equivalence in this implementation for the 1.4576 + // attribute 1.4577 + if (aAttribute.EqualsLiteral("style")) { 1.4578 + // if it is the style attribute, just add the new value to the existing style 1.4579 + // attribute's value 1.4580 + nsAutoString existingValue; 1.4581 + bool wasSet = false; 1.4582 + res = GetAttributeValue(aElement, NS_LITERAL_STRING("style"), existingValue, &wasSet); 1.4583 + NS_ENSURE_SUCCESS(res, res); 1.4584 + existingValue.AppendLiteral(" "); 1.4585 + existingValue.Append(aValue); 1.4586 + if (aSuppressTransaction) 1.4587 + res = aElement->SetAttribute(aAttribute, existingValue); 1.4588 + else 1.4589 + res = SetAttribute(aElement, aAttribute, existingValue); 1.4590 + } 1.4591 + else { 1.4592 + // we have no CSS equivalence for this attribute and it is not the style 1.4593 + // attribute; let's set it the good'n'old HTML way 1.4594 + if (aSuppressTransaction) 1.4595 + res = aElement->SetAttribute(aAttribute, aValue); 1.4596 + else 1.4597 + res = SetAttribute(aElement, aAttribute, aValue); 1.4598 + } 1.4599 + } 1.4600 + } 1.4601 + else { 1.4602 + // we are not in an HTML+CSS editor; let's set the attribute the HTML way 1.4603 + if (aSuppressTransaction) 1.4604 + res = aElement->SetAttribute(aAttribute, aValue); 1.4605 + else 1.4606 + res = SetAttribute(aElement, aAttribute, aValue); 1.4607 + } 1.4608 + return res; 1.4609 +} 1.4610 + 1.4611 +nsresult 1.4612 +nsHTMLEditor::RemoveAttributeOrEquivalent(nsIDOMElement* aElement, 1.4613 + const nsAString& aAttribute, 1.4614 + bool aSuppressTransaction) 1.4615 +{ 1.4616 + nsCOMPtr<dom::Element> element = do_QueryInterface(aElement); 1.4617 + NS_ENSURE_TRUE(element, NS_OK); 1.4618 + 1.4619 + nsCOMPtr<nsIAtom> attribute = do_GetAtom(aAttribute); 1.4620 + MOZ_ASSERT(attribute); 1.4621 + 1.4622 + nsresult res = NS_OK; 1.4623 + if (IsCSSEnabled() && mHTMLCSSUtils) { 1.4624 + res = mHTMLCSSUtils->RemoveCSSEquivalentToHTMLStyle( 1.4625 + element, nullptr, &aAttribute, nullptr, aSuppressTransaction); 1.4626 + NS_ENSURE_SUCCESS(res, res); 1.4627 + } 1.4628 + 1.4629 + if (element->HasAttr(kNameSpaceID_None, attribute)) { 1.4630 + if (aSuppressTransaction) { 1.4631 + res = element->UnsetAttr(kNameSpaceID_None, attribute, 1.4632 + /* aNotify = */ true); 1.4633 + } else { 1.4634 + res = RemoveAttribute(aElement, aAttribute); 1.4635 + } 1.4636 + } 1.4637 + return res; 1.4638 +} 1.4639 + 1.4640 +nsresult 1.4641 +nsHTMLEditor::SetIsCSSEnabled(bool aIsCSSPrefChecked) 1.4642 +{ 1.4643 + if (!mHTMLCSSUtils) { 1.4644 + return NS_ERROR_NOT_INITIALIZED; 1.4645 + } 1.4646 + 1.4647 + mHTMLCSSUtils->SetCSSEnabled(aIsCSSPrefChecked); 1.4648 + 1.4649 + // Disable the eEditorNoCSSMask flag if we're enabling StyleWithCSS. 1.4650 + uint32_t flags = mFlags; 1.4651 + if (aIsCSSPrefChecked) { 1.4652 + // Turn off NoCSS as we're enabling CSS 1.4653 + flags &= ~eEditorNoCSSMask; 1.4654 + } else { 1.4655 + // Turn on NoCSS, as we're disabling CSS. 1.4656 + flags |= eEditorNoCSSMask; 1.4657 + } 1.4658 + 1.4659 + return SetFlags(flags); 1.4660 +} 1.4661 + 1.4662 +// Set the block background color 1.4663 +NS_IMETHODIMP 1.4664 +nsHTMLEditor::SetCSSBackgroundColor(const nsAString& aColor) 1.4665 +{ 1.4666 + if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } 1.4667 + ForceCompositionEnd(); 1.4668 + 1.4669 + // Protect the edit rules object from dying 1.4670 + nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules); 1.4671 + 1.4672 + nsRefPtr<Selection> selection = GetSelection(); 1.4673 + 1.4674 + bool isCollapsed = selection->Collapsed(); 1.4675 + 1.4676 + nsAutoEditBatch batchIt(this); 1.4677 + nsAutoRules beginRulesSniffing(this, EditAction::insertElement, nsIEditor::eNext); 1.4678 + nsAutoSelectionReset selectionResetter(selection, this); 1.4679 + nsAutoTxnsConserveSelection dontSpazMySelection(this); 1.4680 + 1.4681 + bool cancel, handled; 1.4682 + nsTextRulesInfo ruleInfo(EditAction::setTextProperty); 1.4683 + nsresult res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); 1.4684 + NS_ENSURE_SUCCESS(res, res); 1.4685 + if (!cancel && !handled) 1.4686 + { 1.4687 + // loop thru the ranges in the selection 1.4688 + nsAutoString bgcolor; bgcolor.AssignLiteral("bgcolor"); 1.4689 + uint32_t rangeCount = selection->GetRangeCount(); 1.4690 + for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) { 1.4691 + nsCOMPtr<nsIDOMNode> cachedBlockParent = nullptr; 1.4692 + nsRefPtr<nsRange> range = selection->GetRangeAt(rangeIdx); 1.4693 + NS_ENSURE_TRUE(range, NS_ERROR_FAILURE); 1.4694 + 1.4695 + // check for easy case: both range endpoints in same text node 1.4696 + nsCOMPtr<nsIDOMNode> startNode, endNode; 1.4697 + int32_t startOffset, endOffset; 1.4698 + res = range->GetStartContainer(getter_AddRefs(startNode)); 1.4699 + NS_ENSURE_SUCCESS(res, res); 1.4700 + res = range->GetEndContainer(getter_AddRefs(endNode)); 1.4701 + NS_ENSURE_SUCCESS(res, res); 1.4702 + res = range->GetStartOffset(&startOffset); 1.4703 + NS_ENSURE_SUCCESS(res, res); 1.4704 + res = range->GetEndOffset(&endOffset); 1.4705 + NS_ENSURE_SUCCESS(res, res); 1.4706 + if ((startNode == endNode) && IsTextNode(startNode)) 1.4707 + { 1.4708 + // let's find the block container of the text node 1.4709 + nsCOMPtr<nsIDOMNode> blockParent; 1.4710 + blockParent = GetBlockNodeParent(startNode); 1.4711 + // and apply the background color to that block container 1.4712 + if (cachedBlockParent != blockParent) 1.4713 + { 1.4714 + cachedBlockParent = blockParent; 1.4715 + nsCOMPtr<nsIDOMElement> element = do_QueryInterface(blockParent); 1.4716 + int32_t count; 1.4717 + res = mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(element, nullptr, &bgcolor, &aColor, &count, false); 1.4718 + NS_ENSURE_SUCCESS(res, res); 1.4719 + } 1.4720 + } 1.4721 + else if ((startNode == endNode) && nsTextEditUtils::IsBody(startNode) && isCollapsed) 1.4722 + { 1.4723 + // we have no block in the document, let's apply the background to the body 1.4724 + nsCOMPtr<nsIDOMElement> element = do_QueryInterface(startNode); 1.4725 + int32_t count; 1.4726 + res = mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(element, nullptr, &bgcolor, &aColor, &count, false); 1.4727 + NS_ENSURE_SUCCESS(res, res); 1.4728 + } 1.4729 + else if ((startNode == endNode) && (((endOffset-startOffset) == 1) || (!startOffset && !endOffset))) 1.4730 + { 1.4731 + // a unique node is selected, let's also apply the background color 1.4732 + // to the containing block, possibly the node itself 1.4733 + nsCOMPtr<nsIDOMNode> selectedNode = GetChildAt(startNode, startOffset); 1.4734 + bool isBlock =false; 1.4735 + res = NodeIsBlockStatic(selectedNode, &isBlock); 1.4736 + NS_ENSURE_SUCCESS(res, res); 1.4737 + nsCOMPtr<nsIDOMNode> blockParent = selectedNode; 1.4738 + if (!isBlock) { 1.4739 + blockParent = GetBlockNodeParent(selectedNode); 1.4740 + } 1.4741 + if (cachedBlockParent != blockParent) 1.4742 + { 1.4743 + cachedBlockParent = blockParent; 1.4744 + nsCOMPtr<nsIDOMElement> element = do_QueryInterface(blockParent); 1.4745 + int32_t count; 1.4746 + res = mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(element, nullptr, &bgcolor, &aColor, &count, false); 1.4747 + NS_ENSURE_SUCCESS(res, res); 1.4748 + } 1.4749 + } 1.4750 + else 1.4751 + { 1.4752 + // not the easy case. range not contained in single text node. 1.4753 + // there are up to three phases here. There are all the nodes 1.4754 + // reported by the subtree iterator to be processed. And there 1.4755 + // are potentially a starting textnode and an ending textnode 1.4756 + // which are only partially contained by the range. 1.4757 + 1.4758 + // lets handle the nodes reported by the iterator. These nodes 1.4759 + // are entirely contained in the selection range. We build up 1.4760 + // a list of them (since doing operations on the document during 1.4761 + // iteration would perturb the iterator). 1.4762 + 1.4763 + nsCOMPtr<nsIContentIterator> iter = 1.4764 + do_CreateInstance("@mozilla.org/content/subtree-content-iterator;1", &res); 1.4765 + NS_ENSURE_SUCCESS(res, res); 1.4766 + NS_ENSURE_TRUE(iter, NS_ERROR_FAILURE); 1.4767 + 1.4768 + nsCOMArray<nsIDOMNode> arrayOfNodes; 1.4769 + nsCOMPtr<nsIDOMNode> node; 1.4770 + 1.4771 + // iterate range and build up array 1.4772 + res = iter->Init(range); 1.4773 + // init returns an error if no nodes in range. 1.4774 + // this can easily happen with the subtree 1.4775 + // iterator if the selection doesn't contain 1.4776 + // any *whole* nodes. 1.4777 + if (NS_SUCCEEDED(res)) 1.4778 + { 1.4779 + while (!iter->IsDone()) 1.4780 + { 1.4781 + node = do_QueryInterface(iter->GetCurrentNode()); 1.4782 + NS_ENSURE_TRUE(node, NS_ERROR_FAILURE); 1.4783 + 1.4784 + if (IsEditable(node)) 1.4785 + { 1.4786 + arrayOfNodes.AppendObject(node); 1.4787 + } 1.4788 + 1.4789 + iter->Next(); 1.4790 + } 1.4791 + } 1.4792 + // first check the start parent of the range to see if it needs to 1.4793 + // be separately handled (it does if it's a text node, due to how the 1.4794 + // subtree iterator works - it will not have reported it). 1.4795 + if (IsTextNode(startNode) && IsEditable(startNode)) 1.4796 + { 1.4797 + nsCOMPtr<nsIDOMNode> blockParent; 1.4798 + blockParent = GetBlockNodeParent(startNode); 1.4799 + if (cachedBlockParent != blockParent) 1.4800 + { 1.4801 + cachedBlockParent = blockParent; 1.4802 + nsCOMPtr<nsIDOMElement> element = do_QueryInterface(blockParent); 1.4803 + int32_t count; 1.4804 + res = mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(element, nullptr, &bgcolor, &aColor, &count, false); 1.4805 + NS_ENSURE_SUCCESS(res, res); 1.4806 + } 1.4807 + } 1.4808 + 1.4809 + // then loop through the list, set the property on each node 1.4810 + int32_t listCount = arrayOfNodes.Count(); 1.4811 + int32_t j; 1.4812 + for (j = 0; j < listCount; j++) 1.4813 + { 1.4814 + node = arrayOfNodes[j]; 1.4815 + // do we have a block here ? 1.4816 + bool isBlock =false; 1.4817 + res = NodeIsBlockStatic(node, &isBlock); 1.4818 + NS_ENSURE_SUCCESS(res, res); 1.4819 + nsCOMPtr<nsIDOMNode> blockParent = node; 1.4820 + if (!isBlock) { 1.4821 + // no we don't, let's find the block ancestor 1.4822 + blockParent = GetBlockNodeParent(node); 1.4823 + } 1.4824 + if (cachedBlockParent != blockParent) 1.4825 + { 1.4826 + cachedBlockParent = blockParent; 1.4827 + nsCOMPtr<nsIDOMElement> element = do_QueryInterface(blockParent); 1.4828 + int32_t count; 1.4829 + // and set the property on it 1.4830 + res = mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(element, nullptr, &bgcolor, &aColor, &count, false); 1.4831 + NS_ENSURE_SUCCESS(res, res); 1.4832 + } 1.4833 + } 1.4834 + arrayOfNodes.Clear(); 1.4835 + 1.4836 + // last check the end parent of the range to see if it needs to 1.4837 + // be separately handled (it does if it's a text node, due to how the 1.4838 + // subtree iterator works - it will not have reported it). 1.4839 + if (IsTextNode(endNode) && IsEditable(endNode)) 1.4840 + { 1.4841 + nsCOMPtr<nsIDOMNode> blockParent; 1.4842 + blockParent = GetBlockNodeParent(endNode); 1.4843 + if (cachedBlockParent != blockParent) 1.4844 + { 1.4845 + cachedBlockParent = blockParent; 1.4846 + nsCOMPtr<nsIDOMElement> element = do_QueryInterface(blockParent); 1.4847 + int32_t count; 1.4848 + res = mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(element, nullptr, &bgcolor, &aColor, &count, false); 1.4849 + NS_ENSURE_SUCCESS(res, res); 1.4850 + } 1.4851 + } 1.4852 + } 1.4853 + } 1.4854 + } 1.4855 + if (!cancel) 1.4856 + { 1.4857 + // post-process 1.4858 + res = mRules->DidDoAction(selection, &ruleInfo, res); 1.4859 + } 1.4860 + return res; 1.4861 +} 1.4862 + 1.4863 +NS_IMETHODIMP 1.4864 +nsHTMLEditor::SetBackgroundColor(const nsAString& aColor) 1.4865 +{ 1.4866 + nsresult res; 1.4867 + if (IsCSSEnabled()) { 1.4868 + // if we are in CSS mode, we have to apply the background color to the 1.4869 + // containing block (or the body if we have no block-level element in 1.4870 + // the document) 1.4871 + res = SetCSSBackgroundColor(aColor); 1.4872 + } 1.4873 + else { 1.4874 + // but in HTML mode, we can only set the document's background color 1.4875 + res = SetHTMLBackgroundColor(aColor); 1.4876 + } 1.4877 + return res; 1.4878 +} 1.4879 + 1.4880 +/////////////////////////////////////////////////////////////////////////// 1.4881 +// NodesSameType: do these nodes have the same tag? 1.4882 +// 1.4883 +/* virtual */ 1.4884 +bool 1.4885 +nsHTMLEditor::AreNodesSameType(nsIContent* aNode1, nsIContent* aNode2) 1.4886 +{ 1.4887 + MOZ_ASSERT(aNode1); 1.4888 + MOZ_ASSERT(aNode2); 1.4889 + 1.4890 + if (aNode1->Tag() != aNode2->Tag()) { 1.4891 + return false; 1.4892 + } 1.4893 + 1.4894 + if (!IsCSSEnabled() || !aNode1->IsHTML(nsGkAtoms::span)) { 1.4895 + return true; 1.4896 + } 1.4897 + 1.4898 + // If CSS is enabled, we are stricter about span nodes. 1.4899 + return mHTMLCSSUtils->ElementsSameStyle(aNode1->AsDOMNode(), 1.4900 + aNode2->AsDOMNode()); 1.4901 +} 1.4902 + 1.4903 +NS_IMETHODIMP 1.4904 +nsHTMLEditor::CopyLastEditableChildStyles(nsIDOMNode * aPreviousBlock, nsIDOMNode * aNewBlock, 1.4905 + nsIDOMNode **aOutBrNode) 1.4906 +{ 1.4907 + *aOutBrNode = nullptr; 1.4908 + nsCOMPtr<nsIDOMNode> child, tmp; 1.4909 + nsresult res; 1.4910 + // first, clear out aNewBlock. Contract is that we want only the styles from previousBlock. 1.4911 + res = aNewBlock->GetFirstChild(getter_AddRefs(child)); 1.4912 + while (NS_SUCCEEDED(res) && child) 1.4913 + { 1.4914 + res = DeleteNode(child); 1.4915 + NS_ENSURE_SUCCESS(res, res); 1.4916 + res = aNewBlock->GetFirstChild(getter_AddRefs(child)); 1.4917 + } 1.4918 + // now find and clone the styles 1.4919 + child = aPreviousBlock; 1.4920 + tmp = aPreviousBlock; 1.4921 + while (tmp) { 1.4922 + child = tmp; 1.4923 + res = GetLastEditableChild(child, address_of(tmp)); 1.4924 + NS_ENSURE_SUCCESS(res, res); 1.4925 + } 1.4926 + while (child && nsTextEditUtils::IsBreak(child)) { 1.4927 + nsCOMPtr<nsIDOMNode> priorNode; 1.4928 + res = GetPriorHTMLNode(child, address_of(priorNode)); 1.4929 + NS_ENSURE_SUCCESS(res, res); 1.4930 + child = priorNode; 1.4931 + } 1.4932 + nsCOMPtr<nsIDOMNode> newStyles = nullptr, deepestStyle = nullptr; 1.4933 + while (child && (child != aPreviousBlock)) { 1.4934 + if (nsHTMLEditUtils::IsInlineStyle(child) || 1.4935 + nsEditor::NodeIsType(child, nsEditProperty::span)) { 1.4936 + nsAutoString domTagName; 1.4937 + child->GetNodeName(domTagName); 1.4938 + ToLowerCase(domTagName); 1.4939 + if (newStyles) { 1.4940 + nsCOMPtr<nsIDOMNode> newContainer; 1.4941 + res = InsertContainerAbove(newStyles, address_of(newContainer), domTagName); 1.4942 + NS_ENSURE_SUCCESS(res, res); 1.4943 + newStyles = newContainer; 1.4944 + } 1.4945 + else { 1.4946 + res = CreateNode(domTagName, aNewBlock, 0, getter_AddRefs(newStyles)); 1.4947 + NS_ENSURE_SUCCESS(res, res); 1.4948 + deepestStyle = newStyles; 1.4949 + } 1.4950 + res = CloneAttributes(newStyles, child); 1.4951 + NS_ENSURE_SUCCESS(res, res); 1.4952 + } 1.4953 + nsCOMPtr<nsIDOMNode> tmp; 1.4954 + res = child->GetParentNode(getter_AddRefs(tmp)); 1.4955 + NS_ENSURE_SUCCESS(res, res); 1.4956 + child = tmp; 1.4957 + } 1.4958 + if (deepestStyle) { 1.4959 + nsCOMPtr<nsIDOMNode> outBRNode; 1.4960 + res = CreateBR(deepestStyle, 0, address_of(outBRNode)); 1.4961 + NS_ENSURE_SUCCESS(res, res); 1.4962 + // Getters must addref 1.4963 + outBRNode.forget(aOutBrNode); 1.4964 + } 1.4965 + return NS_OK; 1.4966 +} 1.4967 + 1.4968 +nsresult 1.4969 +nsHTMLEditor::GetElementOrigin(nsIDOMElement * aElement, int32_t & aX, int32_t & aY) 1.4970 +{ 1.4971 + aX = 0; 1.4972 + aY = 0; 1.4973 + 1.4974 + NS_ENSURE_TRUE(mDocWeak, NS_ERROR_NOT_INITIALIZED); 1.4975 + nsCOMPtr<nsIPresShell> ps = GetPresShell(); 1.4976 + NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED); 1.4977 + 1.4978 + nsCOMPtr<nsIContent> content = do_QueryInterface(aElement); 1.4979 + nsIFrame *frame = content->GetPrimaryFrame(); 1.4980 + NS_ENSURE_TRUE(frame, NS_OK); 1.4981 + 1.4982 + nsIFrame *container = ps->GetAbsoluteContainingBlock(frame); 1.4983 + NS_ENSURE_TRUE(container, NS_OK); 1.4984 + nsPoint off = frame->GetOffsetTo(container); 1.4985 + aX = nsPresContext::AppUnitsToIntCSSPixels(off.x); 1.4986 + aY = nsPresContext::AppUnitsToIntCSSPixels(off.y); 1.4987 + 1.4988 + return NS_OK; 1.4989 +} 1.4990 + 1.4991 +nsresult 1.4992 +nsHTMLEditor::EndUpdateViewBatch() 1.4993 +{ 1.4994 + nsresult res = nsEditor::EndUpdateViewBatch(); 1.4995 + NS_ENSURE_SUCCESS(res, res); 1.4996 + 1.4997 + // We may need to show resizing handles or update existing ones after 1.4998 + // all transactions are done. This way of doing is preferred to DOM 1.4999 + // mutation events listeners because all the changes the user can apply 1.5000 + // to a document may result in multiple events, some of them quite hard 1.5001 + // to listen too (in particular when an ancestor of the selection is 1.5002 + // changed but the selection itself is not changed). 1.5003 + if (mUpdateCount == 0) { 1.5004 + nsCOMPtr<nsISelection> selection; 1.5005 + res = GetSelection(getter_AddRefs(selection)); 1.5006 + NS_ENSURE_SUCCESS(res, res); 1.5007 + NS_ENSURE_TRUE(selection, NS_ERROR_NOT_INITIALIZED); 1.5008 + res = CheckSelectionStateForAnonymousButtons(selection); 1.5009 + } 1.5010 + return res; 1.5011 +} 1.5012 + 1.5013 +NS_IMETHODIMP 1.5014 +nsHTMLEditor::GetSelectionContainer(nsIDOMElement ** aReturn) 1.5015 +{ 1.5016 + nsCOMPtr<nsISelection>selection; 1.5017 + nsresult res = GetSelection(getter_AddRefs(selection)); 1.5018 + // if we don't get the selection, just skip this 1.5019 + if (NS_FAILED(res) || !selection) return res; 1.5020 + 1.5021 + nsCOMPtr<nsIDOMNode> focusNode; 1.5022 + 1.5023 + if (selection->Collapsed()) { 1.5024 + res = selection->GetFocusNode(getter_AddRefs(focusNode)); 1.5025 + NS_ENSURE_SUCCESS(res, res); 1.5026 + } else { 1.5027 + 1.5028 + int32_t rangeCount; 1.5029 + res = selection->GetRangeCount(&rangeCount); 1.5030 + NS_ENSURE_SUCCESS(res, res); 1.5031 + 1.5032 + if (rangeCount == 1) { 1.5033 + 1.5034 + nsCOMPtr<nsIDOMRange> range; 1.5035 + res = selection->GetRangeAt(0, getter_AddRefs(range)); 1.5036 + NS_ENSURE_SUCCESS(res, res); 1.5037 + NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER); 1.5038 + 1.5039 + nsCOMPtr<nsIDOMNode> startContainer, endContainer; 1.5040 + res = range->GetStartContainer(getter_AddRefs(startContainer)); 1.5041 + NS_ENSURE_SUCCESS(res, res); 1.5042 + res = range->GetEndContainer(getter_AddRefs(endContainer)); 1.5043 + NS_ENSURE_SUCCESS(res, res); 1.5044 + int32_t startOffset, endOffset; 1.5045 + res = range->GetStartOffset(&startOffset); 1.5046 + NS_ENSURE_SUCCESS(res, res); 1.5047 + res = range->GetEndOffset(&endOffset); 1.5048 + NS_ENSURE_SUCCESS(res, res); 1.5049 + 1.5050 + nsCOMPtr<nsIDOMElement> focusElement; 1.5051 + if (startContainer == endContainer && startOffset + 1 == endOffset) { 1.5052 + res = GetSelectedElement(EmptyString(), getter_AddRefs(focusElement)); 1.5053 + NS_ENSURE_SUCCESS(res, res); 1.5054 + if (focusElement) 1.5055 + focusNode = do_QueryInterface(focusElement); 1.5056 + } 1.5057 + if (!focusNode) { 1.5058 + res = range->GetCommonAncestorContainer(getter_AddRefs(focusNode)); 1.5059 + NS_ENSURE_SUCCESS(res, res); 1.5060 + } 1.5061 + } 1.5062 + else { 1.5063 + int32_t i; 1.5064 + nsCOMPtr<nsIDOMRange> range; 1.5065 + for (i = 0; i < rangeCount; i++) 1.5066 + { 1.5067 + res = selection->GetRangeAt(i, getter_AddRefs(range)); 1.5068 + NS_ENSURE_SUCCESS(res, res); 1.5069 + nsCOMPtr<nsIDOMNode> startContainer; 1.5070 + res = range->GetStartContainer(getter_AddRefs(startContainer)); 1.5071 + if (NS_FAILED(res)) continue; 1.5072 + if (!focusNode) 1.5073 + focusNode = startContainer; 1.5074 + else if (focusNode != startContainer) { 1.5075 + res = startContainer->GetParentNode(getter_AddRefs(focusNode)); 1.5076 + NS_ENSURE_SUCCESS(res, res); 1.5077 + break; 1.5078 + } 1.5079 + } 1.5080 + } 1.5081 + } 1.5082 + 1.5083 + if (focusNode) { 1.5084 + uint16_t nodeType; 1.5085 + focusNode->GetNodeType(&nodeType); 1.5086 + if (nsIDOMNode::TEXT_NODE == nodeType) { 1.5087 + nsCOMPtr<nsIDOMNode> parent; 1.5088 + res = focusNode->GetParentNode(getter_AddRefs(parent)); 1.5089 + NS_ENSURE_SUCCESS(res, res); 1.5090 + focusNode = parent; 1.5091 + } 1.5092 + } 1.5093 + 1.5094 + nsCOMPtr<nsIDOMElement> focusElement = do_QueryInterface(focusNode); 1.5095 + focusElement.forget(aReturn); 1.5096 + return NS_OK; 1.5097 +} 1.5098 + 1.5099 +NS_IMETHODIMP 1.5100 +nsHTMLEditor::IsAnonymousElement(nsIDOMElement * aElement, bool * aReturn) 1.5101 +{ 1.5102 + NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER); 1.5103 + nsCOMPtr<nsIContent> content = do_QueryInterface(aElement); 1.5104 + *aReturn = content->IsRootOfNativeAnonymousSubtree(); 1.5105 + return NS_OK; 1.5106 +} 1.5107 + 1.5108 +nsresult 1.5109 +nsHTMLEditor::SetReturnInParagraphCreatesNewParagraph(bool aCreatesNewParagraph) 1.5110 +{ 1.5111 + mCRInParagraphCreatesParagraph = aCreatesNewParagraph; 1.5112 + return NS_OK; 1.5113 +} 1.5114 + 1.5115 +bool 1.5116 +nsHTMLEditor::GetReturnInParagraphCreatesNewParagraph() 1.5117 +{ 1.5118 + return mCRInParagraphCreatesParagraph; 1.5119 +} 1.5120 + 1.5121 +nsresult 1.5122 +nsHTMLEditor::GetReturnInParagraphCreatesNewParagraph(bool *aCreatesNewParagraph) 1.5123 +{ 1.5124 + *aCreatesNewParagraph = mCRInParagraphCreatesParagraph; 1.5125 + return NS_OK; 1.5126 +} 1.5127 + 1.5128 +already_AddRefed<nsIContent> 1.5129 +nsHTMLEditor::GetFocusedContent() 1.5130 +{ 1.5131 + NS_ENSURE_TRUE(mDocWeak, nullptr); 1.5132 + 1.5133 + nsFocusManager* fm = nsFocusManager::GetFocusManager(); 1.5134 + NS_ENSURE_TRUE(fm, nullptr); 1.5135 + 1.5136 + nsCOMPtr<nsIContent> focusedContent = fm->GetFocusedContent(); 1.5137 + 1.5138 + nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak); 1.5139 + bool inDesignMode = doc->HasFlag(NODE_IS_EDITABLE); 1.5140 + if (!focusedContent) { 1.5141 + // in designMode, nobody gets focus in most cases. 1.5142 + if (inDesignMode && OurWindowHasFocus()) { 1.5143 + nsCOMPtr<nsIContent> docRoot = doc->GetRootElement(); 1.5144 + return docRoot.forget(); 1.5145 + } 1.5146 + return nullptr; 1.5147 + } 1.5148 + 1.5149 + if (inDesignMode) { 1.5150 + return OurWindowHasFocus() && 1.5151 + nsContentUtils::ContentIsDescendantOf(focusedContent, doc) ? 1.5152 + focusedContent.forget() : nullptr; 1.5153 + } 1.5154 + 1.5155 + // We're HTML editor for contenteditable 1.5156 + 1.5157 + // If the focused content isn't editable, or it has independent selection, 1.5158 + // we don't have focus. 1.5159 + if (!focusedContent->HasFlag(NODE_IS_EDITABLE) || 1.5160 + focusedContent->HasIndependentSelection()) { 1.5161 + return nullptr; 1.5162 + } 1.5163 + // If our window is focused, we're focused. 1.5164 + return OurWindowHasFocus() ? focusedContent.forget() : nullptr; 1.5165 +} 1.5166 + 1.5167 +already_AddRefed<nsIContent> 1.5168 +nsHTMLEditor::GetFocusedContentForIME() 1.5169 +{ 1.5170 + nsCOMPtr<nsIContent> focusedContent = GetFocusedContent(); 1.5171 + if (!focusedContent) { 1.5172 + return nullptr; 1.5173 + } 1.5174 + 1.5175 + nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak); 1.5176 + NS_ENSURE_TRUE(doc, nullptr); 1.5177 + return doc->HasFlag(NODE_IS_EDITABLE) ? nullptr : focusedContent.forget(); 1.5178 +} 1.5179 + 1.5180 +bool 1.5181 +nsHTMLEditor::IsActiveInDOMWindow() 1.5182 +{ 1.5183 + NS_ENSURE_TRUE(mDocWeak, false); 1.5184 + 1.5185 + nsFocusManager* fm = nsFocusManager::GetFocusManager(); 1.5186 + NS_ENSURE_TRUE(fm, false); 1.5187 + 1.5188 + nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak); 1.5189 + bool inDesignMode = doc->HasFlag(NODE_IS_EDITABLE); 1.5190 + 1.5191 + // If we're in designMode, we're always active in the DOM window. 1.5192 + if (inDesignMode) { 1.5193 + return true; 1.5194 + } 1.5195 + 1.5196 + nsPIDOMWindow* ourWindow = doc->GetWindow(); 1.5197 + nsCOMPtr<nsPIDOMWindow> win; 1.5198 + nsIContent* content = 1.5199 + nsFocusManager::GetFocusedDescendant(ourWindow, false, 1.5200 + getter_AddRefs(win)); 1.5201 + if (!content) { 1.5202 + return false; 1.5203 + } 1.5204 + 1.5205 + // We're HTML editor for contenteditable 1.5206 + 1.5207 + // If the active content isn't editable, or it has independent selection, 1.5208 + // we're not active). 1.5209 + if (!content->HasFlag(NODE_IS_EDITABLE) || 1.5210 + content->HasIndependentSelection()) { 1.5211 + return false; 1.5212 + } 1.5213 + return true; 1.5214 +} 1.5215 + 1.5216 +dom::Element* 1.5217 +nsHTMLEditor::GetActiveEditingHost() 1.5218 +{ 1.5219 + NS_ENSURE_TRUE(mDocWeak, nullptr); 1.5220 + 1.5221 + nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak); 1.5222 + NS_ENSURE_TRUE(doc, nullptr); 1.5223 + if (doc->HasFlag(NODE_IS_EDITABLE)) { 1.5224 + return doc->GetBodyElement(); 1.5225 + } 1.5226 + 1.5227 + // We're HTML editor for contenteditable 1.5228 + nsCOMPtr<nsISelection> selection; 1.5229 + nsresult rv = GetSelection(getter_AddRefs(selection)); 1.5230 + NS_ENSURE_SUCCESS(rv, nullptr); 1.5231 + nsCOMPtr<nsIDOMNode> focusNode; 1.5232 + rv = selection->GetFocusNode(getter_AddRefs(focusNode)); 1.5233 + NS_ENSURE_SUCCESS(rv, nullptr); 1.5234 + nsCOMPtr<nsIContent> content = do_QueryInterface(focusNode); 1.5235 + if (!content) { 1.5236 + return nullptr; 1.5237 + } 1.5238 + 1.5239 + // If the active content isn't editable, or it has independent selection, 1.5240 + // we're not active. 1.5241 + if (!content->HasFlag(NODE_IS_EDITABLE) || 1.5242 + content->HasIndependentSelection()) { 1.5243 + return nullptr; 1.5244 + } 1.5245 + return content->GetEditingHost(); 1.5246 +} 1.5247 + 1.5248 +already_AddRefed<mozilla::dom::EventTarget> 1.5249 +nsHTMLEditor::GetDOMEventTarget() 1.5250 +{ 1.5251 + // Don't use getDocument here, because we have no way of knowing 1.5252 + // whether Init() was ever called. So we need to get the document 1.5253 + // ourselves, if it exists. 1.5254 + NS_PRECONDITION(mDocWeak, "This editor has not been initialized yet"); 1.5255 + nsCOMPtr<mozilla::dom::EventTarget> target = do_QueryReferent(mDocWeak); 1.5256 + return target.forget(); 1.5257 +} 1.5258 + 1.5259 +bool 1.5260 +nsHTMLEditor::ShouldReplaceRootElement() 1.5261 +{ 1.5262 + if (!mRootElement) { 1.5263 + // If we don't know what is our root element, we should find our root. 1.5264 + return true; 1.5265 + } 1.5266 + 1.5267 + // If we temporary set document root element to mRootElement, but there is 1.5268 + // body element now, we should replace the root element by the body element. 1.5269 + nsCOMPtr<nsIDOMHTMLElement> docBody; 1.5270 + GetBodyElement(getter_AddRefs(docBody)); 1.5271 + return !SameCOMIdentity(docBody, mRootElement); 1.5272 +} 1.5273 + 1.5274 +void 1.5275 +nsHTMLEditor::ResetRootElementAndEventTarget() 1.5276 +{ 1.5277 + nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); 1.5278 + 1.5279 + // Need to remove the event listeners first because BeginningOfDocument 1.5280 + // could set a new root (and event target is set by InstallEventListeners()) 1.5281 + // and we won't be able to remove them from the old event target then. 1.5282 + RemoveEventListeners(); 1.5283 + mRootElement = nullptr; 1.5284 + nsresult rv = InstallEventListeners(); 1.5285 + if (NS_FAILED(rv)) { 1.5286 + return; 1.5287 + } 1.5288 + 1.5289 + // We must have mRootElement now. 1.5290 + nsCOMPtr<nsIDOMElement> root; 1.5291 + rv = GetRootElement(getter_AddRefs(root)); 1.5292 + if (NS_FAILED(rv) || !mRootElement) { 1.5293 + return; 1.5294 + } 1.5295 + 1.5296 + rv = BeginningOfDocument(); 1.5297 + if (NS_FAILED(rv)) { 1.5298 + return; 1.5299 + } 1.5300 + 1.5301 + // When this editor has focus, we need to reset the selection limiter to 1.5302 + // new root. Otherwise, that is going to be done when this gets focus. 1.5303 + nsCOMPtr<nsINode> node = GetFocusedNode(); 1.5304 + nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(node); 1.5305 + if (target) { 1.5306 + InitializeSelection(target); 1.5307 + } 1.5308 + 1.5309 + SyncRealTimeSpell(); 1.5310 +} 1.5311 + 1.5312 +nsresult 1.5313 +nsHTMLEditor::GetBodyElement(nsIDOMHTMLElement** aBody) 1.5314 +{ 1.5315 + NS_PRECONDITION(mDocWeak, "bad state, null mDocWeak"); 1.5316 + nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryReferent(mDocWeak); 1.5317 + if (!htmlDoc) { 1.5318 + return NS_ERROR_NOT_INITIALIZED; 1.5319 + } 1.5320 + return htmlDoc->GetBody(aBody); 1.5321 +} 1.5322 + 1.5323 +already_AddRefed<nsINode> 1.5324 +nsHTMLEditor::GetFocusedNode() 1.5325 +{ 1.5326 + nsCOMPtr<nsIContent> focusedContent = GetFocusedContent(); 1.5327 + if (!focusedContent) { 1.5328 + return nullptr; 1.5329 + } 1.5330 + 1.5331 + nsIFocusManager* fm = nsFocusManager::GetFocusManager(); 1.5332 + NS_ASSERTION(fm, "Focus manager is null"); 1.5333 + nsCOMPtr<nsIDOMElement> focusedElement; 1.5334 + fm->GetFocusedElement(getter_AddRefs(focusedElement)); 1.5335 + if (focusedElement) { 1.5336 + nsCOMPtr<nsINode> node = do_QueryInterface(focusedElement); 1.5337 + return node.forget(); 1.5338 + } 1.5339 + 1.5340 + nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak); 1.5341 + return doc.forget(); 1.5342 +} 1.5343 + 1.5344 +bool 1.5345 +nsHTMLEditor::OurWindowHasFocus() 1.5346 +{ 1.5347 + NS_ENSURE_TRUE(mDocWeak, false); 1.5348 + nsIFocusManager* fm = nsFocusManager::GetFocusManager(); 1.5349 + NS_ENSURE_TRUE(fm, false); 1.5350 + nsCOMPtr<nsIDOMWindow> focusedWindow; 1.5351 + fm->GetFocusedWindow(getter_AddRefs(focusedWindow)); 1.5352 + if (!focusedWindow) { 1.5353 + return false; 1.5354 + } 1.5355 + nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak); 1.5356 + nsCOMPtr<nsIDOMWindow> ourWindow = do_QueryInterface(doc->GetWindow()); 1.5357 + return ourWindow == focusedWindow; 1.5358 +} 1.5359 + 1.5360 +bool 1.5361 +nsHTMLEditor::IsAcceptableInputEvent(nsIDOMEvent* aEvent) 1.5362 +{ 1.5363 + if (!nsEditor::IsAcceptableInputEvent(aEvent)) { 1.5364 + return false; 1.5365 + } 1.5366 + 1.5367 + NS_ENSURE_TRUE(mDocWeak, false); 1.5368 + 1.5369 + nsCOMPtr<nsIDOMEventTarget> target; 1.5370 + aEvent->GetTarget(getter_AddRefs(target)); 1.5371 + NS_ENSURE_TRUE(target, false); 1.5372 + 1.5373 + nsCOMPtr<nsIDocument> document = do_QueryReferent(mDocWeak); 1.5374 + if (document->HasFlag(NODE_IS_EDITABLE)) { 1.5375 + // If this editor is in designMode and the event target is the document, 1.5376 + // the event is for this editor. 1.5377 + nsCOMPtr<nsIDocument> targetDocument = do_QueryInterface(target); 1.5378 + if (targetDocument) { 1.5379 + return targetDocument == document; 1.5380 + } 1.5381 + // Otherwise, check whether the event target is in this document or not. 1.5382 + nsCOMPtr<nsIContent> targetContent = do_QueryInterface(target); 1.5383 + NS_ENSURE_TRUE(targetContent, false); 1.5384 + return document == targetContent->GetCurrentDoc(); 1.5385 + } 1.5386 + 1.5387 + // This HTML editor is for contenteditable. We need to check the validity of 1.5388 + // the target. 1.5389 + nsCOMPtr<nsIContent> targetContent = do_QueryInterface(target); 1.5390 + NS_ENSURE_TRUE(targetContent, false); 1.5391 + 1.5392 + // If the event is a mouse event, we need to check if the target content is 1.5393 + // the focused editing host or its descendant. 1.5394 + nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aEvent); 1.5395 + if (mouseEvent) { 1.5396 + nsIContent* editingHost = GetActiveEditingHost(); 1.5397 + // If there is no active editing host, we cannot handle the mouse event 1.5398 + // correctly. 1.5399 + if (!editingHost) { 1.5400 + return false; 1.5401 + } 1.5402 + // If clicked on non-editable root element but the body element is the 1.5403 + // active editing host, we should assume that the click event is targetted. 1.5404 + if (targetContent == document->GetRootElement() && 1.5405 + !targetContent->HasFlag(NODE_IS_EDITABLE) && 1.5406 + editingHost == document->GetBodyElement()) { 1.5407 + targetContent = editingHost; 1.5408 + } 1.5409 + // If the target element is neither the active editing host nor a descendant 1.5410 + // of it, we may not be able to handle the event. 1.5411 + if (!nsContentUtils::ContentIsDescendantOf(targetContent, editingHost)) { 1.5412 + return false; 1.5413 + } 1.5414 + // If the clicked element has an independent selection, we shouldn't 1.5415 + // handle this click event. 1.5416 + if (targetContent->HasIndependentSelection()) { 1.5417 + return false; 1.5418 + } 1.5419 + // If the target content is editable, we should handle this event. 1.5420 + return targetContent->HasFlag(NODE_IS_EDITABLE); 1.5421 + } 1.5422 + 1.5423 + // If the target of the other events which target focused element isn't 1.5424 + // editable or has an independent selection, this editor shouldn't handle the 1.5425 + // event. 1.5426 + if (!targetContent->HasFlag(NODE_IS_EDITABLE) || 1.5427 + targetContent->HasIndependentSelection()) { 1.5428 + return false; 1.5429 + } 1.5430 + 1.5431 + // Finally, check whether we're actually focused or not. When we're not 1.5432 + // focused, we should ignore the dispatched event by script (or something) 1.5433 + // because content editable element needs selection in itself for editing. 1.5434 + // However, when we're not focused, it's not guaranteed. 1.5435 + return IsActiveInDOMWindow(); 1.5436 +} 1.5437 + 1.5438 +NS_IMETHODIMP 1.5439 +nsHTMLEditor::GetPreferredIMEState(IMEState *aState) 1.5440 +{ 1.5441 + // HTML editor don't prefer the CSS ime-mode because IE didn't do so too. 1.5442 + aState->mOpen = IMEState::DONT_CHANGE_OPEN_STATE; 1.5443 + if (IsReadonly() || IsDisabled()) { 1.5444 + aState->mEnabled = IMEState::DISABLED; 1.5445 + } else { 1.5446 + aState->mEnabled = IMEState::ENABLED; 1.5447 + } 1.5448 + return NS_OK; 1.5449 +} 1.5450 + 1.5451 +already_AddRefed<nsIContent> 1.5452 +nsHTMLEditor::GetInputEventTargetContent() 1.5453 +{ 1.5454 + nsCOMPtr<nsIContent> target = GetActiveEditingHost(); 1.5455 + return target.forget(); 1.5456 +} 1.5457 + 1.5458 +bool 1.5459 +nsHTMLEditor::IsEditable(nsIContent* aNode) { 1.5460 + if (!nsPlaintextEditor::IsEditable(aNode)) { 1.5461 + return false; 1.5462 + } 1.5463 + if (aNode->IsElement()) { 1.5464 + // If we're dealing with an element, then ask it whether it's editable. 1.5465 + return aNode->IsEditable(); 1.5466 + } 1.5467 + // We might be dealing with a text node for example, which we always consider 1.5468 + // to be editable. 1.5469 + return true; 1.5470 +} 1.5471 + 1.5472 +// virtual MOZ_OVERRIDE 1.5473 +dom::Element* 1.5474 +nsHTMLEditor::GetEditorRoot() 1.5475 +{ 1.5476 + return GetActiveEditingHost(); 1.5477 +}