1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Pierre Phaneuf <pp@ludusdesign.com>
24 : * Daniel Glazman <glazman@netscape.com>
25 : * Masayuki Nakano <masayuki@d-toybox.com>
26 : * Mats Palmgren <matspal@gmail.com>
27 : * Jesper Kristensen <mail@jesperkristensen.dk>
28 : *
29 : * Alternatively, the contents of this file may be used under the terms of
30 : * either of the GNU General Public License Version 2 or later (the "GPL"),
31 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 : * in which case the provisions of the GPL or the LGPL are applicable instead
33 : * of those above. If you wish to allow use of your version of this file only
34 : * under the terms of either the GPL or the LGPL, and not to allow others to
35 : * use your version of this file under the terms of the MPL, indicate your
36 : * decision by deleting the provisions above and replace them with the notice
37 : * and other provisions required by the GPL or the LGPL. If you do not delete
38 : * the provisions above, a recipient may use your version of this file under
39 : * the terms of any one of the MPL, the GPL or the LGPL.
40 : *
41 : * ***** END LICENSE BLOCK ***** */
42 :
43 : #include "pratom.h"
44 : #include "nsIDOMDocument.h"
45 : #include "nsIDOMHTMLElement.h"
46 : #include "nsIDOMNSEvent.h"
47 : #include "nsIMEStateManager.h"
48 : #include "nsFocusManager.h"
49 : #include "nsUnicharUtils.h"
50 : #include "nsReadableUtils.h"
51 : #include "nsIObserverService.h"
52 : #include "mozilla/Services.h"
53 : #include "mozISpellCheckingEngine.h"
54 : #include "nsIEditorSpellCheck.h"
55 : #include "mozInlineSpellChecker.h"
56 :
57 : #include "nsIDOMText.h"
58 : #include "nsIDOMElement.h"
59 : #include "nsIDOMAttr.h"
60 : #include "nsIDOMNode.h"
61 : #include "nsIDOMDocumentFragment.h"
62 : #include "nsIDOMNamedNodeMap.h"
63 : #include "nsIDOMNodeList.h"
64 : #include "nsIDOMRange.h"
65 : #include "nsIDOMHTMLBRElement.h"
66 : #include "nsIDocument.h"
67 : #include "nsITransactionManager.h"
68 : #include "nsIAbsorbingTransaction.h"
69 : #include "nsIPresShell.h"
70 : #include "nsISelection.h"
71 : #include "nsISelectionPrivate.h"
72 : #include "nsISelectionController.h"
73 : #include "nsIEnumerator.h"
74 : #include "nsEditProperty.h"
75 : #include "nsIAtom.h"
76 : #include "nsCaret.h"
77 : #include "nsIWidget.h"
78 : #include "nsIPlaintextEditor.h"
79 : #include "nsIPrivateDOMEvent.h"
80 : #include "nsGUIEvent.h"
81 :
82 : #include "nsIFrame.h" // Needed by IME code
83 :
84 : #include "nsCSSStyleSheet.h"
85 :
86 : #include "nsIContent.h"
87 : #include "nsDOMString.h"
88 : #include "nsServiceManagerUtils.h"
89 :
90 : // transactions the editor knows how to build
91 : #include "EditAggregateTxn.h"
92 : #include "PlaceholderTxn.h"
93 : #include "ChangeAttributeTxn.h"
94 : #include "CreateElementTxn.h"
95 : #include "InsertElementTxn.h"
96 : #include "DeleteElementTxn.h"
97 : #include "InsertTextTxn.h"
98 : #include "DeleteTextTxn.h"
99 : #include "DeleteRangeTxn.h"
100 : #include "SplitElementTxn.h"
101 : #include "JoinElementTxn.h"
102 : #include "nsStyleSheetTxns.h"
103 : #include "IMETextTxn.h"
104 : #include "nsString.h"
105 :
106 : #include "nsEditor.h"
107 : #include "nsEditorUtils.h"
108 : #include "nsEditorEventListener.h"
109 : #include "nsISelectionDisplay.h"
110 : #include "nsIInlineSpellChecker.h"
111 : #include "nsINameSpaceManager.h"
112 : #include "nsIHTMLDocument.h"
113 : #include "nsIParserService.h"
114 :
115 : #include "nsITransferable.h"
116 : #include "nsComputedDOMStyle.h"
117 : #include "nsTextEditUtils.h"
118 : #include "nsComputedDOMStyle.h"
119 :
120 : #include "mozilla/FunctionTimer.h"
121 : #include "mozilla/Preferences.h"
122 : #include "mozilla/dom/Element.h"
123 : #include "nsContentUtils.h"
124 : #include "nsCCUncollectableMarker.h"
125 :
126 : #define NS_ERROR_EDITOR_NO_SELECTION NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_EDITOR,1)
127 : #define NS_ERROR_EDITOR_NO_TEXTNODE NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_EDITOR,2)
128 :
129 : #ifdef NS_DEBUG_EDITOR
130 : static bool gNoisy = false;
131 : #endif
132 :
133 : #ifdef DEBUG
134 : #include "nsIDOMHTMLDocument.h"
135 : #endif
136 :
137 : using namespace mozilla;
138 : using namespace mozilla::widget;
139 :
140 : // Defined in nsEditorRegistration.cpp
141 : extern nsIParserService *sParserService;
142 :
143 : //---------------------------------------------------------------------------
144 : //
145 : // nsEditor: base editor class implementation
146 : //
147 : //---------------------------------------------------------------------------
148 :
149 0 : nsEditor::nsEditor()
150 : : mModCount(0)
151 : , mFlags(0)
152 : , mUpdateCount(0)
153 : , mSpellcheckCheckboxState(eTriUnset)
154 : , mPlaceHolderTxn(nsnull)
155 : , mPlaceHolderName(nsnull)
156 : , mPlaceHolderBatch(0)
157 : , mSelState(nsnull)
158 : , mSavedSel()
159 : , mRangeUpdater()
160 : , mAction(nsnull)
161 : , mDirection(eNone)
162 : , mIMETextNode(nsnull)
163 : , mIMETextOffset(0)
164 : , mIMEBufferLength(0)
165 : , mInIMEMode(false)
166 : , mIsIMEComposing(false)
167 : , mShouldTxnSetSelection(true)
168 : , mDidPreDestroy(false)
169 : , mDidPostCreate(false)
170 : , mDocDirtyState(-1)
171 : , mDocWeak(nsnull)
172 : , mPhonetic(nsnull)
173 0 : , mLastKeypressEventWasTrusted(eTriUnset)
174 : {
175 : //initialize member variables here
176 0 : }
177 :
178 0 : nsEditor::~nsEditor()
179 : {
180 0 : NS_ASSERTION(!mDocWeak || mDidPreDestroy, "Why PreDestroy hasn't been called?");
181 :
182 0 : mTxnMgr = nsnull;
183 :
184 0 : delete mPhonetic;
185 0 : }
186 :
187 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsEditor)
188 :
189 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsEditor)
190 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRootElement)
191 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mInlineSpellChecker)
192 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mTxnMgr)
193 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mIMETextRangeList)
194 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mIMETextNode)
195 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mActionListeners)
196 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mEditorObservers)
197 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mDocStateListeners)
198 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mEventTarget)
199 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mEventListener)
200 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
201 :
202 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsEditor)
203 : nsIDocument* currentDoc =
204 0 : tmp->mRootElement ? tmp->mRootElement->GetCurrentDoc() : nsnull;
205 0 : if (currentDoc &&
206 0 : nsCCUncollectableMarker::InGeneration(cb, currentDoc->GetMarkedCCGeneration())) {
207 0 : return NS_SUCCESS_INTERRUPTED_TRAVERSE;
208 : }
209 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mRootElement)
210 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mInlineSpellChecker)
211 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mTxnMgr)
212 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mIMETextRangeList)
213 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mIMETextNode)
214 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mActionListeners)
215 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mEditorObservers)
216 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mDocStateListeners)
217 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mEventTarget)
218 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mEventListener)
219 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
220 :
221 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsEditor)
222 0 : NS_INTERFACE_MAP_ENTRY(nsIPhonetic)
223 0 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
224 0 : NS_INTERFACE_MAP_ENTRY(nsIEditorIMESupport)
225 0 : NS_INTERFACE_MAP_ENTRY(nsIEditor)
226 0 : NS_INTERFACE_MAP_ENTRY(nsIObserver)
227 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditor)
228 0 : NS_INTERFACE_MAP_END
229 :
230 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsEditor)
231 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsEditor)
232 :
233 :
234 : NS_IMETHODIMP
235 0 : nsEditor::Init(nsIDOMDocument *aDoc, nsIContent *aRoot, nsISelectionController *aSelCon, PRUint32 aFlags)
236 : {
237 0 : NS_PRECONDITION(aDoc, "bad arg");
238 0 : if (!aDoc)
239 0 : return NS_ERROR_NULL_POINTER;
240 :
241 : // First only set flags, but other stuff shouldn't be initialized now.
242 : // Don't move this call after initializing mDocWeak.
243 : // SetFlags() can check whether it's called during initialization or not by
244 : // them. Note that SetFlags() will be called by PostCreate().
245 : #ifdef DEBUG
246 : nsresult rv =
247 : #endif
248 0 : SetFlags(aFlags);
249 0 : NS_ASSERTION(NS_SUCCEEDED(rv), "SetFlags() failed");
250 :
251 0 : mDocWeak = do_GetWeakReference(aDoc); // weak reference to doc
252 : // HTML editors currently don't have their own selection controller,
253 : // so they'll pass null as aSelCon, and we'll get the selection controller
254 : // off of the presshell.
255 0 : nsCOMPtr<nsISelectionController> selCon;
256 0 : if (aSelCon) {
257 0 : mSelConWeak = do_GetWeakReference(aSelCon); // weak reference to selectioncontroller
258 0 : selCon = aSelCon;
259 : } else {
260 0 : nsCOMPtr<nsIPresShell> presShell = GetPresShell();
261 0 : selCon = do_QueryInterface(presShell);
262 : }
263 0 : NS_ASSERTION(selCon, "Selection controller should be available at this point");
264 :
265 : //set up root element if we are passed one.
266 0 : if (aRoot)
267 0 : mRootElement = do_QueryInterface(aRoot);
268 :
269 0 : mUpdateCount=0;
270 :
271 : /* initialize IME stuff */
272 0 : mIMETextNode = nsnull;
273 0 : mIMETextOffset = 0;
274 0 : mIMEBufferLength = 0;
275 :
276 : /* Show the caret */
277 0 : selCon->SetCaretReadOnly(false);
278 0 : selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
279 :
280 0 : selCon->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL);//we want to see all the selection reflected to user
281 :
282 0 : NS_POSTCONDITION(mDocWeak, "bad state");
283 :
284 : // Make sure that the editor will be destroyed properly
285 0 : mDidPreDestroy = false;
286 : // Make sure that the ediotr will be created properly
287 0 : mDidPostCreate = false;
288 :
289 0 : return NS_OK;
290 : }
291 :
292 :
293 : NS_IMETHODIMP
294 0 : nsEditor::PostCreate()
295 : {
296 : // Synchronize some stuff for the flags. SetFlags() will initialize
297 : // something by the flag difference. This is first time of that, so, all
298 : // initializations must be run. For such reason, we need to invert mFlags
299 : // value first.
300 0 : mFlags = ~mFlags;
301 0 : nsresult rv = SetFlags(~mFlags);
302 0 : NS_ENSURE_SUCCESS(rv, rv);
303 :
304 : // These operations only need to happen on the first PostCreate call
305 0 : if (!mDidPostCreate) {
306 0 : mDidPostCreate = true;
307 :
308 : // Set up listeners
309 0 : CreateEventListeners();
310 0 : rv = InstallEventListeners();
311 0 : NS_ENSURE_SUCCESS(rv, rv);
312 :
313 : // nuke the modification count, so the doc appears unmodified
314 : // do this before we notify listeners
315 0 : ResetModificationCount();
316 :
317 : // update the UI with our state
318 0 : NotifyDocumentListeners(eDocumentCreated);
319 0 : NotifyDocumentListeners(eDocumentStateChanged);
320 :
321 0 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
322 0 : if (obs) {
323 0 : obs->AddObserver(this,
324 : SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION,
325 0 : false);
326 : }
327 : }
328 :
329 : // update nsTextStateManager and caret if we have focus
330 0 : nsCOMPtr<nsIContent> focusedContent = GetFocusedContent();
331 0 : if (focusedContent) {
332 0 : nsCOMPtr<nsIPresShell> ps = GetPresShell();
333 0 : NS_ASSERTION(ps, "no pres shell even though we have focus");
334 0 : NS_ENSURE_TRUE(ps, NS_ERROR_UNEXPECTED);
335 0 : nsPresContext* pc = ps->GetPresContext();
336 :
337 0 : nsIMEStateManager::OnTextStateBlur(pc, nsnull);
338 0 : nsIMEStateManager::OnTextStateFocus(pc, focusedContent);
339 :
340 0 : nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(focusedContent);
341 0 : if (target) {
342 0 : InitializeSelection(target);
343 : }
344 :
345 : // If the text control gets reframed during focus, Focus() would not be
346 : // called, so take a chance here to see if we need to spell check the text
347 : // control.
348 : nsEditorEventListener* listener =
349 0 : reinterpret_cast<nsEditorEventListener*> (mEventListener.get());
350 0 : listener->SpellCheckIfNeeded();
351 : }
352 0 : return NS_OK;
353 : }
354 :
355 : /* virtual */
356 : void
357 0 : nsEditor::CreateEventListeners()
358 : {
359 : // Don't create the handler twice
360 0 : if (!mEventListener) {
361 0 : mEventListener = new nsEditorEventListener();
362 : }
363 0 : }
364 :
365 : nsresult
366 0 : nsEditor::InstallEventListeners()
367 : {
368 0 : NS_ENSURE_TRUE(mDocWeak && mEventListener,
369 : NS_ERROR_NOT_INITIALIZED);
370 :
371 : // Initialize the event target.
372 0 : nsCOMPtr<nsIContent> rootContent = do_QueryInterface(GetRoot());
373 0 : NS_ENSURE_TRUE(rootContent, NS_ERROR_NOT_AVAILABLE);
374 0 : mEventTarget = do_QueryInterface(rootContent->GetParent());
375 0 : NS_ENSURE_TRUE(mEventTarget, NS_ERROR_NOT_AVAILABLE);
376 :
377 : nsEditorEventListener* listener =
378 0 : reinterpret_cast<nsEditorEventListener*>(mEventListener.get());
379 0 : return listener->Connect(this);
380 : }
381 :
382 : void
383 0 : nsEditor::RemoveEventListeners()
384 : {
385 0 : if (!mDocWeak || !mEventListener) {
386 0 : return;
387 : }
388 0 : reinterpret_cast<nsEditorEventListener*>(mEventListener.get())->Disconnect();
389 0 : mEventTarget = nsnull;
390 : }
391 :
392 : bool
393 0 : nsEditor::GetDesiredSpellCheckState()
394 : {
395 : // Check user override on this element
396 0 : if (mSpellcheckCheckboxState != eTriUnset) {
397 0 : return (mSpellcheckCheckboxState == eTriTrue);
398 : }
399 :
400 : // Check user preferences
401 0 : PRInt32 spellcheckLevel = Preferences::GetInt("layout.spellcheckDefault", 1);
402 :
403 0 : if (spellcheckLevel == 0) {
404 0 : return false; // Spellchecking forced off globally
405 : }
406 :
407 0 : if (!CanEnableSpellCheck()) {
408 0 : return false;
409 : }
410 :
411 0 : nsCOMPtr<nsIPresShell> presShell = GetPresShell();
412 0 : if (presShell) {
413 0 : nsPresContext* context = presShell->GetPresContext();
414 0 : if (context && !context->IsDynamic()) {
415 0 : return false;
416 : }
417 : }
418 :
419 : // Check DOM state
420 0 : nsCOMPtr<nsIContent> content = GetRoot();
421 0 : if (!content) {
422 0 : return false;
423 : }
424 :
425 0 : if (content->IsRootOfNativeAnonymousSubtree()) {
426 0 : content = content->GetParent();
427 : }
428 :
429 0 : nsCOMPtr<nsIDOMHTMLElement> element = do_QueryInterface(content);
430 0 : if (!element) {
431 0 : return false;
432 : }
433 :
434 : bool enable;
435 0 : element->GetSpellcheck(&enable);
436 :
437 0 : return enable;
438 : }
439 :
440 : NS_IMETHODIMP
441 0 : nsEditor::PreDestroy(bool aDestroyingFrames)
442 : {
443 0 : if (mDidPreDestroy)
444 0 : return NS_OK;
445 :
446 0 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
447 0 : if (obs) {
448 0 : obs->RemoveObserver(this,
449 0 : SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION);
450 : }
451 :
452 : // Let spellchecker clean up its observers etc. It is important not to
453 : // actually free the spellchecker here, since the spellchecker could have
454 : // caused flush notifications, which could have gotten here if a textbox
455 : // is being removed. Setting the spellchecker to NULL could free the
456 : // object that is still in use! It will be freed when the editor is
457 : // destroyed.
458 0 : if (mInlineSpellChecker)
459 0 : mInlineSpellChecker->Cleanup(aDestroyingFrames);
460 :
461 : // tell our listeners that the doc is going away
462 0 : NotifyDocumentListeners(eDocumentToBeDestroyed);
463 :
464 : // Unregister event listeners
465 0 : RemoveEventListeners();
466 0 : mActionListeners.Clear();
467 0 : mEditorObservers.Clear();
468 0 : mDocStateListeners.Clear();
469 0 : mInlineSpellChecker = nsnull;
470 0 : mSpellcheckCheckboxState = eTriUnset;
471 0 : mRootElement = nsnull;
472 :
473 0 : mDidPreDestroy = true;
474 0 : return NS_OK;
475 : }
476 :
477 : NS_IMETHODIMP
478 0 : nsEditor::GetFlags(PRUint32 *aFlags)
479 : {
480 0 : *aFlags = mFlags;
481 0 : return NS_OK;
482 : }
483 :
484 : NS_IMETHODIMP
485 0 : nsEditor::SetFlags(PRUint32 aFlags)
486 : {
487 0 : if (mFlags == aFlags) {
488 0 : return NS_OK;
489 : }
490 :
491 0 : bool spellcheckerWasEnabled = CanEnableSpellCheck();
492 0 : mFlags = aFlags;
493 :
494 0 : if (!mDocWeak) {
495 : // If we're initializing, we shouldn't do anything now.
496 : // SetFlags() will be called by PostCreate(),
497 : // we should synchronize some stuff for the flags at that time.
498 0 : return NS_OK;
499 : }
500 :
501 : // The flag change may cause the spellchecker state change
502 0 : if (CanEnableSpellCheck() != spellcheckerWasEnabled) {
503 0 : nsresult rv = SyncRealTimeSpell();
504 0 : NS_ENSURE_SUCCESS(rv, rv);
505 : }
506 :
507 : // Might be changing editable state, so, we need to reset current IME state
508 : // if we're focused and the flag change causes IME state change.
509 0 : nsCOMPtr<nsIContent> focusedContent = GetFocusedContent();
510 0 : if (focusedContent) {
511 0 : IMEState newState;
512 0 : nsresult rv = GetPreferredIMEState(&newState);
513 0 : if (NS_SUCCEEDED(rv)) {
514 : // NOTE: When the enabled state isn't going to be modified, this method
515 : // is going to do nothing.
516 0 : nsIMEStateManager::UpdateIMEState(newState, focusedContent);
517 : }
518 : }
519 :
520 0 : return NS_OK;
521 : }
522 :
523 : NS_IMETHODIMP
524 0 : nsEditor::GetIsSelectionEditable(bool *aIsSelectionEditable)
525 : {
526 0 : NS_ENSURE_ARG_POINTER(aIsSelectionEditable);
527 :
528 : // get current selection
529 0 : nsCOMPtr<nsISelection> selection;
530 0 : nsresult res = GetSelection(getter_AddRefs(selection));
531 0 : NS_ENSURE_SUCCESS(res, res);
532 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
533 :
534 : // XXX we just check that the anchor node is editable at the moment
535 : // we should check that all nodes in the selection are editable
536 0 : nsCOMPtr<nsIDOMNode> anchorNode;
537 0 : selection->GetAnchorNode(getter_AddRefs(anchorNode));
538 0 : *aIsSelectionEditable = anchorNode && IsEditable(anchorNode);
539 :
540 0 : return NS_OK;
541 : }
542 :
543 : NS_IMETHODIMP
544 0 : nsEditor::GetIsDocumentEditable(bool *aIsDocumentEditable)
545 : {
546 0 : NS_ENSURE_ARG_POINTER(aIsDocumentEditable);
547 0 : nsCOMPtr<nsIDOMDocument> doc;
548 0 : GetDocument(getter_AddRefs(doc));
549 0 : *aIsDocumentEditable = doc ? true : false;
550 :
551 0 : return NS_OK;
552 : }
553 :
554 : NS_IMETHODIMP
555 0 : nsEditor::GetDocument(nsIDOMDocument **aDoc)
556 : {
557 0 : NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER);
558 0 : *aDoc = nsnull; // init out param
559 0 : NS_PRECONDITION(mDocWeak, "bad state, mDocWeak weak pointer not initialized");
560 0 : nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(mDocWeak);
561 0 : NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
562 0 : NS_ADDREF(*aDoc = doc);
563 0 : return NS_OK;
564 : }
565 :
566 : already_AddRefed<nsIPresShell>
567 0 : nsEditor::GetPresShell()
568 : {
569 0 : NS_PRECONDITION(mDocWeak, "bad state, null mDocWeak");
570 0 : nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
571 0 : NS_ENSURE_TRUE(doc, NULL);
572 0 : nsCOMPtr<nsIPresShell> ps = doc->GetShell();
573 0 : return ps.forget();
574 : }
575 :
576 :
577 : /* attribute string contentsMIMEType; */
578 : NS_IMETHODIMP
579 0 : nsEditor::GetContentsMIMEType(char * *aContentsMIMEType)
580 : {
581 0 : NS_ENSURE_ARG_POINTER(aContentsMIMEType);
582 0 : *aContentsMIMEType = ToNewCString(mContentMIMEType);
583 0 : return NS_OK;
584 : }
585 :
586 : NS_IMETHODIMP
587 0 : nsEditor::SetContentsMIMEType(const char * aContentsMIMEType)
588 : {
589 0 : mContentMIMEType.Assign(aContentsMIMEType ? aContentsMIMEType : "");
590 0 : return NS_OK;
591 : }
592 :
593 : NS_IMETHODIMP
594 0 : nsEditor::GetSelectionController(nsISelectionController **aSel)
595 : {
596 0 : NS_ENSURE_TRUE(aSel, NS_ERROR_NULL_POINTER);
597 0 : *aSel = nsnull; // init out param
598 0 : nsCOMPtr<nsISelectionController> selCon;
599 0 : if (mSelConWeak) {
600 0 : selCon = do_QueryReferent(mSelConWeak);
601 : } else {
602 0 : nsCOMPtr<nsIPresShell> presShell = GetPresShell();
603 0 : selCon = do_QueryInterface(presShell);
604 : }
605 0 : NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
606 0 : NS_ADDREF(*aSel = selCon);
607 0 : return NS_OK;
608 : }
609 :
610 :
611 : NS_IMETHODIMP
612 0 : nsEditor::DeleteSelection(EDirection aAction)
613 : {
614 0 : return DeleteSelectionImpl(aAction);
615 : }
616 :
617 :
618 :
619 : NS_IMETHODIMP
620 0 : nsEditor::GetSelection(nsISelection **aSelection)
621 : {
622 0 : NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
623 0 : *aSelection = nsnull;
624 0 : nsCOMPtr<nsISelectionController> selcon;
625 0 : GetSelectionController(getter_AddRefs(selcon));
626 0 : NS_ENSURE_TRUE(selcon, NS_ERROR_NOT_INITIALIZED);
627 0 : return selcon->GetSelection(nsISelectionController::SELECTION_NORMAL, aSelection); // does an addref
628 : }
629 :
630 : NS_IMETHODIMP
631 0 : nsEditor::DoTransaction(nsITransaction *aTxn)
632 : {
633 : #ifdef NS_DEBUG_EDITOR
634 : if (gNoisy) { printf("Editor::DoTransaction ----------\n"); }
635 : #endif
636 :
637 0 : nsresult result = NS_OK;
638 :
639 0 : if (mPlaceHolderBatch && !mPlaceHolderTxn)
640 : {
641 : // it's pretty darn amazing how many different types of pointers
642 : // this transaction goes through here. I bet this is a record.
643 :
644 : // We start off with an EditTxn since that's what the factory returns.
645 0 : nsRefPtr<EditTxn> editTxn = new PlaceholderTxn();
646 0 : if (!editTxn) { return NS_ERROR_OUT_OF_MEMORY; }
647 :
648 : // Then we QI to an nsIAbsorbingTransaction to get at placeholder functionality
649 0 : nsCOMPtr<nsIAbsorbingTransaction> plcTxn;
650 0 : editTxn->QueryInterface(NS_GET_IID(nsIAbsorbingTransaction), getter_AddRefs(plcTxn));
651 : // have to use line above instead of "plcTxn = do_QueryInterface(editTxn);"
652 : // due to our broken interface model for transactions.
653 :
654 : // save off weak reference to placeholder txn
655 0 : mPlaceHolderTxn = do_GetWeakReference(plcTxn);
656 0 : plcTxn->Init(mPlaceHolderName, mSelState, this);
657 0 : mSelState = nsnull; // placeholder txn took ownership of this pointer
658 :
659 : // finally we QI to an nsITransaction since that's what DoTransaction() expects
660 0 : nsCOMPtr<nsITransaction> theTxn = do_QueryInterface(plcTxn);
661 0 : DoTransaction(theTxn); // we will recurse, but will not hit this case in the nested call
662 :
663 0 : if (mTxnMgr)
664 : {
665 0 : nsCOMPtr<nsITransaction> topTxn;
666 0 : result = mTxnMgr->PeekUndoStack(getter_AddRefs(topTxn));
667 0 : NS_ENSURE_SUCCESS(result, result);
668 0 : if (topTxn)
669 : {
670 0 : plcTxn = do_QueryInterface(topTxn);
671 0 : if (plcTxn)
672 : {
673 : // there is a palceholder transaction on top of the undo stack. It is
674 : // either the one we just created, or an earlier one that we are now merging
675 : // into. From here on out remember this placeholder instead of the one
676 : // we just created.
677 0 : mPlaceHolderTxn = do_GetWeakReference(plcTxn);
678 : }
679 : }
680 : }
681 : }
682 :
683 0 : if (aTxn)
684 : {
685 : // XXX: Why are we doing selection specific batching stuff here?
686 : // XXX: Most entry points into the editor have auto variables that
687 : // XXX: should trigger Begin/EndUpdateViewBatch() calls that will make
688 : // XXX: these selection batch calls no-ops.
689 : // XXX:
690 : // XXX: I suspect that this was placed here to avoid multiple
691 : // XXX: selection changed notifications from happening until after
692 : // XXX: the transaction was done. I suppose that can still happen
693 : // XXX: if an embedding application called DoTransaction() directly
694 : // XXX: to pump its own transactions through the system, but in that
695 : // XXX: case, wouldn't we want to use Begin/EndUpdateViewBatch() or
696 : // XXX: its auto equivalent nsAutoUpdateViewBatch to ensure that
697 : // XXX: selection listeners have access to accurate frame data?
698 : // XXX:
699 : // XXX: Note that if we did add Begin/EndUpdateViewBatch() calls
700 : // XXX: we will need to make sure that they are disabled during
701 : // XXX: the init of the editor for text widgets to avoid layout
702 : // XXX: re-entry during initial reflow. - kin
703 :
704 : // get the selection and start a batch change
705 0 : nsCOMPtr<nsISelection>selection;
706 0 : result = GetSelection(getter_AddRefs(selection));
707 0 : NS_ENSURE_SUCCESS(result, result);
708 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
709 0 : nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(selection));
710 :
711 0 : selPrivate->StartBatchChanges();
712 :
713 0 : if (mTxnMgr) {
714 0 : result = mTxnMgr->DoTransaction(aTxn);
715 : }
716 : else {
717 0 : result = aTxn->DoTransaction();
718 : }
719 0 : if (NS_SUCCEEDED(result)) {
720 0 : result = DoAfterDoTransaction(aTxn);
721 : }
722 :
723 0 : selPrivate->EndBatchChanges(); // no need to check result here, don't lose result of operation
724 : }
725 :
726 0 : NS_ENSURE_SUCCESS(result, result);
727 :
728 0 : return result;
729 : }
730 :
731 :
732 : NS_IMETHODIMP
733 0 : nsEditor::EnableUndo(bool aEnable)
734 : {
735 0 : nsresult result=NS_OK;
736 :
737 0 : if (true==aEnable)
738 : {
739 0 : if (!mTxnMgr)
740 : {
741 0 : mTxnMgr = do_CreateInstance(NS_TRANSACTIONMANAGER_CONTRACTID, &result);
742 0 : if (NS_FAILED(result) || !mTxnMgr) {
743 0 : return NS_ERROR_NOT_AVAILABLE;
744 : }
745 : }
746 0 : mTxnMgr->SetMaxTransactionCount(-1);
747 : }
748 : else
749 : { // disable the transaction manager if it is enabled
750 0 : if (mTxnMgr)
751 : {
752 0 : mTxnMgr->Clear();
753 0 : mTxnMgr->SetMaxTransactionCount(0);
754 : }
755 : }
756 :
757 0 : return NS_OK;
758 : }
759 :
760 : NS_IMETHODIMP
761 0 : nsEditor::GetNumberOfUndoItems(PRInt32* aNumItems)
762 : {
763 0 : *aNumItems = 0;
764 0 : return mTxnMgr ? mTxnMgr->GetNumberOfUndoItems(aNumItems) : NS_OK;
765 : }
766 :
767 : NS_IMETHODIMP
768 0 : nsEditor::GetNumberOfRedoItems(PRInt32* aNumItems)
769 : {
770 0 : *aNumItems = 0;
771 0 : return mTxnMgr ? mTxnMgr->GetNumberOfRedoItems(aNumItems) : NS_OK;
772 : }
773 :
774 : NS_IMETHODIMP
775 0 : nsEditor::GetTransactionManager(nsITransactionManager* *aTxnManager)
776 : {
777 0 : NS_ENSURE_ARG_POINTER(aTxnManager);
778 :
779 0 : *aTxnManager = NULL;
780 0 : NS_ENSURE_TRUE(mTxnMgr, NS_ERROR_FAILURE);
781 :
782 0 : NS_ADDREF(*aTxnManager = mTxnMgr);
783 0 : return NS_OK;
784 : }
785 :
786 : NS_IMETHODIMP
787 0 : nsEditor::SetTransactionManager(nsITransactionManager *aTxnManager)
788 : {
789 0 : NS_ENSURE_TRUE(aTxnManager, NS_ERROR_FAILURE);
790 :
791 0 : mTxnMgr = aTxnManager;
792 0 : return NS_OK;
793 : }
794 :
795 : NS_IMETHODIMP
796 0 : nsEditor::Undo(PRUint32 aCount)
797 : {
798 : #ifdef NS_DEBUG_EDITOR
799 : if (gNoisy) { printf("Editor::Undo ----------\n"); }
800 : #endif
801 :
802 0 : ForceCompositionEnd();
803 :
804 0 : bool hasTxnMgr, hasTransaction = false;
805 0 : CanUndo(&hasTxnMgr, &hasTransaction);
806 0 : NS_ENSURE_TRUE(hasTransaction, NS_OK);
807 :
808 0 : nsAutoRules beginRulesSniffing(this, kOpUndo, nsIEditor::eNone);
809 :
810 0 : if (!mTxnMgr) {
811 0 : return NS_OK;
812 : }
813 :
814 0 : for (PRUint32 i = 0; i < aCount; ++i) {
815 0 : nsresult rv = mTxnMgr->UndoTransaction();
816 0 : NS_ENSURE_SUCCESS(rv, rv);
817 :
818 0 : rv = DoAfterUndoTransaction();
819 0 : NS_ENSURE_SUCCESS(rv, rv);
820 : }
821 :
822 0 : return NS_OK;
823 : }
824 :
825 :
826 0 : NS_IMETHODIMP nsEditor::CanUndo(bool *aIsEnabled, bool *aCanUndo)
827 : {
828 0 : NS_ENSURE_TRUE(aIsEnabled && aCanUndo, NS_ERROR_NULL_POINTER);
829 0 : *aIsEnabled = !!mTxnMgr;
830 0 : if (*aIsEnabled) {
831 0 : PRInt32 numTxns = 0;
832 0 : mTxnMgr->GetNumberOfUndoItems(&numTxns);
833 0 : *aCanUndo = !!numTxns;
834 : } else {
835 0 : *aCanUndo = false;
836 : }
837 0 : return NS_OK;
838 : }
839 :
840 :
841 : NS_IMETHODIMP
842 0 : nsEditor::Redo(PRUint32 aCount)
843 : {
844 : #ifdef NS_DEBUG_EDITOR
845 : if (gNoisy) { printf("Editor::Redo ----------\n"); }
846 : #endif
847 :
848 0 : bool hasTxnMgr, hasTransaction = false;
849 0 : CanRedo(&hasTxnMgr, &hasTransaction);
850 0 : NS_ENSURE_TRUE(hasTransaction, NS_OK);
851 :
852 0 : nsAutoRules beginRulesSniffing(this, kOpRedo, nsIEditor::eNone);
853 :
854 0 : if (!mTxnMgr) {
855 0 : return NS_OK;
856 : }
857 :
858 0 : for (PRUint32 i = 0; i < aCount; ++i) {
859 0 : nsresult rv = mTxnMgr->RedoTransaction();
860 0 : NS_ENSURE_SUCCESS(rv, rv);
861 :
862 0 : rv = DoAfterRedoTransaction();
863 0 : NS_ENSURE_SUCCESS(rv, rv);
864 : }
865 :
866 0 : return NS_OK;
867 : }
868 :
869 :
870 0 : NS_IMETHODIMP nsEditor::CanRedo(bool *aIsEnabled, bool *aCanRedo)
871 : {
872 0 : NS_ENSURE_TRUE(aIsEnabled && aCanRedo, NS_ERROR_NULL_POINTER);
873 :
874 0 : *aIsEnabled = !!mTxnMgr;
875 0 : if (*aIsEnabled) {
876 0 : PRInt32 numTxns = 0;
877 0 : mTxnMgr->GetNumberOfRedoItems(&numTxns);
878 0 : *aCanRedo = !!numTxns;
879 : } else {
880 0 : *aCanRedo = false;
881 : }
882 0 : return NS_OK;
883 : }
884 :
885 :
886 : NS_IMETHODIMP
887 0 : nsEditor::BeginTransaction()
888 : {
889 0 : BeginUpdateViewBatch();
890 :
891 0 : if (mTxnMgr) {
892 0 : mTxnMgr->BeginBatch();
893 : }
894 :
895 0 : return NS_OK;
896 : }
897 :
898 : NS_IMETHODIMP
899 0 : nsEditor::EndTransaction()
900 : {
901 0 : if (mTxnMgr) {
902 0 : mTxnMgr->EndBatch();
903 : }
904 :
905 0 : EndUpdateViewBatch();
906 :
907 0 : return NS_OK;
908 : }
909 :
910 :
911 : // These two routines are similar to the above, but do not use
912 : // the transaction managers batching feature. Instead we use
913 : // a placeholder transaction to wrap up any further transaction
914 : // while the batch is open. The advantage of this is that
915 : // placeholder transactions can later merge, if needed. Merging
916 : // is unavailable between transaction manager batches.
917 :
918 : NS_IMETHODIMP
919 0 : nsEditor::BeginPlaceHolderTransaction(nsIAtom *aName)
920 : {
921 0 : NS_PRECONDITION(mPlaceHolderBatch >= 0, "negative placeholder batch count!");
922 0 : if (!mPlaceHolderBatch)
923 : {
924 : // time to turn on the batch
925 0 : BeginUpdateViewBatch();
926 0 : mPlaceHolderTxn = nsnull;
927 0 : mPlaceHolderName = aName;
928 0 : nsCOMPtr<nsISelection> selection;
929 0 : nsresult res = GetSelection(getter_AddRefs(selection));
930 0 : if (NS_SUCCEEDED(res)) {
931 0 : mSelState = new nsSelectionState();
932 0 : mSelState->SaveSelection(selection);
933 : }
934 : }
935 0 : mPlaceHolderBatch++;
936 :
937 0 : return NS_OK;
938 : }
939 :
940 : NS_IMETHODIMP
941 0 : nsEditor::EndPlaceHolderTransaction()
942 : {
943 0 : NS_PRECONDITION(mPlaceHolderBatch > 0, "zero or negative placeholder batch count when ending batch!");
944 0 : if (mPlaceHolderBatch == 1)
945 : {
946 0 : nsCOMPtr<nsISelection>selection;
947 0 : GetSelection(getter_AddRefs(selection));
948 :
949 0 : nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(selection));
950 :
951 : // By making the assumption that no reflow happens during the calls
952 : // to EndUpdateViewBatch and ScrollSelectionIntoView, we are able to
953 : // allow the selection to cache a frame offset which is used by the
954 : // caret drawing code. We only enable this cache here; at other times,
955 : // we have no way to know whether reflow invalidates it
956 : // See bugs 35296 and 199412.
957 0 : if (selPrivate) {
958 0 : selPrivate->SetCanCacheFrameOffset(true);
959 : }
960 :
961 : {
962 : // Hide the caret here to avoid hiding it twice, once in EndUpdateViewBatch
963 : // and once in ScrollSelectionIntoView.
964 0 : nsRefPtr<nsCaret> caret;
965 0 : nsCOMPtr<nsIPresShell> presShell = GetPresShell();
966 :
967 0 : if (presShell)
968 0 : caret = presShell->GetCaret();
969 :
970 0 : StCaretHider caretHider(caret);
971 :
972 : // time to turn off the batch
973 0 : EndUpdateViewBatch();
974 : // make sure selection is in view
975 :
976 : // After ScrollSelectionIntoView(), the pending notifications might be
977 : // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
978 0 : ScrollSelectionIntoView(false);
979 : }
980 :
981 : // cached for frame offset are Not available now
982 0 : if (selPrivate) {
983 0 : selPrivate->SetCanCacheFrameOffset(false);
984 : }
985 :
986 0 : if (mSelState)
987 : {
988 : // we saved the selection state, but never got to hand it to placeholder
989 : // (else we ould have nulled out this pointer), so destroy it to prevent leaks.
990 0 : delete mSelState;
991 0 : mSelState = nsnull;
992 : }
993 0 : if (mPlaceHolderTxn) // we might have never made a placeholder if no action took place
994 : {
995 0 : nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryReferent(mPlaceHolderTxn);
996 0 : if (plcTxn)
997 : {
998 0 : plcTxn->EndPlaceHolderBatch();
999 : }
1000 : else
1001 : {
1002 : // in the future we will check to make sure undo is off here,
1003 : // since that is the only known case where the placeholdertxn would disappear on us.
1004 : // For now just removing the assert.
1005 : }
1006 : // notify editor observers of action but if composing, it's done by
1007 : // text event handler.
1008 0 : if (!mInIMEMode) NotifyEditorObservers();
1009 : }
1010 : }
1011 0 : mPlaceHolderBatch--;
1012 :
1013 0 : return NS_OK;
1014 : }
1015 :
1016 : NS_IMETHODIMP
1017 0 : nsEditor::ShouldTxnSetSelection(bool *aResult)
1018 : {
1019 0 : NS_ENSURE_TRUE(aResult, NS_ERROR_NULL_POINTER);
1020 0 : *aResult = mShouldTxnSetSelection;
1021 0 : return NS_OK;
1022 : }
1023 :
1024 : NS_IMETHODIMP
1025 0 : nsEditor::SetShouldTxnSetSelection(bool aShould)
1026 : {
1027 0 : mShouldTxnSetSelection = aShould;
1028 0 : return NS_OK;
1029 : }
1030 :
1031 : NS_IMETHODIMP
1032 0 : nsEditor::GetDocumentIsEmpty(bool *aDocumentIsEmpty)
1033 : {
1034 0 : *aDocumentIsEmpty = true;
1035 :
1036 0 : dom::Element* root = GetRoot();
1037 0 : NS_ENSURE_TRUE(root, NS_ERROR_NULL_POINTER);
1038 :
1039 0 : *aDocumentIsEmpty = !root->HasChildren();
1040 0 : return NS_OK;
1041 : }
1042 :
1043 :
1044 : // XXX: the rule system should tell us which node to select all on (ie, the root, or the body)
1045 0 : NS_IMETHODIMP nsEditor::SelectAll()
1046 : {
1047 0 : if (!mDocWeak) { return NS_ERROR_NOT_INITIALIZED; }
1048 0 : ForceCompositionEnd();
1049 :
1050 0 : nsCOMPtr<nsISelectionController> selCon;
1051 0 : GetSelectionController(getter_AddRefs(selCon));
1052 0 : NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
1053 0 : nsCOMPtr<nsISelection> selection;
1054 0 : nsresult result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
1055 0 : if (NS_SUCCEEDED(result) && selection)
1056 : {
1057 0 : result = SelectEntireDocument(selection);
1058 : }
1059 0 : return result;
1060 : }
1061 :
1062 0 : NS_IMETHODIMP nsEditor::BeginningOfDocument()
1063 : {
1064 0 : if (!mDocWeak) { return NS_ERROR_NOT_INITIALIZED; }
1065 :
1066 : // get the selection
1067 0 : nsCOMPtr<nsISelection> selection;
1068 0 : nsresult result = GetSelection(getter_AddRefs(selection));
1069 0 : NS_ENSURE_SUCCESS(result, result);
1070 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NOT_INITIALIZED);
1071 :
1072 : // get the root element
1073 0 : dom::Element* rootElement = GetRoot();
1074 0 : NS_ENSURE_TRUE(rootElement, NS_ERROR_NULL_POINTER);
1075 :
1076 : // find first editable thingy
1077 0 : nsCOMPtr<nsINode> firstNode = GetFirstEditableNode(rootElement);
1078 0 : if (!firstNode) {
1079 : // just the root node, set selection to inside the root
1080 0 : return selection->CollapseNative(rootElement, 0);
1081 : }
1082 :
1083 0 : if (firstNode->NodeType() == nsIDOMNode::TEXT_NODE) {
1084 : // If firstNode is text, set selection to beginning of the text node.
1085 0 : return selection->CollapseNative(firstNode, 0);
1086 : }
1087 :
1088 : // Otherwise, it's a leaf node and we set the selection just in front of it.
1089 0 : nsCOMPtr<nsIContent> parent = firstNode->GetParent();
1090 0 : if (!parent) {
1091 0 : return NS_ERROR_NULL_POINTER;
1092 : }
1093 :
1094 0 : PRInt32 offsetInParent = parent->IndexOf(firstNode);
1095 0 : return selection->CollapseNative(parent, offsetInParent);
1096 : }
1097 :
1098 : NS_IMETHODIMP
1099 0 : nsEditor::EndOfDocument()
1100 : {
1101 0 : if (!mDocWeak) { return NS_ERROR_NOT_INITIALIZED; }
1102 : nsresult res;
1103 :
1104 : // get selection
1105 0 : nsCOMPtr<nsISelection> selection;
1106 0 : res = GetSelection(getter_AddRefs(selection));
1107 0 : NS_ENSURE_SUCCESS(res, res);
1108 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
1109 :
1110 : // get the root element
1111 0 : nsCOMPtr<nsIDOMNode> node = do_QueryInterface(GetRoot());
1112 0 : NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
1113 0 : nsCOMPtr<nsIDOMNode> child;
1114 :
1115 0 : do {
1116 0 : node->GetLastChild(getter_AddRefs(child));
1117 :
1118 0 : if (child) {
1119 0 : if (IsContainer(child)) {
1120 0 : node = child;
1121 : } else {
1122 0 : break;
1123 : }
1124 : }
1125 0 : } while (child);
1126 :
1127 0 : PRUint32 length = 0;
1128 0 : res = GetLengthOfDOMNode(node, length);
1129 0 : NS_ENSURE_SUCCESS(res, res);
1130 :
1131 0 : return selection->Collapse(node, (PRInt32)length);
1132 : }
1133 :
1134 : NS_IMETHODIMP
1135 0 : nsEditor::GetDocumentModified(bool *outDocModified)
1136 : {
1137 0 : NS_ENSURE_TRUE(outDocModified, NS_ERROR_NULL_POINTER);
1138 :
1139 0 : PRInt32 modCount = 0;
1140 0 : GetModificationCount(&modCount);
1141 :
1142 0 : *outDocModified = (modCount != 0);
1143 0 : return NS_OK;
1144 : }
1145 :
1146 : NS_IMETHODIMP
1147 0 : nsEditor::GetDocumentCharacterSet(nsACString &characterSet)
1148 : {
1149 0 : nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
1150 0 : NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
1151 :
1152 0 : characterSet = doc->GetDocumentCharacterSet();
1153 0 : return NS_OK;
1154 : }
1155 :
1156 : NS_IMETHODIMP
1157 0 : nsEditor::SetDocumentCharacterSet(const nsACString& characterSet)
1158 : {
1159 0 : nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
1160 0 : NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
1161 :
1162 0 : doc->SetDocumentCharacterSet(characterSet);
1163 0 : return NS_OK;
1164 : }
1165 :
1166 : NS_IMETHODIMP
1167 0 : nsEditor::Cut()
1168 : {
1169 0 : return NS_ERROR_NOT_IMPLEMENTED;
1170 : }
1171 :
1172 : NS_IMETHODIMP
1173 0 : nsEditor::CanCut(bool *aCanCut)
1174 : {
1175 0 : return NS_ERROR_NOT_IMPLEMENTED;
1176 : }
1177 :
1178 : NS_IMETHODIMP
1179 0 : nsEditor::Copy()
1180 : {
1181 0 : return NS_ERROR_NOT_IMPLEMENTED;
1182 : }
1183 :
1184 : NS_IMETHODIMP
1185 0 : nsEditor::CanCopy(bool *aCanCut)
1186 : {
1187 0 : return NS_ERROR_NOT_IMPLEMENTED;
1188 : }
1189 :
1190 : NS_IMETHODIMP
1191 0 : nsEditor::Paste(PRInt32 aSelectionType)
1192 : {
1193 0 : return NS_ERROR_NOT_IMPLEMENTED;
1194 : }
1195 :
1196 : NS_IMETHODIMP
1197 0 : nsEditor::PasteTransferable(nsITransferable *aTransferable)
1198 : {
1199 0 : return NS_ERROR_NOT_IMPLEMENTED;
1200 : }
1201 :
1202 : NS_IMETHODIMP
1203 0 : nsEditor::CanPaste(PRInt32 aSelectionType, bool *aCanPaste)
1204 : {
1205 0 : return NS_ERROR_NOT_IMPLEMENTED;
1206 : }
1207 :
1208 : NS_IMETHODIMP
1209 0 : nsEditor::CanPasteTransferable(nsITransferable *aTransferable, bool *aCanPaste)
1210 : {
1211 0 : return NS_ERROR_NOT_IMPLEMENTED;
1212 : }
1213 :
1214 : NS_IMETHODIMP
1215 0 : nsEditor::SetAttribute(nsIDOMElement *aElement, const nsAString & aAttribute, const nsAString & aValue)
1216 : {
1217 0 : nsRefPtr<ChangeAttributeTxn> txn;
1218 : nsresult result = CreateTxnForSetAttribute(aElement, aAttribute, aValue,
1219 0 : getter_AddRefs(txn));
1220 0 : if (NS_SUCCEEDED(result)) {
1221 0 : result = DoTransaction(txn);
1222 : }
1223 0 : return result;
1224 : }
1225 :
1226 : NS_IMETHODIMP
1227 0 : nsEditor::GetAttributeValue(nsIDOMElement *aElement,
1228 : const nsAString & aAttribute,
1229 : nsAString & aResultValue,
1230 : bool *aResultIsSet)
1231 : {
1232 0 : NS_ENSURE_TRUE(aResultIsSet, NS_ERROR_NULL_POINTER);
1233 0 : *aResultIsSet = false;
1234 0 : if (!aElement) {
1235 0 : return NS_OK;
1236 : }
1237 0 : nsAutoString value;
1238 0 : nsresult rv = aElement->GetAttribute(aAttribute, value);
1239 0 : NS_ENSURE_SUCCESS(rv, rv);
1240 0 : if (!DOMStringIsNull(value)) {
1241 0 : *aResultIsSet = true;
1242 0 : aResultValue = value;
1243 : }
1244 0 : return rv;
1245 : }
1246 :
1247 : NS_IMETHODIMP
1248 0 : nsEditor::RemoveAttribute(nsIDOMElement *aElement, const nsAString& aAttribute)
1249 : {
1250 0 : nsRefPtr<ChangeAttributeTxn> txn;
1251 : nsresult result = CreateTxnForRemoveAttribute(aElement, aAttribute,
1252 0 : getter_AddRefs(txn));
1253 0 : if (NS_SUCCEEDED(result)) {
1254 0 : result = DoTransaction(txn);
1255 : }
1256 0 : return result;
1257 : }
1258 :
1259 :
1260 : NS_IMETHODIMP
1261 0 : nsEditor::MarkNodeDirty(nsIDOMNode* aNode)
1262 : {
1263 : // mark the node dirty.
1264 0 : nsCOMPtr<nsIContent> element (do_QueryInterface(aNode));
1265 0 : if (element)
1266 : element->SetAttr(kNameSpaceID_None, nsEditProperty::mozdirty,
1267 0 : EmptyString(), false);
1268 0 : return NS_OK;
1269 : }
1270 :
1271 0 : NS_IMETHODIMP nsEditor::GetInlineSpellChecker(bool autoCreate,
1272 : nsIInlineSpellChecker ** aInlineSpellChecker)
1273 : {
1274 0 : NS_ENSURE_ARG_POINTER(aInlineSpellChecker);
1275 :
1276 0 : if (mDidPreDestroy) {
1277 : // Don't allow people to get or create the spell checker once the editor
1278 : // is going away.
1279 0 : *aInlineSpellChecker = nsnull;
1280 0 : return autoCreate ? NS_ERROR_NOT_AVAILABLE : NS_OK;
1281 : }
1282 :
1283 : // We don't want to show the spell checking UI if there are no spell check dictionaries available.
1284 0 : bool canSpell = mozInlineSpellChecker::CanEnableInlineSpellChecking();
1285 0 : if (!canSpell) {
1286 0 : *aInlineSpellChecker = nsnull;
1287 0 : return NS_ERROR_FAILURE;
1288 : }
1289 :
1290 : nsresult rv;
1291 0 : if (!mInlineSpellChecker && autoCreate) {
1292 0 : mInlineSpellChecker = do_CreateInstance(MOZ_INLINESPELLCHECKER_CONTRACTID, &rv);
1293 0 : NS_ENSURE_SUCCESS(rv, rv);
1294 : }
1295 :
1296 0 : if (mInlineSpellChecker) {
1297 0 : rv = mInlineSpellChecker->Init(this);
1298 0 : if (NS_FAILED(rv))
1299 0 : mInlineSpellChecker = nsnull;
1300 0 : NS_ENSURE_SUCCESS(rv, rv);
1301 : }
1302 :
1303 0 : NS_IF_ADDREF(*aInlineSpellChecker = mInlineSpellChecker);
1304 :
1305 0 : return NS_OK;
1306 : }
1307 :
1308 0 : NS_IMETHODIMP nsEditor::Observe(nsISupports* aSubj, const char *aTopic,
1309 : const PRUnichar *aData)
1310 : {
1311 0 : NS_ASSERTION(!strcmp(aTopic,
1312 : SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION),
1313 : "Unexpected observer topic");
1314 :
1315 : // When mozInlineSpellChecker::CanEnableInlineSpellChecking changes
1316 0 : SyncRealTimeSpell();
1317 :
1318 : // When nsIEditorSpellCheck::GetCurrentDictionary changes
1319 0 : if (mInlineSpellChecker) {
1320 : // if the current dictionary is no longer available, find another one
1321 0 : nsCOMPtr<nsIEditorSpellCheck> editorSpellCheck;
1322 0 : mInlineSpellChecker->GetSpellChecker(getter_AddRefs(editorSpellCheck));
1323 0 : if (editorSpellCheck) {
1324 : // Note: This might change the current dictionary, which may call
1325 : // this observer recursively.
1326 0 : editorSpellCheck->CheckCurrentDictionary();
1327 : }
1328 :
1329 : // update the inline spell checker to reflect the new current dictionary
1330 0 : mInlineSpellChecker->SpellCheckRange(nsnull); // causes recheck
1331 : }
1332 :
1333 0 : return NS_OK;
1334 : }
1335 :
1336 0 : NS_IMETHODIMP nsEditor::SyncRealTimeSpell()
1337 : {
1338 : NS_TIME_FUNCTION;
1339 :
1340 0 : bool enable = GetDesiredSpellCheckState();
1341 :
1342 : // Initializes mInlineSpellChecker
1343 0 : nsCOMPtr<nsIInlineSpellChecker> spellChecker;
1344 0 : GetInlineSpellChecker(enable, getter_AddRefs(spellChecker));
1345 :
1346 0 : if (mInlineSpellChecker) {
1347 : // We might have a mInlineSpellChecker even if there are no dictionaries
1348 : // available since we don't destroy the mInlineSpellChecker when the last
1349 : // dictionariy is removed, but in that case spellChecker is null
1350 0 : mInlineSpellChecker->SetEnableRealTimeSpell(enable && spellChecker);
1351 : }
1352 :
1353 0 : return NS_OK;
1354 : }
1355 :
1356 0 : NS_IMETHODIMP nsEditor::SetSpellcheckUserOverride(bool enable)
1357 : {
1358 0 : mSpellcheckCheckboxState = enable ? eTriTrue : eTriFalse;
1359 :
1360 0 : return SyncRealTimeSpell();
1361 : }
1362 :
1363 0 : NS_IMETHODIMP nsEditor::CreateNode(const nsAString& aTag,
1364 : nsIDOMNode * aParent,
1365 : PRInt32 aPosition,
1366 : nsIDOMNode ** aNewNode)
1367 : {
1368 : PRInt32 i;
1369 :
1370 0 : nsAutoRules beginRulesSniffing(this, kOpCreateNode, nsIEditor::eNext);
1371 :
1372 0 : for (i = 0; i < mActionListeners.Count(); i++)
1373 0 : mActionListeners[i]->WillCreateNode(aTag, aParent, aPosition);
1374 :
1375 0 : nsRefPtr<CreateElementTxn> txn;
1376 : nsresult result = CreateTxnForCreateElement(aTag, aParent, aPosition,
1377 0 : getter_AddRefs(txn));
1378 0 : if (NS_SUCCEEDED(result))
1379 : {
1380 0 : result = DoTransaction(txn);
1381 0 : if (NS_SUCCEEDED(result))
1382 : {
1383 0 : result = txn->GetNewNode(aNewNode);
1384 0 : NS_ASSERTION((NS_SUCCEEDED(result)), "GetNewNode can't fail if txn::DoTransaction succeeded.");
1385 : }
1386 : }
1387 :
1388 0 : mRangeUpdater.SelAdjCreateNode(aParent, aPosition);
1389 :
1390 0 : for (i = 0; i < mActionListeners.Count(); i++)
1391 0 : mActionListeners[i]->DidCreateNode(aTag, *aNewNode, aParent, aPosition, result);
1392 :
1393 0 : return result;
1394 : }
1395 :
1396 :
1397 0 : NS_IMETHODIMP nsEditor::InsertNode(nsIDOMNode * aNode,
1398 : nsIDOMNode * aParent,
1399 : PRInt32 aPosition)
1400 : {
1401 : PRInt32 i;
1402 0 : nsAutoRules beginRulesSniffing(this, kOpInsertNode, nsIEditor::eNext);
1403 :
1404 0 : for (i = 0; i < mActionListeners.Count(); i++)
1405 0 : mActionListeners[i]->WillInsertNode(aNode, aParent, aPosition);
1406 :
1407 0 : nsRefPtr<InsertElementTxn> txn;
1408 : nsresult result = CreateTxnForInsertElement(aNode, aParent, aPosition,
1409 0 : getter_AddRefs(txn));
1410 0 : if (NS_SUCCEEDED(result)) {
1411 0 : result = DoTransaction(txn);
1412 : }
1413 :
1414 0 : mRangeUpdater.SelAdjInsertNode(aParent, aPosition);
1415 :
1416 0 : for (i = 0; i < mActionListeners.Count(); i++)
1417 0 : mActionListeners[i]->DidInsertNode(aNode, aParent, aPosition, result);
1418 :
1419 0 : return result;
1420 : }
1421 :
1422 :
1423 : NS_IMETHODIMP
1424 0 : nsEditor::SplitNode(nsIDOMNode * aNode,
1425 : PRInt32 aOffset,
1426 : nsIDOMNode **aNewLeftNode)
1427 : {
1428 : PRInt32 i;
1429 0 : nsAutoRules beginRulesSniffing(this, kOpSplitNode, nsIEditor::eNext);
1430 :
1431 0 : for (i = 0; i < mActionListeners.Count(); i++)
1432 0 : mActionListeners[i]->WillSplitNode(aNode, aOffset);
1433 :
1434 0 : nsRefPtr<SplitElementTxn> txn;
1435 0 : nsresult result = CreateTxnForSplitNode(aNode, aOffset, getter_AddRefs(txn));
1436 0 : if (NS_SUCCEEDED(result))
1437 : {
1438 0 : result = DoTransaction(txn);
1439 0 : if (NS_SUCCEEDED(result))
1440 : {
1441 0 : result = txn->GetNewNode(aNewLeftNode);
1442 0 : NS_ASSERTION((NS_SUCCEEDED(result)), "result must succeeded for GetNewNode");
1443 : }
1444 : }
1445 :
1446 0 : mRangeUpdater.SelAdjSplitNode(aNode, aOffset, *aNewLeftNode);
1447 :
1448 0 : for (i = 0; i < mActionListeners.Count(); i++)
1449 : {
1450 0 : nsIDOMNode *ptr = *aNewLeftNode;
1451 0 : mActionListeners[i]->DidSplitNode(aNode, aOffset, ptr, result);
1452 : }
1453 :
1454 0 : return result;
1455 : }
1456 :
1457 :
1458 :
1459 : NS_IMETHODIMP
1460 0 : nsEditor::JoinNodes(nsIDOMNode * aLeftNode,
1461 : nsIDOMNode * aRightNode,
1462 : nsIDOMNode * aParent)
1463 : {
1464 : PRInt32 i, offset;
1465 0 : nsAutoRules beginRulesSniffing(this, kOpJoinNode, nsIEditor::ePrevious);
1466 :
1467 : // remember some values; later used for saved selection updating.
1468 : // find the offset between the nodes to be joined.
1469 0 : nsresult result = GetChildOffset(aRightNode, aParent, offset);
1470 0 : NS_ENSURE_SUCCESS(result, result);
1471 : // find the number of children of the lefthand node
1472 : PRUint32 oldLeftNodeLen;
1473 0 : result = GetLengthOfDOMNode(aLeftNode, oldLeftNodeLen);
1474 0 : NS_ENSURE_SUCCESS(result, result);
1475 :
1476 0 : for (i = 0; i < mActionListeners.Count(); i++)
1477 0 : mActionListeners[i]->WillJoinNodes(aLeftNode, aRightNode, aParent);
1478 :
1479 0 : nsRefPtr<JoinElementTxn> txn;
1480 0 : result = CreateTxnForJoinNode(aLeftNode, aRightNode, getter_AddRefs(txn));
1481 0 : if (NS_SUCCEEDED(result)) {
1482 0 : result = DoTransaction(txn);
1483 : }
1484 :
1485 0 : mRangeUpdater.SelAdjJoinNodes(aLeftNode, aRightNode, aParent, offset, (PRInt32)oldLeftNodeLen);
1486 :
1487 0 : for (i = 0; i < mActionListeners.Count(); i++)
1488 0 : mActionListeners[i]->DidJoinNodes(aLeftNode, aRightNode, aParent, result);
1489 :
1490 0 : return result;
1491 : }
1492 :
1493 :
1494 0 : NS_IMETHODIMP nsEditor::DeleteNode(nsIDOMNode * aElement)
1495 : {
1496 : PRInt32 i, offset;
1497 0 : nsCOMPtr<nsIDOMNode> parent;
1498 0 : nsAutoRules beginRulesSniffing(this, kOpCreateNode, nsIEditor::ePrevious);
1499 :
1500 : // save node location for selection updating code.
1501 0 : nsresult result = GetNodeLocation(aElement, address_of(parent), &offset);
1502 0 : NS_ENSURE_SUCCESS(result, result);
1503 :
1504 0 : for (i = 0; i < mActionListeners.Count(); i++)
1505 0 : mActionListeners[i]->WillDeleteNode(aElement);
1506 :
1507 0 : nsRefPtr<DeleteElementTxn> txn;
1508 0 : result = CreateTxnForDeleteElement(aElement, getter_AddRefs(txn));
1509 0 : if (NS_SUCCEEDED(result)) {
1510 0 : result = DoTransaction(txn);
1511 : }
1512 :
1513 0 : for (i = 0; i < mActionListeners.Count(); i++)
1514 0 : mActionListeners[i]->DidDeleteNode(aElement, result);
1515 :
1516 0 : return result;
1517 : }
1518 :
1519 : ///////////////////////////////////////////////////////////////////////////
1520 : // ReplaceContainer: replace inNode with a new node (outNode) which is contructed
1521 : // to be of type aNodeType. Put inNodes children into outNode.
1522 : // Callers responsibility to make sure inNode's children can
1523 : // go in outNode.
1524 : nsresult
1525 0 : nsEditor::ReplaceContainer(nsIDOMNode *inNode,
1526 : nsCOMPtr<nsIDOMNode> *outNode,
1527 : const nsAString &aNodeType,
1528 : const nsAString *aAttribute,
1529 : const nsAString *aValue,
1530 : bool aCloneAttributes)
1531 : {
1532 0 : NS_ENSURE_TRUE(inNode && outNode, NS_ERROR_NULL_POINTER);
1533 0 : nsCOMPtr<nsIDOMNode> parent;
1534 : PRInt32 offset;
1535 0 : nsresult res = GetNodeLocation(inNode, address_of(parent), &offset);
1536 0 : NS_ENSURE_SUCCESS(res, res);
1537 :
1538 : // create new container
1539 0 : nsCOMPtr<nsIContent> newContent;
1540 :
1541 : //new call to use instead to get proper HTML element, bug# 39919
1542 0 : res = CreateHTMLContent(aNodeType, getter_AddRefs(newContent));
1543 0 : nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(newContent);
1544 0 : NS_ENSURE_SUCCESS(res, res);
1545 0 : *outNode = do_QueryInterface(elem);
1546 :
1547 : // set attribute if needed
1548 0 : if (aAttribute && aValue && !aAttribute->IsEmpty())
1549 : {
1550 0 : res = elem->SetAttribute(*aAttribute, *aValue);
1551 0 : NS_ENSURE_SUCCESS(res, res);
1552 : }
1553 0 : if (aCloneAttributes)
1554 : {
1555 0 : nsCOMPtr<nsIDOMNode>newNode = do_QueryInterface(elem);
1556 0 : res = CloneAttributes(newNode, inNode);
1557 0 : NS_ENSURE_SUCCESS(res, res);
1558 : }
1559 :
1560 : // notify our internal selection state listener
1561 : // (Note: A nsAutoSelectionReset object must be created
1562 : // before calling this to initialize mRangeUpdater)
1563 0 : nsAutoReplaceContainerSelNotify selStateNotify(mRangeUpdater, inNode, *outNode);
1564 : {
1565 0 : nsAutoTxnsConserveSelection conserveSelection(this);
1566 0 : nsCOMPtr<nsIDOMNode> child;
1567 : bool bHasMoreChildren;
1568 0 : inNode->HasChildNodes(&bHasMoreChildren);
1569 0 : while (bHasMoreChildren)
1570 : {
1571 0 : inNode->GetFirstChild(getter_AddRefs(child));
1572 0 : res = DeleteNode(child);
1573 0 : NS_ENSURE_SUCCESS(res, res);
1574 :
1575 0 : res = InsertNode(child, *outNode, -1);
1576 0 : NS_ENSURE_SUCCESS(res, res);
1577 0 : inNode->HasChildNodes(&bHasMoreChildren);
1578 : }
1579 : }
1580 : // insert new container into tree
1581 0 : res = InsertNode( *outNode, parent, offset);
1582 0 : NS_ENSURE_SUCCESS(res, res);
1583 :
1584 : // delete old container
1585 0 : return DeleteNode(inNode);
1586 : }
1587 :
1588 : ///////////////////////////////////////////////////////////////////////////
1589 : // RemoveContainer: remove inNode, reparenting its children into their
1590 : // the parent of inNode
1591 : //
1592 : nsresult
1593 0 : nsEditor::RemoveContainer(nsINode* aNode)
1594 : {
1595 0 : nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode);
1596 0 : return RemoveContainer(node);
1597 : }
1598 :
1599 : nsresult
1600 0 : nsEditor::RemoveContainer(nsIDOMNode *inNode)
1601 : {
1602 0 : NS_ENSURE_TRUE(inNode, NS_ERROR_NULL_POINTER);
1603 0 : nsCOMPtr<nsIDOMNode> parent;
1604 : PRInt32 offset;
1605 :
1606 0 : nsresult res = GetNodeLocation(inNode, address_of(parent), &offset);
1607 0 : NS_ENSURE_SUCCESS(res, res);
1608 :
1609 : // loop through the child nodes of inNode and promote them
1610 : // into inNode's parent.
1611 : bool bHasMoreChildren;
1612 0 : inNode->HasChildNodes(&bHasMoreChildren);
1613 0 : nsCOMPtr<nsIDOMNodeList> nodeList;
1614 0 : res = inNode->GetChildNodes(getter_AddRefs(nodeList));
1615 0 : NS_ENSURE_SUCCESS(res, res);
1616 0 : NS_ENSURE_TRUE(nodeList, NS_ERROR_NULL_POINTER);
1617 : PRUint32 nodeOrigLen;
1618 0 : nodeList->GetLength(&nodeOrigLen);
1619 :
1620 : // notify our internal selection state listener
1621 0 : nsAutoRemoveContainerSelNotify selNotify(mRangeUpdater, inNode, parent, offset, nodeOrigLen);
1622 :
1623 0 : nsCOMPtr<nsIDOMNode> child;
1624 0 : while (bHasMoreChildren)
1625 : {
1626 0 : inNode->GetLastChild(getter_AddRefs(child));
1627 0 : res = DeleteNode(child);
1628 0 : NS_ENSURE_SUCCESS(res, res);
1629 0 : res = InsertNode(child, parent, offset);
1630 0 : NS_ENSURE_SUCCESS(res, res);
1631 0 : inNode->HasChildNodes(&bHasMoreChildren);
1632 : }
1633 0 : return DeleteNode(inNode);
1634 : }
1635 :
1636 :
1637 : ///////////////////////////////////////////////////////////////////////////
1638 : // InsertContainerAbove: insert a new parent for inNode, returned in outNode,
1639 : // which is contructed to be of type aNodeType. outNode becomes
1640 : // a child of inNode's earlier parent.
1641 : // Callers responsibility to make sure inNode's can be child
1642 : // of outNode, and outNode can be child of old parent.
1643 : nsresult
1644 0 : nsEditor::InsertContainerAbove( nsIDOMNode *inNode,
1645 : nsCOMPtr<nsIDOMNode> *outNode,
1646 : const nsAString &aNodeType,
1647 : const nsAString *aAttribute,
1648 : const nsAString *aValue)
1649 : {
1650 0 : NS_ENSURE_TRUE(inNode && outNode, NS_ERROR_NULL_POINTER);
1651 0 : nsCOMPtr<nsIDOMNode> parent;
1652 : PRInt32 offset;
1653 0 : nsresult res = GetNodeLocation(inNode, address_of(parent), &offset);
1654 0 : NS_ENSURE_SUCCESS(res, res);
1655 :
1656 : // create new container
1657 0 : nsCOMPtr<nsIContent> newContent;
1658 :
1659 : //new call to use instead to get proper HTML element, bug# 39919
1660 0 : res = CreateHTMLContent(aNodeType, getter_AddRefs(newContent));
1661 0 : nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(newContent);
1662 0 : NS_ENSURE_SUCCESS(res, res);
1663 0 : *outNode = do_QueryInterface(elem);
1664 :
1665 : // set attribute if needed
1666 0 : if (aAttribute && aValue && !aAttribute->IsEmpty())
1667 : {
1668 0 : res = elem->SetAttribute(*aAttribute, *aValue);
1669 0 : NS_ENSURE_SUCCESS(res, res);
1670 : }
1671 :
1672 : // notify our internal selection state listener
1673 0 : nsAutoInsertContainerSelNotify selNotify(mRangeUpdater);
1674 :
1675 : // put inNode in new parent, outNode
1676 0 : res = DeleteNode(inNode);
1677 0 : NS_ENSURE_SUCCESS(res, res);
1678 :
1679 : {
1680 0 : nsAutoTxnsConserveSelection conserveSelection(this);
1681 0 : res = InsertNode(inNode, *outNode, 0);
1682 0 : NS_ENSURE_SUCCESS(res, res);
1683 : }
1684 :
1685 : // put new parent in doc
1686 0 : return InsertNode(*outNode, parent, offset);
1687 : }
1688 :
1689 : ///////////////////////////////////////////////////////////////////////////
1690 : // MoveNode: move aNode to {aParent,aOffset}
1691 : nsresult
1692 0 : nsEditor::MoveNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset)
1693 : {
1694 0 : NS_ENSURE_TRUE(aNode && aParent, NS_ERROR_NULL_POINTER);
1695 :
1696 0 : nsCOMPtr<nsIDOMNode> oldParent;
1697 : PRInt32 oldOffset;
1698 0 : nsresult res = GetNodeLocation(aNode, address_of(oldParent), &oldOffset);
1699 :
1700 0 : if (aOffset == -1)
1701 : {
1702 : PRUint32 unsignedOffset;
1703 : // magic value meaning "move to end of aParent"
1704 0 : res = GetLengthOfDOMNode(aParent, unsignedOffset);
1705 0 : NS_ENSURE_SUCCESS(res, res);
1706 0 : aOffset = (PRInt32)unsignedOffset;
1707 : }
1708 :
1709 : // don't do anything if it's already in right place
1710 0 : if ((aParent == oldParent.get()) && (oldOffset == aOffset)) return NS_OK;
1711 :
1712 : // notify our internal selection state listener
1713 0 : nsAutoMoveNodeSelNotify selNotify(mRangeUpdater, oldParent, oldOffset, aParent, aOffset);
1714 :
1715 : // need to adjust aOffset if we are moving aNode further along in its current parent
1716 0 : if ((aParent == oldParent.get()) && (oldOffset < aOffset))
1717 : {
1718 0 : aOffset--; // this is because when we delete aNode, it will make the offsets after it off by one
1719 : }
1720 :
1721 : // put aNode in new parent
1722 0 : res = DeleteNode(aNode);
1723 0 : NS_ENSURE_SUCCESS(res, res);
1724 0 : return InsertNode(aNode, aParent, aOffset);
1725 : }
1726 :
1727 :
1728 : NS_IMETHODIMP
1729 0 : nsEditor::AddEditorObserver(nsIEditorObserver *aObserver)
1730 : {
1731 : // we don't keep ownership of the observers. They must
1732 : // remove themselves as observers before they are destroyed.
1733 :
1734 0 : NS_ENSURE_TRUE(aObserver, NS_ERROR_NULL_POINTER);
1735 :
1736 : // Make sure the listener isn't already on the list
1737 0 : if (mEditorObservers.IndexOf(aObserver) == -1)
1738 : {
1739 0 : if (!mEditorObservers.AppendObject(aObserver))
1740 0 : return NS_ERROR_FAILURE;
1741 : }
1742 :
1743 0 : return NS_OK;
1744 : }
1745 :
1746 :
1747 : NS_IMETHODIMP
1748 0 : nsEditor::RemoveEditorObserver(nsIEditorObserver *aObserver)
1749 : {
1750 0 : NS_ENSURE_TRUE(aObserver, NS_ERROR_FAILURE);
1751 :
1752 0 : if (!mEditorObservers.RemoveObject(aObserver))
1753 0 : return NS_ERROR_FAILURE;
1754 :
1755 0 : return NS_OK;
1756 : }
1757 :
1758 0 : void nsEditor::NotifyEditorObservers(void)
1759 : {
1760 0 : for (PRInt32 i = 0; i < mEditorObservers.Count(); i++)
1761 0 : mEditorObservers[i]->EditAction();
1762 0 : }
1763 :
1764 :
1765 : NS_IMETHODIMP
1766 0 : nsEditor::AddEditActionListener(nsIEditActionListener *aListener)
1767 : {
1768 0 : NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER);
1769 :
1770 : // Make sure the listener isn't already on the list
1771 0 : if (mActionListeners.IndexOf(aListener) == -1)
1772 : {
1773 0 : if (!mActionListeners.AppendObject(aListener))
1774 0 : return NS_ERROR_FAILURE;
1775 : }
1776 :
1777 0 : return NS_OK;
1778 : }
1779 :
1780 :
1781 : NS_IMETHODIMP
1782 0 : nsEditor::RemoveEditActionListener(nsIEditActionListener *aListener)
1783 : {
1784 0 : NS_ENSURE_TRUE(aListener, NS_ERROR_FAILURE);
1785 :
1786 0 : if (!mActionListeners.RemoveObject(aListener))
1787 0 : return NS_ERROR_FAILURE;
1788 :
1789 0 : return NS_OK;
1790 : }
1791 :
1792 :
1793 : NS_IMETHODIMP
1794 0 : nsEditor::AddDocumentStateListener(nsIDocumentStateListener *aListener)
1795 : {
1796 0 : NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER);
1797 :
1798 0 : if (mDocStateListeners.IndexOf(aListener) == -1)
1799 : {
1800 0 : if (!mDocStateListeners.AppendObject(aListener))
1801 0 : return NS_ERROR_FAILURE;
1802 : }
1803 :
1804 0 : return NS_OK;
1805 : }
1806 :
1807 :
1808 : NS_IMETHODIMP
1809 0 : nsEditor::RemoveDocumentStateListener(nsIDocumentStateListener *aListener)
1810 : {
1811 0 : NS_ENSURE_TRUE(aListener, NS_ERROR_NULL_POINTER);
1812 :
1813 0 : if (!mDocStateListeners.RemoveObject(aListener))
1814 0 : return NS_ERROR_FAILURE;
1815 :
1816 0 : return NS_OK;
1817 : }
1818 :
1819 :
1820 0 : NS_IMETHODIMP nsEditor::OutputToString(const nsAString& aFormatType,
1821 : PRUint32 aFlags,
1822 : nsAString& aOutputString)
1823 : {
1824 : // these should be implemented by derived classes.
1825 0 : return NS_ERROR_NOT_IMPLEMENTED;
1826 : }
1827 :
1828 : NS_IMETHODIMP
1829 0 : nsEditor::OutputToStream(nsIOutputStream* aOutputStream,
1830 : const nsAString& aFormatType,
1831 : const nsACString& aCharsetOverride,
1832 : PRUint32 aFlags)
1833 : {
1834 : // these should be implemented by derived classes.
1835 0 : return NS_ERROR_NOT_IMPLEMENTED;
1836 : }
1837 :
1838 : NS_IMETHODIMP
1839 0 : nsEditor::DumpContentTree()
1840 : {
1841 : #ifdef DEBUG
1842 0 : nsCOMPtr<nsIContent> root = do_QueryInterface(mRootElement);
1843 0 : if (root) root->List(stdout);
1844 : #endif
1845 0 : return NS_OK;
1846 : }
1847 :
1848 :
1849 : NS_IMETHODIMP
1850 0 : nsEditor::DebugDumpContent()
1851 : {
1852 : #ifdef DEBUG
1853 0 : nsCOMPtr<nsIDOMHTMLDocument> doc = do_QueryReferent(mDocWeak);
1854 0 : NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
1855 :
1856 0 : nsCOMPtr<nsIDOMHTMLElement>bodyElem;
1857 0 : doc->GetBody(getter_AddRefs(bodyElem));
1858 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(bodyElem);
1859 0 : if (content)
1860 0 : content->List();
1861 : #endif
1862 0 : return NS_OK;
1863 : }
1864 :
1865 :
1866 : NS_IMETHODIMP
1867 0 : nsEditor::DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed)
1868 : {
1869 : #ifdef DEBUG
1870 0 : NS_NOTREACHED("This should never get called. Overridden by subclasses");
1871 : #endif
1872 0 : return NS_OK;
1873 : }
1874 :
1875 :
1876 : bool
1877 0 : nsEditor::ArePreservingSelection()
1878 : {
1879 0 : return !(mSavedSel.IsEmpty());
1880 : }
1881 :
1882 : nsresult
1883 0 : nsEditor::PreserveSelectionAcrossActions(nsISelection *aSel)
1884 : {
1885 0 : mSavedSel.SaveSelection(aSel);
1886 0 : mRangeUpdater.RegisterSelectionState(mSavedSel);
1887 0 : return NS_OK;
1888 : }
1889 :
1890 : nsresult
1891 0 : nsEditor::RestorePreservedSelection(nsISelection *aSel)
1892 : {
1893 0 : if (mSavedSel.IsEmpty()) return NS_ERROR_FAILURE;
1894 0 : mSavedSel.RestoreSelection(aSel);
1895 0 : StopPreservingSelection();
1896 0 : return NS_OK;
1897 : }
1898 :
1899 : void
1900 0 : nsEditor::StopPreservingSelection()
1901 : {
1902 0 : mRangeUpdater.DropSelectionState(mSavedSel);
1903 0 : mSavedSel.MakeEmpty();
1904 0 : }
1905 :
1906 :
1907 : nsresult
1908 0 : nsEditor::BeginIMEComposition()
1909 : {
1910 0 : mInIMEMode = true;
1911 0 : if (mPhonetic) {
1912 0 : mPhonetic->Truncate(0);
1913 : }
1914 0 : return NS_OK;
1915 : }
1916 :
1917 : nsresult
1918 0 : nsEditor::EndIMEComposition()
1919 : {
1920 0 : NS_ENSURE_TRUE(mInIMEMode, NS_OK); // nothing to do
1921 :
1922 0 : nsresult rv = NS_OK;
1923 :
1924 : // commit the IME transaction..we can get at it via the transaction mgr.
1925 : // Note that this means IME won't work without an undo stack!
1926 0 : if (mTxnMgr) {
1927 0 : nsCOMPtr<nsITransaction> txn;
1928 0 : rv = mTxnMgr->PeekUndoStack(getter_AddRefs(txn));
1929 0 : NS_ASSERTION(NS_SUCCEEDED(rv), "PeekUndoStack() failed");
1930 0 : nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryInterface(txn);
1931 0 : if (plcTxn) {
1932 0 : rv = plcTxn->Commit();
1933 0 : NS_ASSERTION(NS_SUCCEEDED(rv),
1934 : "nsIAbsorbingTransaction::Commit() failed");
1935 : }
1936 : }
1937 :
1938 : /* reset the data we need to construct a transaction */
1939 0 : mIMETextNode = nsnull;
1940 0 : mIMETextOffset = 0;
1941 0 : mIMEBufferLength = 0;
1942 0 : mInIMEMode = false;
1943 0 : mIsIMEComposing = false;
1944 :
1945 : // notify editor observers of action
1946 0 : NotifyEditorObservers();
1947 :
1948 0 : return rv;
1949 : }
1950 :
1951 :
1952 : NS_IMETHODIMP
1953 0 : nsEditor::GetPhonetic(nsAString& aPhonetic)
1954 : {
1955 0 : if (mPhonetic)
1956 0 : aPhonetic = *mPhonetic;
1957 : else
1958 0 : aPhonetic.Truncate(0);
1959 :
1960 0 : return NS_OK;
1961 : }
1962 :
1963 :
1964 : static nsresult
1965 0 : GetEditorContentWindow(dom::Element *aRoot, nsIWidget **aResult)
1966 : {
1967 0 : NS_ENSURE_TRUE(aRoot && aResult, NS_ERROR_NULL_POINTER);
1968 :
1969 0 : *aResult = 0;
1970 :
1971 : // Not ref counted
1972 0 : nsIFrame *frame = aRoot->GetPrimaryFrame();
1973 :
1974 0 : NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
1975 :
1976 0 : *aResult = frame->GetNearestWidget();
1977 0 : NS_ENSURE_TRUE(*aResult, NS_ERROR_FAILURE);
1978 :
1979 0 : NS_ADDREF(*aResult);
1980 0 : return NS_OK;
1981 : }
1982 :
1983 : nsresult
1984 0 : nsEditor::GetWidget(nsIWidget **aWidget)
1985 : {
1986 0 : NS_ENSURE_TRUE(aWidget, NS_ERROR_NULL_POINTER);
1987 0 : *aWidget = nsnull;
1988 :
1989 0 : nsCOMPtr<nsIWidget> widget;
1990 0 : nsresult res = GetEditorContentWindow(GetRoot(), getter_AddRefs(widget));
1991 0 : NS_ENSURE_SUCCESS(res, res);
1992 0 : NS_ENSURE_TRUE(widget, NS_ERROR_NOT_AVAILABLE);
1993 :
1994 0 : NS_ADDREF(*aWidget = widget);
1995 :
1996 0 : return NS_OK;
1997 : }
1998 :
1999 : NS_IMETHODIMP
2000 0 : nsEditor::ForceCompositionEnd()
2001 : {
2002 :
2003 : // We can test mInIMEMode and do some optimization for Mac and Window
2004 : // Howerver, since UNIX support over-the-spot, we cannot rely on that
2005 : // flag for Unix.
2006 : // We should use LookAndFeel to resolve this
2007 :
2008 : #if defined(XP_MACOSX) || defined(XP_WIN) || defined(XP_OS2)
2009 : // XXXmnakano see bug 558976, ResetInputState() has two meaning which are
2010 : // "commit the composition" and "cursor is moved". This method name is
2011 : // "ForceCompositionEnd", so, ResetInputState() should be used only for the
2012 : // former here. However, ResetInputState() is also used for the latter here
2013 : // because even if we don't have composition, we call ResetInputState() on
2014 : // Linux. Currently, nsGtkIMModule can know the timing of the cursor move,
2015 : // so, the latter meaning should be gone and we should remove this #if.
2016 : if(! mInIMEMode)
2017 : return NS_OK;
2018 : #endif
2019 :
2020 0 : nsCOMPtr<nsIWidget> widget;
2021 0 : nsresult res = GetWidget(getter_AddRefs(widget));
2022 0 : NS_ENSURE_SUCCESS(res, res);
2023 :
2024 0 : if (widget) {
2025 0 : res = widget->ResetInputState();
2026 0 : NS_ENSURE_SUCCESS(res, res);
2027 : }
2028 :
2029 0 : return NS_OK;
2030 : }
2031 :
2032 : NS_IMETHODIMP
2033 0 : nsEditor::GetPreferredIMEState(IMEState *aState)
2034 : {
2035 0 : NS_ENSURE_ARG_POINTER(aState);
2036 0 : aState->mEnabled = IMEState::ENABLED;
2037 0 : aState->mOpen = IMEState::DONT_CHANGE_OPEN_STATE;
2038 :
2039 0 : if (IsReadonly() || IsDisabled()) {
2040 0 : aState->mEnabled = IMEState::DISABLED;
2041 0 : return NS_OK;
2042 : }
2043 :
2044 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(GetRoot());
2045 0 : NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
2046 :
2047 0 : nsIFrame* frame = content->GetPrimaryFrame();
2048 0 : NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
2049 :
2050 0 : switch (frame->GetStyleUIReset()->mIMEMode) {
2051 : case NS_STYLE_IME_MODE_AUTO:
2052 0 : if (IsPasswordEditor())
2053 0 : aState->mEnabled = IMEState::PASSWORD;
2054 0 : break;
2055 : case NS_STYLE_IME_MODE_DISABLED:
2056 : // we should use password state for |ime-mode: disabled;|.
2057 0 : aState->mEnabled = IMEState::PASSWORD;
2058 0 : break;
2059 : case NS_STYLE_IME_MODE_ACTIVE:
2060 0 : aState->mOpen = IMEState::OPEN;
2061 0 : break;
2062 : case NS_STYLE_IME_MODE_INACTIVE:
2063 0 : aState->mOpen = IMEState::CLOSED;
2064 0 : break;
2065 : }
2066 :
2067 0 : return NS_OK;
2068 : }
2069 :
2070 : NS_IMETHODIMP
2071 0 : nsEditor::GetComposing(bool* aResult)
2072 : {
2073 0 : NS_ENSURE_ARG_POINTER(aResult);
2074 0 : *aResult = IsIMEComposing();
2075 0 : return NS_OK;
2076 : }
2077 :
2078 :
2079 : /* Non-interface, public methods */
2080 :
2081 : NS_IMETHODIMP
2082 0 : nsEditor::GetRootElement(nsIDOMElement **aRootElement)
2083 : {
2084 0 : NS_ENSURE_ARG_POINTER(aRootElement);
2085 0 : NS_ENSURE_TRUE(mRootElement, NS_ERROR_NOT_AVAILABLE);
2086 0 : nsCOMPtr<nsIDOMElement> rootElement = do_QueryInterface(mRootElement);
2087 0 : rootElement.forget(aRootElement);
2088 0 : return NS_OK;
2089 : }
2090 :
2091 :
2092 : /** All editor operations which alter the doc should be prefaced
2093 : * with a call to StartOperation, naming the action and direction */
2094 : NS_IMETHODIMP
2095 0 : nsEditor::StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection)
2096 : {
2097 0 : mAction = opID;
2098 0 : mDirection = aDirection;
2099 0 : return NS_OK;
2100 : }
2101 :
2102 :
2103 : /** All editor operations which alter the doc should be followed
2104 : * with a call to EndOperation */
2105 : NS_IMETHODIMP
2106 0 : nsEditor::EndOperation()
2107 : {
2108 0 : mAction = nsnull;
2109 0 : mDirection = eNone;
2110 0 : return NS_OK;
2111 : }
2112 :
2113 : NS_IMETHODIMP
2114 0 : nsEditor::CloneAttribute(const nsAString & aAttribute,
2115 : nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode)
2116 : {
2117 0 : NS_ENSURE_TRUE(aDestNode && aSourceNode, NS_ERROR_NULL_POINTER);
2118 :
2119 0 : nsCOMPtr<nsIDOMElement> destElement = do_QueryInterface(aDestNode);
2120 0 : nsCOMPtr<nsIDOMElement> sourceElement = do_QueryInterface(aSourceNode);
2121 0 : NS_ENSURE_TRUE(destElement && sourceElement, NS_ERROR_NO_INTERFACE);
2122 :
2123 0 : nsAutoString attrValue;
2124 : bool isAttrSet;
2125 : nsresult rv = GetAttributeValue(sourceElement,
2126 : aAttribute,
2127 : attrValue,
2128 0 : &isAttrSet);
2129 0 : NS_ENSURE_SUCCESS(rv, rv);
2130 0 : if (isAttrSet)
2131 0 : rv = SetAttribute(destElement, aAttribute, attrValue);
2132 : else
2133 0 : rv = RemoveAttribute(destElement, aAttribute);
2134 :
2135 0 : return rv;
2136 : }
2137 :
2138 : // Objects must be DOM elements
2139 : NS_IMETHODIMP
2140 0 : nsEditor::CloneAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode)
2141 : {
2142 0 : NS_ENSURE_TRUE(aDestNode && aSourceNode, NS_ERROR_NULL_POINTER);
2143 :
2144 0 : nsCOMPtr<nsIDOMElement> destElement = do_QueryInterface(aDestNode);
2145 0 : nsCOMPtr<nsIDOMElement> sourceElement = do_QueryInterface(aSourceNode);
2146 0 : NS_ENSURE_TRUE(destElement && sourceElement, NS_ERROR_NO_INTERFACE);
2147 :
2148 0 : nsCOMPtr<nsIDOMNamedNodeMap> sourceAttributes;
2149 0 : sourceElement->GetAttributes(getter_AddRefs(sourceAttributes));
2150 0 : nsCOMPtr<nsIDOMNamedNodeMap> destAttributes;
2151 0 : destElement->GetAttributes(getter_AddRefs(destAttributes));
2152 0 : NS_ENSURE_TRUE(sourceAttributes && destAttributes, NS_ERROR_FAILURE);
2153 :
2154 0 : nsAutoEditBatch beginBatching(this);
2155 :
2156 : // Use transaction system for undo only if destination
2157 : // is already in the document
2158 0 : nsCOMPtr<nsIDOMNode> p = aDestNode;
2159 0 : nsCOMPtr<nsIDOMNode> rootNode = do_QueryInterface(GetRoot());
2160 0 : NS_ENSURE_TRUE(rootNode, NS_ERROR_NULL_POINTER);
2161 0 : bool destInBody = true;
2162 0 : while (p && p != rootNode)
2163 : {
2164 0 : nsCOMPtr<nsIDOMNode> tmp;
2165 0 : if (NS_FAILED(p->GetParentNode(getter_AddRefs(tmp))) || !tmp)
2166 : {
2167 0 : destInBody = false;
2168 : break;
2169 : }
2170 0 : p = tmp;
2171 : }
2172 :
2173 : PRUint32 sourceCount;
2174 0 : sourceAttributes->GetLength(&sourceCount);
2175 : PRUint32 i, destCount;
2176 0 : destAttributes->GetLength(&destCount);
2177 0 : nsCOMPtr<nsIDOMNode> attrNode;
2178 :
2179 : // Clear existing attributes
2180 0 : for (i = 0; i < destCount; i++)
2181 : {
2182 : // always remove item number 0 (first item in list)
2183 0 : if( NS_SUCCEEDED(destAttributes->Item(0, getter_AddRefs(attrNode))) && attrNode)
2184 : {
2185 0 : nsCOMPtr<nsIDOMAttr> destAttribute = do_QueryInterface(attrNode);
2186 0 : if (destAttribute)
2187 : {
2188 0 : nsAutoString str;
2189 0 : if (NS_SUCCEEDED(destAttribute->GetName(str)))
2190 : {
2191 0 : if (destInBody)
2192 0 : RemoveAttribute(destElement, str);
2193 : else
2194 0 : destElement->RemoveAttribute(str);
2195 : }
2196 : }
2197 : }
2198 : }
2199 :
2200 0 : nsresult result = NS_OK;
2201 :
2202 : // Set just the attributes that the source element has
2203 0 : for (i = 0; i < sourceCount; i++)
2204 : {
2205 0 : if( NS_SUCCEEDED(sourceAttributes->Item(i, getter_AddRefs(attrNode))) && attrNode)
2206 : {
2207 0 : nsCOMPtr<nsIDOMAttr> sourceAttribute = do_QueryInterface(attrNode);
2208 0 : if (sourceAttribute)
2209 : {
2210 0 : nsAutoString sourceAttrName;
2211 0 : if (NS_SUCCEEDED(sourceAttribute->GetName(sourceAttrName)))
2212 : {
2213 0 : nsAutoString sourceAttrValue;
2214 : /*
2215 : Presence of an attribute in the named node map indicates that it was set on the
2216 : element even if it has no value.
2217 : */
2218 0 : if (NS_SUCCEEDED(sourceAttribute->GetValue(sourceAttrValue)))
2219 : {
2220 0 : if (destInBody) {
2221 0 : result = SetAttributeOrEquivalent(destElement, sourceAttrName, sourceAttrValue, false);
2222 : }
2223 : else {
2224 : // the element is not inserted in the document yet, we don't want to put a
2225 : // transaction on the UndoStack
2226 0 : result = SetAttributeOrEquivalent(destElement, sourceAttrName, sourceAttrValue, true);
2227 : }
2228 : } else {
2229 : // Do we ever get here?
2230 : #if DEBUG_cmanske
2231 : printf("Attribute in sourceAttribute has empty value in nsEditor::CloneAttributes()\n");
2232 : #endif
2233 : }
2234 : }
2235 : }
2236 : }
2237 : }
2238 0 : return result;
2239 : }
2240 :
2241 :
2242 0 : NS_IMETHODIMP nsEditor::ScrollSelectionIntoView(bool aScrollToAnchor)
2243 : {
2244 0 : nsCOMPtr<nsISelectionController> selCon;
2245 0 : if (NS_SUCCEEDED(GetSelectionController(getter_AddRefs(selCon))) && selCon)
2246 : {
2247 0 : PRInt16 region = nsISelectionController::SELECTION_FOCUS_REGION;
2248 :
2249 0 : if (aScrollToAnchor)
2250 0 : region = nsISelectionController::SELECTION_ANCHOR_REGION;
2251 :
2252 0 : selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
2253 0 : region, 0);
2254 : }
2255 :
2256 0 : return NS_OK;
2257 : }
2258 :
2259 0 : NS_IMETHODIMP nsEditor::InsertTextImpl(const nsAString& aStringToInsert,
2260 : nsCOMPtr<nsIDOMNode> *aInOutNode,
2261 : PRInt32 *aInOutOffset,
2262 : nsIDOMDocument *aDoc)
2263 : {
2264 : // NOTE: caller *must* have already used nsAutoTxnsConserveSelection stack-based
2265 : // class to turn off txn selection updating. Caller also turned on rules sniffing
2266 : // if desired.
2267 :
2268 : nsresult res;
2269 0 : NS_ENSURE_TRUE(aInOutNode && *aInOutNode && aInOutOffset && aDoc, NS_ERROR_NULL_POINTER);
2270 0 : if (!mInIMEMode && aStringToInsert.IsEmpty()) return NS_OK;
2271 0 : nsCOMPtr<nsIDOMText> nodeAsText = do_QueryInterface(*aInOutNode);
2272 0 : if (!nodeAsText && IsPlaintextEditor()) {
2273 0 : nsCOMPtr<nsIDOMNode> rootNode = do_QueryInterface(GetRoot());
2274 : // In some cases, aInOutNode is the anonymous DIV, and aInOutOffset is 0.
2275 : // To avoid injecting unneeded text nodes, we first look to see if we have
2276 : // one available. In that case, we'll just adjust aInOutNode and aInOutOffset
2277 : // accordingly.
2278 0 : if (*aInOutNode == rootNode && *aInOutOffset == 0) {
2279 0 : nsCOMPtr<nsIDOMNode> possibleTextNode;
2280 0 : res = (*aInOutNode)->GetFirstChild(getter_AddRefs(possibleTextNode));
2281 0 : if (NS_SUCCEEDED(res)) {
2282 0 : nodeAsText = do_QueryInterface(possibleTextNode);
2283 0 : if (nodeAsText) {
2284 0 : *aInOutNode = possibleTextNode;
2285 : }
2286 : }
2287 : }
2288 : // In some other cases, aInOutNode is the anonymous DIV, and aInOutOffset points
2289 : // to the terminating mozBR. In that case, we'll adjust aInOutNode and aInOutOffset
2290 : // to the preceding text node, if any.
2291 0 : if (!nodeAsText && *aInOutNode == rootNode && *aInOutOffset > 0) {
2292 0 : nsCOMPtr<nsIDOMNodeList> children;
2293 0 : res = (*aInOutNode)->GetChildNodes(getter_AddRefs(children));
2294 0 : if (NS_SUCCEEDED(res)) {
2295 0 : nsCOMPtr<nsIDOMNode> possibleMozBRNode;
2296 0 : children->Item(*aInOutOffset, getter_AddRefs(possibleMozBRNode));
2297 0 : if (possibleMozBRNode && nsTextEditUtils::IsMozBR(possibleMozBRNode)) {
2298 0 : nsCOMPtr<nsIDOMNode> possibleTextNode;
2299 0 : res = children->Item(*aInOutOffset - 1, getter_AddRefs(possibleTextNode));
2300 0 : if (NS_SUCCEEDED(res)) {
2301 0 : nodeAsText = do_QueryInterface(possibleTextNode);
2302 0 : if (nodeAsText) {
2303 : PRUint32 length;
2304 0 : res = nodeAsText->GetLength(&length);
2305 0 : if (NS_SUCCEEDED(res)) {
2306 0 : *aInOutOffset = PRInt32(length);
2307 0 : *aInOutNode = possibleTextNode;
2308 : }
2309 : }
2310 : }
2311 : } else {
2312 : // The selection might be at the end of the last textnode child,
2313 : // in which case we can just append to the textnode in question.
2314 0 : nsCOMPtr<nsIDOMNode> possibleTextNode;
2315 0 : res = children->Item(*aInOutOffset - 1, getter_AddRefs(possibleTextNode));
2316 0 : nodeAsText = do_QueryInterface(possibleTextNode);
2317 0 : if (nodeAsText) {
2318 : PRUint32 length;
2319 0 : res = nodeAsText->GetLength(&length);
2320 0 : if (NS_SUCCEEDED(res)) {
2321 0 : *aInOutOffset = PRInt32(length);
2322 0 : *aInOutNode = possibleTextNode;
2323 : }
2324 : }
2325 : }
2326 : }
2327 : }
2328 : // Sometimes, aInOutNode is the mozBR element itself. In that case, we'll
2329 : // adjust the insertion point to the previous text node, if one exists, or
2330 : // to the parent anonymous DIV.
2331 0 : if (nsTextEditUtils::IsMozBR(*aInOutNode) && *aInOutOffset == 0) {
2332 0 : nsCOMPtr<nsIDOMNode> previous;
2333 0 : (*aInOutNode)->GetPreviousSibling(getter_AddRefs(previous));
2334 0 : nodeAsText = do_QueryInterface(previous);
2335 0 : if (nodeAsText) {
2336 : PRUint32 length;
2337 0 : res = nodeAsText->GetLength(&length);
2338 0 : if (NS_SUCCEEDED(res)) {
2339 0 : *aInOutOffset = PRInt32(length);
2340 0 : *aInOutNode = previous;
2341 : }
2342 : } else {
2343 0 : nsCOMPtr<nsIDOMNode> parent;
2344 0 : (*aInOutNode)->GetParentNode(getter_AddRefs(parent));
2345 0 : if (parent == rootNode) {
2346 0 : *aInOutNode = parent;
2347 : }
2348 : }
2349 : }
2350 : }
2351 0 : PRInt32 offset = *aInOutOffset;
2352 0 : if (mInIMEMode)
2353 : {
2354 0 : if (!nodeAsText)
2355 : {
2356 : // create a text node
2357 0 : res = aDoc->CreateTextNode(EmptyString(), getter_AddRefs(nodeAsText));
2358 0 : NS_ENSURE_SUCCESS(res, res);
2359 0 : NS_ENSURE_TRUE(nodeAsText, NS_ERROR_NULL_POINTER);
2360 0 : nsCOMPtr<nsIDOMNode> newNode = do_QueryInterface(nodeAsText);
2361 : // then we insert it into the dom tree
2362 0 : res = InsertNode(newNode, *aInOutNode, offset);
2363 0 : NS_ENSURE_SUCCESS(res, res);
2364 0 : offset = 0;
2365 : }
2366 0 : res = InsertTextIntoTextNodeImpl(aStringToInsert, nodeAsText, offset);
2367 0 : NS_ENSURE_SUCCESS(res, res);
2368 : }
2369 : else
2370 : {
2371 0 : if (nodeAsText)
2372 : {
2373 : // we are inserting text into an existing text node.
2374 0 : res = InsertTextIntoTextNodeImpl(aStringToInsert, nodeAsText, offset);
2375 0 : NS_ENSURE_SUCCESS(res, res);
2376 0 : *aInOutOffset += aStringToInsert.Length();
2377 : }
2378 : else
2379 : {
2380 : // we are inserting text into a non-text node
2381 : // first we have to create a textnode (this also populates it with the text)
2382 0 : res = aDoc->CreateTextNode(aStringToInsert, getter_AddRefs(nodeAsText));
2383 0 : NS_ENSURE_SUCCESS(res, res);
2384 0 : NS_ENSURE_TRUE(nodeAsText, NS_ERROR_NULL_POINTER);
2385 0 : nsCOMPtr<nsIDOMNode> newNode = do_QueryInterface(nodeAsText);
2386 : // then we insert it into the dom tree
2387 0 : res = InsertNode(newNode, *aInOutNode, offset);
2388 0 : NS_ENSURE_SUCCESS(res, res);
2389 0 : *aInOutNode = newNode;
2390 0 : *aInOutOffset = aStringToInsert.Length();
2391 : }
2392 : }
2393 0 : return res;
2394 : }
2395 :
2396 :
2397 0 : nsresult nsEditor::InsertTextIntoTextNodeImpl(const nsAString& aStringToInsert,
2398 : nsIDOMCharacterData *aTextNode,
2399 : PRInt32 aOffset,
2400 : bool aSuppressIME)
2401 : {
2402 0 : nsRefPtr<EditTxn> txn;
2403 0 : nsresult result = NS_OK;
2404 0 : bool isIMETransaction = false;
2405 : // aSuppressIME is used when editor must insert text, yet this text is not
2406 : // part of current ime operation. example: adjusting whitespace around an ime insertion.
2407 0 : if (mIMETextRangeList && mInIMEMode && !aSuppressIME)
2408 : {
2409 0 : if (!mIMETextNode)
2410 : {
2411 0 : mIMETextNode = aTextNode;
2412 0 : mIMETextOffset = aOffset;
2413 : }
2414 : PRUint16 len ;
2415 0 : len = mIMETextRangeList->GetLength();
2416 0 : if (len > 0)
2417 : {
2418 0 : nsCOMPtr<nsIPrivateTextRange> range;
2419 0 : for (PRUint16 i = 0; i < len; i++)
2420 : {
2421 0 : range = mIMETextRangeList->Item(i);
2422 0 : if (range)
2423 : {
2424 : PRUint16 type;
2425 0 : result = range->GetRangeType(&type);
2426 0 : if (NS_SUCCEEDED(result))
2427 : {
2428 0 : if (type == nsIPrivateTextRange::TEXTRANGE_RAWINPUT)
2429 : {
2430 : PRUint16 start, end;
2431 0 : result = range->GetRangeStart(&start);
2432 0 : if (NS_SUCCEEDED(result))
2433 : {
2434 0 : result = range->GetRangeEnd(&end);
2435 0 : if (NS_SUCCEEDED(result))
2436 : {
2437 0 : if (!mPhonetic)
2438 0 : mPhonetic = new nsString();
2439 0 : if (mPhonetic)
2440 : {
2441 0 : nsAutoString tmp(aStringToInsert);
2442 0 : tmp.Mid(*mPhonetic, start, end-start);
2443 : }
2444 : }
2445 : }
2446 : } // if
2447 : }
2448 : } // if
2449 : } // for
2450 : } // if
2451 :
2452 0 : nsRefPtr<IMETextTxn> imeTxn;
2453 0 : result = CreateTxnForIMEText(aStringToInsert, getter_AddRefs(imeTxn));
2454 0 : txn = imeTxn;
2455 0 : isIMETransaction = true;
2456 : }
2457 : else
2458 : {
2459 0 : nsRefPtr<InsertTextTxn> insertTxn;
2460 : result = CreateTxnForInsertText(aStringToInsert, aTextNode, aOffset,
2461 0 : getter_AddRefs(insertTxn));
2462 0 : txn = insertTxn;
2463 : }
2464 0 : NS_ENSURE_SUCCESS(result, result);
2465 :
2466 : // let listeners know what's up
2467 : PRInt32 i;
2468 0 : for (i = 0; i < mActionListeners.Count(); i++)
2469 0 : mActionListeners[i]->WillInsertText(aTextNode, aOffset, aStringToInsert);
2470 :
2471 : // XXX we may not need these view batches anymore. This is handled at a higher level now I believe
2472 0 : BeginUpdateViewBatch();
2473 0 : result = DoTransaction(txn);
2474 0 : EndUpdateViewBatch();
2475 :
2476 0 : mRangeUpdater.SelAdjInsertText(aTextNode, aOffset, aStringToInsert);
2477 :
2478 : // let listeners know what happened
2479 0 : for (i = 0; i < mActionListeners.Count(); i++)
2480 0 : mActionListeners[i]->DidInsertText(aTextNode, aOffset, aStringToInsert, result);
2481 :
2482 : // Added some cruft here for bug 43366. Layout was crashing because we left an
2483 : // empty text node lying around in the document. So I delete empty text nodes
2484 : // caused by IME. I have to mark the IME transaction as "fixed", which means
2485 : // that furure ime txns won't merge with it. This is because we don't want
2486 : // future ime txns trying to put their text into a node that is no longer in
2487 : // the document. This does not break undo/redo, because all these txns are
2488 : // wrapped in a parent PlaceHolder txn, and placeholder txns are already
2489 : // savvy to having multiple ime txns inside them.
2490 :
2491 : // delete empty ime text node if there is one
2492 0 : if (isIMETransaction && mIMETextNode)
2493 : {
2494 : PRUint32 len;
2495 0 : mIMETextNode->GetLength(&len);
2496 0 : if (!len)
2497 : {
2498 0 : DeleteNode(mIMETextNode);
2499 0 : mIMETextNode = nsnull;
2500 0 : static_cast<IMETextTxn*>(txn.get())->MarkFixed(); // mark the ime txn "fixed"
2501 : }
2502 : }
2503 :
2504 0 : return result;
2505 : }
2506 :
2507 :
2508 0 : NS_IMETHODIMP nsEditor::SelectEntireDocument(nsISelection *aSelection)
2509 : {
2510 0 : if (!aSelection) { return NS_ERROR_NULL_POINTER; }
2511 :
2512 0 : nsCOMPtr<nsIDOMElement> rootElement = do_QueryInterface(GetRoot());
2513 0 : if (!rootElement) { return NS_ERROR_NOT_INITIALIZED; }
2514 :
2515 0 : return aSelection->SelectAllChildren(rootElement);
2516 : }
2517 :
2518 :
2519 : nsINode*
2520 0 : nsEditor::GetFirstEditableNode(nsINode* aRoot)
2521 : {
2522 0 : MOZ_ASSERT(aRoot);
2523 :
2524 0 : nsIContent* node = GetLeftmostChild(aRoot);
2525 0 : if (node && !IsEditable(node)) {
2526 0 : node = GetNextNode(node, /* aEditableNode = */ true);
2527 : }
2528 :
2529 0 : return (node != aRoot) ? node : nsnull;
2530 : }
2531 :
2532 :
2533 : NS_IMETHODIMP
2534 0 : nsEditor::NotifyDocumentListeners(TDocumentListenerNotification aNotificationType)
2535 : {
2536 0 : PRInt32 numListeners = mDocStateListeners.Count();
2537 0 : if (!numListeners) // maybe there just aren't any.
2538 0 : return NS_OK;
2539 :
2540 0 : nsCOMArray<nsIDocumentStateListener> listeners(mDocStateListeners);
2541 0 : nsresult rv = NS_OK;
2542 : PRInt32 i;
2543 :
2544 0 : switch (aNotificationType)
2545 : {
2546 : case eDocumentCreated:
2547 0 : for (i = 0; i < numListeners;i++)
2548 : {
2549 0 : rv = listeners[i]->NotifyDocumentCreated();
2550 0 : if (NS_FAILED(rv))
2551 0 : break;
2552 : }
2553 0 : break;
2554 :
2555 : case eDocumentToBeDestroyed:
2556 0 : for (i = 0; i < numListeners;i++)
2557 : {
2558 0 : rv = listeners[i]->NotifyDocumentWillBeDestroyed();
2559 0 : if (NS_FAILED(rv))
2560 0 : break;
2561 : }
2562 0 : break;
2563 :
2564 : case eDocumentStateChanged:
2565 : {
2566 : bool docIsDirty;
2567 0 : rv = GetDocumentModified(&docIsDirty);
2568 0 : NS_ENSURE_SUCCESS(rv, rv);
2569 :
2570 0 : if (docIsDirty == mDocDirtyState)
2571 0 : return NS_OK;
2572 :
2573 0 : mDocDirtyState = (PRInt8)docIsDirty;
2574 :
2575 0 : for (i = 0; i < numListeners;i++)
2576 : {
2577 0 : rv = listeners[i]->NotifyDocumentStateChanged(mDocDirtyState);
2578 0 : if (NS_FAILED(rv))
2579 0 : break;
2580 : }
2581 : }
2582 0 : break;
2583 :
2584 : default:
2585 0 : NS_NOTREACHED("Unknown notification");
2586 : }
2587 :
2588 0 : return rv;
2589 : }
2590 :
2591 :
2592 0 : NS_IMETHODIMP nsEditor::CreateTxnForInsertText(const nsAString & aStringToInsert,
2593 : nsIDOMCharacterData *aTextNode,
2594 : PRInt32 aOffset,
2595 : InsertTextTxn ** aTxn)
2596 : {
2597 0 : NS_ENSURE_TRUE(aTextNode && aTxn, NS_ERROR_NULL_POINTER);
2598 : nsresult rv;
2599 :
2600 0 : nsRefPtr<InsertTextTxn> txn = new InsertTextTxn();
2601 0 : rv = txn->Init(aTextNode, aOffset, aStringToInsert, this);
2602 0 : if (NS_SUCCEEDED(rv))
2603 : {
2604 0 : txn.forget(aTxn);
2605 : }
2606 :
2607 0 : return rv;
2608 : }
2609 :
2610 :
2611 0 : NS_IMETHODIMP nsEditor::DeleteText(nsIDOMCharacterData *aElement,
2612 : PRUint32 aOffset,
2613 : PRUint32 aLength)
2614 : {
2615 0 : nsRefPtr<DeleteTextTxn> txn;
2616 : nsresult result = CreateTxnForDeleteText(aElement, aOffset, aLength,
2617 0 : getter_AddRefs(txn));
2618 0 : nsAutoRules beginRulesSniffing(this, kOpDeleteText, nsIEditor::ePrevious);
2619 0 : if (NS_SUCCEEDED(result))
2620 : {
2621 : // let listeners know what's up
2622 : PRInt32 i;
2623 0 : for (i = 0; i < mActionListeners.Count(); i++)
2624 0 : mActionListeners[i]->WillDeleteText(aElement, aOffset, aLength);
2625 :
2626 0 : result = DoTransaction(txn);
2627 :
2628 : // let listeners know what happened
2629 0 : for (i = 0; i < mActionListeners.Count(); i++)
2630 0 : mActionListeners[i]->DidDeleteText(aElement, aOffset, aLength, result);
2631 : }
2632 0 : return result;
2633 : }
2634 :
2635 :
2636 0 : NS_IMETHODIMP nsEditor::CreateTxnForDeleteText(nsIDOMCharacterData *aElement,
2637 : PRUint32 aOffset,
2638 : PRUint32 aLength,
2639 : DeleteTextTxn **aTxn)
2640 : {
2641 0 : NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
2642 :
2643 0 : nsRefPtr<DeleteTextTxn> txn = new DeleteTextTxn();
2644 :
2645 0 : nsresult rv = txn->Init(this, aElement, aOffset, aLength, &mRangeUpdater);
2646 0 : if (NS_SUCCEEDED(rv))
2647 : {
2648 0 : txn.forget(aTxn);
2649 : }
2650 :
2651 0 : return rv;
2652 : }
2653 :
2654 :
2655 :
2656 :
2657 0 : NS_IMETHODIMP nsEditor::CreateTxnForSplitNode(nsIDOMNode *aNode,
2658 : PRUint32 aOffset,
2659 : SplitElementTxn **aTxn)
2660 : {
2661 0 : NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
2662 :
2663 0 : nsRefPtr<SplitElementTxn> txn = new SplitElementTxn();
2664 :
2665 0 : nsresult rv = txn->Init(this, aNode, aOffset);
2666 0 : if (NS_SUCCEEDED(rv))
2667 : {
2668 0 : txn.forget(aTxn);
2669 : }
2670 :
2671 0 : return rv;
2672 : }
2673 :
2674 0 : NS_IMETHODIMP nsEditor::CreateTxnForJoinNode(nsIDOMNode *aLeftNode,
2675 : nsIDOMNode *aRightNode,
2676 : JoinElementTxn **aTxn)
2677 : {
2678 0 : NS_ENSURE_TRUE(aLeftNode && aRightNode, NS_ERROR_NULL_POINTER);
2679 :
2680 0 : nsRefPtr<JoinElementTxn> txn = new JoinElementTxn();
2681 :
2682 0 : nsresult rv = txn->Init(this, aLeftNode, aRightNode);
2683 0 : if (NS_SUCCEEDED(rv))
2684 : {
2685 0 : txn.forget(aTxn);
2686 : }
2687 :
2688 0 : return rv;
2689 : }
2690 :
2691 :
2692 : // END nsEditor core implementation
2693 :
2694 :
2695 : // BEGIN nsEditor public helper methods
2696 :
2697 : nsresult
2698 0 : nsEditor::SplitNodeImpl(nsIDOMNode * aExistingRightNode,
2699 : PRInt32 aOffset,
2700 : nsIDOMNode* aNewLeftNode,
2701 : nsIDOMNode* aParent)
2702 : {
2703 : #ifdef NS_DEBUG_EDITOR
2704 : if (gNoisy) { printf("SplitNodeImpl: left=%p, right=%p, offset=%d\n", (void*)aNewLeftNode, (void*)aExistingRightNode, aOffset); }
2705 : #endif
2706 :
2707 0 : NS_ASSERTION(((nsnull!=aExistingRightNode) &&
2708 : (nsnull!=aNewLeftNode) &&
2709 : (nsnull!=aParent)),
2710 : "null arg");
2711 : nsresult result;
2712 0 : if ((nsnull!=aExistingRightNode) &&
2713 : (nsnull!=aNewLeftNode) &&
2714 : (nsnull!=aParent))
2715 : {
2716 : // get selection
2717 0 : nsCOMPtr<nsISelection> selection;
2718 0 : result = GetSelection(getter_AddRefs(selection));
2719 0 : NS_ENSURE_SUCCESS(result, result);
2720 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
2721 :
2722 : // remember some selection points
2723 0 : nsCOMPtr<nsIDOMNode> selStartNode, selEndNode;
2724 : PRInt32 selStartOffset, selEndOffset;
2725 0 : result = GetStartNodeAndOffset(selection, getter_AddRefs(selStartNode), &selStartOffset);
2726 0 : if (NS_FAILED(result)) selStartNode = nsnull; // if selection is cleared, remember that
2727 0 : result = GetEndNodeAndOffset(selection, getter_AddRefs(selEndNode), &selEndOffset);
2728 0 : if (NS_FAILED(result)) selStartNode = nsnull; // if selection is cleared, remember that
2729 :
2730 0 : nsCOMPtr<nsIDOMNode> resultNode;
2731 0 : result = aParent->InsertBefore(aNewLeftNode, aExistingRightNode, getter_AddRefs(resultNode));
2732 : //printf(" after insert\n"); content->List(); // DEBUG
2733 0 : if (NS_SUCCEEDED(result))
2734 : {
2735 : // split the children between the 2 nodes
2736 : // at this point, aExistingRightNode has all the children
2737 : // move all the children whose index is < aOffset to aNewLeftNode
2738 0 : if (0<=aOffset) // don't bother unless we're going to move at least one child
2739 : {
2740 : // if it's a text node, just shuffle around some text
2741 0 : nsCOMPtr<nsIDOMCharacterData> rightNodeAsText( do_QueryInterface(aExistingRightNode) );
2742 0 : nsCOMPtr<nsIDOMCharacterData> leftNodeAsText( do_QueryInterface(aNewLeftNode) );
2743 0 : if (leftNodeAsText && rightNodeAsText)
2744 : {
2745 : // fix right node
2746 0 : nsAutoString leftText;
2747 0 : rightNodeAsText->SubstringData(0, aOffset, leftText);
2748 0 : rightNodeAsText->DeleteData(0, aOffset);
2749 : // fix left node
2750 0 : leftNodeAsText->SetData(leftText);
2751 : // moose
2752 : }
2753 : else
2754 : { // otherwise it's an interior node, so shuffle around the children
2755 : // go through list backwards so deletes don't interfere with the iteration
2756 0 : nsCOMPtr<nsIDOMNodeList> childNodes;
2757 0 : result = aExistingRightNode->GetChildNodes(getter_AddRefs(childNodes));
2758 0 : if ((NS_SUCCEEDED(result)) && (childNodes))
2759 : {
2760 0 : PRInt32 i=aOffset-1;
2761 0 : for ( ; ((NS_SUCCEEDED(result)) && (0<=i)); i--)
2762 : {
2763 0 : nsCOMPtr<nsIDOMNode> childNode;
2764 0 : result = childNodes->Item(i, getter_AddRefs(childNode));
2765 0 : if ((NS_SUCCEEDED(result)) && (childNode))
2766 : {
2767 0 : result = aExistingRightNode->RemoveChild(childNode, getter_AddRefs(resultNode));
2768 : //printf(" after remove\n"); content->List(); // DEBUG
2769 0 : if (NS_SUCCEEDED(result))
2770 : {
2771 0 : nsCOMPtr<nsIDOMNode> firstChild;
2772 0 : aNewLeftNode->GetFirstChild(getter_AddRefs(firstChild));
2773 0 : result = aNewLeftNode->InsertBefore(childNode, firstChild, getter_AddRefs(resultNode));
2774 : //printf(" after append\n"); content->List(); // DEBUG
2775 : }
2776 : }
2777 : }
2778 : }
2779 : }
2780 : // handle selection
2781 0 : nsCOMPtr<nsIPresShell> ps = GetPresShell();
2782 0 : if (ps)
2783 0 : ps->FlushPendingNotifications(Flush_Frames);
2784 :
2785 0 : if (GetShouldTxnSetSelection())
2786 : {
2787 : // editor wants us to set selection at split point
2788 0 : selection->Collapse(aNewLeftNode, aOffset);
2789 : }
2790 0 : else if (selStartNode)
2791 : {
2792 : // else adjust the selection if needed. if selStartNode is null, then there was no selection.
2793 : // HACK: this is overly simplified - multi-range selections need more work than this
2794 0 : if (selStartNode.get() == aExistingRightNode)
2795 : {
2796 0 : if (selStartOffset < aOffset)
2797 : {
2798 0 : selStartNode = aNewLeftNode;
2799 : }
2800 : else
2801 : {
2802 0 : selStartOffset -= aOffset;
2803 : }
2804 : }
2805 0 : if (selEndNode.get() == aExistingRightNode)
2806 : {
2807 0 : if (selEndOffset < aOffset)
2808 : {
2809 0 : selEndNode = aNewLeftNode;
2810 : }
2811 : else
2812 : {
2813 0 : selEndOffset -= aOffset;
2814 : }
2815 : }
2816 0 : selection->Collapse(selStartNode,selStartOffset);
2817 0 : selection->Extend(selEndNode,selEndOffset);
2818 : }
2819 : }
2820 0 : }
2821 : }
2822 : else
2823 0 : result = NS_ERROR_INVALID_ARG;
2824 :
2825 0 : return result;
2826 : }
2827 :
2828 : nsresult
2829 0 : nsEditor::JoinNodesImpl(nsIDOMNode * aNodeToKeep,
2830 : nsIDOMNode * aNodeToJoin,
2831 : nsIDOMNode * aParent,
2832 : bool aNodeToKeepIsFirst)
2833 : {
2834 0 : NS_ASSERTION(aNodeToKeep && aNodeToJoin && aParent, "null arg");
2835 0 : nsresult result = NS_OK;
2836 0 : if (aNodeToKeep && aNodeToJoin && aParent)
2837 : {
2838 : // get selection
2839 0 : nsCOMPtr<nsISelection> selection;
2840 0 : GetSelection(getter_AddRefs(selection));
2841 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
2842 :
2843 : // remember some selection points
2844 0 : nsCOMPtr<nsIDOMNode> selStartNode, selEndNode;
2845 : PRInt32 selStartOffset, selEndOffset, joinOffset, keepOffset;
2846 0 : result = GetStartNodeAndOffset(selection, getter_AddRefs(selStartNode), &selStartOffset);
2847 0 : if (NS_FAILED(result)) selStartNode = nsnull;
2848 0 : result = GetEndNodeAndOffset(selection, getter_AddRefs(selEndNode), &selEndOffset);
2849 : // Joe or Kin should comment here on why the following line is not a copy/paste error
2850 0 : if (NS_FAILED(result)) selStartNode = nsnull;
2851 :
2852 0 : nsCOMPtr<nsIDOMNode> leftNode;
2853 0 : if (aNodeToKeepIsFirst)
2854 0 : leftNode = aNodeToKeep;
2855 : else
2856 0 : leftNode = aNodeToJoin;
2857 :
2858 : PRUint32 firstNodeLength;
2859 0 : result = GetLengthOfDOMNode(leftNode, firstNodeLength);
2860 0 : NS_ENSURE_SUCCESS(result, result);
2861 0 : nsCOMPtr<nsIDOMNode> parent;
2862 0 : result = GetNodeLocation(aNodeToJoin, address_of(parent), &joinOffset);
2863 0 : NS_ENSURE_SUCCESS(result, result);
2864 0 : result = GetNodeLocation(aNodeToKeep, address_of(parent), &keepOffset);
2865 0 : NS_ENSURE_SUCCESS(result, result);
2866 :
2867 : // if selection endpoint is between the nodes, remember it as being
2868 : // in the one that is going away instead. This simplifies later selection
2869 : // adjustment logic at end of this method.
2870 0 : if (selStartNode)
2871 : {
2872 0 : if (selStartNode == parent)
2873 : {
2874 0 : if (aNodeToKeepIsFirst)
2875 : {
2876 0 : if ((selStartOffset > keepOffset) && (selStartOffset <= joinOffset))
2877 : {
2878 0 : selStartNode = aNodeToJoin;
2879 0 : selStartOffset = 0;
2880 : }
2881 : }
2882 : else
2883 : {
2884 0 : if ((selStartOffset > joinOffset) && (selStartOffset <= keepOffset))
2885 : {
2886 0 : selStartNode = aNodeToJoin;
2887 0 : selStartOffset = firstNodeLength;
2888 : }
2889 : }
2890 : }
2891 0 : if (selEndNode == parent)
2892 : {
2893 0 : if (aNodeToKeepIsFirst)
2894 : {
2895 0 : if ((selEndOffset > keepOffset) && (selEndOffset <= joinOffset))
2896 : {
2897 0 : selEndNode = aNodeToJoin;
2898 0 : selEndOffset = 0;
2899 : }
2900 : }
2901 : else
2902 : {
2903 0 : if ((selEndOffset > joinOffset) && (selEndOffset <= keepOffset))
2904 : {
2905 0 : selEndNode = aNodeToJoin;
2906 0 : selEndOffset = firstNodeLength;
2907 : }
2908 : }
2909 : }
2910 : }
2911 : // ok, ready to do join now.
2912 : // if it's a text node, just shuffle around some text
2913 0 : nsCOMPtr<nsIDOMCharacterData> keepNodeAsText( do_QueryInterface(aNodeToKeep) );
2914 0 : nsCOMPtr<nsIDOMCharacterData> joinNodeAsText( do_QueryInterface(aNodeToJoin) );
2915 0 : if (keepNodeAsText && joinNodeAsText)
2916 : {
2917 0 : nsAutoString rightText;
2918 0 : nsAutoString leftText;
2919 0 : if (aNodeToKeepIsFirst)
2920 : {
2921 0 : keepNodeAsText->GetData(leftText);
2922 0 : joinNodeAsText->GetData(rightText);
2923 : }
2924 : else
2925 : {
2926 0 : keepNodeAsText->GetData(rightText);
2927 0 : joinNodeAsText->GetData(leftText);
2928 : }
2929 0 : leftText += rightText;
2930 0 : keepNodeAsText->SetData(leftText);
2931 : }
2932 : else
2933 : { // otherwise it's an interior node, so shuffle around the children
2934 0 : nsCOMPtr<nsIDOMNodeList> childNodes;
2935 0 : result = aNodeToJoin->GetChildNodes(getter_AddRefs(childNodes));
2936 0 : if ((NS_SUCCEEDED(result)) && (childNodes))
2937 : {
2938 : PRInt32 i; // must be signed int!
2939 0 : PRUint32 childCount=0;
2940 0 : nsCOMPtr<nsIDOMNode> firstNode; //only used if aNodeToKeepIsFirst is false
2941 0 : childNodes->GetLength(&childCount);
2942 0 : if (!aNodeToKeepIsFirst)
2943 : { // remember the first child in aNodeToKeep, we'll insert all the children of aNodeToJoin in front of it
2944 0 : result = aNodeToKeep->GetFirstChild(getter_AddRefs(firstNode));
2945 : // GetFirstChild returns nsnull firstNode if aNodeToKeep has no children, that's ok.
2946 : }
2947 0 : nsCOMPtr<nsIDOMNode> resultNode;
2948 : // have to go through the list backwards to keep deletes from interfering with iteration
2949 0 : nsCOMPtr<nsIDOMNode> previousChild;
2950 0 : for (i=childCount-1; ((NS_SUCCEEDED(result)) && (0<=i)); i--)
2951 : {
2952 0 : nsCOMPtr<nsIDOMNode> childNode;
2953 0 : result = childNodes->Item(i, getter_AddRefs(childNode));
2954 0 : if ((NS_SUCCEEDED(result)) && (childNode))
2955 : {
2956 0 : if (aNodeToKeepIsFirst)
2957 : { // append children of aNodeToJoin
2958 : //was result = aNodeToKeep->AppendChild(childNode, getter_AddRefs(resultNode));
2959 0 : result = aNodeToKeep->InsertBefore(childNode, previousChild, getter_AddRefs(resultNode));
2960 0 : previousChild = do_QueryInterface(childNode);
2961 : }
2962 : else
2963 : { // prepend children of aNodeToJoin
2964 0 : result = aNodeToKeep->InsertBefore(childNode, firstNode, getter_AddRefs(resultNode));
2965 0 : firstNode = do_QueryInterface(childNode);
2966 : }
2967 : }
2968 : }
2969 : }
2970 0 : else if (!childNodes) {
2971 0 : result = NS_ERROR_NULL_POINTER;
2972 : }
2973 : }
2974 0 : if (NS_SUCCEEDED(result))
2975 : { // delete the extra node
2976 0 : nsCOMPtr<nsIDOMNode> resultNode;
2977 0 : result = aParent->RemoveChild(aNodeToJoin, getter_AddRefs(resultNode));
2978 :
2979 0 : if (GetShouldTxnSetSelection())
2980 : {
2981 : // editor wants us to set selection at join point
2982 0 : selection->Collapse(aNodeToKeep, firstNodeLength);
2983 : }
2984 0 : else if (selStartNode)
2985 : {
2986 : // and adjust the selection if needed
2987 : // HACK: this is overly simplified - multi-range selections need more work than this
2988 0 : bool bNeedToAdjust = false;
2989 :
2990 : // check to see if we joined nodes where selection starts
2991 0 : if (selStartNode.get() == aNodeToJoin)
2992 : {
2993 0 : bNeedToAdjust = true;
2994 0 : selStartNode = aNodeToKeep;
2995 0 : if (aNodeToKeepIsFirst)
2996 : {
2997 0 : selStartOffset += firstNodeLength;
2998 : }
2999 : }
3000 0 : else if ((selStartNode.get() == aNodeToKeep) && !aNodeToKeepIsFirst)
3001 : {
3002 0 : bNeedToAdjust = true;
3003 0 : selStartOffset += firstNodeLength;
3004 : }
3005 :
3006 : // check to see if we joined nodes where selection ends
3007 0 : if (selEndNode.get() == aNodeToJoin)
3008 : {
3009 0 : bNeedToAdjust = true;
3010 0 : selEndNode = aNodeToKeep;
3011 0 : if (aNodeToKeepIsFirst)
3012 : {
3013 0 : selEndOffset += firstNodeLength;
3014 : }
3015 : }
3016 0 : else if ((selEndNode.get() == aNodeToKeep) && !aNodeToKeepIsFirst)
3017 : {
3018 0 : bNeedToAdjust = true;
3019 0 : selEndOffset += firstNodeLength;
3020 : }
3021 :
3022 : // adjust selection if needed
3023 0 : if (bNeedToAdjust)
3024 : {
3025 0 : selection->Collapse(selStartNode,selStartOffset);
3026 0 : selection->Extend(selEndNode,selEndOffset);
3027 : }
3028 : }
3029 0 : }
3030 : }
3031 : else
3032 0 : result = NS_ERROR_INVALID_ARG;
3033 :
3034 0 : return result;
3035 : }
3036 :
3037 :
3038 : nsresult
3039 0 : nsEditor::GetChildOffset(nsIDOMNode *aChild, nsIDOMNode *aParent, PRInt32 &aOffset)
3040 : {
3041 0 : NS_ASSERTION((aChild && aParent), "bad args");
3042 :
3043 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(aParent);
3044 0 : nsCOMPtr<nsIContent> cChild = do_QueryInterface(aChild);
3045 0 : NS_ENSURE_TRUE(cChild && content, NS_ERROR_NULL_POINTER);
3046 :
3047 0 : aOffset = content->IndexOf(cChild);
3048 :
3049 0 : return NS_OK;
3050 : }
3051 :
3052 : nsresult
3053 0 : nsEditor::GetNodeLocation(nsIDOMNode *inChild, nsCOMPtr<nsIDOMNode> *outParent, PRInt32 *outOffset)
3054 : {
3055 0 : NS_ASSERTION((inChild && outParent && outOffset), "bad args");
3056 0 : nsresult result = NS_ERROR_NULL_POINTER;
3057 0 : if (inChild && outParent && outOffset)
3058 : {
3059 0 : result = inChild->GetParentNode(getter_AddRefs(*outParent));
3060 0 : if ((NS_SUCCEEDED(result)) && (*outParent))
3061 : {
3062 0 : result = GetChildOffset(inChild, *outParent, *outOffset);
3063 : }
3064 : }
3065 0 : return result;
3066 : }
3067 :
3068 : // returns the number of things inside aNode.
3069 : // If aNode is text, returns number of characters. If not, returns number of children nodes.
3070 : nsresult
3071 0 : nsEditor::GetLengthOfDOMNode(nsIDOMNode *aNode, PRUint32 &aCount)
3072 : {
3073 0 : aCount = 0;
3074 0 : if (!aNode) { return NS_ERROR_NULL_POINTER; }
3075 0 : nsresult result=NS_OK;
3076 0 : nsCOMPtr<nsIDOMCharacterData>nodeAsChar = do_QueryInterface(aNode);
3077 0 : if (nodeAsChar) {
3078 0 : nodeAsChar->GetLength(&aCount);
3079 : }
3080 : else
3081 : {
3082 : bool hasChildNodes;
3083 0 : aNode->HasChildNodes(&hasChildNodes);
3084 0 : if (hasChildNodes)
3085 : {
3086 0 : nsCOMPtr<nsIDOMNodeList>nodeList;
3087 0 : result = aNode->GetChildNodes(getter_AddRefs(nodeList));
3088 0 : if (NS_SUCCEEDED(result) && nodeList) {
3089 0 : nodeList->GetLength(&aCount);
3090 : }
3091 : }
3092 : }
3093 0 : return result;
3094 : }
3095 :
3096 :
3097 : nsresult
3098 0 : nsEditor::GetPriorNode(nsIDOMNode *aParentNode,
3099 : PRInt32 aOffset,
3100 : bool aEditableNode,
3101 : nsCOMPtr<nsIDOMNode> *aResultNode,
3102 : bool bNoBlockCrossing,
3103 : nsIContent *aActiveEditorRoot)
3104 : {
3105 : // just another version of GetPriorNode that takes a {parent, offset}
3106 : // instead of a node
3107 0 : if (!aParentNode || !aResultNode) { return NS_ERROR_NULL_POINTER; }
3108 0 : *aResultNode = nsnull;
3109 :
3110 : // if we are at beginning of node, or it is a textnode, then just look before it
3111 0 : if (!aOffset || IsTextNode(aParentNode))
3112 : {
3113 0 : if (bNoBlockCrossing && IsBlockNode(aParentNode))
3114 : {
3115 : // if we aren't allowed to cross blocks, don't look before this block
3116 0 : return NS_OK;
3117 : }
3118 : return GetPriorNode(aParentNode, aEditableNode, aResultNode,
3119 0 : bNoBlockCrossing, aActiveEditorRoot);
3120 : }
3121 :
3122 : // else look before the child at 'aOffset'
3123 0 : nsCOMPtr<nsIDOMNode> child = GetChildAt(aParentNode, aOffset);
3124 0 : if (child)
3125 : return GetPriorNode(child, aEditableNode, aResultNode, bNoBlockCrossing,
3126 0 : aActiveEditorRoot);
3127 :
3128 : // unless there isn't one, in which case we are at the end of the node
3129 : // and want the deep-right child.
3130 0 : *aResultNode = GetRightmostChild(aParentNode, bNoBlockCrossing);
3131 0 : if (!*aResultNode || !aEditableNode || IsEditable(*aResultNode))
3132 0 : return NS_OK;
3133 :
3134 : // restart the search from the non-editable node we just found
3135 0 : nsCOMPtr<nsIDOMNode> notEditableNode = *aResultNode;
3136 : return GetPriorNode(notEditableNode, aEditableNode, aResultNode,
3137 0 : bNoBlockCrossing, aActiveEditorRoot);
3138 : }
3139 :
3140 :
3141 : nsresult
3142 0 : nsEditor::GetNextNode(nsIDOMNode *aParentNode,
3143 : PRInt32 aOffset,
3144 : bool aEditableNode,
3145 : nsCOMPtr<nsIDOMNode> *aResultNode,
3146 : bool bNoBlockCrossing,
3147 : nsIContent *aActiveEditorRoot)
3148 : {
3149 : // just another version of GetNextNode that takes a {parent, offset}
3150 : // instead of a node
3151 0 : if (!aParentNode || !aResultNode) { return NS_ERROR_NULL_POINTER; }
3152 :
3153 0 : *aResultNode = nsnull;
3154 :
3155 : // if aParentNode is a text node, use its location instead
3156 0 : if (IsTextNode(aParentNode))
3157 : {
3158 0 : nsCOMPtr<nsIDOMNode> parent;
3159 0 : nsEditor::GetNodeLocation(aParentNode, address_of(parent), &aOffset);
3160 0 : aParentNode = parent;
3161 0 : aOffset++; // _after_ the text node
3162 : }
3163 : // look at the child at 'aOffset'
3164 0 : nsCOMPtr<nsIDOMNode> child = GetChildAt(aParentNode, aOffset);
3165 0 : if (child)
3166 : {
3167 0 : if (bNoBlockCrossing && IsBlockNode(child))
3168 : {
3169 0 : *aResultNode = child; // return this block
3170 0 : return NS_OK;
3171 : }
3172 0 : *aResultNode = GetLeftmostChild(child, bNoBlockCrossing);
3173 0 : if (!*aResultNode)
3174 : {
3175 0 : *aResultNode = child;
3176 0 : return NS_OK;
3177 : }
3178 0 : if (!IsDescendantOfBody(*aResultNode))
3179 : {
3180 0 : *aResultNode = nsnull;
3181 0 : return NS_OK;
3182 : }
3183 :
3184 0 : if (!aEditableNode || IsEditable(*aResultNode))
3185 0 : return NS_OK;
3186 :
3187 : // restart the search from the non-editable node we just found
3188 0 : nsCOMPtr<nsIDOMNode> notEditableNode = do_QueryInterface(*aResultNode);
3189 : return GetNextNode(notEditableNode, aEditableNode, aResultNode,
3190 0 : bNoBlockCrossing, aActiveEditorRoot);
3191 : }
3192 :
3193 : // unless there isn't one, in which case we are at the end of the node
3194 : // and want the next one.
3195 0 : if (bNoBlockCrossing && IsBlockNode(aParentNode))
3196 : {
3197 : // don't cross out of parent block
3198 0 : return NS_OK;
3199 : }
3200 : return GetNextNode(aParentNode, aEditableNode, aResultNode, bNoBlockCrossing,
3201 0 : aActiveEditorRoot);
3202 : }
3203 :
3204 :
3205 : nsresult
3206 0 : nsEditor::GetPriorNode(nsIDOMNode *aCurrentNode,
3207 : bool aEditableNode,
3208 : nsCOMPtr<nsIDOMNode> *aResultNode,
3209 : bool bNoBlockCrossing,
3210 : nsIContent *aActiveEditorRoot)
3211 : {
3212 0 : if (!aCurrentNode || !aResultNode) { return NS_ERROR_NULL_POINTER; }
3213 :
3214 0 : nsCOMPtr<nsINode> currentNode = do_QueryInterface(aCurrentNode);
3215 :
3216 0 : if (!IsDescendantOfBody(currentNode) ||
3217 : (aActiveEditorRoot &&
3218 : !nsContentUtils::ContentIsDescendantOf(currentNode,
3219 0 : aActiveEditorRoot))) {
3220 0 : *aResultNode = nsnull;
3221 0 : return NS_OK;
3222 : }
3223 :
3224 : *aResultNode =
3225 : do_QueryInterface(FindNode(currentNode, false, aEditableNode,
3226 0 : bNoBlockCrossing, aActiveEditorRoot));
3227 0 : return NS_OK;
3228 : }
3229 :
3230 : nsIContent*
3231 0 : nsEditor::FindNextLeafNode(nsINode *aCurrentNode,
3232 : bool aGoForward,
3233 : bool bNoBlockCrossing,
3234 : nsIContent *aActiveEditorRoot)
3235 : {
3236 : // called only by GetPriorNode so we don't need to check params.
3237 0 : NS_PRECONDITION(IsDescendantOfBody(aCurrentNode) && !IsRootNode(aCurrentNode) &&
3238 : (!aActiveEditorRoot ||
3239 : nsContentUtils::ContentIsDescendantOf(aCurrentNode,
3240 : aActiveEditorRoot)),
3241 : "Bogus arguments");
3242 :
3243 0 : nsINode* cur = aCurrentNode;
3244 0 : for (;;) {
3245 : // if aCurrentNode has a sibling in the right direction, return
3246 : // that sibling's closest child (or itself if it has no children)
3247 : nsIContent* sibling =
3248 0 : aGoForward ? cur->GetNextSibling() : cur->GetPreviousSibling();
3249 0 : if (sibling) {
3250 0 : if (bNoBlockCrossing && IsBlockNode(sibling)) {
3251 : // don't look inside prevsib, since it is a block
3252 0 : return sibling;
3253 : }
3254 : nsIContent *leaf =
3255 0 : aGoForward ? GetLeftmostChild(sibling, bNoBlockCrossing) :
3256 0 : GetRightmostChild(sibling, bNoBlockCrossing);
3257 0 : if (!leaf) {
3258 0 : return sibling;
3259 : }
3260 :
3261 0 : return leaf;
3262 : }
3263 :
3264 0 : nsINode *parent = cur->GetNodeParent();
3265 0 : if (!parent) {
3266 0 : return nsnull;
3267 : }
3268 :
3269 0 : NS_ASSERTION(IsDescendantOfBody(parent),
3270 : "We started with a proper descendant of root, and should stop "
3271 : "if we ever hit the root, so we better have a descendant of "
3272 : "root now!");
3273 0 : if (IsRootNode(parent) ||
3274 0 : (bNoBlockCrossing && IsBlockNode(parent)) ||
3275 : parent == aActiveEditorRoot) {
3276 0 : return nsnull;
3277 : }
3278 :
3279 0 : cur = parent;
3280 : }
3281 :
3282 : NS_NOTREACHED("What part of for(;;) do you not understand?");
3283 : return nsnull;
3284 : }
3285 :
3286 : nsresult
3287 0 : nsEditor::GetNextNode(nsIDOMNode* aCurrentNode,
3288 : bool aEditableNode,
3289 : nsCOMPtr<nsIDOMNode> *aResultNode,
3290 : bool bNoBlockCrossing,
3291 : nsIContent* aActiveEditorRoot)
3292 : {
3293 0 : nsCOMPtr<nsINode> currentNode = do_QueryInterface(aCurrentNode);
3294 0 : if (!currentNode || !aResultNode) {
3295 0 : return NS_ERROR_NULL_POINTER;
3296 : }
3297 :
3298 : *aResultNode = do_QueryInterface(GetNextNode(currentNode, aEditableNode,
3299 : bNoBlockCrossing,
3300 0 : aActiveEditorRoot));
3301 0 : return NS_OK;
3302 : }
3303 :
3304 : nsIContent*
3305 0 : nsEditor::GetNextNode(nsINode* aCurrentNode,
3306 : bool aEditableNode,
3307 : bool bNoBlockCrossing,
3308 : nsIContent* aActiveEditorRoot)
3309 : {
3310 0 : MOZ_ASSERT(aCurrentNode);
3311 :
3312 0 : if (!IsDescendantOfBody(aCurrentNode) ||
3313 : (aActiveEditorRoot &&
3314 : !nsContentUtils::ContentIsDescendantOf(aCurrentNode,
3315 0 : aActiveEditorRoot))) {
3316 0 : return nsnull;
3317 : }
3318 :
3319 : return FindNode(aCurrentNode, true, aEditableNode, bNoBlockCrossing,
3320 0 : aActiveEditorRoot);
3321 : }
3322 :
3323 : nsIContent*
3324 0 : nsEditor::FindNode(nsINode *aCurrentNode,
3325 : bool aGoForward,
3326 : bool aEditableNode,
3327 : bool bNoBlockCrossing,
3328 : nsIContent *aActiveEditorRoot)
3329 : {
3330 0 : if (IsRootNode(aCurrentNode) || aCurrentNode == aActiveEditorRoot)
3331 : {
3332 : // Don't allow traversal above the root node! This helps
3333 : // prevent us from accidentally editing browser content
3334 : // when the editor is in a text widget.
3335 :
3336 0 : return nsnull;
3337 : }
3338 :
3339 : nsIContent* candidate =
3340 : FindNextLeafNode(aCurrentNode, aGoForward, bNoBlockCrossing,
3341 0 : aActiveEditorRoot);
3342 :
3343 0 : if (!candidate) {
3344 0 : return nsnull;
3345 : }
3346 :
3347 0 : if (!aEditableNode || IsEditable(candidate)) {
3348 0 : return candidate;
3349 : }
3350 :
3351 : return FindNode(candidate, aGoForward, aEditableNode, bNoBlockCrossing,
3352 0 : aActiveEditorRoot);
3353 : }
3354 :
3355 : already_AddRefed<nsIDOMNode>
3356 0 : nsEditor::GetRightmostChild(nsIDOMNode *aCurrentNode,
3357 : bool bNoBlockCrossing)
3358 : {
3359 0 : NS_ENSURE_TRUE(aCurrentNode, nsnull);
3360 0 : nsCOMPtr<nsIDOMNode> resultNode, temp = aCurrentNode;
3361 : bool hasChildren;
3362 0 : aCurrentNode->HasChildNodes(&hasChildren);
3363 0 : while (hasChildren) {
3364 0 : temp->GetLastChild(getter_AddRefs(resultNode));
3365 0 : if (resultNode) {
3366 0 : if (bNoBlockCrossing && IsBlockNode(resultNode)) {
3367 0 : return resultNode.forget();
3368 : }
3369 0 : resultNode->HasChildNodes(&hasChildren);
3370 0 : temp = resultNode;
3371 : } else {
3372 0 : hasChildren = false;
3373 : }
3374 : }
3375 :
3376 0 : return resultNode.forget();
3377 : }
3378 :
3379 : nsIContent*
3380 0 : nsEditor::GetRightmostChild(nsINode *aCurrentNode,
3381 : bool bNoBlockCrossing)
3382 : {
3383 0 : NS_ENSURE_TRUE(aCurrentNode, nsnull);
3384 0 : nsIContent *cur = aCurrentNode->GetLastChild();
3385 0 : if (!cur) {
3386 0 : return nsnull;
3387 : }
3388 0 : for (;;) {
3389 0 : if (bNoBlockCrossing && IsBlockNode(cur)) {
3390 0 : return cur;
3391 : }
3392 0 : nsIContent* next = cur->GetLastChild();
3393 0 : if (!next) {
3394 0 : return cur;
3395 : }
3396 0 : cur = next;
3397 : }
3398 :
3399 : NS_NOTREACHED("What part of for(;;) do you not understand?");
3400 : return nsnull;
3401 : }
3402 :
3403 : nsIContent*
3404 0 : nsEditor::GetLeftmostChild(nsINode *aCurrentNode,
3405 : bool bNoBlockCrossing)
3406 : {
3407 0 : NS_ENSURE_TRUE(aCurrentNode, nsnull);
3408 0 : nsIContent *cur = aCurrentNode->GetFirstChild();
3409 0 : if (!cur) {
3410 0 : return nsnull;
3411 : }
3412 0 : for (;;) {
3413 0 : if (bNoBlockCrossing && IsBlockNode(cur)) {
3414 0 : return cur;
3415 : }
3416 0 : nsIContent *next = cur->GetFirstChild();
3417 0 : if (!next) {
3418 0 : return cur;
3419 : }
3420 0 : cur = next;
3421 : }
3422 :
3423 : NS_NOTREACHED("What part of for(;;) do you not understand?");
3424 : return nsnull;
3425 : }
3426 :
3427 : already_AddRefed<nsIDOMNode>
3428 0 : nsEditor::GetLeftmostChild(nsIDOMNode *aCurrentNode,
3429 : bool bNoBlockCrossing)
3430 : {
3431 0 : NS_ENSURE_TRUE(aCurrentNode, nsnull);
3432 0 : nsCOMPtr<nsIDOMNode> resultNode, temp = aCurrentNode;
3433 : bool hasChildren;
3434 0 : aCurrentNode->HasChildNodes(&hasChildren);
3435 0 : while (hasChildren) {
3436 0 : temp->GetFirstChild(getter_AddRefs(resultNode));
3437 0 : if (resultNode) {
3438 0 : if (bNoBlockCrossing && IsBlockNode(resultNode)) {
3439 0 : return resultNode.forget();
3440 : }
3441 0 : resultNode->HasChildNodes(&hasChildren);
3442 0 : temp = resultNode;
3443 : } else {
3444 0 : hasChildren = false;
3445 : }
3446 : }
3447 :
3448 0 : return resultNode.forget();
3449 : }
3450 :
3451 : bool
3452 0 : nsEditor::IsBlockNode(nsIDOMNode *aNode)
3453 : {
3454 : // stub to be overridden in nsHTMLEditor.
3455 : // screwing around with the class hierarchy here in order
3456 : // to not duplicate the code in GetNextNode/GetPrevNode
3457 : // across both nsEditor/nsHTMLEditor.
3458 0 : return false;
3459 : }
3460 :
3461 : bool
3462 0 : nsEditor::IsBlockNode(nsINode *aNode)
3463 : {
3464 : // stub to be overridden in nsHTMLEditor.
3465 : // screwing around with the class hierarchy here in order
3466 : // to not duplicate the code in GetNextNode/GetPrevNode
3467 : // across both nsEditor/nsHTMLEditor.
3468 0 : return false;
3469 : }
3470 :
3471 : bool
3472 0 : nsEditor::CanContainTag(nsIDOMNode* aParent, const nsAString &aChildTag)
3473 : {
3474 0 : nsCOMPtr<nsIDOMElement> parentElement = do_QueryInterface(aParent);
3475 0 : NS_ENSURE_TRUE(parentElement, false);
3476 :
3477 0 : nsAutoString parentStringTag;
3478 0 : parentElement->GetTagName(parentStringTag);
3479 0 : return TagCanContainTag(parentStringTag, aChildTag);
3480 : }
3481 :
3482 : bool
3483 0 : nsEditor::TagCanContain(const nsAString &aParentTag, nsIDOMNode* aChild)
3484 : {
3485 0 : nsAutoString childStringTag;
3486 :
3487 0 : if (IsTextNode(aChild))
3488 : {
3489 0 : childStringTag.AssignLiteral("#text");
3490 : }
3491 : else
3492 : {
3493 0 : nsCOMPtr<nsIDOMElement> childElement = do_QueryInterface(aChild);
3494 0 : NS_ENSURE_TRUE(childElement, false);
3495 0 : childElement->GetTagName(childStringTag);
3496 : }
3497 0 : return TagCanContainTag(aParentTag, childStringTag);
3498 : }
3499 :
3500 : bool
3501 0 : nsEditor::TagCanContainTag(const nsAString &aParentTag, const nsAString &aChildTag)
3502 : {
3503 0 : return true;
3504 : }
3505 :
3506 : bool
3507 0 : nsEditor::IsRootNode(nsIDOMNode *inNode)
3508 : {
3509 0 : NS_ENSURE_TRUE(inNode, false);
3510 :
3511 0 : nsCOMPtr<nsIDOMNode> rootNode = do_QueryInterface(GetRoot());
3512 :
3513 0 : return inNode == rootNode;
3514 : }
3515 :
3516 : bool
3517 0 : nsEditor::IsRootNode(nsINode *inNode)
3518 : {
3519 0 : NS_ENSURE_TRUE(inNode, false);
3520 :
3521 0 : nsCOMPtr<nsINode> rootNode = GetRoot();
3522 :
3523 0 : return inNode == rootNode;
3524 : }
3525 :
3526 : bool
3527 0 : nsEditor::IsDescendantOfBody(nsIDOMNode *inNode)
3528 : {
3529 0 : nsCOMPtr<nsINode> node = do_QueryInterface(inNode);
3530 0 : return IsDescendantOfBody(node);
3531 : }
3532 :
3533 : bool
3534 0 : nsEditor::IsDescendantOfBody(nsINode *inNode)
3535 : {
3536 0 : NS_ENSURE_TRUE(inNode, false);
3537 0 : nsCOMPtr<nsIContent> root = GetRoot();
3538 0 : NS_ENSURE_TRUE(root, false);
3539 :
3540 0 : return nsContentUtils::ContentIsDescendantOf(inNode, root);
3541 : }
3542 :
3543 : bool
3544 0 : nsEditor::IsContainer(nsIDOMNode *aNode)
3545 : {
3546 0 : return aNode ? true : false;
3547 : }
3548 :
3549 : bool
3550 0 : nsEditor::IsTextInDirtyFrameVisible(nsIContent *aNode)
3551 : {
3552 : // virtual method
3553 : //
3554 : // If this is a simple non-html editor,
3555 : // the best we can do is to assume it's visible.
3556 :
3557 0 : return true;
3558 : }
3559 :
3560 : static inline bool
3561 0 : IsElementVisible(dom::Element* aElement)
3562 : {
3563 0 : if (aElement->GetPrimaryFrame()) {
3564 : // It's visible, for our purposes
3565 0 : return true;
3566 : }
3567 :
3568 0 : nsIContent *cur = aElement;
3569 0 : for (; ;) {
3570 : // Walk up the tree looking for the nearest ancestor with a frame.
3571 : // The state of the child right below it will determine whether
3572 : // we might possibly have a frame or not.
3573 0 : bool haveLazyBitOnChild = cur->HasFlag(NODE_NEEDS_FRAME);
3574 0 : cur = cur->GetFlattenedTreeParent();
3575 0 : if (!cur) {
3576 0 : if (!haveLazyBitOnChild) {
3577 : // None of our ancestors have lazy bits set, so we shouldn't
3578 : // have a frame
3579 0 : return false;
3580 : }
3581 :
3582 : // The root has a lazy frame construction bit. We need to check
3583 : // our style.
3584 0 : break;
3585 : }
3586 :
3587 0 : if (cur->GetPrimaryFrame()) {
3588 0 : if (!haveLazyBitOnChild) {
3589 : // Our ancestor directly under |cur| doesn't have lazy bits;
3590 : // that means we won't get a frame
3591 0 : return false;
3592 : }
3593 :
3594 0 : if (cur->GetPrimaryFrame()->IsLeaf()) {
3595 : // Nothing under here will ever get frames
3596 0 : return false;
3597 : }
3598 :
3599 : // Otherwise, we might end up with a frame when that lazy bit is
3600 : // processed. Figure out our actual style.
3601 0 : break;
3602 : }
3603 : }
3604 :
3605 : // Now it might be that we have no frame because we're in a
3606 : // display:none subtree, or it might be that we're just dealing with
3607 : // lazy frame construction and it hasn't happened yet. Check which
3608 : // one it is.
3609 : nsRefPtr<nsStyleContext> styleContext =
3610 : nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement,
3611 0 : nsnull, nsnull);
3612 0 : if (styleContext) {
3613 0 : return styleContext->GetStyleDisplay()->mDisplay != NS_STYLE_DISPLAY_NONE;
3614 : }
3615 0 : return false;
3616 : }
3617 :
3618 : bool
3619 0 : nsEditor::IsEditable(nsIDOMNode *aNode)
3620 : {
3621 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
3622 0 : return IsEditable(content);
3623 : }
3624 :
3625 : bool
3626 0 : nsEditor::IsEditable(nsIContent *aNode)
3627 : {
3628 0 : NS_ENSURE_TRUE(aNode, false);
3629 :
3630 0 : if (IsMozEditorBogusNode(aNode) || !IsModifiableNode(aNode)) return false;
3631 :
3632 : // see if it has a frame. If so, we'll edit it.
3633 : // special case for textnodes: frame must have width.
3634 0 : if (aNode->IsElement() && !IsElementVisible(aNode->AsElement())) {
3635 : // If the element has no frame, it's not editable. Note that we
3636 : // need to check IsElement() here, because some of our tests
3637 : // rely on frameless textnodes being visible.
3638 0 : return false;
3639 : }
3640 0 : switch (aNode->NodeType()) {
3641 : case nsIDOMNode::ELEMENT_NODE:
3642 0 : return true; // not a text node; not invisible
3643 : case nsIDOMNode::TEXT_NODE:
3644 0 : return IsTextInDirtyFrameVisible(aNode);
3645 : default:
3646 0 : return false;
3647 : }
3648 : }
3649 :
3650 : bool
3651 0 : nsEditor::IsMozEditorBogusNode(nsIContent *element)
3652 : {
3653 : return element &&
3654 : element->AttrValueIs(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom,
3655 0 : kMOZEditorBogusNodeValue, eCaseMatters);
3656 : }
3657 :
3658 : nsresult
3659 0 : nsEditor::CountEditableChildren(nsIDOMNode *aNode, PRUint32 &outCount)
3660 : {
3661 0 : outCount = 0;
3662 0 : if (!aNode) { return NS_ERROR_NULL_POINTER; }
3663 0 : nsresult res=NS_OK;
3664 : bool hasChildNodes;
3665 0 : aNode->HasChildNodes(&hasChildNodes);
3666 0 : if (hasChildNodes)
3667 : {
3668 0 : nsCOMPtr<nsIDOMNodeList>nodeList;
3669 0 : res = aNode->GetChildNodes(getter_AddRefs(nodeList));
3670 0 : if (NS_SUCCEEDED(res) && nodeList)
3671 : {
3672 : PRUint32 i;
3673 : PRUint32 len;
3674 0 : nodeList->GetLength(&len);
3675 0 : for (i=0 ; i<len; i++)
3676 : {
3677 0 : nsCOMPtr<nsIDOMNode> child;
3678 0 : res = nodeList->Item((PRInt32)i, getter_AddRefs(child));
3679 0 : if ((NS_SUCCEEDED(res)) && (child))
3680 : {
3681 0 : if (IsEditable(child))
3682 : {
3683 0 : outCount++;
3684 : }
3685 : }
3686 : }
3687 : }
3688 0 : else if (!nodeList)
3689 0 : res = NS_ERROR_NULL_POINTER;
3690 : }
3691 0 : return res;
3692 : }
3693 :
3694 : //END nsEditor static utility methods
3695 :
3696 :
3697 0 : NS_IMETHODIMP nsEditor::IncrementModificationCount(PRInt32 inNumMods)
3698 : {
3699 0 : PRUint32 oldModCount = mModCount;
3700 :
3701 0 : mModCount += inNumMods;
3702 :
3703 0 : if ((oldModCount == 0 && mModCount != 0)
3704 : || (oldModCount != 0 && mModCount == 0))
3705 0 : NotifyDocumentListeners(eDocumentStateChanged);
3706 0 : return NS_OK;
3707 : }
3708 :
3709 :
3710 0 : NS_IMETHODIMP nsEditor::GetModificationCount(PRInt32 *outModCount)
3711 : {
3712 0 : NS_ENSURE_ARG_POINTER(outModCount);
3713 0 : *outModCount = mModCount;
3714 0 : return NS_OK;
3715 : }
3716 :
3717 :
3718 0 : NS_IMETHODIMP nsEditor::ResetModificationCount()
3719 : {
3720 0 : bool doNotify = (mModCount != 0);
3721 :
3722 0 : mModCount = 0;
3723 :
3724 0 : if (doNotify)
3725 0 : NotifyDocumentListeners(eDocumentStateChanged);
3726 0 : return NS_OK;
3727 : }
3728 :
3729 : //END nsEditor Private methods
3730 :
3731 :
3732 :
3733 : ///////////////////////////////////////////////////////////////////////////
3734 : // GetTag: digs out the atom for the tag of this node
3735 : //
3736 : nsIAtom *
3737 0 : nsEditor::GetTag(nsIDOMNode *aNode)
3738 : {
3739 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
3740 :
3741 0 : if (!content)
3742 : {
3743 0 : NS_ASSERTION(aNode, "null node passed to nsEditor::Tag()");
3744 :
3745 0 : return nsnull;
3746 : }
3747 :
3748 0 : return content->Tag();
3749 : }
3750 :
3751 :
3752 : ///////////////////////////////////////////////////////////////////////////
3753 : // GetTagString: digs out string for the tag of this node
3754 : //
3755 : nsresult
3756 0 : nsEditor::GetTagString(nsIDOMNode *aNode, nsAString& outString)
3757 : {
3758 0 : if (!aNode)
3759 : {
3760 0 : NS_NOTREACHED("null node passed to nsEditor::GetTag()");
3761 0 : return NS_ERROR_NULL_POINTER;
3762 : }
3763 :
3764 0 : nsIAtom *atom = GetTag(aNode);
3765 0 : if (!atom)
3766 : {
3767 0 : return NS_ERROR_FAILURE;
3768 : }
3769 :
3770 0 : atom->ToString(outString);
3771 0 : return NS_OK;
3772 : }
3773 :
3774 :
3775 : ///////////////////////////////////////////////////////////////////////////
3776 : // NodesSameType: do these nodes have the same tag?
3777 : //
3778 : bool
3779 0 : nsEditor::NodesSameType(nsIDOMNode *aNode1, nsIDOMNode *aNode2)
3780 : {
3781 0 : if (!aNode1 || !aNode2)
3782 : {
3783 0 : NS_NOTREACHED("null node passed to nsEditor::NodesSameType()");
3784 0 : return false;
3785 : }
3786 :
3787 0 : return GetTag(aNode1) == GetTag(aNode2);
3788 : }
3789 :
3790 :
3791 : // IsTextOrElementNode: true if node of dom type element or text
3792 : //
3793 : bool
3794 0 : nsEditor::IsTextOrElementNode(nsIDOMNode *aNode)
3795 : {
3796 0 : if (!aNode)
3797 : {
3798 0 : NS_NOTREACHED("null node passed to IsTextOrElementNode()");
3799 0 : return false;
3800 : }
3801 :
3802 : PRUint16 nodeType;
3803 0 : aNode->GetNodeType(&nodeType);
3804 0 : return ((nodeType == nsIDOMNode::ELEMENT_NODE) || (nodeType == nsIDOMNode::TEXT_NODE));
3805 : }
3806 :
3807 :
3808 :
3809 : ///////////////////////////////////////////////////////////////////////////
3810 : // IsTextNode: true if node of dom type text
3811 : //
3812 : bool
3813 0 : nsEditor::IsTextNode(nsIDOMNode *aNode)
3814 : {
3815 0 : if (!aNode)
3816 : {
3817 0 : NS_NOTREACHED("null node passed to IsTextNode()");
3818 0 : return false;
3819 : }
3820 :
3821 : PRUint16 nodeType;
3822 0 : aNode->GetNodeType(&nodeType);
3823 0 : return (nodeType == nsIDOMNode::TEXT_NODE);
3824 : }
3825 :
3826 : bool
3827 0 : nsEditor::IsTextNode(nsINode *aNode)
3828 : {
3829 0 : return aNode->NodeType() == nsIDOMNode::TEXT_NODE;
3830 : }
3831 :
3832 : ///////////////////////////////////////////////////////////////////////////
3833 : // GetIndexOf: returns the position index of the node in the parent
3834 : //
3835 : PRInt32
3836 0 : nsEditor::GetIndexOf(nsIDOMNode *parent, nsIDOMNode *child)
3837 : {
3838 0 : nsCOMPtr<nsINode> parentNode = do_QueryInterface(parent);
3839 0 : NS_PRECONDITION(parentNode, "null parentNode in nsEditor::GetIndexOf");
3840 0 : NS_PRECONDITION(parentNode->IsNodeOfType(nsINode::eCONTENT) ||
3841 : parentNode->IsNodeOfType(nsINode::eDOCUMENT),
3842 : "The parent node must be an element node or a document node");
3843 :
3844 0 : nsCOMPtr<nsIContent> cChild = do_QueryInterface(child);
3845 0 : NS_PRECONDITION(cChild, "null content in nsEditor::GetIndexOf");
3846 :
3847 0 : return parentNode->IndexOf(cChild);
3848 : }
3849 :
3850 :
3851 : ///////////////////////////////////////////////////////////////////////////
3852 : // GetChildAt: returns the node at this position index in the parent
3853 : //
3854 : nsCOMPtr<nsIDOMNode>
3855 0 : nsEditor::GetChildAt(nsIDOMNode *aParent, PRInt32 aOffset)
3856 : {
3857 0 : nsCOMPtr<nsIDOMNode> resultNode;
3858 :
3859 0 : nsCOMPtr<nsIContent> parent = do_QueryInterface(aParent);
3860 :
3861 0 : NS_ENSURE_TRUE(parent, resultNode);
3862 :
3863 0 : resultNode = do_QueryInterface(parent->GetChildAt(aOffset));
3864 :
3865 : return resultNode;
3866 : }
3867 :
3868 : ///////////////////////////////////////////////////////////////////////////
3869 : // GetNodeAtRangeOffsetPoint: returns the node at this position in a range,
3870 : // assuming that aParentOrNode is the node itself if it's a text node, or
3871 : // the node's parent otherwise.
3872 : //
3873 : nsCOMPtr<nsIDOMNode>
3874 0 : nsEditor::GetNodeAtRangeOffsetPoint(nsIDOMNode* aParentOrNode, PRInt32 aOffset)
3875 : {
3876 0 : if (IsTextNode(aParentOrNode)) {
3877 0 : return aParentOrNode;
3878 : }
3879 0 : return GetChildAt(aParentOrNode, aOffset);
3880 : }
3881 :
3882 :
3883 : ///////////////////////////////////////////////////////////////////////////
3884 : // GetStartNodeAndOffset: returns whatever the start parent & offset is of
3885 : // the first range in the selection.
3886 : nsresult
3887 0 : nsEditor::GetStartNodeAndOffset(nsISelection *aSelection,
3888 : nsIDOMNode **outStartNode,
3889 : PRInt32 *outStartOffset)
3890 : {
3891 0 : NS_ENSURE_TRUE(outStartNode && outStartOffset && aSelection, NS_ERROR_NULL_POINTER);
3892 :
3893 0 : *outStartNode = nsnull;
3894 0 : *outStartOffset = 0;
3895 :
3896 0 : nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(aSelection));
3897 0 : nsCOMPtr<nsIEnumerator> enumerator;
3898 0 : nsresult result = selPrivate->GetEnumerator(getter_AddRefs(enumerator));
3899 0 : NS_ENSURE_SUCCESS(result, result);
3900 0 : NS_ENSURE_TRUE(enumerator, NS_ERROR_FAILURE);
3901 :
3902 0 : enumerator->First();
3903 0 : nsCOMPtr<nsISupports> currentItem;
3904 0 : result = enumerator->CurrentItem(getter_AddRefs(currentItem));
3905 0 : NS_ENSURE_SUCCESS(result, result);
3906 :
3907 0 : nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
3908 0 : NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
3909 :
3910 0 : result = range->GetStartContainer(outStartNode);
3911 0 : NS_ENSURE_SUCCESS(result, result);
3912 :
3913 0 : result = range->GetStartOffset(outStartOffset);
3914 0 : NS_ENSURE_SUCCESS(result, result);
3915 :
3916 0 : return NS_OK;
3917 : }
3918 :
3919 :
3920 : ///////////////////////////////////////////////////////////////////////////
3921 : // GetEndNodeAndOffset: returns whatever the end parent & offset is of
3922 : // the first range in the selection.
3923 : nsresult
3924 0 : nsEditor::GetEndNodeAndOffset(nsISelection *aSelection,
3925 : nsIDOMNode **outEndNode,
3926 : PRInt32 *outEndOffset)
3927 : {
3928 0 : NS_ENSURE_TRUE(outEndNode && outEndOffset, NS_ERROR_NULL_POINTER);
3929 :
3930 0 : *outEndNode = nsnull;
3931 :
3932 0 : nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(aSelection));
3933 0 : nsCOMPtr<nsIEnumerator> enumerator;
3934 0 : nsresult result = selPrivate->GetEnumerator(getter_AddRefs(enumerator));
3935 0 : if (NS_FAILED(result) || !enumerator)
3936 0 : return NS_ERROR_FAILURE;
3937 :
3938 0 : enumerator->First();
3939 0 : nsCOMPtr<nsISupports> currentItem;
3940 0 : if (NS_FAILED(enumerator->CurrentItem(getter_AddRefs(currentItem))))
3941 0 : return NS_ERROR_FAILURE;
3942 :
3943 0 : nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
3944 0 : NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
3945 :
3946 0 : if (NS_FAILED(range->GetEndContainer(outEndNode)))
3947 0 : return NS_ERROR_FAILURE;
3948 :
3949 0 : if (NS_FAILED(range->GetEndOffset(outEndOffset)))
3950 0 : return NS_ERROR_FAILURE;
3951 :
3952 0 : return NS_OK;
3953 : }
3954 :
3955 :
3956 : ///////////////////////////////////////////////////////////////////////////
3957 : // IsPreformatted: checks the style info for the node for the preformatted
3958 : // text style.
3959 : nsresult
3960 0 : nsEditor::IsPreformatted(nsIDOMNode *aNode, bool *aResult)
3961 : {
3962 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
3963 :
3964 0 : NS_ENSURE_TRUE(aResult && content, NS_ERROR_NULL_POINTER);
3965 :
3966 0 : nsCOMPtr<nsIPresShell> ps = GetPresShell();
3967 0 : NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
3968 :
3969 : // Look at the node (and its parent if it's not an element), and grab its style context
3970 0 : nsRefPtr<nsStyleContext> elementStyle;
3971 0 : if (!content->IsElement()) {
3972 0 : content = content->GetParent();
3973 : }
3974 0 : if (content && content->IsElement()) {
3975 0 : elementStyle = nsComputedDOMStyle::GetStyleContextForElement(content->AsElement(),
3976 : nsnull,
3977 0 : ps);
3978 : }
3979 :
3980 0 : if (!elementStyle)
3981 : {
3982 : // Consider nodes without a style context to be NOT preformatted:
3983 : // For instance, this is true of JS tags inside the body (which show
3984 : // up as #text nodes but have no style context).
3985 0 : *aResult = false;
3986 0 : return NS_OK;
3987 : }
3988 :
3989 0 : const nsStyleText* styleText = elementStyle->GetStyleText();
3990 :
3991 0 : *aResult = styleText->WhiteSpaceIsSignificant();
3992 0 : return NS_OK;
3993 : }
3994 :
3995 :
3996 : ///////////////////////////////////////////////////////////////////////////
3997 : // SplitNodeDeep: this splits a node "deeply", splitting children as
3998 : // appropriate. The place to split is represented by
3999 : // a dom point at {splitPointParent, splitPointOffset}.
4000 : // That dom point must be inside aNode, which is the node to
4001 : // split. outOffset is set to the offset in the parent of aNode where
4002 : // the split terminates - where you would want to insert
4003 : // a new element, for instance, if that's why you were splitting
4004 : // the node.
4005 : //
4006 : nsresult
4007 0 : nsEditor::SplitNodeDeep(nsIDOMNode *aNode,
4008 : nsIDOMNode *aSplitPointParent,
4009 : PRInt32 aSplitPointOffset,
4010 : PRInt32 *outOffset,
4011 : bool aNoEmptyContainers,
4012 : nsCOMPtr<nsIDOMNode> *outLeftNode,
4013 : nsCOMPtr<nsIDOMNode> *outRightNode)
4014 : {
4015 0 : NS_ENSURE_TRUE(aNode && aSplitPointParent && outOffset, NS_ERROR_NULL_POINTER);
4016 0 : nsCOMPtr<nsIDOMNode> tempNode, parentNode;
4017 0 : PRInt32 offset = aSplitPointOffset;
4018 : nsresult res;
4019 :
4020 0 : if (outLeftNode) *outLeftNode = nsnull;
4021 0 : if (outRightNode) *outRightNode = nsnull;
4022 :
4023 0 : nsCOMPtr<nsIDOMNode> nodeToSplit = do_QueryInterface(aSplitPointParent);
4024 0 : while (nodeToSplit)
4025 : {
4026 : // need to insert rules code call here to do things like
4027 : // not split a list if you are after the last <li> or before the first, etc.
4028 : // for now we just have some smarts about unneccessarily splitting
4029 : // textnodes, which should be universal enough to put straight in
4030 : // this nsEditor routine.
4031 :
4032 0 : nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(nodeToSplit);
4033 : PRUint32 len;
4034 0 : bool bDoSplit = false;
4035 0 : res = GetLengthOfDOMNode(nodeToSplit, len);
4036 0 : NS_ENSURE_SUCCESS(res, res);
4037 :
4038 0 : if (!(aNoEmptyContainers || nodeAsText) || (offset && (offset != (PRInt32)len)))
4039 : {
4040 0 : bDoSplit = true;
4041 0 : res = SplitNode(nodeToSplit, offset, getter_AddRefs(tempNode));
4042 0 : NS_ENSURE_SUCCESS(res, res);
4043 0 : if (outRightNode) *outRightNode = nodeToSplit;
4044 0 : if (outLeftNode) *outLeftNode = tempNode;
4045 : }
4046 :
4047 0 : res = nodeToSplit->GetParentNode(getter_AddRefs(parentNode));
4048 0 : NS_ENSURE_SUCCESS(res, res);
4049 0 : NS_ENSURE_TRUE(parentNode, NS_ERROR_FAILURE);
4050 :
4051 0 : if (!bDoSplit && offset) // must be "end of text node" case, we didn't split it, just move past it
4052 : {
4053 0 : offset = GetIndexOf(parentNode, nodeToSplit) +1;
4054 0 : if (outLeftNode) *outLeftNode = nodeToSplit;
4055 : }
4056 : else
4057 : {
4058 0 : offset = GetIndexOf(parentNode, nodeToSplit);
4059 0 : if (outRightNode) *outRightNode = nodeToSplit;
4060 : }
4061 :
4062 0 : if (nodeToSplit.get() == aNode) // we split all the way up to (and including) aNode; we're done
4063 : break;
4064 :
4065 0 : nodeToSplit = parentNode;
4066 : }
4067 :
4068 0 : if (!nodeToSplit)
4069 : {
4070 0 : NS_NOTREACHED("null node obtained in nsEditor::SplitNodeDeep()");
4071 0 : return NS_ERROR_FAILURE;
4072 : }
4073 :
4074 0 : *outOffset = offset;
4075 :
4076 0 : return NS_OK;
4077 : }
4078 :
4079 :
4080 : ///////////////////////////////////////////////////////////////////////////
4081 : // JoinNodeDeep: this joins two like nodes "deeply", joining children as
4082 : // appropriate.
4083 : nsresult
4084 0 : nsEditor::JoinNodeDeep(nsIDOMNode *aLeftNode,
4085 : nsIDOMNode *aRightNode,
4086 : nsCOMPtr<nsIDOMNode> *aOutJoinNode,
4087 : PRInt32 *outOffset)
4088 : {
4089 0 : NS_ENSURE_TRUE(aLeftNode && aRightNode && aOutJoinNode && outOffset, NS_ERROR_NULL_POINTER);
4090 :
4091 : // while the rightmost children and their descendants of the left node
4092 : // match the leftmost children and their descendants of the right node
4093 : // join them up. Can you say that three times fast?
4094 :
4095 0 : nsCOMPtr<nsIDOMNode> leftNodeToJoin = do_QueryInterface(aLeftNode);
4096 0 : nsCOMPtr<nsIDOMNode> rightNodeToJoin = do_QueryInterface(aRightNode);
4097 0 : nsCOMPtr<nsIDOMNode> parentNode,tmp;
4098 0 : nsresult res = NS_OK;
4099 :
4100 0 : rightNodeToJoin->GetParentNode(getter_AddRefs(parentNode));
4101 :
4102 0 : while (leftNodeToJoin && rightNodeToJoin && parentNode &&
4103 0 : NodesSameType(leftNodeToJoin, rightNodeToJoin))
4104 : {
4105 : // adjust out params
4106 : PRUint32 length;
4107 0 : if (IsTextNode(leftNodeToJoin))
4108 : {
4109 0 : nsCOMPtr<nsIDOMCharacterData>nodeAsText;
4110 0 : nodeAsText = do_QueryInterface(leftNodeToJoin);
4111 0 : nodeAsText->GetLength(&length);
4112 : }
4113 : else
4114 : {
4115 0 : res = GetLengthOfDOMNode(leftNodeToJoin, length);
4116 0 : NS_ENSURE_SUCCESS(res, res);
4117 : }
4118 :
4119 0 : *aOutJoinNode = rightNodeToJoin;
4120 0 : *outOffset = length;
4121 :
4122 : // do the join
4123 0 : res = JoinNodes(leftNodeToJoin, rightNodeToJoin, parentNode);
4124 0 : NS_ENSURE_SUCCESS(res, res);
4125 :
4126 0 : if (IsTextNode(parentNode)) // we've joined all the way down to text nodes, we're done!
4127 0 : return NS_OK;
4128 :
4129 : else
4130 : {
4131 : // get new left and right nodes, and begin anew
4132 0 : parentNode = rightNodeToJoin;
4133 0 : leftNodeToJoin = GetChildAt(parentNode, length-1);
4134 0 : rightNodeToJoin = GetChildAt(parentNode, length);
4135 :
4136 : // skip over non-editable nodes
4137 0 : while (leftNodeToJoin && !IsEditable(leftNodeToJoin))
4138 : {
4139 0 : leftNodeToJoin->GetPreviousSibling(getter_AddRefs(tmp));
4140 0 : leftNodeToJoin = tmp;
4141 : }
4142 0 : if (!leftNodeToJoin) break;
4143 :
4144 0 : while (rightNodeToJoin && !IsEditable(rightNodeToJoin))
4145 : {
4146 0 : rightNodeToJoin->GetNextSibling(getter_AddRefs(tmp));
4147 0 : rightNodeToJoin = tmp;
4148 : }
4149 0 : if (!rightNodeToJoin) break;
4150 : }
4151 : }
4152 :
4153 0 : return res;
4154 : }
4155 :
4156 0 : nsresult nsEditor::BeginUpdateViewBatch()
4157 : {
4158 0 : NS_PRECONDITION(mUpdateCount >= 0, "bad state");
4159 :
4160 :
4161 0 : if (0 == mUpdateCount)
4162 : {
4163 : // Turn off selection updates and notifications.
4164 :
4165 0 : nsCOMPtr<nsISelection> selection;
4166 0 : GetSelection(getter_AddRefs(selection));
4167 :
4168 0 : if (selection)
4169 : {
4170 0 : nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(selection));
4171 0 : selPrivate->StartBatchChanges();
4172 : }
4173 : }
4174 :
4175 0 : mUpdateCount++;
4176 :
4177 0 : return NS_OK;
4178 : }
4179 :
4180 :
4181 0 : nsresult nsEditor::EndUpdateViewBatch()
4182 : {
4183 0 : NS_PRECONDITION(mUpdateCount > 0, "bad state");
4184 :
4185 0 : if (mUpdateCount <= 0)
4186 : {
4187 0 : mUpdateCount = 0;
4188 0 : return NS_ERROR_FAILURE;
4189 : }
4190 :
4191 0 : mUpdateCount--;
4192 :
4193 0 : if (0 == mUpdateCount)
4194 : {
4195 : // Hide the caret with an StCaretHider. By the time it goes out
4196 : // of scope and tries to show the caret, reflow and selection changed
4197 : // notifications should've happened so the caret should have enough info
4198 : // to draw at the correct position.
4199 :
4200 0 : nsRefPtr<nsCaret> caret;
4201 0 : nsCOMPtr<nsIPresShell> presShell = GetPresShell();
4202 :
4203 0 : if (presShell)
4204 0 : caret = presShell->GetCaret();
4205 :
4206 0 : StCaretHider caretHider(caret);
4207 :
4208 : // Turn selection updating and notifications back on.
4209 :
4210 0 : nsCOMPtr<nsISelection>selection;
4211 0 : GetSelection(getter_AddRefs(selection));
4212 :
4213 0 : if (selection) {
4214 0 : nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(selection));
4215 0 : selPrivate->EndBatchChanges();
4216 : }
4217 : }
4218 :
4219 0 : return NS_OK;
4220 : }
4221 :
4222 : bool
4223 0 : nsEditor::GetShouldTxnSetSelection()
4224 : {
4225 0 : return mShouldTxnSetSelection;
4226 : }
4227 :
4228 :
4229 : NS_IMETHODIMP
4230 0 : nsEditor::DeleteSelectionImpl(nsIEditor::EDirection aAction)
4231 : {
4232 0 : nsCOMPtr<nsISelection>selection;
4233 0 : nsresult res = GetSelection(getter_AddRefs(selection));
4234 0 : NS_ENSURE_SUCCESS(res, res);
4235 0 : nsRefPtr<EditAggregateTxn> txn;
4236 0 : nsCOMPtr<nsIDOMNode> deleteNode;
4237 0 : PRInt32 deleteCharOffset = 0, deleteCharLength = 0;
4238 : res = CreateTxnForDeleteSelection(aAction, getter_AddRefs(txn),
4239 0 : getter_AddRefs(deleteNode),
4240 0 : &deleteCharOffset, &deleteCharLength);
4241 0 : nsCOMPtr<nsIDOMCharacterData> deleteCharData(do_QueryInterface(deleteNode));
4242 :
4243 0 : if (NS_SUCCEEDED(res))
4244 : {
4245 0 : nsAutoRules beginRulesSniffing(this, kOpDeleteSelection, aAction);
4246 : PRInt32 i;
4247 : // Notify nsIEditActionListener::WillDelete[Selection|Text|Node]
4248 0 : if (!deleteNode)
4249 0 : for (i = 0; i < mActionListeners.Count(); i++)
4250 0 : mActionListeners[i]->WillDeleteSelection(selection);
4251 0 : else if (deleteCharData)
4252 0 : for (i = 0; i < mActionListeners.Count(); i++)
4253 0 : mActionListeners[i]->WillDeleteText(deleteCharData, deleteCharOffset, 1);
4254 : else
4255 0 : for (i = 0; i < mActionListeners.Count(); i++)
4256 0 : mActionListeners[i]->WillDeleteNode(deleteNode);
4257 :
4258 : // Delete the specified amount
4259 0 : res = DoTransaction(txn);
4260 :
4261 : // Notify nsIEditActionListener::DidDelete[Selection|Text|Node]
4262 0 : if (!deleteNode)
4263 0 : for (i = 0; i < mActionListeners.Count(); i++)
4264 0 : mActionListeners[i]->DidDeleteSelection(selection);
4265 0 : else if (deleteCharData)
4266 0 : for (i = 0; i < mActionListeners.Count(); i++)
4267 0 : mActionListeners[i]->DidDeleteText(deleteCharData, deleteCharOffset, 1, res);
4268 : else
4269 0 : for (i = 0; i < mActionListeners.Count(); i++)
4270 0 : mActionListeners[i]->DidDeleteNode(deleteNode, res);
4271 : }
4272 :
4273 0 : return res;
4274 : }
4275 :
4276 : // XXX: error handling in this routine needs to be cleaned up!
4277 : NS_IMETHODIMP
4278 0 : nsEditor::DeleteSelectionAndCreateNode(const nsAString& aTag,
4279 : nsIDOMNode ** aNewNode)
4280 : {
4281 0 : nsCOMPtr<nsIDOMNode> parentSelectedNode;
4282 : PRInt32 offsetOfNewNode;
4283 : nsresult result = DeleteSelectionAndPrepareToCreateNode(parentSelectedNode,
4284 0 : offsetOfNewNode);
4285 0 : NS_ENSURE_SUCCESS(result, result);
4286 :
4287 0 : nsCOMPtr<nsIDOMNode> newNode;
4288 : result = CreateNode(aTag, parentSelectedNode, offsetOfNewNode,
4289 0 : getter_AddRefs(newNode));
4290 : // XXX: ERROR_HANDLING check result, and make sure aNewNode is set correctly in success/failure cases
4291 0 : *aNewNode = newNode;
4292 0 : NS_IF_ADDREF(*aNewNode);
4293 :
4294 : // we want the selection to be just after the new node
4295 0 : nsCOMPtr<nsISelection> selection;
4296 0 : result = GetSelection(getter_AddRefs(selection));
4297 0 : NS_ENSURE_SUCCESS(result, result);
4298 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
4299 0 : return selection->Collapse(parentSelectedNode, offsetOfNewNode+1);
4300 : }
4301 :
4302 :
4303 : /* Non-interface, protected methods */
4304 :
4305 : nsresult
4306 0 : nsEditor::GetIMEBufferLength(PRInt32* length)
4307 : {
4308 0 : *length = mIMEBufferLength;
4309 0 : return NS_OK;
4310 : }
4311 :
4312 : void
4313 0 : nsEditor::SetIsIMEComposing(){
4314 : // We set mIsIMEComposing according to mIMETextRangeList.
4315 0 : nsCOMPtr<nsIPrivateTextRange> rangePtr;
4316 : PRUint16 listlen, type;
4317 :
4318 0 : mIsIMEComposing = false;
4319 0 : listlen = mIMETextRangeList->GetLength();
4320 :
4321 0 : for (PRUint16 i = 0; i < listlen; i++)
4322 : {
4323 0 : rangePtr = mIMETextRangeList->Item(i);
4324 0 : if (!rangePtr) continue;
4325 0 : nsresult result = rangePtr->GetRangeType(&type);
4326 0 : if (NS_FAILED(result)) continue;
4327 0 : if ( type == nsIPrivateTextRange::TEXTRANGE_RAWINPUT ||
4328 : type == nsIPrivateTextRange::TEXTRANGE_CONVERTEDTEXT ||
4329 : type == nsIPrivateTextRange::TEXTRANGE_SELECTEDRAWTEXT ||
4330 : type == nsIPrivateTextRange::TEXTRANGE_SELECTEDCONVERTEDTEXT )
4331 : {
4332 0 : mIsIMEComposing = true;
4333 : #ifdef DEBUG_IME
4334 : printf("nsEditor::mIsIMEComposing = true\n");
4335 : #endif
4336 0 : break;
4337 : }
4338 : }
4339 : return;
4340 : }
4341 :
4342 : bool
4343 0 : nsEditor::IsIMEComposing() {
4344 0 : return mIsIMEComposing;
4345 : }
4346 :
4347 : NS_IMETHODIMP
4348 0 : nsEditor::DeleteSelectionAndPrepareToCreateNode(nsCOMPtr<nsIDOMNode> &parentSelectedNode, PRInt32& offsetOfNewNode)
4349 : {
4350 0 : nsresult result=NS_ERROR_NOT_INITIALIZED;
4351 0 : nsCOMPtr<nsISelection> selection;
4352 0 : result = GetSelection(getter_AddRefs(selection));
4353 0 : NS_ENSURE_SUCCESS(result, result);
4354 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
4355 :
4356 : bool collapsed;
4357 0 : result = selection->GetIsCollapsed(&collapsed);
4358 0 : if (NS_SUCCEEDED(result) && !collapsed)
4359 : {
4360 0 : result = DeleteSelection(nsIEditor::eNone);
4361 0 : if (NS_FAILED(result)) {
4362 0 : return result;
4363 : }
4364 : // get the new selection
4365 0 : result = GetSelection(getter_AddRefs(selection));
4366 0 : if (NS_FAILED(result)) {
4367 0 : return result;
4368 : }
4369 :
4370 0 : nsCOMPtr<nsIDOMNode> selectedNode;
4371 0 : selection->GetAnchorNode(getter_AddRefs(selectedNode));
4372 : // no selection is ok.
4373 : // if there is a selection, it must be collapsed
4374 0 : if (selectedNode)
4375 : {
4376 0 : bool testCollapsed = false;
4377 0 : selection->GetIsCollapsed(&testCollapsed);
4378 0 : if (!testCollapsed) {
4379 0 : result = selection->CollapseToEnd();
4380 0 : NS_ENSURE_SUCCESS(result, result);
4381 : }
4382 : }
4383 : }
4384 : // split the selected node
4385 : PRInt32 offsetOfSelectedNode;
4386 0 : result = selection->GetAnchorNode(getter_AddRefs(parentSelectedNode));
4387 0 : if (NS_SUCCEEDED(result) && NS_SUCCEEDED(selection->GetAnchorOffset(&offsetOfSelectedNode)) && parentSelectedNode)
4388 : {
4389 0 : nsCOMPtr<nsIDOMNode> selectedNode;
4390 0 : PRUint32 selectedNodeContentCount=0;
4391 0 : nsCOMPtr<nsIDOMCharacterData>selectedParentNodeAsText;
4392 0 : selectedParentNodeAsText = do_QueryInterface(parentSelectedNode);
4393 :
4394 0 : offsetOfNewNode = offsetOfSelectedNode;
4395 :
4396 : /* if the selection is a text node, split the text node if necessary
4397 : and compute where to put the new node
4398 : */
4399 0 : if (selectedParentNodeAsText)
4400 : {
4401 : PRInt32 indexOfTextNodeInParent;
4402 0 : selectedNode = do_QueryInterface(parentSelectedNode);
4403 0 : selectedNode->GetParentNode(getter_AddRefs(parentSelectedNode));
4404 0 : selectedParentNodeAsText->GetLength(&selectedNodeContentCount);
4405 0 : GetChildOffset(selectedNode, parentSelectedNode, indexOfTextNodeInParent);
4406 :
4407 0 : if ((offsetOfSelectedNode!=0) && (((PRUint32)offsetOfSelectedNode)!=selectedNodeContentCount))
4408 : {
4409 0 : nsCOMPtr<nsIDOMNode> newSiblingNode;
4410 0 : result = SplitNode(selectedNode, offsetOfSelectedNode, getter_AddRefs(newSiblingNode));
4411 : // now get the node's offset in its parent, and insert the new tag there
4412 0 : if (NS_SUCCEEDED(result)) {
4413 0 : result = GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode);
4414 0 : }
4415 : }
4416 : else
4417 : { // determine where to insert the new node
4418 0 : if (0==offsetOfSelectedNode) {
4419 0 : offsetOfNewNode = indexOfTextNodeInParent; // insert new node as previous sibling to selection parent
4420 : }
4421 : else { // insert new node as last child
4422 0 : GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode);
4423 0 : offsetOfNewNode++; // offsets are 0-based, and we need the index of the new node
4424 : }
4425 : }
4426 : }
4427 : // Here's where the new node was inserted
4428 : }
4429 : #ifdef DEBUG
4430 : else {
4431 0 : printf("InsertLineBreak into an empty document is not yet supported\n");
4432 : }
4433 : #endif
4434 0 : return result;
4435 : }
4436 :
4437 :
4438 :
4439 : NS_IMETHODIMP
4440 0 : nsEditor::DoAfterDoTransaction(nsITransaction *aTxn)
4441 : {
4442 0 : nsresult rv = NS_OK;
4443 :
4444 : bool isTransientTransaction;
4445 0 : rv = aTxn->GetIsTransient(&isTransientTransaction);
4446 0 : NS_ENSURE_SUCCESS(rv, rv);
4447 :
4448 0 : if (!isTransientTransaction)
4449 : {
4450 : // we need to deal here with the case where the user saved after some
4451 : // edits, then undid one or more times. Then, the undo count is -ve,
4452 : // but we can't let a do take it back to zero. So we flip it up to
4453 : // a +ve number.
4454 : PRInt32 modCount;
4455 0 : GetModificationCount(&modCount);
4456 0 : if (modCount < 0)
4457 0 : modCount = -modCount;
4458 :
4459 0 : rv = IncrementModificationCount(1); // don't count transient transactions
4460 : }
4461 :
4462 0 : return rv;
4463 : }
4464 :
4465 :
4466 : NS_IMETHODIMP
4467 0 : nsEditor::DoAfterUndoTransaction()
4468 : {
4469 0 : nsresult rv = NS_OK;
4470 :
4471 0 : rv = IncrementModificationCount(-1); // all undoable transactions are non-transient
4472 :
4473 0 : return rv;
4474 : }
4475 :
4476 : NS_IMETHODIMP
4477 0 : nsEditor::DoAfterRedoTransaction()
4478 : {
4479 0 : return IncrementModificationCount(1); // all redoable transactions are non-transient
4480 : }
4481 :
4482 : NS_IMETHODIMP
4483 0 : nsEditor::CreateTxnForSetAttribute(nsIDOMElement *aElement,
4484 : const nsAString& aAttribute,
4485 : const nsAString& aValue,
4486 : ChangeAttributeTxn ** aTxn)
4487 : {
4488 0 : NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
4489 :
4490 0 : nsRefPtr<ChangeAttributeTxn> txn = new ChangeAttributeTxn();
4491 :
4492 0 : nsresult rv = txn->Init(this, aElement, aAttribute, aValue, false);
4493 0 : if (NS_SUCCEEDED(rv))
4494 : {
4495 0 : txn.forget(aTxn);
4496 : }
4497 :
4498 0 : return rv;
4499 : }
4500 :
4501 :
4502 : NS_IMETHODIMP
4503 0 : nsEditor::CreateTxnForRemoveAttribute(nsIDOMElement *aElement,
4504 : const nsAString& aAttribute,
4505 : ChangeAttributeTxn ** aTxn)
4506 : {
4507 0 : NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
4508 :
4509 0 : nsRefPtr<ChangeAttributeTxn> txn = new ChangeAttributeTxn();
4510 :
4511 0 : nsresult rv = txn->Init(this, aElement, aAttribute, EmptyString(), true);
4512 0 : if (NS_SUCCEEDED(rv))
4513 : {
4514 0 : txn.forget(aTxn);
4515 : }
4516 :
4517 0 : return rv;
4518 : }
4519 :
4520 :
4521 0 : NS_IMETHODIMP nsEditor::CreateTxnForCreateElement(const nsAString& aTag,
4522 : nsIDOMNode *aParent,
4523 : PRInt32 aPosition,
4524 : CreateElementTxn ** aTxn)
4525 : {
4526 0 : NS_ENSURE_TRUE(aParent, NS_ERROR_NULL_POINTER);
4527 :
4528 0 : nsRefPtr<CreateElementTxn> txn = new CreateElementTxn();
4529 :
4530 0 : nsresult rv = txn->Init(this, aTag, aParent, aPosition);
4531 0 : if (NS_SUCCEEDED(rv))
4532 : {
4533 0 : txn.forget(aTxn);
4534 : }
4535 :
4536 0 : return rv;
4537 : }
4538 :
4539 :
4540 0 : NS_IMETHODIMP nsEditor::CreateTxnForInsertElement(nsIDOMNode * aNode,
4541 : nsIDOMNode * aParent,
4542 : PRInt32 aPosition,
4543 : InsertElementTxn ** aTxn)
4544 : {
4545 0 : NS_ENSURE_TRUE(aNode && aParent, NS_ERROR_NULL_POINTER);
4546 :
4547 0 : nsRefPtr<InsertElementTxn> txn = new InsertElementTxn();
4548 :
4549 0 : nsresult rv = txn->Init(aNode, aParent, aPosition, this);
4550 0 : if (NS_SUCCEEDED(rv))
4551 : {
4552 0 : txn.forget(aTxn);
4553 : }
4554 :
4555 0 : return rv;
4556 : }
4557 :
4558 0 : NS_IMETHODIMP nsEditor::CreateTxnForDeleteElement(nsIDOMNode * aElement,
4559 : DeleteElementTxn ** aTxn)
4560 : {
4561 0 : NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
4562 :
4563 0 : nsRefPtr<DeleteElementTxn> txn = new DeleteElementTxn();
4564 :
4565 0 : nsresult rv = txn->Init(this, aElement, &mRangeUpdater);
4566 0 : if (NS_SUCCEEDED(rv))
4567 : {
4568 0 : txn.forget(aTxn);
4569 : }
4570 :
4571 0 : return rv;
4572 : }
4573 :
4574 : NS_IMETHODIMP
4575 0 : nsEditor::CreateTxnForIMEText(const nsAString& aStringToInsert,
4576 : IMETextTxn ** aTxn)
4577 : {
4578 0 : NS_ASSERTION(aTxn, "illegal value- null ptr- aTxn");
4579 :
4580 0 : nsRefPtr<IMETextTxn> txn = new IMETextTxn();
4581 :
4582 0 : nsresult rv = txn->Init(mIMETextNode, mIMETextOffset, mIMEBufferLength,
4583 0 : mIMETextRangeList, aStringToInsert, this);
4584 0 : if (NS_SUCCEEDED(rv))
4585 : {
4586 0 : txn.forget(aTxn);
4587 : }
4588 :
4589 0 : return rv;
4590 : }
4591 :
4592 :
4593 : NS_IMETHODIMP
4594 0 : nsEditor::CreateTxnForAddStyleSheet(nsCSSStyleSheet* aSheet, AddStyleSheetTxn* *aTxn)
4595 : {
4596 0 : nsRefPtr<AddStyleSheetTxn> txn = new AddStyleSheetTxn();
4597 :
4598 0 : nsresult rv = txn->Init(this, aSheet);
4599 0 : if (NS_SUCCEEDED(rv))
4600 : {
4601 0 : txn.forget(aTxn);
4602 : }
4603 :
4604 0 : return rv;
4605 : }
4606 :
4607 :
4608 :
4609 : NS_IMETHODIMP
4610 0 : nsEditor::CreateTxnForRemoveStyleSheet(nsCSSStyleSheet* aSheet, RemoveStyleSheetTxn* *aTxn)
4611 : {
4612 0 : nsRefPtr<RemoveStyleSheetTxn> txn = new RemoveStyleSheetTxn();
4613 :
4614 0 : nsresult rv = txn->Init(this, aSheet);
4615 0 : if (NS_SUCCEEDED(rv))
4616 : {
4617 0 : txn.forget(aTxn);
4618 : }
4619 :
4620 0 : return rv;
4621 : }
4622 :
4623 :
4624 : NS_IMETHODIMP
4625 0 : nsEditor::CreateTxnForDeleteSelection(nsIEditor::EDirection aAction,
4626 : EditAggregateTxn ** aTxn,
4627 : nsIDOMNode ** aNode,
4628 : PRInt32 *aOffset,
4629 : PRInt32 *aLength)
4630 : {
4631 0 : NS_ENSURE_TRUE(aTxn, NS_ERROR_NULL_POINTER);
4632 0 : *aTxn = nsnull;
4633 :
4634 0 : nsRefPtr<EditAggregateTxn> aggTxn;
4635 0 : nsCOMPtr<nsISelectionController> selCon;
4636 0 : GetSelectionController(getter_AddRefs(selCon));
4637 0 : NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
4638 0 : nsCOMPtr<nsISelection> selection;
4639 0 : nsresult result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
4640 0 : getter_AddRefs(selection));
4641 0 : if ((NS_SUCCEEDED(result)) && selection)
4642 : {
4643 : // Check whether the selection is collapsed and we should do nothing:
4644 : bool isCollapsed;
4645 0 : result = (selection->GetIsCollapsed(&isCollapsed));
4646 0 : if (NS_SUCCEEDED(result) && isCollapsed && aAction == eNone)
4647 0 : return NS_OK;
4648 :
4649 : // allocate the out-param transaction
4650 0 : aggTxn = new EditAggregateTxn();
4651 :
4652 0 : nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(selection));
4653 0 : nsCOMPtr<nsIEnumerator> enumerator;
4654 0 : result = selPrivate->GetEnumerator(getter_AddRefs(enumerator));
4655 0 : if (NS_SUCCEEDED(result) && enumerator)
4656 : {
4657 0 : for (enumerator->First(); NS_OK!=enumerator->IsDone(); enumerator->Next())
4658 : {
4659 0 : nsCOMPtr<nsISupports> currentItem;
4660 0 : result = enumerator->CurrentItem(getter_AddRefs(currentItem));
4661 0 : if ((NS_SUCCEEDED(result)) && (currentItem))
4662 : {
4663 0 : nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
4664 0 : range->GetCollapsed(&isCollapsed);
4665 0 : if (!isCollapsed)
4666 : {
4667 0 : nsRefPtr<DeleteRangeTxn> txn = new DeleteRangeTxn();
4668 0 : if (txn)
4669 : {
4670 0 : txn->Init(this, range, &mRangeUpdater);
4671 0 : aggTxn->AppendChild(txn);
4672 : }
4673 : else
4674 0 : result = NS_ERROR_OUT_OF_MEMORY;
4675 : }
4676 : // Same with range as with selection; if it is collapsed and action
4677 : // is eNone, do nothing.
4678 0 : else if (aAction != eNone)
4679 : { // we have an insertion point. delete the thing in front of it or behind it, depending on aAction
4680 0 : result = CreateTxnForDeleteInsertionPoint(range, aAction, aggTxn, aNode, aOffset, aLength);
4681 : }
4682 : }
4683 : }
4684 : }
4685 : }
4686 :
4687 : // Only set the outparam if building the txn was a success, otherwise
4688 : // we let the aggregation txn be destroyed when the refptr goes out of scope
4689 0 : if (NS_SUCCEEDED(result))
4690 : {
4691 0 : aggTxn.forget(aTxn);
4692 : }
4693 :
4694 0 : return result;
4695 : }
4696 :
4697 : nsresult
4698 0 : nsEditor::CreateTxnForDeleteCharacter(nsIDOMCharacterData *aData,
4699 : PRUint32 aOffset,
4700 : nsIEditor::EDirection aDirection,
4701 : DeleteTextTxn **aTxn)
4702 : {
4703 0 : NS_ASSERTION(aDirection == eNext || aDirection == ePrevious,
4704 : "invalid direction");
4705 0 : nsAutoString data;
4706 0 : aData->GetData(data);
4707 0 : PRUint32 segOffset = aOffset, segLength = 1;
4708 0 : if (aDirection == eNext) {
4709 0 : if (segOffset + 1 < data.Length() &&
4710 0 : NS_IS_HIGH_SURROGATE(data[segOffset]) &&
4711 0 : NS_IS_LOW_SURROGATE(data[segOffset+1])) {
4712 : // delete both halves of the surrogate pair
4713 0 : ++segLength;
4714 : }
4715 0 : } else if (aOffset > 0) {
4716 0 : --segOffset;
4717 0 : if (segOffset > 0 &&
4718 0 : NS_IS_LOW_SURROGATE(data[segOffset]) &&
4719 0 : NS_IS_HIGH_SURROGATE(data[segOffset-1])) {
4720 0 : ++segLength;
4721 0 : --segOffset;
4722 : }
4723 : } else {
4724 0 : return NS_ERROR_FAILURE;
4725 : }
4726 0 : return CreateTxnForDeleteText(aData, segOffset, segLength, aTxn);
4727 : }
4728 :
4729 : //XXX: currently, this doesn't handle edge conditions because GetNext/GetPrior are not implemented
4730 : NS_IMETHODIMP
4731 0 : nsEditor::CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange,
4732 : nsIEditor::EDirection aAction,
4733 : EditAggregateTxn *aTxn,
4734 : nsIDOMNode **aNode,
4735 : PRInt32 *aOffset,
4736 : PRInt32 *aLength)
4737 : {
4738 0 : NS_ASSERTION(aAction != eNone, "invalid action");
4739 :
4740 : // get the node and offset of the insertion point
4741 0 : nsCOMPtr<nsIDOMNode> node;
4742 0 : nsresult result = aRange->GetStartContainer(getter_AddRefs(node));
4743 0 : NS_ENSURE_SUCCESS(result, result);
4744 :
4745 : PRInt32 offset;
4746 0 : result = aRange->GetStartOffset(&offset);
4747 0 : NS_ENSURE_SUCCESS(result, result);
4748 :
4749 : // determine if the insertion point is at the beginning, middle, or end of the node
4750 0 : nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(node);
4751 :
4752 0 : PRUint32 count=0;
4753 :
4754 0 : if (nodeAsText)
4755 0 : nodeAsText->GetLength(&count);
4756 : else
4757 : {
4758 : // get the child list and count
4759 0 : nsCOMPtr<nsIDOMNodeList>childList;
4760 0 : result = node->GetChildNodes(getter_AddRefs(childList));
4761 0 : if ((NS_SUCCEEDED(result)) && childList)
4762 0 : childList->GetLength(&count);
4763 : }
4764 :
4765 0 : bool isFirst = (0 == offset);
4766 0 : bool isLast = (count == (PRUint32)offset);
4767 :
4768 : // XXX: if isFirst && isLast, then we'll need to delete the node
4769 : // as well as the 1 child
4770 :
4771 : // build a transaction for deleting the appropriate data
4772 : // XXX: this has to come from rule section
4773 0 : if ((ePrevious==aAction) && (true==isFirst))
4774 : { // we're backspacing from the beginning of the node. Delete the first thing to our left
4775 0 : nsCOMPtr<nsIDOMNode> priorNode;
4776 0 : result = GetPriorNode(node, true, address_of(priorNode));
4777 0 : if ((NS_SUCCEEDED(result)) && priorNode)
4778 : { // there is a priorNode, so delete its last child (if text content, delete the last char.)
4779 : // if it has no children, delete it
4780 0 : nsCOMPtr<nsIDOMCharacterData> priorNodeAsText = do_QueryInterface(priorNode);
4781 0 : if (priorNodeAsText)
4782 : {
4783 0 : PRUint32 length=0;
4784 0 : priorNodeAsText->GetLength(&length);
4785 0 : if (0<length)
4786 : {
4787 0 : nsRefPtr<DeleteTextTxn> txn;
4788 : result = CreateTxnForDeleteCharacter(priorNodeAsText, length,
4789 0 : ePrevious, getter_AddRefs(txn));
4790 0 : if (NS_SUCCEEDED(result)) {
4791 0 : aTxn->AppendChild(txn);
4792 0 : NS_ADDREF(*aNode = priorNode);
4793 0 : *aOffset = txn->GetOffset();
4794 0 : *aLength = txn->GetNumCharsToDelete();
4795 : }
4796 : }
4797 : else
4798 : { // XXX: can you have an empty text node? If so, what do you do?
4799 0 : printf("ERROR: found a text node with 0 characters\n");
4800 0 : result = NS_ERROR_UNEXPECTED;
4801 : }
4802 : }
4803 : else
4804 : { // priorNode is not text, so tell it's parent to delete it
4805 0 : nsRefPtr<DeleteElementTxn> txn;
4806 0 : result = CreateTxnForDeleteElement(priorNode, getter_AddRefs(txn));
4807 0 : if (NS_SUCCEEDED(result)) {
4808 0 : aTxn->AppendChild(txn);
4809 0 : NS_ADDREF(*aNode = priorNode);
4810 : }
4811 : }
4812 0 : }
4813 : }
4814 0 : else if ((nsIEditor::eNext==aAction) && (true==isLast))
4815 : { // we're deleting from the end of the node. Delete the first thing to our right
4816 0 : nsCOMPtr<nsIDOMNode> nextNode;
4817 0 : result = GetNextNode(node, true, address_of(nextNode));
4818 0 : if ((NS_SUCCEEDED(result)) && nextNode)
4819 : { // there is a nextNode, so delete it's first child (if text content, delete the first char.)
4820 : // if it has no children, delete it
4821 0 : nsCOMPtr<nsIDOMCharacterData> nextNodeAsText = do_QueryInterface(nextNode);
4822 0 : if (nextNodeAsText)
4823 : {
4824 0 : PRUint32 length=0;
4825 0 : nextNodeAsText->GetLength(&length);
4826 0 : if (0<length)
4827 : {
4828 0 : nsRefPtr<DeleteTextTxn> txn;
4829 : result = CreateTxnForDeleteCharacter(nextNodeAsText, 0, eNext,
4830 0 : getter_AddRefs(txn));
4831 0 : if (NS_SUCCEEDED(result)) {
4832 0 : aTxn->AppendChild(txn);
4833 0 : NS_ADDREF(*aNode = nextNode);
4834 0 : *aOffset = txn->GetOffset();
4835 0 : *aLength = txn->GetNumCharsToDelete();
4836 : }
4837 : }
4838 : else
4839 : { // XXX: can you have an empty text node? If so, what do you do?
4840 0 : printf("ERROR: found a text node with 0 characters\n");
4841 0 : result = NS_ERROR_UNEXPECTED;
4842 : }
4843 : }
4844 : else
4845 : { // nextNode is not text, so tell its parent to delete it
4846 0 : nsRefPtr<DeleteElementTxn> txn;
4847 0 : result = CreateTxnForDeleteElement(nextNode, getter_AddRefs(txn));
4848 0 : if (NS_SUCCEEDED(result)) {
4849 0 : aTxn->AppendChild(txn);
4850 0 : NS_ADDREF(*aNode = nextNode);
4851 : }
4852 : }
4853 0 : }
4854 : }
4855 : else
4856 : {
4857 0 : if (nodeAsText)
4858 : { // we have text, so delete a char at the proper offset
4859 0 : nsRefPtr<DeleteTextTxn> txn;
4860 : result = CreateTxnForDeleteCharacter(nodeAsText, offset, aAction,
4861 0 : getter_AddRefs(txn));
4862 0 : if (NS_SUCCEEDED(result)) {
4863 0 : aTxn->AppendChild(txn);
4864 0 : NS_ADDREF(*aNode = node);
4865 0 : *aOffset = txn->GetOffset();
4866 0 : *aLength = txn->GetNumCharsToDelete();
4867 : }
4868 : }
4869 : else
4870 : { // we're either deleting a node or some text, need to dig into the next/prev node to find out
4871 0 : nsCOMPtr<nsIDOMNode> selectedNode;
4872 0 : if (ePrevious==aAction)
4873 : {
4874 0 : result = GetPriorNode(node, offset, true, address_of(selectedNode));
4875 : }
4876 0 : else if (eNext==aAction)
4877 : {
4878 0 : result = GetNextNode(node, offset, true, address_of(selectedNode));
4879 : }
4880 0 : if (NS_FAILED(result)) { return result; }
4881 0 : if (selectedNode)
4882 : {
4883 : nsCOMPtr<nsIDOMCharacterData> selectedNodeAsText =
4884 0 : do_QueryInterface(selectedNode);
4885 0 : if (selectedNodeAsText)
4886 : { // we are deleting from a text node, so do a text deletion
4887 0 : PRUint32 position = 0; // default for forward delete
4888 0 : if (ePrevious==aAction)
4889 : {
4890 0 : selectedNodeAsText->GetLength(&position);
4891 : }
4892 0 : nsRefPtr<DeleteTextTxn> delTextTxn;
4893 : result = CreateTxnForDeleteCharacter(selectedNodeAsText, position,
4894 : aAction,
4895 0 : getter_AddRefs(delTextTxn));
4896 0 : if (NS_FAILED(result)) { return result; }
4897 0 : if (!delTextTxn) { return NS_ERROR_NULL_POINTER; }
4898 0 : aTxn->AppendChild(delTextTxn);
4899 0 : NS_ADDREF(*aNode = selectedNode);
4900 0 : *aOffset = delTextTxn->GetOffset();
4901 0 : *aLength = delTextTxn->GetNumCharsToDelete();
4902 : }
4903 : else
4904 : {
4905 0 : nsRefPtr<DeleteElementTxn> delElementTxn;
4906 : result = CreateTxnForDeleteElement(selectedNode,
4907 0 : getter_AddRefs(delElementTxn));
4908 0 : if (NS_FAILED(result)) { return result; }
4909 0 : if (!delElementTxn) { return NS_ERROR_NULL_POINTER; }
4910 0 : aTxn->AppendChild(delElementTxn);
4911 0 : NS_ADDREF(*aNode = selectedNode);
4912 : }
4913 : }
4914 : }
4915 : }
4916 0 : return result;
4917 : }
4918 :
4919 : nsresult
4920 0 : nsEditor::CreateRange(nsIDOMNode *aStartParent, PRInt32 aStartOffset,
4921 : nsIDOMNode *aEndParent, PRInt32 aEndOffset,
4922 : nsIDOMRange **aRange)
4923 : {
4924 : return nsRange::CreateRange(aStartParent, aStartOffset, aEndParent,
4925 0 : aEndOffset, aRange);
4926 : }
4927 :
4928 : nsresult
4929 0 : nsEditor::AppendNodeToSelectionAsRange(nsIDOMNode *aNode)
4930 : {
4931 0 : NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
4932 0 : nsCOMPtr<nsISelection> selection;
4933 0 : nsresult res = GetSelection(getter_AddRefs(selection));
4934 0 : NS_ENSURE_SUCCESS(res, res);
4935 0 : if(!selection) return NS_ERROR_FAILURE;
4936 :
4937 0 : nsCOMPtr<nsIDOMNode> parentNode;
4938 0 : res = aNode->GetParentNode(getter_AddRefs(parentNode));
4939 0 : NS_ENSURE_SUCCESS(res, res);
4940 0 : NS_ENSURE_TRUE(parentNode, NS_ERROR_NULL_POINTER);
4941 :
4942 : PRInt32 offset;
4943 0 : res = GetChildOffset(aNode, parentNode, offset);
4944 0 : NS_ENSURE_SUCCESS(res, res);
4945 :
4946 0 : nsCOMPtr<nsIDOMRange> range;
4947 0 : res = CreateRange(parentNode, offset, parentNode, offset+1, getter_AddRefs(range));
4948 0 : NS_ENSURE_SUCCESS(res, res);
4949 0 : NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER);
4950 :
4951 0 : return selection->AddRange(range);
4952 : }
4953 :
4954 0 : nsresult nsEditor::ClearSelection()
4955 : {
4956 0 : nsCOMPtr<nsISelection> selection;
4957 0 : nsresult res = nsEditor::GetSelection(getter_AddRefs(selection));
4958 0 : NS_ENSURE_SUCCESS(res, res);
4959 0 : NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
4960 0 : return selection->RemoveAllRanges();
4961 : }
4962 :
4963 : nsresult
4964 0 : nsEditor::CreateHTMLContent(const nsAString& aTag, nsIContent** aContent)
4965 : {
4966 0 : nsCOMPtr<nsIDOMDocument> tempDoc;
4967 0 : GetDocument(getter_AddRefs(tempDoc));
4968 :
4969 0 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(tempDoc);
4970 0 : NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
4971 :
4972 : // XXX Wallpaper over editor bug (editor tries to create elements with an
4973 : // empty nodename).
4974 0 : if (aTag.IsEmpty()) {
4975 : NS_ERROR("Don't pass an empty tag to nsEditor::CreateHTMLContent, "
4976 0 : "check caller.");
4977 0 : return NS_ERROR_FAILURE;
4978 : }
4979 :
4980 0 : return doc->CreateElem(aTag, nsnull, kNameSpaceID_XHTML, aContent);
4981 : }
4982 :
4983 : nsresult
4984 0 : nsEditor::SetAttributeOrEquivalent(nsIDOMElement * aElement,
4985 : const nsAString & aAttribute,
4986 : const nsAString & aValue,
4987 : bool aSuppressTransaction)
4988 : {
4989 0 : return SetAttribute(aElement, aAttribute, aValue);
4990 : }
4991 :
4992 : nsresult
4993 0 : nsEditor::RemoveAttributeOrEquivalent(nsIDOMElement * aElement,
4994 : const nsAString & aAttribute,
4995 : bool aSuppressTransaction)
4996 : {
4997 0 : return RemoveAttribute(aElement, aAttribute);
4998 : }
4999 :
5000 : nsresult
5001 0 : nsEditor::HandleKeyPressEvent(nsIDOMKeyEvent* aKeyEvent)
5002 : {
5003 : // NOTE: When you change this method, you should also change:
5004 : // * editor/libeditor/text/tests/test_texteditor_keyevent_handling.html
5005 : // * editor/libeditor/html/tests/test_htmleditor_keyevent_handling.html
5006 : //
5007 : // And also when you add new key handling, you need to change the subclass's
5008 : // HandleKeyPressEvent()'s switch statement.
5009 :
5010 0 : nsKeyEvent* nativeKeyEvent = GetNativeKeyEvent(aKeyEvent);
5011 0 : NS_ENSURE_TRUE(nativeKeyEvent, NS_ERROR_UNEXPECTED);
5012 0 : NS_ASSERTION(nativeKeyEvent->message == NS_KEY_PRESS,
5013 : "HandleKeyPressEvent gets non-keypress event");
5014 :
5015 : // if we are readonly or disabled, then do nothing.
5016 0 : if (IsReadonly() || IsDisabled()) {
5017 : // consume backspace for disabled and readonly textfields, to prevent
5018 : // back in history, which could be confusing to users
5019 0 : if (nativeKeyEvent->keyCode == nsIDOMKeyEvent::DOM_VK_BACK_SPACE) {
5020 0 : aKeyEvent->PreventDefault();
5021 : }
5022 0 : return NS_OK;
5023 : }
5024 :
5025 0 : switch (nativeKeyEvent->keyCode) {
5026 : case nsIDOMKeyEvent::DOM_VK_META:
5027 : case nsIDOMKeyEvent::DOM_VK_SHIFT:
5028 : case nsIDOMKeyEvent::DOM_VK_CONTROL:
5029 : case nsIDOMKeyEvent::DOM_VK_ALT:
5030 0 : aKeyEvent->PreventDefault(); // consumed
5031 0 : return NS_OK;
5032 : case nsIDOMKeyEvent::DOM_VK_BACK_SPACE:
5033 0 : if (nativeKeyEvent->isControl || nativeKeyEvent->isAlt ||
5034 : nativeKeyEvent->isMeta) {
5035 0 : return NS_OK;
5036 : }
5037 0 : DeleteSelection(nsIEditor::ePrevious);
5038 0 : aKeyEvent->PreventDefault(); // consumed
5039 0 : return NS_OK;
5040 : case nsIDOMKeyEvent::DOM_VK_DELETE:
5041 : // on certain platforms (such as windows) the shift key
5042 : // modifies what delete does (cmd_cut in this case).
5043 : // bailing here to allow the keybindings to do the cut.
5044 0 : if (nativeKeyEvent->isShift || nativeKeyEvent->isControl ||
5045 : nativeKeyEvent->isAlt || nativeKeyEvent->isMeta) {
5046 0 : return NS_OK;
5047 : }
5048 0 : DeleteSelection(nsIEditor::eNext);
5049 0 : aKeyEvent->PreventDefault(); // consumed
5050 0 : return NS_OK;
5051 : }
5052 0 : return NS_OK;
5053 : }
5054 :
5055 : nsresult
5056 0 : nsEditor::HandleInlineSpellCheck(PRInt32 action,
5057 : nsISelection *aSelection,
5058 : nsIDOMNode *previousSelectedNode,
5059 : PRInt32 previousSelectedOffset,
5060 : nsIDOMNode *aStartNode,
5061 : PRInt32 aStartOffset,
5062 : nsIDOMNode *aEndNode,
5063 : PRInt32 aEndOffset)
5064 : {
5065 0 : return mInlineSpellChecker ? mInlineSpellChecker->SpellCheckAfterEditorChange(action,
5066 : aSelection,
5067 : previousSelectedNode,
5068 : previousSelectedOffset,
5069 : aStartNode,
5070 : aStartOffset,
5071 : aEndNode,
5072 0 : aEndOffset) : NS_OK;
5073 : }
5074 :
5075 : already_AddRefed<nsIContent>
5076 0 : nsEditor::FindSelectionRoot(nsINode *aNode)
5077 : {
5078 0 : nsCOMPtr<nsIContent> rootContent = GetRoot();
5079 0 : return rootContent.forget();
5080 : }
5081 :
5082 : nsresult
5083 0 : nsEditor::InitializeSelection(nsIDOMEventTarget* aFocusEventTarget)
5084 : {
5085 0 : nsCOMPtr<nsINode> targetNode = do_QueryInterface(aFocusEventTarget);
5086 0 : NS_ENSURE_TRUE(targetNode, NS_ERROR_INVALID_ARG);
5087 0 : nsCOMPtr<nsIContent> selectionRootContent = FindSelectionRoot(targetNode);
5088 0 : if (!selectionRootContent) {
5089 0 : return NS_OK;
5090 : }
5091 :
5092 : bool isTargetDoc =
5093 0 : targetNode->NodeType() == nsIDOMNode::DOCUMENT_NODE &&
5094 0 : targetNode->HasFlag(NODE_IS_EDITABLE);
5095 :
5096 0 : nsCOMPtr<nsISelection> selection;
5097 0 : nsresult rv = GetSelection(getter_AddRefs(selection));
5098 0 : NS_ENSURE_SUCCESS(rv, rv);
5099 :
5100 0 : nsCOMPtr<nsIPresShell> presShell = GetPresShell();
5101 0 : NS_ENSURE_TRUE(presShell, NS_ERROR_NOT_INITIALIZED);
5102 :
5103 0 : nsCOMPtr<nsISelectionController> selCon;
5104 0 : rv = GetSelectionController(getter_AddRefs(selCon));
5105 0 : NS_ENSURE_SUCCESS(rv, rv);
5106 :
5107 : nsCOMPtr<nsISelectionPrivate> selectionPrivate =
5108 0 : do_QueryInterface(selection);
5109 0 : NS_ENSURE_TRUE(selectionPrivate, NS_ERROR_UNEXPECTED);
5110 :
5111 : // Init the caret
5112 0 : nsRefPtr<nsCaret> caret = presShell->GetCaret();
5113 0 : NS_ENSURE_TRUE(caret, NS_ERROR_UNEXPECTED);
5114 0 : caret->SetIgnoreUserModify(false);
5115 0 : caret->SetCaretDOMSelection(selection);
5116 0 : selCon->SetCaretReadOnly(IsReadonly());
5117 0 : selCon->SetCaretEnabled(true);
5118 :
5119 : // Init selection
5120 0 : selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
5121 0 : selCon->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL);
5122 0 : selCon->RepaintSelection(nsISelectionController::SELECTION_NORMAL);
5123 : // If the computed selection root isn't root content, we should set it
5124 : // as selection ancestor limit. However, if that is root element, it means
5125 : // there is not limitation of the selection, then, we must set NULL.
5126 : // NOTE: If we set a root element to the ancestor limit, some selection
5127 : // methods don't work fine.
5128 0 : if (selectionRootContent->GetParent()) {
5129 0 : selectionPrivate->SetAncestorLimiter(selectionRootContent);
5130 : } else {
5131 0 : selectionPrivate->SetAncestorLimiter(nsnull);
5132 : }
5133 :
5134 : // XXX What case needs this?
5135 0 : if (isTargetDoc) {
5136 : PRInt32 rangeCount;
5137 0 : selection->GetRangeCount(&rangeCount);
5138 0 : if (rangeCount == 0) {
5139 0 : BeginningOfDocument();
5140 : }
5141 : }
5142 :
5143 0 : return NS_OK;
5144 : }
5145 :
5146 : dom::Element *
5147 0 : nsEditor::GetRoot()
5148 : {
5149 0 : if (!mRootElement)
5150 : {
5151 0 : nsCOMPtr<nsIDOMElement> root;
5152 :
5153 : // Let GetRootElement() do the work
5154 0 : GetRootElement(getter_AddRefs(root));
5155 : }
5156 :
5157 0 : return mRootElement;
5158 : }
5159 :
5160 : nsresult
5161 0 : nsEditor::DetermineCurrentDirection()
5162 : {
5163 : // Get the current root direction from its frame
5164 0 : dom::Element *rootElement = GetRoot();
5165 :
5166 : // If we don't have an explicit direction, determine our direction
5167 : // from the content's direction
5168 0 : if (!(mFlags & (nsIPlaintextEditor::eEditorLeftToRight |
5169 0 : nsIPlaintextEditor::eEditorRightToLeft))) {
5170 :
5171 0 : nsIFrame* frame = rootElement->GetPrimaryFrame();
5172 0 : NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
5173 :
5174 : // Set the flag here, to enable us to use the same code path below.
5175 : // It will be flipped before returning from the function.
5176 0 : if (frame->GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
5177 0 : mFlags |= nsIPlaintextEditor::eEditorRightToLeft;
5178 : } else {
5179 0 : mFlags |= nsIPlaintextEditor::eEditorLeftToRight;
5180 : }
5181 : }
5182 :
5183 0 : return NS_OK;
5184 : }
5185 :
5186 : NS_IMETHODIMP
5187 0 : nsEditor::SwitchTextDirection()
5188 : {
5189 : // Get the current root direction from its frame
5190 0 : dom::Element *rootElement = GetRoot();
5191 0 : nsresult rv = DetermineCurrentDirection();
5192 0 : NS_ENSURE_SUCCESS(rv, rv);
5193 :
5194 : // Apply the opposite direction
5195 0 : if (mFlags & nsIPlaintextEditor::eEditorRightToLeft) {
5196 0 : NS_ASSERTION(!(mFlags & nsIPlaintextEditor::eEditorLeftToRight),
5197 : "Unexpected mutually exclusive flag");
5198 0 : mFlags &= ~nsIPlaintextEditor::eEditorRightToLeft;
5199 0 : mFlags |= nsIPlaintextEditor::eEditorLeftToRight;
5200 0 : rv = rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, NS_LITERAL_STRING("ltr"), true);
5201 0 : } else if (mFlags & nsIPlaintextEditor::eEditorLeftToRight) {
5202 0 : NS_ASSERTION(!(mFlags & nsIPlaintextEditor::eEditorRightToLeft),
5203 : "Unexpected mutually exclusive flag");
5204 0 : mFlags |= nsIPlaintextEditor::eEditorRightToLeft;
5205 0 : mFlags &= ~nsIPlaintextEditor::eEditorLeftToRight;
5206 0 : rv = rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, NS_LITERAL_STRING("rtl"), true);
5207 : }
5208 :
5209 0 : return rv;
5210 : }
5211 :
5212 : void
5213 0 : nsEditor::SwitchTextDirectionTo(PRUint32 aDirection)
5214 : {
5215 : // Get the current root direction from its frame
5216 0 : dom::Element *rootElement = GetRoot();
5217 0 : nsresult rv = DetermineCurrentDirection();
5218 0 : NS_ENSURE_SUCCESS(rv, );
5219 :
5220 : // Apply the requested direction
5221 0 : if (aDirection == nsIPlaintextEditor::eEditorLeftToRight &&
5222 : (mFlags & nsIPlaintextEditor::eEditorRightToLeft)) {
5223 0 : NS_ASSERTION(!(mFlags & nsIPlaintextEditor::eEditorLeftToRight),
5224 : "Unexpected mutually exclusive flag");
5225 0 : mFlags &= ~nsIPlaintextEditor::eEditorRightToLeft;
5226 0 : mFlags |= nsIPlaintextEditor::eEditorLeftToRight;
5227 0 : rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, NS_LITERAL_STRING("ltr"), true);
5228 0 : } else if (aDirection == nsIPlaintextEditor::eEditorRightToLeft &&
5229 : (mFlags & nsIPlaintextEditor::eEditorLeftToRight)) {
5230 0 : NS_ASSERTION(!(mFlags & nsIPlaintextEditor::eEditorRightToLeft),
5231 : "Unexpected mutually exclusive flag");
5232 0 : mFlags |= nsIPlaintextEditor::eEditorRightToLeft;
5233 0 : mFlags &= ~nsIPlaintextEditor::eEditorLeftToRight;
5234 0 : rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, NS_LITERAL_STRING("rtl"), true);
5235 : }
5236 : }
5237 :
5238 : #if DEBUG_JOE
5239 : void
5240 : nsEditor::DumpNode(nsIDOMNode *aNode, PRInt32 indent)
5241 : {
5242 : PRInt32 i;
5243 : for (i=0; i<indent; i++)
5244 : printf(" ");
5245 :
5246 : nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
5247 : nsCOMPtr<nsIDOMDocumentFragment> docfrag = do_QueryInterface(aNode);
5248 :
5249 : if (element || docfrag)
5250 : {
5251 : if (element)
5252 : {
5253 : nsAutoString tag;
5254 : element->GetTagName(tag);
5255 : printf("<%s>\n", NS_LossyConvertUTF16toASCII(tag).get());
5256 : }
5257 : else
5258 : {
5259 : printf("<document fragment>\n");
5260 : }
5261 : nsCOMPtr<nsIDOMNodeList> childList;
5262 : aNode->GetChildNodes(getter_AddRefs(childList));
5263 : NS_ENSURE_TRUE(childList, NS_ERROR_NULL_POINTER);
5264 : PRUint32 numChildren;
5265 : childList->GetLength(&numChildren);
5266 : nsCOMPtr<nsIDOMNode> child, tmp;
5267 : aNode->GetFirstChild(getter_AddRefs(child));
5268 : for (i=0; i<numChildren; i++)
5269 : {
5270 : DumpNode(child, indent+1);
5271 : child->GetNextSibling(getter_AddRefs(tmp));
5272 : child = tmp;
5273 : }
5274 : }
5275 : else if (IsTextNode(aNode))
5276 : {
5277 : nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(aNode);
5278 : nsAutoString str;
5279 : textNode->GetData(str);
5280 : nsCAutoString cstr;
5281 : LossyCopyUTF16toASCII(str, cstr);
5282 : cstr.ReplaceChar('\n', ' ');
5283 : printf("<textnode> %s\n", cstr.get());
5284 : }
5285 : }
5286 : #endif
5287 :
5288 : bool
5289 0 : nsEditor::IsModifiableNode(nsIDOMNode *aNode)
5290 : {
5291 0 : return true;
5292 : }
5293 :
5294 : bool
5295 0 : nsEditor::IsModifiableNode(nsINode *aNode)
5296 : {
5297 0 : return true;
5298 : }
5299 :
5300 : nsKeyEvent*
5301 0 : nsEditor::GetNativeKeyEvent(nsIDOMKeyEvent* aDOMKeyEvent)
5302 : {
5303 0 : nsCOMPtr<nsIPrivateDOMEvent> privDOMEvent = do_QueryInterface(aDOMKeyEvent);
5304 0 : NS_ENSURE_TRUE(privDOMEvent, nsnull);
5305 0 : nsEvent* nativeEvent = privDOMEvent->GetInternalNSEvent();
5306 0 : NS_ENSURE_TRUE(nativeEvent, nsnull);
5307 0 : NS_ENSURE_TRUE(nativeEvent->eventStructType == NS_KEY_EVENT, nsnull);
5308 0 : return static_cast<nsKeyEvent*>(nativeEvent);
5309 : }
5310 :
5311 : already_AddRefed<nsIContent>
5312 0 : nsEditor::GetFocusedContent()
5313 : {
5314 0 : nsCOMPtr<nsIDOMEventTarget> piTarget = GetDOMEventTarget();
5315 0 : if (!piTarget) {
5316 0 : return nsnull;
5317 : }
5318 :
5319 0 : nsFocusManager* fm = nsFocusManager::GetFocusManager();
5320 0 : NS_ENSURE_TRUE(fm, nsnull);
5321 :
5322 0 : nsCOMPtr<nsIContent> content = fm->GetFocusedContent();
5323 0 : return SameCOMIdentity(content, piTarget) ? content.forget() : nsnull;
5324 : }
5325 :
5326 : bool
5327 0 : nsEditor::IsActiveInDOMWindow()
5328 : {
5329 0 : nsCOMPtr<nsIDOMEventTarget> piTarget = GetDOMEventTarget();
5330 0 : if (!piTarget) {
5331 0 : return false;
5332 : }
5333 :
5334 0 : nsFocusManager* fm = nsFocusManager::GetFocusManager();
5335 0 : NS_ENSURE_TRUE(fm, false);
5336 :
5337 0 : nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
5338 0 : nsPIDOMWindow* ourWindow = doc->GetWindow();
5339 0 : nsCOMPtr<nsPIDOMWindow> win;
5340 : nsIContent* content =
5341 : nsFocusManager::GetFocusedDescendant(ourWindow, false,
5342 0 : getter_AddRefs(win));
5343 0 : return SameCOMIdentity(content, piTarget);
5344 : }
5345 :
5346 : bool
5347 0 : nsEditor::IsAcceptableInputEvent(nsIDOMEvent* aEvent)
5348 : {
5349 : // If the event is trusted, the event should always cause input.
5350 0 : nsCOMPtr<nsIDOMNSEvent> NSEvent = do_QueryInterface(aEvent);
5351 0 : NS_ENSURE_TRUE(NSEvent, false);
5352 :
5353 : // If this is mouse event but this editor doesn't have focus, we shouldn't
5354 : // handle it.
5355 0 : nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aEvent);
5356 0 : if (mouseEvent) {
5357 0 : nsCOMPtr<nsIContent> focusedContent = GetFocusedContent();
5358 0 : if (!focusedContent) {
5359 0 : return false;
5360 : }
5361 : }
5362 :
5363 : bool isTrusted;
5364 0 : nsresult rv = NSEvent->GetIsTrusted(&isTrusted);
5365 0 : NS_ENSURE_SUCCESS(rv, false);
5366 0 : if (isTrusted) {
5367 0 : return true;
5368 : }
5369 :
5370 : // Ignore untrusted mouse event.
5371 : // XXX Why are we handling other untrusted input events?
5372 0 : if (mouseEvent) {
5373 0 : return false;
5374 : }
5375 :
5376 : // Otherwise, we shouldn't handle any input events when we're not an active
5377 : // element of the DOM window.
5378 0 : return IsActiveInDOMWindow();
5379 : }
5380 :
5381 : NS_IMETHODIMP
5382 0 : nsEditor::GetLastKeypressEventTrusted(bool *aWasTrusted)
5383 : {
5384 0 : NS_ENSURE_ARG_POINTER(aWasTrusted);
5385 :
5386 0 : if (mLastKeypressEventWasTrusted == eTriUnset) {
5387 0 : return NS_ERROR_UNEXPECTED;
5388 : }
5389 :
5390 0 : *aWasTrusted = (mLastKeypressEventWasTrusted == eTriTrue);
5391 0 : return NS_OK;
5392 : }
5393 :
5394 : void
5395 0 : nsEditor::BeginKeypressHandling(nsIDOMNSEvent* aEvent)
5396 : {
5397 0 : NS_ASSERTION(mLastKeypressEventWasTrusted == eTriUnset, "How come our status is not clear?");
5398 :
5399 0 : if (aEvent) {
5400 0 : bool isTrusted = false;
5401 0 : aEvent->GetIsTrusted(&isTrusted);
5402 0 : mLastKeypressEventWasTrusted = isTrusted ? eTriTrue : eTriFalse;
5403 : }
5404 0 : }
5405 :
5406 : void
5407 0 : nsEditor::OnFocus(nsIDOMEventTarget* aFocusEventTarget)
5408 : {
5409 0 : InitializeSelection(aFocusEventTarget);
5410 0 : if (mInlineSpellChecker) {
5411 0 : mInlineSpellChecker->UpdateCurrentDictionary();
5412 : }
5413 4392 : }
|