MediaQueryList.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  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. /* implements DOM interface for querying and observing media queries */
  6. #include "mozilla/dom/MediaQueryList.h"
  7. #include "mozilla/dom/MediaQueryListEvent.h"
  8. #include "mozilla/dom/EventTarget.h"
  9. #include "mozilla/dom/EventTargetBinding.h"
  10. #include "nsPresContext.h"
  11. #include "nsIMediaList.h"
  12. #include "nsCSSParser.h"
  13. #include "nsIDocument.h"
  14. // Fixed event target type
  15. #define ONCHANGE_STRING NS_LITERAL_STRING("change")
  16. namespace mozilla {
  17. namespace dom {
  18. MediaQueryList::MediaQueryList(nsIDocument *aDocument,
  19. const nsAString &aMediaQueryList)
  20. : mDocument(aDocument)
  21. , mMediaList(new nsMediaList)
  22. , mMatchesValid(false)
  23. , mIsKeptAlive(false)
  24. {
  25. PR_INIT_CLIST(this);
  26. nsCSSParser parser;
  27. parser.ParseMediaList(aMediaQueryList, nullptr, 0, mMediaList, false);
  28. }
  29. MediaQueryList::~MediaQueryList()
  30. {
  31. if (mDocument) {
  32. PR_REMOVE_LINK(this);
  33. }
  34. }
  35. NS_IMPL_CYCLE_COLLECTION_CLASS(MediaQueryList)
  36. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MediaQueryList, DOMEventTargetHelper)
  37. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
  38. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
  39. NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MediaQueryList, DOMEventTargetHelper)
  40. if (tmp->mDocument) {
  41. PR_REMOVE_LINK(tmp);
  42. NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
  43. }
  44. tmp->Disconnect();
  45. NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
  46. NS_IMPL_CYCLE_COLLECTION_UNLINK_END
  47. NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaQueryList)
  48. NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
  49. NS_IMPL_ADDREF_INHERITED(MediaQueryList, DOMEventTargetHelper)
  50. NS_IMPL_RELEASE_INHERITED(MediaQueryList, DOMEventTargetHelper)
  51. void
  52. MediaQueryList::GetMedia(nsAString &aMedia)
  53. {
  54. mMediaList->GetText(aMedia);
  55. }
  56. bool
  57. MediaQueryList::Matches()
  58. {
  59. if (!mMatchesValid) {
  60. MOZ_ASSERT(!HasListeners(),
  61. "when listeners present, must keep mMatches current");
  62. RecomputeMatches();
  63. }
  64. return mMatches;
  65. }
  66. void
  67. MediaQueryList::AddListener(EventListener* aListener, ErrorResult& aRv)
  68. {
  69. if (!aListener) {
  70. return;
  71. }
  72. AddEventListenerOptionsOrBoolean options;
  73. options.SetAsBoolean() = false;
  74. AddEventListener(ONCHANGE_STRING, aListener, options, Nullable<bool>(), aRv);
  75. }
  76. void
  77. MediaQueryList::AddEventListener(const nsAString& aType,
  78. EventListener* aCallback,
  79. const AddEventListenerOptionsOrBoolean& aOptions,
  80. const dom::Nullable<bool>& aWantsUntrusted,
  81. ErrorResult& aRv)
  82. {
  83. if (!mMatchesValid) {
  84. MOZ_ASSERT(!HasListeners(),
  85. "when listeners present, must keep mMatches current");
  86. RecomputeMatches();
  87. }
  88. DOMEventTargetHelper::AddEventListener(aType, aCallback, aOptions,
  89. aWantsUntrusted, aRv);
  90. if (aRv.Failed()) {
  91. return;
  92. }
  93. UpdateMustKeepAlive();
  94. }
  95. void
  96. MediaQueryList::RemoveListener(EventListener* aListener, ErrorResult& aRv)
  97. {
  98. if (!aListener) {
  99. return;
  100. }
  101. EventListenerOptionsOrBoolean options;
  102. options.SetAsBoolean() = false;
  103. RemoveEventListener(ONCHANGE_STRING, aListener, options, aRv);
  104. }
  105. void
  106. MediaQueryList::RemoveEventListener(const nsAString& aType,
  107. EventListener* aCallback,
  108. const EventListenerOptionsOrBoolean& aOptions,
  109. ErrorResult& aRv)
  110. {
  111. DOMEventTargetHelper::RemoveEventListener(aType, aCallback, aOptions, aRv);
  112. if (aRv.Failed()) {
  113. return;
  114. }
  115. UpdateMustKeepAlive();
  116. }
  117. EventHandlerNonNull*
  118. MediaQueryList::GetOnchange()
  119. {
  120. if (NS_IsMainThread()) {
  121. return GetEventHandler(nsGkAtoms::onchange, EmptyString());
  122. }
  123. return GetEventHandler(nullptr, ONCHANGE_STRING);
  124. }
  125. void
  126. MediaQueryList::SetOnchange(EventHandlerNonNull* aCallback)
  127. {
  128. if (NS_IsMainThread()) {
  129. SetEventHandler(nsGkAtoms::onchange, EmptyString(), aCallback);
  130. } else {
  131. SetEventHandler(nullptr, ONCHANGE_STRING, aCallback);
  132. }
  133. UpdateMustKeepAlive();
  134. }
  135. void
  136. MediaQueryList::UpdateMustKeepAlive()
  137. {
  138. bool toKeepAlive = HasListeners();
  139. if (toKeepAlive == mIsKeptAlive) {
  140. return;
  141. }
  142. // When we have listeners, the pres context owns a reference to
  143. // this. This is a cyclic reference that can only be broken by
  144. // cycle collection.
  145. mIsKeptAlive = toKeepAlive;
  146. if (toKeepAlive) {
  147. NS_ADDREF_THIS();
  148. } else {
  149. NS_RELEASE_THIS();
  150. }
  151. }
  152. bool
  153. MediaQueryList::HasListeners()
  154. {
  155. return HasListenersFor(ONCHANGE_STRING);
  156. }
  157. void
  158. MediaQueryList::Disconnect()
  159. {
  160. DisconnectFromOwner();
  161. if (mIsKeptAlive) {
  162. mIsKeptAlive = false;
  163. // See NS_ADDREF_THIS() in AddListener.
  164. NS_RELEASE_THIS();
  165. }
  166. }
  167. void
  168. MediaQueryList::RecomputeMatches()
  169. {
  170. if (!mDocument) {
  171. return;
  172. }
  173. if (mDocument->GetParentDocument()) {
  174. // Flush frames on the parent so our prescontext will get
  175. // recreated as needed.
  176. mDocument->GetParentDocument()->FlushPendingNotifications(Flush_Frames);
  177. // That might have killed our document, so recheck that.
  178. if (!mDocument) {
  179. return;
  180. }
  181. }
  182. nsIPresShell* shell = mDocument->GetShell();
  183. if (!shell) {
  184. // XXXbz What's the right behavior here? Spec doesn't say.
  185. return;
  186. }
  187. nsPresContext* presContext = shell->GetPresContext();
  188. if (!presContext) {
  189. // XXXbz What's the right behavior here? Spec doesn't say.
  190. return;
  191. }
  192. mMatches = mMediaList->Matches(presContext, nullptr);
  193. mMatchesValid = true;
  194. }
  195. nsISupports*
  196. MediaQueryList::GetParentObject() const
  197. {
  198. return mDocument;
  199. }
  200. JSObject*
  201. MediaQueryList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
  202. {
  203. return MediaQueryListBinding::Wrap(aCx, this, aGivenProto);
  204. }
  205. void
  206. MediaQueryList::MaybeNotify()
  207. {
  208. mMatchesValid = false;
  209. if (!HasListeners()) {
  210. return;
  211. }
  212. bool oldMatches = mMatches;
  213. RecomputeMatches();
  214. // No need to notify the change.
  215. if (mMatches == oldMatches) {
  216. return;
  217. }
  218. MediaQueryListEventInit init;
  219. init.mBubbles = false;
  220. init.mCancelable = false;
  221. init.mMatches = mMatches;
  222. mMediaList->GetText(init.mMedia);
  223. RefPtr<MediaQueryListEvent> event =
  224. MediaQueryListEvent::Constructor(this, ONCHANGE_STRING, init);
  225. event->SetTrusted(true);
  226. bool dummy;
  227. DispatchEvent(event, &dummy);
  228. }
  229. } // namespace dom
  230. } // namespace mozilla