Bug 1281949 - When resisting fingerprinting, spoof screen.orientation
authorArthur Edelstein <arthuredelstein@gmail.com>
Thu, 23 Jun 2016 15:36:36 -0700
changeset 787086 4c706bce3830e53c8704dc41041678ddf04772ad
parent 786716 d1102663db10b3d4b9358f3cf4e16b7c56902352
child 787087 e7bbf2c3b87b50479351b3e531c70c84401c4a5d
push id130964
push userarthuredelstein@gmail.com
push dateThu, 23 Jun 2016 22:37:02 +0000
treeherdertry@e7bbf2c3b87b [default view] [failures only]
bugs1281949, 18958
milestone50.0a1
Bug 1281949 - When resisting fingerprinting, spoof screen.orientation When privacy.resistFingerprinting is enabled, make sure that screen.orientation.angle -> 0 and screen.orientation.type -> "landscape-primary" Also refactors screen.mozOrientation. Based on https://torproject.org/18958
dom/base/ScreenOrientation.cpp
dom/base/ScreenOrientation.h
dom/base/nsGlobalWindow.cpp
dom/base/nsScreen.cpp
dom/base/test/chrome/bug418986-1.js
--- a/dom/base/ScreenOrientation.cpp
+++ b/dom/base/ScreenOrientation.cpp
@@ -10,16 +10,17 @@
 #include "nsSandboxFlags.h"
 #include "nsScreen.h"
 
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/Hal.h"
 #include "mozilla/Preferences.h"
 
 #include "mozilla/dom/Promise.h"
+#include "nsContentUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(ScreenOrientation,
                                    DOMEventTargetHelper,
                                    mScreen);
 
@@ -404,40 +405,49 @@ ScreenOrientation::UnlockDeviceOrientati
   }
 
   mFullScreenListener = nullptr;
 }
 
 OrientationType
 ScreenOrientation::DeviceType() const
 {
-  return mType;
+  return ShouldResistFingerprinting() ? OrientationType::Landscape_primary
+                                      : mType;
 }
 
 uint16_t
 ScreenOrientation::DeviceAngle() const
 {
-  return mAngle;
+  return ShouldResistFingerprinting() ? 0 : mAngle;
 }
 
 OrientationType
 ScreenOrientation::GetType(ErrorResult& aRv) const
 {
+  if (ShouldResistFingerprinting()) {
+    return OrientationType::Landscape_primary;
+  }
+
   nsIDocument* doc = GetResponsibleDocument();
   if (!doc) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return OrientationType::Portrait_primary;
   }
 
   return doc->CurrentOrientationType();
 }
 
 uint16_t
 ScreenOrientation::GetAngle(ErrorResult& aRv) const
 {
+  if (ShouldResistFingerprinting()) {
+    return 0;
+  }
+
   nsIDocument* doc = GetResponsibleDocument();
   if (!doc) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return 0;
   }
 
   return doc->CurrentOrientationAngle();
 }
@@ -490,16 +500,20 @@ ScreenOrientation::GetResponsibleDocumen
   }
 
   return owner->GetDoc();
 }
 
 void
 ScreenOrientation::Notify(const hal::ScreenConfiguration& aConfiguration)
 {
+  if (ShouldResistFingerprinting()) {
+    return;
+  }
+
   nsIDocument* doc = GetResponsibleDocument();
   if (!doc) {
     return;
   }
 
   ScreenOrientationInternal orientation = aConfiguration.orientation();
   if (orientation != eScreenOrientation_PortraitPrimary &&
       orientation != eScreenOrientation_PortraitSecondary &&
@@ -566,16 +580,27 @@ ScreenOrientation::DispatchChangeEvent()
 }
 
 JSObject*
 ScreenOrientation::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return ScreenOrientationBinding::Wrap(aCx, this, aGivenProto);
 }
 
+bool
+ScreenOrientation::ShouldResistFingerprinting() const
+{
+  bool resist = false;
+  nsCOMPtr<nsPIDOMWindow> owner = GetOwner();
+  if (owner) {
+    resist = nsContentUtils::ShouldResistFingerprinting(owner->GetDocShell());
+  }
+  return resist;
+}
+
 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();
--- a/dom/base/ScreenOrientation.h
+++ b/dom/base/ScreenOrientation.h
@@ -97,16 +97,18 @@ private:
   // 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();
 
