Commit 95d3d105 authored by Tim Huang's avatar Tim Huang Committed by Georg Koppen
Browse files

Bug 1330882 - Part 4: Making the window.open() can only open rounded windows...

Bug 1330882 - Part 4: Making the window.open() can only open rounded windows and the inner window will be automatically rounded after setting size through innerWidth/Height and outerWidth/Height when fingerprinting resistance is enabled. r=smaug

    This patch makes the size of inner windows will be automatically rounded for
    either window.open() with window features or setting window size through
    innerWidth/Height and outerWidth/Height when fingerprinting resistance is
    enabled. If the given value is greater the maximum available rounded size, then
    it will be set to the maximum value. Otherwise, the size will be set to the
    nearest upper 200x100.

    This patch also adds one helper function in nsContentUtils for calculating the
    rounded window dimensions.

    MozReview-Commit-ID: J2r3951vuNN

    --HG--
    extra : rebase_source : a44b19bdf2ce7e90fc831ddc2b85a86d594cb0c3
parent bf701147
Loading
Loading
Loading
Loading
+82 −0
Original line number Diff line number Diff line
@@ -289,6 +289,9 @@ bool nsContentUtils::sPrivacyResistFingerprinting = false;
bool nsContentUtils::sSendPerformanceTimingNotifications = false;
bool nsContentUtils::sUseActivityCursor = false;

int32_t nsContentUtils::sPrivacyMaxInnerWidth = 1000;
int32_t nsContentUtils::sPrivacyMaxInnerHeight = 1000;

uint32_t nsContentUtils::sHandlingInputTimeout = 1000;

uint32_t nsContentUtils::sCookiesLifetimePolicy = nsICookieService::ACCEPT_NORMALLY;
@@ -589,6 +592,14 @@ nsContentUtils::Init()
  Preferences::AddBoolVarCache(&sPrivacyResistFingerprinting,
                               "privacy.resistFingerprinting", false);

  Preferences::AddIntVarCache(&sPrivacyMaxInnerWidth,
                              "privacy.window.maxInnerWidth",
                              1000);

  Preferences::AddIntVarCache(&sPrivacyMaxInnerHeight,
                              "privacy.window.maxInnerHeight",
                              1000);

  Preferences::AddUintVarCache(&sHandlingInputTimeout,
                               "dom.event.handling-user-input-time-limit",
                               1000);
@@ -2133,6 +2144,77 @@ nsContentUtils::ShouldResistFingerprinting(nsIDocShell* aDocShell)
  return !isChrome && sPrivacyResistFingerprinting;
}

/* static */
void
nsContentUtils::CalcRoundedWindowSizeForResistingFingerprinting(int32_t  aChromeWidth,
                                                                int32_t  aChromeHeight,
                                                                int32_t  aScreenWidth,
                                                                int32_t  aScreenHeight,
                                                                int32_t  aInputWidth,
                                                                int32_t  aInputHeight,
                                                                bool     aSetOuterWidth,
                                                                bool     aSetOuterHeight,
                                                                int32_t* aOutputWidth,
                                                                int32_t* aOutputHeight)
{
  MOZ_ASSERT(aOutputWidth);
  MOZ_ASSERT(aOutputHeight);

  int32_t availContentWidth  = 0;
  int32_t availContentHeight = 0;

  availContentWidth = std::min(sPrivacyMaxInnerWidth,
                               aScreenWidth - aChromeWidth);
#ifdef MOZ_WIDGET_GTK
  // In the GTK window, it will not report outside system decorations
  // when we get available window size, see Bug 581863. So, we leave a
  // 40 pixels space for them when calculating the available content
  // height. It is not necessary for the width since the content width
  // is usually pretty much the same as the chrome width.
  availContentHeight = std::min(sPrivacyMaxInnerHeight,
                                (-40 + aScreenHeight) - aChromeHeight);
#else
  availContentHeight = std::min(sPrivacyMaxInnerHeight,
                                aScreenHeight - aChromeHeight);
#endif

  // Ideally, we'd like to round window size to 1000x1000, but the
  // screen space could be too small to accommodate this size in some
  // cases. If it happens, we would round the window size to the nearest
  // 200x100.
  availContentWidth = availContentWidth - (availContentWidth % 200);
  availContentHeight = availContentHeight - (availContentHeight % 100);

  // If aIsOuter is true, we are setting the outer window. So we
  // have to consider the chrome UI.
  int32_t chromeOffsetWidth = aSetOuterWidth ? aChromeWidth : 0;
  int32_t chromeOffsetHeight = aSetOuterHeight ? aChromeHeight : 0;
  int32_t resultWidth = 0, resultHeight = 0;

  // if the original size is greater than the maximum available size, we set
  // it to the maximum size. And if the original value is less than the
  // minimum rounded size, we set it to the minimum 200x100.
  if (aInputWidth > (availContentWidth + chromeOffsetWidth)) {
    resultWidth = availContentWidth + chromeOffsetWidth;
  } else if (aInputWidth < (200 + chromeOffsetWidth)) {
    resultWidth = 200 + chromeOffsetWidth;
  } else {
    // Otherwise, we round the window to the nearest upper rounded 200x100.
    resultWidth = NSToIntCeil((aInputWidth - chromeOffsetWidth) / 200.0) * 200 + chromeOffsetWidth;
  }

  if (aInputHeight > (availContentHeight + chromeOffsetHeight)) {
    resultHeight = availContentHeight + chromeOffsetHeight;
  } else if (aInputHeight < (100 + chromeOffsetHeight)) {
    resultHeight = 100 + chromeOffsetHeight;
  } else {
    resultHeight = NSToIntCeil((aInputHeight - chromeOffsetHeight) / 100.0) * 100 + chromeOffsetHeight;
  }

  *aOutputWidth = resultWidth;
  *aOutputHeight = resultHeight;
}

