2

So I am creating a Chrome app with an embedded <webview> element. The embedded webapp contains a lot of legacy code such as window.showModalDialog calls (which Chrome no longer supports).

The thing I'm trying to do now is to polyfill those calls. I've created a simple test example:

<webview src="http://legacyCodeIsAwesome.com/" style="width:100%; height:100%"></webview>

Code operating on the webview element:

webview.addEventListener('contentload', function() {
    webview.executeScript(
      {
        code: 'window.showModalDialog = function() { console.log ("Hello, world");}',
        runAt: 'document_start'
      }
    );
  });

The above code runs (adding a debug console.log works), only it doesn't do what it's supposed to do, which is overwrite the showModalDialog function.

Is there some sort of restriction on overwriting functions on the window object in webviews? Is there a way around it?

Xan
  • 74,770
  • 16
  • 179
  • 206
Robin-Hoodie
  • 4,886
  • 4
  • 30
  • 63

1 Answers1

2

When you call webview.executeScript, you essentially create a Content Script.

One of the core principles of content scripts is isolated world: a content script sees a separate copy of window object.

Content scripts execute in a special environment called an isolated world. They have access to the DOM of the page they are injected into, but not to any JavaScript variables or functions created by the page. It looks to each content script as if there is no other JavaScript executing on the page it is running on. The same is true in reverse: JavaScript running on the page cannot call any functions or access any variables defined by content scripts.

So, your override works, but only in the content script context.

To perform the override in the page context itself, we need to go deeper. We need to go deeper

A content script can create a <script> element on a page; code in said element will execute in page's context. You can read about this technique, called page-level scripting or injected scripts, in this canonical question.

// Content script
var script = document.createElement('script');
script.textContent = `
  window.showModalDialog = function() {
    console.log("Hello, world");
  }
`;
(document.head||document.documentElement).appendChild(script);
script.remove();

That said, you're injecting your code far too late. On contentload the offending JS likely executed already, and adding "document_start" isn't going to rewind time.

Fortunately, you can declare in advance the content scripts you want to have:

webview.addContentScripts([
  {
    name: "showModalDialogPolyfill",
    matches: ["https://webapp.example.com/*"],
    js: { files: ['content.js'] },
    run_at: 'document_start'
  }
]);
webview.src = "https://webapp.example.com/";
Xan
  • 74,770
  • 16
  • 179
  • 206