From f5a1a863a9426082fce051d573e28fd51a839521 Mon Sep 17 00:00:00 2001 From: wenkaifan0720 Date: Tue, 9 Jun 2026 16:16:52 -0700 Subject: [PATCH 1/5] refactor(P1): extract flutter_cef_platform_interface package MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First phase of the federated cross-platform restructure (see specs/cross-platform/PLAN.md). Adds packages/flutter_cef_platform_interface holding the shared Dart types (cef_events, cef_input — moved here, history preserved) and the FlutterCefPlatform contract: a PlatformInterface whose default MethodChannelFlutterCef exposes the 'flutter_cef' method channel. The cross-platform contract is that method-channel protocol, which each platform's native plugin speaks. Root flutter_cef now depends on the interface package; the controller reads its channel from FlutterCefPlatform.instance, and the public library re-exports the shared types so consumers' `package:flutter_cef/flutter_cef.dart` imports are unchanged. macOS plugin stays at root for now (moves in P2). Gate: flutter analyze clean (both packages); 94 tests pass. Co-Authored-By: Claude Fable 5 --- .gitignore | 3 + example/pubspec.lock | 15 ++++ lib/flutter_cef.dart | 5 +- lib/src/cef_web_controller.dart | 8 +- lib/src/cef_web_view.dart | 3 +- .../lib/flutter_cef_platform_interface.dart | 12 +++ .../lib}/src/cef_events.dart | 0 .../lib}/src/cef_input.dart | 0 .../lib/src/flutter_cef_platform.dart | 49 +++++++++++ .../lib/src/method_channel_flutter_cef.dart | 18 ++++ .../pubspec.yaml | 23 +++++ pubspec.yaml | 2 + specs/cross-platform/PLAN.md | 84 +++++++++++++++++++ test/cef_input_test.dart | 2 +- 14 files changed, 217 insertions(+), 7 deletions(-) create mode 100644 packages/flutter_cef_platform_interface/lib/flutter_cef_platform_interface.dart rename {lib => packages/flutter_cef_platform_interface/lib}/src/cef_events.dart (100%) rename {lib => packages/flutter_cef_platform_interface/lib}/src/cef_input.dart (100%) create mode 100644 packages/flutter_cef_platform_interface/lib/src/flutter_cef_platform.dart create mode 100644 packages/flutter_cef_platform_interface/lib/src/method_channel_flutter_cef.dart create mode 100644 packages/flutter_cef_platform_interface/pubspec.yaml create mode 100644 specs/cross-platform/PLAN.md diff --git a/.gitignore b/.gitignore index 0a67299..2fe29f5 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,6 @@ migrate_working_dir/ # Native CEF host: build output + fetched CEF SDK (see native/build_cef_host.sh). native/cef_host/build/ native/**/cef_binary_*/ + +# Federated sub-packages are libraries too — no pubspec.lock. +/packages/*/pubspec.lock diff --git a/example/pubspec.lock b/example/pubspec.lock index 508c415..600842d 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -77,6 +77,13 @@ packages: relative: true source: path version: "0.1.2" + flutter_cef_platform_interface: + dependency: transitive + description: + path: "../packages/flutter_cef_platform_interface" + relative: true + source: path + version: "0.1.2" flutter_driver: dependency: transitive description: flutter @@ -177,6 +184,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.6" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" process: dependency: transitive description: diff --git a/lib/flutter_cef.dart b/lib/flutter_cef.dart index 603c075..d6f63e4 100644 --- a/lib/flutter_cef.dart +++ b/lib/flutter_cef.dart @@ -3,10 +3,11 @@ /// The page renders off-screen in a `cef_host` subprocess into a shared /// IOSurface and is shown as a [Texture] (so it composites/transforms/clips /// like any widget). Pointer + keyboard input is forwarded by coordinate, and -/// the page cursor drives a [MouseRegion]. macOS only, for now. +/// the page cursor drives a [MouseRegion]. macOS only, for now (the package is +/// federated — see `flutter_cef_macos` and `PORTING.md`). library; -export 'src/cef_events.dart' +export 'package:flutter_cef_platform_interface/flutter_cef_platform_interface.dart' show CefCookie, CefConsoleMessage, diff --git a/lib/src/cef_web_controller.dart b/lib/src/cef_web_controller.dart index 7803440..1bd2305 100644 --- a/lib/src/cef_web_controller.dart +++ b/lib/src/cef_web_controller.dart @@ -4,8 +4,7 @@ import 'dart:convert'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; -import 'cef_events.dart'; -import 'cef_input.dart'; +import 'package:flutter_cef_platform_interface/flutter_cef_platform_interface.dart'; /// Controls one CEF browser session: navigate, drive history, run JavaScript, /// forward input, and observe page state (loading, title, url, cursor). Backed @@ -23,7 +22,10 @@ class CefWebController { _installHandler(); } - static const MethodChannel _channel = MethodChannel('flutter_cef'); + // The method channel of the endorsed platform implementation (the default + // MethodChannelFlutterCef on macOS). A platform plugin may swap the instance + // in its registerWith; this reflects whatever is current. + static MethodChannel get _channel => FlutterCefPlatform.instance.channel; static int _counter = 0; bool _disposed = false; diff --git a/lib/src/cef_web_view.dart b/lib/src/cef_web_view.dart index ccbd5b8..0a8ef00 100644 --- a/lib/src/cef_web_view.dart +++ b/lib/src/cef_web_view.dart @@ -2,7 +2,8 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; -import 'cef_input.dart'; +import 'package:flutter_cef_platform_interface/flutter_cef_platform_interface.dart'; + import 'cef_web_controller.dart'; /// Per-event gain applied to trackpad two-finger pan when forwarding it to the diff --git a/packages/flutter_cef_platform_interface/lib/flutter_cef_platform_interface.dart b/packages/flutter_cef_platform_interface/lib/flutter_cef_platform_interface.dart new file mode 100644 index 0000000..7aa4410 --- /dev/null +++ b/packages/flutter_cef_platform_interface/lib/flutter_cef_platform_interface.dart @@ -0,0 +1,12 @@ +/// The common platform interface for the `flutter_cef` plugin. +/// +/// Holds the shared Dart types ([CefCookie], [CefLoadError], input mappings, +/// …) and the [FlutterCefPlatform] contract that each platform implementation +/// (macOS, and future Windows / Linux) speaks. App-facing code depends on +/// `package:flutter_cef/flutter_cef.dart`, which re-exports the types here. +library; + +export 'src/cef_events.dart'; +export 'src/cef_input.dart'; +export 'src/flutter_cef_platform.dart'; +export 'src/method_channel_flutter_cef.dart'; diff --git a/lib/src/cef_events.dart b/packages/flutter_cef_platform_interface/lib/src/cef_events.dart similarity index 100% rename from lib/src/cef_events.dart rename to packages/flutter_cef_platform_interface/lib/src/cef_events.dart diff --git a/lib/src/cef_input.dart b/packages/flutter_cef_platform_interface/lib/src/cef_input.dart similarity index 100% rename from lib/src/cef_input.dart rename to packages/flutter_cef_platform_interface/lib/src/cef_input.dart diff --git a/packages/flutter_cef_platform_interface/lib/src/flutter_cef_platform.dart b/packages/flutter_cef_platform_interface/lib/src/flutter_cef_platform.dart new file mode 100644 index 0000000..cceb114 --- /dev/null +++ b/packages/flutter_cef_platform_interface/lib/src/flutter_cef_platform.dart @@ -0,0 +1,49 @@ +import 'package:flutter/services.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'method_channel_flutter_cef.dart'; + +/// The interface that platform-specific implementations of `flutter_cef` must +/// implement to be endorsed (macOS today; Windows / Linux down the line). +/// +/// The cross-platform contract is the **method-channel protocol**: every +/// platform exposes a [MethodChannel] named [channelName] over which the +/// app-facing `CefWebController` drives a per-view `cef_host` subprocess +/// (create / navigate / input / dispose) and receives page events. The native +/// side of each platform plugin implements that protocol; the Dart side is +/// usually just this endorsement (see the default [MethodChannelFlutterCef]). +/// +/// A platform plugin registers by setting [instance] from its `registerWith` +/// (wired via `dartPluginClass` in the implementation package's pubspec). +abstract class FlutterCefPlatform extends PlatformInterface { + /// Constructs a [FlutterCefPlatform]. + FlutterCefPlatform() : super(token: _token); + + static final Object _token = Object(); + + static FlutterCefPlatform _instance = MethodChannelFlutterCef(); + + /// The default instance to use — the method-channel implementation, which + /// works for any platform whose native plugin speaks the [channelName] + /// protocol (all of them today). + static FlutterCefPlatform get instance => _instance; + + /// Platform implementations set this in their `registerWith`. The setter + /// verifies the [PlatformInterface] token to discourage implementations that + /// `implements` rather than `extends` this class. + static set instance(FlutterCefPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + /// The name of the method channel the app-facing code and the native plugin + /// communicate over. This is the heart of the cross-platform contract — see + /// `PORTING.md` for the full method + event + IPC-opcode protocol. + static const String channelName = 'flutter_cef'; + + /// The channel the app-facing `CefWebController` talks over. The default + /// returns `MethodChannel(channelName)`; a platform only overrides this if it + /// needs a non-standard transport (none do today). + MethodChannel get channel => + throw UnimplementedError('channel has not been implemented.'); +} diff --git a/packages/flutter_cef_platform_interface/lib/src/method_channel_flutter_cef.dart b/packages/flutter_cef_platform_interface/lib/src/method_channel_flutter_cef.dart new file mode 100644 index 0000000..e00939c --- /dev/null +++ b/packages/flutter_cef_platform_interface/lib/src/method_channel_flutter_cef.dart @@ -0,0 +1,18 @@ +import 'package:flutter/services.dart'; + +import 'flutter_cef_platform.dart'; + +/// The default [FlutterCefPlatform] implementation: a plain [MethodChannel] +/// named [FlutterCefPlatform.channelName]. +/// +/// This works for any platform whose native plugin speaks the channel protocol +/// (macOS today, Windows / Linux later), so most platform implementations need +/// no Dart-side override — they just provide the native plugin and endorse this +/// default instance from their `registerWith`. +class MethodChannelFlutterCef extends FlutterCefPlatform { + final MethodChannel _channel = + const MethodChannel(FlutterCefPlatform.channelName); + + @override + MethodChannel get channel => _channel; +} diff --git a/packages/flutter_cef_platform_interface/pubspec.yaml b/packages/flutter_cef_platform_interface/pubspec.yaml new file mode 100644 index 0000000..9697a74 --- /dev/null +++ b/packages/flutter_cef_platform_interface/pubspec.yaml @@ -0,0 +1,23 @@ +name: flutter_cef_platform_interface +description: "A common platform interface for the flutter_cef plugin: the shared Dart types and the method-channel contract that each platform implementation (macOS, and future Windows/Linux) speaks." +version: 0.1.2 +homepage: https://github.com/FlutterFlow/flutter_cef +repository: https://github.com/FlutterFlow/flutter_cef +# NOTE: We strongly prefer a constraint that allows the current version and +# future patch/minor releases of the interface so federated implementations +# stay compatible. Breaking changes to this package require a major bump and a +# coordinated update of all implementations. + +environment: + sdk: ^3.4.0 + flutter: '>=3.22.0' + +dependencies: + flutter: + sdk: flutter + plugin_platform_interface: ^2.1.0 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^6.0.0 diff --git a/pubspec.yaml b/pubspec.yaml index b564a45..71a7d4a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,6 +17,8 @@ environment: dependencies: flutter: sdk: flutter + flutter_cef_platform_interface: + path: packages/flutter_cef_platform_interface dev_dependencies: flutter_test: diff --git a/specs/cross-platform/PLAN.md b/specs/cross-platform/PLAN.md new file mode 100644 index 0000000..a9476cc --- /dev/null +++ b/specs/cross-platform/PLAN.md @@ -0,0 +1,84 @@ +# flutter_cef — federated cross-platform restructure + +Goal: restructure the package so a Windows/Linux implementation can be added +later as an independent, endorsed federated package, without touching the +app-facing API or the macOS implementation. The Dart `lib/` is already +platform-neutral; the work is (1) splitting into federated packages and (2) +giving the native `cef_host` an internal portable-core / platform-glue seam so +the C++ is shareable across OS implementations down the line. + +## Target layout + +``` +flutter_cef/ # repo root = app-facing package (unchanged name/version) + lib/ + flutter_cef.dart # re-exports public API + shared types + src/cef_web_view.dart # widget (app-facing) + src/cef_web_controller.dart # controller (app-facing) — talks the shared channel + example/ # unchanged location; deps resolve to the federated packages + packages/ + flutter_cef_platform_interface/ # the cross-platform contract + lib/flutter_cef_platform_interface.dart + lib/src/flutter_cef_platform.dart # FlutterCefPlatform (PlatformInterface) + channel name + lib/src/method_channel_flutter_cef.dart + lib/src/cef_events.dart # shared DTOs (moved from root lib/src) + lib/src/cef_input.dart # shared input enums/helpers (moved) + flutter_cef_macos/ # endorsed macOS implementation (self-contained) + lib/flutter_cef_macos.dart # registerWith → sets the default platform instance + macos/ # Swift plugin + podspec (moved from root macos/) + native/cef_host/ # moved from root native/; restructured: + core/ # PORTABLE C++: protocol + CEF client/app + browser control + platform/mac/ # macOS glue: IOSurface/Metal/Cocoa/Unix-socket/sandbox + tool/bundle_cef_host.sh # moved from root tool/ + PORTING.md # what a flutter_cef_windows / _linux must implement +``` + +`flutter_cef` depends on `flutter_cef_platform_interface` and endorses +`flutter_cef_macos` via `plugin: platforms: macos: default_package`. + +## The cross-platform contract (what a new platform implements) + +1. **Dart**: a `flutter_cef_` package with `dartPluginClass` that sets + `FlutterCefPlatform.instance`. In practice the contract is the method-channel + protocol (channel name + method names + event names + the IPC opcode table), + so a platform plugin mostly implements the native side. +2. **Native `cef_host`** — implement `core/platform.h` for the OS: + - **Surface**: allocate/lookup a shared GPU surface by id + present painted + frames. macOS = IOSurface-backed CVPixelBuffer + Metal; Windows = shared + D3D11 texture / DXGI handle; Linux = shared memory / DMA-buf. + - **IPC transport**: framed read/write. macOS/Linux = Unix domain socket; + Windows = named pipe. + - **App bootstrap / run loop**: macOS = NSApplication; Windows/Linux = their + message loops. + - **Sandbox**: macOS = `CefScopedSandboxContext`; Windows = `cef_sandbox` + static lib + `CefScopedSandboxContext`; Linux = SUID/userns sandbox. + - **Framework/resource path resolution** for the CEF binary distribution. +3. **Host plugin** (`macos/` equivalent): spawn the `cef_host` subprocess, own + the texture registration, relay channel calls to the IPC opcodes. + +## Phases (each ends at a green gate: analyze + test + macOS example build) + +- **P1 — platform_interface package.** Create + `packages/flutter_cef_platform_interface`; move `cef_events.dart` + + `cef_input.dart`; add `FlutterCefPlatform` (PlatformInterface) holding the + channel name + default `MethodChannelFlutterCef`. Root `flutter_cef` depends on + it; controller uses the shared channel name. Gate: `flutter analyze` + `flutter test`. +- **P2 — flutter_cef_macos package.** Move `macos/`, `native/`, `tool/` into + `packages/flutter_cef_macos`; rename podspec → `flutter_cef_macos.podspec`; fix + every relative path (`build_cef_host.sh`, `bundle_cef_host.sh`, podspec, + CMake, FLUTTER_CEF_HOST resolution in the Swift plugin); add + `lib/flutter_cef_macos.dart` + `dartPluginClass`. Root pubspec endorses it via + `default_package`. Gate: example macOS build + signed-host smoke. +- **P3 — native core/platform split.** Inside `flutter_cef_macos/native/cef_host`, + extract `core/` (protocol.h + CEF client/app + browser control + dispatch + loop, depending only on `core/platform.h`) and `platform/mac/` (the ~50 + macOS-specific sites). Gate: rebuild cef_host (ad-hoc + signed) clean. +- **P4 — docs.** `PORTING.md`, README structure, CHANGELOG. Gate: `git diff --check`. + +## Invariants + +- macOS must build + render at every phase gate (verify, don't assume). +- App-facing API (`CefWebView`, `CefWebController`, exported types) unchanged — + consumers' imports of `package:flutter_cef/flutter_cef.dart` keep working. +- No behavior change; this is structure only. +- Dev path-deps during the restructure; switch to version deps only at publish. diff --git a/test/cef_input_test.dart b/test/cef_input_test.dart index 7c7f320..6bb1250 100644 --- a/test/cef_input_test.dart +++ b/test/cef_input_test.dart @@ -2,7 +2,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:flutter_cef/src/cef_input.dart'; +import 'package:flutter_cef_platform_interface/flutter_cef_platform_interface.dart'; void main() { group('cefWindowsKeyCode — editing/navigation keys resolve to Windows VK', From 4cbc7c6a7c400adc089cad6f822e3f113c3d5aac Mon Sep 17 00:00:00 2001 From: wenkaifan0720 Date: Tue, 9 Jun 2026 16:20:46 -0700 Subject: [PATCH 2/5] refactor(P2): move macOS impl into the endorsed flutter_cef_macos package MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Second phase of the federated restructure. Relocates the entire macOS implementation — the Swift plugin (macos/), the cef_host native tree (native/), and the bundling tool (tool/) — into packages/flutter_cef_macos, and wires the federation: - New flutter_cef_macos package: implements: flutter_cef, with pluginClass: FlutterCefPlugin + dartPluginClass: FlutterCefMacos (the Dart side just endorses the default method-channel instance). - Podspec renamed flutter_cef.podspec -> flutter_cef_macos.podspec (s.name + repo-root LICENSE path updated). Source files / resources unchanged. - Root flutter_cef pubspec now declares `platforms: macos: default_package: flutter_cef_macos` and depends on it, so a plain dependency on flutter_cef still provides macOS. - The build/bundle scripts moved together with native/, so their relative paths (build_cef_host.sh -> cef_host, bundle_cef_host.sh -> ../native) still resolve. App-facing API and the example are unchanged. The example's GeneratedPluginRegistrant now auto-resolves `import flutter_cef_macos` + FlutterCefPlugin.register — proof the endorsement is correct. Gate: analyze clean (all 3 packages); example macOS build succeeds; cef_host rebuilds + bundles from the new path; render smoke = 1 host + 6 helper subprocesses spawn, clean quit. Co-Authored-By: Claude Fable 5 --- .../Flutter/GeneratedPluginRegistrant.swift | 2 +- example/macos/Podfile.lock | 10 +++--- example/pubspec.lock | 7 +++++ .../lib/flutter_cef_macos.dart | 19 ++++++++++++ .../macos}/Classes/CefWebSession.swift | 0 .../macos}/Classes/FlutterCefPlugin.swift | 0 .../macos}/Resources/PrivacyInfo.xcprivacy | 0 .../macos/flutter_cef_macos.podspec | 4 +-- .../native}/build_cef_host.sh | 0 .../native}/cef_host/CMakeLists.txt | 0 .../native}/cef_host/Info.plist | 0 .../native}/cef_host/entitlements.plist | 0 .../cef_host/entitlements.release.plist | 0 .../native}/cef_host/helper-Info.plist.in | 0 .../native}/cef_host/main.mm | 0 .../native}/cef_host/process_helper.mm | 0 packages/flutter_cef_macos/pubspec.yaml | 31 +++++++++++++++++++ .../tool}/bundle_cef_host.sh | 0 pubspec.yaml | 8 ++++- 19 files changed, 72 insertions(+), 9 deletions(-) create mode 100644 packages/flutter_cef_macos/lib/flutter_cef_macos.dart rename {macos => packages/flutter_cef_macos/macos}/Classes/CefWebSession.swift (100%) rename {macos => packages/flutter_cef_macos/macos}/Classes/FlutterCefPlugin.swift (100%) rename {macos => packages/flutter_cef_macos/macos}/Resources/PrivacyInfo.xcprivacy (100%) rename macos/flutter_cef.podspec => packages/flutter_cef_macos/macos/flutter_cef_macos.podspec (94%) rename {native => packages/flutter_cef_macos/native}/build_cef_host.sh (100%) rename {native => packages/flutter_cef_macos/native}/cef_host/CMakeLists.txt (100%) rename {native => packages/flutter_cef_macos/native}/cef_host/Info.plist (100%) rename {native => packages/flutter_cef_macos/native}/cef_host/entitlements.plist (100%) rename {native => packages/flutter_cef_macos/native}/cef_host/entitlements.release.plist (100%) rename {native => packages/flutter_cef_macos/native}/cef_host/helper-Info.plist.in (100%) rename {native => packages/flutter_cef_macos/native}/cef_host/main.mm (100%) rename {native => packages/flutter_cef_macos/native}/cef_host/process_helper.mm (100%) create mode 100644 packages/flutter_cef_macos/pubspec.yaml rename {tool => packages/flutter_cef_macos/tool}/bundle_cef_host.sh (100%) diff --git a/example/macos/Flutter/GeneratedPluginRegistrant.swift b/example/macos/Flutter/GeneratedPluginRegistrant.swift index 93593d7..9b44c46 100644 --- a/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,7 +5,7 @@ import FlutterMacOS import Foundation -import flutter_cef +import flutter_cef_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FlutterCefPlugin.register(with: registry.registrar(forPlugin: "FlutterCefPlugin")) diff --git a/example/macos/Podfile.lock b/example/macos/Podfile.lock index c77b628..c43960d 100644 --- a/example/macos/Podfile.lock +++ b/example/macos/Podfile.lock @@ -1,20 +1,20 @@ PODS: - - flutter_cef (0.1.2): + - flutter_cef_macos (0.1.2): - FlutterMacOS - FlutterMacOS (1.0.0) DEPENDENCIES: - - flutter_cef (from `Flutter/ephemeral/.symlinks/plugins/flutter_cef/macos`) + - flutter_cef_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_cef_macos/macos`) - FlutterMacOS (from `Flutter/ephemeral`) EXTERNAL SOURCES: - flutter_cef: - :path: Flutter/ephemeral/.symlinks/plugins/flutter_cef/macos + flutter_cef_macos: + :path: Flutter/ephemeral/.symlinks/plugins/flutter_cef_macos/macos FlutterMacOS: :path: Flutter/ephemeral SPEC CHECKSUMS: - flutter_cef: 923b658c344928eb53d81cf23367973f5737457f + flutter_cef_macos: 29ec435deecded6902b4ea56dcf9e134d165a25d FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1 PODFILE CHECKSUM: 54d867c82ac51cbd61b565781b9fada492027009 diff --git a/example/pubspec.lock b/example/pubspec.lock index 600842d..2dec5a6 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -77,6 +77,13 @@ packages: relative: true source: path version: "0.1.2" + flutter_cef_macos: + dependency: transitive + description: + path: "../packages/flutter_cef_macos" + relative: true + source: path + version: "0.1.2" flutter_cef_platform_interface: dependency: transitive description: diff --git a/packages/flutter_cef_macos/lib/flutter_cef_macos.dart b/packages/flutter_cef_macos/lib/flutter_cef_macos.dart new file mode 100644 index 0000000..d58e84c --- /dev/null +++ b/packages/flutter_cef_macos/lib/flutter_cef_macos.dart @@ -0,0 +1,19 @@ +import 'package:flutter_cef_platform_interface/flutter_cef_platform_interface.dart'; + +/// The macOS implementation of `flutter_cef`. +/// +/// The macOS plugin is native-only: the Swift `FlutterCefPlugin` (see +/// `macos/Classes/`) spawns and talks to a per-view `cef_host` subprocess over +/// the `flutter_cef` method channel. This Dart class exists only to **endorse** +/// the default method-channel platform instance at registration time; there is +/// no macOS-specific Dart behavior. +/// +/// Registered via `dartPluginClass: FlutterCefMacos` in this package's pubspec — +/// the Flutter tool calls [registerWith] during plugin registration. +class FlutterCefMacos { + /// Sets the [FlutterCefPlatform] instance to the method-channel + /// implementation (the contract the native macOS plugin speaks). + static void registerWith() { + FlutterCefPlatform.instance = MethodChannelFlutterCef(); + } +} diff --git a/macos/Classes/CefWebSession.swift b/packages/flutter_cef_macos/macos/Classes/CefWebSession.swift similarity index 100% rename from macos/Classes/CefWebSession.swift rename to packages/flutter_cef_macos/macos/Classes/CefWebSession.swift diff --git a/macos/Classes/FlutterCefPlugin.swift b/packages/flutter_cef_macos/macos/Classes/FlutterCefPlugin.swift similarity index 100% rename from macos/Classes/FlutterCefPlugin.swift rename to packages/flutter_cef_macos/macos/Classes/FlutterCefPlugin.swift diff --git a/macos/Resources/PrivacyInfo.xcprivacy b/packages/flutter_cef_macos/macos/Resources/PrivacyInfo.xcprivacy similarity index 100% rename from macos/Resources/PrivacyInfo.xcprivacy rename to packages/flutter_cef_macos/macos/Resources/PrivacyInfo.xcprivacy diff --git a/macos/flutter_cef.podspec b/packages/flutter_cef_macos/macos/flutter_cef_macos.podspec similarity index 94% rename from macos/flutter_cef.podspec rename to packages/flutter_cef_macos/macos/flutter_cef_macos.podspec index acbb104..d1586cc 100644 --- a/macos/flutter_cef.podspec +++ b/packages/flutter_cef_macos/macos/flutter_cef_macos.podspec @@ -5,7 +5,7 @@ # that with native/build_cef_host.sh; see the README for bundling/signing. # Pod::Spec.new do |s| - s.name = 'flutter_cef' + s.name = 'flutter_cef_macos' s.version = '0.1.2' s.summary = 'Live Chromium (CEF) browser as a Flutter Texture (macOS).' s.description = <<-DESC @@ -14,7 +14,7 @@ Texture so it composites, transforms, and clips like any widget and keeps rendering when off-screen. macOS only. DESC s.homepage = 'https://github.com/FlutterFlow/flutter_cef' - s.license = { :file => '../LICENSE' } + s.license = { :file => '../../../LICENSE' } s.author = { 'flutter_cef contributors' => '' } s.source = { :path => '.' } s.source_files = 'Classes/**/*' diff --git a/native/build_cef_host.sh b/packages/flutter_cef_macos/native/build_cef_host.sh similarity index 100% rename from native/build_cef_host.sh rename to packages/flutter_cef_macos/native/build_cef_host.sh diff --git a/native/cef_host/CMakeLists.txt b/packages/flutter_cef_macos/native/cef_host/CMakeLists.txt similarity index 100% rename from native/cef_host/CMakeLists.txt rename to packages/flutter_cef_macos/native/cef_host/CMakeLists.txt diff --git a/native/cef_host/Info.plist b/packages/flutter_cef_macos/native/cef_host/Info.plist similarity index 100% rename from native/cef_host/Info.plist rename to packages/flutter_cef_macos/native/cef_host/Info.plist diff --git a/native/cef_host/entitlements.plist b/packages/flutter_cef_macos/native/cef_host/entitlements.plist similarity index 100% rename from native/cef_host/entitlements.plist rename to packages/flutter_cef_macos/native/cef_host/entitlements.plist diff --git a/native/cef_host/entitlements.release.plist b/packages/flutter_cef_macos/native/cef_host/entitlements.release.plist similarity index 100% rename from native/cef_host/entitlements.release.plist rename to packages/flutter_cef_macos/native/cef_host/entitlements.release.plist diff --git a/native/cef_host/helper-Info.plist.in b/packages/flutter_cef_macos/native/cef_host/helper-Info.plist.in similarity index 100% rename from native/cef_host/helper-Info.plist.in rename to packages/flutter_cef_macos/native/cef_host/helper-Info.plist.in diff --git a/native/cef_host/main.mm b/packages/flutter_cef_macos/native/cef_host/main.mm similarity index 100% rename from native/cef_host/main.mm rename to packages/flutter_cef_macos/native/cef_host/main.mm diff --git a/native/cef_host/process_helper.mm b/packages/flutter_cef_macos/native/cef_host/process_helper.mm similarity index 100% rename from native/cef_host/process_helper.mm rename to packages/flutter_cef_macos/native/cef_host/process_helper.mm diff --git a/packages/flutter_cef_macos/pubspec.yaml b/packages/flutter_cef_macos/pubspec.yaml new file mode 100644 index 0000000..e293fe6 --- /dev/null +++ b/packages/flutter_cef_macos/pubspec.yaml @@ -0,0 +1,31 @@ +name: flutter_cef_macos +description: "macOS implementation of the flutter_cef plugin: a live Chromium (CEF) browser rendered off-screen in a cef_host subprocess and shown as a Flutter Texture." +version: 0.1.2 +homepage: https://github.com/FlutterFlow/flutter_cef +repository: https://github.com/FlutterFlow/flutter_cef + +environment: + sdk: ^3.4.0 + flutter: '>=3.22.0' + +dependencies: + flutter: + sdk: flutter + flutter_cef_platform_interface: + path: ../flutter_cef_platform_interface + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^6.0.0 + +flutter: + plugin: + # Endorsed implementation of the app-facing `flutter_cef` plugin. + implements: flutter_cef + platforms: + macos: + # Swift host plugin (spawns + talks to the cef_host subprocess). + pluginClass: FlutterCefPlugin + # Dart entrypoint: endorses the default method-channel platform instance. + dartPluginClass: FlutterCefMacos diff --git a/tool/bundle_cef_host.sh b/packages/flutter_cef_macos/tool/bundle_cef_host.sh similarity index 100% rename from tool/bundle_cef_host.sh rename to packages/flutter_cef_macos/tool/bundle_cef_host.sh diff --git a/pubspec.yaml b/pubspec.yaml index 71a7d4a..e3437f3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,6 +19,10 @@ dependencies: sdk: flutter flutter_cef_platform_interface: path: packages/flutter_cef_platform_interface + # Endorsed default macOS implementation (see `default_package` below). Depended + # on directly so a plain dependency on `flutter_cef` provides macOS support. + flutter_cef_macos: + path: packages/flutter_cef_macos dev_dependencies: flutter_test: @@ -43,7 +47,9 @@ flutter: plugin: platforms: macos: - pluginClass: FlutterCefPlugin + # Federated: the macOS implementation lives in the endorsed + # flutter_cef_macos package (see packages/flutter_cef_macos). + default_package: flutter_cef_macos # To add assets to your plugin package, add an assets section, like this: # assets: From e180c3b3207ecbe5a89737feb6b8690a4f194b69 Mon Sep 17 00:00:00 2001 From: wenkaifan0720 Date: Tue, 9 Jun 2026 16:25:16 -0700 Subject: [PATCH 3/5] docs(P4): PORTING.md + federated README/CHANGELOG; bump to 0.1.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Final phase of the federated restructure. Adds PORTING.md — the contract for a Windows/Linux implementation: the small Dart/package side (pubspec endorsement + registerWith), the host-plugin responsibilities, and a precise platform-seam map for cef_host (shared surface / IPC transport / app loop / sandbox / framework path) with main.mm file:line references and the macOS reference for each. Documents the decision to DEFER the native core/platform code-split until the first real non-macOS port (a platform abstraction designed without a second consumer guesses the seam wrong; the seam is mapped in PORTING.md instead). Updates README (federated layout, moved build/bundle paths, Windows/Linux roadmap → PORTING.md) + example README paths. Bumps all four packages to 0.1.3. Gate: analyze clean (all 3 packages); git diff --check clean. Co-Authored-By: Claude Fable 5 --- .gitignore | 7 +- CHANGELOG.md | 11 ++ PORTING.md | 122 ++++++++++++++++++ README.md | 21 ++- example/README.md | 6 +- example/pubspec.lock | 6 +- .../macos/flutter_cef_macos.podspec | 2 +- packages/flutter_cef_macos/pubspec.yaml | 2 +- .../pubspec.yaml | 2 +- pubspec.yaml | 2 +- specs/cross-platform/PLAN.md | 12 +- 11 files changed, 169 insertions(+), 24 deletions(-) create mode 100644 PORTING.md diff --git a/.gitignore b/.gitignore index 2fe29f5..8f66413 100644 --- a/.gitignore +++ b/.gitignore @@ -32,9 +32,10 @@ migrate_working_dir/ /build/ /coverage/ -# Native CEF host: build output + fetched CEF SDK (see native/build_cef_host.sh). -native/cef_host/build/ -native/**/cef_binary_*/ +# Native CEF host: build output + fetched CEF SDK. Path-independent so it keeps +# matching regardless of where the macOS package lives (see build_cef_host.sh). +**/cef_host/build/ +**/cef_binary_*/ # Federated sub-packages are libraries too — no pubspec.lock. /packages/*/pubspec.lock diff --git a/CHANGELOG.md b/CHANGELOG.md index 27c322d..08f27a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +## 0.1.3 + +* **Federated package structure** (no API change): `flutter_cef` is now a + federated plugin — the app-facing package plus + `flutter_cef_platform_interface` (the shared Dart types + method-channel + contract) and the endorsed `flutter_cef_macos` (Swift plugin + `cef_host`). + Consumers keep depending on `flutter_cef` and importing + `package:flutter_cef/flutter_cef.dart` exactly as before. A Windows or Linux + implementation can now be added as a sibling `flutter_cef_` package — see + [`PORTING.md`](PORTING.md) for the contract and the platform-seam map. + ## 0.1.2 * **Navigation scheme allowlist**: `CefWebView(allowedSchemes: {...})` restricts diff --git a/PORTING.md b/PORTING.md new file mode 100644 index 0000000..638c621 --- /dev/null +++ b/PORTING.md @@ -0,0 +1,122 @@ +# Porting flutter_cef to a new platform + +`flutter_cef` is a **federated** Flutter plugin. macOS ships today +(`flutter_cef_macos`); Windows and Linux can be added as sibling +implementation packages without touching the app-facing API or the macOS +implementation. CEF itself is cross-platform — the work is re-implementing the +thin platform glue around it. + +``` +flutter_cef/ # app-facing package (re-exports the API) + packages/ + flutter_cef_platform_interface/ # the shared Dart contract (no platform code) + flutter_cef_macos/ # the macOS implementation (reference) + flutter_cef_windows/ _linux/ # <- you add these +``` + +## 1. The Dart / package side (small) + +The Dart `lib/` is already platform-neutral and the cross-platform contract is +the **method-channel protocol** (channel name + method names + event names), +so a new platform needs almost no Dart. + +1. Create `packages/flutter_cef_/` with a `pubspec.yaml`: + ```yaml + name: flutter_cef_ + dependencies: + flutter_cef_platform_interface: { path: ../flutter_cef_platform_interface } + flutter: + plugin: + implements: flutter_cef + platforms: + : + pluginClass: FlutterCefPlugin # your native host plugin + dartPluginClass: FlutterCef # endorses the channel instance + ``` +2. `lib/flutter_cef_.dart` — endorse the default method-channel instance + (mirror `flutter_cef_macos`'s `FlutterCefMacos.registerWith`): + ```dart + class FlutterCef { + static void registerWith() => + FlutterCefPlatform.instance = MethodChannelFlutterCef(); + } + ``` +3. Endorse it from the app-facing package: in `flutter_cef/pubspec.yaml` add + `platforms: : default_package: flutter_cef_` and a path dependency on + it (mirror the `macos:` entry). + +That's the entire Dart side. Everything else is native. + +## 2. The host plugin (your `FlutterCefPlugin` equivalent) + +Reference: `packages/flutter_cef_macos/macos/Classes/` (Swift). The host plugin +lives in the Flutter app's process and: + +- Registers a **platform texture** (a `FlutterTexture` on macOS) backed by a + shared GPU surface, and hands its id to the page. +- Spawns **one `cef_host` subprocess per view**, passing + `--url --width --height --dpr --iosurface-id --ipc --allowed-schemes` as argv. +- Relays method-channel calls → IPC opcodes to `cef_host`, and IPC events from + `cef_host` → `invokeMethod` back to Dart. + +The method/event protocol it must implement is the same on every platform — see +the `case` labels in `FlutterCefPlugin.handle` (host→native verbs: `create`, +`navigate`, `loadTrusted`, `resize`, `dispose`, `pointer`, `key`, `reload`, +`executeJavaScript`, cookies, IME, …) and the `emit(...)` calls (native→Dart +events: `cursor`, `loadingState`, `title`, `url`, `consoleMessage`, `jsDialog`, +`cookies`, `imeCompositionBounds`, …). + +## 3. The `cef_host` subprocess — the platform seams + +Reference: `packages/flutter_cef_macos/native/cef_host/` (`main.mm` is the +browser process, `process_helper.mm` the CEF child processes). The CEF client / +app / handler logic, the browser-control functions, the navigation scheme +allowlist, and the **IPC opcode protocol** are platform-agnostic C++ and can be +reused verbatim. Only these seams are macOS-specific (file:line are into +`main.mm` at the time of writing): + +| Seam | macOS reference | What your platform provides | +| --- | --- | --- | +| **Shared surface** — receive painted frames and present them to the host texture. CEF delivers either software `OnPaint` (CPU buffer) or `OnAcceleratedPaint` (a shared GPU texture handle). | `OnPaint` / `OnAcceleratedPaint` + the `IOSurface*` ops (~lines 346–450); `g_surface` (~133). macOS uses an IOSurface-backed `CVPixelBuffer`. | **Windows**: a shared D3D11 texture / DXGI keyed-mutex handle. **Linux**: shared memory or a DMA-buf, presented via the platform texture. Look the surface up by the `--iosurface-id`-equivalent handle the host passes. | +| **IPC transport** — a framed bidirectional byte stream to the host. Wire format: 4-byte big-endian length prefix, then `[opcode][payload]`. | `WriteAll`/`ReadAll` (207–235), `SendFrame` (~237–249), `ConnectUnixSocket` (1341+), the read loop (~1140). Unix domain socket. | **Windows**: a named pipe. **Linux**: a Unix domain socket (reuse as-is). Keep the same length-prefixed framing. | +| **App / run loop** — give CEF a host application + message loop, and a per-process cache dir. | `CefHostApplication : NSApplication` (304–330); `@autoreleasepool` + `sharedApplication` (1420+); `NSTemporaryDirectory()` cache; `_NSGetExecutablePath` (1335). | **Windows/Linux**: the platform's CEF message-loop integration (`CefRunMessageLoop` or OS loop) and a per-user, per-process cache path. | +| **Sandbox** — bring the child processes into the Chromium sandbox in a signed/release build. | `process_helper.mm`: `CefScopedSandboxContext` (release only); `settings.no_sandbox` toggled by `CEF_HOST_ADHOC` (1426/1433). | **Windows**: link the `cef_sandbox` static lib + `CefScopedSandboxContext`. **Linux**: the SUID / user-namespace sandbox helper. | +| **Framework / resource path** — point CEF at the CEF binary distribution. | `CefScopedLibraryLoader::LoadInMain`/`LoadInHelper`; `framework_dir_path` / `main_bundle_path` (1453/1466). | The equivalent paths for your bundle layout. | +| **Build + bundle + sign** | `native/build_cef_host.sh` (CMake, `CEF_MULTI_PROCESS` / `CEF_HOST_ADHOC` flags), `tool/bundle_cef_host.sh`. | A platform build that produces `cef_host` + the CEF runtime, and a bundling step into the host app. | + +The fetched CEF distribution already ships per-platform binaries and a CMake +config (`cef_binary_*_{macosarm64,windows64,linux64}`), so most of +`build_cef_host.sh` and `CMakeLists.txt` is reusable — adjust the +platform-specific link libs and the surface/transport sources. + +## 4. Recommended: extract a `core/` + `platform/` split *with* your port + +The macOS `main.mm` currently keeps the portable CEF logic and the macOS glue in +one translation unit. The seams above are the natural cut line: + +``` +native/cef_host/ + core/ # protocol (opcodes + framing), CEF client/app, browser control, + # navigation allowlist, dispatch loop — depends only on platform.h + platform/ + mac/ # IOSurface/Metal, Cocoa app, Unix socket, sandbox, paths + / # your implementations of the same platform.h interface +``` + +This split is deliberately **not** done up front: a platform abstraction +designed without a second consumer tends to guess the seam wrong. Do it as the +first step of your port, using the table above as the contract — extract the +portable pieces into `core/` behind a small `platform.h`, implement `platform.h` +for macOS (moving the existing code) and for your OS, and you have a shared core +validated by two real platforms. + +## Checklist + +- [ ] `flutter_cef_` package: pubspec (`implements`, `pluginClass`, + `dartPluginClass`) + `registerWith`. +- [ ] Endorse via `default_package` in the app-facing `flutter_cef/pubspec.yaml`. +- [ ] Host plugin: texture registration, subprocess spawn, channel ↔ IPC relay. +- [ ] `cef_host`: implement the five seams (surface, transport, app loop, + sandbox, paths); reuse the CEF logic + opcode protocol. +- [ ] Build + bundle + (release) signing for the platform. +- [ ] `example/` runs on the platform; pointer / keyboard / IME / navigation work. diff --git a/README.md b/README.md index ee582ec..d8c9d6f 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Embed a **live Chromium browser** (via the [Chromium Embedded Framework](https://bitbucket.org/chromiumembedded/cef/)) as a Flutter widget — rendered into a `Texture`, so it composites, transforms, clips, and zooms like any other widget, and **keeps rendering even when off-screen / not focused**. Pointer, scroll, and trackpad two-finger pans are forwarded by coordinate (pans are caught even when an ancestor opts into Flutter's trackpad gesture API, as canvas hosts do), and keyboard input reaches the page as real `keydown → keypress → keyup` events (Enter activates a focused button / submits a form, Space toggles a checkbox) — including platform IME composition for CJK / emoji and the ⌃⌘Space emoji picker. Text input is bound to the hosting `FlutterView` (as `EditableText` does), so it **works in multi-view / multi-window apps**; the page cursor drives a `MouseRegion`. -> Status: **experimental, macOS 12+ only** (CEF 144 runtime floor). Real Chromium (any site — JS/CSS/WebGL/video). **Multi-process by default** (GPU-accelerated OSR — `OnAcceleratedPaint` GPU compositing into a shared IOSurface, Retina-crisp; renderer/utility crashes isolated, so heavy SPAs like Google sign-in render and survive); `CEF_MULTI_PROCESS=OFF native/build_cef_host.sh` for the simpler single-process build. No mobile (iOS bans third-party engines); desktop by nature. +> Status: **experimental, macOS 12+ only** (CEF 144 runtime floor). Real Chromium (any site — JS/CSS/WebGL/video). **Multi-process by default** (GPU-accelerated OSR — `OnAcceleratedPaint` GPU compositing into a shared IOSurface, Retina-crisp; renderer/utility crashes isolated, so heavy SPAs like Google sign-in render and survive); `CEF_MULTI_PROCESS=OFF packages/flutter_cef_macos/native/build_cef_host.sh` for the simpler single-process build. No mobile (iOS bans third-party engines); desktop by nature. ```dart import 'package:flutter_cef/flutter_cef.dart'; @@ -76,9 +76,11 @@ Same pattern JCEF (JetBrains) and CefSharp use to render Chromium into a non-nat CEF (~200 MB) is **fetched**, not vendored. Build the renderer once: ```sh +# The macOS implementation lives in packages/flutter_cef_macos. +cd packages/flutter_cef_macos native/build_cef_host.sh # fetches CEF + builds cef_host.app export FLUTTER_CEF_HOST="$PWD/native/cef_host/build/cef_host.app/Contents/MacOS/cef_host" -cd example && flutter run -d macos +cd ../../example && flutter run -d macos ``` ### Bundling into a distributable app @@ -89,11 +91,12 @@ automatically (`$FLUTTER_CEF_HOST` → pod resources → `Contents/Frameworks` `Contents/Helpers`). After `flutter build macos`, run: ```sh -path/to/flutter_cef/tool/bundle_cef_host.sh "build/macos/.../YourApp.app" "" "" +packages/flutter_cef_macos/tool/bundle_cef_host.sh "build/macos/.../YourApp.app" "" "" ``` or wire it as a Run Script build phase on your Runner target (snippet in -`tool/bundle_cef_host.sh`) so it runs before Xcode's code-sign phase. Your host +`packages/flutter_cef_macos/tool/bundle_cef_host.sh`) so it runs before Xcode's +code-sign phase. Your host app **must not be App-Sandboxed** (CEF spawns the helper, shares a global IOSurface, writes a cache); entitlements need `com.apple.security.cs.disable-library-validation` + JIT — see @@ -117,7 +120,8 @@ build flag, `CEF_HOST_ADHOC` (default `ON`): The `OFF` posture only *validates* under correct **inside-out Developer-ID signing** of the `cef_host` tree (deepest helper → `libcef_sandbox.dylib` + CEF framework → host, depth-first, Hardened Runtime + trusted timestamp). Build it -with `CEF_HOST_ADHOC=OFF CODESIGN_ID="" native/build_cef_host.sh`, +with `CEF_HOST_ADHOC=OFF CODESIGN_ID="" +packages/flutter_cef_macos/native/build_cef_host.sh`, or — when bundled into a host app — let the app's own signing re-sign the tree with those entitlements. Ad-hoc/dev builds run unsandboxed by necessity (the sandbox can't validate without proper signing), which is why `ON` is the default. @@ -178,8 +182,11 @@ Next: scheme handlers, a typed DevTools/CDP client (the inspector window already ships via `openDevTools`; this is the programmatic CDP surface), and `CefPermissionHandler` (WebRTC camera/mic prompts). -- **Windows / Linux** — the federated structure is ready; each needs its own host - + shared-texture path. +- **Windows / Linux** — the package is **federated** (`flutter_cef` + + `flutter_cef_platform_interface` + `flutter_cef_macos`); a new platform is a + sibling `flutter_cef_` package. The CEF logic + IPC protocol are portable; + each OS supplies its own host plugin + shared-texture / transport / sandbox + glue. See [`PORTING.md`](PORTING.md) for the full contract and seam map. ## Credits diff --git a/example/README.md b/example/README.md index 3cd8c23..12bcccb 100644 --- a/example/README.md +++ b/example/README.md @@ -11,10 +11,10 @@ CEF (~200 MB) is fetched, not vendored — build the renderer once, point the plugin at it, then run: ```sh -cd .. # package root -native/build_cef_host.sh # fetches CEF + builds cef_host.app (one-time) +cd ../packages/flutter_cef_macos # the macOS implementation package +native/build_cef_host.sh # fetches CEF + builds cef_host.app (one-time) export FLUTTER_CEF_HOST="$PWD/native/cef_host/build/cef_host.app/Contents/MacOS/cef_host" -cd example && flutter run -d macos +cd ../../example && flutter run -d macos ``` Without `FLUTTER_CEF_HOST` (or a bundled `cef_host.app` — see the root diff --git a/example/pubspec.lock b/example/pubspec.lock index 2dec5a6..65d6942 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -76,21 +76,21 @@ packages: path: ".." relative: true source: path - version: "0.1.2" + version: "0.1.3" flutter_cef_macos: dependency: transitive description: path: "../packages/flutter_cef_macos" relative: true source: path - version: "0.1.2" + version: "0.1.3" flutter_cef_platform_interface: dependency: transitive description: path: "../packages/flutter_cef_platform_interface" relative: true source: path - version: "0.1.2" + version: "0.1.3" flutter_driver: dependency: transitive description: flutter diff --git a/packages/flutter_cef_macos/macos/flutter_cef_macos.podspec b/packages/flutter_cef_macos/macos/flutter_cef_macos.podspec index d1586cc..503dc43 100644 --- a/packages/flutter_cef_macos/macos/flutter_cef_macos.podspec +++ b/packages/flutter_cef_macos/macos/flutter_cef_macos.podspec @@ -6,7 +6,7 @@ # Pod::Spec.new do |s| s.name = 'flutter_cef_macos' - s.version = '0.1.2' + s.version = '0.1.3' s.summary = 'Live Chromium (CEF) browser as a Flutter Texture (macOS).' s.description = <<-DESC Embed a live Chromium browser via CEF off-screen rendering, shown as a Flutter diff --git a/packages/flutter_cef_macos/pubspec.yaml b/packages/flutter_cef_macos/pubspec.yaml index e293fe6..636ea35 100644 --- a/packages/flutter_cef_macos/pubspec.yaml +++ b/packages/flutter_cef_macos/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_cef_macos description: "macOS implementation of the flutter_cef plugin: a live Chromium (CEF) browser rendered off-screen in a cef_host subprocess and shown as a Flutter Texture." -version: 0.1.2 +version: 0.1.3 homepage: https://github.com/FlutterFlow/flutter_cef repository: https://github.com/FlutterFlow/flutter_cef diff --git a/packages/flutter_cef_platform_interface/pubspec.yaml b/packages/flutter_cef_platform_interface/pubspec.yaml index 9697a74..a1a652a 100644 --- a/packages/flutter_cef_platform_interface/pubspec.yaml +++ b/packages/flutter_cef_platform_interface/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_cef_platform_interface description: "A common platform interface for the flutter_cef plugin: the shared Dart types and the method-channel contract that each platform implementation (macOS, and future Windows/Linux) speaks." -version: 0.1.2 +version: 0.1.3 homepage: https://github.com/FlutterFlow/flutter_cef repository: https://github.com/FlutterFlow/flutter_cef # NOTE: We strongly prefer a constraint that allows the current version and diff --git a/pubspec.yaml b/pubspec.yaml index e3437f3..5e735ac 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_cef description: "Embed a live Chromium (CEF) browser as a Flutter Texture on macOS: composites and clips like any widget, with input forwarding, a JS bridge, navigation and page events." -version: 0.1.2 +version: 0.1.3 homepage: https://github.com/FlutterFlow/flutter_cef repository: https://github.com/FlutterFlow/flutter_cef issue_tracker: https://github.com/FlutterFlow/flutter_cef/issues diff --git a/specs/cross-platform/PLAN.md b/specs/cross-platform/PLAN.md index a9476cc..a6445c6 100644 --- a/specs/cross-platform/PLAN.md +++ b/specs/cross-platform/PLAN.md @@ -69,10 +69,14 @@ flutter_cef/ # repo root = app-facing package (unchanged CMake, FLUTTER_CEF_HOST resolution in the Swift plugin); add `lib/flutter_cef_macos.dart` + `dartPluginClass`. Root pubspec endorses it via `default_package`. Gate: example macOS build + signed-host smoke. -- **P3 — native core/platform split.** Inside `flutter_cef_macos/native/cef_host`, - extract `core/` (protocol.h + CEF client/app + browser control + dispatch - loop, depending only on `core/platform.h`) and `platform/mac/` (the ~50 - macOS-specific sites). Gate: rebuild cef_host (ad-hoc + signed) clean. +- **P3 — native core/platform split. DEFERRED BY DESIGN.** A platform + abstraction designed without a second consumer tends to guess the seam wrong, + and a blind 1486-line teardown risks regressing the just-proven macOS build for + no validated benefit. Instead the seam is **mapped precisely in `PORTING.md`** + (surface / IPC transport / app loop / sandbox / framework-path, with + `main.mm` file:line references), and the `core/` + `platform/` code-split is + the documented first step of the first real non-macOS port — where two + consumers validate the abstraction. - **P4 — docs.** `PORTING.md`, README structure, CHANGELOG. Gate: `git diff --check`. ## Invariants From d5ffcb119aa2fc42ae56a437f974f27275680154 Mon Sep 17 00:00:00 2001 From: wenkaifan0720 Date: Tue, 9 Jun 2026 16:48:44 -0700 Subject: [PATCH 4/5] audit: fix release-signing entitlements + federation packaging hygiene Final-audit remediation (batch 1 of 2): the one real security defect + the publishability/packaging gaps. Security (MEDIUM): - bundle_cef_host.sh selected the dev entitlements.plist (with get-task-allow) unconditionally, so a distributable bundle re-signed with a real identity silently re-added get-task-allow (a task-port priv-esc on a JIT process, and a notarization hard-fail). Now: ad-hoc ("-") keeps the dev plist; a real identity uses entitlements.release.plist; CEF_HOST_ENTITLEMENTS overrides. Packaging hygiene (MEDIUM/LOW) for the new federated sub-packages: - Add LICENSE, README.md, CHANGELOG.md, analysis_options.yaml to both flutter_cef_platform_interface and flutter_cef_macos (pana/pub.dev require per-package; the missing analysis_options meant their code wasn't linted). - Fix the podspec license path '../../../LICENSE' -> '../LICENSE' (the old path escaped the package tree and broke `pod lib lint` on a standalone extraction). - Add topics: + issue_tracker: to both sub-package pubspecs. - Document the publish procedure (path -> hosted constraint swap, bottom-up) in the root pubspec; keep path deps (correct for the source/path/git consumption Campus uses) and silence the analyzer's invalid_dependency warning with that rationale. Gate: flutter analyze clean across all three packages (whole-package). Co-Authored-By: Claude Fable 5 --- analysis_options.yaml | 9 ++++++++ packages/flutter_cef_macos/CHANGELOG.md | 7 +++++++ packages/flutter_cef_macos/LICENSE | 21 +++++++++++++++++++ packages/flutter_cef_macos/README.md | 16 ++++++++++++++ .../flutter_cef_macos/analysis_options.yaml | 12 +++++++++++ .../macos/flutter_cef_macos.podspec | 2 +- packages/flutter_cef_macos/pubspec.yaml | 6 ++++++ .../flutter_cef_macos/tool/bundle_cef_host.sh | 16 +++++++++++++- .../CHANGELOG.md | 6 ++++++ .../flutter_cef_platform_interface/LICENSE | 21 +++++++++++++++++++ .../flutter_cef_platform_interface/README.md | 14 +++++++++++++ .../analysis_options.yaml | 4 ++++ .../pubspec.yaml | 6 ++++++ pubspec.yaml | 7 +++++++ 14 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 packages/flutter_cef_macos/CHANGELOG.md create mode 100644 packages/flutter_cef_macos/LICENSE create mode 100644 packages/flutter_cef_macos/README.md create mode 100644 packages/flutter_cef_macos/analysis_options.yaml create mode 100644 packages/flutter_cef_platform_interface/CHANGELOG.md create mode 100644 packages/flutter_cef_platform_interface/LICENSE create mode 100644 packages/flutter_cef_platform_interface/README.md create mode 100644 packages/flutter_cef_platform_interface/analysis_options.yaml diff --git a/analysis_options.yaml b/analysis_options.yaml index a5744c1..93371c0 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,4 +1,13 @@ include: package:flutter_lints/flutter.yaml +analyzer: + errors: + # Federation members are intentionally path deps: this repo is consumed from + # source (path / git), where path deps resolve the siblings automatically and + # hosted constraints would force every consumer to add dependency_overrides. + # `pub publish` still enforces the swap to hosted constraints — see the + # publish note in pubspec.yaml — so this only silences `flutter analyze`. + invalid_dependency: ignore + # Additional information about this file can be found at # https://dart.dev/guides/language/analysis-options diff --git a/packages/flutter_cef_macos/CHANGELOG.md b/packages/flutter_cef_macos/CHANGELOG.md new file mode 100644 index 0000000..3d81ed8 --- /dev/null +++ b/packages/flutter_cef_macos/CHANGELOG.md @@ -0,0 +1,7 @@ +## 0.1.3 + +* Initial release of the federated macOS implementation of `flutter_cef` (the + Swift host plugin + the `cef_host` subprocess). Split out of the `flutter_cef` + package; no API change for app-facing consumers. Carries the navigation scheme + allowlist, the host-trusted-load exemption, and the `CEF_HOST_ADHOC` build + flag (signed-release sandbox + real Keychain + release entitlements). diff --git a/packages/flutter_cef_macos/LICENSE b/packages/flutter_cef_macos/LICENSE new file mode 100644 index 0000000..3f83b7d --- /dev/null +++ b/packages/flutter_cef_macos/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 the flutter_cef authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/flutter_cef_macos/README.md b/packages/flutter_cef_macos/README.md new file mode 100644 index 0000000..9f072d9 --- /dev/null +++ b/packages/flutter_cef_macos/README.md @@ -0,0 +1,16 @@ +# flutter_cef_macos + +The macOS implementation of the +[`flutter_cef`](https://github.com/FlutterFlow/flutter_cef) plugin. + +It renders a live Chromium (CEF) browser off-screen in a `cef_host` subprocess +and presents it as a Flutter `Texture`. This is the **endorsed** macOS +implementation, pulled in automatically when you depend on `flutter_cef` — you +should depend on `flutter_cef`, not this package directly. + +Native build + bundling lives here (`native/build_cef_host.sh`, +`tool/bundle_cef_host.sh`); see the root +[README](https://github.com/FlutterFlow/flutter_cef#building) for build, +bundling, and signing, and +[`PORTING.md`](https://github.com/FlutterFlow/flutter_cef/blob/main/PORTING.md) +for the platform-seam map. diff --git a/packages/flutter_cef_macos/analysis_options.yaml b/packages/flutter_cef_macos/analysis_options.yaml new file mode 100644 index 0000000..397776d --- /dev/null +++ b/packages/flutter_cef_macos/analysis_options.yaml @@ -0,0 +1,12 @@ +include: package:flutter_lints/flutter.yaml + +analyzer: + errors: + # Depends on flutter_cef_platform_interface via a path dep on purpose: this + # repo is consumed from source (path / git). `pub publish` still enforces the + # swap to a hosted constraint — see the publish note in the root pubspec — so + # this only silences `flutter analyze`. + invalid_dependency: ignore + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/packages/flutter_cef_macos/macos/flutter_cef_macos.podspec b/packages/flutter_cef_macos/macos/flutter_cef_macos.podspec index 503dc43..738df28 100644 --- a/packages/flutter_cef_macos/macos/flutter_cef_macos.podspec +++ b/packages/flutter_cef_macos/macos/flutter_cef_macos.podspec @@ -14,7 +14,7 @@ Texture so it composites, transforms, and clips like any widget and keeps rendering when off-screen. macOS only. DESC s.homepage = 'https://github.com/FlutterFlow/flutter_cef' - s.license = { :file => '../../../LICENSE' } + s.license = { :file => '../LICENSE' } s.author = { 'flutter_cef contributors' => '' } s.source = { :path => '.' } s.source_files = 'Classes/**/*' diff --git a/packages/flutter_cef_macos/pubspec.yaml b/packages/flutter_cef_macos/pubspec.yaml index 636ea35..5e29bdb 100644 --- a/packages/flutter_cef_macos/pubspec.yaml +++ b/packages/flutter_cef_macos/pubspec.yaml @@ -3,6 +3,12 @@ description: "macOS implementation of the flutter_cef plugin: a live Chromium (C version: 0.1.3 homepage: https://github.com/FlutterFlow/flutter_cef repository: https://github.com/FlutterFlow/flutter_cef +issue_tracker: https://github.com/FlutterFlow/flutter_cef/issues +topics: + - webview + - chromium + - cef + - browser environment: sdk: ^3.4.0 diff --git a/packages/flutter_cef_macos/tool/bundle_cef_host.sh b/packages/flutter_cef_macos/tool/bundle_cef_host.sh index b370ee6..b280110 100755 --- a/packages/flutter_cef_macos/tool/bundle_cef_host.sh +++ b/packages/flutter_cef_macos/tool/bundle_cef_host.sh @@ -14,6 +14,13 @@ # native/cef_host/build/cef_host.app # [signing-identity] default: "-" (ad-hoc). Pass your Developer ID / Apple # Development identity for a distributable build. +# +# Entitlements are chosen by signing posture: an ad-hoc ("-") signature is a dev +# bundle and keeps entitlements.plist (with get-task-allow, for local debugging); +# a real identity is a distributable build and uses entitlements.release.plist +# (NO get-task-allow — it hard-fails notarization and is a task-port +# privilege-escalation vector on a process running untrusted JIT'd web content). +# Override the choice explicitly with CEF_HOST_ENTITLEMENTS=. set -euo pipefail HERE="$(cd "$(dirname "$0")" && pwd)" @@ -21,7 +28,14 @@ APP="${1:?usage: bundle_cef_host.sh [cef_host.app] [identity]}" HOST_APP="${2:-${FLUTTER_CEF_HOST_APP:-$HERE/../native/cef_host/build/cef_host.app}}" IDENTITY="${3:-${EXPANDED_CODE_SIGN_IDENTITY:-}}" [ -z "$IDENTITY" ] && IDENTITY="-" -ENT="$HERE/../native/cef_host/entitlements.plist" +if [ -n "${CEF_HOST_ENTITLEMENTS:-}" ]; then + ENT="$CEF_HOST_ENTITLEMENTS" +elif [ "$IDENTITY" = "-" ]; then + ENT="$HERE/../native/cef_host/entitlements.plist" # dev / ad-hoc +else + ENT="$HERE/../native/cef_host/entitlements.release.plist" # distributable +fi +echo "[flutter_cef] signing identity: $IDENTITY ; entitlements: $ENT" [ -d "$APP" ] || { echo "no such app bundle: $APP" >&2; exit 1; } [ -d "$HOST_APP" ] || { diff --git a/packages/flutter_cef_platform_interface/CHANGELOG.md b/packages/flutter_cef_platform_interface/CHANGELOG.md new file mode 100644 index 0000000..30b8f07 --- /dev/null +++ b/packages/flutter_cef_platform_interface/CHANGELOG.md @@ -0,0 +1,6 @@ +## 0.1.3 + +* Initial release of the federated common platform interface for `flutter_cef`: + the shared Dart types (`cef_events`, `cef_input`) and the `FlutterCefPlatform` + contract with its default `MethodChannelFlutterCef`. Split out of the + `flutter_cef` package; no API change for app-facing consumers. diff --git a/packages/flutter_cef_platform_interface/LICENSE b/packages/flutter_cef_platform_interface/LICENSE new file mode 100644 index 0000000..3f83b7d --- /dev/null +++ b/packages/flutter_cef_platform_interface/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 the flutter_cef authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/flutter_cef_platform_interface/README.md b/packages/flutter_cef_platform_interface/README.md new file mode 100644 index 0000000..316d2fb --- /dev/null +++ b/packages/flutter_cef_platform_interface/README.md @@ -0,0 +1,14 @@ +# flutter_cef_platform_interface + +The common platform interface for the +[`flutter_cef`](https://github.com/FlutterFlow/flutter_cef) plugin. + +It holds the shared Dart types (`CefCookie`, `CefLoadError`, the input +mappings, …) and the `FlutterCefPlatform` contract that each platform +implementation (macOS today, Windows / Linux later) speaks. The cross-platform +contract is the method-channel protocol — see +[`PORTING.md`](https://github.com/FlutterFlow/flutter_cef/blob/main/PORTING.md). + +App developers should depend on `flutter_cef`, not this package directly. A new +platform implementation depends on this package and endorses the default +method-channel instance from its `registerWith`. diff --git a/packages/flutter_cef_platform_interface/analysis_options.yaml b/packages/flutter_cef_platform_interface/analysis_options.yaml new file mode 100644 index 0000000..a5744c1 --- /dev/null +++ b/packages/flutter_cef_platform_interface/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/packages/flutter_cef_platform_interface/pubspec.yaml b/packages/flutter_cef_platform_interface/pubspec.yaml index a1a652a..3569721 100644 --- a/packages/flutter_cef_platform_interface/pubspec.yaml +++ b/packages/flutter_cef_platform_interface/pubspec.yaml @@ -3,6 +3,12 @@ description: "A common platform interface for the flutter_cef plugin: the shared version: 0.1.3 homepage: https://github.com/FlutterFlow/flutter_cef repository: https://github.com/FlutterFlow/flutter_cef +issue_tracker: https://github.com/FlutterFlow/flutter_cef/issues +topics: + - webview + - chromium + - cef + - browser # NOTE: We strongly prefer a constraint that allows the current version and # future patch/minor releases of the interface so federated implementations # stay compatible. Breaking changes to this package require a major bump and a diff --git a/pubspec.yaml b/pubspec.yaml index 5e735ac..db7cac3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,6 +17,13 @@ environment: dependencies: flutter: sdk: flutter + # Federation members are wired as path deps: the repo is consumed from source + # (path / git), not pub.dev. PUBLISHING (when it happens): `pub publish` + # rejects path deps, so publish bottom-up — flutter_cef_platform_interface, + # then flutter_cef_macos, then this package — and at each step swap the sibling + # `path:` deps for hosted caret constraints (`^0.1.x`, the + # minor-compatible policy noted in the platform_interface pubspec), keeping the + # `path:` entries under `dependency_overrides` for local dev. flutter_cef_platform_interface: path: packages/flutter_cef_platform_interface # Endorsed default macOS implementation (see `default_package` below). Depended From 0c2797757f2fb43787039b59a8767822f297a861 Mon Sep 17 00:00:00 2001 From: wenkaifan0720 Date: Tue, 9 Jun 2026 16:53:23 -0700 Subject: [PATCH 5/5] audit: code-quality + doc-accuracy nits (batch 2 of 2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Final-audit remediation, batch 2 — verified, no behavior change. Dart: - cef_web_view: `_ensureSession` now bails on `!mounted` first, so the post-frame callback can't read a deactivated MediaQuery or resize a torn-down session after a same-frame removal. - Clarifying comments: the empty `_onPointerPanZoomStart` (required to route pan-zoom updates), `currentTextEditingValue` returning the empty scratch state by design (the page owns the text), and the process-global, never-torn-down static method-call handler. Native (cef_host main.mm): - Header `Args:`/opcode-range comments updated for `--allowed-schemes` and kOpLoadTrusted (0x34). - Gate the three informational bring-up `fprintf(stderr)`s behind FLUTTER_CEF_DEBUG (genuine error prints kept); a release no longer logs noise. - Close `g_ipc_fd` at teardown under the write mutex (was leaked) and clear it. - Document the benign identical-URL trusted-load edge. Swift: - Replace the stale FlutterCefPlugin header verb list (6 of ~27) with a pointer to the switch + PORTING.md as the authoritative protocol reference. Gate: analyze clean; 94 tests pass; cef_host rebuilds clean (0 errors); render smoke OK (host + helpers spawn, clean teardown). Co-Authored-By: Claude Fable 5 --- lib/src/cef_web_controller.dart | 4 +++ lib/src/cef_web_view.dart | 11 +++++++ .../macos/Classes/FlutterCefPlugin.swift | 15 +++++----- .../flutter_cef_macos/native/cef_host/main.mm | 29 ++++++++++++++----- 4 files changed, 44 insertions(+), 15 deletions(-) diff --git a/lib/src/cef_web_controller.dart b/lib/src/cef_web_controller.dart index 1bd2305..df104b9 100644 --- a/lib/src/cef_web_controller.dart +++ b/lib/src/cef_web_controller.dart @@ -116,6 +116,10 @@ class CefWebController { {}; static void _installHandler() { + // Process-global, installed once on the first controller and never torn + // down; it fans events out to live controllers by sessionId and is a no-op + // for disposed ones. It binds the channel of whatever FlutterCefPlatform + // instance is current at first install (one channel in practice). if (_handlerInstalled) return; _handlerInstalled = true; _channel.setMethodCallHandler((call) async { diff --git a/lib/src/cef_web_view.dart b/lib/src/cef_web_view.dart index 0a8ef00..3ae5d9b 100644 --- a/lib/src/cef_web_view.dart +++ b/lib/src/cef_web_view.dart @@ -142,6 +142,11 @@ class _CefWebViewState extends State } Future _ensureSession(Size size) async { + // Scheduled via a post-frame callback, which can fire after this State was + // disposed (same-frame removal). Bail before touching context / the + // controller so we don't read a deactivated MediaQuery or resize a + // torn-down session. + if (!mounted) return; final dpr = MediaQuery.maybeOf(context)?.devicePixelRatio ?? 1.0; final w = size.width.round(); final h = size.height.round(); @@ -317,6 +322,8 @@ class _CefWebViewState extends State // to the page as a scroll. (pan delta ≈ −scroll delta for the same intent, so // we forward it un-negated to match the wheel path above.) The OS doesn't add // momentum to OSR, so a per-event gain brings the distance closer to Chrome's. + // Required to make the Listener route pan-zoom *update* events; nothing to do + // on the start of a trackpad gesture. void _onPointerPanZoomStart(PointerPanZoomStartEvent e) {} void _onPointerPanZoomUpdate(PointerPanZoomUpdateEvent e) { @@ -629,6 +636,10 @@ class _CefWebViewState extends State _textInput?.setEditingState(_editingState); } + // Deliberately returns the empty scratch state, never a real buffer: the page + // (not Flutter) owns the text, so we keep `_editingState` as a fixed insertion + // point for composition and never mirror the page's content back to the + // framework. See `_editingState` / `_pushEditableGeometry`. @override TextEditingValue? get currentTextEditingValue => _editingState; diff --git a/packages/flutter_cef_macos/macos/Classes/FlutterCefPlugin.swift b/packages/flutter_cef_macos/macos/Classes/FlutterCefPlugin.swift index cc5b9d2..2f216f8 100644 --- a/packages/flutter_cef_macos/macos/Classes/FlutterCefPlugin.swift +++ b/packages/flutter_cef_macos/macos/Classes/FlutterCefPlugin.swift @@ -1,14 +1,13 @@ import Cocoa import FlutterMacOS -/// macOS plugin entry point. Channel `flutter_cef`. Verbs: -/// create {sessionId, url, width, height, dpr} -> {textureId, width, height} -/// navigate{sessionId, url} -/// resize {sessionId, width, height, dpr} -/// dispose {sessionId} -/// pointer {sessionId, type, button, clickCount, modifiers, x, y, dx, dy} -/// key {sessionId, type, modifiers, windowsKeyCode, nativeKeyCode, character} -/// Host -> Dart: invokeMethod("cursor", {sessionId, cursor}). +/// macOS plugin entry point. Channel `flutter_cef`. The host->native verbs (the +/// `case` labels in `handle(_:result:)`) and the native->Dart events (the +/// `emit(...)` calls in `create`) together form the cross-platform method-channel +/// protocol — see PORTING.md for the authoritative list. Each verb carries a +/// `sessionId`; `create` returns `{textureId, width, height}`. The Swift side +/// only relays: it spawns and talks to a per-view `cef_host` subprocess (see +/// `CefWebSession`) over the IPC opcode protocol. public class FlutterCefPlugin: NSObject, FlutterPlugin { private weak var textureRegistry: FlutterTextureRegistry? private var channel: FlutterMethodChannel? diff --git a/packages/flutter_cef_macos/native/cef_host/main.mm b/packages/flutter_cef_macos/native/cef_host/main.mm index 5f41bbb..c7211aa 100644 --- a/packages/flutter_cef_macos/native/cef_host/main.mm +++ b/packages/flutter_cef_macos/native/cef_host/main.mm @@ -27,11 +27,11 @@ // Keychain (OSCrypt) — and so requires correct inside-out Developer-ID signing. // // Args: --url= --width= --height= --dpr= --iosurface-id= -// --ipc= +// --ipc= --allowed-schemes= // // IPC wire format: 4-byte big-endian length prefix, then [opcode][payload]. // The full opcode table lives in the `kOp*` constants below — each carries its -// payload layout; cef_host -> host are 0x01-0x1a, host -> cef_host 0x10-0x33. +// payload layout; cef_host -> host are 0x01-0x1a, host -> cef_host 0x10-0x34. #import #import @@ -154,6 +154,9 @@ // the exact URL (and main frame) in OnBeforeBrowse means a page nav to a // different URL can never steal another load's exemption. A multiset tolerates // identical concurrent trusted loads. UI-thread only, so no lock. +// (A page racing the host to the EXACT same data:/file: URL could consume one +// armed entry, but that is benign — it loads the same content the host chose — +// so it is not defended beyond exact-URL matching.) std::multiset g_trusted_pending; // Pending JS dialog callbacks, keyed by id. UI-thread-only (OnJSDialog and the @@ -818,7 +821,8 @@ void OnBeforeCommandLineProcessing( } void OnContextInitialized() override { CEF_REQUIRE_UI_THREAD(); - fprintf(stderr, "[cef_host] OnContextInitialized\n"); + if (std::getenv("FLUTTER_CEF_DEBUG")) + fprintf(stderr, "[cef_host] OnContextInitialized\n"); CefWindowInfo window_info; window_info.SetAsWindowless(0); #ifdef CEF_HOST_MULTIPROCESS @@ -837,7 +841,8 @@ void OnContextInitialized() override { g_browser = CefBrowserHost::CreateBrowserSync( window_info, new HostClient(), g_initial_url, settings, nullptr, nullptr); - fprintf(stderr, "[cef_host] browser=%p\n", (void*)g_browser.get()); + if (std::getenv("FLUTTER_CEF_DEBUG")) + fprintf(stderr, "[cef_host] browser=%p\n", (void*)g_browser.get()); SendFrame(kOpReady, nullptr, 0); } IMPLEMENT_REFCOUNTING(HostApp); @@ -1470,16 +1475,26 @@ int main(int argc, char* argv[]) { fprintf(stderr, "[cef_host] CefInitialize failed\n"); return 1; } - fprintf(stderr, "[cef_host] CefInitialize OK (fd=%d surface=%p)\n", g_ipc_fd, - (void*)g_surface); + if (std::getenv("FLUTTER_CEF_DEBUG")) + fprintf(stderr, "[cef_host] CefInitialize OK (fd=%d surface=%p)\n", + g_ipc_fd, (void*)g_surface); std::thread reader; if (g_ipc_fd >= 0) reader = std::thread(&IpcReadLoop); std::thread(&WatchParentDeath, getppid()).detach(); CefRunMessageLoop(); if (reader.joinable()) { - shutdown(g_ipc_fd, SHUT_RDWR); + shutdown(g_ipc_fd, SHUT_RDWR); // unblock the reader's blocking read reader.join(); } + // Reader is joined (no more reads); close the socket under the write mutex + // (no concurrent SendFrame) and clear the fd so any late write is a no-op. + { + std::lock_guard lock(g_ipc_write_mutex); + if (g_ipc_fd >= 0) { + close(g_ipc_fd); + g_ipc_fd = -1; + } + } CefShutdown(); } return 0;