/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// ALIGN attribute on elements supporting it if (attributeOrStyle == nsGkAtoms::align &&
(&aTagName == nsGkAtoms::div || &aTagName == nsGkAtoms::p ||
&aTagName == nsGkAtoms::h1 || &aTagName == nsGkAtoms::h2 ||
&aTagName == nsGkAtoms::h3 || &aTagName == nsGkAtoms::h4 ||
&aTagName == nsGkAtoms::h5 || &aTagName == nsGkAtoms::h6 ||
&aTagName == nsGkAtoms::td || &aTagName == nsGkAtoms::th ||
&aTagName == nsGkAtoms::table || &aTagName == nsGkAtoms::hr || // For the above, why not use // HTMLEditUtils::SupportsAlignAttr? // It also checks for tbody, tfoot, thead. // Let's add the following elements here even // if "align" has a different meaning for them
&aTagName == nsGkAtoms::legend || &aTagName == nsGkAtoms::caption)) { returntrue;
}
// other elements that we can align using CSS even if they // can't carry the html ALIGN attribute if (attributeOrStyle == nsGkAtoms::align &&
(&aTagName == nsGkAtoms::ul || &aTagName == nsGkAtoms::ol ||
&aTagName == nsGkAtoms::dl || &aTagName == nsGkAtoms::li ||
&aTagName == nsGkAtoms::dd || &aTagName == nsGkAtoms::dt ||
&aTagName == nsGkAtoms::address || &aTagName == nsGkAtoms::pre)) { returntrue;
}
returnfalse;
}
// The lowest level above the transaction; adds the CSS declaration // "aProperty : aValue" to the inline styles carried by aStyledElement
// The lowest level above the transaction; removes the value aValue from the // list of values specified for the CSS property aProperty, or totally remove // the declaration if this property accepts only one value
RefPtr<Element> element = aContent.GetAsElementOrParentElement(); if (NS_WARN_IF(!element)) { return NS_ERROR_INVALID_ARG;
}
// Get the all the computed css styles attached to the element node
RefPtr<nsComputedDOMStyle> computedDOMStyle = GetComputedStyle(element); if (NS_WARN_IF(!computedDOMStyle)) { return NS_ERROR_INVALID_ARG;
}
// from these declarations, get the one we want and that one only // // FIXME(bug 1606994): nsAtomCString copies, we should just keep around the // property id. // // FIXME: Maybe we can avoid copying aValue too, though it's no worse than // what we used to do.
nsAutoCString value;
computedDOMStyle->GetPropertyValue(nsAtomCString(&aCSSProperty), value);
CopyUTF8toUTF16(value, aValue); return NS_OK;
}
// Get the default browser background color if we need it for // GetCSSBackgroundColorState
// static void CSSEditUtils::GetDefaultBackgroundColor(nsAString& aColor) { if (MOZ_UNLIKELY(StaticPrefs::editor_use_custom_colors())) {
nsresult rv = Preferences::GetString("editor.background_color", aColor); // XXX Why don't you validate the pref value? if (NS_FAILED(rv)) {
NS_WARNING("failed to get editor.background_color");
aColor.AssignLiteral("#ffffff"); // Default to white
} return;
}
if (Preferences::GetBool("browser.display.use_system_colors", false)) { return;
}
nsresult rv =
Preferences::GetString("browser.display.background_color", aColor); // XXX Why don't you validate the pref value? if (NS_FAILED(rv)) {
NS_WARNING("failed to get browser.display.background_color");
aColor.AssignLiteral("#ffffff"); // Default to white
}
}
float a = 10.0f, b = 1.0f, value = 0;
int8_t sign = 1;
int32_t i = 0, j = aString.Length();
char16_t c; bool floatingPointFound = false;
c = *iter; if (char16_t('-') == c) {
sign = -1;
iter++;
i++;
} elseif (char16_t('+') == c) {
iter++;
i++;
} while (i < j) {
c = *iter; if ((char16_t('0') == c) || (char16_t('1') == c) || (char16_t('2') == c) ||
(char16_t('3') == c) || (char16_t('4') == c) || (char16_t('5') == c) ||
(char16_t('6') == c) || (char16_t('7') == c) || (char16_t('8') == c) ||
(char16_t('9') == c)) {
value = (value * a) + (b * (c - char16_t('0')));
b = b / 10 * a;
} elseif (!floatingPointFound && (char16_t('.') == c)) {
floatingPointFound = true;
a = 1.0f;
b = 0.1f;
} else break;
iter++;
i++;
}
*aValue = value * sign;
*aUnit = NS_Atomize(StringTail(aString, j - i)).take();
}
// static
nsStaticAtom* CSSEditUtils::GetCSSPropertyAtom(
nsCSSEditableProperty aProperty) { switch (aProperty) { case eCSSEditableProperty_background_color: return nsGkAtoms::backgroundColor; case eCSSEditableProperty_background_image: return nsGkAtoms::background_image; case eCSSEditableProperty_border: return nsGkAtoms::border; case eCSSEditableProperty_caption_side: return nsGkAtoms::caption_side; case eCSSEditableProperty_color: return nsGkAtoms::color; case eCSSEditableProperty_float: return nsGkAtoms::_float; case eCSSEditableProperty_font_family: return nsGkAtoms::font_family; case eCSSEditableProperty_font_size: return nsGkAtoms::font_size; case eCSSEditableProperty_font_style: return nsGkAtoms::font_style; case eCSSEditableProperty_font_weight: return nsGkAtoms::fontWeight; case eCSSEditableProperty_height: return nsGkAtoms::height; case eCSSEditableProperty_list_style_type: return nsGkAtoms::list_style_type; case eCSSEditableProperty_margin_left: return nsGkAtoms::marginLeft; case eCSSEditableProperty_margin_right: return nsGkAtoms::marginRight; case eCSSEditableProperty_text_align: return nsGkAtoms::textAlign; case eCSSEditableProperty_text_decoration: return nsGkAtoms::text_decoration; case eCSSEditableProperty_vertical_align: return nsGkAtoms::vertical_align; case eCSSEditableProperty_whitespace: return nsGkAtoms::white_space; case eCSSEditableProperty_width: return nsGkAtoms::width; case eCSSEditableProperty_NONE: // intentionally empty return nullptr;
}
MOZ_ASSERT_UNREACHABLE("Got unknown property"); return nullptr;
}
// if we have an input value, let's use it
nsAutoString value, lowerCasedValue; if (aValue) {
value.Assign(*aValue);
lowerCasedValue.Assign(*aValue);
ToLowerCase(lowerCasedValue);
}
for (size_t index = 0;; index++) { const nsCSSEditableProperty cssProperty = aEquivTable[index].cssProperty; if (!cssProperty) { break;
} if (aHandlingFor == HandlingFor::SettingStyle ||
aEquivTable[index].gettable) {
nsAutoString cssValue, cssPropertyString; // find the equivalent css value for the index-th property in // the equivalence table
(*aEquivTable[index].processValueFunctor)(
(aHandlingFor == HandlingFor::SettingStyle ||
aEquivTable[index].caseSensitiveValue)
? &value
: &lowerCasedValue,
cssValue, aEquivTable[index].defaultValue,
aEquivTable[index].prependValue, aEquivTable[index].appendValue);
nsStaticAtom* const propertyAtom = GetCSSPropertyAtom(cssProperty); if (MOZ_LIKELY(propertyAtom)) {
aOutCSSDeclarations.AppendElement(
CSSDeclaration{*propertyAtom, cssValue});
}
}
}
}
constauto* equivTable = [&]() -> const CSSEditUtils::CSSEquivTable* { if (nsGkAtoms::b == htmlProperty) { return boldEquivTable;
} if (nsGkAtoms::i == htmlProperty) { return italicEquivTable;
} if (nsGkAtoms::u == htmlProperty) { return underlineEquivTable;
} if (nsGkAtoms::strike == htmlProperty) { return strikeEquivTable;
} if (nsGkAtoms::tt == htmlProperty) { return ttEquivTable;
} if (!attributeOrStyle) { return nullptr;
} if (nsGkAtoms::font == htmlProperty) { if (attributeOrStyle == nsGkAtoms::color) { return fontColorEquivTable;
} if (attributeOrStyle == nsGkAtoms::face) { return fontFaceEquivTable;
} if (attributeOrStyle == nsGkAtoms::size) { return fontSizeEquivTable;
}
MOZ_ASSERT(attributeOrStyle == nsGkAtoms::bgcolor);
} if (attributeOrStyle == nsGkAtoms::bgcolor) { return bgcolorEquivTable;
} if (attributeOrStyle == nsGkAtoms::background) { return backgroundImageEquivTable;
} if (attributeOrStyle == nsGkAtoms::text) { return textColorEquivTable;
} if (attributeOrStyle == nsGkAtoms::border) { return borderEquivTable;
} if (attributeOrStyle == nsGkAtoms::align) { if (aElement.IsHTMLElement(nsGkAtoms::table)) { return tableAlignEquivTable;
} if (aElement.IsHTMLElement(nsGkAtoms::hr)) { return hrAlignEquivTable;
} if (aElement.IsAnyOfHTMLElements(nsGkAtoms::legend, nsGkAtoms::caption)) { return captionAlignEquivTable;
} return textAlignEquivTable;
} if (attributeOrStyle == nsGkAtoms::valign) { return verticalAlignEquivTable;
} if (attributeOrStyle == nsGkAtoms::nowrap) { return nowrapEquivTable;
} if (attributeOrStyle == nsGkAtoms::width) { return widthEquivTable;
} if (attributeOrStyle == nsGkAtoms::height ||
(aElement.IsHTMLElement(nsGkAtoms::hr) &&
attributeOrStyle == nsGkAtoms::size)) { return heightEquivTable;
} if (attributeOrStyle == nsGkAtoms::type &&
aElement.IsAnyOfHTMLElements(nsGkAtoms::ol, nsGkAtoms::ul,
nsGkAtoms::li)) { return listStyleTypeEquivTable;
} return nullptr;
}(); if (equivTable) {
GetCSSDeclarations(equivTable, aValue, aHandlingFor, aOutCSSDeclarations);
}
}
// Add to aNode the CSS inline style equivalent to HTMLProperty/aAttribute/ // aValue for the node, and return in aCount the number of CSS properties set // by the call. The Element version returns aCount instead.
Result<size_t, nsresult> CSSEditUtils::SetCSSEquivalentToStyle(
WithTransaction aWithTransaction, HTMLEditor& aHTMLEditor,
nsStyledElement& aStyledElement, const EditorElementStyle& aStyleToSet, const nsAString* aValue) {
MOZ_DIAGNOSTIC_ASSERT(aStyleToSet.IsCSSSettable(aStyledElement));
// we can apply the styles only if the node is an element and if we have // an equivalence for the requested HTML style in this implementation
// Find the CSS equivalence to the HTML style
AutoTArray<CSSDeclaration, 4> cssDeclarations;
GetCSSDeclarations(aStyledElement, aStyleToSet, aValue,
HandlingFor::SettingStyle, cssDeclarations);
// set the individual CSS inline styles for (const CSSDeclaration& cssDeclaration : cssDeclarations) {
nsresult rv = SetCSSPropertyInternal(
aHTMLEditor, aStyledElement, MOZ_KnownLive(cssDeclaration.mProperty),
cssDeclaration.mValue, aWithTransaction == WithTransaction::No); if (NS_FAILED(rv)) {
NS_WARNING("CSSEditUtils::SetCSSPropertyInternal() failed"); return Err(rv);
}
} return cssDeclarations.Length();
}
// we can apply the styles only if the node is an element and if we have // an equivalence for the requested HTML style in this implementation
// Find the CSS equivalence to the HTML style
AutoTArray<CSSDeclaration, 4> cssDeclarations;
GetCSSDeclarations(aStyledElement, aStyleToRemove, aValue,
HandlingFor::RemovingStyle, cssDeclarations);
aOutValue.Truncate();
AutoTArray<CSSDeclaration, 4> cssDeclarations;
GetCSSDeclarations(aElement, aStyle, nullptr, HandlingFor::GettingStyle,
cssDeclarations);
nsAutoString valueString; for (const CSSDeclaration& cssDeclaration : cssDeclarations) {
valueString.Truncate(); // retrieve the specified/computed value of the property if (aStyleType == StyleType::Computed) {
nsresult rv = GetComputedCSSInlinePropertyBase(
aElement, MOZ_KnownLive(cssDeclaration.mProperty), valueString); if (NS_FAILED(rv)) {
NS_WARNING("CSSEditUtils::GetComputedCSSInlinePropertyBase() failed"); return rv;
}
} else {
nsresult rv = GetSpecifiedCSSInlinePropertyBase(
aElement, cssDeclaration.mProperty, valueString); if (NS_FAILED(rv)) {
NS_WARNING("CSSEditUtils::GetSpecifiedCSSInlinePropertyBase() failed"); return rv;
}
} // append the value to aOutValue (possibly with a leading white-space) if (!aOutValue.IsEmpty()) {
aOutValue.Append(HTMLEditUtils::kSpace);
}
aOutValue.Append(valueString);
} return NS_OK;
}
// Does the node aContent (or its parent, if it's not an element node) have a // CSS style equivalent to the HTML style // aHTMLProperty/aAttribute/valueString? The value of aStyleType controls // the styles we retrieve: specified or computed. The return value aIsSet is // true if the CSS styles are set. // // The nsIContent variant returns aIsSet instead of using an out parameter, and // does not modify aValue.
nsAutoString htmlValueString(aInOutValue); bool isSet = false; // FYI: Cannot use InclusiveAncestorsOfType here because // GetCSSEquivalentTo() may flush pending notifications. for (RefPtr<Element> element = aContent.GetAsElementOrParentElement();
element; element = element->GetParentElement()) {
nsCOMPtr<nsINode> parentNode = element->GetParentNode();
aInOutValue.Assign(htmlValueString); // get the value of the CSS equivalent styles
nsresult rv = GetCSSEquivalentTo(*element, aStyle, aInOutValue, aStyleType); if (NS_WARN_IF(aHTMLEditor.Destroyed())) { return Err(NS_ERROR_EDITOR_DESTROYED);
} if (NS_FAILED(rv)) {
NS_WARNING( "CSSEditUtils::GetCSSEquivalentToHTMLInlineStyleSetInternal() " "failed"); return Err(rv);
} if (NS_WARN_IF(parentNode != element->GetParentNode())) { return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
}
// early way out if we can if (aInOutValue.IsEmpty()) { return isSet;
}
if (!htmlValueString.IsEmpty() &&
htmlValueString.Equals(aInOutValue,
nsCaseInsensitiveStringComparator)) {
isSet = true;
}
if (htmlValueString.EqualsLiteral(u"-moz-editor-invert-value")) {
isSet = !isSet;
}
if (isSet) { returntrue;
}
if (!aStyle.IsStyleOfTextDecoration(
EditorInlineStyle::IgnoreSElement::Yes)) { return isSet;
}
// Unfortunately, the value of the text-decoration property is not // inherited. that means that we have to look at ancestors of node to see // if they are underlined.
} return isSet;
}
// FYI: Unfortunately, we cannot use InclusiveAncestorsOfType here // because GetCSSEquivalentTo() may flush pending notifications.
nsAutoString valueString; for (RefPtr<Element> element = aContent.GetAsElementOrParentElement();
element; element = element->GetParentElement()) {
nsCOMPtr<nsINode> parentNode = element->GetParentNode(); // get the value of the CSS equivalent styles
nsresult rv = GetCSSEquivalentTo(*element, aStyle, valueString, aStyleType); if (NS_WARN_IF(aHTMLEditor.Destroyed())) { return Err(NS_ERROR_EDITOR_DESTROYED);
} if (NS_FAILED(rv)) {
NS_WARNING( "CSSEditUtils::GetCSSEquivalentToHTMLInlineStyleSetInternal() " "failed"); return Err(rv);
} if (NS_WARN_IF(parentNode != element->GetParentNode())) { return Err(NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE);
}
if (!valueString.IsEmpty()) { returntrue;
}
if (!aStyle.IsStyleOfTextDecoration(
EditorInlineStyle::IgnoreSElement::Yes)) { returnfalse;
}
// Unfortunately, the value of the text-decoration property is not // inherited. // that means that we have to look at ancestors of node to see if they // are underlined.
}
returnfalse;
}
// ElementsSameStyle compares two elements and checks if they have the same // specified CSS declarations in the STYLE attribute // The answer is always negative if at least one of them carries an ID or a // class
// static bool CSSEditUtils::DoStyledElementsHaveSameStyle(
nsStyledElement& aStyledElement, nsStyledElement& aOtherStyledElement) { if (aStyledElement.HasAttr(nsGkAtoms::id) ||
aOtherStyledElement.HasAttr(nsGkAtoms::id)) { // at least one of the spans carries an ID ; suspect a CSS rule applies to // it and refuse to merge the nodes returnfalse;
}
nsAutoString firstClass, otherClass; bool isElementClassSet =
aStyledElement.GetAttr(nsGkAtoms::_class, firstClass); bool isOtherElementClassSet = aOtherStyledElement.GetAttr(
kNameSpaceID_None, nsGkAtoms::_class, otherClass); if (isElementClassSet && isOtherElementClassSet) { // both spans carry a class, let's compare them if (!firstClass.Equals(otherClass)) { // WARNING : technically, the comparison just above is questionable : // from a pure HTML/CSS point of view class="a b" is NOT the same than // class="b a" because a CSS rule could test the exact value of the class // attribute to be "a b" for instance ; from a user's point of view, a // wysiwyg editor should probably NOT make any difference. CSS people // need to discuss this issue before any modification. returnfalse;
}
} elseif (isElementClassSet || isOtherElementClassSet) { // one span only carries a class, early way out returnfalse;
}
// XXX If `GetPropertyValue()` won't run script, we can stop using // nsCOMPtr here.
nsCOMPtr<nsICSSDeclaration> firstCSSDecl = aStyledElement.Style(); if (!firstCSSDecl) {
NS_WARNING("nsStyledElement::Style() failed"); returnfalse;
}
nsCOMPtr<nsICSSDeclaration> otherCSSDecl = aOtherStyledElement.Style(); if (!otherCSSDecl) {
NS_WARNING("nsStyledElement::Style() failed"); returnfalse;
}
const uint32_t firstLength = firstCSSDecl->Length(); const uint32_t otherLength = otherCSSDecl->Length(); if (firstLength != otherLength) { // early way out if we can returnfalse;
}
if (!firstLength) { // no inline style ! returntrue;
}
for (uint32_t i = 0; i < firstLength; i++) {
nsAutoCString firstValue, otherValue;
nsAutoCString propertyNameString;
firstCSSDecl->Item(i, propertyNameString);
firstCSSDecl->GetPropertyValue(propertyNameString, firstValue);
otherCSSDecl->GetPropertyValue(propertyNameString, otherValue); // FIXME: We need to handle all properties whose values are color. // However, it's too expensive if we keep using string property names. if (propertyNameString.EqualsLiteral("color") ||
propertyNameString.EqualsLiteral("background-color")) { if (!HTMLEditUtils::IsSameCSSColorValue(firstValue, otherValue)) { returnfalse;
}
} elseif (!firstValue.Equals(otherValue)) { returnfalse;
}
} for (uint32_t i = 0; i < otherLength; i++) {
nsAutoCString firstValue, otherValue;
nsAutoCString propertyNameString;
otherCSSDecl->Item(i, propertyNameString);
otherCSSDecl->GetPropertyValue(propertyNameString, otherValue);
firstCSSDecl->GetPropertyValue(propertyNameString, firstValue); // FIXME: We need to handle all properties whose values are color. // However, it's too expensive if we keep using string property names. if (propertyNameString.EqualsLiteral("color") ||
propertyNameString.EqualsLiteral("background-color")) { if (!HTMLEditUtils::IsSameCSSColorValue(firstValue, otherValue)) { returnfalse;
}
} elseif (!firstValue.Equals(otherValue)) { returnfalse;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.