Bug 1365092 - Move side effects of SetAttr and ParseAttribute to BeforeSetAttr and AfterSetAttr

* Moves side effects of nsGenericHTMLElement and Element's SetAttr, UnsetAttr, and ParseAttribute functions to the corresponding BeforeSetAttr and AfterSetAttr functions
* Moves side effects of HTMLAnchorElement's SetAttr, UnsetAttr, and ParseAttribute functions to the corresponding BeforeSetAttr and AfterSetAttr functions
* Moves side effects of HTMLImageElement's SetAttr function to the corresponding BeforeSetAttr and AfterSetAttr functions
* Moves side effects of SetAttr, UnsetAttr, and ParseAttribute functions to BeforeSetAttr and AfterSetAttr

Tag #1375
This commit is contained in:
Matt A. Tobin 2020-04-17 05:33:06 -04:00 • committed by Roy Tam
commit 6aa436b588
41 changed files with 983 additions and 956 deletions

View file

@ -2476,7 +2476,7 @@ Element::SetAttr(int32_t aNamespaceID, nsIAtom* aName,
if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, value, aNotify, if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, value, aNotify,
oldValue, &modType, &hasListeners, &oldValueSet)) { oldValue, &modType, &hasListeners, &oldValueSet)) {
return NS_OK; return OnAttrSetButNotChanged(aNamespaceID, aName, value, aNotify);
} }
nsAttrValue attrValue; nsAttrValue attrValue;
@ -2494,20 +2494,21 @@ Element::SetAttr(int32_t aNamespaceID, nsIAtom* aName,
preparsedAttrValue); preparsedAttrValue);
} }
nsresult rv = BeforeSetAttr(aNamespaceID, aName, &value, aNotify);
NS_ENSURE_SUCCESS(rv, rv);
// Hold a script blocker while calling ParseAttribute since that can call // Hold a script blocker while calling ParseAttribute since that can call
// out to id-observers // out to id-observers
nsIDocument* document = GetComposedDoc(); nsIDocument* document = GetComposedDoc();
mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify); mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify);
// Even the value was pre-parsed, we still need to call ParseAttribute because nsresult rv = BeforeSetAttr(aNamespaceID, aName, &value, aNotify);
// it can have side effects. NS_ENSURE_SUCCESS(rv, rv);
if (!ParseAttribute(aNamespaceID, aName, aValue, attrValue)) {
if (!preparsedAttrValue &&
!ParseAttribute(aNamespaceID, aName, aValue, attrValue)) {
attrValue.SetTo(aValue); attrValue.SetTo(aValue);
} }
PreIdMaybeChange(aNamespaceID, aName, &value);
return SetAttrAndNotify(aNamespaceID, aName, aPrefix, return SetAttrAndNotify(aNamespaceID, aName, aPrefix,
oldValueSet ? &oldValue : nullptr, oldValueSet ? &oldValue : nullptr,
attrValue, modType, hasListeners, aNotify, attrValue, modType, hasListeners, aNotify,
@ -2538,7 +2539,7 @@ Element::SetParsedAttr(int32_t aNamespaceID, nsIAtom* aName,
if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, value, aNotify, if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, value, aNotify,
oldValue, &modType, &hasListeners, &oldValueSet)) { oldValue, &modType, &hasListeners, &oldValueSet)) {
return NS_OK; return OnAttrSetButNotChanged(aNamespaceID, aName, value, aNotify);
} }
if (aNotify) { if (aNotify) {
@ -2549,6 +2550,8 @@ Element::SetParsedAttr(int32_t aNamespaceID, nsIAtom* aName,
nsresult rv = BeforeSetAttr(aNamespaceID, aName, &value, aNotify); nsresult rv = BeforeSetAttr(aNamespaceID, aName, &value, aNotify);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
PreIdMaybeChange(aNamespaceID, aName, &value);
nsIDocument* document = GetComposedDoc(); nsIDocument* document = GetComposedDoc();
mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify); mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify);
return SetAttrAndNotify(aNamespaceID, aName, aPrefix, return SetAttrAndNotify(aNamespaceID, aName, aPrefix,
@ -2608,6 +2611,8 @@ Element::SetAttrAndNotify(int32_t aNamespaceID,
} }
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
PostIdMaybeChange(aNamespaceID, aName, &valueForAfterSetAttr);
// If the old value owns its own data, we know it is OK to keep using it. // If the old value owns its own data, we know it is OK to keep using it.
// oldValue will be null if there was no previously set value // oldValue will be null if there was no previously set value
const nsAttrValue* oldValue; const nsAttrValue* oldValue;
@ -2720,22 +2725,16 @@ Element::ParseAttribute(int32_t aNamespaceID,
nsAttrValue& aResult) nsAttrValue& aResult)
{ {
if (aNamespaceID == kNameSpaceID_None) { if (aNamespaceID == kNameSpaceID_None) {
if (aAttribute == nsGkAtoms::_class) { MOZ_ASSERT(aAttribute != nsGkAtoms::_class,
SetMayHaveClass(); "The class attribute should be preparsed and therefore should "
// Result should have been preparsed above. "never be passed to Element::ParseAttribute");
return true;
}
if (aAttribute == nsGkAtoms::id) { if (aAttribute == nsGkAtoms::id) {
// Store id as an atom. id="" means that the element has no id, // Store id as an atom. id="" means that the element has no id,
// not that it has an emptystring as the id. // not that it has an emptystring as the id.
RemoveFromIdTable();
if (aValue.IsEmpty()) { if (aValue.IsEmpty()) {
ClearHasID();
return false; return false;
} }
aResult.ParseAtom(aValue); aResult.ParseAtom(aValue);
SetHasID();
AddToIdTable(aResult.GetAtomValue());
return true; return true;
} }
} }
@ -2754,6 +2753,57 @@ Element::SetAndSwapMappedAttribute(nsIDocument* aDocument,
return false; return false;
} }
nsresult
Element::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValueOrString* aValue, bool aNotify)
{
if (aNamespaceID == kNameSpaceID_None) {
if (aName == nsGkAtoms::_class) {
if (aValue) {
// Note: This flag is asymmetrical. It is never unset and isn't exact.
// If it is ever made to be exact, we probably need to handle this
// similarly to how ids are handled in PreIdMaybeChange and
// PostIdMaybeChange.
SetMayHaveClass();
}
}
}
return NS_OK;
}
void
Element::PreIdMaybeChange(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValueOrString* aValue)
{
if (aNamespaceID != kNameSpaceID_None || aName != nsGkAtoms::id) {
return;
}
RemoveFromIdTable();
return;
}
void
Element::PostIdMaybeChange(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValue* aValue)
{
if (aNamespaceID != kNameSpaceID_None || aName != nsGkAtoms::id) {
return;
}
// id="" means that the element has no id, not that it has an empty
// string as the id.
if (aValue && !aValue->IsEmptyString()) {
SetHasID();
AddToIdTable(aValue->GetAtomValue());
} else {
ClearHasID();
}
return;
}
EventListenerManager* EventListenerManager*
Element::GetEventListenerManagerForAttr(nsIAtom* aAttrName, Element::GetEventListenerManagerForAttr(nsIAtom* aAttrName,
bool* aDefer) bool* aDefer)
@ -2847,6 +2897,8 @@ Element::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName,
NS_EVENT_BITS_MUTATION_ATTRMODIFIED, NS_EVENT_BITS_MUTATION_ATTRMODIFIED,
this); this);
PreIdMaybeChange(aNameSpaceID, aName, nullptr);
// Grab the attr node if needed before we remove it from the attr map // Grab the attr node if needed before we remove it from the attr map
RefPtr<Attr> attrNode; RefPtr<Attr> attrNode;
if (hasMutationListeners) { if (hasMutationListeners) {
@ -2865,12 +2917,6 @@ Element::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName,
// react to unexpected attribute changes. // react to unexpected attribute changes.
nsMutationGuard::DidMutate(); nsMutationGuard::DidMutate();
if (aName == nsGkAtoms::id && aNameSpaceID == kNameSpaceID_None) {
// Have to do this before clearing flag. See RemoveFromIdTable
RemoveFromIdTable();
ClearHasID();
}
bool hadValidDir = false; bool hadValidDir = false;
bool hadDirAuto = false; bool hadDirAuto = false;
@ -2883,6 +2929,8 @@ Element::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName,
rv = mAttrsAndChildren.RemoveAttrAt(index, oldValue); rv = mAttrsAndChildren.RemoveAttrAt(index, oldValue);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
PostIdMaybeChange(aNameSpaceID, aName, nullptr);
if (document || HasFlag(NODE_FORCE_XBL_BINDINGS)) { if (document || HasFlag(NODE_FORCE_XBL_BINDINGS)) {
RefPtr<nsXBLBinding> binding = GetXBLBinding(); RefPtr<nsXBLBinding> binding = GetXBLBinding();
if (binding) { if (binding) {

View file

@ -523,6 +523,7 @@ public:
already_AddRefed<mozilla::dom::NodeInfo> already_AddRefed<mozilla::dom::NodeInfo>
GetExistingAttrNameFromQName(const nsAString& aStr) const; GetExistingAttrNameFromQName(const nsAString& aStr) const;
MOZ_ALWAYS_INLINE // Avoid a crashy hook from Avast 10 Beta (Bug 1058131)
nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAString& aValue, bool aNotify) const nsAString& aValue, bool aNotify)
{ {
@ -1378,14 +1379,9 @@ protected:
* will be null. * will be null.
* @param aNotify Whether we plan to notify document observers. * @param aNotify Whether we plan to notify document observers.
*/ */
// Note that this is inlined so that when subclasses call it it gets
// inlined. Those calls don't go through a vtable.
virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValueOrString* aValue, const nsAttrValueOrString* aValue,
bool aNotify) bool aNotify);
{
return NS_OK;
}
/** /**
* Hook that is called by Element::SetAttr to allow subclasses to * Hook that is called by Element::SetAttr to allow subclasses to
@ -1412,6 +1408,64 @@ protected:
return NS_OK; return NS_OK;
} }
/**
* This function shall be called just before the id attribute changes. It will
* be called after BeforeSetAttr. If the attribute being changed is not the id
* attribute, this function does nothing. Otherwise, it will remove the old id
* from the document's id cache.
*
* This must happen after BeforeSetAttr (rather than during) because the
* the subclasses' calls to BeforeSetAttr may notify on state changes. If they
* incorrectly determine whether the element had an id, the element may not be
* restyled properly.
*
* @param aNamespaceID the namespace of the attr being set
* @param aName the localname of the attribute being set
* @param aValue the new id value. Will be null if the id is being unset.
*/
void PreIdMaybeChange(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValueOrString* aValue);
/**
* This function shall be called just after the id attribute changes. It will
* be called before AfterSetAttr. If the attribute being changed is not the id
* attribute, this function does nothing. Otherwise, it will add the new id to
* the document's id cache and properly set the ElementHasID flag.
*
* This must happen before AfterSetAttr (rather than during) because the
* the subclasses' calls to AfterSetAttr may notify on state changes. If they
* incorrectly determine whether the element now has an id, the element may
* not be restyled properly.
*
* @param aNamespaceID the namespace of the attr being set
* @param aName the localname of the attribute being set
* @param aValue the new id value. Will be null if the id is being unset.
*/
void PostIdMaybeChange(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValue* aValue);
/**
* Usually, setting an attribute to the value that it already has results in
* no action. However, in some cases, setting an attribute to its current
* value should have the effect of, for example, forcing a reload of
* network data. To address that, this function will be called in this
* situation to allow the handling of such a case.
*
* @param aNamespaceID the namespace of the attr being set
* @param aName the localname of the attribute being set
* @param aValue the value it's being set to represented as either a string or
* a parsed nsAttrValue.
* @param aNotify Whether we plan to notify document observers.
*/
// Note that this is inlined so that when subclasses call it it gets
// inlined. Those calls don't go through a vtable.
virtual nsresult OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValueOrString& aValue,
bool aNotify)
{
return NS_OK;
}
/** /**
* Hook to allow subclasses to produce a different EventListenerManager if * Hook to allow subclasses to produce a different EventListenerManager if
* needed for attachment of attribute-defined handlers * needed for attachment of attribute-defined handlers

View file

@ -78,6 +78,20 @@ public:
return aOther.EqualsAsStrings(*mAttrValue); return aOther.EqualsAsStrings(*mAttrValue);
} }
/*
* Returns true if the value stored is empty
*/
bool IsEmpty() const
{
if (mStringPtr) {
return mStringPtr->IsEmpty();
}
if (mAttrValue) {
return mAttrValue->IsEmptyString();
}
return true;
}
protected: protected:
const nsAttrValue* mAttrValue; const nsAttrValue* mAttrValue;
mutable const nsAString* mStringPtr; mutable const nsAString* mStringPtr;

View file

@ -40,7 +40,6 @@ nsStyledElement::ParseAttribute(int32_t aNamespaceID,
nsAttrValue& aResult) nsAttrValue& aResult)
{ {
if (aAttribute == nsGkAtoms::style && aNamespaceID == kNameSpaceID_None) { if (aAttribute == nsGkAtoms::style && aNamespaceID == kNameSpaceID_None) {
SetMayHaveStyle();
ParseStyleAttribute(aValue, aResult, false); ParseStyleAttribute(aValue, aResult, false);
return true; return true;
} }
@ -49,6 +48,22 @@ nsStyledElement::ParseAttribute(int32_t aNamespaceID,
aResult); aResult);
} }
nsresult
nsStyledElement::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValueOrString* aValue, bool aNotify)
{
if (aNamespaceID == kNameSpaceID_None) {
if (aName == nsGkAtoms::style) {
if (aValue) {
SetMayHaveStyle();
}
}
}
return nsStyledElementBase::BeforeSetAttr(aNamespaceID, aName, aValue,
aNotify);
}
nsresult nsresult
nsStyledElement::SetInlineStyleDeclaration(DeclarationBlock* aDeclaration, nsStyledElement::SetInlineStyleDeclaration(DeclarationBlock* aDeclaration,
const nsAString* aSerialized, const nsAString* aSerialized,

View file

@ -80,6 +80,10 @@ protected:
* document. * document.
*/ */
nsresult ReparseStyleAttribute(bool aForceInDataDoc); nsresult ReparseStyleAttribute(bool aForceInDataDoc);
virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValueOrString* aValue,
bool aNotify) override;
}; };
NS_DEFINE_STATIC_IID_ACCESSOR(nsStyledElement, NS_STYLED_ELEMENT_IID) NS_DEFINE_STATIC_IID_ACCESSOR(nsStyledElement, NS_STYLED_ELEMENT_IID)

