1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/editor/libeditor/base/nsEditor.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,5257 @@ 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" // for DebugOnly 1.10 + 1.11 +#include <stdio.h> // for nullptr, stdout 1.12 +#include <string.h> // for strcmp 1.13 + 1.14 +#include "ChangeAttributeTxn.h" // for ChangeAttributeTxn 1.15 +#include "CreateElementTxn.h" // for CreateElementTxn 1.16 +#include "DeleteNodeTxn.h" // for DeleteNodeTxn 1.17 +#include "DeleteRangeTxn.h" // for DeleteRangeTxn 1.18 +#include "DeleteTextTxn.h" // for DeleteTextTxn 1.19 +#include "EditAggregateTxn.h" // for EditAggregateTxn 1.20 +#include "EditTxn.h" // for EditTxn 1.21 +#include "IMETextTxn.h" // for IMETextTxn 1.22 +#include "InsertElementTxn.h" // for InsertElementTxn 1.23 +#include "InsertTextTxn.h" // for InsertTextTxn 1.24 +#include "JoinElementTxn.h" // for JoinElementTxn 1.25 +#include "PlaceholderTxn.h" // for PlaceholderTxn 1.26 +#include "SplitElementTxn.h" // for SplitElementTxn 1.27 +#include "mozFlushType.h" // for mozFlushType::Flush_Frames 1.28 +#include "mozISpellCheckingEngine.h" 1.29 +#include "mozInlineSpellChecker.h" // for mozInlineSpellChecker 1.30 +#include "mozilla/IMEStateManager.h" // for IMEStateManager 1.31 +#include "mozilla/Preferences.h" // for Preferences 1.32 +#include "mozilla/dom/Selection.h" // for Selection, etc 1.33 +#include "mozilla/Services.h" // for GetObserverService 1.34 +#include "mozilla/TextComposition.h" // for TextComposition 1.35 +#include "mozilla/TextEvents.h" 1.36 +#include "mozilla/dom/Element.h" // for Element, nsINode::AsElement 1.37 +#include "mozilla/mozalloc.h" // for operator new, etc 1.38 +#include "nsAString.h" // for nsAString_internal::Length, etc 1.39 +#include "nsCCUncollectableMarker.h" // for nsCCUncollectableMarker 1.40 +#include "nsCaret.h" // for nsCaret 1.41 +#include "nsCaseTreatment.h" 1.42 +#include "nsCharTraits.h" // for NS_IS_HIGH_SURROGATE, etc 1.43 +#include "nsComponentManagerUtils.h" // for do_CreateInstance 1.44 +#include "nsComputedDOMStyle.h" // for nsComputedDOMStyle 1.45 +#include "nsContentUtils.h" // for nsContentUtils 1.46 +#include "nsDOMString.h" // for DOMStringIsNull 1.47 +#include "nsDebug.h" // for NS_ENSURE_TRUE, etc 1.48 +#include "nsEditProperty.h" // for nsEditProperty, etc 1.49 +#include "nsEditor.h" 1.50 +#include "nsEditorEventListener.h" // for nsEditorEventListener 1.51 +#include "nsEditorUtils.h" // for nsAutoRules, etc 1.52 +#include "nsError.h" // for NS_OK, etc 1.53 +#include "nsFocusManager.h" // for nsFocusManager 1.54 +#include "nsFrameSelection.h" // for nsFrameSelection 1.55 +#include "nsGkAtoms.h" // for nsGkAtoms, nsGkAtoms::dir 1.56 +#include "nsIAbsorbingTransaction.h" // for nsIAbsorbingTransaction 1.57 +#include "nsIAtom.h" // for nsIAtom 1.58 +#include "nsIContent.h" // for nsIContent 1.59 +#include "nsIDOMAttr.h" // for nsIDOMAttr 1.60 +#include "nsIDOMCharacterData.h" // for nsIDOMCharacterData 1.61 +#include "nsIDOMDocument.h" // for nsIDOMDocument 1.62 +#include "nsIDOMElement.h" // for nsIDOMElement 1.63 +#include "nsIDOMEvent.h" // for nsIDOMEvent 1.64 +#include "nsIDOMEventListener.h" // for nsIDOMEventListener 1.65 +#include "nsIDOMEventTarget.h" // for nsIDOMEventTarget 1.66 +#include "nsIDOMHTMLElement.h" // for nsIDOMHTMLElement 1.67 +#include "nsIDOMKeyEvent.h" // for nsIDOMKeyEvent, etc 1.68 +#include "nsIDOMMozNamedAttrMap.h" // for nsIDOMMozNamedAttrMap 1.69 +#include "nsIDOMMouseEvent.h" // for nsIDOMMouseEvent 1.70 +#include "nsIDOMNode.h" // for nsIDOMNode, etc 1.71 +#include "nsIDOMNodeList.h" // for nsIDOMNodeList 1.72 +#include "nsIDOMRange.h" // for nsIDOMRange 1.73 +#include "nsIDOMText.h" // for nsIDOMText 1.74 +#include "nsIDocument.h" // for nsIDocument 1.75 +#include "nsIDocumentStateListener.h" // for nsIDocumentStateListener 1.76 +#include "nsIEditActionListener.h" // for nsIEditActionListener 1.77 +#include "nsIEditorObserver.h" // for nsIEditorObserver 1.78 +#include "nsIEditorSpellCheck.h" // for nsIEditorSpellCheck 1.79 +#include "nsIFrame.h" // for nsIFrame 1.80 +#include "nsIHTMLDocument.h" // for nsIHTMLDocument 1.81 +#include "nsIInlineSpellChecker.h" // for nsIInlineSpellChecker, etc 1.82 +#include "nsNameSpaceManager.h" // for kNameSpaceID_None, etc 1.83 +#include "nsINode.h" // for nsINode, etc 1.84 +#include "nsIObserverService.h" // for nsIObserverService 1.85 +#include "nsIPlaintextEditor.h" // for nsIPlaintextEditor, etc 1.86 +#include "nsIPresShell.h" // for nsIPresShell 1.87 +#include "nsISelection.h" // for nsISelection, etc 1.88 +#include "nsISelectionController.h" // for nsISelectionController, etc 1.89 +#include "nsISelectionDisplay.h" // for nsISelectionDisplay, etc 1.90 +#include "nsISelectionPrivate.h" // for nsISelectionPrivate, etc 1.91 +#include "nsISupportsBase.h" // for nsISupports 1.92 +#include "nsISupportsUtils.h" // for NS_ADDREF, NS_IF_ADDREF 1.93 +#include "nsITransaction.h" // for nsITransaction 1.94 +#include "nsITransactionManager.h" 1.95 +#include "nsIWeakReference.h" // for nsISupportsWeakReference 1.96 +#include "nsIWidget.h" // for nsIWidget, IMEState, etc 1.97 +#include "nsPIDOMWindow.h" // for nsPIDOMWindow 1.98 +#include "nsPresContext.h" // for nsPresContext 1.99 +#include "nsRange.h" // for nsRange 1.100 +#include "nsReadableUtils.h" // for EmptyString, ToNewCString 1.101 +#include "nsString.h" // for nsAutoString, nsString, etc 1.102 +#include "nsStringFwd.h" // for nsAFlatString 1.103 +#include "nsStyleConsts.h" // for NS_STYLE_DIRECTION_RTL, etc 1.104 +#include "nsStyleContext.h" // for nsStyleContext 1.105 +#include "nsStyleSheetTxns.h" // for AddStyleSheetTxn, etc 1.106 +#include "nsStyleStruct.h" // for nsStyleDisplay, nsStyleText, etc 1.107 +#include "nsStyleStructFwd.h" // for nsIFrame::StyleUIReset, etc 1.108 +#include "nsTextEditUtils.h" // for nsTextEditUtils 1.109 +#include "nsTextNode.h" // for nsTextNode 1.110 +#include "nsThreadUtils.h" // for nsRunnable 1.111 +#include "nsTransactionManager.h" // for nsTransactionManager 1.112 +#include "prtime.h" // for PR_Now 1.113 + 1.114 +class nsIOutputStream; 1.115 +class nsIParserService; 1.116 +class nsITransferable; 1.117 + 1.118 +#ifdef DEBUG 1.119 +#include "nsIDOMHTMLDocument.h" // for nsIDOMHTMLDocument 1.120 +#endif 1.121 + 1.122 +using namespace mozilla; 1.123 +using namespace mozilla::dom; 1.124 +using namespace mozilla::widget; 1.125 + 1.126 +// Defined in nsEditorRegistration.cpp 1.127 +extern nsIParserService *sParserService; 1.128 + 1.129 +//--------------------------------------------------------------------------- 1.130 +// 1.131 +// nsEditor: base editor class implementation 1.132 +// 1.133 +//--------------------------------------------------------------------------- 1.134 + 1.135 +nsEditor::nsEditor() 1.136 +: mPlaceHolderName(nullptr) 1.137 +, mSelState(nullptr) 1.138 +, mPhonetic(nullptr) 1.139 +, mModCount(0) 1.140 +, mFlags(0) 1.141 +, mUpdateCount(0) 1.142 +, mPlaceHolderBatch(0) 1.143 +, mAction(EditAction::none) 1.144 +, mIMETextOffset(0) 1.145 +, mDirection(eNone) 1.146 +, mDocDirtyState(-1) 1.147 +, mSpellcheckCheckboxState(eTriUnset) 1.148 +, mShouldTxnSetSelection(true) 1.149 +, mDidPreDestroy(false) 1.150 +, mDidPostCreate(false) 1.151 +, mDispatchInputEvent(true) 1.152 +{ 1.153 +} 1.154 + 1.155 +nsEditor::~nsEditor() 1.156 +{ 1.157 + NS_ASSERTION(!mDocWeak || mDidPreDestroy, "Why PreDestroy hasn't been called?"); 1.158 + 1.159 + mTxnMgr = nullptr; 1.160 + 1.161 + delete mPhonetic; 1.162 +} 1.163 + 1.164 +NS_IMPL_CYCLE_COLLECTION_CLASS(nsEditor) 1.165 + 1.166 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsEditor) 1.167 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mRootElement) 1.168 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mInlineSpellChecker) 1.169 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mTxnMgr) 1.170 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mIMETextNode) 1.171 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mActionListeners) 1.172 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditorObservers) 1.173 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocStateListeners) 1.174 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mEventTarget) 1.175 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mEventListener) 1.176 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.177 + 1.178 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsEditor) 1.179 + nsIDocument* currentDoc = 1.180 + tmp->mRootElement ? tmp->mRootElement->GetCurrentDoc() : nullptr; 1.181 + if (currentDoc && 1.182 + nsCCUncollectableMarker::InGeneration(cb, currentDoc->GetMarkedCCGeneration())) { 1.183 + return NS_SUCCESS_INTERRUPTED_TRAVERSE; 1.184 + } 1.185 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootElement) 1.186 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInlineSpellChecker) 1.187 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTxnMgr) 1.188 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIMETextNode) 1.189 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActionListeners) 1.190 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditorObservers) 1.191 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocStateListeners) 1.192 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventTarget) 1.193 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventListener) 1.194 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.195 + 1.196 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsEditor) 1.197 + NS_INTERFACE_MAP_ENTRY(nsIPhonetic) 1.198 + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 1.199 + NS_INTERFACE_MAP_ENTRY(nsIEditorIMESupport) 1.200 + NS_INTERFACE_MAP_ENTRY(nsIEditor) 1.201 + NS_INTERFACE_MAP_ENTRY(nsIObserver) 1.202 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditor) 1.203 +NS_INTERFACE_MAP_END 1.204 + 1.205 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsEditor) 1.206 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsEditor) 1.207 + 1.208 + 1.209 +NS_IMETHODIMP 1.210 +nsEditor::Init(nsIDOMDocument *aDoc, nsIContent *aRoot, 1.211 + nsISelectionController *aSelCon, uint32_t aFlags, 1.212 + const nsAString& aValue) 1.213 +{ 1.214 + NS_PRECONDITION(aDoc, "bad arg"); 1.215 + if (!aDoc) 1.216 + return NS_ERROR_NULL_POINTER; 1.217 + 1.218 + // First only set flags, but other stuff shouldn't be initialized now. 1.219 + // Don't move this call after initializing mDocWeak. 1.220 + // SetFlags() can check whether it's called during initialization or not by 1.221 + // them. Note that SetFlags() will be called by PostCreate(). 1.222 +#ifdef DEBUG 1.223 + nsresult rv = 1.224 +#endif 1.225 + SetFlags(aFlags); 1.226 + NS_ASSERTION(NS_SUCCEEDED(rv), "SetFlags() failed"); 1.227 + 1.228 + mDocWeak = do_GetWeakReference(aDoc); // weak reference to doc 1.229 + // HTML editors currently don't have their own selection controller, 1.230 + // so they'll pass null as aSelCon, and we'll get the selection controller 1.231 + // off of the presshell. 1.232 + nsCOMPtr<nsISelectionController> selCon; 1.233 + if (aSelCon) { 1.234 + mSelConWeak = do_GetWeakReference(aSelCon); // weak reference to selectioncontroller 1.235 + selCon = aSelCon; 1.236 + } else { 1.237 + nsCOMPtr<nsIPresShell> presShell = GetPresShell(); 1.238 + selCon = do_QueryInterface(presShell); 1.239 + } 1.240 + NS_ASSERTION(selCon, "Selection controller should be available at this point"); 1.241 + 1.242 + //set up root element if we are passed one. 1.243 + if (aRoot) 1.244 + mRootElement = do_QueryInterface(aRoot); 1.245 + 1.246 + mUpdateCount=0; 1.247 + 1.248 + /* initialize IME stuff */ 1.249 + mIMETextNode = nullptr; 1.250 + mIMETextOffset = 0; 1.251 + /* Show the caret */ 1.252 + selCon->SetCaretReadOnly(false); 1.253 + selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON); 1.254 + 1.255 + selCon->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL);//we want to see all the selection reflected to user 1.256 + 1.257 + NS_POSTCONDITION(mDocWeak, "bad state"); 1.258 + 1.259 + // Make sure that the editor will be destroyed properly 1.260 + mDidPreDestroy = false; 1.261 + // Make sure that the ediotr will be created properly 1.262 + mDidPostCreate = false; 1.263 + 1.264 + return NS_OK; 1.265 +} 1.266 + 1.267 + 1.268 +NS_IMETHODIMP 1.269 +nsEditor::PostCreate() 1.270 +{ 1.271 + // Synchronize some stuff for the flags. SetFlags() will initialize 1.272 + // something by the flag difference. This is first time of that, so, all 1.273 + // initializations must be run. For such reason, we need to invert mFlags 1.274 + // value first. 1.275 + mFlags = ~mFlags; 1.276 + nsresult rv = SetFlags(~mFlags); 1.277 + NS_ENSURE_SUCCESS(rv, rv); 1.278 + 1.279 + // These operations only need to happen on the first PostCreate call 1.280 + if (!mDidPostCreate) { 1.281 + mDidPostCreate = true; 1.282 + 1.283 + // Set up listeners 1.284 + CreateEventListeners(); 1.285 + rv = InstallEventListeners(); 1.286 + NS_ENSURE_SUCCESS(rv, rv); 1.287 + 1.288 + // nuke the modification count, so the doc appears unmodified 1.289 + // do this before we notify listeners 1.290 + ResetModificationCount(); 1.291 + 1.292 + // update the UI with our state 1.293 + NotifyDocumentListeners(eDocumentCreated); 1.294 + NotifyDocumentListeners(eDocumentStateChanged); 1.295 + 1.296 + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 1.297 + if (obs) { 1.298 + obs->AddObserver(this, 1.299 + SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION, 1.300 + false); 1.301 + } 1.302 + } 1.303 + 1.304 + // update nsTextStateManager and caret if we have focus 1.305 + nsCOMPtr<nsIContent> focusedContent = GetFocusedContent(); 1.306 + if (focusedContent) { 1.307 + nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(focusedContent); 1.308 + if (target) { 1.309 + InitializeSelection(target); 1.310 + } 1.311 + 1.312 + // If the text control gets reframed during focus, Focus() would not be 1.313 + // called, so take a chance here to see if we need to spell check the text 1.314 + // control. 1.315 + nsEditorEventListener* listener = 1.316 + reinterpret_cast<nsEditorEventListener*> (mEventListener.get()); 1.317 + listener->SpellCheckIfNeeded(); 1.318 + 1.319 + IMEState newState; 1.320 + rv = GetPreferredIMEState(&newState); 1.321 + NS_ENSURE_SUCCESS(rv, NS_OK); 1.322 + nsCOMPtr<nsIContent> content = GetFocusedContentForIME(); 1.323 + IMEStateManager::UpdateIMEState(newState, content); 1.324 + } 1.325 + return NS_OK; 1.326 +} 1.327 + 1.328 +/* virtual */ 1.329 +void 1.330 +nsEditor::CreateEventListeners() 1.331 +{ 1.332 + // Don't create the handler twice 1.333 + if (!mEventListener) { 1.334 + mEventListener = new nsEditorEventListener(); 1.335 + } 1.336 +} 1.337 + 1.338 +nsresult 1.339 +nsEditor::InstallEventListeners() 1.340 +{ 1.341 + NS_ENSURE_TRUE(mDocWeak && mEventListener, 1.342 + NS_ERROR_NOT_INITIALIZED); 1.343 + 1.344 + // Initialize the event target. 1.345 + nsCOMPtr<nsIContent> rootContent = GetRoot(); 1.346 + NS_ENSURE_TRUE(rootContent, NS_ERROR_NOT_AVAILABLE); 1.347 + mEventTarget = do_QueryInterface(rootContent->GetParent()); 1.348 + NS_ENSURE_TRUE(mEventTarget, NS_ERROR_NOT_AVAILABLE); 1.349 + 1.350 + nsEditorEventListener* listener = 1.351 + reinterpret_cast<nsEditorEventListener*>(mEventListener.get()); 1.352 + return listener->Connect(this); 1.353 +} 1.354 + 1.355 +void 1.356 +nsEditor::RemoveEventListeners() 1.357 +{ 1.358 + if (!mDocWeak || !mEventListener) { 1.359 + return; 1.360 + } 1.361 + reinterpret_cast<nsEditorEventListener*>(mEventListener.get())->Disconnect(); 1.362 + if (mComposition) { 1.363 + mComposition->EndHandlingComposition(this); 1.364 + mComposition = nullptr; 1.365 + } 1.366 + mEventTarget = nullptr; 1.367 +} 1.368 + 1.369 +bool 1.370 +nsEditor::GetDesiredSpellCheckState() 1.371 +{ 1.372 + // Check user override on this element 1.373 + if (mSpellcheckCheckboxState != eTriUnset) { 1.374 + return (mSpellcheckCheckboxState == eTriTrue); 1.375 + } 1.376 + 1.377 + // Check user preferences 1.378 + int32_t spellcheckLevel = Preferences::GetInt("layout.spellcheckDefault", 1); 1.379 + 1.380 + if (spellcheckLevel == 0) { 1.381 + return false; // Spellchecking forced off globally 1.382 + } 1.383 + 1.384 + if (!CanEnableSpellCheck()) { 1.385 + return false; 1.386 + } 1.387 + 1.388 + nsCOMPtr<nsIPresShell> presShell = GetPresShell(); 1.389 + if (presShell) { 1.390 + nsPresContext* context = presShell->GetPresContext(); 1.391 + if (context && !context->IsDynamic()) { 1.392 + return false; 1.393 + } 1.394 + } 1.395 + 1.396 + // Check DOM state 1.397 + nsCOMPtr<nsIContent> content = GetExposedRoot(); 1.398 + if (!content) { 1.399 + return false; 1.400 + } 1.401 + 1.402 + nsCOMPtr<nsIDOMHTMLElement> element = do_QueryInterface(content); 1.403 + if (!element) { 1.404 + return false; 1.405 + } 1.406 + 1.407 + if (!IsPlaintextEditor()) { 1.408 + // Some of the page content might be editable and some not, if spellcheck= 1.409 + // is explicitly set anywhere, so if there's anything editable on the page, 1.410 + // return true and let the spellchecker figure it out. 1.411 + nsCOMPtr<nsIHTMLDocument> doc = do_QueryInterface(content->GetCurrentDoc()); 1.412 + return doc && doc->IsEditingOn(); 1.413 + } 1.414 + 1.415 + bool enable; 1.416 + element->GetSpellcheck(&enable); 1.417 + 1.418 + return enable; 1.419 +} 1.420 + 1.421 +NS_IMETHODIMP 1.422 +nsEditor::PreDestroy(bool aDestroyingFrames) 1.423 +{ 1.424 + if (mDidPreDestroy) 1.425 + return NS_OK; 1.426 + 1.427 + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 1.428 + if (obs) { 1.429 + obs->RemoveObserver(this, 1.430 + SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION); 1.431 + } 1.432 + 1.433 + // Let spellchecker clean up its observers etc. It is important not to 1.434 + // actually free the spellchecker here, since the spellchecker could have 1.435 + // caused flush notifications, which could have gotten here if a textbox 1.436 + // is being removed. Setting the spellchecker to nullptr could free the 1.437 + // object that is still in use! It will be freed when the editor is 1.438 + // destroyed. 1.439 + if (mInlineSpellChecker) 1.440 + mInlineSpellChecker->Cleanup(aDestroyingFrames); 1.441 + 1.442 + // tell our listeners that the doc is going away 1.443 + NotifyDocumentListeners(eDocumentToBeDestroyed); 1.444 + 1.445 + // Unregister event listeners 1.446 + RemoveEventListeners(); 1.447 + mActionListeners.Clear(); 1.448 + mEditorObservers.Clear(); 1.449 + mDocStateListeners.Clear(); 1.450 + mInlineSpellChecker = nullptr; 1.451 + mSpellcheckCheckboxState = eTriUnset; 1.452 + mRootElement = nullptr; 1.453 + 1.454 + mDidPreDestroy = true; 1.455 + return NS_OK; 1.456 +} 1.457 + 1.458 +NS_IMETHODIMP 1.459 +nsEditor::GetFlags(uint32_t *aFlags) 1.460 +{ 1.461 + *aFlags = mFlags; 1.462 + return NS_OK; 1.463 +} 1.464 + 1.465 +NS_IMETHODIMP 1.466 +nsEditor::SetFlags(uint32_t aFlags) 1.467 +{ 1.468 + if (mFlags == aFlags) { 1.469 + return NS_OK; 1.470 + } 1.471 + 1.472 + bool spellcheckerWasEnabled = CanEnableSpellCheck(); 1.473 + mFlags = aFlags; 1.474 + 1.475 + if (!mDocWeak) { 1.476 + // If we're initializing, we shouldn't do anything now. 1.477 + // SetFlags() will be called by PostCreate(), 1.478 + // we should synchronize some stuff for the flags at that time. 1.479 + return NS_OK; 1.480 + } 1.481 + 1.482 + // The flag change may cause the spellchecker state change 1.483 + if (CanEnableSpellCheck() != spellcheckerWasEnabled) { 1.484 + nsresult rv = SyncRealTimeSpell(); 1.485 + NS_ENSURE_SUCCESS(rv, rv); 1.486 + } 1.487 + 1.488 + // If this is called from PostCreate(), it will update the IME state if it's 1.489 + // necessary. 1.490 + if (!mDidPostCreate) { 1.491 + return NS_OK; 1.492 + } 1.493 + 1.494 + // Might be changing editable state, so, we need to reset current IME state 1.495 + // if we're focused and the flag change causes IME state change. 1.496 + nsCOMPtr<nsIContent> focusedContent = GetFocusedContent(); 1.497 + if (focusedContent) { 1.498 + IMEState newState; 1.499 + nsresult rv = GetPreferredIMEState(&newState); 1.500 + if (NS_SUCCEEDED(rv)) { 1.501 + // NOTE: When the enabled state isn't going to be modified, this method 1.502 + // is going to do nothing. 1.503 + nsCOMPtr<nsIContent> content = GetFocusedContentForIME(); 1.504 + IMEStateManager::UpdateIMEState(newState, content); 1.505 + } 1.506 + } 1.507 + 1.508 + return NS_OK; 1.509 +} 1.510 + 1.511 +NS_IMETHODIMP 1.512 +nsEditor::GetIsSelectionEditable(bool *aIsSelectionEditable) 1.513 +{ 1.514 + NS_ENSURE_ARG_POINTER(aIsSelectionEditable); 1.515 + 1.516 + // get current selection 1.517 + nsCOMPtr<nsISelection> selection; 1.518 + nsresult res = GetSelection(getter_AddRefs(selection)); 1.519 + NS_ENSURE_SUCCESS(res, res); 1.520 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.521 + 1.522 + // XXX we just check that the anchor node is editable at the moment 1.523 + // we should check that all nodes in the selection are editable 1.524 + nsCOMPtr<nsIDOMNode> anchorNode; 1.525 + selection->GetAnchorNode(getter_AddRefs(anchorNode)); 1.526 + *aIsSelectionEditable = anchorNode && IsEditable(anchorNode); 1.527 + 1.528 + return NS_OK; 1.529 +} 1.530 + 1.531 +NS_IMETHODIMP 1.532 +nsEditor::GetIsDocumentEditable(bool *aIsDocumentEditable) 1.533 +{ 1.534 + NS_ENSURE_ARG_POINTER(aIsDocumentEditable); 1.535 + nsCOMPtr<nsIDOMDocument> doc = GetDOMDocument(); 1.536 + *aIsDocumentEditable = !!doc; 1.537 + 1.538 + return NS_OK; 1.539 +} 1.540 + 1.541 +already_AddRefed<nsIDocument> 1.542 +nsEditor::GetDocument() 1.543 +{ 1.544 + NS_PRECONDITION(mDocWeak, "bad state, mDocWeak weak pointer not initialized"); 1.545 + nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak); 1.546 + return doc.forget(); 1.547 +} 1.548 + 1.549 +already_AddRefed<nsIDOMDocument> 1.550 +nsEditor::GetDOMDocument() 1.551 +{ 1.552 + NS_PRECONDITION(mDocWeak, "bad state, mDocWeak weak pointer not initialized"); 1.553 + nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(mDocWeak); 1.554 + return doc.forget(); 1.555 +} 1.556 + 1.557 +NS_IMETHODIMP 1.558 +nsEditor::GetDocument(nsIDOMDocument **aDoc) 1.559 +{ 1.560 + nsCOMPtr<nsIDOMDocument> doc = GetDOMDocument(); 1.561 + doc.forget(aDoc); 1.562 + return *aDoc ? NS_OK : NS_ERROR_NOT_INITIALIZED; 1.563 +} 1.564 + 1.565 +already_AddRefed<nsIPresShell> 1.566 +nsEditor::GetPresShell() 1.567 +{ 1.568 + NS_PRECONDITION(mDocWeak, "bad state, null mDocWeak"); 1.569 + nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak); 1.570 + NS_ENSURE_TRUE(doc, nullptr); 1.571 + nsCOMPtr<nsIPresShell> ps = doc->GetShell(); 1.572 + return ps.forget(); 1.573 +} 1.574 + 1.575 +already_AddRefed<nsIWidget> 1.576 +nsEditor::GetWidget() 1.577 +{ 1.578 + nsCOMPtr<nsIPresShell> ps = GetPresShell(); 1.579 + NS_ENSURE_TRUE(ps, nullptr); 1.580 + nsPresContext* pc = ps->GetPresContext(); 1.581 + NS_ENSURE_TRUE(pc, nullptr); 1.582 + nsCOMPtr<nsIWidget> widget = pc->GetRootWidget(); 1.583 + NS_ENSURE_TRUE(widget.get(), nullptr); 1.584 + return widget.forget(); 1.585 +} 1.586 + 1.587 +/* attribute string contentsMIMEType; */ 1.588 +NS_IMETHODIMP 1.589 +nsEditor::GetContentsMIMEType(char * *aContentsMIMEType) 1.590 +{ 1.591 + NS_ENSURE_ARG_POINTER(aContentsMIMEType); 1.592 + *aContentsMIMEType = ToNewCString(mContentMIMEType); 1.593 + return NS_OK; 1.594 +} 1.595 + 1.596 +NS_IMETHODIMP 1.597 +nsEditor::SetContentsMIMEType(const char * aContentsMIMEType) 1.598 +{ 1.599 + mContentMIMEType.Assign(aContentsMIMEType ? aContentsMIMEType : ""); 1.600 + return NS_OK; 1.601 +} 1.602 + 1.603 +NS_IMETHODIMP 1.604 +nsEditor::GetSelectionController(nsISelectionController **aSel) 1.605 +{ 1.606 + NS_ENSURE_TRUE(aSel, NS_ERROR_NULL_POINTER); 1.607 + *aSel = nullptr; // init out param 1.608 + nsCOMPtr<nsISelectionController> selCon; 1.609 + if (mSelConWeak) { 1.610 + selCon = do_QueryReferent(mSelConWeak); 1.611 + } else { 1.612 + nsCOMPtr<nsIPresShell> presShell = GetPresShell(); 1.613 + selCon = do_QueryInterface(presShell); 1.614 + } 1.615 + NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED); 1.616 + NS_ADDREF(*aSel = selCon); 1.617 + return NS_OK; 1.618 +} 1.619 + 1.620 + 1.621 +NS_IMETHODIMP 1.622 +nsEditor::DeleteSelection(EDirection aAction, EStripWrappers aStripWrappers) 1.623 +{ 1.624 + MOZ_ASSERT(aStripWrappers == eStrip || aStripWrappers == eNoStrip); 1.625 + return DeleteSelectionImpl(aAction, aStripWrappers); 1.626 +} 1.627 + 1.628 + 1.629 + 1.630 +NS_IMETHODIMP 1.631 +nsEditor::GetSelection(nsISelection **aSelection) 1.632 +{ 1.633 + NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER); 1.634 + *aSelection = nullptr; 1.635 + nsCOMPtr<nsISelectionController> selcon; 1.636 + GetSelectionController(getter_AddRefs(selcon)); 1.637 + NS_ENSURE_TRUE(selcon, NS_ERROR_NOT_INITIALIZED); 1.638 + return selcon->GetSelection(nsISelectionController::SELECTION_NORMAL, aSelection); // does an addref 1.639 +} 1.640 + 1.641 +Selection* 1.642 +nsEditor::GetSelection() 1.643 +{ 1.644 + nsCOMPtr<nsISelection> sel; 1.645 + nsresult res = GetSelection(getter_AddRefs(sel)); 1.646 + NS_ENSURE_SUCCESS(res, nullptr); 1.647 + 1.648 + return static_cast<Selection*>(sel.get()); 1.649 +} 1.650 + 1.651 +NS_IMETHODIMP 1.652 +nsEditor::DoTransaction(nsITransaction* aTxn) 1.653 +{ 1.654 + if (mPlaceHolderBatch && !mPlaceHolderTxn) { 1.655 + nsCOMPtr<nsIAbsorbingTransaction> plcTxn = new PlaceholderTxn(); 1.656 + 1.657 + // save off weak reference to placeholder txn 1.658 + mPlaceHolderTxn = do_GetWeakReference(plcTxn); 1.659 + plcTxn->Init(mPlaceHolderName, mSelState, this); 1.660 + // placeholder txn took ownership of this pointer 1.661 + mSelState = nullptr; 1.662 + 1.663 + // QI to an nsITransaction since that's what DoTransaction() expects 1.664 + nsCOMPtr<nsITransaction> theTxn = do_QueryInterface(plcTxn); 1.665 + // we will recurse, but will not hit this case in the nested call 1.666 + DoTransaction(theTxn); 1.667 + 1.668 + if (mTxnMgr) { 1.669 + nsCOMPtr<nsITransaction> topTxn = mTxnMgr->PeekUndoStack(); 1.670 + if (topTxn) { 1.671 + plcTxn = do_QueryInterface(topTxn); 1.672 + if (plcTxn) { 1.673 + // there is a placeholder transaction on top of the undo stack. It 1.674 + // is either the one we just created, or an earlier one that we are 1.675 + // now merging into. From here on out remember this placeholder 1.676 + // instead of the one we just created. 1.677 + mPlaceHolderTxn = do_GetWeakReference(plcTxn); 1.678 + } 1.679 + } 1.680 + } 1.681 + } 1.682 + 1.683 + if (aTxn) { 1.684 + // XXX: Why are we doing selection specific batching stuff here? 1.685 + // XXX: Most entry points into the editor have auto variables that 1.686 + // XXX: should trigger Begin/EndUpdateViewBatch() calls that will make 1.687 + // XXX: these selection batch calls no-ops. 1.688 + // XXX: 1.689 + // XXX: I suspect that this was placed here to avoid multiple 1.690 + // XXX: selection changed notifications from happening until after 1.691 + // XXX: the transaction was done. I suppose that can still happen 1.692 + // XXX: if an embedding application called DoTransaction() directly 1.693 + // XXX: to pump its own transactions through the system, but in that 1.694 + // XXX: case, wouldn't we want to use Begin/EndUpdateViewBatch() or 1.695 + // XXX: its auto equivalent nsAutoUpdateViewBatch to ensure that 1.696 + // XXX: selection listeners have access to accurate frame data? 1.697 + // XXX: 1.698 + // XXX: Note that if we did add Begin/EndUpdateViewBatch() calls 1.699 + // XXX: we will need to make sure that they are disabled during 1.700 + // XXX: the init of the editor for text widgets to avoid layout 1.701 + // XXX: re-entry during initial reflow. - kin 1.702 + 1.703 + // get the selection and start a batch change 1.704 + nsRefPtr<Selection> selection = GetSelection(); 1.705 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.706 + 1.707 + selection->StartBatchChanges(); 1.708 + 1.709 + nsresult res; 1.710 + if (mTxnMgr) { 1.711 + res = mTxnMgr->DoTransaction(aTxn); 1.712 + } else { 1.713 + res = aTxn->DoTransaction(); 1.714 + } 1.715 + if (NS_SUCCEEDED(res)) { 1.716 + DoAfterDoTransaction(aTxn); 1.717 + } 1.718 + 1.719 + // no need to check res here, don't lose result of operation 1.720 + selection->EndBatchChanges(); 1.721 + 1.722 + NS_ENSURE_SUCCESS(res, res); 1.723 + } 1.724 + 1.725 + return NS_OK; 1.726 +} 1.727 + 1.728 + 1.729 +NS_IMETHODIMP 1.730 +nsEditor::EnableUndo(bool aEnable) 1.731 +{ 1.732 + if (aEnable) { 1.733 + if (!mTxnMgr) { 1.734 + mTxnMgr = new nsTransactionManager(); 1.735 + } 1.736 + mTxnMgr->SetMaxTransactionCount(-1); 1.737 + } else if (mTxnMgr) { 1.738 + // disable the transaction manager if it is enabled 1.739 + mTxnMgr->Clear(); 1.740 + mTxnMgr->SetMaxTransactionCount(0); 1.741 + } 1.742 + 1.743 + return NS_OK; 1.744 +} 1.745 + 1.746 +NS_IMETHODIMP 1.747 +nsEditor::GetNumberOfUndoItems(int32_t* aNumItems) 1.748 +{ 1.749 + *aNumItems = 0; 1.750 + return mTxnMgr ? mTxnMgr->GetNumberOfUndoItems(aNumItems) : NS_OK; 1.751 +} 1.752 + 1.753 +NS_IMETHODIMP 1.754 +nsEditor::GetNumberOfRedoItems(int32_t* aNumItems) 1.755 +{ 1.756 + *aNumItems = 0; 1.757 + return mTxnMgr ? mTxnMgr->GetNumberOfRedoItems(aNumItems) : NS_OK; 1.758 +} 1.759 + 1.760 +NS_IMETHODIMP 1.761 +nsEditor::GetTransactionManager(nsITransactionManager* *aTxnManager) 1.762 +{ 1.763 + NS_ENSURE_ARG_POINTER(aTxnManager); 1.764 + 1.765 + *aTxnManager = nullptr; 1.766 + NS_ENSURE_TRUE(mTxnMgr, NS_ERROR_FAILURE); 1.767 + 1.768 + NS_ADDREF(*aTxnManager = mTxnMgr); 1.769 + return NS_OK; 1.770 +} 1.771 + 1.772 +NS_IMETHODIMP 1.773 +nsEditor::SetTransactionManager(nsITransactionManager *aTxnManager) 1.774 +{ 1.775 + NS_ENSURE_TRUE(aTxnManager, NS_ERROR_FAILURE); 1.776 + 1.777 + // nsITransactionManager is builtinclass, so this is safe 1.778 + mTxnMgr = static_cast<nsTransactionManager*>(aTxnManager); 1.779 + return NS_OK; 1.780 +} 1.781 + 1.782 +NS_IMETHODIMP 1.783 +nsEditor::Undo(uint32_t aCount) 1.784 +{ 1.785 + ForceCompositionEnd(); 1.786 + 1.787 + bool hasTxnMgr, hasTransaction = false; 1.788 + CanUndo(&hasTxnMgr, &hasTransaction); 1.789 + NS_ENSURE_TRUE(hasTransaction, NS_OK); 1.790 + 1.791 + nsAutoRules beginRulesSniffing(this, EditAction::undo, nsIEditor::eNone); 1.792 + 1.793 + if (!mTxnMgr) { 1.794 + return NS_OK; 1.795 + } 1.796 + 1.797 + for (uint32_t i = 0; i < aCount; ++i) { 1.798 + nsresult rv = mTxnMgr->UndoTransaction(); 1.799 + NS_ENSURE_SUCCESS(rv, rv); 1.800 + 1.801 + DoAfterUndoTransaction(); 1.802 + } 1.803 + 1.804 + return NS_OK; 1.805 +} 1.806 + 1.807 + 1.808 +NS_IMETHODIMP nsEditor::CanUndo(bool *aIsEnabled, bool *aCanUndo) 1.809 +{ 1.810 + NS_ENSURE_TRUE(aIsEnabled && aCanUndo, NS_ERROR_NULL_POINTER); 1.811 + *aIsEnabled = !!mTxnMgr; 1.812 + if (*aIsEnabled) { 1.813 + int32_t numTxns = 0; 1.814 + mTxnMgr->GetNumberOfUndoItems(&numTxns); 1.815 + *aCanUndo = !!numTxns; 1.816 + } else { 1.817 + *aCanUndo = false; 1.818 + } 1.819 + return NS_OK; 1.820 +} 1.821 + 1.822 + 1.823 +NS_IMETHODIMP 1.824 +nsEditor::Redo(uint32_t aCount) 1.825 +{ 1.826 + bool hasTxnMgr, hasTransaction = false; 1.827 + CanRedo(&hasTxnMgr, &hasTransaction); 1.828 + NS_ENSURE_TRUE(hasTransaction, NS_OK); 1.829 + 1.830 + nsAutoRules beginRulesSniffing(this, EditAction::redo, nsIEditor::eNone); 1.831 + 1.832 + if (!mTxnMgr) { 1.833 + return NS_OK; 1.834 + } 1.835 + 1.836 + for (uint32_t i = 0; i < aCount; ++i) { 1.837 + nsresult rv = mTxnMgr->RedoTransaction(); 1.838 + NS_ENSURE_SUCCESS(rv, rv); 1.839 + 1.840 + DoAfterRedoTransaction(); 1.841 + } 1.842 + 1.843 + return NS_OK; 1.844 +} 1.845 + 1.846 + 1.847 +NS_IMETHODIMP nsEditor::CanRedo(bool *aIsEnabled, bool *aCanRedo) 1.848 +{ 1.849 + NS_ENSURE_TRUE(aIsEnabled && aCanRedo, NS_ERROR_NULL_POINTER); 1.850 + 1.851 + *aIsEnabled = !!mTxnMgr; 1.852 + if (*aIsEnabled) { 1.853 + int32_t numTxns = 0; 1.854 + mTxnMgr->GetNumberOfRedoItems(&numTxns); 1.855 + *aCanRedo = !!numTxns; 1.856 + } else { 1.857 + *aCanRedo = false; 1.858 + } 1.859 + return NS_OK; 1.860 +} 1.861 + 1.862 + 1.863 +NS_IMETHODIMP 1.864 +nsEditor::BeginTransaction() 1.865 +{ 1.866 + BeginUpdateViewBatch(); 1.867 + 1.868 + if (mTxnMgr) { 1.869 + mTxnMgr->BeginBatch(nullptr); 1.870 + } 1.871 + 1.872 + return NS_OK; 1.873 +} 1.874 + 1.875 +NS_IMETHODIMP 1.876 +nsEditor::EndTransaction() 1.877 +{ 1.878 + if (mTxnMgr) { 1.879 + mTxnMgr->EndBatch(false); 1.880 + } 1.881 + 1.882 + EndUpdateViewBatch(); 1.883 + 1.884 + return NS_OK; 1.885 +} 1.886 + 1.887 + 1.888 +// These two routines are similar to the above, but do not use 1.889 +// the transaction managers batching feature. Instead we use 1.890 +// a placeholder transaction to wrap up any further transaction 1.891 +// while the batch is open. The advantage of this is that 1.892 +// placeholder transactions can later merge, if needed. Merging 1.893 +// is unavailable between transaction manager batches. 1.894 + 1.895 +NS_IMETHODIMP 1.896 +nsEditor::BeginPlaceHolderTransaction(nsIAtom *aName) 1.897 +{ 1.898 + NS_PRECONDITION(mPlaceHolderBatch >= 0, "negative placeholder batch count!"); 1.899 + if (!mPlaceHolderBatch) 1.900 + { 1.901 + // time to turn on the batch 1.902 + BeginUpdateViewBatch(); 1.903 + mPlaceHolderTxn = nullptr; 1.904 + mPlaceHolderName = aName; 1.905 + nsRefPtr<Selection> selection = GetSelection(); 1.906 + if (selection) { 1.907 + mSelState = new nsSelectionState(); 1.908 + mSelState->SaveSelection(selection); 1.909 + } 1.910 + } 1.911 + mPlaceHolderBatch++; 1.912 + 1.913 + return NS_OK; 1.914 +} 1.915 + 1.916 +NS_IMETHODIMP 1.917 +nsEditor::EndPlaceHolderTransaction() 1.918 +{ 1.919 + NS_PRECONDITION(mPlaceHolderBatch > 0, "zero or negative placeholder batch count when ending batch!"); 1.920 + if (mPlaceHolderBatch == 1) 1.921 + { 1.922 + nsCOMPtr<nsISelection>selection; 1.923 + GetSelection(getter_AddRefs(selection)); 1.924 + 1.925 + nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(selection)); 1.926 + 1.927 + // By making the assumption that no reflow happens during the calls 1.928 + // to EndUpdateViewBatch and ScrollSelectionIntoView, we are able to 1.929 + // allow the selection to cache a frame offset which is used by the 1.930 + // caret drawing code. We only enable this cache here; at other times, 1.931 + // we have no way to know whether reflow invalidates it 1.932 + // See bugs 35296 and 199412. 1.933 + if (selPrivate) { 1.934 + selPrivate->SetCanCacheFrameOffset(true); 1.935 + } 1.936 + 1.937 + { 1.938 + // Hide the caret here to avoid hiding it twice, once in EndUpdateViewBatch 1.939 + // and once in ScrollSelectionIntoView. 1.940 + nsRefPtr<nsCaret> caret; 1.941 + nsCOMPtr<nsIPresShell> presShell = GetPresShell(); 1.942 + 1.943 + if (presShell) 1.944 + caret = presShell->GetCaret(); 1.945 + 1.946 + // time to turn off the batch 1.947 + EndUpdateViewBatch(); 1.948 + // make sure selection is in view 1.949 + 1.950 + // After ScrollSelectionIntoView(), the pending notifications might be 1.951 + // flushed and PresShell/PresContext/Frames may be dead. See bug 418470. 1.952 + ScrollSelectionIntoView(false); 1.953 + } 1.954 + 1.955 + // cached for frame offset are Not available now 1.956 + if (selPrivate) { 1.957 + selPrivate->SetCanCacheFrameOffset(false); 1.958 + } 1.959 + 1.960 + if (mSelState) 1.961 + { 1.962 + // we saved the selection state, but never got to hand it to placeholder 1.963 + // (else we ould have nulled out this pointer), so destroy it to prevent leaks. 1.964 + delete mSelState; 1.965 + mSelState = nullptr; 1.966 + } 1.967 + if (mPlaceHolderTxn) // we might have never made a placeholder if no action took place 1.968 + { 1.969 + nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryReferent(mPlaceHolderTxn); 1.970 + if (plcTxn) 1.971 + { 1.972 + plcTxn->EndPlaceHolderBatch(); 1.973 + } 1.974 + else 1.975 + { 1.976 + // in the future we will check to make sure undo is off here, 1.977 + // since that is the only known case where the placeholdertxn would disappear on us. 1.978 + // For now just removing the assert. 1.979 + } 1.980 + // notify editor observers of action but if composing, it's done by 1.981 + // text event handler. 1.982 + if (!mComposition) { 1.983 + NotifyEditorObservers(); 1.984 + } 1.985 + } 1.986 + } 1.987 + mPlaceHolderBatch--; 1.988 + 1.989 + return NS_OK; 1.990 +} 1.991 + 1.992 +NS_IMETHODIMP 1.993 +nsEditor::ShouldTxnSetSelection(bool *aResult) 1.994 +{ 1.995 + NS_ENSURE_TRUE(aResult, NS_ERROR_NULL_POINTER); 1.996 + *aResult = mShouldTxnSetSelection; 1.997 + return NS_OK; 1.998 +} 1.999 + 1.1000 +NS_IMETHODIMP 1.1001 +nsEditor::SetShouldTxnSetSelection(bool aShould) 1.1002 +{ 1.1003 + mShouldTxnSetSelection = aShould; 1.1004 + return NS_OK; 1.1005 +} 1.1006 + 1.1007 +NS_IMETHODIMP 1.1008 +nsEditor::GetDocumentIsEmpty(bool *aDocumentIsEmpty) 1.1009 +{ 1.1010 + *aDocumentIsEmpty = true; 1.1011 + 1.1012 + dom::Element* root = GetRoot(); 1.1013 + NS_ENSURE_TRUE(root, NS_ERROR_NULL_POINTER); 1.1014 + 1.1015 + *aDocumentIsEmpty = !root->HasChildren(); 1.1016 + return NS_OK; 1.1017 +} 1.1018 + 1.1019 + 1.1020 +// XXX: the rule system should tell us which node to select all on (ie, the root, or the body) 1.1021 +NS_IMETHODIMP nsEditor::SelectAll() 1.1022 +{ 1.1023 + if (!mDocWeak) { return NS_ERROR_NOT_INITIALIZED; } 1.1024 + ForceCompositionEnd(); 1.1025 + 1.1026 + nsCOMPtr<nsISelectionController> selCon; 1.1027 + GetSelectionController(getter_AddRefs(selCon)); 1.1028 + NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED); 1.1029 + nsCOMPtr<nsISelection> selection; 1.1030 + nsresult result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection)); 1.1031 + if (NS_SUCCEEDED(result) && selection) 1.1032 + { 1.1033 + result = SelectEntireDocument(selection); 1.1034 + } 1.1035 + return result; 1.1036 +} 1.1037 + 1.1038 +NS_IMETHODIMP nsEditor::BeginningOfDocument() 1.1039 +{ 1.1040 + if (!mDocWeak) { return NS_ERROR_NOT_INITIALIZED; } 1.1041 + 1.1042 + // get the selection 1.1043 + nsCOMPtr<nsISelection> selection; 1.1044 + nsresult result = GetSelection(getter_AddRefs(selection)); 1.1045 + NS_ENSURE_SUCCESS(result, result); 1.1046 + NS_ENSURE_TRUE(selection, NS_ERROR_NOT_INITIALIZED); 1.1047 + 1.1048 + // get the root element 1.1049 + dom::Element* rootElement = GetRoot(); 1.1050 + NS_ENSURE_TRUE(rootElement, NS_ERROR_NULL_POINTER); 1.1051 + 1.1052 + // find first editable thingy 1.1053 + nsCOMPtr<nsINode> firstNode = GetFirstEditableNode(rootElement); 1.1054 + if (!firstNode) { 1.1055 + // just the root node, set selection to inside the root 1.1056 + return selection->CollapseNative(rootElement, 0); 1.1057 + } 1.1058 + 1.1059 + if (firstNode->NodeType() == nsIDOMNode::TEXT_NODE) { 1.1060 + // If firstNode is text, set selection to beginning of the text node. 1.1061 + return selection->CollapseNative(firstNode, 0); 1.1062 + } 1.1063 + 1.1064 + // Otherwise, it's a leaf node and we set the selection just in front of it. 1.1065 + nsCOMPtr<nsIContent> parent = firstNode->GetParent(); 1.1066 + if (!parent) { 1.1067 + return NS_ERROR_NULL_POINTER; 1.1068 + } 1.1069 + 1.1070 + int32_t offsetInParent = parent->IndexOf(firstNode); 1.1071 + return selection->CollapseNative(parent, offsetInParent); 1.1072 +} 1.1073 + 1.1074 +NS_IMETHODIMP 1.1075 +nsEditor::EndOfDocument() 1.1076 +{ 1.1077 + NS_ENSURE_TRUE(mDocWeak, NS_ERROR_NOT_INITIALIZED); 1.1078 + 1.1079 + // get selection 1.1080 + nsCOMPtr<nsISelection> selection; 1.1081 + nsresult rv = GetSelection(getter_AddRefs(selection)); 1.1082 + NS_ENSURE_SUCCESS(rv, rv); 1.1083 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.1084 + 1.1085 + // get the root element 1.1086 + nsINode* node = GetRoot(); 1.1087 + NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER); 1.1088 + nsINode* child = node->GetLastChild(); 1.1089 + 1.1090 + while (child && IsContainer(child->AsDOMNode())) { 1.1091 + node = child; 1.1092 + child = node->GetLastChild(); 1.1093 + } 1.1094 + 1.1095 + uint32_t length = node->Length(); 1.1096 + return selection->CollapseNative(node, int32_t(length)); 1.1097 +} 1.1098 + 1.1099 +NS_IMETHODIMP 1.1100 +nsEditor::GetDocumentModified(bool *outDocModified) 1.1101 +{ 1.1102 + NS_ENSURE_TRUE(outDocModified, NS_ERROR_NULL_POINTER); 1.1103 + 1.1104 + int32_t modCount = 0; 1.1105 + GetModificationCount(&modCount); 1.1106 + 1.1107 + *outDocModified = (modCount != 0); 1.1108 + return NS_OK; 1.1109 +} 1.1110 + 1.1111 +NS_IMETHODIMP 1.1112 +nsEditor::GetDocumentCharacterSet(nsACString &characterSet) 1.1113 +{ 1.1114 + nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak); 1.1115 + NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED); 1.1116 + 1.1117 + characterSet = doc->GetDocumentCharacterSet(); 1.1118 + return NS_OK; 1.1119 +} 1.1120 + 1.1121 +NS_IMETHODIMP 1.1122 +nsEditor::SetDocumentCharacterSet(const nsACString& characterSet) 1.1123 +{ 1.1124 + nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak); 1.1125 + NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED); 1.1126 + 1.1127 + doc->SetDocumentCharacterSet(characterSet); 1.1128 + return NS_OK; 1.1129 +} 1.1130 + 1.1131 +NS_IMETHODIMP 1.1132 +nsEditor::Cut() 1.1133 +{ 1.1134 + return NS_ERROR_NOT_IMPLEMENTED; 1.1135 +} 1.1136 + 1.1137 +NS_IMETHODIMP 1.1138 +nsEditor::CanCut(bool *aCanCut) 1.1139 +{ 1.1140 + return NS_ERROR_NOT_IMPLEMENTED; 1.1141 +} 1.1142 + 1.1143 +NS_IMETHODIMP 1.1144 +nsEditor::Copy() 1.1145 +{ 1.1146 + return NS_ERROR_NOT_IMPLEMENTED; 1.1147 +} 1.1148 + 1.1149 +NS_IMETHODIMP 1.1150 +nsEditor::CanCopy(bool *aCanCut) 1.1151 +{ 1.1152 + return NS_ERROR_NOT_IMPLEMENTED; 1.1153 +} 1.1154 + 1.1155 +NS_IMETHODIMP 1.1156 +nsEditor::Paste(int32_t aSelectionType) 1.1157 +{ 1.1158 + return NS_ERROR_NOT_IMPLEMENTED; 1.1159 +} 1.1160 + 1.1161 +NS_IMETHODIMP 1.1162 +nsEditor::PasteTransferable(nsITransferable *aTransferable) 1.1163 +{ 1.1164 + return NS_ERROR_NOT_IMPLEMENTED; 1.1165 +} 1.1166 + 1.1167 +NS_IMETHODIMP 1.1168 +nsEditor::CanPaste(int32_t aSelectionType, bool *aCanPaste) 1.1169 +{ 1.1170 + return NS_ERROR_NOT_IMPLEMENTED; 1.1171 +} 1.1172 + 1.1173 +NS_IMETHODIMP 1.1174 +nsEditor::CanPasteTransferable(nsITransferable *aTransferable, bool *aCanPaste) 1.1175 +{ 1.1176 + return NS_ERROR_NOT_IMPLEMENTED; 1.1177 +} 1.1178 + 1.1179 +NS_IMETHODIMP 1.1180 +nsEditor::SetAttribute(nsIDOMElement *aElement, const nsAString & aAttribute, const nsAString & aValue) 1.1181 +{ 1.1182 + nsRefPtr<ChangeAttributeTxn> txn; 1.1183 + nsresult result = CreateTxnForSetAttribute(aElement, aAttribute, aValue, 1.1184 + getter_AddRefs(txn)); 1.1185 + if (NS_SUCCEEDED(result)) { 1.1186 + result = DoTransaction(txn); 1.1187 + } 1.1188 + return result; 1.1189 +} 1.1190 + 1.1191 +NS_IMETHODIMP 1.1192 +nsEditor::GetAttributeValue(nsIDOMElement *aElement, 1.1193 + const nsAString & aAttribute, 1.1194 + nsAString & aResultValue, 1.1195 + bool *aResultIsSet) 1.1196 +{ 1.1197 + NS_ENSURE_TRUE(aResultIsSet, NS_ERROR_NULL_POINTER); 1.1198 + *aResultIsSet = false; 1.1199 + if (!aElement) { 1.1200 + return NS_OK; 1.1201 + } 1.1202 + nsAutoString value; 1.1203 + nsresult rv = aElement->GetAttribute(aAttribute, value); 1.1204 + NS_ENSURE_SUCCESS(rv, rv); 1.1205 + if (!DOMStringIsNull(value)) { 1.1206 + *aResultIsSet = true; 1.1207 + aResultValue = value; 1.1208 + } 1.1209 + return rv; 1.1210 +} 1.1211 + 1.1212 +NS_IMETHODIMP 1.1213 +nsEditor::RemoveAttribute(nsIDOMElement *aElement, const nsAString& aAttribute) 1.1214 +{ 1.1215 + nsRefPtr<ChangeAttributeTxn> txn; 1.1216 + nsresult result = CreateTxnForRemoveAttribute(aElement, aAttribute, 1.1217 + getter_AddRefs(txn)); 1.1218 + if (NS_SUCCEEDED(result)) { 1.1219 + result = DoTransaction(txn); 1.1220 + } 1.1221 + return result; 1.1222 +} 1.1223 + 1.1224 + 1.1225 +bool 1.1226 +nsEditor::OutputsMozDirty() 1.1227 +{ 1.1228 + // Return true for Composer (!eEditorAllowInteraction) or mail 1.1229 + // (eEditorMailMask), but false for webpages. 1.1230 + return !(mFlags & nsIPlaintextEditor::eEditorAllowInteraction) || 1.1231 + (mFlags & nsIPlaintextEditor::eEditorMailMask); 1.1232 +} 1.1233 + 1.1234 + 1.1235 +NS_IMETHODIMP 1.1236 +nsEditor::MarkNodeDirty(nsIDOMNode* aNode) 1.1237 +{ 1.1238 + // Mark the node dirty, but not for webpages (bug 599983) 1.1239 + if (!OutputsMozDirty()) { 1.1240 + return NS_OK; 1.1241 + } 1.1242 + nsCOMPtr<dom::Element> element = do_QueryInterface(aNode); 1.1243 + if (element) { 1.1244 + element->SetAttr(kNameSpaceID_None, nsEditProperty::mozdirty, 1.1245 + EmptyString(), false); 1.1246 + } 1.1247 + return NS_OK; 1.1248 +} 1.1249 + 1.1250 +NS_IMETHODIMP nsEditor::GetInlineSpellChecker(bool autoCreate, 1.1251 + nsIInlineSpellChecker ** aInlineSpellChecker) 1.1252 +{ 1.1253 + NS_ENSURE_ARG_POINTER(aInlineSpellChecker); 1.1254 + 1.1255 + if (mDidPreDestroy) { 1.1256 + // Don't allow people to get or create the spell checker once the editor 1.1257 + // is going away. 1.1258 + *aInlineSpellChecker = nullptr; 1.1259 + return autoCreate ? NS_ERROR_NOT_AVAILABLE : NS_OK; 1.1260 + } 1.1261 + 1.1262 + // We don't want to show the spell checking UI if there are no spell check dictionaries available. 1.1263 + bool canSpell = mozInlineSpellChecker::CanEnableInlineSpellChecking(); 1.1264 + if (!canSpell) { 1.1265 + *aInlineSpellChecker = nullptr; 1.1266 + return NS_ERROR_FAILURE; 1.1267 + } 1.1268 + 1.1269 + nsresult rv; 1.1270 + if (!mInlineSpellChecker && autoCreate) { 1.1271 + mInlineSpellChecker = do_CreateInstance(MOZ_INLINESPELLCHECKER_CONTRACTID, &rv); 1.1272 + NS_ENSURE_SUCCESS(rv, rv); 1.1273 + } 1.1274 + 1.1275 + if (mInlineSpellChecker) { 1.1276 + rv = mInlineSpellChecker->Init(this); 1.1277 + if (NS_FAILED(rv)) 1.1278 + mInlineSpellChecker = nullptr; 1.1279 + NS_ENSURE_SUCCESS(rv, rv); 1.1280 + } 1.1281 + 1.1282 + NS_IF_ADDREF(*aInlineSpellChecker = mInlineSpellChecker); 1.1283 + 1.1284 + return NS_OK; 1.1285 +} 1.1286 + 1.1287 +NS_IMETHODIMP nsEditor::Observe(nsISupports* aSubj, const char *aTopic, 1.1288 + const char16_t *aData) 1.1289 +{ 1.1290 + NS_ASSERTION(!strcmp(aTopic, 1.1291 + SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION), 1.1292 + "Unexpected observer topic"); 1.1293 + 1.1294 + // When mozInlineSpellChecker::CanEnableInlineSpellChecking changes 1.1295 + SyncRealTimeSpell(); 1.1296 + 1.1297 + // When nsIEditorSpellCheck::GetCurrentDictionary changes 1.1298 + if (mInlineSpellChecker) { 1.1299 + // if the current dictionary is no longer available, find another one 1.1300 + nsCOMPtr<nsIEditorSpellCheck> editorSpellCheck; 1.1301 + mInlineSpellChecker->GetSpellChecker(getter_AddRefs(editorSpellCheck)); 1.1302 + if (editorSpellCheck) { 1.1303 + // Note: This might change the current dictionary, which may call 1.1304 + // this observer recursively. 1.1305 + editorSpellCheck->CheckCurrentDictionary(); 1.1306 + } 1.1307 + 1.1308 + // update the inline spell checker to reflect the new current dictionary 1.1309 + mInlineSpellChecker->SpellCheckRange(nullptr); // causes recheck 1.1310 + } 1.1311 + 1.1312 + return NS_OK; 1.1313 +} 1.1314 + 1.1315 +NS_IMETHODIMP nsEditor::SyncRealTimeSpell() 1.1316 +{ 1.1317 + bool enable = GetDesiredSpellCheckState(); 1.1318 + 1.1319 + // Initializes mInlineSpellChecker 1.1320 + nsCOMPtr<nsIInlineSpellChecker> spellChecker; 1.1321 + GetInlineSpellChecker(enable, getter_AddRefs(spellChecker)); 1.1322 + 1.1323 + if (mInlineSpellChecker) { 1.1324 + // We might have a mInlineSpellChecker even if there are no dictionaries 1.1325 + // available since we don't destroy the mInlineSpellChecker when the last 1.1326 + // dictionariy is removed, but in that case spellChecker is null 1.1327 + mInlineSpellChecker->SetEnableRealTimeSpell(enable && spellChecker); 1.1328 + } 1.1329 + 1.1330 + return NS_OK; 1.1331 +} 1.1332 + 1.1333 +NS_IMETHODIMP nsEditor::SetSpellcheckUserOverride(bool enable) 1.1334 +{ 1.1335 + mSpellcheckCheckboxState = enable ? eTriTrue : eTriFalse; 1.1336 + 1.1337 + return SyncRealTimeSpell(); 1.1338 +} 1.1339 + 1.1340 +NS_IMETHODIMP nsEditor::CreateNode(const nsAString& aTag, 1.1341 + nsIDOMNode * aParent, 1.1342 + int32_t aPosition, 1.1343 + nsIDOMNode ** aNewNode) 1.1344 +{ 1.1345 + int32_t i; 1.1346 + 1.1347 + nsAutoRules beginRulesSniffing(this, EditAction::createNode, nsIEditor::eNext); 1.1348 + 1.1349 + for (i = 0; i < mActionListeners.Count(); i++) 1.1350 + mActionListeners[i]->WillCreateNode(aTag, aParent, aPosition); 1.1351 + 1.1352 + nsRefPtr<CreateElementTxn> txn; 1.1353 + nsresult result = CreateTxnForCreateElement(aTag, aParent, aPosition, 1.1354 + getter_AddRefs(txn)); 1.1355 + if (NS_SUCCEEDED(result)) 1.1356 + { 1.1357 + result = DoTransaction(txn); 1.1358 + if (NS_SUCCEEDED(result)) 1.1359 + { 1.1360 + result = txn->GetNewNode(aNewNode); 1.1361 + NS_ASSERTION((NS_SUCCEEDED(result)), "GetNewNode can't fail if txn::DoTransaction succeeded."); 1.1362 + } 1.1363 + } 1.1364 + 1.1365 + mRangeUpdater.SelAdjCreateNode(aParent, aPosition); 1.1366 + 1.1367 + for (i = 0; i < mActionListeners.Count(); i++) 1.1368 + mActionListeners[i]->DidCreateNode(aTag, *aNewNode, aParent, aPosition, result); 1.1369 + 1.1370 + return result; 1.1371 +} 1.1372 + 1.1373 + 1.1374 +NS_IMETHODIMP nsEditor::InsertNode(nsIDOMNode * aNode, 1.1375 + nsIDOMNode * aParent, 1.1376 + int32_t aPosition) 1.1377 +{ 1.1378 + int32_t i; 1.1379 + nsAutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext); 1.1380 + 1.1381 + for (i = 0; i < mActionListeners.Count(); i++) 1.1382 + mActionListeners[i]->WillInsertNode(aNode, aParent, aPosition); 1.1383 + 1.1384 + nsRefPtr<InsertElementTxn> txn; 1.1385 + nsresult result = CreateTxnForInsertElement(aNode, aParent, aPosition, 1.1386 + getter_AddRefs(txn)); 1.1387 + if (NS_SUCCEEDED(result)) { 1.1388 + result = DoTransaction(txn); 1.1389 + } 1.1390 + 1.1391 + mRangeUpdater.SelAdjInsertNode(aParent, aPosition); 1.1392 + 1.1393 + for (i = 0; i < mActionListeners.Count(); i++) 1.1394 + mActionListeners[i]->DidInsertNode(aNode, aParent, aPosition, result); 1.1395 + 1.1396 + return result; 1.1397 +} 1.1398 + 1.1399 + 1.1400 +NS_IMETHODIMP 1.1401 +nsEditor::SplitNode(nsIDOMNode * aNode, 1.1402 + int32_t aOffset, 1.1403 + nsIDOMNode **aNewLeftNode) 1.1404 +{ 1.1405 + int32_t i; 1.1406 + nsAutoRules beginRulesSniffing(this, EditAction::splitNode, nsIEditor::eNext); 1.1407 + 1.1408 + for (i = 0; i < mActionListeners.Count(); i++) 1.1409 + mActionListeners[i]->WillSplitNode(aNode, aOffset); 1.1410 + 1.1411 + nsRefPtr<SplitElementTxn> txn; 1.1412 + nsresult result = CreateTxnForSplitNode(aNode, aOffset, getter_AddRefs(txn)); 1.1413 + if (NS_SUCCEEDED(result)) 1.1414 + { 1.1415 + result = DoTransaction(txn); 1.1416 + if (NS_SUCCEEDED(result)) 1.1417 + { 1.1418 + result = txn->GetNewNode(aNewLeftNode); 1.1419 + NS_ASSERTION((NS_SUCCEEDED(result)), "result must succeeded for GetNewNode"); 1.1420 + } 1.1421 + } 1.1422 + 1.1423 + mRangeUpdater.SelAdjSplitNode(aNode, aOffset, *aNewLeftNode); 1.1424 + 1.1425 + for (i = 0; i < mActionListeners.Count(); i++) 1.1426 + { 1.1427 + nsIDOMNode *ptr = *aNewLeftNode; 1.1428 + mActionListeners[i]->DidSplitNode(aNode, aOffset, ptr, result); 1.1429 + } 1.1430 + 1.1431 + return result; 1.1432 +} 1.1433 + 1.1434 + 1.1435 +nsresult 1.1436 +nsEditor::JoinNodes(nsINode* aNodeToKeep, nsIContent* aNodeToMove) 1.1437 +{ 1.1438 + // We don't really need aNodeToMove's parent to be non-null -- we could just 1.1439 + // skip adjusting any ranges in aNodeToMove's parent if there is none. But 1.1440 + // the current implementation requires it. 1.1441 + MOZ_ASSERT(aNodeToKeep && aNodeToMove && aNodeToMove->GetParentNode()); 1.1442 + nsresult res = JoinNodes(aNodeToKeep->AsDOMNode(), aNodeToMove->AsDOMNode(), 1.1443 + aNodeToMove->GetParentNode()->AsDOMNode()); 1.1444 + NS_ASSERTION(NS_SUCCEEDED(res), "JoinNodes failed"); 1.1445 + NS_ENSURE_SUCCESS(res, res); 1.1446 + return NS_OK; 1.1447 +} 1.1448 + 1.1449 +NS_IMETHODIMP 1.1450 +nsEditor::JoinNodes(nsIDOMNode * aLeftNode, 1.1451 + nsIDOMNode * aRightNode, 1.1452 + nsIDOMNode * aParent) 1.1453 +{ 1.1454 + int32_t i; 1.1455 + nsAutoRules beginRulesSniffing(this, EditAction::joinNode, nsIEditor::ePrevious); 1.1456 + 1.1457 + // remember some values; later used for saved selection updating. 1.1458 + // find the offset between the nodes to be joined. 1.1459 + int32_t offset = GetChildOffset(aRightNode, aParent); 1.1460 + // find the number of children of the lefthand node 1.1461 + uint32_t oldLeftNodeLen; 1.1462 + nsresult result = GetLengthOfDOMNode(aLeftNode, oldLeftNodeLen); 1.1463 + NS_ENSURE_SUCCESS(result, result); 1.1464 + 1.1465 + for (i = 0; i < mActionListeners.Count(); i++) 1.1466 + mActionListeners[i]->WillJoinNodes(aLeftNode, aRightNode, aParent); 1.1467 + 1.1468 + nsRefPtr<JoinElementTxn> txn; 1.1469 + result = CreateTxnForJoinNode(aLeftNode, aRightNode, getter_AddRefs(txn)); 1.1470 + if (NS_SUCCEEDED(result)) { 1.1471 + result = DoTransaction(txn); 1.1472 + } 1.1473 + 1.1474 + mRangeUpdater.SelAdjJoinNodes(aLeftNode, aRightNode, aParent, offset, (int32_t)oldLeftNodeLen); 1.1475 + 1.1476 + for (i = 0; i < mActionListeners.Count(); i++) 1.1477 + mActionListeners[i]->DidJoinNodes(aLeftNode, aRightNode, aParent, result); 1.1478 + 1.1479 + return result; 1.1480 +} 1.1481 + 1.1482 + 1.1483 +NS_IMETHODIMP 1.1484 +nsEditor::DeleteNode(nsIDOMNode* aNode) 1.1485 +{ 1.1486 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.1487 + NS_ENSURE_STATE(node); 1.1488 + return DeleteNode(node); 1.1489 +} 1.1490 + 1.1491 +nsresult 1.1492 +nsEditor::DeleteNode(nsINode* aNode) 1.1493 +{ 1.1494 + nsAutoRules beginRulesSniffing(this, EditAction::createNode, nsIEditor::ePrevious); 1.1495 + 1.1496 + // save node location for selection updating code. 1.1497 + for (int32_t i = 0; i < mActionListeners.Count(); i++) { 1.1498 + mActionListeners[i]->WillDeleteNode(aNode->AsDOMNode()); 1.1499 + } 1.1500 + 1.1501 + nsRefPtr<DeleteNodeTxn> txn; 1.1502 + nsresult res = CreateTxnForDeleteNode(aNode, getter_AddRefs(txn)); 1.1503 + if (NS_SUCCEEDED(res)) { 1.1504 + res = DoTransaction(txn); 1.1505 + } 1.1506 + 1.1507 + for (int32_t i = 0; i < mActionListeners.Count(); i++) { 1.1508 + mActionListeners[i]->DidDeleteNode(aNode->AsDOMNode(), res); 1.1509 + } 1.1510 + 1.1511 + NS_ENSURE_SUCCESS(res, res); 1.1512 + return NS_OK; 1.1513 +} 1.1514 + 1.1515 +/////////////////////////////////////////////////////////////////////////// 1.1516 +// ReplaceContainer: replace inNode with a new node (outNode) which is contructed 1.1517 +// to be of type aNodeType. Put inNodes children into outNode. 1.1518 +// Callers responsibility to make sure inNode's children can 1.1519 +// go in outNode. 1.1520 +nsresult 1.1521 +nsEditor::ReplaceContainer(nsIDOMNode *inNode, 1.1522 + nsCOMPtr<nsIDOMNode> *outNode, 1.1523 + const nsAString &aNodeType, 1.1524 + const nsAString *aAttribute, 1.1525 + const nsAString *aValue, 1.1526 + bool aCloneAttributes) 1.1527 +{ 1.1528 + NS_ENSURE_TRUE(inNode && outNode, NS_ERROR_NULL_POINTER); 1.1529 + 1.1530 + nsCOMPtr<nsINode> node = do_QueryInterface(inNode); 1.1531 + NS_ENSURE_STATE(node); 1.1532 + 1.1533 + nsCOMPtr<dom::Element> element; 1.1534 + nsresult rv = ReplaceContainer(node, getter_AddRefs(element), aNodeType, 1.1535 + aAttribute, aValue, aCloneAttributes); 1.1536 + *outNode = element ? element->AsDOMNode() : nullptr; 1.1537 + return rv; 1.1538 +} 1.1539 + 1.1540 +nsresult 1.1541 +nsEditor::ReplaceContainer(nsINode* aNode, 1.1542 + dom::Element** outNode, 1.1543 + const nsAString& aNodeType, 1.1544 + const nsAString* aAttribute, 1.1545 + const nsAString* aValue, 1.1546 + bool aCloneAttributes) 1.1547 +{ 1.1548 + MOZ_ASSERT(aNode); 1.1549 + MOZ_ASSERT(outNode); 1.1550 + 1.1551 + *outNode = nullptr; 1.1552 + 1.1553 + nsCOMPtr<nsIContent> parent = aNode->GetParent(); 1.1554 + NS_ENSURE_STATE(parent); 1.1555 + 1.1556 + int32_t offset = parent->IndexOf(aNode); 1.1557 + 1.1558 + // create new container 1.1559 + //new call to use instead to get proper HTML element, bug# 39919 1.1560 + nsresult res = CreateHTMLContent(aNodeType, outNode); 1.1561 + NS_ENSURE_SUCCESS(res, res); 1.1562 + 1.1563 + nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(*outNode); 1.1564 + 1.1565 + nsIDOMNode* inNode = aNode->AsDOMNode(); 1.1566 + 1.1567 + // set attribute if needed 1.1568 + if (aAttribute && aValue && !aAttribute->IsEmpty()) { 1.1569 + res = elem->SetAttribute(*aAttribute, *aValue); 1.1570 + NS_ENSURE_SUCCESS(res, res); 1.1571 + } 1.1572 + if (aCloneAttributes) { 1.1573 + res = CloneAttributes(elem, inNode); 1.1574 + NS_ENSURE_SUCCESS(res, res); 1.1575 + } 1.1576 + 1.1577 + // notify our internal selection state listener 1.1578 + // (Note: A nsAutoSelectionReset object must be created 1.1579 + // before calling this to initialize mRangeUpdater) 1.1580 + nsAutoReplaceContainerSelNotify selStateNotify(mRangeUpdater, inNode, elem); 1.1581 + { 1.1582 + nsAutoTxnsConserveSelection conserveSelection(this); 1.1583 + while (aNode->HasChildren()) { 1.1584 + nsCOMPtr<nsIDOMNode> child = aNode->GetFirstChild()->AsDOMNode(); 1.1585 + 1.1586 + res = DeleteNode(child); 1.1587 + NS_ENSURE_SUCCESS(res, res); 1.1588 + 1.1589 + res = InsertNode(child, elem, -1); 1.1590 + NS_ENSURE_SUCCESS(res, res); 1.1591 + } 1.1592 + } 1.1593 + 1.1594 + // insert new container into tree 1.1595 + res = InsertNode(elem, parent->AsDOMNode(), offset); 1.1596 + NS_ENSURE_SUCCESS(res, res); 1.1597 + 1.1598 + // delete old container 1.1599 + return DeleteNode(inNode); 1.1600 +} 1.1601 + 1.1602 +/////////////////////////////////////////////////////////////////////////// 1.1603 +// RemoveContainer: remove inNode, reparenting its children into their 1.1604 +// the parent of inNode 1.1605 +// 1.1606 +nsresult 1.1607 +nsEditor::RemoveContainer(nsIDOMNode* aNode) 1.1608 +{ 1.1609 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.1610 + return RemoveContainer(node); 1.1611 +} 1.1612 + 1.1613 +nsresult 1.1614 +nsEditor::RemoveContainer(nsINode* aNode) 1.1615 +{ 1.1616 + NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER); 1.1617 + 1.1618 + nsCOMPtr<nsINode> parent = aNode->GetParentNode(); 1.1619 + NS_ENSURE_STATE(parent); 1.1620 + 1.1621 + int32_t offset = parent->IndexOf(aNode); 1.1622 + 1.1623 + // loop through the child nodes of inNode and promote them 1.1624 + // into inNode's parent. 1.1625 + uint32_t nodeOrigLen = aNode->GetChildCount(); 1.1626 + 1.1627 + // notify our internal selection state listener 1.1628 + nsAutoRemoveContainerSelNotify selNotify(mRangeUpdater, aNode, parent, offset, nodeOrigLen); 1.1629 + 1.1630 + while (aNode->HasChildren()) { 1.1631 + nsCOMPtr<nsIContent> child = aNode->GetLastChild(); 1.1632 + nsresult rv = DeleteNode(child->AsDOMNode()); 1.1633 + NS_ENSURE_SUCCESS(rv, rv); 1.1634 + 1.1635 + rv = InsertNode(child->AsDOMNode(), parent->AsDOMNode(), offset); 1.1636 + NS_ENSURE_SUCCESS(rv, rv); 1.1637 + } 1.1638 + 1.1639 + return DeleteNode(aNode->AsDOMNode()); 1.1640 +} 1.1641 + 1.1642 + 1.1643 +/////////////////////////////////////////////////////////////////////////// 1.1644 +// InsertContainerAbove: insert a new parent for inNode, returned in outNode, 1.1645 +// which is contructed to be of type aNodeType. outNode becomes 1.1646 +// a child of inNode's earlier parent. 1.1647 +// Callers responsibility to make sure inNode's can be child 1.1648 +// of outNode, and outNode can be child of old parent. 1.1649 +nsresult 1.1650 +nsEditor::InsertContainerAbove( nsIDOMNode *inNode, 1.1651 + nsCOMPtr<nsIDOMNode> *outNode, 1.1652 + const nsAString &aNodeType, 1.1653 + const nsAString *aAttribute, 1.1654 + const nsAString *aValue) 1.1655 +{ 1.1656 + NS_ENSURE_TRUE(inNode && outNode, NS_ERROR_NULL_POINTER); 1.1657 + 1.1658 + nsCOMPtr<nsIContent> node = do_QueryInterface(inNode); 1.1659 + NS_ENSURE_STATE(node); 1.1660 + 1.1661 + nsCOMPtr<dom::Element> element; 1.1662 + nsresult rv = InsertContainerAbove(node, getter_AddRefs(element), aNodeType, 1.1663 + aAttribute, aValue); 1.1664 + *outNode = element ? element->AsDOMNode() : nullptr; 1.1665 + return rv; 1.1666 +} 1.1667 + 1.1668 +nsresult 1.1669 +nsEditor::InsertContainerAbove(nsIContent* aNode, 1.1670 + dom::Element** aOutNode, 1.1671 + const nsAString& aNodeType, 1.1672 + const nsAString* aAttribute, 1.1673 + const nsAString* aValue) 1.1674 +{ 1.1675 + MOZ_ASSERT(aNode); 1.1676 + 1.1677 + nsCOMPtr<nsIContent> parent = aNode->GetParent(); 1.1678 + NS_ENSURE_STATE(parent); 1.1679 + int32_t offset = parent->IndexOf(aNode); 1.1680 + 1.1681 + // create new container 1.1682 + nsCOMPtr<dom::Element> newContent; 1.1683 + 1.1684 + //new call to use instead to get proper HTML element, bug# 39919 1.1685 + nsresult res = CreateHTMLContent(aNodeType, getter_AddRefs(newContent)); 1.1686 + NS_ENSURE_SUCCESS(res, res); 1.1687 + 1.1688 + // set attribute if needed 1.1689 + if (aAttribute && aValue && !aAttribute->IsEmpty()) { 1.1690 + nsIDOMNode* elem = newContent->AsDOMNode(); 1.1691 + res = static_cast<nsIDOMElement*>(elem)->SetAttribute(*aAttribute, *aValue); 1.1692 + NS_ENSURE_SUCCESS(res, res); 1.1693 + } 1.1694 + 1.1695 + // notify our internal selection state listener 1.1696 + nsAutoInsertContainerSelNotify selNotify(mRangeUpdater); 1.1697 + 1.1698 + // put inNode in new parent, outNode 1.1699 + res = DeleteNode(aNode->AsDOMNode()); 1.1700 + NS_ENSURE_SUCCESS(res, res); 1.1701 + 1.1702 + { 1.1703 + nsAutoTxnsConserveSelection conserveSelection(this); 1.1704 + res = InsertNode(aNode->AsDOMNode(), newContent->AsDOMNode(), 0); 1.1705 + NS_ENSURE_SUCCESS(res, res); 1.1706 + } 1.1707 + 1.1708 + // put new parent in doc 1.1709 + res = InsertNode(newContent->AsDOMNode(), parent->AsDOMNode(), offset); 1.1710 + newContent.forget(aOutNode); 1.1711 + return res; 1.1712 +} 1.1713 + 1.1714 +/////////////////////////////////////////////////////////////////////////// 1.1715 +// MoveNode: move aNode to {aParent,aOffset} 1.1716 +nsresult 1.1717 +nsEditor::MoveNode(nsIDOMNode* aNode, nsIDOMNode* aParent, int32_t aOffset) 1.1718 +{ 1.1719 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.1720 + NS_ENSURE_STATE(node); 1.1721 + 1.1722 + nsCOMPtr<nsINode> parent = do_QueryInterface(aParent); 1.1723 + NS_ENSURE_STATE(parent); 1.1724 + 1.1725 + return MoveNode(node, parent, aOffset); 1.1726 +} 1.1727 + 1.1728 +nsresult 1.1729 +nsEditor::MoveNode(nsINode* aNode, nsINode* aParent, int32_t aOffset) 1.1730 +{ 1.1731 + MOZ_ASSERT(aNode); 1.1732 + MOZ_ASSERT(aParent); 1.1733 + MOZ_ASSERT(aOffset == -1 || 1.1734 + (0 <= aOffset && SafeCast<uint32_t>(aOffset) <= aParent->Length())); 1.1735 + 1.1736 + int32_t oldOffset; 1.1737 + nsCOMPtr<nsINode> oldParent = GetNodeLocation(aNode, &oldOffset); 1.1738 + 1.1739 + if (aOffset == -1) { 1.1740 + // Magic value meaning "move to end of aParent". 1.1741 + aOffset = SafeCast<int32_t>(aParent->Length()); 1.1742 + } 1.1743 + 1.1744 + // Don't do anything if it's already in right place. 1.1745 + if (aParent == oldParent && aOffset == oldOffset) { 1.1746 + return NS_OK; 1.1747 + } 1.1748 + 1.1749 + // Notify our internal selection state listener. 1.1750 + nsAutoMoveNodeSelNotify selNotify(mRangeUpdater, oldParent, oldOffset, 1.1751 + aParent, aOffset); 1.1752 + 1.1753 + // Need to adjust aOffset if we are moving aNode further along in its current 1.1754 + // parent. 1.1755 + if (aParent == oldParent && oldOffset < aOffset) { 1.1756 + // This is because when we delete aNode, it will make the offsets after it 1.1757 + // off by one. 1.1758 + aOffset--; 1.1759 + } 1.1760 + 1.1761 + // Hold a reference so aNode doesn't go away when we remove it (bug 772282). 1.1762 + nsCOMPtr<nsINode> kungFuDeathGrip = aNode; 1.1763 + 1.1764 + nsresult rv = DeleteNode(aNode); 1.1765 + NS_ENSURE_SUCCESS(rv, rv); 1.1766 + 1.1767 + return InsertNode(aNode->AsDOMNode(), aParent->AsDOMNode(), aOffset); 1.1768 +} 1.1769 + 1.1770 + 1.1771 +NS_IMETHODIMP 1.1772 +nsEditor::AddEditorObserver(nsIEditorObserver *aObserver) 1.1773 +{ 1.1774 + // we don't keep ownership of the observers. They must 1.1775 + // remove themselves as observers before they are destroyed. 1.1776 + 1.1777 + NS_ENSURE_TRUE(aObserver, NS_ERROR_NULL_POINTER); 1.1778 + 1.1779 + // Make sure the listener isn't already on the list 1.1780 + if (mEditorObservers.IndexOf(aObserver) == -1) 1.1781 + { 1.1782 + if (!mEditorObservers.AppendObject(aObserver)) 1.1783 + return NS_ERROR_FAILURE; 1.1784 + } 1.1785 + 1.1786 + return NS_OK; 1.1787 +} 1.1788 + 1.1789 + 1.1790 +NS_IMETHODIMP 1.1791 +nsEditor::RemoveEditorObserver(nsIEditorObserver *aObserver) 1.1792 +{ 1.1793 + NS_ENSURE_TRUE(aObserver, NS_ERROR_FAILURE); 1.1794 + 1.1795 + if (!mEditorObservers.RemoveObject(aObserver)) 1.1796 + return NS_ERROR_FAILURE; 1.1797 + 1.1798 + return NS_OK; 1.1799 +} 1.1800 + 1.1801 +class EditorInputEventDispatcher : public nsRunnable 1.1802 +{ 1.1803 +public: 1.1804 + EditorInputEventDispatcher(nsEditor* aEditor, 1.1805 + nsIContent* aTarget, 1.1806 + bool aIsComposing) 1.1807 + : mEditor(aEditor) 1.1808 + , mTarget(aTarget) 1.1809 + , mIsComposing(aIsComposing) 1.1810 + { 1.1811 + } 1.1812 + 1.1813 + NS_IMETHOD Run() 1.1814 + { 1.1815 + // Note that we don't need to check mDispatchInputEvent here. We need 1.1816 + // to check it only when the editor requests to dispatch the input event. 1.1817 + 1.1818 + if (!mTarget->IsInDoc()) { 1.1819 + return NS_OK; 1.1820 + } 1.1821 + 1.1822 + nsCOMPtr<nsIPresShell> ps = mEditor->GetPresShell(); 1.1823 + if (!ps) { 1.1824 + return NS_OK; 1.1825 + } 1.1826 + 1.1827 + nsCOMPtr<nsIWidget> widget = mEditor->GetWidget(); 1.1828 + if (!widget) { 1.1829 + return NS_OK; 1.1830 + } 1.1831 + 1.1832 + // Even if the change is caused by untrusted event, we need to dispatch 1.1833 + // trusted input event since it's a fact. 1.1834 + InternalEditorInputEvent inputEvent(true, NS_EDITOR_INPUT, widget); 1.1835 + inputEvent.time = static_cast<uint64_t>(PR_Now() / 1000); 1.1836 + inputEvent.mIsComposing = mIsComposing; 1.1837 + nsEventStatus status = nsEventStatus_eIgnore; 1.1838 + nsresult rv = 1.1839 + ps->HandleEventWithTarget(&inputEvent, nullptr, mTarget, &status); 1.1840 + NS_ENSURE_SUCCESS(rv, NS_OK); // print the warning if error 1.1841 + return NS_OK; 1.1842 + } 1.1843 + 1.1844 +private: 1.1845 + nsRefPtr<nsEditor> mEditor; 1.1846 + nsCOMPtr<nsIContent> mTarget; 1.1847 + bool mIsComposing; 1.1848 +}; 1.1849 + 1.1850 +void nsEditor::NotifyEditorObservers(void) 1.1851 +{ 1.1852 + for (int32_t i = 0; i < mEditorObservers.Count(); i++) { 1.1853 + mEditorObservers[i]->EditAction(); 1.1854 + } 1.1855 + 1.1856 + if (!mDispatchInputEvent) { 1.1857 + return; 1.1858 + } 1.1859 + 1.1860 + FireInputEvent(); 1.1861 +} 1.1862 + 1.1863 +void 1.1864 +nsEditor::FireInputEvent() 1.1865 +{ 1.1866 + // We don't need to dispatch multiple input events if there is a pending 1.1867 + // input event. However, it may have different event target. If we resolved 1.1868 + // this issue, we need to manage the pending events in an array. But it's 1.1869 + // overwork. We don't need to do it for the very rare case. 1.1870 + 1.1871 + nsCOMPtr<nsIContent> target = GetInputEventTargetContent(); 1.1872 + NS_ENSURE_TRUE_VOID(target); 1.1873 + 1.1874 + // NOTE: Don't refer IsIMEComposing() because it returns false even before 1.1875 + // compositionend. However, DOM Level 3 Events defines it should be 1.1876 + // true after compositionstart and before compositionend. 1.1877 + nsContentUtils::AddScriptRunner( 1.1878 + new EditorInputEventDispatcher(this, target, !!GetComposition())); 1.1879 +} 1.1880 + 1.1881 +NS_IMETHODIMP 1.1882 +nsEditor::AddEditActionListener(nsIEditActionListener *aListener) 1.1883 +{ 1.1884 + NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER); 1.1885 + 1.1886 + // Make sure the listener isn't already on the list 1.1887 + if (mActionListeners.IndexOf(aListener) == -1) 1.1888 + { 1.1889 + if (!mActionListeners.AppendObject(aListener)) 1.1890 + return NS_ERROR_FAILURE; 1.1891 + } 1.1892 + 1.1893 + return NS_OK; 1.1894 +} 1.1895 + 1.1896 + 1.1897 +NS_IMETHODIMP 1.1898 +nsEditor::RemoveEditActionListener(nsIEditActionListener *aListener) 1.1899 +{ 1.1900 + NS_ENSURE_TRUE(aListener, NS_ERROR_FAILURE); 1.1901 + 1.1902 + if (!mActionListeners.RemoveObject(aListener)) 1.1903 + return NS_ERROR_FAILURE; 1.1904 + 1.1905 + return NS_OK; 1.1906 +} 1.1907 + 1.1908 + 1.1909 +NS_IMETHODIMP 1.1910 +nsEditor::AddDocumentStateListener(nsIDocumentStateListener *aListener) 1.1911 +{ 1.1912 + NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER); 1.1913 + 1.1914 + if (mDocStateListeners.IndexOf(aListener) == -1) 1.1915 + { 1.1916 + if (!mDocStateListeners.AppendObject(aListener)) 1.1917 + return NS_ERROR_FAILURE; 1.1918 + } 1.1919 + 1.1920 + return NS_OK; 1.1921 +} 1.1922 + 1.1923 + 1.1924 +NS_IMETHODIMP 1.1925 +nsEditor::RemoveDocumentStateListener(nsIDocumentStateListener *aListener) 1.1926 +{ 1.1927 + NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER); 1.1928 + 1.1929 + if (!mDocStateListeners.RemoveObject(aListener)) 1.1930 + return NS_ERROR_FAILURE; 1.1931 + 1.1932 + return NS_OK; 1.1933 +} 1.1934 + 1.1935 + 1.1936 +NS_IMETHODIMP nsEditor::OutputToString(const nsAString& aFormatType, 1.1937 + uint32_t aFlags, 1.1938 + nsAString& aOutputString) 1.1939 +{ 1.1940 + // these should be implemented by derived classes. 1.1941 + return NS_ERROR_NOT_IMPLEMENTED; 1.1942 +} 1.1943 + 1.1944 +NS_IMETHODIMP 1.1945 +nsEditor::OutputToStream(nsIOutputStream* aOutputStream, 1.1946 + const nsAString& aFormatType, 1.1947 + const nsACString& aCharsetOverride, 1.1948 + uint32_t aFlags) 1.1949 +{ 1.1950 + // these should be implemented by derived classes. 1.1951 + return NS_ERROR_NOT_IMPLEMENTED; 1.1952 +} 1.1953 + 1.1954 +NS_IMETHODIMP 1.1955 +nsEditor::DumpContentTree() 1.1956 +{ 1.1957 +#ifdef DEBUG 1.1958 + if (mRootElement) { 1.1959 + mRootElement->List(stdout); 1.1960 + } 1.1961 +#endif 1.1962 + return NS_OK; 1.1963 +} 1.1964 + 1.1965 + 1.1966 +NS_IMETHODIMP 1.1967 +nsEditor::DebugDumpContent() 1.1968 +{ 1.1969 +#ifdef DEBUG 1.1970 + nsCOMPtr<nsIDOMHTMLDocument> doc = do_QueryReferent(mDocWeak); 1.1971 + NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED); 1.1972 + 1.1973 + nsCOMPtr<nsIDOMHTMLElement>bodyElem; 1.1974 + doc->GetBody(getter_AddRefs(bodyElem)); 1.1975 + nsCOMPtr<nsIContent> content = do_QueryInterface(bodyElem); 1.1976 + if (content) 1.1977 + content->List(); 1.1978 +#endif 1.1979 + return NS_OK; 1.1980 +} 1.1981 + 1.1982 + 1.1983 +NS_IMETHODIMP 1.1984 +nsEditor::DebugUnitTests(int32_t *outNumTests, int32_t *outNumTestsFailed) 1.1985 +{ 1.1986 +#ifdef DEBUG 1.1987 + NS_NOTREACHED("This should never get called. Overridden by subclasses"); 1.1988 +#endif 1.1989 + return NS_OK; 1.1990 +} 1.1991 + 1.1992 + 1.1993 +bool 1.1994 +nsEditor::ArePreservingSelection() 1.1995 +{ 1.1996 + return !(mSavedSel.IsEmpty()); 1.1997 +} 1.1998 + 1.1999 +void 1.2000 +nsEditor::PreserveSelectionAcrossActions(Selection* aSel) 1.2001 +{ 1.2002 + mSavedSel.SaveSelection(aSel); 1.2003 + mRangeUpdater.RegisterSelectionState(mSavedSel); 1.2004 +} 1.2005 + 1.2006 +nsresult 1.2007 +nsEditor::RestorePreservedSelection(nsISelection *aSel) 1.2008 +{ 1.2009 + if (mSavedSel.IsEmpty()) return NS_ERROR_FAILURE; 1.2010 + mSavedSel.RestoreSelection(aSel); 1.2011 + StopPreservingSelection(); 1.2012 + return NS_OK; 1.2013 +} 1.2014 + 1.2015 +void 1.2016 +nsEditor::StopPreservingSelection() 1.2017 +{ 1.2018 + mRangeUpdater.DropSelectionState(mSavedSel); 1.2019 + mSavedSel.MakeEmpty(); 1.2020 +} 1.2021 + 1.2022 +void 1.2023 +nsEditor::EnsureComposition(mozilla::WidgetGUIEvent* aEvent) 1.2024 +{ 1.2025 + if (mComposition) { 1.2026 + return; 1.2027 + } 1.2028 + // The compositionstart event must cause creating new TextComposition 1.2029 + // instance at being dispatched by IMEStateManager. 1.2030 + mComposition = IMEStateManager::GetTextCompositionFor(aEvent); 1.2031 + if (!mComposition) { 1.2032 + MOZ_CRASH("IMEStateManager doesn't return proper composition"); 1.2033 + } 1.2034 + mComposition->StartHandlingComposition(this); 1.2035 +} 1.2036 + 1.2037 +nsresult 1.2038 +nsEditor::BeginIMEComposition(WidgetCompositionEvent* aCompositionEvent) 1.2039 +{ 1.2040 + MOZ_ASSERT(!mComposition, "There is composition already"); 1.2041 + EnsureComposition(aCompositionEvent); 1.2042 + if (mPhonetic) { 1.2043 + mPhonetic->Truncate(0); 1.2044 + } 1.2045 + return NS_OK; 1.2046 +} 1.2047 + 1.2048 +void 1.2049 +nsEditor::EndIMEComposition() 1.2050 +{ 1.2051 + NS_ENSURE_TRUE_VOID(mComposition); // nothing to do 1.2052 + 1.2053 + // commit the IME transaction..we can get at it via the transaction mgr. 1.2054 + // Note that this means IME won't work without an undo stack! 1.2055 + if (mTxnMgr) { 1.2056 + nsCOMPtr<nsITransaction> txn = mTxnMgr->PeekUndoStack(); 1.2057 + nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryInterface(txn); 1.2058 + if (plcTxn) { 1.2059 + DebugOnly<nsresult> rv = plcTxn->Commit(); 1.2060 + NS_ASSERTION(NS_SUCCEEDED(rv), 1.2061 + "nsIAbsorbingTransaction::Commit() failed"); 1.2062 + } 1.2063 + } 1.2064 + 1.2065 + /* reset the data we need to construct a transaction */ 1.2066 + mIMETextNode = nullptr; 1.2067 + mIMETextOffset = 0; 1.2068 + mComposition->EndHandlingComposition(this); 1.2069 + mComposition = nullptr; 1.2070 + 1.2071 + // notify editor observers of action 1.2072 + NotifyEditorObservers(); 1.2073 +} 1.2074 + 1.2075 + 1.2076 +NS_IMETHODIMP 1.2077 +nsEditor::GetPhonetic(nsAString& aPhonetic) 1.2078 +{ 1.2079 + if (mPhonetic) 1.2080 + aPhonetic = *mPhonetic; 1.2081 + else 1.2082 + aPhonetic.Truncate(0); 1.2083 + 1.2084 + return NS_OK; 1.2085 +} 1.2086 + 1.2087 +NS_IMETHODIMP 1.2088 +nsEditor::ForceCompositionEnd() 1.2089 +{ 1.2090 + nsCOMPtr<nsIPresShell> ps = GetPresShell(); 1.2091 + if (!ps) { 1.2092 + return NS_ERROR_NOT_AVAILABLE; 1.2093 + } 1.2094 + nsPresContext* pc = ps->GetPresContext(); 1.2095 + if (!pc) { 1.2096 + return NS_ERROR_NOT_AVAILABLE; 1.2097 + } 1.2098 + 1.2099 + if (!mComposition) { 1.2100 + // XXXmnakano see bug 558976, ResetInputState() has two meaning which are 1.2101 + // "commit the composition" and "cursor is moved". This method name is 1.2102 + // "ForceCompositionEnd", so, ResetInputState() should be used only for the 1.2103 + // former here. However, ResetInputState() is also used for the latter here 1.2104 + // because even if we don't have composition, we call ResetInputState() on 1.2105 + // Linux. Currently, nsGtkIMModule can know the timing of the cursor move, 1.2106 + // so, the latter meaning should be gone. 1.2107 + // XXX This may commit a composition in another editor. 1.2108 + return IMEStateManager::NotifyIME(NOTIFY_IME_OF_CURSOR_POS_CHANGED, pc); 1.2109 + } 1.2110 + 1.2111 + return IMEStateManager::NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, pc); 1.2112 +} 1.2113 + 1.2114 +NS_IMETHODIMP 1.2115 +nsEditor::GetPreferredIMEState(IMEState *aState) 1.2116 +{ 1.2117 + NS_ENSURE_ARG_POINTER(aState); 1.2118 + aState->mEnabled = IMEState::ENABLED; 1.2119 + aState->mOpen = IMEState::DONT_CHANGE_OPEN_STATE; 1.2120 + 1.2121 + if (IsReadonly() || IsDisabled()) { 1.2122 + aState->mEnabled = IMEState::DISABLED; 1.2123 + return NS_OK; 1.2124 + } 1.2125 + 1.2126 + nsCOMPtr<nsIContent> content = GetRoot(); 1.2127 + NS_ENSURE_TRUE(content, NS_ERROR_FAILURE); 1.2128 + 1.2129 + nsIFrame* frame = content->GetPrimaryFrame(); 1.2130 + NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE); 1.2131 + 1.2132 + switch (frame->StyleUIReset()->mIMEMode) { 1.2133 + case NS_STYLE_IME_MODE_AUTO: 1.2134 + if (IsPasswordEditor()) 1.2135 + aState->mEnabled = IMEState::PASSWORD; 1.2136 + break; 1.2137 + case NS_STYLE_IME_MODE_DISABLED: 1.2138 + // we should use password state for |ime-mode: disabled;|. 1.2139 + aState->mEnabled = IMEState::PASSWORD; 1.2140 + break; 1.2141 + case NS_STYLE_IME_MODE_ACTIVE: 1.2142 + aState->mOpen = IMEState::OPEN; 1.2143 + break; 1.2144 + case NS_STYLE_IME_MODE_INACTIVE: 1.2145 + aState->mOpen = IMEState::CLOSED; 1.2146 + break; 1.2147 + } 1.2148 + 1.2149 + return NS_OK; 1.2150 +} 1.2151 + 1.2152 +NS_IMETHODIMP 1.2153 +nsEditor::GetComposing(bool* aResult) 1.2154 +{ 1.2155 + NS_ENSURE_ARG_POINTER(aResult); 1.2156 + *aResult = IsIMEComposing(); 1.2157 + return NS_OK; 1.2158 +} 1.2159 + 1.2160 + 1.2161 +/* Non-interface, public methods */ 1.2162 + 1.2163 +NS_IMETHODIMP 1.2164 +nsEditor::GetRootElement(nsIDOMElement **aRootElement) 1.2165 +{ 1.2166 + NS_ENSURE_ARG_POINTER(aRootElement); 1.2167 + NS_ENSURE_TRUE(mRootElement, NS_ERROR_NOT_AVAILABLE); 1.2168 + nsCOMPtr<nsIDOMElement> rootElement = do_QueryInterface(mRootElement); 1.2169 + rootElement.forget(aRootElement); 1.2170 + return NS_OK; 1.2171 +} 1.2172 + 1.2173 + 1.2174 +/** All editor operations which alter the doc should be prefaced 1.2175 + * with a call to StartOperation, naming the action and direction */ 1.2176 +NS_IMETHODIMP 1.2177 +nsEditor::StartOperation(EditAction opID, nsIEditor::EDirection aDirection) 1.2178 +{ 1.2179 + mAction = opID; 1.2180 + mDirection = aDirection; 1.2181 + return NS_OK; 1.2182 +} 1.2183 + 1.2184 + 1.2185 +/** All editor operations which alter the doc should be followed 1.2186 + * with a call to EndOperation */ 1.2187 +NS_IMETHODIMP 1.2188 +nsEditor::EndOperation() 1.2189 +{ 1.2190 + mAction = EditAction::none; 1.2191 + mDirection = eNone; 1.2192 + return NS_OK; 1.2193 +} 1.2194 + 1.2195 +NS_IMETHODIMP 1.2196 +nsEditor::CloneAttribute(const nsAString & aAttribute, 1.2197 + nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode) 1.2198 +{ 1.2199 + NS_ENSURE_TRUE(aDestNode && aSourceNode, NS_ERROR_NULL_POINTER); 1.2200 + 1.2201 + nsCOMPtr<nsIDOMElement> destElement = do_QueryInterface(aDestNode); 1.2202 + nsCOMPtr<nsIDOMElement> sourceElement = do_QueryInterface(aSourceNode); 1.2203 + NS_ENSURE_TRUE(destElement && sourceElement, NS_ERROR_NO_INTERFACE); 1.2204 + 1.2205 + nsAutoString attrValue; 1.2206 + bool isAttrSet; 1.2207 + nsresult rv = GetAttributeValue(sourceElement, 1.2208 + aAttribute, 1.2209 + attrValue, 1.2210 + &isAttrSet); 1.2211 + NS_ENSURE_SUCCESS(rv, rv); 1.2212 + if (isAttrSet) 1.2213 + rv = SetAttribute(destElement, aAttribute, attrValue); 1.2214 + else 1.2215 + rv = RemoveAttribute(destElement, aAttribute); 1.2216 + 1.2217 + return rv; 1.2218 +} 1.2219 + 1.2220 +// Objects must be DOM elements 1.2221 +NS_IMETHODIMP 1.2222 +nsEditor::CloneAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode) 1.2223 +{ 1.2224 + NS_ENSURE_TRUE(aDestNode && aSourceNode, NS_ERROR_NULL_POINTER); 1.2225 + 1.2226 + nsCOMPtr<nsIDOMElement> destElement = do_QueryInterface(aDestNode); 1.2227 + nsCOMPtr<nsIDOMElement> sourceElement = do_QueryInterface(aSourceNode); 1.2228 + NS_ENSURE_TRUE(destElement && sourceElement, NS_ERROR_NO_INTERFACE); 1.2229 + 1.2230 + nsCOMPtr<nsIDOMMozNamedAttrMap> sourceAttributes; 1.2231 + sourceElement->GetAttributes(getter_AddRefs(sourceAttributes)); 1.2232 + nsCOMPtr<nsIDOMMozNamedAttrMap> destAttributes; 1.2233 + destElement->GetAttributes(getter_AddRefs(destAttributes)); 1.2234 + NS_ENSURE_TRUE(sourceAttributes && destAttributes, NS_ERROR_FAILURE); 1.2235 + 1.2236 + nsAutoEditBatch beginBatching(this); 1.2237 + 1.2238 + // Use transaction system for undo only if destination 1.2239 + // is already in the document 1.2240 + nsCOMPtr<nsIDOMNode> p = aDestNode; 1.2241 + nsCOMPtr<nsIDOMNode> rootNode = do_QueryInterface(GetRoot()); 1.2242 + NS_ENSURE_TRUE(rootNode, NS_ERROR_NULL_POINTER); 1.2243 + bool destInBody = true; 1.2244 + while (p && p != rootNode) 1.2245 + { 1.2246 + nsCOMPtr<nsIDOMNode> tmp; 1.2247 + if (NS_FAILED(p->GetParentNode(getter_AddRefs(tmp))) || !tmp) 1.2248 + { 1.2249 + destInBody = false; 1.2250 + break; 1.2251 + } 1.2252 + p = tmp; 1.2253 + } 1.2254 + 1.2255 + uint32_t sourceCount; 1.2256 + sourceAttributes->GetLength(&sourceCount); 1.2257 + uint32_t destCount; 1.2258 + destAttributes->GetLength(&destCount); 1.2259 + nsCOMPtr<nsIDOMAttr> attr; 1.2260 + 1.2261 + // Clear existing attributes 1.2262 + for (uint32_t i = 0; i < destCount; i++) { 1.2263 + // always remove item number 0 (first item in list) 1.2264 + if (NS_SUCCEEDED(destAttributes->Item(0, getter_AddRefs(attr))) && attr) { 1.2265 + nsString str; 1.2266 + if (NS_SUCCEEDED(attr->GetName(str))) { 1.2267 + if (destInBody) { 1.2268 + RemoveAttribute(destElement, str); 1.2269 + } else { 1.2270 + destElement->RemoveAttribute(str); 1.2271 + } 1.2272 + } 1.2273 + } 1.2274 + } 1.2275 + 1.2276 + nsresult result = NS_OK; 1.2277 + 1.2278 + // Set just the attributes that the source element has 1.2279 + for (uint32_t i = 0; i < sourceCount; i++) 1.2280 + { 1.2281 + if (NS_SUCCEEDED(sourceAttributes->Item(i, getter_AddRefs(attr))) && attr) { 1.2282 + nsString sourceAttrName; 1.2283 + if (NS_SUCCEEDED(attr->GetName(sourceAttrName))) { 1.2284 + nsString sourceAttrValue; 1.2285 + /* Presence of an attribute in the named node map indicates that it was 1.2286 + * set on the element even if it has no value. 1.2287 + */ 1.2288 + if (NS_SUCCEEDED(attr->GetValue(sourceAttrValue))) { 1.2289 + if (destInBody) { 1.2290 + result = SetAttributeOrEquivalent(destElement, sourceAttrName, sourceAttrValue, false); 1.2291 + } else { 1.2292 + // the element is not inserted in the document yet, we don't want to put a 1.2293 + // transaction on the UndoStack 1.2294 + result = SetAttributeOrEquivalent(destElement, sourceAttrName, sourceAttrValue, true); 1.2295 + } 1.2296 + } else { 1.2297 + // Do we ever get here? 1.2298 +#if DEBUG_cmanske 1.2299 + printf("Attribute in sourceAttribute has empty value in nsEditor::CloneAttributes()\n"); 1.2300 +#endif 1.2301 + } 1.2302 + } 1.2303 + } 1.2304 + } 1.2305 + return result; 1.2306 +} 1.2307 + 1.2308 + 1.2309 +NS_IMETHODIMP nsEditor::ScrollSelectionIntoView(bool aScrollToAnchor) 1.2310 +{ 1.2311 + nsCOMPtr<nsISelectionController> selCon; 1.2312 + if (NS_SUCCEEDED(GetSelectionController(getter_AddRefs(selCon))) && selCon) 1.2313 + { 1.2314 + int16_t region = nsISelectionController::SELECTION_FOCUS_REGION; 1.2315 + 1.2316 + if (aScrollToAnchor) 1.2317 + region = nsISelectionController::SELECTION_ANCHOR_REGION; 1.2318 + 1.2319 + selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, 1.2320 + region, nsISelectionController::SCROLL_OVERFLOW_HIDDEN); 1.2321 + } 1.2322 + 1.2323 + return NS_OK; 1.2324 +} 1.2325 + 1.2326 +NS_IMETHODIMP 1.2327 +nsEditor::InsertTextImpl(const nsAString& aStringToInsert, 1.2328 + nsCOMPtr<nsIDOMNode>* aInOutNode, 1.2329 + int32_t* aInOutOffset, 1.2330 + nsIDOMDocument* aDoc) 1.2331 +{ 1.2332 + // NOTE: caller *must* have already used nsAutoTxnsConserveSelection 1.2333 + // stack-based class to turn off txn selection updating. Caller also turned 1.2334 + // on rules sniffing if desired. 1.2335 + 1.2336 + NS_ENSURE_TRUE(aInOutNode && *aInOutNode && aInOutOffset && aDoc, 1.2337 + NS_ERROR_NULL_POINTER); 1.2338 + if (!mComposition && aStringToInsert.IsEmpty()) { 1.2339 + return NS_OK; 1.2340 + } 1.2341 + 1.2342 + nsCOMPtr<nsINode> node = do_QueryInterface(*aInOutNode); 1.2343 + NS_ENSURE_STATE(node); 1.2344 + uint32_t offset = static_cast<uint32_t>(*aInOutOffset); 1.2345 + 1.2346 + if (!node->IsNodeOfType(nsINode::eTEXT) && IsPlaintextEditor()) { 1.2347 + nsCOMPtr<nsINode> root = GetRoot(); 1.2348 + // In some cases, node is the anonymous DIV, and offset is 0. To avoid 1.2349 + // injecting unneeded text nodes, we first look to see if we have one 1.2350 + // available. In that case, we'll just adjust node and offset accordingly. 1.2351 + if (node == root && offset == 0 && node->HasChildren() && 1.2352 + node->GetFirstChild()->IsNodeOfType(nsINode::eTEXT)) { 1.2353 + node = node->GetFirstChild(); 1.2354 + } 1.2355 + // In some other cases, node is the anonymous DIV, and offset points to the 1.2356 + // terminating mozBR. In that case, we'll adjust aInOutNode and 1.2357 + // aInOutOffset to the preceding text node, if any. 1.2358 + if (node == root && offset > 0 && node->GetChildAt(offset - 1) && 1.2359 + node->GetChildAt(offset - 1)->IsNodeOfType(nsINode::eTEXT)) { 1.2360 + node = node->GetChildAt(offset - 1); 1.2361 + offset = node->Length(); 1.2362 + } 1.2363 + // Sometimes, node is the mozBR element itself. In that case, we'll adjust 1.2364 + // the insertion point to the previous text node, if one exists, or to the 1.2365 + // parent anonymous DIV. 1.2366 + if (nsTextEditUtils::IsMozBR(node) && offset == 0) { 1.2367 + if (node->GetPreviousSibling() && 1.2368 + node->GetPreviousSibling()->IsNodeOfType(nsINode::eTEXT)) { 1.2369 + node = node->GetPreviousSibling(); 1.2370 + offset = node->Length(); 1.2371 + } else if (node->GetParentNode() && node->GetParentNode() == root) { 1.2372 + node = node->GetParentNode(); 1.2373 + } 1.2374 + } 1.2375 + } 1.2376 + 1.2377 + nsresult res; 1.2378 + if (mComposition) { 1.2379 + if (!node->IsNodeOfType(nsINode::eTEXT)) { 1.2380 + // create a text node 1.2381 + nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc); 1.2382 + NS_ENSURE_STATE(doc); 1.2383 + nsRefPtr<nsTextNode> newNode = doc->CreateTextNode(EmptyString()); 1.2384 + // then we insert it into the dom tree 1.2385 + res = InsertNode(newNode->AsDOMNode(), node->AsDOMNode(), offset); 1.2386 + NS_ENSURE_SUCCESS(res, res); 1.2387 + node = newNode; 1.2388 + offset = 0; 1.2389 + } 1.2390 + nsCOMPtr<nsIDOMCharacterData> charDataNode = do_QueryInterface(node); 1.2391 + NS_ENSURE_STATE(charDataNode); 1.2392 + res = InsertTextIntoTextNodeImpl(aStringToInsert, charDataNode, offset); 1.2393 + NS_ENSURE_SUCCESS(res, res); 1.2394 + offset += aStringToInsert.Length(); 1.2395 + } else { 1.2396 + if (node->IsNodeOfType(nsINode::eTEXT)) { 1.2397 + // we are inserting text into an existing text node. 1.2398 + nsCOMPtr<nsIDOMCharacterData> charDataNode = do_QueryInterface(node); 1.2399 + NS_ENSURE_STATE(charDataNode); 1.2400 + res = InsertTextIntoTextNodeImpl(aStringToInsert, charDataNode, offset); 1.2401 + NS_ENSURE_SUCCESS(res, res); 1.2402 + offset += aStringToInsert.Length(); 1.2403 + } else { 1.2404 + // we are inserting text into a non-text node. first we have to create a 1.2405 + // textnode (this also populates it with the text) 1.2406 + nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc); 1.2407 + NS_ENSURE_STATE(doc); 1.2408 + nsRefPtr<nsTextNode> newNode = doc->CreateTextNode(aStringToInsert); 1.2409 + // then we insert it into the dom tree 1.2410 + res = InsertNode(newNode->AsDOMNode(), node->AsDOMNode(), offset); 1.2411 + NS_ENSURE_SUCCESS(res, res); 1.2412 + node = newNode; 1.2413 + offset = aStringToInsert.Length(); 1.2414 + } 1.2415 + } 1.2416 + 1.2417 + *aInOutNode = node->AsDOMNode(); 1.2418 + *aInOutOffset = static_cast<int32_t>(offset); 1.2419 + return NS_OK; 1.2420 +} 1.2421 + 1.2422 + 1.2423 +nsresult nsEditor::InsertTextIntoTextNodeImpl(const nsAString& aStringToInsert, 1.2424 + nsIDOMCharacterData *aTextNode, 1.2425 + int32_t aOffset, 1.2426 + bool aSuppressIME) 1.2427 +{ 1.2428 + nsRefPtr<EditTxn> txn; 1.2429 + nsresult result = NS_OK; 1.2430 + bool isIMETransaction = false; 1.2431 + // aSuppressIME is used when editor must insert text, yet this text is not 1.2432 + // part of current ime operation. example: adjusting whitespace around an ime insertion. 1.2433 + if (mComposition && !aSuppressIME) { 1.2434 + if (!mIMETextNode) { 1.2435 + mIMETextNode = aTextNode; 1.2436 + mIMETextOffset = aOffset; 1.2437 + } 1.2438 + // Modify mPhonetic with raw text input clauses. 1.2439 + const TextRangeArray* ranges = mComposition->GetRanges(); 1.2440 + for (uint32_t i = 0; i < (ranges ? ranges->Length() : 0); ++i) { 1.2441 + const TextRange& textRange = ranges->ElementAt(i); 1.2442 + if (!textRange.Length() || 1.2443 + textRange.mRangeType != NS_TEXTRANGE_RAWINPUT) { 1.2444 + continue; 1.2445 + } 1.2446 + if (!mPhonetic) { 1.2447 + mPhonetic = new nsString(); 1.2448 + } 1.2449 + nsAutoString stringToInsert(aStringToInsert); 1.2450 + stringToInsert.Mid(*mPhonetic, 1.2451 + textRange.mStartOffset, textRange.Length()); 1.2452 + } 1.2453 + 1.2454 + nsRefPtr<IMETextTxn> imeTxn; 1.2455 + result = CreateTxnForIMEText(aStringToInsert, getter_AddRefs(imeTxn)); 1.2456 + txn = imeTxn; 1.2457 + isIMETransaction = true; 1.2458 + } 1.2459 + else 1.2460 + { 1.2461 + nsRefPtr<InsertTextTxn> insertTxn; 1.2462 + result = CreateTxnForInsertText(aStringToInsert, aTextNode, aOffset, 1.2463 + getter_AddRefs(insertTxn)); 1.2464 + txn = insertTxn; 1.2465 + } 1.2466 + NS_ENSURE_SUCCESS(result, result); 1.2467 + 1.2468 + // let listeners know what's up 1.2469 + int32_t i; 1.2470 + for (i = 0; i < mActionListeners.Count(); i++) 1.2471 + mActionListeners[i]->WillInsertText(aTextNode, aOffset, aStringToInsert); 1.2472 + 1.2473 + // XXX we may not need these view batches anymore. This is handled at a higher level now I believe 1.2474 + BeginUpdateViewBatch(); 1.2475 + result = DoTransaction(txn); 1.2476 + EndUpdateViewBatch(); 1.2477 + 1.2478 + mRangeUpdater.SelAdjInsertText(aTextNode, aOffset, aStringToInsert); 1.2479 + 1.2480 + // let listeners know what happened 1.2481 + for (i = 0; i < mActionListeners.Count(); i++) 1.2482 + mActionListeners[i]->DidInsertText(aTextNode, aOffset, aStringToInsert, result); 1.2483 + 1.2484 + // Added some cruft here for bug 43366. Layout was crashing because we left an 1.2485 + // empty text node lying around in the document. So I delete empty text nodes 1.2486 + // caused by IME. I have to mark the IME transaction as "fixed", which means 1.2487 + // that furure ime txns won't merge with it. This is because we don't want 1.2488 + // future ime txns trying to put their text into a node that is no longer in 1.2489 + // the document. This does not break undo/redo, because all these txns are 1.2490 + // wrapped in a parent PlaceHolder txn, and placeholder txns are already 1.2491 + // savvy to having multiple ime txns inside them. 1.2492 + 1.2493 + // delete empty ime text node if there is one 1.2494 + if (isIMETransaction && mIMETextNode) 1.2495 + { 1.2496 + uint32_t len; 1.2497 + mIMETextNode->GetLength(&len); 1.2498 + if (!len) 1.2499 + { 1.2500 + DeleteNode(mIMETextNode); 1.2501 + mIMETextNode = nullptr; 1.2502 + static_cast<IMETextTxn*>(txn.get())->MarkFixed(); // mark the ime txn "fixed" 1.2503 + } 1.2504 + } 1.2505 + 1.2506 + return result; 1.2507 +} 1.2508 + 1.2509 + 1.2510 +NS_IMETHODIMP nsEditor::SelectEntireDocument(nsISelection *aSelection) 1.2511 +{ 1.2512 + if (!aSelection) { return NS_ERROR_NULL_POINTER; } 1.2513 + 1.2514 + nsCOMPtr<nsIDOMElement> rootElement = do_QueryInterface(GetRoot()); 1.2515 + if (!rootElement) { return NS_ERROR_NOT_INITIALIZED; } 1.2516 + 1.2517 + return aSelection->SelectAllChildren(rootElement); 1.2518 +} 1.2519 + 1.2520 + 1.2521 +nsINode* 1.2522 +nsEditor::GetFirstEditableNode(nsINode* aRoot) 1.2523 +{ 1.2524 + MOZ_ASSERT(aRoot); 1.2525 + 1.2526 + nsIContent* node = GetLeftmostChild(aRoot); 1.2527 + if (node && !IsEditable(node)) { 1.2528 + node = GetNextNode(node, /* aEditableNode = */ true); 1.2529 + } 1.2530 + 1.2531 + return (node != aRoot) ? node : nullptr; 1.2532 +} 1.2533 + 1.2534 + 1.2535 +NS_IMETHODIMP 1.2536 +nsEditor::NotifyDocumentListeners(TDocumentListenerNotification aNotificationType) 1.2537 +{ 1.2538 + int32_t numListeners = mDocStateListeners.Count(); 1.2539 + if (!numListeners) // maybe there just aren't any. 1.2540 + return NS_OK; 1.2541 + 1.2542 + nsCOMArray<nsIDocumentStateListener> listeners(mDocStateListeners); 1.2543 + nsresult rv = NS_OK; 1.2544 + int32_t i; 1.2545 + 1.2546 + switch (aNotificationType) 1.2547 + { 1.2548 + case eDocumentCreated: 1.2549 + for (i = 0; i < numListeners;i++) 1.2550 + { 1.2551 + rv = listeners[i]->NotifyDocumentCreated(); 1.2552 + if (NS_FAILED(rv)) 1.2553 + break; 1.2554 + } 1.2555 + break; 1.2556 + 1.2557 + case eDocumentToBeDestroyed: 1.2558 + for (i = 0; i < numListeners;i++) 1.2559 + { 1.2560 + rv = listeners[i]->NotifyDocumentWillBeDestroyed(); 1.2561 + if (NS_FAILED(rv)) 1.2562 + break; 1.2563 + } 1.2564 + break; 1.2565 + 1.2566 + case eDocumentStateChanged: 1.2567 + { 1.2568 + bool docIsDirty; 1.2569 + rv = GetDocumentModified(&docIsDirty); 1.2570 + NS_ENSURE_SUCCESS(rv, rv); 1.2571 + 1.2572 + if (static_cast<int8_t>(docIsDirty) == mDocDirtyState) 1.2573 + return NS_OK; 1.2574 + 1.2575 + mDocDirtyState = docIsDirty; 1.2576 + 1.2577 + for (i = 0; i < numListeners;i++) 1.2578 + { 1.2579 + rv = listeners[i]->NotifyDocumentStateChanged(mDocDirtyState); 1.2580 + if (NS_FAILED(rv)) 1.2581 + break; 1.2582 + } 1.2583 + } 1.2584 + break; 1.2585 + 1.2586 + default: 1.2587 + NS_NOTREACHED("Unknown notification"); 1.2588 + } 1.2589 + 1.2590 + return rv; 1.2591 +} 1.2592 + 1.2593 + 1.2594 +NS_IMETHODIMP nsEditor::CreateTxnForInsertText(const nsAString & aStringToInsert, 1.2595 + nsIDOMCharacterData *aTextNode, 1.2596 + int32_t aOffset, 1.2597 + InsertTextTxn ** aTxn) 1.2598 +{ 1.2599 + NS_ENSURE_TRUE(aTextNode && aTxn, NS_ERROR_NULL_POINTER); 1.2600 + nsresult rv; 1.2601 + 1.2602 + nsRefPtr<InsertTextTxn> txn = new InsertTextTxn(); 1.2603 + rv = txn->Init(aTextNode, aOffset, aStringToInsert, this); 1.2604 + if (NS_SUCCEEDED(rv)) 1.2605 + { 1.2606 + txn.forget(aTxn); 1.2607 + } 1.2608 + 1.2609 + return rv; 1.2610 +} 1.2611 + 1.2612 + 1.2613 +NS_IMETHODIMP nsEditor::DeleteText(nsIDOMCharacterData *aElement, 1.2614 + uint32_t aOffset, 1.2615 + uint32_t aLength) 1.2616 +{ 1.2617 + nsRefPtr<DeleteTextTxn> txn; 1.2618 + nsresult result = CreateTxnForDeleteText(aElement, aOffset, aLength, 1.2619 + getter_AddRefs(txn)); 1.2620 + nsAutoRules beginRulesSniffing(this, EditAction::deleteText, nsIEditor::ePrevious); 1.2621 + if (NS_SUCCEEDED(result)) 1.2622 + { 1.2623 + // let listeners know what's up 1.2624 + int32_t i; 1.2625 + for (i = 0; i < mActionListeners.Count(); i++) 1.2626 + mActionListeners[i]->WillDeleteText(aElement, aOffset, aLength); 1.2627 + 1.2628 + result = DoTransaction(txn); 1.2629 + 1.2630 + // let listeners know what happened 1.2631 + for (i = 0; i < mActionListeners.Count(); i++) 1.2632 + mActionListeners[i]->DidDeleteText(aElement, aOffset, aLength, result); 1.2633 + } 1.2634 + return result; 1.2635 +} 1.2636 + 1.2637 + 1.2638 +nsresult 1.2639 +nsEditor::CreateTxnForDeleteText(nsIDOMCharacterData* aElement, 1.2640 + uint32_t aOffset, 1.2641 + uint32_t aLength, 1.2642 + DeleteTextTxn** aTxn) 1.2643 +{ 1.2644 + NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER); 1.2645 + 1.2646 + nsRefPtr<DeleteTextTxn> txn = new DeleteTextTxn(); 1.2647 + 1.2648 + nsresult res = txn->Init(this, aElement, aOffset, aLength, &mRangeUpdater); 1.2649 + NS_ENSURE_SUCCESS(res, res); 1.2650 + 1.2651 + txn.forget(aTxn); 1.2652 + return NS_OK; 1.2653 +} 1.2654 + 1.2655 + 1.2656 + 1.2657 + 1.2658 +NS_IMETHODIMP nsEditor::CreateTxnForSplitNode(nsIDOMNode *aNode, 1.2659 + uint32_t aOffset, 1.2660 + SplitElementTxn **aTxn) 1.2661 +{ 1.2662 + NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER); 1.2663 + 1.2664 + nsRefPtr<SplitElementTxn> txn = new SplitElementTxn(); 1.2665 + 1.2666 + nsresult rv = txn->Init(this, aNode, aOffset); 1.2667 + if (NS_SUCCEEDED(rv)) 1.2668 + { 1.2669 + txn.forget(aTxn); 1.2670 + } 1.2671 + 1.2672 + return rv; 1.2673 +} 1.2674 + 1.2675 +NS_IMETHODIMP nsEditor::CreateTxnForJoinNode(nsIDOMNode *aLeftNode, 1.2676 + nsIDOMNode *aRightNode, 1.2677 + JoinElementTxn **aTxn) 1.2678 +{ 1.2679 + NS_ENSURE_TRUE(aLeftNode && aRightNode, NS_ERROR_NULL_POINTER); 1.2680 + 1.2681 + nsRefPtr<JoinElementTxn> txn = new JoinElementTxn(); 1.2682 + 1.2683 + nsresult rv = txn->Init(this, aLeftNode, aRightNode); 1.2684 + if (NS_SUCCEEDED(rv)) 1.2685 + { 1.2686 + txn.forget(aTxn); 1.2687 + } 1.2688 + 1.2689 + return rv; 1.2690 +} 1.2691 + 1.2692 + 1.2693 +// END nsEditor core implementation 1.2694 + 1.2695 + 1.2696 +// BEGIN nsEditor public helper methods 1.2697 + 1.2698 +nsresult 1.2699 +nsEditor::SplitNodeImpl(nsIDOMNode * aExistingRightNode, 1.2700 + int32_t aOffset, 1.2701 + nsIDOMNode* aNewLeftNode, 1.2702 + nsIDOMNode* aParent) 1.2703 +{ 1.2704 + NS_ASSERTION(((nullptr!=aExistingRightNode) && 1.2705 + (nullptr!=aNewLeftNode) && 1.2706 + (nullptr!=aParent)), 1.2707 + "null arg"); 1.2708 + nsresult result; 1.2709 + if ((nullptr!=aExistingRightNode) && 1.2710 + (nullptr!=aNewLeftNode) && 1.2711 + (nullptr!=aParent)) 1.2712 + { 1.2713 + // get selection 1.2714 + nsCOMPtr<nsISelection> selection; 1.2715 + result = GetSelection(getter_AddRefs(selection)); 1.2716 + NS_ENSURE_SUCCESS(result, result); 1.2717 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.2718 + 1.2719 + // remember some selection points 1.2720 + nsCOMPtr<nsIDOMNode> selStartNode, selEndNode; 1.2721 + int32_t selStartOffset, selEndOffset; 1.2722 + result = GetStartNodeAndOffset(selection, getter_AddRefs(selStartNode), &selStartOffset); 1.2723 + if (NS_FAILED(result)) selStartNode = nullptr; // if selection is cleared, remember that 1.2724 + result = GetEndNodeAndOffset(selection, getter_AddRefs(selEndNode), &selEndOffset); 1.2725 + if (NS_FAILED(result)) selStartNode = nullptr; // if selection is cleared, remember that 1.2726 + 1.2727 + nsCOMPtr<nsIDOMNode> resultNode; 1.2728 + result = aParent->InsertBefore(aNewLeftNode, aExistingRightNode, getter_AddRefs(resultNode)); 1.2729 + //printf(" after insert\n"); content->List(); // DEBUG 1.2730 + if (NS_SUCCEEDED(result)) 1.2731 + { 1.2732 + // split the children between the 2 nodes 1.2733 + // at this point, aExistingRightNode has all the children 1.2734 + // move all the children whose index is < aOffset to aNewLeftNode 1.2735 + if (0<=aOffset) // don't bother unless we're going to move at least one child 1.2736 + { 1.2737 + // if it's a text node, just shuffle around some text 1.2738 + nsCOMPtr<nsIDOMCharacterData> rightNodeAsText( do_QueryInterface(aExistingRightNode) ); 1.2739 + nsCOMPtr<nsIDOMCharacterData> leftNodeAsText( do_QueryInterface(aNewLeftNode) ); 1.2740 + if (leftNodeAsText && rightNodeAsText) 1.2741 + { 1.2742 + // fix right node 1.2743 + nsAutoString leftText; 1.2744 + rightNodeAsText->SubstringData(0, aOffset, leftText); 1.2745 + rightNodeAsText->DeleteData(0, aOffset); 1.2746 + // fix left node 1.2747 + leftNodeAsText->SetData(leftText); 1.2748 + // moose 1.2749 + } 1.2750 + else 1.2751 + { // otherwise it's an interior node, so shuffle around the children 1.2752 + // go through list backwards so deletes don't interfere with the iteration 1.2753 + nsCOMPtr<nsIDOMNodeList> childNodes; 1.2754 + result = aExistingRightNode->GetChildNodes(getter_AddRefs(childNodes)); 1.2755 + if ((NS_SUCCEEDED(result)) && (childNodes)) 1.2756 + { 1.2757 + int32_t i=aOffset-1; 1.2758 + for ( ; ((NS_SUCCEEDED(result)) && (0<=i)); i--) 1.2759 + { 1.2760 + nsCOMPtr<nsIDOMNode> childNode; 1.2761 + result = childNodes->Item(i, getter_AddRefs(childNode)); 1.2762 + if ((NS_SUCCEEDED(result)) && (childNode)) 1.2763 + { 1.2764 + result = aExistingRightNode->RemoveChild(childNode, getter_AddRefs(resultNode)); 1.2765 + //printf(" after remove\n"); content->List(); // DEBUG 1.2766 + if (NS_SUCCEEDED(result)) 1.2767 + { 1.2768 + nsCOMPtr<nsIDOMNode> firstChild; 1.2769 + aNewLeftNode->GetFirstChild(getter_AddRefs(firstChild)); 1.2770 + result = aNewLeftNode->InsertBefore(childNode, firstChild, getter_AddRefs(resultNode)); 1.2771 + //printf(" after append\n"); content->List(); // DEBUG 1.2772 + } 1.2773 + } 1.2774 + } 1.2775 + } 1.2776 + } 1.2777 + // handle selection 1.2778 + nsCOMPtr<nsIPresShell> ps = GetPresShell(); 1.2779 + if (ps) 1.2780 + ps->FlushPendingNotifications(Flush_Frames); 1.2781 + 1.2782 + if (GetShouldTxnSetSelection()) 1.2783 + { 1.2784 + // editor wants us to set selection at split point 1.2785 + selection->Collapse(aNewLeftNode, aOffset); 1.2786 + } 1.2787 + else if (selStartNode) 1.2788 + { 1.2789 + // else adjust the selection if needed. if selStartNode is null, then there was no selection. 1.2790 + // HACK: this is overly simplified - multi-range selections need more work than this 1.2791 + if (selStartNode.get() == aExistingRightNode) 1.2792 + { 1.2793 + if (selStartOffset < aOffset) 1.2794 + { 1.2795 + selStartNode = aNewLeftNode; 1.2796 + } 1.2797 + else 1.2798 + { 1.2799 + selStartOffset -= aOffset; 1.2800 + } 1.2801 + } 1.2802 + if (selEndNode.get() == aExistingRightNode) 1.2803 + { 1.2804 + if (selEndOffset < aOffset) 1.2805 + { 1.2806 + selEndNode = aNewLeftNode; 1.2807 + } 1.2808 + else 1.2809 + { 1.2810 + selEndOffset -= aOffset; 1.2811 + } 1.2812 + } 1.2813 + selection->Collapse(selStartNode,selStartOffset); 1.2814 + selection->Extend(selEndNode,selEndOffset); 1.2815 + } 1.2816 + } 1.2817 + } 1.2818 + } 1.2819 + else 1.2820 + result = NS_ERROR_INVALID_ARG; 1.2821 + 1.2822 + return result; 1.2823 +} 1.2824 + 1.2825 +nsresult 1.2826 +nsEditor::JoinNodesImpl(nsINode* aNodeToKeep, 1.2827 + nsINode* aNodeToJoin, 1.2828 + nsINode* aParent) 1.2829 +{ 1.2830 + MOZ_ASSERT(aNodeToKeep); 1.2831 + MOZ_ASSERT(aNodeToJoin); 1.2832 + MOZ_ASSERT(aParent); 1.2833 + 1.2834 + nsRefPtr<Selection> selection = GetSelection(); 1.2835 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.2836 + 1.2837 + // remember some selection points 1.2838 + nsCOMPtr<nsINode> selStartNode; 1.2839 + int32_t selStartOffset; 1.2840 + nsresult result = GetStartNodeAndOffset(selection, getter_AddRefs(selStartNode), &selStartOffset); 1.2841 + if (NS_FAILED(result)) { 1.2842 + selStartNode = nullptr; 1.2843 + } 1.2844 + 1.2845 + nsCOMPtr<nsINode> selEndNode; 1.2846 + int32_t selEndOffset; 1.2847 + result = GetEndNodeAndOffset(selection, getter_AddRefs(selEndNode), &selEndOffset); 1.2848 + // Joe or Kin should comment here on why the following line is not a copy/paste error 1.2849 + if (NS_FAILED(result)) { 1.2850 + selStartNode = nullptr; 1.2851 + } 1.2852 + 1.2853 + uint32_t firstNodeLength = aNodeToJoin->Length(); 1.2854 + 1.2855 + int32_t joinOffset; 1.2856 + GetNodeLocation(aNodeToJoin, &joinOffset); 1.2857 + int32_t keepOffset; 1.2858 + nsINode* parent = GetNodeLocation(aNodeToKeep, &keepOffset); 1.2859 + 1.2860 + // if selection endpoint is between the nodes, remember it as being 1.2861 + // in the one that is going away instead. This simplifies later selection 1.2862 + // adjustment logic at end of this method. 1.2863 + if (selStartNode) { 1.2864 + if (selStartNode == parent && 1.2865 + joinOffset < selStartOffset && selStartOffset <= keepOffset) { 1.2866 + selStartNode = aNodeToJoin; 1.2867 + selStartOffset = firstNodeLength; 1.2868 + } 1.2869 + if (selEndNode == parent && 1.2870 + joinOffset < selEndOffset && selEndOffset <= keepOffset) { 1.2871 + selEndNode = aNodeToJoin; 1.2872 + selEndOffset = firstNodeLength; 1.2873 + } 1.2874 + } 1.2875 + 1.2876 + // ok, ready to do join now. 1.2877 + // if it's a text node, just shuffle around some text 1.2878 + nsCOMPtr<nsIDOMCharacterData> keepNodeAsText( do_QueryInterface(aNodeToKeep) ); 1.2879 + nsCOMPtr<nsIDOMCharacterData> joinNodeAsText( do_QueryInterface(aNodeToJoin) ); 1.2880 + if (keepNodeAsText && joinNodeAsText) { 1.2881 + nsAutoString rightText; 1.2882 + nsAutoString leftText; 1.2883 + keepNodeAsText->GetData(rightText); 1.2884 + joinNodeAsText->GetData(leftText); 1.2885 + leftText += rightText; 1.2886 + keepNodeAsText->SetData(leftText); 1.2887 + } else { 1.2888 + // otherwise it's an interior node, so shuffle around the children 1.2889 + nsCOMPtr<nsINodeList> childNodes = aNodeToJoin->ChildNodes(); 1.2890 + MOZ_ASSERT(childNodes); 1.2891 + 1.2892 + // remember the first child in aNodeToKeep, we'll insert all the children of aNodeToJoin in front of it 1.2893 + // GetFirstChild returns nullptr firstNode if aNodeToKeep has no children, that's ok. 1.2894 + nsCOMPtr<nsIContent> firstNode = aNodeToKeep->GetFirstChild(); 1.2895 + 1.2896 + // have to go through the list backwards to keep deletes from interfering with iteration 1.2897 + for (uint32_t i = childNodes->Length(); i > 0; --i) { 1.2898 + nsCOMPtr<nsIContent> childNode = childNodes->Item(i - 1); 1.2899 + if (childNode) { 1.2900 + // prepend children of aNodeToJoin 1.2901 + ErrorResult err; 1.2902 + aNodeToKeep->InsertBefore(*childNode, firstNode, err); 1.2903 + NS_ENSURE_SUCCESS(err.ErrorCode(), err.ErrorCode()); 1.2904 + firstNode = childNode.forget(); 1.2905 + } 1.2906 + } 1.2907 + } 1.2908 + 1.2909 + // delete the extra node 1.2910 + ErrorResult err; 1.2911 + aParent->RemoveChild(*aNodeToJoin, err); 1.2912 + 1.2913 + if (GetShouldTxnSetSelection()) { 1.2914 + // editor wants us to set selection at join point 1.2915 + selection->Collapse(aNodeToKeep, SafeCast<int32_t>(firstNodeLength)); 1.2916 + } else if (selStartNode) { 1.2917 + // and adjust the selection if needed 1.2918 + // HACK: this is overly simplified - multi-range selections need more work than this 1.2919 + bool bNeedToAdjust = false; 1.2920 + 1.2921 + // check to see if we joined nodes where selection starts 1.2922 + if (selStartNode == aNodeToJoin) { 1.2923 + bNeedToAdjust = true; 1.2924 + selStartNode = aNodeToKeep; 1.2925 + } else if (selStartNode == aNodeToKeep) { 1.2926 + bNeedToAdjust = true; 1.2927 + selStartOffset += firstNodeLength; 1.2928 + } 1.2929 + 1.2930 + // check to see if we joined nodes where selection ends 1.2931 + if (selEndNode == aNodeToJoin) { 1.2932 + bNeedToAdjust = true; 1.2933 + selEndNode = aNodeToKeep; 1.2934 + } else if (selEndNode == aNodeToKeep) { 1.2935 + bNeedToAdjust = true; 1.2936 + selEndOffset += firstNodeLength; 1.2937 + } 1.2938 + 1.2939 + // adjust selection if needed 1.2940 + if (bNeedToAdjust) { 1.2941 + selection->Collapse(selStartNode, selStartOffset); 1.2942 + selection->Extend(selEndNode, selEndOffset); 1.2943 + } 1.2944 + } 1.2945 + 1.2946 + return err.ErrorCode(); 1.2947 +} 1.2948 + 1.2949 + 1.2950 +int32_t 1.2951 +nsEditor::GetChildOffset(nsIDOMNode* aChild, nsIDOMNode* aParent) 1.2952 +{ 1.2953 + MOZ_ASSERT(aChild && aParent); 1.2954 + 1.2955 + nsCOMPtr<nsINode> parent = do_QueryInterface(aParent); 1.2956 + nsCOMPtr<nsINode> child = do_QueryInterface(aChild); 1.2957 + MOZ_ASSERT(parent && child); 1.2958 + 1.2959 + int32_t idx = parent->IndexOf(child); 1.2960 + MOZ_ASSERT(idx != -1); 1.2961 + return idx; 1.2962 +} 1.2963 + 1.2964 +// static 1.2965 +already_AddRefed<nsIDOMNode> 1.2966 +nsEditor::GetNodeLocation(nsIDOMNode* aChild, int32_t* outOffset) 1.2967 +{ 1.2968 + MOZ_ASSERT(aChild && outOffset); 1.2969 + NS_ENSURE_TRUE(aChild && outOffset, nullptr); 1.2970 + *outOffset = -1; 1.2971 + 1.2972 + nsCOMPtr<nsIDOMNode> parent; 1.2973 + 1.2974 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( 1.2975 + aChild->GetParentNode(getter_AddRefs(parent)))); 1.2976 + if (parent) { 1.2977 + *outOffset = GetChildOffset(aChild, parent); 1.2978 + } 1.2979 + 1.2980 + return parent.forget(); 1.2981 +} 1.2982 + 1.2983 +nsINode* 1.2984 +nsEditor::GetNodeLocation(nsINode* aChild, int32_t* aOffset) 1.2985 +{ 1.2986 + MOZ_ASSERT(aChild); 1.2987 + MOZ_ASSERT(aOffset); 1.2988 + 1.2989 + nsINode* parent = aChild->GetParentNode(); 1.2990 + if (parent) { 1.2991 + *aOffset = parent->IndexOf(aChild); 1.2992 + MOZ_ASSERT(*aOffset != -1); 1.2993 + } else { 1.2994 + *aOffset = -1; 1.2995 + } 1.2996 + return parent; 1.2997 +} 1.2998 + 1.2999 +// returns the number of things inside aNode. 1.3000 +// If aNode is text, returns number of characters. If not, returns number of children nodes. 1.3001 +nsresult 1.3002 +nsEditor::GetLengthOfDOMNode(nsIDOMNode *aNode, uint32_t &aCount) 1.3003 +{ 1.3004 + aCount = 0; 1.3005 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.3006 + NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER); 1.3007 + aCount = node->Length(); 1.3008 + return NS_OK; 1.3009 +} 1.3010 + 1.3011 + 1.3012 +nsresult 1.3013 +nsEditor::GetPriorNode(nsIDOMNode *aParentNode, 1.3014 + int32_t aOffset, 1.3015 + bool aEditableNode, 1.3016 + nsCOMPtr<nsIDOMNode> *aResultNode, 1.3017 + bool bNoBlockCrossing) 1.3018 +{ 1.3019 + NS_ENSURE_TRUE(aResultNode, NS_ERROR_NULL_POINTER); 1.3020 + *aResultNode = nullptr; 1.3021 + 1.3022 + nsCOMPtr<nsINode> parentNode = do_QueryInterface(aParentNode); 1.3023 + NS_ENSURE_TRUE(parentNode, NS_ERROR_NULL_POINTER); 1.3024 + 1.3025 + *aResultNode = do_QueryInterface(GetPriorNode(parentNode, aOffset, 1.3026 + aEditableNode, 1.3027 + bNoBlockCrossing)); 1.3028 + return NS_OK; 1.3029 +} 1.3030 + 1.3031 +nsIContent* 1.3032 +nsEditor::GetPriorNode(nsINode* aParentNode, 1.3033 + int32_t aOffset, 1.3034 + bool aEditableNode, 1.3035 + bool aNoBlockCrossing) 1.3036 +{ 1.3037 + MOZ_ASSERT(aParentNode); 1.3038 + 1.3039 + // If we are at the beginning of the node, or it is a text node, then just 1.3040 + // look before it. 1.3041 + if (!aOffset || aParentNode->NodeType() == nsIDOMNode::TEXT_NODE) { 1.3042 + if (aNoBlockCrossing && IsBlockNode(aParentNode)) { 1.3043 + // If we aren't allowed to cross blocks, don't look before this block. 1.3044 + return nullptr; 1.3045 + } 1.3046 + return GetPriorNode(aParentNode, aEditableNode, aNoBlockCrossing); 1.3047 + } 1.3048 + 1.3049 + // else look before the child at 'aOffset' 1.3050 + if (nsIContent* child = aParentNode->GetChildAt(aOffset)) { 1.3051 + return GetPriorNode(child, aEditableNode, aNoBlockCrossing); 1.3052 + } 1.3053 + 1.3054 + // unless there isn't one, in which case we are at the end of the node 1.3055 + // and want the deep-right child. 1.3056 + nsIContent* resultNode = GetRightmostChild(aParentNode, aNoBlockCrossing); 1.3057 + if (!resultNode || !aEditableNode || IsEditable(resultNode)) { 1.3058 + return resultNode; 1.3059 + } 1.3060 + 1.3061 + // restart the search from the non-editable node we just found 1.3062 + return GetPriorNode(resultNode, aEditableNode, aNoBlockCrossing); 1.3063 +} 1.3064 + 1.3065 + 1.3066 +nsresult 1.3067 +nsEditor::GetNextNode(nsIDOMNode *aParentNode, 1.3068 + int32_t aOffset, 1.3069 + bool aEditableNode, 1.3070 + nsCOMPtr<nsIDOMNode> *aResultNode, 1.3071 + bool bNoBlockCrossing) 1.3072 +{ 1.3073 + NS_ENSURE_TRUE(aResultNode, NS_ERROR_NULL_POINTER); 1.3074 + *aResultNode = nullptr; 1.3075 + 1.3076 + nsCOMPtr<nsINode> parentNode = do_QueryInterface(aParentNode); 1.3077 + NS_ENSURE_TRUE(parentNode, NS_ERROR_NULL_POINTER); 1.3078 + 1.3079 + *aResultNode = do_QueryInterface(GetNextNode(parentNode, aOffset, 1.3080 + aEditableNode, 1.3081 + bNoBlockCrossing)); 1.3082 + return NS_OK; 1.3083 +} 1.3084 + 1.3085 +nsIContent* 1.3086 +nsEditor::GetNextNode(nsINode* aParentNode, 1.3087 + int32_t aOffset, 1.3088 + bool aEditableNode, 1.3089 + bool aNoBlockCrossing) 1.3090 +{ 1.3091 + MOZ_ASSERT(aParentNode); 1.3092 + 1.3093 + // if aParentNode is a text node, use its location instead 1.3094 + if (aParentNode->NodeType() == nsIDOMNode::TEXT_NODE) { 1.3095 + nsINode* parent = aParentNode->GetParentNode(); 1.3096 + NS_ENSURE_TRUE(parent, nullptr); 1.3097 + aOffset = parent->IndexOf(aParentNode) + 1; // _after_ the text node 1.3098 + aParentNode = parent; 1.3099 + } 1.3100 + 1.3101 + // look at the child at 'aOffset' 1.3102 + nsIContent* child = aParentNode->GetChildAt(aOffset); 1.3103 + if (child) { 1.3104 + if (aNoBlockCrossing && IsBlockNode(child)) { 1.3105 + return child; 1.3106 + } 1.3107 + 1.3108 + nsIContent* resultNode = GetLeftmostChild(child, aNoBlockCrossing); 1.3109 + if (!resultNode) { 1.3110 + return child; 1.3111 + } 1.3112 + 1.3113 + if (!IsDescendantOfEditorRoot(resultNode)) { 1.3114 + return nullptr; 1.3115 + } 1.3116 + 1.3117 + if (!aEditableNode || IsEditable(resultNode)) { 1.3118 + return resultNode; 1.3119 + } 1.3120 + 1.3121 + // restart the search from the non-editable node we just found 1.3122 + return GetNextNode(resultNode, aEditableNode, aNoBlockCrossing); 1.3123 + } 1.3124 + 1.3125 + // unless there isn't one, in which case we are at the end of the node 1.3126 + // and want the next one. 1.3127 + if (aNoBlockCrossing && IsBlockNode(aParentNode)) { 1.3128 + // don't cross out of parent block 1.3129 + return nullptr; 1.3130 + } 1.3131 + 1.3132 + return GetNextNode(aParentNode, aEditableNode, aNoBlockCrossing); 1.3133 +} 1.3134 + 1.3135 + 1.3136 +nsresult 1.3137 +nsEditor::GetPriorNode(nsIDOMNode *aCurrentNode, 1.3138 + bool aEditableNode, 1.3139 + nsCOMPtr<nsIDOMNode> *aResultNode, 1.3140 + bool bNoBlockCrossing) 1.3141 +{ 1.3142 + NS_ENSURE_TRUE(aResultNode, NS_ERROR_NULL_POINTER); 1.3143 + 1.3144 + nsCOMPtr<nsINode> currentNode = do_QueryInterface(aCurrentNode); 1.3145 + NS_ENSURE_TRUE(currentNode, NS_ERROR_NULL_POINTER); 1.3146 + 1.3147 + *aResultNode = do_QueryInterface(GetPriorNode(currentNode, aEditableNode, 1.3148 + bNoBlockCrossing)); 1.3149 + return NS_OK; 1.3150 +} 1.3151 + 1.3152 +nsIContent* 1.3153 +nsEditor::GetPriorNode(nsINode* aCurrentNode, bool aEditableNode, 1.3154 + bool aNoBlockCrossing /* = false */) 1.3155 +{ 1.3156 + MOZ_ASSERT(aCurrentNode); 1.3157 + 1.3158 + if (!IsDescendantOfEditorRoot(aCurrentNode)) { 1.3159 + return nullptr; 1.3160 + } 1.3161 + 1.3162 + return FindNode(aCurrentNode, false, aEditableNode, aNoBlockCrossing); 1.3163 +} 1.3164 + 1.3165 +nsIContent* 1.3166 +nsEditor::FindNextLeafNode(nsINode *aCurrentNode, 1.3167 + bool aGoForward, 1.3168 + bool bNoBlockCrossing) 1.3169 +{ 1.3170 + // called only by GetPriorNode so we don't need to check params. 1.3171 + NS_PRECONDITION(IsDescendantOfEditorRoot(aCurrentNode) && 1.3172 + !IsEditorRoot(aCurrentNode), 1.3173 + "Bogus arguments"); 1.3174 + 1.3175 + nsINode* cur = aCurrentNode; 1.3176 + for (;;) { 1.3177 + // if aCurrentNode has a sibling in the right direction, return 1.3178 + // that sibling's closest child (or itself if it has no children) 1.3179 + nsIContent* sibling = 1.3180 + aGoForward ? cur->GetNextSibling() : cur->GetPreviousSibling(); 1.3181 + if (sibling) { 1.3182 + if (bNoBlockCrossing && IsBlockNode(sibling)) { 1.3183 + // don't look inside prevsib, since it is a block 1.3184 + return sibling; 1.3185 + } 1.3186 + nsIContent *leaf = 1.3187 + aGoForward ? GetLeftmostChild(sibling, bNoBlockCrossing) : 1.3188 + GetRightmostChild(sibling, bNoBlockCrossing); 1.3189 + if (!leaf) { 1.3190 + return sibling; 1.3191 + } 1.3192 + 1.3193 + return leaf; 1.3194 + } 1.3195 + 1.3196 + nsINode *parent = cur->GetParentNode(); 1.3197 + if (!parent) { 1.3198 + return nullptr; 1.3199 + } 1.3200 + 1.3201 + NS_ASSERTION(IsDescendantOfEditorRoot(parent), 1.3202 + "We started with a proper descendant of root, and should stop " 1.3203 + "if we ever hit the root, so we better have a descendant of " 1.3204 + "root now!"); 1.3205 + if (IsEditorRoot(parent) || 1.3206 + (bNoBlockCrossing && IsBlockNode(parent))) { 1.3207 + return nullptr; 1.3208 + } 1.3209 + 1.3210 + cur = parent; 1.3211 + } 1.3212 + 1.3213 + NS_NOTREACHED("What part of for(;;) do you not understand?"); 1.3214 + return nullptr; 1.3215 +} 1.3216 + 1.3217 +nsresult 1.3218 +nsEditor::GetNextNode(nsIDOMNode* aCurrentNode, 1.3219 + bool aEditableNode, 1.3220 + nsCOMPtr<nsIDOMNode> *aResultNode, 1.3221 + bool bNoBlockCrossing) 1.3222 +{ 1.3223 + nsCOMPtr<nsINode> currentNode = do_QueryInterface(aCurrentNode); 1.3224 + if (!currentNode || !aResultNode) { 1.3225 + return NS_ERROR_NULL_POINTER; 1.3226 + } 1.3227 + 1.3228 + *aResultNode = do_QueryInterface(GetNextNode(currentNode, aEditableNode, 1.3229 + bNoBlockCrossing)); 1.3230 + return NS_OK; 1.3231 +} 1.3232 + 1.3233 +nsIContent* 1.3234 +nsEditor::GetNextNode(nsINode* aCurrentNode, 1.3235 + bool aEditableNode, 1.3236 + bool bNoBlockCrossing) 1.3237 +{ 1.3238 + MOZ_ASSERT(aCurrentNode); 1.3239 + 1.3240 + if (!IsDescendantOfEditorRoot(aCurrentNode)) { 1.3241 + return nullptr; 1.3242 + } 1.3243 + 1.3244 + return FindNode(aCurrentNode, true, aEditableNode, bNoBlockCrossing); 1.3245 +} 1.3246 + 1.3247 +nsIContent* 1.3248 +nsEditor::FindNode(nsINode *aCurrentNode, 1.3249 + bool aGoForward, 1.3250 + bool aEditableNode, 1.3251 + bool bNoBlockCrossing) 1.3252 +{ 1.3253 + if (IsEditorRoot(aCurrentNode)) { 1.3254 + // Don't allow traversal above the root node! This helps 1.3255 + // prevent us from accidentally editing browser content 1.3256 + // when the editor is in a text widget. 1.3257 + 1.3258 + return nullptr; 1.3259 + } 1.3260 + 1.3261 + nsCOMPtr<nsIContent> candidate = 1.3262 + FindNextLeafNode(aCurrentNode, aGoForward, bNoBlockCrossing); 1.3263 + 1.3264 + if (!candidate) { 1.3265 + return nullptr; 1.3266 + } 1.3267 + 1.3268 + if (!aEditableNode || IsEditable(candidate)) { 1.3269 + return candidate; 1.3270 + } 1.3271 + 1.3272 + return FindNode(candidate, aGoForward, aEditableNode, bNoBlockCrossing); 1.3273 +} 1.3274 + 1.3275 +nsIDOMNode* 1.3276 +nsEditor::GetRightmostChild(nsIDOMNode* aCurrentNode, 1.3277 + bool bNoBlockCrossing) 1.3278 +{ 1.3279 + nsCOMPtr<nsINode> currentNode = do_QueryInterface(aCurrentNode); 1.3280 + nsIContent* result = GetRightmostChild(currentNode, bNoBlockCrossing); 1.3281 + return result ? result->AsDOMNode() : nullptr; 1.3282 +} 1.3283 + 1.3284 +nsIContent* 1.3285 +nsEditor::GetRightmostChild(nsINode *aCurrentNode, 1.3286 + bool bNoBlockCrossing) 1.3287 +{ 1.3288 + NS_ENSURE_TRUE(aCurrentNode, nullptr); 1.3289 + nsIContent *cur = aCurrentNode->GetLastChild(); 1.3290 + if (!cur) { 1.3291 + return nullptr; 1.3292 + } 1.3293 + for (;;) { 1.3294 + if (bNoBlockCrossing && IsBlockNode(cur)) { 1.3295 + return cur; 1.3296 + } 1.3297 + nsIContent* next = cur->GetLastChild(); 1.3298 + if (!next) { 1.3299 + return cur; 1.3300 + } 1.3301 + cur = next; 1.3302 + } 1.3303 + 1.3304 + NS_NOTREACHED("What part of for(;;) do you not understand?"); 1.3305 + return nullptr; 1.3306 +} 1.3307 + 1.3308 +nsIContent* 1.3309 +nsEditor::GetLeftmostChild(nsINode *aCurrentNode, 1.3310 + bool bNoBlockCrossing) 1.3311 +{ 1.3312 + NS_ENSURE_TRUE(aCurrentNode, nullptr); 1.3313 + nsIContent *cur = aCurrentNode->GetFirstChild(); 1.3314 + if (!cur) { 1.3315 + return nullptr; 1.3316 + } 1.3317 + for (;;) { 1.3318 + if (bNoBlockCrossing && IsBlockNode(cur)) { 1.3319 + return cur; 1.3320 + } 1.3321 + nsIContent *next = cur->GetFirstChild(); 1.3322 + if (!next) { 1.3323 + return cur; 1.3324 + } 1.3325 + cur = next; 1.3326 + } 1.3327 + 1.3328 + NS_NOTREACHED("What part of for(;;) do you not understand?"); 1.3329 + return nullptr; 1.3330 +} 1.3331 + 1.3332 +nsIDOMNode* 1.3333 +nsEditor::GetLeftmostChild(nsIDOMNode* aCurrentNode, 1.3334 + bool bNoBlockCrossing) 1.3335 +{ 1.3336 + nsCOMPtr<nsINode> currentNode = do_QueryInterface(aCurrentNode); 1.3337 + nsIContent* result = GetLeftmostChild(currentNode, bNoBlockCrossing); 1.3338 + return result ? result->AsDOMNode() : nullptr; 1.3339 +} 1.3340 + 1.3341 +bool 1.3342 +nsEditor::IsBlockNode(nsIDOMNode* aNode) 1.3343 +{ 1.3344 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.3345 + return IsBlockNode(node); 1.3346 +} 1.3347 + 1.3348 +bool 1.3349 +nsEditor::IsBlockNode(nsINode* aNode) 1.3350 +{ 1.3351 + // stub to be overridden in nsHTMLEditor. 1.3352 + // screwing around with the class hierarchy here in order 1.3353 + // to not duplicate the code in GetNextNode/GetPrevNode 1.3354 + // across both nsEditor/nsHTMLEditor. 1.3355 + return false; 1.3356 +} 1.3357 + 1.3358 +bool 1.3359 +nsEditor::CanContain(nsIDOMNode* aParent, nsIDOMNode* aChild) 1.3360 +{ 1.3361 + nsCOMPtr<nsIContent> parent = do_QueryInterface(aParent); 1.3362 + NS_ENSURE_TRUE(parent, false); 1.3363 + 1.3364 + switch (parent->NodeType()) { 1.3365 + case nsIDOMNode::ELEMENT_NODE: 1.3366 + case nsIDOMNode::DOCUMENT_FRAGMENT_NODE: 1.3367 + return TagCanContain(parent->Tag(), aChild); 1.3368 + } 1.3369 + return false; 1.3370 +} 1.3371 + 1.3372 +bool 1.3373 +nsEditor::CanContainTag(nsIDOMNode* aParent, nsIAtom* aChildTag) 1.3374 +{ 1.3375 + nsCOMPtr<nsIContent> parent = do_QueryInterface(aParent); 1.3376 + NS_ENSURE_TRUE(parent, false); 1.3377 + 1.3378 + switch (parent->NodeType()) { 1.3379 + case nsIDOMNode::ELEMENT_NODE: 1.3380 + case nsIDOMNode::DOCUMENT_FRAGMENT_NODE: 1.3381 + return TagCanContainTag(parent->Tag(), aChildTag); 1.3382 + } 1.3383 + return false; 1.3384 +} 1.3385 + 1.3386 +bool 1.3387 +nsEditor::TagCanContain(nsIAtom* aParentTag, nsIDOMNode* aChild) 1.3388 +{ 1.3389 + nsCOMPtr<nsIContent> child = do_QueryInterface(aChild); 1.3390 + NS_ENSURE_TRUE(child, false); 1.3391 + 1.3392 + switch (child->NodeType()) { 1.3393 + case nsIDOMNode::TEXT_NODE: 1.3394 + case nsIDOMNode::ELEMENT_NODE: 1.3395 + case nsIDOMNode::DOCUMENT_FRAGMENT_NODE: 1.3396 + return TagCanContainTag(aParentTag, child->Tag()); 1.3397 + } 1.3398 + return false; 1.3399 +} 1.3400 + 1.3401 +bool 1.3402 +nsEditor::TagCanContainTag(nsIAtom* aParentTag, nsIAtom* aChildTag) 1.3403 +{ 1.3404 + return true; 1.3405 +} 1.3406 + 1.3407 +bool 1.3408 +nsEditor::IsRoot(nsIDOMNode* inNode) 1.3409 +{ 1.3410 + NS_ENSURE_TRUE(inNode, false); 1.3411 + 1.3412 + nsCOMPtr<nsIDOMNode> rootNode = do_QueryInterface(GetRoot()); 1.3413 + 1.3414 + return inNode == rootNode; 1.3415 +} 1.3416 + 1.3417 +bool 1.3418 +nsEditor::IsRoot(nsINode* inNode) 1.3419 +{ 1.3420 + NS_ENSURE_TRUE(inNode, false); 1.3421 + 1.3422 + nsCOMPtr<nsINode> rootNode = GetRoot(); 1.3423 + 1.3424 + return inNode == rootNode; 1.3425 +} 1.3426 + 1.3427 +bool 1.3428 +nsEditor::IsEditorRoot(nsINode* aNode) 1.3429 +{ 1.3430 + NS_ENSURE_TRUE(aNode, false); 1.3431 + nsCOMPtr<nsINode> rootNode = GetEditorRoot(); 1.3432 + return aNode == rootNode; 1.3433 +} 1.3434 + 1.3435 +bool 1.3436 +nsEditor::IsDescendantOfRoot(nsIDOMNode* inNode) 1.3437 +{ 1.3438 + nsCOMPtr<nsINode> node = do_QueryInterface(inNode); 1.3439 + return IsDescendantOfRoot(node); 1.3440 +} 1.3441 + 1.3442 +bool 1.3443 +nsEditor::IsDescendantOfRoot(nsINode* inNode) 1.3444 +{ 1.3445 + NS_ENSURE_TRUE(inNode, false); 1.3446 + nsCOMPtr<nsIContent> root = GetRoot(); 1.3447 + NS_ENSURE_TRUE(root, false); 1.3448 + 1.3449 + return nsContentUtils::ContentIsDescendantOf(inNode, root); 1.3450 +} 1.3451 + 1.3452 +bool 1.3453 +nsEditor::IsDescendantOfEditorRoot(nsIDOMNode* aNode) 1.3454 +{ 1.3455 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.3456 + return IsDescendantOfEditorRoot(node); 1.3457 +} 1.3458 + 1.3459 +bool 1.3460 +nsEditor::IsDescendantOfEditorRoot(nsINode* aNode) 1.3461 +{ 1.3462 + NS_ENSURE_TRUE(aNode, false); 1.3463 + nsCOMPtr<nsIContent> root = GetEditorRoot(); 1.3464 + NS_ENSURE_TRUE(root, false); 1.3465 + 1.3466 + return nsContentUtils::ContentIsDescendantOf(aNode, root); 1.3467 +} 1.3468 + 1.3469 +bool 1.3470 +nsEditor::IsContainer(nsIDOMNode *aNode) 1.3471 +{ 1.3472 + return aNode ? true : false; 1.3473 +} 1.3474 + 1.3475 +static inline bool 1.3476 +IsElementVisible(dom::Element* aElement) 1.3477 +{ 1.3478 + if (aElement->GetPrimaryFrame()) { 1.3479 + // It's visible, for our purposes 1.3480 + return true; 1.3481 + } 1.3482 + 1.3483 + nsIContent *cur = aElement; 1.3484 + for (; ;) { 1.3485 + // Walk up the tree looking for the nearest ancestor with a frame. 1.3486 + // The state of the child right below it will determine whether 1.3487 + // we might possibly have a frame or not. 1.3488 + bool haveLazyBitOnChild = cur->HasFlag(NODE_NEEDS_FRAME); 1.3489 + cur = cur->GetFlattenedTreeParent(); 1.3490 + if (!cur) { 1.3491 + if (!haveLazyBitOnChild) { 1.3492 + // None of our ancestors have lazy bits set, so we shouldn't 1.3493 + // have a frame 1.3494 + return false; 1.3495 + } 1.3496 + 1.3497 + // The root has a lazy frame construction bit. We need to check 1.3498 + // our style. 1.3499 + break; 1.3500 + } 1.3501 + 1.3502 + if (cur->GetPrimaryFrame()) { 1.3503 + if (!haveLazyBitOnChild) { 1.3504 + // Our ancestor directly under |cur| doesn't have lazy bits; 1.3505 + // that means we won't get a frame 1.3506 + return false; 1.3507 + } 1.3508 + 1.3509 + if (cur->GetPrimaryFrame()->IsLeaf()) { 1.3510 + // Nothing under here will ever get frames 1.3511 + return false; 1.3512 + } 1.3513 + 1.3514 + // Otherwise, we might end up with a frame when that lazy bit is 1.3515 + // processed. Figure out our actual style. 1.3516 + break; 1.3517 + } 1.3518 + } 1.3519 + 1.3520 + // Now it might be that we have no frame because we're in a 1.3521 + // display:none subtree, or it might be that we're just dealing with 1.3522 + // lazy frame construction and it hasn't happened yet. Check which 1.3523 + // one it is. 1.3524 + nsRefPtr<nsStyleContext> styleContext = 1.3525 + nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement, 1.3526 + nullptr, nullptr); 1.3527 + if (styleContext) { 1.3528 + return styleContext->StyleDisplay()->mDisplay != NS_STYLE_DISPLAY_NONE; 1.3529 + } 1.3530 + return false; 1.3531 +} 1.3532 + 1.3533 +bool 1.3534 +nsEditor::IsEditable(nsIDOMNode *aNode) 1.3535 +{ 1.3536 + nsCOMPtr<nsIContent> content = do_QueryInterface(aNode); 1.3537 + return IsEditable(content); 1.3538 +} 1.3539 + 1.3540 +bool 1.3541 +nsEditor::IsEditable(nsIContent *aNode) 1.3542 +{ 1.3543 + NS_ENSURE_TRUE(aNode, false); 1.3544 + 1.3545 + if (IsMozEditorBogusNode(aNode) || !IsModifiableNode(aNode)) return false; 1.3546 + 1.3547 + // see if it has a frame. If so, we'll edit it. 1.3548 + // special case for textnodes: frame must have width. 1.3549 + if (aNode->IsElement() && !IsElementVisible(aNode->AsElement())) { 1.3550 + // If the element has no frame, it's not editable. Note that we 1.3551 + // need to check IsElement() here, because some of our tests 1.3552 + // rely on frameless textnodes being visible. 1.3553 + return false; 1.3554 + } 1.3555 + switch (aNode->NodeType()) { 1.3556 + case nsIDOMNode::ELEMENT_NODE: 1.3557 + case nsIDOMNode::TEXT_NODE: 1.3558 + return true; // element or text node; not invisible 1.3559 + default: 1.3560 + return false; 1.3561 + } 1.3562 +} 1.3563 + 1.3564 +bool 1.3565 +nsEditor::IsMozEditorBogusNode(nsIContent *element) 1.3566 +{ 1.3567 + return element && 1.3568 + element->AttrValueIs(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom, 1.3569 + kMOZEditorBogusNodeValue, eCaseMatters); 1.3570 +} 1.3571 + 1.3572 +uint32_t 1.3573 +nsEditor::CountEditableChildren(nsINode* aNode) 1.3574 +{ 1.3575 + MOZ_ASSERT(aNode); 1.3576 + uint32_t count = 0; 1.3577 + for (nsIContent* child = aNode->GetFirstChild(); 1.3578 + child; 1.3579 + child = child->GetNextSibling()) { 1.3580 + if (IsEditable(child)) { 1.3581 + ++count; 1.3582 + } 1.3583 + } 1.3584 + return count; 1.3585 +} 1.3586 + 1.3587 +//END nsEditor static utility methods 1.3588 + 1.3589 + 1.3590 +NS_IMETHODIMP nsEditor::IncrementModificationCount(int32_t inNumMods) 1.3591 +{ 1.3592 + uint32_t oldModCount = mModCount; 1.3593 + 1.3594 + mModCount += inNumMods; 1.3595 + 1.3596 + if ((oldModCount == 0 && mModCount != 0) 1.3597 + || (oldModCount != 0 && mModCount == 0)) 1.3598 + NotifyDocumentListeners(eDocumentStateChanged); 1.3599 + return NS_OK; 1.3600 +} 1.3601 + 1.3602 + 1.3603 +NS_IMETHODIMP nsEditor::GetModificationCount(int32_t *outModCount) 1.3604 +{ 1.3605 + NS_ENSURE_ARG_POINTER(outModCount); 1.3606 + *outModCount = mModCount; 1.3607 + return NS_OK; 1.3608 +} 1.3609 + 1.3610 + 1.3611 +NS_IMETHODIMP nsEditor::ResetModificationCount() 1.3612 +{ 1.3613 + bool doNotify = (mModCount != 0); 1.3614 + 1.3615 + mModCount = 0; 1.3616 + 1.3617 + if (doNotify) 1.3618 + NotifyDocumentListeners(eDocumentStateChanged); 1.3619 + return NS_OK; 1.3620 +} 1.3621 + 1.3622 +//END nsEditor Private methods 1.3623 + 1.3624 + 1.3625 + 1.3626 +/////////////////////////////////////////////////////////////////////////// 1.3627 +// GetTag: digs out the atom for the tag of this node 1.3628 +// 1.3629 +nsIAtom * 1.3630 +nsEditor::GetTag(nsIDOMNode *aNode) 1.3631 +{ 1.3632 + nsCOMPtr<nsIContent> content = do_QueryInterface(aNode); 1.3633 + 1.3634 + if (!content) 1.3635 + { 1.3636 + NS_ASSERTION(aNode, "null node passed to nsEditor::Tag()"); 1.3637 + 1.3638 + return nullptr; 1.3639 + } 1.3640 + 1.3641 + return content->Tag(); 1.3642 +} 1.3643 + 1.3644 + 1.3645 +/////////////////////////////////////////////////////////////////////////// 1.3646 +// GetTagString: digs out string for the tag of this node 1.3647 +// 1.3648 +nsresult 1.3649 +nsEditor::GetTagString(nsIDOMNode *aNode, nsAString& outString) 1.3650 +{ 1.3651 + if (!aNode) 1.3652 + { 1.3653 + NS_NOTREACHED("null node passed to nsEditor::GetTag()"); 1.3654 + return NS_ERROR_NULL_POINTER; 1.3655 + } 1.3656 + 1.3657 + nsIAtom *atom = GetTag(aNode); 1.3658 + if (!atom) 1.3659 + { 1.3660 + return NS_ERROR_FAILURE; 1.3661 + } 1.3662 + 1.3663 + atom->ToString(outString); 1.3664 + return NS_OK; 1.3665 +} 1.3666 + 1.3667 + 1.3668 +/////////////////////////////////////////////////////////////////////////// 1.3669 +// NodesSameType: do these nodes have the same tag? 1.3670 +// 1.3671 +bool 1.3672 +nsEditor::NodesSameType(nsIDOMNode *aNode1, nsIDOMNode *aNode2) 1.3673 +{ 1.3674 + if (!aNode1 || !aNode2) { 1.3675 + NS_NOTREACHED("null node passed to nsEditor::NodesSameType()"); 1.3676 + return false; 1.3677 + } 1.3678 + 1.3679 + nsCOMPtr<nsIContent> content1 = do_QueryInterface(aNode1); 1.3680 + NS_ENSURE_TRUE(content1, false); 1.3681 + 1.3682 + nsCOMPtr<nsIContent> content2 = do_QueryInterface(aNode2); 1.3683 + NS_ENSURE_TRUE(content2, false); 1.3684 + 1.3685 + return AreNodesSameType(content1, content2); 1.3686 +} 1.3687 + 1.3688 +/* virtual */ 1.3689 +bool 1.3690 +nsEditor::AreNodesSameType(nsIContent* aNode1, nsIContent* aNode2) 1.3691 +{ 1.3692 + MOZ_ASSERT(aNode1); 1.3693 + MOZ_ASSERT(aNode2); 1.3694 + return aNode1->Tag() == aNode2->Tag(); 1.3695 +} 1.3696 + 1.3697 + 1.3698 +/////////////////////////////////////////////////////////////////////////// 1.3699 +// IsTextNode: true if node of dom type text 1.3700 +// 1.3701 +bool 1.3702 +nsEditor::IsTextNode(nsIDOMNode *aNode) 1.3703 +{ 1.3704 + if (!aNode) 1.3705 + { 1.3706 + NS_NOTREACHED("null node passed to IsTextNode()"); 1.3707 + return false; 1.3708 + } 1.3709 + 1.3710 + uint16_t nodeType; 1.3711 + aNode->GetNodeType(&nodeType); 1.3712 + return (nodeType == nsIDOMNode::TEXT_NODE); 1.3713 +} 1.3714 + 1.3715 +bool 1.3716 +nsEditor::IsTextNode(nsINode *aNode) 1.3717 +{ 1.3718 + return aNode->NodeType() == nsIDOMNode::TEXT_NODE; 1.3719 +} 1.3720 + 1.3721 +/////////////////////////////////////////////////////////////////////////// 1.3722 +// GetChildAt: returns the node at this position index in the parent 1.3723 +// 1.3724 +nsCOMPtr<nsIDOMNode> 1.3725 +nsEditor::GetChildAt(nsIDOMNode *aParent, int32_t aOffset) 1.3726 +{ 1.3727 + nsCOMPtr<nsIDOMNode> resultNode; 1.3728 + 1.3729 + nsCOMPtr<nsIContent> parent = do_QueryInterface(aParent); 1.3730 + 1.3731 + NS_ENSURE_TRUE(parent, resultNode); 1.3732 + 1.3733 + resultNode = do_QueryInterface(parent->GetChildAt(aOffset)); 1.3734 + 1.3735 + return resultNode; 1.3736 +} 1.3737 + 1.3738 +/////////////////////////////////////////////////////////////////////////// 1.3739 +// GetNodeAtRangeOffsetPoint: returns the node at this position in a range, 1.3740 +// assuming that aParentOrNode is the node itself if it's a text node, or 1.3741 +// the node's parent otherwise. 1.3742 +// 1.3743 +nsCOMPtr<nsIDOMNode> 1.3744 +nsEditor::GetNodeAtRangeOffsetPoint(nsIDOMNode* aParentOrNode, int32_t aOffset) 1.3745 +{ 1.3746 + if (IsTextNode(aParentOrNode)) { 1.3747 + return aParentOrNode; 1.3748 + } 1.3749 + return GetChildAt(aParentOrNode, aOffset); 1.3750 +} 1.3751 + 1.3752 + 1.3753 +/////////////////////////////////////////////////////////////////////////// 1.3754 +// GetStartNodeAndOffset: returns whatever the start parent & offset is of 1.3755 +// the first range in the selection. 1.3756 +nsresult 1.3757 +nsEditor::GetStartNodeAndOffset(nsISelection *aSelection, 1.3758 + nsIDOMNode **outStartNode, 1.3759 + int32_t *outStartOffset) 1.3760 +{ 1.3761 + NS_ENSURE_TRUE(outStartNode && outStartOffset && aSelection, NS_ERROR_NULL_POINTER); 1.3762 + 1.3763 + nsCOMPtr<nsINode> startNode; 1.3764 + nsresult rv = GetStartNodeAndOffset(static_cast<Selection*>(aSelection), 1.3765 + getter_AddRefs(startNode), 1.3766 + outStartOffset); 1.3767 + NS_ENSURE_SUCCESS(rv, rv); 1.3768 + 1.3769 + if (startNode) { 1.3770 + NS_ADDREF(*outStartNode = startNode->AsDOMNode()); 1.3771 + } else { 1.3772 + *outStartNode = nullptr; 1.3773 + } 1.3774 + return NS_OK; 1.3775 +} 1.3776 + 1.3777 +nsresult 1.3778 +nsEditor::GetStartNodeAndOffset(Selection* aSelection, nsINode** aStartNode, 1.3779 + int32_t* aStartOffset) 1.3780 +{ 1.3781 + MOZ_ASSERT(aSelection); 1.3782 + MOZ_ASSERT(aStartNode); 1.3783 + MOZ_ASSERT(aStartOffset); 1.3784 + 1.3785 + *aStartNode = nullptr; 1.3786 + *aStartOffset = 0; 1.3787 + 1.3788 + NS_ENSURE_TRUE(aSelection->GetRangeCount(), NS_ERROR_FAILURE); 1.3789 + 1.3790 + const nsRange* range = aSelection->GetRangeAt(0); 1.3791 + NS_ENSURE_TRUE(range, NS_ERROR_FAILURE); 1.3792 + 1.3793 + NS_ENSURE_TRUE(range->IsPositioned(), NS_ERROR_FAILURE); 1.3794 + 1.3795 + NS_IF_ADDREF(*aStartNode = range->GetStartParent()); 1.3796 + *aStartOffset = range->StartOffset(); 1.3797 + return NS_OK; 1.3798 +} 1.3799 + 1.3800 + 1.3801 +/////////////////////////////////////////////////////////////////////////// 1.3802 +// GetEndNodeAndOffset: returns whatever the end parent & offset is of 1.3803 +// the first range in the selection. 1.3804 +nsresult 1.3805 +nsEditor::GetEndNodeAndOffset(nsISelection *aSelection, 1.3806 + nsIDOMNode **outEndNode, 1.3807 + int32_t *outEndOffset) 1.3808 +{ 1.3809 + NS_ENSURE_TRUE(outEndNode && outEndOffset && aSelection, NS_ERROR_NULL_POINTER); 1.3810 + 1.3811 + nsCOMPtr<nsINode> endNode; 1.3812 + nsresult rv = GetEndNodeAndOffset(static_cast<Selection*>(aSelection), 1.3813 + getter_AddRefs(endNode), 1.3814 + outEndOffset); 1.3815 + NS_ENSURE_SUCCESS(rv, rv); 1.3816 + 1.3817 + if (endNode) { 1.3818 + NS_ADDREF(*outEndNode = endNode->AsDOMNode()); 1.3819 + } else { 1.3820 + *outEndNode = nullptr; 1.3821 + } 1.3822 + return NS_OK; 1.3823 +} 1.3824 + 1.3825 +nsresult 1.3826 +nsEditor::GetEndNodeAndOffset(Selection* aSelection, nsINode** aEndNode, 1.3827 + int32_t* aEndOffset) 1.3828 +{ 1.3829 + MOZ_ASSERT(aSelection); 1.3830 + MOZ_ASSERT(aEndNode); 1.3831 + MOZ_ASSERT(aEndOffset); 1.3832 + 1.3833 + *aEndNode = nullptr; 1.3834 + *aEndOffset = 0; 1.3835 + 1.3836 + NS_ENSURE_TRUE(aSelection->GetRangeCount(), NS_ERROR_FAILURE); 1.3837 + 1.3838 + const nsRange* range = aSelection->GetRangeAt(0); 1.3839 + NS_ENSURE_TRUE(range, NS_ERROR_FAILURE); 1.3840 + 1.3841 + NS_ENSURE_TRUE(range->IsPositioned(), NS_ERROR_FAILURE); 1.3842 + 1.3843 + NS_IF_ADDREF(*aEndNode = range->GetEndParent()); 1.3844 + *aEndOffset = range->EndOffset(); 1.3845 + return NS_OK; 1.3846 +} 1.3847 + 1.3848 + 1.3849 +/////////////////////////////////////////////////////////////////////////// 1.3850 +// IsPreformatted: checks the style info for the node for the preformatted 1.3851 +// text style. 1.3852 +nsresult 1.3853 +nsEditor::IsPreformatted(nsIDOMNode *aNode, bool *aResult) 1.3854 +{ 1.3855 + nsCOMPtr<nsIContent> content = do_QueryInterface(aNode); 1.3856 + 1.3857 + NS_ENSURE_TRUE(aResult && content, NS_ERROR_NULL_POINTER); 1.3858 + 1.3859 + nsCOMPtr<nsIPresShell> ps = GetPresShell(); 1.3860 + NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED); 1.3861 + 1.3862 + // Look at the node (and its parent if it's not an element), and grab its style context 1.3863 + nsRefPtr<nsStyleContext> elementStyle; 1.3864 + if (!content->IsElement()) { 1.3865 + content = content->GetParent(); 1.3866 + } 1.3867 + if (content && content->IsElement()) { 1.3868 + elementStyle = nsComputedDOMStyle::GetStyleContextForElementNoFlush(content->AsElement(), 1.3869 + nullptr, 1.3870 + ps); 1.3871 + } 1.3872 + 1.3873 + if (!elementStyle) 1.3874 + { 1.3875 + // Consider nodes without a style context to be NOT preformatted: 1.3876 + // For instance, this is true of JS tags inside the body (which show 1.3877 + // up as #text nodes but have no style context). 1.3878 + *aResult = false; 1.3879 + return NS_OK; 1.3880 + } 1.3881 + 1.3882 + const nsStyleText* styleText = elementStyle->StyleText(); 1.3883 + 1.3884 + *aResult = styleText->WhiteSpaceIsSignificant(); 1.3885 + return NS_OK; 1.3886 +} 1.3887 + 1.3888 + 1.3889 +/////////////////////////////////////////////////////////////////////////// 1.3890 +// SplitNodeDeep: this splits a node "deeply", splitting children as 1.3891 +// appropriate. The place to split is represented by 1.3892 +// a dom point at {splitPointParent, splitPointOffset}. 1.3893 +// That dom point must be inside aNode, which is the node to 1.3894 +// split. outOffset is set to the offset in the parent of aNode where 1.3895 +// the split terminates - where you would want to insert 1.3896 +// a new element, for instance, if that's why you were splitting 1.3897 +// the node. 1.3898 +// 1.3899 +nsresult 1.3900 +nsEditor::SplitNodeDeep(nsIDOMNode *aNode, 1.3901 + nsIDOMNode *aSplitPointParent, 1.3902 + int32_t aSplitPointOffset, 1.3903 + int32_t *outOffset, 1.3904 + bool aNoEmptyContainers, 1.3905 + nsCOMPtr<nsIDOMNode> *outLeftNode, 1.3906 + nsCOMPtr<nsIDOMNode> *outRightNode) 1.3907 +{ 1.3908 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.3909 + NS_ENSURE_TRUE(node && aSplitPointParent && outOffset, NS_ERROR_NULL_POINTER); 1.3910 + int32_t offset = aSplitPointOffset; 1.3911 + 1.3912 + if (outLeftNode) *outLeftNode = nullptr; 1.3913 + if (outRightNode) *outRightNode = nullptr; 1.3914 + 1.3915 + nsCOMPtr<nsINode> nodeToSplit = do_QueryInterface(aSplitPointParent); 1.3916 + while (nodeToSplit) { 1.3917 + // need to insert rules code call here to do things like 1.3918 + // not split a list if you are after the last <li> or before the first, etc. 1.3919 + // for now we just have some smarts about unneccessarily splitting 1.3920 + // textnodes, which should be universal enough to put straight in 1.3921 + // this nsEditor routine. 1.3922 + 1.3923 + nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(nodeToSplit); 1.3924 + uint32_t len = nodeToSplit->Length(); 1.3925 + bool bDoSplit = false; 1.3926 + 1.3927 + if (!(aNoEmptyContainers || nodeAsText) || (offset && (offset != (int32_t)len))) 1.3928 + { 1.3929 + bDoSplit = true; 1.3930 + nsCOMPtr<nsIDOMNode> tempNode; 1.3931 + nsresult rv = SplitNode(nodeToSplit->AsDOMNode(), offset, 1.3932 + getter_AddRefs(tempNode)); 1.3933 + NS_ENSURE_SUCCESS(rv, rv); 1.3934 + 1.3935 + if (outRightNode) { 1.3936 + *outRightNode = nodeToSplit->AsDOMNode(); 1.3937 + } 1.3938 + if (outLeftNode) { 1.3939 + *outLeftNode = tempNode; 1.3940 + } 1.3941 + } 1.3942 + 1.3943 + nsINode* parentNode = nodeToSplit->GetParentNode(); 1.3944 + NS_ENSURE_TRUE(parentNode, NS_ERROR_FAILURE); 1.3945 + 1.3946 + if (!bDoSplit && offset) { 1.3947 + // must be "end of text node" case, we didn't split it, just move past it 1.3948 + offset = parentNode->IndexOf(nodeToSplit) + 1; 1.3949 + if (outLeftNode) { 1.3950 + *outLeftNode = nodeToSplit->AsDOMNode(); 1.3951 + } 1.3952 + } else { 1.3953 + offset = parentNode->IndexOf(nodeToSplit); 1.3954 + if (outRightNode) { 1.3955 + *outRightNode = nodeToSplit->AsDOMNode(); 1.3956 + } 1.3957 + } 1.3958 + 1.3959 + if (nodeToSplit == node) { 1.3960 + // we split all the way up to (and including) aNode; we're done 1.3961 + break; 1.3962 + } 1.3963 + 1.3964 + nodeToSplit = parentNode; 1.3965 + } 1.3966 + 1.3967 + if (!nodeToSplit) { 1.3968 + NS_NOTREACHED("null node obtained in nsEditor::SplitNodeDeep()"); 1.3969 + return NS_ERROR_FAILURE; 1.3970 + } 1.3971 + 1.3972 + *outOffset = offset; 1.3973 + return NS_OK; 1.3974 +} 1.3975 + 1.3976 + 1.3977 +/////////////////////////////////////////////////////////////////////////// 1.3978 +// JoinNodeDeep: this joins two like nodes "deeply", joining children as 1.3979 +// appropriate. 1.3980 +nsresult 1.3981 +nsEditor::JoinNodeDeep(nsIDOMNode *aLeftNode, 1.3982 + nsIDOMNode *aRightNode, 1.3983 + nsCOMPtr<nsIDOMNode> *aOutJoinNode, 1.3984 + int32_t *outOffset) 1.3985 +{ 1.3986 + NS_ENSURE_TRUE(aLeftNode && aRightNode && aOutJoinNode && outOffset, NS_ERROR_NULL_POINTER); 1.3987 + 1.3988 + // while the rightmost children and their descendants of the left node 1.3989 + // match the leftmost children and their descendants of the right node 1.3990 + // join them up. Can you say that three times fast? 1.3991 + 1.3992 + nsCOMPtr<nsIDOMNode> leftNodeToJoin = do_QueryInterface(aLeftNode); 1.3993 + nsCOMPtr<nsIDOMNode> rightNodeToJoin = do_QueryInterface(aRightNode); 1.3994 + nsCOMPtr<nsIDOMNode> parentNode,tmp; 1.3995 + nsresult res = NS_OK; 1.3996 + 1.3997 + rightNodeToJoin->GetParentNode(getter_AddRefs(parentNode)); 1.3998 + 1.3999 + while (leftNodeToJoin && rightNodeToJoin && parentNode && 1.4000 + NodesSameType(leftNodeToJoin, rightNodeToJoin)) 1.4001 + { 1.4002 + // adjust out params 1.4003 + uint32_t length; 1.4004 + res = GetLengthOfDOMNode(leftNodeToJoin, length); 1.4005 + NS_ENSURE_SUCCESS(res, res); 1.4006 + 1.4007 + *aOutJoinNode = rightNodeToJoin; 1.4008 + *outOffset = length; 1.4009 + 1.4010 + // do the join 1.4011 + res = JoinNodes(leftNodeToJoin, rightNodeToJoin, parentNode); 1.4012 + NS_ENSURE_SUCCESS(res, res); 1.4013 + 1.4014 + if (IsTextNode(parentNode)) // we've joined all the way down to text nodes, we're done! 1.4015 + return NS_OK; 1.4016 + 1.4017 + else 1.4018 + { 1.4019 + // get new left and right nodes, and begin anew 1.4020 + parentNode = rightNodeToJoin; 1.4021 + leftNodeToJoin = GetChildAt(parentNode, length-1); 1.4022 + rightNodeToJoin = GetChildAt(parentNode, length); 1.4023 + 1.4024 + // skip over non-editable nodes 1.4025 + while (leftNodeToJoin && !IsEditable(leftNodeToJoin)) 1.4026 + { 1.4027 + leftNodeToJoin->GetPreviousSibling(getter_AddRefs(tmp)); 1.4028 + leftNodeToJoin = tmp; 1.4029 + } 1.4030 + if (!leftNodeToJoin) break; 1.4031 + 1.4032 + while (rightNodeToJoin && !IsEditable(rightNodeToJoin)) 1.4033 + { 1.4034 + rightNodeToJoin->GetNextSibling(getter_AddRefs(tmp)); 1.4035 + rightNodeToJoin = tmp; 1.4036 + } 1.4037 + if (!rightNodeToJoin) break; 1.4038 + } 1.4039 + } 1.4040 + 1.4041 + return res; 1.4042 +} 1.4043 + 1.4044 +void 1.4045 +nsEditor::BeginUpdateViewBatch() 1.4046 +{ 1.4047 + NS_PRECONDITION(mUpdateCount >= 0, "bad state"); 1.4048 + 1.4049 + if (0 == mUpdateCount) 1.4050 + { 1.4051 + // Turn off selection updates and notifications. 1.4052 + 1.4053 + nsCOMPtr<nsISelection> selection; 1.4054 + GetSelection(getter_AddRefs(selection)); 1.4055 + 1.4056 + if (selection) 1.4057 + { 1.4058 + nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(selection)); 1.4059 + selPrivate->StartBatchChanges(); 1.4060 + } 1.4061 + } 1.4062 + 1.4063 + mUpdateCount++; 1.4064 +} 1.4065 + 1.4066 + 1.4067 +nsresult nsEditor::EndUpdateViewBatch() 1.4068 +{ 1.4069 + NS_PRECONDITION(mUpdateCount > 0, "bad state"); 1.4070 + 1.4071 + if (mUpdateCount <= 0) 1.4072 + { 1.4073 + mUpdateCount = 0; 1.4074 + return NS_ERROR_FAILURE; 1.4075 + } 1.4076 + 1.4077 + mUpdateCount--; 1.4078 + 1.4079 + if (0 == mUpdateCount) 1.4080 + { 1.4081 + // Turn selection updating and notifications back on. 1.4082 + 1.4083 + nsCOMPtr<nsISelection>selection; 1.4084 + GetSelection(getter_AddRefs(selection)); 1.4085 + 1.4086 + if (selection) { 1.4087 + nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(selection)); 1.4088 + selPrivate->EndBatchChanges(); 1.4089 + } 1.4090 + } 1.4091 + 1.4092 + return NS_OK; 1.4093 +} 1.4094 + 1.4095 +bool 1.4096 +nsEditor::GetShouldTxnSetSelection() 1.4097 +{ 1.4098 + return mShouldTxnSetSelection; 1.4099 +} 1.4100 + 1.4101 + 1.4102 +NS_IMETHODIMP 1.4103 +nsEditor::DeleteSelectionImpl(EDirection aAction, 1.4104 + EStripWrappers aStripWrappers) 1.4105 +{ 1.4106 + MOZ_ASSERT(aStripWrappers == eStrip || aStripWrappers == eNoStrip); 1.4107 + 1.4108 + nsCOMPtr<nsISelection>selection; 1.4109 + nsresult res = GetSelection(getter_AddRefs(selection)); 1.4110 + NS_ENSURE_SUCCESS(res, res); 1.4111 + nsRefPtr<EditAggregateTxn> txn; 1.4112 + nsCOMPtr<nsINode> deleteNode; 1.4113 + int32_t deleteCharOffset = 0, deleteCharLength = 0; 1.4114 + res = CreateTxnForDeleteSelection(aAction, getter_AddRefs(txn), 1.4115 + getter_AddRefs(deleteNode), 1.4116 + &deleteCharOffset, &deleteCharLength); 1.4117 + nsCOMPtr<nsIDOMCharacterData> deleteCharData(do_QueryInterface(deleteNode)); 1.4118 + 1.4119 + if (NS_SUCCEEDED(res)) 1.4120 + { 1.4121 + nsAutoRules beginRulesSniffing(this, EditAction::deleteSelection, aAction); 1.4122 + int32_t i; 1.4123 + // Notify nsIEditActionListener::WillDelete[Selection|Text|Node] 1.4124 + if (!deleteNode) 1.4125 + for (i = 0; i < mActionListeners.Count(); i++) 1.4126 + mActionListeners[i]->WillDeleteSelection(selection); 1.4127 + else if (deleteCharData) 1.4128 + for (i = 0; i < mActionListeners.Count(); i++) 1.4129 + mActionListeners[i]->WillDeleteText(deleteCharData, deleteCharOffset, 1); 1.4130 + else 1.4131 + for (i = 0; i < mActionListeners.Count(); i++) 1.4132 + mActionListeners[i]->WillDeleteNode(deleteNode->AsDOMNode()); 1.4133 + 1.4134 + // Delete the specified amount 1.4135 + res = DoTransaction(txn); 1.4136 + 1.4137 + // Notify nsIEditActionListener::DidDelete[Selection|Text|Node] 1.4138 + if (!deleteNode) 1.4139 + for (i = 0; i < mActionListeners.Count(); i++) 1.4140 + mActionListeners[i]->DidDeleteSelection(selection); 1.4141 + else if (deleteCharData) 1.4142 + for (i = 0; i < mActionListeners.Count(); i++) 1.4143 + mActionListeners[i]->DidDeleteText(deleteCharData, deleteCharOffset, 1, res); 1.4144 + else 1.4145 + for (i = 0; i < mActionListeners.Count(); i++) 1.4146 + mActionListeners[i]->DidDeleteNode(deleteNode->AsDOMNode(), res); 1.4147 + } 1.4148 + 1.4149 + return res; 1.4150 +} 1.4151 + 1.4152 +// XXX: error handling in this routine needs to be cleaned up! 1.4153 +NS_IMETHODIMP 1.4154 +nsEditor::DeleteSelectionAndCreateNode(const nsAString& aTag, 1.4155 + nsIDOMNode ** aNewNode) 1.4156 +{ 1.4157 + nsresult result = DeleteSelectionAndPrepareToCreateNode(); 1.4158 + NS_ENSURE_SUCCESS(result, result); 1.4159 + 1.4160 + nsRefPtr<Selection> selection = GetSelection(); 1.4161 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.4162 + 1.4163 + nsCOMPtr<nsINode> node = selection->GetAnchorNode(); 1.4164 + uint32_t offset = selection->AnchorOffset(); 1.4165 + 1.4166 + nsCOMPtr<nsIDOMNode> newNode; 1.4167 + result = CreateNode(aTag, node->AsDOMNode(), offset, 1.4168 + getter_AddRefs(newNode)); 1.4169 + // XXX: ERROR_HANDLING check result, and make sure aNewNode is set correctly 1.4170 + // in success/failure cases 1.4171 + *aNewNode = newNode; 1.4172 + NS_IF_ADDREF(*aNewNode); 1.4173 + 1.4174 + // we want the selection to be just after the new node 1.4175 + return selection->Collapse(node, offset + 1); 1.4176 +} 1.4177 + 1.4178 + 1.4179 +/* Non-interface, protected methods */ 1.4180 + 1.4181 +TextComposition* 1.4182 +nsEditor::GetComposition() const 1.4183 +{ 1.4184 + return mComposition; 1.4185 +} 1.4186 + 1.4187 +bool 1.4188 +nsEditor::IsIMEComposing() const 1.4189 +{ 1.4190 + return mComposition && mComposition->IsComposing(); 1.4191 +} 1.4192 + 1.4193 +nsresult 1.4194 +nsEditor::DeleteSelectionAndPrepareToCreateNode() 1.4195 +{ 1.4196 + nsresult res; 1.4197 + nsRefPtr<Selection> selection = GetSelection(); 1.4198 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.4199 + MOZ_ASSERT(selection->GetAnchorFocusRange()); 1.4200 + 1.4201 + if (!selection->GetAnchorFocusRange()->Collapsed()) { 1.4202 + res = DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip); 1.4203 + NS_ENSURE_SUCCESS(res, res); 1.4204 + 1.4205 + MOZ_ASSERT(selection->GetAnchorFocusRange() && 1.4206 + selection->GetAnchorFocusRange()->Collapsed(), 1.4207 + "Selection not collapsed after delete"); 1.4208 + } 1.4209 + 1.4210 + // If the selection is a chardata node, split it if necessary and compute 1.4211 + // where to put the new node 1.4212 + nsCOMPtr<nsINode> node = selection->GetAnchorNode(); 1.4213 + MOZ_ASSERT(node, "Selection has no ranges in it"); 1.4214 + 1.4215 + if (node && node->IsNodeOfType(nsINode::eDATA_NODE)) { 1.4216 + NS_ASSERTION(node->GetParentNode(), 1.4217 + "It's impossible to insert into chardata with no parent -- " 1.4218 + "fix the caller"); 1.4219 + NS_ENSURE_STATE(node->GetParentNode()); 1.4220 + 1.4221 + uint32_t offset = selection->AnchorOffset(); 1.4222 + 1.4223 + if (offset == 0) { 1.4224 + res = selection->Collapse(node->GetParentNode(), 1.4225 + node->GetParentNode()->IndexOf(node)); 1.4226 + MOZ_ASSERT(NS_SUCCEEDED(res)); 1.4227 + NS_ENSURE_SUCCESS(res, res); 1.4228 + } else if (offset == node->Length()) { 1.4229 + res = selection->Collapse(node->GetParentNode(), 1.4230 + node->GetParentNode()->IndexOf(node) + 1); 1.4231 + MOZ_ASSERT(NS_SUCCEEDED(res)); 1.4232 + NS_ENSURE_SUCCESS(res, res); 1.4233 + } else { 1.4234 + nsCOMPtr<nsIDOMNode> tmp; 1.4235 + res = SplitNode(node->AsDOMNode(), offset, getter_AddRefs(tmp)); 1.4236 + NS_ENSURE_SUCCESS(res, res); 1.4237 + res = selection->Collapse(node->GetParentNode(), 1.4238 + node->GetParentNode()->IndexOf(node)); 1.4239 + MOZ_ASSERT(NS_SUCCEEDED(res)); 1.4240 + NS_ENSURE_SUCCESS(res, res); 1.4241 + } 1.4242 + } 1.4243 + return NS_OK; 1.4244 +} 1.4245 + 1.4246 + 1.4247 + 1.4248 +void 1.4249 +nsEditor::DoAfterDoTransaction(nsITransaction *aTxn) 1.4250 +{ 1.4251 + bool isTransientTransaction; 1.4252 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( 1.4253 + aTxn->GetIsTransient(&isTransientTransaction))); 1.4254 + 1.4255 + if (!isTransientTransaction) 1.4256 + { 1.4257 + // we need to deal here with the case where the user saved after some 1.4258 + // edits, then undid one or more times. Then, the undo count is -ve, 1.4259 + // but we can't let a do take it back to zero. So we flip it up to 1.4260 + // a +ve number. 1.4261 + int32_t modCount; 1.4262 + GetModificationCount(&modCount); 1.4263 + if (modCount < 0) 1.4264 + modCount = -modCount; 1.4265 + 1.4266 + // don't count transient transactions 1.4267 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( 1.4268 + IncrementModificationCount(1))); 1.4269 + } 1.4270 +} 1.4271 + 1.4272 + 1.4273 +void 1.4274 +nsEditor::DoAfterUndoTransaction() 1.4275 +{ 1.4276 + // all undoable transactions are non-transient 1.4277 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( 1.4278 + IncrementModificationCount(-1))); 1.4279 +} 1.4280 + 1.4281 +void 1.4282 +nsEditor::DoAfterRedoTransaction() 1.4283 +{ 1.4284 + // all redoable transactions are non-transient 1.4285 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( 1.4286 + IncrementModificationCount(1))); 1.4287 +} 1.4288 + 1.4289 +NS_IMETHODIMP 1.4290 +nsEditor::CreateTxnForSetAttribute(nsIDOMElement *aElement, 1.4291 + const nsAString& aAttribute, 1.4292 + const nsAString& aValue, 1.4293 + ChangeAttributeTxn ** aTxn) 1.4294 +{ 1.4295 + NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER); 1.4296 + 1.4297 + nsRefPtr<ChangeAttributeTxn> txn = new ChangeAttributeTxn(); 1.4298 + 1.4299 + nsresult rv = txn->Init(this, aElement, aAttribute, aValue, false); 1.4300 + if (NS_SUCCEEDED(rv)) 1.4301 + { 1.4302 + txn.forget(aTxn); 1.4303 + } 1.4304 + 1.4305 + return rv; 1.4306 +} 1.4307 + 1.4308 + 1.4309 +NS_IMETHODIMP 1.4310 +nsEditor::CreateTxnForRemoveAttribute(nsIDOMElement *aElement, 1.4311 + const nsAString& aAttribute, 1.4312 + ChangeAttributeTxn ** aTxn) 1.4313 +{ 1.4314 + NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER); 1.4315 + 1.4316 + nsRefPtr<ChangeAttributeTxn> txn = new ChangeAttributeTxn(); 1.4317 + 1.4318 + nsresult rv = txn->Init(this, aElement, aAttribute, EmptyString(), true); 1.4319 + if (NS_SUCCEEDED(rv)) 1.4320 + { 1.4321 + txn.forget(aTxn); 1.4322 + } 1.4323 + 1.4324 + return rv; 1.4325 +} 1.4326 + 1.4327 + 1.4328 +NS_IMETHODIMP nsEditor::CreateTxnForCreateElement(const nsAString& aTag, 1.4329 + nsIDOMNode *aParent, 1.4330 + int32_t aPosition, 1.4331 + CreateElementTxn ** aTxn) 1.4332 +{ 1.4333 + NS_ENSURE_TRUE(aParent, NS_ERROR_NULL_POINTER); 1.4334 + 1.4335 + nsRefPtr<CreateElementTxn> txn = new CreateElementTxn(); 1.4336 + 1.4337 + nsresult rv = txn->Init(this, aTag, aParent, aPosition); 1.4338 + if (NS_SUCCEEDED(rv)) 1.4339 + { 1.4340 + txn.forget(aTxn); 1.4341 + } 1.4342 + 1.4343 + return rv; 1.4344 +} 1.4345 + 1.4346 + 1.4347 +NS_IMETHODIMP nsEditor::CreateTxnForInsertElement(nsIDOMNode * aNode, 1.4348 + nsIDOMNode * aParent, 1.4349 + int32_t aPosition, 1.4350 + InsertElementTxn ** aTxn) 1.4351 +{ 1.4352 + NS_ENSURE_TRUE(aNode && aParent, NS_ERROR_NULL_POINTER); 1.4353 + 1.4354 + nsRefPtr<InsertElementTxn> txn = new InsertElementTxn(); 1.4355 + 1.4356 + nsresult rv = txn->Init(aNode, aParent, aPosition, this); 1.4357 + if (NS_SUCCEEDED(rv)) 1.4358 + { 1.4359 + txn.forget(aTxn); 1.4360 + } 1.4361 + 1.4362 + return rv; 1.4363 +} 1.4364 + 1.4365 +nsresult 1.4366 +nsEditor::CreateTxnForDeleteNode(nsINode* aNode, DeleteNodeTxn** aTxn) 1.4367 +{ 1.4368 + NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER); 1.4369 + 1.4370 + nsRefPtr<DeleteNodeTxn> txn = new DeleteNodeTxn(); 1.4371 + 1.4372 + nsresult res = txn->Init(this, aNode, &mRangeUpdater); 1.4373 + NS_ENSURE_SUCCESS(res, res); 1.4374 + 1.4375 + txn.forget(aTxn); 1.4376 + return NS_OK; 1.4377 +} 1.4378 + 1.4379 +NS_IMETHODIMP 1.4380 +nsEditor::CreateTxnForIMEText(const nsAString& aStringToInsert, 1.4381 + IMETextTxn ** aTxn) 1.4382 +{ 1.4383 + NS_ASSERTION(aTxn, "illegal value- null ptr- aTxn"); 1.4384 + 1.4385 + nsRefPtr<IMETextTxn> txn = new IMETextTxn(); 1.4386 + 1.4387 + // During handling IME composition, mComposition must have been initialized. 1.4388 + // TODO: We can simplify IMETextTxn::Init() with TextComposition class. 1.4389 + nsresult rv = txn->Init(mIMETextNode, mIMETextOffset, 1.4390 + mComposition->String().Length(), 1.4391 + mComposition->GetRanges(), aStringToInsert, this); 1.4392 + if (NS_SUCCEEDED(rv)) 1.4393 + { 1.4394 + txn.forget(aTxn); 1.4395 + } 1.4396 + 1.4397 + return rv; 1.4398 +} 1.4399 + 1.4400 + 1.4401 +NS_IMETHODIMP 1.4402 +nsEditor::CreateTxnForAddStyleSheet(nsCSSStyleSheet* aSheet, AddStyleSheetTxn* *aTxn) 1.4403 +{ 1.4404 + nsRefPtr<AddStyleSheetTxn> txn = new AddStyleSheetTxn(); 1.4405 + 1.4406 + nsresult rv = txn->Init(this, aSheet); 1.4407 + if (NS_SUCCEEDED(rv)) 1.4408 + { 1.4409 + txn.forget(aTxn); 1.4410 + } 1.4411 + 1.4412 + return rv; 1.4413 +} 1.4414 + 1.4415 + 1.4416 + 1.4417 +NS_IMETHODIMP 1.4418 +nsEditor::CreateTxnForRemoveStyleSheet(nsCSSStyleSheet* aSheet, RemoveStyleSheetTxn* *aTxn) 1.4419 +{ 1.4420 + nsRefPtr<RemoveStyleSheetTxn> txn = new RemoveStyleSheetTxn(); 1.4421 + 1.4422 + nsresult rv = txn->Init(this, aSheet); 1.4423 + if (NS_SUCCEEDED(rv)) 1.4424 + { 1.4425 + txn.forget(aTxn); 1.4426 + } 1.4427 + 1.4428 + return rv; 1.4429 +} 1.4430 + 1.4431 + 1.4432 +nsresult 1.4433 +nsEditor::CreateTxnForDeleteSelection(EDirection aAction, 1.4434 + EditAggregateTxn** aTxn, 1.4435 + nsINode** aNode, 1.4436 + int32_t* aOffset, 1.4437 + int32_t* aLength) 1.4438 +{ 1.4439 + MOZ_ASSERT(aTxn); 1.4440 + *aTxn = nullptr; 1.4441 + 1.4442 + nsRefPtr<Selection> selection = GetSelection(); 1.4443 + NS_ENSURE_STATE(selection); 1.4444 + 1.4445 + // Check whether the selection is collapsed and we should do nothing: 1.4446 + if (selection->Collapsed() && aAction == eNone) { 1.4447 + return NS_OK; 1.4448 + } 1.4449 + 1.4450 + // allocate the out-param transaction 1.4451 + nsRefPtr<EditAggregateTxn> aggTxn = new EditAggregateTxn(); 1.4452 + 1.4453 + for (int32_t rangeIdx = 0; rangeIdx < selection->GetRangeCount(); ++rangeIdx) { 1.4454 + nsRefPtr<nsRange> range = selection->GetRangeAt(rangeIdx); 1.4455 + NS_ENSURE_STATE(range); 1.4456 + 1.4457 + // Same with range as with selection; if it is collapsed and action 1.4458 + // is eNone, do nothing. 1.4459 + if (!range->Collapsed()) { 1.4460 + nsRefPtr<DeleteRangeTxn> txn = new DeleteRangeTxn(); 1.4461 + txn->Init(this, range, &mRangeUpdater); 1.4462 + aggTxn->AppendChild(txn); 1.4463 + } else if (aAction != eNone) { 1.4464 + // we have an insertion point. delete the thing in front of it or 1.4465 + // behind it, depending on aAction 1.4466 + nsresult res = CreateTxnForDeleteInsertionPoint(range, aAction, aggTxn, 1.4467 + aNode, aOffset, aLength); 1.4468 + NS_ENSURE_SUCCESS(res, res); 1.4469 + } 1.4470 + } 1.4471 + 1.4472 + aggTxn.forget(aTxn); 1.4473 + 1.4474 + return NS_OK; 1.4475 +} 1.4476 + 1.4477 +nsresult 1.4478 +nsEditor::CreateTxnForDeleteCharacter(nsIDOMCharacterData* aData, 1.4479 + uint32_t aOffset, 1.4480 + EDirection aDirection, 1.4481 + DeleteTextTxn** aTxn) 1.4482 +{ 1.4483 + NS_ASSERTION(aDirection == eNext || aDirection == ePrevious, 1.4484 + "invalid direction"); 1.4485 + nsAutoString data; 1.4486 + aData->GetData(data); 1.4487 + NS_ASSERTION(data.Length(), "Trying to delete from a zero-length node"); 1.4488 + NS_ENSURE_STATE(data.Length()); 1.4489 + 1.4490 + uint32_t segOffset = aOffset, segLength = 1; 1.4491 + if (aDirection == eNext) { 1.4492 + if (segOffset + 1 < data.Length() && 1.4493 + NS_IS_HIGH_SURROGATE(data[segOffset]) && 1.4494 + NS_IS_LOW_SURROGATE(data[segOffset+1])) { 1.4495 + // delete both halves of the surrogate pair 1.4496 + ++segLength; 1.4497 + } 1.4498 + } else if (aOffset > 0) { 1.4499 + --segOffset; 1.4500 + if (segOffset > 0 && 1.4501 + NS_IS_LOW_SURROGATE(data[segOffset]) && 1.4502 + NS_IS_HIGH_SURROGATE(data[segOffset-1])) { 1.4503 + ++segLength; 1.4504 + --segOffset; 1.4505 + } 1.4506 + } else { 1.4507 + return NS_ERROR_FAILURE; 1.4508 + } 1.4509 + return CreateTxnForDeleteText(aData, segOffset, segLength, aTxn); 1.4510 +} 1.4511 + 1.4512 +//XXX: currently, this doesn't handle edge conditions because GetNext/GetPrior 1.4513 +//are not implemented 1.4514 +nsresult 1.4515 +nsEditor::CreateTxnForDeleteInsertionPoint(nsRange* aRange, 1.4516 + EDirection aAction, 1.4517 + EditAggregateTxn* aTxn, 1.4518 + nsINode** aNode, 1.4519 + int32_t* aOffset, 1.4520 + int32_t* aLength) 1.4521 +{ 1.4522 + MOZ_ASSERT(aAction != eNone); 1.4523 + 1.4524 + nsresult res; 1.4525 + 1.4526 + // get the node and offset of the insertion point 1.4527 + nsCOMPtr<nsINode> node = aRange->GetStartParent(); 1.4528 + NS_ENSURE_STATE(node); 1.4529 + 1.4530 + int32_t offset = aRange->StartOffset(); 1.4531 + 1.4532 + // determine if the insertion point is at the beginning, middle, or end of 1.4533 + // the node 1.4534 + nsCOMPtr<nsIDOMCharacterData> nodeAsCharData = do_QueryInterface(node); 1.4535 + 1.4536 + uint32_t count = node->Length(); 1.4537 + 1.4538 + bool isFirst = (0 == offset); 1.4539 + bool isLast = (count == (uint32_t)offset); 1.4540 + 1.4541 + // XXX: if isFirst && isLast, then we'll need to delete the node 1.4542 + // as well as the 1 child 1.4543 + 1.4544 + // build a transaction for deleting the appropriate data 1.4545 + // XXX: this has to come from rule section 1.4546 + if (aAction == ePrevious && isFirst) { 1.4547 + // we're backspacing from the beginning of the node. Delete the first 1.4548 + // thing to our left 1.4549 + nsCOMPtr<nsIContent> priorNode = GetPriorNode(node, true); 1.4550 + NS_ENSURE_STATE(priorNode); 1.4551 + 1.4552 + // there is a priorNode, so delete its last child (if chardata, delete the 1.4553 + // last char). if it has no children, delete it 1.4554 + nsCOMPtr<nsIDOMCharacterData> priorNodeAsCharData = 1.4555 + do_QueryInterface(priorNode); 1.4556 + if (priorNodeAsCharData) { 1.4557 + uint32_t length = priorNode->Length(); 1.4558 + // Bail out for empty chardata XXX: Do we want to do something else? 1.4559 + NS_ENSURE_STATE(length); 1.4560 + nsRefPtr<DeleteTextTxn> txn; 1.4561 + res = CreateTxnForDeleteCharacter(priorNodeAsCharData, length, 1.4562 + ePrevious, getter_AddRefs(txn)); 1.4563 + NS_ENSURE_SUCCESS(res, res); 1.4564 + 1.4565 + *aOffset = txn->GetOffset(); 1.4566 + *aLength = txn->GetNumCharsToDelete(); 1.4567 + aTxn->AppendChild(txn); 1.4568 + } else { 1.4569 + // priorNode is not chardata, so tell its parent to delete it 1.4570 + nsRefPtr<DeleteNodeTxn> txn; 1.4571 + res = CreateTxnForDeleteNode(priorNode, getter_AddRefs(txn)); 1.4572 + NS_ENSURE_SUCCESS(res, res); 1.4573 + 1.4574 + aTxn->AppendChild(txn); 1.4575 + } 1.4576 + 1.4577 + NS_ADDREF(*aNode = priorNode); 1.4578 + 1.4579 + return NS_OK; 1.4580 + } 1.4581 + 1.4582 + if (aAction == eNext && isLast) { 1.4583 + // we're deleting from the end of the node. Delete the first thing to our 1.4584 + // right 1.4585 + nsCOMPtr<nsIContent> nextNode = GetNextNode(node, true); 1.4586 + NS_ENSURE_STATE(nextNode); 1.4587 + 1.4588 + // there is a nextNode, so delete its first child (if chardata, delete the 1.4589 + // first char). if it has no children, delete it 1.4590 + nsCOMPtr<nsIDOMCharacterData> nextNodeAsCharData = 1.4591 + do_QueryInterface(nextNode); 1.4592 + if (nextNodeAsCharData) { 1.4593 + uint32_t length = nextNode->Length(); 1.4594 + // Bail out for empty chardata XXX: Do we want to do something else? 1.4595 + NS_ENSURE_STATE(length); 1.4596 + nsRefPtr<DeleteTextTxn> txn; 1.4597 + res = CreateTxnForDeleteCharacter(nextNodeAsCharData, 0, eNext, 1.4598 + getter_AddRefs(txn)); 1.4599 + NS_ENSURE_SUCCESS(res, res); 1.4600 + 1.4601 + *aOffset = txn->GetOffset(); 1.4602 + *aLength = txn->GetNumCharsToDelete(); 1.4603 + aTxn->AppendChild(txn); 1.4604 + } else { 1.4605 + // nextNode is not chardata, so tell its parent to delete it 1.4606 + nsRefPtr<DeleteNodeTxn> txn; 1.4607 + res = CreateTxnForDeleteNode(nextNode, getter_AddRefs(txn)); 1.4608 + NS_ENSURE_SUCCESS(res, res); 1.4609 + aTxn->AppendChild(txn); 1.4610 + } 1.4611 + 1.4612 + NS_ADDREF(*aNode = nextNode); 1.4613 + 1.4614 + return NS_OK; 1.4615 + } 1.4616 + 1.4617 + if (nodeAsCharData) { 1.4618 + // we have chardata, so delete a char at the proper offset 1.4619 + nsRefPtr<DeleteTextTxn> txn; 1.4620 + res = CreateTxnForDeleteCharacter(nodeAsCharData, offset, aAction, 1.4621 + getter_AddRefs(txn)); 1.4622 + NS_ENSURE_SUCCESS(res, res); 1.4623 + 1.4624 + aTxn->AppendChild(txn); 1.4625 + NS_ADDREF(*aNode = node); 1.4626 + *aOffset = txn->GetOffset(); 1.4627 + *aLength = txn->GetNumCharsToDelete(); 1.4628 + } else { 1.4629 + // we're either deleting a node or chardata, need to dig into the next/prev 1.4630 + // node to find out 1.4631 + nsCOMPtr<nsINode> selectedNode; 1.4632 + if (aAction == ePrevious) { 1.4633 + selectedNode = GetPriorNode(node, offset, true); 1.4634 + } else if (aAction == eNext) { 1.4635 + selectedNode = GetNextNode(node, offset, true); 1.4636 + } 1.4637 + 1.4638 + while (selectedNode && 1.4639 + selectedNode->IsNodeOfType(nsINode::eDATA_NODE) && 1.4640 + !selectedNode->Length()) { 1.4641 + // Can't delete an empty chardata node (bug 762183) 1.4642 + if (aAction == ePrevious) { 1.4643 + selectedNode = GetPriorNode(selectedNode, true); 1.4644 + } else if (aAction == eNext) { 1.4645 + selectedNode = GetNextNode(selectedNode, true); 1.4646 + } 1.4647 + } 1.4648 + NS_ENSURE_STATE(selectedNode); 1.4649 + 1.4650 + nsCOMPtr<nsIDOMCharacterData> selectedNodeAsCharData = 1.4651 + do_QueryInterface(selectedNode); 1.4652 + if (selectedNodeAsCharData) { 1.4653 + // we are deleting from a chardata node, so do a character deletion 1.4654 + uint32_t position = 0; 1.4655 + if (aAction == ePrevious) { 1.4656 + position = selectedNode->Length(); 1.4657 + } 1.4658 + nsRefPtr<DeleteTextTxn> delTextTxn; 1.4659 + res = CreateTxnForDeleteCharacter(selectedNodeAsCharData, position, 1.4660 + aAction, getter_AddRefs(delTextTxn)); 1.4661 + NS_ENSURE_SUCCESS(res, res); 1.4662 + NS_ENSURE_TRUE(delTextTxn, NS_ERROR_NULL_POINTER); 1.4663 + 1.4664 + aTxn->AppendChild(delTextTxn); 1.4665 + *aOffset = delTextTxn->GetOffset(); 1.4666 + *aLength = delTextTxn->GetNumCharsToDelete(); 1.4667 + } else { 1.4668 + nsRefPtr<DeleteNodeTxn> delElementTxn; 1.4669 + res = CreateTxnForDeleteNode(selectedNode, getter_AddRefs(delElementTxn)); 1.4670 + NS_ENSURE_SUCCESS(res, res); 1.4671 + NS_ENSURE_TRUE(delElementTxn, NS_ERROR_NULL_POINTER); 1.4672 + 1.4673 + aTxn->AppendChild(delElementTxn); 1.4674 + } 1.4675 + 1.4676 + NS_ADDREF(*aNode = selectedNode); 1.4677 + } 1.4678 + 1.4679 + return NS_OK; 1.4680 +} 1.4681 + 1.4682 +nsresult 1.4683 +nsEditor::CreateRange(nsIDOMNode *aStartParent, int32_t aStartOffset, 1.4684 + nsIDOMNode *aEndParent, int32_t aEndOffset, 1.4685 + nsIDOMRange **aRange) 1.4686 +{ 1.4687 + return nsRange::CreateRange(aStartParent, aStartOffset, aEndParent, 1.4688 + aEndOffset, aRange); 1.4689 +} 1.4690 + 1.4691 +nsresult 1.4692 +nsEditor::AppendNodeToSelectionAsRange(nsIDOMNode *aNode) 1.4693 +{ 1.4694 + NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER); 1.4695 + nsCOMPtr<nsISelection> selection; 1.4696 + nsresult res = GetSelection(getter_AddRefs(selection)); 1.4697 + NS_ENSURE_SUCCESS(res, res); 1.4698 + if(!selection) return NS_ERROR_FAILURE; 1.4699 + 1.4700 + nsCOMPtr<nsIDOMNode> parentNode; 1.4701 + res = aNode->GetParentNode(getter_AddRefs(parentNode)); 1.4702 + NS_ENSURE_SUCCESS(res, res); 1.4703 + NS_ENSURE_TRUE(parentNode, NS_ERROR_NULL_POINTER); 1.4704 + 1.4705 + int32_t offset = GetChildOffset(aNode, parentNode); 1.4706 + 1.4707 + nsCOMPtr<nsIDOMRange> range; 1.4708 + res = CreateRange(parentNode, offset, parentNode, offset+1, getter_AddRefs(range)); 1.4709 + NS_ENSURE_SUCCESS(res, res); 1.4710 + NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER); 1.4711 + 1.4712 + return selection->AddRange(range); 1.4713 +} 1.4714 + 1.4715 +nsresult nsEditor::ClearSelection() 1.4716 +{ 1.4717 + nsCOMPtr<nsISelection> selection; 1.4718 + nsresult res = nsEditor::GetSelection(getter_AddRefs(selection)); 1.4719 + NS_ENSURE_SUCCESS(res, res); 1.4720 + NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); 1.4721 + return selection->RemoveAllRanges(); 1.4722 +} 1.4723 + 1.4724 +nsresult 1.4725 +nsEditor::CreateHTMLContent(const nsAString& aTag, dom::Element** aContent) 1.4726 +{ 1.4727 + nsCOMPtr<nsIDocument> doc = GetDocument(); 1.4728 + NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); 1.4729 + 1.4730 + // XXX Wallpaper over editor bug (editor tries to create elements with an 1.4731 + // empty nodename). 1.4732 + if (aTag.IsEmpty()) { 1.4733 + NS_ERROR("Don't pass an empty tag to nsEditor::CreateHTMLContent, " 1.4734 + "check caller."); 1.4735 + return NS_ERROR_FAILURE; 1.4736 + } 1.4737 + 1.4738 + return doc->CreateElem(aTag, nullptr, kNameSpaceID_XHTML, 1.4739 + reinterpret_cast<nsIContent**>(aContent)); 1.4740 +} 1.4741 + 1.4742 +nsresult 1.4743 +nsEditor::SetAttributeOrEquivalent(nsIDOMElement * aElement, 1.4744 + const nsAString & aAttribute, 1.4745 + const nsAString & aValue, 1.4746 + bool aSuppressTransaction) 1.4747 +{ 1.4748 + return SetAttribute(aElement, aAttribute, aValue); 1.4749 +} 1.4750 + 1.4751 +nsresult 1.4752 +nsEditor::RemoveAttributeOrEquivalent(nsIDOMElement * aElement, 1.4753 + const nsAString & aAttribute, 1.4754 + bool aSuppressTransaction) 1.4755 +{ 1.4756 + return RemoveAttribute(aElement, aAttribute); 1.4757 +} 1.4758 + 1.4759 +nsresult 1.4760 +nsEditor::HandleKeyPressEvent(nsIDOMKeyEvent* aKeyEvent) 1.4761 +{ 1.4762 + // NOTE: When you change this method, you should also change: 1.4763 + // * editor/libeditor/text/tests/test_texteditor_keyevent_handling.html 1.4764 + // * editor/libeditor/html/tests/test_htmleditor_keyevent_handling.html 1.4765 + // 1.4766 + // And also when you add new key handling, you need to change the subclass's 1.4767 + // HandleKeyPressEvent()'s switch statement. 1.4768 + 1.4769 + WidgetKeyboardEvent* nativeKeyEvent = 1.4770 + aKeyEvent->GetInternalNSEvent()->AsKeyboardEvent(); 1.4771 + NS_ENSURE_TRUE(nativeKeyEvent, NS_ERROR_UNEXPECTED); 1.4772 + NS_ASSERTION(nativeKeyEvent->message == NS_KEY_PRESS, 1.4773 + "HandleKeyPressEvent gets non-keypress event"); 1.4774 + 1.4775 + // if we are readonly or disabled, then do nothing. 1.4776 + if (IsReadonly() || IsDisabled()) { 1.4777 + // consume backspace for disabled and readonly textfields, to prevent 1.4778 + // back in history, which could be confusing to users 1.4779 + if (nativeKeyEvent->keyCode == nsIDOMKeyEvent::DOM_VK_BACK_SPACE) { 1.4780 + aKeyEvent->PreventDefault(); 1.4781 + } 1.4782 + return NS_OK; 1.4783 + } 1.4784 + 1.4785 + switch (nativeKeyEvent->keyCode) { 1.4786 + case nsIDOMKeyEvent::DOM_VK_META: 1.4787 + case nsIDOMKeyEvent::DOM_VK_WIN: 1.4788 + case nsIDOMKeyEvent::DOM_VK_SHIFT: 1.4789 + case nsIDOMKeyEvent::DOM_VK_CONTROL: 1.4790 + case nsIDOMKeyEvent::DOM_VK_ALT: 1.4791 + aKeyEvent->PreventDefault(); // consumed 1.4792 + return NS_OK; 1.4793 + case nsIDOMKeyEvent::DOM_VK_BACK_SPACE: 1.4794 + if (nativeKeyEvent->IsControl() || nativeKeyEvent->IsAlt() || 1.4795 + nativeKeyEvent->IsMeta() || nativeKeyEvent->IsOS()) { 1.4796 + return NS_OK; 1.4797 + } 1.4798 + DeleteSelection(nsIEditor::ePrevious, nsIEditor::eStrip); 1.4799 + aKeyEvent->PreventDefault(); // consumed 1.4800 + return NS_OK; 1.4801 + case nsIDOMKeyEvent::DOM_VK_DELETE: 1.4802 + // on certain platforms (such as windows) the shift key 1.4803 + // modifies what delete does (cmd_cut in this case). 1.4804 + // bailing here to allow the keybindings to do the cut. 1.4805 + if (nativeKeyEvent->IsShift() || nativeKeyEvent->IsControl() || 1.4806 + nativeKeyEvent->IsAlt() || nativeKeyEvent->IsMeta() || 1.4807 + nativeKeyEvent->IsOS()) { 1.4808 + return NS_OK; 1.4809 + } 1.4810 + DeleteSelection(nsIEditor::eNext, nsIEditor::eStrip); 1.4811 + aKeyEvent->PreventDefault(); // consumed 1.4812 + return NS_OK; 1.4813 + } 1.4814 + return NS_OK; 1.4815 +} 1.4816 + 1.4817 +nsresult 1.4818 +nsEditor::HandleInlineSpellCheck(EditAction action, 1.4819 + nsISelection *aSelection, 1.4820 + nsIDOMNode *previousSelectedNode, 1.4821 + int32_t previousSelectedOffset, 1.4822 + nsIDOMNode *aStartNode, 1.4823 + int32_t aStartOffset, 1.4824 + nsIDOMNode *aEndNode, 1.4825 + int32_t aEndOffset) 1.4826 +{ 1.4827 + // Have to cast action here because this method is from an IDL 1.4828 + return mInlineSpellChecker ? mInlineSpellChecker->SpellCheckAfterEditorChange( 1.4829 + (int32_t)action, aSelection, 1.4830 + previousSelectedNode, previousSelectedOffset, 1.4831 + aStartNode, aStartOffset, aEndNode, 1.4832 + aEndOffset) 1.4833 + : NS_OK; 1.4834 +} 1.4835 + 1.4836 +already_AddRefed<nsIContent> 1.4837 +nsEditor::FindSelectionRoot(nsINode *aNode) 1.4838 +{ 1.4839 + nsCOMPtr<nsIContent> rootContent = GetRoot(); 1.4840 + return rootContent.forget(); 1.4841 +} 1.4842 + 1.4843 +nsresult 1.4844 +nsEditor::InitializeSelection(nsIDOMEventTarget* aFocusEventTarget) 1.4845 +{ 1.4846 + nsCOMPtr<nsINode> targetNode = do_QueryInterface(aFocusEventTarget); 1.4847 + NS_ENSURE_TRUE(targetNode, NS_ERROR_INVALID_ARG); 1.4848 + nsCOMPtr<nsIContent> selectionRootContent = FindSelectionRoot(targetNode); 1.4849 + if (!selectionRootContent) { 1.4850 + return NS_OK; 1.4851 + } 1.4852 + 1.4853 + bool isTargetDoc = 1.4854 + targetNode->NodeType() == nsIDOMNode::DOCUMENT_NODE && 1.4855 + targetNode->HasFlag(NODE_IS_EDITABLE); 1.4856 + 1.4857 + nsCOMPtr<nsISelection> selection; 1.4858 + nsresult rv = GetSelection(getter_AddRefs(selection)); 1.4859 + NS_ENSURE_SUCCESS(rv, rv); 1.4860 + 1.4861 + nsCOMPtr<nsIPresShell> presShell = GetPresShell(); 1.4862 + NS_ENSURE_TRUE(presShell, NS_ERROR_NOT_INITIALIZED); 1.4863 + 1.4864 + nsCOMPtr<nsISelectionController> selCon; 1.4865 + rv = GetSelectionController(getter_AddRefs(selCon)); 1.4866 + NS_ENSURE_SUCCESS(rv, rv); 1.4867 + 1.4868 + nsCOMPtr<nsISelectionPrivate> selectionPrivate = 1.4869 + do_QueryInterface(selection); 1.4870 + NS_ENSURE_TRUE(selectionPrivate, NS_ERROR_UNEXPECTED); 1.4871 + 1.4872 + // Init the caret 1.4873 + nsRefPtr<nsCaret> caret = presShell->GetCaret(); 1.4874 + NS_ENSURE_TRUE(caret, NS_ERROR_UNEXPECTED); 1.4875 + caret->SetIgnoreUserModify(false); 1.4876 + caret->SetCaretDOMSelection(selection); 1.4877 + selCon->SetCaretReadOnly(IsReadonly()); 1.4878 + selCon->SetCaretEnabled(true); 1.4879 + 1.4880 + // Init selection 1.4881 + selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON); 1.4882 + selCon->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL); 1.4883 + selCon->RepaintSelection(nsISelectionController::SELECTION_NORMAL); 1.4884 + // If the computed selection root isn't root content, we should set it 1.4885 + // as selection ancestor limit. However, if that is root element, it means 1.4886 + // there is not limitation of the selection, then, we must set nullptr. 1.4887 + // NOTE: If we set a root element to the ancestor limit, some selection 1.4888 + // methods don't work fine. 1.4889 + if (selectionRootContent->GetParent()) { 1.4890 + selectionPrivate->SetAncestorLimiter(selectionRootContent); 1.4891 + } else { 1.4892 + selectionPrivate->SetAncestorLimiter(nullptr); 1.4893 + } 1.4894 + 1.4895 + // XXX What case needs this? 1.4896 + if (isTargetDoc) { 1.4897 + int32_t rangeCount; 1.4898 + selection->GetRangeCount(&rangeCount); 1.4899 + if (rangeCount == 0) { 1.4900 + BeginningOfDocument(); 1.4901 + } 1.4902 + } 1.4903 + 1.4904 + return NS_OK; 1.4905 +} 1.4906 + 1.4907 +void 1.4908 +nsEditor::FinalizeSelection() 1.4909 +{ 1.4910 + nsCOMPtr<nsISelectionController> selCon; 1.4911 + nsresult rv = GetSelectionController(getter_AddRefs(selCon)); 1.4912 + NS_ENSURE_SUCCESS_VOID(rv); 1.4913 + 1.4914 + nsCOMPtr<nsISelection> selection; 1.4915 + rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, 1.4916 + getter_AddRefs(selection)); 1.4917 + NS_ENSURE_SUCCESS_VOID(rv); 1.4918 + 1.4919 + nsCOMPtr<nsISelectionPrivate> selectionPrivate = do_QueryInterface(selection); 1.4920 + NS_ENSURE_TRUE_VOID(selectionPrivate); 1.4921 + 1.4922 + selectionPrivate->SetAncestorLimiter(nullptr); 1.4923 + 1.4924 + nsCOMPtr<nsIPresShell> presShell = GetPresShell(); 1.4925 + NS_ENSURE_TRUE_VOID(presShell); 1.4926 + 1.4927 + selCon->SetCaretEnabled(false); 1.4928 + 1.4929 + nsFocusManager* fm = nsFocusManager::GetFocusManager(); 1.4930 + NS_ENSURE_TRUE_VOID(fm); 1.4931 + fm->UpdateCaretForCaretBrowsingMode(); 1.4932 + 1.4933 + if (!HasIndependentSelection()) { 1.4934 + // If this editor doesn't have an independent selection, i.e., it must 1.4935 + // mean that it is an HTML editor, the selection controller is shared with 1.4936 + // presShell. So, even this editor loses focus, other part of the document 1.4937 + // may still have focus. 1.4938 + nsCOMPtr<nsIDocument> doc = GetDocument(); 1.4939 + ErrorResult ret; 1.4940 + if (!doc || !doc->HasFocus(ret)) { 1.4941 + // If the document already lost focus, mark the selection as disabled. 1.4942 + selCon->SetDisplaySelection(nsISelectionController::SELECTION_DISABLED); 1.4943 + } else { 1.4944 + // Otherwise, mark selection as normal because outside of a 1.4945 + // contenteditable element should be selected with normal selection 1.4946 + // color after here. 1.4947 + selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON); 1.4948 + } 1.4949 + } else if (IsFormWidget() || IsPasswordEditor() || 1.4950 + IsReadonly() || IsDisabled() || IsInputFiltered()) { 1.4951 + // In <input> or <textarea>, the independent selection should be hidden 1.4952 + // while this editor doesn't have focus. 1.4953 + selCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN); 1.4954 + } else { 1.4955 + // Otherwise, although we're not sure how this case happens, the 1.4956 + // independent selection should be marked as disabled. 1.4957 + selCon->SetDisplaySelection(nsISelectionController::SELECTION_DISABLED); 1.4958 + } 1.4959 + 1.4960 + selCon->RepaintSelection(nsISelectionController::SELECTION_NORMAL); 1.4961 +} 1.4962 + 1.4963 +dom::Element * 1.4964 +nsEditor::GetRoot() 1.4965 +{ 1.4966 + if (!mRootElement) 1.4967 + { 1.4968 + nsCOMPtr<nsIDOMElement> root; 1.4969 + 1.4970 + // Let GetRootElement() do the work 1.4971 + GetRootElement(getter_AddRefs(root)); 1.4972 + } 1.4973 + 1.4974 + return mRootElement; 1.4975 +} 1.4976 + 1.4977 +dom::Element* 1.4978 +nsEditor::GetEditorRoot() 1.4979 +{ 1.4980 + return GetRoot(); 1.4981 +} 1.4982 + 1.4983 +Element* 1.4984 +nsEditor::GetExposedRoot() 1.4985 +{ 1.4986 + Element* rootElement = GetRoot(); 1.4987 + 1.4988 + // For plaintext editors, we need to ask the input/textarea element directly. 1.4989 + if (rootElement && rootElement->IsRootOfNativeAnonymousSubtree()) { 1.4990 + rootElement = rootElement->GetParent()->AsElement(); 1.4991 + } 1.4992 + 1.4993 + return rootElement; 1.4994 +} 1.4995 + 1.4996 +nsresult 1.4997 +nsEditor::DetermineCurrentDirection() 1.4998 +{ 1.4999 + // Get the current root direction from its frame 1.5000 + nsIContent* rootElement = GetExposedRoot(); 1.5001 + 1.5002 + // If we don't have an explicit direction, determine our direction 1.5003 + // from the content's direction 1.5004 + if (!(mFlags & (nsIPlaintextEditor::eEditorLeftToRight | 1.5005 + nsIPlaintextEditor::eEditorRightToLeft))) { 1.5006 + 1.5007 + nsIFrame* frame = rootElement->GetPrimaryFrame(); 1.5008 + NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE); 1.5009 + 1.5010 + // Set the flag here, to enable us to use the same code path below. 1.5011 + // It will be flipped before returning from the function. 1.5012 + if (frame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { 1.5013 + mFlags |= nsIPlaintextEditor::eEditorRightToLeft; 1.5014 + } else { 1.5015 + mFlags |= nsIPlaintextEditor::eEditorLeftToRight; 1.5016 + } 1.5017 + } 1.5018 + 1.5019 + return NS_OK; 1.5020 +} 1.5021 + 1.5022 +NS_IMETHODIMP 1.5023 +nsEditor::SwitchTextDirection() 1.5024 +{ 1.5025 + // Get the current root direction from its frame 1.5026 + nsIContent* rootElement = GetExposedRoot(); 1.5027 + 1.5028 + nsresult rv = DetermineCurrentDirection(); 1.5029 + NS_ENSURE_SUCCESS(rv, rv); 1.5030 + 1.5031 + // Apply the opposite direction 1.5032 + if (mFlags & nsIPlaintextEditor::eEditorRightToLeft) { 1.5033 + NS_ASSERTION(!(mFlags & nsIPlaintextEditor::eEditorLeftToRight), 1.5034 + "Unexpected mutually exclusive flag"); 1.5035 + mFlags &= ~nsIPlaintextEditor::eEditorRightToLeft; 1.5036 + mFlags |= nsIPlaintextEditor::eEditorLeftToRight; 1.5037 + rv = rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, NS_LITERAL_STRING("ltr"), true); 1.5038 + } else if (mFlags & nsIPlaintextEditor::eEditorLeftToRight) { 1.5039 + NS_ASSERTION(!(mFlags & nsIPlaintextEditor::eEditorRightToLeft), 1.5040 + "Unexpected mutually exclusive flag"); 1.5041 + mFlags |= nsIPlaintextEditor::eEditorRightToLeft; 1.5042 + mFlags &= ~nsIPlaintextEditor::eEditorLeftToRight; 1.5043 + rv = rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, NS_LITERAL_STRING("rtl"), true); 1.5044 + } 1.5045 + 1.5046 + if (NS_SUCCEEDED(rv)) { 1.5047 + FireInputEvent(); 1.5048 + } 1.5049 + 1.5050 + return rv; 1.5051 +} 1.5052 + 1.5053 +void 1.5054 +nsEditor::SwitchTextDirectionTo(uint32_t aDirection) 1.5055 +{ 1.5056 + // Get the current root direction from its frame 1.5057 + nsIContent* rootElement = GetExposedRoot(); 1.5058 + 1.5059 + nsresult rv = DetermineCurrentDirection(); 1.5060 + NS_ENSURE_SUCCESS_VOID(rv); 1.5061 + 1.5062 + // Apply the requested direction 1.5063 + if (aDirection == nsIPlaintextEditor::eEditorLeftToRight && 1.5064 + (mFlags & nsIPlaintextEditor::eEditorRightToLeft)) { 1.5065 + NS_ASSERTION(!(mFlags & nsIPlaintextEditor::eEditorLeftToRight), 1.5066 + "Unexpected mutually exclusive flag"); 1.5067 + mFlags &= ~nsIPlaintextEditor::eEditorRightToLeft; 1.5068 + mFlags |= nsIPlaintextEditor::eEditorLeftToRight; 1.5069 + rv = rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, NS_LITERAL_STRING("ltr"), true); 1.5070 + } else if (aDirection == nsIPlaintextEditor::eEditorRightToLeft && 1.5071 + (mFlags & nsIPlaintextEditor::eEditorLeftToRight)) { 1.5072 + NS_ASSERTION(!(mFlags & nsIPlaintextEditor::eEditorRightToLeft), 1.5073 + "Unexpected mutually exclusive flag"); 1.5074 + mFlags |= nsIPlaintextEditor::eEditorRightToLeft; 1.5075 + mFlags &= ~nsIPlaintextEditor::eEditorLeftToRight; 1.5076 + rv = rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, NS_LITERAL_STRING("rtl"), true); 1.5077 + } 1.5078 + 1.5079 + if (NS_SUCCEEDED(rv)) { 1.5080 + FireInputEvent(); 1.5081 + } 1.5082 +} 1.5083 + 1.5084 +#if DEBUG_JOE 1.5085 +void 1.5086 +nsEditor::DumpNode(nsIDOMNode *aNode, int32_t indent) 1.5087 +{ 1.5088 + int32_t i; 1.5089 + for (i=0; i<indent; i++) 1.5090 + printf(" "); 1.5091 + 1.5092 + nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode); 1.5093 + nsCOMPtr<nsIDOMDocumentFragment> docfrag = do_QueryInterface(aNode); 1.5094 + 1.5095 + if (element || docfrag) 1.5096 + { 1.5097 + if (element) 1.5098 + { 1.5099 + nsAutoString tag; 1.5100 + element->GetTagName(tag); 1.5101 + printf("<%s>\n", NS_LossyConvertUTF16toASCII(tag).get()); 1.5102 + } 1.5103 + else 1.5104 + { 1.5105 + printf("<document fragment>\n"); 1.5106 + } 1.5107 + nsCOMPtr<nsIDOMNodeList> childList; 1.5108 + aNode->GetChildNodes(getter_AddRefs(childList)); 1.5109 + NS_ENSURE_TRUE(childList, NS_ERROR_NULL_POINTER); 1.5110 + uint32_t numChildren; 1.5111 + childList->GetLength(&numChildren); 1.5112 + nsCOMPtr<nsIDOMNode> child, tmp; 1.5113 + aNode->GetFirstChild(getter_AddRefs(child)); 1.5114 + for (i=0; i<numChildren; i++) 1.5115 + { 1.5116 + DumpNode(child, indent+1); 1.5117 + child->GetNextSibling(getter_AddRefs(tmp)); 1.5118 + child = tmp; 1.5119 + } 1.5120 + } 1.5121 + else if (IsTextNode(aNode)) 1.5122 + { 1.5123 + nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(aNode); 1.5124 + nsAutoString str; 1.5125 + textNode->GetData(str); 1.5126 + nsAutoCString cstr; 1.5127 + LossyCopyUTF16toASCII(str, cstr); 1.5128 + cstr.ReplaceChar('\n', ' '); 1.5129 + printf("<textnode> %s\n", cstr.get()); 1.5130 + } 1.5131 +} 1.5132 +#endif 1.5133 + 1.5134 +bool 1.5135 +nsEditor::IsModifiableNode(nsIDOMNode *aNode) 1.5136 +{ 1.5137 + return true; 1.5138 +} 1.5139 + 1.5140 +bool 1.5141 +nsEditor::IsModifiableNode(nsINode *aNode) 1.5142 +{ 1.5143 + return true; 1.5144 +} 1.5145 + 1.5146 +already_AddRefed<nsIContent> 1.5147 +nsEditor::GetFocusedContent() 1.5148 +{ 1.5149 + nsCOMPtr<nsIDOMEventTarget> piTarget = GetDOMEventTarget(); 1.5150 + if (!piTarget) { 1.5151 + return nullptr; 1.5152 + } 1.5153 + 1.5154 + nsFocusManager* fm = nsFocusManager::GetFocusManager(); 1.5155 + NS_ENSURE_TRUE(fm, nullptr); 1.5156 + 1.5157 + nsCOMPtr<nsIContent> content = fm->GetFocusedContent(); 1.5158 + return SameCOMIdentity(content, piTarget) ? content.forget() : nullptr; 1.5159 +} 1.5160 + 1.5161 +already_AddRefed<nsIContent> 1.5162 +nsEditor::GetFocusedContentForIME() 1.5163 +{ 1.5164 + return GetFocusedContent(); 1.5165 +} 1.5166 + 1.5167 +bool 1.5168 +nsEditor::IsActiveInDOMWindow() 1.5169 +{ 1.5170 + nsCOMPtr<nsIDOMEventTarget> piTarget = GetDOMEventTarget(); 1.5171 + if (!piTarget) { 1.5172 + return false; 1.5173 + } 1.5174 + 1.5175 + nsFocusManager* fm = nsFocusManager::GetFocusManager(); 1.5176 + NS_ENSURE_TRUE(fm, false); 1.5177 + 1.5178 + nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak); 1.5179 + nsPIDOMWindow* ourWindow = doc->GetWindow(); 1.5180 + nsCOMPtr<nsPIDOMWindow> win; 1.5181 + nsIContent* content = 1.5182 + nsFocusManager::GetFocusedDescendant(ourWindow, false, 1.5183 + getter_AddRefs(win)); 1.5184 + return SameCOMIdentity(content, piTarget); 1.5185 +} 1.5186 + 1.5187 +bool 1.5188 +nsEditor::IsAcceptableInputEvent(nsIDOMEvent* aEvent) 1.5189 +{ 1.5190 + // If the event is trusted, the event should always cause input. 1.5191 + NS_ENSURE_TRUE(aEvent, false); 1.5192 + 1.5193 + // If this is mouse event but this editor doesn't have focus, we shouldn't 1.5194 + // handle it. 1.5195 + nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aEvent); 1.5196 + if (mouseEvent) { 1.5197 + nsCOMPtr<nsIContent> focusedContent = GetFocusedContent(); 1.5198 + if (!focusedContent) { 1.5199 + return false; 1.5200 + } 1.5201 + } else { 1.5202 + nsAutoString eventType; 1.5203 + aEvent->GetType(eventType); 1.5204 + // If composition event or text event isn't dispatched via widget, 1.5205 + // we need to ignore them since they cannot be managed by TextComposition. 1.5206 + // E.g., the event was created by chrome JS. 1.5207 + // Note that if we allow to handle such events, editor may be confused by 1.5208 + // strange event order. 1.5209 + if (eventType.EqualsLiteral("text") || 1.5210 + eventType.EqualsLiteral("compositionstart") || 1.5211 + eventType.EqualsLiteral("compositionend")) { 1.5212 + WidgetGUIEvent* widgetGUIEvent = 1.5213 + aEvent->GetInternalNSEvent()->AsGUIEvent(); 1.5214 + if (!widgetGUIEvent || !widgetGUIEvent->widget) { 1.5215 + return false; 1.5216 + } 1.5217 + } 1.5218 + } 1.5219 + 1.5220 + bool isTrusted; 1.5221 + nsresult rv = aEvent->GetIsTrusted(&isTrusted); 1.5222 + NS_ENSURE_SUCCESS(rv, false); 1.5223 + if (isTrusted) { 1.5224 + return true; 1.5225 + } 1.5226 + 1.5227 + // Ignore untrusted mouse event. 1.5228 + // XXX Why are we handling other untrusted input events? 1.5229 + if (mouseEvent) { 1.5230 + return false; 1.5231 + } 1.5232 + 1.5233 + // Otherwise, we shouldn't handle any input events when we're not an active 1.5234 + // element of the DOM window. 1.5235 + return IsActiveInDOMWindow(); 1.5236 +} 1.5237 + 1.5238 +void 1.5239 +nsEditor::OnFocus(nsIDOMEventTarget* aFocusEventTarget) 1.5240 +{ 1.5241 + InitializeSelection(aFocusEventTarget); 1.5242 + if (mInlineSpellChecker) { 1.5243 + mInlineSpellChecker->UpdateCurrentDictionary(); 1.5244 + } 1.5245 +} 1.5246 + 1.5247 +NS_IMETHODIMP 1.5248 +nsEditor::GetSuppressDispatchingInputEvent(bool *aSuppressed) 1.5249 +{ 1.5250 + NS_ENSURE_ARG_POINTER(aSuppressed); 1.5251 + *aSuppressed = !mDispatchInputEvent; 1.5252 + return NS_OK; 1.5253 +} 1.5254 + 1.5255 +NS_IMETHODIMP 1.5256 +nsEditor::SetSuppressDispatchingInputEvent(bool aSuppress) 1.5257 +{ 1.5258 + mDispatchInputEvent = !aSuppress; 1.5259 + return NS_OK; 1.5260 +}