Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
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 "nsCSSParser.h"
16 : #include "nsIDocument.h"
17 :
18 : #define ONCHANGE_STRING NS_LITERAL_STRING("change")
19 :
20 : namespace mozilla {
21 : namespace dom {
22 :
23 3 : MediaQueryList::MediaQueryList(nsIDocument* aDocument,
24 3 : const nsAString& aMediaQueryList)
25 : : mDocument(aDocument)
26 3 : , mMatchesValid(false)
27 : {
28 : mMediaList =
29 3 : MediaList::Create(aDocument->GetStyleBackendType(), aMediaQueryList);
30 :
31 3 : KeepAliveIfHasListenersFor(ONCHANGE_STRING);
32 3 : }
33 :
34 0 : MediaQueryList::~MediaQueryList()
35 0 : {}
36 :
37 : NS_IMPL_CYCLE_COLLECTION_CLASS(MediaQueryList)
38 :
39 3 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MediaQueryList,
40 : DOMEventTargetHelper)
41 3 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
42 3 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
43 :
44 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MediaQueryList,
45 : DOMEventTargetHelper)
46 0 : if (tmp->mDocument) {
47 0 : tmp->remove();
48 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
49 : }
50 0 : tmp->Disconnect();
51 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
52 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
53 :
54 36 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaQueryList)
55 18 : NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
56 :
57 7 : NS_IMPL_ADDREF_INHERITED(MediaQueryList, DOMEventTargetHelper)
58 3 : NS_IMPL_RELEASE_INHERITED(MediaQueryList, DOMEventTargetHelper)
59 :
60 : void
61 0 : MediaQueryList::GetMedia(nsAString &aMedia)
62 : {
63 0 : mMediaList->GetText(aMedia);
64 0 : }
65 :
66 : bool
67 2 : MediaQueryList::Matches()
68 : {
69 2 : if (!mMatchesValid) {
70 2 : MOZ_ASSERT(!HasListeners(),
71 : "when listeners present, must keep mMatches current");
72 2 : RecomputeMatches();
73 : }
74 :
75 2 : return mMatches;
76 : }
77 :
78 : void
79 1 : MediaQueryList::AddListener(EventListener* aListener, ErrorResult& aRv)
80 : {
81 1 : if (!aListener) {
82 0 : return;
83 : }
84 :
85 2 : AddEventListenerOptionsOrBoolean options;
86 1 : options.SetAsBoolean() = false;
87 :
88 1 : AddEventListener(ONCHANGE_STRING, aListener, options, false, aRv);
89 : }
90 :
91 : void
92 1 : MediaQueryList::AddEventListener(const nsAString& aType,
93 : EventListener* aCallback,
94 : const AddEventListenerOptionsOrBoolean& aOptions,
95 : const dom::Nullable<bool>& aWantsUntrusted,
96 : ErrorResult& aRv)
97 : {
98 1 : if (!mMatchesValid) {
99 1 : MOZ_ASSERT(!HasListeners(),
100 : "when listeners present, must keep mMatches current");
101 1 : RecomputeMatches();
102 : }
103 :
104 1 : DOMEventTargetHelper::AddEventListener(aType, aCallback, aOptions,
105 1 : aWantsUntrusted, aRv);
106 1 : }
107 :
108 : void
109 0 : MediaQueryList::RemoveListener(EventListener* aListener, ErrorResult& aRv)
110 : {
111 0 : if (!aListener) {
112 0 : return;
113 : }
114 :
115 0 : EventListenerOptionsOrBoolean options;
116 0 : options.SetAsBoolean() = false;
117 :
118 0 : RemoveEventListener(ONCHANGE_STRING, aListener, options, aRv);
119 : }
120 :
121 : bool
122 10 : MediaQueryList::HasListeners()
123 : {
124 10 : return HasListenersFor(ONCHANGE_STRING);
125 : }
126 :
127 : void
128 0 : MediaQueryList::Disconnect()
129 : {
130 0 : DisconnectFromOwner();
131 :
132 0 : IgnoreKeepAliveIfHasListenersFor(ONCHANGE_STRING);
133 0 : }
134 :
135 : void
136 4 : MediaQueryList::RecomputeMatches()
137 : {
138 4 : if (!mDocument) {
139 0 : return;
140 : }
141 :
142 4 : if (mDocument->GetParentDocument()) {
143 : // Flush frames on the parent so our prescontext will get
144 : // recreated as needed.
145 0 : mDocument->GetParentDocument()->FlushPendingNotifications(FlushType::Frames);
146 : // That might have killed our document, so recheck that.
147 0 : if (!mDocument) {
148 0 : return;
149 : }
150 : }
151 :
152 4 : nsIPresShell* shell = mDocument->GetShell();
153 4 : if (!shell) {
154 : // XXXbz What's the right behavior here? Spec doesn't say.
155 0 : return;
156 : }
157 :
158 4 : nsPresContext* presContext = shell->GetPresContext();
159 4 : if (!presContext) {
160 : // XXXbz What's the right behavior here? Spec doesn't say.
161 0 : return;
162 : }
163 :
164 4 : mMatches = mMediaList->Matches(presContext);
165 4 : mMatchesValid = true;
166 : }
167 :
168 : nsISupports*
169 3 : MediaQueryList::GetParentObject() const
170 : {
171 3 : return mDocument;
172 : }
173 :
174 : JSObject*
175 3 : MediaQueryList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
176 : {
177 3 : return MediaQueryListBinding::Wrap(aCx, this, aGivenProto);
178 : }
179 :
180 : void
181 7 : MediaQueryList::MaybeNotify()
182 : {
183 7 : mMatchesValid = false;
184 :
185 7 : if (!HasListeners()) {
186 13 : return;
187 : }
188 :
189 1 : bool oldMatches = mMatches;
190 1 : RecomputeMatches();
191 :
192 : // No need to notify the change.
193 1 : if (mMatches == oldMatches) {
194 1 : return;
195 : }
196 :
197 0 : MediaQueryListEventInit init;
198 0 : init.mBubbles = false;
199 0 : init.mCancelable = false;
200 0 : init.mMatches = mMatches;
201 0 : mMediaList->GetText(init.mMedia);
202 :
203 : RefPtr<MediaQueryListEvent> event =
204 0 : MediaQueryListEvent::Constructor(this, ONCHANGE_STRING, init);
205 0 : event->SetTrusted(true);
206 :
207 : bool dummy;
208 0 : DispatchEvent(event, &dummy);
209 : }
210 :
211 : } // namespace dom
212 : } // namespace mozilla
|