├── docs ├── .gitkeep ├── Interstellar sample app.gif ├── index.md └── _template.html ├── Examples ├── Examples.SharedCode │ ├── paket.references │ ├── Examples.SharedCode.fsproj │ └── Library.fs ├── Examples.Wpf.Chromium │ ├── paket.references │ ├── Examples.Wpf.Chromium.fsproj │ └── App.fs ├── Examples.macOS.WebKit │ ├── paket.references │ ├── Assets.xcassets │ │ ├── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── AppIcon-128.png │ │ │ ├── AppIcon-16.png │ │ │ ├── AppIcon-256.png │ │ │ ├── AppIcon-32.png │ │ │ ├── AppIcon-512.png │ │ │ ├── AppIcon-128@2x.png │ │ │ ├── AppIcon-16@2x.png │ │ │ ├── AppIcon-256@2x.png │ │ │ ├── AppIcon-32@2x.png │ │ │ ├── AppIcon-512@2x.png │ │ │ └── Contents.json │ ├── Entitlements.plist │ ├── Main.fs │ ├── AppDelegate.fs │ ├── Info.plist │ └── Examples.macOS.WebKit.fsproj └── Examples.WinForms.Chromium │ ├── paket.references │ ├── Examples.WinForms.Chromium.fsproj │ └── App.fs ├── src ├── Interstellar.MacOS.WebKit │ ├── paket.references │ ├── BrowserApp.fs │ ├── Interstellar.MacOS.WebKit.fsproj │ ├── BrowserWindow.fs │ └── Browser.fs ├── Interstellar.Chromium │ ├── paket.references │ ├── Interstellar.Chromium.fsproj │ ├── JavascriptInjectionFilter.fs │ └── Browser.fs ├── Interstellar.Wpf.Chromium │ ├── paket.references │ ├── Interstellar.Wpf.Chromium.fsproj │ ├── BrowserApp.fs │ └── BrowserWindow.fs ├── Interstellar.WinForms.Chromium │ ├── paket.references │ ├── Interstellar.WinForms.Chromium.fsproj │ ├── BrowserApp.fs │ └── BrowserWindow.fs ├── Interstellar.Core │ ├── paket.references │ ├── Interstellar.Core.fsproj │ ├── IBrowserExtensions.fs │ ├── Script1.fsx │ └── Api.fs └── Interstellar.WindowsCommon.Chromium │ └── Platform.fs ├── .nuget └── nuget.exe ├── Directory.Build.props ├── templates ├── minimal │ └── src │ │ ├── InterstellarApp.macOS │ │ ├── Assets.xcassets │ │ │ ├── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ ├── AppIcon-128.png │ │ │ │ ├── AppIcon-16.png │ │ │ │ ├── AppIcon-256.png │ │ │ │ ├── AppIcon-32.png │ │ │ │ ├── AppIcon-512.png │ │ │ │ ├── AppIcon-128@2x.png │ │ │ │ ├── AppIcon-16@2x.png │ │ │ │ ├── AppIcon-256@2x.png │ │ │ │ ├── AppIcon-32@2x.png │ │ │ │ ├── AppIcon-512@2x.png │ │ │ │ └── Contents.json │ │ ├── Entitlements.plist │ │ ├── Main.fs │ │ ├── Info.plist │ │ ├── AppDelegate.fs │ │ └── InterstellarApp.macOS.fsproj │ │ ├── global.json │ │ ├── InterstellarApp.Core │ │ ├── InterstellarApp.Core.fsproj │ │ └── Library.fs │ │ ├── InterstellarApp.Windows │ │ ├── InterstellarApp.Windows.fsproj │ │ └── Program.fs │ │ ├── .template.config │ │ └── template.json │ │ ├── InterstellarApp.macOS.sln │ │ └── InterstellarApp.Windows.sln └── Interstellar.Template.nuspec ├── .vscode └── settings.json ├── paket.dependencies ├── .config └── dotnet-tools.json ├── AssemblyAndPackageInfo.props ├── LICENSE ├── .github └── workflows │ ├── test-templates.yml │ └── ci.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── nupkg-hack.fsx ├── Interstellar.MacOS.sln ├── README.md ├── Interstellar.Windows.sln ├── paket.lock └── .gitignore /docs/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Examples/Examples.SharedCode/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core -------------------------------------------------------------------------------- /Examples/Examples.Wpf.Chromium/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core -------------------------------------------------------------------------------- /Examples/Examples.macOS.WebKit/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core -------------------------------------------------------------------------------- /src/Interstellar.MacOS.WebKit/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core -------------------------------------------------------------------------------- /Examples/Examples.WinForms.Chromium/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core -------------------------------------------------------------------------------- /src/Interstellar.Chromium/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | CefSharp.Common -------------------------------------------------------------------------------- /src/Interstellar.Wpf.Chromium/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | CefSharp.Wpf -------------------------------------------------------------------------------- /src/Interstellar.WinForms.Chromium/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | CefSharp.WinForms -------------------------------------------------------------------------------- /.nuget/nuget.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/Interstellar/HEAD/.nuget/nuget.exe -------------------------------------------------------------------------------- /src/Interstellar.Core/paket.references: -------------------------------------------------------------------------------- 1 | FSharp.Core 2 | System.Text.Encodings.Web 3 | BlackFox.MasterOfFoo -------------------------------------------------------------------------------- /docs/Interstellar sample app.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/Interstellar/HEAD/docs/Interstellar sample app.gif -------------------------------------------------------------------------------- /Examples/Examples.macOS.WebKit/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | True 4 | 5 | -------------------------------------------------------------------------------- /templates/minimal/src/InterstellarApp.macOS/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "FSharp.fsiExtraParameters": [ 3 | "--compilertool:${workspaceFolder}/packages/build/FSharp.DependencyManager.Paket/lib/netstandard2.0/" 4 | ] 5 | } -------------------------------------------------------------------------------- /Examples/Examples.macOS.WebKit/Assets.xcassets/AppIcon.appiconset/AppIcon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/Interstellar/HEAD/Examples/Examples.macOS.WebKit/Assets.xcassets/AppIcon.appiconset/AppIcon-128.png -------------------------------------------------------------------------------- /Examples/Examples.macOS.WebKit/Assets.xcassets/AppIcon.appiconset/AppIcon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/Interstellar/HEAD/Examples/Examples.macOS.WebKit/Assets.xcassets/AppIcon.appiconset/AppIcon-16.png -------------------------------------------------------------------------------- /Examples/Examples.macOS.WebKit/Assets.xcassets/AppIcon.appiconset/AppIcon-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/Interstellar/HEAD/Examples/Examples.macOS.WebKit/Assets.xcassets/AppIcon.appiconset/AppIcon-256.png -------------------------------------------------------------------------------- /Examples/Examples.macOS.WebKit/Assets.xcassets/AppIcon.appiconset/AppIcon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/Interstellar/HEAD/Examples/Examples.macOS.WebKit/Assets.xcassets/AppIcon.appiconset/AppIcon-32.png -------------------------------------------------------------------------------- /Examples/Examples.macOS.WebKit/Assets.xcassets/AppIcon.appiconset/AppIcon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/Interstellar/HEAD/Examples/Examples.macOS.WebKit/Assets.xcassets/AppIcon.appiconset/AppIcon-512.png -------------------------------------------------------------------------------- /Examples/Examples.macOS.WebKit/Assets.xcassets/AppIcon.appiconset/AppIcon-128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/Interstellar/HEAD/Examples/Examples.macOS.WebKit/Assets.xcassets/AppIcon.appiconset/AppIcon-128@2x.png -------------------------------------------------------------------------------- /Examples/Examples.macOS.WebKit/Assets.xcassets/AppIcon.appiconset/AppIcon-16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/Interstellar/HEAD/Examples/Examples.macOS.WebKit/Assets.xcassets/AppIcon.appiconset/AppIcon-16@2x.png -------------------------------------------------------------------------------- /Examples/Examples.macOS.WebKit/Assets.xcassets/AppIcon.appiconset/AppIcon-256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/Interstellar/HEAD/Examples/Examples.macOS.WebKit/Assets.xcassets/AppIcon.appiconset/AppIcon-256@2x.png -------------------------------------------------------------------------------- /Examples/Examples.macOS.WebKit/Assets.xcassets/AppIcon.appiconset/AppIcon-32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/Interstellar/HEAD/Examples/Examples.macOS.WebKit/Assets.xcassets/AppIcon.appiconset/AppIcon-32@2x.png -------------------------------------------------------------------------------- /Examples/Examples.macOS.WebKit/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/Interstellar/HEAD/Examples/Examples.macOS.WebKit/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png -------------------------------------------------------------------------------- /templates/minimal/src/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "6.0.100", 4 | "rollForward": "latestFeature" 5 | }, 6 | "msbuild-sdks": { 7 | "MSBuild.Sdk.Extras": "3.0.22" 8 | } 9 | } -------------------------------------------------------------------------------- /templates/minimal/src/InterstellarApp.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/Interstellar/HEAD/templates/minimal/src/InterstellarApp.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-128.png -------------------------------------------------------------------------------- /templates/minimal/src/InterstellarApp.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/Interstellar/HEAD/templates/minimal/src/InterstellarApp.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-16.png -------------------------------------------------------------------------------- /templates/minimal/src/InterstellarApp.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/Interstellar/HEAD/templates/minimal/src/InterstellarApp.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-256.png -------------------------------------------------------------------------------- /templates/minimal/src/InterstellarApp.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/Interstellar/HEAD/templates/minimal/src/InterstellarApp.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-32.png -------------------------------------------------------------------------------- /templates/minimal/src/InterstellarApp.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/Interstellar/HEAD/templates/minimal/src/InterstellarApp.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-512.png -------------------------------------------------------------------------------- /templates/minimal/src/InterstellarApp.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/Interstellar/HEAD/templates/minimal/src/InterstellarApp.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-128@2x.png -------------------------------------------------------------------------------- /templates/minimal/src/InterstellarApp.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/Interstellar/HEAD/templates/minimal/src/InterstellarApp.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-16@2x.png -------------------------------------------------------------------------------- /templates/minimal/src/InterstellarApp.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/Interstellar/HEAD/templates/minimal/src/InterstellarApp.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-256@2x.png -------------------------------------------------------------------------------- /templates/minimal/src/InterstellarApp.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/Interstellar/HEAD/templates/minimal/src/InterstellarApp.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-32@2x.png -------------------------------------------------------------------------------- /templates/minimal/src/InterstellarApp.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/Interstellar/HEAD/templates/minimal/src/InterstellarApp.macOS/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png -------------------------------------------------------------------------------- /Examples/Examples.macOS.WebKit/Entitlements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /templates/minimal/src/InterstellarApp.macOS/Entitlements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Examples/Examples.macOS.WebKit/Main.fs: -------------------------------------------------------------------------------- 1 | namespace Example.macOS.WebKit 2 | 3 | open AppKit 4 | open System 5 | 6 | module main = 7 | [] 8 | let main args = 9 | NSApplication.Init() 10 | NSApplication.SharedApplication.Delegate <- new AppDelegate() 11 | NSApplication.Main(args) 12 | 0 13 | -------------------------------------------------------------------------------- /templates/minimal/src/InterstellarApp.macOS/Main.fs: -------------------------------------------------------------------------------- 1 | namespace InterstellarApp.macOS 2 | 3 | open AppKit 4 | open System 5 | 6 | module main = 7 | [] 8 | let main args = 9 | NSApplication.Init() 10 | NSApplication.SharedApplication.Delegate <- new AppDelegate() 11 | NSApplication.Main(args) 12 | 0 13 | -------------------------------------------------------------------------------- /paket.dependencies: -------------------------------------------------------------------------------- 1 | source https://api.nuget.org/v3/index.json 2 | framework: auto-detect 3 | 4 | nuget BlackFox.MasterOfFoo 5 | nuget CefSharp.WinForms 86.0.241 6 | nuget CefSharp.Wpf 86.0.241 7 | nuget FSharp.Core >= 4.2.3 8 | nuget NuGet.Build.Tasks.Pack 9 | nuget System.Text.Encodings.Web 10 | 11 | group Build 12 | 13 | source https://api.nuget.org/v3/index.json 14 | 15 | nuget FSharp.DependencyManager.Paket -------------------------------------------------------------------------------- /templates/minimal/src/InterstellarApp.Core/InterstellarApp.Core.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "fake-cli": { 6 | "version": "5.22.0", 7 | "commands": [ 8 | "fake" 9 | ] 10 | }, 11 | "paket": { 12 | "version": "7.1.5", 13 | "commands": [ 14 | "paket" 15 | ] 16 | }, 17 | "fsharp.formatting.commandtool": { 18 | "version": "11.4.3", 19 | "commands": [ 20 | "fsdocs" 21 | ] 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /Examples/Examples.SharedCode/Examples.SharedCode.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Interstellar.Core/Interstellar.Core.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Interstellar.Chromium/Interstellar.Chromium.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0-windows;net472 5 | true 6 | 7 | AnyCPU 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /templates/minimal/src/InterstellarApp.Windows/InterstellarApp.Windows.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0-Windows 5 | WinExe 6 | true 7 | false 8 | 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/Interstellar.Core/IBrowserExtensions.fs: -------------------------------------------------------------------------------- 1 | namespace Interstellar 2 | open System 3 | open System.Runtime.CompilerServices 4 | 5 | [] 6 | type IBrowserExtensions = 7 | [] 8 | static member Load (this: IBrowser, uri: string) = this.Load (new Uri(uri)) 9 | [] 10 | static member LoadString (this: IBrowser, html: string, ?uri: string) = 11 | this.LoadString (html, ?uri = Option.map Uri uri) 12 | 13 | [] 14 | module FSharpIBrowserExtensions = 15 | open FSharp.Core.Printf 16 | 17 | type IBrowser with 18 | /// printf-style method that executes some Javascript code on a browser instance, sanitizing format parameters using . It is safe to pass in untrusted format parameters from the outside world. Think SQL prepared statements. 19 | member this.ExecuteJavascriptf (format: StringFormat<'a, unit>) = 20 | executeJavascriptf this format -------------------------------------------------------------------------------- /templates/Interstellar.Template.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Interstellar.Template 5 | Interstellar Template Pack 6 | 7 | 0.0.0 8 | John Wostenberg 9 | Templates for building cross-platform browser-based applications using Interstellar. 10 | https://fsprojects.github.io/Interstellar/ 11 | MIT 12 | Copyright 2020 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Examples/Examples.Wpf.Chromium/Examples.Wpf.Chromium.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WinExe 5 | net6.0-windows;net48 6 | true 7 | AnyCPU 8 | true 9 | false 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /AssemblyAndPackageInfo.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | © John Wostenberg 2019-2022 4 | Interstellar Contributors 5 | Cross-platform F# browser library 6 | John Wostenberg 7 | git 8 | https://github.com/fsprojects/Interstellar 9 | https://fsprojects.github.io/Interstellar/ 10 | fsharp;interstellar 11 | LICENSE 12 | true 13 | $(RepositoryUrl)/blob/master/CHANGELOG.md 14 | $(RepositoryUrl)/blob/master/LICENSE 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /templates/minimal/src/InterstellarApp.Windows/Program.fs: -------------------------------------------------------------------------------- 1 | namespace InterstellarApp.Windows 2 | open System 3 | open System.Diagnostics 4 | open System.Threading 5 | open System.Windows 6 | open Interstellar 7 | open Interstellar.Core 8 | open Interstellar.Chromium.Wpf 9 | 10 | type App() = 11 | inherit Application(ShutdownMode = ShutdownMode.OnExplicitShutdown) 12 | 13 | override this.OnStartup (e: StartupEventArgs) = 14 | base.OnStartup e 15 | let onMainWindowCreated (w: IBrowserWindow) = 16 | let nativeWindow = w.NativeWindow 17 | // This is where you could call some WPF-specific APIs on this window 18 | () 19 | BrowserApp.run (InterstellarApp.BrowserApp.app onMainWindowCreated) 20 | 21 | module Main = 22 | [] 23 | let main argv = 24 | Thread.CurrentThread.Name <- "Main" 25 | Interstellar.Chromium.Platform.Initialize () 26 | let app = App() 27 | let result = app.Run () 28 | Debug.WriteLine "main() exiting" 29 | result -------------------------------------------------------------------------------- /Examples/Examples.WinForms.Chromium/Examples.WinForms.Chromium.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0-windows;net48 5 | WinExe 6 | true 7 | AnyCPU 8 | true 9 | false 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Examples/Examples.macOS.WebKit/AppDelegate.fs: -------------------------------------------------------------------------------- 1 | namespace Example.macOS.WebKit 2 | open System 3 | open System.Threading 4 | open AppKit 5 | open Foundation 6 | open Interstellar 7 | open Interstellar.MacOS.WebKit 8 | open Examples.SharedCode 9 | 10 | [] 11 | type AppDelegate() = 12 | inherit NSApplicationDelegate() 13 | 14 | override this.ApplicationShouldTerminateAfterLastWindowClosed sender = false 15 | 16 | override this.DidFinishLaunching notification = 17 | printfn "DidFinishLaunching" 18 | Thread.CurrentThread.Name <- "Main" 19 | 20 | let mainCtx = SynchronizationContext.Current 21 | Async.Start <| async { 22 | let onMainWindowCreated (w: IBrowserWindow) = 23 | let nsWindow = w.NativeWindow 24 | // This is where you could call some Cocoa-specific APIs on this window 25 | () 26 | do! BrowserApp.runAsync mainCtx (SimpleBrowserApp.app ignore) 27 | NSApplication.SharedApplication.Terminate null 28 | } 29 | 30 | () -------------------------------------------------------------------------------- /Examples/Examples.macOS.WebKit/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleName 6 | InterstellarExample 7 | CFBundleIdentifier 8 | com.wostenberg.InterstellarExample 9 | CFBundleShortVersionString 10 | 1.0 11 | CFBundleVersion 12 | 1 13 | LSMinimumSystemVersion 14 | 10.14 15 | CFBundleDevelopmentRegion 16 | en 17 | CFBundleInfoDictionaryVersion 18 | 6.0 19 | CFBundlePackageType 20 | APPL 21 | CFBundleSignature 22 | ???? 23 | NSHumanReadableCopyright 24 | 25 | NSPrincipalClass 26 | NSApplication 27 | XSAppIconAssets 28 | Assets.xcassets/AppIcon.appiconset 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/Interstellar.MacOS.WebKit/BrowserApp.fs: -------------------------------------------------------------------------------- 1 | namespace Interstellar.MacOS.WebKit 2 | open System 3 | open System.Threading 4 | open AppKit 5 | open Interstellar 6 | open Interstellar.MacOS.WebKit.Internal 7 | open Foundation 8 | 9 | module BrowserApp = 10 | /// Starts and runs a BrowserApp's lifecycle in a Cocoa + WebKit host, asychronously 11 | /// indicates the thread that is to be used as the UI thread 12 | /// Describes the application lifecycle 13 | let runAsync mainCtx (app: BrowserApp) = async { 14 | do! Async.SwitchToContext mainCtx 15 | let windowCreator : BrowserWindowCreator = fun config -> 16 | let w = new BrowserWindow(config) 17 | BrowserWindowConfig.applyWindowTitle mainCtx w (w :> IBrowserWindow<_>).Closed config.title 18 | upcast w 19 | do! app.onStart mainCtx windowCreator 20 | do! Async.SwitchToContext mainCtx 21 | } 22 | 23 | let run app = Async.Start <| runAsync SynchronizationContext.Current app -------------------------------------------------------------------------------- /templates/minimal/src/InterstellarApp.macOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleName 6 | InterstellarApp 7 | CFBundleIdentifier 8 | com.companyname.InterstellarApp 9 | CFBundleShortVersionString 10 | 1.0 11 | CFBundleVersion 12 | 1 13 | LSMinimumSystemVersion 14 | 10.14 15 | CFBundleDevelopmentRegion 16 | en 17 | CFBundleInfoDictionaryVersion 18 | 6.0 19 | CFBundlePackageType 20 | APPL 21 | CFBundleSignature 22 | ???? 23 | NSHumanReadableCopyright 24 | 25 | NSPrincipalClass 26 | NSApplication 27 | XSAppIconAssets 28 | Assets.xcassets/AppIcon.appiconset 29 | 30 | 31 | -------------------------------------------------------------------------------- /templates/minimal/src/InterstellarApp.macOS/AppDelegate.fs: -------------------------------------------------------------------------------- 1 | namespace InterstellarApp.macOS 2 | open System 3 | open System.Threading 4 | open AppKit 5 | open Foundation 6 | open Interstellar 7 | open Interstellar.MacOS.WebKit 8 | open InterstellarApp 9 | 10 | [] 11 | type AppDelegate() = 12 | inherit NSApplicationDelegate() 13 | 14 | override this.ApplicationShouldTerminateAfterLastWindowClosed sender = false 15 | 16 | override this.DidFinishLaunching notification = 17 | printfn "DidFinishLaunching" 18 | Thread.CurrentThread.Name <- "Main" 19 | 20 | let mainCtx = SynchronizationContext.Current 21 | Async.Start <| async { 22 | let onMainWindowCreated (w: IBrowserWindow) = 23 | let nsWindow = w.NativeWindow 24 | // This is where you could call some Cocoa-specific APIs on this window 25 | () 26 | do! BrowserApp.runAsync mainCtx (InterstellarApp.BrowserApp.app ignore) 27 | NSApplication.SharedApplication.Terminate null 28 | } 29 | 30 | () -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 John Wostenberg 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/test-templates.yml: -------------------------------------------------------------------------------- 1 | name: Test Templates 2 | 3 | on: workflow_dispatch 4 | 5 | jobs: 6 | build-windows: 7 | name: Test Templates (Windows) 8 | runs-on: windows-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: actions/setup-dotnet@v1 13 | with: 14 | dotnet-version: '6.0.300' 15 | - name: Restore 16 | run: | 17 | dotnet tool restore 18 | dotnet paket restore 19 | - name: Test templates 20 | run: | 21 | dotnet fake build -t BuildTemplateProjects -- Release 22 | 23 | build-macos: 24 | name: Test Templates (macOS) 25 | runs-on: macos-latest 26 | 27 | steps: 28 | - uses: actions/checkout@v2 29 | - uses: actions/setup-dotnet@v1 30 | with: 31 | dotnet-version: '6.0.300' 32 | - name: Install workloads 33 | run: | 34 | dotnet tool restore 35 | sudo dotnet workload install macos 36 | - name: Restore 37 | run: | 38 | dotnet tool restore 39 | dotnet paket restore 40 | - name: Test templates 41 | run: | 42 | dotnet fake build -t BuildTemplateProjects -- Release 43 | -------------------------------------------------------------------------------- /Examples/Examples.WinForms.Chromium/App.fs: -------------------------------------------------------------------------------- 1 | namespace Example.Windows.Chromium.Wpf 2 | open System 3 | open System.Diagnostics 4 | open System.Reflection 5 | open System.Threading 6 | open System.Windows 7 | open System.Windows.Forms 8 | open Interstellar 9 | open Examples.SharedCode 10 | open Interstellar.Chromium.WinForms 11 | open System.Runtime.Versioning 12 | 13 | module Main = 14 | let runApp () = 15 | Application.EnableVisualStyles () 16 | Application.SetCompatibleTextRenderingDefault true 17 | let onMainWindowCreated (w: IBrowserWindow) = 18 | let nativeWindow = w.NativeWindow 19 | // This is where you could call some WinForms-specific APIs on this window 20 | () 21 | BrowserApp.run (SimpleBrowserApp.app onMainWindowCreated) 22 | 23 | [] 24 | let main argv = 25 | Thread.CurrentThread.Name <- "Main" 26 | Trace.WriteLine (sprintf "Starting app. Main thread id: %A" Thread.CurrentThread.ManagedThreadId) 27 | Interstellar.Chromium.Platform.Initialize () 28 | runApp () 29 | Interstellar.Chromium.Platform.Shutdown () 30 | 0 -------------------------------------------------------------------------------- /Examples/Examples.Wpf.Chromium/App.fs: -------------------------------------------------------------------------------- 1 | namespace Example.Windows.Chromium.Wpf 2 | open System 3 | open System.Diagnostics 4 | open System.Reflection 5 | open System.Runtime.Versioning 6 | open System.Threading 7 | open System.Windows 8 | open System.Windows.Controls 9 | open Examples.SharedCode 10 | open Interstellar 11 | open Interstellar.Chromium.Wpf 12 | 13 | type App() = 14 | inherit Application(ShutdownMode = ShutdownMode.OnExplicitShutdown) 15 | 16 | override this.OnStartup (e: StartupEventArgs) = 17 | base.OnStartup e 18 | let onMainWindowCreated (w: IBrowserWindow) = 19 | let nativeWindow = w.NativeWindow 20 | // This is where you could call some WPF-specific APIs on this window 21 | () 22 | BrowserApp.run (SimpleBrowserApp.app onMainWindowCreated) 23 | Trace.WriteLine "returning from OnStartup" 24 | 25 | module Main = 26 | [] 27 | let main argv = 28 | Thread.CurrentThread.Name <- "Main" 29 | Interstellar.Chromium.Platform.Initialize () 30 | let app = new App() 31 | let result = app.Run () 32 | Debug.WriteLine "main() exiting" 33 | result -------------------------------------------------------------------------------- /src/Interstellar.Wpf.Chromium/Interstellar.Wpf.Chromium.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0-windows;net48 5 | Library 6 | true 7 | true 8 | WPF 9 | netcore 10 | 11 | AnyCPU 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/Interstellar.WinForms.Chromium/Interstellar.WinForms.Chromium.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0-windows;net48 5 | Library 6 | true 7 | true 8 | WINFORMS 9 | netcore 10 | 11 | AnyCPU 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/Interstellar.Wpf.Chromium/BrowserApp.fs: -------------------------------------------------------------------------------- 1 | namespace Interstellar.Chromium.Wpf 2 | open System 3 | open System.Windows 4 | open Interstellar 5 | open System.Threading 6 | 7 | module BrowserApp = 8 | /// Starts and runs a BrowserApp's lifecycle in a WPF + Chromium host, asychronously 9 | /// Indicates the thread that is to be used as the UI thread 10 | /// Describes the application lifecycle 11 | let runAsync mainCtx (app: BrowserApp) = async { 12 | let windowCreator : BrowserWindowCreator = fun config -> 13 | let w = new BrowserWindow(config) 14 | BrowserWindowConfig.applyWindowTitle mainCtx w w.Unloaded config.title 15 | upcast w 16 | do! Async.SwitchToContext mainCtx 17 | do! app.onStart mainCtx windowCreator 18 | do! Async.SwitchToContext mainCtx 19 | Application.Current.Shutdown () 20 | } 21 | 22 | /// Starts and runs a BrowserApp's lifecycle in a WPF + Chromium host, using the current thread as the UI thread 23 | /// Describes the application lifecycle 24 | let run app = Async.Start <| runAsync SynchronizationContext.Current app -------------------------------------------------------------------------------- /Examples/Examples.macOS.WebKit/Examples.macOS.WebKit.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0-macos 5 | Exe 6 | Example.macOS.WebKit 7 | InterstellarExample 8 | 10.14 9 | 10 | 11 | false 12 | false 13 | 14 | 15 | false 16 | false 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Examples/Examples.macOS.WebKit/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "AppIcon-16.png", 5 | "size": "16x16", 6 | "scale": "1x", 7 | "idiom": "mac" 8 | }, 9 | { 10 | "filename": "AppIcon-16@2x.png", 11 | "size": "16x16", 12 | "scale": "2x", 13 | "idiom": "mac" 14 | }, 15 | { 16 | "filename": "AppIcon-32.png", 17 | "size": "32x32", 18 | "scale": "1x", 19 | "idiom": "mac" 20 | }, 21 | { 22 | "filename": "AppIcon-32@2x.png", 23 | "size": "32x32", 24 | "scale": "2x", 25 | "idiom": "mac" 26 | }, 27 | { 28 | "filename": "AppIcon-128.png", 29 | "size": "128x128", 30 | "scale": "1x", 31 | "idiom": "mac" 32 | }, 33 | { 34 | "filename": "AppIcon-128@2x.png", 35 | "size": "128x128", 36 | "scale": "2x", 37 | "idiom": "mac" 38 | }, 39 | { 40 | "filename": "AppIcon-256.png", 41 | "size": "256x256", 42 | "scale": "1x", 43 | "idiom": "mac" 44 | }, 45 | { 46 | "filename": "AppIcon-256@2x.png", 47 | "size": "256x256", 48 | "scale": "2x", 49 | "idiom": "mac" 50 | }, 51 | { 52 | "filename": "AppIcon-512.png", 53 | "size": "512x512", 54 | "scale": "1x", 55 | "idiom": "mac" 56 | }, 57 | { 58 | "filename": "AppIcon-512@2x.png", 59 | "size": "512x512", 60 | "scale": "2x", 61 | "idiom": "mac" 62 | } 63 | ], 64 | "info": { 65 | "version": 1, 66 | "author": "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /templates/minimal/src/InterstellarApp.macOS/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "AppIcon-16.png", 5 | "size": "16x16", 6 | "scale": "1x", 7 | "idiom": "mac" 8 | }, 9 | { 10 | "filename": "AppIcon-16@2x.png", 11 | "size": "16x16", 12 | "scale": "2x", 13 | "idiom": "mac" 14 | }, 15 | { 16 | "filename": "AppIcon-32.png", 17 | "size": "32x32", 18 | "scale": "1x", 19 | "idiom": "mac" 20 | }, 21 | { 22 | "filename": "AppIcon-32@2x.png", 23 | "size": "32x32", 24 | "scale": "2x", 25 | "idiom": "mac" 26 | }, 27 | { 28 | "filename": "AppIcon-128.png", 29 | "size": "128x128", 30 | "scale": "1x", 31 | "idiom": "mac" 32 | }, 33 | { 34 | "filename": "AppIcon-128@2x.png", 35 | "size": "128x128", 36 | "scale": "2x", 37 | "idiom": "mac" 38 | }, 39 | { 40 | "filename": "AppIcon-256.png", 41 | "size": "256x256", 42 | "scale": "1x", 43 | "idiom": "mac" 44 | }, 45 | { 46 | "filename": "AppIcon-256@2x.png", 47 | "size": "256x256", 48 | "scale": "2x", 49 | "idiom": "mac" 50 | }, 51 | { 52 | "filename": "AppIcon-512.png", 53 | "size": "512x512", 54 | "scale": "1x", 55 | "idiom": "mac" 56 | }, 57 | { 58 | "filename": "AppIcon-512@2x.png", 59 | "size": "512x512", 60 | "scale": "2x", 61 | "idiom": "mac" 62 | } 63 | ], 64 | "info": { 65 | "version": 1, 66 | "author": "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build-windows: 7 | name: CI (Windows) 8 | runs-on: windows-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: actions/setup-dotnet@v1 13 | with: 14 | dotnet-version: '6.0.400' 15 | - name: Restore 16 | run: | 17 | dotnet tool restore 18 | dotnet paket restore 19 | - name: Build 20 | run: dotnet fake build 21 | - name: Pack 22 | run: | 23 | dotnet fake build -t PackAll 24 | - name: Upload artifacts 25 | uses: actions/upload-artifact@v2 26 | with: 27 | name: interstellar-windows 28 | path: artifacts 29 | - name: Test 30 | run: | 31 | dotnet fake build -t Test -- Release 32 | 33 | build-macos: 34 | name: CI (macOS) 35 | runs-on: macos-latest 36 | 37 | steps: 38 | - uses: actions/checkout@v2 39 | - uses: actions/setup-dotnet@v1 40 | with: 41 | dotnet-version: '6.0.400' 42 | - name: Install workloads 43 | run: | 44 | dotnet tool restore 45 | sudo dotnet workload install macos 46 | - name: Restore 47 | run: | 48 | dotnet tool restore 49 | dotnet paket restore 50 | - name: Build 51 | run: dotnet fake build -- Release 52 | - name: Pack 53 | run: dotnet fake build -t PackAll -- Release 54 | - name: Upload artifacts 55 | uses: actions/upload-artifact@v2 56 | with: 57 | name: interstellar-macos 58 | path: artifacts/ 59 | - name: Test 60 | run: | 61 | dotnet fake build -t Test -- Release 62 | -------------------------------------------------------------------------------- /src/Interstellar.MacOS.WebKit/Interstellar.MacOS.WebKit.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0-macos 5 | Library 6 | Interstellar.macOS.WebKit 7 | 8 | 9 | false 10 | false 11 | 12 | 13 | false 14 | false 15 | 16 | 17 | 18 | 19 | <_Parameter1>$(Version) 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/Interstellar.WinForms.Chromium/BrowserApp.fs: -------------------------------------------------------------------------------- 1 | namespace Interstellar.Chromium.WinForms 2 | open System 3 | open System.Windows.Forms 4 | open Interstellar 5 | open System.Threading 6 | open System.Diagnostics 7 | 8 | module BrowserApp = 9 | /// Starts and runs a BrowserApp's lifecycle in a Windows Forms + Chromium host, asychronously 10 | /// Indicates the thread that is to be used as the UI thread 11 | /// Describes the application lifecycle 12 | let runAsync mainCtx (app: BrowserApp) = async { 13 | let windowCreator : BrowserWindowCreator = fun config -> 14 | let w = new BrowserWindow(config) 15 | BrowserWindowConfig.applyWindowTitle mainCtx w w.Disposed config.title 16 | upcast w 17 | do! Async.SwitchToContext mainCtx 18 | do! app.onStart mainCtx windowCreator 19 | do! Async.SwitchToContext mainCtx 20 | Interstellar.Chromium.Platform.Shutdown () 21 | Application.Exit () 22 | } 23 | 24 | /// Starts and runs a BrowserApp's lifecycle in a Windows Forms + Chromium host, using the current thread as the UI thread 25 | /// Describes the application lifecycle 26 | let run app = 27 | // call a Control ctor in order to initialize SynchronizationContext.Current. Constructing a Form, or calling Application.Run also does this. 28 | use dummyControl = new Control() in () 29 | Debug.WriteLine (sprintf "DummyControl thread: %A" Thread.CurrentThread.ManagedThreadId) 30 | Async.Start <| runAsync SynchronizationContext.Current app 31 | Application.Run () -------------------------------------------------------------------------------- /templates/minimal/src/.template.config/template.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "John Wostenberg", 3 | "classifications": [ 4 | "Interstellar", 5 | "Cross-platform" 6 | ], 7 | "name": "Simple Interstellar App", 8 | "tags": { 9 | "language": "F#", 10 | "type": "project" 11 | }, 12 | "identity": "Interstellar.Template", 13 | "groupIdentity": "Interstellar", 14 | "shortName": "interstellar", 15 | "sourceName": "InterstellarApp", 16 | "preferNameDirectory": true, 17 | "exclude": [ 18 | "**/[Bb]in/**", "**/[Oo]bj/**", 19 | ".template.config/**/*", "**/*.filelist", 20 | "**/*.user", "**/*.lock.json", 21 | "**/.vs/**", "**/.ionide/**", 22 | "**/GPUCache/**" 23 | ], 24 | "symbols": { 25 | "Windows": { 26 | "type": "parameter", 27 | "dataType": "bool", 28 | "defaultValue": "true", 29 | "description": "Generates a Windows host project" 30 | }, 31 | "macOS": { 32 | "type": "parameter", 33 | "dataType": "bool", 34 | "defaultValue": "true", 35 | "description": "Generates a macOS host project" 36 | } 37 | }, 38 | "sources": [ 39 | { 40 | "modifiers": [ 41 | { 42 | "condition": "(!Windows)", 43 | "exclude": ["InterstellarApp.Windows/**/*", "InterstellarApp.Windows.*"] 44 | }, 45 | { 46 | "condition": "(!macOS)", 47 | "exclude": ["InterstellarApp.macOS/**/*", "InterstellarApp.macOS.*"] 48 | } 49 | ] 50 | } 51 | ] 52 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.4.0 4 | 5 | * Fully support .NET 6 on Windows and macOS 6 | 7 | ## 0.3.3 8 | 9 | * Fix FSharp.Core dependency version mistake in last release 10 | 11 | ## 0.3.2 12 | 13 | * WinForms, WPF - Fix a bug that happens under Windows high-DPI mode 14 | * Change targets to netcoreapp3.1 15 | 16 | ## 0.3.1 17 | 18 | * WinForms - Fixed an issue that could cause apps to crash at startup in release mode (https://github.com/jwosty/Interstellar/issues/10 ; thanks to @amaitland for the fix) 19 | 20 | ## 0.3.0 21 | 22 | * Add IBrowser.CanShowDevTools 23 | * Interstellar.MacOS.WebKit 24 | * Remove unnecessary assembly references 25 | 26 | ## 0.2.0 27 | 28 | * Add a generic parameter to IBrowserWindow that carries the type of the underlying window implementation, allowing for usages of native APIs within an Interstellar application without requiring dynamic casting 29 | * This also affects all types and functions that depend on IBrowserWindow -- so just about everything except IBrowser 30 | * Add executeJavascriptf, javascriptf, kjavascriptf, and IBrowser.ExecuteJavascriptf, which make it easy to safely format untrusted inputs into Javascript scripts for execution (a form of code injection) 31 | 32 | ## 0.1.0 33 | 34 | * Fix BrowserApp.create not waiting for the window to close 35 | * Make referencing events from non-UI threads always be a safe operation 36 | * Add IBrowserWindow.IsShowing 37 | * Add IBrowser.LoadAsync and IBrowser.LoadStringAsync 38 | * Add BrowserWindowConfig.title record field and a WindowTitle type to accompany it, and specify the default title to track the page title 39 | * (WinForms) - Fix the implementation of BrowserWindow.Dispose 40 | 41 | ## 0.0.1 42 | 43 | * Initial release 44 | -------------------------------------------------------------------------------- /templates/minimal/src/InterstellarApp.macOS.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.808.3 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "InterstellarApp.Core", "InterstellarApp.Core\InterstellarApp.Core.fsproj", "{3A2B7A09-0068-4013-BE15-D1D967858FBE}" 7 | EndProject 8 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "InterstellarApp.macOS", "InterstellarApp.macOS\InterstellarApp.macOS.fsproj", "{19D61708-80B4-4FE8-8E53-937BDC4BC011}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {3A2B7A09-0068-4013-BE15-D1D967858FBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {3A2B7A09-0068-4013-BE15-D1D967858FBE}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {3A2B7A09-0068-4013-BE15-D1D967858FBE}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {3A2B7A09-0068-4013-BE15-D1D967858FBE}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {19D61708-80B4-4FE8-8E53-937BDC4BC011}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {19D61708-80B4-4FE8-8E53-937BDC4BC011}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {19D61708-80B4-4FE8-8E53-937BDC4BC011}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {19D61708-80B4-4FE8-8E53-937BDC4BC011}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {08DF859A-39F3-4842-9A99-327FA9195D27} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /templates/minimal/src/InterstellarApp.Windows.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.808.3 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "InterstellarApp.Core", "InterstellarApp.Core\InterstellarApp.Core.fsproj", "{3A2B7A09-0068-4013-BE15-D1D967858FBE}" 7 | EndProject 8 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "InterstellarApp.Windows", "InterstellarApp.Windows\InterstellarApp.Windows.fsproj", "{226E8212-8C44-451A-B585-902F4EE0C739}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {3A2B7A09-0068-4013-BE15-D1D967858FBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {3A2B7A09-0068-4013-BE15-D1D967858FBE}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {3A2B7A09-0068-4013-BE15-D1D967858FBE}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {3A2B7A09-0068-4013-BE15-D1D967858FBE}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {226E8212-8C44-451A-B585-902F4EE0C739}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {226E8212-8C44-451A-B585-902F4EE0C739}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {226E8212-8C44-451A-B585-902F4EE0C739}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {226E8212-8C44-451A-B585-902F4EE0C739}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {08DF859A-39F3-4842-9A99-327FA9195D27} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Interstellar 2 | 3 | High-quality contributions are very much welcome. Pull requests should do one thing only and do it well; if you have multiple separate contributions, please split them 4 | into individual, independent PRs, or they may not be accepted. A good PR might consist of one of these (but is not limited to these): 5 | 6 | * A bug fix 7 | * A feature implementation 8 | * Code cleanup (with reasonable scope) 9 | * Documentation improvement (again, with reasonable scope) 10 | 11 | ## Testing 12 | 13 | Interstellar doesn't have any unit tests because it's generally too costly and impractical to unit test GUI systems. If anyone thinks they have some good tests to add 14 | to prove me wrong, certainly submit a PR and I will reconsider! End-to-end tests may be a good idea; i.e. tests that just make sure that the app starts up and a window 15 | appears would probably be prudent. 16 | 17 | Therefore, you must manually test your changes by running the example apps included in the solution files. 18 | 19 | ## Releasing 20 | 21 | To create a new release version, add a new section to [CHANGELOG.md](CHANGELOG.md). The build system uses the first section in the file to determine the version number 22 | as well as the release notes. 23 | 24 | Packaging a release is as easy as running ``dotnet fake build -t PackAll`` (they will end up in the ``artifacts/`` directory). CI will always attempt to build the 25 | NuGet packages, but does not yet automatically publish to NuGet, so to publish a new release, you must download the built packages from the GitHub CI and then manually 26 | publish (see https://github.com/jwosty/Interstellar/issues/16). 27 | 28 | After releasing the packages to NuGet, update the templates (found under templates/) to use the new package versions, and then release those. 29 | -------------------------------------------------------------------------------- /src/Interstellar.WindowsCommon.Chromium/Platform.fs: -------------------------------------------------------------------------------- 1 | namespace Interstellar.Chromium 2 | open System 3 | open System.IO 4 | open System.Reflection 5 | open CefSharp 6 | #if WPF 7 | open CefSharp.Wpf 8 | #endif 9 | #if WINFORMS 10 | open CefSharp.WinForms 11 | #endif 12 | 13 | type Platform private() = 14 | static let mutable isInitialized = false 15 | static let initLock = new Object() 16 | 17 | static member Initialize () = 18 | lock initLock (fun () -> 19 | if not isInitialized then 20 | AppDomain.CurrentDomain.add_AssemblyResolve (ResolveEventHandler(Platform.ResolveCefSharpAssembly)) 21 | Platform.InitAnyCpuCefSharp () |> ignore 22 | ) 23 | 24 | // Without this inlining, WinForms builds crash in Release mode: https://github.com/fsprojects/Interstellar/issues/10 25 | [] 26 | static member Shutdown () = 27 | Cef.Shutdown () 28 | 29 | static member private GetPlatformAssemblyPath assemblyName = 30 | Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, (if Environment.Is64BitProcess then "x64" else "x86"), assemblyName) 31 | 32 | static member private InitAnyCpuCefSharp () = 33 | // Required to fix high DPI issue: https://github.com/fsprojects/Interstellar/issues/25 34 | Cef.EnableHighDPISupport() 35 | 36 | let browserSubpath = Platform.GetPlatformAssemblyPath("CefSharp.BrowserSubprocess.exe") 37 | let settings = new CefSettings(BrowserSubprocessPath = browserSubpath) 38 | //settings.RegisterExtension Browser.bridgeExtension 39 | Cef.Initialize (settings, false, (null : IBrowserProcessHandler)) 40 | 41 | static member private ResolveCefSharpAssembly sender (args: ResolveEventArgs) = 42 | if (args.Name.StartsWith("CefSharp")) then 43 | let assemblyName = args.Name.Split([|','|], 2).[0] + ".dll" 44 | let archSpecificPath = Platform.GetPlatformAssemblyPath assemblyName 45 | if File.Exists archSpecificPath then (Assembly.LoadFile archSpecificPath) else null 46 | else null -------------------------------------------------------------------------------- /nupkg-hack.fsx: -------------------------------------------------------------------------------- 1 | #r "System.IO.Compression" 2 | open System 3 | open System.IO 4 | open System.IO.Compression 5 | open System.Text 6 | open System.Text.RegularExpressions 7 | open Fake.Core 8 | open Fake.IO 9 | 10 | Environment.CurrentDirectory <- __SOURCE_DIRECTORY__ 11 | 12 | let changeVersionConstraints text = 13 | Regex("(?<=id=\"Interstellar.+?\"\\s+version=\")[^[\\]]*?(?=\")") 14 | .Replace (text, MatchEvaluator(fun m -> sprintf "[%s]" m.Value)) 15 | 16 | // """ 17 | // 18 | // 19 | // 20 | // 21 | // 22 | // 23 | // 24 | // 25 | // 26 | // 27 | // """ 28 | // |> changeVersionConstraints |> printfn "%s" 29 | 30 | let hackNupkgFromStream (_: string) (stream: Stream) = 31 | use archive = new ZipArchive(stream, ZipArchiveMode.Update) 32 | let oldEntry = archive.Entries |> Seq.find (fun e -> e.Name.EndsWith ".nuspec") 33 | let input = 34 | (use nuspecReader = new StreamReader(oldEntry.Open(), Encoding.UTF8) in nuspecReader.ReadToEnd()) 35 | let output = changeVersionConstraints input 36 | use nuspecWriter = new StreamWriter(oldEntry.Open(), Encoding.UTF8) 37 | nuspecWriter.Write output 38 | 39 | /// Cracks open a nupkg and changes all Interstellar package reference constraints from >= to = 40 | let hackNupkgAtPath (path: string) = 41 | Trace.log ("Hacking nupkg: " + path) 42 | use file = File.Open (path, FileMode.Open, FileAccess.ReadWrite, FileShare.None) 43 | hackNupkgFromStream path file 44 | 45 | //hackNupkgAtPath (Path.Combine ("artifacts", "Interstellar.Wpf.Chromium.nupkg")) -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Interstellar 2 | 3 | > NOTE: This API is not yet guarenteed to be stable or backward-compatible until v1.0, so breaking changes may occur at any time. 4 | 5 | Interstellar is an F# library providing a standard, mixed-paradigm API for accessing browser controls on various platforms. Currently, there are 3 combinations platform and browser hosts available. See [Examples](https://github.com/fsprojects/Interstellar/tree/master/Examples) for a simple sample application. See https://github.com/jwosty/InterstellarFableHelloWorld for an example of combining Interstellar with [Fable](https://fable.io/), achieving a cross-platform desktop app built completely in F#. 6 | 7 | ## Quick Start 8 | 9 | You will need the .Net 5 SDK (and the mono SDK on macOS). For the Windows projects, you should be able to use any of the standard IDEs (Visual Studio, Visual Studio Code, Rider \[untested but should work\]). For the macOS projects, you need to use Visual Studio for Mac. 10 | 11 | 12 | Create a project from the template: 13 | 14 | ```bash 15 | dotnet new -i Interstellar.Template 16 | dotnet new interstellar -n 17 | ``` 18 | 19 | On Windows, you can run it like so: 20 | 21 | ```bash 22 | dotnet restore .Windows.sln 23 | dotnet run -p .Windows\.Windows.fsproj 24 | ``` 25 | 26 | On macOS, I recommend opening ``.macOS.sln`` in Visual Studio for Mac and running it that way. It runs using the mono-based Xamarin.macOS runtime, and as a result you currently can't run it with ``dotnet run`` (see [xamarin/xamarin-macios#3955](https://github.com/xamarin/xamarin-macios/issues/8955)). This is on the roadmap for .NET 6. You'll have to use Mono's ``msbuild`` instead, if you want to use the CLI. 27 | 28 | You should end up with a simple, cross-platform sample app that opens a window built using embeded HTML, CSS, and Javascript. 29 | 30 |  31 | 32 | ### Customizing the template 33 | 34 | By default, this will create a core sample project, and a host project for each platform (Windows and macOS). To generate a project without a macOS host, use the following: 35 | 36 | ```bash 37 | dotnet new interstellar --macOS false 38 | ``` 39 | 40 | And to disable Windows: 41 | 42 | ```bash 43 | dotnet new interstellar --Windows false 44 | ``` 45 | 46 | To see more info about these options: 47 | 48 | ```bash 49 | dotnet new interstellar --help 50 | ``` 51 | -------------------------------------------------------------------------------- /src/Interstellar.Core/Script1.fsx: -------------------------------------------------------------------------------- 1 | #I "..\\.paket\\load\\netstandard2.0" 2 | #load "BlackFox.MasterOfFoo.fsx" 3 | #load "System.Text.Encodings.Web.fsx" 4 | #load "main.group.fsx" 5 | #load "Api.fs" 6 | 7 | open System.Text.Encodings.Web 8 | open FSharp.Core.Printf 9 | open BlackFox.MasterOfFoo 10 | open Interstellar 11 | 12 | type MockBrowser() = 13 | interface IBrowser with 14 | member this.Address = raise (System.NotImplementedException()) 15 | member this.AreDevToolsShowing = raise (System.NotImplementedException()) 16 | member this.CanGoBack = raise (System.NotImplementedException()) 17 | member this.CanGoForward = raise (System.NotImplementedException()) 18 | member this.CloseDevTools() = raise (System.NotImplementedException()) 19 | member this.Engine = raise (System.NotImplementedException()) 20 | member this.GoBack() = raise (System.NotImplementedException()) 21 | member this.GoForward() = raise (System.NotImplementedException()) 22 | [] 23 | member this.JavascriptMessageRecieved = Unchecked.defaultof> 24 | member this.Load(arg1) = raise (System.NotImplementedException()) 25 | member this.LoadAsync(arg1) = raise (System.NotImplementedException()) 26 | member this.LoadString(html, uri) = raise (System.NotImplementedException()) 27 | member this.LoadStringAsync(html, uri) = raise (System.NotImplementedException()) 28 | [] 29 | member this.PageLoaded = Unchecked.defaultof> 30 | member this.PageTitle = raise (System.NotImplementedException()) 31 | [] 32 | member this.PageTitleChanged = Unchecked.defaultof> 33 | member this.Reload() = raise (System.NotImplementedException()) 34 | member this.ShowDevTools() = raise (System.NotImplementedException()) 35 | member this.ExecuteJavascript script = printfn "%s" script 36 | 37 | let mockBrowser = new MockBrowser() :> IBrowser 38 | 39 | Interstellar.Printf.javascriptf "console.log(\"%s\")" """'hello \"world" """ |> printfn "%s" 40 | Interstellar.Printf.javascriptf "console.log('%s')" """'hello "world""" |> printfn "%s" 41 | Interstellar.Printf.javascriptf "console.log(\"%s\")" """ \"hello world" """ |> printfn "%s" 42 | do executeJavascriptf mockBrowser "%d%b" 42 true 43 | 44 | let executeJavascriptf' (browser: IBrowser) format = kjavascriptf (fun result -> browser.ExecuteJavascript result) format 45 | do executeJavascriptf' mockBrowser "console.log(\"%s\")" """ \"hello world"<%*>; """ -------------------------------------------------------------------------------- /templates/minimal/src/InterstellarApp.macOS/InterstellarApp.macOS.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0-macos 5 | Exe 6 | InterstellarApp.macOS 7 | InterstellarApp 8 | 10.14 9 | 10 | 11 | Mac Developer 12 | false 13 | false 14 | 15 | 16 | true 17 | true 18 | SdkOnly 19 | false 20 | false 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/Interstellar.WinForms.Chromium/BrowserWindow.fs: -------------------------------------------------------------------------------- 1 | namespace Interstellar.Chromium.WinForms 2 | open System 3 | open System.Drawing 4 | open System.Windows 5 | open System.Windows.Forms 6 | open CefSharp 7 | open CefSharp.WinForms 8 | open Interstellar 9 | open System.Threading 10 | open System.Diagnostics 11 | 12 | type BrowserWindow(config: BrowserWindowConfig) as this = 13 | inherit Form(Visible = false) 14 | 15 | let mutable disposed = false 16 | let mutable lastKnownPageTitle = "" 17 | let owningThreadId = Thread.CurrentThread.ManagedThreadId 18 | let cefBrowser = new ChromiumWebBrowser(null: string) 19 | let browser = 20 | new Interstellar.Chromium.Browser( 21 | cefBrowser, 22 | { getPageTitle = fun () -> lastKnownPageTitle 23 | titleChanged = cefBrowser.TitleChanged |> Event.map (fun x -> x.Title) 24 | isBrowserInitializedChanged = cefBrowser.IsBrowserInitializedChanged |> Event.map ignore}, 25 | config) 26 | 27 | let titleChangedHandle = cefBrowser.TitleChanged.Subscribe (fun e -> lastKnownPageTitle <- e.Title) 28 | 29 | // (primary) constructor 30 | do 31 | this.Controls.Add cefBrowser 32 | 33 | member this.ChromiumBrowser = browser 34 | 35 | interface IBrowserWindow with 36 | member this.Browser = upcast browser 37 | member this.Close () = (this :> Form).Close () 38 | [] 39 | member val Closed : IEvent = (this :> Form).FormClosed |> Event.map ignore 40 | member this.IsShowing = 41 | seq { for frm in Application.OpenForms -> frm } 42 | |> Seq.contains (this :> Form) 43 | member this.NativeWindow = this :> Form 44 | member this.Platform = BrowserWindowPlatform.WinForms 45 | member this.Show () = 46 | if (Thread.CurrentThread.ManagedThreadId <> owningThreadId) then 47 | raise (new InvalidOperationException("Show() called from a thread other than the thread on which the BrowserWindow was constructed.")) 48 | (this :> Form).Show () 49 | async { 50 | if cefBrowser.IsBrowserInitialized then () 51 | else 52 | let! _ = Async.AwaitEvent cefBrowser.IsBrowserInitializedChanged 53 | () 54 | } 55 | [] 56 | member val Shown = (this :> Form).Shown |> Event.map ignore 57 | member this.Size 58 | with get () = 59 | let size = (this :> Form).Size 60 | float size.Width, float size.Height 61 | and set (width, height) = 62 | (this :> Form).Size <- new Size(int width, int height) 63 | member this.Title 64 | with get () = this.Text 65 | and set title = this.Text <- title 66 | 67 | override this.Dispose disposing = 68 | if disposing then 69 | titleChangedHandle.Dispose () 70 | base.Dispose disposing -------------------------------------------------------------------------------- /src/Interstellar.Wpf.Chromium/BrowserWindow.fs: -------------------------------------------------------------------------------- 1 | namespace Interstellar.Chromium.Wpf 2 | open System 3 | open System.Threading 4 | open System.Diagnostics 5 | open System.Windows 6 | open System.Windows.Controls 7 | open CefSharp 8 | open CefSharp.Wpf 9 | open Interstellar 10 | open Interstellar.Chromium 11 | 12 | type BrowserWindow(config: BrowserWindowConfig) as this = 13 | inherit Window() 14 | 15 | let mainCtx = SynchronizationContext.Current 16 | let cefBrowser = new CefSharp.Wpf.ChromiumWebBrowser() 17 | let browser = 18 | new Interstellar.Chromium.Browser<_>( 19 | cefBrowser, 20 | { getPageTitle = fun () -> cefBrowser.Title 21 | titleChanged = cefBrowser.TitleChanged |> Event.map (fun x -> x.NewValue :?> string) 22 | isBrowserInitializedChanged = cefBrowser.IsBrowserInitializedChanged |> Event.map ignore}, 23 | config) 24 | let owningThreadId = Thread.CurrentThread.ManagedThreadId 25 | 26 | let mutable alreadyShown = false 27 | let shown = new Event() 28 | 29 | // (primary) constructor 30 | do 31 | this.Content <- cefBrowser 32 | 33 | interface IDisposable with 34 | member this.Dispose () = 35 | Async.StartImmediate <| async { 36 | do! Async.SwitchToContext mainCtx 37 | this.Close () 38 | } 39 | 40 | interface IBrowserWindow with 41 | member this.Browser = upcast browser 42 | member this.Close () = (this :> Window).Close () 43 | [] member val Closed = (this :> Window).Closed |> Event.map ignore 44 | member this.IsShowing = 45 | seq { for w in Application.Current.Windows -> w } 46 | |> Seq.contains (this :> Window) 47 | member this.NativeWindow = this :> Window 48 | member this.Platform = BrowserWindowPlatform.Wpf 49 | member this.Show () = 50 | if owningThreadId <> Thread.CurrentThread.ManagedThreadId then 51 | raise (new InvalidOperationException("Show() called from a thread other than the thread on which the BrowserWindow was constructed.")) 52 | (this :> Window).Show () 53 | async { 54 | if not cefBrowser.IsBrowserInitialized then 55 | let! _ = Async.AwaitEvent cefBrowser.IsBrowserInitializedChanged 56 | () 57 | } 58 | member this.Size 59 | with get () = base.Width, base.Height 60 | and set (width, height) = 61 | base.Width <- width 62 | base.Height <- height 63 | [] member val Shown = shown.Publish 64 | member this.Title 65 | with get () = (this :> Window).Title 66 | and set title = (this :> Window).Title <- title 67 | 68 | override this.OnContentRendered e = 69 | base.OnContentRendered e 70 | if not alreadyShown then 71 | alreadyShown <- true 72 | shown.Trigger () -------------------------------------------------------------------------------- /Interstellar.MacOS.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Interstellar.Core", "src\Interstellar.Core\Interstellar.Core.fsproj", "{7D096C2C-0960-4754-9954-421832426807}" 5 | EndProject 6 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Examples.SharedCode", "Examples\Examples.SharedCode\Examples.SharedCode.fsproj", "{0332BEB2-0915-47CC-A709-49FC4C2B3DB5}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BE6ABDEA-53B5-4D1C-9E46-6B783CD97234}" 9 | ProjectSection(SolutionItems) = preProject 10 | global.json = global.json 11 | AssemblyAndPackageInfo.props = AssemblyAndPackageInfo.props 12 | paket.dependencies = paket.dependencies 13 | EndProjectSection 14 | EndProject 15 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Examples.macOS.WebKit", "Examples\Examples.macOS.WebKit\Examples.macOS.WebKit.fsproj", "{C91E86B6-72B1-4367-9CD4-A1116FECD752}" 16 | EndProject 17 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Interstellar.MacOS.WebKit", "src\Interstellar.MacOS.WebKit\Interstellar.MacOS.WebKit.fsproj", "{77FAE56F-23ED-4B30-B961-BBC7B492AAFE}" 18 | EndProject 19 | Global 20 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 21 | Debug|Any CPU = Debug|Any CPU 22 | Release|Any CPU = Release|Any CPU 23 | EndGlobalSection 24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 25 | {7D096C2C-0960-4754-9954-421832426807}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {7D096C2C-0960-4754-9954-421832426807}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {7D096C2C-0960-4754-9954-421832426807}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {7D096C2C-0960-4754-9954-421832426807}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {0332BEB2-0915-47CC-A709-49FC4C2B3DB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {0332BEB2-0915-47CC-A709-49FC4C2B3DB5}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {0332BEB2-0915-47CC-A709-49FC4C2B3DB5}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {0332BEB2-0915-47CC-A709-49FC4C2B3DB5}.Release|Any CPU.Build.0 = Release|Any CPU 33 | {C91E86B6-72B1-4367-9CD4-A1116FECD752}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {C91E86B6-72B1-4367-9CD4-A1116FECD752}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {C91E86B6-72B1-4367-9CD4-A1116FECD752}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {C91E86B6-72B1-4367-9CD4-A1116FECD752}.Release|Any CPU.Build.0 = Release|Any CPU 37 | {14C8FADA-AD8F-421E-8FB3-F9EA797E507C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {14C8FADA-AD8F-421E-8FB3-F9EA797E507C}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 | {14C8FADA-AD8F-421E-8FB3-F9EA797E507C}.Release|Any CPU.ActiveCfg = Release|Any CPU 40 | {14C8FADA-AD8F-421E-8FB3-F9EA797E507C}.Release|Any CPU.Build.0 = Release|Any CPU 41 | {77FAE56F-23ED-4B30-B961-BBC7B492AAFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 42 | {77FAE56F-23ED-4B30-B961-BBC7B492AAFE}.Debug|Any CPU.Build.0 = Debug|Any CPU 43 | {77FAE56F-23ED-4B30-B961-BBC7B492AAFE}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {77FAE56F-23ED-4B30-B961-BBC7B492AAFE}.Release|Any CPU.Build.0 = Release|Any CPU 45 | EndGlobalSection 46 | EndGlobal 47 | -------------------------------------------------------------------------------- /src/Interstellar.MacOS.WebKit/BrowserWindow.fs: -------------------------------------------------------------------------------- 1 | namespace Interstellar.MacOS.WebKit.Internal 2 | open System 3 | open AppKit 4 | open CoreGraphics 5 | open Foundation 6 | open Interstellar 7 | open Interstellar.MacOS.WebKit 8 | open WebKit 9 | 10 | type NiblessViewController(view: NSView) = 11 | inherit NSViewController() 12 | 13 | override this.LoadView () = 14 | base.View <- view 15 | 16 | type BrowserWindow(config: BrowserWindowConfig) as this = 17 | inherit NSWindowController("BrowserWindow") 18 | 19 | let browser = new Browser(config) 20 | 21 | let closedEvt = new Event<_>() 22 | let shownEvt = new Event<_>() 23 | let disposedEvt = new Event<_>() 24 | 25 | do 26 | let wkBrowserController = { 27 | new NiblessViewController(browser.WebKitBrowser) with 28 | // This KeyDown override prevents the macOS beep (error) sound on key presses 29 | override this.KeyDown (event: NSEvent) = () 30 | override this.ViewDidAppear () = 31 | base.ViewDidAppear () 32 | shownEvt.Trigger () 33 | } 34 | this.Window <- 35 | new NSWindow(new CGRect (0., 0., 1000., 500.), 36 | NSWindowStyle.Titled ||| NSWindowStyle.Closable ||| NSWindowStyle.Miniaturizable ||| NSWindowStyle.Resizable, 37 | NSBackingStore.Buffered, false) 38 | this.Window.WillClose.Add (fun x -> closedEvt.Trigger ()) 39 | this.Window.ContentView <- browser.WebKitBrowser 40 | this.Window.ContentViewController <- wkBrowserController 41 | this.Window.Center () 42 | this.Window.AwakeFromNib () 43 | 44 | member this.WKBrowserView = browser.WebKitBrowser 45 | member this.WKBrowser = browser 46 | 47 | override this.LoadWindow () = 48 | base.LoadWindow () 49 | 50 | interface IBrowserWindow with 51 | member this.Browser = upcast browser 52 | member this.Close () = (this :> NSWindowController).Close () 53 | [] 54 | member val Closed = closedEvt.Publish 55 | member this.IsShowing = 56 | let w = (this :> NSWindowController).Window 57 | w.IsVisible || w.IsMiniaturized 58 | member this.Show () = async { 59 | (this :> NSWindowController).ShowWindow this 60 | } 61 | member this.NativeWindow = this.Window 62 | member this.Platform = BrowserWindowPlatform.MacOS 63 | [] 64 | member val Shown = shownEvt.Publish 65 | member this.Size 66 | with get () = 67 | let size = this.Window.Frame.Size 68 | float size.Width, float size.Height 69 | and set (width, height) = 70 | let oldFrame = this.Window.Frame 71 | // Cocoa uses a bottom-left origin, so we have to move the bottom-left corner in order to keep the top-right 72 | // corner in place 73 | let rect = new CGRect(float oldFrame.X, float oldFrame.Y - (height - float oldFrame.Height), width, height) 74 | this.Window.SetFrame (rect, true, true) 75 | member this.Title 76 | with get () = base.Window.Title 77 | and set x = base.Window.Title <- x -------------------------------------------------------------------------------- /src/Interstellar.Chromium/JavascriptInjectionFilter.fs: -------------------------------------------------------------------------------- 1 | namespace Interstellar.Chromium 2 | open System 3 | open System.Collections.Generic 4 | open System.Diagnostics 5 | open System.IO 6 | open System.Text 7 | open CefSharp 8 | open CefSharp.Handler 9 | 10 | type ScriptLocation = Head | Body 11 | 12 | module JSInjectionHelpers = 13 | // Dealing with inserting arbitrary contents into a script tag is hard to do right, because there's really no right way to do it, since 14 | // the inside of a script tag doesn't recognize HTML entities, meaning we can't just escape it for HTML. Granted, the way that this library 15 | // is meant to be used, I don't expect it to be a vulnerability source, but we can at least try this. 16 | // see https://blog.uploadcare.com/vulnerability-in-html-design-the-script-tag-33d24642359e 17 | // and the HTML spec's recommendations: https://www.w3.org/TR/html52/semantics-scripting.html#restrictions-for-contents-of-script-elements 18 | let escapeScriptTagContents (contents: string) = 19 | contents 20 | .Replace(" 16 | 17 | 18 | 19 | 20 | 21 | 22 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | Links 54 | License (MIT) 55 | Changelog 56 | Source Repository 57 | {{fsdocs-list-of-documents}} 58 | {{fsdocs-list-of-namespaces}} 59 | 60 | 61 | 62 | 63 | 64 | {{fsdocs-collection-name}} 65 | 66 | 67 | 68 | {{fsdocs-content}} 69 | {{fsdocs-tooltips}} 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 |