diff options
Diffstat (limited to 'files/zh-cn/mozilla/firefox/multiprocess_firefox')
16 files changed, 0 insertions, 2078 deletions
diff --git a/files/zh-cn/mozilla/firefox/multiprocess_firefox/cross_process_object_wrappers/index.html b/files/zh-cn/mozilla/firefox/multiprocess_firefox/cross_process_object_wrappers/index.html deleted file mode 100644 index 38fde2acbf..0000000000 --- a/files/zh-cn/mozilla/firefox/multiprocess_firefox/cross_process_object_wrappers/index.html +++ /dev/null @@ -1,114 +0,0 @@ ---- -title: 跨进程对象包装器 -slug: Mozilla/Firefox/Multiprocess_Firefox/Cross_Process_Object_Wrappers -translation_of: Mozilla/Firefox/Multiprocess_Firefox/Cross_Process_Object_Wrappers ---- -<div>{{FirefoxSidebar}}</div><div class="summary"> -<p>本文档介绍了 Cross Process Object Wrappers (CPOWs),这使 chrome 代码能够同步访问多进程 Firefox 中的内容。</p> -</div> - -<p>在多进程 Firefox 中,chrome 代码运行在与 Web 内容不同的另一个进程中。因此 chrome 代码不能直接与 Web 内容交互;相反,它必须考虑将与 Web 内容交互的脚本放在单独的脚本中,这被称为框架脚本(frame scripts),也称帧脚本。</p> - -<p>Chrome 代码可以使用消息管理器加载框架脚本到内容进程,然后可以使用消息传递 API 与它们通信。有关于此的更多信息,详见 <a href="/en-US/docs/The_message_manager">消息管理器</a> 的使用文档。</p> - -<p>Chrome 到内容的通信必须是异步的。这是因为 chrome 进程运行着 Firefox UI,因此如果被内容进程所影响,缓慢的内容进程可能致使 Firefox 对用户无响应。<br> - <br> - 将同步代码转换成异步可能是困难并且耗时的。作为一个迁移的辅助,消息框架使框架脚本变成了内容对象,通过一个被称为 Cross Process Object Wrapper(简称 CPOW)的包装器,使其在 chrome 中可用。但是,尽管 CPOWs 很方便,它们存在<a href="/en-US/docs/Cross_Process_Object_Wrappers#Limitations_of_CPOWs">严重的局限性并且可能导致响应性问题</a>,因此只应在必要时使用,并仅作为迁移的辅助。</p> - -<h2 id="从框架脚本传递_CPOWs">从框架脚本传递 CPOWs</h2> - -<p>框架脚本可以发送消息到 chrome,使用两个全局函数之一:<a href="/en-US/docs/The_message_manager#Content_to_chrome"><code>sendAsyncMessage()</code> 或者 <code>sendSyncMessage()</code></a>。这些函数的第三个可选参数是被包装的属性对象。举例来说,框架脚本在用户点击它时发送一个 DOM 节点到 chrome,并将 <code>clicked</code> 属性作为第三个参数:</p> - -<pre class="brush: js">// frame script -addEventListener("click", function (event) { - sendAsyncMessage("my-e10s-extension-message", {}, { clicked : event.target }); -}, false);</pre> - -<p>在 chrome 脚本中,DOM 节点现在是通过 Cross Process Object Wrapper 访问,作为该消息的 <code>objects</code> 属性的个属性。chrome 脚本可以获得和设置包装的对象属性,以及调用它的函数:</p> - -<pre class="brush: js">// chrome script -windowMM.addMessageListener("my-e10s-extension-message", handleMessage); - -function handleMessage(message) { - let wrapper = message.objects.clicked; - console.log(wrapper.innerHTML); - wrapper.innerHTML = "<h2>已被 chrome 修改!</h2>" - wrapper.setAttribute("align", "center"); -}</pre> - -<h2 id="自动生成的_CPOWs">自动生成的 CPOWs</h2> - -<p>没有自我声明多进程兼容的附加组件会加载一些<a href="/en-US/Firefox/Multiprocess_Firefox/Limitations_of_chrome_scripts#Compatibility_shims">兼容性垫片</a>。其中一个垫片提供了以下行为:每当 chrome 代码尝试直接访问内容(例如通过 <a href="/en/docs/Working_with_windows_in_chrome_code#The_content_shortcut"><code>window.content</code></a> 或者 <a href="/en-US/docs/Mozilla/Tech/XUL/browser#p-contentDocument"><code>browser.contentDocument</code></a>),提供一个包装了内容的 CPOW。这意味着下面这样的例子在多进程 Firefox 中也能正常工作。</p> - -<pre class="brush: js">gBrowser.selectedBrowser.contentDocument.body.innerHTML = "被 chrome 代码替换";</pre> - -<p>但仍然要记住,这是通过 CPOW 访问,并不是直接访问内容。</p> - -<h2 id="双向_CPOWs">双向 CPOWs</h2> - -<p>一个常见的模式是 chrome 代码访问内容对象并添加事件监听器到那里。为了解决这个问题,CPOWs 是双向的。这意味着如果内容传递了一个 CPOW 到 chrome 进程,chrome 进程可以同步传递对象(如事件监听器函数)到 CPOW 中定义的函数。</p> - -<p>这意味着你可以写这样的代码:</p> - -<pre class="brush: js">// frame script - -/* -在 mouseover,发送 button 到 chrome 脚本,以一个CPOW形式。 -*/ - -var button = content.document.getElementById("click-me"); - -button.addEventListener("mouseover", function (event) { - sendAsyncMessage("my-addon-message", {}, { element : event.target }); -}, false);</pre> - -<pre class="brush: js">// chrome script - -/* -载入框架脚本,然后监听消息。 -在我们得到消息时,提取 CPOW 并添加一个函数作为监听器到按钮的 "click" 事件。 -*/ - - browserMM.loadFrameScript("chrome://my-addon/content/frame-script.js", false); - browserMM.addMessageListener("my-addon-message", function(message) { - let wrapper = message.objects.element; - wrapper.addEventListener("click", function() { - console.log("被点击了"); - }); - }); -</pre> - -<h2 id="映射内容文档到_XUL_浏览器">映射内容文档到 XUL 浏览器</h2> - -<p>一个常见的模式是获取 XUL <a href="/en-US/docs/XUL/browser"><code><browser></code></a>,它对应一个内容文档。要做到这点, <code>gBrowser.getBrowserForDocument</code> <code>和 gBrowser.getBrowserForContentWindow</code> 分别可以传递一个内容文档和内容窗口的 CPOW,并且返回这些文档 / 窗口所属的 XUL <code><browser>。</code>如果没有找到这样的浏览器,两者都是返回 null。</p> - -<h2 id="CPOWs_的限制">CPOWs 的限制</h2> - -<p>尽管 CPOWs 可以方便的使用,但它有几个主要的局限性,在下面列出。</p> - -<h3 id="CPOWs_与平台_API">CPOWs 与平台 API</h3> - -<p>你不能传递 CPOWs 到预期会收到 DOM 对象的平台 API。举例来说,你不能传递一个 CPOW <code>到 </code><a href="http://dxr.mozilla.org/mozilla-central/source/dom/interfaces/base/nsIFocusManager.idl"><code>nsIFocusManager.setFocus()</code></a>。</p> - -<h3 id="Chrome_响应性">Chrome 响应性</h3> - -<p>在 chrome 这边缺少同步 API 是有意的:因为 chrome 进程运行着 Firefox UI,任何响应性问题都将影响整个浏览器。在制成 chrome 进程与内容进程的过程中,CPOWs 打破了这个原则,并且致使内容进程可能使整个浏览器陷入无响应状态。</p> - -<h3 id="性能">性能</h3> - -<p>尽管包装器看起来像是一个完全在 chrome 脚本范围下管控的对象,但它实际上只是一个到内容进程中一个对象的引用。在你访问一个包装器的属性时,它发送一个同步消息到内容进程及返回结果。这意味着它比使用一个对象慢很多倍。</p> - -<h3 id="消息顺序">消息顺序</h3> - -<p>CPOWs 可能违反你做出的有关消息排序的假设。考虑以下代码:</p> - -<pre class="brush: js">mm.addMessageListener("GotLoadEvent", function (msg) { - mm.sendAsyncMessage("ChangeDocumentURI", {newURI: "hello.com"}); - let uri = msg.objects.document.documentURI; - dump("收到加载事件: " + uri + "\n"); -}); -</pre> - -<p>这发送了一个消息,要求框架脚本更改当前文档的 URI,然后通过一个 CPOW 访问当前的文档 URI。你可能预期 <code>uri</code> 的值得到设置的 "hello.com"。但这不一定:为了避免死锁,CPOW 消息可以绕过正常的消息并且被优先处理。对 documentURI 属性的请求有可能在 "ChangeDocumentURI" 的消息之前被处理,并因而 <code>uri</code> 持有它在更改之前的值。<br> - <br> - 出于这个原因,最好不要混用 CPOWs 和正常的消息管理器消息。还有一个坏主意是将 CPOWs 用于任何安全相关,因为你可能获得不一致的结果,与使用消息管理器的相关代码。</p> diff --git a/files/zh-cn/mozilla/firefox/multiprocess_firefox/debugging_frame_scripts/index.html b/files/zh-cn/mozilla/firefox/multiprocess_firefox/debugging_frame_scripts/index.html deleted file mode 100644 index 57c1b3412a..0000000000 --- a/files/zh-cn/mozilla/firefox/multiprocess_firefox/debugging_frame_scripts/index.html +++ /dev/null @@ -1,53 +0,0 @@ ---- -title: 调试框架脚本 -slug: Mozilla/Firefox/Multiprocess_Firefox/Debugging_frame_scripts -translation_of: Mozilla/Firefox/Multiprocess_Firefox/Debugging_frame_scripts ---- -<div>{{FirefoxSidebar}}</div><div class="geckoVersionNote"> -<p>浏览器内容工具箱仅在 Firefox Nightly 中可用,并且仅在多进程模式下可用。</p> -</div> - -<p>你可以使用浏览器内容工具箱来调试框架脚本。浏览器内容工具箱是一个单独的窗口,它包括一些共享的 <a href="/en-US/docs/Tools">Firefox 开发者工具</a>,具体来说:<a href="/en-US/docs/Web/API/Console">控制台</a>,<a href="/en-US/docs/Tools/Debugger">JavaScript 调试器</a>,以及<a href="/en-US/docs/Tools/Scratchpad">代码草稿纸</a> —— 但它们着重于浏览器的内容进程。这意味着你可以调试你的附加组件中的框架脚本。</p> - -<h2 id="打开浏览器内容工具箱">打开浏览器内容工具箱</h2> - -<p>{{EmbedYouTube("Cg6X_zIu7Xk")}}</p> - -<p>要打开浏览器内容工具箱,你需要:</p> - -<ul> - <li>正在运行 Firefox Nightly</li> - <li>多进程 Firefox 已启用:打开“选项”页面,选择"启用 E10S (多进程)“,然后重新启动。在你运行多进程 Firefox 时,一个虚拟指示符会出现,远程选项卡的标题会有下划线。</li> - <li>在 Firefox 开发者工具的设置中启用 <a href="/en-US/docs/Tools/Tools_Toolbox#Settings">"chrome 和附加组件调试" 和 "远程调试"</a>。</li> -</ul> - -<p>你应该已经在 Firefox 菜单中 ”Web 开发者“的子菜单中看到”浏览器内容工具箱“(或者工具菜单,如果你显示了菜单栏,或者在 OS X)。它会打开一个单独的窗口:</p> - -<p><img alt="" src="https://mdn.mozillademos.org/files/9761/browser-content-toolbox.png" style="display: block; height: 854px; margin-left: auto; margin-right: auto; width: 723px;">If you've used the <a href="/en-US/docs/Tools/Tools_Toolbox">Firefox Developer Tools</a> before, this should look pretty familiar.</p> - -<p>Along the top is a row of tabs that you can use to switch the active tool. Currently we only support the <a href="/en-US/docs/Web/API/Console">Console</a>, the <a href="/en-US/docs/Tools/Debugger">Debugger</a>, and <a href="/en-US/docs/Tools/Scratchpad">Scratchpad</a> in the Browser Content Toolbox. At the right of this row are three buttons that activate the <a href="/en-US/docs/Tools/Web_Console#The_split_console">split console</a>, open settings, and close the toolbox.</p> - -<p>The rest of the toolbox is taken up with the tool you've currently selected.</p> - -<h2 id="使用">使用</h2> - -<p>{{EmbedYouTube("XF0ULNnNOxg")}}</p> - -<h3 id="调试器">调试器</h3> - -<p>The Debugger lists all the scripts that are loaded into the content process. You'll find your frame scripts listed under the chrome:// URL you registered for them:</p> - -<p><img alt="" src="https://mdn.mozillademos.org/files/9771/browser-content-toolbox-listing.png" style="display: block; margin-left: auto; margin-right: auto; width: 723px;">You can set breakpoints, of course, and do all the other <a href="/en-US/docs/Tools/Debugger">things supported by the debugger</a>.</p> - -<h3 id="控制台">控制台</h3> - -<p>The Console logs output from your frame scripts. If you want to use it to evaluate JavaScript in your frame script's scope, there's a trick you need to know:</p> - -<ul> - <li>在你的框架脚本中设置一个断点。</li> - <li>When you hit the breakpoint, switch to the Console, or activate the <a href="/en-US/docs/Tools/Web_Console#The_split_console">split console</a> by pressing "Escape".</li> -</ul> - -<p>Now the console's scope is your frame script's scope, and you can interact directly with it:</p> - -<p><img alt="" src="https://mdn.mozillademos.org/files/9769/browser-content-toolbox-debugging.png" style="display: block; margin-left: auto; margin-right: auto; width: 839px;"></p> diff --git a/files/zh-cn/mozilla/firefox/multiprocess_firefox/faq/index.html b/files/zh-cn/mozilla/firefox/multiprocess_firefox/faq/index.html deleted file mode 100644 index 3299dd0856..0000000000 --- a/files/zh-cn/mozilla/firefox/multiprocess_firefox/faq/index.html +++ /dev/null @@ -1,18 +0,0 @@ ---- -title: FAQ -slug: Mozilla/Firefox/Multiprocess_Firefox/FAQ -translation_of: Mozilla/Firefox/Multiprocess_Firefox/FAQ ---- -<div>{{FirefoxSidebar}}</div><h2 id="使命">使命</h2> - -<p> </p> - -<h2 id="历史">历史</h2> - -<p> </p> - -<h2 id="方式">方式</h2> - -<p> </p> - -<h2 id="影响">影响</h2> diff --git a/files/zh-cn/mozilla/firefox/multiprocess_firefox/index.html b/files/zh-cn/mozilla/firefox/multiprocess_firefox/index.html deleted file mode 100644 index 2936128948..0000000000 --- a/files/zh-cn/mozilla/firefox/multiprocess_firefox/index.html +++ /dev/null @@ -1,78 +0,0 @@ ---- -title: 多进程 Firefox -slug: Mozilla/Firefox/Multiprocess_Firefox -tags: - - NeedsTranslation - - TopicStub -translation_of: Mozilla/Firefox/Multiprocess_Firefox ---- -<div>{{FirefoxSidebar}}</div><p>在目前版本的桌面版 Firefox 中,整个浏览器运行在单个操作系统进程中。尤其是 JavaScript 在同一进程中运行着用户界面(UI,也称 "chrome 代码"),它还搭载着所有网页(也称“内容”,即“标签页”)。<br> - <br> - 未来版本的 Firefox 将在单独的进程中运行浏览器界面,与网页内容的进程分离。这种架构的第一次迭代是所有浏览器标签页在同一个进程中运行,浏览器界面运行在另一个进程中。在未来的迭代中,我们期望有一个以上的内容进程。提供多进程 Firefox 的项目名为 Electrolysis,有时被简称为 e10s。</p> - -<p>普通的网页不会受到多进程 Firefox 的影响。Firefox 本身和 Firefox 附加组件的开发者将受到影响,如果他们的代码依赖于能直接访问 Web 内容。</p> - -<p>不同于此前的直接访问内容,chrome JavaScript 将使用<a href="/Firefox/Multiprocess_Firefox/Message_manager">消息管理器</a>来访问内容。为了帮助缓解过渡期,我们实现了<a href="/Firefox/Multiprocess_Firefox/Cross_Process_Object_Wrappers">跨进程对象包装器(CPOW)</a>和一些<a href="/Firefox/Multiprocess_Firefox/Limitations_of_chrome_scripts#Compatibility_shims">面向附加组件开发者的兼容性垫片</a>。如果你是一名附加组件开发者并且想知道自己是否受到影响,参见<a href="/Mozilla/Add-ons/Working_with_multiprocess_Firefox">多进程 Firefox 工作指南</a>。</p> - -<p>多进程 Firefox 目前在 <a class="external external-icon" href="https://www.mozilla.org/firefox/developer/">开发者版本</a> 默认启用。</p> - -<hr> -<div class="column-container"> -<div class="column-half"> -<dl> - <dt><a href="/Firefox/Multiprocess_Firefox/Technical_overview">技术概述</a></dt> - <dd>高等层面看待多进程 Firefox 如何被实现。</dd> - <dt><a href="/Firefox/Multiprocess_Firefox/Glossary">术语表</a></dt> - <dd>多进程 Firefox 领域相关的术语参考。</dd> - <dt><a href="/Firefox/Multiprocess_Firefox/Message_Manager">消息管理器</a></dt> - <dd>完整的指南,在 chrome 与内容之间通信的对象。</dd> - <dt><a href="/en-US/Add-ons/SDK/Guides/Multiprocess_Firefox_and_the_SDK">基于 SDK 的附加组件</a></dt> - <dd>如何迁移使用 Add-on SDK 开发的附加组件。</dd> - <dt><a href="/Firefox/Multiprocess_Firefox/Which_URIs_load_where">各类 URI 在哪里加载</a></dt> - <dd>各类 URI 的快速指南:chrome:, about:, file:, resource: - 在哪个进程被加载。</dd> -</dl> -</div> - -<div class="column-half"> -<dl> - <dt><a href="/Firefox/Multiprocess_Firefox/Motivation">动机</a></dt> - <dd>为什么我们要实现多进程的 Firefox:性能、安全和稳定性。</dd> - <dt><a href="/Mozilla/Add-ons/Working_with_multiprocess_Firefox">附加组件迁移指南</a></dt> - <dd>如果你是一名附加组件开发者,看看你的影响,以及如何更新你的代码。</dd> - <dt><a href="/Firefox/Multiprocess_Firefox/Cross_Process_Object_Wrappers">跨进程对象包装器(CPOW)</a></dt> - <dd>Cross Process Object Wrappers 是一个迁移辅助,使 chrome 代码能够访问内容。</dd> - <dt><a href="/Firefox/Multiprocess_Firefox/Debugging_frame_scripts">调试内容进程</a></dt> - <dd>如何调试运行在内容进程中的代码,包括框架和进程脚本。</dd> - <dt><a href="/docs/Mozilla/Firefox/Multiprocess_Firefox/Tab_selection_in_multiprocess_Firefox">多进程 Firefox 中的标签选择</a></dt> - <dd>多进程 Firefox 中如何切换标签页。</dd> -</dl> -</div> -</div> - -<hr> -<div class="column-container"> -<div class="column-half"> -<dl> - <dt><a href="/Firefox/Multiprocess_Firefox/Limitations_of_chrome_scripts">chrome 脚本的限制</a></dt> - <dd>哪些 chrome 代码的做法将不再有效,以及如何解决。</dd> -</dl> -</div> - -<div class="column-half"> -<dl> - <dt><a href="/Firefox/Multiprocess_Firefox/Limitations_of_frame_scripts">框架脚本的限制</a></dt> - <dd>哪些框架脚本的做法将不再有效,以及如何代替。</dd> -</dl> -</div> -</div> - -<hr> -<h2 id="联系我们">联系我们</h2> - -<p>有关此项目的更多信息、参与或提问。</p> - -<ul> - <li><strong>Electrolysis 项目页面</strong>: <a href="https://wiki.mozilla.org/Electrolysis">https://wiki.mozilla.org/Electrolysis</a></li> - <li><strong>IRC</strong>: #e10s on <a href="https://wiki.mozilla.org/IRC">irc.mozilla.org</a></li> - <li><strong>邮件列表</strong>: <a href="https://groups.google.com/forum/#!forum/mozilla.dev.tech.electrolysis">dev.tech.electrolysis</a></li> -</ul> diff --git a/files/zh-cn/mozilla/firefox/multiprocess_firefox/limitations_of_chrome_scripts/index.html b/files/zh-cn/mozilla/firefox/multiprocess_firefox/limitations_of_chrome_scripts/index.html deleted file mode 100644 index 130ce276e3..0000000000 --- a/files/zh-cn/mozilla/firefox/multiprocess_firefox/limitations_of_chrome_scripts/index.html +++ /dev/null @@ -1,197 +0,0 @@ ---- -title: chrome 脚本的限制 -slug: Mozilla/Firefox/Multiprocess_Firefox/Limitations_of_chrome_scripts -translation_of: Mozilla/Firefox/Multiprocess_Firefox/Limitations_of_chrome_scripts ---- -<div>{{FirefoxSidebar}}</div><p>This page describes patterns that used to work in the chrome process that will no longer work in multiprocess Firefox. These are the sorts of things that will break an old add-on in multiprocess Firefox. The fix is generally some variant of "do that in a frame script loaded into the content process".</p> - -<p>This is one of a pair of articles: the other one lists <a href="/en-US/Firefox/Multiprocess_Firefox/Limitations_of_frame_scripts">limitations of frame scripts</a>.</p> - -<h2 id="兼容性垫片">兼容性垫片</h2> - -<p>For many of the patterns described here we've implemented compatibility shims so the patterns still work. For example: whenever extensions try to access web content from the chrome process, the browser will return a <a href="https://developer.mozilla.org/en-US/docs/Cross_Process_Object_Wrappers">Cross Process Object Wrapper</a> that gives the chrome code synchronous access to the content.</p> - -<p>You'll get the shims for your add-on by default, unless you set the <a href="/en-US/Add-ons/Install_Manifests#multiprocessCompatible"><code>multiprocessCompatible</code> flag in your add-on's install manifest</a>.</p> - -<p>However, these shims are not a substitute for migrating extensions:</p> - -<ul> - <li>they are only a temporary measure, and will be removed eventually</li> - <li>they can have a bad effect on responsiveness</li> - <li>there are likely to be edge cases in which they don't work properly</li> -</ul> - -<p>For each pattern we've noted:</p> - -<ul> - <li>whether a shim exists and what kind of behavior it provides</li> - <li>how to update your add-on so you don't need the shim</li> -</ul> - -<h2 id="gBrowser.contentWindow_window.content...">gBrowser.contentWindow, window.content...</h2> - -<h3 id="如果没有垫片">如果没有垫片</h3> - -<p>所有在 chrome 进程中的 API 提供的直接访问内容对象将不再工作。例如:</p> - -<pre class="brush: js">// chrome code - -gBrowser.contentWindow; // null - -gBrowser.contentDocument; // null - -gBrowser.selectedBrowser.contentWindow; // null - -window.content; // null - -content; // null -</pre> - -<p>特别说明,docshells 存在于内容进程,因此它们也无法访问:</p> - -<pre>gBrowser.docShell; // null - -gBrowser.selectedBrowser.docShell; // null</pre> - -<h3 id="如果有垫片">如果有垫片</h3> - -<p>在这些情况下,垫片为你通过一个 <a href="/en-US/Firefox/Multiprocess_Firefox/Cross_Process_Object_Wrappers">CPOW</a> 提供内容对象。</p> - -<p>In some situations, the content process may not be initialized when an add-on asks for access to its content. In this case, the shim will return a JavaScript object that looks somewhat like a window or a document for about:blank. However, this "dummy" object is completely static and only exposes a few of the normal properties that windows and documents have. For this reason, add-ons that try to access content objects in fresh <browser> elements may run into trouble.</p> - -<p>To make the shim unnecessary: factor the code that needs to access content into a separate script, load that script into the content process as a frame script, and communicate between the chrome script and the frame script using the message-passing APIs. See the article on <a href="/en-US/Firefox/Multiprocess_Firefox/The_message_manager">using the message manager</a>.</p> - -<h2 id="CPOW_的限制">CPOW 的限制</h2> - -<p><a href="/en-US/Firefox/Multiprocess_Firefox/Cross_Process_Object_Wrappers">跨进程对象包装器</a> (CPOWs) 是一个迁移辅助,给 chrome 代码带来同步访问内容对象的能力。但是,在使用它时也有各种<a href="/en-US/Firefox/Multiprocess_Firefox/Cross_Process_Object_Wrappers#Limitations_of_CPOWs">限制</a>。</p> - -<h2 id="nsIContentPolicy">nsIContentPolicy</h2> - -<h3 id="如果没有垫片_2">如果没有垫片</h3> - -<p>在多进程的 Firefox 上,你无法在 chrome 进程中使用 <a href="http://dxr.mozilla.org/mozilla-central/source/dom/base/nsIContentPolicy.idl"><code>nsIContentPolicy</code></a>,因为它需要接触网络内容。</p> - -<h3 id="如果有垫片_2">如果有垫片</h3> - -<p>The shim enables you to add content policies in the chrome process. It transparently registers an <code>nsIContentPolicy</code> in the content process, whose <code>shouldLoad</code> just forwards to the chrome process. The content to check is forwarded as a <a href="/en-US/Firefox/Multiprocess_Firefox/Cross_Process_Object_Wrappers">CPOW</a>. The chrome process then checks the content against the policy supplied by the add-on, and forwards the response back to the child to be enforced.</p> - -<p>为了使垫片不再必要,在内容进程中定义和注册 <code>nsIContentPolicy</code>。如果你需要确保该政策只注册一次,使用一个 <a href="/en-US/docs/User:wbamberg/Multiprocess_Firefox-2/Message_manager/Process_scripts">process 脚本</a> 来注册该政策。</p> - -<h2 id="nsIWebProgressListener">nsIWebProgressListener</h2> - -<p>这个 API 在 chrome 进程中工作。有一个垫片让你可以访问传递到 <code>onStateChange</code> 的<code> </code><a href="http://dxr.mozilla.org/mozilla-central/source/uriloader/base/nsIWebProgress.idl"><code>nsIWebProgress</code></a> 对象的 <code>DOMWindow</code> 属性。但是,该 <code>DOMWindow</code> 是异步传递,因此在 chrome 进程收到时,DOM 可能已经改变了(例如,因为代码运行的内容进程已经修改它,或者我们已经导航到另一个页面)。</p> - -<p>还要注意,不同于其他垫片,这个垫片始终有效。</p> - -<p>我们正在努力修复此问题,见 <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1118880">bug 1118880</a>。</p> - -<p>另外,你可以在内容进程中使用 <code>nsIWebProgressListener</code>。</p> - -<h2 id="chrome_进程中的_Observers">chrome 进程中的 Observers</h2> - -<p>根据不同的主题,你需要在 chrome 进程或者一个框架脚本中注册 observers。</p> - -<p>对于大多数主题,你需要在 chrome 进程中注册 observers。</p> - -<p>但是,你必须在一个框架脚本中监听 <a href="/en/docs/Observer_Notifications#Documents"><code>content-document-global-created</code> and <code>document-element-inserted</code></a>。这些主题的 <a href="http://dxr.mozilla.org/mozilla-central/source/xpcom/ds/nsIObserver.idl">Observers</a> 获取内容对象并作为 <code>aSubject</code> 参数到 <code>observe()</code>,因此通知不会发送到 chrome 进程。</p> - -<p>有一个垫片会将两个主题转发到 chrome 进程,将 <a href="/en-US/Firefox/Multiprocess_Firefox/Cross_Process_Object_Wrappers">CPOWs</a> 发送为 <code>aSubject</code> 参数。</p> - -<h3 id="HTTP_请求">HTTP 请求</h3> - -<p>你不能观测(observe)内容进程中的 <a href="https://developer.mozilla.org/en/docs/Observer_Notifications#HTTP_requests">HTTP 请求</a>。如果这样做,你将得到一个错误。<br> - <br> - 如果你在 chrome 进程中这样做,它一般会工作。observer 通知的主题将是一个 <code><a href="http://dxr.mozilla.org/mozilla-central/source/netwerk/protocol/http/nsIHttpChannel.idl">nsIHttpChannel</a></code>,正如你所期望的。</p> - -<p>A common pattern here is to use the <code>notificationCallbacks</code> property of the <code>nsIHttpChannel</code> to get the DOM window that initiated the load, like this:</p> - -<pre class="brush: js">observe: function (subject, topic, data) { - if (topic == "http-on-modify-request") { - var httpChannel = subject.QueryInterface(Ci.nsIHttpChannel); - var domWindow = httpChannel.notificationCallbacks.getInterface(Ci.nsIDOMWindow); - } -}</pre> - -<p>或者这样:</p> - -<pre class="brush: js">observe: function (subject, topic, data) { - if (topic == "http-on-modify-request") { - var httpChannel = subject.QueryInterface(Ci.nsIHttpChannel); - var domWindow = httpChannel.notificationCallbacks.getInterface(Ci.nsILoadContext).associatedWindow; - } -}</pre> - -<p>In multiprocess Firefox these patterns will no longer work: the <code>getInterface</code> call will fail.</p> - -<p>In multiprocess Firefox, <code>notificationCallbacks</code> is a special object that tries to emulate the single-process <code>notificationsCallbacks</code> object as best it can. It will return a dummy <code>nsILoadContext</code> when asked, but any attempt to get a window out of it will fail.<br> - <br> - There is an outstanding bug (<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1108827">bug 1108827</a>) to implement a shim here that will make <code>notificationCallbacks</code> a <a href="/en-US/Firefox/Multiprocess_Firefox/Cross_Process_Object_Wrappers">CPOW</a> for the objects in the content process.</p> - -<p>The correct way to access the DOM window is through a message manager. In an HTTP observer, you can get the browser message manager for the window using code like this:</p> - -<pre class="brush: js">observe: function (subject, topic, data) { - if (topic == "http-on-modify-request") { - var httpChannel = subject.QueryInterface(Ci.nsIHttpChannel); - var loadContext = httpChannel.notificationCallbacks.getInterface(Ci.nsILoadContext); - // topFrameElement is the <browser> element - var topFrameElement = loadContext.topFrameElement; - var browserMM = topFrameElement.messageManager; - console.log("browserMM: " + browserMM); - } -}</pre> - -<p>However, before Firefox 38, this technique will not work if multiprocess Firefox is disabled: specifically, <code>topFrameElement</code> will be null. This means that if you need to write code that works before Firefox 38 and on both multiprocess and non-multiprocess variants, you need to implement both paths:</p> - -<ul> - <li>test whether <code>topFrameElement</code> is null</li> - <li>if it is, you're running in single-process Firefox, and should use the old way</li> - <li>if it isn't, you're running in multiprocess Firefox and should use the new way</li> -</ul> - -<p>From Firefox 38 onwards, the topFrameElement approach always works.</p> - -<h2 id="DOM_事件">DOM 事件</h2> - -<h3 id="如果没有垫片_3">如果没有垫片</h3> - -<p>In multiprocess Firefox, if you want to register an event listener on some content DOM node, that needs to happen in the content process.</p> - -<p>It used to be that if you registered a listener on the <a href="/en-US/docs/XUL/browser">XUL <code><browser></code></a> or <a href="/en-US/docs/XUL/tab"><code><tab></code></a> element that hosted some DOM content, then events in the content would bubble up to the XUL and you could handle them there. This no longer happens in multiprocess Firefox.</p> - -<h3 id="如果有垫片_3">如果有垫片</h3> - -<p>The shim intercepts chrome process code that adds listeners to XUL elements and sets up listeners in the content process, relaying the result back to the chrome process. The <a href="/en-US/docs/Web/API/Event"><code>Event</code></a> object itself is relayed to the chrome process as a <a href="/en-US/Firefox/Multiprocess_Firefox/Cross_Process_Object_Wrappers">CPOW</a>.</p> - -<p>To make the shim unnecessary: register event listeners on the global object inside a <a href="/en-US/Firefox/Multiprocess_Firefox/The_message_manager">frame script</a>. For example:</p> - -<pre class="brush: js">addEventListener("load", handler, true) // for example</pre> - -<div>如果你需要在这时联系 chrome 进程,发送一个消息。</div> - -<div> </div> - -<h2 id="沙盒">沙盒</h2> - -<div>You can create sandboxes in the chrome or the content process. Sandboxes are often used as a safe way to manipulate web content, and if that's your goal, create the sandbox in the content process.</div> - -<div> </div> - -<div>There is a shim for sandboxes: if you make a sandbox in the chrome process and give it content principals (by passing a CPOW as the first argument to <a href="/en-US/docs/Components.utils.Sandbox">Components.utils.Sandbox</a>) then we'll actually make it in the content process.</div> - -<div> </div> - -<div> -<h2 id="nsIAboutModule">nsIAboutModule</h2> - -<p>By default, custom about: pages registered using nsIAboutModule are loaded in the chrome process. This means that you can't access their content from the content process (via XHR, for example).</p> - -<p>你可以在你注册 about: URI 的代码中改变这个默认值。见 <a href="/en-US/Firefox/Multiprocess_Firefox/about:_and_chrome:_URIs">about: 和 chrome: URI</a>。</p> -</div> - -<h2 id="JavaScript_代码模块_(JSM)">JavaScript 代码模块 (JSM)</h2> - -<div>在单进程的 Firefox 中,你可以使用 <a href="/en-US/docs/Mozilla/JavaScript_code_modules">JavaScript 代码模块 (JSM)</a> 来维持全局状态。在多进程的 Firefox 中,一个加载到某个进程的 JSM 不与加载到另一个进程的同样 JSM 共享状态:因此你不能使用一个 JSM 在 chrome 和内容进程之间共享状态。</div> - -<div> </div> - -<div>If an add-on wants to use a JSM to share state in this way, it's best to load the JSM in the chrome process, and have frame scripts store and access the JSM's state by sending messages to the chrome process using the message manager.</div> diff --git a/files/zh-cn/mozilla/firefox/multiprocess_firefox/limitations_of_frame_scripts/index.html b/files/zh-cn/mozilla/firefox/multiprocess_firefox/limitations_of_frame_scripts/index.html deleted file mode 100644 index d942067bd3..0000000000 --- a/files/zh-cn/mozilla/firefox/multiprocess_firefox/limitations_of_frame_scripts/index.html +++ /dev/null @@ -1,101 +0,0 @@ ---- -title: 框架脚本的限制 -slug: Mozilla/Firefox/Multiprocess_Firefox/Limitations_of_frame_scripts -translation_of: Mozilla/Firefox/Multiprocess_Firefox/Limitations_of_frame_scripts ---- -<div>{{FirefoxSidebar}}</div><p>框架脚本使用<a href="/en-US/docs/Security_check_basics#Principals">系统特权</a>运行,并且能够访问<a href="/en-US/docs/Components_object"> Components</a> 对象,使它们能够使用 <a href="/en-US/docs/Mozilla/Tech/XPCOM">XPCOM</a> 对象和 <a href="/en-US/docs/Mozilla/JavaScript_code_modules">JSM</a>。许多特权的 API 在内容进程中仍然工作。数据结构的处理仍将工作。XHR 和 Workers 仍将工作。但是,某些 API 在 chrome 进程中工作,但在框架脚本中将不工作。本文列出最重要的那些 API。</p> - -<p>这是一对文章之一,另一篇是:<a href="/en-US/Firefox/Multiprocess_Firefox/Limitations_of_chrome_scripts">chrome 脚本的限制</a>。</p> - -<h2 id="文件_IO">文件 I/O</h2> - -<p>你不应该从内容脚本写入或者读取磁盘,特别是配置文件目录。即使这是可能的,你也不应该这样做,应该预期它可能在任何时间停止工作。文件 I/O 应该全部放在 chrome 进程完成。例如:</p> - -<ul> - <li><a href="http://dxr.mozilla.org/mozilla-central/source/netwerk/base/public/nsIFileStreams.idl"><code>nsIFileInputStream</code></a></li> - <li><a href="http://dxr.mozilla.org/mozilla-central/source/netwerk/base/public/nsIFileStreams.idl"><code>nsIFileOutputStream</code></a></li> - <li><a href="/en-US/docs/Extensions/Using_the_DOM_File_API_in_chrome_code">Constructing a <code>File</code> from a string or <code>nsIFile</code></a> (但 <code>File</code> 对象可以通过消息管理器发送)</li> - <li><code><a href="/en-US/docs/Web/API/HTMLInputElement/mozSetFileNameArray">HTMLInputElement.mozSetFileNameArray</a></code> (替代: <code>mozSetFileArray</code>)</li> -</ul> - -<h2 id="XUL_和浏览器界面">XUL 和浏览器界面</h2> - -<p>任何试图接触界面或者与 XUL 相关的东西都很可能在内容进程中不工作。例如:</p> - -<ul> - <li><code><a href="http://dxr.mozilla.org/mozilla-central/source/embedding/components/windowwatcher/nsIPromptService.idl">nsIPromptService</a></code></li> - <li><a href="http://dxr.mozilla.org/mozilla-central/source/widget/nsIFilePicker.idl"><code>nsIFilePicker</code></a></li> - <li><code>nsIXUL*</code></li> - <li><更多例子待补充></li> -</ul> - -<h2 id="Services">Services</h2> - -<p>某些服务不能在框架脚本中工作。</p> - -<ul> - <li>Services.search</li> - <li>Services.downloads</li> -</ul> - -<h2 id="Chrome_窗口">Chrome 窗口</h2> - -<p>任何需要使用 chrome 窗口的东西都不能在内容进程中工作。例如:</p> - -<ul> - <li><a href="http://dxr.mozilla.org/mozilla-central/source/mobile/android/components/SessionStore.idl"><code>nsISessionStore</code></a></li> - <li><a href="http://dxr.mozilla.org/mozilla-central/source/xpfe/appshell/nsIWindowMediator.idl"><code>nsIWindowMediator</code></a></li> - <li><更多例子待补充></li> -</ul> - -<h2 id="Places_API">Places API</h2> - -<p><a href="/en-US/docs/Mozilla/Tech/Places">Places API</a> 不能在框架脚本中使用。例如:</p> - -<ul> - <li><a href="http://dxr.mozilla.org/mozilla-central/source/toolkit/components/places/nsINavHistoryService.idl"><code>nsINavHistoryService</code></a></li> - <li><a href="http://dxr.mozilla.org/mozilla-central/source/toolkit/components/places/nsINavBookmarksService.idl"><code>nsINavBookmarksService</code></a></li> -</ul> - -<h2 id="内容进程中的_Observers">内容进程中的 Observers</h2> - -<p>As noted in <a href="https://developer.mozilla.org/en-US/Firefox/Multiprocess_Firefox/Limitations_of_chrome_scripts#Observers_in_the_chrome_process">Observers in the chrome process</a>, most <a href="http://dxr.mozilla.org/mozilla-central/source/xpcom/ds/nsIObserver.idl">observers</a> should be registered in the chrome process and will not work in the content process. The exceptions are:</p> - -<ul> - <li><code><a href="https://developer.mozilla.org/en-US/docs/Observer_Notifications#Documents">content-document-global-created</a></code></li> - <li><a href="https://developer.mozilla.org/en-US/docs/Observer_Notifications#Documents"><code>document-element-inserted</code></a></li> - <li><a href="https://developer.mozilla.org/en-US/docs/Observer_Notifications#Windows"><code>outer-window-destroyed</code></a></li> - <li><a href="https://developer.mozilla.org/en-US/docs/Observer_Notifications#Windows"><code>inner-window-destroyed</code></a></li> - <li><a href="https://developer.mozilla.org/en-US/docs/Observer_Notifications#Windows"><code>dom-window-destroyed</code></a></li> -</ul> - -<p>这些必须在内容进程中注册。</p> - -<h2 id="内容窗口到_chrome_窗口的_QI">内容窗口到 chrome 窗口的 QI</h2> - -<div>There's a particular pattern often used to get from a content window to the associated chrome window. It looks something like this:</div> - -<div> </div> - -<pre class="brush: js">window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShellTreeItem) - .rootTreeItem - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindow);</pre> - -<div>This will no longer work. In the content process the root tree item is an <a href="http://dxr.mozilla.org/mozilla-central/source/dom/interfaces/base/nsITabChild.idl"><code>nsITabChild</code></a>, that cannot be converted to an <code>nsIDOMWindow</code>, so the second <code>getInterface</code> call here will fail.</div> - -<div> </div> - -<p>If you want a chrome window: send a message from the content process using the <a href="/en-US/Firefox/Multiprocess_Firefox/The_message_manager">message manager</a>. The <a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageListener#receiveMessage()"><code>target</code></a> property of the object passed into the message handler in the chrome process is the XUL <a href="/en-US/docs/XUL/browser"><code><browser></code></a> receiving the message, and you can get the chrome window from that (Note: I'm not really sure how...).</p> - -<h2 id="nsIAboutModule">nsIAboutModule</h2> - -<p>默认情况下,使用 <a href="http://dxr.mozilla.org/mozilla-central/source/netwerk/protocol/about/nsIAboutModule.idl"><code>nsIAboutModule</code></a> 注册的自定义的 <code>about:</code> 页面在 chrome 进程中加载。这意味着你不能从内容进程访问它们的内容(比如通过 XHR)。</p> - -<p>你可以在注册 about: URI 的代码中更改这个默认值。见 <a href="/en-US/Firefox/Multiprocess_Firefox/about:_and_chrome:_URIs">about: 和 chrome: URI</a>。</p> - -<h2 id="JavaScript_代码模块_(JSM)">JavaScript 代码模块 (JSM)</h2> - -<div>在多进程的 Firefox 中,一个加载到内容进程的 JSM 不予加载到 chrome 进程的同一个 JSM 共享任何状态。参考 <a href="/en-US/Firefox/Multiprocess_Firefox/Limitations_of_chrome_scripts#JavaScript_code_modules_%28JSMs%29">chrome 脚本的限制</a> 页面中的内容。</div> diff --git a/files/zh-cn/mozilla/firefox/multiprocess_firefox/message_manager/communicating_with_frame_scripts/index.html b/files/zh-cn/mozilla/firefox/multiprocess_firefox/message_manager/communicating_with_frame_scripts/index.html deleted file mode 100644 index 9c8abfa1c9..0000000000 --- a/files/zh-cn/mozilla/firefox/multiprocess_firefox/message_manager/communicating_with_frame_scripts/index.html +++ /dev/null @@ -1,205 +0,0 @@ ---- -title: 与框架脚本通信 -slug: >- - Mozilla/Firefox/Multiprocess_Firefox/Message_Manager/Communicating_with_frame_scripts -translation_of: >- - Mozilla/Firefox/Multiprocess_Firefox/Message_Manager/Communicating_with_frame_scripts ---- -<div>{{FirefoxSidebar}}</div><p>Chrome 的代码与框架脚本的往来通信采用消息 API,它可以包含可 JSON 序列化的对象作为参数。</p> - -<p>这个 API 大多是对称的,但有一个主要的例外:框架脚本可以发送同步或者异步消息到 chrome,但 chrome 只能发送异步消息到内容。这是一种有意的设计,是为了防止内容不响应而导致的 chrome 失去响应。<br> - <br> - 在绝对必要时,框架脚本可以通过被称为 <a href="/en-US/docs/Cross_Process_Object_Wrappers">跨进程对象包装器</a>(也称 CPOWs)的东西到达 chrome,并且 chrome 可以使用这些包装器来获得到内容对象的同步访问。</p> - -<h2 id="内容到_chrome">内容到 chrome</h2> - -<p>框架脚本可以选择发送同步消息或者异步消息到 chrome 代码。</p> - -<h3 id="异步消息">异步消息</h3> - -<p>要发送异步消息,内容脚本应使用 <code>sendAsyncMessage()</code> 函数:</p> - -<pre class="brush: js">// frame script -sendAsyncMessage("my-addon@me.org:my-e10s-extension-message");</pre> - -<p><code>sendAsyncMessage()</code> takes one mandatory parameter, which is the name of the message. All messages share the same namespace, so to avoid conflicts with other code, you'll need to ensure that the names you use are unique. If you're using the message manager in an add-on, a good way to do that is to prefix messages with your add-on's ID.</p> - -<p>After the name, you can pass detailed data as a string or a JSON-serializable object, and after that you can pass any objects it wants to pass to content as <a href="/en-US/docs/Cross_Process_Object_Wrappers">CPOWs</a>.</p> - -<p>The example below sends a message named "my-e10s-extension-message", with a <code>data</code> payload containing <code>details</code> and <code>tag</code> properties, and exposes the <code>event.target</code> object as a CPOW:</p> - -<pre class="brush: js">// frame script -addEventListener("click", function (event) { - sendAsyncMessage("my-addon@me.org:my-e10s-extension-message", { - details : "they clicked", - tag : event.target.tagName - }, - { - target : event.target - }); -}, false);</pre> - -<p>要接收来自内容的消息,一个 chrome 脚本需要使用消息管理器的 <code>addMessageListener()</code> API 添加消息监听器:<br> - <br> - 传递给监听器的消息是一个对象,包含下列属性:</p> - -<table class="fullwidth-table standard-table"> - <tbody> - <tr> - <td><code>name</code></td> - <td>字符串,包含消息的名称。</td> - </tr> - <tr> - <td><code>sync</code></td> - <td>布尔值,表示消息是否为同步发送,或者是异步发送。</td> - </tr> - <tr> - <td><code>data</code></td> - <td>JSON 对象,作为传递给 <code>sendAsyncMessage()</code> 的第二个参数。</td> - </tr> - <tr> - <td><code>target</code></td> - <td>这是 XUL 的 <code><browser></code> 元素,来自消息发送的位置。</td> - </tr> - <tr> - <td><code>objects</code></td> - <td>An object whose properties are any CPOWs exposed by the sender as the third argument to <code>sendAsyncMessage()</code></td> - </tr> - </tbody> -</table> - -<p>在下面的例子中,监听器只是记录所有的消息细节;</p> - -<pre class="brush: js">// chrome script -messageManager.addMessageListener("my-addon@me.org:my-e10s-extension-message", listener); - -function listener(message) { - console.log(message.name); - console.log(message.sync); - console.log(message.data); - console.log(message.target); - console.log(message.objects); -} -</pre> - -<p>So combining this message listener with the message above will give console output somewhat like this, when the user clicks a <code><div></code>:</p> - -<pre>"my-addon@me.org:my-e10s-extension-message" -false -Object { details: "they clicked", tag: "div" } -<xul:browser anonid="initialBrowser" ... > -{ target: <div#searchContainer> } -</pre> - -<p>If your code requires access to a <code>window</code> (for example to run <code>window.openDialog</code>), and your message listener is run from somewhere without access to a <code>window</code> (e.g. an XPCOM component), you can access the window of the <code>browser</code> that sent the message with <code>message.target.ownerDocument.defaultView</code>.</p> - -<h3 id="同步消息">同步消息</h3> - -<p>要发送一个同步消息,框架脚本应使用全局的 <code>sendSyncMessage()</code> 函数:</p> - -<pre class="brush: js">// frame script -sendSyncMessage("my-addon@me.org:my-e10s-extension-message");</pre> - -<p>在一个 chrome 脚本收到一个同步消息时,它应该从它的消息监听器返回一个值:</p> - -<pre class="brush: js">// chrome script -messageManager.addMessageListener("my-addon@me.org:my-e10s-extension-message", listener); - -function listener(message) { - return "value from chrome"; -}</pre> - -<p>This value is then presented to the frame script in the return value of <code>sendSyncMessage()</code>. Because a single message can be received by more than one listener, the return value of <code>sendSyncMessage()</code> is an array of all the values returned from every listener, even if it only contains a single value:</p> - -<pre class="brush: js">// frame script -addEventListener("click", function (event) { - var results = sendSyncMessage("my-addon@me.org:my-e10s-extension-message", { - details : "they clicked", - tag : event.target.tagName - }); - content.console.log(results[0]); // "value from chrome" -}, false);</pre> - -<p>Like arguments, return values from <code>sendSyncMessage()</code> must be JSON-serializable, so chrome can't return functions.</p> - -<h3 id="removeMessageListener()">removeMessageListener()</h3> - -<p>要停止监听来自内容的消息,使用消息管理器的 <code>removeMessageListener()</code> 方法:</p> - -<pre class="brush: js">// chrome script -messageManager.removeMessageListener("my-addon@me.org:my-e10s-extension-message", listener);</pre> - -<h2 id="Chrome_到内容">Chrome 到内容</h2> - -<p>要从 chrome 发送一个消息到内容,你需要知道你正在使用什么类型的消息管理器。如果它是一个浏览器消息管理器,你可以使用消息管理器的 <code>sendAsyncMessage</code> 方法:</p> - -<pre class="brush: js">// chrome script -browser.messageManager.sendAsyncMessage("my-addon@me.org:message-from-chrome");</pre> - -<p>如果你有一个窗口或者全局消息管理器,你需要使用 <code>broadcastAsyncMessage</code> 方法:</p> - -<pre>// chrome script -window.messageManager.broadcastAsyncMessage("my-addon@me.org:message-from-chrome");</pre> - -<p>These methods takes one mandatory parameter, which is the message name. All messages share the same namespace, so to avoid conflicts with other code, you'll need to ensure that the names you use are unique. If you're using the message manager in an add-on, a good way to do that is to prefix messages with your add-on's ID.</p> - -<p>在消息名称后,你可以将详细的数据传递为一个字符串或者一个可 JSON 序列化的对象:</p> - -<pre class="brush: js">// chrome script -messageManager.sendAsyncMessage("my-addon@me.org:message-from-chrome", { - details : "some more details" -});</pre> - -<p>To receive a message from chrome, a frame script uses the global <code>addMessageListener()</code> function. This takes two parameters: the name of the message and a listener function. The listener will be passed a <code>message</code> object whose <code>data</code> property is the message payload:</p> - -<pre class="brush: js">// frame script -function handleMessageFromChrome(message) { - var payload = message.data.details; // "some more details" -} - -addMessageListener("my-addon@me.org:message-from-chrome", handleMessageFromChrome);</pre> - -<h3 id="message-manager-disconnect">message-manager-disconnect</h3> - -<p>If you're using a <a href="https://developer.mozilla.org/en-US/Firefox/Multiprocess_Firefox/The_message_manager">message manager</a> to communicate with a script that may be running in a different process, you can listen for the <a href="/en-US/docs/Observer_Notifications#Message_manager">message-manager-disconnect </a>observer notification to know when the message manager has disconnected from the other end of the conversation, so you can stop sending it messages or expecting to receive messages.</p> - -<p>For example, suppose we load a script into the current <code><browser></code> on some event, and keep the browser message manager in an array, so we can send it messages:</p> - -<pre class="brush: js">var messageManagers = []; - -... - -// on some event -var browserMM = gBrowser.selectedBrowser.messageManager; -browserMM.loadFrameScript("chrome://my-addon@me.org/content/frame-script.js", false); -messageManagers.push(browserMM); -console.log(messageManagers.length);</pre> - -<p>We can listen for <code>message-manager-disconnect</code> to update the array when the message managers disconnect (for example because the user closed the tab):</p> - -<pre class="brush: js">function myObserver() { -} - -myObserver.prototype = { - observe: function(subject, topic, data) { - var index = messageManagers.indexOf(subject); - if (index != -1) { - console.log("one of our message managers disconnected"); - mms.splice(index, 1); - } - }, - register: function() { - var observerService = Cc["@mozilla.org/observer-service;1"] - .getService(Ci.nsIObserverService); - observerService.addObserver(this, "message-manager-disconnect", false); - console.log("listening"); - }, - unregister: function() { - var observerService = Cc["@mozilla.org/observer-service;1"] - .getService(Ci.nsIObserverService); - observerService.removeObserver(this, "message-manager-disconnect"); - } -} - -var observer = new myObserver(); -observer.register();</pre> diff --git a/files/zh-cn/mozilla/firefox/multiprocess_firefox/message_manager/frame_script_environment/index.html b/files/zh-cn/mozilla/firefox/multiprocess_firefox/message_manager/frame_script_environment/index.html deleted file mode 100644 index 8d5e9d287b..0000000000 --- a/files/zh-cn/mozilla/firefox/multiprocess_firefox/message_manager/frame_script_environment/index.html +++ /dev/null @@ -1,104 +0,0 @@ ---- -title: 框架脚本环境 -slug: Mozilla/Firefox/Multiprocess_Firefox/Message_Manager/Frame_script_environment -translation_of: Mozilla/Firefox/Multiprocess_Firefox/Message_Manager/Frame_script_environment ---- -<div>{{FirefoxSidebar}}</div><p>框架脚本的全局是 <a href="http://dxr.mozilla.org/mozilla-central/source/dom/base/nsIMessageManager.idl#345">ContentFrameMessageManager</a>,提供下列环境:</p> - -<table class="fullwidth-table standard-table"> - <tbody> - <tr> - <td><code>content</code></td> - <td>The DOM window of the content loaded in the browser. may be <code>null</code> (see below)</td> - </tr> - <tr> - <td><code>docShell</code></td> - <td>The <a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIDocShell"><code>nsIDocShell</code></a> associated with the browser.</td> - </tr> - <tr> - <td><code>addEventListener()</code></td> - <td>Listen to events from content.</td> - </tr> - <tr> - <td><code>removeEventListener()</code></td> - <td>Stop listening to events from content.</td> - </tr> - <tr> - <td><code>addMessageListener()</code></td> - <td>Listen to messages from chrome.</td> - </tr> - <tr> - <td><code>removeMessageListener()</code></td> - <td>Stop listening to messages from chrome.</td> - </tr> - <tr> - <td><code>sendAsyncMessage()</code></td> - <td>Send an asynchronous message to chrome.</td> - </tr> - <tr> - <td><code>sendSyncMessage()</code></td> - <td>Send a synchronous message to chrome.</td> - </tr> - <tr> - <td><code>dump()</code></td> - <td>Print a message to the console.</td> - </tr> - <tr> - <td><code>atob()</code></td> - <td>Base64 decode.</td> - </tr> - <tr> - <td><code>btoa()</code></td> - <td>Base64 encode.</td> - </tr> - <tr> - <td><code>Components</code></td> - <td>The usual <a href="/en-US/docs/Components_object"><code>Components</code> object</a>.</td> - </tr> - </tbody> -</table> - -<p>特别注意,框架脚本使用<code> content </code>访问 DOM 窗口,而不是 <code>window</code>:</p> - -<pre class="brush: js">// frame script -var links = content.document.getElementsByTagName("a");</pre> - -<p>All the frame scripts running in a tab share this global. However, any top-level variables defined by a script are not stored on the global: instead, top-level variables are stored in a special per-script object that delegates to the per-tab global. This means you don't have to worry about global variables you define conflicting with global variables defined by another frame script. You can still access the global directly via <code>this</code>.</p> - -<p>框架脚本使用系统主体运行。如果你想使用其他主题,可以使用 <a href="/en-US/docs/Components.utils.Sandbox"><code>Sandbox</code></a>。</p> - -<p>Frame scripts run with <a href="https://developer.mozilla.org/en-US/docs/Security_check_basics#Principals">system privileges</a> and have access to the <a href="https://developer.mozilla.org/en-US/docs/Components_object">Components</a> object, enabling them to use <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM">XPCOM</a> objects and <a href="https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules">JSMs</a>. However, some APIs that work in the chrome process will not work in a frame script. See <a href="/en-US/Firefox/Multiprocess_Firefox/Limitations_of_frame_scripts">Limitations of frame scripts</a> for more details.</p> - -<h2 id="事件">事件</h2> - -<p>Besides the regular DOM events being captured/bubbling up from content the current <code>content</code> object the following additional events get fired in a frame script environment:</p> - -<table class="standard-table"> - <tbody> - <tr> - <td><a href="/en-US/docs/Web/Reference/Events/unload">unload</a></td> - <td> - <dl> - <dt style="float: left; text-align: right; width: 120px;">Bubbles</dt> - <dd style="margin: 0 0 0 120px;">No</dd> - </dl> - - <p>Fires when the frame script environment is shut down, i.e. when a tab gets closed.</p> - - <p>If you use a capturing event listener on the <code>ContentFrameMessageManager</code>, you should verify that its <code>event.target</code> is set to the <code>ContentFrameMessageManager</code> global object in order to avoid handling <code>unload</code> events from content.</p> - </td> - </tr> - <tr> - <td><a href="/en-US/docs/Web/Events/DOMWindowCreated">DOMWindowCreated</a></td> - <td> - <p>Fires when a new <code>content</code> object is created.<br> - <br> - This can be used if a framescript needs to interact with individual DOM windows instead of simply listening for events bubbling up from content.<br> - Another use is to interact with the content very early in the page load process, long before <a href="/en-US/docs/Web/Events/DOMContentLoaded">DOMContentLoaded</a> event is fired.<br> - </p> - </td> - </tr> - </tbody> -</table> - -<p> </p> diff --git a/files/zh-cn/mozilla/firefox/multiprocess_firefox/message_manager/frame_script_loading_and_lifetime/index.html b/files/zh-cn/mozilla/firefox/multiprocess_firefox/message_manager/frame_script_loading_and_lifetime/index.html deleted file mode 100644 index ee53fe52e2..0000000000 --- a/files/zh-cn/mozilla/firefox/multiprocess_firefox/message_manager/frame_script_loading_and_lifetime/index.html +++ /dev/null @@ -1,124 +0,0 @@ ---- -title: 框架脚本的加载与寿命 -slug: >- - Mozilla/Firefox/Multiprocess_Firefox/Message_Manager/Frame_script_loading_and_lifetime -translation_of: >- - Mozilla/Firefox/Multiprocess_Firefox/Message_Manager/Frame_script_loading_and_lifetime ---- -<div>{{FirefoxSidebar}}</div><h2 id="加载框架脚本">加载框架脚本</h2> - -<p>要加载一个框架脚本,使用 <code>loadFrameScript()</code> 函数。</p> - -<p>这行代码加载一个框架脚本到当前选中的标签页。该框架脚本只是将 "foo" 写入到命令行:</p> - -<pre class="brush: js">// chrome script -var mm = gBrowser.selectedBrowser.messageManager; -mm.loadFrameScript('data:,dump("foo\\n")', true);</pre> - -<p><code>loadFrameScript()</code> 有两个强制性参数:</p> - -<ul> - <li>一个 URL 指向要加载的框架脚本。</li> - <li>一个布尔变量,<code>allowDelayedLoad</code></li> -</ul> - -<p>Note that if the message manager is a <a href="/en-US/Firefox/Multiprocess_Firefox/Message_Manager/Message_manager_overview#Global_frame_message_manager">global frame message manager</a> or a <a href="/en-US/Firefox/Multiprocess_Firefox/Message_Manager/Message_manager_overview#Window_message_manager">window message manager</a> then <code>loadFrameScript() </code>may load the script multiple times, once into each applicable frame.</p> - -<h3 id="chrome_URL">chrome: URL</h3> - -<p>扩展开发者通常会使用 <code>chrome://</code> URL 来指向一个框架脚本。</p> - -<p>要定义 <code>chrome://</code> URL 的映射和将一个框架脚本打包到扩展中,使用 "chrome.manifest" 文件来 <a href="/en/docs/Chrome_Registration">注册一个chrome URL</a>:</p> - -<pre class="brush: js">// chrome.manifest -content my-e10s-extension content.js</pre> - -<pre class="brush: js">// chrome script -mm.loadFrameScript("chrome://my-e10s-extension/content/content.js", true);</pre> - -<h3 id="allowDelayedLoad">allowDelayedLoad</h3> - -<p>如果消息管理器是一个 <a href="/en-US/Firefox/Multiprocess_Firefox/Message_Manager/Message_manager_overview#Global_frame_message_manager">全局框架消息管理器</a> 或者一个 <a href="/en-US/Firefox/Multiprocess_Firefox/Message_Manager/Message_manager_overview#Window_message_manager">窗口消息管理器</a>,那么:</p> - -<ul> - <li> - <p>如果 <code>allowDelayedLoad</code> 为 <code>true</code>,在 <code>loadFrameScript()</code> 调用后,框架脚本将加载到任何新的已打开的标签页。例如:</p> - - <pre class="brush: js">var mm = window.messageManager; -mm.loadFrameScript("chrome://my-e10s-extension/content/frame-script.js", true);</pre> - - <p>这段脚本将加载到此窗口中目前已打开的所有标签页,以及未来进入的新打开的标签页。</p> - </li> - <li>如果 <code>allowDelayedLoad</code> 为 <code>false</code>,那么脚本只会在执行调用时已打开的标签页中加载。</li> -</ul> - -<p>如果消息管理器是一个 <a href="/en-US/Firefox/Multiprocess_Firefox/Message_Manager/Message_manager_overview#Browser_message_manager">浏览器消息管理器</a>,你应该始终在这里传递 <code>true</code>。因为一个浏览器消息管理器永远只对应一个浏览器标签页,它的 <code>loadFrameScript()</code> 函数只加载框架脚本到一个标签页。因此传递 <code>allowDelayedLoad</code> 仅仅是一个方法来确保脚本被正确加载,在你的标签页在执行后还没有准备好时。</p> - -<p>如果你使用 <code>allowDelayedLoad</code>,你可以使用 <code>removeDelayedFrameScript</code> 取消它:</p> - -<pre class="brush: js">var mm = window.messageManager; -<code>mm.removeDelayedFrameScript</code>("chrome://my-e10s-extension/content/frame-script.js");</pre> - -<p>这意味着我们将停止加载脚本到新的标签页。请注意,此函数不会移除已经加载的任何脚本。</p> - -<h2 id="框架脚本的寿命">框架脚本的寿命</h2> - -<p>框架脚本将在 <code>loadFrameScript()</code> 被调用后尽快加载。如果你设置了 <code>allowDelayedLoad</code>,脚本将加载到一个新的标签页,一旦其已被创建。</p> - -<p>框架脚本与浏览器的标签页相关联,而不是与页面。因此一旦你加载它们,它们就会持续存在,直至标签页被关闭,因此即便你重新加载或者文档重新导航也不会丢失。</p> - -<p>如果你想一个框架脚本在每次新文档被加载后执行操作,你需要监听一个适当的 DOM 事件,通常是 <code><a href="/en-US/docs/Web/Events/DOMWindowCreated">DOMWindowCreated</a></code>, <code><a href="/en-US/docs/Web/Events/DOMContentLoaded">DOMContentLoaded</a></code>, 或者 <code><a href="/en-US/docs/Web/Events/load">load</a></code>。</p> - -<h2 id="卸载框架脚本">卸载框架脚本</h2> - -<p>框架脚本会在托管它们的标签页被关闭时自动卸载。目前还没有办法在已加载它们的标签页之中卸载它们,除了关闭标签页。</p> - -<p>若要监听你的框架脚本被卸载的事件(例如由于标签页被关闭),你必须将 <code>addMessageListener</code> 的第三个参数设置为 true,例如下面的 <code>bootstrap.js</code> 的代码:</p> - -<pre class="brush: js">Services.mm.addMessageListener( - 'my-addon-id', - { - receiveMessage: function() { - console.log('incoming message from frame script:', aMsg.data); - } - }, - true // must set this argument to true, otherwise sending message from framescript will not work during and after the unload event on the ContentMessageManager triggers -);</pre> - -<p>and then in your frame script listen for the unload event of the message manager (which is the global this), and then send a message. If you did not set third argument to true in <code>bootstrap.js</code> on <code>Services.mm.addMessageListener</code>, then this send message during and after unload event, will do nothing.</p> - -<pre class="brush: js">var gContentFrameMessageManager = this; - -addEventListener('unload', function(aEvent) { - if (aEvent.target == gContentFrameMessageManager) { - sendAsyncMessage('my-addon-id', 'framescript-died'); // if you did not set third argument of `Services.mm.addMessageListener` to `true`, then this will fail to send a message - } -}, false);</pre> - -<h3 id="有关卸载升级操作时的卸载">有关卸载/升级操作时的卸载</h3> - -<p>在你的附加组件被卸载或禁用时,你应该:</p> - -<ul> - <li>如果使用了 <code>allowDelayedLoad</code>,调用 <code>removeDelayedFrameScript</code> 取消它,确保框架脚本不会加载到任何新的标签页。</li> - <li>禁用已加载的任何框架脚本。没有任何机制来卸载已加载的框架脚本,因此你需要发送一个消息到你的框架脚本来告诉它们禁用自己(例如撤销所有已取得的更改,以及移除事件监听器)。</li> -</ul> - -<div class="note"> -<div class="warning"> -<p>There is a bug in non-e10s where this oder is not true. In e10s framescripts work fine on updating. For non-e10s waiting for <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1202125">Bug 1202125 - framescripts are not backwards loaded in message order in non-e10s</a>.</p> -</div> - -<p>Note: you might think that there is a race condition here due to the asynchronous nature of the message passing:</p> - -<ul> - <li>your add-on is disabled for an upgrade</li> - <li>your add-on broadcasts "disable" to your frame scripts</li> - <li>your add-on is upgraded, and the new code loads new frame scripts</li> - <li>the new frame scripts receive the "disable" message, and stop working</li> -</ul> - -<p>In fact, the message manager guarantees that <code>loadFrameScript</code> and <code>broadcastAsyncMessage</code> are guaranteed to affect frame scripts in the order that they are called, so in this case "disable" will be received and consumed before the new frame scripts are loaded.</p> -</div> - -<p>At the moment frame scripts are cached until the browser restarts: this problem is tracked as <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1051238">bug 1051238</a>. This is especially a problem for <a href="/en-US/Add-ons/Bootstrapped_extensions">restartless add-ons</a>, because when a new version of the add-on is installed, the old frame scripts will not be unloaded. The workaround here is to randomize the frame script's URL, for example by appending <code>"?" + Math.random()</code> to it.</p> diff --git a/files/zh-cn/mozilla/firefox/multiprocess_firefox/message_manager/index.html b/files/zh-cn/mozilla/firefox/multiprocess_firefox/message_manager/index.html deleted file mode 100644 index be6ba855c9..0000000000 --- a/files/zh-cn/mozilla/firefox/multiprocess_firefox/message_manager/index.html +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: 消息管理器 -slug: Mozilla/Firefox/Multiprocess_Firefox/Message_Manager -translation_of: Mozilla/Firefox/Multiprocess_Firefox/Message_Manager ---- -<div>{{FirefoxSidebar}}</div><p>消息管理器为 chrome 特权的 JavaScript 代码提供了跨进程边界的通信方式。它非常有用,可以允许 chrome 代码(包括浏览器自身的代码和外部代码)访问在单独的进程中运行的网页内容。</p> - -<p>这些指南介绍了如何在多进程 Firefox 中使用消息管理器。</p> - -<p>请注意,多进程 Firefox 的环境并非是必须的:这里描述的一切对单进程 Firefox 也同样有效,所以相同的代码在两个环境下都可以正常工作。</p> - -<hr> -<h2 id="指南">指南</h2> - -<div class="column-container"> -<div class="column-half"> -<dl> - <dt><a href="/Firefox/Multiprocess_Firefox/Message_Manager/Message_manager_overview">消息管理器概述</a></dt> - <dd> </dd> - <dt><a href="/Firefox/Multiprocess_Firefox/Message_Manager/Frame_script_loading_and_lifetime">框架脚本的加载与寿命</a></dt> - <dd> </dd> - <dt><a href="/Firefox/Multiprocess_Firefox/Message_Manager/Communicating_with_frame_scripts">与框架脚本通信</a></dt> - <dd> </dd> - <dt><a href="/Firefox/Multiprocess_Firefox/Message_Manager/Performance">性能的最佳实践</a></dt> - <dd> </dd> -</dl> -</div> - -<div class="column-half"> -<dl> - <dt><a href="/Firefox/Multiprocess_Firefox/Message_Manager/Frame_script_environment">框架脚本环境</a></dt> - <dd> </dd> - <dt><a href="/Firefox/Multiprocess_Firefox/Message_Manager/_Limitations_of_frame_scripts">框架脚本的限制</a></dt> - <dd> </dd> - <dt><a href="/Firefox/Multiprocess_Firefox/Message_Manager/Process_scripts">进程脚本</a></dt> - <dd> </dd> -</dl> -</div> -</div> - -<hr> -<h2 id="API_参考资料">API 参考资料</h2> - -<div class="column-container"> -<div class="column-half"> -<dl> - <dt><a href="/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIFrameScriptLoader">nsIFrameScriptLoader</a></dt> - <dd> </dd> - <dt><a href="/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageListenerManager">nsIMessageListenerManager</a></dt> - <dd> </dd> - <dt><a href="/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageBroadcaster">nsIMessageBroadcaster</a></dt> - <dd> </dd> - <dt><a href="/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageSender">nsIMessageSender</a></dt> - <dd> </dd> -</dl> -</div> - -<div class="column-half"> -<dl> - <dt><a href="/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsISyncMessageSender">nsISyncMessageSender</a></dt> - <dd> </dd> - <dt><a href="/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIContentFrameMessageManager">nsIContentFrameMessageManager</a></dt> - <dd> </dd> - <dt><a href="/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIProcessScriptLoader">nsIProcessScriptLoader</a></dt> - <dd> </dd> -</dl> -</div> -</div> diff --git a/files/zh-cn/mozilla/firefox/multiprocess_firefox/message_manager/message_manager_overview/index.html b/files/zh-cn/mozilla/firefox/multiprocess_firefox/message_manager/message_manager_overview/index.html deleted file mode 100644 index fb85fa5e79..0000000000 --- a/files/zh-cn/mozilla/firefox/multiprocess_firefox/message_manager/message_manager_overview/index.html +++ /dev/null @@ -1,442 +0,0 @@ ---- -title: 消息管理器概述 -slug: Mozilla/Firefox/Multiprocess_Firefox/Message_Manager/Message_manager_overview -translation_of: Mozilla/Firefox/Multiprocess_Firefox/Message_Manager/Message_manager_overview ---- -<div class="summary"> -<p>在多进程 Firefox 中有两个进程:</p> - -<ul> - <li>chrome 进程,也称父进程,运行着浏览器 UI 界面 (chrome) 的代码和扩展安装的代码。</li> - <li>内容进程,也称子进程,运行着所有网页内容。在未来的 Firefox 版本中,不同的标签页可能运行在不同的进程中,但截至目前,所有内容标签页共享使用同一个内容进程。</li> -</ul> - -<p>消息管理器的设计目的是使运行在一个进程中的 chrome 特权的 JavaScript 代码能够与不同进程中的 chrome 特权的 JavaScript 代码通信。</p> - -<p>本文介绍了几种类型的消息管理器,如何访问它们,以及在一个较高层面你可以使用什么。</p> -</div> - -<p>在顶层,有两种不同类型的消息管理器:</p> - -<ul> - <li><em>框架消息管理器</em>:这些使 chrome 进程的代码能够加载脚本到内容进程中的浏览器框架(基本上,就是指一个浏览器标签页)。这些脚本被称为框架脚本,顾名思义,它们限定在特定的浏览器框架中。如果 chrome 代码想要在内容进程中运行可以方便访问网页内容的代码,通常就是使用消息管理器的时候了。</li> - <li><em>进程消息管理器</em>:这些对应着进程边界,使运行在父 (chrome) 进程中的代码能够与子 (内容) 进程中的代码通信。从 Firefox 38 起,这也使运行在父进程中的代码能够加载进程脚本到子进程。这些类似框架脚本,除了它们是全局访问子进程。进程脚本最有用的时候是进程想要只在内容进程中运行某些代码,来访问某些全局服务:例如,注册一个 <a href="/en/docs/Observer_Notifications">observer </a>或者一个 <a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIContentPolicy">内容策略</a>。</li> -</ul> - -<h2 id="框架消息管理器">框架消息管理器</h2> - -<p>在多进程 Firefox 中,当 chrome 代码需要与网页内容交互时,它需要:</p> - -<ul> - <li>将需要直接访问内容的脚本代码放到一个单独的脚本,它被称为“框架脚本”。</li> - <li>使用框架消息管理器来加载这些框架脚本到内容进程</li> - <li>使用框架消息管理器 API 来与框架脚本通信</li> -</ul> - -<div class="note"> -<p>Some older articles on multiprocess Firefox and the message manager might refer to "content scripts" instead of "frame scripts", but this usage is deprecated because the Add-on SDK uses "content script" to refer to a <a href="https://developer.mozilla.org/en-US/Add-ons/SDK/Guides/Content_Scripts">similar but different kind of script</a>.</p> -</div> - -<p>因此从根本上,框架消息管理器使 chrome 代码能够:</p> - -<ul> - <li>加载一个脚本到一个内容进程中的框架(基本上,就是指一个浏览器标签页)。这些脚本被称为“框架脚本”。</li> - <li>使用消息传递 API 与框架脚本通信</li> -</ul> - -<p>有多种类型的框架消息管理器,如图所示:</p> - -<p><img alt="" src="https://mdn.mozillademos.org/files/10795/frame-message-managers.png" style="display: block; height: 596px; margin-left: auto; margin-right: auto; width: 737px;"></p> - -<p>This diagram shows the setup when there are 2 browser windows open, one with 2 tabs open and one with 1 tab open.</p> - -<h3 id="Chrome_进程">Chrome 进程</h3> - -<p>In the chrome process, there's a hierarchy of frame message managers: the global frame message manager, window message managers, and browser message managers.</p> - -<h4 id="全局框架消息管理器">全局框架消息管理器</h4> - -<table class="fullwidth-table standard-table"> - <tbody> - <tr> - <td style="width: 20%;">Description</td> - <td> - <p>There's a single <em>global frame message manager</em> in the chrome process.</p> - - <p>This operates on all frames, in all content tabs. If you load a frame script using the global frame message manager, the script gets loaded separately into every open tab: three times, in the diagram above. Similarly, if you send a message using the global frame message manager, it's received by all content tabs, and is then delivered to any frame scripts that are listening for it.</p> - - <p>Its most important functions and attributes are:</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageBroadcaster#childCount">childCount</a></code> : contains the number of children (typically, browser windows)</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageBroadcaster#getChildAt()">getChildAt()</a></code> : get the child at the given index</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIFrameScriptLoader#loadFrameScript()">loadFrameScript()</a></code> : load a frame script into every tab in the browser</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageBroadcaster#broadcastAsyncMessage()">broadcastAsyncMessage()</a></code> : send a message to frame scripts</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageListenerManager#addMessageListener()">addMessageListener()</a></code> : start listening to a specific message from all frame scripts</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageListenerManager#removeMessageListener()">removeMessageListener()</a></code> : stop listening to a specific message</p> - </td> - </tr> - <tr> - <td>Interfaces</td> - <td> - <p><code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIFrameScriptLoader" title="">nsIFrameScriptLoader</a></code></p> - - <p><code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageListenerManager" title="">nsIMessageListenerManager</a></code></p> - - <p><code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageBroadcaster" title="">nsIMessageBroadcaster</a></code></p> - </td> - </tr> - <tr> - <td>How to access</td> - <td> - <p>Access it using <code><a href="/en-US/docs/Components.classes">Components.classes</a></code>:</p> - - <pre class="brush: js language-js"> -// chrome script -let globalMM = Cc["@mozilla.org/globalmessagemanager;1"] - .getService(Ci.nsIMessageListenerManager);</pre> - </td> - </tr> - </tbody> -</table> - -<h4 id="窗口消息管理器">窗口消息管理器</h4> - -<table class="fullwidth-table standard-table"> - <tbody> - <tr> - <td style="width: 20%;">Description</td> - <td> - <p>There's a <em>window message manager</em> for every browser window: two, in the diagram above.</p> - - <p>It operates on all content tabs in a given window. If you load a frame script using the window message manager it gets loaded separately into each tab open in that particular window. If you send a message using the window message manager, it gets sent to all content tabs in that window.</p> - - <p>Its most important functions and attributes are:</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageBroadcaster#childCount">childCount</a></code> : contains the number of children (typically, browser tabs)</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageBroadcaster#getChildAt()">getChildAt()</a></code> : get the child at the given index</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIFrameScriptLoader#loadFrameScript()">loadFrameScript()</a></code> : load a frame script into every tab in this window</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageBroadcaster#broadcastAsyncMessage()">broadcastAsyncMessage()</a></code> : send a message to all frame scripts in this window</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageListenerManager#addMessageListener()">addMessageListener()</a></code> : start listening to a specific message from frame scripts</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageListenerManager#removeMessageListener()">removeMessageListener()</a></code> : stop listening to a specific message</p> - </td> - </tr> - <tr> - <td>Interfaces</td> - <td> - <p><code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIFrameScriptLoader" title="">nsIFrameScriptLoader</a></code></p> - - <p><code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageListenerManager" title="">nsIMessageListenerManager</a></code></p> - - <p><code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageBroadcaster" title="">nsIMessageBroadcaster</a></code></p> - </td> - </tr> - <tr> - <td>How to access</td> - <td> - <p>You can access it as a property of the browser window:</p> - - <pre class="brush: js"> -// chrome script -let windowMM = window.messageManager;</pre> - </td> - </tr> - </tbody> -</table> - -<h4 id="浏览器消息管理器">浏览器消息管理器</h4> - -<div class="note"> -<p>Note that in this context, "browser" refers to the <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/browser">XUL <browser> object</a>, which is a frame that hosts a single Web document. It does not refer to the more general sense of a Web browser.</p> -</div> - -<table class="fullwidth-table standard-table"> - <tbody> - <tr> - <td style="width: 20%;">Description</td> - <td> - <p>Finally, there's a <em>browser message manager</em> for every open content tab: three, in the diagram above.</p> - - <p>This corresponds one-to-one with a content tab. Scripts you load using a browser message manager are loaded only into that content tab, and messages you send are delivered only to that content tab.</p> - - <p>You can mix and match: so for example, you could load a script into every tab using the global message manager, but then send a message to the script instance loaded into a specific tab by using the browser message manager.</p> - - <p>Its most important functions are:</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIFrameScriptLoader#loadFrameScript()">loadFrameScript()</a></code> : load a frame script into this browser frame (tab)</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageSender#sendAsyncMessage()">sendAsyncMessage()</a></code> : send a message to all frame scripts in this browser frame</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageListenerManager#addMessageListener()">addMessageListener()</a></code> : start listening to a specific message from frame scripts</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageListenerManager#removeMessageListener()">removeMessageListener()</a></code> : stop listening to a specific message</p> - </td> - </tr> - <tr> - <td>Interfaces</td> - <td> - <p><code>nsIProcessChecker</code></p> - - <p><code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIFrameScriptLoader">nsIFrameScriptLoader</a></code></p> - - <p><code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageListenerManager">nsIMessageListenerManager</a></code></p> - - <p><code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageSender">nsIMessageSender</a></code></p> - </td> - </tr> - <tr> - <td>How to access</td> - <td> - <p>The browser message manager can be accessed as a property of the XUL <code><browser></code> element:</p> - - <pre class="brush: js"> -// chrome script -let browserMM = gBrowser.selectedBrowser.messageManager;</pre> - </td> - </tr> - </tbody> -</table> - -<h3 id="内容进程">内容进程</h3> - -<h4 id="内容框架消息管理器">内容框架消息管理器</h4> - -<table class="fullwidth-table standard-table"> - <tbody> - <tr> - <td style="width: 20%;">Description</td> - <td> - <p>There's a <em>content frame message manager</em> for every open tab. It's the content-side end of frame message manager conversations.</p> - - <p>Frame scripts are loaded into the content frame message manager scope, and messages from chrome message managers end up here.</p> - - <p>The content frame message manager provides the <a href="/en-US/Firefox/Multiprocess_Firefox/Frame_script_environment">global object for frame scripts</a> (but note that there is trickery to ensure that top-level variables defined by frame scripts are not shared).</p> - - <p>Frame scripts can use this object to send messages to the chrome process, and to receive messages from the chrome process.</p> - - <p>Its most important attributes and functions are:</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIContentFrameMessageManager#content">content</a></code> : access the DOM window hosted by the tab</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIContentFrameMessageManager#docShell">docShell</a></code> : access the top-level docshell</p> - - <p><code><a href="/en-US/docs/Components_object">Components</a></code> : access privileged objects and APIs</p> - - <p><code><a href="/en-US/docs/Web/API/EventTarget/addEventListener">addEventListener()</a></code> : listen to DOM events</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageListenerManager#addMessageListener()">addMessageListener()</a></code> : receive messages from the chrome process</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageSender#sendAsyncMessage()">sendAsyncMessage()</a></code> : send asynchronous messages to the chrome process</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsISyncMessageSender#sendSyncMessage()">sendSyncMessage()</a></code> : send synchronous messages to the chrome process</p> - </td> - </tr> - <tr> - <td>Interfaces</td> - <td> - <p><code><a href="https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsIDOMEventTarget">nsIDOMEventTarget</a></code></p> - - <p><code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageListenerManager">nsIMessageListenerManager</a></code></p> - - <p><code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageSender">nsIMessageSender</a></code></p> - - <p><code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsISyncMessageSender">nsISyncMessageSender</a></code></p> - - <p><code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIContentFrameMessageManager">nsIContentFrameMessageManager</a></code></p> - </td> - </tr> - <tr> - <td>How to access</td> - <td>The content frame message manager is the global object in frame scripts.</td> - </tr> - </tbody> -</table> - -<h2 id="进程消息管理器">进程消息管理器</h2> - -<p>Process message managers correspond to process boundaries, and enable code running in different processes to communicate. Multiprocess Firefox has the concept of:</p> - -<ul> - <li>a "parent process"</li> - <li>"child processes" which are processes spawned by the parent process.</li> -</ul> - -<p>For practical purposes, in multiprocess Firefox the parent process is the chrome process, and child processes are content processes. </p> - -<p>In each child process, there's a single <em>child process message manager</em> (CPMM). There's also an additional <em>child-in-process message manager</em> (CIPMM) in the parent process.</p> - -<p>For each child process message manager, there's a <em>parent process message manager</em> (PPMM) in the parent process.</p> - -<p>There's also a single <em>global parent process message manager</em> (GPPMM) in the parent process, that provides access to all the parent process message managers. The diagram below shows the setup that would result from having two child processes:</p> - -<p><img alt="" src="https://mdn.mozillademos.org/files/10799/process-message-managers.png" style="display: block; height: 477px; margin-left: auto; margin-right: auto; width: 477px;"></p> - -<p>With the GPPMM, you can broadcast messages to the CIPMM and all CPMMs. With a PPMM, you can send a message to its corresponding CPMM. With a CPMM, you can send messages to the parent process: these messages are received first by the corresponding PPMM, then by the GPPMM.</p> - -<p>From Firefox 38 onwards, you can also use a parent process message manager to load a script into a child process. This is the recommended way to load a script that executes just once per child process, which is something you might want to do if you are interacting with some global service (for example, adding listeners to observer notifications or registering a content policy).</p> - -<h3 id="父消息管理器">父消息管理器</h3> - -<h4 id="全局父进程消息管理器">全局父进程消息管理器</h4> - -<table class="fullwidth-table standard-table"> - <tbody> - <tr> - <td style="width: 20%;">Description</td> - <td> - <p>The global parent process message manager (GPPMM) is global to the parent process.</p> - - <ul> - <li>Messages sent using the GPPMM get sent to all CPMMs in all child processes.</li> - <li>Process scripts loaded using the GPPMM get loaded in all child processes.</li> - </ul> - - <p>Its most important functions and attributes are:</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageBroadcaster#childCount">childCount</a></code> : contains the number of children (child processes, plus the in-content child)</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageBroadcaster#getChildAt()">getChildAt()</a></code> : get the child at the given index</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIProcessScriptLoader#loadProcessScript()">loadProcessScript()</a></code> : load a process script into every content process</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageBroadcaster#broadcastAsyncMessage()">broadcastAsyncMessage()</a></code> : send a message to all process scripts</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageListenerManager#addMessageListener()">addMessageListener()</a></code> : start listening to a specific message from process scripts</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageListenerManager#removeMessageListener()">removeMessageListener()</a></code> : stop listening to a specific message</p> - </td> - </tr> - <tr> - <td>Interfaces</td> - <td> - <p><code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIProcessScriptLoader">nsIProcessScriptLoader</a></code></p> - - <p><code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageListenerManager" title="">nsIMessageListenerManager</a></code></p> - - <p><code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageBroadcaster" title="">nsIMessageBroadcaster</a></code></p> - </td> - </tr> - <tr> - <td>How to access</td> - <td> - <p>You can access the GPPMM with code like this:</p> - - <pre class="brush: js"> -// parent process -let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"] - .getService(Ci.nsIMessageBroadcaster);</pre> - - <p>You can also access it as the <code>ppmm</code> property of <a href="/en-US/docs/Mozilla/JavaScript_code_modules/Services.jsm">Services.jsm</a>, if you are in the parent process.</p> - </td> - </tr> - </tbody> -</table> - -<h4 id="父进程消息管理器">父进程消息管理器</h4> - -<table class="fullwidth-table standard-table"> - <tbody> - <tr> - <td style="width: 20%;">Description</td> - <td> - <p>There's one parent process message manager (PPMM) in the parent process for every child process, and its API is oriented to that one child process.</p> - - <ul> - <li>Messages sent using the PPMM are received only by the corresponding CPMM</li> - <li>Scripts loaded using the PPMM are loaded only into the corresponding child process.</li> - </ul> - - <p>Its most important functions are:</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIProcessScriptLoader#loadProcessScript()">loadProcessScript()</a></code> : load a process script into the content process</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageBroadcaster#broadcastAsyncMessage()">broadcastAsyncMessage()</a></code> : send a message to process scripts</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageListenerManager#addMessageListener()">addMessageListener()</a></code> : start listening to a specific message from process scripts</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageListenerManager#removeMessageListener()">removeMessageListener()</a></code> : stop listening to a specific message</p> - </td> - </tr> - <tr> - <td>Interfaces</td> - <td> - <p><code>nsIProcessChecker</code></p> - - <p><code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIProcessScriptLoader">nsIProcessScriptLoader</a></code></p> - - <p><code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageListenerManager">nsIMessageListenerManager</a></code></p> - - <p><code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageSender">nsIMessageSender</a></code></p> - </td> - </tr> - <tr> - <td>How to access</td> - <td> - <p>You can access a PPMM using the <code>getChildAt()</code> function in the GPPMM:</p> - - <pre class="brush: js"> -// parent process -let ppmm = Services.ppmm.getChildAt(1);</pre> - </td> - </tr> - </tbody> -</table> - -<h3 id="子进程">子进程</h3> - -<h4 id="子进程消息管理器">子进程消息管理器</h4> - -<table class="fullwidth-table standard-table"> - <tbody> - <tr> - <td style="width: 20%;">Description</td> - <td> - <p>There's one child process message manager (CPMM) in each child process. Messages sent using the CPMM are sent to the corresponding PPMM and are also relayed to the GPPMM.</p> - - <p>Its most important attributes and functions are:</p> - - <p><code><a href="/en-US/docs/Components_object">Components</a></code> : access privileged objects and APIs</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageListenerManager#addMessageListener()">addMessageListener()</a></code> : receive messages from the parent process</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageSender#sendAsyncMessage()">sendAsyncMessage()</a></code> : send asynchronous messages to the parent process</p> - - <p><code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsISyncMessageSender#sendSyncMessage()">sendSyncMessage()</a></code> : send synchronous messages to the parent process</p> - </td> - </tr> - <tr> - <td>Interfaces</td> - <td> - <p><code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageListenerManager">nsIMessageListenerManager</a></code></p> - - <p><code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageSender">nsIMessageSender</a></code></p> - - <p><code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsISyncMessageSender">nsISyncMessageSender</a></code></p> - - <p><code>nsIContentProcessMessageManager</code></p> - </td> - </tr> - <tr> - <td>How to access</td> - <td> - <p>Code running in a child process can access the CPMM with code like this:</p> - - <pre class="brush: js"> -// child process script -let cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"] - .getService(Ci.nsISyncMessageSender);</pre> - - <p>You can also access it as the <code>cpmm</code> property of <a href="/en-US/docs/Mozilla/JavaScript_code_modules/Services.jsm">Services.jsm</a>, if you are in the child process.</p> - </td> - </tr> - </tbody> -</table> diff --git a/files/zh-cn/mozilla/firefox/multiprocess_firefox/message_manager/performance/index.html b/files/zh-cn/mozilla/firefox/multiprocess_firefox/message_manager/performance/index.html deleted file mode 100644 index 3bcb04d88e..0000000000 --- a/files/zh-cn/mozilla/firefox/multiprocess_firefox/message_manager/performance/index.html +++ /dev/null @@ -1,292 +0,0 @@ ---- -title: 性能 -slug: Mozilla/Firefox/Multiprocess_Firefox/Message_Manager/Performance -translation_of: Mozilla/Firefox/Multiprocess_Firefox/Message_Manager/Performance ---- -<div>{{FirefoxSidebar}}</div><p>着重讲几点框架脚本/消息管理器的使用方式和替代方法,以避免相关的性能缺陷。</p> - -<p>要牢记几点:</p> - -<ul> - <li>附加组件启动时注册的脚本将在会话恢复时被执行。较长的执行时间会使浏览器启动后的响应速度缓慢。</li> - <li>框架脚本还可以在未恢复的标签页上被执行。所有开销不止发生在已激活的标签页,而是取决于会话中的标签页总数。</li> -</ul> - -<div class="note"> -<p>下面的例子为了简洁,省略了一些样板代码</p> - -<p>“更好”的例子还省略了一些最佳实践。只是为了演示如何解决各子主题中描述的问题。</p> -</div> - -<h2 id="性能的最佳实践">性能的最佳实践</h2> - -<h3 id="每个进程中声明无状态函数">每个进程中声明无状态函数</h3> - -<p>不良:</p> - -<pre class="brush: js">// addon.js -<code>Services.mm.loadFrameScript</code>("framescript.js", true) -</pre> - -<pre class="brush: js">// framescript.js - -const precomputedConstants = // ... - -function helper(window, action) { - // ... do some work on the window -} - -function doSomething(message) { - result = helper(content, message.data) - sendAsyncMessage("my-addon:response-from-child", {something: result}) -} - -addMessageListener("my-addon:request-from-parent", doSomething) -</pre> - -<p> </p> - -<p>Why is this bad? Because declared functions are also objects. And since frame scripts get evaluated for each tab this means new function objects get instantiated, new constants get computed, block scopes must be set up etc.</p> - -<p>While it may seem fairly innocencous in this toy example, real scripts often have a lot more functions and initialize some fairly heavyweight objects.</p> - -<p>更好:</p> - -<p><em>addon.js 如上</em></p> - -<pre class="brush: js">// framescript.js -Components.utils.import("resource://my-addon/processModule.jsm", {}).addFrame(this) -</pre> - -<pre class="brush: js">// processModule.jsm - -const EXPORTED_SYMBOLS = ['addFrame']; - -const precomputedConstants = // ... - -function helper(window, action) { - // ... do some work on the window -} - -function doSomething(message) { - frameGlobal = message.target - result = helper(frameGlobal.content, message.data) - frameGlobal.sendAsyncMessage("my-addon:response-from-child", {something: result}) -} - -function addFrame(frameGlobal) { - frameGlobal.addMessageListener("my-addon:request-from-parent", doSomething) -} -</pre> - -<p><a href="/en-US/docs/Mozilla/JavaScript_code_modules/Using">Javascript modules</a> are per-process singletons and thus all their objects are only initialized once, which makes them suitable for stateless callbacks.<br> - <br> - But care must be taken to not leak references to the frame script global when it is passed into a JSM. Alternatively the frame's <a href="/en-US/Firefox/Multiprocess_Firefox/Message_Manager/Frame_script_environment">unload event</a> or <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap">weak maps</a> can be used to ensure that frames can be cleaned up when their respective tab is closed.</p> - -<h3 id="每个进程中存放重量级的状态"><a name="per-process-state">每个进程中存放重量级的状态</a></h3> - -<p>不良:</p> - -<pre class="brush: js">// addon.js -var main = new MyAddonService(); - -main.onChange(stateChange); - -function stateChange() { - Services.mm.<code>broadcastAsyncMessage("my-addon:update-configuration", {newConfig: </code>main<code>.serialize()})</code> -} -</pre> - -<pre class="brush: js">// framescript.js -var mainCopy; - -function onUpdate(message) { - mainCopy = MyAddonService.deserialize(message.data.newConfig); -} - -addMessageListener("my-addon:update-configuration", onUpdate) - - -// mainCopy used by other functions -</pre> - -<p>The main issue here is that a separate object is kept for each tab. Not only does that increase memory footprint but the deserialization also has to be executed seperately for each tab, thus requiring more CPU time.</p> - -<p>不良:</p> - -<pre class="brush: js">// addon.js -var main = new MyAddonService(); - -main.onChange(stateChange); - -function stateChange() { - Services.ppmm.<code>broadcastAsyncMessage("my-addon:update-configuration", {newConfig: </code>main<code>.serialize()})</code> -}</pre> - -<pre class="brush: js">// processModule.jsm -const EXPORTED_SYMBOLS = ['getMainCopy']; - -var mainCopy; - -Services.cpmm.addMessageListener(<code>"my-addon:update-configuration"</code>, function(message) { - mainCopy = message.data.newConfig; -}) - -funtion getMainCopy() { - return mainCopy; -} - -</pre> - -<pre class="brush: js">// framescript.js -Components.utils.import("resource://my-addon/processModule.jsm") - -// getMainCopy() used by other functions -</pre> - -<p> </p> - -<h3 id="不要在框架脚本中注册观察者(及其他到全局服务的回调)">不要在框架脚本中注册观察者(及其他到全局服务的回调)</h3> - -<p>不良:</p> - -<pre class="brush: js">//framescript.js -Services.obs.addObserver("document-element-inserted", { - observe: function(doc, topic, data) { - if(doc.ownerGlobal.top != content) - return; // bail out if this is for another tab - decorateDocument(doc); - } -})</pre> - -<p>Observer notifications get fired for events that happen <em>anywhere</em> in the browser, they are not scoped to the current tab. If each framescript registers a seperate listener then the observed action will trigger the callbacks in all tabs.</p> - -<p>Additionally the example above does not clean unregister itself, thus leaking objects each time a tab is closed. Frame message manager message and event listeners are limited in their lifetime to that of the frame itself, this does not apply to observer registrations.</p> - -<p>更好:</p> - -<p><span style="display: none;"> </span><span style="display: none;"> </span><span style="display: none;"> </span><span style="display: none;"> </span> </p> - -<dl> - <dt>content-document-global-created 通知</dt> - <dd>可以用 <a href="/en-US/Firefox/Multiprocess_Firefox/Message_Manager/Frame_script_environment">DOMWindowCreated</a> 事件取代</dd> - <dt>其他观察者和服务</dt> - <dd>应该在 <a href="/en-US/docs/Mozilla/Firefox/Multiprocess_Firefox/Message_Manager/Process_scripts">进程脚本</a> 中注册,或者用 JSM 代替</dd> -</dl> - -<p> </p> - -<h3 id="根据需要加载框架脚本">根据需要加载框架脚本</h3> - -<p>不良:</p> - -<pre class="brush: js">// addon.js -<code>Services.mm.loadFrameScript</code>("framescript.js", /*delayed:*/ true) - -// stuff communicating with the framescript -</pre> - -<pre class="brush: js">// framescript.js -function onlyOnceInABlueMoon() { - // we only need this during a total solar eclipse while goat blood rains from the sky - sendAsyncMessage('my-addon:paragraph-count', {num: content.document.querySelectorAll('p').length}) -} -addMessageListener("my-addon:request-from-parent", onlyOnceInABlueMoon) -</pre> - -<p>更好:</p> - -<pre class="brush: js">// addon.js -function onToolbarButton(event) { - let tabMM = gBrowser.mCurrentBrowser.frameLoader.messageManager; - let button = event.target; - let callback = (message) => { - tabMM.removeMessageListener("my-addon:paragraph-count", callback) - decorateButton(button, message.data.num) - } - tabMM.addMessageListener("my-addon:paragraph-count", callback); - tabMM.loadFrameScript("data:,sendAsyncMessage('my-addon:paragraph-count', {num: content.document.querySelectorAll('p').length})", false) -} - -function decorateButton(button, count) { - // do stuff with result -} -</pre> - -<p>This executes the script only when it is needed and only in one tab and allows it to be garbage-collected immediately after execution. As long as it the action does not happen frequently the memory and startup savings should outstrip the added cost of script evaluation.</p> - -<p>Delaying the script registration <a href="/en-US/docs/Observer_Notifications#Application_startup">until the session is restored</a> my provide some middle ground for some addons. It does not provide the same memory footprint reductions but it improves application startup.</p> - -<p> </p> - -<h3 id="向下推进信息,避免到父进程的调用">向下推进信息,避免到父进程的调用</h3> - -<p>不良:</p> - -<pre>// processscript.js - -function ContentPolicy() { - // ... -} - -Object.assign(ContentyPolicy.prototype, { - classDescription: ..., classID: ..., contractID: ..., - QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPolicy]), - - shouldLoad: function(type, location, origin, context) { - - let resultList = Services.cpmm.sendSyncMessage("my-addon:check-load", {destination: location, source: origin}) // <=== SYNC MESSAGE! - - if(resultList.every((r) => r == true)) - return Ci.nsIContentPolicy.<code>ACCEPT; - - return Ci.nsIContentPolicy.REJECT_REQUEST;</code> - } -}); - -// more boilerplate code here -</pre> - -<p>This example is a (somewhat condensed) <a href="/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIContentPolicy">content policy</a> which gets triggered for every network request in a child process to either allow or deny the request. Since the code calls to the parent process to check whether a specific request can be allowed it would slow down all page loads, as web pages generally issue dozens of requests.</p> - -<p>更好:</p> - -<p>Instead of only keeping the state in the parent an addon can employ a master-slave architecture where the parent has the authoritative state and replicates it to the child processes in advance so they can act based on their local copy.</p> - -<p>See the <a href="#per-process-state">previous chapter</a> on how to efficiently replicate addon state to each process.</p> - -<p> </p> - -<p> </p> - -<h3 id="附加组件卸载时的清理">附加组件卸载时的清理</h3> - -<p> </p> - -<p>不良:</p> - -<p>包括上述所有例子,*包括上述的“更好”*</p> - -<p>If your addon is restartless or uses the SDK then updates or the user turning it off and on will load to unload/reload events. Not handling those properly can lead to duplicate or conflicting code execution, especially when messages are sent. It can also lead to conflicts between the old and new code. Under some circumstances it may even cause exceptions when attempting to register something twice under the same ID.</p> - -<p>更好:</p> - -<pre class="brush: js">// addon.js -function onUnload() { - Services.mm.removeDelayedFrameScript("resources://my-addon/framescript.js"); - Services.ppmm.removeDelayedProcessScript("resources://my-addon/processcript.js"); - Services.mm.broadcastAsyncMessage("my-addon:unload"); - Services.ppmm.broadcastAsyncMessage("my-addon:unload"); -} -</pre> - -<p>在框架/进程脚本中:</p> - -<ul> - <li>移除各种监听器</li> - <li>移除观察者通知</li> - <li>移除自定义的类和服务</li> - <li><a href="/en-US/docs/Mozilla/Tech/XPCOM/Language_Bindings/Components.utils.Sandbox#Freeing_the_sandbox">nuke 沙盒</a></li> - <li><a href="/en-US/docs/Mozilla/JavaScript_code_modules/Using#Unloading_code_modules">卸载 JSM</a></li> - <li>必要时还原内容 DOM 的状态,例如移除附加组件加入的不再需要的互动 UI 元素</li> -</ul> diff --git a/files/zh-cn/mozilla/firefox/multiprocess_firefox/motivation/index.html b/files/zh-cn/mozilla/firefox/multiprocess_firefox/motivation/index.html deleted file mode 100644 index 7ae3c2aaef..0000000000 --- a/files/zh-cn/mozilla/firefox/multiprocess_firefox/motivation/index.html +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: 动机 -slug: Mozilla/Firefox/Multiprocess_Firefox/Motivation -translation_of: Mozilla/Firefox/Multiprocess_Firefox/Motivation ---- -<div>{{FirefoxSidebar}}</div><p>使 Firefox 在单独的进程中运行内容的主要原因:性能、安全和稳定性。</p> - -<h2 id="性能">性能</h2> - -<p>Mozilla 在过去两年对性能的工作重点是浏览器的响应速度。目标是减少 “<a href="/en-US/docs/Glossary/Jank">jank</a>”—在加载大的页面时、表单中输入、或者滚动时,浏览器暂时性失去响应。响应问题在当今较高吞吐量的网络上日益显著。这些工作大部分已被 <a href="https://wiki.mozilla.org/Performance/Snappy">Snappy 项目</a>完成。主要的重点在:</p> - -<ul> - <li>将运行时间长的操作移入单独的线程,使主线程可以继续向用户做出响应。</li> - <li>异步或者在其他线程进行 I/O,使主线程不必等待磁盘。</li> - <li>将运行时间长的代码拆分成较短片段,并且在事件循环之间运行。增量式垃圾收集就是一个例子。</li> -</ul> - -<p>大部分能轻易做到的事情在这些领域已经完成。剩下的问题比较难以解决。举例来说,JavaScript 执行和布局都发生在主线程,并且阻挡着事件循环。在单独的线程中运行这些组件是困难的,因为它们访问数据,像是 DOM,它们不是线程安全的。作为替代方案,我们已经考虑让事件循环运行在 JavaScript 执行的中间,但这样做会毁掉大量 Firefox 其他地方做出的假设(更不用提附加组件)。</p> - -<p>在一个单独的进程中运行网络内容,是这些方法的一个不错的替代品。类似线程的方式,Firefox 可以在 JavaScript 和布局在内容进程中运行时运行事件循环。但是不同于线程,用户界面(UI)的代码不能访问内容 DOM 或者其他内容的数据结构,因此没有必要进行锁定或者线程安全。而不足之处是,理所当然的,任何在 Firefox UI 进程中运行的需要访问内容数据的代码,都必须明确的通过消息传递的方式来访问内容数据。</p> - -<p>我们觉得这种权衡有道理,有几个原因:</p> - -<ul> - <li>Firefox 代码访问内容 DOM 并不是很常见。</li> - <li>与 Firefox OS 共享的代码已经使用消息传递。</li> - <li>在多进程模型中,使用消息传递访问内容失败的 Firefox 代码将明显的失败,一致的方式。在线程模型中,没有适当锁定的访问内容的代码将以微妙的方式失败,很难调试。</li> -</ul> - -<h2 id="安全">安全</h2> - -<p>目前来说,如果某人发现了一个 Firefox 中的可利用漏洞,他们能够接管用户的计算机。有很多种技术来缓解这种问题,但其中最强大的是<a href="http://en.wikipedia.org/wiki/Sandbox_%28computer_security%29">沙盒</a>。从技术上讲,沙盒不需要多个进程。但是,涵盖沙盒的单进程 Firefox 并不会很有用。沙盒能阻止进程执行,一个乖巧进程绝不会做的操作。遗憾的是,乖巧的 Firefox 进程(尤其是已安装附加组件的 Firefox)会访问很多网络和文件系统。因此,对单进程的 Firefox 沙盒不能限制太多。</p> - -<p>在多进程 Firefox 中,内容进程将被沙盒化。一个乖巧的内容进程不会直接访问文件系统;它必须询问主线程来执行请求。在那时,主进程可以验证请求是否安全和合理。因此,对内容进程的沙盒化可以是相当严格的。我们希望这样的布局可以使 Firefox 更难被安全漏洞所利用。</p> - -<h2 id="稳定性">稳定性</h2> - -<p>目前来说,在网页中运行的代码出现崩溃将导致整个浏览器崩溃。而多进程 Firefox 中,只有崩溃的内容进程会被终止。</p> - -<div class="note"> -<p>此页面整合了很多 Bill McCloskey 的有关多进程 Firefox 的文章: <a href="http://billmccloskey.wordpress.com/2013/12/05/multiprocess-firefox/">http://billmccloskey.wordpress.com/2013/12/05/multiprocess-firefox/</a></p> -</div> - -<p> </p> diff --git a/files/zh-cn/mozilla/firefox/multiprocess_firefox/tab_selection_in_multiprocess_firefox/index.html b/files/zh-cn/mozilla/firefox/multiprocess_firefox/tab_selection_in_multiprocess_firefox/index.html deleted file mode 100644 index f396b7dcb5..0000000000 --- a/files/zh-cn/mozilla/firefox/multiprocess_firefox/tab_selection_in_multiprocess_firefox/index.html +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: 多进程 Firefox 中的标签选择 -slug: Mozilla/Firefox/Multiprocess_Firefox/Tab_selection_in_multiprocess_Firefox -translation_of: Mozilla/Firefox/Multiprocess_Firefox/Tab_selection_in_multiprocess_Firefox ---- -<div>{{FirefoxSidebar}}</div><p>在单进程的 Firefox 中,当用户切换标签页时,这是同步操作。当浏览器加载新选择的标签页时,浏览器会阻塞,然后切换到该标签页。这表示标签页选定是通过设置 XUL 的 tab 对象的 <code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/Attribute/selected">selected</a></code> 属性。代码(包括浏览器代码、扩展或主题)想要更改选定标签页的外观时,可以使用 <code>selected</code> 属性来应用 CSS 到该标签页。</p> - -<p>在多进程的 Firefox 中,标签页切换是异步的。在用户切换标签页时,chrome 进程发送一个请求到内容进程来加载该页面到新选择的标签页。chrome 进程中的函数会立即返回,以便其他代码可以运行。一旦内容进程准备就绪,它发回一个消息到 chrome 进程,然后在用户界面中切换标签页。</p> - -<p>还有 chrome 进程中的计时器:如果内容进程在计时器到期前没有响应,那么浏览器虽然继续切换标签页,但也显示一个包含标识的空标签页,直到目前的内容进程完成页面的加载。目前的计时器被设定为300毫秒。</p> - -<p>还有,相应的,两个属性用于指示标签页选择:</p> - -<ul> - <li>旧的 <code><a href="/en-US/docs/XUL/Attribute/selected">selected</a></code> 属性是同步设置,在进程的开始。它表示标签页选择已经开始,但在用户界面还没有被更新。</li> - <li>新的属性 <code><a href="/en-US/docs/Mozilla/Tech/XUL/Attribute/visuallyselected">visuallyselected</a></code> 现在表示浏览器在用户界面已实际更新(显示)的标签页,包括内容进程已就绪或者计时器已过期这两种情况。</li> -</ul> - -<p>这意味着想要对当前选中标签页应用的样式需要使用 <code>visuallyselected</code> 属性。如果使用 <code>selected</code> 属性,那么在新选择的标签页的样式被更新时会有一个短暂的断层,浏览器仍然在显示旧的标签页的内容。</p> - -<footer class="entry-meta"> </footer> diff --git a/files/zh-cn/mozilla/firefox/multiprocess_firefox/technical_overview/index.html b/files/zh-cn/mozilla/firefox/multiprocess_firefox/technical_overview/index.html deleted file mode 100644 index 18917fabc4..0000000000 --- a/files/zh-cn/mozilla/firefox/multiprocess_firefox/technical_overview/index.html +++ /dev/null @@ -1,164 +0,0 @@ ---- -title: 技术概述 -slug: Mozilla/Firefox/Multiprocess_Firefox/Technical_overview -translation_of: Mozilla/Firefox/Multiprocess_Firefox/Technical_overview ---- -<div>{{FirefoxSidebar}}</div><div class="note"> -<p>这个页面主要是 Bill McCloskey 的关于多进程 Firefox 的摘要: <a class="external external-icon" href="http://billmccloskey.wordpress.com/2013/12/05/multiprocess-firefox/">http://billmccloskey.wordpress.com/2013/12/05/multiprocess-firefox/</a></p> -</div> - -<p>从总体上来看,多进程 Firefox 的运行方式如下。当 Firefox 启动时的进程被称为父进程。起初和单程 Firefox 一样:打开一个窗口用于显示<code><a href="http://mxr.mozilla.org/mozilla-central/source/browser/base/content/browser.xul">browser.xul</a>,它</code>包含主要的 UI 元素。XUL是Firefox的GUI工具箱, 由它提供了类似Web HTML的方式来声明定义UI元素。Firefox 的 UI也有一个<code>window</code> 对象。它有一个<code>document</code> 属性,并含了在 <code>browser.xul</code> 中所有的XML元素。所有的菜单,工具栏,侧边栏和标签页都是 <code>document</code> 下的 XML 元素。每一个标签页(tab)都包含一个 <code><browser></code> 元素用来显示网页内容.</p> - -<p>多进程与单进程的最主要区别是每个 <code><browser></code> 都有一个<code>remote="true"</code> 的属性。当这个 browser 元素被添加到 document 时,将会启动一个新的内容进程,也称为子进程。同时创建一个跨进程通信的IPC通道。开始时子进程显示<code> about:blank</code>,父进程通过给子进程发送命令来导航显示的内容。</p> - -<h2 id="绘制"><strong id="drawing">绘制</strong></h2> - -<p>有时,显示的网页内容需要从子进程传递到父进程然后再显示到屏幕。多进程模式依赖于一个新的特性(<a href="http://benoitgirard.wordpress.com/2012/05/15/off-main-thread-compositing-omtc-and-why-it-matters/"><em>off main thread compositing</em></a> ,OMTC)。简单来说,每个窗口被分成若干层,概念上类似于photoshop中的层。每一次Firefox进行渲染时,这些层被提交到合成线程来构建为一个图片。</p> - -<p>层是树状结构。树的根节点对应一个Firefox窗口。根节点层包其他子层,如绘制菜单和标签页。一个子树对应所有网页内容。网页内容可被分成更多的层,这些层都以同一个内容层为根节点。</p> - -<p>在多进程Firefox中,内容层的子树是一个垫片(shim)。在大多数时间,它包含一个能够简单地保持到子进程的通信链接的引用的占位符节点。内容进程包括网页内容的层树。它构建并且描绘这个层树。当描绘完成时,它通过IPC将层数的结构发送给父进程。当父进程受到这个层树时,它删除这个占位符内容节点并且将其替换为源于内容的实际树。然后它正常地合成并且绘制。当它完成后,它将占位符放回。</p> - -<p>因为Firefox OS的需要,OMTC怎样于多进程一起工作的基本构架已经存在了一段时间。然而,Matt Woodrow和 David Anderson已经完成了大量工作来使得其在Windows,Mac和Linux正常工作。一个巨大的挑战是使多进程Firefox能够在所有平台下都能使OMTC启动。现在,只有Macs默认使用OMTC。</p> - -<h2 id="用户输入"><strong id="input">用户输入</strong></h2> - -<p>Events in Firefox work the same way as they do on the web. Namely, there is a DOM tree for the entire window, and events are threaded through this tree in capture and bubbling phases. Imagine that the user clicks on a button on a web page. In single-process Firefox, the root DOM node of the Firefox window gets the first chance to process the event. Then, nodes lower down in the DOM tree get a chance. The event handling proceeds down through to the XUL <code><browser></code> element. At this point, nodes in the web page’s DOM tree are given a chance to handle the event, all the way down to the button. The bubble phase follows, running in the opposite order, all the way back up to the root node of the Firefox window.</p> - -<p>With multiple processes, event handling works the same way until the <code><browser></code> element is hit. At that point, if the event hasn’t been handled yet, it gets sent to the child process by IPC, where handling starts at the root of the content DOM tree. The parent process then waits to run its bubbling phase until the content process has finished handling the event.</p> - -<h2 id="进程间通信"><strong id="ipc">进程间通信</strong></h2> - -<p>所有 IPC 使用 Chromium IPC 程序库。每个子进程都有单独的与父进程的 IPC 链接。子进程之间不能直接通信。为了避免死锁和确保响应能力,父进程不允许坐等子进程的消息。但是,子进程可以阻塞等待父进程的消息。</p> - -<p>相比于人们预期的直接通过 IPC 发送数据包,我们使用代码生成使这个过程更漂亮。IPC 协议在 <a href="https://wiki.mozilla.org/IPDL">IPDL</a> 中定义, which sort of stands for “inter-* protocol definition language”. A typical IPDL file is <code><a href="http://mxr.mozilla.org/mozilla-central/source/netwerk/ipc/PNecko.ipdl">PNecko.ipdl</a></code>. It defines a set messages and their parameters. Parameters are serialized and included in the message. To send a message <code>M</code>, C++ code just needs to call the method <code>SendM</code>. To receive the message, it implements the method <code>RecvM</code>.</p> - -<p>IPDL is used in all the low-level C++ parts of Gecko where IPC is required. In many cases, IPC is just used to forward actions from the child to the parent. This is a common pattern in Gecko:</p> - -<pre class="brush: cpp">void AddHistoryEntry(param) { - if (XRE_GetProcessType() == GeckoProcessType_Content) { - // If we're in the child, ask the parent to do this for us. - SendAddHistoryEntry(param); - return; - } - - // Actually add the history entry... -} - -bool RecvAddHistoryEntry(param) { - // Got a message from the child. Do the work for it. - AddHistoryEntry(param); - return true; -} -</pre> - -<p>When <code>AddHistoryEntry</code> is called in the child, we detect that we’re inside the child process and send an IPC message to the parent. When the parent receives that message, it calls <code>AddHistoryEntry</code> on its side.</p> - -<p>For a more realistic illustration, consider the Places database, which stores visited URLs for populating the awesome bar. Whenever the user visits a URL in the content process, we call <a href="http://mxr.mozilla.org/mozilla-central/source/toolkit/components/places/History.cpp?rev=8b9687f6c602#2326">this code</a>. Notice the content process check followed by the <code>SendVisitURI</code> call and an immediate return. The message is received <a href="http://mxr.mozilla.org/mozilla-central/source/dom/ipc/ContentParent.cpp?rev=fecda5f4a0df#2666">here</a>; this code just calls <code>VisitURI</code> in the parent.</p> - -<p>The code for IndexedDB, the places database, and HTTP connections all runs in the parent process, and they all use roughly the same proxying mechanism in the child.</p> - -<h2 id="框架脚本"><strong id="contentscripts">框架脚本</strong></h2> - -<p>IPDL takes care of passing messages in C++, but much of Firefox is actually written in JavaScript. Instead of using IPDL directly, JavaScript code relies on <a href="/en-US/Firefox/Multiprocess_Firefox/The_message_manager">the message manager</a> to communicate between processes. To use the message manager in JS, you need to get hold of a message manager object. There is a global message manager, message managers for each Firefox window, and message managers for each <code><browser></code> element. A message manager can be used to load JS code into the child process and to exchange messages with it.</p> - -<p>As a simple example, imagine that we want to be informed every time a <code>load</code> event triggers in web content. We’re not interested in any particular browser or window, so we use the global message manager. The basic process is as follows:</p> - -<pre class="brush: js">// Get the global message manager. -let mm = Cc["@<span class="skimlinks-unlinked">mozilla.org/globalmessagemanager;1</span>"]. - getService(Ci.nsIMessageListenerManager); - -// Wait for load event. -mm.addMessageListener("GotLoadEvent", function (msg) { - dump("Received load event: " + <span class="skimlinks-unlinked">msg.data.url</span> + "\n"); -}); - -// Load code into the child process to listen for the event. -mm.loadFrameScript("chrome://content/<span class="skimlinks-unlinked">content-script.js</span>", true); -</pre> - -<p>For this to work, we also need to have a file <code>content-script.js</code>:</p> - -<pre class="brush: js">// Listen for the load event. -addEventListener("load", function (e) { - // Inform the parent process. - let docURL = content.document.documentURI; - sendAsyncMessage("GotLoadEvent", {url: docURL}); -}, false); -</pre> - -<p>This file is called a <em>frame script</em>. When the <code>loadFrameScript</code> function call runs, the code for the script is run once for each <code><browser></code> element. This includes both remote browsers and regular ones. If we had used a per-window message manager, the code would only be run for the browser elements in that window. Any time a new browser element is added, the script is run automatically (this is the purpose of the <code>true</code> parameter to <code>loadFrameScript</code>). Since the script is run once per browser, it can access the browser’s window object and docshell via the <code>content</code> and <code>docShell</code> globals.</p> - -<p>The great thing about frame scripts is that they work in both single-process and multiprocess Firefox. To learn more about the message manager, see the <a href="/en-US/Firefox/Multiprocess_Firefox/The_message_manager">message manager guide</a>.</p> - -<h2 id="跨进程_API"><strong id="shims">跨进程 API</strong></h2> - -<p>There are a lot of APIs in Firefox that cross between the parent and child processes. An example is the <code>webNavigation</code> property of XUL <code><browser></code> elements. The <code>webNavigation</code> property is an object that provides methods like <code>loadURI</code>, <code>goBack</code>, and <code>goForward</code>. These methods are called in the parent process, but the actions need to happen in the child. First I’ll cover how these methods work in single-process Firefox, and then I’ll describe how we adapted them for multiple processes.</p> - -<p>The <code>webNavigation</code> property is defined using the XML Binding Language (XBL). XBL is a declarative language for customizing how XML elements work. Its syntax is a combination of XML and JavaScript. Firefox uses XBL extensively to customize XUL elements like <code><browser></code> and <code><tabbrowser></code>. The <code><browser></code> customizations reside in <code><a href="http://mxr.mozilla.org/mozilla-central/source/toolkit/content/widgets/browser.xml?rev=754cf7fc84cd">browser.xml</a></code>. <a href="http://mxr.mozilla.org/mozilla-central/source/toolkit/content/widgets/browser.xml?rev=754cf7fc84cd#262">Here</a> is how <code>browser.webNavigation</code> is defined:</p> - -<pre class="brush: xml"><field name="_webNavigation">null</field> - -<property name="webNavigation" readonly="true"> - <getter> - <![CDATA[ - if (!this._webNavigation) - this._webNavigation = this.docShell.QueryInterface(Components.interfaces.nsIWebNavigation); - return this._webNavigation; - ]]> - </getter> -</property> -</pre> - -<p>This code is invoked whenever JavaScript code in Firefox accesses <code>browser.webNavigation</code>, where <code>browser</code> is some <code><browser></code> element. It checks if the result has already been cached in the <code>browser._webNavigation</code> field. If it hasn’t been cached, then it fetches the navigation object based off the browser’s <em>docshell</em>. The docshell is a Firefox-specific object that encapsulates a lot of functionality for loading new pages, navigating back and forth, and saving page history. In multiprocess Firefox, the docshell lives in the child process. Since the <code>webNavigation</code> accessor runs in the parent process, <code>this.docShell</code> above will just return null. As a consequence, this code will fail completely.</p> - -<p>One way to fix this problem would be to create a fake docshell in C++ that could be returned. It would operate by sending IPDL messages to the real docshell in the child to get work done. We may eventually take this route in the future. We decided to do the message passing in JavaScript instead, since it’s easier and faster to prototype things there. Rather than change every docshell-using accessor to test if we’re using multiprocess browsing, we decided to create a new XBL binding that applies only to remote <code><browser></code> elements. It is called <a href="http://mxr.mozilla.org/mozilla-central/source/toolkit/content/widgets/remote-browser.xml?rev=9583bd3099ae"><code>remote-browser.xml</code></a>, and it extends the existing <code>browser.xml</code> binding.</p> - -<p>The <code>remote-browser.xml</code> binding returns a JavaScript <em>shim object</em> whenever anyone uses <code>browser.webNavigation</code> or other similar objects. The shim object is implemented <a href="http://mxr.mozilla.org/mozilla-central/source/toolkit/modules/RemoteWebNavigation.jsm">in its own JavaScript module</a>. It uses the message manager to send messages like <code>"WebNavigation:LoadURI"</code> to <a href="http://mxr.mozilla.org/mozilla-central/source/toolkit/content/browser-child.js?rev=9583bd3099ae#107">a content script loaded by <code>remote-browser.xml</code></a>. The content script performs the actual action.</p> - -<p>The shims we provide emulate their real counterparts imperfectly. They offer enough functionality to make Firefox work, but add-ons that use them may find them insufficient. I’ll discuss strategies for making add-ons work in more detail later.</p> - -<h2 id="跨进程对象包装器"><strong id="cpows">跨进程对象包装器</strong></h2> - -<p>The message manager API does not allow the parent process to call <code>sendSyncMessage</code>; that is, the parent is not allowed to wait for a response from the child. It’s detrimental for the parent to wait on the child, since we don’t want the browser UI to be unresponsive because of slow content. However, converting Firefox code to be asynchronous (i.e., to use <code>sendAsyncMessage</code> instead) can sometimes be onerous. As an expedient, we’ve introduced a new primitive that allows code in the parent process to access objects in the child process synchronously.</p> - -<p>These objects are called cross-process object wrappers, frequently abbreviated to CPOWs. They’re created using the message manager. Consider this example content script:</p> - -<pre class="brush: js">addEventListener("load", function (e) { - let doc = content.document; - sendAsyncMessage("GotLoadEvent", <strong>{}, {document: doc}</strong>); -}, false); -</pre> - -<p>In this code, we want to be able to send a reference to the document to the parent process. We can’t use the second parameter to <code>sendAsyncMessage</code> to do this: that argument is converted to JSON before it is sent up. The optional third parameter allows us to send object references. Each property of this argument becomes accessible in the parent process as a CPOW. Here’s what the parent code might look like:</p> - -<pre class="brush: js">let mm = Cc["@<span class="skimlinks-unlinked">mozilla.org/globalmessagemanager;1</span>"]. - getService(Ci.nsIMessageListenerManager); - -mm.addMessageListener("GotLoadEvent", function (msg) { - let uri = <strong>msg.objects.document.documentURI</strong>; - dump("Received load event: " + uri + "\n"); -}); -mm.loadFrameScript("chrome://content/<span class="skimlinks-unlinked">content-script.js</span>", true); -</pre> - -<p>It’s important to realize that we’re send object <em>references</em>. The <code>msg.objects.document</code> object is only a wrapper. The access to its <code>documentURI</code> property sends a synchronous message down to the child asking for the value. The dump statement only happens after a reply has come back from the child.</p> - -<p>Because every property access sends a message, CPOWs can be slow to use. There is no caching, so 1,000 accesses to the same property will send 1,000 messages.</p> - -<p>Another problem with CPOWs is that they violate some assumptions people might have about message ordering. Consider this code:</p> - -<pre class="brush: js">mm.addMessageListener("GotLoadEvent", function (msg) { - mm.sendAsyncMessage("ChangeDocumentURI", {newURI: "<span class="skimlinks-unlinked">hello.com</span>"}); - let uri = <strong>msg.objects.document.documentURI</strong>; - dump("Received load event: " + uri + "\n"); -}); -</pre> - -<p>This code sends a message asking the child to change the current document URI. Then it accesses the current document URI via a CPOW. You might expect the value of <code>uri</code> to come back as <code>"hello.com"</code>. But it might not. In order to avoid deadlocks, CPOW messages can bypass normal messages and be processed first. It’s possible that the request for the <code>documentURI</code> property will be processed before the <code>"ChangeDocumentURI"</code> message, in which case <code>uri</code> will have some other value.</p> - -<p>For this reason, it’s best not to mix CPOWs with normal message manager messages. It’s also a bad idea to use CPOWs for anything security-related, since you may not get results that are consistent with surrounding code that might use the message manager.</p> - -<p>Despite these problems, we’ve found CPOWs to be useful for converting certain parts of Firefox to be multiprocess-compatible. It’s best to use them in cases where users are less likely to notice poor responsiveness. As an example, we use CPOWs to implement the context menu that pops up when users right-click on content elements. Whether this code is asynchronous or synchronous, the menu cannot be displayed until content has responded with data about the element that has been clicked. The user is unlikely to notice if, for example, tab animations don’t run while waiting for the menu to pop up. Their only concern is for the menu to come up as quickly as possible, which is entirely gated on the response time of the content process. For this reason, we chose to use CPOWs, since they’re easier than converting the code to be asynchronous.</p> - -<p>It’s possible that CPOWs will be phased out in the future. Asynchronous messaging using the message manager gives a user experience that is at least as good as, and often strictly better than, CPOWs. We strongly recommend that people use the message manager over CPOWs when possible. Nevertheless, CPOWs are sometimes useful.</p> diff --git a/files/zh-cn/mozilla/firefox/multiprocess_firefox/which_uris_load_where/index.html b/files/zh-cn/mozilla/firefox/multiprocess_firefox/which_uris_load_where/index.html deleted file mode 100644 index df24c59988..0000000000 --- a/files/zh-cn/mozilla/firefox/multiprocess_firefox/which_uris_load_where/index.html +++ /dev/null @@ -1,53 +0,0 @@ ---- -title: 各类 URI 在哪里加载 -slug: Mozilla/Firefox/Multiprocess_Firefox/Which_URIs_load_where -translation_of: Mozilla/Firefox/Multiprocess_Firefox/Which_URIs_load_where ---- -<div>{{FirefoxSidebar}}</div><p>浏览器加载一个页面到 chrome 或者内容进程,基于它的 URI 方案(URI scheme)。对于某些方案,你可以改变默认行为。</p> - -<table class="standard-table"> - <thead> - <tr> - <th scope="col">Scheme</th> - <th scope="col">Behavior</th> - </tr> - </thead> - <tbody> - <tr> - <td><code>about:</code></td> - <td> - <p>默认情况下,<code>about:</code> 页面始终在 chrome 进程加载。但是,在你注册新的 <code>about:</code> 页面时,你可以改变此默认值。</p> - - <p>两个新标志定义在 <code><a href="https://dxr.mozilla.org/mozilla-central/source/netwerk/protocol/about/nsIAboutModule.idl">nsIAboutModule</a></code>:</p> - - <ul> - <li><code>URI_CAN_LOAD_IN_CHILD</code>: 页面将加载在加载它的 <code><a href="/en-US/docs/XUL/browser">browser</a></code> 所在的进程。</li> - <li><code>URI_MUST_LOAD_IN_CHILD</code>: 页面将始终加载在一个子进程</li> - </ul> - - <p>要使用上述任一标志,在你的 <a href="/en-US/docs/Custom_about:_URLs">注册 <code>about:</code> URI 的代码</a> 的 <code>getURIFlags</code> 实现中返回它。</p> - </td> - </tr> - <tr> - <td><code>chrome:</code></td> - <td> - <p><code>默认情况下,chrome:</code> 页面始终加载在 chrome 进程。但是,在你注册新的 <code>chrome:</code> 页面时,你可以改变此默认值。</p> - - <p>两个新标志定义在 <a href="/en-US/docs/Chrome_Registration">chrome.manifest 文件</a>:</p> - - <ul> - <li>remoteenabled: 页面将加载在加载它的 <code><a href="/en-US/docs/XUL/browser">browser</a></code> 所在的进程。</li> - <li>remoterequired: 页面将始终加载在一个子进程</li> - </ul> - </td> - </tr> - <tr> - <td><code>file:</code></td> - <td>始终在内容进程中加载。</td> - </tr> - <tr> - <td><code>resource:</code></td> - <td>始终在内容进程中加载。</td> - </tr> - </tbody> -</table> |