Commit 5e0c1a6b authored by Kathleen Brade's avatar Kathleen Brade Committed by Matthew Finkel
Browse files

Bug 16620: Clear window.name when no referrer sent

Convert JS implementation (within Torbutton) to a C++ browser patch.
parent c6e1b1ad
Loading
Loading
Loading
Loading
+60 −0
Original line number Diff line number Diff line
@@ -7747,11 +7747,71 @@ nsresult nsDocShell::CreateContentViewer(const nsACString& aContentType,
    aOpenedChannel->GetURI(getter_AddRefs(mLoadingURI));
  }
  FirePageHideNotification(!mSavingOldViewer);

  if (mIsBeingDestroyed) {
    // Force to stop the newly created orphaned viewer.
    viewer->Stop();
    return NS_ERROR_DOCSHELL_DYING;
  }

  // Tor bug 16620: Clear window.name of top-level documents if
  // there is no referrer. We make an exception for new windows,
  // e.g., window.open(url, "MyName").
  bool isNewWindowTarget = false;
  nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aRequest, &rv));
  if (props) {
    props->GetPropertyAsBool(u"docshell.newWindowTarget"_ns,
                             &isNewWindowTarget);
  }

  if (!isNewWindowTarget) {
    nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aOpenedChannel));
    nsCOMPtr<nsIURI> httpReferrer;
    if (httpChannel) {
      nsCOMPtr<nsIReferrerInfo> referrerInfo;
      rv = httpChannel->GetReferrerInfo(getter_AddRefs(referrerInfo));
      NS_ENSURE_SUCCESS(rv, rv);
      if (referrerInfo) {
        // We want GetComputedReferrer() instead of GetOriginalReferrer(), since
        // the former takes into consideration referrer policy, protocol
        // whitelisting...
        httpReferrer = referrerInfo->GetComputedReferrer();
      }
    }

    bool isTopFrame = mBrowsingContext->IsTop();

#ifdef DEBUG_WINDOW_NAME
    printf("DOCSHELL %p CreateContentViewer - possibly clearing window.name:\n",
           this);
    printf("  current window.name: \"%s\"\n",
           NS_ConvertUTF16toUTF8(mName).get());

    nsAutoCString curSpec, loadingSpec;
    if (this->mCurrentURI) mCurrentURI->GetSpec(curSpec);
    if (mLoadingURI) mLoadingURI->GetSpec(loadingSpec);
    printf("  current URI: %s\n", curSpec.get());
    printf("  loading URI: %s\n", loadingSpec.get());
    printf("  is top document: %s\n", isTopFrame ? "Yes" : "No");

    if (!httpReferrer) {
      printf("  referrer: None\n");
    } else {
      nsAutoCString refSpec;
      httpReferrer->GetSpec(refSpec);
      printf("  referrer: %s\n", refSpec.get());
    }
#endif

    bool clearName = isTopFrame && !httpReferrer;
    if (clearName) SetName(u""_ns);

#ifdef DEBUG_WINDOW_NAME
    printf("  action taken: %s window.name\n",
           clearName ? "Cleared" : "Preserved");
#endif
  }

  mLoadingURI = nullptr;

  // Set mFiredUnloadEvent = false so that the unload handler for the
+3 −0
Original line number Diff line number Diff line
@@ -53,6 +53,8 @@ support-files =
  start_historyframe.html
  url1_historyframe.html
  url2_historyframe.html
  tor_bug16620.html
  tor_bug16620_form.html

[test_anchor_scroll_after_document_open.html]
[test_bfcache_plus_hash.html]
@@ -124,6 +126,7 @@ support-files =
  file_history_length_during_pageload.html
  file_history_length_during_pageload_2.html
[test_pushState_after_document_open.html]
[test_tor_bug16620.html]
[test_navigate_after_pagehide.html]
[test_redirect_history.html]
support-files =
+211 −0
Original line number Diff line number Diff line
<!DOCTYPE HTML>
<html>
<!--
  Tor Bug 16620: Clear window.name when no referrer sent.
                 https://trac.torproject.org/projects/tor/ticket/16620
-->
<meta charset="utf-8">
<head>
  <title>Test for Tor Bug 16620 - Clear window.name when no referrer sent</title>
  <script type="application/javascript"
          src="/tests/SimpleTest/SimpleTest.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://trac.torproject.org/projects/tor/ticket/16620">Tor Bug 16620</a>
<script>
// ## Test constants
const kTestPath = "/tests/docshell/test/mochitest/";
const kLinkFile = "tor_bug16620.html";
const kFormFile = "tor_bug16620_form.html";
const kBaseURL1 = "http://example.com";
const kBaseURL1_https = "https://example.com";
const kBaseURL2 = "http://example.net";
const kSendReferrerPref = "network.http.sendRefererHeader";
const kSendReferrerNever = 0;
const kSendReferrerForUserAction = 1;
const kSendReferrerAlways = 2;

