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