Bug 1131470 - Part 3: Implement ScreenOrientation interface. r=baku
authorWilliam Chen <wchen@mozilla.com>
Tue, 18 Aug 2015 14:55:21 -0700
changeset 292628 3413e043886cf1a8d926942faefe2de6314e79fe
parent 292627 afee399c0f2ff1ff14441c8ac829a11170c0b210
child 292629 4f32d297a97eafd6453ad86327de32618499028a
push id962
push userjlund@mozilla.com
push dateFri, 04 Dec 2015 23:28:54 +0000
treeherdermozilla-release@23a2d286e80f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1131470
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1131470 - Part 3: Implement ScreenOrientation interface. r=baku
dom/base/ScreenOrientation.cpp
dom/base/ScreenOrientation.h
dom/base/StructuredCloneHelper.cpp
dom/base/StructuredCloneHelper.h
dom/base/moz.build
dom/base/nsAttrValue.h
dom/base/nsDocument.cpp
dom/base/nsDocument.h
dom/base/nsIDocument.h
dom/base/nsScreen.cpp
dom/base/nsScreen.h
dom/canvas/ImageBitmap.h
dom/tests/mochitest/general/test_interfaces.html
dom/webidl/Screen.webidl
dom/webidl/ScreenOrientation.webidl
dom/webidl/moz.build
testing/web-platform/meta/screen-orientation/lock-bad-argument.html.ini
testing/web-platform/meta/screen-orientation/lock-basic.html.ini
testing/web-platform/meta/screen-orientation/lock-sandboxed-iframe.html.ini
testing/web-platform/meta/screen-orientation/onchange-event-subframe.html.ini
testing/web-platform/meta/screen-orientation/onchange-event.html.ini
testing/web-platform/meta/screen-orientation/orientation-api.html.ini
testing/web-platform/meta/screen-orientation/orientation-reading.html.ini
new file mode 100644
--- /dev/null
+++ b/dom/base/ScreenOrientation.cpp
@@ -0,0 +1,628 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ScreenOrientation.h"
+#include "nsIDeviceSensors.h"
+#include "nsScreen.h"
+
+using namespace mozilla::dom;
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(ScreenOrientation,
+                                   DOMEventTargetHelper,
+                                   mScreen);
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ScreenOrientation)
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+
+NS_IMPL_ADDREF_INHERITED(ScreenOrientation, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(ScreenOrientation, DOMEventTargetHelper)
+
+static OrientationType
+InternalOrientationToType(ScreenOrientationInternal aOrientation)
+{
+  switch (aOrientation) {
+  case eScreenOrientation_PortraitPrimary:
+    return OrientationType::Portrait_primary;
+  case eScreenOrientation_PortraitSecondary:
+    return OrientationType::Portrait_secondary;
+  case eScreenOrientation_LandscapePrimary:
+    return OrientationType::Landscape_primary;
+  case eScreenOrientation_LandscapeSecondary:
+    return OrientationType::Landscape_secondary;
+  default:
+    MOZ_CRASH("Bad aOrientation value");
+  }
+}
+
+static ScreenOrientationInternal
+OrientationTypeToInternal(OrientationType aOrientation)
+{
+  switch (aOrientation) {
+  case OrientationType::Portrait_primary:
+    return eScreenOrientation_PortraitPrimary;
+  case OrientationType::Portrait_secondary:
+    return eScreenOrientation_PortraitSecondary;
+  case OrientationType::Landscape_primary:
+    return eScreenOrientation_LandscapePrimary;
+  case OrientationType::Landscape_secondary:
+    return eScreenOrientation_LandscapeSecondary;
+  default:
+    MOZ_CRASH("Bad aOrientation value");
+  }
+}
+
+ScreenOrientation::ScreenOrientation(nsPIDOMWindow* aWindow, nsScreen* aScreen)
+  : DOMEventTargetHelper(aWindow), mScreen(aScreen)
+{
+  MOZ_ASSERT(aWindow);
+  MOZ_ASSERT(aScreen);
+
+  hal::RegisterScreenConfigurationObserver(this);
+
+  hal::ScreenConfiguration config;
+  hal::GetCurrentScreenConfiguration(&config);
+  mType = InternalOrientationToType(config.orientation());
+  mAngle = config.angle();
+
+  nsIDocument* doc = GetResponsibleDocument();
+  if (doc) {
+    doc->SetCurrentOrientation(mType, mAngle);
+  }
+}
+
+ScreenOrientation::~ScreenOrientation()
+{
+  hal::UnregisterScreenConfigurationObserver(this);
+  MOZ_ASSERT(!mFullScreenListener);
+}
+
+class ScreenOrientation::FullScreenEventListener final : public nsIDOMEventListener
+{
+  ~FullScreenEventListener() {}
+public:
+  FullScreenEventListener() {}
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMEVENTLISTENER
+};
+
+class ScreenOrientation::VisibleEventListener final : public nsIDOMEventListener
+{
+  ~VisibleEventListener() {}
+public:
+  VisibleEventListener() {}
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMEVENTLISTENER
+};
+
+class ScreenOrientation::LockOrientationTask final : public nsIRunnable
+{
+  ~LockOrientationTask();
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIRUNNABLE
+
+  LockOrientationTask(ScreenOrientation* aScreenOrientation,
+                      Promise* aPromise,
+                      ScreenOrientationInternal aOrientationLock,
+                      nsIDocument* aDocument,
+                      bool aIsFullScreen);
+protected:
+  bool OrientationLockContains(OrientationType aOrientationType);
+
+  nsRefPtr<ScreenOrientation> mScreenOrientation;
+  nsRefPtr<Promise> mPromise;
+  ScreenOrientationInternal mOrientationLock;
+  nsCOMPtr<nsIDocument> mDocument;
+  bool mIsFullScreen;
+};
+
+NS_IMPL_ISUPPORTS(ScreenOrientation::LockOrientationTask, nsIRunnable)
+
+ScreenOrientation::LockOrientationTask::LockOrientationTask(
+  ScreenOrientation* aScreenOrientation, Promise* aPromise,
+  ScreenOrientationInternal aOrientationLock,
+  nsIDocument* aDocument, bool aIsFullScreen)
+  : mScreenOrientation(aScreenOrientation), mPromise(aPromise),
+    mOrientationLock(aOrientationLock), mDocument(aDocument),
+    mIsFullScreen(aIsFullScreen)
+{
+  MOZ_ASSERT(aScreenOrientation);
+  MOZ_ASSERT(aPromise);
+  MOZ_ASSERT(aDocument);
+}
+
+ScreenOrientation::LockOrientationTask::~LockOrientationTask()
+{
+}
+
+bool
+ScreenOrientation::LockOrientationTask::OrientationLockContains(
+  OrientationType aOrientationType)
+{
+  return mOrientationLock & OrientationTypeToInternal(aOrientationType);
+}
+
+NS_IMETHODIMP
+ScreenOrientation::LockOrientationTask::Run()
+{
+  // Step to lock the orientation as defined in the spec.
+
+  if (mDocument->GetOrientationPendingPromise() != mPromise) {
+    // The document's pending promise is not associated with this task
+    // to lock orientation. There has since been another request to
+    // lock orientation, thus we don't need to do anything. Old promise
+    // should be been rejected.
+    return NS_OK;
+  }
+
+  if (mDocument->Hidden()) {
+    // Active orientation lock is not the document's orientation lock.
+    mPromise->MaybeResolve(JS::UndefinedHandleValue);
+    mDocument->SetOrientationPendingPromise(nullptr);
+    return NS_OK;
+  }
+
+  if (mOrientationLock == eScreenOrientation_None) {
+    mScreenOrientation->UnlockDeviceOrientation();
+    mPromise->MaybeResolve(JS::UndefinedHandleValue);
+    mDocument->SetOrientationPendingPromise(nullptr);
+    return NS_OK;
+  }
+
+  ErrorResult rv;
+  bool result = mScreenOrientation->LockDeviceOrientation(mOrientationLock,
+                                                          mIsFullScreen, rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    return rv.StealNSResult();
+  }
+
+  if (NS_WARN_IF(!result)) {
+    mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
+    mDocument->SetOrientationPendingPromise(nullptr);
+    return NS_OK;
+  }
+
+  if (OrientationLockContains(mDocument->CurrentOrientationType()) ||
+      (mOrientationLock == eScreenOrientation_Default &&
+       mDocument->CurrentOrientationAngle() == 0)) {
+    // Orientation lock will not cause an orientation change.
+    mPromise->MaybeResolve(JS::UndefinedHandleValue);
+    mDocument->SetOrientationPendingPromise(nullptr);
+  }
+
+  return NS_OK;
+}
+
+already_AddRefed<Promise>
+ScreenOrientation::Lock(OrientationLockType aOrientation, ErrorResult& aRv)
+{
+  ScreenOrientationInternal orientation = eScreenOrientation_None;
+
+  switch (aOrientation) {
+  case OrientationLockType::Any:
+    orientation = eScreenOrientation_PortraitPrimary |
+                  eScreenOrientation_PortraitSecondary |
+                  eScreenOrientation_LandscapePrimary |
+                  eScreenOrientation_LandscapeSecondary;
+    break;
+  case OrientationLockType::Natural:
+    orientation |= eScreenOrientation_Default;
+    break;
+  case OrientationLockType::Landscape:
+    orientation = eScreenOrientation_LandscapePrimary |
+                  eScreenOrientation_LandscapeSecondary;
+    break;
+  case OrientationLockType::Portrait:
+    orientation = eScreenOrientation_PortraitPrimary |
+                  eScreenOrientation_PortraitSecondary;
+    break;
+  case OrientationLockType::Portrait_primary:
+    orientation = eScreenOrientation_PortraitPrimary;
+    break;
+  case OrientationLockType::Portrait_secondary:
+    orientation = eScreenOrientation_PortraitSecondary;
+    break;
+  case OrientationLockType::Landscape_primary:
+    orientation = eScreenOrientation_LandscapePrimary;
+    break;
+  case OrientationLockType::Landscape_secondary:
+    orientation = eScreenOrientation_LandscapeSecondary;
+    break;
+  default:
+    NS_WARNING("Unexpected orientation type");
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
+  return LockInternal(orientation, aRv);
+}
+
+static inline void
+AbortOrientationPromises(nsIDocShell* aDocShell)
+{
+  MOZ_ASSERT(aDocShell);
+
+  nsIDocument* doc = aDocShell->GetDocument();
+  if (doc) {
+    Promise* promise = doc->GetOrientationPendingPromise();
+    if (promise) {
+      promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+      doc->SetOrientationPendingPromise(nullptr);
+    }
+  }
+
+  int32_t childCount;
+  aDocShell->GetChildCount(&childCount);
+  for (int32_t i = 0; i < childCount; i++) {
+    nsCOMPtr<nsIDocShellTreeItem> child;
+    if (NS_SUCCEEDED(aDocShell->GetChildAt(i, getter_AddRefs(child)))) {
+      nsCOMPtr<nsIDocShell> childShell(do_QueryInterface(child));
+      if (childShell) {
+        AbortOrientationPromises(childShell);
+      }
+    }
+  }
+}
+
+already_AddRefed<Promise>
+ScreenOrientation::LockInternal(ScreenOrientationInternal aOrientation, ErrorResult& aRv)
+{
+  // Steps to apply an orientation lock as defined in spec.
+
+  nsIDocument* doc = GetResponsibleDocument();
+  if (NS_WARN_IF(!doc)) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
+  nsCOMPtr<nsPIDOMWindow> owner = GetOwner();
+  if (NS_WARN_IF(!owner)) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIDocShell> docShell = owner->GetDocShell();
+  if (NS_WARN_IF(!docShell)) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(owner);
+  MOZ_ASSERT(go);
+  nsRefPtr<Promise> p = Promise::Create(go, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+#if !defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_WIDGET_GONK)
+  // User agent does not support locking the screen orientation.
+  p->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+  return p.forget();
+#else
+  LockPermission perm = GetLockOrientationPermission();
+  if (perm == LOCK_DENIED) {
+    p->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    return p.forget();
+  }
+
+  nsCOMPtr<nsIDocShellTreeItem> root;
+  docShell->GetSameTypeRootTreeItem(getter_AddRefs(root));
+  nsCOMPtr<nsIDocShell> rootShell(do_QueryInterface(root));
+  if (!rootShell) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
+  AbortOrientationPromises(rootShell);
+
+  doc->SetOrientationPendingPromise(p);
+
+  nsCOMPtr<nsIRunnable> lockOrientationTask =
+    new LockOrientationTask(this, p, aOrientation, doc,
+                            perm == FULLSCREEN_LOCK_ALLOWED);
+  aRv = NS_DispatchToMainThread(lockOrientationTask);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  return p.forget();
+#endif
+}
+
+bool
+ScreenOrientation::LockDeviceOrientation(ScreenOrientationInternal aOrientation,
+                                         bool aIsFullScreen, ErrorResult& aRv)
+{
+  nsCOMPtr<EventTarget> target = do_QueryInterface(GetOwner()->GetDoc());
+  // We need to register a listener so we learn when we leave full-screen
+  // and when we will have to unlock the screen.
+  // This needs to be done before LockScreenOrientation call to make sure
+  // the locking can be unlocked.
+  if (aIsFullScreen && !target) {
+    return false;
+  }
+
+  if (NS_WARN_IF(!hal::LockScreenOrientation(aOrientation))) {
+    return false;
+  }
+
+  // We are fullscreen and lock has been accepted.
+  if (aIsFullScreen && !mFullScreenListener) {
+    mFullScreenListener = new FullScreenEventListener();
+    aRv = target->AddSystemEventListener(NS_LITERAL_STRING("mozfullscreenchange"),
+                                         mFullScreenListener, /* useCapture = */ true);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+void
+ScreenOrientation::Unlock(ErrorResult& aRv)
+{
+  nsRefPtr<Promise> p = LockInternal(eScreenOrientation_None, aRv);
+}
+
+void
+ScreenOrientation::UnlockDeviceOrientation()
+{
+  hal::UnlockScreenOrientation();
+
+  if (!mFullScreenListener) {
+    return;
+  }
+
+  // Remove event listener in case of fullscreen lock.
+  nsCOMPtr<EventTarget> target = do_QueryInterface(GetOwner()->GetDoc());
+  if (target) {
+    nsresult rv = target->RemoveSystemEventListener(NS_LITERAL_STRING("mozfullscreenchange"),
+                                                    mFullScreenListener, /* useCapture */ true);
+    NS_WARN_IF(NS_FAILED(rv));
+  }
+
+  mFullScreenListener = nullptr;
+}
+
+OrientationType
+ScreenOrientation::DeviceType() const
+{
+  return mType;
+}
+
+uint16_t
+ScreenOrientation::DeviceAngle() const
+{
+  return mAngle;
+}
+
+OrientationType
+ScreenOrientation::GetType(ErrorResult& aRv) const
+{
+  nsIDocument* doc = GetResponsibleDocument();
+  if (!doc) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return OrientationType::Portrait_primary;
+  }
+
+  return doc->CurrentOrientationType();
+}
+
+uint16_t
+ScreenOrientation::GetAngle(ErrorResult& aRv) const
+{
+  nsIDocument* doc = GetResponsibleDocument();
+  if (!doc) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return 0;
+  }
+
+  return doc->CurrentOrientationAngle();
+}
+
+ScreenOrientation::LockPermission
+ScreenOrientation::GetLockOrientationPermission() const
+{
+  nsCOMPtr<nsPIDOMWindow> owner = GetOwner();
+  if (!owner) {
+    return LOCK_DENIED;
+  }
+
+  // Chrome can always lock the screen orientation.
+  nsIDocShell* docShell = owner->GetDocShell();
+  if (docShell && docShell->ItemType() == nsIDocShellTreeItem::typeChrome) {
+    return LOCK_ALLOWED;
+  }
+
+  nsCOMPtr<nsIDocument> doc = owner->GetDoc();
+  if (!doc || doc->Hidden()) {
+    return LOCK_DENIED;
+  }
+
+  // Apps can always lock the screen orientation.
+  if (doc->NodePrincipal()->GetAppStatus() >=
+        nsIPrincipal::APP_STATUS_INSTALLED) {
+    return LOCK_ALLOWED;
+  }
+
+  if (Preferences::GetBool("dom.screenorientation.testing.non_fullscreen_lock_allow",
+                           false)) {
+    return LOCK_ALLOWED;
+  }
+
+  // Other content must be full-screen in order to lock orientation.
+  return doc->MozFullScreen() ? FULLSCREEN_LOCK_ALLOWED : LOCK_DENIED;
+}
+
+nsIDocument*
+ScreenOrientation::GetResponsibleDocument() const
+{
+  nsCOMPtr<nsPIDOMWindow> owner = GetOwner();
+  if (!owner) {
+    return nullptr;
+  }
+
+  return owner->GetDoc();
+}
+
+void
+ScreenOrientation::Notify(const hal::ScreenConfiguration& aConfiguration)
+{
+  nsIDocument* doc = GetResponsibleDocument();
+  if (!doc) {
+    return;
+  }
+
+  ScreenOrientationInternal orientation = aConfiguration.orientation();
+  if (orientation != eScreenOrientation_PortraitPrimary &&
+      orientation != eScreenOrientation_PortraitSecondary &&
+      orientation != eScreenOrientation_LandscapePrimary &&
+      orientation != eScreenOrientation_LandscapeSecondary) {
+    // The platform may notify of some other values from
+    // an orientation lock, but we only care about real
+    // changes to screen orientation which result in one of
+    // the values we care about.
+    return;
+  }
+
+  OrientationType previousOrientation = mType;
+  mAngle = aConfiguration.angle();
+  mType = InternalOrientationToType(orientation);
+
+  nsresult rv;
+  if (mScreen && mType != previousOrientation) {
+    // Use of mozorientationchange is deprecated.
+    rv = mScreen->DispatchTrustedEvent(NS_LITERAL_STRING("mozorientationchange"));
+    NS_WARN_IF(NS_FAILED(rv));
+  }
+
+  if (doc->Hidden() && !mVisibleListener) {
+    mVisibleListener = new VisibleEventListener();
+    rv = doc->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
+                                     mVisibleListener, /* useCapture = */ true);
+    NS_WARN_IF(NS_FAILED(rv));
+    return;
+  }
+
+  if (mType != doc->CurrentOrientationType()) {
+    doc->SetCurrentOrientation(mType, mAngle);
+
+    Promise* pendingPromise = doc->GetOrientationPendingPromise();
+    if (pendingPromise) {
+      pendingPromise->MaybeResolve(JS::UndefinedHandleValue);
+      doc->SetOrientationPendingPromise(nullptr);
+    }
+
+    nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableMethod(this,
+      &ScreenOrientation::DispatchChangeEvent);
+    rv = NS_DispatchToMainThread(runnable);
+    NS_WARN_IF(NS_FAILED(rv));
+  }
+}
+
+void
+ScreenOrientation::DispatchChangeEvent()
+{
+  nsresult rv = DispatchTrustedEvent(NS_LITERAL_STRING("change"));
+  NS_WARN_IF(NS_FAILED(rv));
+}
+
+JSObject*
+ScreenOrientation::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return ScreenOrientationBinding::Wrap(aCx, this, aGivenProto);
+}
+
+NS_IMPL_ISUPPORTS(ScreenOrientation::VisibleEventListener, nsIDOMEventListener)
+
+NS_IMETHODIMP
+ScreenOrientation::VisibleEventListener::HandleEvent(nsIDOMEvent* aEvent)
+{
+  // Document may have become visible, if the page is visible, run the steps
+  // following the "now visible algorithm" as specified.
+  nsCOMPtr<EventTarget> target = aEvent->InternalDOMEvent()->GetCurrentTarget();
+  MOZ_ASSERT(target);
+
+  nsCOMPtr<nsIDocument> doc = do_QueryInterface(target);
+  if (!doc || doc->Hidden()) {
+    return NS_OK;
+  }
+
+  nsGlobalWindow* win = static_cast<nsGlobalWindow*>(doc->GetInnerWindow());
+  if (!win) {
+    return NS_OK;
+  }
+
+  ErrorResult rv;
+  nsScreen* screen = win->GetScreen(rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    return rv.StealNSResult();
+  }
+
+  MOZ_ASSERT(screen);
+  ScreenOrientation* orientation = screen->Orientation();
+  MOZ_ASSERT(orientation);
+
+  rv = target->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
+                                         this, true);
+  if (NS_WARN_IF(rv.Failed())) {
+    return rv.StealNSResult();
+  }
+
+  if (doc->CurrentOrientationType() != orientation->DeviceType()) {
+    doc->SetCurrentOrientation(orientation->DeviceType(), orientation->DeviceAngle());
+
+    Promise* pendingPromise = doc->GetOrientationPendingPromise();
+    if (pendingPromise) {
+      pendingPromise->MaybeResolve(JS::UndefinedHandleValue);
+      doc->SetOrientationPendingPromise(nullptr);
+    }
+
+    nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableMethod(orientation,
+      &ScreenOrientation::DispatchChangeEvent);
+    rv = NS_DispatchToMainThread(runnable);
+    if (NS_WARN_IF(rv.Failed())) {
+      return rv.StealNSResult();
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(ScreenOrientation::FullScreenEventListener, nsIDOMEventListener)
+
+NS_IMETHODIMP
+ScreenOrientation::FullScreenEventListener::HandleEvent(nsIDOMEvent* aEvent)
+{
+#ifdef DEBUG
+  nsAutoString eventType;
+  aEvent->GetType(eventType);
+
+  MOZ_ASSERT(eventType.EqualsLiteral("mozfullscreenchange"));
+#endif
+
+  nsCOMPtr<EventTarget> target = aEvent->InternalDOMEvent()->GetCurrentTarget();
+  MOZ_ASSERT(target);
+
+  nsCOMPtr<nsIDocument> doc = do_QueryInterface(target);
+  MOZ_ASSERT(doc);
+
+  // We have to make sure that the event we got is the event sent when
+  // fullscreen is disabled because we could get one when fullscreen
+  // got enabled if the lock call is done at the same moment.
+  if (doc->MozFullScreen()) {
+    return NS_OK;
+  }
+
+  hal::UnlockScreenOrientation();
+
+  nsresult rv = target->RemoveSystemEventListener(NS_LITERAL_STRING("mozfullscreenchange"),
+                                                  this, true);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
--- a/dom/base/ScreenOrientation.h
+++ b/dom/base/ScreenOrientation.h
@@ -2,28 +2,117 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_ScreenOrientation_h
 #define mozilla_dom_ScreenOrientation_h
 
+#include "mozilla/DOMEventTargetHelper.h"
+#include "mozilla/dom/ScreenOrientationBinding.h"
+#include "mozilla/HalScreenConfiguration.h"
+
+class nsScreen;
+
 namespace mozilla {
 namespace dom {
 
+class Promise;
 // Make sure that any change to ScreenOrientationInternal values are
 // also made in mobile/android/base/GeckoScreenOrientation.java
 typedef uint32_t ScreenOrientationInternal;
 
 static const ScreenOrientationInternal eScreenOrientation_None               = 0;
 static const ScreenOrientationInternal eScreenOrientation_PortraitPrimary    = 1u << 0;
 static const ScreenOrientationInternal eScreenOrientation_PortraitSecondary  = 1u << 1;
 static const ScreenOrientationInternal eScreenOrientation_LandscapePrimary   = 1u << 2;
 static const ScreenOrientationInternal eScreenOrientation_LandscapeSecondary = 1u << 3;
 //eScreenOrientation_Default will use the natural orientation for the deivce,
 //it could be PortraitPrimary or LandscapePrimary depends on display resolution
 static const ScreenOrientationInternal eScreenOrientation_Default            = 1u << 4;
 
+class ScreenOrientation final : public DOMEventTargetHelper,
+                                public mozilla::hal::ScreenConfigurationObserver
+{
+  // nsScreen has deprecated API that shares implementation.
+  friend class ::nsScreen;
+
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ScreenOrientation, mozilla::DOMEventTargetHelper)
+  NS_REALLY_FORWARD_NSIDOMEVENTTARGET(mozilla::DOMEventTargetHelper)
+
+  IMPL_EVENT_HANDLER(change)
+
+  ScreenOrientation(nsPIDOMWindow* aWindow, nsScreen* aScreen);
+
+  already_AddRefed<Promise> Lock(OrientationLockType aOrientation,
+                                 ErrorResult& aRv);
+
+  void Unlock(ErrorResult& aRv);
+
+  // DeviceType and DeviceAngle gets the current type and angle of the device.
+  OrientationType DeviceType() const;
+  uint16_t DeviceAngle() const;
+
+  // GetType and GetAngle gets the type and angle of the responsible document
+  // (as defined in specification).
+  OrientationType GetType(ErrorResult& aRv) const;
+  uint16_t GetAngle(ErrorResult& aRv) const;
+
+  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+  void Notify(const mozilla::hal::ScreenConfiguration& aConfiguration) override;
+
+private:
+  virtual ~ScreenOrientation();
+
+  // Listener to unlock orientation if we leave fullscreen.
+  class FullScreenEventListener;
+
+  // Listener to update document's orienation lock when document becomes
+  // visible.
+  class VisibleEventListener;
+
+  // Task to run step to lock orientation as defined in specification.
+  class LockOrientationTask;
+
+  enum LockPermission {
+    LOCK_DENIED,
+    FULLSCREEN_LOCK_ALLOWED,
+    LOCK_ALLOWED
+  };
+
+  // This method calls into the HAL to lock the device and sets
+  // up listeners for full screen change.
+  bool LockDeviceOrientation(ScreenOrientationInternal aOrientation,
+                             bool aIsFullscreen, ErrorResult& aRv);
+
+  // This method calls in to the HAL to unlock the device and removes
+  // full screen change listener.
+  void UnlockDeviceOrientation();
+
+  // This method performs the same function as |Lock| except it takes
+  // a ScreenOrientationInternal argument instead of an OrientationType.
+  // This method exists in order to share implementation with nsScreen that
+  // uses ScreenOrientationInternal.
+  already_AddRefed<Promise> LockInternal(ScreenOrientationInternal aOrientation,
+                                         ErrorResult& aRv);
+
+  void DispatchChangeEvent();
+
+  LockPermission GetLockOrientationPermission() const;
+
+  // Gets the responsible document as defined in the spec.
+  nsIDocument* GetResponsibleDocument() const;
+
+  nsRefPtr<nsScreen> mScreen;
+  nsRefPtr<FullScreenEventListener> mFullScreenListener;
+  nsRefPtr<VisibleEventListener> mVisibleListener;
+  OrientationType mType;
+  uint16_t mAngle;
+};
+
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ScreenOrientation_h
--- a/dom/base/StructuredCloneHelper.cpp
+++ b/dom/base/StructuredCloneHelper.cpp
@@ -2,21 +2,28 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "StructuredCloneHelper.h"
 
 #include "ImageContainer.h"
+#include "mozilla/AutoRestore.h"
 #include "mozilla/dom/BlobBinding.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/FileList.h"
 #include "mozilla/dom/FileListBinding.h"
 #include "mozilla/dom/ImageBitmap.h"
 #include "mozilla/dom/ImageBitmapBinding.h"
+#include "mozilla/dom/MessagePort.h"
+#include "mozilla/dom/MessagePortBinding.h"
+#include "mozilla/dom/PMessagePort.h"
 #include "mozilla/dom/StructuredCloneTags.h"
+#include "mozilla/dom/ToJSValue.h"
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 
 JSObject*
 StructuredCloneCallbacksRead(JSContext* aCx,
--- a/dom/base/StructuredCloneHelper.h
+++ b/dom/base/StructuredCloneHelper.h
@@ -7,16 +7,17 @@
 #define mozilla_dom_StructuredCloneHelper_h
 
 #include "js/StructuredClone.h"
 #include "nsAutoPtr.h"
 #include "nsISupports.h"
 #include "nsTArray.h"
 
 namespace mozilla {
+class ErrorResult;
 namespace layers {
 class Image;
 }
 
 namespace dom {
 
 class StructuredCloneHelperInternal
 {
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -332,16 +332,17 @@ UNIFIED_SOURCES += [
     'PerformanceMeasure.cpp',
     'PerformanceObserver.cpp',
     'PerformanceObserverEntryList.cpp',
     'PerformanceResourceTiming.cpp',
     'PostMessageEvent.cpp',
     'ProcessGlobal.cpp',
     'ResponsiveImageSelector.cpp',
     'SameProcessMessageQueue.cpp',
+    'ScreenOrientation.cpp',
     'ScriptSettings.cpp',
     'ShadowRoot.cpp',
     'StructuredCloneHelper.cpp',
     'StyleSheetList.cpp',
     'SubtleCrypto.cpp',
     'Text.cpp',
     'TextInputProcessor.cpp',
     'ThirdPartyUtil.cpp',
--- a/dom/base/nsAttrValue.h
+++ b/dom/base/nsAttrValue.h
@@ -20,16 +20,19 @@
 #include "nsMargin.h"
 #include "nsCOMPtr.h"
 #include "SVGAttrValueWrapper.h"
 #include "nsTArrayForwardDeclare.h"
 #include "nsIAtom.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/BindingDeclarations.h"
 
+// Undefine LoadImage to prevent naming conflict with Windows.
+#undef LoadImage
+
 class nsAString;
 class nsIDocument;
 class nsStyledElementNotElementCSSInlineStyle;
 struct MiscContainer;
 
 namespace mozilla {
 namespace css {
 class StyleRule;
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -33,16 +33,17 @@
 #include "mozilla/css/Loader.h"
 #include "mozilla/css/ImageLoader.h"
 #include "nsDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsCOMArray.h"
 #include "nsQueryObject.h"
 #include "nsDOMClassInfo.h"
 #include "mozilla/Services.h"
+#include "nsScreen.h"
 
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/BasicEvents.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStateManager.h"
 #include "nsIDOMNodeFilter.h"
 
 #include "nsIDOMStyleSheet.h"
@@ -1932,16 +1933,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleAttrStyleSheet)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXPathEvaluator)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLayoutHistoryState)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnloadBlocker)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstBaseNodeWithHref)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMImplementation)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageMaps)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOrientationPendingPromise)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginalDocument)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedEncoder)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStateObjectCached)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUndoManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentTimeline)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingAnimationTracker)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection)
@@ -2032,16 +2034,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mUndoManager)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentTimeline)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingAnimationTracker)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mRegistry)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMasterDocument)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOrientationPendingPromise)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mImportManager)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSubImportLinks)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet)
 
   tmp->mParentDocument = nullptr;
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages)
 
