michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set tw=80 expandtab softtabstop=2 ts=2 sw=2: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsStyledElement.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsAttrValue.h" michael@0: #include "nsAttrValueInlines.h" michael@0: #include "mozilla/dom/ElementInlines.h" michael@0: #include "mozilla/InternalMutationEvent.h" michael@0: #include "nsDOMCSSDeclaration.h" michael@0: #include "nsDOMCSSAttrDeclaration.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsIDocument.h" michael@0: #include "mozilla/css/StyleRule.h" michael@0: #include "nsCSSParser.h" michael@0: #include "mozilla/css/Loader.h" michael@0: #include "nsIDOMMutationEvent.h" michael@0: #include "nsXULElement.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsStyleUtil.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // nsIContent methods michael@0: michael@0: nsIAtom* michael@0: nsStyledElementNotElementCSSInlineStyle::GetClassAttributeName() const michael@0: { michael@0: return nsGkAtoms::_class; michael@0: } michael@0: michael@0: nsIAtom* michael@0: nsStyledElementNotElementCSSInlineStyle::GetIDAttributeName() const michael@0: { michael@0: return nsGkAtoms::id; michael@0: } michael@0: michael@0: nsIAtom* michael@0: nsStyledElementNotElementCSSInlineStyle::DoGetID() const michael@0: { michael@0: NS_ASSERTION(HasID(), "Unexpected call"); michael@0: michael@0: // The nullcheck here is needed because Element::UnsetAttr calls michael@0: // out to various code between removing the attribute and we get a chance to michael@0: // ClearHasID(). michael@0: michael@0: const nsAttrValue* attr = mAttrsAndChildren.GetAttr(nsGkAtoms::id); michael@0: michael@0: return attr ? attr->GetAtomValue() : nullptr; michael@0: } michael@0: michael@0: const nsAttrValue* michael@0: nsStyledElementNotElementCSSInlineStyle::DoGetClasses() const michael@0: { michael@0: NS_ASSERTION(HasFlag(NODE_MAY_HAVE_CLASS), "Unexpected call"); michael@0: return mAttrsAndChildren.GetAttr(nsGkAtoms::_class); michael@0: } michael@0: michael@0: bool michael@0: nsStyledElementNotElementCSSInlineStyle::ParseAttribute(int32_t aNamespaceID, michael@0: nsIAtom* aAttribute, michael@0: const nsAString& aValue, michael@0: nsAttrValue& aResult) michael@0: { michael@0: if (aNamespaceID == kNameSpaceID_None) { michael@0: if (aAttribute == nsGkAtoms::style) { michael@0: SetMayHaveStyle(); michael@0: ParseStyleAttribute(aValue, aResult, false); michael@0: return true; michael@0: } michael@0: if (aAttribute == nsGkAtoms::_class) { michael@0: SetFlags(NODE_MAY_HAVE_CLASS); michael@0: aResult.ParseAtomArray(aValue); michael@0: return true; michael@0: } michael@0: if (aAttribute == nsGkAtoms::id) { michael@0: // Store id as an atom. id="" means that the element has no id, michael@0: // not that it has an emptystring as the id. michael@0: RemoveFromIdTable(); michael@0: if (aValue.IsEmpty()) { michael@0: ClearHasID(); michael@0: return false; michael@0: } michael@0: aResult.ParseAtom(aValue); michael@0: SetHasID(); michael@0: AddToIdTable(aResult.GetAtomValue()); michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: return nsStyledElementBase::ParseAttribute(aNamespaceID, aAttribute, aValue, michael@0: aResult); michael@0: } michael@0: michael@0: nsresult michael@0: nsStyledElementNotElementCSSInlineStyle::UnsetAttr(int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: bool aNotify) michael@0: { michael@0: nsAutoScriptBlocker scriptBlocker; michael@0: if (aAttribute == nsGkAtoms::id && aNameSpaceID == kNameSpaceID_None) { michael@0: // Have to do this before clearing flag. See RemoveFromIdTable michael@0: RemoveFromIdTable(); michael@0: } michael@0: michael@0: return Element::UnsetAttr(aNameSpaceID, aAttribute, aNotify); michael@0: } michael@0: michael@0: nsresult michael@0: nsStyledElementNotElementCSSInlineStyle::AfterSetAttr(int32_t aNamespaceID, michael@0: nsIAtom* aAttribute, michael@0: const nsAttrValue* aValue, michael@0: bool aNotify) michael@0: { michael@0: if (aNamespaceID == kNameSpaceID_None && !aValue && michael@0: aAttribute == nsGkAtoms::id) { michael@0: // The id has been removed when calling UnsetAttr but we kept it because michael@0: // the id is used for some layout stuff between UnsetAttr and AfterSetAttr. michael@0: // Now. the id is really removed so it would not be safe to keep this flag. michael@0: ClearHasID(); michael@0: } michael@0: michael@0: return Element::AfterSetAttr(aNamespaceID, aAttribute, aValue, aNotify); michael@0: } michael@0: michael@0: nsresult michael@0: nsStyledElementNotElementCSSInlineStyle::SetInlineStyleRule(css::StyleRule* aStyleRule, michael@0: const nsAString* aSerialized, michael@0: bool aNotify) michael@0: { michael@0: SetMayHaveStyle(); michael@0: bool modification = false; michael@0: nsAttrValue oldValue; michael@0: michael@0: bool hasListeners = aNotify && michael@0: nsContentUtils::HasMutationListeners(this, michael@0: NS_EVENT_BITS_MUTATION_ATTRMODIFIED, michael@0: this); michael@0: michael@0: // There's no point in comparing the stylerule pointers since we're always michael@0: // getting a new stylerule here. And we can't compare the stringvalues of michael@0: // the old and the new rules since both will point to the same declaration michael@0: // and thus will be the same. michael@0: if (hasListeners) { michael@0: // save the old attribute so we can set up the mutation event properly michael@0: // XXXbz if the old rule points to the same declaration as the new one, michael@0: // this is getting the new attr value, not the old one.... michael@0: nsAutoString oldValueStr; michael@0: modification = GetAttr(kNameSpaceID_None, nsGkAtoms::style, michael@0: oldValueStr); michael@0: if (modification) { michael@0: oldValue.SetTo(oldValueStr); michael@0: } michael@0: } michael@0: else if (aNotify && IsInDoc()) { michael@0: modification = !!mAttrsAndChildren.GetAttr(nsGkAtoms::style); michael@0: } michael@0: michael@0: nsAttrValue attrValue(aStyleRule, aSerialized); michael@0: michael@0: // XXXbz do we ever end up with ADDITION here? I doubt it. michael@0: uint8_t modType = modification ? michael@0: static_cast(nsIDOMMutationEvent::MODIFICATION) : michael@0: static_cast(nsIDOMMutationEvent::ADDITION); michael@0: michael@0: return SetAttrAndNotify(kNameSpaceID_None, nsGkAtoms::style, nullptr, michael@0: oldValue, attrValue, modType, hasListeners, michael@0: aNotify, kDontCallAfterSetAttr); michael@0: } michael@0: michael@0: css::StyleRule* michael@0: nsStyledElementNotElementCSSInlineStyle::GetInlineStyleRule() michael@0: { michael@0: if (!MayHaveStyle()) { michael@0: return nullptr; michael@0: } michael@0: const nsAttrValue* attrVal = mAttrsAndChildren.GetAttr(nsGkAtoms::style); michael@0: michael@0: if (attrVal && attrVal->Type() == nsAttrValue::eCSSStyleRule) { michael@0: return attrVal->GetCSSStyleRuleValue(); michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: // --------------------------------------------------------------- michael@0: // Others and helpers michael@0: michael@0: nsICSSDeclaration* michael@0: nsStyledElementNotElementCSSInlineStyle::Style() michael@0: { michael@0: Element::nsDOMSlots *slots = DOMSlots(); michael@0: michael@0: if (!slots->mStyle) { michael@0: // Just in case... michael@0: ReparseStyleAttribute(true); michael@0: michael@0: slots->mStyle = new nsDOMCSSAttributeDeclaration(this, false); michael@0: SetMayHaveStyle(); michael@0: } michael@0: michael@0: return slots->mStyle; michael@0: } michael@0: michael@0: nsresult michael@0: nsStyledElementNotElementCSSInlineStyle::ReparseStyleAttribute(bool aForceInDataDoc) michael@0: { michael@0: if (!MayHaveStyle()) { michael@0: return NS_OK; michael@0: } michael@0: const nsAttrValue* oldVal = mAttrsAndChildren.GetAttr(nsGkAtoms::style); michael@0: michael@0: if (oldVal && oldVal->Type() != nsAttrValue::eCSSStyleRule) { michael@0: nsAttrValue attrValue; michael@0: nsAutoString stringValue; michael@0: oldVal->ToString(stringValue); michael@0: ParseStyleAttribute(stringValue, attrValue, aForceInDataDoc); michael@0: // Don't bother going through SetInlineStyleRule, we don't want to fire off michael@0: // mutation events or document notifications anyway michael@0: nsresult rv = mAttrsAndChildren.SetAndTakeAttr(nsGkAtoms::style, attrValue); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsStyledElementNotElementCSSInlineStyle::ParseStyleAttribute(const nsAString& aValue, michael@0: nsAttrValue& aResult, michael@0: bool aForceInDataDoc) michael@0: { michael@0: nsIDocument* doc = OwnerDoc(); michael@0: michael@0: if (!nsStyleUtil::CSPAllowsInlineStyle(nullptr, NodePrincipal(), michael@0: doc->GetDocumentURI(), 0, aValue, michael@0: nullptr)) michael@0: return; michael@0: michael@0: if (aForceInDataDoc || michael@0: !doc->IsLoadedAsData() || michael@0: doc->IsStaticDocument()) { michael@0: bool isCSS = true; // assume CSS until proven otherwise michael@0: michael@0: if (!IsInNativeAnonymousSubtree()) { // native anonymous content michael@0: // always assumes CSS michael@0: nsAutoString styleType; michael@0: doc->GetHeaderData(nsGkAtoms::headerContentStyleType, styleType); michael@0: if (!styleType.IsEmpty()) { michael@0: static const char textCssStr[] = "text/css"; michael@0: isCSS = (styleType.EqualsIgnoreCase(textCssStr, sizeof(textCssStr) - 1)); michael@0: } michael@0: } michael@0: michael@0: if (isCSS && aResult.ParseStyleAttribute(aValue, this)) { michael@0: return; michael@0: } michael@0: } michael@0: michael@0: aResult.SetTo(aValue); michael@0: }