summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJavier <dev.git@javispedro.com>2014-01-28 17:39:18 +0100
committerJavier <dev.git@javispedro.com>2014-01-28 17:39:18 +0100
commit2231b514e3646c6762818926b45b836e4263c0ea (patch)
treecef1607e512e9b71961ae64e6c8d29d00fd72ed7
downloadtopmenu-mozilla-2231b514e3646c6762818926b45b836e4263c0ea.tar.gz
topmenu-mozilla-2231b514e3646c6762818926b45b836e4263c0ea.zip
initial import
-rw-r--r--.gitignore2
-rw-r--r--COPYING502
-rw-r--r--Makefile18
-rw-r--r--chrome.manifest3
-rw-r--r--chrome/content/ctypes-utils.js192
-rw-r--r--chrome/content/gdk-pixbuf.js28
-rw-r--r--chrome/content/gdk.js64
-rw-r--r--chrome/content/glib.js35
-rw-r--r--chrome/content/gobject.js46
-rw-r--r--chrome/content/gtk.js85
-rw-r--r--chrome/content/log4moz.js698
-rw-r--r--chrome/content/overlay.js52
-rw-r--r--chrome/content/overlay.xul4
-rw-r--r--chrome/content/topmenu-client.js27
-rw-r--r--chrome/content/topmenuservice.js792
-rw-r--r--chrome/content/vkgdkmap.js27
-rw-r--r--install.rdf39
-rw-r--r--topmenu-mozilla.config1
-rw-r--r--topmenu-mozilla.creator1
-rw-r--r--topmenu-mozilla.files15
-rw-r--r--topmenu-mozilla.includes0
21 files changed, 2631 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3467701
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+*.creator.user
+topmenu.xpi
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..4362b49
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,502 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..51d3e3f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,18 @@
+ZIP?=zip
+
+# What goes inside the .xpi package
+CONTENT:=install.rdf chrome.manifest \
+ $(wildcard chrome/content/*)
+
+all: dist
+
+# "dist" target builds .xpi extension package.
+dist: topmenu.xpi
+
+topmenu.xpi: $(CONTENT)
+ $(ZIP) $(ZIPFLAGS) $@ $^
+
+clean:
+ rm -f topmenu.xpi
+
+.PHONY: all dist clean
diff --git a/chrome.manifest b/chrome.manifest
new file mode 100644
index 0000000..2a300c9
--- /dev/null
+++ b/chrome.manifest
@@ -0,0 +1,3 @@
+content topmenu chrome/content/
+
+overlay chrome://browser/content/browser.xul chrome://topmenu/content/overlay.xul
diff --git a/chrome/content/ctypes-utils.js b/chrome/content/ctypes-utils.js
new file mode 100644
index 0000000..9fd24ec
--- /dev/null
+++ b/chrome/content/ctypes-utils.js
@@ -0,0 +1,192 @@
+/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Firetray
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Messaging, Ltd.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Chris Coulson <chris.coulson@canonical.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/ctypes.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("chrome://topmenu/content/log4moz.js");
+
+var EXPORTED_SYMBOLS = [ "ctypes_library" ];
+
+let log = Log4Moz.repository.getLogger("topmenu.ctypes-utils");
+
+/**
+ * Loads a library using ctypes and exports an object on to the specified
+ * global object. The name of the exported object will be the first name
+ * specified in the global objects EXPORTED_SYMBOLS list.
+ *
+ * It is an error to call this function more than once in a JS module. This
+ * implies that you should have one JS module per ctypes library.
+ *
+ * In addition to native types and functions, the exported object will contain
+ * some additional utility functions:
+ *
+ * close Close the library and unload the JS module
+ * available Returns true if the library is available, false otherwise
+ * ABI A property containing the library ABI loaded (or -1 if unavailable)
+ *
+ * @param aName
+ * The name of the library to load, without the "lib" prefix or any
+ * file extension.
+ *
+ * @param aABIs
+ * An array of library ABI's to search for. The first one found will
+ * be loaded and the loaded ABI will be saved on the exported object.
+ *
+ * @param aDefines
+ * A function which will be called to load library symbols and create
+ * types. The function will be called with one parameter, which contains
+ * several functions for binding symbols. The "this" object will be
+ * the exported object, on to which you can should types and symbols.
+ *
+ * @param aGlobal
+ * The global object on to which we export an object. This must be a
+ * a valid JSM global object.
+ *
+ */
+function ctypes_library(aName, aABIs, aDefines, aGlobal) {
+ try {
+ log.debug("Trying to load library: " + aName);
+
+ if (typeof(aName) != "string") {
+ throw Error("Invalid library name");
+ }
+
+ if (!aABIs || typeof(aABIs) != "object") {
+ throw Error("Invalid range of library ABI's");
+ }
+
+ if (typeof(aDefines) != "function") {
+ throw Error("Invalid defines function");
+ }
+
+ if (!aGlobal || typeof(aGlobal) != "object" || !aGlobal.EXPORTED_SYMBOLS ||
+ typeof(aGlobal.EXPORTED_SYMBOLS) != "object") {
+ throw Error("Must specify a valid global object from a loaded JS module");
+ }
+
+ if (!("__URI__" in aGlobal) || !aGlobal.__URI__) {
+ throw Error("This JS module has already been unloaded");
+ }
+
+ if (aGlobal[aGlobal.EXPORTED_SYMBOLS[0]]) {
+ throw Error("Was ctypes_library() called more than once for this module?");
+ }
+
+ var library;
+ for each (let abi in aABIs) {
+ let soname = "lib" + aName + ".so." + abi.toString();
+ log.debug("Trying " + soname);
+ try {
+ library = ctypes.open(soname);
+ this.ABI = abi;
+ log.debug("Successfully loaded " + soname);
+ break;
+ } catch(e) {
+ log.error(soname+" unfound.");
+ }
+ }
+
+ this.name = aName;
+
+ this.close = function() {
+ log.debug("Closing library " + aName);
+ library.close();
+ this.ABI = -1;
+
+ if (!("__URI__" in aGlobal) || !aGlobal.__URI__) {
+ // We could have already been unloaded by now
+ return;
+ }
+
+ log.debug("Unloading JS module " + aGlobal.__URI__);
+ Cu.unload(aGlobal.__URI__);
+ };
+
+ this.available = function() {
+ return this.ABI != -1;
+ };
+
+ if (!library) {
+ log.debug("Failed to load library: " + aName);
+ this.ABI = -1;
+ return;
+ }
+
+ var self = this;
+ let lib = {
+ declare: function() {
+ try {
+ args = [];
+ args.push(arguments[0]);
+ args.push(ctypes.default_abi);
+ for each (let arg in Array.prototype.slice.call(arguments, 1)) {
+ args.push(arg);
+ }
+
+ return library.declare.apply(library, args);
+ } catch (ex) {
+ Cu.reportError(ex);
+ log.error("Missing symbol " + arguments[0] + " in library " + aName);
+ self.ABI = -1;
+ return null;
+ }
+ },
+
+ lazy_bind: function() {
+ var args = Array.prototype.slice.call(arguments, 0);
+ XPCOMUtils.defineLazyGetter(self, arguments[0], function() {
+ return lib.declare.apply(lib, args);
+ });
+ },
+
+ bind_now: function() {
+ self[arguments[0]] = this.declare.apply(this, arguments);
+ }
+ };
+
+ aDefines.call(this, lib);
+
+ aGlobal[aGlobal.EXPORTED_SYMBOLS[0]] = this;
+ } catch(e) {
+ Cu.reportError(e);
+ log.error(aName+" definition error: "+e);
+ this.ABI = -1;
+ }
+}
diff --git a/chrome/content/gdk-pixbuf.js b/chrome/content/gdk-pixbuf.js
new file mode 100644
index 0000000..3e58a4c
--- /dev/null
+++ b/chrome/content/gdk-pixbuf.js
@@ -0,0 +1,28 @@
+var EXPORTED_SYMBOLS = [ "gdk_pixbuf" ];
+
+const LIBNAME = "gdk_pixbuf_xlib-2.0";
+const ABIS = [ 0 ];
+
+const Cu = Components.utils;
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+Cu.import("resource://gre/modules/ctypes.jsm");
+Cu.import("chrome://topmenu/content/ctypes-utils.js");
+Cu.import("chrome://topmenu/content/glib.js");
+Cu.import("chrome://topmenu/content/gobject.js");
+
+function defines(lib) {
+ this.GdkPixbuf = gobject.GObject;
+ this.GdkPixbufDestroyNotify = ctypes.FunctionType(ctypes.default_abi,
+ ctypes.void_t,
+ [glib.guchar.ptr, glib.gpointer]);
+ this.GdkColorspace = glib.guint;
+ this.GDK_COLORSPACE_RGB = 0;
+
+ lib.lazy_bind("gdk_pixbuf_new", this.GdkPixbuf.ptr, this.GdkColorspace, glib.gboolean, glib.gint, glib.gint, glib.gint);
+ lib.lazy_bind("gdk_pixbuf_new_from_data", this.GdkPixbuf.ptr, glib.guchar.ptr, this.GdkColorspace, glib.gboolean, glib.gint, glib.gint, glib.gint, glib.gint, this.GdkPixbufDestroyNotify.ptr, glib.gpointer);
+ lib.lazy_bind("gdk_pixbuf_copy", this.GdkPixbuf.ptr, this.GdkPixbuf.ptr);
+}
+
+new ctypes_library(LIBNAME, ABIS, defines, this);
diff --git a/chrome/content/gdk.js b/chrome/content/gdk.js
new file mode 100644
index 0000000..25458ef
--- /dev/null
+++ b/chrome/content/gdk.js
@@ -0,0 +1,64 @@
+var EXPORTED_SYMBOLS = [ "gdk" ];
+
+const LIBNAME = "gdk-x11-2.0";
+const ABIS = [ 0 ];
+
+const Cu = Components.utils;
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+Cu.import("resource://gre/modules/ctypes.jsm");
+Cu.import("chrome://topmenu/content/ctypes-utils.js");
+Cu.import("chrome://topmenu/content/glib.js");
+Cu.import("chrome://topmenu/content/gobject.js");
+
+function defines(lib) {
+ this.GdkDrawable = gobject.GObject;
+ this.GdkWindow = this.GdkDrawable;
+
+ this.GdkModifierType = glib.guint;
+ this.GDK_SHIFT_MASK = 1 << 0;
+ this.GDK_LOCK_MASK = 1 << 1;
+ this.GDK_CONTROL_MASK = 1 << 2;
+ this.GDK_MOD1_MASK = 1 << 3;
+ this.GDK_ALT_MASK = this.GDK_MOD1_MASK;
+ this.GDK_MOD2_MASK = 1 << 4;
+ this.GDK_MOD3_MASK = 1 << 5;
+ this.GDK_SUPER_MASK = 1 << 26;
+ this.GDK_META_MASK = 1 << 28;
+
+ this.GDK_KEY_BackSpace = 0xff08;
+ this.GDK_KEY_Tab = 0xff09;
+ this.GDK_KEY_Return = 0xff0d;
+ this.GDK_KEY_Escape = 0xff1b;
+ this.GDK_KEY_Delete = 0xffff;
+ this.GDK_KEY_F1 = 0xffbe;
+ this.GDK_KEY_F2 = 0xffbf;
+ this.GDK_KEY_F3 = 0xffc0;
+ this.GDK_KEY_F4 = 0xffc1;
+ this.GDK_KEY_F5 = 0xffc2;
+ this.GDK_KEY_F6 = 0xffc3;
+ this.GDK_KEY_F7 = 0xffc4;
+ this.GDK_KEY_F8 = 0xffc5;
+ this.GDK_KEY_F9 = 0xffc6;
+ this.GDK_KEY_F10 = 0xffc7;
+ this.GDK_KEY_F11 = 0xffc8;
+ this.GDK_KEY_L1 = 0xffc8;
+ this.GDK_KEY_F12 = 0xffc9;
+ this.GDK_KEY_Home = 0xff50;
+ this.GDK_KEY_Left = 0xff51;
+ this.GDK_KEY_Up = 0xff52;
+ this.GDK_KEY_Right = 0xff53;
+ this.GDK_KEY_Down = 0xff54;
+ this.GDK_KEY_Prior = 0xff55;
+ this.GDK_KEY_Page_Up = 0xff55;
+ this.GDK_KEY_Next = 0xff56;
+ this.GDK_KEY_Page_Down = 0xff56;
+ this.GDK_KEY_End = 0xff57;
+
+ lib.lazy_bind("gdk_window_get_toplevel", this.GdkWindow.ptr, this.GdkWindow.ptr);
+
+ lib.lazy_bind("gdk_unicode_to_keyval", glib.guint, glib.guint32);
+}
+
+new ctypes_library(LIBNAME, ABIS, defines, this);
diff --git a/chrome/content/glib.js b/chrome/content/glib.js
new file mode 100644
index 0000000..fed4caf
--- /dev/null
+++ b/chrome/content/glib.js
@@ -0,0 +1,35 @@
+var EXPORTED_SYMBOLS = [ "glib" ];
+
+const LIBNAME = "glib-2.0";
+const ABIS = [ 0 ];
+
+const Cu = Components.utils;
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+Cu.import("resource://gre/modules/ctypes.jsm");
+Cu.import("chrome://topmenu/content/ctypes-utils.js");
+
+function defines(lib) {
+ this.gpointer = ctypes.voidptr_t;
+ this.gulong = ctypes.unsigned_long;
+ this.guint = ctypes.unsigned_int;
+ this.guint8 = ctypes.uint8_t;
+ this.guint16 = ctypes.uint16_t;
+ this.guint32 = ctypes.uint32_t;
+ this.gint = ctypes.int;
+ this.gint8 = ctypes.int8_t;
+ this.gint16 = ctypes.int16_t;
+ this.gint32 = ctypes.int32_t;
+ this.gchar = ctypes.char;
+ this.guchar = ctypes.unsigned_char;
+ this.gboolean = this.gint;
+ this.gfloat = ctypes.float;
+ this.gdouble = ctypes.double;
+ this.gsize = ctypes.unsigned_long;
+ this.GCallback = ctypes.voidptr_t;
+ this.GClosureNotify = this.gpointer;
+ this.GFunc = ctypes.void_t.ptr;
+};
+
+new ctypes_library(LIBNAME, ABIS, defines, this);
diff --git a/chrome/content/gobject.js b/chrome/content/gobject.js
new file mode 100644
index 0000000..92bd318
--- /dev/null
+++ b/chrome/content/gobject.js
@@ -0,0 +1,46 @@
+var EXPORTED_SYMBOLS = [ "gobject" ];
+
+const LIBNAME = "gobject-2.0";
+const ABIS = [ 0 ];
+
+const Cu = Components.utils;
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+Cu.import("resource://gre/modules/ctypes.jsm");
+Cu.import("chrome://topmenu/content/ctypes-utils.js");
+Cu.import("chrome://topmenu/content/glib.js");
+
+function defines(lib) {
+ this.GObject = ctypes.StructType("GObject");
+ this.GCallback = glib.gpointer;
+ this.GClosureNotify = glib.gpointer;
+ this.GConnectFlags = glib.guint;
+ this.G_CONNECT_AFTER = 1 << 0;
+ this.G_CONNECT_SWAPPED = 1 << 1;
+ this.GParamSpec = ctypes.StructType("GParamSpec");
+ this.GObjectNotifyCallback = ctypes.FunctionType(ctypes.default_abi,
+ ctypes.void_t,
+ [this.GObject.ptr, this.GParamSpec.ptr, glib.gpointer]);
+
+ lib.lazy_bind("g_object_unref", ctypes.void_t, this.GObject.ptr);
+ lib.lazy_bind("g_object_ref", this.GObject.ptr, this.GObject.ptr);
+ lib.lazy_bind("g_object_ref_sink", this.GObject.ptr, this.GObject.ptr);
+
+ lib.lazy_bind("g_signal_connect_data", glib.gulong, this.GObject.ptr, glib.gchar.ptr, this.GCallback, glib.gpointer, this.GClosureNotify, this.GConnectFlags);
+ lib.lazy_bind("g_signal_handler_disconnect", ctypes.void_t, this.GObject.ptr, glib.gulong);
+
+ this.ref = function(obj) {
+ this.g_object_ref(obj);
+ return ctypes.CDataFinalizer(obj, this.g_object_unref);
+ }
+ this.ref_sink = function(obj) {
+ this.g_object_ref_sink(obj);
+ return ctypes.CDataFinalizer(obj, this.g_object_unref);
+ }
+ this.signal_connect = function(obj, sign, callback, data) {
+ return this.g_signal_connect_data(obj, sign, callback, data, null, 0);
+ }
+}
+
+new ctypes_library(LIBNAME, ABIS, defines, this);
diff --git a/chrome/content/gtk.js b/chrome/content/gtk.js
new file mode 100644
index 0000000..e24f0d5
--- /dev/null
+++ b/chrome/content/gtk.js
@@ -0,0 +1,85 @@
+var EXPORTED_SYMBOLS = [ "gtk" ];
+
+const LIBNAME = "gtk-x11-2.0";
+const ABIS = [ 0 ];
+
+const Cu = Components.utils;
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+Cu.import("resource://gre/modules/ctypes.jsm");
+Cu.import("chrome://topmenu/content/ctypes-utils.js");
+Cu.import("chrome://topmenu/content/glib.js");
+Cu.import("chrome://topmenu/content/gobject.js");
+Cu.import("chrome://topmenu/content/gdk.js");
+Cu.import("chrome://topmenu/content/gdk-pixbuf.js");
+
+function defines(lib) {
+ this.GtkWidget = gobject.GObject;
+ this.GtkBin = this.GtkWidget;
+ this.GtkMenuShell = this.GtkWidget;
+ this.GtkMenu = this.GtkMenuShell;
+ this.GtkItem = this.GtkWidget;
+ this.GtkMenuItem = this.GtkItem;
+ this.GtkCheckMenuItem = this.GtkMenuItem;
+ this.GtkImageMenuItem = this.GtkMenuItem;
+ this.GtkImage = this.GtkWidget;
+
+ this.GtkAccelGroup = gobject.GObject;
+
+ this.GtkIconSize = glib.guint;
+ this.GTK_ICON_SIZE_INVALID = 0;
+ this.GTK_ICON_SIZE_MENU = 1;
+ this.GTK_ICON_SIZE_SMALL_TOOLBAR = 2;
+ this.GTK_ICON_SIZE_LARGE_TOOLBAR = 3;
+ this.GTK_ICON_SIZE_BUTTON = 4;
+ this.GTK_ICON_SIZE_DND = 5;
+ this.GTK_ICON_SIZE_DIALOG = 6;
+
+ this.GtkAccelFlags = glib.guint;
+ this.GTK_ACCEL_VISIBLE = 1 << 0;
+ this.GTK_ACCEL_LOCKED = 1 << 1;
+
+ this.GtkItemSelect = ctypes.FunctionType(ctypes.default_abi,
+ ctypes.void_t,
+ [this.GtkItem.ptr, glib.gpointer]);
+ this.GtkItemDeselect = this.GtkItemSelect;
+
+ this.GtkMenuItemActivate = ctypes.FunctionType(ctypes.default_abi,
+ ctypes.void_t,
+ [this.GtkMenuItem.ptr, glib.gpointer]);
+
+ lib.lazy_bind("gtk_widget_show", ctypes.void_t, this.GtkWidget.ptr);
+ lib.lazy_bind("gtk_widget_show_all", ctypes.void_t, this.GtkWidget.ptr);
+ lib.lazy_bind("gtk_widget_destroy", ctypes.void_t, this.GtkWidget.ptr);
+ lib.lazy_bind("gtk_widget_add_accelerator", ctypes.void_t, this.GtkWidget.ptr, glib.gchar.ptr, this.GtkAccelGroup.ptr, glib.guint, gdk.GdkModifierType, this.GtkAccelFlags);
+ lib.lazy_bind("gtk_widget_mnemonic_activate", ctypes.void_t, this.GtkWidget.ptr, glib.gboolean);
+ lib.lazy_bind("gtk_widget_set_sensitive", ctypes.void_t, this.GtkWidget.ptr, glib.gboolean);
+ lib.lazy_bind("gtk_widget_set_visible", ctypes.void_t, this.GtkWidget.ptr, glib.gboolean);
+
+ lib.lazy_bind("gtk_bin_get_child", this.GtkWidget.ptr, this.GtkBin.ptr);
+
+ lib.lazy_bind("gtk_menu_shell_insert", ctypes.void_t, this.GtkMenuShell.ptr, this.GtkWidget.ptr, glib.gint);
+ lib.lazy_bind("gtk_menu_shell_append", ctypes.void_t, this.GtkMenuShell.ptr, this.GtkWidget.ptr);
+
+ lib.lazy_bind("gtk_menu_new", this.GtkWidget.ptr);
+
+ lib.lazy_bind("gtk_menu_item_new", this.GtkWidget.ptr);
+ lib.lazy_bind("gtk_menu_item_new_with_mnemonic", this.GtkWidget.ptr, glib.gchar.ptr);
+ lib.lazy_bind("gtk_menu_item_set_label", ctypes.void_t, this.GtkMenuItem.ptr, glib.gchar.ptr);
+ lib.lazy_bind("gtk_menu_item_set_submenu", ctypes.void_t, this.GtkMenuItem.ptr, this.GtkWidget.ptr);
+ lib.lazy_bind("gtk_check_menu_item_new_with_mnemonic", this.GtkWidget.ptr, glib.gchar.ptr);
+ lib.lazy_bind("gtk_check_menu_item_set_active", ctypes.void_t, this.GtkImageMenuItem.ptr, glib.gboolean);
+ lib.lazy_bind("gtk_check_menu_item_set_draw_as_radio", ctypes.void_t, this.GtkImageMenuItem.ptr, glib.gboolean);
+ lib.lazy_bind("gtk_image_menu_item_new_from_stock", this.GtkWidget.ptr, glib.gchar.ptr, this.GtkAccelGroup.ptr);
+ lib.lazy_bind("gtk_image_menu_item_new_with_mnemonic", this.GtkWidget.ptr, glib.gchar.ptr);
+ lib.lazy_bind("gtk_image_menu_item_set_image", ctypes.void_t, this.GtkImageMenuItem.ptr, this.GtkWidget.ptr);
+ lib.lazy_bind("gtk_separator_menu_item_new", this.GtkWidget.ptr);
+
+ lib.lazy_bind("gtk_image_new_from_pixbuf", this.GtkWidget.ptr, gdk_pixbuf.GdkPixbuf.ptr);
+ lib.lazy_bind("gtk_image_new_from_stock", this.GtkWidget.ptr, glib.gchar.ptr, this.GtkIconSize);
+
+ lib.lazy_bind("gtk_accel_group_new", this.GtkAccelGroup.ptr);
+}
+
+new ctypes_library(LIBNAME, ABIS, defines, this);
diff --git a/chrome/content/log4moz.js b/chrome/content/log4moz.js
new file mode 100644
index 0000000..5e12938
--- /dev/null
+++ b/chrome/content/log4moz.js
@@ -0,0 +1,698 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+this.EXPORTED_SYMBOLS = ['Log4Moz'];
+
+const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
+
+const ONE_BYTE = 1;
+const ONE_KILOBYTE = 1024 * ONE_BYTE;
+const ONE_MEGABYTE = 1024 * ONE_KILOBYTE;
+
+const STREAM_SEGMENT_SIZE = 4096;
+const PR_UINT32_MAX = 0xffffffff;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "OS",
+ "resource://gre/modules/osfile.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+ "resource://gre/modules/Task.jsm");
+
+this.Log4Moz = {
+ Level: {
+ Fatal: 70,
+ Error: 60,
+ Warn: 50,
+ Info: 40,
+ Config: 30,
+ Debug: 20,
+ Trace: 10,
+ All: 0,
+ Desc: {
+ 70: "FATAL",
+ 60: "ERROR",
+ 50: "WARN",
+ 40: "INFO",
+ 30: "CONFIG",
+ 20: "DEBUG",
+ 10: "TRACE",
+ 0: "ALL"
+ },
+ Numbers: {
+ "FATAL": 70,
+ "ERROR": 60,
+ "WARN": 50,
+ "INFO": 40,
+ "CONFIG": 30,
+ "DEBUG": 20,
+ "TRACE": 10,
+ "ALL": 0,
+ }
+ },
+
+ get repository() {
+ delete Log4Moz.repository;
+ Log4Moz.repository = new LoggerRepository();
+ return Log4Moz.repository;
+ },
+ set repository(value) {
+ delete Log4Moz.repository;
+ Log4Moz.repository = value;
+ },
+
+ LogMessage: LogMessage,
+ Logger: Logger,
+ LoggerRepository: LoggerRepository,
+
+ Formatter: Formatter,
+ BasicFormatter: BasicFormatter,
+ StructuredFormatter: StructuredFormatter,
+
+ Appender: Appender,
+ DumpAppender: DumpAppender,
+ ConsoleAppender: ConsoleAppender,
+ StorageStreamAppender: StorageStreamAppender,
+
+ FileAppender: FileAppender,
+ BoundedFileAppender: BoundedFileAppender,
+
+ // Logging helper:
+ // let logger = Log4Moz.repository.getLogger("foo");
+ // logger.info(Log4Moz.enumerateInterfaces(someObject).join(","));
+ enumerateInterfaces: function Log4Moz_enumerateInterfaces(aObject) {
+ let interfaces = [];
+
+ for (i in Ci) {
+ try {
+ aObject.QueryInterface(Ci[i]);
+ interfaces.push(i);
+ }
+ catch(ex) {}
+ }
+
+ return interfaces;
+ },
+
+ // Logging helper:
+ // let logger = Log4Moz.repository.getLogger("foo");
+ // logger.info(Log4Moz.enumerateProperties(someObject).join(","));
+ enumerateProperties: function Log4Moz_enumerateProps(aObject,
+ aExcludeComplexTypes) {
+ let properties = [];
+
+ for (p in aObject) {
+ try {
+ if (aExcludeComplexTypes &&
+ (typeof aObject[p] == "object" || typeof aObject[p] == "function"))
+ continue;
+ properties.push(p + " = " + aObject[p]);
+ }
+ catch(ex) {
+ properties.push(p + " = " + ex);
+ }
+ }
+
+ return properties;
+ }
+};
+
+
+/*
+ * LogMessage
+ * Encapsulates a single log event's data
+ */
+function LogMessage(loggerName, level, message, params) {
+ this.loggerName = loggerName;
+ this.level = level;
+ this.message = message;
+ this.params = params;
+
+ // The _structured field will correspond to whether this message is to
+ // be interpreted as a structured message.
+ this._structured = this.params && this.params.action;
+ this.time = Date.now();
+}
+LogMessage.prototype = {
+ get levelDesc() {
+ if (this.level in Log4Moz.Level.Desc)
+ return Log4Moz.Level.Desc[this.level];
+ return "UNKNOWN";
+ },
+
+ toString: function LogMsg_toString(){
+ let msg = "LogMessage [" + this.time + " " + this.level + " " +
+ this.message;
+ if (this.params) {
+ msg += " " + JSON.stringify(this.params);
+ }
+ return msg + "]"
+ }
+};
+
+/*
+ * Logger
+ * Hierarchical version. Logs to all appenders, assigned or inherited
+ */
+
+function Logger(name, repository) {
+ if (!repository)
+ repository = Log4Moz.repository;
+ this._name = name;
+ this.children = [];
+ this.ownAppenders = [];
+ this.appenders = [];
+ this._repository = repository;
+}
+Logger.prototype = {
+ get name() {
+ return this._name;
+ },
+
+ _level: null,
+ get level() {
+ if (this._level != null)
+ return this._level;
+ if (this.parent)
+ return this.parent.level;
+ dump("log4moz warning: root logger configuration error: no level defined\n");
+ return Log4Moz.Level.All;
+ },
+ set level(level) {
+ this._level = level;
+ },
+
+ _parent: null,
+ get parent() this._parent,
+ set parent(parent) {
+ if (this._parent == parent) {
+ return;
+ }
+ // Remove ourselves from parent's children
+ if (this._parent) {
+ let index = this._parent.children.indexOf(this);
+ if (index != -1) {
+ this._parent.children.splice(index, 1);
+ }
+ }
+ this._parent = parent;
+ parent.children.push(this);
+ this.updateAppenders();
+ },
+
+ updateAppenders: function updateAppenders() {
+ if (this._parent) {
+ let notOwnAppenders = this._parent.appenders.filter(function(appender) {
+ return this.ownAppenders.indexOf(appender) == -1;
+ }, this);
+ this.appenders = notOwnAppenders.concat(this.ownAppenders);
+ } else {
+ this.appenders = this.ownAppenders.slice();
+ }
+
+ // Update children's appenders.
+ for (let i = 0; i < this.children.length; i++) {
+ this.children[i].updateAppenders();
+ }
+ },
+
+ addAppender: function Logger_addAppender(appender) {
+ if (this.ownAppenders.indexOf(appender) != -1) {
+ return;
+ }
+ this.ownAppenders.push(appender);
+ this.updateAppenders();
+ },
+
+ removeAppender: function Logger_removeAppender(appender) {
+ let index = this.ownAppenders.indexOf(appender);
+ if (index == -1) {
+ return;
+ }
+ this.ownAppenders.splice(index, 1);
+ this.updateAppenders();
+ },
+
+ /**
+ * Logs a structured message object.
+ *
+ * @param action
+ * (string) A message action, one of a set of actions known to the
+ * log consumer.
+ * @param params
+ * (object) Parameters to be included in the message.
+ * If _level is included as a key and the corresponding value
+ * is a number or known level name, the message will be logged
+ * at the indicated level.
+ */
+ logStructured: function (action, params) {
+ if (!action) {
+ throw "An action is required when logging a structured message.";
+ }
+ if (!params) {
+ return this.log(this.level, undefined, {"action": action});
+ }
+ if (typeof params != "object") {
+ throw "The params argument is required to be an object.";
+ }
+
+ let level = params._level || this.level;
+ if ((typeof level == "string") && level in Log4Moz.Level.Numbers) {
+ level = Log4Moz.Level.Numbers[level];
+ }
+
+ params.action = action;
+ this.log(level, params._message, params);
+ },
+
+ log: function (level, string, params) {
+ if (this.level > level)
+ return;
+
+ // Hold off on creating the message object until we actually have
+ // an appender that's responsible.
+ let message;
+ let appenders = this.appenders;
+ for (let appender of appenders) {
+ if (appender.level > level) {
+ continue;
+ }
+ if (!message) {
+ message = new LogMessage(this._name, level, string, params);
+ }
+ appender.append(message);
+ }
+ },
+
+ fatal: function (string, params) {
+ this.log(Log4Moz.Level.Fatal, string, params);
+ },
+ error: function (string, params) {
+ this.log(Log4Moz.Level.Error, string, params);
+ },
+ warn: function (string, params) {
+ this.log(Log4Moz.Level.Warn, string, params);
+ },
+ info: function (string, params) {
+ this.log(Log4Moz.Level.Info, string, params);
+ },
+ config: function (string, params) {
+ this.log(Log4Moz.Level.Config, string, params);
+ },
+ debug: function (string, params) {
+ this.log(Log4Moz.Level.Debug, string, params);
+ },
+ trace: function (string, params) {
+ this.log(Log4Moz.Level.Trace, string, params);
+ }
+};
+
+/*
+ * LoggerRepository
+ * Implements a hierarchy of Loggers
+ */
+
+function LoggerRepository() {}
+LoggerRepository.prototype = {
+ _loggers: {},
+
+ _rootLogger: null,
+ get rootLogger() {
+ if (!this._rootLogger) {
+ this._rootLogger = new Logger("root", this);
+ this._rootLogger.level = Log4Moz.Level.All;
+ }
+ return this._rootLogger;
+ },
+ set rootLogger(logger) {
+ throw "Cannot change the root logger";
+ },
+
+ _updateParents: function LogRep__updateParents(name) {
+ let pieces = name.split('.');
+ let cur, parent;
+
+ // find the closest parent
+ // don't test for the logger name itself, as there's a chance it's already
+ // there in this._loggers
+ for (let i = 0; i < pieces.length - 1; i++) {
+ if (cur)
+ cur += '.' + pieces[i];
+ else
+ cur = pieces[i];
+ if (cur in this._loggers)
+ parent = cur;
+ }
+
+ // if we didn't assign a parent above, there is no parent
+ if (!parent)
+ this._loggers[name].parent = this.rootLogger;
+ else
+ this._loggers[name].parent = this._loggers[parent];
+
+ // trigger updates for any possible descendants of this logger
+ for (let logger in this._loggers) {
+ if (logger != name && logger.indexOf(name) == 0)
+ this._updateParents(logger);
+ }
+ },
+
+ getLogger: function LogRep_getLogger(name) {
+ if (name in this._loggers)
+ return this._loggers[name];
+ this._loggers[name] = new Logger(name, this);
+ this._updateParents(name);
+ return this._loggers[name];
+ }
+};
+
+/*
+ * Formatters
+ * These massage a LogMessage into whatever output is desired.
+ * BasicFormatter and StructuredFormatter are implemented here.
+ */
+
+// Abstract formatter
+function Formatter() {}
+Formatter.prototype = {
+ format: function Formatter_format(message) {}
+};
+
+// Basic formatter that doesn't do anything fancy.
+function BasicFormatter(dateFormat) {
+ if (dateFormat)
+ this.dateFormat = dateFormat;
+}
+BasicFormatter.prototype = {
+ __proto__: Formatter.prototype,
+
+ format: function BF_format(message) {
+ return message.time + "\t" +
+ message.loggerName + "\t" +
+ message.levelDesc + "\t" +
+ message.message + "\n";
+ }
+};
+
+// Structured formatter that outputs JSON based on message data.
+// This formatter will format unstructured messages by supplying
+// default values.
+function StructuredFormatter() { }
+StructuredFormatter.prototype = {
+ __proto__: Formatter.prototype,
+
+ format: function (logMessage) {
+ let output = {
+ _time: logMessage.time,
+ _namespace: logMessage.loggerName,
+ _level: logMessage.levelDesc
+ };
+
+ for (let key in logMessage.params) {
+ output[key] = logMessage.params[key];
+ }
+
+ if (!output.action) {
+ output.action = "UNKNOWN";
+ }
+
+ if (!output._message && logMessage.message) {
+ output._message = logMessage.message;
+ }
+
+ return JSON.stringify(output);
+ }
+}
+
+/*
+ * Appenders
+ * These can be attached to Loggers to log to different places
+ * Simply subclass and override doAppend to implement a new one
+ */
+
+function Appender(formatter) {
+ this._name = "Appender";
+ this._formatter = formatter? formatter : new BasicFormatter();
+}
+Appender.prototype = {
+ level: Log4Moz.Level.All,
+
+ append: function App_append(message) {
+ if (message) {
+ this.doAppend(this._formatter.format(message));
+ }
+ },
+ toString: function App_toString() {
+ return this._name + " [level=" + this._level +
+ ", formatter=" + this._formatter + "]";
+ },
+ doAppend: function App_doAppend(message) {}
+};
+
+/*
+ * DumpAppender
+ * Logs to standard out
+ */
+
+function DumpAppender(formatter) {
+ this._name = "DumpAppender";
+ Appender.call(this, formatter);
+}
+DumpAppender.prototype = {
+ __proto__: Appender.prototype,
+
+ doAppend: function DApp_doAppend(message) {
+ dump(message);
+ }
+};
+
+/*
+ * ConsoleAppender
+ * Logs to the javascript console
+ */
+
+function ConsoleAppender(formatter) {
+ this._name = "ConsoleAppender";
+ Appender.call(this, formatter);
+}
+ConsoleAppender.prototype = {
+ __proto__: Appender.prototype,
+
+ doAppend: function CApp_doAppend(message) {
+ if (message.level > Log4Moz.Level.Warn) {
+ Cu.reportError(message);
+ return;
+ }
+ Cc["@mozilla.org/consoleservice;1"].
+ getService(Ci.nsIConsoleService).logStringMessage(message);
+ }
+};
+
+/**
+ * Append to an nsIStorageStream
+ *
+ * This writes logging output to an in-memory stream which can later be read
+ * back as an nsIInputStream. It can be used to avoid expensive I/O operations
+ * during logging. Instead, one can periodically consume the input stream and
+ * e.g. write it to disk asynchronously.
+ */
+function StorageStreamAppender(formatter) {
+ this._name = "StorageStreamAppender";
+ Appender.call(this, formatter);
+}
+
+StorageStreamAppender.prototype = {
+ __proto__: Appender.prototype,
+
+ _converterStream: null, // holds the nsIConverterOutputStream
+ _outputStream: null, // holds the underlying nsIOutputStream
+
+ _ss: null,
+
+ get outputStream() {
+ if (!this._outputStream) {
+ // First create a raw stream. We can bail out early if that fails.
+ this._outputStream = this.newOutputStream();
+ if (!this._outputStream) {
+ return null;
+ }
+
+ // Wrap the raw stream in an nsIConverterOutputStream. We can reuse
+ // the instance if we already have one.
+ if (!this._converterStream) {
+ this._converterStream = Cc["@mozilla.org/intl/converter-output-stream;1"]
+ .createInstance(Ci.nsIConverterOutputStream);
+ }
+ this._converterStream.init(
+ this._outputStream, "UTF-8", STREAM_SEGMENT_SIZE,
+ Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
+ }
+ return this._converterStream;
+ },
+
+ newOutputStream: function newOutputStream() {
+ let ss = this._ss = Cc["@mozilla.org/storagestream;1"]
+ .createInstance(Ci.nsIStorageStream);
+ ss.init(STREAM_SEGMENT_SIZE, PR_UINT32_MAX, null);
+ return ss.getOutputStream(0);
+ },
+
+ getInputStream: function getInputStream() {
+ if (!this._ss) {
+ return null;
+ }
+ return this._ss.newInputStream(0);
+ },
+
+ reset: function reset() {
+ if (!this._outputStream) {
+ return;
+ }
+ this.outputStream.close();
+ this._outputStream = null;
+ this._ss = null;
+ },
+
+ doAppend: function (message) {
+ if (!message) {
+ return;
+ }
+ try {
+ this.outputStream.writeString(message);
+ } catch(ex) {
+ if (ex.result == Cr.NS_BASE_STREAM_CLOSED) {
+ // The underlying output stream is closed, so let's open a new one
+ // and try again.
+ this._outputStream = null;
+ } try {
+ this.outputStream.writeString(message);
+ } catch (ex) {
+ // Ah well, we tried, but something seems to be hosed permanently.
+ }
+ }
+ }
+};
+
+/**
+ * File appender
+ *
+ * Writes output to file using OS.File.
+ */
+function FileAppender(path, formatter) {
+ this._name = "FileAppender";
+ this._encoder = new TextEncoder();
+ this._path = path;
+ this._file = null;
+ this._fileReadyPromise = null;
+
+ // This is a promise exposed for testing/debugging the logger itself.
+ this._lastWritePromise = null;
+ Appender.call(this, formatter);
+}
+
+FileAppender.prototype = {
+ __proto__: Appender.prototype,
+
+ _openFile: function () {
+ return Task.spawn(function _openFile() {
+ try {
+ this._file = yield OS.File.open(this._path,
+ {truncate: true});
+ } catch (err) {
+ if (err instanceof OS.File.Error) {
+ this._file = null;
+ } else {
+ throw err;
+ }
+ }
+ }.bind(this));
+ },
+
+ _getFile: function() {
+ if (!this._fileReadyPromise) {
+ this._fileReadyPromise = this._openFile();
+ return this._fileReadyPromise;
+ }
+
+ return this._fileReadyPromise.then(_ => {
+ if (!this._file) {
+ return this._openFile();
+ }
+ });
+ },
+
+ doAppend: function (message) {
+ let array = this._encoder.encode(message);
+ if (this._file) {
+ this._lastWritePromise = this._file.write(array);
+ } else {
+ this._lastWritePromise = this._getFile().then(_ => {
+ this._fileReadyPromise = null;
+ if (this._file) {
+ return this._file.write(array);
+ }
+ });
+ }
+ },
+
+ reset: function () {
+ let fileClosePromise = this._file.close();
+ return fileClosePromise.then(_ => {
+ this._file = null;
+ return OS.File.remove(this._path);
+ });
+ }
+};
+
+/**
+ * Bounded File appender
+ *
+ * Writes output to file using OS.File. After the total message size
+ * (as defined by message.length) exceeds maxSize, existing messages
+ * will be discarded, and subsequent writes will be appended to a new log file.
+ */
+function BoundedFileAppender(path, formatter, maxSize=2*ONE_MEGABYTE) {
+ this._name = "BoundedFileAppender";
+ this._size = 0;
+ this._maxSize = maxSize;
+ this._closeFilePromise = null;
+ FileAppender.call(this, path, formatter);
+}
+
+BoundedFileAppender.prototype = {
+ __proto__: FileAppender.prototype,
+
+ doAppend: function (message) {
+ if (!this._removeFilePromise) {
+ if (this._size < this._maxSize) {
+ this._size += message.length;
+ return FileAppender.prototype.doAppend.call(this, message);
+ }
+ this._removeFilePromise = this.reset();
+ }
+ this._removeFilePromise.then(_ => {
+ this._removeFilePromise = null;
+ this.doAppend(message);
+ });
+ },
+
+ reset: function () {
+ let fileClosePromise;
+ if (this._fileReadyPromise) {
+ // An attempt to open the file may still be in progress.
+ fileClosePromise = this._fileReadyPromise.then(_ => {
+ return this._file.close();
+ });
+ } else {
+ fileClosePromise = this._file.close();
+ }
+
+ return fileClosePromise.then(_ => {
+ this._size = 0;
+ this._file = null;
+ return OS.File.remove(this._path);
+ });
+ }
+};
+
diff --git a/chrome/content/overlay.js b/chrome/content/overlay.js
new file mode 100644
index 0000000..29b9a07
--- /dev/null
+++ b/chrome/content/overlay.js
@@ -0,0 +1,52 @@
+"use strict";
+
+var topmenu = {
+ logger : null,
+ proxy : null,
+
+ setupLogging: function() {
+ Components.utils.import("chrome://topmenu/content/log4moz.js", topmenu);
+ var Log4Moz = this.Log4Moz;
+ var formatter = new Log4Moz.BasicFormatter();
+ var root = Log4Moz.repository.rootLogger;
+ root.level = Log4Moz.Level.Warn;
+
+ var capp = new Log4Moz.ConsoleAppender(formatter);
+ capp.level = Log4Moz.Level.Warn;
+ root.addAppender(capp);
+
+ /*
+ var dapp = new Log4Moz.DumpAppender(formatter);
+ dapp.level = Log4Moz.Level.Debug;
+ root.addAppender(dapp);
+ */
+
+ this.logger = Log4Moz.repository.getLogger("topmenu");
+ },
+
+ setupMenuProxy: function() {
+ Components.utils.import("chrome://topmenu/content/topmenuservice.js", topmenu);
+ this.proxy = topmenu.TopMenuService.createWindowProxy(window);
+ },
+
+ dispose: function() {
+ if (this.proxy) {
+ this.proxy.dispose();
+ this.proxy = null;
+ }
+ },
+
+ onLoad: function() {
+ window.removeEventListener('load', topmenu.onLoad);
+ window.addEventListener('unload', topmenu.onUnload);
+ topmenu.setupLogging();
+ topmenu.setupMenuProxy();
+ },
+
+ onUnload: function() {
+ window.removeEventListener('unload', topmenu.onUnload);
+ topmenu.dispose();
+ },
+}
+
+window.addEventListener('load', topmenu.onLoad);
diff --git a/chrome/content/overlay.xul b/chrome/content/overlay.xul
new file mode 100644
index 0000000..3e61323
--- /dev/null
+++ b/chrome/content/overlay.xul
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<overlay id="topmenu-overlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/x-javascript" src="chrome://topmenu/content/overlay.js"></script>
+</overlay>
diff --git a/chrome/content/topmenu-client.js b/chrome/content/topmenu-client.js
new file mode 100644
index 0000000..a5d7ab2
--- /dev/null
+++ b/chrome/content/topmenu-client.js
@@ -0,0 +1,27 @@
+var EXPORTED_SYMBOLS = [ "topmenu_client" ];
+
+const Cu = Components.utils;
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+Cu.import("resource://gre/modules/ctypes.jsm");
+Cu.import("chrome://topmenu/content/ctypes-utils.js");
+Cu.import("chrome://topmenu/content/glib.js");
+Cu.import("chrome://topmenu/content/gobject.js");
+Cu.import("chrome://topmenu/content/gdk.js");
+Cu.import("chrome://topmenu/content/gtk.js");
+
+function defines(lib) {
+ lib.lazy_bind("topmenu_client_connect_window_widget", ctypes.void_t, gdk.GdkWindow.ptr, gtk.GtkWidget.ptr);
+ lib.lazy_bind("topmenu_client_disconnect_window", ctypes.void_t, gdk.GdkWindow.ptr);
+
+ this.TopMenuAppMenuBar = gobject.GObject;
+ lib.lazy_bind("topmenu_app_menu_bar_new", this.TopMenuAppMenuBar.ptr);
+ lib.lazy_bind("topmenu_app_menu_bar_set_app_menu", ctypes.void_t, this.TopMenuAppMenuBar.ptr, gtk.GtkWidget.ptr);
+
+ this.TopMenuMonitor = gobject.GObject;
+ lib.lazy_bind("topmenu_monitor_get_instance", this.TopMenuMonitor.ptr);
+ lib.lazy_bind("topmenu_monitor_is_topmenu_available", glib.gboolean, this.TopMenuMonitor.ptr);
+}
+
+new ctypes_library("topmenu-client", [ 0 ], defines, this);
diff --git a/chrome/content/topmenuservice.js b/chrome/content/topmenuservice.js
new file mode 100644
index 0000000..ffafba8
--- /dev/null
+++ b/chrome/content/topmenuservice.js
@@ -0,0 +1,792 @@
+var EXPORTED_SYMBOLS = ["TopMenuService"];
+
+var Cu = Components.utils;
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+
+Cu.import("resource://gre/modules/ctypes.jsm");
+Cu.import("chrome://topmenu/content/log4moz.js");
+Cu.import("chrome://topmenu/content/glib.js");
+Cu.import("chrome://topmenu/content/gobject.js");
+Cu.import("chrome://topmenu/content/gdk.js");
+Cu.import("chrome://topmenu/content/gdk-pixbuf.js");
+Cu.import("chrome://topmenu/content/gtk.js");
+Cu.import("chrome://topmenu/content/topmenu-client.js");
+Cu.import("chrome://topmenu/content/vkgdkmap.js");
+
+var log = Log4Moz.repository.getLogger("topmenu.TopMenuService");
+
+/* Escape a string so that it is a valid regular expression literal. */
+function escapePreg(s) {
+ if (!s) return "";
+ var regex = new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\-]', 'g');
+ return s.replace(regex, "\\$&");
+}
+
+function getBaseWindowInterface(w) {
+ return w.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShellTreeItem)
+ .treeOwner
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIBaseWindow);
+}
+
+function getTopLevelGdkWindow(w) {
+ var baseWindow = getBaseWindowInterface(w);
+ var handle = baseWindow.nativeHandle;
+ var gdkWindow = new gdk.GdkWindow.ptr(ctypes.UInt64(handle));
+ return gdk.gdk_window_get_toplevel(gdkWindow);
+}
+
+function createMutationObserver(window, proxy)
+{
+ return new window.MutationObserver(function(mutations) {
+ mutations.forEach(function(mutation) {
+ proxy.handleMutation(mutation);
+ });
+ });
+}
+
+function WindowProxy(window) {
+ this.srcWindow = window;
+ this.srcMenuBar = null;
+ this.observer = null;
+ this.appMenuBar = null;
+ this.gdkWindow = getTopLevelGdkWindow(window);
+ this.accelGroup = null;
+
+ this.flags = {};
+ this.updateFlags();
+
+ var self = this;
+ this.lastCallbackId = 0;
+ this.callbackItems = {};
+ this.callbacks = {
+ activate : gtk.GtkMenuItemActivate.ptr(function (gtkItem, data) {
+ try {
+ var id = ctypes.cast(data, ctypes.uintptr_t).value;
+ var item = self.callbackItems[id];
+ self.activateItem(item);
+ } catch (ex) {
+ Cu.reportError(ex);
+ log.error("Exception in callback: " + ex.toString());
+ }
+ }),
+ select : gtk.GtkItemSelect.ptr(function (gtkItem, data) {
+ try {
+ var id = ctypes.cast(data, ctypes.uintptr_t).value;
+ var item = self.callbackItems[id];
+ self.selectItem(item);
+ } catch (ex) {
+ Cu.reportError(ex);
+ log.error("Exception in callback: " + ex.toString());
+ }
+ }),
+ deselect : gtk.GtkItemDeselect.ptr(function (gtkItem, data) {
+ try {
+ var id = ctypes.cast(data, ctypes.uintptr_t).value;
+ var item = self.callbackItems[id];
+ self.deselectItem(item);
+ } catch (ex) {
+ Cu.reportError(ex);
+ log.error("Exception in callback: " + ex.toString());
+ }
+ }),
+ monitor_available : gobject.GObjectNotifyCallback.ptr(function (obj, pspec, data) {
+ self.updateMenuBarVisibility();
+ }),
+ keypress : function(e) {
+ try {
+ self.onKeyPress(e);
+ } catch (ex) {
+ Cu.reportError(ex);
+ log.error("Exception in callback: " + ex.toString());
+ }
+ }
+ }
+
+ this.accelGroup = gobject.ref_sink(gtk.gtk_accel_group_new());
+
+ this.appMenuBar = gobject.ref_sink(topmenu_client.topmenu_app_menu_bar_new());
+ gtk.gtk_widget_show(this.appMenuBar);
+
+ topmenu_client.topmenu_client_connect_window_widget(this.gdkWindow, this.appMenuBar);
+
+ var menubars = window.document.getElementsByTagNameNS('http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul', 'menubar');
+ if (menubars.length > 0) {
+ this.srcMenuBar = menubars[0];
+ this.observer = createMutationObserver(window, this);
+ this.observer.observe(this.srcMenuBar, {
+ childList: true,
+ attributes: true,
+ subtree: true
+ });
+
+ // Let's find currently added menu items and proxy them
+ this.srcMenuBar.topmenuData = {
+ gtkMenu: this.appMenuBar
+ }
+
+ for (var item = this.srcMenuBar.firstChild; item; item = item.nextSibling) {
+ this.addItem(item, this.srcMenuBar, -1);
+ }
+
+ this.appMenu = this.buildAppMenu();
+ if (this.appMenu) {
+ topmenu_client.topmenu_app_menu_bar_set_app_menu(this.appMenuBar, this.appMenu);
+ }
+
+ window.document.addEventListener("keypress", this.callbacks.keypress);
+ }
+
+ this.monitor = topmenu_client.topmenu_monitor_get_instance();
+ this.monitorConnectionId = gobject.signal_connect(this.monitor, "notify::available",
+ this.callbacks.monitor_available, null);
+ this.updateMenuBarVisibility();
+}
+
+WindowProxy.prototype.updateMenuBarVisibility = function() {
+ var topmenuAvailable = topmenu_client.topmenu_monitor_is_topmenu_available(this.monitor);
+ if (this.srcMenuBar) {
+ this.srcMenuBar.parentNode.parentNode.hidden = topmenuAvailable;
+ }
+}
+
+WindowProxy.prototype.buildAppMenuItem = function(itemId, stockId)
+{
+ var item = this.srcWindow.document.getElementById(itemId);
+
+ if (item) {
+ var gtkItem = gtk.gtk_image_menu_item_new_from_stock(stockId, null);
+ gtk.gtk_widget_show(gtkItem);
+
+
+ var callbackId = this.lastCallbackId++;
+ this.callbackItems[callbackId] = item;
+
+ gobject.signal_connect(gtkItem, 'activate',
+ this.callbacks.activate,
+ glib.gpointer(callbackId));
+
+ return gtkItem;
+ } else {
+ return null;
+ }
+}
+
+WindowProxy.prototype.buildAppMenu = function() {
+ var menu = gobject.ref_sink(gtk.gtk_menu_new());
+ var item;
+
+ item = this.buildAppMenuItem('aboutName', 'gtk-about');
+ if (item) {
+ gtk.gtk_menu_shell_append(menu, item);
+ }
+
+ item = this.buildAppMenuItem('menu_preferences', 'gtk-preferences');
+ if (item) {
+ gtk.gtk_menu_shell_append(menu, item);
+ }
+
+ item = gtk.gtk_separator_menu_item_new();
+ gtk.gtk_widget_show(item);
+ gtk.gtk_menu_shell_append(menu, item);
+
+ item = this.buildAppMenuItem('menu_FileQuitItem', 'gtk-quit');
+ if (item) {
+ gtk.gtk_menu_shell_append(menu, item);
+ }
+
+ return menu;
+}
+
+WindowProxy.prototype.handleMutation = function(mutation) {
+ var target = mutation.target;
+
+ switch (mutation.type) {
+ case 'childList':
+ switch (target.tagName) {
+ case 'menupopup':
+ // Get the parent menu for this menupopup:
+ target = mutation.target.parentNode;
+
+ if ('topmenuData' in target) {
+ this.updateMenu(target, mutation.previousSibling, mutation.removedNodes, mutation.addedNodes);
+ } else {
+ log.warn("I do not know about this menupoup");
+ }
+ break;
+ case 'menubar':
+ // Changes in the root menu bar.
+ if ('topmenuData' in target) {
+ this.updateMenu(target, mutation.previousSibling, mutation.removedNodes, mutation.addedNodes);
+ } else {
+ log.warn("I do not know about this menubar");
+ }
+ }
+ break;
+ case 'attributes':
+ switch (target.tagName) {
+ case 'menupopup':
+ case 'menuitem':
+ case 'menu':
+ if ('topmenuData' in target) {
+ this.updateItem(mutation.target, mutation.attributeName);
+ }
+ break;
+ case 'command':
+ if (mutation.attributeName === 'disabled' && 'topmenuData' in target) {
+ target.topmenuData.menuitems.forEach(function(item) {
+ this.updateItem(item, 'disabled');
+ }, this);
+ }
+ }
+ break;
+ }
+}
+
+WindowProxy.prototype.updateFlags = function() {
+ var prefs = Cc["@mozilla.org/preferences-service;1"].
+ getService(Ci.nsIPrefService).getBranch("");
+ this.flags.accelKey = prefs.getIntPref("ui.key.accelKey");
+ this.flags.alwaysAppendAccessKeys =
+ prefs.getComplexValue("intl.menuitems.alwaysappendaccesskeys",
+ Ci.nsIPrefLocalizedString).data == "true";
+ this.flags.insertSeparatorBeforeAccessKeys =
+ prefs.getComplexValue(
+ "intl.menuitems.insertseparatorbeforeaccesskeys",
+ Ci.nsIPrefLocalizedString).data == "true";
+ this.flags.ellipsis = prefs.getComplexValue("intl.ellipsis",
+ Ci.nsIPrefLocalizedString).data;
+}
+
+/* Convert from XUL mnemonic format to Gtk+ */
+/* https://developer.mozilla.org/En/XUL_Tutorial/Accesskey_display_rules */
+WindowProxy.prototype.createLabelWithAccessKey = function(label, accesskey) {
+ label = label.replace(/_/g, "__");
+ if (accesskey) {
+ var regex = new RegExp(escapePreg(accesskey), "i");
+ if (!this.flags.alwaysAppendAccessKeys && label.search(regex) >= 0) {
+ /* "Inline" access key (ex. "_File") */
+ return label.replace(regex, "_$&");
+ } else {
+ /* Appended access key (ex. "File (_A)") */
+ if (label.slice(-this.flags.ellipsis.length) == this.flags.ellipsis) {
+ /* Label ends with ellipsis */
+ return label.substring(0, label.length-this.flags.ellipsis.length) +
+ (this.flags.insertSeparatorBeforeAccessKeys ? " " : "") +
+ "(_" + accesskey.toUpperCase() + ")" +
+ this.flags.ellipsis;
+ } else {
+ return label +
+ (this.flags.insertSeparatorBeforeAccessKeys ? " " : "") +
+ "(_" + accesskey.toUpperCase() + ")";
+ }
+ }
+ } else {
+ /* No access key */
+ return label;
+ }
+}
+
+WindowProxy.prototype.createImageWidgetFromImage = function(image, rect) {
+ var canvas = this.srcWindow.document.createElementNS("http://www.w3.org/1999/xhtml","canvas");
+ var ctx = canvas.getContext('2d');
+
+ if (!rect[2]) rect[2] = image.width;
+ if (!rect[3]) rect[3] = image.height;
+
+ ctx.drawImage(image, rect[0], rect[1], rect[2], rect[3], 0, 0, 16, 16);
+
+ var img_data = ctx.getImageData(0,0, 16, 16);
+ var pix_data = ctypes.cast(ctypes.uint8_t.array()(img_data.data).address(), glib.guchar.ptr);
+ var pixbuf = gobject.ref_sink(gdk_pixbuf.gdk_pixbuf_new_from_data(pix_data, gdk_pixbuf.GDK_COLORSPACE_RGB, true, 8, 16, 16, 16 * 4, null, null));
+ var pixbuf_copy = gobject.ref_sink(gdk_pixbuf.gdk_pixbuf_copy(pixbuf));
+
+ var gtkImage = gtk.gtk_image_new_from_pixbuf(pixbuf_copy);
+
+ pixbuf.dispose();
+ pixbuf_copy.dispose();
+
+ return gtkImage;
+}
+
+WindowProxy.prototype.createImageWidget = function(item, style) {
+ var image_uri = item.getAttribute("image");
+ var res;
+
+ if (!image_uri) {
+ var style_regex = /url\("(.*?)"\)/;
+ var image_style = style.getPropertyValue("list-style-image");
+ res = style_regex.exec(image_style);
+ if (res) {
+ image_uri = res[1];
+ }
+ }
+
+ if (image_uri) {
+ /* Test if it is a Gtk stock image first */
+ var mozicon_regex = /moz-icon:\/\/stock\/(.*?)\?/;
+ res = mozicon_regex.exec(image_uri);
+ if (res) {
+ return gtk.gtk_image_new_from_stock(res[1], gtk.GTK_ICON_SIZE_MENU);
+ }
+
+ /* Handle image clipping */
+ var rect = [0, 0, 0, 0];
+ var region = style.getPropertyValue("-moz-image-region");
+ if (region && region !== "auto") {
+ var region_regex = /rect\((\d+)px, (\d+)px, (\d+)px, (\d+)px\)/;
+ var values = region_regex.exec(region);
+ if (values) {
+ rect[0] = parseInt(values[4]); /* Left */
+ rect[1] = parseInt(values[1]); /* Top */
+ rect[2] = parseInt(values[2]) - rect[0]; /* Right - Left*/
+ rect[3] = parseInt(values[3]) - rect[1]; /* Bottom - Top */
+ }
+ }
+
+ var img_elem = new this.srcWindow.Image();
+ img_elem.src = image_uri;
+
+ if (img_elem.complete) {
+ return this.createImageWidgetFromImage(img_elem, rect);
+ } else {
+ // Set a handler to try again when the image is loaded
+ var self = this;
+ img_elem.onload = function() {
+ self.updateItem(item, "image");
+ }
+ item.topmenuImgLoader = img_elem;
+ return "pending";
+ }
+ }
+
+ return null;
+}
+
+WindowProxy.prototype.addAccelerator = function(gtkItem, item) {
+ var keyId = item.getAttribute("key");
+ if (!keyId) return false;
+ var keyItem = this.srcWindow.document.getElementById(keyId);
+ if (!keyItem) return false;
+
+ var keyItemMods = keyItem.getAttribute("modifiers");
+ var gtkMods = 0;
+
+ if (keyItemMods) {
+ keyItemMods = keyItemMods.split(/[\s,]+/);
+ for (i in keyItemMods) {
+ var mod = keyItemMods[i];
+ if (mod === "accel") {
+ /* Read platform default accelerator key from prefs */
+ switch (this.flags.accelKey) {
+ case this.srcWindow.KeyEvent.DOM_VK_SHIFT:
+ mod = "shift";
+ break;
+ case this.srcWindow.KeyEvent.DOM_VK_CONTROL:
+ mod = "control";
+ break;
+ case this.srcWindow.KeyEvent.DOM_VK_ALT:
+ mod = "alt";
+ break;
+ case this.srcWindow.KeyEvent.DOM_VK_META:
+ mod = "meta";
+ break;
+ }
+ }
+ switch (mod) {
+ case "shift":
+ gtkMods += gdk.GDK_SHIFT_MASK;
+ break;
+ case "alt":
+ gtkMods += gdk.GDK_ALT_MASK;
+ break;
+ case "meta":
+ gtkMods += gdk.GDK_META_MASK;
+ break;
+ case "control":
+ gtkMods += gdk.GDK_CONTROL_MASK;
+ break;
+ default:
+ continue;
+ }
+ }
+ }
+
+ var keyItemKey = keyItem.getAttribute("key");
+ var keyItemCode = keyItem.getAttribute("keycode");
+ var gtkKey = 0;
+
+ if (keyItemKey) {
+ gtkKey = gdk.gdk_unicode_to_keyval(keyItemKey.charCodeAt(0));
+ } else if (keyItemCode) {
+ gtkKey = vkgdkmap[keyItemCode];
+ if (!gtkKey) {
+ log.info("Keycode " + keyItemCode + " is unmapped");
+ }
+ }
+
+ if (gtkKey) {
+ gtk.gtk_widget_add_accelerator(gtkItem, "activate", this.accelGroup,
+ gtkKey, gtkMods, gtk.GTK_ACCEL_VISIBLE);
+ }
+}
+
+WindowProxy.prototype.addItem = function(item, menu, position) {
+ var disabled = item.disabled;
+ var style = this.srcWindow.getComputedStyle(item, null);
+ var label = this.createLabelWithAccessKey(item.getAttribute('label'), item.accessKey);
+ var visible = !item.hidden && !item.collapsed && style.display !== 'none';
+ var hasSubMenu = item.tagName === "menu" && item.firstChild
+ && item.firstChild.tagName === "menupopup";
+
+ var gtkItem;
+ if (item.tagName === "menuseparator") {
+ gtkItem = gobject.ref_sink(gtk.gtk_separator_menu_item_new());
+ } else if (item.tagName === "menu") {
+ gtkItem = gobject.ref_sink(gtk.gtk_menu_item_new_with_mnemonic(label));
+ } else if (item.tagName === "menuitem") {
+ var image = this.createImageWidget(item, style);
+ if (image) {
+ gtkItem = gobject.ref_sink(gtk.gtk_image_menu_item_new_with_mnemonic(label));
+ if (image !== "pending") {
+ gtk.gtk_image_menu_item_set_image(gtkItem, image);
+ }
+ } else {
+ var type = item.getAttribute("type");
+ var checked = item.getAttribute("checked") ? true : false;
+ switch (type) {
+ case "checkbox":
+ gtkItem = gobject.ref_sink(gtk.gtk_check_menu_item_new_with_mnemonic(label));
+ gtk.gtk_check_menu_item_set_active(gtkItem, checked);
+ break;
+ case "radio":
+ gtkItem = gobject.ref_sink(gtk.gtk_check_menu_item_new_with_mnemonic(label));
+ gtk.gtk_check_menu_item_set_draw_as_radio(gtkItem, true);
+ gtk.gtk_check_menu_item_set_active(gtkItem, checked);
+ break;
+ default:
+ gtkItem = gobject.ref_sink(gtk.gtk_menu_item_new_with_mnemonic(label));
+ }
+ }
+
+ var command = item.getAttribute('command');
+ if (command) {
+ command = this.srcWindow.document.getElementById(command);
+ if (command) {
+ if (!('topmenuData' in command)) {
+ command.topmenuData = {
+ menuitems: [item]
+ };
+ } else {
+ command.topmenuData.menuitems.push(item);
+ }
+
+ this.observer.observe(command, {attributes: true});
+
+ if (command.getAttribute('disabled')) {
+ disabled = true;
+ }
+ }
+ }
+
+ if (item.hasAttribute("key")) {
+ try {
+ this.addAccelerator(gtkItem, item);
+ } catch (ex) {
+ log.warn(ex);
+ }
+ }
+ } else {
+ log.warn("Unknown tagName: " + item.tagName);
+ return;
+ }
+
+ gtk.gtk_widget_set_sensitive(gtkItem, !disabled);
+ gtk.gtk_widget_set_visible(gtkItem, visible);
+
+ item.topmenuData = {
+ gtkItem : gtkItem,
+ callbackId: this.lastCallbackId++
+ };
+ this.callbackItems[item.topmenuData.callbackId] = item;
+
+ if (hasSubMenu) {
+ var gtkSubMenu = this.addMenu(item);
+ gtk.gtk_menu_item_set_submenu(gtkItem, gtkSubMenu);
+ }
+
+ var gtkMenu = menu.topmenuData.gtkMenu;
+ if (position >= 0) {
+ gtk.gtk_menu_shell_insert(gtkMenu, gtkItem, position);
+ } else {
+ gtk.gtk_menu_shell_append(gtkMenu, gtkItem);
+ }
+
+ var conn_id;
+ if (hasSubMenu) {
+ conn_id = gobject.signal_connect(gtkItem, 'select',
+ this.callbacks.select,
+ glib.gpointer(item.topmenuData.callbackId));
+ item.topmenuData.selectConnectionId = conn_id;
+
+ conn_id = gobject.signal_connect(gtkItem, 'deselect',
+ this.callbacks.deselect,
+ glib.gpointer(item.topmenuData.callbackId));
+ item.topmenuData.deselectConnectionId = conn_id;
+ } else {
+ conn_id = gobject.signal_connect(gtkItem, 'activate',
+ this.callbacks.activate,
+ glib.gpointer(item.topmenuData.callbackId));
+ item.topmenuData.activateConnectionId = conn_id;
+ }
+
+ return gtkItem;
+}
+
+WindowProxy.prototype.addMenu = function(menu) {
+ var gtkMenu = gobject.ref_sink(gtk.gtk_menu_new());
+
+ menu.topmenuData.gtkMenu = gtkMenu;
+
+ var popup = menu.firstChild;
+ for (var item = popup.firstChild; item; item = item.nextSibling) {
+ this.addItem(item, menu, -1);
+ }
+
+ return gtkMenu;
+}
+
+WindowProxy.prototype.removeItem = function(item) {
+ var hasSubMenu = item.tagName === "menu" && item.firstChild
+ && item.firstChild.tagName === "menupopup";
+
+ if (hasSubMenu) {
+ this.removeMenu(item);
+ }
+
+ delete this.callbackItems[item.topmenuData.callbackId];
+
+ var gtkItem = item.topmenuData.gtkItem;
+
+ gtk.gtk_widget_destroy(gtkItem);
+ gtkItem.forget();
+
+ delete item.topmenuData;
+}
+
+WindowProxy.prototype.removeMenu = function(menu) {
+ var gtkMenu = menu.topmenuData.gtkMenu;
+
+ gtk.gtk_widget_destroy(gtkMenu);
+ gtkMenu.forget();
+}
+
+WindowProxy.prototype.updateItem = function(item, changedAttr) {
+ var gtkItem = item.topmenuData.gtkItem;
+ var style = this.srcWindow.getComputedStyle(item, null);
+
+ switch (changedAttr) {
+ case "accesskey":
+ case "label":
+ var label = this.createLabelWithAccessKey(item.getAttribute('label'), item.accessKey);
+ gtk.gtk_menu_item_set_label(gtkItem, label);
+ break;
+ case "disabled":
+ var disabled = item.disabled;
+ var command = item.getAttribute('command');
+ if (!disabled && command) {
+ command = this.srcWindow.document.getElementById(command);
+ if (command) {
+ if (command.getAttribute('disabled')) {
+ disabled = true;
+ }
+ }
+ }
+
+ gtk.gtk_widget_set_sensitive(gtkItem, !disabled);
+ break;
+ case "hidden":
+ case "collapsed":
+ var visible = !item.hidden && !item.collapsed && style.display !== 'none';
+ gtk.gtk_widget_set_visible(gtkItem, visible);
+ break;
+ case "image":
+ var image = this.createImageWidget(item, style);
+ if (image !== 'pending') {
+ delete item.topmenuImgLoader;
+ gtk.gtk_image_menu_item_set_image(gtkItem, image);
+ }
+ break;
+ }
+}
+
+WindowProxy.prototype.updateMenu = function(menu, prevItem, removedItems, addedItems) {
+ var position, i, node;
+ var popup = menu.firstChild;
+
+ if (removedItems.length > 0) {
+ for (i = 0; i < removedItems.length; i++) {
+ this.removeItem(removedItems[i]);
+ }
+ }
+
+ if (addedItems.length > 0) {
+ if (prevItem) {
+ position = -1;
+ i = 2;
+ for (node = popup.firstChild; node; node = node.nextSibling, i++) {
+ if (node === prevItem) {
+ position = i;
+ break;
+ }
+ }
+ if (position < 0) {
+ log.warn("prevItem node not found");
+ position = 0;
+ }
+ } else {
+ position = 0;
+ }
+
+ for (i = 0; i < addedItems.length; i++) {
+ this.addItem(addedItems[i], menu, position);
+ }
+ }
+}
+
+WindowProxy.prototype.activateItem = function(item) {
+ this.fakeCommandEvent(item);
+}
+
+WindowProxy.prototype.selectItem = function(item) {
+ var menu = item;
+ var popup = menu.firstChild;
+ if (popup.tagName !== 'menupopup') {
+ log.warn("Selected a menu without a menupopup");
+ return;
+ }
+
+ this.fakePopupEvent(popup, "popupshowing");
+ menu.open = "true";
+
+ // A hack for Firefox and friends
+ if (menu.id === 'edit-menu' && 'gEditUIVisible' in this.srcWindow) {
+ this.srcWindow.gEditUIVisible = true;
+ this.srcWindow.goUpdateGlobalEditMenuItems();
+ }
+
+ // Let's update the label of every item at least.
+ for (var i = popup.firstChild; i; i = i.nextSibling) {
+ switch (i.tagName) {
+ case 'menu':
+ case 'menuitem':
+ this.updateItem(i, 'label');
+ this.updateItem(i, 'hidden');
+ break;
+ case 'menuseparator':
+ this.updateItem(i, 'hidden');
+ break;
+ }
+ }
+
+ this.fakePopupEvent(popup, "popupshown");
+}
+
+WindowProxy.prototype.deselectItem = function(item) {
+ var menu = item;
+ var popup = menu.firstChild;
+ if (popup.tagName !== 'menupopup') {
+ log.warn("Selected a menu without a menupopup");
+ return;
+ }
+
+ this.fakePopupEvent(popup, "popuphiding");
+ menu.removeAttribute('open');
+
+ if (menu.id === 'edit-menu' && 'gEditUIVisible' in this.srcWindow) {
+ this.srcWindow.gEditUIVisible = false;
+ }
+
+ this.fakePopupEvent(popup, "popuphidden");
+}
+
+WindowProxy.prototype.onKeyPress = function(event) {
+ if (!event.altKey) return;
+ if (!event.which) return;
+
+ var char = String.fromCharCode(event.which).toLowerCase();
+ if (!char) return;
+
+ for (var item = this.srcMenuBar.firstChild; item; item = item.nextSibling) {
+ if ('topmenuData' in item &&
+ item.getAttribute('accesskey').toLowerCase() === char) {
+ event.preventDefault();
+ event.stopImmediatePropagation();
+ gtk.gtk_widget_mnemonic_activate(item.topmenuData.gtkItem, false);
+ }
+ }
+}
+
+WindowProxy.prototype.fakePopupEvent = function(popup, type) {
+ var window = this.srcWindow;
+ var popupEvent = window.document.createEvent("MouseEvent");
+ popupEvent.initMouseEvent(type, true, true, window,
+ 0, 0, 0, 0, 0, false, false, false, false, 0, null);
+ popup.dispatchEvent(popupEvent);
+}
+
+WindowProxy.prototype.fakeActiveEvent = function(item, type) {
+ var window = this.srcWindow;
+ var activeEvent = window.document.createEvent("Events");
+ activeEvent.initEvent(type, true, false);
+ item.dispatchEvent(activeEvent);
+}
+
+WindowProxy.prototype.fakeCommandEvent = function(item) {
+ var window = this.srcWindow;
+ var commandEvent = window.document.createEvent("XULCommandEvent");
+ commandEvent.initCommandEvent("command", true, true, window,
+ 0, false, false, false, false, null);
+ item.dispatchEvent(commandEvent);
+}
+
+WindowProxy.prototype.dispose = function() {
+ window.document.removeEventListener("keypress", this.callbacks.keypress);
+ if (this.monitor) {
+ if (this.monitorConnectionId) {
+ gobject.g_signal_handler_disconnect(this.monitor, this.monitorConnectionId);
+ }
+ this.monitorConnectionId = 0;
+ this.monitor = null;
+ }
+ if (this.observer) {
+ this.observer.disconnect();
+ this.observer = null;
+ }
+ if (this.appMenuBar) {
+ this.appMenuBar.dispose();
+ this.appMenuBar = null;
+ }
+ if (this.appMenu) {
+ this.appMenu.dispose();
+ this.appMenu = null;
+ }
+ if (this.accelGroup) {
+ this.accelGroup.dispose();
+ this.accelGroup = null;
+ }
+
+ this.gdkWindow = null;
+ this.srcWindow = null;
+ this.menus = null;
+ this.items = null;
+}
+
+var TopMenuService = {
+ createWindowProxy: function(window) {
+ return new WindowProxy(window);
+ }
+}
diff --git a/chrome/content/vkgdkmap.js b/chrome/content/vkgdkmap.js
new file mode 100644
index 0000000..35a8fc5
--- /dev/null
+++ b/chrome/content/vkgdkmap.js
@@ -0,0 +1,27 @@
+var EXPORTED_SYMBOLS = [ "vkgdkmap" ];
+
+const Cu = Components.utils;
+
+Cu.import("chrome://topmenu/content/gdk.js");
+
+/* Hardcoding DOM_VK_* constants, how ugly. */
+/* But how to get a window scope from here? Need to access KeyEvent. */
+
+vkgdkmap = {
+ VK_F1 : gdk.GDK_KEY_F1,
+ VK_F2 : gdk.GDK_KEY_F2,
+ VK_F3 : gdk.GDK_KEY_F3,
+ VK_F4 : gdk.GDK_KEY_F4,
+ VK_F5 : gdk.GDK_KEY_F5,
+ VK_F6 : gdk.GDK_KEY_F6,
+ VK_F7 : gdk.GDK_KEY_F7,
+ VK_F8 : gdk.GDK_KEY_F8,
+ VK_F9 : gdk.GDK_KEY_F9,
+ VK_F10 : gdk.GDK_KEY_F10,
+ VK_F11 : gdk.GDK_KEY_F11,
+ VK_F12 : gdk.GDK_KEY_F12,
+
+ VK_TAB : gdk.GDK_KEY_Tab,
+ VK_DELETE : gdk.GDK_KEY_Delete
+
+}
diff --git a/install.rdf b/install.rdf
new file mode 100644
index 0000000..11d48d3
--- /dev/null
+++ b/install.rdf
@@ -0,0 +1,39 @@
+<?xml version="1.0"?>
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+ <Description about="urn:mozilla:install-manifest">
+ <em:id>topmenu.mozilla@javispedro.com</em:id>
+ <em:version>0.1</em:version>
+ <em:type>2</em:type>
+
+ <em:targetApplication>
+ <Description>
+ <!-- Mozilla Firefox -->
+ <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
+ <em:minVersion>21.0</em:minVersion>
+ <em:maxVersion>28.*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ <em:targetApplication>
+ <Description>
+ <!-- Mozilla Thunderbird -->
+ <em:id>{3550f703-e582-4d05-9a08-453d09bdfdc6}</em:id>
+ <em:minVersion>21.0</em:minVersion>
+ <em:maxVersion>28.*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ <em:targetApplication>
+ <Description>
+ <!-- Zotero -->
+ <em:id>zotero@chnm.gmu.edu</em:id>
+ <em:minVersion>2.1a1</em:minVersion>
+ <em:maxVersion>4.0.*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>TopMenu bridge</em:name>
+ <em:description>Moves the Firefox menubar to the TopMenu applet</em:description>
+ <em:creator>javispedro</em:creator>
+ <em:homepageURL>http://gitorious.org/firefox-gnome-globalmenu/firefox-gnome-globalmenu</em:homepageURL>
+ </Description>
+</RDF>
diff --git a/topmenu-mozilla.config b/topmenu-mozilla.config
new file mode 100644
index 0000000..8cec188
--- /dev/null
+++ b/topmenu-mozilla.config
@@ -0,0 +1 @@
+// ADD PREDEFINED MACROS HERE!
diff --git a/topmenu-mozilla.creator b/topmenu-mozilla.creator
new file mode 100644
index 0000000..e94cbbd
--- /dev/null
+++ b/topmenu-mozilla.creator
@@ -0,0 +1 @@
+[General]
diff --git a/topmenu-mozilla.files b/topmenu-mozilla.files
new file mode 100644
index 0000000..65ebee1
--- /dev/null
+++ b/topmenu-mozilla.files
@@ -0,0 +1,15 @@
+chrome.manifest
+install.rdf
+Makefile
+chrome/content/overlay.xul
+chrome/content/overlay.js
+chrome/content/log4moz.js
+chrome/content/ctypes-utils.js
+chrome/content/topmenuservice.js
+chrome/content/glib.js
+chrome/content/gobject.js
+chrome/content/topmenu-client.js
+chrome/content/gtk.js
+chrome/content/gdk.js
+chrome/content/gdk-pixbuf.js
+chrome/content/vkgdkmap.js
diff --git a/topmenu-mozilla.includes b/topmenu-mozilla.includes
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/topmenu-mozilla.includes