如何在Chrome打包应用程序中的Webview中从链接打开新窗口

7
为了展示一个横幅交换的广告,我正在加载包含指向广告服务器的iframe的Webview。通过一些JavaScript,我可以让广告链接的目标为“_blank”,这样广告就会在一个独立的浏览器窗口中打开,否则它将在同一个小的Webview中打开。
然而,它并没有起作用,我在控制台中看到了这个:
<webview>: A new window was blocked.

有什么想法如何做到这一点?
1个回答

17

问题在于,默认情况下,Webview 不会让客户端打开任意窗口。相反,它会发出一个 'newwindow' 事件,你可以拦截并决定如何处理它。以较简单的形式表述,代码如下:

chrome.app.runtime.onLaunched.addListener(function() {
  chrome.app.window.create(
    'main.html',
    { 'width': 1000, 'height': 1000 },
    function(win) {
      win.contentWindow.onload = function() {
        var webview = win.contentWindow.document.querySelector('#my_webview');
        webview.addEventListener('newwindow', function(e) {
          e.preventDefault();
          // e.targetUrl contains the target URL of the original link click
          // or window.open() call: use it to open your own window to it.
          // Something to keep in mind: window.open() called from the
          // app's event page is currently (Nov 2013) handicapped and buggy
          // (e.g. it doesn't have access to local storage, including cookie
          // store). You can try to use it here and below, but be prepare that
          // it may sometimes produce bad results.
          chrome.app.window.create(e.targetUrl, ...);
        });
      };
    }
  );
});

根据你描述的条件,我认为这应该适用于你。

在更糟糕的情况下,某些网站可能会像这样打开新窗口/标签页:

function open(href) {
    var w = window.open('', '_blank');
    w.opener = null;
    w.document.write(
      '<META HTTP-EQUIV="refresh" content="0; url=' + href + '">');
    w.document.close();
}

如果一个网站被包裹在webview中,那么一切都变得更加困难:上面的'newwindow'处理程序中的e.targetUrl将包含"about:blank",所以如果不进行修改,代码将打开一个空白窗口/选项卡。为了拦截来自客户端的后续重定向,应用程序还必须使用chrome.webRequest API(文档似乎仅适用于扩展,但该API已经在稳定通道中提供给打包的应用程序使用)。
chrome.app.runtime.onLaunched.addListener(function() {
  chrome.app.window.create(
    'main.html',
    { 'width': 2000, 'height': 1000 },
    function(win) {
      win.contentWindow.onload = function() {
        var webview = win.contentWindow.document.querySelector('#webview');
        webview.addEventListener('newwindow', function(e) {
          e.preventDefault();
          if (e.targetUrl !== 'about:blank') {
            // Easy case where the original link or window.open()
            // already contains a target URL.
            newWindow_openInTab(e.targetUrl);
          } else {
            // Harder case where the guest first opens a blank
            // window and then redirects it via a
            // 'META HTTP-EQUIV="refresh"'.
            newWindow_openInTabAndInterceptRedirect(e.window);
        });
      };
    }
  );
});

function newWindow_openInTab(url) {
  chrome.app.window.create(url, ...);
}

function newWindow_openInTabAndInterceptRedirect(newWindow) {
  // Create an invisible proxy webview to listen to redirect
  // requests from |newWindow| (the window that the guest is
  // trying to open). NOTE: The proxy webview currently has to
  // live somewhere in the DOM, so we append it to the body.
  // This requirement is in the process of being eliminated.
  var proxyWebview = document.createElement('webview');
  document.body.appendChild(proxyWebview);

  // Listen to onBeforeRequest event (chrome.webRequest API)
  // on proxyWebview in order to intercept newWindow's redirects.
  var onBeforeRequestListener = function(e) {
    // Only consider top-level non-blank redirects.
    if (e.type === "main_frame" && e.url !== 'about:blank') {
      chrome.app.window.create(e.url, ...);
      // Don't need proxyWebview anymore.
      document.body.removeChild(proxyWebview);
      // Handled this redirect: cancel further processing.
      return { cancel: true };
    } else {
      // Ignored this redirect: proceed with default processing.
      return { cancel: false };
    }
  };
  proxyWebview.onBeforeRequest.addListener(
    onBeforeRequestListener,
    { urls: [ "*://*/*" ] },
    [ 'blocking' ]
  );

  // Attach |newWindow| to proxyWebview. From the original
  // webview guest's point of view, the window is now opened
  // and ready to be redirected: when it does so, the redirect
  // will be intercepted by |onBeforeRequestListener|.
  newWindow.attach(proxyWebview);
}

我之前不知道newwindow事件,谢谢。第二种方法也是个好技巧。 - Alejandro Pedraza
我并不是把第二种方法当作原问题的解决方案。我想说的是,有些网站,比如Gmail,有时会使用这种技术来打开新标签页并将其定向到目标URL。我会更新我的答案以使其更清晰,并为这个更困难的情况添加一个解决方案。 - Sergey Shevchenko
我突然想到,我一直在假设你正在处理target='_blank'链接,而实际上是你自己将target='_blank'添加到链接中。在这种情况下,您的问题可以通过直接在Webview上使用webRequest API(就像第二个示例中的代理Webview一样)来解决,因此根本不需要代理Webview或侦听“newwindow”。希望能有所帮助。 - Sergey Shevchenko
在权限清单中是否需要执行某些操作?你试图打开的新窗口可能是应用程序外部的,我会收到“此网页未找到”Chrome错误,如果我展开它,我会看到该窗口正在尝试加载“chrome-extension://<extension-id>/url”。 - Mike Bartlett
啊,我认为你只需要执行window.open(e.targetUrl);就可以了。 - Mike Bartlett

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接