View file

@ -14,6 +14,7 @@
#include "nsContentUtils.h" #include "nsContentUtils.h"
#include "nsGkAtoms.h" #include "nsGkAtoms.h"
#include "nsHTMLDNSPrefetch.h" #include "nsHTMLDNSPrefetch.h"
#include "nsAttrValueOrString.h"
#include "nsIDocument.h" #include "nsIDocument.h"
#include "nsIPresShell.h" #include "nsIPresShell.h"
#include "nsPresContext.h" #include "nsPresContext.h"
@ -372,84 +373,37 @@ HTMLAnchorElement::GetHrefURI() const
} }
nsresult nsresult
HTMLAnchorElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, HTMLAnchorElement::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue, const nsAttrValueOrString* aValue,
bool aNotify) bool aNotify)
{ {
bool reset = false; if (aNamespaceID == kNameSpaceID_None) {
if (aName == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) { if (aName == nsGkAtoms::href) {
// If we do not have a cached URI, we have some value here so we must reset
// our link state after calling the parent.
if (!Link::HasCachedURI()) {
reset = true;
}
// However, if we have a cached URI, we'll want to see if the value changed.
else {
nsAutoString val;
GetHref(val);
if (!val.Equals(aValue)) {
reset = true;
}
}
if (reset) {
CancelDNSPrefetch(HTML_ANCHOR_DNS_PREFETCH_DEFERRED, CancelDNSPrefetch(HTML_ANCHOR_DNS_PREFETCH_DEFERRED,
HTML_ANCHOR_DNS_PREFETCH_REQUESTED); HTML_ANCHOR_DNS_PREFETCH_REQUESTED);
} }
} }
nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, return nsGenericHTMLElement::BeforeSetAttr(aNamespaceID, aName, aValue,
aValue, aNotify); aNotify);
// The ordering of the parent class's SetAttr call and Link::ResetLinkState
// is important here! The attribute is not set until SetAttr returns, and
// we will need the updated attribute value because notifying the document
// that content states have changed will call IntrinsicState, which will try
// to get updated information about the visitedness from Link.
if (reset) {
Link::ResetLinkState(!!aNotify, true);
if (IsInComposedDoc()) {
TryDNSPrefetch();
}
}
return rv;
} }
nsresult nsresult
HTMLAnchorElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, HTMLAnchorElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
bool aNotify) const nsAttrValue* aValue,
const nsAttrValue* aOldValue, bool aNotify)
{ {
bool href = if (aNamespaceID == kNameSpaceID_None) {
(aAttribute == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID); if (aName == nsGkAtoms::href) {
Link::ResetLinkState(aNotify, !!aValue);
if (href) { if (aValue && IsInComposedDoc()) {
CancelDNSPrefetch(HTML_ANCHOR_DNS_PREFETCH_DEFERRED, TryDNSPrefetch();
HTML_ANCHOR_DNS_PREFETCH_REQUESTED); }
}
} }
nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID, aAttribute, return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName,
aNotify); aValue, aOldValue, aNotify);
// The ordering of the parent class's UnsetAttr call and Link::ResetLinkState
// is important here! The attribute is not unset until UnsetAttr returns, and
// we will need the updated attribute value because notifying the document
// that content states have changed will call IntrinsicState, which will try
// to get updated information about the visitedness from Link.
if (href) {
Link::ResetLinkState(!!aNotify, false);
}
return rv;
}
bool
HTMLAnchorElement::ParseAttribute(int32_t aNamespaceID,
nsIAtom* aAttribute,
const nsAString& aValue,
nsAttrValue& aResult)
{
return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
aResult);
} }
EventStates EventStates

View file

@ -66,20 +66,13 @@ public:
virtual void GetLinkTarget(nsAString& aTarget) override; virtual void GetLinkTarget(nsAString& aTarget) override;
virtual already_AddRefed<nsIURI> GetHrefURI() const override; virtual already_AddRefed<nsIURI> GetHrefURI() const override;
nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName,
const nsAString& aValue, bool aNotify) const nsAttrValueOrString* aValue,
{ bool aNotify) override;
return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify); virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
} const nsAttrValue* aValue,
virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, const nsAttrValue* aOldValue,
nsIAtom* aPrefix, const nsAString& aValue, bool aNotify) override;
bool aNotify) override;
virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
bool aNotify) override;
virtual bool ParseAttribute(int32_t aNamespaceID,
nsIAtom* aAttribute,
const nsAString& aValue,
nsAttrValue& aResult) override;
virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override;

View file

@ -153,42 +153,22 @@ HTMLAreaElement::UnbindFromTree(bool aDeep, bool aNullParent)
} }
nsresult nsresult
HTMLAreaElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, HTMLAreaElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue, const nsAttrValue* aValue,
bool aNotify) const nsAttrValue* aOldValue, bool aNotify)
{ {
nsresult rv = if (aNamespaceID == kNameSpaceID_None) {
nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue, aNotify); // This must happen after the attribute is set. We will need the updated
// attribute value because notifying the document that content states have
// The ordering of the parent class's SetAttr call and Link::ResetLinkState // changed will call IntrinsicState, which will try to get updated
// is important here! The attribute is not set until SetAttr returns, and // information about the visitedness from Link.
// we will need the updated attribute value because notifying the document if (aName == nsGkAtoms::href) {
// that content states have changed will call IntrinsicState, which will try Link::ResetLinkState(aNotify, !!aValue);
// to get updated information about the visitedness from Link. }
if (aName == nsGkAtoms::href && aNameSpaceID == kNameSpaceID_None) {
Link::ResetLinkState(!!aNotify, true);
} }
return rv; return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName, aValue,
} aOldValue, aNotify);
nsresult
HTMLAreaElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
bool aNotify)
{
nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID, aAttribute,
aNotify);
// The ordering of the parent class's UnsetAttr call and Link::ResetLinkState
// is important here! The attribute is not unset until UnsetAttr returns, and
// we will need the updated attribute value because notifying the document
// that content states have changed will call IntrinsicState, which will try
// to get updated information about the visitedness from Link.
if (aAttribute == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) {
Link::ResetLinkState(!!aNotify, false);
}
return rv;
} }
#define IMPL_URI_PART(_part) \ #define IMPL_URI_PART(_part) \

View file

@ -56,16 +56,6 @@ public:
bool aCompileEventHandlers) override; bool aCompileEventHandlers) override;
virtual void UnbindFromTree(bool aDeep = true, virtual void UnbindFromTree(bool aDeep = true,
bool aNullParent = true) override; bool aNullParent = true) override;
nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAString& aValue, bool aNotify)
{
return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify);
}
virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
bool aNotify) override;
virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
bool aNotify) override;
virtual nsresult Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) const override; virtual nsresult Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) const override;
@ -186,6 +176,11 @@ protected:
virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValue* aValue,
const nsAttrValue* aOldValue,
bool aNotify) override;
RefPtr<nsDOMTokenList > mRelList; RefPtr<nsDOMTokenList > mRelList;
}; };

View file

@ -446,38 +446,37 @@ NS_IMPL_UINT_ATTR_DEFAULT_VALUE(HTMLCanvasElement, Height, height, DEFAULT_CANVA
NS_IMPL_BOOL_ATTR(HTMLCanvasElement, MozOpaque, moz_opaque) NS_IMPL_BOOL_ATTR(HTMLCanvasElement, MozOpaque, moz_opaque)
nsresult nsresult
HTMLCanvasElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, HTMLCanvasElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue, const nsAttrValue* aValue,
bool aNotify) const nsAttrValue* aOldValue, bool aNotify)
{ {
nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue, AfterMaybeChangeAttr(aNamespaceID, aName, aNotify);
aNotify);
if (NS_SUCCEEDED(rv) && mCurrentContext &&
aNameSpaceID == kNameSpaceID_None &&
(aName == nsGkAtoms::width || aName == nsGkAtoms::height || aName == nsGkAtoms::moz_opaque))
{
ErrorResult dummy;
rv = UpdateContext(nullptr, JS::NullHandleValue, dummy);
NS_ENSURE_SUCCESS(rv, rv);
}
return rv; return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName, aValue,
aOldValue, aNotify);
} }
nsresult nsresult
HTMLCanvasElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName, HTMLCanvasElement::OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName,
bool aNotify) const nsAttrValueOrString& aValue,
bool aNotify)
{ {
nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID, aName, aNotify); AfterMaybeChangeAttr(aNamespaceID, aName, aNotify);
if (NS_SUCCEEDED(rv) && mCurrentContext &&
aNameSpaceID == kNameSpaceID_None && return nsGenericHTMLElement::OnAttrSetButNotChanged(aNamespaceID, aName,
(aName == nsGkAtoms::width || aName == nsGkAtoms::height || aName == nsGkAtoms::moz_opaque)) aValue, aNotify);
{ }
void
HTMLCanvasElement::AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName,
bool aNotify)
{
if (mCurrentContext && aNamespaceID == kNameSpaceID_None &&
(aName == nsGkAtoms::width || aName == nsGkAtoms::height ||
aName == nsGkAtoms::moz_opaque)) {
ErrorResult dummy; ErrorResult dummy;
rv = UpdateContext(nullptr, JS::NullHandleValue, dummy); UpdateContext(nullptr, JS::NullHandleValue, dummy);
NS_ENSURE_SUCCESS(rv, rv);
} }
return rv;
} }
void void

View file