namespace mozilla {
namespace dom {
namespace workers {
+19 −0
Original line number Diff line number Diff line
@@ -249,6 +249,22 @@ public:
  static bool ShouldResistFingerprinting();
  static bool ShouldResistFingerprinting(nsIDocShell* aDocShell);

  // A helper function to calculate the rounded window size for fingerprinting
  // resistance. The rounded size is based on the chrome UI size and available
  // screen size. If the inputWidth/Height is greater than the available content
  // size, this will report the available content size. Otherwise, it will
  // round the size to the nearest upper 200x100.
  static void CalcRoundedWindowSizeForResistingFingerprinting(int32_t  aChromeWidth,
                                                              int32_t  aChromeHeight,
                                                              int32_t  aScreenWidth,
                                                              int32_t  aScreenHeight,
                                                              int32_t  aInputWidth,
                                                              int32_t  aInputHeight,
                                                              bool     aSetOuterWidth,
                                                              bool     aSetOuterHeight,
                                                              int32_t* aOutputWidth,
                                                              int32_t* aOutputHeight);

  /**
   * Returns the parent node of aChild crossing document boundaries.
   * Uses the parent node in the composed document.
@@ -2833,6 +2849,9 @@ private:
  static uint32_t sCookiesLifetimePolicy;
  static uint32_t sCookiesBehavior;

  static int32_t sPrivacyMaxInnerWidth;
  static int32_t sPrivacyMaxInnerHeight;

  static nsHtml5StringParser* sHTMLFragmentParser;
  static nsIParser* sXMLFragmentParser;
  static nsIFragmentContentSink* sXMLFragmentSink;
+95 −0
Original line number Diff line number Diff line
@@ -14953,6 +14953,101 @@ nsGlobalWindow::SetReplaceableWindowCoord(JSContext* aCx,
    return;
  }

  if (nsContentUtils::ShouldResistFingerprinting(GetDocShell())) {
    bool innerWidthSpecified = false;
    bool innerHeightSpecified = false;
    bool outerWidthSpecified = false;
    bool outerHeightSpecified = false;

    if (strcmp(aPropName, "innerWidth") == 0) {
      innerWidthSpecified = true;
    } else if (strcmp(aPropName, "innerHeight") == 0) {
      innerHeightSpecified = true;
    } else if (strcmp(aPropName, "outerWidth") == 0) {
      outerWidthSpecified = true;
    } else if (strcmp(aPropName, "outerHeight") == 0) {
      outerHeightSpecified = true;
    }

    if (innerWidthSpecified || innerHeightSpecified ||
        outerWidthSpecified || outerHeightSpecified)
    {
      nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = outer->GetTreeOwnerWindow();
      nsCOMPtr<nsIScreen> screen;
      nsCOMPtr<nsIScreenManager> screenMgr(
        do_GetService("@mozilla.org/gfx/screenmanager;1"));
      int32_t winLeft   = 0;
      int32_t winTop    = 0;
      int32_t winWidth  = 0;
      int32_t winHeight = 0;
      double scale = 1.0;


      if (treeOwnerAsWin && screenMgr) {
        // Acquire current window size.
        treeOwnerAsWin->GetUnscaledDevicePixelsPerCSSPixel(&scale);
        treeOwnerAsWin->GetPositionAndSize(&winLeft, &winTop, &winWidth, &winHeight);
        winLeft = NSToIntRound(winHeight / scale);
        winTop = NSToIntRound(winWidth / scale);
        winWidth = NSToIntRound(winWidth / scale);
        winHeight = NSToIntRound(winHeight / scale);

        // Acquire content window size.
        CSSIntSize contentSize;
        outer->GetInnerSize(contentSize);

        screenMgr->ScreenForRect(winLeft, winTop, winWidth, winHeight,
                                 getter_AddRefs(screen));

        if (screen) {
          int32_t* targetContentWidth  = nullptr;
          int32_t* targetContentHeight = nullptr;
          int32_t screenWidth  = 0;
          int32_t screenHeight = 0;
          int32_t chromeWidth  = 0;
          int32_t chromeHeight = 0;
          int32_t inputWidth   = 0;
          int32_t inputHeight  = 0;
          int32_t unused = 0;

          // Get screen dimensions (in device pixels)
          screen->GetAvailRect(&unused, &unused, &screenWidth,
                               &screenHeight);
          // Convert them to CSS pixels
          screenWidth = NSToIntRound(screenWidth / scale);
          screenHeight = NSToIntRound(screenHeight / scale);

          // Calculate the chrome UI size.
          chromeWidth = winWidth - contentSize.width;
          chromeHeight = winHeight - contentSize.height;

          if (innerWidthSpecified || outerWidthSpecified) {
            inputWidth = value;
            targetContentWidth = &value;
            targetContentHeight = &unused;
          } else if (innerHeightSpecified || outerHeightSpecified) {
            inputHeight = value;
            targetContentWidth = &unused;
            targetContentHeight = &value;
          }

          nsContentUtils::CalcRoundedWindowSizeForResistingFingerprinting(
            chromeWidth,
            chromeHeight,
            screenWidth,
            screenHeight,
            inputWidth,
            inputHeight,
            outerWidthSpecified,
            outerHeightSpecified,
            targetContentWidth,
            targetContentHeight
          );
        }
      }
    }
  }

  (this->*aSetter)(value, aError);
}

+46 −16
Original line number Diff line number Diff line
@@ -2411,6 +2411,7 @@ nsWindowWatcher::SizeOpenedWindow(nsIDocShellTreeOwner* aTreeOwner,
      screenHeight = NSToIntRound(screenHeight / scale);

      if (aSizeSpec.SizeSpecified()) {
        if (!nsContentUtils::ShouldResistFingerprinting()) {
          /* Unlike position, force size out-of-bounds check only if
             size actually was specified. Otherwise, intrinsically sized
             windows are broken. */
@@ -2428,6 +2429,35 @@ nsWindowWatcher::SizeOpenedWindow(nsIDocShellTreeOwner* aTreeOwner,
          if (winWidth > screenWidth) {
            width = screenWidth - (sizeChromeWidth ? 0 : chromeWidth);
          }
        } else {
          int32_t targetContentWidth  = 0;
          int32_t targetContentHeight = 0;

          nsContentUtils::CalcRoundedWindowSizeForResistingFingerprinting(
            chromeWidth,
            chromeHeight,
            screenWidth,
            screenHeight,
            width,
            height,
            sizeChromeWidth,
            sizeChromeHeight,
            &targetContentWidth,
            &targetContentHeight
          );

          if (aSizeSpec.mInnerWidthSpecified ||
              aSizeSpec.mOuterWidthSpecified) {
            width = targetContentWidth;
            winWidth = width + (sizeChromeWidth ? 0 : chromeWidth);
          }

          if (aSizeSpec.mInnerHeightSpecified ||
              aSizeSpec.mOuterHeightSpecified) {
            height = targetContentHeight;
            winHeight = height + (sizeChromeHeight ? 0 : chromeHeight);
          }
        }
      }

