Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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/. */
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"
20 using namespace mozilla;
21 using namespace mozilla::dom;
23 /* static */ already_AddRefed<nsScreen>
24 nsScreen::Create(nsPIDOMWindow* aWindow)
25 {
26 MOZ_ASSERT(aWindow);
28 if (!aWindow->GetDocShell()) {
29 return nullptr;
30 }
32 nsCOMPtr<nsIScriptGlobalObject> sgo =
33 do_QueryInterface(static_cast<nsPIDOMWindow*>(aWindow));
34 NS_ENSURE_TRUE(sgo, nullptr);
36 nsRefPtr<nsScreen> screen = new nsScreen(aWindow);
38 hal::RegisterScreenConfigurationObserver(screen);
39 hal::ScreenConfiguration config;
40 hal::GetCurrentScreenConfiguration(&config);
41 screen->mOrientation = config.orientation();
43 return screen.forget();
44 }
46 nsScreen::nsScreen(nsPIDOMWindow* aWindow)
47 : DOMEventTargetHelper(aWindow)
48 , mEventListener(nullptr)
49 {
50 }
52 nsScreen::~nsScreen()
53 {
54 MOZ_ASSERT(!mEventListener);
55 hal::UnregisterScreenConfigurationObserver(this);
56 }
59 // QueryInterface implementation for nsScreen
60 NS_INTERFACE_MAP_BEGIN(nsScreen)
61 NS_INTERFACE_MAP_ENTRY(nsIDOMScreen)
62 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
64 NS_IMPL_ADDREF_INHERITED(nsScreen, DOMEventTargetHelper)
65 NS_IMPL_RELEASE_INHERITED(nsScreen, DOMEventTargetHelper)
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;
73 nsDeviceContext* context = GetDeviceContext();
75 if (!context) {
76 aRv.Throw(NS_ERROR_FAILURE);
77 return -1;
78 }
80 uint32_t depth;
81 context->GetDepth(depth);
82 return depth;
83 }
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 }
94 FORWARD_LONG_GETTER(AvailWidth)
95 FORWARD_LONG_GETTER(AvailHeight)
96 FORWARD_LONG_GETTER(Width)
97 FORWARD_LONG_GETTER(Height)
99 FORWARD_LONG_GETTER(Top)
100 FORWARD_LONG_GETTER(Left)
101 FORWARD_LONG_GETTER(AvailTop)
102 FORWARD_LONG_GETTER(AvailLeft)
104 FORWARD_LONG_GETTER(PixelDepth)
105 FORWARD_LONG_GETTER(ColorDepth)
107 nsDeviceContext*
108 nsScreen::GetDeviceContext()
109 {
110 return nsLayoutUtils::GetDeviceContextForScreenInfo(GetOwner());
111 }
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);
119 nsDeviceContext *context = GetDeviceContext();
121 if (!context) {
122 return NS_ERROR_FAILURE;
123 }
125 context->GetRect(aRect);
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);
132 return NS_OK;
133 }
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);
141 nsDeviceContext *context = GetDeviceContext();
143 if (!context) {
144 return NS_ERROR_FAILURE;
145 }
147 context->GetClientRect(aRect);
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);
154 return NS_OK;
155 }
157 void
158 nsScreen::Notify(const hal::ScreenConfiguration& aConfiguration)
159 {
160 ScreenOrientation previousOrientation = mOrientation;
161 mOrientation = aConfiguration.orientation();
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!");
169 if (mOrientation != previousOrientation) {
170 DispatchTrustedEvent(NS_LITERAL_STRING("mozorientationchange"));
171 }
172 }
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 }
200 NS_IMETHODIMP
201 nsScreen::GetSlowMozOrientation(nsAString& aOrientation)
202 {
203 nsString orientation;
204 GetMozOrientation(orientation);
205 aOrientation = orientation;
206 return NS_OK;
207 }
209 nsScreen::LockPermission
210 nsScreen::GetLockOrientationPermission() const
211 {
212 nsCOMPtr<nsPIDOMWindow> owner = GetOwner();
213 if (!owner) {
214 return LOCK_DENIED;
215 }
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 }
223 nsCOMPtr<nsIDocument> doc = owner->GetDoc();
224 if (!doc || doc->Hidden()) {
225 return LOCK_DENIED;
226 }
228 // Apps can always lock the screen orientation.
229 if (doc->NodePrincipal()->GetAppStatus() >=
230 nsIPrincipal::APP_STATUS_INSTALLED) {
231 return LOCK_ALLOWED;
232 }
234 // Other content must be full-screen in order to lock orientation.
235 return doc->MozFullScreen() ? FULLSCREEN_LOCK_ALLOWED : LOCK_DENIED;
236 }
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 }
250 bool
251 nsScreen::MozLockOrientation(const Sequence<nsString>& aOrientations,
252 ErrorResult& aRv)
253 {
254 ScreenOrientation orientation = eScreenOrientation_None;
256 for (uint32_t i = 0; i < aOrientations.Length(); ++i) {
257 const nsString& item = aOrientations[i];
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 }
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 }
297 if (!hal::LockScreenOrientation(orientation)) {
298 return false;
299 }
301 // We are fullscreen and lock has been accepted.
302 if (!mEventListener) {
303 mEventListener = new FullScreenEventListener();
304 }
306 aRv = target->AddSystemEventListener(NS_LITERAL_STRING("mozfullscreenchange"),
307 mEventListener, /* useCapture = */ true);
308 return true;
309 }
310 }
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 }
317 void
318 nsScreen::MozUnlockOrientation()
319 {
320 hal::UnlockScreenOrientation();
321 }
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 }
336 /* virtual */
337 JSObject*
338 nsScreen::WrapObject(JSContext* aCx)
339 {
340 return ScreenBinding::Wrap(aCx, this);
341 }
343 NS_IMPL_ISUPPORTS(nsScreen::FullScreenEventListener, nsIDOMEventListener)
345 NS_IMETHODIMP
346 nsScreen::FullScreenEventListener::HandleEvent(nsIDOMEvent* aEvent)
347 {
348 #ifdef DEBUG
349 nsAutoString eventType;
350 aEvent->GetType(eventType);
352 MOZ_ASSERT(eventType.EqualsLiteral("mozfullscreenchange"));
353 #endif
355 nsCOMPtr<EventTarget> target = aEvent->InternalDOMEvent()->GetCurrentTarget();
356 MOZ_ASSERT(target);
358 nsCOMPtr<nsIDocument> doc = do_QueryInterface(target);
359 MOZ_ASSERT(doc);
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 }
368 target->RemoveSystemEventListener(NS_LITERAL_STRING("mozfullscreenchange"),
369 this, true);
371 hal::UnlockScreenOrientation();
373 return NS_OK;
374 }
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 }
386 nsresult
387 nsScreen::GetDOMWindow(nsIDOMWindow **aResult)
388 {
389 NS_ENSURE_ARG_POINTER(aResult);
390 *aResult = NULL;
392 nsCOMPtr<nsPIDOMWindow> owner = GetOwner();
393 if (!owner)
394 return NS_ERROR_FAILURE;
396 nsCOMPtr<nsIDOMWindow> win = do_QueryInterface(owner);
397 NS_ENSURE_STATE(win);
398 win.swap(*aResult);
400 return NS_OK;
401 }
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 }