Working with windows in chrome code

  • Revision slug: Working_with_windows_in_chrome_code
  • Revision title: Working with windows in chrome code
  • Revision id: 65758
  • Created:
  • Creator: Nickolay
  • Is current revision? No
  • Comment /* Content windows */

Revision Content

This article describes working with multiple windows in Mozilla chrome code (XUL applications and Extensions). It contains tips and example code on opening new windows, finding an already opened window, and passing data between different windows.

Opening windows

To open a new window, we usually use a window.open or window.openDialog DOM call, like this:

var win = window.open("chrome://myextension/content/about.xul", 
                      "aboutMyExtension", "chrome,centerscreen"); 

The first parameter to window.open is the URI of the XUL file that describes the window and its contents.

The second parameter is the window's name; the name can be used in links or forms as the target attribute. This is different from the user-visible window title, which is specified using XUL.

The third, and optional, parameter is a list of special window features the window should have.

The window.openDialog function works similarly, but lets you specify optional arguments that can be referenced from the JavaScript code. It also handles window features a little differently, including always assuming the dialog feature is specified.

If the Window object is unavailable (for example, when opening a window from XPCOM component code), you might want to use nsIWindowWatcher interface. Its parameters are similar to window.open, in fact window.open implementation calls nsIWindowWatcher's methods.

var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
                   .getService(Components.interfaces.nsIWindowWatcher);
var win = ww.openWindow(null, "chrome://myextension/content/about.xul",
                        "aboutMyExtension", "chrome,centerscreen", null);

Window object

Note the win variable in the above section, which is assigned the return value of window.open. It can be used to access the opened window. The return value of window.open (and similar methods) is a Window object (usually ChromeWindow), of the same type that the window variable.

Technically speaking, it implements a number of interfaces, including nsIDOMJSWindow and nsIDOMWindowInternal, but it also contains the user-defined properties for global variables and functions of the window. So, for example, to access the DOM document corresponding to the window, you can use win.document.

Note however, that the open() call returns before the window is fully loaded, so some calls, like win.document.getElementById() will fail. To overcome this difficulty, you can move the initialization code to a load handler of the window being opened or pass a callback function, as described below.

You can get a Window object from a document using document.defaultView.

Content windows

When a XUL window contains a widget capable of displaying a page, such as <browser> or <iframe>, the document in that widget is, naturally, separate from the document of the chrome window itself. There also is a Window object for each sub-document, although there's no window in a common sense for the sub-document.

The same holds for chrome windows opened inside a tab of <tt><tabbrowser></tt>. The elements above the chrome document opened in the tab are separate from your chrome document.

The following two subsections describe how to cross chrome-content boundaries in either way, i.e. accessing elements which are ancestors of your chrome document, or accessing elements which are descendants of your chrome document (but nevertheless in a different context).

Accessing content documents

Assume you have a document loaded in a <tt><tabbrowser></tt>, <tt><browser></tt>, or <tt><iframe></tt> element inside your document. You can use browser.contentDocument to access that document and browser.contentWindow to the Window object of that document.

You should be aware of XPCNativeWrappers when working with untrusted content.

In case of <browser type="content-primary"/>, you can use the content shortcut property to accesss the Window object of the content document. For example:

// alerts the title of the document displayed in the content-primary widget

alert(content.document.title);

For example, you can use content.document in a browser.xul overlay to access the web page in the selected tab.

Some examples on the Internet use _content instead of content. The former is now deprecated, and you should use content in the new code.

Accessing elements which are ancestors of the current content window

The opposite case is when you want to access the chrome document from a privileged script loaded in a <tt><browser></tt> or an <tt><iframe></tt>.

The DOM tree, as shown by the DOM Inspector, looks like this:

#document
  window                 main-window
    ...
      tabbrowser         content
        ...
          browser
            #document
              window     myExtensionWindow

where the last window with the ID "<tt>myExtensionWindow</tt>" is your chrome application. The ancestor elements are the elements of the browser.

Your task is to access elements above your chrome document, i.e. to break out of your chrome window and access the ancestors. This can be done using the following statement:

var mainWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                   .getInterface(Components.interfaces.nsIWebNavigation)
                   .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
                   .rootTreeItem
                   .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                   .getInterface(Components.interfaces.nsIDOMWindow) 

This allows you to cross the chrome-content boundaries, and returns the main window object.

Finding already opened windows

The window mediator XPCOM component (nsIWindowMediator interface) provides information about opened windows. Two of its methods are often used to obtain information about currently open windows: getMostRecentWindow and getEnumerator. Please refer to the MozillaZine page about nsIWindowMediator for examples and notes on using nsIWindowMediator. === Example: Opening a window only if it's not opened already === XXX TBD

Passing data between windows

When working with multiple windows, you often need to pass information from one window to another. Since different windows have separate DOM documents and global objects for scripts, you can't just use one global JavaScript variable in scripts from different windows.

There are several techniques of varying power and simplicity that can be used to share data. We'll demonstrate them from the simplest to the most complex in the next few sections.

Example 1: Passing data to window when opening it with openDialog

When you open a window using window.openDialog or nsIWindowWatcher.openWindow, you can pass an arbitrary number of arguments to that window. Arguments are simple JavaScript objects, accessible through window.arguments property in the opened window.

In this example, we're using window.openDialog to open a progress dialog. We pass in the current status text as well as the maximum and current progress values. Note that using nsIWindowWatcher.openWindow is a bit less trivial .

Opener code:

window.openDialog("chrome://test/content/progress.xul",
                  "myProgress", "chrome,centerscreen", 
                  {status: "Reading remote data", maxProgress: 50, progress: 10} );

progress.xul:

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>

<window onload="onLoad();" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script><![CDATA[
  var gStatus, gProgressMeter;
  var maxProgress = 100;
  function onLoad() {
    gStatus = document.getElementById("status");
    gProgressMeter = document.getElementById("progressmeter");
  
    if("arguments" in window && window.arguments.length > 0) {
      maxProgress = window.arguments[0].maxProgress;
      setProgress(window.arguments[0].progress);
      setStatus(window.arguments[0].status);
    }
  }

  function setProgress(value) {
    gProgressMeter.value = 100 * value / maxProgress;
  }

  function setStatus(text) {
    gStatus.value = "Status: " + text + "...";
  }
]]></script>
 
<label id="status" value="(No status)"/>
<hbox>
  <progressmeter id="progressmeter" mode="determined"/>
  <button label="Cancel" oncommand="close();"/>
</hbox>

</window>

Example 2: Interacting with the opener

Sometimes an opened window needs to interact with its opener; for example, it might do so in order to give notice that the user has made changes in the window. You can find the window's opener using its window.opener property or via a callback function passed to the window in a way described in the previous section.

Let's add code to the previous example to notify the opener when the user presses Cancel on the progress dialog.

  • Using window.opener. The opener property returns its window's opener; that is, the {{template.Anch("Window object")}} that opened it.

If we're sure the window that opened the progress dialog declares the cancelOperation function, we can use window.opener.cancelOperation() to notify it, like this:

<button label="Cancel" oncommand="opener.cancelOperation(); close();"/>
  • Using a callback function. Alternatively, the opener window can pass a callback function to the progress dialog in the same way we passed the status string in the previous example:
function onCancel() {
  alert("Operation canceled!");
}

...

window.openDialog("chrome://test/content/progress.xul",
                  "myProgress", "chrome,centerscreen", 
                  {status: "Reading remote data", maxProgress: 50, progress: 10},
                  onCancel); 

The progress dialog can then run the callback like this:

<button label="Cancel" oncommand="window.arguments[1](); close();"/>

Example 3: Using nsIWindowMediator when opener is not enough

The window.opener property is very easy to use, but it's only useful when you're sure that your window was opened from one of a few well-known places. In more complicated cases you need to use the nsIWindowMediator interface, introduced above.

One case in which you might want to use nsIWindowMediator is in an extension's Options window. Suppose you're developing a browser extension that consists of a browser.xul overlay and an Options window. Suppose the overlay contains a button to open the extension's Options window which needs to read some data from the browser window. As you may remember, Firefox's Extension Manager can also be used to open your Options dialog.

