editor/libeditor/html/nsHTMLCSSUtils.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial