Patch macOS keychain handling for CEF builds
Adds a macOS post-build step to patch CEF’s keychain service string to a Nebula-specific name, then re-sign the modified framework (with configurable signing identity or ad-hoc). Also enables Chromium’s `use-mock-keychain` switch on Apple builds to prevent repeated local keychain prompts, fixes titlebar traffic-light hit testing in the mac window view, scopes a WM_CLOSE guard to Windows only, and updates `.clangd` to ignore non-mac platform sources during macOS development.
This commit is contained in:
@@ -15,3 +15,22 @@ CompileFlags:
|
||||
- -Ithirdparty/cef
|
||||
- -Ithirdparty/cef/include
|
||||
- -Wno-c++17-extensions
|
||||
|
||||
# On macOS builds, Windows/Linux platform sources are not compiled. Ignore them
|
||||
# so clangd does not try to parse windows.h / Linux headers with the Mac SDK.
|
||||
---
|
||||
If:
|
||||
PathMatch: src/platform/win/.*
|
||||
CompileFlags:
|
||||
Add:
|
||||
- -Wno-everything
|
||||
Remove:
|
||||
- -xobjective-c++
|
||||
---
|
||||
If:
|
||||
PathMatch: src/platform/linux/.*
|
||||
CompileFlags:
|
||||
Add:
|
||||
- -Wno-everything
|
||||
Remove:
|
||||
- -xobjective-c++
|
||||
|
||||
@@ -296,6 +296,33 @@ function(add_nebula_app_target nebula_target entry_source)
|
||||
VERBATIM
|
||||
)
|
||||
endforeach()
|
||||
|
||||
# Give Nebula its own OSCrypt keychain service name, then resign so the
|
||||
# patched framework still loads. Optional stable identity:
|
||||
# cmake -DNEBULA_CODESIGN_IDENTITY="Apple Development: ..."
|
||||
set(NEBULA_CODESIGN_IDENTITY "" CACHE STRING
|
||||
"codesign identity for macOS app bundles (empty = ad-hoc)")
|
||||
if(NEBULA_CODESIGN_IDENTITY)
|
||||
set(_nebula_codesign_identity "${NEBULA_CODESIGN_IDENTITY}")
|
||||
else()
|
||||
set(_nebula_codesign_identity "-")
|
||||
endif()
|
||||
find_package(Python3 COMPONENTS Interpreter REQUIRED)
|
||||
set(_nebula_cef_framework
|
||||
"${NEBULA_APP}/Contents/Frameworks/Chromium Embedded Framework.framework")
|
||||
set(_nebula_cef_binary
|
||||
"${_nebula_cef_framework}/Versions/Current/Chromium Embedded Framework")
|
||||
add_custom_command(TARGET ${nebula_target} POST_BUILD
|
||||
COMMAND ${Python3_EXECUTABLE}
|
||||
"${CMAKE_SOURCE_DIR}/cmake/macos_patch_cef_keychain.py"
|
||||
"${_nebula_cef_binary}"
|
||||
COMMAND codesign --force --sign "${_nebula_codesign_identity}" --timestamp=none
|
||||
"${_nebula_cef_binary}"
|
||||
COMMAND codesign --force --sign "${_nebula_codesign_identity}" --timestamp=none
|
||||
"${_nebula_cef_framework}"
|
||||
COMMENT "Patching CEF keychain service name and resigning framework"
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
|
||||
# ------------------------------------------------------------
|
||||
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Replace CEF's shared Chromium keychain service name with a Nebula-specific one.
|
||||
|
||||
CEF/Chromium stores the OSCrypt cookie-encryption key in the login keychain under
|
||||
"Chromium Safe Storage". That item is often owned by another Chromium-based app,
|
||||
so unsigned/ad-hoc NebulaBrowser builds get a password prompt on every launch.
|
||||
|
||||
The replacement must be the same length as the original (embedded C string).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
OLD = b"Chromium Safe Storage"
|
||||
NEW = b"NebulaBrowser Storage"
|
||||
|
||||
assert len(OLD) == len(NEW), "replacement must be the same length"
|
||||
|
||||
|
||||
def patch(path: str) -> int:
|
||||
with open(path, "rb") as handle:
|
||||
data = bytearray(handle.read())
|
||||
|
||||
count = data.count(OLD)
|
||||
if count == 0:
|
||||
if NEW in data:
|
||||
print(f"Already patched: {path}")
|
||||
return 0
|
||||
print(f"ERROR: {OLD!r} not found in {path}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
data = data.replace(OLD, NEW)
|
||||
with open(path, "wb") as handle:
|
||||
handle.write(data)
|
||||
|
||||
print(f"Patched {count} occurrence(s) in {path}")
|
||||
print(f" {OLD.decode()} -> {NEW.decode()}")
|
||||
return 0
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("framework_binary")
|
||||
args = parser.parse_args()
|
||||
return patch(args.framework_binary)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -241,11 +241,13 @@ void NebulaController::OnWindowResized(const nebula::window::BrowserLayout& layo
|
||||
}
|
||||
|
||||
void NebulaController::OnWindowCloseRequested() {
|
||||
#if defined(_WIN32)
|
||||
if (!closing_ && !closing_tab_browsers_.empty()) {
|
||||
// CEF Alloy can bubble a child browser close as WM_CLOSE on the host
|
||||
// window. Per-tab closes should not turn into full app shutdown.
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (launch_options_.mode == AppMode::BigPicture && !closing_) {
|
||||
if (!SwitchToSiblingMode(AppMode::Desktop)) {
|
||||
|
||||
@@ -83,6 +83,12 @@ void NebulaApp::OnBeforeCommandLineProcessing(const CefString& process_type,
|
||||
command_line->AppendSwitchWithValue("use-angle", "d3d11");
|
||||
#elif defined(__APPLE__)
|
||||
command_line->AppendSwitchWithValue("use-angle", "metal");
|
||||
// Unsigned/ad-hoc local builds cannot get a stable Keychain ACL, which
|
||||
// causes repeated "wants to use your confidential information" prompts.
|
||||
// CEF's own samples use this switch for the same reason. Cookie encryption
|
||||
// still works via Chromium's fixed mock password; real Keychain backing
|
||||
// should only be re-enabled once the app is signed with a stable identity.
|
||||
command_line->AppendSwitch("use-mock-keychain");
|
||||
#else
|
||||
command_line->AppendSwitchWithValue("use-gl", "egl");
|
||||
#endif
|
||||
|
||||
@@ -17,6 +17,23 @@ struct NebulaWindowImpl;
|
||||
- (BOOL)isFlipped {
|
||||
return YES;
|
||||
}
|
||||
|
||||
// FullSizeContentView draws our CEF chrome under the titlebar. Without this,
|
||||
// that view eats clicks meant for the system traffic lights.
|
||||
- (NSView*)hitTest:(NSPoint)point {
|
||||
NSWindow* window = [self window];
|
||||
NSButton* close_button = [window standardWindowButton:NSWindowCloseButton];
|
||||
NSView* button_container = close_button ? [close_button superview] : nil;
|
||||
if (button_container && ![close_button isHidden]) {
|
||||
const NSPoint window_point = [[self superview] convertPoint:point toView:nil];
|
||||
const NSRect container_rect = [button_container convertRect:[button_container bounds]
|
||||
toView:nil];
|
||||
if (NSPointInRect(window_point, container_rect)) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
return [super hitTest:point];
|
||||
}
|
||||
@end
|
||||
|
||||
@interface NebulaWindowDelegate : NSObject <NSWindowDelegate> {
|
||||
|
||||
Reference in New Issue
Block a user