1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ScreenOrientation.h"
8 #include "nsIDocShell.h"
9 #include "mozilla/dom/Document.h"
10 #include "nsGlobalWindowInner.h"
11 #include "nsSandboxFlags.h"
14 #include "mozilla/DOMEventTargetHelper.h"
15 #include "mozilla/Hal.h"
16 #include "mozilla/Preferences.h"
18 #include "mozilla/dom/ContentChild.h"
19 #include "mozilla/dom/Event.h"
20 #include "mozilla/dom/Promise.h"
21 #include "mozilla/StaticPrefs_browser.h"
22 #include "nsContentUtils.h"
24 using namespace mozilla
;
25 using namespace mozilla::dom
;
27 NS_IMPL_CYCLE_COLLECTION_INHERITED(ScreenOrientation
, DOMEventTargetHelper
,
30 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ScreenOrientation
)
31 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
33 NS_IMPL_ADDREF_INHERITED(ScreenOrientation
, DOMEventTargetHelper
)
34 NS_IMPL_RELEASE_INHERITED(ScreenOrientation
, DOMEventTargetHelper
)
36 static OrientationType
InternalOrientationToType(
37 hal::ScreenOrientation aOrientation
) {
38 switch (aOrientation
) {
39 case hal::ScreenOrientation::PortraitPrimary
:
40 return OrientationType::Portrait_primary
;
41 case hal::ScreenOrientation::PortraitSecondary
:
42 return OrientationType::Portrait_secondary
;
43 case hal::ScreenOrientation::LandscapePrimary
:
44 return OrientationType::Landscape_primary
;
45 case hal::ScreenOrientation::LandscapeSecondary
:
46 return OrientationType::Landscape_secondary
;
48 MOZ_CRASH("Bad aOrientation value");
52 static hal::ScreenOrientation
OrientationTypeToInternal(
53 OrientationType aOrientation
) {
54 switch (aOrientation
) {
55 case OrientationType::Portrait_primary
:
56 return hal::ScreenOrientation::PortraitPrimary
;
57 case OrientationType::Portrait_secondary
:
58 return hal::ScreenOrientation::PortraitSecondary
;
59 case OrientationType::Landscape_primary
:
60 return hal::ScreenOrientation::LandscapePrimary
;
61 case OrientationType::Landscape_secondary
:
62 return hal::ScreenOrientation::LandscapeSecondary
;
64 MOZ_CRASH("Bad aOrientation value");
68 ScreenOrientation::ScreenOrientation(nsPIDOMWindowInner
* aWindow
,
70 : DOMEventTargetHelper(aWindow
), mScreen(aScreen
) {
74 mAngle
= aScreen
->GetOrientationAngle();
75 mType
= InternalOrientationToType(aScreen
->GetOrientationType());
77 Document
* doc
= GetResponsibleDocument();
78 BrowsingContext
* bc
= doc
? doc
->GetBrowsingContext() : nullptr;
79 if (bc
&& !bc
->IsDiscarded() && !bc
->InRDMPane()) {
80 MOZ_ALWAYS_SUCCEEDS(bc
->SetCurrentOrientation(mType
, mAngle
));
84 ScreenOrientation::~ScreenOrientation() {
85 if (mTriedToLockDeviceOrientation
) {
86 UnlockDeviceOrientation();
88 CleanupFullscreenListener();
91 MOZ_ASSERT(!mFullscreenListener
);
94 class ScreenOrientation::FullscreenEventListener final
95 : public nsIDOMEventListener
{
96 ~FullscreenEventListener() = default;
99 FullscreenEventListener() = default;
102 NS_DECL_NSIDOMEVENTLISTENER
105 class ScreenOrientation::VisibleEventListener final
106 : public nsIDOMEventListener
{
107 ~VisibleEventListener() = default;
110 VisibleEventListener() = default;
113 NS_DECL_NSIDOMEVENTLISTENER
116 class ScreenOrientation::LockOrientationTask final
: public nsIRunnable
{
117 ~LockOrientationTask();
123 LockOrientationTask(ScreenOrientation
* aScreenOrientation
, Promise
* aPromise
,
124 hal::ScreenOrientation aOrientationLock
,
125 Document
* aDocument
, bool aIsFullscreen
);
128 bool OrientationLockContains(OrientationType aOrientationType
);
130 RefPtr
<ScreenOrientation
> mScreenOrientation
;
131 RefPtr
<Promise
> mPromise
;
132 hal::ScreenOrientation mOrientationLock
;
133 WeakPtr
<Document
> mDocument
;
137 NS_IMPL_ISUPPORTS(ScreenOrientation::LockOrientationTask
, nsIRunnable
)
139 ScreenOrientation::LockOrientationTask::LockOrientationTask(
140 ScreenOrientation
* aScreenOrientation
, Promise
* aPromise
,
141 hal::ScreenOrientation aOrientationLock
, Document
* aDocument
,
143 : mScreenOrientation(aScreenOrientation
),
145 mOrientationLock(aOrientationLock
),
146 mDocument(aDocument
),
147 mIsFullscreen(aIsFullscreen
) {
148 MOZ_ASSERT(aScreenOrientation
);
149 MOZ_ASSERT(aPromise
);
150 MOZ_ASSERT(aDocument
);
153 ScreenOrientation::LockOrientationTask::~LockOrientationTask() = default;
155 bool ScreenOrientation::LockOrientationTask::OrientationLockContains(
156 OrientationType aOrientationType
) {
157 return bool(mOrientationLock
& OrientationTypeToInternal(aOrientationType
));
161 ScreenOrientation::LockOrientationTask::Run() {
167 mPromise
->MaybeReject(NS_ERROR_DOM_ABORT_ERR
);
171 nsCOMPtr
<nsPIDOMWindowInner
> owner
= mScreenOrientation
->GetOwner();
172 if (!owner
|| !owner
->IsFullyActive()) {
173 mPromise
->MaybeRejectWithAbortError("The document is not fully active.");
177 // Step to lock the orientation as defined in the spec.
178 if (mDocument
->GetOrientationPendingPromise() != mPromise
) {
179 // The document's pending promise is not associated with this task
180 // to lock orientation. There has since been another request to
181 // lock orientation, thus we don't need to do anything. Old promise
182 // should be been rejected.
186 if (mDocument
->Hidden()) {
187 // Active orientation lock is not the document's orientation lock.
188 mPromise
->MaybeResolveWithUndefined();
189 mDocument
->ClearOrientationPendingPromise();
193 if (mOrientationLock
== hal::ScreenOrientation::None
) {
194 mScreenOrientation
->UnlockDeviceOrientation();
195 mPromise
->MaybeResolveWithUndefined();
196 mDocument
->ClearOrientationPendingPromise();
200 BrowsingContext
* bc
= mDocument
->GetBrowsingContext();
202 mPromise
->MaybeResolveWithUndefined();
203 mDocument
->ClearOrientationPendingPromise();
207 OrientationType previousOrientationType
= bc
->GetCurrentOrientationType();
208 mScreenOrientation
->LockDeviceOrientation(mOrientationLock
, mIsFullscreen
)
210 GetCurrentSerialEventTarget(), __func__
,
211 [self
= RefPtr
{this}, previousOrientationType
](
212 const GenericNonExclusivePromise::ResolveOrRejectValue
& aValue
) {
213 if (self
->mPromise
->State() != Promise::PromiseState::Pending
) {
214 // mPromise is already resolved or rejected by
215 // DispatchChangeEventAndResolvePromise() or
216 // AbortInProcessOrientationPromises().
220 if (!self
->mDocument
) {
221 self
->mPromise
->MaybeReject(NS_ERROR_DOM_ABORT_ERR
);
225 if (self
->mDocument
->GetOrientationPendingPromise() !=
227 // mPromise is old promise now and document has new promise by
228 // later `orientation.lock` call. Old promise is already rejected
229 // by AbortInProcessOrientationPromises()
232 if (aValue
.IsResolve()) {
233 // LockDeviceOrientation won't change orientation, so change
234 // event isn't fired.
235 if (BrowsingContext
* bc
= self
->mDocument
->GetBrowsingContext()) {
236 OrientationType currentOrientationType
=
237 bc
->GetCurrentOrientationType();
238 if ((previousOrientationType
== currentOrientationType
&&
239 self
->OrientationLockContains(currentOrientationType
)) ||
240 (self
->mOrientationLock
==
241 hal::ScreenOrientation::Default
&&
242 bc
->GetCurrentOrientationAngle() == 0)) {
243 // Orientation lock will not cause an orientation change, so
244 // we need to manually resolve the promise here.
245 self
->mPromise
->MaybeResolveWithUndefined();
246 self
->mDocument
->ClearOrientationPendingPromise();
251 self
->mPromise
->MaybeReject(aValue
.RejectValue());
252 self
->mDocument
->ClearOrientationPendingPromise();
258 already_AddRefed
<Promise
> ScreenOrientation::Lock(
259 OrientationLockType aOrientation
, ErrorResult
& aRv
) {
260 hal::ScreenOrientation orientation
= hal::ScreenOrientation::None
;
262 switch (aOrientation
) {
263 case OrientationLockType::Any
:
264 orientation
= hal::ScreenOrientation::PortraitPrimary
|
265 hal::ScreenOrientation::PortraitSecondary
|
266 hal::ScreenOrientation::LandscapePrimary
|
267 hal::ScreenOrientation::LandscapeSecondary
;
269 case OrientationLockType::Natural
:
270 orientation
|= hal::ScreenOrientation::Default
;
272 case OrientationLockType::Landscape
:
273 orientation
= hal::ScreenOrientation::LandscapePrimary
|
274 hal::ScreenOrientation::LandscapeSecondary
;
276 case OrientationLockType::Portrait
:
277 orientation
= hal::ScreenOrientation::PortraitPrimary
|
278 hal::ScreenOrientation::PortraitSecondary
;
280 case OrientationLockType::Portrait_primary
:
281 orientation
= hal::ScreenOrientation::PortraitPrimary
;
283 case OrientationLockType::Portrait_secondary
:
284 orientation
= hal::ScreenOrientation::PortraitSecondary
;
286 case OrientationLockType::Landscape_primary
:
287 orientation
= hal::ScreenOrientation::LandscapePrimary
;
289 case OrientationLockType::Landscape_secondary
:
290 orientation
= hal::ScreenOrientation::LandscapeSecondary
;
293 NS_WARNING("Unexpected orientation type");
294 aRv
.Throw(NS_ERROR_UNEXPECTED
);
298 return LockInternal(orientation
, aRv
);
301 // Wait for document entered fullscreen.
302 class FullscreenWaitListener final
: public nsIDOMEventListener
{
304 ~FullscreenWaitListener() = default;
307 FullscreenWaitListener() = default;
311 // When we have pending fullscreen request, we will wait for the completion or
313 RefPtr
<GenericPromise
> Promise(Document
* aDocument
) {
314 if (aDocument
->Fullscreen()) {
315 return GenericPromise::CreateAndResolve(true, __func__
);
318 if (NS_FAILED(InstallEventListener(aDocument
))) {
319 return GenericPromise::CreateAndReject(NS_ERROR_FAILURE
, __func__
);
322 MOZ_ASSERT(aDocument
->HasPendingFullscreenRequests());
323 return mHolder
.Ensure(__func__
);
326 NS_IMETHODIMP
HandleEvent(Event
* aEvent
) override
{
327 nsAutoString eventType
;
328 aEvent
->GetType(eventType
);
330 if (eventType
.EqualsLiteral("pagehide")) {
331 mHolder
.Reject(NS_ERROR_FAILURE
, __func__
);
332 CleanupEventListener();
336 MOZ_ASSERT(eventType
.EqualsLiteral("fullscreenchange") ||
337 eventType
.EqualsLiteral("fullscreenerror") ||
338 eventType
.EqualsLiteral("pagehide"));
339 if (mDocument
->Fullscreen()) {
340 mHolder
.Resolve(true, __func__
);
342 mHolder
.Reject(NS_ERROR_FAILURE
, __func__
);
344 CleanupEventListener();
349 nsresult
InstallEventListener(Document
* aDoc
) {
355 nsresult rv
= aDoc
->AddSystemEventListener(u
"fullscreenchange"_ns
, this,
356 /* aUseCapture = */ true);
358 CleanupEventListener();
362 rv
= aDoc
->AddSystemEventListener(u
"fullscreenerror"_ns
, this,
363 /* aUseCapture = */ true);
365 CleanupEventListener();
369 nsPIDOMWindowOuter
* window
= aDoc
->GetWindow();
370 nsCOMPtr
<EventTarget
> target
= do_QueryInterface(window
);
372 CleanupEventListener();
373 return NS_ERROR_FAILURE
;
375 rv
= target
->AddSystemEventListener(u
"pagehide"_ns
, this,
376 /* aUseCapture = */ true,
377 /* aWantsUntrusted = */ false);
379 CleanupEventListener();
386 void CleanupEventListener() {
390 RefPtr
<FullscreenWaitListener
> kungFuDeathGrip(this);
391 mDocument
->RemoveSystemEventListener(u
"fullscreenchange"_ns
, this, true);
392 mDocument
->RemoveSystemEventListener(u
"fullscreenerror"_ns
, this, true);
393 nsPIDOMWindowOuter
* window
= mDocument
->GetWindow();
394 nsCOMPtr
<EventTarget
> target
= do_QueryInterface(window
);
396 target
->RemoveSystemEventListener(u
"pagehide"_ns
, this, true);
401 MozPromiseHolder
<GenericPromise
> mHolder
;
402 RefPtr
<Document
> mDocument
;
405 NS_IMPL_ISUPPORTS(FullscreenWaitListener
, nsIDOMEventListener
)
407 void ScreenOrientation::AbortInProcessOrientationPromises(
408 BrowsingContext
* aBrowsingContext
) {
409 MOZ_ASSERT(aBrowsingContext
);
411 aBrowsingContext
= aBrowsingContext
->Top();
412 aBrowsingContext
->PreOrderWalk([](BrowsingContext
* aContext
) {
413 nsIDocShell
* docShell
= aContext
->GetDocShell();
415 Document
* doc
= docShell
->GetDocument();
417 Promise
* promise
= doc
->GetOrientationPendingPromise();
419 promise
->MaybeReject(NS_ERROR_DOM_ABORT_ERR
);
420 doc
->ClearOrientationPendingPromise();
427 already_AddRefed
<Promise
> ScreenOrientation::LockInternal(
428 hal::ScreenOrientation aOrientation
, ErrorResult
& aRv
) {
429 // Steps to apply an orientation lock as defined in spec.
432 // Let document be this's relevant global object's associated Document.
434 Document
* doc
= GetResponsibleDocument();
435 if (NS_WARN_IF(!doc
)) {
436 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
441 // If document is not fully active, return a promise rejected with an
442 // "InvalidStateError" DOMException.
444 nsCOMPtr
<nsPIDOMWindowInner
> owner
= GetOwner();
445 if (NS_WARN_IF(!owner
)) {
446 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
450 nsCOMPtr
<nsIDocShell
> docShell
= owner
->GetDocShell();
451 if (NS_WARN_IF(!docShell
)) {
452 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
456 nsCOMPtr
<nsIGlobalObject
> go
= do_QueryInterface(owner
);
458 RefPtr
<Promise
> p
= Promise::Create(go
, aRv
);
459 if (NS_WARN_IF(aRv
.Failed())) {
463 if (!owner
->IsFullyActive()) {
464 p
->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR
);
469 // If document has the sandboxed orientation lock browsing context flag set,
470 // or doesn't meet the pre-lock conditions, or locking would be a security
471 // risk, return a promise rejected with a "SecurityError" DOMException and
472 // abort these steps.
474 LockPermission perm
= GetLockOrientationPermission(true);
475 if (perm
== LOCK_DENIED
) {
476 p
->MaybeReject(NS_ERROR_DOM_SECURITY_ERR
);
481 // If the user agent does not support locking the screen orientation to
482 // orientation, return a promise rejected with a "NotSupportedError"
483 // DOMException and abort these steps.
485 #if !defined(MOZ_WIDGET_ANDROID) && !defined(XP_WIN)
486 // User agent does not support locking the screen orientation.
487 p
->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
490 // Bypass locking screen orientation if preference is false
491 if (!StaticPrefs::dom_screenorientation_allow_lock()) {
492 p
->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
496 RefPtr
<BrowsingContext
> bc
= docShell
->GetBrowsingContext();
497 bc
= bc
? bc
->Top() : nullptr;
499 aRv
.Throw(NS_ERROR_UNEXPECTED
);
503 bc
->SetOrientationLock(aOrientation
, aRv
);
508 AbortInProcessOrientationPromises(bc
);
509 dom::ContentChild::GetSingleton()->SendAbortOtherOrientationPendingPromises(
512 if (!doc
->SetOrientationPendingPromise(p
)) {
513 p
->MaybeReject(NS_ERROR_DOM_SECURITY_ERR
);
517 if (perm
== LOCK_ALLOWED
|| doc
->Fullscreen()) {
518 nsCOMPtr
<nsIRunnable
> lockOrientationTask
= new LockOrientationTask(
519 this, p
, aOrientation
, doc
, perm
== FULLSCREEN_LOCK_ALLOWED
);
520 aRv
= NS_DispatchToMainThread(lockOrientationTask
);
521 if (NS_WARN_IF(aRv
.Failed())) {
528 MOZ_ASSERT(perm
== FULLSCREEN_LOCK_ALLOWED
);
530 // Full screen state is pending. We have to wait for the completion.
531 RefPtr
<FullscreenWaitListener
> listener
= new FullscreenWaitListener();
532 RefPtr
<Promise
> promise
= p
;
533 listener
->Promise(doc
)->Then(
534 GetMainThreadSerialEventTarget(), __func__
,
535 [self
= RefPtr
{this}, promise
= std::move(promise
), aOrientation
,
537 RefPtr
{doc
}](const GenericPromise::ResolveOrRejectValue
& aValue
) {
538 if (aValue
.IsResolve()) {
539 nsCOMPtr
<nsIRunnable
> lockOrientationTask
= new LockOrientationTask(
540 self
, promise
, aOrientation
, document
, true);
541 nsresult rv
= NS_DispatchToMainThread(lockOrientationTask
);
542 if (NS_SUCCEEDED(rv
)) {
546 // Pending full screen request is canceled or causes an error.
547 if (document
->GetOrientationPendingPromise() != promise
) {
548 // The document's pending promise is not associated with
552 // pre-lock conditions aren't matched.
553 promise
->MaybeReject(NS_ERROR_DOM_SECURITY_ERR
);
554 document
->ClearOrientationPendingPromise();
561 RefPtr
<GenericNonExclusivePromise
> ScreenOrientation::LockDeviceOrientation(
562 hal::ScreenOrientation aOrientation
, bool aIsFullscreen
) {
564 return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_DOM_ABORT_ERR
,
568 nsCOMPtr
<EventTarget
> target
= GetOwner()->GetDoc();
569 // We need to register a listener so we learn when we leave fullscreen
570 // and when we will have to unlock the screen.
571 // This needs to be done before LockScreenOrientation call to make sure
572 // the locking can be unlocked.
573 if (aIsFullscreen
&& !target
) {
574 return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_DOM_ABORT_ERR
,
578 // We are fullscreen and lock has been accepted.
580 if (!mFullscreenListener
) {
581 mFullscreenListener
= new FullscreenEventListener();
584 nsresult rv
= target
->AddSystemEventListener(u
"fullscreenchange"_ns
,
586 /* aUseCapture = */ true);
587 if (NS_WARN_IF(NS_FAILED(rv
))) {
588 return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_DOM_ABORT_ERR
,
593 mTriedToLockDeviceOrientation
= true;
594 return hal::LockScreenOrientation(aOrientation
);
597 void ScreenOrientation::Unlock(ErrorResult
& aRv
) {
598 if (RefPtr
<Promise
> p
= LockInternal(hal::ScreenOrientation::None
, aRv
)) {
599 // Don't end up reporting unhandled promise rejection since
600 // screen.orientation.unlock doesn't return promise.
601 MOZ_ALWAYS_TRUE(p
->SetAnyPromiseIsHandled());
605 void ScreenOrientation::UnlockDeviceOrientation() {
606 hal::UnlockScreenOrientation();
607 CleanupFullscreenListener();
610 void ScreenOrientation::CleanupFullscreenListener() {
611 if (!mFullscreenListener
|| !GetOwner()) {
612 mFullscreenListener
= nullptr;
616 // Remove event listener in case of fullscreen lock.
617 if (nsCOMPtr
<EventTarget
> target
= GetOwner()->GetDoc()) {
618 target
->RemoveSystemEventListener(u
"fullscreenchange"_ns
,
620 /* useCapture */ true);
623 mFullscreenListener
= nullptr;
626 OrientationType
ScreenOrientation::DeviceType(CallerType aCallerType
) const {
627 if (nsContentUtils::ShouldResistFingerprinting(
628 aCallerType
, GetOwnerGlobal(), RFPTarget::ScreenOrientation
)) {
629 return OrientationType::Landscape_primary
;
634 uint16_t ScreenOrientation::DeviceAngle(CallerType aCallerType
) const {
635 if (nsContentUtils::ShouldResistFingerprinting(
636 aCallerType
, GetOwnerGlobal(), RFPTarget::ScreenOrientation
)) {
642 OrientationType
ScreenOrientation::GetType(CallerType aCallerType
,
643 ErrorResult
& aRv
) const {
644 if (nsContentUtils::ShouldResistFingerprinting(
645 aCallerType
, GetOwnerGlobal(), RFPTarget::ScreenOrientation
)) {
646 return OrientationType::Landscape_primary
;
649 Document
* doc
= GetResponsibleDocument();
650 BrowsingContext
* bc
= doc
? doc
->GetBrowsingContext() : nullptr;
652 aRv
.Throw(NS_ERROR_UNEXPECTED
);
653 return OrientationType::Portrait_primary
;
656 return bc
->GetCurrentOrientationType();
659 uint16_t ScreenOrientation::GetAngle(CallerType aCallerType
,
660 ErrorResult
& aRv
) const {
661 if (nsContentUtils::ShouldResistFingerprinting(
662 aCallerType
, GetOwnerGlobal(), RFPTarget::ScreenOrientation
)) {
666 Document
* doc
= GetResponsibleDocument();
667 BrowsingContext
* bc
= doc
? doc
->GetBrowsingContext() : nullptr;
669 aRv
.Throw(NS_ERROR_UNEXPECTED
);
673 return bc
->GetCurrentOrientationAngle();
676 ScreenOrientation::LockPermission
677 ScreenOrientation::GetLockOrientationPermission(bool aCheckSandbox
) const {
678 nsCOMPtr
<nsPIDOMWindowInner
> owner
= GetOwner();
683 // Chrome can always lock the screen orientation.
684 if (owner
->GetBrowsingContext()->IsChrome()) {
688 nsCOMPtr
<Document
> doc
= owner
->GetDoc();
689 if (!doc
|| doc
->Hidden()) {
693 // Sandboxed without "allow-orientation-lock"
694 if (aCheckSandbox
&& doc
->GetSandboxFlags() & SANDBOXED_ORIENTATION_LOCK
) {
698 if (Preferences::GetBool(
699 "dom.screenorientation.testing.non_fullscreen_lock_allow", false)) {
703 // Other content must be fullscreen in order to lock orientation.
704 return doc
->Fullscreen() || doc
->HasPendingFullscreenRequests()
705 ? FULLSCREEN_LOCK_ALLOWED
709 Document
* ScreenOrientation::GetResponsibleDocument() const {
710 nsCOMPtr
<nsPIDOMWindowInner
> owner
= GetOwner();
715 return owner
->GetDoc();
718 void ScreenOrientation::MaybeChanged() {
719 Document
* doc
= GetResponsibleDocument();
720 if (!doc
|| doc
->ShouldResistFingerprinting(RFPTarget::ScreenOrientation
)) {
724 BrowsingContext
* bc
= doc
->GetBrowsingContext();
729 hal::ScreenOrientation orientation
= mScreen
->GetOrientationType();
730 if (orientation
!= hal::ScreenOrientation::PortraitPrimary
&&
731 orientation
!= hal::ScreenOrientation::PortraitSecondary
&&
732 orientation
!= hal::ScreenOrientation::LandscapePrimary
&&
733 orientation
!= hal::ScreenOrientation::LandscapeSecondary
) {
734 // The platform may notify of some other values from
735 // an orientation lock, but we only care about real
736 // changes to screen orientation which result in one of
737 // the values we care about.
741 OrientationType previousOrientation
= mType
;
742 mAngle
= mScreen
->GetOrientationAngle();
743 mType
= InternalOrientationToType(orientation
);
745 DebugOnly
<nsresult
> rv
;
746 if (mScreen
&& mType
!= previousOrientation
) {
747 // Use of mozorientationchange is deprecated.
748 rv
= mScreen
->DispatchTrustedEvent(u
"mozorientationchange"_ns
);
749 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "DispatchTrustedEvent failed");
752 if (doc
->Hidden() && !mVisibleListener
) {
753 mVisibleListener
= new VisibleEventListener();
754 rv
= doc
->AddSystemEventListener(u
"visibilitychange"_ns
, mVisibleListener
,
755 /* aUseCapture = */ true);
756 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "AddSystemEventListener failed");
760 if (mType
!= bc
->GetCurrentOrientationType()) {
761 rv
= bc
->SetCurrentOrientation(mType
, mAngle
);
762 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "SetCurrentOrientation failed");
764 nsCOMPtr
<nsIRunnable
> runnable
= DispatchChangeEventAndResolvePromise();
765 rv
= NS_DispatchToMainThread(runnable
);
766 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "NS_DispatchToMainThread failed");
770 void ScreenOrientation::UpdateActiveOrientationLock(
771 hal::ScreenOrientation aOrientation
) {
772 if (aOrientation
== hal::ScreenOrientation::None
) {
773 hal::UnlockScreenOrientation();
775 hal::LockScreenOrientation(aOrientation
)
777 GetMainThreadSerialEventTarget(), __func__
,
778 [](const GenericNonExclusivePromise::ResolveOrRejectValue
& aValue
) {
779 NS_WARNING_ASSERTION(aValue
.IsResolve(),
780 "hal::LockScreenOrientation failed");
785 nsCOMPtr
<nsIRunnable
>
786 ScreenOrientation::DispatchChangeEventAndResolvePromise() {
787 RefPtr
<Document
> doc
= GetResponsibleDocument();
788 RefPtr
<ScreenOrientation
> self
= this;
789 return NS_NewRunnableFunction(
790 "dom::ScreenOrientation::DispatchChangeEvent", [self
, doc
]() {
791 DebugOnly
<nsresult
> rv
= self
->DispatchTrustedEvent(u
"change"_ns
);
792 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "DispatchTrustedEvent failed");
794 Promise
* pendingPromise
= doc
->GetOrientationPendingPromise();
795 if (pendingPromise
) {
796 pendingPromise
->MaybeResolveWithUndefined();
797 doc
->ClearOrientationPendingPromise();
803 JSObject
* ScreenOrientation::WrapObject(JSContext
* aCx
,
804 JS::Handle
<JSObject
*> aGivenProto
) {
805 return ScreenOrientation_Binding::Wrap(aCx
, this, aGivenProto
);
808 NS_IMPL_ISUPPORTS(ScreenOrientation::VisibleEventListener
, nsIDOMEventListener
)
811 ScreenOrientation::VisibleEventListener::HandleEvent(Event
* aEvent
) {
812 // Document may have become visible, if the page is visible, run the steps
813 // following the "now visible algorithm" as specified.
814 MOZ_ASSERT(aEvent
->GetCurrentTarget());
815 nsCOMPtr
<nsINode
> eventTargetNode
=
816 nsINode::FromEventTarget(aEvent
->GetCurrentTarget());
817 if (!eventTargetNode
|| !eventTargetNode
->IsDocument() ||
818 eventTargetNode
->AsDocument()->Hidden()) {
822 RefPtr
<Document
> doc
= eventTargetNode
->AsDocument();
823 auto* win
= nsGlobalWindowInner::Cast(doc
->GetInnerWindow());
828 ScreenOrientation
* orientation
= win
->Screen()->Orientation();
829 MOZ_ASSERT(orientation
);
831 doc
->RemoveSystemEventListener(u
"visibilitychange"_ns
, this, true);
833 BrowsingContext
* bc
= doc
->GetBrowsingContext();
834 if (bc
&& bc
->GetCurrentOrientationType() !=
835 orientation
->DeviceType(CallerType::System
)) {
837 bc
->SetCurrentOrientation(orientation
->DeviceType(CallerType::System
),
838 orientation
->DeviceAngle(CallerType::System
));
839 NS_ENSURE_SUCCESS(result
, result
);
841 nsCOMPtr
<nsIRunnable
> runnable
=
842 orientation
->DispatchChangeEventAndResolvePromise();
843 MOZ_TRY(NS_DispatchToMainThread(runnable
));
848 NS_IMPL_ISUPPORTS(ScreenOrientation::FullscreenEventListener
,
852 ScreenOrientation::FullscreenEventListener::HandleEvent(Event
* aEvent
) {
854 nsAutoString eventType
;
855 aEvent
->GetType(eventType
);
857 MOZ_ASSERT(eventType
.EqualsLiteral("fullscreenchange"));
860 EventTarget
* target
= aEvent
->GetCurrentTarget();
862 MOZ_ASSERT(target
->IsNode());
863 RefPtr
<Document
> doc
= nsINode::FromEventTarget(target
)->AsDocument();
866 // We have to make sure that the event we got is the event sent when
867 // fullscreen is disabled because we could get one when fullscreen
868 // got enabled if the lock call is done at the same moment.
869 if (doc
->Fullscreen()) {
873 BrowsingContext
* bc
= doc
->GetBrowsingContext();
874 bc
= bc
? bc
->Top() : nullptr;
876 bc
->SetOrientationLock(hal::ScreenOrientation::None
, IgnoreErrors());
879 hal::UnlockScreenOrientation();
881 target
->RemoveSystemEventListener(u
"fullscreenchange"_ns
, this, true);