Bug 1854550 - pt 10. Allow LOG() with zero extra arguments r=glandium
[gecko.git] / dom / base / ScreenOrientation.cpp
blob25f84f10b2c3b9304c42d058018a674cf9a0b3f6
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"
12 #include "nsScreen.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,
28 mScreen);
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;
47 default:
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;
63 default:
64 MOZ_CRASH("Bad aOrientation value");
68 ScreenOrientation::ScreenOrientation(nsPIDOMWindowInner* aWindow,
69 nsScreen* aScreen)
70 : DOMEventTargetHelper(aWindow), mScreen(aScreen) {
71 MOZ_ASSERT(aWindow);
72 MOZ_ASSERT(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();
87 } else {
88 CleanupFullscreenListener();
91 MOZ_ASSERT(!mFullscreenListener);
94 class ScreenOrientation::FullscreenEventListener final
95 : public nsIDOMEventListener {
96 ~FullscreenEventListener() = default;
98 public:
99 FullscreenEventListener() = default;
101 NS_DECL_ISUPPORTS
102 NS_DECL_NSIDOMEVENTLISTENER
105 class ScreenOrientation::VisibleEventListener final
106 : public nsIDOMEventListener {
107 ~VisibleEventListener() = default;
109 public:
110 VisibleEventListener() = default;
112 NS_DECL_ISUPPORTS
113 NS_DECL_NSIDOMEVENTLISTENER
116 class ScreenOrientation::LockOrientationTask final : public nsIRunnable {
117 ~LockOrientationTask();
119 public:
120 NS_DECL_ISUPPORTS
121 NS_DECL_NSIRUNNABLE
123 LockOrientationTask(ScreenOrientation* aScreenOrientation, Promise* aPromise,
124 hal::ScreenOrientation aOrientationLock,
125 Document* aDocument, bool aIsFullscreen);
127 protected:
128 bool OrientationLockContains(OrientationType aOrientationType);
130 RefPtr<ScreenOrientation> mScreenOrientation;
131 RefPtr<Promise> mPromise;
132 hal::ScreenOrientation mOrientationLock;
133 WeakPtr<Document> mDocument;
134 bool mIsFullscreen;
137 NS_IMPL_ISUPPORTS(ScreenOrientation::LockOrientationTask, nsIRunnable)
139 ScreenOrientation::LockOrientationTask::LockOrientationTask(
140 ScreenOrientation* aScreenOrientation, Promise* aPromise,
141 hal::ScreenOrientation aOrientationLock, Document* aDocument,
142 bool aIsFullscreen)
143 : mScreenOrientation(aScreenOrientation),
144 mPromise(aPromise),
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));
160 NS_IMETHODIMP
161 ScreenOrientation::LockOrientationTask::Run() {
162 if (!mPromise) {
163 return NS_OK;
166 if (!mDocument) {
167 mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
168 return NS_OK;
171 nsCOMPtr<nsPIDOMWindowInner> owner = mScreenOrientation->GetOwner();
172 if (!owner || !owner->IsFullyActive()) {
173 mPromise->MaybeRejectWithAbortError("The document is not fully active.");
174 return NS_OK;
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.
183 return NS_OK;
186 if (mDocument->Hidden()) {
187 // Active orientation lock is not the document's orientation lock.
188 mPromise->MaybeResolveWithUndefined();
189 mDocument->ClearOrientationPendingPromise();
190 return NS_OK;
193 if (mOrientationLock == hal::ScreenOrientation::None) {
194 mScreenOrientation->UnlockDeviceOrientation();
195 mPromise->MaybeResolveWithUndefined();
196 mDocument->ClearOrientationPendingPromise();
197 return NS_OK;
200 BrowsingContext* bc = mDocument->GetBrowsingContext();
201 if (!bc) {
202 mPromise->MaybeResolveWithUndefined();
203 mDocument->ClearOrientationPendingPromise();
204 return NS_OK;
207 OrientationType previousOrientationType = bc->GetCurrentOrientationType();
208 mScreenOrientation->LockDeviceOrientation(mOrientationLock, mIsFullscreen)
209 ->Then(
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().
217 return;
220 if (!self->mDocument) {
221 self->mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
222 return;
225 if (self->mDocument->GetOrientationPendingPromise() !=
226 self->mPromise) {
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()
230 return;
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();
249 return;
251 self->mPromise->MaybeReject(aValue.RejectValue());
252 self->mDocument->ClearOrientationPendingPromise();
255 return NS_OK;
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;
268 break;
269 case OrientationLockType::Natural:
270 orientation |= hal::ScreenOrientation::Default;
271 break;
272 case OrientationLockType::Landscape:
273 orientation = hal::ScreenOrientation::LandscapePrimary |
274 hal::ScreenOrientation::LandscapeSecondary;
275 break;
276 case OrientationLockType::Portrait:
277 orientation = hal::ScreenOrientation::PortraitPrimary |
278 hal::ScreenOrientation::PortraitSecondary;
279 break;
280 case OrientationLockType::Portrait_primary:
281 orientation = hal::ScreenOrientation::PortraitPrimary;
282 break;
283 case OrientationLockType::Portrait_secondary:
284 orientation = hal::ScreenOrientation::PortraitSecondary;
285 break;
286 case OrientationLockType::Landscape_primary:
287 orientation = hal::ScreenOrientation::LandscapePrimary;
288 break;
289 case OrientationLockType::Landscape_secondary:
290 orientation = hal::ScreenOrientation::LandscapeSecondary;
291 break;
292 default:
293 NS_WARNING("Unexpected orientation type");
294 aRv.Throw(NS_ERROR_UNEXPECTED);
295 return nullptr;
298 return LockInternal(orientation, aRv);
301 // Wait for document entered fullscreen.
302 class FullscreenWaitListener final : public nsIDOMEventListener {
303 private:
304 ~FullscreenWaitListener() = default;
306 public:
307 FullscreenWaitListener() = default;
309 NS_DECL_ISUPPORTS
311 // When we have pending fullscreen request, we will wait for the completion or
312 // cancel of it.
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();
333 return NS_OK;
336 MOZ_ASSERT(eventType.EqualsLiteral("fullscreenchange") ||
337 eventType.EqualsLiteral("fullscreenerror") ||
338 eventType.EqualsLiteral("pagehide"));
339 if (mDocument->Fullscreen()) {
340 mHolder.Resolve(true, __func__);
341 } else {
342 mHolder.Reject(NS_ERROR_FAILURE, __func__);
344 CleanupEventListener();
345 return NS_OK;
348 private:
349 nsresult InstallEventListener(Document* aDoc) {
350 if (mDocument) {
351 return NS_OK;
354 mDocument = aDoc;
355 nsresult rv = aDoc->AddSystemEventListener(u"fullscreenchange"_ns, this,
356 /* aUseCapture = */ true);
357 if (NS_FAILED(rv)) {
358 CleanupEventListener();
359 return rv;
362 rv = aDoc->AddSystemEventListener(u"fullscreenerror"_ns, this,
363 /* aUseCapture = */ true);
364 if (NS_FAILED(rv)) {
365 CleanupEventListener();
366 return rv;
369 nsPIDOMWindowOuter* window = aDoc->GetWindow();
370 nsCOMPtr<EventTarget> target = do_QueryInterface(window);
371 if (!target) {
372 CleanupEventListener();
373 return NS_ERROR_FAILURE;
375 rv = target->AddSystemEventListener(u"pagehide"_ns, this,
376 /* aUseCapture = */ true,
377 /* aWantsUntrusted = */ false);
378 if (NS_FAILED(rv)) {
379 CleanupEventListener();
380 return rv;
383 return NS_OK;
386 void CleanupEventListener() {
387 if (!mDocument) {
388 return;
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);
395 if (target) {
396 target->RemoveSystemEventListener(u"pagehide"_ns, this, true);
398 mDocument = nullptr;
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();
414 if (docShell) {
415 Document* doc = docShell->GetDocument();
416 if (doc) {
417 Promise* promise = doc->GetOrientationPendingPromise();
418 if (promise) {
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.
431 // Step 1.
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);
437 return nullptr;
440 // Step 2.
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);
447 return nullptr;
450 nsCOMPtr<nsIDocShell> docShell = owner->GetDocShell();
451 if (NS_WARN_IF(!docShell)) {
452 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
453 return nullptr;
456 nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(owner);
457 MOZ_ASSERT(go);
458 RefPtr<Promise> p = Promise::Create(go, aRv);
459 if (NS_WARN_IF(aRv.Failed())) {
460 return nullptr;
463 if (!owner->IsFullyActive()) {
464 p->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
465 return p.forget();
468 // Step 3.
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);
477 return p.forget();
480 // Step 4.
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);
488 return p.forget();
489 #else
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);
493 return p.forget();
496 RefPtr<BrowsingContext> bc = docShell->GetBrowsingContext();
497 bc = bc ? bc->Top() : nullptr;
498 if (!bc) {
499 aRv.Throw(NS_ERROR_UNEXPECTED);
500 return nullptr;
503 bc->SetOrientationLock(aOrientation, aRv);
504 if (aRv.Failed()) {
505 return nullptr;
508 AbortInProcessOrientationPromises(bc);
509 dom::ContentChild::GetSingleton()->SendAbortOtherOrientationPendingPromises(
510 bc);
512 if (!doc->SetOrientationPendingPromise(p)) {
513 p->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
514 return p.forget();
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())) {
522 return nullptr;
525 return p.forget();
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,
536 document =
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)) {
543 return;
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
549 // this promise.
550 return;
552 // pre-lock conditions aren't matched.
553 promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
554 document->ClearOrientationPendingPromise();
557 return p.forget();
558 #endif
561 RefPtr<GenericNonExclusivePromise> ScreenOrientation::LockDeviceOrientation(
562 hal::ScreenOrientation aOrientation, bool aIsFullscreen) {
563 if (!GetOwner()) {
564 return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_DOM_ABORT_ERR,
565 __func__);
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,
575 __func__);
578 // We are fullscreen and lock has been accepted.
579 if (aIsFullscreen) {
580 if (!mFullscreenListener) {
581 mFullscreenListener = new FullscreenEventListener();
584 nsresult rv = target->AddSystemEventListener(u"fullscreenchange"_ns,
585 mFullscreenListener,
586 /* aUseCapture = */ true);
587 if (NS_WARN_IF(NS_FAILED(rv))) {
588 return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_DOM_ABORT_ERR,
589 __func__);
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;
613 return;
616 // Remove event listener in case of fullscreen lock.
617 if (nsCOMPtr<EventTarget> target = GetOwner()->GetDoc()) {
618 target->RemoveSystemEventListener(u"fullscreenchange"_ns,
619 mFullscreenListener,
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;
631 return mType;
634 uint16_t ScreenOrientation::DeviceAngle(CallerType aCallerType) const {
635 if (nsContentUtils::ShouldResistFingerprinting(
636 aCallerType, GetOwnerGlobal(), RFPTarget::ScreenOrientation)) {
637 return 0;
639 return mAngle;
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;
651 if (!bc) {
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)) {
663 return 0;
666 Document* doc = GetResponsibleDocument();
667 BrowsingContext* bc = doc ? doc->GetBrowsingContext() : nullptr;
668 if (!bc) {
669 aRv.Throw(NS_ERROR_UNEXPECTED);
670 return 0;
673 return bc->GetCurrentOrientationAngle();
676 ScreenOrientation::LockPermission
677 ScreenOrientation::GetLockOrientationPermission(bool aCheckSandbox) const {
678 nsCOMPtr<nsPIDOMWindowInner> owner = GetOwner();
679 if (!owner) {
680 return LOCK_DENIED;
683 // Chrome can always lock the screen orientation.
684 if (owner->GetBrowsingContext()->IsChrome()) {
685 return LOCK_ALLOWED;
688 nsCOMPtr<Document> doc = owner->GetDoc();
689 if (!doc || doc->Hidden()) {
690 return LOCK_DENIED;
693 // Sandboxed without "allow-orientation-lock"
694 if (aCheckSandbox && doc->GetSandboxFlags() & SANDBOXED_ORIENTATION_LOCK) {
695 return LOCK_DENIED;
698 if (Preferences::GetBool(
699 "dom.screenorientation.testing.non_fullscreen_lock_allow", false)) {
700 return LOCK_ALLOWED;
703 // Other content must be fullscreen in order to lock orientation.
704 return doc->Fullscreen() || doc->HasPendingFullscreenRequests()
705 ? FULLSCREEN_LOCK_ALLOWED
706 : LOCK_DENIED;
709 Document* ScreenOrientation::GetResponsibleDocument() const {
710 nsCOMPtr<nsPIDOMWindowInner> owner = GetOwner();
711 if (!owner) {
712 return nullptr;
715 return owner->GetDoc();
718 void ScreenOrientation::MaybeChanged() {
719 Document* doc = GetResponsibleDocument();
720 if (!doc || doc->ShouldResistFingerprinting(RFPTarget::ScreenOrientation)) {
721 return;
724 BrowsingContext* bc = doc->GetBrowsingContext();
725 if (!bc) {
726 return;
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.
738 return;
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");
757 return;
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();
774 } else {
775 hal::LockScreenOrientation(aOrientation)
776 ->Then(
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");
793 if (doc) {
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)
810 NS_IMETHODIMP
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()) {
819 return NS_OK;
822 RefPtr<Document> doc = eventTargetNode->AsDocument();
823 auto* win = nsGlobalWindowInner::Cast(doc->GetInnerWindow());
824 if (!win) {
825 return NS_OK;
828 ErrorResult rv;
829 nsScreen* screen = win->GetScreen(rv);
830 if (NS_WARN_IF(rv.Failed())) {
831 return rv.StealNSResult();
834 MOZ_ASSERT(screen);
835 ScreenOrientation* orientation = screen->Orientation();
836 MOZ_ASSERT(orientation);
838 doc->RemoveSystemEventListener(u"visibilitychange"_ns, this, true);
840 BrowsingContext* bc = doc->GetBrowsingContext();
841 if (bc && bc->GetCurrentOrientationType() !=
842 orientation->DeviceType(CallerType::System)) {
843 nsresult result =
844 bc->SetCurrentOrientation(orientation->DeviceType(CallerType::System),
845 orientation->DeviceAngle(CallerType::System));
846 NS_ENSURE_SUCCESS(result, result);
848 nsCOMPtr<nsIRunnable> runnable =
849 orientation->DispatchChangeEventAndResolvePromise();
850 rv = NS_DispatchToMainThread(runnable);
851 if (NS_WARN_IF(rv.Failed())) {
852 return rv.StealNSResult();
856 return NS_OK;
859 NS_IMPL_ISUPPORTS(ScreenOrientation::FullscreenEventListener,
860 nsIDOMEventListener)
862 NS_IMETHODIMP
863 ScreenOrientation::FullscreenEventListener::HandleEvent(Event* aEvent) {
864 #ifdef DEBUG
865 nsAutoString eventType;
866 aEvent->GetType(eventType);
868 MOZ_ASSERT(eventType.EqualsLiteral("fullscreenchange"));
869 #endif
871 EventTarget* target = aEvent->GetCurrentTarget();
872 MOZ_ASSERT(target);
873 MOZ_ASSERT(target->IsNode());
874 RefPtr<Document> doc = nsINode::FromEventTarget(target)->AsDocument();
875 MOZ_ASSERT(doc);
877 // We have to make sure that the event we got is the event sent when
878 // fullscreen is disabled because we could get one when fullscreen
879 // got enabled if the lock call is done at the same moment.
880 if (doc->Fullscreen()) {
881 return NS_OK;
884 BrowsingContext* bc = doc->GetBrowsingContext();
885 bc = bc ? bc->Top() : nullptr;
886 if (bc) {
887 bc->SetOrientationLock(hal::ScreenOrientation::None, IgnoreErrors());
890 hal::UnlockScreenOrientation();
892 target->RemoveSystemEventListener(u"fullscreenchange"_ns, this, true);
893 return NS_OK;