This means the value of window.opener in your Options dialog is not necessarily the browser window -- instead, it might be the Extension Manager window. You could check the location property of the opener and use opener.opener in case it's the Extension Manager window, but a better way is to use nsIWindowMediator:

var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
                   .getService(Components.interfaces.nsIWindowMediator);
var browserWindow = wm.getMostRecentWindow("navigator:browser");
// read values from |browserWindow|

You might be tempted to use a similar technique to apply the changes the user made in the Options dialog, but a better way to do that is to use preferences observers.

Advanced data sharing

The above code is useful when you need to pass data from one window to another or to a set of windows, but sometimes you just want to share a JavaScript variable in common between different windows. You could declare a local variable in each window along with corresponding setter functions to keep the "instances" of the variable in sync across windows, but fortunately, there's a better way.

To declare a shared variable, we need to find a place that exists while the application is running and is easily accessible from the code in different chrome windows. There are actually a few such places.

Using an XPCOM singleton component

The cleanest and most powerful way to share data is to define your own XPCOM component (you can write one in JavaScript) and access it from anywhere using a getService call:

Components.classes["@domain.org/mycomponent;1"].getService();
  • Pros:
    • It's the "right way".
    • You can store arbitrary JavaScript objects in the component.
    • The scope is not shared between components, so you don't have to worry about name collisions.
  • Cons:
    • You can't use the window object, its members, like alert and open, and many other objects available from inside a window. The functionality isn't lost, however -- you just have to use the XPCOM components directly instead of using convenient shortcuts. Of course, this doesn't matter if you just store data in the component.
    • Learning to create XPCOM components takes time.

There are several articles and books about creating XPCOM components online.

Storing shared data in preferences

If you just need to store a string or a number, writing a whole XPCOM component may be an unnecessary complication. You can use the preferences service in such cases.

  • Pros:
    • Quite easy to use for storing simple data.
  • Cons:
    • Can't easily be used to store complex data.
    • Abusing the preferences service and not cleaning up after yourself can cause <tt>prefs.js</tt> to grow large and slow down application startup.

See this MozillaZine page about preferences for detailed description of the preferences system and example code.

Example:

var prefs = Components.classes["@mozilla.org/preferences-service;1"]
                      .getService(Components.interfaces.nsIPrefService);
var branch = prefs.getBranch("extensions.myext.");
var var1 = branch.getBoolPref("var1"); // get a pref

The hidden window hack

Some extension authors use the special hidden window to store their data and code. The hidden window is similar to a regular window, but unlike any other window, it's available the whole time the application is running, and isn't visible to user. It's used to implement the menus on Macs, so it isn't needed on Windows. It will be removed from Windows builds eventually.

  • Pros:
    • If you're running code in the hidden window, the Window object and its properties are available, unlike the component case.
    • You can store arbitrary JavaScript objects in the hidden window.
  • Cons:
    • It's a hack.
    • The hidden window may be removed from Windows builds.

You can see this technique in action in rue's SessionSaver extension.

{{ wiki.languages( { "fr": "fr/Travailler_avec_des_fen\u00eatres_dans_le_chrome" } ) }}

Revision Source

<p>This article describes working with multiple windows in Mozilla chrome code (<a href="en/XUL">XUL</a> applications and <a href="en/Extensions">Extensions</a>). It contains tips and example code on opening new windows, finding an already opened window, and passing data between different windows.
</p>
<h3 name="Opening_windows"> Opening windows </h3>
<p>To open a new window, we usually use a <code><a href="en/DOM/window.open">window.open</a></code> or <code><a href="en/DOM/window.openDialog">window.openDialog</a></code> DOM call, like this:
</p>
<pre class="eval">var win = window.open("chrome://myextension/content/about.xul", 
                      "aboutMyExtension", "chrome,centerscreen"); 
