控制网页frame vba_V8 bindings 设计isolate,context,world,frame之间的关系(翻译)

前言

最近看了下V8 binding相关的技术文档,主要是学习下chrome如何使用V8的isolate和context,所以就看了下英文文档,顺便翻译一下。英文文档链接https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/renderer/bindings/core/v8/V8BindingDesign.md

这篇文章主要讲解V8 binding层的架构的几个关键概念,除了DOM wrappers的生命管理周期

目录

1. isolate (隔离)

2. Context (上下文)

3. Entered context and current context( 输入上下文和当前上下文)

4. World

5. A relationship between isolates, contexts, worlds and frames

6. DOM wrappers and worlds

7. DOM wrappers and contexts

isolate (隔离)

在V8中一个isolate是V8的一份实例。在blink中isolate和线程是1:1的关系。主线程与一个isolate相关联,一个工作线程与一个隔离相关联,但是也有个例外compositor worker是多个共享一个isolate

Context 上下文

Context是V8中全局变量范围的概念。简单的说,一个Window对象对应于一个Context。例如<iframe>和parent frame的有不同的Window对象,所以不同的frame具有不同的Context。由于每个Context创建了自己的全局变量和作用域,因此<iframe>的全局变量和原型链与parent frame的全局变量和原型链是隔离的

例子:

// main.html
<html><body>
<iframe src="iframe.html"></iframe>
<script>
var foo = 1234;
String.prototype.substr =
    function (position, length) { // Hijacks String.prototype.substr
        console.log(length);
        return "hijacked";
    };
</script>
</body></html>

// iframe.html
<script>
console.log(foo);  // undefined
var bar = "aaaa".substr(0, 2);  // Nothing is logged.
console.log(bar);  // "aa"
</script>

总之,每个frame都有一个Window对象。每个Window对象都有一个Context。每个Context都有自己的全局变量范围和原型链。

Entered context and current context

isolate和Context之间的关系是比较有趣的。一个isolate会在多个frame中执行JavaScripts,每个frame都有自己的context。这个意思就是一个isolate下的Context是会变化的。换句话说,isolate和Context之间的关系是1:N

这里我们有一个Entered context和current context的概念。要了解差异,您需要了解两种运行时堆栈:

第一个堆栈是JavaScript函数堆栈。该堆栈由V8管理。当一个函数调用另一个函数时,被调用函数入堆栈。当该函数返回时,该函数从堆栈出栈,然后返回到现在位于堆栈顶部的调用函数。每个函数都有一个相关的Context,我们将当前正在运行的函数的上下文(即,堆栈顶部的函数的上下文)称为current context

看以下例子:

// main.html
<html><body>
<iframe src="iframe.html"></iframe>
<script>
var iframe = document.querySelector("iframe");
iframe.onload = function () {
    iframe.contentWindow.func();
}
</script>
</body></html>

// iframe.html
<script>
function func() {
  ...;
}
</script>

在上面的示例中,在运行func()时,current context指的是<iframe>的context。

第二个堆栈以更粗糙的粒度运行。该堆栈由V8绑定(而不是V8)管理。当V8绑定调用JavaScript时,V8绑定进入context并将context推送到堆栈。 JavaScript开始在context中运行。当JavaScript完成并且控件返回到V8绑定时,V8绑定会从堆栈中弹出上下文。鉴于V8绑定和V8之间的控制可以嵌套(即,V8绑定调用JavaScript,调用V8绑定,调用另一个JavaScript等),这些context形成堆栈。推送和弹出是由任何V8 API完成的,它采用上下文参数或显式调用v8 :: Context :: Enter()和v8 :: Context :: Exit()。我们将最近输入的context称为Entered context。

在上面的示例中,在运行func()时,Entered context是main frame的context(而不是<iframe>的context)。

Entered context是实现HTML规范的条目设置对象的概念。当前上下文是实现HTML规范的现任设置对象的概念。

总之,Entered context是从中开始当前JavaScript执行的context。current context是当前正在运行的JavaScript函数的context。

还有另一个称为调试器上下文的特殊上下文。如果调试器处于活动状态,则可以将调试器上下文插入到上下文堆栈中

World

World是在Chrome扩展的内容脚本中沙盒DOM wrappers的概念. 这里三种World类型:

1. main world

2. isolated world

3.worker world

main world用于执行网页的JavaScript脚本

isolated world用于执行Chrome扩展程序的内容脚本

主线程的isolate有1个main world和N个isolated worlds

work thread只有1 worker world ,但是没有isolated world

