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