外部内容

Chrome 应用的安全模型不允许 iframe 中的外部内容,也不允许使用内嵌脚本与 eval()。您可以覆盖这些限制,但是您的外部内容必须与应用程序隔离。

隔离的内容不能直接访问应用程序的数据或任何 API,但可以使用跨站 XMLHttpRequest 及消息传递在事件页面和经过沙盒屏蔽的内容间通信,并间接访问 API。

API 示例:想试试这些代码吗?请参见 sandbox 示例。

引用外部资源

应用使用的内容安全策略不允许多种类型的远程 URL,所以您不能从应用页面中直接引用外部图像、样式表或字体。相反,您可以使用跨站 XMLHttpRequest 获取这些资源,并通过 blob: URL 来使用它们。

清单文件的要求

为了能够发出跨来源 XMLHttpRequest 请求,您需要为远程 URL 的主机添加权限:

"permissions": [
    "...",
    "https://supersweetdomainbutnotcspfriendly.com/"
  ]

跨站 XMLHttpRequest

将远程 URL 获取进应用并通过 blob: URL 来使用它的内容:

var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://supersweetdomainbutnotcspfriendly.com/image.png', true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
  var img = document.createElement('img');
  img.src = window.URL.createObjectURL(this.response);
  document.body.appendChild(img);
};

xhr.send();

您可能希望在本地保存这些资源,这样它们在离线状态下也可以使用。

嵌入外部网页

API 示例:想试试这些代码吗?请参见 browser 示例。

webview 标签允许您在您的应用中嵌入外部网络内容,例如一个网页。它替换了指向远程 URL 的 iframe,在 Chrome 应用内这是禁用的。与 iframe 不同,webview 标签运行在单独的进程中,这意味着它内部的攻击仍然会被隔离开来,不能获得提升的权限。此外,由于它的存储区(Cookie 等)与应用隔离,没有办法让网络内容访问应用的任何数据。

添加 webview 元素

您的 webview 元素必须包含来源内容的 URL 并指定大小。

<webview src="http://news.google.com/" width="640" height="480"></webview>

更新属性

要动态更改 webview 标签的 srcwidthheight 属性,您既可以直接在 JavaScript 对象上设置这些属性,也可以使用 DOM 函数 setAttribute

document.querySelector('#mywebview').src =
    'http://blog.chromium.org/';
// 或
document.querySelector('#mywebview').setAttribute(
    'src', 'http://blog.chromium.org/');

通过沙盒保护本地内容

沙盒功能允许指定页面在经过沙盒屏蔽的唯一来源中运行,这些页面就不受内容安全策略的限制。经过沙盒屏蔽的页面可以使用 iframe、内嵌脚本以及 eval()。请查阅 sandbox 清单文件字段的描述。

不过这也是一个权衡的问题:经过沙盒屏蔽的页面不能使用 chrome.* API。如果您需要做一些类似于 eval() 的事情,请使用这种方法免受 CSP(内容安全策略)的限制,但是您也不能使用一些很棒的新功能。

在沙盒中使用内嵌脚本

如下是一个经过沙盒屏蔽的页面示例,使用了内嵌脚本与 eval()

<html>
  <body>
    <h1>Woot</h1>
    <script>
      eval('console.log(\'这是使用了 eval 的内嵌脚本。\')');
    </script>
  </body>
</html>

在清单文件中包含 sandbox

您需要在清单文件中包含 sandbox 字段,并列出需要在沙盒中运行的应用页面:

"sandbox": {
  "pages": ["sandboxed.html"]
}

在窗口中打开经过沙盒屏蔽的页面

就像其他应用页面一样,您可以创建一个窗口,打开经过沙盒屏蔽的页面。如下是一个示例,它创建了两个窗口,一个用于应用主窗口,不经过沙盒屏蔽,另一个用于经过沙河屏蔽的页面:

注意:使用经过沙盒屏蔽的页面创建窗口时会遇到问题 154662,开发者控制台中会输出错误“Uncaught TypeError: Cannot call method 'initializeAppWindow' of undefined”,而且 app.window.create 不会调用回调函数,并传递窗口对象。但还是会创建新窗口,打开经过沙盒屏蔽的页面。

chrome.app.runtime.onLaunched.addListener(function() {
  chrome.app.window.create('window.html', {
    'bounds': {
      'width': 400,
      'height': 400,
      'left': 0,
      'top': 0
    }
  });

  chrome.app.window.create('sandboxed.html', {
    'bounds': {
      'width': 400,
      'height': 400,
      'left': 400,
      'top': 0
    }
  });
});

在应用页面中嵌入经过沙盒屏蔽的页面

经过沙盒屏蔽的页面也可以使用 iframe 嵌入至另一个应用页面:

<!DOCTYPE html>
<html>
<head>
</head>
  <body>
    <p>I am normal app window.</p>

    <iframe src="sandboxed.html" width="300" height="200"></iframe>
  </body>
</html>

向经过沙盒屏蔽的页面发送消息

发送消息需要两方面同时进行:您需要从发送者页面/窗口投递消息,并在接收端页面/窗口监听消息。

投递消息

您可以使用 postMessage 在您的应用与经过沙盒屏蔽的内容间通信。如下是一个后台脚本的示例,向它打开的经过沙盒屏蔽的页面发送消息:

var myWin = null;

chrome.app.runtime.onLaunched.addListener(function() {
  chrome.app.window.create('sandboxed.html', {
    'bounds': {
      'width': 400,
      'height': 400
    }
  }, function(win) {
       myWin = win;
       myWin.contentWindow.postMessage('只是打个招呼。', '*');
     });
});

一般从网页的角度来说,您希望指定消息发送方的准确来源。Chrome 应用无法访问经过沙盒屏蔽的内容的唯一来源,所以您只能将所有来源添加到可接受来源的白名单中('*')。在接收端,您通常希望检查来源,但是由于 Chrome 应用的内容是受限的,这没有必要。要想了解更多内容,请参见 window.postMessage

监听消息

如下是消息接收端的示例,可以添加到经过沙盒屏蔽的页面中去:

var messageHandler = function(e) {
  console.log('后台脚本打了个招呼。', e.data);
};

window.addEventListener('message', messageHandler);