dom/xbl/nsXBLWindowKeyHandler.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsCOMPtr.h"
     7 #include "nsXBLPrototypeHandler.h"
     8 #include "nsXBLWindowKeyHandler.h"
     9 #include "nsIContent.h"
    10 #include "nsIAtom.h"
    11 #include "nsIDOMKeyEvent.h"
    12 #include "nsXBLService.h"
    13 #include "nsIServiceManager.h"
    14 #include "nsGkAtoms.h"
    15 #include "nsXBLDocumentInfo.h"
    16 #include "nsIDOMElement.h"
    17 #include "nsFocusManager.h"
    18 #include "nsIURI.h"
    19 #include "nsNetUtil.h"
    20 #include "nsContentUtils.h"
    21 #include "nsXBLPrototypeBinding.h"
    22 #include "nsPIDOMWindow.h"
    23 #include "nsIDocShell.h"
    24 #include "nsIPresShell.h"
    25 #include "mozilla/EventStateManager.h"
    26 #include "nsISelectionController.h"
    27 #include "mozilla/Preferences.h"
    28 #include "mozilla/TextEvents.h"
    29 #include "mozilla/dom/Element.h"
    30 #include "mozilla/dom/Event.h"
    31 #include "nsIEditor.h"
    32 #include "nsIHTMLEditor.h"
    33 #include "nsIDOMDocument.h"
    35 using namespace mozilla;
    36 using namespace mozilla::dom;
    38 class nsXBLSpecialDocInfo : public nsIObserver
    39 {
    40 public:
    41   nsRefPtr<nsXBLDocumentInfo> mHTMLBindings;
    42   nsRefPtr<nsXBLDocumentInfo> mUserHTMLBindings;
    44   static const char sHTMLBindingStr[];
    45   static const char sUserHTMLBindingStr[];
    47   bool mInitialized;
    49 public:
    50   NS_DECL_ISUPPORTS
    51   NS_DECL_NSIOBSERVER
    53   void LoadDocInfo();
    54   void GetAllHandlers(const char* aType,
    55                       nsXBLPrototypeHandler** handler,
    56                       nsXBLPrototypeHandler** userHandler);
    57   void GetHandlers(nsXBLDocumentInfo* aInfo,
    58                    const nsACString& aRef,
    59                    nsXBLPrototypeHandler** aResult);
    61   nsXBLSpecialDocInfo() : mInitialized(false) {}
    63   virtual ~nsXBLSpecialDocInfo() {}
    65 };
    67 const char nsXBLSpecialDocInfo::sHTMLBindingStr[] =
    68   "chrome://global/content/platformHTMLBindings.xml";
    70 NS_IMPL_ISUPPORTS(nsXBLSpecialDocInfo, nsIObserver)
    72 NS_IMETHODIMP
    73 nsXBLSpecialDocInfo::Observe(nsISupports* aSubject,
    74                              const char* aTopic,
    75                              const char16_t* aData)
    76 {
    77   MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"), "wrong topic");
    79   // On shutdown, clear our fields to avoid an extra cycle collection.
    80   mHTMLBindings = nullptr;
    81   mUserHTMLBindings = nullptr;
    82   mInitialized = false;
    83   nsContentUtils::UnregisterShutdownObserver(this);
    85   return NS_OK;
    86 }
    88 void nsXBLSpecialDocInfo::LoadDocInfo()
    89 {
    90   if (mInitialized)
    91     return;
    92   mInitialized = true;
    93   nsContentUtils::RegisterShutdownObserver(this);
    95   nsXBLService* xblService = nsXBLService::GetInstance();
    96   if (!xblService)
    97     return;
    99   // Obtain the platform doc info
   100   nsCOMPtr<nsIURI> bindingURI;
   101   NS_NewURI(getter_AddRefs(bindingURI), sHTMLBindingStr);
   102   if (!bindingURI) {
   103     return;
   104   }
   105   xblService->LoadBindingDocumentInfo(nullptr, nullptr,
   106                                       bindingURI,
   107                                       nullptr,
   108                                       true, 
   109                                       getter_AddRefs(mHTMLBindings));
   111   const nsAdoptingCString& userHTMLBindingStr =
   112     Preferences::GetCString("dom.userHTMLBindings.uri");
   113   if (!userHTMLBindingStr.IsEmpty()) {
   114     NS_NewURI(getter_AddRefs(bindingURI), userHTMLBindingStr);
   115     if (!bindingURI) {
   116       return;
   117     }
   119     xblService->LoadBindingDocumentInfo(nullptr, nullptr,
   120                                         bindingURI,
   121                                         nullptr,
   122                                         true, 
   123                                         getter_AddRefs(mUserHTMLBindings));
   124   }
   125 }
   127 //
   128 // GetHandlers
   129 //
   130 // 
   131 void
   132 nsXBLSpecialDocInfo::GetHandlers(nsXBLDocumentInfo* aInfo,
   133                                  const nsACString& aRef,
   134                                  nsXBLPrototypeHandler** aResult)
   135 {
   136   nsXBLPrototypeBinding* binding = aInfo->GetPrototypeBinding(aRef);
   138   NS_ASSERTION(binding, "No binding found for the XBL window key handler.");
   139   if (!binding)
   140     return;
   142   *aResult = binding->GetPrototypeHandlers();
   143 }
   145 void
   146 nsXBLSpecialDocInfo::GetAllHandlers(const char* aType,
   147                                     nsXBLPrototypeHandler** aHandler,
   148                                     nsXBLPrototypeHandler** aUserHandler)
   149 {
   150   if (mUserHTMLBindings) {
   151     nsAutoCString type(aType);
   152     type.Append("User");
   153     GetHandlers(mUserHTMLBindings, type, aUserHandler);
   154   }
   155   if (mHTMLBindings) {
   156     GetHandlers(mHTMLBindings, nsDependentCString(aType), aHandler);
   157   }
   158 }
   160 // Init statics
   161 nsXBLSpecialDocInfo* nsXBLWindowKeyHandler::sXBLSpecialDocInfo = nullptr;
   162 uint32_t nsXBLWindowKeyHandler::sRefCnt = 0;
   164 nsXBLWindowKeyHandler::nsXBLWindowKeyHandler(nsIDOMElement* aElement,
   165                                              EventTarget* aTarget)
   166   : mTarget(aTarget),
   167     mHandler(nullptr),
   168     mUserHandler(nullptr)
   169 {
   170   mWeakPtrForElement = do_GetWeakReference(aElement);
   171   ++sRefCnt;
   172 }
   174 nsXBLWindowKeyHandler::~nsXBLWindowKeyHandler()
   175 {
   176   // If mWeakPtrForElement is non-null, we created a prototype handler.
   177   if (mWeakPtrForElement)
   178     delete mHandler;
   180   --sRefCnt;
   181   if (!sRefCnt) {
   182     NS_IF_RELEASE(sXBLSpecialDocInfo);
   183   }
   184 }
   186 NS_IMPL_ISUPPORTS(nsXBLWindowKeyHandler,
   187                   nsIDOMEventListener)
   189 static void
   190 BuildHandlerChain(nsIContent* aContent, nsXBLPrototypeHandler** aResult)
   191 {
   192   *aResult = nullptr;
   194   // Since we chain each handler onto the next handler,
   195   // we'll enumerate them here in reverse so that when we
   196   // walk the chain they'll come out in the original order
   197   for (nsIContent* key = aContent->GetLastChild();
   198        key;
   199        key = key->GetPreviousSibling()) {
   201     if (key->NodeInfo()->Equals(nsGkAtoms::key, kNameSpaceID_XUL)) {
   202       // Check whether the key element has empty value at key/char attribute.
   203       // Such element is used by localizers for alternative shortcut key
   204       // definition on the locale. See bug 426501.
   205       nsAutoString valKey, valCharCode, valKeyCode;
   206       bool attrExists =
   207         key->GetAttr(kNameSpaceID_None, nsGkAtoms::key, valKey) ||
   208         key->GetAttr(kNameSpaceID_None, nsGkAtoms::charcode, valCharCode) ||
   209         key->GetAttr(kNameSpaceID_None, nsGkAtoms::keycode, valKeyCode);
   210       if (attrExists &&
   211           valKey.IsEmpty() && valCharCode.IsEmpty() && valKeyCode.IsEmpty())
   212         continue;
   214       nsXBLPrototypeHandler* handler = new nsXBLPrototypeHandler(key);
   216       if (!handler)
   217         return;
   219       handler->SetNextHandler(*aResult);
   220       *aResult = handler;
   221     }
   222   }
   223 }
   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 nsXBLWindowKeyHandler::EnsureHandlers()
   233 {
   234   nsCOMPtr<Element> el = GetElement();
   235   NS_ENSURE_STATE(!mWeakPtrForElement || el);
   236   if (el) {
   237     // We are actually a XUL <keyset>.
   238     if (mHandler)
   239       return NS_OK;
   241     nsCOMPtr<nsIContent> content(do_QueryInterface(el));
   242     BuildHandlerChain(content, &mHandler);
   243   } else { // We are an XBL file of handlers.
   244     if (!sXBLSpecialDocInfo) {
   245       sXBLSpecialDocInfo = new nsXBLSpecialDocInfo();
   246       NS_ADDREF(sXBLSpecialDocInfo);
   247     }
   248     sXBLSpecialDocInfo->LoadDocInfo();
   250     // Now determine which handlers we should be using.
   251     if (IsHTMLEditableFieldFocused()) {
   252       sXBLSpecialDocInfo->GetAllHandlers("editor", &mHandler, &mUserHandler);
   253     }
   254     else {
   255       sXBLSpecialDocInfo->GetAllHandlers("browser", &mHandler, &mUserHandler);
   256     }
   257   }
   259   return NS_OK;
   260 }
   262 nsresult
   263 nsXBLWindowKeyHandler::WalkHandlers(nsIDOMKeyEvent* aKeyEvent, nsIAtom* aEventType)
   264 {
   265   bool prevent;
   266   aKeyEvent->GetDefaultPrevented(&prevent);
   267   if (prevent)
   268     return NS_OK;
   270   bool trustedEvent = false;
   271   // Don't process the event if it was not dispatched from a trusted source
   272   aKeyEvent->GetIsTrusted(&trustedEvent);
   274   if (!trustedEvent)
   275     return NS_OK;
   277   nsresult rv = EnsureHandlers();
   278   NS_ENSURE_SUCCESS(rv, rv);
   280   bool isDisabled;
   281   nsCOMPtr<Element> el = GetElement(&isDisabled);
   282   if (!el) {
   283     if (mUserHandler) {
   284       WalkHandlersInternal(aKeyEvent, aEventType, mUserHandler, true);
   285       aKeyEvent->GetDefaultPrevented(&prevent);
   286       if (prevent)
   287         return NS_OK; // Handled by the user bindings. Our work here is done.
   288     }
   289   }
   291   // skip keysets that are disabled
   292   if (el && isDisabled) {
   293     return NS_OK;
   294   }
   296   WalkHandlersInternal(aKeyEvent, aEventType, mHandler, true);
   298   return NS_OK;
   299 }
   301 NS_IMETHODIMP
   302 nsXBLWindowKeyHandler::HandleEvent(nsIDOMEvent* aEvent)
   303 {
   304   nsCOMPtr<nsIDOMKeyEvent> keyEvent(do_QueryInterface(aEvent));
   305   NS_ENSURE_TRUE(keyEvent, NS_ERROR_INVALID_ARG);
   307   uint16_t eventPhase;
   308   aEvent->GetEventPhase(&eventPhase);
   309   if (eventPhase == nsIDOMEvent::CAPTURING_PHASE) {
   310     HandleEventOnCapture(keyEvent);
   311     return NS_OK;
   312   }
   314   nsAutoString eventType;
   315   aEvent->GetType(eventType);
   316   nsCOMPtr<nsIAtom> eventTypeAtom = do_GetAtom(eventType);
   317   NS_ENSURE_TRUE(eventTypeAtom, NS_ERROR_OUT_OF_MEMORY);
   319   return WalkHandlers(keyEvent, eventTypeAtom);
   320 }
   322 void
   323 nsXBLWindowKeyHandler::HandleEventOnCapture(nsIDOMKeyEvent* aEvent)
   324 {
   325   WidgetKeyboardEvent* widgetEvent =
   326     aEvent->GetInternalNSEvent()->AsKeyboardEvent();
   328   if (widgetEvent->mFlags.mNoCrossProcessBoundaryForwarding) {
   329     return;
   330   }
   332   nsCOMPtr<mozilla::dom::Element> originalTarget =
   333     do_QueryInterface(aEvent->GetInternalNSEvent()->originalTarget);
   334   if (!EventStateManager::IsRemoteTarget(originalTarget)) {
   335     return;
   336   }
   338   if (!HasHandlerForEvent(aEvent)) {
   339     return;
   340   }
   342   // If this event hasn't been marked as mNoCrossProcessBoundaryForwarding
   343   // yet, it means it wasn't processed by content. We'll not call any
   344   // of the handlers at this moment, and will wait for the event to be
   345   // redispatched with mNoCrossProcessBoundaryForwarding = 1 to process it.
   347   // Inform the child process that this is a event that we want a reply
   348   // from.
   349   widgetEvent->mFlags.mWantReplyFromContentProcess = 1;
   350   aEvent->StopPropagation();
   351 }
   353 //
   354 // EventMatched
   355 //
   356 // See if the given handler cares about this particular key event
   357 //
   358 bool
   359 nsXBLWindowKeyHandler::EventMatched(nsXBLPrototypeHandler* inHandler,
   360                                     nsIAtom* inEventType,
   361                                     nsIDOMKeyEvent* inEvent,
   362                                     uint32_t aCharCode, bool aIgnoreShiftKey)
   363 {
   364   return inHandler->KeyEventMatched(inEventType, inEvent, aCharCode,
   365                                     aIgnoreShiftKey);
   366 }
   368 bool
   369 nsXBLWindowKeyHandler::IsHTMLEditableFieldFocused()
   370 {
   371   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
   372   if (!fm)
   373     return false;
   375   nsCOMPtr<nsIDOMWindow> focusedWindow;
   376   fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
   377   if (!focusedWindow)
   378     return false;
   380   nsCOMPtr<nsPIDOMWindow> piwin(do_QueryInterface(focusedWindow));
   381   nsIDocShell *docShell = piwin->GetDocShell();
   382   if (!docShell) {
   383     return false;
   384   }
   386   nsCOMPtr<nsIEditor> editor;
   387   docShell->GetEditor(getter_AddRefs(editor));
   388   nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(editor);
   389   if (!htmlEditor) {
   390     return false;
   391   }
   393   nsCOMPtr<nsIDOMDocument> domDocument;
   394   editor->GetDocument(getter_AddRefs(domDocument));
   395   nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDocument);
   396   if (doc->HasFlag(NODE_IS_EDITABLE)) {
   397     // Don't need to perform any checks in designMode documents.
   398     return true;
   399   }
   401   nsCOMPtr<nsIDOMElement> focusedElement;
   402   fm->GetFocusedElement(getter_AddRefs(focusedElement));
   403   nsCOMPtr<nsINode> focusedNode = do_QueryInterface(focusedElement);
   404   if (focusedNode) {
   405     // If there is a focused element, make sure it's in the active editing host.
   406     // Note that GetActiveEditingHost finds the current editing host based on
   407     // the document's selection.  Even though the document selection is usually
   408     // collapsed to where the focus is, but the page may modify the selection
   409     // without our knowledge, in which case this check will do something useful.
   410     nsCOMPtr<Element> activeEditingHost = htmlEditor->GetActiveEditingHost();
   411     if (!activeEditingHost) {
   412       return false;
   413     }
   414     return nsContentUtils::ContentIsDescendantOf(focusedNode, activeEditingHost);
   415   }
   417   return false;
   418 }
   420 //
   421 // WalkHandlersInternal and WalkHandlersAndExecute
   422 //
   423 // Given a particular DOM event and a pointer to the first handler in the list,
   424 // scan through the list to find something to handle the event. If aExecute = true,
   425 // the handler will be executed; otherwise just return an answer telling if a handler
   426 // for that event was found.
   427 //
   428 bool
   429 nsXBLWindowKeyHandler::WalkHandlersInternal(nsIDOMKeyEvent* aKeyEvent,
   430                                             nsIAtom* aEventType, 
   431                                             nsXBLPrototypeHandler* aHandler,
   432                                             bool aExecute)
   433 {
   434   nsAutoTArray<nsShortcutCandidate, 10> accessKeys;
   435   nsContentUtils::GetAccelKeyCandidates(aKeyEvent, accessKeys);
   437   if (accessKeys.IsEmpty()) {
   438     return WalkHandlersAndExecute(aKeyEvent, aEventType, aHandler,
   439                                   0, false, aExecute);
   440   }
   442   for (uint32_t i = 0; i < accessKeys.Length(); ++i) {
   443     nsShortcutCandidate &key = accessKeys[i];
   444     if (WalkHandlersAndExecute(aKeyEvent, aEventType, aHandler,
   445                                key.mCharCode, key.mIgnoreShift, aExecute))
   446       return true;
   447   }
   448   return false;
   449 }
   451 bool
   452 nsXBLWindowKeyHandler::WalkHandlersAndExecute(nsIDOMKeyEvent* aKeyEvent,
   453                                               nsIAtom* aEventType,
   454                                               nsXBLPrototypeHandler* aHandler,
   455                                               uint32_t aCharCode,
   456                                               bool aIgnoreShiftKey,
   457                                               bool aExecute)
   458 {
   459   nsresult rv;
   461   // Try all of the handlers until we find one that matches the event.
   462   for (nsXBLPrototypeHandler *currHandler = aHandler; currHandler;
   463        currHandler = currHandler->GetNextHandler()) {
   464     bool stopped = aKeyEvent->IsDispatchStopped();
   465     if (stopped) {
   466       // The event is finished, don't execute any more handlers
   467       return false;
   468     }
   470     if (!EventMatched(currHandler, aEventType, aKeyEvent,
   471                       aCharCode, aIgnoreShiftKey))
   472       continue;  // try the next one
   474     // Before executing this handler, check that it's not disabled,
   475     // and that it has something to do (oncommand of the <key> or its
   476     // <command> is non-empty).
   477     nsCOMPtr<nsIContent> elt = currHandler->GetHandlerElement();
   478     nsCOMPtr<Element> commandElt;
   480     // See if we're in a XUL doc.
   481     nsCOMPtr<Element> el = GetElement();
   482     if (el && elt) {
   483       // We are.  Obtain our command attribute.
   484       nsAutoString command;
   485       elt->GetAttr(kNameSpaceID_None, nsGkAtoms::command, command);
   486       if (!command.IsEmpty()) {
   487         // Locate the command element in question.  Note that we
   488         // know "elt" is in a doc if we're dealing with it here.
   489         NS_ASSERTION(elt->IsInDoc(), "elt must be in document");
   490         nsIDocument *doc = elt->GetCurrentDoc();
   491         if (doc)
   492           commandElt = do_QueryInterface(doc->GetElementById(command));
   494         if (!commandElt) {
   495           NS_ERROR("A XUL <key> is observing a command that doesn't exist. Unable to execute key binding!");
   496           continue;
   497         }
   498       }
   499     }
   501     if (!commandElt) {
   502       commandElt = do_QueryInterface(elt);
   503     }
   505     if (commandElt) {
   506       nsAutoString value;
   507       commandElt->GetAttribute(NS_LITERAL_STRING("disabled"), value);
   508       if (value.EqualsLiteral("true")) {
   509         continue;  // this handler is disabled, try the next one
   510       }
   512       // Check that there is an oncommand handler
   513       commandElt->GetAttribute(NS_LITERAL_STRING("oncommand"), value);
   514       if (value.IsEmpty()) {
   515         continue;  // nothing to do
   516       }
   517     }
   519     nsCOMPtr<EventTarget> piTarget;
   520     nsCOMPtr<Element> element = GetElement();
   521     if (element) {
   522       piTarget = commandElt;
   523     } else {
   524       piTarget = mTarget;
   525     }
   527     if (!aExecute) {
   528       return true;
   529     }
   531     rv = currHandler->ExecuteHandler(piTarget, aKeyEvent);
   532     if (NS_SUCCEEDED(rv)) {
   533       return true;
   534     }
   535   }
   537   return false;
   538 }
   540 bool
   541 nsXBLWindowKeyHandler::HasHandlerForEvent(nsIDOMKeyEvent* aEvent)
   542 {
   543   if (!aEvent->InternalDOMEvent()->IsTrusted()) {
   544     return false;
   545   }
   547   nsresult rv = EnsureHandlers();
   548   NS_ENSURE_SUCCESS(rv, false);
   550   bool isDisabled;
   551   nsCOMPtr<Element> el = GetElement(&isDisabled);
   552   if (el && isDisabled) {
   553     return false;
   554   }
   556   nsAutoString eventType;
   557   aEvent->GetType(eventType);
   558   nsCOMPtr<nsIAtom> eventTypeAtom = do_GetAtom(eventType);
   559   NS_ENSURE_TRUE(eventTypeAtom, false);
   561   return WalkHandlersInternal(aEvent, eventTypeAtom, mHandler, false);
   562 }
   564 already_AddRefed<Element>
   565 nsXBLWindowKeyHandler::GetElement(bool* aIsDisabled)
   566 {
   567   nsCOMPtr<Element> element = do_QueryReferent(mWeakPtrForElement);
   568   if (element && aIsDisabled) {
   569     *aIsDisabled = element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
   570                                         nsGkAtoms::_true, eCaseMatters);
   571   }
   572   return element.forget();
   573 }
   575 ///////////////////////////////////////////////////////////////////////////////////
   577 already_AddRefed<nsXBLWindowKeyHandler>
   578 NS_NewXBLWindowKeyHandler(nsIDOMElement* aElement, EventTarget* aTarget)
   579 {
   580   nsRefPtr<nsXBLWindowKeyHandler> result =
   581     new nsXBLWindowKeyHandler(aElement, aTarget);
   582   return result.forget();
   583 }

mercurial