Bug 1344749 - Expose API to customize where new tabs open, r=dao,mixedpuppy

This patch implements the preference "browser.tabs.insertAfterCurrent" which,
when set to true, will cause all tabs (related and unrelated) to be opened next
to the current tab.

It also implements the browserSettings API "newTabPosition", which allows
extensions to control both "browser.tabs.insertRelatedAfterCurrent", and
"browser.tabs.insertAfterCurrent" via values for "afterCurrent",
"relatedAfterCurrent" and "atEnd".

The code for "browser.tabs.insertAfterCurrent" including the test for it is
mostly taken from a patch attached to bug 933532 written by Masayuki Nakano.

MozReview-Commit-ID: KQE7M2FGpc7

--HG--
extra : rebase_source : 71dac785c2edbf9edaa79e0e99dca6714addc9d8
This commit is contained in:
Bob Silverberg 2018-01-19 12:59:53 -05:00
Родитель ae8b52cc98
Коммит ee896b579b
8 изменённых файлов: 177 добавлений и 12 удалений

Просмотреть файл

@ -458,7 +458,15 @@ pref("browser.link.open_newwindow.disabled_in_fullscreen", false);
// Tabbed browser
pref("browser.tabs.closeWindowWithLastTab", true);
// Open related links to a tab, e.g., link in current tab, at next to the
// current tab if |insertRelatedAfterCurrent| is true. Otherwise, always
// append new tab to the end.
pref("browser.tabs.insertRelatedAfterCurrent", true);
// Open all links, e.g., bookmarks, history items at next to current tab
// if |insertAfterCurrent| is true. Otherwise, append new tab to the end
// for non-related links. Note that if this is set to true, it will trump
// the value of browser.tabs.insertRelatedAfterCurrent.
pref("browser.tabs.insertAfterCurrent", false);
pref("browser.tabs.warnOnClose", true);
pref("browser.tabs.warnOnCloseOtherTabs", true);
pref("browser.tabs.warnOnOpen", true);

Просмотреть файл

@ -2480,20 +2480,22 @@ class TabBrowser {
}
}
// If we're opening a tab related to the an existing tab, move it
// to a position after that tab.
if (openerTab &&
Services.prefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent")) {
// Move the new tab after another tab if needed.
if ((openerTab &&
Services.prefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent")) ||
Services.prefs.getBoolPref("browser.tabs.insertAfterCurrent")) {
let lastRelatedTab = this._lastRelatedTabMap.get(openerTab);
let newTabPos = (lastRelatedTab || openerTab)._tPos + 1;
if (lastRelatedTab)
lastRelatedTab.owner = null;
else
t.owner = openerTab;
this.moveTabTo(t, newTabPos, true);
let lastRelatedTab = openerTab && this._lastRelatedTabMap.get(openerTab);
let newTabPos = (lastRelatedTab || openerTab || this.mCurrentTab)._tPos + 1;
if (lastRelatedTab)
lastRelatedTab.owner = null;
else if (openerTab)
t.owner = openerTab;
this.moveTabTo(t, newTabPos, true);
if (openerTab)
this._lastRelatedTabMap.set(openerTab, t);
}
}
// This field is updated regardless if we actually animate
// since it's important that we keep this count correct in all cases.

Просмотреть файл

@ -23,6 +23,8 @@ skip-if = !e10s # Test only relevant for e10s.
skip-if = !e10s # Pref and test only relevant for e10s.
[browser_newwindow_tabstrip_overflow.js]
[browser_opened_file_tab_navigated_to_web.js]
[browser_new_tab_insert_position.js]
support-files = file_new_tab_page.html
[browser_overflowScroll.js]
[browser_pinnedTabs.js]
[browser_pinnedTabs_closeByKeyboard.js]

Просмотреть файл

@ -0,0 +1,94 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
async function doTest(aInsertRelatedAfterCurrent, aInsertAfterCurrent) {
const kDescription = "(aInsertRelatedAfterCurrent=" + aInsertRelatedAfterCurrent +
", aInsertAfterCurrent=" + aInsertAfterCurrent + "): ";
is(gBrowser.tabs.length, 1, kDescription + "one tab is open initially");
await SpecialPowers.pushPrefEnv({set: [
["browser.tabs.opentabfor.middleclick", true],
["browser.tabs.loadBookmarksInBackground", false],
["browser.tabs.insertRelatedAfterCurrent", aInsertRelatedAfterCurrent],
["browser.tabs.insertAfterCurrent", aInsertAfterCurrent]
]});
// Add a few tabs.
let tabs = [];
function addTab(aURL, aReferrer) {
let tab = BrowserTestUtils.addTab(gBrowser, aURL, {referrerURI: aReferrer});
tabs.push(tab);
return BrowserTestUtils.browserLoaded(tab.linkedBrowser);
}
await addTab("http://mochi.test:8888/#0");
await addTab("http://mochi.test:8888/#1");
await addTab("http://mochi.test:8888/#2");
await addTab("http://mochi.test:8888/#3");
// Create a new tab page which has a link to "example.com".
let pageURL = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.com");
pageURL = `${pageURL}file_new_tab_page.html`;
let newTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, pageURL);
let newTabURISpec = newTab.linkedBrowser.currentURI.spec;
const kNewTabIndex = 1;
gBrowser.moveTabTo(newTab, kNewTabIndex);
let openTabIndex = aInsertRelatedAfterCurrent || aInsertAfterCurrent ?
kNewTabIndex + 1 : gBrowser.tabs.length;
let openTabDescription = aInsertRelatedAfterCurrent || aInsertAfterCurrent ?
"immediately to the right" : "at rightmost";
// Middle click on the cell should open example.com in a new tab.
let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, "http://example.com/", true);
await BrowserTestUtils.synthesizeMouseAtCenter("#link_to_example_com",
{button: 1}, gBrowser.selectedBrowser);
let openTab = await newTabPromise;
is(openTab.linkedBrowser.currentURI.spec, "http://example.com/",
"Middle click should open site to correct url.");
is(openTab._tPos, openTabIndex,
kDescription + "Middle click should open site in a new tab " + openTabDescription);
// Remove the new opened tab which loaded example.com.
gBrowser.removeTab(gBrowser.tabs[openTabIndex]);
// Go back to the new tab.
gBrowser.selectedTab = newTab;
is(gBrowser.selectedBrowser.currentURI.spec, newTabURISpec,
kDescription + "New tab URI shouldn't be changed");
openTabIndex = aInsertAfterCurrent ? kNewTabIndex + 1 : gBrowser.tabs.length;
openTabDescription = aInsertAfterCurrent ? "immediately to the right" : "at rightmost";
// Open about:mozilla in new tab from the URL bar.
gURLBar.focus();
gURLBar.select();
newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, "about:mozilla");
EventUtils.sendString("about:mozilla");
EventUtils.synthesizeKey("KEY_Alt", { altKey: true, code: "AltLeft", type: "keydown" });
EventUtils.synthesizeKey("KEY_Enter", { altKey: true, code: "Enter" });
EventUtils.synthesizeKey("KEY_Alt", { altKey: false, code: "AltLeft", type: "keyup" });
openTab = await newTabPromise;
is(newTab.linkedBrowser.currentURI.spec, newTabURISpec,
kDescription + "example.com should be loaded in current tab.");
is(openTab.linkedBrowser.currentURI.spec, "about:mozilla",
kDescription + "about:mozilla should be loaded in the new tab.");
is(openTab._tPos, openTabIndex,
kDescription + "Alt+Enter in the URL bar should open page in a new tab " + openTabDescription);
// Remove all tabs opened by this test.
while (gBrowser.tabs[1]) {
gBrowser.removeTab(gBrowser.tabs[1]);
}
}
add_task(async function() {
// Current default settings.
await doTest(true, false);
// Perhaps, some users would love this settings.
await doTest(true, true);
// Maybe, unrealistic cases, but we should test these cases too.
await doTest(false, true);
await doTest(false, false);
});

