1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "mozilla/EventDispatcher.h"
8 #include "mozilla/EventStates.h"
9 #include "mozilla/Maybe.h"
10 #include "mozilla/dom/HTMLOptGroupElement.h"
11 #include "mozilla/dom/HTMLOptGroupElementBinding.h"
12 #include "mozilla/dom/HTMLSelectElement.h" // SafeOptionListMutation
13 #include "nsGkAtoms.h"
14 #include "nsStyleConsts.h"
15 #include "nsIFrame.h"
16 #include "nsIFormControlFrame.h"
17
18 NS_IMPL_NS_NEW_HTML_ELEMENT(OptGroup)
19
20 namespace mozilla::dom {
21
22 /**
23 * The implementation of <optgroup>
24 */
25
HTMLOptGroupElement(already_AddRefed<mozilla::dom::NodeInfo> && aNodeInfo)26 HTMLOptGroupElement::HTMLOptGroupElement(
27 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
28 : nsGenericHTMLElement(std::move(aNodeInfo)) {
29 // We start off enabled
30 AddStatesSilently(NS_EVENT_STATE_ENABLED);
31 }
32
33 HTMLOptGroupElement::~HTMLOptGroupElement() = default;
34
NS_IMPL_ELEMENT_CLONE(HTMLOptGroupElement)35 NS_IMPL_ELEMENT_CLONE(HTMLOptGroupElement)
36
37 void HTMLOptGroupElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
38 aVisitor.mCanHandle = false;
39
40 if (nsIFrame* frame = GetPrimaryFrame()) {
41 // FIXME(emilio): This poking at the style of the frame is broken unless we
42 // flush before every event handling, which we don't really want to.
43 if (frame->StyleUI()->UserInput() == StyleUserInput::None) {
44 return;
45 }
46 }
47
48 nsGenericHTMLElement::GetEventTargetParent(aVisitor);
49 }
50
GetSelect()51 Element* HTMLOptGroupElement::GetSelect() {
52 Element* parent = nsINode::GetParentElement();
53 if (!parent || !parent->IsHTMLElement(nsGkAtoms::select)) {
54 return nullptr;
55 }
56 return parent;
57 }
58
InsertChildBefore(nsIContent * aKid,nsIContent * aBeforeThis,bool aNotify,ErrorResult & aRv)59 void HTMLOptGroupElement::InsertChildBefore(nsIContent* aKid,
60 nsIContent* aBeforeThis,
61 bool aNotify, ErrorResult& aRv) {
62 const uint32_t index =
63 aBeforeThis ? *ComputeIndexOf(aBeforeThis) : GetChildCount();
64 SafeOptionListMutation safeMutation(GetSelect(), this, aKid, index, aNotify);
65 nsGenericHTMLElement::InsertChildBefore(aKid, aBeforeThis, aNotify, aRv);
66 if (aRv.Failed()) {
67 safeMutation.MutationFailed();
68 }
69 }
70
RemoveChildNode(nsIContent * aKid,bool aNotify)71 void HTMLOptGroupElement::RemoveChildNode(nsIContent* aKid, bool aNotify) {
72 SafeOptionListMutation safeMutation(GetSelect(), this, nullptr,
73 *ComputeIndexOf(aKid), aNotify);
74 nsGenericHTMLElement::RemoveChildNode(aKid, aNotify);
75 }
76
AfterSetAttr(int32_t aNameSpaceID,nsAtom * aName,const nsAttrValue * aValue,const nsAttrValue * aOldValue,nsIPrincipal * aSubjectPrincipal,bool aNotify)77 nsresult HTMLOptGroupElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
78 const nsAttrValue* aValue,
79 const nsAttrValue* aOldValue,
80 nsIPrincipal* aSubjectPrincipal,
81 bool aNotify) {
82 if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::disabled) {
83 EventStates disabledStates;
84 if (aValue) {
85 disabledStates |= NS_EVENT_STATE_DISABLED;
86 } else {
87 disabledStates |= NS_EVENT_STATE_ENABLED;
88 }
89
90 EventStates oldDisabledStates = State() & DISABLED_STATES;
91 EventStates changedStates = disabledStates ^ oldDisabledStates;
92
93 if (!changedStates.IsEmpty()) {
94 ToggleStates(changedStates, aNotify);
95
96 // All our children <option> have their :disabled state depending on our
97 // disabled attribute. We should make sure their state is updated.
98 for (nsIContent* child = nsINode::GetFirstChild(); child;
99 child = child->GetNextSibling()) {
100 if (auto optElement = HTMLOptionElement::FromNode(child)) {
101 optElement->OptGroupDisabledChanged(true);
102 }
103 }
104 }
105 }
106
107 return nsGenericHTMLElement::AfterSetAttr(
108 aNameSpaceID, aName, aValue, aOldValue, aSubjectPrincipal, aNotify);
109 }
110
WrapNode(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)111 JSObject* HTMLOptGroupElement::WrapNode(JSContext* aCx,
112 JS::Handle<JSObject*> aGivenProto) {
113 return HTMLOptGroupElement_Binding::Wrap(aCx, this, aGivenProto);
114 }
115
116 } // namespace mozilla::dom
117