Wed, 31 Dec 2014 06:09:35 +0100
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 }