Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion browser/app/Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ ifdef MOZ_DEBUG
MAC_APP_NAME := $(MAC_APP_NAME)Debug
endif

ifdef MOZ_ENTERPRISE
PLIST_EXTRA_FLAGS = -DMOZ_ENTERPRISE
endif

Comment thread
gcp marked this conversation as resolved.
AB_CD = $(MOZ_UI_LOCALE)

ifeq (zh-TW,$(AB_CD))
Expand All @@ -73,7 +77,7 @@ tools repackage:: $(DIST)/bin/$(MOZ_APP_NAME) $(objdir)/macbuild/Contents/MacOS-
$(MKDIR) -p '$(dist_dest)/$(LPROJ)'
rsync -a --exclude '*.in' $(srcdir)/macbuild/Contents '$(dist_dest)' --exclude English.lproj
rsync -a --exclude '*.in' $(srcdir)/macbuild/Contents/Resources/English.lproj/ '$(dist_dest)/$(LPROJ)'
$(call py_action,preprocessor Info.plist,-Fsubstitution -DAPP_VERSION='$(MOZ_APP_VERSION)' -DMOZ_APP_NAME='$(MOZ_APP_NAME)' -DMAC_APP_NAME='$(MAC_APP_NAME)' -DMOZ_MACBUNDLE_ID='$(MOZ_MACBUNDLE_ID)' -DMAC_BUNDLE_VERSION='$(MAC_BUNDLE_VERSION)' -DMOZ_SMPRIVILEGEDEXECUTABLES_REQUIREMENTS='$(MOZ_SMPRIVILEGEDEXECUTABLES_REQUIREMENTS)' -DMOZ_DEVELOPER_REPO_PATH='$(topsrcdir)' -DMOZ_DEVELOPER_OBJ_PATH='$(topobjdir)' $(srcdir)/macbuild/Contents/Info.plist.in -o '$(dist_dest)/Contents/Info.plist')
$(call py_action,preprocessor Info.plist,-Fsubstitution $(PLIST_EXTRA_FLAGS) -DAPP_VERSION='$(MOZ_APP_VERSION)' -DMOZ_APP_NAME='$(MOZ_APP_NAME)' -DMAC_APP_NAME='$(MAC_APP_NAME)' -DMOZ_MACBUNDLE_ID='$(MOZ_MACBUNDLE_ID)' -DMAC_BUNDLE_VERSION='$(MAC_BUNDLE_VERSION)' -DMOZ_SMPRIVILEGEDEXECUTABLES_REQUIREMENTS='$(MOZ_SMPRIVILEGEDEXECUTABLES_REQUIREMENTS)' -DMOZ_DEVELOPER_REPO_PATH='$(topsrcdir)' -DMOZ_DEVELOPER_OBJ_PATH='$(topobjdir)' $(srcdir)/macbuild/Contents/Info.plist.in -o '$(dist_dest)/Contents/Info.plist')
$(call py_action,preprocessor InfoPlist.strings,-Fsubstitution --output-encoding utf-16 -DMAC_APP_NAME='$(MAC_APP_NAME)' $(srcdir)/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in -o '$(dist_dest)/$(LPROJ)/InfoPlist.strings')
rsync -a --exclude-from='$(objdir)/macbuild/Contents/MacOS-files.txt' $(DIST)/bin/ '$(dist_dest)/Contents/Resources'
rsync -a --include-from='$(objdir)/macbuild/Contents/MacOS-files.txt' --exclude '*' $(DIST)/bin/ '$(dist_dest)/Contents/MacOS'
Expand Down
6 changes: 6 additions & 0 deletions browser/app/macbuild/Contents/Info.plist.in
Original file line number Diff line number Diff line change
Expand Up @@ -251,9 +251,15 @@
<string>0</string>
<key>SYSTEM_VERSION_COMPAT</key>
<string>0</string>
#ifdef MOZ_ENTERPRISE
<key>MOZ_FELT_UI</key>
<string>1</string>
#endif
</dict>
#ifdef MOZ_ENTERPRISE
<key>LSUIElement</key>
<true/>
#endif
Comment thread
gcp marked this conversation as resolved.
<key>LSFileQuarantineEnabled</key>
<true/>
<key>LSMinimumSystemVersion</key>
Expand Down
10 changes: 10 additions & 0 deletions testing/enterprise/manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ subsuite = "enterprise"

