Bug 380637, add a general preference to prevent pages from overriding keyboard shortcuts. If a key doesn't specify the reserved attribute, this preference will be used, r=felipe

This commit is contained in:
Neil Deakin 2017-11-09 18:42:39 -05:00
parent 7d3551d146
commit 9bd9459ef2
7 changed files with 79 additions and 10 deletions

View File

@ -429,6 +429,7 @@ pref("permissions.default.camera", 0);
pref("permissions.default.microphone", 0);
pref("permissions.default.geo", 0);
pref("permissions.default.desktop-notification", 0);
pref("permissions.default.shortcuts", 0);
// handle links targeting new windows
// 1=current window/tab, 2=new window, 3=new tab in most recent window

View File

@ -5,6 +5,7 @@ support-files=
[browser_canvas_fingerprinting_resistance.js]
[browser_permissions.js]
[browser_reservedkey.js]
[browser_temporary_permissions.js]
support-files =
temporary_permissions_subframe.html

View File

@ -0,0 +1,44 @@
add_task(async function test_reserved_shortcuts() {
/* eslint-disable no-unsanitized/property */
let keyset = `<keyset>
<key id='kt_reserved' modifiers='shift' key='O' reserved='true' count='0'
oncommand='this.setAttribute("count", Number(this.getAttribute("count")) + 1)'/>
<key id='kt_notreserved' modifiers='shift' key='P' reserved='false' count='0'
oncommand='this.setAttribute("count", Number(this.getAttribute("count")) + 1)'/>
<key id='kt_reserveddefault' modifiers='shift' key='Q' count='0'
oncommand='this.setAttribute("count", Number(this.getAttribute("count")) + 1)'/>
</keyset>`;
let container = document.createElement("box");
container.innerHTML = keyset;
document.documentElement.appendChild(container);
/* eslint-enable no-unsanitized/property */
const pageUrl = "data:text/html,<body onload='document.body.firstChild.focus();'><div onkeydown='event.preventDefault();' tabindex=0>Test</div></body>";
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
EventUtils.synthesizeKey("O", { shiftKey: true });
EventUtils.synthesizeKey("P", { shiftKey: true });
EventUtils.synthesizeKey("Q", { shiftKey: true });
is(document.getElementById("kt_reserved").getAttribute("count"), "1", "reserved='true' with preference off");
is(document.getElementById("kt_notreserved").getAttribute("count"), "0", "reserved='false' with preference off");
is(document.getElementById("kt_reserveddefault").getAttribute("count"), "0", "default reserved with preference off");
// Now try with reserved shortcut key handling enabled.
await new Promise(r => {
SpecialPowers.pushPrefEnv({"set": [["permissions.default.shortcuts", 2]]}, r);
});
EventUtils.synthesizeKey("O", { shiftKey: true });
EventUtils.synthesizeKey("P", { shiftKey: true });
EventUtils.synthesizeKey("Q", { shiftKey: true });
is(document.getElementById("kt_reserved").getAttribute("count"), "2", "reserved='true' with preference on");
is(document.getElementById("kt_notreserved").getAttribute("count"), "0", "reserved='false' with preference on");
is(document.getElementById("kt_reserveddefault").getAttribute("count"), "1", "default reserved with preference on");
document.documentElement.removeChild(container);
await BrowserTestUtils.removeTab(tab);
});

View File

