webviewgadget: get console message with WebMessageReceived

Just starting out? Need help? Post your questions and find answers here.
zikitrake
Addict
Addict
Posts: 868
Joined: Thu Mar 25, 2004 2:15 pm
Location: Spain

webviewgadget: get console message with WebMessageReceived

Post by zikitrake »

(Solved :D )
Following (at least trying :D) @Infratec's steps, I'm trying to read console messages from a viewgadget.

When I finish loading a web, I add a JS function with the function MutationObserver() with the procedure MutationObserverSet(webView)

Then I activate an eventHandler to capture the console output messages from the WebViewGadget using the procedure
WebViewSetWebMessageReceivedEventHandler()

Code: Select all

;--------------------------------------
; https://learn.microsoft.com/en-us/microsoft-edge/webview2/concepts/navigation-events

CompilerIf Defined(IID_IUnknown, #PB_Label) = #False
  DataSection
    IID_IUnknown:
    Data.l $00000000
    Data.w $0000, $0000
    Data.b $C0, $00, $00, $00, $00, $00, $00, $46
  EndDataSection
CompilerEndIf

;- STRUCTURES
Structure IUnknownBase_Structure
  *pVtbl
  *pQueryInterface
  *pAddRef
  *pRelease
  *pInvoke
  
  lRefCount.l
  Token.q
  hEvent.i
  
EndStructure
Structure ICoreWebView2ExecuteScriptCompletedHandler_Structure Extends IUnknownBase_Structure
  Result$
EndStructure
Structure ICoreWebView2NavigationCompletedEventHandler_Structure Extends IUnknownBase_Structure
  *pFunction
EndStructure
Structure ICoreWebView2ContentLoadingEventHandler_Structure Extends IUnknownBase_Structure
  *pFunction
EndStructure
Structure ICoreWebView2WebMessageReceivedEventHandler_Structure Extends IUnknownBase_Structure
  *pFunction
EndStructure

;--------------------------------------

;- NavigationCompleted
Global NewMap MapNavigationCompletedEventHandler.ICoreWebView2NavigationCompletedEventHandler_Structure()
Procedure.l ICoreWebView2NavigationCompletedEventHandler_QueryInterface(*this.IUnknownBase_Structure, *riid.IID, *ppvObject.Integer)
  If *ppvObject And *riid
    If CompareMemory(*riid, ?IID_IUnknown, SizeOf(IID))
      *this\lRefCount + 1
      *ppvObject\i = *this
    Else
      *ppvObject\i = 0
      ProcedureReturn #E_NOINTERFACE
    EndIf
  Else
    ProcedureReturn #E_POINTER
  EndIf
  ProcedureReturn #S_OK
EndProcedure
Procedure.l ICoreWebView2NavigationCompletedEventHandler_AddRef(*this.IUnknownBase_Structure)
  *this\lRefCount + 1
  ProcedureReturn *this\lRefCount
EndProcedure
Procedure.l ICoreWebView2NavigationCompletedEventHandler_Release(*this.IUnknownBase_Structure)
  *this\lRefCount - 1
  ProcedureReturn *this\lRefCount
EndProcedure
Procedure.l ICoreWebView2NavigationCompletedEventHandler_Invoke(*this.ICoreWebView2NavigationCompletedEventHandler_Structure, *sender.ICoreWebView2, *args.ICoreWebView2NavigationCompletedEventArgs)
  Protected isSuccess, *uri, uri.s
  
  If *args And *args\get_IsSuccess(@isSuccess) = #S_OK And isSuccess
    If *sender
      If *sender\get_Source(@*uri) = #S_OK
        If *uri
          uri = PeekS(*uri)
          If *this\pFunction
            CallFunctionFast(*this\pFunction, @uri)
          EndIf
          CoTaskMemFree_(*uri)
        EndIf
        ;-zikichange
        ;*sender\add_WebMessageReceived = *sender\
        
      EndIf
    EndIf
  EndIf
  
  ProcedureReturn #S_OK
  
EndProcedure
Procedure.i WebViewSetNavigateCompleteEventHandler(WebView, *EventHandler)
  
  Protected Result.i
  Protected Controller.ICoreWebView2Controller, Core.ICoreWebView2
  Protected *NavigationCompletedEventHandler.ICoreWebView2NavigationCompletedEventHandler_Structure
  Protected Token.q
  
  
  If GadgetType(WebView) = #PB_GadgetType_WebView And *EventHandler <> #Null
    
    Controller = GetGadgetAttribute(WebView, #PB_WebView_ICoreController)
    If Controller
      
      If Controller\get_CoreWebView2(@Core) = #S_OK And Core <> #Null
        
        If FindMapElement(MapNavigationCompletedEventHandler(), Str(WebView))
          If MapNavigationCompletedEventHandler()\Token
            Core\remove_NavigationCompleted(MapNavigationCompletedEventHandler()\Token)
          EndIf
          DeleteMapElement(MapNavigationCompletedEventHandler())
        EndIf
        
        *NavigationCompletedEventHandler = AddMapElement(MapNavigationCompletedEventHandler(), Str(WebView))
        If *NavigationCompletedEventHandler
          
          *NavigationCompletedEventHandler\pVtbl = *NavigationCompletedEventHandler + OffsetOf(IUnknownBase_Structure\pQueryInterface)
          *NavigationCompletedEventHandler\pQueryInterface = @ICoreWebView2NavigationCompletedEventHandler_QueryInterface()
          *NavigationCompletedEventHandler\pAddRef         = @ICoreWebView2NavigationCompletedEventHandler_AddRef()
          *NavigationCompletedEventHandler\pRelease        = @ICoreWebView2NavigationCompletedEventHandler_Release()
          *NavigationCompletedEventHandler\pInvoke         = @ICoreWebView2NavigationCompletedEventHandler_Invoke()
          
          *NavigationCompletedEventHandler\pFunction = *EventHandler
          
          If Core\add_NavigationCompleted(*NavigationCompletedEventHandler, @*NavigationCompletedEventHandler\Token) = #S_OK
            Result = #True
          Else
            DeleteMapElement(MapNavigationCompletedEventHandler())
          EndIf
          
        EndIf
        
      EndIf
      
      Core\Release()
    EndIf
  EndIf
  
  ProcedureReturn Result
  
EndProcedure
;- ContentLoading
Global NewMap MapContentLoadingEventHandler.ICoreWebView2ContentLoadingEventHandler_Structure()
Procedure.l ICoreWebView2ContentLoadingEventHandler_QueryInterface(*this.IUnknownBase_Structure, *riid.IID, *ppvObject.Integer)
  If *ppvObject And *riid
    If CompareMemory(*riid, ?IID_IUnknown, SizeOf(IID))
      *this\lRefCount + 1
      *ppvObject\i = *this
    Else
      *ppvObject\i = 0
      ProcedureReturn #E_NOINTERFACE
    EndIf
  Else
    ProcedureReturn #E_POINTER
  EndIf
  ProcedureReturn #S_OK
EndProcedure
Procedure.l ICoreWebView2ContentLoadingEventHandler_AddRef(*this.IUnknownBase_Structure)
  *this\lRefCount + 1
  ProcedureReturn *this\lRefCount
EndProcedure
Procedure.l ICoreWebView2ContentLoadingEventHandler_Release(*this.IUnknownBase_Structure)
  *this\lRefCount - 1
  ProcedureReturn *this\lRefCount
EndProcedure
Procedure.l ICoreWebView2ContentLoadingEventHandler_Invoke(*this.ICoreWebView2ContentLoadingEventHandler_Structure, *sender.ICoreWebView2, *args.ICoreWebView2ContentLoadingEventArgs)
  
  Protected isSuccess, *uri, uri.s
  
  ;If *args And *args\get_IsSuccess(@isSuccess) = #S_OK And isSuccess
  If *sender
    If *sender\get_Source(@*uri) = #S_OK
      If *uri
        uri = PeekS(*uri)
        If *this\pFunction
          CallFunctionFast(*this\pFunction, @uri)
        EndIf
        CoTaskMemFree_(*uri)
      EndIf
    EndIf
  EndIf
  ;EndIf
  
  ProcedureReturn #S_OK
EndProcedure
Procedure.i WebViewSetContentLoadingEventHandler(WebView, *EventHandler)
  
  Protected Result.i
  Protected Controller.ICoreWebView2Controller, Core.ICoreWebView2
  Protected *ContentLoadingEventHandler.ICoreWebView2ContentLoadingEventHandler_Structure
  Protected Token.q
  
  
  If GadgetType(WebView) = #PB_GadgetType_WebView And *EventHandler <> #Null
    
    Controller = GetGadgetAttribute(WebView, #PB_WebView_ICoreController)
    If Controller
      
      If Controller\get_CoreWebView2(@Core) = #S_OK 
        If Core
          
          If FindMapElement(MapContentLoadingEventHandler(), Str(WebView))
            If MapContentLoadingEventHandler()\Token
              Core\remove_ContentLoading(MapContentLoadingEventHandler()\Token)
            EndIf
            DeleteMapElement(MapContentLoadingEventHandler())
          EndIf
          
          *ContentLoadingEventHandler = AddMapElement(MapContentLoadingEventHandler(), Str(WebView))
          
          
          If *ContentLoadingEventHandler
            
            *ContentLoadingEventHandler\pVtbl = *ContentLoadingEventHandler + OffsetOf(IUnknownBase_Structure\pQueryInterface)
            *ContentLoadingEventHandler\pQueryInterface = @ICoreWebView2ContentLoadingEventHandler_QueryInterface()
            *ContentLoadingEventHandler\pAddRef         = @ICoreWebView2ContentLoadingEventHandler_AddRef()
            *ContentLoadingEventHandler\pRelease        = @ICoreWebView2ContentLoadingEventHandler_Release()
            *ContentLoadingEventHandler\pInvoke         = @ICoreWebView2ContentLoadingEventHandler_Invoke()
            
            *ContentLoadingEventHandler\pFunction = *EventHandler
            
            If Core\add_ContentLoading(*ContentLoadingEventHandler, @*ContentLoadingEventHandler\Token) = #S_OK
              ;Debug *ContentLoadingEventHandler\Token
              Result = #True
            Else
              DeleteMapElement(MapContentLoadingEventHandler())
            EndIf
            
          EndIf
          
          Core\Release()
        EndIf
      EndIf
    EndIf
  EndIf
  
  ProcedureReturn Result
  
EndProcedure
;- WebMessageReceived
Global NewMap MapMessageReceivedEventHandler.ICoreWebView2WebMessageReceivedEventHandler_Structure()
Procedure.l ICoreWebView2WebMessageReceivedEventHandler_QueryInterface(*this.IUnknownBase_Structure, *riid.IID, *ppvObject.Integer)
  If *ppvObject And *riid
    If CompareMemory(*riid, ?IID_IUnknown, SizeOf(IID))
      *this\lRefCount + 1
      *ppvObject\i = *this
    Else
      *ppvObject\i = 0
      ProcedureReturn #E_NOINTERFACE
    EndIf
  Else
    ProcedureReturn #E_POINTER
  EndIf
  ProcedureReturn #S_OK
EndProcedure
Procedure.l ICoreWebView2WebMessageReceivedEventHandler_AddRef(*this.IUnknownBase_Structure)
  *this\lRefCount + 1
  ProcedureReturn *this\lRefCount
EndProcedure
Procedure.l ICoreWebView2WebMessageReceivedEventHandler_Release(*this.IUnknownBase_Structure)
  *this\lRefCount - 1
  ProcedureReturn *this\lRefCount
EndProcedure
Procedure.l ICoreWebView2WebMessageReceivedEventHandler_Invoke(*this.ICoreWebView2WebMessageReceivedEventHandler_Structure, *sender.ICoreWebView2, *args.ICoreWebView2WebMessageReceivedEventArgs)
  Protected message, *uri, uri.s
  Debug uri
  If *args And *args\TryGetWebMessageAsString(@message) = #S_OK And message    
    If PeekS(message) = "PBWEB content changed!"
      Debug PeekS(message)
      If *sender
        If *sender\get_Source(@*uri) = #S_OK
          If *uri
            uri = PeekS(*uri)
            If *this\pFunction
              CallFunctionFast(*this\pFunction, @uri)
            EndIf
            CoTaskMemFree_(*uri)
          EndIf
        EndIf
      EndIf
    EndIf
  EndIf
  
EndProcedure
Procedure.i WebViewSetWebMessageReceivedEventHandler(WebView, *EventHandler)
  
  Protected Result.i = #False
  Protected Controller.ICoreWebView2Controller, Core.ICoreWebView2
  Protected *WebMessageReceivedEventHandler.ICoreWebView2WebMessageReceivedEventHandler_Structure
  Protected Token.q
  
  
  If GadgetType(WebView) = #PB_GadgetType_WebView And *EventHandler <> #Null
    
    Controller = GetGadgetAttribute(WebView, #PB_WebView_ICoreController)
    If Controller
      
      If Controller\get_CoreWebView2(@Core) = #S_OK 
        If Core
          
          If FindMapElement(MapMessageReceivedEventHandler(), Str(WebView))
            If MapMessageReceivedEventHandler()\Token
              Core\remove_WebMessageReceived(MapMessageReceivedEventHandler()\Token)
            EndIf
            DeleteMapElement(MapMessageReceivedEventHandler())
          EndIf
          
          *WebMessageReceivedEventHandler = AddMapElement(MapMessageReceivedEventHandler(), Str(WebView))
          
          
          If *WebMessageReceivedEventHandler
            
            *WebMessageReceivedEventHandler\pVtbl = *WebMessageReceivedEventHandler + OffsetOf(IUnknownBase_Structure\pQueryInterface)
            *WebMessageReceivedEventHandler\pQueryInterface = @ICoreWebView2WebMessageReceivedEventHandler_QueryInterface()
            *WebMessageReceivedEventHandler\pAddRef         = @ICoreWebView2WebMessageReceivedEventHandler_AddRef()
            *WebMessageReceivedEventHandler\pRelease        = @ICoreWebView2WebMessageReceivedEventHandler_Release()
            *WebMessageReceivedEventHandler\pInvoke         = @ICoreWebView2WebMessageReceivedEventHandler_Invoke()
            
            *WebMessageReceivedEventHandler\pFunction = *EventHandler
            
            If Core\add_WebMessageReceived(*WebMessageReceivedEventHandler, @*WebMessageReceivedEventHandler\Token) = #S_OK
              ;Debug *WebMessageReceivedEventHandler\Token
              Result = #True
            Else
              DeleteMapElement(MapMessageReceivedEventHandler())
            EndIf
            
          EndIf
          
          Core\Release()
        EndIf
      EndIf
    EndIf
  EndIf
  
  ProcedureReturn Result
  
EndProcedure


;--------------------------------------
;- EXECUTESCRIPT WITH RESULTS RETURN
Procedure.l CoreWebView2ExecuteScriptCompletedHandler_Invoke(*this.ICoreWebView2ExecuteScriptCompletedHandler_Structure, errorCode.l, *returnObjectAsJson)
  Protected sResult.s, iCount
  
  ;Debug "CoreWebView2ExecuteScriptCompletedHandler_Invoke"
  
  If errorCode = #S_OK
    If *returnObjectAsJson
      *this\Result$ = PeekS(*returnObjectAsJson)
    EndIf
  EndIf
  
  If *this\hEvent
    SetEvent_(*this\hEvent)
  EndIf
  
  ProcedureReturn #S_OK
  
EndProcedure
Procedure.s WebViewExecuteScriptWithStringResult(WebView, JavaScript$)
  
  Protected Result$
  Protected Controller.ICoreWebView2Controller, Core.ICoreWebView2
  Protected *ExecuteScriptCompletedHandler.ICoreWebView2ExecuteScriptCompletedHandler_Structure
  Protected hEvent
  
  
  If GadgetType(WebView) = #PB_GadgetType_WebView
    
    Controller = GetGadgetAttribute(WebView, #PB_WebView_ICoreController)
    If Controller
      
      If Controller\get_CoreWebView2(@Core) = #S_OK And Core <> #Null
        
        hEvent = CreateEvent_(0, 0, 0, 0)
        If hEvent
          
          *ExecuteScriptCompletedHandler = AllocateMemory(SizeOf(ICoreWebView2ExecuteScriptCompletedHandler_Structure))
          If *ExecuteScriptCompletedHandler
            *ExecuteScriptCompletedHandler\pVtbl = *ExecuteScriptCompletedHandler + OffsetOf(IUnknownBase_Structure\pQueryInterface)
            *ExecuteScriptCompletedHandler\pQueryInterface = @ICoreWebView2NavigationCompletedEventHandler_QueryInterface()
            *ExecuteScriptCompletedHandler\pAddRef = @ICoreWebView2NavigationCompletedEventHandler_AddRef()
            *ExecuteScriptCompletedHandler\pRelease = @ICoreWebView2NavigationCompletedEventHandler_Release()
            *ExecuteScriptCompletedHandler\pInvoke = @CoreWebView2ExecuteScriptCompletedHandler_Invoke()
            
            *ExecuteScriptCompletedHandler\hEvent = hEvent
            
            If Core\ExecuteScript(JavaScript$, *ExecuteScriptCompletedHandler) = #S_OK
              
              While WaitForSingleObject_(hEvent, 0) <> #WAIT_OBJECT_0
                WindowEvent()
              Wend
              
              Result$ = *ExecuteScriptCompletedHandler\Result$
            EndIf

            FreeMemory(*ExecuteScriptCompletedHandler)
          EndIf
          
          CloseHandle_(hEvent)
        EndIf
        
        Core\Release()
      EndIf
    EndIf
    
  EndIf
  
  ProcedureReturn Result$
  
EndProcedure
;--------------------------------------

#WebView_BusyTimer = 666
Global WebView2_Busy.b, WebView2_NaviComplete.b, WebView2_MessageChanged.b

Procedure ContentLoading(uri.s)
  Debug "Loading: " + uri
  WebView2_Busy = #True
EndProcedure
Procedure NaviComplete(uri.s)
  Debug "Loaded: " + uri
  WebView2_Busy = #False
  WebView2_NaviComplete = #True
EndProcedure

Procedure ContentMessageChanged(uri.s)
  Debug "message change: " + uri
  WebView2_MessageChanged = #True
EndProcedure

Procedure MutationObserverSet(webView)
  Protected javascript.s
  javascript = "const observer = new MutationObserver((mutationsList) => {"
  javascript + "	for (let mutation of mutationsList) {"
  javascript + "		if (mutation.type === 'childList') {"
  javascript + "			console.log('A child node has been added or removed.');"
  javascript + "		} else if (mutation.type === 'attributes') {"
  javascript + "			console.log('The ' + mutation.attributeName + ' attribute was modified.');"
  javascript + "		}"
  javascript + "	}"
  javascript + "});"
  javascript + "observer.observe(document.body, { attributes: true, childList: true, subtree: true });"  
  
    
  javascript = "var observer = new MutationObserver(function(mutations) {"
  javascript + "  window.chrome.webview.postMessage('PBWEB content changed!');"
  javascript + "  });"
  javascript + "observer.observe(document.body, { childList: true, subtree: true });"  
  
  
  Debug WebViewExecuteScriptWithStringResult(webView, javascript)
  
EndProcedure

;--------------------------------------
;- Creación de UI
OpenWindow(0, 0, 0, 1000, 900, "", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)

StringGadget(0, 0, 0, 400, 30, "https://www.w3schools.com/xml/tryit.asp?filename=tryxml_parsertest2")
ButtonGadget(1, 410, 0, 50, 30, "Go")
WebViewGadget(2, 0, 35, 1000, 900, #PB_WebView_Debug)


WebViewSetContentLoadingEventHandler(2, @ContentLoading())
WebViewSetNavigateCompleteEventHandler(2, @NaviComplete())

AddWindowTimer(0, #WebView_BusyTimer, 100)

;--------------------------------------
;- EVENT LOOP
Repeat 
  Event = WaitWindowEvent()
  If Event = #PB_Event_Timer
    If EventTimer() = #WebView_BusyTimer
      If WebView2_NaviComplete
        WebView2_NaviComplete = #False
        MutationObserverSet(2)
        WebViewSetWebMessageReceivedEventHandler(2, @ContentMessageChanged())
        Debug "complete!"
      EndIf
      
      If WebView2_Busy
        Debug "WebView is Busy"
      EndIf
      
      If WebView2_MessageChanged
        Debug "Message Changed!"
        WebView2_MessageChanged = #False
      EndIf
      
    EndIf
  EndIf
  If Event = #PB_Event_Gadget
    If EventGadget() = 1
      SetGadgetText(2, Trim(GetGadgetText(0)))
    EndIf
  EndIf
Until Event = #PB_Event_CloseWindow


But I'm 200% sure that I'm doing something wrong, because I never get any result back from the debug console.

I've been guided by the following c# code, but it seems I'm not skilled enough to convert it properly

Code: Select all

using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.WinForms;
using System;
using System.Windows.Forms;

public partial class Form1 : Form
{
    private WebView2 webView;

    public Form1()
    {
        InitializeComponent();
        InitializeWebView();
    }

    private async void InitializeWebView()
    {
        webView = new WebView2();
        webView.Dock = DockStyle.Fill;
        this.Controls.Add(webView);

        await webView.EnsureCoreWebView2Async(null);

        // Navigate to the desired webpage
        webView.CoreWebView2.Navigate("https://example.com");

        // Inject JavaScript to monitor changes in the DOM
        string script = @"
            const observer = new MutationObserver((mutationsList) => {
                for (let mutation of mutationsList) {
                    if (mutation.type === 'childList') {
                        console.log('A child node has been added or removed.');
                    } else if (mutation.type === 'attributes') {
                        console.log('The ' + mutation.attributeName + ' attribute was modified.');
                    }
                }
            });

            observer.observe(document.body, { attributes: true, childList: true, subtree: true });
        ";

        await webView.CoreWebView2.ExecuteScriptAsync(script);

        // Handle JavaScript console messages
        webView.CoreWebView2.WebMessageReceived += (sender, e) =>
        {
            // Output the console message to the C# console
            Console.WriteLine(e.TryGetWebMessageAsString());
        };
    }
}

A (poorly) explanatory video:
Image


Thank you very much for bothering to read me to the end! :)

Translated with DeepL.com (free version)
Last edited by zikitrake on Thu Feb 06, 2025 11:48 am, edited 2 times in total.
PB 6.21 beta, PureVision User
zikitrake
Addict
Addict
Posts: 868
Joined: Thu Mar 25, 2004 2:15 pm
Location: Spain

Re: webviewgadget: get console message with WebMessageReceived

Post by zikitrake »

Just solved with Chatgpt help :D

It works fine If I change the js to

Code: Select all

  javascript = "var observer = new MutationObserver(function(mutations) {"
  javascript + "  window.chrome.webview.postMessage('PBWEB content changed!');"
  javascript + "  });"
  javascript + "observer.observe(document.body, { childList: true, subtree: true });"  
  
PB 6.21 beta, PureVision User
Post Reply