Working with windows in chrome code
2010-01-24 16:58
399 查看
转载自:https://developer.mozilla.org/En/Working_with_windows_in_chrome_code
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.
The first parameter to
The second parameter is the window's name; the name can be used in links or forms as the
The third, and optional, parameter is a list of special window features the window should have.
The
If the
Technically speaking, it implements a number of interfaces, including
Note however, that the
You can get a
The same holds for chrome windows opened inside a tab of
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).
You should be aware of XPCNativeWrappers when working with untrusted content. With XPCNativeWrappers turned on (which is the default in Firefox 1.5+), your extension can safely access the DOM of the content document, but not the content JavaScript. Bypassing XPCNativeWrapper to work with content JavaScript directly can lead to security problems.
See Interaction between privileged and non-privileged pages if you need to interact with the content page.
The
In case of
For example, you can use
Some examples use
For more sidebar tips, see Code snippets:Sidebar.
A typical case when this can be useful is when your code runs in the sidebar in the main Firefox window and you want to access the elements in the main browser window.
The DOM tree, as shown by the DOM Inspector, can look like this:
where the child window is where your code runs in.
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:
This allows you to cross the chrome-content boundaries, and returns the main window object.
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.
Note: if you want to pass data between privileged (chrome) and non-privileged (web page) windows, or vice-versa, read this instead.
Example 1: Passing data to window when opening it with
When you open a window using
In this example, we're using
Opener code:
Let's add code to the previous example to notify the opener when the user presses Cancel on the progress dialog.
Using
If we're sure the window that opened the progress dialog declares the
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:
The progress dialog can then run the callback like this:
Example 3: Using
The
One case in which you might want to use
This means the value of
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.
The function almost works the same as
This uses the wrappedJSObject trick. By passing args to openWindow xpconnect will automatically wrap it as a generic nsISupports. The opened window can then just get at the underlying JavaScript object using wrappedJSObject.
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.
Pros:
It's the "right way"
Very simple to make and import.
No need to build an XPCOM component.
Cons:
Only works in Firefox 3 or newer.
The scope is shared between modules and the importing scope, so you have to worry about name collisions.
You can't use the
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.
Works in older versions of Firefox.
Cons:
You can't use the
Learning to create XPCOM components takes time.
There are several articles and books about creating XPCOM components online.
Pros:
Its 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.
No need to build an XPCOM component.
Cons:
Only works in Firefox 3 or newer.
You can't use the
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
See Code snippets:Preferences for detailed description of the preferences system and example code.
Example:
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 awindow.openor
window.openDialogDOM call, like this:
var win = window.open("chrome://myextension/content/about.xul", "aboutMyExtension", "chrome,centerscreen");
The first parameter to
window.openis 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
targetattribute. 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.openDialogfunction 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
dialogfeature is specified.
If the
Windowobject 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.openimplementation 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 thewinvariable 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
Windowobject (usually
ChromeWindow), of the same type that the
windowvariable. (See Window Terminology).
Technically speaking, it implements a number of interfaces, including
nsIDOMJSWindowand
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
loadhandler of the window being opened or pass a callback function, as described below.
You can get a
Windowobject 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
Windowobject 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
<tabbrowser>. 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<tabbrowser>,
<browser>, or
<iframe>element inside your document. You can use
browser.contentDocumentto access that document and
browser.contentWindowto access the
Windowobject of that document.
You should be aware of XPCNativeWrappers when working with untrusted content. With XPCNativeWrappers turned on (which is the default in Firefox 1.5+), your extension can safely access the DOM of the content document, but not the content JavaScript. Bypassing XPCNativeWrapper to work with content JavaScript directly can lead to security problems.
See Interaction between privileged and non-privileged pages if you need to interact with the content page.
The
contentshortcut
In case of
<browser type="content-primary" />, you can use the content shortcut property to accesss the
Windowobject 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.documentin a
browser.xuloverlay to access the web page in the selected tab in a Firefox window.
Some examples use
_contentinstead of
content. The former has been deprecated for a while, and you should use
contentin the new code.
Accessing a document in the sidebar
Firefox has a sidebar, which is implemented as a<browser>element with id="sidebar". To access the elements and variables inside the sidebar, you need to use
document.getElementById("sidebar").contentDocumentor
.contentWindow, like when Accessing content documents.
For more sidebar tips, see Code snippets:Sidebar.
Accessing the elements of the top-level document from a child window
The opposite case is when you want to access the chrome document from a privileged script loaded in a<browser>or an
<iframe>.
A typical case when this can be useful is when your code runs in the sidebar in the main Firefox window and you want to access the elements in the main browser window.
The DOM tree, as shown by the DOM Inspector, can look like this:
#document window main-window ... browser #document window myExtensionWindow
where the child window is where your code runs in.
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 existing windows. Two of its methods are often used to obtain information about currently open windows:getMostRecentWindowand
getEnumerator. Please refer to the nsIWindowMediator page for more information and examples of using
nsIWindowMediator.
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.
Note: if you want to pass data between privileged (chrome) and non-privileged (web page) windows, or vice-versa, read this instead.
Example 1: Passing data to window when opening it with openDialog
When you open a window using window.openDialogor
nsIWindowWatcher.openWindow, you can pass an arbitrary number of arguments to that window. Arguments are simple JavaScript objects, accessible through
window.argumentsproperty in the opened window.
In this example, we're using
window.openDialogto open a progress dialog. We pass in the current status text as well as the maximum and current progress values. Note that using
nsIWindowWatcher.openWindowis a bit less trivial [1].
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
openerproperty returns its window's opener; that is, the Window object that opened it.
If we're sure the window that opened the progress dialog declares the
cancelOperationfunction, 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.openerproperty 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
nsIWindowMediatorinterface, introduced above.
One case in which you might want to use
nsIWindowMediatoris 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.openerin your Options dialog is not necessarily the browser window -- instead, it might be the Extension Manager window. You could check the
locationproperty of the
openerand use
opener.openerin 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.
Example 4: Using nsIWindowWatcher when you don't have a window (primitives only)
For those times when you don't have access to awindowobject (such as a JavaScript XPCOM object), you can use
nsIWindowWatcher.openWindowto open a XUL window. The problem is passing the data. It's a bit harder than using
window.openDialog. Here is helper function that will package the data correctly and pass it to the newly opened window:
function openDialog(parentWindow, url, windowName, features) { var array = Components.classes["@mozilla.org/array;1"] .createInstance(Components.interfaces.nsIMutableArray); for (var i=4; i<arguments.length; i++) { var variant = Components.classes["@mozilla.org/variant;1"] .createInstance(Components.interfaces.nsIWritableVariant); variant.setFromVariant(arguments[i]); array.appendElement(variant, false); } var watcher = Components.classes["@mozilla.org/embedcomp/window-watcher;1"] .getService(Components.interfaces.nsIWindowWatcher); return watcher.openWindow(parentWindow, url, windowName, features, array); }
The function almost works the same as
window.openDialogbut accepts an optional parent window as first parameter. The parent window can be null. The only data types that can be passed to the new window are primitives and arrays. JavaScript objects cannot be passed. All of the arguments are passed by value, that is to say that any changes made to the values in the new window will not affect the calling code.
Example 5: Using nsIWindowWatcher for passing an arbritrary JavaScript object
It is still possible to pass any JavaScript object using nsIWindowWatcher, it just takes a little more effort. In this example a single object is passed, though it is possible to pass multiple objects and primitives using a combination of this and the example above.// In the calling code var args = { param1: true, param2: 42 }; args.wrappedJSObject = args; var watcher = Components.classes["@mozilla.org/embedcomp/window-watcher;1"] .getService(Components.interfaces.nsIWindowWatcher); watcher.openWindow(null, url, windowName, features, args);
// In the window code var args = window.arguments[0].wrappedJSObject;
This uses the wrappedJSObject trick. By passing args to openWindow xpconnect will automatically wrap it as a generic nsISupports. The opened window can then just get at the underlying JavaScript object using wrappedJSObject.
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 JavaScript code modules
JavaScript code modules New in Firefox 3 is a simple method for creating shared global singleton objects that can be imported into any other JavaScript scope. The importing scope will have access to the objects and data in the code module. Since the code module is cached, all scopes get the same instance of the code module and can share the data in the module. See Components.utils.import for more information.Pros:
It's the "right way"
Very simple to make and import.
No need to build an XPCOM component.
Cons:
Only works in Firefox 3 or newer.
The scope is shared between modules and the importing scope, so you have to worry about name collisions.
You can't use the
windowobject, its members, like
alertand
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.
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 agetServicecall:
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.
Works in older versions of Firefox.
Cons:
You can't use the
windowobject, its members, like
alertand
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.
Using FUEL Application object
The FUEL JavaScript library New in Firefox 3 has a simple method for sharing data between windows. TheApplicationobject supports a
storageproperty which can be used to store data globally. This method is a simplified form of the XPCOM singleton method.
Application.storage.set(keyname, data); var data = Application.storage.get(keyname, default); where: keyname is a string used to identify the data data is the data default is the data value to return if keyname does not exists
Pros:
Its 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.
No need to build an XPCOM component.
Cons:
Only works in Firefox 3 or newer.
You can't use the
windowobject, its members, like
alertand
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.
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
prefs.jsto grow large and slow down application startup.
See Code snippets: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
See also
Code snippets:Dialogs相关文章推荐
- 翻译 在chrome代码中操作窗口 Working with windows in chrome code
- 好文推荐:Development Tools and Techniques for Working with Code in Windows SharePoint Services 3.0
- 好文推荐:Development Tools and Techniques for Working with Code in Windows SharePoint Services 3.0
- Working with hashCode and equals methods in java
- Working with Windows Workflow Foundation in ASP.NET
- Android: Simplified source code for parsing and working with XML data and web services in Android
- Run Android apps in Windows, OS X, Linux with Chrome (and a modified runtime)
- Nullsoft Winamp Plug-in With Bitmapped UI, Docking and Restrictive Resizing - The Code Project - Dialog and Windows
- Working with Windows Workflow Foundation in ASP.NET
- Working With Code Snippets In Sublime Text
- Working with Windows Workflow Foundation in ASP.NET
- OpenOffice.org Code Snippets--Working with a Presentation Document in C++
- Working with hashCode and equals methods in java
- Working with Files(Chapter 16 of Programming in Objective-C 2.0)
- Working with Money in Java
- [Python] Reuse Code in Multiple Projects with Python Modules
- Working with user roles and permissions in SharePoint Object Model
- Code a packet sniffer in python with pcapy exte...
- Working with Web Resources in ASP.NET 2.0
- run html5 in with chrome frame