nsXULCommandDispatcher.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  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/. */
  5. /*
  6. This file provides the implementation for the XUL Command Dispatcher.
  7. */
  8. #include "nsIContent.h"
  9. #include "nsFocusManager.h"
  10. #include "nsIControllers.h"
  11. #include "nsIDOMDocument.h"
  12. #include "nsIDOMElement.h"
  13. #include "nsIDOMWindow.h"
  14. #include "nsIDOMXULElement.h"
  15. #include "nsIDocument.h"
  16. #include "nsPresContext.h"
  17. #include "nsIPresShell.h"
  18. #include "nsIScriptGlobalObject.h"
  19. #include "nsPIDOMWindow.h"
  20. #include "nsPIWindowRoot.h"
  21. #include "nsRDFCID.h"
  22. #include "nsXULCommandDispatcher.h"
  23. #include "mozilla/Logging.h"
  24. #include "nsContentUtils.h"
  25. #include "nsReadableUtils.h"
  26. #include "nsCRT.h"
  27. #include "nsError.h"
  28. #include "nsDOMClassInfoID.h"
  29. #include "mozilla/BasicEvents.h"
  30. #include "mozilla/EventDispatcher.h"
  31. #include "mozilla/dom/Element.h"
  32. using namespace mozilla;
  33. static LazyLogModule gCommandLog("nsXULCommandDispatcher");
  34. ////////////////////////////////////////////////////////////////////////
  35. nsXULCommandDispatcher::nsXULCommandDispatcher(nsIDocument* aDocument)
  36. : mDocument(aDocument), mUpdaters(nullptr)
  37. {
  38. }
  39. nsXULCommandDispatcher::~nsXULCommandDispatcher()
  40. {
  41. Disconnect();
  42. }
  43. // QueryInterface implementation for nsXULCommandDispatcher
  44. NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULCommandDispatcher)
  45. NS_INTERFACE_MAP_ENTRY(nsIDOMXULCommandDispatcher)
  46. NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
  47. NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMXULCommandDispatcher)
  48. NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(XULCommandDispatcher)
  49. NS_INTERFACE_MAP_END
  50. NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULCommandDispatcher)
  51. NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULCommandDispatcher)
  52. NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULCommandDispatcher)
  53. NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULCommandDispatcher)
  54. tmp->Disconnect();
  55. NS_IMPL_CYCLE_COLLECTION_UNLINK_END
  56. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULCommandDispatcher)
  57. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
  58. Updater* updater = tmp->mUpdaters;
  59. while (updater) {
  60. cb.NoteXPCOMChild(updater->mElement);
  61. updater = updater->mNext;
  62. }
  63. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
  64. void
  65. nsXULCommandDispatcher::Disconnect()
  66. {
  67. while (mUpdaters) {
  68. Updater* doomed = mUpdaters;
  69. mUpdaters = mUpdaters->mNext;
  70. delete doomed;
  71. }
  72. mDocument = nullptr;
  73. }
  74. already_AddRefed<nsPIWindowRoot>
  75. nsXULCommandDispatcher::GetWindowRoot()
  76. {
  77. if (mDocument) {
  78. if (nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetWindow()) {
  79. return window->GetTopWindowRoot();
  80. }
  81. }
  82. return nullptr;
  83. }
  84. nsIContent*
  85. nsXULCommandDispatcher::GetRootFocusedContentAndWindow(nsPIDOMWindowOuter** aWindow)
  86. {
  87. *aWindow = nullptr;
  88. if (!mDocument) {
  89. return nullptr;
  90. }
  91. if (nsCOMPtr<nsPIDOMWindowOuter> win = mDocument->GetWindow()) {
  92. if (nsCOMPtr<nsPIDOMWindowOuter> rootWindow = win->GetPrivateRoot()) {
  93. return nsFocusManager::GetFocusedDescendant(rootWindow, true, aWindow);
  94. }
  95. }
  96. return nullptr;
  97. }
  98. NS_IMETHODIMP
  99. nsXULCommandDispatcher::GetFocusedElement(nsIDOMElement** aElement)
  100. {
  101. *aElement = nullptr;
  102. nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
  103. nsIContent* focusedContent =
  104. GetRootFocusedContentAndWindow(getter_AddRefs(focusedWindow));
  105. if (focusedContent) {
  106. CallQueryInterface(focusedContent, aElement);
  107. // Make sure the caller can access the focused element.
  108. nsCOMPtr<nsINode> node = do_QueryInterface(*aElement);
  109. if (!node || !nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller()->Subsumes(node->NodePrincipal())) {
  110. // XXX This might want to return null, but we use that return value
  111. // to mean "there is no focused element," so to be clear, throw an
  112. // exception.
  113. NS_RELEASE(*aElement);
  114. return NS_ERROR_DOM_SECURITY_ERR;
  115. }
  116. }
  117. return NS_OK;
  118. }
  119. NS_IMETHODIMP
  120. nsXULCommandDispatcher::GetFocusedWindow(mozIDOMWindowProxy** aWindow)
  121. {
  122. *aWindow = nullptr;
  123. nsCOMPtr<nsPIDOMWindowOuter> window;
  124. GetRootFocusedContentAndWindow(getter_AddRefs(window));
  125. if (!window)
  126. return NS_OK;
  127. // Make sure the caller can access this window. The caller can access this
  128. // window iff it can access the document.
  129. nsCOMPtr<nsIDocument> doc = window->GetDoc();
  130. // Note: If there is no document, then this window has been cleared and
  131. // there's nothing left to protect, so let the window pass through.
  132. if (doc && !nsContentUtils::CanCallerAccess(doc))
  133. return NS_ERROR_DOM_SECURITY_ERR;
  134. window.forget(aWindow);
  135. return NS_OK;
  136. }
  137. NS_IMETHODIMP
  138. nsXULCommandDispatcher::SetFocusedElement(nsIDOMElement* aElement)
  139. {
  140. nsIFocusManager* fm = nsFocusManager::GetFocusManager();
  141. NS_ENSURE_TRUE(fm, NS_ERROR_FAILURE);
  142. if (aElement)
  143. return fm->SetFocus(aElement, 0);
  144. // if aElement is null, clear the focus in the currently focused child window
  145. nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
  146. GetRootFocusedContentAndWindow(getter_AddRefs(focusedWindow));
  147. return fm->ClearFocus(focusedWindow);
  148. }
  149. NS_IMETHODIMP
  150. nsXULCommandDispatcher::SetFocusedWindow(mozIDOMWindowProxy* aWindow)
  151. {
  152. NS_ENSURE_TRUE(aWindow, NS_OK); // do nothing if set to null
  153. nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(aWindow);
  154. NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
  155. nsIFocusManager* fm = nsFocusManager::GetFocusManager();
  156. NS_ENSURE_TRUE(fm, NS_ERROR_FAILURE);
  157. // get the containing frame for the window, and set it as focused. This will
  158. // end up focusing whatever is currently focused inside the frame. Since
  159. // setting the command dispatcher's focused window doesn't raise the window,
  160. // setting it to a top-level window doesn't need to do anything.
  161. nsCOMPtr<nsIDOMElement> frameElement =
  162. do_QueryInterface(window->GetFrameElementInternal());
  163. if (frameElement)
  164. return fm->SetFocus(frameElement, 0);
  165. return NS_OK;
  166. }
  167. NS_IMETHODIMP
  168. nsXULCommandDispatcher::AdvanceFocus()
  169. {
  170. return AdvanceFocusIntoSubtree(nullptr);
  171. }
  172. NS_IMETHODIMP
  173. nsXULCommandDispatcher::RewindFocus()
  174. {
  175. nsCOMPtr<nsPIDOMWindowOuter> win;
  176. GetRootFocusedContentAndWindow(getter_AddRefs(win));
  177. nsCOMPtr<nsIDOMElement> result;
  178. nsIFocusManager* fm = nsFocusManager::GetFocusManager();
  179. if (fm)
  180. return fm->MoveFocus(win, nullptr, nsIFocusManager::MOVEFOCUS_BACKWARD,
  181. 0, getter_AddRefs(result));
  182. return NS_OK;
  183. }
  184. NS_IMETHODIMP
  185. nsXULCommandDispatcher::AdvanceFocusIntoSubtree(nsIDOMElement* aElt)
  186. {
  187. nsCOMPtr<nsPIDOMWindowOuter> win;
  188. GetRootFocusedContentAndWindow(getter_AddRefs(win));
  189. nsCOMPtr<nsIDOMElement> result;
  190. nsIFocusManager* fm = nsFocusManager::GetFocusManager();
  191. if (fm)
  192. return fm->MoveFocus(win, aElt, nsIFocusManager::MOVEFOCUS_FORWARD,
  193. 0, getter_AddRefs(result));
  194. return NS_OK;
  195. }
  196. NS_IMETHODIMP
  197. nsXULCommandDispatcher::AddCommandUpdater(nsIDOMElement* aElement,
  198. const nsAString& aEvents,
  199. const nsAString& aTargets)
  200. {
  201. NS_PRECONDITION(aElement != nullptr, "null ptr");
  202. if (! aElement)
  203. return NS_ERROR_NULL_POINTER;
  204. NS_ENSURE_TRUE(mDocument, NS_ERROR_UNEXPECTED);
  205. nsresult rv = nsContentUtils::CheckSameOrigin(mDocument, aElement);
  206. if (NS_FAILED(rv)) {
  207. return rv;
  208. }
  209. Updater* updater = mUpdaters;
  210. Updater** link = &mUpdaters;
  211. while (updater) {
  212. if (updater->mElement == aElement) {
  213. #ifdef DEBUG
  214. if (MOZ_LOG_TEST(gCommandLog, LogLevel::Debug)) {
  215. nsAutoCString eventsC, targetsC, aeventsC, atargetsC;
  216. eventsC.AssignWithConversion(updater->mEvents);
  217. targetsC.AssignWithConversion(updater->mTargets);
  218. CopyUTF16toUTF8(aEvents, aeventsC);
  219. CopyUTF16toUTF8(aTargets, atargetsC);
  220. MOZ_LOG(gCommandLog, LogLevel::Debug,
  221. ("xulcmd[%p] replace %p(events=%s targets=%s) with (events=%s targets=%s)",
  222. this, aElement,
  223. eventsC.get(),
  224. targetsC.get(),
  225. aeventsC.get(),
  226. atargetsC.get()));
  227. }
  228. #endif
  229. // If the updater was already in the list, then replace
  230. // (?) the 'events' and 'targets' filters with the new
  231. // specification.
  232. updater->mEvents = aEvents;
  233. updater->mTargets = aTargets;
  234. return NS_OK;
  235. }
  236. link = &(updater->mNext);
  237. updater = updater->mNext;
  238. }
  239. #ifdef DEBUG
  240. if (MOZ_LOG_TEST(gCommandLog, LogLevel::Debug)) {
  241. nsAutoCString aeventsC, atargetsC;
  242. CopyUTF16toUTF8(aEvents, aeventsC);
  243. CopyUTF16toUTF8(aTargets, atargetsC);
  244. MOZ_LOG(gCommandLog, LogLevel::Debug,
  245. ("xulcmd[%p] add %p(events=%s targets=%s)",
  246. this, aElement,
  247. aeventsC.get(),
  248. atargetsC.get()));
  249. }
  250. #endif
  251. // If we get here, this is a new updater. Append it to the list.
  252. *link = new Updater(aElement, aEvents, aTargets);
  253. return NS_OK;
  254. }
  255. NS_IMETHODIMP
  256. nsXULCommandDispatcher::RemoveCommandUpdater(nsIDOMElement* aElement)
  257. {
  258. NS_PRECONDITION(aElement != nullptr, "null ptr");
  259. if (! aElement)
  260. return NS_ERROR_NULL_POINTER;
  261. Updater* updater = mUpdaters;
  262. Updater** link = &mUpdaters;
  263. while (updater) {
  264. if (updater->mElement == aElement) {
  265. #ifdef DEBUG
  266. if (MOZ_LOG_TEST(gCommandLog, LogLevel::Debug)) {
  267. nsAutoCString eventsC, targetsC;
  268. eventsC.AssignWithConversion(updater->mEvents);
  269. targetsC.AssignWithConversion(updater->mTargets);
  270. MOZ_LOG(gCommandLog, LogLevel::Debug,
  271. ("xulcmd[%p] remove %p(events=%s targets=%s)",
  272. this, aElement,
  273. eventsC.get(),
  274. targetsC.get()));
  275. }
  276. #endif
  277. *link = updater->mNext;
  278. delete updater;
  279. return NS_OK;
  280. }
  281. link = &(updater->mNext);
  282. updater = updater->mNext;
  283. }
  284. // Hmm. Not found. Oh well.
  285. return NS_OK;
  286. }
  287. NS_IMETHODIMP
  288. nsXULCommandDispatcher::UpdateCommands(const nsAString& aEventName)
  289. {
  290. nsAutoString id;
  291. nsCOMPtr<nsIDOMElement> element;
  292. GetFocusedElement(getter_AddRefs(element));
  293. if (element) {
  294. nsresult rv = element->GetAttribute(NS_LITERAL_STRING("id"), id);
  295. NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get element's id");
  296. if (NS_FAILED(rv)) return rv;
  297. }
  298. nsCOMArray<nsIContent> updaters;
  299. for (Updater* updater = mUpdaters; updater != nullptr; updater = updater->mNext) {
  300. // Skip any nodes that don't match our 'events' or 'targets'
  301. // filters.
  302. if (! Matches(updater->mEvents, aEventName))
  303. continue;
  304. if (! Matches(updater->mTargets, id))
  305. continue;
  306. nsCOMPtr<nsIContent> content = do_QueryInterface(updater->mElement);
  307. NS_ASSERTION(content != nullptr, "not an nsIContent");
  308. if (! content)
  309. return NS_ERROR_UNEXPECTED;
  310. updaters.AppendObject(content);
  311. }
  312. for (int32_t u = 0; u < updaters.Count(); u++) {
  313. nsIContent* content = updaters[u];
  314. #ifdef DEBUG
  315. if (MOZ_LOG_TEST(gCommandLog, LogLevel::Debug)) {
  316. nsAutoCString aeventnameC;
  317. CopyUTF16toUTF8(aEventName, aeventnameC);
  318. MOZ_LOG(gCommandLog, LogLevel::Debug,
  319. ("xulcmd[%p] update %p event=%s",
  320. this, content,
  321. aeventnameC.get()));
  322. }
  323. #endif
  324. WidgetEvent event(true, eXULCommandUpdate);
  325. EventDispatcher::Dispatch(content, nullptr, &event);
  326. }
  327. return NS_OK;
  328. }
  329. bool
  330. nsXULCommandDispatcher::Matches(const nsString& aList,
  331. const nsAString& aElement)
  332. {
  333. if (aList.EqualsLiteral("*"))
  334. return true; // match _everything_!
  335. int32_t indx = aList.Find(PromiseFlatString(aElement));
  336. if (indx == -1)
  337. return false; // not in the list at all
  338. // okay, now make sure it's not a substring snafu; e.g., 'ur'
  339. // found inside of 'blur'.
  340. if (indx > 0) {
  341. char16_t ch = aList[indx - 1];
  342. if (! nsCRT::IsAsciiSpace(ch) && ch != char16_t(','))
  343. return false;
  344. }
  345. if (indx + aElement.Length() < aList.Length()) {
  346. char16_t ch = aList[indx + aElement.Length()];
  347. if (! nsCRT::IsAsciiSpace(ch) && ch != char16_t(','))
  348. return false;
  349. }
  350. return true;
  351. }
  352. NS_IMETHODIMP
  353. nsXULCommandDispatcher::GetControllers(nsIControllers** aResult)
  354. {
  355. nsCOMPtr<nsPIWindowRoot> root = GetWindowRoot();
  356. NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
  357. return root->GetControllers(aResult);
  358. }
  359. NS_IMETHODIMP
  360. nsXULCommandDispatcher::GetControllerForCommand(const char *aCommand, nsIController** _retval)
  361. {
  362. nsCOMPtr<nsPIWindowRoot> root = GetWindowRoot();
  363. NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
  364. return root->GetControllerForCommand(aCommand, _retval);
  365. }
  366. NS_IMETHODIMP
  367. nsXULCommandDispatcher::GetSuppressFocusScroll(bool* aSuppressFocusScroll)
  368. {
  369. *aSuppressFocusScroll = false;
  370. return NS_OK;
  371. }
  372. NS_IMETHODIMP
  373. nsXULCommandDispatcher::SetSuppressFocusScroll(bool aSuppressFocusScroll)
  374. {
  375. return NS_OK;
  376. }