@ -295,20 +295,6 @@ public:
nsAttrValue& aResult) override; nsAttrValue& aResult) override;
nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute, int32_t aModType) const override; nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute, int32_t aModType) const override;
// SetAttr override. C++ is stupid, so have to override both
// overloaded methods.
nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAString& aValue, bool aNotify)
{
return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify);
}
virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
bool aNotify) override;
virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName,
bool aNotify) override;
virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override;
nsresult CopyInnerTo(mozilla::dom::Element* aDest); nsresult CopyInnerTo(mozilla::dom::Element* aDest);
@ -373,6 +359,14 @@ protected:
nsISupports** aResult); nsISupports** aResult);
void CallPrintCallback(); void CallPrintCallback();
virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValue* aValue,
const nsAttrValue* aOldValue,
bool aNotify) override;
virtual nsresult OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValueOrString& aValue,
bool aNotify) override;
AsyncCanvasRenderer* GetAsyncCanvasRenderer(); AsyncCanvasRenderer* GetAsyncCanvasRenderer();
bool mResetLayer; bool mResetLayer;
@ -406,6 +400,18 @@ public:
CanvasContextType GetCurrentContextType() { CanvasContextType GetCurrentContextType() {
return mCurrentContextType; return mCurrentContextType;
} }
private:
/**
* This function is called by AfterSetAttr and OnAttrSetButNotChanged.
* This function will be called by AfterSetAttr whether the attribute is being
* set or unset.
*
* @param aNamespaceID the namespace of the attr being set
* @param aName the localname of the attribute being set
* @param aNotify Whether we plan to notify document observers.
*/
void AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName, bool aNotify);
}; };
class HTMLCanvasPrintState final : public nsWrapperCache class HTMLCanvasPrintState final : public nsWrapperCache

View file

@ -210,75 +210,63 @@ IsValidContentSelectors(nsCSSSelector* aSelector)
} }
nsresult nsresult
HTMLContentElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, HTMLContentElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue, const nsAttrValue* aValue,
bool aNotify) const nsAttrValue* aOldValue, bool aNotify)
{ {
nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::select) {
aValue, aNotify); if (aValue) {
NS_ENSURE_SUCCESS(rv, rv); // Select attribute was updated, the insertion point may match different
// elements.
nsIDocument* doc = OwnerDoc();
nsCSSParser parser(doc->CSSLoader());
if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::select) { mValidSelector = true;
// Select attribute was updated, the insertion point may match different mSelectorList = nullptr;
// elements.
nsIDocument* doc = OwnerDoc();
nsCSSParser parser(doc->CSSLoader());
mValidSelector = true; nsAutoString valueStr;
mSelectorList = nullptr; aValue->ToString(valueStr);
nsresult rv = parser.ParseSelectorString(valueStr,
doc->GetDocumentURI(),
// Bug 11240
0, // XXX get the line number!
getter_Transfers(mSelectorList));
nsresult rv = parser.ParseSelectorString(aValue, // We don't want to return an exception if parsing failed because
doc->GetDocumentURI(), // the spec does not define it as an exception case.
// Bug 11240 if (NS_SUCCEEDED(rv)) {
0, // XXX get the line number! // Ensure that all the selectors are valid
getter_Transfers(mSelectorList)); nsCSSSelectorList* selectors = mSelectorList;
while (selectors) {
// We don't want to return an exception if parsing failed because if (!IsValidContentSelectors(selectors->mSelectors)) {
// the spec does not define it as an exception case. // If we have an invalid selector, we can not match anything.
if (NS_SUCCEEDED(rv)) { mValidSelector = false;
// Ensure that all the selectors are valid mSelectorList = nullptr;
nsCSSSelectorList* selectors = mSelectorList; break;
while (selectors) { }
if (!IsValidContentSelectors(selectors->mSelectors)) { selectors = selectors->mNext;
// If we have an invalid selector, we can not match anything.
mValidSelector = false;
mSelectorList = nullptr;
break;
} }
selectors = selectors->mNext; }
ShadowRoot* containingShadow = GetContainingShadow();
if (containingShadow) {
containingShadow->DistributeAllNodes();
}
} else {
// The select attribute was removed. This insertion point becomes
// a universal selector.
mValidSelector = true;
mSelectorList = nullptr;
ShadowRoot* containingShadow = GetContainingShadow();
if (containingShadow) {
containingShadow->DistributeAllNodes();
} }
} }
ShadowRoot* containingShadow = GetContainingShadow();
if (containingShadow) {
containingShadow->DistributeAllNodes();
}
} }
return NS_OK; return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName, aValue,
} aOldValue, aNotify);
nsresult
HTMLContentElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
bool aNotify)
{
nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID,
aAttribute, aNotify);
NS_ENSURE_SUCCESS(rv, rv);
if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::select) {
// The select attribute was removed. This insertion point becomes
// a universal selector.
mValidSelector = true;
mSelectorList = nullptr;
ShadowRoot* containingShadow = GetContainingShadow();
if (containingShadow) {
containingShadow->DistributeAllNodes();
}
}
return NS_OK;
} }
bool bool

View file

@ -63,13 +63,6 @@ public:
void InsertMatchedNode(uint32_t aIndex, nsIContent* aContent); void InsertMatchedNode(uint32_t aIndex, nsIContent* aContent);
void ClearMatchedNodes(); void ClearMatchedNodes();
virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
bool aNotify) override;
virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
bool aNotify) override;
// WebIDL methods. // WebIDL methods.
already_AddRefed<DistributedContentList> GetDistributedNodes(); already_AddRefed<DistributedContentList> GetDistributedNodes();
void GetSelect(nsAString& aSelect) void GetSelect(nsAString& aSelect)
@ -95,6 +88,11 @@ protected:
*/ */
void UpdateFallbackDistribution(); void UpdateFallbackDistribution();
virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValue* aValue,
const nsAttrValue* aOldValue,
bool aNotify) override;
/** /**
* An array of nodes from the ShadowRoot host that match the * An array of nodes from the ShadowRoot host that match the
* content insertion selector. * content insertion selector.

View file

@ -189,27 +189,31 @@ HTMLFormElement::GetElements(nsIDOMHTMLCollection** aElements)
} }
nsresult nsresult
HTMLFormElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, HTMLFormElement::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue, const nsAttrValueOrString* aValue, bool aNotify)
bool aNotify)
{ {
if ((aName == nsGkAtoms::action || aName == nsGkAtoms::target) && if (aNamespaceID == kNameSpaceID_None) {
aNameSpaceID == kNameSpaceID_None) { if (aName == nsGkAtoms::action || aName == nsGkAtoms::target) {
if (mPendingSubmission) { // This check is mostly to preserve previous behavior.
// aha, there is a pending submission that means we're in if (aValue) {
// the script and we need to flush it. let's tell it if (mPendingSubmission) {
// that the event was ignored to force the flush. // aha, there is a pending submission that means we're in
// the second argument is not playing a role at all. // the script and we need to flush it. let's tell it
FlushPendingSubmission(); // that the event was ignored to force the flush.
// the second argument is not playing a role at all.
FlushPendingSubmission();
}
// Don't forget we've notified the password manager already if the
// page sets the action/target in the during submit. (bug 343182)
bool notifiedObservers = mNotifiedObservers;
ForgetCurrentSubmission();
mNotifiedObservers = notifiedObservers;
}
} }
// Don't forget we've notified the password manager already if the
// page sets the action/target in the during submit. (bug 343182)
bool notifiedObservers = mNotifiedObservers;
ForgetCurrentSubmission();
mNotifiedObservers = notifiedObservers;
} }
return nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue,
aNotify); return nsGenericHTMLElement::BeforeSetAttr(aNamespaceID, aName, aValue,
aNotify);
} }
nsresult nsresult

View file

@ -105,14 +105,9 @@ public:
bool aCompileEventHandlers) override; bool aCompileEventHandlers) override;
virtual void UnbindFromTree(bool aDeep = true, virtual void UnbindFromTree(bool aDeep = true,
bool aNullParent = true) override; bool aNullParent = true) override;
nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName,
const nsAString& aValue, bool aNotify) const nsAttrValueOrString* aValue,
{ bool aNotify) override;
return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify);
}
virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
bool aNotify) override;
virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValue* aValue, const nsAttrValue* aValue,
const nsAttrValue* aOldValue, const nsAttrValue* aOldValue,

View file

@ -9,6 +9,7 @@
#include "mozilla/dom/EventHandlerBinding.h" #include "mozilla/dom/EventHandlerBinding.h"
#include "nsGlobalWindow.h" #include "nsGlobalWindow.h"
#include "mozilla/UniquePtrExtensions.h" #include "mozilla/UniquePtrExtensions.h"
#include "nsAttrValueOrString.h"
NS_IMPL_NS_NEW_HTML_ELEMENT(FrameSet) NS_IMPL_NS_NEW_HTML_ELEMENT(FrameSet)
@ -65,43 +66,45 @@ HTMLFrameSetElement::GetRows(nsAString& aRows)
} }
nsresult nsresult
HTMLFrameSetElement::SetAttr(int32_t aNameSpaceID, HTMLFrameSetElement::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName,
nsIAtom* aAttribute, const nsAttrValueOrString* aValue,
nsIAtom* aPrefix, bool aNotify)
const nsAString& aValue,
bool aNotify)
{ {
nsresult rv;
/* The main goal here is to see whether the _number_ of rows or /* The main goal here is to see whether the _number_ of rows or
* columns has changed. If it has, we need to reframe; otherwise * columns has changed. If it has, we need to reframe; otherwise
* we want to reflow. So we set mCurrentRowColHint here, then call * we want to reflow.
* nsGenericHTMLElement::SetAttr, which will end up calling * Ideally, the style hint would be changed back to reflow after the reframe
* GetAttributeChangeHint and notifying layout with that hint. * has been performed. Unfortunately, however, the reframe will be performed
* Once nsGenericHTMLElement::SetAttr returns, we want to go back to our * by the call to nsNodeUtils::AttributeChanged, which occurs *after*
* normal hint, which is NS_STYLE_HINT_REFLOW. * AfterSetAttr is called, leaving us with no convenient way of changing the
* value back to reflow afterwards. However, nsNodeUtils::AttributeChanged is
* effectively the only consumer of this value, so as long as we always set
* the value correctly here, we should be fine.
*/ */
if (aAttribute == nsGkAtoms::rows && aNameSpaceID == kNameSpaceID_None) { mCurrentRowColHint = NS_STYLE_HINT_REFLOW;
int32_t oldRows = mNumRows; if (aNamespaceID == kNameSpaceID_None) {
ParseRowCol(aValue, mNumRows, &mRowSpecs); if (aName == nsGkAtoms::rows) {
if (aValue) {
int32_t oldRows = mNumRows;
ParseRowCol(aValue->String(), mNumRows, &mRowSpecs);
if (mNumRows != oldRows) { if (mNumRows != oldRows) {
mCurrentRowColHint = nsChangeHint_ReconstructFrame; mCurrentRowColHint = nsChangeHint_ReconstructFrame;
} }
} else if (aAttribute == nsGkAtoms::cols && }
aNameSpaceID == kNameSpaceID_None) { } else if (aName == nsGkAtoms::cols) {
int32_t oldCols = mNumCols; if (aValue) {
ParseRowCol(aValue, mNumCols, &mColSpecs); int32_t oldCols = mNumCols;
ParseRowCol(aValue->String(), mNumCols, &mColSpecs);
if (mNumCols != oldCols) { if (mNumCols != oldCols) {
mCurrentRowColHint = nsChangeHint_ReconstructFrame; mCurrentRowColHint = nsChangeHint_ReconstructFrame;
}
}
} }
} }
rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aAttribute, aPrefix, return nsGenericHTMLElement::BeforeSetAttr(aNamespaceID, aName, aValue, aNotify);
aValue, aNotify);
mCurrentRowColHint = NS_STYLE_HINT_REFLOW;
return rv;
} }
nsresult nsresult

View file

