1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/base/nsScreen.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,414 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "mozilla/Hal.h" 1.10 +#include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent() 1.11 +#include "mozilla/dom/ScreenBinding.h" 1.12 +#include "nsScreen.h" 1.13 +#include "nsIDocument.h" 1.14 +#include "nsIDocShell.h" 1.15 +#include "nsIDocument.h" 1.16 +#include "nsPresContext.h" 1.17 +#include "nsCOMPtr.h" 1.18 +#include "nsIDocShellTreeItem.h" 1.19 +#include "nsLayoutUtils.h" 1.20 +#include "nsJSUtils.h" 1.21 +#include "nsDeviceContext.h" 1.22 + 1.23 +using namespace mozilla; 1.24 +using namespace mozilla::dom; 1.25 + 1.26 +/* static */ already_AddRefed<nsScreen> 1.27 +nsScreen::Create(nsPIDOMWindow* aWindow) 1.28 +{ 1.29 + MOZ_ASSERT(aWindow); 1.30 + 1.31 + if (!aWindow->GetDocShell()) { 1.32 + return nullptr; 1.33 + } 1.34 + 1.35 + nsCOMPtr<nsIScriptGlobalObject> sgo = 1.36 + do_QueryInterface(static_cast<nsPIDOMWindow*>(aWindow)); 1.37 + NS_ENSURE_TRUE(sgo, nullptr); 1.38 + 1.39 + nsRefPtr<nsScreen> screen = new nsScreen(aWindow); 1.40 + 1.41 + hal::RegisterScreenConfigurationObserver(screen); 1.42 + hal::ScreenConfiguration config; 1.43 + hal::GetCurrentScreenConfiguration(&config); 1.44 + screen->mOrientation = config.orientation(); 1.45 + 1.46 + return screen.forget(); 1.47 +} 1.48 + 1.49 +nsScreen::nsScreen(nsPIDOMWindow* aWindow) 1.50 + : DOMEventTargetHelper(aWindow) 1.51 + , mEventListener(nullptr) 1.52 +{ 1.53 +} 1.54 + 1.55 +nsScreen::~nsScreen() 1.56 +{ 1.57 + MOZ_ASSERT(!mEventListener); 1.58 + hal::UnregisterScreenConfigurationObserver(this); 1.59 +} 1.60 + 1.61 + 1.62 +// QueryInterface implementation for nsScreen 1.63 +NS_INTERFACE_MAP_BEGIN(nsScreen) 1.64 + NS_INTERFACE_MAP_ENTRY(nsIDOMScreen) 1.65 +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) 1.66 + 1.67 +NS_IMPL_ADDREF_INHERITED(nsScreen, DOMEventTargetHelper) 1.68 +NS_IMPL_RELEASE_INHERITED(nsScreen, DOMEventTargetHelper) 1.69 + 1.70 +int32_t 1.71 +nsScreen::GetPixelDepth(ErrorResult& aRv) 1.72 +{ 1.73 + // For non-chrome callers, always return 24 to prevent fingerprinting. 1.74 + if (!IsChrome()) return 24; 1.75 + 1.76 + nsDeviceContext* context = GetDeviceContext(); 1.77 + 1.78 + if (!context) { 1.79 + aRv.Throw(NS_ERROR_FAILURE); 1.80 + return -1; 1.81 + } 1.82 + 1.83 + uint32_t depth; 1.84 + context->GetDepth(depth); 1.85 + return depth; 1.86 +} 1.87 + 1.88 +#define FORWARD_LONG_GETTER(_name) \ 1.89 + NS_IMETHODIMP \ 1.90 + nsScreen::Get ## _name(int32_t* aOut) \ 1.91 + { \ 1.92 + ErrorResult rv; \ 1.93 + *aOut = Get ## _name(rv); \ 1.94 + return rv.ErrorCode(); \ 1.95 + } 1.96 + 1.97 +FORWARD_LONG_GETTER(AvailWidth) 1.98 +FORWARD_LONG_GETTER(AvailHeight) 1.99 +FORWARD_LONG_GETTER(Width) 1.100 +FORWARD_LONG_GETTER(Height) 1.101 + 1.102 +FORWARD_LONG_GETTER(Top) 1.103 +FORWARD_LONG_GETTER(Left) 1.104 +FORWARD_LONG_GETTER(AvailTop) 1.105 +FORWARD_LONG_GETTER(AvailLeft) 1.106 + 1.107 +FORWARD_LONG_GETTER(PixelDepth) 1.108 +FORWARD_LONG_GETTER(ColorDepth) 1.109 + 1.110 +nsDeviceContext* 1.111 +nsScreen::GetDeviceContext() 1.112 +{ 1.113 + return nsLayoutUtils::GetDeviceContextForScreenInfo(GetOwner()); 1.114 +} 1.115 + 1.116 +nsresult 1.117 +nsScreen::GetRect(nsRect& aRect) 1.118 +{ 1.119 + // For non-chrome callers, return window inner rect to prevent fingerprinting. 1.120 + if (!IsChrome()) return GetWindowInnerRect(aRect); 1.121 + 1.122 + nsDeviceContext *context = GetDeviceContext(); 1.123 + 1.124 + if (!context) { 1.125 + return NS_ERROR_FAILURE; 1.126 + } 1.127 + 1.128 + context->GetRect(aRect); 1.129 + 1.130 + aRect.x = nsPresContext::AppUnitsToIntCSSPixels(aRect.x); 1.131 + aRect.y = nsPresContext::AppUnitsToIntCSSPixels(aRect.y); 1.132 + aRect.height = nsPresContext::AppUnitsToIntCSSPixels(aRect.height); 1.133 + aRect.width = nsPresContext::AppUnitsToIntCSSPixels(aRect.width); 1.134 + 1.135 + return NS_OK; 1.136 +} 1.137 + 1.138 +nsresult 1.139 +nsScreen::GetAvailRect(nsRect& aRect) 1.140 +{ 1.141 + // For non-chrome callers, return window inner rect to prevent fingerprinting. 1.142 + if (!IsChrome()) return GetWindowInnerRect(aRect); 1.143 + 1.144 + nsDeviceContext *context = GetDeviceContext(); 1.145 + 1.146 + if (!context) { 1.147 + return NS_ERROR_FAILURE; 1.148 + } 1.149 + 1.150 + context->GetClientRect(aRect); 1.151 + 1.152 + aRect.x = nsPresContext::AppUnitsToIntCSSPixels(aRect.x); 1.153 + aRect.y = nsPresContext::AppUnitsToIntCSSPixels(aRect.y); 1.154 + aRect.height = nsPresContext::AppUnitsToIntCSSPixels(aRect.height); 1.155 + aRect.width = nsPresContext::AppUnitsToIntCSSPixels(aRect.width); 1.156 + 1.157 + return NS_OK; 1.158 +} 1.159 + 1.160 +void 1.161 +nsScreen::Notify(const hal::ScreenConfiguration& aConfiguration) 1.162 +{ 1.163 + ScreenOrientation previousOrientation = mOrientation; 1.164 + mOrientation = aConfiguration.orientation(); 1.165 + 1.166 + NS_ASSERTION(mOrientation == eScreenOrientation_PortraitPrimary || 1.167 + mOrientation == eScreenOrientation_PortraitSecondary || 1.168 + mOrientation == eScreenOrientation_LandscapePrimary || 1.169 + mOrientation == eScreenOrientation_LandscapeSecondary, 1.170 + "Invalid orientation value passed to notify method!"); 1.171 + 1.172 + if (mOrientation != previousOrientation) { 1.173 + DispatchTrustedEvent(NS_LITERAL_STRING("mozorientationchange")); 1.174 + } 1.175 +} 1.176 + 1.177 +void 1.178 +nsScreen::GetMozOrientation(nsString& aOrientation) 1.179 +{ 1.180 + if (!IsChrome()) { 1.181 + aOrientation.AssignLiteral("landscape-primary"); 1.182 + } else { 1.183 + switch (mOrientation) { 1.184 + case eScreenOrientation_PortraitPrimary: 1.185 + aOrientation.AssignLiteral("portrait-primary"); 1.186 + break; 1.187 + case eScreenOrientation_PortraitSecondary: 1.188 + aOrientation.AssignLiteral("portrait-secondary"); 1.189 + break; 1.190 + case eScreenOrientation_LandscapePrimary: 1.191 + aOrientation.AssignLiteral("landscape-primary"); 1.192 + break; 1.193 + case eScreenOrientation_LandscapeSecondary: 1.194 + aOrientation.AssignLiteral("landscape-secondary"); 1.195 + break; 1.196 + case eScreenOrientation_None: 1.197 + default: 1.198 + MOZ_CRASH("Unacceptable mOrientation value"); 1.199 + } 1.200 + } 1.201 +} 1.202 + 1.203 +NS_IMETHODIMP 1.204 +nsScreen::GetSlowMozOrientation(nsAString& aOrientation) 1.205 +{ 1.206 + nsString orientation; 1.207 + GetMozOrientation(orientation); 1.208 + aOrientation = orientation; 1.209 + return NS_OK; 1.210 +} 1.211 + 1.212 +nsScreen::LockPermission 1.213 +nsScreen::GetLockOrientationPermission() const 1.214 +{ 1.215 + nsCOMPtr<nsPIDOMWindow> owner = GetOwner(); 1.216 + if (!owner) { 1.217 + return LOCK_DENIED; 1.218 + } 1.219 + 1.220 + // Chrome can always lock the screen orientation. 1.221 + nsIDocShell* docShell = owner->GetDocShell(); 1.222 + if (docShell && docShell->ItemType() == nsIDocShellTreeItem::typeChrome) { 1.223 + return LOCK_ALLOWED; 1.224 + } 1.225 + 1.226 + nsCOMPtr<nsIDocument> doc = owner->GetDoc(); 1.227 + if (!doc || doc->Hidden()) { 1.228 + return LOCK_DENIED; 1.229 + } 1.230 + 1.231 + // Apps can always lock the screen orientation. 1.232 + if (doc->NodePrincipal()->GetAppStatus() >= 1.233 + nsIPrincipal::APP_STATUS_INSTALLED) { 1.234 + return LOCK_ALLOWED; 1.235 + } 1.236 + 1.237 + // Other content must be full-screen in order to lock orientation. 1.238 + return doc->MozFullScreen() ? FULLSCREEN_LOCK_ALLOWED : LOCK_DENIED; 1.239 +} 1.240 + 1.241 +bool 1.242 +nsScreen::MozLockOrientation(const nsAString& aOrientation, ErrorResult& aRv) 1.243 +{ 1.244 + nsString orientation(aOrientation); 1.245 + Sequence<nsString> orientations; 1.246 + if (!orientations.AppendElement(orientation)) { 1.247 + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); 1.248 + return false; 1.249 + } 1.250 + return MozLockOrientation(orientations, aRv); 1.251 +} 1.252 + 1.253 +bool 1.254 +nsScreen::MozLockOrientation(const Sequence<nsString>& aOrientations, 1.255 + ErrorResult& aRv) 1.256 +{ 1.257 + ScreenOrientation orientation = eScreenOrientation_None; 1.258 + 1.259 + for (uint32_t i = 0; i < aOrientations.Length(); ++i) { 1.260 + const nsString& item = aOrientations[i]; 1.261 + 1.262 + if (item.EqualsLiteral("portrait")) { 1.263 + orientation |= eScreenOrientation_PortraitPrimary | 1.264 + eScreenOrientation_PortraitSecondary; 1.265 + } else if (item.EqualsLiteral("portrait-primary")) { 1.266 + orientation |= eScreenOrientation_PortraitPrimary; 1.267 + } else if (item.EqualsLiteral("portrait-secondary")) { 1.268 + orientation |= eScreenOrientation_PortraitSecondary; 1.269 + } else if (item.EqualsLiteral("landscape")) { 1.270 + orientation |= eScreenOrientation_LandscapePrimary | 1.271 + eScreenOrientation_LandscapeSecondary; 1.272 + } else if (item.EqualsLiteral("landscape-primary")) { 1.273 + orientation |= eScreenOrientation_LandscapePrimary; 1.274 + } else if (item.EqualsLiteral("landscape-secondary")) { 1.275 + orientation |= eScreenOrientation_LandscapeSecondary; 1.276 + } else if (item.EqualsLiteral("default")) { 1.277 + orientation |= eScreenOrientation_Default; 1.278 + } else { 1.279 + // If we don't recognize the token, we should just return 'false' 1.280 + // without throwing. 1.281 + return false; 1.282 + } 1.283 + } 1.284 + 1.285 + switch (GetLockOrientationPermission()) { 1.286 + case LOCK_DENIED: 1.287 + return false; 1.288 + case LOCK_ALLOWED: 1.289 + return hal::LockScreenOrientation(orientation); 1.290 + case FULLSCREEN_LOCK_ALLOWED: { 1.291 + // We need to register a listener so we learn when we leave full-screen 1.292 + // and when we will have to unlock the screen. 1.293 + // This needs to be done before LockScreenOrientation call to make sure 1.294 + // the locking can be unlocked. 1.295 + nsCOMPtr<EventTarget> target = do_QueryInterface(GetOwner()->GetDoc()); 1.296 + if (!target) { 1.297 + return false; 1.298 + } 1.299 + 1.300 + if (!hal::LockScreenOrientation(orientation)) { 1.301 + return false; 1.302 + } 1.303 + 1.304 + // We are fullscreen and lock has been accepted. 1.305 + if (!mEventListener) { 1.306 + mEventListener = new FullScreenEventListener(); 1.307 + } 1.308 + 1.309 + aRv = target->AddSystemEventListener(NS_LITERAL_STRING("mozfullscreenchange"), 1.310 + mEventListener, /* useCapture = */ true); 1.311 + return true; 1.312 + } 1.313 + } 1.314 + 1.315 + // This is only for compilers that don't understand that the previous switch 1.316 + // will always return. 1.317 + MOZ_CRASH("unexpected lock orientation permission value"); 1.318 +} 1.319 + 1.320 +void 1.321 +nsScreen::MozUnlockOrientation() 1.322 +{ 1.323 + hal::UnlockScreenOrientation(); 1.324 +} 1.325 + 1.326 +bool 1.327 +nsScreen::IsDeviceSizePageSize() 1.328 +{ 1.329 + nsPIDOMWindow* owner = GetOwner(); 1.330 + if (owner) { 1.331 + nsIDocShell* docShell = owner->GetDocShell(); 1.332 + if (docShell) { 1.333 + return docShell->GetDeviceSizeIsPageSize(); 1.334 + } 1.335 + } 1.336 + return false; 1.337 +} 1.338 + 1.339 +/* virtual */ 1.340 +JSObject* 1.341 +nsScreen::WrapObject(JSContext* aCx) 1.342 +{ 1.343 + return ScreenBinding::Wrap(aCx, this); 1.344 +} 1.345 + 1.346 +NS_IMPL_ISUPPORTS(nsScreen::FullScreenEventListener, nsIDOMEventListener) 1.347 + 1.348 +NS_IMETHODIMP 1.349 +nsScreen::FullScreenEventListener::HandleEvent(nsIDOMEvent* aEvent) 1.350 +{ 1.351 +#ifdef DEBUG 1.352 + nsAutoString eventType; 1.353 + aEvent->GetType(eventType); 1.354 + 1.355 + MOZ_ASSERT(eventType.EqualsLiteral("mozfullscreenchange")); 1.356 +#endif 1.357 + 1.358 + nsCOMPtr<EventTarget> target = aEvent->InternalDOMEvent()->GetCurrentTarget(); 1.359 + MOZ_ASSERT(target); 1.360 + 1.361 + nsCOMPtr<nsIDocument> doc = do_QueryInterface(target); 1.362 + MOZ_ASSERT(doc); 1.363 + 1.364 + // We have to make sure that the event we got is the event sent when 1.365 + // fullscreen is disabled because we could get one when fullscreen 1.366 + // got enabled if the lock call is done at the same moment. 1.367 + if (doc->MozFullScreen()) { 1.368 + return NS_OK; 1.369 + } 1.370 + 1.371 + target->RemoveSystemEventListener(NS_LITERAL_STRING("mozfullscreenchange"), 1.372 + this, true); 1.373 + 1.374 + hal::UnlockScreenOrientation(); 1.375 + 1.376 + return NS_OK; 1.377 +} 1.378 + 1.379 +bool 1.380 +nsScreen::IsChrome() 1.381 +{ 1.382 + nsCOMPtr<nsPIDOMWindow> owner = GetOwner(); 1.383 + if (owner && owner->GetDocShell()) { 1.384 + return owner->GetDocShell()->ItemType() == nsIDocShellTreeItem::typeChrome; 1.385 + } 1.386 + return false; 1.387 +} 1.388 + 1.389 +nsresult 1.390 +nsScreen::GetDOMWindow(nsIDOMWindow **aResult) 1.391 +{ 1.392 + NS_ENSURE_ARG_POINTER(aResult); 1.393 + *aResult = NULL; 1.394 + 1.395 + nsCOMPtr<nsPIDOMWindow> owner = GetOwner(); 1.396 + if (!owner) 1.397 + return NS_ERROR_FAILURE; 1.398 + 1.399 + nsCOMPtr<nsIDOMWindow> win = do_QueryInterface(owner); 1.400 + NS_ENSURE_STATE(win); 1.401 + win.swap(*aResult); 1.402 + 1.403 + return NS_OK; 1.404 +} 1.405 + 1.406 +nsresult 1.407 +nsScreen::GetWindowInnerRect(nsRect& aRect) 1.408 +{ 1.409 + aRect.x = 0; 1.410 + aRect.y = 0; 1.411 + nsCOMPtr<nsIDOMWindow> win; 1.412 + nsresult rv = GetDOMWindow(getter_AddRefs(win)); 1.413 + NS_ENSURE_SUCCESS(rv, rv); 1.414 + rv = win->GetInnerWidth(&aRect.width); 1.415 + NS_ENSURE_SUCCESS(rv, rv); 1.416 + return win->GetInnerHeight(&aRect.height); 1.417 +}