content/xul/document/src/nsXULCommandDispatcher.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/xul/document/src/nsXULCommandDispatcher.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,490 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=2 sw=2 et tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +/*
    1.11 +
    1.12 +  This file provides the implementation for the XUL Command Dispatcher.
    1.13 +
    1.14 + */
    1.15 +
    1.16 +#include "nsIContent.h"
    1.17 +#include "nsFocusManager.h"
    1.18 +#include "nsIControllers.h"
    1.19 +#include "nsIDOMDocument.h"
    1.20 +#include "nsIDOMElement.h"
    1.21 +#include "nsIDOMWindow.h"
    1.22 +#include "nsIDOMXULElement.h"
    1.23 +#include "nsIDocument.h"
    1.24 +#include "nsPresContext.h"
    1.25 +#include "nsIPresShell.h"
    1.26 +#include "nsIScriptGlobalObject.h"
    1.27 +#include "nsPIDOMWindow.h"
    1.28 +#include "nsPIWindowRoot.h"
    1.29 +#include "nsRDFCID.h"
    1.30 +#include "nsXULCommandDispatcher.h"
    1.31 +#include "prlog.h"
    1.32 +#include "nsContentUtils.h"
    1.33 +#include "nsReadableUtils.h"
    1.34 +#include "nsCRT.h"
    1.35 +#include "nsError.h"
    1.36 +#include "nsDOMClassInfoID.h"
    1.37 +#include "mozilla/BasicEvents.h"
    1.38 +#include "mozilla/EventDispatcher.h"
    1.39 +#include "mozilla/dom/Element.h"
    1.40 +
    1.41 +using namespace mozilla;
    1.42 +
    1.43 +#ifdef PR_LOGGING
    1.44 +static PRLogModuleInfo* gCommandLog;
    1.45 +#endif
    1.46 +
    1.47 +////////////////////////////////////////////////////////////////////////
    1.48 +
    1.49 +nsXULCommandDispatcher::nsXULCommandDispatcher(nsIDocument* aDocument)
    1.50 +    : mDocument(aDocument), mUpdaters(nullptr)
    1.51 +{
    1.52 +
    1.53 +#ifdef PR_LOGGING
    1.54 +  if (! gCommandLog)
    1.55 +    gCommandLog = PR_NewLogModule("nsXULCommandDispatcher");
    1.56 +#endif
    1.57 +}
    1.58 +
    1.59 +nsXULCommandDispatcher::~nsXULCommandDispatcher()
    1.60 +{
    1.61 +  Disconnect();
    1.62 +}
    1.63 +
    1.64 +// QueryInterface implementation for nsXULCommandDispatcher
    1.65 +
    1.66 +DOMCI_DATA(XULCommandDispatcher, nsXULCommandDispatcher)
    1.67 +
    1.68 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULCommandDispatcher)
    1.69 +    NS_INTERFACE_MAP_ENTRY(nsIDOMXULCommandDispatcher)
    1.70 +    NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
    1.71 +    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMXULCommandDispatcher)
    1.72 +    NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(XULCommandDispatcher)
    1.73 +NS_INTERFACE_MAP_END
    1.74 +
    1.75 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULCommandDispatcher)
    1.76 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULCommandDispatcher)
    1.77 +
    1.78 +NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULCommandDispatcher)
    1.79 +
    1.80 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULCommandDispatcher)
    1.81 +  tmp->Disconnect();
    1.82 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    1.83 +
    1.84 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULCommandDispatcher)
    1.85 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
    1.86 +  Updater* updater = tmp->mUpdaters;
    1.87 +  while (updater) {
    1.88 +    cb.NoteXPCOMChild(updater->mElement);
    1.89 +    updater = updater->mNext;
    1.90 +  }
    1.91 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    1.92 +
    1.93 +void
    1.94 +nsXULCommandDispatcher::Disconnect()
    1.95 +{
    1.96 +  while (mUpdaters) {
    1.97 +    Updater* doomed = mUpdaters;
    1.98 +    mUpdaters = mUpdaters->mNext;
    1.99 +    delete doomed;
   1.100 +  }
   1.101 +  mDocument = nullptr;
   1.102 +}
   1.103 +
   1.104 +already_AddRefed<nsPIWindowRoot>
   1.105 +nsXULCommandDispatcher::GetWindowRoot()
   1.106 +{
   1.107 +  if (mDocument) {
   1.108 +    nsCOMPtr<nsPIDOMWindow> window(mDocument->GetWindow());
   1.109 +    if (window) {
   1.110 +      return window->GetTopWindowRoot();
   1.111 +    }
   1.112 +  }
   1.113 +
   1.114 +  return nullptr;
   1.115 +}
   1.116 +
   1.117 +nsIContent*
   1.118 +nsXULCommandDispatcher::GetRootFocusedContentAndWindow(nsPIDOMWindow** aWindow)
   1.119 +{
   1.120 +  *aWindow = nullptr;
   1.121 +
   1.122 +  if (mDocument) {
   1.123 +    nsCOMPtr<nsPIDOMWindow> win = mDocument->GetWindow();
   1.124 +    if (win) {
   1.125 +      nsCOMPtr<nsPIDOMWindow> rootWindow = win->GetPrivateRoot();
   1.126 +      if (rootWindow) {
   1.127 +        return nsFocusManager::GetFocusedDescendant(rootWindow, true, aWindow);
   1.128 +      }
   1.129 +    }
   1.130 +  }
   1.131 +
   1.132 +  return nullptr;
   1.133 +}
   1.134 +
   1.135 +NS_IMETHODIMP
   1.136 +nsXULCommandDispatcher::GetFocusedElement(nsIDOMElement** aElement)
   1.137 +{
   1.138 +  *aElement = nullptr;
   1.139 +
   1.140 +  nsCOMPtr<nsPIDOMWindow> focusedWindow;
   1.141 +  nsIContent* focusedContent =
   1.142 +    GetRootFocusedContentAndWindow(getter_AddRefs(focusedWindow));
   1.143 +  if (focusedContent) {
   1.144 +    CallQueryInterface(focusedContent, aElement);
   1.145 +
   1.146 +    // Make sure the caller can access the focused element.
   1.147 +    if (!nsContentUtils::CanCallerAccess(*aElement)) {
   1.148 +      // XXX This might want to return null, but we use that return value
   1.149 +      // to mean "there is no focused element," so to be clear, throw an
   1.150 +      // exception.
   1.151 +      NS_RELEASE(*aElement);
   1.152 +      return NS_ERROR_DOM_SECURITY_ERR;
   1.153 +    }
   1.154 +  }
   1.155 +
   1.156 +  return NS_OK;
   1.157 +}
   1.158 +
   1.159 +NS_IMETHODIMP
   1.160 +nsXULCommandDispatcher::GetFocusedWindow(nsIDOMWindow** aWindow)
   1.161 +{
   1.162 +  *aWindow = nullptr;
   1.163 +
   1.164 +  nsCOMPtr<nsPIDOMWindow> window;
   1.165 +  GetRootFocusedContentAndWindow(getter_AddRefs(window));
   1.166 +  if (!window)
   1.167 +    return NS_OK;
   1.168 +
   1.169 +  // Make sure the caller can access this window. The caller can access this
   1.170 +  // window iff it can access the document.
   1.171 +  nsCOMPtr<nsIDOMDocument> domdoc;
   1.172 +  nsresult rv = window->GetDocument(getter_AddRefs(domdoc));
   1.173 +  NS_ENSURE_SUCCESS(rv, rv);
   1.174 +
   1.175 +  // Note: If there is no document, then this window has been cleared and
   1.176 +  // there's nothing left to protect, so let the window pass through.
   1.177 +  if (domdoc && !nsContentUtils::CanCallerAccess(domdoc))
   1.178 +    return NS_ERROR_DOM_SECURITY_ERR;
   1.179 +
   1.180 +  CallQueryInterface(window, aWindow);
   1.181 +  return NS_OK;
   1.182 +}
   1.183 +
   1.184 +NS_IMETHODIMP
   1.185 +nsXULCommandDispatcher::SetFocusedElement(nsIDOMElement* aElement)
   1.186 +{
   1.187 +  nsIFocusManager* fm = nsFocusManager::GetFocusManager();
   1.188 +  NS_ENSURE_TRUE(fm, NS_ERROR_FAILURE);
   1.189 +
   1.190 +  if (aElement)
   1.191 +    return fm->SetFocus(aElement, 0);
   1.192 +
   1.193 +  // if aElement is null, clear the focus in the currently focused child window
   1.194 +  nsCOMPtr<nsPIDOMWindow> focusedWindow;
   1.195 +  GetRootFocusedContentAndWindow(getter_AddRefs(focusedWindow));
   1.196 +  return fm->ClearFocus(focusedWindow);
   1.197 +}
   1.198 +
   1.199 +NS_IMETHODIMP
   1.200 +nsXULCommandDispatcher::SetFocusedWindow(nsIDOMWindow* aWindow)
   1.201 +{
   1.202 +  NS_ENSURE_TRUE(aWindow, NS_OK); // do nothing if set to null
   1.203 +
   1.204 +  nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow));
   1.205 +  NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
   1.206 +
   1.207 +  nsIFocusManager* fm = nsFocusManager::GetFocusManager();
   1.208 +  NS_ENSURE_TRUE(fm, NS_ERROR_FAILURE);
   1.209 +
   1.210 +  // get the containing frame for the window, and set it as focused. This will
   1.211 +  // end up focusing whatever is currently focused inside the frame. Since
   1.212 +  // setting the command dispatcher's focused window doesn't raise the window,
   1.213 +  // setting it to a top-level window doesn't need to do anything.
   1.214 +  nsCOMPtr<nsIDOMElement> frameElement =
   1.215 +    do_QueryInterface(window->GetFrameElementInternal());
   1.216 +  if (frameElement)
   1.217 +    return fm->SetFocus(frameElement, 0);
   1.218 +
   1.219 +  return NS_OK;
   1.220 +}
   1.221 +
   1.222 +NS_IMETHODIMP
   1.223 +nsXULCommandDispatcher::AdvanceFocus()
   1.224 +{
   1.225 +  return AdvanceFocusIntoSubtree(nullptr);
   1.226 +}
   1.227 +
   1.228 +NS_IMETHODIMP
   1.229 +nsXULCommandDispatcher::RewindFocus()
   1.230 +{
   1.231 +  nsCOMPtr<nsPIDOMWindow> win;
   1.232 +  GetRootFocusedContentAndWindow(getter_AddRefs(win));
   1.233 +
   1.234 +  nsCOMPtr<nsIDOMElement> result;
   1.235 +  nsIFocusManager* fm = nsFocusManager::GetFocusManager();
   1.236 +  if (fm)
   1.237 +    return fm->MoveFocus(win, nullptr, nsIFocusManager::MOVEFOCUS_BACKWARD,
   1.238 +                         0, getter_AddRefs(result));
   1.239 +  return NS_OK;
   1.240 +}
   1.241 +
   1.242 +NS_IMETHODIMP
   1.243 +nsXULCommandDispatcher::AdvanceFocusIntoSubtree(nsIDOMElement* aElt)
   1.244 +{
   1.245 +  nsCOMPtr<nsPIDOMWindow> win;
   1.246 +  GetRootFocusedContentAndWindow(getter_AddRefs(win));
   1.247 +
   1.248 +  nsCOMPtr<nsIDOMElement> result;
   1.249 +  nsIFocusManager* fm = nsFocusManager::GetFocusManager();
   1.250 +  if (fm)
   1.251 +    return fm->MoveFocus(win, aElt, nsIFocusManager::MOVEFOCUS_FORWARD,
   1.252 +                         0, getter_AddRefs(result));
   1.253 +  return NS_OK;
   1.254 +}
   1.255 +
   1.256 +NS_IMETHODIMP
   1.257 +nsXULCommandDispatcher::AddCommandUpdater(nsIDOMElement* aElement,
   1.258 +                                          const nsAString& aEvents,
   1.259 +                                          const nsAString& aTargets)
   1.260 +{
   1.261 +  NS_PRECONDITION(aElement != nullptr, "null ptr");
   1.262 +  if (! aElement)
   1.263 +    return NS_ERROR_NULL_POINTER;
   1.264 +
   1.265 +  NS_ENSURE_TRUE(mDocument, NS_ERROR_UNEXPECTED);
   1.266 +
   1.267 +  nsresult rv = nsContentUtils::CheckSameOrigin(mDocument, aElement);
   1.268 +
   1.269 +  if (NS_FAILED(rv)) {
   1.270 +    return rv;
   1.271 +  }
   1.272 +
   1.273 +  Updater* updater = mUpdaters;
   1.274 +  Updater** link = &mUpdaters;
   1.275 +
   1.276 +  while (updater) {
   1.277 +    if (updater->mElement == aElement) {
   1.278 +
   1.279 +#ifdef DEBUG
   1.280 +      if (PR_LOG_TEST(gCommandLog, PR_LOG_NOTICE)) {
   1.281 +        nsAutoCString eventsC, targetsC, aeventsC, atargetsC; 
   1.282 +        eventsC.AssignWithConversion(updater->mEvents);
   1.283 +        targetsC.AssignWithConversion(updater->mTargets);
   1.284 +        CopyUTF16toUTF8(aEvents, aeventsC);
   1.285 +        CopyUTF16toUTF8(aTargets, atargetsC);
   1.286 +        PR_LOG(gCommandLog, PR_LOG_NOTICE,
   1.287 +               ("xulcmd[%p] replace %p(events=%s targets=%s) with (events=%s targets=%s)",
   1.288 +                this, aElement,
   1.289 +                eventsC.get(),
   1.290 +                targetsC.get(),
   1.291 +                aeventsC.get(),
   1.292 +                atargetsC.get()));
   1.293 +      }
   1.294 +#endif
   1.295 +
   1.296 +      // If the updater was already in the list, then replace
   1.297 +      // (?) the 'events' and 'targets' filters with the new
   1.298 +      // specification.
   1.299 +      updater->mEvents  = aEvents;
   1.300 +      updater->mTargets = aTargets;
   1.301 +      return NS_OK;
   1.302 +    }
   1.303 +
   1.304 +    link = &(updater->mNext);
   1.305 +    updater = updater->mNext;
   1.306 +  }
   1.307 +#ifdef DEBUG
   1.308 +  if (PR_LOG_TEST(gCommandLog, PR_LOG_NOTICE)) {
   1.309 +    nsAutoCString aeventsC, atargetsC; 
   1.310 +    CopyUTF16toUTF8(aEvents, aeventsC);
   1.311 +    CopyUTF16toUTF8(aTargets, atargetsC);
   1.312 +
   1.313 +    PR_LOG(gCommandLog, PR_LOG_NOTICE,
   1.314 +           ("xulcmd[%p] add     %p(events=%s targets=%s)",
   1.315 +            this, aElement,
   1.316 +            aeventsC.get(),
   1.317 +            atargetsC.get()));
   1.318 +  }
   1.319 +#endif
   1.320 +
   1.321 +  // If we get here, this is a new updater. Append it to the list.
   1.322 +  updater = new Updater(aElement, aEvents, aTargets);
   1.323 +  if (! updater)
   1.324 +      return NS_ERROR_OUT_OF_MEMORY;
   1.325 +
   1.326 +  *link = updater;
   1.327 +  return NS_OK;
   1.328 +}
   1.329 +
   1.330 +NS_IMETHODIMP
   1.331 +nsXULCommandDispatcher::RemoveCommandUpdater(nsIDOMElement* aElement)
   1.332 +{
   1.333 +  NS_PRECONDITION(aElement != nullptr, "null ptr");
   1.334 +  if (! aElement)
   1.335 +    return NS_ERROR_NULL_POINTER;
   1.336 +
   1.337 +  Updater* updater = mUpdaters;
   1.338 +  Updater** link = &mUpdaters;
   1.339 +
   1.340 +  while (updater) {
   1.341 +    if (updater->mElement == aElement) {
   1.342 +#ifdef DEBUG
   1.343 +      if (PR_LOG_TEST(gCommandLog, PR_LOG_NOTICE)) {
   1.344 +        nsAutoCString eventsC, targetsC; 
   1.345 +        eventsC.AssignWithConversion(updater->mEvents);
   1.346 +        targetsC.AssignWithConversion(updater->mTargets);
   1.347 +        PR_LOG(gCommandLog, PR_LOG_NOTICE,
   1.348 +               ("xulcmd[%p] remove  %p(events=%s targets=%s)",
   1.349 +                this, aElement,
   1.350 +                eventsC.get(),
   1.351 +                targetsC.get()));
   1.352 +      }
   1.353 +#endif
   1.354 +
   1.355 +      *link = updater->mNext;
   1.356 +      delete updater;
   1.357 +      return NS_OK;
   1.358 +    }
   1.359 +
   1.360 +    link = &(updater->mNext);
   1.361 +    updater = updater->mNext;
   1.362 +  }
   1.363 +
   1.364 +  // Hmm. Not found. Oh well.
   1.365 +  return NS_OK;
   1.366 +}
   1.367 +
   1.368 +NS_IMETHODIMP
   1.369 +nsXULCommandDispatcher::UpdateCommands(const nsAString& aEventName)
   1.370 +{
   1.371 +  nsAutoString id;
   1.372 +  nsCOMPtr<nsIDOMElement> element;
   1.373 +  GetFocusedElement(getter_AddRefs(element));
   1.374 +  if (element) {
   1.375 +    nsresult rv = element->GetAttribute(NS_LITERAL_STRING("id"), id);
   1.376 +    NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get element's id");
   1.377 +    if (NS_FAILED(rv)) return rv;
   1.378 +  }
   1.379 +
   1.380 +  nsCOMArray<nsIContent> updaters;
   1.381 +
   1.382 +  for (Updater* updater = mUpdaters; updater != nullptr; updater = updater->mNext) {
   1.383 +    // Skip any nodes that don't match our 'events' or 'targets'
   1.384 +    // filters.
   1.385 +    if (! Matches(updater->mEvents, aEventName))
   1.386 +      continue;
   1.387 +
   1.388 +    if (! Matches(updater->mTargets, id))
   1.389 +      continue;
   1.390 +
   1.391 +    nsCOMPtr<nsIContent> content = do_QueryInterface(updater->mElement);
   1.392 +    NS_ASSERTION(content != nullptr, "not an nsIContent");
   1.393 +    if (! content)
   1.394 +      return NS_ERROR_UNEXPECTED;
   1.395 +
   1.396 +    updaters.AppendObject(content);
   1.397 +  }
   1.398 +
   1.399 +  for (int32_t u = 0; u < updaters.Count(); u++) {
   1.400 +    nsIContent* content = updaters[u];
   1.401 +
   1.402 +    nsCOMPtr<nsIDocument> document = content->GetDocument();
   1.403 +
   1.404 +    NS_ASSERTION(document != nullptr, "element has no document");
   1.405 +    if (! document)
   1.406 +      continue;
   1.407 +
   1.408 +#ifdef DEBUG
   1.409 +    if (PR_LOG_TEST(gCommandLog, PR_LOG_NOTICE)) {
   1.410 +      nsAutoCString aeventnameC; 
   1.411 +      CopyUTF16toUTF8(aEventName, aeventnameC);
   1.412 +      PR_LOG(gCommandLog, PR_LOG_NOTICE,
   1.413 +             ("xulcmd[%p] update %p event=%s",
   1.414 +              this, content,
   1.415 +              aeventnameC.get()));
   1.416 +    }
   1.417 +#endif
   1.418 +
   1.419 +    nsCOMPtr<nsIPresShell> shell = document->GetShell();
   1.420 +    if (shell) {
   1.421 +      // Retrieve the context in which our DOM event will fire.
   1.422 +      nsRefPtr<nsPresContext> context = shell->GetPresContext();
   1.423 +
   1.424 +      // Handle the DOM event
   1.425 +      nsEventStatus status = nsEventStatus_eIgnore;
   1.426 +
   1.427 +      WidgetEvent event(true, NS_XUL_COMMAND_UPDATE);
   1.428 +
   1.429 +      EventDispatcher::Dispatch(content, context, &event, nullptr, &status);
   1.430 +    }
   1.431 +  }
   1.432 +  return NS_OK;
   1.433 +}
   1.434 +
   1.435 +bool
   1.436 +nsXULCommandDispatcher::Matches(const nsString& aList, 
   1.437 +                                const nsAString& aElement)
   1.438 +{
   1.439 +  if (aList.EqualsLiteral("*"))
   1.440 +    return true; // match _everything_!
   1.441 +
   1.442 +  int32_t indx = aList.Find(PromiseFlatString(aElement));
   1.443 +  if (indx == -1)
   1.444 +    return false; // not in the list at all
   1.445 +
   1.446 +  // okay, now make sure it's not a substring snafu; e.g., 'ur'
   1.447 +  // found inside of 'blur'.
   1.448 +  if (indx > 0) {
   1.449 +    char16_t ch = aList[indx - 1];
   1.450 +    if (! nsCRT::IsAsciiSpace(ch) && ch != char16_t(','))
   1.451 +      return false;
   1.452 +  }
   1.453 +
   1.454 +  if (indx + aElement.Length() < aList.Length()) {
   1.455 +    char16_t ch = aList[indx + aElement.Length()];
   1.456 +    if (! nsCRT::IsAsciiSpace(ch) && ch != char16_t(','))
   1.457 +      return false;
   1.458 +  }
   1.459 +
   1.460 +  return true;
   1.461 +}
   1.462 +
   1.463 +NS_IMETHODIMP
   1.464 +nsXULCommandDispatcher::GetControllers(nsIControllers** aResult)
   1.465 +{
   1.466 +  nsCOMPtr<nsPIWindowRoot> root = GetWindowRoot();
   1.467 +  NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
   1.468 +
   1.469 +  return root->GetControllers(aResult);
   1.470 +}
   1.471 +
   1.472 +NS_IMETHODIMP
   1.473 +nsXULCommandDispatcher::GetControllerForCommand(const char *aCommand, nsIController** _retval)
   1.474 +{
   1.475 +  nsCOMPtr<nsPIWindowRoot> root = GetWindowRoot();
   1.476 +  NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
   1.477 +
   1.478 +  return root->GetControllerForCommand(aCommand, _retval);
   1.479 +}
   1.480 +
   1.481 +NS_IMETHODIMP
   1.482 +nsXULCommandDispatcher::GetSuppressFocusScroll(bool* aSuppressFocusScroll)
   1.483 +{
   1.484 +  *aSuppressFocusScroll = false;
   1.485 +  return NS_OK;
   1.486 +}
   1.487 +
   1.488 +NS_IMETHODIMP
   1.489 +nsXULCommandDispatcher::SetSuppressFocusScroll(bool aSuppressFocusScroll)
   1.490 +{
   1.491 +  return NS_OK;
   1.492 +}
   1.493 +

mercurial