["test_felt_browser_new_window_from_cli.py"]

["test_felt_browser_rapid_new_tabs_from_cli.py"]
run-if = [
"os == 'mac'",
]

["test_felt_browser_rapid_new_windows_from_cli.py"]
run-if = [
"os == 'mac'",
]

["test_felt_browser_restart_is_quit.py"]

["test_felt_browser_restart_works.py"]
Expand Down
45 changes: 45 additions & 0 deletions testing/enterprise/test_felt_browser_new_window_from_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,51 @@ def _wait_for_window_with_url(self, url, is_private=None):
time.sleep(0.5)
assert False, f"Expected window with url {url}"

def _get_child_tab_urls(self):
self._child_driver.set_context("chrome")
urls = self._child_driver.execute_script(
"""
let urls = [];
let enumerator = Services.wm.getEnumerator("navigator:browser");
while (enumerator.hasMoreElements()) {
let win = enumerator.getNext();
for (let tab of win.gBrowser?.tabs ?? []) {
urls.push(tab.linkedBrowser?.currentURI?.spec ?? "");
}
}
return urls;
"""
)
self._child_driver.set_context("content")
return urls

def _wait_for_tab_urls_containing(self, substring, expected_count):
loops = 0
while loops < 40:
urls = self._get_child_tab_urls()
matching = [u for u in urls if substring in u]
if len(matching) >= expected_count:
return matching
loops += 1
time.sleep(0.5)
assert False, (
f"Expected {expected_count} tabs matching '{substring}', "
f"found {len(matching)}: {matching}"
)

def _wait_for_exact_window_count(self, expected, settle_time=2.0):
"""Wait for the expected window count, then verify no extra windows
appear during a settling period. This detects races where rapid
CLI invocations could create duplicate windows."""
self._wait_for_window_count(expected)
time.sleep(settle_time)
windows = self._get_child_windows()
assert len(windows) == expected, (
f"Expected exactly {expected} windows after settling, "
f"but saw {len(windows)}"
)
return windows

def test_new_window_from_cli(self):
super().run_felt_base()
self.connect_child_browser()
Expand Down
43 changes: 43 additions & 0 deletions testing/enterprise/test_felt_browser_rapid_new_tabs_from_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env python3
# 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 https://mozilla.org/MPL/2.0/.

import os
import subprocess
import sys

sys.path.append(os.path.dirname(__file__))

from test_felt_browser_new_window_from_cli import FeltNewWindowFromCli


class FeltRapidNewTabsFromCli(FeltNewWindowFromCli):
def test_rapid_new_tabs_from_cli(self):
"""Rapidly open multiple tabs via CLI to detect races when URLs
are opened in existing windows (see bug 2002462 comment 18)."""
super().run_felt_base()
self.connect_child_browser()

NUM_TABS = 5
base_url = f"http://localhost:{self.console_port}/ping"

procs = []
for i in range(NUM_TABS):
url = f"{base_url}?tab={i}"
args = [
f"{self._driver.instance.binary}",
"-profile",
self._child_profile_path,
"-url",
url,
]
procs.append(subprocess.Popen(args, shell=False))

for proc in procs:
proc.wait(timeout=30)

matching = self._wait_for_tab_urls_containing(f"{base_url}?tab=", NUM_TABS)
assert len(set(matching)) == NUM_TABS, (
f"Found duplicate URLs in rapid-opened tabs: {matching}"
)
52 changes: 52 additions & 0 deletions testing/enterprise/test_felt_browser_rapid_new_windows_from_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/usr/bin/env python3
# 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 https://mozilla.org/MPL/2.0/.

