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 : * Original Author: Daniel Glazman <glazman@netscape.com>
24 : * Ms2ger <ms2ger@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 :
40 : #include "nsHTMLEditor.h"
41 : #include "nsCOMPtr.h"
42 : #include "nsHTMLEditUtils.h"
43 : #include "nsEditProperty.h"
44 : #include "ChangeCSSInlineStyleTxn.h"
45 : #include "nsIDOMElement.h"
46 : #include "nsIDOMElementCSSInlineStyle.h"
47 : #include "nsIDOMDocument.h"
48 : #include "nsIContent.h"
49 : #include "nsIAtom.h"
50 : #include "nsTextEditUtils.h"
51 : #include "nsReadableUtils.h"
52 : #include "nsUnicharUtils.h"
53 : #include "nsHTMLCSSUtils.h"
54 : #include "nsColor.h"
55 : #include "nsAttrName.h"
56 : #include "nsAutoPtr.h"
57 : #include "mozilla/Preferences.h"
58 :
59 : using namespace mozilla;
60 :
61 : static
62 0 : void ProcessBValue(const nsAString * aInputString, nsAString & aOutputString,
63 : const char * aDefaultValueString,
64 : const char * aPrependString, const char* aAppendString)
65 : {
66 0 : if (aInputString && aInputString->EqualsLiteral("-moz-editor-invert-value")) {
67 0 : aOutputString.AssignLiteral("normal");
68 : }
69 : else {
70 0 : aOutputString.AssignLiteral("bold");
71 : }
72 0 : }
73 :
74 : static
75 0 : void ProcessDefaultValue(const nsAString * aInputString, nsAString & aOutputString,
76 : const char * aDefaultValueString,
77 : const char * aPrependString, const char* aAppendString)
78 : {
79 0 : CopyASCIItoUTF16(aDefaultValueString, aOutputString);
80 0 : }
81 :
82 : static
83 0 : void ProcessSameValue(const nsAString * aInputString, nsAString & aOutputString,
84 : const char * aDefaultValueString,
85 : const char * aPrependString, const char* aAppendString)
86 : {
87 0 : if (aInputString) {
88 0 : aOutputString.Assign(*aInputString);
89 : }
90 : else
91 0 : aOutputString.Truncate();
92 0 : }
93 :
94 : static
95 0 : void ProcessExtendedValue(const nsAString * aInputString, nsAString & aOutputString,
96 : const char * aDefaultValueString,
97 : const char * aPrependString, const char* aAppendString)
98 : {
99 0 : aOutputString.Truncate();
100 0 : if (aInputString) {
101 0 : if (aPrependString) {
102 0 : AppendASCIItoUTF16(aPrependString, aOutputString);
103 : }
104 0 : aOutputString.Append(*aInputString);
105 0 : if (aAppendString) {
106 0 : AppendASCIItoUTF16(aAppendString, aOutputString);
107 : }
108 : }
109 0 : }
110 :
111 : static
112 0 : void ProcessLengthValue(const nsAString * aInputString, nsAString & aOutputString,
113 : const char * aDefaultValueString,
114 : const char * aPrependString, const char* aAppendString)
115 : {
116 0 : aOutputString.Truncate();
117 0 : if (aInputString) {
118 0 : aOutputString.Append(*aInputString);
119 0 : if (-1 == aOutputString.FindChar(PRUnichar('%'))) {
120 0 : aOutputString.AppendLiteral("px");
121 : }
122 : }
123 0 : }
124 :
125 : static
126 0 : void ProcessListStyleTypeValue(const nsAString * aInputString, nsAString & aOutputString,
127 : const char * aDefaultValueString,
128 : const char * aPrependString, const char* aAppendString)
129 : {
130 0 : aOutputString.Truncate();
131 0 : if (aInputString) {
132 0 : if (aInputString->EqualsLiteral("1")) {
133 0 : aOutputString.AppendLiteral("decimal");
134 : }
135 0 : else if (aInputString->EqualsLiteral("a")) {
136 0 : aOutputString.AppendLiteral("lower-alpha");
137 : }
138 0 : else if (aInputString->EqualsLiteral("A")) {
139 0 : aOutputString.AppendLiteral("upper-alpha");
140 : }
141 0 : else if (aInputString->EqualsLiteral("i")) {
142 0 : aOutputString.AppendLiteral("lower-roman");
143 : }
144 0 : else if (aInputString->EqualsLiteral("I")) {
145 0 : aOutputString.AppendLiteral("upper-roman");
146 : }
147 0 : else if (aInputString->EqualsLiteral("square")
148 0 : || aInputString->EqualsLiteral("circle")
149 0 : || aInputString->EqualsLiteral("disc")) {
150 0 : aOutputString.Append(*aInputString);
151 : }
152 : }
153 0 : }
154 :
155 : static
156 0 : void ProcessMarginLeftValue(const nsAString * aInputString, nsAString & aOutputString,
157 : const char * aDefaultValueString,
158 : const char * aPrependString, const char* aAppendString)
159 : {
160 0 : aOutputString.Truncate();
161 0 : if (aInputString) {
162 0 : if (aInputString->EqualsLiteral("center") ||
163 0 : aInputString->EqualsLiteral("-moz-center")) {
164 0 : aOutputString.AppendLiteral("auto");
165 : }
166 0 : else if (aInputString->EqualsLiteral("right") ||
167 0 : aInputString->EqualsLiteral("-moz-right")) {
168 0 : aOutputString.AppendLiteral("auto");
169 : }
170 : else {
171 0 : aOutputString.AppendLiteral("0px");
172 : }
173 : }
174 0 : }
175 :
176 : static
177 0 : void ProcessMarginRightValue(const nsAString * aInputString, nsAString & aOutputString,
178 : const char * aDefaultValueString,
179 : const char * aPrependString, const char* aAppendString)
180 : {
181 0 : aOutputString.Truncate();
182 0 : if (aInputString) {
183 0 : if (aInputString->EqualsLiteral("center") ||
184 0 : aInputString->EqualsLiteral("-moz-center")) {
185 0 : aOutputString.AppendLiteral("auto");
186 : }
187 0 : else if (aInputString->EqualsLiteral("left") ||
188 0 : aInputString->EqualsLiteral("-moz-left")) {
189 0 : aOutputString.AppendLiteral("auto");
190 : }
191 : else {
192 0 : aOutputString.AppendLiteral("0px");
193 : }
194 : }
195 0 : }
196 :
197 : const nsHTMLCSSUtils::CSSEquivTable boldEquivTable[] = {
198 : { nsHTMLCSSUtils::eCSSEditableProperty_font_weight, ProcessBValue, nsnull, nsnull, nsnull, true, false },
199 : { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
200 : };
201 :
202 : const nsHTMLCSSUtils::CSSEquivTable italicEquivTable[] = {
203 : { nsHTMLCSSUtils::eCSSEditableProperty_font_style, ProcessDefaultValue, "italic", nsnull, nsnull, true, false },
204 : { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
205 : };
206 :
207 : const nsHTMLCSSUtils::CSSEquivTable underlineEquivTable[] = {
208 : { nsHTMLCSSUtils::eCSSEditableProperty_text_decoration, ProcessDefaultValue, "underline", nsnull, nsnull, true, false },
209 : { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
210 : };
211 :
212 : const nsHTMLCSSUtils::CSSEquivTable strikeEquivTable[] = {
213 : { nsHTMLCSSUtils::eCSSEditableProperty_text_decoration, ProcessDefaultValue, "line-through", nsnull, nsnull, true, false },
214 : { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
215 : };
216 :
217 : const nsHTMLCSSUtils::CSSEquivTable ttEquivTable[] = {
218 : { nsHTMLCSSUtils::eCSSEditableProperty_font_family, ProcessDefaultValue, "monospace", nsnull, nsnull, true, false },
219 : { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
220 : };
221 :
222 : const nsHTMLCSSUtils::CSSEquivTable fontColorEquivTable[] = {
223 : { nsHTMLCSSUtils::eCSSEditableProperty_color, ProcessSameValue, nsnull, nsnull, nsnull, true, false },
224 : { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
225 : };
226 :
227 : const nsHTMLCSSUtils::CSSEquivTable fontFaceEquivTable[] = {
228 : { nsHTMLCSSUtils::eCSSEditableProperty_font_family, ProcessSameValue, nsnull, nsnull, nsnull, true, false },
229 : { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
230 : };
231 :
232 : const nsHTMLCSSUtils::CSSEquivTable bgcolorEquivTable[] = {
233 : { nsHTMLCSSUtils::eCSSEditableProperty_background_color, ProcessSameValue, nsnull, nsnull, nsnull, true, false },
234 : { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
235 : };
236 :
237 : const nsHTMLCSSUtils::CSSEquivTable backgroundImageEquivTable[] = {
238 : { nsHTMLCSSUtils::eCSSEditableProperty_background_image, ProcessExtendedValue, nsnull, "url(", ")", true, true },
239 : { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
240 : };
241 :
242 : const nsHTMLCSSUtils::CSSEquivTable textColorEquivTable[] = {
243 : { nsHTMLCSSUtils::eCSSEditableProperty_color, ProcessSameValue, nsnull, nsnull, nsnull, true, false },
244 : { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
245 : };
246 :
247 : const nsHTMLCSSUtils::CSSEquivTable borderEquivTable[] = {
248 : { nsHTMLCSSUtils::eCSSEditableProperty_border, ProcessExtendedValue, nsnull, nsnull, "px solid", true, false },
249 : { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
250 : };
251 :
252 : const nsHTMLCSSUtils::CSSEquivTable textAlignEquivTable[] = {
253 : { nsHTMLCSSUtils::eCSSEditableProperty_text_align, ProcessSameValue, nsnull, nsnull, nsnull, true, false },
254 : { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
255 : };
256 :
257 : const nsHTMLCSSUtils::CSSEquivTable captionAlignEquivTable[] = {
258 : { nsHTMLCSSUtils::eCSSEditableProperty_caption_side, ProcessSameValue, nsnull, nsnull, nsnull, true, false },
259 : { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
260 : };
261 :
262 : const nsHTMLCSSUtils::CSSEquivTable verticalAlignEquivTable[] = {
263 : { nsHTMLCSSUtils::eCSSEditableProperty_vertical_align, ProcessSameValue, nsnull, nsnull, nsnull, true, false },
264 : { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
265 : };
266 :
267 : const nsHTMLCSSUtils::CSSEquivTable nowrapEquivTable[] = {
268 : { nsHTMLCSSUtils::eCSSEditableProperty_whitespace, ProcessDefaultValue, "nowrap", nsnull, nsnull, true, false },
269 : { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
270 : };
271 :
272 : const nsHTMLCSSUtils::CSSEquivTable widthEquivTable[] = {
273 : { nsHTMLCSSUtils::eCSSEditableProperty_width, ProcessLengthValue, nsnull, nsnull, nsnull, true, false },
274 : { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
275 : };
276 :
277 : const nsHTMLCSSUtils::CSSEquivTable heightEquivTable[] = {
278 : { nsHTMLCSSUtils::eCSSEditableProperty_height, ProcessLengthValue, nsnull, nsnull, nsnull, true, false },
279 : { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
280 : };
281 :
282 : const nsHTMLCSSUtils::CSSEquivTable listStyleTypeEquivTable[] = {
283 : { nsHTMLCSSUtils::eCSSEditableProperty_list_style_type, ProcessListStyleTypeValue, nsnull, nsnull, nsnull, true, true },
284 : { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
285 : };
286 :
287 : const nsHTMLCSSUtils::CSSEquivTable tableAlignEquivTable[] = {
288 : { nsHTMLCSSUtils::eCSSEditableProperty_text_align, ProcessDefaultValue, "left", nsnull, nsnull, false, false },
289 : { nsHTMLCSSUtils::eCSSEditableProperty_margin_left, ProcessMarginLeftValue, nsnull, nsnull, nsnull, true, false },
290 : { nsHTMLCSSUtils::eCSSEditableProperty_margin_right, ProcessMarginRightValue, nsnull, nsnull, nsnull, true, false },
291 : { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
292 : };
293 :
294 : const nsHTMLCSSUtils::CSSEquivTable hrAlignEquivTable[] = {
295 : { nsHTMLCSSUtils::eCSSEditableProperty_margin_left, ProcessMarginLeftValue, nsnull, nsnull, nsnull, true, false },
296 : { nsHTMLCSSUtils::eCSSEditableProperty_margin_right, ProcessMarginRightValue, nsnull, nsnull, nsnull, true, false },
297 : { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
298 : };
299 :
300 0 : nsHTMLCSSUtils::nsHTMLCSSUtils(nsHTMLEditor* aEditor)
301 : : mHTMLEditor(aEditor)
302 0 : , mIsCSSPrefChecked(false)
303 : {
304 : // let's retrieve the value of the "CSS editing" pref
305 0 : mIsCSSPrefChecked = Preferences::GetBool("editor.use_css", mIsCSSPrefChecked);
306 0 : }
307 :
308 0 : nsHTMLCSSUtils::~nsHTMLCSSUtils()
309 : {
310 0 : }
311 :
312 : // Answers true if we have some CSS equivalence for the HTML style defined
313 : // by aProperty and/or aAttribute for the node aNode
314 : bool
315 0 : nsHTMLCSSUtils::IsCSSEditableProperty(nsIDOMNode * aNode,
316 : nsIAtom * aProperty,
317 : const nsAString * aAttribute)
318 : {
319 0 : NS_ASSERTION(aNode, "Shouldn't you pass aNode? - Bug 214025");
320 :
321 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
322 0 : NS_ENSURE_TRUE(content, false);
323 0 : return IsCSSEditableProperty(content, aProperty, aAttribute);
324 : }
325 :
326 : bool
327 0 : nsHTMLCSSUtils::IsCSSEditableProperty(nsIContent* aNode,
328 : nsIAtom* aProperty,
329 : const nsAString* aAttribute)
330 : {
331 0 : MOZ_ASSERT(aNode);
332 :
333 0 : nsIContent* content = aNode;
334 : // we need an element node here
335 0 : if (content->NodeType() == nsIDOMNode::TEXT_NODE) {
336 0 : content = content->GetParent();
337 0 : NS_ENSURE_TRUE(content, false);
338 : }
339 :
340 0 : nsIAtom *tagName = content->Tag();
341 : // brade: shouldn't some of the above go below the next block?
342 :
343 : // html inline styles B I TT U STRIKE and COLOR/FACE on FONT
344 0 : if (nsEditProperty::b == aProperty
345 : || nsEditProperty::i == aProperty
346 : || nsEditProperty::tt == aProperty
347 : || nsEditProperty::u == aProperty
348 : || nsEditProperty::strike == aProperty
349 : || ((nsEditProperty::font == aProperty) && aAttribute &&
350 0 : (aAttribute->EqualsLiteral("color") ||
351 0 : aAttribute->EqualsLiteral("face")))) {
352 0 : return true;
353 : }
354 :
355 : // ALIGN attribute on elements supporting it
356 0 : if (aAttribute && (aAttribute->EqualsLiteral("align")) &&
357 : (nsEditProperty::div == tagName
358 : || nsEditProperty::p == tagName
359 : || nsEditProperty::h1 == tagName
360 : || nsEditProperty::h2 == tagName
361 : || nsEditProperty::h3 == tagName
362 : || nsEditProperty::h4 == tagName
363 : || nsEditProperty::h5 == tagName
364 : || nsEditProperty::h6 == tagName
365 : || nsEditProperty::td == tagName
366 : || nsEditProperty::th == tagName
367 : || nsEditProperty::table == tagName
368 : || nsEditProperty::hr == tagName
369 : // brade: for the above, why not use nsHTMLEditUtils::SupportsAlignAttr
370 : // brade: but it also checks for tbody, tfoot, thead
371 : // Let's add the following elements here even if ALIGN has not
372 : // the same meaning for them
373 : || nsEditProperty::legend == tagName
374 : || nsEditProperty::caption == tagName)) {
375 0 : return true;
376 : }
377 :
378 0 : if (aAttribute && (aAttribute->EqualsLiteral("valign")) &&
379 : (nsEditProperty::col == tagName
380 : || nsEditProperty::colgroup == tagName
381 : || nsEditProperty::tbody == tagName
382 : || nsEditProperty::td == tagName
383 : || nsEditProperty::th == tagName
384 : || nsEditProperty::tfoot == tagName
385 : || nsEditProperty::thead == tagName
386 : || nsEditProperty::tr == tagName)) {
387 0 : return true;
388 : }
389 :
390 : // attributes TEXT, BACKGROUND and BGCOLOR on BODY
391 0 : if (aAttribute && (nsEditProperty::body == tagName) &&
392 0 : (aAttribute->EqualsLiteral("text")
393 0 : || aAttribute->EqualsLiteral("background")
394 0 : || aAttribute->EqualsLiteral("bgcolor"))) {
395 0 : return true;
396 : }
397 :
398 : // attribute BGCOLOR on other elements
399 0 : if (aAttribute && aAttribute->EqualsLiteral("bgcolor")) {
400 0 : return true;
401 : }
402 :
403 : // attributes HEIGHT, WIDTH and NOWRAP on TD and TH
404 0 : if (aAttribute && ((nsEditProperty::td == tagName)
405 : || (nsEditProperty::th == tagName)) &&
406 0 : (aAttribute->EqualsLiteral("height")
407 0 : || aAttribute->EqualsLiteral("width")
408 0 : || aAttribute->EqualsLiteral("nowrap"))) {
409 0 : return true;
410 : }
411 :
412 : // attributes HEIGHT and WIDTH on TABLE
413 0 : if (aAttribute && (nsEditProperty::table == tagName) &&
414 0 : (aAttribute->EqualsLiteral("height")
415 0 : || aAttribute->EqualsLiteral("width"))) {
416 0 : return true;
417 : }
418 :
419 : // attributes SIZE and WIDTH on HR
420 0 : if (aAttribute && (nsEditProperty::hr == tagName) &&
421 0 : (aAttribute->EqualsLiteral("size")
422 0 : || aAttribute->EqualsLiteral("width"))) {
423 0 : return true;
424 : }
425 :
426 : // attribute TYPE on OL UL LI
427 0 : if (aAttribute && (nsEditProperty::ol == tagName
428 : || nsEditProperty::ul == tagName
429 : || nsEditProperty::li == tagName) &&
430 0 : aAttribute->EqualsLiteral("type")) {
431 0 : return true;
432 : }
433 :
434 0 : if (aAttribute && nsEditProperty::img == tagName &&
435 0 : (aAttribute->EqualsLiteral("border")
436 0 : || aAttribute->EqualsLiteral("width")
437 0 : || aAttribute->EqualsLiteral("height"))) {
438 0 : return true;
439 : }
440 :
441 : // other elements that we can align using CSS even if they
442 : // can't carry the html ALIGN attribute
443 0 : if (aAttribute && aAttribute->EqualsLiteral("align") &&
444 : (nsEditProperty::ul == tagName
445 : || nsEditProperty::ol == tagName
446 : || nsEditProperty::dl == tagName
447 : || nsEditProperty::li == tagName
448 : || nsEditProperty::dd == tagName
449 : || nsEditProperty::dt == tagName
450 : || nsEditProperty::address == tagName
451 : || nsEditProperty::pre == tagName
452 : || nsEditProperty::ul == tagName)) {
453 0 : return true;
454 : }
455 :
456 0 : return false;
457 : }
458 :
459 : // the lowest level above the transaction; adds the css declaration "aProperty : aValue" to
460 : // the inline styles carried by aElement
461 : nsresult
462 0 : nsHTMLCSSUtils::SetCSSProperty(nsIDOMElement *aElement, nsIAtom * aProperty, const nsAString & aValue,
463 : bool aSuppressTransaction)
464 : {
465 0 : nsRefPtr<ChangeCSSInlineStyleTxn> txn;
466 : nsresult result = CreateCSSPropertyTxn(aElement, aProperty, aValue,
467 0 : getter_AddRefs(txn), false);
468 0 : if (NS_SUCCEEDED(result)) {
469 0 : if (aSuppressTransaction) {
470 0 : result = txn->DoTransaction();
471 : }
472 : else {
473 0 : result = mHTMLEditor->DoTransaction(txn);
474 : }
475 : }
476 0 : return result;
477 : }
478 :
479 : nsresult
480 0 : nsHTMLCSSUtils::SetCSSPropertyPixels(nsIDOMElement *aElement,
481 : nsIAtom *aProperty,
482 : PRInt32 aIntValue,
483 : bool aSuppressTransaction)
484 : {
485 0 : nsAutoString s;
486 0 : s.AppendInt(aIntValue);
487 0 : return SetCSSProperty(aElement, aProperty, s + NS_LITERAL_STRING("px"),
488 0 : aSuppressTransaction);
489 : }
490 :
491 : // the lowest level above the transaction; removes the value aValue from the list of values
492 : // specified for the CSS property aProperty, or totally remove the declaration if this
493 : // property accepts only one value
494 : nsresult
495 0 : nsHTMLCSSUtils::RemoveCSSProperty(nsIDOMElement *aElement, nsIAtom * aProperty, const nsAString & aValue,
496 : bool aSuppressTransaction)
497 : {
498 0 : nsRefPtr<ChangeCSSInlineStyleTxn> txn;
499 : nsresult result = CreateCSSPropertyTxn(aElement, aProperty, aValue,
500 0 : getter_AddRefs(txn), true);
501 0 : if (NS_SUCCEEDED(result)) {
502 0 : if (aSuppressTransaction) {
503 0 : result = txn->DoTransaction();
504 : }
505 : else {
506 0 : result = mHTMLEditor->DoTransaction(txn);
507 : }
508 : }
509 0 : return result;
510 : }
511 :
512 : nsresult
513 0 : nsHTMLCSSUtils::CreateCSSPropertyTxn(nsIDOMElement *aElement,
514 : nsIAtom * aAttribute,
515 : const nsAString& aValue,
516 : ChangeCSSInlineStyleTxn ** aTxn,
517 : bool aRemoveProperty)
518 : {
519 0 : NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
520 :
521 0 : *aTxn = new ChangeCSSInlineStyleTxn();
522 0 : NS_ENSURE_TRUE(*aTxn, NS_ERROR_OUT_OF_MEMORY);
523 0 : NS_ADDREF(*aTxn);
524 0 : return (*aTxn)->Init(mHTMLEditor, aElement, aAttribute, aValue, aRemoveProperty);
525 : }
526 :
527 : nsresult
528 0 : nsHTMLCSSUtils::GetSpecifiedProperty(nsIDOMNode *aNode, nsIAtom *aProperty,
529 : nsAString & aValue)
530 : {
531 0 : return GetCSSInlinePropertyBase(aNode, aProperty, aValue, nsnull, SPECIFIED_STYLE_TYPE);
532 : }
533 :
534 : nsresult
535 0 : nsHTMLCSSUtils::GetComputedProperty(nsIDOMNode *aNode, nsIAtom *aProperty,
536 : nsAString & aValue)
537 : {
538 0 : nsCOMPtr<nsIDOMWindow> window;
539 0 : nsresult res = GetDefaultViewCSS(aNode, getter_AddRefs(window));
540 0 : NS_ENSURE_SUCCESS(res, res);
541 :
542 0 : return GetCSSInlinePropertyBase(aNode, aProperty, aValue, window, COMPUTED_STYLE_TYPE);
543 : }
544 :
545 : nsresult
546 0 : nsHTMLCSSUtils::GetCSSInlinePropertyBase(nsINode* aNode, nsIAtom* aProperty,
547 : nsAString& aValue,
548 : nsIDOMWindow* aWindow,
549 : PRUint8 aStyleType)
550 : {
551 0 : nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode);
552 0 : return GetCSSInlinePropertyBase(node, aProperty, aValue, aWindow, aStyleType);
553 : }
554 :
555 : nsresult
556 0 : nsHTMLCSSUtils::GetCSSInlinePropertyBase(nsIDOMNode *aNode, nsIAtom *aProperty,
557 : nsAString& aValue,
558 : nsIDOMWindow* aWindow,
559 : PRUint8 aStyleType)
560 : {
561 0 : aValue.Truncate();
562 0 : NS_ENSURE_TRUE(aProperty, NS_ERROR_NULL_POINTER);
563 :
564 0 : nsCOMPtr<nsIDOMElement> element = GetElementContainerOrSelf(aNode);
565 0 : NS_ENSURE_TRUE(element, NS_ERROR_NULL_POINTER);
566 :
567 0 : switch (aStyleType) {
568 : case COMPUTED_STYLE_TYPE:
569 0 : if (element && aWindow) {
570 0 : nsAutoString value, propString;
571 0 : nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
572 0 : aProperty->ToString(propString);
573 : // Get the all the computed css styles attached to the element node
574 0 : nsresult res = aWindow->GetComputedStyle(element, EmptyString(), getter_AddRefs(cssDecl));
575 0 : if (NS_FAILED(res) || !cssDecl)
576 0 : return res;
577 : // from these declarations, get the one we want and that one only
578 0 : res = cssDecl->GetPropertyValue(propString, value);
579 0 : NS_ENSURE_SUCCESS(res, res);
580 0 : aValue.Assign(value);
581 : }
582 0 : break;
583 : case SPECIFIED_STYLE_TYPE:
584 0 : if (element) {
585 0 : nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
586 : PRUint32 length;
587 0 : nsresult res = GetInlineStyles(element, getter_AddRefs(cssDecl), &length);
588 0 : if (NS_FAILED(res) || !cssDecl) return res;
589 0 : nsAutoString value, propString;
590 0 : aProperty->ToString(propString);
591 0 : res = cssDecl->GetPropertyValue(propString, value);
592 0 : NS_ENSURE_SUCCESS(res, res);
593 0 : aValue.Assign(value);
594 : }
595 0 : break;
596 : }
597 0 : return NS_OK;
598 : }
599 :
600 : nsresult
601 0 : nsHTMLCSSUtils::GetDefaultViewCSS(nsIDOMNode *aNode, nsIDOMWindow **aViewCSS)
602 : {
603 0 : nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
604 0 : NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
605 0 : return GetDefaultViewCSS(node, aViewCSS);
606 : }
607 :
608 : nsresult
609 0 : nsHTMLCSSUtils::GetDefaultViewCSS(nsINode* aNode, nsIDOMWindow** aViewCSS)
610 : {
611 0 : MOZ_ASSERT(aNode);
612 0 : dom::Element* element = GetElementContainerOrSelf(aNode);
613 0 : NS_ENSURE_TRUE(element, NS_ERROR_NULL_POINTER);
614 :
615 0 : nsCOMPtr<nsIDOMWindow> window = element->OwnerDoc()->GetWindow();
616 0 : NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
617 0 : window.forget(aViewCSS);
618 0 : return NS_OK;
619 : }
620 :
621 : // remove the CSS style "aProperty : aPropertyValue" and possibly remove the whole node
622 : // if it is a span and if its only attribute is _moz_dirty
623 : nsresult
624 0 : nsHTMLCSSUtils::RemoveCSSInlineStyle(nsIDOMNode *aNode, nsIAtom *aProperty, const nsAString & aPropertyValue)
625 : {
626 0 : nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(aNode);
627 :
628 : // remove the property from the style attribute
629 0 : nsresult res = RemoveCSSProperty(elem, aProperty, aPropertyValue, false);
630 0 : NS_ENSURE_SUCCESS(res, res);
631 :
632 0 : nsCOMPtr<dom::Element> element = do_QueryInterface(aNode);
633 0 : if (!element || !element->IsHTML(nsGkAtoms::span) ||
634 0 : nsHTMLEditor::HasAttributes(element)) {
635 0 : return NS_OK;
636 : }
637 :
638 0 : return mHTMLEditor->RemoveContainer(aNode);
639 : }
640 :
641 : // Answers true is the property can be removed by setting a "none" CSS value
642 : // on a node
643 : bool
644 0 : nsHTMLCSSUtils::IsCSSInvertable(nsIAtom *aProperty, const nsAString *aAttribute)
645 : {
646 0 : return bool(nsEditProperty::b == aProperty);
647 : }
648 :
649 : // Get the default browser background color if we need it for GetCSSBackgroundColorState
650 : void
651 0 : nsHTMLCSSUtils::GetDefaultBackgroundColor(nsAString & aColor)
652 : {
653 0 : if (Preferences::GetBool("editor.use_custom_colors", false)) {
654 0 : nsresult rv = Preferences::GetString("editor.background_color", &aColor);
655 : // XXX Why don't you validate the pref value?
656 0 : if (NS_FAILED(rv)) {
657 0 : NS_WARNING("failed to get editor.background_color");
658 0 : aColor.AssignLiteral("#ffffff"); // Default to white
659 : }
660 0 : return;
661 : }
662 :
663 0 : if (Preferences::GetBool("browser.display.use_system_colors", false)) {
664 0 : return;
665 : }
666 :
667 : nsresult rv =
668 0 : Preferences::GetString("browser.display.background_color", &aColor);
669 : // XXX Why don't you validate the pref value?
670 0 : if (NS_FAILED(rv)) {
671 0 : NS_WARNING("failed to get browser.display.background_color");
672 0 : aColor.AssignLiteral("#ffffff"); // Default to white
673 : }
674 : }
675 :
676 : // Get the default length unit used for CSS Indent/Outdent
677 : void
678 0 : nsHTMLCSSUtils::GetDefaultLengthUnit(nsAString & aLengthUnit)
679 : {
680 : nsresult rv =
681 0 : Preferences::GetString("editor.css.default_length_unit", &aLengthUnit);
682 : // XXX Why don't you validate the pref value?
683 0 : if (NS_FAILED(rv)) {
684 0 : aLengthUnit.AssignLiteral("px");
685 : }
686 0 : }
687 :
688 : // Unfortunately, CSSStyleDeclaration::GetPropertyCSSValue is not yet implemented...
689 : // We need then a way to determine the number part and the unit from aString, aString
690 : // being the result of a GetPropertyValue query...
691 : void
692 0 : nsHTMLCSSUtils::ParseLength(const nsAString & aString, float * aValue, nsIAtom ** aUnit)
693 : {
694 0 : nsAString::const_iterator iter;
695 0 : aString.BeginReading(iter);
696 :
697 0 : float a = 10.0f , b = 1.0f, value = 0;
698 0 : PRInt8 sign = 1;
699 0 : PRInt32 i = 0, j = aString.Length();
700 : PRUnichar c;
701 0 : bool floatingPointFound = false;
702 0 : c = *iter;
703 0 : if (PRUnichar('-') == c) { sign = -1; iter++; i++; }
704 0 : else if (PRUnichar('+') == c) { iter++; i++; }
705 0 : while (i < j) {
706 0 : c = *iter;
707 0 : if ((PRUnichar('0') == c) ||
708 : (PRUnichar('1') == c) ||
709 : (PRUnichar('2') == c) ||
710 : (PRUnichar('3') == c) ||
711 : (PRUnichar('4') == c) ||
712 : (PRUnichar('5') == c) ||
713 : (PRUnichar('6') == c) ||
714 : (PRUnichar('7') == c) ||
715 : (PRUnichar('8') == c) ||
716 : (PRUnichar('9') == c)) {
717 0 : value = (value * a) + (b * (c - PRUnichar('0')));
718 0 : b = b / 10 * a;
719 : }
720 0 : else if (!floatingPointFound && (PRUnichar('.') == c)) {
721 0 : floatingPointFound = true;
722 0 : a = 1.0f; b = 0.1f;
723 : }
724 0 : else break;
725 0 : iter++;
726 0 : i++;
727 : }
728 0 : *aValue = value * sign;
729 0 : *aUnit = NS_NewAtom(StringTail(aString, j-i));
730 0 : }
731 :
732 : void
733 0 : nsHTMLCSSUtils::GetCSSPropertyAtom(nsCSSEditableProperty aProperty, nsIAtom ** aAtom)
734 : {
735 0 : *aAtom = nsnull;
736 0 : switch (aProperty) {
737 : case eCSSEditableProperty_background_color:
738 0 : *aAtom = nsEditProperty::cssBackgroundColor;
739 0 : break;
740 : case eCSSEditableProperty_background_image:
741 0 : *aAtom = nsEditProperty::cssBackgroundImage;
742 0 : break;
743 : case eCSSEditableProperty_border:
744 0 : *aAtom = nsEditProperty::cssBorder;
745 0 : break;
746 : case eCSSEditableProperty_caption_side:
747 0 : *aAtom = nsEditProperty::cssCaptionSide;
748 0 : break;
749 : case eCSSEditableProperty_color:
750 0 : *aAtom = nsEditProperty::cssColor;
751 0 : break;
752 : case eCSSEditableProperty_float:
753 0 : *aAtom = nsEditProperty::cssFloat;
754 0 : break;
755 : case eCSSEditableProperty_font_family:
756 0 : *aAtom = nsEditProperty::cssFontFamily;
757 0 : break;
758 : case eCSSEditableProperty_font_size:
759 0 : *aAtom = nsEditProperty::cssFontSize;
760 0 : break;
761 : case eCSSEditableProperty_font_style:
762 0 : *aAtom = nsEditProperty::cssFontStyle;
763 0 : break;
764 : case eCSSEditableProperty_font_weight:
765 0 : *aAtom = nsEditProperty::cssFontWeight;
766 0 : break;
767 : case eCSSEditableProperty_height:
768 0 : *aAtom = nsEditProperty::cssHeight;
769 0 : break;
770 : case eCSSEditableProperty_list_style_type:
771 0 : *aAtom = nsEditProperty::cssListStyleType;
772 0 : break;
773 : case eCSSEditableProperty_margin_left:
774 0 : *aAtom = nsEditProperty::cssMarginLeft;
775 0 : break;
776 : case eCSSEditableProperty_margin_right:
777 0 : *aAtom = nsEditProperty::cssMarginRight;
778 0 : break;
779 : case eCSSEditableProperty_text_align:
780 0 : *aAtom = nsEditProperty::cssTextAlign;
781 0 : break;
782 : case eCSSEditableProperty_text_decoration:
783 0 : *aAtom = nsEditProperty::cssTextDecoration;
784 0 : break;
785 : case eCSSEditableProperty_vertical_align:
786 0 : *aAtom = nsEditProperty::cssVerticalAlign;
787 0 : break;
788 : case eCSSEditableProperty_whitespace:
789 0 : *aAtom = nsEditProperty::cssWhitespace;
790 0 : break;
791 : case eCSSEditableProperty_width:
792 0 : *aAtom = nsEditProperty::cssWidth;
793 0 : break;
794 : case eCSSEditableProperty_NONE:
795 : // intentionally empty
796 0 : break;
797 : }
798 0 : }
799 :
800 : // Populate aProperty and aValueArray with the CSS declarations equivalent to the
801 : // value aValue according to the equivalence table aEquivTable
802 : void
803 0 : nsHTMLCSSUtils::BuildCSSDeclarations(nsTArray<nsIAtom*> & aPropertyArray,
804 : nsTArray<nsString> & aValueArray,
805 : const CSSEquivTable * aEquivTable,
806 : const nsAString * aValue,
807 : bool aGetOrRemoveRequest)
808 : {
809 : // clear arrays
810 0 : aPropertyArray.Clear();
811 0 : aValueArray.Clear();
812 :
813 : // if we have an input value, let's use it
814 0 : nsAutoString value, lowerCasedValue;
815 0 : if (aValue) {
816 0 : value.Assign(*aValue);
817 0 : lowerCasedValue.Assign(*aValue);
818 0 : ToLowerCase(lowerCasedValue);
819 : }
820 :
821 0 : PRInt8 index = 0;
822 0 : nsCSSEditableProperty cssProperty = aEquivTable[index].cssProperty;
823 0 : while (cssProperty) {
824 0 : if (!aGetOrRemoveRequest|| aEquivTable[index].gettable) {
825 0 : nsAutoString cssValue, cssPropertyString;
826 : nsIAtom * cssPropertyAtom;
827 : // find the equivalent css value for the index-th property in
828 : // the equivalence table
829 0 : (*aEquivTable[index].processValueFunctor) ((!aGetOrRemoveRequest || aEquivTable[index].caseSensitiveValue) ? &value : &lowerCasedValue,
830 : cssValue,
831 0 : aEquivTable[index].defaultValue,
832 0 : aEquivTable[index].prependValue,
833 0 : aEquivTable[index].appendValue);
834 0 : GetCSSPropertyAtom(cssProperty, &cssPropertyAtom);
835 0 : aPropertyArray.AppendElement(cssPropertyAtom);
836 0 : aValueArray.AppendElement(cssValue);
837 : }
838 0 : index++;
839 0 : cssProperty = aEquivTable[index].cssProperty;
840 : }
841 0 : }
842 :
843 : // Populate cssPropertyArray and cssValueArray with the declarations equivalent
844 : // to aHTMLProperty/aAttribute/aValue for the node aNode
845 : void
846 0 : nsHTMLCSSUtils::GenerateCSSDeclarationsFromHTMLStyle(dom::Element* aElement,
847 : nsIAtom* aHTMLProperty,
848 : const nsAString* aAttribute,
849 : const nsAString* aValue,
850 : nsTArray<nsIAtom*>& cssPropertyArray,
851 : nsTArray<nsString>& cssValueArray,
852 : bool aGetOrRemoveRequest)
853 : {
854 0 : MOZ_ASSERT(aElement);
855 0 : nsIAtom* tagName = aElement->Tag();
856 :
857 0 : if (nsEditProperty::b == aHTMLProperty) {
858 0 : BuildCSSDeclarations(cssPropertyArray, cssValueArray, boldEquivTable, aValue, aGetOrRemoveRequest);
859 : }
860 0 : else if (nsEditProperty::i == aHTMLProperty) {
861 0 : BuildCSSDeclarations(cssPropertyArray, cssValueArray, italicEquivTable, aValue, aGetOrRemoveRequest);
862 : }
863 0 : else if (nsEditProperty::u == aHTMLProperty) {
864 0 : BuildCSSDeclarations(cssPropertyArray, cssValueArray, underlineEquivTable, aValue, aGetOrRemoveRequest);
865 : }
866 0 : else if (nsEditProperty::strike == aHTMLProperty) {
867 0 : BuildCSSDeclarations(cssPropertyArray, cssValueArray, strikeEquivTable, aValue, aGetOrRemoveRequest);
868 : }
869 0 : else if (nsEditProperty::tt == aHTMLProperty) {
870 0 : BuildCSSDeclarations(cssPropertyArray, cssValueArray, ttEquivTable, aValue, aGetOrRemoveRequest);
871 : }
872 0 : else if (aAttribute) {
873 0 : if (nsEditProperty::font == aHTMLProperty &&
874 0 : aAttribute->EqualsLiteral("color")) {
875 0 : BuildCSSDeclarations(cssPropertyArray, cssValueArray, fontColorEquivTable, aValue, aGetOrRemoveRequest);
876 : }
877 0 : else if (nsEditProperty::font == aHTMLProperty &&
878 0 : aAttribute->EqualsLiteral("face")) {
879 0 : BuildCSSDeclarations(cssPropertyArray, cssValueArray, fontFaceEquivTable, aValue, aGetOrRemoveRequest);
880 : }
881 0 : else if (aAttribute->EqualsLiteral("bgcolor")) {
882 0 : BuildCSSDeclarations(cssPropertyArray, cssValueArray, bgcolorEquivTable, aValue, aGetOrRemoveRequest);
883 : }
884 0 : else if (aAttribute->EqualsLiteral("background")) {
885 0 : BuildCSSDeclarations(cssPropertyArray, cssValueArray, backgroundImageEquivTable, aValue, aGetOrRemoveRequest);
886 : }
887 0 : else if (aAttribute->EqualsLiteral("text")) {
888 0 : BuildCSSDeclarations(cssPropertyArray, cssValueArray, textColorEquivTable, aValue, aGetOrRemoveRequest);
889 : }
890 0 : else if (aAttribute->EqualsLiteral("border")) {
891 0 : BuildCSSDeclarations(cssPropertyArray, cssValueArray, borderEquivTable, aValue, aGetOrRemoveRequest);
892 : }
893 0 : else if (aAttribute->EqualsLiteral("align")) {
894 0 : if (nsEditProperty::table == tagName) {
895 0 : BuildCSSDeclarations(cssPropertyArray, cssValueArray, tableAlignEquivTable, aValue, aGetOrRemoveRequest);
896 : }
897 0 : else if (nsEditProperty::hr == tagName) {
898 0 : BuildCSSDeclarations(cssPropertyArray, cssValueArray, hrAlignEquivTable, aValue, aGetOrRemoveRequest);
899 : }
900 0 : else if (nsEditProperty::legend == tagName ||
901 : nsEditProperty::caption == tagName) {
902 0 : BuildCSSDeclarations(cssPropertyArray, cssValueArray, captionAlignEquivTable, aValue, aGetOrRemoveRequest);
903 : }
904 : else {
905 0 : BuildCSSDeclarations(cssPropertyArray, cssValueArray, textAlignEquivTable, aValue, aGetOrRemoveRequest);
906 : }
907 : }
908 0 : else if (aAttribute->EqualsLiteral("valign")) {
909 0 : BuildCSSDeclarations(cssPropertyArray, cssValueArray, verticalAlignEquivTable, aValue, aGetOrRemoveRequest);
910 : }
911 0 : else if (aAttribute->EqualsLiteral("nowrap")) {
912 0 : BuildCSSDeclarations(cssPropertyArray, cssValueArray, nowrapEquivTable, aValue, aGetOrRemoveRequest);
913 : }
914 0 : else if (aAttribute->EqualsLiteral("width")) {
915 0 : BuildCSSDeclarations(cssPropertyArray, cssValueArray, widthEquivTable, aValue, aGetOrRemoveRequest);
916 : }
917 0 : else if (aAttribute->EqualsLiteral("height") ||
918 0 : (nsEditProperty::hr == tagName && aAttribute->EqualsLiteral("size"))) {
919 0 : BuildCSSDeclarations(cssPropertyArray, cssValueArray, heightEquivTable, aValue, aGetOrRemoveRequest);
920 : }
921 0 : else if (aAttribute->EqualsLiteral("type") &&
922 : (nsEditProperty::ol == tagName
923 : || nsEditProperty::ul == tagName
924 : || nsEditProperty::li == tagName)) {
925 0 : BuildCSSDeclarations(cssPropertyArray, cssValueArray, listStyleTypeEquivTable, aValue, aGetOrRemoveRequest);
926 : }
927 : }
928 0 : }
929 :
930 : // Add to aNode the CSS inline style equivalent to HTMLProperty/aAttribute/aValue for the node,
931 : // and return in aCount the number of CSS properties set by the call
932 : nsresult
933 0 : nsHTMLCSSUtils::SetCSSEquivalentToHTMLStyle(nsIDOMNode * aNode,
934 : nsIAtom *aHTMLProperty,
935 : const nsAString *aAttribute,
936 : const nsAString *aValue,
937 : PRInt32 * aCount,
938 : bool aSuppressTransaction)
939 : {
940 0 : nsCOMPtr<dom::Element> element = do_QueryInterface(aNode);
941 0 : *aCount = 0;
942 0 : if (!element || !IsCSSEditableProperty(element, aHTMLProperty, aAttribute)) {
943 0 : return NS_OK;
944 : }
945 :
946 : // we can apply the styles only if the node is an element and if we have
947 : // an equivalence for the requested HTML style in this implementation
948 :
949 : // Find the CSS equivalence to the HTML style
950 0 : nsTArray<nsIAtom*> cssPropertyArray;
951 0 : nsTArray<nsString> cssValueArray;
952 : GenerateCSSDeclarationsFromHTMLStyle(element, aHTMLProperty, aAttribute,
953 : aValue, cssPropertyArray, cssValueArray,
954 0 : false);
955 :
956 0 : nsCOMPtr<nsIDOMElement> domElement = do_QueryInterface(element);
957 : // set the individual CSS inline styles
958 0 : *aCount = cssPropertyArray.Length();
959 0 : for (PRInt32 index = 0; index < *aCount; index++) {
960 0 : nsresult res = SetCSSProperty(domElement, cssPropertyArray[index],
961 0 : cssValueArray[index], aSuppressTransaction);
962 0 : NS_ENSURE_SUCCESS(res, res);
963 : }
964 0 : return NS_OK;
965 : }
966 :
967 : // Remove from aNode the CSS inline style equivalent to HTMLProperty/aAttribute/aValue for the node
968 : nsresult
969 0 : nsHTMLCSSUtils::RemoveCSSEquivalentToHTMLStyle(nsIDOMNode * aNode,
970 : nsIAtom *aHTMLProperty,
971 : const nsAString *aAttribute,
972 : const nsAString *aValue,
973 : bool aSuppressTransaction)
974 : {
975 0 : nsCOMPtr<dom::Element> element = do_QueryInterface(aNode);
976 0 : if (!element || !IsCSSEditableProperty(element, aHTMLProperty, aAttribute)) {
977 0 : return NS_OK;
978 : }
979 :
980 : // we can apply the styles only if the node is an element and if we have
981 : // an equivalence for the requested HTML style in this implementation
982 :
983 : // Find the CSS equivalence to the HTML style
984 0 : nsTArray<nsIAtom*> cssPropertyArray;
985 0 : nsTArray<nsString> cssValueArray;
986 : GenerateCSSDeclarationsFromHTMLStyle(element, aHTMLProperty, aAttribute,
987 : aValue, cssPropertyArray, cssValueArray,
988 0 : true);
989 :
990 0 : nsCOMPtr<nsIDOMElement> domElement = do_QueryInterface(element);
991 : // remove the individual CSS inline styles
992 0 : PRInt32 count = cssPropertyArray.Length();
993 0 : for (PRInt32 index = 0; index < count; index++) {
994 : nsresult res = RemoveCSSProperty(domElement,
995 0 : cssPropertyArray[index],
996 0 : cssValueArray[index],
997 0 : aSuppressTransaction);
998 0 : NS_ENSURE_SUCCESS(res, res);
999 : }
1000 0 : return NS_OK;
1001 : }
1002 :
1003 : // returns in aValueString the list of values for the CSS equivalences to
1004 : // the HTML style aHTMLProperty/aAttribute/aValueString for the node aNode;
1005 : // the value of aStyleType controls the styles we retrieve : specified or
1006 : // computed.
1007 : nsresult
1008 0 : nsHTMLCSSUtils::GetCSSEquivalentToHTMLInlineStyleSet(nsINode* aNode,
1009 : nsIAtom *aHTMLProperty,
1010 : const nsAString *aAttribute,
1011 : nsAString & aValueString,
1012 : PRUint8 aStyleType)
1013 : {
1014 0 : aValueString.Truncate();
1015 0 : nsCOMPtr<dom::Element> theElement = GetElementContainerOrSelf(aNode);
1016 0 : NS_ENSURE_TRUE(theElement, NS_ERROR_NULL_POINTER);
1017 :
1018 0 : if (!theElement || !IsCSSEditableProperty(theElement, aHTMLProperty, aAttribute)) {
1019 0 : return NS_OK;
1020 : }
1021 :
1022 : // Yes, the requested HTML style has a CSS equivalence in this implementation
1023 : // Retrieve the default ViewCSS if we are asked for computed styles
1024 0 : nsCOMPtr<nsIDOMWindow> window;
1025 0 : if (COMPUTED_STYLE_TYPE == aStyleType) {
1026 0 : nsresult res = GetDefaultViewCSS(theElement, getter_AddRefs(window));
1027 0 : NS_ENSURE_SUCCESS(res, res);
1028 : }
1029 0 : nsTArray<nsIAtom*> cssPropertyArray;
1030 0 : nsTArray<nsString> cssValueArray;
1031 : // get the CSS equivalence with last param true indicating we want only the
1032 : // "gettable" properties
1033 : GenerateCSSDeclarationsFromHTMLStyle(theElement, aHTMLProperty, aAttribute, nsnull,
1034 0 : cssPropertyArray, cssValueArray, true);
1035 0 : PRInt32 count = cssPropertyArray.Length();
1036 0 : for (PRInt32 index = 0; index < count; index++) {
1037 0 : nsAutoString valueString;
1038 : // retrieve the specified/computed value of the property
1039 0 : nsresult res = GetCSSInlinePropertyBase(theElement, cssPropertyArray[index],
1040 0 : valueString, window, aStyleType);
1041 0 : NS_ENSURE_SUCCESS(res, res);
1042 : // append the value to aValueString (possibly with a leading whitespace)
1043 0 : if (index) {
1044 0 : aValueString.Append(PRUnichar(' '));
1045 : }
1046 0 : aValueString.Append(valueString);
1047 : }
1048 0 : return NS_OK;
1049 : }
1050 :
1051 : // Does the node aNode (or his parent if it is not an element node) carries
1052 : // the CSS equivalent styles to the HTML style aHTMLProperty/aAttribute/
1053 : // aValueString for this node ?
1054 : // The value of aStyleType controls the styles we retrieve : specified or
1055 : // computed. The return value aIsSet is true is the CSS styles are set.
1056 : nsresult
1057 0 : nsHTMLCSSUtils::IsCSSEquivalentToHTMLInlineStyleSet(nsIDOMNode * aNode,
1058 : nsIAtom *aHTMLProperty,
1059 : const nsAString * aHTMLAttribute,
1060 : bool & aIsSet,
1061 : nsAString & valueString,
1062 : PRUint8 aStyleType)
1063 : {
1064 0 : NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
1065 :
1066 0 : nsAutoString htmlValueString(valueString);
1067 0 : aIsSet = false;
1068 0 : nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
1069 0 : NS_NAMED_LITERAL_STRING(boldStr, "bold");
1070 0 : do {
1071 0 : valueString.Assign(htmlValueString);
1072 : // get the value of the CSS equivalent styles
1073 : nsresult res = GetCSSEquivalentToHTMLInlineStyleSet(node, aHTMLProperty, aHTMLAttribute,
1074 0 : valueString, aStyleType);
1075 0 : NS_ENSURE_SUCCESS(res, res);
1076 :
1077 : // early way out if we can
1078 0 : if (valueString.IsEmpty()) return NS_OK;
1079 :
1080 0 : if (nsEditProperty::b == aHTMLProperty) {
1081 0 : if (valueString.Equals(boldStr)) {
1082 0 : aIsSet = true;
1083 : }
1084 0 : else if (valueString.EqualsLiteral("normal")) {
1085 0 : aIsSet = false;
1086 : }
1087 0 : else if (valueString.EqualsLiteral("bolder")) {
1088 0 : aIsSet = true;
1089 0 : valueString.Assign(boldStr);
1090 : }
1091 : else {
1092 0 : PRInt32 weight = 0;
1093 : PRInt32 errorCode;
1094 0 : nsAutoString value(valueString);
1095 0 : weight = value.ToInteger(&errorCode, 10);
1096 0 : if (400 < weight) {
1097 0 : aIsSet = true;
1098 0 : valueString.Assign(boldStr);
1099 : }
1100 : else {
1101 0 : aIsSet = false;
1102 0 : valueString.AssignLiteral("normal");
1103 : }
1104 : }
1105 : }
1106 :
1107 0 : else if (nsEditProperty::i == aHTMLProperty) {
1108 0 : if (valueString.EqualsLiteral("italic") ||
1109 0 : valueString.EqualsLiteral("oblique")) {
1110 0 : aIsSet= true;
1111 : }
1112 : }
1113 :
1114 0 : else if (nsEditProperty::u == aHTMLProperty) {
1115 0 : nsAutoString val;
1116 0 : val.AssignLiteral("underline");
1117 0 : aIsSet = bool(ChangeCSSInlineStyleTxn::ValueIncludes(valueString, val, false));
1118 : }
1119 :
1120 0 : else if (nsEditProperty::strike == aHTMLProperty) {
1121 0 : nsAutoString val;
1122 0 : val.AssignLiteral("line-through");
1123 0 : aIsSet = bool(ChangeCSSInlineStyleTxn::ValueIncludes(valueString, val, false));
1124 : }
1125 :
1126 0 : else if (aHTMLAttribute &&
1127 : ( (nsEditProperty::font == aHTMLProperty &&
1128 0 : aHTMLAttribute->EqualsLiteral("color")) ||
1129 0 : aHTMLAttribute->EqualsLiteral("bgcolor"))) {
1130 0 : if (htmlValueString.IsEmpty())
1131 0 : aIsSet = true;
1132 : else {
1133 : nscolor rgba;
1134 0 : nsAutoString subStr;
1135 0 : htmlValueString.Right(subStr, htmlValueString.Length()-1);
1136 0 : if (NS_ColorNameToRGB(htmlValueString, &rgba) ||
1137 0 : NS_HexToRGB(subStr, &rgba)) {
1138 0 : nsAutoString htmlColor, tmpStr;
1139 0 : htmlColor.AppendLiteral("rgb(");
1140 :
1141 0 : NS_NAMED_LITERAL_STRING(comma, ", ");
1142 :
1143 0 : tmpStr.AppendInt(NS_GET_R(rgba), 10);
1144 0 : htmlColor.Append(tmpStr + comma);
1145 :
1146 0 : tmpStr.Truncate();
1147 0 : tmpStr.AppendInt(NS_GET_G(rgba), 10);
1148 0 : htmlColor.Append(tmpStr + comma);
1149 :
1150 0 : tmpStr.Truncate();
1151 0 : tmpStr.AppendInt(NS_GET_B(rgba), 10);
1152 0 : htmlColor.Append(tmpStr);
1153 :
1154 0 : htmlColor.Append(PRUnichar(')'));
1155 : aIsSet = htmlColor.Equals(valueString,
1156 0 : nsCaseInsensitiveStringComparator());
1157 : }
1158 : else
1159 : aIsSet = htmlValueString.Equals(valueString,
1160 0 : nsCaseInsensitiveStringComparator());
1161 : }
1162 : }
1163 :
1164 0 : else if (nsEditProperty::tt == aHTMLProperty) {
1165 0 : aIsSet = StringBeginsWith(valueString, NS_LITERAL_STRING("monospace"));
1166 : }
1167 :
1168 0 : else if ((nsEditProperty::font == aHTMLProperty) && aHTMLAttribute
1169 0 : && aHTMLAttribute->EqualsLiteral("face")) {
1170 0 : if (!htmlValueString.IsEmpty()) {
1171 0 : const PRUnichar commaSpace[] = { PRUnichar(','), PRUnichar(' '), 0 };
1172 0 : const PRUnichar comma[] = { PRUnichar(','), 0 };
1173 0 : htmlValueString.ReplaceSubstring(commaSpace, comma);
1174 0 : nsAutoString valueStringNorm(valueString);
1175 0 : valueStringNorm.ReplaceSubstring(commaSpace, comma);
1176 : aIsSet = htmlValueString.Equals(valueStringNorm,
1177 0 : nsCaseInsensitiveStringComparator());
1178 : }
1179 : else {
1180 : // ignore this, it's TT or our default
1181 0 : nsAutoString valueStringLower;
1182 0 : ToLowerCase(valueString, valueStringLower);
1183 0 : aIsSet = !valueStringLower.EqualsLiteral("monospace") &&
1184 0 : !valueStringLower.EqualsLiteral("serif");
1185 : }
1186 0 : return NS_OK;
1187 : }
1188 0 : else if (aHTMLAttribute
1189 0 : && aHTMLAttribute->EqualsLiteral("align")) {
1190 0 : aIsSet = true;
1191 : }
1192 : else {
1193 0 : aIsSet = false;
1194 0 : return NS_OK;
1195 : }
1196 :
1197 0 : if (!htmlValueString.IsEmpty()) {
1198 0 : if (htmlValueString.Equals(valueString,
1199 0 : nsCaseInsensitiveStringComparator())) {
1200 0 : aIsSet = true;
1201 : }
1202 : }
1203 :
1204 0 : if (nsEditProperty::u == aHTMLProperty || nsEditProperty::strike == aHTMLProperty) {
1205 : // unfortunately, the value of the text-decoration property is not inherited.
1206 : // that means that we have to look at ancestors of node to see if they are underlined
1207 0 : node = node->GetElementParent(); // set to null if it's not a dom element
1208 : }
1209 : } while ((nsEditProperty::u == aHTMLProperty || nsEditProperty::strike == aHTMLProperty) &&
1210 0 : !aIsSet && node);
1211 0 : return NS_OK;
1212 : }
1213 :
1214 : nsresult
1215 0 : nsHTMLCSSUtils::SetCSSEnabled(bool aIsCSSPrefChecked)
1216 : {
1217 0 : mIsCSSPrefChecked = aIsCSSPrefChecked;
1218 0 : return NS_OK;
1219 : }
1220 :
1221 : bool
1222 0 : nsHTMLCSSUtils::IsCSSPrefChecked()
1223 : {
1224 0 : return mIsCSSPrefChecked ;
1225 : }
1226 :
1227 : // ElementsSameStyle compares two elements and checks if they have the same
1228 : // specified CSS declarations in the STYLE attribute
1229 : // The answer is always negative if at least one of them carries an ID or a class
1230 : bool
1231 0 : nsHTMLCSSUtils::ElementsSameStyle(nsIDOMNode *aFirstNode, nsIDOMNode *aSecondNode)
1232 : {
1233 : nsresult res;
1234 0 : nsCOMPtr<nsIDOMElement> firstElement = do_QueryInterface(aFirstNode);
1235 0 : nsCOMPtr<nsIDOMElement> secondElement = do_QueryInterface(aSecondNode);
1236 :
1237 0 : NS_ASSERTION((firstElement && secondElement), "Non element nodes passed to ElementsSameStyle.");
1238 :
1239 0 : nsAutoString firstID, secondID;
1240 : bool isFirstIDSet, isSecondIDSet;
1241 0 : res = mHTMLEditor->GetAttributeValue(firstElement, NS_LITERAL_STRING("id"), firstID, &isFirstIDSet);
1242 0 : res = mHTMLEditor->GetAttributeValue(secondElement, NS_LITERAL_STRING("id"), secondID, &isSecondIDSet);
1243 0 : if (isFirstIDSet || isSecondIDSet) {
1244 : // at least one of the spans carries an ID ; suspect a CSS rule applies to it and
1245 : // refuse to merge the nodes
1246 0 : return false;
1247 : }
1248 :
1249 0 : nsAutoString firstClass, secondClass;
1250 : bool isFirstClassSet, isSecondClassSet;
1251 0 : res = mHTMLEditor->GetAttributeValue(firstElement, NS_LITERAL_STRING("class"), firstClass, &isFirstClassSet);
1252 0 : res = mHTMLEditor->GetAttributeValue(secondElement, NS_LITERAL_STRING("class"), secondClass, &isSecondClassSet);
1253 0 : if (isFirstClassSet && isSecondClassSet) {
1254 : // both spans carry a class, let's compare them
1255 0 : if (!firstClass.Equals(secondClass)) {
1256 : // WARNING : technically, the comparison just above is questionable :
1257 : // from a pure HTML/CSS point of view class="a b" is NOT the same than
1258 : // class="b a" because a CSS rule could test the exact value of the class
1259 : // attribute to be "a b" for instance ; from a user's point of view, a
1260 : // wysiwyg editor should probably NOT make any difference. CSS people
1261 : // need to discuss this issue before any modification.
1262 0 : return false;
1263 : }
1264 : }
1265 0 : else if (isFirstClassSet || isSecondClassSet) {
1266 : // one span only carries a class, early way out
1267 0 : return false;
1268 : }
1269 :
1270 0 : nsCOMPtr<nsIDOMCSSStyleDeclaration> firstCSSDecl, secondCSSDecl;
1271 : PRUint32 firstLength, secondLength;
1272 0 : res = GetInlineStyles(firstElement, getter_AddRefs(firstCSSDecl), &firstLength);
1273 0 : if (NS_FAILED(res) || !firstCSSDecl) return false;
1274 0 : res = GetInlineStyles(secondElement, getter_AddRefs(secondCSSDecl), &secondLength);
1275 0 : if (NS_FAILED(res) || !secondCSSDecl) return false;
1276 :
1277 0 : if (firstLength != secondLength) {
1278 : // early way out if we can
1279 0 : return false;
1280 : }
1281 0 : else if (0 == firstLength) {
1282 : // no inline style !
1283 0 : return true;
1284 : }
1285 :
1286 : PRUint32 i;
1287 0 : nsAutoString propertyNameString;
1288 0 : nsAutoString firstValue, secondValue;
1289 0 : for (i=0; i<firstLength; i++) {
1290 0 : firstCSSDecl->Item(i, propertyNameString);
1291 0 : firstCSSDecl->GetPropertyValue(propertyNameString, firstValue);
1292 0 : secondCSSDecl->GetPropertyValue(propertyNameString, secondValue);
1293 0 : if (!firstValue.Equals(secondValue)) {
1294 0 : return false;
1295 : }
1296 : }
1297 0 : for (i=0; i<secondLength; i++) {
1298 0 : secondCSSDecl->Item(i, propertyNameString);
1299 0 : secondCSSDecl->GetPropertyValue(propertyNameString, secondValue);
1300 0 : firstCSSDecl->GetPropertyValue(propertyNameString, firstValue);
1301 0 : if (!firstValue.Equals(secondValue)) {
1302 0 : return false;
1303 : }
1304 : }
1305 :
1306 0 : return true;
1307 : }
1308 :
1309 : nsresult
1310 0 : nsHTMLCSSUtils::GetInlineStyles(nsIDOMElement *aElement,
1311 : nsIDOMCSSStyleDeclaration **aCssDecl,
1312 : PRUint32 *aLength)
1313 : {
1314 0 : NS_ENSURE_TRUE(aElement && aLength, NS_ERROR_NULL_POINTER);
1315 0 : *aLength = 0;
1316 0 : nsCOMPtr<nsIDOMElementCSSInlineStyle> inlineStyles = do_QueryInterface(aElement);
1317 0 : NS_ENSURE_TRUE(inlineStyles, NS_ERROR_NULL_POINTER);
1318 0 : nsresult res = inlineStyles->GetStyle(aCssDecl);
1319 0 : if (NS_FAILED(res) || !aCssDecl) return NS_ERROR_NULL_POINTER;
1320 0 : (*aCssDecl)->GetLength(aLength);
1321 0 : return NS_OK;
1322 : }
1323 :
1324 : already_AddRefed<nsIDOMElement>
1325 0 : nsHTMLCSSUtils::GetElementContainerOrSelf(nsIDOMNode* aNode)
1326 : {
1327 0 : nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
1328 0 : NS_ENSURE_TRUE(node, nsnull);
1329 : nsCOMPtr<nsIDOMElement> element =
1330 0 : do_QueryInterface(GetElementContainerOrSelf(node));
1331 0 : return element.forget();
1332 : }
1333 :
1334 : dom::Element*
1335 0 : nsHTMLCSSUtils::GetElementContainerOrSelf(nsINode* aNode)
1336 : {
1337 0 : MOZ_ASSERT(aNode);
1338 0 : if (nsIDOMNode::DOCUMENT_NODE == aNode->NodeType()) {
1339 0 : return nsnull;
1340 : }
1341 :
1342 0 : nsINode* node = aNode;
1343 : // Loop until we find an element.
1344 0 : while (node && !node->IsElement()) {
1345 0 : node = node->GetNodeParent();
1346 : }
1347 :
1348 0 : NS_ENSURE_TRUE(node, nsnull);
1349 0 : return node->AsElement();
1350 : }
1351 :
1352 : nsresult
1353 0 : nsHTMLCSSUtils::SetCSSProperty(nsIDOMElement * aElement,
1354 : const nsAString & aProperty,
1355 : const nsAString & aValue)
1356 : {
1357 0 : nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
1358 : PRUint32 length;
1359 0 : nsresult res = GetInlineStyles(aElement, getter_AddRefs(cssDecl), &length);
1360 0 : if (NS_FAILED(res) || !cssDecl) return res;
1361 :
1362 0 : return cssDecl->SetProperty(aProperty,
1363 : aValue,
1364 0 : EmptyString());
1365 : }
1366 :
1367 : nsresult
1368 0 : nsHTMLCSSUtils::SetCSSPropertyPixels(nsIDOMElement * aElement,
1369 : const nsAString & aProperty,
1370 : PRInt32 aIntValue)
1371 : {
1372 0 : nsAutoString s;
1373 0 : s.AppendInt(aIntValue);
1374 0 : return SetCSSProperty(aElement, aProperty, s + NS_LITERAL_STRING("px"));
1375 : }
|