Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
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/. */
6 /* implements DOM interface for querying and observing media queries */
8 #include "mozilla/dom/MediaQueryList.h"
9 #include "nsPresContext.h"
10 #include "nsIMediaList.h"
11 #include "nsCSSParser.h"
12 #include "nsIDocument.h"
14 namespace mozilla {
15 namespace dom {
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);
25 SetIsDOMBinding();
27 nsCSSParser parser;
28 parser.ParseMediaList(aMediaQueryList, nullptr, 0, mMediaList, false);
29 }
31 MediaQueryList::~MediaQueryList()
32 {
33 if (mPresContext) {
34 PR_REMOVE_LINK(this);
35 }
36 }
38 NS_IMPL_CYCLE_COLLECTION_CLASS(MediaQueryList)
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
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
55 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(MediaQueryList)
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
63 NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaQueryList)
64 NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaQueryList)
66 void
67 MediaQueryList::GetMedia(nsAString &aMedia)
68 {
69 mMediaList->GetText(aMedia);
70 }
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 }
81 return mMatches;
82 }
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 }
94 if (!mMatchesValid) {
95 NS_ABORT_IF_FALSE(!HasListeners(),
96 "when listeners present, must keep mMatches current");
97 RecomputeMatches();
98 }
100 for (uint32_t i = 0; i < mCallbacks.Length(); ++i) {
101 if (aListener == *mCallbacks[i]) {
102 // Already registered
103 return;
104 }
105 }
107 mCallbacks.AppendElement(&aListener);
108 if (!HasListeners()) {
109 // Append failed; undo the AddRef above.
110 NS_RELEASE_THIS();
111 }
112 }
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 }
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 }
140 void
141 MediaQueryList::RecomputeMatches()
142 {
143 if (!mPresContext) {
144 return;
145 }
147 mMatches = mMediaList->Matches(mPresContext, nullptr);
148 mMatchesValid = true;
149 }
151 void
152 MediaQueryList::MediumFeaturesChanged(NotifyList &aListenersToNotify)
153 {
154 mMatchesValid = false;
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 }
171 nsISupports*
172 MediaQueryList::GetParentObject() const
173 {
174 if (!mPresContext) {
175 return nullptr;
176 }
177 return mPresContext->Document();
178 }
180 JSObject*
181 MediaQueryList::WrapObject(JSContext* aCx)
182 {
183 return MediaQueryListBinding::Wrap(aCx, this);
184 }
186 } // namespace dom
187 } // namespace mozilla