Skip to content

Commit

Permalink
Bug 1503681 - rel=noopener implicit for target=_blank in anchor and a…
Browse files Browse the repository at this point in the history
…rea elements when no rel attribute is set, r=nika

In case anchor and area elements have target=_blank and no rel=opener/noopener,
this patch makes so that rel=noopener is implied. This feature is behind pref
'dom_targetBlankNoOpener_enabled'.

See: whatwg/html#4078
  • Loading branch information
bakulf committed Nov 28, 2018
1 parent b58735b commit fd04f3b
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 1 deletion.
27 changes: 26 additions & 1 deletion docshell/base/nsDocShell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13281,18 +13281,43 @@ nsDocShell::OnLinkClickSync(nsIContent* aContent,
nsAutoString referrer;
aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, referrer);
nsWhitespaceTokenizerTemplate<nsContentUtils::IsHTMLWhitespace> tok(referrer);

bool targetBlank = aTargetSpec.LowerCaseEqualsLiteral("_blank");
bool explicitOpenerSet = false;

// The opener behaviour follows a hierarchy, such that if a higher priority
// behaviour is specified, it always takes priority. That priority is
// currently: norefrerer > noopener > opener > default

while (tok.hasMoreTokens()) {
const nsAString& token = tok.nextToken();
if (token.LowerCaseEqualsLiteral("noreferrer")) {
flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER |
INTERNAL_LOAD_FLAGS_NO_OPENER;
// We now have all the flags we could possibly have, so just stop.
// noreferrer cannot be overwritten by a 'rel=opener'.
explicitOpenerSet = true;
break;
}

if (token.LowerCaseEqualsLiteral("noopener")) {
flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
explicitOpenerSet = true;
}

if (targetBlank &&
StaticPrefs::dom_targetBlankNoOpener_enabled() &&
token.LowerCaseEqualsLiteral("opener") &&
!explicitOpenerSet) {
explicitOpenerSet = true;
}
}

if (targetBlank &&
StaticPrefs::dom_targetBlankNoOpener_enabled() &&
!explicitOpenerSet) {
flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
}

if (aNoOpenerImplied) {
flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
}
Expand Down
4 changes: 4 additions & 0 deletions dom/html/test/browser.ini
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,7 @@ skip-if = os == 'mac' # bug 1494843
[browser_refresh_wyciwyg_url.js]
support-files =
file_refresh_wyciwyg_url.html
[browser_targetBlankNoOpener.js]
support-files =
empty.html
image_yellow.png
83 changes: 83 additions & 0 deletions dom/html/test/browser_targetBlankNoOpener.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
const TEST_URL = "http://mochi.test:8888/browser/dom/html/test/empty.html";

async function checkOpener(browser, elm, name, rel) {
let p = BrowserTestUtils.waitForNewTab(gBrowser, null, true, true);

await ContentTask.spawn(browser, {url: TEST_URL, name, rel, elm }, async obj => {
let element;

if (obj.elm == "anchor") {
element = content.document.createElement("a");
content.document.body.appendChild(element);
element.appendChild(content.document.createTextNode(obj.name));
} else {
let img = content.document.createElement('img');
img.src = "image_yellow.png";
content.document.body.appendChild(img);

element = content.document.createElement("area");
img.appendChild(element);

element.setAttribute("shape", "rect");
element.setAttribute("coords", "0,0,100,100");
}

element.setAttribute("target", "_blank");
element.setAttribute("href", obj.url);

if (obj.rel) {
element.setAttribute("rel", obj.rel);
}

element.click();
});

let newTab = await p;
let newBrowser = gBrowser.getBrowserForTab(newTab);

let hasOpener = await ContentTask.spawn(newTab.linkedBrowser, null, _ => !!content.window.opener);

BrowserTestUtils.removeTab(newTab);
return hasOpener;
}

async function runTests(browser, elm) {
info("Creating an " + elm + " with target=_blank rel=opener");
ok(!!(await checkOpener(browser, elm, "rel=opener", "opener")), "We want the opener with rel=opener");

info("Creating an " + elm + " with target=_blank rel=noopener");
ok(!(await checkOpener(browser, elm, "rel=noopener", "noopener")), "We don't want the opener with rel=noopener");

info("Creating an " + elm + " with target=_blank");
ok(!(await checkOpener(browser, elm, "no rel", null)), "We don't want the opener with no rel is passed");

info("Creating an " + elm + " with target=_blank rel='noopener opener'");
ok(!(await checkOpener(browser, elm, "rel=noopener+opener", "noopener opener")), "noopener wins with rel=noopener+opener");

info("Creating an " + elm + " with target=_blank rel='noreferrer opener'");
ok(!(await checkOpener(browser, elm, "noreferrer wins", "noreferrer opener")), "We don't want the opener with rel=noreferrer+opener");

info("Creating an " + elm + " with target=_blank rel='opener noreferrer'");
ok(!(await checkOpener(browser, elm, "noreferrer wins again", "noreferrer opener")), "We don't want the opener with rel=opener+noreferrer");
}

add_task(async _ => {
await SpecialPowers.flushPrefEnv();
await SpecialPowers.pushPrefEnv({"set": [
["dom.block_multiple_popups", false],
["dom.disable_open_during_load", true],
["dom.targetBlankNoOpener.enabled", true],
]});

let tab = BrowserTestUtils.addTab(gBrowser, TEST_URL);
gBrowser.selectedTab = tab;

let browser = gBrowser.getBrowserForTab(tab);
await BrowserTestUtils.browserLoaded(browser);

await runTests(browser, 'anchor');
await runTests(browser, 'area');

info("Removing the tab");
BrowserTestUtils.removeTab(tab);
});
1 change: 1 addition & 0 deletions dom/html/test/empty.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<html><body></body></html>
Binary file added dom/html/test/image_yellow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions modules/libpref/init/StaticPrefList.h
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,20 @@ VARCACHE_PREF(
bool, true
)

// For area and anchor elements with target=_blank and no rel set to
// opener/noopener, this pref sets noopener by default.
#ifdef EARLY_BETA_OR_EARLIER
#define PREF_VALUE true
#else
#define PREF_VALUE false
#endif
VARCACHE_PREF(
"dom.targetBlankNoOpener.enabled",
dom_targetBlankNoOpener_enabled,
bool, PREF_VALUE
)
#undef PREF_VALUE

//---------------------------------------------------------------------------
// Clear-Site-Data prefs
//---------------------------------------------------------------------------
Expand Down

0 comments on commit fd04f3b

Please sign in to comment.