WebView access Dom to retrieve properties

1,634 views
Skip to first unread message

Paul Norman

unread,
Sep 11, 2015, 8:55:41 AM9/11/15
to AndroidScript
Hi,

I must have missed the obvious here, but is there any way to access properties of a webView Control from DroidScript?

I realise [webView].Execute(text), will run JavaScript within the webView, but it does not return anything to DroidScript does it?

Is there any way of getting access to something like [webView].window.document.body.innerHTML;

Or getElementById .properties etc... please?

Paul

Chris Hopkin

unread,
Sep 11, 2015, 10:16:10 AM9/11/15
to AndroidScript
Hi Paul

You are right, WebView.Execute does not return anything.

If it is your own web page, you can include the app.js script in your page and callback from your web page into your DroidScript app. Something like this:

...

function btn_GetInnerHTML()
{
    web
.Execute( "var inner = document.getElementById( 'myId' ).innerHTML; \
                  app.Execute( 'ShowInnerHTML( \"' + inner + '\" )' );"
);
}

function ShowInnerHTML( innerHtml )
{
    app
.ShowPopup( innerHtml );
}

Thanks

Chris

Dave Smart

unread,
Sep 11, 2015, 10:20:11 AM9/11/15
to AndroidScript
Due to the nature of the JavaScript -> Android bridge provided by Google, we cannot allow direct access to the DOM.

We could however go on step better and add a callack parameter to the web.Execute method which would allow the results of an expression to be returned to the main app. 

Paul Norman

unread,
Sep 12, 2015, 8:46:24 PM9/12/15
to AndroidScript
Thanks Chris, great work around.

Paul Norman

unread,
Sep 12, 2015, 8:48:00 PM9/12/15
to AndroidScript
Looks like some built in security thing.

That idea would be brilliant Dave!

Paul Norman

unread,
Sep 12, 2015, 9:06:17 PM9/12/15
to AndroidScript
“which would allow the results of an expression to be returned to the main app”

Could that be an object, or for security reasons would it have to be a simple string?

stamja

unread,
Sep 23, 2015, 9:33:25 AM9/23/15
to AndroidScript
I've come up with a workaround using JSON to exchange data between the DS app space and the webview space.

The method is to have this XProcess object (code below) in a script that is loaded by both the app and WebView space.

Each process declares an instance of the object as a global variable, allowing each to Execute() the object methods.

To get information out of the webview, your app calls the Request() method of the webview's XProcess instance. Request() uses Execute() to send structured json data to the webview instance's Receive() method.

The webview's instance calls eval(), then sends the stringified result back to the app's XProcess instance.

So, you can send a string expression via the Request() merhod and have the result via a single function call, e.g.: fs = myXProcess.Request('document.body.style.fontSize');

Declare the method as a variable and for brevity you can use something like: fs = xpReq('document.body.style.fontSize');

I'm fairly new to JS coding, so some of this may seem clumsy or kludgy, and I'm sure there're issues I've overlooked. In particular, the FetchResult() method is a bit concerning in the way it polls for the result in a loop, 'though I haven't struck any problems with it as yet.

function XProcess(hst,nam){
  var self=this;
  this.reqID=0;
  this.Name=undefined;
  this.host=undefined;
  this.hostName=undefined;
  this.results=[];
  this.FetchResult=function(rID){
    var wait = true;
    var tmout = 0;
    while (wait) {
      for (var i=0; i<self.results.length; i++)
        if (self.results[i].reqID == rID) {
          var x = self.results[i].result;
          self.results.splice( i, 1 );
          return x;
          wait = false;
        }
      tmout++;
      if (tmout > 99) wait = false;
      else app.Wait( 0.01 );
    }
    return null;
  }
  this.Receive=function(s){
    var obj = JSON.parse(s);
    obj.result = eval( obj.request );
    var s = JSON.stringify(obj);
    app.Execute( obj.clientName + ".SubmitResult(\""
      + s.escString() + "\");" );
  }
  this.Request=function(r){
    var obj={};
    obj.clientName=self.Name;
    obj.request=r;
    obj.result=undefined;
    self.reqID++;
    obj.reqID = self.reqID;
    var s=JSON.stringify(obj);
    self.host.Execute( self.hostName + ".Receive(\""
      + s.escString() + "\");" );
    return self.FetchResult(obj.reqID);
  }
  this.SubmitResult=function(r){
    var obj = JSON.parse(r);
    self.results.push(obj);
  }
}

Note that the request string is escaped, so in the above I'm using:

String.prototype.escString=function() {
  var s = this.replace(/\\/g, "\\\\");
  s = s.replace(/"/g, '\\"');
  return s;
}

Declare the instance in the DS app like so:

//Called when application is started.
function OnStart() {

  // . . . create components, do stuff . . .

  myXProcess = new XProcess();

  myXProcess.Name = "myXProcess";      // Sent to the host's instance so that it can call your instance
  myXProcess.host = web;               // webview object on which to call Execute()
  myXProcess.hostName = "wvXProcess";  // The name of the global declared in webview
  xpReq = myXProcess.Request;          // For convenience, rather than use myXProcess.Request()

  // . . . do other stuff . . .
}

Declaration in the webview's OnLoad script thus:

  wvXProcess=new XProcess();
  wvXProcess.Name="wvXProcess";

And you're basically in business.

Hope this is of interest to someone or maybe even helpful, any comments welcome.

Cheers!

stamja

unread,
Sep 23, 2015, 9:45:01 AM9/23/15
to AndroidScript
"To get information out of the webview, your app calls the Request() method of the webview's XProcess instance."

Actually, that's wrong: Your app calls the Request() method of it's own XProcess instance, which in turn Execute()s the webview instance's Receive() method.

Apologies for any confusion. It's late. :)

Paul Norman

unread,
May 1, 2016, 12:56:31 AM5/1/16
to DroidScript
Just following through please ...

Has this been incorporated along the way at all yet please?

"Due to the nature of the JavaScript -> Android bridge provided by Google, we cannot allow direct access to the DOM.

We could however go on step better and "add a callack parameter to the web.Execute method which would allow the results of an expression to be returned to the main app. - Dave Smart"

Thanks,

Paul

Dave Smart

unread,
May 16, 2016, 9:04:07 AM5/16/16
to DroidScript
Hi Paul,

I've just added a result callback to the web.Execute method.  Should be available in an alpha very soon :)

Paul Norman

unread,
May 18, 2016, 9:43:12 AM5/18/16
to DroidScript
Awesome thanks Dave! That'll be really useful.
Reply all
Reply to author
Forward
0 new messages