Bug 1763672 - Introduce targetTopLevelLinkClicksToBlank on BrowsingContext. r=nika
This property defaults to false. When set to true, user-initiated link clicks in the top-level BrowsingContext will default target to _blank. This is similar to what we do for App Tabs, but is slightly more aggressive in that in will also occur for same-origin navigations. Differential Revision: https://phabricator.services.mozilla.com/D143374
This commit is contained in:
@@ -3076,6 +3076,10 @@ mozilla::dom::TouchEventsOverride BrowsingContext::TouchEventsOverride() const {
|
|||||||
return mozilla::dom::TouchEventsOverride::None;
|
return mozilla::dom::TouchEventsOverride::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BrowsingContext::TargetTopLevelLinkClicksToBlank() const {
|
||||||
|
return Top()->GetTargetTopLevelLinkClicksToBlankInternal();
|
||||||
|
}
|
||||||
|
|
||||||
// We map `watchedByDevTools` WebIDL attribute to `watchedByDevToolsInternal`
|
// We map `watchedByDevTools` WebIDL attribute to `watchedByDevToolsInternal`
|
||||||
// BC field. And we map it to the top level BrowsingContext.
|
// BC field. And we map it to the top level BrowsingContext.
|
||||||
bool BrowsingContext::WatchedByDevTools() {
|
bool BrowsingContext::WatchedByDevTools() {
|
||||||
@@ -3475,6 +3479,13 @@ void BrowsingContext::DidSet(FieldIndex<IDX_HasSessionHistory>,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BrowsingContext::CanSet(
|
||||||
|
FieldIndex<IDX_TargetTopLevelLinkClicksToBlankInternal>,
|
||||||
|
const bool& aTargetTopLevelLinkClicksToBlankInternal,
|
||||||
|
ContentParent* aSource) {
|
||||||
|
return XRE_IsParentProcess() && !aSource && IsTop();
|
||||||
|
}
|
||||||
|
|
||||||
bool BrowsingContext::CanSet(FieldIndex<IDX_BrowserId>, const uint32_t& aValue,
|
bool BrowsingContext::CanSet(FieldIndex<IDX_BrowserId>, const uint32_t& aValue,
|
||||||
ContentParent* aSource) {
|
ContentParent* aSource) {
|
||||||
// We should only be able to set this for toplevel contexts which don't have
|
// We should only be able to set this for toplevel contexts which don't have
|
||||||
|
|||||||
@@ -159,6 +159,7 @@ enum class ExplicitActiveStatus : uint8_t {
|
|||||||
FIELD(AllowContentRetargetingOnChildren, bool) \
|
FIELD(AllowContentRetargetingOnChildren, bool) \
|
||||||
FIELD(ForceEnableTrackingProtection, bool) \
|
FIELD(ForceEnableTrackingProtection, bool) \
|
||||||
FIELD(UseGlobalHistory, bool) \
|
FIELD(UseGlobalHistory, bool) \
|
||||||
|
FIELD(TargetTopLevelLinkClicksToBlankInternal, bool) \
|
||||||
FIELD(FullscreenAllowedByOwner, bool) \
|
FIELD(FullscreenAllowedByOwner, bool) \
|
||||||
/* \
|
/* \
|
||||||
* "is popup" in the spec. \
|
* "is popup" in the spec. \
|
||||||
@@ -557,6 +558,7 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
|
|||||||
void SetWatchedByDevTools(bool aWatchedByDevTools, ErrorResult& aRv);
|
void SetWatchedByDevTools(bool aWatchedByDevTools, ErrorResult& aRv);
|
||||||
|
|
||||||
dom::TouchEventsOverride TouchEventsOverride() const;
|
dom::TouchEventsOverride TouchEventsOverride() const;
|
||||||
|
bool TargetTopLevelLinkClicksToBlank() const;
|
||||||
|
|
||||||
bool FullscreenAllowed() const;
|
bool FullscreenAllowed() const;
|
||||||
|
|
||||||
@@ -1139,6 +1141,10 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
|
|||||||
bool CanSet(FieldIndex<IDX_UseGlobalHistory>, const bool& aUseGlobalHistory,
|
bool CanSet(FieldIndex<IDX_UseGlobalHistory>, const bool& aUseGlobalHistory,
|
||||||
ContentParent* aSource);
|
ContentParent* aSource);
|
||||||
|
|
||||||
|
bool CanSet(FieldIndex<IDX_TargetTopLevelLinkClicksToBlankInternal>,
|
||||||
|
const bool& aTargetTopLevelLinkClicksToBlankInternal,
|
||||||
|
ContentParent* aSource);
|
||||||
|
|
||||||
void DidSet(FieldIndex<IDX_HasSessionHistory>, bool aOldValue);
|
void DidSet(FieldIndex<IDX_HasSessionHistory>, bool aOldValue);
|
||||||
|
|
||||||
bool CanSet(FieldIndex<IDX_BrowserId>, const uint32_t& aValue,
|
bool CanSet(FieldIndex<IDX_BrowserId>, const uint32_t& aValue,
|
||||||
|
|||||||
@@ -317,6 +317,8 @@ void CanonicalBrowsingContext::ReplacedBy(
|
|||||||
// about:blank documents created in it.
|
// about:blank documents created in it.
|
||||||
txn.SetSandboxFlags(GetSandboxFlags());
|
txn.SetSandboxFlags(GetSandboxFlags());
|
||||||
txn.SetInitialSandboxFlags(GetSandboxFlags());
|
txn.SetInitialSandboxFlags(GetSandboxFlags());
|
||||||
|
txn.SetTargetTopLevelLinkClicksToBlankInternal(
|
||||||
|
TargetTopLevelLinkClicksToBlank());
|
||||||
if (aNewContext->EverAttached()) {
|
if (aNewContext->EverAttached()) {
|
||||||
MOZ_ALWAYS_SUCCEEDS(txn.Commit(aNewContext));
|
MOZ_ALWAYS_SUCCEEDS(txn.Commit(aNewContext));
|
||||||
} else {
|
} else {
|
||||||
@@ -2678,6 +2680,12 @@ void CanonicalBrowsingContext::SetTouchEventsOverride(
|
|||||||
SetTouchEventsOverrideInternal(aOverride, aRv);
|
SetTouchEventsOverrideInternal(aOverride, aRv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CanonicalBrowsingContext::SetTargetTopLevelLinkClicksToBlank(
|
||||||
|
bool aTargetTopLevelLinkClicksToBlank, ErrorResult& aRv) {
|
||||||
|
SetTargetTopLevelLinkClicksToBlankInternal(aTargetTopLevelLinkClicksToBlank,
|
||||||
|
aRv);
|
||||||
|
}
|
||||||
|
|
||||||
void CanonicalBrowsingContext::AddPageAwakeRequest() {
|
void CanonicalBrowsingContext::AddPageAwakeRequest() {
|
||||||
MOZ_ASSERT(IsTop());
|
MOZ_ASSERT(IsTop());
|
||||||
auto count = GetPageAwakeRequestCount();
|
auto count = GetPageAwakeRequestCount();
|
||||||
|
|||||||
@@ -322,6 +322,8 @@ class CanonicalBrowsingContext final : public BrowsingContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SetTouchEventsOverride(dom::TouchEventsOverride, ErrorResult& aRv);
|
void SetTouchEventsOverride(dom::TouchEventsOverride, ErrorResult& aRv);
|
||||||
|
void SetTargetTopLevelLinkClicksToBlank(bool aTargetTopLevelLinkClicksToBlank,
|
||||||
|
ErrorResult& aRv);
|
||||||
|
|
||||||
bool IsReplaced() const { return mIsReplaced; }
|
bool IsReplaced() const { return mIsReplaced; }
|
||||||
|
|
||||||
|
|||||||
@@ -12860,7 +12860,8 @@ nsresult nsDocShell::OnLinkClick(
|
|||||||
|
|
||||||
bool noOpenerImplied = false;
|
bool noOpenerImplied = false;
|
||||||
nsAutoString target(aTargetSpec);
|
nsAutoString target(aTargetSpec);
|
||||||
if (ShouldOpenInBlankTarget(aTargetSpec, aURI, aContent)) {
|
if (aFileName.IsVoid() &&
|
||||||
|
ShouldOpenInBlankTarget(aTargetSpec, aURI, aContent, aIsUserTriggered)) {
|
||||||
target = u"_blank";
|
target = u"_blank";
|
||||||
if (!aTargetSpec.Equals(target)) {
|
if (!aTargetSpec.Equals(target)) {
|
||||||
noOpenerImplied = true;
|
noOpenerImplied = true;
|
||||||
@@ -12886,17 +12887,9 @@ nsresult nsDocShell::OnLinkClick(
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool nsDocShell::ShouldOpenInBlankTarget(const nsAString& aOriginalTarget,
|
bool nsDocShell::ShouldOpenInBlankTarget(const nsAString& aOriginalTarget,
|
||||||
nsIURI* aLinkURI,
|
nsIURI* aLinkURI, nsIContent* aContent,
|
||||||
nsIContent* aContent) {
|
bool aIsUserTriggered) {
|
||||||
// Don't modify non-default targets.
|
if (net::SchemeIsJavascript(aLinkURI)) {
|
||||||
if (!aOriginalTarget.IsEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only check targets that are in extension panels or app tabs.
|
|
||||||
// (isAppTab will be false for app tab subframes).
|
|
||||||
nsString mmGroup = mBrowsingContext->Top()->GetMessageManagerGroup();
|
|
||||||
if (!mmGroup.EqualsLiteral("webext-browsers") && !mIsAppTab) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12909,6 +12902,29 @@ bool nsDocShell::ShouldOpenInBlankTarget(const nsAString& aOriginalTarget,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The targetTopLevelLinkClicksToBlank property on BrowsingContext allows
|
||||||
|
// privileged code to change the default targeting behaviour. In particular,
|
||||||
|
// if a user-initiated link click for the (or targetting the) top-level frame
|
||||||
|
// is detected, we default the target to "_blank" to give it a new
|
||||||
|
// top-level BrowsingContext.
|
||||||
|
if (mBrowsingContext->TargetTopLevelLinkClicksToBlank() && aIsUserTriggered &&
|
||||||
|
((aOriginalTarget.IsEmpty() && mBrowsingContext->IsTop()) ||
|
||||||
|
aOriginalTarget == u"_top"_ns)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't modify non-default targets.
|
||||||
|
if (!aOriginalTarget.IsEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only check targets that are in extension panels or app tabs.
|
||||||
|
// (isAppTab will be false for app tab subframes).
|
||||||
|
nsString mmGroup = mBrowsingContext->Top()->GetMessageManagerGroup();
|
||||||
|
if (!mmGroup.EqualsLiteral("webext-browsers") && !mIsAppTab) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIURI> docURI = aContent->OwnerDoc()->GetDocumentURIObject();
|
nsCOMPtr<nsIURI> docURI = aContent->OwnerDoc()->GetDocumentURIObject();
|
||||||
if (!docURI) {
|
if (!docURI) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1114,7 +1114,8 @@ class nsDocShell final : public nsDocLoader,
|
|||||||
bool NoopenerForceEnabled();
|
bool NoopenerForceEnabled();
|
||||||
|
|
||||||
bool ShouldOpenInBlankTarget(const nsAString& aOriginalTarget,
|
bool ShouldOpenInBlankTarget(const nsAString& aOriginalTarget,
|
||||||
nsIURI* aLinkURI, nsIContent* aContent);
|
nsIURI* aLinkURI, nsIContent* aContent,
|
||||||
|
bool aIsUserTriggered);
|
||||||
|
|
||||||
void RecordSingleChannelId(bool aStartRequest, nsIRequest* aRequest);
|
void RecordSingleChannelId(bool aStartRequest, nsIRequest* aRequest);
|
||||||
|
|
||||||
|
|||||||
@@ -158,6 +158,7 @@ https_first_disabled = true
|
|||||||
support-files =
|
support-files =
|
||||||
file_backforward_restore_scroll.html
|
file_backforward_restore_scroll.html
|
||||||
file_backforward_restore_scroll.html^headers^
|
file_backforward_restore_scroll.html^headers^
|
||||||
|
[browser_targetTopLevelLinkClicksToBlank.js]
|
||||||
[browser_title_in_session_history.js]
|
[browser_title_in_session_history.js]
|
||||||
skip-if = !sessionHistoryInParent
|
skip-if = !sessionHistoryInParent
|
||||||
[browser_uriFixupIntegration.js]
|
[browser_uriFixupIntegration.js]
|
||||||
|
|||||||
285
docshell/test/browser/browser_targetTopLevelLinkClicksToBlank.js
Normal file
285
docshell/test/browser/browser_targetTopLevelLinkClicksToBlank.js
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test exercises the behaviour where user-initiated link clicks on
|
||||||
|
* the top-level document result in pageloads in a _blank target in a new
|
||||||
|
* browser window.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const TEST_PAGE = "https://example.com/browser/";
|
||||||
|
const TEST_PAGE_2 = "https://example.com/browser/components/";
|
||||||
|
const TEST_IFRAME_PAGE =
|
||||||
|
getRootDirectory(gTestPath).replace(
|
||||||
|
"chrome://mochitests/content",
|
||||||
|
"https://example.com"
|
||||||
|
) + "dummy_iframe_page.html";
|
||||||
|
|
||||||
|
// There is an <a> element with this href=".." in the TEST_PAGE
|
||||||
|
// that we will click, which should take us up a level.
|
||||||
|
const LINK_URL = "https://example.com/";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that a user-initiated link click results in targeting to a new
|
||||||
|
* <browser> element, and that this properly sets the referrer on the newly
|
||||||
|
* loaded document.
|
||||||
|
*/
|
||||||
|
add_task(async function target_to_new_blank_browser() {
|
||||||
|
let win = await BrowserTestUtils.openNewBrowserWindow();
|
||||||
|
let originalTab = win.gBrowser.selectedTab;
|
||||||
|
let originalBrowser = originalTab.linkedBrowser;
|
||||||
|
BrowserTestUtils.loadURI(originalBrowser, TEST_PAGE);
|
||||||
|
await BrowserTestUtils.browserLoaded(originalBrowser, false, TEST_PAGE);
|
||||||
|
|
||||||
|
// Now set the targetTopLevelLinkClicksToBlank property to true, since it
|
||||||
|
// defaults to false.
|
||||||
|
originalBrowser.browsingContext.targetTopLevelLinkClicksToBlank = true;
|
||||||
|
|
||||||
|
let newTabPromise = BrowserTestUtils.waitForNewTab(win.gBrowser, LINK_URL);
|
||||||
|
await SpecialPowers.spawn(originalBrowser, [], async () => {
|
||||||
|
let anchor = content.document.querySelector(`a[href=".."]`);
|
||||||
|
let userInput = content.windowUtils.setHandlingUserInput(true);
|
||||||
|
try {
|
||||||
|
anchor.click();
|
||||||
|
} finally {
|
||||||
|
userInput.destruct();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let newTab = await newTabPromise;
|
||||||
|
let newBrowser = newTab.linkedBrowser;
|
||||||
|
|
||||||
|
Assert.ok(
|
||||||
|
originalBrowser !== newBrowser,
|
||||||
|
"A new browser should have been created."
|
||||||
|
);
|
||||||
|
await SpecialPowers.spawn(newBrowser, [TEST_PAGE], async referrer => {
|
||||||
|
Assert.equal(
|
||||||
|
content.document.referrer,
|
||||||
|
referrer,
|
||||||
|
"Should have gotten the right referrer set"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
await BrowserTestUtils.switchTab(win.gBrowser, originalTab);
|
||||||
|
BrowserTestUtils.removeTab(newTab);
|
||||||
|
|
||||||
|
// Now do the same thing with a subframe targeting "_top". This should also
|
||||||
|
// get redirected to "_blank".
|
||||||
|
BrowserTestUtils.loadURI(originalBrowser, TEST_IFRAME_PAGE);
|
||||||
|
await BrowserTestUtils.browserLoaded(
|
||||||
|
originalBrowser,
|
||||||
|
false,
|
||||||
|
TEST_IFRAME_PAGE
|
||||||
|
);
|
||||||
|
|
||||||
|
newTabPromise = BrowserTestUtils.waitForNewTab(win.gBrowser, LINK_URL);
|
||||||
|
let frameBC1 = originalBrowser.browsingContext.children[0];
|
||||||
|
Assert.ok(frameBC1, "Should have found a subframe BrowsingContext");
|
||||||
|
|
||||||
|
await SpecialPowers.spawn(frameBC1, [LINK_URL], async linkUrl => {
|
||||||
|
let anchor = content.document.createElement("a");
|
||||||
|
anchor.setAttribute("href", linkUrl);
|
||||||
|
anchor.setAttribute("target", "_top");
|
||||||
|
content.document.body.appendChild(anchor);
|
||||||
|
let userInput = content.windowUtils.setHandlingUserInput(true);
|
||||||
|
try {
|
||||||
|
anchor.click();
|
||||||
|
} finally {
|
||||||
|
userInput.destruct();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
newTab = await newTabPromise;
|
||||||
|
newBrowser = newTab.linkedBrowser;
|
||||||
|
|
||||||
|
Assert.ok(
|
||||||
|
originalBrowser !== newBrowser,
|
||||||
|
"A new browser should have been created."
|
||||||
|
);
|
||||||
|
await SpecialPowers.spawn(
|
||||||
|
newBrowser,
|
||||||
|
[frameBC1.currentURI.spec],
|
||||||
|
async referrer => {
|
||||||
|
Assert.equal(
|
||||||
|
content.document.referrer,
|
||||||
|
referrer,
|
||||||
|
"Should have gotten the right referrer set"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
await BrowserTestUtils.switchTab(win.gBrowser, originalTab);
|
||||||
|
BrowserTestUtils.removeTab(newTab);
|
||||||
|
|
||||||
|
await BrowserTestUtils.closeWindow(win);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that we don't target to _blank loads caused by:
|
||||||
|
* 1. POST requests
|
||||||
|
* 2. Any load that isn't "normal" (in the nsIDocShell.LOAD_CMD_NORMAL sense)
|
||||||
|
* 3. Any loads that are caused by location.replace
|
||||||
|
* 4. Any loads that were caused by setting location.href
|
||||||
|
* 5. Link clicks fired without user interaction.
|
||||||
|
*/
|
||||||
|
add_task(async function skip_blank_target_for_some_loads() {
|
||||||
|
let win = await BrowserTestUtils.openNewBrowserWindow();
|
||||||
|
let currentBrowser = win.gBrowser.selectedBrowser;
|
||||||
|
BrowserTestUtils.loadURI(currentBrowser, TEST_PAGE);
|
||||||
|
await BrowserTestUtils.browserLoaded(currentBrowser, false, TEST_PAGE);
|
||||||
|
|
||||||
|
// Now set the targetTopLevelLinkClicksToBlank property to true, since it
|
||||||
|
// defaults to false.
|
||||||
|
currentBrowser.browsingContext.targetTopLevelLinkClicksToBlank = true;
|
||||||
|
|
||||||
|
let ensureSingleBrowser = () => {
|
||||||
|
Assert.equal(
|
||||||
|
win.gBrowser.browsers.length,
|
||||||
|
1,
|
||||||
|
"There should only be 1 browser."
|
||||||
|
);
|
||||||
|
|
||||||
|
Assert.ok(
|
||||||
|
currentBrowser.browsingContext.targetTopLevelLinkClicksToBlank,
|
||||||
|
"Should still be targeting top-level clicks to _blank"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// First we'll test a POST request
|
||||||
|
let sameBrowserLoad = BrowserTestUtils.browserLoaded(
|
||||||
|
currentBrowser,
|
||||||
|
false,
|
||||||
|
TEST_PAGE
|
||||||
|
);
|
||||||
|
await SpecialPowers.spawn(currentBrowser, [], async () => {
|
||||||
|
let doc = content.document;
|
||||||
|
let form = doc.createElement("form");
|
||||||
|
form.setAttribute("method", "post");
|
||||||
|
doc.body.appendChild(form);
|
||||||
|
let userInput = content.windowUtils.setHandlingUserInput(true);
|
||||||
|
try {
|
||||||
|
form.submit();
|
||||||
|
} finally {
|
||||||
|
userInput.destruct();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await sameBrowserLoad;
|
||||||
|
ensureSingleBrowser();
|
||||||
|
|
||||||
|
// Next, we'll try a non-normal load - specifically, we'll try a reload.
|
||||||
|
// Since we've got a page loaded via a POST request, an attempt to reload
|
||||||
|
// will cause the "repost" dialog to appear, so we temporarily allow the
|
||||||
|
// repost to go through with the always_accept testing pref.
|
||||||
|
await SpecialPowers.pushPrefEnv({
|
||||||
|
set: [["dom.confirm_repost.testing.always_accept", true]],
|
||||||
|
});
|
||||||
|
sameBrowserLoad = BrowserTestUtils.browserLoaded(
|
||||||
|
currentBrowser,
|
||||||
|
false,
|
||||||
|
TEST_PAGE
|
||||||
|
);
|
||||||
|
await SpecialPowers.spawn(currentBrowser, [], async () => {
|
||||||
|
let userInput = content.windowUtils.setHandlingUserInput(true);
|
||||||
|
try {
|
||||||
|
content.location.reload();
|
||||||
|
} finally {
|
||||||
|
userInput.destruct();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await sameBrowserLoad;
|
||||||
|
ensureSingleBrowser();
|
||||||
|
await SpecialPowers.popPrefEnv();
|
||||||
|
|
||||||
|
// Next, we'll try a location.replace
|
||||||
|
sameBrowserLoad = BrowserTestUtils.browserLoaded(
|
||||||
|
currentBrowser,
|
||||||
|
false,
|
||||||
|
TEST_PAGE_2
|
||||||
|
);
|
||||||
|
await SpecialPowers.spawn(currentBrowser, [TEST_PAGE_2], async page2 => {
|
||||||
|
let userInput = content.windowUtils.setHandlingUserInput(true);
|
||||||
|
try {
|
||||||
|
content.location.replace(page2);
|
||||||
|
} finally {
|
||||||
|
userInput.destruct();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await sameBrowserLoad;
|
||||||
|
ensureSingleBrowser();
|
||||||
|
|
||||||
|
// Finally we'll try setting location.href
|
||||||
|
sameBrowserLoad = BrowserTestUtils.browserLoaded(
|
||||||
|
currentBrowser,
|
||||||
|
false,
|
||||||
|
TEST_PAGE
|
||||||
|
);
|
||||||
|
await SpecialPowers.spawn(currentBrowser, [TEST_PAGE], async page1 => {
|
||||||
|
let userInput = content.windowUtils.setHandlingUserInput(true);
|
||||||
|
try {
|
||||||
|
content.location.href = page1;
|
||||||
|
} finally {
|
||||||
|
userInput.destruct();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await sameBrowserLoad;
|
||||||
|
ensureSingleBrowser();
|
||||||
|
|
||||||
|
// Now that we're back at TEST_PAGE, let's try a scripted link click. This
|
||||||
|
// shouldn't target to _blank.
|
||||||
|
sameBrowserLoad = BrowserTestUtils.browserLoaded(
|
||||||
|
currentBrowser,
|
||||||
|
false,
|
||||||
|
LINK_URL
|
||||||
|
);
|
||||||
|
await SpecialPowers.spawn(currentBrowser, [], async () => {
|
||||||
|
let anchor = content.document.querySelector(`a[href=".."]`);
|
||||||
|
anchor.click();
|
||||||
|
});
|
||||||
|
await sameBrowserLoad;
|
||||||
|
ensureSingleBrowser();
|
||||||
|
|
||||||
|
// A javascript:void(0); link should also not target to _blank.
|
||||||
|
sameBrowserLoad = BrowserTestUtils.browserLoaded(
|
||||||
|
currentBrowser,
|
||||||
|
false,
|
||||||
|
TEST_PAGE
|
||||||
|
);
|
||||||
|
await SpecialPowers.spawn(currentBrowser, [TEST_PAGE], async newPageURL => {
|
||||||
|
let anchor = content.document.querySelector(`a[href=".."]`);
|
||||||
|
anchor.href = "javascript:void(0);";
|
||||||
|
anchor.addEventListener("click", e => {
|
||||||
|
content.location.href = newPageURL;
|
||||||
|
});
|
||||||
|
let userInput = content.windowUtils.setHandlingUserInput(true);
|
||||||
|
try {
|
||||||
|
anchor.click();
|
||||||
|
} finally {
|
||||||
|
userInput.destruct();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await sameBrowserLoad;
|
||||||
|
ensureSingleBrowser();
|
||||||
|
|
||||||
|
// Let's also try a non-void javascript: location.
|
||||||
|
sameBrowserLoad = BrowserTestUtils.browserLoaded(
|
||||||
|
currentBrowser,
|
||||||
|
false,
|
||||||
|
TEST_PAGE
|
||||||
|
);
|
||||||
|
await SpecialPowers.spawn(currentBrowser, [TEST_PAGE], async newPageURL => {
|
||||||
|
let anchor = content.document.querySelector(`a[href=".."]`);
|
||||||
|
anchor.href = `javascript:"string-to-navigate-to"`;
|
||||||
|
anchor.addEventListener("click", e => {
|
||||||
|
content.location.href = newPageURL;
|
||||||
|
});
|
||||||
|
let userInput = content.windowUtils.setHandlingUserInput(true);
|
||||||
|
try {
|
||||||
|
anchor.click();
|
||||||
|
} finally {
|
||||||
|
userInput.destruct();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await sameBrowserLoad;
|
||||||
|
ensureSingleBrowser();
|
||||||
|
|
||||||
|
await BrowserTestUtils.closeWindow(win);
|
||||||
|
});
|
||||||
@@ -204,6 +204,12 @@ interface BrowsingContext {
|
|||||||
*/
|
*/
|
||||||
readonly attribute TouchEventsOverride touchEventsOverride;
|
readonly attribute TouchEventsOverride touchEventsOverride;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the top-level BrowsingContext has been configured to
|
||||||
|
* default-target user-initiated link clicks to _blank.
|
||||||
|
*/
|
||||||
|
readonly attribute boolean targetTopLevelLinkClicksToBlank;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Partially determines whether script execution is allowed in this
|
* Partially determines whether script execution is allowed in this
|
||||||
* BrowsingContext. Script execution will be permitted only if this
|
* BrowsingContext. Script execution will be permitted only if this
|
||||||
@@ -323,6 +329,12 @@ interface CanonicalBrowsingContext : BrowsingContext {
|
|||||||
*/
|
*/
|
||||||
[SetterThrows] inherit attribute TouchEventsOverride touchEventsOverride;
|
[SetterThrows] inherit attribute TouchEventsOverride touchEventsOverride;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set to true to configure the top-level BrowsingContext to default-target
|
||||||
|
* user-initiated link clicks to _blank.
|
||||||
|
*/
|
||||||
|
[SetterThrows] inherit attribute boolean targetTopLevelLinkClicksToBlank;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the cross-group opener of this BrowsingContext. This is used to
|
* Set the cross-group opener of this BrowsingContext. This is used to
|
||||||
* retarget the download dialog to an opener window, and close this
|
* retarget the download dialog to an opener window, and close this
|
||||||
|
|||||||
Reference in New Issue
Block a user