@@ -11935,16 +11938,48 @@ nsDocument::IsFullScreenEnabled(bool aCa
   }
   if (!allowed) {
     LogFullScreenDenied(aLogFailure, "FullScreenDeniedIframeNotAllowed", this);
   }
 
   return allowed;
 }
 
+uint16_t
+nsDocument::CurrentOrientationAngle() const
+{
+  return mCurrentOrientationAngle;
+}
+
+OrientationType
+nsDocument::CurrentOrientationType() const
+{
+  return mCurrentOrientationType;
+}
+
+void
+nsDocument::SetCurrentOrientation(mozilla::dom::OrientationType aType,
+                                  uint16_t aAngle)
+{
+  mCurrentOrientationType = aType;
+  mCurrentOrientationAngle = aAngle;
+}
+
+Promise*
+nsDocument::GetOrientationPendingPromise() const
+{
+  return mOrientationPendingPromise;
+}
+
+void
+nsDocument::SetOrientationPendingPromise(Promise* aPromise)
+{
+  mOrientationPendingPromise = aPromise;
+}
+
 static void
 DispatchPointerLockChange(nsIDocument* aTarget)
 {
   if (!aTarget) {
     return;
   }
 
   nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -1274,16 +1274,23 @@ public:
   virtual Element* GetMozFullScreenElement(mozilla::ErrorResult& rv) override;
 
   void RequestPointerLock(Element* aElement) override;
   bool ShouldLockPointer(Element* aElement, Element* aCurrentLock,
                          bool aNoFocusCheck = false);
   bool SetPointerLock(Element* aElement, int aCursorStyle);
   static void UnlockPointer(nsIDocument* aDoc = nullptr);
 
+  void SetCurrentOrientation(mozilla::dom::OrientationType aType,
+                             uint16_t aAngle) override;
+  uint16_t CurrentOrientationAngle() const override;
+  mozilla::dom::OrientationType CurrentOrientationType() const override;
+  void SetOrientationPendingPromise(mozilla::dom::Promise* aPromise) override;
+  mozilla::dom::Promise* GetOrientationPendingPromise() const override;
+
   // This method may fire a DOM event; if it does so it will happen
   // synchronously.
   void UpdateVisibilityState();
   // Posts an event to call UpdateVisibilityState
   virtual void PostVisibilityUpdateEvent() override;
 
   virtual void DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const override;
   // DocAddSizeOfIncludingThis is inherited from nsIDocument.
@@ -1515,16 +1522,18 @@ protected:
   void DispatchPageTransition(mozilla::dom::EventTarget* aDispatchTarget,
                               const nsAString& aType,
                               bool aPersisted);
 
   virtual nsPIDOMWindow *GetWindowInternal() const override;
   virtual nsIScriptGlobalObject* GetScriptHandlingObjectInternal() const override;
   virtual bool InternalAllowXULXBL() override;
 
+  void UpdateScreenOrientation();
+
 #define NS_DOCUMENT_NOTIFY_OBSERVERS(func_, params_)                        \
   NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mObservers, nsIDocumentObserver, \
                                            func_, params_);
 
 #ifdef DEBUG
   void VerifyRootContentState();
 #endif
 
