michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/Hal.h" michael@0: #include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent() michael@0: #include "mozilla/dom/ScreenBinding.h" michael@0: #include "nsScreen.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIDocShell.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsIDocShellTreeItem.h" michael@0: #include "nsLayoutUtils.h" michael@0: #include "nsJSUtils.h" michael@0: #include "nsDeviceContext.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: /* static */ already_AddRefed michael@0: nsScreen::Create(nsPIDOMWindow* aWindow) michael@0: { michael@0: MOZ_ASSERT(aWindow); michael@0: michael@0: if (!aWindow->GetDocShell()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCOMPtr sgo = michael@0: do_QueryInterface(static_cast(aWindow)); michael@0: NS_ENSURE_TRUE(sgo, nullptr); michael@0: michael@0: nsRefPtr screen = new nsScreen(aWindow); michael@0: michael@0: hal::RegisterScreenConfigurationObserver(screen); michael@0: hal::ScreenConfiguration config; michael@0: hal::GetCurrentScreenConfiguration(&config); michael@0: screen->mOrientation = config.orientation(); michael@0: michael@0: return screen.forget(); michael@0: } michael@0: michael@0: nsScreen::nsScreen(nsPIDOMWindow* aWindow) michael@0: : DOMEventTargetHelper(aWindow) michael@0: , mEventListener(nullptr) michael@0: { michael@0: } michael@0: michael@0: nsScreen::~nsScreen() michael@0: { michael@0: MOZ_ASSERT(!mEventListener); michael@0: hal::UnregisterScreenConfigurationObserver(this); michael@0: } michael@0: michael@0: michael@0: // QueryInterface implementation for nsScreen michael@0: NS_INTERFACE_MAP_BEGIN(nsScreen) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDOMScreen) michael@0: NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(nsScreen, DOMEventTargetHelper) michael@0: NS_IMPL_RELEASE_INHERITED(nsScreen, DOMEventTargetHelper) michael@0: michael@0: int32_t michael@0: nsScreen::GetPixelDepth(ErrorResult& aRv) michael@0: { michael@0: // For non-chrome callers, always return 24 to prevent fingerprinting. michael@0: if (!IsChrome()) return 24; michael@0: michael@0: nsDeviceContext* context = GetDeviceContext(); michael@0: michael@0: if (!context) { michael@0: aRv.Throw(NS_ERROR_FAILURE); michael@0: return -1; michael@0: } michael@0: michael@0: uint32_t depth; michael@0: context->GetDepth(depth); michael@0: return depth; michael@0: } michael@0: michael@0: #define FORWARD_LONG_GETTER(_name) \ michael@0: NS_IMETHODIMP \ michael@0: nsScreen::Get ## _name(int32_t* aOut) \ michael@0: { \ michael@0: ErrorResult rv; \ michael@0: *aOut = Get ## _name(rv); \ michael@0: return rv.ErrorCode(); \ michael@0: } michael@0: michael@0: FORWARD_LONG_GETTER(AvailWidth) michael@0: FORWARD_LONG_GETTER(AvailHeight) michael@0: FORWARD_LONG_GETTER(Width) michael@0: FORWARD_LONG_GETTER(Height) michael@0: michael@0: FORWARD_LONG_GETTER(Top) michael@0: FORWARD_LONG_GETTER(Left) michael@0: FORWARD_LONG_GETTER(AvailTop) michael@0: FORWARD_LONG_GETTER(AvailLeft) michael@0: michael@0: FORWARD_LONG_GETTER(PixelDepth) michael@0: FORWARD_LONG_GETTER(ColorDepth) michael@0: michael@0: nsDeviceContext* michael@0: nsScreen::GetDeviceContext() michael@0: { michael@0: return nsLayoutUtils::GetDeviceContextForScreenInfo(GetOwner()); michael@0: } michael@0: michael@0: nsresult michael@0: nsScreen::GetRect(nsRect& aRect) michael@0: { michael@0: // For non-chrome callers, return window inner rect to prevent fingerprinting. michael@0: if (!IsChrome()) return GetWindowInnerRect(aRect); michael@0: michael@0: nsDeviceContext *context = GetDeviceContext(); michael@0: michael@0: if (!context) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: context->GetRect(aRect); michael@0: michael@0: aRect.x = nsPresContext::AppUnitsToIntCSSPixels(aRect.x); michael@0: aRect.y = nsPresContext::AppUnitsToIntCSSPixels(aRect.y); michael@0: aRect.height = nsPresContext::AppUnitsToIntCSSPixels(aRect.height); michael@0: aRect.width = nsPresContext::AppUnitsToIntCSSPixels(aRect.width); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsScreen::GetAvailRect(nsRect& aRect) michael@0: { michael@0: // For non-chrome callers, return window inner rect to prevent fingerprinting. michael@0: if (!IsChrome()) return GetWindowInnerRect(aRect); michael@0: michael@0: nsDeviceContext *context = GetDeviceContext(); michael@0: michael@0: if (!context) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: context->GetClientRect(aRect); michael@0: michael@0: aRect.x = nsPresContext::AppUnitsToIntCSSPixels(aRect.x); michael@0: aRect.y = nsPresContext::AppUnitsToIntCSSPixels(aRect.y); michael@0: aRect.height = nsPresContext::AppUnitsToIntCSSPixels(aRect.height); michael@0: aRect.width = nsPresContext::AppUnitsToIntCSSPixels(aRect.width); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsScreen::Notify(const hal::ScreenConfiguration& aConfiguration) michael@0: { michael@0: ScreenOrientation previousOrientation = mOrientation; michael@0: mOrientation = aConfiguration.orientation(); michael@0: michael@0: NS_ASSERTION(mOrientation == eScreenOrientation_PortraitPrimary || michael@0: mOrientation == eScreenOrientation_PortraitSecondary || michael@0: mOrientation == eScreenOrientation_LandscapePrimary || michael@0: mOrientation == eScreenOrientation_LandscapeSecondary, michael@0: "Invalid orientation value passed to notify method!"); michael@0: michael@0: if (mOrientation != previousOrientation) { michael@0: DispatchTrustedEvent(NS_LITERAL_STRING("mozorientationchange")); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsScreen::GetMozOrientation(nsString& aOrientation) michael@0: { michael@0: if (!IsChrome()) { michael@0: aOrientation.AssignLiteral("landscape-primary"); michael@0: } else { michael@0: switch (mOrientation) { michael@0: case eScreenOrientation_PortraitPrimary: michael@0: aOrientation.AssignLiteral("portrait-primary"); michael@0: break; michael@0: case eScreenOrientation_PortraitSecondary: michael@0: aOrientation.AssignLiteral("portrait-secondary"); michael@0: break; michael@0: case eScreenOrientation_LandscapePrimary: michael@0: aOrientation.AssignLiteral("landscape-primary"); michael@0: break; michael@0: case eScreenOrientation_LandscapeSecondary: michael@0: aOrientation.AssignLiteral("landscape-secondary"); michael@0: break; michael@0: case eScreenOrientation_None: michael@0: default: michael@0: MOZ_CRASH("Unacceptable mOrientation value"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsScreen::GetSlowMozOrientation(nsAString& aOrientation) michael@0: { michael@0: nsString orientation; michael@0: GetMozOrientation(orientation); michael@0: aOrientation = orientation; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsScreen::LockPermission michael@0: nsScreen::GetLockOrientationPermission() const michael@0: { michael@0: nsCOMPtr owner = GetOwner(); michael@0: if (!owner) { michael@0: return LOCK_DENIED; michael@0: } michael@0: michael@0: // Chrome can always lock the screen orientation. michael@0: nsIDocShell* docShell = owner->GetDocShell(); michael@0: if (docShell && docShell->ItemType() == nsIDocShellTreeItem::typeChrome) { michael@0: return LOCK_ALLOWED; michael@0: } michael@0: michael@0: nsCOMPtr doc = owner->GetDoc(); michael@0: if (!doc || doc->Hidden()) { michael@0: return LOCK_DENIED; michael@0: } michael@0: michael@0: // Apps can always lock the screen orientation. michael@0: if (doc->NodePrincipal()->GetAppStatus() >= michael@0: nsIPrincipal::APP_STATUS_INSTALLED) { michael@0: return LOCK_ALLOWED; michael@0: } michael@0: michael@0: // Other content must be full-screen in order to lock orientation. michael@0: return doc->MozFullScreen() ? FULLSCREEN_LOCK_ALLOWED : LOCK_DENIED; michael@0: } michael@0: michael@0: bool michael@0: nsScreen::MozLockOrientation(const nsAString& aOrientation, ErrorResult& aRv) michael@0: { michael@0: nsString orientation(aOrientation); michael@0: Sequence orientations; michael@0: if (!orientations.AppendElement(orientation)) { michael@0: aRv.Throw(NS_ERROR_OUT_OF_MEMORY); michael@0: return false; michael@0: } michael@0: return MozLockOrientation(orientations, aRv); michael@0: } michael@0: michael@0: bool michael@0: nsScreen::MozLockOrientation(const Sequence& aOrientations, michael@0: ErrorResult& aRv) michael@0: { michael@0: ScreenOrientation orientation = eScreenOrientation_None; michael@0: michael@0: for (uint32_t i = 0; i < aOrientations.Length(); ++i) { michael@0: const nsString& item = aOrientations[i]; michael@0: michael@0: if (item.EqualsLiteral("portrait")) { michael@0: orientation |= eScreenOrientation_PortraitPrimary | michael@0: eScreenOrientation_PortraitSecondary; michael@0: } else if (item.EqualsLiteral("portrait-primary")) { michael@0: orientation |= eScreenOrientation_PortraitPrimary; michael@0: } else if (item.EqualsLiteral("portrait-secondary")) { michael@0: orientation |= eScreenOrientation_PortraitSecondary; michael@0: } else if (item.EqualsLiteral("landscape")) { michael@0: orientation |= eScreenOrientation_LandscapePrimary | michael@0: eScreenOrientation_LandscapeSecondary; michael@0: } else if (item.EqualsLiteral("landscape-primary")) { michael@0: orientation |= eScreenOrientation_LandscapePrimary; michael@0: } else if (item.EqualsLiteral("landscape-secondary")) { michael@0: orientation |= eScreenOrientation_LandscapeSecondary; michael@0: } else if (item.EqualsLiteral("default")) { michael@0: orientation |= eScreenOrientation_Default; michael@0: } else { michael@0: // If we don't recognize the token, we should just return 'false' michael@0: // without throwing. michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: switch (GetLockOrientationPermission()) { michael@0: case LOCK_DENIED: michael@0: return false; michael@0: case LOCK_ALLOWED: michael@0: return hal::LockScreenOrientation(orientation); michael@0: case FULLSCREEN_LOCK_ALLOWED: { michael@0: // We need to register a listener so we learn when we leave full-screen michael@0: // and when we will have to unlock the screen. michael@0: // This needs to be done before LockScreenOrientation call to make sure michael@0: // the locking can be unlocked. michael@0: nsCOMPtr target = do_QueryInterface(GetOwner()->GetDoc()); michael@0: if (!target) { michael@0: return false; michael@0: } michael@0: michael@0: if (!hal::LockScreenOrientation(orientation)) { michael@0: return false; michael@0: } michael@0: michael@0: // We are fullscreen and lock has been accepted. michael@0: if (!mEventListener) { michael@0: mEventListener = new FullScreenEventListener(); michael@0: } michael@0: michael@0: aRv = target->AddSystemEventListener(NS_LITERAL_STRING("mozfullscreenchange"), michael@0: mEventListener, /* useCapture = */ true); michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: // This is only for compilers that don't understand that the previous switch michael@0: // will always return. michael@0: MOZ_CRASH("unexpected lock orientation permission value"); michael@0: } michael@0: michael@0: void michael@0: nsScreen::MozUnlockOrientation() michael@0: { michael@0: hal::UnlockScreenOrientation(); michael@0: } michael@0: michael@0: bool michael@0: nsScreen::IsDeviceSizePageSize() michael@0: { michael@0: nsPIDOMWindow* owner = GetOwner(); michael@0: if (owner) { michael@0: nsIDocShell* docShell = owner->GetDocShell(); michael@0: if (docShell) { michael@0: return docShell->GetDeviceSizeIsPageSize(); michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: /* virtual */ michael@0: JSObject* michael@0: nsScreen::WrapObject(JSContext* aCx) michael@0: { michael@0: return ScreenBinding::Wrap(aCx, this); michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsScreen::FullScreenEventListener, nsIDOMEventListener) michael@0: michael@0: NS_IMETHODIMP michael@0: nsScreen::FullScreenEventListener::HandleEvent(nsIDOMEvent* aEvent) michael@0: { michael@0: #ifdef DEBUG michael@0: nsAutoString eventType; michael@0: aEvent->GetType(eventType); michael@0: michael@0: MOZ_ASSERT(eventType.EqualsLiteral("mozfullscreenchange")); michael@0: #endif michael@0: michael@0: nsCOMPtr target = aEvent->InternalDOMEvent()->GetCurrentTarget(); michael@0: MOZ_ASSERT(target); michael@0: michael@0: nsCOMPtr doc = do_QueryInterface(target); michael@0: MOZ_ASSERT(doc); michael@0: michael@0: // We have to make sure that the event we got is the event sent when michael@0: // fullscreen is disabled because we could get one when fullscreen michael@0: // got enabled if the lock call is done at the same moment. michael@0: if (doc->MozFullScreen()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: target->RemoveSystemEventListener(NS_LITERAL_STRING("mozfullscreenchange"), michael@0: this, true); michael@0: michael@0: hal::UnlockScreenOrientation(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: nsScreen::IsChrome() michael@0: { michael@0: nsCOMPtr owner = GetOwner(); michael@0: if (owner && owner->GetDocShell()) { michael@0: return owner->GetDocShell()->ItemType() == nsIDocShellTreeItem::typeChrome; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: nsresult michael@0: nsScreen::GetDOMWindow(nsIDOMWindow **aResult) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aResult); michael@0: *aResult = NULL; michael@0: michael@0: nsCOMPtr owner = GetOwner(); michael@0: if (!owner) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr win = do_QueryInterface(owner); michael@0: NS_ENSURE_STATE(win); michael@0: win.swap(*aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsScreen::GetWindowInnerRect(nsRect& aRect) michael@0: { michael@0: aRect.x = 0; michael@0: aRect.y = 0; michael@0: nsCOMPtr win; michael@0: nsresult rv = GetDOMWindow(getter_AddRefs(win)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = win->GetInnerWidth(&aRect.width); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: return win->GetInnerHeight(&aRect.height); michael@0: }