|
1 /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 /* implements DOM interface for querying and observing media queries */ |
|
7 |
|
8 #include "mozilla/dom/MediaQueryList.h" |
|
9 #include "nsPresContext.h" |
|
10 #include "nsIMediaList.h" |
|
11 #include "nsCSSParser.h" |
|
12 #include "nsIDocument.h" |
|
13 |
|
14 namespace mozilla { |
|
15 namespace dom { |
|
16 |
|
17 MediaQueryList::MediaQueryList(nsPresContext *aPresContext, |
|
18 const nsAString &aMediaQueryList) |
|
19 : mPresContext(aPresContext), |
|
20 mMediaList(new nsMediaList), |
|
21 mMatchesValid(false) |
|
22 { |
|
23 PR_INIT_CLIST(this); |
|
24 |
|
25 SetIsDOMBinding(); |
|
26 |
|
27 nsCSSParser parser; |
|
28 parser.ParseMediaList(aMediaQueryList, nullptr, 0, mMediaList, false); |
|
29 } |
|
30 |
|
31 MediaQueryList::~MediaQueryList() |
|
32 { |
|
33 if (mPresContext) { |
|
34 PR_REMOVE_LINK(this); |
|
35 } |
|
36 } |
|
37 |
|
38 NS_IMPL_CYCLE_COLLECTION_CLASS(MediaQueryList) |
|
39 |
|
40 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(MediaQueryList) |
|
41 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPresContext) |
|
42 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallbacks) |
|
43 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS |
|
44 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
45 |
|
46 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MediaQueryList) |
|
47 if (tmp->mPresContext) { |
|
48 PR_REMOVE_LINK(tmp); |
|
49 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPresContext) |
|
50 } |
|
51 tmp->RemoveAllListeners(); |
|
52 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER |
|
53 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
54 |
|
55 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(MediaQueryList) |
|
56 |
|
57 NS_INTERFACE_MAP_BEGIN(MediaQueryList) |
|
58 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
|
59 NS_INTERFACE_MAP_ENTRY(nsISupports) |
|
60 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(MediaQueryList) |
|
61 NS_INTERFACE_MAP_END |
|
62 |
|
63 NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaQueryList) |
|
64 NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaQueryList) |
|
65 |
|
66 void |
|
67 MediaQueryList::GetMedia(nsAString &aMedia) |
|
68 { |
|
69 mMediaList->GetText(aMedia); |
|
70 } |
|
71 |
|
72 bool |
|
73 MediaQueryList::Matches() |
|
74 { |
|
75 if (!mMatchesValid) { |
|
76 NS_ABORT_IF_FALSE(!HasListeners(), |
|
77 "when listeners present, must keep mMatches current"); |
|
78 RecomputeMatches(); |
|
79 } |
|
80 |
|
81 return mMatches; |
|
82 } |
|
83 |
|
84 void |
|
85 MediaQueryList::AddListener(MediaQueryListListener& aListener) |
|
86 { |
|
87 if (!HasListeners()) { |
|
88 // When we have listeners, the pres context owns a reference to |
|
89 // this. This is a cyclic reference that can only be broken by |
|
90 // cycle collection. |
|
91 NS_ADDREF_THIS(); |
|
92 } |
|
93 |
|
94 if (!mMatchesValid) { |
|
95 NS_ABORT_IF_FALSE(!HasListeners(), |
|
96 "when listeners present, must keep mMatches current"); |
|
97 RecomputeMatches(); |
|
98 } |
|
99 |
|
100 for (uint32_t i = 0; i < mCallbacks.Length(); ++i) { |
|
101 if (aListener == *mCallbacks[i]) { |
|
102 // Already registered |
|
103 return; |
|
104 } |
|
105 } |
|
106 |
|
107 mCallbacks.AppendElement(&aListener); |
|
108 if (!HasListeners()) { |
|
109 // Append failed; undo the AddRef above. |
|
110 NS_RELEASE_THIS(); |
|
111 } |
|
112 } |
|
113 |
|
114 void |
|
115 MediaQueryList::RemoveListener(MediaQueryListListener& aListener) |
|
116 { |
|
117 for (uint32_t i = 0; i < mCallbacks.Length(); ++i) { |
|
118 if (aListener == *mCallbacks[i]) { |
|
119 mCallbacks.RemoveElementAt(i); |
|
120 if (!HasListeners()) { |
|
121 // See NS_ADDREF_THIS() in AddListener. |
|
122 NS_RELEASE_THIS(); |
|
123 } |
|
124 break; |
|
125 } |
|
126 } |
|
127 } |
|
128 |
|
129 void |
|
130 MediaQueryList::RemoveAllListeners() |
|
131 { |
|
132 bool hadListeners = HasListeners(); |
|
133 mCallbacks.Clear(); |
|
134 if (hadListeners) { |
|
135 // See NS_ADDREF_THIS() in AddListener. |
|
136 NS_RELEASE_THIS(); |
|
137 } |
|
138 } |
|
139 |
|
140 void |
|
141 MediaQueryList::RecomputeMatches() |
|
142 { |
|
143 if (!mPresContext) { |
|
144 return; |
|
145 } |
|
146 |
|
147 mMatches = mMediaList->Matches(mPresContext, nullptr); |
|
148 mMatchesValid = true; |
|
149 } |
|
150 |
|
151 void |
|
152 MediaQueryList::MediumFeaturesChanged(NotifyList &aListenersToNotify) |
|
153 { |
|
154 mMatchesValid = false; |
|
155 |
|
156 if (HasListeners()) { |
|
157 bool oldMatches = mMatches; |
|
158 RecomputeMatches(); |
|
159 if (mMatches != oldMatches) { |
|
160 for (uint32_t i = 0, i_end = mCallbacks.Length(); i != i_end; ++i) { |
|
161 HandleChangeData *d = aListenersToNotify.AppendElement(); |
|
162 if (d) { |
|
163 d->mql = this; |
|
164 d->callback = mCallbacks[i]; |
|
165 } |
|
166 } |
|
167 } |
|
168 } |
|
169 } |
|
170 |
|
171 nsISupports* |
|
172 MediaQueryList::GetParentObject() const |
|
173 { |
|
174 if (!mPresContext) { |
|
175 return nullptr; |
|
176 } |
|
177 return mPresContext->Document(); |
|
178 } |
|
179 |
|
180 JSObject* |
|
181 MediaQueryList::WrapObject(JSContext* aCx) |
|
182 { |
|
183 return MediaQueryListBinding::Wrap(aCx, this); |
|
184 } |
|
185 |
|
186 } // namespace dom |
|
187 } // namespace mozilla |