</pre>
<p>The first parameter to <code>window.open</code> is the URI of the XUL file that describes the window and its contents.
</p><p>The second parameter is the window's name; the name can be used in links or forms as the <code>target</code> attribute.  This is different from the user-visible window title, which is specified using XUL.
</p><p>The third, and optional, parameter is a list of special window features the window should have.
</p><p>The <code>window.openDialog</code> function works similarly, but lets you specify optional arguments that can be referenced from the JavaScript code.  It also handles window features a little differently, including always assuming the <code>dialog</code> feature is specified.
</p><p>If the <code>Window</code> object is unavailable (for example, when opening a window from XPCOM component code), you might want to use <a href="en/NsIWindowWatcher">nsIWindowWatcher</a> interface. Its parameters are similar to <code>window.open</code>, in fact <code>window.open</code> implementation calls <code>nsIWindowWatcher</code>'s methods.
</p>
<pre class="eval">var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
                   .getService(Components.interfaces.nsIWindowWatcher);
var win = ww.openWindow(null, "chrome://myextension/content/about.xul",
                        "aboutMyExtension", "chrome,centerscreen", null);
</pre>
<h3 name="Window_object"> Window object </h3>
<p>Note the <code>win</code> variable in the above section, which is assigned the return value of <code>window.open</code>. It can be used to access the opened window. The return value of <code>window.open</code> (and similar methods) is a <code><a class="external" href="http://xulplanet.com/references/objref/Window.html">Window</a></code> object (usually <code><a class="external" href="http://xulplanet.com/references/objref/ChromeWindow.html">ChromeWindow</a></code>), of the same type that the <code>window</code> variable.
</p><p>Technically speaking, it implements a number of interfaces, including <code><a href="en/NsIDOMJSWindow">nsIDOMJSWindow</a></code> and <code><a href="en/NsIDOMWindowInternal">nsIDOMWindowInternal</a></code>, but it also contains the user-defined properties for global variables and functions of the window. So, for example, to access the DOM document corresponding to the window, you can use <code><a href="en/DOM/window.document">win.document</a></code>.
</p><p>Note however, that the <code>open()</code> call returns <i>before</i> the window is fully loaded, so some calls, like <code>win.document.getElementById()</code> will fail. To overcome this difficulty, you can move the initialization code to a <code>load</code> handler of the window being opened or pass a callback function, as described <a href="#callback">below</a>.
</p><p>You can get a <code>Window</code> object from a document using <code>document.defaultView</code>.
</p>
<h3 name="Content_windows"> Content windows </h3>
<p>When a XUL window contains a widget capable of displaying a page, such as <code>&lt;browser&gt;</code> or <code>&lt;iframe&gt;</code>, the document in that widget is, naturally, separate from the document of the chrome window itself. There also is a <code>Window</code> object for each sub-document, although there's no window in a common sense for the sub-document.
</p><p>The same holds for chrome windows opened inside a tab of <tt>&lt;tabbrowser&gt;</tt>. The elements above the chrome document opened in the tab are separate from your chrome document.
</p><p>The following two subsections describe how to cross chrome-content boundaries in either way, i.e. accessing elements which are ancestors of your chrome document, or accessing elements which are descendants of your chrome document (but nevertheless in a different context).
</p>
<h4 name="Accessing_content_documents"> Accessing content documents </h4>
<p>Assume you have a document loaded in a <tt>&lt;tabbrowser&gt;</tt>, <tt>&lt;browser&gt;</tt>, or <tt>&lt;iframe&gt;</tt> element inside your document. You can use <code>browser.contentDocument</code> to access that document and <code>browser.contentWindow</code> to the <code>Window</code> object of that document.
</p><p>You should be aware of <a href="en/XPCNativeWrapper">XPCNativeWrappers</a> when working with <a href="en/XPCNativeWrapper#What_is_a_trusted_window.3F">untrusted content</a>.
</p><p>In case of <code>&lt;browser type="content-primary"/&gt;</code>, you can use the <a href="en/DOM/window.content">content</a> shortcut property to accesss the <code>Window</code> object of the content document. For example:
</p>
<pre class="eval">// alerts the title of the document displayed in the content-primary widget