let gTests = [
  // Test #1: Same domain; never send referrer.
  { startURL: kBaseURL1, destURL: kBaseURL1,
    referrerPref: kSendReferrerNever,
    expectIsolation: true },

  // Test #2: Same domain; send referrer upon user action.
  { startURL: kBaseURL1, destURL: kBaseURL1,
    referrerPref: kSendReferrerForUserAction,
    expectIsolation: false },

  // Test #3: Same domain; always send referrer.
  { startURL: kBaseURL1, destURL: kBaseURL1,
    referrerPref: kSendReferrerAlways,
    expectIsolation: false },

  // Test #4: Different top-level domains; never send referrer.
  { startURL: kBaseURL1, destURL: kBaseURL2,
    referrerPref: kSendReferrerNever,
    expectIsolation: true },

  // Test #5: Different top-level domains; send referrer upon user action.
  { startURL: kBaseURL1, destURL: kBaseURL2,
    referrerPref: kSendReferrerForUserAction,
    expectIsolation: false },

  // Test #6: Different top-level domains; always send referrer.
  { startURL: kBaseURL1, destURL: kBaseURL2,
    referrerPref: kSendReferrerAlways,
    expectIsolation: false },

  // Test #7: https -> http transition.
  { startURL: kBaseURL1_https, destURL: kBaseURL1,
    referrerPref: kSendReferrerForUserAction,
    expectIsolation: true },

  // Test #8: Same domain, rel="noreferrer" on link.
  { startURL: kBaseURL1, destURL: kBaseURL1, noReferrerOnLink: true,
    referrerPref: kSendReferrerAlways,
    expectIsolation: true },

  // Test #9: Same domain, "no-referrer" meta tag in document.
  { startURL: kBaseURL1, destURL: kBaseURL1, noReferrerInMetaTag: true,
    referrerPref: kSendReferrerAlways,
    expectIsolation: true },

  // Test #10: Like test #9, but reset window.name during unload.
  // (similar to http://www.thomasfrank.se/sessvarsTestPage1.html)
  { startURL: kBaseURL1, destURL: kBaseURL1, noReferrerInMetaTag: true,
    resetInUnload: true,
    referrerPref: kSendReferrerAlways,
    expectIsolation: true },

  // Test #11: Data URL as destination (no referrer).
  { startURL: kBaseURL1,
    referrerPref: kSendReferrerAlways,
    expectIsolation: true },

  // Test #12: Ensure that window.name is preserved when a dynamically loaded
  // iframe is used to perform a form post (regression test for Tor bug 18168).
  { startURL: kBaseURL1,
    isFormTest: true,
    referrerPref: kSendReferrerAlways,
    expectIsolation: false },
];

let gCurTest = 0;
let gCurWinName, gChildWin, gDataURL;

// ## Utility functions
function generateRandomName()
{
  // Generate a random 6 character string using 0-9 and a-z.
  return ((1 + Math.random()).toString(36) + '000000').substr(2, 6);
}

function startNextTest() {
  ++gCurTest;
  if (gCurTest > gTests.length) {
    SimpleTest.finish();
  } else {
    let curTest = gTests[gCurTest - 1];
    if ("referrerPref" in curTest)
      SpecialPowers.setIntPref(kSendReferrerPref, curTest.referrerPref);
    else
      SpecialPowers.setIntPref(kSendReferrerPref, kSendReferrerForUserAction);
    gCurWinName = generateRandomName();
    let url = curTest.startURL + kTestPath;
    if (curTest.isFormTest === true) {
      url += kFormFile + "?" + gCurWinName;
      gChildWin = window.open(url, undefined);
    } else {
      url += kLinkFile + "?firstDocLoaded";
      gChildWin = window.open(url, gCurWinName);
    }
  }
}

