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 "nsSVGUtils.h"
11 #include "mozilla/Preferences.h"
12 #include "mozilla/dom/SVGSwitchElementBinding.h"
13 
14 class nsIFrame;
15 
16 NS_IMPL_NS_NEW_NAMESPACED_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 SVGSwitchElementBinding::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(aNodeInfo) {}
44 
~SVGSwitchElement()45 SVGSwitchElement::~SVGSwitchElement() {}
46 
MaybeInvalidate()47 void SVGSwitchElement::MaybeInvalidate() {
48   // We must not change mActiveChild until after
49   // InvalidateAndScheduleBoundsUpdate has been called, otherwise
50   // it will not correctly invalidate the old mActiveChild area.
51 
52   nsIContent* newActiveChild = FindActiveChild();
53 
54   if (newActiveChild == mActiveChild) {
55     return;
56   }
57 
58   nsIFrame* frame = GetPrimaryFrame();
59   if (frame) {
60     nsLayoutUtils::PostRestyleEvent(this, nsRestyleHint(0),
61                                     nsChangeHint_InvalidateRenderingObservers);
62     nsSVGUtils::ScheduleReflowSVG(frame);
63   }
64 
65   mActiveChild = newActiveChild;
66 }
67 
68 //----------------------------------------------------------------------
69 // nsIDOMNode methods
70 
NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGSwitchElement)71 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGSwitchElement)
72 
73 //----------------------------------------------------------------------
74 // nsINode methods
75 
76 nsresult SVGSwitchElement::InsertChildBefore(nsIContent* aKid,
77                                              nsIContent* aBeforeThis,
78                                              bool aNotify) {
79   nsresult rv =
80       SVGSwitchElementBase::InsertChildBefore(aKid, aBeforeThis, aNotify);
81   if (NS_SUCCEEDED(rv)) {
82     MaybeInvalidate();
83   }
84   return rv;
85 }
86 
InsertChildAt_Deprecated(nsIContent * aKid,uint32_t aIndex,bool aNotify)87 nsresult SVGSwitchElement::InsertChildAt_Deprecated(nsIContent* aKid,
88                                                     uint32_t aIndex,
89                                                     bool aNotify) {
90   nsresult rv =
91       SVGSwitchElementBase::InsertChildAt_Deprecated(aKid, aIndex, aNotify);
92   if (NS_SUCCEEDED(rv)) {
93     MaybeInvalidate();
94   }
95   return rv;
96 }
97 
RemoveChildAt_Deprecated(uint32_t aIndex,bool aNotify)98 void SVGSwitchElement::RemoveChildAt_Deprecated(uint32_t aIndex, bool aNotify) {
99   SVGSwitchElementBase::RemoveChildAt_Deprecated(aIndex, aNotify);
100   MaybeInvalidate();
101 }
102 
RemoveChildNode(nsIContent * aKid,bool aNotify)103 void SVGSwitchElement::RemoveChildNode(nsIContent* aKid, bool aNotify) {
104   SVGSwitchElementBase::RemoveChildNode(aKid, aNotify);
105   MaybeInvalidate();
106 }
107 
108 //----------------------------------------------------------------------
109 // nsIContent methods
110 
NS_IMETHODIMP_(bool)111 NS_IMETHODIMP_(bool)
112 SVGSwitchElement::IsAttributeMapped(const nsAtom* name) const {
113   static const MappedAttributeEntry* const map[] = {sFEFloodMap,
114                                                     sFiltersMap,
115                                                     sFontSpecificationMap,
116                                                     sGradientStopMap,
117                                                     sLightingEffectsMap,
118                                                     sMarkersMap,
119                                                     sTextContentElementsMap,
120                                                     sViewportsMap};
121 
122   return FindAttributeDependence(name, map) ||
123          SVGSwitchElementBase::IsAttributeMapped(name);
124 }
125 
126 //----------------------------------------------------------------------
127 // Implementation Helpers:
128 
FindActiveChild() const129 nsIContent* SVGSwitchElement::FindActiveChild() const {
130   nsAutoString acceptLangs;
131   Preferences::GetLocalizedString("intl.accept_languages", acceptLangs);
132 
133   if (!acceptLangs.IsEmpty()) {
134     int32_t bestLanguagePreferenceRank = -1;
135     nsIContent* bestChild = nullptr;
136     nsIContent* defaultChild = nullptr;
137     for (nsIContent* child = nsINode::GetFirstChild(); child;
138          child = child->GetNextSibling()) {
139       if (!child->IsElement()) {
140         continue;
141       }
142       nsCOMPtr<SVGTests> tests(do_QueryInterface(child));
143       if (tests) {
144         if (tests->PassesConditionalProcessingTests(
145                 SVGTests::kIgnoreSystemLanguage)) {
146           int32_t languagePreferenceRank =
147               tests->GetBestLanguagePreferenceRank(acceptLangs);
148           switch (languagePreferenceRank) {
149             case 0:
150               // best possible match
151               return child;
152             case -1:
153               // no match
154               break;
155             case -2:
156               // no systemLanguage attribute. If there's nothing better
157               // we'll use the first such child.
158               if (!defaultChild) {
159                 defaultChild = child;
160               }
161               break;
162             default:
163               if (bestLanguagePreferenceRank == -1 ||
164                   languagePreferenceRank < bestLanguagePreferenceRank) {
165                 bestLanguagePreferenceRank = languagePreferenceRank;
166                 bestChild = child;
167               }
168               break;
169           }
170         }
171       } else if (!bestChild) {
172         bestChild = child;
173       }
174     }
175     return bestChild ? bestChild : defaultChild;
176   }
177 
178   for (nsIContent* child = nsINode::GetFirstChild(); child;
179        child = child->GetNextSibling()) {
180     if (!child->IsElement()) {
181       continue;
182     }
183     nsCOMPtr<SVGTests> tests(do_QueryInterface(child));
184     if (!tests || tests->PassesConditionalProcessingTests(&acceptLangs)) {
185       return child;
186     }
187   }
188   return nullptr;
189 }
190 
191 }  // namespace dom
192 }  // namespace mozilla
193