Bug 1320462 - Support access keys in extension menus. r=mixedpuppy

MozReview-Commit-ID: GsLE3PwNeiC

--HG--
extra : rebase_source : d34b20a2e6ef81623564fd1774a034ab8af9063e
This commit is contained in:
Rob Wu 2018-08-06 16:58:21 +02:00
Родитель 3c600b585e
Коммит fe2be1dd11
3 изменённых файлов: 200 добавлений и 0 удалений

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

@ -198,6 +198,22 @@ var gMenuBuilder = {
customizeElement(element, item, contextData) {
let label = item.title;
if (label) {
let accessKey;
label = label.replace(/&([\S\s]|$)/g, (_, nextChar, i) => {
if (nextChar === "&") {
return "&";
}
if (accessKey === undefined) {
if (nextChar === "%" && label.charAt(i + 2) === "s") {
accessKey = "";
} else {
accessKey = nextChar;
}
}
return nextChar;
});
element.setAttribute("accesskey", accessKey || "");
if (contextData.isTextSelected && label.indexOf("%s") > -1) {
let selection = contextData.selectionText.trim();
// The rendering engine will truncate the title if it's longer than 64 characters.

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

@ -105,6 +105,7 @@ skip-if = (verify && (os == 'linux' || os == 'mac'))
[browser_ext_incognito_popup.js]
[browser_ext_lastError.js]
[browser_ext_menus.js]
[browser_ext_menus_accesskey.js]
[browser_ext_menus_activeTab.js]
[browser_ext_menus_errors.js]
[browser_ext_menus_event_order.js]

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

@ -0,0 +1,183 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
const PAGE = "http://mochi.test:8888/browser/browser/components/extensions/test/browser/context.html";
add_task(async function accesskeys() {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
gBrowser.selectedTab = tab;
async function background() {
// description is informative.
// title is passed to menus.create.
// label and key are compared with the actual values.
const TESTCASES = [{
description: "Amp at start",
title: "&accesskey",
label: "accesskey",
key: "a",
}, {
description: "amp in between",
title: "A& b",
label: "A b",
key: " ",
}, {
description: "lonely amp",
title: "&",
label: "",
key: "",
}, {
description: "amp at end",
title: "End &",
label: "End ",
key: "",
}, {
description: "escaped amp",
title: "A && B",
label: "A & B",
key: "",
}, {
description: "amp before escaped amp",
title: "A &T&& before",
label: "A T& before",
key: "T",
}, {
description: "amp after escaped amp",
title: "A &&&T after",
label: "A &T after",
key: "T",
}, {
// Only the first amp should be used as the access key.
description: "amp, escaped amp, amp to ignore",
title: "First &1 comes && first &2 serves",
label: "First 1 comes & first 2 serves",
key: "1",
}, {
description: "created with amp, updated without amp",
title: "temp with &X", // will be updated below.
label: "remove amp",
key: "",
}, {
description: "created without amp, update with amp",
title: "temp without access key", // will be updated below.
label: "add ampY",
key: "Y",
}];
let menuIds = TESTCASES.map(({title}) => browser.menus.create({title}));
// Should clear the access key:
await browser.menus.update(menuIds[menuIds.length - 2], {title: "remove amp"});
// Should add an access key:
await browser.menus.update(menuIds[menuIds.length - 1], {title: "add amp&Y"});
// Should not clear the access key because title is not set:
await browser.menus.update(menuIds[menuIds.length - 1], {enabled: true});
browser.test.sendMessage("testCases", TESTCASES);
}
let extension = ExtensionTestUtils.loadExtension({
manifest: {
permissions: ["menus"],
},
background,
});
await extension.startup();
const TESTCASES = await extension.awaitMessage("testCases");
let menu = await openExtensionContextMenu();
let items = menu.getElementsByTagName("menuitem");
is(items.length, TESTCASES.length, "Expected menu items for page");
TESTCASES.forEach(({description, label, key}, i) => {
is(items[i].label, label, `Label for item ${i} (${description})`);
is(items[i].accessKey, key, `Accesskey for item ${i} (${description})`);
});
await closeExtensionContextMenu();
await extension.unload();
BrowserTestUtils.removeTab(tab);
});
add_task(async function accesskeys_selection() {
const PAGE_WITH_AMPS = "data:text/plain;charset=utf-8,PageSelection&Amp";
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE_WITH_AMPS);
gBrowser.selectedTab = tab;
async function background() {
const TESTCASES = [{
description: "Selection without amp",
title: "percent-s: %s.",
label: "percent-s: PageSelection&Amp.",
key: "",
}, {
description: "Selection with amp after %s",
title: "percent-s: %s &A.",
label: "percent-s: PageSelection&Amp A.",
key: "A",
}, {
description: "Selection with amp before %s",
title: "percent-s: &B %s.",
label: "percent-s: B PageSelection&Amp.",
key: "B",
}, {
description: "Amp-percent",
title: "Amp-percent: &%.",
label: "Amp-percent: %.",
key: "%",
}, {
// "&%s" should be treated as "%s", and "ignore this" with amps should be ignored.
description: "Selection with amp-percent-s",
title: "Amp-percent-s: &%s.&i&g&n&o&r&e& &t&h&i&s",
label: "Amp-percent-s: PageSelection&Amp.ignore this",
// Chrome uses the first character of the selection as access key.
// Let's not copy that behavior...
key: "",
}, {
description: "Selection with amp before amp-percent-s",
title: "Amp-percent-s: &_ &%s.",
label: "Amp-percent-s: _ PageSelection&Amp.",
key: "_",
}];
let lastMenuId;
for (let {title} of TESTCASES) {
lastMenuId = browser.menus.create({contexts: ["selection"], title});
}
// Round-trip to ensure that the menus have been registered.
await browser.menus.update(lastMenuId, {});
browser.test.sendMessage("testCases", TESTCASES);
}
let extension = ExtensionTestUtils.loadExtension({
manifest: {
permissions: ["menus"],
},
background,
});
await extension.startup();
// Select all
await ContentTask.spawn(gBrowser.selectedBrowser, { }, function* (arg) {
let doc = content.document;
let range = doc.createRange();
let selection = content.getSelection();
selection.removeAllRanges();
range.selectNodeContents(doc.body);
selection.addRange(range);
});
const TESTCASES = await extension.awaitMessage("testCases");
let menu = await openExtensionContextMenu();
let items = menu.getElementsByTagName("menuitem");
is(items.length, TESTCASES.length, "Expected menu items for page");
TESTCASES.forEach(({description, label, key}, i) => {
is(items[i].label, label, `Label for item ${i} (${description})`);
is(items[i].accessKey, key, `Accesskey for item ${i} (${description})`);
});
await closeExtensionContextMenu();
await extension.unload();
BrowserTestUtils.removeTab(tab);
});