我可以跨子域使用indexeddb吗?

时间:2014-06-21 16:22:57

标签: javascript google-chrome-extension indexeddb subdomain

我正在构建Chrome扩展程序并使用db.js包装器来使用indexeddb。问题是,我有几个子域名,我希望能够在它们之间共享信息。

当我使用Chrome开发工具查看资源时,所有单个子域都有自己创建的架构副本,每个子域都有自己的数据。

我唯一知道的尝试是设置document.domain,但这并没有帮助。我并不感到惊讶。

看起来indexeddb上的文档很小。我一直在几个不同的博客中逐字逐句地复制相同的2或3篇博文,并且没有任何内容指出这是可能的或不可能的。

3 个答案:

答案 0 :(得分:3)

您无法从多个子域访问同一个数据库,访问范围仅限于html origin

html_Origin = protocol + "://" + url + ":" + port + "/";

答案 1 :(得分:0)

Chrome扩展程序中基于HTML的存储(indexedDB,localStorage)的行为可能不是预期的,但它非常自然。

  1. 在后台页面中,域名为chrome-extension://yourextensionid/,这是所有扩展程序页面共享的,并且是持久的。

  2. 但是,在内容脚本中,您要与您正在操作的域共享HTML存储空间。如果你想让它分享/坚持下去,这会让生活变得困难。请注意,有时这种行为实际上很有帮助。

  3. 通用解决方案是将DB保留在后台脚本中,并通过Messaging API传递数据/请求。

    localStorage出现之前,这是chrome.storage使用的常用解决方案。但是,由于您使用的是数据库,因此您不需要进行随时可用的扩展程序替换。

答案 2 :(得分:0)

正如@Xan所提到的,如果您可以使用扩展本身(而不是内容页面)拥有的通用来源,那似乎是迄今为止最简单的解决方案。如果出于某种原因您无法执行此操作(或者对于那些想了解常规页面javascript或Greasemonkey样式的用户脚本而不是扩展名的读者),答案是:

是的,尽管有点尴尬并且需要一些工作:

由于您使用的是多个相关子域(而不是完全不相关的域),因此可以在这种情况下使用一种技术。它可以应用于IndexedDBlocalStorageSharedWorkerBroadcastChannel等,所有这些元素在同源页面之间提供共享功能,但是由于某些原因不尊重对document.domain的修改。

(1)选择一个“主”子域作为数据所属。例如,如果您的子域是 https://a.example.com https://b.example.com https://c.example.com ,则您可能会选择您的IndexedDB数据库存储在 https://a.example.com 子域下。

(2)在所有 https://a.example.com 页中正常使用。

(3)在 https://b.example.com https://c.example.com 上,使用javascript设置document.domain = "example.com";。然后还创建一个隐藏的<iframe>,并将其导航到 https://a.example.com 域上的 some 页面( >什么页面,只要您可以在其中插入一小段javascript代码即可。如果要创建该网站,只需为此专门制作一个空白页面即可。扩展名或用户脚本,因此对 example.com 服务器上的页面没有任何控制,只需选择您能找到的最轻巧的页面,然后将脚本插入其中即可。找到”页面可能就可以了。

(4)隐藏的iframe页面上的脚本仅需要(a)设置document.domain = "example.com";,并且(b)完成后通知父窗口。之后,父窗口可以不受限制地访问iframe窗口及其所有对象!因此,最小的iframe页面类似于:

<!doctype html>
<html>
<head>
  <script>
    document.domain = "example.com";
    window.parent.iframeReady();  // function defined & called on parent window
  </script>
</head>
<body></body>
</html>

如果编写用户脚本,则可能不希望将iframeReady()之类的可外部访问的功能添加到unsafeWindow中,因此,通知主窗口用户脚本的一种更好的方法可能是使用自定义事件:

    window.parent.dispatchEvent(new CustomEvent("iframeReady"));

您会通过在首页窗口中添加自定义“ iframeReady”事件的侦听器来进行检测。

(5)一旦隐藏的iframe通知其父窗口已经准备好,父窗口中的脚本就可以使用iframe.contentWindow.indexedDBiframe.contentWindow.localStorageiframe.contentWindow.BroadcastChanneliframe.contentWindow.SharedWorker而不是window.indexedDBwindow.localStorage等。...所有这些对象的作用域将以 https://a.example.com 的原始范围为准-因此它们将具有相同的共享您所有网页的来源!

该技术的“尴尬”部分主要是您必须等待iframe加载后才能继续。因此,例如,您不能仅仅在DOMContentLoaded处理程序中轻松地初始化IndexedDB。另外,您可能需要添加一些错误处理,以检测隐藏的iframe是否无法正确加载。

很显然,您还应该确保在页面生命周期中未删除或导航隐藏的iframe ... OTOH我不知道这样做的结果,但是很可能会发生坏事。 / p>

而且,需要警告:可以使用document.domain标头阻止设置/更改Feature-Policy,在这种情况下,该技术将无法按所述使用。


但是,这种技术的概括要复杂得多,Feature-Policy不能阻止它,并且允许完全不相关的域共享数据,通信和共享的工作器< / strong>(即不只是普通超级域的子域)。 @Xan在答案的第(2)点中提到了这一点:

总体思路是,如上所述,您创建了一个隐藏的iframe以提供正确的访问源;但是,您不仅可以直接获取iframe窗口的属性,还可以在iframe中使用脚本来完成所有工作,并且仅使用postMessage()addEventListener("message",...)在iframe和主窗口之间进行通信。 / p>

之所以可行,是因为postMessage()甚至可以在不同来源的窗口之间使用。但这也要复杂得多,因为您必须将所有内容都通过在iframe和主窗口之间创建的某种消息传递基础结构进行传递,而不是(例如)仅在主窗口代码中直接使用IndexedDB API。