@ -100,17 +100,6 @@ public:
#undef WINDOW_EVENT_HELPER #undef WINDOW_EVENT_HELPER
#undef EVENT #undef EVENT
// These override the SetAttr methods in nsGenericHTMLElement (need
// both here to silence compiler warnings).
nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAString& aValue, bool aNotify)
{
return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify);
}
virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
bool aNotify) override;
/** /**
* GetRowSpec is used to get the "rows" spec. * GetRowSpec is used to get the "rows" spec.
* @param out int32_t aNumValues The number of row sizes specified. * @param out int32_t aNumValues The number of row sizes specified.
@ -143,6 +132,10 @@ protected:
virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValueOrString* aValue,
bool aNotify) override;
private: private:
nsresult ParseRowCol(const nsAString& aValue, nsresult ParseRowCol(const nsAString& aValue,
int32_t& aNumSpecs, int32_t& aNumSpecs,

View file

@ -181,55 +181,50 @@ HTMLIFrameElement::GetAttributeMappingFunction() const
return &MapAttributesIntoRule; return &MapAttributesIntoRule;
} }
nsresult
HTMLIFrameElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
bool aNotify)
{
nsresult rv = nsGenericHTMLFrameElement::SetAttr(aNameSpaceID, aName,
aPrefix, aValue, aNotify);
NS_ENSURE_SUCCESS(rv, rv);
if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::srcdoc) {
// Don't propagate error here. The attribute was successfully set, that's
// what we should reflect.
LoadSrc();
}
return NS_OK;
}
nsresult nsresult
HTMLIFrameElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, HTMLIFrameElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValue* aValue, const nsAttrValue* aValue,
const nsAttrValue* aOldValue, bool aNotify) const nsAttrValue* aOldValue, bool aNotify)
{ {
if (aName == nsGkAtoms::sandbox && AfterMaybeChangeAttr(aNameSpaceID, aName, aNotify);
aNameSpaceID == kNameSpaceID_None && mFrameLoader) {
// If we have an nsFrameLoader, apply the new sandbox flags. if (aNameSpaceID == kNameSpaceID_None) {
// Since this is called after the setter, the sandbox flags have if (aName == nsGkAtoms::sandbox) {
// alreay been updated. if (mFrameLoader) {
mFrameLoader->ApplySandboxFlags(GetSandboxFlags()); // If we have an nsFrameLoader, apply the new sandbox flags.
// Since this is called after the setter, the sandbox flags have
// alreay been updated.
mFrameLoader->ApplySandboxFlags(GetSandboxFlags());
}
}
} }
return nsGenericHTMLFrameElement::AfterSetAttr(aNameSpaceID, aName, aValue, return nsGenericHTMLFrameElement::AfterSetAttr(aNameSpaceID, aName, aValue,
aOldValue, aNotify); aOldValue, aNotify);
} }
nsresult nsresult
HTMLIFrameElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, HTMLIFrameElement::OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName,
bool aNotify) const nsAttrValueOrString& aValue,
bool aNotify)
{ {
// Invoke on the superclass. AfterMaybeChangeAttr(aNamespaceID, aName, aNotify);
nsresult rv = nsGenericHTMLFrameElement::UnsetAttr(aNameSpaceID, aAttribute, aNotify);
NS_ENSURE_SUCCESS(rv, rv);
if (aNameSpaceID == kNameSpaceID_None && return nsGenericHTMLFrameElement::OnAttrSetButNotChanged(aNamespaceID, aName,
aAttribute == nsGkAtoms::srcdoc) { aValue, aNotify);
// Fall back to the src attribute, if any }
LoadSrc();
void
HTMLIFrameElement::AfterMaybeChangeAttr(int32_t aNamespaceID,
nsIAtom* aName,
bool aNotify)
{
if (aNamespaceID == kNameSpaceID_None) {
if (aName == nsGkAtoms::srcdoc) {
// Don't propagate errors from LoadSrc. The attribute was successfully
// set/unset, that's what we should reflect.
LoadSrc();
}
} }
return NS_OK;
} }
uint32_t uint32_t

View file

@ -46,21 +46,6 @@ public:
virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override;
nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAString& aValue, bool aNotify)
{
return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify);
}
virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
bool aNotify) override;
virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValue* aValue,
const nsAttrValue* aOldValue,
bool aNotify) override;
virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
bool aNotify) override;
uint32_t GetSandboxFlags(); uint32_t GetSandboxFlags();
// Web IDL binding methods // Web IDL binding methods
@ -197,11 +182,30 @@ protected:
virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValue* aValue,
const nsAttrValue* aOldValue,
bool aNotify) override;
virtual nsresult OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValueOrString& aValue,
bool aNotify) override;
private: private:
static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes, static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
nsRuleData* aData); nsRuleData* aData);
static const DOMTokenListSupportedToken sSupportedSandboxTokens[]; static const DOMTokenListSupportedToken sSupportedSandboxTokens[];
/**
* This function is called by AfterSetAttr and OnAttrSetButNotChanged.
* This function will be called by AfterSetAttr whether the attribute is being
* set or unset.
*
* @param aNamespaceID the namespace of the attr being set
* @param aName the localname of the attribute being set
* @param aNotify Whether we plan to notify document observers.
*/
void AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName, bool aNotify);
}; };
} // namespace dom } // namespace dom

View file

@ -112,6 +112,7 @@ private:
HTMLImageElement::HTMLImageElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) HTMLImageElement::HTMLImageElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
: nsGenericHTMLElement(aNodeInfo) : nsGenericHTMLElement(aNodeInfo)
, mForm(nullptr) , mForm(nullptr)
, mForceReload(false)
, mInDocResponsiveContent(false) , mInDocResponsiveContent(false)
, mCurrentDensity(1.0) , mCurrentDensity(1.0)
{ {
@ -372,6 +373,9 @@ HTMLImageElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValueOrString* aValue, const nsAttrValueOrString* aValue,
bool aNotify) bool aNotify)
{ {
if (aValue) {
BeforeMaybeChangeAttr(aNameSpaceID, aName, *aValue, aNotify);
}
if (aNameSpaceID == kNameSpaceID_None && mForm && if (aNameSpaceID == kNameSpaceID_None && mForm &&
(aName == nsGkAtoms::name || aName == nsGkAtoms::id)) { (aName == nsGkAtoms::name || aName == nsGkAtoms::id)) {
@ -394,6 +398,10 @@ HTMLImageElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValue* aValue, const nsAttrValue* aValue,
const nsAttrValue* aOldValue, bool aNotify) const nsAttrValue* aOldValue, bool aNotify)
{ {
if (aValue) {
AfterMaybeChangeAttr(aNameSpaceID, aName, aNotify);
}
if (aNameSpaceID == kNameSpaceID_None && mForm && if (aNameSpaceID == kNameSpaceID_None && mForm &&
(aName == nsGkAtoms::name || aName == nsGkAtoms::id) && (aName == nsGkAtoms::name || aName == nsGkAtoms::id) &&
aValue && !aValue->IsEmptyString()) { aValue && !aValue->IsEmptyString()) {
@ -437,6 +445,117 @@ HTMLImageElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
aValue, aOldValue, aNotify); aValue, aOldValue, aNotify);
} }
nsresult
HTMLImageElement::OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValueOrString& aValue,
bool aNotify)
{
BeforeMaybeChangeAttr(aNamespaceID, aName, aValue, aNotify);
AfterMaybeChangeAttr(aNamespaceID, aName, aNotify);
return nsGenericHTMLElement::OnAttrSetButNotChanged(aNamespaceID, aName,
aValue, aNotify);
}
void
HTMLImageElement::BeforeMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValueOrString& aValue,
bool aNotify)
{
// We need to force our image to reload. This must be done here, not in
// AfterSetAttr or BeforeSetAttr, because we want to do it even if the attr is
// being set to its existing value, which is normally optimized away as a
// no-op.
//
// If we are in responsive mode, we drop the forced reload behavior,
// but still trigger a image load task for img.src = img.src per
// spec.
//
// Both cases handle unsetting src in AfterSetAttr
//
// Much of this should probably happen in AfterMaybeChangeAttr.
// See Bug 1370705
if (aNamespaceID == kNameSpaceID_None &&
aName == nsGkAtoms::src) {
if (InResponsiveMode()) {
if (mResponsiveSelector &&
mResponsiveSelector->Content() == this) {
mResponsiveSelector->SetDefaultSource(aValue.String());
}
QueueImageLoadTask(true);
} else if (aNotify && OwnerDoc()->IsCurrentActiveDocument()) {
// If aNotify is false, we are coming from the parser or some such place;
// we'll get bound after all the attributes have been set, so we'll do the
// sync image load from BindToTree. Skip the LoadImage call in that case.
// Note that this sync behavior is partially removed from the spec, bug 1076583
// A hack to get animations to reset. See bug 594771.
mNewRequestsWillNeedAnimationReset = true;
// Force image loading here, so that we'll try to load the image from
// network if it's set to be not cacheable... If we change things so that
// the state gets in Element's attr-setting happen around this
// LoadImage call, we could start passing false instead of aNotify
// here.
LoadImage(aValue.String(), true, aNotify, eImageLoadType_Normal);
mNewRequestsWillNeedAnimationReset = false;
}
} else if (aNamespaceID == kNameSpaceID_None &&
aName == nsGkAtoms::crossorigin &&
aNotify) {
nsAttrValue attrValue;
ParseCORSValue(aValue.String(), attrValue);
if (GetCORSMode() != AttrValueToCORSMode(&attrValue)) {
// Force a new load of the image with the new cross origin policy.
mForceReload = true;
}
} else if (aName == nsGkAtoms::referrerpolicy &&
aNamespaceID == kNameSpaceID_None &&
aNotify) {
ReferrerPolicy referrerPolicy = AttributeReferrerPolicyFromString(aValue.String());
if (!InResponsiveMode() &&
referrerPolicy != RP_Unset &&
referrerPolicy != GetImageReferrerPolicy()) {
// XXX: Bug 1076583 - We still use the older synchronous algorithm
// Because referrerPolicy is not treated as relevant mutations, setting
// the attribute will neither trigger a reload nor update the referrer
// policy of the loading channel (whether it has previously completed or
// not). Force a new load of the image with the new referrerpolicy.
mForceReload = true;
}
}
return;
}
void
HTMLImageElement::AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName,
bool aNotify)
{
// Because we load image synchronously in non-responsive-mode, we need to do
// reload after the attribute has been set if the reload is triggerred by
// cross origin changing.
if (mForceReload) {
mForceReload = false;
if (InResponsiveMode()) {
// per spec, full selection runs when this changes, even though
// it doesn't directly affect the source selection
QueueImageLoadTask(true);
} else if (OwnerDoc()->IsCurrentActiveDocument()) {
// Bug 1076583 - We still use the older synchronous algorithm in
// non-responsive mode. Force a new load of the image with the
// new cross origin policy
ForceReload(aNotify);
}
}
return;
}
nsresult nsresult
HTMLImageElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) HTMLImageElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
{ {
@ -489,97 +608,6 @@ HTMLImageElement::IsHTMLFocusable(bool aWithMouse,
return false; return false;
} }
nsresult
HTMLImageElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
bool aNotify)
{
bool forceReload = false;
// We need to force our image to reload. This must be done here, not in
// AfterSetAttr or BeforeSetAttr, because we want to do it even if the attr is
// being set to its existing value, which is normally optimized away as a
// no-op.
//
// If we are in responsive mode, we drop the forced reload behavior,
// but still trigger a image load task for img.src = img.src per
// spec.
//
// Both cases handle unsetting src in AfterSetAttr
if (aNameSpaceID == kNameSpaceID_None &&
aName == nsGkAtoms::src) {
if (InResponsiveMode()) {
if (mResponsiveSelector &&
mResponsiveSelector->Content() == this) {
mResponsiveSelector->SetDefaultSource(aValue);
}
QueueImageLoadTask(true);
} else if (aNotify) {
// If aNotify is false, we are coming from the parser or some such place;
// we'll get bound after all the attributes have been set, so we'll do the
// sync image load from BindToTree. Skip the LoadImage call in that case.
// Note that this sync behavior is partially removed from the spec, bug 1076583
// A hack to get animations to reset. See bug 594771.
mNewRequestsWillNeedAnimationReset = true;
// Force image loading here, so that we'll try to load the image from
// network if it's set to be not cacheable... If we change things so that
// the state gets in Element's attr-setting happen around this
// LoadImage call, we could start passing false instead of aNotify
// here.
LoadImage(aValue, true, aNotify, eImageLoadType_Normal);
mNewRequestsWillNeedAnimationReset = false;
}
} else if (aNameSpaceID == kNameSpaceID_None &&
aName == nsGkAtoms::crossorigin &&
aNotify) {
nsAttrValue attrValue;
ParseCORSValue(aValue, attrValue);
if (GetCORSMode() != AttrValueToCORSMode(&attrValue)) {
// Force a new load of the image with the new cross origin policy.
forceReload = true;
}
} else if (aName == nsGkAtoms::referrerpolicy &&
aNameSpaceID == kNameSpaceID_None &&
aNotify) {
ReferrerPolicy referrerPolicy = AttributeReferrerPolicyFromString(aValue);
if (!InResponsiveMode() &&
referrerPolicy != RP_Unset &&
referrerPolicy != GetImageReferrerPolicy()) {
// XXX: Bug 1076583 - We still use the older synchronous algorithm
// Because referrerPolicy is not treated as relevant mutations, setting
// the attribute will neither trigger a reload nor update the referrer
// policy of the loading channel (whether it has previously completed or
// not). Force a new load of the image with the new referrerpolicy.
forceReload = true;
}
}
nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix,
aValue, aNotify);
// Because we load image synchronously in non-responsive-mode, we need to do
// reload after the attribute has been set if the reload is triggerred by
// cross origin changing.
if (forceReload) {
if (InResponsiveMode()) {
// per spec, full selection runs when this changes, even though
// it doesn't directly affect the source selection
QueueImageLoadTask(true);
} else {
// Bug 1076583 - We still use the older synchronous algorithm in
// non-responsive mode. Force a new load of the image with the
// new cross origin policy
ForceReload(aNotify);
}
}
return rv;
}
nsresult nsresult
HTMLImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, HTMLImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent, nsIContent* aBindingParent,

