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/dom/SVGSwitchElement.h"
8
9 #include "nsLayoutUtils.h"
10 #include "mozilla/Preferences.h"
11 #include "mozilla/SVGUtils.h"
12 #include "mozilla/dom/SVGSwitchElementBinding.h"
13
14 class nsIFrame;
15
16 NS_IMPL_NS_NEW_SVG_ELEMENT(Switch)
17
18 namespace mozilla {
19 namespace dom {
20
WrapNode(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)21 JSObject* SVGSwitchElement::WrapNode(JSContext* aCx,
22 JS::Handle<JSObject*> aGivenProto) {
23 return SVGSwitchElement_Binding::Wrap(aCx, this, aGivenProto);
24 }
25
26 //----------------------------------------------------------------------
27 // nsISupports methods
28
NS_IMPL_CYCLE_COLLECTION_INHERITED(SVGSwitchElement,SVGSwitchElementBase,mActiveChild)29 NS_IMPL_CYCLE_COLLECTION_INHERITED(SVGSwitchElement, SVGSwitchElementBase,
30 mActiveChild)
31
32 NS_IMPL_ADDREF_INHERITED(SVGSwitchElement, SVGSwitchElementBase)
33 NS_IMPL_RELEASE_INHERITED(SVGSwitchElement, SVGSwitchElementBase)
34
35 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGSwitchElement)
36 NS_INTERFACE_MAP_END_INHERITING(SVGSwitchElementBase)
37
38 //----------------------------------------------------------------------
39 // Implementation
40
41 SVGSwitchElement::SVGSwitchElement(
42 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
43 : SVGSwitchElementBase(std::move(aNodeInfo)) {}
44
MaybeInvalidate()45 void SVGSwitchElement::MaybeInvalidate() {
46 // We must not change mActiveChild until after
47 // InvalidateAndScheduleBoundsUpdate has been called, otherwise
48 // it will not correctly invalidate the old mActiveChild area.
49
50 nsIContent* newActiveChild = FindActiveChild();
51
52 if (newActiveChild == mActiveChild) {
53 return;
54 }
55
56 nsIFrame* frame = GetPrimaryFrame();
57 if (frame) {
58 nsLayoutUtils::PostRestyleEvent(this, RestyleHint{0},
59 nsChangeHint_InvalidateRenderingObservers);
60 SVGUtils::ScheduleReflowSVG(frame);
61 }
62
63 mActiveChild = newActiveChild;
64 }
65
66 //----------------------------------------------------------------------
67 // nsINode methods
68
NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGSwitchElement)69 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGSwitchElement)
70
71 //----------------------------------------------------------------------
72 // nsINode methods
73
74 void SVGSwitchElement::InsertChildBefore(nsIContent* aKid,
75 nsIContent* aBeforeThis, bool aNotify,
76 ErrorResult& aRv) {
77 SVGSwitchElementBase::InsertChildBefore(aKid, aBeforeThis, aNotify, aRv);
78 if (aRv.Failed()) {
79 return;
80 }
81
82 MaybeInvalidate();
83 }
84
RemoveChildNode(nsIContent * aKid,bool aNotify)85 void SVGSwitchElement::RemoveChildNode(nsIContent* aKid, bool aNotify) {
86 SVGSwitchElementBase::RemoveChildNode(aKid, aNotify);
87 MaybeInvalidate();
88 }
89
90 //----------------------------------------------------------------------
91 // nsIContent methods
92
NS_IMETHODIMP_(bool)93 NS_IMETHODIMP_(bool)
94 SVGSwitchElement::IsAttributeMapped(const nsAtom* name) const {
95 static const MappedAttributeEntry* const map[] = {sFEFloodMap,
96 sFiltersMap,
97 sFontSpecificationMap,
98 sGradientStopMap,
99 sLightingEffectsMap,
100 sMarkersMap,
101 sTextContentElementsMap,
102 sViewportsMap};
103
104 return FindAttributeDependence(name, map) ||
105 SVGSwitchElementBase::IsAttributeMapped(name);
106 }
107
108 //----------------------------------------------------------------------
109 // Implementation Helpers:
110
FindActiveChild() const111 nsIContent* SVGSwitchElement::FindActiveChild() const {
112 nsAutoString acceptLangs;
113 Preferences::GetLocalizedString("intl.accept_languages", acceptLangs);
114
115 int32_t bestLanguagePreferenceRank = -1;
116 nsIContent* bestChild = nullptr;
117 nsIContent* defaultChild = nullptr;
118 for (nsIContent* child = nsINode::GetFirstChild(); child;
119 child = child->GetNextSibling()) {
120 if (!child->IsElement()) {
121 continue;
122 }
123 nsCOMPtr<SVGTests> tests(do_QueryInterface(child));
124 if (tests) {
125 if (tests->PassesConditionalProcessingTestsIgnoringSystemLanguage()) {
126 int32_t languagePreferenceRank =
127 tests->GetBestLanguagePreferenceRank(acceptLangs);
128 switch (languagePreferenceRank) {
129 case 0:
130 // best possible match
131 return child;
132 case -1:
133 // no match
134 break;
135 case -2:
136 // no systemLanguage attribute. If there's nothing better
137 // we'll use the first such child.
138 if (!defaultChild) {
139 defaultChild = child;
140 }
141 break;
142 default:
143 if (bestLanguagePreferenceRank == -1 ||
144 languagePreferenceRank < bestLanguagePreferenceRank) {
145 bestLanguagePreferenceRank = languagePreferenceRank;
146 bestChild = child;
147 }
148 break;
149 }
150 }
151 } else if (!bestChild) {
152 bestChild = child;
153 }
154 }
155 return bestChild ? bestChild : defaultChild;
156 }
157
158 } // namespace dom
159 } // namespace mozilla
160