下图有助于理解他们之间的关系

dfb8a23e61ce942482c1350bd64fae1f.png
world关系图

一个isolate中的所有Worlds共享底层C ++ DOM对象,但每个World都有自己的DOM wrappers 。这样一个隔离区中的世界可以在相同的C ++ DOM对象上运行,而无需在World中共享任何DOM wrapper

每个World都有自己的上下文。这意味着每个World都有自己的全局变量范围和原型链

作为沙盒的结果,一个isolate中的World不能共享任何DOM wrappers 或上下文,但可以共享底层C ++ DOM对象。没有共享DOM wrappers 或上下文这一事实意味着World之间不会共享任何JavaScript对象。这样我们就可以保证Chrome扩展在共享底层C ++ DOM对象时不共享任何JavaScript对象的安全模型。此沙箱允许Chrome扩展在共享DOM结构上运行不受信任的JavaScripts。

(注意: isolated world是V8绑定的概念,而isolate和上下文是V8的概念.V8不知道isolated worlds 是什么。)

总之,主线程的isolate由1个main world和N 个isolated worlds组成。worker thread的isolate 由1个 worker world 和 0 isolated world组成。一个隔离中的所有World共享底层的C ++ DOM对象,但每个World都有自己的DOM wrappers。每个World都有自己的上下文,因此有自己的全局变量范围和原型链。

isolates, contexts, worlds and frames 之间的关系

总结一下isolates,contexts,worlds 和frames之间的关系:

DOM端的要求,一个HTML页面具有N个frame。每个frame都有自己的context

JavaScript端的要求,一个isolate具有M个world。每个world都有context

结果,当我们执行涉及N Frame 和M个world的主线程时,存在N * M个上下文。换句话说,为每对(frame,world)创建一个上下文。下图有助于理解这种关系:

0ec3b6fe2d48d4ebeddc11766dec02b9.png
frame,Context,world之间的关系

主线程一次只能有一个current context,但主线程在其生命周期内可以有N * M个上下文。例如,当主线程使用World Y中的JavaScript在frame X上操作时,当前上下文被设置为(X,Y)对的上下文。主线程的当前上下文在其生命周期中发生变化。

另一方面,work thread 有0 frame 和1 World。因此,工作线程只有1个context。工作线程的current context永远不会改变。

DOM wrappers and contexts

出于兼容性原因,只要底层C ++ DOM对象处于活动状态,我们就需要确保将相同的DOM wrapper 返回给JavaScript。我们不应该为同一个C ++ DOM对象返回不同的DOM包装器。

以下是代码例子

var div = document.createElement("div");
div.foo = 1234;  // expando
var p = document.createElement("p");
p.appendChild(div);
div = null;
gc();
console.log(p.firstChild.foo);  // This should be 1234, not undefined

要实现只要底层C ++ DOM对象处于活动状态,同一DOM wrapper 返回到JavaScript的语义,我们需要从C ++ DOM对象到DOM包装器的映射。另外,我们需要在每个world中沙箱DOM wrapper 。为了满足这些要求,我们让每个world都拥有一个DOM wrapper 存储,它存储从C ++ DOM对象到该世界中DOM wrapper 的映射。

因此,我们在一个隔离中有多个DOM wrapper 存储。main World 的映射用ScriptWrappable编写。如果ScriptWrappable :: main_world_wrapper_具有非空值,则它是main World 的C ++ DOM对象的DOM wrapper 。其他world的映射是在DOMWrapperMap中编写的。

DOM wrappers and contexts

创建新的DOM wrapper 时,需要选择创建DOM wrapper 的正确上下文。如果在错误的上下文中创建新的DOM包装器,最终会将JavaScript对象泄漏到其他上下文,这很可能会导致安全问题。

// main.html
<html><body>
<iframe src="iframe.html"></iframe>
<script>
var iframe = document.querySelector("iframe");
iframe;  // The wrapper of the iframe should be created in the context of the main frame.
iframe.contentDocument;  // The wrapper of the document should be created in the context of the iframe.
iframe.contentDocument.addEventListener("click",
    function (event) {  // The wrapper of the event should be created in the context of the iframe.
        event.target;
    });
</script>
</body></html>

// iframe.html
<script>
</script>

要确保在正确的上下文中创建DOM wrapper ,您需要确保在调用ToV8()时必须将当前上下文设置为正确的上下文。

转载注明出处:

麦子:V8 bindings 设计isolate,context,world,frame之间的关系(翻译)​zhuanlan.zhihu.com
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值