Commit 1e019166 authored by Andrea Marchesini's avatar Andrea Marchesini
Browse files

Bug 1531289 - target=_blank with a download should close the download tab, r=nika,Gijs

Differential Revision: https://phabricator.services.mozilla.com/D66454

--HG--
extra : moz-landing-system : lando
parent 56217159
Loading
Loading
Loading
Loading
+9 −4
Original line number Diff line number Diff line
@@ -6206,13 +6206,18 @@ nsBrowserAccess.prototype = {
        browsingContext =
          window.content && BrowsingContext.getFromWindow(window.content);
        if (aURI) {
          let loadflags = isExternal
            ? Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL
            : Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
          let loadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
          if (isExternal) {
            loadFlags |= Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL;
          } else if (!aTriggeringPrincipal.isSystemPrincipal) {
            // XXX this code must be reviewed and changed when bug 1616353
            // lands.
            loadFlags |= Ci.nsIWebNavigation.LOAD_FLAGS_FIRST_LOAD;
          }
          gBrowser.loadURI(aURI.spec, {
            triggeringPrincipal: aTriggeringPrincipal,
            csp: aCsp,
            flags: loadflags,
            loadFlags,
            referrerInfo,
          });
        }
+4 −0
Original line number Diff line number Diff line
@@ -2845,6 +2845,10 @@
          }
          if (fromExternal) {
            flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL;
          } else if (!triggeringPrincipal.isSystemPrincipal) {
            // XXX this code must be reviewed and changed when bug 1616353
            // lands.
            flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FIRST_LOAD;
          }
          if (allowMixedContent) {
            flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_MIXED_CONTENT;
+47 −16
Original line number Diff line number Diff line
@@ -10,7 +10,9 @@
#include "nsServiceManagerUtils.h"
#include "nsDocShellCID.h"
#include "nsIWebNavigationInfo.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/Document.h"
#include "mozilla/Unused.h"
#include "nsError.h"
#include "nsContentSecurityManager.h"
#include "nsDocShellLoadTypes.h"
@@ -41,14 +43,21 @@ void MaybeCloseWindowHelper::SetShouldCloseWindow(bool aShouldCloseWindow) {
}

BrowsingContext* MaybeCloseWindowHelper::MaybeCloseWindow() {
  if (mShouldCloseWindow) {
  if (!mShouldCloseWindow) {
    return mBrowsingContext;
  }

  // This method should not be called more than once, but it's better to avoid
  // closing the current window again.
  mShouldCloseWindow = false;

  // Reset the window context to the opener window so that the dependent
  // dialogs have a parent
    RefPtr<BrowsingContext> opener = mBrowsingContext->GetOpener();
  RefPtr<BrowsingContext> newBC = ChooseNewBrowsingContext(mBrowsingContext);

    if (opener && !opener->IsDiscarded()) {
  if (newBC != mBrowsingContext && newBC && !newBC->IsDiscarded()) {
    mBCToClose = mBrowsingContext;
      mBrowsingContext = opener;
    mBrowsingContext = newBC;

    // Now close the old window.  Do it on a timer so that we don't run
    // into issues trying to close the window before it has fully opened.
@@ -56,10 +65,32 @@ BrowsingContext* MaybeCloseWindowHelper::MaybeCloseWindow() {
    NS_NewTimerWithCallback(getter_AddRefs(mTimer), this, 0,
                            nsITimer::TYPE_ONE_SHOT);
  }
  }

  return mBrowsingContext;
}

already_AddRefed<BrowsingContext>
MaybeCloseWindowHelper::ChooseNewBrowsingContext(BrowsingContext* aBC) {
  RefPtr<BrowsingContext> bc = aBC;

  RefPtr<BrowsingContext> opener = bc->GetOpener();
  if (opener && !opener->IsDiscarded()) {
    return opener.forget();
  }

  if (!XRE_IsParentProcess()) {
    return bc.forget();
  }

  CanonicalBrowsingContext* cbc = CanonicalBrowsingContext::Cast(aBC);
  RefPtr<WindowGlobalParent> wgp = cbc->GetEmbedderWindowGlobal();
  if (!wgp) {
    return bc.forget();
  }

  return do_AddRef(wgp->BrowsingContext());
}

NS_IMETHODIMP
MaybeCloseWindowHelper::Notify(nsITimer* timer) {
  NS_ASSERTION(mBCToClose, "No window to close after timer fired");
@@ -135,7 +166,7 @@ nsDSURIContentListener::DoContent(const nsACString& aContentType,
        RefPtr<MaybeCloseWindowHelper> maybeCloseWindowHelper =
            new MaybeCloseWindowHelper(mDocShell->GetBrowsingContext());
        maybeCloseWindowHelper->SetShouldCloseWindow(true);
        maybeCloseWindowHelper->MaybeCloseWindow();
        Unused << maybeCloseWindowHelper->MaybeCloseWindow();
      }
      return NS_OK;
    }
+10 −4
Original line number Diff line number Diff line
@@ -17,7 +17,7 @@ class nsIInterfaceRequestor;
class nsIWebNavigationInfo;
class nsPIDOMWindowOuter;

// Helper Class to eventually close an already openend window
// Helper Class to eventually close an already opened window
class MaybeCloseWindowHelper final : public nsITimerCallback {
 public:
  NS_DECL_ISUPPORTS
@@ -27,9 +27,12 @@ class MaybeCloseWindowHelper final : public nsITimerCallback {
      mozilla::dom::BrowsingContext* aContentContext);

  /**
   * Closes the provided window async (if mShouldCloseWindow is true)
   * and returns its opener if the window was just opened. Otherwise
   * returns the BrowsingContext provided in the constructor.
   * Closes the provided window async (if mShouldCloseWindow is true) and
   * returns a valid browsingContext to be used instead as parent for dialogs or
   * similar things.
   * In case mShouldCloseWindow is true, the final browsing context will be the
   * a valid new chrome window to use. It can be the opener, or the opener's
   * top, or the top chrome window.
   */
  mozilla::dom::BrowsingContext* MaybeCloseWindow();

@@ -39,6 +42,9 @@ class MaybeCloseWindowHelper final : public nsITimerCallback {
  ~MaybeCloseWindowHelper();

 private:
  already_AddRefed<mozilla::dom::BrowsingContext> ChooseNewBrowsingContext(
      mozilla::dom::BrowsingContext* aBC);

  /**
   * The dom window associated to handle content.
   */
+96 −0
Original line number Diff line number Diff line
@@ -100,6 +100,62 @@ add_task(async function target_blank() {
  });
});

add_task(async function target_blank_no_opener() {
  // Tests that a link with target=_blank and no opener opens a new tab
  // and closes it, returning the window that we're using for navigation.
  await BrowserTestUtils.withNewTab({ gBrowser, url: PAGE_URL }, async function(
    browser
  ) {
    let dialogAppeared = promiseHelperAppDialog();
    let tabOpened = BrowserTestUtils.waitForEvent(
      gBrowser.tabContainer,
      "TabOpen"
    ).then(event => {
      return [event.target, BrowserTestUtils.waitForTabClosing(event.target)];
    });

    await BrowserTestUtils.synthesizeMouseAtCenter(
      "#target_blank_no_opener",
      {},
      browser
    );

    let windowContext = await dialogAppeared;
    is(windowContext, browser.ownerGlobal, "got the right windowContext");
    let [tab, closingPromise] = await tabOpened;
    await closingPromise;
    is(tab.linkedBrowser, null, "tab was opened and closed");
  });
});

add_task(async function open_in_new_tab_no_opener() {
  // Tests that a link with target=_blank and no opener opens a new tab
  // and closes it, returning the window that we're using for navigation.
  await BrowserTestUtils.withNewTab({ gBrowser, url: PAGE_URL }, async function(
    browser
  ) {
    let dialogAppeared = promiseHelperAppDialog();
    let tabOpened = BrowserTestUtils.waitForEvent(
      gBrowser.tabContainer,
      "TabOpen"
    ).then(event => {
      return [event.target, BrowserTestUtils.waitForTabClosing(event.target)];
    });

    await BrowserTestUtils.synthesizeMouseAtCenter(
      "#open_in_new_tab_no_opener",
      {},
      browser
    );

    let windowContext = await dialogAppeared;
    is(windowContext, browser.ownerGlobal, "got the right windowContext");
    let [tab, closingPromise] = await tabOpened;
    await closingPromise;
    is(tab.linkedBrowser, null, "tab was opened and closed");
  });
});

add_task(async function new_window() {
  // Tests that a link that forces us to open a new window (by specifying a
  // width and a height in window.open) opens a new window for the load,
@@ -122,6 +178,46 @@ add_task(async function new_window() {
    // The window should close on its own. If not, this test will time out.
    await BrowserTestUtils.domWindowClosed(win);
    ok(win.closed, "window was opened and closed");

    is(
      await fetch(SJS_URL + "?reset").then(r => r.text()),
      "OK",
      "Test reseted"
    );
  });
});

add_task(async function new_window_no_opener() {
  // Tests that a link that forces us to open a new window (by specifying a
  // width and a height in window.open) opens a new window for the load,
  // realizes that we need to close that window and returns the *original*
  // window as the window context.
  await BrowserTestUtils.withNewTab({ gBrowser, url: PAGE_URL }, async function(
    browser
  ) {
    let dialogAppeared = promiseHelperAppDialog();
    let windowOpened = BrowserTestUtils.waitForNewWindow();

    await BrowserTestUtils.synthesizeMouseAtCenter(
      "#new_window_no_opener",
      {},
      browser
    );
    let win = await windowOpened;
    // Now allow request to complete:
    fetch(SJS_URL + "?finish");

    await dialogAppeared;

    // The window should close on its own. If not, this test will time out.
    await BrowserTestUtils.domWindowClosed(win);
    ok(win.closed, "window was opened and closed");

    is(
      await fetch(SJS_URL + "?reset").then(r => r.text()),
      "OK",
      "Test reseted"
    );
  });
});

Loading