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 : * Daniel Glazman <glazman@netscape.com>
24 : * Mats Palmgren <matspal@gmail.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 : #include "nsUnicharUtils.h"
40 :
41 : #include "nsHTMLEditor.h"
42 : #include "nsHTMLEditRules.h"
43 : #include "nsTextEditUtils.h"
44 : #include "nsHTMLEditUtils.h"
45 : #include "nsIDOMNodeList.h"
46 : #include "nsIDOMAttr.h"
47 : #include "nsIDOMMouseEvent.h"
48 : #include "nsISelection.h"
49 : #include "nsISelectionPrivate.h"
50 : #include "nsIDOMHTMLImageElement.h"
51 : #include "nsISelectionController.h"
52 : #include "nsIDocumentObserver.h"
53 : #include "TypeInState.h"
54 :
55 : #include "nsIEnumerator.h"
56 : #include "nsIContent.h"
57 : #include "nsIContentIterator.h"
58 : #include "nsAttrName.h"
59 :
60 : #include "mozilla/dom/Element.h"
61 :
62 : using namespace mozilla;
63 :
64 0 : NS_IMETHODIMP nsHTMLEditor::AddDefaultProperty(nsIAtom *aProperty,
65 : const nsAString & aAttribute,
66 : const nsAString & aValue)
67 : {
68 0 : nsString outValue;
69 : PRInt32 index;
70 0 : nsString attr(aAttribute);
71 0 : if (TypeInState::FindPropInList(aProperty, attr, &outValue, mDefaultStyles, index))
72 : {
73 0 : PropItem *item = mDefaultStyles[index];
74 0 : item->value = aValue;
75 : }
76 : else
77 : {
78 0 : nsString value(aValue);
79 0 : PropItem *propItem = new PropItem(aProperty, attr, value);
80 0 : mDefaultStyles.AppendElement(propItem);
81 : }
82 0 : return NS_OK;
83 : }
84 :
85 0 : NS_IMETHODIMP nsHTMLEditor::RemoveDefaultProperty(nsIAtom *aProperty,
86 : const nsAString & aAttribute,
87 : const nsAString & aValue)
88 : {
89 0 : nsString outValue;
90 : PRInt32 index;
91 0 : nsString attr(aAttribute);
92 0 : if (TypeInState::FindPropInList(aProperty, attr, &outValue, mDefaultStyles, index))
93 : {
94 0 : delete mDefaultStyles[index];
95 0 : mDefaultStyles.RemoveElementAt(index);
96 : }
97 0 : return NS_OK;
98 : }
99 :
100 0 : NS_IMETHODIMP nsHTMLEditor::RemoveAllDefaultProperties()
101 : {
102 0 : PRUint32 j, defcon = mDefaultStyles.Length();
103 0 : for (j=0; j<defcon; j++)
104 : {
105 0 : delete mDefaultStyles[j];
106 : }
107 0 : mDefaultStyles.Clear();
108 0 : return NS_OK;
109 : }
110 :
111 :
112 : // Add the CSS style corresponding to the HTML inline style defined
113 : // by aProperty aAttribute and aValue to the selection
114 0 : NS_IMETHODIMP nsHTMLEditor::SetCSSInlineProperty(nsIAtom *aProperty,
115 : const nsAString & aAttribute,
116 : const nsAString & aValue)
117 : {
118 0 : if (IsCSSEnabled()) {
119 0 : return SetInlineProperty(aProperty, aAttribute, aValue);
120 : }
121 0 : return NS_OK;
122 : }
123 :
124 0 : NS_IMETHODIMP nsHTMLEditor::SetInlineProperty(nsIAtom *aProperty,
125 : const nsAString & aAttribute,
126 : const nsAString & aValue)
127 : {
128 0 : if (!aProperty) { return NS_ERROR_NULL_POINTER; }
129 0 : if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
130 0 : ForceCompositionEnd();
131 :
132 0 : nsCOMPtr<nsISelection>selection;
133 0 : nsresult res = GetSelection(getter_AddRefs(selection));
134 0 : NS_ENSURE_SUCCESS(res, res);
135 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
136 0 : nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
137 :
138 : bool isCollapsed;
139 0 : selection->GetIsCollapsed(&isCollapsed);
140 0 : if (isCollapsed)
141 : {
142 : // manipulating text attributes on a collapsed selection only sets state for the next text insertion
143 0 : nsString tAttr(aAttribute);//MJUDGE SCC NEED HELP
144 0 : nsString tVal(aValue);//MJUDGE SCC NEED HELP
145 0 : return mTypeInState->SetProp(aProperty, tAttr, tVal);
146 : }
147 :
148 0 : nsAutoEditBatch batchIt(this);
149 0 : nsAutoRules beginRulesSniffing(this, kOpInsertElement, nsIEditor::eNext);
150 0 : nsAutoSelectionReset selectionResetter(selection, this);
151 0 : nsAutoTxnsConserveSelection dontSpazMySelection(this);
152 :
153 : bool cancel, handled;
154 0 : nsTextRulesInfo ruleInfo(nsTextEditRules::kSetTextProperty);
155 0 : res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
156 0 : NS_ENSURE_SUCCESS(res, res);
157 0 : if (!cancel && !handled)
158 : {
159 : // get selection range enumerator
160 0 : nsCOMPtr<nsIEnumerator> enumerator;
161 0 : res = selPriv->GetEnumerator(getter_AddRefs(enumerator));
162 0 : NS_ENSURE_SUCCESS(res, res);
163 0 : NS_ENSURE_TRUE(enumerator, NS_ERROR_FAILURE);
164 :
165 : // loop thru the ranges in the selection
166 0 : enumerator->First();
167 0 : nsCOMPtr<nsISupports> currentItem;
168 0 : while ((NS_ENUMERATOR_FALSE == enumerator->IsDone()))
169 : {
170 0 : res = enumerator->CurrentItem(getter_AddRefs(currentItem));
171 0 : NS_ENSURE_SUCCESS(res, res);
172 0 : NS_ENSURE_TRUE(currentItem, NS_ERROR_FAILURE);
173 :
174 0 : nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
175 :
176 : // adjust range to include any ancestors who's children are entirely selected
177 0 : res = PromoteInlineRange(range);
178 0 : NS_ENSURE_SUCCESS(res, res);
179 :
180 : // check for easy case: both range endpoints in same text node
181 0 : nsCOMPtr<nsIDOMNode> startNode, endNode;
182 0 : res = range->GetStartContainer(getter_AddRefs(startNode));
183 0 : NS_ENSURE_SUCCESS(res, res);
184 0 : res = range->GetEndContainer(getter_AddRefs(endNode));
185 0 : NS_ENSURE_SUCCESS(res, res);
186 0 : if ((startNode == endNode) && IsTextNode(startNode))
187 : {
188 : PRInt32 startOffset, endOffset;
189 0 : range->GetStartOffset(&startOffset);
190 0 : range->GetEndOffset(&endOffset);
191 0 : nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(startNode);
192 0 : res = SetInlinePropertyOnTextNode(nodeAsText, startOffset, endOffset, aProperty, &aAttribute, &aValue);
193 0 : NS_ENSURE_SUCCESS(res, res);
194 : }
195 : else
196 : {
197 : // not the easy case. range not contained in single text node.
198 : // there are up to three phases here. There are all the nodes
199 : // reported by the subtree iterator to be processed. And there
200 : // are potentially a starting textnode and an ending textnode
201 : // which are only partially contained by the range.
202 :
203 : // lets handle the nodes reported by the iterator. These nodes
204 : // are entirely contained in the selection range. We build up
205 : // a list of them (since doing operations on the document during
206 : // iteration would perturb the iterator).
207 :
208 : nsCOMPtr<nsIContentIterator> iter =
209 0 : do_CreateInstance("@mozilla.org/content/subtree-content-iterator;1", &res);
210 0 : NS_ENSURE_SUCCESS(res, res);
211 0 : NS_ENSURE_TRUE(iter, NS_ERROR_FAILURE);
212 :
213 0 : nsCOMArray<nsIDOMNode> arrayOfNodes;
214 0 : nsCOMPtr<nsIDOMNode> node;
215 :
216 : // iterate range and build up array
217 0 : res = iter->Init(range);
218 : // init returns an error if no nodes in range.
219 : // this can easily happen with the subtree
220 : // iterator if the selection doesn't contain
221 : // any *whole* nodes.
222 0 : if (NS_SUCCEEDED(res))
223 : {
224 0 : while (!iter->IsDone())
225 : {
226 0 : node = do_QueryInterface(iter->GetCurrentNode());
227 0 : NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
228 :
229 0 : if (IsEditable(node))
230 : {
231 0 : arrayOfNodes.AppendObject(node);
232 : }
233 :
234 0 : iter->Next();
235 : }
236 : }
237 : // first check the start parent of the range to see if it needs to
238 : // be separately handled (it does if it's a text node, due to how the
239 : // subtree iterator works - it will not have reported it).
240 0 : if (IsTextNode(startNode) && IsEditable(startNode))
241 : {
242 0 : nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(startNode);
243 : PRInt32 startOffset;
244 : PRUint32 textLen;
245 0 : range->GetStartOffset(&startOffset);
246 0 : nodeAsText->GetLength(&textLen);
247 0 : res = SetInlinePropertyOnTextNode(nodeAsText, startOffset, textLen, aProperty, &aAttribute, &aValue);
248 0 : NS_ENSURE_SUCCESS(res, res);
249 : }
250 :
251 : // then loop through the list, set the property on each node
252 0 : PRInt32 listCount = arrayOfNodes.Count();
253 : PRInt32 j;
254 0 : for (j = 0; j < listCount; j++)
255 : {
256 0 : node = arrayOfNodes[j];
257 0 : res = SetInlinePropertyOnNode(node, aProperty, &aAttribute, &aValue);
258 0 : NS_ENSURE_SUCCESS(res, res);
259 : }
260 0 : arrayOfNodes.Clear();
261 :
262 : // last check the end parent of the range to see if it needs to
263 : // be separately handled (it does if it's a text node, due to how the
264 : // subtree iterator works - it will not have reported it).
265 0 : if (IsTextNode(endNode) && IsEditable(endNode))
266 : {
267 0 : nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(endNode);
268 : PRInt32 endOffset;
269 0 : range->GetEndOffset(&endOffset);
270 0 : res = SetInlinePropertyOnTextNode(nodeAsText, 0, endOffset, aProperty, &aAttribute, &aValue);
271 0 : NS_ENSURE_SUCCESS(res, res);
272 : }
273 : }
274 0 : enumerator->Next();
275 : }
276 : }
277 0 : if (!cancel)
278 : {
279 : // post-process
280 0 : res = mRules->DidDoAction(selection, &ruleInfo, res);
281 : }
282 0 : return res;
283 : }
284 :
285 :
286 :
287 : nsresult
288 0 : nsHTMLEditor::SetInlinePropertyOnTextNode( nsIDOMCharacterData *aTextNode,
289 : PRInt32 aStartOffset,
290 : PRInt32 aEndOffset,
291 : nsIAtom *aProperty,
292 : const nsAString *aAttribute,
293 : const nsAString *aValue)
294 : {
295 0 : NS_ENSURE_TRUE(aTextNode, NS_ERROR_NULL_POINTER);
296 0 : nsCOMPtr<nsIDOMNode> parent;
297 0 : nsresult res = aTextNode->GetParentNode(getter_AddRefs(parent));
298 0 : NS_ENSURE_SUCCESS(res, res);
299 :
300 0 : nsAutoString tagString;
301 0 : aProperty->ToString(tagString);
302 0 : if (!CanContainTag(parent, tagString)) return NS_OK;
303 :
304 : // don't need to do anything if no characters actually selected
305 0 : if (aStartOffset == aEndOffset) return NS_OK;
306 :
307 0 : nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aTextNode);
308 :
309 : // don't need to do anything if property already set on node
310 : bool bHasProp;
311 0 : if (IsCSSEnabled() &&
312 0 : mHTMLCSSUtils->IsCSSEditableProperty(node, aProperty, aAttribute)) {
313 : // the HTML styles defined by aProperty/aAttribute has a CSS equivalence
314 : // in this implementation for node; let's check if it carries those css styles
315 0 : nsAutoString value;
316 0 : if (aValue) value.Assign(*aValue);
317 : mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(node, aProperty, aAttribute,
318 : bHasProp, value,
319 0 : COMPUTED_STYLE_TYPE);
320 : }
321 : else
322 : {
323 0 : nsCOMPtr<nsIDOMNode> styleNode;
324 0 : IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, bHasProp, getter_AddRefs(styleNode));
325 : }
326 :
327 0 : if (bHasProp) return NS_OK;
328 :
329 : // do we need to split the text node?
330 : PRUint32 textLen;
331 0 : aTextNode->GetLength(&textLen);
332 :
333 0 : nsCOMPtr<nsIDOMNode> tmp;
334 0 : if ( (PRUint32)aEndOffset != textLen )
335 : {
336 : // we need to split off back of text node
337 0 : res = SplitNode(node, aEndOffset, getter_AddRefs(tmp));
338 0 : NS_ENSURE_SUCCESS(res, res);
339 0 : node = tmp; // remember left node
340 : }
341 0 : if ( aStartOffset )
342 : {
343 : // we need to split off front of text node
344 0 : res = SplitNode(node, aStartOffset, getter_AddRefs(tmp));
345 0 : NS_ENSURE_SUCCESS(res, res);
346 : }
347 :
348 : // look for siblings that are correct type of node
349 0 : nsCOMPtr<nsIDOMNode> sibling;
350 0 : GetPriorHTMLSibling(node, address_of(sibling));
351 0 : if (sibling && NodeIsType(sibling, aProperty) &&
352 0 : HasAttrVal(sibling, aAttribute, aValue) &&
353 0 : IsOnlyAttribute(sibling, aAttribute) )
354 : {
355 : // previous sib is already right kind of inline node; slide this over into it
356 0 : res = MoveNode(node, sibling, -1);
357 0 : return res;
358 : }
359 0 : sibling = nsnull;
360 0 : GetNextHTMLSibling(node, address_of(sibling));
361 0 : if (sibling && NodeIsType(sibling, aProperty) &&
362 0 : HasAttrVal(sibling, aAttribute, aValue) &&
363 0 : IsOnlyAttribute(sibling, aAttribute) )
364 : {
365 : // following sib is already right kind of inline node; slide this over into it
366 0 : res = MoveNode(node, sibling, 0);
367 0 : return res;
368 : }
369 :
370 : // reparent the node inside inline node with appropriate {attribute,value}
371 0 : return SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue);
372 : }
373 :
374 :
375 : nsresult
376 0 : nsHTMLEditor::SetInlinePropertyOnNode( nsIDOMNode *aNode,
377 : nsIAtom *aProperty,
378 : const nsAString *aAttribute,
379 : const nsAString *aValue)
380 : {
381 0 : NS_ENSURE_TRUE(aNode && aProperty, NS_ERROR_NULL_POINTER);
382 :
383 0 : nsresult res = NS_OK;
384 0 : nsCOMPtr<nsIDOMNode> tmp;
385 0 : nsAutoString tag;
386 0 : aProperty->ToString(tag);
387 0 : ToLowerCase(tag);
388 :
389 0 : if (IsCSSEnabled())
390 : {
391 : // we are in CSS mode
392 0 : if (mHTMLCSSUtils->IsCSSEditableProperty(aNode, aProperty, aAttribute))
393 : {
394 : // the HTML style defined by aProperty/aAttribute has a CSS equivalence
395 : // in this implementation for the node aNode
396 0 : nsCOMPtr<nsIDOMNode> tmp = aNode;
397 0 : if (IsTextNode(tmp))
398 : {
399 : // we are working on a text node and need to create a span container
400 : // that will carry the styles
401 : InsertContainerAbove( aNode,
402 : address_of(tmp),
403 0 : NS_LITERAL_STRING("span"),
404 : nsnull,
405 0 : nsnull);
406 : }
407 0 : nsCOMPtr<nsIDOMElement>element;
408 0 : element = do_QueryInterface(tmp);
409 : // first we have to remove occurences of the same style hint in the
410 : // children of the aNode
411 0 : res = RemoveStyleInside(tmp, aProperty, aAttribute, true);
412 0 : NS_ENSURE_SUCCESS(res, res);
413 : PRInt32 count;
414 : // then we add the css styles corresponding to the HTML style request
415 0 : res = mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(element, aProperty, aAttribute, aValue, &count, false);
416 0 : NS_ENSURE_SUCCESS(res, res);
417 :
418 0 : nsCOMPtr<nsIDOMNode> nextSibling, previousSibling;
419 0 : GetNextHTMLSibling(tmp, address_of(nextSibling));
420 0 : GetPriorHTMLSibling(tmp, address_of(previousSibling));
421 0 : if (nextSibling || previousSibling)
422 : {
423 0 : nsCOMPtr<nsIDOMNode> mergeParent;
424 0 : res = tmp->GetParentNode(getter_AddRefs(mergeParent));
425 0 : NS_ENSURE_SUCCESS(res, res);
426 0 : if (previousSibling &&
427 0 : nsEditor::NodeIsType(previousSibling, nsEditProperty::span) &&
428 0 : NodesSameType(tmp, previousSibling))
429 : {
430 0 : res = JoinNodes(previousSibling, tmp, mergeParent);
431 0 : NS_ENSURE_SUCCESS(res, res);
432 : }
433 0 : if (nextSibling &&
434 0 : nsEditor::NodeIsType(nextSibling, nsEditProperty::span) &&
435 0 : NodesSameType(tmp, nextSibling))
436 : {
437 0 : res = JoinNodes(tmp, nextSibling, mergeParent);
438 : }
439 : }
440 0 : return res;
441 : }
442 : }
443 :
444 : // don't need to do anything if property already set on node
445 : bool bHasProp;
446 0 : nsCOMPtr<nsIDOMNode> styleNode;
447 0 : IsTextPropertySetByContent(aNode, aProperty, aAttribute, aValue, bHasProp, getter_AddRefs(styleNode));
448 0 : if (bHasProp) return NS_OK;
449 :
450 : // is it already the right kind of node, but with wrong attribute?
451 0 : if (NodeIsType(aNode, aProperty))
452 : {
453 : // just set the attribute on it.
454 : // but first remove any contrary style in it's children.
455 0 : res = RemoveStyleInside(aNode, aProperty, aAttribute, true);
456 0 : NS_ENSURE_SUCCESS(res, res);
457 0 : nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(aNode);
458 0 : return SetAttribute(elem, *aAttribute, *aValue);
459 : }
460 :
461 : // can it be put inside inline node?
462 0 : if (TagCanContain(tag, aNode))
463 : {
464 0 : nsCOMPtr<nsIDOMNode> priorNode, nextNode;
465 : // is either of it's neighbors the right kind of node?
466 0 : GetPriorHTMLSibling(aNode, address_of(priorNode));
467 0 : GetNextHTMLSibling(aNode, address_of(nextNode));
468 0 : if (priorNode && NodeIsType(priorNode, aProperty) &&
469 0 : HasAttrVal(priorNode, aAttribute, aValue) &&
470 0 : IsOnlyAttribute(priorNode, aAttribute) )
471 : {
472 : // previous sib is already right kind of inline node; slide this over into it
473 0 : res = MoveNode(aNode, priorNode, -1);
474 : }
475 0 : else if (nextNode && NodeIsType(nextNode, aProperty) &&
476 0 : HasAttrVal(nextNode, aAttribute, aValue) &&
477 0 : IsOnlyAttribute(priorNode, aAttribute) )
478 : {
479 : // following sib is already right kind of inline node; slide this over into it
480 0 : res = MoveNode(aNode, nextNode, 0);
481 : }
482 : else
483 : {
484 : // ok, chuck it in its very own container
485 0 : res = InsertContainerAbove(aNode, address_of(tmp), tag, aAttribute, aValue);
486 : }
487 0 : NS_ENSURE_SUCCESS(res, res);
488 0 : return RemoveStyleInside(aNode, aProperty, aAttribute);
489 : }
490 : // none of the above? then cycle through the children.
491 0 : nsCOMPtr<nsIDOMNodeList> childNodes;
492 0 : res = aNode->GetChildNodes(getter_AddRefs(childNodes));
493 0 : NS_ENSURE_SUCCESS(res, res);
494 0 : if (childNodes)
495 : {
496 : PRInt32 j;
497 : PRUint32 childCount;
498 0 : childNodes->GetLength(&childCount);
499 0 : if (childCount)
500 : {
501 0 : nsCOMArray<nsIDOMNode> arrayOfNodes;
502 0 : nsCOMPtr<nsIDOMNode> node;
503 :
504 : // populate the list
505 0 : for (j=0 ; j < (PRInt32)childCount; j++)
506 : {
507 0 : nsCOMPtr<nsIDOMNode> childNode;
508 0 : res = childNodes->Item(j, getter_AddRefs(childNode));
509 0 : if ((NS_SUCCEEDED(res)) && (childNode) && IsEditable(childNode))
510 : {
511 0 : arrayOfNodes.AppendObject(childNode);
512 : }
513 : }
514 :
515 : // then loop through the list, set the property on each node
516 0 : PRInt32 listCount = arrayOfNodes.Count();
517 0 : for (j = 0; j < listCount; j++)
518 : {
519 0 : node = arrayOfNodes[j];
520 0 : res = SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue);
521 0 : NS_ENSURE_SUCCESS(res, res);
522 : }
523 0 : arrayOfNodes.Clear();
524 : }
525 : }
526 0 : return res;
527 : }
528 :
529 :
530 0 : nsresult nsHTMLEditor::SplitStyleAboveRange(nsIDOMRange *inRange,
531 : nsIAtom *aProperty,
532 : const nsAString *aAttribute)
533 : {
534 0 : NS_ENSURE_TRUE(inRange, NS_ERROR_NULL_POINTER);
535 : nsresult res;
536 0 : nsCOMPtr<nsIDOMNode> startNode, endNode, origStartNode;
537 : PRInt32 startOffset, endOffset;
538 :
539 0 : res = inRange->GetStartContainer(getter_AddRefs(startNode));
540 0 : NS_ENSURE_SUCCESS(res, res);
541 0 : res = inRange->GetStartOffset(&startOffset);
542 0 : NS_ENSURE_SUCCESS(res, res);
543 0 : res = inRange->GetEndContainer(getter_AddRefs(endNode));
544 0 : NS_ENSURE_SUCCESS(res, res);
545 0 : res = inRange->GetEndOffset(&endOffset);
546 0 : NS_ENSURE_SUCCESS(res, res);
547 :
548 0 : origStartNode = startNode;
549 :
550 : // split any matching style nodes above the start of range
551 : {
552 0 : nsAutoTrackDOMPoint tracker(mRangeUpdater, address_of(endNode), &endOffset);
553 0 : res = SplitStyleAbovePoint(address_of(startNode), &startOffset, aProperty, aAttribute);
554 0 : NS_ENSURE_SUCCESS(res, res);
555 : }
556 :
557 : // second verse, same as the first...
558 0 : res = SplitStyleAbovePoint(address_of(endNode), &endOffset, aProperty, aAttribute);
559 0 : NS_ENSURE_SUCCESS(res, res);
560 :
561 : // reset the range
562 0 : res = inRange->SetStart(startNode, startOffset);
563 0 : NS_ENSURE_SUCCESS(res, res);
564 0 : res = inRange->SetEnd(endNode, endOffset);
565 0 : return res;
566 : }
567 :
568 0 : nsresult nsHTMLEditor::SplitStyleAbovePoint(nsCOMPtr<nsIDOMNode> *aNode,
569 : PRInt32 *aOffset,
570 : nsIAtom *aProperty, // null here means we split all properties
571 : const nsAString *aAttribute,
572 : nsCOMPtr<nsIDOMNode> *outLeftNode,
573 : nsCOMPtr<nsIDOMNode> *outRightNode)
574 : {
575 0 : NS_ENSURE_TRUE(aNode && *aNode && aOffset, NS_ERROR_NULL_POINTER);
576 0 : if (outLeftNode) *outLeftNode = nsnull;
577 0 : if (outRightNode) *outRightNode = nsnull;
578 : // split any matching style nodes above the node/offset
579 0 : nsCOMPtr<nsIDOMNode> parent, tmp = *aNode;
580 : PRInt32 offset;
581 :
582 0 : bool useCSS = IsCSSEnabled();
583 :
584 : bool isSet;
585 0 : while (tmp && !IsBlockNode(tmp))
586 : {
587 0 : isSet = false;
588 0 : if (useCSS && mHTMLCSSUtils->IsCSSEditableProperty(tmp, aProperty, aAttribute)) {
589 : // the HTML style defined by aProperty/aAttribute has a CSS equivalence
590 : // in this implementation for the node tmp; let's check if it carries those css styles
591 0 : nsAutoString firstValue;
592 : mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(tmp, aProperty, aAttribute,
593 : isSet, firstValue,
594 0 : SPECIFIED_STYLE_TYPE);
595 : }
596 0 : if ( (aProperty && NodeIsType(tmp, aProperty)) || // node is the correct inline prop
597 0 : (aProperty == nsEditProperty::href && nsHTMLEditUtils::IsLink(tmp)) ||
598 : // node is href - test if really <a href=...
599 0 : (!aProperty && NodeIsProperty(tmp)) || // or node is any prop, and we asked to split them all
600 : isSet) // or the style is specified in the style attribute
601 : {
602 : // found a style node we need to split
603 : nsresult rv = SplitNodeDeep(tmp, *aNode, *aOffset, &offset, false,
604 0 : outLeftNode, outRightNode);
605 0 : NS_ENSURE_SUCCESS(rv, rv);
606 : // reset startNode/startOffset
607 0 : tmp->GetParentNode(getter_AddRefs(*aNode));
608 0 : *aOffset = offset;
609 : }
610 0 : tmp->GetParentNode(getter_AddRefs(parent));
611 0 : tmp = parent;
612 : }
613 0 : return NS_OK;
614 : }
615 :
616 0 : bool nsHTMLEditor::NodeIsProperty(nsIDOMNode *aNode)
617 : {
618 0 : NS_ENSURE_TRUE(aNode, false);
619 0 : if (!IsContainer(aNode)) return false;
620 0 : if (!IsEditable(aNode)) return false;
621 0 : if (IsBlockNode(aNode)) return false;
622 0 : if (NodeIsType(aNode, nsEditProperty::a)) return false;
623 0 : return true;
624 : }
625 :
626 0 : nsresult nsHTMLEditor::ApplyDefaultProperties()
627 : {
628 0 : nsresult res = NS_OK;
629 0 : PRUint32 j, defcon = mDefaultStyles.Length();
630 0 : for (j=0; j<defcon; j++)
631 : {
632 0 : PropItem *propItem = mDefaultStyles[j];
633 0 : NS_ENSURE_TRUE(propItem, NS_ERROR_NULL_POINTER);
634 0 : res = SetInlineProperty(propItem->tag, propItem->attr, propItem->value);
635 0 : NS_ENSURE_SUCCESS(res, res);
636 : }
637 0 : return res;
638 : }
639 :
640 0 : nsresult nsHTMLEditor::RemoveStyleInside(nsIDOMNode *aNode,
641 : nsIAtom *aProperty, // null here means remove all properties
642 : const nsAString *aAttribute,
643 : bool aChildrenOnly)
644 : {
645 0 : NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
646 0 : if (IsTextNode(aNode)) return NS_OK;
647 0 : nsresult res = NS_OK;
648 :
649 : // first process the children
650 0 : nsCOMPtr<nsIDOMNode> child, tmp;
651 0 : aNode->GetFirstChild(getter_AddRefs(child));
652 0 : while (child)
653 : {
654 : // cache next sibling since we might remove child
655 0 : child->GetNextSibling(getter_AddRefs(tmp));
656 0 : res = RemoveStyleInside(child, aProperty, aAttribute);
657 0 : NS_ENSURE_SUCCESS(res, res);
658 0 : child = tmp;
659 : }
660 :
661 : // then process the node itself
662 0 : if ((!aChildrenOnly &&
663 0 : ((aProperty && NodeIsType(aNode, aProperty)) || // node is prop we asked for
664 0 : (aProperty == nsEditProperty::href && nsHTMLEditUtils::IsLink(aNode)) || // but check for link (<a href=...)
665 0 : (aProperty == nsEditProperty::name && nsHTMLEditUtils::IsNamedAnchor(aNode)))) || // and for named anchors
666 0 : (!aProperty && NodeIsProperty(aNode))) // or node is any prop and we asked for that
667 : {
668 : // if we weren't passed an attribute, then we want to
669 : // remove any matching inlinestyles entirely
670 0 : if (!aAttribute || aAttribute->IsEmpty())
671 : {
672 0 : NS_NAMED_LITERAL_STRING(styleAttr, "style");
673 0 : NS_NAMED_LITERAL_STRING(classAttr, "class");
674 0 : bool hasStyleAttr = HasAttr(aNode, &styleAttr);
675 0 : bool hasClassAtrr = HasAttr(aNode, &classAttr);
676 0 : if (aProperty &&
677 : (hasStyleAttr || hasClassAtrr)) {
678 : // aNode carries inline styles or a class attribute so we can't
679 : // just remove the element... We need to create above the element
680 : // a span that will carry those styles or class, then we can delete
681 : // the node.
682 0 : nsCOMPtr<nsIDOMNode> spanNode;
683 : res = InsertContainerAbove(aNode, address_of(spanNode),
684 0 : NS_LITERAL_STRING("span"));
685 0 : NS_ENSURE_SUCCESS(res, res);
686 0 : res = CloneAttribute(styleAttr, spanNode, aNode);
687 0 : NS_ENSURE_SUCCESS(res, res);
688 0 : res = CloneAttribute(classAttr, spanNode, aNode);
689 0 : NS_ENSURE_SUCCESS(res, res);
690 0 : if (hasStyleAttr)
691 : {
692 : // we need to remove the styles property corresponding to
693 : // aProperty (bug 215406)
694 0 : nsAutoString propertyValue;
695 : mHTMLCSSUtils->RemoveCSSEquivalentToHTMLStyle(spanNode,
696 : aProperty,
697 : aAttribute,
698 : &propertyValue,
699 0 : false);
700 : // remove the span if it's useless
701 0 : RemoveElementIfNoStyleOrIdOrClass(spanNode);
702 : }
703 : }
704 0 : res = RemoveContainer(aNode);
705 : }
706 : // otherwise we just want to eliminate the attribute
707 : else
708 : {
709 0 : if (HasAttr(aNode, aAttribute))
710 : {
711 : // if this matching attribute is the ONLY one on the node,
712 : // then remove the whole node. Otherwise just nix the attribute.
713 0 : if (IsOnlyAttribute(aNode, aAttribute))
714 : {
715 0 : res = RemoveContainer(aNode);
716 : }
717 : else
718 : {
719 0 : nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(aNode);
720 0 : NS_ENSURE_TRUE(elem, NS_ERROR_NULL_POINTER);
721 0 : res = RemoveAttribute(elem, *aAttribute);
722 : }
723 : }
724 : }
725 : }
726 : else {
727 0 : if (!aChildrenOnly && IsCSSEnabled() &&
728 0 : mHTMLCSSUtils->IsCSSEditableProperty(aNode, aProperty, aAttribute)) {
729 : // the HTML style defined by aProperty/aAttribute has a CSS equivalence
730 : // in this implementation for the node aNode; let's check if it carries those css styles
731 0 : nsAutoString propertyValue;
732 : bool isSet;
733 : mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(aNode, aProperty, aAttribute,
734 : isSet, propertyValue,
735 0 : SPECIFIED_STYLE_TYPE);
736 0 : if (isSet) {
737 : // yes, tmp has the corresponding css declarations in its style attribute
738 : // let's remove them
739 : mHTMLCSSUtils->RemoveCSSEquivalentToHTMLStyle(aNode,
740 : aProperty,
741 : aAttribute,
742 : &propertyValue,
743 0 : false);
744 : // remove the node if it is a span, if its style attribute is empty or absent,
745 : // and if it does not have a class nor an id
746 0 : RemoveElementIfNoStyleOrIdOrClass(aNode);
747 0 : res = NS_OK;
748 : }
749 : }
750 : }
751 0 : if ( aProperty == nsEditProperty::font && // or node is big or small and we are setting font size
752 0 : (nsHTMLEditUtils::IsBig(aNode) || nsHTMLEditUtils::IsSmall(aNode)) &&
753 0 : aAttribute && aAttribute->LowerCaseEqualsLiteral("size"))
754 : {
755 0 : res = RemoveContainer(aNode); // if we are setting font size, remove any nested bigs and smalls
756 : }
757 0 : return res;
758 : }
759 :
760 0 : bool nsHTMLEditor::IsOnlyAttribute(nsIDOMNode *aNode,
761 : const nsAString *aAttribute)
762 : {
763 0 : NS_ENSURE_TRUE(aNode && aAttribute, false); // ooops
764 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
765 0 : NS_ENSURE_TRUE(content, false); // ooops
766 :
767 0 : PRUint32 i, attrCount = content->GetAttrCount();
768 0 : for (i = 0; i < attrCount; ++i) {
769 0 : nsAutoString attrString;
770 0 : const nsAttrName* name = content->GetAttrNameAt(i);
771 0 : if (!name->NamespaceEquals(kNameSpaceID_None)) {
772 0 : return false;
773 : }
774 0 : name->LocalName()->ToString(attrString);
775 : // if it's the attribute we know about, or a special _moz attribute,
776 : // keep looking
777 0 : if (!attrString.Equals(*aAttribute, nsCaseInsensitiveStringComparator()) &&
778 0 : !StringBeginsWith(attrString, NS_LITERAL_STRING("_moz"))) {
779 0 : return false;
780 : }
781 : }
782 : // if we made it through all of them without finding a real attribute
783 : // other than aAttribute, then return true
784 0 : return true;
785 : }
786 :
787 0 : bool nsHTMLEditor::HasAttr(nsIDOMNode* aNode,
788 : const nsAString* aAttribute)
789 : {
790 0 : NS_ENSURE_TRUE(aNode, false);
791 0 : if (!aAttribute || aAttribute->IsEmpty()) {
792 : // everybody has the 'null' attribute
793 0 : return true;
794 : }
795 :
796 : // get element
797 0 : nsCOMPtr<dom::Element> element = do_QueryInterface(aNode);
798 0 : NS_ENSURE_TRUE(element, false);
799 :
800 0 : nsCOMPtr<nsIAtom> atom = do_GetAtom(*aAttribute);
801 0 : NS_ENSURE_TRUE(atom, false);
802 :
803 0 : return element->HasAttr(kNameSpaceID_None, atom);
804 : }
805 :
806 :
807 0 : bool nsHTMLEditor::HasAttrVal(nsIDOMNode* aNode,
808 : const nsAString* aAttribute,
809 : const nsAString* aValue)
810 : {
811 0 : NS_ENSURE_TRUE(aNode, false);
812 0 : if (!aAttribute || aAttribute->IsEmpty()) {
813 : // everybody has the 'null' attribute
814 0 : return true;
815 : }
816 :
817 : // get element
818 0 : nsCOMPtr<dom::Element> element = do_QueryInterface(aNode);
819 0 : NS_ENSURE_TRUE(element, false);
820 :
821 0 : nsCOMPtr<nsIAtom> atom = do_GetAtom(*aAttribute);
822 0 : NS_ENSURE_TRUE(atom, false);
823 :
824 0 : return element->AttrValueIs(kNameSpaceID_None, atom, *aValue, eIgnoreCase);
825 : }
826 :
827 0 : nsresult nsHTMLEditor::PromoteRangeIfStartsOrEndsInNamedAnchor(nsIDOMRange *inRange)
828 : {
829 0 : NS_ENSURE_TRUE(inRange, NS_ERROR_NULL_POINTER);
830 : nsresult res;
831 0 : nsCOMPtr<nsIDOMNode> startNode, endNode, parent, tmp;
832 : PRInt32 startOffset, endOffset, tmpOffset;
833 :
834 0 : res = inRange->GetStartContainer(getter_AddRefs(startNode));
835 0 : NS_ENSURE_SUCCESS(res, res);
836 0 : res = inRange->GetStartOffset(&startOffset);
837 0 : NS_ENSURE_SUCCESS(res, res);
838 0 : res = inRange->GetEndContainer(getter_AddRefs(endNode));
839 0 : NS_ENSURE_SUCCESS(res, res);
840 0 : res = inRange->GetEndOffset(&endOffset);
841 0 : NS_ENSURE_SUCCESS(res, res);
842 :
843 0 : tmp = startNode;
844 0 : while ( tmp &&
845 0 : !nsTextEditUtils::IsBody(tmp) &&
846 0 : !nsHTMLEditUtils::IsNamedAnchor(tmp))
847 : {
848 0 : res = GetNodeLocation(tmp, address_of(parent), &tmpOffset);
849 0 : NS_ENSURE_SUCCESS(res, res);
850 0 : tmp = parent;
851 : }
852 0 : NS_ENSURE_TRUE(tmp, NS_ERROR_NULL_POINTER);
853 0 : if (nsHTMLEditUtils::IsNamedAnchor(tmp))
854 : {
855 0 : res = GetNodeLocation(tmp, address_of(parent), &tmpOffset);
856 0 : NS_ENSURE_SUCCESS(res, res);
857 0 : startNode = parent;
858 0 : startOffset = tmpOffset;
859 : }
860 :
861 0 : tmp = endNode;
862 0 : while ( tmp &&
863 0 : !nsTextEditUtils::IsBody(tmp) &&
864 0 : !nsHTMLEditUtils::IsNamedAnchor(tmp))
865 : {
866 0 : res = GetNodeLocation(tmp, address_of(parent), &tmpOffset);
867 0 : NS_ENSURE_SUCCESS(res, res);
868 0 : tmp = parent;
869 : }
870 0 : NS_ENSURE_TRUE(tmp, NS_ERROR_NULL_POINTER);
871 0 : if (nsHTMLEditUtils::IsNamedAnchor(tmp))
872 : {
873 0 : res = GetNodeLocation(tmp, address_of(parent), &tmpOffset);
874 0 : NS_ENSURE_SUCCESS(res, res);
875 0 : endNode = parent;
876 0 : endOffset = tmpOffset + 1;
877 : }
878 :
879 0 : res = inRange->SetStart(startNode, startOffset);
880 0 : NS_ENSURE_SUCCESS(res, res);
881 0 : res = inRange->SetEnd(endNode, endOffset);
882 0 : return res;
883 : }
884 :
885 0 : nsresult nsHTMLEditor::PromoteInlineRange(nsIDOMRange *inRange)
886 : {
887 0 : NS_ENSURE_TRUE(inRange, NS_ERROR_NULL_POINTER);
888 : nsresult res;
889 0 : nsCOMPtr<nsIDOMNode> startNode, endNode, parent;
890 : PRInt32 startOffset, endOffset;
891 :
892 0 : res = inRange->GetStartContainer(getter_AddRefs(startNode));
893 0 : NS_ENSURE_SUCCESS(res, res);
894 0 : res = inRange->GetStartOffset(&startOffset);
895 0 : NS_ENSURE_SUCCESS(res, res);
896 0 : res = inRange->GetEndContainer(getter_AddRefs(endNode));
897 0 : NS_ENSURE_SUCCESS(res, res);
898 0 : res = inRange->GetEndOffset(&endOffset);
899 0 : NS_ENSURE_SUCCESS(res, res);
900 :
901 0 : while ( startNode &&
902 0 : !nsTextEditUtils::IsBody(startNode) &&
903 0 : IsEditable(startNode) &&
904 0 : IsAtFrontOfNode(startNode, startOffset) )
905 : {
906 0 : res = GetNodeLocation(startNode, address_of(parent), &startOffset);
907 0 : NS_ENSURE_SUCCESS(res, res);
908 0 : startNode = parent;
909 : }
910 0 : NS_ENSURE_TRUE(startNode, NS_ERROR_NULL_POINTER);
911 :
912 0 : while ( endNode &&
913 0 : !nsTextEditUtils::IsBody(endNode) &&
914 0 : IsEditable(endNode) &&
915 0 : IsAtEndOfNode(endNode, endOffset) )
916 : {
917 0 : res = GetNodeLocation(endNode, address_of(parent), &endOffset);
918 0 : NS_ENSURE_SUCCESS(res, res);
919 0 : endNode = parent;
920 0 : endOffset++; // we are AFTER this node
921 : }
922 0 : NS_ENSURE_TRUE(endNode, NS_ERROR_NULL_POINTER);
923 :
924 0 : res = inRange->SetStart(startNode, startOffset);
925 0 : NS_ENSURE_SUCCESS(res, res);
926 0 : res = inRange->SetEnd(endNode, endOffset);
927 0 : return res;
928 : }
929 :
930 0 : bool nsHTMLEditor::IsAtFrontOfNode(nsIDOMNode *aNode, PRInt32 aOffset)
931 : {
932 0 : NS_ENSURE_TRUE(aNode, false); // oops
933 0 : if (!aOffset) {
934 0 : return true;
935 : }
936 :
937 0 : if (IsTextNode(aNode))
938 : {
939 0 : return false;
940 : }
941 : else
942 : {
943 0 : nsCOMPtr<nsIDOMNode> firstNode;
944 0 : GetFirstEditableChild(aNode, address_of(firstNode));
945 0 : NS_ENSURE_TRUE(firstNode, true);
946 : PRInt32 offset;
947 0 : nsEditor::GetChildOffset(firstNode, aNode, offset);
948 0 : if (offset < aOffset) return false;
949 0 : return true;
950 : }
951 : }
952 :
953 0 : bool nsHTMLEditor::IsAtEndOfNode(nsIDOMNode *aNode, PRInt32 aOffset)
954 : {
955 0 : NS_ENSURE_TRUE(aNode, false); // oops
956 : PRUint32 len;
957 0 : GetLengthOfDOMNode(aNode, len);
958 0 : if (aOffset == (PRInt32)len) return true;
959 :
960 0 : if (IsTextNode(aNode))
961 : {
962 0 : return false;
963 : }
964 : else
965 : {
966 0 : nsCOMPtr<nsIDOMNode> lastNode;
967 0 : GetLastEditableChild(aNode, address_of(lastNode));
968 0 : NS_ENSURE_TRUE(lastNode, true);
969 : PRInt32 offset;
970 0 : nsEditor::GetChildOffset(lastNode, aNode, offset);
971 0 : if (offset < aOffset) return true;
972 0 : return false;
973 : }
974 : }
975 :
976 :
977 : nsresult
978 0 : nsHTMLEditor::GetInlinePropertyBase(nsIAtom *aProperty,
979 : const nsAString *aAttribute,
980 : const nsAString *aValue,
981 : bool *aFirst,
982 : bool *aAny,
983 : bool *aAll,
984 : nsAString *outValue,
985 : bool aCheckDefaults)
986 : {
987 0 : NS_ENSURE_TRUE(aProperty, NS_ERROR_NULL_POINTER);
988 :
989 : nsresult result;
990 0 : *aAny=false;
991 0 : *aAll=true;
992 0 : *aFirst=false;
993 0 : bool first=true;
994 :
995 0 : bool useCSS = IsCSSEnabled();
996 :
997 0 : nsCOMPtr<nsISelection>selection;
998 0 : result = GetSelection(getter_AddRefs(selection));
999 0 : NS_ENSURE_SUCCESS(result, result);
1000 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
1001 0 : nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
1002 :
1003 : bool isCollapsed;
1004 0 : selection->GetIsCollapsed(&isCollapsed);
1005 0 : nsCOMPtr<nsIDOMNode> collapsedNode;
1006 0 : nsCOMPtr<nsIEnumerator> enumerator;
1007 0 : result = selPriv->GetEnumerator(getter_AddRefs(enumerator));
1008 0 : NS_ENSURE_SUCCESS(result, result);
1009 0 : NS_ENSURE_TRUE(enumerator, NS_ERROR_NULL_POINTER);
1010 :
1011 0 : enumerator->First();
1012 0 : nsCOMPtr<nsISupports> currentItem;
1013 0 : result = enumerator->CurrentItem(getter_AddRefs(currentItem));
1014 : // XXX: should be a while loop, to get each separate range
1015 : // XXX: ERROR_HANDLING can currentItem be null?
1016 0 : if ((NS_SUCCEEDED(result)) && currentItem)
1017 : {
1018 0 : bool firstNodeInRange = true; // for each range, set a flag
1019 0 : nsCOMPtr<nsIDOMRange> range(do_QueryInterface(currentItem));
1020 :
1021 0 : if (isCollapsed)
1022 : {
1023 0 : range->GetStartContainer(getter_AddRefs(collapsedNode));
1024 0 : NS_ENSURE_TRUE(collapsedNode, NS_ERROR_FAILURE);
1025 : bool isSet, theSetting;
1026 0 : if (aAttribute)
1027 : {
1028 0 : nsString tString(*aAttribute); //MJUDGE SCC NEED HELP
1029 0 : nsString tOutString;//MJUDGE SCC NEED HELP
1030 0 : mTypeInState->GetTypingState(isSet, theSetting, aProperty, tString, &tOutString);
1031 0 : if (outValue)
1032 0 : outValue->Assign(tOutString);
1033 : }
1034 : else
1035 0 : mTypeInState->GetTypingState(isSet, theSetting, aProperty);
1036 0 : if (isSet)
1037 : {
1038 0 : *aFirst = *aAny = *aAll = theSetting;
1039 0 : return NS_OK;
1040 : }
1041 0 : if (!useCSS) {
1042 0 : nsCOMPtr<nsIDOMNode> resultNode;
1043 : IsTextPropertySetByContent(collapsedNode, aProperty, aAttribute, aValue,
1044 0 : isSet, getter_AddRefs(resultNode), outValue);
1045 0 : *aFirst = *aAny = *aAll = isSet;
1046 :
1047 0 : if (!isSet && aCheckDefaults)
1048 : {
1049 : // style not set, but if it is a default then it will appear if
1050 : // content is inserted, so we should report it as set (analogous to TypeInState).
1051 : PRInt32 index;
1052 0 : if (aAttribute &&
1053 0 : TypeInState::FindPropInList(aProperty, *aAttribute, outValue, mDefaultStyles, index))
1054 : {
1055 0 : *aFirst = *aAny = *aAll = true;
1056 0 : if (outValue)
1057 0 : outValue->Assign(mDefaultStyles[index]->value);
1058 : }
1059 : }
1060 0 : return NS_OK;
1061 : }
1062 : }
1063 :
1064 : // non-collapsed selection
1065 : nsCOMPtr<nsIContentIterator> iter =
1066 0 : do_CreateInstance("@mozilla.org/content/post-content-iterator;1");
1067 0 : NS_ENSURE_TRUE(iter, NS_ERROR_NULL_POINTER);
1068 :
1069 0 : iter->Init(range);
1070 0 : nsAutoString firstValue, theValue;
1071 :
1072 0 : nsCOMPtr<nsIDOMNode> endNode;
1073 : PRInt32 endOffset;
1074 0 : result = range->GetEndContainer(getter_AddRefs(endNode));
1075 0 : NS_ENSURE_SUCCESS(result, result);
1076 0 : result = range->GetEndOffset(&endOffset);
1077 0 : NS_ENSURE_SUCCESS(result, result);
1078 0 : while (!iter->IsDone())
1079 : {
1080 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(iter->GetCurrentNode());
1081 :
1082 0 : nsCOMPtr<nsIDOMNode> node = do_QueryInterface(content);
1083 :
1084 0 : if (node && nsTextEditUtils::IsBody(node))
1085 : break;
1086 :
1087 0 : nsCOMPtr<nsIDOMCharacterData>text;
1088 0 : text = do_QueryInterface(content);
1089 :
1090 0 : bool skipNode = false;
1091 :
1092 : // just ignore any non-editable nodes
1093 0 : if (text && !IsEditable(text))
1094 : {
1095 0 : skipNode = true;
1096 : }
1097 0 : else if (text)
1098 : {
1099 0 : if (!isCollapsed && first && firstNodeInRange)
1100 : {
1101 0 : firstNodeInRange = false;
1102 : PRInt32 startOffset;
1103 0 : range->GetStartOffset(&startOffset);
1104 : PRUint32 count;
1105 0 : text->GetLength(&count);
1106 0 : if (startOffset==(PRInt32)count)
1107 : {
1108 0 : skipNode = true;
1109 0 : }
1110 : }
1111 0 : else if (node == endNode && !endOffset)
1112 : {
1113 0 : skipNode = true;
1114 : }
1115 : }
1116 0 : else if (content->IsElement())
1117 : { // handle non-text leaf nodes here
1118 0 : skipNode = true;
1119 : }
1120 0 : if (!skipNode)
1121 : {
1122 0 : if (node)
1123 : {
1124 0 : bool isSet = false;
1125 0 : nsCOMPtr<nsIDOMNode>resultNode;
1126 0 : if (first)
1127 : {
1128 0 : if (useCSS &&
1129 0 : mHTMLCSSUtils->IsCSSEditableProperty(node, aProperty, aAttribute)) {
1130 : // the HTML styles defined by aProperty/aAttribute has a CSS equivalence
1131 : // in this implementation for node; let's check if it carries those css styles
1132 0 : if (aValue) firstValue.Assign(*aValue);
1133 : mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(node, aProperty, aAttribute,
1134 : isSet, firstValue,
1135 0 : COMPUTED_STYLE_TYPE);
1136 : }
1137 : else {
1138 : IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet,
1139 0 : getter_AddRefs(resultNode), &firstValue);
1140 : }
1141 0 : *aFirst = isSet;
1142 0 : first = false;
1143 0 : if (outValue) *outValue = firstValue;
1144 : }
1145 : else
1146 : {
1147 0 : if (useCSS &&
1148 0 : mHTMLCSSUtils->IsCSSEditableProperty(node, aProperty, aAttribute)) {
1149 : // the HTML styles defined by aProperty/aAttribute has a CSS equivalence
1150 : // in this implementation for node; let's check if it carries those css styles
1151 0 : if (aValue) theValue.Assign(*aValue);
1152 : mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(node, aProperty, aAttribute,
1153 : isSet, theValue,
1154 0 : COMPUTED_STYLE_TYPE);
1155 : }
1156 : else {
1157 : IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet,
1158 0 : getter_AddRefs(resultNode), &theValue);
1159 : }
1160 0 : if (firstValue != theValue)
1161 0 : *aAll = false;
1162 : }
1163 :
1164 0 : if (isSet) {
1165 0 : *aAny = true;
1166 : }
1167 : else {
1168 0 : *aAll = false;
1169 : }
1170 : }
1171 : }
1172 :
1173 0 : iter->Next();
1174 : }
1175 : }
1176 0 : if (!*aAny)
1177 : { // make sure that if none of the selection is set, we don't report all is set
1178 0 : *aAll = false;
1179 : }
1180 0 : return result;
1181 : }
1182 :
1183 :
1184 0 : NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty,
1185 : const nsAString &aAttribute,
1186 : const nsAString &aValue,
1187 : bool *aFirst,
1188 : bool *aAny,
1189 : bool *aAll)
1190 : {
1191 0 : NS_ENSURE_TRUE(aProperty && aFirst && aAny && aAll, NS_ERROR_NULL_POINTER);
1192 0 : const nsAString *att = nsnull;
1193 0 : if (!aAttribute.IsEmpty())
1194 0 : att = &aAttribute;
1195 0 : const nsAString *val = nsnull;
1196 0 : if (!aValue.IsEmpty())
1197 0 : val = &aValue;
1198 0 : return GetInlinePropertyBase( aProperty, att, val, aFirst, aAny, aAll, nsnull);
1199 : }
1200 :
1201 :
1202 0 : NS_IMETHODIMP nsHTMLEditor::GetInlinePropertyWithAttrValue(nsIAtom *aProperty,
1203 : const nsAString &aAttribute,
1204 : const nsAString &aValue,
1205 : bool *aFirst,
1206 : bool *aAny,
1207 : bool *aAll,
1208 : nsAString &outValue)
1209 : {
1210 0 : NS_ENSURE_TRUE(aProperty && aFirst && aAny && aAll, NS_ERROR_NULL_POINTER);
1211 0 : const nsAString *att = nsnull;
1212 0 : if (!aAttribute.IsEmpty())
1213 0 : att = &aAttribute;
1214 0 : const nsAString *val = nsnull;
1215 0 : if (!aValue.IsEmpty())
1216 0 : val = &aValue;
1217 0 : return GetInlinePropertyBase( aProperty, att, val, aFirst, aAny, aAll, &outValue);
1218 : }
1219 :
1220 :
1221 0 : NS_IMETHODIMP nsHTMLEditor::RemoveAllInlineProperties()
1222 : {
1223 0 : nsAutoEditBatch batchIt(this);
1224 0 : nsAutoRules beginRulesSniffing(this, kOpResetTextProperties, nsIEditor::eNext);
1225 :
1226 0 : nsresult res = RemoveInlinePropertyImpl(nsnull, nsnull);
1227 0 : NS_ENSURE_SUCCESS(res, res);
1228 0 : return ApplyDefaultProperties();
1229 : }
1230 :
1231 0 : NS_IMETHODIMP nsHTMLEditor::RemoveInlineProperty(nsIAtom *aProperty, const nsAString &aAttribute)
1232 : {
1233 0 : return RemoveInlinePropertyImpl(aProperty, &aAttribute);
1234 : }
1235 :
1236 0 : nsresult nsHTMLEditor::RemoveInlinePropertyImpl(nsIAtom *aProperty, const nsAString *aAttribute)
1237 : {
1238 0 : NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED);
1239 0 : ForceCompositionEnd();
1240 :
1241 : nsresult res;
1242 0 : nsCOMPtr<nsISelection>selection;
1243 0 : res = GetSelection(getter_AddRefs(selection));
1244 0 : NS_ENSURE_SUCCESS(res, res);
1245 0 : NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
1246 0 : nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
1247 :
1248 : bool isCollapsed;
1249 0 : selection->GetIsCollapsed(&isCollapsed);
1250 :
1251 0 : bool useCSS = IsCSSEnabled();
1252 0 : if (isCollapsed)
1253 : {
1254 : // manipulating text attributes on a collapsed selection only sets state for the next text insertion
1255 :
1256 : // For links, aProperty uses "href", use "a" instead
1257 0 : if (aProperty == nsEditProperty::href ||
1258 : aProperty == nsEditProperty::name)
1259 0 : aProperty = nsEditProperty::a;
1260 :
1261 0 : if (aProperty) return mTypeInState->ClearProp(aProperty, nsAutoString(*aAttribute));
1262 0 : else return mTypeInState->ClearAllProps();
1263 : }
1264 0 : nsAutoEditBatch batchIt(this);
1265 0 : nsAutoRules beginRulesSniffing(this, kOpRemoveTextProperty, nsIEditor::eNext);
1266 0 : nsAutoSelectionReset selectionResetter(selection, this);
1267 0 : nsAutoTxnsConserveSelection dontSpazMySelection(this);
1268 :
1269 : bool cancel, handled;
1270 0 : nsTextRulesInfo ruleInfo(nsTextEditRules::kRemoveTextProperty);
1271 0 : res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
1272 0 : NS_ENSURE_SUCCESS(res, res);
1273 0 : if (!cancel && !handled)
1274 : {
1275 : // get selection range enumerator
1276 0 : nsCOMPtr<nsIEnumerator> enumerator;
1277 0 : res = selPriv->GetEnumerator(getter_AddRefs(enumerator));
1278 0 : NS_ENSURE_SUCCESS(res, res);
1279 0 : NS_ENSURE_TRUE(enumerator, NS_ERROR_FAILURE);
1280 :
1281 : // loop thru the ranges in the selection
1282 0 : enumerator->First();
1283 0 : nsCOMPtr<nsISupports> currentItem;
1284 0 : while ((NS_ENUMERATOR_FALSE == enumerator->IsDone()))
1285 : {
1286 0 : res = enumerator->CurrentItem(getter_AddRefs(currentItem));
1287 0 : NS_ENSURE_SUCCESS(res, res);
1288 0 : NS_ENSURE_TRUE(currentItem, NS_ERROR_FAILURE);
1289 :
1290 0 : nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
1291 :
1292 0 : if (aProperty == nsEditProperty::name)
1293 : {
1294 : // promote range if it starts or end in a named anchor and we
1295 : // want to remove named anchors
1296 0 : res = PromoteRangeIfStartsOrEndsInNamedAnchor(range);
1297 : }
1298 : else {
1299 : // adjust range to include any ancestors who's children are entirely selected
1300 0 : res = PromoteInlineRange(range);
1301 : }
1302 0 : NS_ENSURE_SUCCESS(res, res);
1303 :
1304 : // remove this style from ancestors of our range endpoints,
1305 : // splitting them as appropriate
1306 0 : res = SplitStyleAboveRange(range, aProperty, aAttribute);
1307 0 : NS_ENSURE_SUCCESS(res, res);
1308 :
1309 : // check for easy case: both range endpoints in same text node
1310 0 : nsCOMPtr<nsIDOMNode> startNode, endNode;
1311 0 : res = range->GetStartContainer(getter_AddRefs(startNode));
1312 0 : NS_ENSURE_SUCCESS(res, res);
1313 0 : res = range->GetEndContainer(getter_AddRefs(endNode));
1314 0 : NS_ENSURE_SUCCESS(res, res);
1315 0 : if ((startNode == endNode) && IsTextNode(startNode))
1316 : {
1317 : // we're done with this range!
1318 0 : if (useCSS && mHTMLCSSUtils->IsCSSEditableProperty(startNode, aProperty, aAttribute)) {
1319 : // the HTML style defined by aProperty/aAttribute has a CSS equivalence
1320 : // in this implementation for startNode
1321 0 : nsAutoString cssValue;
1322 0 : bool isSet = false;
1323 : mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(startNode,
1324 : aProperty,
1325 : aAttribute,
1326 : isSet ,
1327 : cssValue,
1328 0 : COMPUTED_STYLE_TYPE);
1329 0 : if (isSet) {
1330 : // startNode's computed style indicates the CSS equivalence to the HTML style to
1331 : // remove is applied; but we found no element in the ancestors of startNode
1332 : // carrying specified styles; assume it comes from a rule and let's try to
1333 : // insert a span "inverting" the style
1334 0 : nsAutoString value; value.AssignLiteral("-moz-editor-invert-value");
1335 : PRInt32 startOffset, endOffset;
1336 0 : range->GetStartOffset(&startOffset);
1337 0 : range->GetEndOffset(&endOffset);
1338 0 : nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(startNode);
1339 0 : if (mHTMLCSSUtils->IsCSSInvertable(aProperty, aAttribute)) {
1340 0 : SetInlinePropertyOnTextNode(nodeAsText, startOffset, endOffset, aProperty, aAttribute, &value);
1341 : }
1342 : }
1343 : }
1344 : }
1345 : else
1346 : {
1347 : // not the easy case. range not contained in single text node.
1348 : nsCOMPtr<nsIContentIterator> iter =
1349 0 : do_CreateInstance("@mozilla.org/content/subtree-content-iterator;1", &res);
1350 0 : NS_ENSURE_SUCCESS(res, res);
1351 0 : NS_ENSURE_TRUE(iter, NS_ERROR_FAILURE);
1352 :
1353 0 : nsCOMArray<nsIDOMNode> arrayOfNodes;
1354 0 : nsCOMPtr<nsIDOMNode> node;
1355 :
1356 : // iterate range and build up array
1357 0 : iter->Init(range);
1358 0 : while (!iter->IsDone())
1359 : {
1360 0 : node = do_QueryInterface(iter->GetCurrentNode());
1361 0 : NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
1362 :
1363 0 : if (IsEditable(node))
1364 : {
1365 0 : arrayOfNodes.AppendObject(node);
1366 : }
1367 :
1368 0 : iter->Next();
1369 : }
1370 :
1371 : // loop through the list, remove the property on each node
1372 0 : PRInt32 listCount = arrayOfNodes.Count();
1373 : PRInt32 j;
1374 0 : for (j = 0; j < listCount; j++)
1375 : {
1376 0 : node = arrayOfNodes[j];
1377 0 : res = RemoveStyleInside(node, aProperty, aAttribute);
1378 0 : NS_ENSURE_SUCCESS(res, res);
1379 0 : if (useCSS && mHTMLCSSUtils->IsCSSEditableProperty(node, aProperty, aAttribute)) {
1380 : // the HTML style defined by aProperty/aAttribute has a CSS equivalence
1381 : // in this implementation for node
1382 0 : nsAutoString cssValue;
1383 0 : bool isSet = false;
1384 : mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(node,
1385 : aProperty,
1386 : aAttribute,
1387 : isSet ,
1388 : cssValue,
1389 0 : COMPUTED_STYLE_TYPE);
1390 0 : if (isSet) {
1391 : // startNode's computed style indicates the CSS equivalence to the HTML style to
1392 : // remove is applied; but we found no element in the ancestors of startNode
1393 : // carrying specified styles; assume it comes from a rule and let's try to
1394 : // insert a span "inverting" the style
1395 0 : if (mHTMLCSSUtils->IsCSSInvertable(aProperty, aAttribute)) {
1396 0 : nsAutoString value; value.AssignLiteral("-moz-editor-invert-value");
1397 0 : SetInlinePropertyOnNode(node, aProperty, aAttribute, &value);
1398 : }
1399 : }
1400 : }
1401 : }
1402 0 : arrayOfNodes.Clear();
1403 : }
1404 0 : enumerator->Next();
1405 : }
1406 : }
1407 0 : if (!cancel)
1408 : {
1409 : // post-process
1410 0 : res = mRules->DidDoAction(selection, &ruleInfo, res);
1411 : }
1412 0 : return res;
1413 : }
1414 :
1415 0 : NS_IMETHODIMP nsHTMLEditor::IncreaseFontSize()
1416 : {
1417 0 : return RelativeFontChange(1);
1418 : }
1419 :
1420 0 : NS_IMETHODIMP nsHTMLEditor::DecreaseFontSize()
1421 : {
1422 0 : return RelativeFontChange(-1);
1423 : }
1424 :
1425 : nsresult
1426 0 : nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange)
1427 : {
1428 : // Can only change font size by + or - 1
1429 0 : if ( !( (aSizeChange==1) || (aSizeChange==-1) ) )
1430 0 : return NS_ERROR_ILLEGAL_VALUE;
1431 :
1432 0 : ForceCompositionEnd();
1433 :
1434 : // Get the selection
1435 0 : nsCOMPtr<nsISelection>selection;
1436 0 : nsresult res = GetSelection(getter_AddRefs(selection));
1437 0 : NS_ENSURE_SUCCESS(res, res);
1438 0 : NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
1439 0 : nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
1440 : // Is the selection collapsed?
1441 : bool bCollapsed;
1442 0 : res = selection->GetIsCollapsed(&bCollapsed);
1443 0 : NS_ENSURE_SUCCESS(res, res);
1444 :
1445 : // if it's collapsed set typing state
1446 0 : if (bCollapsed)
1447 : {
1448 0 : nsCOMPtr<nsIAtom> atom;
1449 0 : if (aSizeChange==1) atom = nsEditProperty::big;
1450 0 : else atom = nsEditProperty::small;
1451 :
1452 : // Let's see in what kind of element the selection is
1453 : PRInt32 offset;
1454 0 : nsCOMPtr<nsIDOMNode> selectedNode;
1455 0 : res = GetStartNodeAndOffset(selection, getter_AddRefs(selectedNode), &offset);
1456 0 : if (IsTextNode(selectedNode)) {
1457 0 : nsCOMPtr<nsIDOMNode> parent;
1458 0 : res = selectedNode->GetParentNode(getter_AddRefs(parent));
1459 0 : NS_ENSURE_SUCCESS(res, res);
1460 0 : selectedNode = parent;
1461 : }
1462 0 : nsAutoString tag;
1463 0 : atom->ToString(tag);
1464 0 : if (!CanContainTag(selectedNode, tag)) return NS_OK;
1465 :
1466 : // manipulating text attributes on a collapsed selection only sets state for the next text insertion
1467 0 : return mTypeInState->SetProp(atom, EmptyString(), EmptyString());
1468 : }
1469 :
1470 : // wrap with txn batching, rules sniffing, and selection preservation code
1471 0 : nsAutoEditBatch batchIt(this);
1472 0 : nsAutoRules beginRulesSniffing(this, kOpSetTextProperty, nsIEditor::eNext);
1473 0 : nsAutoSelectionReset selectionResetter(selection, this);
1474 0 : nsAutoTxnsConserveSelection dontSpazMySelection(this);
1475 :
1476 : // get selection range enumerator
1477 0 : nsCOMPtr<nsIEnumerator> enumerator;
1478 0 : res = selPriv->GetEnumerator(getter_AddRefs(enumerator));
1479 0 : NS_ENSURE_SUCCESS(res, res);
1480 0 : NS_ENSURE_TRUE(enumerator, NS_ERROR_FAILURE);
1481 :
1482 : // loop thru the ranges in the selection
1483 0 : enumerator->First();
1484 0 : nsCOMPtr<nsISupports> currentItem;
1485 0 : while ((NS_ENUMERATOR_FALSE == enumerator->IsDone()))
1486 : {
1487 0 : res = enumerator->CurrentItem(getter_AddRefs(currentItem));
1488 0 : NS_ENSURE_SUCCESS(res, res);
1489 0 : NS_ENSURE_TRUE(currentItem, NS_ERROR_FAILURE);
1490 :
1491 0 : nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
1492 :
1493 : // adjust range to include any ancestors who's children are entirely selected
1494 0 : res = PromoteInlineRange(range);
1495 0 : NS_ENSURE_SUCCESS(res, res);
1496 :
1497 : // check for easy case: both range endpoints in same text node
1498 0 : nsCOMPtr<nsIDOMNode> startNode, endNode;
1499 0 : res = range->GetStartContainer(getter_AddRefs(startNode));
1500 0 : NS_ENSURE_SUCCESS(res, res);
1501 0 : res = range->GetEndContainer(getter_AddRefs(endNode));
1502 0 : NS_ENSURE_SUCCESS(res, res);
1503 0 : if ((startNode == endNode) && IsTextNode(startNode))
1504 : {
1505 : PRInt32 startOffset, endOffset;
1506 0 : range->GetStartOffset(&startOffset);
1507 0 : range->GetEndOffset(&endOffset);
1508 0 : nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(startNode);
1509 0 : res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, startOffset, endOffset);
1510 0 : NS_ENSURE_SUCCESS(res, res);
1511 : }
1512 : else
1513 : {
1514 : // not the easy case. range not contained in single text node.
1515 : // there are up to three phases here. There are all the nodes
1516 : // reported by the subtree iterator to be processed. And there
1517 : // are potentially a starting textnode and an ending textnode
1518 : // which are only partially contained by the range.
1519 :
1520 : // lets handle the nodes reported by the iterator. These nodes
1521 : // are entirely contained in the selection range. We build up
1522 : // a list of them (since doing operations on the document during
1523 : // iteration would perturb the iterator).
1524 :
1525 : nsCOMPtr<nsIContentIterator> iter =
1526 0 : do_CreateInstance("@mozilla.org/content/subtree-content-iterator;1", &res);
1527 0 : NS_ENSURE_SUCCESS(res, res);
1528 0 : NS_ENSURE_TRUE(iter, NS_ERROR_FAILURE);
1529 :
1530 0 : nsCOMArray<nsIDOMNode> arrayOfNodes;
1531 0 : nsCOMPtr<nsIDOMNode> node;
1532 :
1533 : // iterate range and build up array
1534 0 : res = iter->Init(range);
1535 0 : if (NS_SUCCEEDED(res))
1536 : {
1537 0 : while (!iter->IsDone())
1538 : {
1539 0 : node = do_QueryInterface(iter->GetCurrentNode());
1540 0 : NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
1541 :
1542 0 : if (IsEditable(node))
1543 : {
1544 0 : arrayOfNodes.AppendObject(node);
1545 : }
1546 :
1547 0 : iter->Next();
1548 : }
1549 :
1550 : // now that we have the list, do the font size change on each node
1551 0 : PRInt32 listCount = arrayOfNodes.Count();
1552 : PRInt32 j;
1553 0 : for (j = 0; j < listCount; j++)
1554 : {
1555 0 : node = arrayOfNodes[j];
1556 0 : res = RelativeFontChangeOnNode(aSizeChange, node);
1557 0 : NS_ENSURE_SUCCESS(res, res);
1558 : }
1559 0 : arrayOfNodes.Clear();
1560 : }
1561 : // now check the start and end parents of the range to see if they need to
1562 : // be separately handled (they do if they are text nodes, due to how the
1563 : // subtree iterator works - it will not have reported them).
1564 0 : if (IsTextNode(startNode) && IsEditable(startNode))
1565 : {
1566 0 : nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(startNode);
1567 : PRInt32 startOffset;
1568 : PRUint32 textLen;
1569 0 : range->GetStartOffset(&startOffset);
1570 0 : nodeAsText->GetLength(&textLen);
1571 0 : res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, startOffset, textLen);
1572 0 : NS_ENSURE_SUCCESS(res, res);
1573 : }
1574 0 : if (IsTextNode(endNode) && IsEditable(endNode))
1575 : {
1576 0 : nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(endNode);
1577 : PRInt32 endOffset;
1578 0 : range->GetEndOffset(&endOffset);
1579 0 : res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, 0, endOffset);
1580 0 : NS_ENSURE_SUCCESS(res, res);
1581 : }
1582 : }
1583 0 : enumerator->Next();
1584 : }
1585 :
1586 0 : return res;
1587 : }
1588 :
1589 : nsresult
1590 0 : nsHTMLEditor::RelativeFontChangeOnTextNode( PRInt32 aSizeChange,
1591 : nsIDOMCharacterData *aTextNode,
1592 : PRInt32 aStartOffset,
1593 : PRInt32 aEndOffset)
1594 : {
1595 : // Can only change font size by + or - 1
1596 0 : if ( !( (aSizeChange==1) || (aSizeChange==-1) ) )
1597 0 : return NS_ERROR_ILLEGAL_VALUE;
1598 0 : NS_ENSURE_TRUE(aTextNode, NS_ERROR_NULL_POINTER);
1599 :
1600 : // don't need to do anything if no characters actually selected
1601 0 : if (aStartOffset == aEndOffset) return NS_OK;
1602 :
1603 0 : nsresult res = NS_OK;
1604 0 : nsCOMPtr<nsIDOMNode> parent;
1605 0 : res = aTextNode->GetParentNode(getter_AddRefs(parent));
1606 0 : NS_ENSURE_SUCCESS(res, res);
1607 0 : if (!CanContainTag(parent, NS_LITERAL_STRING("big"))) return NS_OK;
1608 :
1609 0 : nsCOMPtr<nsIDOMNode> tmp, node = do_QueryInterface(aTextNode);
1610 :
1611 : // do we need to split the text node?
1612 : PRUint32 textLen;
1613 0 : aTextNode->GetLength(&textLen);
1614 :
1615 : // -1 is a magic value meaning to the end of node
1616 0 : if (aEndOffset == -1) aEndOffset = textLen;
1617 :
1618 0 : if ( (PRUint32)aEndOffset != textLen )
1619 : {
1620 : // we need to split off back of text node
1621 0 : res = SplitNode(node, aEndOffset, getter_AddRefs(tmp));
1622 0 : NS_ENSURE_SUCCESS(res, res);
1623 0 : node = tmp; // remember left node
1624 : }
1625 0 : if ( aStartOffset )
1626 : {
1627 : // we need to split off front of text node
1628 0 : res = SplitNode(node, aStartOffset, getter_AddRefs(tmp));
1629 0 : NS_ENSURE_SUCCESS(res, res);
1630 : }
1631 :
1632 0 : NS_NAMED_LITERAL_STRING(bigSize, "big");
1633 0 : NS_NAMED_LITERAL_STRING(smallSize, "small");
1634 0 : const nsAString& nodeType = (aSizeChange==1) ? static_cast<const nsAString&>(bigSize) : static_cast<const nsAString&>(smallSize);
1635 : // look for siblings that are correct type of node
1636 0 : nsCOMPtr<nsIDOMNode> sibling;
1637 0 : GetPriorHTMLSibling(node, address_of(sibling));
1638 0 : if (sibling && NodeIsType(sibling, (aSizeChange==1) ? nsEditProperty::big : nsEditProperty::small))
1639 : {
1640 : // previous sib is already right kind of inline node; slide this over into it
1641 0 : res = MoveNode(node, sibling, -1);
1642 0 : return res;
1643 : }
1644 0 : sibling = nsnull;
1645 0 : GetNextHTMLSibling(node, address_of(sibling));
1646 0 : if (sibling && NodeIsType(sibling, (aSizeChange==1) ? nsEditProperty::big : nsEditProperty::small))
1647 : {
1648 : // following sib is already right kind of inline node; slide this over into it
1649 0 : res = MoveNode(node, sibling, 0);
1650 0 : return res;
1651 : }
1652 :
1653 : // else reparent the node inside font node with appropriate relative size
1654 0 : res = InsertContainerAbove(node, address_of(tmp), nodeType);
1655 0 : return res;
1656 : }
1657 :
1658 :
1659 : nsresult
1660 0 : nsHTMLEditor::RelativeFontChangeHelper( PRInt32 aSizeChange,
1661 : nsIDOMNode *aNode)
1662 : {
1663 : /* This routine looks for all the font nodes in the tree rooted by aNode,
1664 : including aNode itself, looking for font nodes that have the size attr
1665 : set. Any such nodes need to have big or small put inside them, since
1666 : they override any big/small that are above them.
1667 : */
1668 :
1669 : // Can only change font size by + or - 1
1670 0 : if ( !( (aSizeChange==1) || (aSizeChange==-1) ) )
1671 0 : return NS_ERROR_ILLEGAL_VALUE;
1672 0 : NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
1673 :
1674 0 : nsresult res = NS_OK;
1675 0 : nsAutoString tag;
1676 0 : if (aSizeChange == 1) tag.AssignLiteral("big");
1677 0 : else tag.AssignLiteral("small");
1678 0 : nsCOMPtr<nsIDOMNodeList> childNodes;
1679 : PRInt32 j;
1680 : PRUint32 childCount;
1681 0 : nsCOMPtr<nsIDOMNode> childNode;
1682 :
1683 : // if this is a font node with size, put big/small inside it
1684 0 : NS_NAMED_LITERAL_STRING(attr, "size");
1685 0 : if (NodeIsType(aNode, nsEditProperty::font) && HasAttr(aNode, &attr))
1686 : {
1687 : // cycle through children and adjust relative font size
1688 0 : res = aNode->GetChildNodes(getter_AddRefs(childNodes));
1689 0 : NS_ENSURE_SUCCESS(res, res);
1690 0 : if (childNodes)
1691 : {
1692 0 : childNodes->GetLength(&childCount);
1693 0 : for (j=childCount-1; j>=0; j--)
1694 : {
1695 0 : res = childNodes->Item(j, getter_AddRefs(childNode));
1696 0 : if ((NS_SUCCEEDED(res)) && (childNode))
1697 : {
1698 0 : res = RelativeFontChangeOnNode(aSizeChange, childNode);
1699 0 : NS_ENSURE_SUCCESS(res, res);
1700 : }
1701 : }
1702 : }
1703 : }
1704 :
1705 0 : childNodes = nsnull;
1706 : // now cycle through the children.
1707 0 : res = aNode->GetChildNodes(getter_AddRefs(childNodes));
1708 0 : NS_ENSURE_SUCCESS(res, res);
1709 0 : if (childNodes)
1710 : {
1711 0 : childNodes->GetLength(&childCount);
1712 0 : for (j=childCount-1; j>=0; j--)
1713 : {
1714 0 : res = childNodes->Item(j, getter_AddRefs(childNode));
1715 0 : if ((NS_SUCCEEDED(res)) && (childNode))
1716 : {
1717 0 : res = RelativeFontChangeHelper(aSizeChange, childNode);
1718 0 : NS_ENSURE_SUCCESS(res, res);
1719 : }
1720 : }
1721 : }
1722 :
1723 0 : return res;
1724 : }
1725 :
1726 :
1727 : nsresult
1728 0 : nsHTMLEditor::RelativeFontChangeOnNode( PRInt32 aSizeChange,
1729 : nsIDOMNode *aNode)
1730 : {
1731 : // Can only change font size by + or - 1
1732 0 : if ( !( (aSizeChange==1) || (aSizeChange==-1) ) )
1733 0 : return NS_ERROR_ILLEGAL_VALUE;
1734 0 : NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
1735 :
1736 0 : nsresult res = NS_OK;
1737 0 : nsCOMPtr<nsIDOMNode> tmp;
1738 0 : nsAutoString tag;
1739 0 : if (aSizeChange == 1) tag.AssignLiteral("big");
1740 0 : else tag.AssignLiteral("small");
1741 :
1742 : // is it the opposite of what we want?
1743 0 : if ( ((aSizeChange == 1) && nsHTMLEditUtils::IsSmall(aNode)) ||
1744 0 : ((aSizeChange == -1) && nsHTMLEditUtils::IsBig(aNode)) )
1745 : {
1746 : // first populate any nested font tags that have the size attr set
1747 0 : res = RelativeFontChangeHelper(aSizeChange, aNode);
1748 0 : NS_ENSURE_SUCCESS(res, res);
1749 : // in that case, just remove this node and pull up the children
1750 0 : res = RemoveContainer(aNode);
1751 0 : return res;
1752 : }
1753 : // can it be put inside a "big" or "small"?
1754 0 : if (TagCanContain(tag, aNode))
1755 : {
1756 : // first populate any nested font tags that have the size attr set
1757 0 : res = RelativeFontChangeHelper(aSizeChange, aNode);
1758 0 : NS_ENSURE_SUCCESS(res, res);
1759 : // ok, chuck it in.
1760 : // first look at siblings of aNode for matching bigs or smalls.
1761 : // if we find one, move aNode into it.
1762 0 : nsCOMPtr<nsIDOMNode> sibling;
1763 0 : GetPriorHTMLSibling(aNode, address_of(sibling));
1764 0 : if (sibling && nsEditor::NodeIsType(sibling, (aSizeChange==1 ? nsEditProperty::big : nsEditProperty::small)))
1765 : {
1766 : // previous sib is already right kind of inline node; slide this over into it
1767 0 : res = MoveNode(aNode, sibling, -1);
1768 0 : return res;
1769 : }
1770 0 : sibling = nsnull;
1771 0 : GetNextHTMLSibling(aNode, address_of(sibling));
1772 0 : if (sibling && nsEditor::NodeIsType(sibling, (aSizeChange==1 ? nsEditProperty::big : nsEditProperty::small)))
1773 : {
1774 : // following sib is already right kind of inline node; slide this over into it
1775 0 : res = MoveNode(aNode, sibling, 0);
1776 0 : return res;
1777 : }
1778 : // else insert it above aNode
1779 0 : res = InsertContainerAbove(aNode, address_of(tmp), tag);
1780 0 : return res;
1781 : }
1782 : // none of the above? then cycle through the children.
1783 : // MOOSE: we should group the children together if possible
1784 : // into a single "big" or "small". For the moment they are
1785 : // each getting their own.
1786 0 : nsCOMPtr<nsIDOMNodeList> childNodes;
1787 0 : res = aNode->GetChildNodes(getter_AddRefs(childNodes));
1788 0 : NS_ENSURE_SUCCESS(res, res);
1789 0 : if (childNodes)
1790 : {
1791 : PRInt32 j;
1792 : PRUint32 childCount;
1793 0 : childNodes->GetLength(&childCount);
1794 0 : for (j=childCount-1; j>=0; j--)
1795 : {
1796 0 : nsCOMPtr<nsIDOMNode> childNode;
1797 0 : res = childNodes->Item(j, getter_AddRefs(childNode));
1798 0 : if ((NS_SUCCEEDED(res)) && (childNode))
1799 : {
1800 0 : res = RelativeFontChangeOnNode(aSizeChange, childNode);
1801 0 : NS_ENSURE_SUCCESS(res, res);
1802 : }
1803 : }
1804 : }
1805 0 : return res;
1806 : }
1807 :
1808 : NS_IMETHODIMP
1809 0 : nsHTMLEditor::GetFontFaceState(bool *aMixed, nsAString &outFace)
1810 : {
1811 0 : NS_ENSURE_TRUE(aMixed, NS_ERROR_FAILURE);
1812 0 : *aMixed = true;
1813 0 : outFace.Truncate();
1814 :
1815 : nsresult res;
1816 : bool first, any, all;
1817 :
1818 0 : NS_NAMED_LITERAL_STRING(attr, "face");
1819 0 : res = GetInlinePropertyBase(nsEditProperty::font, &attr, nsnull, &first, &any, &all, &outFace);
1820 0 : NS_ENSURE_SUCCESS(res, res);
1821 0 : if (any && !all) return res; // mixed
1822 0 : if (all)
1823 : {
1824 0 : *aMixed = false;
1825 0 : return res;
1826 : }
1827 :
1828 : // if there is no font face, check for tt
1829 0 : res = GetInlinePropertyBase(nsEditProperty::tt, nsnull, nsnull, &first, &any, &all,nsnull);
1830 0 : NS_ENSURE_SUCCESS(res, res);
1831 0 : if (any && !all) return res; // mixed
1832 0 : if (all)
1833 : {
1834 0 : *aMixed = false;
1835 0 : nsEditProperty::tt->ToString(outFace);
1836 : }
1837 :
1838 0 : if (!any)
1839 : {
1840 : // there was no font face attrs of any kind. We are in normal font.
1841 0 : outFace.Truncate();
1842 0 : *aMixed = false;
1843 : }
1844 0 : return res;
1845 : }
1846 :
1847 : NS_IMETHODIMP
1848 0 : nsHTMLEditor::GetFontColorState(bool *aMixed, nsAString &aOutColor)
1849 : {
1850 0 : NS_ENSURE_TRUE(aMixed, NS_ERROR_NULL_POINTER);
1851 0 : *aMixed = true;
1852 0 : aOutColor.Truncate();
1853 :
1854 : nsresult res;
1855 0 : NS_NAMED_LITERAL_STRING(colorStr, "color");
1856 : bool first, any, all;
1857 :
1858 0 : res = GetInlinePropertyBase(nsEditProperty::font, &colorStr, nsnull, &first, &any, &all, &aOutColor);
1859 0 : NS_ENSURE_SUCCESS(res, res);
1860 0 : if (any && !all) return res; // mixed
1861 0 : if (all)
1862 : {
1863 0 : *aMixed = false;
1864 0 : return res;
1865 : }
1866 :
1867 0 : if (!any)
1868 : {
1869 : // there was no font color attrs of any kind..
1870 0 : aOutColor.Truncate();
1871 0 : *aMixed = false;
1872 : }
1873 0 : return res;
1874 : }
1875 :
1876 : // the return value is true only if the instance of the HTML editor we created
1877 : // can handle CSS styles (for instance, Composer can, Messenger can't) and if
1878 : // the CSS preference is checked
1879 : nsresult
1880 0 : nsHTMLEditor::GetIsCSSEnabled(bool *aIsCSSEnabled)
1881 : {
1882 0 : *aIsCSSEnabled = IsCSSEnabled();
1883 0 : return NS_OK;
1884 : }
1885 :
1886 : static bool
1887 0 : HasNonEmptyAttribute(dom::Element* aElement, nsIAtom* aName)
1888 : {
1889 0 : MOZ_ASSERT(aElement);
1890 :
1891 0 : nsAutoString value;
1892 0 : return aElement->GetAttr(kNameSpaceID_None, aName, value) && !value.IsEmpty();
1893 : }
1894 :
1895 : bool
1896 0 : nsHTMLEditor::HasStyleOrIdOrClass(dom::Element* aElement)
1897 : {
1898 0 : MOZ_ASSERT(aElement);
1899 :
1900 : // remove the node if its style attribute is empty or absent,
1901 : // and if it does not have a class nor an id
1902 0 : return HasNonEmptyAttribute(aElement, nsGkAtoms::style) ||
1903 0 : HasNonEmptyAttribute(aElement, nsGkAtoms::_class) ||
1904 0 : HasNonEmptyAttribute(aElement, nsGkAtoms::id);
1905 : }
1906 :
1907 : nsresult
1908 0 : nsHTMLEditor::RemoveElementIfNoStyleOrIdOrClass(nsIDOMNode* aElement)
1909 : {
1910 0 : nsCOMPtr<dom::Element> element = do_QueryInterface(aElement);
1911 0 : NS_ENSURE_TRUE(element, NS_ERROR_NULL_POINTER);
1912 :
1913 : // early way out if node is not the right kind of element
1914 0 : if (!element->IsHTML(nsGkAtoms::span) || HasStyleOrIdOrClass(element)) {
1915 0 : return NS_OK;
1916 : }
1917 :
1918 0 : return RemoveContainer(element);
1919 : }
|