Просмотреть файл

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<a href="http://example.com/" id="link_to_example_com">go to example.com</a>
</body>
</html>

Просмотреть файл

@ -125,6 +125,20 @@ ExtensionPreferencesManager.addSetting("imageAnimationBehavior", {
},
});
ExtensionPreferencesManager.addSetting("newTabPosition", {
prefNames: [
"browser.tabs.insertRelatedAfterCurrent",
"browser.tabs.insertAfterCurrent",
],
setCallback(value) {
return {
"browser.tabs.insertAfterCurrent": value === "afterCurrent",
"browser.tabs.insertRelatedAfterCurrent": value === "relatedAfterCurrent",
};
},
});
ExtensionPreferencesManager.addSetting("openBookmarksInNewTabs", {
prefNames: [
"browser.tabs.loadBookmarksInTabs",
@ -260,6 +274,17 @@ this.browserSettings = class extends ExtensionAPI {
() => {
return Services.prefs.getCharPref("image.animation_mode");
}),
newTabPosition: getSettingsAPI(
extension, "newTabPosition",
() => {
if (Services.prefs.getBoolPref("browser.tabs.insertAfterCurrent")) {
return "afterCurrent";
}
if (Services.prefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent")) {
return "relatedAfterCurrent";
}
return "atEnd";
}),
newTabPageOverride: getSettingsAPI(
extension, NEW_TAB_OVERRIDE_SETTING,
() => {

Просмотреть файл

@ -133,6 +133,10 @@
"$ref": "types.Setting",
"description": "Returns the value of the overridden new tab page. Read-only."
},
"newTabPosition": {
"$ref": "types.Setting",
"description": "Controls where new tabs are opened. `afterCurrent` will open all new tabs next to the current tab, `relatedAfterCurrent` will open only related tabs next to the current tab, and `atEnd` will open all tabs at the end of the tab strip. The default is `relatedAfterCurrent`."
},
"openBookmarksInNewTabs": {
"$ref": "types.Setting",
"description": "This boolean setting controls whether bookmarks are opened in the current tab or in a new tab."

Просмотреть файл

@ -47,6 +47,8 @@ add_task(async function test_browser_settings() {
"network.proxy.no_proxies_on": "localhost, 127.0.0.1",
"network.proxy.autoconfig_url": "",
"signon.autologin.proxy": false,
"browser.tabs.insertRelatedAfterCurrent": true,
"browser.tabs.insertAfterCurrent": false,
};
async function background() {
@ -164,6 +166,25 @@ add_task(async function test_browser_settings() {
{"ui.context_menus.after_mouseup": false});
}
await testSetting(
"newTabPosition", "afterCurrent",
{
"browser.tabs.insertRelatedAfterCurrent": false,
"browser.tabs.insertAfterCurrent": true,
});
await testSetting(
"newTabPosition", "atEnd",
{
"browser.tabs.insertRelatedAfterCurrent": false,
"browser.tabs.insertAfterCurrent": false,
});
await testSetting(
"newTabPosition", "relatedAfterCurrent",
{
"browser.tabs.insertRelatedAfterCurrent": true,
"browser.tabs.insertAfterCurrent": false,
});
await testSetting(
"openBookmarksInNewTabs", true,
{"browser.tabs.loadBookmarksInTabs": true});