alert(content.document.title);
</pre>
<p>For example, you can use <code>content.document</code> in a browser.xul overlay to access the web page in the selected tab.
</p>
<div class="note">Some examples on the Internet use <code>_content</code> instead of <code>content</code>. The former is now deprecated, and you should use <code>content</code> in the new code.</div>
<h4 name="Accessing_elements_which_are_ancestors_of_the_current_content_window"> Accessing elements which are ancestors of the current content window </h4>
<p>The opposite case is when you want to access the chrome document from a privileged script loaded in a <tt>&lt;browser&gt;</tt> or an <tt>&lt;iframe&gt;</tt>.
</p><p>The DOM tree, as shown by the <a href="en/DOM_Inspector">DOM Inspector</a>, looks like this:
</p>
<pre class="eval">#document
  window                 main-window
    ...
      tabbrowser         content
        ...
          browser
            #document
              window     myExtensionWindow
</pre>
<p>where the last window with the ID "<tt>myExtensionWindow</tt>" is your chrome application. The ancestor elements are the elements of the browser.
</p><p>Your task is to access elements above your chrome document, i.e. to break out of your chrome window and access the ancestors. This can be done using the following statement:
</p>
<pre class="eval">var mainWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                   .getInterface(Components.interfaces.nsIWebNavigation)
                   .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
                   .rootTreeItem
                   .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                   .getInterface(Components.interfaces.nsIDOMWindow) 
</pre>
<p>This allows you to cross the chrome-content boundaries, and returns the main window object.
</p>
<h3 name="Finding_already_opened_windows"> Finding already opened windows </h3>
<p>The window mediator XPCOM component (<a href="en/NsIWindowMediator">nsIWindowMediator</a> interface) provides information about opened windows. Two of its methods are often used to obtain information about currently open windows: <code>getMostRecentWindow</code> and <code>getEnumerator</code>. Please refer to the <a class="external" href="http://kb.mozillazine.org/nsIWindowMediator">MozillaZine page about nsIWindowMediator</a> for examples and notes on using <code>nsIWindowMediator</code>.
<span class="comment">=== Example: Opening a window only if it's not opened already === XXX TBD</span>
</p>
<h3 name="Passing_data_between_windows"> Passing data between windows </h3>
<p>When working with multiple windows, you often need to pass information from one window to another. Since different windows have separate DOM documents and global objects for scripts, you can't just use one global JavaScript variable in scripts from different windows.
</p><p>There are several techniques of varying power and simplicity that can be used to share data. We'll demonstrate them from the simplest to the most complex in the next few sections.
</p>
<h4 name="Example_1:_Passing_data_to_window_when_opening_it_with_openDialog"> Example 1: Passing data to window when opening it with <code>openDialog</code> </h4>
<p>When you open a window using <code><a href="en/DOM/window.openDialog">window.openDialog</a></code> or <code>nsIWindowWatcher.openWindow</code>, you can pass an arbitrary number of <i>arguments</i> to that window. Arguments are simple JavaScript objects, accessible through <code><a href="en/DOM/window.arguments">window.arguments</a></code> property in the opened window.
</p><p>In this example, we're using <code>window.openDialog</code> to open a progress dialog.  We pass in the current status text as well as the maximum and current progress values.  Note that using <code>nsIWindowWatcher.openWindow</code> is a bit less trivial <a class="external" href="http://lxr.mozilla.org/seamonkey/source/extensions/help/resources/content/contextHelp.js#70">.
</a></p><p><a class="external" href="http://lxr.mozilla.org/seamonkey/source/extensions/help/resources/content/contextHelp.js#70">Opener code:
</a></p><a class="external" href="http://lxr.mozilla.org/seamonkey/source/extensions/help/resources/content/contextHelp.js#70">
<pre class="eval">window.openDialog("chrome://test/content/progress.xul",
                  "myProgress", "chrome,centerscreen", 
                  {status: "Reading remote data", maxProgress: 50, progress: 10} );
</pre>
<p><code>progress.xul</code>:
</p>
<pre>&lt;?xml version="1.0"?&gt;
&lt;?xml-stylesheet href="chrome://global/skin/" type="text/css"?&gt;