// ## Add a message event listener.
window.addEventListener("message", function(aEvent) {
    if (aEvent.source !== gChildWin)
      return;

//    console.log("parent received message:" + JSON.stringify(aEvent.data));

    let proceedToNextTest = false;
    let curTest = gTests[gCurTest - 1];
    let state = aEvent.data.state;
    let winName = aEvent.data.winName;
    if ("firstDocLoaded" == state) {
      // Process response from step one of the link-based  tests.
      let step1Passed = (winName === gCurWinName);
      if (!step1Passed) {
        ok(step1Passed, "Test #" + gCurTest +
                 " - first document's name matches window.open parameter");
        proceedToNextTest = true;
      }

      // Send an "openURL" message to the loaded document.
      let url2 = (curTest.destURL)
                  ? curTest.destURL + kTestPath + kLinkFile + "?secondDocLoaded"
                  : gDataURL;
      let noReferrerOnLink = (curTest.noReferrerOnLink === true);
      let noReferrerInMetaTag = (curTest.noReferrerInMetaTag === true);
      let resetInUnload = (curTest.resetInUnload === true);
      aEvent.source.postMessage({ action: "openURL", url: url2,
                                  noReferrerOnLink: noReferrerOnLink,
                                  noReferrerInMetaTag: noReferrerInMetaTag,
                                  resetInUnload: resetInUnload },
                                  "*");
    } else if ("secondDocLoaded" == state) {
      // Process response from step two of the link-based tests.
      if (curTest.expectIsolation) {
        ok(winName === "",
           "Test #" + gCurTest + " - second document: name was cleared");
      } else {
        ok(winName === gCurWinName,
           "Test #" + gCurTest + " - second document: name was preserved");
      }
      proceedToNextTest = true;
    } else if ("formPostDone" == state) {
      // Process response from the form post tests.
      if (curTest.expectIsolation) {
        ok(winName === "",
           "Test #" + gCurTest + " - iframe form post: name was cleared");
      } else {
        ok(winName === gCurWinName,
           "Test #" + gCurTest + " - iframe form post: name was preserved");
      }
      proceedToNextTest = true;

    }

    if (proceedToNextTest) {
      gChildWin.close();
      startNextTest();
    }
  }, false);

  SimpleTest.waitForExplicitFinish();

  if (SpecialPowers.getBoolPref("security.nocertdb")) {
    // Mochitests don't simulate https correctly with "security.nocertdb"
    // enabled.  See https://bugs.torproject.org/18087
    ok(false, "Please disable the pref `security.nocertdb` before running this test.");
    SimpleTest.finish();
  } else {

    // Read file contents, construct a data URL (used by some tests), and
    // then start the first test.
    let url = kTestPath + kLinkFile;
    let xhr = new XMLHttpRequest();
    xhr.open("GET", url);
    xhr.onload = function() {
      gDataURL = "data:text/html;charset=utf-8,"
                 + encodeURIComponent(this.responseText);
      startNextTest();
    }
    xhr.send();
  }
</script>
</body>
</html>
+51 −0
Original line number Diff line number Diff line
<!DOCTYPE HTML>
<html>
<!--
  Tor Bug 16620: Clear window.name when no referrer sent.
                 https://trac.torproject.org/projects/tor/ticket/16620
-->
<head>
  <meta charset="UTF-8">
  <title>Supporting Doc for Tor Bug 16620 Tests</title>
</head>
<body>
<a id="link" href="">secondDoc</a>

<script>
// Extract test state from our query string, defaulting to
// "secondDocLoaded" to support use of this HTML content within
// a data URI (where query strings are not supported).
let state = (location.search.length > 0) ? location.search.substr(1)
                                         : "secondDocLoaded";

// Notify the test driver.
opener.postMessage({ state: state, winName: window.name }, "*");

// Add a message event listener to process "openURL" actions.
window.addEventListener("message", function(aEvent) {
  if (aEvent.data.action == "openURL") {
    if (aEvent.data.noReferrerInMetaTag) {
      let metaElem = document.createElement("meta");
      metaElem.name = "referrer";
      metaElem.content = "no-referrer";
      document.head.appendChild(metaElem);
    }

    let linkElem = document.getElementById("link");
    linkElem.href = aEvent.data.url;
    if (aEvent.data.noReferrerOnLink)
      linkElem.rel = "noreferrer";

    if (aEvent.data.resetInUnload) {
      let tmpName = window.name;
      window.addEventListener("unload", function() {
        window.name = tmpName;
      }, false);
    }

    linkElem.click();
  }
}, false);
</script>
</body>
</html>
+51 −0
Original line number Diff line number Diff line
<!DOCTYPE HTML>
<html>
<!--
  Tor Bug 16620: Clear window.name when no referrer sent.
                 https://trac.torproject.org/projects/tor/ticket/16620

  Regression test for bug 18168: iframe-based AJAX call opening in new tab
-->
<head>
  <meta charset="UTF-8">
  <title>Supporting Form-based Doc for Tor Bug 16620 Tests</title>
</head>
<body>

<script>
document.addEventListener("DOMContentLoaded", function () {
  addPostTarget();
}, false);


function addPostTarget()
{
  let frameName = location.search.substr(1);
  let form = document.getElementById("postform");
  let iframe = document.createElement("iframe");
  iframe.style.border = "1px solid red";
  iframe.src = "about:blank";
  form.target = iframe.name = iframe.id = frameName;
  document.body.appendChild(iframe);

  let didSubmit = false;
  iframe.onload = function() {
    if (!didSubmit) {
      didSubmit = true;
      let submitButton = document.getElementById("submitButton");
      submitButton.click();
    } else {
      // Form submission complete. Report iframe's name to test driver.
      opener.postMessage({ state: "formPostDone", winName: iframe.name }, "*");
    }
  };
}

</script>
<form name="postform" id="postform"
      action="data:text/plain;charset=utf-8,Hello%20world"
      method="POST" enctype="multipart/form-data">
  <input type="hidden" name="field1" value="value1"><br>
  <input id="submitButton" type="submit" value="Post It">
</body>
</html>