diff --git a/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/UI.WebView2View.cls b/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/UI.WebView2View.cls index 1f72f3e8e..1d71b1e0d 100644 --- a/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/UI.WebView2View.cls +++ b/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/UI.WebView2View.cls @@ -149,8 +149,6 @@ createUnavailableLink to: self! createWebView - "Currently we don't attempt to set any controller options - see ICoreWebView2ControllerOptions[2]" - | controllerOptions | controllerOptions := webviewEnvironment createCoreWebView2ControllerOptions. self isInPrivateModeEnabled ifTrue: [controllerOptions isInPrivateModeEnabled: true]. @@ -165,7 +163,9 @@ createWebView createWebViewEnvironment | completed userDataFolder | completed := (WebView2CompletionHandler - completionBlock: [:hr :env | hr < 0 ifFalse: [self onEnvironmentCreated: env]]) + completionBlock: [:hr :env | hr < 0 + ifTrue: [HRESULTError signal: 'Failed to create WebView2 environment' with: hr] + ifFalse: [self onEnvironmentCreated: env]]) queryInterface: ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler. userDataFolder := self defaultUserDataFolder. @@ -183,8 +183,13 @@ createWebViewEnvironment to: self! defaultUserDataFolder + "Answer a path for the browser data folder that is writable with user privileges. We make this unique for a particular configuration of environment options, because it isn't possible to start webview2 instances with the same data folder that have mismatched environment options." + ^File composePath: (SessionManager current getenv: 'LocalAppData') - subPath: (File splitFilenameFrom: (DynamicLinkLibrary moduleFileName: nil)) , '.WebView2'! + subPath: '<1s>-<2s>.WebView2' << { + File splitStemFrom: (DynamicLinkLibrary moduleFileName: nil). + self environmentOptions thumbprint + }! defaultWindowProcessing: message wParam: wParam lParam: lParam "Private - Pass an event to the 'default' window procedure of the receiver." @@ -353,6 +358,22 @@ notifyMove webviewController isNull ifTrue: [^self]. webviewController NotifyParentWindowPositionChanged! +observeBrowserProcessExit + "Private - Process exit is a special case as we don't want to unregister this immediately when closing the browser view, or we will never get the event!!. + When the view is being fully recreated, e.g. due to changing a style that is set in environment options such as the scrollbar style, we may very well receive this for the previous browser process after we have already started creating the new view." + + | urlPresenter processExited | + urlPresenter := self presenter. + processExited := WebView2EventSink new. + processExited + source: webviewEnvironment + interfaceClass: ICoreWebView2BrowserProcessExitedEventHandler + handler: + [:source :args | + urlPresenter trigger: #processExited: with: args. + processExited free]. + processExited register! + observeControllerEvents ##({ ICoreWebView2AcceleratorKeyPressedEventHandler. @@ -369,17 +390,22 @@ observeControllerEvents do: [:each | self observeEvent: each from: webviewComposition]]! observeEnvironmentEvents - environmentEventHandlers := OrderedCollection new: 3. - ##({ - ICoreWebView2NewBrowserVersionAvailableEventHandler. - ICoreWebView2BrowserProcessExitedEventHandler. - ICoreWebView2ProcessInfosChangedEventHandler - }) do: - [:each | - self registerEnvironmentEventSink: (WebView2EventSink - source: webviewEnvironment - interfaceClass: each - handler: (each triggerBlockFor: self presenter))]! + "Process exit is a special case as we don't want to unregister this immediately when closing the browser view, or we will never get the event!!" + + environmentEventHandlers + ifNil: + [environmentEventHandlers := ##({ + ICoreWebView2NewBrowserVersionAvailableEventHandler. + ICoreWebView2ProcessInfosChangedEventHandler + }) collect: + [:each | + (WebView2EventSink + source: webviewEnvironment + interfaceClass: each + handler: (each triggerBlockFor: self presenter)) + register; + yourself]]. + self observeBrowserProcessExit! observeEvent: anICoreWebView2EventHandlerClass from: anICoreWebView2EventSource ^self registerEventSink: (WebView2EventSink @@ -439,8 +465,12 @@ observeWindowEvents }) do: [:each | self observeEvent: each from: webview]! onControllerCreated: anICoreWebView2Controller - webviewController := self queryControllerInterface: anICoreWebView2Controller. - self observeControllerEvents. + webviewController := anICoreWebView2Controller downCast. + "Since we cannot trigger events off the presenter until it has been set, we delay observing environment events to here as at this point we know we definitely have a presenter and webview environment" + self + observeEnvironmentEvents; + observeControllerEvents. + self presenter trigger: #controllerAvailable. self resizeContentToFit. webview := webviewController coreWebView2. self initializeControl! @@ -449,13 +479,13 @@ onDestroyed self unregisterEvents. self content close. self releaseWebView. - "We don't free the webview environment, but rather let it be finalised. This allows an external observer to watch for the browser process exiting." - webviewEnvironment := nil. + self releaseWebViewEnvironment. ^super onDestroyed! onEnvironmentCreated: anICoreWebView2Environment webviewEnvironment := anICoreWebView2Environment downCast. - self observeEnvironmentEvents. + "Note that the presenter has not been set this point" + self trigger: #environmentAvailable. self createWebView! onHistoryChanged @@ -522,19 +552,6 @@ profileName: aString profileName := aString. webviewController notNull ifTrue: [self recreate]! -queryControllerInterface: anIUnknown - | interfaceClass | - interfaceClass := ICoreWebView2Controller4. - - [(anIUnknown queryInterface: interfaceClass ifNone: []) ifNotNil: [:interface | ^interface]. - (interfaceClass := interfaceClass superclass) == ICoreWebView2Controller] - whileFalse. - ^anIUnknown queryInterface: ICoreWebView2Controller! - -registerEnvironmentEventSink: aWebView2EventSink - aWebView2EventSink register. - ^environmentEventHandlers add: aWebView2EventSink! - registerEventSink: aWebView2EventSink aWebView2EventSink register. ^eventHandlers add: aWebView2EventSink! @@ -550,6 +567,9 @@ releaseWebView [webviewController free. webviewController := nil]! +releaseWebViewEnvironment + webviewEnvironment := nil! + resizeContentToFit self content rectangle: self clientRectangle! @@ -568,7 +588,7 @@ scrollBarStyle: anInteger | style | style := anInteger ?? COREWEBVIEW2_SCROLLBAR_STYLE_DEFAULT. self scrollBarStyle = style ifTrue: [^self]. - self recreateAround: [environmentOptions scrollBarStyle: style]! + self recreateAround: ["self halt. "environmentOptions scrollBarStyle: style]! setControlBackcolor: aColor self webviewController defaultBackgroundColor: aColor! @@ -594,13 +614,13 @@ subViews ^#()! trackingPreventionLevel - ^environmentOptions trackingPreventionLevel! + ^environmentOptions isTrackingPreventionEnabled + ifTrue: [self profile preferredTrackingPreventionLevel] + ifFalse: [COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_NONE]! trackingPreventionLevel: anInteger - | level | - level := anInteger ?? COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_BALANCED. - self trackingPreventionLevel = level ifTrue: [^self]. - self recreateAround: [environmentOptions trackingPreventionLevel: level]! + self profile + preferredTrackingPreventionLevel: anInteger ?? COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_BALANCED! unregisterEvents eventHandlers @@ -723,6 +743,7 @@ isWebMessageEnabled:!public!settings! ! isZoomControlEnabled!public!settings! ! isZoomControlEnabled:!public!settings! ! notifyMove!event handling!private! ! +observeBrowserProcessExit!private!realizing/unrealizing! ! observeControllerEvents!private!realizing/unrealizing! ! observeEnvironmentEvents!private!realizing/unrealizing! ! observeEvent:from:!helpers!private! ! @@ -740,10 +761,9 @@ onViewCreated!event handling!public! ! profile!commands!public! ! profileName!accessing!public! ! profileName:!accessing!public! ! -queryControllerInterface:!helpers!private! ! -registerEnvironmentEventSink:!helpers!public! ! registerEventSink:!helpers!public! ! releaseWebView!operations!private!realizing/unrealizing! ! +releaseWebViewEnvironment!operations!private!realizing/unrealizing! ! resizeContentToFit!helpers!private! ! scriptLocale!accessing!public! ! scriptLocale:!accessing!public! ! diff --git a/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2 Tests.pax b/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2 Tests.pax index 9c0b11906..ca8e71853 100644 --- a/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2 Tests.pax +++ b/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2 Tests.pax @@ -93,7 +93,7 @@ Core.Tests.DolphinTest UI.Tests.PresenterTest subclass: #'WebView2.Tests.WebView2ViewTest' - instanceVariableNames: 'domContentLoaded navigationCompleted processFailed webViewReady processExitedSink webviewEnvironment processExited navigationStarting' + instanceVariableNames: 'domContentLoaded navigationCompleted processFailed webViewReady webviewEnvironment processExited navigationStarting' classVariableNames: '' imports: #(#{UI}) classInstanceVariableNames: '' diff --git a/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.ICoreWebView2BrowserProcessExitedEventArgs.cls b/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.ICoreWebView2BrowserProcessExitedEventArgs.cls index 264e83a9b..18e6cbcae 100644 --- a/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.ICoreWebView2BrowserProcessExitedEventArgs.cls +++ b/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.ICoreWebView2BrowserProcessExitedEventArgs.cls @@ -48,6 +48,9 @@ browserProcessExitKind self get_BrowserProcessExitKind: buf. ^buf asSignedInteger! +browserProcessExitKindName + ^(#('Normal' 'Failed') lookup: self browserProcessExitKind + 1) ?? 'Unknown'! + browserProcessId "Answer the unsigned value of the 'BrowserProcessId' property of the receiver." @@ -78,10 +81,11 @@ isNormalExit ^self browserProcessExitKind == COREWEBVIEW2_BROWSER_PROCESS_EXIT_KIND_NORMAL! printableProperties - ^#(#browserProcessExitKind #browserProcessId)! ! + ^#(#browserProcessExitKindName #browserProcessId)! ! !WebView2.ICoreWebView2BrowserProcessExitedEventArgs categoriesForMethods! browserProcessExitKind!properties!public! ! +browserProcessExitKindName!properties!public! ! browserProcessId!properties!public! ! get_BrowserProcessExitKind:!**auto generated**!COM Interfaces-ICoreWebView2BrowserProcessExitedEventArgs!private! ! get_BrowserProcessId:!**auto generated**!COM Interfaces-ICoreWebView2BrowserProcessExitedEventArgs!private! ! diff --git a/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.ICoreWebView2Controller.cls b/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.ICoreWebView2Controller.cls index 4cde0c3fa..6adc71e3a 100644 --- a/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.ICoreWebView2Controller.cls +++ b/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.ICoreWebView2Controller.cls @@ -224,7 +224,7 @@ defaultBackgroundColor "Answer the value of the 'DefaultBackgroundColor' property of the receiver." | buf | - buf := ByteArray newFixed: 4. + buf := UInt32Bytes new. self get_DefaultBackgroundColor: buf. ^Graphics.Color fromArray: buf! diff --git a/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.ICoreWebView2EnvironmentOptions7.cls b/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.ICoreWebView2EnvironmentOptions7.cls index cc50701b6..422526d6d 100644 --- a/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.ICoreWebView2EnvironmentOptions7.cls +++ b/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.ICoreWebView2EnvironmentOptions7.cls @@ -109,13 +109,13 @@ releaseChannels: value self put_ReleaseChannels: value! ! !WebView2.ICoreWebView2EnvironmentOptions7 categoriesForMethods! -channelSearchKind!**auto generated**!properties!public! ! +channelSearchKind!properties!public! ! channelSearchKind:!**auto generated**!properties!public! ! get_ChannelSearchKind:!**auto generated**!COM Interfaces-ICoreWebView2EnvironmentOptions7!private! ! get_ReleaseChannels:!**auto generated**!COM Interfaces-ICoreWebView2EnvironmentOptions7!private! ! put_ChannelSearchKind:!**auto generated**!COM Interfaces-ICoreWebView2EnvironmentOptions7!private! ! put_ReleaseChannels:!**auto generated**!COM Interfaces-ICoreWebView2EnvironmentOptions7!private! ! -releaseChannels!**auto generated**!properties!public! ! +releaseChannels!properties!public! ! releaseChannels:!**auto generated**!properties!public! ! ! diff --git a/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.ICoreWebView2EnvironmentOptions8.cls b/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.ICoreWebView2EnvironmentOptions8.cls index 7525a4653..f552a4dd6 100644 --- a/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.ICoreWebView2EnvironmentOptions8.cls +++ b/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.ICoreWebView2EnvironmentOptions8.cls @@ -73,7 +73,7 @@ scrollBarStyle: value !WebView2.ICoreWebView2EnvironmentOptions8 categoriesForMethods! get_ScrollBarStyle:!**auto generated**!COM Interfaces-ICoreWebView2EnvironmentOptions8!private! ! put_ScrollBarStyle:!**auto generated**!COM Interfaces-ICoreWebView2EnvironmentOptions8!private! ! -scrollBarStyle!**auto generated**!properties!public! ! +scrollBarStyle!properties!public! ! scrollBarStyle:!**auto generated**!properties!public! ! ! diff --git a/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.ICoreWebView2EventHandler.cls b/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.ICoreWebView2EventHandler.cls index af829fb39..63860412d 100644 --- a/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.ICoreWebView2EventHandler.cls +++ b/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.ICoreWebView2EventHandler.cls @@ -60,14 +60,14 @@ eventName ^self subclassResponsibility! hasArgs - ^self argsClass == IUnknown! + ^self argsClass ~~ IUnknown! triggerBlockFor: aUrlPresenter | event | event := self eventName. ^self hasArgs - ifTrue: [[aUrlPresenter trigger: event]] - ifFalse: [[:source :args | aUrlPresenter trigger: event with: args]]! ! + ifTrue: [[:source :args | aUrlPresenter trigger: event with: args]] + ifFalse: [[aUrlPresenter trigger: event]]! ! !WebView2.ICoreWebView2EventHandler class categoriesForMethods! argsClass!constants!public! ! diff --git a/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.Tests.WebView2EnvironmentOptionsTest.cls b/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.Tests.WebView2EnvironmentOptionsTest.cls index 14780d7ae..8ab1dedab 100644 --- a/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.Tests.WebView2EnvironmentOptionsTest.cls +++ b/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.Tests.WebView2EnvironmentOptionsTest.cls @@ -114,12 +114,14 @@ testEnableTrackingPrevention subject := WebView2EnvironmentOptions new. interface := subject queryInterface: ICoreWebView2EnvironmentOptions5. self assert: subject trackingPreventionLevel equals: COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_BALANCED. - self assert: interface enableTrackingPrevention - equals: COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_BALANCED. - interface enableTrackingPrevention: COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_STRICT. - self assert: subject trackingPreventionLevel equals: COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_STRICT. - self assert: interface enableTrackingPrevention - equals: COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_STRICT! + self assert: interface enableTrackingPrevention. + interface enableTrackingPrevention: false. + self deny: subject isTrackingPreventionEnabled. + self assert: subject trackingPreventionLevel equals: COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_NONE. + interface enableTrackingPrevention: true. + self assert: subject isTrackingPreventionEnabled. + self assert: subject trackingPreventionLevel equals: COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_BALANCED. +! testExclusiveDataFolderAccess | subject interface | @@ -193,7 +195,7 @@ testStlConvertFromVersion0 self assert: options isCustomCrashReportingEnabled. self assert: options browserVersion equals: '100.0.1185'. self assert: options locale equals: 'fr-FR'. - self assert: options trackingPreventionLevel equals: COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_BALANCED. + self assert: options isTrackingPreventionEnabled. self assert: options channelSearchKind equals: COREWEBVIEW2_CHANNEL_SEARCH_KIND_MOST_STABLE. self assert: options releaseChannels equals: COREWEBVIEW2_RELEASE_CHANNELS_STABLE. self assert: options scrollBarStyle equals: COREWEBVIEW2_SCROLLBAR_STYLE_DEFAULT. @@ -201,12 +203,12 @@ testStlConvertFromVersion0 testStlConvertFromVersion0a | options | - options := (Object fromLiteralStoreArray: #(#'!!STL' 6 1286 #{WebView2.WebView2EnvironmentOptions} nil 13 8 'fr-FR' 8 '100.0.1185' 5)). + options := (Object fromLiteralStoreArray: #(#'!!STL' 6 1286 #{WebView2.WebView2EnvironmentOptions} nil 13 8 'fr-FR' 8 '100.0.1185' 1)). self deny: options allowSSO. self assert: options isCustomCrashReportingEnabled. self assert: options browserVersion equals: '100.0.1185'. self assert: options locale equals: 'fr-FR'. - self assert: options trackingPreventionLevel equals: COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_BALANCED. + self deny: options isTrackingPreventionEnabled. self assert: options channelSearchKind equals: COREWEBVIEW2_CHANNEL_SEARCH_KIND_MOST_STABLE. self assert: options releaseChannels equals: COREWEBVIEW2_RELEASE_CHANNELS_STABLE. self assert: options scrollBarStyle equals: COREWEBVIEW2_SCROLLBAR_STYLE_DEFAULT. @@ -220,7 +222,7 @@ testStlConvertFromVersion1 self assert: options isCustomCrashReportingEnabled. self assert: options browserVersion equals: '100.0.1185'. self assert: options locale equals: 'fr-FR'. - self assert: options trackingPreventionLevel equals: COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_STRICT. + self assert: options isTrackingPreventionEnabled. self assert: options channelSearchKind equals: COREWEBVIEW2_CHANNEL_SEARCH_KIND_LEAST_STABLE. self assert: options releaseChannels equals: COREWEBVIEW2_RELEASE_CHANNELS_BETA. self assert: options scrollBarStyle equals: COREWEBVIEW2_SCROLLBAR_STYLE_FLUENT_OVERLAY. @@ -233,7 +235,7 @@ testStlConvertFromVersion2 self assert: options isCustomCrashReportingEnabled. self assert: options browserVersion equals: '100.0.1185'. self assert: options locale equals: 'fr-FR'. - self assert: options trackingPreventionLevel equals: COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_BALANCED. + self assert: options isTrackingPreventionEnabled. self assert: options channelSearchKind equals: COREWEBVIEW2_CHANNEL_SEARCH_KIND_MOST_STABLE. self assert: options releaseChannels equals: COREWEBVIEW2_RELEASE_CHANNELS_STABLE. self assert: options scrollBarStyle equals: COREWEBVIEW2_SCROLLBAR_STYLE_DEFAULT. diff --git a/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.Tests.WebView2ViewTest.cls b/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.Tests.WebView2ViewTest.cls index 1f291bdc0..4d33276fd 100644 --- a/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.Tests.WebView2ViewTest.cls +++ b/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.Tests.WebView2ViewTest.cls @@ -2,7 +2,7 @@ UI.Tests.PresenterTest subclass: #'WebView2.Tests.WebView2ViewTest' - instanceVariableNames: 'domContentLoaded navigationCompleted processFailed webViewReady processExitedSink webviewEnvironment processExited navigationStarting' + instanceVariableNames: 'domContentLoaded navigationCompleted processFailed webViewReady webviewEnvironment processExited navigationStarting' classVariableNames: '' imports: #(#{UI}) classInstanceVariableNames: '' @@ -34,6 +34,9 @@ initializePresenter to: self; when: #webviewAvailable send: #onWebViewAvailable + to: self; + when: #processExited: + send: #onBrowserProcessExited: to: self. webViewReady := false. presenter createView: URLPresenter defaultView. @@ -42,13 +45,13 @@ initializePresenter self waitAtMost: 20 seconds while: [webViewReady not]. self assert: presenter view webview notNull. webviewEnvironment := presenter view webviewEnvironment. - self assert: webviewEnvironment notNull. - "Set up our own event sink to be notified of the browser process shutting down. There is no point observing the presenter for normal process exit, because the events are torn down before that happens, so we have to register our own event sink. This is only necessary so that we can ensure the browser is truly shut down at the end of one test case before we fire it up again for the next." - processExitedSink := WebView2EventSink - source: presenter view webviewEnvironment - interfaceClass: ICoreWebView2BrowserProcessExitedEventHandler - handler: [:source :args | args isNormalExit ifTrue: [processExited := args browserProcessId]]. - processExitedSink register! + self assert: webviewEnvironment notNull! + +onBrowserProcessExited: anICoreWebView2BrowserProcessExitedEventArgs + "We track browser process shut down in order to wait at the end of one test case before we fire it up again for the next. This significantly increases the reliability of the tests when run together." + + anICoreWebView2BrowserProcessExitedEventArgs isNormalExit + ifTrue: [processExited := anICoreWebView2BrowserProcessExitedEventArgs browserProcessId]! onDomContentLoaded: anICoreWebView2DOMContentLoadedEventArgs domContentLoaded := anICoreWebView2DOMContentLoadedEventArgs navigationId! @@ -77,11 +80,7 @@ tearDown [navigationCompleted free. navigationCompleted := nil]. self destroyPresenter. - processExitedSink - ifNotNil: - [self waitAtMost: 20 seconds while: [processExited isNil]. - processExitedSink free. - processExitedSink := nil]. + self waitAtMost: 20 seconds while: [processExited isNil]. webviewEnvironment notNull ifTrue: [webviewEnvironment free. @@ -136,8 +135,9 @@ testContextMenu self sendKeyPress: VK_RETURN extended: false. self waitAtMost: 200 milliseconds while: [cmdFired not]. cmdEventSink free. - newCmd free. - self assert: cmdFired! + self assert: cmdFired. + +! testNavigateToString "Bit of a kick-the-tyres test, but it does execute a lot of the integration, e.g. creating the WebView2 environment, controller and view, and setting up event handlers and handling of events." @@ -485,6 +485,7 @@ waitForNavigationCompleted !WebView2.Tests.WebView2ViewTest categoriesForMethods! classToTest!constants!private! ! initializePresenter!public!Running! ! +onBrowserProcessExited:!event handling!private! ! onDomContentLoaded:!event handling!private! ! onNavigationCompleted:!event handling!private! ! onNavigationStarting:!event handling!private! ! diff --git a/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.WebView2EnvironmentOptions.cls b/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.WebView2EnvironmentOptions.cls index 5a9a70538..9e851c99b 100644 --- a/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.WebView2EnvironmentOptions.cls +++ b/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.WebView2EnvironmentOptions.cls @@ -2,7 +2,7 @@ OS.COM.InterfaceImplementation subclass: #'WebView2.WebView2EnvironmentOptions' - instanceVariableNames: 'additionalBrowserArguments flags locale browserVersion trackingPreventionLevel channelSearchKind releaseChannels scrollBarStyle customSchemeRegistrations' + instanceVariableNames: 'additionalBrowserArguments flags locale browserVersion _unused5 channelSearchKind releaseChannels scrollBarStyle customSchemeRegistrations' classVariableNames: '' imports: #() classInstanceVariableNames: '' @@ -10,7 +10,8 @@ OS.COM.InterfaceImplementation 'AllowSSOMask' -> 16r1. 'BrowseExtensionsEnabledMask' -> 16r8. 'CustomCrashReportingMask' -> 16r4. - 'ExclusiveUserDataFolderAccessMask' -> 16r2 + 'ExclusiveUserDataFolderAccessMask' -> 16r2. + 'TrackingPreventionDisabledMask' -> 16r10 }! WebView2.WebView2EnvironmentOptions guid: (Core.GUID fromString: '{a29cb496-0b83-42f6-b6b1-2670cb4d42fb}')! @@ -70,6 +71,48 @@ customSchemeRegistrations customSchemeRegistrations: aCollection customSchemeRegistrations := aCollection asArray! +displayOn: aPuttableStream + browserVersion + ifNotNil: + [aPuttableStream + nextPutAll: 'browserVersion: '; + print: browserVersion]. + additionalBrowserArguments + ifNotNil: + [aPuttableStream + nextPutAll: ', additionalBrowserArguments: '; + print: additionalBrowserArguments]. + flags == 0 + ifFalse: + [aPuttableStream + nextPutAll: ', flags: '; + display: flags]. + locale + ifNotNil: + [aPuttableStream + nextPutAll: ', locale: '; + print: locale name]. + channelSearchKind = COREWEBVIEW2_CHANNEL_SEARCH_KIND_MOST_STABLE + ifFalse: + [aPuttableStream + nextPutAll: ', channelSearchKind: '; + display: channelSearchKind]. + releaseChannels = COREWEBVIEW2_RELEASE_CHANNELS_STABLE + ifFalse: + [aPuttableStream + nextPutAll: ', releaseChannels: '; + display: releaseChannels]. + scrollBarStyle = COREWEBVIEW2_SCROLLBAR_STYLE_DEFAULT + ifFalse: + [aPuttableStream + nextPutAll: ', scrollBarStyle: '; + display: scrollBarStyle]. + customSchemeRegistrations isEmpty + ifFalse: + [aPuttableStream + nextPutAll: ', customSchemeRegistrations: '; + display: customSchemeRegistrations]! + exclusiveUserDataFolderAccess ^flags allMask: ExclusiveUserDataFolderAccessMask! @@ -120,7 +163,7 @@ get_EnableTrackingPrevention: value HRESULT __stdcall EnableTrackingPrevention( [out, retval]long* value);" - value value: self trackingPreventionLevel. + value value: self isTrackingPreventionEnabled asParameter. ^S_OK! get_ExclusiveUserDataFolderAccess: value @@ -174,8 +217,7 @@ get_TargetCompatibleBrowserVersion: value HRESULT __stdcall TargetCompatibleBrowserVersion( [out, retval]LPWSTR* value);" - value - value: (browserVersion ifNil: [0] ifNotNil: [browserVersion asUtf16String copyToCOMTaskMemory detach]). + value value: (browserVersion ?? '') asUtf16String copyToCOMTaskMemory detach. ^S_OK! GetCustomSchemeRegistrations: pCount schemeRegistrations: ppSchemeRegistrations @@ -203,7 +245,6 @@ initialize super initialize. flags := ##(AllowSSOMask | ExclusiveUserDataFolderAccessMask). browserVersion := CORE_WEBVIEW_TARGET_PRODUCT_VERSION. - trackingPreventionLevel := COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_BALANCED. channelSearchKind := COREWEBVIEW2_CHANNEL_SEARCH_KIND_MOST_STABLE. releaseChannels := COREWEBVIEW2_RELEASE_CHANNELS_STABLE. scrollBarStyle := COREWEBVIEW2_SCROLLBAR_STYLE_DEFAULT. @@ -215,6 +256,16 @@ isCustomCrashReportingEnabled isCustomCrashReportingEnabled: aBoolean flags := flags mask: CustomCrashReportingMask set: aBoolean! +isTrackingPreventionEnabled + "Answer whether tracking prevention measures should be enabled." + + ^flags noMask: TrackingPreventionDisabledMask! + +isTrackingPreventionEnabled: aBoolean + "Set whether tracking prevention measures should be enabled." + + flags := flags mask: TrackingPreventionDisabledMask set: aBoolean not! + locale ^locale ?? Locale.UserDefault! @@ -392,24 +443,23 @@ supportedInterfaces ICoreWebView2EnvironmentOptions8 })! +thumbprint + "Answer a short (32 character) hex string that uniquely identifies the specific settings in the receiver." + + ^(BCrypt md5Hash: self displayString) asHexString! + trackingPreventionLevel "Answer the from the COREWEBVIEW2_TRACKING_PREVENTION_LEVEL enumeration that specifies the tracking prevention level to be configured for WebView2's created in the environment. By default this is COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_BALANCED (2)." - ^trackingPreventionLevel! + #deprecated. "It isn't possible to control the TPL at this level, only whether on or off. It should be set in the profile." + ^self isTrackingPreventionEnabled + ifTrue: [COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_BALANCED] + ifFalse: [COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_NONE]! -trackingPreventionLevel: value - (value between: COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_NONE - and: COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_STRICT) - ifFalse: - [InvalidArgumentError - signal: 'Tracking prevention level <1p> not in expected range [<2p>, <3p>}' << { - value. - COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_NONE. - COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_STRICT - } - with: 'value']. - trackingPreventionLevel := value! ! +trackingPreventionLevel: anInteger + #deprecated. "It isn't possible to control the TPL at this level, only whether on or off. It should be set in the profile." + self isTrackingPreventionEnabled: anInteger ~= COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_NONE! ! !WebView2.WebView2EnvironmentOptions categoriesForMethods! additionalBrowserArguments!accessing!public! ! @@ -424,6 +474,7 @@ channelSearchKind!accessing!public! ! channelSearchKind:!accessing!public! ! customSchemeRegistrations!public! ! customSchemeRegistrations:!public! ! +displayOn:!displaying!public! ! exclusiveUserDataFolderAccess!accessing!public! ! exclusiveUserDataFolderAccess:!accessing!public! ! get_AdditionalBrowserArguments:!COM Interfaces-ICoreWebView2EnvironmentOptions!private! ! @@ -441,6 +492,8 @@ GetCustomSchemeRegistrations:schemeRegistrations:!COM Interfaces-ICoreWebView2En initialize!initializing!public! ! isCustomCrashReportingEnabled!accessing!public! ! isCustomCrashReportingEnabled:!accessing!public! ! +isTrackingPreventionEnabled!accessing!public! ! +isTrackingPreventionEnabled:!accessing!public! ! locale!accessing!public! ! locale:!accessing!public! ! put_AdditionalBrowserArguments:!COM Interfaces-ICoreWebView2EnvironmentOptions!private! ! @@ -460,6 +513,7 @@ scrollBarStyle!accessing!public! ! scrollBarStyle:!accessing!public! ! SetCustomSchemeRegistrations:schemeRegistrations:!COM Interfaces-ICoreWebView2EnvironmentOptions4!private! ! supportedInterfaces!constants!public! ! +thumbprint!accessing!public! ! trackingPreventionLevel!accessing!public! ! trackingPreventionLevel:!accessing!public! ! ! @@ -481,6 +535,7 @@ icon stbConvertFrom: anSTBClassFormat | version | version := anSTBClassFormat version. + version == 2 ifTrue: [^[:data | self stbConvertFromVersion2: data]]. version == 1 ifTrue: [^[:data | self stbConvertFromVersion1: data]]. version == 0 ifTrue: [^[:data | self stbConvertFromVersion0: data]]. ^super stbConvertFrom: anSTBClassFormat! @@ -508,19 +563,32 @@ stbConvertFromVersion0: anArray stbConvertFromVersion1: anArray "Private - Convert version 1 package by adding default values for customSchemeRegistrations, then as this is the last converter in the chain, translate to a class instance." - ^(anArray copyWith: #()) becomeA: self! + ^self stbConvertFromVersion2: (anArray copyWith: #())! + +stbConvertFromVersion2: anArray + "Private - Convert version 2 options by fixing the misunderstanding of tracking prevention. At the environment options level it is only possible to enable/disable tracking. The actual tracking prevention level must be set in the profile." + + | tpl | + tpl := anArray at: 5. + anArray at: 5 put: nil. + "End of the chain" + ^(anArray becomeA: self) + isTrackingPreventionEnabled: tpl ~= COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_NONE; + yourself! stbVersion "Version 1 - adds channelSearchKind, releaseChannels, scrollBarStyle. - Version 2 - adds customSchemeRegistrations" + Version 2 - adds customSchemeRegistrations + Version 3 - fixes misunderstanding of tracking prevention in environment options. This can only be turned on and off, not level set." - ^2! ! + ^3! ! !WebView2.WebView2EnvironmentOptions class categoriesForMethods! icon!constants!development!public! ! stbConvertFrom:!public! ! stbConvertFromVersion0:!binary filing!private! ! stbConvertFromVersion1:!binary filing!private! ! +stbConvertFromVersion2:!binary filing!private! ! stbVersion!binary filing!public! ! ! diff --git a/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.WebView2EventSink.cls b/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.WebView2EventSink.cls index 0f6a2fa0e..3d939605b 100644 --- a/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.WebView2EventSink.cls +++ b/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.WebView2EventSink.cls @@ -64,6 +64,9 @@ register: anICoreWebView2Interface interface := self queryInterface: interfaceClass. token := interface register: anICoreWebView2Interface! +source + ^source! + source: anICoreWebView2EventSource interfaceClass: anICoreWebView2EventHandlerClass handler: aDyadicValuable source := anICoreWebView2EventSource. interfaceClass := anICoreWebView2EventHandlerClass. @@ -72,15 +75,14 @@ source: anICoreWebView2EventSource interfaceClass: anICoreWebView2EventHandlerCl supportedInterfaces "Answer the set of interface classes supported by the receiver." - ^##({ IUnknown } - , (ICoreWebView2EventHandler allSubclasses reject: [:each | each isNonInstantiable]))! + ^{ IUnknown. interfaceClass }! unregister self unregister: source. source := nil! unregister: anICoreWebView2Interface - interface ifNil: [^self]. + interface isNull ifTrue: [^self]. interface unregister: token from: anICoreWebView2Interface; free. @@ -95,6 +97,7 @@ printOn:!printing!public! ! queryInterface:ifNone:!accessing!accessing-interfaces!public! ! register!initializing!public! ! register:!initializing!public! ! +source!accessing!private! ! source:interfaceClass:handler:!initializing!private! ! supportedInterfaces!constants!public! ! unregister!public!realizing/unrealizing! ! diff --git a/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.pax b/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.pax index 6ec76af0b..30935ebe2 100644 --- a/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.pax +++ b/Core/Object Arts/Dolphin/ActiveX/Components/WebView2/WebView2.pax @@ -320,6 +320,7 @@ package setPrerequisites: #( '..\..\COM\OLE COM' '..\..\Persist\OLE Persistence and Data Transfer' '..\..\Structured Storage\OLE Streams' + '..\..\..\System\Win32\CNG\Windows Crypto NG' '..\..\Shell\Windows Shell' ). @@ -653,7 +654,7 @@ OS.COM.InterfaceImplementation OS.COM.InterfaceImplementation subclass: #'WebView2.WebView2EnvironmentOptions' - instanceVariableNames: 'additionalBrowserArguments flags locale browserVersion trackingPreventionLevel channelSearchKind releaseChannels scrollBarStyle customSchemeRegistrations' + instanceVariableNames: 'additionalBrowserArguments flags locale browserVersion _unused5 channelSearchKind releaseChannels scrollBarStyle customSchemeRegistrations' classVariableNames: '' imports: #() classInstanceVariableNames: '' @@ -661,7 +662,8 @@ OS.COM.InterfaceImplementation 'AllowSSOMask' -> 16r1. 'BrowseExtensionsEnabledMask' -> 16r8. 'CustomCrashReportingMask' -> 16r4. - 'ExclusiveUserDataFolderAccessMask' -> 16r2 + 'ExclusiveUserDataFolderAccessMask' -> 16r2. + 'TrackingPreventionDisabledMask' -> 16r10 }! OS.COM.InterfaceImplementation diff --git a/Core/Object Arts/Dolphin/MVP/Base/UI.View.cls b/Core/Object Arts/Dolphin/MVP/Base/UI.View.cls index c1637cb96..8de4d3f37 100644 --- a/Core/Object Arts/Dolphin/MVP/Base/UI.View.cls +++ b/Core/Object Arts/Dolphin/MVP/Base/UI.View.cls @@ -964,7 +964,7 @@ disableRedraw disconnectFromModel "Private - Remove all event registrations lodged with the current model." - model notNil ifTrue: [model removeEventsTriggeredFor: self]! + model ifNotNil: [model removeEventsTriggeredFor: self]! dispatchMessage: message wParam: wParam lParam: lParam "Private - Dispatch a message to the first level View message handler, if one @@ -2459,7 +2459,7 @@ onDestroyed is now probably invalid. Inform the presenter that its view is now gone. Answer nil to accept the default processing." - self isStateRestoring ifFalse: [self model removeEventsTriggeredFor: self]. + self isStateRestoring ifFalse: [self disconnectFromModel]. self parentView onSubViewRemoved: self. self presenter onViewClosed. ^nil! diff --git a/Core/Object Arts/Samples/ActiveX/Web Browser/OS.COM.Examples.WebBrowserShell.cls b/Core/Object Arts/Samples/ActiveX/Web Browser/OS.COM.Examples.WebBrowserShell.cls index 210423d28..9006e4d62 100644 --- a/Core/Object Arts/Samples/ActiveX/Web Browser/OS.COM.Examples.WebBrowserShell.cls +++ b/Core/Object Arts/Samples/ActiveX/Web Browser/OS.COM.Examples.WebBrowserShell.cls @@ -65,6 +65,16 @@ browserExtensionsMenu "" ! +browserScrollBarStyle: anInteger + "Note that if we named this #scrollBarStyle:, if the browser view itself has focus then because WebView2View implements #scrollBarStyle:, then the command routing framework would judge the browser view to be a valid command target meaning that our command query would not be run, and the radio buttons would not be set. We could alter the command routing to avoid this, but it is simpler just to use a different selector for the command." + + + self browserView scrollBarStyle: anInteger! + +browserTrackingPreventionLevel: anInteger + + self browserView trackingPreventionLevel: anInteger! + browserView ^browser view! @@ -93,7 +103,7 @@ buildScrollBarStyleMenu: aMenu aMenu clear. #('&Default' '&Fluent Overlay') keysAndValuesDo: [:eachKey :eachValue | - aMenu addItem: ((CommandMenuItem command: (Message selector: #scrollBarStyle: argument: eachKey - 1) + aMenu addItem: ((CommandMenuItem command: (Message selector: #browserScrollBarStyle: argument: eachKey - 1) description: eachValue) isRadioButtonStyle: true; yourself)]! @@ -130,14 +140,10 @@ buildSettingsPopup: aMenu addItem: ((Menu description: 'Preferred &Color Scheme') name: #colorSchemeMenu; yourself); - addCommand: #chooseBackcolor description: 'Background &Color'. - "With the fluent overlay scrollbar style the browser seems to stop rendering (is blank)." - false - ifTrue: - [aMenu addItem: ((Menu description: '&ScrollBar Style') - name: #scrollBarStyleMenu; - yourself)]. - aMenu + addCommand: #chooseBackcolor description: 'Background &Color'; + addItem: ((Menu description: '&ScrollBar Style') + name: #scrollBarStyleMenu; + yourself); addSeparator; addCommand: #toggleGeneralAutofill description: 'General &Autofill'; addCommand: #togglePasswordAutosave description: '&Password Autosave'; @@ -152,10 +158,11 @@ buildSettingsPopup: aMenu buildTrackingPreventionLevelMenu: aMenu aMenu clear. +Sound bell. #('&None' '&Basic' 'Ba&lanced' '&Strict') keysAndValuesDo: [:eachKey :eachValue | aMenu addItem: ((CommandMenuItem - command: (Message selector: #trackingPreventionLevel: argument: eachKey - 1) + command: (Message selector: #browserTrackingPreventionLevel: argument: eachKey - 1) description: eachValue) isRadioButtonStyle: true; yourself)]! @@ -195,7 +202,14 @@ createSchematicWiring send: #onColorSchemeChanged to: self. "Some events we need to hook to stay in sync" + browser view + when: #environmentAvailable + send: #onWebViewEnvironmentAvailable + to: self. browser + when: #controllerAvailable + send: #onWebViewControllerAvailable + to: self; when: #webviewAvailable send: #onWebViewAvailable to: self; @@ -540,7 +554,17 @@ onWebViewAvailable "Display the Edge browser version in the status bar field on the right" (self view viewNamed: 'browserVersion') model: self browserView webviewEnvironment asValue. self onZoomFactorChanged. - profileModel value: self browserView profile! + "profileModel value: self browserView profile"! + +onWebViewControllerAvailable + "Private - Not really a view event, but send through the trace logic anyway" + + self onWebView2Event: self browserView webviewController name: #controllerAvailable:! + +onWebViewEnvironmentAvailable + "Private - Not really a view event, but send through the trace logic anyway" + + self onWebView2Event: self browserView webviewEnvironment name: #environmentAvailable:! onZoomFactorChanged zoomModel value: '<1p>%%' << (self browserView zoomFactor * 100) rounded! @@ -726,6 +750,7 @@ queryToggleZoomControl: aCommandQuery isChecked: browser view isZoomControlEnabled! queryTrackingPreventionLevel: aCommandQuery +Sound bell. aCommandQuery beEnabled; isChecked: self browserView trackingPreventionLevel = aCommandQuery command argument! @@ -742,10 +767,6 @@ saveAs << (#('COREWEBVIEW2_SAVE_AS_UI_RESULT_SUCCESS' 'COREWEBVIEW2_SAVE_AS_UI_RESULT_INVALID_PATH' 'COREWEBVIEW2_SAVE_AS_UI_RESULT_FILE_ALREADY_EXISTS' 'COREWEBVIEW2_SAVE_AS_UI_RESULT_KIND_NOT_SUPPORTED' 'COREWEBVIEW2_SAVE_AS_UI_RESULT_CANCELLED') lookup: saveAsUiResult + 1)]]! -scrollBarStyle: anInteger - - self browserView scrollBarStyle: anInteger! - scrollBarStyleMenu @@ -907,10 +928,6 @@ toggleZoomControl "Most settings do not take effect until after navigation, so refresh" self refresh! -trackingPreventionLevel: anInteger - - self browserView trackingPreventionLevel: anInteger! - trackingPreventionLevelMenu @@ -934,6 +951,8 @@ zoomReset !OS.COM.Examples.WebBrowserShell categoriesForMethods! areBrowserExtensionsEnabled!commands-queries!public! ! browserExtensionsMenu!commands-menus!public! ! +browserScrollBarStyle:!commands-actions!public! ! +browserTrackingPreventionLevel:!commands-actions!public! ! browserView!accessing!public! ! buildBrowserExtensionsMenu:!commands-menus!private! ! buildColorSchemeMenu:!commands-menus!private! ! @@ -979,6 +998,8 @@ onWebMessageReceived:!event handling!private! ! onWebResourceRequested:!event handling!private! ! onWebView2Event:name:!event handling!private! ! onWebViewAvailable!event handling!private! ! +onWebViewControllerAvailable!private! ! +onWebViewEnvironmentAvailable!private! ! onZoomFactorChanged!event handling!private! ! openTaskManagerWindow!commands-actions!public! ! openUrl:!operations!public! ! @@ -1016,7 +1037,6 @@ queryToggleZoomControl:!commands-queries!private! ! queryTrackingPreventionLevel:!commands-queries!private! ! refresh!commands-actions!public! ! saveAs!commands-actions!public! ! -scrollBarStyle:!commands-actions!public! ! scrollBarStyleMenu!commands-menus!public! ! settingsMenu!commands-actions!public! ! toggleAllowContextMenu!commands-actions!public! ! @@ -1041,7 +1061,6 @@ toggleSuppressSaveAsDialog!commands-actions!public! ! toggleSwipeNavigation!commands-actions!public! ! toggleWebMessaging!commands-actions!public! ! toggleZoomControl!commands-actions!public! ! -trackingPreventionLevel:!commands-actions!public! ! trackingPreventionLevelMenu!commands-menus!public! ! updateColorScheme!private!updating! ! webview2!accessing!private! ! @@ -1083,7 +1102,7 @@ resource_Default_view Tools.ViewComposer openOn: (UI.ResourceIdentifier class: self selector: #resource_Default_view) " - ^#(#'!!STL' 6 2118 11 #{UI.STBViewProxy} #{UI.ShellView} 38 #{Core.Array} 27 nil nil 8 #(13565952 65536) 32 nil 518 #{Graphics.ThemeColor} #browserBar nil 517 nil nil nil 32 1798 #{UI.BorderLayout} 1 1 18 #{UI.Toolbar} 50 28 nil 32 50 2 8 1140853580 131137 160 nil 112 nil 517 nil nil nil 160 112 nil 518 #{Kernel.STBIdentityDictionaryProxy} #{Core.IdentityDictionary} 50 2 18 #{UI.Toolbar} 50 28 nil 160 50 2 8 1140857676 131137 272 nil 112 nil 517 nil nil nil 272 nil nil 226 #{Core.IdentityDictionary} 8 #() nil nil 98 #browserForeground nil 50 10 1862 2 #{UI.ToolbarTextButton} 272 161 1350 4 #{UI.CommandDescription} #goBack 8 'Back' 1 1 nil nil 98 #browserHistoryArrow nil 8 $\xE830 402 272 161 434 #goForward 8 'Forward' 1 1 nil nil 480 nil 8 $\xEA47 402 272 161 434 #refresh 8 'Re&fresh (Ctrl+R)' 9381 1 nil nil nil nil 8 $\xE72C 402 272 161 434 #home 8 'Home page' 1 1 nil nil nil nil 8 $\xE80F 402 272 161 434 #print 8 'Print ...' 1 1 nil nil nil nil 8 $\xE749 402 272 161 434 #saveAs 8 'Save As ...' 1 1 nil nil nil nil 8 $\xE78C 402 272 161 434 #help 8 'Information' 1 1 nil nil nil nil 8 $\xE946 402 272 161 434 #openTaskManagerWindow 8 'Open &Task Manager' 1 1 nil nil nil nil 8 $\xE9F5 402 272 433 434 #settingsMenu 8 'Settings' 1 1 nil nil nil nil 8 $\xE713 582 1 #{UI.ToolbarSeparator} 272 1 nil nil 13 5 nil nil nil nil 262 #{Core.MessageSequence} 50 1 774 #{Core.MessageSend} #createWindow: 50 1 1030 #{UI.CreateWindow} 262 #{OS.RECTL} 8 #[6 0 0 0 7 0 0 0 46 1 0 0 40 0 0 0] 193 50 2 8 1140857676 131137 8 '' 272 3 8 #() 518 #{Graphics.Point} 193 193 nil 31 8 'browserTools' nil nil nil nil 8 #() nil 518 #{Graphics.Rectangle} 1298 13 15 1298 9 1 9 2369 #mediumSmallIcons nil nil 130 1 1 nil nil nil 272 18 #{UI.ContainerView} 50 15 nil 160 50 2 8 1140850688 131073 1440 nil 112 nil 5 nil 1350 1 #{Graphics.Font} nil true 262 #{OS.LOGFONTW} 8 #[241 255 255 255 0 0 0 0 0 0 0 0 0 0 0 0 144 1 0 0 0 0 0 0 3 2 1 34 83 0 101 0 103 0 111 0 101 0 32 0 85 0 73 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 193 nil nil 1440 130 9 1 nil nil 18 #{UI.PushButton} 50 20 nil 1440 50 2 8 1140924416 1 1600 nil 112 1298 101 51 5 nil nil nil 1600 nil nil 434 #navigate 8 '&Go' 1 1 nil nil true nil nil nil 1058 50 2 1106 #createWindow: 50 1 1154 1186 8 #[60 3 0 0 1 0 0 0 110 3 0 0 26 0 0 0] 193 1632 8 '&Go' 1600 1106 #isEnabled: 8 #(false) 1600 3 8 #() 1298 193 193 nil 29 nil 18 #{UI.TextEdit} 50 20 nil 1440 50 2 8 1149304960 1 1904 nil 98 #browserBackground nil 5 2886 4 #{UI.Menu} nil true 50 2 1094 2 #{UI.CommandMenuItem} 1 434 #navigate 8 '&Go' 1 1 nil nil nil 2034 2097153 434 #accept 8 '&Accept' 1 1 nil nil nil 8 '' nil 1 nil nil nil nil nil nil nil 1904 368 nil 518 #{UI.NullConverter} nil nil 1 #focusLost nil nil nil 1058 50 3 1106 #createWindow: 50 1 1154 1186 8 #[0 0 0 0 1 0 0 0 56 3 0 0 26 0 0 0] 193 1936 nil 1904 1106 #contextMenu: 50 1 2000 1904 1106 #setMarginWidths: 50 1 8 #(4 4) 1904 3 8 #() 1298 193 193 nil 45 226 #{Core.IdentityDictionary} 50 4 1904 8 'address' 1600 8 'go' 1362 1298 1 3 1298 1 15 1058 50 1 1106 #createWindow: 50 1 1154 1186 8 #[46 1 0 0 7 0 0 0 156 4 0 0 40 0 0 0] 193 1472 8 '' 1440 3 50 2 1904 1600 1298 193 193 nil 27 1058 50 1 1106 #createWindow: 50 1 1154 1186 8 #[0 0 0 0 0 0 0 0 160 4 0 0 40 0 0 0] 193 192 8 '' 160 3 50 2 272 1440 1298 193 193 nil 31 18 #{UI.StatusBar} 50 21 nil 32 50 2 8 1140850956 1 2848 nil 6 #{Graphics.Color} #default nil 5 nil 1862 1 #{Graphics.UxThemeFont} nil true nil 193 nil 1609 nil nil 2848 368 nil 226 #{Core.IdentityDictionary} 50 8 1862 1 #{UI.StatusBarItem} 8193 301 2848 nil 518 #{Core.Message} #displayString 8 #() 3042 #iconImageIndex 8 #() nil 8 'browserVersion' 3010 8193 251 2848 nil 3042 #displayString 8 #() 3042 #iconImageIndex 3168 nil 8 'profile' 3010 8193 -1 2848 nil 3042 #displayString 3104 nil nil 8 'status' 3010 8193 101 2848 nil 3042 #displayString 8 #() nil nil 8 'zoom' nil nil nil 50 4 3216 3136 3264 3024 1094 1 #{UI.StatusBarNullItem} 8705 1 2848 nil nil 1058 50 1 1106 #createWindow: 50 1 1154 1186 8 #[0 0 0 0 254 2 0 0 160 4 0 0 19 3 0 0] 193 2880 8 '' 2848 3 8 #() 1298 193 193 nil 29 nil nil 18 #{UI.WebView2View} 50 26 nil 32 50 2 8 1140850688 1 3536 1094 2 #{UI.ValueHolder} nil false 6 #{Kernel.EqualitySearchPolicy} nil 1968 nil 5 nil nil nil 3536 nil nil 2162 nil nil 262145 nil nil nil nil nil 2374 2 #{WebView2.WebView2EnvironmentOptions} nil 15 nil 8 '110.0.1549.0' 5 1 3 1 8 #() nil nil nil nil 1058 50 1 1106 #createWindow: 50 1 1154 1186 8 #[0 0 0 0 40 0 0 0 160 4 0 0 254 2 0 0] 193 3568 8 '' 3536 3 352 1298 193 193 nil 29 226 #{Core.IdentityDictionary} 50 6 3536 8 'browser' 2848 8 'progress' 160 8 'toolbar' nil nil nil nil nil 1 nil nil nil nil nil nil 193 1058 50 1 1106 #createWindow: 50 1 1154 1362 1298 6143 21 1298 8543 1673 193 80 8 'WebView2 Browser' 32 1 50 3 2848 160 3536 1298 193 193 nil 27)! + ^#(#'!!STL' 6 2118 11 #{UI.STBViewProxy} #{UI.ShellView} 38 #{Core.Array} 27 nil nil 8 #(13565952 65536) 32 nil 518 #{Graphics.ThemeColor} #browserBar nil 517 nil nil nil 32 1798 #{UI.BorderLayout} 1 1 18 #{UI.Toolbar} 50 28 nil 32 50 2 8 1140853580 131137 160 nil 112 nil 517 nil nil nil 160 112 nil 518 #{Kernel.STBIdentityDictionaryProxy} #{Core.IdentityDictionary} 50 2 18 #{UI.Toolbar} 50 28 nil 160 50 2 8 1140857676 131137 272 nil 112 nil 517 nil nil nil 272 nil nil 226 #{Core.IdentityDictionary} 8 #() nil nil 98 #browserForeground nil 50 10 1862 2 #{UI.ToolbarTextButton} 272 161 1350 4 #{UI.CommandDescription} #goBack 8 'Back' 1 1 nil nil 98 #browserHistoryArrow nil 8 $\xE830 402 272 161 434 #goForward 8 'Forward' 1 1 nil nil 480 nil 8 $\xEA47 402 272 161 434 #refresh 8 'Re&fresh (Ctrl+R)' 9381 1 nil nil nil nil 8 $\xE72C 402 272 161 434 #home 8 'Home page' 1 1 nil nil nil nil 8 $\xE80F 402 272 161 434 #print 8 'Print ...' 1 1 nil nil nil nil 8 $\xE749 402 272 161 434 #saveAs 8 'Save As ...' 1 1 nil nil nil nil 8 $\xE78C 402 272 161 434 #help 8 'Information' 1 1 nil nil nil nil 8 $\xE946 402 272 161 434 #openTaskManagerWindow 8 'Open &Task Manager' 1 1 nil nil nil nil 8 $\xE9F5 402 272 433 434 #settingsMenu 8 'Settings' 1 1 nil nil nil nil 8 $\xE713 582 1 #{UI.ToolbarSeparator} 272 1 nil nil 13 5 nil nil nil nil 262 #{Core.MessageSequence} 50 1 774 #{Core.MessageSend} #createWindow: 50 1 1030 #{UI.CreateWindow} 262 #{OS.RECTL} 8 #[6 0 0 0 7 0 0 0 46 1 0 0 40 0 0 0] 193 50 2 8 1140857676 131137 8 '' 272 3 8 #() 518 #{Graphics.Point} 193 193 nil 31 8 'browserTools' nil nil nil nil 8 #() nil 518 #{Graphics.Rectangle} 1298 13 15 1298 9 1 9 2369 #mediumSmallIcons nil nil 130 1 1 nil nil nil 272 18 #{UI.ContainerView} 50 15 nil 160 50 2 8 1140850688 131073 1440 nil 112 nil 5 nil 1350 1 #{Graphics.Font} nil true 262 #{OS.LOGFONTW} 8 #[241 255 255 255 0 0 0 0 0 0 0 0 0 0 0 0 144 1 0 0 0 0 0 0 3 2 1 34 83 0 101 0 103 0 111 0 101 0 32 0 85 0 73 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 193 nil nil 1440 130 9 1 nil nil 18 #{UI.PushButton} 50 20 nil 1440 50 2 8 1140924416 1 1600 nil 112 1298 101 51 5 nil nil nil 1600 nil nil 434 #navigate 8 '&Go' 1 1 nil nil true nil nil nil 1058 50 2 1106 #createWindow: 50 1 1154 1186 8 #[60 3 0 0 1 0 0 0 110 3 0 0 26 0 0 0] 193 1632 8 '&Go' 1600 1106 #isEnabled: 8 #(false) 1600 3 8 #() 1298 193 193 nil 29 nil 18 #{UI.TextEdit} 50 20 nil 1440 50 2 8 1149304960 1 1904 nil 98 #browserBackground nil 5 2886 4 #{UI.Menu} nil true 50 2 1094 2 #{UI.CommandMenuItem} 1 434 #navigate 8 '&Go' 1 1 nil nil nil 2034 2097153 434 #accept 8 '&Accept' 1 1 nil nil nil 8 '' nil 1 nil nil nil nil nil nil nil 1904 368 nil 518 #{UI.NullConverter} nil nil 1 #focusLost nil nil nil 1058 50 3 1106 #createWindow: 50 1 1154 1186 8 #[0 0 0 0 1 0 0 0 56 3 0 0 26 0 0 0] 193 1936 nil 1904 1106 #contextMenu: 50 1 2000 1904 1106 #setMarginWidths: 50 1 8 #(4 4) 1904 3 8 #() 1298 193 193 nil 45 226 #{Core.IdentityDictionary} 50 4 1904 8 'address' 1600 8 'go' 1362 1298 1 3 1298 1 15 1058 50 1 1106 #createWindow: 50 1 1154 1186 8 #[46 1 0 0 7 0 0 0 156 4 0 0 40 0 0 0] 193 1472 8 '' 1440 3 50 2 1904 1600 1298 193 193 nil 27 1058 50 1 1106 #createWindow: 50 1 1154 1186 8 #[0 0 0 0 0 0 0 0 160 4 0 0 40 0 0 0] 193 192 8 '' 160 3 50 2 272 1440 1298 193 193 nil 31 18 #{UI.StatusBar} 50 21 nil 32 50 2 8 1140850956 1 2848 nil 6 #{Graphics.Color} #default nil 5 nil 1862 1 #{Graphics.UxThemeFont} nil true nil 193 nil 1609 nil nil 2848 368 nil 226 #{Core.IdentityDictionary} 50 8 1862 1 #{UI.StatusBarItem} 8193 301 2848 nil 518 #{Core.Message} #displayString 8 #() 3042 #iconImageIndex 8 #() nil 8 'browserVersion' 3010 8193 251 2848 nil 3042 #displayString 8 #() 3042 #iconImageIndex 3168 nil 8 'profile' 3010 8193 -1 2848 nil 3042 #displayString 3104 nil nil 8 'status' 3010 8193 101 2848 nil 3042 #displayString 8 #() nil nil 8 'zoom' nil nil nil 50 4 3216 3136 3264 3024 1094 1 #{UI.StatusBarNullItem} 8705 1 2848 nil nil 1058 50 1 1106 #createWindow: 50 1 1154 1186 8 #[0 0 0 0 254 2 0 0 160 4 0 0 19 3 0 0] 193 2880 8 '' 2848 3 8 #() 1298 193 193 nil 29 nil nil 18 #{UI.WebView2View} 50 26 nil 32 50 2 8 1140850688 1 3536 1094 2 #{UI.ValueHolder} nil false 6 #{Kernel.EqualitySearchPolicy} nil 1968 nil 5 nil nil nil 3536 nil nil 2162 nil nil 262145 nil nil nil nil nil 2374 3 #{WebView2.WebView2EnvironmentOptions} nil 7 nil 8 '115.0.1901.177' nil 1 3 1 352 nil nil nil nil 1058 50 1 1106 #createWindow: 50 1 1154 1186 8 #[0 0 0 0 40 0 0 0 160 4 0 0 254 2 0 0] 193 3568 8 '' 3536 3 352 1298 193 193 nil 29 226 #{Core.IdentityDictionary} 50 6 3536 8 'browser' 2848 8 'progress' 160 8 'toolbar' nil nil nil nil nil 1 nil nil nil nil nil nil 193 1058 50 1 1106 #createWindow: 50 1 1154 1362 1298 6143 21 1298 8543 1673 193 80 8 'WebView2 Browser' 32 1 50 3 2848 160 3536 1298 193 193 nil 27)! shutdownOnExit "Answer whether a runtime session should be shutdown when an instance of the receiver is closed."