Skip to content

Commit

Permalink
Bug 1875100 - Propagate top level activeness automatically to top des…
Browse files Browse the repository at this point in the history
…cendants. r=nika,tabbrowser-reviewers,mconley,extension-reviewers,robwu,geckoview-reviewers,owlish,kaya

For that, opt in tabbrowser and the shopping sidebar to manual
activeness management.

Differential Revision: https://phabricator.services.mozilla.com/D198942
  • Loading branch information
emilio committed Feb 7, 2024
1 parent d7779c8 commit 2971047
Show file tree
Hide file tree
Showing 18 changed files with 239 additions and 79 deletions.
6 changes: 1 addition & 5 deletions browser/base/content/tabbrowser.js
Original file line number Diff line number Diff line change
Expand Up @@ -2094,7 +2094,6 @@
uriIsAboutBlank,
userContextId,
skipLoad,
initiallyActive,
} = {}) {
let b = document.createXULElement("browser");
// Use the JSM global to create the permanentKey, so that if the
Expand All @@ -2120,6 +2119,7 @@
messagemanagergroup: "browsers",
tooltip: "aHTMLTooltip",
type: "content",
manualactiveness: "true",
};
for (let attribute in defaultBrowserAttributes) {
b.setAttribute(attribute, defaultBrowserAttributes[attribute]);
Expand All @@ -2129,10 +2129,6 @@
b.setAttribute("maychangeremoteness", "true");
}

if (!initiallyActive) {
b.setAttribute("initiallyactive", "false");
}