&lt;window onload="onLoad();" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"&gt;
&lt;script&gt;&lt;![CDATA[
  var gStatus, gProgressMeter;
  var maxProgress = 100;
  function onLoad() {
    gStatus = document.getElementById("status");
    gProgressMeter = document.getElementById("progressmeter");
  
    if("arguments" in window &amp;&amp; window.arguments.length &gt; 0) {
      maxProgress = window.arguments[0].maxProgress;
      setProgress(window.arguments[0].progress);
      setStatus(window.arguments[0].status);
    }
  }

  function setProgress(value) {
    gProgressMeter.value = 100 * value / maxProgress;
  }

  function setStatus(text) {
    gStatus.value = "Status: " + text + "...";
  }
]]&gt;&lt;/script&gt;
 
&lt;label id="status" value="(No status)"/&gt;
&lt;hbox&gt;
  &lt;progressmeter id="progressmeter" mode="determined"/&gt;
  &lt;button label="Cancel" oncommand="close();"/&gt;
&lt;/hbox&gt;

&lt;/window&gt;
</pre>
<h4 name="Example_2:_Interacting_with_the_opener"> Example 2: Interacting with the opener </h4>
</a><p><a class="external" href="http://lxr.mozilla.org/seamonkey/source/extensions/help/resources/content/contextHelp.js#70">Sometimes an opened window needs to interact with its opener; for example, it might do so in order to give notice that the user has made changes in the window. You can find the window's opener using its </a><a href="en/Window.opener">window.opener</a> property or via a callback function passed to the window in a way described in the previous section.
</p><p>Let's add code to the previous example to notify the opener when the user presses Cancel on the progress dialog.
</p>
<ul><li> <b>Using <code>window.opener</code>.</b> The <code>opener</code> property returns its window's opener; that is, the {{template.Anch("Window object")}} that opened it.
</li></ul>
<p>If we're sure the window that opened the progress dialog declares the <code>cancelOperation</code> function, we can use <code>window.opener.cancelOperation()</code> to notify it, like this:
</p>
<pre class="eval">&lt;button label="Cancel" oncommand="<b>opener.cancelOperation();</b> close();"/&gt;
</pre>
<ul><li> <b id="callback"></b><b>Using a callback function.</b> Alternatively, the opener window can pass a callback function to the progress dialog in the same way we passed the status string in the previous example:
</li></ul>
<pre class="eval">function onCancel() {
  alert("Operation canceled!");
}

...

window.openDialog("chrome://test/content/progress.xul",
                  "myProgress", "chrome,centerscreen", 
                  {status: "Reading remote data", maxProgress: 50, progress: 10},
                  <b>onCancel</b>); 
</pre>
<p>The progress dialog can then run the callback like this:
</p>
<pre class="eval">&lt;button label="Cancel" oncommand="<b>window.arguments[1]();</b> close();"/&gt;
</pre>
<h4 name="Example_3:_Using_nsIWindowMediator_when_opener_is_not_enough"> Example 3: Using <code>nsIWindowMediator</code> when <code>opener</code> is not enough </h4>
<p>The <code>window.opener</code> property is very easy to use, but it's only useful when you're sure that your window was opened from one of a few well-known places. In more complicated cases you need to use the <code><a href="en/NsIWindowMediator">nsIWindowMediator</a></code> interface, introduced above.
</p><p>One case in which you might want to use <code>nsIWindowMediator</code> is in an extension's Options window. Suppose you're developing a browser extension that consists of a browser.xul overlay and an Options window. Suppose the overlay contains a button to open the extension's Options window which needs to read some data from the browser window. As you may remember, Firefox's Extension Manager can also be used to open your Options dialog.
</p><p>This means the value of <code>window.opener</code> in your Options dialog is not necessarily the browser window -- instead, it might be the Extension Manager window. You could check the <code>location</code> property of the <code>opener</code> and use <code>opener.opener</code> in case it's the Extension Manager window, but a better way is to use <code>nsIWindowMediator</code>:
</p>
<pre class="eval">var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
                   .getService(Components.interfaces.nsIWindowMediator);
