1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozila.org code.
16 : *
17 : * The Initial Developer of the Original Code is Mozilla Foundation
18 : * Portions created by the Initial Developer are Copyright (C) 2008
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : *
23 : * Alternatively, the contents of this file may be used under the terms of
24 : * either the GNU General Public License Version 2 or later (the "GPL"), or
25 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 : * in which case the provisions of the GPL or the LGPL are applicable instead
27 : * of those above. If you wish to allow use of your version of this file only
28 : * under the terms of either the GPL or the LGPL, and not to allow others to
29 : * use your version of this file under the terms of the MPL, indicate your
30 : * decision by deleting the provisions above and replace them with the notice
31 : * and other provisions required by the GPL or the LGPL. If you do not delete
32 : * the provisions above, a recipient may use your version of this file under
33 : * the terms of any one of the MPL, the GPL or the LGPL.
34 : *
35 : * ***** END LICENSE BLOCK ***** */
36 :
37 : #include "mozilla/dom/TabParent.h"
38 :
39 : #include "nsFocusManager.h"
40 :
41 : #include "nsIInterfaceRequestor.h"
42 : #include "nsIInterfaceRequestorUtils.h"
43 : #include "nsIServiceManager.h"
44 : #include "nsIEnumerator.h"
45 : #include "nsGkAtoms.h"
46 : #include "nsContentUtils.h"
47 : #include "nsIDocument.h"
48 : #include "nsIDOMWindow.h"
49 : #include "nsPIDOMWindow.h"
50 : #include "nsIDOMElement.h"
51 : #include "nsIDOMXULElement.h"
52 : #include "nsIDOMHTMLFrameElement.h"
53 : #include "nsIDOMHTMLInputElement.h"
54 : #include "nsIDOMHTMLMapElement.h"
55 : #include "nsIDOMHTMLLegendElement.h"
56 : #include "nsIDOMDocument.h"
57 : #include "nsIDOMRange.h"
58 : #include "nsIHTMLDocument.h"
59 : #include "nsIFormControlFrame.h"
60 : #include "nsGenericHTMLElement.h"
61 : #include "nsIDocShell.h"
62 : #include "nsIEditorDocShell.h"
63 : #include "nsIDocShellTreeItem.h"
64 : #include "nsIDocShellTreeOwner.h"
65 : #include "nsLayoutUtils.h"
66 : #include "nsIPresShell.h"
67 : #include "nsIContentViewer.h"
68 : #include "nsFrameTraversal.h"
69 : #include "nsObjectFrame.h"
70 : #include "nsEventDispatcher.h"
71 : #include "nsEventStateManager.h"
72 : #include "nsIMEStateManager.h"
73 : #include "nsIWebNavigation.h"
74 : #include "nsCaret.h"
75 : #include "nsIBaseWindow.h"
76 : #include "nsIViewManager.h"
77 : #include "nsFrameSelection.h"
78 : #include "nsXULPopupManager.h"
79 : #include "nsIDOMNodeFilter.h"
80 : #include "nsIScriptObjectPrincipal.h"
81 : #include "nsIPrincipal.h"
82 : #include "mozilla/dom/Element.h"
83 : #include "mozAutoDocUpdate.h"
84 : #include "mozilla/Preferences.h"
85 : #include "mozilla/LookAndFeel.h"
86 : #include "nsIScriptError.h"
87 :
88 : #ifdef MOZ_XUL
89 : #include "nsIDOMXULTextboxElement.h"
90 : #include "nsIDOMXULMenuListElement.h"
91 : #endif
92 :
93 : #ifdef ACCESSIBILITY
94 : #include "nsAccessibilityService.h"
95 : #endif
96 :
97 : using namespace mozilla;
98 : using namespace mozilla::dom;
99 : using namespace mozilla::widget;
100 :
101 : //#define DEBUG_FOCUS 1
102 : //#define DEBUG_FOCUS_NAVIGATION 1
103 : #define PRINTTAGF(format, content) \
104 : { \
105 : nsAutoString tag(NS_LITERAL_STRING("(none)")); \
106 : if (content) \
107 : content->Tag()->ToString(tag); \
108 : printf(format, NS_ConvertUTF16toUTF8(tag).get()); \
109 : }
110 :
111 : struct nsDelayedBlurOrFocusEvent
112 0 : {
113 0 : nsDelayedBlurOrFocusEvent(PRUint32 aType,
114 : nsIPresShell* aPresShell,
115 : nsIDocument* aDocument,
116 : nsIDOMEventTarget* aTarget)
117 : : mType(aType),
118 : mPresShell(aPresShell),
119 : mDocument(aDocument),
120 0 : mTarget(aTarget) { }
121 :
122 0 : nsDelayedBlurOrFocusEvent(const nsDelayedBlurOrFocusEvent& aOther)
123 : : mType(aOther.mType),
124 : mPresShell(aOther.mPresShell),
125 : mDocument(aOther.mDocument),
126 0 : mTarget(aOther.mTarget) { }
127 :
128 : PRUint32 mType;
129 : nsCOMPtr<nsIPresShell> mPresShell;
130 : nsCOMPtr<nsIDocument> mDocument;
131 : nsCOMPtr<nsIDOMEventTarget> mTarget;
132 : };
133 :
134 51400 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFocusManager)
135 34533 : NS_INTERFACE_MAP_ENTRY(nsIFocusManager)
136 34529 : NS_INTERFACE_MAP_ENTRY(nsIObserver)
137 24986 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
138 11242 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIFocusManager)
139 12 : NS_INTERFACE_MAP_END
140 :
141 44055 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFocusManager)
142 45457 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFocusManager)
143 :
144 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsFocusManager)
145 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFocusManager)
146 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mActiveWindow)
147 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFocusedWindow)
148 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFocusedContent)
149 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFirstBlurEvent)
150 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFirstFocusEvent)
151 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mWindowBeingLowered)
152 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
153 339 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFocusManager)
154 339 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mActiveWindow)
155 339 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFocusedWindow)
156 339 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFocusedContent)
157 339 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFirstBlurEvent)
158 339 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFirstFocusEvent)
159 339 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mWindowBeingLowered)
160 339 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
161 :
162 : nsFocusManager* nsFocusManager::sInstance = nsnull;
163 : bool nsFocusManager::sMouseFocusesFormControl = false;
164 : bool nsFocusManager::sTestMode = false;
165 :
166 : static const char* kObservedPrefs[] = {
167 : "accessibility.browsewithcaret",
168 : "accessibility.tabfocus_applies_to_xul",
169 : "accessibility.mouse_focuses_formcontrol",
170 : "focusmanager.testmode",
171 : NULL
172 : };
173 :
174 1404 : nsFocusManager::nsFocusManager()
175 1404 : { }
176 :
177 2806 : nsFocusManager::~nsFocusManager()
178 : {
179 1403 : Preferences::RemoveObservers(this, kObservedPrefs);
180 :
181 2806 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
182 1403 : if (obs) {
183 1108 : obs->RemoveObserver(this, "xpcom-shutdown");
184 : }
185 1403 : }
186 :
187 : // static
188 : nsresult
189 1404 : nsFocusManager::Init()
190 : {
191 1404 : nsFocusManager* fm = new nsFocusManager();
192 1404 : NS_ENSURE_TRUE(fm, NS_ERROR_OUT_OF_MEMORY);
193 1404 : NS_ADDREF(fm);
194 1404 : sInstance = fm;
195 :
196 : nsIContent::sTabFocusModelAppliesToXUL =
197 : Preferences::GetBool("accessibility.tabfocus_applies_to_xul",
198 1404 : nsIContent::sTabFocusModelAppliesToXUL);
199 :
200 : sMouseFocusesFormControl =
201 1404 : Preferences::GetBool("accessibility.mouse_focuses_formcontrol", false);
202 :
203 1404 : sTestMode = Preferences::GetBool("focusmanager.testmode", false);
204 :
205 1404 : Preferences::AddWeakObservers(fm, kObservedPrefs);
206 :
207 2808 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
208 1404 : if (obs) {
209 1404 : obs->AddObserver(fm, "xpcom-shutdown", true);
210 : }
211 :
212 1404 : return NS_OK;
213 : }
214 :
215 : // static
216 : void
217 1403 : nsFocusManager::Shutdown()
218 : {
219 1403 : NS_IF_RELEASE(sInstance);
220 1403 : }
221 :
222 : NS_IMETHODIMP
223 1409 : nsFocusManager::Observe(nsISupports *aSubject,
224 : const char *aTopic,
225 : const PRUnichar *aData)
226 : {
227 1409 : if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
228 10 : nsDependentString data(aData);
229 5 : if (data.EqualsLiteral("accessibility.browsewithcaret")) {
230 1 : UpdateCaret(false, true, mFocusedContent);
231 : }
232 4 : else if (data.EqualsLiteral("accessibility.tabfocus_applies_to_xul")) {
233 : nsIContent::sTabFocusModelAppliesToXUL =
234 : Preferences::GetBool("accessibility.tabfocus_applies_to_xul",
235 1 : nsIContent::sTabFocusModelAppliesToXUL);
236 : }
237 3 : else if (data.EqualsLiteral("accessibility.mouse_focuses_formcontrol")) {
238 : sMouseFocusesFormControl =
239 : Preferences::GetBool("accessibility.mouse_focuses_formcontrol",
240 1 : false);
241 : }
242 2 : else if (data.EqualsLiteral("focusmanager.testmode")) {
243 1 : sTestMode = Preferences::GetBool("focusmanager.testmode", false);
244 : }
245 1404 : } else if (!nsCRT::strcmp(aTopic, "xpcom-shutdown")) {
246 1404 : mActiveWindow = nsnull;
247 1404 : mFocusedWindow = nsnull;
248 1404 : mFocusedContent = nsnull;
249 1404 : mFirstBlurEvent = nsnull;
250 1404 : mFirstFocusEvent = nsnull;
251 1404 : mWindowBeingLowered = nsnull;
252 1404 : mDelayedBlurFocusEvents.Clear();
253 1404 : mMouseDownEventHandlingDocument = nsnull;
254 : }
255 :
256 1409 : return NS_OK;
257 : }
258 :
259 : // given a frame content node, retrieve the nsIDOMWindow displayed in it
260 : static nsPIDOMWindow*
261 0 : GetContentWindow(nsIContent* aContent)
262 : {
263 0 : nsIDocument* doc = aContent->GetCurrentDoc();
264 0 : if (doc) {
265 0 : nsIDocument* subdoc = doc->GetSubDocumentFor(aContent);
266 0 : if (subdoc)
267 0 : return subdoc->GetWindow();
268 : }
269 :
270 0 : return nsnull;
271 : }
272 :
273 : // get the current window for the given content node
274 : static nsPIDOMWindow*
275 0 : GetCurrentWindow(nsIContent* aContent)
276 : {
277 0 : nsIDocument *doc = aContent->GetCurrentDoc();
278 0 : return doc ? doc->GetWindow() : nsnull;
279 : }
280 :
281 : // static
282 : nsIContent*
283 0 : nsFocusManager::GetFocusedDescendant(nsPIDOMWindow* aWindow, bool aDeep,
284 : nsPIDOMWindow** aFocusedWindow)
285 : {
286 0 : NS_ENSURE_TRUE(aWindow, nsnull);
287 :
288 0 : *aFocusedWindow = nsnull;
289 :
290 0 : nsIContent* currentContent = nsnull;
291 0 : nsPIDOMWindow* window = aWindow->GetOuterWindow();
292 0 : while (window) {
293 0 : *aFocusedWindow = window;
294 0 : currentContent = window->GetFocusedNode();
295 0 : if (!currentContent || !aDeep)
296 0 : break;
297 :
298 0 : window = GetContentWindow(currentContent);
299 : }
300 :
301 0 : NS_IF_ADDREF(*aFocusedWindow);
302 :
303 0 : return currentContent;
304 : }
305 :
306 : // static
307 : nsIContent*
308 0 : nsFocusManager::GetRedirectedFocus(nsIContent* aContent)
309 : {
310 : #ifdef MOZ_XUL
311 0 : if (aContent->IsXUL()) {
312 0 : nsCOMPtr<nsIDOMNode> inputField;
313 :
314 0 : nsCOMPtr<nsIDOMXULTextBoxElement> textbox = do_QueryInterface(aContent);
315 0 : if (textbox) {
316 0 : textbox->GetInputField(getter_AddRefs(inputField));
317 : }
318 : else {
319 0 : nsCOMPtr<nsIDOMXULMenuListElement> menulist = do_QueryInterface(aContent);
320 0 : if (menulist) {
321 0 : menulist->GetInputField(getter_AddRefs(inputField));
322 : }
323 0 : else if (aContent->Tag() == nsGkAtoms::scale) {
324 0 : nsCOMPtr<nsIDocument> doc = aContent->GetCurrentDoc();
325 0 : if (!doc)
326 0 : return nsnull;
327 :
328 0 : nsINodeList* children = doc->BindingManager()->GetXBLChildNodesFor(aContent);
329 0 : if (children) {
330 0 : nsIContent* child = children->GetNodeAt(0);
331 0 : if (child && child->Tag() == nsGkAtoms::slider)
332 0 : return child;
333 : }
334 : }
335 : }
336 :
337 0 : if (inputField) {
338 0 : nsCOMPtr<nsIContent> retval = do_QueryInterface(inputField);
339 0 : return retval;
340 : }
341 : }
342 : #endif
343 :
344 0 : return nsnull;
345 : }
346 :
347 : // static
348 : InputContextAction::Cause
349 0 : nsFocusManager::GetFocusMoveActionCause(PRUint32 aFlags)
350 : {
351 0 : if (aFlags & nsIFocusManager::FLAG_BYMOUSE) {
352 0 : return InputContextAction::CAUSE_MOUSE;
353 0 : } else if (aFlags & nsIFocusManager::FLAG_BYKEY) {
354 0 : return InputContextAction::CAUSE_KEY;
355 : }
356 0 : return InputContextAction::CAUSE_UNKNOWN;
357 : }
358 :
359 : NS_IMETHODIMP
360 28 : nsFocusManager::GetActiveWindow(nsIDOMWindow** aWindow)
361 : {
362 28 : NS_IF_ADDREF(*aWindow = mActiveWindow);
363 28 : return NS_OK;
364 : }
365 :
366 : NS_IMETHODIMP
367 0 : nsFocusManager::SetActiveWindow(nsIDOMWindow* aWindow)
368 : {
369 : // only top-level windows can be made active
370 0 : nsCOMPtr<nsPIDOMWindow> piWindow = do_QueryInterface(aWindow);
371 0 : if (piWindow)
372 0 : piWindow = piWindow->GetOuterWindow();
373 :
374 0 : NS_ENSURE_TRUE(piWindow && (piWindow == piWindow->GetPrivateRoot()),
375 : NS_ERROR_INVALID_ARG);
376 :
377 0 : RaiseWindow(piWindow);
378 0 : return NS_OK;
379 : }
380 :
381 : NS_IMETHODIMP
382 0 : nsFocusManager::GetFocusedWindow(nsIDOMWindow** aFocusedWindow)
383 : {
384 0 : NS_IF_ADDREF(*aFocusedWindow = mFocusedWindow);
385 0 : return NS_OK;
386 : }
387 :
388 0 : NS_IMETHODIMP nsFocusManager::SetFocusedWindow(nsIDOMWindow* aWindowToFocus)
389 : {
390 : #ifdef DEBUG_FOCUS
391 : printf("<<SetFocusedWindow begin>>\n");
392 : #endif
393 :
394 0 : nsCOMPtr<nsPIDOMWindow> windowToFocus(do_QueryInterface(aWindowToFocus));
395 0 : NS_ENSURE_TRUE(windowToFocus, NS_ERROR_FAILURE);
396 :
397 0 : windowToFocus = windowToFocus->GetOuterWindow();
398 :
399 : nsCOMPtr<nsIContent> frameContent =
400 0 : do_QueryInterface(windowToFocus->GetFrameElementInternal());
401 0 : if (frameContent) {
402 : // pass false for aFocusChanged so that the caret does not get updated
403 : // and scrolling does not occur.
404 0 : SetFocusInner(frameContent, 0, false, true);
405 : }
406 : else {
407 : // this is a top-level window. If the window has a child frame focused,
408 : // clear the focus. Otherwise, focus should already be in this frame, or
409 : // already cleared. This ensures that focus will be in this frame and not
410 : // in a child.
411 0 : nsIContent* content = windowToFocus->GetFocusedNode();
412 0 : if (content) {
413 0 : nsCOMPtr<nsIDOMWindow> childWindow = GetContentWindow(content);
414 0 : if (childWindow)
415 0 : ClearFocus(windowToFocus);
416 : }
417 : }
418 :
419 0 : nsCOMPtr<nsPIDOMWindow> rootWindow = windowToFocus->GetPrivateRoot();
420 0 : if (rootWindow)
421 0 : RaiseWindow(rootWindow);
422 :
423 : #ifdef DEBUG_FOCUS
424 : printf("<<SetFocusedWindow end>>\n");
425 : #endif
426 :
427 0 : return NS_OK;
428 : }
429 :
430 : NS_IMETHODIMP
431 0 : nsFocusManager::GetFocusedElement(nsIDOMElement** aFocusedElement)
432 : {
433 0 : if (mFocusedContent)
434 0 : CallQueryInterface(mFocusedContent, aFocusedElement);
435 : else
436 0 : *aFocusedElement = nsnull;
437 0 : return NS_OK;
438 : }
439 :
440 : NS_IMETHODIMP
441 0 : nsFocusManager::GetLastFocusMethod(nsIDOMWindow* aWindow, PRUint32* aLastFocusMethod)
442 : {
443 : // the focus method is stored on the inner window
444 0 : nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow));
445 0 : if (window)
446 0 : window = window->GetCurrentInnerWindow();
447 0 : if (!window)
448 0 : window = mFocusedWindow;
449 :
450 0 : *aLastFocusMethod = window ? window->GetFocusMethod() : 0;
451 :
452 0 : NS_ASSERTION((*aLastFocusMethod & FOCUSMETHOD_MASK) == *aLastFocusMethod,
453 : "invalid focus method");
454 0 : return NS_OK;
455 : }
456 :
457 : NS_IMETHODIMP
458 0 : nsFocusManager::SetFocus(nsIDOMElement* aElement, PRUint32 aFlags)
459 : {
460 : #ifdef DEBUG_FOCUS
461 : printf("<<SetFocus>>\n");
462 : #endif
463 :
464 0 : nsCOMPtr<nsIContent> newFocus = do_QueryInterface(aElement);
465 0 : NS_ENSURE_ARG(newFocus);
466 :
467 0 : SetFocusInner(newFocus, aFlags, true, true);
468 :
469 0 : return NS_OK;
470 : }
471 :
472 : NS_IMETHODIMP
473 0 : nsFocusManager::ElementIsFocusable(nsIDOMElement* aElement, PRUint32 aFlags,
474 : bool* aIsFocusable)
475 : {
476 0 : NS_ENSURE_TRUE(aElement, NS_ERROR_INVALID_ARG);
477 :
478 0 : nsCOMPtr<nsIContent> aContent = do_QueryInterface(aElement);
479 :
480 0 : *aIsFocusable = CheckIfFocusable(aContent, aFlags) != nsnull;
481 :
482 0 : return NS_OK;
483 : }
484 :
485 : NS_IMETHODIMP
486 0 : nsFocusManager::MoveFocus(nsIDOMWindow* aWindow, nsIDOMElement* aStartElement,
487 : PRUint32 aType, PRUint32 aFlags, nsIDOMElement** aElement)
488 : {
489 0 : *aElement = nsnull;
490 :
491 : #ifdef DEBUG_FOCUS
492 : printf("<<MoveFocus Type: %d Flags: %x>>\n<<", aType, aFlags);
493 :
494 : nsCOMPtr<nsPIDOMWindow> focusedWindow = mFocusedWindow;
495 : if (focusedWindow) {
496 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(focusedWindow->GetExtantDocument());
497 : if (doc) {
498 : nsCAutoString spec;
499 : doc->GetDocumentURI()->GetSpec(spec);
500 : printf(" [%p] Focused Window: %s", mFocusedWindow.get(), spec.get());
501 : }
502 : }
503 : PRINTTAGF(">> $[[%s]]\n", mFocusedContent);
504 : #endif
505 :
506 : // use FLAG_BYMOVEFOCUS when switching focus with MoveFocus unless one of
507 : // the other focus methods is already set, or we're just moving to the root
508 : // or caret position.
509 0 : if (aType != MOVEFOCUS_ROOT && aType != MOVEFOCUS_CARET &&
510 : (aFlags & FOCUSMETHOD_MASK) == 0) {
511 0 : aFlags |= FLAG_BYMOVEFOCUS;
512 : }
513 :
514 0 : nsCOMPtr<nsPIDOMWindow> window;
515 0 : nsCOMPtr<nsIContent> startContent;
516 0 : if (aStartElement) {
517 0 : startContent = do_QueryInterface(aStartElement);
518 0 : NS_ENSURE_TRUE(startContent, NS_ERROR_INVALID_ARG);
519 :
520 0 : window = GetCurrentWindow(startContent);
521 : }
522 : else {
523 0 : window = aWindow ? do_QueryInterface(aWindow) : mFocusedWindow;
524 0 : NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
525 0 : window = window->GetOuterWindow();
526 : }
527 :
528 0 : NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
529 :
530 0 : bool noParentTraversal = aFlags & FLAG_NOPARENTFRAME;
531 0 : nsCOMPtr<nsIContent> newFocus;
532 : nsresult rv = DetermineElementToMoveFocus(window, startContent, aType, noParentTraversal,
533 0 : getter_AddRefs(newFocus));
534 0 : NS_ENSURE_SUCCESS(rv, rv);
535 :
536 : #ifdef DEBUG_FOCUS_NAVIGATION
537 : PRINTTAGF("-> Element to be focused: %s\n", newFocus);
538 : #endif
539 :
540 0 : if (newFocus) {
541 : // for caret movement, pass false for the aFocusChanged argument,
542 : // otherwise the caret will end up moving to the focus position. This
543 : // would be a problem because the caret would move to the beginning of the
544 : // focused link making it impossible to navigate the caret over a link.
545 0 : SetFocusInner(newFocus, aFlags, aType != MOVEFOCUS_CARET, true);
546 0 : CallQueryInterface(newFocus, aElement);
547 : }
548 0 : else if (aType == MOVEFOCUS_ROOT || aType == MOVEFOCUS_CARET) {
549 : // no content was found, so clear the focus for these two types.
550 0 : ClearFocus(window);
551 : }
552 :
553 : #ifdef DEBUG_FOCUS
554 : printf("<<MoveFocus end>>\n");
555 : #endif
556 :
557 0 : return NS_OK;
558 : }
559 :
560 : NS_IMETHODIMP
561 0 : nsFocusManager::ClearFocus(nsIDOMWindow* aWindow)
562 : {
563 : #ifdef DEBUG_FOCUS
564 : printf("<<ClearFocus begin>>\n");
565 : #endif
566 :
567 : // if the window to clear is the focused window or an ancestor of the
568 : // focused window, then blur the existing focused content. Otherwise, the
569 : // focus is somewhere else so just update the current node.
570 0 : nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow));
571 0 : NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
572 :
573 0 : window = window->GetOuterWindow();
574 0 : NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
575 :
576 0 : if (IsSameOrAncestor(window, mFocusedWindow)) {
577 0 : bool isAncestor = (window != mFocusedWindow);
578 0 : if (Blur(window, nsnull, isAncestor, true)) {
579 : // if we are clearing the focus on an ancestor of the focused window,
580 : // the ancestor will become the new focused window, so focus it
581 0 : if (isAncestor)
582 0 : Focus(window, nsnull, 0, true, false, false, true);
583 : }
584 : }
585 : else {
586 0 : window->SetFocusedNode(nsnull);
587 : }
588 :
589 : #ifdef DEBUG_FOCUS
590 : printf("<<ClearFocus end>>\n");
591 : #endif
592 :
593 0 : return NS_OK;
594 : }
595 :
596 : NS_IMETHODIMP
597 0 : nsFocusManager::GetFocusedElementForWindow(nsIDOMWindow* aWindow,
598 : bool aDeep,
599 : nsIDOMWindow** aFocusedWindow,
600 : nsIDOMElement** aElement)
601 : {
602 0 : *aElement = nsnull;
603 0 : if (aFocusedWindow)
604 0 : *aFocusedWindow = nsnull;
605 :
606 0 : nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow));
607 0 : NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
608 :
609 0 : window = window->GetOuterWindow();
610 0 : NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
611 :
612 0 : nsCOMPtr<nsPIDOMWindow> focusedWindow;
613 : nsCOMPtr<nsIContent> focusedContent =
614 0 : GetFocusedDescendant(window, aDeep, getter_AddRefs(focusedWindow));
615 0 : if (focusedContent)
616 0 : CallQueryInterface(focusedContent, aElement);
617 :
618 0 : if (aFocusedWindow)
619 0 : NS_IF_ADDREF(*aFocusedWindow = focusedWindow);
620 :
621 0 : return NS_OK;
622 : }
623 :
624 : NS_IMETHODIMP
625 0 : nsFocusManager::MoveCaretToFocus(nsIDOMWindow* aWindow)
626 : {
627 0 : PRInt32 itemType = nsIDocShellTreeItem::typeChrome;
628 :
629 0 : nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(aWindow);
630 0 : nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(webnav);
631 0 : if (dsti) {
632 0 : dsti->GetItemType(&itemType);
633 0 : if (itemType != nsIDocShellTreeItem::typeChrome) {
634 : // don't move the caret for editable documents
635 0 : nsCOMPtr<nsIEditorDocShell> editorDocShell(do_QueryInterface(dsti));
636 0 : if (editorDocShell) {
637 : bool isEditable;
638 0 : editorDocShell->GetEditable(&isEditable);
639 0 : if (isEditable)
640 0 : return NS_OK;
641 : }
642 :
643 0 : nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(dsti);
644 0 : NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
645 :
646 0 : nsCOMPtr<nsIPresShell> presShell;
647 0 : docShell->GetPresShell(getter_AddRefs(presShell));
648 0 : NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
649 :
650 0 : nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow));
651 0 : nsCOMPtr<nsIContent> content = window->GetFocusedNode();
652 0 : if (content)
653 0 : MoveCaretToFocus(presShell, content);
654 : }
655 : }
656 :
657 0 : return NS_OK;
658 : }
659 :
660 : NS_IMETHODIMP
661 0 : nsFocusManager::WindowRaised(nsIDOMWindow* aWindow)
662 : {
663 0 : nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
664 0 : NS_ENSURE_TRUE(window && window->IsOuterWindow(), NS_ERROR_INVALID_ARG);
665 :
666 : #ifdef DEBUG_FOCUS
667 : printf("Window %p Raised [Currently: %p %p] <<", aWindow, mActiveWindow.get(), mFocusedWindow.get());
668 : nsCAutoString spec;
669 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(window->GetExtantDocument());
670 : if (doc) {
671 : doc->GetDocumentURI()->GetSpec(spec);
672 : printf("[%p] Raised Window: %s", aWindow, spec.get());
673 : }
674 : if (mActiveWindow) {
675 : doc = do_QueryInterface(mActiveWindow->GetExtantDocument());
676 : if (doc) {
677 : doc->GetDocumentURI()->GetSpec(spec);
678 : printf(" [%p] Active Window: %s", mActiveWindow.get(), spec.get());
679 : }
680 : }
681 : printf(">>\n");
682 : #endif
683 :
684 0 : if (mActiveWindow == window) {
685 : // The window is already active, so there is no need to focus anything,
686 : // but make sure that the right widget is focused. This is a special case
687 : // for Windows because when restoring a minimized window, a second
688 : // activation will occur and the top-level widget could be focused instead
689 : // of the child we want. We solve this by calling SetFocus to ensure that
690 : // what the focus manager thinks should be the current widget is actually
691 : // focused.
692 0 : EnsureCurrentWidgetFocused();
693 0 : return NS_OK;
694 : }
695 :
696 : // lower the existing window, if any. This shouldn't happen usually.
697 0 : if (mActiveWindow)
698 0 : WindowLowered(mActiveWindow);
699 :
700 0 : nsCOMPtr<nsIWebNavigation> webnav(do_GetInterface(aWindow));
701 0 : nsCOMPtr<nsIDocShellTreeItem> docShellAsItem(do_QueryInterface(webnav));
702 : // If there's no docShellAsItem, this window must have been closed,
703 : // in that case there is no tree owner.
704 0 : NS_ENSURE_TRUE(docShellAsItem, NS_OK);
705 :
706 : // set this as the active window
707 0 : mActiveWindow = window;
708 :
709 : // ensure that the window is enabled and visible
710 0 : nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
711 0 : docShellAsItem->GetTreeOwner(getter_AddRefs(treeOwner));
712 0 : nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(treeOwner);
713 0 : if (baseWindow) {
714 0 : bool isEnabled = true;
715 0 : if (NS_SUCCEEDED(baseWindow->GetEnabled(&isEnabled)) && !isEnabled) {
716 0 : return NS_ERROR_FAILURE;
717 : }
718 :
719 0 : baseWindow->SetVisibility(true);
720 : }
721 :
722 : // inform the DOM window that it has activated, so that the active attribute
723 : // is updated on the window
724 0 : window->ActivateOrDeactivate(true);
725 :
726 : // send activate event
727 0 : nsCOMPtr<nsIDocument> document = do_QueryInterface(window->GetExtantDocument());
728 : nsContentUtils::DispatchTrustedEvent(document,
729 : window,
730 0 : NS_LITERAL_STRING("activate"),
731 0 : true, true, nsnull);
732 :
733 : // retrieve the last focused element within the window that was raised
734 0 : nsCOMPtr<nsPIDOMWindow> currentWindow;
735 : nsCOMPtr<nsIContent> currentFocus =
736 0 : GetFocusedDescendant(window, true, getter_AddRefs(currentWindow));
737 :
738 0 : NS_ASSERTION(currentWindow, "window raised with no window current");
739 0 : if (!currentWindow)
740 0 : return NS_OK;
741 :
742 0 : nsCOMPtr<nsIDocShell> currentDocShell = currentWindow->GetDocShell();
743 :
744 0 : nsCOMPtr<nsIPresShell> presShell;
745 0 : currentDocShell->GetPresShell(getter_AddRefs(presShell));
746 0 : if (presShell) {
747 : // disable selection mousedown state on activation
748 : // XXXndeakin P3 not sure if this is necessary, but it doesn't hurt
749 0 : nsRefPtr<nsFrameSelection> frameSelection = presShell->FrameSelection();
750 0 : frameSelection->SetMouseDownState(false);
751 : }
752 :
753 0 : Focus(currentWindow, currentFocus, 0, true, false, true, true);
754 :
755 0 : return NS_OK;
756 : }
757 :
758 : NS_IMETHODIMP
759 0 : nsFocusManager::WindowLowered(nsIDOMWindow* aWindow)
760 : {
761 0 : nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
762 0 : NS_ENSURE_TRUE(window && window->IsOuterWindow(), NS_ERROR_INVALID_ARG);
763 :
764 : #ifdef DEBUG_FOCUS
765 : printf("Window %p Lowered [Currently: %p %p] <<", aWindow, mActiveWindow.get(), mFocusedWindow.get());
766 : nsCAutoString spec;
767 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(window->GetExtantDocument());
768 : if (doc) {
769 : doc->GetDocumentURI()->GetSpec(spec);
770 : printf("[%p] Lowered Window: %s", aWindow, spec.get());
771 : }
772 : if (mActiveWindow) {
773 : doc = do_QueryInterface(mActiveWindow->GetExtantDocument());
774 : if (doc) {
775 : doc->GetDocumentURI()->GetSpec(spec);
776 : printf(" [%p] Active Window: %s", mActiveWindow.get(), spec.get());
777 : }
778 : }
779 : printf(">>\n");
780 : #endif
781 :
782 0 : if (mActiveWindow != window)
783 0 : return NS_OK;
784 :
785 : // clear the mouse capture as the active window has changed
786 0 : nsIPresShell::SetCapturingContent(nsnull, 0);
787 :
788 : // inform the DOM window that it has deactivated, so that the active
789 : // attribute is updated on the window
790 0 : window->ActivateOrDeactivate(false);
791 :
792 : // send deactivate event
793 0 : nsCOMPtr<nsIDocument> document = do_QueryInterface(window->GetExtantDocument());
794 : nsContentUtils::DispatchTrustedEvent(document,
795 : window,
796 0 : NS_LITERAL_STRING("deactivate"),
797 0 : true, true, nsnull);
798 :
799 : // keep track of the window being lowered, so that attempts to raise the
800 : // window can be prevented until we return. Otherwise, focus can get into
801 : // an unusual state.
802 0 : mWindowBeingLowered = mActiveWindow;
803 0 : mActiveWindow = nsnull;
804 :
805 0 : if (mFocusedWindow)
806 0 : Blur(nsnull, nsnull, true, true);
807 :
808 0 : mWindowBeingLowered = nsnull;
809 :
810 0 : return NS_OK;
811 : }
812 :
813 : nsresult
814 0 : nsFocusManager::ContentRemoved(nsIDocument* aDocument, nsIContent* aContent)
815 : {
816 0 : NS_ENSURE_ARG(aDocument);
817 0 : NS_ENSURE_ARG(aContent);
818 :
819 0 : nsPIDOMWindow *window = aDocument->GetWindow();
820 0 : if (!window)
821 0 : return NS_OK;
822 :
823 : // if the content is currently focused in the window, or is an ancestor
824 : // of the currently focused element, reset the focus within that window.
825 0 : nsIContent* content = window->GetFocusedNode();
826 0 : if (content && nsContentUtils::ContentIsDescendantOf(content, aContent)) {
827 0 : bool shouldShowFocusRing = window->ShouldShowFocusRing();
828 0 : window->SetFocusedNode(nsnull);
829 :
830 0 : nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
831 0 : if (docShell) {
832 0 : nsCOMPtr<nsIPresShell> presShell;
833 0 : docShell->GetPresShell(getter_AddRefs(presShell));
834 0 : nsIMEStateManager::OnRemoveContent(presShell->GetPresContext(), content);
835 : }
836 :
837 : // if this window is currently focused, clear the global focused
838 : // element as well, but don't fire any events.
839 0 : if (window == mFocusedWindow) {
840 0 : mFocusedContent = nsnull;
841 : }
842 : else {
843 : // Check if the node that was focused is an iframe or similar by looking
844 : // if it has a subdocument. This would indicate that this focused iframe
845 : // and its descendants will be going away. We will need to move the
846 : // focus somewhere else, so just clear the focus in the toplevel window
847 : // so that no element is focused.
848 0 : nsIDocument* subdoc = aDocument->GetSubDocumentFor(content);
849 0 : if (subdoc) {
850 0 : nsCOMPtr<nsISupports> container = subdoc->GetContainer();
851 0 : nsCOMPtr<nsPIDOMWindow> childWindow = do_GetInterface(container);
852 0 : if (childWindow && IsSameOrAncestor(childWindow, mFocusedWindow)) {
853 0 : ClearFocus(mActiveWindow);
854 : }
855 : }
856 : }
857 :
858 0 : NotifyFocusStateChange(content, shouldShowFocusRing, false);
859 : }
860 :
861 0 : return NS_OK;
862 : }
863 :
864 : NS_IMETHODIMP
865 0 : nsFocusManager::WindowShown(nsIDOMWindow* aWindow, bool aNeedsFocus)
866 : {
867 0 : nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
868 0 : NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
869 :
870 0 : window = window->GetOuterWindow();
871 :
872 : #ifdef DEBUG_FOCUS
873 : printf("Window %p Shown [Currently: %p %p] <<", window.get(), mActiveWindow.get(), mFocusedWindow.get());
874 : nsCAutoString spec;
875 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(window->GetExtantDocument());
876 : if (doc) {
877 : doc->GetDocumentURI()->GetSpec(spec);
878 : printf("Shown Window: %s", spec.get());
879 : }
880 :
881 : if (mFocusedWindow) {
882 : doc = do_QueryInterface(mFocusedWindow->GetExtantDocument());
883 : if (doc) {
884 : doc->GetDocumentURI()->GetSpec(spec);
885 : printf(" Focused Window: %s", spec.get());
886 : }
887 : }
888 : printf(">>\n");
889 : #endif
890 :
891 0 : if (mFocusedWindow != window)
892 0 : return NS_OK;
893 :
894 0 : if (aNeedsFocus) {
895 0 : nsCOMPtr<nsPIDOMWindow> currentWindow;
896 : nsCOMPtr<nsIContent> currentFocus =
897 0 : GetFocusedDescendant(window, true, getter_AddRefs(currentWindow));
898 0 : if (currentWindow)
899 0 : Focus(currentWindow, currentFocus, 0, true, false, false, true);
900 : }
901 : else {
902 : // Sometimes, an element in a window can be focused before the window is
903 : // visible, which would mean that the widget may not be properly focused.
904 : // When the window becomes visible, make sure the right widget is focused.
905 0 : EnsureCurrentWidgetFocused();
906 : }
907 :
908 0 : return NS_OK;
909 : }
910 :
911 : NS_IMETHODIMP
912 0 : nsFocusManager::WindowHidden(nsIDOMWindow* aWindow)
913 : {
914 : // if there is no window or it is not the same or an ancestor of the
915 : // currently focused window, just return, as the current focus will not
916 : // be affected.
917 :
918 0 : nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
919 0 : NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
920 :
921 0 : window = window->GetOuterWindow();
922 :
923 : #ifdef DEBUG_FOCUS
924 : printf("Window %p Hidden [Currently: %p %p] <<", window.get(), mActiveWindow.get(), mFocusedWindow.get());
925 : nsCAutoString spec;
926 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(window->GetExtantDocument());
927 : if (doc) {
928 : doc->GetDocumentURI()->GetSpec(spec);
929 : printf("Hide Window: %s", spec.get());
930 : }
931 :
932 : if (mFocusedWindow) {
933 : doc = do_QueryInterface(mFocusedWindow->GetExtantDocument());
934 : if (doc) {
935 : doc->GetDocumentURI()->GetSpec(spec);
936 : printf(" Focused Window: %s", spec.get());
937 : }
938 : }
939 :
940 : if (mActiveWindow) {
941 : doc = do_QueryInterface(mActiveWindow->GetExtantDocument());
942 : if (doc) {
943 : doc->GetDocumentURI()->GetSpec(spec);
944 : printf(" Active Window: %s", spec.get());
945 : }
946 : }
947 : printf(">>\n");
948 : #endif
949 :
950 0 : if (!IsSameOrAncestor(window, mFocusedWindow))
951 0 : return NS_OK;
952 :
953 : // at this point, we know that the window being hidden is either the focused
954 : // window, or an ancestor of the focused window. Either way, the focus is no
955 : // longer valid, so it needs to be updated.
956 :
957 0 : nsIContent* oldFocusedContent = mFocusedContent;
958 0 : mFocusedContent = nsnull;
959 :
960 0 : if (oldFocusedContent && oldFocusedContent->IsInDoc()) {
961 : NotifyFocusStateChange(oldFocusedContent,
962 0 : mFocusedWindow->ShouldShowFocusRing(),
963 0 : false);
964 : }
965 :
966 0 : nsCOMPtr<nsIDocShell> focusedDocShell = mFocusedWindow->GetDocShell();
967 0 : nsCOMPtr<nsIPresShell> presShell;
968 0 : focusedDocShell->GetPresShell(getter_AddRefs(presShell));
969 :
970 0 : nsIMEStateManager::OnTextStateBlur(nsnull, nsnull);
971 0 : if (presShell) {
972 : nsIMEStateManager::OnChangeFocus(presShell->GetPresContext(), nsnull,
973 0 : GetFocusMoveActionCause(0));
974 0 : SetCaretVisible(presShell, false, nsnull);
975 : }
976 :
977 : // if the docshell being hidden is being destroyed, then we want to move
978 : // focus somewhere else. Call ClearFocus on the toplevel window, which
979 : // will have the effect of clearing the focus and moving the focused window
980 : // to the toplevel window. But if the window isn't being destroyed, we are
981 : // likely just loading a new document in it, so we want to maintain the
982 : // focused window so that the new document gets properly focused.
983 : bool beingDestroyed;
984 0 : nsCOMPtr<nsIDocShell> docShellBeingHidden = window->GetDocShell();
985 0 : docShellBeingHidden->IsBeingDestroyed(&beingDestroyed);
986 0 : if (beingDestroyed) {
987 : // There is usually no need to do anything if a toplevel window is going
988 : // away, as we assume that WindowLowered will be called. However, this may
989 : // not happen if nsIAppStartup::eForceQuit is used to quit, and can cause
990 : // a leak. So if the active window is being destroyed, call WindowLowered
991 : // directly.
992 0 : NS_ASSERTION(mFocusedWindow->IsOuterWindow(), "outer window expected");
993 0 : if (mActiveWindow == mFocusedWindow || mActiveWindow == window)
994 0 : WindowLowered(mActiveWindow);
995 : else
996 0 : ClearFocus(mActiveWindow);
997 0 : return NS_OK;
998 : }
999 :
1000 : // if the window being hidden is an ancestor of the focused window, adjust
1001 : // the focused window so that it points to the one being hidden. This
1002 : // ensures that the focused window isn't in a chain of frames that doesn't
1003 : // exist any more.
1004 0 : if (window != mFocusedWindow) {
1005 0 : nsCOMPtr<nsIWebNavigation> webnav(do_GetInterface(mFocusedWindow));
1006 0 : nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(webnav);
1007 0 : if (dsti) {
1008 0 : nsCOMPtr<nsIDocShellTreeItem> parentDsti;
1009 0 : dsti->GetParent(getter_AddRefs(parentDsti));
1010 0 : nsCOMPtr<nsPIDOMWindow> parentWindow = do_GetInterface(parentDsti);
1011 0 : if (parentWindow)
1012 0 : parentWindow->SetFocusedNode(nsnull);
1013 : }
1014 :
1015 0 : mFocusedWindow = window;
1016 : }
1017 :
1018 0 : return NS_OK;
1019 : }
1020 :
1021 : NS_IMETHODIMP
1022 0 : nsFocusManager::FireDelayedEvents(nsIDocument* aDocument)
1023 : {
1024 0 : NS_ENSURE_ARG(aDocument);
1025 :
1026 : // fire any delayed focus and blur events in the same order that they were added
1027 0 : for (PRUint32 i = 0; i < mDelayedBlurFocusEvents.Length(); i++)
1028 : {
1029 0 : if (mDelayedBlurFocusEvents[i].mDocument == aDocument &&
1030 0 : !aDocument->EventHandlingSuppressed()) {
1031 0 : PRUint32 type = mDelayedBlurFocusEvents[i].mType;
1032 0 : nsCOMPtr<nsIDOMEventTarget> target = mDelayedBlurFocusEvents[i].mTarget;
1033 0 : nsCOMPtr<nsIPresShell> presShell = mDelayedBlurFocusEvents[i].mPresShell;
1034 0 : mDelayedBlurFocusEvents.RemoveElementAt(i);
1035 0 : SendFocusOrBlurEvent(type, presShell, aDocument, target, 0, false);
1036 0 : --i;
1037 : }
1038 : }
1039 :
1040 0 : return NS_OK;
1041 : }
1042 :
1043 : NS_IMETHODIMP
1044 0 : nsFocusManager::FocusPlugin(nsIContent* aContent)
1045 : {
1046 0 : NS_ENSURE_ARG(aContent);
1047 0 : SetFocusInner(aContent, 0, true, false);
1048 0 : return NS_OK;
1049 : }
1050 :
1051 : /* static */
1052 : void
1053 0 : nsFocusManager::NotifyFocusStateChange(nsIContent* aContent,
1054 : bool aWindowShouldShowFocusRing,
1055 : bool aGettingFocus)
1056 : {
1057 0 : if (!aContent->IsElement()) {
1058 0 : return;
1059 : }
1060 0 : nsEventStates eventState = NS_EVENT_STATE_FOCUS;
1061 0 : if (aWindowShouldShowFocusRing) {
1062 0 : eventState |= NS_EVENT_STATE_FOCUSRING;
1063 : }
1064 0 : if (aGettingFocus) {
1065 0 : aContent->AsElement()->AddStates(eventState);
1066 : } else {
1067 0 : aContent->AsElement()->RemoveStates(eventState);
1068 : }
1069 : }
1070 :
1071 : // static
1072 : void
1073 0 : nsFocusManager::EnsureCurrentWidgetFocused()
1074 : {
1075 0 : if (!mFocusedWindow || sTestMode)
1076 0 : return;
1077 :
1078 : // get the main child widget for the focused window and ensure that the
1079 : // platform knows that this widget is focused.
1080 0 : nsCOMPtr<nsIDocShell> docShell = mFocusedWindow->GetDocShell();
1081 0 : if (docShell) {
1082 0 : nsCOMPtr<nsIPresShell> presShell;
1083 0 : docShell->GetPresShell(getter_AddRefs(presShell));
1084 0 : if (presShell) {
1085 0 : nsIViewManager* vm = presShell->GetViewManager();
1086 0 : if (vm) {
1087 0 : nsCOMPtr<nsIWidget> widget;
1088 0 : vm->GetRootWidget(getter_AddRefs(widget));
1089 0 : if (widget)
1090 0 : widget->SetFocus(false);
1091 : }
1092 : }
1093 : }
1094 : }
1095 :
1096 : void
1097 0 : nsFocusManager::SetFocusInner(nsIContent* aNewContent, PRInt32 aFlags,
1098 : bool aFocusChanged, bool aAdjustWidget)
1099 : {
1100 : // if the element is not focusable, just return and leave the focus as is
1101 0 : nsCOMPtr<nsIContent> contentToFocus = CheckIfFocusable(aNewContent, aFlags);
1102 0 : if (!contentToFocus)
1103 : return;
1104 :
1105 : // check if the element to focus is a frame (iframe) containing a child
1106 : // document. Frames are never directly focused; instead focusing a frame
1107 : // means focus what is inside the frame. To do this, the descendant content
1108 : // within the frame is retrieved and that will be focused instead.
1109 0 : nsCOMPtr<nsPIDOMWindow> newWindow;
1110 0 : nsCOMPtr<nsPIDOMWindow> subWindow = GetContentWindow(contentToFocus);
1111 0 : if (subWindow) {
1112 0 : contentToFocus = GetFocusedDescendant(subWindow, true, getter_AddRefs(newWindow));
1113 : // since a window is being refocused, clear aFocusChanged so that the
1114 : // caret position isn't updated.
1115 0 : aFocusChanged = false;
1116 : }
1117 :
1118 : // unless it was set above, retrieve the window for the element to focus
1119 0 : if (!newWindow)
1120 0 : newWindow = GetCurrentWindow(contentToFocus);
1121 :
1122 : // if the element is already focused, just return. Note that this happens
1123 : // after the frame check above so that we compare the element that will be
1124 : // focused rather than the frame it is in.
1125 0 : if (!newWindow || (newWindow == mFocusedWindow && contentToFocus == mFocusedContent))
1126 : return;
1127 :
1128 : // don't allow focus to be placed in docshells or descendants of docshells
1129 : // that are being destroyed. Also, ensure that the page hasn't been
1130 : // unloaded. The prevents content from being refocused during an unload event.
1131 0 : nsCOMPtr<nsIDocShell> newDocShell = newWindow->GetDocShell();
1132 0 : nsCOMPtr<nsIDocShell> docShell = newDocShell;
1133 0 : while (docShell) {
1134 : bool inUnload;
1135 0 : docShell->GetIsInUnload(&inUnload);
1136 0 : if (inUnload)
1137 : return;
1138 :
1139 : bool beingDestroyed;
1140 0 : docShell->IsBeingDestroyed(&beingDestroyed);
1141 0 : if (beingDestroyed)
1142 : return;
1143 :
1144 0 : nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(docShell);
1145 0 : nsCOMPtr<nsIDocShellTreeItem> parentDsti;
1146 0 : dsti->GetParent(getter_AddRefs(parentDsti));
1147 0 : docShell = do_QueryInterface(parentDsti);
1148 : }
1149 :
1150 : // if the new element is in the same window as the currently focused element
1151 0 : bool isElementInFocusedWindow = (mFocusedWindow == newWindow);
1152 :
1153 0 : if (!isElementInFocusedWindow && mFocusedWindow && newWindow &&
1154 0 : nsContentUtils::IsHandlingKeyBoardEvent()) {
1155 : nsCOMPtr<nsIScriptObjectPrincipal> focused =
1156 0 : do_QueryInterface(mFocusedWindow);
1157 : nsCOMPtr<nsIScriptObjectPrincipal> newFocus =
1158 0 : do_QueryInterface(newWindow);
1159 0 : nsIPrincipal* focusedPrincipal = focused->GetPrincipal();
1160 0 : nsIPrincipal* newPrincipal = newFocus->GetPrincipal();
1161 0 : if (!focusedPrincipal || !newPrincipal) {
1162 : return;
1163 : }
1164 0 : bool subsumes = false;
1165 0 : focusedPrincipal->Subsumes(newPrincipal, &subsumes);
1166 0 : if (!subsumes && !nsContentUtils::IsCallerTrustedForWrite()) {
1167 0 : NS_WARNING("Not allowed to focus the new window!");
1168 : return;
1169 : }
1170 : }
1171 :
1172 : // to check if the new element is in the active window, compare the
1173 : // new root docshell for the new element with the active window's docshell.
1174 0 : bool isElementInActiveWindow = false;
1175 :
1176 0 : nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(newWindow);
1177 0 : nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(webnav);
1178 0 : nsCOMPtr<nsPIDOMWindow> newRootWindow;
1179 0 : if (dsti) {
1180 0 : nsCOMPtr<nsIDocShellTreeItem> root;
1181 0 : dsti->GetRootTreeItem(getter_AddRefs(root));
1182 0 : newRootWindow = do_GetInterface(root);
1183 :
1184 0 : isElementInActiveWindow = (mActiveWindow && newRootWindow == mActiveWindow);
1185 : }
1186 :
1187 : #ifdef DEBUG_FOCUS
1188 : PRINTTAGF("Shift Focus: %s", contentToFocus);
1189 : printf(" Flags: %x Current Window: %p New Window: %p Current Element: %p",
1190 : aFlags, mFocusedWindow.get(), newWindow.get(), mFocusedContent.get());
1191 : printf(" In Active Window: %d In Focused Window: %d\n",
1192 : isElementInActiveWindow, isElementInFocusedWindow);
1193 : #endif
1194 :
1195 : // Exit full-screen if we're focusing a windowed plugin on a non-MacOSX
1196 : // system. We don't control event dispatch to windowed plugins on non-MacOSX,
1197 : // so we can't display the "Press ESC to leave full-screen mode" warning on
1198 : // key input if a windowed plugin is focused, so just exit full-screen
1199 : // to guard against phishing.
1200 : #ifndef XP_MACOSX
1201 0 : if (contentToFocus &&
1202 0 : nsContentUtils::GetRootDocument(contentToFocus->OwnerDoc())->IsFullScreenDoc() &&
1203 0 : nsContentUtils::HasPluginWithUncontrolledEventDispatch(contentToFocus)) {
1204 : nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
1205 : "DOM",
1206 0 : contentToFocus->OwnerDoc(),
1207 : nsContentUtils::eDOM_PROPERTIES,
1208 0 : "FocusedWindowedPluginWhileFullScreen");
1209 0 : nsIDocument::ExitFullScreen(true);
1210 : }
1211 : #endif
1212 :
1213 : // if the FLAG_NOSWITCHFRAME flag is used, only allow the focus to be
1214 : // shifted away from the current element if the new shell to focus is
1215 : // the same or an ancestor shell of the currently focused shell.
1216 0 : bool allowFrameSwitch = !(aFlags & FLAG_NOSWITCHFRAME) ||
1217 0 : IsSameOrAncestor(newWindow, mFocusedWindow);
1218 :
1219 : // if the element is in the active window, frame switching is allowed and
1220 : // the content is in a visible window, fire blur and focus events.
1221 : bool sendFocusEvent =
1222 0 : isElementInActiveWindow && allowFrameSwitch && IsWindowVisible(newWindow);
1223 :
1224 : // When the following conditions are true:
1225 : // * an element has focus
1226 : // * isn't called by trusted event (i.e., called by untrusted event or by js)
1227 : // * the focus is moved to another document's element
1228 : // we need to check the permission.
1229 0 : if (sendFocusEvent && mFocusedContent &&
1230 0 : mFocusedContent->OwnerDoc() != aNewContent->OwnerDoc()) {
1231 : // If the caller cannot access the current focused node, the caller should
1232 : // not be able to steal focus from it. E.g., When the current focused node
1233 : // is in chrome, any web contents should not be able to steal the focus.
1234 0 : nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(mFocusedContent));
1235 0 : sendFocusEvent = nsContentUtils::CanCallerAccess(domNode);
1236 0 : if (!sendFocusEvent && mMouseDownEventHandlingDocument) {
1237 : // However, while mouse down event is handling, the handling document's
1238 : // script should be able to steal focus.
1239 0 : domNode = do_QueryInterface(mMouseDownEventHandlingDocument);
1240 0 : sendFocusEvent = nsContentUtils::CanCallerAccess(domNode);
1241 : }
1242 : }
1243 :
1244 0 : if (sendFocusEvent) {
1245 : // return if blurring fails or the focus changes during the blur
1246 0 : if (mFocusedWindow) {
1247 : // if the focus is being moved to another element in the same document,
1248 : // or to a descendant, pass the existing window to Blur so that the
1249 : // current node in the existing window is cleared. If moving to a
1250 : // window elsewhere, we want to maintain the current node in the
1251 : // window but still blur it.
1252 0 : bool currentIsSameOrAncestor = IsSameOrAncestor(mFocusedWindow, newWindow);
1253 : // find the common ancestor of the currently focused window and the new
1254 : // window. The ancestor will need to have its currently focused node
1255 : // cleared once the document has been blurred. Otherwise, we'll be in a
1256 : // state where a document is blurred yet the chain of windows above it
1257 : // still points to that document.
1258 : // For instance, in the following frame tree:
1259 : // A
1260 : // B C
1261 : // D
1262 : // D is focused and we want to focus C. Once D has been blurred, we need
1263 : // to clear out the focus in A, otherwise A would still maintain that B
1264 : // was focused, and B that D was focused.
1265 0 : nsCOMPtr<nsPIDOMWindow> commonAncestor;
1266 0 : if (!isElementInFocusedWindow)
1267 0 : commonAncestor = GetCommonAncestor(newWindow, mFocusedWindow);
1268 :
1269 0 : if (!Blur(currentIsSameOrAncestor ? mFocusedWindow.get() : nsnull,
1270 0 : commonAncestor, !isElementInFocusedWindow, aAdjustWidget))
1271 : return;
1272 : }
1273 :
1274 0 : Focus(newWindow, contentToFocus, aFlags, !isElementInFocusedWindow,
1275 0 : aFocusChanged, false, aAdjustWidget);
1276 : }
1277 : else {
1278 : // otherwise, for inactive windows and when the caller cannot steal the
1279 : // focus, update the node in the window, and raise the window if desired.
1280 0 : if (allowFrameSwitch)
1281 0 : AdjustWindowFocus(newWindow, true);
1282 :
1283 : // set the focus node and method as needed
1284 : PRUint32 focusMethod = aFocusChanged ? aFlags & FOCUSMETHODANDRING_MASK :
1285 0 : newWindow->GetFocusMethod() | (aFlags & FLAG_SHOWRING);
1286 0 : newWindow->SetFocusedNode(contentToFocus, focusMethod);
1287 0 : if (aFocusChanged) {
1288 0 : nsCOMPtr<nsIDocShell> docShell = newWindow->GetDocShell();
1289 :
1290 0 : nsCOMPtr<nsIPresShell> presShell;
1291 0 : docShell->GetPresShell(getter_AddRefs(presShell));
1292 0 : if (presShell)
1293 0 : ScrollIntoView(presShell, contentToFocus, aFlags);
1294 : }
1295 :
1296 : // update the commands even when inactive so that the attributes for that
1297 : // window are up to date.
1298 0 : if (allowFrameSwitch)
1299 0 : newWindow->UpdateCommands(NS_LITERAL_STRING("focus"));
1300 :
1301 0 : if (aFlags & FLAG_RAISE)
1302 0 : RaiseWindow(newRootWindow);
1303 : }
1304 : }
1305 :
1306 : bool
1307 0 : nsFocusManager::IsSameOrAncestor(nsPIDOMWindow* aPossibleAncestor,
1308 : nsPIDOMWindow* aWindow)
1309 : {
1310 0 : nsCOMPtr<nsIWebNavigation> awebnav(do_GetInterface(aPossibleAncestor));
1311 0 : nsCOMPtr<nsIDocShellTreeItem> ancestordsti = do_QueryInterface(awebnav);
1312 :
1313 0 : nsCOMPtr<nsIWebNavigation> fwebnav(do_GetInterface(aWindow));
1314 0 : nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(fwebnav);
1315 0 : while (dsti) {
1316 0 : if (dsti == ancestordsti)
1317 0 : return true;
1318 0 : nsCOMPtr<nsIDocShellTreeItem> parentDsti;
1319 0 : dsti->GetParent(getter_AddRefs(parentDsti));
1320 0 : dsti.swap(parentDsti);
1321 : }
1322 :
1323 0 : return false;
1324 : }
1325 :
1326 : already_AddRefed<nsPIDOMWindow>
1327 0 : nsFocusManager::GetCommonAncestor(nsPIDOMWindow* aWindow1,
1328 : nsPIDOMWindow* aWindow2)
1329 : {
1330 0 : nsCOMPtr<nsIWebNavigation> webnav(do_GetInterface(aWindow1));
1331 0 : nsCOMPtr<nsIDocShellTreeItem> dsti1 = do_QueryInterface(webnav);
1332 0 : NS_ENSURE_TRUE(dsti1, nsnull);
1333 :
1334 0 : webnav = do_GetInterface(aWindow2);
1335 0 : nsCOMPtr<nsIDocShellTreeItem> dsti2 = do_QueryInterface(webnav);
1336 0 : NS_ENSURE_TRUE(dsti2, nsnull);
1337 :
1338 0 : nsAutoTArray<nsIDocShellTreeItem*, 30> parents1, parents2;
1339 0 : do {
1340 0 : parents1.AppendElement(dsti1);
1341 0 : nsCOMPtr<nsIDocShellTreeItem> parentDsti1;
1342 0 : dsti1->GetParent(getter_AddRefs(parentDsti1));
1343 0 : dsti1.swap(parentDsti1);
1344 0 : } while (dsti1);
1345 0 : do {
1346 0 : parents2.AppendElement(dsti2);
1347 0 : nsCOMPtr<nsIDocShellTreeItem> parentDsti2;
1348 0 : dsti2->GetParent(getter_AddRefs(parentDsti2));
1349 0 : dsti2.swap(parentDsti2);
1350 0 : } while (dsti2);
1351 :
1352 0 : PRUint32 pos1 = parents1.Length();
1353 0 : PRUint32 pos2 = parents2.Length();
1354 0 : nsIDocShellTreeItem* parent = nsnull;
1355 : PRUint32 len;
1356 0 : for (len = NS_MIN(pos1, pos2); len > 0; --len) {
1357 0 : nsIDocShellTreeItem* child1 = parents1.ElementAt(--pos1);
1358 0 : nsIDocShellTreeItem* child2 = parents2.ElementAt(--pos2);
1359 0 : if (child1 != child2) {
1360 0 : break;
1361 : }
1362 0 : parent = child1;
1363 : }
1364 :
1365 0 : nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(parent);
1366 0 : return window.forget();
1367 : }
1368 :
1369 : void
1370 0 : nsFocusManager::AdjustWindowFocus(nsPIDOMWindow* aWindow,
1371 : bool aCheckPermission)
1372 : {
1373 0 : bool isVisible = IsWindowVisible(aWindow);
1374 :
1375 0 : nsCOMPtr<nsPIDOMWindow> window(aWindow);
1376 0 : while (window) {
1377 : // get the containing <iframe> or equivalent element so that it can be
1378 : // focused below.
1379 : nsCOMPtr<nsIContent> frameContent =
1380 0 : do_QueryInterface(window->GetFrameElementInternal());
1381 :
1382 0 : nsCOMPtr<nsIWebNavigation> webnav(do_GetInterface(window));
1383 0 : nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(webnav);
1384 0 : if (!dsti)
1385 : return;
1386 0 : nsCOMPtr<nsIDocShellTreeItem> parentDsti;
1387 0 : dsti->GetParent(getter_AddRefs(parentDsti));
1388 :
1389 0 : window = do_GetInterface(parentDsti);
1390 0 : if (window) {
1391 : // if the parent window is visible but aWindow was not, then we have
1392 : // likely moved up and out from a hidden tab to the browser window, or a
1393 : // similar such arrangement. Stop adjusting the current nodes.
1394 0 : if (IsWindowVisible(window) != isVisible)
1395 : break;
1396 :
1397 : // When aCheckPermission is true, we should check whether the caller can
1398 : // access the window or not. If it cannot access, we should stop the
1399 : // adjusting.
1400 0 : if (aCheckPermission && !nsContentUtils::CanCallerAccess(window))
1401 : break;
1402 :
1403 0 : window->SetFocusedNode(frameContent);
1404 : }
1405 : }
1406 : }
1407 :
1408 : bool
1409 0 : nsFocusManager::IsWindowVisible(nsPIDOMWindow* aWindow)
1410 : {
1411 0 : if (!aWindow)
1412 0 : return false;
1413 :
1414 0 : nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
1415 0 : nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(docShell));
1416 0 : if (!baseWin)
1417 0 : return false;
1418 :
1419 0 : bool visible = false;
1420 0 : baseWin->GetVisibility(&visible);
1421 0 : return visible;
1422 : }
1423 :
1424 : bool
1425 0 : nsFocusManager::IsNonFocusableRoot(nsIContent* aContent)
1426 : {
1427 0 : NS_PRECONDITION(aContent, "aContent must not be NULL");
1428 0 : NS_PRECONDITION(aContent->IsInDoc(), "aContent must be in a document");
1429 :
1430 : // If aContent is in designMode, the root element is not focusable.
1431 : // NOTE: in designMode, most elements are not focusable, just the document is
1432 : // focusable.
1433 : // Also, if aContent is not editable but it isn't in designMode, it's not
1434 : // focusable.
1435 0 : nsIDocument* doc = aContent->GetCurrentDoc();
1436 0 : NS_ASSERTION(doc, "aContent must have current document");
1437 0 : return aContent == doc->GetRootElement() &&
1438 0 : (doc->HasFlag(NODE_IS_EDITABLE) || !aContent->IsEditable());
1439 : }
1440 :
1441 : nsIContent*
1442 0 : nsFocusManager::CheckIfFocusable(nsIContent* aContent, PRUint32 aFlags)
1443 : {
1444 0 : if (!aContent)
1445 0 : return nsnull;
1446 :
1447 : // this is a special case for some XUL elements where an anonymous child is
1448 : // actually focusable and not the element itself.
1449 0 : nsIContent* redirectedFocus = GetRedirectedFocus(aContent);
1450 0 : if (redirectedFocus)
1451 0 : return CheckIfFocusable(redirectedFocus, aFlags);
1452 :
1453 0 : nsCOMPtr<nsIDocument> doc = aContent->GetCurrentDoc();
1454 : // can't focus elements that are not in documents
1455 0 : if (!doc)
1456 0 : return nsnull;
1457 :
1458 : // Make sure that our frames are up to date
1459 0 : doc->FlushPendingNotifications(Flush_Layout);
1460 :
1461 0 : nsIPresShell *shell = doc->GetShell();
1462 0 : if (!shell)
1463 0 : return nsnull;
1464 :
1465 : // the root content can always be focused
1466 0 : if (aContent == doc->GetRootElement())
1467 0 : return aContent;
1468 :
1469 : // cannot focus content in print preview mode. Only the root can be focused.
1470 0 : nsPresContext* presContext = shell->GetPresContext();
1471 0 : if (presContext && presContext->Type() == nsPresContext::eContext_PrintPreview)
1472 0 : return nsnull;
1473 :
1474 0 : nsIFrame* frame = aContent->GetPrimaryFrame();
1475 0 : if (!frame)
1476 0 : return nsnull;
1477 :
1478 0 : if (aContent->Tag() == nsGkAtoms::area && aContent->IsHTML()) {
1479 : // HTML areas do not have their own frame, and the img frame we get from
1480 : // GetPrimaryFrame() is not relevant as to whether it is focusable or
1481 : // not, so we have to do all the relevant checks manually for them.
1482 0 : return frame->IsVisibleConsideringAncestors() &&
1483 0 : aContent->IsFocusable() ? aContent : nsnull;
1484 : }
1485 :
1486 : // if this is a child frame content node, check if it is visible and
1487 : // call the content node's IsFocusable method instead of the frame's
1488 : // IsFocusable method. This skips checking the style system and ensures that
1489 : // offscreen browsers can still be focused.
1490 0 : nsIDocument* subdoc = doc->GetSubDocumentFor(aContent);
1491 0 : if (subdoc && IsWindowVisible(subdoc->GetWindow())) {
1492 0 : const nsStyleUserInterface* ui = frame->GetStyleUserInterface();
1493 : PRInt32 tabIndex = (ui->mUserFocus == NS_STYLE_USER_FOCUS_IGNORE ||
1494 0 : ui->mUserFocus == NS_STYLE_USER_FOCUS_NONE) ? -1 : 0;
1495 0 : return aContent->IsFocusable(&tabIndex, aFlags & FLAG_BYMOUSE) ? aContent : nsnull;
1496 : }
1497 :
1498 0 : return frame->IsFocusable(nsnull, aFlags & FLAG_BYMOUSE) ? aContent : nsnull;
1499 : }
1500 :
1501 : bool
1502 0 : nsFocusManager::Blur(nsPIDOMWindow* aWindowToClear,
1503 : nsPIDOMWindow* aAncestorWindowToFocus,
1504 : bool aIsLeavingDocument,
1505 : bool aAdjustWidgets)
1506 : {
1507 : // hold a reference to the focused content, which may be null
1508 0 : nsCOMPtr<nsIContent> content = mFocusedContent;
1509 0 : if (content) {
1510 0 : if (!content->IsInDoc()) {
1511 0 : mFocusedContent = nsnull;
1512 0 : return true;
1513 : }
1514 0 : if (content == mFirstBlurEvent)
1515 0 : return true;
1516 : }
1517 :
1518 : // hold a reference to the focused window
1519 0 : nsCOMPtr<nsPIDOMWindow> window = mFocusedWindow;
1520 0 : if (!window) {
1521 0 : mFocusedContent = nsnull;
1522 0 : return true;
1523 : }
1524 :
1525 0 : nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
1526 0 : if (!docShell) {
1527 0 : mFocusedContent = nsnull;
1528 0 : return true;
1529 : }
1530 :
1531 : // Keep a ref to presShell since dispatching the DOM event may cause
1532 : // the document to be destroyed.
1533 0 : nsCOMPtr<nsIPresShell> presShell;
1534 0 : docShell->GetPresShell(getter_AddRefs(presShell));
1535 0 : if (!presShell) {
1536 0 : mFocusedContent = nsnull;
1537 0 : return true;
1538 : }
1539 :
1540 0 : bool clearFirstBlurEvent = false;
1541 0 : if (!mFirstBlurEvent) {
1542 0 : mFirstBlurEvent = content;
1543 0 : clearFirstBlurEvent = true;
1544 : }
1545 :
1546 : // if there is still an active window, adjust the IME state.
1547 : // This has to happen before the focus is cleared below, otherwise, the IME
1548 : // compositionend event won't get fired at the element being blurred.
1549 0 : nsIMEStateManager::OnTextStateBlur(nsnull, nsnull);
1550 0 : if (mActiveWindow) {
1551 : nsIMEStateManager::OnChangeFocus(presShell->GetPresContext(), nsnull,
1552 0 : GetFocusMoveActionCause(0));
1553 : }
1554 :
1555 : // now adjust the actual focus, by clearing the fields in the focus manager
1556 : // and in the window.
1557 0 : mFocusedContent = nsnull;
1558 0 : bool shouldShowFocusRing = window->ShouldShowFocusRing();
1559 0 : if (aWindowToClear)
1560 0 : aWindowToClear->SetFocusedNode(nsnull);
1561 :
1562 : #ifdef DEBUG_FOCUS
1563 : PRINTTAGF("**Element %s has been blurred\n", content);
1564 : #endif
1565 :
1566 : // Don't fire blur event on the root content which isn't editable.
1567 : bool sendBlurEvent =
1568 0 : content && content->IsInDoc() && !IsNonFocusableRoot(content);
1569 0 : if (content) {
1570 0 : if (sendBlurEvent) {
1571 0 : NotifyFocusStateChange(content, shouldShowFocusRing, false);
1572 : }
1573 :
1574 : // if an object/plug-in/remote browser is being blurred, move the system focus
1575 : // to the parent window, otherwise events will still get fired at the plugin.
1576 : // But don't do this if we are blurring due to the window being lowered,
1577 : // otherwise, the parent window can get raised again.
1578 0 : if (mActiveWindow) {
1579 0 : nsIFrame* contentFrame = content->GetPrimaryFrame();
1580 0 : nsIObjectFrame* objectFrame = do_QueryFrame(contentFrame);
1581 0 : if (aAdjustWidgets && objectFrame && !sTestMode) {
1582 : // note that the presshell's widget is being retrieved here, not the one
1583 : // for the object frame.
1584 0 : nsIViewManager* vm = presShell->GetViewManager();
1585 0 : if (vm) {
1586 0 : nsCOMPtr<nsIWidget> widget;
1587 0 : vm->GetRootWidget(getter_AddRefs(widget));
1588 0 : if (widget)
1589 0 : widget->SetFocus(false);
1590 : }
1591 : }
1592 :
1593 : // if the object being blurred is a remote browser, deactivate remote content
1594 0 : TabParent* remote = GetRemoteForContent(content);
1595 0 : if (remote) {
1596 0 : remote->Deactivate();
1597 : #ifdef DEBUG_FOCUS
1598 : printf("*Remote browser deactivated\n");
1599 : #endif
1600 : }
1601 : }
1602 : }
1603 :
1604 0 : bool result = true;
1605 0 : if (sendBlurEvent) {
1606 : // if there is an active window, update commands. If there isn't an active
1607 : // window, then this was a blur caused by the active window being lowered,
1608 : // so there is no need to update the commands
1609 0 : if (mActiveWindow)
1610 0 : window->UpdateCommands(NS_LITERAL_STRING("focus"));
1611 :
1612 : SendFocusOrBlurEvent(NS_BLUR_CONTENT, presShell,
1613 0 : content->GetCurrentDoc(), content, 1, false);
1614 : }
1615 :
1616 : // if we are leaving the document or the window was lowered, make the caret
1617 : // invisible.
1618 0 : if (aIsLeavingDocument || !mActiveWindow)
1619 0 : SetCaretVisible(presShell, false, nsnull);
1620 :
1621 : // at this point, it is expected that this window will be still be
1622 : // focused, but the focused content will be null, as it was cleared before
1623 : // the event. If this isn't the case, then something else was focused during
1624 : // the blur event above and we should just return. However, if
1625 : // aIsLeavingDocument is set, a new document is desired, so make sure to
1626 : // blur the document and window.
1627 0 : if (mFocusedWindow != window ||
1628 0 : (mFocusedContent != nsnull && !aIsLeavingDocument)) {
1629 0 : result = false;
1630 : }
1631 0 : else if (aIsLeavingDocument) {
1632 0 : window->TakeFocus(false, 0);
1633 :
1634 : // clear the focus so that the ancestor frame hierarchy is in the correct
1635 : // state. Pass true because aAncestorWindowToFocus is thought to be
1636 : // focused at this point.
1637 0 : if (aAncestorWindowToFocus)
1638 0 : aAncestorWindowToFocus->SetFocusedNode(nsnull, 0, true);
1639 :
1640 0 : mFocusedWindow = nsnull;
1641 0 : mFocusedContent = nsnull;
1642 :
1643 : // pass 1 for the focus method when calling SendFocusOrBlurEvent just so
1644 : // that the check is made for suppressed documents. Check to ensure that
1645 : // the document isn't null in case someone closed it during the blur above
1646 0 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(window->GetExtantDocument());
1647 0 : if (doc)
1648 0 : SendFocusOrBlurEvent(NS_BLUR_CONTENT, presShell, doc, doc, 1, false);
1649 0 : if (mFocusedWindow == nsnull)
1650 0 : SendFocusOrBlurEvent(NS_BLUR_CONTENT, presShell, doc, window, 1, false);
1651 :
1652 : // check if a different window was focused
1653 0 : result = (mFocusedWindow == nsnull && mActiveWindow);
1654 : }
1655 0 : else if (mActiveWindow) {
1656 : // Otherwise, the blur of the element without blurring the document
1657 : // occurred normally. Call UpdateCaret to redisplay the caret at the right
1658 : // location within the document. This is needed to ensure that the caret
1659 : // used for caret browsing is made visible again when an input field is
1660 : // blurred.
1661 0 : UpdateCaret(false, true, nsnull);
1662 : }
1663 :
1664 0 : if (clearFirstBlurEvent)
1665 0 : mFirstBlurEvent = nsnull;
1666 :
1667 0 : return result;
1668 : }
1669 :
1670 : void
1671 0 : nsFocusManager::Focus(nsPIDOMWindow* aWindow,
1672 : nsIContent* aContent,
1673 : PRUint32 aFlags,
1674 : bool aIsNewDocument,
1675 : bool aFocusChanged,
1676 : bool aWindowRaised,
1677 : bool aAdjustWidgets)
1678 : {
1679 0 : if (!aWindow)
1680 0 : return;
1681 :
1682 0 : if (aContent && (aContent == mFirstFocusEvent || aContent == mFirstBlurEvent))
1683 0 : return;
1684 :
1685 : // Keep a reference to the presShell since dispatching the DOM event may
1686 : // cause the document to be destroyed.
1687 0 : nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
1688 0 : if (!docShell)
1689 : return;
1690 :
1691 0 : nsCOMPtr<nsIPresShell> presShell;
1692 0 : docShell->GetPresShell(getter_AddRefs(presShell));
1693 0 : if (!presShell)
1694 : return;
1695 :
1696 : // If the focus actually changed, set the focus method (mouse, keyboard, etc).
1697 : // Otherwise, just get the current focus method and use that. This ensures
1698 : // that the method is set during the document and window focus events.
1699 : PRUint32 focusMethod = aFocusChanged ? aFlags & FOCUSMETHODANDRING_MASK :
1700 0 : aWindow->GetFocusMethod() | (aFlags & FLAG_SHOWRING);
1701 :
1702 0 : if (!IsWindowVisible(aWindow)) {
1703 : // if the window isn't visible, for instance because it is a hidden tab,
1704 : // update the current focus and scroll it into view but don't do anything else
1705 0 : if (CheckIfFocusable(aContent, aFlags)) {
1706 0 : aWindow->SetFocusedNode(aContent, focusMethod);
1707 0 : if (aFocusChanged)
1708 0 : ScrollIntoView(presShell, aContent, aFlags);
1709 : }
1710 : return;
1711 : }
1712 :
1713 0 : bool clearFirstFocusEvent = false;
1714 0 : if (!mFirstFocusEvent) {
1715 0 : mFirstFocusEvent = aContent;
1716 0 : clearFirstFocusEvent = true;
1717 : }
1718 :
1719 : #ifdef DEBUG_FOCUS
1720 : PRINTTAGF("**Element %s has been focused", aContent);
1721 : nsCOMPtr<nsIDocument> docm = do_QueryInterface(aWindow->GetExtantDocument());
1722 : if (docm)
1723 : PRINTTAGF(" from %s", docm->GetRootElement());
1724 : printf(" [Newdoc: %d FocusChanged: %d Raised: %d Flags: %x]\n",
1725 : aIsNewDocument, aFocusChanged, aWindowRaised, aFlags);
1726 : #endif
1727 :
1728 0 : if (aIsNewDocument) {
1729 : // if this is a new document, update the parent chain of frames so that
1730 : // focus can be traversed from the top level down to the newly focused
1731 : // window.
1732 0 : AdjustWindowFocus(aWindow, false);
1733 :
1734 : // Update the window touch registration to reflect the state of
1735 : // the new document that got focus
1736 0 : aWindow->UpdateTouchState();
1737 : }
1738 :
1739 : // indicate that the window has taken focus.
1740 0 : if (aWindow->TakeFocus(true, focusMethod))
1741 0 : aIsNewDocument = true;
1742 :
1743 0 : mFocusedWindow = aWindow;
1744 :
1745 : // Update the system focus by focusing the root widget. But avoid this
1746 : // if 1) aAdjustWidgets is false or 2) aContent is a plugin that has its
1747 : // own widget and is either already focused or is about to be focused.
1748 0 : nsCOMPtr<nsIWidget> objectFrameWidget;
1749 0 : if (aContent) {
1750 0 : nsIFrame* contentFrame = aContent->GetPrimaryFrame();
1751 0 : nsIObjectFrame* objectFrame = do_QueryFrame(contentFrame);
1752 0 : if (objectFrame)
1753 0 : objectFrameWidget = objectFrame->GetWidget();
1754 : }
1755 0 : if (aAdjustWidgets && !objectFrameWidget && !sTestMode) {
1756 0 : nsIViewManager* vm = presShell->GetViewManager();
1757 0 : if (vm) {
1758 0 : nsCOMPtr<nsIWidget> widget;
1759 0 : vm->GetRootWidget(getter_AddRefs(widget));
1760 0 : if (widget)
1761 0 : widget->SetFocus(false);
1762 : }
1763 : }
1764 :
1765 : // if switching to a new document, first fire the focus event on the
1766 : // document and then the window.
1767 0 : if (aIsNewDocument) {
1768 0 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(aWindow->GetExtantDocument());
1769 0 : if (doc)
1770 : SendFocusOrBlurEvent(NS_FOCUS_CONTENT, presShell, doc,
1771 0 : doc, aFlags & FOCUSMETHOD_MASK, aWindowRaised);
1772 0 : if (mFocusedWindow == aWindow && mFocusedContent == nsnull)
1773 : SendFocusOrBlurEvent(NS_FOCUS_CONTENT, presShell, doc,
1774 0 : aWindow, aFlags & FOCUSMETHOD_MASK, aWindowRaised);
1775 : }
1776 :
1777 : // check to ensure that the element is still focusable, and that nothing
1778 : // else was focused during the events above.
1779 0 : if (CheckIfFocusable(aContent, aFlags) &&
1780 0 : mFocusedWindow == aWindow && mFocusedContent == nsnull) {
1781 0 : mFocusedContent = aContent;
1782 :
1783 0 : nsIContent* focusedNode = aWindow->GetFocusedNode();
1784 0 : bool isRefocus = focusedNode && focusedNode->IsEqualTo(aContent);
1785 :
1786 0 : aWindow->SetFocusedNode(aContent, focusMethod);
1787 :
1788 : bool sendFocusEvent =
1789 0 : aContent && aContent->IsInDoc() && !IsNonFocusableRoot(aContent);
1790 0 : nsPresContext* presContext = presShell->GetPresContext();
1791 0 : if (sendFocusEvent) {
1792 : // if the focused element changed, scroll it into view
1793 0 : if (aFocusChanged)
1794 0 : ScrollIntoView(presShell, aContent, aFlags);
1795 :
1796 0 : NotifyFocusStateChange(aContent, aWindow->ShouldShowFocusRing(), true);
1797 :
1798 : // if this is an object/plug-in/remote browser, focus its widget. Note that we might
1799 : // no longer be in the same document, due to the events we fired above when
1800 : // aIsNewDocument.
1801 0 : if (presShell->GetDocument() == aContent->GetDocument()) {
1802 0 : if (aAdjustWidgets && objectFrameWidget && !sTestMode)
1803 0 : objectFrameWidget->SetFocus(false);
1804 :
1805 : // if the object being focused is a remote browser, activate remote content
1806 0 : TabParent* remote = GetRemoteForContent(aContent);
1807 0 : if (remote) {
1808 0 : remote->Activate();
1809 : #ifdef DEBUG_FOCUS
1810 : printf("*Remote browser activated\n");
1811 : #endif
1812 : }
1813 : }
1814 :
1815 : nsIMEStateManager::OnChangeFocus(presContext, aContent,
1816 0 : GetFocusMoveActionCause(aFlags));
1817 :
1818 : // as long as this focus wasn't because a window was raised, update the
1819 : // commands
1820 : // XXXndeakin P2 someone could adjust the focus during the update
1821 0 : if (!aWindowRaised)
1822 0 : aWindow->UpdateCommands(NS_LITERAL_STRING("focus"));
1823 :
1824 : SendFocusOrBlurEvent(NS_FOCUS_CONTENT, presShell,
1825 : aContent->GetCurrentDoc(),
1826 : aContent, aFlags & FOCUSMETHOD_MASK,
1827 0 : aWindowRaised, isRefocus);
1828 :
1829 0 : nsIMEStateManager::OnTextStateFocus(presContext, aContent);
1830 : } else {
1831 0 : nsIMEStateManager::OnTextStateBlur(presContext, nsnull);
1832 : nsIMEStateManager::OnChangeFocus(presContext, nsnull,
1833 0 : GetFocusMoveActionCause(aFlags));
1834 0 : if (!aWindowRaised) {
1835 0 : aWindow->UpdateCommands(NS_LITERAL_STRING("focus"));
1836 : }
1837 : }
1838 : }
1839 : else {
1840 : // If the window focus event (fired above when aIsNewDocument) caused
1841 : // the plugin not to be focusable, update the system focus by focusing
1842 : // the root widget.
1843 0 : if (aAdjustWidgets && objectFrameWidget &&
1844 0 : mFocusedWindow == aWindow && mFocusedContent == nsnull &&
1845 0 : !sTestMode) {
1846 0 : nsIViewManager* vm = presShell->GetViewManager();
1847 0 : if (vm) {
1848 0 : nsCOMPtr<nsIWidget> widget;
1849 0 : vm->GetRootWidget(getter_AddRefs(widget));
1850 0 : if (widget)
1851 0 : widget->SetFocus(false);
1852 : }
1853 : }
1854 :
1855 0 : nsPresContext* presContext = presShell->GetPresContext();
1856 0 : nsIMEStateManager::OnTextStateBlur(presContext, nsnull);
1857 : nsIMEStateManager::OnChangeFocus(presContext, nsnull,
1858 0 : GetFocusMoveActionCause(aFlags));
1859 :
1860 0 : if (!aWindowRaised)
1861 0 : aWindow->UpdateCommands(NS_LITERAL_STRING("focus"));
1862 : }
1863 :
1864 : // update the caret visibility and position to match the newly focused
1865 : // element. However, don't update the position if this was a focus due to a
1866 : // mouse click as the selection code would already have moved the caret as
1867 : // needed. If this is a different document than was focused before, also
1868 : // update the caret's visibility. If this is the same document, the caret
1869 : // visibility should be the same as before so there is no need to update it.
1870 0 : if (mFocusedContent == aContent)
1871 0 : UpdateCaret(aFocusChanged && !(aFlags & FLAG_BYMOUSE), aIsNewDocument,
1872 0 : mFocusedContent);
1873 :
1874 0 : if (clearFirstFocusEvent)
1875 0 : mFirstFocusEvent = nsnull;
1876 : }
1877 :
1878 : class FocusBlurEvent : public nsRunnable
1879 0 : {
1880 : public:
1881 0 : FocusBlurEvent(nsISupports* aTarget, PRUint32 aType,
1882 : nsPresContext* aContext, bool aWindowRaised,
1883 : bool aIsRefocus)
1884 : : mTarget(aTarget), mType(aType), mContext(aContext),
1885 0 : mWindowRaised(aWindowRaised), mIsRefocus(aIsRefocus) {}
1886 :
1887 0 : NS_IMETHOD Run()
1888 : {
1889 0 : nsFocusEvent event(true, mType);
1890 0 : event.flags |= NS_EVENT_FLAG_CANT_BUBBLE;
1891 0 : event.fromRaise = mWindowRaised;
1892 0 : event.isRefocus = mIsRefocus;
1893 0 : return nsEventDispatcher::Dispatch(mTarget, mContext, &event);
1894 : }
1895 :
1896 : nsCOMPtr<nsISupports> mTarget;
1897 : PRUint32 mType;
1898 : nsRefPtr<nsPresContext> mContext;
1899 : bool mWindowRaised;
1900 : bool mIsRefocus;
1901 : };
1902 :
1903 : void
1904 0 : nsFocusManager::SendFocusOrBlurEvent(PRUint32 aType,
1905 : nsIPresShell* aPresShell,
1906 : nsIDocument* aDocument,
1907 : nsISupports* aTarget,
1908 : PRUint32 aFocusMethod,
1909 : bool aWindowRaised,
1910 : bool aIsRefocus)
1911 : {
1912 0 : NS_ASSERTION(aType == NS_FOCUS_CONTENT || aType == NS_BLUR_CONTENT,
1913 : "Wrong event type for SendFocusOrBlurEvent");
1914 :
1915 0 : nsCOMPtr<nsIDOMEventTarget> eventTarget = do_QueryInterface(aTarget);
1916 :
1917 : // for focus events, if this event was from a mouse or key and event
1918 : // handling on the document is suppressed, queue the event and fire it
1919 : // later. For blur events, a non-zero value would be set for aFocusMethod.
1920 0 : if (aFocusMethod && aDocument && aDocument->EventHandlingSuppressed()) {
1921 : // aFlags is always 0 when aWindowRaised is true so this won't be called
1922 : // on a window raise.
1923 0 : NS_ASSERTION(!aWindowRaised, "aWindowRaised should not be set");
1924 :
1925 0 : for (PRUint32 i = mDelayedBlurFocusEvents.Length(); i > 0; --i) {
1926 : // if this event was already queued, remove it and append it to the end
1927 0 : if (mDelayedBlurFocusEvents[i - 1].mType == aType &&
1928 0 : mDelayedBlurFocusEvents[i - 1].mPresShell == aPresShell &&
1929 0 : mDelayedBlurFocusEvents[i - 1].mDocument == aDocument &&
1930 0 : mDelayedBlurFocusEvents[i - 1].mTarget == eventTarget) {
1931 0 : mDelayedBlurFocusEvents.RemoveElementAt(i - 1);
1932 : }
1933 : }
1934 :
1935 : mDelayedBlurFocusEvents.AppendElement(
1936 0 : nsDelayedBlurOrFocusEvent(aType, aPresShell, aDocument, eventTarget));
1937 : return;
1938 : }
1939 :
1940 : #ifdef ACCESSIBILITY
1941 0 : nsAccessibilityService* accService = GetAccService();
1942 0 : if (accService) {
1943 0 : if (aType == NS_FOCUS_CONTENT)
1944 0 : accService->NotifyOfDOMFocus(aTarget);
1945 : else
1946 0 : accService->NotifyOfDOMBlur(aTarget);
1947 : }
1948 : #endif
1949 :
1950 : nsContentUtils::AddScriptRunner(
1951 : new FocusBlurEvent(aTarget, aType, aPresShell->GetPresContext(),
1952 0 : aWindowRaised, aIsRefocus));
1953 : }
1954 :
1955 : void
1956 0 : nsFocusManager::ScrollIntoView(nsIPresShell* aPresShell,
1957 : nsIContent* aContent,
1958 : PRUint32 aFlags)
1959 : {
1960 : // if the noscroll flag isn't set, scroll the newly focused element into view
1961 0 : if (!(aFlags & FLAG_NOSCROLL))
1962 : aPresShell->ScrollContentIntoView(aContent,
1963 : NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE,
1964 : NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE,
1965 0 : nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
1966 0 : }
1967 :
1968 :
1969 : void
1970 0 : nsFocusManager::RaiseWindow(nsPIDOMWindow* aWindow)
1971 : {
1972 : // don't raise windows that are already raised or are in the process of
1973 : // being lowered
1974 0 : if (!aWindow || aWindow == mActiveWindow || aWindow == mWindowBeingLowered)
1975 0 : return;
1976 :
1977 0 : if (sTestMode) {
1978 : // In test mode, emulate the existing window being lowered and the new
1979 : // window being raised.
1980 0 : if (mActiveWindow)
1981 0 : WindowLowered(mActiveWindow);
1982 0 : WindowRaised(aWindow);
1983 0 : return;
1984 : }
1985 :
1986 : #if defined(XP_WIN) || defined(XP_OS2)
1987 : // Windows would rather we focus the child widget, otherwise, the toplevel
1988 : // widget will always end up being focused. Fortunately, focusing the child
1989 : // widget will also have the effect of raising the window this widget is in.
1990 : // But on other platforms, we can just focus the toplevel widget to raise
1991 : // the window.
1992 : nsCOMPtr<nsPIDOMWindow> childWindow;
1993 : GetFocusedDescendant(aWindow, true, getter_AddRefs(childWindow));
1994 : if (!childWindow)
1995 : childWindow = aWindow;
1996 :
1997 : nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
1998 : if (!docShell)
1999 : return;
2000 :
2001 : nsCOMPtr<nsIPresShell> presShell;
2002 : docShell->GetPresShell(getter_AddRefs(presShell));
2003 : if (!presShell)
2004 : return;
2005 :
2006 : nsIViewManager* vm = presShell->GetViewManager();
2007 : if (vm) {
2008 : nsCOMPtr<nsIWidget> widget;
2009 : vm->GetRootWidget(getter_AddRefs(widget));
2010 : if (widget)
2011 : widget->SetFocus(true);
2012 : }
2013 : #else
2014 0 : nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(aWindow);
2015 0 : nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = do_QueryInterface(webnav);
2016 0 : if (treeOwnerAsWin) {
2017 0 : nsCOMPtr<nsIWidget> widget;
2018 0 : treeOwnerAsWin->GetMainWidget(getter_AddRefs(widget));
2019 0 : if (widget)
2020 0 : widget->SetFocus(true);
2021 : }
2022 : #endif
2023 : }
2024 :
2025 : void
2026 1 : nsFocusManager::UpdateCaret(bool aMoveCaretToFocus,
2027 : bool aUpdateVisibility,
2028 : nsIContent* aContent)
2029 : {
2030 : #ifdef DEBUG_FOCUS
2031 : printf("Update Caret: %d %d\n", aMoveCaretToFocus, aUpdateVisibility);
2032 : #endif
2033 :
2034 1 : if (!mFocusedWindow)
2035 1 : return;
2036 :
2037 : // this is called when a document is focused or when the caretbrowsing
2038 : // preference is changed
2039 0 : nsCOMPtr<nsIDocShell> focusedDocShell = mFocusedWindow->GetDocShell();
2040 0 : nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(focusedDocShell);
2041 0 : if (!dsti)
2042 : return;
2043 :
2044 : PRInt32 itemType;
2045 0 : dsti->GetItemType(&itemType);
2046 0 : if (itemType == nsIDocShellTreeItem::typeChrome)
2047 : return; // Never browse with caret in chrome
2048 :
2049 : bool browseWithCaret =
2050 0 : Preferences::GetBool("accessibility.browsewithcaret");
2051 :
2052 0 : nsCOMPtr<nsIPresShell> presShell;
2053 0 : focusedDocShell->GetPresShell(getter_AddRefs(presShell));
2054 0 : if (!presShell)
2055 : return;
2056 :
2057 : // If this is an editable document which isn't contentEditable, or a
2058 : // contentEditable document and the node to focus is contentEditable,
2059 : // return, so that we don't mess with caret visibility.
2060 0 : bool isEditable = false;
2061 0 : nsCOMPtr<nsIEditorDocShell> editorDocShell(do_QueryInterface(dsti));
2062 0 : if (editorDocShell) {
2063 0 : editorDocShell->GetEditable(&isEditable);
2064 :
2065 0 : if (isEditable) {
2066 : nsCOMPtr<nsIHTMLDocument> doc =
2067 0 : do_QueryInterface(presShell->GetDocument());
2068 :
2069 : bool isContentEditableDoc =
2070 0 : doc && doc->GetEditingState() == nsIHTMLDocument::eContentEditable;
2071 :
2072 : bool isFocusEditable =
2073 0 : aContent && aContent->HasFlag(NODE_IS_EDITABLE);
2074 0 : if (!isContentEditableDoc || isFocusEditable)
2075 : return;
2076 : }
2077 : }
2078 :
2079 0 : if (!isEditable && aMoveCaretToFocus)
2080 0 : MoveCaretToFocus(presShell, aContent);
2081 :
2082 0 : if (!aUpdateVisibility)
2083 : return;
2084 :
2085 : // XXXndeakin this doesn't seem right. It should be checking for this only
2086 : // on the nearest ancestor frame which is a chrome frame. But this is
2087 : // what the existing code does, so just leave it for now.
2088 0 : if (!browseWithCaret) {
2089 : nsCOMPtr<nsIContent> docContent =
2090 0 : do_QueryInterface(mFocusedWindow->GetFrameElementInternal());
2091 0 : if (docContent)
2092 0 : browseWithCaret = docContent->AttrValueIs(kNameSpaceID_None,
2093 : nsGkAtoms::showcaret,
2094 0 : NS_LITERAL_STRING("true"),
2095 0 : eCaseMatters);
2096 : }
2097 :
2098 0 : SetCaretVisible(presShell, browseWithCaret, aContent);
2099 : }
2100 :
2101 : void
2102 0 : nsFocusManager::MoveCaretToFocus(nsIPresShell* aPresShell, nsIContent* aContent)
2103 : {
2104 : // domDoc is a document interface we can create a range with
2105 0 : nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aPresShell->GetDocument());
2106 0 : if (domDoc) {
2107 0 : nsRefPtr<nsFrameSelection> frameSelection = aPresShell->FrameSelection();
2108 : nsCOMPtr<nsISelection> domSelection = frameSelection->
2109 0 : GetSelection(nsISelectionController::SELECTION_NORMAL);
2110 0 : if (domSelection) {
2111 0 : nsCOMPtr<nsIDOMNode> currentFocusNode(do_QueryInterface(aContent));
2112 : // First clear the selection. This way, if there is no currently focused
2113 : // content, the selection will just be cleared.
2114 0 : domSelection->RemoveAllRanges();
2115 0 : if (currentFocusNode) {
2116 0 : nsCOMPtr<nsIDOMRange> newRange;
2117 0 : nsresult rv = domDoc->CreateRange(getter_AddRefs(newRange));
2118 0 : if (NS_SUCCEEDED(rv)) {
2119 : // Set the range to the start of the currently focused node
2120 : // Make sure it's collapsed
2121 0 : newRange->SelectNodeContents(currentFocusNode);
2122 0 : nsCOMPtr<nsIDOMNode> firstChild;
2123 0 : currentFocusNode->GetFirstChild(getter_AddRefs(firstChild));
2124 0 : if (!firstChild ||
2125 0 : aContent->IsNodeOfType(nsINode::eHTML_FORM_CONTROL)) {
2126 : // If current focus node is a leaf, set range to before the
2127 : // node by using the parent as a container.
2128 : // This prevents it from appearing as selected.
2129 0 : newRange->SetStartBefore(currentFocusNode);
2130 0 : newRange->SetEndBefore(currentFocusNode);
2131 : }
2132 0 : domSelection->AddRange(newRange);
2133 0 : domSelection->CollapseToStart();
2134 : }
2135 : }
2136 : }
2137 : }
2138 0 : }
2139 :
2140 : nsresult
2141 0 : nsFocusManager::SetCaretVisible(nsIPresShell* aPresShell,
2142 : bool aVisible,
2143 : nsIContent* aContent)
2144 : {
2145 : // When browsing with caret, make sure caret is visible after new focus
2146 : // Return early if there is no caret. This can happen for the testcase
2147 : // for bug 308025 where a window is closed in a blur handler.
2148 0 : nsRefPtr<nsCaret> caret = aPresShell->GetCaret();
2149 0 : if (!caret)
2150 0 : return NS_OK;
2151 :
2152 0 : bool caretVisible = false;
2153 0 : caret->GetCaretVisible(&caretVisible);
2154 0 : if (!aVisible && !caretVisible)
2155 0 : return NS_OK;
2156 :
2157 0 : nsRefPtr<nsFrameSelection> frameSelection;
2158 0 : if (aContent) {
2159 0 : NS_ASSERTION(aContent->GetDocument() == aPresShell->GetDocument(),
2160 : "Wrong document?");
2161 0 : nsIFrame *focusFrame = aContent->GetPrimaryFrame();
2162 0 : if (focusFrame)
2163 0 : frameSelection = focusFrame->GetFrameSelection();
2164 : }
2165 :
2166 0 : nsRefPtr<nsFrameSelection> docFrameSelection = aPresShell->FrameSelection();
2167 :
2168 0 : if (docFrameSelection && caret &&
2169 0 : (frameSelection == docFrameSelection || !aContent)) {
2170 : nsISelection* domSelection = docFrameSelection->
2171 0 : GetSelection(nsISelectionController::SELECTION_NORMAL);
2172 0 : if (domSelection) {
2173 : // First, hide the caret to prevent attempting to show it in SetCaretDOMSelection
2174 0 : caret->SetCaretVisible(false);
2175 :
2176 : // Tell the caret which selection to use
2177 0 : caret->SetCaretDOMSelection(domSelection);
2178 :
2179 : // In content, we need to set the caret. The only special case is edit
2180 : // fields, which have a different frame selection from the document.
2181 : // They will take care of making the caret visible themselves.
2182 :
2183 0 : nsCOMPtr<nsISelectionController> selCon(do_QueryInterface(aPresShell));
2184 0 : if (!selCon)
2185 0 : return NS_ERROR_FAILURE;
2186 :
2187 0 : selCon->SetCaretEnabled(aVisible);
2188 0 : caret->SetCaretVisible(aVisible);
2189 : }
2190 : }
2191 :
2192 0 : return NS_OK;
2193 : }
2194 :
2195 : nsresult
2196 0 : nsFocusManager::GetSelectionLocation(nsIDocument* aDocument,
2197 : nsIPresShell* aPresShell,
2198 : nsIContent **aStartContent,
2199 : nsIContent **aEndContent)
2200 : {
2201 0 : *aStartContent = *aEndContent = nsnull;
2202 0 : nsresult rv = NS_ERROR_FAILURE;
2203 :
2204 0 : nsPresContext* presContext = aPresShell->GetPresContext();
2205 0 : NS_ASSERTION(presContext, "mPresContent is null!!");
2206 :
2207 0 : nsRefPtr<nsFrameSelection> frameSelection = aPresShell->FrameSelection();
2208 :
2209 0 : nsCOMPtr<nsISelection> domSelection;
2210 0 : if (frameSelection) {
2211 : domSelection = frameSelection->
2212 0 : GetSelection(nsISelectionController::SELECTION_NORMAL);
2213 : }
2214 :
2215 0 : nsCOMPtr<nsIDOMNode> startNode, endNode;
2216 0 : bool isCollapsed = false;
2217 0 : nsCOMPtr<nsIContent> startContent, endContent;
2218 0 : PRInt32 startOffset = 0;
2219 0 : if (domSelection) {
2220 0 : domSelection->GetIsCollapsed(&isCollapsed);
2221 0 : nsCOMPtr<nsIDOMRange> domRange;
2222 0 : rv = domSelection->GetRangeAt(0, getter_AddRefs(domRange));
2223 0 : if (domRange) {
2224 0 : domRange->GetStartContainer(getter_AddRefs(startNode));
2225 0 : domRange->GetEndContainer(getter_AddRefs(endNode));
2226 0 : domRange->GetStartOffset(&startOffset);
2227 :
2228 0 : nsIContent *childContent = nsnull;
2229 :
2230 0 : startContent = do_QueryInterface(startNode);
2231 0 : if (startContent && startContent->IsElement()) {
2232 0 : NS_ASSERTION(startOffset >= 0, "Start offset cannot be negative");
2233 0 : childContent = startContent->GetChildAt(startOffset);
2234 0 : if (childContent) {
2235 0 : startContent = childContent;
2236 : }
2237 : }
2238 :
2239 0 : endContent = do_QueryInterface(endNode);
2240 0 : if (endContent && endContent->IsElement()) {
2241 0 : PRInt32 endOffset = 0;
2242 0 : domRange->GetEndOffset(&endOffset);
2243 0 : NS_ASSERTION(endOffset >= 0, "End offset cannot be negative");
2244 0 : childContent = endContent->GetChildAt(endOffset);
2245 0 : if (childContent) {
2246 0 : endContent = childContent;
2247 : }
2248 : }
2249 : }
2250 : }
2251 : else {
2252 0 : rv = NS_ERROR_INVALID_ARG;
2253 : }
2254 :
2255 0 : nsIFrame *startFrame = nsnull;
2256 0 : if (startContent) {
2257 0 : startFrame = startContent->GetPrimaryFrame();
2258 0 : if (isCollapsed) {
2259 : // Next check to see if our caret is at the very end of a node
2260 : // If so, the caret is actually sitting in front of the next
2261 : // logical frame's primary node - so for this case we need to
2262 : // change caretContent to that node.
2263 :
2264 0 : if (startContent->NodeType() == nsIDOMNode::TEXT_NODE) {
2265 0 : nsAutoString nodeValue;
2266 0 : startContent->AppendTextTo(nodeValue);
2267 :
2268 : bool isFormControl =
2269 0 : startContent->IsNodeOfType(nsINode::eHTML_FORM_CONTROL);
2270 :
2271 0 : if (nodeValue.Length() == (PRUint32)startOffset && !isFormControl &&
2272 0 : startContent != aDocument->GetRootElement()) {
2273 : // Yes, indeed we were at the end of the last node
2274 0 : nsCOMPtr<nsIFrameEnumerator> frameTraversal;
2275 0 : nsresult rv = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
2276 : presContext, startFrame,
2277 : eLeaf,
2278 : false, // aVisual
2279 : false, // aLockInScrollView
2280 : true // aFollowOOFs
2281 0 : );
2282 0 : NS_ENSURE_SUCCESS(rv, rv);
2283 :
2284 0 : nsIFrame *newCaretFrame = nsnull;
2285 0 : nsCOMPtr<nsIContent> newCaretContent = startContent;
2286 0 : bool endOfSelectionInStartNode(startContent == endContent);
2287 0 : do {
2288 : // Continue getting the next frame until the primary content for the frame
2289 : // we are on changes - we don't want to be stuck in the same place
2290 0 : frameTraversal->Next();
2291 0 : newCaretFrame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
2292 0 : if (nsnull == newCaretFrame)
2293 0 : break;
2294 0 : newCaretContent = newCaretFrame->GetContent();
2295 0 : } while (!newCaretContent || newCaretContent == startContent);
2296 :
2297 0 : if (newCaretFrame && newCaretContent) {
2298 : // If the caret is exactly at the same position of the new frame,
2299 : // then we can use the newCaretFrame and newCaretContent for our position
2300 0 : nsRefPtr<nsCaret> caret = aPresShell->GetCaret();
2301 0 : nsRect caretRect;
2302 0 : nsIFrame *frame = caret->GetGeometry(domSelection, &caretRect);
2303 0 : if (frame) {
2304 0 : nsPoint caretWidgetOffset;
2305 0 : nsIWidget *widget = frame->GetNearestWidget(caretWidgetOffset);
2306 0 : caretRect.MoveBy(caretWidgetOffset);
2307 0 : nsPoint newCaretOffset;
2308 0 : nsIWidget *newCaretWidget = newCaretFrame->GetNearestWidget(newCaretOffset);
2309 0 : if (widget == newCaretWidget && caretRect.y == newCaretOffset.y &&
2310 : caretRect.x == newCaretOffset.x) {
2311 : // The caret is at the start of the new element.
2312 0 : startFrame = newCaretFrame;
2313 0 : startContent = newCaretContent;
2314 0 : if (endOfSelectionInStartNode) {
2315 0 : endContent = newCaretContent; // Ensure end of selection is not before start
2316 : }
2317 : }
2318 : }
2319 : }
2320 : }
2321 : }
2322 : }
2323 : }
2324 :
2325 0 : *aStartContent = startContent;
2326 0 : *aEndContent = endContent;
2327 0 : NS_IF_ADDREF(*aStartContent);
2328 0 : NS_IF_ADDREF(*aEndContent);
2329 :
2330 0 : return rv;
2331 : }
2332 :
2333 : nsresult
2334 0 : nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
2335 : nsIContent* aStartContent,
2336 : PRInt32 aType, bool aNoParentTraversal,
2337 : nsIContent** aNextContent)
2338 : {
2339 0 : *aNextContent = nsnull;
2340 :
2341 0 : nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
2342 0 : if (!docShell)
2343 0 : return NS_OK;
2344 :
2345 0 : nsCOMPtr<nsIContent> startContent = aStartContent;
2346 0 : if (!startContent && aType != MOVEFOCUS_CARET) {
2347 0 : if (aType == MOVEFOCUS_FORWARDDOC || aType == MOVEFOCUS_BACKWARDDOC) {
2348 : // When moving between documents, make sure to get the right
2349 : // starting content in a descendant.
2350 0 : nsCOMPtr<nsPIDOMWindow> focusedWindow;
2351 0 : startContent = GetFocusedDescendant(aWindow, true, getter_AddRefs(focusedWindow));
2352 : }
2353 : else {
2354 0 : startContent = aWindow->GetFocusedNode();
2355 : }
2356 : }
2357 :
2358 0 : nsCOMPtr<nsIDocument> doc;
2359 0 : if (startContent)
2360 0 : doc = startContent->GetCurrentDoc();
2361 : else
2362 0 : doc = do_QueryInterface(aWindow->GetExtantDocument());
2363 0 : if (!doc)
2364 0 : return NS_OK;
2365 :
2366 : LookAndFeel::GetInt(LookAndFeel::eIntID_TabFocusModel,
2367 0 : &nsIContent::sTabFocusModel);
2368 :
2369 0 : if (aType == MOVEFOCUS_ROOT) {
2370 0 : NS_IF_ADDREF(*aNextContent = GetRootForFocus(aWindow, doc, false, false));
2371 0 : return NS_OK;
2372 : }
2373 0 : if (aType == MOVEFOCUS_FORWARDDOC) {
2374 0 : NS_IF_ADDREF(*aNextContent = GetNextTabbableDocument(startContent, true));
2375 0 : return NS_OK;
2376 : }
2377 0 : if (aType == MOVEFOCUS_BACKWARDDOC) {
2378 0 : NS_IF_ADDREF(*aNextContent = GetNextTabbableDocument(startContent, false));
2379 0 : return NS_OK;
2380 : }
2381 :
2382 0 : nsIContent* rootContent = doc->GetRootElement();
2383 0 : NS_ENSURE_TRUE(rootContent, NS_OK);
2384 :
2385 0 : nsIPresShell *presShell = doc->GetShell();
2386 0 : NS_ENSURE_TRUE(presShell, NS_OK);
2387 :
2388 0 : if (aType == MOVEFOCUS_FIRST) {
2389 0 : if (!aStartContent)
2390 0 : startContent = rootContent;
2391 : return GetNextTabbableContent(presShell, startContent,
2392 : nsnull, startContent,
2393 0 : true, 1, false, aNextContent);
2394 : }
2395 0 : if (aType == MOVEFOCUS_LAST) {
2396 0 : if (!aStartContent)
2397 0 : startContent = rootContent;
2398 : return GetNextTabbableContent(presShell, startContent,
2399 : nsnull, startContent,
2400 0 : false, 0, false, aNextContent);
2401 : }
2402 :
2403 0 : bool forward = (aType == MOVEFOCUS_FORWARD || aType == MOVEFOCUS_CARET);
2404 0 : bool doNavigation = true;
2405 0 : bool ignoreTabIndex = false;
2406 : // when a popup is open, we want to ensure that tab navigation occurs only
2407 : // within the most recently opened panel. If a popup is open, its frame will
2408 : // be stored in popupFrame.
2409 0 : nsIFrame* popupFrame = nsnull;
2410 :
2411 0 : PRInt32 tabIndex = forward ? 1 : 0;
2412 0 : if (startContent) {
2413 0 : nsIFrame* frame = startContent->GetPrimaryFrame();
2414 0 : if (startContent->Tag() == nsGkAtoms::area &&
2415 0 : startContent->IsHTML())
2416 0 : startContent->IsFocusable(&tabIndex);
2417 0 : else if (frame)
2418 0 : frame->IsFocusable(&tabIndex, 0);
2419 : else
2420 0 : startContent->IsFocusable(&tabIndex);
2421 :
2422 : // if the current element isn't tabbable, ignore the tabindex and just
2423 : // look for the next element. The root content won't have a tabindex
2424 : // so just treat this as the beginning of the tab order.
2425 0 : if (tabIndex < 0) {
2426 0 : tabIndex = 1;
2427 0 : if (startContent != rootContent)
2428 0 : ignoreTabIndex = true;
2429 : }
2430 :
2431 : // check if the focus is currently inside a popup. Elements such as the
2432 : // autocomplete widget use the noautofocus attribute to allow the focus to
2433 : // remain outside the popup when it is opened.
2434 0 : if (frame) {
2435 : popupFrame = nsLayoutUtils::GetClosestFrameOfType(frame,
2436 0 : nsGkAtoms::menuPopupFrame);
2437 : }
2438 :
2439 0 : if (popupFrame) {
2440 : // Don't navigate outside of a popup, so pretend that the
2441 : // root content is the popup itself
2442 0 : rootContent = popupFrame->GetContent();
2443 0 : NS_ASSERTION(rootContent, "Popup frame doesn't have a content node");
2444 : }
2445 0 : else if (!forward) {
2446 : // If focus moves backward and when current focused node is root
2447 : // content or <body> element which is editable by contenteditable
2448 : // attribute, focus should move to its parent document.
2449 0 : if (startContent == rootContent) {
2450 0 : doNavigation = false;
2451 : } else {
2452 0 : nsIDocument* doc = startContent->GetCurrentDoc();
2453 0 : if (startContent ==
2454 0 : nsLayoutUtils::GetEditableRootContentByContentEditable(doc)) {
2455 0 : doNavigation = false;
2456 : }
2457 : }
2458 : }
2459 : }
2460 : else {
2461 : #ifdef MOZ_XUL
2462 : // if there is no focus, yet a panel is open, focus the first item in
2463 : // the panel
2464 0 : nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
2465 0 : if (pm)
2466 0 : popupFrame = pm->GetTopPopup(ePopupTypePanel);
2467 : #endif
2468 0 : if (popupFrame) {
2469 0 : rootContent = popupFrame->GetContent();
2470 0 : NS_ASSERTION(rootContent, "Popup frame doesn't have a content node");
2471 0 : startContent = rootContent;
2472 : }
2473 : else {
2474 : // Otherwise, for content shells, start from the location of the caret.
2475 : PRInt32 itemType;
2476 0 : nsCOMPtr<nsIDocShellTreeItem> shellItem = do_QueryInterface(docShell);
2477 0 : shellItem->GetItemType(&itemType);
2478 0 : if (itemType != nsIDocShellTreeItem::typeChrome) {
2479 0 : nsCOMPtr<nsIContent> endSelectionContent;
2480 : GetSelectionLocation(doc, presShell,
2481 0 : getter_AddRefs(startContent),
2482 0 : getter_AddRefs(endSelectionContent));
2483 : // If the selection is on the rootContent, then there is no selection
2484 0 : if (startContent == rootContent) {
2485 0 : startContent = nsnull;
2486 : }
2487 :
2488 0 : if (aType == MOVEFOCUS_CARET) {
2489 : // GetFocusInSelection finds a focusable link near the caret.
2490 : // If there is no start content though, don't do this to avoid
2491 : // focusing something unexpected.
2492 0 : if (startContent) {
2493 : GetFocusInSelection(aWindow, startContent,
2494 0 : endSelectionContent, aNextContent);
2495 : }
2496 0 : return NS_OK;
2497 : }
2498 :
2499 0 : if (startContent) {
2500 : // when starting from a selection, we always want to find the next or
2501 : // previous element in the document. So the tabindex on elements
2502 : // should be ignored.
2503 0 : ignoreTabIndex = true;
2504 : }
2505 : }
2506 :
2507 0 : if (!startContent) {
2508 : // otherwise, just use the root content as the starting point
2509 0 : startContent = rootContent;
2510 0 : NS_ENSURE_TRUE(startContent, NS_OK);
2511 : }
2512 : }
2513 : }
2514 :
2515 0 : NS_ASSERTION(startContent, "starting content not set");
2516 :
2517 : // keep a reference to the starting content. If we find that again, it means
2518 : // we've iterated around completely and we don't want to adjust the focus.
2519 : // The skipOriginalContentCheck will be set to true only for the first time
2520 : // GetNextTabbableContent is called. This ensures that we don't break out
2521 : // when nothing is focused to start with. Specifically,
2522 : // GetNextTabbableContent first checks the root content -- which happens to
2523 : // be the same as the start content -- when nothing is focused and tabbing
2524 : // forward. Without skipOriginalContentCheck set to true, we'd end up
2525 : // returning right away and focusing nothing. Luckily, GetNextTabbableContent
2526 : // will never wrap around on its own, and can only return the original
2527 : // content when it is called a second time or later.
2528 0 : bool skipOriginalContentCheck = true;
2529 0 : nsIContent* originalStartContent = startContent;
2530 :
2531 : #ifdef DEBUG_FOCUS_NAVIGATION
2532 : PRINTTAGF("Focus Navigation Start Content %s\n", startContent);
2533 : printf("[Tabindex: %d Ignore: %d]", tabIndex, ignoreTabIndex);
2534 : #endif
2535 :
2536 0 : while (doc) {
2537 0 : if (doNavigation) {
2538 0 : nsCOMPtr<nsIContent> nextFocus;
2539 : nsresult rv = GetNextTabbableContent(presShell, rootContent,
2540 : skipOriginalContentCheck ? nsnull : originalStartContent,
2541 : startContent, forward,
2542 : tabIndex, ignoreTabIndex,
2543 0 : getter_AddRefs(nextFocus));
2544 0 : NS_ENSURE_SUCCESS(rv, rv);
2545 :
2546 : // found a content node to focus.
2547 0 : if (nextFocus) {
2548 : #ifdef DEBUG_FOCUS_NAVIGATION
2549 : PRINTTAGF("Next Content: %s\n", nextFocus);
2550 : #endif
2551 : // as long as the found node was not the same as the starting node,
2552 : // set it as the return value.
2553 0 : if (nextFocus != originalStartContent)
2554 0 : NS_ADDREF(*aNextContent = nextFocus);
2555 0 : return NS_OK;
2556 : }
2557 :
2558 0 : if (popupFrame) {
2559 : // in a popup, so start again from the beginning of the popup. However,
2560 : // if we already started at the beginning, then there isn't anything to
2561 : // focus, so just return
2562 0 : if (startContent != rootContent) {
2563 0 : startContent = rootContent;
2564 0 : tabIndex = forward ? 1 : 0;
2565 0 : continue;
2566 : }
2567 0 : return NS_OK;
2568 : }
2569 : }
2570 :
2571 0 : doNavigation = true;
2572 0 : skipOriginalContentCheck = false;
2573 0 : ignoreTabIndex = false;
2574 :
2575 0 : if (aNoParentTraversal) {
2576 0 : startContent = rootContent;
2577 0 : tabIndex = forward ? 1 : 0;
2578 0 : continue;
2579 : }
2580 :
2581 : // reached the beginning or end of the document. Traverse up to the parent
2582 : // document and try again.
2583 0 : nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(docShell);
2584 :
2585 0 : nsCOMPtr<nsIDocShellTreeItem> docShellParent;
2586 0 : dsti->GetParent(getter_AddRefs(docShellParent));
2587 0 : if (docShellParent) {
2588 : // move up to the parent shell and try again from there.
2589 :
2590 : // first, get the frame element this window is inside.
2591 0 : nsCOMPtr<nsPIDOMWindow> piWindow = do_GetInterface(docShell);
2592 0 : NS_ENSURE_TRUE(piWindow, NS_ERROR_FAILURE);
2593 :
2594 : // Next, retrieve the parent docshell, document and presshell.
2595 0 : docShell = do_QueryInterface(docShellParent);
2596 0 : NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
2597 :
2598 0 : nsCOMPtr<nsPIDOMWindow> piParentWindow = do_GetInterface(docShellParent);
2599 0 : NS_ENSURE_TRUE(piParentWindow, NS_ERROR_FAILURE);
2600 0 : doc = do_QueryInterface(piParentWindow->GetExtantDocument());
2601 0 : NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
2602 :
2603 0 : presShell = doc->GetShell();
2604 :
2605 0 : rootContent = doc->GetRootElement();
2606 0 : startContent = do_QueryInterface(piWindow->GetFrameElementInternal());
2607 0 : if (startContent) {
2608 0 : nsIFrame* frame = startContent->GetPrimaryFrame();
2609 0 : if (!frame)
2610 0 : return NS_OK;
2611 :
2612 0 : frame->IsFocusable(&tabIndex, 0);
2613 0 : if (tabIndex < 0) {
2614 0 : tabIndex = 1;
2615 0 : ignoreTabIndex = true;
2616 : }
2617 :
2618 : // if the frame is inside a popup, make sure to scan only within the
2619 : // popup. This handles the situation of tabbing amongst elements
2620 : // inside an iframe which is itself inside a popup. Otherwise,
2621 : // navigation would move outside the popup when tabbing outside the
2622 : // iframe.
2623 : popupFrame = nsLayoutUtils::GetClosestFrameOfType(frame,
2624 0 : nsGkAtoms::menuPopupFrame);
2625 0 : if (popupFrame) {
2626 0 : rootContent = popupFrame->GetContent();
2627 0 : NS_ASSERTION(rootContent, "Popup frame doesn't have a content node");
2628 : }
2629 : }
2630 : else {
2631 0 : startContent = rootContent;
2632 0 : tabIndex = forward ? 1 : 0;
2633 : }
2634 : }
2635 : else {
2636 : // no parent, so call the tree owner. This will tell the embedder that
2637 : // it should take the focus.
2638 : bool tookFocus;
2639 0 : docShell->TabToTreeOwner(forward, &tookFocus);
2640 : // if the tree owner, took the focus, blur the current content
2641 0 : if (tookFocus) {
2642 0 : nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(docShell);
2643 0 : if (window->GetFocusedNode() == mFocusedContent)
2644 0 : Blur(mFocusedWindow, nsnull, true, true);
2645 : else
2646 0 : window->SetFocusedNode(nsnull);
2647 0 : return NS_OK;
2648 : }
2649 :
2650 : // reset the tab index and start again from the beginning or end
2651 0 : startContent = rootContent;
2652 0 : tabIndex = forward ? 1 : 0;
2653 : }
2654 :
2655 : // wrapped all the way around and didn't find anything to move the focus
2656 : // to, so just break out
2657 0 : if (startContent == originalStartContent)
2658 : break;
2659 : }
2660 :
2661 0 : return NS_OK;
2662 : }
2663 :
2664 : nsresult
2665 0 : nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
2666 : nsIContent* aRootContent,
2667 : nsIContent* aOriginalStartContent,
2668 : nsIContent* aStartContent,
2669 : bool aForward,
2670 : PRInt32 aCurrentTabIndex,
2671 : bool aIgnoreTabIndex,
2672 : nsIContent** aResultContent)
2673 : {
2674 0 : *aResultContent = nsnull;
2675 :
2676 0 : nsCOMPtr<nsIContent> startContent = aStartContent;
2677 0 : if (!startContent)
2678 0 : return NS_OK;
2679 :
2680 : #ifdef DEBUG_FOCUS_NAVIGATION
2681 : PRINTTAGF("GetNextTabbable: %s", aStartContent);
2682 : printf(" tabindex: %d\n", aCurrentTabIndex);
2683 : #endif
2684 :
2685 0 : nsPresContext* presContext = aPresShell->GetPresContext();
2686 :
2687 0 : bool getNextFrame = true;
2688 0 : nsCOMPtr<nsIContent> iterStartContent = aStartContent;
2689 0 : while (1) {
2690 0 : nsIFrame* startFrame = iterStartContent->GetPrimaryFrame();
2691 : // if there is no frame, look for another content node that has a frame
2692 0 : if (!startFrame) {
2693 : // if the root content doesn't have a frame, just return
2694 0 : if (iterStartContent == aRootContent)
2695 0 : return NS_OK;
2696 :
2697 : // look for the next or previous content node in tree order
2698 0 : iterStartContent = aForward ? iterStartContent->GetNextNode() : iterStartContent->GetPreviousContent();
2699 : // we've already skipped over the initial focused content, so we
2700 : // don't want to traverse frames.
2701 0 : getNextFrame = false;
2702 0 : if (iterStartContent)
2703 0 : continue;
2704 :
2705 : // otherwise, as a last attempt, just look at the root content
2706 0 : iterStartContent = aRootContent;
2707 0 : continue;
2708 : }
2709 :
2710 0 : nsCOMPtr<nsIFrameEnumerator> frameTraversal;
2711 0 : nsresult rv = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
2712 : presContext, startFrame,
2713 : ePreOrder,
2714 : false, // aVisual
2715 : false, // aLockInScrollView
2716 : true // aFollowOOFs
2717 0 : );
2718 0 : NS_ENSURE_SUCCESS(rv, rv);
2719 :
2720 0 : if (iterStartContent == aRootContent) {
2721 0 : if (!aForward) {
2722 0 : frameTraversal->Last();
2723 0 : } else if (aRootContent->IsFocusable()) {
2724 0 : frameTraversal->Next();
2725 : }
2726 : }
2727 0 : else if (getNextFrame &&
2728 0 : (!iterStartContent || iterStartContent->Tag() != nsGkAtoms::area ||
2729 0 : !iterStartContent->IsHTML())) {
2730 : // Need to do special check in case we're in an imagemap which has multiple
2731 : // content nodes per frame, so don't skip over the starting frame.
2732 0 : if (aForward)
2733 0 : frameTraversal->Next();
2734 : else
2735 0 : frameTraversal->Prev();
2736 : }
2737 :
2738 : // Walk frames to find something tabbable matching mCurrentTabIndex
2739 0 : nsIFrame* frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
2740 0 : while (frame) {
2741 : // TabIndex not set defaults to 0 for form elements, anchors and other
2742 : // elements that are normally focusable. Tabindex defaults to -1
2743 : // for elements that are not normally focusable.
2744 : // The returned computed tabindex from IsFocusable() is as follows:
2745 : // < 0 not tabbable at all
2746 : // == 0 in normal tab order (last after positive tabindexed items)
2747 : // > 0 can be tabbed to in the order specified by this value
2748 :
2749 : PRInt32 tabIndex;
2750 0 : frame->IsFocusable(&tabIndex, 0);
2751 :
2752 : #ifdef DEBUG_FOCUS_NAVIGATION
2753 : if (frame->GetContent()) {
2754 : PRINTTAGF("Next Tabbable %s:", frame->GetContent());
2755 : printf(" with tabindex: %d expected: %d\n", tabIndex, aCurrentTabIndex);
2756 : }
2757 : #endif
2758 :
2759 0 : nsIContent* currentContent = frame->GetContent();
2760 0 : if (tabIndex >= 0) {
2761 0 : NS_ASSERTION(currentContent, "IsFocusable set a tabindex for a frame with no content");
2762 0 : if (currentContent->Tag() == nsGkAtoms::img &&
2763 0 : currentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::usemap)) {
2764 : // This is an image with a map. Image map areas are not traversed by
2765 : // nsIFrameTraversal so look for the next or previous area element.
2766 : nsIContent *areaContent =
2767 : GetNextTabbableMapArea(aForward, aCurrentTabIndex,
2768 0 : currentContent, iterStartContent);
2769 0 : if (areaContent) {
2770 0 : NS_ADDREF(*aResultContent = areaContent);
2771 0 : return NS_OK;
2772 : }
2773 : }
2774 0 : else if (aIgnoreTabIndex || aCurrentTabIndex == tabIndex) {
2775 : // break out if we've wrapped around to the start again.
2776 0 : if (aOriginalStartContent && currentContent == aOriginalStartContent) {
2777 0 : NS_ADDREF(*aResultContent = currentContent);
2778 0 : return NS_OK;
2779 : }
2780 :
2781 : // found a node with a matching tab index. Check if it is a child
2782 : // frame. If so, navigate into the child frame instead.
2783 0 : nsIDocument* doc = currentContent->GetCurrentDoc();
2784 0 : NS_ASSERTION(doc, "content not in document");
2785 0 : nsIDocument* subdoc = doc->GetSubDocumentFor(currentContent);
2786 0 : if (subdoc) {
2787 0 : if (!subdoc->EventHandlingSuppressed()) {
2788 0 : if (aForward) {
2789 : // when tabbing forward into a frame, return the root
2790 : // frame so that the canvas becomes focused.
2791 0 : nsCOMPtr<nsPIDOMWindow> subframe = subdoc->GetWindow();
2792 0 : if (subframe) {
2793 : // If the subframe body is editable by contenteditable,
2794 : // we should set the editor's root element rather than the
2795 : // actual root element. Otherwise, we should set the focus
2796 : // to the root content.
2797 : *aResultContent =
2798 0 : nsLayoutUtils::GetEditableRootContentByContentEditable(subdoc);
2799 0 : if (!*aResultContent ||
2800 0 : !((*aResultContent)->GetPrimaryFrame())) {
2801 : *aResultContent =
2802 0 : GetRootForFocus(subframe, subdoc, false, true);
2803 : }
2804 0 : if (*aResultContent) {
2805 0 : NS_ADDREF(*aResultContent);
2806 0 : return NS_OK;
2807 : }
2808 : }
2809 : }
2810 0 : Element* rootElement = subdoc->GetRootElement();
2811 0 : nsIPresShell* subShell = subdoc->GetShell();
2812 0 : if (rootElement && subShell) {
2813 : rv = GetNextTabbableContent(subShell, rootElement,
2814 : aOriginalStartContent, rootElement,
2815 : aForward, (aForward ? 1 : 0),
2816 0 : false, aResultContent);
2817 0 : NS_ENSURE_SUCCESS(rv, rv);
2818 0 : if (*aResultContent)
2819 0 : return NS_OK;
2820 : }
2821 : }
2822 : }
2823 : // otherwise, use this as the next content node to tab to, unless
2824 : // this was the element we started on. This would happen for
2825 : // instance on an element with child frames, where frame navigation
2826 : // could return the original element again. In that case, just skip
2827 : // it. Also, if the next content node is the root content, then
2828 : // return it. This latter case would happen only if someone made a
2829 : // popup focusable.
2830 : // Also, when going backwards, check to ensure that the focus
2831 : // wouldn't be redirected. Otherwise, for example, when an input in
2832 : // a textbox is focused, the enclosing textbox would be found and
2833 : // the same inner input would be returned again.
2834 0 : else if (currentContent == aRootContent ||
2835 0 : (currentContent != startContent &&
2836 0 : (aForward || !GetRedirectedFocus(currentContent)))) {
2837 0 : NS_ADDREF(*aResultContent = currentContent);
2838 0 : return NS_OK;
2839 : }
2840 : }
2841 : }
2842 0 : else if (aOriginalStartContent && currentContent == aOriginalStartContent) {
2843 : // not focusable, so return if we have wrapped around to the original
2844 : // content. This is necessary in case the original starting content was
2845 : // not focusable.
2846 0 : NS_ADDREF(*aResultContent = currentContent);
2847 0 : return NS_OK;
2848 : }
2849 :
2850 : // Move to the next or previous frame, but ignore continuation frames
2851 : // since only the first frame should be involved in focusability.
2852 : // Otherwise, a loop will occur in the following example:
2853 : // <span tabindex="1">...<a/><a/>...</span>
2854 : // where the text wraps onto multiple lines. Tabbing from the second
2855 : // link can find one of the span's continuation frames between the link
2856 : // and the end of the span, and the span would end up getting focused
2857 : // again.
2858 0 : do {
2859 0 : if (aForward)
2860 0 : frameTraversal->Next();
2861 : else
2862 0 : frameTraversal->Prev();
2863 0 : frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
2864 0 : } while (frame && frame->GetPrevContinuation());
2865 : }
2866 :
2867 : // If already at lowest priority tab (0), end search completely.
2868 : // A bit counterintuitive but true, tabindex order goes 1, 2, ... 32767, 0
2869 0 : if (aCurrentTabIndex == (aForward ? 0 : 1)) {
2870 : // if going backwards, the canvas should be focused once the beginning
2871 : // has been reached.
2872 0 : if (!aForward) {
2873 0 : nsCOMPtr<nsPIDOMWindow> window = GetCurrentWindow(aRootContent);
2874 0 : NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
2875 0 : NS_IF_ADDREF(*aResultContent =
2876 0 : GetRootForFocus(window, aRootContent->GetCurrentDoc(), false, true));
2877 : }
2878 : break;
2879 : }
2880 :
2881 : // continue looking for next highest priority tabindex
2882 0 : aCurrentTabIndex = GetNextTabIndex(aRootContent, aCurrentTabIndex, aForward);
2883 0 : startContent = iterStartContent = aRootContent;
2884 : }
2885 :
2886 0 : return NS_OK;
2887 : }
2888 :
2889 : nsIContent*
2890 0 : nsFocusManager::GetNextTabbableMapArea(bool aForward,
2891 : PRInt32 aCurrentTabIndex,
2892 : nsIContent* aImageContent,
2893 : nsIContent* aStartContent)
2894 : {
2895 0 : nsAutoString useMap;
2896 0 : aImageContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usemap, useMap);
2897 :
2898 0 : nsCOMPtr<nsIDocument> doc = aImageContent->GetDocument();
2899 0 : if (doc) {
2900 0 : nsCOMPtr<nsIContent> mapContent = doc->FindImageMap(useMap);
2901 0 : if (!mapContent)
2902 0 : return nsnull;
2903 0 : PRUint32 count = mapContent->GetChildCount();
2904 : // First see if the the start content is in this map
2905 :
2906 0 : PRInt32 index = mapContent->IndexOf(aStartContent);
2907 : PRInt32 tabIndex;
2908 0 : if (index < 0 || (aStartContent->IsFocusable(&tabIndex) &&
2909 : tabIndex != aCurrentTabIndex)) {
2910 : // If aStartContent is in this map we must start iterating past it.
2911 : // We skip the case where aStartContent has tabindex == aStartContent
2912 : // since the next tab ordered element might be before it
2913 : // (or after for backwards) in the child list.
2914 0 : index = aForward ? -1 : (PRInt32)count;
2915 : }
2916 :
2917 : // GetChildAt will return nsnull if our index < 0 or index >= count
2918 0 : nsCOMPtr<nsIContent> areaContent;
2919 0 : while ((areaContent = mapContent->GetChildAt(aForward ? ++index : --index)) != nsnull) {
2920 0 : if (areaContent->IsFocusable(&tabIndex) && tabIndex == aCurrentTabIndex) {
2921 0 : return areaContent;
2922 : }
2923 : }
2924 : }
2925 :
2926 0 : return nsnull;
2927 : }
2928 :
2929 : PRInt32
2930 0 : nsFocusManager::GetNextTabIndex(nsIContent* aParent,
2931 : PRInt32 aCurrentTabIndex,
2932 : bool aForward)
2933 : {
2934 : PRInt32 tabIndex, childTabIndex;
2935 :
2936 0 : if (aForward) {
2937 0 : tabIndex = 0;
2938 0 : for (nsIContent* child = aParent->GetFirstChild();
2939 : child;
2940 0 : child = child->GetNextSibling()) {
2941 0 : childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
2942 0 : if (childTabIndex > aCurrentTabIndex && childTabIndex != tabIndex) {
2943 0 : tabIndex = (tabIndex == 0 || childTabIndex < tabIndex) ? childTabIndex : tabIndex;
2944 : }
2945 :
2946 0 : nsAutoString tabIndexStr;
2947 0 : child->GetAttr(kNameSpaceID_None, nsGkAtoms::tabindex, tabIndexStr);
2948 0 : PRInt32 ec, val = tabIndexStr.ToInteger(&ec);
2949 0 : if (NS_SUCCEEDED (ec) && val > aCurrentTabIndex && val != tabIndex) {
2950 0 : tabIndex = (tabIndex == 0 || val < tabIndex) ? val : tabIndex;
2951 : }
2952 : }
2953 : }
2954 : else { /* !aForward */
2955 0 : tabIndex = 1;
2956 0 : for (nsIContent* child = aParent->GetFirstChild();
2957 : child;
2958 0 : child = child->GetNextSibling()) {
2959 0 : childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
2960 0 : if ((aCurrentTabIndex == 0 && childTabIndex > tabIndex) ||
2961 : (childTabIndex < aCurrentTabIndex && childTabIndex > tabIndex)) {
2962 0 : tabIndex = childTabIndex;
2963 : }
2964 :
2965 0 : nsAutoString tabIndexStr;
2966 0 : child->GetAttr(kNameSpaceID_None, nsGkAtoms::tabindex, tabIndexStr);
2967 0 : PRInt32 ec, val = tabIndexStr.ToInteger(&ec);
2968 0 : if (NS_SUCCEEDED (ec)) {
2969 0 : if ((aCurrentTabIndex == 0 && val > tabIndex) ||
2970 : (val < aCurrentTabIndex && val > tabIndex) ) {
2971 0 : tabIndex = val;
2972 : }
2973 : }
2974 : }
2975 : }
2976 :
2977 0 : return tabIndex;
2978 : }
2979 :
2980 : nsIContent*
2981 0 : nsFocusManager::GetRootForFocus(nsPIDOMWindow* aWindow,
2982 : nsIDocument* aDocument,
2983 : bool aIsForDocNavigation,
2984 : bool aCheckVisibility)
2985 : {
2986 : // the root element's canvas may be focused as long as the document is in a
2987 : // a non-chrome shell and does not contain a frameset.
2988 0 : if (aIsForDocNavigation) {
2989 : nsCOMPtr<nsIContent> docContent =
2990 0 : do_QueryInterface(aWindow->GetFrameElementInternal());
2991 : // document navigation skips iframes and frames that are specifically non-focusable
2992 0 : if (docContent) {
2993 0 : if (docContent->Tag() == nsGkAtoms::iframe)
2994 0 : return nsnull;
2995 :
2996 0 : nsIFrame* frame = docContent->GetPrimaryFrame();
2997 0 : if (!frame || !frame->IsFocusable(nsnull, 0))
2998 0 : return nsnull;
2999 : }
3000 : }
3001 : else {
3002 : PRInt32 itemType;
3003 0 : nsCOMPtr<nsIDocShellTreeItem> shellItem = do_QueryInterface(aWindow->GetDocShell());
3004 0 : shellItem->GetItemType(&itemType);
3005 :
3006 0 : if (itemType == nsIDocShellTreeItem::typeChrome)
3007 0 : return nsnull;
3008 : }
3009 :
3010 0 : if (aCheckVisibility && !IsWindowVisible(aWindow))
3011 0 : return nsnull;
3012 :
3013 0 : Element *rootElement = aDocument->GetRootElement();
3014 0 : if (!rootElement) {
3015 0 : return nsnull;
3016 : }
3017 :
3018 0 : if (aCheckVisibility && !rootElement->GetPrimaryFrame()) {
3019 0 : return nsnull;
3020 : }
3021 :
3022 : // Finally, check if this is a frameset
3023 0 : nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(aDocument);
3024 0 : if (htmlDoc && aDocument->GetHtmlChildElement(nsGkAtoms::frameset)) {
3025 0 : return nsnull;
3026 : }
3027 :
3028 0 : return rootElement;
3029 : }
3030 :
3031 : TabParent*
3032 0 : nsFocusManager::GetRemoteForContent(nsIContent* aContent) {
3033 0 : if (!aContent ||
3034 0 : (aContent->Tag() != nsGkAtoms::browser &&
3035 0 : aContent->Tag() != nsGkAtoms::iframe) ||
3036 0 : !aContent->IsXUL() ||
3037 : !aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::Remote,
3038 0 : nsGkAtoms::_true, eIgnoreCase))
3039 0 : return nsnull;
3040 :
3041 0 : nsCOMPtr<nsIFrameLoaderOwner> loaderOwner = do_QueryInterface(aContent);
3042 0 : if (!loaderOwner)
3043 0 : return nsnull;
3044 :
3045 0 : nsRefPtr<nsFrameLoader> frameLoader = loaderOwner->GetFrameLoader();
3046 0 : if (!frameLoader)
3047 0 : return nsnull;
3048 :
3049 0 : PBrowserParent* remoteBrowser = frameLoader->GetRemoteBrowser();
3050 0 : TabParent* remote = static_cast<TabParent*>(remoteBrowser);
3051 0 : return remote;
3052 : }
3053 :
3054 : void
3055 0 : nsFocusManager::GetLastDocShell(nsIDocShellTreeItem* aItem,
3056 : nsIDocShellTreeItem** aResult)
3057 : {
3058 0 : *aResult = nsnull;
3059 :
3060 0 : nsCOMPtr<nsIDocShellTreeItem> curItem = aItem;
3061 0 : while (curItem) {
3062 0 : PRInt32 childCount = 0;
3063 0 : curItem->GetChildCount(&childCount);
3064 0 : if (!childCount) {
3065 0 : *aResult = curItem;
3066 0 : NS_ADDREF(*aResult);
3067 : return;
3068 : }
3069 :
3070 :
3071 0 : curItem->GetChildAt(childCount - 1, getter_AddRefs(curItem));
3072 : }
3073 : }
3074 :
3075 : void
3076 0 : nsFocusManager::GetNextDocShell(nsIDocShellTreeItem* aItem,
3077 : nsIDocShellTreeItem** aResult)
3078 : {
3079 0 : *aResult = nsnull;
3080 :
3081 0 : PRInt32 childCount = 0;
3082 0 : aItem->GetChildCount(&childCount);
3083 0 : if (childCount) {
3084 0 : aItem->GetChildAt(0, aResult);
3085 0 : if (*aResult)
3086 0 : return;
3087 : }
3088 :
3089 0 : nsCOMPtr<nsIDocShellTreeItem> curItem = aItem;
3090 0 : while (curItem) {
3091 0 : nsCOMPtr<nsIDocShellTreeItem> parentItem;
3092 0 : curItem->GetParent(getter_AddRefs(parentItem));
3093 0 : if (!parentItem)
3094 : return;
3095 :
3096 : // Note that we avoid using GetChildOffset() here because docshell
3097 : // child offsets can't be trusted to be correct. bug 162283.
3098 0 : nsCOMPtr<nsIDocShellTreeItem> iterItem;
3099 0 : childCount = 0;
3100 0 : parentItem->GetChildCount(&childCount);
3101 0 : for (PRInt32 index = 0; index < childCount; ++index) {
3102 0 : parentItem->GetChildAt(index, getter_AddRefs(iterItem));
3103 0 : if (iterItem == curItem) {
3104 0 : ++index;
3105 0 : if (index < childCount) {
3106 0 : parentItem->GetChildAt(index, aResult);
3107 0 : if (*aResult)
3108 : return;
3109 : }
3110 0 : break;
3111 : }
3112 : }
3113 :
3114 0 : curItem = parentItem;
3115 : }
3116 : }
3117 :
3118 : void
3119 0 : nsFocusManager::GetPreviousDocShell(nsIDocShellTreeItem* aItem,
3120 : nsIDocShellTreeItem** aResult)
3121 : {
3122 0 : *aResult = nsnull;
3123 :
3124 0 : nsCOMPtr<nsIDocShellTreeItem> parentItem;
3125 0 : aItem->GetParent(getter_AddRefs(parentItem));
3126 0 : if (!parentItem)
3127 : return;
3128 :
3129 : // Note that we avoid using GetChildOffset() here because docshell
3130 : // child offsets can't be trusted to be correct. bug 162283.
3131 0 : PRInt32 childCount = 0;
3132 0 : parentItem->GetChildCount(&childCount);
3133 0 : nsCOMPtr<nsIDocShellTreeItem> prevItem, iterItem;
3134 0 : for (PRInt32 index = 0; index < childCount; ++index) {
3135 0 : parentItem->GetChildAt(index, getter_AddRefs(iterItem));
3136 0 : if (iterItem == aItem)
3137 0 : break;
3138 0 : prevItem = iterItem;
3139 : }
3140 :
3141 0 : if (prevItem)
3142 0 : GetLastDocShell(prevItem, aResult);
3143 : else
3144 0 : NS_ADDREF(*aResult = parentItem);
3145 : }
3146 :
3147 : nsIContent*
3148 0 : nsFocusManager::GetNextTabbablePanel(nsIDocument* aDocument, nsIFrame* aCurrentPopup, bool aForward)
3149 : {
3150 0 : nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
3151 0 : if (!pm)
3152 0 : return nsnull;
3153 :
3154 : // Iterate through the array backwards if aForward is false.
3155 0 : nsTArray<nsIFrame *> popups = pm->GetVisiblePopups();
3156 0 : PRInt32 i = aForward ? 0 : popups.Length() - 1;
3157 0 : PRInt32 end = aForward ? popups.Length() : -1;
3158 :
3159 0 : for (; i != end; aForward ? i++ : i--) {
3160 0 : nsIFrame* popupFrame = popups[i];
3161 0 : if (aCurrentPopup) {
3162 : // If the current popup is set, then we need to skip over this popup and
3163 : // wait until the currently focused popup is found. Once found, the
3164 : // current popup will be cleared so that the next popup is used.
3165 0 : if (aCurrentPopup == popupFrame)
3166 0 : aCurrentPopup = nsnull;
3167 0 : continue;
3168 : }
3169 :
3170 : // Skip over non-panels
3171 0 : if (popupFrame->GetContent()->Tag() != nsGkAtoms::panel ||
3172 0 : (aDocument && popupFrame->GetContent()->GetCurrentDoc() != aDocument)) {
3173 0 : continue;
3174 : }
3175 :
3176 : // Find the first focusable content within the popup. If there isn't any
3177 : // focusable content in the popup, skip to the next popup.
3178 0 : nsIPresShell* presShell = popupFrame->PresContext()->GetPresShell();
3179 0 : if (presShell) {
3180 0 : nsCOMPtr<nsIContent> nextFocus;
3181 0 : nsIContent* popup = popupFrame->GetContent();
3182 : nsresult rv = GetNextTabbableContent(presShell, popup,
3183 : nsnull, popup,
3184 : true, 1, false,
3185 0 : getter_AddRefs(nextFocus));
3186 0 : if (NS_SUCCEEDED(rv) && nextFocus) {
3187 0 : return nextFocus.get();
3188 : }
3189 : }
3190 : }
3191 :
3192 0 : return nsnull;
3193 : }
3194 :
3195 : nsIContent*
3196 0 : nsFocusManager::GetNextTabbableDocument(nsIContent* aStartContent, bool aForward)
3197 : {
3198 : // If currentPopup is set, then the starting content is in a panel.
3199 0 : nsIFrame* currentPopup = nsnull;
3200 0 : nsCOMPtr<nsIDocument> doc;
3201 0 : nsCOMPtr<nsIDocShellTreeItem> startItem;
3202 :
3203 0 : if (aStartContent) {
3204 0 : doc = aStartContent->GetCurrentDoc();
3205 0 : if (doc) {
3206 0 : startItem = do_QueryInterface(doc->GetWindow()->GetDocShell());
3207 : }
3208 :
3209 : // Check if the starting content is inside a panel. Document navigation
3210 : // must start from this panel instead of the document root.
3211 0 : nsIContent* content = aStartContent;
3212 0 : while (content) {
3213 0 : if (content->NodeInfo()->Equals(nsGkAtoms::panel, kNameSpaceID_XUL)) {
3214 0 : currentPopup = content->GetPrimaryFrame();
3215 0 : break;
3216 : }
3217 0 : content = content->GetParent();
3218 : }
3219 : }
3220 0 : else if (mFocusedWindow) {
3221 0 : startItem = do_QueryInterface(mFocusedWindow->GetDocShell());
3222 0 : doc = do_QueryInterface(mFocusedWindow->GetExtantDocument());
3223 : }
3224 : else {
3225 0 : nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(mActiveWindow);
3226 0 : startItem = do_QueryInterface(webnav);
3227 :
3228 0 : if (mActiveWindow) {
3229 0 : doc = do_QueryInterface(mActiveWindow->GetExtantDocument());
3230 : }
3231 : }
3232 :
3233 0 : if (!startItem)
3234 0 : return nsnull;
3235 :
3236 : // perform a depth first search (preorder) of the docshell tree
3237 : // looking for an HTML Frame or a chrome document
3238 0 : nsIContent* content = aStartContent;
3239 0 : nsCOMPtr<nsIDocShellTreeItem> curItem = startItem;
3240 0 : nsCOMPtr<nsIDocShellTreeItem> nextItem;
3241 0 : do {
3242 : // If moving forward, check for a panel in the starting document. If one
3243 : // exists with focusable content, return that content instead of the next
3244 : // document. If currentPopup is set, then, another panel may exist. If no
3245 : // such panel exists, then continue on to check the next document.
3246 : // When moving backwards, and the starting content is in a panel, then
3247 : // check for additional panels in the starting document. If the starting
3248 : // content is not in a panel, move back to the previous document and check
3249 : // for panels there.
3250 :
3251 0 : bool checkPopups = false;
3252 0 : nsCOMPtr<nsPIDOMWindow> nextFrame = nsnull;
3253 :
3254 0 : if (doc && (aForward || currentPopup)) {
3255 0 : nsIContent* popupContent = GetNextTabbablePanel(doc, currentPopup, aForward);
3256 0 : if (popupContent)
3257 0 : return popupContent;
3258 :
3259 0 : if (!aForward && currentPopup) {
3260 : // The starting content was in a popup, yet no other popups were
3261 : // found. Move onto the starting content's document.
3262 0 : nextFrame = doc->GetWindow();
3263 : }
3264 : }
3265 :
3266 : // Look for the next or previous document.
3267 0 : if (!nextFrame) {
3268 0 : if (aForward) {
3269 0 : GetNextDocShell(curItem, getter_AddRefs(nextItem));
3270 0 : if (!nextItem) {
3271 : // wrap around to the beginning, which is the top of the tree
3272 0 : startItem->GetRootTreeItem(getter_AddRefs(nextItem));
3273 : }
3274 : }
3275 : else {
3276 0 : GetPreviousDocShell(curItem, getter_AddRefs(nextItem));
3277 0 : if (!nextItem) {
3278 : // wrap around to the end, which is the last item in the tree
3279 0 : nsCOMPtr<nsIDocShellTreeItem> rootItem;
3280 0 : startItem->GetRootTreeItem(getter_AddRefs(rootItem));
3281 0 : GetLastDocShell(rootItem, getter_AddRefs(nextItem));
3282 : }
3283 :
3284 : // When going back to the previous document, check for any focusable
3285 : // popups in that previous document first.
3286 0 : checkPopups = true;
3287 : }
3288 :
3289 0 : curItem = nextItem;
3290 0 : nextFrame = do_GetInterface(nextItem);
3291 : }
3292 :
3293 0 : if (!nextFrame)
3294 0 : return nsnull;
3295 :
3296 : // Clear currentPopup for the next iteration
3297 0 : currentPopup = nsnull;
3298 :
3299 : // If event handling is suppressed, move on to the next document. Set
3300 : // content to null so that the popup check will be skipped on the next
3301 : // loop iteration.
3302 0 : doc = do_QueryInterface(nextFrame->GetExtantDocument());
3303 0 : if (!doc || doc->EventHandlingSuppressed()) {
3304 0 : content = nsnull;
3305 0 : continue;
3306 : }
3307 :
3308 0 : if (checkPopups) {
3309 : // When iterating backwards, check the panels of the previous document
3310 : // first. If a panel exists that has focusable content, focus that.
3311 : // Otherwise, continue on to focus the document.
3312 0 : nsIContent* popupContent = GetNextTabbablePanel(doc, nsnull, false);
3313 0 : if (popupContent)
3314 0 : return popupContent;
3315 : }
3316 :
3317 0 : content = GetRootForFocus(nextFrame, doc, true, true);
3318 0 : if (content && !GetRootForFocus(nextFrame, doc, false, false)) {
3319 : // if the found content is in a chrome shell or a frameset, navigate
3320 : // forward one tabbable item so that the first item is focused. Note
3321 : // that we always go forward and not back here.
3322 0 : nsCOMPtr<nsIContent> nextFocus;
3323 0 : Element* rootElement = doc->GetRootElement();
3324 0 : nsIPresShell* presShell = doc->GetShell();
3325 0 : if (presShell) {
3326 : nsresult rv = GetNextTabbableContent(presShell, rootElement,
3327 : nsnull, rootElement,
3328 : true, 1, false,
3329 0 : getter_AddRefs(nextFocus));
3330 0 : return NS_SUCCEEDED(rv) ? nextFocus.get() : nsnull;
3331 : }
3332 : }
3333 :
3334 0 : } while (!content);
3335 :
3336 0 : return content;
3337 : }
3338 :
3339 : void
3340 0 : nsFocusManager::GetFocusInSelection(nsPIDOMWindow* aWindow,
3341 : nsIContent* aStartSelection,
3342 : nsIContent* aEndSelection,
3343 : nsIContent** aFocusedContent)
3344 : {
3345 0 : *aFocusedContent = nsnull;
3346 :
3347 0 : nsCOMPtr<nsIContent> testContent = aStartSelection;
3348 0 : nsCOMPtr<nsIContent> nextTestContent = aEndSelection;
3349 :
3350 0 : nsCOMPtr<nsIContent> currentFocus = aWindow->GetFocusedNode();
3351 :
3352 : // We now have the correct start node in selectionContent!
3353 : // Search for focusable elements, starting with selectionContent
3354 :
3355 : // Method #1: Keep going up while we look - an ancestor might be focusable
3356 : // We could end the loop earlier, such as when we're no longer
3357 : // in the same frame, by comparing selectionContent->GetPrimaryFrame()
3358 : // with a variable holding the starting selectionContent
3359 0 : while (testContent) {
3360 : // Keep testing while selectionContent is equal to something,
3361 : // eventually we'll run out of ancestors
3362 :
3363 0 : nsCOMPtr<nsIURI> uri;
3364 0 : if (testContent == currentFocus ||
3365 0 : testContent->IsLink(getter_AddRefs(uri))) {
3366 0 : NS_ADDREF(*aFocusedContent = testContent);
3367 : return;
3368 : }
3369 :
3370 : // Get the parent
3371 0 : testContent = testContent->GetParent();
3372 :
3373 0 : if (!testContent) {
3374 : // We run this loop again, checking the ancestor chain of the selection's end point
3375 0 : testContent = nextTestContent;
3376 0 : nextTestContent = nsnull;
3377 : }
3378 : }
3379 :
3380 : // We couldn't find an anchor that was an ancestor of the selection start
3381 : // Method #2: look for anchor in selection's primary range (depth first search)
3382 :
3383 : // Turn into nodes so that we can use GetNextSibling() and GetFirstChild()
3384 0 : nsCOMPtr<nsIDOMNode> selectionNode(do_QueryInterface(aStartSelection));
3385 0 : nsCOMPtr<nsIDOMNode> endSelectionNode(do_QueryInterface(aEndSelection));
3386 0 : nsCOMPtr<nsIDOMNode> testNode;
3387 :
3388 0 : do {
3389 0 : testContent = do_QueryInterface(selectionNode);
3390 :
3391 : // We're looking for any focusable link that could be part of the
3392 : // main document's selection.
3393 0 : nsCOMPtr<nsIURI> uri;
3394 0 : if (testContent == currentFocus ||
3395 0 : testContent->IsLink(getter_AddRefs(uri))) {
3396 0 : NS_ADDREF(*aFocusedContent = testContent);
3397 : return;
3398 : }
3399 :
3400 0 : selectionNode->GetFirstChild(getter_AddRefs(testNode));
3401 0 : if (testNode) {
3402 0 : selectionNode = testNode;
3403 0 : continue;
3404 : }
3405 :
3406 0 : if (selectionNode == endSelectionNode)
3407 : break;
3408 0 : selectionNode->GetNextSibling(getter_AddRefs(testNode));
3409 0 : if (testNode) {
3410 0 : selectionNode = testNode;
3411 0 : continue;
3412 : }
3413 :
3414 0 : do {
3415 0 : selectionNode->GetParentNode(getter_AddRefs(testNode));
3416 0 : if (!testNode || testNode == endSelectionNode) {
3417 0 : selectionNode = nsnull;
3418 0 : break;
3419 : }
3420 0 : testNode->GetNextSibling(getter_AddRefs(selectionNode));
3421 0 : if (selectionNode)
3422 0 : break;
3423 0 : selectionNode = testNode;
3424 : } while (true);
3425 : }
3426 0 : while (selectionNode && selectionNode != endSelectionNode);
3427 : }
3428 :
3429 : nsresult
3430 2 : NS_NewFocusManager(nsIFocusManager** aResult)
3431 : {
3432 2 : NS_IF_ADDREF(*aResult = nsFocusManager::GetFocusManager());
3433 2 : return NS_OK;
3434 4392 : }
|