Bug 1419091: Switch to a compiled C++ table for html key bindings for browser and editor. r=masayuki
☠☠ backed out by 1bd4af34fe10 ☠ ☠
authorDave Townsend <dtownsend@oxymoronical.com>
Mon, 08 Oct 2018 11:09:31 -0700
changeset 490657 ac56492b6ed6f03cd61389db83a73e0fdf970089
parent 490656 72ccc94449163738199ec04886f2f5513bce5122
child 490658 2224ee809328bf70ded5648d0a2896d507def220
push id247
push userfmarier@mozilla.com
push dateSat, 27 Oct 2018 01:06:44 +0000
reviewersmasayuki
bugs1419091
milestone64.0a1
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
dom/xbl/builtin/ShortcutKeys.cpp
dom/xbl/builtin/ShortcutKeys.h
dom/xbl/builtin/moz.build
dom/xbl/nsXBLPrototypeHandler.cpp
dom/xbl/nsXBLPrototypeHandler.h
dom/xbl/nsXBLWindowKeyHandler.cpp
dom/xbl/nsXBLWindowKeyHandler.h
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