      if (left + winWidth > screenLeft + screenWidth ||
+35 −43
Original line number Diff line number Diff line
@@ -1061,10 +1061,12 @@ NS_IMETHODIMP nsXULWindow::ForceRoundedDimensions()
    return NS_OK;
  }

  int32_t windowWidth, windowHeight;
  int32_t availWidthCSS, availHeightCSS;
  int32_t contentWidthCSS, contentHeightCSS;
  int32_t contentWidth, contentHeight;
  int32_t availWidthCSS    = 0;
  int32_t availHeightCSS   = 0;
  int32_t contentWidthCSS  = 0;
  int32_t contentHeightCSS = 0;
  int32_t windowWidthCSS   = 0;
  int32_t windowHeightCSS  = 0;
  double devicePerCSSPixels = 1.0;

  GetUnscaledDevicePixelsPerCSSPixel(&devicePerCSSPixels);
@@ -1075,48 +1077,38 @@ NS_IMETHODIMP nsXULWindow::ForceRoundedDimensions()
  // size first. So, here, we size it to its available size.
  SetSpecifiedSize(availWidthCSS, availHeightCSS);

  GetSize(&windowWidth, &windowHeight); // device pixels
  // Get the current window size for calculating chrome UI size.
  GetSize(&windowWidthCSS, &windowHeightCSS); // device pixels
  windowWidthCSS = NSToIntRound(windowWidthCSS / devicePerCSSPixels);
  windowHeightCSS = NSToIntRound(windowHeightCSS / devicePerCSSPixels);

