LCOV - code coverage report
Current view: top level - dom/xbl - nsXBLWindowKeyHandler.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 112 432 25.9 %
Date: 2017-07-14 16:53:18 Functions: 11 37 29.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "nsCOMPtr.h"
       8             : #include "nsXBLPrototypeHandler.h"
       9             : #include "nsXBLWindowKeyHandler.h"
      10             : #include "nsIContent.h"
      11             : #include "nsIAtom.h"
      12             : #include "nsIDOMKeyEvent.h"
      13             : #include "nsXBLService.h"
      14             : #include "nsIServiceManager.h"
      15             : #include "nsGkAtoms.h"
      16             : #include "nsXBLDocumentInfo.h"
      17             : #include "nsIDOMElement.h"
      18             : #include "nsFocusManager.h"
      19             : #include "nsIURI.h"
      20             : #include "nsNetUtil.h"
      21             : #include "nsContentUtils.h"
      22             : #include "nsXBLPrototypeBinding.h"
      23             : #include "nsPIDOMWindow.h"
      24             : #include "nsIDocShell.h"
      25             : #include "nsIPresShell.h"
      26             : #include "mozilla/EventListenerManager.h"
      27             : #include "mozilla/EventStateManager.h"
      28             : #include "mozilla/Move.h"
      29             : #include "nsISelectionController.h"
      30             : #include "mozilla/Preferences.h"
      31             : #include "mozilla/StaticPtr.h"
      32             : #include "mozilla/TextEvents.h"
      33             : #include "mozilla/dom/Element.h"
      34             : #include "mozilla/dom/Event.h"
      35             : #include "mozilla/layers/KeyboardMap.h"
      36             : #include "nsIEditor.h"
      37             : #include "nsIHTMLEditor.h"
      38             : #include "nsIDOMDocument.h"
      39             : 
      40             : using namespace mozilla;
      41             : using namespace mozilla::dom;
      42             : using namespace mozilla::layers;
      43             : 
      44             : class nsXBLSpecialDocInfo : public nsIObserver
      45             : {
      46             : public:
      47             :   RefPtr<nsXBLDocumentInfo> mHTMLBindings;
      48             :   RefPtr<nsXBLDocumentInfo> mUserHTMLBindings;
      49             : 
      50             :   static const char sHTMLBindingStr[];
      51             :   static const char sUserHTMLBindingStr[];
      52             : 
      53             :   bool mInitialized;
      54             : 
      55             : public:
      56             :   NS_DECL_ISUPPORTS
      57             :   NS_DECL_NSIOBSERVER
      58             : 
      59             :   void LoadDocInfo();
      60             :   void GetAllHandlers(const char* aType,
      61             :                       nsXBLPrototypeHandler** handler,
      62             :                       nsXBLPrototypeHandler** userHandler);
      63             :   void GetHandlers(nsXBLDocumentInfo* aInfo,
      64             :                    const nsACString& aRef,
      65             :                    nsXBLPrototypeHandler** aResult);
      66             : 
      67           0 :   nsXBLSpecialDocInfo() : mInitialized(false) {}
      68             : 
      69             : protected:
      70           0 :   virtual ~nsXBLSpecialDocInfo() {}
      71             : 
      72             : };
      73             : 
      74             : const char nsXBLSpecialDocInfo::sHTMLBindingStr[] =
      75             :   "chrome://global/content/platformHTMLBindings.xml";
      76             : 
      77           0 : NS_IMPL_ISUPPORTS(nsXBLSpecialDocInfo, nsIObserver)
      78             : 
      79             : NS_IMETHODIMP
      80           0 : nsXBLSpecialDocInfo::Observe(nsISupports* aSubject,
      81             :                              const char* aTopic,
      82             :                              const char16_t* aData)
      83             : {
      84           0 :   MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"), "wrong topic");
      85             : 
      86             :   // On shutdown, clear our fields to avoid an extra cycle collection.
      87           0 :   mHTMLBindings = nullptr;
      88           0 :   mUserHTMLBindings = nullptr;
      89           0 :   mInitialized = false;
      90           0 :   nsContentUtils::UnregisterShutdownObserver(this);
      91             : 
      92           0 :   return NS_OK;
      93             : }
      94             : 
      95           0 : void nsXBLSpecialDocInfo::LoadDocInfo()
      96             : {
      97           0 :   if (mInitialized)
      98           0 :     return;
      99           0 :   mInitialized = true;
     100           0 :   nsContentUtils::RegisterShutdownObserver(this);
     101             : 
     102           0 :   nsXBLService* xblService = nsXBLService::GetInstance();
     103           0 :   if (!xblService)
     104           0 :     return;
     105             : 
     106             :   // Obtain the platform doc info
     107           0 :   nsCOMPtr<nsIURI> bindingURI;
     108           0 :   NS_NewURI(getter_AddRefs(bindingURI), sHTMLBindingStr);
     109           0 :   if (!bindingURI) {
     110           0 :     return;
     111             :   }
     112           0 :   xblService->LoadBindingDocumentInfo(nullptr, nullptr,
     113             :                                       bindingURI,
     114             :                                       nullptr,
     115             :                                       true,
     116           0 :                                       getter_AddRefs(mHTMLBindings));
     117             : }
     118             : 
     119             : //
     120             : // GetHandlers
     121             : //
     122             : //
     123             : void
     124           0 : nsXBLSpecialDocInfo::GetHandlers(nsXBLDocumentInfo* aInfo,
     125             :                                  const nsACString& aRef,
     126             :                                  nsXBLPrototypeHandler** aResult)
     127             : {
     128           0 :   nsXBLPrototypeBinding* binding = aInfo->GetPrototypeBinding(aRef);
     129             : 
     130           0 :   NS_ASSERTION(binding, "No binding found for the XBL window key handler.");
     131           0 :   if (!binding)
     132           0 :     return;
     133             : 
     134           0 :   *aResult = binding->GetPrototypeHandlers();
     135             : }
     136             : 
     137             : void
     138           0 : nsXBLSpecialDocInfo::GetAllHandlers(const char* aType,
     139             :                                     nsXBLPrototypeHandler** aHandler,
     140             :                                     nsXBLPrototypeHandler** aUserHandler)
     141             : {
     142           0 :   if (mUserHTMLBindings) {
     143           0 :     nsAutoCString type(aType);
     144           0 :     type.AppendLiteral("User");
     145           0 :     GetHandlers(mUserHTMLBindings, type, aUserHandler);
     146             :   }
     147           0 :   if (mHTMLBindings) {
     148           0 :     GetHandlers(mHTMLBindings, nsDependentCString(aType), aHandler);
     149             :   }
     150           0 : }
     151             : 
     152             : // Init statics
     153           3 : static StaticRefPtr<nsXBLSpecialDocInfo> sXBLSpecialDocInfo;
     154             : uint32_t nsXBLWindowKeyHandler::sRefCnt = 0;
     155             : 
     156             : /* static */ void
     157           0 : nsXBLWindowKeyHandler::EnsureSpecialDocInfo()
     158             : {
     159           0 :   if (!sXBLSpecialDocInfo) {
     160           0 :     sXBLSpecialDocInfo = new nsXBLSpecialDocInfo();
     161             :   }
     162           0 :   sXBLSpecialDocInfo->LoadDocInfo();
     163           0 : }
     164             : 
     165           7 : nsXBLWindowKeyHandler::nsXBLWindowKeyHandler(nsIDOMElement* aElement,
     166           7 :                                              EventTarget* aTarget)
     167             :   : mTarget(aTarget),
     168             :     mHandler(nullptr),
     169           7 :     mUserHandler(nullptr)
     170             : {
     171           7 :   mWeakPtrForElement = do_GetWeakReference(aElement);
     172           7 :   ++sRefCnt;
     173           7 : }
     174             : 
     175           3 : nsXBLWindowKeyHandler::~nsXBLWindowKeyHandler()
     176             : {
     177             :   // If mWeakPtrForElement is non-null, we created a prototype handler.
     178           1 :   if (mWeakPtrForElement)
     179           1 :     delete mHandler;
     180             : 
     181           1 :   --sRefCnt;
     182           1 :   if (!sRefCnt) {
     183           0 :     sXBLSpecialDocInfo = nullptr;
     184             :   }
     185           3 : }
     186             : 
     187         266 : NS_IMPL_ISUPPORTS(nsXBLWindowKeyHandler,
     188             :                   nsIDOMEventListener)
     189             : 
     190             : static void
     191           0 : BuildHandlerChain(nsIContent* aContent, nsXBLPrototypeHandler** aResult)
     192             : {
     193           0 :   *aResult = nullptr;
     194             : 
     195             :   // Since we chain each handler onto the next handler,
     196             :   // we'll enumerate them here in reverse so that when we
     197             :   // walk the chain they'll come out in the original order
     198           0 :   for (nsIContent* key = aContent->GetLastChild();
     199           0 :        key;
     200           0 :        key = key->GetPreviousSibling()) {
     201             : 
     202           0 :     if (key->NodeInfo()->Equals(nsGkAtoms::key, kNameSpaceID_XUL)) {
     203             :       // Check whether the key element has empty value at key/char attribute.
     204             :       // Such element is used by localizers for alternative shortcut key
     205             :       // definition on the locale. See bug 426501.
     206           0 :       nsAutoString valKey, valCharCode, valKeyCode;
     207             :       bool attrExists =
     208           0 :         key->GetAttr(kNameSpaceID_None, nsGkAtoms::key, valKey) ||
     209           0 :         key->GetAttr(kNameSpaceID_None, nsGkAtoms::charcode, valCharCode) ||
     210           0 :         key->GetAttr(kNameSpaceID_None, nsGkAtoms::keycode, valKeyCode);
     211           0 :       if (attrExists &&
     212           0 :           valKey.IsEmpty() && valCharCode.IsEmpty() && valKeyCode.IsEmpty())
     213           0 :         continue;
     214             : 
     215           0 :       bool reserved = key->AttrValueIs(kNameSpaceID_None, nsGkAtoms::reserved,
     216           0 :                                        nsGkAtoms::_true, eCaseMatters);
     217           0 :       nsXBLPrototypeHandler* handler = new nsXBLPrototypeHandler(key, reserved);
     218             : 
     219           0 :       handler->SetNextHandler(*aResult);
     220           0 :       *aResult = handler;
     221             :     }
     222             :   }
     223           0 : }
     224             : 
     225             : //
     226             : // EnsureHandlers
     227             : //
     228             : // Lazily load the XBL handlers. Overridden to handle being attached
     229             : // to a particular element rather than the document
     230             : //
     231             : nsresult
     232           0 : nsXBLWindowKeyHandler::EnsureHandlers()
     233             : {
     234           0 :   nsCOMPtr<Element> el = GetElement();
     235           0 :   NS_ENSURE_STATE(!mWeakPtrForElement || el);
     236           0 :   if (el) {
     237             :     // We are actually a XUL <keyset>.
     238           0 :     if (mHandler)
     239           0 :       return NS_OK;
     240             : 
     241           0 :     nsCOMPtr<nsIContent> content(do_QueryInterface(el));
     242           0 :     BuildHandlerChain(content, &mHandler);
     243             :   } else { // We are an XBL file of handlers.
     244           0 :     EnsureSpecialDocInfo();
     245             : 
     246             :     // Now determine which handlers we should be using.
     247           0 :     if (IsHTMLEditableFieldFocused()) {
     248           0 :       sXBLSpecialDocInfo->GetAllHandlers("editor", &mHandler, &mUserHandler);
     249             :     }
     250             :     else {
     251           0 :       sXBLSpecialDocInfo->GetAllHandlers("browser", &mHandler, &mUserHandler);
     252             :     }
     253             :   }
     254             : 
     255           0 :   return NS_OK;
     256             : }
     257             : 
     258             : nsresult
     259           0 : nsXBLWindowKeyHandler::WalkHandlers(nsIDOMKeyEvent* aKeyEvent, nsIAtom* aEventType)
     260             : {
     261             :   bool prevent;
     262           0 :   aKeyEvent->AsEvent()->GetDefaultPrevented(&prevent);
     263           0 :   if (prevent)
     264           0 :     return NS_OK;
     265             : 
     266           0 :   bool trustedEvent = false;
     267             :   // Don't process the event if it was not dispatched from a trusted source
     268           0 :   aKeyEvent->AsEvent()->GetIsTrusted(&trustedEvent);
     269             : 
     270           0 :   if (!trustedEvent)
     271           0 :     return NS_OK;
     272             : 
     273           0 :   nsresult rv = EnsureHandlers();
     274           0 :   NS_ENSURE_SUCCESS(rv, rv);
     275             : 
     276             :   bool isDisabled;
     277           0 :   nsCOMPtr<Element> el = GetElement(&isDisabled);
     278           0 :   if (!el) {
     279           0 :     if (mUserHandler) {
     280           0 :       WalkHandlersInternal(aKeyEvent, aEventType, mUserHandler, true);
     281           0 :       aKeyEvent->AsEvent()->GetDefaultPrevented(&prevent);
     282           0 :       if (prevent)
     283           0 :         return NS_OK; // Handled by the user bindings. Our work here is done.
     284             :     }
     285             :   }
     286             : 
     287             :   // skip keysets that are disabled
     288           0 :   if (el && isDisabled) {
     289           0 :     return NS_OK;
     290             :   }
     291             : 
     292           0 :   WalkHandlersInternal(aKeyEvent, aEventType, mHandler, true);
     293             : 
     294           0 :   return NS_OK;
     295             : }
     296             : 
     297             : void
     298           7 : nsXBLWindowKeyHandler::InstallKeyboardEventListenersTo(
     299             :                          EventListenerManager* aEventListenerManager)
     300             : {
     301             :   // For marking each keyboard event as if it's reserved by chrome,
     302             :   // nsXBLWindowKeyHandlers need to listen each keyboard events before
     303             :   // web contents.
     304           7 :   aEventListenerManager->AddEventListenerByType(
     305          14 :                            this, NS_LITERAL_STRING("keydown"),
     306          28 :                            TrustedEventsAtCapture());
     307           7 :   aEventListenerManager->AddEventListenerByType(
     308          14 :                            this, NS_LITERAL_STRING("keyup"),
     309          28 :                            TrustedEventsAtCapture());
     310           7 :   aEventListenerManager->AddEventListenerByType(
     311          14 :                            this, NS_LITERAL_STRING("keypress"),
     312          28 :                            TrustedEventsAtCapture());
     313           7 :   aEventListenerManager->AddEventListenerByType(
     314          14 :                            this, NS_LITERAL_STRING("mozkeydownonplugin"),
     315          28 :                            TrustedEventsAtCapture());
     316           7 :   aEventListenerManager->AddEventListenerByType(
     317          14 :                            this, NS_LITERAL_STRING("mozkeyuponplugin"),
     318          28 :                            TrustedEventsAtCapture());
     319             : 
     320             :   // For reducing the IPC cost, preventing to dispatch reserved keyboard
     321             :   // events into the content process.
     322           7 :   aEventListenerManager->AddEventListenerByType(
     323          14 :                            this, NS_LITERAL_STRING("keydown"),
     324          28 :                            TrustedEventsAtSystemGroupCapture());
     325           7 :   aEventListenerManager->AddEventListenerByType(
     326          14 :                            this, NS_LITERAL_STRING("keyup"),
     327          28 :                            TrustedEventsAtSystemGroupCapture());
     328           7 :   aEventListenerManager->AddEventListenerByType(
     329          14 :                            this, NS_LITERAL_STRING("keypress"),
     330          28 :                            TrustedEventsAtSystemGroupCapture());
     331           7 :   aEventListenerManager->AddEventListenerByType(
     332          14 :                            this, NS_LITERAL_STRING("mozkeydownonplugin"),
     333          28 :                            TrustedEventsAtSystemGroupCapture());
     334           7 :   aEventListenerManager->AddEventListenerByType(
     335          14 :                            this, NS_LITERAL_STRING("mozkeyuponplugin"),
     336          28 :                            TrustedEventsAtSystemGroupCapture());
     337             : 
     338             :   // Handle keyboard events in bubbling phase of the system event group.
     339           7 :   aEventListenerManager->AddEventListenerByType(
     340          14 :                            this, NS_LITERAL_STRING("keydown"),
     341          28 :                            TrustedEventsAtSystemGroupBubble());
     342           7 :   aEventListenerManager->AddEventListenerByType(
     343          14 :                            this, NS_LITERAL_STRING("keyup"),
     344          28 :                            TrustedEventsAtSystemGroupBubble());
     345           7 :   aEventListenerManager->AddEventListenerByType(
     346          14 :                            this, NS_LITERAL_STRING("keypress"),
     347          28 :                            TrustedEventsAtSystemGroupBubble());
     348           7 :   aEventListenerManager->AddEventListenerByType(
     349          14 :                            this, NS_LITERAL_STRING("mozkeydownonplugin"),
     350          28 :                            TrustedEventsAtSystemGroupBubble());
     351           7 :   aEventListenerManager->AddEventListenerByType(
     352          14 :                            this, NS_LITERAL_STRING("mozkeyuponplugin"),
     353          28 :                            TrustedEventsAtSystemGroupBubble());
     354           7 : }
     355             : 
     356             : void
     357           1 : nsXBLWindowKeyHandler::RemoveKeyboardEventListenersFrom(
     358             :                          EventListenerManager* aEventListenerManager)
     359             : {
     360           1 :   aEventListenerManager->RemoveEventListenerByType(
     361           2 :                            this, NS_LITERAL_STRING("keydown"),
     362           4 :                            TrustedEventsAtCapture());
     363           1 :   aEventListenerManager->RemoveEventListenerByType(
     364           2 :                            this, NS_LITERAL_STRING("keyup"),
     365           4 :                            TrustedEventsAtCapture());
     366           1 :   aEventListenerManager->RemoveEventListenerByType(
     367           2 :                            this, NS_LITERAL_STRING("keypress"),
     368           4 :                            TrustedEventsAtCapture());
     369           1 :   aEventListenerManager->RemoveEventListenerByType(
     370           2 :                            this, NS_LITERAL_STRING("mozkeydownonplugin"),
     371           4 :                            TrustedEventsAtCapture());
     372           1 :   aEventListenerManager->RemoveEventListenerByType(
     373           2 :                            this, NS_LITERAL_STRING("mozkeyuponplugin"),
     374           4 :                            TrustedEventsAtCapture());
     375             : 
     376           1 :   aEventListenerManager->RemoveEventListenerByType(
     377           2 :                            this, NS_LITERAL_STRING("keydown"),
     378           4 :                            TrustedEventsAtSystemGroupCapture());
     379           1 :   aEventListenerManager->RemoveEventListenerByType(
     380           2 :                            this, NS_LITERAL_STRING("keyup"),
     381           4 :                            TrustedEventsAtSystemGroupCapture());
     382           1 :   aEventListenerManager->RemoveEventListenerByType(
     383           2 :                            this, NS_LITERAL_STRING("keypress"),
     384           4 :                            TrustedEventsAtSystemGroupCapture());
     385           1 :   aEventListenerManager->RemoveEventListenerByType(
     386           2 :                            this, NS_LITERAL_STRING("mozkeydownonplugin"),
     387           4 :                            TrustedEventsAtSystemGroupCapture());
     388           1 :   aEventListenerManager->RemoveEventListenerByType(
     389           2 :                            this, NS_LITERAL_STRING("mozkeyuponplugin"),
     390           4 :                            TrustedEventsAtSystemGroupCapture());
     391             : 
     392           1 :   aEventListenerManager->RemoveEventListenerByType(
     393           2 :                            this, NS_LITERAL_STRING("keydown"),
     394           4 :                            TrustedEventsAtSystemGroupBubble());
     395           1 :   aEventListenerManager->RemoveEventListenerByType(
     396           2 :                            this, NS_LITERAL_STRING("keyup"),
     397           4 :                            TrustedEventsAtSystemGroupBubble());
     398           1 :   aEventListenerManager->RemoveEventListenerByType(
     399           2 :                            this, NS_LITERAL_STRING("keypress"),
     400           4 :                            TrustedEventsAtSystemGroupBubble());
     401           1 :   aEventListenerManager->RemoveEventListenerByType(
     402           2 :                            this, NS_LITERAL_STRING("mozkeydownonplugin"),
     403           4 :                            TrustedEventsAtSystemGroupBubble());
     404           1 :   aEventListenerManager->RemoveEventListenerByType(
     405           2 :                            this, NS_LITERAL_STRING("mozkeyuponplugin"),
     406           4 :                            TrustedEventsAtSystemGroupBubble());
     407           1 : }
     408             : 
     409             : /* static */ KeyboardMap
     410           0 : nsXBLWindowKeyHandler::CollectKeyboardShortcuts()
     411             : {
     412             :   // Load the XBL handlers
     413           0 :   EnsureSpecialDocInfo();
     414             : 
     415           0 :   nsXBLPrototypeHandler* handlers = nullptr;
     416           0 :   nsXBLPrototypeHandler* userHandlers = nullptr;
     417           0 :   sXBLSpecialDocInfo->GetAllHandlers("browser", &handlers, &userHandlers);
     418             : 
     419             :   // Convert the handlers into keyboard shortcuts, using an AutoTArray with
     420             :   // the maximum amount of shortcuts used on any platform to minimize allocations
     421           0 :   AutoTArray<KeyboardShortcut, 48> shortcuts;
     422             : 
     423             :   // Append keyboard shortcuts for hardcoded actions like tab
     424           0 :   KeyboardShortcut::AppendHardcodedShortcuts(shortcuts);
     425             : 
     426           0 :   for (nsXBLPrototypeHandler* handler = handlers;
     427           0 :        handler;
     428             :        handler = handler->GetNextHandler()) {
     429           0 :     KeyboardShortcut shortcut;
     430           0 :     if (handler->TryConvertToKeyboardShortcut(&shortcut)) {
     431           0 :       shortcuts.AppendElement(shortcut);
     432             :     }
     433             :   }
     434             : 
     435           0 :   for (nsXBLPrototypeHandler* handler = userHandlers;
     436           0 :        handler;
     437             :        handler = handler->GetNextHandler()) {
     438           0 :     KeyboardShortcut shortcut;
     439           0 :     if (handler->TryConvertToKeyboardShortcut(&shortcut)) {
     440           0 :       shortcuts.AppendElement(shortcut);
     441             :     }
     442             :   }
     443             : 
     444           0 :   return KeyboardMap(mozilla::Move(shortcuts));
     445             : }
     446             : 
     447             : nsIAtom*
     448           0 : nsXBLWindowKeyHandler::ConvertEventToDOMEventType(
     449             :                          const WidgetKeyboardEvent& aWidgetKeyboardEvent) const
     450             : {
     451           0 :   if (aWidgetKeyboardEvent.IsKeyDownOrKeyDownOnPlugin()) {
     452           0 :     return nsGkAtoms::keydown;
     453             :   }
     454           0 :   if (aWidgetKeyboardEvent.IsKeyUpOrKeyUpOnPlugin()) {
     455           0 :     return nsGkAtoms::keyup;
     456             :   }
     457           0 :   if (aWidgetKeyboardEvent.mMessage == eKeyPress) {
     458           0 :     return nsGkAtoms::keypress;
     459             :   }
     460           0 :   MOZ_ASSERT_UNREACHABLE(
     461             :     "All event messages which this instance listens to should be handled");
     462             :   return nullptr;
     463             : }
     464             : 
     465             : NS_IMETHODIMP
     466           0 : nsXBLWindowKeyHandler::HandleEvent(nsIDOMEvent* aEvent)
     467             : {
     468           0 :   nsCOMPtr<nsIDOMKeyEvent> keyEvent(do_QueryInterface(aEvent));
     469           0 :   NS_ENSURE_TRUE(keyEvent, NS_ERROR_INVALID_ARG);
     470             : 
     471             :   uint16_t eventPhase;
     472           0 :   aEvent->GetEventPhase(&eventPhase);
     473           0 :   if (eventPhase == nsIDOMEvent::CAPTURING_PHASE) {
     474           0 :     if (aEvent->WidgetEventPtr()->mFlags.mInSystemGroup) {
     475           0 :       HandleEventOnCaptureInSystemEventGroup(keyEvent);
     476             :     } else {
     477           0 :       HandleEventOnCaptureInDefaultEventGroup(keyEvent);
     478             :     }
     479           0 :     return NS_OK;
     480             :   }
     481             : 
     482             :   WidgetKeyboardEvent* widgetKeyboardEvent =
     483           0 :     aEvent->WidgetEventPtr()->AsKeyboardEvent();
     484           0 :   if (widgetKeyboardEvent->IsKeyEventOnPlugin()) {
     485             :     // key events on plugin shouldn't execute shortcut key handlers which are
     486             :     // not reserved.
     487           0 :     if (!widgetKeyboardEvent->IsReservedByChrome()) {
     488           0 :       return NS_OK;
     489             :     }
     490             : 
     491             :     // If the event is untrusted event or was already consumed, do nothing.
     492           0 :     if (!widgetKeyboardEvent->IsTrusted() ||
     493           0 :         widgetKeyboardEvent->DefaultPrevented()) {
     494           0 :       return NS_OK;
     495             :     }
     496             : 
     497             :     // XXX Don't check isReserved here because even if the handler in this
     498             :     //     instance isn't reserved but another instance reserves the key
     499             :     //     combination, it will be executed when the event is normal keyboard
     500             :     //     events...
     501           0 :     bool isReserved = false;
     502           0 :     if (!HasHandlerForEvent(keyEvent, &isReserved)) {
     503           0 :       return NS_OK;
     504             :     }
     505             :   }
     506             : 
     507             :   nsCOMPtr<nsIAtom> eventTypeAtom =
     508           0 :     ConvertEventToDOMEventType(*widgetKeyboardEvent);
     509           0 :   return WalkHandlers(keyEvent, eventTypeAtom);
     510             : }
     511             : 
     512             : void
     513           0 : nsXBLWindowKeyHandler::HandleEventOnCaptureInDefaultEventGroup(
     514             :                          nsIDOMKeyEvent* aEvent)
     515             : {
     516             :   WidgetKeyboardEvent* widgetKeyboardEvent =
     517           0 :     aEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent();
     518             : 
     519           0 :   if (widgetKeyboardEvent->IsReservedByChrome()) {
     520           0 :     return;
     521             :   }
     522             : 
     523           0 :   bool isReserved = false;
     524           0 :   if (HasHandlerForEvent(aEvent, &isReserved) && isReserved) {
     525           0 :     widgetKeyboardEvent->MarkAsReservedByChrome();
     526             :   }
     527             : }
     528             : 
     529             : void
     530           0 : nsXBLWindowKeyHandler::HandleEventOnCaptureInSystemEventGroup(
     531             :                          nsIDOMKeyEvent* aEvent)
     532             : {
     533             :   WidgetKeyboardEvent* widgetEvent =
     534           0 :     aEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent();
     535             : 
     536           0 :   if (widgetEvent->IsCrossProcessForwardingStopped() ||
     537           0 :       widgetEvent->mFlags.mOnlySystemGroupDispatchInContent) {
     538           0 :     return;
     539             :   }
     540             : 
     541             :   nsCOMPtr<mozilla::dom::Element> originalTarget =
     542           0 :     do_QueryInterface(aEvent->AsEvent()->WidgetEventPtr()->mOriginalTarget);
     543           0 :   if (!EventStateManager::IsRemoteTarget(originalTarget)) {
     544           0 :     return;
     545             :   }
     546             : 
     547           0 :   if (!HasHandlerForEvent(aEvent)) {
     548           0 :     return;
     549             :   }
     550             : 
     551             :   // If this event wasn't marked as IsCrossProcessForwardingStopped,
     552             :   // yet, it means it wasn't processed by content. We'll not call any
     553             :   // of the handlers at this moment, and will wait the reply event.
     554             :   // So, stop immediate propagation in this event first, then, mark it as
     555             :   // waiting reply from remote process.  Finally, when this process receives
     556             :   // a reply from the remote process, it should be dispatched into this
     557             :   // DOM tree again.
     558           0 :   widgetEvent->StopImmediatePropagation();
     559           0 :   widgetEvent->MarkAsWaitingReplyFromRemoteProcess();
     560             : }
     561             : 
     562             : bool
     563           0 : nsXBLWindowKeyHandler::IsHTMLEditableFieldFocused()
     564             : {
     565           0 :   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
     566           0 :   if (!fm)
     567           0 :     return false;
     568             : 
     569           0 :   nsCOMPtr<mozIDOMWindowProxy> focusedWindow;
     570           0 :   fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
     571           0 :   if (!focusedWindow)
     572           0 :     return false;
     573             : 
     574           0 :   auto* piwin = nsPIDOMWindowOuter::From(focusedWindow);
     575           0 :   nsIDocShell *docShell = piwin->GetDocShell();
     576           0 :   if (!docShell) {
     577           0 :     return false;
     578             :   }
     579             : 
     580           0 :   nsCOMPtr<nsIEditor> editor;
     581           0 :   docShell->GetEditor(getter_AddRefs(editor));
     582           0 :   nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(editor);
     583           0 :   if (!htmlEditor) {
     584           0 :     return false;
     585             :   }
     586             : 
     587           0 :   nsCOMPtr<nsIDOMDocument> domDocument;
     588           0 :   editor->GetDocument(getter_AddRefs(domDocument));
     589           0 :   nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDocument);
     590           0 :   if (doc->HasFlag(NODE_IS_EDITABLE)) {
     591             :     // Don't need to perform any checks in designMode documents.
     592           0 :     return true;
     593             :   }
     594             : 
     595           0 :   nsCOMPtr<nsIDOMElement> focusedElement;
     596           0 :   fm->GetFocusedElement(getter_AddRefs(focusedElement));
     597           0 :   nsCOMPtr<nsINode> focusedNode = do_QueryInterface(focusedElement);
     598           0 :   if (focusedNode) {
     599             :     // If there is a focused element, make sure it's in the active editing host.
     600             :     // Note that GetActiveEditingHost finds the current editing host based on
     601             :     // the document's selection.  Even though the document selection is usually
     602             :     // collapsed to where the focus is, but the page may modify the selection
     603             :     // without our knowledge, in which case this check will do something useful.
     604           0 :     nsCOMPtr<Element> activeEditingHost = htmlEditor->GetActiveEditingHost();
     605           0 :     if (!activeEditingHost) {
     606           0 :       return false;
     607             :     }
     608           0 :     return nsContentUtils::ContentIsDescendantOf(focusedNode, activeEditingHost);
     609             :   }
     610             : 
     611           0 :   return false;
     612             : }
     613             : 
     614             : //
     615             : // WalkHandlersInternal and WalkHandlersAndExecute
     616             : //
     617             : // Given a particular DOM event and a pointer to the first handler in the list,
     618             : // scan through the list to find something to handle the event. If aExecute = true,
     619             : // the handler will be executed; otherwise just return an answer telling if a handler
     620             : // for that event was found.
     621             : //
     622             : bool
     623           0 : nsXBLWindowKeyHandler::WalkHandlersInternal(nsIDOMKeyEvent* aKeyEvent,
     624             :                                             nsIAtom* aEventType,
     625             :                                             nsXBLPrototypeHandler* aHandler,
     626             :                                             bool aExecute,
     627             :                                             bool* aOutReservedForChrome)
     628             : {
     629             :   WidgetKeyboardEvent* nativeKeyboardEvent =
     630           0 :     aKeyEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent();
     631           0 :   MOZ_ASSERT(nativeKeyboardEvent);
     632             : 
     633           0 :   AutoShortcutKeyCandidateArray shortcutKeys;
     634           0 :   nativeKeyboardEvent->GetShortcutKeyCandidates(shortcutKeys);
     635             : 
     636           0 :   if (shortcutKeys.IsEmpty()) {
     637           0 :     return WalkHandlersAndExecute(aKeyEvent, aEventType, aHandler,
     638           0 :                                   0, IgnoreModifierState(),
     639           0 :                                   aExecute, aOutReservedForChrome);
     640             :   }
     641             : 
     642           0 :   for (uint32_t i = 0; i < shortcutKeys.Length(); ++i) {
     643           0 :     ShortcutKeyCandidate& key = shortcutKeys[i];
     644           0 :     IgnoreModifierState ignoreModifierState;
     645           0 :     ignoreModifierState.mShift = key.mIgnoreShift;
     646           0 :     if (WalkHandlersAndExecute(aKeyEvent, aEventType, aHandler,
     647             :                                key.mCharCode, ignoreModifierState,
     648             :                                aExecute, aOutReservedForChrome)) {
     649           0 :       return true;
     650             :     }
     651             :   }
     652           0 :   return false;
     653             : }
     654             : 
     655             : bool
     656           0 : nsXBLWindowKeyHandler::WalkHandlersAndExecute(
     657             :                          nsIDOMKeyEvent* aKeyEvent,
     658             :                          nsIAtom* aEventType,
     659             :                          nsXBLPrototypeHandler* aFirstHandler,
     660             :                          uint32_t aCharCode,
     661             :                          const IgnoreModifierState& aIgnoreModifierState,
     662             :                          bool aExecute,
     663             :                          bool* aOutReservedForChrome)
     664             : {
     665           0 :   if (aOutReservedForChrome) {
     666           0 :     *aOutReservedForChrome = false;
     667             :   }
     668             : 
     669             :   WidgetKeyboardEvent* widgetKeyboardEvent =
     670           0 :     aKeyEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent();
     671           0 :   if (NS_WARN_IF(!widgetKeyboardEvent)) {
     672           0 :     return false;
     673             :   }
     674             : 
     675             :   // Try all of the handlers until we find one that matches the event.
     676           0 :   for (nsXBLPrototypeHandler* handler = aFirstHandler;
     677           0 :        handler;
     678             :        handler = handler->GetNextHandler()) {
     679           0 :     bool stopped = aKeyEvent->AsEvent()->IsDispatchStopped();
     680           0 :     if (stopped) {
     681             :       // The event is finished, don't execute any more handlers
     682           0 :       return false;
     683             :     }
     684             : 
     685           0 :     if (aExecute) {
     686             :       // If the event is eKeyDownOnPlugin, it should execute either keydown
     687             :       // handler or keypress handler because eKeyDownOnPlugin events are
     688             :       // never followed by keypress events.
     689           0 :       if (widgetKeyboardEvent->mMessage == eKeyDownOnPlugin) {
     690           0 :         if (!handler->EventTypeEquals(nsGkAtoms::keydown) &&
     691           0 :             !handler->EventTypeEquals(nsGkAtoms::keypress)) {
     692           0 :           continue;
     693             :         }
     694             :       // The other event types should exactly be matched with the handler's
     695             :       // event type.
     696           0 :       } else if (!handler->EventTypeEquals(aEventType)) {
     697           0 :         continue;
     698             :       }
     699             :     } else {
     700           0 :       if (handler->EventTypeEquals(nsGkAtoms::keypress)) {
     701             :         // If the handler is a keypress event handler, we also need to check
     702             :         // if coming keydown event is a preceding event of reserved key
     703             :         // combination because if default action of a keydown event is
     704             :         // prevented, following keypress event won't be fired.  However, if
     705             :         // following keypress event is reserved, we shouldn't allow web
     706             :         // contents to prevent the default of the preceding keydown event.
     707           0 :         if (aEventType != nsGkAtoms::keydown &&
     708           0 :             aEventType != nsGkAtoms::keypress) {
     709           0 :           continue;
     710             :         }
     711           0 :       } else if (!handler->EventTypeEquals(aEventType)) {
     712             :         // Otherwise, aEventType should exactly be matched.
     713           0 :         continue;
     714             :       }
     715             :     }
     716             : 
     717             :     // Check if the keyboard event *may* execute the handler.
     718           0 :     if (!handler->KeyEventMatched(aKeyEvent, aCharCode, aIgnoreModifierState)) {
     719           0 :       continue;  // try the next one
     720             :     }
     721             : 
     722             :     // Before executing this handler, check that it's not disabled,
     723             :     // and that it has something to do (oncommand of the <key> or its
     724             :     // <command> is non-empty).
     725           0 :     nsCOMPtr<Element> commandElement;
     726           0 :     if (!GetElementForHandler(handler, getter_AddRefs(commandElement))) {
     727           0 :       continue;
     728             :     }
     729             : 
     730           0 :     bool isReserved = handler->GetIsReserved();
     731           0 :     if (aOutReservedForChrome) {
     732           0 :       *aOutReservedForChrome = isReserved;
     733             :     }
     734             : 
     735           0 :     if (commandElement) {
     736           0 :       if (aExecute && !IsExecutableElement(commandElement)) {
     737           0 :         continue;
     738             :       }
     739             :     }
     740             : 
     741           0 :     if (!aExecute) {
     742           0 :       if (handler->EventTypeEquals(aEventType)) {
     743           0 :         return true;
     744             :       }
     745             :       // If the command is reserved and the event is keydown, check also if
     746             :       // the handler is for keypress because if following keypress event is
     747             :       // reserved, we shouldn't dispatch the event into web contents.
     748           0 :       if (isReserved &&
     749           0 :           aEventType == nsGkAtoms::keydown &&
     750           0 :           handler->EventTypeEquals(nsGkAtoms::keypress)) {
     751           0 :         return true;
     752             :       }
     753             :       // Otherwise, we've not found a handler for the event yet.
     754           0 :       continue;
     755             :     }
     756             : 
     757             :     // If it's not reserved and the event is a key event on a plugin,
     758             :     // the handler shouldn't be executed.
     759           0 :     if (!isReserved && widgetKeyboardEvent->IsKeyEventOnPlugin()) {
     760           0 :       return false;
     761             :     }
     762             : 
     763           0 :     nsCOMPtr<EventTarget> target;
     764           0 :     nsCOMPtr<Element> chromeHandlerElement = GetElement();
     765           0 :     if (chromeHandlerElement) {
     766             :       // XXX commandElement may be nullptr...
     767           0 :       target = commandElement;
     768             :     } else {
     769           0 :       target = mTarget;
     770             :     }
     771             : 
     772             :     // XXX Do we execute only one handler even if the handler neither stops
     773             :     //     propagation nor prevents default of the event?
     774           0 :     nsresult rv = handler->ExecuteHandler(target, aKeyEvent->AsEvent());
     775           0 :     if (NS_SUCCEEDED(rv)) {
     776           0 :       return true;
     777             :     }
     778             :   }
     779             : 
     780             : #ifdef XP_WIN
     781             :   // Windows native applications ignore Windows-Logo key state when checking
     782             :   // shortcut keys even if the key is pressed.  Therefore, if there is no
     783             :   // shortcut key which exactly matches current modifier state, we should
     784             :   // retry to look for a shortcut key without the Windows-Logo key press.
     785             :   if (!aIgnoreModifierState.mOS && widgetKeyboardEvent->IsOS()) {
     786             :     IgnoreModifierState ignoreModifierState(aIgnoreModifierState);
     787             :     ignoreModifierState.mOS = true;
     788             :     return WalkHandlersAndExecute(aKeyEvent, aEventType, aFirstHandler,
     789             :                                   aCharCode, ignoreModifierState, aExecute);
     790             :   }
     791             : #endif
     792             : 
     793           0 :   return false;
     794             : }
     795             : 
     796             : bool
     797           0 : nsXBLWindowKeyHandler::HasHandlerForEvent(nsIDOMKeyEvent* aEvent,
     798             :                                           bool* aOutReservedForChrome)
     799             : {
     800             :   WidgetKeyboardEvent* widgetKeyboardEvent =
     801           0 :     aEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent();
     802           0 :   if (NS_WARN_IF(!widgetKeyboardEvent) || !widgetKeyboardEvent->IsTrusted()) {
     803           0 :     return false;
     804             :   }
     805             : 
     806           0 :   nsresult rv = EnsureHandlers();
     807           0 :   NS_ENSURE_SUCCESS(rv, false);
     808             : 
     809             :   bool isDisabled;
     810           0 :   nsCOMPtr<Element> el = GetElement(&isDisabled);
     811           0 :   if (el && isDisabled) {
     812           0 :     return false;
     813             :   }
     814             : 
     815             :   nsCOMPtr<nsIAtom> eventTypeAtom =
     816           0 :     ConvertEventToDOMEventType(*widgetKeyboardEvent);
     817           0 :   return WalkHandlersInternal(aEvent, eventTypeAtom, mHandler, false,
     818           0 :                               aOutReservedForChrome);
     819             : }
     820             : 
     821             : already_AddRefed<Element>
     822           0 : nsXBLWindowKeyHandler::GetElement(bool* aIsDisabled)
     823             : {
     824           0 :   nsCOMPtr<Element> element = do_QueryReferent(mWeakPtrForElement);
     825           0 :   if (element && aIsDisabled) {
     826           0 :     *aIsDisabled = element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
     827             :                                         nsGkAtoms::_true, eCaseMatters);
     828             :   }
     829           0 :   return element.forget();
     830             : }
     831             : 
     832             : bool
     833           0 : nsXBLWindowKeyHandler::GetElementForHandler(nsXBLPrototypeHandler* aHandler,
     834             :                                             Element** aElementForHandler)
     835             : {
     836           0 :   MOZ_ASSERT(aElementForHandler);
     837           0 :   *aElementForHandler = nullptr;
     838             : 
     839           0 :   nsCOMPtr<nsIContent> keyContent = aHandler->GetHandlerElement();
     840           0 :   if (!keyContent) {
     841           0 :     return true; // XXX Even though no key element?
     842             :   }
     843             : 
     844           0 :   nsCOMPtr<Element> chromeHandlerElement = GetElement();
     845           0 :   if (!chromeHandlerElement) {
     846           0 :     NS_WARNING_ASSERTION(keyContent->IsInUncomposedDoc(), "uncomposed");
     847           0 :     nsCOMPtr<Element> keyElement = do_QueryInterface(keyContent);
     848           0 :     keyElement.swap(*aElementForHandler);
     849           0 :     return true;
     850             :   }
     851             : 
     852             :   // We are in a XUL doc.  Obtain our command attribute.
     853           0 :   nsAutoString command;
     854           0 :   keyContent->GetAttr(kNameSpaceID_None, nsGkAtoms::command, command);
     855           0 :   if (command.IsEmpty()) {
     856             :     // There is no command element associated with the key element.
     857           0 :     NS_WARNING_ASSERTION(keyContent->IsInUncomposedDoc(), "uncomposed");
     858           0 :     nsCOMPtr<Element> keyElement = do_QueryInterface(keyContent);
     859           0 :     keyElement.swap(*aElementForHandler);
     860           0 :     return true;
     861             :   }
     862             : 
     863             :   // XXX Shouldn't we check this earlier?
     864           0 :   nsIDocument* doc = keyContent->GetUncomposedDoc();
     865           0 :   if (NS_WARN_IF(!doc)) {
     866           0 :     return false;
     867             :   }
     868             : 
     869             :   nsCOMPtr<Element> commandElement =
     870           0 :     do_QueryInterface(doc->GetElementById(command));
     871           0 :   if (!commandElement) {
     872           0 :     NS_ERROR("A XUL <key> is observing a command that doesn't exist. "
     873             :              "Unable to execute key binding!");
     874           0 :     return false;
     875             :   }
     876             : 
     877           0 :   commandElement.swap(*aElementForHandler);
     878           0 :   return true;
     879             : }
     880             : 
     881             : bool
     882           0 : nsXBLWindowKeyHandler::IsExecutableElement(Element* aElement) const
     883             : {
     884           0 :   if (!aElement) {
     885           0 :     return false;
     886             :   }
     887             : 
     888           0 :   nsAutoString value;
     889           0 :   aElement->GetAttribute(NS_LITERAL_STRING("disabled"), value);
     890           0 :   if (value.EqualsLiteral("true")) {
     891           0 :     return false;
     892             :   }
     893             : 
     894           0 :   aElement->GetAttribute(NS_LITERAL_STRING("oncommand"), value);
     895           0 :   if (value.IsEmpty()) {
     896           0 :     return false;
     897             :   }
     898             : 
     899           0 :   return true;
     900             : }
     901             : 
     902             : ///////////////////////////////////////////////////////////////////////////////////
     903             : 
     904             : already_AddRefed<nsXBLWindowKeyHandler>
     905           7 : NS_NewXBLWindowKeyHandler(nsIDOMElement* aElement, EventTarget* aTarget)
     906             : {
     907             :   RefPtr<nsXBLWindowKeyHandler> result =
     908          14 :     new nsXBLWindowKeyHandler(aElement, aTarget);
     909          14 :   return result.forget();
     910           9 : }

Generated by: LCOV version 1.13