View file

@ -75,17 +75,6 @@ public:
bool IsHTMLFocusable(bool aWithMouse, bool *aIsFocusable, int32_t *aTabIndex) override; bool IsHTMLFocusable(bool aWithMouse, bool *aIsFocusable, int32_t *aTabIndex) override;
// SetAttr override. C++ is stupid, so have to override both
// overloaded methods.
nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAString& aValue, bool aNotify)
{
return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify);
}
virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
bool aNotify) override;
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent, nsIContent* aBindingParent,
bool aCompileEventHandlers) override; bool aCompileEventHandlers) override;
@ -352,6 +341,10 @@ protected:
const nsAttrValue* aOldValue, const nsAttrValue* aOldValue,
bool aNotify) override; bool aNotify) override;
virtual nsresult OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValueOrString& aValue,
bool aNotify) override;
// This is a weak reference that this element and the HTMLFormElement // This is a weak reference that this element and the HTMLFormElement
// cooperate in maintaining. // cooperate in maintaining.
HTMLFormElement* mForm; HTMLFormElement* mForm;
@ -365,6 +358,36 @@ private:
static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes, static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
nsRuleData* aData); nsRuleData* aData);
/**
* This function is called by BeforeSetAttr and OnAttrSetButNotChanged.
* It will not be called if the value is being unset.
*
* @param aNamespaceID the namespace of the attr being set
* @param aName the localname of the attribute being set
* @param aValue the value it's being set to represented as either a string or
* a parsed nsAttrValue.
* @param aNotify Whether we plan to notify document observers.
*/
void BeforeMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValueOrString& aValue,
bool aNotify);
/**
* This function is called by AfterSetAttr and OnAttrSetButNotChanged.
* It will not be called if the value is being unset.
*
* @param aNamespaceID the namespace of the attr being set
* @param aName the localname of the attribute being set
* @param aNotify Whether we plan to notify document observers.
*/
void AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName,
bool aNotify);
/**
* Used by BeforeMaybeChangeAttr and AfterMaybeChangeAttr to keep track of
* whether a reload needs to be forced after an attribute change that is
* currently in progress.
*/
bool mForceReload;
bool mInDocResponsiveContent; bool mInDocResponsiveContent;
RefPtr<ImageLoadTask> mPendingImageLoadTask; RefPtr<ImageLoadTask> mPendingImageLoadTask;

View file

@ -70,21 +70,6 @@ HTMLLegendElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
return retval; return retval;
} }
nsresult
HTMLLegendElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
nsIAtom* aPrefix, const nsAString& aValue,
bool aNotify)
{
return nsGenericHTMLElement::SetAttr(aNameSpaceID, aAttribute,
aPrefix, aValue, aNotify);
}
nsresult
HTMLLegendElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
bool aNotify)
{
return nsGenericHTMLElement::UnsetAttr(aNameSpaceID, aAttribute, aNotify);
}
nsresult nsresult
HTMLLegendElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, HTMLLegendElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent, nsIContent* aBindingParent,

View file

@ -42,16 +42,6 @@ public:
nsAttrValue& aResult) override; nsAttrValue& aResult) override;
virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute, virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute,
int32_t aModType) const override; int32_t aModType) const override;
nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAString& aValue, bool aNotify)
{
return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify);
}
virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
bool aNotify) override;
virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
bool aNotify) override;
virtual nsresult Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) const override; virtual nsresult Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) const override;

View file

@ -3726,80 +3726,76 @@ int32_t HTMLMediaElement::TabIndexDefault()
return 0; return 0;
} }
nsresult HTMLMediaElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
bool aNotify)
{
nsresult rv =
nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue,
aNotify);
if (NS_FAILED(rv))
return rv;
if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::src) {
DoLoad();
}
if (aNotify && aNameSpaceID == kNameSpaceID_None) {
if (aName == nsGkAtoms::autoplay) {
StopSuspendingAfterFirstFrame();
CheckAutoplayDataReady();
// This attribute can affect AddRemoveSelfReference
AddRemoveSelfReference();
UpdatePreloadAction();
} else if (aName == nsGkAtoms::preload) {
UpdatePreloadAction();
}
}
return rv;
}
nsresult HTMLMediaElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttr,
bool aNotify)
{
nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID, aAttr, aNotify);
if (NS_FAILED(rv))
return rv;
if (aNotify && aNameSpaceID == kNameSpaceID_None) {
if (aAttr == nsGkAtoms::autoplay) {
// This attribute can affect AddRemoveSelfReference
AddRemoveSelfReference();
UpdatePreloadAction();
} else if (aAttr == nsGkAtoms::preload) {
UpdatePreloadAction();
}
}
return rv;
}
nsresult nsresult
HTMLMediaElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, HTMLMediaElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValue* aValue, const nsAttrValue* aValue,
const nsAttrValue* aOldValue, bool aNotify) const nsAttrValue* aOldValue, bool aNotify)
{ {
if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::src) { if (aNameSpaceID == kNameSpaceID_None) {
mSrcMediaSource = nullptr; if (aName == nsGkAtoms::src) {
if (aValue) { mSrcMediaSource = nullptr;
nsString srcStr = aValue->GetStringValue(); if (aValue) {
nsCOMPtr<nsIURI> uri; nsString srcStr = aValue->GetStringValue();
NewURIFromString(srcStr, getter_AddRefs(uri)); nsCOMPtr<nsIURI> uri;
if (uri && IsMediaSourceURI(uri)) { NewURIFromString(srcStr, getter_AddRefs(uri));
nsresult rv = if (uri && IsMediaSourceURI(uri)) {
NS_GetSourceForMediaSourceURI(uri, getter_AddRefs(mSrcMediaSource)); nsresult rv =
if (NS_FAILED(rv)) { NS_GetSourceForMediaSourceURI(uri, getter_AddRefs(mSrcMediaSource));
nsAutoString spec; if (NS_FAILED(rv)) {
GetCurrentSrc(spec); nsAutoString spec;
const char16_t* params[] = { spec.get() }; GetCurrentSrc(spec);
ReportLoadError("MediaLoadInvalidURI", params, ArrayLength(params)); const char16_t* params[] = { spec.get() };
ReportLoadError("MediaLoadInvalidURI", params, ArrayLength(params));
}
} }
} }
} else if (aName == nsGkAtoms::autoplay) {
if (aNotify) {
if (aValue) {
StopSuspendingAfterFirstFrame();
CheckAutoplayDataReady();
}
// This attribute can affect AddRemoveSelfReference
AddRemoveSelfReference();
UpdatePreloadAction();
}
} else if (aName == nsGkAtoms::preload) {
UpdatePreloadAction();
} }
} }
// Since AfterMaybeChangeAttr may call DoLoad, make sure that it is called
// *after* any possible changes to mSrcMediaSource.
if (aValue) {
AfterMaybeChangeAttr(aNameSpaceID, aName, aNotify);
}
return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName,
aValue, aOldValue, aNotify); aValue, aOldValue, aNotify);
} }
nsresult
HTMLMediaElement::OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValueOrString& aValue,
bool aNotify)
{
AfterMaybeChangeAttr(aNamespaceID, aName, aNotify);
return nsGenericHTMLElement::OnAttrSetButNotChanged(aNamespaceID, aName,
aValue, aNotify);
}
void
HTMLMediaElement::AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName,
bool aNotify)
{
if (aNamespaceID == kNameSpaceID_None) {
if (aName == nsGkAtoms::src) {
DoLoad();
}
}
}
nsresult HTMLMediaElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsresult HTMLMediaElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent, nsIContent* aBindingParent,
bool aCompileEventHandlers) bool aCompileEventHandlers)

View file

@ -132,22 +132,6 @@ public:
nsIAtom* aAttribute, nsIAtom* aAttribute,
const nsAString& aValue, const nsAString& aValue,
nsAttrValue& aResult) override; nsAttrValue& aResult) override;
// SetAttr override. C++ is stupid, so have to override both
// overloaded methods.
nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAString& aValue, bool aNotify)
{
return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify);
}
virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
bool aNotify) override;
virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttr,
bool aNotify) override;
virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValue* aValue,
const nsAttrValue* aOldValue,
bool aNotify) override;
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent, nsIContent* aBindingParent,
@ -1315,6 +1299,14 @@ protected:
already_AddRefed<Promise> CreateDOMPromise(ErrorResult& aRv) const; already_AddRefed<Promise> CreateDOMPromise(ErrorResult& aRv) const;
virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValue* aValue,
const nsAttrValue* aOldValue,
bool aNotify) override;
virtual nsresult OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValueOrString& aValue,
bool aNotify) override;
// The current decoder. Load() has been called on this decoder. // The current decoder. Load() has been called on this decoder.
// At most one of mDecoder and mSrcStream can be non-null. // At most one of mDecoder and mSrcStream can be non-null.
RefPtr<MediaDecoder> mDecoder; RefPtr<MediaDecoder> mDecoder;
@ -1717,6 +1709,17 @@ private:
// We keep track of these because the load algorithm resolves/rejects all // We keep track of these because the load algorithm resolves/rejects all
// already-dispatched pending play promises. // already-dispatched pending play promises.
nsTArray<nsResolveOrRejectPendingPlayPromisesRunner*> mPendingPlayPromisesRunners; nsTArray<nsResolveOrRejectPendingPlayPromisesRunner*> mPendingPlayPromisesRunners;
private:
/**
* This function is called by AfterSetAttr and OnAttrSetButNotChanged.
* It will not be called if the value is being unset.
*
* @param aNamespaceID the namespace of the attr being set
* @param aName the localname of the attribute being set
* @param aNotify Whether we plan to notify document observers.
*/
void AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName, bool aNotify);
}; };
} // namespace dom } // namespace dom

View file

@ -298,41 +298,45 @@ HTMLObjectElement::UnbindFromTree(bool aDeep,
nsresult nsresult
HTMLObjectElement::SetAttr(int32_t aNameSpaceID, nsIAtom *aName, HTMLObjectElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
nsIAtom *aPrefix, const nsAString &aValue, const nsAttrValue* aValue,
bool aNotify) const nsAttrValue* aOldValue, bool aNotify)
{ {
nsresult rv = nsGenericHTMLFormElement::SetAttr(aNameSpaceID, aName, aPrefix, nsresult rv = AfterMaybeChangeAttr(aNamespaceID, aName, aNotify);
aValue, aNotify);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
// if aNotify is false, we are coming from the parser or some such place; return nsGenericHTMLFormElement::AfterSetAttr(aNamespaceID, aName, aValue,
// we'll get bound after all the attributes have been set, so we'll do the aOldValue, aNotify);
// object load from BindToTree/DoneAddingChildren.
// Skip the LoadObject call in that case.
// We also don't want to start loading the object when we're not yet in
// a document, just in case that the caller wants to set additional
// attributes before inserting the node into the document.
if (aNotify && IsInComposedDoc() && mIsDoneAddingChildren &&
aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::data) {
return LoadObject(aNotify, true);
}
return NS_OK;
} }
nsresult nsresult
HTMLObjectElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, HTMLObjectElement::OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName,
bool aNotify) const nsAttrValueOrString& aValue,
bool aNotify)
{ {
nsresult rv = nsGenericHTMLFormElement::UnsetAttr(aNameSpaceID, nsresult rv = AfterMaybeChangeAttr(aNamespaceID, aName, aNotify);
aAttribute, aNotify);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
// See comment in SetAttr return nsGenericHTMLFormElement::OnAttrSetButNotChanged(aNamespaceID, aName,
if (aNotify && IsInComposedDoc() && mIsDoneAddingChildren && aValue, aNotify);
aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::data) { }
return LoadObject(aNotify, true);
nsresult
HTMLObjectElement::AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName,
bool aNotify)
{
if (aNamespaceID == kNameSpaceID_None) {
// if aNotify is false, we are coming from the parser or some such place;
// we'll get bound after all the attributes have been set, so we'll do the
// object load from BindToTree/DoneAddingChildren.
// Skip the LoadObject call in that case.
// We also don't want to start loading the object when we're not yet in
// a document, just in case that the caller wants to set additional
// attributes before inserting the node into the document.
if (aNotify && IsInComposedDoc() && mIsDoneAddingChildren &&
aName == nsGkAtoms::data) {
return LoadObject(aNotify, true);
}
} }
return NS_OK; return NS_OK;