import os
import subprocess
import sys

sys.path.append(os.path.dirname(__file__))

from test_felt_browser_new_window_from_cli import FeltNewWindowFromCli


class FeltRapidNewWindowsFromCli(FeltNewWindowFromCli):
def test_rapid_new_windows_from_cli(self):
"""Rapidly open multiple windows via CLI to detect races in URL
queuing / dock icon handling (see bug 2002462 comment 18)."""
super().run_felt_base()
self.connect_child_browser()

NUM_WINDOWS = 5
windows = self._get_child_windows()
initial_count = len(windows)
base_url = f"http://localhost:{self.console_port}/ping"

procs = []
for i in range(NUM_WINDOWS):
url = f"{base_url}?rapid={i}"
args = [
f"{self._driver.instance.binary}",
"-profile",
self._child_profile_path,
"--new-window",
url,
]
procs.append(subprocess.Popen(args, shell=False))

for proc in procs:
proc.wait(timeout=30)

expected_count = initial_count + NUM_WINDOWS
windows = self._wait_for_exact_window_count(expected_count)

rapid_urls = [w["url"] for w in windows if f"{base_url}?rapid=" in w["url"]]
assert len(rapid_urls) == NUM_WINDOWS, (
f"Expected {NUM_WINDOWS} rapid-opened windows, "
f"found {len(rapid_urls)}: {rapid_urls}"
)
assert len(set(rapid_urls)) == NUM_WINDOWS, (
f"Found duplicate URLs in rapid-opened windows: {rapid_urls}"
)
5 changes: 5 additions & 0 deletions toolkit/xre/MacApplicationDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,9 @@ void InitializeMacApp(void);
nsTArray<nsCString> TakeStartupURLs();
void DisableAppNap(void);

#ifdef MOZ_ENTERPRISE
void SuppressMacDockIcon(void);
void RestoreMacDockIcon(void);
#endif

#endif
17 changes: 17 additions & 0 deletions toolkit/xre/MacApplicationDelegate.mm
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,23 @@ void DisableAppNap() {
}];
}

#ifdef MOZ_ENTERPRISE
void SuppressMacDockIcon() {
// Start the process as a UIElement app so it has no dock icon.
// Remoting clients (processes that just forward a URL to an existing
// instance and exit) will never restore the dock icon, preventing
// transient dock icon flash on rapid link clicks (bug 2002462).
ProcessSerialNumber psn = {0, kCurrentProcess};
TransformProcessType(&psn, kProcessTransformToUIElementApplication);
}

void RestoreMacDockIcon() {
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
NS_OBJC_END_TRY_IGNORE_BLOCK;
}
#endif

void SetupMacApplicationDelegate(bool* gRestartedByOS) {
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;

Expand Down
12 changes: 12 additions & 0 deletions toolkit/xre/nsAppRunner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4453,6 +4453,13 @@ int XREMain::XRE_mainInit(bool* aExitFlag) {
#endif

#ifdef XP_MACOSX
# ifdef MOZ_ENTERPRISE
// Suppress dock icon before NSApp initialization. The dock icon will be
// restored after the remoting check. Remoting clients (processes that just
// forward a URL to an existing instance) exit before restoration, preventing
// transient dock icon flash on rapid link clicks (bug 2002462).
SuppressMacDockIcon();
# endif
// Set up ability to respond to system (Apple) events. This must occur before
// ProcessUpdates to ensure that links clicked in external applications aren't
// lost when updates are pending.
Expand Down Expand Up @@ -5210,6 +5217,11 @@ int XREMain::XRE_mainStartup(bool* aExitFlag) {
}
#endif /* MOZ_HAS_REMOTE */

#if defined(XP_MACOSX) && defined(MOZ_ENTERPRISE)
// Not a remoting client — restore the dock icon for the main process.
RestoreMacDockIcon();
#endif

#if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID)
# ifdef XP_WIN
{
Expand Down