is there possible calling gwt jsni method with chrome extension using chrome.tab.executeScript? - gwt

I have a gwt project that wrap in chrome extensions.
In GWT, I export a java method to jsni called:
Java Method: sendMessage(String msg);
export jsni: $wnd.sendMessage = function(msg) { ... };
then in chrome extension, I execute:
chrome.tabs.executeScript(tabId, {code: "sendMessage('hello');"}
but not thing happened, I've tried:
chrome.tabs.executeScript(tabId, {code: "alert('hello');"}
and it just works fine. but it just can't call my gwt jsni method.

Chrome content scripts exist in an isolated world.
$wnd.sendMessage is exported in the page context, and not accessible from a content script.
You'll need to inject code into the page itself (with a <script> tag) to access it.
See this canonical question on the topic, and this question can also be of use: Executing code at page-level from Background.js and returning the value

solved the problem, when pass a json string, you have to encode the string to prevent double quotes/special character in the string, here is my code:
assume the data format is:
var data = '{"abc":123,"cde":"kkk"}';
then encode the data:
var param = '\'' + encodeURIComponent(data) + '\'';
put then data into the code to execute:
var codeToExec = [
'var actualCode = "window.sendMessage(' + param + ');"',
'var script = document.createElement("script");',
'script.textContent = actualCode;',
'(document.head||document.documentElement).appendChild(script);',
'script.remove();'
].join('\n');
chrome.tabs.executeScript(mainTabId, {code: codeToExec});

Related

How to enable folders in Chrome devtools > Sources > Page > top > (no domain) (files with //# sourceURL=...)

Is there a way to show folders for path's in Chrome devtool > Page panel
for tree nodes in top > (no domain)
these are node comming from js eval with a //# sourceURL=...
as my code contains a lot of these I want them to show as
- top
- MyDomain
- MyFile
- (no domain)
- FolderA
- Eval1
- Eval2
- FolderB
- Eval3
- Eval4
Instead of
- top
- MyDomain
- MyFile
- (no domain)
- FolderA/Eval1
- FolderA/Eval2
- FolderB/Eval3
- FolderB/Eval4
Include the domain (origin) in the sourceUrl.
I had to find this out by trial and error, it is not well-documented anywhere.
JS syntax:
//# sourceURL=http://mySite:8000/FolderA/File1.js
Css syntax:
/*# sourceURL=http://mySite:8000/FolderA/File1.css */
Build it in the browser:
Create a sourceUrl from a relative url:
function jsSrcUrl(path) {
return "//# sourceURL=" + window.origin + path + " \n";
}
var src = "/js/myScript.js"; // use this url in your GET request via fetch()
var sourceUrl = jsSrcUrl(src); // append this and eval it later
Create a sourceUrl during a fetch request:
var src = "/ui/vue.js";
var fullUrl;
fetch(src)
.then(function(response) {
fullUrl = response.url;
return response.text()
})
.then(function(text) {
console.log(text)
})
var sourceUrl = "//# sourceURL=" + fullUrl + " \n"
Add a sourceUrl to your script (which is a string before eval):
var scriptWithSrcUrl = script.concat(sourceUrl);
Eval your script to load your script (string) into the browser and sources panel
eval(scriptWithSrcUrl)
Put it all together: Create a sourceUrl during a fetch request AND append it to the script AND eval it:
async function loadScript(url) {
var fullUrl;
async function getScript() {
return fetch(url)
.then(function(response) {
fullUrl = response.url;
return response.text();
})
.then(function(text) {
return text;
});
}
var script = await getScript();
var sourceUrl = "\n//# sourceURL=" + fullUrl + " \n";
var scriptWithSrcUrl = script.concat(sourceUrl);
(1, eval)(scriptWithSrcUrl);
}
loadScript("/path/to/script.js");
Look in your devtools sources panel and find your file in the proper folder and domain!
Dear Google, Mozilla, Microsoft, & Apple:
The fetch api is nice, but fetching js/css/html via a get request, injecting a sourceUrl, and eval'ing it is not just lame, it's ultra lame. Loading everything in a script tag in the document head doesn't play nice with lazy-loading components, code-splitting, tree shaking, and SSR in general. Wouldn't it be nice if there was an api to load js, css, html templates, components, and js modules into global scope - all from js (not from html) natively using browser api's?
A way that doesn't involve:
ES6 module syntax and scope (for loading into local scope)
fetch() + sourceUrl + eval()
document.createElement('script') + set src + onload() (pollutes the doc head)
Some library/dependency (Jquery $.getScript(), Fetch inject, Requirejs, etc.)
I am sure that this api already exists within the browser, script tags probably already consume that js api...can't we just have nice things?
loadScript("path/to/js.js"); // for loading js into global scope
Such a simple use case, yet no API.

[vscode extension API]: How to create communication channel between javascript included in `vscode.previewHtml` and outside thread?

The thing is, i am trying to create a tool with vscode.previewHtml command, I need some javascript in it listening on user's action, and send a signal to outside thread, so that i can do few high cost "native" job in background, and refresh the previewHtml with new content.
Is it possible?
I assume by "outside thread" you actually mean the hosting environment (VS code). There is no direct way (e.g. a listener/callback/method) to communicate, but you can use postMessage like so:
/* This is in a .js file you include in previewHTML. */
function exportToSVG(type) {
let html = document.querySelectorAll('html')[0];
let svg = document.querySelectorAll('svg')[0];
const args = { name: objectName, type: type, svg: svg.outerHTML, html: html.outerHTML };
window.parent.postMessage({
command: "did-click-link",
data: `command:_antlr.saveSVG?${encodeURIComponent(JSON.stringify(args))}`
}, "file://");
}
In vscode only a few commands are handled. One is that did-click-link command. You then can register to that command in your extension and do the actual work:
// This is in your extension code (usually the activate() method).
context.subscriptions.push(commands.registerCommand('_antlr.saveSVG', (args: { name: string, type: string, svg: string, html: string }) => {
window.showInputBox({
value: workspace.getConfiguration("antlr4." + args.type)["saveDir"] + "/" + args.name + ".svg",
placeHolder: "<Enter full file name here>",
prompt: "Enter the name to an svg file."
}).then((value: string) => {
...
You can use the same approach for the other direction. See for instance the markdown preview extension how to do that.

Web Api returns garbage for text files unless run from the browser bar

I am writing a file service using Asp.Net’s Web Api. The service retrieves files (Css, Excel, Csv, etc.) from SQL Server and serves them up in response to Get requests.
My first test case is for Css files. The issue is that, while I can see the correct data on the server side, when the browser retrieves/decodes it, the results are mangled. The issue appears to be related to the encodings.
Here are the request/response headers in FireFox:
When I click on the response tab in FireBug, here’s what it looks like:
The results look like ascii being displayed as utf8. This is the html view in FireBug:
The above example is an iFrame inside a Facebook application which is running ssl.
If I take the url and open it directly in the browser, it works and correctly displays my Css:
In summary, when I retrieve my Css file from a tag inside my Facebook app, I get garbage (encoding issue?). If I retrieve it straight from the browser, it works.
My CssFormatter MediaTypeFormatter code:
public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
{
var taskSource = new TaskCompletionSource<object>();
try
{
var incomingFile = value as FileRestService.Entity.IFile;
var ms = new MemoryStream(incomingFile.DataBuffer);
ms.CopyTo(writeStream);
ms.Flush();
taskSource.SetResult(writeStream);
}
catch (Exception e)
{
taskSource.SetException(e);
}
return taskSource.Task;
}
Am I creating the response stream incorrectly? I noticed that the response headers do not specify the encoding. Is this an issue?
I find the easiest way to handle this is to write something along the lines of (here's the important details):
public class Formatter : MediaTypeFormatter {
// TODO override the constructor to add some mappings or some other way for this formatter to be picked up
// TODO override CanReadType and CanWriteType according to your rules
public override void SetDefaultContentHeaders(Type t, HttpContentHeaders headers, string mediaType) {
base.SetDefaultContentHeaders(t, headers, mediaType);
headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") {
FileName = "SomeName.ext"
};
}
public override Task WriteToStreamAsync(Type t, object value, Stream s, HttpContentHeaders headers, TransportContext context) {
return Task.Factory.StartNew(() => {
// TODO code to write to the output stream, flush it but don't explicitly close it
});
}
}

How to construct a REST API that takes an array of id's for the resources

I am building a REST API for my project. The API for getting a given user's INFO is:
api.com/users/[USER-ID]
I would like to also allow the client to pass in a list of user IDs. How can I construct the API so that it is RESTful and takes in a list of user ID's?
If you are passing all your parameters on the URL, then probably comma separated values would be the best choice. Then you would have an URL template like the following:
api.com/users?id=id1,id2,id3,id4,id5
api.com/users?id=id1,id2,id3,id4,id5
api.com/users?ids[]=id1&ids[]=id2&ids[]=id3&ids[]=id4&ids[]=id5
IMO, above calls does not looks RESTful, however these are quick and efficient workaround (y). But length of the URL is limited by webserver, eg tomcat.
RESTful attempt:
POST http://example.com/api/batchtask
[
{
method : "GET",
headers : [..],
url : "/users/id1"
},
{
method : "GET",
headers : [..],
url : "/users/id2"
}
]
Server will reply URI of newly created batchtask resource.
201 Created
Location: "http://example.com/api/batchtask/1254"
Now client can fetch batch response or task progress by polling
GET http://example.com/api/batchtask/1254
This is how others attempted to solve this issue:
Google Drive
Facebook
Microsoft
Subbu Allamaraju
I find another way of doing the same thing by using #PathParam. Here is the code sample.
#GET
#Path("data/xml/{Ids}")
#Produces("application/xml")
public Object getData(#PathParam("zrssIds") String Ids)
{
System.out.println("zrssIds = " + Ids);
//Here you need to use String tokenizer to make the array from the string.
}
Call the service by using following url.
http://localhost:8080/MyServices/resources/cm/data/xml/12,13,56,76
where
http://localhost:8080/[War File Name]/[Servlet Mapping]/[Class Path]/data/xml/12,13,56,76
As much as I prefer this approach:-
api.com/users?id=id1,id2,id3,id4,id5
The correct way is
api.com/users?ids[]=id1&ids[]=id2&ids[]=id3&ids[]=id4&ids[]=id5
or
api.com/users?ids=id1&ids=id2&ids=id3&ids=id4&ids=id5
This is how rack does it. This is how php does it. This is how node does it as well...
There seems to be a few ways to achieve this. I'd like to offer how I solve it:
GET /users/<id>[,id,...]
It does have limitation on the amount of ids that can be specified because of URI-length limits - which I find a good thing as to avoid abuse of the endpoint.
I prefer to use path parameters for IDs and keep querystring params dedicated to filters. It maintains RESTful-ness by ensuring the document responding at the URI can still be considered a resource and could still be cached (although there are some hoops to jump to cache it effectively).
I'm interested in comments in my hunt for the ideal solution to this form :)
You can build a Rest API or a restful project using ASP.NET MVC and return data as a JSON.
An example controller function would be:
public JsonpResult GetUsers(string userIds)
{
var values = JsonConvert.DeserializeObject<List<int>>(userIds);
var users = _userRepository.GetAllUsersByIds(userIds);
var collection = users.Select(user => new { id = user.Id, fullname = user.FirstName +" "+ user.LastName });
var result = new { users = collection };
return this.Jsonp(result);
}
public IQueryable<User> GetAllUsersByIds(List<int> ids)
{
return _db.Users.Where(c=> ids.Contains(c.Id));
}
Then you just call the GetUsers function via a regular AJAX function supplying the array of Ids(in this case I am using jQuery stringify to send the array as string and dematerialize it back in the controller but you can just send the array of ints and receive it as an array of int's in the controller). I've build an entire Restful API using ASP.NET MVC that returns the data as cross domain json and that can be used from any app. That of course if you can use ASP.NET MVC.
function GetUsers()
{
var link = '<%= ResolveUrl("~")%>users?callback=?';
var userIds = [];
$('#multiselect :selected').each(function (i, selected) {
userIds[i] = $(selected).val();
});
$.ajax({
url: link,
traditional: true,
data: { 'userIds': JSON.stringify(userIds) },
dataType: "jsonp",
jsonpCallback: "refreshUsers"
});
}

ajaxcontroltoolkit setting hidden value after asyncfileupload has completed

I have an asyncfileupload control that I'm using from the ajaxcontroltoolkit. On the file complete in the code behind I process the file and write the information in the file to a database. I get the id of the record from the database, and this needs to be written to an asp hidden field. I've tried just setting the value:
fldImageID.Value = pimg.IdImageGroup.ToString();
I've tried Registering a script like I've seen in an example on a website:
ScriptManager.RegisterClientScriptBlock(
ImageFileUploader,
ImageFileUploader.GetType(),
"script1",
"alert('hi'); top.document.getElementById('"
+ fldImageID.ClientID
+ "').value='"
+ pimg.IdImageGroup.ToString()
+ "'; top.document.getElementById('"
+ lblError.ClientID
+ "').innerHTML = 'image uploaded'",
true);
I've just tried embedding javascript in a response.Write call from the method I've set to process the uploaded file. Nothing I've done has worked so far. After I've done everything the hidden field still does not contain the required value.
This is pretty easy with jQuery. Have an html hidden input control placed in your page, not the asp:hidden input control. Add a class lets say "hiddenPhoto" to your html hidden control.
so lets say our control html is like this
<input type="hidden" class="hiddenPhoto" runat="server" id="hdPhotoName" />
Now access it using class selector in your OnClientUploadComplete js method and set its value. Have it declared runat="server" in order to access its value on the server side.
Regards
I found an acceptable solution back when I was working on this. And since then I've received emails from people who have had the same problem and have been asking if I found a solution. So I'm presenting it here, stripping out any extraineous code:
From the user control that has the FileUpload control I first set the session variable on the back side in the FileUploadComplete handler:
*in the ascx file (upload_chart.ascx) I have the AsyncFileUpload, what is important is the OnUploadComplete and the OnClientUploadComplete:*
<ajaxToolkit:AsyncFileUpload
OnUploadedComplete="FileUploadComplete1"
OnClientUploadComplete="UploadComplete1"
ID="ImageFileUploader"
runat="server" />
*in the code behind of the ascx file (upload_chart.ascx.cs) I handle the FileUploadComplete:*
public void FileUploadComplete1(object sender, EventArgs e)
{
try
{
if (ImageFileUploader.FileBytes.Length > 0)
{
// File data is in ImageFileUploaded.FileBytes
// Save it however you need to
// I saved it to a database, in a DBImage Object class I created
// DBImage is specific to my application
ODS.Entity.DBImage pimg =
ODS.Data.DataRepository.SaveImageBytes(ImageFileUploaded.FileBytes);
// Set the ImageID1 in the session
Session["ImageID1"] = pimg.IdImageGroup.ToString();
}
else
{
// error handling for an empty file, however you want to handle it
}
}
catch (Exception Ex)
{
// error handling for an unhandled exception, whatever you want to do here
}
}
Javascript and script methods are used to set the value on the page, here is my codebehind for the script method:
// on the aspx page code behind (chartofthedayadmin.aspx.cs) I have the webmethod:
[System.Web.Services.WebMethod]
public static string GetImageID1()
{
System.Web.SessionState.HttpSessionState Session = System.Web.HttpContext.Current.Session;
String retval = Session["ImageID1"].ToString();
Session["ImageID1"] = null;
return retval;
}
Here is the javascript:
// on the aspx front end (chartofthedayadmin.aspx) I have the javascript
// to call the Web method and the javascript failed message:
function UploadComplete1() {
var str = PageMethods.GetImageID1(uploadSuccess1, uploadFailed);
}
function uploadFailed() {
alert('error occurred or some meaningfull error stuff');
}
*// javascript on the user control (upload_chart.ascx) to set the value of the hidden field*
function uploadSuccess1(result) {
document.getElementById('<%= fldImageID.ClientID %>').value = result;
}
note: Make sure your scriptmanager has EnablePageMethods="true".
The better and more simple solution is in code behind:
string script = String.Format("top.document.getElementById('hdnFilename').value='{0}';", safeFilename);
ScriptManager.RegisterClientScriptBlock(this, this.GetType(), "hdnFilenameFromCodeBehind", script, true);
In my case, safeFilename is the unique filename, after handling duplicate filename, i.e. sample_5.png in the 5th upload of sample.png.
See http://forums.asp.net/t/1503989.aspx