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 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
24 * Original Author: David W. Hyatt (hyatt@netscape.com)
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
42 This file provides the implementation for the XUL Command Dispatcher.
46 #include "nsIContent.h"
47 #include "nsIFocusController.h"
48 #include "nsIControllers.h"
49 #include "nsIDOMDocument.h"
50 #include "nsIDOMXULDocument.h"
51 #include "nsIDOMHTMLDocument.h"
52 #include "nsIDOMElement.h"
53 #include "nsIDOMNSHTMLInputElement.h"
54 #include "nsIDOMNSHTMLTextAreaElement.h"
55 #include "nsIDOMWindowInternal.h"
56 #include "nsIDOMXULElement.h"
57 #include "nsIDocument.h"
58 #include "nsPresContext.h"
59 #include "nsIPresShell.h"
60 #include "nsIScriptGlobalObject.h"
61 #include "nsPIDOMWindow.h"
63 #include "nsXULCommandDispatcher.h"
65 #include "nsIDOMEventTarget.h"
66 #include "nsGUIEvent.h"
67 #include "nsContentUtils.h"
68 #include "nsReadableUtils.h"
70 #include "nsDOMError.h"
71 #include "nsEventDispatcher.h"
72 #include "nsPresShellIterator.h"
75 static PRLogModuleInfo
* gLog
;
78 ////////////////////////////////////////////////////////////////////////
80 nsXULCommandDispatcher::nsXULCommandDispatcher(nsIDocument
* aDocument
)
81 : mDocument(aDocument
), mUpdaters(nsnull
)
86 gLog
= PR_NewLogModule("nsXULCommandDispatcher");
90 nsXULCommandDispatcher::~nsXULCommandDispatcher()
95 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULCommandDispatcher
)
97 // QueryInterface implementation for nsXULCommandDispatcher
98 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULCommandDispatcher
)
99 NS_INTERFACE_MAP_ENTRY(nsIDOMXULCommandDispatcher
)
100 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
101 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIDOMXULCommandDispatcher
)
102 NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(XULCommandDispatcher
)
105 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULCommandDispatcher
)
106 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULCommandDispatcher
)
108 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULCommandDispatcher
)
110 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
112 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULCommandDispatcher
)
113 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocument
)
114 Updater
* updater
= tmp
->mUpdaters
;
116 cb
.NoteXPCOMChild(updater
->mElement
);
117 updater
= updater
->mNext
;
119 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
122 nsXULCommandDispatcher::Disconnect()
125 Updater
* doomed
= mUpdaters
;
126 mUpdaters
= mUpdaters
->mNext
;
133 nsXULCommandDispatcher::GetFocusController()
139 nsCOMPtr
<nsPIDOMWindow
> win(do_QueryInterface(mDocument
->GetScriptGlobalObject()));
140 return win
? win
->GetRootFocusController() : nsnull
;
143 ////////////////////////////////////////////////////////////////
144 // nsIDOMXULTracker Interface
147 nsXULCommandDispatcher::GetFocusedElement(nsIDOMElement
** aElement
)
149 nsIFocusController
* fc
= GetFocusController();
150 NS_ENSURE_TRUE(fc
, NS_ERROR_FAILURE
);
152 nsresult rv
= fc
->GetFocusedElement(aElement
);
153 NS_ENSURE_SUCCESS(rv
, rv
);
155 // Make sure the caller can access the focused element.
156 if (*aElement
&& !nsContentUtils::CanCallerAccess(*aElement
)) {
157 // XXX This might want to return null, but we use that return value
158 // to mean "there is no focused element," so to be clear, throw an
160 NS_RELEASE(*aElement
);
161 return NS_ERROR_DOM_SECURITY_ERR
;
168 nsXULCommandDispatcher::GetFocusedWindow(nsIDOMWindow
** aWindow
)
170 nsIFocusController
* fc
= GetFocusController();
171 NS_ENSURE_TRUE(fc
, NS_ERROR_FAILURE
);
173 nsCOMPtr
<nsIDOMWindowInternal
> window
;
174 nsresult rv
= fc
->GetFocusedWindow(getter_AddRefs(window
));
175 NS_ENSURE_TRUE(NS_SUCCEEDED(rv
) && window
, rv
);
177 rv
= CallQueryInterface(window
, aWindow
);
178 NS_ENSURE_SUCCESS(rv
, rv
);
180 // Make sure the caller can access this window. The caller can access this
181 // window iff it can access the document.
182 nsCOMPtr
<nsIDOMDocument
> domdoc
;
183 rv
= (*aWindow
)->GetDocument(getter_AddRefs(domdoc
));
184 NS_ENSURE_SUCCESS(rv
, rv
);
186 // Note: If there is no document, then this window has been cleared and
187 // there's nothing left to protect, so let the window pass through.
188 if (domdoc
&& !nsContentUtils::CanCallerAccess(domdoc
)) {
189 NS_RELEASE(*aWindow
);
190 return NS_ERROR_DOM_SECURITY_ERR
;
197 nsXULCommandDispatcher::SetFocusedElement(nsIDOMElement
* aElement
)
199 nsIFocusController
* fc
= GetFocusController();
200 NS_ENSURE_TRUE(fc
, NS_ERROR_FAILURE
);
202 return fc
->SetFocusedElement(aElement
);
206 nsXULCommandDispatcher::SetFocusedWindow(nsIDOMWindow
* aWindow
)
208 nsIFocusController
* fc
= GetFocusController();
209 NS_ENSURE_TRUE(fc
, NS_ERROR_FAILURE
);
211 nsCOMPtr
<nsIDOMWindowInternal
> window(do_QueryInterface(aWindow
));
213 return fc
->SetFocusedWindow(window
);
217 nsXULCommandDispatcher::AdvanceFocus()
219 nsIFocusController
* fc
= GetFocusController();
220 return fc
? fc
->MoveFocus(PR_TRUE
, nsnull
) : NS_OK
;
224 nsXULCommandDispatcher::RewindFocus()
226 nsIFocusController
* fc
= GetFocusController();
227 return fc
? fc
->MoveFocus(PR_FALSE
, nsnull
) : NS_OK
;
231 nsXULCommandDispatcher::AdvanceFocusIntoSubtree(nsIDOMElement
* aElt
)
233 nsIFocusController
* fc
= GetFocusController();
234 return fc
? fc
->MoveFocus(PR_TRUE
, aElt
) : NS_OK
;
238 nsXULCommandDispatcher::AddCommandUpdater(nsIDOMElement
* aElement
,
239 const nsAString
& aEvents
,
240 const nsAString
& aTargets
)
242 NS_PRECONDITION(aElement
!= nsnull
, "null ptr");
244 return NS_ERROR_NULL_POINTER
;
246 nsCOMPtr
<nsIDOMNode
> doc(do_QueryInterface(mDocument
));
248 nsresult rv
= nsContentUtils::CheckSameOrigin(doc
, aElement
);
254 Updater
* updater
= mUpdaters
;
255 Updater
** link
= &mUpdaters
;
258 if (updater
->mElement
== aElement
) {
261 if (PR_LOG_TEST(gLog
, PR_LOG_NOTICE
)) {
262 nsCAutoString eventsC
, targetsC
, aeventsC
, atargetsC
;
263 eventsC
.AssignWithConversion(updater
->mEvents
);
264 targetsC
.AssignWithConversion(updater
->mTargets
);
265 CopyUTF16toUTF8(aEvents
, aeventsC
);
266 CopyUTF16toUTF8(aTargets
, atargetsC
);
267 PR_LOG(gLog
, PR_LOG_NOTICE
,
268 ("xulcmd[%p] replace %p(events=%s targets=%s) with (events=%s targets=%s)",
277 // If the updater was already in the list, then replace
278 // (?) the 'events' and 'targets' filters with the new
280 updater
->mEvents
= aEvents
;
281 updater
->mTargets
= aTargets
;
285 link
= &(updater
->mNext
);
286 updater
= updater
->mNext
;
289 if (PR_LOG_TEST(gLog
, PR_LOG_NOTICE
)) {
290 nsCAutoString aeventsC
, atargetsC
;
291 CopyUTF16toUTF8(aEvents
, aeventsC
);
292 CopyUTF16toUTF8(aTargets
, atargetsC
);
294 PR_LOG(gLog
, PR_LOG_NOTICE
,
295 ("xulcmd[%p] add %p(events=%s targets=%s)",
302 // If we get here, this is a new updater. Append it to the list.
303 updater
= new Updater(aElement
, aEvents
, aTargets
);
305 return NS_ERROR_OUT_OF_MEMORY
;
312 nsXULCommandDispatcher::RemoveCommandUpdater(nsIDOMElement
* aElement
)
314 NS_PRECONDITION(aElement
!= nsnull
, "null ptr");
316 return NS_ERROR_NULL_POINTER
;
318 Updater
* updater
= mUpdaters
;
319 Updater
** link
= &mUpdaters
;
322 if (updater
->mElement
== aElement
) {
324 if (PR_LOG_TEST(gLog
, PR_LOG_NOTICE
)) {
325 nsCAutoString eventsC
, targetsC
;
326 eventsC
.AssignWithConversion(updater
->mEvents
);
327 targetsC
.AssignWithConversion(updater
->mTargets
);
328 PR_LOG(gLog
, PR_LOG_NOTICE
,
329 ("xulcmd[%p] remove %p(events=%s targets=%s)",
336 *link
= updater
->mNext
;
341 link
= &(updater
->mNext
);
342 updater
= updater
->mNext
;
345 // Hmm. Not found. Oh well.
350 nsXULCommandDispatcher::UpdateCommands(const nsAString
& aEventName
)
352 nsIFocusController
* fc
= GetFocusController();
353 NS_ENSURE_TRUE(fc
, NS_ERROR_FAILURE
);
356 nsCOMPtr
<nsIDOMElement
> element
;
357 fc
->GetFocusedElement(getter_AddRefs(element
));
359 nsresult rv
= element
->GetAttribute(NS_LITERAL_STRING("id"), id
);
360 NS_ASSERTION(NS_SUCCEEDED(rv
), "unable to get element's id");
361 if (NS_FAILED(rv
)) return rv
;
364 for (Updater
* updater
= mUpdaters
; updater
!= nsnull
; updater
= updater
->mNext
) {
365 // Skip any nodes that don't match our 'events' or 'targets'
367 if (! Matches(updater
->mEvents
, aEventName
))
370 if (! Matches(updater
->mTargets
, id
))
373 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(updater
->mElement
);
374 NS_ASSERTION(content
!= nsnull
, "not an nsIContent");
376 return NS_ERROR_UNEXPECTED
;
378 nsCOMPtr
<nsIDocument
> document
= content
->GetDocument();
380 NS_ASSERTION(document
!= nsnull
, "element has no document");
385 if (PR_LOG_TEST(gLog
, PR_LOG_NOTICE
)) {
386 nsCAutoString aeventnameC
;
387 CopyUTF16toUTF8(aEventName
, aeventnameC
);
388 PR_LOG(gLog
, PR_LOG_NOTICE
,
389 ("xulcmd[%p] update %p event=%s",
390 this, updater
->mElement
.get(),
395 nsPresShellIterator
iter(document
);
396 nsCOMPtr
<nsIPresShell
> shell
;
397 while ((shell
= iter
.GetNextShell())) {
399 // Retrieve the context in which our DOM event will fire.
400 nsCOMPtr
<nsPresContext
> context
= shell
->GetPresContext();
402 // Handle the DOM event
403 nsEventStatus status
= nsEventStatus_eIgnore
;
405 nsEvent
event(PR_TRUE
, NS_XUL_COMMAND_UPDATE
);
407 nsEventDispatcher::Dispatch(content
, context
, &event
, nsnull
, &status
);
414 nsXULCommandDispatcher::Matches(const nsString
& aList
,
415 const nsAString
& aElement
)
417 if (aList
.EqualsLiteral("*"))
418 return PR_TRUE
; // match _everything_!
420 PRInt32 indx
= aList
.Find(PromiseFlatString(aElement
));
422 return PR_FALSE
; // not in the list at all
424 // okay, now make sure it's not a substring snafu; e.g., 'ur'
425 // found inside of 'blur'.
427 PRUnichar ch
= aList
[indx
- 1];
428 if (! nsCRT::IsAsciiSpace(ch
) && ch
!= PRUnichar(','))
432 if (indx
+ aElement
.Length() < aList
.Length()) {
433 PRUnichar ch
= aList
[indx
+ aElement
.Length()];
434 if (! nsCRT::IsAsciiSpace(ch
) && ch
!= PRUnichar(','))
442 nsXULCommandDispatcher::GetControllers(nsIControllers
** aResult
)
444 nsIFocusController
* fc
= GetFocusController();
445 NS_ENSURE_TRUE(fc
, NS_ERROR_FAILURE
);
447 return fc
->GetControllers(aResult
);
451 nsXULCommandDispatcher::GetControllerForCommand(const char *aCommand
, nsIController
** _retval
)
453 nsIFocusController
* fc
= GetFocusController();
454 NS_ENSURE_TRUE(fc
, NS_ERROR_FAILURE
);
456 return fc
->GetControllerForCommand(aCommand
, _retval
);
460 nsXULCommandDispatcher::GetSuppressFocusScroll(PRBool
* aSuppressFocusScroll
)
462 nsIFocusController
* fc
= GetFocusController();
463 NS_ENSURE_TRUE(fc
, NS_ERROR_FAILURE
);
465 return fc
->GetSuppressFocusScroll(aSuppressFocusScroll
);
469 nsXULCommandDispatcher::SetSuppressFocusScroll(PRBool aSuppressFocusScroll
)
471 nsIFocusController
* fc
= GetFocusController();
472 NS_ENSURE_TRUE(fc
, NS_ERROR_FAILURE
);
474 return fc
->SetSuppressFocusScroll(aSuppressFocusScroll
);