Bug 1419091: Switch to a compiled C++ table for html key bindings for browser and editor. r=masayuki
Rather than loading an XBL binding for <browser> and <editor> elements this
generates the handlers from static C arrays.
Differential Revision:
https://phabricator.services.mozilla.com/D6181
new file mode 100644
--- /dev/null
+++ b/dom/xbl/builtin/ShortcutKeys.cpp
@@ -0,0 +1,94 @@
+#include "mozilla/ShortcutKeys.h"
+#include "../nsXBLPrototypeHandler.h"
+#include "nsContentUtils.h"
+
+namespace mozilla {
+
+NS_IMPL_ISUPPORTS(ShortcutKeys, nsIObserver);
+
+StaticRefPtr<ShortcutKeys> ShortcutKeys::sInstance;
+
+ShortcutKeys::ShortcutKeys()
+ : mBrowserHandlers(nullptr)
+ , mEditorHandlers(nullptr)
+ , mInputHandlers(nullptr)
+ , mTextAreaHandlers(nullptr)
+{
+ MOZ_ASSERT(!sInstance, "Attempt to instantiate a second ShortcutKeys.");
+ nsContentUtils::RegisterShutdownObserver(this);
+}
+
+ShortcutKeys::~ShortcutKeys()
+{
+ delete mBrowserHandlers;
+ delete mEditorHandlers;
+ delete mInputHandlers;
+ delete mTextAreaHandlers;
+}
+
+nsresult
+ShortcutKeys::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
+{
+ // Clear our strong reference so we can clean up.
+ sInstance = nullptr;
+ return NS_OK;
+}
+
+/* static */ nsXBLPrototypeHandler*
+ShortcutKeys::GetHandlers(HandlerType aType)
+{
+ if (!sInstance) {
+ sInstance = new ShortcutKeys();
+ }
+
+ return sInstance->EnsureHandlers(aType);
+}
+
+nsXBLPrototypeHandler*
+ShortcutKeys::EnsureHandlers(HandlerType aType)
+{
+ ShortcutKeyData* keyData;
+ nsXBLPrototypeHandler** cache;
+
+ switch (aType) {
+ case HandlerType::eBrowser:
+ keyData = &sBrowserHandlers[0];
+ cache = &mBrowserHandlers;
+ break;
+ case HandlerType::eEditor:
+ keyData = &sEditorHandlers[0];
+ cache = &mEditorHandlers;
+ break;
+ case HandlerType::eInput:
+ keyData = &sInputHandlers[0];
+ cache = &mInputHandlers;
+ break;
+ case HandlerType::eTextArea:
+ keyData = &sTextAreaHandlers[0];
+ cache = &mTextAreaHandlers;
+ break;
+ default:
+ MOZ_ASSERT(false, "Unknown handler type requested.");
+ }
+
+ if (*cache) {
+ return *cache;
+ }
+
+ nsXBLPrototypeHandler* lastHandler = nullptr;
+ while (keyData->event) {
+ nsXBLPrototypeHandler* handler =
+ new nsXBLPrototypeHandler(keyData);
+ if (lastHandler) {
+ lastHandler->SetNextHandler(handler);
+ } else {
+ *cache = handler;
+ }
+ lastHandler = handler;
+ keyData++;
+ }
+
+ return *cache;
+}
+
+} // namespace mozilla
--- a/dom/xbl/builtin/ShortcutKeys.h
+++ b/dom/xbl/builtin/ShortcutKeys.h
@@ -1,30 +1,65 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_ShortcutKeys_h
#define mozilla_dom_ShortcutKeys_h
+#include "nsIObserver.h"
+
+class nsXBLPrototypeHandler;
+
namespace mozilla {
typedef struct
{
const char16_t* event;
const char16_t* keycode;
const char16_t* key;
const char16_t* modifiers;
const char16_t* command;
} ShortcutKeyData;
-class ShortcutKeys
+enum class HandlerType
+{
+ eInput,
+ eTextArea,
+ eBrowser,
+ eEditor,
+};
+
+class ShortcutKeys : public nsIObserver
{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ // Returns a pointer to the first handler for the given type.
+ static nsXBLPrototypeHandler* GetHandlers(HandlerType aType);
+
protected:
+ ShortcutKeys();
+ virtual ~ShortcutKeys();
+
+ // Returns a pointer to the first handler for the given type.
+ nsXBLPrototypeHandler* EnsureHandlers(HandlerType aType);
+
+ // Maintains a strong reference to the only instance.
+ static StaticRefPtr<ShortcutKeys> sInstance;
+
+ // Shortcut keys for different elements.
static ShortcutKeyData sBrowserHandlers[];
static ShortcutKeyData sEditorHandlers[];
static ShortcutKeyData sInputHandlers[];
static ShortcutKeyData sTextAreaHandlers[];
+
+ // Cached event handlers generated from the above data.
+ nsXBLPrototypeHandler* mBrowserHandlers;
+ nsXBLPrototypeHandler* mEditorHandlers;
+ nsXBLPrototypeHandler* mInputHandlers;
+ nsXBLPrototypeHandler* mTextAreaHandlers;
};
} // namespace mozilla
#endif // #ifndef mozilla_dom_ShortcutKeys_h
--- a/dom/xbl/builtin/moz.build
+++ b/dom/xbl/builtin/moz.build
@@ -10,8 +10,13 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'co
DIRS += ['mac']
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
DIRS += ['android']
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gtk3':
DIRS += ['unix']
else:
DIRS += ['emacs']
+EXPORTS.mozilla += ['ShortcutKeys.h']
+
+SOURCES += ['ShortcutKeys.cpp']
+
+FINAL_LIBRARY = 'xul'
--- a/dom/xbl/nsXBLPrototypeHandler.cpp
+++ b/dom/xbl/nsXBLPrototypeHandler.cpp
@@ -101,26 +101,41 @@ nsXBLPrototypeHandler::nsXBLPrototypeHan
Init();
ConstructPrototype(nullptr, aEvent, aPhase, aAction, aCommand, aKeyCode,
aCharCode, aModifiers, aButton, aClickCount,
aGroup, aPreventDefault, aAllowUntrusted);
}
nsXBLPrototypeHandler::nsXBLPrototypeHandler(Element* aHandlerElement, XBLReservedKey aReserved)
- : mHandlerElement(nullptr),
+ : mHandlerElement(nullptr)
+ , mLineNumber(0)
+ , mReserved(aReserved)
+ , mNextHandler(nullptr)
+ , mPrototypeBinding(nullptr)
+{
+ Init();
+
+ // Make sure our prototype is initialized.
+ ConstructPrototype(aHandlerElement);
+}
+
+nsXBLPrototypeHandler::nsXBLPrototypeHandler(ShortcutKeyData* aKeyData)
+ : mHandlerText(nullptr),
mLineNumber(0),
- mReserved(aReserved),
+ mReserved(XBLReservedKey_False),
mNextHandler(nullptr),
mPrototypeBinding(nullptr)
{
Init();
- // Make sure our prototype is initialized.
- ConstructPrototype(aHandlerElement);
+ ConstructPrototype(nullptr, aKeyData->event, nullptr, nullptr,
+ aKeyData->command, aKeyData->keycode, aKeyData->key,
+ aKeyData->modifiers, nullptr, nullptr, nullptr, nullptr,
+ nullptr);
}
nsXBLPrototypeHandler::nsXBLPrototypeHandler(nsXBLPrototypeBinding* aBinding)
: mHandlerText(nullptr),
mLineNumber(0),
mPhase(0),
mType(0),
mMisc(0),
--- a/dom/xbl/nsXBLPrototypeHandler.h
+++ b/dom/xbl/nsXBLPrototypeHandler.h
@@ -13,16 +13,17 @@
#include "nsString.h"
#include "nsCOMPtr.h"
#include "nsIController.h"
#include "nsAutoPtr.h"
#include "nsXBLEventHandler.h"
#include "nsIWeakReference.h"
#include "nsCycleCollectionParticipant.h"
#include "js/TypeDecls.h"
+#include "mozilla/ShortcutKeys.h"
class nsIContent;
class nsIObjectInputStream;
class nsIObjectOutputStream;
class nsXBLPrototypeBinding;
namespace mozilla {
@@ -88,16 +89,20 @@ public:
const char16_t* aPreventDefault,
const char16_t* aAllowUntrusted,
nsXBLPrototypeBinding* aBinding,
uint32_t aLineNumber);
// This constructor is used only by XUL key handlers (e.g., <key>)
explicit nsXBLPrototypeHandler(mozilla::dom::Element* aKeyElement, XBLReservedKey aReserved);
+ // This constructor is used for keyboard handlers for browser, editor, input
+ // and textarea elements.
+ explicit nsXBLPrototypeHandler(mozilla::ShortcutKeyData* aKeyData);
+
// This constructor is used for handlers loaded from the cache
explicit nsXBLPrototypeHandler(nsXBLPrototypeBinding* aBinding);
~nsXBLPrototypeHandler();
/**
* Try and convert this XBL handler into an APZ KeyboardShortcut for handling
* key events on the compositor thread. This only works for XBL handlers that
--- a/dom/xbl/nsXBLWindowKeyHandler.cpp
+++ b/dom/xbl/nsXBLWindowKeyHandler.cpp
@@ -29,141 +29,35 @@
#include "mozilla/Preferences.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/TextEvents.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/EventBinding.h"
#include "mozilla/dom/KeyboardEvent.h"
#include "mozilla/layers/KeyboardMap.h"
+#include "mozilla/ShortcutKeys.h"
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::layers;
-class nsXBLSpecialDocInfo : public nsIObserver
-{
-public:
- RefPtr<nsXBLDocumentInfo> mHTMLBindings;
-
- static const char sHTMLBindingStr[];
- static const char sUserHTMLBindingStr[];
-
- bool mInitialized;
-
-public:
- NS_DECL_ISUPPORTS
- NS_DECL_NSIOBSERVER
-
- void LoadDocInfo();
- void GetHandlers(const nsACString& aRef,
- nsXBLPrototypeHandler** handler);
-
- nsXBLSpecialDocInfo() : mInitialized(false) {}
-
-protected:
- virtual ~nsXBLSpecialDocInfo() {}
-
-};
-
-const char nsXBLSpecialDocInfo::sHTMLBindingStr[] =
- "chrome://global/content/platformHTMLBindings.xml";
-
-NS_IMPL_ISUPPORTS(nsXBLSpecialDocInfo, nsIObserver)
-
-NS_IMETHODIMP
-nsXBLSpecialDocInfo::Observe(nsISupports* aSubject,
- const char* aTopic,
- const char16_t* aData)
-{
- MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"), "wrong topic");
-
- // On shutdown, clear our fields to avoid an extra cycle collection.
- mHTMLBindings = nullptr;
- mInitialized = false;
- nsContentUtils::UnregisterShutdownObserver(this);
-
- return NS_OK;
-}
-
-void nsXBLSpecialDocInfo::LoadDocInfo()
-{
- if (mInitialized)
- return;
- mInitialized = true;
- nsContentUtils::RegisterShutdownObserver(this);
-
- nsXBLService* xblService = nsXBLService::GetInstance();
- if (!xblService)
- return;
-
- // Obtain the platform doc info
- nsCOMPtr<nsIURI> bindingURI;
- NS_NewURI(getter_AddRefs(bindingURI), sHTMLBindingStr);
- if (!bindingURI) {
- return;
- }
- xblService->LoadBindingDocumentInfo(nullptr, nullptr,
- bindingURI,
- nullptr,
- true,
- getter_AddRefs(mHTMLBindings));
-}
-
-//
-// GetHandlers
-//
-//
-void
-nsXBLSpecialDocInfo::GetHandlers(const nsACString& aRef,
- nsXBLPrototypeHandler** aHandler)
-{
- if (mHTMLBindings) {
- nsXBLPrototypeBinding* binding = mHTMLBindings->GetPrototypeBinding(aRef);
-
- NS_ASSERTION(binding, "No binding found for the XBL window key handler.");
- if (!binding)
- return;
-
- *aHandler = binding->GetPrototypeHandlers();
- }
-}
-
-// Init statics
-static StaticRefPtr<nsXBLSpecialDocInfo> sXBLSpecialDocInfo;
-uint32_t nsXBLWindowKeyHandler::sRefCnt = 0;
-
-/* static */ void
-nsXBLWindowKeyHandler::EnsureSpecialDocInfo()
-{
- if (!sXBLSpecialDocInfo) {
- sXBLSpecialDocInfo = new nsXBLSpecialDocInfo();
- }
- sXBLSpecialDocInfo->LoadDocInfo();
-}
-
nsXBLWindowKeyHandler::nsXBLWindowKeyHandler(Element* aElement,
EventTarget* aTarget)
: mTarget(aTarget),
mHandler(nullptr)
{
mWeakPtrForElement = do_GetWeakReference(aElement);
- ++sRefCnt;
}
nsXBLWindowKeyHandler::~nsXBLWindowKeyHandler()
{
// If mWeakPtrForElement is non-null, we created a prototype handler.
if (mWeakPtrForElement)
delete mHandler;
-
- --sRefCnt;
- if (!sRefCnt) {
- sXBLSpecialDocInfo = nullptr;
- }
}
NS_IMPL_ISUPPORTS(nsXBLWindowKeyHandler,
nsIDOMEventListener)
static void
BuildHandlerChain(nsIContent* aContent, nsXBLPrototypeHandler** aResult)
{
@@ -225,24 +119,22 @@ nsXBLWindowKeyHandler::EnsureHandlers()
NS_ENSURE_STATE(!mWeakPtrForElement || el);
if (el) {
// We are actually a XUL <keyset>.
if (mHandler)
return NS_OK;
BuildHandlerChain(el, &mHandler);
} else { // We are an XBL file of handlers.
- EnsureSpecialDocInfo();
-
// Now determine which handlers we should be using.
if (IsHTMLEditableFieldFocused()) {
- sXBLSpecialDocInfo->GetHandlers(NS_LITERAL_CSTRING("editor"), &mHandler);
+ mHandler = ShortcutKeys::GetHandlers(HandlerType::eEditor);
}
else {
- sXBLSpecialDocInfo->GetHandlers(NS_LITERAL_CSTRING("browser"), &mHandler);
+ mHandler = ShortcutKeys::GetHandlers(HandlerType::eBrowser);
}
}
return NS_OK;
}
nsresult
nsXBLWindowKeyHandler::WalkHandlers(KeyboardEvent* aKeyEvent, nsAtom* aEventType)
@@ -391,21 +283,17 @@ nsXBLWindowKeyHandler::RemoveKeyboardEve
aEventListenerManager->RemoveEventListenerByType(
this, NS_LITERAL_STRING("mozkeyuponplugin"),
TrustedEventsAtSystemGroupBubble());
}
/* static */ KeyboardMap
nsXBLWindowKeyHandler::CollectKeyboardShortcuts()
{
- // Load the XBL handlers
- EnsureSpecialDocInfo();
-
- nsXBLPrototypeHandler* handlers = nullptr;
- sXBLSpecialDocInfo->GetHandlers(NS_LITERAL_CSTRING("browser"), &handlers);
+ nsXBLPrototypeHandler* handlers = ShortcutKeys::GetHandlers(HandlerType::eBrowser);
// Convert the handlers into keyboard shortcuts, using an AutoTArray with
// the maximum amount of shortcuts used on any platform to minimize allocations
AutoTArray<KeyboardShortcut, 48> shortcuts;
// Append keyboard shortcuts for hardcoded actions like tab
KeyboardShortcut::AppendHardcodedShortcuts(shortcuts);
--- a/dom/xbl/nsXBLWindowKeyHandler.h
+++ b/dom/xbl/nsXBLWindowKeyHandler.h
@@ -83,19 +83,16 @@ protected:
nsXBLPrototypeHandler* aHandler);
// Returns event type for matching between aWidgetKeyboardEvent and
// shortcut key handlers. This is used for calling WalkHandlers(),
// WalkHandlersInternal() and WalkHandlersAndExecute().
nsAtom* ConvertEventToDOMEventType(
const mozilla::WidgetKeyboardEvent& aWidgetKeyboardEvent) const;
- // lazily load the special doc info for loading handlers
- static void EnsureSpecialDocInfo();
-
// lazily load the handlers. Overridden to handle being attached
// to a particular element rather than the document
nsresult EnsureHandlers();
// Is an HTML editable element focused
bool IsHTMLEditableFieldFocused();
// Returns the element which was passed as a parameter to the constructor,
@@ -121,21 +118,16 @@ protected:
* Otherwise, false. aElement should be a command element or a key element.
*/
bool IsExecutableElement(mozilla::dom::Element* aElement) const;
// Using weak pointer to the DOM Element.
nsWeakPtr mWeakPtrForElement;
mozilla::dom::EventTarget* mTarget; // weak ref
- // these are not owning references; the prototype handlers are owned
- // by the prototype bindings which are owned by the docinfo.
nsXBLPrototypeHandler* mHandler; // platform bindings
-
- // holds reference count to document info about bindings
- static uint32_t sRefCnt;
};
already_AddRefed<nsXBLWindowKeyHandler>
NS_NewXBLWindowKeyHandler(mozilla::dom::Element* aElement,
mozilla::dom::EventTarget* aTarget);
#endif