| Index: chrome/common/extensions/docs/server2/templates/articles/overview.html
|
| diff --git a/chrome/common/extensions/docs/server2/templates/articles/overview.html b/chrome/common/extensions/docs/server2/templates/articles/overview.html
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..ca9972bd8893de87659f07597a9e91f53585251a
|
| --- /dev/null
|
| +++ b/chrome/common/extensions/docs/server2/templates/articles/overview.html
|
| @@ -0,0 +1,661 @@
|
| +<h1>Overview</h1>
|
| +
|
| +
|
| +<p>
|
| +Once you've finished this page
|
| +and the
|
| +<a href="getstarted.html">Getting Started</a> tutorial,
|
| +you'll be all set to start writing extensions and packaged apps.
|
| +</p>
|
| +
|
| +<p class="caution">
|
| +<strong>Note:</strong>
|
| +<em>Packaged apps</em> are implemented as extensions,
|
| +so unless otherwise stated,
|
| +everything in this page applies to packaged apps.
|
| +</p>
|
| +
|
| +<h2 id="what">The basics</h2>
|
| +
|
| +<p>
|
| +An extension is a zipped bundle of files—HTML,
|
| +CSS, JavaScript, images, and anything else you need—that
|
| +adds functionality to the Google Chrome browser.
|
| +Extensions are essentially web pages,
|
| +and they can use all the
|
| +<a href="api_other.html">APIs that the browser provides to web pages</a>,
|
| +from XMLHttpRequest to JSON to HTML5.
|
| +</p>
|
| +
|
| +<p>
|
| +Extensions can interact with web pages or servers using
|
| +<a href="content_scripts.html">content scripts</a> or
|
| +<a href="xhr.html">cross-origin XMLHttpRequests</a>.
|
| +Extensions can also interact programmatically
|
| +with browser features such as
|
| +<a href="bookmarks.html">bookmarks</a>
|
| +and <a href="tabs.html">tabs</a>.
|
| +</p>
|
| +
|
| +<h3 id="extension-ui">Extension UIs</h3>
|
| +
|
| +<p>
|
| +Many extensions—but not packaged apps—add
|
| +UI to Google Chrome in the form of
|
| +<a href="browserAction.html">browser actions</a>
|
| +or <a href="pageAction.html">page actions</a>.
|
| +Each extension can have at most one browser action or page action.
|
| +Choose a <b>browser action</b> when the extension is relevant to most pages.
|
| +Choose a <b>page action</b> when the extension's icon
|
| +should appear or disappear,
|
| +depending on the page.
|
| +</p>
|
| +
|
| +<table class="columns">
|
| +<tr>
|
| + <td width="33%">
|
| + <img src="{{static}}/images/overview/browser-action.png"
|
| + width="147" height="100"
|
| + alt="screenshot" />
|
| + </td>
|
| + <td width="33%">
|
| + <img src="{{static}}/images/overview/page-action.png"
|
| + width="147" height="100"
|
| + alt="screenshot" />
|
| + </td>
|
| + <td>
|
| + <img src="{{static}}/images/overview/browser-action-with-popup.png"
|
| + width="147" height="100"
|
| + alt="screenshot" />
|
| + </td>
|
| +</tr>
|
| +
|
| +<tr>
|
| + <td>
|
| + This <a href="samples.html#gmail">mail extension</a>
|
| + uses a <em>browser action</em>
|
| + (icon in the toolbar).
|
| + </td>
|
| + <td>
|
| + This <a href="samples.html#mappy">map extension</a>
|
| + uses a <em>page action</em>
|
| + (icon in the address bar)
|
| + and <em>content script</em>
|
| + (code injected into a web page).
|
| + </td>
|
| + <td>
|
| + This <a href="samples.html#news">news extension</a>
|
| + features a browser action that,
|
| + when clicked,
|
| + shows a <em>popup</em>.
|
| + </td>
|
| +</tr>
|
| +</table>
|
| +
|
| +<p>
|
| +Extensions (and packaged apps) can also present a UI in other ways,
|
| +such as adding to the Chrome context menu,
|
| +providing an options page,
|
| +or using a content script that changes how pages look.
|
| +See the <a href="devguide.html">Developer's Guide</a>
|
| +for a complete list of extension features,
|
| +with links to implementation details
|
| +for each one.
|
| +</p>
|
| +
|
| +
|
| +<h3 id="packagedapp-ui">Packaged app UIs</h3>
|
| +
|
| +<p>
|
| +A packaged app usually presents its main functionality using
|
| +an HTML page that's bundled into the app.
|
| +For example, the following packaged app
|
| +displays a Flash file within an HTML page.
|
| +</p>
|
| +
|
| +<img src="{{static}}/images/overview/flash-app.png"
|
| + width="372" height="300"
|
| + alt="screenshot" />
|
| +
|
| +<p>
|
| +For more information,
|
| +see <a href="apps.html">Packaged Apps</a>.
|
| +</p>
|
| +
|
| +<h2 id="files">Files</h2>
|
| +<p>
|
| +Each extension has the following files:
|
| +
|
| +</p>
|
| +
|
| +<ul>
|
| + <li>A <b>manifest file</b></li>
|
| + <li>One or more <b>HTML files</b> (unless the extension is a theme)</li>
|
| + <li><em>Optional:</em> One or more <b>JavaScript files</b></li>
|
| + <li><em>Optional:</em> Any other files your extension needs—for
|
| + example, image files</li>
|
| +</ul>
|
| +
|
| +<p>
|
| +While you're working on your extension,
|
| +you put all these files into a single folder.
|
| +When you distribute your extension,
|
| +the contents of the folder are packaged into a special ZIP file
|
| +that has a <code>.crx</code> suffix.
|
| +If you upload your extension using the
|
| +<a href="https://chrome.google.com/webstore/developer/dashboard">Chrome Developer Dashboard</a>,
|
| +the <code>.crx</code> file is created for you.
|
| +For details on distributing extensions,
|
| +see <a href="hosting.html">Hosting</a>.
|
| +</p>
|
| +
|
| +
|
| +<h3 id="relative-urls">Referring to files</h3>
|
| +
|
| +<p>
|
| +You can put any file you like into an extension,
|
| +but how do you use it?
|
| +Usually,
|
| +you can refer to the file using a relative URL,
|
| +just as you would in an ordinary HTML page.
|
| +Here's an example of referring to
|
| +a file named <code>myimage.png</code>
|
| +that's in a subfolder named <code>images</code>.
|
| +</p>
|
| +
|
| +<pre>
|
| +<img <b>src="images/myimage.png"</b>>
|
| +</pre>
|
| +
|
| +<p>
|
| +As you might notice while you use the Google Chrome debugger,
|
| +every file in an extension is also accessible by an absolute URL like this:
|
| +</p>
|
| +
|
| +<blockquote>
|
| +<b>chrome-extension://</b><em><extensionID></em><b>/</b><em><pathToFile></em>
|
| +</blockquote>
|
| +
|
| +<p>
|
| +In that URL, the <em><extensionID></em> is a unique identifier
|
| +that the extension system generates for each extension.
|
| +You can see the IDs for all your loaded extensions
|
| +by going to the URL <b>chrome://extensions</b>.
|
| +The <em><pathToFile></em> is the location of the file
|
| +under the extension's top folder;
|
| +it's the same as the relative URL.
|
| +</p>
|
| +
|
| +<p>
|
| +While you're working on an extension
|
| +(before it's packaged),
|
| +the extension ID can change.
|
| +Specifically, the ID of an unpacked extension will change
|
| +if you load the extension from a different directory;
|
| +the ID will change again when you package the extension.
|
| +If your extension's code
|
| +needs to specify the full path to a file within the extension,
|
| +you can use the <code>@@extension_id</code>
|
| +<a href="i18n.html#overview-predefined">predefined message</a>
|
| +to avoid hardcoding the ID during development.
|
| +</p>
|
| +
|
| +<p>
|
| +When you package an extension
|
| +(typically, by uploading it with the dashboard),
|
| +the extension gets a permanent ID,
|
| +which remains the same even after you update the extension.
|
| +Once the extension ID is permanent,
|
| +you can change all occurrences of
|
| +<code>@@extension_id</code> to use the real ID.
|
| +</p>
|
| +
|
| +
|
| +<h3>The manifest file</h3>
|
| +
|
| +<p>
|
| +The manifest file, called <code>manifest.json</code>,
|
| +gives information about the extension,
|
| +such as the most important files
|
| +and the capabilities that the extension might use.
|
| +Here's a typical manifest file for a browser action
|
| +that uses information from google.com:
|
| +</p>
|
| +
|
| +<pre>
|
| +{
|
| + "name": "My Extension",
|
| + "version": "2.1",
|
| + "description": "Gets information from Google.",
|
| + "icons": { "128": "icon_128.png" },
|
| + "background": {
|
| + "scripts": ["bg.js"]
|
| + },
|
| + "permissions": ["http://*.google.com/", "https://*.google.com/"],
|
| + "browser_action": {
|
| + "default_title": "",
|
| + "default_icon": "icon_19.png",
|
| + "default_popup": "popup.html"
|
| + }
|
| +}</pre>
|
| +
|
| +<p>
|
| +For details, see
|
| +<a href="manifest.html">Manifest Files</a>.
|
| +</p>
|
| +
|
| +<h2 id="arch">Architecture</h2>
|
| +
|
| +<p>
|
| +Many extensions have a <em>background page</em>,
|
| +an invisible page
|
| +that holds the main logic of the extension.
|
| +An extension can also contain other pages
|
| +that present the extension's UI.
|
| +If an extension needs to interact with web pages that the user loads
|
| +(as opposed to pages that are included in the extension),
|
| +then the extension must use a content script.
|
| +</p>
|
| +
|
| +
|
| +<h3 id="background_page">The background page</h3>
|
| +
|
| +<p>
|
| +The following figure shows a browser
|
| +that has at least two extensions installed:
|
| +a browser action (yellow icon)
|
| +and a page action (blue icon).
|
| +Both the browser action and the page action
|
| +have background pages.
|
| +This figure shows the browser action's background page,
|
| +which is defined by <code>background.html</code>
|
| +and has JavaScript code that controls
|
| +the behavior of the browser action in both windows.
|
| +</p>
|
| +
|
| +<img src="{{static}}/images/overview/arch-1.gif"
|
| + width="232" height="168"
|
| + alt="Two windows and a box representing a background page (background.html). One window has a yellow icon; the other has both a yellow icon and a blue icon. The yellow icons are connected to the background page." />
|
| +
|
| +<p>
|
| +Although background pages can be useful,
|
| +don't use one if you don't need it.
|
| +Background pages are always open,
|
| +so when a user installs many extensions that have background pages,
|
| +Chrome's performance can suffer.
|
| +</p>
|
| +
|
| +<!-- PENDING: Perhaps show a picture of many background page processes.
|
| + This could build on a figure that shows the process architecture,
|
| + and perhaps the differences between packaged apps and extensions. -->
|
| +
|
| +<p>
|
| +Here are some examples of extensions that usually
|
| +<em>do not need</em> a background page:
|
| +</p>
|
| +
|
| +<ul>
|
| + <li> An extension with a browser action that
|
| + presents its UI solely through a popup
|
| + (and perhaps an options page).
|
| + </li>
|
| + <li>
|
| + An extension that provides an <em>override page</em>—a
|
| + page that replaces a standard Chrome page.
|
| + </li>
|
| + <li>
|
| + An extension with a content script
|
| + that <em>doesn't use</em> localStorage or
|
| + <a href="#apis">extension APIs</a>.
|
| + </li>
|
| + <li>
|
| + An extension that has no UI except for an options page.
|
| + </li>
|
| +</ul>
|
| +
|
| +<p>
|
| +See <a href="background_pages.html">Background Pages</a>
|
| +for more details.
|
| +</p>
|
| +
|
| +<h3 id="pages">UI pages</h3>
|
| +
|
| +<p>
|
| +Extensions can contain ordinary HTML pages that display the extension's UI.
|
| +For example, a browser action can have a popup,
|
| +which is implemented by an HTML file.
|
| +Any extension can have an options page,
|
| +which lets users customize how the extension works.
|
| +Another type of special page is the override page.
|
| +And finally, you can
|
| +use <a href="tabs.html#method-create">chrome.tabs.create()</a>
|
| +or <code>window.open()</code>
|
| +to display any other HTML files that are in the extension.
|
| +</p>
|
| +
|
| +<p>
|
| +The HTML pages inside an extension
|
| +have complete access to each other's DOMs,
|
| +and they can invoke functions on each other.
|
| +</p>
|
| +
|
| +<!-- PENDING: Change the following example and figure
|
| +to use something that's not a popup?
|
| +(It might lead people to think that popups need background pages.) -->
|
| +
|
| +<p>
|
| +The following figure shows the architecture
|
| +of a browser action's popup.
|
| +The popup's contents are a web page
|
| +defined by an HTML file
|
| +(<code>popup.html</code>).
|
| +This extension also happens to have a background page
|
| +(<code>background.html</code>).
|
| +The popup doesn't need to duplicate code
|
| +that's in the background page
|
| +because the popup can invoke functions on the background page.
|
| +</p>
|
| +
|
| +<img src="{{static}}/images/overview/arch-2.gif"
|
| + width="256" height="168"
|
| + alt="A browser window containing a browser action that's displaying a popup. The popup's HTML file (popup.html) can communicate with the extension's background page (background.html)." />
|
| +
|
| +<p>
|
| +See <a href="browserAction.html">Browser Actions</a>,
|
| +<a href="options.html">Options</a>,
|
| +<a href="override.html">Override Pages</a>,
|
| +and the <a href="#pageComm">Communication between pages</a> section
|
| +for more details.
|
| +</p>
|
| +
|
| +
|
| +<h3 id="contentScripts">Content scripts</h3>
|
| +
|
| +<p>
|
| +If your extension needs to interact with web pages,
|
| +then it needs a <em>content script</em>.
|
| +A content script is some JavaScript
|
| +that executes in the context of a page
|
| +that's been loaded into the browser.
|
| +Think of a content script as part of that loaded page,
|
| +not as part of the extension it was packaged with
|
| +(its <em>parent extension</em>).
|
| +</p>
|
| +
|
| +<!-- [PENDING: Consider explaining that the reason content scripts are separated from the extension is due to chrome's multiprocess design. Something like:
|
| +
|
| +Each extension runs in its own process.
|
| +To have rich interaction with a web page, however,
|
| +the extension must be able to
|
| +run some code in the web page's process.
|
| +Extensions accomplish this with content scripts.]
|
| +-->
|
| +
|
| +<p>
|
| +Content scripts can read details of the web pages the browser visits,
|
| +and they can make changes to the pages.
|
| +In the following figure,
|
| +the content script
|
| +can read and modify
|
| +the DOM for the displayed web page.
|
| +It cannot, however, modify the DOM of its parent extension's background page.
|
| +</p>
|
| +
|
| +<img src="{{static}}/images/overview/arch-3.gif"
|
| + width="238" height="169"
|
| + alt="A browser window with a browser action (controlled by background.html) and a content script (controlled by contentscript.js)." />
|
| +
|
| +<p>
|
| +Content scripts aren't completely cut off from their parent extensions.
|
| +A content script can exchange messages with its parent extension,
|
| +as the arrows in the following figure show.
|
| +For example, a content script might send a message
|
| +whenever it finds an RSS feed in a browser page.
|
| +Or a background page might send a message
|
| +asking a content script to change the appearance of its browser page.
|
| +</p>
|
| +
|
| +<img src="{{static}}/images/overview/arch-cs.gif"
|
| + width="238" height="194"
|
| + alt="Like the previous figure, but showing more of the parent extension's files, as well as a communication path between the content script and the parent extension." />
|
| +
|
| +
|
| +
|
| +<p>
|
| +For more information,
|
| +see <a href="content_scripts.html">Content Scripts</a>.
|
| +</p>
|
| +
|
| +
|
| +<h2 id="apis"> Using the chrome.* APIs </h2>
|
| +
|
| +<p>
|
| +In addition to having access to all the APIs that web pages and apps can use,
|
| +extensions can also use Chrome-only APIs
|
| +(often called <em>chrome.* APIs</em>)
|
| +that allow tight integration with the browser.
|
| +For example, any extension or web app can use the
|
| +standard <code>window.open()</code> method to open a URL.
|
| +But if you want to specify which window that URL should be displayed in,
|
| +your extension can use the Chrome-only
|
| +<a href="tabs.html#method-create">chrome.tabs.create()</a>
|
| +method instead.
|
| +</p>
|
| +
|
| +<h3 id="sync"> Asynchronous vs. synchronous methods </h3>
|
| +<p>
|
| +Most methods in the chrome.* APIs are <b>asynchronous</b>:
|
| +they return immediately, without waiting for the operation to finish.
|
| +If you need to know the outcome of that operation,
|
| +then you pass a callback function into the method.
|
| +That callback is executed later (potentially <em>much</em> later),
|
| +sometime after the method returns.
|
| +Here's an example of the signature for an asynchronous method:
|
| +</p>
|
| +
|
| +<p>
|
| +<code>
|
| +chrome.tabs.create(object <em>createProperties</em>, function <em>callback</em>)
|
| +</code>
|
| +</p>
|
| +
|
| +<p>
|
| +Other chrome.* methods are <b>synchronous</b>.
|
| +Synchronous methods never have a callback
|
| +because they don't return until they've completed all their work.
|
| +Often, synchronous methods have a return type.
|
| +Consider the
|
| +<a href="extension.html#method-getBackgroundPage">chrome.extensions.getBackgroundPage()</a> method:
|
| +</p>
|
| +
|
| +<p>
|
| +<code>
|
| +Window chrome.extension.getBackgroundPage()
|
| +</code>
|
| +</p>
|
| +
|
| +<p>
|
| +This method has no callback and a return type of <code>Window</code>
|
| +because it synchronously returns the background page
|
| +and performs no other, asynchronous work.
|
| +</p>
|
| +
|
| +
|
| +<h3 id="sync-example"> Example: Using a callback </h3>
|
| +
|
| +<p>
|
| +Say you want to navigate
|
| +the user's currently selected tab to a new URL.
|
| +To do this, you need to get the current tab's ID
|
| +(using <a href="tabs.html#method-getSelected">chrome.tabs.getSelected()</a>)
|
| +and then make that tab go to the new URL
|
| +(using <a href="tabs.html#method-update">chrome.tabs.update()</a>).
|
| +</p>
|
| +
|
| +<p>
|
| +If <code>getSelected()</code> were synchronous,
|
| +you might write code like this:
|
| +</p>
|
| +
|
| +<pre>
|
| + <b>//THIS CODE DOESN'T WORK</b>
|
| +<span class="linenumber">1: </span>var tab = chrome.tabs.getSelected(null); <b>//WRONG!!!</b>
|
| +<span class="linenumber">2: </span>chrome.tabs.update(tab.id, {url:newUrl});
|
| +<span class="linenumber">3: </span>someOtherFunction();
|
| +</pre>
|
| +
|
| +<p>
|
| +That approach fails
|
| +because <code>getSelected()</code> is asynchronous.
|
| +It returns without waiting for its work to complete,
|
| +and it doesn't even return a value
|
| +(although some asynchronous methods do).
|
| +You can tell that <code>getSelected()</code> is asynchronous
|
| +by the <em>callback</em> parameter in its signature:
|
| +
|
| +<p>
|
| +<code>
|
| +chrome.tabs.getSelected(integer <em>windowId</em>, function <em>callback</em>)
|
| +</code>
|
| +</p>
|
| +
|
| +<p>
|
| +To fix the preceding code,
|
| +you must use that callback parameter.
|
| +The following code shows
|
| +how to define a callback function
|
| +that gets the results from <code>getSelected()</code>
|
| +(as a parameter named <code>tab</code>)
|
| +and calls <code>update()</code>.
|
| +</p>
|
| +
|
| +<pre>
|
| + <b>//THIS CODE WORKS</b>
|
| +<span class="linenumber">1: </span>chrome.tabs.getSelected(null, <b>function(tab) {</b>
|
| +<span class="linenumber">2: </span> chrome.tabs.update(tab.id, {url:newUrl});
|
| +<span class="linenumber">3: </span><b>}</b>);
|
| +<span class="linenumber">4: </span>someOtherFunction();
|
| +</pre>
|
| +
|
| +<p>
|
| +In this example, the lines are executed in the following order: 1, 4, 2.
|
| +The callback function specified to <code>getSelected</code> is called
|
| +(and line 2 executed)
|
| +only after information about the currently selected tab is available,
|
| +which is sometime after <code>getSelected()</code> returns.
|
| +Although <code>update()</code> is asynchronous,
|
| +this example doesn't use its callback parameter,
|
| +since we don't do anything about the results of the update.
|
| +</p>
|
| +
|
| +
|
| +<h3 id="chrome-more"> More details </h3>
|
| +
|
| +<p>
|
| +For more information, see the
|
| +<a href="api_index.html">chrome.* API docs</a>
|
| +and watch this video:
|
| +</p>
|
| +
|
| +<p>
|
| +<iframe title="YouTube video player" width="640" height="390" src="http://www.youtube.com/embed/bmxr75CV36A?rel=0" frameborder="0" allowfullscreen></iframe>
|
| +</p>
|
| +
|
| +<h2 id="pageComm">Communication between pages </h2>
|
| +
|
| +<p>
|
| +The HTML pages within an extension often need to communicate.
|
| +
|
| +Because all of an extension's pages
|
| +execute in same process on the same thread,
|
| +the pages can make direct function calls to each other.
|
| +</p>
|
| +
|
| +<p>
|
| +To find pages in the extension, use
|
| +<a href="extension.html"><code>chrome.extension</code></a>
|
| +methods such as
|
| +<code>getViews()</code> and
|
| +<code>getBackgroundPage()</code>.
|
| +Once a page has a reference to other pages within the extension,
|
| +the first page can invoke functions on the other pages,
|
| +and it can manipulate their DOMs.
|
| +</p>
|
| +
|
| +
|
| +
|
| +
|
| +<h2 id="incognito"> Saving data and incognito mode </h2>
|
| +
|
| +<p>
|
| +Extensions can save data using
|
| +the HTML5 <a href="http://dev.w3.org/html5/webstorage/">web storage API</a>
|
| +(such as <code>localStorage</code>)
|
| +or by making server requests that result in saving data.
|
| +Whenever you want to save something,
|
| +first consider whether it's
|
| +from a window that's in incognito mode.
|
| +By default, extensions don't run in incognito windows,
|
| +and packaged apps <em>do</em>.
|
| +You need to consider what a user expects
|
| +from your extension or packaged app
|
| +when the browser is incognito.
|
| +</p>
|
| +
|
| +<p>
|
| +<em>Incognito mode</em> promises that the window will leave no tracks.
|
| +When dealing with data from incognito windows,
|
| +do your best to honor this promise.
|
| +For example, if your extension normally
|
| +saves browsing history to the cloud,
|
| +don't save history from incognito windows.
|
| +On the other hand, you can store
|
| +your extension's settings from any window,
|
| +incognito or not.
|
| +</p>
|
| +
|
| +<p class="note">
|
| +<b>Rule of thumb:</b>
|
| +If a piece of data might show where a user
|
| +has been on the web or what the user has done,
|
| +don't store it if it's from an incognito window.
|
| +</p>
|
| +
|
| +<p>
|
| +To detect whether a window is in incognito mode,
|
| +check the <code>incognito</code> property of the relevant
|
| +<a href="tabs.html#type-tabs.Tab">Tab</a> or
|
| +<a href="windows.html#type-windows.Window">Window</a> object.
|
| +For example:
|
| +</p>
|
| +
|
| +<pre>
|
| +var bgPage = chrome.extension.getBackgroundPage();
|
| +
|
| +function saveTabData(tab, data) {
|
| + if (tab.incognito) {
|
| + bgPage[tab.url] = data; // Persist data ONLY in memory
|
| + } else {
|
| + localStorage[tab.url] = data; // OK to store data
|
| +}
|
| +</pre>
|
| +
|
| +
|
| +<h2 id="now-what"> Now what? </h2>
|
| +
|
| +<p>
|
| +Now that you've been introduced to extensions,
|
| +you should be ready to write your own.
|
| +Here are some ideas for where to go next:
|
| +</p>
|
| +
|
| +<ul>
|
| + <li> <a href="getstarted.html">Tutorial: Getting Started</a> </li>
|
| + <li> <a href="tut_debugging.html">Tutorial: Debugging</a> </li>
|
| + <li> <a href="devguide.html">Developer's Guide</a> </li>
|
| + <li> <a href="http://dev.chromium.org/developers/design-documents/extensions/samples">Samples</a> </li>
|
| + <li> <a href="http://www.youtube.com/view_play_list?p=CA101D6A85FE9D4B">Videos</a>,
|
| + such as
|
| + <a href="http://www.youtube.com/watch?v=B4M_a7xejYI&feature=PlayList&p=CA101D6A85FE9D4B&index=6">Extension Message Passing</a>
|
| + </li>
|
| +</ul>
|
|
|