Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support access keys in extension menus
base on following bugs: Bug 1320462 - Support access keys in extension menus. r=mixedpuppy Bug 1484914 - Never append duplicate access key hints r=bzbarsky
- Loading branch information
ChiaYen Kan
committed
Aug 17, 2019
1 parent
b1e0f2a
commit 6692fe7
Showing
5 changed files
with
207 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
183 changes: 183 additions & 0 deletions
183
browser/components/extensions/test/browser/browser_ext_menus_accesskey.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters