[dtrace-discuss] Running DTrace Javascript provider on nevada

John Rice John.Rice at Sun.COM
Tue Jan 9 07:59:09 PST 2007


Attaching "file:///usr/lib/firefox/components/nsUrlClassifierLib.js" 
with a .txt extension to keep the filters happy :)

JR

John Rice wrote:
> Ok - Brendan well a few of us debugged this a bit more using pstack, 
> truss, dtruss and dbx. Firefox 2.0 appears to be hanging in js_GC, 
> down in the bowels after a call to JS_CompileScriptForPrincipals( ) 
> for the following script, 
> "file:///usr/lib/firefox/components/nsUrlClassifierLib.js".
>
> I've attached the various debug traces for you and the script to have 
> a butchers at.
>
> Any help much appreciated.
>
> JR
>
>
> John Rice wrote:
>> Brendan - no joy :(
>>
>> Did exactly as you suggested below on nevada b53 using version 0.10 
>> of helper monkey and still getting a hang:
>>
>> $ uname -a
>> SunOS jmrlaptop 5.11 snv_53 i86pc i386 i86pc
>>
>> Without setting up the exports:
>> $ /usr/lib/firefox/firefox-bin -UILocale C -contentLocale c --> 
>> Firefox 2.0 launches fine
>>
>> Setting up exports:
>> $ cd~/helper-monkey-0.10/lib
>> $ export LD_PRELOAD=/usr/lib/firefox/libmozz.so:./libmozjs.so
>> $ export LD_LIBRARY_PATH=/usr/lib/mps:/usr/lib/firefox $ export 
>> MOZ_PLUGIN_PATH=/usr/lib/firefox/plugins
>> $ /usr/lib/firefox/firefox-bin -UILocale C -contentLocale c --> 
>> Firefox 2.0 hangs
>>
>> Any ideas?
>>
>> JR
>>
>>
>> Brendan Gregg - Sun Microsystems wrote:
>>> G'Day John,
>>>
>>> On Mon, Jan 08, 2007 at 05:02:21PM +0000, John Rice wrote:
>>>  
>>>> Brendan - I'd like to try these out:
>>>>
>>>> http://blogs.sun.com/brendan/entry/dtrace_meets_javascript
>>>> http://blogs.sun.com/brendan/resource/helper-monkey-0.10.tar.gz
>>>>
>>>> I'm running nevada b53 at the minute with Firefox 2.0 installed by 
>>>> default. So I grabbed an old tarball of Firefox 1.5.0.4, unpacked 
>>>> it and was able to bring up firefox-bin 1.5.0.4.
>>>> http://releases.mozilla.org/pub/mozilla.org/firefox/releases/1.5.0.4/contrib/solaris_tar_ball/firefox-1.5.0.4.en-US.solaris2.10-i386.tar.bz2 
>>>>
>>>>
>>>> I then setup the LD_PRELOAD and LD_LIBRARY_PATH to point to this 
>>>> unpacked tarball dir and fired up firefox-bin. I could then run 
>>>> dtrace -l javascript*:::* and list out the probes, but firefox 
>>>> failed to display.
>>>>     
>>>
>>> Hmm, I tried the same and also had firefox-bin hang on startup -- 
>>> and yet
>>> running the system shipped build of firefox 1.5.0.4 worked fine. I
>>> thought it may have been a path issue, but I haven't yet found the fix.
>>>
>>> However, if you are on b53 then you may have firefox 2.0, which I just
>>> ran with that version of HelperMonkey. So far it seems to be working 
>>> fine.
>>> I used the following while in the directory with libmozjs.so:
>>>
>>>    $ export LD_PRELOAD=/usr/lib/firefox/libmozz.so:./libmozjs.so
>>>    $ export LD_LIBRARY_PATH=/usr/lib/mps:/usr/lib/firefox     $ 
>>> export MOZ_PLUGIN_PATH=/usr/lib/firefox/plugins
>>>    $ /usr/lib/firefox/firefox-bin -UILocale C -contentLocale c
>>>
>>> Hopefully that will be happier.
>>>
>>> I suspect firefox 1.5.0.4 was hanging while trying to do the auto 
>>> update,
>>> so another workaround may be to skip that (somehow)...
>>>
>>> cheers,
>>>
>>> Brendan
>>>
>>>   
>>
>> _______________________________________________
>> dtrace-discuss mailing list
>> dtrace-discuss at opensolaris.org
>
> ------------------------------------------------------------------------
>
> Javascript Dtrace probes testing on nevada b53 with Firefox 2.0
> Jan 9th 2007
> --------------
>
> Setup:
> http://blogs.sun.com/brendan/resource/helper-monkey-0.10.tar.gz
>
> $ cd ~/Documents/dtest/helper-monkey-0.10/lib
> $ export LD_PRELOAD=/usr/lib/firefox/libmozz.so:\
> /export/home/jr140578/Documents/dtest/helper-monkey-0.10/lib/libmozjs.so
> $ export LD_LIBRARY_PATH=/usr/lib/mps:/usr/lib/firefox
> $ export MOZ_PLUGIN_PATH=/usr/lib/firefox/plugins
>
> Running:
> $ /usr/lib/firefox/firefox-bin -UILocale C -contentLocale c
>
>
> ===============================
> Firefox essentially hung at this point but can list javascript dtrace probes:
> # dtrace -ln javascript*:::*
>   ID   PROVIDER            MODULE                          FUNCTION NAME
> 53946 javascript2097       libmozjs.so             jsdtrace_execute_done execute-done
> 53947 javascript2097       libmozjs.so                        js_Execute execute-done
> 53948 javascript2097       libmozjs.so            jsdtrace_execute_start execute-start
> 53949 javascript2097       libmozjs.so                        js_Execute execute-start
> 53950 javascript2097       libmozjs.so           jsdtrace_function_entry function-entry
> 53951 javascript2097       libmozjs.so                      js_Interpret function-entry
> 53952 javascript2097       libmozjs.so          jsdtrace_function_return function-return
> 53953 javascript2097       libmozjs.so                      js_Interpret function-return
> 53954 javascript2097       libmozjs.so            jsdtrace_object_create object-create
> 53955 javascript2097       libmozjs.so                      js_NewObject object-create
> 53956 javascript2097       libmozjs.so       jsdtrace_object_create_done object-create-done
> 53957 javascript2097       libmozjs.so                      js_NewObject object-create-done
> 53958 javascript2097       libmozjs.so      jsdtrace_object_create_start object-create-start
> 53959 javascript2097       libmozjs.so                      js_NewObject object-create-start
> 53960 javascript2097       libmozjs.so          jsdtrace_object_finalize object-finalize
> 53961 javascript2097       libmozjs.so                 js_FinalizeObject object-finalize
>
>
> # pstack 1780 | c++filt 
> 1780:   /usr/lib/firefox/firefox-bin -safe-mode -UILocale C -contentLocale
> -----------------  lwp# 1 / thread# 1  --------------------
>  c005209c js_GC    (81264f0, 5) + 8e4
>  c00509ad js_NewGCThing (81264f0, 1, 8) + 151
>  c009af70 js_NewString (81264f0, 82d0568, c, 0) + 44
>  c009b477 js_NewStringCopyN (81264f0, 844c718, c, 0) + 5b
>  c0031106 js_AtomizeString (81264f0, 8045e28, 80) + d6
>  c0031302 js_AtomizeChars (81264f0, 844c718, c, 0) + 46
>  c0091120 js_GetToken (81264f0, 844c418) + 20e4
>  c007fe74 Variables (81264f0, 844c418, 80464ec) + 264
>  c007e4f5 Statement (81264f0, 844c418, 80464ec) + 32d
>  c007dc44 Statements (81264f0, 844c418, 80464ec) + 94
>  c007d0eb FunctionBody (81264f0, 844c418, 8414600, 80464ec) + cb
>  c007d937 FunctionDef (81264f0, 844c418, 8046d9c, 1) + 60f
>  c007dbaa FunctionExpr (81264f0, 844c418, 8046d9c) + 16
>  c00834ad PrimaryExpr (81264f0, 844c418, 8046d9c) + 901
>  c0081463 MemberExpr (81264f0, 844c418, 8046d9c, 1) + 53
>  c0081250 UnaryExpr (81264f0, 844c418, 8046d9c) + 1bc
>  c0080dec MulExpr  (81264f0, 844c418, 8046d9c) + 2c
>  c0080d18 AddExpr  (81264f0, 844c418, 8046d9c) + 2c
>  c0080c80 ShiftExpr (81264f0, 844c418, 8046d9c) + 2c
>  c0080b6c RelExpr  (81264f0, 844c418, 8046d9c) + 3c
>  c0080ac4 EqExpr   (81264f0, 844c418, 8046d9c) + 2c
>  c0080a37 BitAndExpr (81264f0, 844c418, 8046d9c) + 2f
>  c00809a7 BitXorExpr (81264f0, 844c418, 8046d9c) + 2f
>  c0080917 BitOrExpr (81264f0, 844c418, 8046d9c) + 2f
>  c008089f AndExpr  (81264f0, 844c418, 8046d9c) + 2f
>  c0080827 OrExpr   (81264f0, 844c418, 8046d9c) + 2f
>  c0080710 CondExpr (81264f0, 844c418, 8046d9c) + 2c
>  c008059f AssignExpr (81264f0, 844c418, 8046d9c) + 37
>  c0083278 PrimaryExpr (81264f0, 844c418, 8046d9c) + 6cc
>  c0081463 MemberExpr (81264f0, 844c418, 8046d9c, 1) + 53
>  c0081250 UnaryExpr (81264f0, 844c418, 8046d9c) + 1bc
>  c0080dec MulExpr  (81264f0, 844c418, 8046d9c) + 2c
>  c0080d18 AddExpr  (81264f0, 844c418, 8046d9c) + 2c
>  c0080c80 ShiftExpr (81264f0, 844c418, 8046d9c) + 2c
>  c0080b6c RelExpr  (81264f0, 844c418, 8046d9c) + 3c
>  c0080ac4 EqExpr   (81264f0, 844c418, 8046d9c) + 2c
>  c0080a37 BitAndExpr (81264f0, 844c418, 8046d9c) + 2f
>  c00809a7 BitXorExpr (81264f0, 844c418, 8046d9c) + 2f
>  c0080917 BitOrExpr (81264f0, 844c418, 8046d9c) + 2f
>  c008089f AndExpr  (81264f0, 844c418, 8046d9c) + 2f
>  c0080827 OrExpr   (81264f0, 844c418, 8046d9c) + 2f
>  c0080710 CondExpr (81264f0, 844c418, 8046d9c) + 2c
>  c008059f AssignExpr (81264f0, 844c418, 8046d9c) + 37
>  c0080681 AssignExpr (81264f0, 844c418, 8046d9c) + 119
>  c00804bf Expr     (81264f0, 844c418, 8046d9c) + 2f
>  c007f413 Statement (81264f0, 844c418, 8046d9c) + 124b
>  c007dc44 Statements (81264f0, 844c418, 8046d9c) + 94
>  c007cc71 js_CompileTokenStream (81264f0, 8265488, 844c418, 8046d9c) + 131
>  c002a4c1 CompileTokenStream (81264f0, 8265488, 844c418, 8126538, 0) + 95
>  c002a6c7 JS_CompileUCScriptForPrincipals (81264f0, 8265488, 812c2ec, 84606c8, 2113f, 8046fd0) + 47
>  c002a62b JS_CompileScriptForPrincipals (81264f0, 8265488, 812c2ec, c883c000, 2113f, 8046fd0) + 4b
>  c8be2d4c unsigned mozJSComponentLoader::GlobalForLocation(const char*,nsIFile*,JSObject**) (80d2b18, 845ec58, 845cbe0, 8047178) + 1794
>  c8be054d nsIModule*mozJSComponentLoader::ModuleForLocation(const char*,nsIFile*,unsigned*) (80d2b18, 845ec58, 845cbe0, 8047380) + 405
>  c8bdf7b0 unsigned mozJSComponentLoader::AttemptRegistration(nsIFile*,int) (80d2b18, 845cbe0, 0) + 148
>  c8bdf4d4 unsigned mozJSComponentLoader::AutoRegisterComponent(int,nsIFile*,int*) (80d2b18, 0, 845cbe0, 804747c) + 114
>  c8bdf092 unsigned mozJSComponentLoader::AutoRegisterComponents(int,nsIFile*) (80d2b18, 0, 80cac38) + 10e
>  ce0b02c1 unsigned nsComponentManagerImpl::AutoRegisterNonNativeComponents(nsIFile*) (80ca018, 80cac38) + d9
>  ce0b004c unsigned nsComponentManagerImpl::AutoRegisterImpl(int,nsIFile*,int) (80ca018, 0, 0, 1) + 370
>  ce0b09a0 unsigned nsComponentManagerImpl::AutoRegister(nsIFile*) (80ca018, 0) + 68
>  ce05f7c9 NS_InitXPCOM3_P (80478ec, 80a9540, 80476c4, 8087458, 1) + 45d
>  0805ab3a unsigned ScopedXPCOMStartup::Initialize() (80478ec) + 3e
>  080606ee XRE_main (5, 80479b0, 8087408) + 159a
>  0805a521 main     (5, 80479b0, 80479c8) + 25
>  0805a46e _start   (5, 8047aa8, 8047ad0, 8047ada, 8047adc, 0) + 7a
> -----------------  lwp# 2 / thread# 2  --------------------
>  ce588855 pollsys  (c8a89c70, 1, 0, 0)
>  ce53f722 poll     (c8a89c70, 1, ffffffff) + 52
>  ce4669f5 _pr_poll_with_poll (81465a8, 1, ffffffff, c8a89ef4, c9ab185e, 81465a8) + 395
>  ce466bc0 PR_Poll  (81465a8, 1, ffffffff) + 14
>  c9ab185e int nsSocketTransportService::Poll(unsigned*) (81460c0, c8a89f74) + 11e
>  c9ab23fc unsigned nsSocketTransportService::Run() (81460c0) + 68c
>  ce0bab14 void nsThread::Main(void*) (813e588) + 74
>  ce4683b4 _pt_root (8146858) + d0
>  ce587564 _thr_setup (ce422400) + 52
>  ce5877c0 _lwp_start (ce422400, 0, 0, 0, 0, 0)
> -----------------  lwp# 3 / thread# 3  --------------------
>  ce587819 lwp_park (0, c897ee58, 0)
>  ce581a32 cond_wait_queue (80c5c44, 80c8df0, c897ee58, 0) + 3e
>  ce581dc7 cond_wait_common (80c5c44, 80c8df0, c897ee58) + 1e1
>  ce581fec _cond_timedwait (80c5c44, 80c8df0, c897eed8) + 4a
>  ce58207b cond_timedwait (80c5c44, 80c8df0, c897eed8) + 27
>  ce5820b8 pthread_cond_timedwait (80c5c44, 80c8df0, c897eed8) + 21
>  ce461b0d PR_WaitCondVar (80c5c40, 7a11e) + 1f9
>  ce0bdd8b unsigned TimerThread::Run() (80c8ac8) + eb
>  ce0bab14 void nsThread::Main(void*) (815d600) + 74
>  ce4683b4 _pt_root (8167658) + d0
>  ce587564 _thr_setup (c8860000) + 52
>  ce5877c0 _lwp_start (c8860000, 0, 0, 0, 0, 0)
> # 
>
> ======================================================
> # truss -p 1780
> /2:         Received signal #20, SIGWINCH, in pollsys() [default]
> /3:     lwp_park(0xC897EE58, 0)                         Err#62 ETIME
> /1:         Received signal #11, SIGSEGV [caught]
> /1:           siginfo: SIGSEGV SEGV_MAPERR addr=0x83EC8B59
> /1:     lwp_sigmask(SIG_SETMASK, 0xFFBFFEFF, 0x0000FFF7) = 0xFFBFFEFF [0x0000FFFF]
> /1:     unlink("/export/home/jr140578/.mozilla/firefox/djmpbyit.default/lock") = 0
> /1:     sigaction(SIGSEGV, 0x08045950, 0x00000000)      = 0
> /1:     lwp_sigmask(SIG_SETMASK, 0xFFBFFAFF, 0x0000FFF7) = 0xFFBFFEFF [0x0000FFFF]
> /1:     lwp_kill(1, SIGSEGV)                            = 0
> /1:         Received signal #11, SIGSEGV [default]
> /1:           siginfo: SIGSEGV pid=1780 uid=0 code=-1
> # 
>
> *** NOTE *** CORE only generated after executing above Truss call on hung process
>
> ======================================================
>
> # /usr/lib/firefox/firefox-bin -safe-mode -UILocale C -contentLocale 
> Segmentation Fault (core dumped)
>
> # dbx /usr/lib/firefox/firefox-bin core
> For information about new features see `help changes'
> To remove this message, put `dbxenv suppress_startup_message 7.5' in your .dbxrc
> Reading firefox-bin
> core file header read successfully
> Reading ld.so.1
> Reading libmozz.so
> Reading libmozjs.so
> Reading libCrun.so.1
> Reading libCstd.so.1
> Reading libc.so.1
> Reading libplds4.so
> Reading libplc4.so
> Reading libnspr4.so
> Reading libpthread.so.1
> Reading libdl.so.1
> Reading librt.so.1
> Reading libsocket.so.1
> Reading libm.so.2
> Reading libdtrace.so.1
> Reading libthread.so.1
> Reading libnsl.so.1
> Reading libgen.so.1
> Reading libproc.so.1
> Reading librtld_db.so.1
> Reading libctf.so.1
> Reading libelf.so.1
> Reading libxpcom_core.so
> Reading libdemangle.so.1
> Reading libmozjs.so
> Reading libgtk-x11-2.0.so.0.1000.6
> Reading libgdk-x11-2.0.so.0.1000.6
> Reading libglib-2.0.so.0.1200.4
> Reading libgobject-2.0.so.0.1200.4
> Reading libmlib.so.2
> Reading libmlib_sse2.so.2
> Reading libcairo.so.2.9.2
> Reading libX11.so.4
> Reading libmp.so.2
> Reading libmd.so.1
> Reading libscf.so.1
> Reading libuutil.so.1
> Reading libgdk_pixbuf-2.0.so.0.1000.6
> Reading libgmodule-2.0.so.0.1200.4
> Reading libatk-1.0.so.0.1213.0
> Reading libpango-1.0.so.0.1400.7
> Reading libpangocairo-1.0.so.0.1400.7
> Reading libXrandr.so.2
> Reading libXrender.so.1
> Reading libXi.so.5
> Reading libXext.so.0
> Reading libfontconfig.so.1
> Reading libfreetype.so.6
> Reading libpng12.so.0.1.2.8
> Reading libz.so.1
> Reading libXau.so.6
> Reading libpangoft2-1.0.so.0.1400.7
> Reading libexpat.so.0.5.0
> Reading UTF-8%8859-1.so
> Reading 646%UTF-16LE.so
> Reading UTF-16LE%646.so
> Reading libaccessibility.so
> Reading libgkgfx.so
> Reading libappcomps.so
> Reading libauth.so
> Reading libautoconfig.so
> Reading libbrowsercomps.so
> Reading libbrowserdirprovider.so
> Reading libcaps.so
> Reading libchrome.so
> Reading libgfxps.so
> Reading libgfxpsshar.so
> Reading libcommandlines.so
> Reading libcomposer.so
> Reading libcookie.so
> Reading libdocshell.so
> Reading libeditor.so
> Reading libembedcomponents.so
> Reading libfileview.so
> Reading libgfx_gtk.so
> Reading libgklayout.so
> Reading libgkplugin.so
> Reading libhtmlpars.so
> Reading libi18n.so
> Reading libimgicon.so
> Reading libimglib2.so
> Reading libjar50.so
> Reading libjsd.so
> Reading libmork.so
> Reading libmozfind.so
> Reading libmozgnome.so
> Reading libmyspell.so
> Reading libnecko.so
> Reading libnecko2.so
> Reading libnkgnomevfs.so
> Reading libgnomevfs-2.so.0.1600.2
> Reading libxml2.so.2
> Reading libresolv.so.2
> Reading libnsappshell.so
> Reading liboji.so
> Reading libpermissions.so
> Reading libpipboot.so
> Reading libpipnss.so
> Reading libssl3.so
> Reading libnss3.so
> Reading libsoftokn3.so
> Reading libbsm.so.1
> Reading libsecdb.so.1
> Reading libpippki.so
> Reading libpref.so
> Reading librdf.so
> Reading libremoteservice.so
> Reading libsearchservice.so
> Reading libspellchecker.so
> Reading libstoragecomps.so
> Reading libsystem-pref.so
> Reading libtoolkitcomps.so
> Reading libtransformiix.so
> Reading libtxmgr.so
> Reading libuconv.so
> Reading libucvmath.so
> Reading libuniversalchardet.so
> Reading libwebbrwsr.so
> Reading libwebsrvcs.so
> Reading libwidget_gtk2.so
> Reading libxmlextras.so
> Reading libxpcom_compat_c.so
> Reading libxpcom_compat.so
> Reading libxpconnect.so
> Reading libxpinstall.so
> t at null (l at 1) terminated by signal SEGV (Segmentation Fault)
> 0xce588dc5: __lwp_kill+0x0015:  jae      __lwp_kill+0x23        [ 0xce588dd3, .+0xe ]
> (dbx) where
> =>[1] __lwp_kill(0x1, 0xb), at 0xce588dc5 
>   [2] _thr_kill(0x1, 0xb), at 0xce58597e 
>   [3] raise(0xb), at 0xce542072 
>   [4] nsProfileLock::FatalSignalHandler(0xb, 0x0, 0x8045a64), at 0x806bc86 
>   [5] __sighndlr(0xb, 0x0, 0x8045a64, 0x806bba0), at 0xce5878bf 
>   ---- called from signal handler with signal 11 (SIGSEGV) ------
>   [6] js_GC(0x81264f0, 0x5), at 0xc005209c 
>   [7] js_NewGCThing(0x81264f0, 0x1, 0x8), at 0xc00509ad 
>   [8] js_NewString(0x81264f0, 0x82d0568, 0xc, 0x0), at 0xc009af70 
>   [9] js_NewStringCopyN(0x81264f0, 0x844c718, 0xc, 0x0), at 0xc009b477 
>   [10] js_AtomizeString(0x81264f0, 0x8045e28, 0x80), at 0xc0031106 
>   [11] js_AtomizeChars(0x81264f0, 0x844c718, 0xc, 0x0), at 0xc0031302 
>   [12] js_GetToken(0x81264f0, 0x844c418), at 0xc0091120 
>   [13] Variables(0x81264f0, 0x844c418, 0x80464ec), at 0xc007fe74 
>   [14] Statement(0x81264f0, 0x844c418, 0x80464ec), at 0xc007e4f5 
>   [15] Statements(0x81264f0, 0x844c418, 0x80464ec), at 0xc007dc44 
>   [16] FunctionBody(0x81264f0, 0x844c418, 0x8414600, 0x80464ec), at 0xc007d0eb 
>   [17] FunctionDef(0x81264f0, 0x844c418, 0x8046d9c, 0x1), at 0xc007d937 
>   [18] FunctionExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc007dbaa 
>   [19] PrimaryExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc00834ad 
>   [20] MemberExpr(0x81264f0, 0x844c418, 0x8046d9c, 0x1), at 0xc0081463 
>   [21] UnaryExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc0081250 
>   [22] MulExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc0080dec 
>   [23] AddExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc0080d18 
>   [24] ShiftExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc0080c80 
>   [25] RelExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc0080b6c 
>   [26] EqExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc0080ac4 
>   [27] BitAndExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc0080a37 
>   [28] BitXorExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc00809a7 
>   [29] BitOrExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc0080917 
>   [30] AndExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc008089f 
>   [31] OrExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc0080827 
>   [32] CondExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc0080710 
>   [33] AssignExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc008059f 
>   [34] PrimaryExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc0083278 
>   [35] MemberExpr(0x81264f0, 0x844c418, 0x8046d9c, 0x1), at 0xc0081463 
>   [36] UnaryExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc0081250 
>   [37] MulExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc0080dec 
>   [38] AddExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc0080d18 
>   [39] ShiftExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc0080c80 
>   [40] RelExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc0080b6c 
>   [41] EqExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc0080ac4 
>   [42] BitAndExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc0080a37 
>   [43] BitXorExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc00809a7 
>   [44] BitOrExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc0080917 
>   [45] AndExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc008089f 
>   [46] OrExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc0080827 
>   [47] CondExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc0080710 
>   [48] AssignExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc008059f 
>   [49] AssignExpr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc0080681 
>   [50] Expr(0x81264f0, 0x844c418, 0x8046d9c), at 0xc00804bf 
>   [51] Statement(0x81264f0, 0x844c418, 0x8046d9c), at 0xc007f413 
>   [52] Statements(0x81264f0, 0x844c418, 0x8046d9c), at 0xc007dc44 
>   [53] js_CompileTokenStream(0x81264f0, 0x8265488, 0x844c418, 0x8046d9c), at 0xc007cc71 
>   [54] CompileTokenStream(0x81264f0, 0x8265488, 0x844c418, 0x8126538, 0x0), at 0xc002a4c1 
>   [55] JS_CompileUCScriptForPrincipals(0x81264f0, 0x8265488, 0x812c2ec, 0x84606c8, 0x2113f, 0x8046fd0, 0x0), at 0xc002a6c7 
>   [56] JS_CompileScriptForPrincipals(0x81264f0, 0x8265488, 0x812c2ec, 0xc883c000, 0x2113f, 0x8046fd0, 0x0), at 0xc002a62b 
>   [57] mozJSComponentLoader::GlobalForLocation(0x80d2b18, 0x845ec58, 0x845cbe0, 0x8047178), at 0xc8be2d4c 
>   [58] mozJSComponentLoader::ModuleForLocation(0x80d2b18, 0x845ec58, 0x845cbe0, 0x8047380), at 0xc8be054d 
>   [59] mozJSComponentLoader::AttemptRegistration(0x80d2b18, 0x845cbe0, 0x0), at 0xc8bdf7b0 
>   [60] mozJSComponentLoader::AutoRegisterComponent(0x80d2b18, 0x0, 0x845cbe0, 0x804747c), at 0xc8bdf4d4 
>   [61] mozJSComponentLoader::AutoRegisterComponents(0x80d2b18, 0x0, 0x80cac38), at 0xc8bdf092 
>   [62] nsComponentManagerImpl::AutoRegisterNonNativeComponents(0x80ca018, 0x80cac38), at 0xce0b02c1 
>   [63] nsComponentManagerImpl::AutoRegisterImpl(0x80ca018, 0x0, 0x0, 0x1), at 0xce0b004c 
>   [64] nsComponentManagerImpl::AutoRegister(0x80ca018, 0x0), at 0xce0b09a0 
>   [65] NS_InitXPCOM3_P(0x80478ec, 0x80a9540, 0x80476c4, 0x8087458, 0x1), at 0xce05f7c9 
>   [66] ScopedXPCOMStartup::Initialize(0x80478ec), at 0x805ab3a 
>   [67] XRE_main(0x5, 0x80479b0, 0x8087408), at 0x80606ee 
>   [68] main(0x5, 0x80479b0, 0x80479c8), at 0x805a521 
> (dbx) print (char *) 0x844c718 
> dbx: warning: unknown language, 'c' assumed
> (char *) 0x844c718 = 0x844c718 "a"
> (dbx) print (char *) 0x8046fd0 
> (char *) 0x8046fd0 = 0x8046fd0 "file:///usr/lib/firefox/components/nsUrlClassifierLib.js"
> (dbx) 
>
> NOTE: JS_CompileScriptForPrincipals( File name of javascript to compile -> arg6 )
>
> ===============================================
>
> DTRUSS
> ========
> Opens nsUrlClassifierLib.js and hangs at or after brk call
>
> # ~/DTraceToolkit-0.96/dtruss /usr/lib/firefox/firefox-bin -safe-mode -UILocale C -contentLocale
>
> stat64("/usr/lib/firefox/components/nsSetDefaultBrowser.js\0", 0x8047080, 0x61D9)                = 0 0
> xstat(0x2, 0x83AA948, 0x83D2868)                 = 0 0
> stat64("/usr/lib/firefox/components/nsSidebar.js\0", 0x8047070, 0x61DD)          = 0 0
> lwp_park(0x1, 0x3, 0x80C5C44)            = 0 0
> lwp_park(0x0, 0xC8978E58, 0x0)           = 0 0
> lwp_park(0x0, 0xC8978E58, 0x0)           = 0 0
> lwp_park(0x0, 0xC8978E58, 0x0)           = 0 0
> brk(0x8456660)           = 0 0
> brk(0x8458660)           = 0 0
> brk(0x8458660)           = 0 0
> brk(0x845A660)           = 0 0
> brk(0x845A660)           = 0 0
> brk(0x845C660)           = 0 0
> brk(0x845C660)           = 0 0
> brk(0x845E660)           = 0 0
> brk(0x845E660)           = 0 0
> brk(0x8460660)           = 0 0
> brk(0x8460660)           = 0 0
> brk(0x8462660)           = 0 0
> munmap(0xC8846000, 0x17F36)              = 0 0
> close(0xB)               = 0 0
> stat64("/usr/lib/firefox/components/nsUpdateService.js\0", 0x8047080, 0x6227)            = 0 0
> xstat(0x2, 0x838C9F0, 0x845CA58)                 = 0 0
> stat64("/usr/lib/firefox/components/nsUrlClassifierLib.js\0", 0x8047070, 0x622B)                 = 0 0
> lwp_park(0x1, 0x3, 0x80C5C44)            = 0 0
> lwp_park(0x0, 0xC8978E58, 0x0)           = 0 0
> yield(0x3F, 0x0, 0x622F)                 = 0 0
> xstat(0x2, 0x8147E70, 0x8147D10)                 = -1 Err#2
> lxstat(0x2, 0x8147E70, 0x8147D10)                = -1 Err#2
> open64("/usr/lib/firefox/components/nsUrlClassifierLib.js\0", 0x0, 0x0)          = 11 0
> fstat64(0xB, 0x8046CB0, 0x6239)          = 0 0
> fxstat(0x2, 0xB, 0x8046DD8)              = 0 0
> mmap(0x0, 0x2113F, 0x1)          = -930889728 0
> brk(0x8462660)           = 0 0
> brk(0x84A4660)           = 0 0
>
>
>
>   
> ------------------------------------------------------------------------
>
>    The original attachment - nsUrlClassifierLib.js - has been removed from this 
>    message, as it is a type of file which has recently been used 
>    to carry viruses and other malicious software. 
>
>    The following extensions are no longer supported as valid 
>    email attachments and will be removed since they are primarily 
>    used to carry viruses and other malicious software:
>
>       asp bas bat chm cmd com exe hlp hta inf isp js jse lnk msi mst
>       pcd pif reg scr url vbe vbs ws wsh sun.com.zip
>
>   

-------------- next part --------------
/* ***** 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 Url Classifier code
 *
 * The Initial Developer of the Original Code is
 * Google Inc.
 * Portions created by the Initial Developer are Copyright (C) 2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Tony Chang <tony at ponderer.org>
 *
 * 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 ***** */

// We wastefully reload the same JS files across components.  This puts all
// the common JS files used by safebrowsing and url-classifier into a
// single component.

const Cc = Components.classes;
const Ci = Components.interfaces;
const G_GDEBUG = false;

// TODO: get rid of application.js and filesystem.js (not used much)
/* ***** 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 Google Safe Browsing.
 *
 * The Initial Developer of the Original Code is Google Inc.
 * Portions created by the Initial Developer are Copyright (C) 2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Aaron Boodman <aa at google.com> (original author)
 *
 * 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 ***** */

// This file has pure js helper functions. Hence you'll find metion
// of browser-specific features in here.


/**
 * lang.js - The missing JavaScript language features
 *
 * WARNING: This class adds members to the prototypes of String, Array, and
 * Function for convenience.
 *
 * The tradeoff is that the for/in statement will not work properly for those
 * objects when this library is used.
 *
 * To work around this for Arrays, you may want to use the forEach() method,
 * which is more fun and easier to read.
 */

/**
 * Returns true if the specified value is |null|
 */
function isNull(val) {
  return val === null;
}

/**
 * Returns true if the specified value is an array
 */
function isArray(val) {
  return isObject(val) && val.constructor == Array;
}

/**
 * Returns true if the specified value is a string
 */
function isString(val) {
  return typeof val == "string";
}

/**
 * Returns true if the specified value is a boolean
 */
function isBoolean(val) {
  return typeof val == "boolean";
}

/**
 * Returns true if the specified value is a number
 */
function isNumber(val) {
  return typeof val == "number";
}

/**
 * Returns true if the specified value is a function
 */
function isFunction(val) {
  return typeof val == "function";
}

/**
 * Returns true if the specified value is an object
 */
function isObject(val) {
  return val && typeof val == "object";
}

/**
 * Returns an array of all the properties defined on an object
 */
function getObjectProps(obj) {
  var ret = [];

  for (var p in obj) {
    ret.push(p);
  }

  return ret;
}

/**
 * Returns true if the specified value is an object which has no properties
 * defined.
 */
function isEmptyObject(val) {
  if (!isObject(val)) {
    return false;
  }

  for (var p in val) {
    return false;
  }

  return true;
}

var getHashCode;
var removeHashCode;

(function () {
  var hashCodeProperty = "lang_hashCode_";

  /**
   * Adds a lang_hashCode_ field to an object. The hash code is unique for the
   * given object.
   * @param obj {Object} The object to get the hash code for
   * @returns {Number} The hash code for the object
   */
  getHashCode = function(obj) {
    // In IE, DOM nodes do not extend Object so they do not have this method.
    // we need to check hasOwnProperty because the proto might have this set.
    if (obj.hasOwnProperty && obj.hasOwnProperty(hashCodeProperty)) {
      return obj[hashCodeProperty];
    }
    if (!obj[hashCodeProperty]) {
      obj[hashCodeProperty] = ++getHashCode.hashCodeCounter_;
    }
    return obj[hashCodeProperty];
  };

  /**
   * Removes the lang_hashCode_ field from an object.
   * @param obj {Object} The object to remove the field from. 
   */
  removeHashCode = function(obj) {
    obj.removeAttribute(hashCodeProperty);
  };

  getHashCode.hashCodeCounter_ = 0;
})();

/**
 * Fast prefix-checker.
 */
String.prototype.startsWith = function(prefix) {
  if (this.length < prefix.length) {
    return false;
  }

  if (this.substring(0, prefix.length) == prefix) {
    return true;
  }

  return false;
}

/**
 * Removes whitespace from the beginning and end of the string
 */
String.prototype.trim = function() {
  return this.replace(/^\s+|\s+$/g, "");
}

/**
 * Does simple python-style string substitution.
 * "foo%s hot%s".subs("bar", "dog") becomes "foobar hotdot".
 * For more fully-featured templating, see template.js.
 */
String.prototype.subs = function() {
  var ret = this;

  // this appears to be slow, but testing shows it compares more or less equiv.
  // to the regex.exec method.
  for (var i = 0; i < arguments.length; i++) {
    ret = ret.replace(/\%s/, String(arguments[i]));
  }

  return ret;
}

/**
 * Returns the last element on an array without removing it.
 */
Array.prototype.peek = function() {
  return this[this.length - 1];
}

// TODO(anyone): add splice the first time someone needs it and then implement
// push, pop, shift, unshift in terms of it where possible.

// TODO(anyone): add the other neat-o functional methods like map(), etc.

/**
 * Partially applies this function to a particular "this object" and zero or
 * more arguments. The result is a new function with some arguments of the first
 * function pre-filled and the value of |this| "pre-specified".
 *
 * Remaining arguments specified at call-time are appended to the pre-
 * specified ones.
 *
 * Also see: partial().
 *
 * Note that bind and partial are optimized such that repeated calls to it do 
 * not create more than one function object, so there is no additional cost for
 * something like:
 *
 * var g = bind(f, obj);
 * var h = partial(g, 1, 2, 3);
 * var k = partial(h, a, b, c);
 *
 * Usage:
 * var barMethBound = bind(myFunction, myObj, "arg1", "arg2");
 * barMethBound("arg3", "arg4");
 *
 * @param thisObj {object} Specifies the object which |this| should point to
 * when the function is run. If the value is null or undefined, it will default
 * to the global object.
 *
 * @returns {function} A partially-applied form of the function bind() was
 * invoked as a method of.
 */
function bind(fn, self, opt_args) {
  var boundargs = (typeof fn.boundArgs_ != "undefined") ? fn.boundArgs_ : [];
  boundargs = boundargs.concat(Array.prototype.slice.call(arguments, 2));

  if (typeof fn.boundSelf_ != "undefined") {
    self = fn.boundSelf_;
  }

  if (typeof fn.boundFn_ != "undefined") {
    fn = fn.boundFn_;
  }

  var newfn = function() {
    // Combine the static args and the new args into one big array
    var args = boundargs.concat(Array.prototype.slice.call(arguments));
    return fn.apply(self, args);
  }

  newfn.boundArgs_ = boundargs;
  newfn.boundSelf_ = self;
  newfn.boundFn_ = fn;

  return newfn;
}

/**
 * An alias to the bind() global function.
 *
 * Usage:
 * var g = f.bind(obj, arg1, arg2);
 * g(arg3, arg4);
 */
Function.prototype.bind = function(self, opt_args) {
  return bind.apply(
    null, [this, self].concat(Array.prototype.slice.call(arguments, 1)));
}

/**
 * Like bind(), except that a "this object" is not required. Useful when the
 * target function is already bound.
 * 
 * Usage:
 * var g = partial(f, arg1, arg2);
 * g(arg3, arg4);
 */
function partial(fn, opt_args) {
  return bind.apply(
    null, [fn, null].concat(Array.prototype.slice.call(arguments, 1)));
}

/**
 * An alias to the partial() global function.
 *
 * Usage:
 * var g = f.partial(arg1, arg2);
 * g(arg3, arg4);
 */
Function.prototype.partial = function(opt_args) {
  return bind.apply(
    null, [this, null].concat(Array.prototype.slice.call(arguments)));
}

/**
 * Convenience. Binds all the methods of obj to itself. Calling this in the
 * constructor before referencing any methods makes things a little more like
 * Java or Python where methods are intrinsically bound to their instance.
 */
function bindMethods(obj) {
  for (var p in obj) {
    if (isFunction(obj[p])) {
      obj[p] = obj[p].bind(obj);
    }
  }
}

/**
 * Inherit the prototype methods from one constructor into another.
 *
 * Usage:
 * <pre>
 * function ParentClass(a, b) { }
 * ParentClass.prototype.foo = function(a) { }
 *
 * function ChildClass(a, b, c) {
 *   ParentClass.call(this, a, b);
 * }
 *
 * ChildClass.inherits(ParentClass);
 *
 * var child = new ChildClass("a", "b", "see");
 * child.foo(); // works
 * </pre>
 *
 * In addition, a superclass' implementation of a method can be invoked
 * as follows:
 *
 * <pre>
 * ChildClass.prototype.foo = function(a) {
 *   ChildClass.superClass_.foo.call(this, a);
 *   // other code
 * };
 * </pre>
 */
Function.prototype.inherits = function(parentCtor) {
  var tempCtor = function(){};
  tempCtor.prototype = parentCtor.prototype;
  this.superClass_ = parentCtor.prototype;
  this.prototype = new tempCtor();
}
//@line 48 "/jds/packages/BUILD/SUNWfirefox-2.0/firefox/toolkit/components/url-classifier/src/nsUrlClassifierLib.js"

/* ***** 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 Google Safe Browsing.
 *
 * The Initial Developer of the Original Code is Google Inc.
 * Portions created by the Initial Developer are Copyright (C) 2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Fritz Schneider <fritz at google.com> (original author)
 *
 * 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 ***** */


// Class for manipulating preferences. Aside from wrapping the pref
// service, useful functionality includes:
//
// - abstracting prefobserving so that you can observe preferences
//   without implementing nsIObserver 
// 
// - getters that return a default value when the pref doesn't exist 
//   (instead of throwing)
// 
// - get-and-set getters
//
// Example:
// 
// var p = new PROT_Preferences();
// alert(p.getPref("some-true-pref"));     // shows true
// alert(p.getPref("no-such-pref", true)); // shows true   
// alert(p.getPref("no-such-pref", null)); // shows null
//
// function observe(prefThatChanged) {
//   alert("Pref changed: " + prefThatChanged);
// };
//
// p.addObserver("somepref", observe);
// p.setPref("somepref", true);            // alerts
// p.removeObserver("somepref", observe);
//
// TODO: should probably have the prefobserver pass in the new and old
//       values

// TODO(tc): Maybe remove this class and just call natively since we're no
//           longer an extension.

/**
 * A class that wraps the preferences service.
 *
 * @param opt_startPoint        A starting point on the prefs tree to resolve 
 *                              names passed to setPref and getPref.
 *
 * @param opt_useDefaultPranch  Set to true to work against the default 
 *                              preferences tree instead of the profile one.
 *
 * @constructor
 */
function G_Preferences(opt_startPoint, opt_getDefaultBranch) {
  this.debugZone = "prefs";
  this.observers_ = {};
  this.getDefaultBranch_ = !!opt_getDefaultBranch;

  this.startPoint_ = opt_startPoint || null;
}

G_Preferences.setterMap_ = { "string": "setCharPref",
                             "boolean": "setBoolPref",
                             "number": "setIntPref" };

G_Preferences.getterMap_ = {};
G_Preferences.getterMap_[Ci.nsIPrefBranch.PREF_STRING] = "getCharPref";
G_Preferences.getterMap_[Ci.nsIPrefBranch.PREF_BOOL] = "getBoolPref";
G_Preferences.getterMap_[Ci.nsIPrefBranch.PREF_INT] = "getIntPref";

G_Preferences.prototype.__defineGetter__('prefs_', function() {
  var prefs;
  var prefSvc = Cc["@mozilla.org/preferences-service;1"]
                  .getService(Ci.nsIPrefService);

  if (this.getDefaultBranch_) {
    prefs = prefSvc.getDefaultBranch(this.startPoint_);
  } else {
    prefs = prefSvc.getBranch(this.startPoint_);
  }

  // QI to prefs in case we want to add observers
  prefs.QueryInterface(Ci.nsIPrefBranchInternal);
  return prefs;
});

/**
 * Stores a key/value in a user preference. Valid types for val are string,
 * boolean, and number. Complex values are not yet supported (but feel free to
 * add them!).
 */
G_Preferences.prototype.setPref = function(key, val) {
  var datatype = typeof(val);

  if (datatype == "number" && (val % 1 != 0)) {
    throw new Error("Cannot store non-integer numbers in preferences.");
  }

  var meth = G_Preferences.setterMap_[datatype];

  if (!meth) {
    throw new Error("Pref datatype {" + datatype + "} not supported.");
  }

  return this.prefs_[meth](key, val);
}

/**
 * Retrieves a user preference. Valid types for the value are the same as for
 * setPref. If the preference is not found, opt_default will be returned 
 * instead.
 */
G_Preferences.prototype.getPref = function(key, opt_default) {
  var type = this.prefs_.getPrefType(key);

  // zero means that the specified pref didn't exist
  if (type == Ci.nsIPrefBranch.PREF_INVALID) {
    return opt_default;
  }

  var meth = G_Preferences.getterMap_[type];

  if (!meth) {
    throw new Error("Pref datatype {" + type + "} not supported.");
  }

  // If a pref has been cleared, it will have a valid type but won't
  // be gettable, so this will throw.
  try {
    return this.prefs_[meth](key);
  } catch(e) {
    return opt_default;
  }
}

/**
 * Set a boolean preference
 *
 * @param which Name of preference to set
 * @param value Boolean indicating value to set
 *
 * @deprecated  Just use setPref.
 */
G_Preferences.prototype.setBoolPref = function(which, value) {
  return this.setPref(which, value);
}

/**
 * Get a boolean preference. WILL THROW IF PREFERENCE DOES NOT EXIST.
 * If you don't want this behavior, use getBoolPrefOrDefault.
 *
 * @param which Name of preference to get.
 *
 * @deprecated  Just use getPref.
 */
G_Preferences.prototype.getBoolPref = function(which) {
  return this.prefs_.getBoolPref(which);
}

/**
 * Get a boolean preference or return some default value if it doesn't
 * exist. Note that the default doesn't have to be bool -- it could be
 * anything (e.g., you could pass in null and check if the return
 * value is === null to determine if the pref doesn't exist).
 *
 * @param which Name of preference to get.
 * @param def Value to return if the preference doesn't exist
 * @returns Boolean value of the pref if it exists, else def
 *
 * @deprecated  Just use getPref.
 */
G_Preferences.prototype.getBoolPrefOrDefault = function(which, def) {
  return this.getPref(which, def);
}

/**
 * Get a boolean preference if it exists. If it doesn't, set its value
 * to a default and return the default. Note that the default will be
 * coherced to a bool if it is set, but not in the return value.
 *
 * @param which Name of preference to get.
 * @param def Value to set and return if the preference doesn't exist
 * @returns Boolean value of the pref if it exists, else def
 *
 * @deprecated  Just use getPref.
 */
G_Preferences.prototype.getBoolPrefOrDefaultAndSet = function(which, def) {
  try {
    return this.prefs_.getBoolPref(which);
  } catch(e) {
    this.prefs_.setBoolPref(which, !!def);  // The !! forces boolean conversion
    return def;
  }
}

/**
 * Delete a preference. 
 *
 * @param which Name of preference to obliterate
 */
G_Preferences.prototype.clearPref = function(which) {
  try {
    // This throws if the pref doesn't exist, which is fine because a 
    // non-existent pref is cleared
    this.prefs_.clearUserPref(which);
  } catch(e) {}
}

/**
 * Add an observer for a given pref.
 *
 * @param which String containing the pref to listen to
 * @param callback Function to be called when the pref changes. This
 *                 function will receive a single argument, a string 
 *                 holding the preference name that changed
 */
G_Preferences.prototype.addObserver = function(which, callback) {
  var observer = new G_PreferenceObserver(callback);
  // Need to store the observer we create so we can eventually unregister it
  if (!this.observers_[which])
    this.observers_[which] = new G_ObjectSafeMap();
  this.observers_[which].insert(callback, observer);
  this.prefs_.addObserver(which, observer, false /* strong reference */);
}

/**
 * Remove an observer for a given pref.
 *
 * @param which String containing the pref to stop listening to
 * @param callback Function to remove as an observer
 */
G_Preferences.prototype.removeObserver = function(which, callback) {
  var observer = this.observers_[which].find(callback);
  G_Assert(this, !!observer, "Tried to unregister a nonexistant observer"); 
  this.prefs_.removeObserver(which, observer);
  this.observers_[which].erase(callback);
}


/**
 * Helper class that knows how to observe preference changes and
 * invoke a callback when they do
 *
 * @constructor
 * @param callback Function to call when the preference changes
 */
function G_PreferenceObserver(callback) {
  this.debugZone = "prefobserver";
  this.callback_ = callback;
}

/**
 * Invoked by the pref system when a preference changes. Passes the
 * message along to the callback.
 *
 * @param subject The nsIPrefBranch that changed
 * @param topic String "nsPref:changed" (aka 
 *              NS_PREFBRANCH_PREFCHANGE_OBSERVER_ID -- but where does it
 *              live???)
 * @param data Name of the pref that changed
 */
G_PreferenceObserver.prototype.observe = function(subject, topic, data) {
  G_Debug(this, "Observed pref change: " + data);
  this.callback_(data);
}

/**
 * XPCOM cruft
 *
 * @param iid Interface id of the interface the caller wants
 */
G_PreferenceObserver.prototype.QueryInterface = function(iid) {
  var Ci = Ci;
  if (iid.equals(Ci.nsISupports) || 
      iid.equals(Ci.nsIObserves) ||
      iid.equals(Ci.nsISupportsWeakReference))
    return this;
  throw Components.results.NS_ERROR_NO_INTERFACE;
}

/* ***** 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 Google Safe Browsing.
 *
 * The Initial Developer of the Original Code is Google Inc.
 * Portions created by the Initial Developer are Copyright (C) 2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Aaron Boodman <aa at google.com> (original author)
 *   Raphael Moll <raphael at google.com>
 *   Fritz Schneider <fritz at google.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 ***** */


// Utilities for working with nsIFile and related interfaces.

/**
 * Stub for an nsIFile wrapper which doesn't exist yet. Perhaps in the future
 * we could add functionality to nsILocalFile which would be useful to us here,
 * but for now, no need for such. This could be done by setting 
 * __proto__ to an instance of nsIFile, for example. Neat.
 */
var G_File = {};

/**
 * Returns an nsIFile pointing to the user's home directory, or optionally, a
 * file inside that dir.
 */
G_File.getHomeFile = function(opt_file) {
  return this.getSpecialFile("Home", opt_file);
}

/**
 * Returns an nsIFile pointing to the current profile folder, or optionally, a
 * file inside that dir.
 */
G_File.getProfileFile = function(opt_file) {
  return this.getSpecialFile("ProfD", opt_file);
}

/**
 * returns an nsIFile pointing to the temporary dir, or optionally, a file 
 * inside that dir.
 */
G_File.getTempFile = function(opt_file) {
  return this.getSpecialFile("TmpD", opt_file);
}

/**
 * Returns an nsIFile pointing to one of the special named directories defined 
 * by Firefox, such as the user's home directory, the profile directory, etc. 
 * 
 * As a convenience, callers may specify the opt_file argument to get that file
 * within the special directory instead.
 *
 * http://lxr.mozilla.org/seamonkey/source/xpcom/io/nsDirectoryServiceDefs.h
 * http://kb.mozillazine.org/File_IO#Getting_special_files
 */
G_File.getSpecialFile = function(loc, opt_file) {
  var file = Cc["@mozilla.org/file/directory_service;1"]
             .getService(Ci.nsIProperties)
             .get(loc, Ci.nsILocalFile);

  if (opt_file) {
    file.append(opt_file);
  }

  return file;
}

/**
 * Creates and returns a pointer to a unique file in the temporary directory
 * with an optional base name.
 */
G_File.createUniqueTempFile = function(opt_baseName) {
  var baseName = (opt_baseName || (new Date().getTime())) + ".tmp";

  var file = this.getSpecialFile("TmpD", baseName);
  file.createUnique(file.NORMAL_FILE_TYPE, 0644);

  return file;
}

/**
 * Creates and returns a pointer to a unique temporary directory, with
 * an optional base name.
 */
G_File.createUniqueTempDir = function(opt_baseName) {
  var baseName = (opt_baseName || (new Date().getTime())) + ".tmp";

  var dir = this.getSpecialFile("TmpD", baseName);
  dir.createUnique(dir.DIRECTORY_TYPE, 0744);

  return dir;
}

/**
 * Static method to retrieve an nsIFile from a file:// URI.
 */
G_File.fromFileURI = function(uri) {
  // Ensure they use file:// url's: discourages platform-specific paths
  if (uri.indexOf("file://") != 0)
    throw new Error("File path must be a file:// URL");

  var fileHandler = Cc["@mozilla.org/network/protocol;1?name=file"]
                    .getService(Ci.nsIFileProtocolHandler);
  return fileHandler.getFileFromURLSpec(uri);
}

// IO Constants

G_File.PR_RDONLY = 0x01;      // read-only
G_File.PR_WRONLY = 0x02;      // write only
G_File.PR_RDWR = 0x04;        // reading and writing
G_File.PR_CREATE_FILE = 0x08; // create if it doesn't exist
G_File.PR_APPEND = 0x10;      // file pntr reset to end prior to writes
G_File.PR_TRUNCATE = 0x20;    // file exists its length is set to zero
G_File.PR_SYNC = 0x40;        // writes wait for data to be physically written
G_File.PR_EXCL = 0x80;        // file does not exist ? created : no action

// The character(s) to use for line-endings, which are platform-specific.
// This doesn't work for mac os9, but I don't know of a good way to detect
// OS9-ness from JS.
G_File.__defineGetter__("LINE_END_CHAR", function() {
  var end_char = Cc["@mozilla.org/xre/app-info;1"]
                 .getService(Ci.nsIXULRuntime)
                 .OS == "WINNT" ? "\r\n" : "\n";

  // Cache result
  G_File.__defineGetter__("LINE_END_CHAR", function() { return end_char; });
  return end_char;
});

/**
 * A class which can read a file incrementally or all at once. Parameter can be
 * either an nsIFile instance or a string file:// URI.
 * Note that this class is not compatible with non-ascii data.
 */
function G_FileReader(file) {
  this.file_ = isString(file) ? G_File.fromFileURI(file) : file;
}

/**
 * Utility method to read the entire contents of a file. Parameter can be either
 * an nsIFile instance or a string file:// URI.
 */
G_FileReader.readAll = function(file) { 
  var reader = new G_FileReader(file);

  try {
    return reader.read();
  } finally {
    reader.close();
  }
}

/**
 * Read up to opt_maxBytes from the stream. If opt_maxBytes is not specified, 
 * the entire file is read.
 */
G_FileReader.prototype.read = function(opt_maxBytes) {
  if (!this.stream_) {
    var fs = Cc["@mozilla.org/network/file-input-stream;1"]
               .createInstance(Ci.nsIFileInputStream);
    fs.init(this.file_, G_File.PR_RDONLY, 0444, 0);

    this.stream_ = Cc["@mozilla.org/scriptableinputstream;1"]
                     .createInstance(Ci.nsIScriptableInputStream);
    this.stream_.init(fs);
  }
  
  if (typeof opt_maxBytes == "undefined") {
    opt_maxBytes = this.stream_.available();
  }

  return this.stream_.read(opt_maxBytes);
}

/**
 * Close the stream. This step is required when reading is done.
 */
G_FileReader.prototype.close = function(opt_maxBytes) {
  if (this.stream_) {
    this.stream_.close();
    this.stream_ = null;
  }
}


// TODO(anyone): Implement G_LineReader. The interface should be something like:
// for (var line = null; line = reader.readLine();) {
//   // do something with line
// }


/**
 * Writes a file incrementally or all at once.
 * Note that this class is not compatible with non-ascii data.
 */
function G_FileWriter(file, opt_append) {
  this.file_ = typeof file == "string" ? G_File.fromFileURI(file) : file;
  this.append_ = !!opt_append;
}

/**
 * Helper to write to a file in one step.
 */
G_FileWriter.writeAll = function(file, data, opt_append) { 
  var writer = new G_FileWriter(file, opt_append);

  try {
    return writer.write(data);
  } finally {
    writer.close();
    return 0;
  }
}

/**
 * Write bytes out to the file. Returns the number of bytes written.
 */
G_FileWriter.prototype.write = function(data) {
  if (!this.stream_) {
    this.stream_ = Cc["@mozilla.org/network/file-output-stream;1"]
                     .createInstance(Ci.nsIFileOutputStream);

    var flags = G_File.PR_WRONLY | 
                G_File.PR_CREATE_FILE | 
                (this.append_ ? G_File.PR_APPEND : G_File.PR_TRUNCATE);


    this.stream_.init(this.file_, 
                      flags, 
                      -1 /* default perms */, 
                      0 /* no special behavior */);
  }

  return this.stream_.write(data, data.length);
}

/**
 * Writes bytes out to file followed by the approriate line-ending character for
 * the current platform.
 */
G_FileWriter.prototype.writeLine = function(data) {
  this.write(data + G_File.LINE_END_CHAR);
}


/**
 * Closes the file. This must becalled when writing is done.
 */
G_FileWriter.prototype.close = function() {
  if (this.stream_) {
    this.stream_.close();
    this.stream_ = null;
  }
}

/* ***** 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 Google Safe Browsing.
 *
 * The Initial Developer of the Original Code is Google Inc.
 * Portions created by the Initial Developer are Copyright (C) 2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Fritz Schneider <fritz at google.com> (original author)
 *   Annie Sullivan <sullivan at google.com>
 *   Aaron Boodman <aa at google.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 ***** */

// Generic logging/debugging functionality that:
//
// (*) when disabled compiles to no-ops at worst (for calls to the service) 
//     and to nothing at best (calls to G_Debug() and similar are compiled
//     away when you use a jscompiler that strips dead code)
//
// (*) has dynamically configurable/creatable debugging "zones" enabling 
//     selective logging
// 
// (*) hides its plumbing so that all calls in different zones are uniform,
//     so you can drop files using this library into other apps that use it
//     without any configuration 
//
// (*) can be controlled programmatically or via preferences.  The
//     preferences that control the service and its zones are under
//     the preference branch "safebrowsing-debug-service."
//
// (*) outputs function call traces when the "loggifier" zone is enabled
// 
// (*) can write output to logfiles so that you can get a call trace
//     from someone who is having a problem
//
// Example:
//
// var G_GDEBUG = true                           // Enable this module
// var G_debugService = new G_DebugService();    // in global context
//
// // You can use it with arbitrary primitive first arguement
// G_Debug("myzone", "Yo yo yo");   // outputs: [myzone] Yo yo yo\n
//
// // But it's nice to use it with an object; it will probe for the zone name
// function Obj() {
//   this.debugZone = "someobj";
// }
// Obj.prototype.foo = function() {
//   G_Debug(this, "foo called");
// }
// (new Obj).foo();                        // outputs: [someobj] foo called\n
//
// G_debugService.loggifier.loggify(Obj.prototype);  // enable call tracing
//
// // En/disable specific zones programmatically (you can also use preferences)
// G_debugService.enableZone("somezone");
// G_debugService.disableZone("someotherzone");
// G_debugService.enableAllZones();
//
// // We also have asserts and errors:
// G_Error(this, "Some error occurred");                    // will throw
// G_Assert(this, (x > 3), "x not greater than three!");    // will throw
//
// See classes below for more methods. 
//
// TODO add abililty to alert() instead of dump()? Should be easy.
// TODO add code to set prefs when not found to the default value of a tristate
// TODO add error level support
// TODO add ability to turn off console output
//
// -------> TO START DEBUGGING: set G_GDEBUG to true

// These are the functions code will typically call. Everything is
// wrapped in if's so we can compile it away when G_GDEBUG is false.


if (typeof G_GDEBUG == "undefined") {
  throw new Error("G_GDEBUG constant must be set before loading debug.js");
}


/**
 * Write out a debugging message.
 *
 * @param who The thingy to convert into a zone name corresponding to the 
 *            zone to which this message belongs
 * @param msg Message to output
 */
function G_Debug(who, msg) {
  if (G_GDEBUG) {
    G_GetDebugZone(who).debug(msg);
  }
}

/**
 * Debugs loudly
 */
function G_DebugL(who, msg) {
  if (G_GDEBUG) {
    var zone = G_GetDebugZone(who);

    if (zone.zoneIsEnabled()) {
      G_debugService.dump(
        G_File.LINE_END_CHAR +
        "************************************************************" +
        G_File.LINE_END_CHAR);

      G_Debug(who, msg);

      G_debugService.dump(
        "************************************************************" +
        G_File.LINE_END_CHAR +
        G_File.LINE_END_CHAR);
    }
  }
}

/**
 * Write out a call tracing message
 *
 * @param who The thingy to convert into a zone name corresponding to the 
 *            zone to which this message belongs
 * @param msg Message to output
 */
function G_TraceCall(who, msg) {
  if (G_GDEBUG) {
    if (G_debugService.callTracingEnabled()) {
      G_debugService.dump(msg + G_File.LINE_END_CHAR);
    }
  }
}

/**
 * Write out an error (and throw)
 *
 * @param who The thingy to convert into a zone name corresponding to the 
 *            zone to which this message belongs
 * @param msg Message to output
 */
function G_Error(who, msg) {
  if (G_GDEBUG) {
    G_GetDebugZone(who).error(msg);
  }
}

/**
 * Assert something as true and signal an error if it's not
 *
 * @param who The thingy to convert into a zone name corresponding to the 
 *            zone to which this message belongs
 * @param condition Boolean condition to test
 * @param msg Message to output
 */
function G_Assert(who, condition, msg) {
  if (G_GDEBUG) {
    G_GetDebugZone(who).assert(condition, msg);
  }
}

/**
 * Assert two things are equal (as in ==).
 */
function G_AssertEqual(who, expected, actual, msg) {
  if (G_GDEBUG) {
    G_GetDebugZone(who).assert(
        expected == actual,
        msg + " Expected: {%s}, got: {%s}".subs(expected, actual));
  }
}

/**
 * Helper function that takes input and returns the DebugZone
 * corresponding to it.
 *
 * @param who Arbitrary input that will be converted into a zone name. Most
 *            likely an object that has .debugZone property, or a string.
 * @returns The DebugZone object corresponding to the input
 */
function G_GetDebugZone(who) {
  if (G_GDEBUG) {
    var zone = "?";

    if (who && who.debugZone) {
      zone = who.debugZone;
    } else if (isString(who)) {
      zone = who;
    }

    return G_debugService.getZone(zone);
  }
}

// Classes that implement the functionality.

/**
 * A debug "zone" is a string derived from arbitrary types (but
 * typically derived from another string or an object). All debugging
 * messages using a particular zone can be enabled or disabled
 * independent of other zones. This enables you to turn on/off logging
 * of particular objects or modules. This object implements a single
 * zone and the methods required to use it.
 *
 * @constructor
 * @param service Reference to the DebugService object we use for 
 *                registration
 * @param prefix String indicating the unique prefix we should use
 *               when creating preferences to control this zone
 * @param zone String indicating the name of the zone
 */
function G_DebugZone(service, prefix, zone) {
  if (G_GDEBUG) {
    this.debugService_ = service;
    this.prefix_ = prefix;
    this.zone_ = zone;
    this.zoneEnabledPrefName_ = prefix + ".zone." + this.zone_;
    this.settings_ = new G_DebugSettings();
  }
}
  
/**
 * @returns Boolean indicating if this zone is enabled
 */
G_DebugZone.prototype.zoneIsEnabled = function() {
  if (G_GDEBUG) {
    var explicit = this.settings_.getSetting(this.zoneEnabledPrefName_, null);

    if (explicit !== null) {
      return explicit;
    } else {
      return this.debugService_.allZonesEnabled();
    }
  }
}

/**
 * Enable this logging zone
 */
G_DebugZone.prototype.enableZone = function() {
  if (G_GDEBUG) {
    this.settings_.setDefault(this.zoneEnabledPrefName_, true);
  }
}

/**
 * Disable this logging zone
 */
G_DebugZone.prototype.disableZone = function() {
  if (G_GDEBUG) {
    this.settings_.setDefault(this.zoneEnabledPrefName_, false);
  }
}

/**
 * Write a debugging message to this zone
 *
 * @param msg String of message to write
 */
G_DebugZone.prototype.debug = function(msg) {
  if (G_GDEBUG) {
    if (this.zoneIsEnabled()) {
      this.debugService_.dump("[%s] %s%s".subs(this.zone_,
                                               msg,
                                               G_File.LINE_END_CHAR));
    }
  }
}

/**
 * Write an error to this zone and throw
 *
 * @param msg String of error to write
 */
G_DebugZone.prototype.error = function(msg) {
  if (G_GDEBUG) {
    this.debugService_.dump("[%s] %s%s".subs(this.zone_,
                                             msg,
                                             G_File.LINE_END_CHAR));
    throw new Error(msg);
    debugger;
  }
}

/**
 * Assert something as true and error if it is not
 *
 * @param condition Boolean condition to test
 * @param msg String of message to write if is false
 */
G_DebugZone.prototype.assert = function(condition, msg) {
  if (G_GDEBUG) {
    if (condition !== true) {
      G_Error(this.zone_, "ASSERT FAILED: " + msg);
    }
  }
}


/**
 * The debug service handles auto-registration of zones, namespacing
 * the zones preferences, and various global settings such as whether
 * all zones are enabled.
 *
 * @constructor
 * @param opt_prefix Optional string indicating the unique prefix we should 
 *                   use when creating preferences
 */
function G_DebugService(opt_prefix) {
  if (G_GDEBUG) {
    this.prefix_ = opt_prefix ? opt_prefix : "safebrowsing-debug-service";
    this.consoleEnabledPrefName_ = this.prefix_ + ".alsologtoconsole";
    this.allZonesEnabledPrefName_ = this.prefix_ + ".enableallzones";
    this.callTracingEnabledPrefName_ = this.prefix_ + ".trace-function-calls";
    this.logFileEnabledPrefName_ = this.prefix_ + ".logfileenabled";
    this.logFileErrorLevelPrefName_ = this.prefix_ + ".logfile-errorlevel";
    this.zones_ = {};

    this.loggifier = new G_Loggifier();
    this.settings_ = new G_DebugSettings();

    // We observe the console service so that we can echo errors that get 
    // reported there to the file log.
    Cc["@mozilla.org/consoleservice;1"]
      .getService(Ci.nsIConsoleService)
      .registerListener(this);
  }
}

// Error levels for reporting console messages to the log.
G_DebugService.ERROR_LEVEL_INFO = "INFO";
G_DebugService.ERROR_LEVEL_WARNING = "WARNING";
G_DebugService.ERROR_LEVEL_EXCEPTION = "EXCEPTION";


/**
 * @returns Boolean indicating if we should send messages to the jsconsole
 */
G_DebugService.prototype.alsoDumpToConsole = function() {
  if (G_GDEBUG) {
    return this.settings_.getSetting(this.consoleEnabledPrefName_, false);
  }
}

/**
 * @returns whether to log output to a file as well as the console.
 */
G_DebugService.prototype.logFileIsEnabled = function() {
  if (G_GDEBUG) {
    return this.settings_.getSetting(this.logFileEnabledPrefName_, false);
  }
}

/**
 * Turns on file logging. dump() output will also go to the file specified by
 * setLogFile()
 */
G_DebugService.prototype.enableLogFile = function() {
  if (G_GDEBUG) {
    this.settings_.setDefault(this.logFileEnabledPrefName_, true);
  }
}

/**
 * Turns off file logging
 */
G_DebugService.prototype.disableLogFile = function() {
  if (G_GDEBUG) {
    this.settings_.setDefault(this.logFileEnabledPrefName_, false);
  }
}

/**
 * @returns an nsIFile instance pointing to the current log file location
 */
G_DebugService.prototype.getLogFile = function() {
  if (G_GDEBUG) {
    return this.logFile_;
  }
}

/**
 * Sets a new log file location
 */
G_DebugService.prototype.setLogFile = function(file) {
  if (G_GDEBUG) {
    this.logFile_ = file;
  }
}

/**
 * Enables sending messages to the jsconsole
 */
G_DebugService.prototype.enableDumpToConsole = function() {
  if (G_GDEBUG) {
    this.settings_.setDefault(this.consoleEnabledPrefName_, true);
  }
}

/**
 * Disables sending messages to the jsconsole
 */
G_DebugService.prototype.disableDumpToConsole = function() {
  if (G_GDEBUG) {
    this.settings_.setDefault(this.consoleEnabledPrefName_, false);
  }
}

/**
 * @param zone Name of the zone to get
 * @returns The DebugZone object corresopnding to input. If not such
 *          zone exists, a new one is created and returned
 */
G_DebugService.prototype.getZone = function(zone) {
  if (G_GDEBUG) {
    if (!this.zones_[zone]) 
      this.zones_[zone] = new G_DebugZone(this, this.prefix_, zone);
    
    return this.zones_[zone];
  }
}

/**
 * @param zone Zone to enable debugging for
 */
G_DebugService.prototype.enableZone = function(zone) {
  if (G_GDEBUG) {
    var toEnable = this.getZone(zone);
    toEnable.enableZone();
  }
}

/**
 * @param zone Zone to disable debugging for
 */
G_DebugService.prototype.disableZone = function(zone) {
  if (G_GDEBUG) {
    var toDisable = this.getZone(zone);
    toDisable.disableZone();
  }
}

/**
 * @returns Boolean indicating whether debugging is enabled for all zones
 */
G_DebugService.prototype.allZonesEnabled = function() {
  if (G_GDEBUG) {
    return this.settings_.getSetting(this.allZonesEnabledPrefName_, false);
  }
}

/**
 * Enables all debugging zones
 */
G_DebugService.prototype.enableAllZones = function() {
  if (G_GDEBUG) {
    this.settings_.setDefault(this.allZonesEnabledPrefName_, true);
  }
}

/**
 * Disables all debugging zones
 */
G_DebugService.prototype.disableAllZones = function() {
  if (G_GDEBUG) {
    this.settings_.setDefault(this.allZonesEnabledPrefName_, false);
  }
}

/**
 * @returns Boolean indicating whether call tracing is enabled
 */
G_DebugService.prototype.callTracingEnabled = function() {
  if (G_GDEBUG) {
    return this.settings_.getSetting(this.callTracingEnabledPrefName_, false);
  }
}

/**
 * Enables call tracing
 */
G_DebugService.prototype.enableCallTracing = function() {
  if (G_GDEBUG) {
    this.settings_.setDefault(this.callTracingEnabledPrefName_, true);
  }
}

/**
 * Disables call tracing
 */
G_DebugService.prototype.disableCallTracing = function() {
  if (G_GDEBUG) {
    this.settings_.setDefault(this.callTracingEnabledPrefName_, false);
  }
}

/**
 * Gets the minimum error that will be reported to the log.
 */
G_DebugService.prototype.getLogFileErrorLevel = function() {
  if (G_GDEBUG) {
    var level = this.settings_.getSetting(this.logFileErrorLevelPrefName_, 
                                          G_DebugService.ERROR_LEVEL_EXCEPTION);

    return level.toUpperCase();
  }
}

/**
 * Sets the minimum error level that will be reported to the log.
 */
G_DebugService.prototype.setLogFileErrorLevel = function(level) {
  if (G_GDEBUG) {
    // normalize case just to make it slightly easier to not screw up.
    level = level.toUpperCase();

    if (level != G_DebugService.ERROR_LEVEL_INFO &&
        level != G_DebugService.ERROR_LEVEL_WARNING &&
        level != G_DebugService.ERROR_LEVEL_EXCEPTION) {
      throw new Error("Invalid error level specified: {" + level + "}");
    }

    this.settings_.setDefault(this.logFileErrorLevelPrefName_, level);
  }
}

/**
 * Internal dump() method
 *
 * @param msg String of message to dump
 */
G_DebugService.prototype.dump = function(msg) {
  if (G_GDEBUG) {
    dump(msg);
    
    if (this.alsoDumpToConsole()) {
      try {
        var console = Components.classes['@mozilla.org/consoleservice;1']
                      .getService(Components.interfaces.nsIConsoleService);
        console.logStringMessage(msg);
      } catch(e) {
        dump("G_DebugZone ERROR: COULD NOT DUMP TO CONSOLE" +
             G_File.LINE_END_CHAR);
      }
    }

    this.maybeDumpToFile(msg);
  }
}

/**
 * Writes the specified message to the log file, if file logging is enabled.
 */
G_DebugService.prototype.maybeDumpToFile = function(msg) {
  if (this.logFileIsEnabled() && this.logFile_) {
    if (!this.logWriter_) {
      this.logWriter_ = new G_FileWriter(this.logFile_, true);
    }

    this.logWriter_.write(msg);
  }
}

/**
 * Implements nsIConsoleListener.observe(). Gets called when an error message
 * gets reported to the console and sends it to the log file as well.
 */
G_DebugService.prototype.observe = function(consoleMessage) {
  if (G_GDEBUG) {
    var errorLevel = this.getLogFileErrorLevel();

    // consoleMessage can be either nsIScriptError or nsIConsoleMessage. The
    // latter does not have things like line number, etc. So we special case 
    // it first. 
    if (!(consoleMessage instanceof Ci.nsIScriptError)) {
      // Only report these messages if the error level is INFO.
      if (errorLevel == G_DebugService.ERROR_LEVEL_INFO) {
        this.maybeDumpToFile(G_DebugService.ERROR_LEVEL_INFO + ": " + 
                             consoleMessage.message + G_File.LINE_END_CHAR);
      }

      return;
    }

    // We make a local copy of these fields because writing to it doesn't seem
    // to work.
    var flags = consoleMessage.flags;
    var sourceName = consoleMessage.sourceName;
    var lineNumber = consoleMessage.lineNumber;

    // Sometimes, a scripterror instance won't have any flags set. We 
    // default to exception.
    if (!flags) {
      flags = Ci.nsIScriptError.exceptionFlag;
    }

    // Default the filename and line number if they aren't set.
    if (!sourceName) {
      sourceName = "<unknown>";
    }

    if (!lineNumber) {
      lineNumber = "<unknown>";
    }

    // Report the error in the log file.
    if (flags & Ci.nsIScriptError.warningFlag) {
      // Only report warnings if the error level is warning or better. 
      if (errorLevel == G_DebugService.ERROR_LEVEL_WARNING ||
          errorLevel == G_DebugService.ERROR_LEVEL_INFO) {
        this.reportScriptError_(consoleMessage.message,
                                sourceName,
                                lineNumber,
                                G_DebugService.ERROR_LEVEL_WARNING);
      }
    } else if (flags & Ci.nsIScriptError.exceptionFlag) {
      // Always report exceptions.
      this.reportScriptError_(consoleMessage.message,
                              sourceName,
                              lineNumber,
                              G_DebugService.ERROR_LEVEL_EXCEPTION);
    }
  }
}

/**
 * Private helper to report an nsIScriptError instance to the log/console.
 */
G_DebugService.prototype.reportScriptError_ = function(message, sourceName, 
                                                       lineNumber, label) {
  var message = ["",
                 "------------------------------------------------------------",
                 label + ": " + message,
                 "location: " + sourceName + ", " + "line: " + lineNumber,
                 "------------------------------------------------------------",
                 "",
                 ""].join(G_File.LINE_END_CHAR);

  dump(message);
  this.maybeDumpToFile(message);
}



/**
 * A class that instruments methods so they output a call trace,
 * including the values of their actual parameters and return value.
 * This code is mostly stolen from Aaron Boodman's original
 * implementation in clobber utils.
 *
 * Note that this class uses the "loggifier" debug zone, so you'll see 
 * a complete call trace when that zone is enabled.
 *
 * @constructor
 */
function G_Loggifier() {
  if (G_GDEBUG) {
    // Careful not to loggify ourselves!
    this.mark_(this);  
  }
}

/**
 * Marks an object as having been loggified. Loggification is not 
 * idempotent :)
 *
 * @param obj Object to be marked
 */
G_Loggifier.prototype.mark_ = function(obj) {
  if (G_GDEBUG) {
    obj.__loggified_ = true;
  }
}

/**
 * @param obj Object to be examined
 * @returns Boolean indicating if the object has been loggified
 */
G_Loggifier.prototype.isLoggified = function(obj) {
  if (G_GDEBUG) {
    return !!obj.__loggified_;
  }
}

/**
 * Attempt to extract the class name from the constructor definition.
 * Assumes the object was created using new.
 *
 * @param constructor String containing the definition of a constructor,
 *                    for example what you'd get by examining obj.constructor
 * @returns Name of the constructor/object if it could be found, else "???"
 */
G_Loggifier.prototype.getFunctionName_ = function(constructor) {
  if (G_GDEBUG) {
    return constructor.name || "???";
  }
}

/**
 * Wraps all the methods in an object so that call traces are
 * automatically outputted.
 *
 * @param obj Object to loggify. SHOULD BE THE PROTOTYPE OF A USER-DEFINED
 *            object. You can get into trouble if you attempt to 
 *            loggify something that isn't, for example the Window.
 *
 * Any additional parameters are considered method names which should not be
 * loggified.
 *
 * Usage:
 * G_debugService.loggifier.loggify(MyClass.prototype,
 *                                  "firstMethodNotToLog",
 *                                  "secondMethodNotToLog",
 *                                  ... etc ...);
 */
G_Loggifier.prototype.loggify = function(obj) {
  if (G_GDEBUG) {
    if (!G_debugService.callTracingEnabled()) {
      return;
    }

    if (typeof window != "undefined" && obj == window || 
        this.isLoggified(obj))   // Don't go berserk!
      return;

    var zone = G_GetDebugZone(obj);
    if (!zone || !zone.zoneIsEnabled()) {
      return;
    }

    this.mark_(obj);

    // Helper function returns an instrumented version of
    // objName.meth, with "this" bound properly. (BTW, because we're
    // in a conditional here, functions will only be defined as
    // they're encountered during execution, so declare this helper
    // before using it.)

    function wrap(meth, objName, methName) {
      return function() {
        
        // First output the call along with actual parameters
        var args = new Array(arguments.length);
        var argsString = "";
        for (var i = 0; i < args.length; i++) {
          args[i] = arguments[i];
          argsString += (i == 0 ? "" : ", ");
          
          if (isFunction(args[i])) {
            argsString += "[function]";
          } else {
            argsString += args[i];
          }
        }

        G_TraceCall(this, "> " + objName + "." + methName + "(" + 
                    argsString + ")");
        
        // Then run the function, capturing the return value and throws
        try {
          var retVal = meth.apply(this, arguments);
          var reportedRetVal = retVal;

          if (typeof reportedRetVal == "undefined")
            reportedRetVal = "void";
          else if (reportedRetVal === "")
            reportedRetVal = "\"\" (empty string)";
        } catch (e) {
          if (e && !e.__logged) {
            G_TraceCall(this, "Error: " + e.message + ". " + 
                        e.fileName + ": " + e.lineNumber);
            try {
              e.__logged = true;
            } catch (e2) {
              // Sometimes we can't add the __logged flag because it's an
              // XPC wrapper
              throw e;
            }
          }
          
          throw e;      // Re-throw!
        }

        // And spit it out already
        G_TraceCall(
          this, 
          "< " + objName + "." + methName + ": " + reportedRetVal);

        return retVal;
      };
    };

    var ignoreLookup = {};

    if (arguments.length > 1) {
      for (var i = 1; i < arguments.length; i++) {
        ignoreLookup[arguments[i]] = true;
      }
    }
    
    // Wrap each method of obj
    for (var p in obj) {
      // Work around bug in Firefox. In ffox typeof RegExp is "function",
      // so make sure this really is a function. Bug as of FFox 1.5b2.
      if (typeof obj[p] == "function" && obj[p].call && !ignoreLookup[p]) {
        var objName = this.getFunctionName_(obj.constructor);
        obj[p] = wrap(obj[p], objName, p);
      }
    }
  }
}


/**
 * Simple abstraction around debug settings. The thing with debug settings is
 * that we want to be able to specify a default in the application's startup,
 * but have that default be overridable by the user via their prefs.
 *
 * To generalize this, we package up a dictionary of defaults with the 
 * preferences tree. If a setting isn't in the preferences tree, then we grab it
 * from the defaults.
 */
function G_DebugSettings() {
  this.defaults_ = {};
  this.prefs_ = new G_Preferences();
}

/**
 * Returns the value of a settings, optionally defaulting to a given value if it
 * doesn't exist. If no default is specified, the default is |undefined|.
 */
G_DebugSettings.prototype.getSetting = function(name, opt_default) {
  var override = this.prefs_.getPref(name, null);

  if (override !== null) {
    return override;
  } else if (typeof this.defaults_[name] != "undefined") {
    return this.defaults_[name];
  } else {
    return opt_default;
  }
}

/**
 * Sets the default value for a setting. If the user doesn't override it with a
 * preference, this is the value which will be returned by getSetting().
 */
G_DebugSettings.prototype.setDefault = function(name, val) {
  this.defaults_[name] = val;
}

var G_debugService = new G_DebugService(); // Instantiate us!

if (G_GDEBUG) {
  G_debugService.enableAllZones();
}
/* ***** 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 Google Safe Browsing.
 *
 * The Initial Developer of the Original Code is Google Inc.
 * Portions created by the Initial Developer are Copyright (C) 2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Fritz Schneider <fritz at google.com> (original author)
 *
 * 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 ***** */


// An Alarm fires a callback after a certain amount of time, or at
// regular intervals. It's a convenient replacement for
// setTimeout/Interval when you don't want to bind to a specific
// window.
//
// The ConditionalAlarm is an Alarm that cancels itself if its callback 
// returns a value that type-converts to true.
//
// Example:
//
//  function foo() { alert('hi'); };
//  new G_Alarm(foo, 10*1000);                   // Fire foo in 10 seconds
//  new G_Alarm(foo, 10*1000, true /*repeat*/);  // Fire foo every 10 seconds
//  new G_Alarm(foo, 10*1000, true, 7);          // Fire foo every 10 seconds
//                                               // seven times
//  new G_ConditionalAlarm(foo, 1000, true); // Fire every sec until foo()==true
//
//  // Fire foo every 10 seconds until foo returns true or until it fires seven
//  // times, whichever happens first.
//  new G_ConditionalAlarm(foo, 10*1000, true /*repeating*/, 7);
//
// TODO: maybe pass an isFinal flag to the callback if they opted to
// set maxTimes and this is the last iteration?


/**
 * Set an alarm to fire after a given amount of time, or at specific 
 * intervals.
 *
 * @param callback Function to call when the alarm fires
 * @param delayMS Number indicating the length of the alarm period in ms
 * @param opt_repeating Boolean indicating whether this should fire 
 *                      periodically
 * @param opt_maxTimes Number indicating a maximum number of times to 
 *                     repeat (obviously only useful when opt_repeating==true)
 */
function G_Alarm(callback, delayMS, opt_repeating, opt_maxTimes) {
  this.debugZone = "alarm";
  this.callback_ = callback;
  this.repeating_ = !!opt_repeating;
  this.timer_ = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  var type = opt_repeating ? 
             this.timer_.TYPE_REPEATING_SLACK : 
             this.timer_.TYPE_ONE_SHOT;
  this.maxTimes_ = opt_maxTimes ? opt_maxTimes : null;
  this.nTimes_ = 0;

  this.observerServiceObserver_ = new G_ObserverServiceObserver(
                                        'xpcom-shutdown',
                                        BindToObject(this.cancel, this));

  // Ask the timer to use nsITimerCallback (.notify()) when ready
  this.timer_.initWithCallback(this, delayMS, type);
}

/**
 * Cancel this timer 
 */
G_Alarm.prototype.cancel = function() {
  if (!this.timer_) {
    return;
  }

  this.timer_.cancel();
  // Break circular reference created between this.timer_ and the G_Alarm
  // instance (this)
  this.timer_ = null;
  this.callback_ = null;

  // We don't need the shutdown observer anymore
  this.observerServiceObserver_.unregister();
}

/**
 * Invoked by the timer when it fires
 * 
 * @param timer Reference to the nsITimer which fired (not currently 
 *              passed along)
 */
G_Alarm.prototype.notify = function(timer) {
  // fire callback and save results
  var ret = this.callback_();
  
  // If they've given us a max number of times to fire, enforce it
  this.nTimes_++;
  if (this.repeating_ && 
      typeof this.maxTimes_ == "number" 
      && this.nTimes_ >= this.maxTimes_) {
    this.cancel();
  } else if (!this.repeating_) {
    // Clear out the callback closure for TYPE_ONE_SHOT timers
    this.cancel();
  }
  // We don't cancel/cleanup timers that repeat forever until either
  // xpcom-shutdown occurs or cancel() is called explicitly.

  return ret;
}

/**
 * XPCOM cruft
 */
G_Alarm.prototype.QueryInterface = function(iid) {
  if (iid.equals(Components.interfaces.nsISupports) ||
      iid.equals(Components.interfaces.nsITimerCallback))
    return this;

  throw Components.results.NS_ERROR_NO_INTERFACE;
}


/**
 * An alarm with the additional property that it cancels itself if its 
 * callback returns true.
 *
 * For parameter documentation, see G_Alarm
 */
function G_ConditionalAlarm(callback, delayMS, opt_repeating, opt_maxTimes) {
  G_Alarm.call(this, callback, delayMS, opt_repeating, opt_maxTimes);
  this.debugZone = "conditionalalarm";
}

G_ConditionalAlarm.inherits(G_Alarm);

/**
 * Invoked by the timer when it fires
 * 
 * @param timer Reference to the nsITimer which fired (not currently 
 *              passed along)
 */
G_ConditionalAlarm.prototype.notify = function(timer) {
  // Call G_Alarm::notify
  var rv = G_Alarm.prototype.notify.call(this, timer);

  if (this.repeating_ && rv) {
    G_Debug(this, "Callback of a repeating alarm returned true; cancelling.");
    this.cancel();
  }
}
/* ***** 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 Google Safe Browsing.
 *
 * The Initial Developer of the Original Code is Google Inc.
 * Portions created by the Initial Developer are Copyright (C) 2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Fritz Schneider <fritz at google.com> (original author)
 *
 * 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 ***** */


// Base64 en/decoding. Not much to say here except that we work with
// decoded values in arrays of bytes. By "byte" I mean a number in [0,
// 255].


/**
 * Base64 en/decoder. Useful in contexts that don't have atob/btoa, or
 * when you need a custom encoding function (e.g., websafe base64).
 *
 * @constructor
 */
function G_Base64() {
  this.byteToCharMap_ = {};
  this.charToByteMap_ = {};
  this.byteToCharMapWebSafe_ = {};
  this.charToByteMapWebSafe_ = {};
  this.init_();
}

/**
 * Our default alphabet. Value 64 (=) is special; it means "nothing."
 */ 
G_Base64.ENCODED_VALS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
                        "abcdefghijklmnopqrstuvwxyz" +
                        "0123456789+/=";

/**
 * Our websafe alphabet. Value 64 (=) is special; it means "nothing."
 */ 
G_Base64.ENCODED_VALS_WEBSAFE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
                                "abcdefghijklmnopqrstuvwxyz" +
                                "0123456789-_=";

/**
 * We want quick mappings back and forth, so we precompute two maps.
 */
G_Base64.prototype.init_ = function() {
  for (var i = 0; i < G_Base64.ENCODED_VALS.length; i++) {
    this.byteToCharMap_[i] = G_Base64.ENCODED_VALS.charAt(i);
    this.charToByteMap_[this.byteToCharMap_[i]] = i;
    this.byteToCharMapWebSafe_[i] = G_Base64.ENCODED_VALS_WEBSAFE.charAt(i);
    this.charToByteMapWebSafe_[this.byteToCharMapWebSafe_[i]] = i;
  }
}

/**
 * Base64-encode an array of bytes.
 *
 * @param input An array of bytes (numbers with value in [0, 255]) to encode
 *
 * @param opt_webSafe Boolean indicating we should use the alternative alphabet 
 *
 * @returns String containing the base64 encoding
 */
G_Base64.prototype.encodeByteArray = function(input, opt_webSafe) {

  if (!(input instanceof Array))
    throw new Error("encodeByteArray takes an array as a parameter");

  var byteToCharMap = opt_webSafe ? 
                      this.byteToCharMapWebSafe_ :
                      this.byteToCharMap_;

  var output = [];

  var i = 0;
  while (i < input.length) {

    var byte1 = input[i];
    var haveByte2 = i + 1 < input.length;
    var byte2 = haveByte2 ? input[i + 1] : 0;
    var haveByte3 = i + 2 < input.length;
    var byte3 = haveByte3 ? input[i + 2] : 0;

    var outByte1 = byte1 >> 2;
    var outByte2 = ((byte1 & 0x03) << 4) | (byte2 >> 4);
    var outByte3 = ((byte2 & 0x0F) << 2) | (byte3 >> 6);
    var outByte4 = byte3 & 0x3F;

    if (!haveByte3) {
      outByte4 = 64;
      
      if (!haveByte2)
        outByte3 = 64;
    }
    
    output.push(byteToCharMap[outByte1]);
    output.push(byteToCharMap[outByte2]);
    output.push(byteToCharMap[outByte3]);
    output.push(byteToCharMap[outByte4]);

    i += 3;
  }

  return output.join("");
}

/**
 * Base64-decode a string.
 *
 * @param input String to decode
 *
 * @param opt_webSafe Boolean indicating we should use the alternative alphabet 
 * 
 * @returns Array of bytes representing the decoded value.
 */
G_Base64.prototype.decodeString = function(input, opt_webSafe) {

  if (input.length % 4)
    throw new Error("Length of b64-encoded data must be zero mod four");

  var charToByteMap = opt_webSafe ? 
                      this.charToByteMapWebSafe_ :
                      this.charToByteMap_;

  var output = [];

  var i = 0;
  while (i < input.length) {

    var byte1 = charToByteMap[input.charAt(i)];
    var byte2 = charToByteMap[input.charAt(i + 1)];
    var byte3 = charToByteMap[input.charAt(i + 2)];
    var byte4 = charToByteMap[input.charAt(i + 3)];

    if (byte1 === undefined || byte2 === undefined ||
        byte3 === undefined || byte4 === undefined)
      throw new Error("String contains characters not in our alphabet: " +
                      input);

    var outByte1 = (byte1 << 2) | (byte2 >> 4);
    output.push(outByte1);
    
    if (byte3 != 64) {
      var outByte2 = ((byte2 << 4) & 0xF0) | (byte3 >> 2);
      output.push(outByte2);
      
      if (byte4 != 64) {
        var outByte3 = ((byte3 << 6) & 0xC0) | byte4;
        output.push(outByte3);
      }
    }

    i += 4;
  }

  return output;
}

/**
 * Helper function that turns a string into an array of numbers. 
 *
 * @param str String to arrify
 *
 * @returns Array holding numbers corresponding to the UCS character codes
 *          of each character in str
 */
G_Base64.prototype.arrayifyString = function(str) {
  var output = [];
  for (var i = 0; i < str.length; i++)
    output.push(str.charCodeAt(i));
  return output;
}

/**
 * Helper function that turns an array of numbers into the string
 * given by the concatenation of the characters to which the numbesr
 * correspond (got that?).
 *
 * @param array Array of numbers representing characters
 *
 * @returns Stringification of the array
 */ 
G_Base64.prototype.stringifyArray = function(array) {
  var output = [];
  for (var i = 0; i < array.length; i++)
    output[i] = String.fromCharCode(array[i]);
  return output.join("");
}


/* ***** 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 Google Safe Browsing.
 *
 * The Initial Developer of the Original Code is Google Inc.
 * Portions created by the Initial Developer are Copyright (C) 2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Fritz Schneider <fritz at google.com> (original author)
 *
 * 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 ***** */


// A very thin wrapper around nsICryptoHash. It's not strictly
// necessary, but makes the code a bit cleaner and gives us the
// opportunity to verify that our implementations give the results that
// we expect, for example if we have to interoperate with a server.
//
// The digest* methods reset the state of the hasher, so it's
// necessary to call init() explicitly after them.
//
// Works only in Firefox 1.5+.
//
// IMPORTANT NOTE: Due to https://bugzilla.mozilla.org/show_bug.cgi?id=321024
// you cannot use the cryptohasher before app-startup. The symptom of doing
// so is a segfault in NSS.

/**
 * Instantiate a new hasher. You must explicitly call init() before use!
 */
function G_CryptoHasher() {
  this.debugZone = "cryptohasher";
  this.decoder_ = new G_Base64();
  this.hasher_ = null;
}

G_CryptoHasher.algorithms = {
  MD2: Ci.nsICryptoHash.MD2,
  MD5: Ci.nsICryptoHash.MD5,
  SHA1: Ci.nsICryptoHash.SHA1,
  SHA256: Ci.nsICryptoHash.SHA256,
  SHA384: Ci.nsICryptoHash.SHA384,
  SHA512: Ci.nsICryptoHash.SHA512,
};

/**
 * Initialize the hasher. This function must be called after every call
 * to one of the digest* methods.
 *
 * @param algorithm Constant from G_CryptoHasher.algorithms specifying the
 *                  algorithm this hasher will use
 */ 
G_CryptoHasher.prototype.init = function(algorithm) {
  var validAlgorithm = false;
  for (var alg in G_CryptoHasher.algorithms)
    if (algorithm == G_CryptoHasher.algorithms[alg])
      validAlgorithm = true;

  if (!validAlgorithm)
    throw new Error("Invalid algorithm: " + algorithm);

  this.hasher_ = Cc["@mozilla.org/security/hash;1"]
                 .createInstance(Ci.nsICryptoHash);
  this.hasher_.init(algorithm);
}

/**
 * Update the hash's internal state with input given in a string. Can be
 * called multiple times for incrementeal hash updates. Note that this function
 * is slllloooowww since it uses the a javascript implementation to convert the
 * string to an array. If you need something faster, use updateFromStream() with
 * an XPCOM stream.
 *
 * @param input String containing data to hash.
 */ 
G_CryptoHasher.prototype.updateFromString = function(input) {
  if (!this.hasher_)
    throw new Error("You must initialize the hasher first!");

  this.hasher_.update(this.decoder_.arrayifyString(input), input.length);
}

/**
 * Update the hash's internal state with input given in an array. Can be
 * called multiple times for incremental hash updates.
 *
 * @param input Array containing data to hash.
 */ 
G_CryptoHasher.prototype.updateFromArray = function(input) {
  if (!this.hasher_)
    throw new Error("You must initialize the hasher first!");

  this.hasher_.update(input, input.length);
}

/**
 * Update the hash's internal state with input given in a stream. Can be
 * called multiple times from incremental hash updates.
 */
G_CryptoHasher.prototype.updateFromStream = function(stream) {
  if (!this.hasher_)
    throw new Error("You must initialize the hasher first!");

  this.hasher_.updateFromStream(stream, stream.available());
}

/**
 * @returns The hash value as a string (sequence of 8-bit values)
 */ 
G_CryptoHasher.prototype.digestRaw = function() {
  var digest = this.hasher_.finish(false /* not b64 encoded */);
  this.hasher_ = null;
  return digest;
}

/**
 * @returns The hash value as a base64-encoded string
 */ 
G_CryptoHasher.prototype.digestBase64 = function() {
  var digest = this.hasher_.finish(true /* b64 encoded */);
  this.hasher_ = null;
  return digest;
}

/**
 * @returns The hash value as a hex-encoded string
 */ 
G_CryptoHasher.prototype.digestHex = function() {
  var raw = this.digestRaw();
  return this.toHex_(raw);
}

/**
 * Converts a sequence of values to a hex-encoded string. The input is a
 * a string, so you can stick 16-bit values in each character.
 *
 * @param str String to conver to hex. (Often this is just a sequence of
 *            16-bit values)
 *
 * @returns String containing the hex representation of the input
 */ 
G_CryptoHasher.prototype.toHex_ = function(str) {
  var hexchars = '0123456789ABCDEF';
  var hexrep = new Array(str.length * 2);

  for (var i = 0; i < str.length; ++i) {
    hexrep[i * 2] = hexchars.charAt((str.charCodeAt(i) >> 4) & 15);
    hexrep[i * 2 + 1] = hexchars.charAt(str.charCodeAt(i) & 15);
  }
  return hexrep.join('');
}

/* ***** 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 Google Safe Browsing.
 *
 * The Initial Developer of the Original Code is Google Inc.
 * Portions created by the Initial Developer are Copyright (C) 2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Aaron Boodman <aa at google.com> (original author)
 *
 * 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 ***** */


// Firefox-specific additions to lib/js/lang.js.


/**
 * The always-useful alert. 
 */
function alert(msg, opt_title) {
  opt_title |= "message";

  Cc["@mozilla.org/embedcomp/prompt-service;1"]
    .getService(Ci.nsIPromptService)
    .alert(null, opt_title, msg.toString());
}


/**
 * The instanceof operator cannot be used on a pure js object to determine if 
 * it implements a certain xpcom interface. The QueryInterface method can, but
 * it throws an error which makes things more complex.
 */
function jsInstanceOf(obj, iid) {
  try {
    obj.QueryInterface(iid);
    return true;
  } catch (e) {
    if (e == Components.results.NS_ERROR_NO_INTERFACE) {
      return false;
    } else {
      throw e;
    }
  }
}


/**
 * Unbelievably, Function inheritence is broken in chrome in Firefox
 * (still as of FFox 1.5b1). Hence if you're working in an extension
 * and not using the subscriptloader, you can't use the method
 * above. Instead, use this global function that does roughly the same
 * thing.
 *
 ***************************************************************************
 *   NOTE THE REVERSED ORDER OF FUNCTION AND OBJECT REFERENCES AS bind()   *
 ***************************************************************************
 * 
 * // Example to bind foo.bar():
 * var bound = BindToObject(bar, foo, "arg1", "arg2");
 * bound("arg3", "arg4");
 * 
 * @param func {string} Reference to the function to be bound
 *
 * @param obj {object} Specifies the object which |this| should point to
 * when the function is run. If the value is null or undefined, it will default
 * to the global object.
 *
 * @param opt_{...} Dummy optional arguments to make a jscompiler happy
 *
 * @returns {function} A partially-applied form of the speficied function.
 */
function BindToObject(func, obj, opt_A, opt_B, opt_C, opt_D, opt_E, opt_F) {
  // This is the sick product of Aaron's mind. Not for the feint of heart.
  var args = Array.prototype.splice.call(arguments, 1, arguments.length);
  return Function.prototype.bind.apply(func, args);
}
/* ***** 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 Google Safe Browsing.
 *
 * The Initial Developer of the Original Code is Google Inc.
 * Portions created by the Initial Developer are Copyright (C) 2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Fritz Schneider <fritz at google.com> (original author)
 *
 * 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 ***** */


// ObjectSafeMap is, shockingly, a Map with which it is safe to use
// objects as keys. It currently uses parallel arrays for storage,
// rendering it inefficient (linear) for large maps. We can always
// swap out the implementation if this becomes a problem. Note that
// this class uses strict equality to determine equivalent keys.
// 
// Interface:
//
//   insert(key, value)
//   erase(key)          // Returns true if key erased, false if not found
//   find(key)           // Returns undefined if key not found
//   replace(otherMap)   // Clones otherMap, replacing the current map
//   forEach(func)
//   size()              // Returns number of items in the map
//
// TODO: should probably make it iterable by implementing getList();


/**
 * Create a new ObjectSafeMap.
 *
 * @param opt_name A string used to name the map
 *
 * @constructor
 */
function G_ObjectSafeMap(opt_name) {
  this.debugZone = "objectsafemap";
  this.name_ = opt_name ? opt_name : "noname";
  this.keys_ = [];
  this.values_ = [];
}

/**
 * Helper function to return the index of a key. 
 *
 * @param key An key to find
 *
 * @returns Index in the keys array where the key is found, -1 if not
 */
G_ObjectSafeMap.prototype.indexOfKey_ = function(key) {
  for (var i = 0; i < this.keys_.length; i++)
    if (this.keys_[i] === key)
      return i;
  return -1;
}

/**
 * Add an item
 *
 * @param key An key to add (overwrites previous key)
 *
 * @param value The value to store at that key
 */
G_ObjectSafeMap.prototype.insert = function(key, value) {
  if (key === null)
    throw new Error("Can't use null as a key");
  if (value === undefined)
    throw new Error("Can't store undefined values in this map");

  var i = this.indexOfKey_(key);
  if (i == -1) {
    this.keys_.push(key);
    this.values_.push(value);
  } else {
    this.keys_[i] = key;
    this.values_[i] = value;
  }

  G_Assert(this, this.keys_.length == this.values_.length, 
           "Different number of keys than values!");
}

/**
 * Remove a key from the map
 *
 * @param key The key to remove
 *
 * @returns Boolean indicating if the key was removed
 */
G_ObjectSafeMap.prototype.erase = function(key) {
  var keyLocation = this.indexOfKey_(key);
  var keyFound = keyLocation != -1;
  if (keyFound) {
    this.keys_.splice(keyLocation, 1);
    this.values_.splice(keyLocation, 1);
  }
  G_Assert(this, this.keys_.length == this.values_.length, 
           "Different number of keys than values!");

  return keyFound;
}

/**
 * Look up a key in the map
 *
 * @param key The key to look up
 * 
 * @returns The value at that key or undefined if it doesn't exist
 */
G_ObjectSafeMap.prototype.find = function(key) {
  var keyLocation = this.indexOfKey_(key);
  return keyLocation == -1 ? undefined : this.values_[keyLocation];
}

/**
 * Replace one map with the content of another
 *
 * @param map input map that needs to be merged into our map
 */
G_ObjectSafeMap.prototype.replace = function(other) {
  this.keys_ = [];
  this.values_ = [];
  for (var i = 0; i < other.keys_.length; i++) {
    this.keys_.push(other.keys_[i]);
    this.values_.push(other.values_[i]);
  }

  G_Assert(this, this.keys_.length == this.values_.length, 
           "Different number of keys than values!");
}

/**
 * Apply a function to each of the key value pairs.
 *
 * @param func Function to apply to the map's key value pairs
 */
G_ObjectSafeMap.prototype.forEach = function(func) {
  if (typeof func != "function")
    throw new Error("argument to forEach is not a function, it's a(n) " + 
                    typeof func);

  for (var i = 0; i < this.keys_.length; i++)
    func(this.keys_[i], this.values_[i]);
}

/**
 * @returns The number of keys in the map
 */
G_ObjectSafeMap.prototype.size = function() {
  return this.keys_.length;
}

/* ***** 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 Google Safe Browsing.
 *
 * The Initial Developer of the Original Code is Google Inc.
 * Portions created by the Initial Developer are Copyright (C) 2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Fritz Schneider <fritz at google.com> (original author)
 *
 * 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 ***** */


// A couple of classes to simplify creating observers. 
//
// // Example1:
//
// function doSomething() { ... }
// var observer = new G_ObserverWrapper(topic, doSomething);
// someObj.addObserver(topic, observer);
//
// // Example2: 
//
// function doSomething() { ... }
// new G_ObserverServiceObserver("profile-after-change", 
//                               doSomething,
//                               true /* run only once */);


/**
 * This class abstracts the admittedly simple boilerplate required of
 * an nsIObserver. It saves you the trouble of implementing the
 * indirection of your own observe() function.
 *
 * @param topic String containing the topic the observer will filter for
 *
 * @param observeFunction Reference to the function to call when the 
 *                        observer fires
 *
 * @constructor
 */
function G_ObserverWrapper(topic, observeFunction) {
  this.debugZone = "observer";
  this.topic_ = topic;
  this.observeFunction_ = observeFunction;
}

/**
 * XPCOM
 */
G_ObserverWrapper.prototype.QueryInterface = function(iid) {
  if (iid.equals(Ci.nsISupports) || iid.equals(Ci.nsIObserver))
    return this;
  throw Components.results.NS_ERROR_NO_INTERFACE;
}

/**
 * Invoked by the thingy being observed
 */
G_ObserverWrapper.prototype.observe = function(subject, topic, data) {
  if (topic == this.topic_)
    this.observeFunction_(subject, topic, data);
}


/**
 * This class abstracts the admittedly simple boilerplate required of
 * observing an observerservice topic. It implements the indirection
 * required, and automatically registers to hear the topic.
 *
 * @param topic String containing the topic the observer will filter for
 *
 * @param observeFunction Reference to the function to call when the 
 *                        observer fires
 *
 * @param opt_onlyOnce Boolean indicating if the observer should unregister
 *                     after it has fired
 *
 * @constructor
 */
function G_ObserverServiceObserver(topic, observeFunction, opt_onlyOnce) {
  this.debugZone = "observerserviceobserver";
  this.topic_ = topic;
  this.observeFunction_ = observeFunction;
  this.onlyOnce_ = !!opt_onlyOnce;
  
  this.observer_ = new G_ObserverWrapper(this.topic_, 
                                         BindToObject(this.observe_, this));
  this.observerService_ = Cc["@mozilla.org/observer-service;1"]
                          .getService(Ci.nsIObserverService);
  this.observerService_.addObserver(this.observer_, this.topic_, false);
}

/**
 * Unregister the observer from the observerservice
 */
G_ObserverServiceObserver.prototype.unregister = function() {
  this.observerService_.removeObserver(this.observer_, this.topic_);
  this.observerService_ = null;
}

/**
 * Invoked by the observerservice
 */
G_ObserverServiceObserver.prototype.observe_ = function(subject, topic, data) {
  this.observeFunction_(subject, topic, data);
  if (this.onlyOnce_)
    this.unregister();
}

/* ***** 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 Google Safe Browsing.
 *
 * The Initial Developer of the Original Code is Google Inc.
 * Portions created by the Initial Developer are Copyright (C) 2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Fritz Schneider <fritz at google.com> (original author)
 *
 * 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 ***** */


// A helper class that knows how to parse from and serialize to
// protocol4. This is a simple, historical format used by some Google
// interfaces, for example the Toolbar (i.e., ancient services).
//
// Protocol4 consists of a newline-separated sequence of name/value
// pairs (strings). Each line consists of the name, the value length,
// and the value itself, all separated by colons. Example:
//
// foo:6:barbaz\n
// fritz:33:issickofdynamicallytypedlanguages\n


/**
 * This class knows how to serialize/deserialize maps to/from their
 * protocol4 representation.
 *
 * @constructor
 */
function G_Protocol4Parser() {
  this.debugZone = "protocol4";

  this.protocol4RegExp_ = new RegExp("([^:]+):\\d+:(.*)$");
  this.newlineRegExp_ = new RegExp("(\\r)?\\n");
}

/**
 * Create a map from a protocol4 string. Silently skips invalid lines.
 *
 * @param text String holding the protocol4 representation
 * 
 * @returns Object as an associative array with keys and values 
 *          given in text. The empty object is returned if none
 *          are parsed.
 */
G_Protocol4Parser.prototype.parse = function(text) {

  var response = {};
  if (!text)
    return response;

  // Responses are protocol4: (repeated) name:numcontentbytes:content\n
  var lines = text.split(this.newlineRegExp_);
  for (var i = 0; i < lines.length; i++)
    if (this.protocol4RegExp_.exec(lines[i]))
      response[RegExp.$1] = RegExp.$2;

  return response;
}

/**
 * Create a protocol4 string from a map (object). Throws an error on 
 * an invalid input.
 *
 * @param map Object as an associative array with keys and values 
 *            given as strings.
 *
 * @returns text String holding the protocol4 representation
 */
G_Protocol4Parser.prototype.serialize = function(map) {
  if (typeof map != "object")
    throw new Error("map must be an object");

  var text = "";
  for (var key in map) {
    if (typeof map[key] != "string")
      throw new Error("Keys and values must be strings");
    
    text += key + ":" + map[key].length + ":" + map[key] + "\n";
  }
  
  return text;
}

//@line 59 "/jds/packages/BUILD/SUNWfirefox-2.0/firefox/toolkit/components/url-classifier/src/nsUrlClassifierLib.js"

/* ***** 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 &q