From ac7d3f7b2a7328ce38fb49252604355823d6df54 Mon Sep 17 00:00:00 2001 From: Henri Sivonen Date: Tue, 18 Feb 2020 13:30:04 +0000 Subject: [PATCH] Bug 1556627 - Make nsFocusManager::SetFocus work in Fission. r=NeilDeakin,farre,masayuki Differential Revision: https://phabricator.services.mozilla.com/D55651 --HG-- extra : moz-landing-system : lando --- browser/base/content/test/tabs/browser.ini | 1 + .../extensions/test/browser/browser.ini | 1 + docshell/base/BrowsingContext.cpp | 13 + docshell/base/BrowsingContextGroup.cpp | 22 + docshell/base/CanonicalBrowsingContext.cpp | 1 + dom/base/nsFocusManager.cpp | 693 ++++++++++++++---- dom/base/nsFocusManager.h | 183 ++++- dom/base/test/mochitest.ini | 2 +- .../tests/mochitest/mochitest.ini | 1 + dom/events/IMEStateManager.cpp | 7 +- dom/html/test/mochitest.ini | 1 + dom/ipc/BrowserParent.cpp | 189 +++-- dom/ipc/BrowserParent.h | 29 +- dom/ipc/ContentChild.cpp | 147 ++++ dom/ipc/ContentChild.h | 19 + dom/ipc/ContentParent.cpp | 166 +++++ dom/ipc/ContentParent.h | 17 + dom/ipc/PContent.ipdl | 21 + dom/media/tests/mochitest/mochitest.ini | 2 +- dom/webauthn/tests/mochitest.ini | 5 + dom/xul/test/mochitest.ini | 2 +- ...out-user-activation-tentative.sub.html.ini | 1 - .../same-origin-autofocus.html.ini | 5 - .../passwordmgr/test/mochitest/mochitest.ini | 1 - toolkit/content/tests/mochitest/mochitest.ini | 2 +- toolkit/content/tests/widgets/mochitest.ini | 2 +- 26 files changed, 1276 insertions(+), 257 deletions(-) delete mode 100644 testing/web-platform/meta/html/interaction/focus/the-autofocus-attribute/same-origin-autofocus.html.ini diff --git a/browser/base/content/test/tabs/browser.ini b/browser/base/content/test/tabs/browser.ini index 71e8bf835a91..586de63365f0 100644 --- a/browser/base/content/test/tabs/browser.ini +++ b/browser/base/content/test/tabs/browser.ini @@ -87,6 +87,7 @@ support-files = file_anchor_elements.html skip-if = (verify && (os == 'win' || os == 'mac')) [browser_preloadedBrowser_zoom.js] [browser_progress_keyword_search_handling.js] +skip-if = fission #Bug 1613589 [browser_reload_deleted_file.js] skip-if = (debug && os == 'mac') || (debug && os == 'linux' && bits == 64) #Bug 1421183, disabled on Linux/OSX for leaked windows [browser_tabCloseSpacer.js] diff --git a/browser/components/extensions/test/browser/browser.ini b/browser/components/extensions/test/browser/browser.ini index ba871dee8512..9dde83d523a4 100644 --- a/browser/components/extensions/test/browser/browser.ini +++ b/browser/components/extensions/test/browser/browser.ini @@ -186,6 +186,7 @@ skip-if = !e10s || !crashreporter # the tab's process is killed during the test. [browser_ext_runtime_openOptionsPage.js] [browser_ext_runtime_openOptionsPage_uninstall.js] [browser_ext_search.js] +skip-if = fission #Bug 1613590 [browser_ext_search_favicon.js] [browser_ext_runtime_setUninstallURL.js] [browser_ext_sessions_forgetClosedTab.js] diff --git a/docshell/base/BrowsingContext.cpp b/docshell/base/BrowsingContext.cpp index 1d49daf23453..ed3e6a881e39 100644 --- a/docshell/base/BrowsingContext.cpp +++ b/docshell/base/BrowsingContext.cpp @@ -459,6 +459,13 @@ void BrowsingContext::Detach(bool aFromIPC) { mGroup->Unregister(this); mIsDiscarded = true; + if (XRE_IsParentProcess()) { + nsFocusManager* fm = nsFocusManager::GetFocusManager(); + if (fm) { + fm->BrowsingContextDetached(this); + } + } + if (nsCOMPtr obs = services::GetObserverService()) { obs->NotifyObservers(ToSupports(this), "browsing-context-discarded", nullptr); @@ -577,6 +584,9 @@ void BrowsingContext::UnregisterWindowContext(WindowContext* aWindow) { // double-check. if (aWindow == mCurrentWindowContext) { mCurrentWindowContext = nullptr; + if (XRE_IsParentProcess()) { + BrowserParent::UpdateFocusFromBrowsingContext(); + } } } @@ -1421,6 +1431,9 @@ bool BrowsingContext::CanSet(FieldIndex, void BrowsingContext::DidSet(FieldIndex) { mCurrentWindowContext = WindowContext::GetById(GetCurrentInnerWindowId()); + if (XRE_IsParentProcess()) { + BrowserParent::UpdateFocusFromBrowsingContext(); + } } bool BrowsingContext::CanSet(FieldIndex, const bool& aValue, diff --git a/docshell/base/BrowsingContextGroup.cpp b/docshell/base/BrowsingContextGroup.cpp index 58d7cc511ffd..82f763734219 100644 --- a/docshell/base/BrowsingContextGroup.cpp +++ b/docshell/base/BrowsingContextGroup.cpp @@ -10,6 +10,7 @@ #include "mozilla/dom/ContentParent.h" #include "mozilla/StaticPrefs_dom.h" #include "mozilla/ThrottledEventQueue.h" +#include "nsFocusManager.h" namespace mozilla { namespace dom { @@ -66,11 +67,27 @@ void BrowsingContextGroup::EnsureSubscribed(ContentParent* aProcess) { Subscribe(aProcess); + bool sendFocused = false; + bool sendActive = false; + BrowsingContext* focused = nullptr; + BrowsingContext* active = nullptr; + nsFocusManager* fm = nsFocusManager::GetFocusManager(); + if (fm) { + focused = fm->GetFocusedBrowsingContextInChrome(); + active = fm->GetActiveBrowsingContextInChrome(); + } + nsTArray inits(mContexts.Count()); nsTArray windowInits(mContexts.Count()); auto addInits = [&](BrowsingContext* aContext) { inits.AppendElement(aContext->GetIPCInitializer()); + if (focused == aContext) { + sendFocused = true; + } + if (active == aContext) { + sendActive = true; + } for (auto& window : aContext->GetWindowContexts()) { windowInits.AppendElement(window->GetIPCInitializer()); } @@ -96,6 +113,11 @@ void BrowsingContextGroup::EnsureSubscribed(ContentParent* aProcess) { // Send all of our contexts to the target content process. Unused << aProcess->SendRegisterBrowsingContextGroup(inits, windowInits); + + if (sendActive || sendFocused) { + Unused << aProcess->SendSetupFocusedAndActive( + sendFocused ? focused : nullptr, sendActive ? active : nullptr); + } } bool BrowsingContextGroup::IsContextCached(BrowsingContext* aContext) const { diff --git a/docshell/base/CanonicalBrowsingContext.cpp b/docshell/base/CanonicalBrowsingContext.cpp index b3477a18b990..daaec1ae9160 100644 --- a/docshell/base/CanonicalBrowsingContext.cpp +++ b/docshell/base/CanonicalBrowsingContext.cpp @@ -6,6 +6,7 @@ #include "mozilla/dom/CanonicalBrowsingContext.h" +#include "mozilla/dom/BrowserParent.h" #include "mozilla/dom/BrowsingContextGroup.h" #include "mozilla/dom/WindowGlobalParent.h" #include "mozilla/dom/ContentProcessManager.h" diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp index 650735a4e193..bddbcabac46a 100644 --- a/dom/base/nsFocusManager.cpp +++ b/dom/base/nsFocusManager.cpp @@ -44,6 +44,7 @@ #include "mozilla/AccessibleCaretEventHub.h" #include "mozilla/ContentEvents.h" +#include "mozilla/dom/ContentChild.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/ElementBinding.h" #include "mozilla/dom/HTMLImageElement.h" @@ -52,6 +53,7 @@ #include "mozilla/dom/BrowserBridgeChild.h" #include "mozilla/dom/Text.h" #include "mozilla/dom/WindowGlobalParent.h" +#include "mozilla/dom/WindowGlobalChild.h" #include "mozilla/EventDispatcher.h" #include "mozilla/EventStateManager.h" #include "mozilla/EventStates.h" @@ -152,9 +154,13 @@ NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFocusManager) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFocusManager) -NS_IMPL_CYCLE_COLLECTION(nsFocusManager, mActiveWindow, mFocusedWindow, - mFocusedElement, mFirstBlurEvent, mFirstFocusEvent, - mWindowBeingLowered, mDelayedBlurFocusEvents) +NS_IMPL_CYCLE_COLLECTION(nsFocusManager, mActiveWindow, + mActiveBrowsingContextInContent, + mActiveBrowsingContextInChrome, mFocusedWindow, + mFocusedBrowsingContextInContent, + mFocusedBrowsingContextInChrome, mFocusedElement, + mFirstBlurEvent, mFirstFocusEvent, mWindowBeingLowered, + mDelayedBlurFocusEvents) nsFocusManager* nsFocusManager::sInstance = nullptr; bool nsFocusManager::sMouseFocusesFormControl = false; @@ -232,7 +238,11 @@ nsFocusManager::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) { if (!nsCRT::strcmp(aTopic, "xpcom-shutdown")) { mActiveWindow = nullptr; + mActiveBrowsingContextInContent = nullptr; + mActiveBrowsingContextInChrome = nullptr; mFocusedWindow = nullptr; + mFocusedBrowsingContextInContent = nullptr; + mFocusedBrowsingContextInChrome = nullptr; mFocusedElement = nullptr; mFirstBlurEvent = nullptr; mFirstFocusEvent = nullptr; @@ -541,9 +551,10 @@ nsFocusManager::ClearFocus(mozIDOMWindowProxy* aWindow) { NS_ENSURE_TRUE(aWindow, NS_ERROR_INVALID_ARG); nsCOMPtr window = nsPIDOMWindowOuter::From(aWindow); - if (IsSameOrAncestor(window, mFocusedWindow)) { - bool isAncestor = (window != mFocusedWindow); - if (Blur(window, nullptr, isAncestor, true)) { + if (IsSameOrAncestor(window, GetFocusedBrowsingContext())) { + BrowsingContext* bc = window->GetBrowsingContext(); + bool isAncestor = (GetFocusedBrowsingContext() != bc); + if (Blur(bc, nullptr, isAncestor, true)) { // 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) Focus(window, nullptr, 0, true, false, false, true); @@ -632,20 +643,44 @@ nsFocusManager::WindowRaised(mozIDOMWindowProxy* aWindow) { } } - 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 NS_OK; - } + 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 NS_OK; + } - // lower the existing window, if any. This shouldn't happen usually. - if (mActiveWindow) WindowLowered(mActiveWindow); + // lower the existing window, if any. This shouldn't happen usually. + if (mActiveWindow) { + WindowLowered(mActiveWindow); + } + } else { + BrowsingContext* bc = window->GetBrowsingContext(); + if (bc == bc->Top()) { + BrowsingContext* active = GetActiveBrowsingContext(); + if (active == bc) { + // EnsureCurrentWidgetFocused() should not be necessary with + // PuppetWidget. + return NS_OK; + } + + if (active) { + if (active->IsInProcess()) { + WindowLowered(active->GetDOMWindow()); + } + // 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 docShellAsItem = window->GetDocShell(); // If there's no docShellAsItem, this window must have been closed, @@ -654,6 +689,12 @@ nsFocusManager::WindowRaised(mozIDOMWindowProxy* aWindow) { // set this as the active window mActiveWindow = window; + if (!XRE_IsParentProcess()) { + BrowsingContext* bc = window->GetBrowsingContext(); + if (bc == bc->Top()) { + SetActiveBrowsingContextInContent(bc); + } + } // ensure that the window is enabled and visible nsCOMPtr treeOwner; @@ -672,9 +713,9 @@ nsFocusManager::WindowRaised(mozIDOMWindowProxy* aWindow) { // Events for child process windows will be sent when ParentActivated // is called. if (XRE_IsParentProcess()) { - // Popping upon lowering was inhibited to accommodate ATOK, - // so we need to do it here. - BrowserParent::PopFocusAll(); + // Unsetting top-level focus upon lowering was inhibited to accommodate + // ATOK, so we need to do it here. + BrowserParent::UnsetTopLevelWebFocusAll(); ActivateOrDeactivate(window, true); } @@ -745,6 +786,12 @@ nsFocusManager::WindowLowered(mozIDOMWindowProxy* aWindow) { // an unusual state. mWindowBeingLowered = mActiveWindow; mActiveWindow = nullptr; + if (!XRE_IsParentProcess()) { + BrowsingContext* bc = window->GetBrowsingContext(); + if (bc == bc->Top()) { + SetActiveBrowsingContextInContent(nullptr); + } + } if (mFocusedWindow) Blur(nullptr, nullptr, true, true); @@ -772,7 +819,7 @@ nsresult nsFocusManager::ContentRemoved(Document* aDocument, // if this window is currently focused, clear the global focused // element as well, but don't fire any events. - if (window == mFocusedWindow) { + if (window->GetBrowsingContext() == GetFocusedBrowsingContext()) { mFocusedElement = nullptr; } else { // Check if the node that was focused is an iframe or similar by looking @@ -780,13 +827,31 @@ nsresult nsFocusManager::ContentRemoved(Document* aDocument, // 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. + // + // This check does not work correctly in Fission: + // https://bugzilla.mozilla.org/show_bug.cgi?id=1613054 Document* subdoc = aDocument->GetSubDocumentFor(content); if (subdoc) { nsCOMPtr docShell = subdoc->GetDocShell(); if (docShell) { nsCOMPtr childWindow = docShell->GetWindow(); - if (childWindow && IsSameOrAncestor(childWindow, mFocusedWindow)) { - ClearFocus(mActiveWindow); + if (childWindow && + IsSameOrAncestor(childWindow, GetFocusedBrowsingContext())) { + if (XRE_IsParentProcess()) { + ClearFocus(mActiveWindow); + } else { + BrowsingContext* active = GetActiveBrowsingContext(); + if (active) { + if (active->IsInProcess()) { + ClearFocus(active->GetDOMWindow()); + } else { + mozilla::dom::ContentChild* contentChild = + mozilla::dom::ContentChild::GetSingleton(); + MOZ_ASSERT(contentChild); + contentChild->SendClearFocus(active); + } + } // no else, because ClearFocus does nothing with nullptr + } } } } @@ -1162,6 +1227,8 @@ void nsFocusManager::SetFocusInner(Element* aNewContent, int32_t aFlags, return; } + RefPtr focusedBrowsingContext = GetFocusedBrowsingContext(); + // 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 @@ -1169,6 +1236,8 @@ void nsFocusManager::SetFocusInner(Element* aNewContent, int32_t aFlags, nsCOMPtr newWindow; nsCOMPtr subWindow = GetContentWindow(elementToFocus); if (subWindow) { + // XXX What if this is an out-of-process iframe? + // https://bugzilla.mozilla.org/show_bug.cgi?id=1613054 elementToFocus = GetFocusedDescendant(subWindow, eIncludeAllDescendants, getter_AddRefs(newWindow)); // since a window is being refocused, clear aFocusChanged so that the @@ -1185,7 +1254,8 @@ void nsFocusManager::SetFocusInner(Element* aNewContent, int32_t aFlags, // after the frame check above so that we compare the element that will be // focused rather than the frame it is in. if (!newWindow || - (newWindow == mFocusedWindow && elementToFocus == mFocusedElement)) { + (newWindow->GetBrowsingContext() == GetFocusedBrowsingContext() && + elementToFocus == mFocusedElement)) { return; } @@ -1197,50 +1267,102 @@ void nsFocusManager::SetFocusInner(Element* aNewContent, int32_t aFlags, while (docShell) { bool inUnload; docShell->GetIsInUnload(&inUnload); - if (inUnload) return; + if (inUnload) { + return; + } bool beingDestroyed; docShell->IsBeingDestroyed(&beingDestroyed); - if (beingDestroyed) return; + if (beingDestroyed) { + return; + } + + BrowsingContext* bc = docShell->GetBrowsingContext(); nsCOMPtr 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; + } + } while (bc && !bc->IsInProcess()); + if (bc) { + docShell = bc->GetDocShell(); + } else { + docShell = nullptr; + } + } } // if the new element is in the same window as the currently focused element - bool isElementInFocusedWindow = (mFocusedWindow == newWindow); + bool isElementInFocusedWindow = + (focusedBrowsingContext == newWindow->GetBrowsingContext()); - if (!isElementInFocusedWindow && mFocusedWindow && newWindow && + if (!isElementInFocusedWindow && newWindow && nsContentUtils::IsHandlingKeyBoardEvent()) { - nsCOMPtr focused = - do_QueryInterface(mFocusedWindow); - nsCOMPtr newFocus = do_QueryInterface(newWindow); - nsIPrincipal* focusedPrincipal = focused->GetPrincipal(); - nsIPrincipal* newPrincipal = newFocus->GetPrincipal(); - if (!focusedPrincipal || !newPrincipal) { - return; - } - bool subsumes = false; - focusedPrincipal->Subsumes(newPrincipal, &subsumes); - if (!subsumes && !nsContentUtils::LegacyIsCallerChromeOrNativeCode()) { - NS_WARNING("Not allowed to focus the new window!"); + if (XRE_IsParentProcess() || + (focusedBrowsingContext && focusedBrowsingContext->IsInProcess())) { + // need to null-check for the XRE_IsParentProcess case + if (focusedBrowsingContext) { + nsCOMPtr focusedWindow = + focusedBrowsingContext->GetDOMWindow(); + MOZ_ASSERT(focusedWindow, + "BrowsingContext should always have a window here."); + nsCOMPtr focused = + do_QueryInterface(focusedWindow); + nsCOMPtr newFocus = + do_QueryInterface(newWindow); + nsIPrincipal* focusedPrincipal = focused->GetPrincipal(); + nsIPrincipal* newPrincipal = newFocus->GetPrincipal(); + if (!focusedPrincipal || !newPrincipal) { + return; + } + bool subsumes = false; + focusedPrincipal->Subsumes(newPrincipal, &subsumes); + if (!subsumes && !nsContentUtils::LegacyIsCallerChromeOrNativeCode()) { + NS_WARNING("Not allowed to focus the new window!"); + return; + } + } + } else if (focusedBrowsingContext && + !nsContentUtils::LegacyIsCallerChromeOrNativeCode()) { return; } } // 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 newRootBrowsingContext = nullptr; bool isElementInActiveWindow = false; + if (XRE_IsParentProcess()) { + nsCOMPtr newRootWindow = nullptr; + nsCOMPtr dsti = newWindow->GetDocShell(); + if (dsti) { + nsCOMPtr root; + dsti->GetInProcessRootTreeItem(getter_AddRefs(root)); + newRootWindow = root ? root->GetWindow() : nullptr; - nsCOMPtr dsti = newWindow->GetDocShell(); - nsCOMPtr newRootWindow; - if (dsti) { - nsCOMPtr root; - dsti->GetInProcessRootTreeItem(getter_AddRefs(root)); - newRootWindow = root ? root->GetWindow() : nullptr; - - isElementInActiveWindow = (mActiveWindow && newRootWindow == mActiveWindow); + isElementInActiveWindow = + (mActiveWindow && newRootWindow == mActiveWindow); + } + if (newRootWindow) { + newRootBrowsingContext = newRootWindow->GetBrowsingContext(); + } + } else { + // XXX This is wrong for `