+  bool ShouldResistFingerprinting() const;
+
   LockPermission GetLockOrientationPermission(bool aCheckSandbox) const;
 
   // Gets the responsible document as defined in the spec.
   nsIDocument* GetResponsibleDocument() const;
 
   RefPtr<nsScreen> mScreen;
   RefPtr<FullScreenEventListener> mFullScreenListener;
   RefPtr<VisibleEventListener> mVisibleListener;
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -13238,18 +13238,18 @@ nsGlobalWindow::DisableDeviceSensor(uint
   }
 }
 
 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
 void
 nsGlobalWindow::EnableOrientationChangeListener()
 {
   MOZ_ASSERT(IsInnerWindow());
-
-  if (!mOrientationChangeObserver) {
+  if (!nsContentUtils::ShouldResistFingerprinting(mDocShell)
+      && !mOrientationChangeObserver) {
     mOrientationChangeObserver =
       new WindowOrientationObserver(this);
   }
 }
 
 void
 nsGlobalWindow::DisableOrientationChangeListener()
 {
@@ -14082,17 +14082,18 @@ nsGlobalWindow::IsModalContentWindow(JSC
 {
   return xpc::WindowOrNull(aGlobal)->IsModalContentWindow();
 }
 
 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
 int16_t
 nsGlobalWindow::Orientation() const
 {
-  return WindowOrientationObserver::OrientationAngle();
+  return nsContentUtils::ShouldResistFingerprinting(mDocShell) ?
+           0 : WindowOrientationObserver::OrientationAngle();
 }
 #endif
 
 Console*
 nsGlobalWindow::GetConsole(ErrorResult& aRv)
 {
   MOZ_RELEASE_ASSERT(IsInnerWindow());
 
--- a/dom/base/nsScreen.cpp
+++ b/dom/base/nsScreen.cpp
@@ -188,35 +188,31 @@ mozilla::dom::ScreenOrientation*
 nsScreen::Orientation() const
 {
   return mScreenOrientation;
 }
 
 void
 nsScreen::GetMozOrientation(nsString& aOrientation) const
 {
-  if (ShouldResistFingerprinting()) {
+  switch (mScreenOrientation->DeviceType()) {
+  case OrientationType::Portrait_primary:
+    aOrientation.AssignLiteral("portrait-primary");
+    break;
+  case OrientationType::Portrait_secondary:
+    aOrientation.AssignLiteral("portrait-secondary");
+    break;
+  case OrientationType::Landscape_primary:
     aOrientation.AssignLiteral("landscape-primary");
-  } else {
-    switch (mScreenOrientation->DeviceType()) {
-    case OrientationType::Portrait_primary:
-      aOrientation.AssignLiteral("portrait-primary");
-      break;
-    case OrientationType::Portrait_secondary:
-      aOrientation.AssignLiteral("portrait-secondary");
-      break;
-    case OrientationType::Landscape_primary:
-      aOrientation.AssignLiteral("landscape-primary");
-      break;
-    case OrientationType::Landscape_secondary:
-      aOrientation.AssignLiteral("landscape-secondary");
-      break;
-    default:
-      MOZ_CRASH("Unacceptable screen orientation type.");
-    }
+    break;
+  case OrientationType::Landscape_secondary:
+    aOrientation.AssignLiteral("landscape-secondary");
+    break;
+  default:
+    MOZ_CRASH("Unacceptable screen orientation type.");
   }
 }
 
 NS_IMETHODIMP
 nsScreen::GetSlowMozOrientation(nsAString& aOrientation)
 {
   nsString orientation;
   GetMozOrientation(orientation);
@@ -258,16 +254,19 @@ nsScreen::MozLockOrientation(const nsASt
   }
   return MozLockOrientation(orientations, aRv);
 }
 
 bool
 nsScreen::MozLockOrientation(const Sequence<nsString>& aOrientations,
                              ErrorResult& aRv)
 {
+  if (ShouldResistFingerprinting()) {
+    return false;
+  }
   ScreenOrientationInternal orientation = eScreenOrientation_None;
 
   for (uint32_t i = 0; i < aOrientations.Length(); ++i) {
     const nsString& item = aOrientations[i];
 
     if (item.EqualsLiteral("portrait")) {
       orientation |= eScreenOrientation_PortraitPrimary |
                      eScreenOrientation_PortraitSecondary;
@@ -305,16 +304,19 @@ nsScreen::MozLockOrientation(const Seque
   // 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()
 {
+  if (ShouldResistFingerprinting()) {
+    return;
+  }
   UpdateDocShellOrientationLock(GetOwner(), eScreenOrientation_None);
   mScreenOrientation->UnlockDeviceOrientation();
 }
 
 bool
 nsScreen::IsDeviceSizePageSize()
 {
   if (nsPIDOMWindowInner* owner = GetOwner()) {
--- a/dom/base/test/chrome/bug418986-1.js
+++ b/dom/base/test/chrome/bug418986-1.js
@@ -17,16 +17,18 @@ var test = function (isContent) {
     ["screen.availWidth", "innerWidth"],
     ["screen.availHeight", "innerHeight"],
     ["screen.left", 0],
     ["screen.top", 0],
     ["screen.availLeft", 0],
     ["screen.availTop", 0],
     ["screen.width", "innerWidth"],
     ["screen.height", "innerHeight"],
+    ["screen.orientation.type", "'landscape-primary'"],
+    ["screen.orientation.angle", 0],
     ["screen.mozOrientation", "'landscape-primary'"],
     ["devicePixelRatio", 1]
   ];
 
   // checkPair: tests if members of pair [a, b] are equal when evaluated.
   let checkPair = function (a, b) {
     is(eval(a), eval(b), a + " should be equal to " + b);
   };