Page 1 of 1

WebViewGadget WebMessageReceived callback

Posted: Fri Oct 25, 2024 9:29 am
by infratec
Windows only.

Save it as WebViewWebMessageReceivedCallback.pbi

Code: Select all

;
; https://www.purebasic.fr/english/viewtopic.php?p=629641
;

CompilerIf #PB_Compiler_IsMainFile
  EnableExplicit
CompilerEndIf

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

CompilerIf Not Defined(IUnknownBase_Structure, #PB_Structure)
  Structure IUnknownBase_Structure
    *pVtable
    lRefCount.l
    *pQueryInterface
    *pAddRef
    *pRelease
  EndStructure
CompilerEndIf


Prototype.i WebViewWebMessageReceived_Prototype(Message$)

Structure ICoreWebView2WebMessageReceivedEventHandler_Structure Extends IUnknownBase_Structure
  *pInvoke
  Token.q
  *pCallback.WebViewWebMessageReceived_Prototype
EndStructure


Global NewMap WebViewWebMessageReceivedEventHandlerMap.ICoreWebView2WebMessageReceivedEventHandler_Structure()




Procedure.l ICoreWebView2WebMessageReceivedEventHandler_QueryInterface(*this.ICoreWebView2WebMessageReceivedEventHandler_Structure, *riid.IID, *ppvObject.Integer)
  
  Protected Result.l
  
  
  Debug "WebView  WebMessageReceived QueryInterface"
  
  Result = #S_OK
  
  If *ppvObject And *riid
    If CompareMemory(*riid, ?IID_IUnknown, SizeOf(IID))
      *this\lRefCount + 1
      *ppvObject\i = *this
    Else
      *ppvObject\i = 0
      Result = #E_NOINTERFACE
    EndIf
  Else
    Result = #E_POINTER
  EndIf
  
  ProcedureReturn Result
  
EndProcedure




Procedure.l ICoreWebView2WebMessageReceivedEventHandler_AddRef(*this.ICoreWebView2WebMessageReceivedEventHandler_Structure)
  
  Debug "WebView WebMessageReceived AddRef"
  
  *this\lRefCount + 1
  
  ProcedureReturn *this\lRefCount
  
EndProcedure




Procedure.l ICoreWebView2WebMessageReceivedEventHandler_Release(*this.ICoreWebView2WebMessageReceivedEventHandler_Structure)
  
  Debug "WebView WebMessageReceived Release"
  
  *this\lRefCount - 1
  
  ProcedureReturn *this\lRefCount
  
EndProcedure




Procedure.l ICoreWebView2WebMessageReceivedEventHandler_Invoke(*this.ICoreWebView2WebMessageReceivedEventHandler_Structure, *sender.ICoreWebView2, *args.ICoreWebView2WebMessageReceivedEventArgs)
  
  Protected *ptr, Result.i, Message$
  
  
  Debug "WebView WebMessageReceived Invoke"
  
  Result = #S_OK
  *args\get_webMessageAsJson(@*ptr)
  If *ptr
    Message$ = PeekS(*ptr)
    Result = *this\pCallback(Message$)
    CoTaskMemFree_(*ptr)
  EndIf
  
  ProcedureReturn Result
  
EndProcedure




Procedure WebViewRemoveWebMessageReceivedCallback(WebView.i, Core.ICoreWebView2)
  
  If FindMapElement(WebViewWebMessageReceivedEventHandlerMap(), Str(WebView))
    
    If WebViewWebMessageReceivedEventHandlerMap()\Token
      If Core
        Core\remove_WebMessageReceived(WebViewWebMessageReceivedEventHandlerMap()\Token)
      EndIf
    EndIf
    
    DeleteMapElement(WebViewWebMessageReceivedEventHandlerMap())
  EndIf
  
EndProcedure




; CompilerIf Not Defined(ICoreWebView2Settings, #PB_Interface)
;   
;   DataSection
;     IID_ICoreWebView2Settings:
;     Data.l $E562E4F0
;     Data.w $D7FA, $43AC
;     Data.b $8D, $71, $C0, $51, $50, $49, $9F, $0
;   EndDataSection
;   
;   Interface ICoreWebView2Settings Extends IUnknown
;     get_IsScriptEnabled(IsScriptEnabled.i)
;     put_IsScriptEnabled(IsScriptEnabled.l)
;     get_IsWebMessageEnabled(IsWebMessageEnabled.i)
;     put_IsWebMessageEnabled(IsWebMessageEnabled.l)
;     get_AreDefaultScriptDialogsEnabled(AreDefaultScriptDialogsEnabled.i)
;     put_AreDefaultScriptDialogsEnabled(AreDefaultScriptDialogsEnabled.l)
;     get_IsStatusBarEnabled(IsStatusBarEnabled.i)
;     put_IsStatusBarEnabled(IsStatusBarEnabled.l)
;     get_AreDevToolsEnabled(AreDevToolsEnabled.i)
;     put_AreDevToolsEnabled(AreDevToolsEnabled.l)
;     get_AreDefaultContextMenusEnabled(enabled.i)
;     put_AreDefaultContextMenusEnabled(enabled.l)
;     get_AreHostObjectsAllowed(allowed.i)
;     put_AreHostObjectsAllowed(allowed.l)
;     get_IsZoomControlEnabled(enabled.i)
;     put_IsZoomControlEnabled(enabled.l)
;     get_IsBuiltInErrorPageEnabled(enabled.i)
;     put_IsBuiltInErrorPageEnabled(enabled.l)
;   EndInterface
; CompilerEndIf




Procedure.i WebViewAddWebMessageReceivedCallback(WebView, *Callback.WebViewWebMessageReceived_Prototype)
  
  Protected Result.i
  Protected Controller.ICoreWebView2Controller, Core.ICoreWebView2
  Protected *EventHandler.ICoreWebView2WebMessageReceivedEventHandler_Structure
  ;Protected Settings.ICoreWebview2Settings
  
  
  If GadgetType(WebView) = #PB_GadgetType_WebView And *Callback <> #Null
    
    Controller = GetGadgetAttribute(WebView, #PB_WebView_ICoreController)
    If Controller
      
      If Controller\get_CoreWebView2(@Core) = #S_OK And Core <> #Null
        
        WebViewRemoveWebMessageReceivedCallback(WebView, Core)
        
;         Core\get_Settings(@Settings)
;         
;         If Settings\get_IsWebMessageEnabled(@Result) = #S_OK
;           If Result
;             Debug "WebMessageEnabled"
;           Else
;             If Settings\put_IsWebMessageEnabled(#True) = #S_OK
;               Debug "Ok"
;             Else
;               Debug "NoK"
;             EndIf
;           EndIf
;         EndIf
        
        *EventHandler = AddMapElement(WebViewWebMessageReceivedEventHandlerMap(), Str(WebView))
        If *EventHandler
          *EventHandler\pVtable = *EventHandler + OffsetOf(IUnknownBase_Structure\pQueryInterface)
          *EventHandler\pQueryInterface = @ICoreWebView2WebMessageReceivedEventHandler_QueryInterface()
          *EventHandler\pAddRef = @ICoreWebView2WebMessageReceivedEventHandler_AddRef()
          *EventHandler\pRelease = @ICoreWebView2WebMessageReceivedEventHandler_Release()
          *EventHandler\pInvoke = @ICoreWebView2WebMessageReceivedEventHandler_Invoke()
          *EventHandler\Token = 0
          *EventHandler\pCallback = *Callback
          
          Result = Core\add_WebMessageReceived(*EventHandler, @*EventHandler\Token)
          If Result = #S_OK
            ;Debug "Token: " + Str(*EventHandler\Token)
            Result = #True
          Else
            WebViewRemoveWebMessageReceivedCallback(WebView, Core)
            Result = #False
          EndIf
          
        EndIf
        
        Core\Release()
      EndIf
    EndIf
  EndIf
  
  ProcedureReturn Result
  
EndProcedure




CompilerIf #PB_Compiler_IsMainFile
  
  
  Procedure.i WebViewWebMessageReceived(Message$)
    
    Debug Message$
    
    ProcedureReturn #S_OK
    
  EndProcedure
  
  
  
  Define Event.i
  
  
  OpenWindow(0, 100, 100, 400, 100, "Hello", #PB_Window_SystemMenu)
  
  WebViewGadget(0, 0, 0, 400, 100)
  SetGadgetItemText(0, #PB_WebView_HtmlCode,
                    ~"<!DOCTYPE html>\n" + 
                    ~"<html lang=\"en\">\n" +
                    ~" <head>" +
                    ~"  <title>WebMessage</title>" +
                    ~" </head>" +
                    ~" <body>" +
                    ~"  After 3 seconds a message will be posted" +
                    ~" </body>" +
                    ~"</html>")
  
  
  WebViewAddWebMessageReceivedCallback(0, @WebViewWebMessageReceived())
  
  AddWindowTimer(0, 1, 3000)
  
  Repeat 
    Event = WaitWindowEvent()
    If Event = #PB_Event_Timer
      If EventTimer() = 1
        RemoveWindowTimer(0, 1)
        WebViewExecuteScript(0, "window.chrome.webview.postMessage('Hello PureBasic');")
      EndIf
    EndIf
  Until Event = #PB_Event_CloseWindow
  
CompilerEndIf

Re: WebViewGadget WebMessageReceived callback

Posted: Fri Oct 25, 2024 9:52 am
by Kiffi
Very cool! Thanks Image

Re: WebViewGadget WebMessageReceived callback

Posted: Fri Oct 25, 2024 1:51 pm
by Quin
Nice work as always! :)

Re: WebViewGadget WebMessageReceived callback

Posted: Fri Oct 25, 2024 4:10 pm
by infratec
Updated the code above.

Forgot a CoTaskMemFree_(*ptr)
And changed the Parameter of the callback to string.

Re: WebViewGadget WebMessageReceived callback

Posted: Tue Oct 29, 2024 2:54 pm
by Zapman
Very nice! Thanks a lot!

Re: WebViewGadget WebMessageReceived callback

Posted: Mon Dec 02, 2024 8:55 pm
by Kwai chang caine
Thanks for sharing 8)

Re: WebViewGadget WebMessageReceived callback

Posted: Thu Dec 05, 2024 8:01 am
by dige
Hello Bernd, thank you very much for the code. Can you please say something about the possibilities?

I'm thinking of an interactive map, if I move a marker by drag and drop etc., can I then get the position?

How does your code differ from the BindWebViewCallback()?

Re: WebViewGadget WebMessageReceived callback

Posted: Thu Dec 05, 2024 8:57 am
by infratec
I extended the code of the BindWebViewCallback() example from the PB help:

Code: Select all

Html$ =  ~"<button id=\"displayInfo\">Display Info</button>\n"+
         ~"<script>\n"+
         ~"  const displayInfoElement=document.getElementById(\"displayInfo\");\n"+
         ~"  document.addEventListener(\"DOMContentLoaded\", () => {\n"+
         ~"    displayInfoElement.addEventListener(\"click\", () => {\n"+
         ~"      window.displayInfo(1000, 2000).then(result => {\n"+
         ~"        displayInfoElement.textContent = result.sum;\n"+
         ~"      });\n"+
         ~"    });\n"+
         ~"  });\n"+
         ~"</script>";


 Procedure IncrementJS(JsonParameters$)
    
    Dim Parameters(0)
    
    ParseJSON(0, JsonParameters$)
    ExtractJSONArray(JSONValue(0), Parameters())
    
    Debug "Nb Parameters: " + ArraySize(Parameters())
    
    Debug "Parameter 1: " + Parameters(0)
    Debug "Parameter 2: " + Parameters(1)
    
    ProcedureReturn UTF8(~"{ \"sum\": "+Str(Parameters(0) + Parameters(1))+ "}")
  EndProcedure


Procedure.i WebViewWebMessageReceived(Message$)
  
  Debug Message$
  
  ProcedureReturn #S_OK
  
EndProcedure


OpenWindow(0, 100, 100, 400, 400, "Hello", #PB_Window_SystemMenu)

WebViewGadget(0, 0, 0, 400, 400)
SetGadgetItemText(0, #PB_WebView_HtmlCode, Html$)

BindWebViewCallback(0, "postMessage", @WebViewWebMessageReceived())
BindWebViewCallback(0, "displayInfo", @IncrementJS())


AddWindowTimer(0, 1, 3000)

Repeat 
  Event = WaitWindowEvent()
  If Event = #PB_Event_Timer
    If EventTimer() = 1
      RemoveWindowTimer(0, 1)
      WebViewExecuteScript(0, "window.chrome.webview.postMessage('Hello PureBasic');")
      Debug "postMessage sent"
    EndIf
  EndIf
  
Until Event = #PB_Event_CloseWindow
It is not possible to use BindWebViewCallback() for postMessage().

It is only one more possibility to communicate between the web page and PB.
What you do with these possibilities depends on your JavaScript skills.

Re: WebViewGadget WebMessageReceived callback

Posted: Thu Dec 05, 2024 2:48 pm
by dige
Thx for the information.

When I click on Display Info, the program crashes with an unhandled exception (Windows PB 6.12 x64)

Re: WebViewGadget WebMessageReceived callback

Posted: Thu Dec 05, 2024 5:31 pm
by infratec
Hm... works with firefox on my win10 PC with PB 6.12 x86
And also with PB 6.12 x64

Re: WebViewGadget WebMessageReceived callback

Posted: Thu Dec 05, 2024 7:18 pm
by dige
Firefox?

If I run the code, a Window with webview gadget opens and there is the Click me button, who crashes the runtime, after clicking.

Re: WebViewGadget WebMessageReceived callback

Posted: Thu Dec 05, 2024 8:42 pm
by infratec
Ups ...

of course you are right. firefox was in the background :oops:

But still, it works here in x86 and x64 without crash.