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