  int32_t availWidth = NSToIntRound(devicePerCSSPixels *
                                    availWidthCSS); // device pixels
  int32_t availHeight = NSToIntRound(devicePerCSSPixels *
                                     availHeightCSS); // device pixels
  // Get the content size for calculating chrome UI size.
  GetPrimaryContentSize(&contentWidthCSS, &contentHeightCSS);

  contentWidth = NSToIntRound(devicePerCSSPixels *
                              contentWidthCSS); // device pixels
  contentHeight = NSToIntRound(devicePerCSSPixels *
                               contentHeightCSS); // device pixels

  // Acquire the chrome UI size.
  int32_t chromeWidth = windowWidth - contentWidth;
  int32_t chromeHeight = windowHeight - contentHeight;

  int maxInnerWidth = Preferences::GetInt("privacy.window.maxInnerWidth",
                                          1000);
  int maxInnerHeight = Preferences::GetInt("privacy.window.maxInnerHeight",
                                           1000);

  // In the GTK window, it will not report outside system decorations when we
  // get available window size, see Bug 581863. So, we leave a five percent
  // space for them when calculating the available content height. It is not
  // necessary for the width since the content width is usually pretty much
  // the same as the chrome width.
  int32_t availForContentWidthCSS =
    std::min(maxInnerWidth, NSToIntRound((availWidth - chromeWidth) /
                                         devicePerCSSPixels));
  int32_t availForContentHeightCSS =
    std::min(maxInnerHeight, NSToIntRound((0.95 * availHeight - chromeHeight) /
                                          devicePerCSSPixels));
  // Ideally, we'd like to round window size to 1000x1000, but the screen space
  // could be too small to accommodate this size in some cases. If it happens,
  // we would round the window size to the nearest 200x100.
  int32_t targetContentWidth =
    NSToIntRound(devicePerCSSPixels *
                 (availForContentWidthCSS - (availForContentWidthCSS % 200)));
  int32_t targetContentHeight =
    NSToIntRound(devicePerCSSPixels *
                 (availForContentHeightCSS - (availForContentHeightCSS % 100)));
  // Calculate the chrome UI size.
  int32_t chromeWidth = 0, chromeHeight = 0;
  chromeWidth = windowWidthCSS - contentWidthCSS;
  chromeHeight = windowHeightCSS - contentHeightCSS;

  int32_t targetContentWidth = 0, targetContentHeight = 0;

  // Here, we use the available screen dimensions as the input dimensions to
  // force the window to be rounded as the maximum available content size.
  nsContentUtils::CalcRoundedWindowSizeForResistingFingerprinting(
    chromeWidth,
    chromeHeight,
    availWidthCSS,
    availHeightCSS,
    availWidthCSS,
    availHeightCSS,
    false, // aSetOuterWidth
    false, // aSetOuterHeight
    &targetContentWidth,
    &targetContentHeight
  );

  targetContentWidth = NSToIntRound(targetContentWidth * devicePerCSSPixels);
  targetContentHeight = NSToIntRound(targetContentHeight * devicePerCSSPixels);

  SetPrimaryContentSize(targetContentWidth, targetContentHeight);