View file

@ -59,11 +59,6 @@ public:
bool aCompileEventHandlers) override; bool aCompileEventHandlers) override;
virtual void UnbindFromTree(bool aDeep = true, virtual void UnbindFromTree(bool aDeep = true,
bool aNullParent = true) override; bool aNullParent = true) override;
virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom *aName,
nsIAtom *aPrefix, const nsAString &aValue,
bool aNotify) override;
virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
bool aNotify) override;
virtual bool IsHTMLFocusable(bool aWithMouse, bool *aIsFocusable, int32_t *aTabIndex) override; virtual bool IsHTMLFocusable(bool aWithMouse, bool *aIsFocusable, int32_t *aTabIndex) override;
virtual IMEState GetDesiredIMEState() override; virtual IMEState GetDesiredIMEState() override;
@ -247,6 +242,15 @@ public:
return GetContentDocument(aSubjectPrincipal); return GetContentDocument(aSubjectPrincipal);
} }
protected:
virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValue* aValue,
const nsAttrValue* aOldValue,
bool aNotify) override;
virtual nsresult OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValueOrString& aValue,
bool aNotify) override;
private: private:
/** /**
* Calls LoadObject with the correct arguments to start the plugin load. * Calls LoadObject with the correct arguments to start the plugin load.
@ -271,6 +275,18 @@ private:
static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes, static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
nsRuleData* aData); nsRuleData* aData);
/**
* This function is called by AfterSetAttr and OnAttrSetButNotChanged.
* This function will be called by AfterSetAttr whether the attribute is being
* set or unset.
*
* @param aNamespaceID the namespace of the attr being set
* @param aName the localname of the attribute being set
* @param aNotify Whether we plan to notify document observers.
*/
nsresult AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName,
bool aNotify);
bool mIsDoneAddingChildren; bool mIsDoneAddingChildren;
}; };

View file

@ -1272,9 +1272,22 @@ HTMLSelectElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValueOrString* aValue, const nsAttrValueOrString* aValue,
bool aNotify) bool aNotify)
{ {
if (aNotify && aName == nsGkAtoms::disabled && if (aNameSpaceID == kNameSpaceID_None) {
aNameSpaceID == kNameSpaceID_None) { if (aName == nsGkAtoms::disabled) {
mDisabledChanged = true; if (aNotify) {
mDisabledChanged = true;
}
} else if (aName == nsGkAtoms::multiple) {
if (!aValue && aNotify && mSelectedIndex >= 0) {
// We're changing from being a multi-select to a single-select.
// Make sure we only have one option selected before we do that.
// Note that this needs to come before we really unset the attr,
// since SetOptionsSelectedByIndex does some bail-out type
// optimization for cases when the select is not multiple that
// would lead to only a single option getting deselected.
SetSelectedIndexInternal(mSelectedIndex, aNotify);
}
}
} }
return nsGenericHTMLFormElementWithState::BeforeSetAttr(aNameSpaceID, aName, return nsGenericHTMLFormElementWithState::BeforeSetAttr(aNameSpaceID, aName,
@ -1294,6 +1307,12 @@ HTMLSelectElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
} else if (aName == nsGkAtoms::autocomplete) { } else if (aName == nsGkAtoms::autocomplete) {
// Clear the cached @autocomplete attribute state // Clear the cached @autocomplete attribute state
mAutocompleteAttrState = nsContentUtils::eAutocompleteAttrState_Unknown; mAutocompleteAttrState = nsContentUtils::eAutocompleteAttrState_Unknown;
} else if (aName == nsGkAtoms::multiple) {
if (!aValue && aNotify) {
// We might have become a combobox; make sure _something_ gets
// selected in that case
CheckSelectSomething(aNotify);
}
} }
} }
@ -1302,37 +1321,6 @@ HTMLSelectElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
aNotify); aNotify);
} }
nsresult
HTMLSelectElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
bool aNotify)
{
if (aNotify && aNameSpaceID == kNameSpaceID_None &&
aAttribute == nsGkAtoms::multiple) {
// We're changing from being a multi-select to a single-select.
// Make sure we only have one option selected before we do that.
// Note that this needs to come before we really unset the attr,
// since SetOptionsSelectedByIndex does some bail-out type
// optimization for cases when the select is not multiple that
// would lead to only a single option getting deselected.
if (mSelectedIndex >= 0) {
SetSelectedIndexInternal(mSelectedIndex, aNotify);
}
}
nsresult rv = nsGenericHTMLFormElementWithState::UnsetAttr(aNameSpaceID, aAttribute,
aNotify);
NS_ENSURE_SUCCESS(rv, rv);
if (aNotify && aNameSpaceID == kNameSpaceID_None &&
aAttribute == nsGkAtoms::multiple) {
// We might have become a combobox; make sure _something_ gets
// selected in that case
CheckSelectSomething(aNotify);
}
return rv;
}
void void
HTMLSelectElement::DoneAddingChildren(bool aHaveNotified) HTMLSelectElement::DoneAddingChildren(bool aHaveNotified)
{ {

View file

@ -381,8 +381,6 @@ public:
const nsAttrValue* aValue, const nsAttrValue* aValue,
const nsAttrValue* aOldValue, const nsAttrValue* aOldValue,
bool aNotify) override; bool aNotify) override;
virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
bool aNotify) override;
virtual void DoneAddingChildren(bool aHaveNotified) override; virtual void DoneAddingChildren(bool aHaveNotified) override;
virtual bool IsDoneAddingChildren() override { virtual bool IsDoneAddingChildren() override {

View file

@ -231,52 +231,32 @@ SetBaseTargetUsingFirstBaseWithTarget(nsIDocument* aDocument,
} }
nsresult nsresult
HTMLSharedElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, HTMLSharedElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue, const nsAttrValue* aValue,
bool aNotify) const nsAttrValue* aOldValue, bool aNotify)
{ {
nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, if (aNamespaceID == kNameSpaceID_None) {
aValue, aNotify);
NS_ENSURE_SUCCESS(rv, rv);
// If the href attribute of a <base> tag is changing, we may need to update
// the document's base URI, which will cause all the links on the page to be
// re-resolved given the new base. If the target attribute is changing, we
// similarly need to change the base target.
if (mNodeInfo->Equals(nsGkAtoms::base) &&
aNameSpaceID == kNameSpaceID_None &&
IsInUncomposedDoc()) {
if (aName == nsGkAtoms::href) { if (aName == nsGkAtoms::href) {
SetBaseURIUsingFirstBaseWithHref(GetUncomposedDoc(), this); // If the href attribute of a <base> tag is changing, we may need to
// update the document's base URI, which will cause all the links on the
// page to be re-resolved given the new base.
// If the href is being unset (aValue is null), we will need to find a new
// <base>.
if (mNodeInfo->Equals(nsGkAtoms::base) && IsInUncomposedDoc()) {
SetBaseURIUsingFirstBaseWithHref(GetUncomposedDoc(),
aValue ? this : nullptr);
}
} else if (aName == nsGkAtoms::target) { } else if (aName == nsGkAtoms::target) {
SetBaseTargetUsingFirstBaseWithTarget(GetUncomposedDoc(), this); // The target attribute is in pretty much the same situation as the href
// attribute, above.
if (mNodeInfo->Equals(nsGkAtoms::base) && IsInUncomposedDoc()) {
SetBaseTargetUsingFirstBaseWithTarget(GetUncomposedDoc(),
aValue ? this : nullptr);
}
} }
} }
return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName, aValue,
return NS_OK; aOldValue, aNotify);
}
nsresult
HTMLSharedElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName,
bool aNotify)
{
nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID, aName, aNotify);
NS_ENSURE_SUCCESS(rv, rv);
// If we're the first <base> with an href and our href attribute is being
// unset, then we're no longer the first <base> with an href, and we need to
// find the new one. Similar for target.
if (mNodeInfo->Equals(nsGkAtoms::base) &&
aNameSpaceID == kNameSpaceID_None &&
IsInUncomposedDoc()) {
if (aName == nsGkAtoms::href) {
SetBaseURIUsingFirstBaseWithHref(GetUncomposedDoc(), nullptr);
} else if (aName == nsGkAtoms::target) {
SetBaseTargetUsingFirstBaseWithTarget(GetUncomposedDoc(), nullptr);
}
}
return NS_OK;
} }
nsresult nsresult

View file

@ -59,17 +59,6 @@ public:
nsIAtom* aAttribute, nsIAtom* aAttribute,
const nsAString& aValue, const nsAString& aValue,
nsAttrValue& aResult) override; nsAttrValue& aResult) override;
nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAString& aValue, bool aNotify)
{
return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify);
}
virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
bool aNotify) override;
virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName,
bool aNotify) override;
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent, nsIContent* aBindingParent,
@ -181,6 +170,11 @@ protected:
virtual ~HTMLSharedElement(); virtual ~HTMLSharedElement();
virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValue* aValue,
const nsAttrValue* aOldValue,
bool aNotify) override;
}; };
} // namespace dom } // namespace dom

View file

