2

I can access a Chrome App Webview HTML with:

webview.executeScript(
    {code: 'document.documentElement.innerHTML'},
    function(results) {
      // results[0] would have the webview's innerHTML.
    });

But I would like to get the value of global variables in the Guest like so:

webview.executeScript(
   {code: 'window.globalVar'},
   function(results) {
   // results[0] should have the webview's value of "globalVar".
   });

How can I do this?

Josh Crozier
  • 233,099
  • 56
  • 391
  • 304
user1541413
  • 307
  • 4
  • 17
  • See http://stackoverflow.com/questions/9515704/building-a-chrome-extension-inject-code-in-a-page-using-a-content-script – Xan Nov 10 '14 at 20:02
  • Thanks Xan, there is a lot of content on the link you suggest. It is difficult to figure out if it applies to current "Chrome App Webviews". It appears that I could be missing configuration in the manifest.json, but from there, I am still lost. The other post is for Chrome Extensions, I am not sure how much applies to Chrome Apps, that will be running in Cordova. – user1541413 Nov 10 '14 at 21:14
  • The point is, `window` objects of the page and the content script are isolated. It does not matter whether it's a webview. But you can access the "real" `window` by injecting a page-level script. – Xan Nov 10 '14 at 22:48
  • Just to be clear, the challenge I am having is getting data BACK from the Guest. I can inject a script and have it interact with the data on the Guest, but using a simple "return window.globalVar" is not sending the data back to the host. I am wondering if I should be using "message passing" instead? – user1541413 Nov 10 '14 at 23:12
  • Right.. Let me find a question for that. http://stackoverflow.com/a/26740141/934239 – Xan Nov 10 '14 at 23:14

2 Answers2

4

An answer to summarize the steps required.

1) You inject a content script with webview.executeScript() into the embedded page.

2) Since the page's real window is isolated, you need a page-level script to access it. You inject it with a <script> tag as discussed here.

3) The page-level script can access the window object, but cannot talk to the app script. However, it can fire a custom DOM event, that the content script can catch. Discussed here.

4) Finally, from the content script you need to send a message to your app script. The content script calls chrome.runtime.sendMessage, while the app script listens with chrome.runtime.onMessage. chrome.runtime.sendMessage does not seem to be available to webview content scripts injected with webview.executeScript(). A workaround is to use postMessage as described here.

It's a bit of an onion structure, that's why you need 2 steps "in" and 2 steps "out". You can't really do it in the return value that's passed to the executeScript callback, since at least one of the "out" steps will be asynchronous.

Community
  • 1
  • 1
Xan
  • 74,770
  • 16
  • 179
  • 206
  • Thanks for your patience and complete answer, I will give that a try ;) FYI, I a trying to get Latitude and Longitude from a BING Map to display on a Google Map. – user1541413 Nov 10 '14 at 23:30
  • Please do; I'm not 100% sure of the answer since I have little to no experience with apps. But that's how `executeScript` works in extensions. – Xan Nov 10 '14 at 23:31
  • I am not sure how common this tool is, but I am using Chrome Dev Editor (Developer Preview) to play with Cordova Chrome Apps, and it is fantastic. Very quick and easy to mock up a simple test. – user1541413 Nov 10 '14 at 23:34
  • 1
    Success! I think I found the simple shortcut to getting data from the Guest. Inject a Script Tag that writes the data to the Guest Console, then use this: webview.addEventListener('consolemessage', function(e) { console.log('Guest page logged a message: ', e.message); }); – user1541413 Nov 11 '14 at 00:35
  • I don't think it's a very good shortcut, since the page in question can also print to the console. A custom event is less likely to interfere. – Xan Nov 11 '14 at 08:44
2

You can inject a script that inserts a DOM node with the global variable's value. Then you return that node's innerHTML and you have your value right away without using a callback:

var code = "script = document.createElement('script'); script.text=\"var n=document.createElement('span');n.style.display='none';n.id='my-id';n.innerHTML=window.globalVar;document.body.appendChild(n)\"; document.head.appendChild(script);document.getElementById('my-id').innerHTML"

webview.executeScript(
    {code: code},
    function(results) {
      console.log(results[0]);
    });

Just use an ID for the DOM node that is not used and you should be fine. It works for me.

marlar
  • 3,858
  • 6
  • 37
  • 60