michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/dom/TabParent.h" michael@0: michael@0: #include "nsFocusManager.h" michael@0: michael@0: #include "nsIInterfaceRequestorUtils.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIDOMWindow.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsIDOMElement.h" michael@0: #include "nsIDOMDocument.h" michael@0: #include "nsIDOMRange.h" michael@0: #include "nsIHTMLDocument.h" michael@0: #include "nsIDocShell.h" michael@0: #include "nsIDocShellTreeOwner.h" michael@0: #include "nsLayoutUtils.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsFrameTraversal.h" michael@0: #include "nsIWebNavigation.h" michael@0: #include "nsCaret.h" michael@0: #include "nsIBaseWindow.h" michael@0: #include "nsViewManager.h" michael@0: #include "nsFrameSelection.h" michael@0: #include "mozilla/dom/Selection.h" michael@0: #include "nsXULPopupManager.h" michael@0: #include "nsIScriptObjectPrincipal.h" michael@0: #include "nsIPrincipal.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsIObjectFrame.h" michael@0: #include "nsBindingManager.h" michael@0: #include "nsStyleCoord.h" michael@0: michael@0: #include "mozilla/ContentEvents.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "mozilla/EventDispatcher.h" michael@0: #include "mozilla/EventStateManager.h" michael@0: #include "mozilla/EventStates.h" michael@0: #include "mozilla/IMEStateManager.h" michael@0: #include "mozilla/LookAndFeel.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/Services.h" michael@0: #include michael@0: michael@0: #ifdef MOZ_XUL michael@0: #include "nsIDOMXULTextboxElement.h" michael@0: #include "nsIDOMXULMenuListElement.h" michael@0: #endif michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: #include "nsAccessibilityService.h" michael@0: #endif michael@0: michael@0: #ifndef XP_MACOSX michael@0: #include "nsIScriptError.h" michael@0: #endif michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: using namespace mozilla::widget; michael@0: michael@0: #ifdef PR_LOGGING michael@0: michael@0: // Two types of focus pr logging are available: michael@0: // 'Focus' for normal focus manager calls michael@0: // 'FocusNavigation' for tab and document navigation michael@0: PRLogModuleInfo* gFocusLog; michael@0: PRLogModuleInfo* gFocusNavigationLog; michael@0: michael@0: #define LOGFOCUS(args) PR_LOG(gFocusLog, 4, args) michael@0: #define LOGFOCUSNAVIGATION(args) PR_LOG(gFocusNavigationLog, 4, args) michael@0: michael@0: #define LOGTAG(log, format, content) \ michael@0: { \ michael@0: nsAutoCString tag(NS_LITERAL_CSTRING("(none)")); \ michael@0: if (content) { \ michael@0: content->Tag()->ToUTF8String(tag); \ michael@0: } \ michael@0: PR_LOG(log, 4, (format, tag.get())); \ michael@0: } michael@0: michael@0: #define LOGCONTENT(format, content) LOGTAG(gFocusLog, format, content) michael@0: #define LOGCONTENTNAVIGATION(format, content) LOGTAG(gFocusNavigationLog, format, content) michael@0: michael@0: #else michael@0: michael@0: #define LOGFOCUS(args) michael@0: #define LOGFOCUSNAVIGATION(args) michael@0: #define LOGCONTENT(format, content) michael@0: #define LOGCONTENTNAVIGATION(format, content) michael@0: michael@0: #endif michael@0: michael@0: struct nsDelayedBlurOrFocusEvent michael@0: { michael@0: nsDelayedBlurOrFocusEvent(uint32_t aType, michael@0: nsIPresShell* aPresShell, michael@0: nsIDocument* aDocument, michael@0: EventTarget* aTarget) michael@0: : mType(aType), michael@0: mPresShell(aPresShell), michael@0: mDocument(aDocument), michael@0: mTarget(aTarget) { } michael@0: michael@0: nsDelayedBlurOrFocusEvent(const nsDelayedBlurOrFocusEvent& aOther) michael@0: : mType(aOther.mType), michael@0: mPresShell(aOther.mPresShell), michael@0: mDocument(aOther.mDocument), michael@0: mTarget(aOther.mTarget) { } michael@0: michael@0: uint32_t mType; michael@0: nsCOMPtr mPresShell; michael@0: nsCOMPtr mDocument; michael@0: nsCOMPtr mTarget; michael@0: }; michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFocusManager) michael@0: NS_INTERFACE_MAP_ENTRY(nsIFocusManager) michael@0: NS_INTERFACE_MAP_ENTRY(nsIObserver) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIFocusManager) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFocusManager) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFocusManager) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION(nsFocusManager, michael@0: mActiveWindow, michael@0: mFocusedWindow, michael@0: mFocusedContent, michael@0: mFirstBlurEvent, michael@0: mFirstFocusEvent, michael@0: mWindowBeingLowered) michael@0: michael@0: nsFocusManager* nsFocusManager::sInstance = nullptr; michael@0: bool nsFocusManager::sMouseFocusesFormControl = false; michael@0: bool nsFocusManager::sTestMode = false; michael@0: michael@0: static const char* kObservedPrefs[] = { michael@0: "accessibility.browsewithcaret", michael@0: "accessibility.tabfocus_applies_to_xul", michael@0: "accessibility.mouse_focuses_formcontrol", michael@0: "focusmanager.testmode", michael@0: nullptr michael@0: }; michael@0: michael@0: nsFocusManager::nsFocusManager() michael@0: { } michael@0: michael@0: nsFocusManager::~nsFocusManager() michael@0: { michael@0: Preferences::RemoveObservers(this, kObservedPrefs); michael@0: michael@0: nsCOMPtr obs = mozilla::services::GetObserverService(); michael@0: if (obs) { michael@0: obs->RemoveObserver(this, "xpcom-shutdown"); michael@0: } michael@0: } michael@0: michael@0: // static michael@0: nsresult michael@0: nsFocusManager::Init() michael@0: { michael@0: nsFocusManager* fm = new nsFocusManager(); michael@0: NS_ENSURE_TRUE(fm, NS_ERROR_OUT_OF_MEMORY); michael@0: NS_ADDREF(fm); michael@0: sInstance = fm; michael@0: michael@0: #ifdef PR_LOGGING michael@0: gFocusLog = PR_NewLogModule("Focus"); michael@0: gFocusNavigationLog = PR_NewLogModule("FocusNavigation"); michael@0: #endif michael@0: michael@0: nsIContent::sTabFocusModelAppliesToXUL = michael@0: Preferences::GetBool("accessibility.tabfocus_applies_to_xul", michael@0: nsIContent::sTabFocusModelAppliesToXUL); michael@0: michael@0: sMouseFocusesFormControl = michael@0: Preferences::GetBool("accessibility.mouse_focuses_formcontrol", false); michael@0: michael@0: sTestMode = Preferences::GetBool("focusmanager.testmode", false); michael@0: michael@0: Preferences::AddWeakObservers(fm, kObservedPrefs); michael@0: michael@0: nsCOMPtr obs = mozilla::services::GetObserverService(); michael@0: if (obs) { michael@0: obs->AddObserver(fm, "xpcom-shutdown", true); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // static michael@0: void michael@0: nsFocusManager::Shutdown() michael@0: { michael@0: NS_IF_RELEASE(sInstance); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFocusManager::Observe(nsISupports *aSubject, michael@0: const char *aTopic, michael@0: const char16_t *aData) michael@0: { michael@0: if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { michael@0: nsDependentString data(aData); michael@0: if (data.EqualsLiteral("accessibility.browsewithcaret")) { michael@0: UpdateCaretForCaretBrowsingMode(); michael@0: } michael@0: else if (data.EqualsLiteral("accessibility.tabfocus_applies_to_xul")) { michael@0: nsIContent::sTabFocusModelAppliesToXUL = michael@0: Preferences::GetBool("accessibility.tabfocus_applies_to_xul", michael@0: nsIContent::sTabFocusModelAppliesToXUL); michael@0: } michael@0: else if (data.EqualsLiteral("accessibility.mouse_focuses_formcontrol")) { michael@0: sMouseFocusesFormControl = michael@0: Preferences::GetBool("accessibility.mouse_focuses_formcontrol", michael@0: false); michael@0: } michael@0: else if (data.EqualsLiteral("focusmanager.testmode")) { michael@0: sTestMode = Preferences::GetBool("focusmanager.testmode", false); michael@0: } michael@0: } else if (!nsCRT::strcmp(aTopic, "xpcom-shutdown")) { michael@0: mActiveWindow = nullptr; michael@0: mFocusedWindow = nullptr; michael@0: mFocusedContent = nullptr; michael@0: mFirstBlurEvent = nullptr; michael@0: mFirstFocusEvent = nullptr; michael@0: mWindowBeingLowered = nullptr; michael@0: mDelayedBlurFocusEvents.Clear(); michael@0: mMouseDownEventHandlingDocument = nullptr; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // given a frame content node, retrieve the nsIDOMWindow displayed in it michael@0: static nsPIDOMWindow* michael@0: GetContentWindow(nsIContent* aContent) michael@0: { michael@0: nsIDocument* doc = aContent->GetCurrentDoc(); michael@0: if (doc) { michael@0: nsIDocument* subdoc = doc->GetSubDocumentFor(aContent); michael@0: if (subdoc) michael@0: return subdoc->GetWindow(); michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: // get the current window for the given content node michael@0: static nsPIDOMWindow* michael@0: GetCurrentWindow(nsIContent* aContent) michael@0: { michael@0: nsIDocument *doc = aContent->GetCurrentDoc(); michael@0: return doc ? doc->GetWindow() : nullptr; michael@0: } michael@0: michael@0: // static michael@0: nsIContent* michael@0: nsFocusManager::GetFocusedDescendant(nsPIDOMWindow* aWindow, bool aDeep, michael@0: nsPIDOMWindow** aFocusedWindow) michael@0: { michael@0: NS_ENSURE_TRUE(aWindow, nullptr); michael@0: michael@0: *aFocusedWindow = nullptr; michael@0: michael@0: nsIContent* currentContent = nullptr; michael@0: nsPIDOMWindow* window = aWindow->GetOuterWindow(); michael@0: while (window) { michael@0: *aFocusedWindow = window; michael@0: currentContent = window->GetFocusedNode(); michael@0: if (!currentContent || !aDeep) michael@0: break; michael@0: michael@0: window = GetContentWindow(currentContent); michael@0: } michael@0: michael@0: NS_IF_ADDREF(*aFocusedWindow); michael@0: michael@0: return currentContent; michael@0: } michael@0: michael@0: // static michael@0: nsIContent* michael@0: nsFocusManager::GetRedirectedFocus(nsIContent* aContent) michael@0: { michael@0: #ifdef MOZ_XUL michael@0: if (aContent->IsXUL()) { michael@0: nsCOMPtr inputField; michael@0: michael@0: nsCOMPtr textbox = do_QueryInterface(aContent); michael@0: if (textbox) { michael@0: textbox->GetInputField(getter_AddRefs(inputField)); michael@0: } michael@0: else { michael@0: nsCOMPtr menulist = do_QueryInterface(aContent); michael@0: if (menulist) { michael@0: menulist->GetInputField(getter_AddRefs(inputField)); michael@0: } michael@0: else if (aContent->Tag() == nsGkAtoms::scale) { michael@0: nsCOMPtr doc = aContent->GetCurrentDoc(); michael@0: if (!doc) michael@0: return nullptr; michael@0: michael@0: nsINodeList* children = doc->BindingManager()->GetAnonymousNodesFor(aContent); michael@0: if (children) { michael@0: nsIContent* child = children->Item(0); michael@0: if (child && child->Tag() == nsGkAtoms::slider) michael@0: return child; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (inputField) { michael@0: nsCOMPtr retval = do_QueryInterface(inputField); michael@0: return retval; michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: // static michael@0: InputContextAction::Cause michael@0: nsFocusManager::GetFocusMoveActionCause(uint32_t aFlags) michael@0: { michael@0: if (aFlags & nsIFocusManager::FLAG_BYMOUSE) { michael@0: return InputContextAction::CAUSE_MOUSE; michael@0: } else if (aFlags & nsIFocusManager::FLAG_BYKEY) { michael@0: return InputContextAction::CAUSE_KEY; michael@0: } michael@0: return InputContextAction::CAUSE_UNKNOWN; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFocusManager::GetActiveWindow(nsIDOMWindow** aWindow) michael@0: { michael@0: NS_IF_ADDREF(*aWindow = mActiveWindow); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFocusManager::SetActiveWindow(nsIDOMWindow* aWindow) michael@0: { michael@0: // only top-level windows can be made active michael@0: nsCOMPtr piWindow = do_QueryInterface(aWindow); michael@0: if (piWindow) michael@0: piWindow = piWindow->GetOuterWindow(); michael@0: michael@0: NS_ENSURE_TRUE(piWindow && (piWindow == piWindow->GetPrivateRoot()), michael@0: NS_ERROR_INVALID_ARG); michael@0: michael@0: RaiseWindow(piWindow); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFocusManager::GetFocusedWindow(nsIDOMWindow** aFocusedWindow) michael@0: { michael@0: NS_IF_ADDREF(*aFocusedWindow = mFocusedWindow); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsFocusManager::SetFocusedWindow(nsIDOMWindow* aWindowToFocus) michael@0: { michael@0: LOGFOCUS(("<>")); michael@0: michael@0: nsCOMPtr windowToFocus(do_QueryInterface(aWindowToFocus)); michael@0: NS_ENSURE_TRUE(windowToFocus, NS_ERROR_FAILURE); michael@0: michael@0: windowToFocus = windowToFocus->GetOuterWindow(); michael@0: michael@0: nsCOMPtr frameContent = michael@0: do_QueryInterface(windowToFocus->GetFrameElementInternal()); michael@0: if (frameContent) { michael@0: // pass false for aFocusChanged so that the caret does not get updated michael@0: // and scrolling does not occur. michael@0: SetFocusInner(frameContent, 0, false, true); michael@0: } michael@0: else { michael@0: // this is a top-level window. If the window has a child frame focused, michael@0: // clear the focus. Otherwise, focus should already be in this frame, or michael@0: // already cleared. This ensures that focus will be in this frame and not michael@0: // in a child. michael@0: nsIContent* content = windowToFocus->GetFocusedNode(); michael@0: if (content) { michael@0: nsCOMPtr childWindow = GetContentWindow(content); michael@0: if (childWindow) michael@0: ClearFocus(windowToFocus); michael@0: } michael@0: } michael@0: michael@0: nsCOMPtr rootWindow = windowToFocus->GetPrivateRoot(); michael@0: if (rootWindow) michael@0: RaiseWindow(rootWindow); michael@0: michael@0: LOGFOCUS(("<>")); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFocusManager::GetFocusedElement(nsIDOMElement** aFocusedElement) michael@0: { michael@0: if (mFocusedContent) michael@0: CallQueryInterface(mFocusedContent, aFocusedElement); michael@0: else michael@0: *aFocusedElement = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFocusManager::GetLastFocusMethod(nsIDOMWindow* aWindow, uint32_t* aLastFocusMethod) michael@0: { michael@0: // the focus method is stored on the inner window michael@0: nsCOMPtr window(do_QueryInterface(aWindow)); michael@0: if (window) michael@0: window = window->GetCurrentInnerWindow(); michael@0: if (!window) michael@0: window = mFocusedWindow; michael@0: michael@0: *aLastFocusMethod = window ? window->GetFocusMethod() : 0; michael@0: michael@0: NS_ASSERTION((*aLastFocusMethod & FOCUSMETHOD_MASK) == *aLastFocusMethod, michael@0: "invalid focus method"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFocusManager::SetFocus(nsIDOMElement* aElement, uint32_t aFlags) michael@0: { michael@0: LOGFOCUS(("<>")); michael@0: michael@0: nsCOMPtr newFocus = do_QueryInterface(aElement); michael@0: NS_ENSURE_ARG(newFocus); michael@0: michael@0: SetFocusInner(newFocus, aFlags, true, true); michael@0: michael@0: LOGFOCUS(("<>")); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFocusManager::ElementIsFocusable(nsIDOMElement* aElement, uint32_t aFlags, michael@0: bool* aIsFocusable) michael@0: { michael@0: NS_ENSURE_TRUE(aElement, NS_ERROR_INVALID_ARG); michael@0: michael@0: nsCOMPtr aContent = do_QueryInterface(aElement); michael@0: michael@0: *aIsFocusable = CheckIfFocusable(aContent, aFlags) != nullptr; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFocusManager::MoveFocus(nsIDOMWindow* aWindow, nsIDOMElement* aStartElement, michael@0: uint32_t aType, uint32_t aFlags, nsIDOMElement** aElement) michael@0: { michael@0: *aElement = nullptr; michael@0: michael@0: #ifdef PR_LOGGING michael@0: LOGFOCUS(("<>", aType, aFlags)); michael@0: michael@0: if (PR_LOG_TEST(gFocusLog, PR_LOG_DEBUG) && mFocusedWindow) { michael@0: nsIDocument* doc = mFocusedWindow->GetExtantDoc(); michael@0: if (doc && doc->GetDocumentURI()) { michael@0: nsAutoCString spec; michael@0: doc->GetDocumentURI()->GetSpec(spec); michael@0: LOGFOCUS((" Focused Window: %p %s", mFocusedWindow.get(), spec.get())); michael@0: } michael@0: } michael@0: michael@0: LOGCONTENT(" Current Focus: %s", mFocusedContent.get()); michael@0: #endif michael@0: michael@0: // use FLAG_BYMOVEFOCUS when switching focus with MoveFocus unless one of michael@0: // the other focus methods is already set, or we're just moving to the root michael@0: // or caret position. michael@0: if (aType != MOVEFOCUS_ROOT && aType != MOVEFOCUS_CARET && michael@0: (aFlags & FOCUSMETHOD_MASK) == 0) { michael@0: aFlags |= FLAG_BYMOVEFOCUS; michael@0: } michael@0: michael@0: nsCOMPtr window; michael@0: nsCOMPtr startContent; michael@0: if (aStartElement) { michael@0: startContent = do_QueryInterface(aStartElement); michael@0: NS_ENSURE_TRUE(startContent, NS_ERROR_INVALID_ARG); michael@0: michael@0: window = GetCurrentWindow(startContent); michael@0: } michael@0: else { michael@0: window = aWindow ? do_QueryInterface(aWindow) : mFocusedWindow; michael@0: NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); michael@0: window = window->GetOuterWindow(); michael@0: } michael@0: michael@0: NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); michael@0: michael@0: bool noParentTraversal = aFlags & FLAG_NOPARENTFRAME; michael@0: nsCOMPtr newFocus; michael@0: nsresult rv = DetermineElementToMoveFocus(window, startContent, aType, noParentTraversal, michael@0: getter_AddRefs(newFocus)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: LOGCONTENTNAVIGATION("Element to be focused: %s", newFocus.get()); michael@0: michael@0: if (newFocus) { michael@0: // for caret movement, pass false for the aFocusChanged argument, michael@0: // otherwise the caret will end up moving to the focus position. This michael@0: // would be a problem because the caret would move to the beginning of the michael@0: // focused link making it impossible to navigate the caret over a link. michael@0: SetFocusInner(newFocus, aFlags, aType != MOVEFOCUS_CARET, true); michael@0: CallQueryInterface(newFocus, aElement); michael@0: } michael@0: else if (aType == MOVEFOCUS_ROOT || aType == MOVEFOCUS_CARET) { michael@0: // no content was found, so clear the focus for these two types. michael@0: ClearFocus(window); michael@0: } michael@0: michael@0: LOGFOCUS(("<>")); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFocusManager::ClearFocus(nsIDOMWindow* aWindow) michael@0: { michael@0: LOGFOCUS(("<>")); michael@0: michael@0: // if the window to clear is the focused window or an ancestor of the michael@0: // focused window, then blur the existing focused content. Otherwise, the michael@0: // focus is somewhere else so just update the current node. michael@0: nsCOMPtr window(do_QueryInterface(aWindow)); michael@0: NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG); michael@0: michael@0: window = window->GetOuterWindow(); michael@0: NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG); michael@0: michael@0: if (IsSameOrAncestor(window, mFocusedWindow)) { michael@0: bool isAncestor = (window != mFocusedWindow); michael@0: if (Blur(window, nullptr, isAncestor, true)) { michael@0: // if we are clearing the focus on an ancestor of the focused window, michael@0: // the ancestor will become the new focused window, so focus it michael@0: if (isAncestor) michael@0: Focus(window, nullptr, 0, true, false, false, true); michael@0: } michael@0: } michael@0: else { michael@0: window->SetFocusedNode(nullptr); michael@0: } michael@0: michael@0: LOGFOCUS(("<>")); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFocusManager::GetFocusedElementForWindow(nsIDOMWindow* aWindow, michael@0: bool aDeep, michael@0: nsIDOMWindow** aFocusedWindow, michael@0: nsIDOMElement** aElement) michael@0: { michael@0: *aElement = nullptr; michael@0: if (aFocusedWindow) michael@0: *aFocusedWindow = nullptr; michael@0: michael@0: nsCOMPtr window(do_QueryInterface(aWindow)); michael@0: NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG); michael@0: michael@0: window = window->GetOuterWindow(); michael@0: NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG); michael@0: michael@0: nsCOMPtr focusedWindow; michael@0: nsCOMPtr focusedContent = michael@0: GetFocusedDescendant(window, aDeep, getter_AddRefs(focusedWindow)); michael@0: if (focusedContent) michael@0: CallQueryInterface(focusedContent, aElement); michael@0: michael@0: if (aFocusedWindow) michael@0: NS_IF_ADDREF(*aFocusedWindow = focusedWindow); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFocusManager::MoveCaretToFocus(nsIDOMWindow* aWindow) michael@0: { michael@0: nsCOMPtr webnav = do_GetInterface(aWindow); michael@0: nsCOMPtr dsti = do_QueryInterface(webnav); michael@0: if (dsti) { michael@0: if (dsti->ItemType() != nsIDocShellTreeItem::typeChrome) { michael@0: nsCOMPtr docShell = do_QueryInterface(dsti); michael@0: NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE); michael@0: michael@0: // don't move the caret for editable documents michael@0: bool isEditable; michael@0: docShell->GetEditable(&isEditable); michael@0: if (isEditable) michael@0: return NS_OK; michael@0: michael@0: nsCOMPtr presShell = docShell->GetPresShell(); michael@0: NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE); michael@0: michael@0: nsCOMPtr window(do_QueryInterface(aWindow)); michael@0: nsCOMPtr content = window->GetFocusedNode(); michael@0: if (content) michael@0: MoveCaretToFocus(presShell, content); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFocusManager::WindowRaised(nsIDOMWindow* aWindow) michael@0: { michael@0: nsCOMPtr window = do_QueryInterface(aWindow); michael@0: NS_ENSURE_TRUE(window && window->IsOuterWindow(), NS_ERROR_INVALID_ARG); michael@0: michael@0: #ifdef PR_LOGGING michael@0: if (PR_LOG_TEST(gFocusLog, PR_LOG_DEBUG)) { michael@0: LOGFOCUS(("Window %p Raised [Currently: %p %p]", aWindow, mActiveWindow.get(), mFocusedWindow.get())); michael@0: nsAutoCString spec; michael@0: nsIDocument* doc = window->GetExtantDoc(); michael@0: if (doc && doc->GetDocumentURI()) { michael@0: doc->GetDocumentURI()->GetSpec(spec); michael@0: LOGFOCUS((" Raised Window: %p %s", aWindow, spec.get())); michael@0: } michael@0: if (mActiveWindow) { michael@0: doc = mActiveWindow->GetExtantDoc(); michael@0: if (doc && doc->GetDocumentURI()) { michael@0: doc->GetDocumentURI()->GetSpec(spec); michael@0: LOGFOCUS((" Active Window: %p %s", mActiveWindow.get(), spec.get())); michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: if (mActiveWindow == window) { michael@0: // The window is already active, so there is no need to focus anything, michael@0: // but make sure that the right widget is focused. This is a special case michael@0: // for Windows because when restoring a minimized window, a second michael@0: // activation will occur and the top-level widget could be focused instead michael@0: // of the child we want. We solve this by calling SetFocus to ensure that michael@0: // what the focus manager thinks should be the current widget is actually michael@0: // focused. michael@0: EnsureCurrentWidgetFocused(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // lower the existing window, if any. This shouldn't happen usually. michael@0: if (mActiveWindow) michael@0: WindowLowered(mActiveWindow); michael@0: michael@0: nsCOMPtr webnav(do_GetInterface(aWindow)); michael@0: nsCOMPtr docShellAsItem(do_QueryInterface(webnav)); michael@0: // If there's no docShellAsItem, this window must have been closed, michael@0: // in that case there is no tree owner. michael@0: NS_ENSURE_TRUE(docShellAsItem, NS_OK); michael@0: michael@0: // set this as the active window michael@0: mActiveWindow = window; michael@0: michael@0: // ensure that the window is enabled and visible michael@0: nsCOMPtr treeOwner; michael@0: docShellAsItem->GetTreeOwner(getter_AddRefs(treeOwner)); michael@0: nsCOMPtr baseWindow = do_QueryInterface(treeOwner); michael@0: if (baseWindow) { michael@0: bool isEnabled = true; michael@0: if (NS_SUCCEEDED(baseWindow->GetEnabled(&isEnabled)) && !isEnabled) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (!sTestMode) { michael@0: baseWindow->SetVisibility(true); michael@0: } michael@0: } michael@0: michael@0: // inform the DOM window that it has activated, so that the active attribute michael@0: // is updated on the window michael@0: window->ActivateOrDeactivate(true); michael@0: michael@0: // send activate event michael@0: nsContentUtils::DispatchTrustedEvent(window->GetExtantDoc(), michael@0: window, michael@0: NS_LITERAL_STRING("activate"), michael@0: true, true, nullptr); michael@0: michael@0: // retrieve the last focused element within the window that was raised michael@0: nsCOMPtr currentWindow; michael@0: nsCOMPtr currentFocus = michael@0: GetFocusedDescendant(window, true, getter_AddRefs(currentWindow)); michael@0: michael@0: NS_ASSERTION(currentWindow, "window raised with no window current"); michael@0: if (!currentWindow) michael@0: return NS_OK; michael@0: michael@0: nsCOMPtr currentDocShell = currentWindow->GetDocShell(); michael@0: michael@0: nsCOMPtr presShell = currentDocShell->GetPresShell(); michael@0: if (presShell) { michael@0: // disable selection mousedown state on activation michael@0: // XXXndeakin P3 not sure if this is necessary, but it doesn't hurt michael@0: nsRefPtr frameSelection = presShell->FrameSelection(); michael@0: frameSelection->SetMouseDownState(false); michael@0: } michael@0: michael@0: Focus(currentWindow, currentFocus, 0, true, false, true, true); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFocusManager::WindowLowered(nsIDOMWindow* aWindow) michael@0: { michael@0: nsCOMPtr window = do_QueryInterface(aWindow); michael@0: NS_ENSURE_TRUE(window && window->IsOuterWindow(), NS_ERROR_INVALID_ARG); michael@0: michael@0: #ifdef PR_LOGGING michael@0: if (PR_LOG_TEST(gFocusLog, PR_LOG_DEBUG)) { michael@0: LOGFOCUS(("Window %p Lowered [Currently: %p %p]", aWindow, mActiveWindow.get(), mFocusedWindow.get())); michael@0: nsAutoCString spec; michael@0: nsIDocument* doc = window->GetExtantDoc(); michael@0: if (doc && doc->GetDocumentURI()) { michael@0: doc->GetDocumentURI()->GetSpec(spec); michael@0: LOGFOCUS((" Lowered Window: %s", spec.get())); michael@0: } michael@0: if (mActiveWindow) { michael@0: doc = mActiveWindow->GetExtantDoc(); michael@0: if (doc && doc->GetDocumentURI()) { michael@0: doc->GetDocumentURI()->GetSpec(spec); michael@0: LOGFOCUS((" Active Window: %s", spec.get())); michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: if (mActiveWindow != window) michael@0: return NS_OK; michael@0: michael@0: // clear the mouse capture as the active window has changed michael@0: nsIPresShell::SetCapturingContent(nullptr, 0); michael@0: michael@0: // inform the DOM window that it has deactivated, so that the active michael@0: // attribute is updated on the window michael@0: window->ActivateOrDeactivate(false); michael@0: michael@0: // send deactivate event michael@0: nsContentUtils::DispatchTrustedEvent(window->GetExtantDoc(), michael@0: window, michael@0: NS_LITERAL_STRING("deactivate"), michael@0: true, true, nullptr); michael@0: michael@0: // keep track of the window being lowered, so that attempts to raise the michael@0: // window can be prevented until we return. Otherwise, focus can get into michael@0: // an unusual state. michael@0: mWindowBeingLowered = mActiveWindow; michael@0: mActiveWindow = nullptr; michael@0: michael@0: if (mFocusedWindow) michael@0: Blur(nullptr, nullptr, true, true); michael@0: michael@0: mWindowBeingLowered = nullptr; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsFocusManager::ContentRemoved(nsIDocument* aDocument, nsIContent* aContent) michael@0: { michael@0: NS_ENSURE_ARG(aDocument); michael@0: NS_ENSURE_ARG(aContent); michael@0: michael@0: nsPIDOMWindow *window = aDocument->GetWindow(); michael@0: if (!window) michael@0: return NS_OK; michael@0: michael@0: // if the content is currently focused in the window, or is an ancestor michael@0: // of the currently focused element, reset the focus within that window. michael@0: nsIContent* content = window->GetFocusedNode(); michael@0: if (content && nsContentUtils::ContentIsDescendantOf(content, aContent)) { michael@0: bool shouldShowFocusRing = window->ShouldShowFocusRing(); michael@0: window->SetFocusedNode(nullptr); michael@0: michael@0: // if this window is currently focused, clear the global focused michael@0: // element as well, but don't fire any events. michael@0: if (window == mFocusedWindow) { michael@0: mFocusedContent = nullptr; michael@0: } michael@0: else { michael@0: // Check if the node that was focused is an iframe or similar by looking michael@0: // if it has a subdocument. This would indicate that this focused iframe michael@0: // and its descendants will be going away. We will need to move the michael@0: // focus somewhere else, so just clear the focus in the toplevel window michael@0: // so that no element is focused. michael@0: nsIDocument* subdoc = aDocument->GetSubDocumentFor(content); michael@0: if (subdoc) { michael@0: nsCOMPtr container = subdoc->GetContainer(); michael@0: nsCOMPtr childWindow = do_GetInterface(container); michael@0: if (childWindow && IsSameOrAncestor(childWindow, mFocusedWindow)) { michael@0: ClearFocus(mActiveWindow); michael@0: } michael@0: } michael@0: } michael@0: michael@0: NotifyFocusStateChange(content, shouldShowFocusRing, false); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFocusManager::WindowShown(nsIDOMWindow* aWindow, bool aNeedsFocus) michael@0: { michael@0: nsCOMPtr window = do_QueryInterface(aWindow); michael@0: NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG); michael@0: michael@0: window = window->GetOuterWindow(); michael@0: michael@0: #ifdef PR_LOGGING michael@0: if (PR_LOG_TEST(gFocusLog, PR_LOG_DEBUG)) { michael@0: LOGFOCUS(("Window %p Shown [Currently: %p %p]", window.get(), mActiveWindow.get(), mFocusedWindow.get())); michael@0: nsAutoCString spec; michael@0: nsIDocument* doc = window->GetExtantDoc(); michael@0: if (doc && doc->GetDocumentURI()) { michael@0: doc->GetDocumentURI()->GetSpec(spec); michael@0: LOGFOCUS(("Shown Window: %s", spec.get())); michael@0: } michael@0: michael@0: if (mFocusedWindow) { michael@0: doc = mFocusedWindow->GetExtantDoc(); michael@0: if (doc && doc->GetDocumentURI()) { michael@0: doc->GetDocumentURI()->GetSpec(spec); michael@0: LOGFOCUS((" Focused Window: %s", spec.get())); michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: if (mFocusedWindow != window) michael@0: return NS_OK; michael@0: michael@0: if (aNeedsFocus) { michael@0: nsCOMPtr currentWindow; michael@0: nsCOMPtr currentFocus = michael@0: GetFocusedDescendant(window, true, getter_AddRefs(currentWindow)); michael@0: if (currentWindow) michael@0: Focus(currentWindow, currentFocus, 0, true, false, false, true); michael@0: } michael@0: else { michael@0: // Sometimes, an element in a window can be focused before the window is michael@0: // visible, which would mean that the widget may not be properly focused. michael@0: // When the window becomes visible, make sure the right widget is focused. michael@0: EnsureCurrentWidgetFocused(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFocusManager::WindowHidden(nsIDOMWindow* aWindow) michael@0: { michael@0: // if there is no window or it is not the same or an ancestor of the michael@0: // currently focused window, just return, as the current focus will not michael@0: // be affected. michael@0: michael@0: nsCOMPtr window = do_QueryInterface(aWindow); michael@0: NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG); michael@0: michael@0: window = window->GetOuterWindow(); michael@0: michael@0: #ifdef PR_LOGGING michael@0: if (PR_LOG_TEST(gFocusLog, PR_LOG_DEBUG)) { michael@0: LOGFOCUS(("Window %p Hidden [Currently: %p %p]", window.get(), mActiveWindow.get(), mFocusedWindow.get())); michael@0: nsAutoCString spec; michael@0: nsIDocument* doc = window->GetExtantDoc(); michael@0: if (doc && doc->GetDocumentURI()) { michael@0: doc->GetDocumentURI()->GetSpec(spec); michael@0: LOGFOCUS((" Hide Window: %s", spec.get())); michael@0: } michael@0: michael@0: if (mFocusedWindow) { michael@0: doc = mFocusedWindow->GetExtantDoc(); michael@0: if (doc && doc->GetDocumentURI()) { michael@0: doc->GetDocumentURI()->GetSpec(spec); michael@0: LOGFOCUS((" Focused Window: %s", spec.get())); michael@0: } michael@0: } michael@0: michael@0: if (mActiveWindow) { michael@0: doc = mActiveWindow->GetExtantDoc(); michael@0: if (doc && doc->GetDocumentURI()) { michael@0: doc->GetDocumentURI()->GetSpec(spec); michael@0: LOGFOCUS((" Active Window: %s", spec.get())); michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: if (!IsSameOrAncestor(window, mFocusedWindow)) michael@0: return NS_OK; michael@0: michael@0: // at this point, we know that the window being hidden is either the focused michael@0: // window, or an ancestor of the focused window. Either way, the focus is no michael@0: // longer valid, so it needs to be updated. michael@0: michael@0: nsCOMPtr oldFocusedContent = mFocusedContent.forget(); michael@0: michael@0: nsCOMPtr focusedDocShell = mFocusedWindow->GetDocShell(); michael@0: nsCOMPtr presShell = focusedDocShell->GetPresShell(); michael@0: michael@0: if (oldFocusedContent && oldFocusedContent->IsInDoc()) { michael@0: NotifyFocusStateChange(oldFocusedContent, michael@0: mFocusedWindow->ShouldShowFocusRing(), michael@0: false); michael@0: window->UpdateCommands(NS_LITERAL_STRING("focus")); michael@0: michael@0: if (presShell) { michael@0: SendFocusOrBlurEvent(NS_BLUR_CONTENT, presShell, michael@0: oldFocusedContent->GetCurrentDoc(), michael@0: oldFocusedContent, 1, false); michael@0: } michael@0: } michael@0: michael@0: nsPresContext* focusedPresContext = michael@0: presShell ? presShell->GetPresContext() : nullptr; michael@0: IMEStateManager::OnChangeFocus(focusedPresContext, nullptr, michael@0: GetFocusMoveActionCause(0)); michael@0: if (presShell) { michael@0: SetCaretVisible(presShell, false, nullptr); michael@0: } michael@0: michael@0: // if the docshell being hidden is being destroyed, then we want to move michael@0: // focus somewhere else. Call ClearFocus on the toplevel window, which michael@0: // will have the effect of clearing the focus and moving the focused window michael@0: // to the toplevel window. But if the window isn't being destroyed, we are michael@0: // likely just loading a new document in it, so we want to maintain the michael@0: // focused window so that the new document gets properly focused. michael@0: bool beingDestroyed; michael@0: nsCOMPtr docShellBeingHidden = window->GetDocShell(); michael@0: docShellBeingHidden->IsBeingDestroyed(&beingDestroyed); michael@0: if (beingDestroyed) { michael@0: // There is usually no need to do anything if a toplevel window is going michael@0: // away, as we assume that WindowLowered will be called. However, this may michael@0: // not happen if nsIAppStartup::eForceQuit is used to quit, and can cause michael@0: // a leak. So if the active window is being destroyed, call WindowLowered michael@0: // directly. michael@0: NS_ASSERTION(mFocusedWindow->IsOuterWindow(), "outer window expected"); michael@0: if (mActiveWindow == mFocusedWindow || mActiveWindow == window) michael@0: WindowLowered(mActiveWindow); michael@0: else michael@0: ClearFocus(mActiveWindow); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // if the window being hidden is an ancestor of the focused window, adjust michael@0: // the focused window so that it points to the one being hidden. This michael@0: // ensures that the focused window isn't in a chain of frames that doesn't michael@0: // exist any more. michael@0: if (window != mFocusedWindow) { michael@0: nsCOMPtr webnav(do_GetInterface(mFocusedWindow)); michael@0: nsCOMPtr dsti = do_QueryInterface(webnav); michael@0: if (dsti) { michael@0: nsCOMPtr parentDsti; michael@0: dsti->GetParent(getter_AddRefs(parentDsti)); michael@0: nsCOMPtr parentWindow = do_GetInterface(parentDsti); michael@0: if (parentWindow) michael@0: parentWindow->SetFocusedNode(nullptr); michael@0: } michael@0: michael@0: SetFocusedWindowInternal(window); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFocusManager::FireDelayedEvents(nsIDocument* aDocument) michael@0: { michael@0: NS_ENSURE_ARG(aDocument); michael@0: michael@0: // fire any delayed focus and blur events in the same order that they were added michael@0: for (uint32_t i = 0; i < mDelayedBlurFocusEvents.Length(); i++) { michael@0: if (mDelayedBlurFocusEvents[i].mDocument == aDocument) { michael@0: if (!aDocument->GetInnerWindow() || michael@0: !aDocument->GetInnerWindow()->IsCurrentInnerWindow()) { michael@0: // If the document was navigated away from or is defunct, don't bother michael@0: // firing events on it. Note the symmetry between this condition and michael@0: // the similar one in nsDocument.cpp:FireOrClearDelayedEvents. michael@0: mDelayedBlurFocusEvents.RemoveElementAt(i); michael@0: --i; michael@0: } else if (!aDocument->EventHandlingSuppressed()) { michael@0: uint32_t type = mDelayedBlurFocusEvents[i].mType; michael@0: nsCOMPtr target = mDelayedBlurFocusEvents[i].mTarget; michael@0: nsCOMPtr presShell = mDelayedBlurFocusEvents[i].mPresShell; michael@0: mDelayedBlurFocusEvents.RemoveElementAt(i); michael@0: SendFocusOrBlurEvent(type, presShell, aDocument, target, 0, false); michael@0: --i; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFocusManager::FocusPlugin(nsIContent* aContent) michael@0: { michael@0: NS_ENSURE_ARG(aContent); michael@0: SetFocusInner(aContent, 0, true, false); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* static */ michael@0: void michael@0: nsFocusManager::NotifyFocusStateChange(nsIContent* aContent, michael@0: bool aWindowShouldShowFocusRing, michael@0: bool aGettingFocus) michael@0: { michael@0: if (!aContent->IsElement()) { michael@0: return; michael@0: } michael@0: EventStates eventState = NS_EVENT_STATE_FOCUS; michael@0: if (aWindowShouldShowFocusRing) { michael@0: eventState |= NS_EVENT_STATE_FOCUSRING; michael@0: } michael@0: if (aGettingFocus) { michael@0: aContent->AsElement()->AddStates(eventState); michael@0: } else { michael@0: aContent->AsElement()->RemoveStates(eventState); michael@0: } michael@0: } michael@0: michael@0: // static michael@0: void michael@0: nsFocusManager::EnsureCurrentWidgetFocused() michael@0: { michael@0: if (!mFocusedWindow || sTestMode) michael@0: return; michael@0: michael@0: // get the main child widget for the focused window and ensure that the michael@0: // platform knows that this widget is focused. michael@0: nsCOMPtr docShell = mFocusedWindow->GetDocShell(); michael@0: if (docShell) { michael@0: nsCOMPtr presShell = docShell->GetPresShell(); michael@0: if (presShell) { michael@0: nsViewManager* vm = presShell->GetViewManager(); michael@0: if (vm) { michael@0: nsCOMPtr widget; michael@0: vm->GetRootWidget(getter_AddRefs(widget)); michael@0: if (widget) michael@0: widget->SetFocus(false); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsFocusManager::SetFocusInner(nsIContent* aNewContent, int32_t aFlags, michael@0: bool aFocusChanged, bool aAdjustWidget) michael@0: { michael@0: // if the element is not focusable, just return and leave the focus as is michael@0: nsCOMPtr contentToFocus = CheckIfFocusable(aNewContent, aFlags); michael@0: if (!contentToFocus) michael@0: return; michael@0: michael@0: // check if the element to focus is a frame (iframe) containing a child michael@0: // document. Frames are never directly focused; instead focusing a frame michael@0: // means focus what is inside the frame. To do this, the descendant content michael@0: // within the frame is retrieved and that will be focused instead. michael@0: nsCOMPtr newWindow; michael@0: nsCOMPtr subWindow = GetContentWindow(contentToFocus); michael@0: if (subWindow) { michael@0: contentToFocus = GetFocusedDescendant(subWindow, true, getter_AddRefs(newWindow)); michael@0: // since a window is being refocused, clear aFocusChanged so that the michael@0: // caret position isn't updated. michael@0: aFocusChanged = false; michael@0: } michael@0: michael@0: // unless it was set above, retrieve the window for the element to focus michael@0: if (!newWindow) michael@0: newWindow = GetCurrentWindow(contentToFocus); michael@0: michael@0: // if the element is already focused, just return. Note that this happens michael@0: // after the frame check above so that we compare the element that will be michael@0: // focused rather than the frame it is in. michael@0: if (!newWindow || (newWindow == mFocusedWindow && contentToFocus == mFocusedContent)) michael@0: return; michael@0: michael@0: // don't allow focus to be placed in docshells or descendants of docshells michael@0: // that are being destroyed. Also, ensure that the page hasn't been michael@0: // unloaded. The prevents content from being refocused during an unload event. michael@0: nsCOMPtr newDocShell = newWindow->GetDocShell(); michael@0: nsCOMPtr docShell = newDocShell; michael@0: while (docShell) { michael@0: bool inUnload; michael@0: docShell->GetIsInUnload(&inUnload); michael@0: if (inUnload) michael@0: return; michael@0: michael@0: bool beingDestroyed; michael@0: docShell->IsBeingDestroyed(&beingDestroyed); michael@0: if (beingDestroyed) michael@0: return; michael@0: michael@0: nsCOMPtr parentDsti; michael@0: docShell->GetParent(getter_AddRefs(parentDsti)); michael@0: docShell = do_QueryInterface(parentDsti); michael@0: } michael@0: michael@0: // if the new element is in the same window as the currently focused element michael@0: bool isElementInFocusedWindow = (mFocusedWindow == newWindow); michael@0: michael@0: if (!isElementInFocusedWindow && mFocusedWindow && newWindow && michael@0: nsContentUtils::IsHandlingKeyBoardEvent()) { michael@0: nsCOMPtr focused = michael@0: do_QueryInterface(mFocusedWindow); michael@0: nsCOMPtr newFocus = michael@0: do_QueryInterface(newWindow); michael@0: nsIPrincipal* focusedPrincipal = focused->GetPrincipal(); michael@0: nsIPrincipal* newPrincipal = newFocus->GetPrincipal(); michael@0: if (!focusedPrincipal || !newPrincipal) { michael@0: return; michael@0: } michael@0: bool subsumes = false; michael@0: focusedPrincipal->Subsumes(newPrincipal, &subsumes); michael@0: if (!subsumes && !nsContentUtils::IsCallerChrome()) { michael@0: NS_WARNING("Not allowed to focus the new window!"); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: // to check if the new element is in the active window, compare the michael@0: // new root docshell for the new element with the active window's docshell. michael@0: bool isElementInActiveWindow = false; michael@0: michael@0: nsCOMPtr webnav = do_GetInterface(newWindow); michael@0: nsCOMPtr dsti = do_QueryInterface(webnav); michael@0: nsCOMPtr newRootWindow; michael@0: if (dsti) { michael@0: nsCOMPtr root; michael@0: dsti->GetRootTreeItem(getter_AddRefs(root)); michael@0: newRootWindow = do_GetInterface(root); michael@0: michael@0: isElementInActiveWindow = (mActiveWindow && newRootWindow == mActiveWindow); michael@0: } michael@0: michael@0: // Exit fullscreen if we're focusing a windowed plugin on a non-MacOSX michael@0: // system. We don't control event dispatch to windowed plugins on non-MacOSX, michael@0: // so we can't display the "Press ESC to leave fullscreen mode" warning on michael@0: // key input if a windowed plugin is focused, so just exit fullscreen michael@0: // to guard against phishing. michael@0: #ifndef XP_MACOSX michael@0: nsIDocument* fullscreenAncestor; michael@0: if (contentToFocus && michael@0: (fullscreenAncestor = nsContentUtils::GetFullscreenAncestor(contentToFocus->OwnerDoc())) && michael@0: nsContentUtils::HasPluginWithUncontrolledEventDispatch(contentToFocus)) { michael@0: nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, michael@0: NS_LITERAL_CSTRING("DOM"), michael@0: contentToFocus->OwnerDoc(), michael@0: nsContentUtils::eDOM_PROPERTIES, michael@0: "FocusedWindowedPluginWhileFullScreen"); michael@0: nsIDocument::ExitFullscreen(fullscreenAncestor, /* async */ true); michael@0: } michael@0: #endif michael@0: michael@0: // if the FLAG_NOSWITCHFRAME flag is used, only allow the focus to be michael@0: // shifted away from the current element if the new shell to focus is michael@0: // the same or an ancestor shell of the currently focused shell. michael@0: bool allowFrameSwitch = !(aFlags & FLAG_NOSWITCHFRAME) || michael@0: IsSameOrAncestor(newWindow, mFocusedWindow); michael@0: michael@0: // if the element is in the active window, frame switching is allowed and michael@0: // the content is in a visible window, fire blur and focus events. michael@0: bool sendFocusEvent = michael@0: isElementInActiveWindow && allowFrameSwitch && IsWindowVisible(newWindow); michael@0: michael@0: // When the following conditions are true: michael@0: // * an element has focus michael@0: // * isn't called by trusted event (i.e., called by untrusted event or by js) michael@0: // * the focus is moved to another document's element michael@0: // we need to check the permission. michael@0: if (sendFocusEvent && mFocusedContent && michael@0: mFocusedContent->OwnerDoc() != aNewContent->OwnerDoc()) { michael@0: // If the caller cannot access the current focused node, the caller should michael@0: // not be able to steal focus from it. E.g., When the current focused node michael@0: // is in chrome, any web contents should not be able to steal the focus. michael@0: nsCOMPtr domNode(do_QueryInterface(mFocusedContent)); michael@0: sendFocusEvent = nsContentUtils::CanCallerAccess(domNode); michael@0: if (!sendFocusEvent && mMouseDownEventHandlingDocument) { michael@0: // However, while mouse down event is handling, the handling document's michael@0: // script should be able to steal focus. michael@0: domNode = do_QueryInterface(mMouseDownEventHandlingDocument); michael@0: sendFocusEvent = nsContentUtils::CanCallerAccess(domNode); michael@0: } michael@0: } michael@0: michael@0: LOGCONTENT("Shift Focus: %s", contentToFocus.get()); michael@0: LOGFOCUS((" Flags: %x Current Window: %p New Window: %p Current Element: %p", michael@0: aFlags, mFocusedWindow.get(), newWindow.get(), mFocusedContent.get())); michael@0: LOGFOCUS((" In Active Window: %d In Focused Window: %d SendFocus: %d", michael@0: isElementInActiveWindow, isElementInFocusedWindow, sendFocusEvent)); michael@0: michael@0: if (sendFocusEvent) { michael@0: // return if blurring fails or the focus changes during the blur michael@0: if (mFocusedWindow) { michael@0: // if the focus is being moved to another element in the same document, michael@0: // or to a descendant, pass the existing window to Blur so that the michael@0: // current node in the existing window is cleared. If moving to a michael@0: // window elsewhere, we want to maintain the current node in the michael@0: // window but still blur it. michael@0: bool currentIsSameOrAncestor = IsSameOrAncestor(mFocusedWindow, newWindow); michael@0: // find the common ancestor of the currently focused window and the new michael@0: // window. The ancestor will need to have its currently focused node michael@0: // cleared once the document has been blurred. Otherwise, we'll be in a michael@0: // state where a document is blurred yet the chain of windows above it michael@0: // still points to that document. michael@0: // For instance, in the following frame tree: michael@0: // A michael@0: // B C michael@0: // D michael@0: // D is focused and we want to focus C. Once D has been blurred, we need michael@0: // to clear out the focus in A, otherwise A would still maintain that B michael@0: // was focused, and B that D was focused. michael@0: nsCOMPtr commonAncestor; michael@0: if (!isElementInFocusedWindow) michael@0: commonAncestor = GetCommonAncestor(newWindow, mFocusedWindow); michael@0: michael@0: if (!Blur(currentIsSameOrAncestor ? mFocusedWindow.get() : nullptr, michael@0: commonAncestor, !isElementInFocusedWindow, aAdjustWidget)) michael@0: return; michael@0: } michael@0: michael@0: Focus(newWindow, contentToFocus, aFlags, !isElementInFocusedWindow, michael@0: aFocusChanged, false, aAdjustWidget); michael@0: } michael@0: else { michael@0: // otherwise, for inactive windows and when the caller cannot steal the michael@0: // focus, update the node in the window, and raise the window if desired. michael@0: if (allowFrameSwitch) michael@0: AdjustWindowFocus(newWindow, true); michael@0: michael@0: // set the focus node and method as needed michael@0: uint32_t focusMethod = aFocusChanged ? aFlags & FOCUSMETHODANDRING_MASK : michael@0: newWindow->GetFocusMethod() | (aFlags & FLAG_SHOWRING); michael@0: newWindow->SetFocusedNode(contentToFocus, focusMethod); michael@0: if (aFocusChanged) { michael@0: nsCOMPtr docShell = newWindow->GetDocShell(); michael@0: michael@0: nsCOMPtr presShell = docShell->GetPresShell(); michael@0: if (presShell) michael@0: ScrollIntoView(presShell, contentToFocus, aFlags); michael@0: } michael@0: michael@0: // update the commands even when inactive so that the attributes for that michael@0: // window are up to date. michael@0: if (allowFrameSwitch) michael@0: newWindow->UpdateCommands(NS_LITERAL_STRING("focus")); michael@0: michael@0: if (aFlags & FLAG_RAISE) michael@0: RaiseWindow(newRootWindow); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: nsFocusManager::IsSameOrAncestor(nsPIDOMWindow* aPossibleAncestor, michael@0: nsPIDOMWindow* aWindow) michael@0: { michael@0: nsCOMPtr awebnav(do_GetInterface(aPossibleAncestor)); michael@0: nsCOMPtr ancestordsti = do_QueryInterface(awebnav); michael@0: michael@0: nsCOMPtr fwebnav(do_GetInterface(aWindow)); michael@0: nsCOMPtr dsti = do_QueryInterface(fwebnav); michael@0: while (dsti) { michael@0: if (dsti == ancestordsti) michael@0: return true; michael@0: nsCOMPtr parentDsti; michael@0: dsti->GetParent(getter_AddRefs(parentDsti)); michael@0: dsti.swap(parentDsti); michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsFocusManager::GetCommonAncestor(nsPIDOMWindow* aWindow1, michael@0: nsPIDOMWindow* aWindow2) michael@0: { michael@0: nsCOMPtr webnav(do_GetInterface(aWindow1)); michael@0: nsCOMPtr dsti1 = do_QueryInterface(webnav); michael@0: NS_ENSURE_TRUE(dsti1, nullptr); michael@0: michael@0: webnav = do_GetInterface(aWindow2); michael@0: nsCOMPtr dsti2 = do_QueryInterface(webnav); michael@0: NS_ENSURE_TRUE(dsti2, nullptr); michael@0: michael@0: nsAutoTArray parents1, parents2; michael@0: do { michael@0: parents1.AppendElement(dsti1); michael@0: nsCOMPtr parentDsti1; michael@0: dsti1->GetParent(getter_AddRefs(parentDsti1)); michael@0: dsti1.swap(parentDsti1); michael@0: } while (dsti1); michael@0: do { michael@0: parents2.AppendElement(dsti2); michael@0: nsCOMPtr parentDsti2; michael@0: dsti2->GetParent(getter_AddRefs(parentDsti2)); michael@0: dsti2.swap(parentDsti2); michael@0: } while (dsti2); michael@0: michael@0: uint32_t pos1 = parents1.Length(); michael@0: uint32_t pos2 = parents2.Length(); michael@0: nsIDocShellTreeItem* parent = nullptr; michael@0: uint32_t len; michael@0: for (len = std::min(pos1, pos2); len > 0; --len) { michael@0: nsIDocShellTreeItem* child1 = parents1.ElementAt(--pos1); michael@0: nsIDocShellTreeItem* child2 = parents2.ElementAt(--pos2); michael@0: if (child1 != child2) { michael@0: break; michael@0: } michael@0: parent = child1; michael@0: } michael@0: michael@0: nsCOMPtr window = do_GetInterface(parent); michael@0: return window.forget(); michael@0: } michael@0: michael@0: void michael@0: nsFocusManager::AdjustWindowFocus(nsPIDOMWindow* aWindow, michael@0: bool aCheckPermission) michael@0: { michael@0: bool isVisible = IsWindowVisible(aWindow); michael@0: michael@0: nsCOMPtr window(aWindow); michael@0: while (window) { michael@0: // get the containing