var browserWindow = wm.getMostRecentWindow("navigator:browser");
// read values from |browserWindow|
</pre>
<p>You might be tempted to use a similar technique to apply the changes the user made in the Options dialog, but a better way to do that is to use <a class="external" href="http://kb.mozillazine.org/Dev_:_Using_preferences#Using_preferences_observers">preferences observers</a>.
</p>
<h3 name="Advanced_data_sharing"> Advanced data sharing </h3>
<p>The above code is useful when you need to pass data from one window to another or to a set of windows, but sometimes you just want to share a JavaScript variable in common between different windows. You could declare a local variable in each window along with corresponding setter functions to keep the "instances" of the variable in sync across windows, but fortunately, there's a better way.
</p><p>To declare a shared variable, we need to find a place that exists while the application is running and is easily accessible from the code in different chrome windows. There are actually a few such places.
</p>
<h4 name="Using_an_XPCOM_singleton_component"> Using an XPCOM singleton component </h4>
<p>The cleanest and most powerful way to share data is to define your own XPCOM component (you can write one in JavaScript) and access it from anywhere using a <code>getService</code> call:
</p>
<pre class="eval">Components.classes["@domain.org/mycomponent;1"].getService();
</pre>
<ul><li> Pros:
<ul><li> It's the "right way".
</li><li> You can store arbitrary JavaScript objects in the component.
</li><li> The scope is not shared between components, so you don't have to worry about name collisions.
</li></ul>
</li><li> Cons:
<ul><li> You can't use the <code><a href="en/DOM/window">window</a></code> object, its members, like <code>alert</code> and <code>open</code>, and many other objects available from inside a window. The functionality isn't lost, however -- you just have to use the XPCOM components directly instead of using convenient shortcuts. Of course, this doesn't matter if you just store data in the component.
</li><li> Learning to create XPCOM components takes time.
</li></ul>
</li></ul>
<p>There are several articles and books about creating XPCOM components online.
</p>
<h4 name="Storing_shared_data_in_preferences"> Storing shared data in preferences </h4>
<p>If you just need to store a string or a number, writing a whole XPCOM component may be an unnecessary complication. You can use the <a class="external" href="http://www.xulplanet.com/references/xpcomref/ifaces/nsIPrefService.html">preferences service</a> in such cases.
</p>
<ul><li> Pros:
<ul><li> Quite easy to use for storing simple data.
</li></ul>
</li><li> Cons:
<ul><li> Can't easily be used to store complex data.
</li><li> Abusing the preferences service and not cleaning up after yourself can cause <tt>prefs.js</tt> to grow large and slow down application startup.
</li></ul>
</li></ul>
<p>See this <a class="external" href="http://kb.mozillazine.org/Dev_:_Using_preferences">MozillaZine page about preferences</a> for detailed description of the preferences system and example code.
</p><p>Example:
</p>
<pre class="eval">var prefs = Components.classes["@mozilla.org/preferences-service;1"]
                      .getService(Components.interfaces.nsIPrefService);
var branch = prefs.getBranch("extensions.myext.");
var var1 = branch.getBoolPref("var1"); // get a pref
</pre>
<h4 name="The_hidden_window_hack"> The hidden window hack </h4>
<p>Some extension authors use the special <i>hidden window</i> to store their data and code. The hidden window is similar to a regular window, but unlike any other window, it's available the whole time the application is running, and isn't visible to user. It's used to implement the menus on Macs, so it isn't needed on Windows. It will be removed from Windows builds eventually.
</p>
<ul><li> Pros:
<ul><li> If you're running code in the hidden window, the <code>Window</code> object and its properties are available, unlike the component case.
</li><li> You can store arbitrary JavaScript objects in the hidden window.
</li></ul>
</li><li> Cons:
<ul><li> It's a hack.
</li><li> The hidden window may be removed from Windows builds.
</li></ul>
</li></ul>
<p>You can see this technique in action in <a class="external" href="https://addons.mozilla.org/firefox/436/">rue's SessionSaver extension</a>.
</p>{{ wiki.languages( { "fr": "fr/Travailler_avec_des_fen\u00eatres_dans_le_chrome" } ) }}
Revert to this revision