@ -161,27 +161,53 @@ HTMLSharedObjectElement::UnbindFromTree(bool aDeep,
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
} }
nsresult
HTMLSharedObjectElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValue* aValue,
const nsAttrValue* aOldValue,
bool aNotify)
{
if (aValue) {
nsresult rv = AfterMaybeChangeAttr(aNamespaceID, aName, aNotify);
NS_ENSURE_SUCCESS(rv, rv);
}
return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName, aValue,
aOldValue, aNotify);
}
nsresult nsresult
HTMLSharedObjectElement::SetAttr(int32_t aNameSpaceID, nsIAtom *aName, HTMLSharedObjectElement::OnAttrSetButNotChanged(int32_t aNamespaceID,
nsIAtom *aPrefix, const nsAString &aValue, nsIAtom* aName,
bool aNotify) const nsAttrValueOrString& aValue,
bool aNotify)
{ {
nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, nsresult rv = AfterMaybeChangeAttr(aNamespaceID, aName, aNotify);
aValue, aNotify);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
// if aNotify is false, we are coming from the parser or some such place; return nsGenericHTMLElement::OnAttrSetButNotChanged(aNamespaceID, aName,
// we'll get bound after all the attributes have been set, so we'll do the aValue, aNotify);
// object load from BindToTree/DoneAddingChildren. }
// Skip the LoadObject call in that case.
// We also don't want to start loading the object when we're not yet in nsresult
// a document, just in case that the caller wants to set additional HTMLSharedObjectElement::AfterMaybeChangeAttr(int32_t aNamespaceID,
// attributes before inserting the node into the document. nsIAtom* aName,
if (aNotify && IsInComposedDoc() && mIsDoneAddingChildren && bool aNotify)
aNameSpaceID == kNameSpaceID_None && aName == URIAttrName() {
&& !BlockEmbedContentLoading()) { if (aNamespaceID == kNameSpaceID_None) {
return LoadObject(aNotify, true); if (aName == URIAttrName()) {
// If aNotify is false, we are coming from the parser or some such place;
// we'll get bound after all the attributes have been set, so we'll do the
// object load from BindToTree/DoneAddingChildren.
// Skip the LoadObject call in that case.
// We also don't want to start loading the object when we're not yet in
// a document, just in case that the caller wants to set additional
// attributes before inserting the node into the document.
if (aNotify && IsInComposedDoc() && mIsDoneAddingChildren) {
nsresult rv = LoadObject(aNotify, true);
NS_ENSURE_SUCCESS(rv, rv);
}
}
} }
return NS_OK; return NS_OK;

View file

@ -57,9 +57,6 @@ public:
bool aCompileEventHandlers) override; bool aCompileEventHandlers) override;
virtual void UnbindFromTree(bool aDeep = true, virtual void UnbindFromTree(bool aDeep = true,
bool aNullParent = true) override; bool aNullParent = true) override;
virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom *aName,
nsIAtom *aPrefix, const nsAString &aValue,
bool aNotify) override;
virtual bool IsHTMLFocusable(bool aWithMouse, bool *aIsFocusable, int32_t *aTabIndex) override; virtual bool IsHTMLFocusable(bool aWithMouse, bool *aIsFocusable, int32_t *aTabIndex) override;
virtual IMEState GetDesiredIMEState() override; virtual IMEState GetDesiredIMEState() override;
@ -200,6 +197,16 @@ public:
* Calls LoadObject with the correct arguments to start the plugin load. * Calls LoadObject with the correct arguments to start the plugin load.
*/ */
void StartObjectLoad(bool aNotify, bool aForceLoad); void StartObjectLoad(bool aNotify, bool aForceLoad);
protected:
virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValue* aValue,
const nsAttrValue* aOldValue,
bool aNotify) override;
virtual nsresult OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValueOrString& aValue,
bool aNotify) override;
private: private:
virtual ~HTMLSharedObjectElement(); virtual ~HTMLSharedObjectElement();
@ -221,6 +228,17 @@ private:
static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes, static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
nsRuleData* aData); nsRuleData* aData);
/**
* This function is called by AfterSetAttr and OnAttrSetButNotChanged.
* It will not be called if the value is being unset.
*
* @param aNamespaceID the namespace of the attr being set
* @param aName the localname of the attribute being set
* @param aNotify Whether we plan to notify document observers.
*/
nsresult AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName,
bool aNotify);
/** /**
* Decides whether we should load embed node content. * Decides whether we should load embed node content.
* *

View file

@ -654,6 +654,43 @@ nsGenericHTMLElement::GetHrefURIForAnchors() const
return uri.forget(); return uri.forget();
} }
nsresult
nsGenericHTMLElement::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValueOrString* aValue,
bool aNotify)
{
if (aNamespaceID == kNameSpaceID_None) {
if (aName == nsGkAtoms::accesskey) {
// Have to unregister before clearing flag. See UnregAccessKey
UnregAccessKey();
if (!aValue) {
UnsetFlags(NODE_HAS_ACCESSKEY);
}
} else if (aName == nsGkAtoms::name) {
// Have to do this before clearing flag. See RemoveFromNameTable
RemoveFromNameTable();
if (!aValue || aValue->IsEmpty()) {
ClearHasName();
}
} else if (aName == nsGkAtoms::contenteditable) {
if (aValue) {
// Set this before the attribute is set so that any subclass code that
// runs before the attribute is set won't think we're missing a
// contenteditable attr when we actually have one.
SetMayHaveContentEditableAttr();
}
}
if (!aValue && IsEventAttributeName(aName)) {
if (EventListenerManager* manager = GetExistingListenerManager()) {
manager->RemoveEventHandler(aName, EmptyString());
}
}
}
return nsGenericHTMLElementBase::BeforeSetAttr(aNamespaceID, aName, aValue,
aNotify);
}
nsresult nsresult
nsGenericHTMLElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, nsGenericHTMLElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValue* aValue, const nsAttrValue* aValue,
@ -694,6 +731,34 @@ nsGenericHTMLElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
} }
} }
SetDirectionalityOnDescendants(this, dir, aNotify); SetDirectionalityOnDescendants(this, dir, aNotify);
} else if (aName == nsGkAtoms::contenteditable) {
int32_t editableCountDelta = 0;
if (aOldValue &&
(aOldValue->Equals(NS_LITERAL_STRING("true"), eIgnoreCase) ||
aOldValue->Equals(EmptyString(), eIgnoreCase))) {
editableCountDelta = -1;
}
if (aValue && (aValue->Equals(NS_LITERAL_STRING("true"), eIgnoreCase) ||
aValue->Equals(EmptyString(), eIgnoreCase))) {
++editableCountDelta;
}
ChangeEditableState(editableCountDelta);
} else if (aName == nsGkAtoms::accesskey) {
if (aValue && !aValue->Equals(EmptyString(), eIgnoreCase)) {
SetFlags(NODE_HAS_ACCESSKEY);
RegAccessKey();
}
} else if (aName == nsGkAtoms::name) {
if (aValue && !aValue->Equals(EmptyString(), eIgnoreCase) &&
CanHaveName(NodeInfo()->NameAtom())) {
// This may not be quite right because we can have subclass code run
// before here. But in practice subclasses don't care about this flag,
// and in particular selector matching does not care. Otherwise we'd
// want to handle it like we handle id attributes (in PreIdMaybeChange
// and PostIdMaybeChange).
SetHasName();
AddToNameTable(aValue->GetAtomValue());
}
} }
} }
@ -819,87 +884,6 @@ nsGenericHTMLElement::SetOn##name_(EventHandlerNonNull* handler) \
#undef FORWARDED_EVENT #undef FORWARDED_EVENT
#undef EVENT #undef EVENT
nsresult
nsGenericHTMLElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
bool aNotify)
{
bool contentEditable = aNameSpaceID == kNameSpaceID_None &&
aName == nsGkAtoms::contenteditable;
bool accessKey = aName == nsGkAtoms::accesskey &&
aNameSpaceID == kNameSpaceID_None;
int32_t change = 0;
if (contentEditable) {
change = GetContentEditableValue() == eTrue ? -1 : 0;
SetMayHaveContentEditableAttr();
}
if (accessKey) {
UnregAccessKey();
}
nsresult rv = nsStyledElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue,
aNotify);
NS_ENSURE_SUCCESS(rv, rv);
if (contentEditable) {
if (aValue.IsEmpty() || aValue.LowerCaseEqualsLiteral("true")) {
change += 1;
}
ChangeEditableState(change);
}
if (accessKey && !aValue.IsEmpty()) {
SetFlags(NODE_HAS_ACCESSKEY);
RegAccessKey();
}
return NS_OK;
}
nsresult
nsGenericHTMLElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
bool aNotify)
{
bool contentEditable = false;
int32_t contentEditableChange = 0;
// Check for event handlers
if (aNameSpaceID == kNameSpaceID_None) {
if (aAttribute == nsGkAtoms::name) {
// Have to do this before clearing flag. See RemoveFromNameTable
RemoveFromNameTable();
ClearHasName();
}
else if (aAttribute == nsGkAtoms::contenteditable) {
contentEditable = true;
contentEditableChange = GetContentEditableValue() == eTrue ? -1 : 0;
}
else if (aAttribute == nsGkAtoms::accesskey) {
// Have to unregister before clearing flag. See UnregAccessKey
UnregAccessKey();
UnsetFlags(NODE_HAS_ACCESSKEY);
}
else if (IsEventAttributeName(aAttribute)) {
if (EventListenerManager* manager = GetExistingListenerManager()) {
manager->RemoveEventHandler(aAttribute, EmptyString());
}
}
}
nsresult rv = nsGenericHTMLElementBase::UnsetAttr(aNameSpaceID, aAttribute,
aNotify);
NS_ENSURE_SUCCESS(rv, rv);
if (contentEditable) {
ChangeEditableState(contentEditableChange);
}
return NS_OK;
}
void void
nsGenericHTMLElement::GetBaseTarget(nsAString& aBaseTarget) const nsGenericHTMLElement::GetBaseTarget(nsAString& aBaseTarget) const
{ {
@ -929,19 +913,13 @@ nsGenericHTMLElement::ParseAttribute(int32_t aNamespaceID,
if (aAttribute == nsGkAtoms::name) { if (aAttribute == nsGkAtoms::name) {
// Store name as an atom. name="" means that the element has no name, // Store name as an atom. name="" means that the element has no name,
// not that it has an emptystring as the name. // not that it has an empty string as the name.
RemoveFromNameTable();
if (aValue.IsEmpty()) { if (aValue.IsEmpty()) {
ClearHasName(); ClearHasName();
return false; return false;
} }
aResult.ParseAtom(aValue); aResult.ParseAtom(aValue);
if (CanHaveName(NodeInfo()->NameAtom())) {
SetHasName();
AddToNameTable(aResult.GetAtomValue());
}
return true; return true;
} }

View file

@ -465,17 +465,6 @@ public:
virtual void UnbindFromTree(bool aDeep = true, virtual void UnbindFromTree(bool aDeep = true,
bool aNullParent = true) override; bool aNullParent = true) override;
MOZ_ALWAYS_INLINE // Avoid a crashy hook from Avast 10 Beta
nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAString& aValue, bool aNotify)
{
return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify);
}
virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
bool aNotify) override;
virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName,
bool aNotify) override;
virtual bool IsFocusableInternal(int32_t *aTabIndex, bool aWithMouse) override virtual bool IsFocusableInternal(int32_t *aTabIndex, bool aWithMouse) override
{ {
bool isFocusable = false; bool isFocusable = false;
@ -914,6 +903,9 @@ private:
void RegUnRegAccessKey(bool aDoReg); void RegUnRegAccessKey(bool aDoReg);
protected: protected:
virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValueOrString* aValue,
bool aNotify) override;
virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValue* aValue, const nsAttrValue* aValue,
const nsAttrValue* aOldValue, const nsAttrValue* aOldValue,

View file

@ -334,55 +334,6 @@ nsGenericHTMLFrameElement::UnbindFromTree(bool aDeep, bool aNullParent)
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
} }
nsresult
nsGenericHTMLFrameElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
bool aNotify)
{
nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix,
aValue, aNotify);
NS_ENSURE_SUCCESS(rv, rv);
if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::src &&
(!IsHTMLElement(nsGkAtoms::iframe) ||
!HasAttr(kNameSpaceID_None,nsGkAtoms::srcdoc))) {
// Don't propagate error here. The attribute was successfully set, that's
// what we should reflect.
LoadSrc();
} else if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::name) {
// Propagate "name" to the docshell to make browsing context names live,
// per HTML5.
nsIDocShell *docShell = mFrameLoader ? mFrameLoader->GetExistingDocShell()
: nullptr;
if (docShell) {
docShell->SetName(aValue);
}
}
return NS_OK;
}
nsresult
nsGenericHTMLFrameElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
bool aNotify)
{
// Invoke on the superclass.
nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID, aAttribute, aNotify);
NS_ENSURE_SUCCESS(rv, rv);
if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::name) {
// Propagate "name" to the docshell to make browsing context names live,
// per HTML5.
nsIDocShell *docShell = mFrameLoader ? mFrameLoader->GetExistingDocShell()
: nullptr;
if (docShell) {
docShell->SetName(EmptyString());
}
}
return NS_OK;
}
/* static */ int32_t /* static */ int32_t
nsGenericHTMLFrameElement::MapScrollingAttribute(const nsAttrValue* aValue) nsGenericHTMLFrameElement::MapScrollingAttribute(const nsAttrValue* aValue)
{ {
@ -415,39 +366,88 @@ nsGenericHTMLFrameElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValue* aValue, const nsAttrValue* aValue,
const nsAttrValue* aOldValue, bool aNotify) const nsAttrValue* aOldValue, bool aNotify)
{ {
if (aName == nsGkAtoms::scrolling && aNameSpaceID == kNameSpaceID_None) { if (aValue) {
if (mFrameLoader) { nsAttrValueOrString value(aValue);
nsIDocShell* docshell = mFrameLoader->GetExistingDocShell(); AfterMaybeChangeAttr(aNameSpaceID, aName, &value, aNotify);
nsCOMPtr<nsIScrollable> scrollable = do_QueryInterface(docshell); } else {
if (scrollable) { AfterMaybeChangeAttr(aNameSpaceID, aName, nullptr, aNotify);
int32_t cur; }
scrollable->GetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_X, &cur);
int32_t val = MapScrollingAttribute(aValue); if (aNameSpaceID == kNameSpaceID_None) {
if (cur != val) { if (aName == nsGkAtoms::scrolling) {
scrollable->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_X, val); if (mFrameLoader) {
scrollable->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_Y, val); nsIDocShell* docshell = mFrameLoader->GetExistingDocShell();
RefPtr<nsPresContext> presContext; nsCOMPtr<nsIScrollable> scrollable = do_QueryInterface(docshell);
docshell->GetPresContext(getter_AddRefs(presContext)); if (scrollable) {
nsIPresShell* shell = presContext ? presContext->GetPresShell() : nullptr; int32_t cur;
nsIFrame* rootScroll = shell ? shell->GetRootScrollFrame() : nullptr; scrollable->GetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_X, &cur);
if (rootScroll) { int32_t val = MapScrollingAttribute(aValue);
shell->FrameNeedsReflow(rootScroll, nsIPresShell::eStyleChange, if (cur != val) {
NS_FRAME_IS_DIRTY); scrollable->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_X, val);
scrollable->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_Y, val);
RefPtr<nsPresContext> presContext;
docshell->GetPresContext(getter_AddRefs(presContext));
nsIPresShell* shell = presContext ? presContext->GetPresShell() : nullptr;
nsIFrame* rootScroll = shell ? shell->GetRootScrollFrame() : nullptr;
if (rootScroll) {
shell->FrameNeedsReflow(rootScroll, nsIPresShell::eStyleChange,
NS_FRAME_IS_DIRTY);
}
} }
} }
} }
} else if (aName == nsGkAtoms::mozbrowser) {
mReallyIsBrowser = !!aValue && BrowserFramesEnabled() &&
PrincipalAllowsBrowserFrame(NodePrincipal());
} }
} }
if (aName == nsGkAtoms::mozbrowser && aNameSpaceID == kNameSpaceID_None) {
mReallyIsBrowser = !!aValue && BrowserFramesEnabled() &&
PrincipalAllowsBrowserFrame(NodePrincipal());
}
return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue, return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue,
aOldValue, aNotify); aOldValue, aNotify);
} }
nsresult
nsGenericHTMLFrameElement::OnAttrSetButNotChanged(int32_t aNamespaceID,
nsIAtom* aName,
const nsAttrValueOrString& aValue,
bool aNotify)
{
AfterMaybeChangeAttr(aNamespaceID, aName, &aValue, aNotify);
return nsGenericHTMLElement::OnAttrSetButNotChanged(aNamespaceID, aName,
aValue, aNotify);
}
void
nsGenericHTMLFrameElement::AfterMaybeChangeAttr(int32_t aNamespaceID,
nsIAtom* aName,
const nsAttrValueOrString* aValue,
bool aNotify)
{
if (aNamespaceID == kNameSpaceID_None) {
if (aName == nsGkAtoms::src) {
if (aValue && (!IsHTMLElement(nsGkAtoms::iframe) ||
!HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc))) {
// Don't propagate error here. The attribute was successfully set,
// that's what we should reflect.
LoadSrc();
}
} else if (aName == nsGkAtoms::name) {
// Propagate "name" to the docshell to make browsing context names live,
// per HTML5.
nsIDocShell* docShell = mFrameLoader ? mFrameLoader->GetExistingDocShell()
: nullptr;
if (docShell) {
if (aValue) {
docShell->SetName(aValue->String());
} else {
docShell->SetName(EmptyString());
}
}
}
}
}
void void
nsGenericHTMLFrameElement::DestroyContent() nsGenericHTMLFrameElement::DestroyContent()
{ {

View file

@ -53,20 +53,7 @@ public:
bool aCompileEventHandlers) override; bool aCompileEventHandlers) override;
virtual void UnbindFromTree(bool aDeep = true, virtual void UnbindFromTree(bool aDeep = true,
bool aNullParent = true) override; bool aNullParent = true) override;
nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAString& aValue, bool aNotify)
{
return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify);
}
virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
bool aNotify) override;
virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
bool aNotify) override;
virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValue* aValue,
const nsAttrValue* aOldValue,
bool aNotify) override;
virtual void DestroyContent() override; virtual void DestroyContent() override;
nsresult CopyInnerTo(mozilla::dom::Element* aDest); nsresult CopyInnerTo(mozilla::dom::Element* aDest);
@ -111,6 +98,14 @@ protected:
nsresult GetContentDocument(nsIDOMDocument** aContentDocument); nsresult GetContentDocument(nsIDOMDocument** aContentDocument);
already_AddRefed<nsPIDOMWindowOuter> GetContentWindow(); already_AddRefed<nsPIDOMWindowOuter> GetContentWindow();
virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValue* aValue,
const nsAttrValue* aOldValue,
bool aNotify) override;
virtual nsresult OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValueOrString& aValue,
bool aNotify) override;
RefPtr<nsFrameLoader> mFrameLoader; RefPtr<nsFrameLoader> mFrameLoader;
nsCOMPtr<nsPIDOMWindowOuter> mOpenerWindow; nsCOMPtr<nsPIDOMWindowOuter> mOpenerWindow;
@ -133,6 +128,18 @@ protected:
private: private:
void GetManifestURL(nsAString& aOut); void GetManifestURL(nsAString& aOut);
/**
* This function is called by AfterSetAttr and OnAttrSetButNotChanged.
* It will be called whether the value is being set or unset.
*
* @param aNamespaceID the namespace of the attr being set
* @param aName the localname of the attribute being set
* @param aValue the value being set or null if the value is being unset
* @param aNotify Whether we plan to notify document observers.
*/
void AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValueOrString* aValue, bool aNotify);
}; };
#endif // nsGenericHTMLFrameElement_h #endif // nsGenericHTMLFrameElement_h

