/src/mozilla-central/layout/style/MediaQueryList.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | /* implements DOM interface for querying and observing media queries */ |
8 | | |
9 | | #include "mozilla/dom/MediaQueryList.h" |
10 | | #include "mozilla/dom/MediaQueryListEvent.h" |
11 | | #include "mozilla/dom/MediaList.h" |
12 | | #include "mozilla/dom/EventTarget.h" |
13 | | #include "mozilla/dom/EventTargetBinding.h" |
14 | | #include "nsPresContext.h" |
15 | | #include "nsIDocument.h" |
16 | | |
17 | 0 | #define ONCHANGE_STRING NS_LITERAL_STRING("change") |
18 | | |
19 | | namespace mozilla { |
20 | | namespace dom { |
21 | | |
22 | | MediaQueryList::MediaQueryList(nsIDocument* aDocument, |
23 | | const nsAString& aMediaQueryList, |
24 | | CallerType aCallerType) |
25 | | : DOMEventTargetHelper(aDocument->GetInnerWindow()) |
26 | | , mDocument(aDocument) |
27 | | , mMatches(false) |
28 | | , mMatchesValid(false) |
29 | 0 | { |
30 | 0 | mMediaList = MediaList::Create(aMediaQueryList, aCallerType); |
31 | 0 |
|
32 | 0 | KeepAliveIfHasListenersFor(ONCHANGE_STRING); |
33 | 0 | } |
34 | | |
35 | | MediaQueryList::~MediaQueryList() |
36 | 0 | {} |
37 | | |
38 | | NS_IMPL_CYCLE_COLLECTION_CLASS(MediaQueryList) |
39 | | |
40 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MediaQueryList, |
41 | 0 | DOMEventTargetHelper) |
42 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument) |
43 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
44 | | |
45 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MediaQueryList, |
46 | 0 | DOMEventTargetHelper) |
47 | 0 | if (tmp->mDocument) { |
48 | 0 | static_cast<LinkedListElement<MediaQueryList>*>(tmp)->remove(); |
49 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument) |
50 | 0 | } |
51 | 0 | tmp->Disconnect(); |
52 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER |
53 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
54 | | |
55 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaQueryList) |
56 | 0 | NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) |
57 | | |
58 | | NS_IMPL_ADDREF_INHERITED(MediaQueryList, DOMEventTargetHelper) |
59 | | NS_IMPL_RELEASE_INHERITED(MediaQueryList, DOMEventTargetHelper) |
60 | | |
61 | | void |
62 | | MediaQueryList::GetMedia(nsAString &aMedia) |
63 | 0 | { |
64 | 0 | mMediaList->GetText(aMedia); |
65 | 0 | } |
66 | | |
67 | | bool |
68 | | MediaQueryList::Matches() |
69 | 0 | { |
70 | 0 | if (!mMatchesValid) { |
71 | 0 | MOZ_ASSERT(!HasListeners(), |
72 | 0 | "when listeners present, must keep mMatches current"); |
73 | 0 | RecomputeMatches(); |
74 | 0 | } |
75 | 0 |
|
76 | 0 | return mMatches; |
77 | 0 | } |
78 | | |
79 | | void |
80 | | MediaQueryList::AddListener(EventListener* aListener, ErrorResult& aRv) |
81 | 0 | { |
82 | 0 | if (!aListener) { |
83 | 0 | return; |
84 | 0 | } |
85 | 0 | |
86 | 0 | AddEventListenerOptionsOrBoolean options; |
87 | 0 | options.SetAsBoolean() = false; |
88 | 0 |
|
89 | 0 | AddEventListener(ONCHANGE_STRING, aListener, options, false, aRv); |
90 | 0 | } |
91 | | |
92 | | void |
93 | | MediaQueryList::EventListenerAdded(nsAtom* aType) |
94 | 0 | { |
95 | 0 | // HasListeners() might still be false if the added thing wasn't a |
96 | 0 | // listener we care about. |
97 | 0 | if (!mMatchesValid && HasListeners()) { |
98 | 0 | RecomputeMatches(); |
99 | 0 | } |
100 | 0 |
|
101 | 0 | DOMEventTargetHelper::EventListenerAdded(aType); |
102 | 0 | } |
103 | | |
104 | | void |
105 | | MediaQueryList::RemoveListener(EventListener* aListener, ErrorResult& aRv) |
106 | 0 | { |
107 | 0 | if (!aListener) { |
108 | 0 | return; |
109 | 0 | } |
110 | 0 | |
111 | 0 | EventListenerOptionsOrBoolean options; |
112 | 0 | options.SetAsBoolean() = false; |
113 | 0 |
|
114 | 0 | RemoveEventListener(ONCHANGE_STRING, aListener, options, aRv); |
115 | 0 | } |
116 | | |
117 | | bool |
118 | | MediaQueryList::HasListeners() |
119 | 0 | { |
120 | 0 | return HasListenersFor(ONCHANGE_STRING); |
121 | 0 | } |
122 | | |
123 | | void |
124 | | MediaQueryList::Disconnect() |
125 | 0 | { |
126 | 0 | DisconnectFromOwner(); |
127 | 0 |
|
128 | 0 | IgnoreKeepAliveIfHasListenersFor(ONCHANGE_STRING); |
129 | 0 | } |
130 | | |
131 | | void |
132 | | MediaQueryList::RecomputeMatches() |
133 | 0 | { |
134 | 0 | mMatches = false; |
135 | 0 |
|
136 | 0 | if (!mDocument) { |
137 | 0 | return; |
138 | 0 | } |
139 | 0 | |
140 | 0 | // FIXME(emilio, bug 1490401): We shouldn't need a pres context to evaluate |
141 | 0 | // media queries. |
142 | 0 | nsPresContext* presContext = mDocument->GetPresContext(); |
143 | 0 | if (!presContext && mDocument->GetParentDocument()) { |
144 | 0 | // Flush frames on the parent so our prescontext will get |
145 | 0 | // created if needed. |
146 | 0 | mDocument->GetParentDocument()->FlushPendingNotifications(FlushType::Frames); |
147 | 0 | // That might have killed our document, so recheck that. |
148 | 0 | if (!mDocument) { |
149 | 0 | return; |
150 | 0 | } |
151 | 0 | |
152 | 0 | presContext = mDocument->GetPresContext(); |
153 | 0 | } |
154 | 0 |
|
155 | 0 | if (!presContext) { |
156 | 0 | // XXXbz What's the right behavior here? Spec doesn't say. |
157 | 0 | return; |
158 | 0 | } |
159 | 0 | |
160 | 0 | mMatches = mMediaList->Matches(presContext); |
161 | 0 | mMatchesValid = true; |
162 | 0 | } |
163 | | |
164 | | nsISupports* |
165 | | MediaQueryList::GetParentObject() const |
166 | 0 | { |
167 | 0 | return mDocument; |
168 | 0 | } |
169 | | |
170 | | JSObject* |
171 | | MediaQueryList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) |
172 | 0 | { |
173 | 0 | return MediaQueryList_Binding::Wrap(aCx, this, aGivenProto); |
174 | 0 | } |
175 | | |
176 | | void |
177 | | MediaQueryList::MaybeNotify() |
178 | 0 | { |
179 | 0 | mMatchesValid = false; |
180 | 0 |
|
181 | 0 | if (!HasListeners()) { |
182 | 0 | return; |
183 | 0 | } |
184 | 0 | |
185 | 0 | bool oldMatches = mMatches; |
186 | 0 | RecomputeMatches(); |
187 | 0 |
|
188 | 0 | // No need to notify the change. |
189 | 0 | if (mMatches == oldMatches) { |
190 | 0 | return; |
191 | 0 | } |
192 | 0 | |
193 | 0 | MediaQueryListEventInit init; |
194 | 0 | init.mBubbles = false; |
195 | 0 | init.mCancelable = false; |
196 | 0 | init.mMatches = mMatches; |
197 | 0 | mMediaList->GetText(init.mMedia); |
198 | 0 |
|
199 | 0 | RefPtr<MediaQueryListEvent> event = |
200 | 0 | MediaQueryListEvent::Constructor(this, ONCHANGE_STRING, init); |
201 | 0 | event->SetTrusted(true); |
202 | 0 |
|
203 | 0 | DispatchEvent(*event); |
204 | 0 | } |
205 | | |
206 | | size_t |
207 | | MediaQueryList::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const |
208 | 0 | { |
209 | 0 | size_t n = 0; |
210 | 0 | // mMediaList is reference counted, but it's created and primarily owned |
211 | 0 | // by this MediaQueryList object. |
212 | 0 | n += mMediaList->SizeOfIncludingThis(aMallocSizeOf); |
213 | 0 | return n; |
214 | 0 | } |
215 | | |
216 | | } // namespace dom |
217 | | } // namespace mozilla |