@@ -1662,16 +1671,23 @@ public:
   bool mParserAborted:1;
 
   friend class nsPointerLockPermissionRequest;
   friend class nsCallRequestFullScreen;
   // When set, trying to lock the pointer doesn't require permission from the
   // user.
   bool mAllowRelocking:1;
 
+  // ScreenOrientation "pending promise" as described by
+  // http://www.w3.org/TR/screen-orientation/
+  nsRefPtr<mozilla::dom::Promise> mOrientationPendingPromise;
+
+  uint16_t mCurrentOrientationAngle;
+  mozilla::dom::OrientationType mCurrentOrientationType;
+
   // Whether we're observing the "app-theme-changed" observer service
   // notification.  We need to keep track of this because we might get multiple
   // OnPageShow notifications in a row without an OnPageHide in between, if
   // we're getting document.open()/close() called on us.
   bool mObservingAppThemeChanged:1;
 
   // Keeps track of whether we have a pending
   // 'style-sheet-applicable-state-changed' notification.
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -128,17 +128,19 @@ struct FullscreenRequest;
 class ImportManager;
 class HTMLBodyElement;
 struct LifecycleCallbackArgs;
 class Link;
 class MediaQueryList;
 class GlobalObject;
 class NodeFilter;
 class NodeIterator;