if (userContextId) {
b.setAttribute("usercontextid", userContextId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ export class FullscreenFrameChild extends JSWindowActorChild {
case "ExitFullscreen":
return this.contentWindow.document.exitFullscreen();
case "RequestFullscreen":
this.browsingContext.isActive = true;
return Promise.all([this.changed(), this.requestFullscreen()]);
case "CreateChild":
let child = msg.data;
Expand Down
2 changes: 2 additions & 0 deletions browser/components/extensions/test/browser/browser.toml
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,8 @@ skip-if = [
"verify && !debug && os == 'mac'",
]

["browser_ext_optionsPage_activity.js"]

["browser_ext_optionsPage_browser_style.js"]

["browser_ext_optionsPage_links_open_in_tabs.js"]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

add_task(async function test_options_activity() {
async function backgroundScript() {
browser.runtime.openOptionsPage();
}

function optionsScript() {
browser.test.sendMessage("options-page:loaded", document.documentURI);
}

let extension = ExtensionTestUtils.loadExtension({
useAddonManager: "temporary",

manifest: {
options_ui: {
page: "options.html",
},
},
files: {
"options.html": `<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="options.js" type="text/javascript"></script>
</head>
<body style="height: 100px;">
<h1>Extensions Options</h1>
<a href="https://example.com/options-page-link">options page link</a>
</body>
</html>`,
"options.js": optionsScript,
},
background: backgroundScript,
});

const aboutAddonsTab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
"about:addons"
);

await extension.startup();

await extension.awaitMessage("options-page:loaded");

const optionsBrowser = getInlineOptionsBrowser(gBrowser.selectedBrowser);

ok(
!optionsBrowser.ownerDocument.hidden,
"Parent should be active since it's in the foreground"
);
ok(
optionsBrowser.docShellIsActive,
"Should be active since we're in the foreground"
);

let parentVisibilityChange = BrowserTestUtils.waitForEvent(
optionsBrowser.ownerDocument,
"visibilitychange"
);
const aboutBlankTab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
"about:blank"
);

await parentVisibilityChange;
ok(
!optionsBrowser.docShellIsActive,
"Should become inactive since parent was backgrounded"
);

BrowserTestUtils.removeTab(aboutBlankTab);
BrowserTestUtils.removeTab(aboutAddonsTab);

await extension.unload();
});
3 changes: 1 addition & 2 deletions browser/components/shopping/ShoppingSidebarParent.sys.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -341,11 +341,10 @@ class ShoppingSidebarManagerClass {
if (!sidebar) {
return;
}
let { browsingContext } = sidebar.querySelector("browser");
try {
// Tell Gecko when the sidebar visibility changes to avoid background
// sidebars taking more CPU / energy than needed.
browsingContext.isActive =
sidebar.querySelector("browser").docShellIsActive =
!document.hidden &&
aBrowser == gBrowser.selectedBrowser &&
!sidebar.hidden;
Expand Down
1 change: 1 addition & 0 deletions browser/components/shopping/content/shopping-sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
disablehistory="true"
flex="1"
message="true"
manualactiveness="true"
remoteType="privilegedabout"
maychangeremoteness="true"
remote="true"
Expand Down
96 changes: 58 additions & 38 deletions docshell/base/BrowsingContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -422,15 +422,13 @@ already_AddRefed<BrowsingContext> BrowsingContext::CreateDetached(

fields.Get<IDX_HistoryID>() = nsID::GenerateUUID();
fields.Get<IDX_ExplicitActive>() = [&] {
if (parentBC) {
if (parentBC || aType == Type::Content) {
// Non-root browsing-contexts inherit their status from its parent.
// Top-content either gets managed by the top chrome, or gets manually
// managed by the front-end (see ManuallyManagesActiveness). In any case
// we want to start off as inactive.
return ExplicitActiveStatus::None;
}
if (aType == Type::Content) {
// Content gets managed by the chrome front-end / embedder element and
// starts as inactive.
return ExplicitActiveStatus::Inactive;
}
// Chrome starts as active.
return ExplicitActiveStatus::Active;
}();
Expand Down Expand Up @@ -739,21 +737,24 @@ void BrowsingContext::SetEmbedderElement(Element* aEmbedder) {
txn.SetEmbedderInnerWindowId(inner->WindowID());
}
txn.SetFullscreenAllowedByOwner(OwnerAllowsFullscreen(*aEmbedder));
if (XRE_IsParentProcess() && IsTopContent()) {
if (XRE_IsParentProcess() && aEmbedder->IsXULElement() && IsTopContent()) {
nsAutoString messageManagerGroup;
if (aEmbedder->IsXULElement()) {
aEmbedder->GetAttr(nsGkAtoms::messagemanagergroup, messageManagerGroup);
if (!aEmbedder->AttrValueIs(kNameSpaceID_None,
nsGkAtoms::initiallyactive,
nsGkAtoms::_false, eIgnoreCase)) {
txn.SetExplicitActive(ExplicitActiveStatus::Active);
aEmbedder->GetAttr(nsGkAtoms::messagemanagergroup, messageManagerGroup);
txn.SetMessageManagerGroup(messageManagerGroup);
txn.SetUseGlobalHistory(
!aEmbedder->HasAttr(nsGkAtoms::disableglobalhistory));
if (!aEmbedder->HasAttr(nsGkAtoms::manualactiveness)) {
// We're active iff the parent cross-chrome-boundary is active. Note we
// can't just use this->Canonical()->GetParentCrossChromeBoundary here,
// since mEmbedderElement is still null at this point.
RefPtr bc = aEmbedder->OwnerDoc()->GetBrowsingContext();
const bool isActive = bc && bc->IsActive();
txn.SetExplicitActive(isActive ? ExplicitActiveStatus::Active
: ExplicitActiveStatus::Inactive);
if (auto* bp = Canonical()->GetBrowserParent()) {
bp->SetRenderLayers(isActive);
}
}
txn.SetMessageManagerGroup(messageManagerGroup);

bool useGlobalHistory =
!aEmbedder->HasAttr(nsGkAtoms::disableglobalhistory);
txn.SetUseGlobalHistory(useGlobalHistory);
}

MOZ_ALWAYS_SUCCEEDS(txn.Commit(this));
Expand Down Expand Up @@ -2634,8 +2635,16 @@ void BrowsingContext::DidSet(FieldIndex<IDX_GVInaudibleAutoplayRequestStatus>) {
"browsing context");
}

bool BrowsingContext::CanSet(FieldIndex<IDX_ExplicitActive>,
const ExplicitActiveStatus&,
ContentParent* aSource) {
return XRE_IsParentProcess() && IsTop() && !aSource;
}

void BrowsingContext::DidSet(FieldIndex<IDX_ExplicitActive>,
ExplicitActiveStatus aOldValue) {
MOZ_ASSERT(IsTop());

const bool isActive = IsActive();
const bool wasActive = [&] {
if (aOldValue != ExplicitActiveStatus::None) {
Expand All @@ -2648,32 +2657,43 @@ void BrowsingContext::DidSet(FieldIndex<IDX_ExplicitActive>,
return;
}

if (IsTop()) {
Group()->UpdateToplevelsSuspendedIfNeeded();

if (XRE_IsParentProcess()) {
auto* bc = Canonical();
if (BrowserParent* bp = bc->GetBrowserParent()) {
bp->RecomputeProcessPriority();
Group()->UpdateToplevelsSuspendedIfNeeded();
if (XRE_IsParentProcess()) {
if (BrowserParent* bp = Canonical()->GetBrowserParent()) {
bp->RecomputeProcessPriority();
#if defined(XP_WIN) && defined(ACCESSIBILITY)
if (a11y::Compatibility::IsDolphin()) {
// update active accessible documents on windows
if (a11y::DocAccessibleParent* tabDoc =
bp->GetTopLevelDocAccessible()) {
HWND window = tabDoc->GetEmulatedWindowHandle();
MOZ_ASSERT(window);
if (window) {
if (isActive) {
a11y::nsWinUtils::ShowNativeWindow(window);
} else {
a11y::nsWinUtils::HideNativeWindow(window);
}
if (a11y::Compatibility::IsDolphin()) {
// update active accessible documents on windows
if (a11y::DocAccessibleParent* tabDoc =
bp->GetTopLevelDocAccessible()) {
HWND window = tabDoc->GetEmulatedWindowHandle();
MOZ_ASSERT(window);
if (window) {
if (isActive) {
a11y::nsWinUtils::ShowNativeWindow(window);
} else {
a11y::nsWinUtils::HideNativeWindow(window);
}
}
}
#endif
}
#endif
}

// NOTE(emilio): Ideally we'd want to reuse the ExplicitActiveStatus::None
// set-up, but that's non-trivial to do because in content processes we
// can't access the top-cross-chrome-boundary bc.
auto manageTopDescendant = [&](auto* aChild) {
if (!aChild->ManuallyManagesActiveness()) {
aChild->SetIsActiveInternal(isActive, IgnoreErrors());
if (BrowserParent* bp = aChild->GetBrowserParent()) {
bp->SetRenderLayers(isActive);
}
}
return CallState::Continue;
};
Canonical()->CallOnAllTopDescendants(manageTopDescendant,
/* aIncludeNestedBrowsers = */ false);
}

PreOrderWalk([&](BrowsingContext* aContext) {
Expand Down
8 changes: 2 additions & 6 deletions docshell/base/BrowsingContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -606,12 +606,6 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
}

bool IsActive() const;
void SetIsActive(bool aIsActive, mozilla::ErrorResult& aRv) {
SetExplicitActive(aIsActive ? ExplicitActiveStatus::Active
: ExplicitActiveStatus::Inactive,
aRv);
}

bool ForceOffline() const { return GetForceOffline(); }

bool ForceDesktopViewport() const { return GetForceDesktopViewport(); }
Expand Down Expand Up @@ -1115,6 +1109,8 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {

void DidSet(FieldIndex<IDX_DisplayMode>, enum DisplayMode aOldValue);

bool CanSet(FieldIndex<IDX_ExplicitActive>, const ExplicitActiveStatus&,
ContentParent* aSource);
void DidSet(FieldIndex<IDX_ExplicitActive>, ExplicitActiveStatus aOldValue);

bool CanSet(FieldIndex<IDX_IsActiveBrowserWindowInternal>, const bool& aValue,
Expand Down
Loading

0 comments on commit 2971047

Please sign in to comment.