/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Two types of focus pr logging are available: // 'Focus' for normal focus manager calls // 'FocusNavigation' for tab and document navigation
LazyLogModule gFocusLog("Focus");
LazyLogModule gFocusNavigationLog("FocusNavigation");
bool nsFocusManager::IsInActiveWindow(BrowsingContext* aBC) const {
RefPtr<BrowsingContext> top = aBC->Top(); if (XRE_IsParentProcess()) {
top = top->Canonical()->TopCrossChromeBoundary();
} return IsSameOrAncestor(top, GetActiveBrowsingContext());
}
// get the current window for the given content node static nsPIDOMWindowOuter* GetCurrentWindow(nsIContent* aContent) {
Document* doc = aContent->GetComposedDoc(); return doc ? doc->GetWindow() : nullptr;
}
// If the child window doesn't have PresShell, it means the window is // invisible.
nsIDocShell* docShell = window->GetDocShell(); if (!docShell) { break;
} if (!docShell->GetPresShell()) { break;
}
}
NS_IMETHODIMP
nsFocusManager::GetActiveWindow(mozIDOMWindowProxy** aWindow) {
MOZ_ASSERT(XRE_IsParentProcess(), "Must not be called outside the parent process.");
NS_IF_ADDREF(*aWindow = mActiveWindow); return NS_OK;
}
NS_IMETHODIMP
nsFocusManager::GetFocusedContentBrowsingContext(
BrowsingContext** aBrowsingContext) {
MOZ_DIAGNOSTIC_ASSERT(
XRE_IsParentProcess(), "We only have use cases for this in the parent process");
NS_IF_ADDREF(*aBrowsingContext = GetFocusedBrowsingContextInChrome()); return NS_OK;
}
NS_IMETHODIMP
nsFocusManager::GetActiveContentBrowsingContext(
BrowsingContext** aBrowsingContext) {
MOZ_DIAGNOSTIC_ASSERT(
XRE_IsParentProcess(), "We only have use cases for this in the parent process");
NS_IF_ADDREF(*aBrowsingContext = GetActiveBrowsingContextInChrome()); return NS_OK;
}
nsCOMPtr<Element> frameElement = windowToFocus->GetFrameElementInternal();
Maybe<uint64_t> existingActionId; if (frameElement) { // pass false for aFocusChanged so that the caret does not get updated // and scrolling does not occur.
existingActionId = SetFocusInner(frameElement, 0, false, true);
} elseif (auto* bc = windowToFocus->GetBrowsingContext();
bc && !bc->IsTop()) { // No frameElement means windowToFocus is an OOP iframe, so // the above SetFocusInner is not called. That means the focus // of the currently focused BC is not going to be cleared. So // we do that manually here. if (RefPtr<BrowsingContext> focusedBC = GetFocusedBrowsingContext()) { // If focusedBC is an ancestor of bc, blur will be handled // correctly by nsFocusManager::AdjustWindowFocus. if (!IsSameOrAncestor(focusedBC, bc)) {
existingActionId.emplace(sInstance->GenerateFocusActionId());
Blur(focusedBC, nullptr, true, true, false, existingActionId.value());
}
}
} else { // this is a top-level window. If the window has a child frame focused, // clear the focus. Otherwise, focus should already be in this frame, or // already cleared. This ensures that focus will be in this frame and not // in a child. if (Element* el = windowToFocus->GetFocusedElement()) { if (nsCOMPtr<nsPIDOMWindowOuter> childWindow = GetContentWindow(el)) {
ClearFocus(windowToFocus);
}
}
}
LOGCONTENT(" Current Focus: %s", mFocusedElement.get());
// use FLAG_BYMOVEFOCUS when switching focus with MoveFocus unless one of // the other focus methods is already set, or we're just moving to the root // or caret position. if (aType != MOVEFOCUS_ROOT && aType != MOVEFOCUS_CARET &&
(aFlags & METHOD_MASK) == 0) {
aFlags |= FLAG_BYMOVEFOCUS;
}
// Flush to ensure that focusability of descendants is computed correctly. if (RefPtr<Document> doc = window->GetExtantDoc()) {
doc->FlushPendingNotifications(FlushType::EnsurePresShellInitAndFrames);
}
LOGCONTENTNAVIGATION("Element to be focused: %s", newFocus.get());
if (newFocus && newFocus->IsElement()) { // for caret movement, pass false for the aFocusChanged argument, // otherwise the caret will end up moving to the focus position. This // would be a problem because the caret would move to the beginning of the // focused link making it impossible to navigate the caret over a link.
SetFocusInner(MOZ_KnownLive(newFocus->AsElement()), aFlags,
aType != MOVEFOCUS_CARET, true);
*aElement = do_AddRef(newFocus->AsElement()).take();
} elseif (aType == MOVEFOCUS_ROOT || aType == MOVEFOCUS_CARET) { // no content was found, so clear the focus for these two types.
ClearFocus(window);
}
// if the window to clear is the focused window or an ancestor of the // focused window, then blur the existing focused content. Otherwise, the // focus is somewhere else so just update the current node.
NS_ENSURE_TRUE(aWindow, NS_ERROR_INVALID_ARG);
nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(aWindow);
if (IsSameOrAncestor(window, GetFocusedBrowsingContext())) {
RefPtr<BrowsingContext> bc = window->GetBrowsingContext();
RefPtr<BrowsingContext> focusedBC = GetFocusedBrowsingContext(); constbool isAncestor = (focusedBC != bc);
RefPtr<BrowsingContext> ancestorBC = isAncestor ? bc : nullptr; if (Blur(focusedBC, ancestorBC, isAncestor, true, false,
GenerateFocusActionId())) { // if we are clearing the focus on an ancestor of the focused window, // the ancestor will become the new focused window, so focus it if (isAncestor) { // Intentionally use a new actionId here because the above // Blur() will clear the focus of the ancestors of focusedBC, and // this Focus() call might need to update the focus of those ancestors, // so it needs to have a newer actionId to make that happen.
Focus(window, nullptr, 0, true, false, false, true,
GenerateFocusActionId());
}
}
} else {
window->SetFocusedElement(nullptr);
}
if (XRE_IsParentProcess()) { if (mActiveWindow == window) { // The window is already active, so there is no need to focus anything, // but make sure that the right widget is focused. This is a special case // for Windows because when restoring a minimized window, a second // activation will occur and the top-level widget could be focused instead // of the child we want. We solve this by calling SetFocus to ensure that // what the focus manager thinks should be the current widget is actually // focused.
EnsureCurrentWidgetFocused(CallerType::System); return;
}
// lower the existing window, if any. This shouldn't happen usually. if (nsCOMPtr<nsPIDOMWindowOuter> activeWindow = mActiveWindow) {
WindowLowered(activeWindow, aActionId);
}
} elseif (bc->IsTop()) {
BrowsingContext* active = GetActiveBrowsingContext(); if (active == bc && !mActiveBrowsingContextInContentSetFromOtherProcess) { // EnsureCurrentWidgetFocused() should not be necessary with // PuppetWidget. return;
}
if (active && active != bc) { if (active->IsInProcess()) {
nsCOMPtr<nsPIDOMWindowOuter> activeWindow = active->GetDOMWindow();
WindowLowered(activeWindow, aActionId);
} // No else, because trying to lower other-process windows // from here can result in the BrowsingContext no longer // existing in the parent process by the time it deserializes // the IPC message.
}
}
nsCOMPtr<nsIDocShellTreeItem> docShellAsItem = window->GetDocShell(); // If there's no docShellAsItem, this window must have been closed, // in that case there is no tree owner. if (!docShellAsItem) { return;
}
// set this as the active window if (XRE_IsParentProcess()) {
mActiveWindow = window;
} elseif (bc->IsTop()) {
SetActiveBrowsingContextInContent(bc, aActionId, false/* aIsEnteringBFCache */);
}
// ensure that the window is enabled and visible
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
docShellAsItem->GetTreeOwner(getter_AddRefs(treeOwner)); if (nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(treeOwner)) { bool isEnabled = true; if (NS_SUCCEEDED(baseWindow->GetEnabled(&isEnabled)) && !isEnabled) { return;
}
baseWindow->SetVisibility(true);
}
if (XRE_IsParentProcess()) { // Unsetting top-level focus upon lowering was inhibited to accommodate // ATOK, so we need to do it here.
BrowserParent::UnsetTopLevelWebFocusAll();
ActivateOrDeactivate(window, true);
}
// Retrieve the last focused element within the window that was raised.
MoveFocusToWindowAfterRaise(window, aActionId);
}
NS_ASSERTION(currentWindow, "window raised with no window current"); if (!currentWindow) { return;
}
// We use mFocusedWindow here is basically for the case that iframe navigate // from a.com to b.com for example, so it ends up being loaded in a different // process after Fission, but // currentWindow->GetBrowsingContext() == GetFocusedBrowsingContext() would // still be true because focused browsing context is synced, and we won't // fire a focus event while focusing if we use it as condition.
Focus(currentWindow, currentFocus, /* aFlags = */ 0, /* aIsNewDocument = */ currentWindow != mFocusedWindow, /* aFocusChanged = */ false, /* aWindowRaised = */ true, /* aAdjustWidget = */ true, aActionId);
}
if (MOZ_LOG_TEST(gFocusLog, LogLevel::Debug)) {
LOGFOCUS(("Window %p Lowered [Currently: %p %p]", aWindow,
mActiveWindow.get(), mFocusedWindow.get()));
Document* doc = window->GetExtantDoc(); if (doc && doc->GetDocumentURI()) {
LOGFOCUS((" Lowered Window: %s",
doc->GetDocumentURI()->GetSpecOrDefault().get()));
} if (mActiveWindow) {
doc = mActiveWindow->GetExtantDoc(); if (doc && doc->GetDocumentURI()) {
LOGFOCUS((" Active Window: %s",
doc->GetDocumentURI()->GetSpecOrDefault().get()));
}
}
}
if (XRE_IsParentProcess()) { if (mActiveWindow != window) { return;
}
} else {
BrowsingContext* bc = window->GetBrowsingContext();
BrowsingContext* active = GetActiveBrowsingContext(); if (active != bc->Top()) { return;
}
}
// clear the mouse capture as the active window has changed
PresShell::ReleaseCapturingContent();
// In addition, reset the drag state to ensure that we are no longer in // drag-select mode. if (mFocusedWindow) {
nsCOMPtr<nsIDocShell> docShell = mFocusedWindow->GetDocShell(); if (docShell) { if (PresShell* presShell = docShell->GetPresShell()) {
RefPtr<nsFrameSelection> frameSelection = presShell->FrameSelection();
frameSelection->SetDragState(false);
}
}
}
if (XRE_IsParentProcess()) {
ActivateOrDeactivate(window, false);
}
// keep track of the window being lowered, so that attempts to raise the // window can be prevented until we return. Otherwise, focus can get into // an unusual state.
mWindowBeingLowered = window; if (XRE_IsParentProcess()) {
mActiveWindow = nullptr;
} else {
BrowsingContext* bc = window->GetBrowsingContext(); if (bc == bc->Top()) {
SetActiveBrowsingContextInContent(nullptr, aActionId, false/* aIsEnteringBFCache */);
}
}
if (mFocusedWindow) {
Blur(nullptr, nullptr, true, true, false, aActionId);
}
nsPIDOMWindowOuter* windowPtr = aDocument->GetWindow(); if (!windowPtr) { return NS_OK;
}
// if the content is currently focused in the window, or is an // shadow-including inclusive ancestor of the currently focused element, // reset the focus within that window.
Element* previousFocusedElementPtr = windowPtr->GetFocusedElement(); if (!previousFocusedElementPtr) { return NS_OK;
}
if (!nsContentUtils::ContentIsHostIncludingDescendantOf(
previousFocusedElementPtr, aContent)) { return NS_OK;
}
RefPtr<Element> newFocusedElement = [&]() -> Element* { if (auto* sr = ShadowRoot::FromNode(aContent)) { if (sr->IsUAWidget() && sr->Host()->IsHTMLElement(nsGkAtoms::input)) { return sr->Host();
}
} return nullptr;
}();
window->SetFocusedElement(newFocusedElement);
// if this window is currently focused, clear the global focused // element as well, but don't fire any events. if (window->GetBrowsingContext() == GetFocusedBrowsingContext()) {
mFocusedElement = newFocusedElement;
} elseif (Document* subdoc =
aDocument->GetSubDocumentFor(previousFocusedElement)) { // Check if the node that was focused is an iframe or similar by looking if // it has a subdocument. This would indicate that this focused iframe // and its descendants will be going away. We will need to move the focus // somewhere else, so just clear the focus in the toplevel window so that no // element is focused. // // The Fission case is handled in FlushAndCheckIfFocusable(). if (nsCOMPtr<nsIDocShell> docShell = subdoc->GetDocShell()) {
nsCOMPtr<nsPIDOMWindowOuter> childWindow = docShell->GetWindow(); if (childWindow &&
IsSameOrAncestor(childWindow, GetFocusedBrowsingContext())) { if (XRE_IsParentProcess()) {
nsCOMPtr<nsPIDOMWindowOuter> activeWindow = mActiveWindow;
ClearFocus(activeWindow);
} else {
BrowsingContext* active = GetActiveBrowsingContext(); if (active) { if (active->IsInProcess()) {
nsCOMPtr<nsPIDOMWindowOuter> activeWindow =
active->GetDOMWindow();
ClearFocus(activeWindow);
} else {
mozilla::dom::ContentChild* contentChild =
mozilla::dom::ContentChild::GetSingleton();
MOZ_ASSERT(contentChild);
contentChild->SendClearFocus(active);
}
} // no else, because ClearFocus does nothing with nullptr
}
}
}
}
// Notify the editor in case we removed its ancestor limiter. if (previousFocusedElement->IsEditable()) { if (nsIDocShell* const docShell = aDocument->GetDocShell()) { if (HTMLEditor* const htmlEditor = docShell->GetHTMLEditor()) {
Selection* const selection = htmlEditor->GetSelection(); if (selection && selection->GetFrameSelection() &&
previousFocusedElement ==
selection->GetFrameSelection()->GetAncestorLimiter()) { // The editing host may be being removed right now. So, it's already // removed from the child chain of the parent node, but it still know // the parent node. This could cause unexpected result at scheduling // paint of the caret. Therefore, we should call FinalizeSelection // after unblocking to run the script.
nsContentUtils::AddScriptRunner(
NewRunnableMethod("HTMLEditor::FinalizeSelection", htmlEditor,
&HTMLEditor::FinalizeSelection));
}
}
}
}
if (!newFocusedElement) {
NotifyFocusStateChange(previousFocusedElement, newFocusedElement, 0, /* aGettingFocus = */ false, false);
} else { // We should already have the right state, which is managed by the <input> // widget.
MOZ_ASSERT(newFocusedElement->State().HasState(ElementState::FOCUS));
}
// If we changed focused element and the element still has focus, let's // notify IME of focus. Note that if new focus move has already occurred // by running script, we should not let IMEStateManager of outdated focus // change. if (mFocusedElement == newFocusedElement && mFocusedWindow == window) {
RefPtr<nsPresContext> presContext(aDocument->GetPresContext());
IMEStateManager::OnChangeFocus(presContext, newFocusedElement,
InputContextAction::Cause::CAUSE_UNKNOWN);
}
if (mFocusedWindow) {
doc = mFocusedWindow->GetExtantDoc(); if (doc && doc->GetDocumentURI()) {
LOGFOCUS((" Focused Window: %s",
doc->GetDocumentURI()->GetSpecOrDefault().get()));
}
}
}
if (XRE_IsParentProcess()) { if (BrowsingContext* bc = window->GetBrowsingContext()) { if (bc->IsTop()) {
bc->SetIsActiveBrowserWindow(bc->GetIsActiveBrowserWindow());
}
}
}
if (XRE_IsParentProcess()) { if (mFocusedWindow != window) { return;
}
} else {
BrowsingContext* bc = window->GetBrowsingContext(); if (!bc || mFocusedBrowsingContextInContent != bc) { return;
} // Sync the window for a newly-created OOP iframe // Set actionId to zero to signify that it should be ignored.
SetFocusedWindowInternal(window, 0, false);
}
if (currentWindow) {
Focus(currentWindow, currentFocus, 0, true, false, false, true,
GenerateFocusActionId());
}
} else { // Sometimes, an element in a window can be focused before the window is // visible, which would mean that the widget may not be properly focused. // When the window becomes visible, make sure the right widget is focused.
EnsureCurrentWidgetFocused(CallerType::System);
}
}
void nsFocusManager::WindowHidden(mozIDOMWindowProxy* aWindow,
uint64_t aActionId, bool aIsEnteringBFCache) { // if there is no window or it is not the same or an ancestor of the // currently focused window, just return, as the current focus will not // be affected.
if (mFocusedWindow) {
doc = mFocusedWindow->GetExtantDoc(); if (doc && doc->GetDocumentURI()) {
LOGFOCUS((" Focused Window: %s",
doc->GetDocumentURI()->GetSpecOrDefault().get()));
}
}
if (mActiveWindow) {
doc = mActiveWindow->GetExtantDoc(); if (doc && doc->GetDocumentURI()) {
LOGFOCUS((" Active Window: %s",
doc->GetDocumentURI()->GetSpecOrDefault().get()));
}
}
}
if (!IsSameOrAncestor(window, mFocusedWindow)) { return;
}
// at this point, we know that the window being hidden is either the focused // window, or an ancestor of the focused window. Either way, the focus is no // longer valid, so it needs to be updated.
// If a window is being "hidden" because its BrowsingContext is changing // remoteness, we don't want to handle docshell destruction by moving focus. // Instead, the focused browsing context should stay the way it is (so that // the newly "shown" window in the other process knows to take focus) and // we should just null out the process-local field.
nsCOMPtr<nsIDocShell> docShellBeingHidden = window->GetDocShell(); // Check if we're currently hiding a non-remote nsDocShell due to its // BrowsingContext navigating to become remote. Normally, when a focused // subframe is hidden, focus is moved to the frame element, but focus should // stay with the BrowsingContext when performing a process switch. We don't // need to consider process switches where the hiding docshell is already // remote (ie. GetEmbedderElement is nullptr), as shifting remoteness to the // frame element is handled elsewhere. if (docShellBeingHidden &&
nsDocShell::Cast(docShellBeingHidden)->WillChangeProcess() &&
docShellBeingHidden->GetBrowsingContext()->GetEmbedderElement()) { if (mFocusedWindow != window) { // The window being hidden is an ancestor of the focused window. #ifdef DEBUG
BrowsingContext* ancestor = window->GetBrowsingContext();
BrowsingContext* bc = mFocusedWindow->GetBrowsingContext(); for (;;) { if (!bc) {
MOZ_ASSERT(false, "Should have found ancestor");
}
bc = bc->GetParent(); if (ancestor == bc) { break;
}
} #endif // This call adjusts the focused browsing context and window. // The latter gets nulled out immediately below.
SetFocusedWindowInternal(window, aActionId);
}
mFocusedWindow = nullptr;
window->SetFocusedElement(nullptr); return;
}
// if the docshell being hidden is being destroyed, then we want to move // focus somewhere else. Call ClearFocus on the toplevel window, which // will have the effect of clearing the focus and moving the focused window // to the toplevel window. But if the window isn't being destroyed, we are // likely just loading a new document in it, so we want to maintain the // focused window so that the new document gets properly focused. bool beingDestroyed = !docShellBeingHidden; if (docShellBeingHidden) {
docShellBeingHidden->IsBeingDestroyed(&beingDestroyed);
} if (beingDestroyed) { // There is usually no need to do anything if a toplevel window is going // away, as we assume that WindowLowered will be called. However, this may // not happen if nsIAppStartup::eForceQuit is used to quit, and can cause // a leak. So if the active window is being destroyed, call WindowLowered // directly.
if (XRE_IsParentProcess()) {
nsCOMPtr<nsPIDOMWindowOuter> activeWindow = mActiveWindow; if (activeWindow == mFocusedWindow || activeWindow == window) {
WindowLowered(activeWindow, aActionId);
} else {
ClearFocus(activeWindow);
}
} else {
BrowsingContext* active = GetActiveBrowsingContext(); if (active) { if (nsCOMPtr<nsPIDOMWindowOuter> activeWindow =
active->GetDOMWindow()) { if ((mFocusedWindow &&
mFocusedWindow->GetBrowsingContext() == active) ||
(window->GetBrowsingContext() == active)) {
WindowLowered(activeWindow, aActionId);
} else {
ClearFocus(activeWindow);
}
} // else do nothing when an out-of-process iframe is torn down
}
} return;
}
// if the window being hidden is an ancestor of the focused window, adjust // the focused window so that it points to the one being hidden. This // ensures that the focused window isn't in a chain of frames that doesn't // exist any more. if (window != mFocusedWindow) {
nsCOMPtr<nsIDocShellTreeItem> dsti =
mFocusedWindow ? mFocusedWindow->GetDocShell() : nullptr; if (dsti) {
nsCOMPtr<nsIDocShellTreeItem> parentDsti;
dsti->GetInProcessParent(getter_AddRefs(parentDsti)); if (parentDsti) { if (nsCOMPtr<nsPIDOMWindowOuter> parentWindow =
parentDsti->GetWindow()) {
parentWindow->SetFocusedElement(nullptr);
}
}
}
// fire any delayed focus and blur events in the same order that they were // added for (uint32_t i = 0; i < mDelayedBlurFocusEvents.Length(); i++) { if (mDelayedBlurFocusEvents[i].mDocument == aDocument) { if (!aDocument->GetInnerWindow() ||
!aDocument->GetInnerWindow()->IsCurrentInnerWindow()) { // If the document was navigated away from or is defunct, don't bother // firing events on it. Note the symmetry between this condition and // the similar one in Document.cpp:FireOrClearDelayedEvents.
mDelayedBlurFocusEvents.RemoveElementAt(i);
--i;
} elseif (!aDocument->EventHandlingSuppressed()) {
EventMessage message = mDelayedBlurFocusEvents[i].mEventMessage;
nsCOMPtr<EventTarget> target = mDelayedBlurFocusEvents[i].mTarget;
RefPtr<PresShell> presShell = mDelayedBlurFocusEvents[i].mPresShell;
nsCOMPtr<EventTarget> relatedTarget =
mDelayedBlurFocusEvents[i].mRelatedTarget;
mDelayedBlurFocusEvents.RemoveElementAt(i);
// https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo staticbool ShouldMatchFocusVisible(nsPIDOMWindowOuter* aWindow, const Element& aElement,
int32_t aFocusFlags) { // If we were explicitly requested to show the ring, do it. if (aFocusFlags & nsIFocusManager::FLAG_SHOWRING) { returntrue;
}
if (aFocusFlags & nsIFocusManager::FLAG_NOSHOWRING) { returnfalse;
}
if (aWindow->ShouldShowFocusRing()) { // The window decision also trumps any other heuristic. returntrue;
}
// Any element which supports keyboard input (such as an input element, or any // other element which may trigger a virtual keyboard to be shown on focus if // a physical keyboard is not present) should always match :focus-visible when // focused.
{ if (aElement.IsHTMLElement(nsGkAtoms::textarea) || aElement.IsEditable()) { returntrue;
}
if (auto* input = HTMLInputElement::FromNode(aElement)) { if (input->IsSingleLineTextControl()) { returntrue;
}
}
}
switch (nsFocusManager::GetFocusMoveActionCause(aFocusFlags)) { case InputContextAction::CAUSE_KEY: // If the user interacts with the page via the keyboard, the currently // focused element should match :focus-visible (i.e. keyboard usage may // change whether this pseudo-class matches even if it doesn't affect // :focus). returntrue; case InputContextAction::CAUSE_UNKNOWN: // We render outlines if the last "known" focus method was by key or there // was no previous known focus method, otherwise we don't. return aWindow->UnknownFocusMethodShouldShowOutline(); case InputContextAction::CAUSE_MOUSE: case InputContextAction::CAUSE_TOUCH: case InputContextAction::CAUSE_LONGPRESS: // If the user interacts with the page via a pointing device, such that // the focus is moved to a new element which does not support user input, // the newly focused element should not match :focus-visible. returnfalse; case InputContextAction::CAUSE_UNKNOWN_CHROME: case InputContextAction::CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT: case InputContextAction::CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT: // TODO(emilio): We could return some of these though, looking at // UserActivation. We may want to suppress focus rings for unknown / // programatic focus if the user is interacting with the page but not // during keyboard input, or such.
MOZ_ASSERT_UNREACHABLE( "These don't get returned by GetFocusMoveActionCause"); break;
} returnfalse;
}
// Special case for <input type="checkbox"> and <input type="radio">. // The other browsers cancel active state when they gets lost focus, but // does not do it for the other elements such as <button> and <a href="...">. // Additionally, they may be activated with <label>, but they will get focus // at `click`, but activated at `mousedown`. Therefore, we need to cancel // active state at moving focus. if (RefPtr<nsPresContext> presContext =
aElement->GetPresContext(Element::PresContextFor::eForComposedDoc)) {
RefPtr<EventStateManager> esm = presContext->EventStateManager(); auto* activeInputElement =
HTMLInputElement::FromNodeOrNull(esm->GetActiveContent()); if (activeInputElement &&
(activeInputElement->ControlType() == FormControlType::InputCheckbox ||
activeInputElement->ControlType() == FormControlType::InputRadio) &&
!activeInputElement->State().HasState(ElementState::FOCUS)) {
esm->SetContentState(nullptr, ElementState::ACTIVE);
}
}
for (nsIContent* content = aElement; content && content != commonAncestor;
content = content->GetFlattenedTreeParent()) {
Element* element = Element::FromNode(content); if (!element) { continue;
}
if (aGettingFocus) { if (element->State().HasState(ElementState::FOCUS_WITHIN)) { break;
}
element->AddStates(ElementState::FOCUS_WITHIN);
} else {
element->RemoveStates(ElementState::FOCUS_WITHIN);
}
}
}
// get the main child widget for the focused window and ensure that the // platform knows that this widget is focused.
nsCOMPtr<nsIDocShell> docShell = mFocusedWindow->GetDocShell(); if (!docShell) { return;
}
RefPtr<PresShell> presShell = docShell->GetPresShell(); if (!presShell) { return;
}
nsViewManager* vm = presShell->GetViewManager(); if (!vm) { return;
}
nsCOMPtr<nsIWidget> widget = vm->GetRootWidget(); if (!widget) { return;
}
widget->SetFocus(nsIWidget::Raise::No, aCallerType);
}
// Retrieves innerWindowId of the window of the last focused element to // log a warning to the website console. void LogWarningFullscreenWindowRaise(Element* aElement) {
nsCOMPtr<nsFrameLoaderOwner> frameLoaderOwner(do_QueryInterface(aElement));
NS_ENSURE_TRUE_VOID(frameLoaderOwner);
// Ensure that when an embedded popup with a noautofocus attribute // like a date picker is opened and focused, the parent page does not blur staticbool IsEmeddededInNoautofocusPopup(BrowsingContext& aBc) { auto* embedder = aBc.GetEmbedderElement(); if (!embedder) { returnfalse;
}
nsIFrame* f = embedder->GetPrimaryFrame(); if (!f || !f->HasAnyStateBits(NS_FRAME_IN_POPUP)) { returnfalse;
}
Maybe<uint64_t> nsFocusManager::SetFocusInner(Element* aNewContent,
int32_t aFlags, bool aFocusChanged, bool aAdjustWidget) { // if the element is not focusable, just return and leave the focus as is
RefPtr<Element> elementToFocus =
FlushAndCheckIfFocusable(aNewContent, aFlags); if (!elementToFocus) { return Nothing();
}
// check if the element to focus is a frame (iframe) containing a child // document. Frames are never directly focused; instead focusing a frame // means focus what is inside the frame. To do this, the descendant content // within the frame is retrieved and that will be focused instead.
nsCOMPtr<nsPIDOMWindowOuter> newWindow;
nsCOMPtr<nsPIDOMWindowOuter> subWindow = GetContentWindow(elementToFocus); if (subWindow) {
elementToFocus = GetFocusedDescendant(subWindow, eIncludeAllDescendants,
getter_AddRefs(newWindow));
// since a window is being refocused, clear aFocusChanged so that the // caret position isn't updated.
aFocusChanged = false;
}
// unless it was set above, retrieve the window for the element to focus if (!newWindow) {
newWindow = GetCurrentWindow(elementToFocus);
}
RefPtr<BrowsingContext> newBrowsingContext; if (newWindow) {
newBrowsingContext = newWindow->GetBrowsingContext();
}
// if the element is already focused, just return. Note that this happens // after the frame check above so that we compare the element that will be // focused rather than the frame it is in. if (!newWindow || (newBrowsingContext == GetFocusedBrowsingContext() &&
elementToFocus == mFocusedElement)) { return Nothing();
}
MOZ_ASSERT(newBrowsingContext);
BrowsingContext* browsingContextToFocus = newBrowsingContext; if (RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(elementToFocus)) { // Only look at pre-existing browsing contexts. If this function is // called during reflow, calling GetBrowsingContext() could cause frame // loader initialization at a time when it isn't safe. if (BrowsingContext* bc = flo->GetExtantBrowsingContext()) { // If focus is already in the subtree rooted at bc, return early // to match the single-process focus semantics. Otherwise, we'd // blur and immediately refocus whatever is focused.
BrowsingContext* walk = focusedBrowsingContext; while (walk) { if (walk == bc) { return Nothing();
}
walk = walk->GetParent();
}
browsingContextToFocus = bc;
}
}
// don't allow focus to be placed in docshells or descendants of docshells // that are being destroyed. Also, ensure that the page hasn't been // unloaded. The prevents content from being refocused during an unload event.
nsCOMPtr<nsIDocShell> newDocShell = newWindow->GetDocShell();
nsCOMPtr<nsIDocShell> docShell = newDocShell; while (docShell) { bool inUnload;
docShell->GetIsInUnload(&inUnload); if (inUnload) { return Nothing();
}
bool beingDestroyed;
docShell->IsBeingDestroyed(&beingDestroyed); if (beingDestroyed) { return Nothing();
}
BrowsingContext* bc = docShell->GetBrowsingContext();
nsCOMPtr<nsIDocShellTreeItem> parentDsti;
docShell->GetInProcessParent(getter_AddRefs(parentDsti));
docShell = do_QueryInterface(parentDsti); if (!docShell && !XRE_IsParentProcess()) { // We don't have an in-process parent, but let's see if we have // an in-process ancestor or if an out-of-process ancestor // is discarded. do {
bc = bc->GetParent(); if (bc && bc->IsDiscarded()) { return Nothing();
}
} while (bc && !bc->IsInProcess()); if (bc) {
docShell = bc->GetDocShell();
} else {
docShell = nullptr;
}
}
}
if (focusedBrowsingContext && focusMovesToDifferentBC &&
nsContentUtils::IsHandlingKeyBoardEvent() &&
!nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
MOZ_ASSERT(browsingContextToFocus, "BrowsingContext to focus should be non-null.");
if (!focusedPrincipal || !newPrincipal) { return Nothing();
}
if (!focusedPrincipal->Subsumes(newPrincipal)) {
NS_WARNING("Not allowed to focus the new window!"); return Nothing();
}
}
// to check if the new element is in the active window, compare the // new root docshell for the new element with the active window's docshell.
RefPtr<BrowsingContext> newRootBrowsingContext = nullptr; bool isElementInActiveWindow = false; if (XRE_IsParentProcess()) {
nsCOMPtr<nsPIDOMWindowOuter> newRootWindow = nullptr;
nsCOMPtr<nsIDocShellTreeItem> dsti = newWindow->GetDocShell(); if (dsti) {
nsCOMPtr<nsIDocShellTreeItem> root;
dsti->GetInProcessRootTreeItem(getter_AddRefs(root));
newRootWindow = root ? root->GetWindow() : nullptr;
isElementInActiveWindow =
(mActiveWindow && newRootWindow == mActiveWindow);
} if (newRootWindow) {
newRootBrowsingContext = newRootWindow->GetBrowsingContext();
}
} else { // XXX This is wrong for `<iframe mozbrowser>` and for XUL // `<browser remote="true">`. See: // https://searchfox.org/mozilla-central/rev/8a63fc190b39ed6951abb4aef4a56487a43962bc/dom/base/nsFrameLoader.cpp#229-232
newRootBrowsingContext = newBrowsingContext->Top(); // to check if the new element is in the active window, compare the // new root docshell for the new element with the active window's docshell.
isElementInActiveWindow =
(GetActiveBrowsingContext() == newRootBrowsingContext);
}
// Exit fullscreen if a website focuses another window if (StaticPrefs::full_screen_api_exit_on_windowRaise() &&
!isElementInActiveWindow && (aFlags & FLAG_RAISE)) { if (XRE_IsParentProcess()) { if (Document* doc = mActiveWindow ? mActiveWindow->GetDoc() : nullptr) {
Document::ClearPendingFullscreenRequests(doc); if (doc->GetFullscreenElement()) {
LogWarningFullscreenWindowRaise(mFocusedElement);
Document::AsyncExitFullscreen(doc);
}
}
} else {
BrowsingContext* activeBrowsingContext = GetActiveBrowsingContext(); if (activeBrowsingContext) {
nsIDocShell* shell = activeBrowsingContext->GetDocShell(); if (shell) { if (Document* doc = shell->GetDocument()) {
Document::ClearPendingFullscreenRequests(doc); if (doc->GetFullscreenElement()) {
Document::AsyncExitFullscreen(doc);
}
}
} else {
mozilla::dom::ContentChild* contentChild =
mozilla::dom::ContentChild::GetSingleton();
MOZ_ASSERT(contentChild);
contentChild->SendMaybeExitFullscreen(activeBrowsingContext);
}
}
}
}
// if the FLAG_NOSWITCHFRAME flag is used, only allow the focus to be // shifted away from the current element if the new shell to focus is // the same or an ancestor shell of the currently focused shell. bool allowFrameSwitch = !(aFlags & FLAG_NOSWITCHFRAME) ||
IsSameOrAncestor(newWindow, focusedBrowsingContext);
// if the element is in the active window, frame switching is allowed and // the content is in a visible window, fire blur and focus events. bool sendFocusEvent =
isElementInActiveWindow && allowFrameSwitch && IsWindowVisible(newWindow);
// Don't allow to steal the focus from chrome nodes if the caller cannot // access them. if (sendFocusEvent && mFocusedElement &&
mFocusedElement->OwnerDoc() != aNewContent->OwnerDoc() &&
mFocusedElement->NodePrincipal()->IsSystemPrincipal() &&
!nsContentUtils::LegacyIsCallerNativeCode() &&
!nsContentUtils::CanCallerAccess(mFocusedElement)) {
sendFocusEvent = false;
}
LOGCONTENT("Shift Focus: %s", elementToFocus.get());
LOGFOCUS((" Flags: %x Current Window: %p New Window: %p Current Element: %p",
aFlags, mFocusedWindow.get(), newWindow.get(),
mFocusedElement.get())); const uint64_t actionId = GenerateFocusActionId();
LOGFOCUS(
(" In Active Window: %d Moves to different BrowsingContext: %d " "SendFocus: %d actionid: %" PRIu64,
isElementInActiveWindow, focusMovesToDifferentBC, sendFocusEvent,
actionId));
if (sendFocusEvent) {
Maybe<BlurredElementInfo> blurredInfo; if (mFocusedElement) {
--> --------------------
--> maximum size reached
--> --------------------
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.36Angebot
Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.