From 9bd9459ef262d04831e5fa06bf0132dee6fde89c Mon Sep 17 00:00:00 2001 From: Neil Deakin Date: Thu, 9 Nov 2017 18:42:39 -0500 Subject: [PATCH] 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 --- browser/app/profile/firefox.js | 1 + .../base/content/test/permissions/browser.ini | 1 + .../test/permissions/browser_reservedkey.js | 44 +++++++++++++++++++ dom/xbl/nsXBLPrototypeHandler.cpp | 6 +-- dom/xbl/nsXBLPrototypeHandler.h | 15 +++++-- dom/xbl/nsXBLWindowKeyHandler.cpp | 19 ++++++-- extensions/cookie/nsPermissionManager.cpp | 3 +- 7 files changed, 79 insertions(+), 10 deletions(-) create mode 100644 browser/base/content/test/permissions/browser_reservedkey.js diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index ace53c0c565f..268c73b07adb 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -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 diff --git a/browser/base/content/test/permissions/browser.ini b/browser/base/content/test/permissions/browser.ini index 3fe359a85399..4a146a3b2ba5 100644 --- a/browser/base/content/test/permissions/browser.ini +++ b/browser/base/content/test/permissions/browser.ini @@ -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 diff --git a/browser/base/content/test/permissions/browser_reservedkey.js b/browser/base/content/test/permissions/browser_reservedkey.js new file mode 100644 index 000000000000..f49fe891c11f --- /dev/null +++ b/browser/base/content/test/permissions/browser_reservedkey.js @@ -0,0 +1,44 @@ +add_task(async function test_reserved_shortcuts() { + /* eslint-disable no-unsanitized/property */ + let keyset = ` + + + + `; + + let container = document.createElement("box"); + container.innerHTML = keyset; + document.documentElement.appendChild(container); + /* eslint-enable no-unsanitized/property */ + + const pageUrl = "data:text/html,
Test
"; + 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); +}); diff --git a/dom/xbl/nsXBLPrototypeHandler.cpp b/dom/xbl/nsXBLPrototypeHandler.cpp index ead9b8a7c449..f3f8580297da 100644 --- a/dom/xbl/nsXBLPrototypeHandler.cpp +++ b/dom/xbl/nsXBLPrototypeHandler.cpp @@ -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) { diff --git a/dom/xbl/nsXBLPrototypeHandler.h b/dom/xbl/nsXBLPrototypeHandler.h index b32072f80edd..ed00ed925916 100644 --- a/dom/xbl/nsXBLPrototypeHandler.h +++ b/dom/xbl/nsXBLPrototypeHandler.h @@ -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., ) - 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; // is reserved for chrome. Not used by handlers. + XBLReservedKey mReserved; // 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. diff --git a/dom/xbl/nsXBLWindowKeyHandler.cpp b/dom/xbl/nsXBLWindowKeyHandler.cpp index 0385617d865c..019f9b0756f9 100644 --- a/dom/xbl/nsXBLWindowKeyHandler.cpp +++ b/dom/xbl/nsXBLWindowKeyHandler.cpp @@ -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 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; } diff --git a/extensions/cookie/nsPermissionManager.cpp b/extensions/cookie/nsPermissionManager.cpp index 81af32dd8c80..e412975bc9f1 100644 --- a/extensions/cookie/nsPermissionManager.cpp +++ b/extensions/cookie/nsPermissionManager.cpp @@ -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