mingyaulee / Blazor.BrowserExtension

Build browser extensions easily with Blazor.
https://mingyaulee.github.io/Blazor.BrowserExtension/
MIT License
379 stars 42 forks source link

How to use JSInterop in popup.html? #33

Closed pawelel closed 2 years ago

pawelel commented 2 years ago

There is a file in wwwroot clipboardCopy.js with content:

window.clipboardCopy = {
    copyText: function (text) {
        navigator.clipboard.writeText(text)
            .catch(function (error) {
                alert(error);
            });
    }
};

line in index.html:

<script src="clipboardCopy.js"></script>

And code in razor.cs file:

 [Inject] IJSRuntime JSRuntime { get; set; }

        private async Task CopyTextToClipboard()
        {
            await JSRuntime.InvokeVoidAsync("clipboardCopy.copyText", Text);
        }

WebAssembly cannot find the file. How to enable JSRuntime or use another approach?

crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
 Unhandled exception rendering component: Could not find 'clipboardCopy.copyText' ('clipboardCopy' was undefined).
pawelel commented 2 years ago

Alternative approach:

{
"content_security_policy": "script-src 'self' 'sha256-rvx3UjhGQCYQupYSZD7x0TZFdqU5Fb98lYCztRgGW9A=' 'unsafe-eval' 'wasm-eval' 'sha256-v8v3RKRPmN4odZ1CWM5gw80QKPCCWMcpNeOmimNL2AA='; object-src 'self'"
}

Is there another way?

mingyaulee commented 2 years ago

There are two things that you can check, one is whether the JS is loaded before the Blazor initialization. If it is, then open up the console and make sure that from the developer tools console you can access to the variable. Otherwise I think it should work as it is with your code.

pawelel commented 2 years ago

I need to fill form on web page using Blazor.BrowserExtension instead of copy to clipboard. Could you share how to access input? An example:

 <div id="X78Edit" class="mandatoryFieldStyle xEdit" style="height:20px;">
<input type="text" id="X78"
name="instance/brief.description"
dvdvar="" buttonid=""
datatype="string"
sctype="Text" tabindex="" spellcheck="true"
onkeyup="handleOnChange(this, event);"
onfocus="handleOnFocus(this, event);"
onblur="handleOnBlur(this, event);
applyToSameControl(this);"
onclick="handleOnClick(this, event);"
onchange="handleOnChange(this, event);"
value="Type Text Here"
scripttype="text"></div>

I assume, that ContentScript.razor is somehow responsible for it but writing JS code there is not allowed. Code used in other Chrome extension: background.js

'use strict';
chrome.runtime.onInstalled.addListener(function() {
    chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
      chrome.declarativeContent.onPageChanged.addRules([{
        conditions: [new chrome.declarativeContent.PageStateMatcher({
          pageUrl: { ports: [80, 443, [1000, 1200]]},
        })
        ],
            actions: [new chrome.declarativeContent.ShowPageAction()]
      }]);
    });
});

inject.js

var input = document.getElementsByTagName('input');
if( input.length > 0 ){
    for( var index in input ){

      if( input[index].type == 'text' )
        input[index].value = "Type Text Here";

      if( input[index].name == undefined ){
        continue;
      }
  }

popup.js

let fillForm = document.getElementById('fillForm');
fillForm.onclick = function(element) {
    chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
      chrome.tabs.executeScript(
          tabs[0].id,
          {file: 'inject.js'});
    });
  };
mingyaulee commented 2 years ago

The example that you have shown is injecting JavaScript file into the tab to update the value of the inputs. You can do the same with Blazor, but only for the background and popup. The inject.js file still needs to be in JavaScript because it is not possible to inject wasm into a tab. This is probably the easier way to go.

Another way you can achieve this and avoiding JavaScript completely would be quite troublesome but still feasible, which is to use content script and listen to messages sent by the popup. The content script will need to use JsBind or any other alternative Blazor DOM access library to be able to access the DOM API from Blazor.

pawelel commented 2 years ago

The second way is the one I'm interested in, but I'm afraid without an example it can take forever. Is there a chance for it in near future? Something like change browser background color https://github.com/GoogleChrome/chrome-extensions-samples/tree/main/tutorials/getting-started

The example that you have shown is injecting JavaScript file into the tab to update the value of the inputs. You can do the same with Blazor, but only for the background and popup. The inject.js file still needs to be in JavaScript because it is not possible to inject wasm into a tab. This is probably the easier way to go.

Another way you can achieve this and avoiding JavaScript completely would be quite troublesome but still feasible, which is to use content script and listen to messages sent by the popup. The content script will need to use JsBind or any other alternative Blazor DOM access library to be able to access the DOM API from Blazor.

mingyaulee commented 2 years ago

If your use case is specific and you know what DOM API you want to use, you can create the bindings to the interop fairly easily with JsBind. There are some examples in the project already for creating bindings to DOM APIs.

As for the messaging, you will need to make sure the tab receiving the message checks that it is the intended recipient. Check out the runtime sendMessage and onMessage for this.