+enum class OrientationType : uint32_t;
 class ProcessingInstruction;
+class Promise;
 class StyleSheetList;
 class SVGDocument;
 class Touch;
 class TouchList;
 class TreeWalker;
 class UndoManager;
 class XPathEvaluator;
 class XPathExpression;
@@ -1197,16 +1199,24 @@ public:
    * Returns whether there is any fullscreen request handled.
    */
   static bool HandlePendingFullscreenRequests(nsIDocument* aDocument);
 
   virtual void RequestPointerLock(Element* aElement) = 0;
 
   static void UnlockPointer(nsIDocument* aDoc = nullptr);
 
+  // ScreenOrientation related APIs
+
+  virtual void SetCurrentOrientation(mozilla::dom::OrientationType aType,
+                                     uint16_t aAngle) = 0;
+  virtual uint16_t CurrentOrientationAngle() const = 0;
+  virtual mozilla::dom::OrientationType CurrentOrientationType() const = 0;
+  virtual void SetOrientationPendingPromise(mozilla::dom::Promise* aPromise) = 0;
+  virtual mozilla::dom::Promise* GetOrientationPendingPromise() const = 0;
 
   //----------------------------------------------------------------------
 
   // Document notification API's
 
   /**
    * Add a new observer of document change notifications. Whenever
    * content is changed, appended, inserted or removed the observers are
--- a/dom/base/nsScreen.cpp
+++ b/dom/base/nsScreen.cpp
@@ -1,15 +1,14 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "mozilla/Hal.h"
 #include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
 #include "mozilla/dom/ScreenBinding.h"
 #include "nsScreen.h"
 #include "nsIDocument.h"
 #include "nsIDocShell.h"
 #include "nsIDocument.h"
 #include "nsPresContext.h"
 #include "nsCOMPtr.h"
@@ -31,46 +30,42 @@ nsScreen::Create(nsPIDOMWindow* aWindow)
     return nullptr;
   }
 
   nsCOMPtr<nsIScriptGlobalObject> sgo =
     do_QueryInterface(static_cast<nsPIDOMWindow*>(aWindow));
   NS_ENSURE_TRUE(sgo, nullptr);
 
   nsRefPtr<nsScreen> screen = new nsScreen(aWindow);
-
-  hal::RegisterScreenConfigurationObserver(screen);
-  hal::ScreenConfiguration config;
-  hal::GetCurrentScreenConfiguration(&config);
-  screen->mOrientationInternal = config.orientation();
-
   return screen.forget();
 }
 
 nsScreen::nsScreen(nsPIDOMWindow* aWindow)
   : DOMEventTargetHelper(aWindow)
-  , mEventListener(nullptr)
+  , mScreenOrientation(new ScreenOrientation(aWindow, this))
 {
 }
 
 nsScreen::~nsScreen()
 {
-  MOZ_ASSERT(!mEventListener);
-  hal::UnregisterScreenConfigurationObserver(this);
 }
 
 
 // QueryInterface implementation for nsScreen
-NS_INTERFACE_MAP_BEGIN(nsScreen)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsScreen)
   NS_INTERFACE_MAP_ENTRY(nsIDOMScreen)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(nsScreen, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(nsScreen, DOMEventTargetHelper)
 
+NS_IMPL_CYCLE_COLLECTION_INHERITED(nsScreen,
+                                   DOMEventTargetHelper,
+                                   mScreenOrientation)
+
 int32_t
 nsScreen::GetPixelDepth(ErrorResult& aRv)
 {
   // Return 24 to prevent fingerprinting.
   if (ShouldResistFingerprinting()) {
     return 24;
   }
 
@@ -157,97 +152,56 @@ nsScreen::GetAvailRect(nsRect& aRect)
   aRect.x = nsPresContext::AppUnitsToIntCSSPixels(aRect.x);
   aRect.y = nsPresContext::AppUnitsToIntCSSPixels(aRect.y);
   aRect.height = nsPresContext::AppUnitsToIntCSSPixels(aRect.height);
   aRect.width = nsPresContext::AppUnitsToIntCSSPixels(aRect.width);
 
   return NS_OK;
 }
 
-void
-nsScreen::Notify(const hal::ScreenConfiguration& aConfiguration)
+mozilla::dom::ScreenOrientation*
+nsScreen::Orientation() const
 {
-  ScreenOrientationInternal previousOrientation = mOrientationInternal;
-  mOrientationInternal = aConfiguration.orientation();
-
-  NS_ASSERTION(mOrientationInternal == eScreenOrientation_PortraitPrimary ||
-               mOrientationInternal == eScreenOrientation_PortraitSecondary ||
-               mOrientationInternal == eScreenOrientation_LandscapePrimary ||
-               mOrientationInternal == eScreenOrientation_LandscapeSecondary,
-               "Invalid orientation value passed to notify method!");
-
-  if (mOrientationInternal != previousOrientation) {
-    DispatchTrustedEvent(NS_LITERAL_STRING("mozorientationchange"));
-  }
+  return mScreenOrientation;
 }
 
 void
-nsScreen::GetMozOrientation(nsString& aOrientation)
+nsScreen::GetMozOrientation(nsString& aOrientation) const
 {
   if (ShouldResistFingerprinting()) {
     aOrientation.AssignLiteral("landscape-primary");
   } else {
-    switch (mOrientationInternal) {
-    case eScreenOrientation_PortraitPrimary:
+    switch (mScreenOrientation->DeviceType()) {
+    case OrientationType::Portrait_primary:
       aOrientation.AssignLiteral("portrait-primary");
       break;
-    case eScreenOrientation_PortraitSecondary:
+    case OrientationType::Portrait_secondary:
       aOrientation.AssignLiteral("portrait-secondary");
       break;
-    case eScreenOrientation_LandscapePrimary:
+    case OrientationType::Landscape_primary:
       aOrientation.AssignLiteral("landscape-primary");
       break;
-    case eScreenOrientation_LandscapeSecondary:
+    case OrientationType::Landscape_secondary:
       aOrientation.AssignLiteral("landscape-secondary");
       break;
-    case eScreenOrientation_None:
     default:
-      MOZ_CRASH("Unacceptable mOrientationInternal value");
+      MOZ_CRASH("Unacceptable screen orientation type.");
     }
   }
 }
 
 NS_IMETHODIMP
 nsScreen::GetSlowMozOrientation(nsAString& aOrientation)
 {
   nsString orientation;
   GetMozOrientation(orientation);
   aOrientation = orientation;
   return NS_OK;
 }
 
-nsScreen::LockPermission
-nsScreen::GetLockOrientationPermission() const
-{
-  nsCOMPtr<nsPIDOMWindow> owner = GetOwner();
-  if (!owner) {
-    return LOCK_DENIED;
-  }
-
-  // Chrome can always lock the screen orientation.
-  nsIDocShell* docShell = owner->GetDocShell();
-  if (docShell && docShell->ItemType() == nsIDocShellTreeItem::typeChrome) {
-    return LOCK_ALLOWED;
-  }
-
-  nsCOMPtr<nsIDocument> doc = owner->GetDoc();
-  if (!doc || doc->Hidden()) {
-    return LOCK_DENIED;
-  }
-
-  // Apps can always lock the screen orientation.
-  if (doc->NodePrincipal()->GetAppStatus() >=
-        nsIPrincipal::APP_STATUS_INSTALLED) {
-    return LOCK_ALLOWED;
-  }
-
-  // Other content must be full-screen in order to lock orientation.
-  return doc->MozFullScreen() ? FULLSCREEN_LOCK_ALLOWED : LOCK_DENIED;
-}
-
 bool
 nsScreen::MozLockOrientation(const nsAString& aOrientation, ErrorResult& aRv)
 {
   nsString orientation(aOrientation);
   Sequence<nsString> orientations;
   if (!orientations.AppendElement(orientation, fallible)) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return false;
@@ -282,68 +236,34 @@ nsScreen::MozLockOrientation(const Seque
       orientation |= eScreenOrientation_Default;
     } else {
       // If we don't recognize the token, we should just return 'false'
       // without throwing.
       return false;
     }
   }
 
-  switch (GetLockOrientationPermission()) {
-    case LOCK_DENIED:
+  switch (mScreenOrientation->GetLockOrientationPermission()) {
+    case ScreenOrientation::LOCK_DENIED:
       return false;
-    case LOCK_ALLOWED:
-      return hal::LockScreenOrientation(orientation);
-    case FULLSCREEN_LOCK_ALLOWED: {
-      // We need to register a listener so we learn when we leave full-screen
-      // and when we will have to unlock the screen.
-      // This needs to be done before LockScreenOrientation call to make sure
-      // the locking can be unlocked.
-      nsCOMPtr<EventTarget> target = do_QueryInterface(GetOwner()->GetDoc());
-      if (!target) {
-        return false;
-      }
-
-      if (!hal::LockScreenOrientation(orientation)) {
-        return false;
-      }
-
-      // We are fullscreen and lock has been accepted.
-      if (!mEventListener) {
-        mEventListener = new FullScreenEventListener();
-      }
-
-      aRv = target->AddSystemEventListener(NS_LITERAL_STRING("mozfullscreenchange"),
-                                           mEventListener, /* useCapture = */ true);
-      return true;
-    }
+    case ScreenOrientation::LOCK_ALLOWED:
+      return mScreenOrientation->LockDeviceOrientation(orientation, false, aRv);
+    case ScreenOrientation::FULLSCREEN_LOCK_ALLOWED:
+      return mScreenOrientation->LockDeviceOrientation(orientation, true, aRv);
   }
 
   // This is only for compilers that don't understand that the previous switch
   // will always return.
   MOZ_CRASH("unexpected lock orientation permission value");
 }
 
 void
 nsScreen::MozUnlockOrientation()
 {
-  hal::UnlockScreenOrientation();
-
-  if (!mEventListener) {
-    return;
-  }
-
-  // Remove event listener in case of fullscreen lock.
-  nsCOMPtr<EventTarget> target = do_QueryInterface(GetOwner()->GetDoc());
-  if (target) {
-    target->RemoveSystemEventListener(NS_LITERAL_STRING("mozfullscreenchange"),
-                                      mEventListener, /* useCapture */ true);
-  }
-
-  mEventListener = nullptr;
+  mScreenOrientation->UnlockDeviceOrientation();
 }
 
 bool
 nsScreen::IsDeviceSizePageSize()
 {
   nsPIDOMWindow* owner = GetOwner();
   if (owner) {
     nsIDocShell* docShell = owner->GetDocShell();
@@ -356,49 +276,16 @@ nsScreen::IsDeviceSizePageSize()
 
 /* virtual */
 JSObject*
 nsScreen::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return ScreenBinding::Wrap(aCx, this, aGivenProto);
 }
 
-NS_IMPL_ISUPPORTS(nsScreen::FullScreenEventListener, nsIDOMEventListener)
-
-NS_IMETHODIMP
-nsScreen::FullScreenEventListener::HandleEvent(nsIDOMEvent* aEvent)
-{
-#ifdef DEBUG
-  nsAutoString eventType;
-  aEvent->GetType(eventType);
-
-  MOZ_ASSERT(eventType.EqualsLiteral("mozfullscreenchange"));
-#endif
-
-  nsCOMPtr<EventTarget> target = aEvent->InternalDOMEvent()->GetCurrentTarget();
-  MOZ_ASSERT(target);
-
-  nsCOMPtr<nsIDocument> doc = do_QueryInterface(target);
-  MOZ_ASSERT(doc);
-
-  // We have to make sure that the event we got is the event sent when
-  // fullscreen is disabled because we could get one when fullscreen
-  // got enabled if the lock call is done at the same moment.
-  if (doc->MozFullScreen()) {
-    return NS_OK;
-  }
-
-  target->RemoveSystemEventListener(NS_LITERAL_STRING("mozfullscreenchange"),
-                                    this, true);
-
-  hal::UnlockScreenOrientation();
-
-  return NS_OK;
-}
-
 nsresult
 nsScreen::GetWindowInnerRect(nsRect& aRect)
 {
   aRect.x = 0;
   aRect.y = 0;
   nsCOMPtr<nsIDOMWindow> win = GetOwner();
   if (!win) {
     return NS_ERROR_FAILURE;
--- a/dom/base/nsScreen.h
+++ b/dom/base/nsScreen.h
@@ -5,34 +5,33 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef nsScreen_h___
 #define nsScreen_h___
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/ScreenOrientation.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/ErrorResult.h"
-#include "mozilla/HalScreenConfiguration.h"
 #include "nsIDOMScreen.h"
 #include "nsCOMPtr.h"
 #include "nsRect.h"
 
 class nsDeviceContext;
 
 // Script "screen" object
 class nsScreen : public mozilla::DOMEventTargetHelper
                , public nsIDOMScreen
-               , public mozilla::hal::ScreenConfigurationObserver
 {
   typedef mozilla::ErrorResult ErrorResult;
 public:
   static already_AddRefed<nsScreen> Create(nsPIDOMWindow* aWindow);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIDOMSCREEN
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsScreen, mozilla::DOMEventTargetHelper)
   NS_REALLY_FORWARD_NSIDOMEVENTTARGET(mozilla::DOMEventTargetHelper)
 
   nsPIDOMWindow* GetParentObject() const
   {
     return GetOwner();
   }
 
   int32_t GetTop(ErrorResult& aRv)
@@ -110,58 +109,39 @@ public:
 
   int32_t GetAvailHeight(ErrorResult& aRv)
   {
     nsRect rect;
     aRv = GetAvailRect(rect);
     return rect.height;
   }
 
-  void GetMozOrientation(nsString& aOrientation);
+  // Deprecated
+  void GetMozOrientation(nsString& aOrientation) const;
 
   IMPL_EVENT_HANDLER(mozorientationchange)
 
   bool MozLockOrientation(const nsAString& aOrientation, ErrorResult& aRv);
   bool MozLockOrientation(const mozilla::dom::Sequence<nsString>& aOrientations, ErrorResult& aRv);
   void MozUnlockOrientation();
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
-  void Notify(const mozilla::hal::ScreenConfiguration& aConfiguration) override;
+  mozilla::dom::ScreenOrientation* Orientation() const;
 
 protected:
   nsDeviceContext* GetDeviceContext();
   nsresult GetRect(nsRect& aRect);
   nsresult GetAvailRect(nsRect& aRect);
   nsresult GetWindowInnerRect(nsRect& aRect);
 
-  mozilla::dom::ScreenOrientationInternal mOrientationInternal;
-
 private:
-  class FullScreenEventListener final : public nsIDOMEventListener
-  {
-    ~FullScreenEventListener() {}
-  public:
-    FullScreenEventListener() {}
-
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIDOMEVENTLISTENER
-  };
-
   explicit nsScreen(nsPIDOMWindow* aWindow);
   virtual ~nsScreen();
 
-  enum LockPermission {
-    LOCK_DENIED,
-    FULLSCREEN_LOCK_ALLOWED,
-    LOCK_ALLOWED
-  };
-
-  LockPermission GetLockOrientationPermission() const;
-
   bool IsDeviceSizePageSize();
 
   bool ShouldResistFingerprinting() const;
 
-  nsRefPtr<FullScreenEventListener> mEventListener;
+  nsRefPtr<mozilla::dom::ScreenOrientation> mScreenOrientation;
 };
 
 #endif /* nsScreen_h___ */
--- a/dom/canvas/ImageBitmap.h
+++ b/dom/canvas/ImageBitmap.h
@@ -13,16 +13,18 @@
 #include "mozilla/gfx/Rect.h"
 #include "mozilla/Maybe.h"
 #include "nsCycleCollectionParticipant.h"
 
 struct JSContext;
 struct JSStructuredCloneReader;
 struct JSStructuredCloneWriter;
 
+class nsIGlobalObject;
+
 namespace mozilla {
 
 class ErrorResult;
 
 namespace gfx {
 class SourceSurface;
 }
 
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -959,16 +959,18 @@ var interfaceNamesInGlobalScope =
     "RTCRtpReceiver",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "RTCRtpSender",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "RTCStatsReport",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Screen",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    "ScreenOrientation",
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "ScriptProcessorNode",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ScrollAreaEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Selection",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "ServiceWorker", b2g: false, android: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/webidl/Screen.webidl
+++ b/dom/webidl/Screen.webidl
@@ -24,30 +24,38 @@ interface Screen : EventTarget {
   [Throws]
   readonly attribute long left;
   [Throws]
   readonly attribute long availTop;
   [Throws]
   readonly attribute long availLeft;
 
   /**
+   * DEPRECATED, use ScreenOrientation API instead.
    * Returns the current screen orientation.
    * Can be: landscape-primary, landscape-secondary,
    *         portrait-primary or portrait-secondary.
    */
   readonly attribute DOMString mozOrientation;
 
   attribute EventHandler onmozorientationchange;
 
   /**
+   * DEPRECATED, use ScreenOrientation API instead.
    * Lock screen orientation to the specified type.
    */
   [Throws, UnsafeInPrerendering]
   boolean mozLockOrientation(DOMString orientation);
   [Throws, UnsafeInPrerendering]
   boolean mozLockOrientation(sequence<DOMString> orientation);
 
   /**
+   * DEPRECATED, use ScreenOrientation API instead.
    * Unlock the screen orientation.
    */
   [UnsafeInPrerendering]
   void mozUnlockOrientation();
 };
+
+// https://w3c.github.io/screen-orientation
+partial interface Screen {
+  readonly attribute ScreenOrientation orientation;
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/ScreenOrientation.webidl
@@ -0,0 +1,42 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * https://w3c.github.io/screen-orientation
+ *
+ * Copyright © 2014 W3C® (MIT, ERCIM, Keio, Beihang), All Rights
+ * Reserved. W3C liability, trademark and document use rules apply.
+ */
+
+enum OrientationType {
+  "portrait-primary",
+  "portrait-secondary",
+  "landscape-primary",
+  "landscape-secondary"
+};
+
+enum OrientationLockType {
+  "any",
+  "natural",
+  "landscape",
+  "portrait",
+  "portrait-primary",
+  "portrait-secondary",
+  "landscape-primary",
+  "landscape-secondary"
+};
+
+[UnsafeInPrerendering]
+interface ScreenOrientation : EventTarget {
+  [Throws]
+  Promise<void> lock(OrientationLockType orientation);
+  [Throws]
+  void unlock();
+  [Throws]
+  readonly attribute OrientationType type;
+  [Throws]
+  readonly attribute unsigned short angle;
+  attribute EventHandler onchange;
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -382,16 +382,17 @@ WEBIDL_FILES = [
     'RequestSyncManager.webidl',
     'RequestSyncScheduler.webidl',
     'ResourceStats.webidl',
     'ResourceStatsManager.webidl',
     'Response.webidl',
     'RGBColor.webidl',
     'RTCStatsReport.webidl',
     'Screen.webidl',
+    'ScreenOrientation.webidl',
     'ScriptProcessorNode.webidl',
     'ScrollAreaEvent.webidl',
     'ScrollBoxObject.webidl',
     'Selection.webidl',
     'ServiceWorker.webidl',
     'ServiceWorkerContainer.webidl',
     'ServiceWorkerGlobalScope.webidl',
     'SettingChangeNotification.webidl',
--- a/testing/web-platform/meta/screen-orientation/lock-bad-argument.html.ini
+++ b/testing/web-platform/meta/screen-orientation/lock-bad-argument.html.ini
@@ -1,6 +1,5 @@
 [lock-bad-argument.html]
   type: testharness
-  expected: ERROR
   [Test that screen.orientation.lock() throws when the input isn't valid.]
-    expected: NOTRUN
+    expected: FAIL
 
--- a/testing/web-platform/meta/screen-orientation/lock-basic.html.ini
+++ b/testing/web-platform/meta/screen-orientation/lock-basic.html.ini
@@ -1,8 +1,6 @@
 [lock-basic.html]
   type: testharness
-  [Test that screen.orientation.unlock() doesn't throw when there is no lock]
-    expected: FAIL
+  expected: TIMEOUT
+  [Test that screen.orientation.lock returns a pending promise.]
+    expected: TIMEOUT
 
-  [Test that screen.orientation.lock returns a pending promise.]
-    expected: FAIL
-
--- a/testing/web-platform/meta/screen-orientation/lock-sandboxed-iframe.html.ini
+++ b/testing/web-platform/meta/screen-orientation/lock-sandboxed-iframe.html.ini
@@ -1,9 +1,9 @@
 [lock-sandboxed-iframe.html]
   type: testharness
   expected: TIMEOUT
   [Test without 'allow-orientation-lock' sandboxing directive]
-    expected: NOTRUN
+    expected: FAIL
 
   [Test with 'allow-orientation-lock' sandboxing directive]
     expected: NOTRUN
 
--- a/testing/web-platform/meta/screen-orientation/onchange-event-subframe.html.ini
+++ b/testing/web-platform/meta/screen-orientation/onchange-event-subframe.html.ini
@@ -1,6 +1,6 @@
 [onchange-event-subframe.html]
   type: testharness
-  expected: ERROR
+  expected: TIMEOUT
   [Test subframes receive orientation change events]
     expected: NOTRUN
 
--- a/testing/web-platform/meta/screen-orientation/onchange-event.html.ini
+++ b/testing/web-platform/meta/screen-orientation/onchange-event.html.ini
@@ -1,9 +1,9 @@
 [onchange-event.html]
   type: testharness
-  expected: ERROR
+  expected: TIMEOUT
   [Test that orientationchange event is fired when the orientation changes.]
     expected: NOTRUN
 
   [Test that orientationchange event is not fired when the orientation does not change.]
     expected: NOTRUN
 
deleted file mode 100644
--- a/testing/web-platform/meta/screen-orientation/orientation-api.html.ini
+++ /dev/null
@@ -1,11 +0,0 @@
-[orientation-api.html]
-  type: testharness
-  [Test that the Screen Orientation API is present.]
-    expected: FAIL
-
-  [Test Screen Orientation API property types.]
-    expected: FAIL
-
-  [Test that screen.orientation is an EventTarget.]
-    expected: FAIL
-
--- a/testing/web-platform/meta/screen-orientation/orientation-reading.html.ini
+++ b/testing/web-platform/meta/screen-orientation/orientation-reading.html.ini
@@ -1,14 +1,8 @@
 [orientation-reading.html]
   type: testharness
-  [Test screen.orientation properties]
-    expected: FAIL
-
   [Test screen.orientation default values.]
     expected: FAIL
 
-  [Test that screen.orientation properties are not writable]
-    expected: FAIL
-
   [Test that screen.orientation values change if the orientation changes]
     expected: FAIL