View file

@ -1085,50 +1085,27 @@ nsMathMLElement::GetHrefURI() const
} }
nsresult nsresult
nsMathMLElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, nsMathMLElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue, const nsAttrValue* aValue,
bool aNotify) const nsAttrValue* aOldValue, bool aNotify)
{ {
nsresult rv = nsMathMLElementBase::SetAttr(aNameSpaceID, aName, aPrefix, // It is important that this be done after the attribute is set/unset.
aValue, aNotify); // We will need the updated attribute value because notifying the document
// The ordering of the parent class's SetAttr call and Link::ResetLinkState
// is important here! The attribute is not set until SetAttr returns, and
// we will need the updated attribute value because notifying the document
// that content states have changed will call IntrinsicState, which will try // that content states have changed will call IntrinsicState, which will try
// to get updated information about the visitedness from Link. // to get updated information about the visitedness from Link.
if (aName == nsGkAtoms::href && if (aName == nsGkAtoms::href &&
(aNameSpaceID == kNameSpaceID_None || (aNameSpaceID == kNameSpaceID_None ||
aNameSpaceID == kNameSpaceID_XLink)) { aNameSpaceID == kNameSpaceID_XLink)) {
if (aNameSpaceID == kNameSpaceID_XLink) { if (aValue && aNameSpaceID == kNameSpaceID_XLink) {
WarnDeprecated(u"xlink:href", u"href", OwnerDoc()); WarnDeprecated(u"xlink:href", u"href", OwnerDoc());
} }
Link::ResetLinkState(!!aNotify, true); // Note: When unsetting href, there may still be another href since there
// are 2 possible namespaces.
Link::ResetLinkState(aNotify, aValue || Link::ElementHasHref());
} }
return rv; return nsMathMLElementBase::AfterSetAttr(aNameSpaceID, aName, aValue,
} aOldValue, aNotify);
nsresult
nsMathMLElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttr,
bool aNotify)
{
nsresult rv = nsMathMLElementBase::UnsetAttr(aNameSpaceID, aAttr, aNotify);
// The ordering of the parent class's UnsetAttr call and Link::ResetLinkState
// is important here! The attribute is not unset until UnsetAttr returns, and
// we will need the updated attribute value because notifying the document
// that content states have changed will call IntrinsicState, which will try
// to get updated information about the visitedness from Link.
if (aAttr == nsGkAtoms::href &&
(aNameSpaceID == kNameSpaceID_None ||
aNameSpaceID == kNameSpaceID_XLink)) {
// Note: just because we removed a single href attr doesn't mean there's no href,
// since there are 2 possible namespaces.
Link::ResetLinkState(!!aNotify, Link::ElementHasHref());
}
return rv;
} }
JSObject* JSObject*

View file

@ -93,16 +93,6 @@ public:
virtual bool IsLink(nsIURI** aURI) const override; virtual bool IsLink(nsIURI** aURI) const override;
virtual void GetLinkTarget(nsAString& aTarget) override; virtual void GetLinkTarget(nsAString& aTarget) override;
virtual already_AddRefed<nsIURI> GetHrefURI() const override; virtual already_AddRefed<nsIURI> GetHrefURI() const override;
nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAString& aValue, bool aNotify)
{
return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify);
}
virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,
bool aNotify) override;
virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
bool aNotify) override;
virtual nsIDOMNode* AsDOMNode() override { return this; } virtual nsIDOMNode* AsDOMNode() override { return this; }
@ -111,6 +101,11 @@ protected:
virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValue* aValue,
const nsAttrValue* aOldValue,
bool aNotify) override;
private: private:
bool mIncrementScriptLevel; bool mIncrementScriptLevel;
}; };

View file

@ -7,6 +7,7 @@
#include "mozilla/dom/XBLChildrenElement.h" #include "mozilla/dom/XBLChildrenElement.h"
#include "nsCharSeparatedTokenizer.h" #include "nsCharSeparatedTokenizer.h"
#include "mozilla/dom/NodeListBinding.h" #include "mozilla/dom/NodeListBinding.h"
#include "nsAttrValueOrString.h"
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
@ -27,34 +28,24 @@ NS_INTERFACE_MAP_END_INHERITING(Element)
NS_IMPL_ELEMENT_CLONE(XBLChildrenElement) NS_IMPL_ELEMENT_CLONE(XBLChildrenElement)
nsresult nsresult
XBLChildrenElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, XBLChildrenElement::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName,
bool aNotify) const nsAttrValueOrString* aValue,
bool aNotify)
{ {
if (aAttribute == nsGkAtoms::includes && if (aNamespaceID == kNameSpaceID_None) {
aNameSpaceID == kNameSpaceID_None) { if (aName == nsGkAtoms::includes) {
mIncludes.Clear(); mIncludes.Clear();
} if (aValue) {
nsCharSeparatedTokenizer tok(aValue->String(), '|',
return Element::UnsetAttr(aNameSpaceID, aAttribute, aNotify); nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
} while (tok.hasMoreTokens()) {
mIncludes.AppendElement(NS_Atomize(tok.nextToken()));
bool }
XBLChildrenElement::ParseAttribute(int32_t aNamespaceID, }
nsIAtom* aAttribute,
const nsAString& aValue,
nsAttrValue& aResult)
{
if (aAttribute == nsGkAtoms::includes &&
aNamespaceID == kNameSpaceID_None) {
mIncludes.Clear();
nsCharSeparatedTokenizer tok(aValue, '|',
nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
while (tok.hasMoreTokens()) {
mIncludes.AppendElement(NS_Atomize(tok.nextToken()));
} }
} }
return false; return nsXMLElement::BeforeSetAttr(aNamespaceID, aName, aValue, aNotify);
} }
} // namespace dom } // namespace dom

View file

@ -37,14 +37,6 @@ public:
virtual nsIDOMNode* AsDOMNode() override { return this; } virtual nsIDOMNode* AsDOMNode() override { return this; }
// nsIContent interface methods
virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
bool aNotify) override;
virtual bool ParseAttribute(int32_t aNamespaceID,
nsIAtom* aAttribute,
const nsAString& aValue,
nsAttrValue& aResult) override;
void AppendInsertedChild(nsIContent* aChild) void AppendInsertedChild(nsIContent* aChild)
{ {
mInsertedChildren.AppendElement(aChild); mInsertedChildren.AppendElement(aChild);
@ -147,6 +139,10 @@ public:
protected: protected:
~XBLChildrenElement(); ~XBLChildrenElement();
virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValueOrString* aValue,
bool aNotify) override;
private: private:
nsTArray<nsIContent*> mInsertedChildren; // WEAK nsTArray<nsIContent*> mInsertedChildren; // WEAK
nsTArray<nsCOMPtr<nsIAtom> > mIncludes; nsTArray<nsCOMPtr<nsIAtom> > mIncludes;