@ -93,7 +93,7 @@ nsXBLPrototypeHandler::nsXBLPrototypeHandler(const char16_t* aEvent,
uint32_t aLineNumber)
: mHandlerText(nullptr),
mLineNumber(aLineNumber),
mReserved(false),
mReserved(XBLReservedKey_False),
mNextHandler(nullptr),
mPrototypeBinding(aBinding)
{
@ -104,7 +104,7 @@ nsXBLPrototypeHandler::nsXBLPrototypeHandler(const char16_t* aEvent,
aGroup, aPreventDefault, aAllowUntrusted);
}
nsXBLPrototypeHandler::nsXBLPrototypeHandler(nsIContent* aHandlerElement, bool aReserved)
nsXBLPrototypeHandler::nsXBLPrototypeHandler(nsIContent* aHandlerElement, XBLReservedKey aReserved)
: mHandlerElement(nullptr),
mLineNumber(0),
mReserved(aReserved),
@ -120,7 +120,7 @@ nsXBLPrototypeHandler::nsXBLPrototypeHandler(nsIContent* aHandlerElement, bool a
nsXBLPrototypeHandler::nsXBLPrototypeHandler(nsXBLPrototypeBinding* aBinding)
: mHandlerText(nullptr),
mLineNumber(0),
mReserved(false),
mReserved(XBLReservedKey_False),
mNextHandler(nullptr),
mPrototypeBinding(aBinding)
{

View File

@ -55,6 +55,15 @@ class KeyboardShortcut;
#define NS_PHASE_TARGET 2
#define NS_PHASE_BUBBLING 3
// Values of the reserved attribute. When unset, the default value depends on
// the permissions.default.shortcuts preference.
enum XBLReservedKey : uint8_t
{
XBLReservedKey_False = 0,
XBLReservedKey_True = 1,
XBLReservedKey_Unset = 2,
};
class nsXBLPrototypeHandler
{
typedef mozilla::IgnoreModifierState IgnoreModifierState;
@ -74,7 +83,7 @@ public:
uint32_t aLineNumber);
// This constructor is used only by XUL key handlers (e.g., <key>)
explicit nsXBLPrototypeHandler(nsIContent* aKeyElement, bool aReserved);
explicit nsXBLPrototypeHandler(nsIContent* aKeyElement, XBLReservedKey aReserved);
// This constructor is used for handlers loaded from the cache
explicit nsXBLPrototypeHandler(nsXBLPrototypeBinding* aBinding);
@ -118,7 +127,7 @@ public:
uint8_t GetPhase() { return mPhase; }
uint8_t GetType() { return mType; }
bool GetIsReserved() { return mReserved; }
XBLReservedKey GetIsReserved() { return mReserved; }
nsXBLPrototypeHandler* GetNextHandler() { return mNextHandler; }
void SetNextHandler(nsXBLPrototypeHandler* aHandler) { mNextHandler = aHandler; }
@ -233,7 +242,7 @@ protected:
// stores whether or not we're a key code or char code.
// For mouse events, stores the clickCount.
bool mReserved; // <key> is reserved for chrome. Not used by handlers.
XBLReservedKey mReserved; // <key> is reserved for chrome. Not used by handlers.
int32_t mKeyMask; // Which modifier keys this event handler expects to have down
// in order to be matched.

View File

@ -211,8 +211,16 @@ BuildHandlerChain(nsIContent* aContent, nsXBLPrototypeHandler** aResult)
valKey.IsEmpty() && valCharCode.IsEmpty() && valKeyCode.IsEmpty())
continue;
bool reserved = key->AttrValueIs(kNameSpaceID_None, nsGkAtoms::reserved,
nsGkAtoms::_true, eCaseMatters);
// reserved="pref" is the default for <key> elements.
XBLReservedKey reserved = XBLReservedKey_Unset;
if (key->AttrValueIs(kNameSpaceID_None, nsGkAtoms::reserved,
nsGkAtoms::_true, eCaseMatters)) {
reserved = XBLReservedKey_True;
} else if (key->AttrValueIs(kNameSpaceID_None, nsGkAtoms::reserved,
nsGkAtoms::_false, eCaseMatters)) {
reserved = XBLReservedKey_False;
}
nsXBLPrototypeHandler* handler = new nsXBLPrototypeHandler(key, reserved);
handler->SetNextHandler(*aResult);
@ -725,7 +733,12 @@ nsXBLWindowKeyHandler::WalkHandlersAndExecute(
continue;
}
bool isReserved = handler->GetIsReserved();
bool isReserved = handler->GetIsReserved() == XBLReservedKey_True;
if (handler->GetIsReserved() == XBLReservedKey_Unset &&
Preferences::GetInt("permissions.default.shortcuts") == 2) {
isReserved = true;
}
if (aOutReservedForChrome) {
*aOutReservedForChrome = isReserved;
}

View File

@ -135,7 +135,8 @@ static const char* kPermissionsWithDefaults[] = {
"camera",
"microphone",
"geo",
"desktop-notification"
"desktop-notification",
"shortcuts"
};
// NOTE: nullptr can be passed as aType - if it is this function will return