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