diff options
Diffstat (limited to 'dom/html')
32 files changed, 783 insertions, 850 deletions
diff --git a/dom/html/HTMLAnchorElement.cpp b/dom/html/HTMLAnchorElement.cpp index 501d6e3d06..0759af3395 100644 --- a/dom/html/HTMLAnchorElement.cpp +++ b/dom/html/HTMLAnchorElement.cpp @@ -14,6 +14,7 @@ #include "nsContentUtils.h" #include "nsGkAtoms.h" #include "nsHTMLDNSPrefetch.h" +#include "nsAttrValueOrString.h" #include "nsIDocument.h" #include "nsIPresShell.h" #include "nsPresContext.h" @@ -372,84 +373,37 @@ HTMLAnchorElement::GetHrefURI() const } nsresult -HTMLAnchorElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) +HTMLAnchorElement::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue, + bool aNotify) { - bool reset = false; - if (aName == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) { - // 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) { + if (aNamespaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::href) { CancelDNSPrefetch(HTML_ANCHOR_DNS_PREFETCH_DEFERRED, HTML_ANCHOR_DNS_PREFETCH_REQUESTED); } } - nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, - aValue, 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; + return nsGenericHTMLElement::BeforeSetAttr(aNamespaceID, aName, aValue, + aNotify); } nsresult -HTMLAnchorElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, - bool aNotify) +HTMLAnchorElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { - bool href = - (aAttribute == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID); - - if (href) { - CancelDNSPrefetch(HTML_ANCHOR_DNS_PREFETCH_DEFERRED, - HTML_ANCHOR_DNS_PREFETCH_REQUESTED); - } - - 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 (href) { - Link::ResetLinkState(!!aNotify, false); + if (aNamespaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::href) { + Link::ResetLinkState(aNotify, !!aValue); + if (aValue && IsInComposedDoc()) { + TryDNSPrefetch(); + } + } } - return rv; -} - -bool -HTMLAnchorElement::ParseAttribute(int32_t aNamespaceID, - nsIAtom* aAttribute, - const nsAString& aValue, - nsAttrValue& aResult) -{ - return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue, - aResult); + return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName, + aValue, aOldValue, aNotify); } EventStates diff --git a/dom/html/HTMLAnchorElement.h b/dom/html/HTMLAnchorElement.h index 027d8d689a..0875559644 100644 --- a/dom/html/HTMLAnchorElement.h +++ b/dom/html/HTMLAnchorElement.h @@ -66,20 +66,13 @@ public: virtual void GetLinkTarget(nsAString& aTarget) 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 bool ParseAttribute(int32_t aNamespaceID, - nsIAtom* aAttribute, - const nsAString& aValue, - nsAttrValue& aResult) override; + virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue, + bool aNotify) override; + virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, + bool aNotify) override; virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; diff --git a/dom/html/HTMLAreaElement.cpp b/dom/html/HTMLAreaElement.cpp index 213d4831dd..4c85774f45 100644 --- a/dom/html/HTMLAreaElement.cpp +++ b/dom/html/HTMLAreaElement.cpp @@ -153,42 +153,22 @@ HTMLAreaElement::UnbindFromTree(bool aDeep, bool aNullParent) } nsresult -HTMLAreaElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) -{ - nsresult rv = - nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue, 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 (aName == nsGkAtoms::href && aNameSpaceID == kNameSpaceID_None) { - Link::ResetLinkState(!!aNotify, true); +HTMLAreaElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) +{ + if (aNamespaceID == kNameSpaceID_None) { + // This must happen after the attribute is set. 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 (aName == nsGkAtoms::href) { + Link::ResetLinkState(aNotify, !!aValue); + } } - return rv; -} - -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; + return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName, aValue, + aOldValue, aNotify); } #define IMPL_URI_PART(_part) \ diff --git a/dom/html/HTMLAreaElement.h b/dom/html/HTMLAreaElement.h index c2ed7a9283..d408934c99 100644 --- a/dom/html/HTMLAreaElement.h +++ b/dom/html/HTMLAreaElement.h @@ -56,16 +56,6 @@ public: bool aCompileEventHandlers) override; virtual void UnbindFromTree(bool aDeep = true, 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; @@ -186,6 +176,11 @@ protected: 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; }; diff --git a/dom/html/HTMLCanvasElement.cpp b/dom/html/HTMLCanvasElement.cpp index 803c60e8ac..ba1fbedbb0 100644 --- a/dom/html/HTMLCanvasElement.cpp +++ b/dom/html/HTMLCanvasElement.cpp @@ -446,38 +446,37 @@ NS_IMPL_UINT_ATTR_DEFAULT_VALUE(HTMLCanvasElement, Height, height, DEFAULT_CANVA NS_IMPL_BOOL_ATTR(HTMLCanvasElement, MozOpaque, moz_opaque) nsresult -HTMLCanvasElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) -{ - nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue, - 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); - } +HTMLCanvasElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) +{ + AfterMaybeChangeAttr(aNamespaceID, aName, aNotify); - return rv; + return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName, aValue, + aOldValue, aNotify); } nsresult -HTMLCanvasElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName, - bool aNotify) +HTMLCanvasElement::OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString& aValue, + bool aNotify) { - nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID, aName, aNotify); - if (NS_SUCCEEDED(rv) && mCurrentContext && - aNameSpaceID == kNameSpaceID_None && - (aName == nsGkAtoms::width || aName == nsGkAtoms::height || aName == nsGkAtoms::moz_opaque)) - { + AfterMaybeChangeAttr(aNamespaceID, aName, aNotify); + + return nsGenericHTMLElement::OnAttrSetButNotChanged(aNamespaceID, aName, + 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; - rv = UpdateContext(nullptr, JS::NullHandleValue, dummy); - NS_ENSURE_SUCCESS(rv, rv); + UpdateContext(nullptr, JS::NullHandleValue, dummy); } - return rv; } void diff --git a/dom/html/HTMLCanvasElement.h b/dom/html/HTMLCanvasElement.h index de26c475a3..822f053978 100644 --- a/dom/html/HTMLCanvasElement.h +++ b/dom/html/HTMLCanvasElement.h @@ -295,20 +295,6 @@ public: nsAttrValue& aResult) 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; nsresult CopyInnerTo(mozilla::dom::Element* aDest); @@ -373,6 +359,14 @@ protected: nsISupports** aResult); 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(); bool mResetLayer; @@ -406,6 +400,18 @@ public: CanvasContextType GetCurrentContextType() { 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 diff --git a/dom/html/HTMLContentElement.cpp b/dom/html/HTMLContentElement.cpp index 01c0158a03..158f8d0ce9 100644 --- a/dom/html/HTMLContentElement.cpp +++ b/dom/html/HTMLContentElement.cpp @@ -210,75 +210,63 @@ IsValidContentSelectors(nsCSSSelector* aSelector) } nsresult -HTMLContentElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) +HTMLContentElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { - nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, - aValue, aNotify); - NS_ENSURE_SUCCESS(rv, rv); - - if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::select) { - // Select attribute was updated, the insertion point may match different - // elements. - nsIDocument* doc = OwnerDoc(); - nsCSSParser parser(doc->CSSLoader()); - - mValidSelector = true; - mSelectorList = nullptr; - - nsresult rv = parser.ParseSelectorString(aValue, - doc->GetDocumentURI(), - // Bug 11240 - 0, // XXX get the line number! - getter_Transfers(mSelectorList)); - - // We don't want to return an exception if parsing failed because - // the spec does not define it as an exception case. - if (NS_SUCCEEDED(rv)) { - // Ensure that all the selectors are valid - nsCSSSelectorList* selectors = mSelectorList; - while (selectors) { - if (!IsValidContentSelectors(selectors->mSelectors)) { - // If we have an invalid selector, we can not match anything. - mValidSelector = false; - mSelectorList = nullptr; - break; + if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::select) { + if (aValue) { + // Select attribute was updated, the insertion point may match different + // elements. + nsIDocument* doc = OwnerDoc(); + nsCSSParser parser(doc->CSSLoader()); + + mValidSelector = true; + mSelectorList = nullptr; + + nsAutoString valueStr; + aValue->ToString(valueStr); + nsresult rv = parser.ParseSelectorString(valueStr, + doc->GetDocumentURI(), + // Bug 11240 + 0, // XXX get the line number! + getter_Transfers(mSelectorList)); + + // We don't want to return an exception if parsing failed because + // the spec does not define it as an exception case. + if (NS_SUCCEEDED(rv)) { + // Ensure that all the selectors are valid + nsCSSSelectorList* selectors = mSelectorList; + while (selectors) { + if (!IsValidContentSelectors(selectors->mSelectors)) { + // If we have an invalid selector, we can not match anything. + mValidSelector = false; + mSelectorList = nullptr; + break; + } + selectors = selectors->mNext; } - selectors = selectors->mNext; } - } - - ShadowRoot* containingShadow = GetContainingShadow(); - if (containingShadow) { - containingShadow->DistributeAllNodes(); - } - } - - return NS_OK; -} -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(); + 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(); + } } } - return NS_OK; + return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName, aValue, + aOldValue, aNotify); } bool diff --git a/dom/html/HTMLContentElement.h b/dom/html/HTMLContentElement.h index f019e56cda..d2491a7928 100644 --- a/dom/html/HTMLContentElement.h +++ b/dom/html/HTMLContentElement.h @@ -63,13 +63,6 @@ public: void InsertMatchedNode(uint32_t aIndex, nsIContent* aContent); 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. already_AddRefed<DistributedContentList> GetDistributedNodes(); void GetSelect(nsAString& aSelect) @@ -95,6 +88,11 @@ protected: */ 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 * content insertion selector. diff --git a/dom/html/HTMLFormElement.cpp b/dom/html/HTMLFormElement.cpp index 1fdfb602e1..58cba6863a 100644 --- a/dom/html/HTMLFormElement.cpp +++ b/dom/html/HTMLFormElement.cpp @@ -189,27 +189,31 @@ HTMLFormElement::GetElements(nsIDOMHTMLCollection** aElements) } nsresult -HTMLFormElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) -{ - if ((aName == nsGkAtoms::action || aName == nsGkAtoms::target) && - aNameSpaceID == kNameSpaceID_None) { - if (mPendingSubmission) { - // aha, there is a pending submission that means we're in - // the script and we need to flush it. let's tell it - // that the event was ignored to force the flush. - // the second argument is not playing a role at all. - FlushPendingSubmission(); +HTMLFormElement::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue, bool aNotify) +{ + if (aNamespaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::action || aName == nsGkAtoms::target) { + // This check is mostly to preserve previous behavior. + if (aValue) { + if (mPendingSubmission) { + // aha, there is a pending submission that means we're in + // the script and we need to flush it. let's tell it + // 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 diff --git a/dom/html/HTMLFormElement.h b/dom/html/HTMLFormElement.h index 6102ea61f1..f06db4b51e 100644 --- a/dom/html/HTMLFormElement.h +++ b/dom/html/HTMLFormElement.h @@ -105,14 +105,9 @@ public: bool aCompileEventHandlers) override; virtual void UnbindFromTree(bool aDeep = true, 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 BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue, + bool aNotify) override; virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, const nsAttrValue* aValue, const nsAttrValue* aOldValue, diff --git a/dom/html/HTMLFrameSetElement.cpp b/dom/html/HTMLFrameSetElement.cpp index 6d39caa19a..861dbfe9fc 100644 --- a/dom/html/HTMLFrameSetElement.cpp +++ b/dom/html/HTMLFrameSetElement.cpp @@ -9,6 +9,7 @@ #include "mozilla/dom/EventHandlerBinding.h" #include "nsGlobalWindow.h" #include "mozilla/UniquePtrExtensions.h" +#include "nsAttrValueOrString.h" NS_IMPL_NS_NEW_HTML_ELEMENT(FrameSet) @@ -65,43 +66,45 @@ HTMLFrameSetElement::GetRows(nsAString& aRows) } nsresult -HTMLFrameSetElement::SetAttr(int32_t aNameSpaceID, - nsIAtom* aAttribute, - nsIAtom* aPrefix, - const nsAString& aValue, - bool aNotify) +HTMLFrameSetElement::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue, + bool aNotify) { - nsresult rv; /* The main goal here is to see whether the _number_ of rows or - * columns has changed. If it has, we need to reframe; otherwise - * we want to reflow. So we set mCurrentRowColHint here, then call - * nsGenericHTMLElement::SetAttr, which will end up calling - * GetAttributeChangeHint and notifying layout with that hint. - * Once nsGenericHTMLElement::SetAttr returns, we want to go back to our - * normal hint, which is NS_STYLE_HINT_REFLOW. + * columns has changed. If it has, we need to reframe; otherwise + * we want to reflow. + * Ideally, the style hint would be changed back to reflow after the reframe + * has been performed. Unfortunately, however, the reframe will be performed + * by the call to nsNodeUtils::AttributeChanged, which occurs *after* + * 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) { - int32_t oldRows = mNumRows; - ParseRowCol(aValue, mNumRows, &mRowSpecs); + mCurrentRowColHint = NS_STYLE_HINT_REFLOW; + if (aNamespaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::rows) { + if (aValue) { + int32_t oldRows = mNumRows; + ParseRowCol(aValue->String(), mNumRows, &mRowSpecs); - if (mNumRows != oldRows) { - mCurrentRowColHint = nsChangeHint_ReconstructFrame; - } - } else if (aAttribute == nsGkAtoms::cols && - aNameSpaceID == kNameSpaceID_None) { - int32_t oldCols = mNumCols; - ParseRowCol(aValue, mNumCols, &mColSpecs); + if (mNumRows != oldRows) { + mCurrentRowColHint = nsChangeHint_ReconstructFrame; + } + } + } else if (aName == nsGkAtoms::cols) { + if (aValue) { + int32_t oldCols = mNumCols; + ParseRowCol(aValue->String(), mNumCols, &mColSpecs); - if (mNumCols != oldCols) { - mCurrentRowColHint = nsChangeHint_ReconstructFrame; + if (mNumCols != oldCols) { + mCurrentRowColHint = nsChangeHint_ReconstructFrame; + } + } } } - rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aAttribute, aPrefix, - aValue, aNotify); - mCurrentRowColHint = NS_STYLE_HINT_REFLOW; - - return rv; + return nsGenericHTMLElement::BeforeSetAttr(aNamespaceID, aName, aValue, aNotify); } nsresult diff --git a/dom/html/HTMLFrameSetElement.h b/dom/html/HTMLFrameSetElement.h index b6bbe5d957..fe3bc2341c 100644 --- a/dom/html/HTMLFrameSetElement.h +++ b/dom/html/HTMLFrameSetElement.h @@ -100,17 +100,6 @@ public: #undef WINDOW_EVENT_HELPER #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. * @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 nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue, + bool aNotify) override; + private: nsresult ParseRowCol(const nsAString& aValue, int32_t& aNumSpecs, diff --git a/dom/html/HTMLIFrameElement.cpp b/dom/html/HTMLIFrameElement.cpp index 6ae4ec9ddd..2b66cbd268 100644 --- a/dom/html/HTMLIFrameElement.cpp +++ b/dom/html/HTMLIFrameElement.cpp @@ -182,54 +182,49 @@ HTMLIFrameElement::GetAttributeMappingFunction() const } 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 HTMLIFrameElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, const nsAttrValue* aValue, const nsAttrValue* aOldValue, bool aNotify) { - if (aName == nsGkAtoms::sandbox && - aNameSpaceID == kNameSpaceID_None && mFrameLoader) { - // 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()); + AfterMaybeChangeAttr(aNameSpaceID, aName, aNotify); + + if (aNameSpaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::sandbox) { + if (mFrameLoader) { + // 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, aOldValue, aNotify); } nsresult -HTMLIFrameElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, - bool aNotify) +HTMLIFrameElement::OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString& aValue, + bool aNotify) { - // Invoke on the superclass. - nsresult rv = nsGenericHTMLFrameElement::UnsetAttr(aNameSpaceID, aAttribute, aNotify); - NS_ENSURE_SUCCESS(rv, rv); - - if (aNameSpaceID == kNameSpaceID_None && - aAttribute == nsGkAtoms::srcdoc) { - // Fall back to the src attribute, if any - LoadSrc(); - } + AfterMaybeChangeAttr(aNamespaceID, aName, aNotify); + + return nsGenericHTMLFrameElement::OnAttrSetButNotChanged(aNamespaceID, aName, + aValue, aNotify); +} - return NS_OK; +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(); + } + } } uint32_t diff --git a/dom/html/HTMLIFrameElement.h b/dom/html/HTMLIFrameElement.h index 23afaaddd0..e94ae0bad6 100644 --- a/dom/html/HTMLIFrameElement.h +++ b/dom/html/HTMLIFrameElement.h @@ -46,21 +46,6 @@ public: 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(); // Web IDL binding methods @@ -197,11 +182,30 @@ protected: 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: static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes, nsRuleData* aData); 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 diff --git a/dom/html/HTMLImageElement.cpp b/dom/html/HTMLImageElement.cpp index dd43ac36ad..444c352e2c 100644 --- a/dom/html/HTMLImageElement.cpp +++ b/dom/html/HTMLImageElement.cpp @@ -112,6 +112,7 @@ private: HTMLImageElement::HTMLImageElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) : nsGenericHTMLElement(aNodeInfo) , mForm(nullptr) + , mForceReload(false) , mInDocResponsiveContent(false) , mCurrentDensity(1.0) { @@ -372,6 +373,9 @@ HTMLImageElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, const nsAttrValueOrString* aValue, bool aNotify) { + if (aValue) { + BeforeMaybeChangeAttr(aNameSpaceID, aName, *aValue, aNotify); + } if (aNameSpaceID == kNameSpaceID_None && mForm && (aName == nsGkAtoms::name || aName == nsGkAtoms::id)) { @@ -394,6 +398,10 @@ HTMLImageElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, const nsAttrValue* aValue, const nsAttrValue* aOldValue, bool aNotify) { + if (aValue) { + AfterMaybeChangeAttr(aNameSpaceID, aName, aNotify); + } + if (aNameSpaceID == kNameSpaceID_None && mForm && (aName == nsGkAtoms::name || aName == nsGkAtoms::id) && aValue && !aValue->IsEmptyString()) { @@ -438,63 +446,22 @@ HTMLImageElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, } nsresult -HTMLImageElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) -{ - // We handle image element with attribute ismap in its corresponding frame - // element. Set mMultipleActionsPrevented here to prevent the click event - // trigger the behaviors in Element::PostHandleEventForLinks - WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent(); - if (mouseEvent && mouseEvent->IsLeftClickEvent() && IsMap()) { - mouseEvent->mFlags.mMultipleActionsPrevented = true; - } - return nsGenericHTMLElement::GetEventTargetParent(aVisitor); -} - -bool -HTMLImageElement::IsHTMLFocusable(bool aWithMouse, - bool *aIsFocusable, int32_t *aTabIndex) +HTMLImageElement::OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString& aValue, + bool aNotify) { - int32_t tabIndex = TabIndex(); - - if (IsInUncomposedDoc()) { - nsAutoString usemap; - GetUseMap(usemap); - // XXXbz which document should this be using? sXBL/XBL2 issue! I - // think that OwnerDoc() is right, since we don't want to - // assume stuff about the document we're bound to. - if (OwnerDoc()->FindImageMap(usemap)) { - if (aTabIndex) { - // Use tab index on individual map areas - *aTabIndex = (sTabFocusModel & eTabFocus_linksMask)? 0 : -1; - } - // Image map is not focusable itself, but flag as tabbable - // so that image map areas get walked into. - *aIsFocusable = false; - - return false; - } - } - - if (aTabIndex) { - // Can be in tab order if tabindex >=0 and form controls are tabbable. - *aTabIndex = (sTabFocusModel & eTabFocus_formElementsMask)? tabIndex : -1; - } - - *aIsFocusable = -#ifdef XP_MACOSX - (!aWithMouse || nsFocusManager::sMouseFocusesFormControl) && -#endif - (tabIndex >= 0 || HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)); + BeforeMaybeChangeAttr(aNamespaceID, aName, aValue, aNotify); + AfterMaybeChangeAttr(aNamespaceID, aName, aNotify); - return false; + return nsGenericHTMLElement::OnAttrSetButNotChanged(aNamespaceID, aName, + aValue, aNotify); } -nsresult -HTMLImageElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) +void +HTMLImageElement::BeforeMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString& 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 @@ -505,16 +472,19 @@ HTMLImageElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, // spec. // // Both cases handle unsetting src in AfterSetAttr - if (aNameSpaceID == kNameSpaceID_None && + // + // 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); + mResponsiveSelector->SetDefaultSource(aValue.String()); } QueueImageLoadTask(true); - } else if (aNotify) { + } 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. @@ -529,23 +499,23 @@ HTMLImageElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, // 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); + LoadImage(aValue.String(), true, aNotify, eImageLoadType_Normal); mNewRequestsWillNeedAnimationReset = false; } - } else if (aNameSpaceID == kNameSpaceID_None && + } else if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::crossorigin && aNotify) { nsAttrValue attrValue; - ParseCORSValue(aValue, attrValue); + ParseCORSValue(aValue.String(), attrValue); if (GetCORSMode() != AttrValueToCORSMode(&attrValue)) { // Force a new load of the image with the new cross origin policy. - forceReload = true; + mForceReload = true; } } else if (aName == nsGkAtoms::referrerpolicy && - aNameSpaceID == kNameSpaceID_None && + aNamespaceID == kNameSpaceID_None && aNotify) { - ReferrerPolicy referrerPolicy = AttributeReferrerPolicyFromString(aValue); + ReferrerPolicy referrerPolicy = AttributeReferrerPolicyFromString(aValue.String()); if (!InResponsiveMode() && referrerPolicy != RP_Unset && referrerPolicy != GetImageReferrerPolicy()) { @@ -554,22 +524,28 @@ HTMLImageElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, // 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; + mForceReload = true; } } - nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, - aValue, aNotify); + 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 (forceReload) { + 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 { + } 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 @@ -577,7 +553,59 @@ HTMLImageElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, } } - return rv; + return; +} + +nsresult +HTMLImageElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) +{ + // We handle image element with attribute ismap in its corresponding frame + // element. Set mMultipleActionsPrevented here to prevent the click event + // trigger the behaviors in Element::PostHandleEventForLinks + WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent(); + if (mouseEvent && mouseEvent->IsLeftClickEvent() && IsMap()) { + mouseEvent->mFlags.mMultipleActionsPrevented = true; + } + return nsGenericHTMLElement::GetEventTargetParent(aVisitor); +} + +bool +HTMLImageElement::IsHTMLFocusable(bool aWithMouse, + bool *aIsFocusable, int32_t *aTabIndex) +{ + int32_t tabIndex = TabIndex(); + + if (IsInUncomposedDoc()) { + nsAutoString usemap; + GetUseMap(usemap); + // XXXbz which document should this be using? sXBL/XBL2 issue! I + // think that OwnerDoc() is right, since we don't want to + // assume stuff about the document we're bound to. + if (OwnerDoc()->FindImageMap(usemap)) { + if (aTabIndex) { + // Use tab index on individual map areas + *aTabIndex = (sTabFocusModel & eTabFocus_linksMask)? 0 : -1; + } + // Image map is not focusable itself, but flag as tabbable + // so that image map areas get walked into. + *aIsFocusable = false; + + return false; + } + } + + if (aTabIndex) { + // Can be in tab order if tabindex >=0 and form controls are tabbable. + *aTabIndex = (sTabFocusModel & eTabFocus_formElementsMask)? tabIndex : -1; + } + + *aIsFocusable = +#ifdef XP_MACOSX + (!aWithMouse || nsFocusManager::sMouseFocusesFormControl) && +#endif + (tabIndex >= 0 || HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)); + + return false; } nsresult diff --git a/dom/html/HTMLImageElement.h b/dom/html/HTMLImageElement.h index 52480d0a05..bb4a098824 100644 --- a/dom/html/HTMLImageElement.h +++ b/dom/html/HTMLImageElement.h @@ -75,17 +75,6 @@ public: 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, nsIContent* aBindingParent, bool aCompileEventHandlers) override; @@ -352,6 +341,10 @@ protected: const nsAttrValue* aOldValue, 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 // cooperate in maintaining. HTMLFormElement* mForm; @@ -365,6 +358,36 @@ private: static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes, 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; RefPtr<ImageLoadTask> mPendingImageLoadTask; diff --git a/dom/html/HTMLLegendElement.cpp b/dom/html/HTMLLegendElement.cpp index 1a593d7698..3f329de42d 100644 --- a/dom/html/HTMLLegendElement.cpp +++ b/dom/html/HTMLLegendElement.cpp @@ -71,21 +71,6 @@ HTMLLegendElement::GetAttributeChangeHint(const nsIAtom* aAttribute, } 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 HTMLLegendElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsIContent* aBindingParent, bool aCompileEventHandlers) diff --git a/dom/html/HTMLLegendElement.h b/dom/html/HTMLLegendElement.h index e2d71d62dd..0b476c1194 100644 --- a/dom/html/HTMLLegendElement.h +++ b/dom/html/HTMLLegendElement.h @@ -42,16 +42,6 @@ public: nsAttrValue& aResult) override; virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute, 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; diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index 4bcc188d39..05eaf3e8b5 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -3726,78 +3726,74 @@ int32_t HTMLMediaElement::TabIndexDefault() 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(); +nsresult +HTMLMediaElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) +{ + if (aNameSpaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::src) { + mSrcMediaSource = nullptr; + if (aValue) { + nsString srcStr = aValue->GetStringValue(); + nsCOMPtr<nsIURI> uri; + NewURIFromString(srcStr, getter_AddRefs(uri)); + if (uri && IsMediaSourceURI(uri)) { + nsresult rv = + NS_GetSourceForMediaSourceURI(uri, getter_AddRefs(mSrcMediaSource)); + if (NS_FAILED(rv)) { + nsAutoString spec; + GetCurrentSrc(spec); + 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(); } } - return rv; + // 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, + aValue, aOldValue, aNotify); } -nsresult HTMLMediaElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttr, - bool aNotify) +nsresult +HTMLMediaElement::OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString& aValue, + 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(); - } - } + AfterMaybeChangeAttr(aNamespaceID, aName, aNotify); - return rv; + return nsGenericHTMLElement::OnAttrSetButNotChanged(aNamespaceID, aName, + aValue, aNotify); } -nsresult -HTMLMediaElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, - const nsAttrValue* aValue, - const nsAttrValue* aOldValue, bool aNotify) +void +HTMLMediaElement::AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName, + bool aNotify) { - if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::src) { - mSrcMediaSource = nullptr; - if (aValue) { - nsString srcStr = aValue->GetStringValue(); - nsCOMPtr<nsIURI> uri; - NewURIFromString(srcStr, getter_AddRefs(uri)); - if (uri && IsMediaSourceURI(uri)) { - nsresult rv = - NS_GetSourceForMediaSourceURI(uri, getter_AddRefs(mSrcMediaSource)); - if (NS_FAILED(rv)) { - nsAutoString spec; - GetCurrentSrc(spec); - const char16_t* params[] = { spec.get() }; - ReportLoadError("MediaLoadInvalidURI", params, ArrayLength(params)); - } - } + if (aNamespaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::src) { + DoLoad(); } } - - return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, - aValue, aOldValue, aNotify); } nsresult HTMLMediaElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, diff --git a/dom/html/HTMLMediaElement.h b/dom/html/HTMLMediaElement.h index d81e9f5918..0d8d5d4e07 100644 --- a/dom/html/HTMLMediaElement.h +++ b/dom/html/HTMLMediaElement.h @@ -132,22 +132,6 @@ public: nsIAtom* aAttribute, const nsAString& aValue, 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, nsIContent* aBindingParent, @@ -1315,6 +1299,14 @@ protected: 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. // At most one of mDecoder and mSrcStream can be non-null. RefPtr<MediaDecoder> mDecoder; @@ -1717,6 +1709,17 @@ private: // We keep track of these because the load algorithm resolves/rejects all // already-dispatched pending play promises. 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 diff --git a/dom/html/HTMLObjectElement.cpp b/dom/html/HTMLObjectElement.cpp index 65f4078892..cd2f3f37a7 100644 --- a/dom/html/HTMLObjectElement.cpp +++ b/dom/html/HTMLObjectElement.cpp @@ -298,41 +298,45 @@ HTMLObjectElement::UnbindFromTree(bool aDeep, nsresult -HTMLObjectElement::SetAttr(int32_t aNameSpaceID, nsIAtom *aName, - nsIAtom *aPrefix, const nsAString &aValue, - bool aNotify) +HTMLObjectElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { - nsresult rv = nsGenericHTMLFormElement::SetAttr(aNameSpaceID, aName, aPrefix, - aValue, aNotify); + nsresult rv = AfterMaybeChangeAttr(aNamespaceID, aName, aNotify); NS_ENSURE_SUCCESS(rv, rv); - // 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 && - aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::data) { - return LoadObject(aNotify, true); - } - - return NS_OK; + return nsGenericHTMLFormElement::AfterSetAttr(aNamespaceID, aName, aValue, + aOldValue, aNotify); } nsresult -HTMLObjectElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, - bool aNotify) +HTMLObjectElement::OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString& aValue, + bool aNotify) { - nsresult rv = nsGenericHTMLFormElement::UnsetAttr(aNameSpaceID, - aAttribute, aNotify); + nsresult rv = AfterMaybeChangeAttr(aNamespaceID, aName, aNotify); NS_ENSURE_SUCCESS(rv, rv); - // See comment in SetAttr - if (aNotify && IsInComposedDoc() && mIsDoneAddingChildren && - aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::data) { - return LoadObject(aNotify, true); + return nsGenericHTMLFormElement::OnAttrSetButNotChanged(aNamespaceID, aName, + aValue, aNotify); +} + +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; diff --git a/dom/html/HTMLObjectElement.h b/dom/html/HTMLObjectElement.h index 5226154da9..6ce09e1cc4 100644 --- a/dom/html/HTMLObjectElement.h +++ b/dom/html/HTMLObjectElement.h @@ -59,11 +59,6 @@ public: bool aCompileEventHandlers) override; virtual void UnbindFromTree(bool aDeep = true, 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 IMEState GetDesiredIMEState() override; @@ -245,6 +240,15 @@ public: 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: /** * Calls LoadObject with the correct arguments to start the plugin load. @@ -269,6 +273,18 @@ private: static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes, 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; }; diff --git a/dom/html/HTMLSelectElement.cpp b/dom/html/HTMLSelectElement.cpp index d11f8b90f0..36dac0852b 100644 --- a/dom/html/HTMLSelectElement.cpp +++ b/dom/html/HTMLSelectElement.cpp @@ -1272,9 +1272,22 @@ HTMLSelectElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName, const nsAttrValueOrString* aValue, bool aNotify) { - if (aNotify && aName == nsGkAtoms::disabled && - aNameSpaceID == kNameSpaceID_None) { - mDisabledChanged = true; + if (aNameSpaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::disabled) { + 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, @@ -1294,6 +1307,12 @@ HTMLSelectElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, } else if (aName == nsGkAtoms::autocomplete) { // Clear the cached @autocomplete attribute state 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); } -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 HTMLSelectElement::DoneAddingChildren(bool aHaveNotified) { diff --git a/dom/html/HTMLSelectElement.h b/dom/html/HTMLSelectElement.h index 90e13da7a2..298b1a8d16 100644 --- a/dom/html/HTMLSelectElement.h +++ b/dom/html/HTMLSelectElement.h @@ -381,8 +381,6 @@ public: const nsAttrValue* aValue, const nsAttrValue* aOldValue, bool aNotify) override; - virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, - bool aNotify) override; virtual void DoneAddingChildren(bool aHaveNotified) override; virtual bool IsDoneAddingChildren() override { diff --git a/dom/html/HTMLSharedElement.cpp b/dom/html/HTMLSharedElement.cpp index bcb84d29b2..a7a10b0823 100644 --- a/dom/html/HTMLSharedElement.cpp +++ b/dom/html/HTMLSharedElement.cpp @@ -231,52 +231,32 @@ SetBaseTargetUsingFirstBaseWithTarget(nsIDocument* aDocument, } nsresult -HTMLSharedElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, - nsIAtom* aPrefix, const nsAString& aValue, - bool aNotify) +HTMLSharedElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, + const nsAttrValue* aOldValue, bool aNotify) { - nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, - 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) { - SetBaseURIUsingFirstBaseWithHref(GetUncomposedDoc(), this); - } else if (aName == nsGkAtoms::target) { - SetBaseTargetUsingFirstBaseWithTarget(GetUncomposedDoc(), this); - } - } - - return NS_OK; -} - -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 (aNamespaceID == kNameSpaceID_None) { if (aName == nsGkAtoms::href) { - SetBaseURIUsingFirstBaseWithHref(GetUncomposedDoc(), nullptr); + // 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) { - SetBaseTargetUsingFirstBaseWithTarget(GetUncomposedDoc(), nullptr); + // 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 NS_OK; + return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName, aValue, + aOldValue, aNotify); } nsresult diff --git a/dom/html/HTMLSharedElement.h b/dom/html/HTMLSharedElement.h index 205750cf6d..a12ee096df 100644 --- a/dom/html/HTMLSharedElement.h +++ b/dom/html/HTMLSharedElement.h @@ -59,17 +59,6 @@ public: nsIAtom* aAttribute, const nsAString& aValue, 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, nsIContent* aBindingParent, @@ -181,6 +170,11 @@ protected: virtual ~HTMLSharedElement(); 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 diff --git a/dom/html/HTMLSharedObjectElement.cpp b/dom/html/HTMLSharedObjectElement.cpp index 889fd5aef3..4f35068e1c 100644 --- a/dom/html/HTMLSharedObjectElement.cpp +++ b/dom/html/HTMLSharedObjectElement.cpp @@ -161,27 +161,53 @@ HTMLSharedObjectElement::UnbindFromTree(bool aDeep, 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 -HTMLSharedObjectElement::SetAttr(int32_t aNameSpaceID, nsIAtom *aName, - nsIAtom *aPrefix, const nsAString &aValue, - bool aNotify) +HTMLSharedObjectElement::OnAttrSetButNotChanged(int32_t aNamespaceID, + nsIAtom* aName, + const nsAttrValueOrString& aValue, + bool aNotify) { - nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, - aValue, aNotify); + nsresult rv = AfterMaybeChangeAttr(aNamespaceID, aName, aNotify); NS_ENSURE_SUCCESS(rv, rv); - // 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 && - aNameSpaceID == kNameSpaceID_None && aName == URIAttrName() - && !BlockEmbedContentLoading()) { - return LoadObject(aNotify, true); + return nsGenericHTMLElement::OnAttrSetButNotChanged(aNamespaceID, aName, + aValue, aNotify); +} + +nsresult +HTMLSharedObjectElement::AfterMaybeChangeAttr(int32_t aNamespaceID, + nsIAtom* aName, + bool aNotify) +{ + if (aNamespaceID == kNameSpaceID_None) { + 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; diff --git a/dom/html/HTMLSharedObjectElement.h b/dom/html/HTMLSharedObjectElement.h index c70ba8ebd0..07cf86cb01 100644 --- a/dom/html/HTMLSharedObjectElement.h +++ b/dom/html/HTMLSharedObjectElement.h @@ -57,9 +57,6 @@ public: bool aCompileEventHandlers) override; virtual void UnbindFromTree(bool aDeep = true, 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 IMEState GetDesiredIMEState() override; @@ -200,6 +197,16 @@ public: * Calls LoadObject with the correct arguments to start the plugin load. */ 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: virtual ~HTMLSharedObjectElement(); @@ -222,6 +229,17 @@ private: 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. * * If this is an embed node there are cases in which we should not try to load diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp index 569dae6c26..be78dc1cf0 100644 --- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -655,6 +655,43 @@ nsGenericHTMLElement::GetHrefURIForAnchors() const } 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 nsGenericHTMLElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, const nsAttrValue* aValue, const nsAttrValue* aOldValue, bool aNotify) @@ -694,6 +731,34 @@ nsGenericHTMLElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, } } 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 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 nsGenericHTMLElement::GetBaseTarget(nsAString& aBaseTarget) const { @@ -929,19 +913,13 @@ nsGenericHTMLElement::ParseAttribute(int32_t aNamespaceID, if (aAttribute == nsGkAtoms::name) { // Store name as an atom. name="" means that the element has no name, - // not that it has an emptystring as the name. - RemoveFromNameTable(); + // not that it has an empty string as the name. if (aValue.IsEmpty()) { ClearHasName(); return false; } aResult.ParseAtom(aValue); - - if (CanHaveName(NodeInfo()->NameAtom())) { - SetHasName(); - AddToNameTable(aResult.GetAtomValue()); - } return true; } diff --git a/dom/html/nsGenericHTMLElement.h b/dom/html/nsGenericHTMLElement.h index b9c0340493..16f438acc0 100644 --- a/dom/html/nsGenericHTMLElement.h +++ b/dom/html/nsGenericHTMLElement.h @@ -465,17 +465,6 @@ public: virtual void UnbindFromTree(bool aDeep = true, 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 { bool isFocusable = false; @@ -914,6 +903,9 @@ private: void RegUnRegAccessKey(bool aDoReg); protected: + virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValueOrString* aValue, + bool aNotify) override; virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, const nsAttrValue* aValue, const nsAttrValue* aOldValue, diff --git a/dom/html/nsGenericHTMLFrameElement.cpp b/dom/html/nsGenericHTMLFrameElement.cpp index 1aeb0a7d2e..03e6ffb421 100644 --- a/dom/html/nsGenericHTMLFrameElement.cpp +++ b/dom/html/nsGenericHTMLFrameElement.cpp @@ -334,55 +334,6 @@ nsGenericHTMLFrameElement::UnbindFromTree(bool aDeep, bool 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 nsGenericHTMLFrameElement::MapScrollingAttribute(const nsAttrValue* aValue) { @@ -415,39 +366,88 @@ nsGenericHTMLFrameElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, const nsAttrValue* aValue, const nsAttrValue* aOldValue, bool aNotify) { - if (aName == nsGkAtoms::scrolling && aNameSpaceID == kNameSpaceID_None) { - if (mFrameLoader) { - nsIDocShell* docshell = mFrameLoader->GetExistingDocShell(); - nsCOMPtr<nsIScrollable> scrollable = do_QueryInterface(docshell); - if (scrollable) { - int32_t cur; - scrollable->GetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_X, &cur); - int32_t val = MapScrollingAttribute(aValue); - if (cur != val) { - 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); + if (aValue) { + nsAttrValueOrString value(aValue); + AfterMaybeChangeAttr(aNameSpaceID, aName, &value, aNotify); + } else { + AfterMaybeChangeAttr(aNameSpaceID, aName, nullptr, aNotify); + } + + if (aNameSpaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::scrolling) { + if (mFrameLoader) { + nsIDocShell* docshell = mFrameLoader->GetExistingDocShell(); + nsCOMPtr<nsIScrollable> scrollable = do_QueryInterface(docshell); + if (scrollable) { + int32_t cur; + scrollable->GetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_X, &cur); + int32_t val = MapScrollingAttribute(aValue); + if (cur != val) { + 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, 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 nsGenericHTMLFrameElement::DestroyContent() { diff --git a/dom/html/nsGenericHTMLFrameElement.h b/dom/html/nsGenericHTMLFrameElement.h index 1570b19563..adcd17715e 100644 --- a/dom/html/nsGenericHTMLFrameElement.h +++ b/dom/html/nsGenericHTMLFrameElement.h @@ -53,20 +53,7 @@ public: bool aCompileEventHandlers) override; virtual void UnbindFromTree(bool aDeep = true, 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; nsresult CopyInnerTo(mozilla::dom::Element* aDest); @@ -111,6 +98,14 @@ protected: nsresult GetContentDocument(nsIDOMDocument** aContentDocument); 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; nsCOMPtr<nsPIDOMWindowOuter> mOpenerWindow; @@ -133,6 +128,18 @@ protected: private: 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 |