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/dom/HTMLOptGroupElement.h"
10 #include "mozilla/dom/HTMLOptGroupElementBinding.h"
11 #include "mozilla/dom/HTMLSelectElement.h" // SafeOptionListMutation
12 #include "nsGkAtoms.h"
13 #include "nsStyleConsts.h"
14 #include "nsIFrame.h"
15 #include "nsIFormControlFrame.h"
16
17 NS_IMPL_NS_NEW_HTML_ELEMENT(OptGroup)
18
19 namespace mozilla {
20 namespace 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()->mUserInput == 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)59 nsresult HTMLOptGroupElement::InsertChildBefore(nsIContent* aKid,
60 nsIContent* aBeforeThis,
61 bool aNotify) {
62 int32_t index = aBeforeThis ? ComputeIndexOf(aBeforeThis) : GetChildCount();
63 SafeOptionListMutation safeMutation(GetSelect(), this, aKid, index, aNotify);
64 nsresult rv =
65 nsGenericHTMLElement::InsertChildBefore(aKid, aBeforeThis, aNotify);
66 if (NS_FAILED(rv)) {
67 safeMutation.MutationFailed();
68 }
69 return rv;
70 }
71
RemoveChildNode(nsIContent * aKid,bool aNotify)72 void HTMLOptGroupElement::RemoveChildNode(nsIContent* aKid, bool aNotify) {
73 SafeOptionListMutation safeMutation(GetSelect(), this, nullptr,
74 ComputeIndexOf(aKid), aNotify);
75 nsGenericHTMLElement::RemoveChildNode(aKid, aNotify);
76 }
77
AfterSetAttr(int32_t aNameSpaceID,nsAtom * aName,const nsAttrValue * aValue,const nsAttrValue * aOldValue,nsIPrincipal * aSubjectPrincipal,bool aNotify)78 nsresult HTMLOptGroupElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
79 const nsAttrValue* aValue,
80 const nsAttrValue* aOldValue,
81 nsIPrincipal* aSubjectPrincipal,
82 bool aNotify) {
83 if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::disabled) {
84 EventStates disabledStates;
85 if (aValue) {
86 disabledStates |= NS_EVENT_STATE_DISABLED;
87 } else {
88 disabledStates |= NS_EVENT_STATE_ENABLED;
89 }
90
91 EventStates oldDisabledStates = State() & DISABLED_STATES;
92 EventStates changedStates = disabledStates ^ oldDisabledStates;
93
94 if (!changedStates.IsEmpty()) {
95 ToggleStates(changedStates, aNotify);
96
97 // All our children <option> have their :disabled state depending on our
98 // disabled attribute. We should make sure their state is updated.
99 for (nsIContent* child = nsINode::GetFirstChild(); child;
100 child = child->GetNextSibling()) {
101 if (auto optElement = HTMLOptionElement::FromNode(child)) {
102 optElement->OptGroupDisabledChanged(true);
103 }
104 }
105 }
106 }
107
108 return nsGenericHTMLElement::AfterSetAttr(
109 aNameSpaceID, aName, aValue, aOldValue, aSubjectPrincipal, aNotify);
110 }
111
WrapNode(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)112 JSObject* HTMLOptGroupElement::WrapNode(JSContext* aCx,
113 JS::Handle<JSObject*> aGivenProto) {
114 return HTMLOptGroupElement_Binding::Wrap(aCx, this, aGivenProto);
115 }
116
117 } // namespace dom
118 } // namespace mozilla
119