content/xul/document/src/nsXULCommandDispatcher.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=2 sw=2 et 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/. */
     7 /*
     9   This file provides the implementation for the XUL Command Dispatcher.
    11  */
    13 #include "nsIContent.h"
    14 #include "nsFocusManager.h"
    15 #include "nsIControllers.h"
    16 #include "nsIDOMDocument.h"
    17 #include "nsIDOMElement.h"
    18 #include "nsIDOMWindow.h"
    19 #include "nsIDOMXULElement.h"
    20 #include "nsIDocument.h"
    21 #include "nsPresContext.h"
    22 #include "nsIPresShell.h"
    23 #include "nsIScriptGlobalObject.h"
    24 #include "nsPIDOMWindow.h"
    25 #include "nsPIWindowRoot.h"
    26 #include "nsRDFCID.h"
    27 #include "nsXULCommandDispatcher.h"
    28 #include "prlog.h"
    29 #include "nsContentUtils.h"
    30 #include "nsReadableUtils.h"
    31 #include "nsCRT.h"
    32 #include "nsError.h"
    33 #include "nsDOMClassInfoID.h"
    34 #include "mozilla/BasicEvents.h"
    35 #include "mozilla/EventDispatcher.h"
    36 #include "mozilla/dom/Element.h"
    38 using namespace mozilla;
    40 #ifdef PR_LOGGING
    41 static PRLogModuleInfo* gCommandLog;
    42 #endif
    44 ////////////////////////////////////////////////////////////////////////
    46 nsXULCommandDispatcher::nsXULCommandDispatcher(nsIDocument* aDocument)
    47     : mDocument(aDocument), mUpdaters(nullptr)
    48 {
    50 #ifdef PR_LOGGING
    51   if (! gCommandLog)
    52     gCommandLog = PR_NewLogModule("nsXULCommandDispatcher");
    53 #endif
    54 }
    56 nsXULCommandDispatcher::~nsXULCommandDispatcher()
    57 {
    58   Disconnect();
    59 }
    61 // QueryInterface implementation for nsXULCommandDispatcher
    63 DOMCI_DATA(XULCommandDispatcher, nsXULCommandDispatcher)
    65 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULCommandDispatcher)
    66     NS_INTERFACE_MAP_ENTRY(nsIDOMXULCommandDispatcher)
    67     NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
    68     NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMXULCommandDispatcher)
    69     NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(XULCommandDispatcher)
    70 NS_INTERFACE_MAP_END
    72 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULCommandDispatcher)
    73 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULCommandDispatcher)
    75 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULCommandDispatcher)
    77 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULCommandDispatcher)
    78   tmp->Disconnect();
    79 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    81 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULCommandDispatcher)
    82   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
    83   Updater* updater = tmp->mUpdaters;
    84   while (updater) {
    85     cb.NoteXPCOMChild(updater->mElement);
    86     updater = updater->mNext;
    87   }
    88 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    90 void
    91 nsXULCommandDispatcher::Disconnect()
    92 {
    93   while (mUpdaters) {
    94     Updater* doomed = mUpdaters;
    95     mUpdaters = mUpdaters->mNext;
    96     delete doomed;
    97   }
    98   mDocument = nullptr;
    99 }
   101 already_AddRefed<nsPIWindowRoot>
   102 nsXULCommandDispatcher::GetWindowRoot()
   103 {
   104   if (mDocument) {
   105     nsCOMPtr<nsPIDOMWindow> window(mDocument->GetWindow());
   106     if (window) {
   107       return window->GetTopWindowRoot();
   108     }
   109   }
   111   return nullptr;
   112 }
   114 nsIContent*
   115 nsXULCommandDispatcher::GetRootFocusedContentAndWindow(nsPIDOMWindow** aWindow)
   116 {
   117   *aWindow = nullptr;
   119   if (mDocument) {
   120     nsCOMPtr<nsPIDOMWindow> win = mDocument->GetWindow();
   121     if (win) {
   122       nsCOMPtr<nsPIDOMWindow> rootWindow = win->GetPrivateRoot();
   123       if (rootWindow) {
   124         return nsFocusManager::GetFocusedDescendant(rootWindow, true, aWindow);
   125       }
   126     }
   127   }
   129   return nullptr;
   130 }
   132 NS_IMETHODIMP
   133 nsXULCommandDispatcher::GetFocusedElement(nsIDOMElement** aElement)
   134 {
   135   *aElement = nullptr;
   137   nsCOMPtr<nsPIDOMWindow> focusedWindow;
   138   nsIContent* focusedContent =
   139     GetRootFocusedContentAndWindow(getter_AddRefs(focusedWindow));
   140   if (focusedContent) {
   141     CallQueryInterface(focusedContent, aElement);
   143     // Make sure the caller can access the focused element.
   144     if (!nsContentUtils::CanCallerAccess(*aElement)) {
   145       // XXX This might want to return null, but we use that return value
   146       // to mean "there is no focused element," so to be clear, throw an
   147       // exception.
   148       NS_RELEASE(*aElement);
   149       return NS_ERROR_DOM_SECURITY_ERR;
   150     }
   151   }
   153   return NS_OK;
   154 }
   156 NS_IMETHODIMP
   157 nsXULCommandDispatcher::GetFocusedWindow(nsIDOMWindow** aWindow)
   158 {
   159   *aWindow = nullptr;
   161   nsCOMPtr<nsPIDOMWindow> window;
   162   GetRootFocusedContentAndWindow(getter_AddRefs(window));
   163   if (!window)
   164     return NS_OK;
   166   // Make sure the caller can access this window. The caller can access this
   167   // window iff it can access the document.
   168   nsCOMPtr<nsIDOMDocument> domdoc;
   169   nsresult rv = window->GetDocument(getter_AddRefs(domdoc));
   170   NS_ENSURE_SUCCESS(rv, rv);
   172   // Note: If there is no document, then this window has been cleared and
   173   // there's nothing left to protect, so let the window pass through.
   174   if (domdoc && !nsContentUtils::CanCallerAccess(domdoc))
   175     return NS_ERROR_DOM_SECURITY_ERR;
   177   CallQueryInterface(window, aWindow);
   178   return NS_OK;
   179 }
   181 NS_IMETHODIMP
   182 nsXULCommandDispatcher::SetFocusedElement(nsIDOMElement* aElement)
   183 {
   184   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
   185   NS_ENSURE_TRUE(fm, NS_ERROR_FAILURE);
   187   if (aElement)
   188     return fm->SetFocus(aElement, 0);
   190   // if aElement is null, clear the focus in the currently focused child window
   191   nsCOMPtr<nsPIDOMWindow> focusedWindow;
   192   GetRootFocusedContentAndWindow(getter_AddRefs(focusedWindow));
   193   return fm->ClearFocus(focusedWindow);
   194 }
   196 NS_IMETHODIMP
   197 nsXULCommandDispatcher::SetFocusedWindow(nsIDOMWindow* aWindow)
   198 {
   199   NS_ENSURE_TRUE(aWindow, NS_OK); // do nothing if set to null
   201   nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow));
   202   NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
   204   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
   205   NS_ENSURE_TRUE(fm, NS_ERROR_FAILURE);
   207   // get the containing frame for the window, and set it as focused. This will
   208   // end up focusing whatever is currently focused inside the frame. Since
   209   // setting the command dispatcher's focused window doesn't raise the window,
   210   // setting it to a top-level window doesn't need to do anything.
   211   nsCOMPtr<nsIDOMElement> frameElement =
   212     do_QueryInterface(window->GetFrameElementInternal());
   213   if (frameElement)
   214     return fm->SetFocus(frameElement, 0);
   216   return NS_OK;
   217 }
   219 NS_IMETHODIMP
   220 nsXULCommandDispatcher::AdvanceFocus()
   221 {
   222   return AdvanceFocusIntoSubtree(nullptr);
   223 }
   225 NS_IMETHODIMP
   226 nsXULCommandDispatcher::RewindFocus()
   227 {
   228   nsCOMPtr<nsPIDOMWindow> win;
   229   GetRootFocusedContentAndWindow(getter_AddRefs(win));
   231   nsCOMPtr<nsIDOMElement> result;
   232   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
   233   if (fm)
   234     return fm->MoveFocus(win, nullptr, nsIFocusManager::MOVEFOCUS_BACKWARD,
   235                          0, getter_AddRefs(result));
   236   return NS_OK;
   237 }
   239 NS_IMETHODIMP
   240 nsXULCommandDispatcher::AdvanceFocusIntoSubtree(nsIDOMElement* aElt)
   241 {
   242   nsCOMPtr<nsPIDOMWindow> win;
   243   GetRootFocusedContentAndWindow(getter_AddRefs(win));
   245   nsCOMPtr<nsIDOMElement> result;
   246   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
   247   if (fm)
   248     return fm->MoveFocus(win, aElt, nsIFocusManager::MOVEFOCUS_FORWARD,
   249                          0, getter_AddRefs(result));
   250   return NS_OK;
   251 }
   253 NS_IMETHODIMP
   254 nsXULCommandDispatcher::AddCommandUpdater(nsIDOMElement* aElement,
   255                                           const nsAString& aEvents,
   256                                           const nsAString& aTargets)
   257 {
   258   NS_PRECONDITION(aElement != nullptr, "null ptr");
   259   if (! aElement)
   260     return NS_ERROR_NULL_POINTER;
   262   NS_ENSURE_TRUE(mDocument, NS_ERROR_UNEXPECTED);
   264   nsresult rv = nsContentUtils::CheckSameOrigin(mDocument, aElement);
   266   if (NS_FAILED(rv)) {
   267     return rv;
   268   }
   270   Updater* updater = mUpdaters;
   271   Updater** link = &mUpdaters;
   273   while (updater) {
   274     if (updater->mElement == aElement) {
   276 #ifdef DEBUG
   277       if (PR_LOG_TEST(gCommandLog, PR_LOG_NOTICE)) {
   278         nsAutoCString eventsC, targetsC, aeventsC, atargetsC; 
   279         eventsC.AssignWithConversion(updater->mEvents);
   280         targetsC.AssignWithConversion(updater->mTargets);
   281         CopyUTF16toUTF8(aEvents, aeventsC);
   282         CopyUTF16toUTF8(aTargets, atargetsC);
   283         PR_LOG(gCommandLog, PR_LOG_NOTICE,
   284                ("xulcmd[%p] replace %p(events=%s targets=%s) with (events=%s targets=%s)",
   285                 this, aElement,
   286                 eventsC.get(),
   287                 targetsC.get(),
   288                 aeventsC.get(),
   289                 atargetsC.get()));
   290       }
   291 #endif
   293       // If the updater was already in the list, then replace
   294       // (?) the 'events' and 'targets' filters with the new
   295       // specification.
   296       updater->mEvents  = aEvents;
   297       updater->mTargets = aTargets;
   298       return NS_OK;
   299     }
   301     link = &(updater->mNext);
   302     updater = updater->mNext;
   303   }
   304 #ifdef DEBUG
   305   if (PR_LOG_TEST(gCommandLog, PR_LOG_NOTICE)) {
   306     nsAutoCString aeventsC, atargetsC; 
   307     CopyUTF16toUTF8(aEvents, aeventsC);
   308     CopyUTF16toUTF8(aTargets, atargetsC);
   310     PR_LOG(gCommandLog, PR_LOG_NOTICE,
   311            ("xulcmd[%p] add     %p(events=%s targets=%s)",
   312             this, aElement,
   313             aeventsC.get(),
   314             atargetsC.get()));
   315   }
   316 #endif
   318   // If we get here, this is a new updater. Append it to the list.
   319   updater = new Updater(aElement, aEvents, aTargets);
   320   if (! updater)
   321       return NS_ERROR_OUT_OF_MEMORY;
   323   *link = updater;
   324   return NS_OK;
   325 }
   327 NS_IMETHODIMP
   328 nsXULCommandDispatcher::RemoveCommandUpdater(nsIDOMElement* aElement)
   329 {
   330   NS_PRECONDITION(aElement != nullptr, "null ptr");
   331   if (! aElement)
   332     return NS_ERROR_NULL_POINTER;
   334   Updater* updater = mUpdaters;
   335   Updater** link = &mUpdaters;
   337   while (updater) {
   338     if (updater->mElement == aElement) {
   339 #ifdef DEBUG
   340       if (PR_LOG_TEST(gCommandLog, PR_LOG_NOTICE)) {
   341         nsAutoCString eventsC, targetsC; 
   342         eventsC.AssignWithConversion(updater->mEvents);
   343         targetsC.AssignWithConversion(updater->mTargets);
   344         PR_LOG(gCommandLog, PR_LOG_NOTICE,
   345                ("xulcmd[%p] remove  %p(events=%s targets=%s)",
   346                 this, aElement,
   347                 eventsC.get(),
   348                 targetsC.get()));
   349       }
   350 #endif
   352       *link = updater->mNext;
   353       delete updater;
   354       return NS_OK;
   355     }
   357     link = &(updater->mNext);
   358     updater = updater->mNext;
   359   }
   361   // Hmm. Not found. Oh well.
   362   return NS_OK;
   363 }
   365 NS_IMETHODIMP
   366 nsXULCommandDispatcher::UpdateCommands(const nsAString& aEventName)
   367 {
   368   nsAutoString id;
   369   nsCOMPtr<nsIDOMElement> element;
   370   GetFocusedElement(getter_AddRefs(element));
   371   if (element) {
   372     nsresult rv = element->GetAttribute(NS_LITERAL_STRING("id"), id);
   373     NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get element's id");
   374     if (NS_FAILED(rv)) return rv;
   375   }
   377   nsCOMArray<nsIContent> updaters;
   379   for (Updater* updater = mUpdaters; updater != nullptr; updater = updater->mNext) {
   380     // Skip any nodes that don't match our 'events' or 'targets'
   381     // filters.
   382     if (! Matches(updater->mEvents, aEventName))
   383       continue;
   385     if (! Matches(updater->mTargets, id))
   386       continue;
   388     nsCOMPtr<nsIContent> content = do_QueryInterface(updater->mElement);
   389     NS_ASSERTION(content != nullptr, "not an nsIContent");
   390     if (! content)
   391       return NS_ERROR_UNEXPECTED;
   393     updaters.AppendObject(content);
   394   }
   396   for (int32_t u = 0; u < updaters.Count(); u++) {
   397     nsIContent* content = updaters[u];
   399     nsCOMPtr<nsIDocument> document = content->GetDocument();
   401     NS_ASSERTION(document != nullptr, "element has no document");
   402     if (! document)
   403       continue;
   405 #ifdef DEBUG
   406     if (PR_LOG_TEST(gCommandLog, PR_LOG_NOTICE)) {
   407       nsAutoCString aeventnameC; 
   408       CopyUTF16toUTF8(aEventName, aeventnameC);
   409       PR_LOG(gCommandLog, PR_LOG_NOTICE,
   410              ("xulcmd[%p] update %p event=%s",
   411               this, content,
   412               aeventnameC.get()));
   413     }
   414 #endif
   416     nsCOMPtr<nsIPresShell> shell = document->GetShell();
   417     if (shell) {
   418       // Retrieve the context in which our DOM event will fire.
   419       nsRefPtr<nsPresContext> context = shell->GetPresContext();
   421       // Handle the DOM event
   422       nsEventStatus status = nsEventStatus_eIgnore;
   424       WidgetEvent event(true, NS_XUL_COMMAND_UPDATE);
   426       EventDispatcher::Dispatch(content, context, &event, nullptr, &status);
   427     }
   428   }
   429   return NS_OK;
   430 }
   432 bool
   433 nsXULCommandDispatcher::Matches(const nsString& aList, 
   434                                 const nsAString& aElement)
   435 {
   436   if (aList.EqualsLiteral("*"))
   437     return true; // match _everything_!
   439   int32_t indx = aList.Find(PromiseFlatString(aElement));
   440   if (indx == -1)
   441     return false; // not in the list at all
   443   // okay, now make sure it's not a substring snafu; e.g., 'ur'
   444   // found inside of 'blur'.
   445   if (indx > 0) {
   446     char16_t ch = aList[indx - 1];
   447     if (! nsCRT::IsAsciiSpace(ch) && ch != char16_t(','))
   448       return false;
   449   }
   451   if (indx + aElement.Length() < aList.Length()) {
   452     char16_t ch = aList[indx + aElement.Length()];
   453     if (! nsCRT::IsAsciiSpace(ch) && ch != char16_t(','))
   454       return false;
   455   }
   457   return true;
   458 }
   460 NS_IMETHODIMP
   461 nsXULCommandDispatcher::GetControllers(nsIControllers** aResult)
   462 {
   463   nsCOMPtr<nsPIWindowRoot> root = GetWindowRoot();
   464   NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
   466   return root->GetControllers(aResult);
   467 }
   469 NS_IMETHODIMP
   470 nsXULCommandDispatcher::GetControllerForCommand(const char *aCommand, nsIController** _retval)
   471 {
   472   nsCOMPtr<nsPIWindowRoot> root = GetWindowRoot();
   473   NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
   475   return root->GetControllerForCommand(aCommand, _retval);
   476 }
   478 NS_IMETHODIMP
   479 nsXULCommandDispatcher::GetSuppressFocusScroll(bool* aSuppressFocusScroll)
   480 {
   481   *aSuppressFocusScroll = false;
   482   return NS_OK;
   483 }
   485 NS_IMETHODIMP
   486 nsXULCommandDispatcher::SetSuppressFocusScroll(bool aSuppressFocusScroll)
   487 {
   488   return NS_OK;
   489 }

mercurial