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