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 1464 : 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 aIndexInContainer)
3529 : {
3530 0 : ContentInserted(aDocument, aContainer, aFirstNewContent, aIndexInContainer);
3531 0 : }
3532 :
3533 : void
3534 0 : nsHTMLEditor::ContentInserted(nsIDocument *aDocument, nsIContent* aContainer,
3535 : nsIContent* aChild, PRInt32 aIndexInContainer)
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 : // Update spellcheck for only the newly-inserted node (bug 743819)
3555 0 : if (mInlineSpellChecker) {
3556 0 : nsRefPtr<nsRange> range = new nsRange();
3557 : nsresult res = range->Set(aContainer, aIndexInContainer,
3558 0 : aContainer, aIndexInContainer + 1);
3559 0 : if (NS_SUCCEEDED(res)) {
3560 0 : mInlineSpellChecker->SpellCheckRange(range);
3561 : }
3562 : }
3563 : }
3564 : }
3565 :
3566 : void
3567 0 : nsHTMLEditor::ContentRemoved(nsIDocument *aDocument, nsIContent* aContainer,
3568 : nsIContent* aChild, PRInt32 aIndexInContainer,
3569 : nsIContent* aPreviousSibling)
3570 : {
3571 0 : nsCOMPtr<nsIHTMLEditor> kungFuDeathGrip(this);
3572 :
3573 0 : if (SameCOMIdentity(aChild, mRootElement)) {
3574 0 : ResetRootElementAndEventTarget();
3575 : }
3576 : // We don't need to handle our own modifications
3577 0 : else if (!mAction && (aContainer ? aContainer->IsEditable() : aDocument->IsEditable())) {
3578 0 : if (aChild && IsMozEditorBogusNode(aChild)) {
3579 : // Ignore removal of the bogus node
3580 : return;
3581 : }
3582 0 : mRules->DocumentModified();
3583 : }
3584 : }
3585 :
3586 :
3587 : /* This routine examines aNode and its ancestors looking for any node which has the
3588 : -moz-user-select: all style lit. Return the highest such ancestor. */
3589 : already_AddRefed<nsIDOMNode>
3590 0 : nsHTMLEditor::FindUserSelectAllNode(nsIDOMNode* aNode)
3591 : {
3592 0 : nsCOMPtr<nsIDOMNode> node = aNode;
3593 0 : nsCOMPtr<nsIDOMElement> root = do_QueryInterface(GetRoot());
3594 0 : if (!nsEditorUtils::IsDescendantOf(aNode, root))
3595 0 : return nsnull;
3596 :
3597 0 : nsCOMPtr<nsIDOMNode> resultNode; // starts out empty
3598 0 : nsAutoString mozUserSelectValue;
3599 0 : while (node)
3600 : {
3601 : // retrieve the computed style of -moz-user-select for node
3602 0 : mHTMLCSSUtils->GetComputedProperty(node, nsEditProperty::cssMozUserSelect, mozUserSelectValue);
3603 0 : if (mozUserSelectValue.EqualsLiteral("all"))
3604 : {
3605 0 : resultNode = node;
3606 : }
3607 0 : if (node != root)
3608 : {
3609 0 : nsCOMPtr<nsIDOMNode> tmp;
3610 0 : node->GetParentNode(getter_AddRefs(tmp));
3611 0 : node = tmp;
3612 : }
3613 : else
3614 : {
3615 0 : node = nsnull;
3616 : }
3617 : }
3618 :
3619 0 : return resultNode.forget();
3620 : }
3621 :
3622 : NS_IMETHODIMP_(bool)
3623 0 : nsHTMLEditor::IsModifiableNode(nsIDOMNode *aNode)
3624 : {
3625 0 : nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
3626 0 : return IsModifiableNode(node);
3627 : }
3628 :
3629 : bool
3630 0 : nsHTMLEditor::IsModifiableNode(nsINode *aNode)
3631 : {
3632 0 : return !aNode || aNode->IsEditable();
3633 : }
3634 :
3635 0 : static nsresult SetSelectionAroundHeadChildren(nsCOMPtr<nsISelection> aSelection, nsWeakPtr aDocWeak)
3636 : {
3637 0 : nsresult res = NS_OK;
3638 : // Set selection around <head> node
3639 0 : nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(aDocWeak);
3640 0 : NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
3641 :
3642 0 : nsCOMPtr<nsIDOMNodeList>nodeList;
3643 0 : res = doc->GetElementsByTagName(NS_LITERAL_STRING("head"), getter_AddRefs(nodeList));
3644 0 : NS_ENSURE_SUCCESS(res, res);
3645 0 : NS_ENSURE_TRUE(nodeList, NS_ERROR_NULL_POINTER);
3646 :
3647 : PRUint32 count;
3648 0 : nodeList->GetLength(&count);
3649 0 : if (count < 1) return NS_ERROR_FAILURE;
3650 :
3651 0 : nsCOMPtr<nsIDOMNode> headNode;
3652 0 : res = nodeList->Item(0, getter_AddRefs(headNode));
3653 0 : NS_ENSURE_SUCCESS(res, res);
3654 0 : NS_ENSURE_TRUE(headNode, NS_ERROR_NULL_POINTER);
3655 :
3656 : // Collapse selection to before first child of the head,
3657 0 : res = aSelection->Collapse(headNode, 0);
3658 0 : NS_ENSURE_SUCCESS(res, res);
3659 :
3660 : // then extend it to just after
3661 0 : nsCOMPtr<nsIDOMNodeList> childNodes;
3662 0 : res = headNode->GetChildNodes(getter_AddRefs(childNodes));
3663 0 : NS_ENSURE_SUCCESS(res, res);
3664 0 : NS_ENSURE_TRUE(childNodes, NS_ERROR_NULL_POINTER);
3665 : PRUint32 childCount;
3666 0 : childNodes->GetLength(&childCount);
3667 :
3668 0 : return aSelection->Extend(headNode, childCount+1);
3669 : }
3670 :
3671 : NS_IMETHODIMP
3672 0 : nsHTMLEditor::GetHeadContentsAsHTML(nsAString& aOutputString)
3673 : {
3674 0 : nsCOMPtr<nsISelection> selection;
3675 0 : nsresult res = GetSelection(getter_AddRefs(selection));
3676 0 : NS_ENSURE_SUCCESS(res, res);
3677 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
3678 :
3679 : // Save current selection
3680 0 : nsAutoSelectionReset selectionResetter(selection, this);
3681 :
3682 0 : res = SetSelectionAroundHeadChildren(selection, mDocWeak);
3683 0 : NS_ENSURE_SUCCESS(res, res);
3684 :
3685 0 : res = OutputToString(NS_LITERAL_STRING("text/html"),
3686 : nsIDocumentEncoder::OutputSelectionOnly,
3687 0 : aOutputString);
3688 0 : if (NS_SUCCEEDED(res))
3689 : {
3690 : // Selection always includes <body></body>,
3691 : // so terminate there
3692 0 : nsReadingIterator<PRUnichar> findIter,endFindIter;
3693 0 : aOutputString.BeginReading(findIter);
3694 0 : aOutputString.EndReading(endFindIter);
3695 : //counting on our parser to always lower case!!!
3696 0 : if (CaseInsensitiveFindInReadable(NS_LITERAL_STRING("<body"),
3697 0 : findIter, endFindIter))
3698 : {
3699 0 : nsReadingIterator<PRUnichar> beginIter;
3700 0 : aOutputString.BeginReading(beginIter);
3701 0 : PRInt32 offset = Distance(beginIter, findIter);//get the distance
3702 :
3703 0 : nsWritingIterator<PRUnichar> writeIter;
3704 0 : aOutputString.BeginWriting(writeIter);
3705 : // Ensure the string ends in a newline
3706 0 : PRUnichar newline ('\n');
3707 0 : findIter.advance(-1);
3708 0 : if (offset ==0 || (offset >0 && (*findIter) != newline)) //check for 0
3709 : {
3710 0 : writeIter.advance(offset);
3711 0 : *writeIter = newline;
3712 0 : aOutputString.Truncate(offset+1);
3713 : }
3714 : }
3715 : }
3716 0 : return res;
3717 : }
3718 :
3719 : NS_IMETHODIMP
3720 0 : nsHTMLEditor::DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed)
3721 : {
3722 : #ifdef DEBUG
3723 0 : NS_ENSURE_TRUE(outNumTests && outNumTestsFailed, NS_ERROR_NULL_POINTER);
3724 :
3725 0 : TextEditorTest *tester = new TextEditorTest();
3726 0 : NS_ENSURE_TRUE(tester, NS_ERROR_OUT_OF_MEMORY);
3727 :
3728 0 : tester->Run(this, outNumTests, outNumTestsFailed);
3729 0 : delete tester;
3730 0 : return NS_OK;
3731 : #else
3732 : return NS_ERROR_NOT_IMPLEMENTED;
3733 : #endif
3734 : }
3735 :
3736 :
3737 : NS_IMETHODIMP
3738 0 : nsHTMLEditor::StyleSheetLoaded(nsCSSStyleSheet* aSheet, bool aWasAlternate,
3739 : nsresult aStatus)
3740 : {
3741 0 : nsresult rv = NS_OK;
3742 0 : nsAutoEditBatch batchIt(this);
3743 :
3744 0 : if (!mLastStyleSheetURL.IsEmpty())
3745 0 : RemoveStyleSheet(mLastStyleSheetURL);
3746 :
3747 0 : nsRefPtr<AddStyleSheetTxn> txn;
3748 0 : rv = CreateTxnForAddStyleSheet(aSheet, getter_AddRefs(txn));
3749 0 : if (!txn) rv = NS_ERROR_NULL_POINTER;
3750 0 : if (NS_SUCCEEDED(rv))
3751 : {
3752 0 : rv = DoTransaction(txn);
3753 0 : if (NS_SUCCEEDED(rv))
3754 : {
3755 : // Get the URI, then url spec from the sheet
3756 0 : nsCAutoString spec;
3757 0 : rv = aSheet->GetSheetURI()->GetSpec(spec);
3758 :
3759 0 : if (NS_SUCCEEDED(rv))
3760 : {
3761 : // Save it so we can remove before applying the next one
3762 0 : mLastStyleSheetURL.AssignWithConversion(spec.get());
3763 :
3764 : // Also save in our arrays of urls and sheets
3765 0 : AddNewStyleSheetToList(mLastStyleSheetURL, aSheet);
3766 : }
3767 : }
3768 : }
3769 :
3770 0 : return NS_OK;
3771 : }
3772 :
3773 :
3774 : /** All editor operations which alter the doc should be prefaced
3775 : * with a call to StartOperation, naming the action and direction */
3776 : NS_IMETHODIMP
3777 0 : nsHTMLEditor::StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection)
3778 : {
3779 : // Protect the edit rules object from dying
3780 0 : nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
3781 :
3782 0 : nsEditor::StartOperation(opID, aDirection); // will set mAction, mDirection
3783 0 : if (mRules) return mRules->BeforeEdit(mAction, mDirection);
3784 0 : return NS_OK;
3785 : }
3786 :
3787 :
3788 : /** All editor operations which alter the doc should be followed
3789 : * with a call to EndOperation */
3790 : NS_IMETHODIMP
3791 0 : nsHTMLEditor::EndOperation()
3792 : {
3793 : // Protect the edit rules object from dying
3794 0 : nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
3795 :
3796 : // post processing
3797 0 : nsresult res = NS_OK;
3798 0 : if (mRules) res = mRules->AfterEdit(mAction, mDirection);
3799 0 : nsEditor::EndOperation(); // will clear mAction, mDirection
3800 0 : return res;
3801 : }
3802 :
3803 : bool
3804 0 : nsHTMLEditor::TagCanContainTag(const nsAString& aParentTag, const nsAString& aChildTag)
3805 : {
3806 0 : nsIParserService* parserService = nsContentUtils::GetParserService();
3807 :
3808 : PRInt32 childTagEnum;
3809 : // XXX Should this handle #cdata-section too?
3810 0 : if (aChildTag.EqualsLiteral("#text")) {
3811 0 : childTagEnum = eHTMLTag_text;
3812 : }
3813 : else {
3814 0 : childTagEnum = parserService->HTMLStringTagToId(aChildTag);
3815 : }
3816 :
3817 0 : PRInt32 parentTagEnum = parserService->HTMLStringTagToId(aParentTag);
3818 0 : NS_ASSERTION(parentTagEnum < NS_HTML_TAG_MAX,
3819 : "Fix the caller, this type of node can never contain children.");
3820 :
3821 0 : return nsHTMLEditUtils::CanContain(parentTagEnum, childTagEnum);
3822 : }
3823 :
3824 : bool
3825 0 : nsHTMLEditor::IsContainer(nsINode* aNode)
3826 : {
3827 0 : nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode);
3828 0 : return IsContainer(node);
3829 : }
3830 :
3831 : bool
3832 0 : nsHTMLEditor::IsContainer(nsIDOMNode *aNode)
3833 : {
3834 0 : if (!aNode) {
3835 0 : return false;
3836 : }
3837 :
3838 0 : nsAutoString stringTag;
3839 :
3840 0 : nsresult rv = aNode->GetNodeName(stringTag);
3841 0 : NS_ENSURE_SUCCESS(rv, false);
3842 :
3843 : PRInt32 tagEnum;
3844 : // XXX Should this handle #cdata-section too?
3845 0 : if (stringTag.EqualsLiteral("#text")) {
3846 0 : tagEnum = eHTMLTag_text;
3847 : }
3848 : else {
3849 0 : tagEnum = nsContentUtils::GetParserService()->HTMLStringTagToId(stringTag);
3850 : }
3851 :
3852 0 : return nsHTMLEditUtils::IsContainer(tagEnum);
3853 : }
3854 :
3855 :
3856 : NS_IMETHODIMP
3857 0 : nsHTMLEditor::SelectEntireDocument(nsISelection *aSelection)
3858 : {
3859 0 : if (!aSelection || !mRules) { return NS_ERROR_NULL_POINTER; }
3860 :
3861 : // Protect the edit rules object from dying
3862 0 : nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
3863 :
3864 : // get editor root node
3865 0 : nsCOMPtr<nsIDOMElement> rootElement = do_QueryInterface(GetRoot());
3866 :
3867 : // is doc empty?
3868 : bool bDocIsEmpty;
3869 0 : nsresult res = mRules->DocumentIsEmpty(&bDocIsEmpty);
3870 0 : NS_ENSURE_SUCCESS(res, res);
3871 :
3872 0 : if (bDocIsEmpty)
3873 : {
3874 : // if its empty dont select entire doc - that would select the bogus node
3875 0 : return aSelection->Collapse(rootElement, 0);
3876 : }
3877 :
3878 0 : return nsEditor::SelectEntireDocument(aSelection);
3879 : }
3880 :
3881 : NS_IMETHODIMP
3882 0 : nsHTMLEditor::SelectAll()
3883 : {
3884 0 : ForceCompositionEnd();
3885 :
3886 : nsresult rv;
3887 0 : nsCOMPtr<nsISelectionController> selCon;
3888 0 : rv = GetSelectionController(getter_AddRefs(selCon));
3889 0 : NS_ENSURE_SUCCESS(rv, rv);
3890 :
3891 0 : nsCOMPtr<nsISelection> selection;
3892 0 : rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
3893 0 : getter_AddRefs(selection));
3894 0 : NS_ENSURE_SUCCESS(rv, rv);
3895 :
3896 0 : nsCOMPtr<nsIDOMNode> anchorNode;
3897 0 : rv = selection->GetAnchorNode(getter_AddRefs(anchorNode));
3898 0 : NS_ENSURE_SUCCESS(rv, rv);
3899 :
3900 0 : nsCOMPtr<nsIContent> anchorContent = do_QueryInterface(anchorNode, &rv);
3901 0 : NS_ENSURE_SUCCESS(rv, rv);
3902 :
3903 : // If the anchor content has independent selection, we never need to explicitly
3904 : // select its children.
3905 0 : if (anchorContent->HasIndependentSelection()) {
3906 0 : nsCOMPtr<nsISelectionPrivate> selPriv = do_QueryInterface(selection);
3907 0 : NS_ENSURE_TRUE(selPriv, NS_ERROR_UNEXPECTED);
3908 0 : rv = selPriv->SetAncestorLimiter(nsnull);
3909 0 : NS_ENSURE_SUCCESS(rv, rv);
3910 0 : nsCOMPtr<nsIDOMNode> rootElement = do_QueryInterface(mRootElement, &rv);
3911 0 : NS_ENSURE_SUCCESS(rv, rv);
3912 0 : return selection->SelectAllChildren(rootElement);
3913 : }
3914 :
3915 0 : nsCOMPtr<nsIPresShell> ps = GetPresShell();
3916 0 : nsIContent *rootContent = anchorContent->GetSelectionRootContent(ps);
3917 0 : NS_ENSURE_TRUE(rootContent, NS_ERROR_UNEXPECTED);
3918 :
3919 0 : nsCOMPtr<nsIDOMNode> rootElement = do_QueryInterface(rootContent, &rv);
3920 0 : NS_ENSURE_SUCCESS(rv, rv);
3921 :
3922 0 : return selection->SelectAllChildren(rootElement);
3923 : }
3924 :
3925 :
3926 : // this will NOT find aAttribute unless aAttribute has a non-null value
3927 : // so singleton attributes like <Table border> will not be matched!
3928 0 : void nsHTMLEditor::IsTextPropertySetByContent(nsIDOMNode *aNode,
3929 : nsIAtom *aProperty,
3930 : const nsAString *aAttribute,
3931 : const nsAString *aValue,
3932 : bool &aIsSet,
3933 : nsIDOMNode **aStyleNode,
3934 : nsAString *outValue)
3935 : {
3936 : nsresult result;
3937 0 : aIsSet = false; // must be initialized to false for code below to work
3938 0 : nsAutoString propName;
3939 0 : aProperty->ToString(propName);
3940 0 : nsCOMPtr<nsIDOMNode>node = aNode;
3941 :
3942 0 : while (node)
3943 : {
3944 0 : nsCOMPtr<nsIDOMElement>element;
3945 0 : element = do_QueryInterface(node);
3946 0 : if (element)
3947 : {
3948 0 : nsAutoString tag, value;
3949 0 : element->GetTagName(tag);
3950 0 : if (propName.Equals(tag, nsCaseInsensitiveStringComparator()))
3951 : {
3952 0 : bool found = false;
3953 0 : if (aAttribute && 0!=aAttribute->Length())
3954 : {
3955 0 : element->GetAttribute(*aAttribute, value);
3956 0 : if (outValue) *outValue = value;
3957 0 : if (!value.IsEmpty())
3958 : {
3959 0 : if (!aValue) {
3960 0 : found = true;
3961 : }
3962 : else
3963 : {
3964 0 : nsString tString(*aValue);
3965 0 : if (tString.Equals(value, nsCaseInsensitiveStringComparator())) {
3966 0 : found = true;
3967 : }
3968 : else { // we found the prop with the attribute, but the value doesn't match
3969 : break;
3970 : }
3971 : }
3972 : }
3973 : }
3974 : else {
3975 0 : found = true;
3976 : }
3977 0 : if (found)
3978 : {
3979 0 : aIsSet = true;
3980 : break;
3981 : }
3982 : }
3983 : }
3984 0 : nsCOMPtr<nsIDOMNode>temp;
3985 0 : result = node->GetParentNode(getter_AddRefs(temp));
3986 0 : if (NS_SUCCEEDED(result) && temp) {
3987 0 : node = temp;
3988 : }
3989 : else {
3990 0 : node = nsnull;
3991 : }
3992 : }
3993 0 : }
3994 :
3995 :
3996 : //================================================================
3997 : // HTML Editor methods
3998 : //
3999 : // Note: Table Editing methods are implemented in nsTableEditor.cpp
4000 : //
4001 :
4002 :
4003 : bool
4004 0 : nsHTMLEditor::IsNodeInActiveEditor(nsIDOMNode* aNode)
4005 : {
4006 0 : nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
4007 0 : return node && IsNodeInActiveEditor(node);
4008 : }
4009 :
4010 : bool
4011 0 : nsHTMLEditor::IsNodeInActiveEditor(nsINode* aNode)
4012 : {
4013 0 : nsIContent* activeEditingHost = GetActiveEditingHost();
4014 0 : if (!activeEditingHost) {
4015 0 : return false;
4016 : }
4017 0 : return nsContentUtils::ContentIsDescendantOf(aNode, activeEditingHost);
4018 : }
4019 :
4020 : bool
4021 0 : nsHTMLEditor::SetCaretInTableCell(nsIDOMElement* aElement)
4022 : {
4023 0 : nsCOMPtr<dom::Element> element = do_QueryInterface(aElement);
4024 0 : if (!element || !element->IsHTML() ||
4025 0 : !nsHTMLEditUtils::IsTableElement(element) ||
4026 0 : !IsNodeInActiveEditor(element)) {
4027 0 : return false;
4028 : }
4029 :
4030 0 : nsIContent* node = element;
4031 0 : while (node->HasChildren()) {
4032 0 : node = node->GetFirstChild();
4033 : }
4034 :
4035 : // Set selection at beginning of the found node
4036 0 : nsCOMPtr<nsISelection> selection;
4037 0 : nsresult rv = GetSelection(getter_AddRefs(selection));
4038 0 : NS_ENSURE_SUCCESS(rv, false);
4039 0 : NS_ENSURE_TRUE(selection, false);
4040 :
4041 0 : return NS_SUCCEEDED(selection->CollapseNative(node, 0));
4042 : }
4043 :
4044 : ///////////////////////////////////////////////////////////////////////////
4045 : // GetEnclosingTable: find ancestor who is a table, if any
4046 : //
4047 : nsCOMPtr<nsIDOMNode>
4048 0 : nsHTMLEditor::GetEnclosingTable(nsIDOMNode *aNode)
4049 : {
4050 0 : NS_PRECONDITION(aNode, "null node passed to nsHTMLEditor::GetEnclosingTable");
4051 0 : nsCOMPtr<nsIDOMNode> tbl, tmp, node = aNode;
4052 :
4053 0 : while (!tbl)
4054 : {
4055 0 : tmp = GetBlockNodeParent(node);
4056 0 : if (!tmp) break;
4057 0 : if (nsHTMLEditUtils::IsTable(tmp)) tbl = tmp;
4058 0 : node = tmp;
4059 : }
4060 : return tbl;
4061 : }
4062 :
4063 :
4064 : #ifdef PRE_NODE_IN_BODY
4065 : nsCOMPtr<nsIDOMElement> nsHTMLEditor::FindPreElement()
4066 : {
4067 : nsCOMPtr<nsIDOMDocument> domdoc;
4068 : nsEditor::GetDocument(getter_AddRefs(domdoc));
4069 : NS_ENSURE_TRUE(domdoc, 0);
4070 :
4071 : nsCOMPtr<nsIDocument> doc (do_QueryInterface(domdoc));
4072 : NS_ENSURE_TRUE(doc, 0);
4073 :
4074 : nsCOMPtr<nsIContent> rootContent = doc->GetRootElement();
4075 : NS_ENSURE_TRUE(rootContent, 0);
4076 :
4077 : nsCOMPtr<nsIDOMNode> rootNode (do_QueryInterface(rootContent));
4078 : NS_ENSURE_TRUE(rootNode, 0);
4079 :
4080 : nsString prestr ("PRE"); // GetFirstNodeOfType requires capitals
4081 : nsCOMPtr<nsIDOMNode> preNode;
4082 : if (NS_FAILED(nsEditor::GetFirstNodeOfType(rootNode, prestr,
4083 : getter_AddRefs(preNode))))
4084 : return 0;
4085 :
4086 : return do_QueryInterface(preNode);
4087 : }
4088 : #endif /* PRE_NODE_IN_BODY */
4089 :
4090 : /* this method scans the selection for adjacent text nodes
4091 : * and collapses them into a single text node.
4092 : * "adjacent" means literally adjacent siblings of the same parent.
4093 : * Uses nsEditor::JoinNodes so action is undoable.
4094 : * Should be called within the context of a batch transaction.
4095 : */
4096 : NS_IMETHODIMP
4097 0 : nsHTMLEditor::CollapseAdjacentTextNodes(nsIDOMRange *aInRange)
4098 : {
4099 0 : NS_ENSURE_TRUE(aInRange, NS_ERROR_NULL_POINTER);
4100 0 : nsAutoTxnsConserveSelection dontSpazMySelection(this);
4101 0 : nsTArray<nsIDOMNode*> textNodes;
4102 : // we can't actually do anything during iteration, so store the text nodes in an array
4103 : // don't bother ref counting them because we know we can hold them for the
4104 : // lifetime of this method
4105 :
4106 :
4107 : // build a list of editable text nodes
4108 : nsresult result;
4109 : nsCOMPtr<nsIContentIterator> iter =
4110 0 : do_CreateInstance("@mozilla.org/content/subtree-content-iterator;1", &result);
4111 0 : NS_ENSURE_SUCCESS(result, result);
4112 :
4113 0 : iter->Init(aInRange);
4114 :
4115 0 : while (!iter->IsDone())
4116 : {
4117 0 : nsINode* node = iter->GetCurrentNode();
4118 0 : if (node->NodeType() == nsIDOMNode::TEXT_NODE &&
4119 0 : IsEditable(static_cast<nsIContent*>(node))) {
4120 0 : nsCOMPtr<nsIDOMNode> domNode = do_QueryInterface(node);
4121 0 : textNodes.AppendElement(domNode);
4122 : }
4123 :
4124 0 : iter->Next();
4125 : }
4126 :
4127 : // now that I have a list of text nodes, collapse adjacent text nodes
4128 : // NOTE: assumption that JoinNodes keeps the righthand node
4129 0 : while (textNodes.Length() > 1)
4130 : {
4131 : // we assume a textNodes entry can't be nsnull
4132 0 : nsIDOMNode *leftTextNode = textNodes[0];
4133 0 : nsIDOMNode *rightTextNode = textNodes[1];
4134 0 : NS_ASSERTION(leftTextNode && rightTextNode,"left or rightTextNode null in CollapseAdjacentTextNodes");
4135 :
4136 : // get the prev sibling of the right node, and see if its leftTextNode
4137 0 : nsCOMPtr<nsIDOMNode> prevSibOfRightNode;
4138 : result =
4139 0 : rightTextNode->GetPreviousSibling(getter_AddRefs(prevSibOfRightNode));
4140 0 : NS_ENSURE_SUCCESS(result, result);
4141 0 : if (prevSibOfRightNode && (prevSibOfRightNode == leftTextNode))
4142 : {
4143 0 : nsCOMPtr<nsIDOMNode> parent;
4144 0 : result = rightTextNode->GetParentNode(getter_AddRefs(parent));
4145 0 : NS_ENSURE_SUCCESS(result, result);
4146 0 : NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
4147 0 : result = JoinNodes(leftTextNode, rightTextNode, parent);
4148 0 : NS_ENSURE_SUCCESS(result, result);
4149 : }
4150 :
4151 0 : textNodes.RemoveElementAt(0); // remove the leftmost text node from the list
4152 : }
4153 :
4154 0 : return result;
4155 : }
4156 :
4157 : NS_IMETHODIMP
4158 0 : nsHTMLEditor::SetSelectionAtDocumentStart(nsISelection *aSelection)
4159 : {
4160 0 : dom::Element* rootElement = GetRoot();
4161 0 : NS_ENSURE_TRUE(rootElement, NS_ERROR_NULL_POINTER);
4162 :
4163 0 : return aSelection->CollapseNative(rootElement, 0);
4164 : }
4165 :
4166 :
4167 : ///////////////////////////////////////////////////////////////////////////
4168 : // RemoveBlockContainer: remove inNode, reparenting its children into their
4169 : // the parent of inNode. In addition, INSERT ANY BR's NEEDED
4170 : // TO PRESERVE IDENTITY OF REMOVED BLOCK.
4171 : //
4172 : nsresult
4173 0 : nsHTMLEditor::RemoveBlockContainer(nsIDOMNode *inNode)
4174 : {
4175 0 : NS_ENSURE_TRUE(inNode, NS_ERROR_NULL_POINTER);
4176 : nsresult res;
4177 0 : nsCOMPtr<nsIDOMNode> sibling, child, unused;
4178 :
4179 : // Two possibilities: the container cold be empty of editable content.
4180 : // If that is the case, we need to compare what is before and after inNode
4181 : // to determine if we need a br.
4182 : // Or it could not be empty, in which case we have to compare previous
4183 : // sibling and first child to determine if we need a leading br,
4184 : // and compare following sibling and last child to determine if we need a
4185 : // trailing br.
4186 :
4187 0 : res = GetFirstEditableChild(inNode, address_of(child));
4188 0 : NS_ENSURE_SUCCESS(res, res);
4189 :
4190 0 : if (child) // the case of inNode not being empty
4191 : {
4192 : // we need a br at start unless:
4193 : // 1) previous sibling of inNode is a block, OR
4194 : // 2) previous sibling of inNode is a br, OR
4195 : // 3) first child of inNode is a block OR
4196 : // 4) either is null
4197 :
4198 0 : res = GetPriorHTMLSibling(inNode, address_of(sibling));
4199 0 : NS_ENSURE_SUCCESS(res, res);
4200 0 : if (sibling && !IsBlockNode(sibling) && !nsTextEditUtils::IsBreak(sibling))
4201 : {
4202 0 : res = GetFirstEditableChild(inNode, address_of(child));
4203 0 : NS_ENSURE_SUCCESS(res, res);
4204 0 : if (child && !IsBlockNode(child))
4205 : {
4206 : // insert br node
4207 0 : res = CreateBR(inNode, 0, address_of(unused));
4208 0 : NS_ENSURE_SUCCESS(res, res);
4209 : }
4210 : }
4211 :
4212 : // we need a br at end unless:
4213 : // 1) following sibling of inNode is a block, OR
4214 : // 2) last child of inNode is a block, OR
4215 : // 3) last child of inNode is a block OR
4216 : // 4) either is null
4217 :
4218 0 : res = GetNextHTMLSibling(inNode, address_of(sibling));
4219 0 : NS_ENSURE_SUCCESS(res, res);
4220 0 : if (sibling && !IsBlockNode(sibling))
4221 : {
4222 0 : res = GetLastEditableChild(inNode, address_of(child));
4223 0 : NS_ENSURE_SUCCESS(res, res);
4224 0 : if (child && !IsBlockNode(child) && !nsTextEditUtils::IsBreak(child))
4225 : {
4226 : // insert br node
4227 : PRUint32 len;
4228 0 : res = GetLengthOfDOMNode(inNode, len);
4229 0 : NS_ENSURE_SUCCESS(res, res);
4230 0 : res = CreateBR(inNode, (PRInt32)len, address_of(unused));
4231 0 : NS_ENSURE_SUCCESS(res, res);
4232 : }
4233 : }
4234 : }
4235 : else // the case of inNode being empty
4236 : {
4237 : // we need a br at start unless:
4238 : // 1) previous sibling of inNode is a block, OR
4239 : // 2) previous sibling of inNode is a br, OR
4240 : // 3) following sibling of inNode is a block, OR
4241 : // 4) following sibling of inNode is a br OR
4242 : // 5) either is null
4243 0 : res = GetPriorHTMLSibling(inNode, address_of(sibling));
4244 0 : NS_ENSURE_SUCCESS(res, res);
4245 0 : if (sibling && !IsBlockNode(sibling) && !nsTextEditUtils::IsBreak(sibling))
4246 : {
4247 0 : res = GetNextHTMLSibling(inNode, address_of(sibling));
4248 0 : NS_ENSURE_SUCCESS(res, res);
4249 0 : if (sibling && !IsBlockNode(sibling) && !nsTextEditUtils::IsBreak(sibling))
4250 : {
4251 : // insert br node
4252 0 : res = CreateBR(inNode, 0, address_of(unused));
4253 0 : NS_ENSURE_SUCCESS(res, res);
4254 : }
4255 : }
4256 : }
4257 :
4258 : // now remove container
4259 0 : return RemoveContainer(inNode);
4260 : }
4261 :
4262 :
4263 : ///////////////////////////////////////////////////////////////////////////
4264 : // GetPriorHTMLSibling: returns the previous editable sibling, if there is
4265 : // one within the parent
4266 : //
4267 : nsresult
4268 0 : nsHTMLEditor::GetPriorHTMLSibling(nsIDOMNode *inNode, nsCOMPtr<nsIDOMNode> *outNode)
4269 : {
4270 0 : NS_ENSURE_TRUE(outNode && inNode, NS_ERROR_NULL_POINTER);
4271 0 : nsresult res = NS_OK;
4272 0 : *outNode = nsnull;
4273 0 : nsCOMPtr<nsIDOMNode> temp, node = do_QueryInterface(inNode);
4274 :
4275 0 : while (1)
4276 : {
4277 0 : res = node->GetPreviousSibling(getter_AddRefs(temp));
4278 0 : NS_ENSURE_SUCCESS(res, res);
4279 0 : if (!temp) {
4280 : // return null sibling
4281 0 : return NS_OK;
4282 : }
4283 : // if it's editable, we're done
4284 0 : if (IsEditable(temp)) break;
4285 : // otherwise try again
4286 0 : node = temp;
4287 : }
4288 0 : *outNode = temp;
4289 0 : return res;
4290 : }
4291 :
4292 :
4293 :
4294 : ///////////////////////////////////////////////////////////////////////////
4295 : // GetPriorHTMLSibling: returns the previous editable sibling, if there is
4296 : // one within the parent. just like above routine but
4297 : // takes a parent/offset instead of a node.
4298 : //
4299 : nsresult
4300 0 : nsHTMLEditor::GetPriorHTMLSibling(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr<nsIDOMNode> *outNode)
4301 : {
4302 0 : NS_ENSURE_TRUE(outNode && inParent, NS_ERROR_NULL_POINTER);
4303 0 : nsresult res = NS_OK;
4304 0 : *outNode = nsnull;
4305 0 : if (inOffset <= 0) {
4306 : // return null sibling if at offset zero
4307 0 : return NS_OK;
4308 : }
4309 0 : nsCOMPtr<nsIDOMNode> node = nsEditor::GetChildAt(inParent,inOffset-1);
4310 0 : if (node && IsEditable(node)) {
4311 0 : *outNode = node;
4312 0 : return res;
4313 : }
4314 : // else
4315 0 : return GetPriorHTMLSibling(node, outNode);
4316 : }
4317 :
4318 :
4319 :
4320 : ///////////////////////////////////////////////////////////////////////////
4321 : // GetNextHTMLSibling: returns the next editable sibling, if there is
4322 : // one within the parent
4323 : //
4324 : nsresult
4325 0 : nsHTMLEditor::GetNextHTMLSibling(nsIDOMNode *inNode, nsCOMPtr<nsIDOMNode> *outNode)
4326 : {
4327 0 : NS_ENSURE_TRUE(outNode, NS_ERROR_NULL_POINTER);
4328 0 : nsresult res = NS_OK;
4329 0 : *outNode = nsnull;
4330 0 : nsCOMPtr<nsIDOMNode> temp, node = do_QueryInterface(inNode);
4331 :
4332 0 : while (1)
4333 : {
4334 0 : res = node->GetNextSibling(getter_AddRefs(temp));
4335 0 : NS_ENSURE_SUCCESS(res, res);
4336 0 : if (!temp) {
4337 : // return null sibling
4338 0 : return NS_OK;
4339 : }
4340 : // if it's editable, we're done
4341 0 : if (IsEditable(temp)) break;
4342 : // otherwise try again
4343 0 : node = temp;
4344 : }
4345 0 : *outNode = temp;
4346 0 : return res;
4347 : }
4348 :
4349 :
4350 :
4351 : ///////////////////////////////////////////////////////////////////////////
4352 : // GetNextHTMLSibling: returns the next editable sibling, if there is
4353 : // one within the parent. just like above routine but
4354 : // takes a parent/offset instead of a node.
4355 : //
4356 : nsresult
4357 0 : nsHTMLEditor::GetNextHTMLSibling(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr<nsIDOMNode> *outNode)
4358 : {
4359 0 : NS_ENSURE_TRUE(outNode && inParent, NS_ERROR_NULL_POINTER);
4360 0 : nsresult res = NS_OK;
4361 0 : *outNode = nsnull;
4362 0 : nsCOMPtr<nsIDOMNode> node = nsEditor::GetChildAt(inParent,inOffset);
4363 0 : if (!node) {
4364 : // return null sibling if no sibling
4365 0 : return NS_OK;
4366 : }
4367 0 : if (node && IsEditable(node)) {
4368 0 : *outNode = node;
4369 0 : return res;
4370 : }
4371 : // else
4372 0 : return GetPriorHTMLSibling(node, outNode);
4373 : }
4374 :
4375 :
4376 :
4377 : ///////////////////////////////////////////////////////////////////////////
4378 : // GetPriorHTMLNode: returns the previous editable leaf node, if there is
4379 : // one within the <body>
4380 : //
4381 : nsresult
4382 0 : nsHTMLEditor::GetPriorHTMLNode(nsIDOMNode *inNode, nsCOMPtr<nsIDOMNode> *outNode, bool bNoBlockCrossing)
4383 : {
4384 0 : NS_ENSURE_TRUE(outNode, NS_ERROR_NULL_POINTER);
4385 :
4386 0 : nsIContent* activeEditingHost = GetActiveEditingHost();
4387 0 : if (!activeEditingHost) {
4388 0 : *outNode = nsnull;
4389 0 : return NS_OK;
4390 : }
4391 :
4392 0 : nsresult res = GetPriorNode(inNode, true, address_of(*outNode), bNoBlockCrossing, activeEditingHost);
4393 0 : NS_ENSURE_SUCCESS(res, res);
4394 :
4395 0 : NS_ASSERTION(!*outNode || IsNodeInActiveEditor(*outNode),
4396 : "GetPriorNode screwed up");
4397 0 : return res;
4398 : }
4399 :
4400 :
4401 : ///////////////////////////////////////////////////////////////////////////
4402 : // GetPriorHTMLNode: same as above but takes {parent,offset} instead of node
4403 : //
4404 : nsresult
4405 0 : nsHTMLEditor::GetPriorHTMLNode(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr<nsIDOMNode> *outNode, bool bNoBlockCrossing)
4406 : {
4407 0 : NS_ENSURE_TRUE(outNode, NS_ERROR_NULL_POINTER);
4408 :
4409 0 : nsIContent* activeEditingHost = GetActiveEditingHost();
4410 0 : if (!activeEditingHost) {
4411 0 : *outNode = nsnull;
4412 0 : return NS_OK;
4413 : }
4414 :
4415 0 : nsresult res = GetPriorNode(inParent, inOffset, true, address_of(*outNode), bNoBlockCrossing, activeEditingHost);
4416 0 : NS_ENSURE_SUCCESS(res, res);
4417 :
4418 0 : NS_ASSERTION(!*outNode || IsNodeInActiveEditor(*outNode),
4419 : "GetPriorNode screwed up");
4420 0 : return res;
4421 : }
4422 :
4423 :
4424 : ///////////////////////////////////////////////////////////////////////////
4425 : // GetNextHTMLNode: returns the next editable leaf node, if there is
4426 : // one within the <body>
4427 : //
4428 : nsresult
4429 0 : nsHTMLEditor::GetNextHTMLNode(nsIDOMNode *inNode, nsCOMPtr<nsIDOMNode> *outNode, bool bNoBlockCrossing)
4430 : {
4431 0 : NS_ENSURE_TRUE(outNode, NS_ERROR_NULL_POINTER);
4432 0 : nsresult res = GetNextNode(inNode, true, address_of(*outNode), bNoBlockCrossing);
4433 0 : NS_ENSURE_SUCCESS(res, res);
4434 :
4435 : // if it's not in the body, then zero it out
4436 0 : if (*outNode && !IsNodeInActiveEditor(*outNode)) {
4437 0 : *outNode = nsnull;
4438 : }
4439 0 : return res;
4440 : }
4441 :
4442 :
4443 : ///////////////////////////////////////////////////////////////////////////
4444 : // GetNHTMLextNode: same as above but takes {parent,offset} instead of node
4445 : //
4446 : nsresult
4447 0 : nsHTMLEditor::GetNextHTMLNode(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr<nsIDOMNode> *outNode, bool bNoBlockCrossing)
4448 : {
4449 0 : NS_ENSURE_TRUE(outNode, NS_ERROR_NULL_POINTER);
4450 0 : nsresult res = GetNextNode(inParent, inOffset, true, address_of(*outNode), bNoBlockCrossing);
4451 0 : NS_ENSURE_SUCCESS(res, res);
4452 :
4453 : // if it's not in the body, then zero it out
4454 0 : if (*outNode && !IsNodeInActiveEditor(*outNode)) {
4455 0 : *outNode = nsnull;
4456 : }
4457 0 : return res;
4458 : }
4459 :
4460 :
4461 : nsresult
4462 0 : nsHTMLEditor::IsFirstEditableChild( nsIDOMNode *aNode, bool *aOutIsFirst)
4463 : {
4464 : // check parms
4465 0 : NS_ENSURE_TRUE(aOutIsFirst && aNode, NS_ERROR_NULL_POINTER);
4466 :
4467 : // init out parms
4468 0 : *aOutIsFirst = false;
4469 :
4470 : // find first editable child and compare it to aNode
4471 0 : nsCOMPtr<nsIDOMNode> parent, firstChild;
4472 0 : nsresult res = aNode->GetParentNode(getter_AddRefs(parent));
4473 0 : NS_ENSURE_SUCCESS(res, res);
4474 0 : NS_ENSURE_TRUE(parent, NS_ERROR_FAILURE);
4475 0 : res = GetFirstEditableChild(parent, address_of(firstChild));
4476 0 : NS_ENSURE_SUCCESS(res, res);
4477 :
4478 0 : *aOutIsFirst = (firstChild.get() == aNode);
4479 0 : return res;
4480 : }
4481 :
4482 :
4483 : nsresult
4484 0 : nsHTMLEditor::IsLastEditableChild( nsIDOMNode *aNode, bool *aOutIsLast)
4485 : {
4486 : // check parms
4487 0 : NS_ENSURE_TRUE(aOutIsLast && aNode, NS_ERROR_NULL_POINTER);
4488 :
4489 : // init out parms
4490 0 : *aOutIsLast = false;
4491 :
4492 : // find last editable child and compare it to aNode
4493 0 : nsCOMPtr<nsIDOMNode> parent, lastChild;
4494 0 : nsresult res = aNode->GetParentNode(getter_AddRefs(parent));
4495 0 : NS_ENSURE_SUCCESS(res, res);
4496 0 : NS_ENSURE_TRUE(parent, NS_ERROR_FAILURE);
4497 0 : res = GetLastEditableChild(parent, address_of(lastChild));
4498 0 : NS_ENSURE_SUCCESS(res, res);
4499 :
4500 0 : *aOutIsLast = (lastChild.get() == aNode);
4501 0 : return res;
4502 : }
4503 :
4504 :
4505 : nsresult
4506 0 : nsHTMLEditor::GetFirstEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutFirstChild)
4507 : {
4508 : // check parms
4509 0 : NS_ENSURE_TRUE(aOutFirstChild && aNode, NS_ERROR_NULL_POINTER);
4510 :
4511 : // init out parms
4512 0 : *aOutFirstChild = nsnull;
4513 :
4514 : // find first editable child
4515 0 : nsCOMPtr<nsIDOMNode> child;
4516 0 : nsresult res = aNode->GetFirstChild(getter_AddRefs(child));
4517 0 : NS_ENSURE_SUCCESS(res, res);
4518 :
4519 0 : while (child && !IsEditable(child))
4520 : {
4521 0 : nsCOMPtr<nsIDOMNode> tmp;
4522 0 : res = child->GetNextSibling(getter_AddRefs(tmp));
4523 0 : NS_ENSURE_SUCCESS(res, res);
4524 0 : NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE);
4525 0 : child = tmp;
4526 : }
4527 :
4528 0 : *aOutFirstChild = child;
4529 0 : return res;
4530 : }
4531 :
4532 :
4533 : nsresult
4534 0 : nsHTMLEditor::GetLastEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutLastChild)
4535 : {
4536 : // check parms
4537 0 : NS_ENSURE_TRUE(aOutLastChild && aNode, NS_ERROR_NULL_POINTER);
4538 :
4539 : // init out parms
4540 0 : *aOutLastChild = aNode;
4541 :
4542 : // find last editable child
4543 0 : nsCOMPtr<nsIDOMNode> child;
4544 0 : nsresult res = aNode->GetLastChild(getter_AddRefs(child));
4545 0 : NS_ENSURE_SUCCESS(res, res);
4546 :
4547 0 : while (child && !IsEditable(child))
4548 : {
4549 0 : nsCOMPtr<nsIDOMNode> tmp;
4550 0 : res = child->GetPreviousSibling(getter_AddRefs(tmp));
4551 0 : NS_ENSURE_SUCCESS(res, res);
4552 0 : NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE);
4553 0 : child = tmp;
4554 : }
4555 :
4556 0 : *aOutLastChild = child;
4557 0 : return res;
4558 : }
4559 :
4560 : nsresult
4561 0 : nsHTMLEditor::GetFirstEditableLeaf( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutFirstLeaf)
4562 : {
4563 : // check parms
4564 0 : NS_ENSURE_TRUE(aOutFirstLeaf && aNode, NS_ERROR_NULL_POINTER);
4565 :
4566 : // init out parms
4567 0 : *aOutFirstLeaf = aNode;
4568 :
4569 : // find leftmost leaf
4570 0 : nsCOMPtr<nsIDOMNode> child;
4571 0 : nsresult res = NS_OK;
4572 0 : child = GetLeftmostChild(aNode);
4573 0 : while (child && (!IsEditable(child) || !nsEditorUtils::IsLeafNode(child)))
4574 : {
4575 0 : nsCOMPtr<nsIDOMNode> tmp;
4576 0 : res = GetNextHTMLNode(child, address_of(tmp));
4577 0 : NS_ENSURE_SUCCESS(res, res);
4578 0 : NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE);
4579 :
4580 : // only accept nodes that are descendants of aNode
4581 0 : if (nsEditorUtils::IsDescendantOf(tmp, aNode))
4582 0 : child = tmp;
4583 : else
4584 : {
4585 0 : child = nsnull; // this will abort the loop
4586 : }
4587 : }
4588 :
4589 0 : *aOutFirstLeaf = child;
4590 0 : return res;
4591 : }
4592 :
4593 :
4594 : nsresult
4595 0 : nsHTMLEditor::GetLastEditableLeaf(nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutLastLeaf)
4596 : {
4597 : // check parms
4598 0 : NS_ENSURE_TRUE(aOutLastLeaf && aNode, NS_ERROR_NULL_POINTER);
4599 :
4600 : // init out parms
4601 0 : *aOutLastLeaf = nsnull;
4602 :
4603 : // find rightmost leaf
4604 0 : nsCOMPtr<nsIDOMNode> child = GetRightmostChild(aNode, false);
4605 0 : nsresult res = NS_OK;
4606 0 : while (child && (!IsEditable(child) || !nsEditorUtils::IsLeafNode(child)))
4607 : {
4608 0 : nsCOMPtr<nsIDOMNode> tmp;
4609 0 : res = GetPriorHTMLNode(child, address_of(tmp));
4610 0 : NS_ENSURE_SUCCESS(res, res);
4611 0 : NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE);
4612 :
4613 : // only accept nodes that are descendants of aNode
4614 0 : if (nsEditorUtils::IsDescendantOf(tmp, aNode))
4615 0 : child = tmp;
4616 : else
4617 : {
4618 0 : child = nsnull;
4619 : }
4620 : }
4621 :
4622 0 : *aOutLastLeaf = child;
4623 0 : return res;
4624 : }
4625 :
4626 : bool
4627 0 : nsHTMLEditor::IsTextInDirtyFrameVisible(nsIContent *aNode)
4628 : {
4629 : bool isEmptyTextNode;
4630 0 : nsresult rv = IsVisTextNode(aNode, &isEmptyTextNode, false);
4631 0 : if (NS_FAILED(rv)) {
4632 : // We are following the historical decision:
4633 : // if we don't know, we say it's visible...
4634 0 : return true;
4635 : }
4636 :
4637 0 : return !isEmptyTextNode;
4638 : }
4639 :
4640 :
4641 : ///////////////////////////////////////////////////////////////////////////
4642 : // IsVisTextNode: figure out if textnode aTextNode has any visible content.
4643 : //
4644 : nsresult
4645 0 : nsHTMLEditor::IsVisTextNode(nsIContent* aNode,
4646 : bool* outIsEmptyNode,
4647 : bool aSafeToAskFrames)
4648 : {
4649 0 : NS_ENSURE_TRUE(aNode && outIsEmptyNode, NS_ERROR_NULL_POINTER);
4650 0 : *outIsEmptyNode = true;
4651 :
4652 : // callers job to only call us with text nodes
4653 0 : if (!aNode->IsNodeOfType(nsINode::eTEXT)) {
4654 0 : return NS_ERROR_NULL_POINTER;
4655 : }
4656 :
4657 0 : PRUint32 length = aNode->TextLength();
4658 0 : if (aSafeToAskFrames)
4659 : {
4660 0 : nsCOMPtr<nsISelectionController> selCon;
4661 0 : nsresult res = GetSelectionController(getter_AddRefs(selCon));
4662 0 : NS_ENSURE_SUCCESS(res, res);
4663 0 : NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE);
4664 0 : bool isVisible = false;
4665 : // ask the selection controller for information about whether any
4666 : // of the data in the node is really rendered. This is really
4667 : // something that frames know about, but we aren't supposed to talk to frames.
4668 : // So we put a call in the selection controller interface, since it's already
4669 : // in bed with frames anyway. (this is a fix for bug 22227, and a
4670 : // partial fix for bug 46209)
4671 0 : res = selCon->CheckVisibilityContent(aNode, 0, length, &isVisible);
4672 0 : NS_ENSURE_SUCCESS(res, res);
4673 0 : if (isVisible)
4674 : {
4675 0 : *outIsEmptyNode = false;
4676 : }
4677 : }
4678 0 : else if (length)
4679 : {
4680 0 : if (aNode->TextIsOnlyWhitespace())
4681 : {
4682 0 : nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode);
4683 0 : nsWSRunObject wsRunObj(this, node, 0);
4684 0 : nsCOMPtr<nsIDOMNode> visNode;
4685 0 : PRInt32 outVisOffset=0;
4686 0 : PRInt16 visType=0;
4687 : nsresult res = wsRunObj.NextVisibleNode(node, 0, address_of(visNode),
4688 0 : &outVisOffset, &visType);
4689 0 : NS_ENSURE_SUCCESS(res, res);
4690 0 : if ( (visType == nsWSRunObject::eNormalWS) ||
4691 : (visType == nsWSRunObject::eText) )
4692 : {
4693 0 : *outIsEmptyNode = (node != visNode);
4694 : }
4695 : }
4696 : else
4697 : {
4698 0 : *outIsEmptyNode = false;
4699 : }
4700 : }
4701 0 : return NS_OK;
4702 : }
4703 :
4704 :
4705 : ///////////////////////////////////////////////////////////////////////////
4706 : // IsEmptyNode: figure out if aNode is an empty node.
4707 : // A block can have children and still be considered empty,
4708 : // if the children are empty or non-editable.
4709 : //
4710 : nsresult
4711 0 : nsHTMLEditor::IsEmptyNode( nsIDOMNode *aNode,
4712 : bool *outIsEmptyNode,
4713 : bool aSingleBRDoesntCount,
4714 : bool aListOrCellNotEmpty,
4715 : bool aSafeToAskFrames)
4716 : {
4717 0 : nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
4718 : return IsEmptyNode(node, outIsEmptyNode, aSingleBRDoesntCount,
4719 0 : aListOrCellNotEmpty, aSafeToAskFrames);
4720 : }
4721 :
4722 : nsresult
4723 0 : nsHTMLEditor::IsEmptyNode(nsINode* aNode,
4724 : bool* outIsEmptyNode,
4725 : bool aSingleBRDoesntCount,
4726 : bool aListOrCellNotEmpty,
4727 : bool aSafeToAskFrames)
4728 : {
4729 0 : NS_ENSURE_TRUE(aNode && outIsEmptyNode, NS_ERROR_NULL_POINTER);
4730 0 : *outIsEmptyNode = true;
4731 0 : bool seenBR = false;
4732 : return IsEmptyNodeImpl(aNode, outIsEmptyNode, aSingleBRDoesntCount,
4733 0 : aListOrCellNotEmpty, aSafeToAskFrames, &seenBR);
4734 : }
4735 :
4736 : ///////////////////////////////////////////////////////////////////////////
4737 : // IsEmptyNodeImpl: workhorse for IsEmptyNode.
4738 : //
4739 : nsresult
4740 0 : nsHTMLEditor::IsEmptyNodeImpl(nsINode* aNode,
4741 : bool *outIsEmptyNode,
4742 : bool aSingleBRDoesntCount,
4743 : bool aListOrCellNotEmpty,
4744 : bool aSafeToAskFrames,
4745 : bool *aSeenBR)
4746 : {
4747 0 : NS_ENSURE_TRUE(aNode && outIsEmptyNode && aSeenBR, NS_ERROR_NULL_POINTER);
4748 :
4749 0 : if (aNode->NodeType() == nsIDOMNode::TEXT_NODE) {
4750 0 : return IsVisTextNode(static_cast<nsIContent*>(aNode), outIsEmptyNode, aSafeToAskFrames);
4751 : }
4752 :
4753 : // if it's not a text node (handled above) and it's not a container,
4754 : // then we don't call it empty (it's an <hr>, or <br>, etc).
4755 : // Also, if it's an anchor then don't treat it as empty - even though
4756 : // anchors are containers, named anchors are "empty" but we don't
4757 : // want to treat them as such. Also, don't call ListItems or table
4758 : // cells empty if caller desires. Form Widgets not empty.
4759 0 : if (!IsContainer(aNode) ||
4760 0 : (aNode->IsElement() &&
4761 0 : (nsHTMLEditUtils::IsNamedAnchor(aNode->AsElement()) ||
4762 0 : nsHTMLEditUtils::IsFormWidget(aNode->AsElement()) ||
4763 : (aListOrCellNotEmpty &&
4764 0 : (nsHTMLEditUtils::IsListItem(aNode->AsElement()) ||
4765 0 : nsHTMLEditUtils::IsTableCell(aNode->AsElement())))))) {
4766 0 : *outIsEmptyNode = false;
4767 0 : return NS_OK;
4768 : }
4769 :
4770 : // need this for later
4771 0 : bool isListItemOrCell = aNode->IsElement() &&
4772 0 : (nsHTMLEditUtils::IsListItem(aNode->AsElement()) ||
4773 0 : nsHTMLEditUtils::IsTableCell(aNode->AsElement()));
4774 :
4775 : // loop over children of node. if no children, or all children are either
4776 : // empty text nodes or non-editable, then node qualifies as empty
4777 0 : for (nsCOMPtr<nsIContent> child = aNode->GetFirstChild();
4778 0 : child;
4779 0 : child = child->GetNextSibling()) {
4780 : // Is the child editable and non-empty? if so, return false
4781 0 : if (nsEditor::IsEditable(child)) {
4782 0 : if (child->NodeType() == nsIDOMNode::TEXT_NODE) {
4783 0 : nsresult rv = IsVisTextNode(child, outIsEmptyNode, aSafeToAskFrames);
4784 0 : NS_ENSURE_SUCCESS(rv, rv);
4785 : // break out if we find we aren't emtpy
4786 0 : if (!*outIsEmptyNode) {
4787 0 : return NS_OK;
4788 : }
4789 : } else {
4790 : // An editable, non-text node. We need to check its content.
4791 : // Is it the node we are iterating over?
4792 0 : if (child == aNode) {
4793 0 : break;
4794 : }
4795 :
4796 0 : if (aSingleBRDoesntCount && !*aSeenBR && child->IsHTML(nsGkAtoms::br)) {
4797 : // the first br in a block doesn't count if the caller so indicated
4798 0 : *aSeenBR = true;
4799 : } else {
4800 : // is it an empty node of some sort?
4801 : // note: list items or table cells are not considered empty
4802 : // if they contain other lists or tables
4803 0 : if (child->IsElement()) {
4804 0 : if (isListItemOrCell) {
4805 0 : if (nsHTMLEditUtils::IsList(child->AsElement()) || child->IsHTML(nsGkAtoms::table)) {
4806 : // break out if we find we aren't empty
4807 0 : *outIsEmptyNode = false;
4808 0 : return NS_OK;
4809 : }
4810 0 : } else if (nsHTMLEditUtils::IsFormWidget(child->AsElement())) {
4811 : // is it a form widget?
4812 : // break out if we find we aren't empty
4813 0 : *outIsEmptyNode = false;
4814 0 : return NS_OK;
4815 : }
4816 : }
4817 :
4818 0 : bool isEmptyNode = true;
4819 : nsresult rv = IsEmptyNodeImpl(child, &isEmptyNode,
4820 : aSingleBRDoesntCount,
4821 : aListOrCellNotEmpty, aSafeToAskFrames,
4822 0 : aSeenBR);
4823 0 : NS_ENSURE_SUCCESS(rv, rv);
4824 0 : if (!isEmptyNode) {
4825 : // otherwise it ain't empty
4826 0 : *outIsEmptyNode = false;
4827 0 : return NS_OK;
4828 : }
4829 : }
4830 : }
4831 : }
4832 : }
4833 :
4834 0 : return NS_OK;
4835 : }
4836 :
4837 : // add to aElement the CSS inline styles corresponding to the HTML attribute
4838 : // aAttribute with its value aValue
4839 : nsresult
4840 0 : nsHTMLEditor::SetAttributeOrEquivalent(nsIDOMElement * aElement,
4841 : const nsAString & aAttribute,
4842 : const nsAString & aValue,
4843 : bool aSuppressTransaction)
4844 : {
4845 0 : nsresult res = NS_OK;
4846 0 : if (IsCSSEnabled() && mHTMLCSSUtils) {
4847 : PRInt32 count;
4848 : res = mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(aElement, nsnull, &aAttribute, &aValue, &count,
4849 0 : aSuppressTransaction);
4850 0 : NS_ENSURE_SUCCESS(res, res);
4851 0 : if (count) {
4852 : // we found an equivalence ; let's remove the HTML attribute itself if it is set
4853 0 : nsAutoString existingValue;
4854 0 : bool wasSet = false;
4855 0 : res = GetAttributeValue(aElement, aAttribute, existingValue, &wasSet);
4856 0 : NS_ENSURE_SUCCESS(res, res);
4857 0 : if (wasSet) {
4858 0 : if (aSuppressTransaction)
4859 0 : res = aElement->RemoveAttribute(aAttribute);
4860 : else
4861 0 : res = RemoveAttribute(aElement, aAttribute);
4862 : }
4863 : }
4864 : else {
4865 : // count is an integer that represents the number of CSS declarations applied to the
4866 : // element. If it is zero, we found no equivalence in this implementation for the
4867 : // attribute
4868 0 : if (aAttribute.EqualsLiteral("style")) {
4869 : // if it is the style attribute, just add the new value to the existing style
4870 : // attribute's value
4871 0 : nsAutoString existingValue;
4872 0 : bool wasSet = false;
4873 0 : res = GetAttributeValue(aElement, NS_LITERAL_STRING("style"), existingValue, &wasSet);
4874 0 : NS_ENSURE_SUCCESS(res, res);
4875 0 : existingValue.AppendLiteral(" ");
4876 0 : existingValue.Append(aValue);
4877 0 : if (aSuppressTransaction)
4878 0 : res = aElement->SetAttribute(aAttribute, existingValue);
4879 : else
4880 0 : res = SetAttribute(aElement, aAttribute, existingValue);
4881 : }
4882 : else {
4883 : // we have no CSS equivalence for this attribute and it is not the style
4884 : // attribute; let's set it the good'n'old HTML way
4885 0 : if (aSuppressTransaction)
4886 0 : res = aElement->SetAttribute(aAttribute, aValue);
4887 : else
4888 0 : res = SetAttribute(aElement, aAttribute, aValue);
4889 : }
4890 : }
4891 : }
4892 : else {
4893 : // we are not in an HTML+CSS editor; let's set the attribute the HTML way
4894 0 : if (aSuppressTransaction)
4895 0 : res = aElement->SetAttribute(aAttribute, aValue);
4896 : else
4897 0 : res = SetAttribute(aElement, aAttribute, aValue);
4898 : }
4899 0 : return res;
4900 : }
4901 :
4902 : nsresult
4903 0 : nsHTMLEditor::RemoveAttributeOrEquivalent(nsIDOMElement * aElement,
4904 : const nsAString & aAttribute,
4905 : bool aSuppressTransaction)
4906 : {
4907 0 : nsresult res = NS_OK;
4908 0 : if (IsCSSEnabled() && mHTMLCSSUtils) {
4909 : res = mHTMLCSSUtils->RemoveCSSEquivalentToHTMLStyle(aElement, nsnull, &aAttribute, nsnull,
4910 0 : aSuppressTransaction);
4911 0 : NS_ENSURE_SUCCESS(res, res);
4912 : }
4913 :
4914 0 : nsAutoString existingValue;
4915 0 : bool wasSet = false;
4916 0 : res = GetAttributeValue(aElement, aAttribute, existingValue, &wasSet);
4917 0 : NS_ENSURE_SUCCESS(res, res);
4918 0 : if (wasSet) {
4919 0 : if (aSuppressTransaction)
4920 0 : res = aElement->RemoveAttribute(aAttribute);
4921 : else
4922 0 : res = RemoveAttribute(aElement, aAttribute);
4923 : }
4924 0 : return res;
4925 : }
4926 :
4927 : nsresult
4928 0 : nsHTMLEditor::SetIsCSSEnabled(bool aIsCSSPrefChecked)
4929 : {
4930 0 : if (!mHTMLCSSUtils) {
4931 0 : return NS_ERROR_NOT_INITIALIZED;
4932 : }
4933 :
4934 0 : nsresult rv = mHTMLCSSUtils->SetCSSEnabled(aIsCSSPrefChecked);
4935 0 : NS_ENSURE_SUCCESS(rv, rv);
4936 :
4937 : // Disable the eEditorNoCSSMask flag if we're enabling StyleWithCSS.
4938 0 : PRUint32 flags = mFlags;
4939 0 : if (aIsCSSPrefChecked) {
4940 : // Turn off NoCSS as we're enabling CSS
4941 0 : flags &= ~eEditorNoCSSMask;
4942 : } else {
4943 : // Turn on NoCSS, as we're disabling CSS.
4944 0 : flags |= eEditorNoCSSMask;
4945 : }
4946 :
4947 0 : return SetFlags(flags);
4948 : }
4949 :
4950 : // Set the block background color
4951 : NS_IMETHODIMP
4952 0 : nsHTMLEditor::SetCSSBackgroundColor(const nsAString& aColor)
4953 : {
4954 0 : if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
4955 0 : ForceCompositionEnd();
4956 :
4957 : // Protect the edit rules object from dying
4958 0 : nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
4959 :
4960 : nsresult res;
4961 0 : nsCOMPtr<nsISelection>selection;
4962 0 : res = GetSelection(getter_AddRefs(selection));
4963 0 : NS_ENSURE_SUCCESS(res, res);
4964 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
4965 0 : nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
4966 :
4967 : bool isCollapsed;
4968 0 : selection->GetIsCollapsed(&isCollapsed);
4969 :
4970 0 : nsAutoEditBatch batchIt(this);
4971 0 : nsAutoRules beginRulesSniffing(this, kOpInsertElement, nsIEditor::eNext);
4972 0 : nsAutoSelectionReset selectionResetter(selection, this);
4973 0 : nsAutoTxnsConserveSelection dontSpazMySelection(this);
4974 :
4975 : bool cancel, handled;
4976 0 : nsTextRulesInfo ruleInfo(nsTextEditRules::kSetTextProperty);
4977 0 : res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
4978 0 : NS_ENSURE_SUCCESS(res, res);
4979 0 : if (!cancel && !handled)
4980 : {
4981 : // get selection range enumerator
4982 0 : nsCOMPtr<nsIEnumerator> enumerator;
4983 0 : res = selPriv->GetEnumerator(getter_AddRefs(enumerator));
4984 0 : NS_ENSURE_SUCCESS(res, res);
4985 0 : NS_ENSURE_TRUE(enumerator, NS_ERROR_FAILURE);
4986 :
4987 : // loop thru the ranges in the selection
4988 0 : enumerator->First();
4989 0 : nsCOMPtr<nsISupports> currentItem;
4990 0 : nsAutoString bgcolor; bgcolor.AssignLiteral("bgcolor");
4991 0 : nsCOMPtr<nsIDOMNode> cachedBlockParent = nsnull;
4992 0 : while ((NS_ENUMERATOR_FALSE == enumerator->IsDone()))
4993 : {
4994 0 : res = enumerator->CurrentItem(getter_AddRefs(currentItem));
4995 0 : NS_ENSURE_SUCCESS(res, res);
4996 0 : NS_ENSURE_TRUE(currentItem, NS_ERROR_FAILURE);
4997 :
4998 0 : nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
4999 :
5000 : // check for easy case: both range endpoints in same text node
5001 0 : nsCOMPtr<nsIDOMNode> startNode, endNode;
5002 : PRInt32 startOffset, endOffset;
5003 0 : res = range->GetStartContainer(getter_AddRefs(startNode));
5004 0 : NS_ENSURE_SUCCESS(res, res);
5005 0 : res = range->GetEndContainer(getter_AddRefs(endNode));
5006 0 : NS_ENSURE_SUCCESS(res, res);
5007 0 : res = range->GetStartOffset(&startOffset);
5008 0 : NS_ENSURE_SUCCESS(res, res);
5009 0 : res = range->GetEndOffset(&endOffset);
5010 0 : NS_ENSURE_SUCCESS(res, res);
5011 0 : if ((startNode == endNode) && IsTextNode(startNode))
5012 : {
5013 : // let's find the block container of the text node
5014 0 : nsCOMPtr<nsIDOMNode> blockParent;
5015 0 : blockParent = GetBlockNodeParent(startNode);
5016 : // and apply the background color to that block container
5017 0 : if (cachedBlockParent != blockParent)
5018 : {
5019 0 : cachedBlockParent = blockParent;
5020 0 : nsCOMPtr<nsIDOMElement> element = do_QueryInterface(blockParent);
5021 : PRInt32 count;
5022 0 : res = mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(element, nsnull, &bgcolor, &aColor, &count, false);
5023 0 : NS_ENSURE_SUCCESS(res, res);
5024 : }
5025 : }
5026 0 : else if ((startNode == endNode) && nsTextEditUtils::IsBody(startNode) && isCollapsed)
5027 : {
5028 : // we have no block in the document, let's apply the background to the body
5029 0 : nsCOMPtr<nsIDOMElement> element = do_QueryInterface(startNode);
5030 : PRInt32 count;
5031 0 : res = mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(element, nsnull, &bgcolor, &aColor, &count, false);
5032 0 : NS_ENSURE_SUCCESS(res, res);
5033 : }
5034 0 : else if ((startNode == endNode) && (((endOffset-startOffset) == 1) || (!startOffset && !endOffset)))
5035 : {
5036 : // a unique node is selected, let's also apply the background color
5037 : // to the containing block, possibly the node itself
5038 0 : nsCOMPtr<nsIDOMNode> selectedNode = GetChildAt(startNode, startOffset);
5039 0 : bool isBlock =false;
5040 0 : res = NodeIsBlockStatic(selectedNode, &isBlock);
5041 0 : NS_ENSURE_SUCCESS(res, res);
5042 0 : nsCOMPtr<nsIDOMNode> blockParent = selectedNode;
5043 0 : if (!isBlock) {
5044 0 : blockParent = GetBlockNodeParent(selectedNode);
5045 : }
5046 0 : if (cachedBlockParent != blockParent)
5047 : {
5048 0 : cachedBlockParent = blockParent;
5049 0 : nsCOMPtr<nsIDOMElement> element = do_QueryInterface(blockParent);
5050 : PRInt32 count;
5051 0 : res = mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(element, nsnull, &bgcolor, &aColor, &count, false);
5052 0 : NS_ENSURE_SUCCESS(res, res);
5053 : }
5054 : }
5055 : else
5056 : {
5057 : // not the easy case. range not contained in single text node.
5058 : // there are up to three phases here. There are all the nodes
5059 : // reported by the subtree iterator to be processed. And there
5060 : // are potentially a starting textnode and an ending textnode
5061 : // which are only partially contained by the range.
5062 :
5063 : // lets handle the nodes reported by the iterator. These nodes
5064 : // are entirely contained in the selection range. We build up
5065 : // a list of them (since doing operations on the document during
5066 : // iteration would perturb the iterator).
5067 :
5068 : nsCOMPtr<nsIContentIterator> iter =
5069 0 : do_CreateInstance("@mozilla.org/content/subtree-content-iterator;1", &res);
5070 0 : NS_ENSURE_SUCCESS(res, res);
5071 0 : NS_ENSURE_TRUE(iter, NS_ERROR_FAILURE);
5072 :
5073 0 : nsCOMArray<nsIDOMNode> arrayOfNodes;
5074 0 : nsCOMPtr<nsIDOMNode> node;
5075 :
5076 : // iterate range and build up array
5077 0 : res = iter->Init(range);
5078 : // init returns an error if no nodes in range.
5079 : // this can easily happen with the subtree
5080 : // iterator if the selection doesn't contain
5081 : // any *whole* nodes.
5082 0 : if (NS_SUCCEEDED(res))
5083 : {
5084 0 : while (!iter->IsDone())
5085 : {
5086 0 : node = do_QueryInterface(iter->GetCurrentNode());
5087 0 : NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
5088 :
5089 0 : if (IsEditable(node))
5090 : {
5091 0 : arrayOfNodes.AppendObject(node);
5092 : }
5093 :
5094 0 : iter->Next();
5095 : }
5096 : }
5097 : // first check the start parent of the range to see if it needs to
5098 : // be separately handled (it does if it's a text node, due to how the
5099 : // subtree iterator works - it will not have reported it).
5100 0 : if (IsTextNode(startNode) && IsEditable(startNode))
5101 : {
5102 0 : nsCOMPtr<nsIDOMNode> blockParent;
5103 0 : blockParent = GetBlockNodeParent(startNode);
5104 0 : if (cachedBlockParent != blockParent)
5105 : {
5106 0 : cachedBlockParent = blockParent;
5107 0 : nsCOMPtr<nsIDOMElement> element = do_QueryInterface(blockParent);
5108 : PRInt32 count;
5109 0 : res = mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(element, nsnull, &bgcolor, &aColor, &count, false);
5110 0 : NS_ENSURE_SUCCESS(res, res);
5111 : }
5112 : }
5113 :
5114 : // then loop through the list, set the property on each node
5115 0 : PRInt32 listCount = arrayOfNodes.Count();
5116 : PRInt32 j;
5117 0 : for (j = 0; j < listCount; j++)
5118 : {
5119 0 : node = arrayOfNodes[j];
5120 : // do we have a block here ?
5121 0 : bool isBlock =false;
5122 0 : res = NodeIsBlockStatic(node, &isBlock);
5123 0 : NS_ENSURE_SUCCESS(res, res);
5124 0 : nsCOMPtr<nsIDOMNode> blockParent = node;
5125 0 : if (!isBlock) {
5126 : // no we don't, let's find the block ancestor
5127 0 : blockParent = GetBlockNodeParent(node);
5128 : }
5129 0 : if (cachedBlockParent != blockParent)
5130 : {
5131 0 : cachedBlockParent = blockParent;
5132 0 : nsCOMPtr<nsIDOMElement> element = do_QueryInterface(blockParent);
5133 : PRInt32 count;
5134 : // and set the property on it
5135 0 : res = mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(element, nsnull, &bgcolor, &aColor, &count, false);
5136 0 : NS_ENSURE_SUCCESS(res, res);
5137 : }
5138 : }
5139 0 : arrayOfNodes.Clear();
5140 :
5141 : // last check the end parent of the range to see if it needs to
5142 : // be separately handled (it does if it's a text node, due to how the
5143 : // subtree iterator works - it will not have reported it).
5144 0 : if (IsTextNode(endNode) && IsEditable(endNode))
5145 : {
5146 0 : nsCOMPtr<nsIDOMNode> blockParent;
5147 0 : blockParent = GetBlockNodeParent(endNode);
5148 0 : if (cachedBlockParent != blockParent)
5149 : {
5150 0 : cachedBlockParent = blockParent;
5151 0 : nsCOMPtr<nsIDOMElement> element = do_QueryInterface(blockParent);
5152 : PRInt32 count;
5153 0 : res = mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(element, nsnull, &bgcolor, &aColor, &count, false);
5154 0 : NS_ENSURE_SUCCESS(res, res);
5155 : }
5156 : }
5157 : }
5158 0 : enumerator->Next();
5159 : }
5160 : }
5161 0 : if (!cancel)
5162 : {
5163 : // post-process
5164 0 : res = mRules->DidDoAction(selection, &ruleInfo, res);
5165 : }
5166 0 : return res;
5167 : }
5168 :
5169 : NS_IMETHODIMP
5170 0 : nsHTMLEditor::SetBackgroundColor(const nsAString& aColor)
5171 : {
5172 : nsresult res;
5173 0 : if (IsCSSEnabled()) {
5174 : // if we are in CSS mode, we have to apply the background color to the
5175 : // containing block (or the body if we have no block-level element in
5176 : // the document)
5177 0 : res = SetCSSBackgroundColor(aColor);
5178 : }
5179 : else {
5180 : // but in HTML mode, we can only set the document's background color
5181 0 : res = SetHTMLBackgroundColor(aColor);
5182 : }
5183 0 : return res;
5184 : }
5185 :
5186 : ///////////////////////////////////////////////////////////////////////////
5187 : // NodesSameType: do these nodes have the same tag?
5188 : //
5189 : bool
5190 0 : nsHTMLEditor::NodesSameType(nsIDOMNode *aNode1, nsIDOMNode *aNode2)
5191 : {
5192 0 : if (!aNode1 || !aNode2)
5193 : {
5194 0 : NS_NOTREACHED("null node passed to nsEditor::NodesSameType()");
5195 0 : return false;
5196 : }
5197 :
5198 0 : nsIAtom *tag1 = GetTag(aNode1);
5199 :
5200 0 : if (tag1 == GetTag(aNode2)) {
5201 0 : if (IsCSSEnabled() && tag1 == nsEditProperty::span) {
5202 0 : if (mHTMLCSSUtils->ElementsSameStyle(aNode1, aNode2)) {
5203 0 : return true;
5204 : }
5205 : }
5206 : else {
5207 0 : return true;
5208 : }
5209 : }
5210 0 : return false;
5211 : }
5212 :
5213 : NS_IMETHODIMP
5214 0 : nsHTMLEditor::CopyLastEditableChildStyles(nsIDOMNode * aPreviousBlock, nsIDOMNode * aNewBlock,
5215 : nsIDOMNode **aOutBrNode)
5216 : {
5217 0 : *aOutBrNode = nsnull;
5218 0 : nsCOMPtr<nsIDOMNode> child, tmp;
5219 : nsresult res;
5220 : // first, clear out aNewBlock. Contract is that we want only the styles from previousBlock.
5221 0 : res = aNewBlock->GetFirstChild(getter_AddRefs(child));
5222 0 : while (NS_SUCCEEDED(res) && child)
5223 : {
5224 0 : res = DeleteNode(child);
5225 0 : NS_ENSURE_SUCCESS(res, res);
5226 0 : res = aNewBlock->GetFirstChild(getter_AddRefs(child));
5227 : }
5228 : // now find and clone the styles
5229 0 : child = aPreviousBlock;
5230 0 : tmp = aPreviousBlock;
5231 0 : while (tmp) {
5232 0 : child = tmp;
5233 0 : res = GetLastEditableChild(child, address_of(tmp));
5234 0 : NS_ENSURE_SUCCESS(res, res);
5235 : }
5236 0 : while (child && nsTextEditUtils::IsBreak(child)) {
5237 0 : nsCOMPtr<nsIDOMNode> priorNode;
5238 0 : res = GetPriorHTMLNode(child, address_of(priorNode));
5239 0 : NS_ENSURE_SUCCESS(res, res);
5240 0 : child = priorNode;
5241 : }
5242 0 : nsCOMPtr<nsIDOMNode> newStyles = nsnull, deepestStyle = nsnull;
5243 0 : while (child && (child != aPreviousBlock)) {
5244 0 : if (nsHTMLEditUtils::IsInlineStyle(child) ||
5245 0 : nsEditor::NodeIsType(child, nsEditProperty::span)) {
5246 0 : nsAutoString domTagName;
5247 0 : child->GetNodeName(domTagName);
5248 0 : ToLowerCase(domTagName);
5249 0 : if (newStyles) {
5250 0 : nsCOMPtr<nsIDOMNode> newContainer;
5251 0 : res = InsertContainerAbove(newStyles, address_of(newContainer), domTagName);
5252 0 : NS_ENSURE_SUCCESS(res, res);
5253 0 : newStyles = newContainer;
5254 : }
5255 : else {
5256 0 : res = CreateNode(domTagName, aNewBlock, 0, getter_AddRefs(newStyles));
5257 0 : NS_ENSURE_SUCCESS(res, res);
5258 0 : deepestStyle = newStyles;
5259 : }
5260 0 : res = CloneAttributes(newStyles, child);
5261 0 : NS_ENSURE_SUCCESS(res, res);
5262 : }
5263 0 : nsCOMPtr<nsIDOMNode> tmp;
5264 0 : res = child->GetParentNode(getter_AddRefs(tmp));
5265 0 : NS_ENSURE_SUCCESS(res, res);
5266 0 : child = tmp;
5267 : }
5268 0 : if (deepestStyle) {
5269 0 : nsCOMPtr<nsIDOMNode> outBRNode;
5270 0 : res = CreateBR(deepestStyle, 0, address_of(outBRNode));
5271 0 : NS_ENSURE_SUCCESS(res, res);
5272 : // Getters must addref
5273 0 : outBRNode.forget(aOutBrNode);
5274 : }
5275 0 : return NS_OK;
5276 : }
5277 :
5278 : nsresult
5279 0 : nsHTMLEditor::GetElementOrigin(nsIDOMElement * aElement, PRInt32 & aX, PRInt32 & aY)
5280 : {
5281 0 : aX = 0;
5282 0 : aY = 0;
5283 :
5284 0 : NS_ENSURE_TRUE(mDocWeak, NS_ERROR_NOT_INITIALIZED);
5285 0 : nsCOMPtr<nsIPresShell> ps = GetPresShell();
5286 0 : NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
5287 :
5288 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
5289 0 : nsIFrame *frame = content->GetPrimaryFrame();
5290 0 : NS_ENSURE_TRUE(frame, NS_OK);
5291 :
5292 0 : nsIFrame *container = ps->GetAbsoluteContainingBlock(frame);
5293 0 : NS_ENSURE_TRUE(container, NS_OK);
5294 0 : nsPoint off = frame->GetOffsetTo(container);
5295 0 : aX = nsPresContext::AppUnitsToIntCSSPixels(off.x);
5296 0 : aY = nsPresContext::AppUnitsToIntCSSPixels(off.y);
5297 :
5298 0 : return NS_OK;
5299 : }
5300 :
5301 : nsresult
5302 0 : nsHTMLEditor::EndUpdateViewBatch()
5303 : {
5304 0 : nsresult res = nsEditor::EndUpdateViewBatch();
5305 0 : NS_ENSURE_SUCCESS(res, res);
5306 :
5307 : // We may need to show resizing handles or update existing ones after
5308 : // all transactions are done. This way of doing is preferred to DOM
5309 : // mutation events listeners because all the changes the user can apply
5310 : // to a document may result in multiple events, some of them quite hard
5311 : // to listen too (in particular when an ancestor of the selection is
5312 : // changed but the selection itself is not changed).
5313 0 : if (mUpdateCount == 0) {
5314 0 : nsCOMPtr<nsISelection> selection;
5315 0 : res = GetSelection(getter_AddRefs(selection));
5316 0 : NS_ENSURE_SUCCESS(res, res);
5317 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NOT_INITIALIZED);
5318 0 : res = CheckSelectionStateForAnonymousButtons(selection);
5319 : }
5320 0 : return res;
5321 : }
5322 :
5323 : NS_IMETHODIMP
5324 0 : nsHTMLEditor::GetSelectionContainer(nsIDOMElement ** aReturn)
5325 : {
5326 0 : nsCOMPtr<nsISelection>selection;
5327 0 : nsresult res = GetSelection(getter_AddRefs(selection));
5328 : // if we don't get the selection, just skip this
5329 0 : if (NS_FAILED(res) || !selection) return res;
5330 :
5331 : bool bCollapsed;
5332 0 : res = selection->GetIsCollapsed(&bCollapsed);
5333 0 : NS_ENSURE_SUCCESS(res, res);
5334 :
5335 0 : nsCOMPtr<nsIDOMNode> focusNode;
5336 :
5337 0 : if (bCollapsed) {
5338 0 : res = selection->GetFocusNode(getter_AddRefs(focusNode));
5339 0 : NS_ENSURE_SUCCESS(res, res);
5340 : }
5341 : else {
5342 :
5343 : PRInt32 rangeCount;
5344 0 : res = selection->GetRangeCount(&rangeCount);
5345 0 : NS_ENSURE_SUCCESS(res, res);
5346 :
5347 0 : if (rangeCount == 1) {
5348 :
5349 0 : nsCOMPtr<nsIDOMRange> range;
5350 0 : res = selection->GetRangeAt(0, getter_AddRefs(range));
5351 0 : NS_ENSURE_SUCCESS(res, res);
5352 0 : NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER);
5353 :
5354 0 : nsCOMPtr<nsIDOMNode> startContainer, endContainer;
5355 0 : res = range->GetStartContainer(getter_AddRefs(startContainer));
5356 0 : NS_ENSURE_SUCCESS(res, res);
5357 0 : res = range->GetEndContainer(getter_AddRefs(endContainer));
5358 0 : NS_ENSURE_SUCCESS(res, res);
5359 : PRInt32 startOffset, endOffset;
5360 0 : res = range->GetStartOffset(&startOffset);
5361 0 : NS_ENSURE_SUCCESS(res, res);
5362 0 : res = range->GetEndOffset(&endOffset);
5363 0 : NS_ENSURE_SUCCESS(res, res);
5364 :
5365 0 : nsCOMPtr<nsIDOMElement> focusElement;
5366 0 : if (startContainer == endContainer && startOffset + 1 == endOffset) {
5367 0 : res = GetSelectedElement(EmptyString(), getter_AddRefs(focusElement));
5368 0 : NS_ENSURE_SUCCESS(res, res);
5369 0 : if (focusElement)
5370 0 : focusNode = do_QueryInterface(focusElement);
5371 : }
5372 0 : if (!focusNode) {
5373 0 : res = range->GetCommonAncestorContainer(getter_AddRefs(focusNode));
5374 0 : NS_ENSURE_SUCCESS(res, res);
5375 : }
5376 : }
5377 : else {
5378 : PRInt32 i;
5379 0 : nsCOMPtr<nsIDOMRange> range;
5380 0 : for (i = 0; i < rangeCount; i++)
5381 : {
5382 0 : res = selection->GetRangeAt(i, getter_AddRefs(range));
5383 0 : NS_ENSURE_SUCCESS(res, res);
5384 0 : nsCOMPtr<nsIDOMNode> startContainer;
5385 0 : res = range->GetStartContainer(getter_AddRefs(startContainer));
5386 0 : if (NS_FAILED(res)) continue;
5387 0 : if (!focusNode)
5388 0 : focusNode = startContainer;
5389 0 : else if (focusNode != startContainer) {
5390 0 : res = startContainer->GetParentNode(getter_AddRefs(focusNode));
5391 0 : NS_ENSURE_SUCCESS(res, res);
5392 : break;
5393 : }
5394 : }
5395 : }
5396 : }
5397 :
5398 0 : if (focusNode) {
5399 : PRUint16 nodeType;
5400 0 : focusNode->GetNodeType(&nodeType);
5401 0 : if (nsIDOMNode::TEXT_NODE == nodeType) {
5402 0 : nsCOMPtr<nsIDOMNode> parent;
5403 0 : res = focusNode->GetParentNode(getter_AddRefs(parent));
5404 0 : NS_ENSURE_SUCCESS(res, res);
5405 0 : focusNode = parent;
5406 : }
5407 : }
5408 :
5409 0 : nsCOMPtr<nsIDOMElement> focusElement = do_QueryInterface(focusNode);
5410 0 : focusElement.forget(aReturn);
5411 0 : return NS_OK;
5412 : }
5413 :
5414 : NS_IMETHODIMP
5415 0 : nsHTMLEditor::IsAnonymousElement(nsIDOMElement * aElement, bool * aReturn)
5416 : {
5417 0 : NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
5418 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
5419 0 : *aReturn = content->IsRootOfNativeAnonymousSubtree();
5420 0 : return NS_OK;
5421 : }
5422 :
5423 : nsresult
5424 0 : nsHTMLEditor::SetReturnInParagraphCreatesNewParagraph(bool aCreatesNewParagraph)
5425 : {
5426 0 : mCRInParagraphCreatesParagraph = aCreatesNewParagraph;
5427 0 : return NS_OK;
5428 : }
5429 :
5430 : nsresult
5431 0 : nsHTMLEditor::GetReturnInParagraphCreatesNewParagraph(bool *aCreatesNewParagraph)
5432 : {
5433 0 : *aCreatesNewParagraph = mCRInParagraphCreatesParagraph;
5434 0 : return NS_OK;
5435 : }
5436 :
5437 : already_AddRefed<nsIContent>
5438 0 : nsHTMLEditor::GetFocusedContent()
5439 : {
5440 0 : NS_ENSURE_TRUE(mDocWeak, nsnull);
5441 :
5442 0 : nsFocusManager* fm = nsFocusManager::GetFocusManager();
5443 0 : NS_ENSURE_TRUE(fm, nsnull);
5444 :
5445 0 : nsCOMPtr<nsIContent> focusedContent = fm->GetFocusedContent();
5446 :
5447 0 : nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
5448 0 : bool inDesignMode = doc->HasFlag(NODE_IS_EDITABLE);
5449 0 : if (!focusedContent) {
5450 : // in designMode, nobody gets focus in most cases.
5451 0 : if (inDesignMode && OurWindowHasFocus()) {
5452 0 : nsCOMPtr<nsIContent> docRoot = doc->GetRootElement();
5453 0 : return docRoot.forget();
5454 : }
5455 0 : return nsnull;
5456 : }
5457 :
5458 0 : if (inDesignMode) {
5459 0 : return OurWindowHasFocus() &&
5460 0 : nsContentUtils::ContentIsDescendantOf(focusedContent, doc) ?
5461 0 : focusedContent.forget() : nsnull;
5462 : }
5463 :
5464 : // We're HTML editor for contenteditable
5465 :
5466 : // If the focused content isn't editable, or it has independent selection,
5467 : // we don't have focus.
5468 0 : if (!focusedContent->HasFlag(NODE_IS_EDITABLE) ||
5469 0 : focusedContent->HasIndependentSelection()) {
5470 0 : return nsnull;
5471 : }
5472 : // If our window is focused, we're focused.
5473 0 : return OurWindowHasFocus() ? focusedContent.forget() : nsnull;
5474 : }
5475 :
5476 : bool
5477 0 : nsHTMLEditor::IsActiveInDOMWindow()
5478 : {
5479 0 : NS_ENSURE_TRUE(mDocWeak, false);
5480 :
5481 0 : nsFocusManager* fm = nsFocusManager::GetFocusManager();
5482 0 : NS_ENSURE_TRUE(fm, false);
5483 :
5484 0 : nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
5485 0 : bool inDesignMode = doc->HasFlag(NODE_IS_EDITABLE);
5486 :
5487 : // If we're in designMode, we're always active in the DOM window.
5488 0 : if (inDesignMode) {
5489 0 : return true;
5490 : }
5491 :
5492 0 : nsPIDOMWindow* ourWindow = doc->GetWindow();
5493 0 : nsCOMPtr<nsPIDOMWindow> win;
5494 : nsIContent* content =
5495 : nsFocusManager::GetFocusedDescendant(ourWindow, false,
5496 0 : getter_AddRefs(win));
5497 0 : if (!content) {
5498 0 : return false;
5499 : }
5500 :
5501 : // We're HTML editor for contenteditable
5502 :
5503 : // If the active content isn't editable, or it has independent selection,
5504 : // we're not active).
5505 0 : if (!content->HasFlag(NODE_IS_EDITABLE) ||
5506 0 : content->HasIndependentSelection()) {
5507 0 : return false;
5508 : }
5509 0 : return true;
5510 : }
5511 :
5512 : nsIContent*
5513 0 : nsHTMLEditor::GetActiveEditingHost()
5514 : {
5515 0 : NS_ENSURE_TRUE(mDocWeak, nsnull);
5516 :
5517 0 : nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
5518 0 : NS_ENSURE_TRUE(doc, nsnull);
5519 0 : if (doc->HasFlag(NODE_IS_EDITABLE)) {
5520 0 : return doc->GetBodyElement();
5521 : }
5522 :
5523 : // We're HTML editor for contenteditable
5524 0 : nsCOMPtr<nsISelection> selection;
5525 0 : nsresult rv = GetSelection(getter_AddRefs(selection));
5526 0 : NS_ENSURE_SUCCESS(rv, nsnull);
5527 0 : nsCOMPtr<nsIDOMNode> focusNode;
5528 0 : rv = selection->GetFocusNode(getter_AddRefs(focusNode));
5529 0 : NS_ENSURE_SUCCESS(rv, nsnull);
5530 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(focusNode);
5531 0 : if (!content) {
5532 0 : return nsnull;
5533 : }
5534 :
5535 : // If the active content isn't editable, or it has independent selection,
5536 : // we're not active.
5537 0 : if (!content->HasFlag(NODE_IS_EDITABLE) ||
5538 0 : content->HasIndependentSelection()) {
5539 0 : return nsnull;
5540 : }
5541 0 : return content->GetEditingHost();
5542 : }
5543 :
5544 : already_AddRefed<nsIDOMEventTarget>
5545 0 : nsHTMLEditor::GetDOMEventTarget()
5546 : {
5547 : // Don't use getDocument here, because we have no way of knowing
5548 : // whether Init() was ever called. So we need to get the document
5549 : // ourselves, if it exists.
5550 0 : NS_PRECONDITION(mDocWeak, "This editor has not been initialized yet");
5551 0 : nsCOMPtr<nsIDOMEventTarget> target = do_QueryReferent(mDocWeak.get());
5552 0 : return target.forget();
5553 : }
5554 :
5555 : bool
5556 0 : nsHTMLEditor::ShouldReplaceRootElement()
5557 : {
5558 0 : if (!mRootElement) {
5559 : // If we don't know what is our root element, we should find our root.
5560 0 : return true;
5561 : }
5562 :
5563 : // If we temporary set document root element to mRootElement, but there is
5564 : // body element now, we should replace the root element by the body element.
5565 0 : nsCOMPtr<nsIDOMHTMLElement> docBody;
5566 0 : GetBodyElement(getter_AddRefs(docBody));
5567 0 : return !SameCOMIdentity(docBody, mRootElement);
5568 : }
5569 :
5570 : void
5571 0 : nsHTMLEditor::ResetRootElementAndEventTarget()
5572 : {
5573 0 : nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
5574 :
5575 : // Need to remove the event listeners first because BeginningOfDocument
5576 : // could set a new root (and event target is set by InstallEventListeners())
5577 : // and we won't be able to remove them from the old event target then.
5578 0 : RemoveEventListeners();
5579 0 : mRootElement = nsnull;
5580 0 : nsresult rv = InstallEventListeners();
5581 0 : if (NS_FAILED(rv)) {
5582 : return;
5583 : }
5584 :
5585 : // We must have mRootElement now.
5586 0 : nsCOMPtr<nsIDOMElement> root;
5587 0 : rv = GetRootElement(getter_AddRefs(root));
5588 0 : if (NS_FAILED(rv) || !mRootElement) {
5589 : return;
5590 : }
5591 :
5592 0 : rv = BeginningOfDocument();
5593 0 : if (NS_FAILED(rv)) {
5594 : return;
5595 : }
5596 :
5597 : // When this editor has focus, we need to reset the selection limiter to
5598 : // new root. Otherwise, that is going to be done when this gets focus.
5599 0 : nsCOMPtr<nsINode> node = GetFocusedNode();
5600 0 : nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(node);
5601 0 : if (target) {
5602 0 : InitializeSelection(target);
5603 : }
5604 :
5605 0 : SyncRealTimeSpell();
5606 : }
5607 :
5608 : nsresult
5609 0 : nsHTMLEditor::GetBodyElement(nsIDOMHTMLElement** aBody)
5610 : {
5611 0 : NS_PRECONDITION(mDocWeak, "bad state, null mDocWeak");
5612 0 : nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryReferent(mDocWeak);
5613 0 : if (!htmlDoc) {
5614 0 : return NS_ERROR_NOT_INITIALIZED;
5615 : }
5616 0 : return htmlDoc->GetBody(aBody);
5617 : }
5618 :
5619 : already_AddRefed<nsINode>
5620 0 : nsHTMLEditor::GetFocusedNode()
5621 : {
5622 0 : nsCOMPtr<nsIContent> focusedContent = GetFocusedContent();
5623 0 : if (!focusedContent) {
5624 0 : return nsnull;
5625 : }
5626 :
5627 0 : nsIFocusManager* fm = nsFocusManager::GetFocusManager();
5628 0 : NS_ASSERTION(fm, "Focus manager is null");
5629 0 : nsCOMPtr<nsIDOMElement> focusedElement;
5630 0 : fm->GetFocusedElement(getter_AddRefs(focusedElement));
5631 0 : if (focusedElement) {
5632 0 : nsCOMPtr<nsINode> node = do_QueryInterface(focusedElement);
5633 0 : return node.forget();
5634 : }
5635 :
5636 0 : nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
5637 0 : return doc.forget();
5638 : }
5639 :
5640 : bool
5641 0 : nsHTMLEditor::OurWindowHasFocus()
5642 : {
5643 0 : NS_ENSURE_TRUE(mDocWeak, false);
5644 0 : nsIFocusManager* fm = nsFocusManager::GetFocusManager();
5645 0 : NS_ENSURE_TRUE(fm, false);
5646 0 : nsCOMPtr<nsIDOMWindow> focusedWindow;
5647 0 : fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
5648 0 : if (!focusedWindow) {
5649 0 : return false;
5650 : }
5651 0 : nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
5652 0 : nsCOMPtr<nsIDOMWindow> ourWindow = do_QueryInterface(doc->GetWindow());
5653 0 : return ourWindow == focusedWindow;
5654 : }
5655 :
5656 : bool
5657 0 : nsHTMLEditor::IsAcceptableInputEvent(nsIDOMEvent* aEvent)
5658 : {
5659 0 : if (!nsEditor::IsAcceptableInputEvent(aEvent)) {
5660 0 : return false;
5661 : }
5662 :
5663 0 : NS_ENSURE_TRUE(mDocWeak, false);
5664 :
5665 0 : nsCOMPtr<nsIDOMEventTarget> target;
5666 0 : aEvent->GetTarget(getter_AddRefs(target));
5667 0 : NS_ENSURE_TRUE(target, false);
5668 :
5669 0 : nsCOMPtr<nsIDocument> document = do_QueryReferent(mDocWeak);
5670 0 : if (document->HasFlag(NODE_IS_EDITABLE)) {
5671 : // If this editor is in designMode and the event target is the document,
5672 : // the event is for this editor.
5673 0 : nsCOMPtr<nsIDocument> targetDocument = do_QueryInterface(target);
5674 0 : if (targetDocument) {
5675 0 : return targetDocument == document;
5676 : }
5677 : // Otherwise, check whether the event target is in this document or not.
5678 0 : nsCOMPtr<nsIContent> targetContent = do_QueryInterface(target);
5679 0 : NS_ENSURE_TRUE(targetContent, false);
5680 0 : return document == targetContent->GetCurrentDoc();
5681 : }
5682 :
5683 : // This HTML editor is for contenteditable. We need to check the validity of
5684 : // the target.
5685 0 : nsCOMPtr<nsIContent> targetContent = do_QueryInterface(target);
5686 0 : NS_ENSURE_TRUE(targetContent, false);
5687 :
5688 : // If the event is a mouse event, we need to check if the target content is
5689 : // the focused editing host or its descendant.
5690 0 : nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aEvent);
5691 0 : if (mouseEvent) {
5692 0 : nsIContent* editingHost = GetActiveEditingHost();
5693 : // If there is no active editing host, we cannot handle the mouse event
5694 : // correctly.
5695 0 : if (!editingHost) {
5696 0 : return false;
5697 : }
5698 : // If clicked on non-editable root element but the body element is the
5699 : // active editing host, we should assume that the click event is targetted.
5700 0 : if (targetContent == document->GetRootElement() &&
5701 0 : !targetContent->HasFlag(NODE_IS_EDITABLE) &&
5702 0 : editingHost == document->GetBodyElement()) {
5703 0 : targetContent = editingHost;
5704 : }
5705 : // If the target element is neither the active editing host nor a descendant
5706 : // of it, we may not be able to handle the event.
5707 0 : if (!nsContentUtils::ContentIsDescendantOf(targetContent, editingHost)) {
5708 0 : return false;
5709 : }
5710 : // If the clicked element has an independent selection, we shouldn't
5711 : // handle this click event.
5712 0 : if (targetContent->HasIndependentSelection()) {
5713 0 : return false;
5714 : }
5715 : // If the target content is editable, we should handle this event.
5716 0 : return targetContent->HasFlag(NODE_IS_EDITABLE);
5717 : }
5718 :
5719 : // If the target of the other events which target focused element isn't
5720 : // editable or has an independent selection, this editor shouldn't handle the
5721 : // event.
5722 0 : if (!targetContent->HasFlag(NODE_IS_EDITABLE) ||
5723 0 : targetContent->HasIndependentSelection()) {
5724 0 : return false;
5725 : }
5726 :
5727 : // Finally, check whether we're actually focused or not. When we're not
5728 : // focused, we should ignore the dispatched event by script (or something)
5729 : // because content editable element needs selection in itself for editing.
5730 : // However, when we're not focused, it's not guaranteed.
5731 0 : return IsActiveInDOMWindow();
5732 : }
5733 :
5734 : NS_IMETHODIMP
5735 0 : nsHTMLEditor::GetPreferredIMEState(IMEState *aState)
5736 : {
5737 : // HTML editor don't prefer the CSS ime-mode because IE didn't do so too.
5738 0 : aState->mOpen = IMEState::DONT_CHANGE_OPEN_STATE;
5739 0 : if (IsReadonly() || IsDisabled()) {
5740 0 : aState->mEnabled = IMEState::DISABLED;
5741 : } else {
5742 0 : aState->mEnabled = IMEState::ENABLED;
5743 : }
5744 0 : return NS_OK;
5745 4392 : }
|