|
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 |
|
6 #include "mozilla/Hal.h" |
|
7 #include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent() |
|
8 #include "mozilla/dom/ScreenBinding.h" |
|
9 #include "nsScreen.h" |
|
10 #include "nsIDocument.h" |
|
11 #include "nsIDocShell.h" |
|
12 #include "nsIDocument.h" |
|
13 #include "nsPresContext.h" |
|
14 #include "nsCOMPtr.h" |
|
15 #include "nsIDocShellTreeItem.h" |
|
16 #include "nsLayoutUtils.h" |
|
17 #include "nsJSUtils.h" |
|
18 #include "nsDeviceContext.h" |
|
19 |
|
20 using namespace mozilla; |
|
21 using namespace mozilla::dom; |
|
22 |
|
23 /* static */ already_AddRefed<nsScreen> |
|
24 nsScreen::Create(nsPIDOMWindow* aWindow) |
|
25 { |
|
26 MOZ_ASSERT(aWindow); |
|
27 |
|
28 if (!aWindow->GetDocShell()) { |
|
29 return nullptr; |
|
30 } |
|
31 |
|
32 nsCOMPtr<nsIScriptGlobalObject> sgo = |
|
33 do_QueryInterface(static_cast<nsPIDOMWindow*>(aWindow)); |
|
34 NS_ENSURE_TRUE(sgo, nullptr); |
|
35 |
|
36 nsRefPtr<nsScreen> screen = new nsScreen(aWindow); |
|
37 |
|
38 hal::RegisterScreenConfigurationObserver(screen); |
|
39 hal::ScreenConfiguration config; |
|
40 hal::GetCurrentScreenConfiguration(&config); |
|
41 screen->mOrientation = config.orientation(); |
|
42 |
|
43 return screen.forget(); |
|
44 } |
|
45 |
|
46 nsScreen::nsScreen(nsPIDOMWindow* aWindow) |
|
47 : DOMEventTargetHelper(aWindow) |
|
48 , mEventListener(nullptr) |
|
49 { |
|
50 } |
|
51 |
|
52 nsScreen::~nsScreen() |
|
53 { |
|
54 MOZ_ASSERT(!mEventListener); |
|
55 hal::UnregisterScreenConfigurationObserver(this); |
|
56 } |
|
57 |
|
58 |
|
59 // QueryInterface implementation for nsScreen |
|
60 NS_INTERFACE_MAP_BEGIN(nsScreen) |
|
61 NS_INTERFACE_MAP_ENTRY(nsIDOMScreen) |
|
62 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) |
|
63 |
|
64 NS_IMPL_ADDREF_INHERITED(nsScreen, DOMEventTargetHelper) |
|
65 NS_IMPL_RELEASE_INHERITED(nsScreen, DOMEventTargetHelper) |
|
66 |
|
67 int32_t |
|
68 nsScreen::GetPixelDepth(ErrorResult& aRv) |
|
69 { |
|
70 // For non-chrome callers, always return 24 to prevent fingerprinting. |
|
71 if (!IsChrome()) return 24; |
|
72 |
|
73 nsDeviceContext* context = GetDeviceContext(); |
|
74 |
|
75 if (!context) { |
|
76 aRv.Throw(NS_ERROR_FAILURE); |
|
77 return -1; |
|
78 } |
|
79 |
|
80 uint32_t depth; |
|
81 context->GetDepth(depth); |
|
82 return depth; |
|
83 } |
|
84 |
|
85 #define FORWARD_LONG_GETTER(_name) \ |
|
86 NS_IMETHODIMP \ |
|
87 nsScreen::Get ## _name(int32_t* aOut) \ |
|
88 { \ |
|
89 ErrorResult rv; \ |
|
90 *aOut = Get ## _name(rv); \ |
|
91 return rv.ErrorCode(); \ |
|
92 } |
|
93 |
|
94 FORWARD_LONG_GETTER(AvailWidth) |
|
95 FORWARD_LONG_GETTER(AvailHeight) |
|
96 FORWARD_LONG_GETTER(Width) |
|
97 FORWARD_LONG_GETTER(Height) |
|
98 |
|
99 FORWARD_LONG_GETTER(Top) |
|
100 FORWARD_LONG_GETTER(Left) |
|
101 FORWARD_LONG_GETTER(AvailTop) |
|
102 FORWARD_LONG_GETTER(AvailLeft) |
|
103 |
|
104 FORWARD_LONG_GETTER(PixelDepth) |
|
105 FORWARD_LONG_GETTER(ColorDepth) |
|
106 |
|
107 nsDeviceContext* |
|
108 nsScreen::GetDeviceContext() |
|
109 { |
|
110 return nsLayoutUtils::GetDeviceContextForScreenInfo(GetOwner()); |
|
111 } |
|
112 |
|
113 nsresult |
|
114 nsScreen::GetRect(nsRect& aRect) |
|
115 { |
|
116 // For non-chrome callers, return window inner rect to prevent fingerprinting. |
|
117 if (!IsChrome()) return GetWindowInnerRect(aRect); |
|
118 |
|
119 nsDeviceContext *context = GetDeviceContext(); |
|
120 |
|
121 if (!context) { |
|
122 return NS_ERROR_FAILURE; |
|
123 } |
|
124 |
|
125 context->GetRect(aRect); |
|
126 |
|
127 aRect.x = nsPresContext::AppUnitsToIntCSSPixels(aRect.x); |
|
128 aRect.y = nsPresContext::AppUnitsToIntCSSPixels(aRect.y); |
|
129 aRect.height = nsPresContext::AppUnitsToIntCSSPixels(aRect.height); |
|
130 aRect.width = nsPresContext::AppUnitsToIntCSSPixels(aRect.width); |
|
131 |
|
132 return NS_OK; |
|
133 } |
|
134 |
|
135 nsresult |
|
136 nsScreen::GetAvailRect(nsRect& aRect) |
|
137 { |
|
138 // For non-chrome callers, return window inner rect to prevent fingerprinting. |
|
139 if (!IsChrome()) return GetWindowInnerRect(aRect); |
|
140 |
|
141 nsDeviceContext *context = GetDeviceContext(); |
|
142 |
|
143 if (!context) { |
|
144 return NS_ERROR_FAILURE; |
|
145 } |
|
146 |
|
147 context->GetClientRect(aRect); |
|
148 |
|
149 aRect.x = nsPresContext::AppUnitsToIntCSSPixels(aRect.x); |
|
150 aRect.y = nsPresContext::AppUnitsToIntCSSPixels(aRect.y); |
|
151 aRect.height = nsPresContext::AppUnitsToIntCSSPixels(aRect.height); |
|
152 aRect.width = nsPresContext::AppUnitsToIntCSSPixels(aRect.width); |
|
153 |
|
154 return NS_OK; |
|
155 } |
|
156 |
|
157 void |
|
158 nsScreen::Notify(const hal::ScreenConfiguration& aConfiguration) |
|
159 { |
|
160 ScreenOrientation previousOrientation = mOrientation; |
|
161 mOrientation = aConfiguration.orientation(); |
|
162 |
|
163 NS_ASSERTION(mOrientation == eScreenOrientation_PortraitPrimary || |
|
164 mOrientation == eScreenOrientation_PortraitSecondary || |
|
165 mOrientation == eScreenOrientation_LandscapePrimary || |
|
166 mOrientation == eScreenOrientation_LandscapeSecondary, |
|
167 "Invalid orientation value passed to notify method!"); |
|
168 |
|
169 if (mOrientation != previousOrientation) { |
|
170 DispatchTrustedEvent(NS_LITERAL_STRING("mozorientationchange")); |
|
171 } |
|
172 } |
|
173 |
|
174 void |
|
175 nsScreen::GetMozOrientation(nsString& aOrientation) |
|
176 { |
|
177 if (!IsChrome()) { |
|
178 aOrientation.AssignLiteral("landscape-primary"); |
|
179 } else { |
|
180 switch (mOrientation) { |
|
181 case eScreenOrientation_PortraitPrimary: |
|
182 aOrientation.AssignLiteral("portrait-primary"); |
|
183 break; |
|
184 case eScreenOrientation_PortraitSecondary: |
|
185 aOrientation.AssignLiteral("portrait-secondary"); |
|
186 break; |
|
187 case eScreenOrientation_LandscapePrimary: |
|
188 aOrientation.AssignLiteral("landscape-primary"); |
|
189 break; |
|
190 case eScreenOrientation_LandscapeSecondary: |
|
191 aOrientation.AssignLiteral("landscape-secondary"); |
|
192 break; |
|
193 case eScreenOrientation_None: |
|
194 default: |
|
195 MOZ_CRASH("Unacceptable mOrientation value"); |
|
196 } |
|
197 } |
|
198 } |
|
199 |
|
200 NS_IMETHODIMP |
|
201 nsScreen::GetSlowMozOrientation(nsAString& aOrientation) |
|
202 { |
|
203 nsString orientation; |
|
204 GetMozOrientation(orientation); |
|
205 aOrientation = orientation; |
|
206 return NS_OK; |
|
207 } |
|
208 |
|
209 nsScreen::LockPermission |
|
210 nsScreen::GetLockOrientationPermission() const |
|
211 { |
|
212 nsCOMPtr<nsPIDOMWindow> owner = GetOwner(); |
|
213 if (!owner) { |
|
214 return LOCK_DENIED; |
|
215 } |
|
216 |
|
217 // Chrome can always lock the screen orientation. |
|
218 nsIDocShell* docShell = owner->GetDocShell(); |
|
219 if (docShell && docShell->ItemType() == nsIDocShellTreeItem::typeChrome) { |
|
220 return LOCK_ALLOWED; |
|
221 } |
|
222 |
|
223 nsCOMPtr<nsIDocument> doc = owner->GetDoc(); |
|
224 if (!doc || doc->Hidden()) { |
|
225 return LOCK_DENIED; |
|
226 } |
|
227 |
|
228 // Apps can always lock the screen orientation. |
|
229 if (doc->NodePrincipal()->GetAppStatus() >= |
|
230 nsIPrincipal::APP_STATUS_INSTALLED) { |
|
231 return LOCK_ALLOWED; |
|
232 } |
|
233 |
|
234 // Other content must be full-screen in order to lock orientation. |
|
235 return doc->MozFullScreen() ? FULLSCREEN_LOCK_ALLOWED : LOCK_DENIED; |
|
236 } |
|
237 |
|
238 bool |
|
239 nsScreen::MozLockOrientation(const nsAString& aOrientation, ErrorResult& aRv) |
|
240 { |
|
241 nsString orientation(aOrientation); |
|
242 Sequence<nsString> orientations; |
|
243 if (!orientations.AppendElement(orientation)) { |
|
244 aRv.Throw(NS_ERROR_OUT_OF_MEMORY); |
|
245 return false; |
|
246 } |
|
247 return MozLockOrientation(orientations, aRv); |
|
248 } |
|
249 |
|
250 bool |
|
251 nsScreen::MozLockOrientation(const Sequence<nsString>& aOrientations, |
|
252 ErrorResult& aRv) |
|
253 { |
|
254 ScreenOrientation orientation = eScreenOrientation_None; |
|
255 |
|
256 for (uint32_t i = 0; i < aOrientations.Length(); ++i) { |
|
257 const nsString& item = aOrientations[i]; |
|
258 |
|
259 if (item.EqualsLiteral("portrait")) { |
|
260 orientation |= eScreenOrientation_PortraitPrimary | |
|
261 eScreenOrientation_PortraitSecondary; |
|
262 } else if (item.EqualsLiteral("portrait-primary")) { |
|
263 orientation |= eScreenOrientation_PortraitPrimary; |
|
264 } else if (item.EqualsLiteral("portrait-secondary")) { |
|
265 orientation |= eScreenOrientation_PortraitSecondary; |
|
266 } else if (item.EqualsLiteral("landscape")) { |
|
267 orientation |= eScreenOrientation_LandscapePrimary | |
|
268 eScreenOrientation_LandscapeSecondary; |
|
269 } else if (item.EqualsLiteral("landscape-primary")) { |
|
270 orientation |= eScreenOrientation_LandscapePrimary; |
|
271 } else if (item.EqualsLiteral("landscape-secondary")) { |
|
272 orientation |= eScreenOrientation_LandscapeSecondary; |
|
273 } else if (item.EqualsLiteral("default")) { |
|
274 orientation |= eScreenOrientation_Default; |
|
275 } else { |
|
276 // If we don't recognize the token, we should just return 'false' |
|
277 // without throwing. |
|
278 return false; |
|
279 } |
|
280 } |
|
281 |
|
282 switch (GetLockOrientationPermission()) { |
|
283 case LOCK_DENIED: |
|
284 return false; |
|
285 case LOCK_ALLOWED: |
|
286 return hal::LockScreenOrientation(orientation); |
|
287 case FULLSCREEN_LOCK_ALLOWED: { |
|
288 // We need to register a listener so we learn when we leave full-screen |
|
289 // and when we will have to unlock the screen. |
|
290 // This needs to be done before LockScreenOrientation call to make sure |
|
291 // the locking can be unlocked. |
|
292 nsCOMPtr<EventTarget> target = do_QueryInterface(GetOwner()->GetDoc()); |
|
293 if (!target) { |
|
294 return false; |
|
295 } |
|
296 |
|
297 if (!hal::LockScreenOrientation(orientation)) { |
|
298 return false; |
|
299 } |
|
300 |
|
301 // We are fullscreen and lock has been accepted. |
|
302 if (!mEventListener) { |
|
303 mEventListener = new FullScreenEventListener(); |
|
304 } |
|
305 |
|
306 aRv = target->AddSystemEventListener(NS_LITERAL_STRING("mozfullscreenchange"), |
|
307 mEventListener, /* useCapture = */ true); |
|
308 return true; |
|
309 } |
|
310 } |
|
311 |
|
312 // This is only for compilers that don't understand that the previous switch |
|
313 // will always return. |
|
314 MOZ_CRASH("unexpected lock orientation permission value"); |
|
315 } |
|
316 |
|
317 void |
|
318 nsScreen::MozUnlockOrientation() |
|
319 { |
|
320 hal::UnlockScreenOrientation(); |
|
321 } |
|
322 |
|
323 bool |
|
324 nsScreen::IsDeviceSizePageSize() |
|
325 { |
|
326 nsPIDOMWindow* owner = GetOwner(); |
|
327 if (owner) { |
|
328 nsIDocShell* docShell = owner->GetDocShell(); |
|
329 if (docShell) { |
|
330 return docShell->GetDeviceSizeIsPageSize(); |
|
331 } |
|
332 } |
|
333 return false; |
|
334 } |
|
335 |
|
336 /* virtual */ |
|
337 JSObject* |
|
338 nsScreen::WrapObject(JSContext* aCx) |
|
339 { |
|
340 return ScreenBinding::Wrap(aCx, this); |
|
341 } |
|
342 |
|
343 NS_IMPL_ISUPPORTS(nsScreen::FullScreenEventListener, nsIDOMEventListener) |
|
344 |
|
345 NS_IMETHODIMP |
|
346 nsScreen::FullScreenEventListener::HandleEvent(nsIDOMEvent* aEvent) |
|
347 { |
|
348 #ifdef DEBUG |
|
349 nsAutoString eventType; |
|
350 aEvent->GetType(eventType); |
|
351 |
|
352 MOZ_ASSERT(eventType.EqualsLiteral("mozfullscreenchange")); |
|
353 #endif |
|
354 |
|
355 nsCOMPtr<EventTarget> target = aEvent->InternalDOMEvent()->GetCurrentTarget(); |
|
356 MOZ_ASSERT(target); |
|
357 |
|
358 nsCOMPtr<nsIDocument> doc = do_QueryInterface(target); |
|
359 MOZ_ASSERT(doc); |
|
360 |
|
361 // We have to make sure that the event we got is the event sent when |
|
362 // fullscreen is disabled because we could get one when fullscreen |
|
363 // got enabled if the lock call is done at the same moment. |
|
364 if (doc->MozFullScreen()) { |
|
365 return NS_OK; |
|
366 } |
|
367 |
|
368 target->RemoveSystemEventListener(NS_LITERAL_STRING("mozfullscreenchange"), |
|
369 this, true); |
|
370 |
|
371 hal::UnlockScreenOrientation(); |
|
372 |
|
373 return NS_OK; |
|
374 } |
|
375 |
|
376 bool |
|
377 nsScreen::IsChrome() |
|
378 { |
|
379 nsCOMPtr<nsPIDOMWindow> owner = GetOwner(); |
|
380 if (owner && owner->GetDocShell()) { |
|
381 return owner->GetDocShell()->ItemType() == nsIDocShellTreeItem::typeChrome; |
|
382 } |
|
383 return false; |
|
384 } |
|
385 |
|
386 nsresult |
|
387 nsScreen::GetDOMWindow(nsIDOMWindow **aResult) |
|
388 { |
|
389 NS_ENSURE_ARG_POINTER(aResult); |
|
390 *aResult = NULL; |
|
391 |
|
392 nsCOMPtr<nsPIDOMWindow> owner = GetOwner(); |
|
393 if (!owner) |
|
394 return NS_ERROR_FAILURE; |
|
395 |
|
396 nsCOMPtr<nsIDOMWindow> win = do_QueryInterface(owner); |
|
397 NS_ENSURE_STATE(win); |
|
398 win.swap(*aResult); |
|
399 |
|
400 return NS_OK; |
|
401 } |
|
402 |
|
403 nsresult |
|
404 nsScreen::GetWindowInnerRect(nsRect& aRect) |
|
405 { |
|
406 aRect.x = 0; |
|
407 aRect.y = 0; |
|
408 nsCOMPtr<nsIDOMWindow> win; |
|
409 nsresult rv = GetDOMWindow(getter_AddRefs(win)); |
|
410 NS_ENSURE_SUCCESS(rv, rv); |
|
411 rv = win->GetInnerWidth(&aRect.width); |
|
412 NS_ENSURE_SUCCESS(rv, rv); |
|
413 return win->GetInnerHeight(&aRect.height); |
|
414 } |